/* UserBus Admin - ONE stylesheet, no build step, no framework.
   ============================================================================
   THE CONSOLE SPEAKS THE LANDING'S LANGUAGE. https://userbus.com states its own
   idea in its stylesheet: TRANSIT SIGNAGE - a bus, in both senses - signage
   yellow, black and white, a route line where the content is a real sequence,
   and system grotesk on purpose (no font files to ship). This file carries that
   language: the landing's palette VERBATIM, its black bands, its yellow pill,
   its pill status badges, its thick rules, its 1px row rules, its one error
   colour, and its fine print in grey.

   This file IS the design system of `admin/docs/information-architecture.md`
   section 4 (the components) and section 5 (the four states). A page does not
   invent a size, a space or a colour: it uses a class from this file.

   THREE THINGS ARE LOAD-BEARING HERE, and a later edit must not quietly drop
   them:

   1. THE SIX COLOUR ROLES. Every status on every page is one of six ROLES, and
      the role is stated IN WORDS in the markup (`.role-word`); colour only
      reinforces it, so a reader who cannot see colour, or who prints the page,
      loses nothing. `unknown` IS NOT `refused`: `unknown` says "nobody measured
      this, and the page says why" and it is drawn with a DASHED outline, while
      `refused` says "the platform said no, or this failed with a cause". The
      six roles keep their MEANING; only the palette under them changed.
   2. THE FOUR STATES. `.state-empty`, `.state-loading`, `.state-error` and
      `.state-populated` each render, so no view has an undesigned state. THE
      LOADING STATE CARRIES NO SKELETON AND NO NUMBER: a skeleton that looks
      like data is how a console lies during the minute an operator is anxious.
      It is the page's own frame plus a thin progress line.
   3. NOTHING IS SPACED BY EYE. Every padding, margin and gap is one of the six
      spacing tokens; every font-size is one of the five type tokens.
   ========================================================================= */

:root {
  /* THE LANDING'S PALETTE, VERBATIM. One yellow, black and white; `--rule` for a
     hairline, `--night-rule` for a hairline ON the black band, `--amber` for the
     dot-matrix sign, and `--alarm` for the one figure that is negative. */
  --yellow: #ffd400;
  --black: #000;
  --white: #fff;
  --grey: #5c5c5c;
  --rule: #d9d9d9;
  --night-rule: #3a3a3a;
  --amber: #ffb000;
  --alarm: #ff8a75;
  --focus: #000;
  --sans: "Helvetica Neue", Helvetica, Arial, "Liberation Sans", sans-serif;
  /* STUDIO'S TECHNICAL FACE: ids, addresses, digests and codes are set in the system monospace. */
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  /* TYPE SCALE - five sizes, and no page uses a sixth. The landing's own
     device: 700 headings with NEGATIVE letter-spacing, and tabular figures. */
  --t-title-size: 26px;   --t-title-line: 32px;   /* the page's h1 - ONE per page */
  --t-section-size: 18px; --t-section-line: 24px; /* section headings inside a page */
  --t-body-size: 15px;    --t-body-line: 23px;    /* everything else */
  --t-meta-size: 13px;    --t-meta-line: 18px;    /* table headers, timestamps, scope lines, reasons */
  --t-number-size: 26px;  --t-number-line: 32px;  /* a figure that IS the page's answer */

  /* SPACING SCALE - 4 8 12 16 24 32, and nothing between. 24 between sections,
     12 between a label and its value, 8 inside a control. UNCHANGED by the
     redesign: the IA owns this scale, so no step is added and none is removed. */
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 24px;
  --s6: 32px;

  /* COLOUR ROLES - six roles, never six colours picked per page. These are the
     landing's own colours: plain ink for `normal`; the brand yellow DARKENED to
     stay legible as text for `positive`, which is also the one role that is a
     FILL instead of ink (a yellow pill with black text, the landing's own
     device); the dot-matrix sign's amber for `waiting`; the landing's one error
     colour for `refused`; and the grey for the two roles that claim nothing. */
  --role-normal: var(--black);     /* measured, working, in the expected state */
  --role-positive: #8a6d00;        /* a completed good outcome: paid, delivered, credited */
  --role-waiting: var(--amber);    /* expected to change without intervention */
  --role-refused: #b00020;         /* the platform said no, or a failure with a cause */
  --role-unknown: var(--grey);     /* nobody measured this, and the page says why */
  --role-neutral: var(--grey);     /* no claim: labels, ids, structure */

  /* SURFACE - the landing has ONE surface: white, with rules. The black band is
     the header's and the footer's, and it is a band, not a surface. */
  --ink: var(--black);
  --ink-soft: var(--grey);
  --line: var(--rule);
  --paper: var(--white);
  --paper-soft: var(--white);
  --sidebar: var(--white);
  /* STUDIO'S STRUCTURE (2026-09-24): the console now shares the studio's shell - a raised header, a
     page ground, surfaces for cards, and two steps of surface for controls. Light values here; the
     studio's own dark values are the default below. */
  --ground: #f7f7f7;
  --paper-2: #f2f2f2;
  --paper-3: #e9e9e9;

  /* THEME TOKENS (2026-09-24): the roles a dark theme flips. The landing palette above stays
     literal; rules use these. `--band` is the transit-signage black band, which stays black in
     BOTH themes; `--strong` is a heavy rule or input edge on paper. */
  --strong: #b8b8b8;
  --band: var(--white);
  --band-ink: var(--black);
  --band-muted: var(--grey);

  /* THE DESIGN SYSTEM'S OWN NAMES (2026-09-26), for the pieces written against it: the waitlist page and
     the primary button's hover. The console's names above (--ground, --paper, --ink, --strong, --s1..s6) are
     this file's older spelling of the same palette and stay for every other page; a page that is touched
     moves to these. Values copied from userbus-design src/styles/tokens.css at 7969088 (the light register
     here, the dark one in the theme blocks below) - a value changes there first. */
  --c-accent: #ffd400;
  --c-accent-hover: #f0c400;
  --c-accent-ink: #000000;
  --c-surface: #ffffff;
  --c-line-strong: #858585;
  --c-text: #000000;
  --c-text-soft: #000000;
  --c-text-dim: #5c5c5c;
  --c-failed: #b00020;
  --c-succeeded: #000000;
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --r-md: 8px;
  --fs-sm: 0.8125rem;
  --fs-md: 0.9375rem;

  color-scheme: light;
  --sidebar-width: 208px;
  --measure-width: 1240px;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font: 400 var(--t-body-size)/var(--t-body-line) var(--sans);
  font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased;
}

