/* ==========================================================================
   Vistaria base layer (vx-base.css): fonts, reset, element defaults, type
   scale, script adaptations, focus/selection/scrollbars, the desk texture,
   icon mask tokens, motion keyframes, reduced motion, and small utilities.
   Spec §3, §2.8, §5.2–5.3, §4.36. Reads ONLY tokens from vx-tokens.css.

   ⚠️ Tracking: no literal letter-spacing here except on always-Latin elements.
      Everything reads var(--vx-track-*), which §3.4 zeroes for RTL/CJK/Hindi
      (negative tracking breaks Persian joining). scripts/design_qa.py check 10.
   ⚠️ Logical properties only (spec §7).
   ========================================================================== */

/* ── 1. @font-face (spec §3.1) ─────────────────────────────────────────── */
/* Open Runde (SIL OFL 1.1, licence: /static/lab/fonts/OFL-open-runde-inter.txt).
   The Latin subset deliberately has no U+200C–200F: if it did, a Persian word
   with a nim-fasele would switch fonts mid-word and break joining.
   Latin = Basic Latin, Latin-1 and Latin Extended-A (pl, tr need Ext-A; no
   catalog uses Ext-B or the Vietnamese block, checked against all 18 on
   2026-10-04), so each file is ~40 KB, not ~67 KB. A character outside the
   range (ș, ơ …) falls to the next family in the stack, glyph by glyph. */
@font-face{font-family:"Open Runde";font-style:normal;font-display:swap;font-weight:400 500; /* 400 renders with the 500 file: body is medium by rule */
  src:url(/static/lab/fonts/vx-runde-500-latin.woff2) format("woff2");
  unicode-range:U+0000-017F,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0300-0301,U+0303-0304,U+0308,U+0329,U+2000-200B,U+2010-206F,U+20A0-20C0,U+2113,U+2122,U+2190-2199,U+2212,U+2215,U+2713,U+2717,U+FEFF,U+FFFD}
@font-face{font-family:"Open Runde";font-style:normal;font-display:swap;font-weight:600;
  src:url(/static/lab/fonts/vx-runde-600-latin.woff2) format("woff2");
  unicode-range:U+0000-017F,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0300-0301,U+0303-0304,U+0308,U+0329,U+2000-200B,U+2010-206F,U+20A0-20C0,U+2113,U+2122,U+2190-2199,U+2212,U+2215,U+2713,U+2717,U+FEFF,U+FFFD}
@font-face{font-family:"Open Runde";font-style:normal;font-display:swap;font-weight:700 900; /* 800 renders 700: Open Runde has no 800 */
  src:url(/static/lab/fonts/vx-runde-700-latin.woff2) format("woff2");
  unicode-range:U+0000-017F,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0300-0301,U+0303-0304,U+0308,U+0329,U+2000-200B,U+2010-206F,U+20A0-20C0,U+2113,U+2122,U+2190-2199,U+2212,U+2215,U+2713,U+2717,U+FEFF,U+FFFD}
@font-face{font-family:"Open Runde";font-style:normal;font-display:swap;font-weight:400 500;
  src:url(/static/lab/fonts/vx-runde-500-cyrgreek.woff2) format("woff2");
  unicode-range:U+0370-03FF,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:"Open Runde";font-style:normal;font-display:swap;font-weight:600;
  src:url(/static/lab/fonts/vx-runde-600-cyrgreek.woff2) format("woff2");
  unicode-range:U+0370-03FF,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}
@font-face{font-family:"Open Runde";font-style:normal;font-display:swap;font-weight:700 900;
  src:url(/static/lab/fonts/vx-runde-700-cyrgreek.woff2) format("woff2");
  unicode-range:U+0370-03FF,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116}

/* "Open Runde Core": the same files under a narrower range with NO General
   Punctuation, for CJK stacks, so “ ” ‘ ’ … — stay the CJK font's full-width
   forms (critique #33). */
@font-face{font-family:"Open Runde Core";font-style:normal;font-display:swap;font-weight:400 500;
  src:url(/static/lab/fonts/vx-runde-500-latin.woff2) format("woff2"); unicode-range:U+0020-007E,U+00A0-017F}
@font-face{font-family:"Open Runde Core";font-style:normal;font-display:swap;font-weight:600;
  src:url(/static/lab/fonts/vx-runde-600-latin.woff2) format("woff2"); unicode-range:U+0020-007E,U+00A0-017F}
@font-face{font-family:"Open Runde Core";font-style:normal;font-display:swap;font-weight:700 900;
  src:url(/static/lab/fonts/vx-runde-700-latin.woff2) format("woff2"); unicode-range:U+0020-007E,U+00A0-017F}

