/**
 * DONAT — foundation.
 *
 * Loaded first by every document. It declares the layer order for the whole application, so
 * nothing below has to win a specificity argument: a rule in `portal` always beats a rule in
 * `ui`, whatever the selectors look like, and a one-class component rule can safely be
 * overridden by another one-class rule in a later layer. This is the fix for the previous
 * stylesheet set, where `.section` and `.cta` cancelled each other's padding depending on
 * which file happened to load last.
 *
 *   tokens  raw palette and scales — no selector here draws anything
 *   reset   the normalisation
 *   type    headings, prose, the eyebrow
 *   util    container, rhythm, screen-reader helpers, the finder mark
 *   ui      shared components (css/ui.css) — buttons, fields, cards, tables, notices
 *   site    the public pages (css/site.css)
 *   portal  sign-in and the dashboard (css/portal.css)
 *   motion  keyframes and reveals (css/motion.css), last so it can damp anything
 *
 * ## Colour
 *
 * The palette is the club's own, measured rather than invented. `tools/gen-icons.mjs` samples
 * the painted logo in `public/assets/icons/donat-logo.png` by radius and reports crust
 * #f5c396, glaze #eda9f0, highlight #fff3d0 and sprinkles #7fd4e8 / #8fdc8f / #ffd75e. Those
 * six values are the brand. Everything else is derived from them: the dark ground is the glaze
 * hue (297deg) driven to 7% lightness, and each state colour is a brand hue driven dark enough
 * to pass 4.5:1 as text on paper. No grey in this file is truly neutral — they all carry a
 * little of that hue, which is what keeps a screen of tables looking like it belongs to the
 * same object as the donut.
 *
 * ## Two grounds, one component layer
 *
 * Components are written against the semantic tokens below (`--fg`, `--bg`, `--line`,
 * `--accent`) and never against a raw palette value. `:root` sets those to the paper context;
 * `.on-ink` sets them to the plum context. A card, a button or a field is therefore correct on
 * either ground with no variant class, and a dark band on a light page is one class.
 *
 * ## Two schemes, and what `.on-ink` means in each
 *
 * The document is paper by default and ink when the reader asks for it — from the operating
 * system, or from the control in the top bar, which `js/theme.js` records as `data-theme` on
 * `<html>`. The dark block below is written twice on purpose: once behind
 * `prefers-color-scheme` for the reader who has never touched the control (and for the reader
 * with no JavaScript at all), and once behind the attribute for the reader who has. A media
 * query cannot hold half a selector list and `light-dark()` has no fallback path inside a
 * custom property — a browser without it computes `--bg` as invalid and the page loses its
 * background outright, with nothing to guard the failure. Two honest blocks and a checker that
 * refuses to let them drift is the trade that was made; see `tools/check-contrast.mjs`, which
 * fails the build if the two stop declaring the same set of names.
 *
 * `.on-ink` therefore stops meaning "plum" and starts meaning **one step toward the club's own
 * chrome, relative to whatever the page is**. On paper that is a jump to `--ink-800`; on ink it
 * is a lift to `--ink-700`, so the top bar and the footer stay a frame around the document
 * instead of dissolving into it. The three `--band*` pointers are the whole of that shift, which
 * is why `.on-ink` is still one rule and no component below has heard of any of this.
 */

@layer tokens, reset, type, util, ui, site, portal, motion;

