/* ===========================================================================
   SEENN IN-BROWSER VOICE DEMO — styles
   ---------------------------------------------------------------------------
   The `.preview-orb*` block below is a PORT of the orb from the dashboard app
   repo:  smart-secretary-ai/src/index.css  (component: src/components/
   preview-call/SecretaryOrb.tsx).

   Class names, gradients, keyframes and per-state animation durations are kept
   byte-for-byte identical to the origin so the two copies can be diffed and do
   not silently diverge. Only two things are additive here, both marked
   "WIDGET ADDITION":

     1. a `--prompting` state (the dashboard has no microphone step), and
     2. `--orb-level`, a JS-driven 0..1 audio level so the orb reacts while the
        secretary speaks.

   The origin maps onto this widget's states as:
     idle -> idle | prompting -> (new) | connecting -> submitting
     live -> dialing | ended -> onTheWay | denied/busy/error -> failed

   Everything under `.svd*` is widget chrome and has no counterpart upstream.
   Pure CSS gradients + keyframes — no animation library.
   =========================================================================== */

/* ---------------------------------------------------------------------------
   HERO INTEGRATION
   The first fold becomes a balanced split: the existing headline, copy and
   business CTA on one side, a live-demo card on the other. Nothing about the
   site's type scale, palette, radii or shadows is invented here — the card
   borrows the gold rim and deep-navy glass already used by the orb, and the
   button reuses the hero CTA's own gold.
   --------------------------------------------------------------------------- */

.hero-split {
  display: grid;
  grid-template-columns: 1fr;
  gap: 2rem;
  align-items: center;
  max-width: 72rem;
  margin: 0 auto;
  /* Grid items default to min-width:auto, so a wide child pushes the track
     past the viewport and the whole hero scrolls sideways on a phone. */
  min-width: 0;
}

.hero-split > * {
  min-width: 0;
}

@media (min-width: 1024px) {
  .hero-split {
    grid-template-columns: 1.05fr 0.95fr;
    gap: 3.5rem;
  }
}

/* Alignment is expressed with Tailwind's own responsive utilities in the
   markup (text-center lg:text-start, justify-center lg:justify-start), not
   here: Tailwind's CDN styles are injected after this file, so competing
   declarations would lose and leave the copy centred above a left-aligned
   button — which is exactly what happened first time. */

/* The card. Deliberately quiet: it sits *in* the aurora hero rather than on
   top of it, so the orb reads as part of the artwork. */
.hero-demo {
  position: relative;
  width: 100%;
  max-width: min(26rem, 100%);
  margin-inline: auto;
  /* No panel: no border, no background, no blur, no shadow. The demo sits
     directly on the aurora, which is what "connected to the fold" finally
     means — there is no edge left to disconnect. */
  padding: 0;
}

/* One soft pool of light behind the ball so it has somewhere to sit without a
   box. Ambient, not a container. */
.hero-demo::before {
  content: '';
  position: absolute;
  inset: -18% -20% -10%;
  z-index: -1;
  background: radial-gradient(
    46% 38% at 50% 28%,
    rgba(240, 193, 75, 0.14) 0%,
    rgba(120, 132, 214, 0.08) 46%,
    rgba(9, 11, 26, 0) 74%
  );
  filter: blur(30px);
  pointer-events: none;
}

/* Compact on a phone. The orb's size is an inline style set by JS, so this
   needs !important — the alternative is a second source of truth for the
   size. Keeping the card short is what lets the start button sit at or near
   the fold instead of the orb swallowing the screen. */
@media (max-width: 640px) {
  .hero-demo {
    padding: 1.1rem 1rem 1.3rem;
  }

  .hero-demo .preview-orb {
    width: 146px !important;
    height: 146px !important;
  }

  .hero-demo .svd__stage {
    padding-bottom: 24px;
  }

  .hero-demo .svd__start {
    margin-top: 0.75rem;
  }
}

/* Reserve the widget's height before JS runs, so a page that *will* show the
   widget never reflows on load.

   The `:not([hidden])` matters: PUBLIC_DEMO_MODE is disabled by default, and
   the mount ships with `hidden` set. Reserving unconditionally would leave a
   360px gap in the hero from first paint until the deferred bundle runs and
   hides it — a visible hole for a feature that is switched off. The widget
   removes `hidden` itself, and only once it has actually mounted. */
.svd-mount:not([hidden]) {
  min-height: 360px;
}

@media (max-width: 640px) {
  .svd-mount:not([hidden]) {
    min-height: 300px;
  }
}

