/* Marionette — mobile-first palette */

:root {
  /* ─── Design tokens - mirror reachy_mini_marionette_v2/src/theme.ts ──
     The shell (Emotions / Telepresence / mobile) uses an MUI theme;
     we don't pull MUI in here, we just copy its *light* palette so
     this app stays visually consistent. Values map 1:1 to the theme:
       ACCENT            #FF9500   → --primary / --ring
       shape.borderRadius 12       → --radius
       background.default #fafafa  → --background
       background.paper   #ffffff  → --surface / --card
       text.primary       #111111  → --foreground
       text.secondary     rgba(0,0,0,.65) → --muted-foreground
       divider            rgba(0,0,0,.08) → --border
     Dark mode lives in the `[data-theme="dark"]` block below; the host
     decides which palette is active (see index.html bootstrap + the
     applyTheme()/onThemeChange wiring in main.js). Component styles
     read these tokens only, so they flip for free. */

  color-scheme: light;
  --radius: 12px;                       /* theme shape.borderRadius */
  --radius-sm: calc(var(--radius) - 4px);
  --radius-md: calc(var(--radius) - 2px);
  --radius-lg: var(--radius);
  --radius-xl: calc(var(--radius) + 4px);
  --radius-2xl: calc(var(--radius) + 8px);

  --background: #fafafa;                /* theme background.default */
  --foreground: #111111;               /* theme text.primary */
  --surface: #ffffff;                  /* theme background.paper */
  --surface-2: #f2f2f2;
  --card: #ffffff;                     /* theme background.paper */
  --primary: #FF9500;                  /* theme ACCENT (palette.primary.main) */
  --primary-foreground: #ffffff;
  --muted: #f2f2f2;
  --muted-foreground: rgba(0, 0, 0, 0.65);  /* theme text.secondary */
  --accent: rgba(255, 149, 0, 0.10);   /* tinted accent surface */
  --destructive: oklch(0.62 0.22 27);
  --border: rgba(0, 0, 0, 0.08);       /* theme divider */
  --input: rgba(0, 0, 0, 0.12);
  --overlay-hover: rgba(0, 0, 0, 0.06); /* subtle hover/press tint */
  --ring: #FF9500;                     /* theme ACCENT */
  --mint: oklch(0.84 0.1 165);
  --coral: oklch(0.78 0.13 30);
  --primary-soft: #ffb84d;             /* lighter accent - gradient end (was --violet) */
  --sky: oklch(0.82 0.08 225);

  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.04),
                 0 8px 24px -8px rgba(0, 0, 0, 0.08);
  --shadow-elev: 0 2px 4px rgba(0, 0, 0, 0.04),
                 0 16px 40px -12px rgba(0, 0, 0, 0.12);
  --shadow-focus: 0 0 0 4px rgba(255, 149, 0, 0.15);  /* theme ACCENT @ 15% */

  --font-display: "Plus Jakarta Sans", ui-sans-serif, system-ui, sans-serif;
  --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", ui-sans-serif, system-ui, sans-serif;  /* theme FONT_FAMILY */
}

/* ─── Dark palette ──────────────────────────────────────────────────
   Activated by `data-theme="dark"` on <html>, set by the host:
   index.html's bootstrap paints a best guess pre-connect, then main.js
   applies the host's authoritative `handle.theme` and follows
   `onThemeChange` live. Only color tokens are overridden here — radii,
   fonts and the geometry above are shared. The accent stays the same
   Reachy orange, nudged a touch brighter so it carries on a dark
   surface. */
[data-theme="dark"] {
  color-scheme: dark;

  --background: #0e0e10;
  --foreground: #f5f5f7;
  --surface: #1b1b1e;
  --surface-2: #2a2a2e;
  --card: #1b1b1e;
  --primary: #ff9f0a;                  /* accent, brightened for dark */
  --primary-foreground: #1a1205;       /* near-black reads better on orange */
  --muted: #2a2a2e;
  --muted-foreground: rgba(255, 255, 255, 0.62);
  --accent: rgba(255, 159, 10, 0.16);  /* tinted accent surface */
  --border: rgba(255, 255, 255, 0.12);
  --input: rgba(255, 255, 255, 0.18);
  --ring: #ff9f0a;
  --overlay-hover: rgba(255, 255, 255, 0.08);

  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.4),
                 0 8px 24px -8px rgba(0, 0, 0, 0.6);
  --shadow-elev: 0 2px 4px rgba(0, 0, 0, 0.45),
                 0 16px 40px -12px rgba(0, 0, 0, 0.7);
  --shadow-focus: 0 0 0 4px rgba(255, 159, 10, 0.22);
}

/* Dark-mode touch-ups. The tokens above re-skin every token-driven
   surface for free; these few selectors exist only because their
   backgrounds are fixed light gradients hand-tuned for a light backdrop,
   which would leave the token-driven text on top unreadable on dark. */
[data-theme="dark"] .sync-banner {
  background: linear-gradient(135deg, rgba(255, 196, 0, 0.10), rgba(255, 149, 0, 0.06));
  border-color: rgba(255, 196, 0, 0.22);
}
[data-theme="dark"] .sync-banner .ttl { color: oklch(0.9 0.07 85); }
[data-theme="dark"] .sync-banner-icon {
  background: rgba(255, 196, 0, 0.12);
  border-color: rgba(255, 196, 0, 0.24);
}
[data-theme="dark"] .row.new {
  background: linear-gradient(135deg, rgba(255, 149, 0, 0.08), rgba(255, 149, 0, 0.04));
}
[data-theme="dark"] .row .emoji,
[data-theme="dark"] .folder-head .emoji {
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.03));
}

* { box-sizing: border-box; border-color: var(--border); }
html, body {
  margin: 0;
  background: var(--background);
  color: var(--foreground);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
body {
  min-height: 100dvh;
}
h1, h2, h3, h4 { font-family: var(--font-display); letter-spacing: -0.02em; margin: 0; }
button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; }
input { font-family: inherit; }

/* ─── Layout shell (mobile-first) ─────────────────────── */

.app-shell {
  max-width: 28rem;
  margin: 0 auto;
  min-height: 100dvh;
  position: relative;
  padding-bottom: env(safe-area-inset-bottom);
}

.topbar {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: max(12px, env(safe-area-inset-top)) 12px 12px;
  background: color-mix(in oklab, var(--background) 85%, transparent);
  backdrop-filter: blur(10px);
}
/* Left slot reserves the back button's footprint so the cog stays pinned
   to the right edge even on roots where Back is hidden. Grows to take the
   leftover width so the page title can ellipsis instead of pushing the
   right-hand actions off-screen. */
.topbar-left {
  display: flex; align-items: center; gap: 2px;
  flex: 1 1 auto; min-width: 0; min-height: 40px;
}
.topbar-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
/* Title + optional subtitle, stacked. Takes the leftover width so both
   lines ellipsis instead of pushing the right-hand actions off-screen. */