@layer tokens {
  :root {
    /* ---- Brand, measured from the painted logo ------------------------------------- */
    --glaze: #eda9f0;
    --crust: #f5c396;
    --highlight: #fff3d0;
    --spr-cyan: #7fd4e8;
    --spr-mint: #8fdc8f;
    --spr-butter: #ffd75e;

    /* ---- Ink: the glaze hue at low lightness --------------------------------------- */
    /* `--ink-950` exists for the dark scheme alone. Sunken has to sit *below* the ground, and
       once the page itself is `--ink-900` there was nothing left underneath it. */
    --ink-950: #0a0710;
    --ink-900: #100b16;
    --ink-800: #17111f;
    --ink-700: #221a2e;
    --ink-600: #2e2440;
    --ink-500: #453a5c;
    --ink-400: #6a5c86;

    /* ---- Bond: cool paper, a violet whisper in it ---------------------------------- */
    --bond-000: #ffffff;
    --bond-050: #faf9fc;
    --bond-100: #f2f0f6;
    --bond-200: #e9e6f0;
    --bond-300: #ddd8e8;
    --bond-400: #c6bfd6;
    --bond-500: #9d95b0;
    --bond-600: #6b6480;

    /* ---- Brand hues driven dark enough to be text on paper ------------------------- */
    --glaze-ink: #8a2e93;
    --glaze-ink-deep: #6f1f77;
    --amber-ink: #8a4e12;
    --mint-ink: #16663c;
    --cyan-ink: #10566e;
    --berry-ink: #b3234a;

    /* ---- Semantic: the paper context ----------------------------------------------- */
    --bg: var(--bond-100);
    --bg-raise: var(--bond-000);
    --bg-sunken: var(--bond-200);
    --bg-inset: var(--bond-050);
    --fg: var(--ink-800);
    --fg-muted: var(--bond-600);
    --fg-faint: var(--bond-500);
    --line: var(--bond-300);
    --line-quiet: var(--bond-200);
    --line-strong: var(--bond-400);
    --accent: var(--glaze-ink);
    --accent-hover: var(--glaze-ink-deep);
    --accent-fg: var(--bond-000);
    --accent-wash: #f6e9f7;
    --accent-line: #d9a9de;
    --ring: var(--glaze-ink);
    --shadow-hue: 23 17 31;

    /**
     * ---- The band, and the two things a browser paints for us ------------------------
     *
     * `--band*` is what `.on-ink` reads. It is a pointer rather than a literal because it is
     * the one part of the plum context that has to move between schemes: a dark band on paper
     * is a jump, and the same band on ink is a lift. Everything else `.on-ink` sets is
     * identical in both, because a light-on-dark treatment does not care how dark.
     *
     * `--select-arrow` is a token for a reason that looks like an accident: the arrow is a
     * data URI with the fill baked into it, so it cannot read a custom property from inside
     * and there is no way to tint it in place. Three values, one per context, is the whole fix
     * — and it means `select.form-control` has exactly one rule again instead of one per
     * ground.
     *
     * The scrollbar pair is only read by the portal, which draws its own; the public pages
     * scroll the document and keep the browser's.
     */
    --band: var(--ink-800);
    --band-raise: var(--ink-700);
    --band-sunken: var(--ink-900);
    --band-faint: #93869f;

    --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%236b6480' d='M1.4 0 6 4.6 10.6 0 12 1.4 6 7.4 0 1.4z'/%3E%3C/svg%3E");
    --select-arrow-ink: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%23c3b6cd' d='M1.4 0 6 4.6 10.6 0 12 1.4 6 7.4 0 1.4z'/%3E%3C/svg%3E");

    /* The tick inside a checked `.form-checkbox`, baked-in fill for the same reason as the
       arrow above. Unlike the arrow this needs only one value: the box it sits on is `--accent`
       in every context, and the glaze pink is the same colour on paper and on ink. */
    --check-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='10' viewBox='0 0 13 10'%3E%3Cpath fill='none' stroke='%2317111f' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round' d='M1.5 5.2 4.7 8.4 11.5 1.6'/%3E%3C/svg%3E");

    --scrollbar-thumb: var(--bond-400);
    --scrollbar-thumb-quiet: var(--bond-300);

    /* What an overlay dims the page with. A token because 62% of `--ink-900` is a scrim on
       paper and nothing whatsoever on a page that is already `--ink-900` — the modal would
       still be readable, since its panel is a lighter surface, but the page behind it would not
       be dimmed at all and the boundary would stop reading as one. */
    --scrim: rgb(16 11 22 / 0.62);

    /* ---- State (paper context) ----------------------------------------------------- */
    --ok: var(--mint-ink);
    --ok-wash: #e6f4ec;
    --warn: var(--amber-ink);
    --warn-wash: #fbeedf;
    --bad: var(--berry-ink);
    --bad-wash: #fbe8ed;
    /* The label on a filled danger button. It is a token and not the `#fff` it used to be
       because `--bad` is a deep berry on paper and a light pink on ink, and white on light pink
       is 1.9:1 — a Delete nobody can read. This is the only state colour that gets a paired
       foreground, because it is the only one that is ever a button's whole ground. */
    --bad-fg: var(--bond-000);
    --info: var(--cyan-ink);
    --info-wash: #e4f1f6;

    /* ---- Branch identity, one sprinkle each ---------------------------------------- */
    --munchkin: var(--spr-butter);
    --munchkin-ink: #7a5300;
    --prober: var(--spr-cyan);
    --prober-ink: var(--cyan-ink);
    --cipher: var(--spr-mint);
    --cipher-ink: var(--mint-ink);

    /* ---- Type ---------------------------------------------------------------------- */
    --font-display: 'Bricolage Grotesque', 'Trebuchet MS', 'Segoe UI', system-ui, sans-serif;
    --font-sans: 'Instrument Sans', system-ui, -apple-system, 'Segoe UI', Roboto,
      'Helvetica Neue', sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono',
      monospace;

    --t-micro: 0.6875rem;
    --t-xs: 0.75rem;
    --t-sm: 0.8125rem;
    --t-base: 0.9375rem;
    --t-md: 1.0625rem;
    --t-lg: clamp(1.125rem, 1.05rem + 0.35vw, 1.3125rem);
    --t-xl: clamp(1.375rem, 1.2rem + 0.9vw, 1.875rem);
    --t-2xl: clamp(1.75rem, 1.35rem + 1.9vw, 2.75rem);
    --t-3xl: clamp(2.25rem, 1.55rem + 3.4vw, 4rem);
    --t-hero: clamp(3.25rem, 0.9rem + 11.5vw, 9.5rem);

    --lh-tight: 0.94;
    --lh-snug: 1.14;
    --lh-normal: 1.45;
    --lh-prose: 1.62;
    --track-tight: -0.028em;
    --track-label: 0.14em;

    /* ---- Space, rhythm, geometry --------------------------------------------------- */
    --s1: 0.25rem;
    --s2: 0.5rem;
    --s3: 0.75rem;
    --s4: 1rem;
    --s5: 1.5rem;
    --s6: 2rem;
    --s7: 3rem;
    --s8: 4rem;
    --s9: 6rem;
    --s10: 8rem;

    --gutter: clamp(1.125rem, 4vw, 3rem);
    --maxw: 1200px;
    --maxw-prose: 62ch;
    /* Half a gap: see `.quiet-zone` in the util layer for why it is not the whole one. */
    --quiet: clamp(2.25rem, 4.5vw, 4.25rem);
    --topbar-h: 4.25rem;

    --r1: 3px;
    --r2: 6px;
    --r3: 10px;
    --r4: 16px;
    --r5: 24px;
    --r-pill: 999px;
    /* One QR module — the unit the hairline rules and the scanner reticle are built from. */
    --module: 6px;

    --sh1: 0 1px 2px rgb(var(--shadow-hue) / 0.06), 0 2px 6px rgb(var(--shadow-hue) / 0.04);
    --sh2: 0 2px 4px rgb(var(--shadow-hue) / 0.05), 0 12px 28px rgb(var(--shadow-hue) / 0.08);
    --sh3: 0 4px 8px rgb(var(--shadow-hue) / 0.06), 0 28px 64px rgb(var(--shadow-hue) / 0.16);

    --dur-1: 120ms;
    --dur-2: 220ms;
    --dur-3: 420ms;
    --dur-4: 780ms;
    --ease: cubic-bezier(0.2, 0.7, 0.25, 1);
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

    --z-base: 1;
    --z-raise: 10;
    --z-topbar: 60;
    --z-sheet: 70;
    --z-banner: 80;
    --z-modal: 90;
    --z-toast: 100;

    color-scheme: light;
    accent-color: #8a2e93;
  }

  /**
   * The dark ground.
   *
   * The page drops to `--ink-900` and cards rise to `--ink-800`, which is the inverse of the
   * paper relationship and the reason no component needed editing: a card is still "the surface
   * above the page", it is simply reached by going up rather than down. Sunken has to go below
   * the page, which is what `--ink-950` is for.
   *
   * Everything from `--fg` down is the plum treatment the `.on-ink` band has always used, with
   * two changes. `--fg-faint` is lifted from `#93869f` to `#9d90aa`: the original measures
   * 4.4:1 on `--ink-600`, which is a surface that only exists in this scheme, and a placeholder
   * that fails on one ground in one theme is exactly the kind of thing nobody finds by looking.
   * And the three shadows carry real alpha here — on paper a shadow is a soft grey, on ink it
   * is the only thing separating a modal from the page behind it, because `--shadow-hue` is
   * black and 6% of black on near-black is nothing at all.
   *
   * ## Written twice, on purpose
   *
   * The first block is the reader who has never touched the control, including the one with no
   * JavaScript; the second is the reader who has. They must stay identical, and
   * `tools/check-contrast.mjs` fails the build if they stop declaring the same set of names.
   * See the note at the top of this file for why this is not one `light-dark()` block.
   */
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme='light']) {
      --bg: var(--ink-900);
      --bg-raise: var(--ink-800);
      --bg-sunken: var(--ink-950);
      --bg-inset: var(--ink-800);
      --fg: #f7f2f8;
      --fg-muted: #c3b6cd;
      --fg-faint: #9d90aa;
      --line: rgb(255 255 255 / 0.13);
      --line-quiet: rgb(255 255 255 / 0.07);
      --line-strong: rgb(255 255 255 / 0.26);
      --accent: var(--glaze);
      --accent-hover: #f7cbf9;
      --accent-fg: var(--ink-900);
      --accent-wash: rgb(237 169 240 / 0.12);
      --accent-line: rgb(237 169 240 / 0.42);
      --ring: var(--glaze);
      --shadow-hue: 0 0 0;
      --sh1: 0 1px 2px rgb(var(--shadow-hue) / 0.32), 0 2px 6px rgb(var(--shadow-hue) / 0.24);
      --sh2: 0 2px 4px rgb(var(--shadow-hue) / 0.34), 0 12px 28px rgb(var(--shadow-hue) / 0.46);
      --sh3: 0 4px 8px rgb(var(--shadow-hue) / 0.4), 0 28px 64px rgb(var(--shadow-hue) / 0.64);

      --ok: var(--spr-mint);
      --ok-wash: rgb(143 220 143 / 0.14);
      --warn: var(--crust);
      --warn-wash: rgb(245 195 150 / 0.14);
      --bad: #ff9db4;
      --bad-wash: rgb(255 157 180 / 0.14);
      --bad-fg: var(--ink-900);
      --info: var(--spr-cyan);
      --info-wash: rgb(127 212 232 / 0.14);

      --munchkin-ink: var(--spr-butter);
      --prober-ink: var(--spr-cyan);
      --cipher-ink: var(--spr-mint);

      --band: var(--ink-700);
      --band-raise: var(--ink-600);
      --band-sunken: var(--ink-800);
      --band-faint: #9d90aa;

      --select-arrow: var(--select-arrow-ink);

      --scrollbar-thumb: var(--ink-500);
      --scrollbar-thumb-quiet: var(--ink-600);

      --scrim: rgb(0 0 0 / 0.74);

      color-scheme: dark;
      accent-color: var(--glaze);
    }
  }

  :root[data-theme='dark'] {
    --bg: var(--ink-900);
    --bg-raise: var(--ink-800);
    --bg-sunken: var(--ink-950);
    --bg-inset: var(--ink-800);
    --fg: #f7f2f8;
    --fg-muted: #c3b6cd;
    --fg-faint: #9d90aa;
    --line: rgb(255 255 255 / 0.13);
    --line-quiet: rgb(255 255 255 / 0.07);
    --line-strong: rgb(255 255 255 / 0.26);
    --accent: var(--glaze);
    --accent-hover: #f7cbf9;
    --accent-fg: var(--ink-900);
    --accent-wash: rgb(237 169 240 / 0.12);
    --accent-line: rgb(237 169 240 / 0.42);
    --ring: var(--glaze);
    --shadow-hue: 0 0 0;
    --sh1: 0 1px 2px rgb(var(--shadow-hue) / 0.32), 0 2px 6px rgb(var(--shadow-hue) / 0.24);
    --sh2: 0 2px 4px rgb(var(--shadow-hue) / 0.34), 0 12px 28px rgb(var(--shadow-hue) / 0.46);
    --sh3: 0 4px 8px rgb(var(--shadow-hue) / 0.4), 0 28px 64px rgb(var(--shadow-hue) / 0.64);

    --ok: var(--spr-mint);
    --ok-wash: rgb(143 220 143 / 0.14);
    --warn: var(--crust);
    --warn-wash: rgb(245 195 150 / 0.14);
    --bad: #ff9db4;
    --bad-wash: rgb(255 157 180 / 0.14);
    --bad-fg: var(--ink-900);
    --info: var(--spr-cyan);
    --info-wash: rgb(127 212 232 / 0.14);

    --munchkin-ink: var(--spr-butter);
    --prober-ink: var(--spr-cyan);
    --cipher-ink: var(--spr-mint);

    --band: var(--ink-700);
    --band-raise: var(--ink-600);
    --band-sunken: var(--ink-800);
    --band-faint: #9d90aa;

    --select-arrow: var(--select-arrow-ink);

    --scrollbar-thumb: var(--ink-500);
    --scrollbar-thumb-quiet: var(--ink-600);

    --scrim: rgb(0 0 0 / 0.74);

    color-scheme: dark;
    accent-color: var(--glaze);
  }

  /**
   * Paper, asked for outright.
   *
   * The dark block above is skipped for this document, so the only thing left to say is the
   * scheme itself — without it a reader who has chosen light on a dark-mode phone still gets
   * dark scrollbars, dark form controls and a dark `<select>` popup from the browser.
   */
  :root[data-theme='light'] {
    color-scheme: light;
  }

  /**
   * The plum context.
   *
   * Applied to the hero, to any full-bleed dark band, and to the slide-over menu. Only the
   * semantic tokens change, so every component inside is already correct — there is no
   * `.card--dark` anywhere in this application and there should never need to be one.
   *
   * On paper this is a jump to `--ink-800`. In the dark scheme the page is already ink, so it
   * is a *lift* to `--ink-700` instead, and the top bar and footer go on reading as a frame
   * around the document rather than merging with it. The three `--band*` pointers above are the
   * entire difference; every other declaration here is a light-on-dark treatment that does not
   * care how dark the ground underneath it is.
   *
   * `color-scheme: dark` stays because it is about the *browser's* furniture — the `<select>`
   * popup, a scrollbar inside the sheet — and that furniture is dark on both grounds. It is
   * deliberately not what selects the values below; those come from the pointers, which are
   * resolved on `:root` where the document's real scheme lives.
   */
  .on-ink {
    --bg: var(--band);
    --bg-raise: var(--band-raise);
    --bg-sunken: var(--band-sunken);
    --bg-inset: var(--band-raise);
    --fg: #f7f2f8;
    --fg-muted: #c3b6cd;
    --fg-faint: var(--band-faint);
    --line: rgb(255 255 255 / 0.13);
    --line-quiet: rgb(255 255 255 / 0.07);
    --line-strong: rgb(255 255 255 / 0.26);
    --accent: var(--glaze);
    --accent-hover: #f7cbf9;
    --accent-fg: var(--ink-900);
    --accent-wash: rgb(237 169 240 / 0.12);
    --accent-line: rgb(237 169 240 / 0.42);
    --ring: var(--glaze);
    --shadow-hue: 0 0 0;

    --ok: var(--spr-mint);
    --ok-wash: rgb(143 220 143 / 0.14);
    --warn: var(--crust);
    --warn-wash: rgb(245 195 150 / 0.14);
    --bad: #ff9db4;
    --bad-wash: rgb(255 157 180 / 0.14);
    --bad-fg: var(--ink-900);
    --info: var(--spr-cyan);
    --info-wash: rgb(127 212 232 / 0.14);

    --munchkin-ink: var(--spr-butter);
    --prober-ink: var(--spr-cyan);
    --cipher-ink: var(--spr-mint);

    --select-arrow: var(--select-arrow-ink);
    --scrollbar-thumb: var(--ink-500);
    --scrollbar-thumb-quiet: var(--ink-600);

    --scrim: rgb(0 0 0 / 0.74);

    color-scheme: dark;
    background-color: var(--bg);
    color: var(--fg);
  }
}

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

  * {
    margin: 0;
  }

  html {
    /**
     * The page's own ground, not the plum.
     *
     * This is what shows past the ends of the document: under a page shorter than the viewport,
     * and in the overscroll gutter. It was `--ink-900`, which turned the short sign-in page into a
     * light document with a black strip under its footer. `--bg` is seamless there in both
     * schemes — paper on paper, ink on ink — and the only place it is visible on the marketing
     * pages is when someone overscrolls past a dark footer, which is a far cheaper wrong than a
     * stray band in the middle of the page. It is deliberately `--bg` and not `--bond-100`: the
     * literal was correct until there was a second ground for it to be wrong on.
     */
    background: var(--bg);
    scroll-behavior: smooth;
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
  }

  body {
    /* `clip`, not `hidden`: hidden on the body makes the document itself the scroll container
       and breaks `position: sticky` on the top bar in Safari. */
    min-height: 100svh;
    overflow-x: clip;
    background: var(--bg);
    color: var(--fg);
    font-family: var(--font-sans);
    font-size: var(--t-md);
    line-height: var(--lh-prose);
    font-synthesis-weight: none;
    text-rendering: optimizeLegibility;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }

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

  img,
  video {
    height: auto;
  }

  button,
  input,
  select,
  textarea {
    color: inherit;
    font: inherit;
    letter-spacing: inherit;
  }

  button,
  [role='button'],
  label,
  summary {
    cursor: pointer;
  }

  button {
    border: 0;
    background: none;
    -webkit-tap-highlight-color: transparent;
  }

  a {
    color: inherit;
    text-decoration: none;
  }

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

  table {
    width: 100%;
    border-collapse: collapse;
  }

  fieldset {
    border: 0;
  }

  ::selection {
    background: var(--accent);
    color: var(--accent-fg);
  }

  /* One ring for the whole application. It uses `--ring`, which flips with the surface, so it
     stays visible on plum and on paper without a second rule. */
  :focus-visible {
    outline: 2px solid var(--ring);
    outline-offset: 3px;
  }

  :target {
    scroll-margin-top: calc(var(--topbar-h) + var(--s5));
  }

  [hidden] {
    display: none !important;
  }

  /* Set on <body> while the slide-over menu or a dialog owns the screen. */
  .is-locked {
    overflow: hidden;
  }

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

