/* =============================================================================
 * Voicebot — modern blue/violet aesthetic
 *
 * No framework. Three Google Fonts. CSS custom properties drive the entire
 * palette so theme tweaks happen in one place. The layout is a CSS Grid
 * with two columns (hero card / right rail) plus a top bar and footer.
 *
 * Conventions:
 *   - all sizes in `rem` except 1px borders
 *   - color slots in CSS variables (--c-*)
 *   - state-driven UI uses `data-state="..."` attribute selectors
 *   - animations are CSS-only; no JS animation loops
 * ============================================================================= */

/* ----- Design tokens --------------------------------------------------------- */

:root {
  /* Palette ---------- */
  --c-bg: #0a0b1f;
  --c-bg-soft: #0f1029;
  --c-surface: #15172e;
  --c-surface-2: #1d1f3d;
  --c-surface-3: #282b4d;
  --c-border: rgba(255, 255, 255, 0.07);
  --c-border-strong: rgba(255, 255, 255, 0.12);

  --c-text: #ececf7;
  --c-text-muted: #9a9ab8;
  --c-text-dim: #6a6a86;

  /* Accents — primary violet, secondary electric blue. The gradient
     blends them so any `linear-gradient(var(--gradient-accent))` reads
     as the brand without us having to hardcode hex values in component
     rules. */
  --c-accent: #8b6cf6;
  --c-accent-2: #5b8cf2;
  --c-accent-soft: rgba(139, 108, 246, 0.18);
  --c-accent-glow: rgba(139, 108, 246, 0.45);

  --c-success: #5fd0a3;
  --c-warning: #ffb547;
  --c-error: #ff5a5f;
  --c-recording: #f04a7a;

  /* Geometry ---------- */
  --radius-sm: 0.5rem;
  --radius-md: 0.875rem;
  --radius-lg: 1.5rem;
  --radius-xl: 2rem;
  --radius-full: 999px;

  --shadow-card: 0 1px 0 rgba(255, 255, 255, 0.05) inset,
    0 30px 60px -30px rgba(0, 0, 0, 0.8);
  --shadow-orb: 0 0 0 1px rgba(255, 255, 255, 0.08) inset,
    0 30px 80px -10px rgba(139, 108, 246, 0.55),
    0 0 120px 20px rgba(91, 140, 242, 0.22);

  --gradient-accent: linear-gradient(135deg, #5b8cf2 0%, #8b6cf6 55%, #b16cf6 100%);
  --gradient-accent-radial: radial-gradient(
    circle at 30% 30%,
    #c4b5fd 0%,
    #8b6cf6 40%,
    #5b8cf2 70%,
    #2540c9 100%
  );

  /* Type ---------- */
  --f-display: 'Bricolage Grotesque', 'Manrope', system-ui, sans-serif;
  --f-body: 'Manrope', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
  --f-mono: 'IBM Plex Mono', ui-monospace, 'JetBrains Mono', monospace;
}

/* The HTML `hidden` attribute defaults to `display: none` via the UA stylesheet,
   but our custom `display: flex|inline-flex` rules on specific elements outrank
   that default. Make the attribute authoritative everywhere so toggling
   `el.hidden = true` always wins over component layout rules. */
[hidden] {
  display: none !important;
}

/* ----- Tippy.js theme: voicebot ---------------------------------------------
 *
 * Activated by `theme: 'voicebot'` in the tippy() init call (see ui.js).
 * Designed to read on the dark blue/violet surfaces — a slightly raised
 * panel-like card with a subtle violet glow ring on the arrow.
 */

.tippy-box[data-theme~='voicebot'] {
  background: var(--c-surface-3);
  color: var(--c-text);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--radius-md);
  box-shadow: 0 8px 24px -10px rgba(0, 0, 0, 0.7),
    0 0 0 1px rgba(139, 108, 246, 0.08);
  font-family: var(--f-body);
  font-size: 0.78rem;
  line-height: 1.45;
  padding: 0.1rem 0.15rem;
  letter-spacing: 0.005em;
}

.tippy-box[data-theme~='voicebot'] .tippy-content {
  padding: 0.55rem 0.8rem;
}

/* Arrow color must match the background per-placement. Tippy uses a
   pseudo-element triangle; override its color for our theme. */
.tippy-box[data-theme~='voicebot'][data-placement^='top'] > .tippy-arrow::before {
  border-top-color: var(--c-surface-3);
}
.tippy-box[data-theme~='voicebot'][data-placement^='bottom'] > .tippy-arrow::before {
  border-bottom-color: var(--c-surface-3);
}
.tippy-box[data-theme~='voicebot'][data-placement^='left'] > .tippy-arrow::before {
  border-left-color: var(--c-surface-3);
}
.tippy-box[data-theme~='voicebot'][data-placement^='right'] > .tippy-arrow::before {
  border-right-color: var(--c-surface-3);
}

/* ----- Base reset ------------------------------------------------------------ */

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

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--f-body);
  font-size: 15px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Subtle backdrop: dark navy with a warm radial bloom in the upper-left so
     the hero orb's halo feels rooted in the page, not floating. */
  background-image: radial-gradient(
      ellipse 1200px 600px at 30% 10%,
      rgba(139, 108, 246, 0.08) 0%,
      transparent 60%
    ),
    radial-gradient(ellipse 800px 500px at 80% 90%, rgba(50, 30, 120, 0.06) 0%, transparent 70%);
  background-attachment: fixed;
}

body {
  overflow: hidden;
}

button {
  font-family: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
}

button:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
a:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 2px;
}

select,
textarea,
input {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
}

/* ----- Layout shell ---------------------------------------------------------- */

.app {
  height: 100vh;
  display: grid;
  grid-template-rows: auto 1fr auto;
  padding: 1.25rem;
  gap: 1.25rem;
  max-width: 1400px;
  margin: 0 auto;
}

/* ----- Top bar --------------------------------------------------------------- */

.topbar {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  padding: 0.25rem 0.5rem;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.625rem;
}

.brand__mark {
  width: 2.1rem;
  height: 2.1rem;
  border-radius: var(--radius-sm);
  background: var(--gradient-accent);
  display: grid;
  place-items: center;
  font-family: var(--f-display);
  font-weight: 800;
  font-size: 1.1rem;
  color: #0b0b14;
  box-shadow: 0 8px 20px -10px rgba(139, 108, 246, 0.7);
}

.brand__name {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: -0.005em;
  line-height: 1.1;
}

.brand__sub {
  font-family: var(--f-mono);
  font-size: 0.625rem;
  letter-spacing: 0.18em;
  color: var(--c-text-dim);
  text-transform: uppercase;
}

