/* RIGOR companion.

   The palette, the type, the woven band, the radii and the shadows are taken
   from the Peru Observatorio's theme so the two products read as one family.
   RIGOR uses indigo, one of the Observatorio's own five threads, as its action
   colour, so it is recognisably a sibling rather than a copy.

   The fonts are the Observatorio's, served from this machine. Nothing on this
   page reaches the network: a screen handling an unpublished manuscript should
   not need to. */

@font-face {
  font-family: "Bricolage Grotesque Variable";
  src: url("fonts/bricolage-grotesque.woff2") format("woff2-variations");
  font-weight: 200 800; font-display: swap;
}
@font-face {
  font-family: "Hanken Grotesk Variable";
  src: url("fonts/hanken-grotesk.woff2") format("woff2-variations");
  font-weight: 100 900; font-display: swap;
}

:root {
  /* warm Andean paper palette — light, luminous, textile-inspired */
  --bg: #e6d6b9;
  --bg-2: #dcc7a5;
  --panel: #f3e7d1;
  --panel-2: #eadbc1;
  --border: #d8c39f;
  --border-strong: #c9ae82;
  --ink: #2c2318;
  --text: #34291c;
  --text-dim: #6f6150;
  --text-mute: #7d6f5b;

  --terracotta: #c85a34;
  --teal: #157a6e;
  --ochre: #d99a2b;
  --plum: #8a4a6b;
  --indigo: #3f5aa6;

  /* Terracotta carries the product, the Mediterranean teal answers it. The
     indigo stays in the palette because the woven band uses all five threads,
     but it is no longer the accent. */
  --accent: var(--terracotta);
  --accent-deep: #a8461f;
  --accent-soft: #f0d9c6;

  --fatal: #9b2c1a;
  --major: var(--terracotta);
  --moderate: var(--ochre);
  --minor: var(--text-mute);
  --verified: var(--teal);

  --radius: 16px;
  --radius-sm: 10px;
  --shadow: 0 10px 40px -12px rgba(80, 50, 20, .22);
  --shadow-sm: 0 2px 14px -6px rgba(80, 50, 20, .18);
  --top-h: 66px;

  --font-display: "Bricolage Grotesque Variable", "Bricolage Grotesque", Georgia, serif;
  --font-body: "Hanken Grotesk Variable", "Hanken Grotesk", system-ui, sans-serif;
  --font-mono: "Cascadia Mono", Consolas, ui-monospace, monospace;

  --ease: cubic-bezier(.22, .61, .36, 1);

  /* the signature band: one thread per audit dimension */
  --tejido: repeating-linear-gradient(90deg,
    #3f5aa6 0 6px, #157a6e 6px 12px, #d99a2b 12px 18px,
    #c85a34 18px 24px, #8a4a6b 24px 30px);
}

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body { overflow-x: hidden; }        /* nothing here should scroll sideways */
body {
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
body::before {
  content: ""; position: fixed; inset: 0; z-index: -2; pointer-events: none;
  background:
    radial-gradient(1200px 700px at 85% -10%, rgba(63, 90, 166, .07), transparent 60%),
    radial-gradient(900px 600px at -5% 100%, rgba(21, 122, 110, .06), transparent 55%);
}
body::after {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none; opacity: .05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}
h1, h2, h3, .brand-text strong { font-family: var(--font-display); font-weight: 700; }
button, input { font-family: inherit; }
button { cursor: pointer; }
a { color: inherit; text-decoration: none; }
::selection { background: var(--accent-soft); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 6px; }
.hidden { display: none !important; }

/* ---------------------------------------------------------- topbar */
.topbar {
  height: var(--top-h); display: flex; align-items: center; gap: 14px;
  padding: 0 24px; background: rgba(243, 231, 209, .90);
  backdrop-filter: blur(12px) saturate(1.2);
  border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 40;
}
.topbar::after {
  content: ""; position: absolute; left: 0; bottom: -2px; height: 2px; width: 210px;
  background: var(--tejido); animation: tejido-drift 24s linear infinite; opacity: .85;
}
@keyframes tejido-drift { from { background-position: 0 0; } to { background-position: 30px 0; } }
.brand { display: flex; align-items: center; gap: 13px; }
/* The mark is the way home: clicking it returns to the start. It carries a
   quiet hover so the affordance is visible without turning the corner of every
   page into a button. */
a.brand { cursor: pointer; }
a.brand .brand-mark { transition: transform .18s var(--ease), box-shadow .18s var(--ease); }
a.brand:hover .brand-mark {
  transform: translateY(-1px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .9),
              inset 0 -2px 4px rgba(150, 100, 60, .12),
              0 3px 8px rgba(80, 50, 20, .24);
}
a.brand:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 12px; }
@media (prefers-reduced-motion: reduce) { a.brand .brand-mark { transition: none; } }
/* The mark is a small cream tile with a terracotta R cut into it: pressed
   paper rather than a coloured chip. It sits a shade deeper than the bar behind
   it so it reads as an object, and the letter carries a light edge above and a
   shadow below, which is what gives it volume. */
.brand-mark {
  width: 32px; height: 32px; border-radius: 10px; display: grid; place-items: center;
  background: linear-gradient(160deg, var(--panel) 0%, var(--panel-2) 55%,
                              var(--bg-2) 100%);
  border: 1px solid var(--border-strong);
  color: var(--terracotta);
  font-size: 18px; font-weight: 800; line-height: 1;
  font-family: var(--font-display);
  text-shadow: 0 1px 0 rgba(255, 255, 255, .95),
               0 -1px 0 rgba(120, 60, 30, .18);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .9),
              inset 0 -2px 4px rgba(150, 100, 60, .12),
              0 1px 2px rgba(80, 50, 20, .18);
}
.brand-text { display: flex; flex-direction: column; line-height: 1.1; }
/* The wordmark carries the accent; the line under it stays quiet. */
.brand-text strong {
  font-family: var(--font-display); font-size: 17px; letter-spacing: .14em;
  font-weight: 800; color: var(--accent);
}
.brand-text small { color: var(--text-mute); font-size: 11px; font-weight: 500; }
.topbar-spacer { flex: 1; }
.topbar-link {
  color: var(--text-dim); font-size: 13px; font-weight: 600; padding: 7px 14px;
  border-radius: 20px; border: 1px solid var(--border); background: none;
  transition: all .2s var(--ease);
}
.topbar-link:hover { color: var(--accent); border-color: var(--accent); }
.topbar-state {
  display: flex; align-items: center; gap: 7px; font-size: 12px;
  color: var(--text-mute); font-weight: 600;
}
.topbar-state .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--teal); }

/* The shell is wide; the measure is not.

   A 54rem column on a 27-inch screen is a narrow strip of work with a field of
   empty page on either side of it, and the emptiness reads as an unfinished
   page rather than as restraint. The shell now takes the width, and the things
   that would be hurt by it -- running prose, which becomes unreadable past
   about seventy characters -- keep their own measure below. The grids of
   options, engines and stages simply get more room, which is what they wanted
   all along. */