@layer type {
  h1,
  h2,
  h3,
  h4,
  h5,
  h6 {
    color: var(--fg);
    font-family: var(--font-display);
    font-weight: 600;
    font-optical-sizing: auto;
    line-height: var(--lh-snug);
    letter-spacing: var(--track-tight);
    text-wrap: balance;
  }

  h1 {
    font-size: var(--t-3xl);
    font-weight: 700;
    line-height: var(--lh-tight);
  }

  h2 {
    font-size: var(--t-2xl);
  }

  h3 {
    font-size: var(--t-xl);
  }

  h4 {
    font-size: var(--t-lg);
    letter-spacing: -0.015em;
  }

  h5,
  h6 {
    font-size: var(--t-base);
    letter-spacing: 0;
  }

  p {
    text-wrap: pretty;
  }

  strong,
  b {
    color: var(--fg);
    font-weight: 600;
  }

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

  /* An unclassed link inside prose. Anything with a class styles itself. */
  a:not([class]) {
    color: var(--accent);
    text-decoration: underline;
    text-decoration-thickness: from-font;
    text-underline-offset: 0.2em;
  }

  a:not([class]):hover {
    text-decoration-thickness: 2px;
  }

  /**
   * The eyebrow.
   *
   * Mono, letterspaced, and preceded by a single filled module — the smallest piece of the QR
   * language this design is built from. It says what *kind* of thing follows, which is the one
   * job a label has.
   */
  .eyebrow {
    display: flex;
    align-items: center;
    gap: var(--s2);
    color: var(--fg-muted);
    font-family: var(--font-mono);
    font-size: var(--t-micro);
    font-weight: 500;
    line-height: 1;
    letter-spacing: var(--track-label);
    text-transform: uppercase;
  }

  .eyebrow::before {
    content: '';
    flex: 0 0 auto;
    width: var(--module);
    height: var(--module);
    border-radius: 1px;
    background: var(--accent);
  }

  .eyebrow--bare::before {
    display: none;
  }

  .display {
    font-family: var(--font-display);
    font-weight: 700;
    line-height: var(--lh-tight);
    letter-spacing: var(--track-tight);
  }

  .lede {
    max-width: var(--maxw-prose);
    color: var(--fg-muted);
    font-size: var(--t-lg);
    line-height: var(--lh-normal);
  }

  .prose {
    max-width: var(--maxw-prose);
  }

  .prose > * + * {
    margin-top: var(--s4);
  }

  .prose p,
  .prose li {
    color: var(--fg-muted);
  }

  .prose a {
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 0.2em;
  }

  .mono {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
  }

  .num {
    font-variant-numeric: tabular-nums;
  }
}

