/* patentcontinuation.com
 *
 * Rebuilt 2026-08-15. The first version was a mobile-first document stylesheet
 * that never got a desktop design: a 42rem column on a 1280px viewport left most
 * of the screen empty, every block had the same rhythm, and the brand palette
 * this repo had just written a conventions document about went unused.
 *
 * Brand values are verbatim from docs/conventions/ui-ux.md, which records where the
 * canonical palette lives. That citation stays in docs/ and not in this file:
 * THIS STYLESHEET IS SERVED TO EVERY VISITOR, so a comment naming a private
 * sibling repo is a live leak. Component code uses the SEMANTIC names below, never
 * the raw --brand-* values.
 *
 * The one recorded divergence still holds: brand cyan measures about 2.05:1 on
 * white, under the 4.5:1 AA floor, so cyan is for SURFACES, focus rings and the
 * glow. Accent TEXT uses the darker derived cyan.
 *
 * Self-hosted only. No CDN font, no icon set, no third-party request of any kind:
 * the CSP in main.go enforces that rather than trusting it.
 */

/* Brand type, self-hosted. Inter for body and UI, Satoshi for display and headings,
 * both upright variable only: the italic variants are another 432 KB for a surface
 * that sets no italic display type. font-src 'self' in main.go's CSP already
 * anticipated these, so no policy change is needed. The internal provenance of the
 * files is recorded in docs/conventions/ui-ux.md, not here: THIS FILE IS SERVED TO
 * THE PUBLIC, so a comment naming a private sibling repo is a live leak, which is
 * exactly what this one was until 2026-08-15. */
@font-face {
  font-family: "Inter";
  src: url("/static/fonts/InterVariable.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Satoshi";
  src: url("/static/fonts/Satoshi-Variable.woff2") format("woff2-variations");
  font-weight: 300 900;
  font-style: normal;
  font-display: swap;
}

:root {
  --font-sans: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-display: "Satoshi", "Inter", ui-sans-serif, system-ui, sans-serif;

  /* Brand, verbatim. Do not adjust or re-sample. */
  --brand-black: #111111;
  --brand-white: #ffffff;
  --brand-cyan: #2dc5e8;
  --brand-purple: #4a3f78;
  --brand-purple-light: #bfa8ff;
  --brand-rose: #d4637a;
  --brand-gray-light: #e5e5e5;
  --brand-gray-bg: #f7f8fa;

  /* Semantic. Use these in component code. */
  --bg-canvas: var(--brand-white);
  --bg-subtle: var(--brand-gray-bg);
  --bg-inverse: var(--brand-black);
  --fg-default: var(--brand-black);
  --fg-muted: #55606c;
  --fg-inverse: #f2f5f7;
  --fg-on-accent: #06222b;
  --border-subtle: var(--brand-gray-light);
  --border-strong: #cdd4db; /* DECORATIVE only: dividers, dots, rules. Not a control boundary. */
  /* --border-control is the visual boundary of an interactive control, which WCAG
     2.2 SC 1.4.11 holds to 3:1 against its background. --border-strong is 1.50:1 on
     white and was being used for .btn-secondary's outline, which made the secondary
     button's boundary effectively invisible as a control. Caught by
     contrast_test.go, not by eye. */
  --border-control: #7d8792; /* 3.65:1 on canvas, 3.44:1 on the subtle surface */
  --accent-default: #0e7a96; /* accent TEXT: 4.95:1 on white. See divergence. */
  --accent-surface: var(--brand-cyan);
  --accent-deep: var(--brand-purple);
  /* Footer links. The brand assigns brand-rose (#d4637a) this role, and adopting it
     verbatim on a light ground gives 3.37:1, under the 4.5 floor: the SAME mistake
     the cyan divergence already documents, in a second role. Darkened here, and
     brand-rose is kept for the surfaces and tags where it is not body-weight text.
     On the dark ground brand-rose itself clears the floor, exactly as cyan does, so
     the divergence is once again light-theme-only. */
  --footer-link: #a8455c; /* 5.38:1 on the subtle surface */

  --radius-sm: 4px;
  --radius-md: 6px;
  --radius-lg: 8px;
  --radius-xl: 12px;

  --shell: 74rem; /* full-width section inner bound */
  --prose: 38rem; /* reading measure, narrower on purpose */
  /* THE MIDDLE TIER, named 2026-08-30. It already existed twice as a hardcoded 46rem
     (.steps, footer .maturity), so this formalises a convention rather than inventing a
     number. It is for supporting text: caveats, methodology notes, export limitations.
     Those are small and muted, which is the worst combination to read at 74rem, and
     .small carried no measure of its own, so any note not nested inside .prose or
     .band-head ran the full shell width. */
  --note: 46rem;  /* supporting notes: wider than prose, far short of the shell */

  --step-0: 1.0625rem;
  --step-1: 1.25rem;
  --step-2: 1.5rem;
  --step-3: clamp(1.6rem, 1.2rem + 1.6vw, 2.1rem);
  --step-4: clamp(2.2rem, 1.5rem + 3.2vw, 3.6rem);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg-canvas: #0d1319;
    --bg-subtle: #141d26;
    --bg-inverse: #060a0e;
    --fg-default: #e9eff4;
    --fg-muted: #9fb1c0;
    --border-subtle: #26323e;
    --border-strong: #35434f;
    --border-control: #5d6d78; /* 3.49:1 on the dark canvas, 3.18:1 on the subtle surface */
    /* Brand rose clears 4.5:1 on the dark ground unchanged, so the light-theme
       darkening above is not carried here. */
    --footer-link: var(--brand-rose);
    /* On a dark ground the brand cyan clears AA comfortably, so it is the accent
       here. The divergence is a light-theme problem only. */
    --accent-default: #5ad4f0;
  }
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg-canvas);
  color: var(--fg-default);
  font: var(--step-0)/1.6 var(--font-sans);
  text-rendering: optimizeLegibility;
}