/* Every state must occupy the same space, or the hero jumps as the call moves
   from connecting to listening to an error. The copy block is the only part
   whose length varies, so it — not the card — carries the reservation. */
.svd__copy {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  min-height: 7.5rem;
  width: 100%;
}

@media (max-width: 640px) {
  .svd__copy {
    min-height: 6.5rem;
  }
}

/* ---------------------------------------------------------------------------
   PORTED ORB — keep in sync with smart-secretary-ai/src/index.css
   --------------------------------------------------------------------------- */

.preview-orb {
  position: relative;
  display: grid;
  place-items: center;
  isolation: isolate;
  transition: filter 600ms ease, opacity 600ms ease;
  /* WIDGET ADDITION: audio level, written by JS on every animation frame. */
  --orb-level: 0;
}

/* Soft halo bleeding past the orb edge */
.preview-orb__glow {
  position: absolute;
  inset: -14%;
  border-radius: 9999px;
  background: radial-gradient(
    circle at 50% 55%,
    rgba(240, 193, 75, 0.28) 0%,
    rgba(240, 193, 75, 0.10) 38%,
    rgba(30, 30, 45, 0) 70%
  );
  filter: blur(18px);
  animation: preview-orb-breathe 7s ease-in-out infinite;
}

/* Stacked, heavily blurred gradient layers drifting at different speeds */
.preview-orb__layer {
  position: absolute;
  inset: 0;
  border-radius: 9999px;
  will-change: transform, opacity;
}

.preview-orb__layer {
  opacity: 0.35;
}

.preview-orb__layer--a {
  background: radial-gradient(
      circle at 32% 30%,
      rgba(245, 217, 139, 0.95) 0%,
      rgba(232, 178, 58, 0.75) 26%,
      rgba(120, 92, 60, 0.35) 52%,
      rgba(15, 18, 38, 0.95) 78%
    ),
    radial-gradient(circle at 70% 78%, rgba(60, 68, 122, 0.9) 0%, rgba(15, 18, 38, 0) 60%);
  filter: blur(14px);
  /* Full 360° sweep: the off-centre highlight travels around the sphere. */
  animation: preview-orb-swirl-a 22s linear infinite;
}

.preview-orb__layer--b {
  background: radial-gradient(
    circle at 72% 68%,
    rgba(120, 132, 214, 0.55) 0%,
    rgba(42, 47, 85, 0.5) 34%,
    rgba(15, 18, 38, 0) 68%
  );
  filter: blur(20px);
  mix-blend-mode: screen;
  /* Counter-rotation against layer A so the two never move as one mass. */
  animation: preview-orb-swirl-b 30s linear infinite;
}

.preview-orb__layer--c {
  background: radial-gradient(
    circle at 46% 18%,
    rgba(255, 245, 220, 0.7) 0%,
    rgba(240, 193, 75, 0) 42%
  );
  filter: blur(16px);
  /* Specular highlight orbiting the upper half. */
  animation: preview-orb-orbit 12s ease-in-out infinite;
}

/* Liquid inner wisp — inset far enough that its morphing edge never reaches
   the rim, so the silhouette stays a clean circle. */
.preview-orb__wisp {
  opacity: 0.18 !important;
  position: absolute;
  inset: 12%;
  background: linear-gradient(
    135deg,
    rgba(255, 236, 190, 0.5) 0%,
    rgba(240, 193, 75, 0.22) 45%,
    rgba(60, 68, 122, 0) 78%
  );
  filter: blur(16px);
  mix-blend-mode: screen;
  /* Dialled back: the video core now carries most of the internal movement. */
  opacity: 0.45;
  will-change: transform, border-radius;
  animation: preview-orb-wisp 17s ease-in-out infinite;
}

/* Looping liquid video, masked to a circle and faded at the edge so it melts
   into the gradient sphere instead of reading as a disc pasted on top.

   WIDGET NOTE: nothing currently injects this element — the widget builds a
   CSS-only orb, and the source video (orb-core.mp4) is not shipped with the
   site. These rules are kept so this file still diffs cleanly against the
   origin in smart-secretary-ai/src/index.css. Copy the asset from that repo's
   public/avatars/ if the video core is ever wanted here. */
.preview-orb__core {
  position: absolute;
  /* Edge to edge. Previously inset 5% behind a radial mask fading from 56%,
     which left the ball floating loose inside the sphere instead of the circle
     framing it. The circle IS the ball's frame now. */
  inset: 0;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  opacity: 1;
  pointer-events: none;
  transition: opacity 400ms ease;
}

.preview-orb--dialing .preview-orb__core,
.preview-orb--idle .preview-orb__core {
  opacity: 1;
}