@layer util {
  .container {
    width: 100%;
    max-width: calc(var(--maxw) + 2 * var(--gutter));
    margin-inline: auto;
    padding-inline: var(--gutter);
  }

  .container--wide {
    max-width: calc(1440px + 2 * var(--gutter));
  }

  .container--narrow {
    max-width: calc(840px + 2 * var(--gutter));
  }

  /**
   * The vertical rhythm.
   *
   * Named for the QR term: the empty margin a symbol needs before a reader can find its edges at
   * all. Sections get one; nothing else does, and no section in any later layer sets its own
   * `padding-block` — that rule is what stopped `.section` and `.cta` cancelling each other out
   * in the stylesheet this replaced.
   *
   * The value is half a gap, not a whole one, because two stacked sections each contribute their
   * own. Adjacent sections therefore sit `2 * --quiet` apart, which is the intended rhythm, while
   * a section that paints its own ground — the footer, a page head, anything `.on-ink` — still
   * carries `--quiet` of its own inside the band. Collapsing the second section's top edge
   * instead looked tidier in the flow but pushed whatever came first inside a dark band flush
   * against the top edge of that band, which is worse than the doubling it was fixing.
   */
  .quiet-zone {
    padding-block: var(--quiet);
  }

  .quiet-zone--half {
    padding-block: calc(var(--quiet) * 0.6);
  }

  .quiet-zone--top {
    padding-block: var(--quiet) 0;
  }

  .quiet-zone--bottom {
    padding-block: 0 var(--quiet);
  }

  .stack {
    display: flex;
    flex-direction: column;
    gap: var(--stack-gap, var(--s4));
  }

  .cluster {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--cluster-gap, var(--s3));
  }

  /**
   * The finder mark.
   *
   * The three concentric squares a QR reader looks for to orient itself: 7 modules across, a
   * 5-module light gap, a 3-module core. Drawn at the real ratio from one variable.
   *
   * It marks the head of a section, and it is used *instead of* 01 / 02 / 03 because these
   * sections are not a sequence — nothing is read in order, and numbering them would claim
   * otherwise. A finder pattern means "the content starts here", which is what a section head
   * actually says.
   */
  .finder {
    --finder-m: 3px;
    /* `display` matters: this is a `<span>`, and an inline box ignores width and height outright.
       It happens to be blockified wherever it is used today because every parent is a flex row, but
       a mark whose geometry depends on its container is a mark that will one day render as a 6px
       tall bar somewhere. */
    position: relative;
    display: block;
    flex: 0 0 auto;
    width: calc(var(--finder-m) * 7);
    height: calc(var(--finder-m) * 7);
    border: var(--finder-m) solid currentcolor;
    border-radius: 1px;
    color: var(--accent);
  }

  .finder::after {
    position: absolute;
    inset: var(--finder-m);
    border-radius: 1px;
    background: currentcolor;
    content: '';
  }

  .finder--lg {
    --finder-m: 5px;
  }

  /* Used by the dashboard widgets (table captions, loading announcements) and the skip link. */
  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .skip-link {
    position: fixed;
    top: var(--s3);
    left: var(--s3);
    z-index: var(--z-toast);
    padding: var(--s2) var(--s4);
    border-radius: var(--r-pill);
    /* `--accent` and not `--glaze-ink`: the literal resolved to the same deep purple on paper and
       stayed there in the dark scheme, where the accent is the light glaze and the label under it
       has to be dark. The pair is the same colour it always was on a light page. */
    background: var(--accent);
    box-shadow: var(--sh2);
    color: var(--accent-fg);
    font-size: var(--t-sm);
    font-weight: 600;
    transform: translateY(-250%);
    transition: transform var(--dur-2) var(--ease-out);
  }

  .skip-link:focus-visible {
    transform: none;
  }

  .rule {
    height: 1px;
    border: 0;
    background: var(--line);
  }

  /* A hairline made of modules rather than a solid line. Used between the dense parts of the
     portal, where a solid rule reads as a table border and this reads as a seam. */
  .rule--modules {
    height: 3px;
    background: repeating-linear-gradient(
      to right,
      var(--line-strong) 0 3px,
      transparent 3px 6px
    );
  }

  .text-center {
    text-align: center;
  }

  .full-bleed {
    width: 100vw;
    margin-inline: calc(50% - 50vw);
  }
}

