/* ============================================================================
   Agentidoo marketing site — shared design system.

   Ported from the Claude Design source `Agentidoo.dc.html`. Every page used to
   carry its own 260–400 line <style> block of the old dark theme; they now all
   link this one file instead, so the nav, footer, type scale and controls stay
   in step.

   Design language follows the Aimee style showcase: a slightly cool off-white
   ground, near-black ink, Archivo set large and light with tight tracking, warm
   hairline rules between full-bleed sections, letterspaced micro-labels
   instead of filled chips, and one violet accent (#71639E) spent on a single
   highlighted word rather than sprinkled around.
   ========================================================================= */

:root {
    /* Palette sampled from the Aimee style showcase. The ground is a hair
       cooler than pure white and the ink is a hair warmer than pure black —
       at a glance it reads as black-on-white, but large fields of it are
       noticeably softer. The accent is unchanged: the showcase's violet
       resolves to the same brand #71639E. */
    --ink:        #121016;   /* oklch(.09 .01 280)  */
    --ink-2:      #3a3745;
    --ink-3:      #5e5b68;   /* oklch(.43 .025 280) */
    --paper:      #ffffff;   /* the ground is plain white everywhere (2026-09-18); was #FAFAFB */
    --paper-pure: #ffffff;   /* cards/frames that need to lift off the ground */
    --ink-band:   #1B1B1B;   /* the near-black full-bleed band */
    --line:       #C5C1B9;   /* warm hairline, not a grey tint of the ink */
    --line-soft:  #E5E2DC;
    --accent:     #71639E;
    --accent-dk:  #5d5184;
    --accent-dkr: #4e4470;
    --accent-lt:  #8074ad;
    --ok:         #2e9e5b;

    /* The hero and every section share one horizontal inset so headings,
       rules and section bodies all land on the same two vertical lines. */
    /* The hero is a fixed frame whose axis moves with the window's height, so
       no width-only gutter can sit on its line at every size. Tying the page to
       that arithmetic aligned the edges and cost the page its width on a large
       screen, which read worse than the step it removed. So the gutter is its
       own value again, just a wider one than the 8.3vw it started at: 168px at
       1400, 230px at 1920, which keeps the sections clear of the edge without
       pinching the column. */
    --gutter: clamp(40px, 12vw, 240px);
    --nav-h: 64px;

    --r-pill: 999px;
    --r-card: 24px;
    --r-frame: 16px;
    --r-chip: 8px;
}

/* Gothic №60 — the hero's display face, self-hosted and subset to 4 KB.
   See fonts/README.md: the licence for web use still needs confirming, and
   removing this block is the only change needed to fall back to Archivo.
   `swap` so the hero word is never invisible while the font loads. */