/* Metric-matched fallbacks: what the text is set in until Open Runde arrives
   (font-display:swap). A local Arial-metric face (Arial, Arimo, Liberation
   Sans; Mac, Windows, iOS, ChromeOS), Roboto (Android) or DejaVu Sans
   (Linux), scaled so a line of copy is as wide as it will be in Open Runde:
   the swap no longer re-wraps the hero (en landing phone CLS 0.068 → 0,
   with no 700 preload — preloading it cost ~300ms of FCP/LCP on a slow
   phone for no CLS gain once these exist). font-display:swap on them too:
   with the default (block) a still-loading LOCAL face hid the text until
   Open Runde itself arrived (+450ms FCP on a slow phone). Values from the en catalog's own letter frequencies
   against the shipped subsets (size-adjust = Runde ÷ fallback average
   advance; ascent/descent = Runde's 2728/680 per 2816 em ÷ size-adjust). */
/* impeccable-disable-next-line design-system-font -- a metric-matched FALLBACK for Open Runde (a local Arial / DejaVu face, scaled), shown only until Open Runde loads; not a brand face */
@font-face{font-family:"Vx Runde Fallback";font-style:normal;font-display:swap;font-weight:100 500;
  src:local("Arial"),local("ArialMT"),local("Arimo"),local("Liberation Sans"),local("LiberationSans");
  unicode-range:U+0000-017F,U+2000-200A,U+2010-206F,U+20A0-20C0,U+2122,U+2190-2199,U+2212;
  size-adjust:108.21%;ascent-override:89.52%;descent-override:22.31%;line-gap-override:0%}
/* impeccable-disable-next-line design-system-font -- a metric-matched FALLBACK for Open Runde (a local Arial / DejaVu face, scaled), shown only until Open Runde loads; not a brand face */
@font-face{font-family:"Vx Runde Fallback";font-style:normal;font-display:swap;font-weight:600 900;
  src:local("Arial Bold"),local("Arial-BoldMT"),local("Arimo Bold"),local("Arimo-Bold"),local("Liberation Sans Bold"),local("LiberationSans-Bold");
  unicode-range:U+0000-017F,U+2000-200A,U+2010-206F,U+20A0-20C0,U+2122,U+2190-2199,U+2212;
  size-adjust:102.53%;ascent-override:94.49%;descent-override:23.55%;line-gap-override:0%}
/* impeccable-disable-next-line design-system-font -- a metric-matched FALLBACK for Open Runde (Android's Roboto, scaled), shown only until Open Runde loads; not a brand face */
@font-face{font-family:"Vx Runde Fallback R";font-style:normal;font-display:swap;font-weight:100 500;
  src:local("Roboto"),local("Roboto-Regular"),local("Roboto Regular");
  unicode-range:U+0000-017F,U+2000-200A,U+2010-206F,U+20A0-20C0,U+2122,U+2190-2199,U+2212;
  size-adjust:108.47%;ascent-override:89.31%;descent-override:22.26%;line-gap-override:0%}
/* impeccable-disable-next-line design-system-font -- a metric-matched FALLBACK for Open Runde (Android's Roboto, scaled), shown only until Open Runde loads; not a brand face */
@font-face{font-family:"Vx Runde Fallback R";font-style:normal;font-display:swap;font-weight:600 900;
  src:local("Roboto Bold"),local("Roboto-Bold");
  unicode-range:U+0000-017F,U+2000-200A,U+2010-206F,U+20A0-20C0,U+2122,U+2190-2199,U+2212;
  size-adjust:109.12%;ascent-override:88.78%;descent-override:22.13%;line-gap-override:0%}
/* impeccable-disable-next-line design-system-font -- a metric-matched FALLBACK for Open Runde (a local Arial / DejaVu face, scaled), shown only until Open Runde loads; not a brand face */
@font-face{font-family:"Vx Runde Fallback DV";font-style:normal;font-display:swap;font-weight:100 500;
  src:local("DejaVu Sans"),local("DejaVuSans");
  unicode-range:U+0000-017F,U+2000-200A,U+2010-206F,U+20A0-20C0,U+2122,U+2190-2199,U+2212;
  size-adjust:94.58%;ascent-override:102.42%;descent-override:25.53%;line-gap-override:0%}
/* impeccable-disable-next-line design-system-font -- a metric-matched FALLBACK for Open Runde (a local Arial / DejaVu face, scaled), shown only until Open Runde loads; not a brand face */
@font-face{font-family:"Vx Runde Fallback DV";font-style:normal;font-display:swap;font-weight:600 900;
  src:local("DejaVu Sans Bold"),local("DejaVuSans-Bold");
  unicode-range:U+0000-017F,U+2000-200A,U+2010-206F,U+20A0-20C0,U+2122,U+2190-2199,U+2212;
  size-adjust:85.6%;ascent-override:113.17%;descent-override:28.21%;line-gap-override:0%}

