/* AppCub website: one stylesheet for every public page.
   ---------------------------------------------------------------------------------------------
   The Flutter app is the source of truth for this look (lib/theme/app_palette.dart, app_theme.dart,
   lib/core/ui/components.dart). Same colours by role, light and dark (following the system setting,
   like the app's ThemeMode.system), Inter for text and UI, Space Grotesk for headings, pill buttons,
   1px hairline borders, flat cards with 16-20px corners. The brand is monochrome: primary buttons are
   near-black on light and white on dark. Green, amber, blue and red appear only as status (dots,
   icons, status text, the sale border, the danger tint), never as decoration.

   Used by: web/index.html (whose Flutter boot CSS, #loading and html.app, stays inline there),
   the six business app pages and /pricing/ (generated by tool/seo/build_pages.py and pricing.py),
   /waitlist/, /play-launch/, /ios-launch/ and the legal pages.

   Fonts come from one Google Fonts stylesheet linked in each page's <head>:
   Inter 400/500/600/700 and Space Grotesk 500/600/700, display=swap.

   Plain CSS, no build step. Sections, in order:
     1. Tokens           2. Base             3. Page shell (header, footer, wrap, sections)
     4. Type             5. Buttons          6. Hero and breadcrumbs
     7. Content blocks   8. Pricing          9. FAQ and tables
    10. Forms           11. Long-form prose 12. Small parts (badges, pills, dots, icon tiles)
    13. Waitlist media  14. Motion          15. Print and forced colours

   Moving a page onto this file. The old names have no rule here, and most rules hang on a class,
   not a bare element, so a page or generator that keeps the old markup quietly loses its look.
   Rename every row of this map:
     Shell
       header.top                        -> header.site-header
       .top nav                          -> nav.site-nav
       the brand's img                   -> img.logo
       footer .wrap.cols                 -> .site-footer > .wrap.footer-grid
                                            (+ .footer-company, nav.footer-col, .footer-base)
       bare <section id=...>             -> section.section (no class = no vertical padding)
       legal, play and iOS <main>        -> main.wrap.narrow.prose (no .wrap = no 16px gutter)
     Blocks
       .closing contents                 -> wrapped in .panel
       .get                              -> .grid
       figure.shot, .wide > figure       -> figure.screen, or div.screen > figure
       .wl                               -> .split (+ .split-main / .split-side / .split-media)
       ul.perks                          -> ul.checks
       legal/play/ios div.note           -> div.callout (.note now means only the hero's muted line)
       play/ios ol.steps                 -> ol.steps.stack (plain ol.steps is a grid of step cards)
       .table-wrap                       -> .tablebox
       the home plans table              -> table.stack (rows stack on phones, not a sideways scroll)
       a.cta                             -> a.btn
     Forms
       form.join, form.ask               -> form.form.card.lg
       bare <label>Name <input></label>  -> label.field (the label look; the control is also
                                            caught by the .form fallback in section 10)
       .two                              -> .form-row
       div.small label + .chips          -> fieldset.choices > legend + .chips
       classless <button type="submit">  -> button.btn (play-launch, ios-launch)
       .done                             -> .card.lg
   --------------------------------------------------------------------------------------------- */


/* 1. Tokens ----------------------------------------------------------------------------------
   Role names match the app's AppPalette. Two values differ from the app on purpose, for WCAG AA
   on the web: --text-subtle (app #7D8793 light / #6B7380 dark measure 3.4-4.4:1 for body text)
   and --control-border (the 3:1 edge a text field needs; the app's hairline is about 1.2:1). */
:root {
  color-scheme: light dark;

  --bg: #F6F7F9;
  --surface: #FFFFFF;
  --card: #FFFFFF;
  --card-high: #EEF1F4;
  --border: #E2E6EB;
  --border-high: #C9CFD8;
  --text: #0F1419;
  --text-muted: #55606C;
  --text-subtle: #636C78;          /* app #7D8793; this one is 4.96:1 on bg, 5.32:1 on white */
  --primary: #0F1419;
  --on-primary: #FFFFFF;
  --success: #047857;
  --warning: #B45309;
  --info: #0369A1;
  --danger: #DC2626;
  --danger-text: #B91C1C;          /* red text on the red tint: 4.9:1 (#DC2626 there is 3.8:1) */

  --shadow: rgba(0, 0, 0, 0.08);   /* the app's one shadow, on the home idea box */
  --input-fill: #EEF1F4;           /* InputDecorationTheme fill */
  --control-border: #878F9A;       /* 3:1 edge for text fields: 3.27:1 on white */
  --focus: #0F1419;                /* focus ring = text colour, 17:1 */
  --primary-hover: #2B2F34;        /* primary mixed 88% with bg, like Material's ink overlay */
  --hover: rgba(15, 20, 25, 0.06); /* quiet hover tint for ghost buttons, nav links, rows */
  --danger-tint: rgba(220, 38, 38, 0.12);
  --danger-line: rgba(220, 38, 38, 0.40);
  --header-glass: rgba(246, 247, 249, 0.90);
  --logo-line: #2B2E37;            /* hairline round the black logo tile, both themes */
  /* The select chevron (section 10), drawn in --text-muted. A data: image can't read a custom
     property, so the colour is written into it and the dark block repeats it. */
  --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%2355606C' d='M12 15.4 6.6 10l1.4-1.4 4 4 4-4 1.4 1.4z'/%3E%3C/svg%3E");

  /* Radii: the app's AppTokens scale, plus AppCard's 18px. Nothing on the site is rounder than
     20px except pills. */
  --r-xs: 8px;
  --r-sm: 12px;
  --r-md: 16px;                    /* inputs, theme cards, project tiles, FAQ rows, callouts */
  --r-card: 18px;                  /* AppCard / GrokCard, sale banner */
  --r-lg: 20px;                    /* idea box, dialogs, big cards, closing panel, screenshots */
  --r-pill: 999px;                 /* buttons, chips, badges */

  /* Spacing: AppTokens 4/8/16/24/32 plus the larger steps a web page needs. */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 64px;

  /* Layout. 1080px is the app's desktop footprint (232px sidebar + 840px content). */
  --wrap: 1080px;
  --measure: 760px;                /* reading column: home column, legal text, FAQ, forms */
  --gutter: 16px;                  /* ResponsiveContainer: 16 / 24 / 32 */
  --header-h: 64px;
  --section-y: 48px;

  --font-body: "Inter", "Inter Fallback", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-head: "Space Grotesk", "Inter", "Inter Fallback", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

  --ease: cubic-bezier(0.4, 0, 0.2, 1);   /* fastOutSlowIn */
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #000000;
    --surface: #08090B;
    --card: #14171D;
    --card-high: #1B1E26;
    --border: #232730;
    --border-high: #323742;
    --text: #F7F9F9;
    --text-muted: #8B949E;
    --text-subtle: #868E99;        /* app #6B7380; this one is 6.34:1 on bg, 5.42:1 on card */
    --primary: #FFFFFF;
    --on-primary: #000000;
    --success: #10B981;
    --warning: #F59E0B;
    --info: #38BDF8;
    --danger: #EF4444;
    --danger-text: #F87171;        /* on the red tint: 7.0:1 over bg, 5.8:1 over card, 5.4:1 over
                                      cardHigh (#EF4444 there is only 4.3:1 over card) */

    --shadow: rgba(0, 0, 0, 0.20);
    --input-fill: #101216;         /* dark fill is AppColors.grokSurface, not cardHigh */
    --control-border: #646B7A;     /* 3.36:1 on card, 3.93:1 on bg */
    --focus: #F7F9F9;
    --primary-hover: #E1E3E5;
    --hover: rgba(247, 249, 249, 0.08);
    --danger-tint: rgba(239, 68, 68, 0.12);
    --danger-line: rgba(239, 68, 68, 0.40);
    --header-glass: rgba(0, 0, 0, 0.88);
    --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%238B949E' d='M12 15.4 6.6 10l1.4-1.4 4 4 4-4 1.4 1.4z'/%3E%3C/svg%3E");
  }
}

