/* LLMBench portal — S10 Step 1.5.
 *
 * The :root block below is copied VERBATIM from
 * sites/llm-bench/assets/css/style.css :5-16 (anchor c21) so the portal and the
 * public benchmark site are one visual language, then EXTENDED with the state,
 * band and basis tokens a dashboard needs (README §10).
 *
 * Mobile-first. The only breakpoints are §P8's verification widths:
 * 1440 / 1024 / 768 / 390 / 320 — do not invent a sixth.
 *
 * No framework, no CDN, no chart library (§STOP-8).
 */

/* --- verbatim from the public site ------------------------------------- */
:root {
  --color-bg: #fafaf7;
  --color-fg: #1a1a1a;
  --color-muted: #6b6b6b;
  --color-accent: #2a5d9f;
  --color-border: #e4e4dc;
  --color-card-bg: #ffffff;
  --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --max-width: 1100px;
  --pad: 1.5rem;
}

/* --- portal extensions -------------------------------------------------- */
:root {
  /* State tokens. Colour is ALWAYS a second channel here: every state also
     carries a text label and an icon/pattern (README §10 item 5), so a
     colour-blind or monochrome reader loses nothing. */
  --state-ok: #1f7a4d;
  --state-warn: #8a6100;
  --state-error: #a32020;
  --state-neutral: var(--color-muted);
  --state-ok-bg: #edf7f1;
  --state-warn-bg: #fdf6e6;
  --state-error-bg: #fbeded;
  --state-neutral-bg: #f2f2ee;

  /* Band tokens. Deliberately CALM — an exploring task is normal operation,
     not an error (§P12), so `exploring` must never inherit error styling. */
  --band-exploring: #4a5b7a;
  --band-exploring-bg: #eef1f6;
  --band-mature: var(--state-ok);
  --band-mature-bg: var(--state-ok-bg);
  --band-insufficient: var(--color-muted);
  --band-insufficient-bg: var(--state-neutral-bg);

  --radius: 6px;
  --focus-ring: 3px solid var(--color-accent);
}

* { box-sizing: border-box; }

html { font-size: 16px; }

body.portal {
  margin: 0;
  background: var(--color-bg);
  color: var(--color-fg);
  font-family: var(--font-body);
  line-height: 1.5;
}

/* Focus is never removed, only restyled — keyboard review is a DoD item. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: var(--focus-ring);
  outline-offset: 2px;
}

/* --- skip link ---------------------------------------------------------- */
/* Off-screen until focused, and the first focusable element in the document. */
.portal-skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  padding: 0.5rem 1rem;
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  z-index: 100;
}
.portal-skip-link:focus {
  left: 0;
}

/* --- header ------------------------------------------------------------- */
.portal-header {
  background: var(--color-card-bg);
  border-bottom: 1px solid var(--color-border);
}
.portal-header__inner {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0.75rem var(--pad);
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
  justify-content: space-between;
}
.portal-brand {
  font-weight: 600;
  letter-spacing: 0.01em;
}
.portal-identity {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.portal-tenant {
  font-weight: 500;
}

/* --- chips -------------------------------------------------------------- */
.portal-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.35rem;
  padding: 0.15rem 0.5rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--state-neutral-bg);
  font-size: 0.85rem;
}
.portal-chip__label {
  color: var(--color-muted);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.portal-chip__value {
  font-weight: 600;
}

/* --- layout ------------------------------------------------------------- */
.portal-body {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: var(--pad);
  display: block;
}
.portal-main {
  min-width: 0; /* lets long tables scroll instead of blowing out the grid */
}
.portal-main:focus {
  outline: none; /* programmatic focus target for the skip link */
}

/* --- nav ---------------------------------------------------------------- */
.portal-nav {
  margin-bottom: 1.5rem;
}
.portal-nav__group + .portal-nav__group {
  margin-top: 1rem;
}
/* A <span>, not a heading — see base.html for why (heading-level skip). */
.portal-nav__group-label {
  display: block;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-muted);
  margin: 0 0 0.35rem;
}
.portal-nav__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.75rem;
}
.portal-nav__link {
  display: inline-block;
  padding: 0.25rem 0;
  color: var(--color-accent);
  text-decoration: none;
  border-bottom: 2px solid transparent;
}
.portal-nav__link:hover { text-decoration: underline; }
/* Active state is weight + rule + aria-current, never colour alone. */
.portal-nav__link--active {
  color: var(--color-fg);
  font-weight: 600;
  border-bottom-color: var(--color-accent);
}

