/* ====
   Hablox v5 · tokens.css · design-system foundation (re-skin of v4)
   Load order: tokens.css → styles.css → learn.css → models.css (this file first).
   Look: flat, inked, bright. Hablox orange #FF7A1A as a field with ink text on it,
   2px ink outlines, small corners, no soft shadows (design/v5/00-DESIGN-SPEC.md).

   Fonts (index.html loads them without blocking, display=swap):
     Geologica 800 (display) + Onest 400–700 (text), Google Fonts css2, ≈ 88 KB first visit.

   Rules: components use the SEMANTIC tokens (--page, --text, --brand, --ok-fg …),
   never the raw scales (--gray-*, --orange-*). Hex colours live in this file only.

   1 colour · 2 type · 3 space, radius, elevation, motion, z, layout
   4 compat aliases · 5 base · 6 primitives · 7 utilities · 8 v5 media primitives
   9 motion & forced colours
   ==== */

/* v14: arrows and maths signs (≈ ≥ →) from the phone's font: no 25 KB Onest "math" subset just for "≈" (subscripts stay Onest) */
@font-face { font-family: "Hb Sym"; font-weight: 100 599; unicode-range: U+2190-21FF, U+2200-22FF;
  src: local("Roboto"), local("Roboto-Regular"), local("Roboto Regular"), local("NotoSans-Regular"), local("Arial"), local("ArialMT"), local("Helvetica"), local("SegoeUI"); }
@font-face { font-family: "Hb Sym"; font-weight: 600 900; unicode-range: U+2190-21FF, U+2200-22FF;
  src: local("Roboto Bold"), local("Roboto-Bold"), local("NotoSans-Bold"), local("Arial Bold"), local("Arial-BoldMT"), local("Helvetica Bold"), local("Helvetica-Bold"), local("SegoeUI-Bold"); }
/* v14: Roboto scaled to the brand fonts' width until they arrive, so lines keep their breaks on the swap */
@font-face { font-family: "Geologica fb"; font-weight: 800; size-adjust: 110.4%; src: local("Roboto Black"), local("Roboto-Black"); }
@font-face { font-family: "Onest fb"; font-weight: 400; size-adjust: 102.4%; src: local("Roboto"), local("Roboto Regular"), local("Roboto-Regular"); }
@font-face { font-family: "Onest fb"; font-weight: 500 600; size-adjust: 104.2%; src: local("Roboto Medium"), local("Roboto-Medium"); }
@font-face { font-family: "Onest fb"; font-weight: 700; size-adjust: 105%; src: local("Roboto Bold"), local("Roboto-Bold"); }