@media (min-width: 600px)  { :root { --gutter: 24px; } }
@media (min-width: 700px)  { :root { --section-y: 64px; } }
@media (min-width: 1024px) { :root { --gutter: 32px; --section-y: 72px; } }

/* Arial sized to Inter's metrics, so text barely moves when Inter arrives (display=swap). */
@font-face {
  font-family: "Inter Fallback";
  src: local("Arial"), local("ArialMT");
  ascent-override: 90.2%;
  descent-override: 22.48%;
  line-gap-override: 0%;
  size-adjust: 107.4%;
}


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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-padding-top: calc(var(--header-h) + 16px);   /* in-page anchors land below the sticky bar */
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 400 16px/1.6 var(--font-body);
  letter-spacing: -0.006em;        /* the app's bodyLarge -0.1px */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-wrap: break-word;
}

/* Scripts hide things with the hidden attribute (#waitlist after sending, #sale, .was, media
   slots). A component's own display rule must never bring them back. */
[hidden] { display: none !important; }

img, video { max-width: 100%; height: auto; }
img { border-style: none; }
figure { margin: 0; }              /* no browser 40px side margins on any screenshot or media */
p { margin: 0 0 14px; }
strong, b { font-weight: 600; }
small { font-size: 0.875em; }
hr { border: 0; border-top: 1px solid var(--border); margin: var(--s-6) 0; }

/* Links in running text: text colour, underlined (the app's chat links), never a blue accent. */
a {
  color: var(--text);
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--text-subtle);
  text-underline-offset: 3px;
}
a:hover { text-decoration-color: currentColor; }

/* Focus: a 2px ring in the text colour (17:1 light, 20:1 dark). */
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

::selection { background: var(--text); color: var(--bg); }

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

.skip {
  position: absolute;
  left: -9999px;
  top: 8px;
  z-index: 100;
  background: var(--primary);
  color: var(--on-primary);
  border-radius: var(--r-pill);
  padding: 12px 18px;
  font-weight: 600;
  font-size: 14px;
  text-decoration: none;
}
.skip:focus { left: 12px; }

/* Honeypot fields on the forms: off-screen for people, still there for bots. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }


/* 3. Page shell -----------------------------------------------------------------------------
   .wrap is the content column; .wrap.narrow is the reading column for legal and launch pages. */
.wrap {
  width: 100%;
  max-width: calc(var(--wrap) + 2 * var(--gutter));
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.wrap.narrow { max-width: calc(var(--measure) + 2 * var(--gutter)); }

/* Header: like the app bar, flat, with a hairline, and frosted while content scrolls under it. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
}
@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
  .site-header {
    background: var(--header-glass);
    -webkit-backdrop-filter: saturate(180%) blur(14px);
    backdrop-filter: saturate(180%) blur(14px);
  }
}
.site-header .wrap {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  min-height: var(--header-h);
}
.site-header .brand { margin-right: auto; }

/* The logo: the app's black tile with the white "A" mark (web/icons/Icon-192.png is that tile,
   full bleed), rounded like AppCubLogo, with its #2B2E37 hairline. */
.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  border-radius: var(--r-sm);
  color: var(--text);
  font: 700 18px/1 var(--font-head);
  letter-spacing: -0.2px;
  text-decoration: none;
}
.logo {
  display: block;
  flex: none;
  width: 32px;
  height: 32px;
  border-radius: var(--r-xs);
  background: #000;
  outline: 1px solid var(--logo-line);
  outline-offset: -1px;
}

.site-nav { display: flex; align-items: center; gap: 2px; }
.site-nav a:not(.btn) {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 12px;
  border-radius: var(--r-pill);
  color: var(--text-muted);
  font-size: 14px;
  font-weight: 500;
  text-decoration: none;
  white-space: nowrap;
}
.site-nav a:not(.btn)[aria-current="page"] { color: var(--text); background: var(--card-high); font-weight: 600; }
.site-nav .btn { margin-left: var(--s-2); }
/* Below the app's sidebar breakpoint the text links give way; Sign in stays. They are all in the footer. */
@media (max-width: 767.98px) {
  .site-nav a:not(.btn) { display: none; }
}

/* Sections: generous rhythm, no rules between them; the cards inside carry the structure. */
.section { padding-block: var(--section-y); }
.section + .section { padding-top: 0; }               /* one gap between sections, not two */
.hero + .section { padding-top: 16px; }               /* the hero already has bottom space */
.section > .wrap > h2,
.section-title { margin-bottom: 12px; }
/* A heading followed straight by content (no .intro) still needs air before the content. */
.section > .wrap > h2 + :not(p) { margin-top: 20px; }
/* A band sets one section apart on a surface background (white in light, near-black in dark). */
.section.band {
  background: var(--surface);
  border-block: 1px solid var(--border);
  padding-top: var(--section-y);
}
.section.band + .section { padding-top: var(--section-y); }

/* Footer */
.site-footer {
  background: var(--surface);
  border-top: 1px solid var(--border);
  color: var(--text-muted);
  font-size: 14px;
  line-height: 1.55;
}
.footer-grid {
  display: grid;
  gap: var(--s-6) var(--s-5);
  grid-template-columns: minmax(0, 1fr);
  padding-block: var(--s-7) var(--s-5);
}
@media (min-width: 600px)  { .footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .footer-grid { grid-template-columns: minmax(0, 1.35fr) repeat(3, minmax(0, 1fr)); } }
.footer-company .brand { margin-bottom: var(--s-3); }
.footer-company p { margin: 0 0 var(--s-3); }
.footer-company strong { color: var(--text); font-weight: 600; }
.footer-col ul { list-style: none; margin: 0; padding: 0; }
.footer-col a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--text-muted);
  text-decoration: none;
}
.footer-col a:hover { color: var(--text); text-decoration: underline; }
.footer-col a[aria-current="page"] { color: var(--text); font-weight: 600; }
.footer-col .overline { margin-left: 0; }
.footer-base {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-4);
  justify-content: space-between;
  padding-block: var(--s-4) var(--s-6);
  font-size: 13px;
  /* A hairline across the content column only (a border would run into the gutters). */
  background: linear-gradient(var(--border), var(--border)) center top / calc(100% - 2 * var(--gutter)) 1px no-repeat;
}
.footer-base p { margin: 0; }


