/* GrowAgent — growagent.dev

   THE DESIGN SYSTEM IS MAYA'S, taken from the product itself
   (maya.growagent.dev, Maya's own app/globals.css) and requested directly:
   "let's use maya.growagent.dev design elements .. theme colour everything
   except logo".

   WHAT THAT REPLACED: an editorial pass modelled on inworld.ai -- Gambetta
   serif on warm #FAF7F5 paper with a cyan accent. It was a good page and it
   was a SECOND design system. A marketing site that looks nothing like the
   product it sells makes signing up feel like arriving somewhere else, and
   of the three surfaces (this site, the console, Maya) two already agreed
   with each other. This one now agrees too.

   WHAT CAME ACROSS, verbatim: Maya's oklch palette in both themes -- warm
   near-paper ground, warm-dark ink text, indigo primary, and the semantic
   status colours (ochre for scheduled, green for published) that mean the
   same thing in the product. Maya's three faces: Newsreader on titles, IBM
   Plex Sans on body and UI, IBM Plex Mono on anything that lines up in a
   column. Maya's 0.625rem radius scale.

   WHAT DID NOT, and why:

   - THE LOGO. Excluded by the request, and rightly: the lockup is the one
     asset every surface shares, so it is the thing that must NOT be
     re-themed per site. Both on-light and on-dark twins stay as they are.

   - `.on-ink` AND THE FOOTER BAND. Maya's landing page has no dark band
     device, but this site's rhythm is built on one, so `--ink` survives as
     a band fill -- in Maya's indigo rather than the old near-black, and,
     unlike before, it FLIPS in dark mode (see the dark block) because a
     fixed indigo band would have vanished into an indigo ground.

   FONT HOSTING: `style-src`/`font-src` in vercel.json now allow Google
   Fonts, where all three of Maya's faces live. The previous version of this
   comment argued against exactly that for IBM Plex Mono -- "a difference
   nobody can see at that size" -- which was true when the site had its own
   type. It stops being true the moment the goal is to match the product. */

/* NO @import HERE, DELIBERATELY -- the fonts are <link>ed from each page's
   <head> instead, and that is a performance fix rather than a style choice.

   AN @import INSIDE THIS FILE SERIALISES THREE ROUND TRIPS. Measured on the
   live site: styles.css started at 48ms and finished at 89ms; the font CSS
   could not even be DISCOVERED until 90ms, and the font files not until
   91ms. The browser cannot begin any of it until the previous hop has been
   downloaded and parsed, because each URL is only visible inside the
   response before it:

       HTML -> styles.css -> fonts CSS -> the font files

   On this connection each hop cost ~40ms and the total looked harmless. On
   a real cold connection each hop is a DNS lookup, a TLS handshake and a
   round trip -- 300-600ms apiece -- so the same structure costs well over a
   second before any text can be laid out in its real face. A <link> in the
   <head> collapses that: the browser sees styles.css AND the font CSS in
   the first parse of the HTML and fetches them in parallel, and a
   preconnect to fonts.gstatic.com warms the connection the font FILES will
   need while the CSS that names them is still in flight.

   THE COST of moving them out is that every page must carry the link tags,
   and a new page that forgets them gets fallback type. That is the trade:
   one duplicated three-line block per page against a serialised chain on
   every first visit. */


:root {
  /* ---- MAYA'S PALETTE, light. Every value is copied from Maya's own
     `:root` rather than re-derived, so the two surfaces cannot drift. ---- */

  /* The dark BAND fill -- `.on-ink` sections and the footer. Maya's own
     dark-mode ground, used here as a band on paper. Unlike every other
     brand literal this one flips in dark mode; see the dark block. */
  --ink: oklch(0.155 0.014 265);
  --white: #FFFFFF;

  /* THE ACCENT IS MAYA'S INDIGO PRIMARY, not the old cyan. Kept under the
     name `--cyan` because nine rules reference it and renaming them buys
     nothing; `--cyan-dark` is its dark-mode twin, also Maya's own. */
  --cyan: oklch(0.32 0.14 265);
  --cyan-dark: oklch(0.78 0.09 265);

  --paper: oklch(0.97 0.008 75);   /* Maya --background: warm, not white */
  --mist: oklch(0.93 0.012 70);    /* Maya --muted */

  /* Maya's three faces, each with the same job it has in the product.

     THE WEIGHTS REQUESTED IN EACH PAGE'S <head> ARE AUDITED AGAINST THIS
     FILE, not guessed, and the audit is worth 352KB. Measured, latin subsets
     only, which is all an English page fetches:

       Newsreader with the `opsz` 6..72 axis, 3 instances   319.2 KB
       Newsreader without `opsz`, 400 + italic 400           45.8 KB
       IBM Plex Sans as 3 static weights                    117.9 KB
       IBM Plex Sans as one variable range, 400..700         39.3 KB
       ------------------------------------------------------------
       whole request, before                                456.8 KB
       whole request, now                                   104.8 KB

     THREE FINDINGS, in order of size:

     1. THE `opsz` AXIS COST 182KB and bought nothing. Newsreader's optical
        size axis makes the served file carry every size from 6pt to 72pt;
        this site sets an explicit `font-size` everywhere, so it was paying
        for a range it never varies.
     2. A VARIABLE RANGE BEAT THREE STATICS for IBM Plex Sans -- 39.3KB in
        one file against 117.9KB in three, and it covers every weight in
        between rather than only the three named. (IBM Plex Mono has no
        variable version on Google Fonts; its two statics stay.)
     3. NEWSREADER 500 COST 91KB and had exactly two consumers, `.agent b`
        and `.footer-mark-word`, both small text where 400 reads the same.
        Both are 400 now.

     An earlier pass also asked for IBM Plex Sans 600, which nothing used,
     while six rules used 700 and got a synthetic smear of 500 instead.

     ADDING A WEIGHT TO A RULE HERE means checking the link in all six
     pages -- and measuring, because the cost is not proportional to the
     number of weights. */
  --font-display: 'Newsreader', ui-serif, Georgia, serif;
  --font-sans: 'IBM Plex Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* Five carry information, and these are now MAYA'S semantic colours --
     the same hues the product paints a post status in, so a pill on this
     page and a pill in the app mean the same thing by sight. One meaning
     per colour, held everywhere. Never let this group do the brand's job. */
  --compound: oklch(0.48 0.09 222);   /* retained lessons, carried forward — text safe */
  --memory: oklch(0.42 0.12 284);     /* agent state, recall, stored context — text safe */
  --signal: oklch(0.52 0.16 352);     /* replies, meetings, anything a human did */
  --experiment: oklch(0.6 0.15 75);   /* live, unproven — Maya's `scheduled` ochre */
  --revenue: oklch(0.48 0.09 155);    /* money that arrived — Maya's `published` green */

  /* Text on the ochre fill. This used to borrow `--ink`, which breaks the
     moment `--ink` starts flipping for dark mode: a pill's fill does not
     flip, so its label must not either.

     DARKER THAN MAYA'S OWN --accent-foreground (L 0.36), which measured
     2.78:1 against the L 0.6 ochre -- below AA for 13px text. Maya can
     live with its version because it pairs the saturated ochre with a
     LIGHT TINT background (its --status-scheduled-bg, L 0.94); this site's
     pills are filled, per the token note above ("fill only"), so the label
     has to carry the contrast instead. At L 0.2 it measures 4.55:1 on the
     light ochre and 8.95:1 on the lightened dark-mode one, so one value
     covers both modes and no dark twin is needed. */
  --on-warm: oklch(0.2 0.045 60);

  /* The label on a FILLED information pill. White in light mode, where
     every information hue sits at L 0.42-0.6. It needs to be a token
     rather than `--white` because the dark block lightens those same
     hues to L 0.72 to make them legible on a dark ground -- at which
     point a white label on them falls to 2.4:1. Same pill, opposite
     label, one token. */
  --on-info: #FFFFFF;

  --hairline: oklch(0.87 0.014 60);   /* Maya --border */
  --ink-70: oklch(0.38 0.018 52);
  --ink-55: oklch(0.46 0.02 55);      /* Maya --muted-foreground */
  --on-dark-70: rgba(255, 255, 255, 0.70);
  --on-dark-55: rgba(255, 255, 255, 0.55);

  /* MAYA'S RADIUS SCALE. Its `--radius` is 0.625rem, and `radius-md` --
     what every button and input in the product uses -- is 0.8 of that. The
     old 6px/16px pair was tuned to a serif display this site no longer
     has, and a 16px card read noticeably rounder than the same card in the
     app. */
  --r-control: 0.5rem;
  --r-card: 0.625rem;
  --r-pill: 999px;

  /* ---- THE GROUND-DEPENDENT LAYER ----------------------------------
     Every token below has exactly ONE role, which is what lets the dark
     block redefine it and nothing else. Each maps to the Maya token named
     in its comment.

     WHY A SEPARATE LAYER AT ALL: `--white` does double duty -- text on a
     dark band AND, before this, the fill of a card -- and `--ink` is now
     the band fill itself. Reusing either for a role that flips would have
     made `.on-ink` a white band with white text on it. So the literals
     above stay literal, and the roles that FLIP get their own names. */
  --text: oklch(0.22 0.015 50);           /* Maya --foreground */
  --surface: oklch(0.995 0.004 75);       /* Maya --card: lifts off the ground */
  --band: oklch(0.93 0.012 70);           /* Maya --muted: section banding */
  --veil: oklch(0.97 0.008 75 / 0.85);    /* the sticky header, over content */
  --solid: oklch(0.32 0.14 265);          /* Maya --primary: the button fill */
  --solid-text: oklch(0.99 0.004 75);     /* Maya --primary-foreground */

  /* THE STICKY HEADER'S REAL HEIGHT, measured rather than guessed: 96px
     with the nav and theme toggle on one row. Every anchor target offsets
     by this plus a breath, so following a `#` link never lands with the
     heading tucked under the header. The two places that need it used a
     hand-written 90px, which was 6px short. */
  --header-h: 96px;
  --anchor-offset: calc(var(--header-h) + 16px);

  --measure: 62ch;
  --gutter: clamp(20px, 5vw, 40px);
  --max: 1140px;
}

