/* Vouchsafe caseworker workbench.

   The signature device is the withheld bar: wherever a record value would sit,
   there is a solid block instead, and the answer rides beside it. The product
   is about what does not move, so the interface has to show absence.

   Tokens are semantic rather than literal, because "ink" means two opposite
   colours across the two themes. --withheld inverts deliberately: a redaction
   block reads as a solid mass, and a black bar on a dark surface is invisible. */

:root {
  color-scheme: light;

  --bg: #e7e9ec;
  --surface: #fafbfc;
  --surface-2: #f2f4f6;
  --fg: #12141a;
  --fg-muted: #5d636e;
  --border: #c3c7ce;
  --border-soft: #dfe2e7;

  --invert-bg: #12141a;
  --invert-fg: #ffffff;
  --invert-fg-muted: rgba(255, 255, 255, .6);

  --withheld: #12141a;
  --withheld-label: rgba(255, 255, 255, .5);

  --attest: #1b5e3f;
  --attest-on-invert: #5fbf8c;
  --refuse: #8a2b2b;
  --refuse-on-invert: #e08585;
  --hold: #7f570c;
  --hold-on-invert: #d9a441;
  --hold-surface: #f7f1e4;

  --display: 'Archivo', system-ui, sans-serif;
  --mono: 'IBM Plex Mono', ui-monospace, Consolas, monospace;
}

:root[data-theme='dark'] {
  color-scheme: dark;

  --bg: #0c0e12;
  --surface: #15181e;
  --surface-2: #1b1f26;
  --fg: #e9ebef;
  --fg-muted: #98a0ac;
  --border: #2c323b;
  --border-soft: #23282f;

  --invert-bg: #1b1f26;
  --invert-fg: #e9ebef;
  --invert-fg-muted: rgba(233, 235, 239, .6);

  /* Inverted: the block still reads as a solid mass against a dark surface. */
  --withheld: #d7dbe2;
  --withheld-label: rgba(0, 0, 0, .55);

  --attest: #5fbf8c;
  --attest-on-invert: #5fbf8c;
  --refuse: #e08585;
  --refuse-on-invert: #e08585;
  --hold: #d9a441;
  --hold-on-invert: #d9a441;
  --hold-surface: #2a2313;
}

* { box-sizing: border-box; }

/* Nothing on this page is meant to be reachable by scrolling sideways. `clip`
   rather than `hidden` because it does not turn the root into a scroll
   container, so fixed and sticky positioning keep working. Load ?diag=1 to see
   anything this is masking rather than letting it hide a real layout bug. */
html { overflow-x: clip; }


/* An author `display` rule beats the UA sheet's [hidden] rule, so anything
   given a display value silently ignores the attribute. This keeps `hidden`
   meaning hidden. */
[hidden] { display: none !important; }

body {
  margin: 0;
  padding-bottom: 4rem;
  background: var(--bg);
  color: var(--fg);
  font: 400 15px/1.5 var(--display);
  -webkit-font-smoothing: antialiased;
}

svg { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

:where(a, button, [tabindex]):focus-visible {
  outline: 3px solid var(--attest);
  outline-offset: 2px;
}

/* ============ cover ============ */

/* The landing sits in normal document flow, above the workbench.
   An earlier version was a fixed overlay with the body scroll locked, which
   meant scrolling had nowhere to go: the gesture did nothing, and hiding the
   scrollbar only hid the symptom. Now there is somewhere to scroll to. */

.cover {
  min-height: 100vh;
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  background: var(--bg);
}

.cover-left {
  background: var(--invert-bg);
  color: var(--invert-fg);
  padding: 2.5rem 3rem 3rem;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.cover-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: auto;
}

.eyebrow {
  font: 500 11px var(--mono);
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--invert-fg-muted);
}

/* A status readout, not a control.
   This used to be a boxed `LIVE - 001` - a full 1px border with padding,
   parked in the top-right of a hero, which is exactly where a button lives.
   It read as clickable and did nothing, and the 001 meant nothing either.
   A dot plus a word is the settled convention for a liveness indicator and
   cannot be mistaken for something to press. The word is now bound to the
   federation, so it reports rather than decorates. */