/* 4. Type -------------------------------------------------------------------------------------
   Space Grotesk 700 for headings with the app's tight tracking; Inter for everything else.
   (The app's w800 renders as 700: Google Fonts ships Space Grotesk 300-700.)
   The sizes are the app's, with its 700px "wide" switch, and there is one scale for every page
   (marketing, legal and launch pages alike):
     h1     30px, 40px from 700px   the home hero title ("What do you want to build?")
     h2     22px, 26px from 700px   headlineMedium, then displaySmall
     h3     19px                    headlineSmall
     .lede  15px, 16px from 700px   the hero's supporting line */
h1, h2, h3, h4 {
  margin: 0 0 0.5em;
  color: var(--text);
  font-family: var(--font-head);
  font-weight: 700;
  text-wrap: balance;
}
h1 {
  font-size: 30px;
  line-height: 1.15;
  letter-spacing: -0.5px;
  margin-bottom: 12px;
}
h2 {
  font-size: 22px;
  line-height: 1.2;
  letter-spacing: -0.5px;
}
@media (min-width: 700px) {
  h1 { font-size: 40px; }
  h2 { font-size: 26px; }
}
h3 {
  font-size: 19px;
  line-height: 1.3;
  letter-spacing: -0.2px;
  margin-bottom: 6px;
}
h4 { font: 700 16px/1.4 var(--font-body); letter-spacing: 0; margin-bottom: 4px; }

/* Supporting line under a page's h1. */
.lede {
  color: var(--text-muted);
  font-size: 15px;
  line-height: 1.5;
  max-width: 58ch;
  margin-bottom: 0;
}
p.lede + ul, p.lede + ol { margin-top: var(--s-5); }
/* Supporting paragraph under a section's h2: the same size as the lede. */
.intro {
  color: var(--text-muted);
  font-size: 15px;
  line-height: 1.6;
  max-width: 62ch;
  margin-bottom: var(--s-6);
}
@media (min-width: 700px) {
  .lede, .intro { font-size: 16px; }
}
/* Small muted text. Scripts set className = "small" (or "small sent" / "small failed") on the
   form notes and #launch-note, so this class carries its look on its own. */
.small { color: var(--text-muted); font-size: 14px; line-height: 1.5; }
/* Status text: the form notes after sending. */
.sent { color: var(--success); font-weight: 500; }
.failed { color: var(--danger-text); font-weight: 500; }   /* 6.0:1 or better, both themes */

/* Section header from the app (GrokSectionHeader): small caps label above a group. */
.overline {
  display: block;
  margin: 0 0 10px 4px;
  color: var(--text-subtle);
  font: 700 11.5px/1.4 var(--font-head);
  letter-spacing: 1.8px;
  text-transform: uppercase;
}

.center { text-align: center; }
.center > .lede, .center > .intro { margin-inline: auto; }
.muted { color: var(--text-muted); }


/* 5. Buttons -----------------------------------------------------------------------------------
   Pills, like AppButton and the Material buttons in the app.
     .btn            primary: near-black on light, white on dark
     .btn.secondary  card background, hairline border (the app's "Log out" button)
     .btn.quiet      same as .secondary. The pricing script toggles .quiet on the currency buttons,
                     so it must stay the unselected look.
     .btn.ghost      no fill, no border
     .btn.danger     the app's danger tint
     .btn.small      44px tall (tap target), for headers and tight rows
     .btn.block      full width
   No icons inside links that launch.js rewrites (it replaces their text). */
.btn {
  -webkit-appearance: none;
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 48px;
  padding: 0 22px;
  border: 1px solid var(--primary);
  border-radius: var(--r-pill);
  background: var(--primary);
  color: var(--on-primary);
  font: 600 15px/1.2 var(--font-body);
  letter-spacing: 0.1px;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.btn.secondary,
.btn.quiet {
  background: var(--card);
  color: var(--text);
  border-color: var(--border-high);
}
.btn.ghost {
  background: transparent;
  color: var(--text);
  border-color: transparent;
}
.btn.danger {
  background: var(--danger-tint);
  color: var(--danger-text);
  border-color: var(--danger-line);
}
.btn.small {
  min-height: 44px;
  padding: 0 16px;
  font-size: 14px;
}
.btn.block { display: flex; width: 100%; }
.btn svg { width: 18px; height: 18px; flex: none; }

/* Hover and press only on enabled buttons. :where() adds no weight, so the specificity of these
   rules is what it would be without the guard. */
@media (hover: hover) {
  .btn:where(:not(:disabled, [aria-disabled="true"])):hover { background: var(--primary-hover); border-color: var(--primary-hover); }
  .btn.secondary:where(:not(:disabled, [aria-disabled="true"])):hover,
  .btn.quiet:where(:not(:disabled, [aria-disabled="true"])):hover { background: var(--card-high); border-color: var(--border-high); }
  .btn.ghost:where(:not(:disabled, [aria-disabled="true"])):hover { background: var(--hover); border-color: transparent; }
  .btn.danger:where(:not(:disabled, [aria-disabled="true"])):hover { background: var(--danger-tint); border-color: var(--danger); }
  .site-nav a:not(.btn):hover { color: var(--text); background: var(--hover); }
  .site-nav a:not(.btn)[aria-current="page"]:hover { background: var(--card-high); }
}
.btn:where(:not(:disabled, [aria-disabled="true"])):active { filter: brightness(0.96); }

/* Disabled (the waitlist and enquiry scripts disable their submit while sending): half strength,
   like AppButton's 50% background. Only opacity and the cursor change, so every variant
   (secondary, quiet, ghost, danger) keeps its own colours. */
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* A row of buttons. */
.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s-3);
  margin-top: 28px;
}
.center .actions, .actions.center { justify-content: center; }


/* 6. Hero and breadcrumbs ------------------------------------------------------------------- */
.hero { padding-block: 20px 40px; }
@media (min-width: 700px) { .hero { padding-block: 32px 56px; } }
/* .hero.tall: pages without breadcrumbs (the home page) get the app home's 48px top space. */
@media (min-width: 700px) { .hero.tall { padding-top: 48px; } }
.hero h1 { max-width: 18ch; }

/* Muted line under the hero buttons. launch.js looks for ".hero .note, .hero .lede". Scoped to
   the hero: elsewhere .note means nothing (the old in-text box is .callout now). */
.hero .note {
  color: var(--text-muted);
  font-size: 14.5px;
  line-height: 1.55;
  max-width: 62ch;
  margin: var(--s-4) 0 0;
}