/* ---------- Dark mode ----------

   THREE BLOCKS, and all three are load-bearing:

   1. The bare `:root` above holds the COMPLETE light palette. No colour
      gets its only definition inside a media query, so a browser that
      matches neither block still renders a finished page.
   2. `prefers-color-scheme` is the default -- someone whose OS is dark
      gets a dark site with no click and no stored preference. It is
      guarded by `:not([data-theme="light"])` so that choosing Light
      explicitly beats the OS setting.
   3. `[data-theme="dark"]` lets the toggle win in the other direction
      too: dark site on a light OS. /theme.js stamps the attribute on
      <html> before first paint.

   The ground-dependent tokens are redefined, plus `--ink` (the band fill,
   which has to move -- see its note below) and the five information
   colours. `--white` alone stays fixed: it is the text on a dark band in
   both modes. */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
  /* MAYA'S `.dark` BLOCK, copied value for value. Its dark theme is an
     indigo-neutral one rather than a darkened version of its own warm
     light palette, so these are not derived from the `:root` above. */
  --paper: oklch(0.155 0.014 265);   /* Maya --background */
  --surface: oklch(0.205 0.016 265); /* Maya --card */
  --band: oklch(0.27 0.018 265);     /* Maya --muted */
  --mist: oklch(0.27 0.018 265);
  --veil: oklch(0.155 0.014 265 / 0.85);

  --text: oklch(0.965 0.004 265);    /* Maya --foreground */
  --ink-70: oklch(0.82 0.012 265);
  --ink-55: oklch(0.7 0.015 265);    /* Maya --muted-foreground */
  --hairline: oklch(1 0 0 / 12%);    /* Maya --border */

  /* Maya's dark `--primary`, which is the light indigo -- its light-mode
     indigo (L 0.32) is unreadable on this ground. */
  --cyan: oklch(0.78 0.09 265);
  --solid: oklch(0.78 0.09 265);
  --solid-text: oklch(0.17 0.03 265);  /* Maya --primary-foreground */

  /* `--ink` IS THE ONE BRAND LITERAL THAT FLIPS, and it has to. It is the
     `.on-ink`/footer band fill, and in light mode it IS Maya's dark ground
     (L 0.155) -- which is now also this mode's page ground, so a fixed
     value would have made every band invisible. Lifting it to 0.235 keeps
     the band one clear step above the ground, the same relationship it has
     on paper, and keeps white legible on it. `--white` does not flip: it
     is the text ON this band in both modes. */
  --ink: oklch(0.235 0.018 265);

  /* The information colours lift too. Maya defines light-mode values only
     (its own dark palette is flagged in its globals.css as pending a
     design pass), so these raise each hue's lightness enough to read on
     the dark ground while holding the hue exactly -- the hue is the part
     that carries the meaning. */
  --compound: oklch(0.72 0.1 222);
  --memory: oklch(0.72 0.12 284);
  --signal: oklch(0.72 0.15 352);
  --experiment: oklch(0.78 0.13 75);
  --revenue: oklch(0.72 0.13 155);
  /* ...and so the filled pills' label inverts with them. */
  --on-info: oklch(0.2 0.02 265);
  }
  /* THE LOCKUP IS A TWO-TONE ASSET, navy plus teal -- on a dark ground its
     navy half all but disappears, so this swaps in the real on-dark file
     rather than filtering the on-light one. Same pair, same reasoning, as
     the app's header (see the console's components/AppShell.tsx). */
  :root:not([data-theme="light"]) .logo-light { display: none; }
  :root:not([data-theme="light"]) .logo-dark { display: block; }
}