:root {
  color-scheme: light;

  /* -- 1a. Neutrals: warm, a little paper -- */
  --gray-0: #ffffff; --gray-50: #fbf9f5; --gray-100: #f6f2eb; --gray-200: #ece7de; --gray-300: #e6e1d8;
  --gray-400: #d4cec3; --gray-500: #aba497; --gray-600: #8a847a; --gray-700: #7d776d; --gray-800: #57524a;
  --gray-900: #2b2722; --gray-1000: #17140f;

  /* -- 1b. Brand scale: Hablox orange (500 is the brand) -- */
  --orange-50: #fff4ea; --orange-100: #ffe8d4; --orange-200: #ffd2ad; --orange-300: #ffb37a;
  --orange-400: #ff9447; --orange-500: #ff7a1a; --orange-700: #a84400; --orange-800: #8a3800;

  /* RGB triplets for translucent uses: rgb(var(--x-rgb) / .2) */
  --brand-rgb: 255 122 26;
  --ink-rgb: 23 20 15;
  --glass-rgb: 255 255 255;

  /* -- 1c. Surfaces: the page is white (flat); sunken is warm paper -- */
  --page: var(--gray-0);
  --raised: var(--gray-0);
  --raised-hover: var(--gray-50);
  --raised-active: var(--gray-100);
  --sunken: var(--gray-100);
  --sunken-hover: var(--gray-200);
  --overlay: var(--gray-0);
  --scrim: rgb(var(--ink-rgb) / .5);
  --track: var(--gray-200);
  --wash: rgb(var(--ink-rgb) / .06);           /* NEW: hover wash on white controls */
  --photo-bg: #cfc8bb;                         /* NEW: photo frame while the image loads */
  --note-label: rgb(255 255 255 / .92);        /* NEW: .photo-note label over a photo */

  /* -- 1d. Text -- */
  --text-strong: var(--gray-1000);
  --text: var(--gray-900);
  --text-muted: var(--gray-800);               /* 7.75:1 on white */
  --text-faint: var(--gray-700);               /* 4.44:1: meta ≥ 14px and icons only */
  --text-on-brand: var(--gray-1000);           /* ink on orange, never white */
  --text-on-ink: #ffffff;                      /* NEW */
  --text-link: var(--gray-1000);               /* links are ink and underlined */
  --text-link-hover: var(--orange-700);
  --link-underline: rgb(var(--ink-rgb) / .45);

  /* -- 1e. Borders -- */
  --ink-line: var(--gray-1000);                /* NEW: 2px button and key-card outlines */
  --border-hairline: var(--gray-300);
  --border: var(--gray-400);
  --border-hover: var(--gray-500);
  --border-strong: var(--gray-600);            /* inputs: 3.71:1 on white (WCAG 1.4.11) */
  --border-strong-hover: var(--gray-1000);
  --border-disabled: var(--gray-400);
  --note-bd: rgb(var(--ink-rgb) / .1);         /* NEW: notes and banners */

  /* -- 1f. Brand roles -- */
  --brand: var(--orange-500);
  --brand-hover: var(--orange-400);
  --brand-active: var(--gray-1000);            /* pressed primary flips to ink with white text */
  --brand-tint: var(--orange-100);
  --brand-tint-hover: var(--orange-200);
  --brand-tint-2: var(--orange-50);            /* NEW: next-step card, selected option */
  --brand-bd: var(--orange-300);
  --brand-text: var(--orange-700);             /* orange text on white: 6.01:1 */
  --brand-ring: #0a5cd6;
  --brand-ring-soft: rgb(10 92 214 / .25);
  --focus-ring: #0a5cd6;                       /* blue: visible on white, orange and ink */
  --selection: var(--orange-200);

  /* -- 1g. Accent (saffron): the highlight marker, <mark> and the draft line -- */
  --accent-fill: #ffd447; --accent-tint: #fff3c4; --accent-bd: #f0d36a; --accent-fg: #7a5a00;

  /* -- 1h. Semantic: fg = text and solid fill, bg = tint, bd = border -- */
  --ok-fg: #1c6b2a;   --ok-bg: #e3f5e1;   --ok-bd: #a9d8a6;
  --warn-fg: #8a5000; --warn-bg: #fff1dc; --warn-bd: #f2cf94;   /* = "partial" (.part) */
  --bad-fg: #b42318;  --bad-bg: #fde8e5;  --bad-bd: #f3b8b0;
  --info-fg: #1f56b8; --info-bg: #edf3ff; --info-bd: #bfd2f7;

  /* -- 1i. Subjects --
     bare name = text-safe marker (text, strokes); -fill = flat square fill with ink numerals (NEW);
     -bg / -bd = chip tints */
  --taj: #7a5a00;  --taj-fg: #7a5a00;  --taj-fill: #ffd447;  --taj-bg: #fff8dc;  --taj-bd: #f0d36a;
  --bio: #2b6a2f;  --bio-fg: #2b6a2f;  --bio-fill: #8fd694;  --bio-bg: #ecf8ec;  --bio-bd: #b5e2b5;
  --chem: #0f6b66; --chem-fg: #0f6b66; --chem-fill: #7fddd9; --chem-bg: #e6f8f7; --chem-bd: #a8e6e3;
  --phys: #1f56b8; --phys-fg: #1f56b8; --phys-fill: #8cb8ff; --phys-bg: #edf3ff; --phys-bd: #bfd2f7;
  /* NEW: second audience (#/parents, #/teachers): an ink band with white text (no lilac) */
  --aud: #17140f; --aud-tint: var(--sunken); --aud-fg: #17140f;

  /* -- 2. Typography -- */
  --font-sans: "Hb Sym", "Onest", "Onest fb", system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans", Arial, sans-serif;
  --font-display: "Hb Sym", "Geologica", "Geologica fb", "Onest", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: ui-monospace, "SF Mono", SFMono-Regular, "Cascadia Mono", "Roboto Mono", "Droid Sans Mono", Menlo, Consolas, "Liberation Mono", monospace;

  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;                              /* NEW: h3 card titles */
  --fw-display: 800;                           /* Geologica, the only display weight loaded */

  /* Fluid scale, 360px → 1120px viewport. rem + vw, so browser zoom still works. */
  --fs-xs: 0.8125rem;                                          /* 13px: meta minimum */
  --fs-sm: clamp(0.875rem, 0.8454rem + 0.1316vw, 0.9375rem);   /* 14 → 15 */
  --fs-base: clamp(1rem, 0.9704rem + 0.1316vw, 1.0625rem);     /* 16 → 17 body */
  --fs-reading: clamp(1.0625rem, 1.0329rem + 0.1316vw, 1.125rem); /* 17 → 18 lesson text */
  --fs-md: clamp(1.125rem, 1.0658rem + 0.2632vw, 1.25rem);     /* 18 → 20 lead, h3 */
  --fs-lg: clamp(1.25rem, 1.1316rem + 0.5263vw, 1.5rem);       /* 20 → 24 */
  --fs-xl: clamp(1.5rem, 1.25rem + 1.1vw, 2rem);               /* 24 → 32 h2 */
  --fs-2xl: clamp(2rem, 1.6rem + 1.8vw, 3rem);                 /* 32 → 48 h1 */
  --fs-3xl: clamp(2.25rem, 1.6rem + 3vw, 4rem);                /* 36 → 64 hero */
  --fs-display: clamp(3rem, 2.2rem + 3.5vw, 5rem);             /* 48 → 80 big numbers */
  --fs-control: 1rem;                                          /* 16px button label */
  --fs-input: 1rem;                                            /* 16px: stops iOS zoom on focus */
  /* -- 2+. v14 type steps -- */
  --fs-question: clamp(1.1875rem, 1.13rem + .26vw, 1.3125rem);   /* 19 → 21: the question outranks its 16px answers */
  --fs-hero-m: clamp(2.0625rem, calc(12.5vw - 7px), 2.375rem);   /* landing h1 on phones: 33px at 320 → 38px from 360 */
  --fs-hero-m-ru: clamp(1.6875rem, calc(7.5vw + 3px), 1.875rem); /* the longer Russian h1: 27px at 320 → 30px from 360 */
  --fs-pair: 1.5rem;                        /* 24px: a big Geologica number over a small grey label */
  --fs-caption: var(--fs-xs);               /* 13px photo captions (were 11px, below our own minimum); they wrap, never cut */

  --lh-tight: 1.04;                    /* never lower: the ӣ ӯ macrons need room */
  --lh-heading: 1.1;
  --lh-snug: 1.3;
  --lh-ui: 1.2;
  --lh-body: 1.55;

  --ls-tight: -0.03em;
  --ls-snug: -0.02em;
  --ls-h3: -0.01em;
  --ls-num: -0.035em;                  /* NEW: big numbers */
  --ls-normal: 0;
  --ls-label: 0.01em;                  /* kicker, eyebrow (no uppercase) */

  /* -- 3a. Space (4px base) -- */
  --sp-0: 0;      --sp-px: 1px;   --sp-0-5: 2px;  --sp-1: 4px;    --sp-1-5: 6px;
  --sp-2: 8px;    --sp-3: 12px;   --sp-4: 16px;   --sp-5: 20px;   --sp-6: 24px;
  --sp-8: 32px;   --sp-10: 40px;  --sp-12: 48px;  --sp-16: 64px;  --sp-20: 80px;  --sp-24: 96px;

  /* -- 3b. Radius: small and hard (2 / 4 / 8 / 16) -- */
  --r-xs: 2px;                         /* marks, squares, checkbox */
  --r-sm: 4px;                         /* chips, badges, tiles, photos in cards */
  --r-md: 8px;                         /* buttons, inputs, notes, options */
  --r-lg: 4px;                         /* cards */
  --r-xl: 16px;                        /* hero photo frame, sheets */
  --r-pill: 999px;                     /* progress dots only */

  /* -- 3c. Elevation: flat. Only --lift (and menus/dialogs) cast a shadow. -- */
  --shadow-xs: none;
  --shadow-1: none;
  --shadow-2: none;
  --shadow-3: 0 4px 16px rgb(var(--ink-rgb) / .14);   /* menus and sheets only */
  --shadow-4: 0 8px 32px rgb(var(--ink-rgb) / .2);    /* dialogs only */
  --lift: 0 4px 16px rgb(var(--ink-rgb) / .14);       /* NEW: an HTML card over a photo */
  --shadow-inset: none;
  --shadow-focus: 0 0 0 2px var(--gray-0), 0 0 0 4px var(--focus-ring);
  /* -- 3c+. v14 press ledge (§6b, §10) -- */
  --ledge-y: 3px;                           /* the ledge at rest */
  --ledge-y-press: 1px;                     /* while it is held */
  --press-y: 2px;                           /* how far a held filled button travels down */

  /* -- 3d. Motion (v14): tokens only. What moves is in §10, styles.css §14 and learn.css §17 -- */
  --dur-press: 90ms;     /* a held control dips 1px */
  --dur-fast: 150ms;     /* hover and press colour */
  --dur-ctl: 220ms;      /* control colour */
  --dur-base: 220ms;     /* kept for old uses */
  --dur-slow: 280ms;     /* kept for old uses */
  --dur-enter: 200ms;    /* a fresh block rises or slides in */
  --dur-emph: 320ms;     /* the feedback after Check, a toast */
  --dur-data: 560ms;     /* a bar or a number moving to its real value */
  --stagger: 35ms;       /* between siblings; at most 4 steps (140 ms) */
  --ease-out: cubic-bezier(.22, 1, .36, 1);     /* enters and data (easeOutQuint) */
  --ease-std: cubic-bezier(.22, 1, .36, 1);     /* colour */
  --ease-in: cubic-bezier(.5, 0, .75, 0);       /* the few exits (toast) */
  --ease-in-out: cubic-bezier(.65, 0, .35, 1);  /* waiting loops (skeleton, boot line) */
  --ease-pop: cubic-bezier(.3, 1.45, .55, 1);   /* the one overshoot: a stamp (chosen letter, right mark, today's dot) */
  --ease-spring: var(--ease-pop);
  --rise: 10px;          /* enter distance */
  --slide: 18px;         /* the next question */

  /* -- 3e. z-index -- */
  --z-base: 0;  --z-raised: 1;  --z-sticky: 10;  --z-appbar: 20;  --z-tabbar: 20;
  --z-dropdown: 30;  --z-overlay: 40;  --z-modal: 50;  --z-toast: 60;

  /* -- 3f. Layout -- */
  --w-text: 68ch;
  --w-narrow: 720px;                   /* learning screens, forms */
  --w-page: 1200px;                    /* landing, wide grids */
  --gutter: clamp(16px, 4vw, 32px);
  --appbar-h: 56px;                    /* 72px at ≥ 900 via the media query in styles.css */
  --tabbar-h: 64px;
  --safe-b: env(safe-area-inset-bottom, 0px);
  --control-h: 48px;
  --control-h-sm: 40px;
  --control-h-lg: 56px;
  --section-gap: 64px;                 /* NEW: between landing/home sections */
  --card-pad: clamp(16px, 1rem + .5vw, 20px);
  --hero-bg: none;                     /* the v4 radial glow is gone */
  --glass: var(--raised);              /* no frosted bar */
  --glass-solid: var(--raised);
  --glass-filter: none;
  /* -- 3f+. v14 meter: a row of squares for a real count -- */
  --meter-sq: 12px;                         /* .meter.lg uses 16px */

  /* -- 4. Compat aliases (used by models.css; do not use in new code) -- */
  --bg: var(--page);
  --surface: var(--raised);
  --surface-2: var(--sunken);
  --muted: var(--text-muted);
  --line: var(--border);
  --line-strong: var(--border-strong);
  --brand-strong: var(--brand-hover);
  --brand-ink: var(--text-on-brand);
  --brand-soft: var(--brand-tint);
  --ok: var(--ok-fg);
  --ok-soft: var(--ok-bg);
  --bad: var(--bad-fg);
  --bad-soft: var(--bad-bg);
  --part: var(--warn-fg);
  --part-soft: var(--warn-bg);
  --taj-soft: var(--taj-bg);
  --bio-soft: var(--bio-bg);
  --chem-soft: var(--chem-bg);
  --phys-soft: var(--phys-bg);
  --mono: var(--font-mono);
  --font: var(--font-sans);
  --card: var(--raised);
  --radius: var(--r-lg);
  --radius-sm: var(--r-md);
  --ease: var(--ease-out);
  --accent: var(--accent-fg);
  --accent-bg: var(--accent-fill);
  --on-ok: var(--text-on-ink);
  --on-bad: var(--text-on-ink);
  --shadow: var(--shadow-1);
}