.tag {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  cursor: default;
  user-select: none;
  font: 600 10px var(--mono);
  letter-spacing: .16em;
  /* Matches the eyebrow it shares a line with. The DOM text stays sentence
     case so the status is announced as a word, not shouted. */
  text-transform: uppercase;
  color: var(--invert-fg);
  white-space: nowrap;
}
.tag.light { color: var(--fg); }

.tag-dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  /* Colour carries the state on its own; the pulse below is decoration and
     stays behind the reduced-motion gate.

     Amber is the default because most not-live states are waiting, not
     failing: connecting, standby without the WebMCP flag, a partial
     federation. Red is reserved for the one real failure - WebMCP is on and
     no origin answered. */
  color: var(--hold-on-invert);
}
.tag.on .tag-dot { color: var(--attest-on-invert); }
.tag.off .tag-dot { color: var(--refuse-on-invert); }

@media (prefers-reduced-motion: no-preference) {
  .tag.on .tag-dot { animation: tag-pulse 2s ease-in-out infinite; }
}
@keyframes tag-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .35; }
}

.cover-title {
  margin: 3rem 0 0;
  font: 800 clamp(2.6rem, 6.2vw, 5.2rem)/0.95 var(--display);
  letter-spacing: -0.045em;
}

.cover-body {
  margin: 1.6rem 0 0;
  max-width: 46ch;
  font-size: 15.5px;
  line-height: 1.6;
  color: var(--invert-fg-muted);
}

.cover-actions {
  margin-top: 2.5rem;
  padding-top: 2rem;
  border-top: 1px solid rgba(255, 255, 255, .18);
  display: flex;
  align-items: center;
  gap: 1.5rem;
  flex-wrap: wrap;
}
:root[data-theme='dark'] .cover-actions { border-top-color: var(--border); }

.cover-cta {
  display: inline-flex;
  align-items: center;
  gap: 1.5rem;
  background: var(--invert-fg);
  color: var(--invert-bg);
  border: none;
  font: 700 13.5px var(--display);
  letter-spacing: .08em;
  text-transform: uppercase;
  padding: 1.05rem 1.4rem;
  cursor: pointer;
  transition: transform .2s ease, background .2s ease;
}
.cover-cta svg { width: 18px; height: 18px; }
.cover-cta:hover { transform: translateX(3px); }

.cover-alt {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  color: var(--invert-fg-muted);
  font: 500 12px var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  text-underline-offset: 5px;
  min-height: 24px;
}
/* Without this the icon takes its intrinsic size and lands as a huge arrow. */
.cover-alt svg { width: 14px; height: 14px; flex: none; }
.cover-alt:hover { color: var(--invert-fg); }

.cover-right {
  background: var(--surface);
  padding: 2.5rem 2.5rem 0;
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.cover-right-head { display: flex; justify-content: flex-end; }

.cover-demo {
  margin: auto 0;
  padding: 2rem 0;
  display: grid;
  gap: 1.4rem;
}

/* Available to screen readers, absent from the page. */
.vh {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.demo-group { display: grid; gap: .4rem; }

.demo-group-label {
  font: 600 9.5px var(--mono);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--fg-muted);
}

/* Origin on its own line above the question: at this width a fourth column
   would squeeze the question text past the point of being readable. */
.demo-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 7rem 3rem;
  grid-template-areas:
    "origin origin origin"
    "q      bar    a";
  gap: .2rem .9rem;
  align-items: center;
  padding: .5rem .7rem;
  background: var(--surface-2);
  border-left: 3px solid var(--attest);
  font: 400 11.5px var(--mono);
}

.demo-origin {
  grid-area: origin;
  font-size: 10px;
  letter-spacing: .04em;
  color: var(--fg-muted);
}
.demo-q { grid-area: q; min-width: 0; overflow-wrap: anywhere; }
.demo-a { grid-area: a; text-align: right; color: var(--attest); font-weight: 600; }

.demo-bar {
  grid-area: bar;
  display: grid;
  place-items: center;
  height: 14px;
  background: var(--withheld);
  color: var(--withheld-label);
  font: 600 8.5px var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
}

/* The counterfactual. Same row shape so the contrast is in the content and
   not in two different-looking widgets. */
.demo-row.wire {
  border-left-color: var(--refuse);
  /* The payload needs the full width - it is the point of the row. */
  grid-template-areas:
    "origin origin origin"
    "q      q      a"
    "sent   sent   sent";
}
.demo-row.wire .demo-a.no { color: var(--refuse); }

