/* ============================================================================
   Grounded - marketing + document surfaces, in BACKBONE.

   Loaded by: /home/, /browse/, /help/, /privacy/, /terms/, /accessibility/ and
   /b/ (the bot profile). It owns three things and nothing else:

     1. the SHARED CHROME that public/js/chrome.js injects at runtime
        (`header.nav` + `footer.foot` + the account widget). That module is not
        ours to edit, so its class names are re-dressed here instead.
     2. the SHARED PRIMITIVES those pages have always used by name - `.wrap`,
        `.btn`, `.skip`, `.eyebrow`, `.lede`, `.empty`, h1..h3. /b/b.css builds
        on these, so they keep their names and keep working.
     3. the /home/ composition and the /help//privacy//terms//accessibility
        document layout (previously duplicated in four inline <style> blocks).

   TOKENS ARE NOT DECLARED HERE. public/css/backbone.css loads AFTER this file
   and re-values every token for both themes, so a palette here would only be
   dead weight. Everything below reads --sig / --text / --muted / --line / ...
   and inverts for free.

   Geometry: radius 0. The hexagon clip is the only curve.
   Accent: one, --sig. --warn appears only where a refusal is being marked.
   ========================================================================= */

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
a { color: inherit; }
img { max-width: 100%; display: block; }

/* ══ THE ONE MOBILE GUTTER ════════════════════════════════════════════════
   A flat layout constant, not a themed one (same value in light and dark), so
   declaring it here does not break the "tokens are not declared here" rule
   above - that rule is about the colour tokens backbone.css re-values per
   theme. Below the RESPONSIVE breakpoint every surface this file lays out -
   the hero, the shared `.empty` card, and the `.wrap`/`.doc-head` document
   family - pins to this ONE value instead of each reading backbone's `--gut`
   independently, which is a vw-scaled clamp() and would otherwise let them
   drift a few px apart from each other between 391-640px even though they
   read the identical custom property today. /b/'s own mobile inset (a
   sibling surface this file does not own - see b.css) should match this same
   18px so the profile page reads as one system rather than a coincidence. */
:root { --gut-m: 18px; }

/* ══ THE ONE BAND RHYTHM ══════════════════════════════════════════════════
   Same category as --gut-m above - a flat layout constant with the same value
   in both themes - so it is declared here for the same reason, and the "tokens
   are not declared here" rule (about the COLOUR tokens backbone.css re-values
   per theme) still holds.

   Before this existed, every band on /home/ picked its own clamp() and the
   gaps between them ran roughly 120px to 250px with nothing in common: the
   page read as assembled rather than composed. Every vertical band inset is
   now a whole or half step of THIS value and nothing else -

     1.5x  the hero's top. The one place allowed to breathe more, and it
           breathes by a step of the same scale rather than a free number.
     1x    every .bb-band, top and bottom, so band to band is always 2x.
     0.5x  the hero's bottom. The measurement strip is the hero's tail, not
           the next band - it keeps a 0 top inset, and the half step is what
           attaches it to the console above rather than floating it.

   The clamp's floor IS the narrow-viewport step: below ~1040px it stops scaling
   and holds 48px, so a phone gets a rhythm proportionate to its own screen
   instead of a desktop one shrunk by vw.

   TIGHTENED 2026-08-06, from clamp(56px, 6vw, 92px). At 1440 that resolved to
   86.4px on both edges of six bands — 173px between one band's last line and the
   next band's first, over and over, on a page that measured 4,409px. The house
   rules do license a 45-60% whitespace budget, but explicitly on the condition
   that the empty space carries texture; between these bands it carried nothing
   but gradient. The rhythm above is unchanged — every step is still a whole or
   half of this one number — it is the number that moved. */
:root { --band-y: clamp(48px, 4.6vw, 76px); }

/* ══ LAYOUT ════════════════════════════════════════════════════════════════ */
.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 var(--gut);
  position: relative;
  z-index: 2;
}
section { scroll-margin-top: 84px; }

/* ══ TYPE ══════════════════════════════════════════════════════════════════
   Sentence case at the top level on purpose: /b/ renders a bot's own NAME in
   an h1, and a bot named "Half Step" must not become "HALF STEP". Uppercase is
   opted into by the page-level classes below (.bb-display, .doc h2, ...). */
h1, h2, h3 { font-family: var(--sans); letter-spacing: -0.015em; text-wrap: balance; }
h1 { font-size: clamp(1.9rem, 4.6vw, 3.1rem); font-weight: 800; line-height: 1.04; margin: 0 0 18px; }
h2 { font-size: clamp(1.45rem, 2.8vw, 2.1rem); font-weight: 700; line-height: 1.1; margin: 0 0 14px; }
h3 { font-size: 1.12rem; font-weight: 700; line-height: 1.25; margin: 0 0 8px; letter-spacing: -0.005em; }

.lede {
  color: var(--muted);
  font-size: clamp(15px, 1.2vw, 17px);
  line-height: 1.62;
  max-width: 62ch;
  text-wrap: pretty;
}
.muted { color: var(--muted); }

/* One eyebrow per three sections, at most. Mono, wide, signal cyan. */
.eyebrow {
  display: block;
  margin: 0 0 20px;
  font: 600 11px/1.4 var(--mono);
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--sig);
}

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

/* ══ SKIP LINK ═════════════════════════════════════════════════════════════
   theme.css re-colours `.skip` in dark (`color: var(--paper)`), which lands
   #061f2b on the cyan fill: 10.7:1. Light keeps --sig-ink on --sig: 8.7:1. */
.skip {
  position: absolute; left: 12px; top: -70px; z-index: 100;
  padding: 11px 18px;
  background: var(--sig);
  color: var(--sig-ink);
  font: 700 12px/1 var(--mono);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  text-decoration: none;
  transition: top var(--t-ui, 160ms) ease;
}
.skip:focus { top: 12px; }

/* ══ BUTTONS ═══════════════════════════════════════════════════════════════
   The legacy names (.btn/.btn-primary/.btn-ghost/.btn-sm) are what chrome.js,
   /b/ and the directory emit, so they carry the Backbone button rather than
   being renamed. Same shape and same states as .bb-btn. */
.btn {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  gap: 9px;
  min-height: 46px;
  padding: 0 24px;
  border: 1px solid var(--line-hi);
  background: transparent;
  color: var(--text);
  font: 600 11.5px/1 var(--mono);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  overflow: hidden;
  transition: border-color 160ms ease, color 160ms ease, background-color 160ms ease, translate 100ms ease;
}
/* Directional wipe rather than a glow: the affordance should read mechanical. */
.btn::before {
  content: "";
  position: absolute; inset: 0; z-index: -1;
  background: color-mix(in srgb, var(--sig) 12%, transparent);
  scale: 0 1;
  transform-origin: left center;
  transition: scale 220ms var(--ease, ease);
}
.btn:hover { border-color: var(--sig); color: var(--sig-text); }
.btn:hover::before { scale: 1 1; }
.btn:active { translate: 0 1px; }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; pointer-events: none; }

.btn-primary { background: var(--sig); border-color: var(--sig); color: var(--sig-ink); }
.btn-primary::before { background: var(--sig-lift); }
.btn-primary:hover { background: var(--sig-lift); border-color: var(--sig-lift); color: var(--sig-ink); }

.btn-ghost { background: transparent; border-color: var(--line-hi); color: var(--text); }
.btn-ghost:hover { border-color: var(--sig); color: var(--sig-text); }

.btn-sm { min-height: 34px; padding: 0 13px; font-size: 10px; letter-spacing: 0.16em; }
.btn-lg { min-height: 54px; padding: 0 32px; }
.btn svg { width: 15px; height: 15px; flex: none; }

/* ══ INJECTED CHROME: header.nav ═══════════════════════════════════════════
   Markup comes from public/js/chrome.js. Selectors here only re-dress it. */
