/**
 * SLYP — stylesheet.
 *
 * One file, no preprocessor, no framework. Layers keep specificity flat so a
 * component rule always beats a token rule without anyone writing !important.
 *
 * The palette is the game's own, lifted from Theme.swift: a flat, matte e-ink
 * system where paper and ink swap roles between day and night, with a single
 * burnt accent the game only ever spends on a reward.
 *
 * Several rules are marked LOAD-BEARING. They look removable. They are not —
 * each exists because of a bug that was expensive to find on a sibling site.
 */

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

/* =========================================================== tokens */
@layer tokens {
  :root {
    /* Day, straight from Theme.light: `paper` is the game's background and
       `ink` the square that holds the centre. */
    --paper: #ece8e0;
    --paper-raised: #f3f0ea;
    --paper-sunk: #e2ddd3;
    --ink: #161513;
    --ink-2: #201e1b;
    /* Theme.light.secondaryText, darkened a step. The game's own value clears
       AA on paper (4.67:1) but not on the tinted sections (4.22:1), and half
       the muted text on this page sits on the tint. This value clears both:
       5.04:1 on paper, 4.55:1 on tint. */
    --muted: #666157;
    --rule: rgb(22 21 19 / 0.10);
    --rule-strong: rgb(22 21 19 / 0.22);

    /* The accent. The game's own #b85c29 measures 3.74:1 on paper, which is
       fine for the large PERFECT it is spent on there and not enough for text
       here. `--accent` is that colour darkened until it clears AA on BOTH
       grounds — 5.14:1 on paper, 4.64:1 on the tinted sections — because every
       kicker on the page sits on one or the other. `--accent-flat` is the
       untouched game value and is only ever a shape, never type. */
    --accent: #974b22;
    --accent-flat: #b85c29;
    --accent-soft: rgb(184 92 41 / 0.12);

    --focus: #1b4ea8;

    /* A quiet grotesque for prose, monospace for labels and figures — the game
       sets its own HUD in mono. */
    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
      Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

    --step--1: clamp(0.82rem, 0.79rem + 0.14vw, 0.9rem);
    --step-0: clamp(1rem, 0.96rem + 0.2vw, 1.09rem);
    --step-1: clamp(1.18rem, 1.1rem + 0.36vw, 1.4rem);
    --step-2: clamp(1.45rem, 1.28rem + 0.75vw, 1.95rem);
    --step-3: clamp(1.85rem, 1.5rem + 1.55vw, 2.9rem);
    --step-4: clamp(2.1rem, 1.5rem + 2.7vw, 3.9rem);

    --section-space: clamp(3.5rem, 2.2rem + 5.4vw, 6.5rem);
    --shell-pad: clamp(1.15rem, 0.75rem + 1.9vw, 2.5rem);
    --shell-max: 68rem;
    --shell-narrow: 44rem;

    --radius: 2px;
    --header-h: 3.75rem;

    --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  }

  /* Night inverts the sheet exactly as ThemeMode.dark does: the same paper and
     the same ink, changing places. Only tokens move. */
  @media (prefers-color-scheme: dark) {
    :root {
      --paper: #161513;
      --paper-raised: #1e1c19;
      --paper-sunk: #100f0d;
      --ink: #edeae3;
      --ink-2: #dedbd3;
      --muted: #99958b;
      --rule: rgb(237 234 227 / 0.12);
      --rule-strong: rgb(237 234 227 / 0.24);
      --accent: #db9e57;
      --accent-flat: #db9e57;
      --accent-soft: rgb(219 158 87 / 0.14);
      --focus: #8fb4ff;
    }
  }
}

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

  /* LOAD-BEARING: the clip lives on html alone. Putting overflow on body too
     breaks viewport propagation, and the drawer's scroll lock stops working. */
  html {
    overflow-x: clip;
    scroll-padding-top: calc(var(--header-h) + 1rem);
    -webkit-text-size-adjust: 100%;
  }

  @media (prefers-reduced-motion: no-preference) {
    html {
      scroll-behavior: smooth;
    }
  }

  body {
    margin: 0;
    background: var(--paper);
    color: var(--ink);
    font-family: var(--font-sans);
    font-size: var(--step-0);
    line-height: 1.62;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
  }

  h1,
  h2,
  h3 {
    margin: 0;
    font-weight: 600;
    letter-spacing: -0.02em;
    line-height: 1.15;
  }

  p {
    margin: 0;
  }

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

  img,
  svg {
    display: block;
    max-width: 100%;
  }

  a {
    color: inherit;
  }

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

  ::selection {
    background: var(--accent-soft);
  }
}