.demo-sent-label {
  display: block;
  font-size: 8.5px;
  letter-spacing: .12em;
  text-transform: uppercase;
  opacity: .75;
  margin-bottom: .1rem;
}

.demo-sent {
  grid-area: sent;
  min-width: 0;
  overflow-wrap: anywhere;
  margin-top: .15rem;
  padding: .25rem .4rem;
  background: var(--hold-surface);
  color: var(--refuse);
  font-size: 10px;
}

/* Nothing was withheld here because nothing was asked. */
.demo-row.none { border-left-color: var(--border); }
.demo-row.none .demo-a { color: var(--fg-muted); }

.demo-bar.empty {
  background: none;
  border: 1px dashed var(--border);
  color: var(--fg-muted);
  font-size: 7.5px;
  letter-spacing: .06em;
}

.demo-caption {
  margin: 0;
  font-size: 12.5px;
  color: var(--fg-muted);
  max-width: 52ch;
}

.cover-stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: var(--border);
  margin: 0 -2.5rem;
  border-top: 1px solid var(--border);
}

.cover-stat {
  background: var(--invert-bg);
  color: var(--invert-fg);
  padding: 1.25rem 1.5rem 1.4rem;
}
.cover-stat-label {
  display: block;
  font: 600 10px var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--attest-on-invert);
  margin-bottom: .35rem;
}
.cover-stat-figure {
  font: 700 34px/1 var(--mono);
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 900px) {
  .cover { grid-template-columns: 1fr; min-height: 0; }
  .cover-left { padding: 1.75rem 1.5rem 2.25rem; }
  .cover-right { padding: 1.75rem 1.5rem 0; }
  .cover-stats { margin: 0 -1.5rem; }
  .cover-title { margin-top: 2rem; }
}



/* ============ what this is ============ */

.intro {
  background: var(--bg);
  border-top: 1px solid var(--border);
  padding: 4rem 2rem 4.25rem;
}

.intro-inner { max-width: 1240px; margin: 0 auto; }

/* Reset the pane-heading rules, which this would otherwise inherit. */
.intro-title {
  display: block;
  margin: .9rem 0 2rem;
  padding: 0;
  border: none;
  text-transform: none;
  font: 700 clamp(1.5rem, 3vw, 2.2rem)/1.15 var(--display);
  letter-spacing: -0.03em;
  max-width: 24ch;
}

.intro-cols {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: 3rem;
  align-items: start;
}

.intro-lead {
  margin: 0;
  font-size: clamp(15.5px, 1.35vw, 18px);
  line-height: 1.6;
  max-width: 62ch;
}

/* This column carries the actual claim - what WebMCP is and what the demo
   does - and it was set 4px smaller in muted grey, a third of the lead's
   contrast. That reads as a footnote to the paragraph beside it rather than
   the other half of the same argument. Still subordinate to the lead, no
   longer demoted out of it. Capped measure because at this width the smaller
   type was running past 75 characters a line. */
.intro-body {
  max-width: 64ch;
  color: var(--fg);
}
.intro-cols > div p {
  margin: 0;
  font-size: clamp(14.5px, 1.15vw, 16px);
  line-height: 1.65;
  color: var(--fg);
}
/* After the reset above, not before it: same specificity, so order decides. */
.intro-cols > div p + p { margin-top: 1rem; }
.intro-cols strong { color: var(--fg); }

@media (max-width: 860px) {
  .intro { padding: 2.75rem 1.1rem 3rem; }
  .intro-cols { grid-template-columns: 1fr; gap: 1.5rem; }
  .intro-title { margin-bottom: 1.5rem; }
}

/* ============ how it works ============ */

.explain {
  background: var(--surface);
  border-top: 1px solid var(--border);
  padding: 4rem 2rem 4.5rem;
}

.explain-head { max-width: 1240px; margin: 0 auto 2.5rem; }
.eyebrow.dark { color: var(--fg-muted); display: block; margin-bottom: .8rem; }

/* Reset the pane-heading rules: h2 elsewhere is a small uppercase label with a
   rule under it, which this was silently inheriting. */