.topbar__actions {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 0.5rem;
}

.icon-btn {
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-sm);
  display: grid;
  place-items: center;
  color: var(--c-text-muted);
  transition: background 150ms ease, color 150ms ease;
}

.icon-btn:hover {
  background: var(--c-surface-2);
  color: var(--c-text);
}

.icon-btn svg {
  width: 1.05rem;
  height: 1.05rem;
}

.icon-btn--sm {
  width: 1.75rem;
  height: 1.75rem;
}

.icon-btn--sm svg {
  width: 0.85rem;
  height: 0.85rem;
}

.btn-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.55rem 1rem;
  border-radius: var(--radius-full);
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  color: var(--c-text);
  font-size: 0.85rem;
  font-weight: 500;
  transition: background 150ms ease, border-color 150ms ease;
}

.btn-pill:hover {
  background: var(--c-surface-3);
  border-color: var(--c-border-strong);
}

.btn-pill svg {
  width: 0.9rem;
  height: 0.9rem;
}

/* ----- Main grid ------------------------------------------------------------- */

.main {
  display: grid;
  /* Two columns by default (hero | rail). When diagnostics is toggled open,
     a third column slides in on the left and the hero gets squeezed. We
     animate grid-template-columns so the layout shift feels intentional
     rather than abrupt — supported in Chromium 125+, Firefox 137+, Safari 18+
     and gracefully snap-resizes on older engines. */
  grid-template-columns: minmax(0, 1fr) 25rem;
  gap: 1.25rem;
  min-height: 0;
  transition: grid-template-columns 250ms cubic-bezier(0.4, 0, 0.2, 1);
}

body[data-diagnostics='open'] .main {
  grid-template-columns: 18rem minmax(0, 1fr) 25rem;
}

/* ----- Hero card ------------------------------------------------------------- */

.hero {
  position: relative;
  /* Clip the decorative halo only; the stage must not clip the orb / transport
     buttons — on narrow viewports the old `overflow: hidden` + `1fr` stage row
     squeezed the controls below the card and the rail's stats grid sat on top,
     swallowing clicks on "Start session". */
  overflow: clip;
  border-radius: var(--radius-xl);
  background: linear-gradient(
      180deg,
      rgba(91, 140, 242, 0.06) 0%,
      transparent 30%
    ),
    var(--c-surface);
  border: 1px solid var(--c-border);
  padding: clamp(1.5rem, 3vw, 2.5rem);
  display: grid;
  /* Two children now: the orb stage (auto, sized to the orb + transport) on
     top, and the call-flow visualization filling the rest below. The flow
     pane gets min-height:0 so its inner list can scroll instead of stretching
     the card. */
  grid-template-rows: auto minmax(0, 1fr);
  gap: clamp(0.75rem, 2vw, 1.5rem);
  box-shadow: var(--shadow-card);
  /* min-height:0 lets the hero shrink to its grid cell so the flow pane's
     own overflow can take over — this is what makes the call-flow scroll
     fill the *remaining* space with no fixed vh value, at any viewport size. */
  min-height: 0;
}

/* ----- Call-flow visualization (under the orb) ------------------------------ */

.flow-viz {
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-top: 1px solid var(--c-border);
  padding-top: clamp(0.75rem, 2vw, 1.25rem);
}

.flow-viz__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 0.75rem;
}

.flow-viz__title {
  font-family: var(--f-mono);
  font-size: 0.62rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--c-text-muted);
}

.flow-viz__count {
  font-family: var(--f-mono);
  font-size: 0.7rem;
  color: var(--c-accent);
}

.flow-viz__body {
  flex: 1;
  /* min-height:0 (not a fixed height) is what lets this flex child shrink
     below its content and scroll. Combined with the bounded hero above, the
     pane fills the leftover space on any screen — no viewport-specific value.
     Scrolls on both axes (branchy graphs can be wider than the column). */
  min-height: 0;
  overflow: auto;
  scrollbar-width: thin;
  padding-right: 0.25rem;
}

.flow-viz__empty {
  color: var(--c-text-muted);
  font-size: 0.85rem;
  line-height: 1.5;
}

/* Custom flow graph: HTML node cards + an SVG edge layer, laid out by dagre
   (flow-viz.js). Shows the real step structure incl. branches, with the
   active / visited steps highlighted. */
.flow-graph {
  position: relative;
  margin: 0 auto;
}

.flow-graph__edges {
  position: absolute;
  inset: 0;
  overflow: visible;
  pointer-events: none;
}

.flow-graph__edges .flow-edge {
  fill: none;
  stroke: rgba(255, 255, 255, 0.18);
  stroke-width: 1.5;
}

.flow-graph__edges #flowArrow path {
  fill: rgba(255, 255, 255, 0.32);
}

.flow-node {
  position: absolute;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.15rem;
  padding: 0.5rem 0.7rem;
  border-radius: var(--radius-md);
  border: 1px solid var(--c-border);
  background: var(--c-surface);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  transition:
    border-color 200ms ease,
    box-shadow 200ms ease,
    background 200ms ease;
}

.flow-node__id {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--c-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.flow-node__goal {
  font-size: 0.68rem;
  line-height: 1.3;
  color: var(--c-text-muted);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Terminal pills (Call start / Call end). */
.flow-node--term {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-full);
  font-family: var(--f-mono);
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-text-muted);
  background: transparent;
  box-shadow: none;
}

.flow-node[data-state='done'] {
  border-color: var(--c-accent);
  background: var(--c-accent-soft);
}

.flow-node[data-state='current'] {
  border-color: var(--c-accent);
  background: var(--c-accent-soft);
  box-shadow:
    0 0 0 2px var(--c-accent-soft),
    var(--shadow-card);
}

.flow-node[data-state='current'] .flow-node__id {
  color: var(--c-accent);
}

.flow-edge-label {
  position: absolute;
  transform: translate(-50%, -50%);
  padding: 0.05rem 0.35rem;
  border-radius: var(--radius-full);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  font-size: 0.6rem;
  color: var(--c-text-muted);
  white-space: nowrap;
  pointer-events: none;
}

/* Plain-list fallback if the Mermaid graph can't render. */
.flow-fallback {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.flow-fallback__item {
  padding: 0.45rem 0.6rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--c-border);
}

.flow-fallback__item[data-current='true'] {
  border-color: var(--c-accent);
  background: var(--c-accent-soft);
}

.flow-fallback__id {
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--c-text);
}

.flow-fallback__goal {
  font-size: 0.72rem;
  color: var(--c-text-muted);
  margin-top: 0.1rem;
}