/* Thin rim keeps the blurred mass reading as a sphere */
.preview-orb__rim {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  /* A hairline ring sitting exactly on the edge, plus depth beneath. */
  /* Only a hairline ring plus outer depth. The heavy inner bottom shadow bit
     a dark arc out of the sphere once the card behind it was removed. */
  box-shadow:
    inset 0 0 0 1px rgba(245, 214, 130, 0.38),
    0 20px 44px rgba(9, 11, 26, 0.45);
}

/* Concentric ripples — dialing state only */
.preview-orb__ripple {
  position: absolute;
  inset: 0;
  border-radius: 9999px;
  border: 1px solid rgba(240, 193, 75, 0.55);
  animation: preview-orb-ripple 2.6s ease-out infinite;
}

.preview-orb__ripple--2 {
  animation-delay: 0.85s;
}

.preview-orb__ripple--3 {
  animation-delay: 1.7s;
}

/* Dark circular call button overlapping the orb's lower centre */
.preview-orb__call {
  position: absolute;
  bottom: -14px;
  /* Explicit centring: an absolutely positioned child of a grid container
     should not depend on static-position alignment. */
  left: 50%;
  transform: translate(-50%, 0);
  z-index: 2;
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  padding: 0;
  border: 0;
  cursor: pointer;
  border-radius: 9999px;
  color: #f0c14b;
  background: linear-gradient(160deg, #23253a 0%, #12131f 100%);
  box-shadow: 0 10px 24px rgba(9, 11, 26, 0.55),
    inset 0 0 0 1px rgba(240, 193, 75, 0.35);
  transition: transform 180ms ease, box-shadow 180ms ease, color 180ms ease;
}

.preview-orb__call:hover:not(:disabled) {
  transform: translate(-50%, -2px);
  color: #ffdc86;
  box-shadow: 0 14px 30px rgba(9, 11, 26, 0.6),
    inset 0 0 0 1px rgba(240, 193, 75, 0.7);
}

.preview-orb__call:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(240, 193, 75, 0.45),
    0 10px 24px rgba(9, 11, 26, 0.55);
}

.preview-orb__call:disabled {
  cursor: not-allowed;
  color: rgba(240, 193, 75, 0.4);
  box-shadow: 0 6px 16px rgba(9, 11, 26, 0.4),
    inset 0 0 0 1px rgba(255, 255, 255, 0.12);
}

/* --- States ------------------------------------------------------------- */

/* Idle: slow breathing, muted */
.preview-orb--idle {
  filter: saturate(0.82) brightness(0.92);
}

/* WIDGET ADDITION — Prompting: everything holds its breath while the browser
   permission sheet is up, so the page is visibly waiting on the visitor. */
.preview-orb--prompting {
  filter: saturate(0.7) brightness(0.86);
}

.preview-orb--prompting .preview-orb__glow,
.preview-orb--prompting .preview-orb__layer,
.preview-orb--prompting .preview-orb__wisp {
  animation-play-state: paused;
}

/* Submitting: same palette, everything winds up */
.preview-orb--submitting .preview-orb__glow {
  animation-duration: 1.6s;
}

.preview-orb--submitting .preview-orb__layer {
  opacity: 0.35;
}

.preview-orb__layer--a {
  animation-duration: 8s;
}

.preview-orb--submitting .preview-orb__layer--b {
  animation-duration: 11s;
}

.preview-orb--submitting .preview-orb__layer--c {
  animation-duration: 4.5s;
}

.preview-orb--submitting .preview-orb__wisp {
  animation-duration: 6s;
}

/* Dialing / ringing: brighter, faster, with ripples */
.preview-orb--dialing {
  filter: saturate(1.16) brightness(1.12);
}

.preview-orb--dialing .preview-orb__glow {
  animation-duration: 2.4s;
}

.preview-orb--dialing .preview-orb__layer {
  opacity: 0.35;
}

.preview-orb__layer--a {
  animation-duration: 11s;
}

.preview-orb--dialing .preview-orb__layer--b {
  animation-duration: 15s;
}

.preview-orb--dialing .preview-orb__layer--c {
  animation-duration: 6s;
}

.preview-orb--dialing .preview-orb__wisp {
  animation-duration: 9s;
}

/* Call is on its way — calm, still lit, still moving */
.preview-orb--onTheWay {
  filter: saturate(0.95) brightness(1.02);
}

.preview-orb--onTheWay .preview-orb__glow {
  animation-duration: 5s;
}

.preview-orb--onTheWay .preview-orb__layer {
  opacity: 0.35;
}

