/* =============================================================================
 * SpeakoFlow web demo — styles.css
 *
 * A dependency-free re-package of the SpeakoFlow app's floating "pill" voice
 * HUD, its live audio waveform, and the collapsed assistant pill, for use on a
 * plain static marketing site.
 *
 * All values below are the REAL design tokens lifted from the app source:
 *   - src/overlay/RecordingOverlay.css        (the dictation / recording pill)
 *   - src/components/shared/AudioWaveform.css  (the waveform bars + motion)
 *   - src/assistant/AssistantPanel.css         (the assistant pill + panel)
 *   - src/App.css                              (brand accent)
 *
 * Nothing here talks to Tauri, audio hardware, or any network. The waveform and
 * state changes are driven entirely by a scripted, looping timeline in demo.js
 * (reactive listening) or by pure CSS keyframes (thinking / speaking / idle).
 *
 * The two demo surfaces are transparent-background friendly: only the pills and
 * the writing card paint a surface, so the folder can sit on a light OR a dark
 * section of a page.
 * ========================================================================== */

/* ---------------------------------------------------------------------------
 * 1. Design tokens — exported as CSS custom properties
 *    (scoped to .sf-scope so they never leak into the host page's own styles)
 * ------------------------------------------------------------------------- */

.sf-scope {
  /* Surfaces (warm near-black glass) */
  --sf-pill-surface: rgba(24, 22, 20, 0.94); /* recording + assistant pill */
  --sf-panel-surface: rgba(21, 19, 17, 1); /* expanded panel (solid) */

  /* Ink ramp (off-white on dark) */
  --sf-ink: #f7f6f4; /* primary text / icons */
  --sf-body: #d9d6d2; /* assistant message body */
  --sf-muted: #a9a39c; /* secondary / glyphs */
  --sf-faint: #7b756d; /* placeholders / resting dot */

  /* Hairlines & raised fills */
  --sf-hairline: rgba(255, 255, 255, 0.09);
  --sf-hairline-strong: rgba(255, 255, 255, 0.16);
  --sf-raise: rgba(255, 255, 255, 0.075);
  --sf-raise-strong: rgba(255, 255, 255, 0.13);
  --sf-inset-bg: rgba(0, 0, 0, 0.3);

  /* Accents */
  --sf-live: #e5484d; /* real app's live-recording red cue */
  --sf-accent: #14b8a6; /* brand teal (per demo brief; app uses #0f766e/#3dd6c3) */
  --sf-error: #fda4af;
  --sf-error-bg: rgba(244, 63, 94, 0.12);

  /* Depth: inner highlight + inner hairline (no drop shadow — the app windows
   * are transparent, so a big shadow would clip / show a milky band). */
  --sf-inner-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.08),
    inset 0 0 0 0.5px rgba(0, 0, 0, 0.45);

  /* Waveform bar fill (dark surface). Light-surface variant swapped below. */
  --sf-wave-bar: linear-gradient(to top, #14b8a6, #99f6e4 80%);

  /* Blur / glass */
  --sf-pill-blur: blur(20px) saturate(1.4);
  --sf-panel-blur: blur(22px) saturate(1.4);

  /* Radii */
  --sf-pill-radius: 999px; /* recording pill — full stadium */
  --sf-apill-radius: 16px; /* assistant pill — rounded rect (matches panel) */

  /* The one shared easing curve used across every settle / resize / bar move. */
  --sf-ease: cubic-bezier(0.22, 0.61, 0.36, 1);

  /* Typography — Inter Variable in the app; we fall back to the same system
   * stack the app declares (no @font-face is bundled in the app either). */
  --sf-font:
    "Inter Variable", -apple-system, BlinkMacSystemFont,
    "Segoe UI Variable Text", "Segoe UI", Roboto, system-ui, sans-serif;

  font-family: var(--sf-font);
  color: var(--sf-ink);
  color-scheme: dark;
  box-sizing: border-box;
}

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