.topbar-titles {
  display: flex; flex-direction: column; justify-content: center;
  min-width: 0; gap: 1px;
}
/* Page title (Material-style top app bar). Sits next to the Back chevron,
   replacing the explicit "Back" label when a view supplies a title. */
.topbar-title {
  font-family: var(--font-display, inherit);
  font-size: 22px; font-weight: 800; line-height: 1.15;
  color: var(--foreground);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  min-width: 0;
}
/* Muted one-line description under the title. */
.topbar-subtitle {
  font-size: 13px; font-weight: 500; line-height: 1.2;
  color: var(--muted-foreground);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  min-width: 0;
}
/* Roots have no Back chevron, so give their lone title block a little
   breathing room from the safe-area edge. */
.topbar-left > .topbar-titles:first-child { padding-left: 8px; }
/* Contextual "⋯" actions menu — primary-tinted, borderless icon button,
   matching the in-app menu trigger convention. */
.topbar-back, .topbar-menu, .topbar-settings { color: var(--primary); }
.topbar-back:hover, .topbar-menu:hover, .topbar-settings:hover { background: var(--accent); }
.icon-btn {
  width: 40px; height: 40px; border-radius: 999px;
  display: inline-grid; place-items: center;
  transition: background-color .15s ease, transform .15s ease;
}
.icon-btn:hover { background: var(--surface-2); }
.icon-btn:active { transform: scale(0.96); }
/* Labeled variant: outlined-primary icon + text pill (topbar Back / Settings). */
.icon-btn--labeled {
  width: auto; height: 40px;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 0 12px; border-radius: 12px;
  font-size: 14px; font-weight: 600;
  color: var(--primary);
  background: transparent;
}
.icon-btn--labeled:hover { background: var(--accent); }
.icon-btn-label { line-height: 1; }

/* ─── Step container ──────────────────────────────────── */

.step {
  padding: 8px 20px 140px;
}
.step h2 {
  font-size: 28px; font-weight: 800; line-height: 1.1; margin-top: 14px;
}
.step p.sub { margin-top: 6px; color: var(--muted-foreground); font-size: 15px; }

.card {
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--radius-2xl);
}

/* Announcement banner shown at the top of HomeView for the
   2026-05-25 sync architecture change. Dismissible. */
.sync-banner {
  position: relative;
  display: flex;
  gap: 12px;
  margin: 16px 20px 4px;
  padding: 14px 42px 14px 14px;
  background: linear-gradient(135deg, oklch(0.97 0.035 90), oklch(0.98 0.025 60));
  border: 1px solid oklch(0.88 0.07 82);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-card);
}
.sync-banner-icon {
  flex-shrink: 0;
  width: 34px; height: 34px;
  display: grid; place-items: center;
  font-size: 17px;
  border-radius: 10px;
  background: oklch(0.92 0.08 85 / 0.6);
  border: 1px solid oklch(0.85 0.09 82 / 0.5);
}
.sync-banner-content { min-width: 0; }
.sync-banner .ttl {
  font-weight: 700;
  font-size: 13.5px;
  line-height: 1.3;
  margin-bottom: 3px;
  color: oklch(0.38 0.06 70);
}
.sync-banner .body {
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--muted-foreground);
}
.sync-banner .dismiss {
  position: absolute;
  top: 8px; right: 8px;
  width: 26px; height: 26px;
  display: grid; place-items: center;
  border: none; background: transparent;
  border-radius: 50%;
  cursor: pointer;
  color: var(--muted-foreground);
  font-size: 17px; line-height: 1;
  transition: background-color .15s ease, color .15s ease;
}
.sync-banner .dismiss:hover { background: var(--overlay-hover); color: var(--foreground); }

