/*
 * The docs site's figures (docs-figures.js): a scene player, a small shared
 * vocabulary, then one section per scene.
 *
 * How the motion works, because every rule below leans on it. The player
 * writes one thing per beat onto a scene's root - `data-beat="N"` and the
 * cumulative classes `beat-ge-1` ... `beat-ge-N` - and this file is the
 * whole of what that means: each element has a resting state, a rule per
 * beat that moves it, and one shared `transition` that gets it there.
 * There is no timeline anywhere. A state can be jumped to, replayed,
 * screenshotted and rendered with motion off; a timeline can only be
 * played from the top (docs-figures.js's header has the longer argument).
 * The few `@keyframes` here decorate a state that is already complete
 * without them - a caret blinks, a scan band passes - and carry no
 * information.
 *
 * `transform` and `opacity` only, so the compositor does the work and a
 * figure never relayouts the page under a reader's thumb. That rule is
 * why a highlight is a ring that fades in (`::after`) rather than a
 * `border-color` that changes, and why a bar grows by `scaleX` rather than
 * by `width`.
 *
 * Travel: something that goes from one place to another is laid out where
 * it ENDS, and the distances back to its earlier stops arrive from
 * docs-figures.js `measure()` as `--to-<stop>-x` / `--to-<stop>-y`. Every
 * use has a `0px` fallback, so a scene that was never measured shows its
 * travellers at rest in their final places - a complete picture, a beat
 * early, rather than a broken one.
 *
 * Every transition sits inside `prefers-reduced-motion: no-preference`.
 * With motion off the player shows the last beat at once (`scene-still`)
 * and the captions become a numbered list; nothing in this file puts
 * information inside a motion rule.
 *
 * Restacking is by the figure's own width (`@container docsfig`), not the
 * window's. The slot a figure sits in is `min(880px, 100vw - 340px)` above
 * the site's 860px breakpoint (docs.css) - so at a 900px window it is
 * 560px wide while the window still says "desktop", and at an 850px
 * window it is over 800px wide while the window says "phone". The figure
 * is what has to fit, so the figure is what is asked. The one
 * `@media (max-width: 860px)` rule below is the one thing that is about
 * the window: the figure's own padding, which a container cannot query on
 * itself.
 *
 * Light palette only, `styles.css`'s tokens and nothing invented: the
 * agent is `--text` on white, an invariant wears `--accent`, a person's
 * move is `--attention`, a measurement is `--series-1`. Class names are
 * `scene-` (the player), `fig-` (shared parts) or the scene's own short
 * name, so nothing collides with docs.css's `docs-*`.
 */

/* --- shared ----------------------------------------------------------- */

.docsfig {
  container: docsfig / inline-size;
  box-sizing: border-box;
  padding: 18px 18px 12px;
  font-family: var(--font-ui);
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--text);
  text-align: left;

  --ease: cubic-bezier(0.2, 0.8, 0.2, 1);
  --beat: 600ms;
  --stagger: 70ms;
  --fig-shadow: 0 1px 2px color-mix(in srgb, var(--text) 5%, transparent),
    0 3px 10px color-mix(in srgb, var(--text) 4%, transparent);
  --fig-lift: 0 2px 4px color-mix(in srgb, var(--text) 7%, transparent),
    0 10px 24px color-mix(in srgb, var(--text) 9%, transparent);
}

@media (max-width: 860px) {
  .docsfig {
    padding: 12px 10px 8px;
  }
}

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

/* Resets at no specificity (`:where`), so a one-class rule further down -
   `.setup-cmd { padding }` on a `<p>` - wins without having to shout. */
:where(.docsfig) :where(p, ol, ul) {
  margin: 0;
  padding: 0;
  list-style: none;
}

:where(.docsfig) :where(code) {
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: inherit;
}

/* Off the visible page, on the accessibility tree: the line that speaks a
   beat's caption as it changes. */
.docsfig-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;
}

.fig-glyph {
  display: block;
  flex: none;
  width: 1em;
  height: 1em;
}

.fig-glyph-line {
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.fig-glyph-fill {
  fill: currentColor;
}

/* The agent: a dark tile with a spark, and the word. The same in every
   scene, so the tile alone (`.fig-agent-mark`) can stand for "the agent's
   hand is on this" wherever it lands. */
.fig-agent {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px 3px 3px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--bg-raised);
  box-shadow: var(--fig-shadow);
  font-size: 12px;
  font-weight: 650;
  line-height: 1;
  white-space: nowrap;
}

.fig-agent-mark {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--text);
  color: var(--bg-raised);
  font-size: 12px;
}

/* A person: the same chip, in the one colour that means "somebody's move". */
.fig-person {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px 3px 3px;
  border: 1px solid color-mix(in srgb, var(--attention) 45%, var(--bg-raised));
  border-radius: 999px;
  background: var(--bg-raised);
  box-shadow: var(--fig-shadow);
  font-size: 12px;
  font-weight: 650;
  line-height: 1;
  white-space: nowrap;
}

.fig-person-mark {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--attention);
  color: var(--on-attention);
  font-size: 13px;
}

/* What a person presses. `-do` and `-done` share one grid cell so the
   button is as wide as the longer word from the start and turning over
   never moves its neighbours; the pale `::before` is what "done" looks
   like, faded in over the orange rather than painted by a colour change. */
.fig-btn {
  position: relative;
  display: inline-grid;
  place-items: center;
  padding: 5px 12px;
  border-radius: 6px;
  background: var(--attention);
  color: var(--on-attention);
  font-size: 11.5px;
  font-weight: 700;
  line-height: 1.1;
  white-space: nowrap;
}

.fig-btn::before {
  content: "";
  position: absolute;
  inset: 0;
  border: 1px solid var(--ok-border);
  border-radius: inherit;
  background: var(--bg-raised);
  opacity: 0;
}

.fig-btn-do,
.fig-btn-done {
  position: relative;
  grid-area: 1 / 1;
}

.fig-btn-done {
  color: var(--ok-text);
  opacity: 0;
}

/* An invariant: a file card. Monospace key, version in the accent, and the
   accent edge that says "invariant" before the words are read. */
.fig-inv {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  gap: 6px;
  padding: 4px 7px 4px 6px;
  border: 1px solid var(--border-strong);
  border-left: 3px solid var(--accent);
  border-radius: 6px;
  background: var(--bg-raised);
  box-shadow: var(--fig-shadow);
  vertical-align: middle;
}

.docsfig .fig-inv-key {
  display: inline-flex;
  font-size: 11px;
  font-weight: 600;
  line-height: 16px;
  white-space: nowrap;
}

.fig-inv-ver {
  display: inline-grid;
  color: var(--accent);
  font-weight: 700;
}

.fig-inv-ver > * {
  grid-area: 1 / 1;
}