@font-face {
    font-family: 'Gothic60';
    src: url('/assets/fonts/gothic60-subset.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

/* Darker Grotesque SemiBold — the wordmark face. Latin subset only, vendored
   from Google Fonts (OFL) so the logo never depends on the CDN. `block` not
   `swap`: the hero types the name letter by letter, and a fallback face
   flashing in mid-animation looks like a glitch, not a swap. */
@font-face {
    font-family: 'Darker Grotesque';
    src: url('/assets/fonts/darker-grotesque-600-latin.woff2') format('woff2');
    font-weight: 600;
    font-style: normal;
    font-display: block;
}

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
    margin: 0;
    background: var(--paper);
    color: var(--ink);
    font-family: 'Archivo', system-ui, -apple-system, 'Segoe UI', sans-serif;
    font-size: 16px;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

a { color: var(--ink); text-decoration: none; transition: color .18s ease; }
/* Hover darkens toward ink, it never turns violet (Christina, 2026-10-14):
   the violet read as a stray colour on black and grey text. */
a:hover { color: var(--ink); }
img, svg, video { max-width: 100%; }
::selection { background: var(--accent); color: #fff; }

/* The showcase's headings are large, light-weight and set very tight — the
   size does the emphasis, not the weight. Archivo is a neo-grotesque rather
   than the showcase's geometric face, so it reads narrower and more editorial
   at display sizes; the tracking below is tuned for that, not copied across. */
h1, h2, h3, h4 { margin: 0; font-weight: 400; letter-spacing: -0.035em; line-height: 0.98; }
h3, h4 { font-weight: 500; letter-spacing: -0.02em; line-height: 1.15; }
p { margin: 0; }

/* Full-bleed near-black band, the showcase's one dark surface. */
.band-dark {
    background: var(--ink-band);
    color: var(--paper);
}
.band-dark h1, .band-dark h2, .band-dark h3 { color: var(--paper); }
.band-dark p { color: rgba(255,255,255,.72); }

/* Respect a user's reduced-motion setting: the mascot idle and
   the scroll-driven hero all stop rather than merely slow down. */
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
    }
}

/* ---------------------------------------------------------------- layout -- */

.wrap { padding-left: var(--gutter); padding-right: var(--gutter); }

/* Sections are separated by whitespace alone — no full-bleed hairlines. The
   only rules left in the design are the short ones inside components (.ruled,
   .faq rows, the footer's baseline). */
/* Positioned so a full-bleed background from a neighbouring section can run
   underneath without covering this one's copy — a non-positioned later
   sibling paints BELOW an earlier positioned element, not above it. */
.section { position: relative; z-index: 1; padding: 96px var(--gutter); }
.section--tight { padding: 64px var(--gutter); }
.section--flush { padding: 0; }

/* ------------------------------------------------------------------ eyebrow */

/* The tag that opens each section. The showcase sets these as bare
   letterspaced micro-labels rather than filled chips — the accent is spent on
   one highlighted word per page instead of on every section marker, which is
   what keeps the violet feeling deliberate. Full ink rather than --ink-3: at
   12px and 0.16em these were reading as disabled text. */
.eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--ink);
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}
.eyebrow--invert { color: rgba(255,255,255,.75); }

.kicker {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.14em;
    color: var(--ink-3);
    text-transform: uppercase;
}

/* ------------------------------------------------------------------ buttons */

.btn {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    border-radius: var(--r-pill);
    padding: 15px 28px;
    font-size: 15.5px;
    font-weight: 600;
    border: 1px solid transparent;
    cursor: pointer;
    transition: background-color .18s ease, background-position .55s cubic-bezier(.32,.72,0,1),
                color .18s ease, border-color .18s ease, box-shadow .35s ease;
}
/* Black, not a violet fill — the accent is reserved for the highlighted words
   and the pricing band. Shared class, so the nav, hero and footer CTAs stay
   identical to one another. */
/* The brand gradient as one token: all three stops verbatim from
   brand/mascot.svg, on the diagonal the SVG runs (0,0 to 72,64 is about
   138deg). Every button that used to hover to flat Odoo violet now hovers
   into this instead, so the whole page has one accent object. */