/* ====
   5. Base layer
   ==== */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%; text-size-adjust: 100%;
  scroll-padding-top: calc(var(--appbar-h) + var(--sp-4));
  /* phones and tablets: the fixed tab bar must not cover a control that gets focus near the bottom */
  scroll-padding-bottom: calc(var(--tabbar-h) + var(--safe-b) + var(--sp-4));
  background: var(--page);
}
@media (min-width: 900px) { html { scroll-padding-bottom: var(--sp-4); } }
body {
  margin: 0;
  background: var(--page);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  font-weight: var(--fw-regular);
  line-height: var(--lh-body);
  letter-spacing: var(--ls-normal);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  font-synthesis: none;                /* never fake a bold or an italic */
  overflow-wrap: break-word;
  -webkit-tap-highlight-color: transparent;
}

h1, h2, h3, h4, .h1, .h2, .h3, .h4 { margin: 0 0 var(--sp-3); color: var(--text-strong); text-wrap: balance; }
h1, .h1 { font: var(--fw-display) var(--fs-2xl)/var(--lh-heading) var(--font-display); letter-spacing: var(--ls-snug); }
h2, .h2 { font: var(--fw-display) var(--fs-xl)/var(--lh-heading) var(--font-display); letter-spacing: var(--ls-snug); }
h3, .h3 { font: var(--fw-bold) var(--fs-md)/1.25 var(--font-sans); letter-spacing: var(--ls-h3); }
h4, .h4 { font: var(--fw-semibold) var(--fs-base)/var(--lh-snug) var(--font-sans); letter-spacing: var(--ls-normal); }
/* app.js puts an icon before the text in .h3/.h4 headings */
.h3 > .ic, .h4 > .ic { margin-inline-end: 0.4em; vertical-align: -0.2em; color: var(--text-strong); }

