/* ════════════════════════════════════════════════════════════════════════
   GrammarMetric — styles.css
   One stylesheet for every marketing page. Read DESIGN_BRIEF.md first.

   How colour works here (the rule everything else serves):
   - Ink and paper carry the page. Sections are an ink, paper, pale or
     accent field; the field classes below set every colour a component
     needs (--bg, --fg, --fg-2, --line, --surface, --link, --focus), so
     components never name a hex and never ask which field they sit on.
   - <html data-accent="cyan|yellow|orange|magenta"> picks the page's ONE
     accent (cyan leads), used at full strength but only in the places
     DESIGN_BRIEF.md §3 lists: CTA, links, .hl headline words, one
     .field-accent band, one .card-accent, the tool-frame glow.
   - On ink the raw accent glows. On paper, text and focus rings use the
     darkened --accent-deep, which clears 4.7:1 on #f5f5f5 and 5.1:1 on
     white. tools/tokens-check.mjs re-measures every pairing.

   Motion: only transform and opacity animate. Reveals and scroll scenes
   exist only under `.js` AND prefers-reduced-motion: no-preference, so
   no-JS and reduced motion both get the finished, static page.
   ════════════════════════════════════════════════════════════════════════ */
/* Lexend, self-hosted (fonts/OFL.txt). One variable file per script covers
   every weight the site uses; latin-ext only downloads when a page needs it. */
