/*
 * Terminal demo. Owned by web-terminal; loaded after design-tokens.css and
 * site.css. Every colour is a --p-* token and nothing here defines one; every
 * class is prefixed `pterm`, so it cannot collide with site.css.
 *
 * The window carries `data-theme="dark"`, which scopes the kit's OWN dark token
 * values to it - a terminal is dark in both page themes, and the light-theme
 * --p-warning measures 2.9:1 on that background.
 *
 * THREE RENDER STATES, and the order matters:
 *
 *   1. No `.pterm--ready`  - static. Every step in normal document flow, all
 *      output visible. This is what a visitor without JavaScript sees and it
 *      is the default, so a failed script degrades to readable content rather
 *      than a blank box.
 *   2. `.pterm--ready`     - animated. Steps are stacked absolutely inside a
 *      box whose height JS pins to the tallest step, so switching step causes
 *      no reflow anywhere on the page.
 *   3. reduced motion - `prefers-reduced-motion: reduce` returns the component
 *      to state 1: every step in flow, every line revealed, nothing hidden. A
 *      reduced-motion visitor gets ALL FIVE transcripts, not just the one the
 *      animation would have ended on, because setting an accessibility
 *      preference must not cost you content. Nothing autoplays. JS adds
 *      `.pterm--motion-ok` if the visitor presses Play, which releases the
 *      media query and lets the animation run on request.
 *
 * Hidden steps and unrevealed lines use `opacity`, never `visibility` or
 * `display`, because both of those drop content out of the accessibility tree.
 * A screen reader reads all five transcripts in full at any moment.
 */

.pterm {
  container-type: inline-size;
  container-name: pterm;
  margin: 0;
}

.pterm-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: var(--p-space-8);
  /* Consistent window size, not stretched to the caption column: stretching left
     ~460px of empty black under a 3-line transcript. */
  align-items: start;
}

/* One column until two would actually fit. 1131px, not a round 900, because the
   terminal gets only ~57% of the grid: a 900px container gives a two-column
   window 575px usable against the 607px that 84 columns need at the 12px floor,
   where ONE column gives it 826px. The old 900px threshold scrolled from a 981px
   container to ~1109px, a band none of 360/768/1280/1920 touches. Two columns
   first fit at 1132px: 620px against 617px. Re-measure if a transcript widens. */
@container pterm (max-width: 1131px) {
  .pterm-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--p-space-6);
  }
}

/* ---------------------------------------------------------------- terminal */

.pterm-main { display: flex; flex-direction: column; min-width: 0; }


.pterm-window {
  position: relative;
  display: flex;
  flex-direction: column;
  /* Fixed, not a floor: the screen inside scrolls, so a taller box buys nothing
     and an empty one looks broken. */
  height: clamp(320px, 56vh, 580px);
  min-width: 0;
  border: 1px solid var(--p-border);
  border-radius: var(--p-radius-md);
  /* The window carries data-theme="dark", so every token inside it resolves to
     the kit's dark value. A terminal is dark in both page themes. */
  background: var(--p-bg);
  box-shadow: var(--p-shadow);
  overflow: hidden;
}

/* Sideways-scroll cue, present only while there is more to the right. At 360px a
   Rich table cannot fit at legible type, and a scrollable region with no
   affordance is invisible. On the window so it does not scroll away. */
.pterm-window::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: var(--p-space-8);
  opacity: 0;
  pointer-events: none;
  background: linear-gradient(to right, transparent, var(--p-bg));
  /* No transition: a fade here would animate under `reduce`. */
}

.pterm--scrollable .pterm-window::after { opacity: 1; }

.pterm-chrome {
  display: flex;
  align-items: center;
  gap: var(--p-space-3);
  padding: var(--p-space-3) var(--p-space-4);
  border-bottom: 1px solid var(--p-border);
  background: var(--p-surface);
}

.pterm-dots {
  display: inline-flex;
  gap: 6px;
  flex: none;
}
.pterm-dots i { width: 10px; height: 10px; border-radius: 50%; background: var(--p-border); }

.pterm-chrome-title {
  margin: 0;
  font-family: var(--p-mono);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  color: var(--p-accent);
}