:root[data-theme="dark"] {
  /* MAYA'S `.dark` BLOCK, copied value for value. Its dark theme is an
     indigo-neutral one rather than a darkened version of its own warm
     light palette, so these are not derived from the `:root` above. */
  --paper: oklch(0.155 0.014 265);   /* Maya --background */
  --surface: oklch(0.205 0.016 265); /* Maya --card */
  --band: oklch(0.27 0.018 265);     /* Maya --muted */
  --mist: oklch(0.27 0.018 265);
  --veil: oklch(0.155 0.014 265 / 0.85);

  --text: oklch(0.965 0.004 265);    /* Maya --foreground */
  --ink-70: oklch(0.82 0.012 265);
  --ink-55: oklch(0.7 0.015 265);    /* Maya --muted-foreground */
  --hairline: oklch(1 0 0 / 12%);    /* Maya --border */

  /* Maya's dark `--primary`, which is the light indigo -- its light-mode
     indigo (L 0.32) is unreadable on this ground. */
  --cyan: oklch(0.78 0.09 265);
  --solid: oklch(0.78 0.09 265);
  --solid-text: oklch(0.17 0.03 265);  /* Maya --primary-foreground */

  /* `--ink` IS THE ONE BRAND LITERAL THAT FLIPS, and it has to. It is the
     `.on-ink`/footer band fill, and in light mode it IS Maya's dark ground
     (L 0.155) -- which is now also this mode's page ground, so a fixed
     value would have made every band invisible. Lifting it to 0.235 keeps
     the band one clear step above the ground, the same relationship it has
     on paper, and keeps white legible on it. `--white` does not flip: it
     is the text ON this band in both modes. */
  --ink: oklch(0.235 0.018 265);

  /* The information colours lift too. Maya defines light-mode values only
     (its own dark palette is flagged in its globals.css as pending a
     design pass), so these raise each hue's lightness enough to read on
     the dark ground while holding the hue exactly -- the hue is the part
     that carries the meaning. */
  --compound: oklch(0.72 0.1 222);
  --memory: oklch(0.72 0.12 284);
  --signal: oklch(0.72 0.15 352);
  --experiment: oklch(0.78 0.13 75);
  --revenue: oklch(0.72 0.13 155);
  /* ...and so the filled pills' label inverts with them. */
  --on-info: oklch(0.2 0.02 265);
}
/* THE LOCKUP IS A TWO-TONE ASSET, navy plus teal -- on a dark ground its
   navy half all but disappears, so this swaps in the real on-dark file
   rather than filtering the on-light one. Same pair, same reasoning, as
   the app's header (see the console's components/AppShell.tsx). */
:root[data-theme="dark"] .logo-light { display: none; }
:root[data-theme="dark"] .logo-dark { display: block; }

/* ---------- Theme toggle ----------

   The same three-option control as the app's header (Auto / Light / Dark),
   at the same 11px mono, so the two surfaces do not disagree about what a
   theme switch looks like. AUTO IS AN OPTION AND THE DEFAULT: a two-state
   switch would have to guess an initial value and thereby override the OS
   setting for everyone who never touches it.

   PROGRESSIVE ENHANCEMENT, not decoration: the markup is hidden until
   /theme.js marks it live, because a control that cannot do anything
   should not be on the page. With JavaScript off the site still renders
   both palettes correctly -- via `prefers-color-scheme` -- so nothing is
   actually lost. */
.theme-toggle { display: none; }
.theme-toggle[data-live] {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
}
.theme-toggle button {
  appearance: none;
  border: 0;
  background: transparent;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: var(--r-pill);
  color: var(--ink-55);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1;
}
.theme-toggle button:hover { color: var(--text); }
/* `aria-pressed` IS the styling hook, so the visible state and the state
   announced to a screen reader cannot drift apart. */
.theme-toggle button[aria-pressed="true"] {
  background: var(--band);
  color: var(--text);
}
.theme-toggle button:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 1px;
}
/* Below the mobile breakpoint the header is already carrying the lockup,
   Sign in and Sign up. A three-option control is the first thing that
   should give up its space -- and `prefers-color-scheme` still applies,
   because Auto is the default. */
/* Below the breakpoint the toggle moves INSIDE the hamburger panel rather
   than disappearing, so a phone is not the one place the theme cannot be
   changed. `.nav > .theme-toggle` is the header-bar copy; the panel has its
   own. */
@media (max-width: 720px) { .nav > .theme-toggle[data-live] { display: none; } }

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--text);
  font-family: var(--font-sans);
  /* BODY · 400 · 0 · 1.6 */
  font-weight: 400;
  font-size: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

/* ---------- Type: five steps, no in-between ---------- */

/*
  DISPLAY AND HEADINGS ARE THE SERIF at weight 400, which is exactly how
  Maya sets every title it renders (`font-serif ... font-normal
  tracking-tight`). The face is now Newsreader, the product's own, so a
  headline here and a headline in the app are the same object at different
  sizes. The tight tracking and 1.04 leading are what keep a light serif
  authoritative rather than delicate at 5rem.
*/
.display {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: -0.022em;
  line-height: 1.04;
  /* THE FLOOR DROPPED FROM 2.75rem TO 2.125rem. At 44px on a 375px screen
     the hero headline broke to roughly seven characters a line and the first
     viewport was almost entirely one sentence -- a desktop display size
     shipped to a phone. 34px is the size the blog post's own h1 already
     resolves to on mobile, where it reads well. The ceiling and the growth
     rate are unchanged, so nothing above 600px moves. */
  font-size: clamp(2.125rem, 1.1rem + 5.6vw, 5.25rem);
  margin: 0;
}

/*
  The italic accent phrase inside a headline -- inworld puts one clause of
  its h1 in italic serif in the accent colour, and it is what stops a
  long serif headline reading as a wall. Cyan, not their green: see the
  file header.
*/
.display em {
  font-style: italic;
  color: var(--cyan);
  font-weight: 400;
}
.on-ink .display em { color: var(--cyan-dark); }

h2 {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: -0.018em;
  line-height: 1.14;
  font-size: clamp(1.875rem, 1.3rem + 2.1vw, 2.875rem);
  margin: 0;
}

/*
  h3 IS SERIF TOO NOW, reversing an earlier call. The previous version set
  card titles in sans 700 on the argument that a serif "loses its
  distinction" at 1.1875rem -- but Maya titles every card and every log
  entry in the serif at exactly this size (`font-serif text-xl
  font-normal`), and it works there because the WEIGHT does the separating,
  not the face: serif 400 against sans 400 body copy at 500 is already two
  clearly different registers. Matching the product matters more than the
  earlier preference, and the product is the one with users looking at it
  all day.
*/
h3 {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: -0.015em;
  line-height: 1.3;
  font-size: 1.1875rem;
  margin: 0;
}

.small {
  /* SMALL · 500 · 0 · 1.5 */
  font-weight: 500;
  font-size: 0.9375rem;
  line-height: 1.5;
  letter-spacing: 0;
}

/*
  EYEBROW IS MONO, and now QUIET rather than accent-coloured. Maya paints
  every uppercase mono micro-label -- column heads, slot counts, dates,
  provenance captions -- in `text-muted-foreground`, never in the primary.
  That restraint is most of why the product's chrome recedes and its
  content does not, and a coloured label on this site was the loudest
  remaining disagreement with it. Section labels only, never a sentence.
*/
.eyebrow {
  font-family: var(--font-mono);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: 0.6875rem;
  line-height: 1.5;
  color: var(--ink-55);
  margin: 0 0 20px;
}
.on-ink .eyebrow { color: var(--on-dark-55); }

.lede { font-size: clamp(1.0625rem, 1rem + 0.4vw, 1.25rem); max-width: 54ch; }
p { max-width: var(--measure); }
a { color: var(--cyan); text-underline-offset: 3px; text-decoration-thickness: 1px; }
.on-ink a { color: var(--cyan-dark); }

/* ---------- Layout ---------- */

.wrap { max-width: var(--max); margin: 0 auto; padding: 0 var(--gutter); }
section { padding: clamp(72px, 10vw, 132px) 0; }
/* ANY SECTION THAT CAN BE JUMPED TO clears the sticky header. Without this,
   following `#week` or `#agents` scrolls the section's own heading under the
   header and the reader lands mid-paragraph. Only `.article` headings had it,
   which covered the blog and nothing else. */