.hero::before {
  /* faint radial halo behind the orb */
  content: '';
  position: absolute;
  left: 50%;
  top: 60%;
  transform: translate(-50%, -50%);
  width: 80%;
  aspect-ratio: 1;
  background: radial-gradient(circle, rgba(139, 108, 246, 0.22) 0%, transparent 60%);
  filter: blur(40px);
  z-index: 0;
  pointer-events: none;
}

.hero > * {
  position: relative;
  z-index: 1;
}

.hero__badge {
  justify-self: center;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.85rem;
  border-radius: var(--radius-full);
  background: var(--c-accent-soft);
  border: 1px solid rgba(139, 108, 246, 0.32);
  color: var(--c-accent);
  font-family: var(--f-mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.hero__badge::before {
  content: '✦';
  font-family: var(--f-display);
  font-size: 0.75rem;
}

.hero__title {
  font-family: var(--f-display);
  font-weight: 700;
  font-size: clamp(2.4rem, 5.5vw, 4.5rem);
  line-height: 1;
  letter-spacing: -0.035em;
  text-align: center;
  margin: 0;
  display: inline-flex;
  align-items: baseline;
  gap: 0.45rem;
  justify-content: center;
  width: 100%;
}

.hero__title .hero__title-name {
  background: var(--gradient-accent);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* Italic gives the wordmark an editorial, hand-set feel; keep it tight
     against the suffix so the two words read as one composition. */
  font-style: italic;
}

.hero__title .hero__title-suffix {
  font-weight: 400;
  color: var(--c-text-muted);
  font-size: 0.58em;
  letter-spacing: -0.015em;
  align-self: baseline;
  transform: translateY(-0.05em);
}

.hero__subtitle {
  max-width: 38rem;
  margin: 0 auto;
  text-align: center;
  color: var(--c-text-muted);
  font-size: 0.95rem;
  line-height: 1.6;
}

.hero__stage {
  display: grid;
  place-items: center;
  align-content: center;
  gap: 1rem;
  /* Size to the orb + transport row; never shrink below that or the buttons
     paint outside the hero card and lose pointer events. */
  min-height: min(100%, max(18rem, calc(clamp(16rem, 28vw, 19rem) + 5rem)));
}

/* ----- Voice orb ------------------------------------------------------------- */

.orb {
  position: relative;
  width: clamp(16rem, 28vw, 19rem);
  aspect-ratio: 1;
  border: none;
  background: transparent;
  display: grid;
  place-items: center;
  cursor: pointer;
  border-radius: 50%;
  /* Use ::before for the gradient sphere and ::after for the bloom halo so
     the host <button> can carry semantic focus styles without conflict. */
}

.orb::before {
  content: '';
  position: absolute;
  inset: 8%;
  border-radius: 50%;
  background: var(--gradient-accent-radial);
  box-shadow: var(--shadow-orb);
  transition: transform 250ms ease, filter 250ms ease;
}

.orb::after {
  content: '';
  position: absolute;
  inset: -20%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(139, 108, 246, 0.32) 0%, transparent 55%);
  filter: blur(30px);
  z-index: -1;
  transition: opacity 300ms ease;
  opacity: 0.7;
}

.orb:hover::before {
  filter: brightness(1.07);
}

.orb[data-state='listening']::before {
  animation: orb-breathe 1.8s ease-in-out infinite;
}

.orb[data-state='speaking']::before {
  animation: orb-pulse 0.6s ease-in-out infinite;
}

.orb[data-state='thinking']::before {
  filter: brightness(0.8) saturate(0.7);
}

.orb[data-state='thinking']::after {
  opacity: 1;
  animation: halo-rotate 3s linear infinite;
}

.orb[data-state='idle']::before {
  animation: orb-idle 4s ease-in-out infinite;
}

/* ----- Visualizer (12 vertical bars inside the orb) -------------------------- */

.visualizer {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 0.3rem;
  height: 45%;
  /* The "bars" are pseudo-elements would over-complicate things; render them
     as 12 spans with staggered animation-delay to create the sound-wave look. */
}

.visualizer__bar {
  display: block;
  width: 0.22rem;
  height: 100%;
  border-radius: var(--radius-full);
  background: linear-gradient(180deg, #fff 0%, rgba(255, 255, 255, 0.6) 100%);
  transform-origin: center;
  transform: scaleY(0.15);
  transition: transform 200ms ease;
  box-shadow: 0 0 6px rgba(255, 255, 255, 0.4);
}

.orb[data-state='listening'] .visualizer__bar,
.orb[data-state='speaking'] .visualizer__bar {
  animation: bar-dance 1.1s ease-in-out infinite;
}

.orb[data-state='thinking'] .visualizer__bar {
  /* Hide the wave during thinking — the spinning halo carries the load. */
  transform: scaleY(0.08);
  opacity: 0.35;
  animation: none;
}

.orb[data-state='idle'] .visualizer__bar {
  transform: scaleY(0.2);
}

/* Stagger the bars so they look like a wave rather than a unison flash. */
.visualizer__bar:nth-child(1) {
  animation-delay: 0s;
}
.visualizer__bar:nth-child(2) {
  animation-delay: 0.08s;
}
.visualizer__bar:nth-child(3) {
  animation-delay: 0.16s;
}
.visualizer__bar:nth-child(4) {
  animation-delay: 0.24s;
}
.visualizer__bar:nth-child(5) {
  animation-delay: 0.32s;
}
.visualizer__bar:nth-child(6) {
  animation-delay: 0.4s;
}
.visualizer__bar:nth-child(7) {
  animation-delay: 0.48s;
}
.visualizer__bar:nth-child(8) {
  animation-delay: 0.4s;
}
.visualizer__bar:nth-child(9) {
  animation-delay: 0.32s;
}
.visualizer__bar:nth-child(10) {
  animation-delay: 0.24s;
}
.visualizer__bar:nth-child(11) {
  animation-delay: 0.16s;
}
.visualizer__bar:nth-child(12) {
  animation-delay: 0.08s;
}

/* ----- Stage status + transport --------------------------------------------- */

.stage-status {
  font-family: var(--f-mono);
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--c-text-muted);
  min-height: 1rem;
}

.transport {
  display: flex;
  gap: 0.6rem;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
}

.transport__primary {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.85rem 1.4rem;
  border-radius: var(--radius-full);
  background: var(--gradient-accent);
  color: #0b0b14;
  font-weight: 600;
  font-size: 0.9rem;
  border: 0;
  box-shadow: 0 12px 25px -10px rgba(139, 108, 246, 0.55);
  transition: transform 150ms ease, box-shadow 200ms ease;
}

.transport__primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 16px 30px -10px rgba(139, 108, 246, 0.72);
}