.preview-orb__layer--a {
  animation-duration: 18s;
}

.preview-orb--onTheWay .preview-orb__layer--c {
  animation-duration: 10s;
}

/* Failed: desaturate to grey */
.preview-orb--failed {
  filter: grayscale(1) brightness(0.68);
}

.preview-orb--failed .preview-orb__glow,
.preview-orb--failed .preview-orb__layer,
.preview-orb--failed .preview-orb__wisp {
  animation-play-state: paused;
}

@keyframes preview-orb-breathe {
  0%,
  100% {
    transform: scale(1);
    opacity: 0.85;
  }
  50% {
    transform: scale(1.05);
    opacity: 1;
  }
}

/* 0% and 100% match so the linear loop never jumps. */
@keyframes preview-orb-swirl-a {
  0% {
    transform: rotate(0deg) scale(1.02) translate(0, 0);
  }
  25% {
    transform: rotate(90deg) scale(1.05) translate(1%, -1%);
  }
  50% {
    transform: rotate(180deg) scale(1.03) translate(0, 1%);
  }
  75% {
    transform: rotate(270deg) scale(1.06) translate(-1%, 0);
  }
  100% {
    transform: rotate(360deg) scale(1.02) translate(0, 0);
  }
}

@keyframes preview-orb-swirl-b {
  0% {
    transform: rotate(0deg) scale(1.04);
    opacity: 0.85;
  }
  33% {
    transform: rotate(-120deg) scale(1);
    opacity: 1;
  }
  66% {
    transform: rotate(-240deg) scale(1.06);
    opacity: 0.8;
  }
  100% {
    transform: rotate(-360deg) scale(1.04);
    opacity: 0.85;
  }
}

@keyframes preview-orb-orbit {
  0% {
    transform: translate(0, 0) scale(1);
    opacity: 0.55;
  }
  25% {
    transform: translate(6%, 4%) scale(1.08);
    opacity: 0.9;
  }
  50% {
    transform: translate(2%, 9%) scale(1.02);
    opacity: 0.7;
  }
  75% {
    transform: translate(-5%, 4%) scale(1.09);
    opacity: 0.95;
  }
  100% {
    transform: translate(0, 0) scale(1);
    opacity: 0.55;
  }
}

@keyframes preview-orb-wisp {
  0% {
    border-radius: 46% 54% 52% 48% / 50% 46% 54% 50%;
    transform: rotate(0deg) scale(1);
  }
  50% {
    border-radius: 54% 46% 48% 52% / 46% 54% 50% 50%;
    transform: rotate(180deg) scale(1.07);
  }
  100% {
    border-radius: 46% 54% 52% 48% / 50% 46% 54% 50%;
    transform: rotate(360deg) scale(1);
  }
}

