/* ============================================================================
   summit-ui.css — the shared UI system for Summit's product surfaces.

   WHY THIS EXISTS (2026-08-20). Every page carried its own <style> block, which
   is how student.html and probe.html ended up with different palettes for the
   same product. A visual direction is a SYSTEM — one spacing scale, one set of
   form patterns, one contrast rule — and it only holds if both surfaces draw
   from one file. Styling each page separately is how they drift again.

   THE DIRECTION (Taylor ruled it 2026-08-20, "direction 2", for BOTH the student
   and professor surfaces). Squared corners, hard rules, heavy top band, forms
   that state plainly what they want. The visual argument is that this is
   infrastructure a department already trusts — nothing decorative.

   WHAT IT BORROWS AND WHAT IT DOESN'T. The STRUCTURE comes from the US Web
   Design System (public domain, US federal work): the spacing scale, the form
   patterns, the contrast and hit-target rules. Those are the tedious, easy-to-
   get-wrong parts and they exist to be reused. The IDENTITY is Summit's: the
   brand blue, clay/amber/held, and the serif from the probe report. Per CLAUDE.md
   § "UI design, borrowed styles, and licensing" — a design system is structure,
   not identity, and the layering is what makes it ours.

   ⚠ NO AFFILIATION CUES. USWDS's own guidance asks that reuse not imply
   government endorsement: no flag banner, no "official website of the United
   States government" strip, no federal seals, no .gov styling. By the same
   principle, no real institution's name, logo, or domain anywhere in Summit's
   chrome — a course code is what belongs in the band.

   ANTI-"LOOKS AI" RULES BAKED IN, because the four tells are structural:
     1. Content is LEFT-ALIGNED. There is deliberately no centered-hero utility.
     2. Panels are squared and hairline-ruled — not a stack of soft rounded
        cards all wearing the same shadow.
     3. The accent appears ONLY on what you press. State color is a separate
        scale and never doubles as the accent.
     4. Headlines are serif and numbers are mono, so type carries hierarchy
        instead of size alone.

   NAMING. Everything is `su-` prefixed. Page-local styles still live in each
   page's <style> block; they must not redefine an su- class, only compose with
   it. When a page-local rule starts fighting one here, the rule belongs here.
   ============================================================================ */

