/* banter — design tokens */
:root {
  /* Palette A — Tangerine (default) */
  --ink: #14110F;
  --ink-soft: #2B2622;
  --ink-mute: #6B625B;
  --paper: #F5EFE6;
  --paper-warm: #EFE6D8;
  --paper-cool: #FAF6EE;
  --line: #1A1714;
  --rule: rgba(20, 17, 15, 0.14);
  /* Diagram connector hairlines — ink at 35% in light mode, swapped to
     bone at 35% in the [data-paper="ink"] block below so the hero
     diagram's lines remain visible on both palettes. */
  --connector: rgba(20, 17, 15, 0.35);

  --primary: #FF5B1F;     /* tangerine */
  --primary-deep: #C7390A;
  /* Brand-only token — the wordmark's period reads as the canonical
     brand mark and should not flow with the UI primary swap. Sticks
     to tangerine across light + dark mode regardless of what
     `--primary` does. UI elements (CTAs, focus rings, accents) keep
     using `--primary` and pick up the dark-mode swap below. */
  --brand-primary: #FF5B1F;
  --secondary: #1F4FBF;   /* cobalt */
  --tertiary: #E9C46A;    /* ochre */
  --plum: #6B2D5C;
  --moss: #2F5D3A;

  /* Persona accents */
  --p1: #FF5B1F;
  --p2: #1F4FBF;
  --p3: #2F5D3A;
  --p4: #6B2D5C;
  --p5: #E9C46A;
  --p6: #14110F;

  /* Complementary accents — softer tones paired with each persona */
  --a1: #F08A6A; /* Coral  → Tangerine */
  --a2: #7FA9D9; /* Sky    → Cobalt */
  --a3: #9BB28A; /* Sage   → Moss */
  --a4: #B07FA0; /* Mauve  → Plum */
  --a5: #F2D9A4; /* Cream  → Ochre */
  --a6: #5C6670; /* Slate  → Ink */

  /* Type */
  --serif: "garamond-atf-text", "Cormorant Garamond", Georgia, serif;
  --serif-display: "linotype-sabon", "Sabon Next", "Cormorant Garamond", Georgia, serif;
  /* Sabon at 600 (semibold) is the canonical display weight — chosen so
     H1 / .serif-display surfaces sit in the same visual register as the
     Wordmark, which is fixed at 600 in brand/logo.jsx. The earlier 700
     cut read as too forceful next to the lighter italic body. Override
     per-display-serif below. */
  --serif-display-weight: 600;

  /* Sans — ITC Franklin Gothic (Vic Caruso 1979 reissue of Morris Fuller
     Benton's 1902 ATF original). The "newsstand-editorial" workhorse
     sans. Three cuts published in our Typekit kit (yet1bgj):
       franklin-gothic            — 400 / 700 (Book / Bold), each ± italic
       franklin-gothic-condensed  — 400 / 600 (Cond / Demi Cond), ± italic
       franklin-gothic-compressed — 400 / 600 (Comp / Demi Comp), ± italic
     Trap to mind: regular Franklin has 400 and 700 — there is NO 500 or 600
     in the regular cut. Anywhere a "demi" weight is needed (CTAs, h-3
     labels, persona-name tiles, form-row labels), reach for
     franklin-gothic-condensed at 600 — same visual mass without the
     browser synthesizing a faux weight off 700. Use franklin-gothic-
     compressed for headline-strip moments and stacked numerals.

     System fallback chain favors the platform sans during the brief
     network window before Adobe Fonts resolves; "Inter Tight" stays in
     the chain as a name-only fallback (we no longer pull it from
     Google Fonts) since some users will already have it locally. */
  --sans:            "franklin-gothic", system-ui, -apple-system, "Helvetica Neue", "Inter Tight", sans-serif;
  --sans-condensed:  "franklin-gothic-condensed", "franklin-gothic", system-ui, -apple-system, "Helvetica Neue", sans-serif;
  --sans-compressed: "franklin-gothic-compressed", "franklin-gothic-condensed", "franklin-gothic", system-ui, -apple-system, "Helvetica Neue", sans-serif;

  --mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* Geometry */
  --radius: 14px;
  --radius-lg: 22px;
  --radius-pill: 999px;

  /* Spacing scale */
  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 64px;
  --s-9: 96px;
  --s-10: 128px;
}