/* launch.js inserts p#launch-note (class "small") right after the hero lede while the waitlist
   is on. It reads as the app's news strip: a card led by an 18px sparkle in the text colour
   (an announcement is not a status, so no accent colour). */
.hero #launch-note {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: fit-content;
  max-width: 100%;
  margin: var(--s-4) 0 0;
  padding: 10px 14px;
  background: var(--card);
  border: 1px solid var(--border-high);
  border-radius: 14px;
  color: var(--text);
  font-size: 14.5px;
  line-height: 1.45;
}
/* The app's duotone auto_awesome sparkle (lib/widgets/cub_icons.dart), 18px, centred on the
   first line (14.5px x 1.45 = 21px line). */
.hero #launch-note::before {
  content: "";
  flex: none;
  width: 18px;
  height: 18px;
  margin-top: 1.5px;
  background: var(--text);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath opacity='.36' d='M18.6 2.4c.3 1.9 1.1 2.7 3 3-1.9.3-2.7 1.1-3 3-.3-1.9-1.1-2.7-3-3 1.9-.3 2.7-1.1 3-3z'/%3E%3Cpath d='M10.6 4c.7 4.8 2.6 6.7 7.4 7.4-4.8.7-6.7 2.6-7.4 7.4-.7-4.8-2.6-6.7-7.4-7.4 4.8-.7 6.7-2.6 7.4-7.4z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath opacity='.36' d='M18.6 2.4c.3 1.9 1.1 2.7 3 3-1.9.3-2.7 1.1-3 3-.3-1.9-1.1-2.7-3-3 1.9-.3 2.7-1.1 3-3z'/%3E%3Cpath d='M10.6 4c.7 4.8 2.6 6.7 7.4 7.4-4.8.7-6.7 2.6-7.4 7.4-.7-4.8-2.6-6.7-7.4-7.4 4.8-.7 6.7-2.6 7.4-7.4z'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* Hero with a phone video beside it (home): text on the left, the demo in a phone-sized frame on
   the right; on phones the video comes after the buttons. The frame's shape is reserved in the
   markup (#media-hero.reserve with --ar), so the text never moves when the video arrives. */
.hero-split { display: grid; gap: var(--s-6); grid-template-columns: minmax(0, 1fr); align-items: center; }
.hero-split .media-hero { margin-top: 0; justify-self: center; width: 100%; max-width: 320px; }
@media (min-width: 900px) {
  .hero-split { grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); gap: var(--s-7); }
  .hero-split .media-hero { justify-self: end; }
}

/* Centred hero: every block in it centres, not just the text (.center sets text-align). */
.hero.center h1,
.hero.center .lede,
.hero.center .note,
.hero.center #launch-note { margin-inline: auto; }

/* Breadcrumbs: "AppCub › Pricing". */
.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 6px;
  margin-bottom: var(--s-4);
  color: var(--text-muted);
  font-size: 13.5px;
}
.crumbs a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--text-muted);
  text-decoration: none;
}
.crumbs a:hover { color: var(--text); text-decoration: underline; }
.crumbs [aria-current="page"] { color: var(--text); font-weight: 500; }

/* Screenshot rows (phone screens). Up to three side by side; on phones they scroll sideways,
   edge to edge, instead of stacking three tall images. */
.shots {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 280px);
  justify-content: start;
  gap: var(--s-4);
  margin: 36px 0 var(--s-3);
}
.shots figure { min-width: 0; }
.shots img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--card-high);
}
@media (max-width: 699.98px) {
  .shots {
    grid-auto-columns: 68%;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--gutter);
    margin-inline: calc(-1 * var(--gutter));
    padding: 0 var(--gutter) 8px;
    scrollbar-width: thin;
  }
  .shots figure { scroll-snap-align: start; }
}

/* One wide screenshot (the workspace picture on the home and tiffin pages): figure.screen, or a
   div.screen holding the figure (figures have no margin here, so both line up with the text). */
.screen { margin: 40px 0 var(--s-3); }
.screen img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--card-high);
}

figcaption {
  margin-top: 10px;
  color: var(--text-muted);
  font-size: 13.5px;
  line-height: 1.45;
}


/* 7. Content blocks ------------------------------------------------------------------------ */

/* Cards. .card is the app's AppCard; .card.lg is a dialog-sized card (forms, notices). */
.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: 20px;
}
.card.lg { border-radius: var(--r-lg); padding: 24px; }
@media (min-width: 700px) { .card.lg { padding: 28px; } }
.card > :last-child { margin-bottom: 0; }
/* The app's card and dialog title: headlineSmall, 19px. */
.card-title {
  margin: 0 0 8px;
  font: 700 19px/1.3 var(--font-head);
  letter-spacing: -0.2px;
}

/* Feature cards: a grid of div > h3 + p. */
.grid {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}
.grid > * {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: 20px;
}
/* Item titles inside cards and lists are Inter, as in the app (template cards, "How it works",
   tiles). Space Grotesk stays for display, section, card/dialog and plan titles. */
.grid h3 { margin-bottom: 6px; font: 700 16px/1.35 var(--font-body); letter-spacing: 0; }
.grid p { color: var(--text-muted); font-size: 15px; line-height: 1.55; margin: 0; }

/* Numbered steps.
   ol.steps        a grid of step cards (li > h3 + p), each with the app's 32px step tile
   ol.steps.stack  a vertical list (li > strong + text), like the app's "How it works" panel */
ol.steps {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: step;
  display: grid;
  gap: var(--s-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
}
ol.steps > li {
  counter-increment: step;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: 20px;
}
ol.steps > li::before {
  content: counter(step);
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  margin-bottom: 14px;
  border-radius: 9px;
  background: var(--card-high);
  color: var(--text);
  font: 700 15px/1 var(--font-head);
}
ol.steps h3 { margin-bottom: 6px; font: 700 16px/1.35 var(--font-body); letter-spacing: 0; }
ol.steps p { color: var(--text-muted); font-size: 15px; line-height: 1.55; margin: 0; }

ol.steps.stack { display: block; margin: 0 0 var(--s-4); }
ol.steps.stack > li {
  position: relative;
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0 0 0 48px;
  min-height: 32px;
}
ol.steps.stack > li + li { margin-top: var(--s-4); }
ol.steps.stack > li::before { position: absolute; left: 0; top: -2px; margin: 0; }
ol.steps.stack strong { display: block; color: var(--text); }

/* The prompt box: drawn like the app's home idea box (border-high, 20px corners, the one soft
   shadow). Holds p#prompt-text and the Copy button, whose text the copy script swaps. */
.prompt {
  display: flex;
  flex-direction: column;
  gap: var(--s-3);
  max-width: var(--measure);
  margin-bottom: var(--s-6);
  padding: 18px 14px 14px 20px;
  background: var(--card);
  border: 1px solid var(--border-high);
  border-radius: var(--r-lg);
  box-shadow: 0 8px 24px var(--shadow);
}
.prompt p { margin: 0; padding-right: 6px; color: var(--text); font-size: 16px; line-height: 1.55; }
.prompt .btn { align-self: flex-end; }

/* Lists.
   ul.plain   muted bullet list (costs, limits)
   ul.checks  the app's feature rows: a green tick, then the text (also used inside .plan and .trial) */
ul.plain {
  margin: 0 0 14px;
  padding-left: 20px;
  color: var(--text-muted);
  max-width: 70ch;
}
ul.plain li { margin-bottom: 8px; padding-left: 2px; }
ul.plain li::marker { color: var(--text-subtle); }
ul.plain strong { color: var(--text); }

ul.checks,
.plan ul,
.trial ul {
  list-style: none;
  margin: 0 0 var(--s-5);
  padding: 0;
}
ul.checks li,
.plan li,
.trial li {
  position: relative;
  padding-left: 26px;
  color: var(--text-muted);
  font-size: 15px;
  line-height: 1.5;
}
ul.checks li + li,
.plan li + li,
.trial li + li { margin-top: 8px; }
ul.checks li::before,
.plan li::before,
.trial li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.18em;
  width: 17px;
  height: 17px;
  background: var(--success);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9.55 17.6 4.4 12.45l1.5-1.5 3.65 3.65 8.55-8.55 1.5 1.5z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M9.55 17.6 4.4 12.45l1.5-1.5 3.65 3.65 8.55-8.55 1.5 1.5z'/%3E%3C/svg%3E") center / contain no-repeat;
}
ul.checks strong,
.plan li strong,
.trial li strong { color: var(--text); }