.fig-inv-ver-next {
  opacity: 0;
  transform: translateY(70%);
}

.fig-inv-rows {
  display: grid;
  gap: 4px;
  padding-top: 6px;
  border-top: 1px solid var(--border);
}

.fig-inv-row {
  display: grid;
  grid-template-columns: 46px minmax(0, 1fr);
  align-items: baseline;
  gap: 6px;
}

.fig-inv-kind {
  font-size: 11px;
  font-weight: 650;
  color: var(--accent);
}

.fig-inv-vals {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 8px;
}

.docsfig .fig-inv-val {
  font-size: 11px;
  color: var(--text-dim);
  white-space: nowrap;
}

/* Room held open for a traveller: an unseen copy of it (docs-figures.js
   `slotFor`), so the room is always exactly its size. */
.fig-slot {
  position: relative;
  display: inline-flex;
  flex: none;
  vertical-align: middle;
}

.fig-ghost {
  visibility: hidden;
}

.fig-slot-shown::before {
  content: "";
  position: absolute;
  inset: 0;
  border: 1px dashed var(--border-strong);
  border-radius: 6px;
}

.fig-tick {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 10px;
}

.fig-panel {
  position: relative;
  min-width: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-raised);
  box-shadow: var(--fig-shadow);
}

.fig-panel-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 32px;
  padding: 6px 10px;
  border-bottom: 1px solid var(--border);
  font-size: 11.5px;
  font-weight: 650;
  color: var(--text-dim);
}

.fig-panel-title {
  margin-right: auto;
}

.fig-bolt {
  color: var(--accent);
  font-size: 13px;
}

/* --- the player -------------------------------------------------------- */

.scene {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.scene-stage {
  position: relative;
}

/* Every caption in one cell, one visible: the row is as tall as the
   longest sentence from the start, so nothing under it moves. */
.scene-captions {
  display: grid;
  margin-top: 8px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}

.scene-caption {
  grid-area: 1 / 1;
  display: flex;
  align-items: baseline;
  gap: 10px;
  max-width: 88ch;
  font-size: 14px;
  line-height: 1.45;
  opacity: 0;
  transform: translateY(4px);
  pointer-events: none;
  user-select: none;
}

.scene-caption.is-current {
  opacity: 1;
  transform: none;
  pointer-events: auto;
  user-select: auto;
}

.scene-caption-num {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 20px;
  height: 20px;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  color: var(--accent);
  transform: translateY(-1px);
}

/* Motion off: the whole script, as the list it always was. */
.scene-still .scene-captions {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.scene-still .scene-caption {
  opacity: 1;
  transform: none;
  pointer-events: auto;
  user-select: auto;
}

.scene-still .scene-controls {
  display: none;
}

.scene-controls {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 28px;
}

.scene-dots {
  display: flex;
  gap: 4px;
}

/* A dot is a short bar with a tall invisible button round it: the bar is
   quiet, the target is a fingertip. */
.docsfig .scene-dot {
  width: 26px;
  padding: 10px 0;
  border: 0;
  border-radius: 4px;
  background: none;
  cursor: pointer;
}

.scene-dot-track {
  display: block;
  height: 3px;
  border-radius: 2px;
  background: var(--border);
  overflow: hidden;
}

.scene-dot:hover .scene-dot-track {
  background: var(--border-strong);
}

.scene-dot-fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
}

.scene-dot.is-past .scene-dot-fill {
  transform: none;
  opacity: 0.45;
}

.scene-dot[aria-current] .scene-dot-fill {
  transform: none;
}

.scene-dot:focus-visible,
.scene-replay:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.scene-count {
  margin-left: auto;
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  color: var(--text-faint);
}

.docsfig .scene-replay {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 8px;
  border: 0;
  border-radius: 6px;
  background: none;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-dim);
  cursor: pointer;
}

.scene-replay:hover {
  background: var(--bg-hover);
  color: var(--text);
}

.scene-replay .fig-glyph {
  font-size: 13px;
}

@media (prefers-reduced-motion: no-preference) {
  /* The one transition every moving thing on a stage shares. `--i` is a
     sibling's place in line (docs-figures.js `stagger`). */
  .scene-stage *,
  .scene-stage *::before,
  .scene-stage *::after {
    transition-property: transform, opacity;
    transition-duration: var(--beat), 420ms;
    transition-timing-function: var(--ease), ease-out;
    transition-delay: calc(var(--i, 0) * var(--stagger));
  }

  .scene-stage {
    transition: opacity 220ms ease-out;
  }

  .scene-rewinding .scene-stage {
    opacity: 0;
  }

  /* The old sentence is gone before the new one arrives: two lines of
     text cross-fading through each other is a smear, not a transition. */
  .scene-caption {
    transition: opacity 140ms ease-out, transform 0s linear 140ms;
  }

  .scene-caption.is-current {
    transition: opacity 320ms ease-out 140ms, transform 420ms var(--ease) 140ms;
  }

  .scene-dot-fill {
    transition: transform 240ms ease-out, opacity 240ms ease-out;
  }

  /* While it plays itself the current dot fills over the beat's dwell -
     how long until the next sentence, without a number. */
  .scene-playing .scene-dot[aria-current] .scene-dot-fill {
    transition: transform var(--dwell, 2600ms) linear;
  }

  /* State set with no motion: a measurement landing, a rewind. */
  .scene-snap,
  .scene-snap *,
  .scene-snap *::before,
  .scene-snap *::after {
    transition: none !important;
    animation: none !important;
  }
}

/* --- loop --------------------------------------------------------------
   Six stations in a ring - across the top, back along the bottom - and
   one card that goes all the way round. A station is faint until its beat
   has come, ringed while it is the beat, and plain after. */

.loop-stage {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 30px;
}

.loop-s1 { grid-area: 1 / 1; }
.loop-s2 { grid-area: 1 / 2; }
.loop-s3 { grid-area: 1 / 3; }
.loop-s4 { grid-area: 2 / 3; }
.loop-s5 { grid-area: 2 / 2; }
.loop-s6 { grid-area: 2 / 1; }

.loop-station {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 118px;
  padding: 10px 12px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-raised);
  opacity: 0.5;
}

.loop-station::after {
  content: "";
  position: absolute;
  inset: -1px;
  border: 1px solid var(--border-strong);
  border-radius: inherit;
  box-shadow: var(--fig-lift);
  opacity: 0;
  pointer-events: none;
}

.beat-ge-1 .loop-s1,
.beat-ge-2 .loop-s2,
.beat-ge-3 .loop-s3,
.beat-ge-4 .loop-s4,
.beat-ge-5 .loop-s5,
.beat-ge-6 .loop-s6 {
  opacity: 1;
}