/* ---------------------------------------------------------------------------
 * 2. AudioWaveform  (ported from src/components/shared/AudioWaveform.css)
 *
 * A mirrored "voice spindle": rounded-cap bars arch up from a centre line.
 * One visual language for three modes: reactive (live), shimmer (thinking),
 * flow (speaking), plus an idle "breathe". Bar heights are set inline by
 * demo.js (reactive) or come from the inline arch silhouette (shimmer/flow).
 * ------------------------------------------------------------------------- */

.sf-waveform {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  line-height: 0;
  pointer-events: none; /* decorative — never intercept clicks/drags */
}

.sf-waveform.sm {
  height: 24px;
}
.sf-waveform.md {
  height: 38px;
}

.sf-wave-bars {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sf-bar-gap, 2px);
  width: 100%;
  height: 100%;
}

.sf-wave-bar {
  flex: 1 1 0;
  min-width: 1.5px;
  max-width: var(--sf-bar-w, 2.5px);
  min-height: 2px;
  border-radius: 999px;
  background: var(--sf-wave-bar);
  transform-origin: center center;
  /* Eased rise/fall — long enough to bridge the gaps between scripted frames,
   * so the bars glide instead of stepping. */
  transition:
    height 240ms var(--sf-ease),
    transform 260ms var(--sf-ease);
  will-change: height, transform;
}

/* Idle (reactive, no signal): a calm row of soft dots that breathe together
 * on one slow synchronised pulse. */
.sf-waveform.is-idle .sf-wave-bar {
  animation: sf-wave-idle-breathe 2.8s ease-in-out infinite;
}

@keyframes sf-wave-idle-breathe {
  0%,
  100% {
    opacity: 0.3;
    transform: scaleY(0.82);
  }
  50% {
    opacity: 0.72;
    transform: scaleY(1.5);
  }
}

/* Self-animated states: centre-out ripple driven by per-bar --dist (0 centre,
 * 1 rim), so the motion blooms symmetrically from the middle outward. */
.sf-waveform.shimmer .sf-wave-bar,
.sf-waveform.flow .sf-wave-bar {
  animation-name: sf-wave-pulse;
  animation-iteration-count: infinite;
  animation-timing-function: cubic-bezier(0.37, 0, 0.63, 1);
  will-change: transform;
}

/* Thinking — slow and shallow, a quiet premium breath. */
.sf-waveform.shimmer .sf-wave-bar {
  --sf-pulse-lo: 0.78;
  animation-duration: 2.4s;
  animation-delay: calc(var(--dist, 0) * -0.62s);
}

/* Speaking — a touch livelier than thinking, but long + gentle so it soothes. */
.sf-waveform.flow .sf-wave-bar {
  --sf-pulse-lo: 0.58;
  animation-duration: 2.3s;
  animation-delay: calc(var(--dist, 0) * -0.8s);
}

@keyframes sf-wave-pulse {
  0%,
  100% {
    transform: scaleY(var(--sf-pulse-lo, 0.6));
  }
  50% {
    transform: scaleY(1);
  }
}

/* ---------------------------------------------------------------------------
 * 3. Recording / dictation pill
 *    (ported from src/overlay/RecordingOverlay.css)
 *
 * Used by Piece A (the dictation hero). A compact stadium chip: warm near-black
 * glass, off-white ink, hairline edge, depth from inner highlight (no drop
 * shadow). Only the "recording / listening" state is needed for the demo.
 * ------------------------------------------------------------------------- */

.sf-rec-pill {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  height: 34px;
  min-width: 46px;
  max-width: 100%;
  padding: 0 10px;
  border-radius: var(--sf-pill-radius);
  background: var(--sf-pill-surface);
  border: 1px solid var(--sf-hairline);
  box-shadow: var(--sf-inner-shadow);
  backdrop-filter: var(--sf-pill-blur);
  -webkit-backdrop-filter: var(--sf-pill-blur);
  /* Reveal: fade + a small settle, toggled by .is-visible. */
  opacity: 0;
  transform: scale(0.9) translateY(2px);
  transform-origin: center;
  transition:
    opacity 240ms ease-out,
    transform 260ms var(--sf-ease),
    height 240ms var(--sf-ease);
}

.sf-rec-pill.is-visible {
  opacity: 1;
  transform: scale(1) translateY(0);
}