/* Pill links (the app's example chips): ul.links > li > a, or any a.chip / button.chip. */
ul.links {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2);
}
ul.links a,
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 44px;
  padding: 0 16px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  color: var(--text);
  font: 500 14px/1.2 var(--font-body);
  text-decoration: none;
}
@media (hover: hover) {
  ul.links a:hover,
  a.chip:hover,
  button.chip:hover { background: var(--card-high); border-color: var(--border-high); }
}

/* Callout box inside text (legal "In short", launch "What we can't promise"). */
.callout {
  margin: 20px 0;
  padding: 16px 18px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--text);
}
.callout > :last-child { margin-bottom: 0; }
/* Status callouts: a 3px left edge in the status colour, like the app's warning/error replies. */
.callout.warning { border-left: 3px solid var(--warning); }
/* border-color first: as a shorthand it also sets the left colour, so the left edge comes after. */
.callout.danger {
  background: var(--danger-tint);
  border-color: var(--danger-line);
  border-left: 3px solid var(--danger);
}

/* Closing call to action: a centred card, like the app's waitlist notice (a flat card with a
   hairline), at dialog size: 20px corners. */
.panel {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 28px 20px;
  text-align: center;
}
@media (min-width: 700px) { .panel { padding: 40px 32px; } }
.panel h2 { margin-inline: auto; max-width: 24ch; }
.panel .intro { margin-inline: auto; margin-bottom: 0; }
.panel .actions { justify-content: center; }
.panel > .btn { margin-top: 28px; }

/* Two columns that fold to one: main + side on wide screens. Used by the waitlist page:
   .split-main (headline), .split-side (the form), .split-media (video and screenshots).
   On phones the form comes straight after the headline and the media go last, so media that
   arrive late never push the form down. */
.split {
  display: grid;
  gap: var(--s-6);
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "main" "side" "media";
  align-items: start;
}
.split-main { grid-area: main; }
.split-side { grid-area: side; }
.split-media { grid-area: media; min-width: 0; }
@media (min-width: 860px) {
  .split {
    gap: var(--s-6) var(--s-7);
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    grid-template-rows: auto 1fr;
    grid-template-areas: "main side" "media side";
  }
}
.split-media .shots { margin-top: 0; }

/* Ready-made app cards (the app's template gallery): screenshot on top, name, one line, link. */
.templates {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 220px;
  gap: var(--s-3);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  margin-inline: calc(-1 * var(--gutter));
  /* 6px on top: room for a focused card's ring (2px, 2px out), which the scroller would clip. */
  padding: 6px var(--gutter) 10px;
  scroll-padding-inline: var(--gutter);
  scrollbar-width: thin;
}
.template {
  position: relative;
  display: flex;
  flex-direction: column;
  scroll-snap-align: start;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  overflow: hidden;
  color: var(--text);
  text-decoration: none;
}
.template img {
  display: block;
  width: 100%;
  height: 150px;
  object-fit: cover;
  object-position: top;
  background: var(--card-high);
}
/* The card's own small badge ("Free · 1 min"), not the GrokBadge look: Inter 600, sentence case.
   It sits under the screenshot, above the name: on the screenshot it covered the app's title. */
.template .badge {
  order: 1;
  align-self: flex-start;
  margin: 12px 14px 0;
  gap: 4px;
  padding: 3px 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  font: 600 11.5px/1.3 var(--font-body);
  letter-spacing: 0;
  text-transform: none;
}
.template-body { order: 2; display: flex; flex-direction: column; gap: 4px; padding: 12px 14px 14px; flex: 1; }
.template:has(.badge) .template-body { padding-top: 8px; }
.template-name { font: 700 15px/1.3 var(--font-body); }
.template-line { color: var(--text-muted); font-size: 13px; line-height: 1.4; }
.template-link { margin-top: auto; padding-top: 10px; font-size: 13.5px; font-weight: 600; }
@media (hover: hover) { a.template:hover { border-color: var(--border-high); } }
/* From 700px (where .shots stop scrolling too) the six cards are a plain grid of three: none is
   cut off at the column's edge, nothing hides behind a sideways scroll a mouse can't see, and no
   focus ring is clipped. The screenshot keeps the phone card's 220:150 crop at the wider size. */
@media (min-width: 700px) {
  .templates {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    overflow: visible;
    margin-inline: 0;
    padding: 0;
  }
  .template img { height: auto; aspect-ratio: 22 / 15; }
}


/* 8. Pricing --------------------------------------------------------------------------------- */

/* Currency switch: the app's SegmentedButton. The page script marks the unselected button .quiet
   and sets aria-pressed. */
.currency {
  display: inline-flex;
  gap: 2px;
  margin-top: 28px;
  padding: 3px;
  background: var(--card);
  border: 1px solid var(--border-high);
  border-radius: var(--r-pill);
}
.currency .btn { min-height: 44px; border-color: transparent; }
.currency .btn.quiet { background: transparent; color: var(--text-muted); border-color: transparent; }
@media (hover: hover) {
  .currency .btn.quiet:hover { background: var(--hover); color: var(--text); border-color: transparent; }
}
.currency + .small { margin-top: 12px; }

/* Sale banner (#sale): the app's sale card, an AppCard (18px corners, 16px padding) with a
   green border. */
.sale {
  max-width: 62ch;
  margin-top: var(--s-4);
  padding: 16px;
  background: var(--card);
  border: 1px solid var(--success);
  border-radius: var(--r-card);
  color: var(--text);
  font-size: 15px;
}