section[id] { scroll-margin-top: var(--anchor-offset); }
.on-ink { background: var(--ink); color: var(--white); }
/*
  The banding tone is WARM now. `--mist` (#F6F5FB) is a cool lavender-grey
  and sat visibly blue against the paper ground -- two different whites
  reading as a rendering fault rather than as a band. This is paper,
  darkened, so the band reads as the same surface with more weight.
*/
.on-mist { background: var(--band); }
.stack > * + * { margin-top: 20px; }

/* ---------- Header ---------- */

/*
  A LIGHT HEADER ON PAPER, not the ink bar this had. inworld's header is
  the same ground as its hero with only a hairline under it, which is what
  lets the serif headline be the first thing the eye lands on -- a dark
  bar above a light hero takes that job instead. Sticky with a blur, so
  the nav stays reachable through a long page.
*/
header {
  position: sticky;
  top: 0;
  z-index: 50;
  /* The positioning context for `.nav-panel`, which anchors to `top: 100%`. */
  background: var(--veil);
  backdrop-filter: saturate(140%) blur(10px);
  padding: 16px 0;
  border-bottom: 1px solid var(--hairline);
}
.header-row { display: flex; align-items: center; justify-content: space-between; gap: 24px; }
/* Tagline lockup: first contact only. Drop the tagline everywhere it repeats. */
.logo-tagline { height: 34px; width: auto; display: block; }
/* THE LIGHT COPY IS THE DEFAULT, and the on-dark twin is hidden until one
   of the two dark selectors above shows it. Stated here in the base rules
   rather than only inside the media query, so a browser that matches
   neither dark block renders exactly one lockup rather than both. */
.logo-dark { display: none; }
.nav { display: flex; align-items: center; gap: 22px; }
.nav a {
  color: var(--ink-70);
  text-decoration: none;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.nav a:hover { color: var(--text); }
/*
  SPECIFICITY, not decoration. `.nav a` is (class + type) and `.btn-primary`
  is (class), so the nav's link colour was winning inside the header and
  painting the primary button's label ink-on-ink -- a dark rectangle with
  no visible text. These two rules restate the button's own colours at a
  specificity that beats `.nav a`.
*/
.nav .btn-primary { color: var(--solid-text); }
.nav .btn-primary:hover { color: var(--solid-text); }
/* ---------- The mobile menu ----------

   A <details> ELEMENT, NOT JAVASCRIPT. Open, close, keyboard operation and
   focus all come from the platform; `summary` is already a button to a screen
   reader and already in the tab order. This is the same choice the product's
   own account menu makes, and the reason is the same: it costs nothing, and on
   a site whose CSP was `script-src 'none'` until last week, a nav that needs a
   script to be usable is a nav that breaks for anyone the script fails for.

   WHAT IT REPLACED: `.nav-links { display: none }` and nothing else. Below
   720px the four section links were simply gone -- no menu, no replacement --
   so How it works, Agents, Maya and Blog were unreachable on a phone. The
   links were in the markup the whole time, hidden.

   ONE HONEST LIMITATION: <details> does not close on an outside click without
   JavaScript. It closes on Escape, on a second tap of the button, and on any
   link inside it (because that navigates). Tapping the page behind it leaves
   it open. That is a real if minor wart, and cheaper than the alternative.
*/
.nav-menu { display: none; }
.nav-menu > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  /* 44px: the minimum comfortable touch target. The glyph inside is 18px, so
     the padding is doing the accessibility work, not the icon. */
  width: 44px;
  height: 44px;
  margin-right: calc(var(--gutter) * -0.25);
  border-radius: var(--r-control);
  color: var(--text);
}
.nav-menu > summary::-webkit-details-marker { display: none; }
.nav-menu > summary::marker { content: ''; }
.nav-menu > summary:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
/* Three bars drawn in CSS -- no icon font, no SVG request, and it inherits
   `currentColor` so it themes for free. */
.nav-menu .bars { position: relative; width: 18px; height: 2px; background: currentColor; border-radius: 2px; }
.nav-menu .bars::before,
.nav-menu .bars::after {
  content: '';
  position: absolute;
  left: 0;
  width: 18px;
  height: 2px;
  background: currentColor;
  border-radius: 2px;
  transition: transform .18s ease;
}
.nav-menu .bars::before { top: -6px; }
.nav-menu .bars::after { top: 6px; }
/* Open state: the bars become an X. The middle bar is the one that vanishes. */
.nav-menu[open] .bars { background: transparent; }
.nav-menu[open] .bars::before { transform: translateY(6px) rotate(45deg); }
.nav-menu[open] .bars::after { transform: translateY(-6px) rotate(-45deg); }
@media (prefers-reduced-motion: reduce) {
  .nav-menu .bars::before, .nav-menu .bars::after { transition: none; }
}

/* The panel. Anchored to the header's own bottom edge and full-bleed, so it
   reads as the header extending rather than as a floating card. */
/* HIDDEN EXPLICITLY WHEN CLOSED, rather than trusting the <details> element
   to do it. Measured: with `position: absolute` on the panel, this Chrome laid
   the panel's children out anyway -- the theme toggle inside it rendered at the
   header's right edge, 119px past the viewport, and gave the whole page a
   horizontal scrollbar on a phone. A closed <details> hides its content
   through `::details-content`, and an out-of-flow child slipped that. One
   declaration, and the behaviour no longer depends on how a given engine
   implements the slot. */
.nav-menu:not([open]) .nav-panel { display: none; }
.nav-panel {
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px var(--gutter) 18px;
  background: var(--paper);
  border-bottom: 1px solid var(--hairline);
  box-shadow: 0 12px 24px -12px rgba(0, 0, 0, 0.18);
  /* The panel can be taller than a short phone in landscape. */
  max-height: calc(100dvh - 100%);
  overflow-y: auto;
}
/* NOT `.nav a`'s 11px mono uppercase. In a stacked panel these are the primary
   navigation rather than header chrome, so they get a real reading size and a
   44px row -- the mono micro-label treatment is for a horizontal bar. */
.nav-panel a {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0 4px;
  font-family: var(--font-sans);
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--text);
  text-decoration: none;
}
.nav-panel a:hover { color: var(--cyan); }
.nav-panel hr { border: 0; border-top: 1px solid var(--hairline); margin: 8px 0; width: 100%; }
.nav-panel .btn { justify-content: center; margin-top: 4px; }
.nav-panel .theme-toggle[data-live] { align-self: flex-start; margin-top: 10px; }

@media (max-width: 720px) {
  .nav-links { display: none; }
  /* The two header actions move into the panel too. Three items plus a
     hamburger does not fit a 320px bar, which is what forced the row to wrap
     and made the header two lines tall. */
  .nav > a { display: none; }
  .nav-menu { display: block; }
}
@media (min-width: 721px) {
  /* Belt to the braces: the panel is inside <details>, so it is already
     hidden when closed, but this guarantees it can never appear on desktop
     even if the element is left open by a resize. */
  .nav-panel { display: none; }
}

/* ---------- Buttons: ink primary, cyan secondary, outline escape hatch.
              Hover darkens; nothing scales. ---------- */

