/* Live pi terminal widget.
 *
 * The one surface on the site where the product is running rather than being
 * described, so it is styled as an instrument and not as a picture of a shell.
 * No window dressing: there are no traffic-light dots, because the thing is
 * live and a mac-window costume is the clearest possible signal that it is not.
 *
 * The governance chain is the subject. Everything else — chrome, meters,
 * composer — is deliberately quiet so that the rail, the approval card, and a
 * blocked call are what the eye lands on.
 *
 * Always dark, in both themes. --sp-color-terminal-* is intentionally not a
 * light-dark() pair: a console that follows the page theme stops reading as a
 * console. */

.pi-terminal {
  /* Local tokens. Scoped here rather than added to core/tokens.css — one
     component's rail geometry is not a design-system concern. */
  --pi-pip: 0.875rem;
  --pi-rail-track: color-mix(in oklch, var(--sp-color-terminal-border) 70%, transparent);
  --pi-ring-size: 2.25rem;
  --pi-ring-width: 3px;
  --pi-ring-fill: 1;
  --pi-rim: color-mix(in oklch, var(--sp-color-primary) 32%, transparent);
  --pi-pass: var(--sp-color-success);
  --pi-fail: var(--sp-color-error);

  display: block;
  max-width: 860px;
  margin: 0 auto;
}

.pi-demo {
  padding: var(--sp-space-4xl) 0 var(--sp-space-12);
  border-top: 1px solid var(--sp-color-border);
}

.pi-demo-note {
  max-width: 860px;
  margin: var(--sp-space-4) auto 0;
  color: var(--sp-color-text-secondary);
  font-size: 0.85rem;
  text-align: center;
}

/* Screen-reader-only text. Local because the public site has no utility layer;
   the glyphs and colours in the rail all have a spoken equivalent and this is
   what carries it. */
.sp-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── shell ──────────────────────────────────────────────────────────────── */

.pi-terminal .terminal {
  position: relative;
  opacity: 1;
  visibility: visible;
  background: var(--sp-color-terminal-bg);
  /* Softened: at full strength the edge read as a hard white frame against the
     dark stage. The rim-light below is what says "lit"; the border only has to
     say "ends here". */
  border: 1px solid color-mix(in oklch, var(--sp-color-terminal-border) 65%, transparent);
  /* The brand's asymmetric corner, which the rest of the site uses and this
     widget used to ignore. */
  border-radius: var(--sp-corners-lg);
  box-shadow: var(--sp-shadow-hero);
  overflow: hidden;
}

/* A rim-light along the top edge, so the shell reads as lit rather than as a
   flat rectangle sitting on the page. */
.pi-terminal .terminal::before {
  content: '';
  position: absolute;
  inset: 0 0 auto;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--pi-rim) 18%, var(--pi-rim) 82%, transparent);
  pointer-events: none;
  z-index: 1;
}

/* On the homepage stage the shell becomes glass so the field reads through it.
   Scoped, because everywhere else this widget sits on an opaque page and glass
   there would just look like a rendering bug.

   One surface, not two. The transcript used to carry a near-solid slab of its
   own while the chrome around it stayed transparent — which made the shell read
   as a box inside a box, with a visible seam where the slab ended. The whole
   shell now wears the near-solid ink (mono text over moving footage still needs
   it) and every section inside is a region of that one surface. */
.home-split-body .pi-terminal {
  --pi-rim: var(--sp-stage-rim);
}

.home-split-body .pi-terminal .terminal {
  background: oklch(0.14 0.01 55 / 0.86);
  backdrop-filter: blur(var(--sp-stage-blur)) saturate(140%);
  -webkit-backdrop-filter: blur(var(--sp-stage-blur)) saturate(140%);
  border-color: var(--sp-stage-glass-border);
  box-shadow: 0 24px 60px oklch(0 0 0 / 0.45), 0 0 0 1px oklch(1 0 0 / 0.04) inset;
}

.home-split-body .pi-terminal .terminal-body {
  background: transparent;
}

.home-split-body .pi-terminal .terminal-header,
.home-split-body .pi-terminal .pi-composer,
.home-split-body .pi-terminal .pi-approvals:not(:empty) {
  background: transparent;
}

/* The replay strip is chrome like the rest of it. Left opaque it was a grey
   slab across the foot of a glass card — the one place the illusion broke. */
.home-split-body .pi-terminal .pi-gate {
  background: transparent;
}

/* Every internal division uses the shell's own edge. The default here is
   `--sp-color-terminal-border`, a zero-chroma grey that belongs to the opaque
   theme this widget wears everywhere else; against the stage's warm glass it
   reads as a hard rule drawn across the material rather than a seam in it. */
.home-split-body .pi-terminal .terminal-header {
  border-bottom-color: var(--sp-stage-glass-border);
}

.home-split-body .pi-terminal .pi-composer,
.home-split-body .pi-terminal .pi-gate,
.home-split-body .pi-terminal .pi-approvals:not(:empty) {
  border-top-color: var(--sp-stage-glass-border);
}

/* ── welcome ────────────────────────────────────────────────────────────── */

/* The empty transcript's one job: hand the visitor a first prompt. Removed by
   the transcript's own append, so it never coexists with real output. */
.pi-welcome {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-4);
  padding: var(--sp-space-6) var(--sp-space-4);
  max-width: 34rem;
}

.pi-welcome__lead {
  margin: 0;
  color: var(--sp-color-terminal-text);
  font-size: 0.9rem;
  line-height: 1.5;
}

.pi-welcome__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-space-2);
}

.pi-welcome__chip {
  cursor: pointer;
  font: inherit;
  font-size: 0.85rem;
  color: var(--sp-color-terminal-text);
  background: color-mix(in oklch, var(--sp-color-terminal-border) 30%, transparent);
  border: 1px solid color-mix(in oklch, var(--sp-color-terminal-border) 80%, transparent);
  border-radius: var(--sp-corners-sm);
  padding: var(--sp-space-2) var(--sp-space-3);
  text-align: start;
  transition: border-color var(--sp-transition-fast), background var(--sp-transition-fast);
}

.pi-welcome__chip:hover,
.pi-welcome__chip:focus-visible {
  border-color: var(--sp-color-primary);
  background: color-mix(in oklch, var(--sp-color-primary) 14%, transparent);
}

.pi-welcome__more {
  margin: 0;
  color: var(--sp-color-text-secondary);
  font-size: 0.8rem;
}


/* ── header ─────────────────────────────────────────────────────────────── */

.pi-terminal .terminal-header {
  display: flex;
  /* The chips are all nowrap chrome; when the row runs out of width they must
     wrap to a second line, not shrink to zero and paint over each other. */
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-space-1) var(--sp-space-3);
  padding: var(--sp-space-2) var(--sp-space-4);
  background: var(--sp-color-terminal-header);
  border-bottom: 1px solid color-mix(in oklch, var(--sp-color-terminal-border) 70%, transparent);
}

/* Until a signed-in session is actually live (`is-session`, set on
   session_ready) the header is a statement, not a control surface: it dims,
   and its chips take no clicks. */
.pi-terminal:not(.is-session) .terminal-header {
  opacity: 0.55;
}

.pi-terminal:not(.is-session) .terminal-header :is(button, select, a) {
  pointer-events: none;
  cursor: default;
}

/* The one exception: the way out of this state cannot be dimmed by the state
   itself. It sits at full strength and takes clicks while everything else in
   the header is a statement. */
.pi-terminal .terminal-header .pi-replay-cta {
  opacity: 1;
  pointer-events: auto;
  cursor: pointer;
  /* Pushed to the trailing edge, where the header's commit action belongs;
     `wrap` on the row means it drops to its own line rather than shrinking. */
  margin-left: auto;
}

/* Read-only, said once, in the register the header speaks in. Warning-toned
   rather than error: nothing is broken, it is simply not live. */
.pi-replay-flag {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.1rem 0.5rem;
  border: 1px solid color-mix(in oklch, var(--sp-color-warning) 45%, transparent);
  border-radius: var(--sp-radius-full);
  color: var(--sp-color-warning);
  font-family: var(--sp-font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

/* The class sets display, so the hidden attribute needs saying twice. */
.pi-replay-flag[hidden] {
  display: none;
}

.pi-replay-flag::before {
  content: '▶';
  font-size: 0.7em;
}

/* Header density: the CTA is a chip here, not a composer button. */
.pi-replay-cta {
  min-height: 1.75rem;
  min-width: 0;
  padding: 0.2rem var(--sp-space-3);
  font-size: 0.72rem;
}

/* The titlebar is branded rather than labelled.
   It used to read "pi — governed", which named an internal binary and a property
   in a way that meant nothing to a visitor. A product surface should say whose
   it is; what it does is demonstrated by the thing underneath. */
.pi-brand {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-space-2);
  flex: none;
}

/* The wordmark asset itself; height picks the scale, width follows. */
.pi-brand-wordmark {
  display: block;
  height: 16px;
  width: auto;
  flex: none;
}

/* Confinement, promoted out of the transcript. It was the first line of output
   and therefore the first thing to scroll away — a claim about a sandbox is
   worth more when it is still on screen an hour into a session. */
.pi-jail-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.1rem 0.5rem;
  border: 1px solid color-mix(in oklch, var(--sp-color-success) 40%, transparent);
  border-radius: var(--sp-radius-full);
  color: var(--sp-color-success);
  font-family: var(--sp-font-mono);
  font-size: 0.66rem;
  white-space: nowrap;
}

.pi-jail-chip::before {
  content: '⛨';
  font-size: 0.8em;
}

/* Who the session is signed to. A padlock and the identity, dressed as a chip
   so the header stays one family — the lock is the claim that every call this
   terminal makes carries a token minted for exactly this identity. */
.pi-user-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  min-width: 0;
  padding: 0.1rem 0.5rem;
  border: 1px solid color-mix(in oklch, var(--sp-color-primary) 35%, transparent);
  border-radius: var(--sp-radius-full);
  color: var(--sp-color-terminal-text);
  font-family: var(--sp-font-mono);
  font-size: 0.66rem;
}

.pi-user-lock {
  width: 0.7rem;
  height: 0.7rem;
  flex: none;
  color: var(--sp-color-primary);
}