main {
  width: 100%;
  /* 86rem was about 1376px, so on an ordinary desktop at 100% zoom the intake
     stretched the full width of the window and every card became a wide, thin
     band with its content marooned at the left. It only looked right at 150%
     zoom, because zooming NARROWS the effective viewport -- the page was in
     effect designed at a width nobody browses at. 68rem keeps the option grids
     two and three across while the shell stays a readable column. */
  /* FLUID, not a breakpoint. A fixed 68rem was 1088px at every zoom level, so
     on a 1905px monitor it left 400px of dead margin, and at 150% -- where the
     CSS viewport is about 1270px -- a max-width media query set just below that
     never fired at all. `min()` makes the column take 94% of whatever viewport
     it actually has and stop growing on a very wide screen, which is the
     behaviour both complaints were asking for: not stranded in the middle at
     100%, not cramped at 150%. */
  max-width: min(76rem, 94vw);
  margin: 0 auto;
  padding: 26px clamp(14px, 2vw, 32px) 64px;
}

/* At 150% browser zoom the CSS viewport is only about 1045px, so the 68rem
   column already fills it and the padding is the only thing left taking width.
   Give it back: the reading measure inside the cards is capped separately, so
   this widens the shell without lengthening a line of prose. */
@media (max-width: 78rem) {
  .card { padding-left: 14px; padding-right: 14px; }
  /* The hero headline is the thing that actually overflowed at 150%: it is a
     single unwrapped line at a fixed 30px. Let it scale with the space it has
     instead of running off the edge. */
  .hero h1 { font-size: clamp(22px, 3.4vw, 30px); }
}

/* Running prose keeps a reading measure whatever the shell does. */
.card > p, .card li, .caveat, .footnote, .locked-note, .activity {
  max-width: 46rem;
}

.hero { margin-bottom: 20px; }
/* The display face and the warm ink, with the running state in terracotta so
   the page's own voice matches the mark in the corner. */
.hero h1 {
  font-family: var(--font-display); font-weight: 700;
  font-size: 30px; margin: 4px 0 8px; letter-spacing: -.4px; color: var(--ink);
}
#running-title { color: var(--accent); }
.hero p { color: var(--text-dim); font-size: 15px; margin: 0; max-width: 620px; }
#running-sub { color: var(--text-mute); font-family: var(--font-display); }

/* Five short threads under the opening line, one for each dimension the audit
   weaves together, echoing the band in the top bar. A signature, not a rule:
   the threads stagger in on load and hold. */
.hero-threads { display: flex; gap: 5px; margin-top: 16px; }
.hero-threads i {
  height: 4px; width: 34px; border-radius: 2px; display: block;
  transform-origin: left; animation: thread-in .55s var(--ease) both;
}
.hero-threads i:nth-child(1) { background: var(--indigo);     animation-delay: .28s; }
.hero-threads i:nth-child(2) { background: var(--teal);       animation-delay: .36s; }
.hero-threads i:nth-child(3) { background: var(--ochre);      animation-delay: .44s; }
.hero-threads i:nth-child(4) { background: var(--terracotta); animation-delay: .52s; width: 46px; }
.hero-threads i:nth-child(5) { background: var(--plum);       animation-delay: .60s; }
@keyframes thread-in { from { opacity: 0; transform: scaleX(0); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .hero-threads i { animation: none; } }

/* ----------------------------------------------------------- cards */
.card {
  position: relative;
  /* Slightly translucent so the geoglyph field behind shows THROUGH the frame.
     Only the panel is thinned -- the text sits in its own elements at full
     strength, so legibility is untouched.

     The opaque declaration comes FIRST as a fallback. A browser without
     color-mix drops the second line and keeps this one; without it, such a
     browser would drop the only background rule and render the cards with no
     panel at all -- text straight onto the geoglyph field. */
  background: var(--panel);
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  border: 1px solid var(--border);
  border-radius: var(--radius); padding: 18px 22px; margin-bottom: 11px;
  box-shadow: var(--shadow-sm);
  transition: box-shadow .24s var(--ease), border-color .24s var(--ease),
              transform .24s var(--ease);
}
/* The step in hand carries a thread of the signature band down its edge, so
   the eye knows where the work is without reading the numbers. The bar grows
   from nothing as focus lands and retracts as it leaves, which is what turns a
   stack of panels into a sequence a person is moving through. */
.card::before {
  content: ""; position: absolute; left: 0; top: 10px; bottom: 10px; width: 3px;
  border-radius: 0 3px 3px 0;
  background: var(--accent); transform: scaleY(0); transform-origin: top;
  transition: transform .32s var(--ease); pointer-events: none;
}
.card.active { box-shadow: var(--shadow); border-color: var(--border-strong); }
.card.active::before { transform: scaleY(1); }
.card.locked::before { display: none; }

/* On first paint the intake steps settle in one after another rather than
   arriving all at once, so the page reads as being laid out for the reader. */
#intake > .card { animation: card-in .5s var(--ease) both; }
#intake > .card:nth-of-type(1) { animation-delay: .04s; }
#intake > .card:nth-of-type(2) { animation-delay: .10s; }
#intake > .card:nth-of-type(3) { animation-delay: .16s; }
#intake > .card:nth-of-type(4) { animation-delay: .22s; }
@keyframes card-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  #intake > .card { animation: none; }
  .card::before { transition: none; }
}
/* Dimmed with a filter rather than `opacity`, because the entrance animation
   holds its own final opacity and would otherwise light every locked card back
   up on load. */
.card.locked { filter: opacity(.55); }
.card.locked > *:not(h2):not(.locked-note) { display: none; }
.card .locked-note { margin: 0; font-size: 13.5px; color: var(--text-mute); }
.card:not(.locked) .locked-note { display: none; }
.card > h2 {
  font-family: var(--font-body); font-size: 12px; font-weight: 700;
  letter-spacing: .12em; text-transform: uppercase; color: var(--text-mute);
  margin: 0 0 14px; display: flex; align-items: center; gap: 9px;
}
.card > h2 .num { color: var(--accent); }
.card > h2 .hint { margin-left: auto; text-transform: none; letter-spacing: 0; font-weight: 500; }

/* upload */
.drop {
  width: 100%; border: 1.5px dashed var(--border-strong); border-radius: var(--radius-sm);
  background: var(--panel-2); padding: 40px 20px; text-align: center;
  transition: all .2s var(--ease);
}
.drop:hover, .drop.over { border-color: var(--accent); background: var(--accent-soft); }
.drop.over { transform: scale(1.004); }
.drop .big { display: block; font-family: var(--font-display); font-size: 18px; color: var(--ink); font-weight: 700; }
.drop .small {
  display: block; margin-top: 8px; font-size: 12px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--text-mute); font-weight: 600;
}
.filed {
  display: flex; align-items: center; gap: 12px; padding: 14px 16px;
  border: 1px solid var(--border); border-left: 3px solid var(--teal);
  border-radius: var(--radius-sm); background: var(--panel-2);
  animation: rise .26s var(--ease) both;
}
.filed .name { font-family: var(--font-display); font-weight: 700; color: var(--ink); }
.filed .facts { font-size: 13px; color: var(--text-mute); font-weight: 600; }
.filed .replace {
  margin-left: auto; background: none; border: 1px solid var(--border-strong);
  border-radius: 8px; padding: 5px 12px; font-size: 12.5px; font-weight: 700;
  color: var(--text-dim); transition: all .18s var(--ease);
}
.filed .replace:hover { border-color: var(--terracotta); color: var(--terracotta); }
.detected { margin: 12px 0 0; font-size: 13.5px; color: var(--text-dim); animation: rise .3s var(--ease) both; }
.detected strong { color: var(--ink); }
@keyframes rise { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }

/* ------------------------------------------------- intent + options */
/* Auto-fit rather than a fixed pair: on a wide shell the choices add a
   column instead of each one stretching into a banner. */
.intents { display: grid; grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); gap: 12px; }
@media (max-width: 46rem) { .intents { grid-template-columns: 1fr; } }
.intent {
  text-align: left; padding: 18px 20px; border-radius: var(--radius-sm);
  background: var(--panel); border: 2px solid var(--border);
  transition: all .2s var(--ease);
}
.intent:hover { border-color: var(--accent); transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.intent[aria-expanded="true"] { border-color: var(--accent); background: var(--accent-soft); }
.intent .t { display: block; font-family: var(--font-display); font-size: 17px; font-weight: 700; color: var(--ink); }
.intent .d { display: block; font-size: 13px; color: var(--text-dim); margin-top: 4px; }

.options { margin-top: 16px; animation: rise .24s var(--ease) both; }
.opt-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); gap: 8px; }
.opt {
  display: flex; align-items: center; gap: 9px; text-align: left;
  padding: 11px 13px; border-radius: 9px; background: var(--panel-2);
  border: 1px solid var(--border); font-size: 14px; font-weight: 700; color: var(--text-dim);
  transition: all .16s var(--ease);
}
.opt:hover:not(:disabled) { background: var(--panel); border-color: var(--accent); color: var(--ink); transform: translateX(2px); }
.opt[aria-pressed="true"] { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.opt:disabled { opacity: .42; cursor: not-allowed; }
.opt .box {
  width: 14px; height: 14px; border-radius: 4px; flex: none;
  border: 1.5px solid var(--border-strong); background: var(--panel);
  display: grid; place-items: center; transition: all .16s var(--ease);
}
.opt[aria-pressed="true"] .box { background: var(--accent); border-color: var(--accent); }
.opt[aria-pressed="true"] .box::after { content: "✓"; color: #fff; font-size: 10px; }
.opt .star { margin-left: auto; color: var(--ochre); font-size: 11px; }
.opt-note { margin: 12px 0 0; font-size: 13px; color: var(--text-mute); }
.opt-note b { color: var(--ink); }
.opt-note.warn { color: var(--terracotta); }

/* plan */
.plan { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border); }
.plan h4 { font-family: var(--font-body); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--text-mute); margin: 0 0 8px; font-weight: 700; }
.plan ul { list-style: none; margin: 0; padding: 0; columns: 2; column-gap: 26px; }
@media (max-width: 44rem) { .plan ul { columns: 1; } }
.plan li { display: flex; gap: 8px; padding: 3px 0; font-size: 13.5px; color: var(--text-dim); break-inside: avoid; animation: rise .22s var(--ease) both; }
.plan li::before { content: "✓"; color: var(--teal); font-weight: 700; }
.plan li.shared::before { content: "◆"; color: var(--accent); font-size: 10px; }
.plan .count { font-size: 12.5px; color: var(--text-mute); margin: 10px 0 0; font-weight: 600; }

/* ------------------------------------------------------- execution */
.modes { display: grid; grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); gap: 12px; }
@media (max-width: 46rem) { .modes { grid-template-columns: 1fr; } }
.mode {
  text-align: left; padding: 16px 18px; border-radius: var(--radius-sm);
  background: var(--panel); border: 2px solid var(--border); transition: all .2s var(--ease);
}
.mode:hover { border-color: var(--accent); }
/* THE QUESTION ABOVE THE TWO ANSWERS. How the AI is paid for is not the same
   question as which model reads the paper, and until now the two shared one
   list -- which is how a reseller came to sit among the models as though it
   were a third kind of intelligence. */
.modes-ask { grid-column: 1 / -1; margin: 0 0 2px; font-size: .95rem;
             letter-spacing: .01em; }
/* Offered, visibly not ready, and honest about it. A disabled card that looks
   exactly like the working one beside it is the trap; this one is dimmed, is
   not focusable, and carries the reason in its own words. */
.mode-unavailable { opacity: .58; cursor: not-allowed; }
.mode-unavailable:hover { border-color: var(--border); }
.mode[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-soft); }
.mode .t { font-family: var(--font-display); font-size: 15.5px; font-weight: 700; color: var(--ink); }
.mode .flag { float: right; font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--ochre); font-weight: 700; }
.mode .d { display: block; font-size: 13px; color: var(--text-dim); margin-top: 5px; }

.engines { display: grid; grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); gap: 10px; margin-top: 14px; }
@media (max-width: 46rem) { .engines { grid-template-columns: 1fr; } }
.engine {
  text-align: left; padding: 14px 16px; border-radius: var(--radius-sm);
  background: var(--panel-2); border: 1px solid var(--border); transition: all .18s var(--ease);
}
.engine:hover:not(.disabled) { border-color: var(--accent); }
.engine[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-soft); }
.engine .name { font-family: var(--font-display); font-weight: 700; color: var(--ink); }
.engine .state { display: flex; align-items: center; gap: 6px; font-size: 12.5px; margin-top: 5px; font-weight: 600; }
.engine .state .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
/* The connection states a person can act on. These names match what the
   companion reports, so a state can never render without its colour. */
.state[data-s="CONNECTED"] { color: var(--teal); }
.state[data-s="CONNECTED"] .dot { background: var(--teal); }
.state[data-s="NOT_CONNECTED"] { color: var(--text-mute); }
.state[data-s="NOT_CONNECTED"] .dot { background: var(--text-mute); }
.state[data-s="CONNECTING"], .state[data-s="WAITING_FOR_PROVIDER"] { color: var(--ochre); }
.state[data-s="CONNECTING"] .dot,
.state[data-s="WAITING_FOR_PROVIDER"] .dot { background: var(--ochre); animation: pulse 1.5s ease-in-out infinite; }
.state[data-s="SESSION_EXPIRED"] { color: var(--plum); }
.state[data-s="SESSION_EXPIRED"] .dot { background: var(--plum); }
.state[data-s="USAGE_LIMIT_REACHED"], .state[data-s="ERROR"] { color: var(--terracotta); }
.state[data-s="USAGE_LIMIT_REACHED"] .dot,
.state[data-s="ERROR"] .dot { background: var(--terracotta); }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) {
  .state .dot { animation: none; }
}
.engine .connect {
  margin-top: 9px; background: var(--accent); border: 0; color: #fff;
  border-radius: 8px; padding: 6px 14px; font-size: 12.5px; font-weight: 700;
}
.engine .connect:hover { background: var(--accent-deep); }
.engine .hint { font-size: 12px; color: var(--text-mute); margin-top: 7px; }

/* Providers as rows: the selected one expands to the right with its models.
   Anthropic shows Opus 5, Fable 5 and Sonnet 5 to choose between; OpenAI
   show their single model as a label. */
/* Providers side by side, one column each. */
#api-providers { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.api-row { display: block; margin: 0; }
.api-row .api-provider { width: 100%; height: 100%; margin-top: 0; }
.api-row-models { flex: 1 1 20rem; display: flex; gap: 8px; align-items: stretch; min-width: 0; }
.api-row-models .model-opt { flex: 1 1 0; align-items: flex-start; text-align: left; }
.api-row-models .model-opt:disabled {
  opacity: 1; cursor: default;               /* a single model is shown, not disabled */
  background: var(--panel-2); border-color: var(--border);
}
.model-text { display: flex; flex-direction: column; gap: 2px; }
.model-text strong { font-family: var(--font-display); color: var(--ink); }
.model-note { font-size: 12px; font-weight: 500; color: var(--text-mute); white-space: normal; }
@media (max-width: 46rem) {
  #api-providers { grid-template-columns: 1fr; }
}