.nav {
  position: sticky; top: 0; z-index: 40;
  /* THE HEIGHT IS RESERVED BEFORE THE MARKUP EXISTS. `<header class="nav">` ships
     EMPTY — chrome.js fills it on module load — so until that module ran the
     header was 0px tall and every pixel of the document sat 68px too high, then
     jumped. Measured as the whole of /home/'s remaining layout shift: CLS 0.052,
     a Lighthouse score of 0.99 and the one failing audit on the page.
     `min-height` on the header itself (not on `.nav-in`, which does not exist
     yet either) is what holds the space open through that gap. It matches
     `.nav-in`'s own 68px floor, so the filled header lands on exactly the line
     the empty one was already occupying and nothing moves at all. */
  min-height: 68px;
  background: color-mix(in srgb, var(--deep-0) 80%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--text) 16%, transparent);
  backdrop-filter: blur(12px) saturate(140%);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
}
/* Wrapping is allowed at EVERY width, not just the mobile breakpoint. Between 561px and ~950px
   nothing thins this row and nothing wrapped it, so the full link set simply overflowed: measured
   909px of content in a 640px viewport, taking the document's horizontal scroll with it. `min-height`
   rather than `height` so the bar keeps its 68px on the one-line desktop case but can grow instead
   of pushing sideways. */
.nav-in {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: clamp(12px, 2.4vw, 26px); row-gap: 8px;
  min-height: 68px;
}

.brand {
  display: inline-flex; align-items: center; gap: 10px;
  flex: none;
  font: 700 clamp(14px, 1.5vw, 17px)/1 var(--sans);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--text);
}
/* The `.brand-mark` corrections that used to live here are gone with the thing
   they corrected: chrome.js shipped a rounded green chat-bubble SVG, and these
   rules squared its rx and repainted it. The nav mark is now the `.bb-mark`
   bracket wordmark, which has no SVG at all. */

.nav-links {
  margin-left: auto;
  display: flex; align-items: center; flex-wrap: wrap; justify-content: flex-end;
  gap: clamp(6px, 1.4vw, 16px); row-gap: 8px;
}
.nav-links a:not(.btn) {
  font: 600 11px/1 var(--mono);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
  padding: 8px 0;
  border-bottom: 1px solid transparent;
  transition: color 160ms ease, border-color 160ms ease;
}
.nav-links a:not(.btn):hover,
.nav-links a:not(.btn)[aria-current="page"] { color: var(--text); border-bottom-color: var(--sig); }

/* ── ONE DOOR IN, ONE ACTION ────────────────────────────────────────────────
   chrome.js marks three header entries `.nav-hide`: the two in-page anchors
   (Features, Try it) and the Admin sign-in button. They used to drop out only
   below 560px, which left the desktop row carrying five text links, TWO
   sign-ins side by side and a primary CTA. Two sign-ins side by side make a
   first-time visitor choose a realm - their own account, or one bot's console -
   before anything has told them the difference, which is the header doing the
   sign-in page's job: /login/ already closes with "Managing a single chatbot's
   own console instead? Use Bot admin."

   So the rule holds at EVERY width now, and the row is one sign-in entry plus
   one primary action. Nothing is stranded. Admin sign in lives in the Build
   column of /home/'s own footer, in chrome.js's footer row on the other pages,
   and one click past /login/ — but NOT in the compact `.nav-menu`, which would
   have made the phone header carry a route the desktop header does not (see the
   inventory note above navMenuMarkup in js/chrome.js);
   Features and Try it are sections of /home/ itself, listed in that page's
   footer under Product. */
.nav-links a.nav-hide { display: none; }

/* ── compact mobile menu (markup: chrome.js's `.nav-menu`, the marketing
   variant only — /my/'s nav wraps instead of hiding anything, so it ships
   neither the trigger nor the panel) ─────────────────────────────────────────
   Hidden by default; the RESPONSIVE section below reveals it at exactly the
   breakpoint where Browse bots/Pricing/Help disappear from the row, so it is
   never on screen next to the links it duplicates. "Create a bot" is the one
   context-appropriate primary action left outside it (2026-08-04 review, P1)
   — it stays a plain nav link/button at every width, so it does not need a
   second copy in here. Sign in and the theme toggle DID move in here on
   2026-08-06: keeping them in the row is what made the mobile header three
   lines and 151px tall. See the 560px block below. */
.nav-menu { position: relative; display: none; }
.nav-menu-btn {
  display: inline-flex; align-items: center; gap: 8px;
  min-width: 44px; min-height: 44px;
  padding: 0 14px;
  border: 1px solid var(--line-hi);
  background: transparent;
  color: var(--text);
  font: 600 11px/1 var(--mono);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  cursor: pointer;
  transition: border-color 160ms ease, color 160ms ease;
}
.nav-menu-btn:hover,
.nav-menu-btn[aria-expanded="true"] { border-color: var(--sig); color: var(--sig-text); }
.nav-menu-ico { display: inline-flex; flex-direction: column; gap: 3px; }
.nav-menu-ico span { display: block; width: 15px; height: 1.5px; background: currentColor; }

.nav-menu-panel {
  position: absolute; right: 0; top: calc(100% + 8px);
  z-index: 50;
  min-width: 216px;
  display: flex; flex-direction: column;
  background: var(--deep-0);
  border: 1px solid var(--line-hi);
  padding: 6px;
}
/* [hidden] loses to an explicit `display` on the same element — pin it back. */
.nav-menu-panel[hidden] { display: none !important; }
/* 12px padding on a 1-line 11.5px mono label totals 42px — two pixels under the minimum, on a
   control that only ever appears on a touch viewport (the panel is `display:none` above 560px).
   Flex + min-height rather than more padding, so the number is stated instead of arrived at. */