.skip { position: absolute; left: -9999px; }
.skip:focus {
  left: 1rem; top: 1rem; padding: .6rem 1rem; z-index: 20;
  background: var(--accent-surface); color: var(--fg-on-accent); border-radius: var(--radius-md);
}

/* ---------- shell ---------- */

.wrap { width: 100%; max-width: var(--shell); margin: 0 auto; padding: 0 1.5rem; }
.prose { max-width: var(--prose); }

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

header.site {
  position: sticky; top: 0; z-index: 10;
  background: color-mix(in srgb, var(--bg-canvas) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border-subtle);
}
header.site .wrap {
  display: flex; align-items: center; gap: 1rem 2rem; flex-wrap: wrap;
  min-height: 4rem;
}
.brand {
  display: inline-flex; align-items: center; gap: .55rem;
  font-weight: 700; letter-spacing: -.01em; color: var(--fg-default); text-decoration: none;
  margin-right: auto;
}
.brand img { width: 1.5rem; height: 1.5rem; display: block; }
header.site nav { display: flex; flex-wrap: wrap; gap: 1.5rem; }
/* The 44px minimum is the accessibility floor in docs/conventions/ui-ux.md item 4,
   and the header was the surface that never got it: measured on production at 375px,
   every header link was 33px tall. The footer nav was built to 44px when it was added
   and the header was not revisited, which is a rule applied at the instance that
   prompted it rather than across the set it governs.

   inline-flex with min-height rather than vertical padding, so the TARGET grows
   without moving the text or changing the header's visual rhythm on desktop, where
   the wrap already has min-height 4rem. */
header.site nav a {
  color: var(--fg-muted); text-decoration: none; font-size: .97rem;
  padding: .25rem 0; border-bottom: 2px solid transparent;
  display: inline-flex; align-items: center; min-height: 44px;
}
header.site nav a:hover, header.site nav a:focus-visible {
  color: var(--fg-default); border-bottom-color: var(--accent-surface);
}

/* ---------- type ---------- */

/* Satoshi carries the display voice; the tight tracking below is part of it and
   should not be loosened. Body and UI stay on Inter via the body rule. */
h1, h2, h3 { font-family: var(--font-display); }
h1 { font-size: var(--step-4); line-height: 1.05; letter-spacing: -.025em; margin: 0 0 1rem; font-weight: 800; }
h2 { font-size: var(--step-3); line-height: 1.15; letter-spacing: -.018em; margin: 0 0 .75rem; font-weight: 700; }
h3 { font-size: var(--step-1); line-height: 1.25; margin: 0 0 .4rem; font-weight: 650; }
p { margin: 0 0 1rem; }
.lede { font-size: var(--step-1); line-height: 1.5; color: var(--fg-muted); }
.small { font-size: .92rem; color: var(--fg-muted); max-width: var(--note); }
.eyebrow {
  display: inline-block; font-size: .8rem; font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: var(--accent-default); margin-bottom: .9rem;
}
a { color: var(--accent-default); text-underline-offset: 2px; }

