/*
 * leastgrant.xyz
 *
 * One rule holds the visual system together: colour is a verdict.
 *
 * The page is otherwise achromatic -- warm near-black, bone, and four greys.
 * The only saturated pixels anywhere are the three decisions the product can
 * return. That is not a stylistic preference, it is the product's own
 * discipline: the CLI reserves ✓ / ? / ✗ for decisions and says everything
 * else in plain text, so the website does the same. It also means a reader
 * skimming for "where does this thing actually decide something" can find it
 * by looking for the only colour on screen.
 *
 * Consequences worth knowing before editing:
 *   - Links are not blue. They are bone with an underline.
 *   - There is no brand accent. Do not add one.
 *   - Amber is `ask`. Using it for a button would say the button is a verdict.
 *     Focus rings are the single exception, and they earn it: a focus ring
 *     means "this is where the attention is", which is what ask means too.
 */

/* --- fonts ---------------------------------------------------------------- */
/*
 * Self-hosted, subset to the glyphs this site uses, from 222 KB down to 41 KB.
 * No external origin: a webfont from a CDN is a third party who can see every
 * reader of a security tool's homepage, and a script-injection surface if that
 * CDN is ever wrong.
 */
@font-face {
  font-family: 'Plex Mono';
  src: url('/fonts/IBMPlexMono-Regular.aa779c16fe.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Plex Mono';
  src: url('/fonts/IBMPlexMono-SemiBold.90740aae78.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Plex Sans';
  src: url('/fonts/IBMPlexSans-Regular.0cfced70c9.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Plex Sans';
  src: url('/fonts/IBMPlexSans-SemiBold.aac7be1152.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

:root {
  --void: #0b0a09;
  --pit: #100e0c;
  --raise: #171512;

  /*
   * Three greys, and they are not interchangeable.
   *
   *   --line     decorative hairlines only: section rules, card edges. These
   *              carry no information, so they are exempt from contrast minima
   *              and are allowed to be nearly invisible.
   *   --edge     the visible boundary of something you can click. WCAG 1.4.11
   *              wants 3:1 for that against the page, and a button whose only
   *              affordance is its border has to meet it. 3.28:1.
   *              Never type. It was being used as the text colour in six rules
   *              — evidence tags, config-path captions, the "unknown" marker —
   *              all of them at 0.66-0.72rem, where 3.02:1 is furthest from
   *              adequate. Small secondary text is --dim, which is the dimmest
   *              token that still clears 4.5:1 on all three backgrounds.
   *   --dim      text. Every label, caption and eyebrow on this site is small,
   *              so it needs the full 4.5:1 and gets it on all three
   *              backgrounds -- 5.17 on void, 5.03 on pit, 4.76 on raise.
   *
   * The earlier palette used one grey for all three jobs, which put 1.5:1 text
   * on the page. Do not merge them again.
   */
  --line: #262220;
  --edge: #6b6159;
  --dim: #8a8177;

  --body: #b0a89d;
  --bright: #f2ede5;

  --allow: #7ea75e;
  --ask: #e2a63f;
  --deny: #d06043;

  /* U+2717 is not in IBM Plex Mono. The CLI emits it, the site renders the
   * CLI verbatim, so the stack has to reach a font that has it rather than
   * show a tofu box in the one place the page says "deny". */
  --mono: 'Plex Mono', ui-monospace, 'Cascadia Mono', 'Segoe UI Symbol', 'Apple Symbols',
    'Noto Sans Symbols2', 'DejaVu Sans Mono', monospace;
  --sans: 'Plex Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;

  --shell: 1180px;
  --gap: clamp(1.25rem, 4vw, 2.5rem);
  --rhythm: clamp(4rem, 10vw, 7.5rem);

  color-scheme: dark;
}

/* --- reset ---------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Anchored headings otherwise land under the sticky header. */
  scroll-padding-top: 5rem;
}

body {
  margin: 0;
  background: var(--void);
  color: var(--body);
  font-family: var(--sans);
  font-size: 1.0625rem;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img,
svg {
  max-width: 100%;
  height: auto;
}

/* --- type ----------------------------------------------------------------- */
/*
 * Headings are monospace. At display size the fixed advance width reads as
 * engineered rather than typeset, which is the right register for a tool whose
 * output you read in a terminal -- and it is not the grotesk-with-tight-
 * tracking that every other developer landing page opens with.
 */

h1,
h2,
h3,
h4 {
  font-family: var(--mono);
  font-weight: 600;
  color: var(--bright);
  letter-spacing: -0.03em;
  line-height: 1.1;
  margin: 0 0 0.6em;
  text-wrap: balance;
}

h1 {
  font-size: clamp(2.4rem, 7vw, 4.6rem);
  line-height: 0.98;
  letter-spacing: -0.045em;
}

h2 {
  font-size: clamp(1.6rem, 3.6vw, 2.3rem);
}

h3 {
  font-size: clamp(1.1rem, 2vw, 1.3rem);
  letter-spacing: -0.02em;
}

p {
  margin: 0 0 1.1em;
  max-width: 68ch;
}

a {
  color: var(--bright);
  text-decoration-line: underline;
  text-decoration-color: var(--edge);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.22em;
  transition: text-decoration-color 120ms ease;
}

a:hover {
  text-decoration-color: var(--bright);
}

:focus-visible {
  outline: 2px solid var(--ask);
  outline-offset: 3px;
  border-radius: 2px;
}

code,
kbd,
pre,
samp {
  font-family: var(--mono);
  font-size: 0.9em;
}

/*
 * `pre code` is deliberately excluded from all of this. A code sample scrolls;
 * a wrapped command line is a different command line, and somebody copying it
 * would get that one.
 */
:not(pre) > code {
  background: var(--raise);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0.1em 0.34em;
  color: var(--bright);
  font-size: 0.85em;
  /* `anywhere` rather than `break-word`: only the former also lowers the
     min-content width, which is what a grid or flex track measures. An
     identifier like `mcp__server__tool` was setting the minimum width of the
     column it sat in even though it was allowed to break. */
  overflow-wrap: anywhere;
}

strong {
  color: var(--bright);
  font-weight: 600;
}

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 2.5rem 0;
}

/*
 * A word longer than its column breaks rather than pushing the page.
 *
 * Only affects text that would otherwise overflow, so ordinary paragraphs are
 * untouched. Without it a single unbreakable token — a full advisory URL on the
 * security policy page, a capability path in the verification list — set the
 * minimum width of the whole document and every viewport narrower than that
 * token scrolled sideways.
 */
body {
  overflow-wrap: break-word;
}

/* --- layout --------------------------------------------------------------- */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gap);
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--bright);
  color: var(--void);
  padding: 0.6rem 1rem;
  z-index: 100;
  font-family: var(--mono);
  font-size: 0.85rem;
}

.skip:focus {
  left: 0.5rem;
  top: 0.5rem;
}

section {
  padding-block: var(--rhythm);
  border-top: 1px solid var(--line);
}

section:first-of-type {
  border-top: 0;
}

/*
 * The section label. Lowercase, because the CLI is lowercase -- it writes
 * `what it does` and `blast radius`, never `WHAT IT DOES`. Carrying that into
 * the page keeps the product's voice instead of importing the small-caps
 * eyebrow every landing page uses.
 */
.eyebrow {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  color: var(--dim);
  margin: 0 0 1.4rem;
  display: flex;
  align-items: center;
  gap: 0.7rem;
}

.eyebrow::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--line);
}