:root {
  /* ── ground ─────────────────────────────────────────────────────────────
     Deliberately near-white rather than a tinted neutral: the institutional
     read comes from rules and squared edges, not from a colored page. */
  --su-bg:        #FFFFFF;
  --su-bg-alt:    #FAFCFF;   /* secondary column / zebra / inset */
  --su-bg-sunk:   #EFF5FF;

  /* ── WARMED TOWARD THE PROBE (2026-08-21) ──────────────────────────────
     The dashboards and the probe session had drifted into two design
     languages: neutral grey, near-black rules and square corners here; a
     blue-biased, soft, slightly rounded surface there. The probe is the one
     that has had real learners in front of it, and it is the one Taylor kept
     responding to — so the STRUCTURE stays (left-aligned, dense, full-bleed,
     one accent for actions) and the SURFACE comes from the probe.

     Every neutral now carries the probe's blue bias, taken from its own values
     (#0f1b2d / #4a5568 / #68758a / #d9e2f0 / #fafcff). A grey with a hue bias
     toward the accent reads as chosen; a pure neutral grey reads as default.

     ⚠ THE ONE PLACE THE PROBE'S VALUES ARE NOT COPIED: its #d9e2f0 border is
     1.31:1 against white. That is fine for a row separator, which is
     decorative, and it FAILS the 3:1 a control boundary needs — an input whose
     edge you cannot see is not a soft design, it is an invisible form field. So
     the hairline keeps the probe's softness and --su-line-strong (3.30:1)
     carries any edge that has to be findable. Same reasoning as the two-step
     state palette above: one value cannot do both jobs.

     #94a1b8, which the probe uses for hint text, is 2.61:1 and is NOT adopted
     for text anywhere. */
  --su-rule:      #1A1A3E;   /* was #1B1B1B near-black — now the brand navy */
  --su-hair:      #D9E2F0;   /* decorative separators only */
  --su-line-strong: #7D8FA8; /* control boundaries — 3.30:1 on white */

  --su-ink:       #0F1B2D;
  --su-mid:       #4A5568;
  --su-mute:      #68758A;   /* 4.67:1 — the floor for small text */

  /* ── identity ───────────────────────────────────────────────────────────
     Summit blue — the brand navy the rest of the product already uses for the
     wordmark and headings (#1A1A3E), with #2A2A52 as the fill so a large button
     is not a black slab. Replaces the federal navy the structure would suggest,
     and replaces the pen green this file shipped with for one commit: green read
     as a second state color next to held-green, which muddied the one rule that
     matters here — ACCENT IS FOR ACTIONS ONLY, state is a separate scale. */
  --su-accent:      #2A2A52;
  --su-accent-deep: #1A1A3E;
  --su-accent-soft: #ECEDF5;
  --su-on-accent:   #FFFFFF;

  /* ── state ──────────────────────────────────────────────────────────────
     A SEPARATE scale from the accent. gap / shaky / held is Summit's
     vocabulary and these three colors are what a reader remembers.

     TWO STEPS PER BAND, and the split is not cosmetic. A FILL only has to clear
     3:1 against the surface; TEXT has to clear 4.5:1. The amber that reads
     correctly as a bar (#D19412, 2.6:1) is unreadable as a numeral, and the
     amber that reads correctly as a numeral is muddy as a bar. One value cannot
     do both jobs, so each band ships the pair.

     RE-STEPPED 2026-08-20 after running the palette validator. The first version
     shipped #A0402F / #7A5410, which are ΔE 3.0 apart under deuteranopia and 9.4
     apart to NORMAL vision — a red-green colorblind professor could not tell
     "struggling" from "forming" on the class gap map, where the color IS the
     finding. The fills below pass the lightness band, chroma floor, CVD
     separation, normal-vision floor, and contrast checks.

     Color is still never the only channel: every band ships with its WORD and a
     bordered enclosure (.su-badge, .band-pill), which is what makes this safe on
     a washed-out projector as well as for a CVD reader. */
  --su-gap:        #8E2A20;   /* text  — 8.4:1 on white */
  --su-gap-fill:   #A32E24;   /* mark  */
  --su-gap-wash:   #F7E6E3;
  --su-shaky:      #7A5410;   /* text  — 6.8:1 on white */
  --su-shaky-fill: #D19412;   /* mark  */
  --su-shaky-wash: #FAF0DA;
  --su-held:       #0A5E45;   /* text  — 7.8:1 on white */
  --su-held-fill:  #08825E;   /* mark  */
  --su-held-wash:  #E0F0EA;

  /* ── liveness ───────────────────────────────────────────────────────────
     IN ONE LINE: the one warm colour Summit keeps, and it means exactly one
     thing -- THE MODEL IS WORKING RIGHT NOW.

     WHY IT SURVIVED when coral did not. Gold #d4a745 was doing three unrelated
     jobs (buttons and links, two taglines and a bullet, and the streaming caret).
     Ruled 2026-09-01: retired from the first two, kept for the third. theme.css:324
     had already noticed it was one job -- "same accent as the caret it stands in
     for" -- before there was a name for it.

     WHY IT IS NOT ANY EXISTING TOKEN. Not the accent: nothing here is pressable.
     Not a state: liveness says nothing about whether the learner is doing well.
     Not --su-progress: progress measures how far along something is, a caret
     just says the thing is alive. A warm pulse against an otherwise cool palette
     is legible precisely because nothing else in the system is warm.

     ONLY the streaming caret and the thinking dots. If it starts appearing on a
     button or a heading again, that is the drift this ruling exists to stop. */
  --su-live: #D4A745;

  /* ── on-dark accent ─────────────────────────────────────────────────────
     IN ONE LINE: the accent's counterpart for anything sitting ON the deep-accent
     band, where --su-accent IS the background and would render invisible.

     WHY IT EXISTS. Retiring coral 2026-09-01 exposed a hole: the dashboard's nav
     and hamburger hovers were coral BECAUSE coral was the only colour in the
     palette that showed up against the navy band. Swapping them to --su-accent
     would have made them disappear -- a hover state you cannot see is not a subtle
     design, it is a broken affordance, the same argument as --su-line-strong.

     A lighter step of the SAME navy family, so the band still reads as one
     surface. Use it for hover fills and borders on .su-band and anything else
     drawn on --su-accent-deep. It is NOT a second accent: nothing on light ground
     should ever take it. */
  --su-accent-on-dark: #4A4A7A;

  /* ── progress ───────────────────────────────────────────────────────────
     IN ONE LINE: a filled progress bar is neither an action nor a state, so it
     gets its own neutral token instead of borrowing the accent or a state colour.

     WHY IT IS NOT THE ACCENT. Accent means "press this". A progress bar is not
     pressable, and letting it take the accent is exactly the confusion that got
     coral #e85d4a retired on 2026-09-01 — one colour was the primary button AND
     the intake progress bar at the same time.

     WHY IT IS NOT A STATE COLOUR. --su-shaky-fill was the obvious substitute and
     it is wrong: shaky means WOBBLING. A bar filling up normally is not a warning,
     and painting it amber tells the learner something is going badly when nothing
     is. Ruled by Taylor 2026-09-01.

     WHY THIS VALUE. The mid ink, so a bar reads as measurement rather than mood —
     the same argument as serif headlines and mono numerals carrying hierarchy so
     colour does not have to. 6.97:1 against --su-bg-sunk, well clear of the 3:1
     a mark needs.

     ⚠ IN theme.css USE THE FALLBACK FORM — var(--su-progress, #4A5568). theme.css
     is linked by 29 pages and only 5 of them link this file, so a bare var() there
     resolves to nothing on 24 pages. */
  --su-progress: #4A5568;

  /* ── type ───────────────────────────────────────────────────────────────
     Serif headlines tie the dashboard to the probe report. Body is a system
     stack — Summit ships NO webfonts today, so no font license attaches (see
     public/lib/THIRD-PARTY.md).

     TO ADOPT PUBLIC SANS (public domain, USWDS's face): drop the .woff2 files
     into public/lib/fonts/, add the @font-face blocks, and prepend
     "Public Sans" to --su-sans. That is the ONLY change needed — nothing else
     in this file names a face. Public domain, so nothing to attribute. */
  --su-serif: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif;
  --su-sans:  -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --su-mono:  ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  /* ── spacing: a 4px scale, used everywhere ──────────────────────────────
     Hand-picked values are how rhythm dies. If a gap is not on this scale it
     is a mistake, not a decision. */
  --su-1: 4px;  --su-2: 8px;  --su-3: 12px; --su-4: 16px;
  --su-5: 24px; --su-6: 32px; --su-7: 48px; --su-8: 64px;

  /* PANELS stay squared — that is the density and the institutional read, and
     a stack of soft-rounded cards is the second generated-look tell. CONTROLS
     round, because a 0px input next to a 0px button is the harshest thing on a
     form and the probe's 8-11px is most of why it feels approachable. */
  /* Bubbles get their OWN radius so the squared-panel rule is bent in exactly one
     named place -- see the transcript component below for why. */
  --su-r-bubble: 12px;
  /* Code is the one surface allowed a dark ground; syntax needs the contrast. */
  --su-code-bg: #1E1E2E;
  --su-code-ink: #E6E6F0;
  --su-radius: 2px;
  --su-r-control: 6px;
  /* Touch target floor. Below this a control is unusable on a phone, which is
     how half of a pilot cohort will open Summit. */
  --su-tap: 44px;
}