/* ---------- sections ---------- */

section.band { padding: 4.5rem 0; }
section.band + section.band { border-top: 1px solid var(--border-subtle); }
section.band.tint { background: var(--bg-subtle); }
.band-head { margin-bottom: 2rem; max-width: var(--prose); }

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

.hero {
  padding: 4.5rem 0 4rem;
  background:
    radial-gradient(60rem 28rem at 78% -12%, color-mix(in srgb, var(--accent-surface) 17%, transparent), transparent 70%),
    radial-gradient(44rem 24rem at 4% 8%, color-mix(in srgb, var(--accent-deep) 11%, transparent), transparent 70%);
}
.hero-grid { display: grid; gap: 3rem; align-items: start; }
@media (min-width: 62rem) {
  .hero-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr); gap: 4rem; align-items: center; }
}

/* ---------- buttons ---------- */

.cta-row { display: flex; flex-wrap: wrap; gap: .75rem; margin: 1.75rem 0 1rem; }
/* The top-of-funnel entry, above .cta-row rather than in it. A third .btn would have
   changed which control is first, and that ordering is another plan's shipped decision;
   this adds a stage in front of the row without touching it. */
.cta-lead {
  margin: 1.6rem 0 0; padding: .8rem 1rem; font-size: .98rem;
  background: var(--bg-subtle); border: 1px solid var(--border-subtle);
  border-left: 3px solid var(--accent-surface); border-radius: var(--radius-lg);
}
.btn {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .8rem 1.4rem; border-radius: var(--radius-lg);
  font-weight: 650; font-size: 1.02rem; text-decoration: none; border: 1px solid transparent;
  transition: transform .12s ease, box-shadow .12s ease, background .12s ease;
}
.btn-primary {
  background: var(--bg-inverse); color: var(--fg-inverse);
  box-shadow: 0 1px 2px rgba(17, 17, 17, .18);
}
.btn-primary:hover { transform: translateY(-1px); box-shadow: 0 6px 20px -8px var(--accent-surface); }
.btn-secondary { background: transparent; color: var(--fg-default); border-color: var(--border-control); }
.btn-secondary:hover { border-color: var(--accent-surface); background: color-mix(in srgb, var(--accent-surface) 8%, transparent); }

/* ---------- the output sample: the product, shown ---------- */

.sample {
  border: 1px solid var(--border-subtle); border-radius: var(--radius-xl);
  background: var(--bg-canvas); overflow: hidden;
  box-shadow: 0 24px 60px -32px rgba(17, 17, 17, .38);
}
.sample-bar {
  display: flex; align-items: center; gap: .5rem;
  padding: .7rem 1rem; border-bottom: 1px solid var(--border-subtle);
  background: var(--bg-subtle); font-size: .82rem; color: var(--fg-muted);
}
.dot { width: .6rem; height: .6rem; border-radius: 50%; background: var(--border-strong); }
.sample-body { padding: 1.1rem 1.2rem; display: grid; gap: .9rem; }
.sample-step { display: grid; gap: .3rem; }
.sample-label {
  font-size: .74rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--fg-muted);
}
.claim {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .86rem; line-height: 1.55;
  background: var(--bg-subtle); border-left: 3px solid var(--border-strong);
  padding: .7rem .85rem; border-radius: 0 var(--radius-md) var(--radius-md) 0;
}
.claim.out { border-left-color: var(--accent-surface); }
.flag {
  display: grid; gap: .25rem;
  border-left: 3px solid var(--brand-rose);
  background: color-mix(in srgb, var(--brand-rose) 8%, transparent);
  padding: .7rem .85rem; border-radius: 0 var(--radius-md) var(--radius-md) 0;
  font-size: .88rem;
}
.flag-tag { font-weight: 700; color: var(--brand-rose); font-size: .78rem; letter-spacing: .04em; }

/* ---------- card grids ---------- */

.grid { display: grid; gap: 1.25rem; }
.grid-3 { grid-template-columns: 1fr; }
@media (min-width: 44rem) { .grid-3 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 66rem) { .grid-3 { grid-template-columns: repeat(3, 1fr); } }
/* Two-up, for the five tier cards on the practice hub. Deliberately not grid-3: a
   tier card carries a sentence rather than a label, and three columns of prose at this
   measure is unreadable. Five cards over two columns also leaves the last one wide,
   which is where Tier E sits and reads as a footnote, which it roughly is. */