.lede {
  font-size: clamp(1.1rem, 2vw, 1.3rem);
  color: var(--body);
  max-width: 60ch;
}

/*
 * Subheadings directly inside a section need air above them; the ones nested in
 * cards, panels and steps set their own.
 */
section > .shell > h3 {
  margin-top: 2.8rem;
}

section > .shell > .facts + h3,
section > .shell > .support + h3,
section > .shell > .term + h3 {
  margin-top: 3.2rem;
}

/* --- header --------------------------------------------------------------- */

/*
 * Opaque, not translucent.
 *
 * A blurred backdrop needs its own compositing layer that repaints on every
 * scroll frame, and buys nothing on a page whose background is one flat colour
 * -- the blur has nothing to blur. It also, in passing, made the page
 * impossible to screenshot reliably.
 */
.top {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--void);
  border-bottom: 1px solid var(--line);
}

.top-in {
  display: flex;
  align-items: center;
  gap: 1.4rem;
  height: 3.6rem;
}

.wordmark {
  font-family: var(--mono);
  font-weight: 600;
  color: var(--bright);
  text-decoration: none;
  letter-spacing: -0.03em;
  font-size: 1rem;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.wordmark:hover {
  text-decoration: none;
}

/*
 * The mark: an L drawn as a corner -- a wall and a floor -- with a G contained
 * inside it. The site's initials and its central distinction at once.
 *
 * Bone, never amber. Colour on this site means a verdict, and a logo that
 * borrows the `ask` colour would be claiming to be one.
 */
.wordmark .mark {
  width: 1.15rem;
  height: 1.15rem;
  flex: none;
  color: var(--bright);
}

/*
 * It draws itself once, in the order the engine evaluates: the boundary first,
 * then the thing being judged against it. A floor is a precondition, not an
 * outcome -- the same reason the decision table puts the floors above learning.
 *
 * 420ms, once, on load. Not a spin and not a pulse; if it ever becomes one,
 * delete it.
 */
.wordmark .mark path {
  stroke-dasharray: 60;
  stroke-dashoffset: 0;
}

.wordmark .mark .lg-wall {
  animation: lg-draw 200ms linear both;
}

.wordmark .mark .lg-bowl {
  animation: lg-draw 220ms linear 200ms both;
}

@keyframes lg-draw {
  from {
    stroke-dashoffset: 60;
  }
  to {
    stroke-dashoffset: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .wordmark .mark path {
    animation: none;
    stroke-dashoffset: 0;
  }
}

.top nav {
  margin-left: auto;
  display: flex;
  gap: 1.25rem;
  align-items: center;
  font-family: var(--mono);
  font-size: 0.82rem;
}

.top nav a {
  color: var(--dim);
  text-decoration: none;
}

.top nav a:hover,
.top nav a[aria-current='page'] {
  color: var(--bright);
}

@media (max-width: 640px) {
  .top nav .hide-sm {
    display: none;
  }
}

/*
 * At 320px the masthead is the widest thing on every page — logo plus four
 * links came to 342px against a 320px viewport, so all twenty pages scrolled
 * by the same 22px. Tightening the nav is enough; hiding a link would cost
 * navigation on the size that needs it most.
 */
@media (max-width: 380px) {
  .top nav {
    gap: 0.55rem;
    font-size: 0.72rem;
  }

  /* The shell's own inset is part of the same 320px budget. */
  .shell {
    padding-inline: 1rem;
  }
}

/* --- hero ----------------------------------------------------------------- */

.hero {
  padding-block: clamp(3rem, 9vw, 6rem) var(--rhythm);
  border-top: 0;
}

/*
 * `minmax(0, 1fr)`, never a bare `1fr`.
 *
 * A `1fr` track is `minmax(auto, 1fr)`, and `auto` means "at least as wide as
 * the content's minimum". A nowrap command string or a wide <pre> therefore
 * pushes the track past the viewport and the whole page scrolls sideways on a
 * phone. Every single-column grid on this page is clamped for that reason.
 */
.hero-grid {
  display: grid;
  gap: clamp(2.5rem, 5vw, 4rem);
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}

@media (min-width: 960px) {
  .hero-grid {
    grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr);
    gap: 3.5rem;
  }
}

.tagline {
  font-family: var(--mono);
  font-size: clamp(1.05rem, 2.2vw, 1.35rem);
  color: var(--bright);
  font-weight: 400;
  letter-spacing: -0.02em;
  margin: 0 0 1.2rem;
  text-wrap: balance;
}

.hero p.lede {
  margin-bottom: 2rem;
}

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
}