/* --- Model chooser: provider's models as a three-column grid ---------------- */
.model-select { margin: 18px 0 4px; }
.model-head { font-family: var(--font-display); font-size: 13px; letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-dim); margin-bottom: 10px; }
.model-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.model-card { display: flex; flex-direction: column; gap: 6px; padding: 14px 15px;
  border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--panel);
  cursor: pointer; transition: border-color .12s, background .12s; position: relative; }
.model-card:hover:not(.locked) { border-color: var(--border-strong); }
.model-card.chosen { border-color: var(--accent); background: var(--accent-soft); }
.model-card.locked { cursor: not-allowed; opacity: .58; background: var(--panel-2); }
.model-card-top { display: flex; align-items: center; gap: 10px; }
.model-card .box { flex: 0 0 auto; width: 16px; height: 16px;
  border: 1.5px solid var(--border-strong); border-radius: 50%; position: relative; }
.model-card.chosen .box { border-color: var(--accent); }
.model-card.chosen .box::after { content: ""; position: absolute; inset: 3px;
  border-radius: 50%; background: var(--accent); }
.model-card-name { font-family: var(--font-display); font-size: 16px; color: var(--ink); }
.model-card-tier { font-size: 11px; font-weight: 600; color: var(--text-dim);
  text-transform: uppercase; letter-spacing: .04em; }
.model-card-min { font-size: 13px; color: var(--text); }
.model-card .viability { align-self: flex-start; font-size: 11.5px; font-weight: 700;
  letter-spacing: .01em; padding: 3px 9px; border-radius: 999px; margin-top: 2px; }
.model-card .viability[data-ok="true"] { color: var(--teal); background: rgba(21, 122, 110, .12); }
.model-card .viability[data-ok="false"] { color: var(--fatal); background: rgba(155, 44, 26, .10); }

.model-block { margin-top: 10px; padding: 13px 15px; border-radius: var(--radius-sm);
  border: 1px solid var(--major); background: rgba(200, 90, 52, .07); }
.model-block-msg { margin: 0 0 10px; font-size: 13px; color: var(--ink); line-height: 1.5; }
.model-block-actions { display: flex; gap: 9px; flex-wrap: wrap; }
@media (max-width: 46rem) { .model-grid { grid-template-columns: 1fr; } }

/* The spend limit for an audit, on the visitor's own API account. */
.budgetfield { margin-top: 14px; }
.budgetfield label { display: block; font-size: 12.5px; font-weight: 700; color: var(--text-dim); margin-bottom: 6px; }
.budget-input {
  display: flex; align-items: center; max-width: 11rem;
  border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
  background: var(--panel); transition: border-color .18s var(--ease);
}
.budget-input:focus-within { border-color: var(--accent); }
.budget-currency { padding: 0 2px 0 12px; color: var(--text-mute); font-weight: 700; }
.budget-input input {
  border: 0; background: none; padding: 10px 12px 10px 3px; width: 100%;
  font-family: var(--font-mono); font-size: 14px; color: var(--ink);
}
.budget-input input:focus { outline: none; }

.keyfield { margin-top: 14px; }
.keyfield label { display: block; font-size: 12.5px; font-weight: 700; color: var(--text-dim); margin-bottom: 6px; }
.keyfield input {
  width: 100%; padding: 10px 12px; border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong); background: var(--panel);
  font-family: var(--font-mono); font-size: 13px;
}
.keyfield .note { font-size: 12px; color: var(--text-mute); margin: 7px 0 0; }

/* trust */
.trust { margin-top: 16px; padding: 14px 16px; border-radius: var(--radius-sm);
  background: var(--panel-2); border: 1px solid var(--border); border-left: 3px solid var(--teal); }
.trust ul { list-style: none; margin: 0; padding: 0; }
.trust li { display: flex; gap: 8px; font-size: 13px; color: var(--text-dim); padding: 2px 0; }
.trust li::before { content: "✓"; color: var(--teal); font-weight: 700; }
.trust .caveat { margin: 10px 0 0; padding-top: 9px; border-top: 1px solid var(--border); font-size: 12.5px; color: var(--text-mute); }

/* run */
.consent { display: flex; gap: 10px; align-items: flex-start; font-size: 13.5px; color: var(--text-dim); margin-bottom: 14px; }
.consent input { margin-top: 3px; accent-color: var(--accent); }
.run {
  width: 100%; border: 0; border-radius: var(--radius-sm); background: var(--accent);
  color: #fff; font-family: var(--font-display); font-size: 16px; font-weight: 700;
  letter-spacing: .06em; padding: 16px; transition: all .18s var(--ease);
}
.run:hover:not(:disabled) { background: var(--accent-deep); }
.run:active:not(:disabled) { transform: translateY(1px); }
.run:disabled { background: var(--border-strong); color: var(--panel); cursor: not-allowed; }
.footnote { font-size: 12.5px; color: var(--text-mute); margin: 11px 0 0; text-align: center; }

/* --------------------------------------------------------- progress */
.stages { list-style: none; margin: 0; padding: 0; }
.stages li {
  display: flex; align-items: center; gap: 12px; padding: 8px 0;
  border-bottom: 1px solid var(--border); color: var(--text-mute); font-size: 14.5px; font-weight: 600;
}
.stages li:last-child { border-bottom: 0; }
.stages li[data-state="done"], .stages li[data-state="warned"] { color: var(--ink); }
.stages li[data-state="running"] { color: var(--accent); }
.stages li[data-state="skipped"] { font-style: italic; }
.stages .glyph { width: 16px; flex: none; text-align: center; }
.stages li[data-state="done"] .glyph, .stages li[data-state="warned"] .glyph { color: var(--teal); }
.stages li[data-state="failed"] .glyph { color: var(--fatal); }
.stages .spin { display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: var(--accent); animation: pulse 1.5s ease-in-out infinite; }
.stages .reason { margin-left: auto; font-size: 12.5px; color: var(--text-mute); font-weight: 500; }
@keyframes pulse { 0%,100% { opacity: .25; } 50% { opacity: 1; } }

.tally { display: flex; flex-wrap: wrap; gap: 22px; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); font-size: 13px; color: var(--text-dim); font-weight: 600; }
.tally b { font-family: var(--font-display); color: var(--ink); font-size: 15px; }
/* EMPTY MEANS GONE, not a 96px box with a bar down its side. The height and the
   rule were there so the teleprompter would not jump as lines arrive, and they
   are still right once it HAS lines -- but before the first observation they
   drew an empty panel under the waiting strip that read as something broken.
   The strip holds that space while there is nothing to say. */
.activity { font-size: 14px; color: var(--text); }
.activity.marquee { margin-top: 12px; padding-left: 13px;
  border-left: 2px solid var(--accent); min-height: 96px; }
/* The teleprompter: newest line at the bottom, older ones fading above it.
   Only sanitised phrases reach here, never a command or a line of the brief. */