[data-beat="1"] .loop-s1::after,
[data-beat="2"] .loop-s2::after,
[data-beat="3"] .loop-s3::after,
[data-beat="4"] .loop-s4::after,
[data-beat="5"] .loop-s5::after,
[data-beat="6"] .loop-s6::after {
  opacity: 1;
}

.loop-head {
  display: flex;
  align-items: center;
  gap: 7px;
}

.loop-num {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 18px;
  height: 18px;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  color: var(--text-dim);
}

.loop-title {
  font-size: 12px;
  font-weight: 650;
  color: var(--text-dim);
}

.loop-body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  min-width: 0;
}

.loop-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
  max-width: 100%;
}

.loop-row-end {
  align-items: flex-end;
}

/* The way round: an arrow in the gap after each station, grey until the
   story has crossed it. Two copies stacked, the lit one faded in - a
   colour that changes is not a transform. */
.loop-link {
  position: absolute;
  display: grid;
  width: 18px;
  height: 18px;
  font-size: 18px;
}

.loop-link > * {
  grid-area: 1 / 1;
}

.loop-link-rest {
  color: var(--border-strong);
}

.loop-link-lit {
  color: var(--accent);
  opacity: 0;
}

.loop-s1 .loop-link,
.loop-s2 .loop-link {
  right: -25px;
  top: calc(50% - 9px);
}

.loop-s3 .loop-link {
  bottom: -25px;
  left: calc(50% - 9px);
  transform: rotate(90deg);
}

.loop-s4 .loop-link,
.loop-s5 .loop-link {
  left: -25px;
  top: calc(50% - 9px);
  transform: rotate(180deg);
}

.loop-s6 .loop-link {
  top: -25px;
  left: calc(50% - 9px);
  transform: rotate(-90deg);
}

.beat-ge-2 .loop-s1 .loop-link-lit,
.beat-ge-3 .loop-s2 .loop-link-lit,
.beat-ge-4 .loop-s3 .loop-link-lit,
.beat-ge-5 .loop-s4 .loop-link-lit,
.beat-ge-6 .loop-s5 .loop-link-lit,
.beat-ge-6 .loop-s6 .loop-link-lit {
  opacity: 1;
}

/* The two that travel. Taken out of the flow and parked at the stage's
   corner; every place they are ever seen is a measured distance from it. */
.loop-card:not(.fig-ghost),
.loop-agent:not(.fig-ghost) {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 3;
}

.loop-agent:not(.fig-ghost) {
  transform: translate(var(--to-s2-x, 0px), var(--to-s2-y, 0px));
}

.beat-ge-3 .loop-agent:not(.fig-ghost) {
  transform: translate(var(--to-s3-x, 0px), var(--to-s3-y, 0px));
}

.loop-card:not(.fig-ghost) {
  opacity: 0;
  transform: translate(var(--to-s2-x, 0px), var(--to-s2-y, 0px)) translateY(10px) scale(0.94);
}

.beat-ge-2 .loop-card:not(.fig-ghost) {
  opacity: 1;
  transform: translate(var(--to-s2-x, 0px), var(--to-s2-y, 0px));
}

.beat-ge-3 .loop-card:not(.fig-ghost) {
  transform: translate(var(--to-s3-x, 0px), var(--to-s3-y, 0px));
}

.beat-ge-4 .loop-card:not(.fig-ghost) {
  transform: translate(var(--to-s4-x, 0px), var(--to-s4-y, 0px));
}

.beat-ge-5 .loop-card:not(.fig-ghost) {
  transform: translate(var(--to-s5-x, 0px), var(--to-s5-y, 0px));
}

.beat-ge-6 .loop-card:not(.fig-ghost) {
  transform: translate(var(--to-s6-x, 0px), var(--to-s6-y, 0px));
}

/* Where the card has been. Each stop keeps a faint print of it once it
   has moved on - "filed under backend/payments@3" has to go on saying
   which invariant after the card has left for the pull request - so the
   last frame is a record of the trip and not five empty rooms. */
.loop-cardslot .fig-ghost {
  visibility: visible;
  box-shadow: none;
  opacity: 0;
}

.beat-ge-4 .loop-s3 .loop-cardslot .fig-ghost,
.beat-ge-5 .loop-s4 .loop-cardslot .fig-ghost,
.beat-ge-6 .loop-s5 .loop-cardslot .fig-ghost {
  opacity: 0.45;
  transition-delay: 300ms;
}

/* `@3` turns over into `@4` once the card has landed under the merge. */
.beat-ge-6 .loop-card:not(.fig-ghost) .fig-inv-ver-now {
  opacity: 0;
  transform: translateY(-70%);
  transition-delay: 750ms;
}

.beat-ge-6 .loop-card:not(.fig-ghost) .fig-inv-ver-next {
  opacity: 1;
  transform: none;
  transition-delay: 750ms;
}

/* 1 */
.loop-task {
  font-size: 13px;
  font-weight: 600;
  line-height: 1.35;
  opacity: 0;
  transform: translateX(-14px);
}

.beat-ge-1 .loop-task {
  opacity: 1;
  transform: none;
}

.loop-next {
  font-size: 12px;
  font-weight: 600;
  color: var(--accent);
  opacity: 0;
  transform: translateY(6px);
}

.docsfig .loop-next-key {
  font-weight: 700;
}

.beat-ge-6 .loop-next {
  opacity: 1;
  transform: none;
  transition-delay: 1300ms;
}

.beat-ge-6 .loop-s6 .loop-link-lit {
  transition-delay: 1000ms;
}

/* 2 - the catalogue, as a pile: the card comes up off the top of it, and
   its place stays outlined, because choosing one does not take it away. */
.docsfig .loop-call {
  font-size: 11px;
  color: var(--text-faint);
  opacity: 0;
}

.beat-ge-2 .loop-call {
  opacity: 1;
}

.loop-stack {
  display: inline-flex;
  flex-direction: column;
  align-items: stretch;
}

.loop-stack .fig-slot::before {
  content: "";
  position: absolute;
  inset: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
}

/* The catalogue keeps its copy, and at the very end its copy is `@4`:
   that is the better setup the next task finds. */
.beat-ge-3 .loop-stack .fig-ghost {
  opacity: 0.45;
}

.beat-ge-6 .loop-stack .fig-ghost .fig-inv-ver-now {
  opacity: 0;
  transform: translateY(-70%);
  transition-delay: 1300ms;
}

.beat-ge-6 .loop-stack .fig-ghost .fig-inv-ver-next {
  opacity: 1;
  transform: none;
  transition-delay: 1300ms;
}

.loop-stack::before,
.loop-stack::after {
  content: "";
  order: 2;
  height: 5px;
  margin: 0 6px;
  border: 1px solid var(--border);
  border-top: 0;
  border-radius: 0 0 5px 5px;
}

.loop-stack::after {
  margin: 0 12px;
}