.btn {
  font-family: var(--mono);
  font-size: 0.9rem;
  border: 1px solid var(--edge);
  background: var(--raise);
  color: var(--bright);
  padding: 0.7rem 1.15rem;
  border-radius: 4px;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  cursor: pointer;
  transition:
    border-color 120ms ease,
    background 120ms ease;
}

.btn:hover {
  border-color: var(--bright);
  background: #1d1a17;
  text-decoration: none;
}

.btn-primary {
  background: var(--bright);
  color: var(--void);
  border-color: var(--bright);
  font-weight: 600;
}

.btn-primary:hover {
  background: #fff;
  border-color: #fff;
}

.hero-note {
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--dim);
  margin-top: 1.6rem;
}

.hero-note a {
  color: var(--dim);
}

/* --- terminal ------------------------------------------------------------- */
/*
 * The window is deliberately plain: a hairline, a title, no traffic lights, no
 * fake blur, no drop shadow. It is the only large surface on the page, and it
 * has to read as an artifact rather than an illustration -- the text inside is
 * literally what `leastgrant check` printed during the build.
 */

.term {
  background: var(--pit);
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow: hidden;
  font-family: var(--mono);
}

.term-bar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.5rem 0.85rem;
  border-bottom: 1px solid var(--line);
  font-size: 0.72rem;
  color: var(--dim);
  letter-spacing: 0.06em;
  background: #0e0c0b;
}

.term-bar .dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--edge);
  flex: none;
}

.term-bar .grow {
  flex: 1;
}

.term-body {
  padding: 1rem 1.1rem 1.2rem;
  overflow-x: auto;
  font-size: 0.8rem;
  line-height: 1.65;
}

@media (min-width: 700px) {
  .term-body {
    font-size: 0.84rem;
  }
}

.term-body pre {
  margin: 0;
  font-size: inherit;
  white-space: pre;
}

.term .prompt {
  color: var(--dim);
}

.term .cmd {
  color: var(--bright);
}

/* Verdict marks. The only colour in the block. */
.v-allow {
  color: var(--allow);
}
.v-ask {
  color: var(--ask);
}
.v-deny {
  color: var(--deny);
}

.term .label {
  color: var(--dim);
}

.term .hint {
  color: var(--dim);
}

.term .bullet {
  color: var(--dim);
}

.term .muted {
  color: var(--dim);
}

/* The typing caret, only while the hero demo is running. */
.term-body .caret {
  display: inline-block;
  width: 0.55em;
  height: 1.05em;
  vertical-align: -0.18em;
  background: var(--bright);
  animation: blink 1s steps(1) infinite;
}

@keyframes blink {
  50% {
    opacity: 0;
  }
}

.term-foot {
  border-top: 1px solid var(--line);
  padding: 0.55rem 0.85rem;
  font-size: 0.72rem;
  color: var(--dim);
  display: flex;
  gap: 0.8rem;
  align-items: center;
  flex-wrap: wrap;
}

.term-foot .spacer {
  flex: 1;
}

.replay {
  font-family: var(--mono);
  font-size: 0.72rem;
  background: none;
  border: 1px solid var(--line);
  color: var(--dim);
  padding: 0.2rem 0.6rem;
  border-radius: 3px;
  cursor: pointer;
}

.replay:hover {
  color: var(--bright);
  border-color: var(--edge);
}

/* --- verdict legend ------------------------------------------------------- */

.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 1.2rem;
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--dim);
  margin-top: 1.1rem;
}

.legend span {
  display: inline-flex;
  gap: 0.4rem;
  align-items: center;
}

/* --- prose ---------------------------------------------------------------- */

.prose {
  max-width: 72ch;
}

.prose > * + * {
  margin-top: 1.1em;
}

.prose h2 {
  margin-top: 2.4em;
}