.nav-menu-panel a,
.nav-menu-panel .nav-menu-signout {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 12px 13px;
  font: 600 11.5px/1 var(--mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
  text-decoration: none;
  border-left: 2px solid transparent;
  transition: color 160ms ease, background-color 160ms ease, border-color 160ms ease;
}
/* Sign out is a <button> (it POSTs), so it needs the link dressing restated rather than inherited —
   same row, same 44px target, same type. `width: 100%` and `text-align: left` undo the two things a
   button does differently from an anchor inside a column flex container. */
.nav-menu-panel .nav-menu-signout {
  width: 100%;
  text-align: left;
  background: transparent;
  border: 0;
  border-left: 2px solid transparent;
  cursor: pointer;
}
.nav-menu-panel a:hover,
.nav-menu-panel .nav-menu-signout:hover { color: var(--text); background: color-mix(in srgb, var(--sig) 9%, transparent); }
/* The current-page mark: colour AND a left rule, so it does not read on colour alone. */
.nav-menu-panel a[aria-current="page"] { color: var(--sig-text); border-left-color: var(--sig); }

/* The theme row. A labelled group rather than a fourth link, because it is a control and not a
   destination — and because `role="menu"` may not hold a bare <div>, so the label and the switch
   sit inside `role="group"` (chrome.js) with the mounted button carrying `role="menuitem"`. The
   1px rule above it is the only separator in the panel: it marks where destinations end and
   controls begin, which is the one distinction a visitor scanning this list needs. */
.nav-menu-theme {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  min-height: 44px;
  padding: 6px 13px;
  margin-top: 6px;
  border-top: 1px solid var(--line);
}
.nav-menu-theme-lbl {
  font: 600 11.5px/1 var(--mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}
.nav-menu-theme-host { display: inline-flex; --bb-ts-size: 44px; }

/* ── account widget ─────────────────────────────────────────────────────────
   Nothing to correct here any more, and that is the change worth recording.
   chrome.js used to build the account pill itself and inject a `.gcb-acct-*`
   <style> block at RUNTIME — after every stylesheet, so this surface could only
   answer it with higher specificity: radius back to 0, and a readable ink on the
   accent fill, because the injected rule wrote `color:#fff` over
   `background: var(--verified)` and the dark verified signal #5ce1e6 is a LIGHT
   fill (white on it is 1.3:1; --sig-ink #04202a on it is 10.4:1).
   The pill is now js/ui/account-menu.js rendering `.bb-idbtn` + `.bb-pop` from
   css/backbone.css, square and tokenised at source, so there is nothing left to
   undo from out here. */

/* ══ INJECTED CHROME: footer.foot ══════════════════════════════════════════
   The one-row footer chrome.js builds, for /browse/, the documents and /b/.
   /home/ ships its own four-column foot instead (see .site-foot below). */
.foot {
  border-top: 1px solid var(--line);
  padding: 34px 0 40px;
  font-size: 14px;
  color: var(--muted);
}
.foot-in { display: flex; flex-wrap: wrap; gap: 12px 28px; align-items: center; }
.foot a, .foot .foot-linkbtn {
  color: var(--muted);
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
  transition: color 160ms ease;
}
.foot a:hover, .foot .foot-linkbtn:hover { color: var(--sig-text); }
.foot .foot-brand {
  font: 700 13px/1 var(--sans);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text);
}
/* "Cookie choices" must be a BUTTON (it reopens the chooser rather than
   navigating) but sits in a row of links, so it is given the link's exact
   appearance. Focus stays visible: the outline is never removed. */
.foot .foot-linkbtn { font: inherit; font-size: 14px; background: none; border: 0; padding: 0; cursor: pointer; }

/* ══ TOUCH TARGETS: the header mark and the footer link row ════════════════
   Measured at 360px before this block: the brand wordmark had a hit area of
   about 127 × 12 CSS px, and the footer links 19–22 px tall. Both are plain
   inline text, so their box was the text box — which is why every OTHER
   control on the page already cleared 44px (they are buttons with padding)
   and these two did not. WCAG 2.5.8 asks for 24px, 2.5.5 for 44px; these are
   STANDALONE links, not items in a sentence, so the 44px reading applies.

   Vertical padding + a negative margin of the same size: the target grows,
   the visual rhythm of the row does not move. Nothing here changes how any
   of it LOOKS — the row keeps its 12px/28px gaps and the mark keeps its
   position — it changes only what a finger has to hit.

   Scoped to `pointer: coarse` rather than a width breakpoint. The failure is
   a property of the INPUT DEVICE, not of the viewport: a touch laptop at
   1400px has exactly the same problem, and a narrow window on a mouse-driven
   desktop does not have it at all. A width query would have fixed the wrong
   set of devices in both directions. */
@media (pointer: coarse) {
  .foot-in a,
  .foot-in .foot-linkbtn {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    /* The row's own `gap` already separates these, so the negative margin only
       cancels the padding this rule adds — it never pulls neighbours together. */
    margin-block: -11px;
  }
  .brand {
    min-height: 44px;
    /* Horizontal padding too: the mark is wide but only ~12px tall, and it sits
       at the very start of the row where a thumb reaching from the edge is most
       likely to fall short. */
    padding: 11px 6px;
    margin: -11px -6px;
  }

  /* The four-column site footer that /home/ and /pricing/ ship instead of the
     one-row `footer.foot` chrome.js injects. Same defect, same fix, stated here
     rather than in backbone.css so this stays one file to bump: both pages load
     home.css, and backbone.css is loaded by twenty-odd surfaces that do not have
     this footer at all.
     No negative margin on these: they are stacked list items whose 11px
     `li + li` gap is the visual rhythm, and pulling them back together would
     re-create the overlap the padding is there to prevent. The column simply
     gets taller, which on a phone footer costs nothing. */
  .bb-foot-col a,
  .bb-foot-col .bb-linkbtn {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
  .bb-foot-col li + li { margin-top: 0; }
  .bb-foot-base a { display: inline-flex; align-items: center; min-height: 44px; }
}

/* ══ HOME: THE BAND RHYTHM, APPLIED ════════════════════════════════════════
   backbone.css loads AFTER this file, so a bare `.bb-band` here would lose to
   its own clamp(); the descendant selector is what lets the one scale win.
   `.bb-band` appears on /home/ and nowhere else, so this reaches none of the
   other six pages that load this stylesheet. */
#main .bb-band { padding-block: var(--band-y); }

/* ══ HOME: HERO ════════════════════════════════════════════════════════════ */
/* `padding-block`, deliberately not the `padding` shorthand, for the reason
   spelled out above `.doc-head` below: a 3-value shorthand also sets the inline
   padding, and the hero's inline inset belongs to `.bb-shell` inside it. */
.hero { position: relative; overflow: hidden; padding-block: calc(var(--band-y) * 1.5) calc(var(--band-y) * 0.5); }
.hero-in { position: relative; z-index: 2; text-align: center; }
.hero .bb-display, .hero .bb-lede { margin-inline: auto; }
.hero .bb-lede { text-align: center; max-width: 56ch; }
.hero-cta { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; margin-top: 32px; }
.hero-note {
  margin: 22px 0 0;
  font: 500 11.5px/1.7 var(--mono);
  letter-spacing: 0.09em;
  color: var(--muted);
}
.hero-console { max-width: 720px; margin: clamp(40px, 5vw, 64px) auto 0; }
.hero-console .bb-console-body { min-height: 232px; justify-content: flex-end; }

/* ══ HOME: MEASUREMENT STRIP ═══════════════════════════════════════════════
   The hero's tail, not the band after it: no top inset at all, so the half
   step the hero ends on is the whole distance between the console and this
   row. Its bottom is a full step, which puts the standard 2x between it and
   the honeycomb. */
.strip-band { padding-block: 0 var(--band-y); }

/* ══ HOME: HONEYCOMB LEGIBILITY ════════════════════════════════════════════
   Layout, interlock and clip-path stay exactly as backbone.css draws them.
   Only the type inside each cell moves: at 12.5px label and 12.5px body the
   six tiles were the smallest text on the page sitting on the busiest fill,
   with a ~25px-tall chip for a button. Everything here is one step up.

   Contrast is checked against the LIGHTEST point of each face, not the darkest:
   both foregrounds are light-on-dark, so the bright cyan lobe through the
   middle of every fill is the worst case and the dark rim is the easy one.
   That lobe composites to about rgb(40, 108, 118) once .bb-hex-scrim is over
   it - 6.0:1 for the white label, 4.9:1 for the #d8ecf4 body. Size does not
   move either number, it only makes them easier to earn, so these rules raise
   the size and leave the two colours exactly where backbone.css set them.

   backbone.css loads after this file, so each rule carries the extra `.bb-comb`
   to outrank the one it corrects. `.bb-comb` is on /home/ and nowhere else. */
.bb-comb .bb-hex-label { font-size: 13.5px; }
.bb-comb .bb-hex-desc { font-size: 14px; }
/* The whole hexagon is the link, so this chip was never the only hit area - but
   it READS as the button, and on a touch screen anything that reads as a button
   has to be one. inline-flex + min-height rather than more padding, so the label
   stays optically centred in the taller box instead of riding its top edge. */
.bb-comb .bb-hex-go {
  display: inline-flex; align-items: center;
  min-height: 40px;
  padding: 0 15px;
  font-size: 10.5px;
}
@media (pointer: coarse) {
  .bb-comb .bb-hex-go { min-height: 44px; padding-inline: 18px; }
}

/* ══ HOME: TRY IT LIVE ═════════════════════════════════════════════════════ */
/* Wider than the 820px the single-pane version used: two panes of answer at a
   readable size do not fit in it, and this section is the page's one hero
   statement, which is the case the house rules reserve full width for. */
.try-in { max-width: 1080px; margin-inline: auto; text-align: center; }
.try-in .bb-lede { max-width: 62ch; margin-inline: auto; text-align: center; }

/* ── the two chip groups ──────────────────────────────────────────────────
   In-scope and off-scope, labelled. The off-scope questions used to be a
   fourth chip in a row of four, which made "the bot refuses" look like a
   product question someone forgot to delete. */
.try-chips { display: flex; flex-wrap: wrap; gap: 10px 34px; justify-content: center; margin: 26px 0 22px; }
.try-chip-group { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.try-chip-label {
  font: 700 9.5px/1 var(--mono);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
}
.try-chip-row { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
/* A QUESTION IS A SENTENCE, AND `.bb-chip` IS `white-space: nowrap`. That default was written for
   one-word filters and it held here only while the examples were four words long. "Which medium
   roast coffees under $17 are in stock?" rendered as a single 490px chip inside a 390px viewport —
   measured, in device emulation, on the one section of this page a phone visitor is meant to touch.
   The row already wraps BETWEEN chips; this is what lets a chip wrap inside itself. Two classes so
   it outranks backbone.css, which loads after this file. */
.try-chip-row .bb-chip-q {
  white-space: normal;
  max-width: 100%;
  text-align: left;
  line-height: 1.45;
}
/* The off-scope group's label carries the refusal colour, the same one the
   refusal pane and the refusal flag use. One meaning, one hue, three places. */
.try-chip-group:last-child .try-chip-label { color: var(--warn); }

.try-box { text-align: left; }

/* ── the two panes ────────────────────────────────────────────────────────
   Side by side above 860px, stacked below. Both keep their last result, which
   is the whole point: a grounded cited answer and a deliberate refusal on
   screen at the same time. The panel used to hold one answer, so clicking the
   off-scope chip erased the grounded one and the contrast existed only in the
   visitor's memory.

   `align-items: stretch` plus the divider rule means the two columns read as
   one object split in half rather than as two cards. Separation by a single
   1px rule and nothing else, per the house rule that surfaces separate by
   luminance and structure rather than by boxing everything. */
/* TWO CLASSES, NOT ONE, and that is not decoration. backbone.css is loaded AFTER
   this file so its tokens win, and it declares `.bb-console-body { display: flex }`
   at the same specificity — a bare `.try-body { display: grid }` lost the cascade
   silently and both panes rendered full width, stacked, at 1440px. Measured.
   Every layout property below has to out-specify that rule. */
.bb-console-body.try-body {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0;
  min-height: 168px;
  padding: 0;
}
.try-pane { padding: 16px 18px; min-width: 0; }
.try-pane + .try-pane { border-top: 1px solid var(--line); }
.try-pane-h {
  margin: 0 0 12px;
  font: 700 9.5px/1 var(--mono);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--sig);
}
.try-pane-refusal .try-pane-h { color: var(--warn); }
/* The bracket in the empty state is a sample of the thing being described, so
   it gets the marker's own type but none of its interactivity. */
.try-empty-mark { font-family: var(--mono); color: var(--sig-text); }

@media (min-width: 860px) {
  .bb-console-body.try-body { grid-template-columns: 1fr 1fr; }
  .try-pane + .try-pane { border-top: 0; border-left: 1px solid var(--line); }
}
/* Placeholder-weight text that still has to be read: --faint drops to 3.4:1 on
   the brightest band, --muted holds 5.5:1 there and 5.4:1 in light. */
.try-empty { color: var(--muted); font-size: 14px; margin: 0; max-width: 52ch; }
.try-empty b { color: var(--text); font-weight: 700; }
.try-form { padding: 14px 16px; border-top: 1px solid var(--line); display: flex; flex-direction: column; gap: 8px; }
.try-row { display: flex; gap: 8px; }
.try-row .btn { flex: none; }
.try-input { flex: 1; min-width: 0; }
.try-input::placeholder { color: var(--muted); opacity: 1; }
/* The submitted question, restated above the answer (home.js `echo()`). Quieter than the answer —
   it is context, not the result — and separated by a rule so the panel reads as two things. */
.try-asked {
  margin: 0 0 14px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--line);
  font-size: 14px;
  line-height: 1.5;
  color: var(--muted);
}
.try-asked-label {
  display: block; margin-bottom: 6px;
  font: 700 9.5px/1 var(--mono);
  letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--muted);
}
.try-answer { font-size: 14.5px; line-height: 1.6; white-space: pre-wrap; }
.try-flag {
  display: block; margin-bottom: 8px;
  font: 700 9.5px/1 var(--mono);
  letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--sig);
}
/* --warn is reserved for one meaning on this platform: a refusal. */
.try-answer.refusal { border-left: 2px solid var(--warn); padding-left: 13px; }
.try-answer.refusal .try-flag { color: var(--warn); }
.try-answer.problem { border-left: 2px solid var(--line-hi); padding-left: 13px; }
.try-answer.problem .try-flag { color: var(--muted); }