.activity.marquee { display: flex; flex-direction: column; justify-content: flex-end; gap: 3px; overflow: hidden; }
.marquee-line { line-height: 1.4; transition: opacity 700ms ease; }
.marquee-line.fresh { animation: marquee-rise 620ms cubic-bezier(.22, .61, .36, 1); }
@keyframes marquee-rise {
  from { opacity: 0; transform: translateY(9px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .marquee-line.fresh { animation: none; }
  .marquee-line { transition: none; }
}

/* findings */
.finding { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 13px 15px; margin-bottom: 9px; background: var(--panel); animation: rise .28s var(--ease) both; }
.finding.fatal { border-left: 3px solid var(--fatal); }
.finding.major { border-left: 3px solid var(--major); }
.finding.moderate { border-left: 3px solid var(--moderate); }
.finding.minor { border-left: 3px solid var(--minor); }
.finding .head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.finding .sev { font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; font-weight: 700; }
.finding.fatal .sev { color: var(--fatal); } .finding.major .sev { color: var(--major); }
.finding.moderate .sev { color: var(--moderate); } .finding.minor .sev { color: var(--minor); }
.finding .id { font-family: var(--font-mono); font-size: 12px; color: var(--text-mute); }
.finding .verify { margin-left: auto; font-size: 11px; padding: 2px 9px; border-radius: 20px; border: 1px solid var(--border-strong); color: var(--text-dim); font-weight: 700; }
.finding .verify[data-v="verified"] { color: var(--teal); border-color: var(--teal); }
.finding .verify[data-v="preliminary"] { color: var(--ochre); border-color: var(--ochre); }
.finding .verify[data-v="reopened"] { color: var(--terracotta); border-color: var(--terracotta); }
.finding p { margin: 7px 0 0; font-size: 14px; }
.finding .where { font-size: 12.5px; color: var(--text-mute); margin-top: 6px; }

/* ---------------------------------------------------------- result */
.verdict-card { border-left: 3px solid var(--accent); }
.verdict { font-family: var(--font-display); font-size: 22px; font-weight: 700; margin: 0 0 5px; }
.verdict.ready { color: var(--teal); } .verdict.blocked { color: var(--fatal); }
.verdict-sub { color: var(--text-dim); margin: 0; font-size: 14px; }
.severity { display: flex; gap: 28px; margin-top: 18px; flex-wrap: wrap; }
.severity .n { font-family: var(--font-display); font-size: 30px; font-weight: 800; line-height: 1; }
.severity .n.fatal { color: var(--fatal); } .severity .n.major { color: var(--major); }
.severity .n.moderate { color: var(--moderate); } .severity .n.minor { color: var(--minor); }
.severity .n.verified { color: var(--teal); }
.severity .k { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--text-mute); margin-top: 5px; font-weight: 700; }

.plan-item { border: 1px solid var(--border); border-radius: var(--radius-sm); margin-bottom: 8px; background: var(--panel); }
.plan-item > summary { padding: 12px 15px; display: flex; align-items: center; gap: 10px; list-style: none; font-weight: 600; }
.plan-item > summary::-webkit-details-marker { display: none; }
.plan-item > summary::before { content: "▸"; color: var(--text-mute); }
.plan-item[open] > summary::before { content: "▾"; }
.plan-item .id { font-family: var(--font-mono); font-size: 12.5px; color: var(--ink); }
.plan-item .locs { font-size: 13px; color: var(--text-dim); }
.plan-item .ready { margin-left: auto; font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--teal); font-weight: 700; }
.plan-item .ready.not { color: var(--ochre); }
.plan-item .body { padding: 0 15px 14px; border-top: 1px solid var(--border); }
.plan-item dt { font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--text-mute); margin-top: 12px; font-weight: 700; }
.plan-item dd { margin: 4px 0 0; font-size: 14px; }
.plan-item ul { margin: 4px 0 0; padding-left: 18px; font-size: 14px; }

.cite { display: flex; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--border); font-size: 13.5px; }
.cite:last-child { border-bottom: 0; }
.cite .tag { flex: none; font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; padding: 2px 9px; border-radius: 20px; height: fit-content; font-weight: 700; }
.cite .tag.verified { color: var(--teal); border: 1px solid var(--teal); }
.cite .tag.not { color: var(--terracotta); border: 1px solid var(--terracotta); }
.cite .when { margin-left: auto; font-size: 12px; color: var(--text-mute); flex: none; }

.journals { list-style: none; margin: 0; padding: 0; counter-reset: j; }
.journals li { display: flex; gap: 11px; padding: 9px 0; border-bottom: 1px solid var(--border); font-size: 14px; }
.journals li:last-child { border-bottom: 0; }
.journals li::before { counter-increment: j; content: counter(j) "."; color: var(--text-mute); font-family: var(--font-mono); }
.journals .fit { margin-left: auto; font-size: 12px; color: var(--text-mute); font-weight: 700; }

.revision { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.revision th, .revision td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); }
.revision th { font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--text-mute); font-weight: 700; }
.revision .was { color: var(--text-mute); }
.revision .now.closed { color: var(--teal); font-weight: 700; }
.revision .now.open { color: var(--terracotta); font-weight: 700; }
.revision .now.reopened { color: var(--fatal); font-weight: 700; }
.revision .miss { font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--fatal); border: 1px solid var(--fatal); border-radius: 20px; padding: 1px 8px; font-weight: 700; }

.actions { display: flex; gap: 11px; margin-top: 18px; flex-wrap: wrap; }
.primary, .secondary {
  border-radius: var(--radius-sm); font-family: var(--font-body); font-size: 14px;
  font-weight: 700; padding: 12px 22px; display: inline-block; border: 1px solid var(--accent);
  transition: all .18s var(--ease);
}
.primary { background: var(--accent); color: #fff; }
.primary:hover { background: var(--accent-deep); border-color: var(--accent-deep); }
.secondary { background: none; color: var(--accent); }
.secondary:hover { background: var(--accent-soft); }

.error {
  border: 1px solid var(--terracotta); border-left-width: 3px; border-radius: var(--radius-sm);
  background: #fbeee9; padding: 13px 15px; margin-top: 13px; font-size: 13.5px; color: #7a2c14;
}
.error .fix { margin-top: 9px; display: flex; gap: 8px; flex-wrap: wrap; }
/* The exact reason, under the sentence a person acts on. Present for
   anyone who wants it, quiet enough not to be the message. */
.error .technical {
  margin-top: 9px; font-size: 0.82rem; line-height: 1.45;
  color: var(--text-mute); word-break: break-word;
}
.error .fix button {
  background: none; border: 1px solid var(--terracotta); color: #7a2c14;
  border-radius: 8px; padding: 6px 13px; font-size: 12.5px; font-weight: 700;
}
/* The link to what the model produced on a failed run. Set apart from the
   sentence a person acts on, quiet but reachable. */
.diag-link {
  display: inline-block; margin-top: 11px; padding: 6px 13px;
  border: 1px solid var(--terracotta); border-radius: 8px;
  font-size: 12.5px; font-weight: 700; color: #7a2c14;
  transition: background-color .18s var(--ease);
}
.diag-link:hover { background: var(--accent-soft); }

/* --------------------------------------------------------- site footer
   The work sits high on a tall screen, and an unclosed page below it reads as
   unfinished rather than as calm. The footer anchors the column and gives the
   one promise the whole product turns on a fixed place to live: the manuscript
   does not leave this machine except to the provider the reader picked. */
.sitefoot { margin-top: 46px; border-top: 1px solid var(--border); }
.sitefoot-band { height: 3px; background: var(--tejido); opacity: .8;
  animation: tejido-drift 24s linear infinite; }
.sitefoot-in {
  max-width: 78rem; margin: 0 auto; padding: 20px clamp(24px, 4vw, 48px) 30px;
  display: flex; align-items: center; gap: 15px;
}
.sitefoot-mark {
  width: 26px; height: 26px; flex: none; border-radius: 8px; display: grid;
  place-items: center; font-family: var(--font-display); font-weight: 800;
  font-size: 14px; color: var(--terracotta);
  background: linear-gradient(160deg, var(--panel), var(--bg-2));
  border: 1px solid var(--border-strong);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.9);
}
.sitefoot-line { flex: 1; margin: 0; max-width: 52rem; font-size: 12.5px;
  color: var(--text-mute); line-height: 1.5; }