/* THE STUDIO'S HEADINGS: 700 with the landing's negative tracking, and its technical face for code. */
h1 { letter-spacing: -0.045em; }
h2 { letter-spacing: -0.035em; }
code, .mono { font-family: var(--mono); font-size: var(--t-meta-size); }

/* KEYBOARD FOCUS IS ALWAYS VISIBLE. Never `outline: none`. ON A BLACK BAND the
   ring must not be black: the landing re-points `--focus` inside its own bands,
   and the header and the footer do the same here. */
:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 3px;
  border-radius: 2px;
}

a { color: inherit; text-underline-offset: 0.18em; text-decoration-thickness: 0.09em; }
a:hover { text-decoration-thickness: 0.16em; }

.skip-link {
  position: absolute; left: -9999px; top: 0;
  background: var(--paper); color: var(--ink);
  border: 1px solid var(--strong); border-radius: 8px;
  padding: var(--s2) var(--s3); font-weight: 700; z-index: 10;
}
.skip-link:focus { left: var(--s3); top: var(--s3); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ---------------------------------------------------------------- the shell */

.app-shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

/* A SIGNED-OUT PAGE HAS NO SIDEBAR (`Views.page_open/3` renders none without a session), so the shell
   is ONE column: the grid's first track belongs to the sidebar, and without this rule the page would be
   laid out inside that 208px track. The marker is the same one the shell already writes. */
body[data-session="signed_out"] .app-shell { flex-direction: column; }

/* THE SIDEBAR IS WHITE WITH BLACK TEXT AND A HAIRLINE ON ITS RIGHT: the black
   band belongs to the header below, and a second black column would fight it.
   The ONE mark of the current destination is a black band (the header's own
   device, small) with the word in white and 700. */
.sidebar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s3) var(--s4);
  background: var(--band);
  color: var(--band-ink);
  padding: var(--s3) var(--s5) 0;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  color: var(--band-ink);
  font-size: var(--t-section-size);
  line-height: var(--t-section-line);
  font-weight: 650;
  letter-spacing: -0.01em;
  text-decoration: none;
  padding: 0;
}

/* THE LANDING'S OWN MARK, beside the wordmark. It is a fixed-size flex item:
   the geometry is the SVG's, and this only keeps it from being squeezed and
   puts the one gap between it and the words. */
.brand-mark { flex: none; width: 26px; height: 26px; }

/* THE MARK IN THE DOOR'S OWN HEADER, AND IT IS WHITE BECAUSE THE BAND IS BLACK.
   A signed-out page carries the brand in the black `.topbar` (the public site's own strip: the mark on the
   left, its call to action on the right), while the sidebar's `.brand` is ink on white - so the colour is
   stated for the band, and the sidebar's own bottom padding is not inherited there. The hover is the
   landing's current-item device: a 3px YELLOW underline, which is how its own nav marks where you are. */
.topbar .brand, .brand-door { color: var(--band-ink); padding: 0; }
.brand-door:hover, .brand-door:focus-visible {
  text-decoration: underline;
  text-decoration-color: var(--yellow);
  text-decoration-thickness: 3px;
  text-underline-offset: 6px;
}

.nav { display: flex; flex-wrap: wrap; gap: var(--s1); margin-left: var(--s2); }

.nav a {
  display: inline-block;
  color: var(--band-muted);
  font-size: var(--t-meta-size);
  line-height: var(--t-meta-line);
  font-weight: 500;
  text-decoration: none;
  padding: var(--s2) var(--s3);
  border-radius: 8px;
}

/* THE HOVER IS THE HAIRLINE DEVICE: a yellow bar where the current item's band
   will be, so the navigation never moves when a pointer crosses it. */
.nav a:hover {
  color: var(--band-ink);
  text-decoration: underline;
  text-decoration-thickness: 3px;
  text-decoration-color: var(--yellow);
  text-underline-offset: 8px;
}

.nav a[aria-current="page"] {
  color: var(--band-ink);
  font-weight: 700;
  text-decoration: underline;
  text-decoration-thickness: 3px;
  text-decoration-color: var(--yellow);
  text-underline-offset: 8px;
}

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

/* THE HEADER IS THE LANDING'S SIGN STRIP: a black band, white words, a night
   hairline under it, and the one yellow pill on the page - the Search button. */
.topbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3) var(--s4);
  align-items: center;
  background: var(--band);
  color: var(--band-ink);
  border-bottom: 1px solid var(--line);
  padding: var(--s3) var(--s5);
}
.topbar-end { margin-left: auto; display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); }

.topbar .search { display: flex; }
.topbar .search label { display: block; font-size: var(--t-meta-size); line-height: var(--t-meta-line); color: var(--band-muted); }
.topbar .search input {
  font: inherit;
  padding: var(--s2) var(--s3);
  background: var(--ground);
  color: var(--band-ink);
  border: 1px solid var(--strong);
  border-radius: 8px;
  width: 16rem;
  font-size: var(--t-meta-size);
  padding: var(--s1) var(--s3);
  min-height: 32px;
}
.topbar .search input::placeholder { color: var(--band-muted); }

.who { display: flex; gap: var(--s3); align-items: center; font-size: var(--t-meta-size); line-height: var(--t-meta-line); color: var(--band-muted); }
.who form.sign-out { display: inline-flex; }
.who .who-label { color: var(--band-muted); }
.who .who-name { color: var(--band-ink); font-family: var(--mono); font-weight: 400; }

/* THE LOADING STATE'S ONLY MARK. A patch in flight is a thin progress line at
   the top of the page - never an invented number and never a skeleton made of
   grey boxes that a reader mistakes for rows. `app.js` sets data-loading from
   the LiveView client's own page-loading events. */