/* ══ TRY-IT: the interactive citation marker ═══════════════════════════════
   The single most important control on this page, and until now the least
   interactive element on it: `[1]` was a bracket and a digit in a text node,
   on the page whose entire argument is that an answer can be checked.

   Class names match public/styles.css's chat markers on purpose — this is the
   same affordance in a second surface, not a second feature, and a visitor who
   learns it here finds it unchanged in the chat. The styles are restated
   rather than shared because /home/ does not load the chat stylesheet.

   A <button>, so it is focusable and keyboard-operable without any extra
   wiring; hover is layered on top and is never the only way in. */
/* The marker and its panel, as siblings inside one positioned inline box. The panel used to be a
   CHILD of the button, which was legal while it held only text and stopped being legal when it
   gained its own control — see home.js `citationMarker`. */
.citation-wrap { position: relative; display: inline; }
.citation-chip {
  position: relative;
  display: inline;
  padding: 0 1px;
  margin: 0 1px;
  border: 0;
  background: transparent;
  color: var(--sig-text);
  font: inherit;
  cursor: pointer;
  /* `white-space: pre-wrap` on .try-answer would otherwise let the marker's own
     whitespace wrap away from the word it belongs to. */
  white-space: normal;
}
.citation-chip sup {
  font-family: var(--mono);
  font-size: 0.72em;
  font-weight: 700;
  padding: 1px 3px;
  border-bottom: 1px solid color-mix(in srgb, var(--sig) 55%, transparent);
  transition: background-color 160ms var(--ease), border-color 160ms var(--ease);
}
.citation-chip:hover sup,
.citation-chip[aria-expanded="true"] sup {
  background: color-mix(in srgb, var(--sig) 16%, transparent);
  border-bottom-color: var(--sig);
}
.citation-chip:focus-visible { outline: 2px solid var(--sig); outline-offset: 2px; border-radius: 2px; }

/* The popover. Absolutely positioned so opening one never reflows the answer
   under it — a citation that moves the sentence it belongs to as you inspect
   it is worse than no citation. */
.citation-pop {
  position: absolute;
  left: 0;
  top: calc(100% + 8px);
  z-index: 30;
  display: block;
  width: max-content;
  max-width: min(320px, 68vw);
  padding: 10px 12px;
  background: var(--deep-0);
  border: 1px solid var(--line-hi);
  box-shadow: 0 10px 30px rgb(0 0 0 / 0.35);
  white-space: normal;
  text-align: left;
  cursor: auto;
}
.citation-pop[hidden] { display: none !important; }
.citation-pop-title {
  display: block;
  margin-bottom: 6px;
  font: 700 9.5px/1.3 var(--mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--sig-text);
}
/* The QUOTED SPAN. Set as a quotation, because that is what it is — the exact
   passage the sentence above came from, not a description of it. */
.citation-pop-excerpt {
  display: block;
  font-size: 13px;
  line-height: 1.55;
  color: var(--muted);
  border-left: 2px solid var(--line-hi);
  padding-left: 10px;
}

/* WHAT KIND of source this is — knowledge base, dataset row, web result. The
   chat surface's marker has carried this line for some time (public/app.js
   `citationKindLabel`); this page's did not, so a row out of a table and a
   paragraph out of an article looked identical in the one place a visitor
   actually inspects them. Same wording, same order, same treatment. */
.citation-pop-kind {
  display: block;
  margin-bottom: 6px;
  font: 700 9.5px/1.3 var(--mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--faint);
}
/* WHERE in the original — "page 3, table 1, row 4" — present only for a row
   extracted out of a document. Kept visually separate from the excerpt for the
   same reason it is a separate field in the payload: it is provenance, not
   evidence, and it must not read as part of the quotation. */
.citation-pop-prov {
  display: block;
  margin-top: 8px;
  font: 400 11.5px/1.5 var(--mono);
  color: var(--faint);
}
/* Step 06. Not a link — there is no page for a typed-in source to open — so it
   is a button that reveals that source's own record in the inventory below. */
.citation-open {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  margin-top: 10px;
  padding: 0 10px;
  border: 1px solid var(--line-hi);
  background: transparent;
  color: var(--sig-text);
  font: 700 9.5px/1 var(--mono);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
}
.citation-open:hover { border-color: var(--sig); background: color-mix(in srgb, var(--sig) 12%, transparent); }
.citation-open:focus-visible { outline: 2px solid var(--sig); outline-offset: 2px; }
@media (pointer: coarse) { .citation-open { min-height: 44px; padding-inline: 14px; } }