:root {
    --ag-grad: linear-gradient(138deg, #8B5DC1 0%, #B07FF0 45%, #6F9BFF 100%);
}

/* Start free carries the mascot's own gradient, all three stops verbatim from
   brand/mascot.svg (#8B5DC1 to #B07FF0 to #6F9BFF) on the same diagonal the
   SVG runs (0,0 to 72,64 is about 138deg). Shared class, so the nav and hero
   CTAs stay identical.
   Hover keeps exactly these colours: the gradient is drawn larger than the
   button and slides, so the fill travels from the violet end towards the
   blue one. Feedback without a second palette. */
.btn--primary {
    background-image: var(--ag-grad);
    background-size: 190% 190%;
    background-position: 0% 0%;
    border-color: transparent; color: #fff;
}
.btn--primary:hover {
    background-position: 100% 100%;
    border-color: transparent; color: #fff;
    box-shadow: 0 14px 30px -14px rgba(139,93,193,.55);
}

/* Outline-only buttons are frosted, matching the header: a translucent tint
   plus a backdrop blur, so whatever is behind them shows through. Filled
   buttons (--primary, --on-accent) stay opaque — a blur under a solid fill
   costs a compositing layer and buys nothing.
   The tint must stay translucent or the backdrop-filter has nothing to do. */
.btn--outline {
    border-color: var(--ink);
    color: var(--ink);
    font-weight: 500;
    background: rgba(255,255,255,.16);
    backdrop-filter: blur(16px) saturate(1.6);
    -webkit-backdrop-filter: blur(16px) saturate(1.6);
}
/* Hover into the brand gradient, not flat Odoo violet. background-image so
   the frosted background-color underneath stops showing through. */
.btn--outline:hover {
    background-image: var(--ag-grad); background-color: transparent;
    background-size: 190% 190%; background-position: 100% 100%;
    border-color: transparent; color: #fff; backdrop-filter: none;
}

.btn--on-accent { background: #fff; border-color: #fff; color: var(--accent); }
.btn--on-accent:hover { background: rgba(255,255,255,.85); color: var(--accent-dkr); }

/* The light-on-violet variant of the same idea, for the pricing band. */
.btn--ghost-light {
    border-color: rgba(255,255,255,.7);
    color: #fff;
    font-weight: 500;
    background: rgba(255,255,255,.12);
    backdrop-filter: blur(16px) saturate(1.4);
    -webkit-backdrop-filter: blur(16px) saturate(1.4);
}
.btn--ghost-light:hover { background: #fff; color: #8B5DC1; }

.btn--sm { padding: 10px 22px; font-size: 14px; }

/* Circular outline arrow — the showcase's scroll cue and card affordance.
   Sized in px rather than em because it is a control, not a glyph in text. */
.btn-circle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 72px;
    height: 72px;
    flex-shrink: 0;
    border: 1px solid var(--ink);
    border-radius: 50%;
    background: rgba(255,255,255,.16);
    backdrop-filter: blur(16px) saturate(1.6);
    -webkit-backdrop-filter: blur(16px) saturate(1.6);
    color: var(--ink);
    font-size: 20px;
    cursor: pointer;
    transition: background .2s ease, color .2s ease, border-color .2s ease;
}
.btn-circle:hover { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.btn-circle--sm { width: 44px; height: 44px; font-size: 15px; }

/* ------------------------------------------------------- corner micro-labels */

/* The paired uppercase labels the showcase runs across the top of the hero —
   an index on the left, a statement of intent right-aligned on the right. */
.corner-labels {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 24px;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--ink);
}
.corner-labels > :last-child { text-align: right; line-height: 1.45; }

/* --------------------------------------------------------------------- nav */

/* Frosted glass at all times, so the hero gradient behind it shows through as
   colour rather than being hidden by an opaque bar. `.is-solid` — toggled from
   site.js once the wordmark has docked — only firms it up for the rest of the
   page, where there is no gradient to see and the links need a solid ground.

   The backdrop-filter is what does the work; the background tint must stay
   translucent or there is nothing to see through. */
#nav {
    position: fixed;
    inset: 0 0 auto 0;
    z-index: 90;
    display: flex;
    align-items: center;
    gap: 36px;
    height: var(--nav-h);
    /* Same gutter as the sections below, so the brand and the copy share a
       left edge. */
    padding: 0 var(--gutter);
    background: rgba(255,255,255,.14);
    border-bottom: 1px solid rgba(255,255,255,.22);
    backdrop-filter: blur(16px) saturate(1.6);
    -webkit-backdrop-filter: blur(16px) saturate(1.6);
    transition: background .35s ease, border-color .35s ease, backdrop-filter .35s ease,
                opacity .5s ease, transform .5s cubic-bezier(0.22, 1, 0.36, 1);
}

/* The landing page holds the bar back until the hero has finished arriving.
   site.js sets `nav-held` on <body> synchronously and clears it on the last
   step, so the bar drops in rather than sitting there through the intro.
   Scoped to .has-hero: the sub-pages must never hide their nav. */
body.has-hero.nav-held #nav {
    opacity: 0;
    transform: translateY(-100%);
    pointer-events: none;
}
#nav.is-solid {
    background: rgba(250,250,251,.72);
    border-bottom-color: var(--line-soft);
    backdrop-filter: blur(18px) saturate(1.4);
    -webkit-backdrop-filter: blur(18px) saturate(1.4);
}

/* The wordmark lives here on every page including the landing page — it is no
   longer revealed by the hero's scroll animation. */
.nav-logo { display: block; line-height: 0; margin-right: auto; }
/* The brand lockup: mascot, then the name in caps — Darker Grotesque
   SemiBold, same drawing as the logo file, built from live text so it takes
   the ink color wherever it sits. */
.brand-lockup {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-family: 'Darker Grotesque', 'Archivo', system-ui, sans-serif;
    font-size: 27px;
    font-weight: 600;
    letter-spacing: 0.005em;
    line-height: 1;
    text-transform: uppercase;
    color: var(--ink);
}
.brand-lockup img { width: 0.82em; display: block; }
.brand-lockup > span { transform: translateY(-0.045em); }