.list { display: flex; flex-direction: column; gap: 10px; margin-top: 18px; }
.row {
  display: flex; align-items: center; gap: 12px;
  padding: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-card);
  transition: transform .15s ease, box-shadow .15s ease, border-color .15s ease;
  text-align: left;
  width: 100%;
}
.row { cursor: pointer; }
.row:active { transform: scale(0.99); }
.row.selected { border-color: var(--primary); box-shadow: var(--shadow-focus); }
/* "Default" pill on the default folder's title line. */
.row-badge {
  display: inline-block; margin-left: 8px;
  padding: 1px 7px; border-radius: 999px;
  font-size: 10px; font-weight: 700; letter-spacing: .02em;
  text-transform: uppercase; vertical-align: middle;
  color: var(--muted-foreground);
  background: var(--surface-2); border: 1px solid var(--border);
}
/* ⋯ more-actions button on a Mine folder row. */
.row-menu {
  flex-shrink: 0; width: 32px; height: 32px; border-radius: 10px;
  display: grid; place-items: center;
  font-size: 20px; line-height: 1;
  color: var(--primary);
  background: transparent;
  transition: background-color .15s ease, color .15s ease;
}
.row-menu:hover { background: var(--accent); color: var(--primary); }
.row .emoji {
  width: 44px; height: 44px; border-radius: 12px;
  display: grid; place-items: center; font-size: 22px;
  background: linear-gradient(135deg, oklch(0.95 0.02 260), oklch(0.97 0.02 285));
  flex-shrink: 0;
}
.row .grow { flex: 1; min-width: 0; }
.row .title { font-weight: 600; font-size: 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.row .meta { color: var(--muted-foreground); font-size: 12px; margin-top: 2px; }
.row .chev { color: var(--muted-foreground); flex-shrink: 0; }

/* Anchored popover menu (folder ⋯). Backdrop catches outside clicks. */
.menu-backdrop { position: fixed; inset: 0; z-index: 90; }
.menu-popover {
  position: fixed; z-index: 91; min-width: 176px; padding: 6px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; box-shadow: var(--shadow-card);
  animation: menu-pop .12s ease both;
}
@keyframes menu-pop {
  from { opacity: 0; transform: translateY(-4px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}
.menu-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 10px 12px; border-radius: 10px;
  font-size: 14px; font-weight: 600; color: var(--foreground);
  text-align: left; transition: background-color .12s ease;
}
.menu-item:hover { background: var(--accent); }
.menu-item.danger { color: var(--destructive); }
.menu-ico {
  width: 18px; height: 18px; flex-shrink: 0;
  display: grid; place-items: center;
}
.menu-ico svg { width: 18px; height: 18px; }

.row.new {
  background: linear-gradient(135deg, oklch(0.97 0.02 270), oklch(0.96 0.04 290));
  border: 1px dashed rgba(255, 149, 0, 0.4);
}
.row.new .emoji {
  background: linear-gradient(135deg, var(--primary), var(--primary-soft));
  color: white;
}

.segmented {
  display: inline-flex; padding: 4px;
  background: var(--surface-2); border-radius: 999px; gap: 4px;
  border: 1px solid var(--border);
}
.segmented button {
  padding: 8px 14px; border-radius: 999px; font-size: 13px; font-weight: 600;
  color: var(--muted-foreground);
}
.segmented button.active {
  background: var(--surface); color: var(--foreground); box-shadow: var(--shadow-card);
}

/* ─── Forms ───────────────────────────────────────────── */

.field { display: flex; flex-direction: column; gap: 6px; margin-top: 14px; }
.field label { font-size: 13px; color: var(--muted-foreground); font-weight: 600; }
.input, .textarea {
  width: 100%; padding: 15px 16px;
  background: transparent; color: var(--foreground);
  border: 1px solid var(--border); border-radius: 14px;
  font-size: 16px; outline: none; transition: box-shadow .15s ease, border-color .15s ease;
}
.input:focus { border-color: var(--primary); box-shadow: var(--shadow-focus); }

/* ─── Select (themed dropdown) ────────────────────────── */
/* Trigger mirrors .input metrics; the listbox opens in an anchored
   popover (see .select-popover) sized to the trigger width. */
.select {
  width: 100%; display: flex; align-items: center; gap: 10px;
  padding: 15px 16px; text-align: left;
  background: transparent; color: var(--foreground);
  border: 1px solid var(--border); border-radius: 14px;
  font-size: 16px; outline: none; cursor: pointer;
  transition: box-shadow .15s ease, border-color .15s ease;
}
.select:hover { border-color: color-mix(in oklab, var(--primary) 45%, var(--border)); }
.select[aria-expanded="true"] { border-color: var(--primary); box-shadow: var(--shadow-focus); }
.select-label {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.select-label.is-placeholder { color: var(--muted-foreground); }
.select-chevron { flex-shrink: 0; color: var(--muted-foreground); transition: transform .15s ease; }
.select[aria-expanded="true"] .select-chevron { transform: rotate(180deg); color: var(--primary); }

.select-popover {
  position: fixed; z-index: 91; max-height: 280px; overflow-y: auto;
  padding: 6px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; box-shadow: var(--shadow-card);
  animation: menu-pop .12s ease both;
}
.select-option {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 11px 12px; border-radius: 10px;
  font-size: 15px; font-weight: 600; color: var(--foreground);
  text-align: left; transition: background-color .12s ease;
}
.select-option:hover { background: var(--accent); }
.select-option.selected { color: var(--primary); }
.select-option-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.select-check { width: 16px; flex-shrink: 0; color: var(--primary); display: inline-grid; place-items: center; }

/* ─── Buttons ─────────────────────────────────────────── */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 14px 18px; border-radius: 14px; font-weight: 700; font-size: 15px;
  /* Fixed line-height so a button's height doesn't change whether or not
     its label contains an emoji (emoji glyphs have a taller line box). */
  line-height: 20px;
  background: var(--surface); color: var(--foreground);
  border: 1px solid var(--border); box-shadow: var(--shadow-card);
  transition: transform .15s ease, box-shadow .15s ease, background-color .15s ease;
}
.btn:active { transform: scale(0.98); }
.btn.primary {
  background: transparent;
  color: var(--primary);
  border: 1px solid var(--primary);
  box-shadow: none;
}
.btn.primary:hover { background: var(--accent); }
.btn.danger {
  background: transparent;
  color: var(--destructive);
  border: 1px solid var(--destructive);
  box-shadow: none;
}
.btn.danger:hover { background: color-mix(in oklab, var(--destructive) 12%, transparent); }
.btn.ghost { background: transparent; box-shadow: none; }
.btn[disabled] { opacity: 0.5; pointer-events: none; }
.btn-block { width: 100%; }

/* ─── Sticky footer ───────────────────────────────────── */

.footer {
  position: sticky; bottom: 0; left: 0; right: 0;
  padding: 12px 20px max(16px, env(safe-area-inset-bottom));
  background: color-mix(in oklab, var(--background) 85%, transparent);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--border);
}

/* ─── Step 2: sound tiles ─────────────────────────────── */

/* Sound type: 3 joined, outlined buttons (segmented), sharing borders. */
.tiles { display: flex; margin-top: 0; }
.tile {
  flex: 1; position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 14px 8px;
  background: transparent; color: var(--foreground);
  border: 1px solid var(--border); box-shadow: none;
  text-align: center;
  transition: border-color .15s ease, color .15s ease, background-color .15s ease;
}
.tile:not(:first-child) { margin-left: -1px; }
.tile:first-child { border-radius: var(--radius-xl) 0 0 var(--radius-xl); }
.tile:last-child { border-radius: 0 var(--radius-xl) var(--radius-xl) 0; }
.tile.active {
  border-color: var(--primary); color: var(--primary);
  background: var(--accent); z-index: 1;
}
.tile .ico {
  width: 36px; height: 36px; border-radius: 10px; display: grid; place-items: center;
  background: linear-gradient(135deg, var(--accent), var(--surface-2));
  font-size: 18px;
}
.tile .ttl { font-weight: 700; font-size: 13px; }
.tile .desc { font-size: 11px; line-height: 1.25; color: var(--muted-foreground); }

/* Loaded-file block for the upload picker: outlined like the buttons,
   left-aligned, showing the file name + a "Replace" affordance. */
.file-picker {
  display: flex; align-items: center; gap: 12px;
  width: 100%; padding: 12px 16px; margin-top: 14px;
  background: transparent; color: var(--foreground);
  border: 1px solid var(--primary); border-radius: 14px;
  text-align: left;
}
.file-picker .fp-ico { font-size: 18px; flex-shrink: 0; }
.file-picker .fp-body { flex: 1; min-width: 0; }
.file-picker .fp-name {
  display: block; font-weight: 700; font-size: 14px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.file-picker .fp-meta {
  display: block; font-size: 12px; color: var(--muted-foreground);
  font-variant-numeric: tabular-nums;
}
.file-picker .fp-action {
  flex-shrink: 0; font-size: 13px; font-weight: 700; color: var(--primary);
}

/* Themed range slider — default browser sliders render in accent blue,
   which clashes with the Reachy-orange theme. Track in --surface-2,
   thumb in the primary gradient. */
/* Outlined slider, matching the app's outlined-primary language: a thin
   bordered track (more visible than a flat 4px line) and a smaller knob with
   a transparent-ish fill ringed by a 2px primary border, instead of the old
   chunky gradient disc. Track is drawn via the runnable-track pseudo so the
   thumb can be centered precisely across browsers. */
.slider {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 20px;
  background: transparent;
  outline: none; cursor: pointer;
}
.slider::-webkit-slider-runnable-track {
  height: 6px; border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
}
.slider::-moz-range-track {
  height: 6px; border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
}
.slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--background);
  border: 2px solid var(--primary);
  box-shadow: 0 1px 2px oklch(0 0 0 / 0.14);
  cursor: pointer;
  transition: background-color .12s ease, box-shadow .12s ease;
  /* Center the 16px(+2px border)=20px knob on the 6px(+1px border)=8px track. */
  margin-top: -6px;
}
.slider::-moz-range-thumb {
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--background);
  border: 2px solid var(--primary);
  box-shadow: 0 1px 2px oklch(0 0 0 / 0.14);
  cursor: pointer;
}
.slider:hover::-webkit-slider-thumb { background: var(--accent); }
.slider:hover::-moz-range-thumb { background: var(--accent); }
.slider:active::-webkit-slider-thumb {
  background: var(--accent);
  box-shadow: 0 0 0 4px var(--accent);
}
.slider:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 4px var(--accent);
}