.transport__primary svg {
  width: 1rem;
  height: 1rem;
}

.transport__primary[data-state='idle']::after {
  content: 'Start session';
}

.transport__primary[data-state='listening']::after,
.transport__primary[data-state='thinking']::after,
.transport__primary[data-state='speaking']::after {
  content: 'End session';
}

/* Secondary pill — currently used for "End turn" (manual end-of-speech).
   Lower visual weight than the primary so the user knows it's optional. */
.transport__secondary {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.7rem 1.15rem;
  border-radius: var(--radius-full);
  background: var(--c-surface-2);
  color: var(--c-text-muted);
  font-size: 0.85rem;
  font-weight: 500;
  border: 1px solid var(--c-border);
  transition: background 150ms ease, color 150ms ease, border-color 150ms ease;
}

.transport__secondary:hover {
  background: var(--c-surface-3);
  color: var(--c-text);
  border-color: var(--c-border-strong);
}

.transport__secondary svg {
  width: 0.95rem;
  height: 0.95rem;
}

/* ----- Right rail ------------------------------------------------------------ */

.rail {
  display: grid;
  grid-template-rows: 1fr auto;
  gap: 1.25rem;
  min-height: 0;
}

.card {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  min-height: 0;
}

.card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1rem 1.15rem;
  border-bottom: 1px solid var(--c-border);
}

.card__title {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 0.92rem;
  letter-spacing: -0.01em;
}

.card__meta {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--f-mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  color: var(--c-text-muted);
  text-transform: uppercase;
}

.card__meta::before {
  content: '';
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: var(--c-text-dim);
}

.card__meta[data-active='true']::before {
  background: var(--c-recording);
  box-shadow: 0 0 0 0 rgba(240, 74, 122, 0.6);
  animation: rec-blink 1.4s ease-in-out infinite;
}

/* ----- Transcript bubbles --------------------------------------------------- */

.transcript {
  flex: 1;
  overflow-y: auto;
  padding: 1rem 1.15rem 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.12) transparent;
  min-height: 0;
}

.transcript::-webkit-scrollbar {
  width: 6px;
}

.transcript::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.12);
  border-radius: var(--radius-full);
}

.transcript__empty {
  margin: auto;
  color: var(--c-text-dim);
  font-size: 0.82rem;
  text-align: center;
  padding: 2rem 0.5rem;
}

.msg {
  display: grid;
  grid-template-columns: 1.6rem 1fr;
  gap: 0.65rem;
  align-items: flex-start;
}

.msg__avatar {
  width: 1.6rem;
  height: 1.6rem;
  border-radius: var(--radius-sm);
  display: grid;
  place-items: center;
  font-family: var(--f-display);
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #0b0b14;
}

.msg--bot .msg__avatar {
  background: linear-gradient(135deg, #d6b8ff 0%, #8a6dff 100%);
  color: #0b0b14;
}

.msg--user .msg__avatar {
  background: var(--gradient-accent);
}

.msg__body {
  min-width: 0;
}

.msg__role {
  font-family: var(--f-mono);
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  color: var(--c-text-muted);
  text-transform: uppercase;
  margin-bottom: 0.18rem;
}

.msg__text {
  font-size: 0.86rem;
  line-height: 1.5;
  color: var(--c-text);
  word-wrap: break-word;
  white-space: pre-wrap;
}

.msg--partial .msg__text {
  font-style: italic;
  color: var(--c-text-muted);
}

.msg__replay {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: 0.45rem;
  padding: 0.25rem 0.55rem;
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--c-border);
  color: var(--c-text-muted);
  font-family: var(--f-mono);
  font-size: 0.65rem;
  letter-spacing: 0.06em;
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease;
}

.msg__replay:hover {
  background: var(--c-accent-soft);
  color: var(--c-accent);
}

.msg__replay svg {
  width: 0.7rem;
  height: 0.7rem;
}

/* ----- Stats grid ----------------------------------------------------------- */

.stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.7rem;
}

.stat {
  padding: 0.85rem 1rem;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-card);
}

.stat__label {
  font-family: var(--f-mono);
  font-size: 0.62rem;
  letter-spacing: 0.16em;
  color: var(--c-text-dim);
  text-transform: uppercase;
}

.stat__value {
  margin-top: 0.3rem;
  font-family: var(--f-display);
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--c-text);
}

/* ----- Footer --------------------------------------------------------------- */

.footer {
  display: flex;
  justify-content: flex-start;
  font-family: var(--f-mono);
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  color: var(--c-text-dim);
  text-transform: uppercase;
  padding: 0 0.5rem;
}

/* ----- Slide-over panels (settings + diagnostics) ---------------------------- */

.overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(2px);
  z-index: 60;
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms ease;
}

.panel {
  position: fixed;
  top: 0;
  bottom: 0;
  width: min(26rem, 100vw);
  background: var(--c-bg-soft);
  border-left: 1px solid var(--c-border);
  box-shadow: -30px 0 60px -20px rgba(0, 0, 0, 0.6);
  z-index: 70;
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1);
}

.panel--right {
  right: 0;
}

body[data-panel='settings'] #panel-settings {
  transform: translateX(0);
}

body[data-panel='settings'] .overlay {
  opacity: 1;
  pointer-events: auto;
}

.panel__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.2rem 1.4rem;
  border-bottom: 1px solid var(--c-border);
}

.panel__title {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
}

.panel__body {
  flex: 1;
  overflow-y: auto;
  padding: 1.25rem 1.4rem;
  display: flex;
  flex-direction: column;
  gap: 1.15rem;
  scrollbar-width: thin;
}

/* ----- Form controls inside panels ------------------------------------------ */

.field {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.field__label {
  font-family: var(--f-mono);
  font-size: 0.65rem;
  letter-spacing: 0.14em;
  color: var(--c-text-muted);
  text-transform: uppercase;
}

.field__hint {
  font-size: 0.72rem;
  color: var(--c-text-dim);
}

/* Checkbox row: a checkbox sitting inline with its label text. Reuses the
   .field__label typography for the text span only (not the row) so the box
   isn't transformed/letter-spaced like the uppercase labels. */
.field__check {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
}

.field__check span {
  font-family: var(--f-mono);
  font-size: 0.65rem;
  letter-spacing: 0.14em;
  color: var(--c-text-muted);
  text-transform: uppercase;
}

.checkbox {
  width: 1rem;
  height: 1rem;
  accent-color: var(--c-accent);
  cursor: pointer;
}

/* Right-aligned numeric readout sitting inline with .field__label. */
.field__value {
  float: right;
  font-family: var(--f-mono);
  font-size: 0.72rem;
  color: var(--c-text);
  letter-spacing: 0.04em;
  text-transform: none;
}

/* Collapsible "advanced" group inside the settings form (e.g. Speech
   end-detection). A divider + breathing room above so it doesn't crowd the
   field above it, and a readable, obviously-clickable summary row. */
.field--advanced {
  margin-top: 0.9rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--c-border);
  gap: 0.85rem;
}