/* Palette B — Plum */
[data-palette="plum"] {
  --primary: #7A2A6B;
  --primary-deep: #4D1A45;
  --secondary: #E9C46A;
  --tertiary: #2F5D3A;
  --paper: #F3ECE2;
  --p1: #7A2A6B; --p2: #E9C46A; --p3: #2F5D3A; --p4: #C73E5A; --p5: #1F4FBF; --p6: #14110F;
  --a1: #B07FA0; --a2: #F2D9A4; --a3: #9BB28A; --a4: #E89BA8; --a5: #7FA9D9; --a6: #5C6670;
}
/* Palette C — Cobalt */
[data-palette="cobalt"] {
  --primary: #1F4FBF;
  --primary-deep: #0E2E78;
  --secondary: #FF5B1F;
  --tertiary: #E9C46A;
  --paper: #F2EEE6;
  --p1: #1F4FBF; --p2: #FF5B1F; --p3: #E9C46A; --p4: #6B2D5C; --p5: #2F5D3A; --p6: #14110F;
  --a1: #7FA9D9; --a2: #F08A6A; --a3: #F2D9A4; --a4: #B07FA0; --a5: #9BB28A; --a6: #5C6670;
}

[data-serif="garamond"] {
  --serif: "garamond-atf-text", "Cormorant Garamond", Georgia, serif;
  --serif-display: "linotype-sabon", "Sabon Next", "Cormorant Garamond", Georgia, serif;
  --serif-display-weight: 600;
}
[data-serif="fraunces"]  {
  --serif: "Fraunces", Georgia, serif;
  --serif-display: "Fraunces", Georgia, serif;
  --serif-display-weight: 600;
}
[data-serif="sabon"]     {
  --serif: "linotype-sabon", "Sabon Next", Georgia, serif;
  --serif-display: "linotype-sabon", "Sabon Next", Georgia, serif;
  --serif-display-weight: 600;
}

.serif-display { font-family: var(--serif-display); font-weight: var(--serif-display-weight, 400); letter-spacing: -0.02em; }

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Inline `<code>` — semantic monospace span used for technical
   tokens in body copy (mode names like `Strict`, tab labels,
   SwiftUI / Banter API references). Day mode: ink fill on cream
   type. Dark mode reverses automatically because `--ink` and
   `--paper` swap under `[data-paper="ink"]`. */
code {
  display: inline-block;
  background: var(--ink);
  color: var(--paper);
  padding: 1px 6px;
  border-radius: 4px;
  font-family: var(--mono);
  font-size: 0.82em;
  font-weight: 500;
  line-height: 1.4;
}

/* type utilities */
.serif { font-family: var(--serif); font-weight: 400; letter-spacing: -0.01em; }
.mono  { font-family: var(--mono); }
/* Editorial small-caps section label. Franklin Gothic Condensed at
   600 with wide tracking gives the "Bloomberg / Vanity Fair editorial"
   feel that the previous mono treatment was reaching for, but in the
   same family as the body so the type system stays cohesive. Tracking
   is slightly tighter than the old mono (0.18 → 0.14em) because
   Franklin Condensed has narrower glyphs and looks gappy at 0.18. */