.prose h3 {
  margin-top: 2em;
}

.prose ul,
.prose ol {
  padding-left: 1.3em;
  margin-block: 1.1em;
}

.prose li {
  margin-bottom: 0.5em;
}

.prose li::marker {
  color: var(--dim);
}

.prose blockquote {
  margin: 1.4em 0;
  padding: 0.2em 0 0.2em 1.1em;
  border-left: 2px solid var(--edge);
  color: var(--dim);
}

.prose blockquote p:last-child {
  margin-bottom: 0;
}

/* --- code blocks ---------------------------------------------------------- */

figure.code {
  margin: 1.5em 0;
  background: var(--pit);
  border: 1px solid var(--line);
  border-radius: 5px;
  overflow: hidden;
  position: relative;
}

figure.code pre {
  margin: 0;
  padding: 0.9rem 1rem;
  overflow-x: auto;
  font-size: 0.82rem;
  line-height: 1.6;
}

figure.code[data-lang]::before {
  content: attr(data-lang);
  position: absolute;
  top: 0;
  right: 0;
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.1em;
  color: var(--dim);
  padding: 0.35rem 0.6rem;
  pointer-events: none;
}

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

.table-wrap {
  overflow-x: auto;
  margin: 1.5em 0;
  border: 1px solid var(--line);
  border-radius: 5px;
}

table {
  border-collapse: collapse;
  width: 100%;
  font-size: 0.94rem;
}

th,
td {
  text-align: left;
  padding: 0.62rem 0.85rem;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}

th {
  font-family: var(--mono);
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  color: var(--dim);
  background: var(--pit);
  white-space: nowrap;
}

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

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

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

/* --- the gauntlet --------------------------------------------------------- */
/*
 * The interactive decision order. This is the one place the page spends its
 * complexity budget, because the order *is* the product: the same command gets
 * a different answer depending on which gate reaches it first, and a static
 * list of six bullet points cannot show that.
 */

.gauntlet {
  display: grid;
  gap: clamp(1.5rem, 4vw, 3rem);
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 900px) {
  .gauntlet {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
    align-items: start;
  }
}

.picker {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-bottom: 1.5rem;
}

.pick {
  font-family: var(--mono);
  font-size: 0.8rem;
  text-align: left;
  background: var(--pit);
  border: 1px solid var(--line);
  color: var(--body);
  padding: 0.6rem 0.8rem;
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  transition:
    border-color 120ms ease,
    color 120ms ease;
  width: 100%;
}

.pick:hover {
  border-color: var(--edge);
  color: var(--bright);
}

.pick[aria-pressed='true'] {
  border-color: var(--edge);
  background: var(--raise);
  color: var(--bright);
}

.pick .cmdtext {
  /* A nowrap flex child will not shrink below its content width without this,
   * so the ellipsis never engages and the button overflows instead. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pick .glyph {
  flex: none;
  width: 1ch;
  color: var(--dim);
}

.pick[aria-pressed='true'] .glyph {
  color: inherit;
}

.gates {
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--pit);
  overflow: hidden;
}

.gate {
  display: grid;
  grid-template-columns: 2.2rem minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.8rem;
  padding: 0.75rem 0.9rem;
  border-bottom: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 0.82rem;
  transition:
    opacity 180ms ease,
    background 180ms ease;
}

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

.gate .n {
  color: var(--dim);
  font-size: 0.72rem;
  text-align: right;
}

.gate .what {
  color: var(--body);
  min-width: 0;
}

.gate .emits {
  font-size: 0.7rem;
  color: var(--dim);
  letter-spacing: 0.06em;
}

/* Consulted, and had nothing to say. */
.gate[data-state='passed'] {
  opacity: 0.5;
}

/* Never consulted, because something above already answered. */
.gate[data-state='unreached'] {
  opacity: 0.18;
}

.gate[data-state='unreached'] .emits {
  visibility: hidden;
}

.gate-key {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 1.1rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--dim);
  margin: 0.6rem 0 0;
  padding-left: 0.2rem;
}