/**
 * Paper, on paper.
 *
 * Unlayered and last in this file, so it beats every layer without `!important` — the same
 * device the card-print block at the end of portal.css uses, and for the same reason: nothing in
 * `ui`, `site`, `portal` or `motion` may put the dark scheme back.
 *
 * A sheet of member cards printed from a dark-mode dashboard was ink on ink. The card faces
 * themselves are hardcoded white and always were, but the QR tile's quiet zone, the trim
 * hairlines and anything that reached a semantic token would have come out of the dark block —
 * and a symbol whose quiet zone is not the light half of the decoder's comparison is a card that
 * fails at a door. Resetting the context rather than patching each rule means anything printed
 * later is covered too.
 *
 * `color-scheme: light` is what stops the browser inverting form controls and scrollbars in the
 * print rendering; the token reset is what stops *our* colours doing it.
 */
@media print {
  :root {
    --bg: var(--bond-100);
    --bg-raise: var(--bond-000);
    --bg-sunken: var(--bond-200);
    --bg-inset: var(--bond-050);
    --fg: var(--ink-800);
    --fg-muted: var(--bond-600);
    --fg-faint: var(--bond-500);
    --line: var(--bond-300);
    --line-quiet: var(--bond-200);
    --line-strong: var(--bond-400);
    --accent: var(--glaze-ink);
    --accent-hover: var(--glaze-ink-deep);
    --accent-fg: var(--bond-000);
    --accent-wash: #f6e9f7;
    --accent-line: #d9a9de;
    --ring: var(--glaze-ink);
    --shadow-hue: 23 17 31;

    --ok: var(--mint-ink);
    --ok-wash: #e6f4ec;
    --warn: var(--amber-ink);
    --warn-wash: #fbeedf;
    --bad: var(--berry-ink);
    --bad-wash: #fbe8ed;
    --bad-fg: var(--bond-000);
    --info: var(--cyan-ink);
    --info-wash: #e4f1f6;

    --munchkin-ink: #7a5300;
    --prober-ink: var(--cyan-ink);
    --cipher-ink: var(--mint-ink);

    --band: var(--ink-800);
    --band-raise: var(--ink-700);
    --band-sunken: var(--ink-900);
    --band-faint: #93869f;

    --scrim: rgb(16 11 22 / 0.62);

    color-scheme: light;
  }

  html,
  body {
    background: var(--bond-000);
  }
}
