/* GENERATED — do not edit.
   Source: strim-ai-portal/tokens/tokens.json
   Regenerate: node scripts/generate-tokens.ts

   Colours are declared per theme; everything scalar is theme-independent.
   Anything in this file that is not in tokens.json is listed as a supplement
   in the generator, which is also the list of portal CSS that has no token. */

:root {
  --token-space-1: 4px;
  --token-space-2: 8px;
  --token-space-3: 12px;
  --token-space-4: 16px;
  --token-space-5: 20px;
  --token-space-6: 24px;
  --token-space-8: 32px;
  --token-space-10: 40px;
  --token-space-12: 48px;
  --token-space-16: 64px;
  --token-space-24: 96px;

  --token-radius-sm: 8px;
  --token-radius-md: 12px;
  --token-radius-lg: 16px;
  --token-radius-pill: 999px;

  --token-fs-xs: 0.75rem;
  --token-fs-sm: 0.875rem;
  --token-fs-md: 1rem;
  --token-fs-lg: 1.25rem;
  --token-fs-xl: 1.625rem;
  --token-fs-2xl: 2.75rem;

  --token-lh-tight: 1.1;
  --token-lh-snug: 1.3;
  --token-lh-normal: 1.55;
  --token-lh-relaxed: 1.65;

  --token-dur-fast: 120ms;
  --token-dur-base: 200ms;
  --token-dur-slow: 400ms;

  --token-z-base: 0;
  --token-z-content: 1;
  --token-z-sticky: 10;

  /* font families — used by design-system.html, absent from tokens.json */
  --token-font-display: "E UkraineHead", "E Ukraine", Arial, sans-serif;
  --token-font-body: "E Ukraine", Arial, sans-serif;
  --token-font-mono: ui-monospace, "SF Mono", SFMono-Regular, "Cascadia Mono", Consolas, Menlo, monospace;

  /* border widths — the portal writes 1px/2px/3px inline */
  --token-border-hair: 1px;
  --token-border-thick: 2px;
  --token-border-accent: 3px;

  /* sub-space values below space-1, used by chips and segmented controls */
  --token-inset-chip: 3px;
  --token-gap-hair: 2px;

  /* control sizes — no size scale exists in tokens.json */
  --token-size-dot: 9px;
  --token-size-dot-sm: 7px;
  --token-size-avatar: 26px;
  --token-size-checkbox: 16px;
  --token-size-caret: 5px;
  --token-size-hidden: 1px;

  /* layout widths — page frame and grid minimums */
  --token-layout-max: 1080px;
  --token-layout-card-min: 250px;
  --token-layout-panel-min: 300px;
  --token-layout-stat-min: 140px;
  --token-layout-field-basis: 220px;
  --token-layout-field-max: 340px;
  --token-layout-offscreen: -9999px;

  /* measures — line length in characters, so they follow the font, not the viewport */
  --token-measure-lede: 60ch;
  --token-measure-note: 34ch;
  --token-measure-prose: 44ch;
  --token-measure-cell: 7ch;

  /* one-off scale gaps */
  --token-fs-hero-min: 2rem;
  --token-z-skip: 100;
}

/* Dark by default: strim.co is dark, and an unset preference should not flash
   a light page at someone who never asked for one. */
:root,
:root[data-theme='dark'] {
  color-scheme: dark;
  --token-color-surface: #111111;
  --token-color-surface-raised: #181818;
  --token-color-surface-inset: #1f1f1f;
  --token-color-surface-sunken: #0c0c0c;
  --token-color-text-primary: #f5f5f5;
  --token-color-text-secondary: #b8b8b8;
  --token-color-text-tertiary: #9c968b;
  --token-color-text-meta: #858179;
  --token-color-border-subtle: #2a2a2a;
  --token-color-border-strong: #3b3b3b;
  --token-color-accent-fill: #573caf;
  --token-color-accent-text: #c0c0fa;
  --token-color-on-accent: #ffffff;
  --token-color-accent-wash: #241a3d;
  --token-color-state-ok-text: #c0c0fa;
  --token-color-state-ok-bg: #241a3d;
  --token-color-state-run: #b8b8b8;
  --token-color-state-fail: #b8b8b8;
  --token-color-state-ghost: #9c968b;
}

:root[data-theme='light'] {
  color-scheme: light;
  --token-color-surface: #f5f5f5;
  --token-color-surface-raised: #ffffff;
  --token-color-surface-inset: #fafafa;
  --token-color-surface-sunken: #ececec;
  --token-color-text-primary: #111111;
  --token-color-text-secondary: #3b3b3b;
  --token-color-text-tertiary: #6b6b6b;
  --token-color-text-meta: #6b6b6b;
  --token-color-border-subtle: #e2e2e2;
  --token-color-border-strong: #d2d2d2;
  --token-color-accent-fill: #573caf;
  --token-color-accent-text: #46309a;
  --token-color-on-accent: #ffffff;
  --token-color-accent-wash: #ece9f7;
  --token-color-state-ok-text: #46309a;
  --token-color-state-ok-bg: #ece9f7;
  --token-color-state-run: #3b3b3b;
  --token-color-state-fail: #3b3b3b;
  --token-color-state-ghost: #6b6b6b;
}