p { margin: 0 0 var(--sp-3); text-wrap: pretty; }
ul, ol { margin: 0 0 var(--sp-3); padding-inline-start: 1.25em; }
b, strong { font-weight: var(--fw-semibold); color: var(--text-strong); }
small { font-size: var(--fs-sm); }

a {
  color: var(--text-link);
  text-decoration: underline;
  text-decoration-color: var(--link-underline);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  transition: color var(--dur-fast) var(--ease-std), text-decoration-color var(--dur-fast) var(--ease-std);
}
/* :where() keeps this at element specificity, so any class (.btn, .tab, .row) wins */
a:where(:hover) { color: var(--text-link-hover); text-decoration-color: currentColor; }

code, kbd, samp, pre { font-family: var(--font-mono); font-size: 0.9em; }
code { padding: 0.1em 0.35em; border-radius: var(--r-xs); background: var(--sunken); color: var(--text-strong); }
pre { overflow-x: auto; }
mark { padding: 0 0.15em; border-radius: var(--r-xs); background: var(--accent-tint); color: var(--text-strong); box-shadow: inset 0 -2px 0 var(--accent-fill); }
hr { height: 0; margin: var(--sp-6) 0; border: 0; border-top: 1px solid var(--border-hairline); }
img, picture, video, canvas { display: block; max-width: 100%; height: auto; }
svg { max-width: 100%; }
table, time, output, meter, progress { font-variant-numeric: tabular-nums; }
input, button, textarea, select { font: inherit; color: inherit; letter-spacing: inherit; }
button { cursor: pointer; }
button:disabled { cursor: not-allowed; }
textarea { resize: vertical; }

::selection { background: var(--selection); color: var(--text-strong); }

/* Focus: one blue ring for every control, 2px with a 2px gap, on white, orange and ink alike.
   Mouse clicks do not show it; the keyboard does. */
:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
:focus:not(:focus-visible) { outline: none; }
[tabindex="-1"]:focus { outline: none; }   /* headings focused after a route change */

.sr {
  position: absolute !important;
  width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
[hidden] { display: none !important; }

/* ====
   6. Primitives (class names are the ones app.js already outputs)
   Every transition is colour only (background-color, color, border-color).
   ==== */

/* -- 6a. Icons: <svg class="ic"> with stroke="currentColor" -- */
.ic {
  display: inline-block; flex: none;
  width: 1.25em; height: 1.25em; vertical-align: -0.25em;
  stroke-width: 2;
}
.ic.sm { width: 1em; height: 1em; vertical-align: -0.125em; }
.ic.big { width: 40px; height: 40px; stroke-width: 1.75; }

/* -- 6b. Buttons --
   .btn alone = outline (white, ink text, 2px ink border). Variants .primary .sun (orange slab)
   .ink (only on an orange band) .ghost (text + underline) · sizes .small .big · .wide · .sq.
   Each variant only resets the --btn-* variables, so later files can do the same. */
.btn {
  --btn-bg: var(--raised);
  --btn-bg-hover: var(--wash);
  --btn-bg-active: var(--raised-active);
  --btn-fg: var(--text-strong);
  --btn-fg-active: var(--text-strong);
  --btn-bd: var(--ink-line);
  --btn-bd-hover: var(--ink-line);
  --btn-shadow: none;
  -webkit-appearance: none; appearance: none;
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  max-width: 100%;
  min-height: var(--control-h);
  padding: var(--sp-2) var(--sp-4);
  border: 2px solid var(--btn-bd);
  border-radius: var(--r-md);
  background: var(--btn-bg);
  color: var(--btn-fg);
  box-shadow: var(--btn-shadow);
  font: var(--fw-semibold) var(--fs-control)/var(--lh-ui) var(--font-sans);
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  user-select: none;
  transition:
    background-color var(--dur-ctl) var(--ease-std),
    border-color var(--dur-ctl) var(--ease-std),
    color var(--dur-ctl) var(--ease-std);
}
.btn .ic { width: 20px; height: 20px; vertical-align: 0; }
@media (hover: hover) and (pointer: fine) {
  .btn:hover { background: var(--btn-bg-hover); border-color: var(--btn-bd-hover); color: var(--btn-fg); }
}
.btn:active { background: var(--btn-bg-active); color: var(--btn-fg-active); }

.btn.primary, .btn.sun {
  --btn-bg: var(--brand);
  --btn-bg-hover: var(--brand-hover);
  --btn-bg-active: var(--brand-active);
  --btn-fg: var(--text-on-brand);
  --btn-fg-active: var(--text-on-ink);
  --btn-bd: var(--ink-line);
  --btn-bd-hover: var(--ink-line);
}
/* Ink slab: used only on an orange band, where an orange button would vanish. */
.btn.ink {
  --btn-bg: var(--gray-1000);
  --btn-bg-hover: var(--gray-900);
  --btn-bg-active: var(--gray-800);
  --btn-fg: var(--text-on-ink);
  --btn-fg-active: var(--text-on-ink);
  --btn-bd: var(--ink-line);
  --btn-bd-hover: var(--ink-line);
}
/* v14: the filled button stands on a hard 3px ledge (no blur): ink; under the ink slab the band's dark orange. Static;
   the press travel is in §10 */
.btn.primary, .btn.sun, .btn.ink { --ledge-c: var(--ink-line); --btn-shadow: 0 var(--ledge-y) 0 var(--ledge-c); }
.btn.ink { --ledge-c: var(--orange-800); }
.btn.primary[disabled], .btn.sun[disabled], .btn.ink[disabled],
.btn.primary[aria-disabled="true"], .btn.sun[aria-disabled="true"], .btn.ink[aria-disabled="true"] { --btn-shadow: none; }
.btn.ghost {
  --btn-bg: transparent;
  --btn-bg-hover: var(--wash);
  --btn-bg-active: var(--sunken);
  --btn-fg: var(--text-strong);
  --btn-bd: transparent;
  --btn-bd-hover: transparent;
  text-decoration: underline;
  text-decoration-color: var(--link-underline);
  text-underline-offset: 0.2em;
}

.btn.small { min-height: var(--control-h-sm); padding: var(--sp-1-5) var(--sp-3); gap: var(--sp-1-5); font-size: 0.9375rem; }
.btn.small .ic { width: 18px; height: 18px; }
.btn.big { min-height: var(--control-h-lg); padding: var(--sp-2) var(--sp-5); font-size: 1.0625rem; }
.btn.wide { display: flex; width: 100%; }
/* Square button: 48×48, one 24px glyph, always with an aria-label */
.btn.sq { flex: none; width: var(--control-h); min-width: var(--control-h); padding: 0; }
.btn.sq .ic { width: 24px; height: 24px; }

.btn[disabled], .btn[aria-disabled="true"] {
  --btn-bg: var(--sunken);
  --btn-bg-hover: var(--sunken);
  --btn-bg-active: var(--sunken);
  --btn-fg: var(--text-faint);
  --btn-fg-active: var(--text-faint);
  --btn-bd: var(--border);
  --btn-bd-hover: var(--border);
  cursor: not-allowed;
}
a.btn[aria-disabled="true"] { pointer-events: none; }

/* Two-line label inside a big button: main text + <small> */
.btn2 { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; text-align: start; line-height: var(--lh-ui); }
.btn2 small { font-size: var(--fs-xs); font-weight: var(--fw-medium); }

/* -- 6c. Icon button: 48px square, 2px ink border, 24px glyph -- */
.iconbtn {
  -webkit-appearance: none; appearance: none;
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: var(--control-h); height: var(--control-h); padding: 0;
  border: 2px solid var(--ink-line); border-radius: var(--r-md);
  background: var(--raised); color: var(--text-strong);
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-std), color var(--dur-fast) var(--ease-std), border-color var(--dur-fast) var(--ease-std);
}
.iconbtn .ic { width: 24px; height: 24px; }
@media (hover: hover) and (pointer: fine) { .iconbtn:hover { background: var(--wash); } }
.iconbtn:active { background: var(--sunken-hover); }