.grid-2 { grid-template-columns: 1fr; }
@media (min-width: 50rem) { .grid-2 { grid-template-columns: repeat(2, 1fr); } }

.card {
  border: 1px solid var(--border-subtle); border-radius: var(--radius-xl);
  padding: 1.4rem 1.5rem; background: var(--bg-canvas);
  transition: border-color .12s ease, transform .12s ease;
}
.card:hover { border-color: var(--brand-purple-light); transform: translateY(-2px); }
/* A CARD IS A LINK WRAPPING A BLOCK OF PROSE, and until 2026-08-17 every word of that
   prose rendered underlined and in the accent colour. `a` sets a colour and never resets
   text-decoration, and `.card` never overrode it, so the browser default underlined the
   title, the meta line and the summary alike. It was live on production and had been
   since the corpus shipped.

   Found while rebuilding the hub rather than by review, and the reason is worth the
   line: the defect was survivable when a card carried a one-line summary and became
   obvious when phase 1 gave each card a full sentence. A readability problem scales with
   the content, so the change that made the page better is the change that surfaced it.

   The title keeps the accent colour and gains an underline on hover, so the affordance
   is still there; the body text stops shouting. */
.card { color: var(--fg-default); text-decoration: none; }
.card h3 { display: flex; align-items: baseline; gap: .5rem; color: var(--accent-default); }
.card:hover h3 { text-decoration: underline; }
.card p:last-child { margin-bottom: 0; }
/* .card-meta WAS USED BY THE MARKUP AND DEFINED BY NOTHING, on every record card since
   the corpus shipped. Found 2026-08-17 while rebuilding the hub, by checking a class
   name I was about to reuse. Undefined, it inherited the enclosing anchor's colour and
   the body size, so a card's subtitle rendered as full-size cyan link text competing
   with the title above it. A dangling class is invisible in review because the markup
   reads exactly as intended; only the rendered page disagrees. */
.card-meta { font-size: .88rem; color: var(--fg-muted); margin-bottom: .6rem; }
.card-rule { width: 2.2rem; height: 3px; border-radius: 2px; background: var(--accent-surface); margin-bottom: .9rem; }

/* ---------- tier cards ---------- */

.tiers { display: grid; gap: 1.25rem; grid-template-columns: 1fr; }
@media (min-width: 50rem) { .tiers { grid-template-columns: repeat(2, 1fr); } }
.tier { display: flex; flex-direction: column; }
.tier .machine { font-size: .85rem; color: var(--fg-muted); font-weight: 600; letter-spacing: .02em; }
.tier .model {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 1.22rem; font-weight: 700; margin: .2rem 0 .9rem; color: var(--fg-default);
}
.figs { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: .9rem; }
.fig {
  border: 1px solid var(--border-subtle); border-radius: 999px;
  padding: .22rem .7rem; font-size: .82rem; color: var(--fg-muted);
}
.fig strong { color: var(--fg-default); }
.fig.warn { border-color: var(--brand-rose); color: var(--brand-rose); }
.fig.warn strong { color: var(--brand-rose); }
.tier .why { flex: 1; }
.tier .go { margin-top: 1rem; }

/* ---------- steps ---------- */

/* minmax(0, 1fr) RATHER THAN 1fr, ON BOTH GRIDS, AND THE ZERO IS THE ENTIRE FIX.
   A grid item defaults to min-width: auto, so a track sized `1fr` still refuses to go
   below its content's min-content width. One <pre> here has a min-content of 586px, so
   the track grew to 637px inside a 342px container and dragged every sibling row with
   it: on /remote the document reached 661px against a 390px viewport, on /download 556px.
   The step that visibly overflowed contained no code at all, which is why this reads as
   a mystery from the markup; it was sized by a code block three rows away.
   The <pre> already had overflow-x: auto and it could not help, because the blowout was
   in the TRACK and not in the block. Found 2026-08-30 by an external review, reproduced
   at 390px, and pre-dating the OS-aware command work. Not media-queried: a track that
   cannot shrink is wrong at every width, and it happened to be survivable above 700px. */
.steps { counter-reset: step; display: grid; gap: 1.5rem; max-width: var(--note); grid-template-columns: minmax(0, 1fr); }
.step { counter-increment: step; display: grid; grid-template-columns: 2.25rem minmax(0, 1fr); gap: 1rem; align-items: start; }
.step > * { min-width: 0; }
.step::before {
  content: counter(step);
  width: 2.25rem; height: 2.25rem; border-radius: 50%;
  display: grid; place-items: center; font-weight: 700; font-size: .95rem;
  background: var(--accent-surface); color: var(--fg-on-accent);
}
.step > div > :last-child { margin-bottom: 0; }

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