/* ── base ──────────────────────────────────────────────────────────────── */
.su, .su * { box-sizing: border-box; }

.su {
  background: var(--su-bg);
  color: var(--su-ink);
  font-family: var(--su-sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* Focus is never removed, only restyled. A 2px offset ring on the accent
   clears every background in this system. */
.su :where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 3px solid var(--su-accent);
  outline-offset: 2px;
}

.su-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── band — the top strip ──────────────────────────────────────────────────
   Identity + context, nothing actionable. NO institution name, logo, or
   domain goes here; a course code is the correct level of specificity. */
.su-band {
  background: var(--su-accent-deep);
  color: var(--su-on-accent);
  padding: var(--su-2) var(--su-5);
  font-size: 13px;
  display: flex; align-items: center; gap: var(--su-3);
  flex-wrap: wrap;
}
/* THE MOUNTAIN, as identity rather than decoration.
   It was removed from the dashboards on 2026-08-20 along with the centered hero,
   and that was half right: a decorative graphic floating above a vague "Begin"
   was one of the four generated-look tells. The MARK is not that — it is the
   wordmark, it is the same geometry as public/lib/mountain.js and the favicon,
   and it belongs in the one strip every surface shares.

   A background-image rather than inline SVG so it lives in ONE place and every
   band picks it up — the dashboard, the instructor home, the class report, the
   probe arrival screen. (A CSS background cannot inherit currentColor, which is
   fine here: this band is always the deep accent, so the mark is always white.)
   The empty-state mountain is inline SVG instead, because it sits on light and
   needs to take its colour from context. */
.su-band b {
  font-weight: 700; letter-spacing: .1em; text-transform: uppercase; font-size: 12px;
  display: inline-flex; align-items: center; gap: 8px;
}
.su-band b::before {
  content: '';
  width: 17px; height: 17px; flex: 0 0 auto;
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2028%2028%22%3E%3Cpolygon%20points%3D%2214%2C4%2026%2C24%202%2C24%22%20fill%3D%22%23ffffff%22%20opacity%3D%220.45%22%2F%3E%3Cpolygon%20points%3D%2214%2C4%2020%2C24%208%2C24%22%20fill%3D%22%23ffffff%22%20opacity%3D%220.8%22%2F%3E%3Cpolygon%20points%3D%2214%2C4%2017%2C10%2011%2C10%22%20fill%3D%22%23ffffff%22%2F%3E%3C%2Fsvg%3E");
  background-size: contain; background-repeat: no-repeat; background-position: center;
}
.su-band .su-band-end { margin-left: auto; opacity: .85; font-size: 12.5px; }

/* ── page header — the hard rule under the title is the signature ───────── */
.su-pagehead {
  padding: var(--su-5) var(--su-5) var(--su-4);
  /* Was 3px of #1B1B1B. That single rule under every heading was doing most of
     the coldness — it reads as a ruled form, not a product. 2px of the brand
     navy keeps the structure it was drawn for and stops shouting. */
  border-bottom: 2px solid var(--su-rule);
}
.su-pagehead h1 {
  margin: 0 0 var(--su-1);
  font-family: var(--su-serif);
  font-size: clamp(24px, 3.2vw, 32px);
  font-weight: 600; letter-spacing: -.015em; line-height: 1.2;
  text-wrap: balance;
}
.su-pagehead p { margin: 0; color: var(--su-mid); font-size: 15px; max-width: 62ch; }

/* ── section label ─────────────────────────────────────────────────────── */
.su-label {
  display: block;
  font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--su-mute);
  margin-bottom: var(--su-2);
}