/* The identity may be long; the chip may not be. */
.pi-user-chip > span {
  max-width: 14rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* In replay the composer takes no input, so buttons that cannot act are not
   drawn at all — dead controls read as a broken page, not a demo. Clear joins
   them: with no live session there is nothing to clear. */
.pi-terminal.is-replay .pi-btn--send,
.pi-terminal.is-replay .pi-btn--stop,
.pi-terminal.is-replay .pi-btn--clear,
.pi-terminal:not(.is-session) .pi-btn--clear {
  display: none;
}

/* The class sets display, so without this the hidden attribute loses the
   cascade and a JS `hidden = true` silently stops working. */
.pi-btn[hidden] {
  display: none;
}

/* The model picker, dressed as a chip so the header stays one family. Only
   rendered when the gateway advertises more than one model. The native
   arrow is replaced with an inline chevron so the closed control matches the
   jail chip beside it on every platform. */
.pi-model {
  appearance: none;
  -webkit-appearance: none;
  /* Wide enough for `id · $in/$out · ctx` — the closed control shows the
     chosen model's price card, and an ellipsis through the rate defeats the
     point of carrying it. */
  max-width: 19rem;
  padding: 0.15rem 1.3rem 0.15rem 0.6rem;
  overflow: hidden;
  background-color: color-mix(in oklch, var(--sp-color-terminal-text-dim) 8%, transparent);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 6'%3E%3Cpath d='M1 1.5 4 4.5 7 1.5' fill='none' stroke='%23999' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.45rem center;
  background-size: 0.5rem;
  border: 1px solid color-mix(in oklch, var(--sp-color-terminal-text-dim) 40%, transparent);
  border-radius: var(--sp-radius-full);
  color: var(--sp-color-terminal-text-dim);
  font-family: var(--sp-font-mono);
  font-size: 0.66rem;
  line-height: 1.4;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease, background-color 0.15s ease;
}

.pi-model:hover {
  border-color: color-mix(in oklch, var(--sp-color-primary) 55%, transparent);
  color: var(--sp-color-terminal-text);
}

.pi-model:focus-visible {
  outline: 2px solid var(--sp-color-primary);
  outline-offset: 1px;
}

/* The open list is native; these are the only knobs it honours. */
.pi-model option,
.pi-model optgroup {
  background: var(--sp-color-terminal-bg, #1a1a1a);
  color: var(--sp-color-terminal-text, #eee);
  font-family: var(--sp-font-mono);
}

.pi-model optgroup {
  color: var(--sp-color-terminal-text-dim, #999);
  font-size: 0.7rem;
  font-style: normal;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.pi-live {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-space-2);
}

.pi-live-dot {
  width: 7px;
  height: 7px;
  border-radius: var(--sp-radius-full);
  background: var(--sp-color-terminal-text-dim);
  flex: none;
}

.pi-live[data-state="live"] .pi-live-dot { background: var(--sp-color-success); }
.pi-live[data-state="reconnecting"] .pi-live-dot,
.pi-live[data-state="session in use"] .pi-live-dot { background: var(--sp-color-warning); }
.pi-live[data-state="ended"] .pi-live-dot { background: var(--sp-color-error); }

.pi-status {
  font-family: var(--sp-font-mono);
  font-size: 0.75rem;
  color: var(--sp-color-terminal-text-dim);
  text-transform: lowercase;
}

.pi-status[data-state="live"] {
  color: var(--sp-color-success);
}

.pi-status[data-state="reconnecting"],
.pi-status[data-state="session in use"] {
  color: var(--sp-color-warning);
}

.pi-status[data-state="ended"] {
  color: var(--sp-color-error);
}

/* Meters. The page claims governance is metered; a number that moves while you
   watch is the cheapest available proof, so it lives in the chrome. */
.pi-meters {
  display: flex;
  align-items: center;
  gap: var(--sp-space-3);
  margin-left: auto;
}

.pi-meter {
  display: inline-flex;
  align-items: baseline;
  gap: 0.25rem;
  font-family: var(--sp-font-mono);
  font-size: 0.72rem;
  color: var(--sp-color-terminal-text-dim);
}

.pi-meter b {
  color: var(--sp-color-terminal-text);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.pi-meter i {
  font-style: normal;
  opacity: 0.72;
}

/* A non-zero block count is the interesting number on this page. */
.pi-meter[data-hot="1"] b {
  color: var(--sp-color-error);
}

.pi-trace-link {
  font-family: var(--sp-font-mono);
  font-size: 0.72rem;
  color: var(--sp-color-primary);
  text-decoration: none;
  white-space: nowrap;
}

.pi-trace-link:hover {
  text-decoration: underline;
}


/* The approval-mode toggle. Quiet in auto-approve, where it is stating the
   ordinary case, and lit in manual, where it is stating that this terminal is
   holding calls for a person — the state the operator must not lose track of.
   Warn rather than success for the lit state: manual is not "safer", it is
   attention owed. */
.pi-approve-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.1rem 0.5rem;
  border: 1px solid color-mix(in oklch, var(--sp-color-terminal-text-dim) 40%, transparent);
  border-radius: var(--sp-radius-full);
  background: color-mix(in oklch, var(--sp-color-terminal-text-dim) 8%, transparent);
  color: var(--sp-color-terminal-text-dim);
  font-family: var(--sp-font-mono);
  font-size: 0.66rem;
  white-space: nowrap;
  cursor: pointer;
}

.pi-approve-chip svg {
  width: 0.7rem;
  height: 0.7rem;
  flex: none;
}

.pi-approve-chip:hover {
  color: var(--sp-color-terminal-text);
  border-color: color-mix(in oklch, var(--sp-color-terminal-text-dim) 70%, transparent);
}

.pi-approve-chip[aria-pressed='true'] {
  border-color: color-mix(in oklch, var(--sp-color-warning) 55%, transparent);
  background: color-mix(in oklch, var(--sp-color-warning) 12%, transparent);
  color: var(--sp-color-warning);
}

/* The tick belongs to the mode that lets calls through; manual is a pause,
   not a pass. */
.pi-approve-chip[aria-pressed='true'] svg {
  display: none;
}

.pi-approve-chip:focus-visible {
  outline: 2px solid var(--sp-color-primary);
  outline-offset: 1px;
}

/* ── transcript ─────────────────────────────────────────────────────────── */

.pi-body-wrap {
  position: relative;
}

.pi-terminal .terminal-body {
  min-height: 260px;
  max-height: 480px;
  overflow-y: auto;
  /* Growth must never shift what the reader is looking at; the widget decides
     when to follow the output, and the browser must not second-guess it. */
  overflow-anchor: none;
  padding: var(--sp-space-4) var(--sp-space-5);
  font-family: var(--sp-font-mono);
  font-variant-ligatures: none;
  font-size: 0.875rem;
  line-height: 1.7;
  color: var(--sp-color-terminal-text);
  box-shadow: var(--sp-shadow-inner-glow);
  scrollbar-width: thin;
  scrollbar-color: var(--sp-color-terminal-border) transparent;
}

.pi-terminal .terminal-body:focus-visible {
  outline: 1px solid var(--pi-rim);
  outline-offset: -2px;
}

.pi-terminal .terminal-body::-webkit-scrollbar {
  width: 10px;
}

.pi-terminal .terminal-body::-webkit-scrollbar-track {
  background: transparent;
}

.pi-terminal .terminal-body::-webkit-scrollbar-thumb {
  background: var(--sp-color-terminal-border);
  border-radius: var(--sp-radius-full);
  border: 3px solid transparent;
  background-clip: content-box;
}

.pi-terminal .terminal-line {
  white-space: pre-wrap;
  word-break: break-word;
  /* Measure cap: without one, mono across the full pane runs past 150
     characters a line. Matches .pi-prose so the two kinds of text share one
     right edge. */
  max-width: 88ch;
}

.pi-terminal .prompt {
  color: var(--sp-color-primary);
}

/* A user turn opens every act, so a hairline above each one is all the
   grouping the transcript needs: the eye scans a flush-left column of `>`
   prompts and each answer hangs off its question. */
.pi-terminal .pi-turn-user {
  margin-top: var(--sp-space-5);
  padding-top: var(--sp-space-3);
  border-top: 1px solid color-mix(in oklch, var(--sp-color-terminal-border) 45%, transparent);
}

.pi-terminal .pi-turn-user:first-child {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

.pi-terminal .pi-turn-user .command {
  font-weight: 600;
}

/* Replay commentary — the narrator's voice, not the agent's. Set apart as an
   editorial aside so the two can never be read as the same speaker. */
.pi-terminal .pi-note {
  display: inline-block;
  max-width: 60ch;
  margin: var(--sp-space-2) 0 var(--sp-space-1);
  font-size: 0.78rem;
  font-style: italic;
}

.pi-terminal .pi-note::before {
  content: '— ';
  opacity: 0.5;
}

/* In replay the agent's half of the exchange is indented off the prompt
   column. Scoped to replay: a live session's transcript is the operator's
   working surface and keeps its full width. */
.pi-terminal.is-replay .terminal-body .pi-prose,
.pi-terminal.is-replay .terminal-body .pi-tool,
.pi-terminal.is-replay .terminal-body .pi-rail-line,
.pi-terminal.is-replay .terminal-body .pi-blocked,
.pi-terminal.is-replay .terminal-body .pi-note {
  margin-left: var(--sp-space-4);
}

.pi-terminal.is-replay .terminal-body .pi-prose {
  padding-left: var(--sp-space-3);
  border-left: 1px solid color-mix(in oklch, var(--sp-color-terminal-border) 40%, transparent);
}

/* The replay stops here rather than looping, so the button is the last thing
   in the transcript — sitting in the same indented column as the agent's half
   of the exchange it just finished. */
.pi-terminal.is-replay .terminal-body .pi-replay-end {
  margin: var(--sp-space-3) 0 var(--sp-space-2) var(--sp-space-4);
}

.pi-terminal .pi-replay-restart {
  font-size: 0.78rem;
}

.pi-terminal .command {
  color: var(--sp-color-terminal-text);
}

.pi-terminal .output,
.pi-terminal .pi-stream {
  color: var(--sp-color-terminal-text);
}

.pi-terminal .output-dim {
  color: var(--sp-color-terminal-text-dim);
}

.output-warn {
  color: var(--sp-color-warning);
}

/* Credit exhaustion is a state with a way out, not a stream of failures, so it
   renders once as a card with the action beside the sentence. */
.pi-error-card {
  display: flex;
  align-items: center;
  gap: var(--sp-space-2);
  margin-block: var(--sp-space-2);
  padding: var(--sp-space-2) var(--sp-space-3);
  border: 1px solid color-mix(in oklch, var(--sp-color-warning) 45%, transparent);
  border-radius: var(--sp-radius-md);
  background: color-mix(in oklch, var(--sp-color-warning) 8%, transparent);
  font-family: var(--sp-font-mono);
  font-size: 0.8rem;
}

.pi-error-card__icon {
  color: var(--sp-color-warning);
}

.pi-error-card__text {
  flex: 1;
  color: var(--sp-color-terminal-text);
}

.pi-error-card__action {
  padding: var(--sp-space-1) var(--sp-space-2);
  border: 1px solid var(--sp-color-warning);
  border-radius: var(--sp-radius-full);
  color: var(--sp-color-warning);
  text-decoration: none;
  white-space: nowrap;
}

.pi-error-card__action:hover {
  background: color-mix(in oklch, var(--sp-color-warning) 16%, transparent);
}

.pi-trimmed {
  color: var(--sp-color-terminal-text-dim);
  opacity: 0.6;
}


/* Jump-to-bottom. Only appears once the reader has scrolled away, because that
   is the only time autoscroll is suppressed. */
.pi-jump {
  position: absolute;
  right: var(--sp-space-4);
  bottom: var(--sp-space-3);
  padding: var(--sp-space-1) var(--sp-space-3);
  background: var(--sp-color-terminal-header);
  border: 1px solid var(--pi-rim);
  border-radius: var(--sp-radius-full);
  color: var(--sp-color-terminal-text);
  font-family: var(--sp-font-mono);
  font-size: 0.72rem;
  cursor: pointer;
  box-shadow: var(--sp-shadow-lift);
  z-index: 2;
}

/* ── working ────────────────────────────────────────────────────────────── */

/* What replaced the blinking block cursor that used to be appended to the
   transcript. That caret implied the transcript was an input; this says the
   true thing instead — a block is being written, and this much has landed. */
.pi-working {
  display: flex;
  align-items: center;
  gap: var(--sp-space-2);
  padding: var(--sp-space-1) 0;
  font-family: var(--sp-font-mono);
  font-size: 0.78rem;
  color: var(--sp-color-terminal-text-dim);
}

.pi-working-dot {
  width: 7px;
  height: 7px;
  flex: none;
  border-radius: var(--sp-radius-full);
  background: var(--sp-color-primary);
}

.pi-working-label {
  letter-spacing: 0.04em;
}

.pi-working-count {
  font-variant-numeric: tabular-nums;
  opacity: 0.7;
}


/* ── rendered prose ─────────────────────────────────────────────────────── */

/* The agent's answers keep the terminal's mono voice, tuned for reading
   rather than for output: a step up in size, looser leading, a tighter
   measure, and the full-strength ink — the transcript's dimmer greys are for
   chrome, not for the thing the visitor came to read. */
.pi-prose {
  color: var(--sp-color-terminal-text);
  max-width: 88ch;
  font-size: 0.95rem;
  line-height: 1.8;
  margin-block: var(--sp-space-3) var(--sp-space-4);
}

.pi-prose > :first-child { margin-top: 0; }
.pi-prose > :last-child { margin-bottom: 0; }

.pi-prose p {
  margin: 0 0 var(--sp-space-4);
}

.pi-heading {
  margin: var(--sp-space-4) 0 var(--sp-space-2);
  font-family: var(--sp-font-mono);
  font-size: 1rem;
  font-weight: var(--sp-font-semibold);
  color: var(--sp-color-terminal-text);
}

.pi-list {
  margin: 0 0 var(--sp-space-3);
  padding-left: var(--sp-space-5);
}

.pi-list li {
  margin-block: 0.3rem;
}

.pi-list li {
  margin-bottom: 0.15rem;
}

.pi-code {
  font-family: var(--sp-font-mono);
  padding: 0.05rem 0.3rem;
  background: color-mix(in oklch, var(--sp-color-terminal-header) 80%, var(--sp-color-primary) 8%);
  border-radius: var(--sp-radius-xs);
  font-size: 0.9em;
}

.pi-strong { color: var(--sp-color-terminal-text); font-weight: 650; }
.pi-em { font-style: italic; }

.pi-prose a {
  color: var(--sp-color-primary);
  text-underline-offset: 2px;
}

.pi-codeblock {
  margin: var(--sp-space-2) 0;
  background: var(--sp-color-terminal-header);
  border: 1px solid var(--sp-color-terminal-border);
  border-radius: var(--sp-corners-inner-md);
  overflow: hidden;
}

.pi-codeblock-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.2rem var(--sp-space-3);
  border-bottom: 1px solid var(--sp-color-terminal-border);
}

.pi-codeblock-lang {
  font-size: 0.68rem;
  color: var(--sp-color-terminal-text-dim);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.pi-copy {
  background: none;
  border: none;
  color: var(--sp-color-terminal-text-dim);
  font-family: var(--sp-font-mono);
  font-size: 0.68rem;
  cursor: pointer;
}

.pi-copy:hover { color: var(--sp-color-primary); }

.pi-codeblock pre {
  margin: 0;
  padding: var(--sp-space-3);
  overflow-x: auto;
  font-family: var(--sp-font-mono);
  font-size: 0.8rem;
  line-height: 1.55;
}

/* Reuses the palette the blog's code blocks use, so a snippet here looks like a
   snippet there. */
.pi-tok-comment { color: var(--sp-syntax-comment); font-style: italic; }
.pi-tok-string { color: var(--sp-syntax-string); }
.pi-tok-keyword { color: var(--sp-syntax-keyword); }
.pi-tok-number { color: var(--sp-syntax-number); }

/* ── tables and rules ───────────────────────────────────────────────────── */

/* A table wider than the shell scrolls on its own axis. Widening the terminal
   instead would push the transcript sideways and break the column the whole
   page is built on. Focusable, because a scrollable region that only a mouse
   can reach is not reachable. */
.pi-table-wrap {
  margin: var(--sp-space-2) 0;
  overflow-x: auto;
  scrollbar-width: thin;
  border: 1px solid var(--sp-color-terminal-border);
  border-radius: var(--sp-corners-inner-md);
}

.pi-table-wrap:focus-visible {
  outline: 1px solid var(--pi-rim);
  outline-offset: -1px;
}

.pi-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--sp-font-mono);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.pi-table th,
.pi-table td {
  padding: 0.3rem var(--sp-space-3);
  text-align: left;
  vertical-align: top;
  white-space: nowrap;
}

.pi-table thead th {
  background: var(--sp-color-terminal-header);
  border-bottom: 1px solid var(--sp-color-terminal-border);
  color: var(--sp-color-terminal-text);
  font-weight: 600;
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* Row rules rather than a full grid: the eye only needs help crossing a row. */
.pi-table tbody tr + tr td {
  border-top: 1px solid color-mix(in oklch, var(--sp-color-terminal-border) 55%, transparent);
}

.pi-table tbody td {
  color: var(--sp-color-terminal-text-dim);
}

.pi-table tbody td:first-child {
  color: var(--sp-color-terminal-text);
}

.pi-hr {
  height: 0;
  margin: var(--sp-space-3) 0;
  border: 0;
  border-top: 1px solid var(--sp-color-terminal-border);
}

/* ── thinking ───────────────────────────────────────────────────────────── */

.pi-think {
  margin: var(--sp-space-1) 0;
  font-size: 0.8rem;
  color: var(--sp-color-terminal-text-dim);
}

.pi-think > summary {
  display: flex;
  align-items: baseline;
  gap: var(--sp-space-2);
  cursor: pointer;
  list-style: none;
  opacity: 0.8;
}

.pi-think > summary::before {
  content: '▸';
  font-size: 0.7em;
}

.pi-think[open] > summary::before {
  content: '▾';
}

.pi-think-count {
  font-size: 0.68rem;
  opacity: 0.7;
  font-variant-numeric: tabular-nums;
}

.pi-think-body {
  margin: var(--sp-space-1) 0 var(--sp-space-2) var(--sp-space-4);
  padding-left: var(--sp-space-3);
  border-left: 1px solid var(--sp-color-terminal-border);
  white-space: pre-wrap;
  opacity: 0.85;
}


/* ── the governance chain ───────────────────────────────────────────────── */

/* Deliberately quiet at rest. The rail is provenance for the transcript, not
   the exhibit itself — it sharpens under the pointer, when it is being read. */
.pi-rail-line {
  margin: var(--sp-space-2) 0;
  opacity: 0.75;
  transition: opacity 150ms ease;
}

.pi-rail-line:hover,
.pi-rail-line:focus-within {
  opacity: 1;
}

.pi-rail {
  display: flex;
  align-items: center;
  gap: 0;
  flex-wrap: wrap;
}

/* The per-check cost, stated but not shouted. */
.pi-pip-ms {
  font-size: 0.6rem;
  opacity: 0.55;
  font-variant-numeric: tabular-nums;
}

/* The way into the audit trail for this exact decision. */
.pi-rail-audit {
  margin-left: auto;
  padding-left: var(--sp-space-3);
  font-family: var(--sp-font-mono);
  font-size: 0.65rem;
  color: var(--sp-color-terminal-text-dim);
  text-decoration: none;
  white-space: nowrap;
}

.pi-rail-audit:hover,
.pi-rail-audit:focus-visible {
  color: var(--sp-color-primary);
  text-decoration: underline;
}

/* One pip per policy, connected by the segment before it, so the row reads as a
   pipeline rather than as a set of badges. */
.pi-pip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.1rem 0.6rem 0.1rem 0.35rem;
  font-family: var(--sp-font-mono);
  font-size: 0.65rem;
  color: var(--sp-color-terminal-text-dim);
  cursor: default;
}

/* The connector meets the dot's centre, not the top of the pip's box. It was
   drawn against the flex line box before, so on a wrapped rail it joined the
   dots at a visibly different height on each row. */
.pi-pip + .pi-pip::before {
  content: '';
  position: absolute;
  left: -0.35rem;
  top: 50%;
  width: 0.7rem;
  height: 1px;
  background: var(--pi-rail-track);
}

.pi-pip-dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--pi-pip);
  height: var(--pi-pip);
  border-radius: var(--sp-radius-full);
  border: 1px solid var(--pi-rail-track);
  font-size: 0.55rem;
  line-height: 1;
  flex: none;
  /* Sits above the connector so the line never crosses the glyph. */
  position: relative;
  z-index: 1;
  transition: box-shadow 200ms ease;
}

/* A pass is tinted, not lit. Passing is the expected outcome on every call, so
   it must not out-shout the transcript below; only a failure gets weight. */
.pi-pip[data-result="pass"] .pi-pip-dot {
  border-color: color-mix(in oklch, var(--pi-pass) 70%, transparent);
  background: color-mix(in oklch, var(--pi-pass) 10%, transparent);
  color: var(--pi-pass);
}

/* A failure is filled, not outlined. It is the one verdict that must be
   findable in peripheral vision, and weight carries further than hue. */
.pi-pip[data-result="fail"] .pi-pip-dot {
  border-color: var(--pi-fail);
  background: var(--pi-fail);
  color: var(--sp-color-terminal-bg);
  font-weight: 700;
  box-shadow: 0 0 0 3px color-mix(in oklch, var(--pi-fail) 22%, transparent);
}

.pi-pip[data-result="fail"] .pi-pip-name {
  color: var(--pi-fail);
  font-weight: 600;
}

/* The load-bearing state. A policy that never ran is dimmed, not ticked — so a
   visitor watching a blocked call can see exactly where the chain stopped. */
.pi-pip[data-result="skip"] {
  opacity: 0.4;
}

.pi-pip[data-result="skip"] .pi-pip-dot {
  border-style: dashed;
}

/* The compact rail, drawn inside the row whose call it judged.
   Attached to its subject, it no longer has to name it: the dots carry the
   verdicts and the names arrive on hover or keyboard focus. */
.pi-rail--compact {
  --pi-pip: 0.75rem;
  margin-left: auto;
  flex: none;
  flex-wrap: nowrap;
}

/* Collapsed pips have no room for timings; the tooltip still carries them. */
.pi-rail--compact .pi-pip-ms {
  display: none;
}

.pi-rail--compact .pi-pip {
  padding: 0 0.3rem 0 0.2rem;
}

.pi-rail--compact .pi-pip-name {
  /* Collapsed, not removed — the screen-reader text is a separate node and is
     unaffected, so the spoken rail stays complete either way. */
  max-width: 0;
  overflow: hidden;
  white-space: nowrap;
  opacity: 0;
  transition: max-width 200ms ease, opacity 200ms ease, margin 200ms ease;
}

.pi-tool:hover .pi-rail--compact .pi-pip-name,
.pi-tool:focus-within .pi-rail--compact .pi-pip-name,
.pi-rail--compact .pi-pip:hover .pi-pip-name {
  max-width: 10rem;
  margin-left: 0.35rem;
  opacity: 1;
}

/* A denied call colours its whole row, so the chain and its subject agree. */
.pi-tool.is-denied > summary .pi-tool-name {
  color: var(--pi-fail);
}

/* ── blocked ────────────────────────────────────────────────────────────── */

.pi-blocked {
  position: relative;
  margin: var(--sp-space-2) 0;
  max-width: 46rem;
  padding: var(--sp-space-2) var(--sp-space-3);
  background: color-mix(in oklch, var(--sp-color-error) 8%, var(--sp-color-terminal-header));
  /* The verdict carries the colour; the frame does not have to shout it. A
     bright outline against the dark stage read as an alarm banner — the left
     accent alone marks the row, and the outer edge stays quiet. */
  border: 1px solid color-mix(in oklch, var(--sp-color-error) 28%, transparent);
  border-left: 3px solid color-mix(in oklch, var(--sp-color-error) 75%, transparent);
  border-radius: var(--sp-corners-inner-md);
  font-size: 0.8rem;
  overflow: hidden;
}

/* A slow light travelling the left accent — the one edge that carries the
   verdict is the one that is alive. Decorative: motion-gated below. */
.pi-blocked::before {
  content: '';
  position: absolute;
  /* Just inside the accent — the shell's overflow:hidden clips anything drawn
     over the border itself. */
  left: 0;
  top: 0;
  bottom: 0;
  width: 2px;
  background: linear-gradient(180deg, transparent 20%, var(--sp-color-error) 50%, transparent 80%);
  background-size: 100% 300%;
  opacity: 0;
  pointer-events: none;
}

.pi-blocked-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-space-2);
  color: var(--sp-color-terminal-text);
}