/* --- typography --------------------------------------------------------- */
.portal-page-title {
  margin: 0 0 0.5rem;
  font-size: 1.5rem;
}
.portal-lede {
  margin: 0 0 1.25rem;
  color: var(--color-muted);
}

/* --- money (§P10) ------------------------------------------------------- */
/* Tabular numerals on EVERY numeric cell, so columns align and a changing
   figure does not jitter (README §10 item 6). */
.portal-money,
.portal-num,
.portal-table td.portal-num {
  font-variant-numeric: tabular-nums;
  font-family: var(--font-mono);
}
.portal-money {
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
  flex-wrap: wrap;
}
.portal-money__amount {
  font-weight: 600;
}
/* The basis label is NOT decoration — it is what stops the four cost concepts
   collapsing into one number (README §4.4). It renders adjacent, always. */
.portal-money__basis {
  font-family: var(--font-body);
  font-size: 0.8rem;
  color: var(--color-muted);
  border-bottom: 1px dotted var(--color-border);
  cursor: help;
  text-decoration: none;
}

/* --- states (§Step 4) --------------------------------------------------- */
.portal-state {
  display: block;
  padding: 0.75rem 1rem;
  border: 1px solid var(--color-border);
  border-left-width: 4px;
  border-radius: var(--radius);
  background: var(--state-neutral-bg);
  border-left-color: var(--state-neutral);
}
.portal-state__head {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}
/* The icon is a text glyph with its own accessible name; it is the non-colour
   channel, so it must never be display:none. */
.portal-state__icon {
  font-family: var(--font-mono);
  font-weight: 700;
  flex: none;
}
.portal-state__label {
  font-weight: 600;
}
.portal-state__detail {
  margin: 0.35rem 0 0;
  color: var(--color-muted);
}

.portal-state[data-portal-state="error"] {
  background: var(--state-error-bg);
  border-left-color: var(--state-error);
}
.portal-state[data-portal-state="permission_denied"],
.portal-state[data-portal-state="past_due"] {
  background: var(--state-warn-bg);
  border-left-color: var(--state-warn);
}
.portal-state[data-portal-state="mature"] {
  background: var(--band-mature-bg);
  border-left-color: var(--band-mature);
}
/* Calm, NOT alarmist: exploring is normal operation (§P12). */
.portal-state[data-portal-state="exploring"] {
  background: var(--band-exploring-bg);
  border-left-color: var(--band-exploring);
}
.portal-state[data-portal-state="insufficient_evidence"] {
  background: var(--band-insufficient-bg);
  border-left-color: var(--band-insufficient);
}

/* --- maturity ----------------------------------------------------------- */
.portal-maturity__note {
  margin: 0.35rem 0 0;
  font-size: 0.85rem;
  color: var(--color-muted);
}

/* --- footer ------------------------------------------------------------- */
.portal-footer {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 1rem var(--pad) 2rem;
  border-top: 1px solid var(--color-border);
}
.portal-footer__note {
  margin: 0;
  font-size: 0.85rem;
  color: var(--color-muted);
}

/* --- breakpoints (§P8: 320 / 390 / 768 / 1024 / 1440) -------------------- */
/* Mobile-first: 320 and 390 are the unqualified rules above. */

@media (min-width: 768px) {
  .portal-nav__list { gap: 0.25rem 1.25rem; }
}