/* System preference, until the reader picks a theme explicitly. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    color-scheme: light;
    --token-color-surface: #f5f5f5;
    --token-color-surface-raised: #ffffff;
    --token-color-surface-inset: #fafafa;
    --token-color-surface-sunken: #ececec;
    --token-color-text-primary: #111111;
    --token-color-text-secondary: #3b3b3b;
    --token-color-text-tertiary: #6b6b6b;
    --token-color-text-meta: #6b6b6b;
    --token-color-border-subtle: #e2e2e2;
    --token-color-border-strong: #d2d2d2;
    --token-color-accent-fill: #573caf;
    --token-color-accent-text: #46309a;
    --token-color-on-accent: #ffffff;
    --token-color-accent-wash: #ece9f7;
    --token-color-state-ok-text: #46309a;
    --token-color-state-ok-bg: #ece9f7;
    --token-color-state-run: #3b3b3b;
    --token-color-state-fail: #3b3b3b;
    --token-color-state-ghost: #6b6b6b;
  }
}

/* People export the quota and cost tables to PDF. Printed from the dark theme
   that is light text on white paper — a blank sheet — so print forces the light
   values here rather than making app.css restate them as literals. */
@media print {
  :root {
    color-scheme: light;
    --token-color-surface: #f5f5f5;
    --token-color-surface-raised: #ffffff;
    --token-color-surface-inset: #fafafa;
    --token-color-surface-sunken: #ececec;
    --token-color-text-primary: #111111;
    --token-color-text-secondary: #3b3b3b;
    --token-color-text-tertiary: #6b6b6b;
    --token-color-text-meta: #6b6b6b;
    --token-color-border-subtle: #e2e2e2;
    --token-color-border-strong: #d2d2d2;
    --token-color-accent-fill: #573caf;
    --token-color-accent-text: #46309a;
    --token-color-on-accent: #ffffff;
    --token-color-accent-wash: #ece9f7;
    --token-color-state-ok-text: #46309a;
    --token-color-state-ok-bg: #ece9f7;
    --token-color-state-run: #3b3b3b;
    --token-color-state-fail: #3b3b3b;
    --token-color-state-ghost: #6b6b6b;
  }
}

/* The component layer: where a new component or a third theme is bent in one
   place instead of forking a rule. */
:root {
  --token-button-primary-bg: var(--token-color-accent-fill);
  --token-button-primary-fg: var(--token-color-on-accent);
  --token-badge-ok-fg: var(--token-color-state-ok-text);
  --token-badge-ok-bg: var(--token-color-state-ok-bg);
  --token-badge-run-fg: var(--token-color-state-run);
  --token-badge-fail-fg: var(--token-color-state-fail);
  --token-badge-ghost-fg: var(--token-color-state-ghost);
  --token-focus-ring: var(--token-color-text-primary);
}

@font-face {
  font-family: 'E Ukraine';
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url('/assets/fonts/e-Ukraine-Regular.e7953d686c52.woff') format('woff');
}
@font-face {
  font-family: 'E Ukraine';
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  src: url('/assets/fonts/e-Ukraine-Medium.f2c124bc9df3.woff') format('woff');
}
@font-face {
  font-family: 'E UkraineHead';
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  src: url('/assets/fonts/e-UkraineHead-Regular.ad7a6fa8a4e2.woff') format('woff');
}
/* Component styles for @strim/web.
   Structure mirrors strim-ai-portal/design/design-system.html.

   Not one value is written here. v1 copied the portal's CSS by hand and then
   fixed things in the copy, so the two drifted with nothing to say they had;
   every colour, space, radius, size and duration below is a token from
   src/generated/tokens.css, and scripts/lint-literals.ts fails the build on a
   literal. Only breakpoints stay literal, because custom properties are not
   substituted inside a media query prelude. */

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

body {
  margin: 0;
}

/* The @font-face rules are GENERATED, in src/assets/fonts.ts, and prepended to
   this file. They used to sit here pointing at the marketing CDN, which the
   production CSP (`font-src 'self'`) blocked outright — the page rendered in a
   system face and looked merely wrong rather than broken. The faces are served
   from our own origin now, at paths containing their own hash, and a hash is
   not something a static stylesheet can spell. */

html {
  scroll-behavior: smooth;
  /* Without this every anchor lands under the sticky header. */
  scroll-padding-top: var(--token-space-16);
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  * {
    transition: none !important;
  }
}

body {
  font-family: var(--token-font-body);
  background: var(--token-color-surface);
  color: var(--token-color-text-primary);
  font-size: var(--token-fs-md);
  line-height: var(--token-lh-normal);
  -webkit-font-smoothing: antialiased;
  transition: background var(--token-dur-slow) ease, color var(--token-dur-slow) ease;
}