/* -- 6d. Card: white, 1px hairline, 4px corners, no shadow -- */
.card {
  padding: var(--card-pad);
  border: 1px solid var(--border-hairline);
  border-radius: var(--r-lg);
  background: var(--raised);
  box-shadow: none;
}
.card > :first-child { margin-top: 0; }
.card > :last-child { margin-bottom: 0; }
.card.center { text-align: center; }
/* Key card (next step, result, mock confirm): weight comes from a 2px ink border, never a shadow */
.card.key { border: 2px solid var(--ink-line); }
.card.key.tint { background: var(--brand-tint-2); }

/* -- 6e. Badges and chips: 4px corners, 1px border, 13px Onest 600 -- */
.badge, .schip, .mchip {
  display: inline-flex; align-items: center; gap: var(--sp-1-5);
  min-height: 24px;
  padding: 1px var(--sp-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--raised);
  color: var(--text-strong);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.badge.ok   { background: var(--ok-bg);   border-color: var(--ok-bd);   color: var(--ok-fg); }
.badge.part { background: var(--warn-bg); border-color: var(--warn-bd); color: var(--warn-fg); }
.badge.bad  { background: var(--bad-bg);  border-color: var(--bad-bd);  color: var(--bad-fg); }
.badge.info { background: var(--info-bg); border-color: var(--info-bd); color: var(--info-fg); }
.badge.brand { background: var(--brand-tint); border-color: var(--brand-bd); color: var(--text-strong); }

/* Subject chip: white with the subject's mark. <span class="schip bio"><svg class="ic">…</svg>Биология</span> */
.schip { padding-inline-start: var(--sp-1-5); }
.schip .ic { width: 14px; height: 14px; vertical-align: 0; }
.schip.taj .ic  { color: var(--taj-fg); }
.schip.bio .ic  { color: var(--bio-fg); }
.schip.chem .ic { color: var(--chem-fg); }
.schip.phys .ic { color: var(--phys-fg); }

/* Subject square mark (12px, ink edge, flat subject fill): <span class="sq-mark bio"></span> */
.sq-mark { display: inline-block; flex: none; width: 12px; height: 12px; border: 1px solid var(--ink-line); border-radius: var(--r-xs); background: var(--sunken); }
.sq-mark.taj { background: var(--taj-fill); }
.sq-mark.bio { background: var(--bio-fill); }
.sq-mark.chem { background: var(--chem-fill); }
.sq-mark.phys { background: var(--phys-fill); }

/* Mastery chip: m0 not started · m1 started · m2 almost · m3 knows well. The square fills up. */
.mchip::before {
  content: "";
  flex: none;
  width: 8px; height: 8px;
  border: 1.5px solid currentColor;
  border-radius: var(--r-xs);
}
.mchip.m1 { background: var(--warn-bg);    border-color: var(--warn-bd);  color: var(--warn-fg); }
.mchip.m2 { background: var(--brand-tint); border-color: var(--brand-bd); color: var(--text-strong); }
.mchip.m3 { background: var(--ok-bg);      border-color: var(--ok-bd);    color: var(--ok-fg); }
.mchip.m1::before { box-shadow: inset -3px 0 0 currentColor; }   /* half-filled, no gradient */
.mchip.m2::before, .mchip.m3::before { background: currentColor; }

/* -- 6f. Progress bars: <div class="bar thin bio"><div class="fill" style="width:40%"> --
   8px track, 2px corners; the fill is flat with a 1px ink right edge. */
.bar { position: relative; height: 8px; overflow: hidden; border-radius: var(--r-xs); background: var(--track); }
.bar.thin { height: 6px; }
.bar .fill {
  height: 100%; max-width: 100%;
  border-radius: inherit;
  background: var(--brand);
  box-shadow: inset -1px 0 0 var(--ink-line);
}
.bar .fill[style*="width:0"], .bar .fill[style*="width: 0"] { box-shadow: none; }
.bar.taj .fill  { background: var(--taj-fill); }
.bar.bio .fill  { background: var(--bio-fill); }
.bar.chem .fill { background: var(--chem-fill); }
.bar.phys .fill { background: var(--phys-fill); }

/* -- 6g. Forms: 48px, 8px corners, 2px border that turns ink on hover and focus -- */
.form { display: flex; flex-direction: column; gap: var(--sp-4); }

.fld { display: flex; flex-direction: column; gap: var(--sp-1-5); }
.fld > span { color: var(--text-strong); font-size: var(--fs-sm); font-weight: var(--fw-semibold); }
.fld input, .fld select, .fld textarea {
  width: 100%;
  min-height: var(--control-h);
  padding: 10px var(--sp-3);
  border: 2px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--raised);
  color: var(--text-strong);
  font-size: var(--fs-input);
  line-height: 1.4;
  transition: border-color var(--dur-fast) var(--ease-std);
}
.fld textarea { min-height: 112px; resize: vertical; }
.fld select {
  -webkit-appearance: none; appearance: none;
  padding-inline-end: 40px;
  background: var(--raised) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M6 8l4 4 4-4' fill='none' stroke='%2317140f' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 12px center / 18px;
}
.fld input::placeholder, .fld textarea::placeholder { color: var(--text-muted); opacity: 1; }
@media (hover: hover) and (pointer: fine) { .fld input:hover, .fld select:hover, .fld textarea:hover { border-color: var(--border-strong-hover); } }
.fld input:focus, .fld select:focus, .fld textarea:focus { border-color: var(--ink-line); }
.fld input:focus-visible, .fld select:focus-visible, .fld textarea:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.fld [aria-invalid="true"] { border-color: var(--bad-fg); }