@media (min-width: 1024px) {
  /* The nav becomes a rail once there is room for one. */
  .portal-body {
    display: grid;
    grid-template-columns: 13rem minmax(0, 1fr);
    gap: 2rem;
  }
  .portal-nav { margin-bottom: 0; }
  .portal-nav__list { flex-direction: column; gap: 0.15rem; }
}

@media (min-width: 1440px) {
  :root { --max-width: 1280px; }
  .portal-body { grid-template-columns: 15rem minmax(0, 1fr); }
}

/* ======================================================================== *
 * S10-γ — the page surfaces (Overview, Task models, Playground)
 *
 * Still mobile-first, still no framework, and still only §P8's five widths.
 * Two rules here are contract rather than taste:
 *
 *   1. `tabular-nums` on every numeric cell (README §10 item 6) — a column of
 *      figures that does not align is a column nobody scans.
 *   2. Nothing conveys state by colour alone. Every coloured token below sits
 *      next to a glyph and a word that already say the same thing, so removing
 *      the colour loses nothing (README §10 item 5).
 * ======================================================================== */

/* --- text --------------------------------------------------------------- */
/* Long display names WRAP. Clipping one hides the string a customer searched
   for, and a 60-character task-model name is a real case, not an edge one. */
.portal-wrap {
  overflow-wrap: anywhere;
  word-break: break-word;
}

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

.portal-note {
  display: block;
  margin: 0.35rem 0 0;
  font-size: 0.85rem;
  color: var(--color-muted);
}

/* --- sections ----------------------------------------------------------- */
.portal-section {
  margin: 2rem 0 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--color-border);
}
.portal-section > h2 {
  margin: 0 0 0.75rem;
  font-size: 1.15rem;
}
.portal-section h3 {
  margin: 1.25rem 0 0.4rem;
  font-size: 0.98rem;
}

.portal-jump ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
  margin: 0.75rem 0 0;
  padding: 0;
  list-style: none;
  font-size: 0.9rem;
}

/* --- tables ------------------------------------------------------------- */
/* A wide table scrolls INSIDE its own wrapper; the page body never scrolls
   sideways at 320px. `display: block` on the table is what gives it its own
   scroll context without a wrapper element in every template. */
.portal-table {
  width: 100%;
  margin: 0.75rem 0 0;
  border-collapse: collapse;
  font-size: 0.92rem;
  display: block;
  overflow-x: auto;
}
.portal-table caption {
  caption-side: top;
  text-align: left;
  padding: 0 0 0.5rem;
  font-size: 0.85rem;
  color: var(--color-muted);
}
.portal-table th,
.portal-table td {
  padding: 0.5rem 0.75rem 0.5rem 0;
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--color-border);
}
.portal-table thead th {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-muted);
  white-space: nowrap;
}
.portal-table td.portal-num,
.portal-table th.portal-num,
.portal-table .portal-num {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  /* Numbers align RIGHT so digits of the same magnitude stack, which is what
     makes a column scannable. Tabular figures alone do not do this. */
  text-align: right;
}
.portal-kv .portal-num { text-align: right; }

/* --- task-model identity cell ------------------------------------------- */
.portal-tm { max-width: 22rem; }
.portal-tm__name { display: block; font-weight: 600; }
.portal-tm__slug {
  display: inline-block;
  margin: 0.15rem 0.4rem 0 0;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--color-muted);
}

/* Provenance and on/off flags. The glyph is a distinct SHAPE per value, not one
   shape in two colours — that is what survives a monochrome render. */
.portal-badge,
.portal-flag {
  display: inline-block;
  padding: 0.05rem 0.4rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  font-size: 0.75rem;
  white-space: nowrap;
}
.portal-badge__glyph { font-family: var(--font-mono); }
.portal-badge--system { border-color: var(--color-accent); color: var(--color-accent); }
.portal-badge--own { color: var(--color-muted); }
.portal-flag--on { color: var(--state-ok); border-color: var(--state-ok); }
.portal-flag--off { color: var(--color-muted); background: var(--state-neutral-bg); }