@keyframes preview-orb-ripple {
  0% {
    transform: scale(0.86);
    opacity: 0.55;
  }
  100% {
    transform: scale(1.55);
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .preview-orb__glow,
  .preview-orb__layer,
  .preview-orb__wisp,
  .preview-orb__ripple {
    animation: none !important;
  }
}

/* ---------------------------------------------------------------------------
   WIDGET ADDITION — audio-reactive level
   The orb tracks the secretary's voice. Two channels, both cheap: a ring that
   scales, and a brightness lift on the whole sphere. The transition is short
   (90ms) so it reads as speech rather than as a slow pulse, and it overrides
   the 600ms state transition only while live.
   --------------------------------------------------------------------------- */

.preview-orb--dialing.preview-orb--reactive {
  filter: saturate(calc(1.16 + var(--orb-level) * 0.22))
    brightness(calc(1.06 + var(--orb-level) * 0.34));
  transition: filter 90ms linear;
}

/* Absolutely positioned so these stay out of the orb's grid flow — every other
   child of `.preview-orb` is out of flow too, and an in-flow item would become
   the grid's only sized track. */
.svd__ripples {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* Placeholder for the video core until it is injected on first activation. */
.svd__core-slot {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.svd__level {
  position: absolute;
  inset: 0;
  border-radius: 9999px;
  pointer-events: none;
  border: 2px solid rgba(240, 193, 75, calc(0.10 + var(--orb-level) * 0.55));
  transform: scale(calc(1 + var(--orb-level) * 0.09));
  transition: transform 90ms linear, border-color 90ms linear;
}

@media (prefers-reduced-motion: reduce) {
  /* Keep the brightness cue — it carries the information — but stop the ring
     from moving. */
  .svd__level {
    transform: none;
    transition: border-color 120ms linear;
  }
}

/* ---------------------------------------------------------------------------
   WIDGET CHROME
   Rebuilt for the ten-state machine. State names come from src/state.ts;
   `hidden` is the mechanism for showing and hiding panels, so the reset below
   has to win against every `display` rule in this file.
   --------------------------------------------------------------------------- */

.svd [hidden] {
  display: none !important;
}

.svd {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  width: 100%;
  max-width: min(34rem, 100%);
  margin: 0 auto;
  padding-top: 0.5rem;
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
  color: #fff;
  text-align: center;
}

.svd__stage {
  /* Room for the call button, which hangs 14px below the sphere. */
  padding-bottom: 30px;
}

.svd__headline {
  margin: 0;
  font-family: 'Montserrat', 'Inter', sans-serif;
  font-size: 1.125rem;
  font-weight: 600;
  line-height: 1.4;
  letter-spacing: -0.01em;
  color: #fff;
  text-shadow: 0 1px 12px rgba(9, 11, 26, 0.55);
}

.svd__sub {
  margin: 0.35rem 0 0;
  max-width: 26rem;
  font-size: 0.875rem;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.78);
  text-shadow: 0 1px 10px rgba(9, 11, 26, 0.5);
  /* Monospace digits so the countdown does not jitter the line each second. */
  font-variant-numeric: tabular-nums;
}

.svd__hint {
  max-width: 26rem;
  margin: 0.5rem 0 0;
  font-size: 0.8125rem;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.7);
}

/* The orb's own circular button is redundant inside the hero card: the
   full-width labelled CTA starts the demo and the disconnect button ends it.
   Hiding it keeps the ball cleanly framed and leaves exactly one primary
   action, which is the rule the design skill flags hardest. It stays in the
   DOM so keyboard users and the staging surface keep the control. */
.hero-demo .preview-orb__call {
  display: none;
}

/* --- Card header ---------------------------------------------------------- */

/* Header row removed: its "Talk to Jess — live" label duplicated the button. */

/* Folded into the footer line instead — fewer words, one place to read them. */
.svd__duration {
  display: none;
}

.svd__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
}

.svd__pulse {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #4ade9b;
  box-shadow: 0 0 0 0 rgba(74, 222, 155, 0.5);
  animation: svd-pulse 2.4s ease-out infinite;
}

@keyframes svd-pulse {
  0% { box-shadow: 0 0 0 0 rgba(74, 222, 155, 0.45); }
  70% { box-shadow: 0 0 0 7px rgba(74, 222, 155, 0); }
  100% { box-shadow: 0 0 0 0 rgba(74, 222, 155, 0); }
}

.svd__duration {
  margin: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.5);
}

/* The divider belonged to the card; without one there is nothing to divide. */
.svd__rule {
  display: none;
}

/* --- Orb beside the copy, as in the reference ----------------------------- */

.svd__body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.15rem;
  width: 100%;
  text-align: center;
}

.svd__body .svd__stage {
  flex: 0 0 auto;
  padding-bottom: 0;
}

.svd__body .svd__copy {
  align-items: center;
  text-align: center;
  min-height: 0;
}

/* --- Equaliser ------------------------------------------------------------
   Replaces the single sine line. Bars carry the same meaning — amplitude is
   bound to the agent's real audio level while a call is live — but read far
   more like a voice signature at this size. */

.svd__bars {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 3px;
  width: 100%;
  height: 44px;
  margin-top: 1.1rem;
}

.svd__bar {
  flex: 1 1 0;
  min-width: 2px;
  max-width: 5px;
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(245, 214, 130, 0.95), rgba(201, 181, 85, 0.7));
  /* Idle rhythm; --wave-level lifts the whole row while she speaks. */
  height: calc((18% + var(--h) * 46%) * (1 + var(--wave-level, 0) * 1.1));
  animation: svd-bar 1.15s ease-in-out infinite alternate;
  animation-delay: var(--d, 0s);
  transition: height 110ms linear;
}

@keyframes svd-bar {
  from { transform: scaleY(0.42); }
  to { transform: scaleY(1); }
}

@media (prefers-reduced-motion: reduce) {
  .svd__bar {
    animation: none;
    transform: scaleY(0.8);
  }
  .svd__pulse {
    animation: none;
  }
}

/* --- Living waveform ------------------------------------------------------
   The signature of a voice product. It sits behind the orb and runs wider than
   the card, bleeding past both edges, which is the second thing tying the card
   to the hero: the motion continues where the panel stops.

   It carries meaning rather than decoration — the amplitude tracks the agent's
   real audio level while a call is live, and it idles low when nothing is
   happening. */