/* Checkbox and radio rows: <label class="chk"><input type="checkbox"><span>…</span></label> */
.chk, .radio {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  min-height: var(--control-h);
  padding-block: 12px;
  color: var(--text);
  font-size: var(--fs-sm);
  line-height: 1.45;
  cursor: pointer;
}
.radio { display: inline-flex; align-items: center; padding-block: 0; font-size: var(--fs-base); }
.chk input, .radio input {
  -webkit-appearance: none; appearance: none;
  flex: none;
  width: 22px; height: 22px; margin: 0;
  border: 2px solid var(--border-strong);
  border-radius: var(--r-xs);
  background: var(--raised) no-repeat center / 16px;
  accent-color: var(--gray-1000);      /* used where appearance:none is ignored */
  cursor: pointer;
  transition: background-color var(--dur-fast) var(--ease-std), border-color var(--dur-fast) var(--ease-std);
}
.chk input { margin-top: -1px; }
.radio input { border-radius: var(--r-pill); }
.chk input:checked {
  border-color: var(--ink-line);
  background-color: var(--gray-1000);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M5 10.5l3.2 3.2L15 7' fill='none' stroke='%23ffffff' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.radio input:checked { border-color: var(--ink-line); background-color: var(--gray-1000); box-shadow: inset 0 0 0 4px var(--raised); }
@media (hover: hover) and (pointer: fine) { .chk:hover input:not(:checked), .radio:hover input:not(:checked) { border-color: var(--border-strong-hover); } }
.chk input:disabled, .radio input:disabled { background-color: var(--sunken); border-color: var(--border-disabled); cursor: not-allowed; }

/* Role picker: radios as outlined tiles (no :has(); the checked radio itself shows the state) */
fieldset.roles { display: flex; flex-wrap: wrap; gap: var(--sp-2); min-width: 0; margin: 0; padding: 0; border: 0; }
fieldset.roles legend { margin-bottom: var(--sp-2); padding: 0; color: var(--text-strong); font-size: var(--fs-sm); font-weight: var(--fw-semibold); }
fieldset.roles.col { flex-direction: column; }
fieldset.roles .radio {
  padding: 0 var(--sp-4) 0 var(--sp-3);
  border: 2px solid var(--border);
  border-radius: var(--r-md);
  background: var(--raised);
  transition: border-color var(--dur-fast) var(--ease-std), background-color var(--dur-fast) var(--ease-std);
}
@media (hover: hover) and (pointer: fine) { fieldset.roles .radio:hover { border-color: var(--ink-line); } }

.err { margin: 0; color: var(--bad-fg); font-size: var(--fs-sm); font-weight: var(--fw-medium); }
.okmsg {
  margin: 0;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--ok-bd);
  border-radius: var(--r-md);
  background: var(--ok-bg);
  color: var(--ok-fg);
  font-weight: var(--fw-medium);
}
/* Honeypot: off-screen, unfocusable (app.js sets tabindex=-1), invisible */
.hp { position: absolute !important; left: -10000px; width: 1px; height: 1px; overflow: hidden; opacity: 0; pointer-events: none; }

/* -- 6h. Segmented control: an ink-outlined group; the active segment is ink-filled -- */
.segctl {
  display: flex; gap: 0;
  padding: 0;
  border: 2px solid var(--ink-line);
  border-radius: var(--r-md);
  background: var(--raised);
  overflow: hidden;
}
.segctl button {
  -webkit-appearance: none; appearance: none;
  flex: 1 1 0; min-width: 0;
  min-height: 44px;                    /* + the 2px border = 48px */
  padding: var(--sp-1-5) var(--sp-3);
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--text-strong);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-ui);
  transition: background-color var(--dur-ctl) var(--ease-std), color var(--dur-ctl) var(--ease-std);
  position: relative;
}
.segctl button + button { border-inline-start: 1px solid var(--ink-line); }
@media (hover: hover) and (pointer: fine) { .segctl button:hover { background: var(--wash); } }
.segctl button.on, .segctl button[aria-pressed="true"] {
  background: var(--gray-1000);
  color: var(--text-on-ink);
}
.segctl button:focus-visible { outline-offset: -4px; outline-color: var(--focus-ring); box-shadow: inset 0 0 0 2px var(--gray-0); }