/* Declared on everything that actually changes colour, not just on body:
   otherwise a theme switch repaints the page in pieces. */
.panel,
.badge,
.btn,
.field,
select,
table,
th,
td,
.seg,
.tabs a {
  transition: background var(--token-dur-slow) ease, color var(--token-dur-slow) ease,
    border-color var(--token-dur-slow) ease;
}

h1,
h2,
h3 {
  font-family: var(--token-font-display);
  font-weight: 400;
  margin: 0;
  line-height: var(--token-lh-tight);
  text-wrap: balance;
}

p {
  margin: 0;
}

a {
  color: var(--token-color-accent-text);
}

.mono {
  font-family: var(--token-font-mono);
  font-variant-numeric: tabular-nums;
}

/* Visually hidden, still announced. */
.sr {
  position: absolute;
  width: var(--token-size-hidden);
  height: var(--token-size-hidden);
  margin: calc(var(--token-size-hidden) * -1);
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip {
  position: absolute;
  left: var(--token-layout-offscreen);
  top: 0;
  /* Above the sticky header, or the first Tab focuses something invisible. */
  z-index: var(--token-z-skip);
  background: var(--token-color-surface-raised);
  color: var(--token-color-text-primary);
  padding: var(--token-space-3) var(--token-space-4);
  border-radius: 0 0 var(--token-radius-sm) 0;
}

.skip:focus {
  left: 0;
}

:where(a, button, input, select, summary, [tabindex]):focus-visible {
  /* text-primary, not the accent: on a segment already filled with the accent
     an accent ring disappeared into its own background in the light theme. */
  outline: var(--token-border-thick) solid var(--token-focus-ring);
  outline-offset: var(--token-border-thick);
  border-radius: var(--token-radius-sm);
}

/* ------------------------------------------------------------- app chrome */

.chrome {
  position: sticky;
  top: 0;
  z-index: var(--token-z-sticky);
  background: var(--token-color-surface);
  border-bottom: var(--token-border-hair) solid var(--token-color-border-subtle);
}

.chrome-in {
  max-width: var(--token-layout-max);
  margin: 0 auto;
  padding: var(--token-space-3) var(--token-space-6);
  display: flex;
  align-items: center;
  gap: var(--token-space-4) var(--token-space-6);
  flex-wrap: wrap;
}

.brand {
  font-family: var(--token-font-display);
  font-size: var(--token-fs-md);
  display: flex;
  align-items: center;
  gap: var(--token-space-2);
  text-decoration: none;
  color: var(--token-color-text-primary);
}

.brand .dot {
  width: var(--token-size-dot);
  height: var(--token-size-dot);
  border-radius: 50%;
  background: var(--token-color-accent-fill);
}

.chrome .spacer {
  margin-left: auto;
}

/* The signed-in account.
 *
 * `min-width: 0` is what stops the header running off the screen. A flex item
 * refuses to shrink below its content by default, and the address inside is
 * `mono`, which does not wrap — so on a narrow window the row neither wrapped
 * nor shrank, and the address and the sign-out link were simply cut off at the
 * right edge with no scrollbar to reach them. */
.acct {
  display: flex;
  align-items: center;
  gap: var(--token-space-2);
  font-size: var(--token-fs-sm);
  color: var(--token-color-text-secondary);
  min-width: 0;
}

/* The address gives way before the sign-out link does: one is recognisable from
 * its first half, the other is the way out. The whole value stays in `title`. */
.acct .mono {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.acct .linkish {
  flex: none;
}

.avatar {
  width: var(--token-size-avatar);
  height: var(--token-size-avatar);
  border-radius: 50%;
  background: var(--token-color-accent-fill);
  color: var(--token-color-on-accent);
  display: grid;
  place-items: center;
  font-size: var(--token-fs-xs);
  font-weight: 500;
}

.linkish {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font: inherit;
  color: var(--token-color-accent-text);
}

/* Product navigation. The design system's header carried only a product
   switcher; with several products and an admin area the tabs have to be named. */
.tabs {
  display: flex;
  gap: var(--token-space-4);
}

.tabs a {
  font-size: var(--token-fs-sm);
  color: var(--token-color-text-secondary);
  text-decoration: none;
  padding: var(--token-space-1) 0;
  border-bottom: var(--token-border-thick) solid transparent;
}

.tabs a:hover {
  color: var(--token-color-text-primary);
}

.tabs a[aria-current='page'] {
  color: var(--token-color-text-primary);
  border-bottom-color: var(--token-color-accent-text);
}

/* -------------------------------------------------------- choose-exactly-one */

.ctrl {
  display: flex;
  align-items: center;
  gap: var(--token-space-2);
}

/* ONE rule, unscoped. It was declared three times — `.ctrl > .lbl`,
   `.radios > .lbl`, `.control > .lbl, .field > .lbl` — and a label anywhere
   else matched none of them: four in the admin area rendered at body size in
   the primary colour, beside identical-looking labels at fs-xs. A shared class
   that only works under certain parents is not a shared class. */
.lbl {
  font-size: var(--token-fs-xs);
  color: var(--token-color-text-tertiary);
  white-space: nowrap;
  padding: 0;
}

.seg {
  display: inline-flex;
  background: var(--token-color-surface-inset);
  border: var(--token-border-hair) solid var(--token-color-border-subtle);
  border-radius: var(--token-radius-pill);
  padding: var(--token-inset-chip);
  gap: var(--token-gap-hair);
}

.seg button,
.seg a {
  font-size: var(--token-fs-xs);
  font-weight: 500;
  /* Transparent rather than absent, so lifting the chosen one below does not
     move every other segment by a hairline. */
  border: var(--token-border-hair) solid transparent;
  background: transparent;
  color: var(--token-color-text-secondary);
  padding: var(--token-space-1) var(--token-space-3);
  border-radius: var(--token-radius-pill);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
}

.seg button:hover,
.seg a:hover {
  color: var(--token-color-text-primary);
}

/* The chosen segment is RAISED OUT of the track, not painted over it.
 *
 * The metaphor the control already has is a groove with a tile sliding along
 * it, and an accent-filled tile reads as a button instead — two of them side by
 * side looked like one lozenge cut in half rather than a choice with one option
 * taken. Lifting the surface colour keeps the accent for things that are
 * actions. */
.seg button[aria-pressed='true'],
.seg a[aria-current='true'] {
  background: var(--token-color-surface);
  color: var(--token-color-text-primary);
  border-color: var(--token-color-border-subtle);
}

/* The same choice, made inside a form somebody else opened. A fieldset carries
   the grouping, so its default border and padding are removed rather than
   styled — the row of options is the affordance, not a box around them. */
.radios {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--token-space-3);
  border: 0;
  margin: 0;
  padding: 0;
  min-width: 0;
}

.pick {
  display: inline-flex;
  align-items: center;
  gap: var(--token-space-2);
  font-size: var(--token-fs-sm);
  color: var(--token-color-text-secondary);
  cursor: pointer;
}

/* Native, and visible. A drawn pill with the input hidden is a control nothing
   announces and nobody can tell is selected — the same lesson the access
   matrix's checkboxes are there for. */
input[type='radio'] {
  accent-color: var(--token-color-accent-fill);
  width: var(--token-size-checkbox);
  height: var(--token-size-checkbox);
  cursor: pointer;
  margin: 0;
}

/* ------------------------------------------------------------------ frame */

.wrap {
  max-width: var(--token-layout-max);
  margin: 0 auto;
  padding: 0 var(--token-space-6) var(--token-space-24);
}

/* The page's vertical rhythm, in one rule.
 *
 * Every block on a page — a heading, a nav, a card, a table — was responsible
 * for its own bottom margin, which meant most of them had none and a page read
 * as one stuck-together column. Spacing BETWEEN siblings belongs to the thing
 * that owns the sequence, and the last child adds nothing, so a page never ends
 * in a gap. */
.wrap > * + * {
  margin-top: var(--token-space-6);
}

/* The heading already carries its own generous padding; a second gap under it
 * pushes the first block of the page too far down. */
.wrap > .hero + * {
  margin-top: 0;
}

.hero {
  padding: var(--token-space-16) 0 var(--token-space-8);
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--token-space-6);
  flex-wrap: wrap;
}