.pterm-screen {
  /* A fixed viewport onto text taller than itself, like a real terminal.
     `flex: 1 1 0` with `min-height: 0` sizes it from the space the window gives
     it, NOT its content: with `flex-basis: auto` the tallest transcript pushed
     the window to 1,714px and the page shifted by 0.48 CLS. */
  flex: 1 1 0;
  min-height: 0;
  padding: var(--p-space-4);
  overflow: auto;
  scrollbar-gutter: stable;
  font-family: var(--p-mono);
  /* Ligatures OFF. Cascadia Code and SF Mono both draw `--` as one long dash,
     which turns `--dry-run` into `—dry-run` and misrepresents the command a
     visitor is being shown. A transcript has to render character for
     character. */
  font-variant-ligatures: none;
  font-feature-settings: 'liga' 0, 'clig' 0, 'calt' 0;
  /* Every transcript was captured at COLUMNS=84 and the widest line is 84. JS
     replaces this with an exact measured size so all 84 fit the content box and
     nothing clips; the clamp is the no-JS fallback. Below about 12px the screen
     scrolls sideways rather than reflowing a Rich table into something the tool
     never printed. */
  font-size: clamp(12px, 0.66cqi + 5px, 13px);
  /* Tight, like a terminal. Verified at 3x zoom that the box-drawing verticals
     form a continuous rule at this value once `.pterm-line` stopped
     double-spacing them. */
  line-height: 1.2;
  color: var(--p-text);
}

.pterm-screen:focus-visible {
  outline: 2px solid var(--p-focus);
  outline-offset: -2px;
}

.pterm-steps {
  position: relative;
  margin: 0;
  padding: 0;
  list-style: none;
}

.pterm-step + .pterm-step {
  margin-top: var(--p-space-6);
}

.pterm--ready .pterm-steps {
  /* Pinned by JS to the current step's own height, so the scroll extent inside
     the window matches what is on screen. The window has a fixed height, so
     this never moves anything on the page. */
  height: var(--pterm-steps-height, auto);
}

.pterm--ready .pterm-step {
  position: absolute;
  inset: 0 auto auto 0;
  width: 100%;
  margin-top: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--p-duration) ease;
}

.pterm--ready .pterm-step.is-current {
  opacity: 1;
  pointer-events: auto;
}

@media (prefers-reduced-motion: reduce) {
  /* Reduced motion must not cost content. Measured before this block was widened:
     step opacities sat at [0, 0, 0, 0, 1] indefinitely, so 4 of 5 transcripts were
     invisible - 1,443 of 8,379 characters, 17%. Asking for less animation returned
     less information, the inverse of the intent.

     Dropping the transition alone was not enough, and `opacity: 1` alone would be
     worse: the steps are absolutely stacked so the reveal can cross-fade them in
     place, so five transcripts would pile on top of one another. With no reveal there
     is no reason to stack, so un-stack them and let all five read as a list. The
     container's height is pinned by JS to the current step, and that must be released
     too or the list is clipped to one step's worth. */
  /* `.pterm--motion-ok` is added by JS only when the visitor presses Play or
     Replay. Nothing autoplays under `reduce`; an explicit request is consent,
     and without this gate the button would be a control that does nothing. */
  .pterm--ready:not(.pterm--motion-ok) .pterm-steps {
    height: auto;
  }

  .pterm--ready:not(.pterm--motion-ok) .pterm-step {
    position: static;
    opacity: 1;
    pointer-events: auto;
    transition: none;
  }

  /* Separate them, since they are no longer distinguished by being revealed. */
  .pterm--ready:not(.pterm--motion-ok) .pterm-step + .pterm-step {
    margin-top: var(--p-space-6);
    padding-top: var(--p-space-6);
    border-top: 1px solid var(--p-border);
  }

  /* A step counter means nothing when every step is on screen at once. */
  .pterm--ready:not(.pterm--motion-ok) .pterm-progress {
    display: none;
  }
}

.pterm-cmd {
  position: relative;
  margin: 0 0 var(--p-space-2);
  white-space: pre;
}

.pterm-prompt {
  color: var(--p-accent);
  user-select: none;
}

.pterm-cmd code {
  /* `width` in ch is how the typing is done: the full command is always in the
     DOM and the box that clips it grows. No text node is ever rewritten, so a
     screen reader reads the whole command and a copy-paste gets all of it. */
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  vertical-align: bottom;
  white-space: pre;
  font: inherit;
  color: inherit;
}

.pterm--ready .pterm-step.is-typing .pterm-cmd code {
  width: var(--pterm-typed, 0ch);
}

/* Out of flow, and moved with `transform` rather than `left`: an in-flow caret
   stepping one character at a time reports a layout shift on every frame, and
   so does an absolutely positioned one whose `left` changes. A transform does
   not. The prompt is exactly two monospace characters wide. */
.pterm-caret {
  position: absolute;
  top: 0.18em;
  left: 2ch;
  display: none;
  width: 0.55em;
  height: 1.15em;
  background: var(--p-accent);
  transform: translateX(var(--pterm-typed, 0ch));
}

.pterm--ready .pterm-step.is-typing .pterm-caret,
.pterm--ready .pterm-step.is-running .pterm-caret {
  display: inline-block;
  animation: pterm-blink 1s steps(1, end) infinite;
}

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

@media (prefers-reduced-motion: reduce) {
  .pterm-caret {
    animation: none !important;
  }
}

.pterm-out {
  margin: 0;
  font: inherit;
  white-space: pre;
  color: inherit;
}