/* ====
   7. Utilities
   ==== */
.muted { color: var(--text-muted); }
.small { font-size: var(--fs-sm); line-height: 1.45; }
.center { text-align: center; }
.tnum { font-variant-numeric: tabular-nums; }
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.display {
  font: var(--fw-display) var(--fs-display)/1 var(--font-display);
  letter-spacing: var(--ls-num);
  font-variant-numeric: lining-nums tabular-nums;
  color: var(--text-strong);
}
.lead { max-width: var(--w-text); margin: 0 0 var(--sp-4); color: var(--text-muted); font-size: var(--fs-md); line-height: 1.5; }

/* Labels above a title. Cyrillic, so no uppercase: weight 600 and light tracking only. */
.kicker, .eyebrow {
  margin: 0 0 var(--sp-1-5);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-label);
}
.kicker { color: var(--brand-text); }
.eyebrow { color: var(--text-muted); }

/* Notes and banners: tint fill, faint ink border, 8px corners */
.note {
  margin: 0 0 var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--note-bd);
  border-radius: var(--r-md);
  background: var(--sunken);
  color: var(--text);
  font-size: var(--fs-sm);
  line-height: 1.5;
}
.note > :last-child { margin-bottom: 0; }
.note.info { background: var(--info-bg); }
.note.warn { background: var(--warn-bg); }
.note.ok { background: var(--ok-bg); }
.note.draft { background: var(--accent-tint); }

/* Spinner: the only infinite animation in the system */
.spinner {
  display: inline-block; flex: none;
  width: 20px; height: 20px;
  border: 2px solid var(--track);
  border-top-color: var(--ink-line);
  border-radius: var(--r-pill);
  animation: hb-spin .7s linear infinite;
}
@keyframes hb-spin { to { transform: rotate(360deg); } }

/* App bar surface: solid, never frosted */
.glass { background: var(--raised); }

/* Logo mark: a white square with a 2px ink border and an ink H (index.html boot bar, logo()) */
.logo .logo-sq { fill: var(--gray-0); stroke: var(--ink-line); stroke-width: 2; }
.logo .logo-ink { fill: var(--text-strong); }
/* v14: orange on white bars, like the app icon (white on bands and the footer) */
.appbar:not(.band) .logo .logo-sq { fill: var(--brand); }

/* v14 meter(n, total, cls, key, label): squares for a real count; .lg 16px; .taj/.bio/.chem/.phys = subject fill */
.meter { display: inline-flex; flex-wrap: wrap; gap: 3px; vertical-align: middle; }
.meter i { display: block; width: var(--meter-sq); height: var(--meter-sq); border: 1.5px solid var(--border-strong); border-radius: var(--r-xs); background: var(--raised); }
.meter.lg i { width: 16px; height: 16px; }
.meter i.on { border-color: var(--ink-line); background: var(--brand); }
.meter.taj i.on { background: var(--taj-fill); } .meter.bio i.on { background: var(--bio-fill); }
.meter.chem i.on { background: var(--chem-fill); } .meter.phys i.on { background: var(--phys-fill); }

/* Boot screen (index.html): the orange bar with the logo; the text is unchanged */
.boot .boot-bar { border-bottom: 1px solid rgb(var(--ink-rgb) / .12); background: var(--brand); color: var(--text-on-brand); }
.boot .boot-in b { color: var(--text-strong); font-family: var(--font-display); font-weight: var(--fw-display); letter-spacing: var(--ls-snug); }

/* ====
   8. v5 media primitives: photo frame, stickers, hand-drawn marks, credits
   (app.js helpers photo(), sticker(), handLoop(), handUnderline(), creditsBlock())
   ==== */

/* -- 8a. Photo frame: <figure class="photo [hero]"><img …><figcaption class="photo-note">…</figcaption></figure> -- */
.photo {
  position: relative;
  margin: 0;
  overflow: hidden;
  aspect-ratio: 16 / 10;
  border: 2px solid var(--ink-line);
  border-radius: var(--r-sm);
  background: var(--photo-bg);
}
.photo.hero { border-radius: var(--r-xl); }
.photo.tall { aspect-ratio: 4 / 5; }
.photo img { width: 100%; height: 100%; object-fit: cover; color: transparent; }
/* A photo that failed to load (app.js adds .img-fail): the empty frame and its caption stay as a clean placeholder. */
.photo img.img-fail { visibility: hidden; }
@media (max-height: 700px) { .photo.hero { aspect-ratio: 2 / 1; } }
.photo-note {
  position: absolute; left: var(--sp-2); top: var(--sp-2);
  max-width: 70%;
  padding: 4px 7px;
  border-radius: var(--r-xs);
  background: var(--note-label);
  color: var(--text-strong);
  /* v14: 13px, wraps, never cut (places are named) */
  font: var(--fw-medium) var(--fs-caption)/1.25 var(--font-sans);
}
/* An HTML card that overlaps the lower part of a photo: put it right after the figure.
   The only shadow on a page. */
.photo + .over {
  position: relative; z-index: var(--z-raised);
  margin: calc(-1 * var(--over-h, 72px)) 0 0 14px;
  padding: var(--card-pad);
  border: 1px solid var(--ink-line);
  border-radius: var(--r-sm);
  background: var(--raised);
  box-shadow: var(--lift);
}

/* -- 8b. Stickers: <img class="sticker …"> (decorative, alt="", never moves) --
   The parent needs position:relative (.has-sticker). Sizes: .sm 64 · default 80 · .lg 104.
   Placement .tr overlaps the top-right corner by about a quarter of its size.
   Tilt (only one tilted thing per screen): .tilt-l -6° · .tilt-4 -4° · .tilt-r 5°. */
.has-sticker { position: relative; }
.sticker {
  position: absolute; z-index: var(--z-raised);
  width: 80px; height: auto; max-width: none;
  pointer-events: none;
  user-select: none;
  color: transparent;
}
img.sticker.img-fail { display: none; }   /* failed to load: no broken-image box */
.sticker.sm { width: 64px; }
.sticker.lg { width: 104px; }
.sticker.tr { top: -20px; right: -12px; }
.sticker.br { bottom: -18px; right: -10px; }
.sticker.inline { position: static; display: block; }
.sticker.tilt-l { transform: rotate(-6deg); }
.sticker.tilt-4 { transform: rotate(-4deg); }
.sticker.tilt-r { transform: rotate(5deg); }
@media (max-width: 599px) { .sticker.lg { width: 88px; } }