.svd__wave {
  display: none;
  position: absolute;
  left: -8%;
  right: -8%;
  top: 44%;
  height: 110px;
  z-index: 0;
  pointer-events: none;
  opacity: 0.85;
  /* Fade at both ends so the wave has no hard start or stop. */
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 18%, #000 82%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0%, #000 18%, #000 82%, transparent 100%);
  transition: opacity 400ms ease;
}

.svd__wave svg {
  width: 200%;
  height: 100%;
  overflow: visible;
}

.svd__wave-track {
  /* Two periods are drawn and the group travels exactly one period, so the
     loop is seamless with no visible jump. */
  animation: svd-wave-drift 9s linear infinite;
  transform-origin: center;
}

.svd__wave-line {
  fill: none;
  stroke: rgba(245, 214, 130, 0.75);
  stroke-width: 2;
  stroke-linecap: round;
  /* Amplitude follows the agent's voice while live; idles gently otherwise. */
  transform: scaleY(calc(0.55 + var(--wave-level, 0) * 0.85));
  transform-origin: center;
  transition: transform 120ms linear;
}

.svd__wave-line--back {
  stroke: rgba(190, 205, 255, 0.42);
  transform: translateX(52px) scaleY(calc(0.42 + var(--wave-level, 0) * 0.7));
}

@keyframes svd-wave-drift {
  to {
    transform: translateX(-400px);
  }
}

/* Live states lift the wave forward; idle keeps it as quiet atmosphere. */
.svd[data-state='listening'] .svd__wave,
.svd[data-state='assistantThinking'] .svd__wave,
.svd[data-state='assistantSpeaking'] .svd__wave {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .svd__wave-track {
    animation: none;
  }

  .svd__wave-line {
    transition: none;
  }
}

/* The orb sits above the wave. */
.svd__stage,
.svd__copy,
.svd__eyebrow {
  position: relative;
  z-index: 1;
}

/* Small label above the orb. */
.svd__eyebrow {
  margin: 0 0 0.75rem;
  font-family: 'Montserrat', 'Inter', sans-serif;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #f0c14b;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
}

/* The labelled primary control. Reuses the hero CTA's gold so the two read as
   the same family, at a lower weight so the sales CTA still leads. */
.svd__start {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  /* SECONDARY by design. "Request a Demo" is the page's primary action and
     stays solid gold; this must read as the quieter of the two — outlined,
     narrower and lighter — while still being obviously clickable. */
  width: auto;
  max-width: 100%;
  margin-top: 1rem;
  padding: 0.6rem 1.15rem;
  border: 1px solid rgba(240, 193, 75, 0.55);
  border-radius: 999px;
  background: rgba(9, 11, 26, 0.34);
  color: #f0c14b;
  font-family: 'Montserrat', 'Inter', sans-serif;
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease;
}

.svd__start-icon {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
}

.svd__start-mic {
  width: 15px;
  height: 15px;
}

.svd__start-support {
  margin: 0.5rem 0 0;
  font-size: 0.75rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.66);
}

.svd__start:hover {
  background: rgba(240, 193, 75, 0.14);
  border-color: rgba(240, 193, 75, 0.85);
  color: #ffdc86;
}

.svd__start:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 3px;
}

.svd__meta {
  margin: 0.35rem 0 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.625rem;
  letter-spacing: 0.02em;
  color: rgba(255, 255, 255, 0.34);
}

/* Reassurance that the assistant is multilingual. Deliberately quiet: it
   replaces a language picker rather than pointing at one. */
.svd__adaptive {
  max-width: 26rem;
  margin: 0.4rem 0 0;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.62);
  text-shadow: 0 1px 10px rgba(9, 11, 26, 0.5);
}

/* --- Recording consent ---------------------------------------------------
   The notice text is server-authored and rendered verbatim; only the frame is
   ours. Sized so a two-paragraph legal notice does not blow the hero apart. */

.svd__consent {
  width: 100%;
  max-width: 30rem;
  margin-top: 1rem;
  padding: 1rem 1.15rem 1.15rem;
  border: 1px solid rgba(240, 193, 75, 0.35);
  border-radius: 0.75rem;
  background: rgba(9, 11, 26, 0.55);
  backdrop-filter: blur(6px);
  text-align: start;
}

.svd__consent-heading {
  margin: 0 0 0.5rem;
  font-family: 'Montserrat', 'Inter', sans-serif;
  font-size: 0.9375rem;
  font-weight: 600;
  color: #f0c14b;
}

.svd__consent-text {
  margin: 0;
  max-height: 9rem;
  overflow-y: auto;
  font-size: 0.8125rem;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.85);
  white-space: pre-line;
}