/* INLINE, deliberately. A real newline follows each span inside the <pre>, which
   produces the break and keeps a copy-paste's lines. `display: block` added a
   SECOND break: pitch measured 32.88px against a 16.44px line box, so every table
   row and frame was double-spaced. That, not font metrics, is why the panel
   verticals rendered as isolated dashes. */
.pterm-line {
  display: inline;
}

.pterm--ready .pterm-line,
.pterm--ready .pterm-elided {
  transition: opacity 90ms linear;
}

/* Nothing has been printed yet while the command is still being typed or is
   "running". The text is present and in the accessibility tree the whole
   time; only its opacity changes, and its box never moves. */
.pterm--ready .pterm-step.is-typing .pterm-line,
.pterm--ready .pterm-step.is-typing .pterm-elided,
.pterm--ready .pterm-step.is-running .pterm-line,
.pterm--ready .pterm-step.is-running .pterm-elided,
.pterm--ready .pterm-line.is-pending,
.pterm--ready .pterm-elided.is-pending {
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  .pterm--ready .pterm-line,
  .pterm--ready .pterm-elided {
    transition: none;
  }
}

/* Our annotation, never tool output. Deliberately not monospace and not the
   terminal's text colour so it cannot be mistaken for something Proteus
   printed. */
.pterm-elided {
  display: block;
  margin: var(--p-space-2) 0;
  padding-left: var(--p-space-3);
  border-left: 2px solid var(--p-accent);
  font-family: var(--p-font);
  font-size: 0.82rem;
  font-style: italic;
  line-height: 1.5;
  white-space: normal;
  color: var(--p-accent);
}

.pterm-elided::before {
  content: '⋯ ';
  font-style: normal;
}

/* ------------------------------------------------------------------- aside */

.pterm-side {
  min-width: 0;
}

.pterm-captions {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--p-space-4);
}

/* The current caption is marked with the accent rule and a tinted panel, NOT by
   dimming the others: dimming muted text to 45% took it to 1.8:1. */
.pterm-caption {
  padding: var(--p-space-2) var(--p-space-4);
  border-left: 2px solid var(--p-border);
  border-radius: 0 var(--p-radius-sm) var(--p-radius-sm) 0;
  color: var(--p-muted);
  transition: border-color var(--p-duration) ease, background var(--p-duration) ease;
}

.pterm--ready .pterm-caption.is-current {
  border-left-color: var(--p-accent);
  background: var(--p-surface-alt);
}

.pterm--ready .pterm-caption.is-current .pterm-caption-body {
  color: var(--p-text);
}

@media (prefers-reduced-motion: reduce) {
  .pterm-caption {
    transition: none;
  }
}

.pterm-caption-label {
  margin: 0 0 var(--p-space-1);
  font-family: var(--p-mono);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--p-accent);
}

.pterm-caption-title {
  margin: 0 0 var(--p-space-2);
  font-size: 1.05rem;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--p-text);
}

.pterm-caption-body {
  margin: 0;
  font-size: 0.94rem;
  line-height: 1.6;
  color: var(--p-muted);
  transition: color var(--p-duration) ease;
}

.pterm-caption-body p { margin: 0; }

/* ---------------------------------------------------------------- controls */

.pterm-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--p-space-3);
  margin-top: var(--p-space-4);
}

.pterm-progress { margin-left: auto; }

/* Injected by JS. Absent without it, because a pause button that pauses
   nothing is a lie. */
.pterm-controls[hidden] {
  display: none;
}

/* One solid accent primary, one outline secondary, as the brand kit has it. */
.pterm-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--p-space-2);
  min-height: 40px;
  /* Wide enough for the longest toggle label, so swapping Pause for Play cannot
     reflow the row - without it the end of the run shifted it 102px. */
  min-width: 92px;
  justify-content: center;
  padding: 0 var(--p-space-4);
  border: 1px solid var(--p-border);
  border-radius: 999px;
  background: var(--p-surface);
  font-family: var(--p-font);
  font-size: 0.88rem;
  font-weight: 600;
  color: var(--p-text);
  cursor: pointer;
  transition: background var(--p-duration) ease, border-color var(--p-duration) ease;
}

.pterm-btn--primary {
  border-color: var(--p-accent);
  background: var(--p-accent);
  color: var(--p-on-accent);
}

.pterm-btn:hover {
  border-color: var(--p-accent);
  background: var(--p-surface-alt);
}

.pterm-btn--primary:hover {
  background: var(--p-accent);
  filter: brightness(1.08);
}

.pterm-btn:focus-visible {
  outline: 2px solid var(--p-focus);
  outline-offset: 2px;
}

.pterm-progress {
  margin: 0;
  font-family: var(--p-mono);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  color: var(--p-muted);
}

@media (prefers-reduced-motion: reduce) {
  .pterm-btn {
    transition: none;
  }
}