@font-face {
  font-family: 'Lexend'; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(/fonts/lexend-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: 'Lexend'; font-style: normal; font-weight: 100 900; font-display: swap;
  src: url(/fonts/lexend-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;
}

/* ── 1. Tokens ─────────────────────────────────────────────────────────── */
:root {
  /* Brand palette: fixed, seven hexes. */
  --ink:     #050505;
  --paper:   #ffffff;
  --yellow:  #FFFC00;
  --cyan:    #00FFFF;
  --orange:  #FF3503;
  --magenta: #FF00FF;
  --error:   #ff5252;          /* semantic errors ONLY */

  /* Neutral ramp: straight sRGB mixes of ink into paper (percent = ink). */
  --n-4:   #f5f5f5;            /*  4%  pale field, cards on paper      */
  --n-8:   #ebebeb;            /*  8%  pressed / secondary surface      */
  --n-16:  #d7d7d7;            /* 16%  hairlines on paper               */
  --n-60:  #696969;            /* 60%  secondary text on paper  5.0:1+  */
  --n-40d: #9b9b9b;            /* paper 60% into ink: secondary text on ink 6.4:1+ */
  --n-16d: #2d2d2d;            /* paper 16% into ink: hairlines on ink  */
  --n-7d:  #171717;            /* paper  7% into ink: cards on ink      */
  --n-12d: #232323;            /* paper 12% into ink: pressed on ink    */

  /* Accents darkened with ink until they read as text on pale paper. */
  --yellow-deep:  #727003;
  --cyan-deep:    #037a7a;
  --orange-deep:  #d12c03;
  --magenta-deep: #c201c2;
  --error-deep:   #c23f3f;

  /* Type. Pages load Lexend 300/400/600 only: three weights, per the brief. */
  --font: 'Lexend', system-ui, -apple-system, 'Segoe UI', Roboto,
          'Noto Sans Thai', 'Leelawadee UI', 'PingFang SC', 'Noto Sans SC',
          'Microsoft YaHei', sans-serif;
  --w-light: 300;
  --w-regular: 400;
  --w-bold: 600;

  /* Fluid sizes: min at a 390px phone, max at a 1440px desktop. */
  --fs-hero:  clamp(3.5rem,    2.943rem + 2.286vw, 5rem);      /* 56 → 80 */
  --fs-title: clamp(2.5rem,    2.129rem + 1.524vw, 3.5rem);    /* 40 → 56 */
  --fs-card:  clamp(1.3125rem, 1.15rem  + 0.667vw, 1.75rem);   /* 21 → 28 */
  --fs-lead:  clamp(1.3125rem, 1.15rem  + 0.667vw, 1.75rem);   /* 21 → 28 */
  --fs-body:  1.0625rem;                                       /* 17 */
  --fs-small: 0.875rem;                                        /* 14 */
  --fs-fine:  0.75rem;                                         /* 12 */

  --lh-hero: 1.05;
  --lh-title: 1.08;
  --lh-lead: 1.3;
  --lh-body: 1.47;
  --lh-fine: 1.4;

  --tr-hero:  -0.016em;        /* → -0.02em at 1441px */
  --tr-title: -0.015em;        /* → -0.016em at 1441px */
  --tr-lead:  -0.005em;
  --tr-body:  -0.006em;
  --tr-fine:  0;
  --tr-nav:   0.01em;

  --measure: 65ch;

  /* Space: an 8pt rhythm. */
  --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px; --s-5: 24px;
  --s-6: 32px; --s-7: 48px; --s-8: 64px; --s-9: 80px; --s-10: 100px;

  --section-pad: 40px;         /* 64 at 735px, 100 at 1069px */
  --gutter: 22px;
  --col-gap: 16px;
  --content: 980px;
  --nav-h: 48px;

  /* Radii. */
  --r-control: 8px;
  --r-card: 18px;
  --r-pill: 980px;
  --r-panel: 30px;             /* 36 at 735px */

  /* Depth: used on paper fields only; ink needs none. */
  --shadow-float: 0 2px 6px rgba(5,5,5,.05), 0 24px 60px -24px rgba(5,5,5,.28);
  --shadow-lift:  0 2px 8px rgba(5,5,5,.06), 0 32px 72px -24px rgba(5,5,5,.34);

  /* Motion. */
  --ease-out: cubic-bezier(.22, .61, .36, 1);
  --dur-fast: 200ms;
  --dur-reveal: 600ms;
  --stagger: 60ms;
  --rise: 16px;
}

@media (min-width: 735px) {
  :root { --section-pad: 64px; --r-panel: 36px; --col-gap: 20px; }
}
@media (min-width: 1069px) {
  :root { --section-pad: 100px; --col-gap: 24px; }
}
@media (min-width: 1441px) {
  :root { --tr-hero: -0.02em; --tr-title: -0.016em; }
}

/* Negative tracking is a Latin optical correction; Thai and Chinese
   glyphs collide under it, and Thai stacks vowels and tone marks above
   and below the line, so it needs a taller line. */
:lang(th), :lang(zh) {
  --tr-hero: 0; --tr-title: 0; --tr-lead: 0; --tr-body: 0; --tr-nav: 0;
}
:lang(th) { --lh-hero: 1.25; --lh-title: 1.25; --lh-lead: 1.45; --lh-body: 1.7; }
:lang(zh) { --lh-hero: 1.2;  --lh-title: 1.2;  --lh-lead: 1.45; --lh-body: 1.65; }

/* ── 2. Accent: one per page ─────────────────────────────────────────── */
:root, [data-accent="cyan"] { --accent: var(--cyan);    --accent-deep: var(--cyan-deep); }
[data-accent="yellow"]      { --accent: var(--yellow);  --accent-deep: var(--yellow-deep); }
[data-accent="orange"]      { --accent: var(--orange);  --accent-deep: var(--orange-deep); }
[data-accent="magenta"]     { --accent: var(--magenta); --accent-deep: var(--magenta-deep); }

/* ── 3. Fields: every section declares one ───────────────────────────── */
.field-paper, .field-pale {
  --bg: var(--paper);
  --fg: var(--ink);
  --fg-2: var(--n-60);
  --line: var(--n-16);
  --surface: var(--n-4);
  --surface-2: var(--n-8);
  --link: var(--accent-deep);
  --focus: var(--accent-deep);
  --btn-edge: rgba(5,5,5,.16);
  --error-fg: var(--error-deep);
  --shadow: var(--shadow-float);
  color-scheme: light;
  background: var(--bg);
  color: var(--fg);
}
.field-pale {
  --bg: var(--n-4);
  --surface: var(--paper);
  --surface-2: var(--n-8);
}
/* body is an ink field: the translucent nav sits over it at the top of
   every page, and every page opens on an ink hero. */
.field-ink, body {
  --bg: var(--ink);
  --fg: var(--n-4);
  --fg-2: var(--n-40d);
  --line: var(--n-16d);
  --surface: var(--n-7d);
  --surface-2: var(--n-12d);
  --link: var(--accent);
  --focus: var(--accent);
  --btn-edge: transparent;
  --error-fg: var(--error);
  --shadow: none;
  color-scheme: dark;
  background: var(--bg);
  color: var(--fg);
}

/* The accent as a field: the page's graphic moment at full strength.
   All text is solid ink. No tint of ink clears 4.5:1 on orange, so there
   is no grey secondary text here; hierarchy comes from size and weight. */
.field-accent {
  --bg: var(--accent);
  --fg: var(--ink);
  --fg-2: var(--ink);
  --line: rgba(5,5,5,.22);
  --surface: rgba(5,5,5,.08);
  --surface-2: rgba(5,5,5,.14);
  --link: var(--ink);
  --focus: var(--ink);
  --btn-edge: transparent;
  --error-fg: var(--ink);
  --shadow: none;
  color-scheme: light;
  background: var(--bg);
  color: var(--fg);
}
/* On an accent field the primary button inverts: ink pill, accent label. */
.field-accent .btn-primary { background: var(--ink); color: var(--accent); }
.field-accent .btn-primary::after { background: var(--paper); }

/* ── 4. Base ─────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

/* font-family on <html>, not only body: the root element otherwise falls
   back to the UA serif, which leaks into anything sized in rem. */
html {
  font-family: var(--font);
  font-size: 100%;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-padding-top: calc(var(--nav-h) + 8px);
  background: var(--ink);        /* overscroll behind the ink nav */
}
body {
  margin: 0;
  font-size: var(--fs-body);
  font-weight: var(--w-regular);
  line-height: var(--lh-body);
  letter-spacing: var(--tr-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}
html.nav-locked { overflow: hidden; }

h1, h2, h3, h4, p, ul, ol, figure, blockquote, dl, dd { margin: 0; }
h1, h2, h3, h4 { font-weight: var(--w-bold); text-wrap: balance; }
p { text-wrap: pretty; }
b, strong { font-weight: var(--w-bold); }
img, svg, video, iframe { display: block; max-width: 100%; }
img { height: auto; }
button, input, select, textarea { font: inherit; letter-spacing: inherit; color: inherit; }
ul[role="list"], ol[role="list"] { list-style: none; padding: 0; }
[hidden] { display: none !important; }

/* Focus: restyled, never removed. The field decides the colour. */
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
}
:focus:not(:focus-visible) { outline: none; }

.skip-link {
  position: fixed; top: 8px; left: 8px; z-index: 1000;
  padding: 10px 18px; border-radius: var(--r-pill);
  background: var(--accent); color: var(--ink);
  font-size: var(--fs-small); text-decoration: none;
  transform: translateY(-200%);
}
.skip-link:focus-visible { transform: none; outline-color: var(--paper); }

.visually-hidden {
  position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ── 5. Type ─────────────────────────────────────────────────────────── */
.t-hero {
  font-size: var(--fs-hero); font-weight: var(--w-bold);
  line-height: var(--lh-hero); letter-spacing: var(--tr-hero);
}
.t-title {
  font-size: var(--fs-title); font-weight: var(--w-bold);
  line-height: var(--lh-title); letter-spacing: var(--tr-title);
}
.t-card {
  font-size: var(--fs-card); font-weight: var(--w-bold);
  line-height: 1.14; letter-spacing: var(--tr-lead);
}
.t-lead {
  font-size: var(--fs-lead); font-weight: var(--w-light);
  line-height: var(--lh-lead); letter-spacing: var(--tr-lead);
}
.t-body  { font-size: var(--fs-body); line-height: var(--lh-body); letter-spacing: var(--tr-body); }
.t-small { font-size: var(--fs-small); line-height: var(--lh-fine); letter-spacing: var(--tr-fine); }
.t-fine  { font-size: var(--fs-fine);  line-height: var(--lh-fine); letter-spacing: var(--tr-fine); }
.t-muted { color: var(--fg-2); }
.t-num   { font-variant-numeric: tabular-nums; }

/* The two-level H1: a small keyword line that carries the search terms,
   then the 2–5 word headline. Both live inside the one <h1>. */
.t-kicker {
  display: block;
  margin-bottom: var(--s-3);
  font-size: var(--fs-lead); font-weight: var(--w-regular);
  line-height: var(--lh-lead); letter-spacing: var(--tr-lead);
  color: var(--fg-2);
}

/* Highlight: the words a headline turns on. On ink the accent glows as
   text; on paper it becomes a highlighter stroke under ink text, so the
   colour stays at full strength and the text stays at 18:1+. */
.hl { color: var(--accent); }
.field-paper .hl, .field-pale .hl {
  color: inherit;
  background-image: linear-gradient(var(--accent), var(--accent));
  background-repeat: no-repeat;
  background-size: 100% .38em;
  background-position: 0 88%;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  padding-inline: .04em;
}
.field-accent .hl { color: inherit; background: none; }
.t-kicker.is-accent { color: var(--link); }

.prose { max-width: var(--measure); }
.prose > * + * { margin-top: 1em; }
.prose ul, .prose ol { padding-left: 1.25em; }
.prose li + li { margin-top: .4em; }

/* Inline links sit inside running text, where colour alone cannot mark
   them (the deep accents are as dark as the body text), so they keep an
   underline. Stand-alone "more" links have no underline and a chevron. */
a { color: var(--link); }
.prose a, a.link {
  color: var(--link);
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}
.link-more {
  display: inline-flex; align-items: center; gap: .3em;
  min-height: 44px;
  color: var(--link); text-decoration: none;
  font-size: var(--fs-body);
}
.link-more::after {
  content: ""; width: .42em; height: .42em;
  border-top: 1.5px solid currentColor; border-right: 1.5px solid currentColor;
  transform: rotate(45deg); margin-left: .05em;
}
.field-paper .link-more, .field-pale .link-more { color: var(--accent-deep); }

@media (hover: hover) {
  .prose a:hover, a.link:hover { text-decoration-thickness: 2px; }
  .link-more:hover { text-decoration: underline; text-underline-offset: 0.18em; }
}

/* ── 6. Layout ───────────────────────────────────────────────────────── */
.wrap {
  width: 100%;
  max-width: calc(var(--content) + 2 * var(--gutter));
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.section { padding-block: var(--section-pad); }
.section-head { max-width: 760px; margin-bottom: var(--s-7); }
.section-head.center, .center { text-align: center; margin-inline: auto; }
.section-head .t-lead { margin-top: var(--s-4); color: var(--fg); }
.section-action { margin-top: var(--s-7); }
.center .section-action, .section-action.center { display: flex; justify-content: center; }

.bleed { width: 100%; }

/* 1 column on phones, 6 on tablets, 12 on desktops. Children span with
   .span-t-* (≥735) and .span-d-* (≥1069); a phone is always full width. */
.grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--col-gap);
}
@media (min-width: 735px) {
  .grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .grid > * { grid-column: span 6; }
  .grid > .span-t-2 { grid-column: span 2; }
  .grid > .span-t-3 { grid-column: span 3; }
  .grid > .span-t-4 { grid-column: span 4; }
}
@media (min-width: 1069px) {
  .grid { grid-template-columns: repeat(12, minmax(0, 1fr)); }
  .grid > * { grid-column: span 12; }
  .grid > .span-d-3 { grid-column: span 3; }
  .grid > .span-d-4 { grid-column: span 4; }
  .grid > .span-d-5 { grid-column: span 5; }
  .grid > .span-d-6 { grid-column: span 6; }
  .grid > .span-d-7 { grid-column: span 7; }
  .grid > .span-d-8 { grid-column: span 8; }
}

.stack > * + * { margin-top: var(--s-4); }
.row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-4); }