/* --- key/value lists ---------------------------------------------------- */
.portal-kv {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.15rem 1rem;
  margin: 0.75rem 0 0;
}
.portal-kv dt {
  font-size: 0.8rem;
  color: var(--color-muted);
}
.portal-kv dd {
  margin: 0 0 0.5rem;
  overflow-wrap: anywhere;
}
.portal-kv dd.portal-num { font-variant-numeric: tabular-nums; }

/* --- tiles -------------------------------------------------------------- */
.portal-tiles {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
  margin: 0.75rem 0 0;
}
.portal-tile {
  padding: 0.75rem;
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}
.portal-tile dt {
  font-size: 0.8rem;
  color: var(--color-muted);
}
.portal-tile dd {
  margin: 0.25rem 0 0;
  font-size: 1.25rem;
  font-variant-numeric: tabular-nums;
}
/* A money node inside a tile is a label plus a number, so it must not inherit
   the tile's display size — the basis label is the point, not decoration. */
.portal-tile dd .portal-money { font-size: 0.95rem; }

.portal-absences {
  margin: 0.5rem 0 0;
  padding-left: 1.1rem;
  font-size: 0.9rem;
  color: var(--color-muted);
}
.portal-absences li { margin-bottom: 0.35rem; }

/* --- readiness checklist ------------------------------------------------ */
.portal-checklist {
  margin: 0.75rem 0 0;
  padding: 0;
  list-style: none;
}
.portal-checklist__item {
  display: grid;
  grid-template-columns: 1.25rem minmax(0, 1fr);
  gap: 0 0.5rem;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--color-border);
}
.portal-checklist__mark {
  font-family: var(--font-mono);
  color: var(--color-muted);
}
.portal-checklist__item[data-ok="true"] .portal-checklist__mark { color: var(--state-ok); }
.portal-checklist__label { font-weight: 600; }
.portal-checklist__todo {
  grid-column: 2;
  font-size: 0.88rem;
  color: var(--color-muted);
}

/* --- forms -------------------------------------------------------------- */
.portal-filters,
.portal-form {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin: 1rem 0 0;
  padding: 1rem;
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}
.portal-field { display: flex; flex-direction: column; gap: 0.25rem; }
.portal-field > label { font-size: 0.85rem; font-weight: 600; }
.portal-field input,
.portal-field select,
.portal-field textarea {
  padding: 0.45rem 0.5rem;
  font: inherit;
  color: inherit;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}
.portal-field textarea { font-family: var(--font-mono); font-size: 0.9rem; resize: vertical; }
.portal-field :disabled { opacity: 0.6; cursor: not-allowed; }

.portal-btn {
  display: inline-block;
  align-self: flex-start;
  padding: 0.45rem 0.9rem;
  font: inherit;
  color: var(--color-card-bg);
  background: var(--color-accent);
  border: 1px solid var(--color-accent);
  border-radius: var(--radius);
  text-decoration: none;
  cursor: pointer;
}
.portal-btn:disabled { opacity: 0.55; cursor: not-allowed; }
.portal-btn--quiet {
  color: var(--color-accent);
  background: transparent;
}

.portal-pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin: 1rem 0 0;
}

/* --- code --------------------------------------------------------------- */
.portal-code {
  margin: 0.5rem 0 0;
  padding: 0.75rem;
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  line-height: 1.45;
  background: var(--color-card-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}
/* An answer is prose, so it wraps rather than scrolling. */
.portal-code--answer { white-space: pre-wrap; overflow-wrap: anywhere; }

/* --- β′: notices, action clusters, the one-time reveal ------------------ */
/* Post-redirect notices. Deliberately NOT a live region (see base.html) —
   they arrive with a fresh page load and a screen reader reads them in
   document order. Each carries its level as TEXT-adjacent structure
   (`data-portal-notice`), never colour alone. */
.portal-notices { margin: 0 0 1rem; }
.portal-notice {
  margin: 0 0 0.5rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--color-border);
  border-left-width: 4px;
  border-radius: var(--radius);
  background: var(--color-card-bg);
  font-size: 0.92rem;
}
.portal-notice--success { border-left-color: var(--state-ok); }
.portal-notice--error { border-left-color: var(--state-error); }
.portal-notice--warning { border-left-color: var(--state-warn); }