.explain-title {
  display: block;
  margin: 0;
  padding: 0;
  border: none;
  text-transform: none;
  font: 800 clamp(1.7rem, 3.4vw, 2.6rem)/1.05 var(--display);
  letter-spacing: -0.035em;
  max-width: 20ch;
}

.steps {
  list-style: none;
  max-width: 1240px;
  margin: 0 auto;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1px;
  background: var(--border-soft);
  border: 1px solid var(--border-soft);
}

.steps li {
  background: var(--surface);
  padding: 1.5rem 1.5rem 1.7rem;
}

/* The numbering is real: these are ordered, and the order is the mechanism. */
.step-no {
  display: block;
  font: 700 11px var(--mono);
  letter-spacing: .18em;
  color: var(--attest);
  margin-bottom: .9rem;
}

.steps h3 {
  margin: 0 0 .5rem;
  font: 600 15px/1.25 var(--display);
  letter-spacing: -0.01em;
  /* Reserve two lines. Three of the four headings wrap and one does not, so
     without this the odd card's body copy started 18px above its neighbours -
     visible as a broken baseline straight across a row of four. */
  min-height: 2.5em;
}

.steps p { margin: 0; font-size: 13.5px; line-height: 1.6; color: var(--fg-muted); }
.steps code { font-size: 11.5px; overflow-wrap: anywhere; }
.steps em { font-style: normal; color: var(--hold); font-weight: 600; }

.explain-foot {
  max-width: 1240px;
  margin: 2.5rem auto 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
  flex-wrap: wrap;
  border-top: 1px solid var(--border);
  padding-top: 2rem;
}

.explain-note { margin: 0; max-width: 58ch; font-size: 13px; color: var(--fg-muted); }

.cover-cta.dark { background: var(--fg); color: var(--bg); }

@media (max-width: 620px) {
  .explain { padding: 2.5rem 1.1rem 3rem; }
  .explain-foot { padding-top: 1.5rem; margin-top: 1.75rem; }
  .cover-cta.dark { width: 100%; justify-content: space-between; }
}

/* ============ trust banner ============ */

.trust {
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  font-size: 12.5px;
}

.trust-row {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .5rem 2rem;
  max-width: 1240px;
  margin: 0 auto;
  flex-wrap: wrap;
}

.trust-mark { width: 15px; height: 15px; flex: none; color: var(--attest); }

.trust-more {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  background: none;
  border: none;
  padding: .2rem .1rem;
  color: var(--attest);
  font: 500 12.5px var(--display);
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 3px;
  min-height: 24px;
}
.trust-more svg { width: 13px; height: 13px; transition: transform .2s ease; }
.trust-more[aria-expanded='true'] svg { transform: rotate(180deg); }

.trust-detail {
  max-width: 1240px;
  margin: 0 auto;
  padding: .25rem 2rem 1.1rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 1.5rem;
}
.trust-detail h3 {
  margin: 0 0 .3rem;
  font: 700 11px var(--display);
  letter-spacing: .1em;
  text-transform: uppercase;
}
.trust-detail p { margin: 0; color: var(--fg-muted); line-height: 1.55; }
.trust-detail a { color: var(--attest); }

/* ============ ticker ============ */

.ticker {
  /* Paint containment: the track is several viewports wide, and this keeps it
     from affecting anything outside no matter how it is sized. */
  contain: paint;
  background: var(--invert-bg);
  color: var(--invert-fg);
  overflow: hidden;
  border-bottom: 1px solid var(--border);
  padding: .4rem 0;
}

.ticker-set { display: contents; }

.ticker-track {
  display: flex;
  gap: 2.5rem;
  white-space: nowrap;
  width: max-content;
  font: 500 10.5px var(--mono);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--invert-fg-muted);
  animation: ticker 24s linear infinite;
}
.ticker-track b { color: var(--attest-on-invert); font-weight: 600; }

@keyframes ticker { to { transform: translateX(-50%); } }

/* The ticker scrolls regardless of prefers-reduced-motion, at the product
   owner's explicit request. That is a deliberate exception, not an oversight:
   a machine with animations switched off will still see this move. It is a
   single decorative strip carrying no information that is not also on the page
   in static form, and every other animation here still honours the setting.
   To restore the behaviour, give .ticker-track `animation: none` inside a
   prefers-reduced-motion block. */

/* ============ header ============ */

header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.5rem;
  padding: 1.5rem 2rem 1.25rem;
  background: var(--invert-bg);
  color: var(--invert-fg);
}