/* The trial card: a flat AppCard like the plan cards (18px corners, 1px hairline, no shadow).
   The one soft shadow on the site belongs to the .prompt idea box. */
.trial {
  max-width: 560px;
  padding: 24px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
}
@media (min-width: 700px) { .trial { padding: 28px; } }

.plans {
  display: grid;
  gap: var(--s-3);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
  margin-bottom: var(--s-6);
}
.plan {
  display: flex;
  flex-direction: column;
  padding: 24px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
}
/* Highlighted plan (or trial), as in the app: a border in the primary colour. */
.plan.highlight,
.trial.highlight { border-color: var(--primary); }
.plan h3 { margin-bottom: 10px; font-size: 18px; letter-spacing: -0.2px; }   /* app plan name */
.plan .btn,
.trial .btn { width: 100%; }
.plan .btn { margin-top: auto; }

/* p.price: the figure, its period and the struck-out old price, at the app's plan card sizes
   (22px price, 15px old price, small muted period). */
p.price { margin: 0 0 4px; color: var(--text); }
p.price strong {
  font: 700 22px/1.25 var(--font-body);
  letter-spacing: -0.3px;
  font-variant-numeric: tabular-nums;
}
p.price .per { color: var(--text-muted); font-size: 14px; }
p.price .was {
  margin-right: 6px;
  color: var(--text-muted);
  font-size: 15px;
  text-decoration: line-through;
}
.yearly { margin-bottom: var(--s-4); }
.trial p.price { margin-bottom: var(--s-4); }

/* td.price: a price inside a table. */
td.price { white-space: nowrap; font-variant-numeric: tabular-nums; }


/* 9. FAQ and tables ------------------------------------------------------------------------- */

/* FAQ: each <details> is a card row with a chevron, like the app's settings rows. */
details {
  max-width: var(--measure);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
}
details + details { margin-top: var(--s-2); }
summary {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 56px;
  padding: 14px 52px 14px 18px;
  border-radius: inherit;
  color: var(--text);
  font: 600 16px/1.4 var(--font-body);
  list-style: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
summary::-webkit-details-marker { display: none; }
summary::after {
  content: "";
  position: absolute;
  right: 18px;
  top: 50%;
  width: 20px;
  height: 20px;
  margin-top: -10px;
  background: var(--text-muted);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 15.4 6.6 10l1.4-1.4 4 4 4-4 1.4 1.4z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 15.4 6.6 10l1.4-1.4 4 4 4-4 1.4 1.4z'/%3E%3C/svg%3E") center / contain no-repeat;
}
details[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
details[open] > summary::after { transform: rotate(180deg); }
@media (hover: hover) { summary:hover { background: var(--hover); } }
details > :not(summary) {
  margin: 0;
  padding: 0 18px 16px;
  color: var(--text-muted);
  font-size: 15.5px;
  line-height: 1.6;
}
details + p, details + .small { margin-top: var(--s-5); }

/* Tables live in a .tablebox card, which scrolls sideways if a table is too wide.
   table.stack turns each row into its own block on phones, labelled from td[data-label]. */
.tablebox {
  max-width: 100%;
  overflow-x: auto;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  margin-bottom: var(--s-4);
}
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
  line-height: 1.5;
}
th, td {
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
}
tr:last-child > th, tr:last-child > td { border-bottom: 0; }
thead tr:last-child > th { border-bottom: 1px solid var(--border); }
/* Column headers (th in a thead, scope="col"). */
th:not([scope="row"]) { color: var(--text-muted); font-size: 13px; font-weight: 600; letter-spacing: 0.1px; }
th[scope="row"] { color: var(--text); font-weight: 600; }
caption { text-align: left; }

/* Text tables that don't stack (the privacy page's three columns) get tighter cells and 14px
   text on phones, so a 360px screen leaves each column room for more than one word. */
@media (max-width: 599.98px) {
  table:not(.stack) { font-size: 14px; }
  table:not(.stack) :is(th, td) { padding: 10px 12px; }
}

@media (max-width: 699.98px) {
  table.stack thead {
    position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
  table.stack,
  table.stack tbody,
  table.stack tr,
  table.stack th,
  table.stack td { display: block; }
  table.stack tbody tr { padding: 14px 16px; border-bottom: 1px solid var(--border); }
  table.stack tbody tr:last-child { border-bottom: 0; }
  table.stack tbody th,
  table.stack tbody td { padding: 2px 0; border: 0; }
  table.stack tbody th { margin-bottom: 4px; color: var(--text); font: 700 16px/1.35 var(--font-body); letter-spacing: 0; }
  table.stack td[data-label]::before { content: attr(data-label) ": "; color: var(--text-muted); }
  table.stack td.price { white-space: normal; }
}


/* 10. Forms ----------------------------------------------------------------------------------
   form.form (with .card.lg for the card look) holds label.field rows. Fields are the app's filled
   inputs: 16px corners, focus border in the primary colour. 16px text so phones don't zoom. */
.form { display: grid; gap: var(--s-4); }
.form > .card-title,
.form > h2 { margin: 0; font: 700 19px/1.3 var(--font-head); letter-spacing: -0.2px; }
.form > p { margin: 0; }

.field {
  display: grid;
  gap: 6px;
  color: var(--text-muted);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.4;
}
/* The filled text control. ".field input/textarea/select" is the intended markup and .input a
   standalone control. The last selector in each list is a safety net for a form whose labels
   have not been given .field yet: inside any .form it styles the text controls at zero weight
   (every real rule beats it; the label itself keeps its plain look), and it leaves radios,
   checkboxes, hidden inputs and input buttons alone. (:where needs Chrome 88 / Safari 14, the
   same baseline as the prose rules. aspect-ratio needs Safari 15, so the waitlist media frames
   in section 13 carry a fallback for it.) */
.field input,
.field textarea,
.field select,
.input,
:where(.form) :where(input:not([type="radio"], [type="checkbox"], [type="hidden"], [type="submit"], [type="button"], [type="reset"], [type="image"], [type="file"], [type="range"], [type="color"]), textarea, select) {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  min-height: 48px;
  margin: 0;
  padding: 12px 16px;
  background: var(--input-fill);
  border: 1px solid var(--control-border);
  border-radius: var(--r-md);
  color: var(--text);
  font: 400 16px/1.4 var(--font-body);
  letter-spacing: 0;
}
.field textarea,
textarea.input,
:where(.form) :where(textarea) { min-height: 120px; resize: vertical; }
/* Selects lose the native arrow (appearance: none above), so they get the app's dropdown
   chevron back: a background image, since a select cannot hold a pseudo-element. Its colour
   follows the theme through --chevron (section 1). */
.field select:not([multiple]),
select.input:not([multiple]),
:where(.form) :where(select:not([multiple])) {
  padding-right: 44px;
  background-image: var(--chevron);
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 20px 20px;
}
.field ::placeholder,
.input::placeholder,
:where(.form) ::placeholder { color: var(--text-subtle); opacity: 1; }
@media (hover: hover) {
  .field input:hover, .field textarea:hover, .field select:hover, .input:hover,
  :where(.form) :where(input:not([type="radio"], [type="checkbox"], [type="hidden"], [type="submit"], [type="button"], [type="reset"], [type="image"], [type="file"], [type="range"], [type="color"]), textarea, select):hover { border-color: var(--text-muted); }
}
.field input:focus,
.field textarea:focus,
.field select:focus,
.input:focus,
:where(.form) :where(input:not([type="radio"], [type="checkbox"], [type="hidden"], [type="submit"], [type="button"], [type="reset"], [type="image"], [type="file"], [type="range"], [type="color"]), textarea, select):focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary);
  outline: 2px solid transparent;            /* still shows in forced-colours mode */
}
/* Invalid fields: a red edge, kept while focused. aria-invalid and :user-invalid sit in separate
   rules on purpose: a browser that doesn't know :user-invalid (Chrome before 119, Safari before
   16.5) drops every selector in a list that contains it, and the aria-invalid edge must survive. */