/*
  MONO, UPPERCASE, 12px -- inworld's button treatment, and the reason its
  CTAs read as precise rather than loud next to a big serif. The label
  gets wide tracking to stay legible at that size, and the radius drops to
  6px (--r-control) so a button reads as a machined control rather than a
  soft app pill.
*/
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 13px 20px;
  border-radius: var(--r-control);
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none; border: 1px solid transparent; cursor: pointer;
  transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
.btn-primary { background: var(--solid); color: var(--solid-text); }
/* `color-mix` rather than another hard-coded hex: #0D0819 was a manual
   darkening of the OLD near-black primary and stayed behind when the palette
   became Maya's indigo, so the button's hover was quietly the wrong hue. */
.btn-primary:hover { background: color-mix(in oklab, var(--solid) 88%, var(--text)); }
.btn-outline { background: transparent; color: var(--text); border-color: var(--hairline); }
.btn-outline:hover { border-color: var(--ink-55); }
/* On ink, the primary flips so it stays the heaviest thing on the surface */
.on-ink .btn-primary { background: var(--white); color: var(--ink); }
.on-ink .btn-primary:hover { background: color-mix(in oklab, var(--white) 92%, var(--ink)); }
.on-ink .btn-outline { color: var(--white); border-color: rgba(255,255,255,0.28); }
.on-ink .btn-outline:hover { border-color: var(--white); }
.btn-row { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 34px; }

:where(a, button):focus-visible {
  outline: 2px solid var(--cyan-dark);
  outline-offset: 3px;
  border-radius: 4px;
}

/* ---------- Cards: one shadow OR one border per surface, never both ---------- */

.card {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
  padding: 28px;
}
/* On paper, a white card now genuinely lifts -- which is the point of
   moving the ground off pure white. On the darker band it needs no border
   to separate. */
.card-ink { background: rgba(255,255,255,0.05); border: none; border-radius: var(--r-card); padding: 28px; }
.grid { display: grid; gap: 18px; }
.grid-2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); }
.card p, .card-ink p { max-width: 46ch; }
.card .small, .card-ink .small { color: var(--ink-70); }
.card-ink .small { color: var(--on-dark-70); }

/* ---------- Status: filled pills. Never outlined — it reads as disabled. ---------- */

.pill {
  display: inline-block;
  border-radius: var(--r-pill);
  padding: 5px 13px;
  font-weight: 700; font-size: 0.8125rem; line-height: 1.4;
  letter-spacing: -0.005em;
}
.pill-compound   { background: var(--compound);   color: var(--on-info); }
.pill-memory     { background: var(--memory);     color: var(--on-info); }
.pill-signal     { background: var(--signal);     color: var(--on-info); }
.pill-experiment { background: var(--experiment); color: var(--on-warm); }

/* ---------- The seam: a reused rule, since the mark is a staircase ---------- */

.seam { height: 1px; background: var(--hairline); border: 0; margin: 0; }
.on-ink .seam { background: rgba(255,255,255,0.12); }

/* ---------- Then / now pairs ---------- */

.pair { display: grid; grid-template-columns: 1fr; gap: 6px; padding: 20px 0; border-bottom: 1px solid var(--hairline); }
.pair:last-child { border-bottom: 0; }
.pair-a { font-weight: 700; letter-spacing: -0.03em; font-size: 1.0625rem; }
.pair-b { color: var(--ink-70); }
@media (min-width: 760px) {
  .pair { grid-template-columns: 1fr 1fr; gap: 32px; align-items: baseline; }
}

/* ---------- The agent stack ----------
   Replaces the old flat `.motion` rows. The structure being drawn has real
   shape -- three foundation stages, three named go-to-market motions, then
   two layers that sit under all of them -- and a flat list of fifteen names
   threw that away. Each LAYER is a labelled band; each GROUP inside it is a
   card; each AGENT is a row with its own one-line job.

   The layer label is mono uppercase and sits in its own narrow column on
   wide screens, so the eye reads down the spine of the stack rather than
   left-to-right across unrelated groups. */

.layer {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
  padding: 34px 0;
  border-top: 1px solid var(--hairline);
}
.layer:first-of-type { border-top: 0; padding-top: 0; }
@media (min-width: 900px) {
  .layer { grid-template-columns: 200px 1fr; gap: 40px; }
}
.layer-label { display: flex; flex-direction: column; gap: 6px; }
.layer-name {
  font-family: var(--font-mono);
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-size: 0.6875rem;
  color: var(--cyan);
}
.layer-note { color: var(--ink-55); font-size: 0.875rem; line-height: 1.45; }

.groups { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 240px), 1fr)); }
.group {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
  padding: 20px;
}
.group-name {
  font-family: var(--font-mono);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-size: 0.625rem;
  color: var(--ink-55);
  margin-bottom: 14px;
}

.agent { display: flex; align-items: baseline; gap: 10px; padding: 7px 0; }
.agent + .agent { border-top: 1px solid var(--hairline); }
.agent b {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.0625rem;
  letter-spacing: -0.01em;
  flex: 0 0 auto;
}
.agent span { color: var(--ink-70); font-size: 0.875rem; line-height: 1.4; }
/* "Live" earns a filled pill; everything else says nothing at all rather
   than wearing a "soon" badge fifteen times over. */
.agent .live {
  font-family: var(--font-mono);
  font-size: 0.5625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: var(--revenue);
  color: var(--on-info);
  border-radius: var(--r-pill);
  padding: 2px 7px;
  margin-left: auto;
  flex: 0 0 auto;
}

/* ---------- Footer ---------- */

footer { background: var(--ink); color: var(--on-dark-70); padding: 56px 0 40px; }
.footer-row { display: flex; flex-wrap: wrap; gap: 20px 34px; align-items: center; justify-content: space-between; }
footer a { color: var(--on-dark-70); text-decoration: none; font-size: 0.9375rem; font-weight: 500; }
footer a:hover { color: var(--white); text-decoration: underline; }
footer .small { color: var(--on-dark-55); }

/* ---------- Legal pages ---------- */

/* ---------- Hero visual: the compounding staircase ----------
   The mark's own story, made literal and moving: five steps, each one
   taller than the last, rising in sequence and settling into a gentle
   float. The five colours are the SAME five the rest of the brand uses
   for "things that compound" (--compound/--memory/--signal/--experiment/
   --revenue) -- not decoration borrowed from nowhere. */
.hero-grid { display: grid; grid-template-columns: 1fr; gap: 56px; align-items: center; }
@media (min-width: 900px) {
  .hero-grid { grid-template-columns: 1.05fr 0.95fr; gap: 40px; }
}
.hero-visual { display: flex; align-items: flex-end; justify-content: center; gap: clamp(10px, 1.6vw, 18px); height: clamp(220px, 28vw, 320px); padding: 0 clamp(8px, 2vw, 24px); }
.hero-step {
  flex: 1 1 0;
  max-width: 74px;
  /* 6px, matching the control radius, and only on the top corners -- the
     steps sit ON the baseline, so rounding the bottom would float them. */
  border-radius: 6px 6px 0 0;
  opacity: 0;
  transform: translateY(24px);
  animation: hero-step-rise 700ms cubic-bezier(0.16, 1, 0.3, 1) forwards, hero-step-float 4200ms ease-in-out 900ms infinite;
}
.hero-step:nth-child(1) { height: 34%; background: var(--memory); animation-delay: 0ms, 900ms; }
.hero-step:nth-child(2) { height: 52%; background: var(--compound); animation-delay: 90ms, 990ms; }
.hero-step:nth-child(3) { height: 70%; background: var(--experiment); animation-delay: 180ms, 1080ms; }
.hero-step:nth-child(4) { height: 86%; background: var(--signal); animation-delay: 270ms, 1170ms; }
.hero-step:nth-child(5) { height: 100%; background: var(--revenue); animation-delay: 360ms, 1260ms; }
@keyframes hero-step-rise {
  to { opacity: 1; transform: translateY(0); }
}
@keyframes hero-step-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-6px); }
}
@media (prefers-reduced-motion: reduce) {
  .hero-step { animation: hero-step-rise-reduced 1ms linear forwards; }
  @keyframes hero-step-rise-reduced { to { opacity: 1; transform: translateY(0); } }
}

