/* >>> GENERATED FROM packages/design-tokens/tokens.css — DO NOT EDIT BELOW.
   Change a token THERE, then run: node apps/landing/scripts/sync-tokens.mjs
   Editing this region by hand is the one failure this whole arrangement
   exists to prevent, and nothing about the page would look wrong if you did. */
/* =========================================================================
   PANOpeak Design System — Tokens & Globals
   Brand: sage / forest / terracotta — derived from the PANOpeak mountain mark
   ========================================================================= */

:root {
  /* ---- Brand: Sage ----
   * Sampled from the wordmark, not invented around it. The mark's green is
   * #859C84 → oklch(0.622 0.042 140): a MUTED green, roughly half the chroma
   * the old ramp carried at hue 135. That gap is why the old UI read as a
   * greener, brighter product than its own logo. Every step below holds hue
   * ≈140 and stays deliberately low-chroma; the colour earns attention through
   * contrast, not saturation.
   */
  --sage-50:  oklch(0.975 0.008 140);
  --sage-100: oklch(0.945 0.016 140);
  --sage-200: oklch(0.895 0.026 140);
  --sage-300: oklch(0.820 0.036 140);
  --sage-400: oklch(0.720 0.042 140);
  --sage-500: oklch(0.622 0.042 140);  /* the wordmark itself */
  --sage-600: oklch(0.520 0.044 141);
  --sage-700: oklch(0.420 0.044 142);
  --sage-800: oklch(0.320 0.038 143);
  --sage-900: oklch(0.220 0.028 145);

  /* ---- Accent: Terracotta ----
   * The sun dot over the peak: #C26F4C → oklch(0.606 0.113 45). Hue 45, not
   * the old 35 — the mark's dot is a burnt orange, not a red.
   *
   * This is the ONLY saturated colour in the system and it is rationed on
   * purpose: in the mark it appears exactly once, and the UI keeps that rule.
   * Terracotta means "live" or "needs you". If a screen shows two of them,
   * one of them is decoration and should be taken out.
   */
  --terra-50:  oklch(0.975 0.010 45);
  --terra-100: oklch(0.945 0.024 45);
  --terra-200: oklch(0.885 0.046 45);
  --terra-300: oklch(0.800 0.075 45);
  --terra-400: oklch(0.700 0.100 45);
  --terra-500: oklch(0.606 0.113 45);  /* the dot itself */
  --terra-600: oklch(0.520 0.105 44);
  --terra-700: oklch(0.420 0.086 43);
  --terra-800: oklch(0.320 0.066 42);
  --terra-900: oklch(0.230 0.048 40);

  /* ---- Neutral: Forest ----
   * Warm at the light end (paper), green-inked at the dark end. The hue walks
   * from 110 to 148 as lightness drops so pale surfaces read as warm stock and
   * text reads as a very dark green-black rather than a neutral grey. Nothing
   * here is a pure grey and nothing is pure black.
   */
  --forest-50:  oklch(0.978 0.005 110);
  --forest-100: oklch(0.958 0.007 110);
  --forest-200: oklch(0.920 0.008 115);
  --forest-300: oklch(0.855 0.009 120);
  --forest-400: oklch(0.700 0.010 125);
  /* 0.540, not the 0.560 the rest of this scale would suggest: this step is
     read ONLY through --fg-subtle, and at 0.560 it lands 4.35:1 on
     --bg-canvas — under the 4.5 that WCAG AA asks of body text, in 29
     places across the landing site alone. 0.540 is 4.74:1 there and
     5.05:1 on white, which leaves room for --bg-canvas to move without
     silently falling back under. Verified by
     apps/landing/scripts/audit-contrast.mjs; do not lighten it back
     without re-running that. */
  --forest-500: oklch(0.540 0.012 130);
  --forest-600: oklch(0.430 0.014 135);
  --forest-700: oklch(0.310 0.016 140);
  --forest-800: oklch(0.235 0.016 143);
  --forest-900: oklch(0.180 0.015 145);
  --forest-950: oklch(0.140 0.014 148);

  /* ---- Semantic ---- */
  --info-500:    oklch(0.55 0.075 235);
  --warn-500:    oklch(0.70 0.105 75);
  --success-500: oklch(0.58 0.090 150);
  --danger-500:  var(--terra-600);

  /* ---- Radii ----  SOFT SAGE, 2026-09-03
   * Reverses the 0–4px collapse. That ladder existed to echo the mark's mitred
   * corners, which put the whole UI in the job of imitating the logo; the
   * redesign gives that job back to the logo. The mark stays exactly as drawn
   * and is now the ONLY mitred object in the product — the same rationing the
   * terracotta dot gets — while every surface around it is round.
   *
   * The names are unchanged on purpose: ~46 call sites already reference these
   * tokens, so the entire app rounds by editing this block and nothing else.
   *
   * Nothing in the system is below 12px except --r-xs, which exists for chips
   * and thumbnails small enough that 12 would read as a circle.
   */
  --r-xs: 10px;
  --r-sm: 12px;
  --r-md: 16px;
  --r-lg: 22px;
  --r-xl: 28px;
  --r-2xl: 34px;
  --r-full: 999px;

  /* ---- Motion ----
   * Tokenised so a new component cannot invent its own timing. Theme-agnostic,
   * so they live here rather than in either theme block.
   *
   * ONE RULE decides the curve: spring for anything the USER did, decelerate
   * for anything the SYSTEM did. A button the seller pressed may overshoot; a
   * row that reordered itself may not — overshoot on a machine-initiated
   * change reads as instability rather than as mass.
   *
   * Two hard limits. Nothing exceeds --dur-5. And money NEVER springs: a figure
   * that overshoots and settles reads as a slot machine, so prices, deltas and
   * counts always take --ease-out.
   *
   * Every one of these is disabled wholesale by the prefers-reduced-motion
   * block at the bottom of this file — one rule, not a per-component decision.
   */
  --dur-1: 130ms;  /* instant state flips: pill tone, hover tint, focus ring */
  --dur-2: 170ms;  /* button press and hover — the standard control beat */
  --dur-3: 240ms;  /* toggle knob travel, tick draw, slider settle */
  --dur-4: 420ms;  /* disclosure, toast entry, row reorder travel */
  --dur-5: 620ms;  /* digit roll, stagger, sparkline draw — the longest allowed */

  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);  /* user-initiated ONLY */
  --ease-out:    cubic-bezier(0.22, 1, 0.36, 1);     /* system-initiated ONLY */

  /* ---- Type ----
   * ONE family for text and display. Archivo is variable on two axes
   * (wght 100–900, wdth 62–125), so the wide display cut and the normal-width
   * UI text come out of the same file — see --wdth-* below.
   *
   * There is deliberately no --font-serif any more. The redesign moved every
   * heading onto the display cut of Archivo and migrated all ~dozen
   * `font-serif` call sites, so a variable of that name would now be a trap:
   * it would not be a serif, and the next person to use it would get one by
   * accident in whatever fallback stack they wrote around it.
   */
  --font-sans:    "Archivo", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --font-display: "Archivo", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --font-mono:    "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Width axis. Display copy is set WIDE (115) to echo the wordmark's stretched
   * letterforms and its full-width baseline rule; running text stays at 100
   * because wide text at paragraph length is slow to read. */
  --wdth-text:    100;
  --wdth-display: 115;

  /* ---- Type scale ----
   * Every value is `rem`, i.e. relative to the ROOT element, which is left at
   * the browser default. That is the whole point: the app used to size every
   * one of ~197 inline styles in absolute px, so a reader who had raised their
   * browser font size saw no change at all (WCAG 1.4.4). Nothing may set a
   * font-size on `html` — doing so re-pins the scale and silently undoes this.
   *
   * The px comments are the rendered size at a default 16px root ONLY; a
   * reader at 20px sees all of them proportionally larger, which is correct
   * and will look like a bug to anyone who does not expect it.
   */
  --fs-3xs:  0.6875rem;  /* 11px */
  --fs-2xs:  0.75rem;    /* 12px */
  --fs-xs:   0.8125rem;  /* 13px */
  --fs-sm:   0.875rem;   /* 14px */
  --fs-base: 0.9375rem;  /* 15px */
  --fs-md:   1rem;       /* 16px — body base */
  --fs-lg:   1.0625rem;  /* 17px */
  --fs-xl:   1.125rem;   /* 18px */
  --fs-2xl:  1.1875rem;  /* 19px */
  --fs-3xl:  1.25rem;    /* 20px */
  --fs-4xl:  1.375rem;   /* 22px */

  /* Display sizes — page furniture (hero lines, section titles, hero numbers)
   * rather than steps in the running-text ladder. */
  --fs-display-xs:  1.625rem;  /* 26px */
  --fs-display-sm:  1.875rem;  /* 30px */
  --fs-display-md:  2rem;      /* 32px */
  --fs-display-lg:  2.125rem;  /* 34px */
  --fs-display-xl:  2.375rem;  /* 38px */
  --fs-display-2xl: 2.625rem;  /* 42px */
  --fs-display-3xl: 3.625rem;  /* 58px */

  /* Inline monospace inside sans body copy. A half-step below --fs-base
   * because Plex Mono renders optically larger than Archivo at the same
   * nominal size; matching the ladder exactly makes the code span dominate
   * the sentence around it. */
  --fs-mono-inline: 0.90625rem; /* 14.5px */
}