.loop-pile-label {
  font-size: 11px;
  color: var(--text-faint);
}

/* 3 */
.loop-folders {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
}

.loop-folder {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  opacity: 0;
  transform: translateY(5px);
}

.docsfig .loop-folder code {
  font-size: 11px;
  color: var(--text-dim);
}

.beat-ge-3 .loop-folder {
  opacity: 1;
  transform: none;
}

/* 4 */
.loop-bars {
  opacity: 0;
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 34px;
  padding-bottom: 1px;
  border-bottom: 1px solid var(--border-strong);
}

.loop-bar {
  width: 7px;
  height: calc(var(--h) * 32px);
  border-radius: 2px 2px 0 0;
  background: var(--series-1);
  transform: scaleY(0);
  transform-origin: center bottom;
}

.beat-ge-4 .loop-bars {
  opacity: 1;
}

.beat-ge-4 .loop-bar {
  transform: none;
}

.loop-filed {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
}

.loop-label {
  font-size: 11px;
  color: var(--text-faint);
  opacity: 0;
}

.beat-ge-4 .loop-label {
  opacity: 1;
}

/* 5 */
.loop-pr {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}

.loop-pr-head {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  font-weight: 650;
  color: var(--text-dim);
  opacity: 0;
}

.loop-pr-head .fig-glyph {
  font-size: 13px;
}

.docsfig .loop-diff {
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--diff-add-bg);
  font-size: 11px;
  color: var(--ok-text);
  opacity: 0;
  transform: translateX(-8px);
}

.beat-ge-5 .loop-pr-head,
.beat-ge-5 .loop-diff {
  opacity: 1;
  transform: none;
}

.beat-ge-5 .loop-diff {
  transition-delay: 450ms;
}

/* 6 */
.loop-who {
  opacity: 0;
  transform: translateY(6px);
}

.beat-ge-6 .loop-who {
  opacity: 1;
  transform: none;
}

.beat-ge-6 .loop-merge::before,
.beat-ge-6 .loop-merge .fig-btn-done {
  opacity: 1;
  transition-delay: 600ms;
}

.beat-ge-6 .loop-merge .fig-btn-do {
  opacity: 0;
  transition-delay: 600ms;
}

/* Narrow: the same ring, turned - down the left, back up the right - so
   six is still beside one and the way round still closes. */
@container docsfig (max-width: 600px) {
  .loop-stage {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 26px 16px;
  }

  .loop-s1 { grid-area: 1 / 1; }
  .loop-s2 { grid-area: 2 / 1; }
  .loop-s3 { grid-area: 3 / 1; }
  .loop-s4 { grid-area: 3 / 2; }
  .loop-s5 { grid-area: 2 / 2; }
  .loop-s6 { grid-area: 1 / 2; }

  .loop-station {
    padding: 8px 6px 10px;
  }

  .loop-s1 .loop-link,
  .loop-s2 .loop-link {
    right: auto;
    top: auto;
    bottom: -22px;
    left: calc(50% - 9px);
    transform: rotate(90deg);
  }

  .loop-s3 .loop-link {
    bottom: auto;
    left: auto;
    right: -17px;
    top: calc(50% - 9px);
    transform: none;
  }

  .loop-s4 .loop-link,
  .loop-s5 .loop-link {
    left: calc(50% - 9px);
    top: -22px;
    transform: rotate(-90deg);
  }

  .loop-s6 .loop-link {
    top: calc(50% - 9px);
    left: -17px;
    transform: rotate(180deg);
  }
}

/* --- setup-line ---------------------------------------------------------
   A terminal, the Executors page, and the machine going from one to the
   other. */

.setup-stage {
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) 26px minmax(0, 1fr);
  align-items: stretch;
  gap: 10px;
}

.setup-lights {
  width: 7px;
  height: 7px;
  margin-right: 22px;
  border-radius: 50%;
  background: var(--border-strong);
  box-shadow: 11px 0 0 var(--border-strong), 22px 0 0 var(--border-strong);
}

.setup-term {
  font-family: var(--font-mono);
}

.setup-term .fig-panel-bar {
  font-family: var(--font-ui);
}

.setup-cmd {
  display: flex;
  gap: 7px;
  padding: 12px 12px 0;
  font-size: 11.5px;
  line-height: 18px;
  white-space: nowrap;
}

.setup-prompt {
  color: var(--text-faint);
}

/* Typed by uncovering. The cover is the pane's own white, exactly as wide
   as the command, and slides off it to the right in as many steps as the
   command has characters (set inline, from the string's length); the
   caret rides its leading edge. `1ch` of room past the last character is
   where the caret rests when the line is done. */
.setup-typed {
  position: relative;
  display: inline-block;
  padding-right: 1ch;
  overflow: hidden;
}

.setup-cover {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: calc(100% - 1ch);
  background: var(--bg-raised);
}

.setup-cover::after {
  content: "";
  position: absolute;
  left: 100%;
  top: 0;
  bottom: 0;
  width: 1ch;
  background: var(--bg-raised);
}

.setup-caret {
  position: absolute;
  z-index: 1;
  left: 0;
  top: 3px;
  bottom: 3px;
  width: 0.55ch;
  background: var(--text);
}

.beat-ge-1 .setup-cover {
  transform: translateX(100%);
}

.beat-ge-2 .setup-caret {
  opacity: 0;
}

.setup-found {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 10px 12px 0;
  --stagger: 150ms;
}

.setup-out {
  font-size: 11.5px;
  color: var(--text-faint);
}

.setup-found > * {
  opacity: 0;
  transform: translateY(6px);
}

.beat-ge-2 .setup-found > * {
  opacity: 1;
  transform: none;
}

.setup-harness {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px 2px 3px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  font-family: var(--font-ui);
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
}

.setup-harness .fig-tick {
  transform: scale(0);
}

.beat-ge-2 .setup-harness .fig-tick {
  transform: none;
  transition-delay: calc(var(--i, 0) * var(--stagger) + 220ms);
}

.setup-connected {
  padding: 10px 12px 14px;
  font-size: 11.5px;
  opacity: 0;
}

.docsfig .setup-origin {
  font-weight: 600;
}

.beat-ge-3 .setup-connected {
  opacity: 1;
}

.setup-wire {
  align-self: center;
  display: grid;
  place-items: center;
  font-size: 20px;
  color: var(--border-strong);
}

.setup-wire .fig-glyph {
  opacity: 0.6;
}

.beat-ge-3 .setup-wire .fig-glyph {
  opacity: 1;
}

.setup-rows {
  position: relative;
  min-height: 132px;
  padding: 10px;
}

.setup-empty {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 12px;
  color: var(--text-faintest);
}

.beat-ge-3 .setup-empty {
  opacity: 0;
}

/* The row is laid out where it lands and starts where it was made: on the
   words "Connected as dev-laptop", small, inside the terminal. */