.gate-key span {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.gate-key i {
  display: inline-block;
  width: 1.5rem;
  height: 2px;
  background: var(--dim);
  flex: none;
  font-style: normal;
}

.gate-key .k-passed i {
  opacity: 0.5;
}

.gate-key .k-hit i {
  background: currentColor;
  height: 3px;
}

.gate-key .k-unreached i {
  opacity: 0.18;
}

/* The gate that answered. */
.gate[data-state='hit'] {
  background: var(--raise);
  opacity: 1;
}

.gate[data-state='hit'] .n,
.gate[data-state='hit'] .what {
  color: var(--bright);
}

.gate[data-state='hit'] .emits {
  color: inherit;
  font-weight: 600;
}

.gate[data-verdict='allow'][data-state='hit'] {
  box-shadow: inset 3px 0 0 var(--allow);
  color: var(--allow);
}
.gate[data-verdict='ask'][data-state='hit'] {
  box-shadow: inset 3px 0 0 var(--ask);
  color: var(--ask);
}
.gate[data-verdict='deny'][data-state='hit'] {
  box-shadow: inset 3px 0 0 var(--deny);
  color: var(--deny);
}

/*
 * The sweep. When an example is chosen the gates light in order, top to
 * bottom, and stop at the one that answered -- the point being that gate 6
 * never ran because gate 4 already had something to say.
 *
 * Delays live in nth-child rules rather than an inline `--n` because a style
 * attribute needs `style-src-attr 'unsafe-inline'`, and no animation is worth
 * widening the policy for.
 */
.gates[data-run] .gate {
  animation: sweep 260ms ease both;
}
.gates[data-run] .gate:nth-child(1) {
  animation-delay: 0ms;
}
.gates[data-run] .gate:nth-child(2) {
  animation-delay: 70ms;
}
.gates[data-run] .gate:nth-child(3) {
  animation-delay: 140ms;
}
.gates[data-run] .gate:nth-child(4) {
  animation-delay: 210ms;
}
.gates[data-run] .gate:nth-child(5) {
  animation-delay: 280ms;
}
.gates[data-run] .gate:nth-child(6) {
  animation-delay: 350ms;
}

@keyframes sweep {
  from {
    opacity: 0.08;
    transform: translateX(-3px);
  }
}

/*
 * Without JavaScript every example panel is visible, stacked and labelled.
 * That is a worse reading order but a complete one. Once the script has taken
 * over it hides all but the chosen panel, and the separators below stop being
 * needed.
 */
.panel + .panel {
  margin-top: 1.5rem;
  padding-top: 1.5rem;
  border-top: 1px dashed var(--line);
}

[data-enhanced] .panel + .panel {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

.panel-label {
  font-family: var(--mono);
  font-size: 0.78rem;
  color: var(--dim);
  margin: 0 0 0.7rem;
}

[data-enhanced] .panel-label {
  display: none;
}

.outcome {
  margin-top: 1.1rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--pit);
  padding: 0.9rem 1rem;
}

.outcome-head {
  font-family: var(--mono);
  font-size: 0.95rem;
  font-weight: 600;
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
  margin-bottom: 0.55rem;
  flex-wrap: wrap;
}

/* The verdict is two words and must never be split across a line break. */
.outcome-head > span:first-child {
  white-space: nowrap;
  flex: none;
}

.outcome-head .muted {
  font-weight: 400;
  font-family: var(--sans);
  font-size: 0.92rem;
  color: var(--dim);
  min-width: 12rem;
  flex: 1;
}

.outcome-why {
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 0.88rem;
  color: var(--body);
}

.outcome-why li {
  display: flex;
  gap: 0.6rem;
  margin-bottom: 0.25rem;
}

.outcome-why li::before {
  content: '•';
  color: var(--dim);
  flex: none;
}

.outcome-blast {
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--dim);
  margin-top: 0.7rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--line);
}

/*
 * A claim the page wants read, not a footnote. Set at reading size in the body
 * face with the same left rule the terminal hints use, so it reads as the
 * project talking rather than as fine print.
 */
.pull {
  font-size: clamp(1.02rem, 1.7vw, 1.15rem);
  color: var(--body);
  max-width: 62ch;
  margin: 2rem 0 0;
  padding-left: 1.1rem;
  border-left: 2px solid var(--edge);
  line-height: 1.65;
}

.disclaimer {
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--dim);
  margin-top: 1.2rem;
  padding-left: 0.9rem;
  border-left: 2px solid var(--line);
  max-width: 62ch;
}

/* --- the two-panel claim -------------------------------------------------- */

.pair {
  display: grid;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow: hidden;
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 760px) {
  .pair {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
}

.pair > div {
  background: var(--pit);
  padding: clamp(1.4rem, 3vw, 2.2rem);
}

.pair h3 {
  font-size: clamp(1.3rem, 3vw, 1.75rem);
  letter-spacing: -0.03em;
  margin-bottom: 0.7rem;
}

.pair p {
  margin-bottom: 0;
  font-size: 0.98rem;
}

.pair .ne {
  color: var(--dim);
  font-weight: 400;
}

/* --- install -------------------------------------------------------------- */

.install-box {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--edge);
  border-radius: 5px;
  background: var(--pit);
  overflow: hidden;
  max-width: 34rem;
  font-family: var(--mono);
}

.install-box code {
  padding: 0.85rem 1rem;
  flex: 1;
  overflow-x: auto;
  white-space: nowrap;
  color: var(--bright);
  font-size: 0.9rem;
  background: none;
  border: 0;
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.install-box code .prompt {
  color: var(--dim);
  user-select: none;
}

.copy {
  border: 0;
  border-left: 1px solid var(--line);
  background: var(--raise);
  color: var(--dim);
  font-family: var(--mono);
  font-size: 0.76rem;
  padding-inline: 0.95rem;
  cursor: pointer;
  flex: none;
  min-width: 5.2rem;
  transition: color 120ms ease;
}

.copy:hover {
  color: var(--bright);
}

.copy[data-done='true'] {
  color: var(--allow);
}

.steps {
  counter-reset: step;
  list-style: none;
  padding: 0;
  margin: 2rem 0 0;
  display: grid;
  gap: 1.6rem;
}

.steps > li {
  counter-increment: step;
  display: grid;
  /* minmax(0, …) for the reason set out at .hero-grid: a bare 1fr is
     minmax(auto, 1fr), so the widest unbreakable token in the step text became
     the page's minimum width and the home page scrolled sideways on every phone
     size. This was the last bare 1fr in the file. */
  grid-template-columns: 1.8rem minmax(0, 1fr);
  gap: 1rem;
}

.steps > li::before {
  content: counter(step);
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--dim);
  border: 1px solid var(--line);
  border-radius: 50%;
  width: 1.7rem;
  height: 1.7rem;
  display: grid;
  place-items: center;
  margin-top: 0.1rem;
}