.progress-line {
  display: none;
  height: 3px;
  background: var(--line);
  overflow: hidden;
}
.progress-line span {
  display: block;
  height: 100%;
  width: 30%;
  background: var(--yellow);
  animation: progress-line 1.1s ease-in-out infinite;
}
body[data-loading="true"] .progress-line,
.column[data-loading="true"] .progress-line { display: block; }

@keyframes progress-line {
  0%   { transform: translateX(-30%); }
  100% { transform: translateX(330%); }
}

@media (prefers-reduced-motion: reduce) {
  .progress-line span { animation: none; width: 100%; }
}

main.page {
  flex: 1;
  width: 100%;
  /* **A FLEX ITEM'S `min-width` IS `auto`**, so one wide descendant would push the page sideways; this
     lets a table's own scroll wrapper do its job. */
  min-width: 0;
  max-width: var(--measure-width);
  margin: 0 auto;
  padding: var(--s5) var(--s5) var(--s6);
}

/* THE FOOTER IS THE LANDING'S FOOTER BAND: black, white, meta size. */
.footer {
  background: none;
  color: var(--ink-soft);
  border-top: 1px solid var(--line);
  font-size: var(--t-meta-size);
  line-height: var(--t-meta-line);
  width: 100%;
  max-width: var(--measure-width);
  margin: 0 auto;
  padding: var(--s4) var(--s5);
  display: flex;
  justify-content: space-between;
  gap: var(--s4);
}
.footer p { margin: 0; }

/* ------------------------------------------------- THE COMPONENTS (IA sec.4) */

/* Page header: h1 + one lede line + at most one primary action on the right. */
.page-head {
  display: flex;
  gap: var(--s4);
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: var(--s5);
}
.page-head h1 {
  font-size: var(--t-title-size);
  line-height: var(--t-title-line);
  font-weight: 700;
  letter-spacing: -0.045em;
  margin: 0 0 var(--s2);
}
/* THE LEDE IS THE LANDING'S `.lead`: a supporting line that is larger than body
   copy, in grey, with a 36em measure. The landing sets 17px there; this console
   has FIVE sizes and no sixth, so the lede takes the section step, which keeps
   the landing's relation (a lead is bigger than the text under it). */
.page-head .lede {
  font-size: var(--t-section-size);
  line-height: var(--t-section-line);
  color: var(--ink-soft);
  max-width: 36em;
  margin: 0;
}
.page-action { flex: none; }

/* Tabs: the views of ONE SUBJECT, as LINKS. A tab is a URL, not a hidden div -
   the current one is TEXT carrying aria-current="page" and every other one is
   an anchor, so the keyboard, a screen reader and "open in a new tab" work by
   default and no script is added for what is navigation. THE CURRENT TAB IS THE
   LANDING'S CURRENT-ITEM MARK: 700, with a 3px YELLOW rule under it. */
.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s1);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--s5);
}
.tabs .tab {
  display: block;
  padding: var(--s2) 0;
  margin-right: var(--s5);
  border: 0;
  border-bottom: 3px solid transparent;
  margin-bottom: -1px;
  color: var(--ink-soft);
  font-size: var(--t-meta-size);
  font-weight: 700;
  text-decoration: none;
}
.tabs a.tab:hover { color: var(--ink); }
.tabs .tab[aria-current="page"] {
  color: var(--ink);
  border-bottom-color: var(--yellow);
}

/* Toolbar: the page's SCOPE - chips, rows, filter in words, as of when, bound. */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2) var(--s3);
  align-items: center;
  border: 1px solid var(--line);
  background: var(--paper);
  border-radius: 8px;
  padding: var(--s2) var(--s3);
  margin-bottom: var(--s4);
  font-size: var(--t-meta-size);
  line-height: var(--t-meta-line);
}
/* A CHIP IS A PILL, like the landing's provenance marks: a 1.5px black outline
   and a 99px radius. */
.toolbar .chip {
  border: 1.5px solid var(--strong);
  border-radius: 99px;
  padding: 0 var(--s2);
  background: var(--paper-2);
  color: var(--ink-soft);
  font-weight: 700;
  text-decoration: none;
}
.toolbar .scope { margin: 0; color: var(--ink-soft); }
.toolbar .as-of { margin-left: auto; color: var(--ink-soft); }
/* THE MONEY MONITOR'S WINDOW: the chips are links, and the current window is the filled one. */
.window-select { flex-wrap: wrap; gap: var(--s2); }
.toolbar .chip-current { background: var(--strong); color: var(--paper); }
/* A WITHHELD MONEY FIGURE STATES ITS REASON UNDER THE TABLE, and an alert is one line each. */
.withheld, .alerts { margin: var(--s3) 0 0; padding-left: var(--s5); }
.withheld li, .alerts li { margin: 0 0 var(--s2); }

/* Table: the caption repeats the scope, and EVERY ROW LEADS SOMEWHERE. The
   landing's own table device: one black rule over the head, a 1px rule per row,
   NO ZEBRA STRIPES, and figures set tabular. */
.table {
  /* (1) CONTENT-DRIVEN, NEVER PAGE-DRIVEN: see the block below the header rules. */
  width: auto;
  min-width: 100%;
  border-collapse: collapse;
  background: transparent;
  border-top: 1px solid var(--line);
  margin-bottom: var(--s4);
}
/* A CAPTION IS FOR A SCREEN READER: the card's heading already names the table on screen. */
.table caption, .timeline caption, .console-table caption {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}
.table tbody th { text-transform: none; letter-spacing: 0; color: var(--ink); font-weight: 400; }
/* ============================================================================
   COLUMN WIDTHS FIT THE VALUES THE COLUMNS HOLD.

   MEASURED (2026-09-21, on the live console): every cell allowed
   `overflow-wrap: anywhere` AND the table was `width: 100%`, so a browser broke
   values in the middle - an id, an address - to make the columns share the PAGE's
   width, while a table with little content stretched its columns across the whole
   box. Both readings are wrong for a console whose cells hold VALUES a person
   reads and copies.

   THE FOUR RULES, in the order they act:

   1. **THE TABLE IS AS WIDE AS ITS CONTENT, AND AT LEAST AS WIDE AS ITS BOX**
      (`width: auto` with `min-width: 100%`): a column is sized by what it holds,
      and a table with more columns than room SCROLLS inside `.table-scroll`,
      which is the rule that already existed and is asserted by the suite.
   2. **A VALUE IS NEVER BROKEN TO MAKE IT FIT** (`white-space: nowrap` on the
      cell), so an id, an address, a timestamp or an amount stays one piece of
      text.
   3. **WHAT GENUINELY CANNOT FIT MAY BREAK ITSELF**: a long `<code>` value (a
      digest, a path) breaks inside itself rather than widening the table for
      ever, and a SENTENCE - a refusal's reason, a note, a "not measured" answer
      - wraps inside its cell, capped at a readable measure.
   4. **NUMBERS LINE UP**: tabular figures (already on the cell) with no
      wrapping, so a column of amounts can be compared down its length.
   ============================================================================ */
