/*! FivePaths microsite base, release 3.0.0
    https://cdn.fivepaths.com/microsite/v3/base-3.0.0.css

    A structural framework for small sites. One shape, many brands: the
    components, the spacing and type roles, the dark-band machinery and the
    accessibility floor are shared; the palette and the typeface are tokens a
    site replaces. The FivePaths colours below are the default, not the system.

      <link rel="preconnect" href="https://cdn.fivepaths.com" crossorigin>
      <link rel="preload" as="font" type="font/woff2" crossorigin
            href="https://cdn.fivepaths.com/microsite/v3/fonts/overpass-latin-wght-normal.woff2">
      <link rel="stylesheet" href="https://cdn.fivepaths.com/microsite/v3/base-3.0.0.css">
      <link rel="stylesheet" href="/assets/site.css">

    ── WHAT V3 CHANGES ───────────────────────────────────────────────────────
    v2 styled about sixty classes. v3 styles elements and their position, and
    keeps five. `<div class="card-grid">` told CSS three things and a screen
    reader nothing; `<ul data-list="cards">` tells CSS the same three and
    announces "list, 3 items". Every hook removed here was removed by reaching
    for the element that already meant it, so the markup is shorter and more
    informative at once.

    The trade is real and worth stating: structural selectors are brittle
    under hand editing. Wrap a heading in a div and `header > h2 + p` stops
    matching, with no error. This sheet is for generated markup, where the
    component set is closed and a checker fails a build whose structure does
    not match. A hand-authored site should link v2, which is unchanged and
    stays live.

    MARKUP.md in this repo is the full element-by-element contract. The short
    version:

      section                    a band. data-band="alt|dark|hero|proof|cta"
      section > header           the section head. p:first-child is an eyebrow
                                 when a heading follows; the p after a heading
                                 is the lead
      ul                         a tick list. data-list picks another shape:
                                 cards, logos
      ol                         a numbered list. data-list="steps" for the grid
      [data-layout]              split, feature, shots
      [data-actions]             a button group. The first link is the primary
      figure                     a screenshot, a table, a quotation, a listing,
                                 told apart by what is inside them
      details                    a disclosure
      aside                      a callout. data-severity carries the level
      small                      fine print

    The five classes that remain name something no element or position
    implies: ghost, ink and teal on an action link, tld inside a wordmark, and
    visually-hidden, which is a utility with no element of its own.

    Four more exist and are not design decisions: c, k, m and p on the spans
    inside a syntax-highlighted listing. Those are data about the code, and
    only a highlighter emitting different elements would remove them.

    ── COLOUR ────────────────────────────────────────────────────────────────
    Two roles, and the default palette gives each one hue.
      fill    a background with dark ink on it. Buttons, chips, highlights.
              Never a text colour on a light ground.
      accent  every text accent: links, eyebrows, list markers, step numbers,
              focus rings.
    Neutral inks and papers do everything else. Multicolour appears only as
    small data blips (--fp-route-*), never as chrome.

    Every text and background pairing defined here clears WCAG AAA (7:1),
    measured against the least favourable ground the token can land on.
    tokens.json beside this file carries the pairings and the repo's test
    measures them. A tint or wash is color-mix() against a token, never a new
    literal, so it follows a site's override and the dark-band remap.

    ── LIGHT / DARK ──────────────────────────────────────────────────────────
    One declaration per token, via light-dark(). v2 wrote the dark palette
    three times: a media query, a pinned [data-theme="dark"] block, and the
    board tokens. Every new token needed four coordinated edits and one of
    them was always forgotten. Here the scheme is chosen by color-scheme, and
    a site pins itself with <html data-theme="light"> or "dark".

    ── DARK BANDS ARE A TOKEN REMAP ──────────────────────────────────────────
    A section with data-band="dark", or hero, proof or cta, re-points the
    neutral tokens at the near-black board palette. Every component below is
    written purely in tokens, so each adapts on its own and there is no
    parallel set of dark rules. Corollary: never hardcode a colour.

    ── LAYERS ────────────────────────────────────────────────────────────────
    tokens, base, layout, components, a11y. v2 depended on source order and
    said so in a comment ("these rules sit at the end of the sheet"). Now it
    is declared. A site's own sheet is unlayered, so it always wins.
*/

@layer tokens, base, layout, components, a11y;

/* ── Type: Overpass / Overpass Mono (SIL OFL 1.1), variable, self-hosted ─── */

@font-face {
  font-family: "Overpass";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/overpass-latin-wght-normal.woff2") format("woff2-variations");
}
@font-face {
  font-family: "Overpass Mono";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("fonts/overpass-mono-latin-wght-normal.woff2") format("woff2-variations");
}