code, pre { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
code { background: var(--bg-subtle); padding: .1em .35em; border-radius: var(--radius-sm); font-size: .9em; }
pre {
  background: var(--bg-inverse); color: var(--fg-inverse);
  border-radius: var(--radius-lg); padding: .95rem 1.1rem; overflow-x: auto;
  font-size: .87rem; line-height: 1.6; margin: .6rem 0 0;
}
pre code { background: none; padding: 0; color: inherit; }

/* ---------- notes, tables, lists ---------- */

.note {
  border: 1px solid var(--border-subtle); border-left: 3px solid var(--accent-surface);
  background: var(--bg-subtle); border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
  padding: 1.2rem 1.4rem;
}
.note h2, .note h3 { margin-top: 0; }
/* A CALLOUT OR CARD HEADING KEEPS ITS SIZE AT ANY LEVEL. Eleven pages went h1 straight
   to h3 because these were marked up at the level that looked right rather than the one
   that nests right; an external review found it 2026-08-30. The levels are corrected in
   the templates and the appearance is held here, so the fix is semantic only and nothing
   moves on the page. */
.note h2, .card h2 { font-size: var(--step-1); line-height: 1.25; margin: 0 0 .4rem; font-weight: 650; letter-spacing: 0; }
.card h2 { display: flex; align-items: baseline; gap: .5rem; color: var(--accent-default); }
.card:hover h2 { text-decoration: underline; }
.note :last-child { margin-bottom: 0; }
.warnbox { border-left-color: var(--brand-rose); background: color-mix(in srgb, var(--brand-rose) 7%, transparent); }

table { border-collapse: collapse; width: 100%; font-size: .94rem; }
th, td { text-align: left; padding: .65rem .7rem; border-bottom: 1px solid var(--border-subtle); }
th { font-weight: 650; color: var(--fg-muted); font-size: .84rem; letter-spacing: .04em; text-transform: uppercase; }
/* A SCROLLABLE TABLE THAT LOOKS LIKE A COMPLETE ONE IS THE DEFECT, not the scrolling.
   At 390px the measured table shows three of its six columns and nothing said so, which
   an external review found on 2026-08-30. The columns were reordered so the ones that
   decide are visible; this is the second half, telling a reader the rest exist.

   Pure CSS, because the site ships no JavaScript. Two gradients per edge: a solid one
   attached to the LOCAL background, which scrolls with the content and so covers the
   shadow when that edge is reached, and a shadow attached to SCROLL, which stays put.
   The shadow is therefore visible exactly when there is more to see in that direction
   and disappears at each end. Roma Komarov's technique, no script and no state. */
.table-scroll {
  overflow-x: auto;
  background:
    linear-gradient(to right, var(--bg-canvas) 40%, rgba(255,255,255,0)) left center / 2.5rem 100% no-repeat local,
    linear-gradient(to left,  var(--bg-canvas) 40%, rgba(255,255,255,0)) right center / 2.5rem 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, rgba(17,17,17,.16), rgba(17,17,17,0)) left center / .9rem 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, rgba(17,17,17,.16), rgba(17,17,17,0)) right center / .9rem 100% no-repeat scroll;
}

/* CONTENTS ON AN AUTHORITY RECORD. Compact on purpose: it is a map, not a section, and
   it sits on a page that runs to fifteen thousand pixels. Numbered because the sections
   of a record are read in order, which is the argument for the record's structure in the
   first place. */
.toc { margin: 0 0 2rem; padding: .9rem 1.1rem; border-left: 3px solid var(--border-strong);
       background: var(--bg-subtle); border-radius: 0 var(--radius-md) var(--radius-md) 0; }
.toc-label { font-size: .74rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
             color: var(--fg-muted); margin: 0 0 .5rem; }
.toc ol { margin: 0; padding-left: 1.1rem; display: grid; gap: .3rem; font-size: .95rem; }
.toc a { color: var(--fg-default); }
/* A jumped-to heading must not land under the sticky header, and must be visible as the
   thing that was jumped to. */
h2[id] { scroll-margin-top: 5rem; }