.table th, .table td {
  text-align: left;
  padding: var(--s3) var(--s4) var(--s3) 0;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
  font-variant-numeric: tabular-nums;
}
.table th {
  font-size: var(--t-meta-size);
  line-height: var(--t-meta-line);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  font-weight: 700;
  white-space: nowrap;
}
/* (2) A VALUE IS ONE PIECE OF TEXT - and THE SAME THREE RULES APPLY TO EVERY
   TABLE THIS APP RENDERS (`Views.table/3`, `Views.timeline/1` and
   `Views.value_rows/3`): `.table`, `.timeline` and `.console-table`. A table that
   opted out would be the one place a value still broke in half. MEASURED: the
   first version of this covered `.table` alone, and a long "actor" reason in a
   TIMELINE cell kept `white-space: nowrap` and widened its column for ever. */
.table, .timeline, .console-table { width: auto; min-width: 100%; }
.table td, .timeline td, .console-table td { white-space: nowrap; }

/* (3) WHAT GENUINELY CANNOT FIT MAY BREAK ITSELF, and only that: a long `<code>`
   value breaks inside itself (a digest, a path - the alternative is a column as
   wide as the value), and a SENTENCE wraps in its cell, capped at a readable
   measure so one long reason cannot push a table sideways. */
.table td code, .timeline td code, .console-table td code { white-space: nowrap; }
.table td code.long, .timeline td code.long, .console-table td code.long {
  white-space: normal;
  overflow-wrap: anywhere;
}

.table td .reason, .timeline td .reason, .console-table td .reason,
.table td .note, .timeline td .note, .console-table td .note,
.table td .value-not-measured, .timeline td .value-not-measured, .console-table td .value-not-measured,
.table td .value-unreadable, .timeline td .value-unreadable, .console-table td .value-unreadable,
.table td .value-empty, .timeline td .value-empty, .console-table td .value-empty {
  white-space: normal;
  display: inline-block;
  max-width: 30rem;
  vertical-align: top;
}

.table tbody tr { border-top: 1px solid var(--rule); }
/* NO ZEBRA AND NO TINT: the row's cue is the landing's yellow rule under the
   link the row leads to. */
.table tbody tr:hover .row-link { text-decoration: underline; text-decoration-color: var(--yellow); text-decoration-thickness: 3px; text-underline-offset: 2px; }
/* A ROW LINK IS STRUCTURE, so, as in the studio, it is not underlined at rest - the hover rule above marks it. */
.table .row-link { font-weight: 700; text-decoration: none; }

/* A WIDE TABLE SCROLLS INSIDE ITS OWN BOX, AT EVERY WIDTH - the box is the
   WRAPPER, and the table stays a table (no `display: block`), so the columns
   keep their alignment while the page keeps its width. MEASURED 2026-09-20:
   the rule that let a table scroll applied only at <=52rem, so between 833px
   and about 1000px the wide tables pushed the PAGE sideways - at a 900px
   viewport `documentElement.scrollWidth` was 1015 for /money/ledger, 1243 for
   /users and 1245 for Pulse, against a 900px client width. The wrapper is
   emitted by every component that renders a table (Views.table/3,
   Views.timeline/1, and the tables the pages build by hand). */
.table-scroll { overflow-x: auto; max-width: 100%; }

/* Detail card: LABEL / VALUE / (optional) REASON. A value nobody measured is
   `unknown` WITH ITS REASON, never a dash. */
/* A CARD CARRIES THE LANDING'S THICK TOP RULE. Its sections (`.facts`, `.door`) are marked by a 3px black
   rule with hairline rows under it, and a console card drawn with four 1px grey sides was the one place the
   redesign still read like the old one. The rule is the section's own edge; the hairline stays for the rest. */
.card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: var(--s5);
  margin-bottom: var(--s4);
}
.card h2 {
  font-size: var(--t-section-size);
  line-height: var(--t-section-line);
  font-weight: 700;
  letter-spacing: -0.02em;
  margin: 0 0 var(--s3);
}
/* THE FACTS ARE THE LANDING'S OWN `.facts`: one 3px rule over the block, a 1px
   rule under every row, and the LABEL in 700. */
.facts { margin: 0; border-top: 1px solid var(--line); }
.fact {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
  gap: var(--s3);
  padding: var(--s3) 0;
  border-bottom: 1px solid var(--line);
  /* **A GRID ITEM'S `min-width` IS ALSO `auto`**: one of Pulse's facts carries a TABLE in its `dd`, and
     without this line the item's automatic minimum size is the table's min-content width (measured 792px
     at a 700px viewport), so the item - and the page - grew instead of the table's own scroll wrapper
     scrolling. `min-width: 0` is what lets the wrapper do its job. */
  min-width: 0;
}
.fact dt { color: var(--ink-soft); font-weight: 600; font-size: var(--t-meta-size); line-height: var(--t-meta-line); }
.fact dd { margin: 0; overflow-wrap: anywhere; min-width: 0; }