.eyebrow {
  font-family: var(--sans-condensed);
  font-weight: 600;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
/* Subsection subhead. Sabon (serif-display) italic, smaller than the
   H1 hero — used for subsections inside a section (e.g.,
   "Three ways to cast" inside PERSONAS). Distinct from .eyebrow
   (mono caps), which is reserved for section-level kickers, so the
   hierarchy reads: kicker → H1 → subhead → body. */
.subhead {
  font-family: var(--serif-display);
  font-weight: var(--serif-display-weight, 400);
  font-style: italic;
  /* Tightened from clamp(20, 2.6vw, 26) — the previous range crowded
     the 20px body in narrow-column subsections (HRooms mode list,
     HTokens "Levers of Power" trio) where subhead + body sat next to
     each other in a 2-col grid. 18–24 keeps the editorial hierarchy
     legible without making the subhead read like a third-tier section
     title. */
  font-size: clamp(18px, 2.2vw, 24px);
  line-height: 1.15;
  letter-spacing: -0.015em;
  color: var(--ink);
}
/* Secondary-section header — the missing intermediate tier between
   the section H1 (.h-1, clamp 40–72) and the caption-scale .subhead
   (clamp 18–24). Heads a *secondary section inside a main section*
   (e.g. "Two heads are different than one." under the Models H1),
   where .subhead read as too small to register as a real divider.
   Same Sabon (serif-display) family as the hero, non-italic so it
   lands as a "mini hero" rather than an editorial caption; .subhead
   stays italic + small for in-column caption labels. Hierarchy:
   kicker → H1 → subsection-head → body, with subhead reserved for
   the tight two-column caption uses. NB: named `subsection-head`
   not `section-head` — the latter is already a grid-layout utility
   (the kicker | content two-column block, defined below). */
.subsection-head {
  font-family: var(--serif-display);
  font-weight: var(--serif-display-weight, 400);
  font-size: clamp(26px, 3vw, 38px);
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--ink);
}
.lede { font-family: var(--serif); font-size: clamp(22px, 2.4vw, 30px); line-height: 1.3; letter-spacing: -0.01em; color: var(--ink-soft); }
.h-display {
  font-family: var(--serif-display);
  font-weight: var(--serif-display-weight, 400);
  font-size: clamp(56px, 9vw, 156px);
  line-height: 0.92;
  letter-spacing: -0.035em;
}
.h-1 { font-family: var(--serif-display); font-weight: var(--serif-display-weight, 400); font-size: clamp(40px, 5vw, 72px); line-height: 1; letter-spacing: -0.025em; }
.h-2 { font-family: var(--serif); font-weight: 400; font-size: clamp(28px, 3vw, 44px); line-height: 1.05; letter-spacing: -0.015em; }
.h-3 { font-family: var(--sans-condensed); font-weight: 600; font-size: 18px; letter-spacing: 0.005em; }
/* Demi-weight inline label utility. Franklin Gothic's regular cut
   skips 500 / 600, so anywhere a UI label needs a "between body and
   bold" presence, reach for the condensed cut at 600. Pair with
   slight positive tracking (~0.005em) for ≤14px sizes. */
.demi { font-family: var(--sans-condensed); font-weight: 600; }
.body-l { font-size: 18px; line-height: 1.55; color: var(--ink-soft); }
.body { font-size: 15px; line-height: 1.55; color: var(--ink-soft); }
.small { font-size: 13px; line-height: 1.5; color: var(--ink-mute); }

.rule { height: 1px; background: var(--rule); width: 100%; }
.rule-strong { height: 1px; background: var(--ink); width: 100%; }

/* Caption block — sits below a chrome card / window (AnimFrame,
   BanterWindow, brand-guide tile, …). The horizontal inset matches
   the chrome's border-radius so the caption text lines up with the
   interior corner. Callers override `--caption-inset` per use; the
   fallback is the brand's `--radius` token. */
.caption {
  padding-left: var(--caption-inset, var(--radius));
  padding-right: var(--caption-inset, var(--radius));
}

.shell {
  max-width: 1320px;
  margin: 0 auto;
  padding: 0 var(--s-8);
}
@media (max-width: 720px) {
  .shell { padding: 0 var(--s-5); }
}