.steps h3 {
  margin-bottom: 0.3rem;
  font-size: 1rem;
}

.steps p {
  margin-bottom: 0;
  font-size: 0.96rem;
}

.steps figure.code {
  margin: 0.7rem 0 0;
}

/* --- fact grid ------------------------------------------------------------ */

.facts {
  display: grid;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow: hidden;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
}

.fact {
  background: var(--pit);
  padding: 1.1rem 1.15rem;
}

/*
 * Stacked panels for prose, sharing the hairline treatment of `.facts` without
 * being it.
 *
 * The six agent pages used `<ul class="facts">` for the "when the hook breaks"
 * block. `.facts` is a card grid whose entire appearance comes from children
 * carrying `.fact`; bare `<li>`s have no background and no padding, so the block
 * rendered as one flat slab of `--line` with the text against its borders, the
 * `.prose ul` indent showing as a bare stripe inside the left edge, and disc
 * markers floating in the previous column's text. Same shape as the `.install`
 * collision: one class, two kinds of element.
 *
 * A column rather than a grid because these are sentences of unequal length,
 * not stat tiles. The `.prose` half of the selector is load-bearing — `.prose ul`
 * is more specific than a lone class and would put the indent back.
 */
.failure-list,
.prose .failure-list {
  list-style: none;
  margin: 1.2rem 0;
  padding: 0;
  display: grid;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow: hidden;
}

.failure-list > li {
  background: var(--pit);
  padding: 0.95rem 1.15rem;
}

.failure-list > li em {
  color: var(--dim);
  font-style: normal;
  font-family: var(--mono);
  font-size: 0.8rem;
}

.fact dt {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  color: var(--dim);
  margin-bottom: 0.45rem;
}

.fact dd {
  margin: 0;
  font-family: var(--mono);
  font-size: 1.5rem;
  font-weight: 600;
  color: var(--bright);
  letter-spacing: -0.03em;
  line-height: 1.15;
}

.fact .sub {
  display: block;
  font-family: var(--sans);
  font-size: 0.82rem;
  font-weight: 400;
  color: var(--dim);
  letter-spacing: 0;
  margin-top: 0.35rem;
  line-height: 1.5;
}

/* --- support matrix ------------------------------------------------------- */

.support {
  display: grid;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: 6px;
  overflow: hidden;
}

.row {
  background: var(--pit);
  padding: 1rem 1.15rem;
  display: grid;
  gap: 0.35rem 1rem;
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 780px) {
  .row {
    grid-template-columns: 13rem minmax(0, 1fr);
    align-items: baseline;
  }
}

.row .who {
  font-family: var(--mono);
  font-weight: 600;
  color: var(--bright);
  font-size: 0.95rem;
  display: flex;
  align-items: center;
  gap: 0.55rem;
}

.row .how {
  font-size: 0.92rem;
  margin: 0;
  max-width: none;
}

.pip {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: none;
  background: var(--dim);
}

.pip[data-level='verified'] {
  background: var(--allow);
}

.pip[data-level='unverified'] {
  background: var(--ask);
}

.status-line {
  font-family: var(--mono);
  font-size: 0.76rem;
  color: var(--dim);
  margin: 0 0 0.3rem;
}

/* --- callout -------------------------------------------------------------- */

.callout {
  border: 1px solid var(--line);
  border-left: 2px solid var(--ask);
  border-radius: 0 5px 5px 0;
  background: var(--pit);
  padding: 1.1rem 1.25rem;
  margin: 1.8rem 0;
}

.callout p:last-child {
  margin-bottom: 0;
}

.callout .tag {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  color: var(--ask);
  display: block;
  margin-bottom: 0.5rem;
}

.callout[data-tone='deny'] {
  border-left-color: var(--deny);
}

.callout[data-tone='deny'] .tag {
  color: var(--deny);
}

/* --- docs layout ---------------------------------------------------------- */

.doc {
  display: grid;
  gap: clamp(2rem, 5vw, 3.5rem);
  grid-template-columns: minmax(0, 1fr);
  padding-block: clamp(2.5rem, 6vw, 4rem);
}

/*
 * The sidebar column only exists when there is a sidebar.
 *
 * `.doc` used to declare two columns unconditionally, so a page with no table
 * of contents put its article in the 15rem track and rendered as a narrow
 * ribbon with the rest of the screen empty. Mobile was fine, which is exactly
 * why it survived review — the layout is single-column there.
 */
@media (min-width: 1000px) {
  .doc--toc {
    grid-template-columns: 15rem minmax(0, 1fr);
    align-items: start;
  }
}

.toc {
  position: sticky;
  top: 5rem;
  font-family: var(--mono);
  font-size: 0.8rem;
  max-height: calc(100vh - 7rem);
  overflow-y: auto;
}

@media (max-width: 999px) {
  .toc {
    position: static;
    max-height: none;
    border: 1px solid var(--line);
    border-radius: 6px;
    padding: 1rem 1.1rem;
    background: var(--pit);
  }
}