/* A row's controls. Each control is its own <form> (one POST route per act),
   so they need a flex row rather than inheriting the vertical `.portal-form`
   stack. `align-items: flex-end` keeps a labelled <select> aligned with the
   bare buttons beside it. */
.portal-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.4rem 0.6rem;
}
.portal-actions form {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.4rem;
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
}

/* The one-time secret reveal. Loud on purpose: this is the only moment the
   value exists, and a customer who scrolls past it has lost it. The warning is
   a SENTENCE, not a colour (README §10 item 5). */
.portal-reveal {
  margin: 1rem 0 0;
  padding: 1rem;
  background: var(--color-card-bg);
  border: 2px solid var(--color-accent);
  border-radius: var(--radius);
}
.portal-reveal__title { margin: 0 0 0.5rem; font-size: 1.05rem; }
.portal-reveal__warning { margin: 0 0 0.75rem; }
.portal-reveal__label { margin: 0 0 0.5rem; font-size: 0.9rem; color: var(--color-muted); }
.portal-reveal__value {
  font-family: var(--font-mono);
  font-size: 0.9rem;
  /* Wraps rather than scrolling: a token that runs off the edge of a 320px
     screen is a token the customer cannot select in one gesture. */
  overflow-wrap: anywhere;
}

/* --- γ breakpoints ------------------------------------------------------ */
@media (min-width: 768px) {
  .portal-filters { flex-direction: row; flex-wrap: wrap; align-items: flex-end; }
  .portal-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .portal-kv { grid-template-columns: 14rem minmax(0, 1fr); }
  .portal-kv dt { padding-top: 0.1rem; }
}

@media (min-width: 1024px) {
  .portal-tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  /* Wide enough for real columns: the table stops being its own scroll box. */
  .portal-table { display: table; }
}

/* --- reduced motion (§P8 / §Step 12.2) ---------------------------------- *
 *
 * The portal ships NO animation or transition today, so this rule changes
 * nothing right now — and it is here precisely because that is a fact about
 * this moment rather than a property of the stylesheet.
 *
 * A reduced-motion query added on the day the first transition is written is
 * added by someone who remembered; one that is already here is honoured by
 * someone who did not. The failure mode it guards against is not a stylistic
 * one: for a reader with a vestibular disorder, an unannounced transition is
 * nausea, and they find out by experiencing it.
 *
 * `animation: none` over `!important` is deliberate — the point is to defeat a
 * future author's declaration, and a non-important rule at equal specificity
 * would lose to it.
 */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}


/* --- usage: daily spend chart ------------------------------------------- */
.portal-chart {
  display: block;
  margin: 0.75rem 0 0.5rem;
  background: var(--surface-2, #f6f6f4);
  border-radius: 4px;
}
.portal-chart__bar { fill: var(--accent, #2a5d9f); }
.portal-chart__bar:hover { fill: var(--accent-strong, #1e4373); }
.portal-details { margin-top: 0.5rem; }
.portal-details > summary { cursor: pointer; font-size: 0.9rem; }
@media (prefers-reduced-motion: no-preference) {
  .portal-chart__bar { transition: fill 120ms ease-in-out; }
}

.portal-chart__axis { stroke: var(--color-border, #d8d8d2); stroke-width: 1; }
.portal-chart__tick {
  font-size: 10px;
  fill: var(--color-muted, #6b6b66);
  font-variant-numeric: tabular-nums;
}
/* A money value in a numeric cell aligns right like any other number. */
.portal-table td.portal-num .portal-money { justify-content: flex-end; }