.field__summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
  list-style: none;
  user-select: none;
  font-family: var(--f-mono);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-text);
  transition: color 0.15s ease;
}

.field__summary::-webkit-details-marker {
  display: none;
}

/* Custom disclosure caret (replaces the default marker) that rotates on open. */
.field__summary::before {
  content: '';
  width: 0;
  height: 0;
  border-left: 6px solid var(--c-accent);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform 0.15s ease;
}

.field--advanced[open] > .field__summary::before {
  transform: rotate(90deg);
}

.field__summary:hover {
  color: var(--c-accent);
}

/* Lead-in hint directly under a summary: drop the inherited top gap so the
   description sits tight beneath the heading once expanded. */
.field__hint--lead {
  margin: 0.1rem 0 0;
}

/* ----- Slider (range input) ------------------------------------------------ */

.slider {
  appearance: none;
  width: 100%;
  height: 0.35rem;
  background: var(--c-surface-2);
  border-radius: var(--radius-full);
  outline: none;
  cursor: pointer;
}

.slider::-webkit-slider-thumb {
  appearance: none;
  width: 1.05rem;
  height: 1.05rem;
  border-radius: 50%;
  background: var(--c-accent);
  border: 2px solid var(--c-bg);
  box-shadow: 0 0 0 1px var(--c-border-strong);
  cursor: pointer;
  transition: transform 120ms ease;
}

.slider::-moz-range-thumb {
  width: 1.05rem;
  height: 1.05rem;
  border-radius: 50%;
  background: var(--c-accent);
  border: 2px solid var(--c-bg);
  box-shadow: 0 0 0 1px var(--c-border-strong);
  cursor: pointer;
}

.slider:hover::-webkit-slider-thumb {
  transform: scale(1.08);
}

/* ----- Panel section (visual separator) ------------------------------------ */

.panel__section {
  margin-top: 0.4rem;
  padding-top: 1.1rem;
  border-top: 1px solid var(--c-border);
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}

.panel__section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.panel__section-title {
  margin: 0;
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 0.92rem;
  letter-spacing: -0.005em;
}

.input,
.textarea,
.select {
  width: 100%;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-md);
  padding: 0.65rem 0.85rem;
  font-size: 0.88rem;
  color: var(--c-text);
  transition: border-color 150ms ease, background 150ms ease;
}

.input:hover,
.textarea:hover,
.select:hover {
  border-color: var(--c-border-strong);
}

.input:focus,
.textarea:focus,
.select:focus {
  border-color: var(--c-accent);
  outline: none;
}

.textarea {
  resize: vertical;
  min-height: 5rem;
  font-family: inherit;
  line-height: 1.5;
}

.select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%238a8a96'><path d='M4 6l4 4 4-4'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.7rem center;
  background-size: 0.8rem;
  padding-right: 2rem;
}

.btn-row {
  display: flex;
  gap: 0.6rem;
  margin-top: 0.5rem;
}

/* A row that pairs a compact control with the primary action button. */
.btn-row--split {
  align-items: center;
}

.inline-field {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  flex: 0 0 auto;
}

.inline-field__label {
  font-size: 0.78rem;
  color: var(--c-text-dim);
}

.inline-field .select,
.select--sm {
  width: auto;
  padding: 0.4rem 0.6rem;
  font-size: 0.8rem;
}

.btn {
  flex: 1;
  padding: 0.65rem 1rem;
  border-radius: var(--radius-md);
  font-size: 0.85rem;
  font-weight: 500;
  border: 1px solid transparent;
  transition: background 150ms ease, border-color 150ms ease, color 150ms ease;
}

.btn--ghost {
  background: transparent;
  border-color: var(--c-border);
  color: var(--c-text-muted);
}

.btn--ghost:hover {
  background: var(--c-surface-2);
  color: var(--c-text);
}

/* Extra-small variant used by inline diagnostics header (clear button etc.) */
.btn--xs {
  flex: 0 0 auto;
  padding: 0.3rem 0.6rem;
  font-size: 0.7rem;
  font-family: var(--f-mono);
  letter-spacing: 0.05em;
}

.btn--primary {
  background: var(--gradient-accent);
  color: #0b0b14;
  font-weight: 600;
}

.btn--primary:hover {
  filter: brightness(1.05);
}

/* ----- Skip-to-latest control (transcript) --------------------------------- */

.transcript-skip {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  justify-content: center;
  padding: 0.4rem 0 0.2rem;
  background: linear-gradient(
    to bottom,
    var(--c-surface) 60%,
    transparent
  );
  pointer-events: none; /* the button itself re-enables */
}

.transcript-skip__btn {
  pointer-events: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.45rem 0.95rem;
  border-radius: var(--radius-full);
  background: var(--gradient-accent);
  color: #ffffff;
  font-size: 0.78rem;
  font-weight: 600;
  border: 0;
  box-shadow: 0 10px 20px -8px rgba(139, 108, 246, 0.6);
  transition: transform 150ms ease, box-shadow 180ms ease;
  animation: skipPulse 1.6s ease-in-out infinite;
}

.transcript-skip__btn:hover {
  transform: translateY(-1px);
  box-shadow: 0 14px 26px -8px rgba(139, 108, 246, 0.75);
}

.transcript-skip__btn svg {
  width: 0.85rem;
  height: 0.85rem;
}

@keyframes skipPulse {
  0%, 100% {
    box-shadow: 0 10px 20px -8px rgba(139, 108, 246, 0.55);
  }
  50% {
    box-shadow: 0 14px 28px -6px rgba(139, 108, 246, 0.9);
  }
}

/* ----- Mic level meter (diagnostics footer) -------------------------------- */

.mic-meter {
  margin-top: 0.85rem;
  padding-top: 0.85rem;
  border-top: 1px solid var(--c-border);
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  font-family: var(--f-mono);
  font-size: 0.7rem;
}

.mic-meter__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.mic-meter__title {
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-text-muted);
  font-size: 0.62rem;
}