/* Facts about the evaluation, right-aligned in the head row. */
.pi-blocked-meta {
  margin-left: auto;
}

.pi-blocked-mark {
  color: var(--sp-color-error);
  font-weight: 700;
}

.pi-policy-chip {
  padding: 0.05rem 0.45rem;
  background: color-mix(in oklch, var(--sp-color-error) 20%, transparent);
  border-radius: var(--sp-radius-full);
  color: var(--sp-color-error);
  font-family: var(--sp-font-mono);
  font-size: 0.68rem;
}

.pi-blocked-reason {
  margin: var(--sp-space-1) 0 0;
  max-width: 60ch;
  color: var(--sp-color-terminal-text);
  font-family: var(--sp-font-mono);
  font-size: 0.72rem;
}

.pi-blocked-note {
  margin: var(--sp-space-1) 0 0;
  color: var(--sp-color-terminal-text-dim);
  font-size: 0.68rem;
}


/* ── approval queue ─────────────────────────────────────────────────────── */

/* A queue, not a modal: parallel tool calls each get their own card and the
   server resolves them independently. */
.pi-approvals:not(:empty) {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-2);
  padding: var(--sp-space-3) var(--sp-space-4);
  border-top: 1px solid var(--sp-color-terminal-border);
}

/* Registered so the sweep angle interpolates. Harmless where @property is
   unsupported — the animated border simply never engages and the static soft
   border below stands. */
@property --pi-sweep {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

.pi-approval-card {
  /* Smaller ring: the card is an instrument in a queue, not a dialog. */
  --pi-ring-size: 1.75rem;

  padding: var(--sp-space-2) var(--sp-space-3);
  background: linear-gradient(
    180deg,
    color-mix(in oklch, var(--sp-color-warning) 9%, var(--sp-color-terminal-header)),
    var(--sp-color-terminal-header)
  );
  /* Quiet by default — the amber wash and the draining ring already say
     "pending"; a bright outline was the frame shouting over its contents. */
  border: 1px solid color-mix(in oklch, var(--sp-color-warning) 30%, transparent);
  border-radius: var(--sp-corners-inner-md);
  box-shadow: var(--sp-shadow-lift);
  font-family: var(--sp-font-mono);
  font-size: 0.8rem;
  color: var(--sp-color-terminal-text);
}

/* Args beside the cleared chain: the two facts the decision rests on, side by
   side rather than stacked into a banner. */
.pi-approval-grid {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: start;
  gap: var(--sp-space-2) var(--sp-space-4);
  margin-top: var(--sp-space-2);
}

/* Flat arguments as a ledger — `TO board@acme.com` reads at a glance where a
   JSON blob has to be parsed by eye. Non-flat input keeps the exact <pre>. */
.pi-approval-kv {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.15rem var(--sp-space-3);
  margin: 0;
  padding: var(--sp-space-2);
  background: color-mix(in oklch, var(--sp-color-terminal-bg) 70%, transparent);
  border-radius: var(--sp-radius-sm);
}

.pi-approval-kv dt {
  color: var(--sp-color-terminal-text-dim);
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  line-height: 1.9;
}

.pi-approval-kv dd {
  margin: 0;
  color: var(--sp-color-terminal-text);
  font-size: 0.75rem;
  line-height: 1.6;
  word-break: break-word;
}

.pi-approval-card--canned {
  opacity: 0.62;
}

/* A call the gate cleared on its own: same facts as the human card, but green
   instead of amber, quieter, and with nothing to click. */
.pi-approval-card--auto {
  padding: var(--sp-space-1) var(--sp-space-3) var(--sp-space-2);
  background: linear-gradient(
    180deg,
    color-mix(in oklch, var(--sp-color-success) 7%, var(--sp-color-terminal-header)),
    var(--sp-color-terminal-header)
  );
  border-color: color-mix(in oklch, var(--sp-color-success) 25%, transparent);
}

.pi-auto-mark {
  flex: none;
  color: var(--sp-color-success);
  font-size: 0.85rem;
}

.pi-approval-card--auto .pi-detail-chip:last-child {
  border-color: color-mix(in oklch, var(--sp-color-success) 45%, transparent);
  color: var(--sp-color-success);
}

/* A tool with no parameters, stated as a fact instead of an empty blob. */
.pi-approval-noargs {
  margin: 0;
  padding: var(--sp-space-2);
  background: color-mix(in oklch, var(--sp-color-terminal-bg) 70%, transparent);
  border-radius: var(--sp-radius-sm);
  color: var(--sp-color-terminal-text-dim);
  font-size: 0.7rem;
  font-style: italic;
}

.pi-approval-card.is-settling {
  opacity: 0.5;
}

.pi-approval-head {
  display: flex;
  align-items: center;
  gap: var(--sp-space-3);
}

.pi-approval-title {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
}

.pi-approval-sub {
  color: var(--sp-color-terminal-text-dim);
  font-size: 0.7rem;
}

/* The countdown as a ring around the glyph: a shape draining is legible at a
   glance in a way a number is not, and the number stays for the exact value. */
.pi-ring {
  position: relative;
  width: var(--pi-ring-size);
  height: var(--pi-ring-size);
  flex: none;
  border-radius: var(--sp-radius-full);
  background:
    conic-gradient(
      var(--sp-color-warning) calc(var(--pi-ring-fill) * 360deg),
      var(--pi-rail-track) 0
    );
  display: grid;
  place-items: center;
  transition: background 1s linear;
}

.pi-ring::after {
  content: '';
  position: absolute;
  inset: var(--pi-ring-width);
  border-radius: var(--sp-radius-full);
  background: var(--sp-color-terminal-header);
}

.pi-ring-glyph {
  position: relative;
  z-index: 1;
  font-size: 0.85rem;
  color: var(--sp-color-warning);
}

.pi-countdown {
  color: var(--sp-color-warning);
  font-variant-numeric: tabular-nums;
  flex: none;
}

.pi-countdown[data-urgency="warn"] { color: var(--sp-color-warning); }
.pi-countdown[data-urgency="critical"] { color: var(--sp-color-error); font-weight: 700; }

.pi-approval-input {
  margin: var(--sp-space-2) 0 0;
  padding: var(--sp-space-2);
  max-height: 120px;
  overflow: auto;
  background: var(--sp-color-terminal-bg);
  border-radius: var(--sp-radius-sm);
  color: var(--sp-color-terminal-text-dim);
  font-size: 0.75rem;
  white-space: pre-wrap;
  scrollbar-width: thin;
}

.pi-approval-grid .pi-approval-input {
  margin: 0;
}

/* What already cleared, not a bare "allow this?" — the operator should see the
   chain that passed before being asked. */
.pi-approval-cleared {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-1);
  align-items: flex-start;
}

/* The cleared rail is compact; hovering the block names the policies, the same
   contract the compact rail keeps inside a tool row. */
.pi-approval-cleared:hover .pi-rail--compact .pi-pip-name,
.pi-approval-cleared:focus-within .pi-rail--compact .pi-pip-name {
  max-width: 10rem;
  margin-left: 0.35rem;
  opacity: 1;
}

.pi-approval-cleared-label {
  display: block;
  color: var(--sp-color-success);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
}

/* Small true facts as pills — shared by the blocked callout and the approval
   card. Detail belongs in chrome like this, not padded into the prose. */
.pi-detail-row {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-space-1);
}

.pi-approval-card > .pi-detail-row {
  margin-top: var(--sp-space-2);
}

.pi-detail-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.05rem 0.4rem;
  border: 1px solid color-mix(in oklch, var(--sp-color-terminal-border) 70%, transparent);
  border-radius: var(--sp-radius-full);
  color: var(--sp-color-terminal-text-dim);
  font-family: var(--sp-font-mono);
  font-size: 0.62rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.pi-approval-actions {
  display: flex;
  gap: var(--sp-space-2);
  margin-top: var(--sp-space-3);
}


/* Who decided, and when. A human decision carries an initials avatar and a
   timestamp; a system decision (timeout, auto-approve) is muted and
   avatar-less so a machine's verdict never dresses up as a person's. */
.pi-attribution {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-space-1);
  margin-top: var(--sp-space-2);
  font-size: 0.72rem;
  color: var(--sp-color-terminal-text-dim);
}

.pi-attribution-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.35rem;
  height: 1.35rem;
  border-radius: var(--sp-radius-full);
  background: color-mix(in oklch, var(--sp-color-success) 25%, transparent);
  border: 1px solid color-mix(in oklch, var(--sp-color-success) 55%, transparent);
  color: var(--sp-color-terminal-text);
  font-family: var(--sp-font-mono);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.03em;
}

.pi-attribution-text strong {
  color: var(--sp-color-terminal-text);
  font-weight: 600;
}

.pi-attribution-at {
  font-family: var(--sp-font-mono);
  font-variant-numeric: tabular-nums;
}

.pi-attribution[data-actor='system'] {
  opacity: 0.75;
  font-style: italic;
}

/* A settled card is a record, not a question: the verdict edge replaces the
   countdown chrome the settle() pass already removed. Inside a folded record
   it drops its own frame — the record supplies one. */
.pi-approval-card.is-settled {
  border-left: 2px solid var(--sp-color-success);
}

.pi-approval-card.is-settled[data-outcome='denied'],
.pi-approval-card.is-settled[data-outcome='timeout'],
.pi-approval-card.is-settled[data-outcome='abandoned'] {
  border-left-color: var(--sp-color-warning);
}

/* ── settled approval records ───────────────────────────────────────────────
   The folded form of an answered approval. Lives beside the card CSS rather
   than inside it: the card is a question, this is the record it leaves. */

/* A run of answered approvals should read as a list of verdicts, not a wall of
   identical amber blocks. Collapsed, each is one line; the card is one click
   away, and only one is ever open at a time. */
.pi-approval-record {
  --pi-record-edge: var(--sp-color-success);

  margin: var(--sp-space-1) 0;
  border-left: 2px solid var(--pi-record-edge);
  border-radius: var(--sp-radius-sm);
  background: color-mix(in oklch, var(--sp-color-terminal-header) 55%, transparent);
}

.pi-approval-record[data-outcome='denied'],
.pi-approval-record[data-outcome='timeout'],
.pi-approval-record[data-outcome='abandoned'],
.pi-approval-record[data-outcome='expired'] {
  --pi-record-edge: var(--sp-color-warning);
}

.pi-approval-record-head {
  display: flex;
  align-items: center;
  gap: var(--sp-space-2);
  padding: 0.3rem var(--sp-space-3);
  cursor: pointer;
  font-family: var(--sp-font-mono);
  font-size: 0.72rem;
  color: var(--sp-color-terminal-text-dim);
  list-style: none;
}

.pi-approval-record-head::-webkit-details-marker { display: none; }

.pi-approval-record-head:hover,
.pi-approval-record[open] > .pi-approval-record-head {
  background: color-mix(in oklch, var(--sp-color-terminal-header) 85%, transparent);
}

.pi-approval-record-mark {
  flex: none;
  color: var(--pi-record-edge);
}

.pi-approval-record-tool {
  color: var(--sp-color-terminal-text);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The stamp is the summary's own by-line here, so it loses the block spacing
   it carries when it sits at the foot of an open card. */
.pi-approval-record-head .pi-attribution {
  margin-top: 0;
  margin-left: auto;
  font-size: 0.68rem;
}

.pi-approval-record-head .pi-attribution-avatar {
  width: 1.1rem;
  height: 1.1rem;
  font-size: 0.52rem;
}

/* The affordance: a chevron that turns when the record opens. */
.pi-approval-record-more::before {
  content: '▸';
  display: inline-block;
  color: var(--sp-color-terminal-text-dim);
  transition: transform 0.15s ease;
}

.pi-approval-record[open] .pi-approval-record-more::before {
  transform: rotate(90deg);
}

/* Open, the card sits inside the record's frame rather than carrying its own. */
.pi-approval-record > .pi-approval-card.is-settled {
  border-left: none;
  border-radius: 0 0 var(--sp-corners-inner-md) var(--sp-corners-inner-md);
  box-shadow: none;
}

@media (prefers-reduced-motion: reduce) {
  .pi-approval-record-more::before { transition: none; }
}

/* ── aggregated runs ────────────────────────────────────────────────────────
   A chain of tool calls with nothing between them collapses to one card per
   verdict. The tally is the glance version; the records are inside it. */
.pi-gate-group {
  --pi-record-edge: var(--sp-color-success);

  margin: var(--sp-space-1) 0;
  border: 1px solid color-mix(in oklch, var(--pi-record-edge) 22%, transparent);
  border-left-width: 2px;
  border-left-color: var(--pi-record-edge);
  border-radius: var(--sp-radius-sm);
  background: color-mix(in oklch, var(--sp-color-terminal-header) 55%, transparent);
}

/* Blocked calls never sit in the same tally as calls that got through. */
.pi-gate-group[data-kind='blocked'] {
  --pi-record-edge: var(--sp-color-error);
}

.pi-gate-group-head {
  display: flex;
  align-items: center;
  gap: var(--sp-space-2);
  padding: 0.35rem var(--sp-space-3);
  cursor: pointer;
  font-family: var(--sp-font-mono);
  font-size: 0.72rem;
  color: var(--sp-color-terminal-text-dim);
  list-style: none;
}

.pi-gate-group-head::-webkit-details-marker { display: none; }

.pi-gate-group-head:hover,
.pi-gate-group[open] > .pi-gate-group-head {
  background: color-mix(in oklch, var(--sp-color-terminal-header) 85%, transparent);
}

.pi-gate-group-mark {
  flex: none;
  color: var(--pi-record-edge);
}

.pi-gate-group-count {
  color: var(--sp-color-terminal-text);
  font-weight: 600;
}

/* Which tools, and how many of each — the one fact a tally of calls is missing. */
.pi-gate-group-tools {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pi-gate-group-more::before {
  content: '▸';
  display: inline-block;
  color: var(--sp-color-terminal-text-dim);
  transition: transform 0.15s ease;
}

.pi-gate-group-more {
  margin-left: auto;
}

.pi-gate-group[open] .pi-gate-group-more::before {
  transform: rotate(90deg);
}

.pi-gate-group-body {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-1);
  padding: var(--sp-space-2);
  border-top: 1px solid color-mix(in oklch, var(--pi-record-edge) 18%, transparent);
}

/* Inside a tally the members drop their own outer margin — the group spaces them. */
.pi-gate-group-body > .pi-approval-record,
.pi-gate-group-body > .pi-blocked {
  margin: 0;
}

@media (prefers-reduced-motion: reduce) {
  .pi-gate-group-more::before { transition: none; }
}

/* ── composer ───────────────────────────────────────────────────────────── */

/* Slash-command palette.
   Discovery only: a leading `/` works whether or not this is open, so it never
   sits between the viewer and the input. It shares the composer's top border
   rather than floating, because a floating list over a streaming terminal
   fights the output for attention. */
.pi-palette {
  display: flex;
  flex-direction: column;
  max-height: 11rem;
  overflow-y: auto;
  border-top: 1px solid var(--sp-color-terminal-border);
  scrollbar-width: thin;
}

.pi-palette-row {
  display: flex;
  align-items: baseline;
  gap: var(--sp-space-3);
  width: 100%;
  padding: var(--sp-space-2) var(--sp-space-4);
  background: transparent;
  border: none;
  text-align: left;
  cursor: pointer;
  font-family: var(--sp-font-mono);
  font-size: 0.8rem;
}

/* Selection is one state, whether a mouse or an arrow key put it there. The
   list used to answer only to the mouse; `.is-selected` is what the keyboard
   moves, and hover writes the same class so the two can never look different. */
.pi-palette-row:hover,
.pi-palette-row:focus-visible,
.pi-palette-row.is-selected {
  background: color-mix(in oklch, var(--sp-color-primary) 16%, var(--sp-color-terminal-header));
  outline: none;
}

.pi-palette-row.is-selected {
  box-shadow: inset 2px 0 0 var(--sp-color-primary);
}

.pi-palette-row.is-selected .pi-palette-cmd {
  color: var(--sp-color-primary);
}

.pi-palette-cmd {
  flex: none;
  color: var(--sp-color-terminal-text);
}

.pi-palette-desc {
  flex: 1;
  min-width: 0;
  color: var(--sp-color-terminal-text-dim);
  /* One line: these descriptions are a sentence long, and wrapping them turns
     a four-row list into a wall. The full text is in the skill itself. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The composer is the only caret on the surface.
   A blinking block used to be appended to the transcript, which claimed the
   transcript took input and left the actual input unmarked. Focus is stated
   here instead, on the thing that really does accept typing. */
.pi-composer {
  /* The glyph and the input are typographic siblings, so their metrics come
     from one place. The old bar set them separately and squared the difference
     up with a padding fudge on the glyph, which only held at one line height
     and drifted the moment the textarea grew. */
  --pi-composer-size: 0.85rem;
  --pi-composer-leading: 1.45;

  position: relative;
  display: flex;
  /* flex-start, not flex-end: a shell prompt marks the line a command starts
     on. Pinned to the bottom it slid down the side of a growing textarea. */
  align-items: flex-start;
  gap: var(--sp-space-3);
  padding: var(--sp-space-4);
  border-top: 1px solid var(--sp-color-terminal-border);
  transition:
    border-color var(--sp-duration-normal) ease,
    background var(--sp-duration-normal) ease,
    box-shadow var(--sp-duration-normal) ease;
}

/* Lit from the seam rather than outlined. A border colour swap states focus;
   the glow makes the bar read as the live edge of the instrument. */
.pi-composer:focus-within {
  border-top-color: var(--pi-rim);
  background: color-mix(in oklch, var(--sp-color-primary) 5%, transparent);
  box-shadow: 0 -8px 24px -12px color-mix(in oklch, var(--sp-color-primary) 55%, transparent);
}

.pi-composer .prompt {
  flex: none;
  width: 1ch;
  font-size: var(--pi-composer-size);
  line-height: var(--pi-composer-leading);
  color: var(--sp-color-terminal-text-dim);
  transition: color var(--sp-duration-normal) ease;
}

.pi-composer .prompt svg {
  display: block;
  width: 1em;
  height: calc(var(--pi-composer-size) * var(--pi-composer-leading));
  overflow: visible;
}

.pi-composer:focus-within .prompt {
  color: var(--sp-color-primary);
}

/* While the skill list is open the glyph names what the input is doing. The
   palette sits immediately before the composer, so no JS hook is needed. */
.pi-palette:not([hidden]) + .pi-composer .prompt {
  color: var(--sp-color-primary);
}

.pi-input {
  flex: 1;
  min-width: 0;
  padding: 0;
  background: transparent;
  border: none;
  outline: none;
  resize: none;
  overflow-y: auto;
  color: var(--sp-color-terminal-text);
  font-family: var(--sp-font-mono);
  font-size: var(--pi-composer-size);
  line-height: var(--pi-composer-leading);
  scrollbar-width: thin;
}

.pi-input::placeholder {
  color: var(--sp-color-terminal-text-dim);
}

.pi-input:disabled {
  color: var(--sp-color-text-disabled);
}

/* A turn in flight, stated on the seam the composer already owns rather than
   in a spinner that would compete with the transcript for attention. */
.pi-composer[data-busy]::before {
  content: '';
  position: absolute;
  inset: -1px 0 auto;
  height: 2px;
  width: 35%;
  background: linear-gradient(90deg, transparent, var(--sp-color-primary), transparent);
  animation: pi-composer-scan 1.6s ease-in-out infinite;
  pointer-events: none;
}

@keyframes pi-composer-scan {
  from { transform: translateX(-100%); }
  to { transform: translateX(285%); }
}

.pi-hint {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-space-1) var(--sp-space-3);
  padding: 0 var(--sp-space-4) var(--sp-space-3);
  color: var(--sp-color-terminal-text-dim);
  font-family: var(--sp-font-mono);
  font-size: 0.66rem;
}

/* Both set display, so a JS `hidden = true` needs the attribute stated. */
.pi-composer[hidden],
.pi-hint[hidden] {
  display: none;
}

.pi-hint__item {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-space-1);
  opacity: 0.75;
}