ul.plain { list-style: none; padding: 0; margin: 0; display: grid; gap: .5rem; }
ul.downloads { list-style: none; padding: 0; display: grid; gap: .5rem; }
ul.downloads li {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: .5rem 1rem;
  padding: .85rem 1rem; border: 1px solid var(--border-subtle); border-radius: var(--radius-lg);
}
ul.downloads li:hover { border-color: var(--accent-surface); }
ul.downloads a { font-weight: 650; }
ul.downloads code { margin-left: auto; color: var(--fg-muted); font-size: .8rem; }

/* ---------- small layout utilities ----------
 *
 * These exist because the CSP forbids inline styles, deliberately. An earlier
 * revision used style="margin-top:1rem" attributes and the browser blocked every
 * one of them: the policy this site ships was blocking the site's own markup.
 * Utilities in the stylesheet are the fix; relaxing style-src to 'unsafe-inline'
 * would have been the other one, and it would have re-opened the hole the policy
 * exists to close.
 */

.note-after { margin-top: 1rem; }
.section-gap { margin-top: 2rem; }
.sample-title { margin-left: .5rem; }

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

footer.site { border-top: 1px solid var(--border-subtle); background: var(--bg-subtle); padding: 3rem 0; }
footer.site .maturity { max-width: var(--note); font-size: .95rem; color: var(--fg-muted); }
footer.site .maturity strong { color: var(--fg-default); }
footer.site a { color: var(--footer-link); }
/* The footer had the disclaimer and nothing else, so the bottom of a long page was
   a dead end. Targets are padded to clear the 44px minimum with room to spare. */
.footer-nav {
  display: flex; flex-wrap: wrap; gap: .25rem 1.5rem;
  margin-top: 1.75rem; padding-top: 1.5rem;
  border-top: 1px solid var(--border-subtle);
}
.footer-nav a { font-size: .92rem; display: inline-flex; align-items: center; min-height: 44px; }

/* Small screens: the nav wrapped to a second line and pushed the hero ~275px
   down on a 390px viewport, which is a lot of the fold spent on chrome. */
@media (max-width: 34rem) {
  header.site .wrap { min-height: 0; padding-top: .6rem; padding-bottom: .6rem; gap: .5rem 1rem; }
  .brand { font-size: .98rem; margin-right: 0; width: 100%; }
  header.site nav { gap: .5rem 1.1rem; width: 100%; }
  header.site nav a { font-size: .9rem; }
  .hero { padding: 2.5rem 0 3rem; }
  section.band { padding: 3rem 0; }

  /* THE HERO SAMPLE COSTS 710px OF A 1596px HERO ON A 375px SCREEN, measured against
     production on 2026-08-30, which puts the first content section 2.1 screens down.
     Tightened rather than collapsed behind a <details>: the four steps ARE the
     demonstration, the closing note is the honesty disclaimer that
     TestTheHomeSampleIsLabelledOrReal pins, and this site ships no JavaScript, so a
     disclosure widget would have to fight the browser's own details styling to stay
     open on desktop. Padding and leading are free; content is not. */
  .sample-body { padding: .85rem .9rem; gap: .65rem; }
  .sample-step { gap: .2rem; }
  .claim, .flag { padding: .55rem .7rem; line-height: 1.45; }
  .sample-bar { padding: .55rem .85rem; }

  /* THE BUTTONS COME STRAIGHT AFTER THE LEDE ON A PHONE, and the course offer follows
     them instead of preceding them.

     Measured against production on 2026-08-30 at 375px: the hero carried 246px of
     supporting prose around a 54px button row, 128px of it BEFORE the row. Reordering
     moves the buttons up 142px, from 668 to 526, which on a real handset is the
     difference between the primary action sitting at the fold and sitting comfortably
     above it. Nothing is deleted; all three blocks earn their place and each was added
     to close a real gap.

     IT IS MOBILE ONLY, AND THAT IS THE WHOLE JUSTIFICATION. home.html records a
     deliberate decision that the course offer sits ABOVE the row, taken so a third
     button would not reorder what comes first. On desktop the column is read at a
     glance and that decision stands untouched. On a phone the same markup is a queue,
     and putting a "not ready to install?" aside in front of the install buttons delays
     the thing most visitors came for. Different medium, different order.

     Costs 11px: flex ends margin collapsing between these children. Worth 142px. */
  .hero-grid > div:first-child { display: flex; flex-direction: column; }
  .hero-grid > div:first-child > .eyebrow  { order: 1; }
  .hero-grid > div:first-child > h1        { order: 2; }
  .hero-grid > div:first-child > .lede     { order: 3; }
  .hero-grid > div:first-child > .cta-row  { order: 4; }
  .hero-grid > div:first-child > .cta-lead { order: 5; }
  .hero-grid > div:first-child > .small    { order: 6; }
}