/* Mic sub-selector (inside "Record now") — two joined, outlined buttons
   sharing borders, full width. Same treatment as the sound-type buttons. */
/* Joined, outlined segmented buttons. Shared by the record-now mic
   picker (.mic-sub) and the generic tab component (.tabs, see
   views/components/tabs.js) so Mine/Community + the community sort match
   the "This device / Robot mic" look. */
.mic-sub { display: flex; margin-top: 12px; }
.tabs { display: flex; }
.mic-sub .opt,
.tabs .opt {
  /* Match a normal .btn's box metrics so these sit at the same height. */
  flex: 1; position: relative; padding: 14px 18px;
  background: transparent; color: var(--foreground);
  border: 1px solid var(--border);
  text-align: center; font-size: 15px; font-weight: 600;
  transition: border-color .15s, color .15s, background-color .15s;
}
.mic-sub .opt:not(:first-child),
.tabs .opt:not(:first-child) { margin-left: -1px; }
.mic-sub .opt:first-child,
.tabs .opt:first-child { border-radius: 14px 0 0 14px; }
.mic-sub .opt:last-child,
.tabs .opt:last-child { border-radius: 0 14px 14px 0; }
.mic-sub .opt.active,
.tabs .opt.active {
  border-color: var(--primary); color: var(--primary);
  background: var(--accent); z-index: 1;
}
.mic-sub .opt[disabled],
.tabs .opt[disabled] { opacity: 0.45; pointer-events: none; }

/* Live mic-input meter, embedded inside the Stop button while recording.
   Bars use the button's text color (white on the danger button), kept
   faint and short so it stays discreet next to the label. */
.mic-meter {
  display: inline-flex; align-items: center; gap: 2px;
  height: 14px;
}
.mic-meter span {
  width: 2px; height: 2px; border-radius: 1px;
  background: currentColor; opacity: 0.7;
  transition: height .07s linear;
}

/* ─── Step 3: recording stage ─────────────────────────── */

.stage {
  position: relative;
  margin-top: 18px;
  /* Fixed height — the card NEVER resizes between idle / countdown /
     recording. The illustration is flex-centered; everything else (badge,
     countdown, progress readout) is an absolute overlay so nothing reflows. */
  height: 260px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--radius-xl);
  background: var(--surface);
  border: 1px solid var(--border);
  padding: 20px;
  text-align: center;
  box-shadow: var(--shadow-card);
  overflow: hidden;
  transition: border-color .25s ease, box-shadow .25s ease;
}
/* Active capture: warm the frame so the recording state reads at a
   glance even if the REC badge scrolls off. */
.stage.rec {
  border-color: color-mix(in oklch, var(--destructive) 45%, var(--border));
  box-shadow: var(--shadow-card), 0 0 0 3px oklch(0.62 0.22 27 / 0.12);
}

/* Bottom overlay holding the live status + progress readout. Absolute so it
   floats over the card's lower edge and never affects the card height. */
.stage-readout {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 0 16px 12px;
}

/* Hero: the Reachy-head-in-human-hands illustration. Fixed aspect box
   so the layout doesn't jump while the asset loads, and so the
   countdown overlay + REC badge have a stable canvas to sit on. */
.stage-hero {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  max-height: 190px;
  margin: 2px auto 0;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform .25s ease;
}
.stage-illu {
  height: 100%;
  max-width: 100%;
  display: grid;
  place-items: center;
  filter: drop-shadow(0 12px 26px rgba(0, 0, 0, 0.18));
  transition: filter .25s ease, opacity .25s ease, transform .25s ease;
}
/* Inlined line-art: fit by height, keep strokes crisp + theme-aware
   regardless of the large viewBox downscale. */
.stage-illu > svg {
  display: block;
  height: 100%;
  width: auto;
  max-width: 100%;
}
/* While counting down, push the illustration back so the big 3·2·1
   number owns the frame. */
.stage-hero.counting .stage-illu {
  filter: drop-shadow(0 12px 26px rgba(0, 0, 0, 0.18)) blur(3px);
  opacity: 0.45;
  transform: scale(0.97);
}
/* No asset yet (or load failure): an ambient glow placeholder so the
   hero box still reads as intentional rather than empty/broken. */
.stage-hero--empty::after {
  content: "";
  position: absolute; left: 50%; top: 50%;
  width: 150px; height: 150px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle at 50% 42%, var(--primary-soft), transparent 70%);
  opacity: 0.45;
}

.countdown {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display); font-size: 110px; font-weight: 800; line-height: 1;
  background: linear-gradient(135deg, var(--primary), var(--primary-soft));
  -webkit-background-clip: text; background-clip: text; color: transparent;
  pointer-events: none;
}

.rec-badge {
  position: absolute; top: 14px; right: 14px; z-index: 2;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 12px; border-radius: 999px;
  background: oklch(0.62 0.22 27 / 0.12); color: var(--destructive);
  font-weight: 700; font-size: 12px;
}
.rec-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--destructive); animation: pulse-ring 1.6s ease-out infinite; }

/* Live elapsed-time readout under the record-now Stop button. */
.rec-elapsed {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin-top: 10px;
  color: var(--destructive); font-weight: 700; font-size: 15px;
  font-variant-numeric: tabular-nums;
}
.rec-elapsed[hidden] { display: none; }

/* Status label (warnings, "Get ready…", live Hz) pinned top-left as an
   absolute overlay so it never shifts the centered illustration. The REC
   badge owns the top-right corner. */
.stage-status {
  position: absolute; top: 14px; left: 16px; z-index: 2;
  max-width: calc(100% - 110px);
  margin: 0; text-align: left;
  font-weight: 700; font-size: 13px; line-height: 1.3;
  color: var(--foreground);
}
.stage-status[hidden] { display: none; }
.capture-meters[hidden] { display: none; }

/* Capture guidance: a horizontal stepper sitting BELOW the (fixed-height)
   stage card. Each step lights up as the capture phase advances; earlier
   steps read as "done". A single helper line under it mirrors the active
   step's description. */
.stepper {
  list-style: none; margin: 22px 0 0; padding: 0;
  display: flex; align-items: flex-start;
}
.stepper-step {
  flex: 1; position: relative;
  display: flex; flex-direction: column; align-items: center; gap: 9px;
  text-align: center; min-width: 0;
  color: var(--muted-foreground);
}
/* Connector line running from this dot to the next one. Spans the full gap
   between adjacent dots so the stepper reads edge-to-edge. */