/* The page's ANSWER figure. */
.number {
  font-size: var(--t-number-size);
  line-height: var(--t-number-line);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Timeline: one entry per recorded edge, oldest first. The history cap is a
   CAP FACT, never a transition. */
.timeline { width: 100%; border-collapse: collapse; background: transparent; border-top: 1px solid var(--line); }
.timeline th, .timeline td { text-align: left; padding: var(--s3) var(--s4) var(--s3) 0; border-bottom: 1px solid var(--rule); vertical-align: top; }
.timeline th { font-size: var(--t-meta-size); text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-soft); font-weight: 700; }
.timeline .edge { font-weight: 700; }
.timeline .transition { white-space: nowrap; }
.timeline .cap-fact { border-top: 1px solid var(--line); color: var(--ink-soft); font-size: var(--t-meta-size); }

/* Money row: MINOR UNITS with their own currency, never converted, never
   totalled across currencies. */
.money { white-space: nowrap; }
.money .amount { font-variant-numeric: tabular-nums; font-weight: 700; }
.money .unit { color: var(--ink-soft); font-size: var(--t-meta-size); }
.money .currency { color: var(--ink-soft); font-size: var(--t-meta-size); }
.money .reference { color: var(--ink-soft); }

/* Form: labels ABOVE inputs, one column, inline error beside the field. The
   landing's inputs: a 2px black border, an 8px radius, a 700 value. */
.form { background: var(--paper); border: 1px solid var(--line); border-radius: 14px; padding: var(--s5); max-width: 34rem; }
.form .field { margin-bottom: var(--s3); }
.form label { display: block; font-weight: 600; font-size: var(--t-meta-size); color: var(--ink-soft); margin-bottom: var(--s1); }
.form input[type="text"], .form input[type="password"], .form input[type="search"], .form textarea, .form select {
  width: 100%;
  font: 400 var(--t-body-size)/var(--t-body-line) var(--sans);
  padding: var(--s2) var(--s3);
  color: var(--ink);
  background: var(--ground);
  border: 1px solid var(--strong);
  border-radius: 8px;
}
.form .field-error { color: var(--role-refused); font-weight: 700; font-size: var(--t-meta-size); }
.form .hint { color: var(--ink-soft); font-size: var(--t-meta-size); line-height: var(--t-meta-line); }
.form .actions { display: flex; gap: var(--s3); align-items: center; }

/* THE BUTTONS ARE THE LANDING'S TWO: a black pill that inverts to white on
   hover, and the yellow pill with black text (its call to action). */
.btn {
  font: inherit;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-height: 36px;
  background: var(--paper-2);
  color: var(--ink);
  font-size: var(--t-meta-size);
  font-weight: 700;
  padding: var(--s2) var(--s4);
  border: 1px solid var(--strong);
  border-radius: 8px;
  cursor: pointer;
  text-decoration: none;
}
.btn:hover { background: var(--paper-3); color: var(--ink); }
.btn-primary { background: var(--yellow); color: var(--black); border: 1px solid var(--yellow); }
/* THE HOVER IS THE DESIGN SYSTEM'S TOKEN (it was the dark register's literal, in both themes): the light
   register hovers to a deeper yellow that still reads on white. */
.btn-primary:hover { background: var(--c-accent-hover); border-color: var(--c-accent-hover); color: var(--black); }
.btn-link { border: 0; background: none; color: var(--ink); padding: var(--s1) 0; }
.btn-link:hover {
  background: none;
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--yellow);
  text-decoration-thickness: 3px;
  text-underline-offset: 6px;
}
/* ON THE BLACK BAND the quiet link is white, and its hover rule is yellow. */
.topbar .btn-link, .topbar .btn-link:hover { color: var(--band-ink); }

/* Confirmation: the facts that WILL change, the required reason, and a primary
   button that NAMES the action. */
.confirm { border: 1px solid var(--strong); border-radius: 14px; background: var(--paper); padding: var(--s4); margin-bottom: var(--s5); }
.confirm h2 { font-size: var(--t-section-size); line-height: var(--t-section-line); font-weight: 700; letter-spacing: -0.02em; margin: 0 0 var(--s3); }
.confirm .confirm-facts { margin: 0 0 var(--s4); }
.confirm .required { font-size: var(--t-meta-size); color: var(--role-refused); font-weight: 700; }
.confirm .actions { display: flex; gap: var(--s3); align-items: center; }

/* Empty state: one sentence saying nothing exists YET, and the action that
   would create the first thing. Never a blank table. A plain hairline box, because a
   dash is this design's "nothing here yet". */
.empty { background: var(--paper); border: 1px solid var(--line); border-radius: 14px; padding: var(--s4); margin-bottom: var(--s5); }
.empty p { margin: 0 0 var(--s2); }
.empty .empty-reason { color: var(--ink-soft); font-size: var(--t-meta-size); }

/* Verdict banner: ONE line, used ONLY on Pulse. It is the landing's bus plate:
   a yellow plate with black 700 text. */
.verdict {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3);
  align-items: baseline;
  background: var(--yellow);
  color: var(--ink);
  border-radius: 14px;
  padding: var(--s3) var(--s4);
  margin-bottom: var(--s5);
  font-size: var(--t-body-size);
  line-height: var(--t-body-line);
  font-weight: 700;
}
.verdict-normal { background: var(--paper); color: var(--ink); border: 1px solid var(--line); }
.verdict .role-word { text-transform: capitalize; }
.verdict-refused { background: var(--paper); color: var(--role-refused); border: 2px solid var(--role-refused); }
.verdict-unknown { background: var(--paper); color: var(--role-unknown); border: 1px solid var(--line); }

/* --------------------------------------------------------- THE SIX ROLES */

/* The WORD is always in the markup; the class only reinforces it. A role is a
   PILL - a 1.5px outline and a 99px radius, the landing's provenance badge. */
.role { display: inline-flex; gap: var(--s1); align-items: baseline; border: 1.5px solid; border-radius: 99px; padding: 0 var(--s2); background: var(--paper-2); font-size: var(--t-meta-size); line-height: var(--t-meta-line); font-weight: 700; }
.role-word { font-weight: 700; }
.role-detail { color: var(--ink-soft); font-weight: 400; }

.role-normal { color: var(--role-normal); border-color: var(--role-normal); }
/* THE ONE ROLE THAT IS A FILL: the landing's yellow pill with black text, so a
   completed good outcome reads as a sign rather than as another colour. */