/* EDGE CONTAINMENT IS MEASURED, NOT GUESSED. This used to be `@media (min-width: 860px) {
   .try-pane-refusal .citation-pop { left: auto; right: 0 } }` — a rule that fixed the one case
   somebody had looked at (the right-hand pane on a desktop) and left the case that actually broke:
   a marker two thirds across a 390px phone opened a 265px panel 130px past the screen edge and
   widened the document to 520px. Which side overflows depends on where in the sentence the marker
   fell, so home.js `keepInView()` measures the panel after opening it and shifts it in by exactly
   the overhang. Nothing here needs to know about breakpoints or panes. */
@media (prefers-reduced-motion: reduce) {
  .citation-chip sup { transition: none; }
}

/* ══ TRY-IT: the wait, and the way out of a failed one ═════════════════════
   The dots used to be the entire loading state, and a measured request from
   this panel took 7.4 seconds — long enough that three animating squares read
   as a stalled page rather than a bot thinking. `.try-wait-note` is the
   caption home.js rewrites as the wait lengthens; it is plain sentence-case
   text at body colour, not a mono label, because it is a sentence addressed
   to the reader rather than an operational tag. */
.try-wait { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.try-wait-note { font-size: 13.5px; line-height: 1.5; color: var(--muted); }

/* Every failure state now ends in a control instead of a dead end. Sized as a
   normal small button so it clears 44px on touch via the shared .btn rules;
   the top margin separates it from the message it belongs to without needing
   a wrapper element. */
.try-retry { margin-top: 14px; align-self: flex-start; }

/* ══ HOME: WHO IT IS FOR ═══════════════════════════════════════════════════
   Two columns, one rule between them and no boxes: the house rule is that
   surfaces separate by luminance and structure, and two bordered cards side by
   side on a gradient band read as an ad unit. Above 900px they are columns of
   one object; below, they stack and the rule becomes horizontal.

   ONE ACCENT, RATIONED. "For you" is the accent; "For your business" is plain
   --text on purpose. Colouring both would make the pair read as a comparison
   with a winner, and the whole point of the section is that they are two jobs
   for the same product rather than two tiers. */
.who-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(28px, 3.4vw, 44px);
  margin-top: clamp(30px, 3.4vw, 46px);
}
@media (min-width: 900px) {
  .who-grid { grid-template-columns: 1fr 1fr; gap: 0; }
  .who-card + .who-card { border-left: 1px solid var(--line); padding-left: clamp(30px, 3.4vw, 52px); }
  .who-card:first-child { padding-right: clamp(30px, 3.4vw, 52px); }
}
.who-card + .who-card { border-top: 1px solid var(--line); padding-top: clamp(28px, 3.4vw, 40px); }
@media (min-width: 900px) { .who-card + .who-card { border-top: 0; padding-top: 0; } }

.who-tag {
  margin: 0 0 14px;
  font: 700 9.5px/1 var(--mono);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--sig);
}
.who-tag-biz { color: var(--muted); }
.who-h { font-size: clamp(1.15rem, 1.7vw, 1.45rem); margin: 0 0 12px; max-width: 22ch; }
.who-lede { color: var(--muted); font-size: 15px; line-height: 1.62; margin: 0 0 20px; max-width: 46ch; }

/* Not a bulleted list of adjectives. Each line is a claim with a subject in
   bold and the qualification after it, because an unqualified claim on this
   page is the thing the product exists to refuse. */
.who-list { list-style: none; margin: 0 0 26px; padding: 0; display: grid; gap: 12px; }
.who-list li {
  position: relative;
  padding-left: 20px;
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--muted);
  max-width: 48ch;
}
.who-list li::before {
  content: "";
  position: absolute;
  left: 0; top: 9px;
  width: 6px; height: 6px;
  background: var(--sig);
}
.who-list b { color: var(--text); font-weight: 700; }

/* ══ HOME: THE WORKFLOW ════════════════════════════════════════════════════
   Three numbered stops, then the before/after that proves the middle one.

   ORDINALS AS A DESIGN ELEMENT, at ghost contrast: the numbers are large and
   nearly transparent, so they pace the section and mark position without
   competing with the headings for the one accent. */
.wf-steps {
  list-style: none;
  margin: clamp(30px, 3.4vw, 46px) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(24px, 3vw, 34px);
}
@media (min-width: 820px) { .wf-steps { grid-template-columns: repeat(3, 1fr); } }
.wf-step { position: relative; padding-top: 16px; border-top: 1px solid var(--line); }
.wf-num {
  display: block;
  margin-bottom: 10px;
  font: 700 clamp(28px, 3.4vw, 40px)/1 var(--mono);
  letter-spacing: -0.04em;
  color: color-mix(in srgb, var(--sig) 26%, transparent);
}
.wf-h { font-size: 1.05rem; margin: 0 0 8px; }
.wf-step p { margin: 0; color: var(--muted); font-size: 14.5px; line-height: 1.55; max-width: 40ch; }
/* The provenance line for the step itself. Same mono-label treatment the rest
   of the page uses for "this is an operational tag, not prose". */
.wf-where {
  margin-top: 12px !important;
  font: 700 9.5px/1.4 var(--mono) !important;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint) !important;
}

/* ── the before/after ─────────────────────────────────────────────────────
   Two panes and an arrow. The arrow is a character, not an asset, and it
   rotates to point down when the panes stack — an arrow that keeps pointing
   right at 390px is telling the reader the wrong story about the layout. */
.wf-tf {
  display: grid;
  grid-template-columns: 1fr;
  align-items: stretch;
  gap: 14px;
  margin-top: clamp(32px, 3.6vw, 48px);
}
@media (min-width: 900px) { .wf-tf { grid-template-columns: 1fr auto 1fr; gap: 18px; } }
.wf-pane {
  margin: 0;
  border: 1px solid var(--line);
  background: var(--slab);
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.wf-pane-h {
  padding: 12px 14px;
  border-bottom: 1px solid var(--line);
  font-size: 13.5px;
  color: var(--muted);
}
.wf-pane-tag {
  display: block;
  margin-bottom: 6px;
  font: 700 9.5px/1 var(--mono);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--faint);
}
/* The one label on this page that promises the thing beside it is live. It
   gets the accent, because it is the claim being made. */
.wf-pane-tag-live { color: var(--sig); }
.wf-raw {
  margin: 0;
  padding: 14px;
  font: 400 12.5px/1.7 var(--mono);
  color: var(--muted);
  white-space: pre-wrap;
  overflow-x: auto;
}
.wf-arrow {
  margin: 0;
  align-self: center;
  justify-self: center;
  font-size: 22px;
  line-height: 1;
  color: var(--sig);
  rotate: 90deg;
}
@media (min-width: 900px) { .wf-arrow { rotate: none; } }

.wf-table { padding: 14px; }
.wf-table-name {
  margin: 0 0 12px;
  font: 700 13px/1 var(--mono);
  letter-spacing: 0.04em;
  color: var(--sig-text);
}
.wf-cols { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
.wf-cols li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 7px;
  border-bottom: 1px solid var(--line);
  font-size: 13.5px;
}
.wf-cols li:last-child { border-bottom: 0; padding-bottom: 0; }
.wf-col-name { font-family: var(--mono); color: var(--text); }
.wf-col-type { font: 700 9.5px/1 var(--mono); letter-spacing: 0.16em; color: var(--faint); }
.wf-table-meta { margin: 12px 0 0; font-size: 13px; color: var(--muted); }
.wf-table-meta.is-error { color: var(--warn); }

.wf-lead-in {
  margin: clamp(26px, 3vw, 36px) 0 0;
  font-size: 15px;
  line-height: 1.62;
  color: var(--muted);
  max-width: 62ch;
}
.wf-lead-in b { color: var(--text); }

/* ══ HOME: TRY-IT STEPS 05 / 06 ════════════════════════════════════════════
   Instructions for controls that are already on screen, so they sit under the
   console rather than beside it and never look like a third pane. */
.try-steps {
  list-style: none;
  margin: clamp(22px, 2.6vw, 30px) 0 0;
  padding: 0;
  display: grid;
  gap: 14px;
  text-align: left;
}
@media (min-width: 820px) { .try-steps { grid-template-columns: 1fr 1fr; gap: 22px; } }
.try-steps li {
  padding-top: 14px;
  border-top: 1px solid var(--line);
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--muted);
}
.try-steps b { color: var(--text); }
.try-step-num {
  display: block;
  margin-bottom: 8px;
  font: 700 20px/1 var(--mono);
  letter-spacing: -0.03em;
  color: color-mix(in srgb, var(--sig) 26%, transparent);
}