/* ── layout ────────────────────────────────────────────────────────────── */
.su-cols {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
}
.su-cell { padding: var(--su-5); min-width: 0; }
/* The second column is inset and hairline-separated — the institutional
   equivalent of a card, without the shadow. */
.su-cols > .su-cell + .su-cell {
  border-left: 1px solid var(--su-hair);
  background: var(--su-bg-alt);
}

/* A .su block that ends a page needs a closing rule and room under it, or the
   last row runs into the viewport edge and the page reads as cut off (Taylor,
   2026-08-20). The border is what says "this ended" rather than "this scrolled". */
.su-end {
  border-bottom: 2px solid var(--su-rule);
  margin-bottom: var(--su-6);
}

.su-panel {
  border: 1px solid var(--su-hair);
  background: var(--su-bg);
  padding: var(--su-4) var(--su-5) var(--su-5);
}

/* ── forms — say plainly what you want ─────────────────────────────────── */
.su-field { margin-bottom: var(--su-4); }
.su-field > label {
  display: block; font-weight: 600; font-size: 15px; margin-bottom: var(--su-1);
}
/* Help text sits ABOVE the input, not below it: below, it is read after the
   answer has already been typed, which is too late to help. */
.su-field .su-help { margin: 0 0 var(--su-2); font-size: 13.5px; color: var(--su-mid); }

