/**
 * Self-hosted variable faces, carried over from principle-stash-landing.
 *
 * All four are SIL Open Font License 1.1. OFL §2 requires every copy to ship the licence
 * text, which is why `public/fonts/OFL-Fraunces.txt` and `OFL-Geist.txt` sit beside the
 * `.woff2` files rather than being assumed from node_modules.
 *
 * ## The fourth face is the one that matters here
 *
 * The Geist Mono subset inherited from principle-stash stops at U+2212 — measured, it carries
 * 0 of the 32 Block Elements and 0 of the 128 Box Drawing glyphs. On a site whose design is
 * ASCII, `░▒▓█` and `┌─┐│` would every one of them have rendered as tofu.
 *
 * Geist Mono publishes a `symbols2` subset that is 5.8 KB and contains all 160 of them, from
 * the same family under the same licence. It is declared as a second face for the same
 * family with a `unicode-range`, which is what routes those codepoints to it while everything
 * else keeps using the main file — one family name, two files, no fallback seam.
 *
 * It is preloaded alongside the others rather than left to lazy-load: the dependency tree and
 * the bar charts are above the fold on the pages that matter, so treating it as optional
 * would just mean a visible reflow on every first paint.
 *
 * ## The URLs are a deployment contract
 *
 * `/fonts/*` is absolute and unhashed, and both tiers depend on that. The files themselves are
 * in `packages/web/public/` because Astro needs real files in its `publicDir`; the console is
 * served by a different process that has no asset pipeline at all, and reaches them because
 * `Caddyfile` routes `/fonts/*` to the web tier for every request regardless of which upstream
 * rendered the page. Same origin from the browser's side, so the console's stricter
 * `default-src 'self'` covers it without an exception.
 *
 * Letting Vite fingerprint these instead would be the better cache story and would break the
 * console, which cannot know the hash. Unhashed is the deliberate trade; `font-display: swap`
 * and a short max-age carry the cost.
 */