/* -- 8c. Hand-drawn marks (inline SVG, aria-hidden, stroke = currentColor) --
   Loop label: handLoop(textHtml) → <span class="hand-label">text<svg>…</svg></span> (landing only).
   Underline: handUnderline(html, onInk) → <span class="uline [ink]">phrase<svg>…</svg></span>
   (landing h1 and the #/results heading only). Orange on white; .ink = ink on an orange band. */
.hand-label {
  position: relative; display: inline-block;
  margin: 4px 0 18px 2px;
  padding: 9px 20px 10px;
  color: var(--text-strong);
  font: var(--fw-semibold) 0.8125rem/1 var(--font-sans);
  letter-spacing: var(--ls-label);
}
.hand-label svg {
  position: absolute; left: -6px; top: -3px;
  width: calc(100% + 16px); height: calc(100% + 9px);
  overflow: visible; pointer-events: none;
  color: var(--ink-line);
}
.uline { position: relative; display: inline-block; }
.uline svg {
  position: absolute; left: -2%; bottom: -0.16em;
  width: 104%; height: 0.32em;
  overflow: visible; pointer-events: none;
  color: var(--brand);
}
.uline.ink svg { color: var(--ink-line); }

/* -- 8d. Credits block (#/privacy#credits) -- */
.credits { margin-top: var(--sp-8); }
.credits ul { list-style: none; margin: 0; padding: 0; }
.credits li { padding: var(--sp-3) 0; border-top: 1px solid var(--border-hairline); font-size: var(--fs-sm); line-height: 1.45; }
.credits li:last-child { border-bottom: 1px solid var(--border-hairline); }
.credits li b { display: block; }
.credits .muted { display: block; }

/* ====
   9. Reduced motion and forced colours
   ==== */
@media (prefers-reduced-motion: reduce) {
  :root { --dur-fast: 1ms; --dur-ctl: 1ms; --dur-base: 1ms; --dur-slow: 1ms; }
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
  .spinner { animation: none; border-color: var(--border); border-top-color: var(--ink-line); }
}

/* Windows high contrast: real borders stay; orange and ink fills fall back to system colours */
@media (forced-colors: active) {
  .chk input, .radio input { appearance: auto; }   /* forced colours = Windows only, so no prefix */
  .btn, .btn.primary, .btn.sun, .btn.ink, .iconbtn { background: ButtonFace; color: ButtonText; border-color: ButtonText; }
  .btn.ghost { border-color: transparent; }
  .bar .fill { background: Highlight; }
  .segctl button.on, .segctl button[aria-pressed="true"] { background: Highlight; color: HighlightText; }
  .sq-mark, .card.key, .photo { border-color: CanvasText; }
  .logo .logo-sq, .appbar:not(.band) .logo .logo-sq { fill: Canvas; stroke: CanvasText; }
  .logo .logo-ink { fill: CanvasText; }
  .btn.primary, .btn.sun, .btn.ink { --btn-shadow: none; }
  .meter i { border-color: CanvasText; }
  .meter i.on { background: Highlight; }
}

/* ====
   10. Motion system (v14, design/v14/analysis/motion.md). app.js puts one-shot classes (fx(), celebrate()) on
   freshly rendered nodes; these keyframes do the moving. Only transform and opacity move (two exceptions: the
   Check-ready cue and the exam timer colour), fill-mode is backwards (nothing lingers once done), and every moving
   rule sits inside (prefers-reduced-motion: no-preference). §9 stays as the safety net.
   ==== */
@keyframes hb-rise { from { opacity: 0; transform: translate3d(0, var(--rise), 0); } }
@keyframes hb-fade { from { opacity: 0; } }
@keyframes hb-soft { from { opacity: .35; } }
@keyframes hb-in-r { from { opacity: 0; transform: translate3d(var(--slide), 0, 0); } }
@keyframes hb-in-l { from { opacity: 0; transform: translate3d(calc(-1 * var(--slide)), 0, 0); } }
@keyframes hb-stamp { from { transform: scale(.78); } }
@keyframes hb-pop { from { opacity: 0; transform: scale(.5); } }
@keyframes hb-grow { from { transform: scaleX(var(--from, 0)); } }
@keyframes hb-breathe { from { opacity: 1; } to { opacity: .45; } }

@media (prefers-reduced-motion: no-preference) {
  /* press: a held control dips 1px (v5's colour flip stays) */
  .btn, .iconbtn {
    transition: background-color var(--dur-ctl) var(--ease-std), border-color var(--dur-ctl) var(--ease-std),
      color var(--dur-ctl) var(--ease-std), transform var(--dur-press) var(--ease-out);
  }
  .btn:not([disabled]):not([aria-disabled="true"]):active, .iconbtn:active { transform: translate3d(0, 1px, 0) scale(.985); }
  /* v14: a held filled button sinks 2px onto its ledge (3px → 1px) */
  .btn.primary:not([disabled]):not([aria-disabled="true"]):active,
  .btn.sun:not([disabled]):not([aria-disabled="true"]):active,
  .btn.ink:not([disabled]):not([aria-disabled="true"]):active { transform: translate3d(0, var(--press-y), 0); --ledge-y: var(--ledge-y-press); }
  /* bars: the fill is laid out at its final width; .fx-grow scales it from where it was (--from = old / new) */
  .bar .fill { transform-origin: 0 50%; }
  .bar .fill.fx-grow { animation: hb-grow var(--dur-data) var(--ease-out) backwards; }
  /* a details block opened by a tap: its content rises in once */
  details.fx-open > :not(summary) { animation: hb-rise var(--dur-enter) var(--ease-out) backwards; }
  /* v14 meters: the squares that are new since this visit last showed them stamp in (dataMotion sets the delays) */
  .meter i.fx-stamp { animation: hb-stamp 260ms var(--ease-pop) backwards; }
}
