/* Generated by scripts/sync-lab-modules.js from public/lab/modules. Do not edit. */
/* Self-hosted Latin subsets (Fontsource 5.3.0, all SIL OFL 1.1; licences in
   ./licences). A face only downloads when text on the page uses it, so only
   the active typeface preset loads. */

@font-face { font-family: 'Inter Variable'; font-style: normal; font-display: swap; font-weight: 100 900; src: url(fonts/inter-latin-wght-normal.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Playfair Display Variable'; font-style: normal; font-display: swap; font-weight: 400 900; src: url(fonts/playfair-display-latin-wght-normal.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Playfair Display Variable'; font-style: italic; font-display: swap; font-weight: 400 900; src: url(fonts/playfair-display-latin-wght-italic.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Fraunces Variable'; font-style: normal; font-display: swap; font-weight: 100 900; src: url(fonts/fraunces-latin-full-normal.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Fraunces Variable'; font-style: italic; font-display: swap; font-weight: 100 900; src: url(fonts/fraunces-latin-full-italic.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Hanken Grotesk Variable'; font-style: normal; font-display: swap; font-weight: 100 900; src: url(fonts/hanken-grotesk-latin-wght-normal.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Bricolage Grotesque Variable'; font-style: normal; font-display: swap; font-weight: 200 800; src: url(fonts/bricolage-grotesque-latin-opsz-normal.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'DM Sans Variable'; font-style: normal; font-display: swap; font-weight: 100 1000; src: url(fonts/dm-sans-latin-wght-normal.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Instrument Serif'; font-style: normal; font-display: swap; font-weight: 400; src: url(fonts/instrument-serif-latin-400-normal.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Instrument Serif'; font-style: italic; font-display: swap; font-weight: 400; src: url(fonts/instrument-serif-latin-400-italic.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Manrope Variable'; font-style: normal; font-display: swap; font-weight: 200 800; src: url(fonts/manrope-latin-wght-normal.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Outfit Variable'; font-style: normal; font-display: swap; font-weight: 100 900; src: url(fonts/outfit-latin-wght-normal.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Geist Variable'; font-style: normal; font-display: swap; font-weight: 100 900; src: url(fonts/geist-latin-wght-normal.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }
@font-face { font-family: 'Geist Mono Variable'; font-style: normal; font-display: swap; font-weight: 100 900; src: url(fonts/geist-mono-latin-wght-normal.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }

/* Design tokens: four palettes, five typeface presets, motion.
   Switched by attributes on any element (usually <html>):
     data-pal  = ember | forest | aurora | voltage
     data-mode = light | dark          (aurora and voltage are dark only)
     data-type = 0 | 1 | 2 | 3 | 4
     data-calm = on                    (same effect as prefers-reduced-motion)
   Attribute selectors, not :root, so a swatch or sample can carry its own
   palette or preset and show it live. Gold is for earned moments only. */

:root {
  --ease-out: cubic-bezier(.2, .85, .25, 1);
  --ease-spring: cubic-bezier(.3, 1.4, .5, 1);
  --dur-1: 120ms;
  --dur-2: 200ms;
  --dur-3: 350ms;
  --dur-4: 600ms;
  --dur-5: 1100ms;

  --r-s: 12px;
  --r-m: 16px;
  --r-l: 22px;
  --r-xl: 28px;

  --stack-sans: ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, system-ui, sans-serif;
  --stack-serif: 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, ui-serif, serif;
  --stack-mono: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
}

/* ------------------------------------------------------------------ palettes */

/* Ember and Cloud (default), light. Also the fallback before any script runs. */
:root,
[data-pal='ember'][data-mode='light'] {
  --bg: #F0EEE9;
  --surface: #F9F8F4;
  --surface2: #E7E4DC;
  --line: #D8D4CA;
  --text: #17302F;
  --muted: #586765;
  --accent: #B4573A;
  --accent2: #316263;
  --gold: #A07C3A;
  --hero-bg: #24494A;
  --hero-text: #F0EEE9;
  --hero-accent: #FF9B78;
  --hero-muted: #B8CFCB;
  --glow: rgba(255, 106, 61, .34);
  --on-accent: #FFFFFF;
  --shadow: 0 1px 0 rgba(23, 48, 47, .04), 0 10px 30px rgba(23, 48, 47, .08);
  --scrim: rgba(12, 22, 22, .38);
  color-scheme: light;
}

[data-pal='ember'][data-mode='dark'] {
  --bg: #0B0D10;
  --surface: #151A21;
  --surface2: #1E252E;
  --line: #2A323C;
  --text: #F0EEE9;
  --muted: #9AA4AE;
  --accent: #FF6A3D;
  --accent2: #5FA8A5;
  --gold: #C9A46B;
  --hero-bg: #151A21;
  --hero-text: #F0EEE9;
  --hero-accent: #FF6A3D;
  --hero-muted: #9AA4AE;
  --glow: rgba(255, 106, 61, .30);
  --on-accent: #0B0D10;
  --shadow: 0 1px 0 rgba(0, 0, 0, .3), 0 12px 32px rgba(0, 0, 0, .35);
  --scrim: rgba(0, 0, 0, .55);
  color-scheme: dark;
}

/* Before the boot script runs (or without it), follow the phone. */
@media (prefers-color-scheme: dark) {
  :root:not([data-mode]) {
    --bg: #0B0D10;
    --surface: #151A21;
    --surface2: #1E252E;
    --line: #2A323C;
    --text: #F0EEE9;
    --muted: #9AA4AE;
    --accent: #FF6A3D;
    --accent2: #5FA8A5;
    --gold: #C9A46B;
    --hero-bg: #151A21;
    --hero-text: #F0EEE9;
    --hero-accent: #FF6A3D;
    --hero-muted: #9AA4AE;
    --glow: rgba(255, 106, 61, .30);
    --on-accent: #0B0D10;
    --shadow: 0 1px 0 rgba(0, 0, 0, .3), 0 12px 32px rgba(0, 0, 0, .35);
    --scrim: rgba(0, 0, 0, .55);
    color-scheme: dark;
  }
}

/* Forest and Gold: the original look, tidied. bg, surface, line, text, gold
   and the dark set are the repo's real values; muted and accent are darker
   than before so small text passes 4.5:1 (see docs/decisions.md). */
[data-pal='forest'][data-mode='light'] {
  --bg: #F3EEE3;
  --surface: #FAF7F0;
  --surface2: #E6DFCF;
  --line: #DED6C5;
  --text: #1E3A2F;
  --muted: #52645B;
  --accent: #8F6C2C;
  --accent2: #2F6B4B;
  --gold: #A5823A;
  --hero-bg: #1E3A2F;
  --hero-text: #F3EEE3;
  --hero-accent: #D7B770;
  --hero-muted: #A9BDB2;
  --glow: rgba(201, 164, 107, .28);
  --on-accent: #FFFFFF;
  --shadow: 0 1px 0 rgba(30, 58, 47, .04), 0 8px 24px rgba(30, 58, 47, .07);
  --scrim: rgba(10, 20, 15, .38);
  color-scheme: light;
}

[data-pal='forest'][data-mode='dark'] {
  --bg: #0D1712;
  --surface: #132019;
  --surface2: #1D2B23;
  --line: #243329;
  --text: #ECE7DA;
  --muted: #93A094;
  --accent: #CDA85A;
  --accent2: #8CC6A1;
  --gold: #CDA85A;
  --hero-bg: #132019;
  --hero-text: #ECE7DA;
  --hero-accent: #CDA85A;
  --hero-muted: #93A094;
  --glow: rgba(143, 197, 164, .20);
  --on-accent: #0D1712;
  --shadow: 0 1px 0 rgba(0, 0, 0, .2), 0 10px 28px rgba(0, 0, 0, .25);
  --scrim: rgba(0, 0, 0, .55);
  color-scheme: dark;
}

/* Aurora Glass: dark only. Surfaces are translucent over drifting blobs
   (atmosphere module). */
[data-pal='aurora'] {
  --bg: #150A1F;
  --surface: rgba(237, 231, 227, .075);
  --surface2: rgba(237, 231, 227, .13);
  --line: rgba(237, 231, 227, .17);
  --text: #EDE7E3;
  --muted: #C8C0D5;
  --accent: #FF7A50;
  --accent2: #9DB6E6;
  --gold: #FFD66B;
  --hero-bg: rgba(237, 231, 227, .085);
  --hero-text: #EDE7E3;
  --hero-accent: #FF9270;
  --hero-muted: #C9C0D6;
  --glow: rgba(136, 163, 214, .34);
  --on-accent: #150A1F;
  --shadow: 0 1px 0 rgba(0, 0, 0, .25), 0 16px 40px rgba(0, 0, 0, .35);
  --scrim: rgba(8, 3, 14, .6);
  --glass: blur(20px) saturate(140%);
  /* A dark layer under the glass tint keeps text readable over the brightest
     blob; the blobs stay vivid in the gaps between cards. */
  --glass-under: rgba(21, 10, 31, .48);
  --blob-strength: .62;
  color-scheme: dark;
}

/* Voltage: dark only. */
[data-pal='voltage'] {
  --bg: #0B0D10;
  --surface: #151A21;
  --surface2: #1E252E;
  --line: #2A323C;
  --text: #F2F4EF;
  --muted: #98A2AC;
  --accent: #B6FF3B;
  --accent2: #5B92FF;
  --gold: #FFD66B;
  --hero-bg: #151A21;
  --hero-text: #F2F4EF;
  --hero-accent: #B6FF3B;
  --hero-muted: #98A2AC;
  --glow: rgba(182, 255, 59, .18);
  --on-accent: #0B0D10;
  --shadow: 0 1px 0 rgba(0, 0, 0, .3), 0 12px 32px rgba(0, 0, 0, .35);
  --scrim: rgba(0, 0, 0, .6);
  color-scheme: dark;
}

/* Derived tokens: declared wherever a palette is, so nested swatches work. */
:root,
[data-pal] {
  --text-2: color-mix(in srgb, var(--text) 72%, var(--muted));
  --track: var(--surface2);
  --ahead: var(--accent2);
  --behind: var(--accent);
  --focus: var(--accent2);
  --gold-soft: color-mix(in srgb, var(--gold) 18%, transparent);
  --accent-soft: color-mix(in srgb, var(--accent) 14%, transparent);
  --accent2-soft: color-mix(in srgb, var(--accent2) 14%, transparent);
  /* An opaque surface for sheets and dialogs that sit over everything. */
  --surface-solid: var(--surface);
}
[data-pal='aurora'] { --surface-solid: #211530; }

/* ------------------------------------------------------------------ typeface presets */
/* --num-*: the hero figure. --quote-*: the motivation line. */

:root,
[data-type='2'] {
  /* 2 Confident modern (default) */
  --font-ui: 'DM Sans Variable', 'DM Sans', var(--stack-sans);
  --font-head: 'Bricolage Grotesque Variable', 'Bricolage Grotesque', var(--stack-sans);
  --font-num: 'Bricolage Grotesque Variable', 'Bricolage Grotesque', var(--stack-sans);
  --font-quote: 'Instrument Serif', var(--stack-serif);
  --head-weight: 600;
  --head-fvs: 'opsz' 36;
  --head-track: -0.015em;
  --num-weight: 700;
  --num-fvs: 'opsz' 96;
  --num-size: 108px;
  --num-track: -0.045em;
  --num-fit: 27;
  --quote-size: 23px;
  --quote-style: italic;
  --quote-weight: 400;
  --quote-lh: 1.2;
}

[data-type='0'] {
  /* 0 Current: Inter and Playfair Display, as before this pass */
  --font-ui: 'Inter Variable', Inter, var(--stack-sans);
  --font-head: 'Playfair Display Variable', 'Playfair Display', var(--stack-serif);
  --font-num: 'Inter Variable', Inter, var(--stack-sans);
  --font-quote: 'Playfair Display Variable', 'Playfair Display', var(--stack-serif);
  --head-weight: 500;
  --head-fvs: normal;
  --head-track: -0.005em;
  --num-weight: 650;
  --num-fvs: normal;
  --num-size: 104px;
  --num-track: -0.045em;
  --num-fit: 27;
  --quote-size: 20px;
  --quote-style: italic;
  --quote-weight: 400;
  --quote-lh: 1.35;
}

[data-type='1'] {
  /* 1 Warm editorial */
  --font-ui: 'Hanken Grotesk Variable', 'Hanken Grotesk', var(--stack-sans);
  --font-head: 'Fraunces Variable', Fraunces, var(--stack-serif);
  --font-num: 'Fraunces Variable', Fraunces, var(--stack-serif);
  --font-quote: 'Fraunces Variable', Fraunces, var(--stack-serif);
  --head-weight: 560;
  --head-fvs: 'SOFT' 50, 'opsz' 36;
  --head-track: -0.01em;
  --num-weight: 600;
  --num-fvs: 'SOFT' 70, 'opsz' 144;
  --num-size: 104px;
  --num-track: -0.035em;
  --num-fit: 26;
  --quote-size: 19px;
  --quote-style: italic;
  --quote-weight: 400;
  --quote-lh: 1.35;
}

[data-type='3'] {
  /* 3 Quiet luxe */
  --font-ui: 'Manrope Variable', Manrope, var(--stack-sans);
  --font-head: 'Instrument Serif', var(--stack-serif);
  --font-num: 'Instrument Serif', var(--stack-serif);
  --font-quote: 'Instrument Serif', var(--stack-serif);
  --head-weight: 400;
  --head-fvs: normal;
  --head-track: 0;
  --num-weight: 400;
  --num-fvs: normal;
  --num-size: 126px;
  --num-track: -0.02em;
  --num-fit: 34;
  --quote-size: 23px;
  --quote-style: italic;
  --quote-weight: 400;
  --quote-lh: 1.2;
}

[data-type='4'] {
  /* 4 Clean data */
  --font-ui: 'Geist Variable', Geist, var(--stack-sans);
  --font-head: 'Outfit Variable', Outfit, var(--stack-sans);
  --font-num: 'Outfit Variable', Outfit, var(--stack-sans);
  --font-quote: 'Geist Mono Variable', 'Geist Mono', var(--stack-mono);
  --head-weight: 600;
  --head-fvs: normal;
  --head-track: -0.02em;
  --num-weight: 600;
  --num-fvs: normal;
  --num-size: 108px;
  --num-track: -0.04em;
  --num-fit: 27;
  --quote-size: 14px;
  --quote-style: normal;
  --quote-weight: 400;
  --quote-lh: 1.5;
}

/* ---- theme-switcher ---- */
/* theme-switcher picker. Colours and type come from tokens.css, so the
   swatches and samples show each palette and preset live. */

.ts { display: grid; gap: 1.25rem; }
.ts-group { margin: 0; padding: 0; border: 0; min-width: 0; display: grid; gap: .5rem; }
.ts-legend { padding: 0; margin-bottom: .5rem; font-size: .8125rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.ts-option { position: relative; display: grid; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.ts-option input, .ts-seg input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.ts-name { font-size: .9375rem; font-weight: 600; color: var(--text); }
.ts-note { font-size: .8125rem; color: var(--muted); }

/* palettes: 2 x 2 cards, each a tiny live preview */
.ts-palettes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .625rem; }
.ts-pal { gap: .125rem; padding: .5rem .5rem .625rem; border-radius: var(--r-m); border: 1.5px solid var(--line); background: var(--surface); }
.ts-pal:has(input:checked) { border-color: var(--text); box-shadow: inset 0 0 0 1px var(--text); }
.ts-pal:has(input:focus-visible), .ts-type:has(input:focus-visible) { outline: 3px solid var(--focus); outline-offset: 2px; }
.ts-swatch {
  display: grid; grid-template-columns: 1fr 1fr; gap: 5px; height: 64px; padding: 6px; margin-bottom: .375rem;
  border-radius: calc(var(--r-m) - 5px); background: var(--bg); overflow: hidden; position: relative;
}
.ts-swatch[data-pal='aurora']::before {
  content: ''; position: absolute; inset: -20%;
  background: radial-gradient(40% 50% at 20% 30%, rgba(90, 75, 138, .9), transparent 70%), radial-gradient(40% 50% at 80% 70%, rgba(255, 106, 61, .4), transparent 70%), radial-gradient(40% 40% at 60% 20%, rgba(136, 163, 214, .45), transparent 70%);
}
.ts-swatch i { position: relative; display: block; border-radius: 8px; }
.ts-sw-hero { background: var(--hero-bg); overflow: hidden; }
.ts-sw-hero::after { content: ''; position: absolute; top: -40%; right: -40%; width: 100%; height: 100%; border-radius: 50%; background: radial-gradient(closest-side, var(--glow), transparent); }
.ts-sw-hero b { position: absolute; left: 7px; bottom: 7px; width: 55%; height: 12px; border-radius: 4px; background: var(--hero-text); }
.ts-sw-card { background: var(--surface); border: 1px solid var(--line); display: flex; align-items: flex-end; gap: 4px; padding: 6px; }
.ts-sw-card b { width: 11px; height: 11px; border-radius: 50%; }
.ts-sw-card .a { background: var(--accent); }
.ts-sw-card .a2 { background: var(--accent2); }
.ts-sw-card .g { background: var(--gold); }

/* typefaces: a row per preset with a live figure */
.ts-types { display: grid; gap: .5rem; }
.ts-type {
  grid-template-columns: 6.5rem 1fr; grid-template-rows: auto auto; column-gap: .75rem; align-items: center;
  min-height: 4rem; padding: .5rem .875rem; border-radius: var(--r-m); border: 1.5px solid var(--line); background: var(--surface);
}
.ts-type:has(input:checked) { border-color: var(--text); box-shadow: inset 0 0 0 1px var(--text); }
.ts-sample {
  grid-row: 1 / 3; font-family: var(--font-num); font-weight: var(--num-weight); font-variation-settings: var(--num-fvs);
  letter-spacing: var(--num-track); font-size: 2.5rem; line-height: 1; color: var(--text);
  font-variant-numeric: tabular-nums lining-nums; white-space: nowrap;
}
.ts-type .ts-name { align-self: end; font-family: var(--font-ui); }
.ts-type .ts-note { align-self: start; }

/* light / dark / follow */
.ts-seg { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0; padding: 3px; border-radius: var(--r-m); background: var(--surface2); }
.ts-seg label { position: relative; }
.ts-seg span { display: grid; place-items: center; min-height: 2.75rem; padding: 0 .25rem; border-radius: calc(var(--r-m) - 3px); font-size: .9375rem; font-weight: 600; color: var(--text); text-align: center; }
.ts-seg input:checked + span { background: var(--surface); box-shadow: 0 1px 3px rgba(0, 0, 0, .12); }
.ts-seg input:focus-visible + span { outline: 3px solid var(--focus); outline-offset: 1px; }
.ts-seg input:disabled + span { opacity: .55; }
.ts-seg input:disabled { cursor: default; }
.ts-hint { font-size: .8125rem; color: var(--muted); }

/* calm mode switch */
.ts-switch { position: relative; display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 1rem; min-height: 3.5rem; cursor: pointer; }
.ts-switch-text { display: grid; gap: .125rem; }
.ts-switch input { position: absolute; inset: 0; opacity: 0; margin: 0; cursor: pointer; }
.ts-switch-ui { position: relative; width: 3.25rem; height: 2rem; border-radius: 99px; background: var(--surface2); border: 1px solid var(--line); }
.ts-switch-ui::after {
  content: ''; position: absolute; top: 3px; left: 3px; width: calc(2rem - 8px); height: calc(2rem - 8px); border-radius: 50%;
  background: var(--text); transition: transform var(--dur-2) var(--ease-out);
}
.ts-switch input:checked ~ .ts-switch-ui { background: var(--accent2); border-color: var(--accent2); }
.ts-switch input:checked ~ .ts-switch-ui::after { transform: translateX(1.25rem); background: var(--surface); }
.ts-switch input:focus-visible ~ .ts-switch-ui { outline: 3px solid var(--focus); outline-offset: 2px; }

/* ---- view-transitions ---- */
/* view-transitions: the root cross-fade uses the motion tokens. Give an
   element its own view-transition-name to have it move independently. */

::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: var(--dur-3, 350ms);
  animation-timing-function: var(--ease-out, ease-out);
}
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}

/* ---- atmosphere ---- */
/* atmosphere: drifting blobs (.atm), frosted glass (.atm-glass), a soft
   radial glow (.atm-glow) and film grain (.atm-grain). Colours from tokens
   where they exist; the blob colours are passed in by module.js.
   --blob-strength (a palette token) scales the blobs so text on glass keeps
   its contrast; scripts/contrast.js checks the result. */

.atm { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; display: none; }
.atm.atm-on { display: block; }
.atm-blob {
  position: absolute; left: var(--x); top: var(--y); width: var(--s); aspect-ratio: 1; border-radius: 50%;
  background: var(--c); opacity: calc(var(--o) * var(--blob-strength, 1));
  filter: blur(48px);
  will-change: transform;
}
@media (prefers-reduced-motion: no-preference) {
  .atm-blob { animation: atm-drift var(--d) ease-in-out infinite alternate; animation-delay: calc(var(--i) * -4s); }
  .atm-blob:nth-child(2) { animation-name: atm-drift-2; }
  .atm-blob:nth-child(3) { animation-name: atm-drift-3; }
}
.atm.atm-paused .atm-blob { animation-play-state: paused; }
.atm.atm-still .atm-blob { animation: none; }
@keyframes atm-drift { from { transform: translate3d(0, 0, 0) scale(1); } to { transform: translate3d(12vw, 8vh, 0) scale(1.12); } }
@keyframes atm-drift-2 { from { transform: translate3d(0, 0, 0) scale(1.05); } to { transform: translate3d(-14vw, -6vh, 0) scale(.92); } }
@keyframes atm-drift-3 { from { transform: translate3d(0, 0, 0) scale(.95); } to { transform: translate3d(10vw, -12vh, 0) scale(1.1); } }

/* Frosted glass: a dark under-layer, the palette's light tint, blur, and a
   1px line border. Falls back to a solid, readable panel without blur. */
.atm-glass {
  background: linear-gradient(var(--surface), var(--surface)), var(--glass-under, transparent);
  -webkit-backdrop-filter: var(--glass, blur(20px) saturate(140%));
  backdrop-filter: var(--glass, blur(20px) saturate(140%));
  border: 1px solid var(--line);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .atm-glass { background: var(--surface-solid, var(--surface)); }
}

/* A soft glow in the top right of a block. */
.atm-glow { position: relative; isolation: isolate; overflow: hidden; }
.atm-glow::before {
  content: ''; position: absolute; z-index: -1; top: -45%; right: -35%; width: 95%; aspect-ratio: 1; border-radius: 50%;
  background: radial-gradient(closest-side, var(--glow), transparent); pointer-events: none;
}

/* Film grain from an inline SVG noise filter. Static, so it costs nothing to keep. */
.atm-grain { position: relative; }
.atm-grain::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; border-radius: inherit; opacity: var(--grain, .08); mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ---- rolling-digits ---- */
/* rolling-digits. Inherits font, size, colour and tracking from its parent.
   --rd-h sets the window height per digit (a little over 1em so tall digits
   in serif faces are not clipped). */

.rd { position: relative; display: inline-flex; align-items: flex-start; font-variant-numeric: tabular-nums lining-nums; white-space: nowrap; line-height: 1; }
.rd-vis { display: inline-flex; }
.rd-col { display: inline-block; height: var(--rd-h, 1.12em); overflow: hidden; }
.rd-strip { display: block; will-change: transform; }
.rd-strip > span, .rd-sym { display: block; height: var(--rd-h, 1.12em); line-height: var(--rd-h, 1.12em); text-align: center; }
.rd-sr {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ---- scroll-reveal ---- */
/* scroll-reveal. Native scroll timelines where supported; the fallback
   classes (.sr-pending, .sr-in) are only added by module.js when they are
   not. `animation-timeline` must come after the `animation` shorthand, which
   would otherwise reset it. */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    :root:not([data-calm='on']) [data-reveal] {
      animation: sr-rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 55%;
    }
  }
}

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    :root:not([data-calm='on']) [data-scroll-fade] {
      transform-origin: left bottom;
      animation: sr-away linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 var(--sr-away, 50vh);
    }
  }
}

@keyframes sr-rise {
  from { opacity: .3; transform: translateY(28px) scale(.985); }
  to { opacity: 1; transform: none; }
}
@keyframes sr-away {
  to { opacity: .45; transform: scale(.88); }
}

/* IntersectionObserver fallback */
.sr-pending { opacity: .3; transform: translateY(28px) scale(.985); }
.sr-in { transition: opacity var(--dur-4, 600ms) var(--ease-out, ease-out), transform var(--dur-4, 600ms) var(--ease-out, ease-out); }

/* ---- pinned-bar ---- */
/* pinned-bar. Fixed to the top, clear of the status bar (safe-area inset),
   blurred translucent background, slides with one transform. */

.pinbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 15;
  display: block; width: 100%; margin: 0; border: 0; padding: env(safe-area-inset-top) 0 0; text-align: inherit;
  font: inherit; color: var(--text); cursor: pointer; -webkit-tap-highlight-color: transparent;
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  -webkit-backdrop-filter: blur(18px) saturate(1.4);
  backdrop-filter: blur(18px) saturate(1.4);
  border-bottom: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
  transform: translateY(-100%);
  transition: transform var(--dur-3, 350ms) var(--ease-out, ease-out);
}
.pinbar.pinbar-shown { transform: translateY(0); }
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .pinbar { background: color-mix(in srgb, var(--bg) 97%, transparent); }
}
.pinbar:focus-visible { outline: 3px solid var(--focus); outline-offset: -3px; }
.pinbar-inner {
  display: flex; align-items: center; gap: .75rem; max-width: 520px; min-height: 2.875rem; margin: 0 auto;
  padding: 0 calc(env(safe-area-inset-right) + 1rem) 0 calc(env(safe-area-inset-left) + 1rem);
}
.pinbar-track { position: relative; height: 3px; overflow: hidden; background: color-mix(in srgb, var(--line) 60%, transparent); }
.pinbar-progress {
  display: block; height: 100%; background: var(--accent2);
  transform-origin: left center; transform: scaleX(0);
  transition: transform var(--dur-4, 600ms) var(--ease-out, ease-out);
}
@media (prefers-reduced-motion: reduce) { .pinbar, .pinbar-progress { transition: none; } }
:root[data-calm='on'] .pinbar, :root[data-calm='on'] .pinbar-progress { transition: none; }

/* ---- hold-to-log ---- */
/* hold-to-log. Ring colours: --htl-fill (default accent), --htl-done (gold).
   The fill is an SVG stroke (stroke-dashoffset), the one non-transform
   property here; everything else is transform and opacity. */

.htl { touch-action: manipulation; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none; -webkit-tap-highlight-color: transparent; }
.htl-ring { position: relative; display: grid; place-items: center; }
.htl-ring > svg:first-child { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.htl-track { fill: none; stroke: var(--track, var(--surface2)); stroke-width: 3; }
.htl-fill { fill: none; stroke: var(--htl-fill, var(--accent)); stroke-width: 3.2; stroke-linecap: round; stroke-dasharray: 1; stroke-dashoffset: 1; opacity: 0; }
.htl-tick { fill: none; stroke: var(--htl-done, var(--gold)); stroke-width: 2.8; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 1; stroke-dashoffset: 1; opacity: 0; transform: rotate(90deg); transform-origin: 20px 20px; }
.htl.is-holding .htl-fill, .htl.is-busy .htl-fill, .htl.is-done .htl-fill { opacity: 1; }
.htl.is-done .htl-fill { stroke: var(--htl-done, var(--gold)); stroke-dashoffset: 0; }
.htl.is-done .htl-tick { opacity: 1; stroke-dashoffset: 0; }
.htl.is-done .htl-icon { opacity: 0; }
.htl.is-busy { opacity: .6; }

@media (prefers-reduced-motion: no-preference) {
  :root:not([data-calm='on']) .htl { transition: transform var(--dur-1, 120ms) var(--ease-out, ease-out); }
  :root:not([data-calm='on']) .htl.is-holding { transform: scale(.96); }
  :root:not([data-calm='on']) .htl.is-armed { transform: scale(.94); }
  :root:not([data-calm='on']) .htl.is-done { animation: htl-settle var(--dur-4, 600ms) var(--ease-spring, ease-out) both; }
  :root:not([data-calm='on']) .htl.is-done .htl-ring { animation: htl-pop var(--dur-3, 350ms) var(--ease-spring, ease-out) both; }
  :root:not([data-calm='on']) .htl.is-done .htl-tick { transition: stroke-dashoffset var(--dur-2, 200ms) ease-out var(--dur-1, 120ms); }
  :root:not([data-calm='on']) .htl-icon { transition: opacity var(--dur-1, 120ms) ease; }
}
@keyframes htl-settle { 0% { transform: scale(.94); } 100% { transform: scale(1); } }
@keyframes htl-pop { 0% { transform: scale(.8); } 100% { transform: scale(1); } }

/* ---- toast-undo ---- */
/* toast-undo. Sits above a bottom bar if the page sets --toast-bottom. */

.toast {
  position: fixed; left: 50%; z-index: 25;
  bottom: var(--toast-bottom, calc(env(safe-area-inset-bottom) + 16px));
  transform: translateX(-50%);
  display: flex; align-items: center; gap: 10px;
  width: min(92vw, 440px);
  padding: 6px 6px 6px 14px;
  border-radius: var(--r-m, 16px);
  background: var(--text); color: var(--bg);
  box-shadow: var(--shadow);
  font-size: .9375rem;
  overflow: hidden;
}
.toast-tick svg { width: 20px; height: 20px; display: block; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
.toast-text { flex: 1; }
.toast-undo {
  min-height: 44px; min-width: 64px; border: 0; border-radius: var(--r-s, 12px); background: transparent; color: inherit;
  font: inherit; font-weight: 650; text-decoration: underline; text-underline-offset: 3px; cursor: pointer;
}
.toast-undo:focus-visible { outline: 3px solid var(--bg); outline-offset: -3px; }
.toast-timer {
  position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: color-mix(in srgb, var(--bg) 45%, transparent);
  transform-origin: left center; transform: scaleX(0);
}

@media (prefers-reduced-motion: no-preference) {
  :root:not([data-calm='on']) .toast:not([hidden]) { animation: toast-in var(--dur-2, 200ms) var(--ease-out, ease-out) both; }
  :root:not([data-calm='on']) .toast.timed .toast-timer { animation: toast-timer var(--toast-ms, 6000ms) linear both; }
}
@keyframes toast-in { from { opacity: 0; transform: translate(-50%, 8px); } to { opacity: 1; transform: translate(-50%, 0); } }
@keyframes toast-timer { from { transform: scaleX(1); } to { transform: scaleX(0); } }

/* ---- summit-path ---- */
/* summit-path. All colour from tokens: --text for the climbed line,
   --accent2 for reached markers, --accent for today, --gold for the summit. */

.sp svg { display: block; overflow: visible; }
.sp-land { fill: var(--text); fill-opacity: .045; }
.sp-base { fill: none; stroke: var(--line); stroke-width: 3; stroke-linecap: round; }
.sp-done { fill: none; stroke: var(--text); stroke-width: 3.5; stroke-linecap: round; }
.sp-ahead { fill: none; stroke: var(--accent); stroke-width: 3; stroke-linecap: round; stroke-dasharray: .1 6.5; }
.sp-ahead.sp-behind { stroke: var(--muted); }
.sp-wp { fill: var(--line); }
.sp-wp.sp-reached { fill: var(--accent2); }
.sp-next { fill: var(--surface); stroke: var(--accent2); stroke-width: 2.5; }
.sp-label { font: 600 12px var(--font-ui, sans-serif); fill: var(--muted); font-variant-numeric: tabular-nums; }
.sp-next-label { fill: var(--text); font-weight: 700; }
.sp-flag line { stroke: var(--text); stroke-width: 1.6; }
.sp-flag path { fill: var(--gold); }
.sp-today-dot { fill: var(--accent); stroke: var(--surface); stroke-width: 2.5; }
.sp-today-label { font: 700 13px var(--font-ui, sans-serif); fill: var(--text); font-variant-numeric: tabular-nums; }
.sp-me-halo { fill: var(--text); fill-opacity: .12; }
.sp-me-dot { fill: var(--text); stroke: var(--surface); stroke-width: 2; }

/* ---- milestone-burst ---- */
/* milestone-burst. The burst draws on its own fixed canvas; this only keeps
   a canvas you pass in out of the way of taps. */

.mb-canvas { position: fixed; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 60; }
