/* Generated by scripts/generate_brand.mjs. Edit brand/tokens.json `motion`. */
:root {
  --motion-enter: cubic-bezier(0.16, 1, 0.3, 1);
  --motion-exit: cubic-bezier(0.6, 0, 0.9, 0.35);
  --motion-move: cubic-bezier(0.65, 0, 0.35, 1);
  --motion-land: cubic-bezier(0.55, 0, 0.2, 1.12);
  --motion-press-ms: 100ms;
  --motion-small-ms: 160ms;
  --motion-small-exit-ms: 100ms;
  --motion-dialog-ms: 240ms;
  --motion-dialog-exit-ms: 120ms;
  --motion-layout-ms: 320ms;
  --motion-enter-ms: 240ms;
  --motion-exit-ms: 120ms;
  --motion-stagger-ms: 30ms;
  --motion-land-ms: 420ms;
  --motion-stagger-items: 6;
  --motion-state-cycles: 2;
  --motion-enter-px: 8px;
  --motion-route-ms: 1200ms;
  --motion-lift-ms: 1400ms;
  --motion-charge-ms: 1800ms;
  --motion-start-ms: 1000ms;
  --motion-think-ms: 1200ms;
  --motion-scan-ms: 1800ms;
  --motion-sweep-ms: 2200ms;
  --motion-alarm-ms: 1200ms;
  --motion-caution-ms: 2000ms;
  --motion-pulse-ms: 700ms;
  --motion-flow-ms: 2000ms;
  --motion-wire-ms: 900ms;
}