/* ── 7. Global nav ───────────────────────────────────────────────────── */
.gnav {
  position: sticky; top: 0; z-index: 100;
  height: var(--nav-h);
  background: rgba(5,5,5,.8);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid rgba(245,245,245,.1);
  color: var(--n-4);
  --focus: var(--accent);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .gnav { background: var(--ink); }
}
.gnav-inner {
  display: flex; align-items: center; gap: var(--s-5);
  height: 100%;
}
.gnav-mark {
  flex: none;
  display: inline-flex; align-items: center; min-height: 44px;
  color: var(--n-4); text-decoration: none;
  font-size: var(--fs-body); font-weight: var(--w-bold); letter-spacing: -0.01em;
}
.gnav-links {
  display: none;
  flex: 1; margin: 0; padding: 0; list-style: none;
  justify-content: space-between; align-items: center;
}
.gnav-links a, .gnav-lang a {
  display: inline-flex; align-items: center; min-height: 44px;
  color: rgba(245,245,245,.8); text-decoration: none;
  font-size: var(--fs-small); letter-spacing: var(--tr-nav);
}
.gnav-links a[aria-current="page"] { color: var(--n-4); }
.gnav-end { display: flex; align-items: center; gap: var(--s-4); margin-left: auto; }
.gnav-lang { display: none; gap: var(--s-3); }
.gnav-lang [aria-current="true"] { color: var(--n-4); }