.su-input {
  width: 100%;
  font: inherit; font-size: 16px;   /* 16px stops iOS Safari zooming on focus */
  min-height: var(--su-tap);
  padding: var(--su-2) var(--su-3);
  /* Was 2px of near-black. The probe's field is a 1px blue-grey with a soft
     focus glow, and that is the friendlier shape — but at its own #d9e2f0 the
     edge is invisible (1.31:1), so this uses the strong line instead. */
  border: 1px solid var(--su-line-strong);
  border-radius: var(--su-r-control);
  background: var(--su-bg);
  color: var(--su-ink);
}
.su-input::placeholder { color: var(--su-mute); }
/* The probe's focus treatment: the border takes the accent and a soft ring
   spreads behind it. Additive — the keyboard outline below is untouched,
   because a glow is a nicety and the outline is the accessibility guarantee. */
.su-input:focus {
  outline: none;
  border-color: var(--su-accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--su-accent) 14%, transparent);
}
.su-input.su-mono { font-family: var(--su-mono); font-size: 15px; }
.su-input[aria-invalid="true"] { border-color: var(--su-gap); }

/* Errors are announced, not just colored — color alone fails for a
   colorblind reader, and this is a form they cannot get past if it fails. */
.su-error {
  display: none;
  margin: var(--su-2) 0 0;
  padding-left: var(--su-3);
  border-left: 4px solid var(--su-gap);
  color: var(--su-gap); font-size: 14px; font-weight: 600;
}
.su-error.on { display: block; }

/* ── buttons ───────────────────────────────────────────────────────────── */
.su-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--su-2);
  font: inherit; font-size: 15px; font-weight: 700;
  min-height: var(--su-tap);
  padding: var(--su-2) var(--su-5);
  border: 2px solid var(--su-accent);
  border-radius: var(--su-r-control);
  background: var(--su-accent);
  color: var(--su-on-accent);
  cursor: pointer;
  text-decoration: none;
}
.su-btn:hover { background: var(--su-accent-deep); border-color: var(--su-accent-deep); }
.su-btn[disabled] { opacity: .55; cursor: default; }

.su-btn-secondary {
  background: transparent; color: var(--su-accent); border-color: var(--su-accent);
}
.su-btn-secondary:hover { background: var(--su-accent-soft); color: var(--su-accent-deep); }

/* A link that is genuinely a link, for demoted actions. Underlined because
   color alone is not an affordance. */
.su-link {
  background: none; border: 0; padding: 0; font: inherit;
  color: var(--su-accent); font-weight: 600; cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px;
}
.su-link:hover { color: var(--su-accent-deep); }

/* ── rows — the workhorse list ─────────────────────────────────────────────
   A list of things with a name, a state, a date, and a number. Used by both
   surfaces; the professor view adds columns rather than restyling. */