/* ---- THEME: Light (default) ---- */
:root, [data-theme="light"] {
  --bg-canvas:  var(--forest-50);
  --bg-base:    #ffffff;
  --bg-raised:  #ffffff;
  --bg-overlay: var(--forest-100);
  --bg-sunken:  var(--forest-100);
  --bg-inverse: var(--forest-900);

  --fg-strong:  var(--forest-900);
  --fg-default: var(--forest-800);
  --fg-muted:   var(--forest-600);
  --fg-subtle:  var(--forest-500);
  --fg-inverse: var(--forest-50);

  /* Text and glyphs ON a saturated palette fill — a terracotta button, a
     sage-700 chip.
   *
   * THE ONLY TOKEN IN THIS SHEET DECLARED WITH THE SAME VALUE IN BOTH THEME
   * BLOCKS, and that is the point rather than a copy-paste slip. The fills it
   * sits on (--terra-600, --sage-700) are palette steps, so they do not change
   * with the theme; a foreground that DOES change is therefore wrong in exactly
   * one theme. Measured, on the console's environment chip: --fg-inverse over
   * --sage-700 is 7.84:1 in light and 2.38:1 in dark, and over --terra-600 it
   * is 5.39:1 and 3.46:1 — so the dark theme silently shipped 11px bold text
   * below WCAG AA. This value holds 8.35:1 and 5.74:1 in both.
   *
   * It is declared in both blocks anyway, identically, so that the sheet's rule
   * — every token appears in both — still reads as true at a glance. Do not
   * "fix" the duplication by deleting one.
   *
   * `apps/web` predates this and hardcodes `#fff` at the same two call sites
   * (the danger and primary Button variants). Those can move onto this token;
   * it is backlog work, not a silent edit. */
  --on-accent: var(--forest-50);

  /* Borders carry the structure now that panels are flat, so they sit a step
     darker than the old set — a rule you cannot see is not a rule. */
  --border-default: oklch(0.885 0.008 118);
  --border-strong:  oklch(0.800 0.010 125);
  --border-subtle:  oklch(0.935 0.007 112);

  /* The edge of an UNCHECKED control — checkbox box, toggle track.
   *
   * Those are drawn as recessed wells, and a well is defined by its --sink-1
   * shadow plus a fill one step off the surface. Measured, that boundary was
   * 1.13:1 against white: WCAG 1.4.11 asks for 3:1 on the visual boundary of a
   * control, so an empty checkbox was effectively invisible to anyone who
   * needs contrast. None of the existing border tokens reach it either —
   * --border-strong is 1.86:1 on white — hence a token of its own rather than
   * borrowing one that was tuned for hairlines between surfaces.
   *
   * 3.93:1 on white, 3.69:1 on the canvas. */
  --control-edge: oklch(0.600 0.012 128);

  --accent-primary:    var(--sage-700);
  --accent-primary-fg: #ffffff;
  --accent-primary-hover: var(--sage-800);
  --accent-soft-bg:    var(--sage-100);
  --accent-soft-fg:    var(--sage-800);

  --accent-warm:       var(--terra-600);
  --accent-warm-soft-bg: var(--terra-100);
  --accent-warm-soft-fg: var(--terra-700);

  --status-win-bg:  oklch(0.955 0.020 150);
  --status-win-fg:  oklch(0.400 0.070 150);
  --status-loss-bg: var(--terra-100);
  --status-loss-fg: var(--terra-700);
  --status-pend-bg: oklch(0.960 0.030 80);
  --status-pend-fg: oklch(0.420 0.080 65);
  --status-info-bg: oklch(0.950 0.022 235);
  --status-info-fg: oklch(0.390 0.070 235);
  --status-neutral-bg: var(--forest-100);
  --status-neutral-fg: var(--forest-600);

  /* ---- Depth: LIGHT — a light source above ----
   * The half of the depth model that most systems write and then "invert" for
   * dark mode. It cannot be inverted; see the dark block for why.
   *
   * A raised object is lit from the top (a white inset edge), shaded
   * underneath (a dark inset edge) and drops a soft shadow. A sunk one is the
   * same two edges swapped. Four levels total, and only four:
   *   --rise-2  modals, drawers, the active row
   *   --rise-1  cards, panels, buttons, listing rows
   *   (flush)   the page ground — no shadow, a hairline at most
   *   --sink-1  inputs, slider tracks, toggle tracks, code blocks
   */
  --rise-1: inset 0 2px 0 rgba(255, 255, 255, 0.85),
            inset 0 -2px 0 oklch(0.18 0.015 145 / 0.09),
            0 6px 14px -8px oklch(0.18 0.015 145 / 0.32);
  --rise-2: inset 0 2px 0 rgba(255, 255, 255, 0.95),
            inset 0 -3px 0 oklch(0.18 0.015 145 / 0.12),
            0 18px 32px -20px oklch(0.18 0.015 145 / 0.55);
  --sink-1: inset 0 2px 5px oklch(0.18 0.015 145 / 0.14),
            inset 0 -1px 0 rgba(255, 255, 255, 0.60);
  /* The coloured lift under a primary button. Sage in light, and deliberately
     weaker in dark where a glow on a dark ground reads as a bloom. */
  --halo:      0 10px 22px -10px oklch(0.420 0.044 142 / 0.60);
  --halo-warm: 0 10px 22px -10px oklch(0.520 0.105 44 / 0.55);

  /* Elevation, retained for the three float surfaces that still use it
     (sidebar drawer, dropdowns, modals) and for nothing new. */
  --elev-1: 0 1px 1px oklch(0.20 0.02 140 / 0.03);
  --elev-2: 0 1px 2px oklch(0.20 0.02 140 / 0.05);
  --elev-3: 0 6px 20px oklch(0.20 0.02 140 / 0.10), 0 1px 3px oklch(0.20 0.02 140 / 0.06);
  --elev-4: 0 20px 50px oklch(0.20 0.02 140 / 0.16), 0 3px 8px oklch(0.20 0.02 140 / 0.08);

  --focus-ring: 0 0 0 2px var(--bg-base), 0 0 0 4px var(--sage-600);
}