.sf-rec-pill .sf-pill-wave {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

/* A fixed box packs the bars into a dense spindle instead of stretching thin. */
.sf-rec-pill .sf-wave-box {
  width: 66px;
  display: flex;
  align-items: center;
}

.sf-rec-pill .sf-waveform.sm {
  height: 20px;
}

/* Cancel (✕) — hidden until the chip is hovered, so the resting chip is clean. */
.sf-pill-cancel {
  position: absolute;
  right: 3px;
  top: 50%;
  transform: translateY(-50%) scale(0.85);
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(40, 36, 33, 0.6);
  color: #f5f5f4;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition:
    opacity 140ms ease-out,
    background-color 140ms ease-out,
    transform 120ms ease-out;
}

.sf-rec-pill:hover .sf-pill-cancel {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(-50%) scale(1);
}

.sf-pill-cancel:hover {
  background: rgba(220, 38, 38, 0.32);
}

/* ---------------------------------------------------------------------------
 * 4. Assistant pill  (ported from src/assistant/AssistantPanel.css)
 *
 * Used by Piece B (the standalone cycling pill). Same glass + ink as the
 * recording pill, but a rounded-RECT silhouette (16px, matching its panel)
 * so it reads as a small window rather than the dictation stadium. States:
 * listening (reactive wave), thinking (shimmer + spinner + sparkle), speaking
 * (flow + speaker), and collapsed/dimmed (a small translucent nub).
 * ------------------------------------------------------------------------- */

.sf-apill {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  box-sizing: border-box;
  height: 34px;
  min-width: 52px;
  max-width: 100%;
  padding: 0 8px;
  border-radius: var(--sf-apill-radius);
  background: var(--sf-pill-surface);
  border: 1px solid var(--sf-hairline);
  box-shadow: var(--sf-inner-shadow);
  backdrop-filter: var(--sf-pill-blur);
  -webkit-backdrop-filter: var(--sf-pill-blur);
  user-select: none;
  /* Reveal + smooth resize between states (width/height/padding all eased). */
  opacity: 0;
  transform: scale(0.92) translateY(2px);
  transition:
    opacity 220ms ease-out,
    transform 240ms var(--sf-ease),
    width 240ms var(--sf-ease),
    height 260ms var(--sf-ease),
    padding 240ms ease-out,
    border-color 220ms ease-out,
    box-shadow 220ms ease-out;
}

.sf-apill.is-visible {
  opacity: 1;
  transform: scale(1) translateY(0);
}

/* Small icon buttons inside the pill. */
.sf-apill-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 21px;
  height: 21px;
  padding: 0;
  flex-shrink: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--sf-ink);
  cursor: pointer;
  transition:
    background 0.15s ease,
    color 0.15s ease,
    transform 0.12s ease;
}

.sf-apill-btn:hover {
  background: var(--sf-raise);
}
.sf-apill-btn:active {
  transform: scale(0.92);
}

/* The idle mic — the pill's one standing affordance (quiet disc). */
.sf-apill-mic {
  border: 1px solid var(--sf-hairline);
  background: var(--sf-raise);
}
.sf-apill-mic:hover {
  background: var(--sf-raise-strong);
}

/* Waveform container — a fixed little spindle box. */
.sf-apill-wave {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 64px;
  height: 20px;
  flex-shrink: 0;
  transition: width 220ms var(--sf-ease);
}

.sf-apill-wave.rest {
  width: 40px;
  opacity: 0.5;
}

/* Phase glyph (sparkle identity anchor, speaker while speaking). */
.sf-apill-glyph {
  display: flex;
  align-items: center;
  flex-shrink: 0;
  color: var(--sf-muted);
  pointer-events: none;
}

/* The sparkle that leads listening / thinking carries the live accent, so the
 * red reads as an intentional identity mark on one small element. */
.sf-apill-glyph.identity {
  color: var(--sf-live);
}

/* Working spinner. */
.sf-apill-spin {
  flex-shrink: 0;
  color: var(--sf-muted);
  animation: sf-apill-rotate 0.85s linear infinite;
  pointer-events: none;
}

@keyframes sf-apill-rotate {
  to {
    transform: rotate(360deg);
  }
}