:focus-visible {
  /* --accent-default, NOT --accent-surface. WCAG 2.2 SC 1.4.11 wants 3:1 for a
     focus indicator against what it sits on, and brand cyan is 2.05:1 on the light
     canvas: the same ratio that disqualified it for text. The conventions file used
     to list focus rings alongside surfaces as a cyan role; it was corrected on
     2026-08-15 and this is the fix. Dark theme is unaffected either way (cyan runs
     about 9:1 on #0d1319), but one token that is correct in both themes beats two. */
  outline: 3px solid var(--accent-default);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
  .btn:hover, .card:hover { transform: none; }
}

/* Label for a figure group. Two studies sit on the home page and running their
   figures together implied one study with the wrong n; the labels are what keep
   them apart. */
.figs-label {
  font-size: .78rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--fg-muted); margin: 1.5rem 0 .6rem;
}
.figs-label:first-of-type { margin-top: 0; }

/* Current-page indication. Colour alone is not an indicator: the underline carries it
   for anyone who cannot distinguish the two colours, and aria-current carries it for
   anyone not looking at all. */
header.site nav a.here {
  color: var(--fg-default);
  border-bottom: 2px solid var(--accent-default);
  padding-bottom: 2px;
}

/* /sample's provenance list. A definition list is the right element for
   label/value pairs and the default rendering is an indented block, which reads
   as an afterthought next to the claims above it. */
dl.meta {
  display: grid; grid-template-columns: max-content 1fr; gap: .35rem 1.25rem;
  margin: 1rem 0 1.5rem; font-size: .93rem;
}
dl.meta dt { font-weight: 650; color: var(--fg-muted); }
dl.meta dd { margin: 0; }
@media (max-width: 34rem) { dl.meta { grid-template-columns: 1fr; gap: .1rem .5rem; } }
.sample-note { font-size: .88rem; color: var(--fg-muted); margin: .35rem 0 0; }

/* The measured time cost at the top of a lesson and on the course index. Quiet on
   purpose: it is a price tag, not a claim, and it sits directly under a lede it must
   not compete with. See site/reading.go. */
.length { color: var(--muted); margin-top: .35rem; }

/* The stateless self-check at the foot of every lesson. See learn.go's lessonChecks:
   retrieval practice with the answer disclosed, no score, no state. Styled as a quiet
   aside rather than a quiz, because the audience is expert and a quiz-shaped box reads
   as remedial. */
.self-check { border-left: 3px solid var(--rule); padding: .1rem 0 .1rem 1rem; margin: 2rem 0; }
.self-check summary { cursor: pointer; color: var(--link); }
.self-check details[open] summary { margin-bottom: .5rem; }

/* /models memory filter, CSS-only. The control is a radio group because a range
   input cannot drive CSS state and this page carries no JavaScript by design
   (site_test.go bans every script tag; the CSP is script-src 'self').

   The selector reaches the table from the fieldset via the general sibling
   combinator, so the markup order fieldset -> table is load-bearing. Rows are
   tagged .gb-<n> from Entry.MinGB, which is derived from the same "runs on" prose
   the findings document publishes, so the filter cannot disagree with the column
   beside it. .gb-0 is cloud-only and stays hidden at every stop: no amount of
   local memory runs those, which is a different statement from "needs more". */
.mem-filter {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: 1rem 1.1rem .9rem;
  margin: 0 0 1.1rem;
}
.mem-filter legend {
  font-size: .82rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--fg-muted); padding: 0 .4rem;
}
.mem-stops { display: flex; flex-wrap: wrap; gap: .4rem; }
.mem-radio { position: absolute; opacity: 0; width: 0; height: 0; }
.mem-stops label {
  display: inline-block; cursor: pointer; user-select: none;
  border: 1px solid var(--border-strong); border-radius: var(--radius-full);
  padding: .3rem .85rem; font-size: .9rem; font-variant-numeric: tabular-nums;
  transition: background var(--dur, 120ms), color var(--dur, 120ms);
}
.mem-stops label::after { content: " GB"; opacity: .55; font-size: .8em; }
.mem-stops label.mem-all::after { content: ""; }
.mem-stops label.mem-all { font-weight: 600; }
.mem-radio:checked + label {
  background: var(--accent-default); color: var(--bg-canvas);
  border-color: var(--accent-default); font-weight: 600;
}
/* The ring uses --accent-default, not --accent-surface: brand cyan is 2.05:1 on
   the light canvas, under the 3:1 that WCAG 2.2 SC 1.4.11 asks of a focus
   indicator. Same rule as :focus-visible in this file. */