.field :is(input, textarea, select)[aria-invalid="true"],
.input[aria-invalid="true"] { border-color: var(--danger); }
.field :is(input, textarea, select)[aria-invalid="true"]:focus,
.input[aria-invalid="true"]:focus { box-shadow: 0 0 0 1px var(--danger); }
.field input:user-invalid,
.field textarea:user-invalid,
.field select:user-invalid,
.input:user-invalid { border-color: var(--danger); }
.field input:user-invalid:focus,
.field textarea:user-invalid:focus,
.field select:user-invalid:focus,
.input:user-invalid:focus { box-shadow: 0 0 0 1px var(--danger); }
.field .hint { color: var(--text-muted); font-size: 12.5px; font-weight: 400; }

/* Two fields side by side, one column on small phones. */
.form-row { display: grid; gap: var(--s-4); grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 519.98px) { .form-row { grid-template-columns: minmax(0, 1fr); } }

/* A group of choices (radio chips) with its legend. */
fieldset.choices { margin: 0; padding: 0; border: 0; min-width: 0; }
fieldset.choices legend {
  margin-bottom: 8px;
  padding: 0;
  color: var(--text-muted);
  font-size: 14px;
  font-weight: 500;
}

/* Radio chips: label > input[type=radio] + span. The input covers the chip, invisible, so it
   stays focusable and clickable; the span shows the state. */
.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.chips label { position: relative; display: inline-flex; }
.chips input {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
/* Unselected chips are card-coloured with a borderHigh edge, like the app's chips on a card (the
   chat suggestion chips); a surface fill would look sunken inside a dark card. */
.chips span {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 16px;
  background: var(--card);
  border: 1px solid var(--border-high);
  border-radius: var(--r-pill);
  color: var(--text);
  font-size: 14px;
  font-weight: 500;
  line-height: 1.2;
}
@media (hover: hover) { .chips label:hover span { background: var(--card-high); } }
.chips input:checked + span {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--on-primary);
  font-weight: 600;
}
.chips input:focus-visible + span { outline: 2px solid var(--focus); outline-offset: 2px; }

/* The submit button sits at the start on wide screens and fills the width on phones. */
.form > .btn { justify-self: start; }
@media (max-width: 599.98px) { .form > .btn { justify-self: stretch; } }


/* 11. Long-form prose (legal pages, launch help pages) -------------------------------------
   main.prose. The prose rules weigh no more than a bare element rule: :where(.prose) adds
   nothing, so ":where(.prose) h2" is (0,0,1) like "h2". They beat the element defaults above
   (they come later) and lose to any component class (.card-title, .overline, .intro, .steps,
   details > p, ...). The heading rules also only take plain headings sitting straight in the
   column, so a heading inside a form, card or callout never gets the hairline above it.
   h1 uses the site-wide size (30px, 40px from 700px). */
.prose { padding-block: 20px var(--section-y); font-size: 16px; line-height: 1.65; }
@media (min-width: 700px) { .prose { padding-top: 32px; } }
:where(.prose) h1 { margin-bottom: 8px; }
:where(.prose) > h2:where(:not([class])) {
  margin: 44px 0 12px;
  padding-top: 24px;
  border-top: 1px solid var(--border);
  font-size: 22px;                 /* headlineMedium: section heads in a reading column */
  line-height: 1.3;
  letter-spacing: -0.4px;
}
:where(.prose) > h3:where(:not([class])) { margin: 28px 0 6px; font: 700 17px/1.4 var(--font-body); letter-spacing: -0.1px; }
:where(.prose) :is(ul, ol):where(:not([class])) { margin: 0 0 14px; padding-left: 22px; }
:where(.prose) :is(ul, ol):where(:not([class])) > li { margin: 6px 0; }
:where(.prose) li::marker { color: var(--text-subtle); }
:where(.prose) strong { color: var(--text); }
.prose .lede { margin: 8px 0 24px; }
.prose .updated { margin: 0 0 28px; color: var(--text-muted); font-size: 14px; }
.prose > .btn { margin: 4px 0 12px; }
.prose .form { margin: 16px 0 24px; }
.prose .tablebox { margin: 16px 0 20px; }


/* 12. Small parts --------------------------------------------------------------------------- */

/* Badge (GrokBadge): "MOST POPULAR", "20% OFF". */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 12px;
  background: var(--card-high);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  color: var(--text);
  font: 700 11.5px/1.3 var(--font-head);
  letter-spacing: 1.5px;
  text-transform: uppercase;
  white-space: nowrap;
}

/* Info pill (the app's usage pill): "Pro · 420 credits left". */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.2;
}

/* Status dot and status text. */
.dot {
  display: inline-block;
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--text-muted);
}
.dot.success { background: var(--success); }
.dot.warning { background: var(--warning); }
.dot.info { background: var(--info); }
.dot.danger { background: var(--danger); }
.status { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; font-weight: 600; }
.status.success { color: var(--success); }
.status.warning { color: var(--warning); }
.status.info { color: var(--info); }
.status.danger { color: var(--danger); }

/* Icon tiles: inline SVG icons (the app's duotone Cub icons, fill="currentColor") in a tile.
   .icon-tile 36px settings tile; .sm 32px step tile; .lg 44px plan tile in the primary colour. */
.icon-tile {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 36px;
  height: 36px;
  background: var(--card-high);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text);
}
.icon-tile svg { width: 18px; height: 18px; }
.icon-tile.sm { width: 32px; height: 32px; border: 0; border-radius: 9px; }
.icon-tile.sm svg { width: 16px; height: 16px; }
.icon-tile.lg { width: 44px; height: 44px; border: 0; border-radius: var(--r-sm); background: var(--primary); color: var(--on-primary); }
.icon-tile.lg svg { width: 22px; height: 22px; }