.stepper-step:not(:last-child)::after {
  content: ""; position: absolute;
  top: 15px; left: calc(50% + 20px); right: calc(-50% + 20px); height: 2px;
  background: color-mix(in oklab, var(--primary) 20%, transparent);
  transition: background .2s ease;
}
.stepper-dot {
  flex-shrink: 0;
  width: 32px; height: 32px; border-radius: 999px;
  display: grid; place-items: center;
  font-size: 13.5px; font-weight: 800;
  color: var(--muted-foreground);
  background: var(--surface-2);
  border: 2px solid color-mix(in oklab, var(--primary) 20%, transparent);
  transition: color .2s ease, background .2s ease, border-color .2s ease, box-shadow .2s ease;
}
.stepper-label {
  font-size: 13px; font-weight: 600; line-height: 1.2;
  max-width: 100%;
}
.stepper-step.is-active .stepper-dot {
  color: var(--primary-foreground); background: var(--primary); border-color: var(--primary);
  box-shadow: 0 0 0 4px var(--primary-soft);
}
.stepper-step.is-active .stepper-label { color: var(--foreground); font-weight: 700; }
.stepper-step.is-done .stepper-dot {
  color: var(--primary); background: var(--primary-soft); border-color: var(--primary);
}
.stepper-step.is-done .stepper-label { color: var(--foreground); }
.stepper-step.is-done::after { background: var(--primary); }
.stepper-desc {
  margin: 16px auto 0; max-width: 30rem; text-align: center;
  font-size: 13.5px; line-height: 1.5; color: var(--muted-foreground);
  min-height: 3em;
}
.stepper-desc strong { color: var(--foreground); font-weight: 700; }
/* Capture progress bar — a full-width strip flush against the card's bottom
   edge (the card's overflow:hidden + radius round its corners). Only visible
   while recording. */
.stage-progress {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 5px; background: var(--surface-2);
  opacity: 0; transition: opacity .2s ease;
}
.stage.rec .stage-progress { opacity: 1; }
.stage-progress > span {
  display: block; height: 100%; width: 0%;
  background: linear-gradient(90deg, var(--primary), var(--primary-soft));
  transition: width .15s linear;
}
/* Elapsed / total readout, pinned bottom-left and bottom-right just above the
   progress strip. */
.capture-meters .time {
  display: flex; justify-content: space-between; color: var(--muted-foreground);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
}

/* ─── Folder view ─────────────────────────────────────── */

.folder-head {
  display: flex; align-items: center; gap: 12px; padding: 8px 20px 4px;
}
.folder-head .emoji {
  width: 52px; height: 52px; border-radius: 14px; display: grid; place-items: center; font-size: 26px;
  background: linear-gradient(135deg, oklch(0.95 0.02 260), oklch(0.97 0.02 285));
  flex-shrink: 0;
}
.folder-head .grow { flex: 1; min-width: 0; }
.folder-head .title {
  font-weight: 800; font-size: 20px; font-family: var(--font-display);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.folder-head .meta { color: var(--muted-foreground); font-size: 13px; }
.folder-head .row-menu { width: 36px; height: 36px; font-size: 22px; }

/* Folder view: primary CTA, moves section, and the collapsible
   "Playback settings" disclosure. */
/* Folder + Home views: flex the shell + view so the primary CTA docks at the
   bottom (thumb zone) while the list stays top-aligned and scrolls. */
.app-shell--folder,
.app-shell--home { display: flex; flex-direction: column; }
.folder-view,
.home-view { flex: 1; display: flex; flex-direction: column; }

/* Home reuses the .step shell (140px reserved bottom padding meant for a
   floating CTA); with the dock now in flow, drop that padding and instead
   reserve space under the list so the last folder clears the sticky dock. */
.home-view { padding-bottom: 0; }
.home-view #folder-list { padding-bottom: 88px; }

/* Docked primary action: margin-top:auto pushes it to the bottom when content
   is short; position:sticky keeps it visible above the scrolling list when the
   content overflows. The trailing .disclosure reserves padding-bottom so its
   last row isn't hidden behind the dock. */
.cta-dock {
  margin-top: auto;
  position: sticky;
  bottom: 0;
  z-index: 5;
  padding: 16px 20px max(20px, env(safe-area-inset-bottom));
  background: color-mix(in oklab, var(--background) 88%, transparent);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--border);
}

.folder-body { padding: 10px 20px 4px; }
.folder-body .list { margin-top: 10px; }
.section-label {
  font-size: 12px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted-foreground);
}

/* Playback controls (volume + per-folder audio sync) live in the bottom
   dock, stacked above the primary CTA. The dock owns the outer padding, so
   here we just stack the rows and add a little breathing room below before
   the record button. */
.folder-playback {
  display: flex; flex-direction: column; gap: 10px;
  margin-bottom: 14px;
}
.folder-playback .section-label { margin-bottom: 2px; }
/* Keep the card chrome but let it span the full section width: the only
   undesirable inset was the volume-row's horizontal margin (20px). */
.folder-playback .volume-row {
  margin: 0;
}

/* Folder empty state: icon badge + title + hint, centered. */
.folder-empty {
  text-align: center; padding: 24px 20px 4px;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.folder-empty-ico {
  width: 64px; height: 64px; border-radius: 18px;
  display: grid; place-items: center; font-size: 30px;
  background: var(--surface-2); border: 1px solid var(--border);
}
.folder-empty-title { font-weight: 800; font-size: 17px; font-family: var(--font-display); }
.folder-empty-sub { color: var(--muted-foreground); font-size: 13px; line-height: 1.45; max-width: 260px; }

.disclosure { padding-bottom: max(96px, env(safe-area-inset-bottom)); }
.disclosure-toggle {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; padding: 14px 20px;
  color: var(--foreground); font-size: 14px; font-weight: 600;
}
.disclosure-toggle:hover { color: var(--primary); }
.disclosure-chev { color: var(--muted-foreground); transition: transform .2s ease; }
.disclosure.open .disclosure-chev { transform: rotate(180deg); }
.disclosure-body { padding-bottom: 6px; }

/* Generic move card — used in the folder list and the review screen.
   Leading icon + name/meta + optional play toggle + contextual action
   buttons, with a dedicated progress bar pinned to the top edge. */
.move-card {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: 12px; padding: 12px 14px;
  border-radius: var(--radius-xl); border: 1px solid var(--border); background: var(--surface);
  box-shadow: var(--shadow-card);
}
.move-card .move-card-row {
  display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0;
}
.move-card.highlight {
  border-color: var(--primary); box-shadow: var(--shadow-focus);
}
.move-card-play {
  width: 40px; height: 40px; border-radius: 12px;
  display: grid; place-items: center;
  background: linear-gradient(135deg, var(--primary), var(--primary-soft));
  color: white; font-size: 15px;
  flex-shrink: 0;
}
.move-card-play:disabled { opacity: 0.4; cursor: not-allowed; }
/* Loading state: the play button shows a spinner while the move is being
   prepared/uploaded (no progress bar during this phase). */
.move-card.is-loading .move-card-play { color: transparent; position: relative; }
.move-card.is-loading .move-card-play::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  width: 16px; height: 16px; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.35); border-top-color: #fff;
  animation: move-card-spin 0.7s linear infinite;
}
@keyframes move-card-spin { to { transform: rotate(360deg); } }
.move-card-ico {
  width: 34px; height: 34px; flex-shrink: 0;
  display: grid; place-items: center;
  font-size: 18px;
  border-radius: 10px;
  background: var(--surface-2);
}
.move-card-body { flex: 1; min-width: 0; }
.move-card-title { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.move-card-meta { color: var(--muted-foreground); font-size: 12px; }
.move-card-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
/* Compact outlined action buttons on a move card — same primary button
   language as the rest of the app, sized down to sit inside the row. */
.move-card-actions .btn.move-action {
  padding: 7px 12px; gap: 6px;
  font-size: 13px; border-radius: 10px;
  box-shadow: none;
}
.move-card-actions .btn.move-action svg { width: 16px; height: 16px; }
.move-card-actions .btn.move-action.icon-only {
  padding: 0; width: 36px; height: 36px;
}
/* "Shared" state: swap the outline to the success accent. */
.move-card-actions .btn.move-action.shared {
  color: oklch(0.5 0.12 165);
  border-color: oklch(0.5 0.12 165);
}
.move-card-actions .btn.move-action.shared:hover {
  background: oklch(0.84 0.1 165 / 0.16);
}

/* Playback progress — a thin bar flush to the top edge of the card.
   Hidden (no track) until activated, so idle cards show nothing. */
.move-progress {
  position: absolute; top: 0; left: 0; right: 0;
  height: 3px;
  background: var(--surface-2);
  z-index: 2;
  pointer-events: none;
  overflow: hidden;
  display: none;
}
.move-progress.is-active { display: block; }
.move-progress > span {
  display: block; height: 100%; width: 0%;
  background: linear-gradient(90deg, var(--primary), var(--primary-soft));
  transition: width 80ms linear;
}

.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 10px; border-radius: 999px; font-size: 12px; font-weight: 600;
  background: var(--surface-2); color: var(--muted-foreground);
  border: 1px solid var(--border);
}
.chip.shared { background: oklch(0.84 0.1 165 / 0.2); color: oklch(0.4 0.12 165); border-color: transparent; }