/* The keyboard contract is UI, not a footnote — so the keys are drawn as keys
   instead of run together in a dot-separated sentence. */
.pi-hint kbd {
  padding: 0.1rem 0.3rem;
  border: 1px solid var(--sp-color-terminal-border);
  border-radius: var(--sp-radius-sm);
  background: color-mix(in oklch, var(--sp-color-terminal-header) 70%, transparent);
  color: var(--sp-color-terminal-text);
  font-family: inherit;
  font-size: inherit;
  line-height: 1.3;
}

/* The hint is for someone with a keyboard; on a touch device it is noise. */
@media (hover: none) {
  .pi-hint { display: none; }
}

.pi-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-space-1);
  /* Fitts's Law floor for a primary action, held even in dense console chrome. */
  min-height: 2.25rem;
  min-width: 2.75rem;
  padding: var(--sp-space-2) var(--sp-space-4);
  background: var(--sp-color-primary);
  border: 1px solid transparent;
  border-radius: var(--sp-corners-inner-sm);
  color: var(--sp-color-text-on-primary);
  font-family: var(--sp-font-body);
  font-size: 0.8rem;
  text-decoration: none;
  cursor: pointer;
  flex: none;
  transition:
    background var(--sp-transition-fast),
    box-shadow var(--sp-transition-fast),
    filter var(--sp-transition-fast),
    transform var(--sp-duration-fast) ease-out;
}

.pi-btn__icon {
  width: 0.75rem;
  height: 0.75rem;
  flex: none;
}

/* brightness rather than a per-variant hover colour: every variant below sets
   its own background, and one rule keeps them all consistent. */
.pi-btn:hover {
  filter: brightness(1.08);
}

.pi-btn:active {
  transform: scale(0.97);
}

.pi-btn:focus-visible {
  outline: 2px solid var(--sp-color-primary);
  outline-offset: 2px;
}

.pi-btn:disabled,
.pi-btn--disabled {
  opacity: 0.45;
  cursor: default;
  filter: none;
  transform: none;
}

/* Run. Gradient and inset highlight rather than a flat fill — against a dark
   console a solid rectangle reads as a swatch, not as a pressable control. */
.pi-btn--send {
  background: linear-gradient(
    180deg,
    color-mix(in oklch, var(--sp-color-primary), white 10%),
    var(--sp-color-primary)
  );
  box-shadow:
    inset 0 1px 0 color-mix(in oklch, white 30%, transparent),
    0 1px 2px oklch(0 0 0 / 0.35);
}

.pi-btn--send:hover {
  box-shadow:
    inset 0 1px 0 color-mix(in oklch, white 34%, transparent),
    0 4px 10px -2px color-mix(in oklch, var(--sp-color-primary) 45%, transparent);
}

/* Nothing to run yet. Stated by draining the colour out of the button rather
   than by fading it, so it reads as "not yet" instead of "broken". */
.pi-btn--send:disabled,
.pi-composer:has(.pi-input:placeholder-shown)
  .pi-btn--send:not([data-mode="reconnect"]) {
  background: color-mix(in oklch, var(--sp-color-primary) 10%, var(--sp-color-terminal-bg));
  border-color: var(--sp-color-terminal-border);
  color: var(--sp-color-terminal-text-dim);
  box-shadow: none;
  opacity: 1;
  cursor: default;
  filter: none;
  transform: none;
}

/* Ended session. The composer is dead but the commit button is not: it becomes
   the way back, so it keeps full emphasis while the dead textarea beside it
   greys out. Only one of the two glyphs is ever in the flow. */
.pi-btn__icon--reconnect,
.pi-btn--send[data-mode="reconnect"] .pi-btn__icon--send {
  display: none;
}

.pi-btn--send[data-mode="reconnect"] .pi-btn__icon--reconnect {
  display: block;
}

/* Mid-turn Run steers the running turn — a real action, but not the emphasised
   one while Stop is on the bar. */
.pi-composer[data-busy] .pi-btn--send {
  background: transparent;
  border-color: var(--sp-color-terminal-border);
  color: var(--sp-color-terminal-text-dim);
  box-shadow: none;
}

.pi-composer[data-busy] .pi-btn--stop {
  border-color: color-mix(in oklch, var(--sp-color-primary) 45%, transparent);
  color: var(--sp-color-primary);
}

/* The one loud button outside a live session: register. Same weight as the
   auth pane's primary, built from the shared accent token rather than that
   pane's classes — the two surfaces should agree on emphasis without one
   importing the other's stylesheet. */
.pi-btn--cta {
  background: linear-gradient(
    180deg,
    color-mix(in oklch, var(--sp-color-primary), white 12%),
    var(--sp-color-primary)
  );
  color: var(--sp-color-text-on-primary);
  font-weight: 600;
  box-shadow:
    inset 0 1px 0 color-mix(in oklch, white 30%, transparent),
    0 1px 2px oklch(0 0 0 / 0.35);
}

.pi-btn--cta:hover {
  box-shadow:
    inset 0 1px 0 color-mix(in oklch, white 34%, transparent),
    0 4px 10px -2px color-mix(in oklch, var(--sp-color-primary) 45%, transparent);
}

.pi-btn--ghost {
  background: transparent;
  border-color: var(--sp-color-terminal-border);
  color: var(--sp-color-terminal-text-dim);
}

/* Motion is decoration here; the colour and shadow states carry every meaning
   the animation does, so they stay for everyone. */
@media (prefers-reduced-motion: reduce) {
  .pi-btn:active { transform: none; }

  .pi-composer[data-busy]::before {
    width: 100%;
    animation: pi-composer-pulse 2s ease-in-out infinite;
  }

  @keyframes pi-composer-pulse {
    0%, 100% { opacity: 0.25; }
    50% { opacity: 1; }
  }
}

.pi-btn--danger {
  background: var(--sp-color-error);
}

/* Approve is not the emphasised choice. Three of the four ways an approval can
   end are denials, and a card that pushes toward allow would be arguing against
   the thing this page exists to demonstrate. */
.pi-btn--allow {
  background: transparent;
  border-color: var(--sp-color-success);
  color: var(--sp-color-success);
}

.pi-btn--allow:hover {
  background: color-mix(in oklch, var(--sp-color-success) 16%, transparent);
  opacity: 1;
}

.pi-btn--deny {
  background: var(--sp-color-error);
  color: var(--sp-primitive-white);
}

/* Standing approval is the heaviest commitment on the card — it answers calls
   that have not been made yet — so it is the quietest control, not a second
   Approve. */
.pi-btn--always {
  background: transparent;
  border-color: var(--sp-color-terminal-border);
  color: var(--sp-color-terminal-text-dim);
}

.pi-btn--always:hover {
  border-color: color-mix(in oklch, var(--sp-color-success) 45%, transparent);
  color: var(--sp-color-terminal-text);
}


/* ── tool rows ──────────────────────────────────────────────────────────── */

.pi-tool {
  margin: var(--sp-space-2) 0;
  font-family: var(--sp-font-mono);
}

.pi-tool > summary {
  display: flex;
  align-items: baseline;
  gap: var(--sp-space-2);
  cursor: pointer;
  list-style: none;
  color: var(--sp-color-accent-cyan, var(--sp-color-info));
}

.pi-tool > summary::-webkit-details-marker {
  display: none;
}

.pi-tool-icon {
  flex: none;
  width: 1ch;
}

.pi-tool-name {
  color: var(--sp-color-terminal-text);
}

.pi-tool-arg {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--sp-color-terminal-text-dim);
}

.pi-tool-state {
  margin-left: auto;
  flex: none;
  font-size: 0.68rem;
  color: var(--sp-color-terminal-text-dim);
  opacity: 0.8;
}

.pi-tool[data-state="ok"] > summary { color: var(--sp-color-success); }
.pi-tool[data-state="failed"] > summary,
.pi-tool[data-state="blocked"] > summary { color: var(--sp-color-error); }

.pi-tool-body {
  margin: var(--sp-space-1) 0 var(--sp-space-2) var(--sp-space-4);
  padding: var(--sp-space-2) var(--sp-space-3);
  overflow-x: auto;
  background: var(--sp-color-terminal-header);
  border-left: 1px solid var(--sp-color-terminal-border);
  border-radius: var(--sp-radius-sm);
  color: var(--sp-color-terminal-text-dim);
  font-size: 0.72rem;
  white-space: pre-wrap;
  scrollbar-width: thin;
}

/* Kept: the flat single-line tool row, still used when the gate view has not
   loaded. */
.pi-tool-row {
  color: var(--sp-color-accent-cyan, var(--sp-color-info));
}

.pi-tool-row--blocked {
  color: var(--sp-color-error);
}

/* ── degraded footer ────────────────────────────────────────────────────── */

.pi-gate {
  padding: var(--sp-space-4);
  border-top: 1px solid var(--sp-color-terminal-border);
  background: var(--sp-color-terminal-header);
  color: var(--sp-color-terminal-text-dim);
  font-size: 0.85rem;
}

.pi-gate p {
  margin: 0 0 var(--sp-space-2);
}

.pi-gate-actions {
  display: flex;
  gap: var(--sp-space-2);
  margin: var(--sp-space-3) 0 0;
}

/* ── replay footer ──────────────────────────────────────────────────────── */

/* What stands where the composer would be while the terminal is a recording.
   It is deliberately heavier than the gate blurb below it: the blurb explains
   the state, this is the way out of it. */
.pi-replay-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-space-2) var(--sp-space-3);
  padding: var(--sp-space-3) var(--sp-space-4);
  border-top: 1px solid color-mix(in oklch, var(--sp-color-primary) 35%, transparent);
  background: color-mix(in oklch, var(--sp-color-primary) 10%, var(--sp-color-terminal-header));
}

.pi-replay-bar[hidden] {
  display: none;
}

.pi-replay-bar__lock {
  width: 0.9rem;
  height: 0.9rem;
  flex: none;
  color: var(--sp-color-primary);
}

.pi-replay-bar__copy {
  flex: 1 1 14rem;
  min-width: 0;
  margin: 0;
  color: var(--sp-color-terminal-text);
  font-size: 0.85rem;
}

/* The actions sit on the same row as the copy when there is width for them,
   so this loses the block margin the gate version carries. */
.pi-replay-bar .pi-gate-actions {
  margin: 0;
  margin-left: auto;
}

.pi-auth {
  margin-top: var(--sp-space-4);
  max-width: 380px;
}

.pi-auth-tabs {
  display: flex;
  gap: var(--sp-space-1);
  border-bottom: 1px solid var(--sp-color-terminal-border);
}

.pi-tab {
  padding: var(--sp-space-2) var(--sp-space-3);
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--sp-color-terminal-text-dim);
  font-family: var(--sp-font-body);
  font-size: 0.85rem;
  cursor: pointer;
}

.pi-tab.is-active {
  color: var(--sp-color-terminal-text);
  border-bottom-color: var(--sp-color-primary);
}

.pi-auth-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-2);
  padding-top: var(--sp-space-3);
}

.pi-label {
  font-size: 0.78rem;
  color: var(--sp-color-terminal-text-dim);
}

.pi-field {
  padding: var(--sp-space-2) var(--sp-space-3);
  background: var(--sp-color-terminal-bg);
  border: 1px solid var(--sp-color-terminal-border);
  border-radius: var(--sp-corners-inner-sm);
  color: var(--sp-color-terminal-text);
  font-family: var(--sp-font-body);
  font-size: 0.9rem;
}

.pi-field:focus {
  outline: none;
  border-color: var(--sp-color-primary);
}

.pi-auth-note {
  margin: 0;
  font-size: 0.75rem;
  color: var(--sp-color-terminal-text-dim);
}


/* ── motion ─────────────────────────────────────────────────────────────── */

/* All of it opt-out. Every state above is legible from colour, glyph, and text
   alone, so switching motion off removes emphasis and never information. */
@media (prefers-reduced-motion: no-preference) {
  .pi-live[data-state="live"] .pi-live-dot {
    animation: pi-pulse 2.4s ease-in-out infinite;
  }

  /* Pips are revealed by JS removing .is-pending on a stagger, so the chain
     resolves left to right at reading speed. */
  .pi-pip {
    transition: opacity 220ms ease, transform 220ms ease;
  }

  .pi-pip.is-pending {
    opacity: 0;
    transform: translateY(2px);
  }

  .pi-approval-card {
    animation: pi-card-in 260ms cubic-bezier(0.16, 1, 0.3, 1);
  }

  /* A pending card wears a slow light circling its edge — pending, stated by
     the border itself. The amber gradient repaints over padding-box and the
     conic sweep owns the 1px border-box ring behind it. Browsers without
     @property never interpolate --pi-sweep and keep the static border above. */
  @supports (background: conic-gradient(from 1deg, red, red)) {
    .pi-approval-card {
      border-color: transparent;
      background:
        linear-gradient(
          180deg,
          color-mix(in oklch, var(--sp-color-warning) 9%, var(--sp-color-terminal-header)),
          var(--sp-color-terminal-header)
        ) padding-box,
        conic-gradient(
          from var(--pi-sweep),
          color-mix(in oklch, var(--sp-color-warning) 65%, transparent),
          color-mix(in oklch, var(--sp-color-warning) 14%, transparent) 30%,
          color-mix(in oklch, var(--sp-color-warning) 14%, transparent) 70%,
          color-mix(in oklch, var(--sp-color-warning) 65%, transparent)
        ) border-box;
      animation:
        pi-card-in 260ms cubic-bezier(0.16, 1, 0.3, 1),
        pi-border-sweep 6s linear infinite;
    }
  }

  /* The auto-approved variant is settled, not pending: it arrives, but the
     amber sweep that says "waiting on you" would be a lie on it. */
  .pi-approval-card--auto {
    border-color: color-mix(in oklch, var(--sp-color-success) 25%, transparent);
    background: linear-gradient(
      180deg,
      color-mix(in oklch, var(--sp-color-success) 7%, var(--sp-color-terminal-header)),
      var(--sp-color-terminal-header)
    );
    animation: pi-card-in 260ms cubic-bezier(0.16, 1, 0.3, 1);
  }

  .pi-blocked.is-arriving {
    animation: pi-block-in 300ms cubic-bezier(0.36, 0.07, 0.19, 0.97);
  }

  /* The blocked card's left accent carries a slow travelling light. */
  .pi-blocked::before {
    opacity: 1;
    animation: pi-edge-shimmer 4s ease-in-out infinite;
  }

  .pi-jump {
    animation: pi-card-in 180ms ease-out;
  }

  /* Prose arrives as a finished unit, so it gets an arrival. Same easing as the
     approval card, because both are "a thing resolved and is now here". */
  .pi-reveal {
    animation: pi-card-in 260ms cubic-bezier(0.16, 1, 0.3, 1);
  }

  .pi-working-dot {
    animation: pi-pulse-dot 1.4s ease-in-out infinite;
  }
}