/* The action stays with the title on a wide screen and drops below it on a
 * narrow one, where a button squeezed beside a wrapping heading is worse than
 * a button underneath. */
.hero-action {
  flex: none;
  padding-top: var(--token-space-4);
}

.hero h1 {
  font-size: clamp(var(--token-fs-hero-min), 5.5vw, var(--token-fs-2xl));
}

.lede {
  margin-top: var(--token-space-5);
  font-size: var(--token-fs-md);
  color: var(--token-color-text-secondary);
  max-width: var(--token-measure-lede);
}

.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--token-space-2);
  margin-top: var(--token-space-6);
  padding: var(--token-space-2) var(--token-space-4);
  border: var(--token-border-hair) solid var(--token-color-border-strong);
  border-radius: var(--token-radius-pill);
  background: var(--token-color-surface-raised);
  font-size: var(--token-fs-sm);
  color: var(--token-color-text-secondary);
}

.pill .dot {
  width: var(--token-size-dot-sm);
  height: var(--token-size-dot-sm);
  border-radius: 50%;
  background: var(--token-color-accent-text);
}

section {
  padding-top: var(--token-space-16);
}

.sec-head {
  display: flex;
  align-items: baseline;
  gap: var(--token-space-4);
  margin-bottom: var(--token-space-6);
  border-bottom: var(--token-border-hair) solid var(--token-color-border-subtle);
  padding-bottom: var(--token-space-3);
}

/* Inside a grid the gap already provides the space below a heading; the
   margin was adding a second helping, so a group heading sat noticeably
   further from its table than every other heading on the page. */