.brand-name {
  margin: 0;
  font: 800 30px/1 var(--display);
  letter-spacing: -0.035em;
}

.sub {
  margin: .3rem 0 0;
  font-size: 12px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--invert-fg-muted);
}

.header-right { display: flex; align-items: flex-end; gap: 1.25rem; }

.case { text-align: right; display: grid; gap: .1rem; }
.case-label {
  font: 600 10px var(--display);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--invert-fg-muted);
}
.case-no { font: 600 15px var(--mono); letter-spacing: .04em; }
.case-name { font-size: 12.5px; color: var(--invert-fg-muted); }

.theme-toggle {
  width: 40px;
  height: 40px;
  padding: 0;
  display: grid;
  place-items: center;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, .28);
  color: var(--invert-fg);
  cursor: pointer;
  transition: background .2s ease, border-color .2s ease;
}
:root[data-theme='dark'] .theme-toggle { border-color: var(--border); }
.theme-toggle:hover { background: rgba(255, 255, 255, .1); }
.theme-toggle svg { width: 18px; height: 18px; }
.theme-toggle .icon-moon { display: none; }
:root[data-theme='dark'] .theme-toggle .icon-sun { display: none; }
:root[data-theme='dark'] .theme-toggle .icon-moon { display: block; }

/* ============ thesis row ============ */

.thesis {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1px;
  background: var(--border);
  border-bottom: 1px solid var(--border);
}

.stat {
  background: var(--surface);
  padding: 1.4rem 1.75rem 1.5rem;
  display: grid;
  gap: .15rem;
  align-content: start;
}

.stat .figure {
  font: 700 44px/1 var(--mono);
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

.stat .label {
  font: 600 12px var(--display);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--fg-muted);
}

.stat .hint { font-size: 12.5px; color: var(--fg-muted); max-width: 34ch; }

/* The one that must never move. */
.stat.held { background: var(--invert-bg); color: var(--invert-fg); }
.stat.held .figure { color: var(--invert-fg); }
.stat.held .label { color: var(--invert-fg); }
.stat.held .hint { color: var(--invert-fg-muted); }

/* In light mode the inverted block is the emphasis. Dark mode has no such
   inversion available - every surface is already dark - so the same "this is
   the number that matters" signal is carried by the accent instead. */
:root[data-theme='dark'] .stat.held {
  background: var(--surface-2);
  border-top: 3px solid var(--attest);
}
:root[data-theme='dark'] .stat.held .figure { color: var(--attest); }


@media (max-width: 860px) {
  .thesis { grid-template-columns: 1fr; }
}

/* ============ layout ============ */

main {
  max-width: 1240px;
  margin: 0 auto;
  padding: 1.5rem 2rem;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: 1.25rem;
  /* Content height, not row height. Stretching made both panes as tall as the
     taller one, so after a screening the ledger carried 247px of empty pane
     below its last row - and 89px before one. A pane that has run out of
     content should end. */
  align-items: start;
}

.pane {
  background: var(--surface);
  border: 1px solid var(--border);
  padding: 1.1rem 1.25rem 1.25rem;
  display: flex;
  flex-direction: column;
  /* Grid items default to min-width:auto and refuse to shrink below their
     content. Without this the ledger's min-width drags every pane past the
     viewport and the whole page scrolls sideways. */
  min-width: 0;
}
.span2 { grid-column: 1 / -1; }

@media (max-width: 980px) {
  main { grid-template-columns: 1fr; }
}

h2 {
  margin: 0 0 .7rem;
  font: 700 12px/1.3 var(--display);
  text-transform: uppercase;
  letter-spacing: .13em;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  border-bottom: 2px solid var(--fg);
  padding-bottom: .5rem;
}

.pane-note {
  font: 400 12px var(--display);
  text-transform: none;
  letter-spacing: 0;
  color: var(--fg-muted);
  text-align: right;
}

.note { margin: .4rem 0 .8rem; color: var(--fg-muted); font-size: 13px; max-width: 68ch; }

code {
  font: 12px var(--mono);
  background: var(--surface-2);
  padding: .1em .35em;
}

/* ============ agencies ============ */

.agencies { display: grid; gap: 1px; background: var(--border-soft); border: 1px solid var(--border-soft); }