/* =========================================================== layout */
@layer layout {
  .shell {
    width: 100%;
    max-width: var(--shell-max);
    margin-inline: auto;
    padding-inline: max(var(--shell-pad), env(safe-area-inset-left));
  }

  .shell--narrow {
    max-width: var(--shell-narrow);
  }

  .section {
    padding-block: var(--section-space);
    border-top: 1px solid var(--rule);
  }

  /* Alternating ground. Two tinted sections never sit next to each other, so
     the page reads as a stack of sheets rather than a gradient. */
  .section--tint {
    background: var(--paper-sunk);
  }

  .section__head {
    max-width: 42rem;
  }

  .section__title {
    margin-top: 0.75rem;
    font-size: var(--step-3);
  }

  .section__lead {
    margin-top: 1.1rem;
    font-size: var(--step-1);
    line-height: 1.52;
    color: var(--ink-2);
  }

  .section__note {
    margin-top: clamp(1.75rem, 1.2rem + 2vw, 2.5rem);
    max-width: 42rem;
    font-size: var(--step--1);
    color: var(--muted);
  }
}

/* ======================================================= components */
@layer components {
  /* -------------------------------------------------------- chrome */
  .skip-link {
    position: absolute;
    inset-inline-start: 1rem;
    inset-block-start: -4rem;
    z-index: 20;
    padding: 0.7rem 1.1rem;
    background: var(--ink);
    color: var(--paper);
    font-size: var(--step--1);
    text-decoration: none;
    transition: inset-block-start 0.18s var(--ease);
  }

  .skip-link:focus {
    inset-block-start: 1rem;
  }

  .site-header {
    position: sticky;
    inset-block-start: 0;
    z-index: 10;
    /* The header keeps its own ground so nothing scrolling underneath shows
       through it. Only the mark inside is transparent, and it takes this
       ground rather than carrying one of its own. */
    background: color-mix(in srgb, var(--paper) 92%, transparent);
    backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--rule);
  }

  .site-header__inner {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    min-height: var(--header-h);
  }

  .wordmark {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    text-decoration: none;
    font-weight: 600;
    letter-spacing: 0.06em;
  }

  /* No radius: there is no ground left to round, only the shapes themselves. */
  .brand-mark {
    width: 1.6rem;
    height: 1.6rem;
  }

  .site-nav {
    margin-inline-start: auto;
  }

  .site-nav__list {
    display: flex;
    gap: clamp(1rem, 0.4rem + 1.6vw, 2rem);
  }

  .site-nav__list a {
    font-size: var(--step--1);
    text-decoration: none;
    color: var(--ink-2);
    transition: color 0.16s var(--ease);
  }

  .site-nav__list a:hover {
    color: var(--accent);
  }

  .site-header__end {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-inline-start: auto;
  }

  .site-nav + .site-header__end {
    margin-inline-start: 0;
  }

  .lang-switch {
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    font-family: var(--font-mono);
    font-size: var(--step--1);
  }

  .lang-switch__item {
    text-decoration: none;
    color: var(--muted);
  }

  .lang-switch__item[aria-current] {
    color: var(--ink);
  }

  .lang-switch__sep {
    color: var(--rule-strong);
  }

  .lang-switch abbr {
    text-decoration: none;
  }

  /* -------------------------------------------------------- drawer */
  .nav-toggle {
    display: none;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    background: none;
    border: 0;
    cursor: pointer;
  }

  .nav-toggle__bar,
  .nav-toggle__bar::before {
    display: block;
    width: 1.35rem;
    height: 1px;
    margin-inline: auto;
    background: var(--ink);
  }

  .nav-toggle__bar::before {
    content: "";
    transform: translateY(-0.4rem);
  }

  .drawer {
    position: fixed;
    inset: 0;
    z-index: 15;
    display: grid;
    /* LOAD-BEARING: auto then minmax(0, 1fr). Without the zero minimum the
       list cannot shrink and a long menu overflows behind the fold instead of
       scrolling. */
    grid-template-rows: auto minmax(0, 1fr);
    background: var(--paper);
    visibility: hidden;
    opacity: 0;
    /* LOAD-BEARING: visibility is delayed only on the way out, so the drawer
       is focusable the instant it opens. */
    transition: opacity 0.22s var(--ease), visibility 0s linear 0.22s;
  }

  .drawer[data-open="true"] {
    visibility: visible;
    opacity: 1;
    transition: opacity 0.22s var(--ease), visibility 0s;
  }

  .drawer__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    min-height: var(--header-h);
    border-bottom: 1px solid var(--rule);
  }

  .drawer__body {
    display: grid;
    align-content: start;
    gap: 2rem;
    padding-block: 2rem;
    overflow-y: auto;
  }

  .drawer__list {
    display: grid;
    gap: 0.4rem;
  }

  .drawer__list a {
    display: inline-block;
    padding-block: 0.35rem;
    font-size: var(--step-2);
    text-decoration: none;
  }

  .drawer__foot {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    font-size: var(--step--1);
    color: var(--muted);
  }

  .drawer__foot a {
    color: inherit;
    display: inline-block;
    padding-block: 0.35rem;
  }

  /* ---------------------------------------------------------- hero */
  .hero {
    padding-block: clamp(3rem, 1.6rem + 6.5vw, 7.5rem) var(--section-space);
  }

  /* No narrower max-width on `.hero__inner`. `.shell` centres whatever box it
     is given, so capping it re-centres the hero and pushes it out of alignment
     with the header and every section below. The cap belongs on the text. */
  .hero__inner {
    display: grid;
    /* The headline is two lines by design; the text column has to be wide
       enough that neither of them wraps into a third. */
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr);
    align-items: center;
    gap: clamp(2rem, 1rem + 4vw, 5rem);
  }

  /* LOAD-BEARING: grid, not block. Each line needs vertical room for
     descenders; with block layout the negative margin that buys it collapses
     against the next element and the whole page shifts. */
  .hero__title {
    display: grid;
    margin-top: 1.5rem;
    font-size: var(--step-4);
    line-height: 1.04;
    letter-spacing: -0.03em;
  }

  .hero__line {
    /* Room for descenders without adding leading between the lines. */
    --bleed: 0.14em;
    display: block;
    padding-block: var(--bleed);
    margin-block: calc(var(--bleed) * -1);
  }

  /* The second line is the turn in the sentence — the walls, not the square. */
  .hero__line:last-child {
    color: var(--muted);
  }

  .hero__lead,
  .hero__note {
    max-width: 34rem;
  }

  .hero__lead {
    margin-top: clamp(1.25rem, 0.9rem + 1.4vw, 2rem);
    font-size: var(--step-1);
    line-height: 1.52;
    color: var(--ink-2);
  }

  .hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: clamp(1.75rem, 1.2rem + 2vw, 2.5rem);
  }

  .hero__note {
    margin-top: 1.5rem;
    font-family: var(--font-mono);
    font-size: var(--step--1);
    color: var(--muted);
  }

  /* ---------------------------------------------------------- mark
     The app's own logo, day and night. Each file carries its own ground, and
     that ground is a shade off the page's — so it is framed as a sheet rather
     than blended in, which is the honest way to present artwork that already
     has a background. */
  .hero__mark {
    justify-self: center;
    width: 100%;
    max-width: 30rem;
  }

  /* Both files are transparent but for their shapes, so the mark sits directly
     on the page in either scheme — no sheet, no border, no ground of its own. */
  .hero__mark picture {
    display: block;
  }

  .mark {
    width: 100%;
    height: auto;
  }

  /* --------------------------------------------------------- steps */
  .steps,
  .sheets,
  .cards {
    display: grid;
    gap: 1px;
    margin-top: clamp(2.5rem, 1.8rem + 2.6vw, 4rem);
    background: var(--rule);
    border-block: 1px solid var(--rule);
  }

  .step,
  .sheet,
  .card {
    display: grid;
    gap: 0.5rem;
    align-content: start;
    padding: clamp(1.5rem, 1.1rem + 1.6vw, 2.25rem);
    background: var(--paper);
  }

  .section--tint .step,
  .section--tint .sheet,
  .section--tint .card {
    background: var(--paper-sunk);
  }

  .step__index {
    font-family: var(--font-mono);
    font-size: var(--step--1);
    letter-spacing: 0.12em;
    color: var(--accent);
  }

  .step__title,
  .sheet__name,
  .card__title {
    font-size: var(--step-1);
  }

  .step__body,
  .sheet__body,
  .card__body {
    max-width: 34rem;
    color: var(--muted);
  }

  /* ------------------------------------------------------- figures */
  .figures {
    display: grid;
    gap: clamp(1.75rem, 1.2rem + 2vw, 3rem);
    margin-top: clamp(2.5rem, 1.8rem + 2.6vw, 4rem);
  }

  .figure__value {
    font-family: var(--font-mono);
    font-size: var(--step-3);
    line-height: 1;
    letter-spacing: -0.02em;
  }

  .figure__label {
    margin-top: 0.6rem;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--muted);
  }

  /* --------------------------------------------------------- split */
  .split {
    display: grid;
    gap: clamp(2rem, 1.4rem + 2.4vw, 3.5rem);
  }

  .split__aside {
    align-self: end;
  }

  .milestones {
    font-family: var(--font-mono);
    font-size: var(--step-1);
    letter-spacing: 0.04em;
    color: var(--accent);
  }

  .milestones__label {
    margin-top: 0.6rem;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--muted);
  }

  .split__note {
    margin-top: 1.75rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--rule);
    font-size: var(--step--1);
    color: var(--muted);
  }

  /* -------------------------------------------------------- sheets
     Three squares, each on its own ground, so the two palettes are shown
     rather than described. The third follows the reader's system setting,
     which is exactly what Auto does in the app. */
  .sheet__demo {
    position: relative;
    display: grid;
    place-items: center;
    margin-bottom: 0.4rem;
    aspect-ratio: 16 / 7;
    border: 1px solid var(--rule);
  }

  .sheet__square {
    width: 22%;
    aspect-ratio: 1;
  }

  /* The six looks, in the order ThemeMode cycles them: the mono trio, then the
     colour trio. Indices match the order in content.mjs.

     DAY and NIGHT are fixed sheets — each shows its own scheme whatever the
     reader's system is set to, because that is the point of choosing them.
     AUTO and SOFT AUTO are drawn from tokens instead, so they follow the
     reader's device exactly as they follow the player's. */

  /* 0 — DAY: Theme.light, background and player. */
  .sheet[data-sheet="0"] .sheet__demo {
    background: #ece8e0;
  }
  .sheet[data-sheet="0"] .sheet__square {
    background: #161513;
  }

  /* 1 — NIGHT: Theme.dark. The same sheet, inverted. */
  .sheet[data-sheet="1"] .sheet__demo {
    background: #161513;
  }
  .sheet[data-sheet="1"] .sheet__square {
    background: #edeae3;
  }

  /* 2 — AUTO: follows the device, so it is drawn from tokens. */
  .sheet[data-sheet="2"] .sheet__demo {
    background: var(--paper);
  }
  .sheet[data-sheet="2"] .sheet__square {
    background: var(--ink);
  }

  /* 3 — SOFT DAY: Theme.soft. Warmer paper, a bluer square. */
  .sheet[data-sheet="3"] .sheet__demo {
    background: #f1ede4;
  }
  .sheet[data-sheet="3"] .sheet__square {
    background: #262938;
  }

  /* 4 — SOFT NIGHT: Theme.softDark. Warm charcoal, off-white square. */
  .sheet[data-sheet="4"] .sheet__demo {
    background: #1f1f26;
  }
  .sheet[data-sheet="4"] .sheet__square {
    background: #e8e6db;
  }

  /* 5 — SOFT AUTO: the colour set on whichever sheet the device is showing. */
  .sheet[data-sheet="5"] .sheet__demo {
    background: #f1ede4;
  }
  .sheet[data-sheet="5"] .sheet__square {
    background: #262938;
  }

  @media (prefers-color-scheme: dark) {
    .sheet[data-sheet="5"] .sheet__demo {
      background: #1f1f26;
    }
    .sheet[data-sheet="5"] .sheet__square {
      background: #e8e6db;
    }
  }

  /* The ten wall colours. Taken one for one from Theme.soft.wallPalette and
     Theme.softDark.wallPalette — the colours ARE what the Pro themes are, and a
     description cannot stand in for them. The strip reads its ten from --w1..10,
     so each sheet only has to say which palette it is on. */
  /* Inset top and bottom rather than filling the field: the walls travel across
     a sheet, and if the strip covered it the theme's own ground would never
     show — which is most of what separates Soft Day from Soft Night. */
  .sheet__walls {
    position: absolute;
    inset: 22% 0;
    display: grid;
    grid-template-columns: repeat(10, 1fr);
  }

  /* Full opacity. The palette is the whole point of these three sheets, and
     softening it invented a colour the app does not have — most visibly on
     Soft Night, where 0.85 over charcoal dulled the lifted palette back down. */
  .sheet__walls i {
    background: var(--wall);
  }

  .sheet__walls i:nth-child(1) { --wall: var(--w1); }
  .sheet__walls i:nth-child(2) { --wall: var(--w2); }
  .sheet__walls i:nth-child(3) { --wall: var(--w3); }
  .sheet__walls i:nth-child(4) { --wall: var(--w4); }
  .sheet__walls i:nth-child(5) { --wall: var(--w5); }
  .sheet__walls i:nth-child(6) { --wall: var(--w6); }
  .sheet__walls i:nth-child(7) { --wall: var(--w7); }
  .sheet__walls i:nth-child(8) { --wall: var(--w8); }
  .sheet__walls i:nth-child(9) { --wall: var(--w9); }
  .sheet__walls i:nth-child(10) { --wall: var(--w10); }

  /* Soft Day, and Soft Auto while the device is light. */
  .sheet[data-sheet="3"] .sheet__walls,
  .sheet[data-sheet="5"] .sheet__walls {
    --w1: #6b8cb8;
    --w2: #759e78;
    --w3: #c78061;
    --w4: #9480b5;
    --w5: #c9a159;
    --w6: #619c99;
    --w7: #cc8a94;
    --w8: #b26157;
    --w9: #91965c;
    --w10: #7378ad;
  }

  /* Soft Night: the same ten, lifted so they carry on charcoal. */
  .sheet[data-sheet="4"] .sheet__walls {
    --w1: #85a6d6;
    --w2: #8cbd8f;
    --w3: #e09978;
    --w4: #ad99d4;
    --w5: #e3bd70;
    --w6: #75bab8;
    --w7: #e6a3ad;
    --w8: #d1786e;
    --w9: #adb373;
    --w10: #8f94cc;
  }

  @media (prefers-color-scheme: dark) {
    .sheet[data-sheet="5"] .sheet__walls {
      --w1: #85a6d6;
      --w2: #8cbd8f;
      --w3: #e09978;
      --w4: #ad99d4;
      --w5: #e3bd70;
      --w6: #75bab8;
      --w7: #e6a3ad;
      --w8: #d1786e;
      --w9: #adb373;
      --w10: #8f94cc;
    }
  }

  /* The square sits on top of the wall strip, which is what it does in play. */
  .sheet__square {
    position: relative;
  }

  .sheet__name {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
  }

  .sheet__pro {
    padding: 0.1rem 0.4rem;
    border: 1px solid var(--accent);
    border-radius: var(--radius);
    font-family: var(--font-mono);
    font-size: 0.66rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--accent);
  }

  /* ------------------------------------------------------ download */
  .download__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    margin-top: clamp(1.75rem, 1.2rem + 2vw, 2.5rem);
  }

  /* Not a button and not a link: a stated fact, sized to sit level with the
     one real control beside it. Nothing here is focusable, because there is
     nowhere for focus to go. */
  .status {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    min-height: 2.9rem;
    padding: 0.7rem 1.35rem;
    border: 1px dashed var(--rule-strong);
    border-radius: var(--radius);
    font-family: var(--font-mono);
    font-size: var(--step--1);
    color: var(--ink-2);
    cursor: default;
  }

  /* Decorative. The words already say what the state is; this only gives the
     line the one warm note the game spends on a reward. */
  .status__mark {
    flex: none;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent-flat);
  }

  .contact__actions {
    margin-top: clamp(1.75rem, 1.2rem + 2vw, 2.5rem);
  }

  /* ------------------------------------------------------- buttons */
  .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 2.9rem;
    padding: 0.7rem 1.35rem;
    border: 1px solid transparent;
    border-radius: var(--radius);
    font-size: var(--step--1);
    font-weight: 600;
    letter-spacing: 0.01em;
    text-decoration: none;
    transition: background-color 0.16s var(--ease), color 0.16s var(--ease),
      border-color 0.16s var(--ease);
  }

  .btn--primary {
    background: var(--ink);
    color: var(--paper);
    border-color: var(--ink);
  }

  .btn--primary:hover {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--paper);
  }

  @media (prefers-color-scheme: dark) {
    .btn--primary:hover {
      color: #161513;
    }
  }

  .btn--ghost {
    border-color: var(--rule-strong);
    color: var(--ink);
  }

  .btn--ghost:hover {
    border-color: var(--ink);
  }

  /* Not a link: the address exists as a constant, not yet as a mailbox. */
  .btn--disabled {
    border-color: var(--rule);
    color: var(--muted);
    font-family: var(--font-mono);
    font-weight: 500;
    cursor: default;
  }

  .kicker {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--accent);
  }

  /* ---------------------------------------------- document pages */
  .section--doc {
    border-top: 0;
  }

  .doc__head {
    max-width: var(--shell-narrow);
    margin-bottom: clamp(2rem, 1.4rem + 2.2vw, 3rem);
  }

  .doc__title {
    font-size: var(--step-3);
  }

  .doc__meta {
    margin-top: 0.9rem;
    font-family: var(--font-mono);
    font-size: var(--step--1);
    color: var(--muted);
  }

  .doc__lead {
    margin-top: 1.1rem;
    font-size: var(--step-1);
    line-height: 1.52;
    color: var(--ink-2);
  }

  .doc {
    max-width: var(--shell-narrow);
  }

  .doc__block + .doc__block {
    margin-top: clamp(2rem, 1.5rem + 1.8vw, 3rem);
  }

  .doc__block h2 {
    font-size: var(--step-2);
  }

  .doc__block h3 {
    margin-top: 1.75rem;
    font-size: var(--step-1);
  }

  .doc__block p {
    margin-top: 1rem;
    color: var(--ink-2);
  }

  .doc__list {
    margin-top: 1rem;
    display: grid;
    gap: 0.5rem;
    padding-inline-start: 1.1rem;
    list-style: disc;
    color: var(--ink-2);
  }

  /* Prose links take the accent — but a button placed in prose is still a
     button, and this rule outweighs .btn--primary's own colour. Without the
     exclusion the support page's mail button drew accent text on the ink
     ground: 2.91:1 by day, 1.93:1 by night, both well under AA. */
  .doc__block a:not(.btn) {
    color: var(--accent);
  }

  .support__mail {
    margin-top: 1.5rem;
  }

  .support__pending {
    margin-top: 1rem;
    max-width: var(--shell-narrow);
    font-size: var(--step--1);
    color: var(--muted);
  }

  /* -------------------------------------------------------- footer */
  .site-footer {
    padding-block: var(--section-space) 2.5rem;
    border-top: 1px solid var(--rule);
    /* Sunken, as before. The mark inside is transparent and picks this up. */
    background: var(--paper-sunk);
  }

  .site-footer__inner {
    display: grid;
    gap: clamp(2rem, 1.4rem + 2.4vw, 3.5rem);
  }

  .site-footer__brand {
    display: grid;
    gap: 0.9rem;
    align-content: start;
  }

  .site-footer__tagline {
    max-width: 22rem;
    font-size: var(--step--1);
    color: var(--muted);
  }

  /* Four groups as a grid rather than a flex row: the columns can fall to 2×2
     on tablets without ever being squeezed into four narrow strips. */
  .site-footer__cols {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.75rem 1.25rem;
  }

  .site-footer__title {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    font-weight: 500;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 0.75rem;
  }

  .site-footer__col ul {
    display: grid;
    gap: 0.4rem;
  }

  .site-footer__col a {
    /* inline-block plus padding lifts every row past a 24px target without
       touching the type size — landscape phones are touch devices too, and
       they sit above the narrow-phone breakpoint. */
    display: inline-block;
    padding-block: 0.2rem;
    font-size: var(--step--1);
    text-decoration: none;
    color: var(--ink-2);
  }

  .site-footer__col a:hover {
    color: var(--accent);
  }

  .site-footer__base {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 1rem;
    margin-top: clamp(2.5rem, 1.8rem + 2.6vw, 4rem);
    padding-top: 1.5rem;
    border-top: 1px solid var(--rule);
    font-size: var(--step--1);
    color: var(--muted);
  }

  .site-footer__base a {
    text-decoration: none;
    color: inherit;
    display: inline-block;
    padding-block: 0.2rem;
  }

  /* Reveal-on-scroll. Only ever active when JS confirmed it is observing;
     without JS, or if the module fails, the failsafe drops js-reveal and
     everything below is inert. */
  .js-reveal .reveal {
    opacity: 0;
    transform: translateY(0.6rem);
  }

  .js-reveal .reveal.is-in {
    opacity: 1;
    transform: none;
    transition: opacity 0.5s var(--ease) calc(var(--i, 0) * 45ms),
      transform 0.5s var(--ease) calc(var(--i, 0) * 45ms);
  }

  /* Locked by nav.js while the drawer is open. Applied to the root element,
     because body-level overflow does not reliably stop the viewport. */
  .is-locked {
    overflow: hidden;
  }
}

