/*
 * The named motion set. NINE keyframes as of 2026-08-28, and no tenth.
 * It was ten until mark-circle-in was retired with the nav mark it handed
 * off to; see design.md, Waveform Hero. No keyframe may be added here.
 * An animation runs only while the state it represents is currently true.
 * No two ambient loops share a period: caret 1100, ping 1400, pulse 1800,
 * waveform bars each carry a unique per-bar duration between 2200 and 3600ms
 * (ruling Q), a range that clears every named period in the system.
 * Ruling V: every one of these loops belongs to ELIA Voice and nowhere else.
 */

/* A call has landed. The ring expands out of the live dot and dies. */
@keyframes call-ring-ping {
  0%        { transform: scale(1);   opacity: 0.55; }
  70%, 100% { transform: scale(2.4); opacity: 0; }
}

/* An agent is working. The dot breathes and glows.
   This box-shadow is signal, not elevation. The only shadow in the system. */
@keyframes agent-dot-pulse {
  0%, 100% { opacity: 1;    box-shadow: 0 0 0 0   rgb(255 90 54 / 0.45); }
  50%      { opacity: 0.62; box-shadow: 0 0 0 5px rgb(255 90 54 / 0); }
}

/* A record has been written into the CRM or ERP. Runs once, overflow hidden row. */
@keyframes record-write-sweep {
  from { transform: translateX(-100%); }
  to   { transform: translateX(200%); }
}

/* A status value changed. Short settle, no bounce, no colour flash. */
@keyframes status-pill-commit {
  0%   { transform: translateY(0)    scale(1); }
  40%  { transform: translateY(-1px) scale(1.03); }
  100% { transform: translateY(0)    scale(1); }
}

/* The transcript line is still streaming. A hard blink, not a fade. */
@keyframes transcript-caret-blink {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}

/* A line of agent output arrived. */
@keyframes agent-line-reveal {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* The agent is listening. Per-bar duration and delay assigned in JS. */
@keyframes waveform-bars-listen {
  0%, 100% { transform: scaleY(0.82); }
  50%      { transform: scaleY(var(--bar-peak, 1)); }
}

/* The mark resolving. Bars converge on centre and fade. */
@keyframes waveform-resolve {
  0%   { transform: scaleY(var(--bar-peak, 1)) translateX(0);                   opacity: 1; }
  60%  { transform: scaleY(0.18)               translateX(var(--bar-converge)); opacity: 1; }
  100% { transform: scaleY(0.18)               translateX(var(--bar-converge)); opacity: 0; }
}

/* The write path being drawn between Elia and the client system. */
@keyframes integration-edge-draw {
  from { stroke-dashoffset: var(--edge-length); }
  to   { stroke-dashoffset: 0; }
}

/* ---- Application ---- */

/* Ember is the LIVE signal, so it is carried by the state, not by the base.
   A dot with no state is not live and must not wear the accent: leaving it
   Ember meant a finished call still showed an ember dot indefinitely. */
.live-dot {
  width: 8px; height: 8px;
  border-radius: var(--radius-dot);
  background: var(--color-ink-mist);
  position: relative;
  flex: none;
}
.live-dot[data-state="connected"],
.live-dot[data-state="working"] { background: var(--color-ember); }
.live-dot[data-state="working"] {
  animation: agent-dot-pulse var(--dur-pulse) var(--ease-emit) infinite;
}
.live-dot[data-state="connected"]::after {
  content: "";
  position: absolute; inset: 0;
  border: 1px solid var(--color-ember);
  border-radius: var(--radius-dot);
  animation: call-ring-ping var(--dur-ping) var(--ease-emit) infinite;
}
/* No data-state, no animation. The default is stillness. */
.live-dot[data-state="complete"] {
  background: var(--color-sand);
}

/* The reveal encodes "a new line has arrived", which can only be true on a
   card whose call is live. Completed cards paint at final state: no animation
   on finished work, ever. */
/* A line reveals wherever a call is LIVE. The rail console runs the same
   component, and its lines were arriving with no reveal at all. */
.card-live .transcript-line.revealed,
.console-transcript .transcript-line.revealed {
  animation: agent-line-reveal var(--dur-slow) var(--ease-signal) both;
}
.transcript-line { opacity: 0; }
.transcript-line.revealed { opacity: 1; }

.transcript-caret {
  display: inline-block;
  width: 1px; height: 14px;
  background: var(--color-ember);
  vertical-align: text-bottom;
  margin-left: 2px;
  animation: transcript-caret-blink var(--dur-caret) linear infinite;
}

.waveform-bar {
  transform-origin: bottom;
  background: var(--color-ember);
  transform: scaleY(0.82);
}
.waveform.listening .waveform-bar {
  animation: waveform-bars-listen var(--bar-duration) var(--ease-emit) infinite;
  animation-delay: var(--bar-delay);
}
.waveform.resolved .waveform-bar {
  animation: waveform-resolve var(--dur-resolve) var(--ease-settle) 1 both;
}

/* A caret blinks because a line is streaming AND someone is watching it.
   Reduced motion keeps the caret visible without the blink; so does this. */
.call-card-body:not(.is-watched) .transcript-caret,
.console-transcript:not(.is-watched) .transcript-caret {
  animation: none;
  opacity: 1;
}

.status-pill.committing {
  animation: status-pill-commit var(--dur-base) var(--ease-settle) 1;
}

/* The pill's own dot pulses while the agent works, per the status table.
   The header dot rings while the call is connected: 1400 and 1800 never meet. */
.status-pill[data-state="working"] .pill-dot {
  animation: agent-dot-pulse var(--dur-pulse) var(--ease-emit) infinite;
}

/* Reduced motion: the state stays legible, the motion goes. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .live-dot[data-state] { opacity: 1; box-shadow: 0 0 0 3px rgb(255 90 54 / 0.28); }
  .transcript-caret { opacity: 1; }
  .transcript-line { opacity: 1; }
  .waveform-bar { transform: scaleY(var(--bar-peak, 0.6)); }
}