/* The wordmark "Vistaria" on an RTL page: the six letters of Open Runde 700
   (1.6 KB) instead of the two 40 KB Latin faces an RTL page otherwise
   downloads for one word (PERF-03). LTR pages already carry Open Runde. */
/* impeccable-disable-next-line design-system-font -- Open Runde 700 itself, cut to the six letters of "Vistaria" for RTL pages */
@font-face{font-family:"Vx Wordmark";font-style:normal;font-display:swap;font-weight:100 900;
  src:url(/static/lab/fonts/vx-wordmark-700.woff2) format("woff2"); unicode-range:U+0056,U+0061,U+0069,U+0072-0074}

/* Tabular figures: a digits-only subset of Inter (Open Runde has no tnum). */
@font-face{font-family:"Vx Figures";font-style:normal;font-display:swap;font-weight:100 900;
  src:url(/static/lab/fonts/vx-figures.woff2) format("woff2");
  unicode-range:U+0024-0025,U+002B-002E,U+0030-003A,U+00A0,U+00D7,U+2009,U+2212}

/* Arabic script (fa, ar, ur). Variable 100–900, has tnum. */
@font-face{font-family:"Vazirmatn";font-style:normal;font-display:swap;font-weight:100 900;
  src:url(/static/lab/fonts/Vazirmatn.woff2) format("woff2")}