@keyframes pi-pulse-dot {
  0%, 100% { opacity: 0.35; transform: scale(0.8); }
  50% { opacity: 1; transform: scale(1); }
}

@keyframes pi-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklch, var(--sp-color-success) 55%, transparent); }
  50% { box-shadow: 0 0 0 4px color-mix(in oklch, var(--sp-color-success) 0%, transparent); }
}

@keyframes pi-card-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}

@keyframes pi-border-sweep {
  to { --pi-sweep: 360deg; }
}

@keyframes pi-edge-shimmer {
  from { background-position: 0 300%; }
  to { background-position: 0 -300%; }
}

/* A short, small shake. Enough to say "this stopped", not enough to be a toy. */
@keyframes pi-block-in {
  0% { opacity: 0; transform: translateX(0); }
  30% { opacity: 1; transform: translateX(-3px); }
  55% { transform: translateX(3px); }
  75% { transform: translateX(-2px); }
  100% { transform: none; }
}

/* ── narrow ─────────────────────────────────────────────────────────────── */

@media (max-width: 640px) {
  .pi-terminal .terminal-body {
    min-height: 200px;
    padding: var(--sp-space-3);
    font-size: 0.8rem;
  }

  .pi-terminal .terminal-header {
    flex-wrap: wrap;
    row-gap: var(--sp-space-1);
  }

  /* The identity keeps its lock but yields width to the transcript. */
  .pi-user-chip > span {
    max-width: 8rem;
  }

  /* The indent that shapes the wide transcript is a tax at phone widths — the
     column is barely 40ch, so the answer needs every one of them. Prompts keep
     their hairline rule, which still carries the act grouping. */
  .pi-terminal.is-replay .terminal-body .pi-prose,
  .pi-terminal.is-replay .terminal-body .pi-tool,
  .pi-terminal.is-replay .terminal-body .pi-rail-line,
  .pi-terminal.is-replay .terminal-body .pi-blocked,
  .pi-terminal.is-replay .terminal-body .pi-note {
    margin-left: 0;
  }

  .pi-terminal.is-replay .terminal-body .pi-prose {
    padding-left: var(--sp-space-2);
  }

  .pi-terminal .pi-turn-user {
    margin-top: var(--sp-space-4);
    padding-top: var(--sp-space-2);
  }

  /* Chrome tightens; the transcript is what the height is for. */
  .pi-composer {
    padding: var(--sp-space-3);
  }

  .pi-approvals:not(:empty) {
    padding: var(--sp-space-2) var(--sp-space-3);
  }

  .pi-gate {
    padding: var(--sp-space-3);
    font-size: 0.8rem;
  }

  /* The kv ledger's two columns fight for width a phone does not have. */
  .pi-approval-kv {
    grid-template-columns: 1fr;
    gap: 0 var(--sp-space-3);
  }

  .pi-approval-kv dt {
    line-height: 1.4;
  }

  .pi-approval-kv dd {
    margin-bottom: var(--sp-space-1);
  }

  /* Chips give way before the verdict does. */
  .pi-blocked-meta {
    margin-left: 0;
    width: 100%;
  }

  /* The meters scroll rather than wrapping into a second row that pushes the
     transcript off the first screen. */
  .pi-meters {
    order: 3;
    width: 100%;
    margin-left: 0;
    overflow-x: auto;
    scrollbar-width: none;
  }

  .pi-meters::-webkit-scrollbar { display: none; }

  /* The rail is the one thing that must never be truncated: a chain with a pip
     hidden off-screen is a chain the viewer cannot verify. */
  .pi-rail {
    flex-wrap: wrap;
    row-gap: 0.2rem;
  }

  .pi-approval-actions,
  .pi-gate-actions {
    flex-direction: column;
  }

  /* The CTA is the one thing on a phone that must stay side by side and
     full-width-ish: stacked into a column it pushes the transcript off the
     first screen, which is the thing it is selling. */
  .pi-replay-bar {
    padding: var(--sp-space-3);
  }

  .pi-replay-bar .pi-gate-actions {
    flex-direction: row;
    width: 100%;
  }

  .pi-replay-bar .pi-gate-actions .pi-btn {
    flex: 1 1 0;
  }

  /* Args over the cleared chain, single column. */
  .pi-approval-grid {
    grid-template-columns: 1fr;
  }

  .pi-tool-state {
    display: none;
  }
}

/* ── artifacts: the results tools left behind ───────────────────────────── */

/* The "view result" affordance on a finished tool row. The wrapper exists so
   the button can sit on the summary line without living inside <summary>,
   which may not contain another control. */
.pi-tool-artifact-wrap {
  position: relative;
  margin: var(--sp-space-2) 0;
}

.pi-tool-artifact-wrap > .pi-tool {
  margin: 0;
}

.pi-tool-artifact-wrap > .pi-tool > summary {
  padding-right: 5.5rem;
}

.pi-tool-artifact {
  position: absolute;
  top: 0;
  right: 0;
  padding: 0.05rem 0.45rem;
  border: 1px solid color-mix(in oklch, var(--sp-color-success) 40%, transparent);
  border-radius: var(--sp-radius-full);
  background: transparent;
  color: var(--sp-color-success);
  font-family: var(--sp-font-mono);
  font-size: 0.62rem;
  white-space: nowrap;
  cursor: pointer;
}

.pi-tool-artifact:hover,
.pi-tool-artifact:focus-visible {
  background: color-mix(in oklch, var(--sp-color-success) 12%, transparent);
}

/* A no-arg call states the fact instead of asking the eye to parse `{}`. */
.pi-tool-body--empty {
  color: var(--sp-color-terminal-text-dim);
  font-style: italic;
}

/* ── header chip + gallery panel ────────────────────────────────────────── */

.pi-art {
  position: relative;
  display: inline-flex;
}

.pi-art-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.1rem 0.5rem;
  border: 1px solid var(--sp-color-terminal-border);
  border-radius: var(--sp-radius-full);
  background: transparent;
  color: var(--sp-color-terminal-text-dim);
  font-family: var(--sp-font-mono);
  font-size: 0.66rem;
  cursor: pointer;
}

.pi-art-chip:hover,
.pi-art-chip[aria-expanded="true"] {
  color: var(--sp-color-terminal-text);
  border-color: color-mix(in oklch, var(--sp-color-success) 45%, transparent);
}

.pi-art-count {
  color: var(--sp-color-success);
  font-weight: 700;
}

.pi-art-panel {
  position: absolute;
  top: calc(100% + 0.35rem);
  right: 0;
  z-index: 30;
  min-width: 16rem;
  max-width: 22rem;
  max-height: 18rem;
  overflow-y: auto;
  padding: var(--sp-space-2);
  background: var(--sp-color-terminal-header);
  border: 1px solid var(--sp-color-terminal-border);
  border-radius: var(--sp-corners-inner-md);
  box-shadow: var(--sp-shadow-lift);
  scrollbar-width: thin;
}

.pi-art-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.pi-art-item {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1rem;
  width: 100%;
  padding: 0.35rem 0.5rem;
  border: 0;
  border-radius: var(--sp-radius-sm);
  background: transparent;
  color: var(--sp-color-terminal-text);
  font-family: var(--sp-font-mono);
  font-size: 0.72rem;
  text-align: left;
  cursor: pointer;
}

.pi-art-item:hover,
.pi-art-item:focus-visible {
  background: color-mix(in oklch, var(--sp-color-terminal-bg) 70%, transparent);
}

.pi-art-item-title {
  font-weight: 600;
}

.pi-art-item-kind {
  color: var(--sp-color-terminal-text-dim);
  font-size: 0.62rem;
}

/* ── the viewer: floating over the transcript, never a modal ────────────── */

.pi-artifact-overlay {
  position: absolute;
  inset: var(--sp-space-3);
  z-index: 20;
  display: flex;
  flex-direction: column;
  background: var(--sp-color-terminal-header);
  border: 1px solid color-mix(in oklch, var(--sp-color-success) 30%, transparent);
  border-radius: var(--sp-corners-inner-md);
  box-shadow: var(--sp-shadow-lift);
  overflow: hidden;
}

.pi-artifact-head {
  display: flex;
  align-items: center;
  gap: var(--sp-space-2);
  padding: var(--sp-space-2) var(--sp-space-3);
  border-bottom: 1px solid var(--sp-color-terminal-border);
  font-family: var(--sp-font-mono);
}

.pi-artifact-title {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--sp-color-terminal-text);
  font-size: 0.8rem;
}

.pi-artifact-tabs {
  display: inline-flex;
  gap: 0.15rem;
}

.pi-artifact-tab {
  padding: 0.1rem 0.55rem;
  border: 1px solid var(--sp-color-terminal-border);
  border-radius: var(--sp-radius-full);
  background: transparent;
  color: var(--sp-color-terminal-text-dim);
  font-family: var(--sp-font-mono);
  font-size: 0.66rem;
  cursor: pointer;
}

.pi-artifact-tab[aria-selected="true"] {
  color: var(--sp-color-terminal-text);
  border-color: color-mix(in oklch, var(--sp-color-success) 45%, transparent);
  background: color-mix(in oklch, var(--sp-color-success) 10%, transparent);
}

.pi-artifact-close {
  padding: 0.1rem 0.4rem;
  border: 0;
  background: transparent;
  color: var(--sp-color-terminal-text-dim);
  font-size: 0.8rem;
  cursor: pointer;
}

.pi-artifact-close:hover,
.pi-artifact-close:focus-visible {
  color: var(--sp-color-terminal-text);
}

.pi-artifact-frame {
  flex: 1;
  width: 100%;
  border: 0;
  background: var(--sp-color-terminal-bg);
}

.pi-artifact-raw {
  flex: 1;
  margin: 0;
  padding: var(--sp-space-3);
  overflow: auto;
  background: var(--sp-color-terminal-bg);
  color: var(--sp-color-terminal-text-dim);
  font-size: 0.72rem;
  white-space: pre-wrap;
  scrollbar-width: thin;
}

/* The homepage: a governed agent on the left, your account and its telemetry
 * on the right, and nothing else.
 *
 * The two halves are one argument — the terminal shows a tool call stopping for
 * approval, the pane shows the audit row that decision wrote. Putting them on
 * separate pages was the old design, and it meant nobody ever saw both. */

/* The whole page is one viewport: hero on top, the marquee as its baseline,
   nothing below the fold to scroll to. */
.home-split-body {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  overflow: hidden;
}

.home-split {
  display: grid;
  /* Asymmetric: the terminal is the argument, the pane is its receipt. The
     pane's content caps out well below its share anyway. */
  grid-template-columns: minmax(0, 62fr) minmax(0, 38fr);
  gap: var(--sp-space-5);
  /* The site header is `position: fixed`, so it reserves no space — this is the
     one rule that owns the hero's geometry. `reset.css` also puts a
     `padding-top` on `main`, and `.home-split` *is* the `<main>`, so declaring
     the same property here replaces it rather than stacking with it. */
  flex: 1 1 auto;
  padding-top: var(--sp-header-height);
  padding-inline: var(--sp-space-6);
  padding-block-end: var(--sp-space-5);
  min-height: 0;
  /* The body owns the field now. */
  background: none;
}

.home-split-body .compat-marquee {
  flex: none;
}

.home-split-pane {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
  padding-block: var(--sp-space-5);
  overflow: hidden;
}

/* Both halves are one sheet of glass, so they need one top edge and one bottom
   edge. The card fills its column and scrolls *inside* its own glass — pane-
   level scrolling put a bare browser scrollbar outside the card and cropped
   its radii against the column edge. */
.home-split-pane--profile .auth-pane {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.home-split-pane--profile .auth-pane .pane {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overflow-x: clip;
  scrollbar-width: thin;
  scrollbar-color: var(--sp-stage-glass-border, var(--sp-color-terminal-border)) transparent;
}

/* The card's sections keep their natural height and the card scrolls past
   them; letting them shrink instead squashes every section into the next
   (tabs printed over the credit figure, rows through the bottom radius). */
.home-split-pane--profile .auth-pane .pane > * {
  flex: none;
}

.home-split-pane--profile .auth-pane .pane::-webkit-scrollbar {
  width: 10px;
}

.home-split-pane--profile .auth-pane .pane::-webkit-scrollbar-track {
  background: transparent;
}

.home-split-pane--profile .auth-pane .pane::-webkit-scrollbar-thumb {
  background: var(--sp-stage-glass-border, var(--sp-color-terminal-border));
  border-radius: var(--sp-radius-full);
  border: 3px solid transparent;
  background-clip: content-box;
}

/* ── the terminal, filling its half ─────────────────────────────────────── */

/* pi-terminal.css sizes the widget for a centred band inside a marketing
   section. Here it is the surface, so the width cap and the fixed body height
   both have to go — the body takes the slack instead. */
.home-split-pane--terminal .pi-terminal {
  flex: 1 1 auto;
  min-height: 0;
  max-width: none;
  margin: 0;
}

.home-split-pane--terminal .pi-terminal .terminal {
  display: flex;
  flex-direction: column;
  height: 100%;
}

/* `.pi-body-wrap` is the flex child of `.terminal`; `.terminal-body` sits inside
   it. Sizing only the inner element left the wrapper at `flex: 0 1 auto`, so it
   grew to the full height of the transcript, overflowed the shell, and pushed
   the composer out through `overflow: hidden` — the input was in the DOM and
   simply off-screen. The wrapper has to be the flexible one. */
.home-split-pane--terminal .pi-terminal .pi-body-wrap {
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
}

.home-split-pane--terminal .pi-terminal .terminal-body {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}

.home-split-pane--terminal .pi-approvals:not(:empty) {
  flex: none;
  max-height: 40%;
  overflow-y: auto;
}

/* Everything below the transcript is chrome and must never be what shrinks. */
.home-split-pane--terminal .pi-composer,
.home-split-pane--terminal .pi-hint,
.home-split-pane--terminal .pi-palette,
.home-split-pane--terminal .pi-replay-bar,
.home-split-pane--terminal .pi-gate {
  flex: none;
}

/* ── narrow ─────────────────────────────────────────────────────────────── */

/* Below the split threshold the two halves stack, terminal first: it is the
   thing worth looking at, and the pane reads as its caption. Each keeps enough
   height to be usable rather than each getting an unreadable sliver. */
@media (max-width: 900px) {
  /* Stacked panes need real height again, so the page goes back to scrolling. */
  .home-split-body {
    display: block;
    height: auto;
    overflow: visible;
  }

  .home-split {
    /* minmax(0, 1fr), never bare 1fr: a bare 1fr's implicit minimum is
       min-content, and the widest row inside the terminal (meters, approval
       internals) would size the column past the viewport — the transcript then
       renders wider than the screen and is clipped at the pane's edge. */
    grid-template-columns: minmax(0, 1fr);
    height: auto;
    padding-inline: var(--sp-space-3);
    padding-block-end: var(--sp-space-6);
  }

  .home-split-pane {
    min-height: 70dvh;
    overflow: visible;
    padding-block: var(--sp-space-3);
  }

  .home-split-pane--terminal .pi-terminal .terminal-body {
    min-height: 320px;
  }
}

/* <sp-auth-pane> — sign in, then watch your own agent's governance spine.
 *
 * Light DOM, so everything is scoped under .auth-pane rather than shadowed.
 * The two states share the same shell deliberately: signing in should read as
 * the panel filling in, not as one screen replacing another. */

.auth-pane {
  display: block;
  min-width: 0;

  /* Local dark-glass tokens. This component is used by the homepage and
     nowhere else, so it is styled for the stage directly rather than being
     themed through it. */
  --sp-ap-glass: var(--sp-stage-glass, oklch(0.24 0.012 55 / 0.72));
  --sp-ap-border: var(--sp-stage-glass-border, oklch(1 0 0 / 0.09));
  --sp-ap-well: oklch(0 0 0 / 0.28);
  --sp-ap-raise: oklch(1 0 0 / 0.08);
  --sp-ap-text: var(--sp-stage-text, oklch(0.96 0.004 60));
  --sp-ap-text-dim: var(--sp-stage-text-dim, oklch(0.82 0.008 60));
  --sp-ap-text-muted: var(--sp-stage-text-muted, oklch(0.68 0.01 60));
}

/* The JS wizard toggles `hidden` on the form, on each step, and on the busy
   row. An author `display` on any of those silently defeats the UA's
   `[hidden] { display: none }` — which is exactly how sign-in, register step
   1, register step 2, and a spinner all ended up stacked on screen at once.
   These live above the `display: flex` rules and must stay above them. */
.auth-pane .pane-form[hidden],
.auth-pane .pane-step[hidden],
.auth-pane .pane-busy[hidden],
.auth-pane .pane-panel[hidden],
.auth-pane .pane-alert[hidden] { display: none; }

.auth-pane .pane {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-5);
  padding: var(--sp-space-5);
  /* Children respect the rounded corners — without this a request row can
     paint past the pane's bottom radius. */
  overflow: clip;
  background: var(--sp-ap-glass);
  backdrop-filter: blur(var(--sp-stage-blur, 16px)) saturate(140%);
  -webkit-backdrop-filter: blur(var(--sp-stage-blur, 16px)) saturate(140%);
  border: 1px solid var(--sp-ap-border);
  border-radius: var(--sp-corners-lg);
  box-shadow: 0 24px 60px oklch(0 0 0 / 0.45), 0 0 0 1px oklch(1 0 0 / 0.04) inset;
  color: var(--sp-ap-text-dim);
}