.gnav-cta {
  position: relative;
  display: inline-flex; align-items: center;
  padding: 4px 14px; min-height: 28px;
  border-radius: var(--r-pill);
  background: var(--accent); color: var(--ink);
  font-size: var(--fs-small); text-decoration: none; white-space: nowrap;
}
.gnav-cta::before { content: ""; position: absolute; inset: -8px -4px; } /* 44px hit area */

.gnav-burger, .gnav-burger-link {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; margin-right: -10px;
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--n-4); text-decoration: none;
}
.gnav-burger-link { width: auto; margin-right: 0; font-size: var(--fs-small); }
.gnav-burger .bars { position: relative; width: 18px; height: 12px; }
.gnav-burger .bars::before, .gnav-burger .bars::after {
  content: ""; position: absolute; left: 0; right: 0; height: 1.5px;
  background: currentColor; border-radius: 1px;
  transition: transform 300ms var(--ease-out);
}
.gnav-burger .bars::before { top: 2px; }
.gnav-burger .bars::after  { bottom: 2px; }
.gnav-burger[aria-expanded="true"] .bars::before { transform: translateY(3.25px) rotate(45deg); }
.gnav-burger[aria-expanded="true"] .bars::after  { transform: translateY(-3.25px) rotate(-45deg); }

@media (hover: hover) {
  .gnav-links a:hover, .gnav-lang a:hover, .gnav-mark:hover { color: var(--paper); }
}

@media (min-width: 1069px) {
  .gnav-links { display: flex; margin-inline: var(--s-6); }
  .gnav-lang { display: flex; }
  .gnav-burger, .gnav-burger-link { display: none; }
  .gnav-end { margin-left: 0; }
}

/* Mobile overlay: full-screen ink under the bar; links stagger in. */
.gnav-overlay {
  position: fixed; inset: var(--nav-h) 0 0 0; z-index: 99;
  overflow-y: auto; overscroll-behavior: contain;
  padding-block: var(--s-5) var(--s-8);
}
.gnav-overlay-list { list-style: none; margin: 0; padding: 0; }
.gnav-overlay-list a {
  display: block; padding: 6px 0;
  color: var(--n-4); text-decoration: none;
  font-size: 1.75rem; font-weight: var(--w-bold);
  line-height: 1.2; letter-spacing: var(--tr-lead);
}
.gnav-overlay-list a[aria-current="page"] { color: var(--n-40d); }
.gnav-overlay-sub {
  list-style: none; margin: var(--s-6) 0 0; padding: var(--s-5) 0 0;
  border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-5);
}
.gnav-overlay-sub a {
  display: inline-flex; align-items: center; min-height: 44px;
  color: var(--fg-2); text-decoration: none; font-size: var(--fs-body);
}
.gnav-overlay-sub .gnav-overlay-cta { color: var(--accent); }