/* ══ HOME: THE TRUST SUMMARY ═══════════════════════════════════════════════
   A record, not a marketing panel: mono labels, values that read as data, one
   1px rule between rows. It is deliberately the plainest surface on the page —
   the argument it makes is that these are facts read off a live bot, and a
   decorated card undercuts that. */
.trust-card {
  margin-top: clamp(28px, 3.2vw, 40px);
  border: 1px solid var(--line);
  background: var(--slab);
}
.trust-loading { margin: 0; padding: 20px; color: var(--muted); font-size: 14.5px; }
.trust-loading.is-error { color: var(--warn); }

/* The four policy answers. Four columns at desktop, two at tablet, one on a
   phone — a policy line that wraps to three lines is not more readable in a
   narrow column than it is stacked. */
.trust-policy {
  display: grid;
  grid-template-columns: 1fr;
  border-bottom: 1px solid var(--line);
}
@media (min-width: 620px) { .trust-policy { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1000px) { .trust-policy { grid-template-columns: repeat(4, 1fr); } }
.trust-cell { padding: 16px 18px; border-top: 1px solid var(--line); min-width: 0; }
.trust-cell:first-child { border-top: 0; }
@media (min-width: 620px) {
  .trust-cell { border-top: 0; border-left: 1px solid var(--line); }
  .trust-cell:nth-child(odd) { border-left: 0; }
  .trust-cell:nth-child(n + 3) { border-top: 1px solid var(--line); }
}
@media (min-width: 1000px) {
  .trust-cell { border-top: 0; border-left: 1px solid var(--line); }
  .trust-cell:nth-child(odd) { border-left: 1px solid var(--line); }
  .trust-cell:first-child { border-left: 0; }
  .trust-cell:nth-child(n + 3) { border-top: 0; }
}
.trust-k {
  display: block;
  margin-bottom: 8px;
  font: 700 9.5px/1.3 var(--mono);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--faint);
}
.trust-v { display: block; font-size: 15px; line-height: 1.4; color: var(--text); font-weight: 700; }
/* The state colours carry ONE meaning each, the same two this platform uses
   everywhere: accent = grounded/closed, --warn = a boundary being crossed. */
.trust-v.is-closed { color: var(--sig-text); }
.trust-v.is-open { color: var(--warn); }
.trust-note { display: block; margin-top: 6px; font-size: 13px; line-height: 1.5; color: var(--muted); }

.trust-inv { padding: 18px; }
.trust-inv + .trust-inv { border-top: 1px solid var(--line); }
.trust-inv-h {
  margin: 0 0 14px;
  font: 700 9.5px/1 var(--mono);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--sig);
}
.trust-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
.trust-item { border-top: 1px solid var(--line); }
.trust-item:first-child { border-top: 0; }

/* Each source is a disclosure, not a link: there is no page to open, and the
   record IS the thing being opened. A <button> with aria-expanded, so the
   keyboard and a screen reader get the same affordance a pointer does. */
.trust-src {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  width: 100%;
  min-height: 44px;
  padding: 11px 2px;
  border: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 14.5px;
  text-align: left;
  cursor: pointer;
}
.trust-src:hover .trust-src-t { color: var(--sig-text); }
.trust-src:focus-visible { outline: 2px solid var(--sig); outline-offset: -2px; }
.trust-src-t { min-width: 0; }
.trust-src-kind {
  flex: none;
  font: 700 9.5px/1 var(--mono);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint);
}
.trust-src-kind.is-stale { color: var(--warn); }
.trust-src-body { padding: 0 2px 14px; }
.trust-src-body[hidden] { display: none !important; }
.trust-meta { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.trust-meta li { font-size: 13px; line-height: 1.5; color: var(--muted); }
.trust-meta b { color: var(--text); font-weight: 700; }
.trust-cols { margin: 8px 0 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: 6px; }
.trust-cols li {
  padding: 3px 8px;
  border: 1px solid var(--line);
  font: 400 11.5px/1.5 var(--mono);
  color: var(--muted);
}
.trust-more { margin: 12px 0 0; font-size: 13px; color: var(--muted); }

/* The row a citation's "Open source" button jumped to. A flash, not a
   permanent highlight: it answers "which one" and then gets out of the way. */
.trust-item.is-target { background: color-mix(in srgb, var(--sig) 10%, transparent); }
@media (prefers-reduced-motion: no-preference) {
  .trust-item.is-target { transition: background-color 900ms var(--ease); }
}

.trust-support { margin: clamp(20px, 2.4vw, 28px) 0 0; font-size: 14.5px; color: var(--muted); }
.trust-support a { color: var(--sig-text); }

/* ══ HOME: STATEMENT + FINALE ══════════════════════════════════════════════ */
.statement { max-width: 24ch; margin: 0 auto; }
.statement p {
  margin: 0;
  font-size: clamp(21px, 3.1vw, 38px);
  font-weight: 300;
  line-height: 1.24;
  letter-spacing: -0.012em;
  max-width: 20ch;
  text-wrap: pretty;
}
.statement b { font-weight: 700; color: var(--sig-text); }
.finale { text-align: center; }
.finale .statement { max-width: none; }
.finale .statement p { max-width: 26ch; margin-inline: auto; }
.finale-cta { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; margin-top: clamp(34px, 4vw, 52px); }

/* ══ HOME: FOUR-COLUMN FOOT ════════════════════════════════════════════════
   /home/ owns its footer markup outright (chrome.js only fills `footer.foot`,
   and this one is not that), so the cookie-choices button is wired by home.js. */
.site-foot .bb-linkbtn { font-size: 14px; }
.site-foot .foot-brand {
  font: 700 13px/1 var(--sans);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text);
}

/* ══ DOCUMENT PAGES (/help/, /privacy/, /terms/, /accessibility/) ═══════════
   Read mode. Comprehension outranks expression here: less motion, no reveal,
   one eyebrow, a measured column and a visible structure. */
/* `padding-block`, deliberately not the `padding` shorthand: a 3-value shorthand
   (`V1 0 V2`) sets left AND right to that middle 0, which was silently zeroing
   the horizontal inset `.wrap` (the other class on this same element) supplies
   - the exact "eyebrow/headline flush to the viewport" gutter defect the
   2026-08-04 design review found on /help/, /privacy/, /terms/ and
   /accessibility/. `padding-block` only ever sets top/bottom, so the fix
   cannot regress into the same bug by omission. */