/* ---- THEME: Dark ----  RETUNED 2026-09-04
 * The previous block aimed for "deep forest, not black" and did not get there.
 * It aliased the shared ramp for its grounds, so the canvas was forest-950 —
 * oklch(0.140 0.014 148), which resolves to #060B06. That is black in every way
 * that matters: at L* 0.14 a chroma of 0.014 is below the threshold where the
 * hue is visible at all, so the "real green cast" was a claim the values did
 * not support.
 *
 * The contrast that came out of it was the actual complaint. Measured against
 * WCAG 2.1, fg-strong on the canvas was 18.5:1 — HIGHER than the light theme's
 * 17.6:1, and near the 21:1 ceiling of pure black on pure white. Text that hot
 * on a ground that dark produces halation: the glyphs bloom into the
 * background, which is tiring over a working session and worse on OLED.
 *
 * So the grounds come up, the text comes down, and the chroma roughly doubles
 * so the forest cast survives:
 *
 *              L*     →           contrast on canvas
 *   canvas   0.140 → 0.205        strong  18.5 → 13.5
 *   base     0.180 → 0.238        default 16.0 → 11.7
 *   raised   0.235 → 0.283        muted   12.8 →  8.1
 *   sunken   0.120 → 0.168        subtle   7.5 →  5.2
 *   strong   0.975 → 0.905        sage     8.1 →  7.3
 *
 * Everything above stays AAA (7:1) for body text; `subtle` sits at 5.2, above
 * AA, which is correct for a de-emphasised label that is not load-bearing.
 *
 * These are LITERALS, not ramp aliases, and that is deliberate. --forest-900 is
 * the light theme's fg-strong AND was the dark theme's bg-base; --forest-800 is
 * light's fg-default and was dark's bg-raised. Editing the ramp to fix dark
 * would have dragged the light theme's text in the opposite direction. The ramp
 * stays the brand ladder; the dark surfaces are tuned for the dark surface.
 *
 * The depth model is unchanged and the steps are actually more even now
 * (ΔL* 0.037 / 0.033 / 0.045, previously 0.020 / 0.040 / 0.055): light comes
 * from the rules and from --rise-1, never from a radial spotlight.
 */