.grid > .sec-head {
  margin-bottom: 0;
}

.sec-head .num {
  font-size: var(--token-fs-xs);
  color: var(--token-color-accent-text);
  font-weight: 600;
}

.sec-head h2 {
  font-size: var(--token-fs-xl);
}

.sec-head .note {
  margin-left: auto;
  max-width: var(--token-measure-note);
  text-align: right;
}

.foot {
  margin-top: var(--token-space-24);
  padding-top: var(--token-space-5);
  border-top: var(--token-border-hair) solid var(--token-color-border-subtle);
  font-size: var(--token-fs-xs);
  color: var(--token-color-text-tertiary);
}

/* ------------------------------------------------------------- containers */

.grid {
  display: grid;
  gap: var(--token-space-4);
}

/* `min(…, 100%)` is what stops a grid overflowing its container.
 *
 * `minmax(300px, 1fr)` is not a maximum of 300px — it is a FLOOR, so on a
 * viewport narrower than one column the track keeps its 300px and the row runs
 * off the side of the page. Capping the floor at the container's own width
 * lets the last column shrink instead. */
.grid-card {
  grid-template-columns: repeat(auto-fit, minmax(min(var(--token-layout-card-min), 100%), 1fr));
}

.grid-panel {
  grid-template-columns: repeat(auto-fit, minmax(min(var(--token-layout-panel-min), 100%), 1fr));
}

/* Stat tiles are not panels: at the panel minimum four tiles fell into three
   columns and the fourth was orphaned on its own row. */
.grid-stat {
  grid-template-columns: repeat(auto-fit, minmax(min(var(--token-layout-stat-min), 100%), 1fr));
}

.panel {
  border: var(--token-border-hair) solid var(--token-color-border-subtle);
  border-radius: var(--token-radius-lg);
  background: var(--token-color-surface-raised);
  padding: var(--token-space-5);
}

/* Declared standalone, not as `.panel > .cap`. The design system scopes these
   to a parent, so used anywhere else they did nothing at all and every piece of
   secondary text rendered as full-size body copy. */
.cap {
  font-size: var(--token-fs-xs);
  color: var(--token-color-text-tertiary);
  margin-bottom: var(--token-space-3);
}

.note {
  font-size: var(--token-fs-sm);
  color: var(--token-color-text-tertiary);
}

.desc {
  font-size: var(--token-fs-sm);
  color: var(--token-color-text-secondary);
}

.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--token-space-3);
  text-align: center;
  padding: var(--token-space-10) var(--token-space-4);
}

.empty h3 {
  font-size: var(--token-fs-lg);
}

.empty p {
  font-size: var(--token-fs-sm);
  color: var(--token-color-text-secondary);
  max-width: var(--token-measure-prose);
}

/* ----------------------------------------------------------------- status */

/* State reads as treatment, not hue. The fill belongs to the SUCCESSFUL state:
   before that, "done" was the quietest chip on the page and "failed" the
   loudest, which is backwards for a page people scan for what is finished. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--token-space-1);
  font-size: var(--token-fs-xs);
  font-weight: 500;
  padding: var(--token-inset-chip) var(--token-space-3);
  border-radius: var(--token-radius-pill);
  border: var(--token-border-hair) solid transparent;
  white-space: nowrap;
}

.b-ok {
  background: var(--token-badge-ok-bg);
  color: var(--token-badge-ok-fg);
  border-color: color-mix(in srgb, var(--token-badge-ok-fg) 34%, transparent);
}

.b-run {
  background: transparent;
  color: var(--token-badge-run-fg);
  border-color: var(--token-badge-run-fg);
}

.b-fail {
  background: transparent;
  color: var(--token-badge-fail-fg);
  border-color: var(--token-badge-fail-fg);
  border-style: dashed;
}

.b-ghost {
  background: var(--token-color-surface-inset);
  color: var(--token-badge-ghost-fg);
  border-color: var(--token-color-border-subtle);
}

/* ----------------------------------------------------------------- tables */

/* A box whose content is wider than itself scrolls INSIDE itself, at every
   width. This lived behind `max-width: 640px`, on the assumption that wide
   tables only happen on phones — so a browser at 150% zoom, a half-screen
   window or a laptop showing a ten-column table all fell in the gap and had
   their right-hand columns clipped away with no way to reach them.

   A dozen columns of ids, models and timestamps: left to wrap, a date breaks
   across four lines and the row stops being scannable. Scrolling is the answer,
   and it is not a breakpoint's business. */
.scroller,
.panel {
  overflow-x: auto;
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--token-fs-sm);
}

caption {
  text-align: left;
  font-size: var(--token-fs-xs);
  color: var(--token-color-text-tertiary);
  padding-bottom: var(--token-space-3);
}

th {
  text-align: left;
  font-size: var(--token-fs-xs);
  font-weight: 500;
  color: var(--token-color-text-tertiary);
  padding: 0 var(--token-space-4) var(--token-space-2) 0;
  border-bottom: var(--token-border-hair) solid var(--token-color-border-subtle);
  white-space: nowrap;
}

