/* ============================================================================
 * Sundae — waveform hero (B5).
 *
 * Owns ONLY the .wf-* surface. Every colour, shadow, radius and font here is a
 * token already defined by style.css (:root), which is itself matched to
 * webapp/fleet-dashboard.html. Nothing new is invented, no external asset is
 * loaded, LIGHT ONLY — the fleet system has no dark mode.
 *
 * The strip sits between the header and the stage: the first thing you see is
 * Sundae working. Its height is fluid (clamp) rather than stepped, so it is
 * one continuous object from a 390 px iPhone to a 1120 px desk without any
 * breakpoint gymnastics.
 * ==========================================================================*/

.wf-hero {
  position: relative;
  width: 100%;
  height: clamp(122px, 17vw, 178px);
  border-radius: 20px;
  overflow: hidden;
  background:
    radial-gradient(120% 150% at 50% 120%, rgba(17, 155, 198, .10), transparent 70%),
    linear-gradient(180deg, #ffffff 0%, #f7fbfd 100%);
  border: 1px solid var(--line-light);
  box-shadow: var(--shadow-sm);
  isolation: isolate;
  transition: box-shadow .5s ease, border-color .5s ease, background .6s ease;
  -webkit-tap-highlight-color: transparent;
}

/* Fine horizontal rules behind the ribbon: a barely-there studio grid. It
   gives the ribbon something to move against, which is what makes small
   amplitudes legible at all. 3 % alpha — present, never noticed. */
.wf-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    repeating-linear-gradient(
      to bottom,
      rgba(14, 138, 177, .05) 0 1px,
      transparent 1px 14px);
  mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
  z-index: 0;
}

.wf-canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  z-index: 1;
}

/* State moods. The ribbon itself changes colour in canvas; the CARD reacts
   too, one step behind and much more quietly, so the whole strip feels lit
   from within rather than repainted. */