/* ─── Empty, toast ────────────────────────────────────── */

.empty {
  text-align: center; padding: 40px 20px; color: var(--muted-foreground);
}

/* Toast: a single status card, bottom-center. Primary-outlined (accent
   border + soft accent halo) so it stays legible when it lands over
   similar surface cards. Accent is theme-aware and per-kind. The bottom
   bar runs in 3 modes — countdown / determinate / indeterminate (see
   components/toast.js). */
.toast {
  --toast-accent: var(--primary);
  position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%);
  display: flex; align-items: center; gap: 12px;
  /* Span the full app width, aligned with the content cards' 20px gutters
     (.app-shell is 28rem; on phones it tracks the viewport edges). */
  width: min(calc(28rem - 40px), calc(100vw - 40px));
  padding: 16px 16px 14px;
  background: color-mix(in oklab, var(--toast-accent) 12%, var(--surface));
  color: var(--foreground);
  border: 1.5px solid var(--toast-accent);
  border-radius: 16px;
  box-shadow: var(--shadow-elev);
  z-index: 80; overflow: hidden;
  font-size: 14px; font-weight: 600; line-height: 1.35;
  animation: toast-in .26s cubic-bezier(.21, 1.02, .73, 1) both;
}
.toast.success { --toast-accent: oklch(0.55 0.13 165); }
.toast.error { --toast-accent: var(--destructive); }
.toast.toast-out { animation: toast-out .2s ease forwards; }
.toast-msg { flex: 1; min-width: 0; overflow-wrap: anywhere; }
/* With a trailing metric (live Hz), keep the message on a single line so the
   appearing number can't push it to two lines — ellipsis long names instead. */
.toast.has-detail .toast-msg {
  overflow-wrap: normal; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
/* Trailing metric (e.g. live "100 Hz" during playback): muted, monospaced,
   fixed min-width so the digits don't jitter the layout as they tick. */
.toast-detail {
  flex-shrink: 0;
  min-width: 54px; text-align: center;
  font: 600 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--muted-foreground);
  font-variant-numeric: tabular-nums;
  padding: 3px 8px; border-radius: 999px;
  background: color-mix(in oklab, var(--toast-accent) 14%, transparent);
}
.toast-icon {
  flex-shrink: 0; width: 22px; height: 22px; border-radius: 999px;
  display: grid; place-items: center;
  font-size: 13px; font-weight: 800; color: white;
  background: var(--toast-accent);
}
/* Loading spinner inside the toast icon chip — same pattern as the move-card
   play button (reuses the move-card-spin keyframes). */
.toast-spinner {
  width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, 0.4); border-top-color: #fff;
  animation: move-card-spin 0.7s linear infinite;
}
.toast-close {
  flex-shrink: 0; width: 26px; height: 26px; margin: -4px -6px -4px 0;
  border-radius: 999px; font-size: 19px; line-height: 1;
  color: var(--muted-foreground);
  display: grid; place-items: center;
  transition: background-color .15s ease, color .15s ease;
}
.toast-close:hover { background: var(--surface-2); color: var(--foreground); }

/* Top progress bar (track + fill). */
.toast-bar {
  position: absolute; left: 0; right: 0; top: 0; height: 3px;
  background: color-mix(in oklab, var(--toast-accent) 18%, transparent);
  overflow: hidden;
}
.toast-bar > i {
  display: block; height: 100%; background: var(--toast-accent);
  transform-origin: left center;
}
/* countdown: drains 100% → 0% over the toast's lifetime (duration set inline). */
.toast-bar.countdown > i { transform: scaleX(1); animation: toast-countdown linear forwards; }
@keyframes toast-countdown { from { transform: scaleX(1); } to { transform: scaleX(0); } }
/* determinate: width = progress fraction (transform set inline per tick).
   The bar node is preserved across in-place refreshes (see toast.js), so a
   short linear transition smooths the stepped updates into a steady fill —
   used by both the import progress and the live playback bar. */