/* State glyphs: 24 x 24 strokes in currentColor. The caller sets the colour. */
.state-glyph { width: 1.25em; height: 1.25em; flex: none; overflow: visible; vertical-align: -.25em; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.state-glyph path { transform-box: fill-box; transform-origin: center; }
/* Running states play their loop --motion-state-cycles times when the state starts (a mount or a glyph swap), then
   hold the base style: a static pictogram that keeps the meaning. Only stop and think loop while they last. */
/* Route: chevrons flow toward the destination, then hold as full chevrons. */
.state-glyph[data-state=route] path { animation: state-flow var(--motion-route-ms) linear var(--motion-state-cycles); }
.state-glyph[data-state=route] path:nth-child(2) { animation-delay: calc(var(--motion-route-ms) / 6); }
.state-glyph[data-state=route] path:nth-child(3) { animation-delay: calc(var(--motion-route-ms) / 3); }
/* Load and unload: the load rises or lowers on the fork, then rests on it. */
.state-glyph[data-state=load] path:nth-child(2) { animation: state-raise var(--motion-lift-ms) var(--motion-move) var(--motion-state-cycles); }
.state-glyph[data-state=unload] path:nth-child(2) { animation: state-lower var(--motion-lift-ms) var(--motion-move) var(--motion-state-cycles); }
/* Charge: a light wash fills the cell from the left behind the bolt and fades, then the cell rests empty: the bolt
   carries the meaning and no cell suggests a level beside the reported percent. Caution: a static low cell; the
   battery colour and banner carry the warning. */
.state-glyph:is([data-state=charge], [data-state=caution]) path:nth-child(3) { fill: currentColor; stroke: none; transform-origin: left center; }
.state-glyph[data-state=charge] path:nth-child(3) { opacity: 0; animation: state-fill var(--motion-charge-ms) var(--motion-move) var(--motion-state-cycles); }
.state-glyph[data-state=charge] path:nth-child(4) { stroke-width: 1.5; }
/* Start: one arc turns while the robot prepares, then holds still. */
.state-glyph[data-state=start] path { transform-box: view-box; transform-origin: 12px 12px; animation: state-turn var(--motion-start-ms) linear var(--motion-state-cycles); }
/* Done: the check draws once and lands. The drawn check is the base state, so it stays visible when animation is off. */
.state-glyph[data-state=done] path { stroke-dasharray: 24; animation: state-draw var(--motion-land-ms) var(--motion-land) backwards; }
/* Stop: hard blink, the same period as the full-screen alarm. */
.state-glyph[data-state=stop] { animation: state-alarm var(--motion-alarm-ms) steps(1, end) infinite; }
/* Think: three dots hop in turn. */
.state-glyph[data-state=think] path { stroke-width: 3.4; animation: state-hop var(--motion-think-ms) var(--motion-move) infinite; }
.state-glyph[data-state=think] path:nth-child(2) { animation-delay: calc(var(--motion-think-ms) / 6); }
.state-glyph[data-state=think] path:nth-child(3) { animation-delay: calc(var(--motion-think-ms) / 3); }

/* The running item: a soft sheen travels across it when the item starts, then rests off the item. */
.state-sweep { position: relative; }
.state-sweep::after { content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background: linear-gradient(100deg, transparent 35%, color-mix(in srgb, var(--state-sweep-color, currentColor) 16%, transparent) 50%, transparent 65%) -100% 0 / 250% 100% no-repeat;
  animation: state-sweep var(--motion-sweep-ms) linear var(--motion-state-cycles); }

/* Emergency: the whole screen blinks in the alert role on the top layer (theme.css). The caller places the message. */
.state-alarm-screen { position: fixed; inset: 0; z-index: var(--app-z-alarm); pointer-events: none; display: grid; place-items: start center; padding: 16px; }
.state-alarm-screen::before { content: ""; position: absolute; inset: 0; z-index: -1; border: 10px solid var(--state-alarm-color, var(--app-alert, var(--color-alert)));
  background: color-mix(in srgb, var(--state-alarm-color, var(--app-alert, var(--color-alert))) 24%, transparent); box-shadow: inset 0 0 72px color-mix(in srgb, var(--state-alarm-color, var(--app-alert, var(--color-alert))) 48%, transparent);
  animation: state-alarm var(--motion-alarm-ms) steps(1, end) infinite; }

/* Answers: each new word rises in once, then holds still while it is read. Words after the first 6 rise together. */
.state-words > span { display: inline-block; animation: state-rise var(--motion-small-ms) var(--motion-enter) both; animation-delay: calc(min(var(--i, 0), 6) * var(--motion-stagger-ms)); }
/* Thinking: placeholder lines shimmer until the answer arrives. */
.state-think-lines { display: grid; gap: 8px; }
.state-think-lines > i { display: block; height: 10px; border-radius: 5px; animation: state-shimmer var(--motion-think-ms) linear infinite;
  background: linear-gradient(90deg, color-mix(in srgb, currentColor 10%, transparent) 30%, color-mix(in srgb, currentColor 24%, transparent) 50%, color-mix(in srgb, currentColor 10%, transparent) 70%) 0 0 / 300% 100%; }
.state-think-lines > i:nth-child(2) { width: 84%; animation-delay: calc(var(--motion-think-ms) / 8); }
.state-think-lines > i:nth-child(3) { width: 58%; animation-delay: calc(var(--motion-think-ms) / 4); }
.state-enter { animation: state-rise var(--motion-enter-ms) var(--motion-enter) both; }

@keyframes state-flow { 0%, 100% { opacity: .25; } 30% { opacity: 1; } }
@keyframes state-raise { 0% { transform: translateY(0); opacity: 0; } 20%, 75% { opacity: 1; } 100% { transform: translateY(-6px); opacity: 0; } }
@keyframes state-lower { 0% { transform: translateY(-6px); opacity: 0; } 20%, 75% { opacity: 1; } 100% { transform: translateY(0); opacity: 0; } }
@keyframes state-fill { 0% { transform: scaleX(.12); opacity: 0; } 15% { opacity: .3; } 80% { transform: scaleX(1); opacity: .3; } 100% { transform: scaleX(1); opacity: 0; } }
@keyframes state-turn { to { transform: rotate(360deg); } }
@keyframes state-draw { from { stroke-dashoffset: 24; } to { stroke-dashoffset: 0; } }
@keyframes state-alarm { 50% { opacity: .28; } }
@keyframes state-hop { 0%, 60%, 100% { transform: translateY(0); opacity: .45; } 30% { transform: translateY(-3px); opacity: 1; } }
@keyframes state-sweep { from { background-position: 150% 0; } to { background-position: -100% 0; } }
@keyframes state-rise { from { opacity: 0; transform: translateY(.35em); } }
@keyframes state-shimmer { from { background-position: 100% 0; } to { background-position: -200% 0; } }

@media (prefers-reduced-motion: reduce) {
  :root { --motion-enter-px: 0px; }
  /* Gentler, not none: each animation plays once at press speed, and transitions keep only opacity and colour
     (no transform). The rules below still remove loops, glyph motion and the alarm blink. */
  *, *::before, *::after { animation-iteration-count: 1 !important; animation-duration: var(--motion-press-ms) !important;
    transition-property: opacity, color, background-color, border-color, outline-color, fill, stroke !important;
    transition-duration: var(--motion-press-ms) !important; scroll-behavior: auto !important; }
  .state-glyph, .state-glyph *, .state-alarm-screen::before, .state-words > span, .state-think-lines > i, .state-enter { animation: none !important; }
  .state-sweep::after { display: none; }
}