.mic-meter__dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: var(--c-text-dim);
  transition: background 100ms ease, box-shadow 100ms ease;
}

.mic-meter__dot[data-state='open'] {
  background: #f2c14e;
  box-shadow: 0 0 6px rgba(242, 193, 78, 0.6);
}

.mic-meter__dot[data-state='speaking'] {
  background: var(--c-success);
  box-shadow: 0 0 8px rgba(102, 194, 122, 0.65);
}

.mic-meter__bar {
  position: relative;
  height: 0.55rem;
  background: var(--c-surface-2);
  border-radius: var(--radius-full);
  overflow: hidden;
}

.mic-meter__fill {
  height: 100%;
  width: 0%;
  background: linear-gradient(to right, #4f7df0, #6fa1ff);
  transition: width 70ms linear, background 150ms ease;
  border-radius: var(--radius-full);
}

.mic-meter__fill[data-above-threshold='true'] {
  background: linear-gradient(to right, var(--c-accent-2), var(--c-accent));
}

.mic-meter__threshold {
  position: absolute;
  top: -2px;
  bottom: -2px;
  width: 2px;
  background: rgba(139, 108, 246, 0.95);
  box-shadow: 0 0 4px rgba(139, 108, 246, 0.7);
  pointer-events: none;
  transition: left 80ms linear;
}

.mic-meter__spark {
  width: 100%;
  height: 2.4rem;
  background: var(--c-surface-2);
  border-radius: var(--radius-sm);
}

.mic-meter__nums {
  display: flex;
  justify-content: space-between;
  color: var(--c-text-dim);
}

.mic-meter__nums strong {
  color: var(--c-text);
  font-weight: 500;
}

/* ----- Inline diagnostics column ------------------------------------------- */

/* The diagnostics column lives inside .main as a regular grid cell, so toggling
   it pushes the hero left rather than overlaying it. We hide it by collapsing
   its visibility (display:none) when closed; the grid's column transition
   handles the width animation. */

.diagnostics {
  display: none;
  flex-direction: column;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-lg);
  padding: 1.05rem 1rem 0.8rem;
  min-height: 0;
  max-height: 100%;
  overflow: hidden;
  box-shadow: var(--shadow-card);
  animation: diagnosticsFade 220ms ease both;
}

body[data-diagnostics='open'] .diagnostics {
  display: flex;
}

.diagnostics__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.85rem;
  padding-bottom: 0.7rem;
  border-bottom: 1px solid var(--c-border);
}

.diagnostics__title {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: -0.005em;
}

.diagnostics__actions {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

/* The log list inside .diagnostics scrolls independently when it overflows
   the column height. We also pin the scroll to the bottom by default so new
   events stay in view; logs.js scrolls programmatically on append. */
.diagnostics .log-list {
  flex: 1;
  overflow-y: auto;
  padding-right: 0.25rem;
  scrollbar-width: thin;
}

@keyframes diagnosticsFade {
  from {
    opacity: 0;
    transform: translateX(-8px);
  }
  to {
    opacity: 1;
    transform: translateX(0);
  }
}

/* ----- Diagnostics (debug log) list ----------------------------------------- */

.log-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  font-family: var(--f-mono);
  font-size: 0.74rem;
  line-height: 1.4;
}

.log-list__item {
  display: grid;
  grid-template-columns: 0.5rem 1fr;
  gap: 0.6rem;
  align-items: flex-start;
  padding: 0.45rem 0;
  border-bottom: 1px dashed var(--c-border);
}

.log-list__item:last-child {
  border-bottom: none;
}

.log-list__dot {
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  margin-top: 0.35rem;
  background: var(--c-text-dim);
}

.log-list__item[data-category='asr'] .log-list__dot {
  background: #76c5ff;
}

.log-list__item[data-category='llm'] .log-list__dot {
  background: var(--c-success);
}

.log-list__item[data-category='tts'] .log-list__dot {
  background: var(--c-accent);
}

.log-list__item[data-category='session'] .log-list__dot {
  background: var(--c-text-dim);
}

.log-list__item[data-category='flow'] .log-list__dot {
  background: #c792ea;
}

.log-list__item[data-category='kb'] .log-list__dot {
  background: #ffcb6b;
}

.log-list__item[data-level='warn'] {
  color: var(--c-warning);
}

.log-list__item[data-level='error'] {
  color: var(--c-error);
}

.log-list__time {
  color: var(--c-text-dim);
  font-size: 0.68rem;
}

/* ----- Details panel (assistant <show> content) ----------------------------- */

/* Right slide-over, NON-modal: no dimming overlay, so it can auto-open on a new
   response without trapping the user mid-conversation. Toggled via
   body[data-details='open']. */
.details-panel {
  position: fixed;
  top: 0;
  bottom: 0;
  right: 0;
  width: min(32rem, 100vw);
  background: var(--c-bg-soft);
  border-left: 1px solid var(--c-border);
  box-shadow: -30px 0 60px -20px rgba(0, 0, 0, 0.6);
  z-index: 75;
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform 260ms cubic-bezier(0.4, 0, 0.2, 1);
}

body[data-details='open'] .details-panel {
  transform: translateX(0);
}

.details-panel__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.2rem 1.4rem;
  border-bottom: 1px solid var(--c-border);
}

.details-panel__title {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 1.05rem;
  letter-spacing: -0.01em;
}

.details-panel__body {
  flex: 1;
  overflow-y: auto;
  padding: 1.25rem 1.4rem;
  scrollbar-width: thin;
}

.details-panel__empty {
  color: var(--c-text-dim);
  font-size: 0.85rem;
  text-align: center;
  padding: 2rem 0.5rem;
}

/* One stored turn's shown content. Hidden unless it's the active entry. */
.details-entry {
  display: none;
}

.details-entry[data-active='true'] {
  display: block;
  animation: detailsFade 200ms ease both;
}

.details-entry__meta {
  font-family: var(--f-mono);
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--c-text-dim);
  margin-bottom: 0.9rem;
}