/* ── head ───────────────────────────────────────────────────────────────── */

.auth-pane .pane-title {
  margin: 0 0 var(--sp-space-2);
  font-family: var(--sp-font-heading);
  font-size: clamp(1.35rem, 1.1rem + 0.9vw, 1.9rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--sp-ap-text);
}

.auth-pane .pane-sub {
  margin: 0;
  color: var(--sp-ap-text-muted);
  font-size: 0.9rem;
  line-height: 1.6;
}

.auth-pane .pane-head--profile {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-space-4);
  padding-bottom: var(--sp-space-4);
  border-bottom: 1px solid var(--sp-ap-border);
}

.auth-pane .pane-id {
  display: flex;
  align-items: center;
  gap: var(--sp-space-3);
  min-width: 0;
}

.auth-pane .pane-avatar {
  display: grid;
  place-items: center;
  flex: none;
  width: 2.5rem;
  height: 2.5rem;
  background: var(--sp-color-primary);
  color: var(--sp-color-text-on-primary);
  /* The branded asymmetric corner: square where the identity anchors, round
     everywhere else. */
  border-radius: var(--sp-radius-full) var(--sp-radius-full) var(--sp-radius-full) var(--sp-radius-sm);
  font-family: var(--sp-font-heading);
  font-weight: 600;
}

.auth-pane .pane-name {
  display: block;
  color: var(--sp-ap-text);
  font-size: 0.95rem;
  line-height: 1.3;
}

.auth-pane .pane-email {
  display: block;
  color: var(--sp-ap-text-muted);
  font-size: 0.8rem;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A <button> because for an admin it opens the Platform panel; for everyone
   else it stays disabled and reads as the plain role chip it always was. */
.auth-pane .pane-badge {
  flex: none;
  padding: 0.2rem var(--sp-space-3);
  background: transparent;
  border: 1px solid currentcolor;
  border-radius: var(--sp-radius-full);
  font: inherit;
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.auth-pane .pane-badge:disabled {
  opacity: 1;
  cursor: default;
}

.auth-pane .pane-badge--ok { color: var(--sp-color-success); }
.auth-pane .pane-badge--pending { color: var(--sp-color-warning); }

/* The admin role is a door, and it should look like one. */
.auth-pane .pane-badge--admin {
  color: var(--sp-color-primary-text, var(--sp-color-primary));
  cursor: pointer;
  transition: background 0.18s ease, box-shadow 0.18s ease;
}

.auth-pane .pane-badge--admin:hover {
  background: rgb(var(--sp-color-primary-rgb) / 0.12);
}

.auth-pane .pane-badge--admin.is-active {
  background: rgb(var(--sp-color-primary-rgb) / 0.18);
  box-shadow: 0 0 0 2px rgb(var(--sp-color-primary-rgb) / 0.2);
}

.auth-pane .pane-badge--admin:focus-visible {
  outline: 2px solid var(--sp-color-primary);
  outline-offset: 1px;
}

/* ── tabs ───────────────────────────────────────────────────────────────── */

.auth-pane .pane-tabs {
  display: flex;
  gap: var(--sp-space-1);
  padding: 0.25rem;
  background: var(--sp-ap-well);
  border-radius: var(--sp-radius-md);
}

.auth-pane .pane-tab {
  flex: 1;
  padding: var(--sp-space-2) var(--sp-space-3);
  background: transparent;
  border: none;
  border-radius: var(--sp-radius-sm);
  color: var(--sp-ap-text-muted);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease;
}

.auth-pane .pane-tab.is-active {
  background: var(--sp-ap-raise);
  color: var(--sp-ap-text);
  box-shadow: 0 1px 2px oklch(0 0 0 / 0.35);
}

/* ── forms ──────────────────────────────────────────────────────────────── */

.auth-pane .pane-form,
.auth-pane .pane-step {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-2);
  margin: 0;
  padding: 0;
  border: none;
}

.auth-pane .pane-label {
  color: var(--sp-ap-text-muted);
  font-size: 0.78rem;
  letter-spacing: 0.02em;
}

.auth-pane .pane-label:not(:first-child) {
  margin-top: var(--sp-space-2);
}

.auth-pane .pane-field {
  width: 100%;
  padding: var(--sp-space-3);
  background: var(--sp-ap-well);
  border: 1px solid var(--sp-ap-border);
  border-radius: var(--sp-radius-md);
  color: var(--sp-ap-text);
  font: inherit;
  font-size: 0.9rem;
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}

.auth-pane .pane-field:focus-visible {
  outline: none;
  border-color: var(--sp-color-primary);
  box-shadow: 0 0 0 3px rgba(var(--sp-color-primary-rgb), 0.18);
}

.auth-pane textarea.pane-field {
  resize: vertical;
}

.auth-pane .pane-actions {
  display: flex;
  gap: var(--sp-space-2);
  margin-top: var(--sp-space-2);
}

.auth-pane .pane-btn {
  padding: var(--sp-space-3) var(--sp-space-5);
  border: 1px solid transparent;
  border-radius: var(--sp-radius-md);
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
  transition: background 0.18s ease, transform 0.12s ease, opacity 0.18s ease;
}

.auth-pane .pane-btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.auth-pane .pane-btn--primary {
  margin-top: var(--sp-space-2);
  background: var(--sp-color-primary);
  color: var(--sp-color-text-on-primary);
  box-shadow: var(--sp-shadow-accent-sm);
}

.auth-pane .pane-btn--primary:hover:not(:disabled) {
  background: var(--sp-color-primary-hover);
}

.auth-pane .pane-btn--primary:active:not(:disabled) {
  transform: translateY(1px);
}

.auth-pane .pane-btn--ghost {
  background: transparent;
  border-color: var(--sp-ap-border);
  color: var(--sp-ap-text-dim);
}

.auth-pane .pane-note {
  margin: var(--sp-space-1) 0 0;
  color: var(--sp-ap-text-muted);
  font-size: 0.78rem;
  line-height: 1.55;
}

.auth-pane .pane-note--pending {
  padding: var(--sp-space-3);
  background: var(--sp-ap-well);
  border-left: 2px solid var(--sp-color-warning);
  border-radius: 0 var(--sp-radius-md) var(--sp-radius-md) 0;
  color: var(--sp-ap-text-dim);
}

.auth-pane .pane-link {
  padding: 0;
  background: none;
  border: none;
  color: var(--sp-color-primary-text);
  font: inherit;
  font-size: inherit;
  text-decoration: underline;
  cursor: pointer;
}

/* ── feedback ───────────────────────────────────────────────────────────── */

.auth-pane .pane-alert {
  padding: var(--sp-space-3);
  border-radius: var(--sp-radius-md);
  font-size: 0.85rem;
  line-height: 1.5;
}

.auth-pane .pane-alert[data-tone="error"] {
  background: color-mix(in oklch, var(--sp-color-error) 14%, transparent);
  border-left: 2px solid var(--sp-color-error);
  color: var(--sp-color-error);
}

.auth-pane .pane-alert[data-tone="info"] {
  background: var(--sp-ap-well);
  border-left: 2px solid var(--sp-color-info);
  color: var(--sp-ap-text-dim);
}

.auth-pane .pane-busy {
  display: flex;
  align-items: center;
  gap: var(--sp-space-3);
  color: var(--sp-ap-text-dim);
  font-size: 0.85rem;
}

.auth-pane .pane-spinner {
  width: 1rem;
  height: 1rem;
  border: 2px solid var(--sp-ap-border);
  border-top-color: var(--sp-color-primary);
  border-radius: var(--sp-radius-full);
  animation: auth-pane-spin 0.8s linear infinite;
}

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

/* ── telemetry ──────────────────────────────────────────────────────────── */

.auth-pane .pane-section {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-3);
  min-height: 0;
}

.auth-pane .pane-h3 {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-space-3);
  margin: 0;
  color: var(--sp-ap-text-muted);
  font-family: var(--sp-font-heading);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.auth-pane .pane-h3-sub {
  color: var(--sp-ap-text-muted);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
}


/* ── The offer, and what is left of it ──────────────────────────────────────
   Two faces of one fact. Signed out it is `.pane-offer`: the reason to make an
   account. Signed in it is `.pane-credit`: what that grant has left. They are
   styled as a pair on purpose — the promise and the receipt should look like
   the same object, because a visitor who acted on the first is owed the
   second in the same visual language. */

.auth-pane .pane-offer {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-1);
  margin-bottom: var(--sp-space-4);
  padding: var(--sp-space-4);
  background: linear-gradient(
    135deg,
    rgb(var(--sp-color-primary-rgb) / 0.14),
    rgb(var(--sp-color-primary-rgb) / 0.04)
  );
  border: 1px solid rgb(var(--sp-color-primary-rgb) / 0.35);
  border-radius: var(--sp-radius-md) var(--sp-radius-md) var(--sp-radius-md) var(--sp-radius-sm);
}

.auth-pane .pane-offer-amount {
  color: var(--sp-color-primary-text, var(--sp-ap-text));
  font-family: var(--sp-font-heading);
  font-size: 1.45rem;
  line-height: 1.1;
}

.auth-pane .pane-offer-line {
  color: var(--sp-ap-text);
  font-size: 0.92rem;
}

.auth-pane .pane-offer-fine {
  color: var(--sp-ap-text-muted);
  font-size: 0.78rem;
  line-height: 1.45;
}

.auth-pane .pane-credit {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-2);
}

.auth-pane .pane-credit-figure {
  display: flex;
  align-items: baseline;
  gap: var(--sp-space-2);
}

.auth-pane .pane-credit-left {
  color: var(--sp-ap-text);
  font-family: var(--sp-font-mono);
  font-size: 1.5rem;
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.auth-pane .pane-credit-cap {
  color: var(--sp-ap-text-muted);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.auth-pane .pane-credit-bar {
  overflow: hidden;
  height: 0.4rem;
  background: var(--sp-ap-raise);
  border: 1px solid var(--sp-ap-border);
  border-radius: var(--sp-radius-full);
}

.auth-pane .pane-credit-bar > span {
  display: block;
  width: 0;
  height: 100%;
  background: var(--sp-color-success);
  border-radius: inherit;
  /* Eased because the bar moves on a 3s poll, not per frame: a jump would
     read as a glitch, a slide reads as spend. */
  transition: width 0.5s ease-out, background-color 0.3s ease-out;
}

.auth-pane .pane-credit-bar[data-state="low"] > span { background: var(--sp-color-warning); }
.auth-pane .pane-credit-bar[data-state="empty"] > span { background: var(--sp-color-error); }

.auth-pane .pane-credit-note {
  margin: 0;
  color: var(--sp-ap-text-muted);
  font-size: 0.78rem;
  line-height: 1.45;
}

@media (prefers-reduced-motion: reduce) {
  .auth-pane .pane-credit-bar > span { transition: none; }
}

.auth-pane .pane-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: var(--sp-space-2);
  margin: 0;
}

.auth-pane .pane-stat {
  padding: var(--sp-space-3);
  background: var(--sp-ap-raise);
  border: 1px solid var(--sp-ap-border);
  /* One squared corner per tile, so a grid of them reads as one family. */
  border-radius: var(--sp-radius-md) var(--sp-radius-md) var(--sp-radius-md) var(--sp-radius-sm);
}

.auth-pane .pane-stat dt {
  color: var(--sp-ap-text-muted);
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.auth-pane .pane-stat dd {
  margin: var(--sp-space-1) 0 0;
  color: var(--sp-ap-text);
  font-family: var(--sp-font-mono);
  font-size: 0.95rem;
  font-variant-numeric: tabular-nums;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A number that just moved is the point of the pane; a brief warm wash is
   enough to catch the eye without pulling it off the terminal. */
.auth-pane .pane-stat dd.is-changed {
  animation: auth-pane-flash 0.6s ease-out;
}

@keyframes auth-pane-flash {
  from { color: var(--sp-color-primary); }
  to { color: var(--sp-ap-text); }
}

.auth-pane .pane-section--feed {
  flex: 1 1 auto;
  min-height: 0;
}

.auth-pane .pane-feed {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-2);
  margin: 0;
  padding: 0;
  max-height: 22rem;
  overflow-y: auto;
  list-style: none;
}

.auth-pane .pane-feed-empty {
  color: var(--sp-ap-text-muted);
  font-size: 0.82rem;
  line-height: 1.55;
}

.auth-pane .pane-feed-item {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: var(--sp-space-2) var(--sp-space-3);
  background: var(--sp-ap-well);
  border-left: 2px solid var(--sp-color-success);
  border-radius: 0 var(--sp-radius-sm) var(--sp-radius-sm) 0;
  font-family: var(--sp-font-mono);
  font-size: 0.76rem;
}

.auth-pane .pane-feed-item--deny {
  border-left-color: var(--sp-color-error);
}

.auth-pane .pane-feed-head {
  color: var(--sp-ap-text);
}

.auth-pane .pane-feed-tail {
  color: var(--sp-ap-text-muted);
  overflow-wrap: anywhere;
}

.auth-pane .pane-foot {
  display: flex;
  justify-content: flex-end;
  padding-top: var(--sp-space-3);
  border-top: 1px solid var(--sp-ap-border);
}

/* Leaving is a quiet action: a compact ghost button in the pane's own
   language, not an orange link shouting from the footer. */
.auth-pane .pane-btn--signout {
  padding: var(--sp-space-2) var(--sp-space-4);
  font-size: 0.8rem;
  color: var(--sp-ap-text-muted);
}

.auth-pane .pane-btn--signout:hover {
  border-color: var(--sp-ap-text-muted);
  color: var(--sp-ap-text);
}

/* A non-zero block count is the number this pane exists to show, so it gets a
   hot state — the same one the terminal header's meter already uses, because
   the two halves are reporting the same fact. */
.auth-pane .pane-stat[data-hot="1"] {
  border-color: color-mix(in oklch, var(--sp-color-error) 55%, transparent);
  background: color-mix(in oklch, var(--sp-color-error) 12%, var(--sp-ap-raise));
}

.auth-pane .pane-stat[data-hot="1"] dd {
  color: var(--sp-color-error);
}


/* The human by-line under a decision — who answered the gate, and when. */
.auth-pane .pane-feed-by {
  display: block;
  font-size: 0.66rem;
  color: var(--sp-color-success);
  font-variant-numeric: tabular-nums;
}

/* ── policy pipeline ────────────────────────────────────────────────────── */

.auth-pane .pane-stages {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-1);
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: pane-stage;
}

.auth-pane .pane-stage {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-space-2);
  padding: var(--sp-space-2) var(--sp-space-3);
  background: var(--sp-ap-well);
  border-left: 2px solid var(--sp-ap-border);
  border-radius: 0 var(--sp-radius-sm) var(--sp-radius-sm) 0;
  font-size: 0.78rem;
}

/* Numbered, because the order the checks run in is part of the claim. */
.auth-pane .pane-stage-name::before {
  counter-increment: pane-stage;
  content: counter(pane-stage) ". ";
  color: var(--sp-ap-text-muted);
  font-family: var(--sp-font-mono);
}

.auth-pane .pane-stage-name {
  color: var(--sp-ap-text);
}

.auth-pane .pane-stage-tally {
  color: var(--sp-ap-text-muted);
  font-family: var(--sp-font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* A stage that has not evaluated anything yet reads as present but quiet —
   dimmed rather than absent, so the pipeline never appears to grow. */
.auth-pane .pane-stage[data-active="0"] {
  opacity: 0.55;
}

.auth-pane .pane-stage[data-active="1"] {
  border-left-color: var(--sp-color-success);
}

.auth-pane .pane-stage[data-hot="1"] {
  border-left-color: var(--sp-color-error);
  background: color-mix(in oklch, var(--sp-color-error) 12%, var(--sp-ap-well));
  opacity: 1;
}

.auth-pane .pane-stage[data-hot="1"] .pane-stage-tally {
  color: var(--sp-color-error);
}

/* ── platform pulse (the admin-only Platform tab) ────────────────────────── */

/* Muted throughout: this panel is context about everyone else, and it must
   not compete with the operator's own numbers on the other tabs. */
.auth-pane .pane-section--pulse {
  padding-top: var(--sp-space-3);
}

.auth-pane .pane-stats--pulse {
  grid-template-columns: repeat(auto-fit, minmax(6.5rem, 1fr));
}

.auth-pane .pane-stats--pulse .pane-stat {
  padding: var(--sp-space-2);
  background: transparent;
  border-style: dashed;
}

.auth-pane .pane-stats--pulse .pane-stat dd {
  color: var(--sp-ap-text-muted);
  font-size: 0.85rem;
}

.auth-pane .pane-pulse-note {
  margin: var(--sp-space-2) 0 0;
  color: var(--sp-ap-text-muted);
  font-size: 0.75rem;
  line-height: 1.5;
}

/* ── tabbed telemetry ───────────────────────────────────────────────────────
   The signed-in pane is four panels behind one tablist. Panels all stay in
   the DOM — switching toggles `hidden` — so the counters inside a hidden tab
   are already current the instant it is shown. */

/* Deliberately not sticky: a bar that pins itself while the credit header
   slides underneath reads as a glitch, and the Governance chip already
   carries the one signal worth seeing from any scroll position. */

.auth-pane .pane-tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-space-1);
}