.setup-exec {
  position: relative;
  z-index: 2;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--bg-raised);
  box-shadow: var(--fig-shadow);
  transform-origin: left center;
  opacity: 0;
  transform: translate(var(--to-term-x, 0px), var(--to-term-y, 0px)) scale(0.5);
}

.beat-ge-3 .setup-exec {
  opacity: 1;
  transform: none;
  transition-duration: 750ms, 300ms;
}

.setup-exec-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
  padding: 8px 10px;
}

.setup-live-dot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ok);
}

.docsfig .setup-exec-name {
  font-size: 12px;
  font-weight: 650;
}

.setup-exec-meta {
  margin-left: auto;
  font-size: 11px;
  color: var(--ok-text);
}

.setup-session {
  margin: 0 0 0 14px;
  padding: 9px 0 2px 12px;
  border-left: 1px solid var(--border-strong);
  opacity: 0;
  transform: translateY(-8px);
}

.beat-ge-4 .setup-session {
  opacity: 1;
  transform: none;
}

.setup-session-head {
  display: flex;
  align-items: center;
  gap: 7px;
}

.setup-session-head .fig-agent-mark {
  width: 16px;
  height: 16px;
  font-size: 10px;
}

.setup-session-title {
  min-width: 0;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.3;
}

.setup-session-live {
  flex: none;
  margin-left: auto;
  padding: 1px 6px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--series-1) 45%, var(--bg-raised));
  font-size: 11px;
  font-weight: 700;
  color: var(--series-1);
}

.setup-events {
  display: grid;
  gap: 3px;
  padding: 7px 0 0 23px;
  --stagger: 300ms;
}

.setup-event {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
  overflow-wrap: anywhere;
  opacity: 0;
  transform: translateX(-8px);
}

.beat-ge-4 .setup-event {
  opacity: 1;
  transform: none;
}

@container docsfig (max-width: 600px) {
  .setup-stage {
    grid-template-columns: minmax(0, 1fr);
  }

  .setup-wire {
    justify-self: center;
    transform: rotate(90deg);
  }

  .setup-cmd {
    padding: 12px 10px 0;
    font-size: 11px;
  }

  .setup-found,
  .setup-connected {
    padding-left: 10px;
    padding-right: 10px;
  }
}

/* --- scoping ------------------------------------------------------------
   The agent's reach is its mark on everything it might have to look at. No
   rule in this section draws an edge round the scope, dashes a border,
   strikes a name through or takes a pointer away: what is out of scope
   gets quieter and keeps its place. A scope tells; it does not block. */

.scope-stage {
  display: grid;
  grid-template-columns: minmax(0, 236px) minmax(0, 1fr);
  align-items: start;
  gap: 22px;
}

.scope-side {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 12px;
  min-width: 0;
  padding-top: 4px;
}

.scope-who {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
}

.scope-count {
  font-size: 12px;
  color: var(--text-dim);
  opacity: 0;
}

.beat-ge-1 .scope-count {
  opacity: 1;
}

.scope-count-num {
  display: inline-grid;
  justify-items: center;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

.scope-count-num > * {
  grid-area: 1 / 1;
}

.scope-count-named {
  color: var(--accent);
  opacity: 0;
  transform: translateY(70%);
}

.beat-ge-3 .scope-count-all {
  opacity: 0;
  transform: translateY(-70%);
  transition-delay: 500ms;
}

.beat-ge-3 .scope-count-named {
  opacity: 1;
  transform: none;
  transition-delay: 500ms;
}

.scope-dock {
  display: grid;
  align-self: stretch;
}

.scope-dock > * {
  grid-area: 1 / 1;
}

.scope-dock-empty {
  display: grid;
  place-items: center;
  border: 1px dashed var(--border-strong);
  border-radius: 6px;
  font-size: 11.5px;
  color: var(--text-faintest);
}

.beat-ge-2 .scope-dock-empty {
  opacity: 0;
}

/* Laid out in the dock; before it arrives it is a speck on `invariants/`,
   the folder it is a file in. */
.scope-card {
  z-index: 4;
  justify-self: start;
  align-self: start;
  max-width: 100%;
  opacity: 0;
  transform: translate(var(--to-file-x, 0px), var(--to-file-y, 0px)) scale(0.25);
}

.beat-ge-2 .scope-card {
  opacity: 1;
  transform: none;
  transition-duration: 800ms, 300ms;
}

.scope-repo {
  display: flex;
  flex-direction: column;
}

.scope-group {
  display: grid;
  grid-template-columns: 52px minmax(0, 1fr);
  align-items: start;
  gap: 8px;
  padding: 10px 10px 0;
}

.scope-group-title {
  padding-top: 6px;
  font-size: 11px;
  font-weight: 650;
  color: var(--text-faint);
}

/* Folders sit on a grid, because a repository is a lot of the same kind
   of thing; tools and skills are short rows of chips, as wide as their
   names, because `Read` and `migrations.md` forced to one width is either
   a clipped name or a lot of air. */
.scope-tiles {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.scope-group-folders .scope-tiles {
  display: grid;
}

.scope-group-folders .scope-tiles {
  grid-template-columns: repeat(auto-fill, minmax(152px, 1fr));
}

.scope-tile {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  min-width: 0;
  min-height: 28px;
  padding: 4px 6px 4px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg-raised);
}

.scope-tile.is-named::after {
  content: "";
  position: absolute;
  inset: -1px;
  border: 1.5px solid var(--accent);
  border-radius: inherit;
  opacity: 0;
}

.beat-ge-3 .scope-tile.is-named::after {
  opacity: 1;
  transition-delay: 450ms;
}

.docsfig .scope-tile-name {
  min-width: 0;
  font-size: 11px;
  white-space: nowrap;
}

.beat-ge-3 .scope-tile:not(.is-named) .scope-tile-name {
  opacity: 0.5;
}

/* The last beat's point, made in ink as well as in words: what dimmed
   comes part of the way back. It was never gone. */
.beat-ge-4 .scope-tile:not(.is-named) .scope-tile-name {
  opacity: 0.72;
}

.scope-reach {
  position: relative;
  z-index: 3;
  display: grid;
  place-items: center;
  flex: none;
  width: 14px;
  height: 14px;
  --stagger: 45ms;
  transform: scale(0);
}

.scope-reach > * {
  grid-area: 1 / 1;
}

.scope-reach-mark.fig-agent-mark {
  width: 14px;
  height: 14px;
  font-size: 9px;
}

/* The ring a reaching mark gives off on the first beat (the keyframes are
   at the foot of the file, with the rest of what needs a clock). */
.scope-reach::after {
  content: "";
  grid-area: 1 / 1;
  width: 14px;
  height: 14px;
  border: 1.5px solid var(--text);
  border-radius: 50%;
  opacity: 0;
}

.beat-ge-1 .scope-reach {
  transform: none;
}

/* Settling. A mark that is on something the scope names stays, and its
   tile is ringed; a mark that is not flies to one that is and is absorbed
   - the transform first, the fade only as it lands. */

.beat-ge-3 .scope-tile:not(.is-named) .scope-reach {
  opacity: 0;
  transform: translate(var(--to-scope-x, 0px), var(--to-scope-y, 0px)) scale(0.6);
}

.scope-foot {
  display: flex;
  align-items: center;
  gap: 7px;
  margin-top: auto;
  padding: 10px 10px 12px;
  font-size: 12px;
  color: var(--text-dim);
  opacity: 0;
  transform: translateY(5px);
}

.scope-foot-dot {
  flex: none;
  width: 9px;
  height: 9px;
  border: 1.5px solid var(--text-faint);
  border-radius: 50%;
}

.beat-ge-4 .scope-foot {
  opacity: 1;
  transform: none;
}

@container docsfig (max-width: 600px) {
  .scope-stage {
    grid-template-columns: minmax(0, 1fr);
    gap: 14px;
  }

  .scope-group {
    grid-template-columns: minmax(0, 1fr);
    gap: 5px;
    padding: 8px 8px 0;
  }

  .scope-group-title {
    padding-top: 0;
  }

  .scope-group-folders .scope-tiles {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .scope-tile {
    padding: 4px 5px 4px 7px;
    gap: 4px;
  }
}

/* --- choosing -----------------------------------------------------------
   Two sides and an MCP call between them: the agent's on the left, the
   catalogue's on the right. Everything that crosses is a traveller. */

.choose-stage {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 44px minmax(0, 1.08fr);
  align-items: start;
}

.choose-side {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  min-width: 0;
}

.choose-label {
  font-size: 11px;
  font-weight: 650;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-faint);
}

.choose-task {
  align-self: stretch;
  padding: 8px 10px 9px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-raised);
  box-shadow: var(--fig-shadow);
}