.svd__consent-link {
  display: inline-block;
  margin-top: 0.5rem;
  font-size: 0.8125rem;
  color: #f0c14b;
  text-underline-offset: 3px;
}

.svd__consent-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.9rem;
}

.svd__consent-accept,
.svd__consent-decline {
  flex: 1 1 auto;
  padding: 0.6rem 1.1rem;
  border: 0;
  border-radius: 0.5rem;
  font-family: 'Montserrat', 'Inter', sans-serif;
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  transition: background-color 150ms ease, color 150ms ease;
}

.svd__consent-accept {
  background: #c9b555;
  color: #000;
}

.svd__consent-accept:hover {
  background: #bfa34a;
}

.svd__consent-decline {
  background: transparent;
  color: rgba(255, 255, 255, 0.8);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.25);
}

.svd__consent-decline:hover {
  color: #fff;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.5);
}

.svd__consent-accept:focus-visible,
.svd__consent-decline:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* --- Disconnect ----------------------------------------------------------
   A visible, separate control. The orb doubles as hang-up, but a session the
   visitor cannot obviously end is a session they will abandon by closing the
   tab — which is exactly the leak the cleanup path exists to avoid. */

.svd__disconnect {
  margin-top: 0.85rem;
  padding: 0.45rem 1.1rem;
  border: 0;
  border-radius: 9999px;
  background: rgba(9, 11, 26, 0.5);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.28);
  color: rgba(255, 255, 255, 0.85);
  font-family: 'Inter', sans-serif;
  font-size: 0.8125rem;
  cursor: pointer;
  transition: color 150ms ease, box-shadow 150ms ease;
}

.svd__disconnect:hover {
  color: #fff;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.55);
}

.svd__disconnect:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* --- Support ID ----------------------------------------------------------
   Shown only when the remote agent never became ready. Carries the backend
   session id and nothing else — no token, no room URL, no credentials. */

.svd__support {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 0.9rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 0.5rem;
  background: rgba(9, 11, 26, 0.45);
}

.svd__support-label {
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}

.svd__support-id {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.8125rem;
  color: #f0c14b;
  user-select: all;
  word-break: break-all;
}

.svd__support-copy {
  padding: 0.25rem 0.6rem;
  border: 0;
  border-radius: 0.35rem;
  background: rgba(255, 255, 255, 0.12);
  color: #fff;
  font-family: inherit;
  font-size: 0.75rem;
  cursor: pointer;
}

.svd__support-copy:hover {
  background: rgba(255, 255, 255, 0.22);
}

.svd__support-copy:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* --- Conversion moment ---------------------------------------------------- */

.svd__cta {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  margin-top: 1rem;
}

.svd__cta-button {
  display: inline-block;
  padding: 0.75rem 2rem;
  border-radius: 0.5rem;
  background: #c9b555;
  color: #000;
  font-family: 'Montserrat', 'Inter', sans-serif;
  font-size: 1rem;
  font-weight: 500;
  text-decoration: none;
  transition: background-color 150ms ease, transform 150ms ease;
}

.svd__cta-button:hover {
  background: #bfa34a;
  transform: translateY(-1px);
}

.svd__cta-button:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* --- State tweaks --------------------------------------------------------- */

/* While consent is up, the orb is not the subject — the notice is. */
.svd--consent .svd__stage {
  opacity: 0.55;
  transition: opacity 250ms ease;
}

/* Unavailable and rate-limited are dead ends: nothing to click on the orb. */
.svd[data-state='unavailable'] .preview-orb__call {
  cursor: default;
}

.svd__audio {
  display: none;
}

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

.svd-spin {
  animation: svd-spin 900ms linear infinite;
}

@keyframes svd-spin {
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .svd-spin {
    animation: none;
  }
}

/* --- RTL -----------------------------------------------------------------
   The widget sets dir on its own root from the resolved locale, so Hebrew and
   Arabic are correct even when embedded in an LTR page. Centred content needs
   no mirroring; only the consent block is text-aligned, and it uses `start`. */

.svd[dir='rtl'] .svd__consent {
  text-align: start;
}


/* =========================================================================
   Recording consent
   -------------------------------------------------------------------------
   Rendered only when `recordingConsentMode: 'required'` is set in the page's
   config block. The committed pages do not set it, so none of this is on the
   live site today — see tools/voice-demo/CONFIGURATION.md §7.

   The disclosure sits between the outlined start button and the "~2 minutes"
   line: a visitor should read it before pressing, not after. It is deliberately
   legible rather than decorative — greying it out until it disappears is how a
   disclosure becomes a dark pattern.
   ========================================================================= */