.su-rows { border-top: 1px solid var(--su-hair); }
.su-row {
  display: flex; align-items: baseline; gap: var(--su-3);
  padding: var(--su-3) 0;
  border-bottom: 1px solid var(--su-hair);
  color: inherit; text-decoration: none;
}
a.su-row:hover { background: var(--su-bg-alt); }
.su-row .su-row-name {
  font-weight: 600; font-size: 15px; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.su-row .su-row-sub { display: block; font-weight: 400; font-size: 13px; color: var(--su-mute); }
.su-row .su-row-end { margin-left: auto; flex: 0 0 auto; }
.su-row .su-row-date { font-size: 13px; color: var(--su-mute); flex: 0 0 auto; }

/* ── state: score box + badge ──────────────────────────────────────────────
   The bordered box is deliberate. A bare colored numeral relies on color to
   carry meaning; the box gives it a second channel (weight + enclosure) that
   survives a colorblind reader and a bad projector at a demo. */
.su-score {
  font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums;
  padding: 1px var(--su-2);
  border: 2px solid currentColor;
  flex: 0 0 auto;
}
.su-score.gap { color: var(--su-gap); }
.su-score.shaky { color: var(--su-shaky); }
.su-score.held { color: var(--su-held); }

.su-badge {
  display: inline-block;
  font-size: 11.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  padding: 2px var(--su-2);
  border: 1px solid currentColor;
  border-radius: 3px;
  flex: 0 0 auto;
}
.su-badge.gap { color: var(--su-gap); background: var(--su-gap-wash); }
.su-badge.shaky { color: var(--su-shaky); background: var(--su-shaky-wash); }
.su-badge.held { color: var(--su-held); background: var(--su-held-wash); }
.su-badge.none { color: var(--su-mute); background: var(--su-bg-alt); }

/* ── meters — the Wakatime-style bar, shared by both surfaces ──────────── */
.su-meter {
  height: 10px; background: var(--su-bg-sunk); overflow: hidden; flex: 0 0 auto;
}
.su-meter > i { display: block; height: 100%; background: currentColor; }
/* Meters take the FILL step — they are marks, not text. */
.su-meter.gap { color: var(--su-gap-fill); }
.su-meter.shaky { color: var(--su-shaky-fill); }
.su-meter.held { color: var(--su-held-fill); }

/* ── transcript — the conversation surface ────────────────────────────────
   WHY THIS IS IN THE SYSTEM (2026-09-01). index.html is roughly 60% transcript by
   area and was the last in-scope page that could not adopt summit-ui.css, because
   the system had no conversation component at all -- .su-rows is a list, not a
   dialogue. Migrating that page meant ruling on this first.

   THE ONE PLACE THE SQUARED RULE BENDS, DELIBERATELY. Everything else here is 2px
   because squared panels are the institutional read. A chat bubble is not a panel:
   it is a speech act, the rounding is what makes a transcript scannable as turns
   rather than as a table, and every product a learner has ever used says so. It
   takes --su-r-bubble, its own token, so bending the rule is a NAMED exception
   rather than a 12px that crept in. If a future surface wants squared bubbles it
   changes one value.

   COLOUR IS STILL EARNED. The learner's own turn takes the accent because it is
   the one thing on screen they authored; the assistant's turn is the sunk ground
   with a hairline, so a long reply is quiet. No gradients, no shadows, no second
   accent for "AI". */
.su-transcript {
  flex: 1; min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--su-5) var(--su-4);
  display: flex; flex-direction: column; gap: var(--su-4);
  background: var(--su-bg);
}

.su-msg {
  max-width: min(75%, 640px);
  padding: var(--su-3) var(--su-4);
  border-radius: var(--su-r-bubble);
  line-height: 1.55;
  font-size: 15px;
  overflow-wrap: anywhere;   /* a pasted URL must not widen the page */
}
/* The tail-corner flattening is what makes a turn read as pointing at its speaker. */
.su-msg.me {
  align-self: flex-end;
  background: var(--su-accent); color: var(--su-on-accent);
  border-bottom-right-radius: var(--su-2);
}
.su-msg.them {
  align-self: flex-start;
  background: var(--su-bg-alt); color: var(--su-ink);
  border: 1px solid var(--su-hair);
  border-bottom-left-radius: var(--su-2);
}

/* LIVENESS, and the only place --su-live is allowed (see its note above): the caret
   says the model is still producing, which is neither an action nor a state. */