/* ── 2. stacks per script (spec §3.2) ──────────────────────────────────── */
[dir="rtl"], :lang(fa), :lang(ar), :lang(ur){
  --vx-font: "Vazirmatn", system-ui, -apple-system, "Segoe UI", Tahoma, sans-serif;   /* Vazirmatn FIRST: it has its own Latin */
  --vx-font-figures: "Vazirmatn", var(--vx-font);                                   /* Persian digits from Vazirmatn (tnum) */
}
:lang(ja){ --vx-font: "Open Runde Core", "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Yu Gothic UI", "Yu Gothic", "Noto Sans JP", "Noto Sans CJK JP", Meiryo, sans-serif; }
:lang(zh){ --vx-font: "Open Runde Core", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", "Noto Sans SC", "Noto Sans CJK SC", sans-serif; }
:lang(ko){ --vx-font: "Open Runde Core", "Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans KR", "Noto Sans CJK KR", sans-serif; }
:lang(hi){ --vx-font: "Open Runde", "Noto Sans Devanagari", "Kohinoor Devanagari", "Nirmala UI", Mangal, sans-serif; }
/* An RTL page keeps Vazirmatn even when an inner element carries a Latin lang. */
[dir="rtl"] :lang(ja), [dir="rtl"] :lang(zh), [dir="rtl"] :lang(ko){ --vx-font: "Vazirmatn", system-ui, sans-serif; }

/* ── 3. reset ──────────────────────────────────────────────────────────── */
/* Zero-specificity (:where) so any class rule wins. margin/padding 0 is kept
   because every legacy page was written against it (the old inline block). */
*, ::before, ::after{ box-sizing:border-box; }
:where(*){ margin:0; padding:0; }
:where(img, svg, video, canvas, picture){ max-inline-size:100%; }
:where(img, video){ block-size:auto; }
:where(button, input, select, textarea){ font:inherit; color:inherit; letter-spacing:inherit; }
:where(button){ cursor:pointer; }
:where(button:disabled, [aria-disabled="true"]){ cursor:not-allowed; }
:where(ul, ol)[role="list"]{ list-style:none; }
:where(table){ border-collapse:collapse; }
:where([hidden]):not([hidden="until-found"]){ display:none !important; }
:where(dialog){ color:inherit; }

/* ── 4. element defaults (spec §3.3) ───────────────────────────────────── */
html{ -webkit-text-size-adjust:100%; text-size-adjust:100%; background-color:var(--vx-desk);
  scrollbar-color:color-mix(in srgb, var(--vx-ink) 28%, transparent) transparent;
  /* WCAG 2.4.11: a focused control (and an anchor jump) never lands under the
     sticky chrome. This is the no-JS estimate for a public page (gutter +
     taskbar + gutter); ui.js §10 measures the real stack on every page (the
     report's section nav, the app scene and tabs, the cockpit's, the phone tab
     bar and a sticky save bar below) and writes it inline on <html>. */
  scroll-padding-block-start:calc(var(--vx-gutter) * 2 + var(--vx-bar-h) + 8px); }
body{ font:500 0.9375rem/1.5 var(--vx-font); color:var(--vx-text); background-color:var(--vx-desk); min-block-size:100svh;
  -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale; font-variant-ligatures:common-ligatures;
  transition:background-color var(--vx-dur-theme) var(--vx-ease-standard); }
h1,h2,h3,h4,h5,h6{ color:var(--vx-ink); font-weight:700; margin:0; text-wrap:balance; overflow-wrap:break-word; }
h1{ font-size:2.25rem; line-height:2.5rem; letter-spacing:var(--vx-track-tight); }
h2{ font-size:1.5rem; line-height:2rem; letter-spacing:var(--vx-track-head); }
h3{ font-size:1.125rem; line-height:1.75rem; letter-spacing:calc(var(--vx-track-head) / 2); }
h4{ font-size:1rem; line-height:1.5rem; font-weight:600; letter-spacing:calc(var(--vx-track-head) / 2); }
p{ margin:0; text-wrap:pretty; }
b,strong{ font-weight:700; color:var(--vx-ink); }
small{ font-size:.8125rem; }
a{ color:inherit; text-underline-offset:.18em; text-decoration-thickness:1px; }
:where(p,li,td,dd) a:not([class]){ color:var(--vx-link); font-weight:600; text-decoration:none; }
:where(p,li,td,dd) a:not([class]):hover{ color:var(--vx-link-hover); text-decoration:underline; }
code,kbd,samp,pre{ font-family:var(--vx-mono); font-size:.8125rem; line-height:1.5; }
hr{ border:0; border-block-start:1px solid var(--vx-line-soft); }
::placeholder{ color:var(--vx-text-3); opacity:1; }
@media (max-width:640px){ h1{ font-size:1.875rem; line-height:2.25rem; } }
/* Long words (de Nutzungsbedingungen, Suchmaschinenoptimierung; ru Политика…)
   were cut at phone widths. Every heading-like line breaks a word that cannot
   fit; on a phone it hyphenates first (only words of 14+ letters — the
   compounds that cannot fit: Nutzungsbedingungen, конфиденциальности — in the
   page's language; "impressions" stays whole). */
:is(h1,h2,h3,h4,h5,h6,.vx-display,.vx-h1,.vx-h2,.vx-h3,.vx-h4,.vx-lede){ overflow-wrap:break-word; }
@media (max-width:640px){
  :is(h1,h2,h3,h4,h5,h6,.vx-display,.vx-h1,.vx-h2,.vx-h3,.vx-h4){ -webkit-hyphens:auto; hyphens:auto; hyphenate-limit-chars:14 6 6; }
}
/* User data (a domain, an email, a subject) is set in <bdi>: in running text,
   headings and the shells' one-line slots it may break anywhere rather than
   run off a phone screen. Table cells are left alone: a table scrolls. */
:where(p, li, dd, h1, h2, h3, h4, h5, h6, blockquote, figcaption, .vx-msg, .vx-crumbs, .vx-scene__title, .vx-scene__desc,
  .vx-panel__title, .vx-panel__desc, .vx-empty__text, .vx-callout__body, .vx-banner, .vx-toast, .vx-card__excerpt, .vx-tile__title) bdi{ overflow-wrap:anywhere; }

/* ── 5. type classes (spec §3.3) ───────────────────────────────────────── */
.vx-display{ font-size:clamp(2.25rem, 1.35rem + 3.2vw, 3.75rem); line-height:1.05; font-weight:700;
  letter-spacing:var(--vx-track-tight); color:var(--vx-ink); text-wrap:balance; }
.vx-h1{ font-size:2.25rem; line-height:2.5rem; font-weight:700; letter-spacing:var(--vx-track-tight); color:var(--vx-ink); }
.vx-h2{ font-size:1.5rem; line-height:2rem; font-weight:700; letter-spacing:var(--vx-track-head); color:var(--vx-ink); }
.vx-h3{ font-size:1.125rem; line-height:1.75rem; font-weight:700; letter-spacing:calc(var(--vx-track-head) / 2); color:var(--vx-ink); }
.vx-h4{ font-size:1rem; line-height:1.5rem; font-weight:600; letter-spacing:calc(var(--vx-track-head) / 2); color:var(--vx-ink); }
@media (max-width:640px){ .vx-h1{ font-size:1.875rem; line-height:2.25rem; } }
.vx-lede{ font-size:1.0625rem; line-height:1.625rem; font-weight:500; color:var(--vx-text); max-inline-size:36rem; }
.vx-prose{ font-size:1.0625rem; line-height:1.75rem; font-weight:500; color:var(--vx-text); }
.vx-prose a:not([class]){ color:var(--vx-prose-link); font-weight:600; text-decoration:underline; }
.vx-ui{ font-size:.875rem; line-height:1.25rem; font-weight:500; }
.vx-small{ font-size:.8125rem; line-height:1.125rem; font-weight:500; color:var(--vx-text-2); }
.vx-label{ font-size:.6875rem; line-height:1rem; font-weight:600; letter-spacing:var(--vx-track-label);
  text-transform:var(--vx-case-label); color:var(--vx-text-3); }
.vx-bignum{ font-family:var(--vx-font-figures); font-variant-numeric:tabular-nums; font-size:clamp(1.75rem, 1rem + 2vw, 2.5rem);
  line-height:1.1; font-weight:700; letter-spacing:var(--vx-track-tight); color:var(--vx-ink); }

/* ── 6. script adaptations (spec §3.4) ─────────────────────────────────── */
[dir="rtl"], :lang(ja), :lang(zh), :lang(ko), :lang(hi){
  --vx-track-tight:0em; --vx-track-head:0em; --vx-track-label:0em; --vx-case-label:none; }   /* 0em, not 0: stays a <length> inside calc() */
[dir="rtl"], :lang(ja), :lang(zh), :lang(ko), :lang(hi){ letter-spacing:0; }
/* A Latin-script block embedded in an RTL page (the English-only API docs and
   Search Console guide viewed with a fa/ar/ur interface) gets its own face and
   tracking back. It must carry both dir="ltr" and an explicit lang; a <bdi
   dir="ltr"> run inherits the page's lang and is deliberately not matched. */
[dir="rtl"] [dir="ltr"][lang]:not(:lang(fa)):not(:lang(ar)):not(:lang(ur)){
  --vx-font: "Open Runde", "Vx Runde Fallback", "Vx Runde Fallback R", "Vx Runde Fallback DV", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --vx-font-figures: "Vx Figures", var(--vx-font);
  --vx-track-tight:-0.025em; --vx-track-head:-0.02em; --vx-track-label:0.06em; --vx-case-label:uppercase;
  font-family:var(--vx-font); letter-spacing:normal; line-height:1.5; }
/* …and the mirror: a Persian/Arabic/Urdu island inside an LTR page (the
   Instagram report, whose dogfood copy is Persian, viewed with an English UI).
   Custom properties inherit, but the document-level RTL rules (tracking 0,
   line-height 1.8) key off the ROOT, so the island restates them. */
[dir="ltr"] [dir="rtl"][lang]:is(:lang(fa),:lang(ar),:lang(ur)){
  --vx-font: "Vazirmatn", system-ui, -apple-system, "Segoe UI", Tahoma, sans-serif;
  --vx-font-figures: "Vazirmatn", var(--vx-font);
  --vx-track-tight:0em; --vx-track-head:0em; --vx-track-label:0em; --vx-case-label:none;
  font-family:var(--vx-font); letter-spacing:0; line-height:1.8; }

/* fa / ar / ur — keyed off the ELEMENT's own language, not a [dir=rtl]
   ancestor: a Latin island (lang="en" dir="ltr") inside an RTL page keeps the
   Latin sizes, weights and italics, and a Persian island in an LTR page gets
   these. (Same specificity as the [dir] form they replace.) */
[dir="rtl"] body{ line-height:1.8; }
:is(.vx-ui,.vx-app):is(:lang(fa),:lang(ar),:lang(ur)){ font-size:.90625rem; line-height:1.75; }            /* 14.5px */
.vx-small:is(:lang(fa),:lang(ar),:lang(ur)){ font-size:.84375rem; line-height:1.7; }                         /* 13.5px */
:is(.vx-label,.vx-panel__meta,.vx-tile__meta,.vx-menu__label,.vx-sidenav__label):is(:lang(fa),:lang(ar),:lang(ur)){ font-size:.75rem; font-weight:700; line-height:1.4; }
:is(h1,h2,h3,h4,.vx-display,.vx-h1,.vx-h2,.vx-h3,.vx-h4):is(:lang(fa),:lang(ar),:lang(ur)){ line-height:1.35; }
.vx-display:is(:lang(fa),:lang(ar),:lang(ur)){ line-height:1.3; }
:is(.vx-display,.vx-h1,h1):is(:lang(fa),:lang(ar),:lang(ur)){ font-weight:800; }
:is(em,i,cite,address):is(:lang(fa),:lang(ar),:lang(ur)){ font-style:normal; }                                /* Vazirmatn has no italic */

/* ja / zh / ko */
:is(:lang(ja),:lang(zh),:lang(ko)) body{ line-height:1.8; }
:is(:lang(ja),:lang(zh),:lang(ko)) .vx-display{ font-size:calc(clamp(2.25rem, 1.35rem + 3.2vw, 3.75rem) * .9); line-height:1.3; }
:is(:lang(ja),:lang(zh),:lang(ko)) :is(h1,h2,h3,.vx-h1,.vx-h2,.vx-h3){ line-height:1.4; }
:is(:lang(ja),:lang(zh),:lang(ko)) :is(em,i,.vx-handnote){ font-style:normal; }
:lang(ja), :lang(zh){ line-break:strict; }
:lang(ko){ word-break:keep-all; }

/* hi */
:lang(hi) body{ line-height:1.75; }
:lang(hi) :is(h1,h2,h3,h4,.vx-display,.vx-h1,.vx-h2,.vx-h3,.vx-h4){ line-height:1.4; }

/* ── 7. focus, selection, scrollbars ───────────────────────────────────── */
:where(a, button, input, select, textarea, summary, [role="button"], [role="switch"], [role="tab"], [role="menuitem"], [tabindex]):focus-visible{
  outline:2px solid var(--vx-focus); outline-offset:2px; }
:where(a, button, summary, [role="button"], [tabindex]):focus:not(:focus-visible){ outline:none; }
::selection{ background:var(--vx-selection); color:var(--vx-ink); }
.vx-scroll{ scrollbar-width:thin; scrollbar-color:color-mix(in srgb, var(--vx-ink) 28%, transparent) transparent; }
.vx-scroll::-webkit-scrollbar{ inline-size:8px; block-size:8px; }
.vx-scroll::-webkit-scrollbar-thumb{ background:color-mix(in srgb, var(--vx-ink) 28%, transparent); border-radius:var(--vx-r-full); }
.vx-scroll::-webkit-scrollbar-track{ background:transparent; }

/* ── 8. desk texture (spec §2.8): the only background "image" ──────────── */
/* Uniform dots, never a network/constellation. Never inside app/cockpit sheets.
   The page's desk is a FIXED LAYER behind everything (body.vx-desk::before),
   not background-attachment:fixed: a fixed background repaints the whole
   viewport on every scroll frame (landing on a phone: 321 paints, 1.1s of
   paint per scroll; results_paid frame p95 67ms → 33ms without it). The layer
   is composited once and the page scrolls over it. Same dots, same glow, same
   place on screen. */
.vx-desk, .vx-card--desk{
  background-color:var(--vx-desk);
  background-image:
    radial-gradient(72% 72% at 50% 38%, var(--vx-desk-glow), transparent 70%),
    radial-gradient(var(--vx-desk-dot) 1.4px, transparent 0);
  background-repeat:no-repeat, repeat;
  background-size:auto, var(--vx-desk-pitch) var(--vx-desk-pitch);
}
body.vx-desk{ background-image:none; isolation:isolate; }
body.vx-desk::before{ content:""; position:fixed; inset:0; z-index:-1; pointer-events:none;
  background-color:var(--vx-desk);
  background-image:
    radial-gradient(72% 72% at 50% 38%, var(--vx-desk-glow), transparent 70%),
    radial-gradient(var(--vx-desk-dot) 1.4px, transparent 0);
  background-repeat:no-repeat, repeat;
  background-size:auto, var(--vx-desk-pitch) var(--vx-desk-pitch);
  will-change:transform; }

/* ── 9. icon MASK tokens ───────────────────────────────────────────────── */
/* Colourless on purpose (stroke black): use them as mask-image and paint
   with background:currentColor / a token, e.g.
     .x::after{ background:var(--vx-text-3); mask:var(--vx-i-check) center/contain no-repeat; }
   (A coloured data-URI is a colour token and lives in vx-tokens.css, like
   --vx-i-select.) Paths: Lucide (ISC). */
:root{
  --vx-i-plus:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M12 5v14'/%3E%3C/svg%3E");
  --vx-i-minus:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E");
  --vx-i-check:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
  --vx-i-x:        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6 6 18M6 6l12 12'/%3E%3C/svg%3E");
  --vx-i-chevron:  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");        /* down */
  --vx-i-chevron-end: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 18 6-6-6-6'/%3E%3C/svg%3E"); /* right: add .vx-flip-rtl or scale:var(--vx-dir) 1 */
  --vx-i-arrow:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M12 5l7 7-7 7'/%3E%3C/svg%3E");      /* right; flips with --vx-dir */
  --vx-i-external: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 17 17 7M7 7h10v10'/%3E%3C/svg%3E");
  --vx-i-dot:      url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='5' fill='black'/%3E%3C/svg%3E");
  --vx-i-info:     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 16v-4M12 8h.01'/%3E%3C/svg%3E");
  --vx-i-alert:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3'/%3E%3Cpath d='M12 9v4M12 17h.01'/%3E%3C/svg%3E");
}
/* select chevron default: a native <select> styled by a recipe reads --vx-i-select (vx-tokens.css). */

/* ── 10. motion: keyframes (spec §5.2) ─────────────────────────────────── */
@keyframes vx-pop-in   { from{opacity:0; scale:.92} to{opacity:1; scale:1} }
@keyframes vx-pop-out  { from{opacity:1; scale:1}   to{opacity:0; scale:.92} }
@keyframes vx-fade-in  { from{opacity:0; translate:0 2px} to{opacity:1; translate:0 0} }
@keyframes vx-fade-up  { from{opacity:0; translate:0 10px} to{opacity:1; translate:0 0} }
@keyframes vx-menu-in  { from{opacity:0; translate:0 -4px; scale:.98} to{opacity:1; translate:0 0; scale:1} }
@keyframes vx-toast-in { from{opacity:0; translate:calc((100% + 24px) * var(--vx-dir)) 0} to{opacity:1; translate:0 0} }
@keyframes vx-toast-out{ to{opacity:0; translate:calc(32px * var(--vx-dir)) 0} }
@keyframes vx-drawer-in{ from{translate:calc(100% * var(--vx-dir)) 0} to{translate:0 0} }
@keyframes vx-sheet-up { from{translate:0 100%} to{translate:0 0} }
@keyframes vx-shimmer  { 0%{translate:calc(-100% * var(--vx-dir)) 0} 100%{translate:calc(100% * var(--vx-dir)) 0} }   /* the skeleton's highlight sweep, on a ::after (transform only) */
@keyframes vx-spin     { to{rotate:360deg} }
@keyframes vx-loadbar  { 0%{translate:calc(-100% * var(--vx-dir)) 0} 100%{translate:calc(300% * var(--vx-dir)) 0} }   /* transform only: a 33% strip crosses the track (compositor) */
@keyframes vx-row-new  { from{background:var(--vx-accent-tint)} to{background:transparent} }
@keyframes vx-tilt-shake{ 0%{rotate:0deg} 16.6%{rotate:12deg} 50%{rotate:-10deg} 83.3%{rotate:8deg} 100%{rotate:0deg} }
@keyframes vx-marquee  { to{translate:calc(-50% * var(--vx-dir)) 0} }
@keyframes vx-nudge    { 0%,100%{rotate:calc(6deg * var(--vx-dir))} 33%{rotate:0deg} 66%{rotate:calc(10deg * var(--vx-dir))} }  /* 600ms once */

/* legacy reveal (.rv → .vx-reveal): visible by default; JS arms it. Do not add to new sections. */
html.js .vx-reveal:not(.is-in){ opacity:0; translate:0 10px; }
html.js .vx-reveal.is-in{ animation:vx-fade-up var(--vx-dur-panel) ease-out both; animation-delay:calc(min(var(--i, 0), 5) * 60ms); }

/* landing hero entrance (first paint only): 600ms ease-out, 100ms stagger.
   It rises from 60% opacity, never from 0: an element at opacity 0 is not
   "painted" for LCP, so the fade delayed the landing's LCP by 172–440ms. */
@keyframes vx-hero-rise{ from{opacity:.6; translate:0 10px} to{opacity:1; translate:0 0} }
.vx-hero-in > *{ animation:vx-hero-rise 600ms var(--vx-ease-out) both; }
.vx-hero-in > :nth-child(2){ animation-delay:100ms; }
.vx-hero-in > :nth-child(3){ animation-delay:200ms; }
.vx-hero-in > :nth-child(n+4){ animation-delay:300ms; }

/* count-up (ui.js): the animating text is aria-hidden, the final value is the label */
[data-vx-count], .vx-num, .n{ unicode-bidi:isolate; }

/* ── 11. reduced motion: reduce, never remove (spec §5.3) ──────────────── */
@media (prefers-reduced-motion: reduce){
  :root{ --vx-dur-fast:0ms; --vx-dur-press:0ms; --vx-dur-open:0ms; --vx-dur-tile:0ms; --vx-dur-panel:0ms;
         --vx-dur-reveal:0ms; --vx-dur-draw:0ms; --vx-dur-count:0ms; --vx-dur-theme:0ms; }
  /* Transitions are 0s, not 1ms: transition-property defaults to `all`, so a
     1ms duration gave EVERY element a transition on EVERY property — a value
     read right after a change (a sticky bar's offset, a drawer's visibility)
     was still the old one, which broke ui.js's measurements and the drawer's
     focus (the vx-shell note). Nothing listens for transitionend. */
  *,*::before,*::after{ animation-duration:1ms !important; animation-iteration-count:1 !important; animation-delay:0ms !important;
                        transition-duration:0s !important; transition-delay:0s !important; scroll-behavior:auto !important; }
  html.js .vx-reveal:not(.is-in){ opacity:1; translate:none; }          /* never stranded at opacity 0 */
  html.js .vx-mark.is-armed{ background-size:100% 70%; }              /* shown, not drawn */
  .vx-skel::after{ animation:none !important; display:none; }
  .vx-spinner,.vx-btn.is-loading::after{ animation:vx-spin 1.6s linear infinite !important; }  /* progress must still read as progress */
  .vx-table-wrap.is-loading::before{ animation:none !important; inline-size:100%; translate:none; opacity:.5; }
  .vx-btn:hover{ translate:0 0; --vx-btn-d:var(--vx-btn-depth); }      /* no hover lift; the press still lands */
  .vx-card--link:hover{ translate:none; }
  .vx-sticker{ animation:none !important; }
}

/* ── 12. utilities (spec §4.36) ────────────────────────────────────────── */
.vx-divider{ border:0; border-block-start:1px solid var(--vx-line-soft); margin-block:16px; }
.vx-vh{ position:absolute !important; inline-size:1px; block-size:1px; overflow:hidden; clip-path:inset(50%); white-space:nowrap; }
/* Two SEPARATE rules on purpose: a browser without :dir() drops a whole
   selector list that contains it, which would take the [dir] fallback down too. */
[dir="rtl"] .vx-flip-rtl{ scale:-1 1; }
.vx-flip-rtl:dir(rtl){ scale:-1 1; }
[dir="ltr"] .vx-flip-rtl:dir(ltr){ scale:1 1; }                        /* an LTR island inside an RTL page stays unflipped */
.vx-latin{ font-family:"Open Runde", system-ui, sans-serif; }
[dir="rtl"] .vx-latin{ letter-spacing:0; }
.vx-num{ font-family:var(--vx-font-figures); font-variant-numeric:tabular-nums; }
:where(.vx-icon){ flex:none; stroke-width:1.5; vertical-align:-.125em; }   /* zero specificity: any recipe may resize */
:where(svg.vx-icon:not([width])){ inline-size:16px; block-size:16px; }

/* ── 13. playful layer (spec §8.2): V-facet sticker + hand-drawn arrow ─── */
/* Markup: main/_vx_sticker.html (vx_sticker(), vx_arrow()). */
.vx-sticker{ display:inline-block; inline-size:var(--vx-sticker-size, 72px); block-size:auto; flex:none;
  rotate:calc(6deg * var(--vx-dir)); filter:drop-shadow(2px 3px 0 rgb(0 0 0 / .85)); pointer-events:none; }
.vx-sticker .vx-sticker__cut{ fill:var(--vx-card); stroke:var(--vx-card); }
.vx-sticker--nudge{ animation:vx-nudge 600ms var(--vx-ease-standard) 1.2s 1 both; }
.vx-arrow{ display:inline-block; inline-size:var(--vx-arrow-size, 64px); block-size:auto; flex:none; color:var(--vx-blue-ink);
  scale:var(--vx-dir) 1; pointer-events:none; }

/* ── 14. shared partials owned by the foundation ───────────────────────── */
/* _theme_toggle.html: JS-only control (the handler lives in ui.js). The icon
   swap keys on the attribute the boot script always sets once JS runs, so the
   no-JS mirror is irrelevant here: without JS the button is not shown at all. */
html:not(.js) [data-lab-theme]{ display:none !important; }
.vx-theme-toggle .vx-theme-toggle__sun{ display:none; }
html[data-theme="dark"] .vx-theme-toggle .vx-theme-toggle__sun{ display:inline; }
html[data-theme="dark"] .vx-theme-toggle .vx-theme-toggle__moon{ display:none; }
.vx-theme-float{ position:fixed; z-index:var(--vx-z-sticky); inset-block-end:calc(12px + env(safe-area-inset-bottom)); inset-inline-end:12px;
  background:var(--vx-card); border-color:var(--vx-line); box-shadow:var(--vx-shadow-sheet); }

/* _lang_switcher.html (.vx-lang): a .vx-dropdown; these only add what the
   generic recipe does not know about. */
.vx-lang{ display:inline-block; }
.vx-lang > summary{ list-style:none; }
.vx-lang > summary::-webkit-details-marker{ display:none; }
.vx-lang .vx-lang__caret{ color:var(--vx-text-3); transition:rotate var(--vx-dur-fast) var(--vx-ease-standard); }
.vx-lang[open] .vx-lang__caret{ rotate:180deg; }
.vx-lang .vx-menu{ min-inline-size:12rem; }
.vx-lang .vx-lang__check{ margin-inline-start:auto; color:var(--vx-accent-ink); }

/* ui.js drawer plumbing (spec §5.4 item 10): the scrim it inserts before an
   open #vx-sidenav / #vx-navsheet, and the scroll lock it puts on <html>. */
.vx-scrim{ position:fixed; inset:0; z-index:calc(var(--vx-z-nav) - 1); background:var(--vx-scrim);
  animation:vx-fade-in var(--vx-dur-open) ease-out both; }
html.vx-scroll-lock, html.vx-scroll-lock body{ overflow:hidden; }
.vx-scene__sentinel{ pointer-events:none; }