/* Live-recording cue: a whisper-thin, low-opacity accent ring — the quiet warm
 * edge that tells the assistant's listening pill apart from a plain chip. */
.sf-apill.listening {
  border-color: color-mix(in srgb, var(--sf-live) 30%, var(--sf-hairline));
  box-shadow:
    var(--sf-inner-shadow),
    0 0 0 1px color-mix(in srgb, var(--sf-live) 12%, transparent);
}

/* Collapsed / idle-dim: after a spell at rest the pill fades and shrinks to a
 * small compact nub (both axes), well out of the way. Hovering snaps it back. */
.sf-apill.dimmed {
  width: 54px;
  min-width: 0;
  height: 14px;
  padding: 0;
  opacity: 0.32;
  overflow: hidden;
  cursor: default;
}

.sf-apill.dimmed > * {
  visibility: hidden;
}

.sf-apill.dimmed:hover {
  width: auto;
  min-width: 52px;
  height: 34px;
  padding: 0 8px;
  opacity: 1;
  overflow: visible;
}

.sf-apill.dimmed:hover > * {
  visibility: visible;
}

/* ---------------------------------------------------------------------------
 * 5. Piece A — Dictation hero (the writing surface)
 *
 * A clean, understated writing card: just the text and a blinking caret, with
 * the recording pill floating at the bottom. No fake toolbar or window chrome.
 * ------------------------------------------------------------------------- */

.sf-dictation {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 640px;
  margin: 0 auto;
}

.sf-doc {
  position: relative;
  min-height: 168px;
  padding: 8px 4px 64px; /* bottom room so text never sits under the pill */
  font-size: clamp(20px, 3.4vw, 30px);
  line-height: 1.45;
  font-weight: 450;
  letter-spacing: -0.01em;
  color: var(--sf-doc-ink, #1c1a18); /* host sets this per light/dark section */
}

.sf-doc-text {
  white-space: pre-wrap;
  word-break: break-word;
}

/* Blinking caret — a thin vertical bar that rides the end of the typed text. */
.sf-caret {
  display: inline-block;
  width: 2px;
  height: 1.05em;
  margin-left: 1px;
  vertical-align: text-bottom;
  background: var(--sf-accent);
  border-radius: 1px;
  animation: sf-caret-blink 1.06s steps(1) infinite;
}

@keyframes sf-caret-blink {
  0%,
  49% {
    opacity: 1;
  }
  50%,
  100% {
    opacity: 0;
  }
}

/* The pill sits centered at the bottom of the writing surface. */
.sf-dictation-pill {
  position: absolute;
  left: 50%;
  bottom: 4px;
  transform: translateX(-50%);
  display: flex;
  justify-content: center;
}

/* When the pill is anchored, cancel the reveal's translate so it stays put. */
.sf-dictation-pill .sf-rec-pill.is-visible {
  transform: scale(1);
}
.sf-dictation-pill .sf-rec-pill {
  transform: scale(0.9);
}

/* ---------------------------------------------------------------------------
 * 6. Piece B — standalone pill stage
 * ------------------------------------------------------------------------- */

.sf-pill-stage {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 72px;
  width: 100%;
}

/* Optional caption under Piece B naming the current state (demo affordance,
 * not part of the app). */
.sf-state-label {
  margin-top: 14px;
  text-align: center;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--sf-muted);
}

/* ---------------------------------------------------------------------------
 * 6b. Piece C — Screen vision
 *
 * A framed on-screen "display" the assistant looks at. A teal scan line sweeps
 * the screen while a selection marquee settles over a region (capture flash +
 * the pill's camera affordance going live); then a short answer streams into a
 * floating glass panel. All motion is scripted in demo.js; this file only paints
 * the surfaces and the resting/transition states.
 * ------------------------------------------------------------------------- */

.sf-screenvision {
  width: 100%;
}

/* The display — a dark bezel-less screen with the scene image inside. */
.sf-screen {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  border-radius: 16px;
  overflow: hidden;
  isolation: isolate;
  background: #0e1116;
  border: 1px solid var(--sf-hairline);
  box-shadow:
    0 26px 64px -30px rgba(14, 16, 22, 0.6),
    var(--sf-inner-shadow);
}