.role-positive { background: var(--yellow); color: var(--black); border-color: var(--black); }
.role-waiting { color: var(--role-waiting); border-color: var(--role-waiting); }
.role-refused { color: var(--role-refused); border-color: var(--role-refused); }
/* UNKNOWN IS NOT REFUSED, and it is not a blank with a tick over it either: a
   DASHED outline, its own grey, and the word "unknown" beside the reason. */
.role-unknown { color: var(--role-unknown); border-color: var(--line); }
.role-neutral { color: var(--role-neutral); border-color: var(--role-neutral); }

/* A value, rendered in its role. NOTHING TO SHOW IS A MUTED DASH; its reason is the hover title. */
.value-absent { color: var(--ink-soft); cursor: help; }
.value { overflow-wrap: anywhere; }
.value-not-measured { color: var(--role-unknown); }
.value-empty { color: var(--ink-soft); }
.value-unreadable { color: var(--role-refused); }
/* THE LANDING'S FINE PRINT: grey, at meta size. */
.reason, .fine, .hint { color: var(--ink-soft); font-size: var(--t-meta-size); line-height: var(--t-meta-line); }
.note { color: var(--ink-soft); font-size: var(--t-meta-size); line-height: var(--t-meta-line); margin: var(--s2) 0 0; }
.refused { color: var(--role-refused); }

/* ------------------------------------------------------ THE FOUR STATES */

.state { border: 1px solid var(--line); border-radius: 14px; background: var(--paper); padding: var(--s4); margin-bottom: var(--s5); }
.state p { margin: 0 0 var(--s2); }

/* EMPTY: nothing exists YET, the reason, and the action that creates the first one. */
.state-empty { border: 1px solid var(--line); }
.state-empty .state-reason { color: var(--ink-soft); font-size: var(--t-meta-size); }
/* WHAT WOULD CREATE THE FIRST RECORD: the sentence a brand-new deployment needs, in the empty state. */
.state-empty .state-created-by { color: var(--grey); font-size: 0.9em; }

/* LOADING: the page's frame, its header and its toolbar with the values it
   already knows, and a thin progress line. NO INVENTED NUMBERS and no skeleton
   that looks like data. */
.state-loading { border: 1px solid var(--line); color: var(--ink-soft); }
.state-loading .loading-sentence { font-size: var(--t-meta-size); color: var(--ink-soft); }

/* ERROR / REFUSED: the store that could not be read, named; Retry-After when
   the app sets it; and the sentence "nothing on this page was measured". The
   error box is the landing's ONE error colour; a refusal is the dot-matrix
   sign's amber, because a refusal is a state of the world and not a fault of
   this page's. */
.state-error { border: 2px solid var(--role-refused); }
.state-refused { border: 2px solid var(--role-waiting); }
.state-error h2, .state-refused h2 { font-size: var(--t-section-size); line-height: var(--t-section-line); font-weight: 700; letter-spacing: -0.02em; margin: 0 0 var(--s2); }
.state-error h2 { color: var(--role-refused); }
.state-error .surface { font-weight: 700; }
.state-error .retry-after { color: var(--ink-soft); font-size: var(--t-meta-size); }

.state-populated { border: 0; background: none; padding: 0; margin: 0; }

/* A plain notice, kept for the sentences that are neither a state nor a role.
   A 2px bar on the left, the landing's opening device for a warning. */
.notice { border-left: 2px solid var(--yellow); background: none; padding: var(--s2) 0 var(--s2) var(--s3); margin: 0 0 var(--s4); color: var(--ink-soft); font-size: var(--t-meta-size); line-height: var(--t-meta-line); }
.notice.refused { border-left: 2px solid var(--role-refused); color: var(--role-refused); }

/* ------------------------------------------------------------------ lists */

/* A MEASURE IS A RULED COLUMN, the landing's `.facts` at figure size: a 3px
   rule over it, a 1px rule under it, no box. */
.measures { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: var(--s5); margin: 0 0 var(--s5); }
.measure { background: var(--paper); border: 1px solid var(--line); border-radius: 14px; padding: var(--s4); }
.measure dt a { color: inherit; text-decoration: none; }
.measure dt a:hover { color: var(--ink); text-decoration: underline; text-decoration-color: var(--yellow); }
.table .num, .table th.num { text-align: right; }
.measure dt { color: var(--ink-soft); font-weight: 600; font-size: var(--t-meta-size); line-height: var(--t-meta-line); }
.measure dd { margin: var(--s1) 0 0; font-size: var(--t-number-size); line-height: var(--t-number-line); font-weight: 700; font-variant-numeric: tabular-nums; }
/* NARROWER TILES: each at least 10rem wide, as many per line as the width holds,
   and auto-fit stretches a short row to fill its line. Pulse's two rows use it. */
.measures.measures-row { grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); }
/* A tile's own link, when the list it opens is not exactly the figure it shows. */
.measure dd.measure-link { font-size: var(--t-meta-size); line-height: var(--t-meta-line); font-weight: 400; font-variant-numeric: normal; }

section.block { background: var(--paper); border: 1px solid var(--line); border-radius: 14px; padding: var(--s5); margin: 0 0 var(--s5); }
section.block h2 { font-size: var(--t-section-size); line-height: var(--t-section-line); font-weight: 700; letter-spacing: -0.02em; margin: 0 0 var(--s3); }

/* ------------------------------------------------- legacy class aliases */
/* The pages the redesign has not reached yet still use these names. Each page
   commit moves that page onto the components above and drops its use of the
   alias; the aliases exist so the SHELL could land without restyling ten pages
   in one commit. They now carry the same language as the components. */