.agency {
  display: grid;
  grid-template-columns: 10px minmax(11rem, max-content) 1fr max-content;
  align-items: center;
  gap: .9rem;
  padding: .6rem .8rem;
  background: var(--surface);
  text-decoration: none;
  color: inherit;
}
.agency:hover { background: var(--surface-2); }

.agency .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--refuse); }
.agency.online .dot { background: var(--attest); }

.agency-name { font-weight: 600; font-size: 13px; }
.agency-origin { font: 400 11.5px var(--mono); color: var(--fg-muted); }
.agency-state {
  font: 600 10px var(--display);
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--refuse);
}
.agency.online .agency-state { color: var(--attest); }

.controls { display: flex; gap: .5rem; margin-top: 1rem; flex-wrap: wrap; }

button {
  font: 600 13px var(--display);
  letter-spacing: .01em;
  padding: .6rem 1.1rem;
  border: 1px solid var(--fg);
  cursor: pointer;
  transition: background .2s ease, border-color .2s ease, color .2s ease;
}
button:disabled { opacity: .5; cursor: progress; }
button.primary { background: var(--fg); color: var(--bg); }
button.primary:hover:not(:disabled) { opacity: .85; }
button.ghost { background: transparent; color: var(--fg); border-color: var(--border); }
button.ghost:hover { border-color: var(--fg); }

/* ============ determinations ============ */

.programs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: .5rem;
  align-content: start;
}

.program {
  border: 1px solid var(--border-soft);
  border-left: 4px solid var(--border);
  padding: .75rem .9rem;
  background: var(--surface);
}
.program.eligible { border-left-color: var(--attest); }
.program.ineligible { border-left-color: var(--refuse); }
.program.indeterminate { border-left-color: var(--hold); }

/* Verdict above the name, not beside it.
   Beside it, the badge took a third of a 266px card and squeezed the longest
   name onto a third line, so that card's description sat 16px below its
   neighbours'. It also put the one thing you scan for - the verdict - at a
   different height on every card. Stacked, the name gets the full width and
   the stamp lands in the same place every time. DOM order is still name then
   verdict, so it reads "Household Food Assistance, eligible" aloud. */
.program header {
  display: flex;
  flex-direction: column-reverse;
  align-items: flex-start;
  gap: .35rem;
  padding: 0;
  background: none;
  color: inherit;
}
.program h3 { margin: 0; font: 600 14.5px var(--display); letter-spacing: -0.01em; }

/* Stamped, not badged: this is a determination on a case file. */
.badge {
  font: 700 10px var(--mono);
  text-transform: uppercase;
  letter-spacing: .14em;
  padding: .2rem .45rem;
  border: 1.5px solid var(--fg-muted);
  color: var(--fg-muted);
  white-space: nowrap;
  transform: rotate(-1.5deg);
}
.program.eligible .badge { color: var(--attest); border-color: var(--attest); }
.program.ineligible .badge { color: var(--refuse); border-color: var(--refuse); }
.program.indeterminate .badge { color: var(--hold); border-color: var(--hold); }

.blurb { margin: .3rem 0 .55rem; font-size: 12.5px; color: var(--fg-muted); }
.blurb, .program h3 { min-width: 0; overflow-wrap: anywhere; }

.basis { list-style: none; margin: 0; padding: 0; display: grid; gap: 1px; }
.basis li {
  display: flex; justify-content: space-between; gap: .8rem;
  font-size: 12.5px; padding: .28rem .45rem; background: var(--surface-2);
  min-width: 0;
}
.basis li span { min-width: 0; overflow-wrap: anywhere; }
.basis li em { flex: none; font-style: normal; font: 600 10px var(--mono); letter-spacing: .08em; text-transform: uppercase; }
.basis li.pass em { color: var(--attest); }
.basis li.fail em { color: var(--refuse); }
.basis li.unavailable em, .basis li.denied em, .basis li.cancelled em { color: var(--hold); }

.program footer {
  margin-top: .55rem; font-size: 12px; color: var(--fg-muted);
  border-top: 1px solid var(--border-soft); padding-top: .4rem;
}
.program footer.zero { color: var(--attest); font-weight: 600; }

/* ============ ledger ============ */