.sitefoot-link {
  flex: none; font-size: 12.5px; font-weight: 700; color: var(--text-dim);
  padding: 7px 14px; border-radius: 20px; border: 1px solid var(--border);
  transition: color .2s var(--ease), border-color .2s var(--ease);
}
.sitefoot-link:hover { color: var(--accent); border-color: var(--accent); }
@media (max-width: 46rem) {
  .sitefoot-in { flex-wrap: wrap; gap: 12px; }
  .sitefoot-link { order: 3; }
}
@media (prefers-reduced-motion: reduce) { .sitefoot-band { animation: none; } }

/* Content that belongs only to the local companion: the subscription path
   through a signed-in provider CLI does not exist on the hosted site, which
   offers API keys only, so the hosted page hides it rather than describing a
   mode a visitor there can never reach. */
:root[data-mode="hosted"] .local-only { display: none; }

/* What the audit cost to run, under the severity tally: token count, dollar
   figure and elapsed time, set quietly so it reads as a receipt, not a result. */
.run-meta {
  margin: 16px 0 0; padding-top: 12px; border-top: 1px solid var(--border);
  font-size: 12.5px; color: var(--text-mute); font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* FAQ page */
.faq h2 { font-size: 20px; color: var(--ink); margin: 30px 0 8px; }
.faq h3 { font-size: 15.5px; color: var(--ink); margin: 20px 0 4px; }
.faq p, .faq li { font-size: 14.5px; color: var(--text); max-width: 42rem; }
.faq .term { font-family: var(--font-display); font-weight: 700; color: var(--accent); }

/* -------------------------------------------------------- small screens */
@media (max-width: 46rem) {
  .topbar { height: auto; padding: 10px 16px; flex-wrap: wrap; gap: 10px; }
  .topbar-state { display: none; }          /* the location line needs the room */
  .brand-text small { display: none; }
  main { padding: 22px 16px 60px; }
  .hero h1 { font-size: 24px; }
  .hero p { font-size: 14px; }
  .card { padding: 18px 16px; border-radius: 12px; }
  .severity { gap: 18px; }
  .severity .n { font-size: 24px; }
  .opt-grid { grid-template-columns: 1fr; }
  .actions { flex-direction: column; }
  .actions .primary, .actions .secondary { width: 100%; text-align: center; }
  /* The run control follows you down a long form. */
  #card-run:not(.locked) { position: sticky; bottom: 0; z-index: 5; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* ---------------------------------------------- progress ring
   Filled by stages completed, not by time. The clock in the middle is the real
   elapsed time, shown as a duration instead of a bare count of seconds. */
.run-head { display: flex; gap: 22px; align-items: center; flex-wrap: wrap; }
.run-head .stages { flex: 1 1 260px; margin: 0; }
.ring { position: relative; width: 116px; height: 116px; flex: 0 0 auto; }
.ring-svg { display: block; }
.ring-track { stroke: var(--border); }
.ring-arc {
  stroke: var(--accent);
  transition: stroke-dashoffset 600ms cubic-bezier(.22, .61, .36, 1);
}
.ring-arc.failed { stroke: var(--text-mute); }
.ring-centre {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2px; text-align: center;
}
.ring-centre b {
  font-family: var(--font-display); font-size: 1.35rem; line-height: 1;
  color: var(--text); font-variant-numeric: tabular-nums;
}
.ring-centre span { font-size: 0.68rem; color: var(--text-mute); }
/* A slow sweep, so a long quiet stage still looks alive without implying
   progress that has not happened. */
.ring.spinning .ring-svg { animation: ring-breathe 2.8s ease-in-out infinite; }
@keyframes ring-breathe { 0%, 100% { opacity: 1; } 50% { opacity: .72; } }
@media (prefers-reduced-motion: reduce) {
  .ring.spinning .ring-svg { animation: none; }
  .ring-arc { transition: none; }
}

/* ------------------------------------------------- press
   A button that does not move under the finger feels like a picture of a
   button. Depth on rest, lift on hover, and a real compression on press. */
.run, .primary, .secondary, .engine .connect, .mode, .modal-actions button {
  transition: transform .12s var(--ease), box-shadow .18s var(--ease),
              background-color .18s var(--ease), border-color .18s var(--ease);
}
.run, .primary, .engine .connect, .modal-actions .primary {
  box-shadow: 0 2px 0 var(--accent-deep), var(--shadow-sm);
}
.run:hover:not(:disabled), .primary:hover, .engine .connect:hover,
.modal-actions .primary:hover {
  transform: translateY(-1px);
  box-shadow: 0 3px 0 var(--accent-deep), var(--shadow);
}
.run:active:not(:disabled), .primary:active, .engine .connect:active,
.modal-actions .primary:active {
  transform: translateY(2px);
  box-shadow: 0 0 0 var(--accent-deep), inset 0 2px 6px rgba(80, 40, 20, .28);
}
.secondary, .mode, .modal-actions .ghost {
  box-shadow: 0 1px 0 var(--border-strong);
}
.secondary:hover, .mode:hover, .modal-actions .ghost:hover {
  transform: translateY(-1px);
  box-shadow: 0 2px 0 var(--border-strong), var(--shadow-sm);
}
.secondary:active, .mode:active, .modal-actions .ghost:active {
  transform: translateY(1px);
  box-shadow: inset 0 2px 5px rgba(80, 40, 20, .16);
}
.run:disabled { box-shadow: none; transform: none; }
/* Keyboard users get the same affordance, visibly. */
.run:focus-visible, .primary:focus-visible, .secondary:focus-visible,
.mode:focus-visible, .engine .connect:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .run, .primary, .secondary, .engine .connect, .mode { transition: none; }
  .run:hover, .primary:hover, .secondary:hover, .mode:hover { transform: none; }
}

/* The module name a reader recognises, ahead of the tracking identifier. */
.finding .module {
  font-size: 13px; font-weight: 700; color: var(--text);
  letter-spacing: .01em;
}
/* The raw identifier keeps its place, quietly, so the report and the screen
   can be matched to each other. */
.finding .id { opacity: .65; }

/* The finding number, between the severity and the raw identifier. */
.finding .num-tag {
  font-family: var(--font-mono); font-size: 12.5px; font-weight: 700;
  color: var(--text);
}

/* The running heading changes with the stage. It fades out and back rather than
   snapping, and the dots after it show the page is live without implying a
   percentage nobody can compute. */
#running-title { transition: opacity 220ms var(--ease); }
#running-title.fading { opacity: 0; }
#running-title::after {
  content: "";
  animation: thinking-dots 1.6s steps(4, end) infinite;
}
@keyframes thinking-dots {
  0%   { content: ""; }
  25%  { content: "."; }
  50%  { content: ".."; }
  75%  { content: "..."; }
  100% { content: ""; }
}
#result-title::after, #running-title.done::after { content: ""; animation: none; }
@media (prefers-reduced-motion: reduce) {
  #running-title { transition: none; }
  #running-title::after { animation: none; content: ""; }
}