.nav-links {
    display: flex;
    align-items: center;
    gap: 32px;
    margin: 0 0 0 auto;
    padding: 0;
    list-style: none;
    font-size: 14px;
    font-weight: 500;
    letter-spacing: 0.02em;
}
.nav-cta { display: flex; align-items: center; gap: 12px; }
/* The phone menu's language row; hidden until the menu opens (see below). */
.nav-lang { display: none; margin: 0; }

.nav-burger {
    display: none;
    width: 40px; height: 40px;
    align-items: center; justify-content: center;
    margin-left: auto;
    border: 0;
    padding: 0;
    /* Bare icon — no chip. The nav already carries the glass; a second frosted
       tile on top of it read as a floating button rather than as part of the
       bar. The 40px box stays for the tap target. */
    background: none;
    cursor: pointer;
}
.nav-burger span { display: block; width: 16px; height: 1.5px; background: var(--ink); position: relative; }
.nav-burger span::before,
.nav-burger span::after { content: ''; position: absolute; left: 0; width: 16px; height: 1.5px; background: var(--ink); }
.nav-burger span::before { top: -5px; }
.nav-burger span::after { top: 5px; }

/* --------------------------------------------------------- page hero (sub) */

/* Sub-pages get a compact header instead of the landing page's full-height
   hero, so the nav has something to sit against. */
.page-head { padding: calc(var(--nav-h) + 84px) var(--gutter) 72px; }
.page-head h1 {
    margin: 32px 0 0;
    font-size: clamp(44px, 6vw, 96px);
    letter-spacing: -0.04em;
    line-height: 0.94;
}
.page-head .lede {
    margin: 24px 0 0;
    max-width: 620px;
    font-size: 17px;
    line-height: 1.6;
    color: var(--ink-2);
}

/* ------------------------------------------------------------------- prose */

/* Long-form pages (docs, privacy, terms, blog, changelog, about). */
.prose { max-width: 780px; font-size: 16px; line-height: 1.7; color: var(--ink-2); }
.prose > * + * { margin-top: 20px; }
.prose h2 {
    margin-top: 64px;
    padding-top: 28px;
    border-top: 1px solid var(--ink);
    font-size: clamp(26px, 2.4vw, 38px);
    color: var(--ink);
}
.prose h3 { margin-top: 40px; font-size: clamp(19px, 1.6vw, 24px); color: var(--ink); }
.prose h4 { margin-top: 28px; font-size: 16px; font-weight: 600; color: var(--ink); }
.prose ul, .prose ol { margin: 16px 0 0; padding-left: 22px; }
.prose li + li { margin-top: 8px; }
.prose strong { color: var(--ink); font-weight: 600; }
.prose a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.prose code {
    background: #F5F3F7;
    border: 1px solid var(--line);
    border-radius: 5px;
    padding: 1px 6px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: .9em;
    color: var(--accent-dkr);
    /* API paths have no spaces to break on, so a long one pushed the whole
       document 6px wider than a 390px phone. Let them break mid-token. */
    overflow-wrap: anywhere;
}
.prose pre {
    background: #FAF9FB;
    border: 1px solid var(--line);
    border-radius: var(--r-chip);
    padding: 18px 20px;
    overflow-x: auto;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 13.5px;
    line-height: 1.6;
    color: var(--ink);
}
.prose pre code { background: none; border: 0; padding: 0; color: inherit; }
.prose blockquote {
    border-left: 2px solid var(--accent);
    padding-left: 20px;
    color: var(--ink-2);
}
.prose table { width: 100%; border-collapse: collapse; font-size: 14.5px; }
.prose th, .prose td { border-bottom: 1px solid var(--line); padding: 12px 14px; text-align: left; }
.prose th { font-weight: 600; color: var(--ink); }

/* -------------------------------------------------------------------- icons */

/* Streamline Ultimate (free), CC BY 4.0 — see assets/icons/README.md.
   Sized in em and drawn in currentColor so a glyph takes the colour and
   scale of the text it sits beside, the way the emoji it replaced did. */