td {
  padding: var(--token-space-3) var(--token-space-4) var(--token-space-3) 0;
  border-bottom: var(--token-border-hair) solid var(--token-color-border-subtle);
  vertical-align: middle;
}

tr:last-child td {
  border-bottom: 0;
}

th:last-child,
td:last-child {
  padding-right: 0;
}

/* A numeric column: the CELL gets tabular figures so digits line up down the
 * column, the HEADER gets only the alignment.
 *
 * Giving the header the cell's typeface too is what made a header row read as
 * ragged — three words in the body font and one in mono, at the top of a table
 * whose whole job is to be scanned across. */
td.num {
  font-family: var(--token-font-mono);
  font-variant-numeric: tabular-nums;
}

td.num,
th.num {
  text-align: right;
}

td.mono {
  font-family: var(--token-font-mono);
  white-space: nowrap;
}

/* The row under a row: a failure reason, a quote, an empty state. A cell inside
   the row would either widen the table past the viewport or clip the one string
   the page exists to show. Ids and provider messages have no spaces to break
   on, hence `anywhere`. */
td.detail {
  padding-top: 0;
  font-size: var(--token-fs-xs);
  color: var(--token-color-text-tertiary);
  white-space: normal;
  overflow-wrap: anywhere;
}

td.tick,
th.tick {
  text-align: center;
}

/* accent-color tints the native control with the brand and keeps the platform's
   own affordance — obviously clickable, obviously ticked — instead of drawing a
   replacement that only looks like one. */
input[type='checkbox'] {
  accent-color: var(--token-color-accent-fill);
  width: var(--token-size-checkbox);
  height: var(--token-size-checkbox);
  cursor: pointer;
  margin: 0;
}

.tickbox {
  display: inline-flex;
  align-items: center;
  gap: var(--token-space-2);
  font-size: var(--token-fs-sm);
  color: var(--token-color-text-secondary);
  cursor: pointer;
}

/* A visible label in a matrix column still has to be readable, so it gets a
   floor. The hidden variant is excluded: it is out of the flow already, and a
   width on it would be a claim about space nothing occupies. */
td.tick .tickbox .tick-label {
  min-width: var(--token-measure-cell);
  text-align: center;
}

/* ------------------------------------------------------------------ forms */

form.stack {
  display: grid;
  gap: var(--token-space-3);
}

/* inline-flex, not inline: a form holding a control and a button laid them out
   as two blocks, so the select sat above the button instead of beside it. */
form.inline {
  display: inline-flex;
  align-items: center;
  gap: var(--token-space-2);
}

.kit {
  display: flex;
  flex-wrap: wrap;
  gap: var(--token-space-3);
  align-items: center;
}

.control {
  display: inline-flex;
  align-items: center;
  gap: var(--token-space-2);
  font-size: var(--token-fs-sm);
}

.field {
  display: flex;
  align-items: center;
  gap: var(--token-space-2);
  background: var(--token-color-surface-raised);
  border: var(--token-border-hair) solid var(--token-color-border-strong);
  border-radius: var(--token-radius-sm);
  padding: var(--token-space-2) var(--token-space-3);
  /* A minimum rather than a fixed width: pinned, it slid off a 320px screen. */
  min-width: 0;
  flex: 1 1 var(--token-layout-field-basis);
  max-width: var(--token-layout-field-max);
}

/* A field holding a textarea stacks instead of sitting on one line, and takes
   the width it is given rather than the single-line basis. */
.field-tall {
  flex-direction: column;
  align-items: stretch;
  flex-basis: auto;
  max-width: none;
}

.field-tall textarea {
  font: inherit;
  font-size: var(--token-fs-sm);
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--token-color-text-primary);
  width: 100%;
  min-width: 0;
  resize: vertical;
}

.field-tall textarea::placeholder {
  color: var(--token-color-text-tertiary);
}

.field:focus-within {
  border-color: var(--token-color-accent-text);
}

.field input {
  font: inherit;
  font-size: var(--token-fs-sm);
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--token-color-text-primary);
  width: 100%;
  min-width: 0;
}

.field input::placeholder {
  color: var(--token-color-text-tertiary);
}

/* A bare <select> brings its own border and background from the platform, so
   putting one inside .field draws two boxes around one control. It is styled
   directly instead, caret included. */
select {
  font: inherit;
  font-size: var(--token-fs-sm);
  color: var(--token-color-text-primary);
  background: var(--token-color-surface-raised);
  border: var(--token-border-hair) solid var(--token-color-border-strong);
  border-radius: var(--token-radius-sm);
  padding: var(--token-space-2) var(--token-space-8) var(--token-space-2) var(--token-space-3);
  appearance: none;
  cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-size: var(--token-size-caret) var(--token-size-caret),
    var(--token-size-caret) var(--token-size-caret);
  background-position: right var(--token-space-4) center,
    right calc(var(--token-space-4) - var(--token-size-caret)) center;
  background-repeat: no-repeat;
}

