/* ==========================================================================
   PHYSICS SIM Lab — site stylesheet

   Tokens are lifted verbatim from the published design system. Do not adjust
   colors, add border radius or add shadows: --radius-none and --shadow-none
   are deliberate. Vertical rhythm is transcribed from the 1440 and 390
   artboards; the values are unrounded on purpose (9, 13, 22, 26).

   One approved exception to the colour rule, 2026-09-01: --ink-500 was darkened
   three steps to clear WCAG AA. The reason is recorded at the token itself.
   ========================================================================== */

/* --- fonts ---------------------------------------------------------------
   Newsreader — display serif. Variable 300-700. */
@font-face{font-family:'Newsreader';font-style:normal;font-weight:300 700;font-display:swap;src:url("../fonts/newsreader-latin.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:'Newsreader';font-style:normal;font-weight:300 700;font-display:swap;src:url("../fonts/newsreader-latin-ext.woff2") format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Newsreader';font-style:normal;font-weight:300 700;font-display:swap;src:url("../fonts/newsreader-vietnamese.woff2") format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}
@font-face{font-family:'Newsreader';font-style:italic;font-weight:300 600;font-display:swap;src:url("../fonts/newsreader-italic-latin.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:'Newsreader';font-style:italic;font-weight:300 600;font-display:swap;src:url("../fonts/newsreader-italic-latin-ext.woff2") format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Newsreader';font-style:italic;font-weight:300 600;font-display:swap;src:url("../fonts/newsreader-italic-vietnamese.woff2") format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}

/* Public Sans — text sans. Variable 100-900, used at 400/600. */
@font-face{font-family:'Public Sans';font-style:normal;font-weight:100 900;font-display:swap;src:url("../fonts/public-sans-latin.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:'Public Sans';font-style:normal;font-weight:100 900;font-display:swap;src:url("../fonts/public-sans-latin-ext.woff2") format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Public Sans';font-style:normal;font-weight:100 900;font-display:swap;src:url("../fonts/public-sans-vietnamese.woff2") format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB}

/* Spline Sans Mono — spec labels and numeric values. Variable 300-700, used at 400/500. */
@font-face{font-family:'Spline Sans Mono';font-style:normal;font-weight:300 700;font-display:swap;src:url("../fonts/spline-sans-mono-latin.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:'Spline Sans Mono';font-style:normal;font-weight:300 700;font-display:swap;src:url("../fonts/spline-sans-mono-latin-ext.woff2") format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}

/* --- Hangul ---------------------------------------------------------------
   Subset to the 363 syllables this site actually sets, plus ASCII. Latin still
   resolves from the three brand faces, which sit ahead of these in every stack.

   'PSL Sans KR' is a subset of Pretendard (SIL OFL 1.1, Kil Hyung-jin). It is
   renamed because Pretendard carries a Reserved Font Name and a subset counts
   as a Modified Version under the licence. Noto Serif KR (SIL OFL 1.1) declares
   no Reserved Font Name, so it keeps its own. Both licences ship next to the
   files in assets/fonts/ — keep them there. */
@font-face{font-family:'PSL Sans KR';font-style:normal;font-weight:100 900;font-display:swap;src:url("../fonts/pretendard-ko.woff2") format('woff2')}
@font-face{font-family:'Noto Serif KR';font-style:normal;font-weight:300;font-display:swap;src:url("../fonts/noto-serif-kr-300.woff2") format('woff2')}
@font-face{font-family:'Noto Serif KR';font-style:normal;font-weight:600;font-display:swap;src:url("../fonts/noto-serif-kr-600.woff2") format('woff2')}

/* --- tokens ------------------------------------------------------------- */
:root{
  /* paper ground */
  --paper-100:#FFFFFF;
  --paper-200:#FBFAF7;
  --paper-300:#F2F0EA;
  --paper-400:#CFCEC9;

  /* rules and hairlines */
  --rule-100:#D8D4CA;
  --rule-200:#D2CEC4;
  --rule-300:#C6C2B8;
  --rule-400:#B9B8B2;

  /* ink */
  --ink-900:#16181C;
  --ink-700:#52565E;
  /* #6A6E77 in the handoff measured 4.48:1 on paper-300 — 0.02 short of AA
     for the 47 label, spec and caption elements that use it. Three steps
     darker on the same hue clears it at 4.69:1 and is indistinguishable. */
  --ink-500:#676B74;

  /* brand */
  --navy-900:#1F2C55;
  --signal-600:#D2382A;
  --well-900:#101E1B;

  /* semantic aliases */
  --bg-page:var(--paper-300);
  --surface-card:var(--paper-100);
  --surface-raised:var(--paper-200);
  --surface-well:var(--well-900);
  --text-primary:var(--ink-900);
  --text-body:var(--ink-700);
  --text-secondary:var(--ink-500);
  --text-inverse:var(--paper-300);
  --text-accent:var(--navy-900);
  --text-signal:var(--signal-600);
  --text-on-well:rgba(242,240,234,0.66);
  --accent:var(--navy-900);
  --accent-hover:var(--ink-900);
  --signal:var(--signal-600);
  --border-hairline:var(--rule-100);
  --border-rule:var(--rule-200);
  --border-control:var(--rule-300);
  --border-grid:var(--rule-400);
  --link:var(--navy-900);
  --link-hover:var(--ink-900);

  /* typography */
  /* Hangul sits behind the brand faces in every stack, so English is unchanged.
     The display serif pairs with a Korean serif; text and mono share the sans. */
  --hangul-serif:'Noto Serif KR','Apple SD Gothic Neo','Malgun Gothic';
  --hangul-sans:'PSL Sans KR','Apple SD Gothic Neo','Malgun Gothic';

  --font-display:'Newsreader',var(--hangul-serif),Georgia,'Times New Roman',serif;
  --font-text:'Public Sans',var(--hangul-sans),system-ui,-apple-system,sans-serif;
  --font-mono:'Spline Sans Mono',ui-monospace,'SF Mono',Menlo,var(--hangul-sans),monospace;

  --weight-display-light:300;
  --weight-display-semi:600;
  --weight-text-regular:400;
  --weight-text-semi:600;
  --weight-mono-regular:400;
  --weight-mono-medium:500;

  --ls-display:-0.015em;
  --size-h1:2.75rem;        --lh-h1:1.1;       --ls-h1:-0.015em;
  --size-h2:1.625rem;       --lh-h2:1.25;      --ls-h2:0;
  --size-h3:1.1875rem;      --lh-h3:1.35;
  --size-body:1.0625rem;    --lh-body:1.65;
  --size-ui:0.9375rem;      --lh-ui:1.45;
  --size-caption:0.8125rem; --lh-caption:1.5;
  --size-mono-value:0.8125rem;
  --size-mono-meta:0.75rem;
  --size-mono-label:0.6875rem; --ls-mono-label:0.1em;
  --ls-mono-value:0.08em;
  --ls-mono-eyebrow:0.14em;

  --type-h1:var(--weight-display-semi) var(--size-h1)/var(--lh-h1) var(--font-display);
  --type-h2:var(--weight-display-semi) var(--size-h2)/var(--lh-h2) var(--font-display);
  --type-h3:var(--weight-text-semi) var(--size-h3)/var(--lh-h3) var(--font-text);
  --type-body:var(--weight-text-regular) var(--size-body)/var(--lh-body) var(--font-text);
  --type-ui:var(--weight-text-semi) var(--size-ui)/var(--lh-ui) var(--font-text);
  --type-caption:var(--weight-text-regular) var(--size-caption)/var(--lh-caption) var(--font-text);

  --measure-body:62ch;
  --measure-lede:74ch;

  /* spacing */
  --space-1:4px;  --space-2:6px;  --space-3:8px;  --space-4:9px;
  --space-5:10px; --space-6:12px; --space-7:13px; --space-8:14px;
  --space-9:18px; --space-10:22px; --space-11:26px; --space-12:36px;
  --space-13:40px; --space-14:52px; --space-15:56px; --space-16:72px;

  --pad-card:13px 14px 15px;
  --pad-cell:16px 18px;
  --pad-control:13px 22px;
  --gap-hairline:1px;
  --gap-card-grid:18px;
  --gap-column:56px;
  --ratio-render:16 / 10;

  /* borders */
  --border-card:1px solid var(--border-hairline);
  --border-section:1px solid var(--border-rule);
  --border-button:1px solid var(--border-control);
  --border-table:1px solid var(--border-grid);
  --radius-none:0;
  --radius-dot:50%;
  --dot-size:7px;
  --shadow-none:none;

  /* motion */
  --dur-fast:120ms;
  --ease-standard:cubic-bezier(0.2,0,0.2,1);
  --transition-control:background-color var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard),color var(--dur-fast) var(--ease-standard);

  /* layout */
  --page-max:1440px;
  --gutter:64px;
  --header-h:80px;
}

/* --- base --------------------------------------------------------------- */
*,*::before,*::after{box-sizing:border-box}

html{
  -webkit-text-size-adjust:100%;
  scroll-behavior:smooth;
  scroll-padding-top:var(--header-h);
}

body{
  margin:0;
  background:var(--bg-page);
  color:var(--text-primary);
  font:var(--type-body);
  text-wrap:pretty;
  -webkit-font-smoothing:antialiased;
}

img,video,svg{max-width:100%}
h1,h2,h3,p{margin:0}
a{color:var(--link)}
a:hover{color:var(--link-hover)}

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

.skip-link{
  position:absolute;
  left:var(--space-3);
  top:-100px;
  z-index:100;
  background:var(--paper-100);
  color:var(--text-accent);
  font:var(--type-ui);
  padding:var(--space-5) var(--space-8);
  border:var(--border-button);
}
.skip-link:focus{top:var(--space-3)}

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

/* The page frame. Caps at the 1440 canvas width so wider monitors match it. */
.page{
  max-width:var(--page-max);
  margin-inline:auto;
  background:var(--paper-300);
}

/* ==========================================================================
   Components — the five design-system components, as HTML + CSS
   ========================================================================== */

/* Button — square-cornered control. Primary = navy fill, secondary = hairline.
   The prototype tracked hover in React state; :hover does the same job. */
.btn{
  font-family:var(--font-text);
  font-weight:var(--weight-text-semi);
  font-size:var(--size-ui);
  line-height:var(--lh-ui);
  padding:var(--pad-control);
  border-radius:var(--radius-none);
  text-decoration:none;
  display:inline-block;
  cursor:pointer;
  transition:var(--transition-control);
}
.btn--primary{color:var(--text-inverse);background:var(--accent);border:1px solid var(--accent)}
.btn--primary:hover{color:var(--text-inverse);background:var(--accent-hover);border-color:var(--accent-hover)}
.btn--secondary{color:var(--accent);background:transparent;border:var(--border-button)}
.btn--secondary:hover{color:var(--accent);border-color:var(--accent)}

/* Eyebrow — mono, uppercase, wide-tracked, with the single signal dot. */
/* 11px marker row — the widest tracking on the page, reserved for the two
   things that mark a position: a section eyebrow and a block numeral. */
.eyebrow{
  font-family:var(--font-mono);
  font-weight:var(--weight-mono-medium);
  font-size:var(--size-mono-label);
  letter-spacing:var(--ls-mono-eyebrow);
  text-transform:uppercase;
  color:var(--text-secondary);
}

/* The block numeral is the one marker that sits ON the same line as its
   title rather than above it, so it is set to the title's size. At label
   size beside 19px text it reads as a mistake, not as a marker. Mono is
   kept — every figure on the page is mono — and the tracking comes back to
   --ls-mono-value, since eyebrow tracking is far too wide at this size. */
.udm-block__index{
  font-family:var(--font-mono);
  font-weight:var(--weight-mono-medium);
  font-size:var(--size-h3);
  line-height:var(--lh-h3);
  letter-spacing:var(--ls-mono-value);
  color:var(--text-secondary);
}
.eyebrow{display:flex;align-items:center;gap:var(--space-6)}
.eyebrow::before{
  content:"";
  width:var(--dot-size);
  height:var(--dot-size);
  background:var(--signal);
  border-radius:var(--radius-dot);
  flex:0 0 auto;
}

/* DisplayStatement — Newsreader 300 at display size, in accent navy. */
/* One size. The 64px base and its --h1 override were a two-step scale that
   only ever resolved to the smaller step. */
.display-statement{
  font-family:var(--font-display);
  font-weight:var(--weight-display-light);
  font-size:var(--size-h1);
  line-height:var(--lh-h1);
  letter-spacing:var(--ls-display);
  color:var(--text-accent);
}

/* Coming-soon notice. The dialog element carries no box of its own so that a
   click anywhere on it is a backdrop click; the panel is what you see. */
.notice{
  padding:0;
  border:0;
  background:transparent;
  max-width:min(460px, calc(100vw - 2 * var(--gutter)));
}
.notice::backdrop{background:rgba(16,24,28,0.55)}
.notice__panel{
  padding:var(--space-13);
  background:var(--surface-card);
  border:var(--border-section);
  display:flex;
  flex-direction:column;
  gap:var(--space-9);
}
.notice__actions{display:flex;gap:var(--space-8);flex-wrap:wrap;margin-top:var(--space-3)}

/* RenderWell — the dark plate a render sits in. */
.well{
  position:relative;
  aspect-ratio:var(--ratio-render);
  background:var(--surface-well);
  overflow:hidden;
  margin:0;
}
.well > img,
.well > video{width:100%;height:100%;object-fit:cover;display:block}
/* Artwork that keeps its own proportions: no 16:10 frame, no cover-cropping.
   Used where the supplied image must appear exactly as delivered. */
.well--natural{aspect-ratio:auto;background:none;overflow:visible}
.well--natural > img{height:auto;object-fit:contain}

/* A plot rather than footage: contained, so cover-cropping cannot cut the axes
   off. The SVG paints its own canvas in --well-900, the same colour the well
   uses, so the letterbox above and below it is invisible. */
.well--plot > img{object-fit:contain}

/* Ratio overrides for footage that is not 16:10. */
.well--square{aspect-ratio:1}
/* The four ground-truth channels are cropped from a single 1374px source sheet.
   315x286 is their native size, so this ratio is the one that shows them 1:1 —
   any other would scale pixels the source does not have. */
.well--gt{aspect-ratio:315 / 286}
/* Matches the height of a two-up square pair in a column of the same width:
   each cell is (W-3)/2 tall and the strip adds its two 1px rules back. */
.well--pair-height{aspect-ratio:2 / 1}

.well__label{
  position:absolute;
  top:var(--space-3);
  left:var(--space-5);
  color:var(--text-on-well);
}

/* --- shared type roles --------------------------------------------------- */
.h1{font:var(--type-h1);letter-spacing:var(--ls-h1);color:var(--text-primary)}
/* One 19px semibold role, three names. */
.h3,.notice__title,.udm-block__title,.udm-block__sub{font:var(--type-h3);color:var(--text-primary)}
.body,.notice__body{font:var(--type-body);color:var(--text-body)}
.caption{font:var(--type-caption);color:var(--text-body)}
.lede-emphasis{
  font-family:var(--font-text);
  font-weight:var(--weight-text-semi);
  font-size:1.0625rem;
  line-height:1.65;
  color:var(--text-primary);
}
.card__lead{
  font-family:var(--font-text);
  font-weight:var(--weight-text-semi);
  font-size:1rem;
  line-height:1.4;
  color:var(--text-primary);
}
/* 11px label row. Everything set in caps at label size tracks at
   --ls-mono-label; only the marker row above goes wider. */
.mono-label,.lang-switch__on,.lang-switch__off,.well__label,.hand__more{
  font-family:var(--font-mono);
  font-size:var(--size-mono-label);
  letter-spacing:var(--ls-mono-label);
  text-transform:uppercase;
}
.mono-label,.lang-switch__on,.lang-switch__off{font-weight:var(--weight-mono-medium)}
.mono-label{color:var(--text-secondary)}
/* 13px numeric row — registry values, spec counts, table cells. One size and
   one tracking; only the colour separates the four uses. */
.mono-value,.mono-meta,.hand__act,.hand-table__act{
  font-family:var(--font-mono);
  font-size:var(--size-mono-value);
  letter-spacing:var(--ls-mono-value);
}
.mono-value{color:var(--text-primary)}
.mono-meta{color:var(--text-secondary)}

/* --- layout primitives --------------------------------------------------- */
.stack{display:flex;flex-direction:column}
.section{padding-inline:var(--gutter)}
.section--rule{border-top:var(--border-section)}
.measure{max-width:var(--measure-body)}
.measure-lede{max-width:var(--measure-lede)}

.tag-row{display:flex;gap:var(--space-3);flex-wrap:wrap}
/* 12px quiet row — tags and finger counts. The literal 0.75rem these three
   carried was --size-mono-meta, which nothing else was using. */
.tag,.hand__fgr,.hand-table__fgr{
  font-family:var(--font-mono);
  font-size:var(--size-mono-meta);
  letter-spacing:var(--ls-mono-value);
  color:var(--text-secondary);
}
.tag{border:1px solid var(--rule-300);padding:5px 10px}

.card{background:var(--surface-card);border:var(--border-card)}
.card__body{padding:var(--pad-card);display:flex;flex-direction:column;gap:var(--space-5)}

/* Hairline stack — 1px gaps drawn by the rule color showing through. */
.rule-stack{
  display:flex;
  flex-direction:column;
  gap:var(--gap-hairline);
  background:var(--border-grid);
  border:var(--border-table);
}

/* Strip — a hairline row of cells. A grid at 1440, a scroll-snap filmstrip
   at 390. One set of markup serves both; no duplicated DOM. */
.strip{
  display:grid;
  gap:var(--gap-hairline);
  background:var(--border-grid);
  border:var(--border-table);
}
.strip--2{grid-template-columns:repeat(2,1fr)}
.strip--3{grid-template-columns:repeat(3,1fr)}
.strip--5{grid-template-columns:repeat(5,1fr)}

/* Band — full-width raised statement band. The Dual-Core couplet lives here
   and must keep it: 64px, display face, its own ground. */
.band{
  background:var(--surface-raised);
  border-top:var(--border-section);
  border-bottom:var(--border-section);
  padding:var(--gutter);
  display:flex;
  flex-direction:column;
  gap:var(--space-10);
}

/* ==========================================================================
   Header — one fixed element. Transparent over the hero, paper ground past
   it. Appearance is its only motion: no slide, no fade, no transition.
   ========================================================================== */
.site-header{
  position:fixed;
  top:0;left:0;right:0;
  z-index:50;
  display:flex;
  align-items:center;
  padding:var(--space-11) var(--gutter);
  max-width:var(--page-max);
  margin-inline:auto;
}
.site-header.is-stuck{
  background:var(--paper-300);
  border-bottom:var(--border-section);
}
.site-header__logo{height:28px;width:auto;display:block}
.site-nav{display:flex;gap:var(--space-11);margin-left:auto;align-items:center}
/* One nav item is a button — it opens the coming-soon dialog rather than going
   anywhere — so it is stripped back to look exactly like its neighbours. */
.site-nav a,
.site-nav__btn{
  font-family:var(--font-text);
  font-size:var(--size-ui);
  color:var(--ink-900);
  text-decoration:none;
}
.site-nav__btn{
  background:none;
  border:0;
  padding:0;
  cursor:pointer;
  line-height:inherit;
}
.site-nav a:hover,
.site-nav__btn:hover{text-decoration:underline}

/* The language toggle occupies the 30px slot the canvas reserved after
   Contact. It is a plain link to the other locale, so it works without JS. */
/* Both languages are always shown. The one you are reading is set in ink and
   is not a link; the other is faint until you point at it. A single toggle
   labelled with the language you are NOT reading is the ambiguous case this
   replaces — it reads as a label of the current page just as easily. */
.lang-switch{
  display:flex;
  align-items:center;
  gap:5px;
  margin-left:var(--space-11);
}
.lang-switch__on{color:var(--ink-900)}
.lang-switch__sep{
  font-family:var(--font-mono);
  font-size:var(--size-mono-label);
  color:var(--rule-400);
}
/* Faint, but still a control someone has to find: --text-secondary is the
   level every other secondary label on the page sits at. A rule-grey here
   measured 1.74:1 against paper, which is not legible text. */
.lang-switch__off{
  color:var(--text-secondary);
  text-decoration:none;
  transition:var(--transition-control);
}
.lang-switch__off:hover,
.lang-switch__off:focus-visible{color:var(--ink-900)}

.nav-toggle{display:none}

/* ==========================================================================
   Hero
   ========================================================================== */
.hero{position:relative;height:810px;overflow:hidden;background:var(--paper-300)}
.hero__video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
/* Cream scrim at 0.60 — measured at 5.6:1 minimum against ink in the darkest
   headline-zone regions. 0.72 flattens the footage, 0.55 drops to 4.8:1. */
.hero__scrim{position:absolute;inset:0;background:rgba(242,240,234,0.60)}
.hero__ramp{
  position:absolute;
  left:0;right:0;bottom:0;
  height:140px;
  background:linear-gradient(to bottom,rgba(242,240,234,0),#F2F0EA);
}
.hero__content{
  position:absolute;
  left:var(--gutter);
  top:0;bottom:0;
  display:flex;
  flex-direction:column;
  justify-content:center;
  gap:var(--space-11);
  max-width:760px;
}
/* Real h1 text, never baked into an image. Title Case is intentional. */
/* The one display line set semibold rather than light: over video it has to
   hold on its own, and weight buys that more cheaply than size — hence 3.5rem
   where the rest of the display scale would put 4. */
.hero__title{
  font-family:var(--font-display);
  font-weight:var(--weight-display-semi);
  font-size:3.5rem;
  line-height:1.0;
  letter-spacing:var(--ls-display);
  color:var(--text-primary);
}
.hero__actions{display:flex;gap:var(--space-8);margin-top:var(--space-3)}

/* ==========================================================================
   4.1 The bottleneck
   ========================================================================== */
.s-bottleneck{padding-top:88px}
.claim{display:grid;gap:var(--gap-column);align-items:center}
.claim--01{grid-template-columns:1fr minmax(300px,420px);padding-top:64px}
.claim--02{grid-template-columns:minmax(300px,420px) 1fr;padding-top:44px}
/* 03's art column was wider in the canvas, back when the three claims also
   escalated in type size. With the sizes levelled the odd width just read as
   a misalignment, so all three art columns now share one width. */
.claim--03{grid-template-columns:1fr minmax(300px,420px);padding-top:44px}
.claim__text{display:flex;flex-direction:column;gap:var(--space-6)}
.claim--01 .claim__text{padding-left:120px}
.claim--02 .claim__text{padding-right:60px}
.claim--03 .claim__text{padding-left:40px}
/* The three claims share one size. The canvas escalated 28 → 36 → 44px so the
   argument got louder down the page; that was dropped on request because the
   steps read as inconsistent rather than deliberate. 01's size is the keeper. */
.claim__line{
  font-family:var(--font-display);
  font-weight:var(--weight-display-light);
  font-size:1.75rem;
  line-height:1.2;
  letter-spacing:-0.01em;
  color:var(--text-primary);
}

.band{margin-top:88px}
/* Matches .band exactly: the pair should read as one recurring device. */
.band--dual{margin-top:88px}

/* ==========================================================================
   4.2 The pipeline
   ========================================================================== */
.s-pipeline{padding-top:64px}
.s-pipeline__grid{padding-top:36px}
.s-pipeline__panel{padding-top:26px}
.pipeline__step{background:var(--surface-card);display:flex;flex-direction:column}
.pipeline__figure{height:128px;background:var(--well-900);margin:0}
.pipeline__figure img{width:100%;height:100%;object-fit:contain;display:block}
.pipeline__body{padding:var(--pad-cell) 18px;display:flex;flex-direction:column;gap:var(--space-3)}
.pipeline__name{
  font-family:var(--font-text);
  font-weight:var(--weight-text-semi);
  font-size:var(--size-ui);
  color:var(--text-primary);
}
.pipeline__note{font:var(--type-caption);color:var(--text-secondary)}

.panel{
  background:var(--surface-card);
  border-top:1px solid var(--rule-100);
  border-bottom:1px solid var(--rule-100);
  padding:var(--space-11) 30px;
  display:flex;
  flex-direction:column;
  gap:var(--space-5);
}

/* ==========================================================================
   Dual-core detail
   ========================================================================== */
.s-engines{padding-top:52px}
/* Both columns stretch to the taller one and the footage is pushed to the
   bottom of each, so the two frames start and end on the same line even
   though the paragraphs above them run to different depths. */
.engines{display:grid;grid-template-columns:1fr 1fr;gap:var(--gap-column);align-items:stretch}
/* No padding here: aspect-ratio resolves against the border box, so padding
   would shrink the frame and break the height match. The stack gap spaces it. */
.s-engines__media{margin-top:auto}
.s-multiply{padding-top:44px}
.s-multiply__strip{padding-top:10px}

/* ==========================================================================
   UDM
   ========================================================================== */
.s-udm{margin-top:64px;padding-top:44px}
.s-udm__census{border-top:var(--border-section);padding-top:var(--space-8)}
.s-udm__figure{
  width:720px;
  max-width:100%;
  background:var(--well-900);
  margin:var(--space-3) 0 0;
}
/* height:auto matters — without it the img's height attribute is taken as a
   presentational height and the screenshot renders squashed. */
.s-udm__figure img{width:100%;height:auto;display:block}
.s-udm__head{max-width:none;margin-top:var(--space-2)}

/* Blocks 01 and 02 — platforms and hands. They are the evidence for the
   universality claim above, not sections of their own, so they sit inside
   s-udm under numbered heads that line up with the page gutter. */
.udm-block{padding-top:var(--space-16)}
.udm-block__head{
  display:flex;
  align-items:baseline;
  gap:var(--space-8);
  padding-bottom:var(--space-9);
}
.udm-block__title,

/* Sits directly under the block title, so it matches it rather than competing
   with the section headline above. */
.udm-block__sub{max-width:none}
/* A ninth hand is coming; the empty cell holds its place rather than letting
   the grid rule show through as a grey block. */
/* The ninth cell. It is not a gap being hidden — it says the library is still
   being added to, so it is centred in the cell rather than sitting on the
   baseline the eight spec blocks share. */
.hand--empty{
  background:var(--surface-card);
  display:flex;
  align-items:center;
  justify-content:center;
  padding:var(--pad-card);
  /* Eight hands plus this cell is nine, and nine into five leaves one slot
     where the grid rule would show through as a bare grey block. Spanning the
     last two closes the row: three hands and one wide note. */
  grid-column:span 2;
}
.hand__more{line-height:1.7;text-align:center;color:var(--text-secondary)}

/* ==========================================================================
   4.3 The data
   ========================================================================== */
.s-data{margin-top:88px;padding-block:88px 72px;background:var(--paper-300)}
.s-data > .section:first-child{padding-bottom:36px}
.s-data__tags{padding-top:22px}
/* Two paragraphs since 2026-08-31, and the reset leaves <p> with no margin, so
   the block sets its own rhythm — the same 22px the standard section uses. */
.s-data__note{padding-top:30px;display:flex;flex-direction:column;gap:var(--space-10)}
.s-data__gt{padding-top:36px}

/* ==========================================================================
   4.4 Hand library
   ========================================================================== */
.udm-block .stack{padding-bottom:var(--space-12)}
/* Every hand well is the same dark teal, so the light grid rule cuts five pale
   lines straight across the thumbnail band. Dark rules disappear into the wells
   and read as ordinary card dividers where they cross the cream bodies below.
   Scoped to this block: the other strips sit on lighter media. */
.s-hands__strip .strip{background:var(--well-900);border-color:var(--well-900)}
.hand{background:var(--surface-card)}
.hand__body{padding:var(--pad-card);display:flex;flex-direction:column;gap:var(--space-2)}
.hand__head{display:flex;justify-content:space-between;align-items:baseline;gap:var(--space-6)}
/* Card names, hands and platforms alike. */
.hand__name,.platform__name{
  font-family:var(--font-text);
  font-weight:var(--weight-text-semi);
  font-size:1rem;
  color:var(--text-primary);
}
.hand__act{color:var(--navy-900);white-space:nowrap}

/* The eight-row spec table is a mobile affordance: at 390 only one tile is
   visible at a time, so the census has to live somewhere scannable. */
.s-hands__table{display:none}
.hand-table__row{
  background:var(--surface-raised);
  display:grid;
  grid-template-columns:1fr 64px 48px;
  gap:var(--space-5);
  padding:11px var(--space-8);
  align-items:baseline;
}
.hand-table__row--head{background:var(--surface-card);padding:var(--space-5) var(--space-8)}
.hand-table__row--head .mono-label:not(:first-child){text-align:right}
/* 14px was the one size off the scale; this is --size-ui like every other
   compact text role. */
.hand-table__name{
  font-family:var(--font-text);
  font-weight:var(--weight-text-semi);
  font-size:var(--size-ui);
  color:var(--text-primary);
}
.hand-table__act{color:var(--navy-900);text-align:right}
.hand-table__fgr{text-align:right}
.s-hands__census{padding-top:22px}
/* The platform list outgrew three columns, so it scrolls sideways at every
   width, gutter-aligned with the block heads above it. */
.platform-row{
  display:flex;
  gap:var(--gap-card-grid);
  padding-inline:var(--gutter);
  overflow-x:auto;
  scroll-snap-type:x mandatory;
  scroll-padding-inline:var(--gutter);
  -webkit-overflow-scrolling:touch;
}
/* Five thumbnails across the column; a sixth scrolls in from the right. */
.platform-row > .platform{
  flex:none;
  width:calc((100% - 4 * var(--gap-card-grid)) / 5);
  scroll-snap-align:start;
}
.platform__name{padding:var(--pad-card)}

/* ==========================================================================
   4.5 The rest of the stack
   ========================================================================== */
.s-stack{padding-block:72px 64px}
.s-stack > .section:first-child{padding-bottom:30px}
/* stretch, not start: the three cards carry unequal amounts of copy, and the
   row reads as a set only when they close on one line. The shortest card pads
   out with whitespace under its last paragraph. */
.stack-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--gap-card-grid);align-items:stretch}
/* Both placeholders in the stack row are now filled with supplied footage: the
   simulators card on 2026-08-31, the assets card the same day. .well-empty is
   gone with them — restore it from git if a card is ever emptied again. */

/* ==========================================================================
   4.6 – 4.9 and the footer
   ========================================================================== */
.s-licence,.s-standard,.s-team{padding-block:72px 64px}
.s-licence__tags{border-top:var(--border-section);padding-top:var(--space-9)}
.s-contact{padding-block:72px 88px}
.contact__email{
  font-family:var(--font-mono);
  font-weight:var(--weight-mono-medium);
  font-size:2rem;
  letter-spacing:0.02em;
  color:var(--text-primary);
  margin-top:var(--space-9);
}

.site-footer{
  background:var(--surface-raised);
  border-top:var(--border-section);
  padding:34px var(--gutter) var(--space-13);
  display:flex;
  justify-content:space-between;
  gap:var(--space-13);
  align-items:flex-start;
}
/* 20px read as a watermark next to the registry type. At 48 the wordmark is
   legible, which is the only job it has down here. */
.site-footer__logo{height:48px;width:auto;display:block}
.site-footer__grid{
  display:grid;
  grid-template-columns:auto minmax(220px,420px);
  gap:var(--space-4) var(--space-11);
  align-items:baseline;
}
.site-footer__grid .mono-value{text-align:right}

/* ==========================================================================
   Mobile — the 390 artboard. Grids stack, three- and four-up rows become
   scroll-snap filmstrips, and the hand grid gains its spec table.
   ========================================================================== */
@media (max-width:1023px){
  :root{
    --gutter:22px;
    --header-h:60px;
  }

  .hero{height:100svh;min-height:700px}
  .hero__title{font-size:2.25rem;line-height:1.05}
  .hero__content{left:var(--gutter);right:var(--gutter);gap:var(--space-9)}
  .hero__actions{flex-direction:column;align-items:flex-start;gap:var(--space-6);margin-top:var(--space-2)}

  /* nav collapses behind MENU; 44px hit targets */
  .site-header{padding:var(--space-9) var(--gutter)}
  .site-header__logo{height:22px}
  .site-nav{
    display:none;
    position:absolute;
    top:100%;left:0;right:0;
    flex-direction:column;
    align-items:stretch;
    gap:0;
    background:var(--paper-300);
    border-top:var(--border-section);
    border-bottom:var(--border-section);
  }
  .site-nav.is-open{display:flex}
  .site-nav a,
  .site-nav__btn{
    padding:var(--space-8) var(--gutter);
    min-height:44px;
    display:flex;
    align-items:center;
    justify-content:flex-start;
    border-bottom:1px solid var(--rule-100);
  }
  .site-nav a:last-of-type{border-bottom:0}
  /* The toggle stays in the bar rather than hiding inside the drawer. */
  /* The row is 44px; stretch rather than centre so the link itself is the
     tap target, not an 18px sliver inside it. */
  .lang-switch{
    margin-left:auto;
    min-height:44px;
    align-items:stretch;
    padding-inline:var(--space-3);
  }
  .lang-switch__on,
  .lang-switch__sep,
  .lang-switch__off{
    display:flex;
    align-items:center;
    padding-inline:var(--space-2);
  }
  .nav-toggle{
    display:block;
    margin-left:var(--space-6);
    background:none;
    border:0;
    padding:var(--space-3) 0;
    min-height:44px;
    font-family:var(--font-mono);
    font-weight:var(--weight-mono-medium);
    font-size:var(--size-mono-label);
    letter-spacing:var(--ls-mono-label);
    color:var(--ink-900);
    text-transform:uppercase;
    cursor:pointer;
  }

  .h1{font:var(--type-h2);letter-spacing:var(--ls-h2)}

  .band{margin-top:52px;padding:44px var(--gutter);gap:var(--space-9)}
  .band--dual{margin-top:52px}

  /* claims stack, art below the line, type steps down one rung */
  .s-bottleneck{padding-top:52px}
  .claim{grid-template-columns:1fr;gap:var(--space-6)}
  .claim--01{padding-top:40px}
  .claim--02,.claim--03{padding-top:34px}
  .claim__text{padding:0 !important}
  /* the art always follows the line on mobile, whichever side it sat on */
  .claim--02 .well{order:2}
  .claim__line{font-size:1.375rem;line-height:1.25;letter-spacing:0}

  /* pipeline: five columns become five rows, diagram under the label */
  .s-pipeline{padding-top:44px}
  .s-pipeline__grid{padding-top:26px}
  .s-pipeline__panel{padding-top:22px}
  .strip--5{grid-template-columns:1fr}
  .pipeline__step{padding:var(--space-8)}
  .pipeline__body{padding:0;gap:7px}
  .pipeline__figure{height:auto;order:2;margin-top:5px}
  .pipeline__figure img{height:auto}
  .panel{padding:var(--space-9);gap:var(--space-4)}

  .s-engines{padding-top:36px}
  .engines{grid-template-columns:1fr;gap:var(--space-11)}
  .s-multiply{padding-top:36px}
  .s-multiply__strip{padding-top:16px}

  .s-udm{margin-top:44px;padding-top:30px}
  .s-udm__census{font-size:0.75rem;line-height:1.6;padding-top:var(--space-6)}
  .s-udm__figure{width:auto}

  .s-data{margin-top:52px;padding-block:44px 0}
  .s-data > .section:first-child{padding-bottom:22px}
  .s-data__tags{padding-top:16px}
  .s-data__note,.s-data__gt{padding-top:22px}
  .s-data__gt{padding-bottom:52px}
  /* Five channels do not fold into a grid at this width, so they swipe — the
     same treatment .strip--3 gets above. Scoped, because .strip--5 also carries
     the platform row, which stacks instead. */
  .s-data__gt .strip{
    display:flex;
    overflow-x:auto;
    scroll-snap-type:x mandatory;
    -webkit-overflow-scrolling:touch;
  }
  .s-data__gt .strip > *{flex:none;scroll-snap-align:start;width:240px}

  .udm-block{padding-top:44px}
  .udm-block__head{padding-bottom:var(--space-8)}
  .udm-block .stack{padding-bottom:var(--space-11)}
  .hand__fgr{display:none}
  .hand__body{padding:11px var(--space-8) var(--space-7)}
  /* It used to be hidden here because it was a blank spacer. Now it carries the
     "more in progress" line, so it rides the end of the swipe instead — the
     span-2 is a grid property and simply does not apply in this flex row. */
  .hand--empty{width:330px}
  .s-hands__table{display:block;padding-top:20px}
  .s-hands__census{padding-top:18px}
  .s-hands__census .mono-meta{font-size:0.75rem;line-height:1.6}
  /* filmstrips run full-bleed, outside the gutter */
  .s-hands__strip{padding-inline:0}
  .platform-row{padding-inline:0}
  .platform .well{aspect-ratio:var(--ratio-render)}

  .s-stack{padding-block:52px 44px}
  .s-stack > .section:first-child{padding-bottom:22px}
  .stack-grid{grid-template-columns:1fr;gap:var(--space-9)}

  .s-licence,.s-standard,.s-team{padding-block:52px 44px}
  .s-licence .stack,.s-standard .stack{gap:var(--space-9)}
  .s-licence__tags{padding-top:var(--space-8)}
  .s-contact{padding-block:52px 60px}
  .contact__email{font-size:1.375rem;margin-top:var(--space-5);word-break:break-all}

  .site-footer{flex-direction:column;padding:var(--space-11) var(--gutter) 32px;gap:var(--space-9)}
  .site-footer__logo{height:38px}
  .site-footer__grid{grid-template-columns:auto 1fr;gap:var(--space-3) var(--space-8)}
  .site-footer__grid .mono-label{font-size:0.625rem}
  .site-footer__grid .mono-value{font-size:0.75rem;text-align:left;line-height:1.5}

  .tag{padding:4px 8px;font-size:0.6875rem}
  .tag-row{gap:var(--space-2)}

  /* --- filmstrips ------------------------------------------------------ */
  /* The hand grid is five-up on desktop but keeps the sideways swipe here, so
     it is named explicitly — .strip--5 stacks, which is right for the pipeline
     but would leave eight tall cards to scroll past. */
  .strip--3,
  .s-hands__strip .strip{
    display:flex;
    overflow-x:auto;
    scroll-snap-type:x mandatory;
    border-inline:0;
    -webkit-overflow-scrolling:touch;
  }
  .strip--3 > *,
  .s-hands__strip .strip > *{flex:none;scroll-snap-align:start;width:330px}
  .strip--randomize > *{width:280px}

  /* filmstrips run full-bleed, outside the 22px gutter */
  .s-data__strip,.s-multiply__strip{padding-inline:0}

  .platform-row{
    gap:var(--gap-hairline);
    background:var(--border-grid);
    border-top:var(--border-table);
    border-bottom:var(--border-table);
    scroll-padding-inline:0;
  }
  .platform-row > .platform{width:300px;border:0}
  .platform__name{padding:11px var(--space-8) var(--space-7);font-size:var(--size-ui)}
}

/* ==========================================================================
   Korean — /ko/. The nav and the hero stay English and carry lang="en", so
   :lang(ko) reaches only the copy that is actually translated.

   Two things Korean needs that Latin does not: it must not break mid-word
   (keep-all), and it wants more leading at the same size.
   ========================================================================== */
.h1:lang(ko),.h3:lang(ko),.body:lang(ko),.caption:lang(ko),.lede-emphasis:lang(ko),
.claim__line:lang(ko),.card__lead:lang(ko),.display-statement:lang(ko),
.pipeline__name:lang(ko),.pipeline__note:lang(ko),.tag:lang(ko),.eyebrow:lang(ko){
  word-break:keep-all;
}

.display-statement:lang(ko){line-height:1.3;letter-spacing:-0.01em}
.h1:lang(ko),.claim__line:lang(ko){line-height:1.35;letter-spacing:-0.01em}

/* Korean headlines are a noun phrase plus a hyphenated subtitle, and they read
   badly when a two-word tail drops to its own line. Korean sets wider than
   English at the same character count, so the measures the English copy was
   fitted to now force a wrap. Headings get the full section column instead.

   The caps are lifted on the heading and on its stack rather than by widening
   the heading itself — a heading given more width than its container would
   push out of a grid cell and scroll the page. The !important clears the inline
   max-width the template carries on four headings; none of this reaches the
   English page. */
.h1:lang(ko){max-width:none !important;text-wrap:balance}
.stack.measure-lede:lang(ko){max-width:none}
.stack.measure-lede:lang(ko) .lede-emphasis{max-width:var(--measure-body)}
.claim__line:lang(ko){text-wrap:balance}
.card__lead:lang(ko){line-height:1.5}
.body:lang(ko),.caption:lang(ko),.lede-emphasis:lang(ko){line-height:1.8}
.pipeline__name:lang(ko),.pipeline__note:lang(ko){line-height:1.55}

/* Measures are deliberately not overridden. `ch` resolves against Public
   Sans either way, so the column keeps the exact width the canvas drew —
   which lands at roughly 34 Korean characters, well inside the comfortable
   range. Changing it here would only pull the Korean page off the design. */

/* ==========================================================================
   Reduced motion — poster frames only, hero and hand grid alike.
   This is a requirement, not a nicety. site.js pauses every video; this
   block removes the remaining transitions.
   ========================================================================== */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:0.01ms !important;
    animation-iteration-count:1 !important;
    transition-duration:0.01ms !important;
    scroll-behavior:auto !important;
  }
}