.auth-pane .pane-tab[aria-selected="true"] {
  background: var(--sp-ap-raise);
  color: var(--sp-ap-text);
  box-shadow: 0 1px 2px oklch(0 0 0 / 0.35);
}

.auth-pane .pane-tab:focus-visible {
  outline: 2px solid var(--sp-color-primary);
  outline-offset: 1px;
}

/* The Governance tab's live count. Red once anything has been denied — the
   one signal that must reach a visitor looking at a different tab. */
.auth-pane .pane-tab-chip {
  padding: 0 0.4rem;
  background: var(--sp-ap-well);
  border-radius: var(--sp-radius-full);
  color: var(--sp-ap-text-dim);
  font-family: var(--sp-font-mono);
  font-size: 0.68rem;
  font-variant-numeric: tabular-nums;
  line-height: 1.4;
}

.auth-pane .pane-tab-chip[data-alert="1"] {
  background: color-mix(in oklch, var(--sp-color-error) 25%, var(--sp-ap-well));
  color: var(--sp-color-error);
}

.auth-pane .pane-panel {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-4);
  animation: auth-pane-panel-in 0.16s ease-out;
}

.auth-pane .pane-panel:focus-visible {
  outline: 2px solid var(--sp-color-primary);
  outline-offset: 2px;
  border-radius: var(--sp-radius-sm);
}

@keyframes auth-pane-panel-in {
  from { opacity: 0; transform: translateY(2px); }
  to { opacity: 1; transform: none; }
}

/* Denser tiles inside panels than the old stacked layout: the tab already
   names the group, so each tile carries less framing. */
.auth-pane .pane-panel .pane-stat {
  padding: var(--sp-space-2) var(--sp-space-3);
}

/* The Overview's headline numbers, sized to be read from across the split. */
.auth-pane .pane-stats--hero {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.auth-pane .pane-stats--hero .pane-stat dd {
  font-size: 1.35rem;
}

.auth-pane .pane-model-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-space-2);
  margin: 0;
  padding: var(--sp-space-2) var(--sp-space-3);
  background: var(--sp-ap-well);
  border-radius: var(--sp-radius-sm);
  color: var(--sp-ap-text);
  font-family: var(--sp-font-mono);
  font-size: 0.78rem;
}

.auth-pane .pane-model-sep {
  color: var(--sp-ap-text-muted);
}

/* One pip per policy stage — the Overview's echo of the full pipeline. */
.auth-pane .pane-stage-mini {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-space-1);
}

.auth-pane .pane-stage-pip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.2rem var(--sp-space-2);
  border: 1px solid var(--sp-ap-border);
  border-radius: var(--sp-radius-full);
  color: var(--sp-ap-text-muted);
  font-size: 0.7rem;
  letter-spacing: 0.02em;
}

.auth-pane .pane-stage-pip::before {
  content: "";
  width: 0.4rem;
  height: 0.4rem;
  background: var(--sp-ap-text-muted);
  border-radius: var(--sp-radius-full);
}

.auth-pane .pane-stage-pip[data-active="1"] {
  color: var(--sp-ap-text-dim);
}

.auth-pane .pane-stage-pip[data-active="1"]::before {
  background: var(--sp-color-success);
}

.auth-pane .pane-stage-pip[data-hot="1"] {
  border-color: color-mix(in oklch, var(--sp-color-error) 55%, transparent);
  color: var(--sp-color-error);
}

.auth-pane .pane-stage-pip[data-hot="1"]::before {
  background: var(--sp-color-error);
}

/* Inline share/latency bars — same recipe as the credit meter, so every bar
   in the pane speaks one visual language. */
.auth-pane .pane-bar {
  overflow: hidden;
  flex: 1;
  height: 0.35rem;
  min-width: 3rem;
  background: var(--sp-ap-raise);
  border: 1px solid var(--sp-ap-border);
  border-radius: var(--sp-radius-full);
}

.auth-pane .pane-bar > span {
  display: block;
  width: 0;
  height: 100%;
  background: var(--sp-color-primary);
  border-radius: inherit;
  transition: width 0.5s ease-out;
}

.auth-pane .pane-lat {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-2);
}

.auth-pane .pane-lat-row,
.auth-pane .pane-mix-row {
  display: flex;
  align-items: center;
  gap: var(--sp-space-2);
  font-family: var(--sp-font-mono);
  font-size: 0.76rem;
}