.mem-radio:focus-visible + label { outline: 3px solid var(--accent-default); outline-offset: 2px; }
.mem-note { margin: .6rem 0 0; color: var(--fg-muted); }

/* One block per stop: hide every tier above it, plus cloud-only.

   :has() ON THE ROOT, NOT A SIBLING COMBINATOR. The first version of this used
   `#mem-64:checked ~ .table-scroll`, which cannot match: the radios sit inside the
   fieldset and the table is the fieldset's sibling, not theirs, so ~ never reaches
   it. The tests still passed, because nothing rendered checks that a CSS rule
   SELECTS anything. Anchoring on :root:has(...) makes the rule independent of
   markup order, which is also what stops a future template edit from silently
   breaking the filter. */
:root:has(#mem-16:checked) #measured-table tbody tr.gb-32,
:root:has(#mem-16:checked) #measured-table tbody tr.gb-64,
:root:has(#mem-16:checked) #measured-table tbody tr.gb-96,
:root:has(#mem-16:checked) #measured-table tbody tr.gb-128,
:root:has(#mem-16:checked) #measured-table tbody tr.gb-192,
:root:has(#mem-16:checked) #measured-table tbody tr.gb-256,
:root:has(#mem-16:checked) #measured-table tbody tr.gb-512,
:root:has(#mem-16:checked) #measured-table tbody tr.gb-0 { display: none; }
:root:has(#mem-32:checked) #measured-table tbody tr.gb-64,
:root:has(#mem-32:checked) #measured-table tbody tr.gb-96,
:root:has(#mem-32:checked) #measured-table tbody tr.gb-128,
:root:has(#mem-32:checked) #measured-table tbody tr.gb-192,
:root:has(#mem-32:checked) #measured-table tbody tr.gb-256,
:root:has(#mem-32:checked) #measured-table tbody tr.gb-512,
:root:has(#mem-32:checked) #measured-table tbody tr.gb-0 { display: none; }
:root:has(#mem-64:checked) #measured-table tbody tr.gb-96,
:root:has(#mem-64:checked) #measured-table tbody tr.gb-128,
:root:has(#mem-64:checked) #measured-table tbody tr.gb-192,
:root:has(#mem-64:checked) #measured-table tbody tr.gb-256,
:root:has(#mem-64:checked) #measured-table tbody tr.gb-512,
:root:has(#mem-64:checked) #measured-table tbody tr.gb-0 { display: none; }
:root:has(#mem-96:checked) #measured-table tbody tr.gb-128,
:root:has(#mem-96:checked) #measured-table tbody tr.gb-192,
:root:has(#mem-96:checked) #measured-table tbody tr.gb-256,
:root:has(#mem-96:checked) #measured-table tbody tr.gb-512,
:root:has(#mem-96:checked) #measured-table tbody tr.gb-0 { display: none; }
:root:has(#mem-128:checked) #measured-table tbody tr.gb-192,
:root:has(#mem-128:checked) #measured-table tbody tr.gb-256,
:root:has(#mem-128:checked) #measured-table tbody tr.gb-512,
:root:has(#mem-128:checked) #measured-table tbody tr.gb-0 { display: none; }
:root:has(#mem-192:checked) #measured-table tbody tr.gb-256,
:root:has(#mem-192:checked) #measured-table tbody tr.gb-512,
:root:has(#mem-192:checked) #measured-table tbody tr.gb-0 { display: none; }
:root:has(#mem-256:checked) #measured-table tbody tr.gb-512,
:root:has(#mem-256:checked) #measured-table tbody tr.gb-0 { display: none; }
:root:has(#mem-512:checked) #measured-table tbody tr.gb-0 { display: none; }

/* Cloud only: the inverse selection. Everything self-hostable hides, leaving the
   rows that need a provider key. Deliberately NOT a memory stop, because it is not
   a bigger machine, it is a different trust boundary: the specification leaves the
   reader's machine. */
:root:has(#mem-cloud:checked) #measured-table tbody tr:not(.gb-0) { display: none; }