/* ======================================================== utilities */
@layer utilities {
  .visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* ====================================================== breakpoints */

/* Wide enough to open out: three across for the lists, two for the split, and
   the footer's four groups on one axis. */
@media (min-width: 46rem) {
  .steps,
  .cards,
  .figures {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  /* Four sheets: two by two here, four across only when there is room. */
  .sheets {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .split {
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
  }

  .site-footer__inner {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .site-footer__cols {
    grid-template-columns: repeat(4, auto);
    gap: clamp(1.5rem, 0.9rem + 2.2vw, 3rem);
  }
}

@media (min-width: 64rem) {
  .sheets {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* Below the two-column threshold the hero stacks: text first, mark under it. */
@media (max-width: 60rem) {
  .hero__inner {
    grid-template-columns: 1fr;
    gap: clamp(2rem, 1.4rem + 3vw, 3rem);
  }

  .hero__mark {
    justify-self: start;
    max-width: 22rem;
  }
}

@media (max-width: 56rem) {
  .site-nav {
    display: none;
  }

  .nav-toggle {
    display: block;
  }
}

@media (min-width: 56.0625rem) {
  .drawer {
    display: none;
  }
}

/* Short landscape: the hero must not eat the whole viewport, and the drawer
   list has to stay scrollable rather than overflow behind the fold. */
@media (orientation: landscape) and (max-height: 30rem) {
  .hero {
    padding-block: 2.25rem 2.75rem;
  }

  .hero__title {
    font-size: clamp(1.9rem, 1.2rem + 4vw, 2.6rem);
  }

  .hero__lead {
    font-size: var(--step-0);
  }

  /* Two columns even below the desktop threshold: a phone held sideways has
     width to spare and no height at all. */
  .hero__inner {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
    align-items: center;
    gap: 1.75rem;
  }

  .hero__mark {
    justify-self: center;
    max-width: 15rem;
  }

  .drawer__list a {
    font-size: var(--step-1);
    padding-block: 0.4rem;
  }

  .drawer__body {
    gap: 1.25rem;
  }
}

/* ============================================ hero entrance
 *
 * Short and once only. The `js` class is set by the inline script in the head
 * before the first paint, so these run whether or not the module ever arrives,
 * and the text is legible from its first frame — it rises a few pixels rather
 * than fading up from nothing.
 */
@media (prefers-reduced-motion: no-preference) {
  .js .hero .kicker,
  .js .hero__line,
  .js .hero__lead,
  .js .hero__actions,
  .js .hero__note {
    animation: rise 0.5s var(--ease) both;
  }

  .js .hero .kicker {
    animation-delay: 0.08s;
  }

  .js .hero__line {
    animation-delay: calc(0.18s + var(--i, 0) * 0.09s);
  }

  .js .hero__lead {
    animation-delay: 0.4s;
  }

  .js .hero__actions {
    animation-delay: 0.5s;
  }

  .js .hero__note {
    animation-delay: 0.58s;
  }

  /* The mark arrives from the side, the way the walls do. */
  .js .hero__mark {
    animation: mark-in 0.7s var(--ease) both;
    animation-delay: 0.26s;
  }

  @keyframes rise {
    from {
      opacity: 0;
      transform: translateY(0.5rem);
    }
    to {
      opacity: 1;
      transform: none;
    }
  }

  @keyframes mark-in {
    from {
      opacity: 0;
      transform: translateX(1.5rem);
    }
    to {
      opacity: 1;
      transform: none;
    }
  }
}

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

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }

  /* Never leave a reveal target stuck at opacity 0 when motion is off. */
  .js-reveal .reveal {
    opacity: 1;
    transform: none;
  }

  .hero .kicker,
  .hero__line,
  .hero__lead,
  .hero__actions,
  .hero__note,
  .hero__mark {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
}