.choose-task-text {
  padding-top: 2px;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.35;
}

.choose-who {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 30px;
  max-width: 100%;
}

.choose-dock {
  display: grid;
  align-items: center;
  justify-items: start;
  min-width: 0;
}

.choose-dock > * {
  grid-area: 1 / 1;
}

.choose-dock-card {
  position: relative;
  display: inline-flex;
}

/* The dock, empty: an outline the size of a card, until there is one. */
.choose-dock-card::before {
  content: "";
  position: absolute;
  inset: 0;
  border: 1px dashed var(--border-strong);
  border-radius: 6px;
}

.beat-ge-4 .choose-dock-card::before {
  opacity: 0;
}

/* What the agent says as it picks - only on that beat. The reason is the
   point: a choice with a reason is somebody's. */
.choose-reason {
  position: relative;
  z-index: 1;
  padding: 5px 9px;
  border: 1px solid var(--border-strong);
  border-radius: 10px 10px 10px 3px;
  background: var(--bg-raised);
  box-shadow: var(--fig-shadow);
  font-size: 12px;
  line-height: 1.3;
  opacity: 0;
  transform: translateX(-6px) scale(0.96);
  transform-origin: left center;
}

[data-beat="4"] .choose-reason {
  opacity: 1;
  transform: none;
  transition-delay: 500ms;
}

.choose-calls {
  display: grid;
  justify-items: start;
  max-width: 100%;
}

.choose-calls > * {
  grid-area: 1 / 1;
}

.docsfig .choose-call {
  color: var(--text);
  font-size: 11px;
  line-height: 18px;
  white-space: nowrap;
}

/* The ask is laid out in the search box, where it ends up. It appears
   beside the agent first, and only then sets off - the fade is quick and
   the flight waits for it. */
.choose-ask:not(.fig-ghost) {
  position: relative;
  z-index: 3;
  opacity: 0;
  transform: translate(var(--to-agent-x, 0px), var(--to-agent-y, 0px));
}

.beat-ge-1 .choose-ask:not(.fig-ghost) {
  opacity: 1;
  transform: none;
  transition-duration: 800ms, 250ms;
  transition-delay: 450ms, 0ms;
}

.choose-load {
  opacity: 0;
  transform: translateY(5px);
}

.beat-ge-5 .choose-load {
  opacity: 1;
  transform: none;
}

.choose-short-label {
  opacity: 0;
}

.beat-ge-3 .choose-short-label {
  opacity: 1;
}

.choose-shortlist {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 7px;
  --stagger: 90ms;
}

.choose-short {
  position: relative;
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 5px 8px;
  opacity: 0;
  transform: translate(var(--to-row-x, 0px), var(--to-row-y, 0px));
}

.beat-ge-3 .choose-short {
  opacity: 1;
  transform: none;
  transition-duration: 750ms, 250ms;
}

/* Once the agent has picked, the rest of the list is still what came
   back - it is only no longer what the eye is for. */
.beat-ge-4 .choose-short {
  opacity: 0.4;
  transition-delay: 350ms;
}

[data-beat="4"] .choose-short.is-picked {
  opacity: 1;
}

.choose-rank {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 17px;
  height: 17px;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 700;
  color: var(--text-dim);
}

.choose-place-corner,
.choose-hand {
  position: absolute;
  left: -8px;
  top: -8px;
  width: 16px;
  height: 16px;
}

.choose-place.fig-slot-shown::before {
  opacity: 0;
}

.beat-ge-5 .choose-place.fig-slot-shown::before {
  opacity: 1;
}

/* The chosen card: in its catalogue row, then on the shortlist, then - its
   resting place, and so its layout - docked beside the agent. */
.choose-picked:not(.fig-ghost) {
  z-index: 4;
  --stagger: 90ms;
  opacity: 0;
  transform: translate(var(--to-row-x, 0px), var(--to-row-y, 0px));
}

.choose-picked:not(.fig-ghost)::after {
  content: "";
  position: absolute;
  inset: -1px -1px -1px -3px;
  border: 1.5px solid var(--accent);
  border-radius: inherit;
  box-shadow: var(--fig-lift);
  opacity: 0;
}

.beat-ge-3 .choose-picked:not(.fig-ghost) {
  opacity: 1;
  transform: translate(var(--to-list-x, 0px), var(--to-list-y, 0px));
  transition-duration: 750ms, 250ms;
}

.beat-ge-4 .choose-picked:not(.fig-ghost) {
  transform: translate(var(--to-list-x, 0px), var(--to-list-y, 0px)) translate(3px, -3px) scale(1.05);
  transition-delay: 380ms;
}

.beat-ge-4 .choose-picked:not(.fig-ghost)::after {
  opacity: 1;
  transition-delay: 380ms;
}

.beat-ge-5 .choose-picked:not(.fig-ghost) {
  transform: none;
  transition-duration: 800ms, 250ms;
  transition-delay: 300ms;
}