[data-theme="dark"] {
  --bg-canvas:  oklch(0.205 0.021 147);
  --bg-base:    oklch(0.238 0.022 146);
  --bg-raised:  oklch(0.283 0.023 144);
  --bg-overlay: oklch(0.320 0.023 144);
  --bg-sunken:  oklch(0.168 0.017 148);
  --bg-inverse: var(--forest-50);

  --fg-strong:  oklch(0.905 0.009 112);
  --fg-default: oklch(0.862 0.011 116);
  --fg-muted:   oklch(0.752 0.013 120);
  --fg-subtle:  oklch(0.632 0.015 125);
  --fg-inverse: var(--forest-950);

  /* Identical to the light block on purpose — see the long note there. The
     fills this sits on do not change with the theme, so neither may this. */
  --on-accent: var(--forest-50);

  --border-default: oklch(0.368 0.023 143);
  --border-strong:  oklch(0.455 0.024 142);
  --border-subtle:  oklch(0.300 0.022 145);

  /* 3.65:1 against the LIGHTEST dark surface (bg-raised), which is the worst
     case; 4.19 on bg-base and 4.55 on the canvas. */
  --control-edge: oklch(0.600 0.018 130);

  --accent-primary:    var(--sage-400);
  --accent-primary-fg: var(--forest-950);
  --accent-primary-hover: var(--sage-300);
  --accent-soft-bg:    oklch(0.340 0.036 142);
  --accent-soft-fg:    var(--sage-200);

  --accent-warm:       var(--terra-400);
  --accent-warm-soft-bg: oklch(0.348 0.062 45);
  --accent-warm-soft-fg: var(--terra-200);

  /* Status fills lift with the grounds — a pill at L* 0.29 on a raised surface
     at 0.283 was a fill you could no longer see. Their text comes down to match
     the softened fg ramp, so a badge is not the brightest thing on the page. */
  --status-win-bg:  oklch(0.330 0.050 150);
  --status-win-fg:  oklch(0.835 0.072 150);
  --status-loss-bg: oklch(0.348 0.064 45);
  --status-loss-fg: var(--terra-200);
  --status-pend-bg: oklch(0.340 0.055 78);
  --status-pend-fg: oklch(0.835 0.088 80);
  --status-info-bg: oklch(0.330 0.044 235);
  --status-info-fg: oklch(0.835 0.062 235);
  --status-neutral-bg: oklch(0.300 0.020 145);
  --status-neutral-fg: oklch(0.752 0.013 120);

  /* ---- Depth: DARK — a rim light, NOT an inversion ----
   * A drop shadow is invisible against forest-950, so "raised" cannot be
   * signalled the way it is in light. Here a raised object is instead LIGHTER
   * than its ground (bg-raised sits above bg-base sits above bg-canvas), with
   * a 1px white rim along its top edge and a hard dark edge beneath it. Same
   * four levels, different physics. Naively flipping the light values produces
   * a surface that is technically shadowed and visually flat.
   */
  --rise-1: inset 0 1px 0 rgba(255, 255, 255, 0.10),
            inset 0 -2px 0 rgba(0, 0, 0, 0.38),
            0 6px 14px -10px #000;
  --rise-2: inset 0 1px 0 rgba(255, 255, 255, 0.14),
            inset 0 -3px 0 rgba(0, 0, 0, 0.50),
            0 20px 36px -22px #000;
  --sink-1: inset 0 2px 6px rgba(0, 0, 0, 0.55),
            inset 0 -1px 0 rgba(255, 255, 255, 0.06);
  --halo:      0 10px 24px -12px oklch(0.720 0.042 140 / 0.45);
  --halo-warm: 0 10px 24px -12px oklch(0.700 0.100 45 / 0.45);

  --elev-1: 0 1px 1px oklch(0 0 0 / 0.30);
  --elev-2: 0 1px 3px oklch(0 0 0 / 0.40);
  --elev-3: 0 8px 24px oklch(0 0 0 / 0.50), 0 1px 3px oklch(0 0 0 / 0.40);
  --elev-4: 0 24px 60px oklch(0 0 0 / 0.60), 0 4px 10px oklch(0 0 0 / 0.45);

  --focus-ring: 0 0 0 2px var(--bg-base), 0 0 0 4px var(--sage-400);
}
/* <<< END GENERATED. Everything below is the landing site's own. */
/* ---- GLOBALS ---- */
* { box-sizing: border-box; }
/* min-height, NOT height. `height: 100%` caps <body> at the viewport, and a
   sticky element can only travel inside its containing block — so the sticky
   header left the 800px-tall body the moment you scrolled past one screen and
   never came back. min-height keeps the full-height intent (a short page's
   footer still sits at the bottom) while letting body grow to its content.
   `#root` is a leftover from the app's globals; this static site has no #root,
   but it is harmless and kept so the two sheets still read alike. */