/* Fallback for engines without aspect-ratio. */
@supports not (aspect-ratio: 1 / 1) {
  .sf-screen {
    height: 0;
    padding-bottom: 62.5%; /* 16:10 */
  }
  .sf-screen > * {
    position: absolute;
  }
}

.sf-screen-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}

/* Legibility wash: darker toward the bottom (where the HUD floats) plus a faint
 * cool cast so the teal capture cues read against a bright sky. */
.sf-screen-veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to top, rgba(10, 12, 16, 0.52) 0%, rgba(10, 12, 16, 0) 44%),
    radial-gradient(120% 90% at 50% 6%, rgba(20, 184, 166, 0.06), transparent 60%);
}

/* Capture reticle — a camera-style viewfinder that locks onto a region. No full
 * border or fill (that read as a stray box); just four teal corner brackets. */
.sf-screen-marquee {
  position: absolute;
  inset: 6% 5.5%;
  opacity: 0;
  transform: scale(1.03);
  transition:
    opacity 260ms var(--sf-ease),
    transform 320ms var(--sf-ease);
  pointer-events: none;
}
.sf-screen-marquee.is-on {
  opacity: 1;
  transform: scale(1);
}

/* Four corner brackets, framing the whole display like a capture viewfinder. */
.sf-screen-marquee span {
  position: absolute;
  width: 28px;
  height: 28px;
  border: 2.5px solid var(--sf-accent);
  filter: drop-shadow(
    0 0 6px color-mix(in srgb, var(--sf-accent) 55%, transparent)
  );
}
.sf-screen-marquee span:nth-child(1) {
  top: -2px;
  left: -2px;
  border-right: 0;
  border-bottom: 0;
  border-top-left-radius: 6px;
}
.sf-screen-marquee span:nth-child(2) {
  top: -2px;
  right: -2px;
  border-left: 0;
  border-bottom: 0;
  border-top-right-radius: 6px;
}
.sf-screen-marquee span:nth-child(3) {
  right: -2px;
  bottom: -2px;
  border-left: 0;
  border-top: 0;
  border-bottom-right-radius: 6px;
}
.sf-screen-marquee span:nth-child(4) {
  bottom: -2px;
  left: -2px;
  border-right: 0;
  border-top: 0;
  border-bottom-left-radius: 6px;
}

/* Capture scan line — a thin glowing bar swept top→bottom by demo.js. */
.sf-screen-scan {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 3px;
  opacity: 0;
  pointer-events: none;
  background: linear-gradient(
    to right,
    transparent,
    color-mix(in srgb, var(--sf-accent) 85%, white) 50%,
    transparent
  );
  box-shadow: 0 0 18px 2px color-mix(in srgb, var(--sf-accent) 55%, transparent);
  will-change: top, opacity;
}

/* Capture flash. */
.sf-screen-flash {
  position: absolute;
  inset: 0;
  background: #ffffff;
  opacity: 0;
  pointer-events: none;
}