.toc h2 {
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  color: var(--dim);
  font-weight: 400;
  margin-bottom: 0.9rem;
}

.toc ul {
  list-style: none;
  padding: 0;
  margin: 0 0 1.5rem;
}

.toc li {
  margin-bottom: 0.15rem;
}

.toc a {
  color: var(--dim);
  text-decoration: none;
  display: block;
  padding: 0.15rem 0;
  line-height: 1.45;
}

.toc a:hover {
  color: var(--bright);
}

.toc .lvl-3 {
  padding-left: 0.9rem;
  font-size: 0.95em;
}

.doc-body h2 {
  padding-top: 0.4em;
  border-top: 1px solid var(--line);
}

.doc-body h2:first-child {
  border-top: 0;
  padding-top: 0;
}

.source-note {
  font-family: var(--mono);
  font-size: 0.76rem;
  color: var(--dim);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 0.7rem 0.9rem;
  margin-bottom: 2rem;
  background: var(--pit);
}

/* --- docs index cards ----------------------------------------------------- */

/*
 * Separate cards rather than one gapless slab.
 *
 * The slab treatment used elsewhere needs a full last row; there are nine docs
 * and any column count leaves a hole, which reads as a broken card rather than
 * as empty space.
 */
.cards {
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}

.card {
  background: var(--pit);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 1.2rem 1.3rem;
  display: block;
  text-decoration: none;
  transition:
    background 120ms ease,
    border-color 120ms ease;
}

.card:hover {
  background: var(--raise);
  border-color: var(--edge);
  text-decoration: none;
}

.card h3 {
  margin-bottom: 0.4rem;
  font-size: 1rem;
}

.card p {
  margin: 0;
  font-size: 0.9rem;
  color: var(--dim);
}

.card .arrow {
  color: var(--dim);
}

.card:hover .arrow {
  color: var(--bright);
}

/* --- footer --------------------------------------------------------------- */

footer {
  border-top: 1px solid var(--line);
  padding-block: 3rem 4rem;
  font-size: 0.9rem;
}

.foot-grid {
  display: grid;
  gap: 2rem;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
}

footer h2 {
  font-size: 0.72rem;
  font-family: var(--mono);
  letter-spacing: 0.14em;
  color: var(--dim);
  font-weight: 400;
  margin-bottom: 0.8rem;
}

footer ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

footer li {
  margin-bottom: 0.4rem;
}

footer a {
  color: var(--body);
  text-decoration: none;
}

footer a:hover {
  color: var(--bright);
  text-decoration: underline;
}

.colophon {
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--dim);
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.4rem;
}

.colophon a {
  color: var(--dim);
}

/* --- motion --------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* --- print ---------------------------------------------------------------- */

@media print {
  .top,
  .toc,
  .replay,
  .copy,
  .picker {
    display: none;
  }

  body {
    background: #fff;
    color: #000;
  }
}

/* --- the compatibility matrix ---------------------------------------------
 *
 * A support table is structurally bad at bad news: a gap becomes an empty cell
 * and an empty cell reads as "fine". So every cell carries a word, and the
 * words that mean "this does not work" are given the same visual weight as the
 * ones that mean it does — no muted grey for the inconvenient half.
 *
 * Colour is never the only signal. `not seen` and `gated` differ in text
 * before they differ in hue, which matters on a page whose whole purpose is to
 * be read accurately.
 */

.matrix th[scope='row'] {
  white-space: normal;
  background: transparent;
  font-family: var(--sans);
  font-size: 0.95rem;
  color: var(--ink);
}

.matrix th[scope='row'] .ver {
  display: block;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--dim);
  letter-spacing: 0;
}

.matrix caption {
  caption-side: bottom;
  padding: 0.7rem 0.85rem;
  text-align: left;
  font-size: 0.82rem;
  color: var(--dim);
  border-top: 1px solid var(--line);
}

.cap {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.76rem;
  line-height: 1.5;
  padding: 0.1rem 0.42rem;
  border-radius: 3px;
  white-space: nowrap;
  border: 1px solid transparent;
}

.cap[data-tone='ok'] {
  color: var(--allow);
  border-color: color-mix(in srgb, var(--allow) 35%, transparent);
}

.cap[data-tone='warn'] {
  color: var(--ask);
  border-color: color-mix(in srgb, var(--ask) 35%, transparent);
}

.cap[data-tone='bad'] {
  color: var(--deny);
  border-color: color-mix(in srgb, var(--deny) 40%, transparent);
}

.cap[data-tone='unknown'] {
  color: var(--dim);
  border-color: var(--line);
  border-style: dashed;
}

.cap[data-tone='none'] {
  color: var(--dim);
  border-color: transparent;
}

/* The per-agent findings list. A marker in the gutter rather than a bullet, so
 * the shape of the list tells you how much is wrong before you read a word. */

ul.findings {
  list-style: none;
  padding-left: 0;
  margin: 1.2em 0;
}

ul.findings li {
  position: relative;
  padding-left: 1.4rem;
  margin: 0.5em 0;
}

ul.findings li::before {
  position: absolute;
  left: 0;
  font-family: var(--mono);
}