.doc-head { padding-block: clamp(52px, 7vw, 92px) clamp(18px, 3vw, 30px); }
.doc-head .lede { max-width: 60ch; }
.doc-head h1 { text-transform: uppercase; letter-spacing: -0.02em; font-weight: 800; }
.updated {
  margin: 18px 0 0;
  font: 500 10.5px/1 var(--mono);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ── the document page's second column ────────────────────────────────────────────────────────
   `.doc` is a 74ch measure, and at 1440 that left 45.7% of the window empty to its right on
   /about/, /contact/ and /changelog/ — measured, per page and per theme, by
   scripts/measure-fill.mjs. The measure is NOT widened: 74ch is a reading decision. The space is
   given a job instead, with the pattern /help/ already ships (.help-layout / .help-index below in
   help.css) generalised by public/js/doc-index.js so there is one answer to this and not two.

   1080px, and the rest of these numbers, are help.css's — the width at which 74ch of prose plus a
   260px index plus their gap first fit inside `.wrap` without either being squeezed. Below it the
   index is `display: none`, not stacked: a second copy of every heading directly above those
   headings is noise on a phone. It is a second column or it is nothing. */
.doc-layout { display: block; }
.doc-index { display: none; }

@media (min-width: 1080px) {
  .doc-index { display: block; }
  .doc-layout {
    display: grid;
    /* Pushed to the right margin rather than stretched: the index is a 260px object, and letting it
       absorb 300px of slack turns a tight list into a loose one. */
    grid-template-columns: minmax(0, 74ch) 260px;
    justify-content: space-between;
    align-items: start;
    gap: clamp(40px, 4vw, 72px);
  }
  .doc-layout > .doc { max-width: none; }
  /* /about/, /privacy/ and /terms/ already carry an in-page `ul.toc` chip row at the top of the
     document. Above the breakpoint the rail IS that index, in a better place, so the inline copy is
     hidden — two navigations over the same six headings, one directly above the other, is the
     redundancy this whole change exists to avoid. `display: none` also takes it out of the
     accessibility tree, so exactly one "On this page" is ever exposed at any width. Below 1080px
     the rail is the one that is gone and the chip row is the one that remains. */
  .doc-layout [data-doc-toc] { display: none; }
}

.doc-index {
  position: sticky;
  top: 88px;                          /* clears the 68px sticky header, plus a breath */
  max-height: calc(100vh - 120px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-left: 18px;
  border-left: 1px solid var(--line);
}
.doc-index-h {
  margin: 0 0 14px;
  font: 600 11px/1 var(--mono);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--sig-text);
}
.doc-index ul { list-style: none; margin: 0; padding: 0; }
.doc-index li { margin: 0; }
.doc-index a {
  display: block;
  padding: 6px 0 6px 10px;
  margin-left: -11px;
  border-left: 1px solid transparent;
  font-size: 13px;
  line-height: 1.45;
  color: var(--muted);
  text-decoration: none;
  transition: color 160ms ease, border-color 160ms ease;
}
.doc-index a:hover { color: var(--text); border-left-color: var(--sig); }
.doc-index a:focus-visible { outline: 2px solid var(--sig); outline-offset: 2px; }
/* The section you are in. `aria-current` is the state, so it is also the selector — there is no
   second class that could drift out of step with what a screen reader is told. */
.doc-index a[aria-current] { color: var(--text); border-left-color: var(--sig); }
@media (prefers-reduced-motion: reduce) {
  .doc-index a { transition: none; }
}

.doc { max-width: 74ch; padding-bottom: clamp(64px, 9vw, 110px); }
.doc section { padding: 30px 0 8px; border-top: 1px solid var(--line); scroll-margin-top: 88px; }
.doc section:first-of-type { border-top: 0; padding-top: 6px; }
.doc h2 {
  margin: 0 0 12px;
  font-size: clamp(17px, 1.9vw, 21px);
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.doc h3 {
  margin: 22px 0 7px;
  font: 700 12px/1.35 var(--mono);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text);
}
.doc p, .doc li { color: var(--muted); font-size: 15.5px; line-height: 1.72; }
.doc p { margin: 0 0 12px; }
.doc ul { padding-left: 20px; margin: 10px 0 16px; }
.doc li { margin: 7px 0; }
.doc li::marker { color: var(--sig); }
.doc b, .doc strong { color: var(--text); font-weight: 700; }
.doc em { font-style: italic; color: var(--text); }
.doc code {
  font-family: var(--mono); font-size: 0.87em;
  background: var(--slab);
  border: 1px solid var(--line);
  padding: 1px 6px;
  color: var(--text);
  /* A <code> span is one unbreakable token to the line-breaking algorithm, so a long one does not
     wrap — it sticks out of the column and takes the DOCUMENT with it. Measured: the privacy
     notice's `static.cloudflareinsights.com/beacon.min.js` rendered 354px wide and pushed
     documentElement.scrollWidth to 381px at both 320px and 360px viewports, i.e. page-level
     horizontal scrolling on every phone. `anywhere` rather than `break-word` because these are
     hostnames and paths with no spaces at all: `break-word` only breaks a word that could not fit
     on a line OF ITS OWN, which is a weaker guarantee than this needs.
     `max-width: 100%` is the second half — without it the inline box can still report a width
     wider than its container even once the text inside has wrapped. */
  overflow-wrap: anywhere;
  word-break: break-word;
  max-width: 100%;
}
.doc a { color: var(--sig-text); text-underline-offset: 4px; text-decoration-thickness: 1px; }
.doc a:hover { color: var(--text); }

/* Table of contents: square chips, not pills. */
.toc { display: flex; flex-wrap: wrap; gap: 7px; list-style: none; margin: 0 0 30px; padding: 0; }
.toc a {
  display: inline-block;
  padding: 9px 13px;
  border: 1px solid var(--line-hi);
  background: var(--slab);
  font: 500 11px/1 var(--mono);
  letter-spacing: 0.1em;
  color: var(--muted);
  text-decoration: none;
  transition: border-color 160ms ease, color 160ms ease, background-color 160ms ease;
}
.toc a:hover { border-color: var(--sig); color: var(--sig-text); background: var(--slab-hi); }

/* ══ EMPTY STATE ═══════════════════════════════════════════════════════════
   Empty states teach the interface. They never just say "nothing here". */
.empty {
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  padding: clamp(48px, 8vw, 84px) 20px;
  text-align: center;
}
.empty h2 { margin: 0; font-size: 17px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text); }
.empty p { margin: 0; max-width: 46ch; color: var(--muted); font-size: 14.5px; }
.empty .btn { margin-top: 6px; }
/* A built hexagon, so an empty region still carries the identity. */
.empty-mark {
  width: 52px; height: 60px;
  clip-path: var(--hex-clip);
  background: linear-gradient(158deg, var(--deep-3), var(--deep-1));
}