/* HUD — the floating assistant, bottom-centre over the display. */
.sf-screen-hud {
  position: absolute;
  left: 0;
  right: 0;
  bottom: clamp(14px, 4.5%, 28px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  pointer-events: none;
}

/* Message bubble: the question, then the streamed answer. */
.sf-sv-bubble {
  max-width: min(80%, 460px);
  padding: 10px 14px;
  border-radius: 13px;
  font-size: 14px;
  line-height: 1.45;
  opacity: 0;
  transform: translateY(6px);
  transition:
    opacity 240ms ease-out,
    transform 260ms var(--sf-ease);
}
.sf-sv-bubble.is-on {
  opacity: 1;
  transform: translateY(0);
}
/* The user's question — a quiet glass chip. */
.sf-sv-bubble.sf-is-ask {
  text-align: center;
  font-weight: 500;
  color: #ffffff;
  background: rgba(255, 255, 255, 0.14);
  border: 1px solid rgba(255, 255, 255, 0.24);
  backdrop-filter: blur(8px) saturate(1.2);
  -webkit-backdrop-filter: blur(8px) saturate(1.2);
}
/* The assistant's answer — the app's dark glass panel. */
.sf-sv-bubble.sf-is-answer {
  text-align: left;
  color: var(--sf-ink);
  background: var(--sf-panel-surface);
  border: 1px solid var(--sf-hairline-strong);
  box-shadow:
    var(--sf-inner-shadow),
    0 14px 34px -16px rgba(0, 0, 0, 0.65);
}

/* Pill body wrapper (so the camera can sit outside the flow). */
.sf-apill-sv {
  position: relative;
  overflow: visible;
}
.sf-apill-body {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

/* Camera affordance riding the pill's top-right corner. */
.sf-apill-cam {
  position: absolute;
  top: -9px;
  right: -9px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  color: var(--sf-muted);
  background: var(--sf-pill-surface);
  border: 1px solid var(--sf-hairline-strong);
  box-shadow: var(--sf-inner-shadow);
  backdrop-filter: var(--sf-pill-blur);
  -webkit-backdrop-filter: var(--sf-pill-blur);
  transition:
    color 200ms ease,
    border-color 200ms ease,
    box-shadow 220ms ease;
}
.sf-apill-cam.is-live {
  color: var(--sf-ink);
  border-color: color-mix(in srgb, var(--sf-live) 45%, var(--sf-hairline-strong));
  box-shadow:
    var(--sf-inner-shadow),
    0 0 0 3px color-mix(in srgb, var(--sf-live) 16%, transparent);
  animation: sf-cam-pulse 1.5s ease-in-out infinite;
}

/* The little red "recording" dot on the lens, shown only when live. */
.sf-apill-cam-dot {
  position: absolute;
  top: 0;
  right: 0;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--sf-live);
  opacity: 0;
  transform: scale(0.4);
  transition:
    opacity 200ms ease,
    transform 200ms var(--sf-ease);
}
.sf-apill-cam.is-live .sf-apill-cam-dot {
  opacity: 1;
  transform: scale(1);
}

@keyframes sf-cam-pulse {
  0%,
  100% {
    box-shadow:
      var(--sf-inner-shadow),
      0 0 0 3px color-mix(in srgb, var(--sf-live) 16%, transparent);
  }
  50% {
    box-shadow:
      var(--sf-inner-shadow),
      0 0 0 5px color-mix(in srgb, var(--sf-live) 6%, transparent);
  }
}

@media (max-width: 640px) {
  .sf-screen {
    aspect-ratio: 4 / 3;
    border-radius: 13px;
  }
  @supports not (aspect-ratio: 1 / 1) {
    .sf-screen {
      padding-bottom: 75%;
    }
  }
  .sf-sv-bubble {
    font-size: 13px;
    max-width: 88%;
  }
}

/* ---------------------------------------------------------------------------
 * 6c. Piece D — The assistant
 *
 * The app's assistant, replayed as a clean editorial exchange: a calm, ready
 * pill wakes and listens while a quiet question transcribes in, it thinks, then
 * the reply streams in as the hero line and the pill settles back to rest. It
 * uses the very same pill + waveform as every other piece. There is no panel
 * chrome (no label, logo or keycaps) — the section copy already names the
 * assistant and its shortcut — so it stays as clean as the dictation card.
 * ------------------------------------------------------------------------- */

.sf-assistant {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
}

/* Conversation — a quiet question, then the reply as the hero line. Fixed
 * height + a bottom anchor so the exchange rises out of the pill and the card
 * never jumps as the reply streams in. */
.sf-asst-thread {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 14px;
  height: 138px;
  overflow: hidden;
}

.sf-asst-msg {
  max-width: 88%;
  opacity: 0;
  transform: translateY(10px);
  transition:
    opacity 340ms var(--sf-ease),
    transform 440ms var(--sf-ease);
}
.sf-asst-msg.is-on {
  opacity: 1;
  transform: translateY(0);
}

.sf-asst-msg-text {
  color: var(--sf-doc-ink);
}
.sf-asst-msg-text strong {
  font-weight: 600;
  color: var(--sf-doc-ink);
}

/* The question you asked — quiet and secondary, a soft chip on the right. */
.sf-asst-msg.is-user {
  align-self: flex-end;
  padding: 8px 14px;
  border-radius: 15px 15px 5px 15px;
  background: color-mix(in srgb, var(--sf-doc-ink) 5%, transparent);
}
.sf-asst-msg.is-user .sf-asst-msg-text {
  font-size: 14.5px;
  line-height: 1.45;
  font-weight: 500;
  color: color-mix(in srgb, var(--sf-doc-ink) 58%, transparent);
}
/* Once the reply arrives it leads, so the question gently recedes. */
.sf-asst-thread.is-answering .sf-asst-msg.is-user {
  opacity: 0.5;
}

/* The reply — the hero line: large and inked, streamed like the dictation
 * text so it reads as the same, premium product. */
.sf-asst-msg.is-assistant {
  align-self: flex-start;
}
.sf-asst-msg.is-assistant .sf-asst-msg-text {
  font-size: clamp(17px, 1.02rem + 0.5vw, 21px);
  line-height: 1.5;
  font-weight: 450;
  letter-spacing: -0.011em;
}

/* Streaming caret on whichever line is being produced. */
.sf-asst-msg.is-live .sf-asst-msg-text::after {
  content: "";
  display: inline-block;
  width: 2px;
  height: 1.05em;
  margin-left: 3px;
  vertical-align: -3px;
  border-radius: 1px;
  background: var(--sf-accent);
  animation: sf-caret-blink 1.06s steps(1) infinite;
}
.sf-asst-msg.is-user.is-live .sf-asst-msg-text::after {
  height: 0.92em;
  vertical-align: -2px;
  background: color-mix(in srgb, var(--sf-doc-ink) 42%, transparent);
}

/* Dock — the real assistant pill, centred below the exchange. */
.sf-asst-dock {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 52px;
  margin-top: 12px;
}

@media (max-width: 640px) {
  .sf-asst-thread {
    height: 150px;
  }
}

/* ---------------------------------------------------------------------------
 * 7. Light-surface adaptation
 *    Add class "sf-on-light" on a scope that sits over a light section to
 *    swap the waveform bars to deeper beige/taupe (matches the app's light
 *    theme waveform) and darken the dictation ink.
 * ------------------------------------------------------------------------- */

.sf-scope.sf-on-light {
  --sf-wave-bar: linear-gradient(to top, #0d9488, #5eead4 78%);
  --sf-doc-ink: #1c1a18;
}

.sf-scope.sf-on-dark {
  --sf-doc-ink: #f2efec;
}

/* ---------------------------------------------------------------------------
 * 8. Reduced motion — freeze to a tasteful static state.
 *    (mirrors the @media blocks in every source stylesheet)
 * ------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .sf-scope .sf-wave-bar,
  .sf-scope .sf-rec-pill,
  .sf-scope .sf-apill,
  .sf-scope .sf-apill-wave,
  .sf-scope .sf-asst-msg,
  .sf-scope .sf-pill-cancel {
    transition: none;
  }

  .sf-scope .sf-waveform.is-idle .sf-wave-bar,
  .sf-scope .sf-waveform.shimmer .sf-wave-bar,
  .sf-scope .sf-waveform.flow .sf-wave-bar,
  .sf-scope .sf-apill-spin,
  .sf-scope .sf-apill-cam.is-live,
  .sf-scope .sf-asst-msg.is-live .sf-asst-msg-text::after {
    animation: none;
  }

  /* Piece C — no scan/flash/marquee motion; the JS renders a single static
     "captured + answered" frame, so just drop the transitions. */
  .sf-scope .sf-screen-marquee,
  .sf-scope .sf-sv-bubble,
  .sf-scope .sf-apill-cam,
  .sf-scope .sf-apill-cam-dot {
    transition: none;
  }

  /* Rest the idle dots at a calm middle opacity instead of pulsing. */
  .sf-scope .sf-waveform.is-idle .sf-wave-bar {
    opacity: 0.5;
  }

  /* Freeze the caret solid (no blink). */
  .sf-scope .sf-caret {
    animation: none;
    opacity: 1;
  }
}