@layer tokens {
  :root {
    /* light dark: follow the reader's OS. A site pins itself with data-theme,
       which sets this to one keyword and freezes every light-dark() below. */
    color-scheme: light dark;

    /* ── Neutrals ─────────────────────────────────────────────────────── */
    --fp-bg: light-dark(#ffffff, #10141c);
    --fp-bg-alt: light-dark(#f8f9fb, #161b25);
    --fp-bg-card: light-dark(#ffffff, #1b212c);
    --fp-bg-blur: light-dark(rgb(255 255 255 / 0.92), rgb(16 20 28 / 0.92));
    --fp-ink: light-dark(#141821, #edf0f6);
    --fp-ink-soft: light-dark(#3d4350, #b9c0ce);
    --fp-ink-mute: light-dark(#505562, #a4abbb);
    --fp-rule: light-dark(#e5e7eb, #272e3c);
    --fp-shadow: light-dark(0 1px 2px rgb(20 24 33 / 0.05), 0 1px 2px rgb(0 0 0 / 0.4));
    --fp-shadow-lift: light-dark(0 14px 40px rgb(20 24 33 / 0.10), 0 18px 44px rgb(0 0 0 / 0.4));

    /* ── Accents ──────────────────────────────────────────────────────── */
    --fp-accent: light-dark(#006060, #4dd2d2);
    --fp-accent-strong: light-dark(#004f4f, #7fdede);
    --fp-bronze: light-dark(#6f5000, #ffc94d);         /* the fill family as text */
    --fp-bronze-strong: light-dark(#5a4100, #ffdd8a);
    --fp-amber-wash: light-dark(#fff8e4, #241a00);
    /* --fp-link and --fp-link-hover are read as fallbacks and never declared.
       Leaving them unset defers the lookup to the link itself, so a link in a
       dark band picks up that band's accent. Setting one on :root pins links
       to a single colour everywhere, because a custom property containing
       var() is substituted where it is declared and inherited flat. Setting
       one on a section is the intended way to override deliberately. */

    /* ── Scheme-independent ───────────────────────────────────────────── */
    /* the fill reads the same either way: it is always a ground with dark ink */
    --fp-amber: #ffb300;
    --fp-amber-hover: #ffc94d;
    --fp-amber-ink: #1a1200;
    --fp-teal-solid: #006060;      /* the accent as a fill, with white ink */

    /* the board: near-black grounds for heroes, closing bands, footers, code */
    --fp-board: #10141c;
    --fp-board-2: #161b25;
    --fp-board-deep: #0b0f16;
    --fp-board-line: #272e3c;
    --fp-board-glow: #1e2431;
    --fp-board-ink: #edf0f6;
    --fp-board-soft: #d6dbe7;
    --fp-board-dim: #9fa5b7;
    --fp-board-accent: #4dd2d2;
    --fp-board-accent-2: #7fdede;

    /* data blips: route badges, series keys. Never chrome. */
    --fp-route-blue: #0d5b98;
    --fp-route-green: #006640;
    --fp-route-red: #a72925;
    --fp-route-orange: #f5851f;
    --fp-route-purple: #684b8b;
    --fp-route-ink: #ffffff;       /* white clears 7:1 on every blip but orange */
    --fp-route-ink-dark: #221200;  /* which takes dark ink, 7.2:1 */

    /* code listings, on the board ground */
    --fp-code-comment: #a0a5b4;
    --fp-code-path: #a7d8b6;
    --fp-code-key: #a9bfe0;

    /* ── Type scale ───────────────────────────────────────────────────── */
    --fp-sans: "Overpass", system-ui, -apple-system, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, sans-serif;
    --fp-mono: "Overpass Mono", ui-monospace, "SF Mono", SFMono-Regular, Menlo,
               Consolas, "Liberation Mono", monospace;
    --fp-text: 1.0625rem;
    --fp-leading: 1.65;
    --fp-h1: clamp(2rem, 5.5vw, 3.25rem);
    --fp-h2: clamp(1.5rem, 3.5vw, 2.125rem);
    --fp-h3: 1.2rem;
    --fp-h4: 1.02rem;
    --fp-lead: clamp(1.1rem, 2.2vw, 1.3rem);
    --fp-tracking-h: -0.015em;
    --fp-weight-h1: 800;
    --fp-weight-h2: 750;
    --fp-weight-h: 700;
    --fp-weight-ui: 650;
    --fp-weight-nav: 550;

    /* WCAG 1.4.8 caps running text at 80 characters. In ch, so it tracks the
       face: v2 hard-coded 29em, derived from Overpass's average character
       width, which stopped being true the moment a site changed --fp-sans.

       52ch is measured, not assumed. The zero glyph is wider than an average
       lowercase letter, so a ch value buys more characters than it names: at
       this size a line of lowercase prose holds about 73, which is inside the
       cap and inside the 45-to-75 range that reads best. The repo's test
       measures it rather than trusting this comment, because the first value
       tried here named 68ch and held 88 characters. */
    --fp-measure: 52ch;

    /* ── Spacing and layout ───────────────────────────────────────────── */
    --fp-space-section: 4.5rem;
    --fp-space-section-tight: 3rem;
    --fp-space-head: 2.5rem;
    --fp-gap: 1.25rem;
    --fp-gap-wide: 3rem;
    --fp-pad-card: 1.5rem;
    --fp-pad-panel: 2rem;
    --fp-wrap: 70rem;              /* the content track */
    --fp-wrap-wide: 90rem;         /* the wide track, for figures */
    --fp-gutter: 1.5rem;
    --fp-radius: 12px;
    --fp-radius-sm: 8px;
    --fp-radius-xs: 6px;
    --fp-pill: 999px;
    --fp-target: 44px;             /* WCAG 2.5.5 */
    --fp-header-clear: 6.5rem;
    --fp-sticky-top: 5.5rem;       /* a sticky sub-element clears the header */
  }

  /* Pinning. One keyword decides every light-dark() above. */
  :root[data-theme="light"] { color-scheme: light; }
  :root[data-theme="dark"] { color-scheme: dark; }

  /* Dark grounds: the neutral tokens point at the board palette. Custom
     properties resolve after the cascade, so every component rule picks up the
     remapped values with no dark-specific rule anywhere. */
  :where([data-band="dark"], [data-band="hero"], [data-band="proof"], [data-band="cta"], body > footer) {
    color-scheme: dark;
    /* The remap alone is not enough. `color` inherits a computed value, so
       text inside a dark band keeps the ink body resolved from the light
       palette unless something re-reads the token here. Headings re-read it
       themselves (see the base layer), which is why they need no rule of
       their own per band. */
    color: var(--fp-ink-soft);
    --fp-bg: var(--fp-board);
    --fp-bg-alt: var(--fp-board-2);
    --fp-bg-card: var(--fp-board-2);
    --fp-bg-blur: rgb(16 20 28 / 0.92);
    --fp-ink: var(--fp-board-ink);
    --fp-ink-soft: var(--fp-board-soft);
    --fp-ink-mute: var(--fp-board-dim);
    --fp-rule: var(--fp-board-line);
    --fp-shadow: 0 1px 2px rgb(0 0 0 / 0.4);
    --fp-shadow-lift: 0 18px 44px rgb(0 0 0 / 0.4);
    --fp-accent: var(--fp-board-accent);
    --fp-accent-strong: var(--fp-board-accent-2);
    --fp-bronze: var(--fp-amber);
    --fp-bronze-strong: var(--fp-amber-hover);
    --fp-amber-wash: #241a00;
  }
}

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

  html {
    scroll-behavior: smooth;
    scroll-padding-top: var(--fp-header-clear);
    -webkit-text-size-adjust: 100%;
  }

  body {
    margin: 0;
    background: var(--fp-bg);
    color: var(--fp-ink);
    font-family: var(--fp-sans);
    font-size: var(--fp-text);
    line-height: var(--fp-leading);
    -webkit-font-smoothing: antialiased;
  }

  /* height:auto is not optional. Generated markup carries width and height on
     every image to stop the page reflowing as each one loads, and max-width
     alone then stretches them vertically. v2 lacked this and distorted every
     image outside a figure. */
  img, svg, video, canvas { max-width: 100%; height: auto; }

  a { color: var(--fp-link, var(--fp-accent)); }
  a:hover { color: var(--fp-link-hover, var(--fp-accent-strong)); }

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

  /* Headings read the ink token at the point of use rather than inheriting a
     computed colour, so a dark band recolours them by remapping the token and
     needs no heading rule of its own. */
  h1, h2, h3, h4 {
    margin: 0 0 0.5em;
    color: var(--fp-ink);
    line-height: 1.15;
    letter-spacing: var(--fp-tracking-h);
    text-wrap: balance;
  }
  h1 { font-size: var(--fp-h1); font-weight: var(--fp-weight-h1); }
  h2 { font-size: var(--fp-h2); font-weight: var(--fp-weight-h2); }
  h3 { font-size: var(--fp-h3); font-weight: var(--fp-weight-h); }
  h4 { font-size: var(--fp-h4); font-weight: var(--fp-weight-h); }

  p { margin: 0 0 1em; text-wrap: pretty; }
  p:last-child { margin-bottom: 0; }

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

  /* fine print. v2 painted this with .fine and .fineprint; the element for it
     already existed. */
  small { font-size: 0.9rem; color: var(--fp-ink-mute); }

  strong { color: var(--fp-ink); }

  hr { border: 0; border-top: 1px solid var(--fp-rule); margin: 2rem 0; }

  @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;
    }
  }
}

@layer layout {
  /* ── The content column ────────────────────────────────────────────────
     Two mechanisms, because chrome and content need different things.

     A section is a grid with a centred content track and wider tracks either
     side, so a child sits in `content` by default and anything needing more
     room asks for `wide` or `full` by name. That one mechanism replaces v2's
     .wrap, .wrap-wide and .shot-bleed, and it removes the wrapper div that
     used to sit inside every section. A full-bleed figure in v2 had to break
     out with a negative margin and a 100vw width that overflowed by the
     scrollbar's width; a named grid line has neither problem.

     The header and footer have no full-bleed children, so they take the
     simpler form: padding that collapses to the gutter on a narrow screen and
     grows to centre a capped column on a wide one. That leaves the header
     free to be a flex row, which is what a brand beside a nav wants. */
  main > section {
    display: grid;
    /* The content track is sized with min() rather than minmax(0, ...) so it
       resolves before the tracks around it. With two flexible neighbours the
       grid algorithm shared the shortfall between them instead, and on a
       narrow viewport the breakout tracks took 90px each from the content
       column: the hero indented further than the header above it. */
    grid-template-columns:
      [full-start] minmax(var(--fp-gutter), 1fr)
      [wide-start] minmax(0, calc((var(--fp-wrap-wide) - var(--fp-wrap)) / 2))
      [content-start] min(100% - var(--fp-gutter) * 2, var(--fp-wrap)) [content-end]
      minmax(0, calc((var(--fp-wrap-wide) - var(--fp-wrap)) / 2)) [wide-end]
      minmax(var(--fp-gutter), 1fr) [full-end];
  }
  main > section > * { grid-column: content; }
  main > section > [data-width="wide"] { grid-column: wide; }
  main > section > [data-width="full"] { grid-column: full; }

  body > :is(header, footer) {
    padding-inline: max(var(--fp-gutter), (100% - var(--fp-wrap)) / 2);
  }

  /* ── Bands ────────────────────────────────────────────────────────────── */
  main > section { padding-block: var(--fp-space-section); }
  /* the head's gap to the content below it, the one place a section needs one */
  main > section > header { margin-bottom: var(--fp-space-head); }
  /* a heading that follows content starts a new group and needs air above it.
     A section carrying several components reads as one run without this. */
  main > section > * + :is(h2, h3) { margin-top: 2.5rem; }
  main > section[data-tight] { padding-block: var(--fp-space-section-tight); }
  main > section[data-band="alt"] { background: var(--fp-bg-alt); }
  main > section[data-band="dark"] { background: var(--fp-board); }

  /* a rule-bounded strip of short reassurances */
  main > section[data-band="strip"] {
    padding-block: 1.1rem;
    border-block: 1px solid var(--fp-rule);
  }

  /* ── Hero ─────────────────────────────────────────────────────────────── */
  main > section[data-band="hero"] {
    padding-block: 5rem 5.5rem;
    overflow: hidden;
    background:
      radial-gradient(1200px 500px at 75% -10%, var(--fp-board-glow) 0%, transparent 60%),
      var(--fp-board);
  }
  main > section[data-band="hero"] h1 { max-width: 16em; }
  main > section[data-band="hero"][data-tight] { padding-block: 4rem; }
  main > section[data-band="hero"][data-tight] h1 { max-width: 18em; }

  /* ── Closing band ─────────────────────────────────────────────────────── */
  main > section[data-band="cta"] {
    padding-block: var(--fp-space-section);
    text-align: center;
    background:
      radial-gradient(900px 400px at 20% 120%, var(--fp-board-glow) 0%, transparent 60%),
      var(--fp-board);
  }
  main > section[data-band="cta"] > * { max-width: 40rem; margin-inline: auto; }
  main > section[data-band="cta"] p { margin-inline: auto; margin-bottom: 1.8rem; }
  main > section[data-band="cta"] small { display: block; margin-top: 1.6rem; }

  /* ── Proof strip ──────────────────────────────────────────────────────
     One row rather than a stack, so it takes the chrome layout: no child of
     a proof strip runs full bleed, and a flex row is what a claim beside a
     badge wants. */
  main > section[data-band="proof"] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 1.5rem;
    padding-block: 2.4rem;
    padding-inline: max(var(--fp-gutter), (100% - var(--fp-wrap)) / 2);
    background: var(--fp-board);
  }
  main > section[data-band="proof"] > p { margin: 0; font-size: 1.12rem; }

  /* ── The section head ─────────────────────────────────────────────────
     header > p:first-child is an eyebrow only when a heading follows it,
     which is what tells it apart from a lone paragraph of standfirst. */
  main > section > header { max-width: 46em; }
  main > section > header > :last-child { margin-bottom: 0; }
  main > section > header > p:first-child:has(+ :is(h1, h2, h3)) {
    display: inline-block;
    margin-bottom: 0.6rem;
    color: var(--fp-accent);
    font-weight: var(--fp-weight-h);
    font-size: 0.78rem;
    letter-spacing: 0.09em;
    text-transform: uppercase;
  }
  main > section > header > :is(h1, h2, h3) + p {
    font-size: var(--fp-lead);
    color: var(--fp-ink-soft);
    max-width: var(--fp-measure);
  }
  main > section > header > p:not(:first-child):not(:is(h1, h2, h3) + p) {
    color: var(--fp-ink-soft);
    font-size: 1.1rem;
  }

  /* A lead directly under a hero h1 is the same idea without a header. */
  main > section[data-band="hero"] > h1 + p,
  main > section[data-band="cta"] > h2 + p {
    font-size: var(--fp-lead);
    color: var(--fp-ink-soft);
    max-width: var(--fp-measure);
  }
  main > section[data-band="cta"] > h2 + p { margin-inline: auto; }
}

@layer components {
  /* ── Action groups ────────────────────────────────────────────────────
     The first link in the group is the page's primary ask and renders as the
     fill. A second primary now needs a second group, which is visible in the
     markup and in review, so the design guide's "one primary per view" is
     something the stylesheet enforces rather than advises. */
  [data-actions] {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    flex-wrap: wrap;
    margin-top: 1.8rem;
  }
  main > section[data-band="cta"] [data-actions] { justify-content: center; margin-top: 0; }

  [data-actions] > a,
  button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: var(--fp-target);
    /* Overpass sits its glyphs high in the line box; nudge the label down */
    padding: calc(0.7rem + 0.13em) 1.3rem calc(0.7rem - 0.13em);
    border: 1.5px solid transparent;
    border-radius: var(--fp-pill);
    font-family: inherit;
    font-size: 0.98rem;
    font-weight: var(--fp-weight-ui);
    line-height: 1.25;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 120ms ease, color 120ms ease, border-color 120ms ease;
    background: var(--fp-amber);
    color: var(--fp-amber-ink);
  }
  [data-actions] > a:hover,
  button:hover {
    background: var(--fp-amber-hover);
    color: var(--fp-amber-ink);
    text-decoration: underline;
    text-underline-offset: 0.2em;
  }

  /* Everything after the first is secondary, so it does not spend the fill. */
  [data-actions] > a:not(:first-child) {
    background: transparent;
    border-color: var(--fp-ink-soft);
    color: var(--fp-ink);
  }
  [data-actions] > a:not(:first-child):hover {
    background: var(--fp-bg-alt);
    border-color: var(--fp-ink);
    color: var(--fp-ink);
  }

  /* Three named variants, because a secondary ask is a choice with no
     structural signal. They are the only classes on an action. */
  [data-actions] > a.ghost { background: transparent; border-color: var(--fp-ink-soft); color: var(--fp-ink); }
  [data-actions] > a.ghost:hover { background: var(--fp-bg-alt); border-color: var(--fp-ink); color: var(--fp-ink); }
  [data-actions] > a.ink, a.ink { background: var(--fp-ink); border-color: transparent; color: var(--fp-bg); }
  [data-actions] > a.ink:hover, a.ink:hover { background: var(--fp-ink-soft); color: var(--fp-bg); }
  [data-actions] > a.teal { background: var(--fp-teal-solid); border-color: transparent; color: #ffffff; }
  [data-actions] > a.teal:hover { background: var(--fp-accent-strong); color: #ffffff; }

  /* ── Lists ────────────────────────────────────────────────────────────
     A bullet list is the system's list: a drawn metro-stop dot, not a glyph,
     so nothing falls back to a box. An ordered list numbers in a ring. Both
     stay in flow; data-list picks a shape that needs a grid. */
  main :where(ul, ol):not([data-list]) {
    display: grid;
    gap: 0.6rem;
    list-style: none;
    margin: 0 0 1em;
    padding: 0;
  }
  main :where(ul, ol):not([data-list]) > li {
    position: relative;
    padding-left: 1.7rem;
    color: var(--fp-ink-soft);
  }
  main ul:not([data-list]) > li::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0.42em;
    width: 0.7rem;
    height: 0.7rem;
    border-radius: 50%;
    border: 3.5px solid var(--fp-accent);
    background: var(--fp-bg);
  }
  /* "verified" rather than "a stop on a line": a drawn check. */
  main ul[data-marker="check"] > li { padding-left: 1.6rem; }
  main ul[data-marker="check"] > li::before {
    top: 0.38em;
    width: 0.85rem;
    height: 0.42rem;
    border: 0 solid var(--fp-accent);
    border-left-width: 3.5px;
    border-bottom-width: 3.5px;
    border-radius: 0;
    background: none;
    transform: rotate(-45deg);
  }
  main ul:not([data-list])[data-cols="3"] { gap: 1rem 2.5rem; }

  main ol:not([data-list]) { counter-reset: step; }
  main ol:not([data-list]) > li { counter-increment: step; padding-left: 2.6rem; }
  main ol:not([data-list]) > li::before {
    content: counter(step);
    position: absolute;
    left: 0;
    top: 0.1em;
    display: inline-grid;
    place-items: center;
    box-sizing: border-box;
    min-width: 1.9rem;
    height: 1.9rem;
    padding: 0.13em 0.5em 0;
    border: 2.5px solid var(--fp-accent);
    border-radius: var(--fp-pill);
    font-family: var(--fp-mono);
    font-weight: var(--fp-weight-h);
    font-size: 0.9rem;
    line-height: 1;
    color: var(--fp-accent);
  }

  /* ── Steps: the same numbering, laid out as a grid ────────────────────── */
  ol[data-list="steps"] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: var(--fp-gap);
    counter-reset: step;
    list-style: none;
    margin: 0;
    padding: 0;
  }
  ol[data-list="steps"] > li {
    counter-increment: step;
    border-top: 1px solid var(--fp-rule);
    padding-top: 1.1rem;
  }
  ol[data-list="steps"] > li::before {
    content: counter(step);
    display: inline-grid;
    place-items: center;
    box-sizing: border-box;
    min-width: 1.9rem;
    height: 1.9rem;
    padding: 0.13em 0.5em 0;
    border: 2.5px solid var(--fp-accent);
    border-radius: var(--fp-pill);
    font-family: var(--fp-mono);
    font-weight: var(--fp-weight-h);
    font-size: 0.9rem;
    line-height: 1;
    color: var(--fp-accent);
  }
  ol[data-list="steps"] > li > h3 { margin-top: 0.35rem; }
  ol[data-list="steps"] > li > p { color: var(--fp-ink-soft); font-size: 0.98rem; }
  ol[data-list="steps"] > li > :last-child { margin-bottom: 0; }

  /* ── Cards ────────────────────────────────────────────────────────────── */
  ul[data-list="cards"] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15.5rem, 1fr));
    gap: var(--fp-gap);
    list-style: none;
    margin: 0;
    padding: 0;
  }
  @media (min-width: 52rem) {
    ul[data-list="cards"][data-cols="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    ul[data-list="cards"][data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    ul[data-list="cards"][data-cols="4"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  }
  @media (min-width: 68rem) {
    ul[data-list="cards"][data-cols="4"] { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  }
  ul[data-list="cards"] > li {
    display: flex;
    background: var(--fp-bg-card);
    border: 1px solid var(--fp-rule);
    border-radius: var(--fp-radius);
  }
  section[data-band="alt"] ul[data-list="cards"] > li { box-shadow: var(--fp-shadow); }
  /* the card's interior belongs to whatever fills it, so the padding goes on
     the child: a whole-card link then covers the entire card. */
  ul[data-list="cards"] > li > * {
    flex: 1;
    padding: var(--fp-pad-card) var(--fp-pad-card) calc(var(--fp-pad-card) - 0.15rem);
  }
  ul[data-list="cards"] > li h3 { margin-top: 0; }
  ul[data-list="cards"] > li p { color: var(--fp-ink-soft); font-size: 0.98rem; }
  ul[data-list="cards"] > li p:last-of-type { margin-bottom: 0; }

  ul[data-list="cards"] > li > a {
    display: block;
    color: inherit;
    text-decoration: none;
    border-radius: inherit;
    transition: border-color 120ms ease, transform 120ms ease;
  }
  ul[data-list="cards"] > li:has(> a:hover) { border-color: var(--fp-ink-mute); transform: translateY(-2px); }
  @media (prefers-reduced-motion: reduce) {
    ul[data-list="cards"] > li:has(> a:hover) { transform: none; }
  }
  /* the affordance closing a card link */
  ul[data-list="cards"] > li > a > span:last-child {
    display: inline-block;
    margin-top: 0.9rem;
    color: var(--fp-accent);
    font-weight: 600;
    font-size: 0.92rem;
  }
  ul[data-list="cards"] > li > a:hover > span:last-child { color: var(--fp-accent-strong); text-decoration: underline; }

  /* a neutral icon chip; colour stays reserved for data */
  ul[data-list="cards"] > li svg:first-child {
    width: 42px;
    height: 42px;
    padding: 8px;
    border-radius: 10px;
    background: var(--fp-bg-alt);
    border: 1px solid var(--fp-rule);
    color: var(--fp-ink);
  }
  ul[data-list="cards"] > li svg:first-child + * { margin-top: 0.9rem; }

  /* ── Logos ────────────────────────────────────────────────────────────── */
  ul[data-list="logos"] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1.5rem 2.5rem;
    list-style: none;
    margin: 0;
    padding: 0;
  }
  ul[data-list="logos"] img { max-height: 2rem; }
  ul[data-list="logos"] a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: var(--fp-target);
    min-width: var(--fp-target);
    padding: 0 0.25rem;
  }

  /* ── Split panels ─────────────────────────────────────────────────────── */
  [data-layout="split"] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr));
    gap: var(--fp-gap);
  }
  [data-layout="split"] > * {
    background: var(--fp-bg-card);
    border: 1px solid var(--fp-rule);
    border-radius: var(--fp-radius);
    padding: var(--fp-pad-panel);
  }
  [data-layout="split"] > * > :last-child { margin-bottom: 0; }
  [data-layout="split"] [data-actions] { margin-top: 0.6rem; }

  /* ── Feature rows ─────────────────────────────────────────────────────── */
  [data-layout~="feature"] {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--fp-gap-wide);
    align-items: center;
    padding-block: 2.75rem;
  }
  [data-layout~="feature"] + [data-layout~="feature"] { border-top: 1px solid var(--fp-rule); }
  [data-layout~="feature"][data-layout~="uneven"] { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
  /* when the columns differ a lot in height, centring the short one against
     the tall one reads as a mistake */
  [data-layout~="feature"][data-layout~="top"] { align-items: start; }
  [data-layout~="feature"][data-layout~="flip"] > :first-child { order: 2; }
  [data-layout~="feature"] > * { min-width: 0; }
  [data-layout~="feature"] h2 { font-size: clamp(1.35rem, 3vw, 1.75rem); }
  [data-layout~="feature"] h3 { font-size: 1.45rem; }
  [data-layout~="feature"] > * > p { color: var(--fp-ink-soft); }

  /* Stacking. One breakpoint for every two-column arrangement, so nothing is
     half stacked at any width. 56rem is the convention's `stack` value; see
     tokens.json, which is where a site should read it from rather than
     guessing, because a media query cannot take a custom property. */
  @media (max-width: 56rem) {
    main > section[data-band="hero"] { padding-block: 3.5rem 4rem; }
    [data-layout~="feature"],
    [data-layout~="feature"][data-layout~="uneven"] {
      grid-template-columns: 1fr;
      gap: 1.5rem;
    }
    [data-layout~="feature"][data-layout~="flip"] > :first-child { order: 0; }
    [data-layout="shots"][data-uneven] { grid-template-columns: 1fr; }
  }

  /* ── Screenshots and figures ──────────────────────────────────────────
     Told apart by what is inside them, which is also what decides how each
     one is framed. */
  [data-layout="shots"] {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
    gap: 2rem;
    align-items: start;
  }
  [data-layout="shots"][data-uneven] { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }

  figure { margin: 0; min-width: 0; }
  figure:has(> :is(img, picture, a > img)) img {
    display: block;
    width: 100%;
    height: auto;
    border: 1px solid var(--fp-rule);
    border-radius: var(--fp-radius);
    box-shadow: var(--fp-shadow-lift);
  }
  figure figcaption {
    margin-top: 0.75rem;
    max-width: var(--fp-measure);
    font-size: 0.88rem;
    color: var(--fp-ink-mute);
  }
  /* a full-bleed figure loses the frame it no longer has room for */
  [data-width="full"] :is(img, figure > img) {
    border-radius: 0;
    border-inline: 0;
    box-shadow: none;
  }
  /* the caption is prose, so it keeps the content column even when the
     picture above it does not */
  [data-width="full"] figcaption {
    max-width: var(--fp-wrap);
    margin-inline: auto;
    padding-inline: var(--fp-gutter);
  }

  /* ── Code ─────────────────────────────────────────────────────────────── */
  pre {
    margin: 0;
    padding: 1.2rem 1.4rem;
    background: var(--fp-board);
    color: var(--fp-board-soft);
    border: 1px solid var(--fp-board-line);
    border-radius: var(--fp-radius);
    font-family: var(--fp-mono);
    font-size: 0.86rem;
    line-height: 1.7;
    overflow-x: auto;
  }
  pre .c { color: var(--fp-code-comment); }
  pre .m { color: var(--fp-amber); }
  pre .p { color: var(--fp-code-path); }
  pre .k { color: var(--fp-code-key); }

  /* A listing with a filename is a figure whose caption is the title bar.
     v2 drew three decorative dots with three empty <i> elements; they are a
     border-image's worth of decoration, so they belong in CSS. */
  figure:has(> pre) {
    background: var(--fp-bg-card);
    border: 1px solid var(--fp-rule);
    border-radius: var(--fp-radius);
    overflow: hidden;
    box-shadow: var(--fp-shadow-lift);
  }
  figure:has(> pre) > figcaption {
    /* The dot metrics and the space reserved for them are derived from each
       other, because they were written independently once and the filename
       sat on top of the third dot. */
    --dot: 0.65rem;
    --dot-step: 1.1rem;   /* a dot plus the gap after it */
    --dots-end: calc(0.9rem + 2 * var(--dot-step) + var(--dot));
    display: flex;
    align-items: center;
    margin: 0;
    padding: 0.6rem 0.9rem 0.6rem calc(var(--dots-end) + 0.7rem);
    max-width: none;
    border-bottom: 1px solid var(--fp-rule);
    font-family: var(--fp-mono);
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    color: var(--fp-ink-soft);
    position: relative;
  }
  figure:has(> pre) > figcaption::before {
    content: "";
    position: absolute;
    left: 0.9rem;
    top: 50%;
    translate: 0 -50%;
    width: var(--dot);
    height: var(--dot);
    border-radius: 50%;
    background: var(--fp-rule);
    box-shadow: var(--dot-step) 0 var(--fp-rule), calc(2 * var(--dot-step)) 0 var(--fp-rule);
  }
  figure:has(> pre) > pre { border: 0; border-radius: 0; box-shadow: none; }

  /* ── Tables ───────────────────────────────────────────────────────────── */
  figure:has(> table) { overflow-x: auto; margin-block: 1.5rem; }
  table { border-collapse: collapse; width: 100%; min-width: 32rem; font-size: 0.95rem; }
  caption {
    text-align: left;
    font-family: var(--fp-mono);
    font-size: 0.72rem;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--fp-ink-mute);
    padding-bottom: 0.7rem;
  }
  th, td {
    text-align: left;
    padding: 0.7rem 1.2rem 0.7rem 0;
    border-bottom: 1px solid var(--fp-rule);
    vertical-align: top;
  }
  thead th {
    font-family: var(--fp-mono);
    font-size: 0.71rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--fp-ink-mute);
  }
  tbody th { font-weight: var(--fp-weight-ui); color: var(--fp-ink); }

  /* ── Quotations ───────────────────────────────────────────────────────── */
  figure:has(> blockquote) {
    margin-block: 1.75rem;
    padding-left: 1.15rem;
    border-left: 3px solid var(--fp-rule);
  }
  blockquote { margin: 0; font-size: 1.1rem; line-height: 1.55; color: var(--fp-ink); }
  figure:has(> blockquote) > figcaption { margin-top: 0.6rem; font-size: 0.9rem; }

  /* ── Callouts ─────────────────────────────────────────────────────────
     Severity is carried by the visible label as well as the colour, because
     colour alone is not information (WCAG 1.4.1). */
  aside {
    border-left: 3px solid var(--fp-accent);
    background: var(--fp-bg-alt);
    border-radius: 0 var(--fp-radius-sm) var(--fp-radius-sm) 0;
    padding: 1rem 1.15rem;
    margin-block: 1.5rem;
  }
  aside[data-severity="warning"] { border-left-color: var(--fp-bronze); background: var(--fp-amber-wash); }
  aside[data-severity="critical"] { border-left-color: var(--fp-route-red); }
  aside > p:first-child {
    font-family: var(--fp-mono);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--fp-ink-mute);
    margin-bottom: 0.4rem;
  }
  aside > :last-child { margin-bottom: 0; }

  /* ── Disclosure ───────────────────────────────────────────────────────── */
  details {
    border: 1px solid var(--fp-rule);
    border-radius: var(--fp-radius);
    padding: 1rem 1.15rem;
    background: var(--fp-bg-card);
    margin-block: 1rem;
  }
  details > summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    min-height: var(--fp-target);
    font-weight: var(--fp-weight-ui);
    color: var(--fp-ink);
    cursor: pointer;
    /* display:flex on a summary drops the native marker, so the disclosure
       state has to be drawn back. Both spellings: the pseudo-element is
       WebKit's and list-style covers everyone else. */
    list-style: none;
  }
  details > summary::-webkit-details-marker { display: none; }
  /* A triangle pointing at what it will do: right when closed, down when open.
     Drawn with borders rather than a glyph, for the same reason the list
     markers are: there is nothing for a glyph to fall back to. */
  details > summary::after {
    content: "";
    flex: none;
    width: 0;
    height: 0;
    border-left: 0.36rem solid var(--fp-ink-mute);
    border-block: 0.3rem solid transparent;
    transition: rotate 120ms ease;
  }
  details[open] > summary::after { rotate: 90deg; }
  details > :not(summary):last-child { margin-bottom: 0; }

  /* ── Definition lists ─────────────────────────────────────────────────── */
  dl { margin-block: 1.5rem; }
  dt {
    font-weight: var(--fp-weight-ui);
    color: var(--fp-ink);
    margin-top: 1.25rem;
    scroll-margin-top: var(--fp-header-clear);
  }
  dt:first-child { margin-top: 0; }
  dd { margin: 0.35rem 0 0; color: var(--fp-ink-soft); max-width: var(--fp-measure); }

  /* ── Data blips ───────────────────────────────────────────────────────
     The one component that spends colour on meaning. v2 shipped the tokens
     with nothing that used them, so every site that wanted a route badge
     wrote its own. */
  [data-route] {
    display: inline-block;
    padding: 0.1rem 0.5rem;
    border-radius: var(--fp-radius-xs);
    font-family: var(--fp-mono);
    font-size: 0.78rem;
    font-weight: 600;
    color: var(--fp-route-ink);
    background: var(--fp-ink-mute);
  }
  [data-route="blue"] { background: var(--fp-route-blue); }
  [data-route="green"] { background: var(--fp-route-green); }
  [data-route="red"] { background: var(--fp-route-red); }
  [data-route="purple"] { background: var(--fp-route-purple); }
  [data-route="orange"] { background: var(--fp-route-orange); color: var(--fp-route-ink-dark); }

  /* ── A mono chip ──────────────────────────────────────────────────────
     A jump-list entry, a module name, a proof-strip badge. Each had been
     written twice in site sheets before it moved here. */
  [data-chip] {
    display: inline-flex;
    align-items: center;
    min-height: var(--fp-target);
    padding: 0.2rem 0.7rem;
    border: 1px solid var(--fp-rule);
    border-radius: var(--fp-pill);
    font-family: var(--fp-mono);
    font-size: 0.8rem;
    color: var(--fp-ink-soft);
    text-decoration: none;
  }
  a[data-chip]:hover { border-color: var(--fp-ink-mute); color: var(--fp-accent); }
  /* On the proof strip the chip carries the fill colour as text. The board is
     the one ground where that clears AAA, at 10.3:1. */
  section[data-band="proof"] [data-chip] { color: var(--fp-amber); white-space: nowrap; }

  /* ── Site header ──────────────────────────────────────────────────────── */
  body > header {
    position: sticky;
    top: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 1rem 1.5rem;
    padding-block: 0.85rem;
    background: var(--fp-bg);
    border-bottom: 1px solid var(--fp-rule);
  }
  @supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    body > header {
      background: var(--fp-bg-blur);
      -webkit-backdrop-filter: blur(10px);
      backdrop-filter: blur(10px);
    }
  }
  body > header > a:first-child {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    min-height: var(--fp-target);
    color: var(--fp-ink);
    font-weight: var(--fp-weight-h1);
    font-size: 1.2rem;
    letter-spacing: -0.02em;
    text-decoration: none;
  }
  body > header > a:first-child:hover { color: var(--fp-ink); }
  body > header > a:first-child :is(svg, img) { display: block; height: var(--fp-logo-height, 3.2rem); width: auto; }
  .tld { color: var(--fp-ink-mute); font-weight: 600; }

  body > header > nav {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.85rem 1.4rem;
  }
  body > header > nav a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: var(--fp-target);
    min-width: var(--fp-target);
    color: var(--fp-ink-soft);
    font-weight: var(--fp-weight-nav);
    font-size: 0.95rem;
    text-decoration: none;
  }
  body > header > nav a:hover { color: var(--fp-ink); text-decoration: underline; }
  /* "page" marks the current page; "location" the section in view. Same mark. */
  body > header > nav a[aria-current] { color: var(--fp-ink); box-shadow: 0 2px 0 var(--fp-accent); }
  /* the header's call to action is the small pill; it needs no size class */
  body > header > nav a.ink,
  body > header > nav a.ghost {
    padding: calc(0.45rem + 0.13em) 1rem calc(0.45rem - 0.13em);
    border: 1.5px solid transparent;
    border-radius: var(--fp-pill);
    font-size: 0.9rem;
    font-weight: var(--fp-weight-ui);
  }
  body > header > nav a.ink:hover { text-decoration: underline; text-underline-offset: 0.2em; }

  @media (max-width: 45rem) {
    body > header { padding-block: 1.15rem 1.4rem; row-gap: 1.15rem; }
    body > header > nav { justify-content: flex-start; }
  }

  /* ── Site footer ──────────────────────────────────────────────────────── */
  body > footer {
    display: grid;
    row-gap: 1.25rem;
    justify-items: start;
    background: var(--fp-board-deep);
    padding-block: 2.25rem 2.75rem;
    font-size: 0.95rem;
  }
  body > footer > * { width: 100%; }
  body > footer :is(h2, h3) {
    margin-bottom: 0.9rem;
    color: var(--fp-ink);
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.1em;
  }
  body > footer ul {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 1.4rem;
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: 0.85rem;
  }
  body > footer li { display: flex; }
  body > footer a {
    display: inline-flex;
    align-items: center;
    min-height: var(--fp-target);
    color: var(--fp-ink-soft);
    text-decoration: none;
  }
  body > footer a:hover { color: var(--fp-ink); text-decoration: underline; }
  /* the byline row: the site, then "is a FivePaths product." */
  body > footer > p:first-child {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin: 0;
  }
  body > footer > p:first-child a {
    gap: 0.4rem;
    font-weight: var(--fp-weight-h);
    color: var(--fp-accent);
  }
  body > footer > p:first-child a:hover { color: var(--fp-accent-strong); }
  body > footer > p:first-child img { display: block; height: 1.5rem; width: auto; }
  /* the copyright sits at the far edge of the byline row */
  body > footer > p:first-child > :last-child:not(a) { margin-left: auto; }
  body > footer > small {
    display: block;
    margin-top: 1.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--fp-rule);
    font-size: 0.85rem;
  }
  body > footer > small p { margin: 0 0 0.4rem; }
  body > footer > small p:last-child { margin-bottom: 0; }
}