.icn {
    width: 1em;
    height: 1em;
    flex-shrink: 0;
    color: inherit;
    vertical-align: -0.125em;
}
.icn--lg { width: 1.5em; height: 1.5em; }

/* The bordered container used where an icon leads a card or a list row. */
.icn--chip {
    box-sizing: content-box;
    width: 20px;
    height: 20px;
    padding: 9px;
    border: 1px solid var(--line);
    border-radius: var(--r-chip);
    color: var(--accent);
    vertical-align: middle;
}

/* An icon + text row (feature bullets, contact cards). */
.icn-row { display: flex; align-items: flex-start; gap: 14px; }
.icn-row > .icn--chip { margin-top: 2px; }

/* -------------------------------------------------------------------- cards */

.card {
    border: 1px solid var(--line-soft);
    border-radius: var(--r-frame);
    padding: 28px;
    background: var(--paper-pure);
}
.card h3 { font-size: clamp(20px, 1.8vw, 26px); }
.card p { margin-top: 12px; font-size: 15px; line-height: 1.6; color: var(--ink-2); }

/* A column that opens with a hard rule — the design's recurring list unit. */
.ruled { border-top: 1px solid var(--ink); padding-top: 24px; }
.ruled h3 { font-size: clamp(22px, 1.8vw, 30px); letter-spacing: -0.01em; }
.ruled p { margin-top: 14px; font-size: 15px; line-height: 1.6; color: var(--ink-2); }

/* Video/screenshot frame: hairline border with the media inset by its padding. */
/* No chrome: the clips are their own composition and already sit on a pale
   wash, so a white mat plus a hairline border read as a second, competing
   frame — and the still images below them never had one. Corner radius moves
   onto the media itself now that there is no padding to round. */
.frame { border-radius: var(--r-frame); overflow: hidden; }
.frame video, .frame img { display: block; width: 100%; border-radius: var(--r-frame); }

.grid { display: grid; gap: 48px; }
.grid--2 { grid-template-columns: 1fr 1fr; }
.grid--3 { grid-template-columns: repeat(3, 1fr); }

/* --------------------------------------------------------------------- FAQ */

.faq { margin-top: 56px; border-top: 1px solid var(--line-soft); }
.faq details { border-bottom: 1px solid var(--line-soft); }
.faq summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
    padding: 26px 0;
    font-size: clamp(18px, 1.6vw, 24px);
    font-weight: 500;
    list-style: none;
    cursor: pointer;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary .sign { flex-shrink: 0; color: var(--accent); font-size: 26px; font-weight: 400; }
.faq details[open] summary .sign { transform: rotate(45deg); }
.faq summary .sign { transition: transform .2s ease; }
.faq p { padding: 0 0 26px; max-width: 720px; font-size: 15.5px; line-height: 1.65; color: var(--ink-2); }

/* ------------------------------------------------------------------ footer */

footer { padding: 0 var(--gutter) 48px; }

.foot-cta {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 20px;
    padding: 110px 0;
}
.foot-cta h2 { max-width: 800px; font-size: clamp(32px, 3.8vw, 58px); letter-spacing: -0.03em; line-height: 1.05; }
.foot-cta p { max-width: 480px; font-size: 16px; line-height: 1.6; color: var(--ink-2); }
.foot-cta .btn-row { display: flex; align-items: center; gap: 14px; margin-top: 16px; flex-wrap: wrap; justify-content: center; }
.foot-cta .btn { padding: 16px 34px; font-size: 16px; }

.foot-grid {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr 1fr;
    gap: 48px;
    padding-top: 72px;
}
.foot-brand .brand-lockup { font-size: 36px; }
.foot-brand p { margin-top: 24px; max-width: 300px; font-size: 14px; line-height: 1.6; color: var(--ink-3); }
.foot-col h4 { font-size: 15px; font-weight: 600; }
.foot-col ul { margin: 16px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 16px; font-size: 15px; }
.foot-col a { color: var(--ink-3); }
.foot-col a:hover { color: var(--ink); }

.foot-bot {
    display: flex;
    justify-content: space-between;
    gap: 24px;
    border-top: 1px solid var(--line-soft);
    margin-top: 72px;
    padding-top: 24px;
    font-size: 13px;
    color: var(--ink-3);
    flex-wrap: wrap;
}
.foot-bot a { color: var(--ink-3); }
.foot-bot-links { display: inline-flex; gap: 20px; }