.console-table { width: 100%; border-collapse: collapse; background: transparent; border-top: 1px solid var(--line); }
.console-table th, .console-table td { text-align: left; padding: var(--s3) var(--s4) var(--s3) 0; border-bottom: 1px solid var(--rule); vertical-align: top; font-variant-numeric: tabular-nums; }
.console-table tbody tr { border-top: 1px solid var(--rule); }
.console-table th { color: var(--ink-soft); font-weight: 700; font-size: var(--t-meta-size); line-height: var(--t-meta-line); text-transform: uppercase; letter-spacing: 0.06em; }
form.console-form { background: var(--paper); border: 1px solid var(--line); border-radius: 14px; padding: var(--s5); max-width: 34rem; }
/* A FORM INSIDE A CARD IS PART OF THE CARD: the studio never draws a box inside a box. */
.card form.console-form, .card .form { background: none; border: 0; padding: 0; }
form.console-form label { display: block; margin: 0 0 var(--s1); font-weight: 600; font-size: var(--t-meta-size); color: var(--ink-soft); }
form.console-form input[type="text"], form.console-form input[type="password"] {
  width: 100%;
  padding: var(--s2) var(--s3);
  border: 1px solid var(--strong);
  border-radius: 8px;
  font: 400 var(--t-body-size)/var(--t-body-line) var(--sans);
  color: var(--ink);
  background: var(--ground);
  margin-bottom: var(--s3);
}
form.console-form button[type="submit"] { background: var(--yellow); color: var(--black); border: 1px solid var(--yellow); border-radius: 8px; min-height: 36px; padding: var(--s2) var(--s4); font: inherit; font-size: var(--t-meta-size); font-weight: 700; cursor: pointer; }
form.console-form button[type="submit"]:hover { background: var(--c-accent-hover); border-color: var(--c-accent-hover); }
form.console-form.search { display: flex; gap: var(--s2); align-items: flex-end; max-width: 40rem; margin-bottom: var(--s4); }
form.console-form.search input[type="text"] { margin-bottom: 0; }
form.console-form.search label { margin: 0; }

/* ------------------------------------------------------ phone width */
/* The READ-ONLY tasks must be readable on a phone: the sidebar becomes a
   horizontal strip, the content takes the full width, and a wide table scrolls
   INSIDE its own box rather than moving the page. */
/* THE PHONE BREAKPOINT IS 52rem (832px), not 48rem: measured - at 821px the two-column shell plus a
   table's widest cell crossed the viewport, and the single-column layout is the right one there. */
@media (max-width: 52rem) {
  .sidebar { padding: var(--s3) var(--s3) 0; }
  .nav { margin-left: 0; }
  .topbar { padding: var(--s3); }
  .topbar .search input { min-width: 0; width: 100%; }
  main.page { padding: var(--s4) var(--s3) var(--s5); }
  .fact { grid-template-columns: 1fr; gap: var(--s1); }
  /* THE LINE THAT MADE A TABLE A BLOCK IS GONE: the wrapper (`.table-scroll`)
     scrolls a wide table at EVERY width, which is what this block was doing for
     the narrow case only - and a table that is still a table keeps its columns
     aligned. The rest of this block (the single column, the sidebar strip) has
     nothing to do with tables and stays. */
}

/* THE THEME SWITCH, on the black band: three small pills, the current one filled yellow. Hidden
   until `theme.js` marks the document, because without script the buttons would do nothing. The
   current choice is drawn from <html data-theme>, so it is right before any script re-marks it. */
.theme-switch { display: none; gap: var(--s1); align-items: center; }
.theme-js .theme-switch { display: inline-flex; }
.theme-switch button {
  font: inherit; font-size: var(--t-meta-size); line-height: var(--t-meta-line);
  color: var(--band-ink); background: none; border: 1.5px solid var(--band-muted);
  border-radius: 999px; padding: 0 var(--s2); cursor: pointer;
}
.theme-switch button:hover { border-color: var(--yellow); }
.theme-switch button:focus-visible { outline: 2px solid var(--yellow); outline-offset: 2px; }
:root:not([data-theme]) .theme-switch [data-theme-choice="dark"],
:root[data-theme="dark"] .theme-switch [data-theme-choice="dark"],
:root[data-theme="light"] .theme-switch [data-theme-choice="light"],
:root[data-theme="auto"] .theme-switch [data-theme-choice="auto"] {
  background: var(--yellow); border-color: var(--yellow); color: var(--black); font-weight: 700;
}

/* ------------------------------------------------------ theme */
/* THE CONSOLE WEARS THE STUDIO'S DARK GROUND BY DEFAULT (2026-09-24), with the same three choices the
   header offers: DARK (the default, and what a browser without script gets), LIGHT, and AUTO, which
   follows the operating system. `theme.js` stamps <html data-theme="light|auto"> before first paint;
   Dark stamps nothing. Signage keeps its meaning in both: the yellow is the action and the current
   place, amber is waiting, and grey is "not recorded". No broken-line borders anywhere (2026-09-24). */
:root:not([data-theme]),
:root[data-theme="dark"] {
  /* THE STUDIO'S PALETTE, VERBATIM (userbus-studio/priv/static/app.css): the landing's dark register. */
  --ground: #0a0a0a;
  --band: #0f0f0f;
  --paper: #111111;
  --paper-soft: #171717;
  --paper-2: #171717;
  --paper-3: #1f1f1f;
  --sidebar: #0f0f0f;
  --rule: #2a2a2a;
  --line: #2a2a2a;
  --strong: #3a3a3a;
  --ink: #ffffff;
  --ink-soft: #c9c9c9;
  --grey: #c9c9c9;
  --band-ink: #ffffff;
  --band-muted: #c9c9c9;
  --role-normal: #ffffff;
  --role-positive: #ffd400;
  --role-refused: #ff8a75;
  --focus: #ffd400;
  /* the design system's dark register (userbus-design tokens.css at 7969088) */
  --c-accent-hover: #ffe14d;
  --c-accent-ink: #ffd400;
  --c-surface: #111111;
  --c-line-strong: #3a3a3a;
  --c-text: #ffffff;
  --c-text-soft: #e2e2e2;
  --c-text-dim: #c9c9c9;
  --c-failed: #ff8a75;
  --c-succeeded: #ffd400;
  color-scheme: dark;
}
@media (prefers-color-scheme: dark) {
  :root[data-theme="auto"] {
  /* THE STUDIO'S PALETTE, VERBATIM (userbus-studio/priv/static/app.css): the landing's dark register. */
  --ground: #0a0a0a;
  --band: #0f0f0f;
  --paper: #111111;
  --paper-soft: #171717;
  --paper-2: #171717;
  --paper-3: #1f1f1f;
  --sidebar: #0f0f0f;
  --rule: #2a2a2a;
  --line: #2a2a2a;
  --strong: #3a3a3a;
  --ink: #ffffff;
  --ink-soft: #c9c9c9;
  --grey: #c9c9c9;
  --band-ink: #ffffff;
  --band-muted: #c9c9c9;
  --role-normal: #ffffff;
  --role-positive: #ffd400;
  --role-refused: #ff8a75;
  --focus: #ffd400;
  /* the design system's dark register (userbus-design tokens.css at 7969088) */
  --c-accent-hover: #ffe14d;
  --c-accent-ink: #ffd400;
  --c-surface: #111111;
  --c-line-strong: #3a3a3a;
  --c-text: #ffffff;
  --c-text-soft: #e2e2e2;
  --c-text-dim: #c9c9c9;
  --c-failed: #ff8a75;
  --c-succeeded: #ffd400;
  color-scheme: dark;
  }
}