/* Responsive grid + spacing tokens.
 *
 * Inline-style grids in home.jsx reach for these so a single breakpoint
 * collapses every multi-column section to a single column on phones.
 * Phase B will introduce a tablet step (cf. --cols-3 → 2-up before
 * collapsing fully), but phase A is just one media query.
 *
 * --cols-2 / --cols-3 / --cols-3-min   inline grid templates
 * --section-pad / --hero-pad-y         vertical rhythm at the section level
 * --window-w-460                       width clamp for fixed-width app windows
 *                                      (TokenLedger, persona-editor scenes)
 */
:root {
  --cols-2: repeat(2, 1fr);
  --cols-3: repeat(3, 1fr);
  --cols-3-min: repeat(3, minmax(0, 1fr));
  --section-pad: 120px;
  --hero-pad-y: 80px 0 120px;
  --window-w-460: 460px;
  --carousel-min-h: 320px;
}
/* Tablet: 3-up grids step down to 2-up so cards don't squeeze under
   ~250px each. 2-up grids stay 2-up. The PersonaCarousel keeps its
   3-column internal layout because each column gets ~220px which is
   still readable; collapsing it here would orphan the identity card. */
@media (max-width: 1024px) {
  :root {
    /* `--cols-2` collapses to single-column at the same break as the
       3-up grids. Anything between 720 and 1024 was rendering as a
       cramped 2-col (body squeezed alongside a fixed-width anim);
       1-col stacked reads cleanly without touching the ≤720 (mobile)
       or ≥1024 (full-desktop) ranges. */
    --cols-2: 1fr;
    --cols-3: repeat(2, 1fr);
    --cols-3-min: repeat(2, minmax(0, 1fr));
    --section-pad: 96px;
  }
}
/* Phone: every multi-column section collapses to a single column.
   Animations get the full row width (which on a 360px phone is the
   only way they're legible at all). */
@media (max-width: 720px) {
  :root {
    --cols-2: 1fr;
    --cols-3: 1fr;
    --cols-3-min: 1fr;
    --section-pad: 64px;
    --hero-pad-y: 48px 0 72px;
    --window-w-460: 100%;
    /* PersonaCarousel hides voice + engine columns on phone (see
       .persona-col-voice / .persona-col-engine in app.css); only the
       identity card shows, so the carousel doesn't need extra height. */
    --carousel-min-h: 360px;
  }
  /* Safety net — any animation with content wider than its parent
     (fixed-width SVG, absolute children, an overlooked min-width)
     can't push the page wider than the viewport. The scroll container
     of choice is the animation's own frame, never the document.
     `overflow-x: clip` (rather than `hidden`) does NOT establish a
     scroll container — important so the sticky TopBar still anchors
     to the viewport on iOS Safari, where `overflow-x: hidden` on
     html/body is treated as a scrollport and breaks `position: sticky`. */
  html, body { overflow-x: clip; max-width: 100vw; }

  /* Force text to wrap inside marketing copy / chip-styled <code>
     spans. `code` is `display: inline-block` for the chip pill look,
     which stops it from breaking mid-token; `overflow-wrap: anywhere`
     on the parent paragraph re-allows the break and prevents lines
     from extending past viewport. */
  body p, body li { overflow-wrap: anywhere; word-break: break-word; }
}
@media (max-width: 720px) {
  /* Ledger breakdown row (anthropic · claude-sonnet-4-6 · 1× · 1.4k ·
     <$0.01) — let it wrap on phones since the model id alone eats
     most of a 360px row, and a single-line layout would clip the
     numbers on the right. */
  .banter-ledger-breakdown-row { flex-wrap: wrap; }
  .banter-ledger-metrics       { row-gap: 6px; }
}

.section {
  padding: var(--s-10) 0;
  border-top: 1px solid var(--rule);
}
.section:first-of-type { border-top: 0; }