@layer a11y {
  /* The skip link is the first thing in the body and the first thing a
     keyboard reaches. */
  body > a:first-child {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 100;
    padding: 0.6rem 1rem;
    background: var(--fp-amber);
    color: var(--fp-amber-ink);
    border-radius: 0 var(--fp-radius-sm) 0 0;
    font-weight: var(--fp-weight-h);
    text-decoration: none;
  }
  body > a:first-child:focus { left: 0; }

  /* Target size, WCAG 2.5.5 AAA. A link that is the whole content of its list
     item or paragraph is a standalone control, not a link in a sentence, so
     it needs a real target. v2 enumerated the components this applied to and
     the list grew every time a component was added; stated structurally it
     covers the ones nobody has written yet.

     Links inside running prose are untouched: they keep the line-height of
     the text around them, which is what the criterion's exception is for.

     The :not() is load-bearing. A whole-card link is also the only child of
     its list item, and without the exclusion this rule made it inline-flex
     and laid its heading, prose and affordance out in a row. A link wrapping
     block content is already far larger than the minimum; the rule is for a
     link whose target is only as big as its text. */
  main :is(li, p, dd) > a:only-child:not(:has(:is(h1, h2, h3, h4, h5, h6, p, ul, ol, dl, div, figure, table))) {
    display: inline-flex;
    align-items: center;
    min-height: var(--fp-target);
    min-width: var(--fp-target);
  }

  /* Measure, WCAG 1.4.8 AAA. A block of running text stays under 80
     characters. Headings, tables and code are not running text and keep the
     full column. */
  main p,
  main li,
  body > footer > small p {
    max-width: var(--fp-measure);
  }
  main :is(section[data-band="cta"], [data-centered]) p { margin-inline: auto; }
  /* a list already indents its text; the marker column is not part of the measure */
  main :where(ul, ol):not([data-list]) > li { max-width: calc(var(--fp-measure) + 1.7rem); }

  /* Text for assistive technology only. Carries the "opens in a new tab"
     note external links need (WCAG 3.2.5), among other things. */
  .visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  /* The colour-scheme control WCAG 1.4.8 asks for. Behaviour is in
     theme-1.0.0.js beside this file; the glyphs show one at a time so the
     visible label always names the scheme it offers to move to. */
  body > header > nav button {
    min-height: var(--fp-target);
    padding: 0 0.15rem;
    background: none;
    border: 0;
    color: inherit;
    font: inherit;
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 0.2em;
    gap: 0.45rem;
  }
  body > header > nav button:hover { background: none; color: var(--fp-accent); }
  body > header > nav button svg { width: 1.05em; height: 1.05em; flex: none; }
  [data-scheme="dark"] { display: inline; }
  [data-scheme="light"] { display: none; }
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) [data-scheme="dark"] { display: none; }
    :root:not([data-theme="light"]) [data-scheme="light"] { display: inline; }
  }
  :root[data-theme="dark"] [data-scheme="dark"] { display: none; }
  :root[data-theme="dark"] [data-scheme="light"] { display: inline; }
  :root[data-theme="light"] [data-scheme="dark"] { display: inline; }
  :root[data-theme="light"] [data-scheme="light"] { display: none; }
}