html, body, #root { margin: 0; padding: 0; min-height: 100%; }

html { background: var(--bg-canvas); color-scheme: light dark; }
body {
  font-family: var(--font-sans);
  /* Pin the width axis explicitly. Archivo's default is 100, but stating it
     means a nested element can raise it (.font-display) and any child that
     does not opt in inherits normal-width text rather than the parent's
     stretch. */
  font-variation-settings: "wdth" var(--wdth-text);
  background: var(--bg-canvas);
  color: var(--fg-default);
  font-size: var(--fs-md);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

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

/* The display cut: same family, wider and heavier, tracked in slightly. This
   is what replaced the serif — a wide grotesque reads as the wordmark's
   sibling, where a high-contrast serif read as a different company's. */
.font-display {
  font-family: var(--font-display);
  font-variation-settings: "wdth" var(--wdth-display);
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.08;
}

/* ---- The rule ----
 * The mark's load-bearing device is a heavy horizontal rule the letterforms
 * sit on. It is the structural unit here too: sections, panels, table rows and
 * list items are separated by rules instead of being boxed into cards.
 * `.rule-heavy` is the mark's own baseline weight and is used once per section
 * at most.
 */
.rule       { border: 0; border-top: 1px solid var(--border-default); margin: 0; }
.rule-heavy { border: 0; border-top: 2px solid var(--fg-strong); margin: 0; }

/* The single accent. One per view — see the terracotta note in the tokens. */
.dot-live {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: var(--r-full);
  background: var(--terra-500);
  flex: none;
}

.txt-strong  { color: var(--fg-strong); }
.txt-default { color: var(--fg-default); }
.txt-muted   { color: var(--fg-muted); }
.txt-subtle  { color: var(--fg-subtle); }

.focusable:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.placeholder-stripes {
  background-image: repeating-linear-gradient(
    45deg,
    var(--border-subtle) 0,
    var(--border-subtle) 1px,
    transparent 1px,
    transparent 10px
  );
  background-color: var(--bg-sunken);
}

*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb { background: var(--border-default); border-radius: 999px; border: 2px solid transparent; background-clip: padding-box; }
*::-webkit-scrollbar-thumb:hover { background: var(--border-strong); background-clip: padding-box; }

/*
 * A DATA label — the caption on a figure, the header on a column — and nothing
 * else. It is deliberately no longer used as a section "eyebrow" above a
 * heading, which is the one job its name implies: a small caps tag floating
 * over every H2 is a tic, and it was on all seven landing sections.
 *
 * Kept small, mono and quiet so it recedes behind the number it labels.
 */
.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--fg-subtle);
}

.brandmark-text {
  font-family: var(--font-sans);
  font-weight: 600;
  letter-spacing: -0.02em;
}

.chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 8px; border-radius: var(--r-full);
  background: var(--bg-overlay); color: var(--fg-muted);
  font-size: var(--fs-sm); line-height: 1;
}

@keyframes shimmer { 0%, 100% { opacity: 0.6; } 50% { opacity: 0.95; } }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}