/* ══ RESPONSIVE ════════════════════════════════════════════════════════════ */
@media (max-width: 900px) {
  .nav-in { height: 62px; }
  .try-form { padding: 12px 14px; }
}
@media (max-width: 640px) {
  .btn { padding: 0 18px; letter-spacing: 0.16em; }
  .try-row { flex-wrap: wrap; }
  .try-input { flex-basis: 100%; }
  .try-row .btn { width: 100%; }

  /* THE ONE MOBILE GUTTER (--gut-m, declared up top): pins the hero and the shared
     empty-state card to the same flat inset the .wrap/.doc-head document family already
     carries, instead of each independently reading backbone's vw-scaled --gut. */
  .wrap, .doc-head, .hero .hero-in, .empty { padding-inline: var(--gut-m); }
}
@media (max-width: 560px) {
  /* `.nav-links` does not wrap on its own, so anything left in it that does not fit pushes the
     DOCUMENT sideways rather than dropping to a second row — at 390px the bar was overflowing by
     23px and taking the whole page's horizontal scroll with it. Browse bots, Pricing and Help are
     hidden here (Admin sign in has already left the row at every width, via the `.nav-hide` rule up
     in the nav section); none of the four are lost, because they reappear inside the compact
     `.nav-menu` disclosure chrome.js renders — see the 2026-08-04 review, P1, and the `.nav-menu*`
     rules above. As of the 2026-08-04 P0 fix, `.nav-menu` is a SIBLING of `.nav-links`, not a
     descendant, so this selector only ever reaches the top row's own copies — it can no longer
     accidentally hide the panel's. (It briefly did: the panel used to be interpolated inside
     `.nav-links`, so its `<a href="/browse/">` etc. matched this same rule and opened with three of
     its four items already `display:none`.)
     `.nav-in`/`.nav-links` are ALSO allowed to wrap below, as a second line of defence: whatever is
     still left after this thinning can drop to its own row instead of overflowing, so nothing here
     depends on getting an exact pixel budget right (that budget is exactly what broke last time). */
  .nav-links a[href="/browse/"],
  .nav-links a[href="/pricing/"],
  .nav-links a[href="/help/"] { display: none; }
  .nav-menu { display: inline-flex; }

  /* ── ONE ROW, 64px (2026-08-06) ───────────────────────────────────────────
     Measured before this block, at 390x844x2 with touch: the header was 151px
     tall over three lines — wordmark, then Sign in + Create a bot + theme
     toggle, then Menu — so the h1 began at y=283, a third of the way down the
     first screen. The row's budget at 390px is 390 - 36 gutters = 354px, and
     it was being asked to carry 155 + 86 + 123 + 44 + gaps = 428px.
     Two secondary controls leave the row for the panel (chrome.js's
     navMenuMarkup) and the trigger drops its word, which brings the ask to
     155 + 123 + 44 + 20 = 342px. That fits with 12px to spare, and the panel
     is where both controls now live, so nothing became unreachable.
     `.bb-idmenu` is hidden alongside `#nav-signin` — chrome.js replaces one with
     the other when a session exists, and the replacement arrives AFTER the
     session probe resolves. A 36px pill appearing in the row a few hundred ms
     into the load would push it past its budget and wrap it, shifting the
     document 54px: the 2026-08-05 header-oscillation defect again. Hidden in
     both states, the row's width no longer depends on the session at all, and
     chrome.js mirrors My bots / Settings / Sign out into the panel so nothing
     becomes unreachable. (`.bb-idmenu` is the shared profile control's wrapper,
     js/ui/account-menu.js; it was `.gcb-acct` when chrome.js built its own.) */
  .nav-links #nav-signin,
  .nav-links .bb-idmenu,
  .nav-links .bb-theme-switch { display: none; }
  /* Hamburger only. "MENU" at 11px/0.2em plus its 8px gap is 43px of a 354px
     row — the exact amount that decides whether this fits on one line. The
     button keeps its 44x44 target and its `aria-label`, so nothing is lost to
     a screen reader or to a thumb; only the redundant word goes. */
  .nav-menu-btn { padding: 0; width: 44px; justify-content: center; gap: 0; }
  .nav-menu-lbl { display: none; }
  /* The panel anchors to the NAV ROW, not to the trigger. `.nav-menu` goes `position: static` here
     so the panel's containing block becomes the `.wrap.nav-in` row (`.wrap` is `position: relative`,
     home.css:57), and `right` is measured from that row's right edge — which is always on screen.
     `top: calc(100% + 8px)` is then 100% of the ROW, so the panel clears however many lines the bar
     has wrapped to rather than cutting through them.

     Anchoring it to the trigger is what broke at exactly 390px: that is the width where `.nav-menu`
     stops fitting beside anything else and wraps onto a line of its own, where it has no
     `margin-left: auto` and so sits at flex-start (x=18). A `right: 0` panel then grew its 216px
     LEFTWARD from there and rendered at x=-111, almost entirely off-canvas. It looked fine at 430px
     and 560px, where the trigger still shares a line and lands on the right. Giving `.nav-menu` its
     own `margin-left: auto` would fight `.nav-links`'s — two auto margins on one line split the free
     space and pull the trigger away from the links — so the containing block moves instead. */
  .nav-menu { position: static; }
  /* THE FULL-WIDTH PIN IS GONE (2026-08-06), AND THE REASON IT EXISTED IS GONE WITH IT.
     It read `.nav-menu { flex: 0 0 100% }` and forced the trigger onto its own row at every width in
     this range. That was a stability fix, not a layout preference: with Sign in and the theme toggle
     still in the row, 390px sat within a few pixels of the line box, so any late width change
     flipped the trigger between row 2 and row 1 and back — measured on /home/ and /browse/ under the
     perf harness as two shifts ~20ms apart at ~1.25s (nav-menu y 87->33 then 33->87, main#main y
     142->88 then 88->142), 54px of document movement each, 0.6874 CLS against a 0.01 budget.
     Pinning the wrap made the height independent of the free space, at the cost of a permanent
     second row.
     Both late width changes have now been removed at the source: the theme ghost->button swap and
     the Sign-in->account-pill swap are both `display: none` in this range (see the ONE ROW block
     above), so the row holds three fixed-width items whose total is decided at first paint and never
     moves. 342px of a 354px budget is not a threshold anyone can oscillate across, and `flex-wrap:
     wrap` below still catches any width narrow enough to genuinely need a second line. */
  /* FULL-WIDTH SHEET, not a right-hung dropdown. Pinning only `right` fixed the off-canvas bug
     described above but left a second one the review caught: below 430px the trigger wraps to its
     own line at the LEFT gutter (x=18) while a 216px panel hung from the right edge opened in the
     opposite corner, so the menu appeared nowhere near the button that opened it. Spanning both
     gutters resolves both failures at once and needs no measurement: the panel's left edge now
     lands on the same gutter the trigger sits on at the widths where it wraps, and at 430-560px,
     where the trigger shares the row and sits right, the sheet still reaches it. It also cannot go
     off-canvas in either direction, which is what the `right`-only version was there to guarantee. */
  .nav-menu-panel {
    left: var(--gut-m);
    right: var(--gut-m);
    width: auto;
    min-width: 0;
    max-width: none;
  }
  /* 44px control + 8px above and below = 60px, under the 64px the review asks for and a genuine
     third of what this header was. `min-height` has to be restated, not just `height: auto`: the
     base rule's 68px floor was winning over the content box and holding the bar 8px taller than
     anything in it needed. */
  .nav-in { flex-wrap: wrap; height: auto; min-height: 0; padding-block: 8px; row-gap: 8px; }
  /* The reserved height follows the row down: 44px control + 8px above and below. */
  .nav { min-height: 60px; }
  .nav-links { flex-wrap: wrap; justify-content: flex-end; row-gap: 8px; }
  .hero-note { font-size: 11px; }
}

/* At 320px the row has to hold the wordmark, Sign in, Create a bot and the Menu trigger together.
   The wrapping above is what actually guarantees no horizontal overflow at this width — flex-wrap
   lets the row fall to as many lines as it needs instead of pushing the document sideways, which is
   the failure mode that got "Create a bot" dropped from here in the first place. It now STAYS at
   every width: the 2026-08-04 review (P1) calls out one context-appropriate primary action that has
   to survive outside the compact menu, and this is it. This block only tightens the gap between the
   smaller set of items still sharing a line. */
@media (max-width: 430px) {
  .nav-in { gap: 10px; }
}

/* ══ REDUCED MOTION ════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .btn::before { display: none; }
  .skip, .btn, .toc a, .nav-links a:not(.btn), .foot a { transition: none; }
}

/* The theme switch matches the nav buttons beside it (.btn-sm renders 34px tall). */
.nav-links .bb-theme-switch { --bb-ts-size: 34px; }

/* ── The shared header, on a finger ─────────────────────────────────────────
   This file styles chrome.js's header for EVERY marketing page, so a 34px
   control here is 34px on /home/, /pricing/, /browse/, /help/, /login/ and
   /join/ at once — which is why the audit's worst touch-target pages were all
   of them, and all for the same four controls. The desktop row keeps its 34px
   density deliberately; only a coarse pointer gets the bigger boxes.
   The text links need PADDING rather than min-height: they are baseline-set
   against the row, and a min-height would leave each label riding the top edge
   of its own target. 13 + 13 + the 11px line box = 43.5, which rounds up. */
@media (pointer: coarse) {
  /* The demo bot's example questions are the single most-tapped thing on this page — they are the
     invitation to try the product — and backbone gives .bb-chip a fixed 36px height. */
  .bb-chip { height: auto; min-height: 44px; }
  .nav-links a:not(.btn) { padding-block: 13px; }
  .nav-links .btn-sm { min-height: 44px; padding-inline: 16px; }
  .nav-links .bb-theme-switch { --bb-ts-size: 44px; }
  .nav-menu-panel a { padding-block: 15px; }
  /* The skip link is the FIRST thing a switch-control or screen-reader user
     reaches on every one of these pages. It was 34px. */
  .skip { min-height: 44px; display: inline-flex; align-items: center; }
}

/* ══ GPU COMPOSITING (/home/ only) ═════════════════════════════════════════
   home.js puts `.gpu-on` on <html> as the page loads, so the surfaces that
   animate are already on their own compositing layers by the time the visitor
   can scroll — not promoted halfway through the first transition. The class is
   NOT set under prefers-reduced-motion: nothing moves there, so a layer would
   be memory spent on nothing. Only /home/ runs home.js, so the other six pages
   that load this file are untouched.

   Two standing promotions, both earning the layer they hold:

     .nav is sticky AND carries backdrop-filter: blur(12px) saturate(140%), so
     every scroll frame re-samples the 68px band of page behind it. translateZ(0)
     pins it to its own surface instead of letting the compositor fold it back
     into the scrolling contents.

     #demo rewrites its text every ~14-34ms for the whole session (the hero
     console's typing loop). `contain: layout paint` keeps that churn from
     invalidating the hero band around it and holds the repaint inside the
     console's own bounds.

   The 29 [data-reveal] layers are deliberately NOT here. They are transient, so
   home.js sets and clears `will-change` around each node's own transition; a
   standing rule would hold 29 permanent layers to save one 620ms move each. The
   hero circuit is absent for a measured reason - see the profile note above
   `.bb-trace-live` in css/backbone.css. */
.gpu-on .nav { transform: translateZ(0); }
.gpu-on #demo { contain: layout paint; }