@font-face {
  font-family: "Geist";
  src: url("/fonts/geist-sans-var.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "Geist Mono";
  src: url("/fonts/geist-mono-var.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* Box Drawing (U+2500–257F) and Block Elements (U+2580–259F). */
@font-face {
  font-family: "Geist Mono";
  src: url("/fonts/geist-mono-symbols2.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+2500-259F;
}

@font-face {
  font-family: "Fraunces";
  src: url("/fonts/fraunces-var.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/**
 * The italic, and the only STATIC face here.
 *
 * Fraunces ships its italic as a separate family — the variable roman above carries `opsz` and
 * `wght` and no `ital` axis — so `font-style: italic` against it would have produced a
 * synthetic oblique: the roman mechanically slanted, with none of the real italic's redrawn
 * a, e, g or its −16 degree fitted forms. At the display size the one italic on the site is set
 * at, that is visible.
 *
 * Shipping the variable italic cost 82 KB, and 71 KB even subset to Latin, because a variable
 * font's weight is mostly its `gvar` deltas rather than its outlines. It is INSTANCED instead —
 * pinned at `opsz` 40 and `wght` 480, which is exactly what the one rule that uses it already
 * asked for — and that drops it to under 20 KB. The cost of pinning is that this face cannot
 * track its optical size the way the roman does; the roman is display type at many sizes, and
 * this is one line at one size.
 *
 * Deliberately NOT preloaded, unlike the four faces in `Base.astro`. It sets a single line at
 * the very bottom of one page, so paying for it in the critical path would be backwards.
 */
@font-face {
  font-family: "Fraunces";
  src: url("/fonts/fraunces-italic.woff2") format("woff2");
  font-weight: 100 900;
  font-style: italic;
  font-display: swap;
}

/*
 * ## Metric-matched fallbacks
 *
 * What a reader sees for the fraction of a second before a face above lands: a system font
 * wrapped in a face whose size and vertical metrics are overridden to occupy the same box as
 * the webfont, so the swap repaints glyphs without moving a line break. Each token stack in
 * `tokens.css` names its fallback immediately after its webfont.
 *
 * Measured, never hand-edited: `bun run fonts:measure` sets the site's own sentences in both
 * fonts in Chrome and writes this block. Each face records the hash of the file it was measured
 * against, and `check:fonts` fails when that file changes without a re-measure. This replaces
 * `font-size-adjust: from-font`, which matched x-height and left advance widths — the metric
 * that decides where a line breaks — alone.
 */
/* fallback-metrics:begin */
/* geist-sans-var.woff2 sha256:19f9c92546aa300c312235e3125af1b81394d8db9a4bc4a425cd5b641d2d54e1 · measured at 400 against local("Arial") */
@font-face {
  font-family: "Geist Fallback";
  src: local("Arial"), local("ArialMT");
  font-weight: 100 500;
  font-style: normal;
  size-adjust: 103.06%;
  ascent-override: 98%;
  descent-override: 28.14%;
  line-gap-override: 0%;
}

/* geist-sans-var.woff2 sha256:19f9c92546aa300c312235e3125af1b81394d8db9a4bc4a425cd5b641d2d54e1 · measured at 600 against local("Arial Bold") */
@font-face {
  font-family: "Geist Fallback";
  src: local("Arial Bold"), local("Arial-BoldMT");
  font-weight: 600 900;
  font-style: normal;
  size-adjust: 99.87%;
  ascent-override: 101.13%;
  descent-override: 29.04%;
  line-gap-override: 0%;
}

/* geist-mono-var.woff2 sha256:af61b969e7f999969f6af576e584ee85dca301a008a76be1251d172d56b9904c · measured at 400 against local("Courier New") */
@font-face {
  font-family: "Geist Mono Fallback";
  src: local("Courier New"), local("CourierNewPSMT");
  font-weight: 100 900;
  font-style: normal;
  size-adjust: 99.98%;
  ascent-override: 101.02%;
  descent-override: 29%;
  line-gap-override: 0%;
}

/* fraunces-var.woff2 sha256:7234ed860a9cc83045413c4faee63c960a8f2d1917adcf728119307d56e0d783 · measured at 480 against local("Georgia") */
@font-face {
  font-family: "Fraunces Fallback";
  src: local("Georgia");
  font-weight: 100 900;
  font-style: normal;
  size-adjust: 86.25%;
  ascent-override: 113.62%;
  descent-override: 30.15%;
  line-gap-override: 0%;
}

/* fraunces-italic.woff2 sha256:84f7522b3ebbaeabb4e80d554a227d48cb55efa563dceb63ac144193f06aa4f3 · measured at 480 against local("Georgia Italic") */
@font-face {
  font-family: "Fraunces Fallback";
  src: local("Georgia Italic"), local("Georgia-Italic");
  font-weight: 100 900;
  font-style: italic;
  size-adjust: 96.28%;
  ascent-override: 101.79%;
  descent-override: 27.01%;
  line-gap-override: 0%;
}
/* fallback-metrics:end */

/**
 * The brand palette is the ANSI table.
 *
 * `packages/client/src/style.ts` lets an advertiser pick from exactly four foreground slots —
 * cyan, blue, green, magenta — and excludes red and yellow on purpose, because in a terminal
 * those mean error and warning. An ad rendered in red is impersonating a build failure.
 *
 * That constraint is the identity, so the site is built from the same table rather than from
 * a palette invented alongside it:
 *
 *   - cyan / blue / green / magenta are the working accent range. Green keeps the meaning it
 *     already has in the product: paid, settled.
 *   - red and yellow appear ONLY for error and warning — exactly what they mean in a
 *     terminal, which is exactly why an advertiser may not use them. The site obeys the rule
 *     it enforces.
 *   - black and white are the ground.
 *
 * Slots are numbered, not named, because the number is the thing the product actually emits:
 * `--ansi-6` is what `ESC[36m` selects. `/ads` renders each creative in its real slot colour
 * with the escape code beside it, so the page documents the palette it is drawn in.
 *
 * Values are tuned per ground rather than shared. A terminal theme does the same thing — the
 * cyan in a light profile is not the cyan in a dark one — and a single value legible on both
 * would be washed out on one and glaring on the other. Every pairing below is checked against
 * its own ground by `scripts/check-contrast.ts`.
 *
 * ## Why this lives in `shared`, and why it is a plain `:root` block
 *
 * Two processes render this product's surfaces: the Astro site and the Hono advertiser console.
 * They have no build step in common — the site's stylesheet is a content-hashed Vite output that
 * only exists after `astro build`, so the console cannot link it — and the palette duly drifted:
 * the console shipped fourteen hardcoded hexes of its own, a different green for "paid" and a
 * different amber for "pending", on the operator screen where those two words decide whether
 * somebody has been paid.
 *
 * The meanings are the part that must not diverge. `packages/client/src/style.ts` permits four
 * slots and reserves red and yellow, and every surface that renders money is agreeing to the
 * same table — which makes this a contract between the ends, like everything else here, not
 * decoration belonging to whichever tier happened to need it first.
 *
 * It was a Tailwind `@theme` block while it lived in the web package. That was doing no work:
 * the site uses zero Tailwind colour utilities and reaches for `var(--color-*)` in scoped
 * `<style>` blocks 131 times, so `@theme` was an elaborate way to declare custom properties.
 * `:root` declares them too, and — the point of the move — a browser understands it. An unknown
 * at-rule is skipped whole, so an `@theme` block linked into the console would have dropped
 * every declaration inside it and left the page unstyled.
 */
:root {
  /* ── The ground ─────────────────────────────────────────────────────── */
  --color-ansi-0: light-dark(#1b1f22, #14181a); /* black   — deepest surface */
  --color-ansi-7: light-dark(#5c666e, #c8d0cc); /* white   — body text on dark, muted on light */
  --color-ansi-8: light-dark(#7c858c, #5f6a69); /* br black — rules, dividers */
  --color-ansi-15: light-dark(#2a3033, #f2f6f3); /* br white — headings */

  /* ── Reserved: error and warning only ───────────────────────────────── */
  --color-ansi-1: light-dark(#b3202f, #f07784); /* red     — error */
  --color-ansi-3: light-dark(#8a6413, #e5c07b); /* yellow  — warning, pending */
  --color-ansi-9: light-dark(#c8323f, #ff939c);
  --color-ansi-11: light-dark(#9a6a08, #f2d49a);

  /* ── The working accent range (the four an advertiser may pick) ─────── */
  --color-ansi-2: light-dark(#0f7245, #56d698); /* green   — paid, settled */
  --color-ansi-4: light-dark(#1f4fc4, #7fa9f0); /* blue */
  --color-ansi-5: light-dark(#8f2bab, #cf9cf0); /* magenta */
  --color-ansi-6: light-dark(#0d6875, #5fd0cc); /* cyan    — the primary accent */
  --color-ansi-10: light-dark(#128551, #79e6b4);
  --color-ansi-12: light-dark(#3565d8, #9cc0ff);
  --color-ansi-13: light-dark(#a341bd, #e0b8ff);
  --color-ansi-14: light-dark(#0f7d8c, #84e6e2);

  /* ── Semantic aliases ────────────────────────────────────────────────
     Components reference these, never a slot number directly, so the meaning
     survives a palette change. The mapping is the load-bearing part. */
  --color-bg: light-dark(#fbfbf9, #0b0e0f);
  --color-surface: light-dark(#f3f4f1, #14181a);
  --color-surface-2: light-dark(#e9ebe7, #1c2123);
  --color-fg: var(--color-ansi-15);
  --color-muted: var(--color-ansi-7);
  /* The podium. Three colours that only ever sit on the leaderboard's top three, dark enough
     on paper and bright enough on the terminal ground to stay legible on both. */
  --color-gold: light-dark(#8a6a12, #d9b036);
  --color-silver: light-dark(#5f6670, #b9c0c9);
  --color-bronze: light-dark(#8a5320, #cd7f32);

  --color-line: light-dark(#e2e4df, #232829);
  --color-line-strong: light-dark(#cdd0ca, #333a3b);

  /* Brand accent is green (ansi-2), to match the O logotype. Was ansi-6 (cyan); cyan stays a
     live ANSI slot an advertiser can pick — it just no longer doubles as the site accent.
     Heads-up: ansi-2 is also --color-paid, so the accent and a "paid" state now read as the
     same green. If that conflates them anywhere, split one onto its own shade. */
  --color-accent: var(--color-ansi-2);
  --color-paid: var(--color-ansi-2);
  --color-pending: var(--color-ansi-3);
  --color-error: var(--color-ansi-1);

  /* The ASCII field sits behind text and must never compete with it — but it does have to be
     visible. The dark value needs roughly twice the alpha of the light one: a translucent
     green gains contrast against near-white far faster than against near-black, so matching
     the two numerically makes the dark field disappear. */
  /*
   * The two alphas were 0x2e light and 0x59 dark, on the reasoning that dark ink on white
   * carries further than light ink on black. It does — but the field is not a solid, it is
   * scattered glyphs, and at 0x2e the light theme had neither the density nor the weight to
   * read. Now the same alpha in both, with `TONE_LIGHT` doing the thinning instead: one lever
   * for how MUCH ink there is, one for how strong it is, rather than both at once.
   */
  --color-ascii: light-dark(#0f724559, #56d69859);

  /* ── Type ────────────────────────────────────────────────────────────
     Ported from principle-stash-landing so the two sites stay recognisably
     related. Fraunces is display only; body is Geist; ASCII is Geist Mono. */
  /* Each webfont is followed by its measured fallback (`fonts.css`), so the stack that renders
     before the swap occupies the same box as the one after it. `check:fonts` holds the order. */
  --font-sans:
    "Geist", "Geist Fallback", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono:
    "Geist Mono", "Geist Mono Fallback", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --font-serif:
    "Fraunces", "Fraunces Fallback", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia,
    serif;

  /*
   * The scale, which did not exist until it was audited.
   *
   * Fifty-four `font-size` declarations had grown into twenty distinct values between
   * 0.62rem and 1.45rem — 0.75 beside 0.76, 0.82 beside 0.84 beside 0.85, 1.15 beside 1.16.
   * Pairs that close are not decisions, they are drift: nobody chose 1.16 over 1.15, they
   * wrote a number without knowing the other one existed. Eleven steps carry all fifty-four
   * with nothing moving more than 5.3%, and `check:type` is what stops a twelfth appearing.
   *
   * Named by size rather than by role (`--text-sm`, not `--text-caption`) because the same
   * step legitimately sets a table cell, a badge and a footnote, and a role name would have
   * to lie about two of them.
   */
  /*
   * The console's body size. 15px, but expressed against the 16px root rather than as `15px`,
   * because a `px` body size is the one that a reader who has raised their browser's default
   * font size does not get to raise. Nothing sets `html { font-size }`, so `rem` here is the
   * browser default and 0.9375rem renders at exactly the 15px it replaced.
   */
  --text-root: 1rem;
  /* Raised from 0.9375rem (15px) with the whole scale behind it, about 7%: the body was set
     a step below the browser's default, which is a choice every reader who has already
     chosen one pays for twice. */

  --text-3xs: 0.66rem;
  --text-2xs: 0.73rem;
  --text-xs: 0.77rem;
  --text-sm: 0.83rem;
  --text-md: 0.88rem;
  --text-base: 0.91rem;
  --text-lg: 1.02rem;
  --text-xl: 1.12rem;
  --text-2xl: 1.24rem;
  --text-3xl: 1.55rem;

  /*
   * Fluid display sizes. Tokens for the same reason the fixed steps are: hand-written, the same
   * role had grown three slightly different clamps — a money figure at 2.4–4rem on one page
   * and 2.6–4.4rem on the next, a package name at two ranges in two files. Named for the job
   * rather than the size, unlike the fixed steps, because a fluid step is never shared by
   * unrelated elements: a figure does not borrow a title's size.
   *
   * `--text-quote` is the one tuned to a sentence: its ceiling is set by how the coda's English
   * quote breaks in the 46rem column, measured in `.coda__line`.
   */
  --text-figure: clamp(2.6rem, 10vw, 4.4rem);
  --text-title: clamp(1.9rem, 4.4vw, 3.1rem);
  --text-headline: clamp(1.35rem, 4vw, 2.1rem);
  --text-subtitle: clamp(1.15rem, 3.2vw, 1.6rem);
  --text-quote: clamp(1.3rem, 4.4vw, 2.125rem);

  /* The smallest a form field may render on a touch screen: below 16px, iOS zooms the page when
     the field takes focus. Read only by the floor in `composer.css`. */
  --text-field-floor: 1rem;

  /*
   * Leading. `--leading-none` is for single-line boxes where the line box should be the text
   * box — buttons, badges, the caret — and is deliberately unitless so it inherits as a ratio
   * rather than a length.
   */
  --leading-none: 1;
  --leading-flush: 1.12;
  --leading-tight: 1.2;
  --leading-snug: 1.3;
  --leading-normal: 1.4;
  --leading-relaxed: 1.5;
  --leading-body: 1.6;
  --leading-loose: 1.75;

  /*
   * Tracking. Display type is set tight because Fraunces at 4rem has too much air by default;
   * small caps and eyebrow labels are set wide because at 0.62rem the letterforms otherwise
   * touch. The four negative steps collapsed from -0.05/-0.04/-0.02/-0.015/-0.012/-0.01,
   * which is six values doing the work of three.
   */
  --tracking-tightest: -0.05em;
  --tracking-tighter: -0.04em;
  --tracking-tight: -0.02em;
  --tracking-normal: normal;
  /* Mono at small sizes wants a little air; it is also the masthead typewriter's cell gap. */
  --tracking-slight: 0.02em;
  --tracking-wide: 0.04em;
  --tracking-wider: 0.08em;
  --tracking-widest: 0.12em;
  --tracking-caps: 0.14em;

  /*
   * Spacing. Every margin, padding and gap on both tiers is one of these; `check:spacing` fails
   * on anything else. Moved here from the site's primitives stylesheet, where the console could
   * not see it, after an audit found 150 declarations holding 51 raw values — 0.3 beside 0.32
   * beside 0.34 beside 0.35rem — against nine steps nobody outside the primitives used.
   *
   * `2xs` is the one step added in the migration: pills, badges and keyboard hints carried
   * 0.1–0.15rem paddings that `xs` would have visibly fattened. Anything else between two steps
   * took the nearer one, and a tie took the smaller.
   */
  --space-2xs: 0.125rem;
  --space-xs: 0.25rem;
  --space-s: 0.5rem;
  --space-m: 0.75rem;
  --space-l: 1rem;
  --space-xl: 1.5rem;
  --space-2xl: 2rem;
  --space-3xl: 3rem;
  --space-4xl: 4rem;
  --space-5xl: 6rem;

  /* ── Motion ──────────────────────────────────────────────────────────
     From `web-animation-design`: ease-out for enter/exit, ease-in-out for
     on-screen movement, ease for hover. The built-in curves are too weak, so
     these are the named ones — same names as principle-stash. */
  --ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1);
  --ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);
  --ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1);

  --radius: 12px;
  --radius-sm: 8px;
  /*
   * The reading measure, and the table measure.
   *
   * A status line is up to 80 visible characters; at 0.85rem mono that is ~38rem for the
   * preview column alone, which is why `/ads` cannot use the prose width — at 46rem its table
   * needs 1019px and starts scrolling sideways, and the page's whole claim is that the line is
   * shown as it appears.
   *
   * 66rem, not the 74rem it was. 1019px is what the widest table actually asks for, measured;
   * 74rem gave it 1184 and the extra 165px bought nothing but a page visibly out of step with
   * every other one. `/leaderboard` was on this measure too and needed none of it — both its
   * tables fit the reading measure with no scrolling — so it is back on the prose width and
   * `/ads` is now the only page on the site that opts out.
   *
   * Shared with the advertiser console, whose campaign page uses the same escape hatch. Its
   * table is inside an `.overflow` scroller, so the narrower measure costs it a scrollbar at
   * worst rather than a broken layout.
   */
  --container: 46rem;
  --container-wide: 66rem;
}

/**
 * The vocabulary both tiers render in.
 *
 * Obrigado draws its surfaces from two processes: the Astro site (`packages/web`) and the Hono
 * advertiser console (`packages/server/src/views`). They share no build step — the site's
 * stylesheet is a content-hashed Vite output that does not exist until `astro build`, so the
 * console cannot link it — and for a while they shared no design either. The console had grown
 * its own copy of `.figures`, `.figure .value`, `.pill`, `table`/`th`/`td`, `.card` and
 * `.prose`: the same class names, written twice, drifting apart in the details.
 *
 * The drift was worst exactly where it mattered. Both files defined "paid" and "pending"
 * colours; neither pair matched. An operator reading a settlement figure on the console saw a
 * different green from the one that means settled everywhere else in the product, on the screen
 * where that distinction decides whether somebody has been paid.
 *
 * So this is the single copy, and the split between the tiers is now only about delivery: the
 * site `@import`s this file through Vite, the console reads it as text and serves it from a
 * route. What is NOT here is anything one surface alone has — the command palette, the hero,
 * the scroll reveals, the console's forms — because a shared file that accumulates one tier's
 * specifics stops being shared and becomes a place where everything is kept.
 *
 * Plain CSS with no at-rules a browser would skip, and no build step assumed, for the same
 * reason: one of the two consumers has neither.
 */

/* `light-dark()` reads this, and every colour in tokens.css is a `light-dark()` pair — so
   without it the whole palette resolves to its light branch regardless of preference. The site
   additionally pins a choice via `:root[data-theme]`; the console runs under `script-src
   'none'`, has no toggle to pin one with, and follows the operating system. */
:root {
  color-scheme: light dark;
}

/*
 * Scrollbars, thin and in the palette, on both tiers.
 *
 * The two properties do not behave the same way, and it matters. `scrollbar-color` INHERITS,
 * so the root declaration reaches every scroller under it. `scrollbar-width` DOES NOT — it was
 * verified here: with both set on `:root`, a code block's computed `scrollbar-color` was the
 * palette pair and its `scrollbar-width` was still `auto`. So the colour is set once and the
 * width is set on `*`, which is the only way the page's own scroller and the `.overflow`
 * tables and the code blocks all come out the same thickness.
 *
 * On `*` rather than a class for the same reason: a scroller added later is styled before
 * anyone remembers to style it.
 *
 * A transparent track rather than a toned one. At `thin` the track is a couple of pixels of
 * chrome running the full height of the page, and any colour we gave it would draw a line down
 * the edge of every screen — which is a border nobody asked for. Transparent lets the page's
 * own ground show through, so the thumb reads as the only moving part.
 *
 * `--color-line-strong` for the thumb: the same value the layout already uses for a divider
 * that has to be seen rather than felt. One resting colour and no hover state — the standard
 * properties have no selector for the thumb itself, and the nearest thing, `:root:hover`,
 * matches whenever the pointer is anywhere on the page, so it would light the thumb
 * permanently and retire the resting colour. The WebKit path below does get a real
 * `:hover` on the thumb, and uses it.
 *
 * `color-scheme` above is doing work here too — it is what makes the fallback native scrollbar
 * dark in dark mode on the engines that do not take our colours yet.
 */
:root {
  --scrollbar-thumb: var(--color-line-strong);
  --scrollbar-track: transparent;

  scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
}

/* The half that does not inherit. */
* {
  scrollbar-width: thin;
}

/*
 * Safari below 26.2 — `scrollbar-color` only became Baseline on 2025-12-12, and the site's
 * readers do not all update the day a browser ships. Guarded by `@supports not`, so an engine
 * that understands the standard properties above never sees these: WebKit honours both, and
 * the legacy pseudo-elements would otherwise win by specificity and undo the `thin`.
 *
 * 8px because `::-webkit-scrollbar` has no `thin` keyword — it takes a length, and 8 is what
 * `thin` measures to in the engines that do implement it. The custom properties cascade in
 * from the rule above rather than being restated.
 */
@supports not (scrollbar-color: auto) {
  ::-webkit-scrollbar {
    width: 8px;
    height: 8px;
  }

  ::-webkit-scrollbar-track {
    background: var(--scrollbar-track);
  }

  ::-webkit-scrollbar-thumb {
    border-radius: 4px;
    background: var(--scrollbar-thumb);
  }

  ::-webkit-scrollbar-thumb:hover {
    background: var(--color-muted);
  }

  /* Where two scrollers meet. Left alone it is a small raised square in the system's own
     colours, which is the one piece of native chrome that would still show through. */
  ::-webkit-scrollbar-corner {
    background: transparent;
  }
}

/*
 * A thin scrollbar in a low-contrast palette is the first thing to disappear for a reader who
 * has asked the OS for more contrast. `--color-fg` on `--color-surface` is the strongest pair
 * the palette holds, and the track stops being transparent so the trough is findable at all.
 */
@media (prefers-contrast: more) {
  :root {
    --scrollbar-thumb: var(--color-fg);
    --scrollbar-track: var(--color-surface);
  }
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  padding: var(--space-2xl) var(--space-l);
  background: var(--color-bg);
  color: var(--color-fg);
  font-family: var(--font-sans);
  font-size: var(--text-root);
  line-height: var(--leading-body);
  -webkit-font-smoothing: antialiased;
}

main {
  max-width: var(--container);
  margin: 0 auto;
}

main.wide {
  max-width: var(--container-wide);
}

a {
  color: inherit;
  text-underline-offset: 2px;
}

/*
 * A link inside a sentence is underlined, and set in the full text colour.
 *
 * The site's preflight strips `text-decoration` from every `a`, so "See the FAQ" and "the
 * moderation policy" were indistinguishable from the words around them on every page that
 * said them. Scoped to sentences, and to links with no class of their own (or only `.mono`, a
 * path set in code type mid-sentence), so the nav and footer (bare links directly in a `nav`),
 * table cells and titles keep their own treatment.
 * The colour lifts a link out of muted prose the way `.prose strong` lifts a phrase.
 */
main :is(p, blockquote, .prose li) a:is(:not([class]), .mono) {
  color: var(--color-fg);
  text-decoration: underline;
  text-decoration-color: var(--color-line-strong);
  transition:
    color 150ms ease,
    text-decoration-color 150ms ease;
}

@media (hover: hover) and (pointer: fine) {
  main :is(p, blockquote, .prose li) a:is(:not([class]), .mono):hover {
    color: var(--color-accent);
    text-decoration-color: currentColor;
  }
}

/* ── Type scale ──────────────────────────────────────────────────────────
   Fraunces is display only, and its optical size axis tracks the size it
   actually renders at — that is the whole reason the `opsz` subset was chosen
   over the plain weight one. The browser does the tracking
   (`font-optical-sizing: auto`, the default), which is why nothing here pins
   `"opsz"`: a pinned value freezes one optical size across a whole fluid
   range, and `check:fonts` refuses it. */

h1 {
  font-family: var(--font-serif);
  font-weight: 480;
  font-size: var(--text-title);
  line-height: var(--leading-flush);
  letter-spacing: var(--tracking-tight);
  margin: 0 0 var(--space-m);
  text-wrap: balance;
  max-width: 20ch;
}

/*
 * A heading, rather than a label that happens to sit above a section.
 *
 * It was mono, upper case, `--text-sm` and `--color-muted` — smaller than the `h3` under it
 * and quieter than the prose beside it, which is a caption's treatment given to the second
 * level of the document. The voice stays (this site is a terminal, and the serif is the
 * article's own), but a heading has to outrank what it heads.
 */
h2 {
  font-family: var(--font-mono);
  font-size: var(--text-3xl);
  letter-spacing: var(--tracking-tight);
  color: var(--color-fg);
  margin: var(--space-3xl) 0 var(--space-l);
  font-weight: 550;
  text-wrap: balance;
}

h3 {
  font-size: var(--text-xl);
  font-weight: 600;
  letter-spacing: var(--tracking-tight);
  margin: var(--space-xs) 0 var(--space-s);
  text-wrap: balance;
}

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

/* Tailwind's preflight zeroes margins on every element, so block rhythm has to be restated
   rather than inherited from the browser default. */
p {
  margin: 0 0 var(--space-l);
}

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

.prose {
  color: var(--color-muted);
  font-size: var(--text-lg);
  text-wrap: pretty;
  max-width: 66ch;
}

.prose strong {
  color: var(--color-fg);
  font-weight: 550;
}

.note {
  font-size: var(--text-md);
  color: var(--color-muted);
  text-wrap: pretty;
  max-width: 70ch;
}

/* Replaces the `style="margin-top:…"` attributes the Hono views used. Inline style
   attributes are governed by `style-src-attr`, which CSP hashes do not cover, so every one
   of them would be dropped in production. */
.spaced {
  margin-top: var(--space-l);
}

.sub {
  font-size: var(--text-sm);
}

.package-id {
  font-size: var(--text-headline);
  max-width: none;
  word-break: break-word;
}

/* ── Chrome ─────────────────────────────────────────────────────────────── */

header.site {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-l);
  flex-wrap: wrap;
  border-bottom: 1px solid var(--color-line);
  padding-bottom: var(--space-l);
  margin-bottom: var(--space-2xl);
}

/* Astro collapses the newline between two adjacent inline elements, so the gap between the
   wordmark and the tagline is set here rather than depending on source whitespace surviving
   the template. */
header.site .masthead {
  display: flex;
  align-items: baseline;
  gap: var(--space-s);
  flex-wrap: wrap;
}

header.site .brand {
  font-family: var(--font-serif);
  font-weight: 520;
  font-size: var(--text-2xl);
  text-decoration: none;
  /* One word, so it wraps as one. It used to be `inline-flex` with a gap, which is what an
     icon beside a word wants; the mark is a letter now and belongs in the text flow. */
  white-space: nowrap;
}

/* The logotype: the letter O built from Fraunces dollar signs, masked so it takes the accent
   colour and so tracks the theme — the mask carries alpha only, which is what lets the paint
   come from `background` and follow the site's theme rather than the OS scheme.

   Generated from /o-mark-dollars.svg: every second row and column, with the glyph overscaled
   so the marks overlap and the ring closes solid. The favicon keeps its marks separate and
   this does not, which is the difference between an icon and a letter — an icon can spend
   weight on texture, a letter standing in a word has to match the type beside it or it reads
   as a pair of parentheses. Overlapping buys the weight of the full 32-row mark for the bytes
   of a quarter of it, which matters when the string is embedded twice in a stylesheet that
   blocks rendering.

   It is the word's own O — "[O]brigado" — rather than a logo standing next to a word. That
   changes how it has to be measured: sized against the cap height of the Fraunces text beside
   it, and set on that text's baseline instead of centred in the line box. The box carries the
   mask's aspect ratio because `contain` letterboxes anything else, and a letterboxed glyph
   leaves a gap exactly where the next letter should start. */
header.site .brand__mark {
  display: inline-block;
  /* Matched by eye against the ascender of the "b" beside it, at three sizes. Cap height
     alone measures too small here: the glyph does not quite fill its own viewBox, and the
     dollar texture reads lighter than solid type, so a mark set to the arithmetic height
     looks like a smaller letter than the one it is standing in for. */
  height: 0.92em;
  aspect-ratio: 2257.1 / 2265.2;
  /* Round letters overshoot the baseline; Fraunces' O is no exception. */
  vertical-align: -0.045em;
  background: var(--color-accent);
  -webkit-mask: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xMTAuOSAtMTQwLjYgMjI1Ny4xIDIyNjUuMiI+PGRlZnM+PGcgaWQ9ImciPjxwYXRoIGQ9Ik02MzcuNDc2NTE2NDM2Nzg1NSAxMzkzLjE5OTk1MTE3MTg3NVE3OTUuOTUzODEzODY1NzgwOCAxMzkzLjE5OTk1MTE3MTg3NSA5MDYuMzY3NjQ1OTQzNTM3NCAxMzUxLjQ2OTQ5MDUxNjkzMDhRMTAxNi43ODE0NzgwMjEyOTM5IDEzMDkuNzM5MDI5ODYxOTg2NiAxMDc0Ljg5NTUxNTM2OTI1MTQgMTIzNi40NzgxMjA3NTkxMjk1UTExMzMuMDA5NTUyNzE3MjA4OSAxMTYzLjIxNzIxMTY1NjI3MjQgMTEzMy4wMDk1NTI3MTcyMDg5IDEwNjcuODE3NDMxMzgyODM1UTExMzMuMDA5NTUyNzE3MjA4OSAxMDAxLjAwMTA1NzUxMzA1ODIgMTA5NS4wMDUxMTE0NzgyNjkgOTUzLjg2MjU2MjUxMTExNjNRMTA1Ny4wMDA2NzAyMzkzMjkzIDkwNi43MjQwNjc1MDkxNzQzIDk5MS41MjE1MDQzNjExODI1IDkwNi43MjQwNjc1MDkxNzQzUTkxOC4wNzgxNTg2NjkxNzM3IDkwNi43MjQwNjc1MDkxNzQzIDg3MC43NTEyMTA3ODA4MTQzIDk0OC4xNzQ2OTMxODM5NzM0UTgyMy40MjQyNjI4OTI0NTQ5IDk4OS42MjUzMTg4NTg3NzI1IDgyMy40MjQyNjI4OTI0NTQ5IDEwNTYuMzI2NzQxMTA2ODA4MlYxMTQxLjU5OTczMTQ0NTMxMjVRODIzLjQyNDI2Mjg5MjQ1NDkgMTIyOS41ODA3MjU4NzEwMjY1IDc3MS4yODcwMzAzMjI0NzcyIDEyODMuNDQ5MjUxMTY1NjEzNVE3MTkuMTQ5Nzk3NzUyNDk5NiAxMzM3LjMxNzc3NjQ2MDIwMDUgNjI2LjU2NDg5Nzg1MDE1NTggMTMzNy4zMTc3NzY0NjAyMDA1UTU2MS42NzkwNjU5NzI1NjY2IDEzMzcuMzE3Nzc2NDYwMjAwNSA1MTEuNjgyODcyNTc0Nzc2NCAxMzEyLjI2NjQ5Mzg1NTA0NDJRNDYxLjY4NjY3OTE3Njk4NjIgMTI4Ny4yMTUyMTEyNDk4ODggNDMzLjI4NjQ2NjY3ODYwNDUgMTIzOS42NTM4NDY2MzI2ODkyUTQwNC44ODYyNTQxODAyMjI3NSAxMTkyLjA5MjQ4MjAxNTQ5MDUgNDA0Ljg4NjI1NDE4MDIyMjc1IDExMjQuNDE1Mzc0MTgyMTY0N1E0MDQuODg2MjU0MTgwMjIyNzUgMTA2Mi42MDc5NDM4MjU0MjM3IDQzMy40MjUyNzY4MTQwMjg2IDEwMTEuNzg3ODMyNDgzNjQ5M1E0NjEuOTY0Mjk5NDQ3ODM0NSA5NjAuOTY3NzIxMTQxODc0OCA1MjguNDgzNDU4MjU0NDg2MyA5MTguODAyMzM1MTc0NzU0M1E1OTUuMDAyNjE3MDYxMTM4MiA4NzYuNjM2OTQ5MjA3NjMzNyA3MDguOTEwMzk5MTcyNDU1MSA4MzguNzI2MzQ1Nzk2MTM4UTg3NS44NTg1Njc4NTk5Nzc1IDc4Mi44ODEyMzkzMzk3MDkzIDk3MS4wNDg0NDc2MjE5ODYzIDcxNy4yMTgyMTk0MTYyMTZRMTA2Ni4yMzgzMjczODM5OTUgNjUxLjU1NTE5OTQ5MjcyMjcgMTEwNi4zOTcyNDYxMzE2NzM1IDU3NS4zMzE3MDg2NjU5MzcyUTExNDYuNTU2MTY0ODc5MzUxOSA0OTkuMTA4MjE3ODM5MTUxNiAxMTQ2LjU1NjE2NDg3OTM1MTkgNDA5LjUzMDYzOTY0ODQzNzVRMTE0Ni41NTYxNjQ4NzkzNTE5IDI4Ny4yNzMwNjEzMzEzNjE1MyAxMDgxLjE3ODQ5MjQwNjM4MzIgMTk2LjI2OTA3MTYyMzY4Mjk4UTEwMTUuODAwODE5OTMzNDE0NSAxMDUuMjY1MDgxOTE2MDA0NDIgODkyLjk1MzMzMjQ2MTQxNjcgNTQuOTMyNTU5MjY4NTQ5MDg1UTc3MC4xMDU4NDQ5ODk0MTkgNC42MDAwMzY2MjEwOTM3NSA1OTguOTgyMTkzOTk1MjY3MiA0LjYwMDAzNjYyMTA5Mzc1UTQyOS45NjMwNDYzNjQ0ODYyIDQuNjAwMDM2NjIxMDkzNzUgMzEyLjgyNDQxMzcxMTIwNTEgNDcuODgzNzEzNjk5ODc3MjZRMTk1LjY4NTc4MTA1NzkyNDAzIDkxLjE2NzM5MDc3ODY2MDc3IDEzNS4wNDI2NTU0NzkxNjI5MyAxNjMuNzMyNjIzMDMzMjI1NTRRNzQuMzk5NTI5OTAwNDAxODMgMjM2LjI5Nzg1NTI4Nzc5MDMgNzQuMzk5NTI5OTAwNDAxODMgMzIzLjU1ODgzNTM4MzUwNDYzUTc0LjM5OTUyOTkwMDQwMTgzIDM5My40MzE5ODQ0MTM0MTUyIDExNS43MzgwOTMxNjU2ODA3NyA0MzcuOTUyMTU3Nzg2MTE2UTE1Ny4wNzY2NTY0MzA5NTk3IDQ4Mi40NzIzMzExNTg4MTY4IDIyOC4yODAxNTAxNDkwMTc1NyA0ODIuNDcyMzMxMTU4ODE2OFEyOTEuNzQ4OTg5ODM5MTA2OCA0ODIuNDcyMzMxMTU4ODE2OCAzMzYuOTUwNTk1MTkyNjExMiA0MzkuMzg2OTg2NTY2NzA3NVEzODIuMTUyMjAwNTQ2MTE1NjQgMzk2LjMwMTY0MTk3NDU5ODE3IDM4Mi4xNTIyMDA1NDYxMTU2NCAzMjQuMjk4MTUwNTA1ODcwNlYyNDIuNTE3ODE4NDY5NTU0MlEzODIuMTUyMjAwNTQ2MTE1NjQgMTYzLjk5MjI3MzM5NDAxODQgNDQxLjI5MzY3MTk0MzI0NzMgMTEzLjY3ODMwOTgwMzgyODZRNTAwLjQzNTE0MzM0MDM3OSA2My4zNjQzNDYyMTM2Mzg3OCA2MDEuMTk0ODA1NTkyMjk4NSA2My4zNjQzNDYyMTM2Mzg3OFE3MDYuNjEyMzc1NzUxMTM3NyA2My4zNjQzNDYyMTM2Mzg3OCA3NjkuMjcyODg4ODYzNDU5MiAxMTcuMDEyNDU3MzY4ODk1NDFRODMxLjkzMzQwMTk3NTc4MDcgMTcwLjY2MDU2ODUyNDE1MjA0IDgzMS45MzM0MDE5NzU3ODA3IDI2MS4xNDUzMDk3MzUwODk1NFE4MzEuOTMzNDAxOTc1NzgwNyAzNTUuNTkyNzk5NTg1MzEyNiA3NjUuNTEzNjcwNjkyMjIwMyA0MjMuMzk2NjIwNzcyNzc5UTY5OS4wOTM5Mzk0MDg2NTk5IDQ5MS4yMDA0NDE5NjAyNDUzNyA1MjMuMDAzNzE5NDkwMDIxNSA1NDkuNjMwMDMxMDMwNjI1MVEzNjUuMjIwNzc3OTEzOTI4MDMgNjAzLjg4NjcxNjI0Mjg3OTYgMjcxLjM3NjE4ODM1ODI5MiA2NjcuMTgyMzk5MTk0Njg3NlExNzcuNTMxNTk4ODAyNjU1OTQgNzMwLjQ3ODA4MjE0NjQ5NTYgMTM2LjMyOTU2MTIyNDIwNzI4IDgwOC42MjQ3ODYzNzY5NTMxUTk1LjEyNzUyMzY0NTc1ODYzIDg4Ni43NzE0OTA2MDc0MTA3IDk1LjEyNzUyMzY0NTc1ODYzIDk4NC4yOTc1NjI1NzY4MzA0UTk1LjEyNzUyMzY0NTc1ODYzIDEwOTguNDA3ODUzMTA0MTc0MSAxNjEuMDQ0MjE4NzUyNTMzMiAxMTkxLjE5OTc4NDI1NjUxOFEyMjYuOTYwOTEzODU5MzA3NzcgMTI4My45OTE3MTU0MDg4NjE2IDM0OC43MDQyNTc5MTEwNzEyIDEzMzguNTk1ODMzMjkwMzY4M1E0NzAuNDQ3NjAxOTYyODM0NiAxMzkzLjE5OTk1MTE3MTg3NSA2MzcuNDc2NTE2NDM2Nzg1NSAxMzkzLjE5OTk1MTE3MTg3NVpNNjU4LjI3ODY4NzgxMjM4OCA3NTAuMDIyNjY4OTAxODMwOUg1OTguODQ0NzUyNzgxMDkzMVE1OTkuMDEyMTMzNjAyMDUyOSA4OTkuMTE2MTA3NDIyODU4NSA1OTMuNzY0NzUzMTM0OTIxMiAxMDE2Ljg0NzYxNTAyNDA3NDlRNTg4LjUxNzM3MjY2Nzc4OTUgMTEzNC41NzkxMjI2MjUyOTEzIDU4MS41ODIwODczNzcwODYzIDEyMjYuOTQ1NTQxNjMzMjkzUTU3NC42NDY4MDIwODYzODMxIDEzMTkuMzExOTYwNjQxMjk0NyA1NjkuMzgwOTg5MDAyMDYzOSAxMzkxLjU1MDU4NDQ5NjkzMDJRNTY0LjExNTE3NTkxNzc0NDYgMTQ2My43ODkyMDgzNTI1NjU4IDU2NC4xMTUxNzU5MTc3NDQ2IDE1MjEuMDk2ODkxNTg5NDYyOFE1NjQuMTE1MTc1OTE3NzQ0NiAxNTU1LjQ4NzUyNDA0MDA0MzQgNTgyLjAzMzE2NTgzNjcwNjggMTU3My44MzM4Nzk4NTQ1M1E1OTkuOTUxMTU1NzU1NjY4OSAxNTkyLjE4MDIzNTY2OTAxNjggNjI4Ljk2NTk3ODU1OTEwNjYgMTU5Mi4xODAyMzU2NjkwMTY4UTY1Ni4xMDI5MDAzMjk5NzczIDE1OTIuMTgwMjM1NjY5MDE2OCA2NzQuOTQzNTI1MDcyMTg3MiAxNTczLjgzMzg3OTg1NDUzUTY5My43ODQxNDk4MTQzOTcxIDE1NTUuNDg3NTI0MDQwMDQzNCA2OTMuNzg0MTQ5ODE0Mzk3MSAxNTE5LjcyNTMxNTc5MDYyMzRRNjkzLjc4NDE0OTgxNDM5NzEgMTQ2Ny4zOTY0NDMzOTY4MDY3IDY4OC4zMzQ2NDAyMTYwODIzIDEzOTcuMDUxNDU3Njg2MzQ5N1E2ODIuODg1MTMwNjE3NzY3NiAxMzI2LjcwNjQ3MTk3NTg5MjggNjc1LjkyOTI5NTc4NTcyNTEgMTIzMy43Mjc5Njk5OTg0OTM4UTY2OC45NzM0NjA5NTM2ODI3IDExNDAuNzQ5NDY4MDIxMDk0OCA2NjMuNTQyMzgzOTcyNTU1NCAxMDIwLjkyODU0ODg5NDgyMjZRNjU4LjExMTMwNjk5MTQyODEgOTAxLjEwNzYyOTc2ODU1MDQgNjU4LjI3ODY4NzgxMjM4OCA3NTAuMDIyNjY4OTAxODMwOVpNNTc1LjEyMzA0NDQzODY2MDEgNjcxLjQwNTA5MTYyMDk4MTdINjMzLjg4NzUwNzI2MzU3MVE2MzMuODg3NTA3MjYzNTcxIDUyMi42MzA3Nzk3ODQxNzI4IDYzOS4zMTg1ODQyNDQ2OTgzIDQwMy43NzkzOTcyNzM0MzYyUTY0NC43NDk2NjEyMjU4MjU1IDI4NC45MjgwMTQ3NjI2OTk2IDY1MS43MDU0OTYwNTc4NjggMTkyLjEzNTM1MTc2MjE3NTU2UTY1OC42NjEzMzA4ODk5MTA1IDk5LjM0MjY4ODc2MTY1MTUyIDY2NC4wMjcxNTAwNzc3NDUzIDI4LjgxNDAzMjA3NTkyNjY2UTY2OS4zOTI5NjkyNjU1ODAyIC00MS43MTQ2MjQ2MDk3OTgxOSA2NjkuMzkyOTY5MjY1NTgwMiAtOTQuNzEyOTY5MjA5OTk4ODVRNjY5LjM5Mjk2OTI2NTU4MDIgLTEyOS4wNzA5NzAyNzQ1MDggNjUxLjU1NjU1MjgzMjk0NjIgLTE0Ny40MDEwMTAzOTU5NTkwMlE2MzMuNzIwMTM2NDAwMzEyMiAtMTY1LjczMTA1MDUxNzQxMDA0IDYwNC41NzQ3OTgwMTAyODk3IC0xNjUuNzMxMDUwNTE3NDEwMDRRNTc2Ljg5ODkxOTYxOTYxOTggLTE2NS43MzEwNTA1MTc0MTAwNCA1NTguMzExNDU3NDk0MjczOCAtMTQ3LjQxNzMyNjA4ODk5NDc0UTUzOS43MjM5OTUzNjg5Mjc3IC0xMjkuMTAzNjAxNjYwNTc5NDQgNTM5LjcyMzk5NTM2ODkyNzcgLTkzLjQwNjY1NjE4MzMwMjRRNTM5LjcyMzk5NTM2ODkyNzcgLTM2LjA5ODk3Mjk0NjQwNTQxIDU0NC45ODk4MDg0NTMyNDcgMzUuMzE5MTEzNTc0OTIyMDg1UTU1MC4yNTU2MjE1Mzc1NjYyIDEwNi43MzcyMDAwOTYyNDk1OCA1NTcuMTA3MjE2NDE3Nzg5NSAxOTguNTE3Nzg2MjMwODkxOTRRNTYzLjk1ODgxMTI5ODAxMjcgMjkwLjI5ODM3MjM2NTUzNDMgNTY5LjU0MDkyNzg2ODMzNjQgNDA3LjEyNTYwMTE0NDUwNzVRNTc1LjEyMzA0NDQzODY2MDEgNTIzLjk1MjgyOTkyMzQ4MDcgNTc1LjEyMzA0NDQzODY2MDEgNjcxLjQwNTA5MTYyMDk4MTdaIiB0cmFuc2Zvcm09InNjYWxlKDAuMTIzNDkgLTAuMTIzNDkpIHRyYW5zbGF0ZSgtNjEwLjUgLTcxMy4yKSIvPjwvZz48L2RlZnM+PGcgZmlsbD0iIzAwMCI+PHVzZSBocmVmPSIjZyIgeD0iNzEwLjQiIHk9IjMyIi8+PHVzZSBocmVmPSIjZyIgeD0iNzg3LjIiIHk9IjMyIi8+PHVzZSBocmVmPSIjZyIgeD0iODY0IiB5PSIzMiIvPjx1c2UgaHJlZj0iI2ciIHg9Ijk0MC44IiB5PSIzMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjEwMTcuNiIgeT0iMzIiLz48dXNlIGhyZWY9IiNnIiB4PSIxMDk0LjQiIHk9IjMyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTE3MS4yIiB5PSIzMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjEyNDgiIHk9IjMyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTMyNC44IiB5PSIzMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjQwMy4yIiB5PSIxNjAiLz48dXNlIGhyZWY9IiNnIiB4PSI0ODAiIHk9IjE2MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjU1Ni44IiB5PSIxNjAiLz48dXNlIGhyZWY9IiNnIiB4PSI2MzMuNiIgeT0iMTYwIi8+PHVzZSBocmVmPSIjZyIgeD0iNzEwLjQiIHk9IjE2MCIvPjx1c2UgaHJlZj0iI2ciIHg9Ijc4Ny4yIiB5PSIxNjAiLz48dXNlIGhyZWY9IiNnIiB4PSI4NjQiIHk9IjE2MCIvPjx1c2UgaHJlZj0iI2ciIHg9Ijk0MC44IiB5PSIxNjAiLz48dXNlIGhyZWY9IiNnIiB4PSIxMDE3LjYiIHk9IjE2MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjEwOTQuNCIgeT0iMTYwIi8+PHVzZSBocmVmPSIjZyIgeD0iMTE3MS4yIiB5PSIxNjAiLz48dXNlIGhyZWY9IiNnIiB4PSIxMjQ4IiB5PSIxNjAiLz48dXNlIGhyZWY9IiNnIiB4PSIxMzI0LjgiIHk9IjE2MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0MDEuNiIgeT0iMTYwIi8+PHVzZSBocmVmPSIjZyIgeD0iMTQ3OC40IiB5PSIxNjAiLz48dXNlIGhyZWY9IiNnIiB4PSIxNTU1LjIiIHk9IjE2MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjE2MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjMyNi40IiB5PSIyODgiLz48dXNlIGhyZWY9IiNnIiB4PSI0MDMuMiIgeT0iMjg4Ii8+PHVzZSBocmVmPSIjZyIgeD0iNDgwIiB5PSIyODgiLz48dXNlIGhyZWY9IiNnIiB4PSI1NTYuOCIgeT0iMjg4Ii8+PHVzZSBocmVmPSIjZyIgeD0iNjMzLjYiIHk9IjI4OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjExNzEuMiIgeT0iMjg4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTI0OCIgeT0iMjg4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTMyNC44IiB5PSIyODgiLz48dXNlIGhyZWY9IiNnIiB4PSIxNDAxLjYiIHk9IjI4OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iMjg4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTU1NS4yIiB5PSIyODgiLz48dXNlIGhyZWY9IiNnIiB4PSIxNjMyIiB5PSIyODgiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzA4LjgiIHk9IjI4OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3Mi44IiB5PSI0MTYiLz48dXNlIGhyZWY9IiNnIiB4PSIyNDkuNiIgeT0iNDE2Ii8+PHVzZSBocmVmPSIjZyIgeD0iMzI2LjQiIHk9IjQxNiIvPjx1c2UgaHJlZj0iI2ciIHg9IjQwMy4yIiB5PSI0MTYiLz48dXNlIGhyZWY9IiNnIiB4PSI0ODAiIHk9IjQxNiIvPjx1c2UgaHJlZj0iI2ciIHg9IjU1Ni44IiB5PSI0MTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxMjQ4IiB5PSI0MTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxMzI0LjgiIHk9IjQxNiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0MDEuNiIgeT0iNDE2Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTQ3OC40IiB5PSI0MTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxNTU1LjIiIHk9IjQxNiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjQxNiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3MDguOCIgeT0iNDE2Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTc4NS42IiB5PSI0MTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxODYyLjQiIHk9IjQxNiIvPjx1c2UgaHJlZj0iI2ciIHg9Ijk2IiB5PSI1NDQiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzIuOCIgeT0iNTQ0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMjQ5LjYiIHk9IjU0NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjMyNi40IiB5PSI1NDQiLz48dXNlIGhyZWY9IiNnIiB4PSI0MDMuMiIgeT0iNTQ0Ii8+PHVzZSBocmVmPSIjZyIgeD0iNDgwIiB5PSI1NDQiLz48dXNlIGhyZWY9IiNnIiB4PSI1NTYuOCIgeT0iNTQ0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTMyNC44IiB5PSI1NDQiLz48dXNlIGhyZWY9IiNnIiB4PSIxNDAxLjYiIHk9IjU0NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iNTQ0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTU1NS4yIiB5PSI1NDQiLz48dXNlIGhyZWY9IiNnIiB4PSIxNjMyIiB5PSI1NDQiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzA4LjgiIHk9IjU0NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3ODUuNiIgeT0iNTQ0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTg2Mi40IiB5PSI1NDQiLz48dXNlIGhyZWY9IiNnIiB4PSIxOTM5LjIiIHk9IjU0NCIvPjx1c2UgaHJlZj0iI2ciIHg9Ijk2IiB5PSI2NzIiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzIuOCIgeT0iNjcyIi8+PHVzZSBocmVmPSIjZyIgeD0iMjQ5LjYiIHk9IjY3MiIvPjx1c2UgaHJlZj0iI2ciIHg9IjMyNi40IiB5PSI2NzIiLz48dXNlIGhyZWY9IiNnIiB4PSI0MDMuMiIgeT0iNjcyIi8+PHVzZSBocmVmPSIjZyIgeD0iNDgwIiB5PSI2NzIiLz48dXNlIGhyZWY9IiNnIiB4PSI1NTYuOCIgeT0iNjcyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTQwMS42IiB5PSI2NzIiLz48dXNlIGhyZWY9IiNnIiB4PSIxNDc4LjQiIHk9IjY3MiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE1NTUuMiIgeT0iNjcyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTYzMiIgeT0iNjcyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTcwOC44IiB5PSI2NzIiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzg1LjYiIHk9IjY3MiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE4NjIuNCIgeT0iNjcyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTkzOS4yIiB5PSI2NzIiLz48dXNlIGhyZWY9IiNnIiB4PSIxOS4yIiB5PSI4MDAiLz48dXNlIGhyZWY9IiNnIiB4PSI5NiIgeT0iODAwIi8+PHVzZSBocmVmPSIjZyIgeD0iMTcyLjgiIHk9IjgwMCIvPjx1c2UgaHJlZj0iI2ciIHg9IjI0OS42IiB5PSI4MDAiLz48dXNlIGhyZWY9IiNnIiB4PSIzMjYuNCIgeT0iODAwIi8+PHVzZSBocmVmPSIjZyIgeD0iNDAzLjIiIHk9IjgwMCIvPjx1c2UgaHJlZj0iI2ciIHg9IjQ4MCIgeT0iODAwIi8+PHVzZSBocmVmPSIjZyIgeD0iNTU2LjgiIHk9IjgwMCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0MDEuNiIgeT0iODAwIi8+PHVzZSBocmVmPSIjZyIgeD0iMTQ3OC40IiB5PSI4MDAiLz48dXNlIGhyZWY9IiNnIiB4PSIxNTU1LjIiIHk9IjgwMCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjgwMCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3MDguOCIgeT0iODAwIi8+PHVzZSBocmVmPSIjZyIgeD0iMTc4NS42IiB5PSI4MDAiLz48dXNlIGhyZWY9IiNnIiB4PSIxODYyLjQiIHk9IjgwMCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE5MzkuMiIgeT0iODAwIi8+PHVzZSBocmVmPSIjZyIgeD0iMjAxNiIgeT0iODAwIi8+PHVzZSBocmVmPSIjZyIgeD0iMTkuMiIgeT0iOTI4Ii8+PHVzZSBocmVmPSIjZyIgeD0iOTYiIHk9IjkyOCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3Mi44IiB5PSI5MjgiLz48dXNlIGhyZWY9IiNnIiB4PSIyNDkuNiIgeT0iOTI4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMzI2LjQiIHk9IjkyOCIvPjx1c2UgaHJlZj0iI2ciIHg9IjQwMy4yIiB5PSI5MjgiLz48dXNlIGhyZWY9IiNnIiB4PSI0ODAiIHk9IjkyOCIvPjx1c2UgaHJlZj0iI2ciIHg9IjU1Ni44IiB5PSI5MjgiLz48dXNlIGhyZWY9IiNnIiB4PSIxNDc4LjQiIHk9IjkyOCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE1NTUuMiIgeT0iOTI4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTYzMiIgeT0iOTI4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTcwOC44IiB5PSI5MjgiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzg1LjYiIHk9IjkyOCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE4NjIuNCIgeT0iOTI4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTkzOS4yIiB5PSI5MjgiLz48dXNlIGhyZWY9IiNnIiB4PSIyMDE2IiB5PSI5MjgiLz48dXNlIGhyZWY9IiNnIiB4PSIxOS4yIiB5PSIxMDU2Ii8+PHVzZSBocmVmPSIjZyIgeD0iOTYiIHk9IjEwNTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzIuOCIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjI0OS42IiB5PSIxMDU2Ii8+PHVzZSBocmVmPSIjZyIgeD0iMzI2LjQiIHk9IjEwNTYiLz48dXNlIGhyZWY9IiNnIiB4PSI0MDMuMiIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjQ4MCIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjU1Ni44IiB5PSIxMDU2Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTQ3OC40IiB5PSIxMDU2Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTU1NS4yIiB5PSIxMDU2Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTYzMiIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3MDguOCIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3ODUuNiIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE4NjIuNCIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE5MzkuMiIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjIwMTYiIHk9IjEwNTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxOS4yIiB5PSIxMTg0Ii8+PHVzZSBocmVmPSIjZyIgeD0iOTYiIHk9IjExODQiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzIuOCIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjI0OS42IiB5PSIxMTg0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMzI2LjQiIHk9IjExODQiLz48dXNlIGhyZWY9IiNnIiB4PSI0MDMuMiIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjQ4MCIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjU1Ni44IiB5PSIxMTg0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTQ3OC40IiB5PSIxMTg0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTU1NS4yIiB5PSIxMTg0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTYzMiIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3MDguOCIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3ODUuNiIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE4NjIuNCIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE5MzkuMiIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjIwMTYiIHk9IjExODQiLz48dXNlIGhyZWY9IiNnIiB4PSI5NiIgeT0iMTMxMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3Mi44IiB5PSIxMzEyIi8+PHVzZSBocmVmPSIjZyIgeD0iMjQ5LjYiIHk9IjEzMTIiLz48dXNlIGhyZWY9IiNnIiB4PSIzMjYuNCIgeT0iMTMxMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjQwMy4yIiB5PSIxMzEyIi8+PHVzZSBocmVmPSIjZyIgeD0iNDgwIiB5PSIxMzEyIi8+PHVzZSBocmVmPSIjZyIgeD0iNTU2LjgiIHk9IjEzMTIiLz48dXNlIGhyZWY9IiNnIiB4PSI2MzMuNiIgeT0iMTMxMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iMTMxMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE1NTUuMiIgeT0iMTMxMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjEzMTIiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzA4LjgiIHk9IjEzMTIiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzg1LjYiIHk9IjEzMTIiLz48dXNlIGhyZWY9IiNnIiB4PSIxODYyLjQiIHk9IjEzMTIiLz48dXNlIGhyZWY9IiNnIiB4PSIxOTM5LjIiIHk9IjEzMTIiLz48dXNlIGhyZWY9IiNnIiB4PSI5NiIgeT0iMTQ0MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3Mi44IiB5PSIxNDQwIi8+PHVzZSBocmVmPSIjZyIgeD0iMjQ5LjYiIHk9IjE0NDAiLz48dXNlIGhyZWY9IiNnIiB4PSIzMjYuNCIgeT0iMTQ0MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjQwMy4yIiB5PSIxNDQwIi8+PHVzZSBocmVmPSIjZyIgeD0iNDgwIiB5PSIxNDQwIi8+PHVzZSBocmVmPSIjZyIgeD0iNTU2LjgiIHk9IjE0NDAiLz48dXNlIGhyZWY9IiNnIiB4PSI2MzMuNiIgeT0iMTQ0MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iMTQ0MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE1NTUuMiIgeT0iMTQ0MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjE0NDAiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzA4LjgiIHk9IjE0NDAiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzg1LjYiIHk9IjE0NDAiLz48dXNlIGhyZWY9IiNnIiB4PSIxODYyLjQiIHk9IjE0NDAiLz48dXNlIGhyZWY9IiNnIiB4PSIxOTM5LjIiIHk9IjE0NDAiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzIuOCIgeT0iMTU2OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjI0OS42IiB5PSIxNTY4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMzI2LjQiIHk9IjE1NjgiLz48dXNlIGhyZWY9IiNnIiB4PSI0MDMuMiIgeT0iMTU2OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjQ4MCIgeT0iMTU2OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjU1Ni44IiB5PSIxNTY4Ii8+PHVzZSBocmVmPSIjZyIgeD0iNjMzLjYiIHk9IjE1NjgiLz48dXNlIGhyZWY9IiNnIiB4PSI3MTAuNCIgeT0iMTU2OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iMTU2OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE1NTUuMiIgeT0iMTU2OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjE1NjgiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzA4LjgiIHk9IjE1NjgiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzg1LjYiIHk9IjE1NjgiLz48dXNlIGhyZWY9IiNnIiB4PSIxODYyLjQiIHk9IjE1NjgiLz48dXNlIGhyZWY9IiNnIiB4PSIyNDkuNiIgeT0iMTY5NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjMyNi40IiB5PSIxNjk2Ii8+PHVzZSBocmVmPSIjZyIgeD0iNDAzLjIiIHk9IjE2OTYiLz48dXNlIGhyZWY9IiNnIiB4PSI0ODAiIHk9IjE2OTYiLz48dXNlIGhyZWY9IiNnIiB4PSI1NTYuOCIgeT0iMTY5NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjYzMy42IiB5PSIxNjk2Ii8+PHVzZSBocmVmPSIjZyIgeD0iNzEwLjQiIHk9IjE2OTYiLz48dXNlIGhyZWY9IiNnIiB4PSI3ODcuMiIgeT0iMTY5NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iMTY5NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE1NTUuMiIgeT0iMTY5NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjE2OTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzA4LjgiIHk9IjE2OTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzg1LjYiIHk9IjE2OTYiLz48dXNlIGhyZWY9IiNnIiB4PSIzMjYuNCIgeT0iMTgyNCIvPjx1c2UgaHJlZj0iI2ciIHg9IjQwMy4yIiB5PSIxODI0Ii8+PHVzZSBocmVmPSIjZyIgeD0iNDgwIiB5PSIxODI0Ii8+PHVzZSBocmVmPSIjZyIgeD0iNTU2LjgiIHk9IjE4MjQiLz48dXNlIGhyZWY9IiNnIiB4PSI2MzMuNiIgeT0iMTgyNCIvPjx1c2UgaHJlZj0iI2ciIHg9IjcxMC40IiB5PSIxODI0Ii8+PHVzZSBocmVmPSIjZyIgeD0iNzg3LjIiIHk9IjE4MjQiLz48dXNlIGhyZWY9IiNnIiB4PSI4NjQiIHk9IjE4MjQiLz48dXNlIGhyZWY9IiNnIiB4PSI5NDAuOCIgeT0iMTgyNCIvPjx1c2UgaHJlZj0iI2ciIHg9IjEzMjQuOCIgeT0iMTgyNCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0MDEuNiIgeT0iMTgyNCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iMTgyNCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE1NTUuMiIgeT0iMTgyNCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjE4MjQiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzA4LjgiIHk9IjE4MjQiLz48dXNlIGhyZWY9IiNnIiB4PSI1NTYuOCIgeT0iMTk1MiIvPjx1c2UgaHJlZj0iI2ciIHg9IjYzMy42IiB5PSIxOTUyIi8+PHVzZSBocmVmPSIjZyIgeD0iNzEwLjQiIHk9IjE5NTIiLz48dXNlIGhyZWY9IiNnIiB4PSI3ODcuMiIgeT0iMTk1MiIvPjx1c2UgaHJlZj0iI2ciIHg9Ijg2NCIgeT0iMTk1MiIvPjx1c2UgaHJlZj0iI2ciIHg9Ijk0MC44IiB5PSIxOTUyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTAxNy42IiB5PSIxOTUyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTA5NC40IiB5PSIxOTUyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTE3MS4yIiB5PSIxOTUyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTI0OCIgeT0iMTk1MiIvPjx1c2UgaHJlZj0iI2ciIHg9IjEzMjQuOCIgeT0iMTk1MiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0MDEuNiIgeT0iMTk1MiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iMTk1MiIvPjwvZz48L3N2Zz4=")
    center / contain no-repeat;
  mask: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xMTAuOSAtMTQwLjYgMjI1Ny4xIDIyNjUuMiI+PGRlZnM+PGcgaWQ9ImciPjxwYXRoIGQ9Ik02MzcuNDc2NTE2NDM2Nzg1NSAxMzkzLjE5OTk1MTE3MTg3NVE3OTUuOTUzODEzODY1NzgwOCAxMzkzLjE5OTk1MTE3MTg3NSA5MDYuMzY3NjQ1OTQzNTM3NCAxMzUxLjQ2OTQ5MDUxNjkzMDhRMTAxNi43ODE0NzgwMjEyOTM5IDEzMDkuNzM5MDI5ODYxOTg2NiAxMDc0Ljg5NTUxNTM2OTI1MTQgMTIzNi40NzgxMjA3NTkxMjk1UTExMzMuMDA5NTUyNzE3MjA4OSAxMTYzLjIxNzIxMTY1NjI3MjQgMTEzMy4wMDk1NTI3MTcyMDg5IDEwNjcuODE3NDMxMzgyODM1UTExMzMuMDA5NTUyNzE3MjA4OSAxMDAxLjAwMTA1NzUxMzA1ODIgMTA5NS4wMDUxMTE0NzgyNjkgOTUzLjg2MjU2MjUxMTExNjNRMTA1Ny4wMDA2NzAyMzkzMjkzIDkwNi43MjQwNjc1MDkxNzQzIDk5MS41MjE1MDQzNjExODI1IDkwNi43MjQwNjc1MDkxNzQzUTkxOC4wNzgxNTg2NjkxNzM3IDkwNi43MjQwNjc1MDkxNzQzIDg3MC43NTEyMTA3ODA4MTQzIDk0OC4xNzQ2OTMxODM5NzM0UTgyMy40MjQyNjI4OTI0NTQ5IDk4OS42MjUzMTg4NTg3NzI1IDgyMy40MjQyNjI4OTI0NTQ5IDEwNTYuMzI2NzQxMTA2ODA4MlYxMTQxLjU5OTczMTQ0NTMxMjVRODIzLjQyNDI2Mjg5MjQ1NDkgMTIyOS41ODA3MjU4NzEwMjY1IDc3MS4yODcwMzAzMjI0NzcyIDEyODMuNDQ5MjUxMTY1NjEzNVE3MTkuMTQ5Nzk3NzUyNDk5NiAxMzM3LjMxNzc3NjQ2MDIwMDUgNjI2LjU2NDg5Nzg1MDE1NTggMTMzNy4zMTc3NzY0NjAyMDA1UTU2MS42NzkwNjU5NzI1NjY2IDEzMzcuMzE3Nzc2NDYwMjAwNSA1MTEuNjgyODcyNTc0Nzc2NCAxMzEyLjI2NjQ5Mzg1NTA0NDJRNDYxLjY4NjY3OTE3Njk4NjIgMTI4Ny4yMTUyMTEyNDk4ODggNDMzLjI4NjQ2NjY3ODYwNDUgMTIzOS42NTM4NDY2MzI2ODkyUTQwNC44ODYyNTQxODAyMjI3NSAxMTkyLjA5MjQ4MjAxNTQ5MDUgNDA0Ljg4NjI1NDE4MDIyMjc1IDExMjQuNDE1Mzc0MTgyMTY0N1E0MDQuODg2MjU0MTgwMjIyNzUgMTA2Mi42MDc5NDM4MjU0MjM3IDQzMy40MjUyNzY4MTQwMjg2IDEwMTEuNzg3ODMyNDgzNjQ5M1E0NjEuOTY0Mjk5NDQ3ODM0NSA5NjAuOTY3NzIxMTQxODc0OCA1MjguNDgzNDU4MjU0NDg2MyA5MTguODAyMzM1MTc0NzU0M1E1OTUuMDAyNjE3MDYxMTM4MiA4NzYuNjM2OTQ5MjA3NjMzNyA3MDguOTEwMzk5MTcyNDU1MSA4MzguNzI2MzQ1Nzk2MTM4UTg3NS44NTg1Njc4NTk5Nzc1IDc4Mi44ODEyMzkzMzk3MDkzIDk3MS4wNDg0NDc2MjE5ODYzIDcxNy4yMTgyMTk0MTYyMTZRMTA2Ni4yMzgzMjczODM5OTUgNjUxLjU1NTE5OTQ5MjcyMjcgMTEwNi4zOTcyNDYxMzE2NzM1IDU3NS4zMzE3MDg2NjU5MzcyUTExNDYuNTU2MTY0ODc5MzUxOSA0OTkuMTA4MjE3ODM5MTUxNiAxMTQ2LjU1NjE2NDg3OTM1MTkgNDA5LjUzMDYzOTY0ODQzNzVRMTE0Ni41NTYxNjQ4NzkzNTE5IDI4Ny4yNzMwNjEzMzEzNjE1MyAxMDgxLjE3ODQ5MjQwNjM4MzIgMTk2LjI2OTA3MTYyMzY4Mjk4UTEwMTUuODAwODE5OTMzNDE0NSAxMDUuMjY1MDgxOTE2MDA0NDIgODkyLjk1MzMzMjQ2MTQxNjcgNTQuOTMyNTU5MjY4NTQ5MDg1UTc3MC4xMDU4NDQ5ODk0MTkgNC42MDAwMzY2MjEwOTM3NSA1OTguOTgyMTkzOTk1MjY3MiA0LjYwMDAzNjYyMTA5Mzc1UTQyOS45NjMwNDYzNjQ0ODYyIDQuNjAwMDM2NjIxMDkzNzUgMzEyLjgyNDQxMzcxMTIwNTEgNDcuODgzNzEzNjk5ODc3MjZRMTk1LjY4NTc4MTA1NzkyNDAzIDkxLjE2NzM5MDc3ODY2MDc3IDEzNS4wNDI2NTU0NzkxNjI5MyAxNjMuNzMyNjIzMDMzMjI1NTRRNzQuMzk5NTI5OTAwNDAxODMgMjM2LjI5Nzg1NTI4Nzc5MDMgNzQuMzk5NTI5OTAwNDAxODMgMzIzLjU1ODgzNTM4MzUwNDYzUTc0LjM5OTUyOTkwMDQwMTgzIDM5My40MzE5ODQ0MTM0MTUyIDExNS43MzgwOTMxNjU2ODA3NyA0MzcuOTUyMTU3Nzg2MTE2UTE1Ny4wNzY2NTY0MzA5NTk3IDQ4Mi40NzIzMzExNTg4MTY4IDIyOC4yODAxNTAxNDkwMTc1NyA0ODIuNDcyMzMxMTU4ODE2OFEyOTEuNzQ4OTg5ODM5MTA2OCA0ODIuNDcyMzMxMTU4ODE2OCAzMzYuOTUwNTk1MTkyNjExMiA0MzkuMzg2OTg2NTY2NzA3NVEzODIuMTUyMjAwNTQ2MTE1NjQgMzk2LjMwMTY0MTk3NDU5ODE3IDM4Mi4xNTIyMDA1NDYxMTU2NCAzMjQuMjk4MTUwNTA1ODcwNlYyNDIuNTE3ODE4NDY5NTU0MlEzODIuMTUyMjAwNTQ2MTE1NjQgMTYzLjk5MjI3MzM5NDAxODQgNDQxLjI5MzY3MTk0MzI0NzMgMTEzLjY3ODMwOTgwMzgyODZRNTAwLjQzNTE0MzM0MDM3OSA2My4zNjQzNDYyMTM2Mzg3OCA2MDEuMTk0ODA1NTkyMjk4NSA2My4zNjQzNDYyMTM2Mzg3OFE3MDYuNjEyMzc1NzUxMTM3NyA2My4zNjQzNDYyMTM2Mzg3OCA3NjkuMjcyODg4ODYzNDU5MiAxMTcuMDEyNDU3MzY4ODk1NDFRODMxLjkzMzQwMTk3NTc4MDcgMTcwLjY2MDU2ODUyNDE1MjA0IDgzMS45MzM0MDE5NzU3ODA3IDI2MS4xNDUzMDk3MzUwODk1NFE4MzEuOTMzNDAxOTc1NzgwNyAzNTUuNTkyNzk5NTg1MzEyNiA3NjUuNTEzNjcwNjkyMjIwMyA0MjMuMzk2NjIwNzcyNzc5UTY5OS4wOTM5Mzk0MDg2NTk5IDQ5MS4yMDA0NDE5NjAyNDUzNyA1MjMuMDAzNzE5NDkwMDIxNSA1NDkuNjMwMDMxMDMwNjI1MVEzNjUuMjIwNzc3OTEzOTI4MDMgNjAzLjg4NjcxNjI0Mjg3OTYgMjcxLjM3NjE4ODM1ODI5MiA2NjcuMTgyMzk5MTk0Njg3NlExNzcuNTMxNTk4ODAyNjU1OTQgNzMwLjQ3ODA4MjE0NjQ5NTYgMTM2LjMyOTU2MTIyNDIwNzI4IDgwOC42MjQ3ODYzNzY5NTMxUTk1LjEyNzUyMzY0NTc1ODYzIDg4Ni43NzE0OTA2MDc0MTA3IDk1LjEyNzUyMzY0NTc1ODYzIDk4NC4yOTc1NjI1NzY4MzA0UTk1LjEyNzUyMzY0NTc1ODYzIDEwOTguNDA3ODUzMTA0MTc0MSAxNjEuMDQ0MjE4NzUyNTMzMiAxMTkxLjE5OTc4NDI1NjUxOFEyMjYuOTYwOTEzODU5MzA3NzcgMTI4My45OTE3MTU0MDg4NjE2IDM0OC43MDQyNTc5MTEwNzEyIDEzMzguNTk1ODMzMjkwMzY4M1E0NzAuNDQ3NjAxOTYyODM0NiAxMzkzLjE5OTk1MTE3MTg3NSA2MzcuNDc2NTE2NDM2Nzg1NSAxMzkzLjE5OTk1MTE3MTg3NVpNNjU4LjI3ODY4NzgxMjM4OCA3NTAuMDIyNjY4OTAxODMwOUg1OTguODQ0NzUyNzgxMDkzMVE1OTkuMDEyMTMzNjAyMDUyOSA4OTkuMTE2MTA3NDIyODU4NSA1OTMuNzY0NzUzMTM0OTIxMiAxMDE2Ljg0NzYxNTAyNDA3NDlRNTg4LjUxNzM3MjY2Nzc4OTUgMTEzNC41NzkxMjI2MjUyOTEzIDU4MS41ODIwODczNzcwODYzIDEyMjYuOTQ1NTQxNjMzMjkzUTU3NC42NDY4MDIwODYzODMxIDEzMTkuMzExOTYwNjQxMjk0NyA1NjkuMzgwOTg5MDAyMDYzOSAxMzkxLjU1MDU4NDQ5NjkzMDJRNTY0LjExNTE3NTkxNzc0NDYgMTQ2My43ODkyMDgzNTI1NjU4IDU2NC4xMTUxNzU5MTc3NDQ2IDE1MjEuMDk2ODkxNTg5NDYyOFE1NjQuMTE1MTc1OTE3NzQ0NiAxNTU1LjQ4NzUyNDA0MDA0MzQgNTgyLjAzMzE2NTgzNjcwNjggMTU3My44MzM4Nzk4NTQ1M1E1OTkuOTUxMTU1NzU1NjY4OSAxNTkyLjE4MDIzNTY2OTAxNjggNjI4Ljk2NTk3ODU1OTEwNjYgMTU5Mi4xODAyMzU2NjkwMTY4UTY1Ni4xMDI5MDAzMjk5NzczIDE1OTIuMTgwMjM1NjY5MDE2OCA2NzQuOTQzNTI1MDcyMTg3MiAxNTczLjgzMzg3OTg1NDUzUTY5My43ODQxNDk4MTQzOTcxIDE1NTUuNDg3NTI0MDQwMDQzNCA2OTMuNzg0MTQ5ODE0Mzk3MSAxNTE5LjcyNTMxNTc5MDYyMzRRNjkzLjc4NDE0OTgxNDM5NzEgMTQ2Ny4zOTY0NDMzOTY4MDY3IDY4OC4zMzQ2NDAyMTYwODIzIDEzOTcuMDUxNDU3Njg2MzQ5N1E2ODIuODg1MTMwNjE3NzY3NiAxMzI2LjcwNjQ3MTk3NTg5MjggNjc1LjkyOTI5NTc4NTcyNTEgMTIzMy43Mjc5Njk5OTg0OTM4UTY2OC45NzM0NjA5NTM2ODI3IDExNDAuNzQ5NDY4MDIxMDk0OCA2NjMuNTQyMzgzOTcyNTU1NCAxMDIwLjkyODU0ODg5NDgyMjZRNjU4LjExMTMwNjk5MTQyODEgOTAxLjEwNzYyOTc2ODU1MDQgNjU4LjI3ODY4NzgxMjM4OCA3NTAuMDIyNjY4OTAxODMwOVpNNTc1LjEyMzA0NDQzODY2MDEgNjcxLjQwNTA5MTYyMDk4MTdINjMzLjg4NzUwNzI2MzU3MVE2MzMuODg3NTA3MjYzNTcxIDUyMi42MzA3Nzk3ODQxNzI4IDYzOS4zMTg1ODQyNDQ2OTgzIDQwMy43NzkzOTcyNzM0MzYyUTY0NC43NDk2NjEyMjU4MjU1IDI4NC45MjgwMTQ3NjI2OTk2IDY1MS43MDU0OTYwNTc4NjggMTkyLjEzNTM1MTc2MjE3NTU2UTY1OC42NjEzMzA4ODk5MTA1IDk5LjM0MjY4ODc2MTY1MTUyIDY2NC4wMjcxNTAwNzc3NDUzIDI4LjgxNDAzMjA3NTkyNjY2UTY2OS4zOTI5NjkyNjU1ODAyIC00MS43MTQ2MjQ2MDk3OTgxOSA2NjkuMzkyOTY5MjY1NTgwMiAtOTQuNzEyOTY5MjA5OTk4ODVRNjY5LjM5Mjk2OTI2NTU4MDIgLTEyOS4wNzA5NzAyNzQ1MDggNjUxLjU1NjU1MjgzMjk0NjIgLTE0Ny40MDEwMTAzOTU5NTkwMlE2MzMuNzIwMTM2NDAwMzEyMiAtMTY1LjczMTA1MDUxNzQxMDA0IDYwNC41NzQ3OTgwMTAyODk3IC0xNjUuNzMxMDUwNTE3NDEwMDRRNTc2Ljg5ODkxOTYxOTYxOTggLTE2NS43MzEwNTA1MTc0MTAwNCA1NTguMzExNDU3NDk0MjczOCAtMTQ3LjQxNzMyNjA4ODk5NDc0UTUzOS43MjM5OTUzNjg5Mjc3IC0xMjkuMTAzNjAxNjYwNTc5NDQgNTM5LjcyMzk5NTM2ODkyNzcgLTkzLjQwNjY1NjE4MzMwMjRRNTM5LjcyMzk5NTM2ODkyNzcgLTM2LjA5ODk3Mjk0NjQwNTQxIDU0NC45ODk4MDg0NTMyNDcgMzUuMzE5MTEzNTc0OTIyMDg1UTU1MC4yNTU2MjE1Mzc1NjYyIDEwNi43MzcyMDAwOTYyNDk1OCA1NTcuMTA3MjE2NDE3Nzg5NSAxOTguNTE3Nzg2MjMwODkxOTRRNTYzLjk1ODgxMTI5ODAxMjcgMjkwLjI5ODM3MjM2NTUzNDMgNTY5LjU0MDkyNzg2ODMzNjQgNDA3LjEyNTYwMTE0NDUwNzVRNTc1LjEyMzA0NDQzODY2MDEgNTIzLjk1MjgyOTkyMzQ4MDcgNTc1LjEyMzA0NDQzODY2MDEgNjcxLjQwNTA5MTYyMDk4MTdaIiB0cmFuc2Zvcm09InNjYWxlKDAuMTIzNDkgLTAuMTIzNDkpIHRyYW5zbGF0ZSgtNjEwLjUgLTcxMy4yKSIvPjwvZz48L2RlZnM+PGcgZmlsbD0iIzAwMCI+PHVzZSBocmVmPSIjZyIgeD0iNzEwLjQiIHk9IjMyIi8+PHVzZSBocmVmPSIjZyIgeD0iNzg3LjIiIHk9IjMyIi8+PHVzZSBocmVmPSIjZyIgeD0iODY0IiB5PSIzMiIvPjx1c2UgaHJlZj0iI2ciIHg9Ijk0MC44IiB5PSIzMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjEwMTcuNiIgeT0iMzIiLz48dXNlIGhyZWY9IiNnIiB4PSIxMDk0LjQiIHk9IjMyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTE3MS4yIiB5PSIzMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjEyNDgiIHk9IjMyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTMyNC44IiB5PSIzMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjQwMy4yIiB5PSIxNjAiLz48dXNlIGhyZWY9IiNnIiB4PSI0ODAiIHk9IjE2MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjU1Ni44IiB5PSIxNjAiLz48dXNlIGhyZWY9IiNnIiB4PSI2MzMuNiIgeT0iMTYwIi8+PHVzZSBocmVmPSIjZyIgeD0iNzEwLjQiIHk9IjE2MCIvPjx1c2UgaHJlZj0iI2ciIHg9Ijc4Ny4yIiB5PSIxNjAiLz48dXNlIGhyZWY9IiNnIiB4PSI4NjQiIHk9IjE2MCIvPjx1c2UgaHJlZj0iI2ciIHg9Ijk0MC44IiB5PSIxNjAiLz48dXNlIGhyZWY9IiNnIiB4PSIxMDE3LjYiIHk9IjE2MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjEwOTQuNCIgeT0iMTYwIi8+PHVzZSBocmVmPSIjZyIgeD0iMTE3MS4yIiB5PSIxNjAiLz48dXNlIGhyZWY9IiNnIiB4PSIxMjQ4IiB5PSIxNjAiLz48dXNlIGhyZWY9IiNnIiB4PSIxMzI0LjgiIHk9IjE2MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0MDEuNiIgeT0iMTYwIi8+PHVzZSBocmVmPSIjZyIgeD0iMTQ3OC40IiB5PSIxNjAiLz48dXNlIGhyZWY9IiNnIiB4PSIxNTU1LjIiIHk9IjE2MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjE2MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjMyNi40IiB5PSIyODgiLz48dXNlIGhyZWY9IiNnIiB4PSI0MDMuMiIgeT0iMjg4Ii8+PHVzZSBocmVmPSIjZyIgeD0iNDgwIiB5PSIyODgiLz48dXNlIGhyZWY9IiNnIiB4PSI1NTYuOCIgeT0iMjg4Ii8+PHVzZSBocmVmPSIjZyIgeD0iNjMzLjYiIHk9IjI4OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjExNzEuMiIgeT0iMjg4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTI0OCIgeT0iMjg4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTMyNC44IiB5PSIyODgiLz48dXNlIGhyZWY9IiNnIiB4PSIxNDAxLjYiIHk9IjI4OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iMjg4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTU1NS4yIiB5PSIyODgiLz48dXNlIGhyZWY9IiNnIiB4PSIxNjMyIiB5PSIyODgiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzA4LjgiIHk9IjI4OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3Mi44IiB5PSI0MTYiLz48dXNlIGhyZWY9IiNnIiB4PSIyNDkuNiIgeT0iNDE2Ii8+PHVzZSBocmVmPSIjZyIgeD0iMzI2LjQiIHk9IjQxNiIvPjx1c2UgaHJlZj0iI2ciIHg9IjQwMy4yIiB5PSI0MTYiLz48dXNlIGhyZWY9IiNnIiB4PSI0ODAiIHk9IjQxNiIvPjx1c2UgaHJlZj0iI2ciIHg9IjU1Ni44IiB5PSI0MTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxMjQ4IiB5PSI0MTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxMzI0LjgiIHk9IjQxNiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0MDEuNiIgeT0iNDE2Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTQ3OC40IiB5PSI0MTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxNTU1LjIiIHk9IjQxNiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjQxNiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3MDguOCIgeT0iNDE2Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTc4NS42IiB5PSI0MTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxODYyLjQiIHk9IjQxNiIvPjx1c2UgaHJlZj0iI2ciIHg9Ijk2IiB5PSI1NDQiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzIuOCIgeT0iNTQ0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMjQ5LjYiIHk9IjU0NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjMyNi40IiB5PSI1NDQiLz48dXNlIGhyZWY9IiNnIiB4PSI0MDMuMiIgeT0iNTQ0Ii8+PHVzZSBocmVmPSIjZyIgeD0iNDgwIiB5PSI1NDQiLz48dXNlIGhyZWY9IiNnIiB4PSI1NTYuOCIgeT0iNTQ0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTMyNC44IiB5PSI1NDQiLz48dXNlIGhyZWY9IiNnIiB4PSIxNDAxLjYiIHk9IjU0NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iNTQ0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTU1NS4yIiB5PSI1NDQiLz48dXNlIGhyZWY9IiNnIiB4PSIxNjMyIiB5PSI1NDQiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzA4LjgiIHk9IjU0NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3ODUuNiIgeT0iNTQ0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTg2Mi40IiB5PSI1NDQiLz48dXNlIGhyZWY9IiNnIiB4PSIxOTM5LjIiIHk9IjU0NCIvPjx1c2UgaHJlZj0iI2ciIHg9Ijk2IiB5PSI2NzIiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzIuOCIgeT0iNjcyIi8+PHVzZSBocmVmPSIjZyIgeD0iMjQ5LjYiIHk9IjY3MiIvPjx1c2UgaHJlZj0iI2ciIHg9IjMyNi40IiB5PSI2NzIiLz48dXNlIGhyZWY9IiNnIiB4PSI0MDMuMiIgeT0iNjcyIi8+PHVzZSBocmVmPSIjZyIgeD0iNDgwIiB5PSI2NzIiLz48dXNlIGhyZWY9IiNnIiB4PSI1NTYuOCIgeT0iNjcyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTQwMS42IiB5PSI2NzIiLz48dXNlIGhyZWY9IiNnIiB4PSIxNDc4LjQiIHk9IjY3MiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE1NTUuMiIgeT0iNjcyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTYzMiIgeT0iNjcyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTcwOC44IiB5PSI2NzIiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzg1LjYiIHk9IjY3MiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE4NjIuNCIgeT0iNjcyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTkzOS4yIiB5PSI2NzIiLz48dXNlIGhyZWY9IiNnIiB4PSIxOS4yIiB5PSI4MDAiLz48dXNlIGhyZWY9IiNnIiB4PSI5NiIgeT0iODAwIi8+PHVzZSBocmVmPSIjZyIgeD0iMTcyLjgiIHk9IjgwMCIvPjx1c2UgaHJlZj0iI2ciIHg9IjI0OS42IiB5PSI4MDAiLz48dXNlIGhyZWY9IiNnIiB4PSIzMjYuNCIgeT0iODAwIi8+PHVzZSBocmVmPSIjZyIgeD0iNDAzLjIiIHk9IjgwMCIvPjx1c2UgaHJlZj0iI2ciIHg9IjQ4MCIgeT0iODAwIi8+PHVzZSBocmVmPSIjZyIgeD0iNTU2LjgiIHk9IjgwMCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0MDEuNiIgeT0iODAwIi8+PHVzZSBocmVmPSIjZyIgeD0iMTQ3OC40IiB5PSI4MDAiLz48dXNlIGhyZWY9IiNnIiB4PSIxNTU1LjIiIHk9IjgwMCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjgwMCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3MDguOCIgeT0iODAwIi8+PHVzZSBocmVmPSIjZyIgeD0iMTc4NS42IiB5PSI4MDAiLz48dXNlIGhyZWY9IiNnIiB4PSIxODYyLjQiIHk9IjgwMCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE5MzkuMiIgeT0iODAwIi8+PHVzZSBocmVmPSIjZyIgeD0iMjAxNiIgeT0iODAwIi8+PHVzZSBocmVmPSIjZyIgeD0iMTkuMiIgeT0iOTI4Ii8+PHVzZSBocmVmPSIjZyIgeD0iOTYiIHk9IjkyOCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3Mi44IiB5PSI5MjgiLz48dXNlIGhyZWY9IiNnIiB4PSIyNDkuNiIgeT0iOTI4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMzI2LjQiIHk9IjkyOCIvPjx1c2UgaHJlZj0iI2ciIHg9IjQwMy4yIiB5PSI5MjgiLz48dXNlIGhyZWY9IiNnIiB4PSI0ODAiIHk9IjkyOCIvPjx1c2UgaHJlZj0iI2ciIHg9IjU1Ni44IiB5PSI5MjgiLz48dXNlIGhyZWY9IiNnIiB4PSIxNDc4LjQiIHk9IjkyOCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE1NTUuMiIgeT0iOTI4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTYzMiIgeT0iOTI4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTcwOC44IiB5PSI5MjgiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzg1LjYiIHk9IjkyOCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE4NjIuNCIgeT0iOTI4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTkzOS4yIiB5PSI5MjgiLz48dXNlIGhyZWY9IiNnIiB4PSIyMDE2IiB5PSI5MjgiLz48dXNlIGhyZWY9IiNnIiB4PSIxOS4yIiB5PSIxMDU2Ii8+PHVzZSBocmVmPSIjZyIgeD0iOTYiIHk9IjEwNTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzIuOCIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjI0OS42IiB5PSIxMDU2Ii8+PHVzZSBocmVmPSIjZyIgeD0iMzI2LjQiIHk9IjEwNTYiLz48dXNlIGhyZWY9IiNnIiB4PSI0MDMuMiIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjQ4MCIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjU1Ni44IiB5PSIxMDU2Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTQ3OC40IiB5PSIxMDU2Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTU1NS4yIiB5PSIxMDU2Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTYzMiIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3MDguOCIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3ODUuNiIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE4NjIuNCIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE5MzkuMiIgeT0iMTA1NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjIwMTYiIHk9IjEwNTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxOS4yIiB5PSIxMTg0Ii8+PHVzZSBocmVmPSIjZyIgeD0iOTYiIHk9IjExODQiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzIuOCIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjI0OS42IiB5PSIxMTg0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMzI2LjQiIHk9IjExODQiLz48dXNlIGhyZWY9IiNnIiB4PSI0MDMuMiIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjQ4MCIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjU1Ni44IiB5PSIxMTg0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTQ3OC40IiB5PSIxMTg0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTU1NS4yIiB5PSIxMTg0Ii8+PHVzZSBocmVmPSIjZyIgeD0iMTYzMiIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3MDguOCIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3ODUuNiIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE4NjIuNCIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE5MzkuMiIgeT0iMTE4NCIvPjx1c2UgaHJlZj0iI2ciIHg9IjIwMTYiIHk9IjExODQiLz48dXNlIGhyZWY9IiNnIiB4PSI5NiIgeT0iMTMxMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3Mi44IiB5PSIxMzEyIi8+PHVzZSBocmVmPSIjZyIgeD0iMjQ5LjYiIHk9IjEzMTIiLz48dXNlIGhyZWY9IiNnIiB4PSIzMjYuNCIgeT0iMTMxMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjQwMy4yIiB5PSIxMzEyIi8+PHVzZSBocmVmPSIjZyIgeD0iNDgwIiB5PSIxMzEyIi8+PHVzZSBocmVmPSIjZyIgeD0iNTU2LjgiIHk9IjEzMTIiLz48dXNlIGhyZWY9IiNnIiB4PSI2MzMuNiIgeT0iMTMxMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iMTMxMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE1NTUuMiIgeT0iMTMxMiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjEzMTIiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzA4LjgiIHk9IjEzMTIiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzg1LjYiIHk9IjEzMTIiLz48dXNlIGhyZWY9IiNnIiB4PSIxODYyLjQiIHk9IjEzMTIiLz48dXNlIGhyZWY9IiNnIiB4PSIxOTM5LjIiIHk9IjEzMTIiLz48dXNlIGhyZWY9IiNnIiB4PSI5NiIgeT0iMTQ0MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE3Mi44IiB5PSIxNDQwIi8+PHVzZSBocmVmPSIjZyIgeD0iMjQ5LjYiIHk9IjE0NDAiLz48dXNlIGhyZWY9IiNnIiB4PSIzMjYuNCIgeT0iMTQ0MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjQwMy4yIiB5PSIxNDQwIi8+PHVzZSBocmVmPSIjZyIgeD0iNDgwIiB5PSIxNDQwIi8+PHVzZSBocmVmPSIjZyIgeD0iNTU2LjgiIHk9IjE0NDAiLz48dXNlIGhyZWY9IiNnIiB4PSI2MzMuNiIgeT0iMTQ0MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iMTQ0MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE1NTUuMiIgeT0iMTQ0MCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjE0NDAiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzA4LjgiIHk9IjE0NDAiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzg1LjYiIHk9IjE0NDAiLz48dXNlIGhyZWY9IiNnIiB4PSIxODYyLjQiIHk9IjE0NDAiLz48dXNlIGhyZWY9IiNnIiB4PSIxOTM5LjIiIHk9IjE0NDAiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzIuOCIgeT0iMTU2OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjI0OS42IiB5PSIxNTY4Ii8+PHVzZSBocmVmPSIjZyIgeD0iMzI2LjQiIHk9IjE1NjgiLz48dXNlIGhyZWY9IiNnIiB4PSI0MDMuMiIgeT0iMTU2OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjQ4MCIgeT0iMTU2OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjU1Ni44IiB5PSIxNTY4Ii8+PHVzZSBocmVmPSIjZyIgeD0iNjMzLjYiIHk9IjE1NjgiLz48dXNlIGhyZWY9IiNnIiB4PSI3MTAuNCIgeT0iMTU2OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iMTU2OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE1NTUuMiIgeT0iMTU2OCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjE1NjgiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzA4LjgiIHk9IjE1NjgiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzg1LjYiIHk9IjE1NjgiLz48dXNlIGhyZWY9IiNnIiB4PSIxODYyLjQiIHk9IjE1NjgiLz48dXNlIGhyZWY9IiNnIiB4PSIyNDkuNiIgeT0iMTY5NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjMyNi40IiB5PSIxNjk2Ii8+PHVzZSBocmVmPSIjZyIgeD0iNDAzLjIiIHk9IjE2OTYiLz48dXNlIGhyZWY9IiNnIiB4PSI0ODAiIHk9IjE2OTYiLz48dXNlIGhyZWY9IiNnIiB4PSI1NTYuOCIgeT0iMTY5NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjYzMy42IiB5PSIxNjk2Ii8+PHVzZSBocmVmPSIjZyIgeD0iNzEwLjQiIHk9IjE2OTYiLz48dXNlIGhyZWY9IiNnIiB4PSI3ODcuMiIgeT0iMTY5NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iMTY5NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE1NTUuMiIgeT0iMTY5NiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjE2OTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzA4LjgiIHk9IjE2OTYiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzg1LjYiIHk9IjE2OTYiLz48dXNlIGhyZWY9IiNnIiB4PSIzMjYuNCIgeT0iMTgyNCIvPjx1c2UgaHJlZj0iI2ciIHg9IjQwMy4yIiB5PSIxODI0Ii8+PHVzZSBocmVmPSIjZyIgeD0iNDgwIiB5PSIxODI0Ii8+PHVzZSBocmVmPSIjZyIgeD0iNTU2LjgiIHk9IjE4MjQiLz48dXNlIGhyZWY9IiNnIiB4PSI2MzMuNiIgeT0iMTgyNCIvPjx1c2UgaHJlZj0iI2ciIHg9IjcxMC40IiB5PSIxODI0Ii8+PHVzZSBocmVmPSIjZyIgeD0iNzg3LjIiIHk9IjE4MjQiLz48dXNlIGhyZWY9IiNnIiB4PSI4NjQiIHk9IjE4MjQiLz48dXNlIGhyZWY9IiNnIiB4PSI5NDAuOCIgeT0iMTgyNCIvPjx1c2UgaHJlZj0iI2ciIHg9IjEzMjQuOCIgeT0iMTgyNCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0MDEuNiIgeT0iMTgyNCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iMTgyNCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE1NTUuMiIgeT0iMTgyNCIvPjx1c2UgaHJlZj0iI2ciIHg9IjE2MzIiIHk9IjE4MjQiLz48dXNlIGhyZWY9IiNnIiB4PSIxNzA4LjgiIHk9IjE4MjQiLz48dXNlIGhyZWY9IiNnIiB4PSI1NTYuOCIgeT0iMTk1MiIvPjx1c2UgaHJlZj0iI2ciIHg9IjYzMy42IiB5PSIxOTUyIi8+PHVzZSBocmVmPSIjZyIgeD0iNzEwLjQiIHk9IjE5NTIiLz48dXNlIGhyZWY9IiNnIiB4PSI3ODcuMiIgeT0iMTk1MiIvPjx1c2UgaHJlZj0iI2ciIHg9Ijg2NCIgeT0iMTk1MiIvPjx1c2UgaHJlZj0iI2ciIHg9Ijk0MC44IiB5PSIxOTUyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTAxNy42IiB5PSIxOTUyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTA5NC40IiB5PSIxOTUyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTE3MS4yIiB5PSIxOTUyIi8+PHVzZSBocmVmPSIjZyIgeD0iMTI0OCIgeT0iMTk1MiIvPjx1c2UgaHJlZj0iI2ciIHg9IjEzMjQuOCIgeT0iMTk1MiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0MDEuNiIgeT0iMTk1MiIvPjx1c2UgaHJlZj0iI2ciIHg9IjE0NzguNCIgeT0iMTk1MiIvPjwvZz48L3N2Zz4=")
    center / contain no-repeat;
}

/*
 * The definition, and the quietest thing in the header.
 *
 * It and the nav links were both `--color-muted` in the same mono face at within 0.03rem of
 * each other, so the whole row read as one undifferentiated band of grey type and neither
 * half was doing its job. The links moved up rather than this moving down: they are the only
 * things in the header anyone can press, and the tagline is a dictionary entry that has
 * already said everything it has to say. Down was also not available — the token below
 * `--color-muted` is `--color-ansi-8`, which is for rules and dividers and is exempt from the
 * 4.5:1 text floor precisely because it does not clear it. `check:contrast` measures token
 * slots against the ground, not what a rule does with them, so it would not have caught that.
 */
header.site .tagline {
  /* The grid the typewriter below counts in, declared once and inherited by the entry: the
     English entry is 46 cells wide, and a cell is one monospace advance plus one tracking
     gap. A locale that overrides `chrome.tagline` sets its own count beside it here, keyed
     off `:root[lang]` — the console has no inline styles to carry one in. Getting it wrong
     only makes the reveal chunkier or smoother than a character at a time, which is the
     whole reason this is a number in the stylesheet and not a measurement in JavaScript:
     neither tier is allowed the script that would do the measuring. */
  --tagline-chars: 46;
  /*
   * The stem WITHOUT its trailing space, and the interjection WITH it — 36 and 10, not 37
   * and 9. They still sum to 46, which is the invariant the untype depends on; what moved is
   * which side of the boundary the space sits on, and that is the whole fix for a bug that
   * showed as `tthanks`.
   *
   * At 37 the clip landed exactly where the `t` of `thank you` begins. Not near it — ON it:
   * the entry's width is `37 * (1ch + tracking)` and the 37th glyph starts at that same
   * `37 * (1ch + tracking)`, so the two are one floating-point comparison apart. Measured in
   * Chrome at DPR 2 the `t` fell 0.016px outside and stayed hidden; a hundredth of a pixel
   * the other way — a different DPR, zoom, platform rasteriser — and the whole glyph shows,
   * with the synonym typing straight after it.
   *
   * At 36 the first thing past the clip is the SPACE, which has no ink. The boundary now has
   * a full cell of slack in the direction that used to fail, and the space it gives up comes
   * back as padding on `.tagline__word` so the spacing reads identically.
   */
  --tagline-stem-chars: 36;
  --tagline-int-chars: 10;
  --tagline-tracking: 0.02em;
  --tagline-cell: calc(1ch + var(--tagline-tracking));

  /* One slot of the rotation, and the whole cycle. Four synonyms, so a word is on screen for
     12s and each one's turn is a quarter of the cycle — which is where the percentages in
     `tagline-word` come from, and what has to be recomputed if the list changes length.

     12s rather than the 20s this started at. At 20s the rotation read as something that had
     gone wrong and corrected itself twice a minute; at 12s it reads as a thing the header
     does. The floor is roughly the time to notice a word has changed and look back at it,
     which is where the slot stops being a rotation and starts being a flicker. */
  --tagline-slot: 12s;
  --tagline-cycle: 48s;

  color: var(--color-muted);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: var(--tagline-tracking);
}

/**
 * The caret: an element after the entry, blinking on the accent for as long as the page is
 * open.
 *
 * It is a real element rather than a border on the entry so that it stays after the last
 * character when the entry wraps — the narrow case below, where the typewriter does not run
 * and a border on a two-line box would draw down the right-hand edge of both lines.
 *
 * Accent, not `--color-muted` like the entry it follows. The entry is the quietest type in
 * the header on purpose (see above); the caret is the one thing in it that moves, and a
 * moving grey tick reads as a rendering artefact rather than as a cursor. It is also the
 * only place the brand green appears above the fold on most pages.
 *
 * `steps(2, jump-none)` resolves to exactly two held values — opacity 1 for the first half of
 * the period, 0 for the second — so it toggles the way a terminal caret does instead of
 * breathing in and out. 1.06s is the terminal default: 530ms lit, 530ms dark.
 *
 * Not wrapped in `prefers-reduced-motion: no-preference` the way the entrance below is,
 * because the blanket `reduce` rule at the foot of this file already handles it correctly:
 * one iteration at 0.01ms, no fill, so the caret settles back to its declared opacity and
 * simply stands still. A static caret is still the right furniture; a blinking one is what
 * WCAG 2.2.2 is about.
 */
header.site .tagline__caret {
  display: inline-block;
  width: 2px;
  height: 1em;
  margin-inline-start: 0.18em;
  /* Sits the bar across the mono x-height rather than hanging it off the baseline. */
  vertical-align: -0.16em;
  background: var(--color-accent);
  animation: tagline-caret 1.06s steps(2, jump-none) infinite;
}

@keyframes tagline-caret {
  from {
    opacity: 1;
  }

  to {
    opacity: 0;
  }
}

/**
 * The slot the synonyms share, and their resting state: nothing at all.
 *
 * `display: none` is the base, not `width: 0`, because this is what the markup does whenever
 * the rotation below is NOT running — a narrow viewport, `prefers-reduced-motion`, a printed
 * page. In every one of those the entry keeps its own last word for good, and a synonym that
 * merely had no width would still be laid out, still be read by find-in-page, and still come
 * away on the clipboard. The entry alone is the entry; the synonyms exist only while
 * something is animating them.
 *
 * `user-select` covers the case where they ARE animating. A word waiting its turn is clipped
 * to zero width rather than removed, so its text is still in the DOM between the entry and
 * the caret — without this, dragging across the header yields the entry with every synonym
 * concatenated onto its tail. `aria-hidden` keeps the same duplication out of the
 * accessibility tree; this keeps it off the clipboard.
 */
header.site .tagline__swap {
  user-select: none;
}

header.site .tagline__word {
  display: none;
}

/**
 * The entrance: the entry types itself out, one cell per step, and the caret rides along in
 * front of it because it is the next thing in the line box — a growing `width` pushes it.
 * That is the whole reason the reveal is a width and not a `clip-path`: clipping does not
 * move anything, so the caret would sit parked at the end of a line still being typed.
 *
 * Guarded twice.
 *
 * `prefers-reduced-motion: no-preference`, rather than leaning on the blanket `reduce` rule
 * below, because that rule collapses the duration but not the `animation-delay` — the entry
 * would still blink out of existence for 200ms on every navigation. Outside this query there
 * is no animation to collapse: the entry is an ordinary span and the tagline is just there.
 *
 * The width, because typing needs the entry on one line and 46 cells of Geist Mono at
 * 0.75rem is 342.2px — wider than the row a 360px phone has left after the body's 1.25rem
 * padding, and the root clips overflow, so the last cells would be cut rather than wrapped.
 * 26rem leaves 34px of slack, which is what buys room for a classic 15px desktop scrollbar
 * at that width as well; below it the entry stays a span that wraps where it likes and only
 * the caret runs.
 *
 * `screen` because a `backwards` fill is held at zero width until the timeline runs, and a
 * print rendering has no timeline — the tagline would be missing from the page rather than
 * merely un-animated on it.
 */
@media screen and (min-width: 26rem) and (prefers-reduced-motion: no-preference) {
  header.site .tagline__entry {
    display: inline-block;
    overflow: hidden;
    white-space: nowrap;
    /* Both the resting state and the animation's target. `ch` is the advance of `0`, which in
       a monospace face is the advance of everything — so this is exact for Geist Mono and
       stays exact for whichever fallback mono is painting during the `font-display: swap`
       window, instead of a pixel count that would clip the last character in one of them. */
    width: calc(var(--tagline-chars) * 1ch + var(--tagline-chars) * var(--tagline-tracking));
    /* `overflow: hidden` moves an inline-block's baseline to its bottom margin edge, which
       would lift the entry off the line the wordmark is aligned to. The box is exactly one
       strut tall, so aligning the bottoms instead puts it back precisely. */
    vertical-align: bottom;
    /* Two animations on one width, handed over cleanly because neither fills where the other
       is working. `tagline-type` is `backwards`, so the delay holds at zero width rather than
       flashing the finished entry and then wiping it, and it contributes nothing once it has
       run — the declared width above takes back over. `tagline-untype` is `forwards` and
       contributes nothing until its delay elapses, then holds the stem width for good.
       ~33ms a cell typing, ~22ms backspacing: a fast typist, not a teleprinter. It was 26ms
       and read as a paste rather than a keystroke — the point of a typewriter is that the
       eye can follow it, and below about 30ms a cell it cannot.

       `jump-start` on the untype, and it is not a taste call. A `forwards` fill holds the
       value at the END of the active interval, and `jump-end` never reaches it — its last
       step is (n-1)/n, so the entry settled one cell short and sat there for good with the
       `t` of `thank you` still showing under the synonym. `jump-start` puts the first jump
       at zero and the last at one, which is also what backspacing feels like: the key takes
       a character the instant it goes down. The entrance keeps `end` because it does not
       fill forwards — once it is done the declared width takes back over. */
    animation:
      tagline-type 1.5s steps(var(--tagline-chars), end) 0.2s backwards,
      tagline-untype 0.2s steps(var(--tagline-int-chars), jump-start) var(--tagline-slot) forwards;
  }

  /**
   * A navigation is not an arrival, so the entrance plays once a tab.
   *
   * `data-chrome="settled"` is stamped on the root before first paint by the site's inline
   * head script — the same before-paint slot, and the same by-hash CSP admission, as the
   * theme script beside it. Only the ENTRANCE is dropped; the entry still gives up its last
   * word at 12s and the rotation still runs, because those are the page's heartbeat rather
   * than its arrival, and freezing them would leave the caret blinking beside a word that
   * never changes.
   *
   * The console can never set the attribute — `script-src 'none'` is the property that makes
   * "the ad surfaces literally cannot run code" true, and this is not the feature worth
   * spending it on. It types the entry in on every page, which is the right behaviour for a
   * dashboard somebody lands on rather than browses.
   */
  :root[data-chrome="settled"] header.site .tagline__entry {
    animation: tagline-untype 0.2s steps(var(--tagline-int-chars), jump-start) var(--tagline-slot)
      forwards;
  }

  /**
   * The rotation: each synonym clipped to nothing until its turn, so the words stack in the
   * same slot without any of them being taken out of flow. Only one is ever wider than zero,
   * which is what keeps the caret — the next thing in the line box — parked against whichever
   * word is currently typed, with no second animation to keep in sync.
   *
   * The 0.24s dead beat between one word backspacing and the next typing is deliberate: the
   * caret blinks alone for a moment, the way a prompt does when somebody is deciding.
   */
  header.site .tagline__word {
    display: inline-block;
    overflow: hidden;
    white-space: nowrap;
    /* The space the entry stopped drawing. Inside the animated width (`box-sizing:
       border-box` is global), so it is nothing at all while the word is clipped to zero and
       the caret sits flush against the entry between synonyms — which a margin here would
       have broken. `--word-chars` counts it, which is why every one of them is a letter more
       than its word. */
    padding-inline-start: var(--tagline-cell);
    vertical-align: bottom;
    width: 0;
    animation: tagline-word var(--tagline-cycle) steps(var(--word-chars), end) infinite;
  }

  /* The counts CSS cannot derive. Order is `TAGLINE_INTERJECTIONS` from index 1, then back to
     index 0 — `test/chrome.test.ts` fails if these drift from the strings. */
  header.site .tagline__word:nth-child(1) {
    --word-chars: 7; /* thanks, plus the leading space */

    animation-delay: 12.2s;
  }

  header.site .tagline__word:nth-child(2) {
    --word-chars: 13; /* much obliged, plus the leading space */

    animation-delay: 24.2s;
  }

  header.site .tagline__word:nth-child(3) {
    --word-chars: 7; /* cheers, plus the leading space */

    animation-delay: 36.2s;
  }

  header.site .tagline__word:nth-child(4) {
    --word-chars: 10; /* thank you, plus the leading space */

    animation-delay: 48.2s;
  }
}

@keyframes tagline-type {
  from {
    width: 0;
  }
}

/* The entry gives up its own last word so the rotation can take the slot over. It never grows
   back: from here the tail belongs to `.tagline__swap`. */
@keyframes tagline-untype {
  to {
    width: calc(var(--tagline-stem-chars) * var(--tagline-cell));
  }
}

/**
 * One word's turn, as a share of the whole cycle: type in over 0.36s, hold, backspace over
 * 0.24s, then stay at nothing while the other three take theirs.
 *
 * The stops are percentages of `--tagline-cycle` (48s), so they are the one thing here that a
 * change to the number of synonyms invalidates — 24.5% is 11.76s, a slot less the beat that
 * follows it, and it is a quarter only because there are four words. The durations were
 * picked to land on exact percentages at this cycle length: 0.36s is 0.75% of 48s and 0.24s
 * is 0.5%, where the 0.3s/0.2s they replace would have been recurring decimals.
 *
 * The timing function is on the ELEMENT, not in here, and that is not a style preference.
 * `animation-timing-function: steps(var(--word-chars), end)` inside a keyframe is dropped:
 * `var()` does not substitute there, the declaration is discarded as invalid, and the segment
 * silently falls back to the animation's own function. Measured against a literal in the same
 * page — `steps(6, end)` stepped in sixths, `steps(var(--n), end)` ramped smoothly — which is
 * why the synonyms used to slide in rather than type. The same `var()` works fine in the
 * `animation` shorthand on the element, which is where it now lives.
 *
 * On the element it applies to every segment rather than the two that move, and that costs
 * nothing: the hold and the tail are both constant widths, where a timing function has no
 * observable effect.
 */
@keyframes tagline-word {
  0% {
    width: 0;
  }

  0.75%,
  24% {
    width: calc(var(--word-chars) * var(--tagline-cell));
  }

  24.5%,
  100% {
    width: 0;
  }
}

nav.site {
  display: flex;
  align-items: center;
  gap: var(--space-s) var(--space-l);
  /* Measured at a 386px viewport the row is 425px wide, and the root clips overflow — so
     without wrapping the theme toggle sits past the edge, invisible and unclickable. The
     header wraps; the nav inside it did not. */
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* The interactive layer of the header, and now the brightest: the links lead, the tagline and
   the ⌘K hint and the theme toggle sit back at `--color-muted` behind them. */
nav.site a {
  --link-hover: var(--color-accent);

  color: var(--color-fg);
  text-decoration: none;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-slight);
  position: relative;
  padding-bottom: 2px;
}

/*
 * The underline is drawn rather than toggled, so it reads as a terminal cursor sweeping under
 * the word. Hover only where hover is real: a touch device fires it on tap and would leave
 * every tapped link stuck in the hovered state.
 *
 * Both link rows, from one definition. They differ only in where hover goes — the header sits
 * at `--color-fg` and brightens to the accent, the footer sits at `--color-muted` and comes up
 * to `--color-fg` — so the destination is a custom property each row sets for itself and the
 * mechanics below never mention a colour.
 *
 * It is deliberately NOT on every link that had its underline removed. `header.site .brand` is
 * the wordmark rather than a link to read, and everything else on the site is inline in prose,
 * where the static underline is the thing that makes a link findable without a pointer. A
 * sweep there would trade an affordance for an animation.
 *
 * Focus is already covered: the global `:focus-visible` outline draws on both rows, which is
 * why this can stay inside a hover query without stranding the keyboard.
 */
@media (hover: hover) and (pointer: fine) {
  :is(nav.site, nav.footer__links) a::after {
    content: "";
    position: absolute;
    inset-inline-start: 0;
    bottom: 0;
    width: 100%;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 180ms var(--ease-out-quart);
  }

  /* The underline is the main affordance either way — this keeps hover saying something on its
     own, for a pointer that is over the word but not yet reading the rule under it. */
  :is(nav.site, nav.footer__links) a:hover {
    color: var(--link-hover);
  }

  :is(nav.site, nav.footer__links) a:hover::after {
    transform: scaleX(1);
  }
}

/**
 * Nav links get a real 44px target where the pointer is a finger.
 *
 * Measured at 390px they were 22px tall — half the Apple/WCAG 2.5.5 guideline. They are also
 * NOT covered by WCAG 2.5.8's inline exception: that exempts links sitting in a sentence,
 * whose size is constrained by the line-height of the text around them. A navigation row is
 * a list of standalone targets and has no such excuse.
 *
 * `pointer: coarse` rather than a width breakpoint, because the problem is the input device,
 * not the viewport — a narrow desktop window still has a mouse. It also means the desktop
 * layout, which is already tuned, does not move at all.
 *
 * Height rather than a `::before` hitbox, which is the trick used on the theme toggle. A 44px
 * absolutely-positioned hitbox on each nav link would overlap its neighbours — the wrapped
 * rows sit about 31px apart — and overlapping hitboxes mean the tap lands on whichever
 * element wins the z-order, which is worse than a small target because it is wrong rather
 * than merely fiddly.
 */
@media (pointer: coarse) {
  nav.site a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding-bottom: 0;
  }

  /* The taller links supply their own separation; keeping the row gap as well would push the
     header down for no reason. */
  nav.site {
    row-gap: 0;
  }
}

footer {
  margin-top: var(--space-4xl);
  padding-top: var(--space-l);
  border-top: 1px solid var(--color-line);
  color: var(--color-muted);
  font-size: var(--text-md);
  text-wrap: pretty;
}

/* Who runs this, under the links. A step quieter again: it is the answer to a question
   nobody asks until it matters, and it should be there when they do. */
.footer__legal {
  margin-block: var(--space-l) 0;
  font-size: var(--text-base);
  color: var(--color-muted);
}

/* The pages the nav gave up (`footerNav()`). Same mono as `nav.site a` so they read as the
   same kind of thing, a step down in size and left at `--color-muted` so they read as the
   quieter copy of it. Wraps for the same reason the header nav does: the root clips
   overflow, so a row that cannot wrap puts its last link past the edge. */
nav.footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s) var(--space-l);
  margin-top: var(--space-l);
}

nav.footer__links a {
  --link-hover: var(--color-fg);

  color: var(--color-muted);
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  letter-spacing: var(--tracking-slight);
  text-decoration: none;
  position: relative;
  padding-bottom: 2px;
}

/* ── Figures ────────────────────────────────────────────────────────────── */

.figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 1px;
  /* The page colour, not the rule colour. Filling the container with the rule colour and
     letting it show through the gaps is the usual trick, but it also fills any cell the grid
     did not use — six tiles in a four-column row leaves two, and they render as a grey slab
     that reads like a broken tile. Each figure draws its own hairline instead. */
  background: var(--color-bg);
  border-radius: var(--radius);
  overflow: hidden;
}

/* A sentence after a row of figures or a table. Both blocks end without a margin, so the
   paragraph under them sat flush against their last row. */
:is(.figures, .overflow) + p {
  margin-top: var(--space-l);
}

/* At most three to a row, for a group of six: `auto-fit` at 11rem fits four across the reading
   measure, which laid six figures out as four and two. Each column is at least a third of the
   row (less the two 1px gaps) and never under 11rem, so a phone still gets two columns. */
.figures--three {
  grid-template-columns: repeat(auto-fit, minmax(max(11rem, calc((100% - 2px) / 3)), 1fr));
}

.figure {
  background: var(--color-bg);
  padding: var(--space-l) var(--space-l);
  /* A shadow rather than a border, so adjacent tiles overlap inside the 1px gap and meet as
     a single hairline instead of a doubled one. */
  box-shadow: 0 0 0 1px var(--color-line);
}

.figure .label {
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--color-muted);
  display: flex;
  align-items: flex-start;
  gap: var(--space-s);
  /* Two lines' worth, always. A label that wraps would otherwise push its own value down and
     break the baseline it shares with the rest of the row — most visible on "Open-source pool
     (70%)", which sits beside three single-line labels. */
  min-height: 2.1em;
  line-height: var(--leading-snug);
}

.figure .value {
  font-family: var(--font-mono);
  font-size: var(--text-3xl);
  font-weight: 500;
  letter-spacing: var(--tracking-tight);
  margin-top: var(--space-xs);
  font-variant-numeric: tabular-nums;
}

.figure .note {
  margin-top: var(--space-xs);
}

.value.paid {
  color: var(--color-paid);
}

.value.pending {
  color: var(--color-pending);
}

.pill {
  font-family: var(--font-mono);
  /* A pill is one token. Left to wrap, its 1px border breaks into a fragment per line and the
     result reads as a rendering fault — seen on the console's `no-active-creatives`, which is
     long enough to wrap in a narrow table cell. */
  white-space: nowrap;
  font-size: var(--text-3xs);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  padding: var(--space-2xs) var(--space-s);
  border-radius: 4px;
  border: 1px solid currentColor;
}

.pill.pending {
  color: var(--color-pending);
}

.pill.paid {
  color: var(--color-paid);
}

/* Standalone rule cards on /moderation. */
.rule {
  border: 1px solid var(--color-line);
  border-radius: var(--radius);
  margin-bottom: var(--space-l);
}

/* ── Tables ─────────────────────────────────────────────────────────────── */

table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-base);
}

th {
  text-align: start;
  font-family: var(--font-mono);
  font-weight: 500;
  color: var(--color-muted);
  font-size: var(--text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  padding: var(--space-s) var(--space-s);
}

td {
  padding: var(--space-s) var(--space-s);
  border-top: 1px solid var(--color-line);
  font-variant-numeric: tabular-nums;
}

td.num,
th.num {
  text-align: end;
}

/* One series, one hue — so the bar carries the comparison and the number beside it keeps its
   text token. Colouring the figure too would say the two encode different things. */
td.bar {
  color: var(--color-accent);
  letter-spacing: var(--tracking-tightest);
  white-space: pre;
  width: 1%;
  padding-inline-end: var(--space-xs);
}

th.bar-head {
  width: 1%;
  white-space: nowrap;
}

/* A gutter marker on the hovered row — the terminal idiom for "this one", and cheaper to
   read than a background wash. */
@media (hover: hover) and (pointer: fine) {
  tbody tr {
    position: relative;
  }

  tbody tr td:first-child::before {
    content: "▸";
    position: absolute;
    inset-inline-start: -0.85rem;
    color: var(--color-accent);
    opacity: 0;
    transform: translateX(-2px);
    transition:
      opacity 150ms ease,
      transform 150ms var(--ease-out-quart);
  }

  tbody tr:hover td:first-child::before {
    opacity: 1;
    transform: translateX(0);
  }
}

/* ── Ad previews ─────────────────────────────────────────────────────────
   The four slots an advertiser may pick, bound to the real ANSI tokens. Red and
   yellow are absent because `client/src/style.ts` does not offer them: in a
   terminal they mean error and warning, and an ad in red is impersonating a
   build failure. There is nothing to style here because there is nothing to
   pick. */

.slot-cyan {
  color: var(--color-ansi-6);
}

.slot-blue {
  color: var(--color-ansi-4);
}

.slot-green {
  color: var(--color-ansi-2);
}

.slot-magenta {
  color: var(--color-ansi-5);
}

.ad-preview {
  font-size: var(--text-base);
  line-height: var(--leading-relaxed);
  /* 80 visible characters is the status-line budget (`MAX_VISIBLE_LENGTH`). Reserving the
     room means the preview reads as the single line a developer actually sees, instead of
     wrapping into a shape the product never produces. `.overflow` on the table handles the
     narrow-viewport case by scrolling rather than by rewrapping. */
  min-width: 34rem;
}

/* The brand mark, at the size it ships and no larger. The bytes are capped at 160x24, so
   this only guards against a stored asset that predates a cap change — scaling one UP to fill
   a preview would show a reviewer something better-looking than what reaches a developer. */
.ad-logo {
  /* The mark sits ON the line, beside the label, because that is where it sits in a terminal.
     Tailwind's preflight makes every `img` a block, so the site put the mark on its own row
     while the console — which loads no preflight — kept it inline: two surfaces whose entire
     claim is "exactly as it appears", appearing differently. Declared here, both agree. */
  display: inline-block;
  max-height: 24px;
  max-width: 160px;
  vertical-align: text-bottom;
  margin-inline-end: 0.5ch;
}

/* The label is never styled and never coloured — §3 makes it the disclosure, and a
   disclosure that competes with the copy is not one. */
.ad-label {
  color: var(--color-muted);
}

.ad-link {
  text-decoration: underline;
  text-underline-offset: 2px;
}

.is-bold {
  font-weight: 650;
}

.is-italic {
  font-style: italic;
}

/* Reverse video, not a background colour — the terminal swaps the reader's own foreground
   and background, so it cannot render unreadably against a theme we know nothing about. */
.is-highlight {
  background: var(--color-fg);
  color: var(--color-bg);
  padding: 0 0.15em;
}

/* ── Surfaces and controls ──────────────────────────────────────────────── */

.card {
  border: 1px solid var(--color-line);
  border-radius: var(--radius);
  padding: var(--space-l) var(--space-l);
  background: var(--color-surface);
}

.empty {
  color: var(--color-muted);
  font-style: italic;
  padding: var(--space-xl) 0;
}

.overflow {
  overflow-x: auto;
}

/*
 * One checkmark, wherever something is on.
 *
 * The browser's own box is the only blue on a green page, and `accent-color` alone still
 * leaves each engine drawing its own tick. So the box is replaced: the accent square carries
 * the mark below, which is Lucide's `check` — the same one the copy button shows when a
 * command has been copied.
 *
 * One definition serves both uses. As a background it paints the tick on the filled square; as
 * a mask the stroke is ignored and the colour comes from `background-color`, which is how a
 * chip shows the same tick in the accent without a second copy of the path.
 *
 * The stroke is the page's own ground, because the tick sits on the accent: near-white on the
 * light accent and near-black on the dark one. A URL cannot go through `light-dark()`, so the
 * pair below restates `--color-bg` from `tokens.css` and the scheme is read the way
 * `global.css` reads it — the console, which has no toggle, follows the system.
 */
:root {
  --icon-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fbfbf9' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --icon-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230b0e0f' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
  }
}

:root[data-theme="dark"] {
  --icon-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230b0e0f' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

input[type="checkbox"] {
  appearance: none;
  flex: none;
  inline-size: 1.05em;
  block-size: 1.05em;
  border: 1px solid var(--color-line-strong);
  border-radius: 0.25em;
  background-color: var(--color-bg);
  cursor: pointer;
  transition:
    border-color 150ms ease,
    background-color 150ms ease;
}

input[type="checkbox"]:checked {
  border-color: var(--color-accent);
  background-color: var(--color-accent);
  background-image: var(--icon-check);
  background-position: center;
  background-repeat: no-repeat;
  background-size: 0.8em;
}

@media (hover: hover) and (pointer: fine) {
  input[type="checkbox"]:hover {
    border-color: var(--color-accent);
  }
}

button {
  font: inherit;
  font-weight: 550;
  padding: var(--space-s) var(--space-l);
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-line-strong);
  background: var(--color-surface);
  color: var(--color-fg);
  cursor: pointer;
  transition:
    transform 120ms var(--ease-out-quart),
    border-color 150ms ease;
}

/* Instant feedback that the interface is listening. */
button:active {
  transform: scale(0.97);
}

@media (hover: hover) and (pointer: fine) {
  button:hover {
    border-color: var(--color-accent);
  }
}

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

/* ── Disclosures ────────────────────────────────────────────────────────────
   The FAQ's questions and the composer's "Inline markup" and "Advanced". The markup contract
   and the slide are in `disclosure.ts`; what is here works with no script at all. */

.disclosure__summary {
  display: flex;
  align-items: center;
  gap: var(--space-s);
  cursor: pointer;
  list-style: none;
}

.disclosure__summary::-webkit-details-marker {
  display: none;
}

/*
 * The mark: a plus whose upright turns flat into a minus.
 *
 * Two bars drawn in CSS instead of an icon morph, because the console hydrates the composer too
 * and would otherwise ship an animation library for two lines. It reads `[open]` rather than
 * waiting for a script, so it is right however the panel opened. `data-closing` turns it back
 * the moment a close starts: `open` stays set until the slide ends, and a minus that lingered
 * through the whole close would say the panel was staying.
 *
 * The bars are the Lucide plus the FAQ used to draw, at the same 14px: 9.5px across. They
 * are 1.5px thick rather than that icon's 1.3px, so they land on whole device pixels at 2x.
 * `flex: none` so a long summary wrapping cannot squeeze the mark.
 */
.disclosure__mark {
  position: relative;
  flex: none;
  inline-size: 14px;
  block-size: 14px;
  color: var(--color-muted);
  transition: color 150ms ease;
}

.disclosure__mark::before,
.disclosure__mark::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  inline-size: 9.5px;
  block-size: 1.5px;
  border-radius: 1px;
  background: currentColor;
}

.disclosure__mark::after {
  transform: rotate(90deg);
  transition: transform 260ms var(--ease-out-quart);
}

.disclosure[open]:not([data-closing]) .disclosure__mark {
  color: var(--color-accent);
}

.disclosure[open]:not([data-closing]) .disclosure__mark::after {
  transform: rotate(180deg);
}

/* `flow-root` so a margin inside cannot collapse through the slot's edge. The slide measures
   the slot, and a margin hanging outside it would appear all at once after the slide ends. */
.disclosure__slot {
  display: flow-root;
}

.disclosure[data-sliding] > .disclosure__slot {
  overflow: hidden;
}

@media (hover: hover) and (pointer: fine) {
  .disclosure__summary:hover {
    color: var(--color-accent);
  }

  /* The mark sets its own colour, so the line above does not reach it by inheritance and the
     plus stayed grey while the words beside it went green. Anything else in a summary that
     quiets itself the same way needs the same line. */
  .disclosure__summary:hover .disclosure__mark {
    color: inherit;
  }
}

@media (pointer: coarse) {
  .disclosure__summary {
    min-height: 44px;
  }
}

/* ── Error details ──────────────────────────────────────────────────────────
   What `ErrorDetails` renders beneath an error page, in development only. A trace is read
   line by line and its lines run long, so it scrolls sideways inside its card rather than
   wrapping into a column nobody can scan; a message or a field value wraps, because it is
   read as a sentence. Ligatures are off because this text is evidence: Geist Mono's
   contextual alternates drew Postgres's `'...'` as `.!.'`. */

.error-details__entry {
  margin-block-start: var(--space-l);
  font-variant-ligatures: none;
}

.error-details__name {
  font-family: var(--font-mono);
  font-size: var(--text-md);
  letter-spacing: var(--tracking-normal);
  color: var(--color-error);
  margin-block: 0 var(--space-xs);
}

.error-details__message {
  color: var(--color-fg);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  margin-block-end: 0;
}

.error-details__fields {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--space-2xs) var(--space-l);
  margin-block: var(--space-m) 0;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}

.error-details__fields dt {
  color: var(--color-muted);
}

.error-details__fields dd {
  margin: 0;
  overflow-wrap: anywhere;
}

.error-details__trace {
  margin-block: var(--space-m) 0;
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
  color: var(--color-muted);
}

/**
 * Motion is an enhancement, never load-bearing.
 *
 * Every transition above is opt-in on top of a complete static state, so switching them off
 * removes decoration rather than information. `prefers-reduced-motion` is a real preference
 * with a real cause, and the canvas island reads the same query to render one static frame.
 */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/**
 * Forms, and the composer built out of them.
 *
 * ## Why the form vocabulary moved here
 *
 * It was in `packages/server/src/views/console-only.css`, under a comment reading "the
 * public site is read-only, so none of this belongs in the shared file." That was true right
 * up until the site grew a form — and not just any form: the composer is the SAME component
 * the console mounts, so the site and the console are now rendering identical markup. Two
 * copies of the styling for one component is the exact drift `shared-styles.test.ts` exists
 * to prevent, one layer down.
 *
 * So the primitives — `form.stack`, `label`, the controls, the filled submit — live here now
 * and both tiers serve them. `console-only.css` keeps what genuinely is console-only: the
 * moderation queue's affordances.
 *
 * ## No `style` attributes, on either tier
 *
 * Everything the composer needs is a class. Inline styles are governed by `style-src-attr`,
 * which CSP hashes do not cover, so an attribute here would be dropped by the console's
 * policy and the preview would render as undifferentiated text — the one surface where that
 * is not a cosmetic failure, since "exactly as it appears" is the whole claim.
 */

/* ── Form primitives ───────────────────────────────────────────────────── */

form.stack {
  display: grid;
  gap: var(--space-m);
  max-width: 30rem;
}

/*
 * A label is a sentence, not a stack.
 *
 * This was `display: grid`, which makes every inline child its own grid item — so the
 * targeting label, whose text is "one rule per line, <code>npm:react</code> or
 * <code>ecosystem:cargo</code>. Leave empty to match every dependency set.", rendered as five
 * stacked rows, one per element, with the two examples orphaned on lines of their own. Block
 * flow keeps the sentence together; the control below is placed explicitly.
 */
label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  line-height: var(--leading-relaxed);
  color: var(--color-muted);
}

label input,
label select,
label textarea {
  display: block;
  width: 100%;
  margin-top: var(--space-xs);
}

/*
 * ...but never the code inside one.
 *
 * The targeting field's label reads "one rule per line, <code>npm:react</code>", and uppercased
 * that instructs the advertiser to type NPM:REACT — a casing no package matches. The
 * transform is visual only, so a screen reader was always reading the right string; the person
 * looking at the form was the one being told the wrong thing.
 */
label code {
  text-transform: none;
  letter-spacing: var(--tracking-normal);
}

input,
select,
textarea {
  font: inherit;
  font-family: var(--font-mono);
  font-size: var(--text-base);
  padding: var(--space-s) var(--space-s);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  color: var(--color-fg);
  /* Matches the button's, so a field and the button beside it settle together. */
  transition: border-color 150ms ease;
}

input:focus,
select:focus,
textarea:focus {
  border-color: var(--color-accent);
}

/*
 * No form field renders below 16px on a touch screen.
 *
 * Below that, iOS zooms the whole page when a field takes focus, and the page stays zoomed after
 * it blurs. The fields above are 0.85rem and the command palette's is 0.95rem, both under it.
 *
 * A floor rather than a size, and `!important` because a floor that a class can override is not
 * one: `.cmdk__input` outranks any element selector. `max(…, 1em)` keeps a field that inherits
 * larger text at that size. `check:type` holds both halves — this rule has to exist, and no other
 * `font-size` may carry `!important` to win it back.
 */
@media (pointer: coarse) {
  input,
  select,
  textarea {
    font-size: max(var(--text-field-floor), 1em) !important;
  }
}

/* A field the composer is refusing takes the red of the message under it, focused or not: a
   green highlight beside a line reading "too long" says the opposite of it. */
:is(input, textarea, select)[aria-invalid="true"],
:is(input, textarea, select)[aria-invalid="true"]:focus {
  border-color: var(--color-error);
}

:is(input, textarea, select)[aria-invalid="true"]:focus-visible {
  outline-color: var(--color-error);
}

/* The primary control on a form is filled; base.css leaves buttons outlined, which is right
   for the site's one-per-page usage and too quiet for a page with three. */
button[type="submit"] {
  background: var(--color-fg);
  color: var(--color-bg);
  border-color: var(--color-fg);
}

/*
 * A disabled submit says "not yet", not "broken".
 *
 * The composer disables it while the copy is over budget or the markup does not parse, and
 * the reason is always printed directly above — so this only has to stop the button reading
 * as the next thing to click.
 */
button[type="submit"]:disabled {
  background: var(--color-surface-2);
  color: var(--color-muted);
  border-color: var(--color-line);
  cursor: not-allowed;
}

/* ── The composer ──────────────────────────────────────────────────────── */

/*
 * `form.composer`, not `.composer` — and the same for the modifier below.
 *
 * `form.stack` above is (0,1,1) and a bare class is (0,1,0), so a plain `.composer` rule loses
 * to it and both composers silently rendered at the generic 30rem form width. The same trap
 * caught `button.composer__cta`; anything overriding a rule in the primitives block at the top
 * of this file has to match its element-plus-class shape.
 */
form.composer {
  max-width: none;
  gap: var(--space-xl);
}

/*
 * The composer takes the page's whole measure, and the fields that hold a short value are
 * narrowed instead (`.composer__money`).
 *
 * It was capped at 34rem, which is a form measure for six stacked fields. The line being
 * written is up to 80 characters of mono, though, and at 34rem the field and the preview
 * showed about sixty of them. The preview is the one place on the page that has to show the
 * line whole, so the other fields get narrower rather than the preview getting shorter.
 */

/*
 * A field: its label, its control, and anything said about it.
 *
 * The gap inside a field is a third of the gap between fields, so a label reads as belonging
 * to the control under it and not to the one above. The old stack put a label, its control and
 * the next label all 12px apart, which is what made the form read as one clump.
 *
 * Also used on a `fieldset`, for groups of choices. Its groove and padding are reset here
 * because the console does not load the preflight that resets them on the site.
 */
.composer__field {
  display: grid;
  align-content: start;
  gap: var(--space-s);
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

.composer__field > input,
.composer__field > textarea {
  width: 100%;
}

/*
 * One size for everything in the form, told apart by weight and colour.
 *
 * The form used to set labels in 11.5px uppercase mono and notes in 13.1px, with the controls at
 * 13.6px and the page at 15px. Four sizes within a few pixels of each other read as mistakes,
 * not as hierarchy. Now a label is the control's size at 550 weight, a hint is the same size
 * in the muted colour, and the page's only other size is the heading.
 *
 * It overrides the global `label` rule (uppercase mono), which stays for the console's other
 * forms.
 */
.composer__label {
  display: block;
  padding: 0;
  font-family: var(--font-sans);
  font-size: var(--text-base);
  font-weight: 550;
  line-height: var(--leading-normal);
  letter-spacing: normal;
  text-transform: none;
  color: var(--color-fg);
}

/* A legend is not a grid item: the fieldset draws it in its own border area. So the gap the
   other children get from the grid is restated here as a margin. */
legend.composer__label {
  margin-block-end: var(--space-s);
}

/* A label with something set against its far edge: the line's character count. */
.composer__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-m);
}

.composer__hint {
  margin: 0;
  max-width: 68ch;
  font-size: var(--text-base);
  line-height: var(--leading-relaxed);
  color: var(--color-muted);
  text-wrap: pretty;
}

/*
 * The consent sentence's link, which base.css's rule for links in sentences does not reach:
 * it sits in a `label`, not a paragraph. The same treatment, so it reads as the other links on
 * the form do.
 */
.composer__consent a {
  text-decoration: underline;
  text-decoration-color: var(--color-line-strong);
  transition: text-decoration-color 150ms ease;
}

@media (hover: hover) and (pointer: fine) {
  .composer__consent a:hover {
    color: var(--color-accent);
    text-decoration-color: currentColor;
  }
}

/*
 * Why the form refused a field, under the field. The composer's own, in place of the browser's
 * bubble: see `composer-validation.tsx`. The same red as a field refusing the line's length.
 */
.composer__error {
  margin: 0;
  font-size: var(--text-base);
  line-height: var(--leading-relaxed);
  color: var(--color-error);
}

/* An amount's border and focus ring are on its group, not on the input inside it. */
.composer__money:has(input[aria-invalid="true"]) {
  border-color: var(--color-error);
  outline-color: var(--color-error);
}

/* Two short fields side by side, when there is room for both. */
.composer__pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: var(--space-xl) var(--space-l);
}

/*
 * A pair whose second half is one control, not a field: the swatches take the room and the
 * effect takes what it needs. Equal columns pushed Magenta onto a second row to leave space
 * beside a single chip.
 */
.composer__pair--aside {
  grid-template-columns: minmax(0, 1fr) auto;
}

/*
 * The preview sits in a well, not on the page.
 *
 * A sponsored line is something a developer sees inside another program's chrome, and
 * rendering it flush against the form would read as part of the form. The surface colour is
 * the same one `.card` uses, so the two agree without this needing to be a card — a card has
 * a border, and a border around the preview reads as a frame around the ad rather than as the
 * terminal it will actually appear in.
 */
.composer__preview {
  background: var(--color-surface);
  border-radius: var(--radius-sm);
  padding: var(--space-m) var(--space-l);
  min-height: 3rem;
  display: flex;
  align-items: center;
  overflow-x: auto;
}

/*
 * One line, however long, and scrolled rather than wrapped.
 *
 * `.ad-preview` reserves 34rem so that `/ads` shows a line at its real width. Inside this well
 * the reservation was wider than the well's own content box, so the well scrolled even for a
 * ten-character line and a long line still wrapped inside the 34rem. A status line is clipped
 * by the terminal, never wrapped, so the well scrolls instead.
 */
.composer__preview .ad-preview {
  min-width: 0;
  white-space: nowrap;
}

/*
 * The field and its button on one line.
 *
 * `flex-wrap` with a generous basis on the input rather than a breakpoint: the button drops
 * below the field when the two no longer fit, at whatever width that happens to be, which is
 * the right answer inside a hero that is sometimes a column and sometimes half of one.
 *
 * `align-items: stretch` so the button matches the input's height exactly. Left to its own
 * padding it lands a few pixels short, and a control that is nearly the same height as the
 * one beside it reads as a mistake in a way a deliberately different one does not.
 */
.composer__row {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: var(--space-s);
}

.composer__row input {
  flex: 1 1 18rem;
  min-width: 0;
}

.composer__row button {
  flex: 0 0 auto;
  white-space: nowrap;
}

/*
 * The call to action.
 *
 * Filled in the accent rather than the foreground, which is what `button[type="submit"]`
 * gives every other form. That is deliberate and it is the only place on the site that gets
 * it: this button is competing for attention with an install command, an animated ASCII
 * field and a live status-bar demo, and a dark-grey rectangle loses to all three.
 *
 * The accent is the ANSI green the palette already uses for links and focus rings, so this is
 * louder rather than new — and `--color-bg` on top of it clears WCAG AA on both grounds
 * (#0f7245 on near-white, #56d698 on near-black), which is why the pairing is these two
 * tokens and not a hand-picked hex.
 *
 * `button.composer__cta`, not `.composer__cta`: the `button[type="submit"]` rule above is
 * (0,1,1) and a bare class is (0,1,0), so the filled-foreground default would have won and
 * this would have rendered as the same grey rectangle it exists to replace — silently, since
 * both are valid CSS and the button still looks like a button.
 */
button.composer__cta {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-bg);
  font-weight: 600;
  padding-inline: var(--space-l);
  /* The label and the icon the hero draws after it, centred on one line — including when the
     button has wrapped to the full width of the row. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-s);
}

@media (hover: hover) and (pointer: fine) {
  /* base.css hovers the BORDER to the accent, which is invisible on a button already filled
     with it. Lifting the whole surface is the equivalent gesture for a solid control. */
  button.composer__cta:hover {
    filter: brightness(1.08);
  }
}

/* 37px of button is a small target for a thumb, and this is the primary action on the page.
   The same 44px the FAQ rows take, and for the same reason. */
@media (pointer: coarse) {
  button.composer__cta {
    min-height: 44px;
  }
}

/*
 * Once the button has wrapped, it should own its line.
 *
 * The row wraps on available width, which CSS cannot query — but below 30rem the hero column
 * IS the viewport, so the two agree and a media query is an honest proxy. A 9rem button
 * stranded at the left under a full-width field reads as an afterthought rather than as the
 * thing to press next.
 */
@media (max-width: 30rem) {
  .composer__row button {
    flex: 1 1 100%;
  }
}

/* The count beside the label. `visible / 80` alone; what it counts is said to a screen reader in
   `.composer__sr` rather than printed, since the number moving as you type says it already. */
.composer__count {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  color: var(--color-muted);
}

/* Read, not shown. The standard recipe: a 1px box clipped to nothing, still in the tree. */
.composer__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/*
 * The one colour that means "this is wrong".
 *
 * `--color-error` is the ANSI red the rest of the product reserves for failure, which is why
 * an ad may never use it (`/moderation` rule: no colour that means "error" in a terminal).
 * Using it HERE, on the interface around the ad rather than the ad itself, is the same
 * distinction the moderation rule is drawing.
 */
.is-over {
  color: var(--color-error);
}

.composer__problems {
  margin: 0;
  padding-inline-start: var(--space-l);
  display: grid;
  gap: var(--space-xs);
}

/* Pending, not error: a package name in copy is refusable, not refused — a reviewer decides,
   and the advertiser can still submit. Colouring it red would claim otherwise. */
.composer__warning {
  border-color: var(--color-pending);
}

.composer__warning ul {
  margin: var(--space-s) 0;
  padding-inline-start: var(--space-l);
}

.composer__help,
.composer__advanced {
  font-size: var(--text-base);
  color: var(--color-muted);
}

/* The markup reference hangs off the field above it, so it sits closer than a field would. */
.composer__help summary {
  padding-block: var(--space-2xs);
}

.composer__help .overflow {
  margin-block-start: var(--space-s);
}

.composer__help .note {
  margin-block: var(--space-s) 0;
}

.composer__advanced summary {
  padding-block: var(--space-s);
}

/* What the summary covers, quieter than the word that names it. */
.composer__aside {
  color: var(--color-muted);
  font-weight: 400;
  transition: color 150ms ease;
}

/* Inside a summary it is part of what the reader is pressing, so it follows the hover rather
   than staying grey beside a green word. Its own `color` is what stopped it inheriting. */
@media (hover: hover) and (pointer: fine) {
  .disclosure__summary:hover .composer__aside {
    color: inherit;
  }
}

/* The second accelerator over the rules, beside the map and above the field both write into.
   The same label-over-control rhythm every field on this form uses, so it reads as one of
   them rather than as a stray input. */
.composer__rulebuilder {
  display: grid;
  gap: var(--space-s);
  margin-block: var(--space-m) var(--space-l);
}

/*
 * A field and the button that commits it: the IP range row, and the rule builder's row minus
 * its kind select. One rhythm, because it is one gesture — type a value, add it to a list.
 */
.composer__addrow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-s);
  align-items: center;
}

/*
 * Tag rows that carry a name, in one grid so the tags line up under each other.
 *
 * `Uses` and `Reads` are not the same width, so a row apiece left the first tag of each
 * starting at a different place. The label column is sized by the longest of them.
 */
.composer__taggrid {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--space-s) var(--space-m);
  align-items: center;
}

/* Kind, value, add. The value takes the room because it is the part being searched; the other
   two are as wide as their own words. */
.composer__rulebuilder-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: var(--space-s);
  align-items: center;
}

/* A select is laid out from its own font metrics and came out 2px shorter than the input
   beside it. `stretch` makes the row's height the one they both take. */
.composer__rulekind {
  width: auto;
  margin: 0;
  align-self: stretch;
}

.composer__rulebuilder-row input,
.composer__ruleadd {
  align-self: stretch;
}

.composer__ruleadd {
  padding: var(--space-s) var(--space-l);
}

/* The advanced fields: the same rhythm as the form, indented under the mark that opened them. */
.composer__panel {
  display: grid;
  gap: var(--space-xl);
  padding-block: var(--space-l) var(--space-s);
  padding-inline-start: var(--space-xl);
}

/*
 * An amount: the field between its currency and its unit.
 *
 * The symbol and the unit are drawn inside the border, so the label can say "Budget" instead of
 * "Budget, in dollars". The border and the focus ring belong to the group; the input inside
 * draws neither. Capped at a width that fits the largest amount it takes, because a
 * page-wide box for a three-digit number reads as a field that expects a paragraph.
 */
.composer__money {
  display: flex;
  align-items: center;
  max-width: 16rem;
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius-sm);
  background: var(--color-bg);
  font-family: var(--font-mono);
  font-size: var(--text-base);
  transition: border-color 150ms ease;
}

.composer__money:focus-within {
  border-color: var(--color-accent);
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.composer__money input {
  flex: 1;
  min-width: 0;
  margin: 0;
  border: 0;
  background: transparent;
  padding-inline: var(--space-xs);
}

.composer__money input:focus-visible {
  outline: none;
}

/* A locked amount reads as a fact rather than a field. */
.composer__money:has(input:read-only) {
  border-style: dashed;
}

.composer__money input:read-only {
  color: var(--color-muted);
}

.composer__affix {
  flex: none;
  padding-inline-start: var(--space-m);
  color: var(--color-muted);
}

.composer__affix--end {
  padding-inline: var(--space-s) var(--space-m);
}

/*
 * A choice drawn as a row of chips: the line's colour, and italic beside it.
 *
 * Real radios and a real checkbox, hidden with the recipe that keeps them focusable, and the
 * face beside each one styled from its state with a sibling selector, which needs no `:has()`
 * and so no fallback. Five colours are few enough to show at once, and the swatch shows the
 * colour itself instead of a name that stands in for it.
 */
.composer__chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-s);
}

.composer__chip {
  position: relative;
  display: inline-flex;
  cursor: pointer;
  font-family: var(--font-sans);
  font-size: var(--text-base);
  letter-spacing: normal;
  text-transform: none;
  color: var(--color-fg);
}

.composer__chip input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.composer__chip-face {
  display: inline-flex;
  align-items: center;
  gap: var(--space-s);
  padding: var(--space-xs) var(--space-m);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius-sm);
  color: var(--color-muted);
  transition:
    border-color 150ms ease,
    color 150ms ease,
    background-color 150ms ease;
}

.composer__chip input:checked + .composer__chip-face {
  border-color: var(--color-accent);
  background: var(--color-surface);
  color: var(--color-fg);
}

/* A checkbox chip carries the same tick the boxes do; a colour chip has its swatch instead. */
.composer__chip input[type="checkbox"]:checked + .composer__chip-face::after {
  content: "";
  flex: none;
  inline-size: 0.8em;
  block-size: 0.8em;
  background-color: var(--color-accent);
  mask: var(--icon-check) center / contain no-repeat;
}

.composer__chip input:focus-visible + .composer__chip-face {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

@media (hover: hover) and (pointer: fine) {
  .composer__chip:hover .composer__chip-face {
    color: var(--color-fg);
  }
}

@media (pointer: coarse) {
  .composer__chip-face {
    min-height: 44px;
  }
}

/* Italic is a different kind of choice from the colours, so a rule sets it apart from them. */
/* The colour itself. `currentColor`, so the default swatch is the text colour a line with no
   colour renders in, and each other swatch takes its colour from the `.slot-*` class beside it. */
.composer__swatch {
  flex: none;
  inline-size: 0.625rem;
  block-size: 0.625rem;
  border-radius: 50%;
  background: currentColor;
}

/* The button, and the two facts about what pressing it does. */
.composer__submit {
  display: grid;
  justify-items: start;
  gap: var(--space-m);
}

.composer__submit button {
  padding-inline: var(--space-xl);
}

@media (pointer: coarse) {
  .composer__submit button {
    min-height: 44px;
  }
}

@media (max-width: 30rem) {
  .composer__submit {
    justify-items: stretch;
  }
}

/*
 * The consent line is a checkbox followed by a sentence, so it opts out of the label
 * vocabulary above — uppercase mono is a field name, and this is prose somebody has to read
 * before agreeing to it.
 *
 * The sentence is wrapped in one `span` in the markup. Left as bare text around a link, each run
 * of text and the link became a separate flex item, and the gap below opened up on both sides
 * of the link.
 */
.composer__consent {
  display: flex;
  align-items: baseline;
  gap: var(--space-s);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  text-transform: none;
  letter-spacing: normal;
  color: var(--color-fg);
  cursor: pointer;
}

.composer__consent input {
  width: auto;
  margin: 0;
  flex: none;
  translate: 0 0.15em;
}

/*
 * The targeting map.
 *
 * Drawn as one filled path per country over a fixed 180×76 viewBox, so it scales to any column
 * width without a media query and without a tile ever leaving the page. Colour carries the
 * whole state — selected, about to be selected, neither — so the three have to stay distinct
 * in both themes and at the size a country like Luxembourg occupies, which is one cell.
 */
.composer__targeting {
  gap: var(--space-m);
}

/*
 * The rule syntax, as a reference rather than a paragraph.
 *
 * It was one sentence carrying six examples, set in uppercase mono, so every `code` inside it
 * needed an exemption from the transform. A two-column list is quicker to scan and needs none.
 */
.composer__rules {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-xs) var(--space-l);
  margin: 0;
  font-size: var(--text-base);
  color: var(--color-muted);
}

.composer__rules dt {
  font-family: var(--font-mono);
  color: var(--color-fg);
}

.composer__rules dd {
  margin: 0;
}

/*
 * The moderation rules, beside the box that accepts them.
 *
 * Capped and scrolled rather than allowed to run: five rules is most of a screen, and a
 * consent checkbox that pushes the pay button off the page has made the form worse to read
 * what it is for. The cap is generous enough to show two rules at rest, which is what says
 * there are more.
 */
.composer__policy {
  display: grid;
  gap: var(--space-xs);
}

.composer__policy-rules {
  display: grid;
  gap: var(--space-xs);
  max-block-size: 14rem;
  overflow-y: auto;
  margin: 0;
  padding-block: var(--space-s);
  font-size: var(--text-base);
  line-height: var(--leading-normal);
  color: var(--color-muted);
}

.composer__policy-rules dt {
  font-weight: 550;
  color: var(--color-fg);
}

.composer__policy-rules dd {
  margin: 0;
  padding-block-end: var(--space-s);
}

/*
 * A rule as a chip: a closed set's toggles, and an open set's removable tags.
 *
 * One list style for both, because they are the same thing to a reader — what is on, and
 * where to click to change it. `is-on` is the only state: a toggle that is off and a tag that
 * exists at all are drawn the same, so a row of hosts reads as a row of hosts either way.
 */
.composer__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-s);
  margin: 0;
  padding: 0;
  list-style: none;
}

button.composer__tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-s);
  font: inherit;
  font-family: var(--font-mono);
  font-size: var(--text-md);
  padding: var(--space-xs) var(--space-s);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-muted);
  cursor: pointer;
}

/* Sized off the tag's own text so it stays with the label at any scale, and never squeezed
   by a long package id beside it. */
.composer__tagicon {
  flex: none;
  inline-size: 1.05em;
  block-size: 1.05em;
}

/* Names a tag row when two sit together and the vendor marks no longer tell them apart. */
.composer__tagrow {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  color: var(--color-muted);
}

button.composer__tag.is-on {
  border-color: var(--color-accent);
  background: var(--color-accent);
  color: var(--color-surface);
}

/* The cost of a rule kind: the one figure in the row, so it takes the text colour. */
.composer__cost {
  color: var(--color-fg);
  font-variant-numeric: tabular-nums;
}

.worldmap {
  display: grid;
  gap: var(--space-s);
  /* The canvas already refuses selection; the legend and the chips below it did not, so a
     drag that ran off the map, or a quick second click, highlighted the text underneath.
     WebKit wants the prefix: unprefixed `user-select` only landed in Safari 17, and every
     embedded WKWebView older than that reads this rule and ignores it. */
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

.worldmap__canvas {
  display: block;
  inline-size: 100%;
  block-size: auto;
  background: var(--color-surface);
  border-radius: var(--radius-sm);
  /* The gesture is a drag, and a drag over an SVG otherwise selects it or scrolls the page.
     WebKit paints a selection over the whole element rather than over the words in it, so the
     symptom there is the entire map turning highlight-blue. `-webkit-user-drag` is the other
     half: a selected country is filled with a flag image, and an image is draggable. */
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
  cursor: crosshair;
}

.worldmap__land {
  /* Cells are unit squares that share edges; without this the seams show as hairlines. */
  shape-rendering: crispedges;
}

/*
 * `:not(.is-selected)` on both, because a selected country is filled with its flag through a
 * `fill` PRESENTATION ATTRIBUTE, and any CSS `fill` here would win over it. The attribute is
 * used rather than an inline style because the console serves this under a CSP that drops
 * those, and `no-inline-styles.test.ts` is what keeps that honest.
 */
.worldmap__land:not(.is-selected) {
  fill: var(--color-ascii);
}

.worldmap__land.is-previewed:not(.is-selected) {
  fill: var(--color-pending);
}

/* Behind the flag, so a country still reads as chosen if the image never arrives. */
.worldmap__flagbase {
  fill: var(--color-accent);
}

/*
 * A country the raster cannot draw, marked rather than filled.
 *
 * Hollow while the circle is still being drawn and solid once chosen, which is the same two
 * states the land has. Smaller than a cell so it reads as a marker on the map rather than as
 * a country the size of Belgium.
 */
.worldmap__pin {
  fill: none;
  stroke: var(--color-pending);
  stroke-width: 0.45;
  vector-effect: non-scaling-stroke;
}

.worldmap__pin.is-selected {
  fill: var(--color-accent);
  stroke: var(--color-accent);
}

.worldmap__circle {
  fill: none;
  stroke: var(--color-accent);
  stroke-width: 0.5;
  vector-effect: non-scaling-stroke;
}

.worldmap__chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

button.worldmap__chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-s);
  font: inherit;
  font-size: var(--text-md);
  padding: var(--space-2xs) var(--space-s);
  border: 1px solid var(--color-line-strong);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-fg);
  cursor: pointer;
}