/* --------------------------------------------------------------- responsive */

@media (max-width: 1080px) {
    .grid--3 { grid-template-columns: 1fr 1fr; }
    .foot-grid { grid-template-columns: 1fr 1fr; gap: 40px; }
}

@media (max-width: 860px) {
    :root { --gutter: 24px; }
    .nav-links, .nav-cta { display: none; }
    .nav-burger { display: inline-flex; }

    /* --- the mobile menu ---
       site.js toggles `nav-open` on <body>; these are the rules that make
       that mean something. The bar itself grows into a panel: #nav is a flex
       row that is allowed to wrap, the logo keeps `margin-right:auto` so it
       and the burger hold the first line, and the two full-width children
       below it become the stacked menu. No extra markup, so all eight pages
       get the same menu from the shared nav they already share. */
    body.nav-open { overflow: hidden; }
    body.nav-open #nav {
        height: auto;
        flex-wrap: wrap;
        row-gap: 4px;
        padding-bottom: 22px;
        /* Glass, like the closed bar. This LEANS ON the backdrop blur for
           legibility — the hero's display type is 100px+ and reads straight
           through an unblurred panel of this opacity. Chrome and Safari blur
           it; Playwright's WebKit does NOT, so the mobile captures in
           landing-shots look far worse than the real thing. Check on a device
           before lowering the alpha. */
        background: rgba(250,250,251,.62);
        border-bottom-color: rgba(255,255,255,.28);
        backdrop-filter: blur(36px) saturate(1.9);
        -webkit-backdrop-filter: blur(36px) saturate(1.9);
        -webkit-backdrop-filter: blur(36px) saturate(1.9);
    }
    body.nav-open .nav-logo {
        display: flex;
        align-items: center;
        height: var(--nav-h);
    }
    body.nav-open .nav-burger { align-self: center; height: 40px; }

    /* The burger is last in the DOM, so without an explicit order the two
       full-width children wrap ahead of it and the close button lands at the
       bottom of the panel. Pushing them to later orders keeps the logo and the
       burger together on the first line. */
    body.nav-open .nav-links,
    body.nav-open .nav-cta {
        display: flex;
        flex-direction: column;
        align-items: stretch;
        width: 100%;
        margin: 0;
    }
    /* The desktop rule sets gap:32px between the inline links; stacked, that
       becomes 32px on top of each row's own padding. */
    body.nav-open .nav-links { order: 1; gap: 0; }
    body.nav-open .nav-cta { order: 2; }
    body.nav-open .nav-links a {
        display: block;
        padding: 15px 0;
        font-size: 17px;
        border-bottom: 1px solid var(--ink);
    }
    body.nav-open .nav-cta { gap: 10px; margin-top: 20px; }
    body.nav-open .nav-cta .btn { justify-content: center; padding: 14px 22px; font-size: 15px; }
    /* The language switch, on pages that exist in both languages: the
       header's toggle at menu width, after the links (same order, later in
       the DOM) and before the buttons, and drawn like them: the track is the
       outline button's, the knob the black one's. The knob carries a white
       copy of the names that it moves the other way, so a name turns from
       black to white exactly where the knob passes over it; knob and copy
       follow data-current, which nav.js flips before the other page loads. */
    body.nav-open .nav-lang {
        --lang-pos: 0;
        position: relative;
        display: grid;
        grid-template-columns: 1fr 1fr;
        order: 1;
        width: 100%;
        height: 50px;
        margin: 22px 0 4px;
        padding: 4px;
        border: 1px solid var(--line);
        border-radius: 999px;
        background: rgba(255, 255, 255, .5);
    }
    body.nav-open .nav-lang[data-current="en"] { --lang-pos: 1; }
    .nav-lang a,
    .nav-lang a:hover,
    .nav-lang .lang-knob__labels span {
        display: grid;
        place-items: center;
        border-radius: 999px;
        font-size: 15px;
        font-weight: 500;
        color: var(--ink);
        text-decoration: none;
    }
    .nav-lang .lang-knob {
        position: absolute;
        top: 4px;
        left: 4px;
        width: calc(50% - 4px);
        height: calc(100% - 8px);
        border-radius: 999px;
        overflow: hidden;
        pointer-events: none;
        background: var(--ink);
        transform: translateX(calc(var(--lang-pos) * 100%));
        transition: transform .28s cubic-bezier(.32,.72,0,1);
    }
    .nav-lang .lang-knob__labels {
        display: grid;
        grid-template-columns: 1fr 1fr;
        width: 200%;
        height: 100%;
        transform: translateX(calc(var(--lang-pos) * -50%));
        transition: transform .28s cubic-bezier(.32,.72,0,1);
    }
    .nav-lang .lang-knob__labels span { color: #fff; }

    /* Burger becomes a close cross. */
    body.nav-open .nav-burger span { background: transparent; }
    body.nav-open .nav-burger span::before { top: 0; transform: rotate(45deg); }
    body.nav-open .nav-burger span::after { top: 0; transform: rotate(-45deg); }
    .nav-burger span::before,
    .nav-burger span::after { transition: transform .2s ease, top .2s ease; }
    /* The nav bar is the only chrome on a phone, so it is always legible
       rather than transparent over the hero. */
    #nav {
        background: rgba(255,255,255,.16);
        border-bottom-color: rgba(255,255,255,.22);
        backdrop-filter: blur(18px) saturate(1.6);
        -webkit-backdrop-filter: blur(18px) saturate(1.6);
    }
    .nav-logo .brand-lockup { font-size: 23px; }

    .section { padding: 64px var(--gutter); }
    .grid--2, .grid--3 { grid-template-columns: 1fr; gap: 32px; }
    .foot-grid { grid-template-columns: 1fr; gap: 32px; }
    .foot-cta { padding: 72px 0; }
    .page-head { padding: calc(var(--nav-h) + 48px) var(--gutter) 48px; }
}