/* The per-review extracts, under the combined report they came from. */
.more-reports {
  display: flex; flex-wrap: wrap; gap: 8px; width: 100%;
  margin-top: 4px; padding-top: 10px; border-top: 1px solid var(--border);
}
.more-reports .secondary { font-size: 13px; }

/* Whether this companion is holding a key, and what happened when you asked it
   to stop. A control whose only effect is invisible reads as a broken control:
   Forget key cleared an already-empty field, called the server, and changed
   nothing a person could see. */
.key-held {
  font-size: 13px; font-weight: 600; color: var(--text-mute);
  align-self: center; margin-left: 10px;
}
.key-held[data-state="held"] { color: var(--ochre); }
.key-held[data-state="gone"] { color: var(--teal); }
.key-held[data-state="error"] { color: var(--fatal); }

/* The remedy, set apart from the diagnosis. */
.next-step {
  margin: 10px 0 0; padding-top: 9px; font-weight: 600;
  border-top: 1px solid var(--border);
  color: var(--ink);
}

/* --- AI models page ------------------------------------------------------- */
.models-page .model-table { width: 100%; border-collapse: collapse; margin: 10px 0 20px;
  font-size: 14px; }
.models-page .model-table th { text-align: left; font-family: var(--font-display);
  font-size: 12px; letter-spacing: .03em; text-transform: uppercase; color: var(--text-dim);
  padding: 8px 12px; border-bottom: 1.5px solid var(--border-strong); }
.models-page .model-table td { padding: 10px 12px; border-bottom: 1px solid var(--border);
  color: var(--text); vertical-align: top; }
.models-page .model-table td.num { font-variant-numeric: tabular-nums; color: var(--ink); }
.models-page .model-table td.m-name { font-family: var(--font-display); color: var(--ink);
  font-weight: 600; white-space: nowrap; }
.models-page .model-table td.m-note { color: var(--text-mute); }
.models-page .chart-wrap { margin: 8px 0 6px; overflow-x: auto; }
.models-page .chart-wrap svg { width: 100%; height: auto; min-width: 480px;
  background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-sm); }
.models-page .chart-controls { display: flex; gap: 8px; flex-wrap: wrap; margin: 10px 0; }
.models-page .chart-tab { padding: 6px 13px; border: 1px solid var(--border-strong);
  border-radius: 999px; background: var(--panel-2); color: var(--text-dim);
  font-size: 13px; cursor: pointer; font-family: var(--font-body); }
.models-page .chart-tab.on { background: var(--accent-soft); border-color: var(--accent);
  color: var(--accent); font-weight: 600; }
.models-page .chart-caption { font-size: 13px; color: var(--text); margin: 6px 0 2px; }

/* --- "Thinking" indicator: thoughts rise from the mind while the model works -- */
.thinking { display: flex; align-items: center; gap: 16px; padding: 14px 4px 6px; }
.think-orb { position: relative; width: 34px; height: 34px; flex: 0 0 auto; }
/* The mind: a soft pulsing disc the thoughts rise from. */
.think-orb::after { content: ""; position: absolute; left: 7px; bottom: 0;
  width: 20px; height: 20px; border-radius: 50%;
  background: radial-gradient(circle at 35% 35%, var(--accent-soft), var(--accent));
  box-shadow: 0 0 0 0 rgba(200, 90, 52, .35);
  animation: think-pulse 2.6s var(--ease) infinite; }
/* Three thoughts float up and fade, staggered, so there is always motion. */
.think-orb span { position: absolute; bottom: 16px; width: 7px; height: 7px;
  border-radius: 50%; background: var(--accent); opacity: 0;
  animation: think-rise 2.6s ease-out infinite; }
.think-orb span:nth-child(1) { left: 6px;  animation-delay: 0s; }
.think-orb span:nth-child(2) { left: 14px; animation-delay: .55s; }
.think-orb span:nth-child(3) { left: 22px; animation-delay: 1.15s; }
@keyframes think-rise {
  0%   { transform: translateY(0) scale(.5);  opacity: 0; }
  25%  { opacity: .85; }
  60%  { opacity: .55; }
  100% { transform: translateY(-24px) scale(1); opacity: 0; }
}
@keyframes think-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(200, 90, 52, .30); }
  50%      { box-shadow: 0 0 0 7px rgba(200, 90, 52, 0); }
}
.think-label { font-size: 13.5px; color: var(--text-dim); line-height: 1.5; }
@media (prefers-reduced-motion: reduce) {
  .think-orb span, .think-orb::after { animation: none; }
  .think-orb span:nth-child(2) { opacity: .6; }
}

/* Upload warning: a readable-PDF gate. Shown when a scanned or thin PDF is
   uploaded, so the reader is told rather than handed an empty audit. */
.extraction-warning {
  margin: 0.75rem 0;
  padding: 0.75rem 1rem;
  border: 1px solid #d9a441;
  border-left-width: 4px;
  border-radius: 6px;
  background: #fbf3e2;
  color: #6b4e16;
  font-size: 0.92rem;
  line-height: 1.4;
}

/* ---------------------------------------------------------------------------
   The two reference pages. Everything below renders content that docs.js builds
   from rigor-docs.js, which tools/build_web.py emits from the engine -- so this
   file styles shapes (a definition list, a labelled list, a pill) and never
   names a module, a depth or a price.
   --------------------------------------------------------------------------- */

.depth-block {
  border-top: 1px solid var(--border);
  padding-top: 14px;
  margin-top: 16px;
}
.depth-block:first-of-type { border-top: 0; padding-top: 0; margin-top: 8px; }
.depth-block h3 { margin: 0 0 4px; }

/* Obligation lists: the term on the left, what it commits to on the right, so
   the two depths can be compared down the page rather than across a table. */
.spec-list {
  margin: 10px 0 0;
  display: grid;
  grid-template-columns: minmax(8rem, 12rem) 1fr;
  gap: 5px 18px;
  max-width: 46rem;
}
.spec-list dt {
  font-size: .82rem; letter-spacing: .02em;
  color: var(--text-dim);
}
.spec-list dd { margin: 0; color: var(--text); }

.focus-list, .model-list {
  list-style: none; padding: 0;
  margin: 10px 0 0;
  max-width: 46rem;
}
.focus-list li, .model-list li {
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
}
.focus-list li:last-child, .model-list li:last-child { border-bottom: 0; }
.focus-blurb, .model-meta { color: var(--text-dim); }

.pill {
  display: inline-block;
  margin-left: 8px;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: .68rem; text-transform: uppercase; letter-spacing: .06em;
  background: var(--accent-soft); color: var(--accent-deep);
  vertical-align: middle;
}
/* Reserved for a claim the product has NOT verified end to end. It has to read
   as a caution rather than as a badge, or it becomes decoration. */