/* The chip's country, in the map's cells and the map's selected colour. A square box, so a
   long country and a tall one sit at the same size; `crispedges` keeps the cells square. */
.worldmap__glyph {
  flex: none;
  inline-size: 1.5em;
  block-size: 1.5em;
  fill: var(--color-accent);
  shape-rendering: crispedges;
}

button.worldmap__chip:hover {
  border-color: var(--color-accent);
  color: var(--color-accent);
}

/**
 * The console's own layer: what the site has no equivalent of.
 *
 * Everything here is either a form control or a moderation-queue affordance. The class names
 * that DO have equivalents — .card, .figures, .pill, table, .prose, .empty, header.site,
 * nav.site — are deliberately absent, because they are defined once in base.css and the
 * console's markup already uses exactly those names.
 *
 * A real stylesheet rather than a template literal in the .ts beside it, and that is not a
 * matter of taste: this file is mostly prose about CSS, and CSS prose is full of property
 * names that want to be quoted. Three separate times a `display: grid` or a `style-src-attr`
 * in a comment closed the template early and took the whole server down with a parse error.
 * A .css file cannot be closed early by its own contents.
 */
/* The form vocabulary — form.stack, label, the controls, the filled submit — moved to
   @obrigado/internal/styles/composer.css. It was here under "the public site is read-only,
   so none of this belongs in the shared file", which stopped being true when the site grew
   the composer: both tiers now render that same component, and two copies of one
   component's styling is the drift shared-styles.test.ts exists to prevent. */

/* ── Moderation queue ───────────────────────────────────────────────────── */

.entry {
  margin-bottom: var(--space-xl);
}

.entry .copy {
  margin: var(--space-s) 0;
}

.findings {
  margin: var(--space-s) 0 0;
  padding-inline-start: var(--space-l);
  font-size: var(--text-base);
  color: var(--color-muted);
}

.actions {
  display: flex;
  gap: var(--space-l);
  margin-top: var(--space-l);
  flex-wrap: wrap;
}

.actions form {
  display: flex;
  gap: var(--space-s);
  flex-wrap: wrap;
}

.actions input[type="text"] {
  min-width: 20rem;
}

/* ── Spacing utilities that replaced style attributes ───────────────────────
   A style attribute is governed by style-src-attr, which CSP hashes do not cover, so each
   one of these would have been dropped in production and the console would have rendered with
   its spacing and layout gone. Named here instead. */
.markup-help {
  font-size: var(--text-base);
}

.markup-help table {
  margin-top: var(--space-s);
}

.tight {
  margin: 0;
}

.tight-top {
  margin: var(--space-s) 0 0;
}

.spaced-top {
  margin-top: var(--space-xl);
}