@keyframes detailsFade {
  from {
    opacity: 0;
    transform: translateY(6px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* Rendered Markdown. Scoped tightly so LLM content can't break the app shell. */
.markdown {
  color: var(--c-text);
  font-size: 0.92rem;
  line-height: 1.62;
  word-break: break-word;
}

.markdown > :first-child {
  margin-top: 0;
}

.markdown h1,
.markdown h2,
.markdown h3,
.markdown h4 {
  font-family: var(--f-display);
  font-weight: 600;
  line-height: 1.25;
  margin: 1.4rem 0 0.6rem;
}

.markdown h1 {
  font-size: 1.4rem;
}

.markdown h2 {
  font-size: 1.2rem;
}

.markdown h3 {
  font-size: 1.05rem;
}

.markdown p,
.markdown ul,
.markdown ol,
.markdown blockquote,
.markdown pre,
.markdown table {
  margin: 0 0 0.85rem;
}

.markdown ul,
.markdown ol {
  padding-left: 1.3rem;
}

.markdown li {
  margin: 0.25rem 0;
}

.markdown a {
  color: var(--c-accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.markdown code {
  font-family: var(--f-mono);
  font-size: 0.85em;
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-sm);
  padding: 0.1rem 0.35rem;
}

.markdown pre {
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-md);
  padding: 0.85rem 1rem;
  overflow-x: auto;
}

.markdown pre code {
  background: none;
  border: 0;
  padding: 0;
  font-size: 0.82rem;
  line-height: 1.5;
}

.markdown blockquote {
  border-left: 3px solid var(--c-border-strong);
  padding-left: 0.9rem;
  color: var(--c-text-muted);
}

.markdown table {
  border-collapse: collapse;
  width: 100%;
  font-size: 0.86rem;
}

.markdown th,
.markdown td {
  border: 1px solid var(--c-border);
  padding: 0.45rem 0.6rem;
  text-align: left;
}

.markdown th {
  background: var(--c-surface-2);
  font-weight: 600;
}

/* "View details" link on an assistant transcript bubble. */
.msg__details-link {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: 0.5rem;
  padding: 0.3rem 0.7rem;
  border-radius: var(--radius-full);
  background: var(--c-accent-soft);
  border: 1px solid rgba(139, 108, 246, 0.32);
  color: var(--c-accent);
  font-size: 0.72rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 150ms ease, border-color 150ms ease;
}

.msg__details-link:hover {
  background: rgba(139, 108, 246, 0.2);
  border-color: var(--c-accent);
}

.msg__details-link svg {
  width: 0.8rem;
  height: 0.8rem;
}

.msg__details-link[data-loading='true'] {
  opacity: 0.7;
}

.log-list__head {
  display: flex;
  gap: 0.5rem;
  align-items: baseline;
  flex-wrap: wrap;
}

.log-list__cat {
  color: var(--c-text-dim);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  font-size: 0.62rem;
}

.log-list__meta {
  color: var(--c-text-dim);
  font-size: 0.66rem;
  word-break: break-all;
}

/* ----- Toast ----------------------------------------------------------------- */

.toast {
  position: fixed;
  right: 1.25rem;
  bottom: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  z-index: 80;
  pointer-events: none;
}

.toast__item {
  pointer-events: auto;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-left: 3px solid var(--c-error);
  border-radius: var(--radius-md);
  padding: 0.7rem 1rem;
  max-width: 24rem;
  font-size: 0.82rem;
  color: var(--c-text);
  box-shadow: 0 20px 40px -20px rgba(0, 0, 0, 0.7);
  animation: toast-in 220ms ease-out;
}

/* ----- Keyframes ------------------------------------------------------------- */

@keyframes orb-breathe {
  0%, 100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.03);
  }
}

@keyframes orb-pulse {
  0%, 100% {
    transform: scale(1);
    filter: brightness(1);
  }
  50% {
    transform: scale(1.02);
    filter: brightness(1.1);
  }
}

@keyframes orb-idle {
  0%, 100% {
    filter: brightness(0.92);
  }
  50% {
    filter: brightness(1);
  }
}

@keyframes halo-rotate {
  from {
    background: conic-gradient(
      from 0deg,
      transparent 0deg,
      rgba(139, 108, 246, 0.45) 60deg,
      transparent 120deg,
      transparent 360deg
    );
  }
  to {
    background: conic-gradient(
      from 360deg,
      transparent 0deg,
      rgba(139, 108, 246, 0.45) 60deg,
      transparent 120deg,
      transparent 360deg
    );
  }
}

@keyframes bar-dance {
  0%, 100% {
    transform: scaleY(0.2);
  }
  20% {
    transform: scaleY(0.8);
  }
  40% {
    transform: scaleY(0.4);
  }
  60% {
    transform: scaleY(0.95);
  }
  80% {
    transform: scaleY(0.55);
  }
}

@keyframes rec-blink {
  0%, 100% {
    box-shadow: 0 0 0 0 rgba(240, 74, 122, 0.5);
  }
  50% {
    box-shadow: 0 0 0 4px rgba(240, 74, 122, 0);
  }
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* ----- Reduced motion ------------------------------------------------------- */

/* ----- Conversation flows page ---------------------------------------------- */

.flows-layout {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  /* Bound the row to the available height so the columns fit the viewport and
     the preview diagram/source can flex-fill instead of overflowing + clipping
     the bottom of the card. */
  grid-template-rows: minmax(0, 1fr);
  gap: 1.25rem;
  padding: 1.5rem;
  align-items: stretch;
}

@media (max-width: 1000px) {
  .flows-layout {
    grid-template-columns: minmax(0, 1fr);
    /* Stacked single column: let the rows size to content and the layout
       scroll, rather than squeezing both cards into one viewport height. */
    grid-template-rows: none;
    align-items: start;
    overflow-y: auto;
  }
}

.flows-col {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  min-height: 0;
}

/* Card body gutter. The head and the preview tab strip are intentionally
   full-bleed (their bottom borders span edge to edge); every other body
   element lines up at the same 1.15rem gutter the head text and save bar use.
   Previously the left column's fields and the preview panel sat flush against
   the rounded card border. */
.flows-col > .field,
.flows-col > .flow-status {
  padding-left: 1.15rem;
  padding-right: 1.15rem;
}

/* Bottom breathing room so the last control / status isn't flush with the card
   edge (the left column is content-height; the right column flex-fills). */
.flows-col > .field:last-child,
.flows-col > .flow-status:last-child {
  padding-bottom: 1.25rem;
}

.flows-row {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}

.flows-row .select,
.flows-row .flows-name {
  flex: 1;
  min-width: 0;
}

/* Prominent save action at the top of the Preview column: name input + a
   primary Save button sit together as a toolbar, mirroring "Generate flow"
   on the left so saving isn't buried at the bottom of the page. */
.flows-savebar {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  /* Top spacing comes from the column gap (matches the left column's first
     field below the head); only the side gutter is needed here. */
  padding: 0 1.15rem;
}

.flows-savebar .flows-name {
  flex: 1;
  min-width: 0;
}

.flows-save-btn {
  flex: 0 0 auto;
  min-width: 9rem;
  white-space: nowrap;
}

.flows-savebar__hint {
  display: block;
  padding: 0 1.15rem;
  /* Hug the save bar above (counteract most of the column gap) so the caption
     reads as belonging to it rather than floating midway to the tabs. */
  margin-top: -0.6rem;
}

.flows-textarea {
  min-height: 7rem;
}

/* Source panel fills the card height like the diagram does. */
#flow-panel-source .field {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
}

.flows-source {
  flex: 1;
  min-height: 14rem;
  font-family: var(--f-mono);
  font-size: 0.78rem;
  line-height: 1.5;
  white-space: pre;
  overflow-wrap: normal;
  overflow: auto;
}

/* ----- Preview tabs (Diagram / Source) ------------------------------------- */

.flow-tabs {
  display: flex;
  gap: 0.25rem;
  border-bottom: 1px solid var(--c-border);
  /* Nudge the labels to the 1.15rem body gutter (each tab adds 0.9rem of its
     own) while the divider border stays full-bleed. */
  padding-left: 0.25rem;
}

.flow-tab {
  appearance: none;
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  padding: 0.5rem 0.9rem;
  font-family: inherit;
  font-size: 0.8rem;
  color: var(--c-text-dim);
  cursor: pointer;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}

.flow-tab:hover {
  color: var(--c-text);
}

.flow-tab.is-active {
  color: var(--c-text);
  border-bottom-color: var(--c-accent);
}

.flow-tab:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 2px;
}