.wf-hero[data-wf-state="listening"] {
  border-color: #cbe6f0;
  box-shadow: var(--shadow-sm), 0 0 0 3px rgba(14, 138, 177, .07);
}
.wf-hero[data-wf-state="speaking"] {
  border-color: #f0dcb8;
  box-shadow: var(--shadow-md), 0 0 0 3px rgba(244, 177, 94, .12);
}
.wf-hero[data-wf-state="protocol"] {
  border-color: var(--gold);
  background:
    radial-gradient(120% 150% at 50% 120%, rgba(244, 177, 94, .20), transparent 70%),
    linear-gradient(180deg, #fffdf8 0%, var(--gold-light) 100%);
  box-shadow: var(--shadow-md), 0 0 0 4px rgba(244, 177, 94, .22);
}

/* ---------------------------------------------------------- SLEEPING (B3)
 * The ribbon does not stop when Sundae sleeps — a stopped ribbon is
 * indistinguishable from a crashed page, which is the single worst thing
 * this strip could say. It goes to a near-flat breathing line instead (see
 * shapeSleeping in waveform.js: ~7 breaths a minute, ~4 px of travel).
 *
 * The CARD carries the rest: the lit white studio backdrop cools to dusk
 * and the glow drops out, so the strip reads as a room with the lights
 * turned down rather than a widget that failed. Deliberately still LIGHT —
 * the fleet system has no dark mode and a black strip in an otherwise white
 * page reads as an error state, not as night. */
.wf-hero[data-wf-state="sleeping"] {
  border-color: #d4dfe8;
  background:
    radial-gradient(120% 150% at 50% 120%, rgba(93, 111, 136, .10), transparent 70%),
    linear-gradient(180deg, #f4f7fa 0%, #e9eff5 100%);
  box-shadow: inset 0 1px 3px rgba(13, 59, 82, .06);
}
/* The studio grid all but disappears: less to see, because there is less
   happening. It is the cheapest possible way to say "quiet". */
.wf-hero[data-wf-state="sleeping"]::before { opacity: .35; }
.wf-hero[data-wf-state="sleeping"] .wf-state { color: #5d6f88; }

/* ------------------------------------------------------------ WAKING (B3)
 * ~1.2 s, once, on every sleeping -> awake transition. Gold floods in from
 * the bottom (waveform.js paints the matching swell in the ribbon), the
 * border warms, and it settles back to whatever state she woke into. This
 * is the strip's only one-shot animation, and it exists because waking has
 * to feel like waking rather than like a value changing. */
.wf-hero[data-wf-state="waking"] {
  border-color: #f0dcb8;
  background:
    radial-gradient(120% 150% at 50% 125%, rgba(244, 177, 94, .30), transparent 68%),
    linear-gradient(180deg, #ffffff 0%, #fffaf1 100%);
  box-shadow: var(--shadow-md), 0 0 0 4px rgba(244, 177, 94, .16);
}
.wf-hero[data-wf-state="waking"] .wf-state { color: #8a5c14; }

/* ------------------------------------------------------------- overlay */
.wf-overlay {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 13px 16px;
  pointer-events: none;
}

.wf-lede { display: flex; flex-direction: column; gap: 3px; min-width: 0; }

.wf-eyebrow {
  font-family: var(--font-ui);
  font-size: .58rem;
  font-weight: 800;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--faint);
}

.wf-state {
  font-family: var(--font-display);
  font-size: clamp(1.02rem, 3.4vw, 1.32rem);
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--ink);
  line-height: 1.12;
  transition: color .45s ease;
}
.wf-hero[data-wf-state="offline"] .wf-state { color: var(--faint); }
.wf-hero[data-wf-state="speaking"] .wf-state { color: #8a5c14; }
.wf-hero[data-wf-state="protocol"] .wf-state { color: #7a4f0c; }

/* A live dot next to the eyebrow while a session is up. */
.wf-eyebrow::before {
  content: "";
  display: inline-block;
  width: 6px; height: 6px;
  margin-right: 6px;
  border-radius: 50%;
  background: var(--faint);
  vertical-align: middle;
  transition: background .3s ease, box-shadow .3s ease;
}
.wf-hero:not([data-wf-state="offline"]) .wf-eyebrow::before {
  background: var(--ok);
  box-shadow: 0 0 0 3px rgba(31, 138, 77, .16);
}
.wf-hero[data-wf-state="protocol"] .wf-eyebrow::before {
  background: var(--err);
  box-shadow: 0 0 0 3px rgba(182, 47, 47, .18);
}
/* Asleep the session is still UP, so the dot must not go grey (grey means
   disconnected on this page and always has). It stays live and pulses
   slowly instead — the visual equivalent of "she's here, she's just out". */
.wf-hero[data-wf-state="sleeping"] .wf-eyebrow::before {
  background: #7f92aa;
  box-shadow: 0 0 0 3px rgba(127, 146, 170, .18);
  animation: wfDozeDot 4.6s ease-in-out infinite;
}
@keyframes wfDozeDot { 0%, 100% { opacity: .45; } 50% { opacity: 1; } }
.wf-hero[data-wf-state="waking"] .wf-eyebrow::before {
  background: var(--gold);
  box-shadow: 0 0 0 4px rgba(244, 177, 94, .28);
}

/* Clinical badge — the only element that appears/disappears. */
.wf-badge {
  align-self: flex-start;
  flex: none;
  font-family: var(--font-ui);
  font-size: .62rem;
  font-weight: 800;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: #fff;
  background: var(--gold);
  border: 1px solid #e19f45;
  border-radius: 999px;
  padding: 5px 11px;
  box-shadow: var(--shadow-sm);
  animation: wfBadgeIn .35s cubic-bezier(.22, .61, .36, 1);
  white-space: nowrap;
}
.wf-badge[hidden] { display: none; }
.wf-badge[data-sev="high"] { background: var(--err); border-color: #9c2828; }
.wf-badge[data-sev="medium"] { background: var(--warn); border-color: #8a590a; }
@keyframes wfBadgeIn {
  from { opacity: 0; transform: translateY(-6px) scale(.94); }
  to   { opacity: 1; transform: none; }
}

/* Mode note (story time / meditation / …) sits bottom-left, out of the way. */
.wf-foot {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 11px;
  z-index: 2;
  font-family: var(--font-ui);
  font-size: .66rem;
  font-weight: 700;
  color: var(--faint);
  pointer-events: none;
  /* The foot also carries the "how to wake her" hint while she sleeps, which
     is a whole sentence rather than a two-word mode name — bound it so it can
     never push the ribbon or wrap onto the overlay. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.wf-hero[data-wf-state="sleeping"] .wf-foot { color: #5d6f88; }

/* ------------------------------------------------- reduced-motion fallback
   Not "the same animation, slower" — a genuinely different, calm object: one
   bar that breathes on a 7 s cycle. waveform.js hides the canvas and stops
   the rAF loop entirely in this mode, so there is no 60 fps work at all. */
.wf-fallback {
  position: absolute;
  left: 8%; right: 8%;
  top: 50%;
  height: 10px;
  margin-top: -5px;
  border-radius: 999px;
  background: var(--grad);
  opacity: .38;
  transform: scaleX(.5);
  transform-origin: center;
  display: none;
  z-index: 1;
}
.wf-hero[data-wf-reduced="1"] .wf-fallback { display: block; }
.wf-hero[data-wf-reduced="1"][data-wf-state="protocol"] .wf-fallback {
  background: linear-gradient(135deg, #c98a2a, var(--gold));
  opacity: .6;
}
/* Reduced motion + sleeping: the bar thins to a line and stops moving. The
   state is still carried, by form and colour instead of by motion. */
.wf-hero[data-wf-reduced="1"][data-wf-state="sleeping"] .wf-fallback {
  background: #94a7b8;
  height: 4px; margin-top: -2px;
  opacity: .5;
  transform: scaleX(.94);
  animation: none;
}

@media (prefers-reduced-motion: no-preference) {
  .wf-fallback { animation: wfBreathe 7s ease-in-out infinite; }
}
@keyframes wfBreathe {
  0%, 100% { transform: scaleX(.5); opacity: .3; }
  50%      { transform: scaleX(.92); opacity: .5; }
}

/* ---------------------------------------------------------------- iPhone */
@media (max-width: 560px) {
  .wf-hero { height: clamp(104px, 26vw, 132px); border-radius: 16px; }
  .wf-overlay { padding: 11px 13px; }
  .wf-foot { left: 13px; right: 13px; bottom: 9px; font-size: .62rem; }
  .wf-badge { font-size: .56rem; padding: 4px 9px; }
}

/* Very short landscape phones: the hero must never eat the fold. */
@media (max-height: 460px) and (orientation: landscape) {
  .wf-hero { height: 86px; }
}