/* The agent's hand. It leaves the agent, lands on the card it has chosen,
   and from then on goes where the card goes. */
.choose-hand.fig-agent-mark {
  z-index: 5;
  font-size: 10px;
  box-shadow: 0 0 0 2px var(--bg-raised);
  opacity: 0;
  transform: translate(var(--to-agent-x, 0px), var(--to-agent-y, 0px));
}

.beat-ge-4 .choose-hand.fig-agent-mark {
  opacity: 1;
  transform: translate(var(--to-list-x, 0px), var(--to-list-y, 0px)) translate(-1px, -4px);
  transition-duration: 520ms, 120ms;
}

.beat-ge-5 .choose-hand.fig-agent-mark {
  transform: none;
  transition-duration: 800ms, 120ms;
  transition-delay: 300ms;
}

.choose-filed {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 3px 6px;
  font-size: 12px;
  color: var(--text-dim);
  opacity: 0;
  transform: translateY(6px);
}

.docsfig .choose-filed code {
  font-size: 11px;
  font-weight: 600;
  color: var(--text);
}

.choose-filed-dot {
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--series-1);
}

.beat-ge-5 .choose-filed {
  opacity: 1;
  transform: none;
  transition-delay: 900ms;
}

.choose-wire {
  position: relative;
  align-self: stretch;
  min-height: 24px;
}

.choose-wire::before {
  content: "";
  position: absolute;
  left: 4px;
  right: 4px;
  top: 52px;
  border-top: 1px dashed var(--border-strong);
}

.choose-wire-tag {
  position: absolute;
  left: 50%;
  top: 52px;
  transform: translate(-50%, -50%);
  padding: 0 3px;
  background: var(--bg-raised);
  font-size: 11px;
  font-weight: 650;
  color: var(--text-faint);
}

.choose-search {
  display: flex;
  align-items: center;
  gap: 7px;
  min-height: 34px;
  margin: 10px;
  padding: 6px 9px;
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  font-size: 13px;
  color: var(--text-faint);
}

.choose-list {
  position: relative;
  overflow: hidden;
  border-radius: 0 0 8px 8px;
}

.choose-row {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 4px 8px;
  min-height: 31px;
  padding: 5px 10px;
  border-top: 1px solid var(--border);
}

.choose-row.is-match::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--bg-hover);
  opacity: 0;
}

.docsfig .choose-row-key {
  position: relative;
  font-size: 11px;
  white-space: nowrap;
}

.beat-ge-2 .choose-row:not(.is-match) .choose-row-key {
  opacity: 0.4;
  transition-delay: 500ms;
}

.beat-ge-2 .choose-row.is-match::before {
  opacity: 1;
  transition-delay: calc(500ms + var(--i, 0) * 140ms);
}

.choose-why {
  position: relative;
  display: inline-flex;
  gap: 4px;
}

.choose-badge {
  padding: 1px 7px;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--bg-raised);
  font-size: 11px;
  font-weight: 600;
  line-height: 15px;
  color: var(--text-dim);
  white-space: nowrap;
}

.choose-badge-meaning {
  border-color: color-mix(in srgb, var(--series-1) 45%, var(--bg-raised));
  color: var(--series-1);
}

.choose-row .choose-badge {
  opacity: 0;
  transform: scale(0.7);
}

.beat-ge-2 .choose-row .choose-badge {
  opacity: 1;
  transform: none;
  transition-delay: calc(600ms + var(--i, 0) * 140ms);
}

/* The search, passing over the catalogue once. Decoration on a state that
   is complete without it - the badges are the result, this is only the
   looking. */
.choose-scan {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
}

.choose-scan-band {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 44px;
  background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--series-1) 16%, transparent));
  border-bottom: 1.5px solid var(--series-1);
}

@container docsfig (max-width: 700px) {
  .choose-stage {
    grid-template-columns: minmax(0, 1fr);
  }

  .choose-wire {
    height: 34px;
  }

  .choose-wire::before {
    left: 50%;
    right: auto;
    top: 4px;
    bottom: 4px;
    border-top: 0;
    border-left: 1px dashed var(--border-strong);
  }

  .choose-wire-tag {
    top: 50%;
    padding: 1px 0;
  }

  .choose-search {
    margin: 8px;
    padding: 6px 7px;
  }

  .choose-search .fig-glyph {
    display: none;
  }

  .choose-row {
    padding: 5px 8px;
  }
}

/* --- eval-loop ----------------------------------------------------------
   Three panes: picked, run, proposed. Orange at both ends and nowhere in
   the middle - a person starts it and a person lands it. */

.eval-stage {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.2fr) minmax(0, 1fr);
  align-items: stretch;
  gap: 12px;
}

.eval-person:not(.fig-ghost) {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 5;
  transform: translate(var(--to-pick-x, 0px), var(--to-pick-y, 0px));
}

.beat-ge-5 .eval-person:not(.fig-ghost) {
  transform: translate(var(--to-pull-x, 0px), var(--to-pull-y, 0px));
  transition-duration: 900ms, 300ms;
}

.eval-pick,
.eval-trials,
.eval-pull {
  display: flex;
  flex-direction: column;
}

.eval-options {
  display: grid;
  gap: 9px;
  padding: 12px 10px 4px;
}

.eval-option {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 26px;
}

.eval-box {
  display: grid;
  flex: none;
  width: 15px;
  height: 15px;
  border: 1.5px solid var(--border-strong);
  border-radius: 4px;
  font-size: 12px;
}

.eval-box .fig-glyph {
  margin: -1.5px;
  width: 15px;
  height: 15px;
  padding: 2px;
  border-radius: 4px;
  background: var(--attention);
  color: var(--on-attention);
  opacity: 0;
  transform: scale(0.5);
}

.beat-ge-1 .is-picked .eval-box .fig-glyph {
  opacity: 1;
  transform: none;
}

.docsfig .eval-option-key {
  font-size: 11px;
  color: var(--text-dim);
  white-space: nowrap;
}

.eval-run {
  align-self: flex-start;
  margin: 8px 10px 12px;
  opacity: 0.35;
}

.beat-ge-1 .eval-run {
  opacity: 1;
  transition-delay: 350ms;
}

.beat-ge-2 .eval-run {
  opacity: 0.35;
  transition-delay: 0ms;
}

.eval-legend {
  font-size: 11px;
  font-weight: 500;
  color: var(--text-faint);
}

.eval-config {
  padding: 10px 10px 12px;
}

.eval-config + .eval-config {
  border-top: 1px solid var(--border);
}

.eval-config-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 8px;
}

/* Both configs are the picked invariant, so both cards start on the
   option that was ticked and part from it - one as it is, one changed. */