.pill.warn { background: #f6e7c9; color: #8a5a12; }

.tier-ladder {
  font-variant-numeric: tabular-nums;
  font-size: 1.05rem;
  color: var(--text);
  margin: 10px 0 6px;
}

/* One completed run, presented as evidence rather than as a brochure. The card
   carries facts read out of that run's own record; nothing here is styled to
   look like a price or a promise. */
.sample h3 {
  margin: 16px 0 2px;
  font-size: .95rem;
  color: var(--text-dim);
  letter-spacing: .01em;
}
.sample .spec-list { margin-top: 12px; }
.sample .spec-list dd { font-variant-numeric: tabular-nums; }

/* Two columns: manuscripts written for testing | published papers. */
.sample-intro p { margin: 0; font-size: .92rem; }
.sample-board {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  align-items: start;
}
.sample-col .sample-group { margin: 0 0 4px; font-size: 1.15rem; }
.sample-col .sample-group-note { margin: 0 0 12px; font-size: .86rem; }
.sample-col .card.sample { margin: 0 0 14px; padding: 16px 18px; }
.sample-title { margin: 0 0 6px; font-size: 1rem; }
.sample-paper { margin: 0 0 10px; font-size: .9rem; }
.sample .sample-download { margin: 0 0 12px; }
.sample .spec-list { margin-top: 0; font-size: .88rem; }
.sample-more { margin-top: 10px; font-size: .86rem; }
.sample-more summary { cursor: pointer; color: var(--text-dim); }
@media (max-width: 820px) {
  .sample-board { grid-template-columns: 1fr; }
}

.sample-download {
  display: inline-block;
  margin-top: 16px;
  padding: 9px 16px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--panel-2);
  color: var(--ink);
  font-weight: 700;
  font-size: .92rem;
  text-decoration: none;
  transition: border-color .18s var(--ease), background .18s var(--ease);
}
.sample-download:hover {
  border-color: var(--accent);
  background: var(--accent-soft);
}

/* Which product this sample is, under the heading that says what it bought. */
.eyebrow-depth {
  margin: -2px 0 10px;
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--text-mute);
  font-weight: 700;
}

/* ---- the waiting strip ---------------------------------------------------

   A figure that runs across while the model reads, stops in the middle to say
   hello, and carries on. It sits under the thinking dots.

   WHY IT IS HERE. A long reasoning turn sends nothing back for minutes. The
   screen stops moving, and a screen that stops moving reads as a screen that
   has died. The thinking dots already say work is happening; this says it
   warmly, and the stop in the middle is where the sentence lands so it is read
   rather than glimpsed.

   IT IS NOT A PROGRESS BAR. Nothing here is derived from how far the audit has
   got, because inventing that would be the decorative lie this product spends
   its time removing. The one thing it tracks is real: when the model has gone
   quiet the figure runs, which is the same fact the status line states in
   words. */

.runner-strip {
  position: relative;
  /* TALL ENOUGH FOR TWO LINES ABOVE HIS HEAD. At 104px a two-line greeting
     pressed against the top edge, and a bubble clipped by its own container is
     the kind of detail that makes a considered thing look accidental. */
  height: 148px;
  margin-top: 14px;
  border-radius: 10px;
  background: linear-gradient(180deg, var(--panel) 0%, var(--panel-2) 100%);
  border: 1px solid var(--border);
  overflow: hidden;
}
.runner-strip::after {                       /* the ground he runs on */
  content: "";
  position: absolute; left: 0; right: 0; bottom: 18px;
  height: 1px; background: var(--border-strong); opacity: 0.6;
}

.runner {
  position: absolute;
  bottom: 19px; left: 0;
  width: 34px; height: 42px;
  /* POSITION IS SET FROM SCRIPT, not from a keyframe. The first version ran the
     crossing as a CSS animation and the stand from timers, and the two never
     agreed: he crossed without ever stopping, so the greeting never showed and
     the legs never held. One clock cannot disagree with itself. */
  transform: translateX(-50px);
  will-change: transform;
}

.runner .figure { position: absolute; inset: 0; }
.runner .figure i { position: absolute; display: block; border-radius: 2px; }
.runner .cap  { left: 4px;  top: -2px; width: 25px; height: 6px;  background: var(--plum); }
.runner .head { left: 9px;  top: 4px;  width: 16px; height: 12px; background: var(--terracotta); }
.runner .body { left: 8px;  top: 17px; width: 19px; height: 15px; background: var(--teal); }
.runner .arm  { left: 24px; top: 20px; width: 9px;  height: 6px;  background: var(--teal); }
.runner .leg  { top: 32px; width: 6px; height: 10px; background: var(--ink); }
.runner .leg-a { left: 9px;  animation: runner-step 0.5s steps(2) infinite; }
.runner .leg-b { left: 19px; animation: runner-step 0.5s steps(2) infinite reverse; }

@keyframes runner-step {
  0%   { height: 10px; transform: translateX(0); }
  100% { height: 5px;  transform: translateX(-4px); }
}

/* HE ACTUALLY STOPS. The legs keep their own fast clock so the stride reads at
   all, and legs still moving under a standing figure is the one detail that
   makes an animation look broken rather than deliberate. */
.runner.is-standing .leg-a,
.runner.is-standing .leg-b { animation-play-state: paused; }
.runner.is-standing .arm { top: 21px; }

.runner .greet {
  position: absolute;
  bottom: 52px; left: 26px;
  /* Two lines, wrapped rather than broken by hand: the message changes
     between waiting and finished, and a hard break sized for one of them is
     wrong for the other. */
  white-space: normal;
  width: 190px;
  text-align: center;
  line-height: 1.35;
  padding: 7px 11px;
  border-radius: 10px;
  background: var(--panel);
  border: 1px solid var(--border-strong);
  color: var(--text);
  font-size: 15px; font-weight: 500;
  opacity: 0; pointer-events: none;
  transition: opacity 0.4s ease;
  box-shadow: 0 2px 6px rgba(44, 35, 24, 0.10);
}
.runner .greet::after {
  content: "";
  position: absolute; bottom: -5px; left: 10px;
  width: 8px; height: 8px;
  background: var(--panel);
  border-right: 1px solid var(--border-strong);
  border-bottom: 1px solid var(--border-strong);
  transform: rotate(45deg);
}
.runner.is-standing .greet { opacity: 1; }

.runner-strip.is-quiet .leg-a,
.runner-strip.is-quiet .leg-b { animation-duration: 0.3s; }

/* Anyone who asked their system for less motion gets the greeting, standing
   still. Nothing is lost but the movement. */
@media (prefers-reduced-motion: reduce) {
  .runner .leg-a, .runner .leg-b { animation: none; }
  .runner .greet { opacity: 1; }
}

/* A failed deliverable, said at the size of the problem.
   This was a `.footnote`: an audit that finished and could not produce its
   document announced that in grey six-point type under the download buttons,
   while the heading above still read "Audit complete". */
.build-failed {
  margin-top: 14px;
  padding: 14px 16px;
  border-radius: 10px;
  border: 1px solid var(--terracotta);
  background: var(--panel-2);
}
.build-failed strong { display: block; margin-bottom: 6px; color: var(--terracotta); }
.build-failed p { margin: 0 0 6px; font-size: 14px; }
.build-failed .footnote { margin: 0; }

/* Run / Scientific outcome / Report, as three separate facts. */
.result-states { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
.state-chip {
  font-size: 12px; padding: 4px 10px; border-radius: 999px;
  border: 1px solid var(--border-strong); background: var(--panel-2);
  color: var(--text-dim);
}
.state-chip b { color: var(--text); font-weight: 600; }