/* ---------- Hover lift: cards and pairs feel touchable, nothing scales ---------- */
.card, .card-ink { transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease; }
.card:hover, .card-ink:hover { transform: translateY(-3px); box-shadow: 0 12px 28px -12px rgba(23,15,46,0.22); }
.card-ink:hover { box-shadow: 0 12px 28px -12px rgba(0,0,0,0.45); border-color: rgba(255,255,255,0.14); }
@media (prefers-reduced-motion: reduce) {
  .card, .card-ink { transition: none; }
  .card:hover, .card-ink:hover { transform: none; }
}
.pair { transition: transform 160ms ease, border-color 160ms ease; }
.pair:hover { transform: translateX(10px); border-color: var(--cyan); }
@media (prefers-reduced-motion: reduce) {
  .pair { transition: border-color 160ms ease; }
  .pair:hover { transform: none; }
}

/* ---------- A wash behind the hero ----------
   Retuned for PAPER, not ink. The original was two bright cyan/violet
   radials at 0.16 alpha, which is right on a dark ground and invisible on
   a light one. These are the same two hues at much lower alpha, so the
   top of the page is warm-tinted rather than flat -- the effect inworld
   gets from a near-imperceptible gradient behind its hero. */
.hero-glow {
  position: relative;
  isolation: isolate;
}
.hero-glow::before {
  content: "";
  position: absolute;
  inset: -10% -10% auto -10%;
  height: 80%;
  background: radial-gradient(55% 55% at 25% 15%, rgba(10,124,153,0.07), transparent 70%),
              radial-gradient(45% 45% at 88% 5%, rgba(108,75,244,0.06), transparent 70%);
  z-index: -1;
  pointer-events: none;
}
/* On an ink section the wash has to go back to the bright values, or it
   disappears entirely. */
.on-ink.hero-glow::before {
  background: radial-gradient(60% 60% at 30% 20%, rgba(77,232,255,0.16), transparent 70%),
              radial-gradient(50% 50% at 85% 10%, rgba(108,75,244,0.14), transparent 70%);
}

.legal { padding: clamp(48px, 6vw, 76px) 0 96px; }
.legal h2 { margin-top: 52px; }
.legal h3 { margin-top: 30px; }
.legal p, .legal li { max-width: 68ch; }
.legal ul { padding-left: 22px; }
.legal li + li { margin-top: 8px; }
.legal-meta { color: var(--ink-55); }
.legal table { border-collapse: collapse; width: 100%; max-width: 68ch; margin-top: 18px; font-size: 0.9375rem; }
.legal th, .legal td { text-align: left; padding: 11px 14px; border-bottom: 1px solid var(--hairline); vertical-align: top; }
.legal th { font-weight: 700; letter-spacing: -0.01em; }
.table-scroll { overflow-x: auto; }
.callout { background: var(--mist); border-radius: var(--r-card); padding: 24px; margin-top: 26px; }
.callout p { max-width: 62ch; }
.callout :first-child { margin-top: 0; }
.back { display: inline-block; margin-bottom: 28px; font-weight: 700; font-size: 0.9375rem; text-decoration: none; }
.back:hover { text-decoration: underline; }

/* ---------- Breadcrumbs ----------
   Visible crumbs matching the BreadcrumbList schema on the same page. Schema
   that describes navigation the page does not actually show is the kind of
   mismatch Google treats as a penalty risk, so these two always ship
   together. */
.crumbs {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-55);
  margin-bottom: 22px;
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
.crumbs a { color: var(--ink-55); text-decoration: none; }
.crumbs a:hover { color: var(--cyan); text-decoration: underline; }

/* ---------- Blog index ---------- */

.post-list { list-style: none; margin: 0; padding: 0; }
.post-item + .post-item { margin-top: 14px; }
.post-link {
  display: block;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
  padding: 28px;
  text-decoration: none;
  color: inherit;
  transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease;
}
.post-link:hover {
  transform: translateY(-3px);
  box-shadow: 0 12px 28px -12px rgba(23,15,46,0.22);
  border-color: rgba(23,15,46,0.18);
}
@media (prefers-reduced-motion: reduce) {
  .post-link { transition: border-color 180ms ease; }
  .post-link:hover { transform: none; box-shadow: none; }
}
.post-meta {
  display: flex; gap: 10px; flex-wrap: wrap;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-55);
}
.post-title {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: -0.018em;
  line-height: 1.15;
  font-size: clamp(1.5rem, 1.2rem + 1.1vw, 2rem);
  margin: 12px 0 0;
}
.post-excerpt { color: var(--ink-70); margin: 12px 0 0; max-width: 68ch; }
.post-more {
  display: inline-block;
  margin-top: 16px;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cyan);
}

/* ---------- Article (blog post template) ----------
   A reading measure, not the full 1140px grid: long-form prose at container
   width is unreadable, and every AEO gain here depends on the piece actually
   being read. */
.article { max-width: 74ch; }
.article > * + * { margin-top: 22px; }
.article h2 {
  margin-top: 52px;
  /* Question-shaped H2s are the AEO device on this page type -- they give an
     answer engine an extractable question/answer pair per section. */
  scroll-margin-top: var(--anchor-offset);
}
.article h3 { margin-top: 34px; scroll-margin-top: var(--anchor-offset); }
.article ul { padding-left: 22px; color: var(--ink-70); }
.article li + li { margin-top: 8px; }
.article p { color: var(--ink-70); max-width: 74ch; }
.article strong { color: var(--text); font-weight: 700; }

/* The answer block: the self-contained 130-170 word paragraph directly under
   an H2, which is the unit an answer engine actually lifts.

   MARKED BY TYPE, NOT BY A BOX. This was a white card with a 2px cyan
   left border, which is the single most recognisable tell of a machine-made
   interface -- and it appeared six times on one page, in a template every
   future post copies. It also fought the design it sits in: the page is an
   editorial layout that leads sections with type and whitespace, and
   inworld (the reference for this pass) uses no bordered callouts at all.

   So the answer now simply READS as the lede of its section: a step up in
   size and full-strength ink against the `--ink-70` body copy around it.
   That still tells a writer at a glance whether a section has an answer
   paragraph -- the thing the box was for -- without a decorative stripe,
   and it leaves exactly one filled box on the page (the takeaways), which
   is what makes that one mean something. */
.answer p {
  margin: 0;
  color: var(--text);
  font-size: clamp(1.0625rem, 1rem + 0.35vw, 1.1875rem);
  line-height: 1.6;
  max-width: 70ch;
}

.byline {
  display: flex; gap: 10px; flex-wrap: wrap; align-items: baseline;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-55);
  margin-top: 20px;
}