.toast-bar.determinate > i { transform: scaleX(0); transition: transform .16s linear; }
/* indeterminate: a short segment looping for unknown-length operations. */
.toast-bar.indeterminate > i {
  width: 40%; transform: translateX(-120%);
  animation: toast-indet 1.15s ease-in-out infinite;
}
@keyframes toast-indet {
  0%   { transform: translateX(-120%); }
  100% { transform: translateX(280%); }
}
@keyframes toast-in {
  from { opacity: 0; transform: translateX(-50%) translateY(12px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}
@keyframes toast-out {
  from { opacity: 1; transform: translateX(-50%) translateY(0); }
  to   { opacity: 0; transform: translateX(-50%) translateY(12px); }
}

/* ─── Connection gate ─────────────────────────────────── */

.gate {
  display: grid; place-items: center;
  min-height: 100dvh;
  padding: 20px;
}
.gate-card {
  max-width: 26rem;
  width: 100%;
  padding: 32px 24px;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-2xl);
  box-shadow: var(--shadow-elev);
}
.gate-card .big-emoji { font-size: 64px; }
/* Image variant of .big-emoji (the Marionette logo SVG). The div
   variant above keeps its 64px font sizing for the robot-picker emoji. */
.gate-card img.big-emoji {
  width: 96px; height: 96px;
  object-fit: contain;
  display: block;
  margin: 0 auto 4px;
}
.gate-card h1 {
  font-family: var(--font-display); font-weight: 800; font-size: 28px;
  margin-top: 6px;
}
.gate-card p { color: var(--muted-foreground); font-size: 15px; margin-top: 6px; }
.gate-card .btn { margin-top: 18px; }
.gate-card .divider {
  margin: 22px 0 6px;
  color: var(--muted-foreground);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 1px;
  position: relative;
}
.gate-card .divider::before,
.gate-card .divider::after {
  content: "";
  position: absolute; top: 50%;
  width: calc(50% - 30px);
  height: 1px; background: var(--border);
}
.gate-card .divider::before { left: 0; }
.gate-card .divider::after { right: 0; }

.robot-list { display: grid; gap: 8px; margin-top: 14px; text-align: left; }

/* ─── Settings sheet ──────────────────────────────────── */

.sheet-backdrop {
  position: fixed; inset: 0; background: oklch(0.18 0.02 260 / 0.4);
  z-index: 60; animation: fade-in .2s ease both;
}
/* Settings: a full-screen page that pushes in from the right (native
   "settings page" feel) instead of a cramped bottom sheet. Sticky header
   (Back chevron + title) mirrors the app topbar; the body scrolls on its
   own so the long list of rows has full height to breathe. */
.settings-page {
  position: fixed; inset: 0; z-index: 70;
  max-width: 28rem; margin: 0 auto;
  background: var(--background); color: var(--foreground);
  display: flex; flex-direction: column;
}
.settings-head {
  position: sticky; top: 0; z-index: 1;
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
  /* Vertical padding matches the global topbar (12px top/bottom) so the
     settings header is exactly the same height. Right padding (12px) keeps
     the close X over the cog it covers; left padding (20px) lines the title
     up with the rows below. */
  padding: max(12px, env(safe-area-inset-top)) 12px 12px 20px;
  background: color-mix(in oklab, var(--background) 85%, transparent);
  backdrop-filter: blur(10px);
}
.settings-title {
  font-family: var(--font-display); font-weight: 800; font-size: 22px;
}
.settings-head .icon-btn { color: var(--primary); }
.settings-head .icon-btn:hover { background: var(--accent); }
.settings-body {
  flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding: 4px 20px max(24px, env(safe-area-inset-bottom));
}
.settings-page .row-setting {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 0;
  border-bottom: 1px solid var(--border);
  gap: 12px;
}
.settings-page .row-setting:last-child { border-bottom: none; }
.settings-page .row-setting .label { font-weight: 600; font-size: 14px; }
.settings-page .row-setting .sublabel { color: var(--muted-foreground); font-size: 12px; margin-top: 2px; }
/* Stacked variant: label/help on top, a full-width control (tabs) below.
   Used for the Audio output + Debug HUD segmented controls. */
.settings-page .row-setting.stack { flex-direction: column; align-items: stretch; gap: 10px; }

.segmented.small button { padding: 6px 10px; font-size: 12px; }

/* ─── Animations ──────────────────────────────────────── */

@keyframes float-y { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-8px)} }
@keyframes pulse-ring {
  0% { box-shadow: 0 0 0 0 oklch(0.62 0.22 27 / 0.4); }
  70% { box-shadow: 0 0 0 16px oklch(0.62 0.22 27 / 0); }
  100% { box-shadow: 0 0 0 0 oklch(0.62 0.22 27 / 0); }
}
@keyframes slide-up { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

/* ----------------------------------------------------------------------
   Waveform trim widget: a decoded waveform canvas with a start bracket,
   an end bracket, and a draggable playhead. A tap on the waveform toggles
   play/pause of the selected region.
   ----------------------------------------------------------------------

   Layout in pixels:
     - Outer .trim-bar: 72 px tall, 12 px horizontal padding.  The
       padding equals half the widest handle (24 px) so when start is
       at 0 or end is at dur the handles still sit inside the card.
     - .trim-wave: the <canvas> waveform; bars inside the selection are
       drawn in --primary, bars outside are dimmed.
     - .trim-region: a faint highlight band between the brackets.
    - .trim-handle: 32 px wide hit area (>=44 px on phones via media
      query) so taps land reliably.  Visible shape is a single thin
      vertical bar drawn with ::before.
    - .trim-playhead-new: a thin vertical line marking playback position.
*/
.trim-bar {
  position: relative;
  height: 72px;
  padding: 0 12px;          /* gutter == half the widest handle */
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  cursor: pointer;
}
.trim-wave {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  z-index: 0;
}
.trim-region {
  /* No fill: the waveform bars already color the selection. Kept as a
     zero-cost layout marker for the bracket handles. */
  position: absolute;
  top: 6px; bottom: 6px;
  background: transparent;
  z-index: 1;
  pointer-events: none;
}
.trim-handle {
  position: absolute;
  top: 0; bottom: 0;
  width: 32px;              /* wide hit area for touch */
  margin-left: -16px;       /* center on the time x */
  cursor: ew-resize;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
}
@media (hover: none) and (pointer: coarse) {
  /* Phones: even wider hit area. */
  .trim-handle { width: 44px; margin-left: -22px; }
}
.trim-handle::before {
  /* Vertical spine of the bracket. */
  content: "";
  position: absolute;
  width: 6px;
  top: 6px; bottom: 6px;
  background: var(--primary);
  border-radius: 3px;
  box-shadow: 0 0 0 1px var(--background), 0 1px 3px rgba(0,0,0,0.18);
}
.trim-handle:hover::before { filter: brightness(1.1); }
.trim-handle.dragging::before { box-shadow: 0 0 0 2px var(--background), 0 2px 6px rgba(0,0,0,0.25); }

.trim-playhead-new {
  position: absolute;
  top: 0; bottom: 0;
  width: 28px;              /* hit area, knob is smaller */
  margin-left: -14px;
  cursor: grab;
  display: none;            /* shown once a cursor exists */
  z-index: 3;
}
.trim-playhead-new.on { display: block; }
.trim-playhead-new.dragging { cursor: grabbing; }
.trim-playhead-new::before {
  /* The thin red vertical line itself. */
  content: "";
  position: absolute;
  top: 6px; bottom: 6px;
  left: 50%;
  width: 2px;
  margin-left: -1px;
  background: var(--destructive);
  box-shadow: 0 0 6px var(--destructive);
  border-radius: 1px;
  pointer-events: none;
}
.trim-link {
  background: transparent; border: none; padding: 0;
  font-size: 13px; font-weight: 600; color: var(--primary);
  /* Fixed line-height so swapping the ▶ / ⏸ glyph (which have different
     intrinsic heights) doesn't change the row height. */
  line-height: 20px; height: 20px;
  cursor: pointer;
}
.trim-link:hover { text-decoration: underline; }

/* Generic slider field: a leading icon, the themed range (.slider), and
   a right-aligned value readout. One row, layout-neutral — wrap it in a
   card (.volume-row) or drop it straight into a form. */
.slider-field {
  display: flex; align-items: center; gap: 10px;
}
.slider-field .slider { flex: 1; }
/* Disabled (e.g. the sync-offset slider while a move is playing): dim and
   block interaction, but keep the value readout legible. */
.slider-field.is-disabled { opacity: 0.5; }
.slider-field.is-disabled .slider { pointer-events: none; cursor: not-allowed; }
.slider-field .sf-ico {
  width: 28px; height: 28px;
  display: grid; place-items: center;
  font-size: 16px; flex-shrink: 0;
}
.slider-field .sf-val {
  min-width: 40px; text-align: right;
  font-variant-numeric: tabular-nums;
  font-size: 13px; font-weight: 600;
  color: var(--muted-foreground);
}

/* Card presentation for a slider field — the speaker/mic volume and the
   per-folder audio offset bars at the top of Home/Folder, always within
   reach when playing moves. */
.volume-row {
  padding: 10px 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-card);
  margin: 14px 20px 0;
}