.su-msg .su-caret {
  display: inline-block; width: 2px; height: 1em;
  background: var(--su-live);
  margin-left: 2px; vertical-align: text-bottom;
  animation: su-blink .8s step-end infinite;
}
@keyframes su-blink { 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .su-msg .su-caret { animation: none; }
}

/* Code keeps its own dark ground -- syntax needs the contrast, and it is the one
   surface where the page's near-white would hurt. It scrolls ITSELF rather than
   widening the transcript, which is the horizontal-overflow rule. */
.su-msg pre, .su-code {
  margin: var(--su-2) 0 0;
  padding: var(--su-3);
  border-radius: var(--su-r-control);
  background: var(--su-code-bg); color: var(--su-code-ink);
  font-family: var(--su-mono); font-size: 13.5px; line-height: 1.5;
  overflow-x: auto;
}
.su-msg :not(pre) > code {
  font-family: var(--su-mono); font-size: .92em;
  padding: 1px 4px; border-radius: 3px;
  background: var(--su-bg-sunk); color: var(--su-ink);
}
.su-msg.me :not(pre) > code {
  background: color-mix(in srgb, var(--su-on-accent) 18%, transparent);
  color: var(--su-on-accent);
}
.su-msg p { margin: 0 0 var(--su-2); }
.su-msg p:last-child { margin-bottom: 0; }

@media (max-width: 600px) {
  /* A 75% bubble on a phone wastes the narrow axis it cannot spare. */
  .su-msg { max-width: 88%; font-size: 14.5px; }
  .su-transcript { padding: var(--su-4) var(--su-3); gap: var(--su-3); }
}

/* ── empty state ───────────────────────────────────────────────────────── */
.su-empty {
  padding: var(--su-5); background: var(--su-bg-alt);
  border-left: 4px solid var(--su-hair);
  color: var(--su-mid); font-size: 14.5px;
}

/* A FIRST-RUN empty state, which is a different thing from "this list happens to
   be empty". Hiding the block entirely — what the dashboard did until now — means
   a brand-new account opens onto a form and nothing else, which is the coldest
   possible first impression of a product about climbing something.
   The mountain takes currentColor so it sits quietly in the muted ink rather than
   shouting; it is the only decorative element in the system and it earns that by
   appearing exactly once, on the screen with nothing else to say. */
.su-firstrun {
  display: flex; align-items: flex-start; gap: var(--su-5);
  padding: var(--su-6) var(--su-5);
  border-top: 1px solid var(--su-hair);
  color: var(--su-mute);
}
.su-firstrun .su-firstrun-mark {
  flex: 0 0 auto; width: 56px; height: 56px; color: var(--su-hair);
}
.su-firstrun .su-firstrun-mark svg { width: 100%; height: 100%; display: block; }
.su-firstrun h3 {
  margin: 0 0 var(--su-1);
  font-family: var(--su-serif); font-size: 20px; font-weight: 600;
  letter-spacing: -.01em; color: var(--su-ink);
}
.su-firstrun p { margin: 0; font-size: 14.5px; max-width: 56ch; color: var(--su-mid); }
@media (max-width: 600px) {
  .su-firstrun { gap: var(--su-4); }
  .su-firstrun .su-firstrun-mark { width: 40px; height: 40px; }
}

/* ── responsive ────────────────────────────────────────────────────────── */
@media (max-width: 760px) {
  .su-cols { grid-template-columns: minmax(0, 1fr); }
  .su-cols > .su-cell + .su-cell { border-left: 0; border-top: 1px solid var(--su-hair); }
  .su-pagehead, .su-cell { padding-left: var(--su-4); padding-right: var(--su-4); }
  .su-band { padding-left: var(--su-4); padding-right: var(--su-4); }
  .su-row { flex-wrap: wrap; }
}

/* Honors the reader's contrast preference rather than assuming our palette
   clears every screen it lands on. */
@media (prefers-contrast: more) {
  :root { --su-hair: #6B7278; --su-mute: #43494F; }
}