/* Key-takeaways box: the extractable summary near the top. Answer engines
   quote this disproportionately, and a human skimming gets the piece in
   fifteen seconds. */
.takeaways {
  background: var(--band);
  border-radius: var(--r-card);
  padding: 24px 26px;
}
.takeaways .footer-col-title { margin-bottom: 12px; }
.takeaways ul { margin: 0; padding-left: 20px; }
.takeaways li { color: var(--ink-70); }

/* FAQ block. FAQPage rich results were retired May 2026, so this is here for
   AI citation and for readers -- not for a SERP accordion. Plain details/
   summary: no JavaScript, which this site's CSP forbids anyway. */
.faq { border-top: 1px solid var(--hairline); }
.faq details { border-bottom: 1px solid var(--hairline); padding: 4px 0; }
.faq summary {
  cursor: pointer;
  list-style: none;
  padding: 16px 0;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; float: right; color: var(--cyan); font-weight: 400; }
.faq details[open] summary::after { content: "–"; }
.faq details p { margin: 0 0 18px; color: var(--ink-70); }

/* ---------- Footer: navigation, AI summarise, animated mark ---------- */

.footer-mark {
  display: inline-flex; align-items: center; gap: 12px;
  text-decoration: none;
}
.footer-mark-word {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.375rem;
  letter-spacing: -0.02em;
  color: var(--white);
}
/*
  The staircase, attaching and detaching -- the same gesture as the product's
  loading mark (apps/console/components/AgentWorking.tsx), so the motion
  means the same thing on the marketing site as it does in the app. Translate
  only: cheap to composite, and the values are in the SVG's own viewBox units
  so the motion scales with the mark.
*/
.ga-stair-a { animation: ga-stair-a 3600ms cubic-bezier(0.45, 0, 0.55, 1) infinite; }
.ga-stair-b { animation: ga-stair-b 3600ms cubic-bezier(0.45, 0, 0.55, 1) infinite; }
@keyframes ga-stair-a {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(-8px, -8px); }
}
@keyframes ga-stair-b {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(8px, 8px); }
}
@media (prefers-reduced-motion: reduce) {
  .ga-stair-a, .ga-stair-b { animation: none; }
}

.footer-nav {
  display: grid;
  gap: 32px 28px;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr));
}
.footer-col { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
.footer-col-title {
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 0.625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--on-dark-55);
  margin: 0;
}
.footer-col a { color: var(--on-dark-70); text-decoration: none; font-size: 0.9375rem; }
.footer-col a:hover { color: var(--white); text-decoration: underline; }
.footer-soon { color: var(--on-dark-55); font-size: 0.9375rem; }

/* The AI column spans two tracks where there is room -- five assistant chips
   in a 160px column wrap into an unreadable stack. */
.footer-col-ai { grid-column: span 1; }
@media (min-width: 900px) { .footer-col-ai { grid-column: span 2; } }
.ai-links { display: flex; flex-wrap: wrap; gap: 8px; }
.ai-links a {
  border: 1px solid rgba(255,255,255,0.22);
  border-radius: var(--r-control);
  padding: 7px 12px;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--on-dark-70);
  text-decoration: none;
  transition: border-color .15s ease, color .15s ease;
}
.ai-links a:hover { border-color: var(--cyan-dark); color: var(--cyan-dark); text-decoration: none; }
.footer-ai-note { color: var(--on-dark-55); font-size: 0.8125rem; margin: 4px 0 0; max-width: 40ch; }

/* ================================================================
   MAYA'S LANDING PAGE, ported to /maya

   These rules exist because the content of maya.growagent.dev's own
   root page moved here, markup-for-markup, and that page is built from
   objects this site did not have: a real month grid, the product's
   status pills, and a dated log whose left rail is the ruler.

   Requested directly: "why this subdomain is still accessible .. this
   should be behind the gate and move the content of this (exact to)
   /maya website page". The subdomain's root now redirects here.

   EVERY COLOUR BELOW IS MAYA'S OWN STATUS TOKEN, not a new invention --
   the pills on this page and the pills in the product are the same
   object, which is the point of moving the page rather than rewriting
   it. Scoped under `.maya-doc` so nothing here can leak onto the
   homepage or the blog.
   ================================================================ */

.maya-doc {
  /* Maya's five post statuses, as tint + saturated text. This is the
     product's OWN pill pattern (`bg-status-X-bg text-status-X`), which is
     why these are tints rather than the filled `.pill-*` chips the
     homepage uses for its motion labels -- two different objects that
     would otherwise look alike. */
  --st-draft: oklch(0.46 0.02 55);
  --st-draft-bg: oklch(0.93 0.012 70);
  /* ONE STEP DARKER THAN MAYA'S OWN `--status-scheduled` (L 0.6), and the
     only value on this page that is not the product's verbatim. Maya's
     pairing measures 3.36:1 against its own tint background -- below AA for
     the 11px text these pills carry, and there are thirteen of them in the
     grid. L 0.52 holds the hue exactly (75) and measures 4.7:1, so it still
     reads as the same ochre at a glance. Worth fixing here rather than
     copying a defect faithfully; worth fixing in Maya too. */
  --st-scheduled: oklch(0.52 0.13 75);
  --st-scheduled-bg: oklch(0.94 0.05 78);
  --st-published: oklch(0.42 0.085 155);
  --st-published-bg: oklch(0.93 0.035 155);
}

/* Maya's dark theme defines --accent/--accent-foreground for the ochre and
   leaves the rest of its status set at light values, flagged in its own
   globals.css as pending a design pass. These hold each hue and swap the
   lightness roles, which is the same transformation its accent makes. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .maya-doc {
    --st-draft: oklch(0.8 0.015 70);
    --st-draft-bg: oklch(0.29 0.014 265);
    --st-scheduled: oklch(0.85 0.09 78);
    --st-scheduled-bg: oklch(0.29 0.035 72);
    --st-published: oklch(0.84 0.09 155);
    --st-published-bg: oklch(0.28 0.04 155);
  }
}
:root[data-theme="dark"] .maya-doc {
  --st-draft: oklch(0.8 0.015 70);
  --st-draft-bg: oklch(0.29 0.014 265);
  --st-scheduled: oklch(0.85 0.09 78);
  --st-scheduled-bg: oklch(0.29 0.035 72);
  --st-published: oklch(0.84 0.09 155);
  --st-published-bg: oklch(0.28 0.04 155);
}

/* ---------- The status pill ---------- */
.st {
  display: inline-block;
  border-radius: var(--r-control);
  padding: 2px 6px;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.st-draft     { background: var(--st-draft-bg);     color: var(--st-draft); }
.st-scheduled { background: var(--st-scheduled-bg); color: var(--st-scheduled); }
.st-published { background: var(--st-published-bg); color: var(--st-published); }

/* ---------- The month grid ---------- */

/* WIDE CONTENT SCROLLS INSIDE ITS OWN CONTAINER, which is Maya's own rule
   for this object: seven columns must never push the page sideways on a
   phone. The 30rem min-width is the app's. */
.month {
  overflow-x: auto;
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
  background: var(--surface);
}
.month-inner { min-width: 30rem; }
.month-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  border-bottom: 1px solid var(--hairline);
  padding: 12px 16px;
}
.month-name { font-family: var(--font-display); font-size: 1rem; color: var(--text); }
.month-count {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink-55);
}
.month-dow {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  border-bottom: 1px solid var(--hairline);
  background: var(--band);
}
.month-dow span {
  padding: 8px;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--ink-55);
}
/* The 1px gap IS the gridline: the cells paint the surface and the grid
   paints the border colour behind them. */