@media (prefers-reduced-motion: no-preference) {
  .gnav-overlay { opacity: 0; transition: opacity 300ms var(--ease-out); }
  .gnav-overlay.is-shown { opacity: 1; }
  .gnav-overlay [data-stagger-i] {
    opacity: 0; transform: translateY(-8px);
    transition: opacity 400ms var(--ease-out), transform 400ms var(--ease-out);
    transition-delay: calc(60ms + var(--i, 0) * 40ms);
  }
  .gnav-overlay.is-shown [data-stagger-i] { opacity: 1; transform: none; }
}
@media (min-width: 1069px) { .gnav-overlay { display: none !important; } }

/* ── 8. Buttons ──────────────────────────────────────────────────────── */
.btn {
  position: relative; isolation: isolate;
  display: inline-flex; align-items: center; justify-content: center; gap: .4em;
  min-height: 44px; padding: 11px 22px;
  border: 0; border-radius: var(--r-pill);
  font-size: var(--fs-body); font-weight: var(--w-regular);
  line-height: 1.2; letter-spacing: var(--tr-body);
  text-decoration: none; text-align: center;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--dur-fast) var(--ease-out);
}
.btn::after {                   /* hover/press tint, animated by opacity only */
  content: ""; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit; background: var(--ink);
  opacity: 0; transition: opacity var(--dur-fast) var(--ease-out);
}
.btn-lg { min-height: 52px; padding: 14px 30px; font-size: 1.125rem; }

.btn-primary {
  background: var(--accent); color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--btn-edge);
}
.btn-quiet {
  background: transparent; color: var(--fg);
  box-shadow: inset 0 0 0 1px var(--line);
}
.field-ink .btn-quiet { box-shadow: inset 0 0 0 1px rgba(245,245,245,.32); }
.field-ink .btn-quiet::after { background: var(--paper); }

@media (hover: hover) {
  .btn:hover::after, .btn.is-hover::after { opacity: .08; }
}
.btn.is-hover::after { opacity: .08; }            /* styleguide state demo */
.btn:active, .btn.is-active { transform: scale(.97); }
.btn:active::after, .btn.is-active::after { opacity: .14; }
.btn.is-focus { outline: 2px solid var(--focus); outline-offset: 3px; }
.btn:disabled, .btn[aria-disabled="true"] {
  cursor: not-allowed; opacity: .4; transform: none;
}
.btn:disabled::after, .btn[aria-disabled="true"]::after { opacity: 0; }

/* ── 9. Cards, panels, numbers ───────────────────────────────────────── */
.card {
  position: relative;
  display: flex; flex-direction: column;
  padding: var(--s-6);
  border-radius: var(--r-card);
  background: var(--surface);
  color: var(--fg);
}
@media (min-width: 1069px) {
  .card { padding: 40px; }
  .grid > .card.span-d-3 { padding: 28px; }   /* four across: keep a readable measure */
}
.card > * + * { margin-top: var(--s-3); }
.card .t-card + p { margin-top: var(--s-3); color: var(--fg-2); }
.card-foot { margin-top: auto !important; padding-top: var(--s-5); }
.card-float { box-shadow: var(--shadow); }

/* A card filled with the accent, ink text throughout (see .field-accent).
   --ring keeps its focus outline visible against the field around it. */
.card.card-accent {
  background: var(--accent);
  --fg: var(--ink); --fg-2: var(--ink); --link: var(--ink); --focus: var(--ink);
  --line: rgba(5,5,5,.22);
  color: var(--ink);
}
.field-ink .card-accent { --ring: var(--accent); }
.field-paper .card-accent, .field-pale .card-accent { --ring: var(--accent-deep); }