.auth-pane .pane-lat-label,
.auth-pane .pane-mix-label {
  flex: none;
  width: 5.5rem;
  overflow: hidden;
  color: var(--sp-ap-text-muted);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.auth-pane .pane-lat-val,
.auth-pane .pane-mix-pct {
  flex: none;
  min-width: 3.2rem;
  color: var(--sp-ap-text);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.auth-pane .pane-mix {
  display: flex;
  flex-direction: column;
  gap: var(--sp-space-2);
}

.auth-pane .pane-feed--preview {
  max-height: none;
  overflow: visible;
}

/* ── governance filter ──────────────────────────────────────────────────────
   A smaller sibling of the tablist. Filtering is one CSS rule per mode keyed
   on the list's data-filter, so entries pushed live obey the active filter. */

.auth-pane .pane-filter {
  display: flex;
  gap: var(--sp-space-1);
  padding: 0.2rem;
  background: var(--sp-ap-well);
  border-radius: var(--sp-radius-sm);
}

.auth-pane .pane-filter-btn {
  flex: 1;
  padding: 0.25rem var(--sp-space-2);
  background: transparent;
  border: none;
  border-radius: var(--sp-radius-sm);
  color: var(--sp-ap-text-muted);
  font: inherit;
  font-size: 0.72rem;
  cursor: pointer;
  transition: background 0.18s ease, color 0.18s ease;
}

.auth-pane .pane-filter-btn.is-active {
  background: var(--sp-ap-raise);
  color: var(--sp-ap-text);
}

/* The scope switch governs every tab beneath it, so it sits above the tablist
   and reads as a heading rather than as one more control inside a panel. */
.auth-pane .pane-filter--scope {
  margin-bottom: var(--sp-space-2);
}

.auth-pane .pane-filter--scope .pane-filter-btn.is-active {
  color: var(--sp-color-primary);
  font-weight: 600;
}

.auth-pane .pane-filter-btn:focus-visible {
  outline: 2px solid var(--sp-color-primary);
  outline-offset: 1px;
}

.auth-pane .pane-feed[data-filter="blocked"] .pane-feed-item:not([data-outcome="deny"]),
.auth-pane .pane-feed[data-filter="tool"]
  .pane-feed-item:not([data-kind="tool"]):not([data-kind="fire"]),
.auth-pane .pane-feed[data-filter="request"]
  .pane-feed-item:not([data-kind="request"]):not([data-kind="route"]) {
  display: none;
}

/* A denial is the one thing this feed exists to surface — let it be loud. */
.auth-pane .pane-feed-item--deny {
  background: color-mix(in oklch, var(--sp-color-error) 12%, var(--sp-ap-well));
}

.auth-pane .pane-feed-item--deny .pane-feed-head {
  color: var(--sp-color-error);
  font-weight: 600;
}

/* Per-model speed and spend under each mix bar. */
.auth-pane .pane-mix-meta {
  margin-top: -0.25rem;
  padding-left: 5.5rem;
  color: var(--sp-ap-text-muted);
  font-family: var(--sp-font-mono);
  font-size: 0.68rem;
  font-variant-numeric: tabular-nums;
}

.auth-pane .pane-link--sm {
  font-size: 0.72rem;
  text-transform: none;
  letter-spacing: 0;
}

@media (prefers-reduced-motion: reduce) {
  .auth-pane .pane-spinner,
  .auth-pane .pane-stat dd.is-changed,
  .auth-pane .pane-panel {
    animation: none;
  }

  .auth-pane .pane-bar > span {
    transition: none;
  }
}

/* Chart, request-list, and activity widgets for <sp-auth-pane>.
   Companion to auth-pane-telemetry.css, which is at its size budget. */

.auth-pane .pane-chart,
.auth-pane .pane-strip {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 44px;
  padding: 4px 6px;
  border: 1px solid var(--sp-ap-border);
  border-radius: var(--sp-radius-sm);
  background: var(--sp-ap-well);
}

.auth-pane .pane-strip {
  height: 30px;
  margin-top: 10px;
}

.auth-pane .pane-strip .pane-chart {
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  flex: 1;
}

.auth-pane .pane-chart-col {
  flex: 1;
  min-width: 3px;
  max-width: 14px;
  border-radius: 2px 2px 0 0;
  background: var(--sp-color-primary);
  opacity: 0.75;
}

.auth-pane .pane-chart-col[data-hot="1"] {
  background: var(--sp-color-error);
  opacity: 1;
}

.auth-pane .pane-ribbon {
  margin: 10px 0 0;
  font-size: 0.72rem;
  line-height: 1.5;
  color: var(--sp-ap-text-muted);
}

/* success / failure split bar */
.auth-pane .pane-split {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
}

.auth-pane .pane-split-bar {
  flex: 1;
  display: flex;
  height: 6px;
  border-radius: var(--sp-radius-full);
  overflow: hidden;
  background: var(--sp-ap-well);
}

.auth-pane .pane-split-ok { background: var(--sp-color-success); }

.auth-pane .pane-split-bad { background: var(--sp-color-error); }

.auth-pane .pane-split-legend {
  font-size: 0.68rem;
  color: var(--sp-ap-text-muted);
  white-space: nowrap;
}

/* per-request rows — capped like .pane-feed so a long conversation scrolls
   inside its own box instead of stretching the pane. */
.auth-pane .pane-reqs,
.auth-pane .pane-convs {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 6px;
  max-height: 22rem;
  overflow-y: auto;
}

.auth-pane .pane-req,
.auth-pane .pane-conv {
  display: grid;
  gap: 2px 8px;
  padding: 6px 8px;
  border: 1px solid var(--sp-ap-border);
  border-radius: var(--sp-radius-sm);
  background: var(--sp-ap-raise);
  font-size: 0.72rem;
}

.auth-pane .pane-req--failed { border-color: var(--sp-color-error); }

.auth-pane .pane-req-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
}

.auth-pane summary.pane-req-head { cursor: pointer; }

.auth-pane .pane-req-at,
.auth-pane .pane-conv-when {
  font-family: var(--sp-font-mono);
  color: var(--sp-ap-text-dim);
}

.auth-pane .pane-req-model { color: var(--sp-ap-text); }

.auth-pane .pane-req-lat,
.auth-pane .pane-conv-tally {
  color: var(--sp-ap-text-muted);
}

.auth-pane .pane-req-status {
  margin-left: auto;
  font-family: var(--sp-font-mono);
  font-size: 0.64rem;
  text-transform: uppercase;
  color: var(--sp-color-success);
}

.auth-pane .pane-req-status[data-state="failed"],
.auth-pane .pane-req-status[data-state="rejected"] {
  color: var(--sp-color-error);
}

.auth-pane .pane-req-error {
  margin: 6px 0 0;
  padding: 6px 8px;
  border-left: 2px solid var(--sp-color-error);
  font-family: var(--sp-font-mono);
  font-size: 0.68rem;
  color: var(--sp-ap-text);
  word-break: break-word;
}

/* activity rows */
.auth-pane .pane-conv-title {
  color: var(--sp-ap-text);
  text-decoration: none;
  font-weight: 600;
}

.auth-pane .pane-conv-title:hover { text-decoration: underline; }

.auth-pane .pane-conv--live { border-color: var(--sp-color-primary); }

.auth-pane .pane-conv-cost {
  font-family: var(--sp-font-mono);
  color: var(--sp-ap-text);
}

.auth-pane .pane-feed-audit { margin-left: auto; }

/* ── the operator block of the platform pulse ────────────────────────────────
 *
 * Rendered by sp-pulse-admin.js, and only for a caller the server decided is an
 * admin. It lives inside .pane-section--pulse, so it inherits the muted
 * treatment that keeps the platform aggregate from competing with the visitor's
 * own numbers — but it is denser than anything else in the pane, and the rules
 * here are mostly about surviving that density in a column roughly 30rem wide.
 *
 * Tokens are the --sp-ap-* set defined by auth-pane-core.css rather than raw colours,
 * so this block follows the pane into dark mode without a second theme block.
 */

/* An opaque backing of its own: this block is dense figures over the page's
   fixed video layer, and the pane's glass alone leaves them unreadable. */
.auth-pane .pulse-admin {
  margin-top: var(--sp-space-4);
  padding: var(--sp-space-3);
  background: oklch(0.19 0.012 55 / 0.92);
  border: 1px solid var(--sp-ap-border);
  border-radius: var(--sp-radius-md) var(--sp-radius-md) var(--sp-radius-md) var(--sp-radius-sm);
}

.auth-pane .pulse-admin-h {
  margin: var(--sp-space-4) 0 var(--sp-space-2);
  color: var(--sp-ap-text-muted);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.auth-pane .pulse-admin > .pulse-admin-h:first-child {
  margin-top: 0;
}

.auth-pane .pulse-admin-block {
  margin-bottom: var(--sp-space-3);
}

/* Two columns, collapsing to one at the same 900px where home-split.css stacks
   the terminal above the pane — below that the pane is full-width but the
   viewport is narrow, and two columns of figures would be unreadable in either
   arrangement. Matching the split's own breakpoint rather than inventing a
   second one keeps the two from disagreeing mid-resize. */
.auth-pane .pulse-admin-cols {
  display: grid;
  gap: var(--sp-space-3);
  grid-template-columns: 1fr 1fr;
}

@media (max-width: 900px) {
  .auth-pane .pulse-admin-cols {
    grid-template-columns: 1fr;
  }
}

/* ── tiles ──────────────────────────────────────────────────────────────── */

.auth-pane .pulse-admin-tiles {
  display: grid;
  gap: var(--sp-space-2);
  grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
  margin: 0 0 var(--sp-space-3);
}

.auth-pane .pulse-admin-tile {
  padding: var(--sp-space-2);
  border: 1px dashed var(--sp-ap-border);
  border-radius: var(--sp-radius-sm, 4px);
}

.auth-pane .pulse-admin-tile dt {
  color: var(--sp-ap-text-muted);
  font-size: 0.65rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.auth-pane .pulse-admin-tile dd {
  display: flex;
  align-items: baseline;
  gap: 0.4em;
  margin: 0.15rem 0 0;
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
}

/* Direction carries a colour and a glyph, never colour alone — the arrow is
   what makes the trend legible to a viewer who cannot distinguish the two. */
.auth-pane .pulse-admin-up,
.auth-pane .pulse-admin-down,
.auth-pane .pulse-admin-flat {
  font-size: 0.65rem;
  font-style: normal;
  font-weight: 600;
}

.auth-pane .pulse-admin-up { color: var(--sp-color-success, #2e7d32); }
.auth-pane .pulse-admin-down { color: var(--sp-color-error, #c62828); }
.auth-pane .pulse-admin-flat { color: var(--sp-ap-text-muted); }

/* ── tables ─────────────────────────────────────────────────────────────── */

/* Wide content scrolls inside its own box. Page-level horizontal scroll on the
   homepage would be a regression caused by a block most visitors never see. */
.auth-pane .pulse-admin-scroll {
  overflow-x: auto;
}

.auth-pane .pulse-admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.78rem;
}

.auth-pane .pulse-admin-table tbody tr:nth-child(even) {
  background: oklch(1 0 0 / 0.03);
}

.auth-pane .pulse-admin-table th {
  padding: 0.25rem 0.4rem 0.25rem 0;
  color: var(--sp-ap-text-muted);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-align: left;
  text-transform: uppercase;
  border-bottom: 1px solid var(--sp-ap-border);
}

.auth-pane .pulse-admin-table td {
  padding: 0.25rem 0.4rem 0.25rem 0;
  color: var(--sp-ap-text-dim);
  border-bottom: 1px solid color-mix(in oklab, var(--sp-ap-border) 45%, transparent);
}

/* The first column is a name and may be long — a tool id, a URL. It truncates;
   every other column is a figure and must stay readable. */
.auth-pane .pulse-admin-table td:first-child {
  max-width: 14rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.auth-pane .pulse-admin-table td.num {
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

.auth-pane .pulse-admin-empty {
  margin: 0;
  color: var(--sp-ap-text-muted);
  font-size: 0.72rem;
}

/* ── sparkline ──────────────────────────────────────────────────────────── */

/* Bars, not a line: the series is one count per day, and a line between two
   days draws values at 3am that were never measured. */
.auth-pane .pulse-admin-spark {
  display: flex;
  align-items: flex-end;
  gap: 1px;
  height: 2.5rem;
  padding: 0.15rem;
  border: 1px dashed var(--sp-ap-border);
  border-radius: var(--sp-radius-sm, 4px);
}

.auth-pane .pulse-admin-spark span {
  flex: 1 1 0;
  min-width: 1px;
  background: color-mix(in oklab, var(--sp-ap-accent, currentColor) 55%, transparent);
  border-radius: 1px 1px 0 0;
}

/* The homepage stage: one full-bleed dark field, the terminal and the account
 * pane floating on it as glass, and everything utilitarian below the fold.
 *
 * `home-split.css` owns the split grid. This file owns the field itself, the
 * header's disappearing act, and the band under the hero. They are separate
 * because the grid is a layout and this is a look — and only this one is
 * scoped, wholesale, to `body.home-split-body`.
 *
 * Brand orange appears here as *light*, never as paint: two soft off-screen
 * glows. That keeps the field neutral enough for the chain rail's green/red
 * pips and the amber approval card to stay the loudest things on the page. */

:root:has(body.home-split-body),
.home-split-body {
  --sp-stage-ink: oklch(0.16 0.012 55);
  --sp-stage-glow: oklch(0.62 0.19 47);
  --sp-stage-glass: oklch(0.24 0.012 55 / 0.58);
  --sp-stage-glass-border: oklch(1 0 0 / 0.09);
  --sp-stage-blur: 24px;

  /* What separates glass from a translucent rectangle is the edge: real glass
     catches light along its top rim and loses it toward the bottom. One
     gradient, painted on a 1px inset ring, does the whole job — and it is a
     token rather than a rule because both panes need the identical edge or
     they read as two different materials. */
  --sp-stage-specular:
    linear-gradient(160deg,
      oklch(1 0 0 / 0.34) 0%,
      oklch(1 0 0 / 0.06) 26%,
      oklch(1 0 0 / 0) 52%,
      oklch(1 0 0 / 0.05) 100%);
  --sp-stage-rim: oklch(0.62 0.19 47 / 0.28);
  --sp-stage-text: oklch(0.96 0.004 60);
  --sp-stage-text-dim: oklch(0.82 0.008 60);
  --sp-stage-text-muted: oklch(0.68 0.01 60);

  /* Brand orange as light, not paint. Declared once and used twice — on the
     root, and again over the video in `.stage-video-layer::after`. */
  --sp-stage-glows:
    radial-gradient(60rem 40rem at 12% -10%, oklch(0.62 0.19 47 / 0.22), transparent 60%),
    radial-gradient(50rem 35rem at 88% 60%, oklch(0.62 0.19 47 / 0.13), transparent 60%);
}

/* The field lives on the root, not on the body, so the band below the fold
   shares it with no seam — and, critically, so it cannot paint *over* the video.
   `head-assets.html` sets `html { background-color }` for anti-FOUC, and a root
   that has a background is exactly the condition under which the body's own
   background stops propagating to the canvas: it would paint on the body box
   instead, above `.stage-video-layer`'s `z-index: -1`, hiding the footage
   completely. Putting it here keeps it behind the layer by construction.
   This is also the fallback the page falls back to if the video never arrives. */
:root:has(body.home-split-body) {
  background: var(--sp-stage-glows), var(--sp-stage-ink);
  background-attachment: fixed;
}

.home-split-body {
  background: transparent;
  /* Native form controls and scrollbars follow; the stage is always dark. */
  color-scheme: dark;
  color: var(--sp-stage-text);
  /* The page scrolls now; only the horizontal overflow is unwanted. */
  overflow-x: hidden;
}

/* ── the video field ────────────────────────────────────────────────────── */

/* Fixed and behind everything, so the hero and the band below it share one
   continuous field with no seam at the fold and nothing to keep in sync while
   scrolling. `z-index: -1` puts it under the page's own content without taking
   it out of the body's stacking context, so the orange glows above composite
   over it rather than being hidden by it. */
/* The poster is set here as well as on the <video> so that first paint has an
   image whichever one the browser gets to first. Needs core >= 0.25.0, which is
   where `resolve_mime_type` learned image/webp — these responses carry
   `nosniff`, so an unmapped extension does not degrade, it fails to decode. */
.stage-video-layer {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  background: var(--sp-stage-ink) url("/files/video/hero/hero-poster.webp") center / cover no-repeat;
}

.stage-video-layer .hero-video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Starts invisible over its own poster and crossfades once it can play, so a
     half-buffered first frame never pops. hero-header.js adds `.is-ready`. */
  opacity: 0;
  transition: opacity 900ms ease;
}

.stage-video-layer .hero-video.is-ready {
  opacity: 1;
}

/* The scrim is the reason any of this is readable.
   Adapted from the hero implementation this replaces, where it was tuned for
   exactly this problem: light text over an image that will not hold still. The
   blur takes the detail out of the footage so the eye stops trying to resolve
   it, and the gradients weight the darkening toward the top, where the header
   sits, and the bottom, where the band arrives. */
/* The glows again, this time above the footage. `background-attachment: fixed`
   on the body cannot reach here, but the layer is itself fixed, so they hold
   the same position on screen either way. */
.stage-video-layer::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--sp-stage-glows);
  z-index: 2;
  pointer-events: none;
}

.stage-video-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  /* The top and bottom stay heavy, because that is where the header and the
     band land; the middle lifts, because that is the only part of the frame
     anyone sees move. */
  backdrop-filter: blur(6px) saturate(1.08);
  -webkit-backdrop-filter: blur(6px) saturate(1.08);
  background:
    linear-gradient(180deg,
      oklch(0.12 0.012 55 / 0.80) 0%,
      oklch(0.12 0.012 55 / 0.42) 32%,
      oklch(0.12 0.012 55 / 0.42) 68%,
      oklch(0.12 0.012 55 / 0.86) 100%),
    linear-gradient(90deg,
      oklch(0.12 0.012 55 / 0.42) 0%,
      transparent 55%);
}

/* ── the glass edge ─────────────────────────────────────────────────────── */

/* Both panes get the same rim from here rather than each from its own file,
   because the thing that makes two floating surfaces read as one sheet of
   glass is that the light hits them identically. `mask-composite` paints the
   gradient only in the 1px ring, so this is an edge and not a wash over the
   content — and it sits on ::before at z-index 0 so the terminal's own
   children, which set their own stacking, stay above it. */
.home-split-body .pi-terminal .terminal,
.home-split-body .auth-pane .pane {
  position: relative;
}

.home-split-body .pi-terminal .terminal::before,
.home-split-body .auth-pane .pane::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  border-radius: inherit;
  padding: 1px;
  background: var(--sp-stage-specular);
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}

/* On a dark field the white wordmark is the right asset, and it is already in
   the header markup — see the `.logo-video` rule in header-core.css. */
.home-split-body .logo-light,
.home-split-body .logo-dark { display: none; }
.home-split-body .logo-video { display: block; }

/* ── header ─────────────────────────────────────────────────────────────── */

/* Over the stage the header is a logo and nothing else: it dissolves into the
   field rather than sitting on it as a white bar. */
.home-split-body .site-header {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-bottom-color: transparent;
  box-shadow: none;
}

/* The brand hairline belongs to the glass bar, not to the dissolved one. */
.home-split-body .site-header::after { opacity: 0; }
[data-scrolled] .home-split-body .site-header::after { opacity: 0.55; }

.home-split-body .hamburger-line { background: var(--sp-stage-text); }

/* Once the hero has scrolled past, the header needs its own surface again or
   the logo lands on top of the band's content. `hero-header.js` sets the
   attribute on <html>. */
[data-scrolled] .home-split-body .site-header {
  background: oklch(0.16 0.012 55 / 0.72);
  backdrop-filter: blur(var(--sp-stage-blur)) saturate(160%);
  -webkit-backdrop-filter: blur(var(--sp-stage-blur)) saturate(160%);
  border-bottom-color: var(--sp-stage-glass-border);
}

/* `display: none` rather than visual hiding: the nav links are genuinely gone
   here, so they leave the tab order and the a11y tree with them. The actions
   stay — the header is the wordmark and the three CTAs, nothing else. */
.home-split-body .nav-links,
.home-split-body .mobile-menu-toggle,
.home-split-body .mobile-menu-overlay { display: none; }

/* The ghost CTAs need the stage's light ink and glass edge, not the
   light-theme grey. */
.home-split-body .nav-cta-link {
  color: var(--sp-stage-text-dim);
  border-color: var(--sp-stage-glass-border);
}

.home-split-body .nav-cta-link:hover,
.home-split-body .nav-cta-link:focus-visible {
  color: var(--sp-stage-text);
  border-color: var(--sp-stage-text-muted);
}

/* The expand toggle and the expanded state.
 *
 * Expanded is a fixed overlay rather than :fullscreen — same approach as the
 * artifact overlay — so the browser chrome stays, Escape is ours to handle,
 * and nothing renegotiates the stream. The field is solid stage ink: over the
 * moving video the shell is glass, but at full bleed the footage behind the
 * transcript is noise, not atmosphere. */

.pi-expand-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.1rem 0.5rem;
  border: 1px solid color-mix(in oklch, var(--sp-color-terminal-text-dim) 40%, transparent);
  border-radius: var(--sp-radius-full);
  background: color-mix(in oklch, var(--sp-color-terminal-text-dim) 8%, transparent);
  color: var(--sp-color-terminal-text-dim);
  font-family: var(--sp-font-mono);
  font-size: 0.66rem;
  white-space: nowrap;
  cursor: pointer;
}

.pi-expand-chip svg {
  width: 0.7rem;
  height: 0.7rem;
  flex: none;
}

.pi-expand-chip:hover,
.pi-expand-chip[aria-pressed='true'] {
  color: var(--sp-color-terminal-text);
  border-color: color-mix(in oklch, var(--sp-color-terminal-text-dim) 70%, transparent);
}

.pi-expand-chip:focus-visible {
  outline: 2px solid var(--sp-color-primary);
  outline-offset: 1px;
}

.pi-terminal.pi-terminal--expanded {
  position: fixed;
  inset: 0;
  z-index: var(--sp-z-modal);
  display: flex;
  flex-direction: column;
  max-width: none;
  margin: 0;
  padding: 0;
  background: oklch(0.12 0.01 55);
}

/* Full bleed means no shell: the glass, the rim, the border, and the brand
   corner all belong to a floating surface, and this one is the page. The
   selector carries .home-split-body so it outweighs the stage's glass rule. */
.pi-terminal.pi-terminal--expanded .terminal,
.home-split-body .pi-terminal.pi-terminal--expanded .terminal {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  height: 100%;
  border: 0;
  border-radius: 0;
  background: oklch(0.12 0.01 55);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: none;
}

.home-split-body .pi-terminal.pi-terminal--expanded .terminal::before {
  content: none;
}

.pi-terminal.pi-terminal--expanded .pi-body-wrap {
  display: flex;
  flex: 1 1 auto;
  min-height: 0;
}

.pi-terminal.pi-terminal--expanded .terminal-body {
  flex: 1 1 auto;
  min-height: 0;
  max-height: none;
}

/* Full screen is a reading surface. The transcript centres into a bounded
   column — on a wide monitor the tool rows otherwise stretch the full glass
   while the prose strands at the left edge, and the eye has to travel between
   them. Type steps up with the room. */
.pi-terminal.pi-terminal--expanded .terminal-body {
  padding-block: var(--sp-space-6);
  padding-inline: max(var(--sp-space-6), calc((100% - 110ch) / 2));
}

.pi-terminal.pi-terminal--expanded .pi-prose {
  font-size: 1rem;
  line-height: 1.85;
}

/* Expanded, the composer is the instrument, not a seam: bigger type, a
   multi-line field, and its own lifted surface so it reads as "type here"
   from across the room. The min-height wins over autogrow's inline height,
   so the field stays tall while empty and still grows past three lines. */
.pi-terminal.pi-terminal--expanded .pi-composer {
  --pi-composer-size: 1rem;
  --pi-composer-leading: 1.55;
  padding-block: var(--sp-space-5);
  padding-inline: max(var(--sp-space-6), calc((100% - 110ch) / 2));
  background: oklch(0.155 0.012 55);
  border-top: 1px solid var(--sp-stage-glass-border, var(--sp-color-terminal-border));
}

.pi-terminal.pi-terminal--expanded .pi-input {
  min-height: calc(3 * var(--pi-composer-size) * var(--pi-composer-leading));
  cursor: text;
}

.pi-terminal.pi-terminal--expanded .pi-composer:focus-within {
  background: color-mix(in oklch, var(--sp-color-primary) 7%, oklch(0.155 0.012 55));
}

body.pi-terminal-expanded {
  overflow: hidden;
}

/* The compatibility marquee: one sliding band between the hero and the band
 * below, saying who the gateway speaks to.
 *
 * Adapted from systemprompt-web's `deploy-marquee` — same mechanics: a track of
 * four identical groups sliding -25% for a seamless loop, edge-masked so names
 * enter and leave as light rather than popping at a hard edge, paused on hover
 * and focus, and static (single group, wrapped) under reduced motion.
 *
 * Items are spans, not links: this band states compatibility, it does not
 * endorse or send traffic. Colours are the stage tokens declared on
 * `body.home-split-body` in home-stage.css, so the band cannot drift from the
 * glass panes above it. */

.compat-marquee {
  position: relative;
  overflow: hidden;
  padding: var(--sp-space-4) 0;
  border-top: 1px solid var(--sp-stage-glass-border);
  -webkit-mask-image: linear-gradient(90deg, transparent, black 6%, black 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, black 6%, black 94%, transparent);
}

.compat-marquee-track {
  display: flex;
  width: max-content;
  animation: compat-marquee-scroll 40s linear infinite;
}

.compat-marquee:hover .compat-marquee-track,
.compat-marquee:focus-within .compat-marquee-track {
  animation-play-state: paused;
}

.compat-marquee-group {
  display: flex;
  align-items: center;
  gap: var(--sp-space-8);
  padding-right: var(--sp-space-8);
  white-space: nowrap;
}

.compat-marquee-label {
  font-family: var(--sp-font-brand, inherit);
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--sp-stage-glow);
}

/* Items link out to the thing they name. They dress as quiet text, not as
   links — the underline would draw fifty-six lines across the band. */
.compat-marquee-item {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-space-2);
  font-family: var(--sp-font-brand, inherit);
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--sp-stage-text-dim);
  text-decoration: none;
  opacity: 0.85;
}

.compat-marquee-item:hover,
.compat-marquee-item:focus-visible {
  color: var(--sp-stage-text);
  opacity: 1;
}

/* Third-party marks arrive in their own colours and proportions; the band
   wants one voice and one optical size. Flattened to the stage's light ink,
   boxed to the same square so differing viewBoxes still sit inline. */
.compat-marquee-logo {
  width: 1.5rem;
  height: 1.5rem;
  object-fit: contain;
  filter: brightness(0) invert(1);
  opacity: 0.8;
}

/* A quiet divider between the agents and the providers, so the one band reads
   as two lists rather than a soup. */
.compat-marquee-sep {
  width: 1px;
  height: 1rem;
  background: var(--sp-stage-glass-border);
}

@keyframes compat-marquee-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-25%); }
}

@media (prefers-reduced-motion: reduce) {
  .compat-marquee-track {
    animation: none;
    width: auto;
    flex-wrap: wrap;
    justify-content: center;
  }

  .compat-marquee-group[aria-hidden="true"] {
    display: none;
  }

  .compat-marquee-group {
    flex-wrap: wrap;
    justify-content: center;
    padding: 0 var(--sp-space-4);
    white-space: normal;
    row-gap: var(--sp-space-2);
  }
}

/* Dialog built lazily by js/components/video-modal.js. */

.video-modal {
  position: fixed;
  inset: 0;
  margin: auto;
  border: none;
  padding: 0;
  background: transparent;
  width: 100%;
  max-width: min(960px, 92vw);
  height: max-content;
  max-height: 90vh;
  color: var(--sp-color-text);
}

.video-modal::backdrop {
  background: oklch(0 0 0 / 0.78);
  backdrop-filter: blur(6px);
}

.video-modal[open] {
  animation: sp-video-modal-in 200ms ease-out;
}

@keyframes sp-video-modal-in {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

.video-modal__inner {
  background: var(--sp-color-surface);
  border: 1px solid var(--sp-color-border);
  border-radius: var(--sp-radius-lg);
  overflow: hidden;
  box-shadow: 0 20px 60px oklch(0 0 0 / 0.4);
}

.video-modal__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-space-4);
  padding: var(--sp-space-3) var(--sp-space-4);
  border-bottom: 1px solid var(--sp-color-border);
}

.video-modal__title {
  margin: 0;
  font-family: var(--sp-font-brand);
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--sp-color-text);
}

.video-modal__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.25rem;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--sp-radius-md);
  color: var(--sp-color-text-secondary);
  cursor: pointer;
  transition: color 0.18s ease, background 0.18s ease, border-color 0.18s ease;
}

.video-modal__close:hover,
.video-modal__close:focus-visible {
  color: var(--sp-color-text);
  background: var(--sp-color-surface-alt);
  border-color: var(--sp-color-border);
  outline: none;
}

.video-modal__frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
}

.video-modal__frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

@media (max-width: 640px) {
  .video-modal {
    max-width: 100vw;
    margin: 0;
  }

  .video-modal__inner {
    border-radius: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .video-modal[open] { animation: none; }
}