.month-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 1px;
  background: var(--hairline);
}
.month-cell { min-height: 64px; background: var(--surface); padding: 8px; }
.month-day {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-55);
}
.month-cell.outside .month-day { opacity: 0.5; }
.month-slot { margin-top: 6px; }
@media (min-width: 640px) { .month-cell { min-height: 80px; } }

/* The one authored motion moment on the page, carried over from Maya: the
   slots settle in, staggered, once. Everything else here is static. The
   stagger delay is set inline per cell, as it is in the app. */
.month-slot { animation: maya-slot-in 0.45s both; }
@keyframes maya-slot-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .month-slot { animation: none; }
}

/* ---------- The dated log ---------- */

/* THE RAIL IS THE RULER: the same unit repeated, where the emptiness is
   the argument. Maya's own note on this section, and the reason four of
   the entries are nearly blank. */
.log-entry {
  display: grid;
  grid-template-columns: 3.5rem 1fr;
  column-gap: 20px;
  border-top: 1px solid var(--hairline);
  padding: 32px 0;
}
@media (min-width: 640px) {
  .log-entry { grid-template-columns: 7rem 1fr; column-gap: 40px; padding: 40px 0; }
}
.log-when { display: flex; flex-direction: column; gap: 2px; }
.log-day, .log-time {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}
.log-day { text-transform: uppercase; letter-spacing: 0.12em; color: var(--text); }
.log-time { color: var(--ink-55); }
.log-body { min-width: 0; }
.log-body > h3 { margin: 0; }
/* The quiet entries -- Thu, Sat, Sun and the Friday publish -- drop to
   muted so the week reads as mostly empty at a glance. */
.log-entry.quiet .log-day,
.log-entry.quiet .log-body > h3 { color: var(--ink-55); }

/* Caption under a specimen. Says where the evidence came from, every
   time -- Maya's `Provenance`, and the discipline it encodes is the
   product's own claim about not inventing things. */
.prov {
  margin-top: 12px;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--ink-55);
}

/* ---------- Specimens inside the log ---------- */
.spec {
  margin-top: 20px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
  background: var(--surface);
  padding: 16px;
}
.spec-label { font-size: 0.75rem; font-weight: 500; color: var(--ink-55); }
.spec-field {
  margin-top: 6px;
  border: 1px solid var(--hairline);
  border-radius: var(--r-control);
  padding: 8px 12px;
  font-family: var(--font-mono);
  font-size: 0.875rem;
  color: var(--text);
}
.spec-rule { margin-top: 16px; border-top: 1px solid var(--hairline); padding-top: 16px; }
.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.chip {
  border: 1px solid var(--hairline);
  border-radius: var(--r-pill);
  padding: 2px 10px;
  font-size: 0.75rem;
  color: var(--text);
}

/* The ICP and pricing rows: a bordered list with hairline dividers, which
   is how Maya renders every list of records it owns. */
.rows {
  margin: 20px 0 0;
  padding: 0;
  list-style: none;
  border: 1px solid var(--hairline);
  border-radius: var(--r-card);
  overflow: hidden;
  background: var(--surface);
}
.rows > li { padding: 12px 16px; }
.rows > li + li { border-top: 1px solid var(--hairline); }
.rows .who { margin: 0; font-size: 0.875rem; font-weight: 500; color: var(--text); }
.rows .pain { margin: 2px 0 0; font-size: 0.75rem; line-height: 1.5; color: var(--ink-55); }
.row-split {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.row-split .tier { font-size: 0.875rem; font-weight: 500; color: var(--text); }
.row-split .price { font-size: 0.875rem; color: var(--ink-55); }
.row-split .price b {
  font-family: var(--font-mono);
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* The draft specimen. Its footer carries PICTURES OF THE APP'S CONTROLS,
   not controls -- Maya's own comment, and the reason they are <span>s: no
   handler, no tab stop, and no disabled semantics to mislead anyone. */
.draft-body > p { margin: 0 0 12px; font-size: 0.9375rem; line-height: 1.6; color: var(--text); max-width: none; }
.draft-body > p:last-child { margin-bottom: 0; }
.draft-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
  border-top: 1px solid var(--hairline);
  padding-top: 12px;
}
.faux {
  border-radius: var(--r-control);
  padding: 6px 12px;
  font-size: 0.75rem;
}
.faux-primary { background: var(--solid); color: var(--solid-text); font-weight: 500; }
.faux-ghost { border: 1px solid var(--hairline); color: var(--ink-55); }
.draft-foot .no-invent {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  color: var(--st-published);
}

/* ---------- The trust two-column ---------- */
.trust-grid { display: grid; gap: 24px 40px; margin-top: 24px; }
@media (min-width: 640px) { .trust-grid { grid-template-columns: 1fr 1fr; } }
.trust-list { margin: 0; padding: 0; list-style: none; }
.trust-list > li { border-top: 1px solid var(--hairline); padding-top: 12px; margin-top: 12px; }
.trust-list > li:first-child { border-top: 0; padding-top: 0; margin-top: 0; }
.trust-list .k {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text);
}
.trust-list p { margin: 4px 0 0; font-size: 0.875rem; line-height: 1.6; color: var(--ink-55); max-width: 52ch; }

/* ---------- Measures ----------
   SCALE IS THE PAGE'S RHYTHM, and it is Maya's: the hero runs wide, the
   log narrows to a reading measure, and the "Yours" line reopens to full
   width as the second peak. Uniform width is what made an earlier version
   of Maya's page read flat, per its own comment. */
.measure-read { max-width: 48rem; margin-inline: auto; }
.measure-claim { max-width: 760px; margin-inline: auto; text-align: center; }

/* ---------- Touch targets ----------

   WCAG 2.5.8 asks for 24x24 CSS pixels minimum, and the dense link lists were
   under it: breadcrumbs measured 18px tall, and the footer columns and the
   "Back" link sat exactly at 24px with no margin for a thumb. Measured at
   375px across all six pages.

   PADDING RATHER THAN font-size OR line-height: the type scale is Maya's and
   should not change on a phone, and growing the line-height would loosen the
   footer columns' rhythm. Padding grows the hit area and leaves the text where
   it is. Applied only below the breakpoint, so the desktop footer keeps its
   tighter spacing. */
@media (max-width: 720px) {
  /* The legal tables already scroll inside `.table-scroll`, so nothing was
     broken -- but a three-column table measured 376px against a 335px content
     box, so every reader on a phone had to scroll a table sideways to see the
     "Why" column. Tighter cells and a slightly smaller face recover ~40px,
     which is enough for it to fit outright at 375px. The scroll container
     stays as the backstop for narrower phones. */
  .legal th, .legal td { padding: 9px 8px; }
  .legal table { font-size: 0.875rem; }

  .footer-col a,
  .crumbs a,
  .back {
    display: inline-flex;
    align-items: center;
    min-height: 32px;
  }
  /* `.footer-col` is a flex column with `gap: 10px`, and the rows are now
     32px tall rather than 24px. Keeping the same gap would have added 8px per
     row to a footer that has four columns of them, so the gap comes down by
     the amount the rows grew and the footer's overall height is unchanged. */
  .footer-col { gap: 2px; }
}