/* A whole card that is one link: the title's link stretches over it. */
.card-link a.card-cover { color: inherit; text-decoration: none; }
.card-link a.card-cover::before { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.card-link:focus-within { outline: 2px solid var(--ring, var(--focus)); outline-offset: 3px; }
.card-link a.card-cover:focus-visible { outline: none; }
@media (prefers-reduced-motion: no-preference) and (hover: hover) {
  .card-link { transition: transform 400ms var(--ease-out); }
  .card-link::after {
    content: ""; position: absolute; inset: 0; z-index: -1;
    border-radius: inherit; box-shadow: var(--shadow-lift);
    opacity: 0; transition: opacity 400ms var(--ease-out);
  }
  .card-link:hover { transform: translateY(-4px); }
  .field-paper .card-link:hover::after, .field-pale .card-link:hover::after { opacity: 1; }
}

.panel {
  position: relative; overflow: hidden;
  border-radius: var(--r-panel);
  background: var(--surface);
}

/* Screenshot frame: a rounded rectangle and nothing else. No browser
   chrome, no shadow on ink, no angle. */
.shot {
  overflow: hidden;
  border-radius: var(--r-card);
  background: var(--surface);
  box-shadow: 0 0 0 1px var(--line);
}
.shot img { width: 100%; }

/* Numbers instead of adjectives. */
.stats {
  display: grid; gap: var(--s-6) var(--col-gap);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin: 0; padding: 0; list-style: none;
}
@media (min-width: 1069px) { .stats { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.stat-num {
  display: block;
  font-size: var(--fs-title); font-weight: var(--w-bold);
  line-height: 1; letter-spacing: var(--tr-title);
  font-variant-numeric: tabular-nums;
}
.stat-label {
  display: block; margin-top: var(--s-2);
  font-size: var(--fs-small); line-height: var(--lh-fine); color: var(--fg-2);
}

/* Price */
.price-card { text-align: left; }
.price-name { font-size: var(--fs-small); color: var(--fg-2); }
.price-amount {
  font-size: var(--fs-title); font-weight: var(--w-bold);
  line-height: 1; letter-spacing: var(--tr-title);
  font-variant-numeric: tabular-nums;
}
.price-unit { font-size: var(--fs-body); color: var(--fg-2); }
.price-list { margin: var(--s-5) 0 0; padding: 0; list-style: none; border-top: 1px solid var(--line); }
.price-list li { padding: var(--s-3) 0; border-bottom: 1px solid var(--line); font-size: var(--fs-small); }

/* ── 10. The device frame (live tool embeds) ─────────────────────────── */
.device {
  --ratio: 390 / 720;
  position: relative;
  width: 100%; max-width: 420px; margin-inline: auto;
  padding: 10px;
  border-radius: 44px;
  background: var(--n-7d);
  box-shadow: inset 0 0 0 1px var(--n-16d);
}
@media (min-width: 735px) {
  .device { --ratio: 4 / 3; max-width: 720px; border-radius: 40px; padding: 12px; }
}
@media (min-width: 1069px) {
  .device { --ratio: 16 / 10; max-width: 880px; }
}
.field-paper .device, .field-pale .device { box-shadow: var(--shadow-float), inset 0 0 0 1px var(--n-16d); }
/* On ink the frame can glow in the accent: a static shadow, not a blob. */
.device.is-glow {
  box-shadow:
    inset 0 0 0 1px var(--n-16d),
    0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent),
    0 40px 140px -40px color-mix(in srgb, var(--accent) 55%, transparent);
}
.device-screen {
  position: relative; overflow: hidden;
  aspect-ratio: var(--ratio);
  border-radius: 34px;
  background: var(--ink);
}
@media (min-width: 735px) { .device-screen { border-radius: 28px; } }
.device-screen iframe {
  position: absolute; inset: 0; width: 100%; height: 100%; border: 0;
}
.device-poster {
  position: absolute; inset: 0; z-index: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s-4); padding: var(--s-6);
  text-align: center; text-decoration: none;
  background: var(--ink); color: var(--n-4);
  --focus: var(--accent);
}
.device-poster img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.device-poster > :not(img) { position: relative; }
.device-play {
  display: inline-flex; align-items: center; justify-content: center;
  width: 64px; height: 64px; border-radius: 50%;
  background: var(--accent); color: var(--ink);
}
.device-play svg { width: 22px; height: 22px; margin-left: 3px; }
.device-status { font-size: var(--fs-small); color: var(--n-40d); }
.device.is-loaded .device-poster { opacity: 0; pointer-events: none; }
/* A screenshot poster: the image fills the screen under a scrim, so the play
   button and caption stay readable over busy game art. */
.device-poster > picture { position: absolute; inset: 0; }
.device-poster.has-shot::before {
  content: ""; position: absolute; inset: 0; z-index: 1; background: rgba(5,5,5,.5);
}
.device-poster.has-shot > :not(picture) { z-index: 2; }
/* The hero frame sits under the headline and the one action. */
.hero .hero-device { margin-top: var(--s-8); }
.hero .section-note { margin-top: var(--s-4); }
@media (prefers-reduced-motion: no-preference) {
  .device-poster { transition: opacity 500ms var(--ease-out); }
}

/* ── 11. Forms ───────────────────────────────────────────────────────── */
.form { display: grid; gap: var(--s-5); }
.field { display: grid; gap: var(--s-2); }
.field-label { font-size: var(--fs-small); color: var(--fg); }
.field-help  { font-size: var(--fs-small); color: var(--fg-2); }
.input, .select select, .textarea {
  width: 100%; min-height: 48px;
  padding: 12px 14px;
  border: 1px solid var(--line); border-radius: var(--r-control);
  background: var(--surface); color: var(--fg);
  font-size: var(--fs-body); line-height: 1.3;
  -webkit-appearance: none; appearance: none;
}
.field-pale .input, .field-pale .select select, .field-pale .textarea { background: var(--paper); }
.textarea { min-height: 128px; resize: vertical; }
.input::placeholder, .textarea::placeholder { color: var(--fg-2); opacity: 1; }
.input:focus-visible, .select select:focus-visible, .textarea:focus-visible {
  outline: 2px solid var(--focus); outline-offset: 1px; border-color: transparent;
}
.input:disabled, .select select:disabled, .textarea:disabled { opacity: .5; cursor: not-allowed; }

.select { position: relative; }
.select select { padding-right: 40px; cursor: pointer; }
.select::after {
  content: ""; position: absolute; right: 18px; top: 50%;
  width: 7px; height: 7px; margin-top: -6px; pointer-events: none;
  border-right: 1.5px solid var(--fg-2); border-bottom: 1.5px solid var(--fg-2);
  transform: rotate(45deg);
}

/* Errors: colour plus a ✕ and words, never colour alone. */
.field.is-invalid .input, .field.is-invalid .select select, .field.is-invalid .textarea {
  border-color: var(--error-fg); box-shadow: inset 0 0 0 1px var(--error-fg);
}
.field-error {
  display: flex; gap: .4em; align-items: baseline;
  font-size: var(--fs-small); color: var(--error-fg);
}
.field-error::before { content: "✕"; font-size: .85em; }

/* Segmented control: a radio group dressed as one native control. */
.segmented {
  justify-self: start;
  display: inline-grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  padding: 3px; margin: 0; border: 0;
  border-radius: calc(var(--r-control) + 3px);
  background: var(--surface-2);
}
.segmented legend { padding: 0; margin-bottom: var(--s-2); font-size: var(--fs-small); }
.segmented label { position: relative; }
.segmented input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.segmented span {
  display: flex; align-items: center; justify-content: center;
  min-height: 40px; padding: 8px 16px;
  border-radius: var(--r-control);
  font-size: var(--fs-small); white-space: nowrap; color: var(--fg-2);
}
.segmented input:checked + span {
  background: var(--bg); color: var(--fg);
  box-shadow: 0 1px 3px rgba(5,5,5,.12), 0 0 0 1px var(--line);
}
.field-ink .segmented input:checked + span { background: var(--n-16d); box-shadow: none; }
.segmented input:focus-visible + span { outline: 2px solid var(--focus); outline-offset: 1px; }
.segmented-wrap { display: grid; gap: var(--s-2); }

.check { display: flex; gap: var(--s-3); align-items: flex-start; font-size: var(--fs-body); cursor: pointer; }
.check input {
  flex: none; width: 20px; height: 20px; margin: 2px 0 0;
  accent-color: var(--accent-deep);
}
.field-ink .check input { accent-color: var(--accent); }

.form-fallback {                 /* what a no-JS visitor sees instead */
  padding: var(--s-5); border-radius: var(--r-card);
  background: var(--surface); font-size: var(--fs-body);
}

/* ── 12. FAQ ─────────────────────────────────────────────────────────── */
.faq { border-top: 1px solid var(--line); max-width: 760px; }
.faq details { border-bottom: 1px solid var(--line); }
.faq summary {
  display: flex; justify-content: space-between; align-items: center; gap: var(--s-5);
  min-height: 44px; padding: var(--s-5) 0;
  font-size: var(--fs-card); font-weight: var(--w-regular);
  line-height: 1.25; letter-spacing: var(--tr-lead);
  cursor: pointer; list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ""; flex: none; width: 14px; height: 14px;
  background:
    linear-gradient(currentColor, currentColor) center / 14px 1.5px no-repeat,
    linear-gradient(currentColor, currentColor) center / 1.5px 14px no-repeat;
  color: var(--fg-2);
}
.faq details[open] summary::after { transform: rotate(45deg); }
@media (prefers-reduced-motion: no-preference) {
  .faq summary::after { transition: transform 300ms var(--ease-out); }
}
.faq details > p {
  max-width: var(--measure);
  padding-bottom: var(--s-5);
  color: var(--fg-2);
}

/* ── 13. Footer ──────────────────────────────────────────────────────── */
.gfoot {
  padding-block: var(--s-7) var(--s-6);
  font-size: var(--fs-fine); line-height: var(--lh-fine);
  color: var(--fg-2);
}
.gfoot a { color: var(--fg-2); text-decoration: none; }
.gfoot-cols {
  display: grid; gap: var(--s-6) var(--col-gap);
  grid-template-columns: repeat(2, minmax(0, 1fr));
  padding-bottom: var(--s-6);
  border-bottom: 1px solid var(--line);
}
@media (min-width: 735px)  { .gfoot-cols { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1069px) { .gfoot-cols { grid-template-columns: repeat(5, minmax(0, 1fr)); } }
.gfoot h2 {
  margin-bottom: var(--s-2);
  font-size: var(--fs-fine); font-weight: var(--w-bold); color: var(--fg);
}
.gfoot ul { list-style: none; margin: 0; padding: 0; }
.gfoot li a, .gfoot address a {
  display: inline-block; padding-block: 5px;   /* 24px+ target, 12px type */
}
.gfoot address { font-style: normal; }
.gfoot-base {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--s-3) var(--s-5);
  padding-top: var(--s-5);
}
.gfoot-base ul { display: flex; flex-wrap: wrap; gap: var(--s-4); }
@media (hover: hover) { .gfoot a:hover { color: var(--fg); text-decoration: underline; } }

/* ── 14. Motion ──────────────────────────────────────────────────────── */
/* Reveal once, fade up 16px. Hidden only when JS runs AND motion is
   welcome; app.js adds .is-in and never removes it. */
@media (prefers-reduced-motion: no-preference) {
  .js .reveal {
    opacity: 0; transform: translateY(var(--rise));
    transition:
      opacity var(--dur-reveal) var(--ease-out),
      transform var(--dur-reveal) var(--ease-out);
    transition-delay: calc(var(--i, 0) * var(--stagger));
  }
  .js .reveal.is-in { opacity: 1; transform: none; }
  /* The first screen is never hidden: it is what the visitor waits for (and
     what Largest Contentful Paint measures), so a fade there only delays it. */
  .js .hero .reveal, .js .book-head .reveal { opacity: 1; transform: none; transition: none; }
}

/* The page's one cinematic moment. A tall .scene pins .scene-pin with
   position: sticky; app.js writes scroll progress 0→1 into --p. Each
   .scene-layer maps its own window [--from, --to] of that progress to
   opacity and a small rise/scale. Nothing else moves. */
.scene { --p: 0; position: relative; }
@media (prefers-reduced-motion: no-preference) {
  .js .scene { height: var(--scene-h, 240vh); }
  .js .scene-pin {
    position: sticky; top: var(--nav-h);
    height: calc(100vh - var(--nav-h));
    height: calc(100svh - var(--nav-h));
    display: flex; flex-direction: column; justify-content: center;
    overflow: hidden;
  }
  .js .scene-layer {
    --lp: clamp(0, (var(--p) - var(--from, 0)) / (var(--to, 1) - var(--from, 0)), 1);
    opacity: var(--lp);
    transform:
      translate3d(0, calc((1 - var(--lp)) * var(--layer-rise, 40px)), 0)
      scale(calc(1 - (1 - var(--lp)) * var(--layer-shrink, 0)));
  }
}

/* ── 15. Page furniture (homepage, stage 3) ──────────────────────────── */
/* Hero: the headline owns most of the first screen, then one action. */
.hero {
  display: flex; align-items: center;
  min-height: calc(84vh - var(--nav-h));
  min-height: calc(84svh - var(--nav-h));
  padding-block: var(--s-9);
}
.hero .t-lead { margin-top: var(--s-5); max-width: 34ch; }
.hero-action { margin-top: var(--s-6); }

.section-note {
  max-width: var(--measure); margin-top: var(--s-5);
  font-size: var(--fs-small); color: var(--fg-2);
}
.section-note.center { margin-inline: auto; text-align: center; }

/* Short claims, each marked with a tick rather than a bullet. */
.ticks { display: grid; gap: var(--s-5); margin: 0; padding: 0; list-style: none; align-content: start; }
.ticks li { display: flex; gap: var(--s-3); }
.ticks li::before { content: "✓"; flex: none; font-weight: var(--w-bold); }
@media (min-width: 1069px) { .ticks { padding-top: var(--s-3); } }

/* A scene that pins on desktop only. Below 1069px its layers would not fit
   one pinned screen, so it falls back to an ordinary static section. */
@media (max-width: 1068px) {
  .js .scene--wide { height: auto; }
  .js .scene--wide .scene-pin { position: static; height: auto; display: block; overflow: visible; }
  .js .scene--wide .scene-layer { opacity: 1; transform: none; }
}

/* ── 16. Long-form pages (stage 5) ───────────────────────────────────── */
/* A row of in-page links under a long page's hero (/faq/). Plain text links,
   44px tall, never chips: they are navigation, not the page's one action. */
.jump ul {
  display: flex; flex-wrap: wrap; gap: 0 var(--s-5);
  margin: var(--s-7) 0 0; padding: 0; list-style: none;
}
.jump a {
  display: inline-flex; align-items: center; min-height: 44px;
  font-size: var(--fs-small); color: var(--fg-2); text-decoration: none;
}
@media (hover: hover) { .jump a:hover { color: var(--fg); text-decoration: underline; } }
/* Price cards above a question list. */
.price-grid { margin-bottom: var(--s-7); }
/* A project's copy and its screenshot row (/special-projects/). */
.project-copy, .shots { margin-top: var(--s-7); }
.shots figure { margin: 0; }
.shots figcaption { margin-top: var(--s-3); font-size: var(--fs-small); color: var(--fg-2); }
/* About: the portrait beside the bio, and the contact cards. */
.portrait { margin: 0; }
.portrait img { width: 100%; max-width: 360px; border-radius: var(--r-card); }
.teacher .prose { margin-top: var(--s-5); }
.teacher-ticks { margin-top: var(--s-6); }
.contact .t-card { overflow-wrap: anywhere; }
.copy-id {
  padding: 0; border: 0; background: none; cursor: pointer;
  font: inherit; color: var(--link); text-decoration: underline; text-underline-offset: .18em;
}
.copy-id:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
.copy-note { margin-left: var(--s-2); font-size: var(--fs-small); color: var(--fg-2); }
.nowrap { white-space: nowrap; }
/* Book: a short head, then the form beside what to expect. */
.book-head { padding-block: var(--s-8) var(--s-7); }
.book-head .t-lead { margin-top: var(--s-5); max-width: 40ch; }
.form-pair { row-gap: var(--s-5); }
.choices-wrap { margin: 0; padding: 0; border: 0; min-width: 0; }
.choices-wrap legend { margin-bottom: var(--s-3); }
.choices { display: grid; gap: var(--s-3) var(--s-5); }
@media (min-width: 735px) { .choices { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form-status { margin-top: var(--s-6); }
.form-status:focus { outline: none; }
.book-aside .section-note { margin-top: var(--s-6); }
@media (min-width: 1069px) { .book-aside { padding-left: var(--s-7); } }
/* Sign-up: the PromptPay QR, kept on white and large enough to scan from a screen. */
.pay-qr { display: block; width: 100%; max-width: 280px; height: auto; border-radius: var(--r-control); background: #fff; }