/* ── Start screen ──────────────────────────────────────────────────────────
   The picture the dashboard shows while it boots, on the site: the mark and
   one small word on the site's own paper, in front of everything until the
   page is dressed.

   Every rule hangs off `ag-booting`, which assets/boot.js puts on <html>
   from the document head. Nothing here is the page's default state: if the
   script is missing or blocked, no class is ever set, the veil stays
   invisible and the hero arrives exactly as it did before this existed. */
.ag-boot {
    position: fixed;
    inset: 0;
    z-index: 900;
    display: grid;
    place-items: center;
    background: var(--paper);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    /* Leaving: the paper fades and only then stops taking the page's place,
       so the last frame of the fade is still the veil and not a jump. */
    transition: opacity .42s ease, visibility 0s linear .42s;
}

html.ag-booting .ag-boot {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    /* Arriving is not a transition: the veil is simply there on the first
       frame. Only the removal of the class animates. */
    transition: none;
}

.ag-boot__in {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 20px;
}

.ag-boot__mark {
    display: block;
    width: 24px;
    height: auto;
    color: var(--ink);
    opacity: .9;
}

.ag-boot__label {
    margin: 0;
    /* The one piece of type on the page that must not wait for the webfont:
       the start screen is up precisely because Archivo has not arrived yet,
       and a swap under the visitor's eyes is the flicker this is here to
       prevent. At 12px with this much tracking the difference is nil. */
    font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
    color: var(--ink-3);
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}

/* The page does not scroll while it is covered. Without this the document
   moves under a veil that does not, so the start screen could lift onto a
   viewport that has already left the hero it was holding back. The gutter is
   reserved so that giving the scrollbar back is not a 15px jump on the
   platforms that draw a classic one. */
html.ag-booting {
    overflow: hidden;
    scrollbar-gutter: stable;
}

/* The hero builds itself in pure CSS from its first painted frame (see the
   arrival block in hero.css). Behind the veil that would be a performance
   nobody watches, so while the veil is up the arrival simply does not exist,
   and putting the names back is what starts it, from its own first frame.

   Not `animation-play-state: paused`: that holds every piece at the `from`
   frame, which is opacity 0, and an element whose first paint is transparent
   is not a Largest Contentful Paint candidate. The hero would have handed
   that measurement to the word "Loading". Cancelled instead, the hero paints
   itself at full strength behind the veil and stays the page's own LCP. */
html.ag-booting .fig-hero__wash,
html.ag-booting .fig-hero__canvas > *,
html.ag-booting .fig-hero__cursor {
    animation-name: none;
}