.eval-card {
  z-index: 3;
  --stagger: 110ms;
  opacity: 0;
  transform: translate(var(--to-pick-x, 0px), var(--to-pick-y, 0px));
}

.beat-ge-1 .eval-card {
  opacity: 1;
  transition-delay: 0ms;
}

.beat-ge-2 .eval-card {
  transform: none;
  transition-duration: 800ms, 300ms;
  transition-delay: calc(var(--i, 0) * var(--stagger));
}

.eval-config-note {
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-dim);
  opacity: 0;
}

.is-best .eval-config-note {
  color: var(--ok-text);
  font-weight: 600;
}

.beat-ge-2 .eval-config-note {
  opacity: 1;
  transition-delay: 500ms;
}

.eval-cells {
  display: grid;
  grid-template-columns: repeat(8, 18px);
  gap: 4px;
  padding-top: 9px;
  --stagger: 65ms;
}

.eval-cell {
  display: grid;
  width: 18px;
  height: 18px;
  border: 1px solid var(--border);
  border-radius: 4px;
}

.eval-cell-mark {
  display: grid;
  place-items: center;
  margin: -1px;
  border-radius: 4px;
  font-size: 11px;
  opacity: 0;
  transform: scale(0.3);
}

.is-pass .eval-cell-mark {
  background: var(--heat-1);
  color: var(--ok-text);
}

.is-fail .eval-cell-mark {
  background: var(--diff-del-bg);
  color: var(--err-text);
}

.beat-ge-2 .eval-cell-mark {
  opacity: 1;
  transform: none;
  transition-duration: 380ms, 200ms;
  transition-delay: calc(650ms + var(--i, 0) * var(--stagger));
}

.eval-rate {
  display: flex;
  align-items: center;
  gap: 9px;
  padding-top: 9px;
  --stagger: 160ms;
}

.eval-rate-track {
  flex: 1 1 auto;
  max-width: 172px;
  height: 6px;
  border-radius: 3px;
  background: var(--bar-rest);
  overflow: hidden;
}

.eval-rate-fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--series-1);
  transform: scaleX(0);
  transform-origin: left center;
}

.beat-ge-3 .eval-rate-fill {
  transform: scaleX(var(--rate));
  transition-duration: 900ms, 300ms;
}

.eval-rate-value {
  flex: none;
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  color: var(--text-faint);
  opacity: 0;
}

.eval-rate-value strong {
  font-size: 12.5px;
  color: var(--text);
}

.is-best .eval-rate-value strong {
  color: var(--ok-text);
}

.beat-ge-3 .eval-rate-value {
  opacity: 1;
  transition-delay: calc(450ms + var(--i, 0) * var(--stagger));
}

.eval-pull {
  position: relative;
}

.eval-pull .fig-panel-bar .fig-glyph {
  font-size: 13px;
}

.eval-pull-empty {
  position: absolute;
  inset: 32px 0 0;
  display: grid;
  place-items: center;
  font-size: 12px;
  color: var(--text-faintest);
}

.beat-ge-4 .eval-pull-empty {
  opacity: 0;
}

/* The proposal comes out of the results: it starts small, in the middle
   of the trials pane, because that is what it is made of. */
.eval-proposal {
  position: relative;
  z-index: 4;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 7px;
  padding: 12px 10px 4px;
  opacity: 0;
  transform: translate(var(--to-trials-x, 0px), var(--to-trials-y, 0px)) scale(0.5);
}

.beat-ge-4 .eval-proposal {
  opacity: 1;
  transform: none;
  transition-duration: 800ms, 300ms;
}

.eval-proposal-title {
  font-size: 12.5px;
  font-weight: 650;
  line-height: 1.3;
}

.docsfig .eval-diff {
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--diff-add-bg);
  font-size: 11px;
  color: var(--ok-text);
  overflow-wrap: anywhere;
}

.eval-evidence {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  color: var(--text-dim);
}

.eval-evidence::before {
  content: "";
  flex: none;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--series-1);
}

.beat-ge-5 .eval-proposed .fig-inv-ver-now {
  opacity: 0;
  transform: translateY(-70%);
  transition-delay: 1300ms;
}

.beat-ge-5 .eval-proposed .fig-inv-ver-next {
  opacity: 1;
  transform: none;
  transition-delay: 1300ms;
}

.eval-review {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 8px;
  margin-top: auto;
  padding: 8px 10px 12px;
}

.eval-merge {
  opacity: 0;
}

.beat-ge-4 .eval-merge {
  opacity: 0.4;
  transition-delay: 500ms;
}

.beat-ge-5 .eval-merge {
  opacity: 1;
  transition-delay: 0ms;
}

.beat-ge-5 .eval-merge::before,
.beat-ge-5 .eval-merge .fig-btn-done {
  opacity: 1;
  transition-delay: 1100ms;
}

.beat-ge-5 .eval-merge .fig-btn-do {
  opacity: 0;
  transition-delay: 1100ms;
}

@container docsfig (max-width: 680px) {
  .eval-stage {
    grid-template-columns: minmax(0, 1fr);
  }

  .eval-legend {
    display: none;
  }

  .eval-pull {
    min-height: 0;
  }
}

/* --- decoration that needs a clock -------------------------------------
   The only keyframes in the file, and none of them says anything: a caret
   blinks while a line is being typed, the search band passes once, the
   reach dots pulse twice to say "reaching". Each is tied to a single beat
   by `data-beat`, so leaving the beat ends it. */
@media (prefers-reduced-motion: no-preference) {
  [data-beat="0"] .setup-caret,
  [data-beat="1"] .setup-caret {
    animation: fig-blink 1s steps(1) infinite;
  }

  /* The cover's steps are set inline; its duration is the typing speed. */
  .scene-stage .setup-cover {
    transition-duration: 1500ms;
    transition-delay: 350ms;
  }

  [data-beat="2"] .choose-scan {
    animation: fig-scan 950ms ease-in-out both;
  }

  [data-beat="1"] .scope-reach::after {
    animation: fig-pulse 1200ms ease-out 900ms 2 both;
    animation-delay: calc(900ms + var(--i, 0) * 45ms);
  }

  /* Reach leaving for the scope: the flight first, the fade as it lands. */
  .beat-ge-3 .scope-tile:not(.is-named) .scope-reach {
    transition-duration: 800ms, 260ms;
    transition-delay: calc(var(--i, 0) * 30ms), calc(var(--i, 0) * 30ms + 560ms);
  }

  @keyframes fig-blink {
    50% { opacity: 0; }
  }

  @keyframes fig-scan {
    from { opacity: 1; transform: translateY(-100%); }
    80% { opacity: 1; }
    to { opacity: 0; transform: none; }
  }

  @keyframes fig-pulse {
    from { opacity: 0.45; transform: scale(0.8); }
    to { opacity: 0; transform: scale(2); }
  }
}