.section-head {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: var(--s-7);
  align-items: baseline;
  margin-bottom: var(--s-7);
}
@media (max-width: 720px) {
  .section-head { grid-template-columns: 1fr; gap: var(--s-3); }
}

.tag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border: 1px solid var(--ink);
  border-radius: var(--radius-pill);
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--primary); }

/* Paper modes — light is the `:root` default (no `data-paper`
   attribute set); dark is `[data-paper="ink"]`. Earlier drafts also
   defined `ivory` / `bone` / `porcelain` / `snow` light-mode
   alternatives, but none of them were ever invoked in production —
   the website (and the macOS app's BrandColor.paper) standardize on
   the brand's warm cream `#F5EFE6`. The dark-mode toggle in
   `home.jsx` / `lg-kit.jsx` writes `data-paper="ink"` for dark and
   removes the attribute for light. */
[data-paper="ink"] {
  --paper: #1A1714;
  --paper-warm: #221E1A;
  --paper-cool: #221E1A;
  --ink: #F5EFE6;
  --ink-soft: #D8D2C8;
  --ink-mute: #8E867E;
  --rule: rgba(245, 239, 230, 0.16);
  --line: #F5EFE6;
  --connector: rgba(245, 239, 230, 0.35);
  /* Dark-mode --primary swap (test, 2026-05-12). Tangerine
     `#FF5B1F` from :root reads as "alarm / destructive" on the
     ink paper at full saturation (revert history: 08d6edd swapped
     it to cobalt and the lift didn't land aesthetically; reverted
     to tangerine; this is the second attempt with Ochre instead).
     `#E9C46A` is the brand's warm-gold tertiary — same temperature
     family as tangerine, much higher luminance against ink paper
     (~12.5:1 contrast, WCAG AAA for both normal and large text).
     `--primary-deep` follows the gradient slope tangerine →
     tangerine-deep uses (≈35% darker, similar saturation). */
  --primary: #E9C46A;
  --primary-deep: #B8983D;
  /* Nox = the ink persona; flip to match the inverted ink color so the
     mask doesn't vanish into the dark background. */
  --p6: #F5EFE6;
  /* In dark mode, persona accents collapse to their lighter
     complements — the `--a*` tokens — so persona avatars / credit
     lines don't sit as dark fills on the dark paper. The complement
     pairing is canonical (see brand-guide pairing strip): Tangerine→
     Coral, Cobalt→Sky, Moss→Sage, Plum→Mauve, Ochre→Cream. The
     brand-guide swatch tiles (sections-2-color-type-voice.jsx) read
     from the `h` hex literal directly so they keep showing canonical
     hues even when this swap is in effect. */
  --p1: var(--a1);
  --p2: var(--a2);
  --p3: var(--a3);
  --p4: var(--a4);
  --p5: var(--a5);
}

/* Bass / Bass-Alt talking mouths. Every face component's "talking"
   branch tags its mouth element with className="face-mouth-talk". The
   rule scales it vertically around its own bbox center at ~3Hz so the
   mouth reads as flapping rather than freezing open on hover.
   `transform-box: fill-box` is required so the transform origin sits on
   the element's own bounding box (not the SVG root); without it, the
   ellipse moves around the SVG center and looks broken. Rule lives in
   tokens.css (loaded by every face-bearing page) rather than inside
   <BassDefs>/<BassAltDefs> because React drops the text content of an
   SVG-namespaced <style> element passed via JSX children. */
.face-mouth-talk {
  transform-box: fill-box;
  transform-origin: center;
  animation: face-mouth-talk 0.32s ease-in-out infinite;
}
@keyframes face-mouth-talk {
  0%, 100% { transform: scaleY(0.32); }
  50%      { transform: scaleY(1); }
}
@media (prefers-reduced-motion: reduce) {
  .face-mouth-talk { animation: none; }
}