.ledger-head, .entry {
  display: grid;
  grid-template-columns: minmax(5.5rem, max-content) minmax(0, 1fr) 7.5rem 4.5rem;
  gap: .7rem;
  align-items: center;
}

.ledger-head {
  font: 600 10px var(--display);
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--fg-muted);
  padding: 0 .5rem .35rem;
  border-bottom: 1px solid var(--border);
}

/* The ledger rows are genuinely wide, and screening repeatedly makes the log
   genuinely long - so this container scrolls on both axes. The page body never
   scrolls sideways, and the pane never grows past the determinations beside it.
   min-height: 0 because a flex item will not shrink below its content
   otherwise, and the cap would do nothing. */
.ledger-scroll {
  overflow: auto;
  flex: 1 1 auto;
  min-height: 0;
}

/* The fallback lives on the pane, not the scroller. On the scroller it was a
   second cap competing with the measured one, and the smaller of the two won:
   80vh of a short window is less than the determinations pane beside it, so
   the ledger ended up shorter than the thing it is supposed to line up with.
   Here the inline max-height that capLedgerToDeterminations sets overrides it,
   and this only applies when the panes stack or the measurement never runs. */
.pane.ledger-pane { max-height: 85vh; }
.ledger-head, .ledger { min-width: 32rem; }

/* Column labels stay put while the log scrolls under them. */
.ledger-head {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--surface);
}

.ledger { display: grid; gap: 1px; align-content: start; }

.entry {
  padding: .4rem .5rem;
  background: var(--surface-2);
  font: 400 11.5px var(--mono);
}
.entry.denied, .entry.unavailable, .entry.cancelled { background: var(--hold-surface); }

.entry .origin { color: var(--fg-muted); }
/* Wraps, never truncates. This column was nowrap with an ellipsis, and at the
   pane's real width it was cutting the threshold and tax year off nine of
   eleven rows - up to 141px of the question gone. The ledger exists to show
   exactly what was asked; a row that hides half the question is not evidence. */
.entry .q {
  color: var(--fg);
  min-width: 0;
  overflow-wrap: anywhere;
}
.entry .q i { font-style: normal; color: var(--fg-muted); }
/* Why an answer is missing, said out loud rather than hidden in a tooltip. */
.entry .why { display: block; color: var(--hold); font-size: 10px; }

/* The signature: a block standing in for the value that never moved. */
.withheld {
  display: block;
  height: 13px;
  background: var(--withheld);
  position: relative;
}
.withheld::after {
  content: attr(data-field);
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font: 600 8.5px var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--withheld-label);
}
.entry.denied .withheld, .entry.unavailable .withheld, .entry.cancelled .withheld { background: var(--hold); }

/* Nothing was withheld here, because nothing was reached. */
.entry .nothing {
  font: 400 9.5px var(--display);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--hold);
  border: 1px dashed currentColor;
  text-align: center;
  padding: .1rem 0;
}

.entry .a { font-weight: 600; color: var(--attest); text-align: right; }
.entry .a.no { color: var(--refuse); }
.entry .a.none { color: var(--fg-muted); }


/* Empty state, doing a job. Same row geometry as a real crossing so the
   example teaches the thing the ledger will show. */
.ledger-empty { min-width: 32rem; }

.entry.example {
  background: transparent;
  border: 1px dashed var(--border);
  opacity: .85;
}
.entry.example .a { color: var(--attest); }

.example-note {
  margin: .7rem 0 0;
  padding: 0 .5rem;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--fg-muted);
  max-width: 62ch;
}
.example-note strong { color: var(--fg); }

/* What each column means. Fills the panel with something worth reading rather
   than with padding, and answers the question a first-time viewer has. */
.legend {
  margin: 1.1rem 0 0;
  padding: .9rem 0 0;
  border-top: 1px solid var(--border-soft);
  display: grid;
  grid-template-columns: minmax(8rem, max-content) 1fr;
  row-gap: .5rem;
  column-gap: 1rem;
}
.legend dt {
  font: 600 10px var(--display);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--attest);
  padding-top: .1rem;
}
.legend dd { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--fg-muted); }

@media (max-width: 620px) {
  .legend { grid-template-columns: 1fr; row-gap: .15rem; }
  .legend dd { margin-bottom: .5rem; }
}


/* ============ agent tool surface ============ */

.agent-tools {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(330px, 1fr));
  gap: 1px;
}