.btn {
  font-family: var(--token-font-body);
  font-size: var(--token-fs-sm);
  font-weight: 500;
  padding: var(--token-space-2) var(--token-space-5);
  border-radius: var(--token-radius-pill);
  border: var(--token-border-hair) solid transparent;
  cursor: pointer;
}

/* Every .btn in the design system is a <button>, so underlines never came up.
   Here some of them have to be links, and links arrive underlined. */
a.btn {
  text-decoration: none;
  display: inline-flex;
  align-items: center;
}

.btn-primary {
  background: var(--token-button-primary-bg);
  color: var(--token-button-primary-fg);
}

/* Mixing towards text-primary self-corrects per theme: hover always moves
   towards the foreground, dark or light. */
.btn-primary:hover {
  background: color-mix(in srgb, var(--token-button-primary-bg) 84%, var(--token-color-text-primary));
}

.btn-ghost {
  background: transparent;
  color: var(--token-color-text-primary);
  border-color: var(--token-color-border-strong);
}

.btn-ghost:hover {
  background: var(--token-color-surface-inset);
}

.btn-quiet {
  background: transparent;
  color: var(--token-color-accent-text);
}

.btn[disabled] {
  color: var(--token-color-text-tertiary);
  border-color: var(--token-color-border-subtle);
  cursor: not-allowed;
}

.btn-block {
  width: 100%;
  justify-content: center;
  display: inline-flex;
  align-items: center;
  gap: var(--token-space-2);
  margin-top: var(--token-space-5);
}

/* -------------------------------------------------------------- secondary */

/* Wanted rarely, so not a heading and not a panel: a line you see only when
   you are looking for it. */
.log {
  display: grid;
  gap: var(--token-space-2);
  font-size: var(--token-fs-sm);
}

.log > summary {
  cursor: pointer;
  font-size: var(--token-fs-xs);
  color: var(--token-color-text-tertiary);
  padding: var(--token-space-2) 0;
  list-style: none;
}

/* The arrow is drawn here: ::marker cannot be styled in Safari, and
   ::-webkit-details-marker leaves its own in place, so without list-style:none
   there would be two. */
.log > summary::before {
  content: '▸ ';
}

.log[open] > summary::before {
  content: '▾ ';
}

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

.log > summary:hover {
  color: var(--token-color-text-primary);
}

.log[open] > summary {
  color: var(--token-color-text-secondary);
}

.pager {
  display: flex;
  align-items: center;
  gap: var(--token-space-3);
  margin-top: var(--token-space-4);
}

/* Where you are in the whole list goes at the end of the row, away from the
   controls that move you through it. */
.pager .note {
  margin-left: auto;
}

/* ------------------------------------------------------------- responsive */

@media (max-width: 640px) {
  .sec-head {
    flex-wrap: wrap;
  }
  .sec-head .note {
    margin-left: 0;
    text-align: left;
    max-width: none;
    flex-basis: 100%;
  }
}

/* People export the quota and cost tables to PDF. The generated tokens force
   the light palette for print; what is left is hiding the parts of the page
   that are navigation rather than content. */
@media print {
  .chrome,
  .skip,
  .seg,
  .ctrl,
  .kit,
  .pager,
  .btn,
  .log > summary {
    display: none !important;
  }
  a {
    text-decoration: underline;
  }
}

/* A sortable column header.
 *
 * It is a link, so it inherits the focus ring and the keyboard behaviour every
 * other link already has — a header that only responded to a click would be
 * unreachable without a mouse. It stays the colour of the header text rather
 * than the accent, because a whole row of accent-coloured headings reads as a
 * navigation bar. */
th .sort {
  color: inherit;
  text-decoration: none;
}

th .sort:hover {
  color: var(--token-color-text-primary);
  text-decoration: underline;
}

/* The bar behind a score.
 *
 * A `<meter>` would style itself differently in every browser, so it is two
 * elements with a token-driven width. The number stays next to it in the markup:
 * the bar is the comparison, the number is the value, and a bar alone cannot be
 * read out. */
.meter {
  display: block;
  width: 100%;
  height: var(--token-space-2);
  border-radius: var(--token-radius-sm);
  background: var(--token-color-border-subtle);
  overflow: hidden;
}

.meter > span {
  display: block;
  height: 100%;
  background: var(--token-color-accent-fill);
}

/* A section's action sits at the end of the heading row, after the note that
 * may or may not be there. */
.sec-action {
  margin-left: auto;
  flex: none;
}

/* Styles the views need and the component library does not have.

   Everything here is a shape that only makes sense inside a page: a result
   item with its quote, a month grid. Nothing that belongs to a component lives
   here — a rule for .panel or .badge in this file would be the second copy that
   drifts, which is the whole reason app.css declares no values of its own.

   Same rule as app.css: not one literal. Every value is a token, and
   scripts/lint-literals.ts fails the build on a colour or a px. */