/* In-app dialogs (confirm / prompt) — themed alternative to the
   native browser pop-ups, which look out-of-place. */
.dialog-backdrop {
  position: fixed; inset: 0; background: oklch(0.18 0.02 260 / 0.5);
  z-index: 100; animation: fade-in .18s ease both;
}
.dialog {
  position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-2xl);
  box-shadow: var(--shadow-elev);
  z-index: 110;
  width: min(420px, calc(100vw - 32px));
  padding: 22px 22px 18px;
  animation: dialog-pop .2s ease both;
}
.dialog h3 {
  font-family: var(--font-display); font-weight: 800; font-size: 18px;
  margin-bottom: 8px;
}
.dialog p {
  margin: 0; color: var(--muted-foreground); font-size: 14px;
  line-height: 1.45;
}
.dialog .input {
  margin-top: 14px;
}
.dialog-actions {
  display: flex; gap: 10px; margin-top: 20px;
}
/* Every dialog button is outlined regardless of its semantic kind: a
   transparent fill with a 1px border + text tinted to its color. Keeps the
   modal calm and consistent (no heavy filled buttons), while the border/text
   color still signals intent (neutral cancel, primary confirm, danger). */
.dialog-actions .btn {
  flex: 1; padding: 12px 14px;
  background: transparent;
  box-shadow: none;
}
.dialog-actions .btn.ghost {
  border: 1px solid var(--border);
  color: var(--foreground);
}
.dialog-actions .btn.ghost:hover { background: var(--surface-2); }
.dialog-actions .btn.primary {
  border: 1px solid var(--primary);
  color: var(--primary);
}
.dialog-actions .btn.primary:hover { background: var(--accent); }
.dialog-actions .btn.danger {
  border: 1px solid var(--destructive);
  color: var(--destructive);
}
.dialog-actions .btn.danger:hover {
  background: color-mix(in oklab, var(--destructive) 12%, transparent);
}

@keyframes dialog-pop {
  from { opacity: 0; transform: translate(-50%, calc(-50% + 8px)) scale(.97); }
  to   { opacity: 1; transform: translate(-50%, -50%)              scale(1); }
}

/* Tiny debug HUD (toggled from settings) */
.debug-hud {
  position: fixed;
  right: 8px; bottom: 8px;
  background: rgba(0,0,0,0.72);
  color: #fff;
  font-family: ui-monospace, monospace;
  font-size: 11px;
  padding: 6px 10px;
  border-radius: 8px;
  z-index: 90;
  pointer-events: none;
  line-height: 1.4;
}

/* ─── Welcome / onboarding ─────────────────────────────── */
.welcome {
  text-align: center;
  padding-bottom: 48px;
}

/* Vertical centering: the shell becomes a flex column for this view only
   (set in main.js via .app-shell--welcome) and the onboarding stack grows
   to fill the height below the topbar, centering its content. */
.app-shell--welcome { display: flex; flex-direction: column; }
.app-shell--welcome .welcome {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-bottom: 0;
}

/* Wizard create steps: center the [form body + action row] together so
   the validation button sits directly below the form, not pinned to the
   viewport bottom. `margin-top: auto` above the body and `margin-bottom:
   auto` below the footer split the free space evenly, centering the whole
   group. When the content is taller than the viewport both auto margins
   collapse to 0 and it scrolls from the top. The footer drops its sticky
   bar chrome here and flows inline. Scoped to the wizard via the modifier
   main.js adds. */
.app-shell--wizard { display: flex; flex-direction: column; }
.app-shell--wizard .step {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding-bottom: 0;
}
/* Body content sits flush under the topbar (top-aligned). The primary
   actions get pushed to the bottom via the .footer rule below, giving the
   standard 3-zone mobile layout (header / scrolling content / thumb-zone CTA). */
.app-shell--wizard .wiz-body { margin-top: 0; width: 100%; }
.app-shell--wizard .wiz-body--top { margin-top: 0; }
.app-shell--wizard .footer {
  /* 3-zone mobile layout: body top-aligned, primary actions pinned to the
     bottom (thumb zone). margin-top:auto absorbs the slack above the footer
     so it sits at the bottom of the shell while the body stays flush at top. */
  margin-top: auto;
  position: static;
  background: none;
  backdrop-filter: none;
  border-top: 0;
  /* No horizontal padding: the parent .step already pads 20px, so the
     action row aligns with the form fields and the two buttons span the
     full content width. Extra top padding separates the actions from the
     form above. */
  padding: 32px 0 max(16px, env(safe-area-inset-bottom));
}

/* Hero: the icon.svg under a glow, with decorative "strings" above it
   that gently sway — a nod to the puppeteer control cross in the mark. */
.welcome-hero {
  position: relative;
  width: 168px;
  height: 180px;
  margin: 14px auto 2px;
  display: grid;
  place-items: end center;
}
.welcome-hero::before {
  content: "";
  position: absolute;
  left: 50%; bottom: 6px;
  width: 180px; height: 180px;
  transform: translateX(-50%);
  background: radial-gradient(circle at 50% 55%, var(--accent), transparent 70%);
  z-index: 0;
}
.welcome-mark {
  position: relative;
  z-index: 1;
  width: 140px;
  height: 140px;
  object-fit: contain;
}
.welcome-title {
  font-family: var(--font-display);
  font-size: 34px;
  font-weight: 800;
  letter-spacing: -0.02em;
  margin-top: 10px;
}
.welcome-tagline {
  color: var(--primary);
  font-weight: 700;
  font-size: 15px;
  margin-top: 2px;
}
.welcome-lede {
  color: var(--muted-foreground);
  font-size: 15px;
  line-height: 1.5;
  margin: 12px auto 0;
  max-width: 19rem;
}

.welcome-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 20px;
}