.agent-tools li {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  /* Cards in a row stretch to the tallest, and auto grid rows default to
     absorbing that slack - so a card sharing a row with a long description
     pushed its own name and description down by 6 and 12px. Pin the rows to
     their content and let the slack fall to the bottom of the card, where it
     costs nothing. */
  align-content: start;
  gap: .1rem;
  padding: .55rem .6rem;
  background: var(--surface-2);
  border-left: 3px solid var(--attest);
}

.tool-title { font: 600 13px var(--display); }
.agent-tools code { font-size: 11px; background: none; padding: 0; color: var(--attest); }
.tool-desc { font-size: 12px; color: var(--fg-muted); line-height: 1.45; }
.tool-empty { border-left-color: var(--hold); font-size: 12.5px; color: var(--fg-muted); }

.try {
  margin-top: 1rem;
  padding-top: .9rem;
  border-top: 1px solid var(--border-soft);
}
.try-label {
  display: block;
  font: 600 10px var(--display);
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--fg-muted);
  margin-bottom: .5rem;
}
.try ul {
  list-style: none; margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: .4rem;
}
.try li {
  font: 400 12.5px var(--mono);
  color: var(--fg);
  padding: .35rem .55rem;
  background: var(--surface-2);
  border-left: 3px solid var(--border);
}
.try li::before { content: '“'; color: var(--fg-muted); }
.try li::after { content: '”'; color: var(--fg-muted); }

/* ============ self-test ============ */

.st {
  display: grid;
  grid-template-columns: 3.2rem 17rem 1fr;
  gap: .8rem;
  padding: .32rem .5rem;
  font: 400 12px var(--mono);
  background: var(--surface-2);
  border-left: 4px solid var(--attest);
  margin-bottom: 1px;
}
.st.bad { border-left-color: var(--refuse); }
.st span:first-child { font-weight: 700; color: var(--attest); letter-spacing: .1em; }
.st.bad span:first-child { color: var(--refuse); }

/* ============ frames ============ */

.frame-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: .75rem;
}
.frame-grid iframe {
  width: 100%;
  /* Close to what these pages actually measure (864-910px), so the first
     paint is already about right and the height message only fine-tunes it.
     A big late resize left the out-of-process frames compositing at their old
     surface size, which showed up as content clipped at the right edge. It is
     also a far better fallback if the message never arrives. */
  height: 900px;
  border: 1px solid var(--border);
  background: var(--surface);
}

/* ============ alerts ============ */

.alert {
  background: var(--hold-surface);
  border-bottom: 2px solid var(--hold);
  color: var(--fg);
  padding: .8rem 2rem;
  font-size: 13px;
}
.alert.consent { background: var(--surface-2); border-bottom-color: var(--attest); }

/* ============ narrow screens ============ */

@media (max-width: 620px) {
  header {
    flex-direction: column;
    align-items: flex-start;
    gap: .75rem;
    padding: 1.1rem 1.1rem 1rem;
  }
  .header-right { width: 100%; justify-content: space-between; align-items: center; }
  .case { text-align: left; }
  .brand-name { font-size: 25px; }

  main { padding: 1rem; gap: 1rem; }
  .pane { padding: .9rem 1rem 1rem; }
  .stat { padding: 1.1rem 1.1rem 1.2rem; }
  .alert, .trust-row, .trust-detail { padding-left: 1.1rem; padding-right: 1.1rem; }

  /* Let the caption drop under the heading rather than fight it for room. */
  h2 { flex-direction: column; align-items: flex-start; gap: .2rem; }
  .pane-note { text-align: left; }

  /* Origin and status move under the agency name instead of overflowing. */
  .agency {
    grid-template-columns: 10px minmax(0, 1fr);
    row-gap: .15rem;
    padding: .65rem .7rem;
  }
  .agency-origin, .agency-state { grid-column: 2; }
  .agency-state { justify-self: start; }

  .controls button { flex: 1 1 auto; }
  .frame-grid iframe { height: 640px; }

  .demo-row { grid-template-columns: minmax(0, 1fr) 5rem 2.5rem; gap: .5rem; }
}

@media (prefers-reduced-motion: no-preference) {
  .entry { animation: settle .22s ease-out; }
  @keyframes settle { from { opacity: 0; transform: translateY(-2px); } }
}