/* ---------------------------------------------------------------- results */

/* A claim, who it belongs to, and the line of the transcript it rests on.
   Rows in a list rather than cards: five cards of prose is five borders around
   sentences, and the dividing line is all the separation a claim needs. */
.item {
  padding: var(--token-space-4) 0;
  border-bottom: var(--token-border-hair) solid var(--token-color-border-subtle);
  display: grid;
  gap: var(--token-space-2);
}

.item:last-child {
  border-bottom: 0;
}

.item > p {
  max-width: var(--token-measure-lede);
}

/* Attribution and "this did not come up" alike: secondary, and small. */
.who {
  font-size: var(--token-fs-xs);
  color: var(--token-color-text-tertiary);
}

/* The transcript's own words. Marked as quoted rather than styled as prose,
   because the difference between what was said and what the model concluded is
   the entire value of the feature. */
.quote {
  font-size: var(--token-fs-sm);
  color: var(--token-color-text-secondary);
  border-left: var(--token-border-accent) solid var(--token-color-accent-wash);
  padding-left: var(--token-space-3);
  max-width: var(--token-measure-lede);
}

/* ------------------------------------------------------------- transcript */

/* The longest page in the product, so the mechanics that only matter at length
   all matter here.

   The sticky chrome STAYS. On a two-hour transcript it is the difference
   between a way back that is always to hand and one that is forty thousand
   words above you; the header is a single row, and `scroll-padding-top` in
   app.css already keeps anchor targets clear of it. */
.transcript {
  display: grid;
  gap: var(--token-space-6);
}

.turn {
  display: grid;
  gap: var(--token-space-2);
}

/* Who spoke, and where in the recording. Small and quiet: it is the label on
   the block, not part of what was said. */
.turn-who {
  display: flex;
  align-items: baseline;
  gap: var(--token-space-3);
  font-size: var(--token-fs-xs);
  color: var(--token-color-text-tertiary);
}

.turn-at {
  color: var(--token-color-text-meta);
  text-decoration: none;
}

.turn-at:hover {
  color: var(--token-color-accent-text);
}

/* The reading measure is the whole point of a transcript page: 40 000 words set
   to the full width of a desktop window is unreadable however good the type is.
   `overflow-wrap` because a transcript is machine-produced text and a single
   unbroken token — a pasted URL, a run-together artefact of recognition — would
   otherwise push the measure wider than the window. */
.turn-text {
  display: grid;
  gap: var(--token-space-3);
  max-width: var(--token-measure-prose);
  color: var(--token-color-text-primary);
  line-height: var(--token-lh-relaxed);
  overflow-wrap: anywhere;
}

/* --------------------------------------------------------------- calendar */

.monthnav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--token-space-3);
  font-family: var(--token-font-display);
}

.month {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: var(--token-gap-hair);
}

.dow {
  font-size: var(--token-fs-xs);
  color: var(--token-color-text-tertiary);
  padding: var(--token-space-2);
  text-align: center;
}

/* A fixed minimum, not an aspect ratio: a day holding four calls has to grow,
   and a square cell would clip them behind nothing at all. */
.day {
  min-height: var(--token-layout-stat-min);
  border: var(--token-border-hair) solid var(--token-color-border-subtle);
  border-radius: var(--token-radius-sm);
  background: var(--token-color-surface-raised);
  padding: var(--token-space-2);
  display: grid;
  align-content: start;
  gap: var(--token-gap-hair);
  overflow: hidden;
}

/* Leading blanks carry no border: an empty box before the 1st reads as a day. */
.day-blank {
  border-color: transparent;
  background: transparent;
}

.day.today {
  border-color: var(--token-color-accent-text);
}

.dayno {
  font-size: var(--token-fs-xs);
  color: var(--token-color-text-tertiary);
  font-variant-numeric: tabular-nums;
}

/* One line per call. Truncated rather than wrapped: a cell that grows with the
   longest title in the month makes every other row taller for nothing. */
.ev {
  font-size: var(--token-fs-xs);
  color: var(--token-color-text-primary);
  text-decoration: none;
  background: var(--token-color-surface-inset);
  border-radius: var(--token-radius-sm);
  padding: var(--token-inset-chip) var(--token-space-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

a.ev:hover {
  background: var(--token-color-accent-wash);
}

.hm {
  color: var(--token-color-text-tertiary);
  font-variant-numeric: tabular-nums;
}

/* Below this the seven columns are unreadable, so the month becomes a list of
   the days that have something in them — which is what it now is. It used to
   say this while rendering every day of the month at full height, so a phone
   got a 4,000px column of empty numbered boxes with no weekday headings.

   The grid alignment is what the empty cells were for; in one column there is
   nothing to align to, so they go, and the height floor goes with them. */
@media (max-width: 640px) {
  .month {
    grid-template-columns: 1fr;
  }

  .dow,
  .day-blank,
  .day-empty {
    display: none;
  }

  .day {
    min-height: 0;
  }
}