/* ------------------------------------------------------ print */
/* A printed page keeps every WORD - the role words are text, not colour. */
@media print {
  .sidebar, .topbar, .progress-line { display: none; }
  .app-shell { display: block; }
  /* A SCROLL CONTAINER CLIPS WHEN IT PRINTS: on paper the table is shown whole. */
  .table-scroll { overflow: visible; }
  body { background: var(--white); }
  /* PAPER IS WHITE: a dark theme never prints dark. */
  :root, :root[data-theme], :root:not([data-theme]) { --ink: #000; --ink-soft: #5c5c5c; --grey: #5c5c5c;
    --paper: #fff; --paper-soft: #fff; --paper-2: #fff; --paper-3: #fff; --ground: #fff; --band: #fff;
    --band-ink: #000; --band-muted: #5c5c5c; --sidebar: #fff; --rule: #d9d9d9; --line: #d9d9d9;
    --strong: #000; --role-normal: #000; --role-positive: #8a6d00; --role-refused: #b00020;
    --c-surface: #fff; --c-text: #000; --c-text-soft: #000; --c-text-dim: #5c5c5c; --c-line-strong: #858585;
    --c-failed: #b00020; --c-succeeded: #000; color-scheme: light; }
}

/* ---------------------------------------------------------------------------------------------
   A JOB'S OUTPUT (2026-09-25): the generated content in the browser's own player on
   `/jobs/:id/output`, and a lazily loaded thumbnail beside the link on `/jobs`. The player never
   grows past its card, and the list's thumbnail is a fixed small box so rows do not jump as the
   images arrive.
   --------------------------------------------------------------------------------------------- */
.output-player,
.output-image {
  display: block;
  max-width: 100%;
  max-height: 70vh;
  margin-block: var(--s3);
  background: var(--black);
  border: 1px solid var(--rule);
}

/* THE PLAYER TAKES THE CARD'S WIDTH (up to a readable size) before the clip's metadata arrives, so the
   page does not jump from the browser's 300x150 default to the clip's own shape. */
.output-player {
  width: min(100%, 960px);
}

.output-image {
  background: transparent;
}

.output-audio {
  display: block;
  width: 100%;
  margin-block: var(--s3);
}

.output-note {
  margin-block: var(--s2);
}

.output-link {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  white-space: nowrap;
}

.output-thumb {
  width: 64px;
  height: 36px;
  object-fit: cover;
  border: 1px solid var(--rule);
  background: var(--rule);
}

/* ---------------------------------------------------------------------------------------------
   THE WAITLIST (2026-09-26, `/waitlist`): written against the design system's own names (the tokens
   block in :root). `.alert` and `.empty-state` are the design system's components, copied from
   userbus-design src/styles/components.css at 7969088; the rest is this page's.
   --------------------------------------------------------------------------------------------- */
.alert {
  border: 1px solid var(--c-line-strong); border-left-width: 6px; border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4); background: var(--c-surface); font-size: var(--fs-sm); color: var(--c-text);
  margin-bottom: var(--sp-4);
}
.alert.error { border-left-color: var(--c-failed); }
.alert.notice { border-left-color: var(--c-succeeded); }
.alert.info { border-left-color: var(--c-accent-ink); }
.alert > strong { display: block; margin-bottom: 2px; }
.alert p { margin: 0 0 var(--sp-2); }
.alert p:last-child { margin-bottom: 0; }
.alert pre { margin: 0 0 var(--sp-2); white-space: pre-wrap; overflow-wrap: anywhere; }

.empty-state { text-align: center; padding: var(--sp-5) var(--sp-4); color: var(--c-text-soft); }
.empty-state h2 { color: var(--c-text); margin: 0 0 var(--sp-2); font-size: var(--fs-md); letter-spacing: -0.02em; }
.empty-state p { max-width: 46ch; margin: 0 auto var(--sp-4); }

/* THE CURRENT FILTER IS THE FILLED CHIP, in ink on its surface so it reads in both registers. */
.waitlist-filter .chip-current { background: var(--c-text); border-color: var(--c-text); color: var(--c-surface); }

/* THE NOTE IS THE PERSON'S OWN WORDS: it wraps inside its cell at a readable measure, keeps its line
   breaks, and a long one opens in place. */
.table td.waitlist-note-cell { white-space: normal; min-width: 16ch; max-width: 40ch; }
.waitlist-note { white-space: pre-line; overflow-wrap: anywhere; color: var(--c-text-soft); }
.waitlist-note summary { cursor: pointer; }
.waitlist-note p { margin: var(--sp-2) 0 0; }

/* SEND INVITE OPENS ITS OWN SENTENCE: the row's button is the plain one, and the yellow one appears only
   inside the open confirmation. */
.waitlist-invite summary { list-style: none; }
.waitlist-invite summary::-webkit-details-marker { display: none; }
.waitlist-invite[open] summary { margin-bottom: var(--sp-2); }
.waitlist-invite-form { white-space: normal; max-width: 36ch; }
.waitlist-invite-form p { margin: 0 0 var(--sp-2); color: var(--c-text-soft); font-size: var(--fs-sm); }