ul.findings li[data-tone='ok']::before {
  content: '\2713';
  color: var(--allow);
}

ul.findings li[data-tone='warn']::before {
  content: '!';
  color: var(--ask);
}

ul.findings li[data-tone='bad']::before {
  content: '\2717';
  color: var(--deny);
}

/* --- the Agents reference -------------------------------------------------
 *
 * These pages carry two verdicts about every agent at once: how much of a
 * decision survives the trip through it, and what has actually been run to
 * establish that. The styling exists to keep those two readable as separate
 * claims, because the whole point of the pages is that they are not the same
 * question and one is routinely mistaken for the other.
 *
 * Every value carries its evidence grade beside it, smaller and dimmer but
 * never invisible. The first version shipped with no rule for that element at
 * all, so `degrades` and `probe` rendered as `degradesprobe` — which is not a
 * cosmetic problem: an evidence grade nobody can pick out of the word beside
 * it is an evidence grade nobody reads.
 */

.badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0 0 1.6rem;
}

.badge {
  display: inline-block;
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.5;
  letter-spacing: 0.02em;
  padding: 0.22rem 0.55rem;
  border-radius: 3px;
  white-space: nowrap;
  border: 1px solid var(--line);
  color: var(--dim);
}

.badge[data-tone='ok'] {
  color: var(--allow);
  border-color: color-mix(in srgb, var(--allow) 38%, transparent);
}

.badge[data-tone='warn'] {
  color: var(--ask);
  border-color: color-mix(in srgb, var(--ask) 38%, transparent);
}

.badge[data-tone='bad'] {
  color: var(--deny);
  border-color: color-mix(in srgb, var(--deny) 42%, transparent);
}

.badge[data-tone='info'] {
  color: var(--dim);
  border-color: var(--line);
}

/* A verdict cell: the word, then how it was established. Laid out as a column
 * so the grade never runs into the value on a narrow screen, which is exactly
 * how the two got welded together in the first place. */

.matrix td.v {
  white-space: nowrap;
}

.matrix td.v span {
  display: block;
  font-family: var(--mono);
  font-size: 0.82rem;
}

.matrix td.v em {
  display: block;
  margin-top: 0.15rem;
  font-family: var(--mono);
  font-style: normal;
  font-size: 0.66rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--dim);
  cursor: help;
}

.matrix td.v[data-tone='ok'] span {
  color: var(--allow);
}

.matrix td.v[data-tone='warn'] span {
  color: var(--ask);
}

.matrix td.v[data-tone='bad'] span {
  color: var(--deny);
}

.matrix td.n {
  font-size: 0.82rem;
  color: var(--body);
  white-space: normal;
  min-width: 16rem;
}

.matrix td.u,
.doc-body .u {
  color: var(--dim);
}

.matrix.install td .sub {
  display: block;
  margin-top: 0.2rem;
  font-family: var(--mono);
  font-size: 0.68rem;
  color: var(--dim);
  white-space: normal;
}

/* What has and has not been run. A run that did not happen states why, and
 * that sentence is usually the most useful thing on the page — Cursor's says a
 * human has to drive a GUI, which tells you the gap will not close by waiting.
 * So it is not styled as an absence. */

ul.runs {
  list-style: none;
  padding-left: 1.4rem;
  margin: 1.2em 0;
}

ul.runs li {
  position: relative;
  margin: 0.6em 0;
  line-height: 1.55;
}

ul.runs li::before {
  position: absolute;
  left: -1.4rem;
  font-family: var(--mono);
}

ul.runs li[data-done='yes']::before {
  content: '\2713';
  color: var(--allow);
}

ul.runs li[data-done='no']::before {
  content: '\00b7';
  color: var(--ask);
}

ul.runs li.u::before {
  content: '?';
  color: var(--dim);
}

ul.runs .where {
  display: block;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--dim);
  margin-top: 0.15rem;
}

/* The sharpest edge of each agent, carried onto the index. A support table
 * read on its own is where somebody decides an integration is complete. */

ul.limits {
  list-style: none;
  padding-left: 0;
  margin: 1.2em 0;
}

ul.limits li {
  margin: 0.7em 0;
  padding-left: 0.9rem;
  border-left: 2px solid var(--line);
  line-height: 1.55;
}

dl.grades {
  margin: 1.2em 0;
}

dl.grades dt {
  font-family: var(--mono);
  font-size: 0.76rem;
  letter-spacing: 0.03em;
  margin-top: 0.9em;
}

dl.grades dt[data-tone='ok'] {
  color: var(--allow);
}

dl.grades dt[data-tone='warn'] {
  color: var(--ask);
}

dl.grades dt[data-tone='bad'] {
  color: var(--deny);
}

dl.grades dd {
  margin: 0.2em 0 0;
  padding-left: 0;
  color: var(--body);
}

@media (max-width: 640px) {
  .matrix td.n {
    min-width: 0;
  }
}

/*
 * The evidence marker beside a capability cell.
 *
 * The compatibility page promises that every claim carries how it was
 * established. It now does, which means 81 more marks on one table — so they
 * are deliberately quiet: mono, small, --dim, and on their own line under the
 * label rather than beside it, so the row still scans as a matrix.
 */
.cap + .ev {
  display: block;
  margin-top: 0.15rem;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.04em;
  color: var(--dim);
}