/* 13. Waitlist media -----------------------------------------------------------------------
   Slots for the posters, videos and animations the owner adds under web/waitlist/media/
   (listed in its manifest.json). Every slot starts hidden and stays hidden when the manifest has
   nothing for it. Each frame has its aspect ratio set (--ar, from the manifest's width/height)
   before anything loads, so nothing on the page moves while media arrive. */
/* The hero comes after the app screenshots in .split-media (it arrives late, so it must not push
   them down while they are on screen). */
.media-hero { margin-top: var(--s-6); }
.media-hero > .media-figure.portrait { max-width: none; }
/* A hero slot that holds its shape before the manifest arrives: an empty box of the video's
   proportions (--ar on the slot), filled in place by media.js. Hidden again if there is none. */
.media-hero.reserve:empty {
  aspect-ratio: var(--ar, 9 / 16);
  background: var(--card-high);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}
@supports not (aspect-ratio: 1 / 1) {
  .media-hero.reserve:empty { height: 0; padding-top: var(--ar-pad, 177.78%); }
}
.media-frame {
  position: relative;
  aspect-ratio: var(--ar, 16 / 9);
  width: 100%;
  overflow: hidden;
  background: var(--card-high);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}
/* Safari before 15 (iOS 14) has no aspect-ratio, and everything inside a frame is absolutely
   placed, so the frame would collapse to its 2px border. The old padding trick gives it the same
   shape: --ar-pad is height / width as a percentage (the script sets it next to --ar), and a
   padding percentage is measured against the width of the box around the frame, the figure,
   which the frame fills. */
@supports not (aspect-ratio: 1 / 1) {
  .media-frame { height: 0; padding-top: var(--ar-pad, 56.25%); }
}
/* Upright media (taller than wide) get a phone-sized figure instead of filling the column. The
   cap is on the figure, not the frame, so a caption lines up with the frame (and the fallback
   above measures the frame's own width). */
.media-figure.portrait { max-width: 360px; }
/* Posters fill their grid column or carousel card instead (both sized below), so the gaps
   between posters and at the edge stay even. */
.posters > .media-figure.portrait { max-width: none; }
.media-frame > img,
.media-frame > video,
.media-frame > .lottie {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
}
.media-frame > .lottie svg { display: block; width: 100% !important; height: 100% !important; }
/* A Lottie animation draws over its poster image once it has loaded. */
.media-frame > .lottie { background: transparent; }
.media-figure { margin: 0; min-width: 0; }

/* Play/pause button on moving media (WCAG 2.2.2: anything that moves for more than 5 seconds
   can be paused). The script sets data-state="playing" or "paused" and the aria-label. */
.media-toggle {
  position: absolute;
  right: 12px;
  bottom: 12px;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  background: rgba(0, 0, 0, 0.62);
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 50%;
  color: #FFFFFF;
  cursor: pointer;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.media-toggle::before {
  content: "";
  width: 20px;
  height: 20px;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M8 5.5v13l10.5-6.5z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M8 5.5v13l10.5-6.5z'/%3E%3C/svg%3E") center / contain no-repeat;
}
.media-toggle[data-state="playing"]::before {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6.5 5h4v14h-4zM13.5 5h4v14h-4z'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M6.5 5h4v14h-4zM13.5 5h4v14h-4z'/%3E%3C/svg%3E");
}
.media-toggle:focus-visible { outline: 2px solid #FFFFFF; outline-offset: 2px; box-shadow: 0 0 0 4px #000000; }

/* Posters: a grid on wide screens (two across from 700px, about 320-430px each; three from
   920px, at most 350px), a sideways carousel on phones (cards 78% of the width, at most 360px,
   so the next poster always peeks in). media.js gives the browser these widths
   ("sizes"), so keep the two in step. */
.posters {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
}
@media (max-width: 699.98px) {
  .posters {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: min(78%, 360px);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--gutter);
    margin-inline: calc(-1 * var(--gutter));
    padding: 0 var(--gutter) 8px;
    scrollbar-width: thin;
  }
  .posters > * { scroll-snap-align: start; }
}

/* Animation blocks (Lottie or short video loops), meant to be square or 4:3 and filling their
   column. An upright one keeps the phone-sized cap above (a full-width 9:16 loop would be taller
   than the screen on a tablet), with its caption under it at the same width. */
.anims {
  display: grid;
  gap: var(--s-4);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr));
}
/* section#media > .wrap.media-stack > .media-group (posters, then animations). A grid gap, so a
   hidden group leaves no stray space. */
.media-stack { display: grid; gap: var(--s-7); }
.media-group { min-width: 0; }
.media-group > h2 { margin-bottom: var(--s-5); }


/* 14. Motion ---------------------------------------------------------------------------------
   The app is almost still: quick colour changes on hover, a fade-up when something new appears,
   the breathing logo while working. All of it is off when the system asks for reduced motion. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
  .btn,
  .site-nav a,
  .chip,
  ul.links a,
  .chips span,
  summary,
  .field input,
  .field textarea,
  .field select,
  .input,
  :where(.form) :where(input, textarea, select),
  .template,
  .footer-col a,
  .crumbs a {
    transition: background-color 150ms ease-out, border-color 150ms ease-out, color 150ms ease-out,
                box-shadow 150ms ease-out, opacity 150ms ease-out;
  }
  summary::after { transition: transform 180ms var(--ease); }

  /* Media frames the script adds get .fade-up: the app's FadeUpwards page transition. */
  .fade-up { animation: fade-up 300ms var(--ease) both; }

  /* The app's "working" indicator, for a logo or dot that shows progress. */
  .breathe { animation: breathe 1.4s ease-in-out infinite alternate; }
}
@keyframes fade-up {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}
@keyframes breathe {
  from { opacity: 0.55; transform: scale(0.9); }
  to { opacity: 1; transform: scale(1); }
}


/* 15. Print and forced colours -------------------------------------------------------------- */
@media print {
  .site-header { position: static; background: none; -webkit-backdrop-filter: none; backdrop-filter: none; }
  .site-nav, .skip, .media-toggle { display: none; }
  /* A printout (or saved PDF, mostly the legal pages) keeps the text, the company block and the
     © line; the footer's link columns and the breadcrumb are navigation, useless on paper. */
  .site-footer .footer-col, .crumbs { display: none; }
  body { background: #FFFFFF; color: #000000; }
  .card, .grid > *, ol.steps > li, details, .tablebox, .plan, .trial, .prompt, .panel { box-shadow: none; break-inside: avoid; }
}

@media (forced-colors: active) {
  .btn, .chip, ul.links a, .chips span, .currency, details, .card, .plan, .trial, .prompt, .panel { border: 1px solid CanvasText; }
  .chips input:checked + span,
  .currency .btn:not(.quiet) {
    forced-color-adjust: none;
    background: Highlight;
    color: HighlightText;
    border-color: Highlight;
  }
  ul.checks li::before, .plan li::before, .trial li::before, summary::after, .media-toggle::before,
  .hero #launch-note::before {
    forced-color-adjust: none;
    background: CanvasText;
  }
}