.flow-panel {
  display: none;
  min-height: 0;
}

/* The active panel fills the remaining card height so the diagram/source size
   to the viewport instead of a fixed height (which used to overflow + clip the
   bottom once the save toolbar was added above the tabs). */
.flow-panel.is-active {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  /* Inset the diagram / source so the preview doesn't touch the card edges,
     with bottom room matching the rest of the body gutter. */
  padding: 0.65rem 1.15rem 1.25rem;
}

.flow-diagram-wrap {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.flow-diagram {
  background: var(--c-bg-soft);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-md);
  flex: 1;
  min-height: 14rem;
  overflow: hidden;
  position: relative;
  cursor: grab;
  touch-action: none;
}

.flow-diagram.is-panning {
  cursor: grabbing;
}

/* The SVG is positioned + scaled by JS (transform). Anchor it top-left so the
   transform math in flows.js (_setupSvg/_zoomAt) is predictable. */
.flow-diagram svg {
  position: absolute;
  top: 0;
  left: 0;
  display: block;
}

.flow-diagram__empty {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
  color: var(--c-text-dim);
  font-size: 0.85rem;
  text-align: center;
}

.flow-diagram__controls {
  position: absolute;
  top: 0.5rem;
  right: 0.5rem;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.25rem;
  background: color-mix(in srgb, var(--c-surface) 80%, transparent);
  border: 1px solid var(--c-border);
  border-radius: var(--radius-full);
  backdrop-filter: blur(6px);
}

.flow-diagram__hint {
  position: absolute;
  bottom: 0.5rem;
  left: 0.75rem;
  z-index: 2;
  font-size: 0.72rem;
  color: var(--c-text-dim);
  pointer-events: none;
  user-select: none;
}

.flow-diagram__raw {
  width: 100%;
  margin: 0;
  font-family: var(--f-mono);
  font-size: 0.72rem;
  color: var(--c-warning);
  white-space: pre-wrap;
}

.flow-status {
  min-height: 1.25rem;
  font-size: 0.82rem;
  color: var(--c-text-muted);
}

.flow-status[data-level='ok'] {
  color: var(--c-success);
}

.flow-status[data-level='warn'] {
  color: var(--c-warning);
}

.flow-status[data-level='error'] {
  color: var(--c-error);
}

/* ----- Post-call results card (transcript) ---------------------------------- */

.post-call {
  margin: 0.75rem 0;
  padding: 0.85rem 1rem;
  background: var(--c-surface-2);
  border: 1px solid var(--c-border);
  border-left: 3px solid var(--c-accent);
  border-radius: var(--radius-md);
}

.post-call__head {
  font-family: var(--f-display);
  font-weight: 600;
  font-size: 0.85rem;
  color: var(--c-text);
  margin-bottom: 0.5rem;
}

.post-call__list {
  margin: 0;
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.3rem 0.8rem;
}

.post-call__list dt {
  color: var(--c-text-muted);
  font-size: 0.78rem;
}

.post-call__list dd {
  margin: 0;
  color: var(--c-text);
  font-size: 0.82rem;
  font-weight: 500;
}

.post-call__answer--empty {
  color: var(--c-text-dim);
  font-style: italic;
  font-weight: 400;
}

/* ----- Reduced motion ------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .orb::before,
  .visualizer__bar,
  .card__meta[data-active='true']::before {
    animation: none !important;
  }
}

/* ----- Responsive overrides -------------------------------------------------
   Kept at the END of the file (after every desktop rule) so these max-width
   overrides win on source order — a media query placed above the base rules it
   targets would be overridden by them (same specificity, later wins). */

@media (max-width: 1100px) {
  /* Single column: sections stack, so let the main area scroll as a whole
     (the fixed-viewport per-pane scroll model only makes sense side-by-side). */
  .main {
    grid-template-columns: 1fr;
    overflow-y: auto;
  }
  body[data-diagnostics='open'] .main {
    grid-template-columns: 1fr;
  }

  /* Stacked hero is natural-height (the main scrolls); both rows auto so the
     flow pane shows fully. It still scrolls horizontally inside its own box if
     a branchy graph is wider than the column. Keep session controls above the
     rail when sections overlap during the column transition. */
  .hero {
    grid-template-rows: auto auto;
    position: relative;
    z-index: 2;
  }

  .rail {
    position: relative;
    z-index: 1;
  }

  .transport {
    position: relative;
    z-index: 3;
  }
}

@media (max-width: 640px) {
  /* Phones: the orb is otherwise locked at its 16rem minimum and dominates the
     screen, pushing the call-flow off the card. Scale the hero down and drop
     the stage's tall min-height so the orb + call-flow both fit, and keep the
     diagnostics compact with its own (rem-based) internal scroll. */
  .app {
    padding: 0.75rem;
    gap: 0.85rem;
  }

  .hero {
    padding: 1.1rem;
    gap: 0.85rem;
  }

  .orb {
    width: clamp(10rem, 54vw, 14rem);
  }

  .hero__stage {
    min-height: auto;
    gap: 0.75rem;
  }

  .diagnostics {
    max-height: 22rem;
  }

  .mic-meter__spark {
    height: 1.6rem;
  }
}
