/* GENERATED by `node scripts/i18n.mjs build` from fonts.css, chrome.css, lang.css, paper.css, page.css. Edit those; this file is overwritten. */

/* ---- fonts.css ---- */
/* Self-hosted copies of the three faces the paper system uses (Newsreader,
   Geist, Geist Mono), subset per script exactly as Google Fonts serves them.
   Served from our own CloudFront instead of fonts.googleapis.com: one fewer
   third-party connection, no render-blocking cross-origin stylesheet, and no
   font request leaving the EU for a GDPR-sensitive audience (SEO audit,
   2026-09-13). Regenerate by re-fetching the Google Fonts CSS with a modern
   browser UA and mapping each url() to assets/fonts/. */
@font-face{font-family:'Geist';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/geist-400-cyrillic-ext.woff2) format('woff2');unicode-range:U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F}
@font-face{font-family:'Geist';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/geist-400-cyrillic.woff2) format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116}
@font-face{font-family:'Geist';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/geist-400-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:'Geist';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/geist-400-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:'Geist';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/geist-400-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:'Geist';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/geist-400-cyrillic-ext.woff2) format('woff2');unicode-range:U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F}
@font-face{font-family:'Geist';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/geist-400-cyrillic.woff2) format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116}
@font-face{font-family:'Geist';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/geist-400-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:'Geist';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/geist-400-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:'Geist';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/geist-400-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:'Geist';font-style:normal;font-weight:600;font-display:swap;src:url(/assets/fonts/geist-400-cyrillic-ext.woff2) format('woff2');unicode-range:U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F}
@font-face{font-family:'Geist';font-style:normal;font-weight:600;font-display:swap;src:url(/assets/fonts/geist-400-cyrillic.woff2) format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116}
@font-face{font-family:'Geist';font-style:normal;font-weight:600;font-display:swap;src:url(/assets/fonts/geist-400-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:'Geist';font-style:normal;font-weight:600;font-display:swap;src:url(/assets/fonts/geist-400-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:'Geist';font-style:normal;font-weight:600;font-display:swap;src:url(/assets/fonts/geist-400-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:'Geist Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/geist-mono-400-cyrillic-ext.woff2) format('woff2');unicode-range:U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F}
@font-face{font-family:'Geist Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/geist-mono-400-cyrillic.woff2) format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116}
@font-face{font-family:'Geist Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/geist-mono-400-symbols2.woff2) format('woff2');unicode-range:U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F}
@font-face{font-family:'Geist Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/geist-mono-400-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:'Geist Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/geist-mono-400-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:'Geist Mono';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/geist-mono-400-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:'Geist Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/geist-mono-400-cyrillic-ext.woff2) format('woff2');unicode-range:U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F}
@font-face{font-family:'Geist Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/geist-mono-400-cyrillic.woff2) format('woff2');unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116}
@font-face{font-family:'Geist Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/geist-mono-400-symbols2.woff2) format('woff2');unicode-range:U+2000-2001, U+2004-2008, U+200A, U+23B8-23BD, U+2500-259F}
@font-face{font-family:'Geist Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/geist-mono-400-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:'Geist Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/geist-mono-400-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:'Geist Mono';font-style:normal;font-weight:500;font-display:swap;src:url(/assets/fonts/geist-mono-400-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;font-display:swap;src:url(/assets/fonts/newsreader-300-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:normal;font-weight:300;font-display:swap;src:url(/assets/fonts/newsreader-300-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;font-display:swap;src:url(/assets/fonts/newsreader-300-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:400;font-display:swap;src:url(/assets/fonts/newsreader-300-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:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/newsreader-300-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:400;font-display:swap;src:url(/assets/fonts/newsreader-300-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}

/* ---- chrome.css ---- */
/* Site chrome — the one header and the one footer.
 *
 * These rules used to be retyped inside every page's inline <style>, in two
 * incompatible versions: a 80px grid bar on the fourteen pages that carry their
 * own stylesheet, and a 64px flex bar in styles.css on the six that don't. Same
 * class names, different heights, layouts, CTA shapes and breakpoints — which
 * is why shared/nav-mobile.js has to MEASURE whether the links are visible
 * instead of just asking a media query. This file is the single version; the
 * markup that uses it is generated by scripts/i18n.mjs into the
 * <!-- i18n:header --> and <!-- i18n:footer --> blocks. Do not hand-edit either.
 *
 * Every page links this, including index.html, which carries a full inline
 * stylesheet and never loads styles.css — the same reason shared/lang.css is
 * its own file.
 *
 * Nothing here may depend on a page-local custom property, because the pages
 * define different ones. --accent is the single exception and it is always
 * given a literal fallback: index.html and styles.css both declare it, but a
 * new page might not, and a missing accent would render the CTA invisible.
 */

/* ---------------------------------------------------------------------------
 * Header
 *
 * position:fixed is what fourteen pages already do, and their heroes reserve
 * the space with their own top padding. A page without a hero (the legal pages,
 * 404) reserves it in its own layout rule — see .legal in styles.css.
 * ------------------------------------------------------------------------- */

.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 60;
  background: rgba(13, 13, 15, .9);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid rgba(255, 255, 255, .1);
}

/* The homepage nav sits over the top of a full-bleed hero video, so it starts
 * almost invisible and fades to solid once you scroll past the fold. Only the
 * homepage opts in, via variant="overlay" on the generated block; the class is
 * what shared/nav-mobile.js looks for before it binds the scroll listener. */
.nav--overlay {
  background: rgba(13, 13, 15, .12);
  backdrop-filter: none; -webkit-backdrop-filter: none;
  border-bottom-color: transparent;
  transition: background .35s ease, backdrop-filter .35s ease, border-color .35s ease;
}
.nav--overlay.scrolled {
  background: rgba(13, 13, 15, .9);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border-bottom-color: rgba(255, 255, 255, .1);
}

/* The homepage is a light document, so its header is paper rather than ink: the
 * same overlay behaviour (near-transparent over the fold, solid once you scroll
 * past it) painted the other way round. It opts in with variant="light" on the
 * generated block, and shared/nav-mobile.js binds .scrolled for both variants.
 * Every colour below is a literal for the same reason the rest of this file is:
 * a page must not have to declare a token to get a working header. */
.nav--light {
  /* A gradient rather than a flat translucent fill: the homepage photograph
   * runs up behind the bar, and a flat fill drew a hard horizontal edge across
   * it at 80px. This keeps the links legible over the dark hair and dissolves
   * into the photograph instead of ending on it. */
  background: linear-gradient(180deg, rgba(243, 242, 238, .96) 0%, rgba(243, 242, 238, .82) 62%, rgba(243, 242, 238, 0) 100%);
  border-bottom-color: transparent;
  transition: background .35s ease, border-color .35s ease;
}
.nav--light.scrolled {
  background: rgba(243, 242, 238, .94);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border-bottom-color: #dcd9d2;
}
.nav--light .nav-logo,
.nav--light .nav-logo svg { color: #111113; }
.nav--light .nav-links a.navlink {
  color: #4a4a51;
  position: relative; padding-bottom: 3px;
}
.nav--light .nav-links a.navlink:hover,
.nav--light .nav-links a.navlink[aria-current="page"] { color: #111113; }
/* The rule grows from the left rather than appearing: on a page this quiet the
 * hover has to be visible without being loud. */
.nav--light .nav-links a.navlink::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 1px; width: 0;
  background: currentColor; transition: width .28s cubic-bezier(.2, .7, .2, 1);
}
.nav--light .nav-links a.navlink:hover::after,
.nav--light .nav-links a.navlink[aria-current="page"]::after { width: 100%; }
@media (prefers-reduced-motion: reduce) {
  .nav--light .nav-links a.navlink::after { transition: none; }
}
.nav--light .nav-login { color: #111113; }
/* The light bar takes the DARKER accent: the brand orange is 2.7:1 on paper and
 * this is text, not a hairline. --accent-ink is home.css's; the literal is here
 * so the rule still works on a page that has not declared it. */
.nav--light .nav-login:hover { color: var(--accent-ink, #B83E08); }
/* Ink pill, not accent: the homepage's one accent is a hairline, never a surface. */
.nav--light .nav-cta {
  background: #111113; color: #fff !important;
  border-radius: 6px; padding: 12px 22px;
  font-size: 13px; font-weight: 600; text-transform: none; letter-spacing: -.005em;
}
.nav--light .nav-cta:hover { filter: none; background: #2d2d33; }
/* The burger is injected by shared/nav-mobile.js with a hardcoded white; the
 * drawer it opens is a dark full-width overlay and stays that way. */
.nav--light .mp-burger { color: #111113; }
/* The selector is generated with the dark variant on every page. Repaint it
 * here rather than teach the generator a second one. The border is 3.2:1 on
 * paper, not the page's decorative hairline: it is the edge of a control. */
.nav--light .lang-switch--dark > summary {
  color: #4a4a51; border-color: #888279; background: rgba(255, 255, 255, .5);
}
.nav--light .lang-switch--dark > summary:hover { color: #111113; border-color: #111113; }

.nav .wrap {
  max-width: 1240px; margin: 0 auto; padding: 0 24px;
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; height: 80px;
}

.nav-logo {
  justify-self: start; display: flex; align-items: center; gap: 12px;
  text-decoration: none; font-weight: 800; letter-spacing: -.01em; color: #fff;
}
.nav-logo svg { height: 28px; width: auto; color: #fff; }
.nav-logo span { font-size: 22px; }

.nav-links {
  justify-self: center; display: flex; align-items: center; gap: 32px;
  font-size: 13px; font-weight: 600;
}
/* font-size/weight are repeated here on purpose. styles.css still ships on the
 * legal pages and its `.nav-links a` beats the 13px/600 inherited from
 * `.nav-links`, closing the language pill to a 0px gap against the last link. */
.nav-links a.navlink {
  font-size: 13px; font-weight: 600;
  text-decoration: none; color: rgba(255, 255, 255, .7); transition: color .15s;
}
.nav-links a.navlink:hover,
.nav-links a.navlink[aria-current="page"] { color: #fff; }

/* Log in sits beside Start now: a returning owner and a new one want different
 * doors, and only one of them was on the page. */
.nav-actions { justify-self: end; display: flex; align-items: center; gap: 20px; }
.nav-login {
  font-size: 13px; font-weight: 700; color: rgba(255, 255, 255, .9);
  text-decoration: none; transition: color .15s; white-space: nowrap;
}
.nav-login:hover { color: var(--accent, #FF2D78); }
.nav-cta {
  background: var(--accent, #FF2D78); color: #fff !important;
  padding: 11px 22px; border-radius: 4px;
  font-size: 13px; font-weight: 800; text-decoration: none;
  text-transform: uppercase; letter-spacing: .03em; white-space: nowrap;
}
.nav-cta:hover { filter: brightness(1.08); }

/* One breakpoint, not two. 820px is where the eight links stop fitting beside
 * the logo and the actions; below it nav-mobile.js clones them into a drawer. */
@media (max-width: 820px) {
  .nav-links { display: none; }
  .nav .wrap { grid-template-columns: 1fr auto; }
}
@media (max-width: 420px) { .nav-actions { gap: 13px; } }

/* One "Start free" at a time on a phone.
 *
 * The hero carries the primary CTA and the bar carries a second one 60px above
 * it: on a 390px screen that is the same words twice in one glance. The bar's
 * copy is held back until you have scrolled past the hero, at which point it is
 * the only one on screen and earns its place. Desktop shows both, where they
 * are far enough apart to read as a persistent bar and a page CTA.
 * `.scrolled` is set by shared/nav-mobile.js at 36px. */
@media (max-width: 820px) {
  .nav--light .nav-cta {
    opacity: 0; transform: scale(.94); pointer-events: none;
    transition: opacity .3s ease, transform .3s cubic-bezier(.2, .7, .2, 1);
  }
  .nav--light.scrolled .nav-cta { opacity: 1; transform: none; pointer-events: auto; }
  @media (prefers-reduced-motion: reduce) {
    .nav--light .nav-cta { transition: none; }
  }
}

/* An in-page jump must not land under the fixed bar. */
html { scroll-padding-top: 96px; }

/* shared/lang.js inserts its "also available in Portuguese" bar as the body's
 * first child, which worked when the header was in normal flow and the bar
 * pushed it down. The header is fixed now, so in flow the bar lands underneath
 * it and is unreadable. Pin it directly below instead. Scoped to this file, so
 * the help centre — which loads lang.css but not chrome.css and has its own
 * header — keeps the in-flow bar it was built for.
 *
 * `html .lang-nudge` rather than `.lang-nudge`: lang.css is linked AFTER this
 * file on every page, so at equal specificity its `position: relative` would
 * win and the bar would only appear to move (a relative offset still reserves
 * its space at the top of the flow).
 *
 * z-index 55 puts it under the mobile drawer (59) and its scrim (58), which
 * open at the same 80px. lang.css's 80 sat on top of both, so on a phone the
 * nudge bar painted through an open menu. */
html .lang-nudge { position: fixed; top: 80px; left: 0; right: 0; z-index: 55; }

/* The language menu opens and closes with motion.
 *
 * A <details> flips instantly by nature, so the open half is a keyframe on the
 * panel and the close half needs `::details-content` with
 * `transition-behavior: allow-discrete` (Chrome 131+) to hold the element
 * around long enough to animate out. Where that is unsupported the panel simply
 * appears on close-out, which is what it did before, so nothing regresses.
 * Scoped to the chrome's two switchers; the help centre has its own header. */
@media (prefers-reduced-motion: no-preference) {
  .nav details[open] > .lang-menu,
  .site-foot details[open] > .lang-menu {
    animation: langMenuIn .22s cubic-bezier(.2, .7, .2, 1) both;
  }
  @keyframes langMenuIn {
    from { opacity: 0; transform: translateY(-6px) scale(.985); }
    to   { opacity: 1; transform: none; }
  }
  .site-foot details[open] > .lang-menu { animation-name: langMenuInUp; }
  @keyframes langMenuInUp {
    from { opacity: 0; transform: translateY(6px) scale(.985); }
    to   { opacity: 1; transform: none; }
  }
  /* the closing half, where the browser can keep the content around for it */
  .nav details.lang-switch::details-content,
  .site-foot details.lang-switch::details-content {
    opacity: 0;
    transition: opacity .18s ease, content-visibility .18s allow-discrete;
  }
  .nav details.lang-switch[open]::details-content,
  .site-foot details.lang-switch[open]::details-content { opacity: 1; }
  /* the chevron turns with it */
  .nav .lang-switch > summary .lang-chevron,
  .site-foot .lang-switch > summary .lang-chevron { transition: transform .22s cubic-bezier(.2, .7, .2, 1); }
  .nav .lang-switch[open] > summary .lang-chevron,
  .site-foot .lang-switch[open] > summary .lang-chevron { transform: rotate(180deg); }
}

/* A runtime page (404.html) carries one copy of the chrome per language and
 * shows the reader's. Stated rather than left to the UA stylesheet, because a
 * header that fails to hide is a second fixed bar on top of the first. */
.chrome-lang[hidden] { display: none; }

/* ---------------------------------------------------------------------------
 * Footer
 *
 * Dark, because it has to sit at the bottom of a white marketing page, a white
 * legal page and a near-black article page without knowing which. The light
 * variant the homepage used to carry read its colours from that page's own
 * --bg2 / --ink / --border, none of which exist on a page served by styles.css.
 * Self-contained literals are what make one footer possible.
 * ------------------------------------------------------------------------- */

footer.site-foot {
  background: #0d0d0f; color: #fff; padding: 70px 0 34px;
  border-top: 0;
}
footer.site-foot .wrap { max-width: 1240px; margin: 0 auto; padding: 0 24px; }

/* The brand block, then one implicit 1fr column per link group, however many
   there are: a language with two groups and one with three (the English footer
   grew a Compare column, 14 Sep 2026) both lay out on one row. Not auto-fit:
   an auto-repeat beside an fr track is an invalid declaration and the whole
   footer stacked. */
.foot-top { display: grid; grid-auto-flow: column; grid-template-columns: 1.6fr; grid-auto-columns: 1fr; gap: 48px; }
.foot-brand {
  display: flex; align-items: center; gap: 10px;
  font-weight: 800; font-size: 20px; margin-bottom: 14px; color: #fff;
}
.foot-brand svg { height: 24px; width: auto; color: #fff; }
.foot-tag {
  font-size: 14px; line-height: 1.6; color: rgba(255, 255, 255, .5);
  max-width: 42ch; margin: 0 0 22px;
}
.foot-badges { display: flex; gap: 12px; flex-wrap: wrap; }
.foot-badges img { height: 40px; width: auto; display: block; }
.foot-badges a { transition: transform .15s; }
.foot-badges a:hover { transform: translateY(-2px); }

.foot-col h3 {
  font-size: 11px; letter-spacing: .18em; text-transform: uppercase;
  /* .58, not .4: .4 measured 3.80:1 on the footer ground, under AA for 11px */
  color: rgba(255, 255, 255, .58); margin: 0 0 16px; font-weight: 800;
}
.foot-col a {
  display: block; font-size: 14px; color: rgba(255, 255, 255, .72);
  text-decoration: none; padding: 5px 0; transition: color .15s;
  /* styles.css sets margin-bottom on this same selector and is not overridden
   * by the padding above, which made footer rows 9px taller on the legal pages
   * than on the homepage. */
  margin-bottom: 0;
}
.foot-col a:hover,
.foot-col a[aria-current="page"] { color: #fff; }

.foot-bottom {
  display: flex; justify-content: space-between; align-items: center;
  gap: 16px; flex-wrap: wrap;
  margin-top: 52px; padding-top: 24px;
  border-top: 1px solid rgba(255, 255, 255, .1);
  font-size: 12.5px; color: rgba(255, 255, 255, .58);
}

@media (max-width: 820px) {
  .foot-top { grid-auto-flow: row; grid-template-columns: 1fr; gap: 36px; }
  .foot-bottom { flex-direction: column; align-items: flex-start; }
}

/* The footer's language selector.
 *
 * shared/lang.css's `--light` variant means two things at once: opens upward,
 * and painted for a light surface. The footer is dark now, so only the first
 * half still applies, and the second left a white pill sitting on #0d0d0f.
 * Repaint it here rather than in lang.css, because the help centre uses the
 * same file against a genuinely light page.
 */
footer.site-foot .lang-switch--light > summary {
  color: rgba(255, 255, 255, .72);
  border-color: rgba(255, 255, 255, .18);
  background: rgba(255, 255, 255, .06);
}
footer.site-foot .lang-switch--light > summary:hover {
  color: #fff;
  border-color: rgba(255, 255, 255, .38);
}
footer.site-foot .lang-switch--light .lang-menu {
  background: #17171a;
  border-color: rgba(255, 255, 255, .14);
  box-shadow: 0 -8px 28px rgba(0, 0, 0, .45);
}
footer.site-foot .lang-switch--light .lang-menu a,
footer.site-foot .lang-switch--light .lang-menu button { color: rgba(255, 255, 255, .72); }
footer.site-foot .lang-switch--light .lang-menu a:hover,
footer.site-foot .lang-switch--light .lang-menu button:hover,
footer.site-foot .lang-switch--light .lang-menu a[aria-current="page"],
footer.site-foot .lang-switch--light .lang-menu button[aria-current="true"] {
  background: rgba(255, 255, 255, .08);
  color: #fff;
}

/* ---- lang.css ---- */
/* Language selector — matpilot.io
   ---------------------------------------------------------------------------
   Lives in its own file rather than in styles.css because index.html carries a
   full inline stylesheet and never loads styles.css. One file, linked by every
   page that has a selector, keeps the widget from existing in two places.
   Every custom property below has a literal fallback, so this drops onto a page
   with any token set (or none).

   The selector is a <details>/<summary> disclosure. The expanded state is a
   browser primitive and each row is a plain <a href>, so it opens, closes and
   switches language with JavaScript disabled. shared/lang.js only adds
   conveniences on top. The markup is generated into each page by
   scripts/i18n.mjs from i18n.config.json.

   Built to grow: .lang-menu scrolls once the list outgrows its height, which is
   the point of a list rather than a two-way toggle. */

.lang-switch { position: relative; display: inline-block; }
.lang-switch > summary {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; border-radius: 9999px; cursor: pointer;
  font-size: 12px; font-weight: 700; letter-spacing: 0.02em; line-height: 1.2;
  list-style: none; -webkit-user-select: none; user-select: none;
}
.lang-switch > summary::-webkit-details-marker { display: none; }
.lang-switch > summary::marker { content: ""; }
.lang-switch > summary:focus-visible { outline: 2px solid var(--accent, #ff2d78); outline-offset: 2px; }
/* The flag is sized in em by the .fi rule below, so this font-size IS its size:
   12px of font gives a 16x12 box. It was 14px while the flag was an emoji glyph,
   which needed the extra to match the label optically; a solid rectangle does
   not, and at 14px it overpowered the 12px code beside it. */
.lang-switch .lang-flag { font-size: 12px; line-height: 1; }
.lang-switch .lang-chevron {
  width: 0; height: 0; margin-left: 1px;
  border-left: 3.5px solid transparent; border-right: 3.5px solid transparent;
  border-top: 4px solid currentColor; opacity: 0.75;
  transition: transform 0.15s ease;
}
.lang-switch[open] > summary .lang-chevron { transform: rotate(180deg); }

/* Flags — a trimmed subset of flag-icons 7.5.0 (MIT), NOT the library.
   ---------------------------------------------------------------------------
   Why not the emoji it replaced: Windows Chrome ships no country-flag font, so
   🇬🇧 rendered there as the bare letters "GB". SVG images render everywhere.

   Why not the full flag-icons.min.css: it is ~250 background-image rules, one
   per country, and web/ has no build step to tree-shake — `aws s3 sync web/`
   ships whatever is committed. Two languages do not need 250 rules or the
   ~10MB of SVGs behind them. So the base `.fi` rule and one `.fi-<cc>` rule per
   shipped country are copied here by hand, and only those countries' SVGs are
   vendored into shared/flags/. The class NAMES are flag-icons' own, so the
   generated markup stays portable: if a third language ever makes the real
   package worth carrying, swap this block for it and the HTML is unchanged.

   ADDING A LANGUAGE — three steps, none of them automatic:
     1. copy node_modules/flag-icons/flags/4x3/<cc>.svg to shared/flags/<cc>.svg
     2. add `.fi-<cc> { background-image: url("flags/<cc>.svg"); }` below
     3. set `country: "<cc>"` on the language in i18n.config.json, then
        `node scripts/i18n.mjs build`
   `node scripts/i18n.mjs check` fails if 1 or 2 is missed.

   The url()s are relative to THIS FILE, not to the page that links it, so the
   single copy at /shared/lang.css resolves to /shared/flags/<cc>.svg for a root
   page, a /pt/ page and a /help/ page alike.

   Unscoped, and at flag-icons' own specificity, so that swap stays a clean
   replacement rather than a fight neither rule wins. */
.fi {
  background-size: contain; background-position: 50%; background-repeat: no-repeat;
  position: relative; display: inline-block; width: 1.333333em; line-height: 1em;
}
.fi::before { content: "\00a0"; } /* the span is empty; this gives it height */
.fi-ie { background-image: url("flags/ie.svg"); }
.fi-gb { background-image: url("flags/gb.svg"); }
.fi-br { background-image: url("flags/br.svg"); }
.fi-us { background-image: url("flags/us.svg"); }
/* Spanish takes two flags, not one: shared/lang.js swaps a reader in the Americas
   onto the Mexican one, so both are shipped even though only `es` is named in
   i18n.config.json. Same reason `gb` sits above `ie` for English. */
.fi-es { background-image: url("flags/es.svg"); }
.fi-mx { background-image: url("flags/mx.svg"); }
/* Local touch, not from the library: the flags sit against a dark header and a
   white footer, and both tricolour and union flags reach the edge of their box
   in each. */
.lang-switch .lang-flag.fi { border-radius: 1px; }

.lang-menu {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 70;
  min-width: 128px; max-height: 264px;
  overflow-y: auto; overscroll-behavior: contain;
  margin: 0; padding: 6px; list-style: none;
  border-radius: 12px; box-shadow: 0 12px 32px rgba(0, 0, 0, 0.28);
}
/* On a phone the menu is IN FLOW, not floating.
   Anchoring it to the left edge stopped it growing off the side, and it was
   still cut off: on mobile the header's actions are cloned into .mp-drawer,
   which scrolls (`overflow-y: auto`), and a scroll container clips an
   absolutely positioned child at its own edge no matter where that child is
   anchored. The footer's switcher has the same shape. So below 820px the menu
   stops floating and simply takes its place in the column, pushing whatever is
   under it down and scrolling with the drawer. Nothing can crop what is not
   positioned out of the flow (reported twice on an iPhone, 10 Sep 2026). */
@media (max-width: 820px) {
  .lang-switch { display: block; }
  .lang-switch > summary { display: flex; width: max-content; }
  .lang-menu {
    position: static; left: auto; right: auto; top: auto; bottom: auto;
    width: 100%; min-width: 0; max-width: none; max-height: none;
    margin-top: 8px; box-shadow: none;
  }
  .lang-switch--light .lang-menu { top: auto; bottom: auto; }
}
.lang-menu li { margin: 0; }
/* A row is an <a> when the selector links to a sibling page, and a <button>
   when the page switches language in place (scripts/i18n.mjs mode="runtime",
   used by /help/). They are the same control to a reader, so every rule below
   names both rather than letting one mode quietly go unstyled. */
.lang-menu a,
.lang-menu button {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; border-radius: 8px; text-decoration: none;
  font-size: 12px; font-weight: 700; letter-spacing: 0.02em; white-space: nowrap;
  width: 100%; border: 0; background: none; font-family: inherit;
  cursor: pointer; text-align: left;
}
.lang-menu .lang-check { margin-left: auto; padding-left: 14px; min-width: 10px; }
/* A ROW IS A COUNTRY, WITH ITS LANGUAGE UNDER IT: "Brasil / Português",
   "United States / English". The pages differ by country (prices, terms), so
   the picker is a country picker and says so (Vinny, 11 Sep 2026). The code
   stays in the closed pill, now the country's own (BR, IE, ES, US), where a
   flag and two letters are all that fit; in a row it is for screen readers. */
.lang-menu .lang-code {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.lang-menu a, .lang-menu button { padding-top: 7px; padding-bottom: 7px; }
.lang-menu .lang-place { display: flex; flex-direction: column; gap: 1px; line-height: 1.25; }
.lang-menu .lang-country { font-size: 13px; font-weight: 600; letter-spacing: 0; }
/* .85 of the row's own ink, not a fixed grey: on the dark menu that is 6:1,
   where a fixed muted grey measured under 4.5:1 behind the country above it */
.lang-menu .lang-name { font-size: 11.5px; font-weight: 400; letter-spacing: 0; opacity: .85; }
/* And the list says what it is a list OF. */
.lang-menu-head {
  white-space: nowrap;
  padding: 5px 10px 6px; font-size: 10px; font-weight: 800;
  letter-spacing: 0.09em; text-transform: uppercase; opacity: 0.6;
}

/* Dark variant — the black site header. */
.lang-switch--dark > summary { color: rgba(255, 255, 255, 0.7); border: 1px solid rgba(255, 255, 255, 0.2); }
.lang-switch--dark > summary:hover { color: #ffffff; border-color: rgba(255, 255, 255, 0.34); }
.lang-switch--dark .lang-menu { background: #141416; border: 1px solid rgba(255, 255, 255, 0.12); }
.lang-switch--dark .lang-menu-head { color: rgba(255, 255, 255, 0.5); }
.lang-switch--dark .lang-menu a,
.lang-switch--dark .lang-menu button { color: rgba(255, 255, 255, 0.7); }
.lang-switch--dark .lang-menu a:hover,
.lang-switch--dark .lang-menu button:hover { background: rgba(255, 255, 255, 0.08); color: #ffffff; }
/* A linked row carries aria-current="page"; a runtime row is repainted by
   the page script, which uses "true". Both mean "the language you are in". */
.lang-switch--dark .lang-menu a[aria-current="page"],
.lang-switch--dark .lang-menu button[aria-current="true"] { background: rgba(255, 255, 255, 0.1); color: #ffffff; }

/* Light variant — the footer. Opens upward: it sits at the bottom of the page. */
.lang-switch--light > summary {
  color: var(--text-secondary, #56565c); border: 1px solid var(--border, #e7e7ea);
  background: var(--bg, #ffffff);
}
.lang-switch--light > summary:hover { color: var(--ink, #0a0a0a); border-color: var(--text-muted, #8e8e95); }
.lang-switch--light .lang-menu {
  top: auto; bottom: calc(100% + 8px);
  background: var(--bg, #ffffff); border: 1px solid var(--border, #e7e7ea);
  box-shadow: 0 -8px 28px rgba(0, 0, 0, 0.12);
}
.lang-switch--light .lang-menu-head { color: var(--text-muted, #8e8e95); }
.lang-switch--light .lang-menu a,
.lang-switch--light .lang-menu button { color: var(--text-secondary, #56565c); }
.lang-switch--light .lang-menu a:hover,
.lang-switch--light .lang-menu button:hover { background: var(--surface, #f6f6f7); color: var(--ink, #0a0a0a); }
/* A linked row carries aria-current="page"; a runtime row is repainted by
   the page script, which uses "true". Both mean "the language you are in". */
.lang-switch--light .lang-menu a[aria-current="page"],
.lang-switch--light .lang-menu button[aria-current="true"] { background: var(--surface, #f6f6f7); color: var(--ink, #0a0a0a); }

/* Suggestion bar injected by shared/lang.js when the reader's language and the
   page's language disagree. It is a link, never a redirect, so there is no loop
   to get stuck in and nothing about it is required for switching to work. */
.lang-nudge {
  display: flex; align-items: center; justify-content: center; gap: 14px;
  flex-wrap: wrap; padding: 10px 16px; position: relative; z-index: 80;
  background: var(--surface, #f6f6f7); border-bottom: 1px solid var(--border, #e7e7ea);
  font-family: inherit; font-size: 13px; color: var(--text-secondary, #56565c);
}
.lang-nudge a { font-weight: 600; color: var(--ink, #0a0a0a); text-decoration: underline; }
.lang-nudge button {
  border: 0; background: transparent; cursor: pointer;
  font: inherit; font-size: 16px; color: #64646c; padding: 2px 6px; line-height: 1;
}
.lang-nudge button:hover { color: var(--ink, #0a0a0a); }

/* ---- paper.css ---- */
/* The paper design system: editorial, monochrome, warm off-white.
 *
 * Every page on the new design loads this first and its own sheet after it:
 * the homepage adds shared/home.css, the product pages shared/page.css, the
 * legal pages shared/legal.css. It replaces shared/product.css and /styles.css
 * page by page; the two never load together, so nothing here has to
 * out-specify anything there.
 *
 * Palette is the brand's own (lib/theme/index.ts): black ink on warm paper,
 * with #FF6115 used only as a hairline or a dot, never as a surface. Contrast
 * against --paper is held above 4.5:1 for every ink token below.
 */
:root{
  --paper:#f3f2ee;      /* warm off-white; matches the hero photograph's ground */
  --paper-2:#eceae5;
  --white:#ffffff;
  /* Ratios are the WORST case, against --paper-2, which is the ground under the
     platform and pricing sections. Held to WCAG 2.2 AA: 4.5:1 for text, 3:1 for
     a border that carries meaning, which is why an interactive edge takes
     --rule-3 and a decorative hairline takes --rule. */
  --ink:#111113;        /* 15.7:1 */
  --ink-2:#4a4a51;      /*  7.3:1 */
  --ink-3:#64646c;      /*  4.9:1 — the lightest ink allowed on text */
  --rule:#dcd9d2;       /* hairline between blocks, decorative */
  --rule-2:#c9c5bc;     /* a heavier hairline, still decorative */
  --rule-3:#888279;     /*  3.2:1 — the edge of a button or a ringed arrow */
  --dark:#0b0b0c;
  --dark-2:#151517;
  /* Two oranges, because one cannot do both jobs. #FF6115 is the brand mark and
     it is 6.5:1 on the ink band but only 2.69:1 on paper, so anything that has
     to be READ or that carries a state on paper (a hover underline, a link
     colour) takes --accent-ink at 4.7:1 instead. */
  --accent:#FF6115;
  --accent-ink:#B83E08;
  --accent-rgb:255,97,21;
  --maxw:1280px;
  /* Three faces, one job each. Newsreader at 300 is the whole editorial voice:
     a thin serif only reads as premium when it is also LARGE, so the display
     sizes below are floors as much as ceilings. Geist carries body and UI at
     400/500, Geist Mono every uppercase micro-label. All three are on Google
     Fonts and all three load in the app through expo-font, so the site and the
     product can end up on one type system. */
  --serif:"Newsreader",ui-serif,Georgia,"Times New Roman",serif;
  --sans:"Geist","Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --mono:"Geist Mono",ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
}
*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
  margin:0;background:var(--paper);color:var(--ink);overflow-x:hidden;
  font-family:var(--sans);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
  font-size:16px;line-height:1.55;
}
a{color:inherit}
img{max-width:100%;height:auto;display:block}
button{font-family:inherit}
p{margin:0}
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 40px}
@media (max-width:760px){ .wrap{padding:0 22px} }

/* ---------- type ----------
 * The serif carries every display line and nothing else, at 300. A light weight
 * needs room: 80px for the h1, 40px for an h2, and negative tracking so the
 * thin stems still read as one word rather than a row of letters. */
h1,h2,h3,h4{margin:0;font-weight:300}
.display{font-family:var(--serif);font-weight:300;line-height:1.02;letter-spacing:-.022em}
/* The h1 alone sits at 400. A light weight thins out as the size grows, and at
   80px the 300 read as a hairline beside the photograph; the h2 down at 40px
   does not have that problem and stays at 300. */
h1.display{font-size:clamp(44px,5.8vw,80px);font-weight:400;letter-spacing:-.026em}
h2.display{font-size:clamp(30px,3.4vw,40px);line-height:1.06;letter-spacing:-.018em;text-wrap:balance}
h3.display{font-size:clamp(24px,2.5vw,30px);line-height:1.1;letter-spacing:-.016em}
.eyebrow{
  display:block;font-family:var(--mono);font-size:11px;font-weight:500;
  letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3);
}
/* Mono's own sidebearings mean a label needs less tracking than the same label
   set in the sans did, hence .12em above.
   The lede is 19px, not the 24 it started at: at 24 a plain sans paragraph is
   the biggest block of default-looking text on the page and competes with the
   headline. Tracked in slightly, and `.on` promotes the one sentence that is
   doing the positioning work to full ink. */
.lede{font-size:19px;line-height:1.55;letter-spacing:-.012em;color:var(--ink-2);max-width:34ch;text-wrap:pretty}
/* Its own line, always. Left inline, the emphasis straddled the wrap and read
   like a bolded fragment rather than the sentence it is. */
.lede .on{display:block;color:var(--ink);font-weight:500}
.note{font-size:13.5px;letter-spacing:-.005em;color:var(--ink-3)}
/* read by a screen reader, not drawn */
.vh{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.section{padding:106px 0}
@media (max-width:760px){ .section{padding:76px 0} }

/* ---------- buttons ---------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;
  font-size:14px;font-weight:500;letter-spacing:-.012em;
  padding:14px 24px;border-radius:4px;text-decoration:none;border:1px solid transparent;
  cursor:pointer;white-space:nowrap;
  transition:background .18s ease,color .18s ease,border-color .18s ease,transform .12s ease;
}
.btn:active{transform:translateY(1px)}
.btn-solid{background:var(--ink);color:var(--white)}
.btn-solid:hover{background:#2d2d33}
/* The ghost fills from the bottom. The label sits in its own layer so it can
   flip to paper as the ink arrives; a plain background transition would cross
   through a grey that neither colour is legible against. */
.btn-line{position:relative;background:transparent;color:var(--ink);border-color:var(--rule-3);overflow:hidden;isolation:isolate}
.btn-line::before{
  content:"";position:absolute;inset:0;z-index:-1;background:var(--ink);
  transform:translateY(101%);transition:transform .42s cubic-bezier(.2,.7,.2,1);
}
.btn-line:hover{color:var(--paper);border-color:var(--ink)}
.btn-line:hover::before{transform:translateY(0)}
.btn .arw{font-family:var(--sans);transition:transform .2s ease}
.btn:hover .arw{transform:translateX(3px)}
.btn-invert{background:var(--white);color:var(--ink)}
.btn-invert:hover{background:#e9e7e2}

/* the "Get in touch" pairing: label plus a ringed arrow */
.btn-ring{
  display:inline-flex;align-items:center;gap:12px;text-decoration:none;
  font-size:14px;font-weight:500;letter-spacing:-.012em;color:var(--ink);padding:6px 4px;
}
.btn-ring i{
  display:grid;place-items:center;width:34px;height:34px;flex:none;
  border:1px solid var(--rule-3);border-radius:50%;font-style:normal;font-size:13px;
  transition:background .2s ease,color .2s ease,border-color .2s ease,transform .2s ease;
}
.btn-ring:hover i{background:var(--ink);border-color:var(--ink);color:var(--white);transform:translateX(3px)}

/* an underlined text link with a moving arrow */
.link-arw{
  display:inline-flex;align-items:center;gap:9px;text-decoration:none;
  font-size:14px;font-weight:500;letter-spacing:-.012em;color:var(--ink);
  border-bottom:1px solid var(--ink);padding-bottom:3px;
  transition:gap .18s ease,border-color .18s ease;
}
.link-arw:hover{gap:15px;border-color:var(--accent-ink)}

/* shared/chrome.css sets the wordmark at 800 for a page whose body font is
   Inter. Geist ships here at 400/500/600, so an 800 would be synthesised into a
   smeared bold. Ask for the heaviest weight the page actually loads. */
.nav--light .nav-logo,.site-foot .foot-brand{font-weight:600}
/* Smaller than shared/chrome.css sets it. On a page whose h1 is 80px, a 22px
   wordmark competes with the headline for the same eye. */
.nav--light .nav-logo svg{height:22px}
.nav--light .nav-logo span{font-size:18px;letter-spacing:-.02em}
.nav--light .nav-cta,.nav--light .nav-login,.nav--light .nav-links a.navlink{font-weight:500}

/* ---------- reveal ---------- */
[data-reveal]{opacity:0;transform:translateY(16px);transition:opacity .8s cubic-bezier(.2,.7,.2,1),transform .8s cubic-bezier(.2,.7,.2,1)}
[data-reveal].is-in{opacity:1;transform:none}
[data-reveal-delay="1"]{transition-delay:.08s}
[data-reveal-delay="2"]{transition-delay:.16s}
[data-reveal-delay="3"]{transition-delay:.24s}
@media (prefers-reduced-motion:reduce){ [data-reveal]{opacity:1;transform:none;transition:none} }
.progress{position:fixed;top:0;left:0;height:2px;width:0;background:var(--ink);z-index:61;pointer-events:none}
@keyframes mp-progress{from{width:0}to{width:100%}}

/* ---------- a section head: heading left, a rail of words right ----------
 * This carried a five-column capability grid under it. The grid listed
 * Members / Classes / Payments / Grading, which is exactly what the tabs 100px
 * below it already say, so it was four labels twice and it went. */
.sol-head{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:40px;align-items:end}
.sol-head .eyebrow{margin-bottom:22px}
.sol-head h2{max-width:16ch}
.sol-aside{display:grid;gap:8px;text-align:right;padding-bottom:8px}
.sol-aside span{font-family:var(--mono);font-size:10.5px;font-weight:400;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3)}
@media (max-width:1024px){
  .sol-head{grid-template-columns:minmax(0,1fr)}
  .sol-aside{text-align:left}
}

/* ---------- device frames ----------
 * One laptop and one phone, drawn in CSS so the screenshot inside stays the
 * only raster. The laptop is a black lid on a lighter deck; the phone is a
 * thin bezel, because on this page the phone is the supporting device. */
.frame{position:relative}
.frame-top{background:#0d0d10;border:1px solid rgba(0,0,0,.55);border-bottom:0;border-radius:14px 14px 0 0;padding:11px 11px 0;box-shadow:0 30px 60px -34px rgba(0,0,0,.6)}
.frame-top::before{content:"";position:absolute;top:4.5px;left:50%;width:5px;height:5px;margin-left:-2.5px;border-radius:50%;background:#2b2b31}
.frame-top img{border-radius:6px 6px 0 0;width:100%}
.frame-base{height:15px;margin:0 -3.6%;background:linear-gradient(#33333a,#17171b 60%,#0d0d10);border-radius:0 0 12px 12px;position:relative}
.frame-base::after{content:"";position:absolute;top:0;left:50%;width:13%;height:4px;margin-left:-6.5%;background:#0a0a0c;border-radius:0 0 5px 5px}
.shot{position:relative}
.shot img{position:absolute;inset:0;opacity:0;transition:opacity .5s ease}
.shot img:first-child{position:relative}
.shot img.is-active{opacity:1}
/* The phone: a thin black bezel, an island in a status strip painted the app
   header's own white, then the screenshot. Every measure is in cqw of the
   phone's wrapper, so it is the same phone at 80px wide on a small screen and
   at 170px on a large one, rather than a bezel that fattens as it shrinks.
   The strip exists because the capture is the web app, which has no status
   bar: an island drawn straight onto the screenshot sat on the academy's name. */
.phone-float{container-type:inline-size}
.phone{position:relative;background:#111113;border-radius:15cqw;padding:2.4cqw;box-shadow:0 34px 60px -30px rgba(0,0,0,.55),0 0 0 1px rgba(0,0,0,.35)}
.phone-bar{position:relative;height:11cqw;background:#fff;border-radius:12.6cqw 12.6cqw 0 0}
.phone-bar::before{content:"";position:absolute;top:2.6cqw;left:50%;width:29cqw;height:7cqw;margin-left:-14.5cqw;border-radius:99px;background:#111113}
.phone .shot{border-radius:0 0 12.6cqw 12.6cqw;overflow:hidden;background:#fff}
.phone .shot img{width:100%}
/* Hung off the phone's right edge so it reads as the phone's caption at any
   width, including the ones where the phone is narrower than the line. */
.phone-note{position:absolute;right:0;top:calc(100% + 14px);white-space:nowrap;font-family:var(--mono);font-size:11px;letter-spacing:.02em;color:var(--ink-3)}

/* ---------- shared components ----------
 * The pieces the homepage and the product pages both use: a numbered list on
 * hairlines, three facts in a row, and the pricing furniture (a tier, plan
 * cards, rows, the add-on list). Written once here so a price looks the same
 * wherever it is quoted. */
.steps-list{display:grid;gap:0;margin:34px 0 32px;border-top:1px solid var(--rule)}
/* `> div` twice over: the row is the grid, the text block inside it is not. */
.steps-list > div{
  display:grid;grid-template-columns:38px minmax(0,1fr);gap:14px;
  padding:18px 14px 18px 0;border-bottom:1px solid var(--rule);
  transition:padding-left .35s cubic-bezier(.2,.7,.2,1),background .35s ease;
}
.steps-list > div:hover{padding-left:14px;background:rgba(0,0,0,.022)}
.steps-list > div:hover i{color:var(--accent-ink)}
.steps-list i{transition:color .3s ease}
.steps-list i{font-style:normal;font-family:var(--mono);font-size:13px;line-height:1.7;color:var(--ink-3)}
.steps-list b{display:block;font-size:15px;font-weight:500;letter-spacing:-.012em}
.steps-list p{font-size:15px;line-height:1.5;letter-spacing:-.006em;color:var(--ink-2);margin-top:3px}

/* three facts in a row, no heading
 * This used to carry two unattributed testimonials above it. An unattributed
 * quote is weaker than no quote, so they are gone and the facts stand alone. */
.claims{display:grid;grid-template-columns:repeat(3,1fr)}
.claim{padding:0 30px;border-left:1px solid var(--rule)}
.claim:first-child{border-left:0;padding-left:0}
.claim b{display:block;font-family:var(--serif);font-size:26px;font-weight:300;letter-spacing:-.016em;margin-bottom:9px;text-wrap:balance}
.claim span{font-size:14.5px;line-height:1.5;letter-spacing:-.006em;color:var(--ink-2);text-wrap:pretty}
@media (max-width:900px){
  .claims{grid-template-columns:1fr;gap:30px}
  .claim{border-left:0;padding:0}
}

/* pricing:
 * Gym management is two plans and needs cards. Websites and the white-label app
 * are one line each and would look absurd as cards, so they are rows: same
 * hairline language, a quarter of the ink. */
.tier{padding:38px 0;border-top:1px solid var(--rule-2);display:grid;grid-template-columns:minmax(0,.62fr) minmax(0,1.38fr);gap:48px;align-items:start}
.tier-head p{margin-top:14px;font-size:14.5px;line-height:1.5;letter-spacing:-.006em;color:var(--ink-2);max-width:30ch;text-wrap:pretty}
.tier-head .eyebrow{color:var(--ink)}
.plans{display:grid;grid-template-columns:1fr 1fr;gap:20px;align-items:stretch}
.plan{display:flex;flex-direction:column;background:var(--white);border:1px solid var(--rule);border-radius:14px;padding:30px 28px 28px;position:relative}
.plan.pro{background:var(--dark);color:var(--white);border-color:var(--dark)}
.plan-name{font-family:var(--mono);font-size:11px;font-weight:400;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3);margin-bottom:16px}
.plan.pro .plan-name{color:rgba(255,255,255,.6)}
.plan-price{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}
.plan-price .num{font-family:var(--serif);font-size:52px;font-weight:300;line-height:1;letter-spacing:-.028em}
.plan-price .per{font-size:14px;font-weight:400;letter-spacing:-.006em;color:var(--ink-3)}
.plan.pro .plan-price .per{color:rgba(255,255,255,.62)}
.plan-sub{margin:16px 0 24px;font-size:15px;line-height:1.5;letter-spacing:-.006em;color:var(--ink-2)}
.plan.pro .plan-sub{color:rgba(255,255,255,.74)}
/* align-content:start: the list takes the card's spare height so the buttons
   line up across cards, but its rows keep their own spacing rather than
   spreading out to fill it */
.plan ul{list-style:none;margin:0 0 28px;padding:0;display:grid;gap:10px;flex:1;align-content:start}
.plan li{font-size:14.5px;line-height:1.5;letter-spacing:-.006em;color:var(--ink-2);padding-left:18px;position:relative}
.plan.pro li{color:rgba(255,255,255,.8)}
.plan li::before{content:"";position:absolute;left:0;top:9px;width:6px;height:1px;background:var(--rule-3)}
.plan.pro li::before{background:rgba(255,255,255,.4)}
.plan .btn{width:100%}
/* the ghost button's ink fill has to invert against a dark card, not a light one */
.plan.pro .btn-invert:hover{background:#e9e7e2}

.rows{display:grid;gap:0}
.row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:32px;align-items:center;padding:22px 0;border-bottom:1px solid var(--rule)}
.row:last-of-type{border-bottom:0}
.row-what b{display:block;font-size:16px;font-weight:500;letter-spacing:-.014em;margin-bottom:5px}
.row-what span{display:block;font-size:14.5px;line-height:1.5;letter-spacing:-.006em;color:var(--ink-2);max-width:52ch;text-wrap:pretty}
.row-cost{text-align:right;white-space:nowrap}
.row-cost b{font-family:var(--serif);font-size:34px;font-weight:300;letter-spacing:-.024em}
.row-cost span{font-size:14px;color:var(--ink-3);margin-left:6px}
/* Website add-ons. Quoted, so there is no price to put in the right-hand
   column: "Quoted" holds the column's place in the same serif the prices use,
   and ONE button serves the group. Five identical "Get a quote" buttons in a
   stack was the version this replaced. */
/* no border-top: the row above already ends on a hairline, and two of them 10px
   apart read as a rendering fault */
.addons{padding-top:24px}
.addons-label{display:block;margin-bottom:6px;font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3)}
.addons-list{list-style:none;margin:0 0 22px;padding:0;display:grid;grid-template-columns:1fr 1fr;column-gap:40px}
.addons-list li{display:flex;justify-content:space-between;align-items:baseline;gap:16px;padding:13px 0;border-bottom:1px solid var(--rule)}
.addons-list b{font-size:15px;font-weight:500;letter-spacing:-.012em}
.addons-list span{flex:none;font-family:var(--serif);font-size:17px;font-weight:300;letter-spacing:-.01em;color:var(--ink-3)}
/* the photography line carries the studio's own credit, logo first */
.addon-media b em{display:flex;align-items:center;gap:6px;margin-top:5px;font-style:normal;font-size:12.5px;font-weight:400;letter-spacing:-.004em;color:var(--ink-2)}
.addon-media b em svg{height:11px;width:auto;flex:none;color:var(--ink)}
.addons-list li{transition:padding-left .35s cubic-bezier(.2,.7,.2,1)}
.addons-list li:hover{padding-left:8px}
@media (max-width:640px){
  .addons-list{grid-template-columns:1fr}
  .addons .btn{width:100%}
}
.row-note{margin-top:20px;font-size:13.5px;line-height:1.55;color:var(--ink-3);max-width:64ch;text-wrap:pretty}
.row-note a{font-weight:500;color:var(--ink);text-decoration:underline;text-decoration-color:var(--accent-ink);text-decoration-thickness:1.5px;text-underline-offset:3px}

.plan-more{margin-top:38px;display:flex;flex-wrap:wrap;gap:6px 16px;align-items:center;justify-content:center;font-size:14px;letter-spacing:-.006em;color:var(--ink-2)}
.plan-more a{font-weight:500;color:var(--ink);text-decoration:underline;text-decoration-color:var(--accent-ink);text-decoration-thickness:1.5px;text-underline-offset:3px}
.plan-more .sep{color:var(--rule-2)}
@media (max-width:1024px){
  .tier{grid-template-columns:minmax(0,1fr);gap:26px}
  .tier-head p{max-width:52ch}
}
@media (max-width:640px){
  .plans{grid-template-columns:1fr}
  .row{grid-template-columns:minmax(0,1fr);gap:14px}
  .row-cost{text-align:left;white-space:normal}
  .row-cost .btn{width:100%}
}


/* the curriculum loop in three steps (homepage section, curriculum page)
 * Drawn, not screenshotted: the platform section just above already shows the
 * product, and what this section has to get across is an idea, that eighty
 * planned techniques become the two that were taught. Three columns on one
 * rule, the live moment lifted onto a white card, and the ticks draw as the
 * band crosses the window. The orange is a hairline that runs along the top
 * of each step in turn. */
.cur-flow{list-style:none;margin:64px 0 0;padding:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));border-top:1px solid var(--rule-2)}
.cur-node{position:relative;padding:36px 40px 44px;border-left:1px solid var(--rule)}
.cur-node:first-child{border-left:0;padding-left:0}
.cur-node::before{content:"";position:absolute;left:0;right:0;top:-1px;height:1px;background:var(--accent);transform-origin:0 50%}
.cur-node:first-child::before{right:40px}
/* The card's own edge is the divider on both of its sides, so the hairlines
   either side of it go, and the two words that sit on those edges need no
   patch of background to cut the line behind them. */
.cur-live{background:var(--white);box-shadow:0 30px 60px -40px rgba(0,0,0,.35);border-left:0}
.cur-live + .cur-node{border-left:0}
.cur-tag{display:block;margin-bottom:18px;font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3)}
.cur-node h3.display{margin-bottom:26px}
/* the word that happens between two steps, sat on the rule that divides them */
.cur-link{position:absolute;left:0;top:50%;transform:translate(-50%,-50%);z-index:1}
.cur-link i{display:block;font-style:normal;font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);writing-mode:vertical-rl;transform:rotate(180deg)}
.cur-list,.cur-ticks{list-style:none;margin:0;padding:0}
.cur-list li,.cur-ticks li{padding:11px 0;border-top:1px solid var(--rule);font-size:15px;letter-spacing:-.008em;color:var(--ink)}
.cur-list li:last-child,.cur-ticks li:last-child{border-bottom:1px solid var(--rule)}
.cur-list .faint,.cur-ticks .faint{color:var(--ink-3)}
.cur-ticks li{display:flex;align-items:center;gap:12px}
.cur-ticks .box{display:grid;place-items:center;width:18px;height:18px;flex:none;border:1px solid var(--rule-3);border-radius:4px;color:var(--white)}
.cur-ticks .on .box{background:var(--ink);border-color:var(--ink)}
.cur-ticks .box svg{width:12px;height:12px}
.cur-ticks .box path{stroke-dasharray:14;stroke-dashoffset:0}
.cur-people{margin:0}
.cur-people div{padding:11px 0;border-top:1px solid var(--rule)}
.cur-people div:last-child{border-bottom:1px solid var(--rule)}
.cur-people dt{font-size:15px;font-weight:500;letter-spacing:-.008em}
.cur-people dd{margin:3px 0 0;font-size:13.5px;color:var(--ink-3)}
.cur-foot{margin-top:22px;font-family:var(--serif);font-size:22px;font-weight:300;letter-spacing:-.014em;line-height:1.2}
.cur-close{margin-top:58px;display:flex;flex-wrap:wrap;gap:22px 48px;align-items:flex-end;justify-content:space-between}
.cur-close p{font-size:17px;line-height:1.6;letter-spacing:-.008em;color:var(--ink-2);max-width:58ch;text-wrap:pretty}
@media (max-width:900px){
  .cur-flow{grid-template-columns:minmax(0,1fr);border-top:0}
  .cur-node,.cur-node:first-child{border-left:0;border-top:1px solid var(--rule-2);padding:40px 0 36px}
  .cur-node:first-child::before{right:0}
  .cur-live{padding:40px 22px 36px;margin:0 -22px}
  .cur-link{left:22px;top:0;transform:translateY(-50%);padding:0 10px;background:var(--paper-2)}
  .cur-live .cur-link{background:var(--white)}
  .cur-live .cur-link{left:22px}
  .cur-node:not(.cur-live) .cur-link{left:0;padding-left:0}
  .cur-link i{writing-mode:horizontal-tb;transform:none}
}

/* ---------- closing band ---------- */
.closing{background:var(--dark);color:var(--white);padding:110px 0}
.closing .wrap{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:48px;align-items:center}
.closing .eyebrow{color:rgba(255,255,255,.6);margin-bottom:22px}
.closing h2{max-width:24ch}
.closing .note{color:rgba(255,255,255,.6);margin-top:14px}
.closing-cta{display:flex;flex-wrap:wrap;gap:12px;align-items:center}
/* The ink-band twin of .btn-line. It used to be an inline style on the anchor,
   which meant the hover state was simply missing. .36 white and not .3: a
   button's edge is a meaningful border, so it owes 3:1, and .3 measured 2.60
   against this band. */
.btn-onink{background:transparent;color:var(--white);border-color:rgba(255,255,255,.36)}
.btn-onink:hover{border-color:var(--white);background:rgba(255,255,255,.08)}
@media (max-width:980px){
  .closing{padding:80px 0}
  .closing .wrap{grid-template-columns:minmax(0,1fr);gap:34px}
}

/* ---------- motion ----------
 *
 * Two layers, and the browser picks one.
 *
 * The FALLBACK is the transition on [data-reveal] near the top of this file,
 * flipped by shared/product.js on an IntersectionObserver: an element fades up
 * once, on a timer, and stays. Safari and Firefox get this.
 *
 * The REAL one, under @supports below, is a scroll-driven animation
 * (`animation-timeline: view()`). The element's progress IS the scroll
 * position, so a reveal tracks the wheel and reverses when you scroll back,
 * which is what makes the page feel attached to the hand rather than triggered
 * by it. No JS, no rAF, no listener: the compositor runs it. Chrome and Edge.
 *
 * Everything here is decorative and everything is off under
 * prefers-reduced-motion, which is asserted at the end of the block. Any hover
 * transform is paired with an :active state, because a phone has no hover and a
 * press is the only feedback it can give.
 */
@keyframes rise{from{opacity:0;transform:translateY(22px)}to{opacity:1;transform:none}}
@keyframes photoIn{from{opacity:0;transform:scale(1.06)}to{opacity:1;transform:none}}
@keyframes revealUp{from{opacity:0;transform:translateY(46px) scale(.972)}to{opacity:1;transform:none}}
@keyframes liftIn{from{opacity:0;transform:translateY(60px)}to{opacity:1;transform:none}}
@keyframes tiltIn{from{opacity:0;transform:perspective(1600px) rotateX(11deg) translateY(40px)}to{opacity:1;transform:perspective(1600px) rotateX(0) translateY(0)}}
@keyframes driftY{from{transform:translateY(22px)}to{transform:translateY(-22px)}}
@keyframes heroPush{from{transform:scale(1) translateY(0)}to{transform:scale(1.10) translateY(2.5%)}}
@keyframes foldOut{from{transform:none;opacity:1}to{transform:translateY(-64px);opacity:.12}}
@keyframes railOut{from{transform:none;opacity:1}to{transform:translateY(38px);opacity:0}}
@keyframes driftUp{from{transform:translateY(30px)}to{transform:translateY(-30px)}}
@keyframes driftUpSmall{from{transform:translateY(18px)}to{transform:translateY(-18px)}}
@keyframes phoneDrift{from{transform:translateY(34px)}to{transform:translateY(-34px)}}
@keyframes growX{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
@keyframes tickBox{from{background:transparent;border-color:var(--rule-3)}to{background:var(--ink);border-color:var(--ink)}}
@keyframes tickDraw{from{stroke-dashoffset:14}to{stroke-dashoffset:0}}
@keyframes driftDown{from{transform:translateY(-26px)}to{transform:translateY(26px)}}
@keyframes stepIn{from{opacity:0;transform:translateX(-26px)}to{opacity:1;transform:none}}
@keyframes tabTimer{from{width:0}to{width:100%}}

/* ---- pointer feedback ---- */
.plan{transition:transform .35s cubic-bezier(.2,.7,.2,1),box-shadow .35s ease}
.plan:hover{transform:translateY(-6px);box-shadow:0 30px 60px -34px rgba(0,0,0,.4)}
.plan.pro:hover{box-shadow:0 30px 60px -30px rgba(0,0,0,.65)}
.claim{transition:transform .35s cubic-bezier(.2,.7,.2,1)}
.claim:hover{transform:translateY(-3px)}
.btn:active{transform:translateY(1px) scale(.988)}
.btn-ring:active i{transform:scale(.94)}
.link-arw:active{opacity:.7}

/* the JS fallback's timed stagger for a row of facts */
.is-in .claim{animation:rise .8s cubic-bezier(.2,.7,.2,1) both}
.is-in .claim:nth-child(2){animation-delay:.1s}
.is-in .claim:nth-child(3){animation-delay:.2s}

/* the curriculum loop's fallback: the ticks draw once the list has revealed */
.cur-flow .on .box{background:transparent;border-color:var(--rule-3);transition:background .3s ease .9s,border-color .3s ease .9s}
.cur-flow .on + .on .box{transition-delay:1.25s}
.cur-flow .box path{stroke-dashoffset:14;transition:stroke-dashoffset .45s ease 1s}
.cur-flow .on + .on .box path{transition-delay:1.35s}
.cur-flow.is-in .on .box{background:var(--ink);border-color:var(--ink)}
.cur-flow.is-in .box path{stroke-dashoffset:0}
.cur-node::before{transform:scaleX(0);transition:transform 1.1s cubic-bezier(.2,.7,.2,1) .3s}
.cur-node:nth-child(2)::before{transition-delay:.7s}
.cur-node:nth-child(3)::before{transition-delay:1.1s}
.cur-flow.is-in .cur-node::before{transform:none}

/* ---- scroll-driven reveals ----
 * Late and quick: a block is still moving while it is below the middle of the
 * window and settled by the time you read it. The constant motion comes from
 * the DRIFT rules, which run across an element's whole crossing and so never
 * finish while it is on screen. Pages add their own under the same guard. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    [data-reveal]{
      opacity:1;transform:none;transition:none;
      animation:revealUp linear both;
      animation-timeline:view();
      animation-range:entry 18% cover 44%;
    }
    /* DRIFT: the section head and its rail pull apart as the band crosses */
    .sol-head > div:first-child{animation:driftUp linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
    .sol-aside{animation:driftDown linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
    /* Curriculum: the steps arrive left to right, the orange runs along each
       top in turn, and the two ticks draw in the middle of the crossing. */
    .cur-node{animation:liftIn linear both;animation-timeline:view();animation-range:entry 14% cover 40%}
    .cur-node:nth-child(2){animation-range:entry 20% cover 46%}
    .cur-node:nth-child(3){animation-range:entry 26% cover 52%}
    .cur-node::before,.cur-flow.is-in .cur-node::before{transition:none;animation:growX linear both;animation-timeline:view();animation-range:cover 20% cover 38%}
    .cur-node:nth-child(2)::before,.cur-flow.is-in .cur-node:nth-child(2)::before{animation-range:cover 30% cover 48%}
    .cur-node:nth-child(3)::before,.cur-flow.is-in .cur-node:nth-child(3)::before{animation-range:cover 40% cover 58%}
    .cur-flow .on .box,.cur-flow.is-in .on .box{transition:none;animation:tickBox linear both;animation-timeline:view();animation-range:cover 30% cover 38%}
    .cur-flow .on + .on .box,.cur-flow.is-in .on + .on .box{animation-range:cover 36% cover 44%}
    .cur-flow .on .box path,.cur-flow.is-in .on .box path{transition:none;animation:tickDraw linear both;animation-timeline:view();animation-range:cover 32% cover 40%}
    .cur-flow .on + .on .box path,.cur-flow.is-in .on + .on .box path{animation-range:cover 38% cover 46%}
    .cur-link{animation:fadeIn linear both;animation-timeline:view();animation-range:cover 26% cover 40%}
    .cur-close{animation:driftUpSmall linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
    /* numbered steps, facts and pricing arrive one after another, each still
       moving while the next one starts */
    .claims{animation:driftUpSmall linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
    .tier-head{animation:driftUpSmall linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
    .steps-list > div{animation:stepIn linear both;animation-timeline:view();animation-range:entry 22% cover 46%}
    .steps-list > div:nth-child(2){animation-range:entry 28% cover 52%}
    .steps-list > div:nth-child(3){animation-range:entry 34% cover 58%}
    .claim{animation:liftIn linear both;animation-timeline:view();animation-range:entry 20% cover 44%}
    .claim:nth-child(2){animation-range:entry 26% cover 50%}
    .claim:nth-child(3){animation-range:entry 32% cover 56%}
    .is-in .claim{animation:liftIn linear both;animation-timeline:view()}
    .tier{animation:revealUp linear both;animation-timeline:view();animation-range:entry 18% cover 44%}
    .plan:first-child{animation:liftIn linear both;animation-timeline:view();animation-range:entry 22% cover 48%}
    .plan.pro{animation:liftIn linear both;animation-timeline:view();animation-range:entry 28% cover 54%}
    .row{animation:stepIn linear both;animation-timeline:view();animation-range:entry 22% cover 48%}
    .addons-list li{animation:stepIn linear both;animation-timeline:view();animation-range:entry 20% cover 46%}
    .addons-list li:nth-child(even){animation-range:entry 26% cover 52%}
  }
}
@media (prefers-reduced-motion:reduce){
  .is-in .claim{animation:none}
  .cur-flow .on .box{background:var(--ink);border-color:var(--ink);transition:none}
  .cur-flow .box path{stroke-dashoffset:0;transition:none}
  .cur-node::before{transform:none;transition:none}
  .shot img,.btn,.btn-ring i,.btn-line::before,.steps-list > div,.addons-list li,.plan,.claim{transition:none}
  .btn:active{transform:none}
}

/* The features hub and the home's feature grid (scripts/i18n.mjs renders the
   cards from i18n.config.json), and the "Related" block at the foot of every
   feature page. Cards are links: the anchor text is the query the page targets. */
.feature-hub{padding:clamp(40px,6vw,80px) 0}
.feature-grid{list-style:none;margin:32px 0 0;padding:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}
.feature-grid a,.related a{display:block;height:100%;padding:22px 22px 24px;border:1px solid var(--rule);border-radius:14px;background:var(--white);text-decoration:none;color:var(--ink);transition:border-color .3s,transform .4s var(--ease-out)}
.feature-grid a:hover,.related a:hover{border-color:var(--ink);transform:translateY(-2px)}
/* A hub card wears the page's own screenshot on top (14 Sep 2026): the picture
   is cropped to one aspect so phone and desktop shots sit in the same grid,
   anchored to the top where the screen's chrome is. */
.feature-grid a{padding:0;overflow:hidden}
.feature-grid img{display:block;width:100%;aspect-ratio:16/10;object-fit:cover;object-position:50% 0;border-bottom:1px solid var(--rule);background:var(--paper)}
.feature-grid .fg-body{padding:18px 22px 22px}
.feature-grid b,.related b{display:block;font-size:17px;font-weight:500;letter-spacing:-.014em;margin-bottom:6px}
.feature-grid span,.related span{display:block;font-size:14px;line-height:1.5;letter-spacing:-.006em;color:var(--ink-2);text-wrap:pretty}
.related{margin:clamp(48px,7vw,96px) auto 0;padding:0 40px;max-width:var(--maxw)}
.related-h{margin:0 0 14px;font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3)}
.related ul{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}
@media (max-width:900px){.feature-grid,.related ul{grid-template-columns:1fr}.related{padding:0 22px}}

/* A page's questions: a <details> list under a display heading. FAQPage
   schema in the page's head names the same pairs. */
.faq{padding:clamp(64px,8vw,110px) 0}
.faq h2{max-width:20ch;margin-bottom:34px}
.faq details{border-top:1px solid var(--rule);padding:20px 0}
.faq details:last-of-type{border-bottom:1px solid var(--rule)}
.faq summary{cursor:pointer;font-size:18px;font-weight:500;letter-spacing:-.012em;list-style:none;display:flex;justify-content:space-between;gap:24px}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"+";font-family:var(--mono);color:var(--ink-3)}
.faq details[open] summary::after{content:"\2013"}
.faq p{margin-top:12px;font-size:15.5px;line-height:1.6;color:var(--ink-2);max-width:62ch;text-wrap:pretty}

/* ---- page.css ---- */
/* The product pages (AI co-pilot, Websites, Curriculum), on shared/paper.css.
 *
 * Same system as the homepage, a different job: a homepage sells the whole
 * thing in one pass, a product page takes one idea and walks through it. So
 * the pieces here are a page hero with a device beside it, feature rows that
 * alternate text and screen down the page, a browser frame, a reply card, a
 * swipeable rail and a prose column for the parts that are an argument rather
 * than a list.
 */

/* ---------- page hero ---------- */
.page-hero{position:relative;padding:176px 0 108px;overflow:clip}
.page-hero > .wrap{display:grid;grid-template-columns:minmax(0,.92fr) minmax(0,1.08fr);gap:72px;align-items:center}
.page-hero .eyebrow{margin-bottom:24px}
.page-hero h1{max-width:12ch}
.page-hero .lede{margin:30px 0 38px;max-width:40ch}
.page-cta{display:flex;flex-wrap:wrap;gap:14px 26px;align-items:center}
.page-hero .note{margin-top:22px}
.page-hero-visual{position:relative}
@media (max-width:1024px){
  .page-hero{padding:132px 0 84px}
  .page-hero > .wrap{grid-template-columns:minmax(0,1fr);gap:60px}
  .page-hero h1{max-width:14ch}
}
.page-hero-copy > *{animation:rise 1s cubic-bezier(.2,.7,.2,1) both}
.page-hero-copy .eyebrow{animation-delay:.06s}
.page-hero-copy h1{animation-delay:.14s}
.page-hero-copy .lede{animation-delay:.3s}
.page-hero-copy .page-cta{animation-delay:.44s}
.page-hero-copy .note{animation-delay:.56s}
.page-hero-visual{animation:heroIn 1.3s .2s cubic-bezier(.2,.7,.2,1) both}
@keyframes heroIn{from{opacity:0;transform:translateY(34px) scale(.985)}to{opacity:1;transform:none}}

/* ---------- a section ---------- */
.band{background:var(--paper-2);border-top:1px solid var(--rule)}
.band-top{border-top:1px solid var(--rule)}
.sol-lede{margin-top:30px;font-size:17px;line-height:1.65;letter-spacing:-.008em;color:var(--ink-2);max-width:62ch;text-wrap:pretty}
.sol-lede strong{color:var(--ink);font-weight:500}

/* ---------- feature rows ----------
 * Text and screen side by side, the screen on alternate sides down the page,
 * every row opening on a hairline. The number is mono so it reads as an index
 * rather than as part of the headline. */
.features{margin-top:64px}
.feature{display:grid;grid-template-columns:minmax(0,.86fr) minmax(0,1.14fr);gap:72px;align-items:center;padding:72px 0;border-top:1px solid var(--rule-2)}
.feature:last-child{padding-bottom:0}
.feature.flip{grid-template-columns:minmax(0,1.14fr) minmax(0,.86fr)}
.feature.flip > .feature-copy{order:2}
.feature-num{display:block;margin-bottom:20px;font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.12em;color:var(--ink-3)}
.feature h3{font-family:var(--serif);font-weight:300;font-size:clamp(26px,2.7vw,34px);line-height:1.1;letter-spacing:-.018em;margin-bottom:20px;text-wrap:balance}
.feature p{font-size:16.5px;line-height:1.68;letter-spacing:-.008em;color:var(--ink-2);max-width:48ch;margin-bottom:14px;text-wrap:pretty}
.feature p:last-child{margin-bottom:0}
.feature p strong{color:var(--ink);font-weight:500}
.feature-visual{position:relative}
@media (max-width:1024px){
  .feature{grid-template-columns:minmax(0,1fr);gap:40px;padding:56px 0}
  .feature.flip{grid-template-columns:minmax(0,1fr)}
  .feature.flip > .feature-copy{order:0}
}

/* ---------- a browser frame ----------
 * For a website, the frame is part of the claim: it says "this is a page on the
 * web", which a laptop lid does not. Light, one hairline, three quiet dots. */
.browser{background:var(--white);border:1px solid var(--rule-2);border-radius:8px;overflow:hidden;box-shadow:0 30px 60px -40px rgba(0,0,0,.45)}
.browser-bar{display:flex;gap:6px;align-items:center;height:24px;padding:0 11px;border-bottom:1px solid var(--rule);background:#faf9f6}
.browser-bar span{width:7px;height:7px;border-radius:50%;background:var(--rule-2)}
.browser img{width:100%}

/* ---------- a reply card: one captured answer from the co-pilot ---------- */
.reply{position:relative;border-radius:18px;overflow:hidden;background:var(--white);border:1px solid var(--rule);box-shadow:0 40px 80px -46px rgba(0,0,0,.45)}
.reply img{width:100%}

/* ---------- a swipeable rail of cards ----------
 * Scroll-snap does the physics, so a trackpad, a mouse wheel, a finger and
 * the two arrows all move it the same way. The rail is its own scroll
 * container, which is exactly why nothing INSIDE it carries a view()
 * animation: a view timeline in there would resolve against the rail and not
 * the page. The reveal goes on the rail itself. */
.rail-head{display:flex;align-items:flex-end;justify-content:space-between;gap:24px}
.rail-nav{display:flex;gap:10px;flex:none}
.rail-nav button{
  display:grid;place-items:center;width:44px;height:44px;border-radius:50%;
  border:1px solid var(--rule-3);background:transparent;color:var(--ink);cursor:pointer;font-size:15px;
  transition:background .2s ease,color .2s ease,border-color .2s ease,opacity .2s ease,transform .12s ease;
}
.rail-nav button:hover:not(:disabled){background:var(--ink);border-color:var(--ink);color:var(--white)}
.rail-nav button:active:not(:disabled){transform:scale(.94)}
.rail-nav button:disabled{opacity:.35;cursor:default}
.rail{
  display:grid;grid-auto-flow:column;grid-auto-columns:min(372px,82vw);gap:20px;
  margin:44px calc(50% - 50vw) 0;padding:6px max(40px,calc(50vw - 600px)) 34px;
  overflow-x:auto;scroll-snap-type:x mandatory;scroll-padding-inline:max(40px,calc(50vw - 600px));
  scrollbar-width:none;overscroll-behavior-x:contain;
}
.rail::-webkit-scrollbar{display:none}
.rail > *{scroll-snap-align:start}
.rail-progress{height:1px;background:var(--rule);position:relative;margin-top:-10px}
.rail-progress i{position:absolute;left:0;top:0;height:1px;width:0;background:var(--accent);transition:width .25s ease}
@media (max-width:760px){
  .rail{padding-inline:22px;scroll-padding-inline:22px}
  /* beside the heading the arrows took a third of a phone's width from it */
  .rail-head{flex-direction:column;align-items:flex-start}
}

/* a job the co-pilot does: what it is called, what it does, then the real
   reply, whose top already carries the owner's ask in its own bubble */
.job{display:flex;flex-direction:column;background:var(--white);border:1px solid var(--rule);border-radius:16px;overflow:hidden;transition:transform .35s cubic-bezier(.2,.7,.2,1),box-shadow .35s ease}
.job:hover{transform:translateY(-4px);box-shadow:0 30px 60px -38px rgba(0,0,0,.4)}
.job-copy{padding:24px 24px 20px}
.job-kind{display:block;margin-bottom:10px;font-family:var(--mono);font-size:10.5px;font-weight:500;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3)}
.job h3{font-family:var(--serif);font-weight:300;font-size:25px;line-height:1.12;letter-spacing:-.016em;margin-bottom:10px}
.job p{font-size:14.5px;line-height:1.55;letter-spacing:-.006em;color:var(--ink-2);text-wrap:pretty}
/* the reply, cropped to its top: the part that answers the ask */
.job-shot{margin-top:auto;border-top:1px solid var(--rule);height:340px;overflow:hidden;position:relative;background:var(--white)}
.job-shot img{width:100%}
.job-shot::after{content:"";position:absolute;left:0;right:0;bottom:0;height:64px;background:linear-gradient(rgba(255,255,255,0),#fff)}

/* ---------- a fan of sites (Websites hero) ----------
 * Four real pages, fanned up and to the left from a front page on the right,
 * so each one behind shows its left edge, where every site keeps its logo. */
.fan{position:relative;height:clamp(300px,34vw,470px)}
.fan .browser{position:absolute;right:0;bottom:0;width:72%;transition:transform .6s cubic-bezier(.2,.7,.2,1)}
.fan .browser:nth-child(1){z-index:4}
.fan .browser:nth-child(2){z-index:3;transform:translate(-14%,-11%) scale(.95)}
.fan .browser:nth-child(3){z-index:2;transform:translate(-28%,-22%) scale(.9)}
.fan .browser:nth-child(4){z-index:1;transform:translate(-40%,-33%) scale(.85)}
.fan:hover .browser:nth-child(1){transform:translate(2%,3%)}
.fan:hover .browser:nth-child(2){transform:translate(-16%,-13%) scale(.95)}
.fan:hover .browser:nth-child(3){transform:translate(-32%,-26%) scale(.9)}
.fan:hover .browser:nth-child(4){transform:translate(-46%,-39%) scale(.85)}

/* ---------- a gallery of sites ---------- */
.site-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:40px 28px;margin-top:60px}
.site-card{text-decoration:none;color:inherit;display:block}
.site-card .browser{transition:transform .45s cubic-bezier(.2,.7,.2,1),box-shadow .45s ease}
.site-card:hover .browser{transform:translateY(-6px);box-shadow:0 40px 70px -40px rgba(0,0,0,.5)}
.site-card figcaption{display:flex;justify-content:space-between;gap:12px;margin-top:16px;padding-top:12px;border-top:1px solid var(--rule)}
.site-card figcaption b{font-size:14.5px;font-weight:500;letter-spacing:-.01em}
.site-card figcaption span{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-3);align-self:center}
.site-card:hover figcaption b{text-decoration:underline;text-decoration-color:var(--accent-ink);text-decoration-thickness:1.5px;text-underline-offset:3px}
@media (max-width:1024px){ .site-grid{grid-template-columns:repeat(2,minmax(0,1fr))} }
@media (max-width:600px){ .site-grid{grid-template-columns:minmax(0,1fr)} }
.gallery-note{margin-top:44px;font-family:var(--serif);font-size:22px;font-weight:300;letter-spacing:-.014em;text-align:center}

/* ---------- a grid of facts: two or three across, each on a hairline ---------- */
.fact-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0 44px;margin-top:56px}
.fact-grid.two{grid-template-columns:repeat(2,minmax(0,1fr))}
.fact{padding:26px 0 30px;border-top:1px solid var(--rule-2)}
.fact b{display:block;font-family:var(--serif);font-size:23px;font-weight:300;letter-spacing:-.014em;line-height:1.15;margin-bottom:10px}
.fact p{font-size:14.5px;line-height:1.55;letter-spacing:-.006em;color:var(--ink-2);text-wrap:pretty}
@media (max-width:900px){ .fact-grid,.fact-grid.two{grid-template-columns:minmax(0,1fr)} }

/* ---------- wired: the app and the site, and what runs between them ---------- */
.wired{display:grid;grid-template-columns:minmax(0,1fr) 220px minmax(0,1fr);gap:28px;align-items:center;margin-top:64px}
.wired-label{display:block;margin-top:16px;font-family:var(--mono);font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3);text-align:center}
.wired-mid{display:grid;gap:34px}
.flow-arrow{display:block;font-size:14px;font-weight:500;letter-spacing:-.01em;text-align:center}
.flow-arrow small{display:block;margin-top:4px;font-size:12.5px;font-weight:400;color:var(--ink-3);line-height:1.4}
.flow-arrow i{display:block;position:relative;height:1px;margin-top:12px;background:var(--rule-3)}
.flow-arrow i::after{content:"";position:absolute;right:-1px;top:-3.5px;border:4px solid transparent;border-left:6px solid var(--rule-3);border-right:0}
.flow-arrow.back i::after{right:auto;left:-1px;border-left:0;border-right:6px solid var(--rule-3)}
/* a dot of orange runs along each wire, one way each */
.flow-arrow i::before{content:"";position:absolute;top:-2px;left:0;width:5px;height:5px;border-radius:50%;background:var(--accent);animation:wire 2.8s cubic-bezier(.5,0,.5,1) infinite}
.flow-arrow.back i::before{animation-name:wireBack;animation-delay:1.4s}
@keyframes wire{0%{left:0;opacity:0}12%{opacity:1}88%{opacity:1}100%{left:calc(100% - 5px);opacity:0}}
@keyframes wireBack{0%{left:calc(100% - 5px);opacity:0}12%{opacity:1}88%{opacity:1}100%{left:0;opacity:0}}
.wired-cap{grid-column:1 / -1;margin-top:26px;text-align:center;font-family:var(--serif);font-size:22px;font-weight:300;letter-spacing:-.014em}
@media (max-width:1024px){
  .wired{grid-template-columns:minmax(0,1fr);gap:34px}
  .wired-mid{max-width:320px;margin:0 auto;width:100%}
}

/* ---------- prose: the parts that are an argument ---------- */
.prose{max-width:700px}
.prose .eyebrow{margin-bottom:22px}
.prose h2{margin-bottom:28px}
.prose p{font-size:17px;line-height:1.72;letter-spacing:-.008em;color:var(--ink-2);margin-bottom:18px;text-wrap:pretty}
.prose p:last-child{margin-bottom:0}
.prose strong{color:var(--ink);font-weight:500}
.prose em{font-style:italic}
.prose .pull{margin:34px 0;padding-left:22px;border-left:1px solid var(--accent);font-family:var(--serif);font-size:24px;font-weight:300;line-height:1.3;letter-spacing:-.014em;color:var(--ink)}
.callout{margin:26px 0;padding:22px 24px;background:var(--white);border:1px solid var(--rule);border-radius:10px}
.callout .eyebrow{margin-bottom:10px;color:var(--ink)}
.callout p{font-size:15px;line-height:1.6;margin:0}
/* two columns of who owns what */
.cols{display:grid;grid-template-columns:1fr 1fr;gap:0 40px;margin:36px 0 12px}
.cols .eyebrow{padding-bottom:12px;border-bottom:1px solid var(--rule-2);margin-bottom:6px;color:var(--ink)}
.cols ul{list-style:none;margin:0;padding:0}
.cols li{padding:10px 0;border-bottom:1px solid var(--rule);font-size:15px;color:var(--ink-2)}
.cols-cap{margin-top:18px;font-size:14.5px!important;color:var(--ink-3)!important}
@media (max-width:640px){ .cols{grid-template-columns:1fr;gap:28px} }

/* ---------- page by page ---------- */
/* a feature row used as a section's whole layout: no rule, no padding of its own */
.feature.plain{border-top:0;padding:0}
.feature.plain .sol-lede{margin-bottom:8px}
.feature.plain .steps-list{margin-top:32px}

/* AI: the captured reply stands in the hero, with the autopilot tag hung off
   its lower left corner so the two read as one object. Below the card, not
   on it: laid over the reply it hid the Cancel button, which is the point. */
.ai-hero-visual{max-width:420px;margin:0 auto;width:100%}
.ai-hero-card{border-radius:20px}
.ai-tag{
  position:absolute;left:-44px;bottom:-34px;z-index:2;max-width:236px;
  padding:14px 16px 15px 34px;background:var(--white);border:1px solid var(--rule);border-radius:10px;
  box-shadow:0 24px 50px -30px rgba(0,0,0,.45);
}
.ai-tag::before{content:"";position:absolute;left:16px;top:20px;width:7px;height:7px;border-radius:50%;background:var(--accent);animation:pulse 2.6s ease-in-out infinite}
.ai-tag b{display:block;font-size:13.5px;font-weight:500;letter-spacing:-.01em}
.ai-tag span{display:block;margin-top:3px;font-size:13px;line-height:1.45;color:var(--ink-2)}
@keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.45;transform:scale(.78)}}
@media (max-width:1024px){ .ai-tag{left:-12px} }
@media (max-width:600px){ .ai-tag{position:relative;left:auto;bottom:auto;margin:-40px 16px 0;max-width:none} }
.claims.guard,.claims.why{margin-top:56px;padding-top:34px;border-top:1px solid var(--rule-2)}

/* Websites */
.editor-row{margin-top:56px}
.editor-row .feature-visual{grid-column:1 / -1}
.editor-row.feature{grid-template-columns:minmax(0,1fr)}
.web-plans{margin-top:56px}
.plan .plan-sub strong{color:inherit;font-weight:600}
#offers .addons{margin-top:34px}
.own-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:72px;align-items:start}
.own-grid .prose{max-width:none}
.own-grid .prose h2{font-size:clamp(28px,3vw,36px)}
@media (max-width:1024px){ .own-grid{grid-template-columns:minmax(0,1fr);gap:64px} }
.ladder{display:grid;grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr);gap:56px;margin-top:80px;padding-top:40px;border-top:1px solid var(--rule-2)}
.ladder-head .eyebrow{margin-bottom:16px;color:var(--ink)}
.ladder-head p{font-size:15px;line-height:1.65;color:var(--ink-2);text-wrap:pretty}
.ladder-head strong{color:var(--ink);font-weight:500}
@media (max-width:1024px){ .ladder{grid-template-columns:minmax(0,1fr);gap:28px} }

/* Curriculum */
.cur-flow{margin-top:56px}
.report-facts{margin:8px 0 0;padding-bottom:72px}

/* ---------- motion ---------- */
.feature-visual .frame{perspective:1600px}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    /* the screen drifts against its row for the whole crossing; the copy a
       little less, so the two pull apart as the row goes by */
    .feature-visual{animation:driftUp linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
    .feature-copy{animation:driftUpSmall linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
    .feature-visual > *{animation:tiltIn linear both;animation-timeline:view();animation-range:entry 10% cover 40%}
    .page-hero-visual > *{animation:heroDrift linear both;animation-timeline:scroll(root);animation-range:0 90vh}
    .page-hero-copy{animation:foldOut linear both;animation-timeline:scroll(root);animation-range:0 80vh}
    .fact{animation:liftIn linear both;animation-timeline:view();animation-range:entry 18% cover 42%}
    .fact:nth-child(3n+2){animation-range:entry 24% cover 48%}
    .fact:nth-child(3n){animation-range:entry 30% cover 54%}
    .site-card{animation:liftIn linear both;animation-timeline:view();animation-range:entry 12% cover 38%}
    .site-card:nth-child(3n+2){animation-range:entry 18% cover 44%}
    .site-card:nth-child(3n){animation-range:entry 24% cover 50%}
    .wired > div:first-child{animation:driftUp linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
    .wired > div:nth-child(3){animation:driftDown linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
    .prose .pull{animation:stepIn linear both;animation-timeline:view();animation-range:entry 30% cover 50%}
    .cols li{animation:stepIn linear both;animation-timeline:view();animation-range:entry 20% cover 44%}
    /* these claims run to four lines, and a tall block reaches cover 44% late:
       it was still at .78 opacity with its top 200px from the top of the window */
    .claims.why .claim,.claims.guard .claim{animation-range:entry 8% cover 28%}
    .claims.why .claim:nth-child(2),.claims.guard .claim:nth-child(2){animation-range:entry 12% cover 32%}
    .claims.why .claim:nth-child(3),.claims.guard .claim:nth-child(3){animation-range:entry 16% cover 36%}
  }
}
@keyframes heroDrift{from{transform:none}to{transform:translateY(-70px)}}
@media (prefers-reduced-motion:reduce){
  .page-hero-copy > *,.page-hero-visual,.flow-arrow i::before,.ai-tag::before{animation:none}
  .fan .browser,.site-card .browser,.job,.rail-nav button,.rail-progress i{transition:none}
}
/* A hero with copy and no visual (pricing, features): one column, and no
   room reserved under it for a picture that is not there. */
.page-hero--text{padding-bottom:clamp(24px,4vw,48px)}
.page-hero--text > .wrap{grid-template-columns:minmax(0,1fr)}
.page-hero--text .page-hero-copy{max-width:640px}
/* A feature told in words alone (the seminars page): one column at reading
   width, not a two-column grid with an empty half. Four facts sit in four
   columns rather than three and a straggler. */
.feature:not(:has(.feature-visual)){grid-template-columns:minmax(0,1fr)}
.feature:not(:has(.feature-visual)) .feature-copy{max-width:64ch}
.fact-grid--4{grid-template-columns:repeat(4,minmax(0,1fr))}
@media (max-width:900px){.fact-grid--4{grid-template-columns:repeat(2,minmax(0,1fr))}}

/* ---- comparison pages (compare/*.html): one table, MatPilot beside one vendor,
   read on a phone as a scrolling block and on a desktop as a table. The first
   column is the row label, held sticky so a narrow screen keeps its bearings. */
.compare-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;margin:8px 0 12px}
.compare{width:100%;min-width:640px;border-collapse:separate;border-spacing:0;font-size:15px;line-height:1.5}
.compare th,.compare td{padding:14px 16px;border-bottom:1px solid var(--rule);vertical-align:top;text-align:left}
.compare thead th{font-family:var(--mono);font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-2);font-weight:500;border-bottom:1px solid var(--ink)}
.compare tbody th{font-weight:600;color:var(--ink);width:26%;position:sticky;left:0;background:var(--paper)}
.compare td{color:var(--ink-2);width:37%}
.compare td.us{color:var(--ink)}
.compare tr:last-child th,.compare tr:last-child td{border-bottom:0}
/* MatPilot's column is the ink column: black ground, paper text, the mark in the
   header; the vendor's header carries their own logo (Vinny, 14 Sep 2026). */
.compare th.c-mp,.compare td.us{background:var(--dark);color:var(--paper)}
.compare th.c-mp{border-bottom-color:rgba(255,255,255,.18);border-radius:12px 12px 0 0}
.compare td.us{border-bottom-color:rgba(255,255,255,.12)}
.compare tr:last-child td.us{border-radius:0 0 12px 12px}
.compare th.c-vendor{vertical-align:middle}
.c-logo{display:inline-flex;align-items:center;gap:9px;font-family:var(--sans);font-size:16px;font-weight:700;letter-spacing:-.01em;text-transform:none;color:inherit;line-height:1}
.c-logo svg{height:22px;width:auto;display:block}
.c-logo img{height:24px;width:auto;max-width:150px;display:block;object-fit:contain}
.c-logo--vendor{color:var(--ink)}
.compare-note{font-family:var(--mono);font-size:12.5px;letter-spacing:.02em;color:var(--ink-3);margin:14px 0 0}
.fit-grid{display:grid;grid-template-columns:1fr 1fr;gap:28px;margin-top:28px}
@media (max-width:760px){ .fit-grid{grid-template-columns:1fr} }
.fit{padding:26px 26px 28px;border:1px solid var(--rule);border-radius:14px;background:var(--paper)}
.fit h3{font-family:var(--serif);font-weight:400;font-size:26px;letter-spacing:-.02em;margin:0 0 12px}
.fit ul{margin:0;padding-left:18px}
.fit li{margin:0 0 8px;color:var(--ink-2)}
.fit li b{color:var(--ink);font-weight:600}

/* ---- a row of three framed screenshots with captions (comparison pages) */
.shot-row{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:28px 22px;margin-top:36px}
@media (max-width:760px){ .shot-row{grid-template-columns:1fr} }
/* .cmp-shot, not .shot: paper.css owns .shot for the home page's tabbed
   screenshots, where the image is hidden until it is the active tab. */
.cmp-shot{margin:0}
.cmp-shot .frame-top{padding:7px 7px 0}
.cmp-shot .frame-top::before{top:2.5px;width:4px;height:4px;margin-left:-2px}
.cmp-shot .frame-base{height:10px}
.cmp-shot figcaption{margin-top:16px;font-size:14px;line-height:1.5;letter-spacing:-.006em;color:var(--ink-2);text-wrap:pretty}

/* ---- hero blend: the product in the empty half of a text hero (Vinny, 14 Sep
   2026). A desktop screenshot in the laptop frame, the phone over its corner,
   and the mark drawn in behind at watermark strength. The phone chrome is
   paper.css's (.phone-float / .phone / .phone-bar / .shot). */
.hero-blend{position:relative;padding:8% 0 12% 0}
.hero-blend .frame{position:relative;z-index:1;margin-left:9%}
.hero-blend .hb-phone{position:absolute;left:-1%;bottom:0;width:27%;z-index:2}
.hero-blend .hb-mark{position:absolute;right:-4%;top:-6%;width:44%;height:auto;z-index:0;color:var(--ink);opacity:.07;pointer-events:none}
.hero-blend .hb-mark .d{fill:currentColor;fill-opacity:0;stroke:currentColor;stroke-width:1.1;stroke-linejoin:round;stroke-dasharray:420;stroke-dashoffset:420;animation:hbDraw 2.6s .5s cubic-bezier(.2,.7,.2,1) both}
.hero-blend .hb-mark .d:nth-child(2){animation-delay:.7s}
.hero-blend .hb-mark .d:nth-child(3){animation-delay:.9s}
.hero-blend .hb-mark .d:nth-child(4){animation-delay:1.1s}
@keyframes hbDraw{0%{stroke-dashoffset:420;fill-opacity:0}55%{stroke-dashoffset:0;fill-opacity:0}100%{stroke-dashoffset:0;fill-opacity:1}}
@media (prefers-reduced-motion:reduce){ .hero-blend .hb-mark .d{animation:none;stroke-dashoffset:0;fill-opacity:1} }
@media (max-width:1024px){ .hero-blend{max-width:640px} }

/* ---- hero vs: the vendor's own app beside MatPilot's (comparison pages). The
   vendor's shot is their App Store marketing frame, shown as it is in a plain
   card; MatPilot's is our phone chrome. Same height, a mono "vs" between. */
.hero-vs{position:relative;padding:4% 0 6%}
.hero-vs .hb-mark{position:absolute;right:-4%;top:-8%;width:44%;height:auto;z-index:0;color:var(--ink);opacity:.07;pointer-events:none}
.hero-vs .hb-mark .d{fill:currentColor;fill-opacity:0;stroke:currentColor;stroke-width:1.1;stroke-linejoin:round;stroke-dasharray:420;stroke-dashoffset:420;animation:hbDraw 2.6s .5s cubic-bezier(.2,.7,.2,1) both}
.hero-vs .hb-mark .d:nth-child(2){animation-delay:.7s}
.hero-vs .hb-mark .d:nth-child(3){animation-delay:.9s}
.hero-vs .hb-mark .d:nth-child(4){animation-delay:1.1s}
.vs-grid{position:relative;z-index:1;display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);gap:24px;align-items:center}
.vs-side{margin:0;display:flex;flex-direction:column;align-items:center;gap:16px}
.vs-card{width:min(100%,260px);border-radius:22px;overflow:hidden;border:1px solid var(--rule);background:var(--white);box-shadow:0 34px 60px -30px rgba(0,0,0,.35)}
.vs-card img{display:block;width:100%;height:auto;aspect-ratio:9/16;object-fit:cover;object-position:top}
.vs-phone{width:min(100%,220px)}
.vs-word{font-family:var(--serif);font-size:clamp(28px,3vw,40px);font-weight:300;letter-spacing:-.02em;color:var(--ink-3)}
.vs-side figcaption{display:flex;align-items:center;justify-content:center;min-height:24px}
.vs-side figcaption img{height:18px;width:auto;max-width:130px;display:block}
.vs-side .c-logo{font-size:15px}
.vs-side .c-logo svg{height:18px}
.vs-note{margin:22px 0 0;font-family:var(--mono);font-size:11.5px;letter-spacing:.03em;color:var(--ink-3);text-align:center}
@media (max-width:1024px){ .hero-vs{max-width:640px} }
@media (max-width:520px){ .vs-grid{gap:12px} .vs-card{border-radius:14px} }

/* ---- hero vs, wide: a desktop screen against a desktop screen, stacked. */
.hero-vs--wide{padding:2% 0 4%}
.vs-stack{position:relative;z-index:1;display:grid;gap:14px;justify-items:center}
.vs-stack .vs-side{width:100%;gap:12px}
.vs-card--wide{width:100%;border-radius:12px}
.vs-card--wide img{aspect-ratio:auto;object-fit:contain}
.vs-stack .frame{width:100%}
.vs-stack .frame-top{padding:7px 7px 0}
.vs-stack .frame-top::before{top:2.5px;width:4px;height:4px;margin-left:-2px}
.vs-stack .frame-base{height:10px}
.vs-stack .vs-word{font-size:clamp(22px,2.2vw,30px);line-height:1}

/* ---- only on MatPilot: six framed screens with a title and a line each. */
.only-grid{list-style:none;margin:36px 0 0;padding:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:34px 22px}
@media (max-width:760px){ .only-grid{grid-template-columns:1fr;gap:30px} }
.only-grid li{margin:0}
.only-grid figcaption b{display:block;font-size:17px;font-weight:500;letter-spacing:-.014em;margin-bottom:6px;color:var(--ink)}
.only-grid figcaption span{display:block;font-size:14px;line-height:1.55;color:var(--ink-2);text-wrap:pretty}
.only-shot{display:block;color:inherit;text-decoration:none}
.only-grid figcaption b a{color:inherit;text-decoration:none}
.only-grid figcaption b a:hover{text-decoration:underline;text-decoration-thickness:1.5px;text-underline-offset:3px}
.only-more{display:inline-block;margin-top:10px;font-family:var(--mono);font-size:12px;letter-spacing:.04em;text-transform:uppercase;color:var(--ink);text-decoration:none;border-bottom:1px solid var(--ink)}
.only-more i{font-style:normal}