.svd__rec {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  margin-top: 0.6rem;
  padding-inline: 0.5rem;
  font-family: 'Inter', sans-serif;
  /* 12.5px: small, but above the site's own fine print. */
  font-size: 0.78rem;
  line-height: 1.5;
  /* ~7:1 on the aurora behind it. Readable is the whole point. */
  color: rgba(233, 239, 247, 0.86);
}

.svd__rec-icon {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  color: #f0c14b;
}

.svd__rec-lock {
  width: 13px;
  height: 13px;
}

.svd__rec-sep {
  opacity: 0.5;
}

.svd__rec-details {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: #f0c14b;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.svd__rec-details:hover {
  color: #f7d582;
}

.svd__rec-details:focus-visible {
  outline: 2px solid #f0c14b;
  outline-offset: 3px;
  border-radius: 3px;
}

/* ---------- The gate ---------- */

.svd__gate {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}

.svd__gate[hidden] {
  display: none;
}

.svd__gate-scrim {
  position: absolute;
  inset: 0;
  background: rgba(6, 8, 18, 0.62);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
}

/* Same deep-navy glass as the orb card, so the dialog reads as part of the
   demo rather than as a browser chrome prompt bolted on top of it. */
.svd__gate-panel {
  position: relative;
  width: min(26rem, 100%);
  max-height: calc(100vh - 2rem);
  overflow-y: auto;
  padding: 1.5rem 1.4rem 1.25rem;
  border: 1px solid rgba(240, 193, 75, 0.28);
  border-radius: 18px;
  background: linear-gradient(165deg, rgba(19, 25, 48, 0.97) 0%, rgba(9, 11, 26, 0.98) 100%);
  box-shadow: 0 28px 70px rgba(3, 5, 15, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.06);
  color: #e9eff7;
  text-align: start;
}

.svd__gate-title {
  margin: 0 0 0.75rem;
  font-family: 'Montserrat', 'Inter', sans-serif;
  font-size: 1.1rem;
  font-weight: 700;
  color: #fff;
}

.svd__gate-body {
  margin-bottom: 1rem;
}

.svd__gate-text {
  margin: 0 0 0.6rem;
  font-family: 'Inter', sans-serif;
  font-size: 0.9rem;
  line-height: 1.6;
  color: rgba(233, 239, 247, 0.9);
}

.svd__gate-text:last-child {
  margin-bottom: 0;
}

.svd__gate-policy {
  display: inline-block;
  margin-bottom: 1.15rem;
  font-family: 'Inter', sans-serif;
  font-size: 0.85rem;
  color: #f0c14b;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.svd__gate-policy:hover {
  color: #f7d582;
}

.svd__gate-actions {
  display: flex;
  gap: 0.6rem;
  justify-content: flex-end;
}

/* Order lives in the DOM (dismiss first, affirmative last) and is not flipped
   here: `flex-end` already resolves to the correct edge in both directions, so
   a screen reader and a sighted RTL reader get the same order. */

.svd__gate-back,
.svd__gate-agree {
  min-height: 44px;
  padding: 0.55rem 1.15rem;
  border-radius: 999px;
  font-family: 'Montserrat', 'Inter', sans-serif;
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease;
}

.svd__gate-back {
  border: 1px solid rgba(233, 239, 247, 0.28);
  background: transparent;
  color: rgba(233, 239, 247, 0.9);
}

.svd__gate-back:hover {
  border-color: rgba(233, 239, 247, 0.5);
  color: #fff;
}

.svd__gate-agree {
  border: 1px solid #f0c14b;
  background: #f0c14b;
  color: #10131f;
}

.svd__gate-agree:hover {
  background: #f7d582;
  border-color: #f7d582;
}

.svd__gate-back:focus-visible,
.svd__gate-agree:focus-visible,
.svd__gate-policy:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* ---------- Phone: a bottom sheet, not a centred box ---------- */

@media (max-width: 640px) {
  .svd__gate {
    align-items: flex-end;
    padding: 0;
  }

  .svd__gate-panel {
    width: 100%;
    max-width: none;
    /* Clears the home indicator on iOS. */
    padding-bottom: calc(1.25rem + env(safe-area-inset-bottom, 0px));
    border-inline: 0;
    border-bottom: 0;
    border-radius: 20px 20px 0 0;
    max-height: 85vh;
  }

  .svd__gate-actions {
    flex-direction: column-reverse;
  }

  .svd__gate-back,
  .svd__gate-agree {
    width: 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .svd__gate-back,
  .svd__gate-agree {
    transition: none;
  }
}
