/* =========================================================================
   Habenula — shared editorial design system
   Used by every page (landing, faq, privacy, terms, blog). Type-led,
   minimal, premium. Sparse accents. Restrained motion.
   Served from /site.css (not /assets/* — kept out of the immutable cache
   while the site is under active development).
   ========================================================================= */

/* ---- Fonts (self-hosted; CSP font-src 'self') ---- */
@font-face {
  font-family: 'Inter';
  src: url('assets/fonts/Inter/Inter.woff2') format('woff2');
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Cousine';
  src: url('assets/fonts/CousineLibertinus_MathLibertinus_Sans/Cousine/Cousine-Regular.woff2') format('woff2'),
       url('assets/fonts/CousineLibertinus_MathLibertinus_Sans/Cousine/Cousine-Regular.ttf') format('truetype');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Cousine';
  src: url('assets/fonts/CousineLibertinus_MathLibertinus_Sans/Cousine/Cousine-Bold.ttf') format('truetype');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Cousine';
  src: url('assets/fonts/CousineLibertinus_MathLibertinus_Sans/Cousine/Cousine-Italic.ttf') format('truetype');
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}

/* ---- Tokens ----
   Two themes: Signal (dark) is the base, Paper (light) overrides below.
   Everything that differs between them is a token, so the themes swap values
   and never fight each other with duplicated rules. The light values appear
   under two selectors — the system preference, and an explicit choice — which
   is the one duplication worth keeping: it means the correct theme renders
   with no JavaScript at all, and the toggle only has to set an attribute. */
:root {
  color-scheme: dark;
  --bg: #110A0E;
  --bg-raised: rgba(251, 247, 242, 0.022);
  --text: #FBF7F2;
  --text-muted: #A89AA1;
  --text-dim: #8E818A;
  /* Three accents, split by "do vs show":
       Wine (--primary) is the brand voice in type — headlines, emphasis, prose
         links, the reef. It never wears an affirmative control.
       Gold (--gold) marks what the SYSTEM shows you — its own artifacts and
         labels: section eyebrows, inline code, the terminal $ prompt, and
         metadata markers (topic tags, source links).
       Seafoam (--accent) marks what you DO — affirmative and live: buttons and
         CTAs, the (agent, service, verb, noun) permission tuple, the terminal
         success tick, Beny's live dot, selection, interactive hovers.
     Burnt orange means stop — the only warm in the decision UI, so "Deny" can
     never be misread as "go". [color-tweak: wine #99244F + bright-amber gold on
     the deepened #110A0E ground.] */
  --primary: #CC557A;
  --primary-dim: #B0416A;
  /* Gold — a bright amber (from the jewel study) rather than the earlier olive,
     so it stays clean and warm against the wine. Paper deepens it to clear 4.5
     on cream. */
  --gold: #F0B93F;
  --gold-dim: #D19B27;
  --accent: #4EBB9D;
  --accent-dim: #2E9B7F;
  --border: rgba(251, 247, 242, 0.09);
  --border-soft: rgba(251, 247, 242, 0.055);
  --border-strong: #45303C;
  /* Danger is a burnt orange, kept clear of both brand accents: ~40° off the
     wine and ~42° off the gold, so "Deny" reads as its own warm and is never
     confused with the type beside it. One global value — the Paper orange
     #BB3A01 — on both themes. */
  --accent-red: #BB3A01;
  --danger-text: #EF9376;
  --danger-bg: rgba(187, 58, 1, 0.06);
  --danger-line: rgba(187, 58, 1, 0.4);

  /* Theme-varying surfaces and effects. Paper is matte: no atmospheric wash,
     no glass blur, no text halo, flat shadows. */
  --header-bg: rgba(17, 10, 14, 0.62);
  --header-bg-solid: rgba(17, 10, 14, 0.94);
  --dot: rgba(251, 247, 242, 0.15);
  --dot-opacity: 0.72;
  --halo: 0 1px 6px rgba(17, 10, 14, 0.45), 0 0 2px rgba(17, 10, 14, 0.7);
  --atmos-display: block;
  --glass-blur: blur(8px);
  --glass-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
  --card-bg: rgba(251, 247, 242, 0.03);
  /* Control fill. Heavier than --card-bg on purpose: the CTAs sit in open page
     where Beny swims behind them, and at 0.03 she read straight through. Its own
     token rather than a bump to --card-bg, which would also weight Beny's card and
     the confirmation card. */
  --control-fill: rgba(251, 247, 242, 0.085);
  --card-shadow: 0 30px 80px rgba(0, 0, 0, 0.55);
  --chip-bg: rgba(78, 187, 157, 0.16);
  /* Ink for text sitting on a chip. Same problem --term-accent solves one block
     down: the seafoam tint is recessed in both themes, so the full accent reads
     on an ink ground while Paper needs the deeper one to clear 4.5 on cream. */
  --chip-text: var(--accent);
  /* Terminal blocks. The background used to be a hardcoded near-black, so on
     Paper the panel stayed dark while every text token inside it flipped to a
     dark value — a black slab with black text in it. All five are tokens now.
     Paper does not just lighten the panel: it also takes the ink for the body
     text and the deeper accent for the prompt, because on a recessed cream
     panel --text-muted lands at 4.45 and --accent at 3.98, both under 4.5. */
  --term-bg: #000000;
  --term-text: var(--text-muted);
  --term-strong: var(--text);
  --term-accent: var(--accent);
  --term-dim: var(--text-dim);
  /* The reef takes Primary — the colour of "habenula", the first word of the
     hero definition. Aliased rather than copied, and deliberately not repeated
     in the Paper blocks: a custom property resolves at use, so this one line
     follows Primary into whichever theme is live. */
  --coral: var(--primary);
  --coral-dim: var(--primary-dim);
  --seabed: #2A1D22;
  /* Blend for the reef's out-of-register plate. Overprinting ink darkens what
     it crosses, which is `multiply` on a cream stock; on an ink ground the same
     gesture has to lift instead, so Signal screens. */
  --overprint: screen;
  /* The reef's drawn line — fan veins and boulder grooves. It has to read
     against the reef's own inks in both themes, so it lifts on an ink ground
     and darkens on paper, the same logic --overprint follows. Previously
     hardcoded to Signal's seabed, which made it invisible here and harsh on
     Paper. */
  --reef-line: rgba(251, 247, 242, 0.40);
  /* Emphasis inside copy is the OTHER stock: Signal bolds to Paper's ground,
     Paper bolds to Signal's. Body copy runs at --text-muted, so the bold lifts
     clear of it either way, and the relationship is symmetric rather than each
     theme reaching for its own brightest ink. Paper already satisfied this by
     coincidence — Signal's ground and Paper's Ink are the same value — so the
     visible change is on Signal alone, and it is small: #FBF7F2 to #EFE9DA. */
  --emphasis: #EFE9DA;
  --logo-filter: none;
  --selection: rgba(78, 187, 157, 0.26);

  --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-mono: 'Cousine', "Fira Code", Menlo, Consolas, monospace;

  --measure: 66ch;      /* reading column for prose */
  /* The two fixed things that flank the page, and the column derived from them —
     one source of truth, so none of the three can drift apart. The reef sits bottom
     left, Beny's control card bottom right, and the column reserve is symmetric to
     keep the text centred, so it has to be sized off whichever of them is wider.
     Text starts at (W - C) / 2 + gutter, so clearing an edge element of width E
     needs C <= W - 2 * (E - gutter). Both flank widths are fluid, so the reserve
     only ever claims what is actually occupied — that is what keeps both corner
     treatments alive down to the mobile breakpoint instead of surrendering them.
     100% rather than 100vw: vw includes the scrollbar, which would eat the
     clearance. */
  --reef-w: clamp(200px, 18vw, 240px);
  --card-w: clamp(200px, 20vw, 236px);
  --card-inset: 1.2rem;
  /* Plus a gap, so the column clears the flanks rather than abutting them. Without
     it the reserve is exact and the copy touches Beny's card. */
  --edge-gap: 1.5rem;
  --edge-w: calc(max(var(--reef-w), calc(var(--card-w) + var(--card-inset))) + var(--edge-gap));
  --container: min(1000px, calc(100% - 2 * (var(--edge-w) - var(--gutter))));
  --gutter: clamp(1.25rem, 5vw, 3rem);

  /* ---- One vertical scale for every page ----
     Four values every page-level pattern reads, so the drop from header to
     title, the gap under a title block, the whitespace at a section boundary,
     and the close above the footer are the same on every route. Patterns with
     symmetric padding (.faq-group, .rm-group, the section stacks) take half
     the boundary each side, so adjacent blocks still sum to one boundary.
     The page's last block ends flush — the footer's own margin is the close,
     and it must not be doubled by trailing section padding. */
  --space-page-top: clamp(4rem, 12vh, 8rem);
  --space-head-gap: 2.5rem;
  --space-section: 6rem;
  --space-footer-gap: 6rem;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --t: 0.22s;        /* one clock for every control */
  --lift: -2px;      /* hover rise */
  --press: 0.98;     /* active squash */
}

/* ---- Paper (light) ----
   The same token names with light values. Declared twice on purpose: once for
   readers whose system asks for light and who have not overridden it, once for
   readers who chose light explicitly. Costs a duplicated block and buys the
   correct theme with no JavaScript at all — the toggle only sets an attribute.
   Pink and the cool accent both deepen: the dark-theme values sit near 2:1 on
   a cream ground, which is fine for a headline and not for a link. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    color-scheme: light;
    --bg: #F0E9D4;
    --bg-raised: rgba(30, 20, 24, 0.03);
    --text: #1E1418;
    --text-muted: #6A6158;
    --text-dim: #6C6153;
    --primary: #99244F;
    --primary-dim: #7E1B41;
    --gold: #856400;
    --gold-dim: #6A5000;
    --accent: #006F55;
    --accent-dim: #00614C;
    --border: rgba(30, 20, 24, 0.12);
    --border-soft: rgba(30, 20, 24, 0.08);
    --border-strong: #D5CAB2;
    --accent-red: #BB3A01;
    --danger-text: #A83608;
    --danger-bg: rgba(187, 58, 1, 0.06);
    --danger-line: rgba(187, 58, 1, 0.45);
    --header-bg: rgba(240, 233, 212, 0.82);
    --header-bg-solid: rgba(240, 233, 212, 0.96);
    --dot: rgba(40, 28, 16, 0.09);
    --dot-opacity: 0.5;
    --halo: none;
    --atmos-display: none;
    --glass-blur: none;
    --glass-shadow: none;
    --card-bg: rgba(30, 20, 24, 0.035);
    --control-fill: rgba(30, 20, 24, 0.06);
    --card-shadow: 0 8px 24px rgba(30, 20, 24, 0.1);
    --chip-bg: rgba(0, 111, 85, 0.14);
    --chip-text: var(--accent-dim);
    --term-bg: #E4DECF;
    --term-text: var(--text);
    --term-strong: var(--text);
    --term-accent: var(--accent-dim);
    --term-dim: var(--text-muted);
    --seabed: #C9BCA6;
    --overprint: multiply;
    --reef-line: rgba(30, 20, 24, 0.45);
    --emphasis: #1E1418;
    --logo-filter: invert(0.92) sepia(0.06);
    --selection: rgba(0, 111, 85, 0.22);
  }
}
:root[data-theme="light"] {
  color-scheme: light;
  --bg: #F0E9D4;
  --bg-raised: rgba(30, 20, 24, 0.03);
  --text: #1E1418;
  --text-muted: #6A6158;
  --text-dim: #6C6153;
  --primary: #99244F;
  --primary-dim: #7E1B41;
  --gold: #856400;
  --gold-dim: #6A5000;
  --accent: #006F55;
  --accent-dim: #00614C;
  --border: rgba(30, 20, 24, 0.12);
  --border-soft: rgba(30, 20, 24, 0.08);
  --border-strong: #D5CAB2;
  --accent-red: #BB3A01;
  --danger-text: #A83608;
  --danger-bg: rgba(187, 58, 1, 0.06);
  --danger-line: rgba(187, 58, 1, 0.45);
  --header-bg: rgba(240, 233, 212, 0.82);
  --header-bg-solid: rgba(240, 233, 212, 0.96);
  --dot: rgba(40, 28, 16, 0.09);
  --dot-opacity: 0.5;
  --halo: none;
  --atmos-display: none;
  --glass-blur: none;
  --glass-shadow: none;
  --card-bg: rgba(30, 20, 24, 0.035);
  --control-fill: rgba(30, 20, 24, 0.06);
  --card-shadow: 0 8px 24px rgba(30, 20, 24, 0.1);
  --chip-bg: rgba(0, 111, 85, 0.14);
  --chip-text: var(--accent-dim);
  --term-bg: #E4DECF;
  --term-text: var(--text);
  --term-strong: var(--text);
  --term-accent: var(--accent-dim);
  --term-dim: var(--text-muted);
  --seabed: #C9BCA6;
  --overprint: multiply;
  --reef-line: rgba(30, 20, 24, 0.45);
  --emphasis: #1E1418;
  --logo-filter: invert(0.92) sepia(0.06);
  --selection: rgba(0, 111, 85, 0.22);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: var(--font-sans);
  background: var(--bg);
  color: var(--text);
  line-height: 1.65;
  font-size: 1.0625rem;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
  overflow-x: hidden;
}

/* ===== Atmospheric background =====
   Three fixed layers, carried from the coming-soon page: a slowly drifting
   wash, a dot grid, and a soft glow. They live here rather than on the home
   page because every page shares one backdrop — a reader moving from `/` to a
   whitepaper should not cross a visible seam. Each page opens its body with
   the three layer divs; they are inert, `position: fixed`, and sit behind all
   content on negative z-indexes, so page flow never sees them. */

.gradient-bg {
  position: fixed;
  inset: 0;
  z-index: -3;
  pointer-events: none;
  display: var(--atmos-display);
  /* Five faint blooms spanning the brand — wine leads, wine-dim anchors the
     opposite corner, gold and seafoam sit off to the sides, and a barely-there
     warm-white lifts the centre. All 1.5–6% alpha, so the ground still reads
     near-black; the interest is in how they overlap and drift, not in strength. */
  background:
    radial-gradient(ellipse 62% 46% at 12% 16%, rgba(204, 85, 122, 0.06) 0%, transparent 60%),
    radial-gradient(ellipse 52% 44% at 30% 78%, rgba(176, 65, 106, 0.05) 0%, transparent 56%),
    radial-gradient(ellipse 54% 44% at 88% 24%, rgba(240, 185, 63, 0.035) 0%, transparent 55%),
    radial-gradient(ellipse 58% 48% at 80% 76%, rgba(78, 187, 157, 0.032) 0%, transparent 55%),
    radial-gradient(ellipse 46% 38% at 52% 48%, rgba(251, 247, 242, 0.016) 0%, transparent 50%);
  animation: gradientDrift 20s ease-in-out infinite alternate;
}
/* Kept overfilled (scale never < 1.03) so the drift can also translate without
   ever exposing an edge of the fixed layer. */
@keyframes gradientDrift {
  0%   { transform: scale(1.04) rotate(0deg)    translate(0, 0); }
  25%  { transform: scale(1.09) rotate(1.3deg)  translate(1.4%, -1.1%); }
  50%  { transform: scale(1.05) rotate(-0.9deg) translate(-1.2%, 1.4%); }
  75%  { transform: scale(1.10) rotate(0.7deg)  translate(0.8%, -0.4%); }
  100% { transform: scale(1.06) rotate(-0.5deg) translate(-1.5%, 0.6%); }
}
.dot-grid {
  position: fixed;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  background-image: radial-gradient(circle, var(--dot) 1px, transparent 1px);
  background-size: 24px 24px;
  opacity: var(--dot-opacity);
}
.hero-glow {
  position: fixed;
  top: 26%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 62vmin;
  height: 62vmin;
  z-index: -1;
  pointer-events: none;
  display: var(--atmos-display);
  background: radial-gradient(ellipse, rgba(204, 85, 122, 0.05) 0%, transparent 70%);
  filter: blur(60px);
  /* A slow breath on its own clock (9s vs the wash's 20s), so the two layers
     never line up and the atmosphere reads as depth rather than one moving sheet. */
  animation: heroGlowPulse 9s ease-in-out infinite alternate;
}
@keyframes heroGlowPulse {
  0%   { transform: translate(-50%, -50%) scale(0.94); opacity: 0.7; }
  100% { transform: translate(-50%, -50%) scale(1.12); opacity: 1; }
}

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

a { color: inherit; text-decoration: none; }

/* ---- Layout primitives ---- */
.container {
  width: 100%;
  max-width: var(--container);
  margin: 0 auto;
  padding-inline: var(--gutter);
}
.prose { max-width: var(--measure); }

/* ---- Shared kicker / eyebrow ---- */
.kicker {
  font-family: var(--font-mono);
  font-size: 1.1rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--gold);
  /* No opacity here — blending the label toward the ground put the light-theme
     kicker under the 4.5 contrast floor. The gold token carries the full color. */
}
/* Section head — the "What's built in" treatment, one head structure for every
   page: kicker, then the heading, then a muted intro. Lifted from index.html's
   inline sheet so the scale is shared rather than copied per page. The p rule
   skips the kicker: both are <p>s, and unscoped the intro rule restyled the
   kicker to 1.08rem — the pattern's own label was the first thing its cascade
   broke. It also skips .doc-meta, which is a dateline, not an intro. Ledes keep
   their voice: .lede/.lede-wide are !important by design and win the intro
   slot. The pattern owns structure and rhythm and sizes only the h2 tier — an
   h1 in the head keeps its page's display scale. */
.section-head :is(h1, h2) { margin-top: 0.9rem; }
.section-head h2 { font-size: clamp(1.9rem, 4.5vw, 2.8rem); max-width: 18ch; letter-spacing: -0.03em; }
.section-head p:not(.kicker):not(.doc-meta) { color: var(--text-muted); max-width: 48ch; margin-top: 0.9rem; font-size: 1.08rem; }
.center .section-head :is(h2, p:not(.kicker)) { margin-left: auto; margin-right: auto; }

/* ---- Editorial type scale ---- */
.display {
  font-weight: 640;
  letter-spacing: -0.035em;
  line-height: 1.04;
  font-size: clamp(2.6rem, 7vw, 5rem);
}
h1, h2, h3, h4 { font-weight: 620; letter-spacing: -0.02em; line-height: 1.15; }
.accent { color: var(--primary); }
.serif-note { font-family: var(--font-mono); font-style: italic; color: var(--text-muted); }

/* Display headlines run in ink, not colour. Colour is reserved for the small
   marks — kickers, the verb tuple, terminal ticks — and the affirmative CTA,
   which is what keeps it meaning something when it does appear. */
.headline {
  color: var(--text);
  letter-spacing: -0.035em;
}


/* Pill CTA wrapper. Inert on purpose: it was a glass slab with its own fill,
   border, shadow and a specular ::before highlight, which all fought the outline
   treatment the button now carries. It survives only to hold the pill radius that
   .btn-mono inherits and to keep the markup stable across the four pages that use
   it. The padding went to 0 so the hover fill reaches the outline with no gap. */
.cta-glass {
  display: inline-flex;
  position: relative;
  padding: 0;
  border-radius: 9999px;
  background: transparent;
  border: none;
  box-shadow: none;
}
.btn-mono {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  /* Vertical metrics are .btn's, exactly: same padding-block, same font-size and
     so the same computed line-height, plus a transparent border to match .btn's
     1px. Without those three this button computed 48.8px against the ghost's
     52.7px and the pair sat 3.9px out of alignment side by side in .hero-cta.
     The horizontal padding stays wider — the primary earns more width — and the
     mono face stays, because that is what this control is named for. */
  padding: 0.8rem 2.5rem;
  border-radius: inherit;
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 0.95rem;
  /* Explicit, because this pill is now worn by a real <button> too (the hero's
     demo trigger) and the UA gives buttons line-height: normal instead of the
     page's 1.65 — the same 7px runt #beny-recall documents. Anchors already
     inherited 1.65, so this changes nothing for them. */
  line-height: 1.65;
  text-decoration: none;
  /* Affirmative control: cool only. See the token block on why no pink here.
     An outline that fills on hover rather than a standing gradient — the shape is
     drawn by the accent and the hover state is the shape completing. */
  /* Frosted control: a translucent fill over a blurred backdrop, so Beny diffuses
     as she passes behind rather than showing through sharp. --glass-blur is `none`
     on Paper by design (the light theme is matte), so there the fill does the work
     alone. The terminal block is deliberately not this surface — it uses the opaque
     --term-bg and recesses. */
  background: var(--control-fill);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  color: var(--accent);
  border: 1px solid var(--accent);
  cursor: pointer;
}
.btn-mono:hover { background: var(--accent); color: var(--bg); box-shadow: 0 10px 30px var(--selection); }

/* ---- Header (shared, minimal, sticky) ---- */
.site-header {
  position: sticky;
  top: 0;
  /* Above everything, explicitly above Beny's control card and restore handle at
     1000. The header is the page's chrome; nothing decorative sits over it. */
  z-index: 1100;
  background: var(--header-bg);
  backdrop-filter: saturate(1.4) blur(14px);
  -webkit-backdrop-filter: saturate(1.4) blur(14px);
  border-bottom: 1px solid var(--border-soft);
}
/* If the browser can't blur the backdrop, keep the sticky nav legible with a near-opaque bar. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header { background: var(--header-bg-solid); }
}
/* Every anchor target must clear the sticky bar (72px + border, plus
   breathing room), or a deep link lands with its heading underneath it.
   Global on purpose: enumerating target selectors kept missing section-level
   ids (/community#contact shipped hidden under the bar), and scroll-margin is
   inert except when the element is the scroll target — it also positions the
   demo's scrollIntoView() reveal. */
[id] { scroll-margin-top: 88px; }
/* The bar carries more content than the page's text column can hold — the
   wordmark, seven links, the injected agent button and the toggle come to
   ~1060px against the column's 1000px cap. Sharing --container let that
   content overflow the column's right edge: flex overflow spills rightward,
   so the container stayed centred while its content did not, which read as
   the header sitting off-centre and then clipping as the window narrowed.
   The bar gets its own, wider centred container instead; the cap is sized so
   the full nav fits inside it with the standard gutters. */
.site-header .container { max-width: none; }
.site-header .bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 72px;
}
.site-header .brand { display: inline-flex; align-items: center; gap: 0.6rem; flex-shrink: 0; }
.site-header .brand img { display: block; height: 60px; width: auto; opacity: 0.94; filter: var(--logo-filter); }
.site-header nav { display: flex; align-items: center; gap: 1.6rem; }
.site-header nav a {
  font-size: 0.925rem;
  color: var(--text-muted);
  white-space: nowrap;
  transition: color var(--t) var(--ease);
}
.site-header nav a:hover, .site-header nav a[aria-current="page"] { color: var(--text); }
/* Theme toggle. One button, two icons — the sun is shown in dark mode (what a
   press gets you) and the moon in light. The pair is swapped with CSS so the
   markup is identical on every page and nothing has to be re-rendered. */
.theme-toggle {
  display: inline-grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-raised);
  color: var(--text-muted);
  cursor: pointer;
}
.theme-toggle:hover { color: var(--text); border-color: var(--accent); box-shadow: 0 8px 24px var(--selection); }
.theme-toggle svg { width: 17px; height: 17px; display: block; grid-area: 1 / 1; }
.theme-toggle .icon-moon { display: none; }
:root[data-theme="light"] .theme-toggle .icon-sun { display: none; }
:root[data-theme="light"] .theme-toggle .icon-moon { display: block; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .theme-toggle .icon-sun { display: none; }
  :root:not([data-theme="dark"]) .theme-toggle .icon-moon { display: block; }
}

/* ---- Buttons ---- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  font-family: var(--font-sans); font-weight: 600; font-size: 0.95rem;
  padding: 0.8rem 1.6rem; border-radius: 999px; cursor: pointer;
  border: 1px solid transparent;
}
.btn-primary { background: var(--accent); color: var(--bg); }
.btn-primary:hover { color: var(--bg); box-shadow: 0 10px 30px var(--selection); }
.btn-ghost {
  background: var(--control-fill); color: var(--accent); border-color: var(--accent);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
}
.btn-ghost:hover { background: var(--accent); color: var(--bg); border-color: var(--accent); box-shadow: 0 10px 30px var(--selection); }

/* ---- "Ask your AI about us" ----
   A header control and its dialog, both injected by agent-context.js. It reads as a
   quiet outline chip rather than a CTA: it is a utility for readers who want their
   own AI briefed on Habenula, not the action the page is asking for. */
#ctx-open {
  font-family: var(--font-mono);
  font-size: 0.85rem; letter-spacing: 0.04em; font-weight: 600;
  padding: 0.4rem 0.7rem; border-radius: 999px; cursor: pointer;
  color: var(--text-muted); background: var(--control-fill);
  border: 1px solid var(--border); white-space: nowrap; text-decoration: none;
}
#ctx-open:hover { color: var(--accent); border-color: var(--accent); }

#ctx-dialog {
  /* Explicit, because the UA centring for a modal dialog is `inset: 0; margin: auto`
     and setting width alone was enough to lose it — it rendered hard against the
     top-left corner. */
  inset: 0; margin: auto;
  max-width: 640px; width: calc(100% - 2rem);
  padding: 1.4rem; border-radius: 16px;
  color: var(--text); background: var(--bg);
  border: 1px solid var(--border-strong);
  box-shadow: var(--card-shadow);
}
#ctx-dialog::backdrop { background: rgba(0, 0, 0, 0.55); }
#ctx-dialog .ctx-head { display: flex; align-items: flex-start; gap: 1rem; }
#ctx-dialog h2 { font-size: 1.15rem; letter-spacing: -0.01em; }
/* The deny-orange reminder: an honest note that the tool it points to is
   fallible, in the same colour the governance UI uses for "deny". */
#ctx-dialog .ctx-disclaimer { margin: 0.4rem 0 0; font-family: var(--font-mono); font-size: 0.82rem; line-height: 1.5; color: var(--danger-text); letter-spacing: 0.01em; }
#ctx-dialog .ctx-steps {
  margin: 0.45rem 0 0; padding-left: 1.25rem;
  color: var(--text-muted); font-size: 0.86rem; line-height: 1.55;
}
#ctx-dialog .ctx-steps li { margin-top: 0.15rem; }
#ctx-dialog .ctx-close {
  margin-left: auto; flex-shrink: 0; display: grid; place-items: center;
  width: 26px; height: 26px; padding: 0; cursor: pointer;
  color: var(--text-dim); background: none;
  border: 1px solid var(--border-strong); border-radius: 7px;
}
#ctx-dialog .ctx-close:hover { color: var(--text); border-color: var(--accent); }
#ctx-dialog .ctx-close svg { width: 12px; height: 12px; }
/* The prompt is meant to be read and copied, so it wraps rather than scrolling
   sideways — a horizontal scrollbar hides half of what the reader is about to paste. */
#ctx-dialog .ctx-prompt {
  margin-top: 1.1rem; padding: 0.9rem 1rem; border-radius: 10px;
  background: var(--term-bg); border: 1px solid var(--border-soft);
  max-height: 46vh; overflow-y: auto;
}
#ctx-dialog .ctx-prompt code {
  font-family: var(--font-mono); font-size: 0.85rem; line-height: 1.7;
  color: var(--term-text); background: none; padding: 0;
  white-space: pre-wrap; word-break: break-word;
}
#ctx-dialog .ctx-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.7rem 1rem; margin-top: 1.1rem; }
#ctx-dialog .ctx-copy,
#ctx-dialog .ctx-copy-full {
  font-family: var(--font-mono); font-size: 0.85rem; font-weight: 600;
  padding: 0.6rem 1.1rem; border-radius: 999px; cursor: pointer;
  color: var(--accent); background: var(--control-fill);
  border: 1px solid var(--accent); min-width: 8.5rem;
}
#ctx-dialog .ctx-copy:hover,
#ctx-dialog .ctx-copy-full:hover { color: var(--bg); background: var(--accent); }
/* The full-text button is the fallback, so it sits a step quieter than the default. */
#ctx-dialog .ctx-copy-full { color: var(--text-muted); border-color: var(--border-strong); }
#ctx-dialog .ctx-copy-full:disabled { cursor: wait; opacity: 0.7; }
#ctx-dialog .ctx-raw { font-family: var(--font-mono); font-size: 0.85rem; color: var(--text-muted); }
#ctx-dialog .ctx-raw:hover { color: var(--accent); }
#ctx-dialog .ctx-hint { margin-top: 0.7rem; color: var(--text-dim); font-size: 0.8rem; }
/* The label is the first thing to go when the nav runs out of room. */
@media (max-width: 1180px) { #ctx-open { display: none; } }

/* ---- One interaction language ----
   Every control rises the same distance on hover, presses the same amount on
   click, runs on the same clock, and shows the same focus ring. Before this
   the lift was -2px, -1px or nothing depending on which control you touched,
   only the hero CTA had a press state, transitions ran at two speeds, and
   nothing had a focus style at all. `#beny-recall` picks the same tokens up in
   the page's own stylesheet. The nav no longer has a filled CTA link — every page
   carries home's nav, which has none — so those selectors are gone. */
.cta-glass,
.btn,
.btn-mono,
.theme-toggle,
#ctx-open,
#ctx-dialog .ctx-copy,
#ctx-dialog .ctx-copy-full {
  transition: color var(--t) var(--ease), background var(--t) var(--ease),
              border-color var(--t) var(--ease), transform var(--t) var(--ease),
              box-shadow var(--t) var(--ease);
}
.cta-glass:hover,
.btn:hover,
.theme-toggle:hover { transform: translateY(var(--lift)); }

.cta-glass:active,
.btn:active,
.theme-toggle:active { transform: translateY(0) scale(var(--press)); }

/* Keyboard users had no way to see where they were. */
a:focus-visible,
button:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .cta-glass, .btn, .btn-mono, .theme-toggle { transition: none; }
  .cta-glass:hover, .btn:hover, .theme-toggle:hover,
  .cta-glass:active, .btn:active, .theme-toggle:active { transform: none; }
}

/* ---- Code / terminal (the product moments) ---- */
/* Bold in copy carries the emphasis ink. The colour lift lived on the long-form
   document rule only, so pages that do not use that wrapper — faq, compare — had
   bold with no lift at all, identical in colour to the copy around it. A base rule
   covers every page, and nothing bold sits inside a button, card or terminal where
   a local colour would need to win. */
strong, b { color: var(--emphasis); font-weight: 600; }
code, kbd, samp { font-family: var(--font-mono); font-size: 0.92em; }
.mono { font-family: var(--font-mono); }
.tuple { font-family: var(--font-mono); color: var(--accent); font-size: 0.9em; }
.term {
  font-family: var(--font-mono);
  font-size: 0.86rem;
  line-height: 1.75;
  background: var(--term-bg);
  border: 1px solid var(--border-soft);
  border-radius: 12px;
  padding: 1.1rem 1.25rem;
  overflow-x: auto;
  color: var(--term-text);
}
.term .c-prompt { color: var(--gold); }
.term .c-cmd { color: var(--term-strong); }
.term .c-ok { color: var(--term-accent); } /* success reads cool, like every affirmative */
.term .c-dim { color: var(--term-dim); }

/* ===== Copyable command ===== */
/* A single shell command the reader can actually copy — the terminal surface of
   .term, but a real control rather than a vignette. The command string lives in
   data-copy so the copied text never carries the prompt glyph. Clipboard API
   needs a secure context; copy-cmd wiring selects the line as a ⌘C fallback where
   it is absent, and the noscript rule hides the button when JS is off (the line
   stays readable and selectable either way). */
.cmd-copy {
  display: flex; align-items: center; gap: 0.75rem;
  font-family: var(--font-mono); font-size: 0.86rem; line-height: 1.6;
  background: var(--term-bg); border: 1px solid var(--border-soft);
  border-radius: 12px; padding: 0.62rem 0.62rem 0.62rem 1.05rem;
  color: var(--term-text);
}
.cmd-copy .cmd-line { flex: 1 1 auto; min-width: 0; overflow-x: auto; white-space: nowrap; }
.cmd-copy .c-prompt { color: var(--gold); }
.cmd-copy .c-cmd { color: var(--term-strong); }
.cmd-copy-btn {
  flex-shrink: 0; font-family: var(--font-mono); font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.04em; color: var(--accent); background: var(--control-fill);
  border: 1px solid var(--accent); border-radius: 8px; padding: 0.42rem 0.72rem;
  cursor: pointer;
  transition: color var(--t) var(--ease), background var(--t) var(--ease), border-color var(--t) var(--ease);
}
.cmd-copy-btn:hover { background: var(--accent); color: var(--bg); }
.cmd-copy-btn.copied { color: var(--bg); background: var(--accent); border-color: var(--accent); }

/* Install paths (the open-source section): the one-command npx path and the
   from-source clone path, each labelled. */
.install { display: flex; flex-direction: column; gap: 1.5rem; }
.install-path { display: flex; flex-direction: column; gap: 0.5rem; }
.install-label { font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-muted); margin: 0; }
.install-path .term { margin-top: 0.1rem; }
/* Reassurance under the one-command install: the first thing we ask for is not
   blind trust. Muted, so it reads as a footnote to the command, not a claim. */
.install-note { font-family: var(--font-mono); font-size: 0.76rem; color: var(--text-dim); line-height: 1.55; letter-spacing: 0.02em; margin: 0.1rem 0 0; }
/* Quick-install pointer under a page intro (the product page head). */
.head-install { margin-top: 1.05rem; font-family: var(--font-mono); font-size: 0.9rem; }
/* External-link glyph: marks a link that opens in a new tab (GitHub, the source).
   Sized to the surrounding text and slightly dimmed so it reads as a hint. */
.ext-icon { width: 0.72em; height: 0.72em; margin-left: 0.3em; vertical-align: -0.02em; opacity: 0.6; flex-shrink: 0; }

/* ---- Footer (shared) ---- */
.site-footer {
  border-top: 1px solid var(--border-soft);
  margin-top: var(--space-footer-gap);
  padding: 3.5rem 0 3rem;
  color: var(--text-muted);
  font-size: 0.9rem;
}
/* Six blocks: brand + four link columns + the Social icon column, which
   hugs its content so the text columns keep their width. */
.site-footer .cols {
  display: grid;
  grid-template-columns: 1.4fr repeat(4, 1fr) auto;
  gap: 2.5rem;
}
/* A <p>, not an <h4>: the column labels sat four heading levels under the
   page h1 and tripped heading-order on every route. They are field
   labels, not document structure — same voice as .post-meta and figcaption. */
.site-footer .foot-label {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-dim);
  font-weight: 400;
  margin: 0 0 1rem;
  white-space: nowrap;
}
.site-footer ul { list-style: none; display: flex; flex-direction: column; gap: 0.55rem; }
.site-footer a { color: var(--text-muted); transition: color var(--t) var(--ease); }
.site-footer a:hover { color: var(--primary); }
/* The brand is a column like any other, so its header occupies exactly one
   .foot-label line plus that label's margin — the tagline then starts on the
   same line as every column's first link. The wordmark is taller than a label
   line, so it is centred on that line and the surplus is absorbed above and
   below. display:block drops the inline image's descender gap, which is what
   pushed the tagline a line lower than the links. */
.site-footer .foot-brand img {
  --foot-label-line: calc(0.85rem * 1.65); /* .foot-label font-size x body line-height */
  --foot-wordmark-bleed: calc((var(--foot-label-line) - 30px) / 2);
  display: block;
  height: 30px;
  width: auto;
  opacity: 0.9;
  margin: var(--foot-wordmark-bleed) 0 calc(1rem + var(--foot-wordmark-bleed));
  filter: var(--logo-filter);
}
.site-footer .foot-brand p { max-width: 30ch; color: var(--text-dim); font-size: 0.85rem; }
/* Social logos: the footer's Social column, a 2x3 icon grid beside the
   Company column on every page. Inline SVG glyphs take currentColor, so
   they theme with the footer. The profile URLs also live in the homepage
   Organization JSON-LD sameAs array — a handle change updates both. */
.site-footer .social-col {
  display: grid;
  grid-template-columns: repeat(2, max-content);
  gap: 0.9rem 1.1rem;
}
.site-footer .social-col a { display: inline-flex; padding: 0.1rem 0; }
.site-footer .social-col svg { width: 20px; height: 20px; }
.site-footer .legal-row {
  margin-top: 3rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--border-soft);
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  color: var(--text-dim);
  font-size: 0.85rem;
}

/* ---- FAQ ---- */
.faq-group { padding: calc(var(--space-section) / 2) 0; border-top: 1px solid var(--border-soft); }
.faq-group > h2 {
  font-size: clamp(1.6rem, 3.4vw, 2.1rem);
  margin-bottom: 2.2rem;
  color: var(--text);
}
.qa { max-width: var(--measure); padding: 1.6rem 0; }
.qa + .qa { border-top: 1px solid var(--border-soft); }
/* Question headers are static coral — not clickable (the permalink lives on the
   parent <article id>), so they carry the brand colour rather than a link's. */
.qa h3 { font-size: 1.2rem; font-weight: 600; margin-bottom: 0.7rem; color: var(--primary); }
.qa p { color: var(--text-muted); }
.qa p + p { margin-top: 0.85rem; }
.qa .term { margin-top: 1rem; max-width: 100%; }
.qa code { color: var(--gold); background: rgba(255,255,255,0.04); padding: 0.08em 0.4em; border-radius: 5px; }

/* Public roadmap — prose plus nested release lists. Same section rhythm as the
   FAQ groups; the lists get real spacing and an accent marker so a release
   stops reading as a wall of text. */
.rm-group { padding: calc(var(--space-section) / 2) 0; border-top: 1px solid var(--border-soft); }
.rm-group > h2 { font-size: clamp(1.5rem, 3.2vw, 2rem); letter-spacing: -0.02em; color: var(--text); margin-bottom: 1.1rem; }
.rm-group > p { color: var(--text-muted); max-width: var(--measure); line-height: 1.7; margin: 0 0 0.9rem; }
.rm-group > p strong { color: var(--text); }
.rm-group ul { list-style: none; margin: 0.2rem 0 1.6rem; padding: 0; max-width: var(--measure); display: grid; gap: 0.7rem; }
.rm-group li { position: relative; padding-left: 1.4rem; color: var(--text-muted); line-height: 1.6; }
.rm-group li strong { color: var(--text); }
.rm-group li::before { content: ""; position: absolute; left: 0; top: 0.6em; width: 6px; height: 6px; border-radius: 999px; background: var(--accent); }
.rm-group ul ul { margin: 0.6rem 0 0; gap: 0.5rem; }
.rm-group ul ul li::before { width: 6px; height: 6px; border-radius: 999px; background: none; border: 1px solid var(--accent); top: 0.55em; }
.rm-group code { color: var(--gold); background: rgba(255,255,255,0.04); padding: 0.08em 0.4em; border-radius: 5px; }

/* ---- Long-form document (privacy, terms, blog posts) ---- */
/* Same top drop as .page-head, so the title does not jump between a listing
   page and an article. No bottom padding: the footer's margin is the close. */
.doc { max-width: var(--measure); padding: var(--space-page-top) 0 0; }
.doc h1 { font-size: clamp(2.2rem, 5vw, 3.2rem); letter-spacing: -0.03em; }
.doc .doc-meta { color: var(--text-dim); font-family: var(--font-mono); font-size: 0.85rem; margin-top: 1rem; }
/* Flat 1.5rem is deliberate against .section-head h2's clamp: that
   clamp is the lead-subheader scale, this is the in-document scale — a legal
   doc has a dozen h2s per screenful and they must not compete with the page
   head. FAQ's .section-num and the whitepapers' .sec-no hang off these
   metrics, so a change here moves both. */
.doc h2 { font-size: 1.5rem; margin: 2.8rem 0 0.9rem; }
/* Primary, not primary-dim: 1.15rem is under the WCAG large-text threshold,
   and the dim step reads 3.85:1 on the dark ground. */
.doc h3 { font-size: 1.15rem; margin: 1.8rem 0 0.6rem; color: var(--primary); }
/* The p and a rules exclude components through :where(), which adds zero
   specificity. The kicker is a <p> and these are the same cascade trap
   .section-head p fell into: at 0-1-1 the prose rules beat .kicker's
   0-1-0 on color and hand it a paragraph margin. The buttons are <a>s on
   careers/team, and .doc a beat .btn-ghost/.btn-mono the same way — pink
   underlined text inside a green pill. Plain :not() would fix those two but
   lift these selectors past .post-meta .doc-md (0-2-0), un-minting the
   whitepaper download links; :where() keeps the specificity exactly where the
   rest of the file expects it. The :not()s are chained rather than a selector
   list: Firefox 78-83 parses :where() but not list-argument :not(), and
   :where()'s forgiving parsing would silently turn a list form into
   match-nothing there. .doc li stays outside the :where() rules so browsers
   with no :where() support at all keep list-item color. */
.doc p:where(:not(.kicker)) { color: var(--text-muted); margin-bottom: 1rem; }
.doc ul, .doc ol { margin: 0 0 1rem 1.3rem; }
.doc li { color: var(--text-muted); margin-bottom: 0.5rem; }
.doc a:where(:not(.btn):not(.btn-ghost):not(.btn-mono)) { color: var(--primary); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: rgba(204, 85, 122, 0.35); }
.doc a:where(:not(.btn):not(.btn-ghost):not(.btn-mono)):hover { text-decoration-color: var(--primary); }
/* Inline "read more →" links. Wine — a text link is brand voice, like the prose
   links; green is reserved for operable controls (buttons, the confirm card).
   Declared after the .doc link rules so it also wins inside a post body. */
a.arrow-link { color: var(--primary); font-weight: 500; transition: color var(--t) var(--ease); }
a.arrow-link:hover { color: var(--primary); text-decoration: underline; text-underline-offset: 3px; }
.doc .callout {
  border: 1px solid var(--border-soft);
  border-left: 2px solid var(--accent);
  background: var(--bg-raised);
  border-radius: 10px;
  padding: 1rem 1.2rem;
  margin: 1.5rem 0;
  font-size: 0.94rem;
}

/* Summary variant — the TL;DR box that leads the "what is an agent" post.
   A richer take on .callout so a skimmable summary reads as designed rather
   than as a plain note: accent arrow markers instead of discs, roomier
   rhythm, crisper body text, and a soft accent lift. Tokens only, so both
   themes track. Declared after .callout so its overrides win at equal
   specificity; every other .callout on the site is untouched. */
.doc .callout-summary {
  border-left-width: 3px;
  border-radius: 12px;
  padding: 1.5rem 1.7rem;
  margin: 2rem 0 2.4rem;
  font-size: 1rem;
  box-shadow: 0 14px 34px -20px var(--selection);
}
.doc .callout-summary ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.95rem;
}
.doc .callout-summary li {
  position: relative;
  margin: 0;
  padding-left: 1.7rem;
  color: var(--text);
  line-height: 1.62;
}
.doc .callout-summary li::before {
  content: "→";
  position: absolute;
  left: 0;
  color: var(--accent);
  font-weight: 700;
}

/* ---- Long-form paper bodies (whitepapers) ----
   The papers carry structures the shorter doc pages never needed: a gaps
   table, fenced examples, inline identifiers, and a figure. */

.doc code {
  font-family: var(--font-mono);
  font-size: 0.88em;
  /* Gold at code size clears the 4.5 floor on both themes — bright amber on the
     dark chip, the deep Paper gold on cream. Same call as .tuple / .qa code. */
  color: var(--gold);
  background: rgba(255, 255, 255, 0.04);
  padding: 0.08em 0.4em;
  border-radius: 5px;
}
.doc pre {
  background: var(--bg-raised);
  border: 1px solid var(--border-soft);
  border-radius: 10px;
  padding: 1rem 1.2rem;
  margin: 1.5rem 0;
  overflow-x: auto;
}
.doc pre code { background: none; padding: 0; color: var(--text-muted); font-size: 0.86rem; line-height: 1.7; }

/* Wide tables scroll in their own track — the page body never scrolls sideways. */
.doc .table-scroll { overflow-x: auto; margin: 1.5rem 0; }
.doc table { border-collapse: collapse; width: 100%; min-width: 34rem; font-size: 0.9rem; }
.doc th, .doc td {
  text-align: left;
  vertical-align: top;
  padding: 0.7rem 1rem 0.7rem 0;
  border-bottom: 1px solid var(--border-soft);
}
.doc thead th {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-dim);
  font-weight: 500;
}
.doc tbody th { font-weight: 500; color: var(--text); padding-right: 2rem; }
.doc td { color: var(--text-muted); }

/* Section numbers in paper headings, set back from the title text. */
.doc h2 .sec-no, .doc h3 .sec-no { color: var(--text-dim); font-weight: 400; margin-right: 0.6rem; }

/* The architecture figure: authored as inline SVG so it needs no renderer and
   stays inside the `default-src 'none'` CSP the site serves. */
.doc .figure { margin: 1.8rem 0; }
.doc .figure svg { width: 100%; height: auto; max-width: 640px; display: block; }
.doc .figure figcaption {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  margin-top: 0.8rem;
}

/* ---- Blog: index list ---- */
.post-list { max-width: 760px; border-top: 1px solid var(--border-soft); }
.post-item { display: block; padding: 2rem 0; border-bottom: 1px solid var(--border-soft); transition: opacity var(--t) var(--ease); }
/* The signup card closes the list, so it carries no rule under it — a
   divider there would separate the card from the paragraph that follows the
   list rather than from another card. Its 2rem padding-bottom then matches
   the 2rem above, and the paragraph after the list supplies no top margin of
   its own. */
.post-item#subscribe { border-bottom: 0; }
/* Hover styling is scoped to a.post-item: every list card is a link except
   the signup card (a plain div), and a link color on a non-link heading
   reads as a broken link. */
a.post-item:hover { opacity: 1; }
a.post-item:hover .post-title { color: var(--primary); }
.post-meta { font-family: var(--font-mono); font-size: 0.99rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-dim); }
.post-meta .tag-topic { color: var(--gold); }
.post-title { font-size: clamp(1.4rem, 3vw, 1.9rem); font-weight: 620; letter-spacing: -0.02em; margin: 0.7rem 0 0.5rem; color: var(--text); transition: color var(--t) var(--ease); }
.post-dek { color: var(--text-muted); max-width: 60ch; line-height: 1.6; }
.post-more { display: inline-block; margin-top: 0.9rem; font-family: var(--font-mono); font-size: 0.95rem; color: var(--primary); }

/* ---- Mailing-list signup (community page) ----
   Lives inside a .post-item card. Input and button share a wrapping row;
   the Turnstile widget renders into its own block below (Cloudflare sizes
   the iframe itself), and the status line reports the fetch's outcome. */
/* Grid, not flex, so the bot check can be exactly as wide as the field above
   it and start on the same left edge. A flex row can give the widget a line
   of its own or a share of one, but not the width of a sibling. Column 1 is
   the field, column 2 the button, and the widget takes column 1 on the next
   row. Below the breakpoint the button drops under the field and everything
   is one column. */
.subscribe-form {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0.7rem;
  align-items: center;
  margin-top: 1.1rem;
}
.subscribe-form .cf-turnstile { grid-column: 1; }
@media (max-width: 560px) {
  .subscribe-form { grid-template-columns: 1fr; }
  .subscribe-form .btn { justify-self: start; }
}
.subscribe-form input[type="email"] {
  padding: 0.72rem 1rem;
  font: inherit;
  color: var(--text);
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: 10px;
}
.subscribe-form input[type="email"]::placeholder { color: var(--text-dim); }
.subscribe-form input[type="email"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.subscribe-form .cf-turnstile { flex-basis: 100%; }
/* Both of these used to hold a row open under the input. The status line
   reserved 1.2em against a layout shift that costs one line and only ever
   happens on submit, and the widget container reserved a row before the
   Turnstile script had put anything in it — which, running interaction-only,
   is most visits. The element stays in the DOM either way: it is an
   aria-live region, and a region that appears only when it has something to
   say is not reliably announced. */
.subscribe-status { grid-column: 1 / -1; margin: 0; font-family: var(--font-mono); font-size: 0.85rem; color: var(--text-muted); }
.subscribe-form .cf-turnstile:empty { display: none; }

/* Shared "release notes by email" widget wrapper — a centred, bordered block
   any page can drop the subscribe form into (the homepage scopes its own copy
   inside .final; this is the base other pages use). */
.subscribe-cta { max-width: 34rem; margin: 3rem auto 0; padding-top: 2.4rem; border-top: 1px solid var(--border-soft); text-align: center; }
.subscribe-cta .tags-label { margin: 0 0 0.5rem; }
.subscribe-cta .subscribe-note { color: var(--text-muted); margin: 0 0 0.4rem; font-size: 0.95rem; }
.subscribe-cta .subscribe-form { text-align: left; }

/* ---- Blog: single post ---- */
/* The published source, linked from the byline of the page it rendered into.
   A metadata marker, so it takes gold (the governed-artifact / label colour),
   not the wine prose-link. Scoped through .post-meta for specificity: `.doc a`
   sets the body-link wine at 0-1-1, which beats a bare `.doc-md` at 0-1-0 no
   matter where it sits in the file, so without the scope it renders as prose
   wine rather than the label gold. */
.post-meta .doc-md { color: var(--gold); text-decoration-color: rgba(240, 185, 63, 0.4); }
.post-meta .doc-md:hover { color: var(--gold); text-decoration-color: var(--gold); }
.post-back { font-family: var(--font-mono); font-size: 0.85rem; color: var(--text-muted); display: inline-block; margin-bottom: 2rem; }
.post-back:hover { color: var(--primary); }
.post-header { max-width: var(--measure); }
.post-header .post-meta { margin-bottom: 1.2rem; }
.post-header h1 { font-size: clamp(2rem, 5vw, 3rem); letter-spacing: -0.03em; line-height: 1.08; }
.post-header .dek { color: var(--text-muted); font-size: 1.2rem; line-height: 1.55; margin-top: 1.2rem; max-width: 54ch; }
.post-byline { font-family: var(--font-mono); font-size: 0.85rem; color: var(--text-dim); margin-top: 1.6rem; padding-top: 1.2rem; border-top: 1px solid var(--border-soft); }
.post-sample-note { font-family: var(--font-mono); font-size: 0.85rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-dim); border: 1px solid var(--border-soft); border-radius: 999px; padding: 0.3rem 0.8rem; display: inline-block; }

/* ---- Team roster ---- */
/* Headshots ship as square, self-hosted JPEGs under assets/team/, displayed as
   circular avatars — the CSP serves `img-src 'self' data:`, so every photo lives
   in the repo rather than a CDN. Anyone without a photo yet falls back to a
   two-letter monogram in the mono face; .roster-photo and .roster-mono share a
   footprint and radius so a mixed row stays aligned.
   The roster breaks out of .doc's reading measure — a 66ch column would stack
   two-up cards into one narrow file — so it sets its own width. */
.roster {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(23rem, 1fr));
  gap: 1.1rem;
  margin: 1.8rem 0 0;
  width: min(100%, 54rem);
}
.roster-card {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 1.35rem;
  padding: 1.5rem 1.6rem;
  /* Same frosted surface as Beny's control card (#beny-agent): the translucent
     --card-bg fill over a blurred backdrop, with the card shadow. --glass-blur is
     `none` on Paper, so the light theme stays matte and the fill carries it. */
  background: var(--card-bg);
  border: 1px solid var(--border-soft);
  border-radius: 18px;
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  box-shadow: var(--card-shadow);
}
.roster-mono,
.roster-photo {
  flex-shrink: 0;
  width: 5rem;
  height: 5rem;
  border-radius: 50%;
}
.roster-mono {
  background: var(--chip-bg);
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-size: 1.3rem;
  letter-spacing: 0.04em;
  color: var(--chip-text);
}
.roster-photo {
  object-fit: cover;
  /* Centre the face in the circle for a portrait source (Jack, Eli): balanced
     headroom above, no chest-heavy centring. A no-op on square sources (Jamie,
     Koby), where cover crops nothing. */
  object-position: center 15%;
  display: block;
  background: var(--chip-bg);
}
/* Eli's source is cropped tighter, so she frames at her own vertical. */
.roster-photo[src$="eli-margolin.jpg"] { object-position: center 20%; }
.roster-name { font-weight: 600; color: var(--text); font-size: 1.05rem; }
/* The role sits in the mono face at the same size and tracking as every other
   label in the system (.post-meta, figcaption, footer h4) so it reads as a field
   label rather than a second, quieter name. It takes gold — the colour for a
   label the system shows you, like the section eyebrow — not seafoam, which is
   reserved for things you do. */
.roster-role {
  font-family: var(--font-mono);
  font-size: 0.85rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold);
  margin-top: 0.3rem;
}
/* Overrides .doc p, which would otherwise add a bottom margin the card's own
   padding already provides, and hold the prose line-height at a full paragraph's. */
.doc .roster-card p.roster-bio {
  color: var(--text-muted);
  font-size: 0.88rem;
  line-height: 1.55;
  margin: 0.6rem 0 0;
}

/* ---- Contact grid (community page) ---- */
/* Four contact routes, two-up. The cards borrow the roster's shell (card-bg,
   border-soft, 18px radius) and the post list's label vocabulary; the whole
   card is the link, and the address line takes the post-title hover. */
/* The contact head is a sub-section of the page, not a second page title:
   its h2 matches .post-title's size so it sits level with the card titles
   in the lists above it. */
#contact .section-head h2 {
  font-size: clamp(1.4rem, 3vw, 1.9rem);
  letter-spacing: -0.02em;
}
.contact-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  gap: 1.1rem;
  max-width: 760px;
  margin-top: 1.8rem;
}
.contact-card {
  display: block;
  padding: 1.3rem 1.5rem;
  background: var(--card-bg);
  border: 1px solid var(--border-soft);
  border-radius: 18px;
}
.contact-card:hover .contact-addr { color: var(--primary); }
.contact-addr {
  font-size: 1.05rem;
  font-weight: 620;
  letter-spacing: -0.01em;
  color: var(--text);
  margin: 0.55rem 0 0.35rem;
  transition: color var(--t) var(--ease);
}
.contact-note {
  color: var(--text-muted);
  font-size: 0.88rem;
  line-height: 1.55;
}

/* ---- Reveal-on-scroll (subtle: fade + short rise) ---- */
.reveal { opacity: 0; transform: translateY(14px); transition: opacity 0.7s var(--ease), transform 0.7s var(--ease); }
.reveal.in { opacity: 1; transform: none; }

/* Two-step squeeze, measured against the real nav rather than estimated. With
   the six inline links (Home through GitHub) plus the Reading dropdown, the
   agent button and the toggle, the nav measures 745px at 1181px — the tightest
   width of the full-width state — leaving 148px of slack beside the wordmark,
   so the 1180 collapse keeps a comfortable margin for cross-engine
   text-measurement variance.
   The 1.2rem gap belongs to the collapsed state, where only the controls remain,
   so it moves with the collapse — left above it, it would loosen the gaps in the
   squeeze band while the links were still shown, and cost the fit it is meant
   to protect. Keep the footer's own breakpoint separate. */
@media (max-width: 1280px) {
  .site-header nav { gap: 1.05rem; }
}
@media (max-width: 1180px) {
  .site-header nav a.hide-sm { display: none; }
  .site-header nav { gap: 1.2rem; }
}
@media (max-width: 780px) {
  .site-footer .cols { grid-template-columns: 1fr 1fr; gap: 2rem; }
  .site-footer .foot-brand { grid-column: 1 / -1; }
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1 !important; transform: none !important; transition: none; }
  /* The backdrop's drift is decorative — hold it still rather than remove it. */
  .gradient-bg { animation: none !important; }
  .hero-glow { animation: none !important; }
}

/* ===== Utility layer =====
   Small, composable classes lifted out of the per-element style="" attributes
   the pages used to carry. Two reasons they live here now: the served CSP is
   the place to keep style, and `just web-check` enforces no-inline-style
   (WCAG H63 / html-validate) across every page. Each rule reproduces its
   original declarations verbatim — this moved the CSS, it did not restyle it.
   Spacing classes are named for their literal value (mt-14 = 1.4rem) so the
   editorial rhythm the pages were authored with stays legible at the callsite
   rather than being rounded into an invented scale.

   Why !important, deliberately and only here: every declaration below used to
   be a style="" attribute, which outranks any selector. Component rules in
   this file are routinely two classes deep (`.doc .callout { margin: 1.5rem 0 }`),
   so a plain single-class utility LOSES to them and the page silently reflows —
   which is exactly what happened on the first pass of this refactor, verified
   by pixel-diffing the pages before and after. !important restores the
   precedence the inline styles had, so these classes mean what they say at
   the callsite. Keep this layer last in the file, keep it to explicit
   per-element overrides, and do not reach for !important above it. */

/* Text tone */
/* Underlined because color alone cannot mark a link inside body copy: primary
   and muted sit near the same luminance on the dark ground (1.07:1), which is
   invisible to low color vision. Same offset as .doc a. */
.t-mint { color: var(--primary) !important; text-decoration: underline; text-underline-offset: 3px; }
.t-muted { color: var(--text-muted) !important; }
/* The small run-in number on a section heading ("01 Product"). Sized in `em`
   so it tracks the heading it sits inside. Deliberately NOT named .kicker —
   that is an existing standalone-label component above, and reusing the name
   would silently restyle every one of them. */
.section-num { color: var(--gold) !important; font-size: 0.6em !important; letter-spacing: 0.2em !important; }

/* Vertical rhythm */
.mt-09 { margin-top: 0.9rem !important; }
.mt-10 { margin-top: 1rem !important; }
.mt-11 { margin-top: 1.1rem !important; }
.mt-12 { margin-top: 1.2rem !important; }
.mt-14 { margin-top: 1.4rem !important; }
.mt-16 { margin-top: 1.6rem !important; }
.mt-18 { margin-top: 1.8rem !important; }
.mt-25 { margin-top: 2.5rem !important; }
.mt-50 { margin-top: 5rem !important; }
/* The one utility named for a token rather than a literal: it IS the section
   boundary, and it must follow --space-section if that value ever moves. Worn
   by a section with a sibling section below it — never by a page's last
   section, whose close is the footer's own margin. */
.pb-section { padding-bottom: var(--space-section) !important; }

/* Section and type scale */
.page-head { padding: var(--space-page-top) 0 var(--space-head-gap) !important; }
.fs-hero { font-size: clamp(2.4rem, 6vw, 4rem) !important; }
.fs-sub { font-size: clamp(1.5rem, 3vw, 2rem) !important; }
/* Width pinned like .lede-wide's, or the section-head intro rule (higher
   specificity than .prose) would silently narrow lede page heads to 48ch. */
.lede { color: var(--text-muted) !important; font-size: 1.15rem !important; max-width: var(--measure) !important; }
.lede-wide { font-size: 1.2rem !important; line-height: 1.6 !important; color: var(--text-muted) !important; max-width: 60ch !important; }
.measure { max-width: var(--measure) !important; }
.sub-centered { color: var(--text-muted) !important; max-width: 46ch !important; margin: 0.8rem auto 1.8rem !important; }

/* Composite blocks */
.panel-narrow { max-width: 560px !important; margin: 2.2rem auto 0 !important; text-align: left !important; }
.cta-panel { padding: 3rem !important; border: 1px solid var(--border-soft) !important; border-radius: 16px !important; background: var(--bg-raised) !important; text-align: center !important; }
.btn-row { display: flex !important; flex-wrap: wrap !important; gap: 0.75rem !important; align-items: center !important; }
.btn-row-center { display: flex !important; gap: 0.9rem !important; justify-content: center !important; flex-wrap: wrap !important; }
.meta-strip { display: flex !important; flex-wrap: wrap !important; gap: 0.6rem 1.4rem !important; padding: 1.4rem 0 3rem !important; border-top: 1px solid var(--border-soft) !important; border-bottom: 1px solid var(--border-soft) !important; font-family: var(--font-mono) !important; font-size: 0.95rem !important; }
/* Section-nav links highlight in the brand coral on hover, keyboard focus, and
   when their section is the one scrolled into view (.active, set by the FAQ
   scroll-spy). !important because .t-muted sets the resting colour with one. */
.meta-strip a { transition: color var(--t) var(--ease); }
.meta-strip a:hover,
.meta-strip a:focus-visible,
.meta-strip a.active { color: var(--primary) !important; }

/* Opt out of the reveal animation for elements that must paint immediately. */
.no-anim { animation: none !important; }

/* ---- Content sits above Beny ----
   The fish and the reef are fixed at z-index 0. The homepage lifts its
   sections to 1; every other page needs the same promise or she swims
   over the article text. Positioning <main> itself covers them all. */
main { position: relative; z-index: 1; }

/* ---- Beny cursor-chaser (signature from coming-soon) ---- */
/* z-index 0: above the background layers, but behind the content/text (sections are z-index 1). */
/* Beny sits a layer behind the reef (z-index 0), so she passes behind the
   coral and the fronds rather than over them. Still above the dot grid. */
/* No drop-shadow. A black 30% shadow under a flat mark does not read as
   depth, it reads as dirt: on the cream ground it fringed her green with
   olive and that was the muddiness. Losing it also lets her own colour come
   through, so she needs less transparency to sit at the same weight. */
/* z-index 0 matches the reef, and she is later in the DOM, so she paints in
   front of the floor and the colonies. Still behind page content, which sits
   at z-index 1. At -1 she swam behind the seabed and vanished into it. */
#cursor-chaser { position: fixed; top: 0; left: 0; z-index: 0; width: 72px; height: 72px; pointer-events: none; opacity: 0.78; will-change: transform; }
/* Beny's reef — her home water, bottom-left. She drifts through the coral
   when idle and swims out to follow the cursor. Open water, so unlike the
   bowl it has no rim and she enters and leaves from any direction. */
#beny-reef {
  /* Pushed below the fold so the floor sits lower and the colony feet are
     cropped by the page edge rather than standing complete on a visible
     mound — a form running off the bottom reads as continuing past it, which
     is what ties the reef to the page instead of leaving it a diorama
     floating in the corner. Only flat seabed fill is lost to the crop.
     One number; tune it here. */
  position: fixed; left: 0; bottom: -8px; z-index: 0;
  width: var(--reef-w); height: auto; aspect-ratio: 240 / 230; pointer-events: none;
  /* Deliberately NOT isolated. `mix-blend-mode` needs a backdrop to blend
     with, and isolating the reef gave the overprint plate a transparent one
     over all the open water — blending against transparency returns the
     source unchanged, so the "overprint" was just a flat teal dot-field
     with a visible edge. Letting it see the page ground is what makes it
     behave like ink. The blend does shift slightly as content scrolls
     behind the reef; that is the cost of a real overprint. */
}
#beny-reef svg { width: 100%; height: 100%; display: block; }
#beny-reef .reef-sun { display: none; }
#beny-reef .overprint { mix-blend-mode: var(--overprint); }
/* Each frond sways from its own base, offset so they don't move in unison.
   transform-box: fill-box is what makes "bottom center" mean the frond's
   own base — without it the origin is the whole SVG viewport and they all
   pivot around one distant point. */
#beny-reef .frond {
  transform-box: fill-box; transform-origin: bottom center;
  animation: reefSway 7s ease-in-out infinite alternate;
}
#beny-reef .frond:nth-child(2) { animation-duration: 9s; animation-delay: -2s; }
#beny-reef .frond:nth-child(3) { animation-duration: 8s; animation-delay: -4s; }
#beny-reef .frond:nth-child(4) { animation-duration: 10s; animation-delay: -1s; }
#beny-reef .frond:nth-child(5) { animation-duration: 8.5s; animation-delay: -3s; }
#beny-reef .frond:nth-child(6) { animation-duration: 11s; animation-delay: -5s; }
#beny-reef .frond:nth-child(7) { animation-duration: 7.5s; animation-delay: -6s; }
#beny-reef .frond:nth-child(8) { animation-duration: 9.5s; animation-delay: -2.5s; }
@keyframes reefSway { from { transform: rotate(-2.2deg); } to { transform: rotate(2.2deg); } }
/* Beny as a live "agent" you can call off, modelled on the signature
   confirmation card (.confirm-card / .cc-head / .cc-btn). Deliberately not
   framed as the kill switch: this parks a cartoon fish, and the real
   `habenula kill` is a governance claim that should not share its name
   with a toy. */
#beny-agent {
  position: fixed; bottom: 1.2rem; right: var(--card-inset); z-index: 1000; width: var(--card-w);
  background: var(--card-bg);
  border: 1px solid var(--border-strong); border-radius: 14px; padding: 0.9rem;
  box-shadow: var(--card-shadow);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
}
#beny-agent .ba-head { display: flex; align-items: center; gap: 0.6rem; padding-bottom: 0.75rem; border-bottom: 1px solid var(--border-strong); }
/* The mark is the one place the card says who the agent IS, and at 18px the
   blowfish read as an abstract dot. 28px in a 40px chip is the floor
   where her silhouette — fins, tail, the round body — survives; the chip
   keeps the same inset ratio so it still reads as a chip, not a sticker. */
#beny-agent .ba-mark { width: 40px; height: 40px; border-radius: 10px; background: var(--chip-bg); display: grid; place-items: center; flex-shrink: 0; }
#beny-agent .ba-mark img { width: 28px; height: 28px; }
#beny-agent .ba-title { font-family: var(--font-sans); font-weight: 600; font-size: 0.85rem; color: var(--text); }
#beny-agent .ba-sub { display: flex; align-items: center; gap: 0.4rem; margin-top: 2px; font-family: var(--font-mono); font-size: 0.85rem; letter-spacing: 0.04em; color: var(--text-dim); }
#beny-agent .ba-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 3px var(--selection); flex-shrink: 0; }
#beny-agent.recalled .ba-dot { background: var(--accent-red); box-shadow: none; }
#beny-recall {
  /* Type and metrics are .cc-btn's, because this card is modelled on the
     confirmation card and was reading in a different voice: sans at 0.82rem
     on 0.6rem padding computed 37.2px against .cc-btn's 44.9px. Behaviour was
     already shared; the box never was. */
  width: 100%; margin-top: 0.8rem; font-family: var(--font-mono); font-weight: 600;
  font-size: 0.85rem; padding: 0.68rem 0.5rem; border-radius: 10px; cursor: pointer;
  /* Explicit, because this is a real <button> and the UA stylesheet gives
     buttons `line-height: normal` rather than letting them inherit. The
     .cc-btn it matches is a <div>, so it picks up the page's 1.65 for free —
     which is the whole 6px that was left after the padding and size matched. */
  line-height: 1.65;
  color: var(--danger-text); background: var(--danger-bg); border: 1px solid var(--danger-line);
  transition: color var(--t) var(--ease), background var(--t) var(--ease),
    border-color var(--t) var(--ease), transform var(--t) var(--ease),
    box-shadow var(--t) var(--ease);
}
/* Same rise, press and clock as every other control (see site.css). */
#beny-recall:hover { color: var(--accent-red); background: var(--danger-bg); border-color: var(--accent-red); transform: translateY(var(--lift)); }
#beny-recall:active { transform: translateY(0) scale(var(--press)); }
/* Recalled → "Release" takes the card's affirmative look (like "For this session"). */
#beny-agent.recalled #beny-recall { color: var(--bg); background: linear-gradient(135deg, var(--accent), var(--accent-dim)); border-color: transparent; }
#beny-agent.recalled #beny-recall:hover { filter: brightness(1.05); }

/* Attention glow on the recall button — a prominent orange pulse that starts
   7s after the homepage loads and runs for 7s (5 × 1.4s), teaching a first-time
   visitor who does not want Beny the way to send her back. Homepage only
   (`body.home`): with no session tracking we cannot know where a visitor first
   landed, so the pattern is taught on the home page — where most first
   encounters happen — and deeper pages stay quiet. Also scoped to the following
   state: the button stops glowing the moment she is recalled, and never starts
   if she was already recalled on a previous visit (the .recalled class is set
   from localStorage before the delay elapses). */
@keyframes beny-recall-attn {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 122, 24, 0); border-color: var(--danger-line); }
  50%      { box-shadow: 0 0 22px 5px rgba(255, 122, 24, 0.85), 0 0 8px 2px rgba(255, 122, 24, 0.95); border-color: #ff7a18; }
}
@keyframes beny-recall-attn-static {
  /* Constant glow held across the 7s window — no fade, no pulse. It appears
     and disappears instantly at the window edges (a box-shadow toggle, not
     movement), so there is no animation for a reduced-motion user to feel. */
  0%, 100% { box-shadow: 0 0 18px 4px rgba(255, 122, 24, 0.8); border-color: #ff7a18; }
}
@media (prefers-reduced-motion: no-preference) {
  body.home #beny-agent:not(.recalled) #beny-recall { animation: beny-recall-attn 1.4s ease-in-out 7s 5; }
}
@media (prefers-reduced-motion: reduce) {
  /* No pulsing for reduced motion — a single fade-in, hold, fade-out over the
     same 7s-to-14s window still points the button out. */
  body.home #beny-agent:not(.recalled) #beny-recall { animation: beny-recall-attn-static 7s ease-in-out 7s 1; }
}

/* Minimise / restore. `ba-head` is a flex row, so the button pushes itself to
   the far end rather than needing absolute positioning. */
#beny-min {
  margin-left: auto; align-self: flex-start; display: grid; place-items: center;
  width: 20px; height: 20px; padding: 0; cursor: pointer;
  background: none; border: 1px solid var(--border-strong); border-radius: 6px;
  color: var(--text-dim);
  transition: color var(--t) var(--ease), border-color var(--t) var(--ease);
}
#beny-min:hover { color: var(--text); border-color: var(--accent); }
#beny-min svg { width: 12px; height: 12px; }
/* The restore handle. Sits exactly where the card's bottom-right corner was, so
   the picture comes back from the point it collapsed into. */
#beny-restore {
  position: fixed; bottom: var(--card-inset); right: var(--card-inset); z-index: 1000; display: none;
  align-items: center; gap: 0.45rem; cursor: pointer;
  font-family: var(--font-mono); font-size: 0.85rem; letter-spacing: 0.08em;
  padding: 0.45rem 0.7rem; border-radius: 999px;
  color: var(--text-dim); background: var(--card-bg);
  border: 1px solid var(--border-strong);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  transition: color var(--t) var(--ease), border-color var(--t) var(--ease);
}
#beny-restore.on { display: inline-flex; }
#beny-restore:hover { color: var(--text); border-color: var(--accent); }
#beny-restore .br-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--text-dim); }

/* An old CRT losing power: the picture collapses to a bright horizontal line,
   the line shrinks to a point, and the phosphor fades. Two animations rather
   than one, because the cursor loop writes Beny's transform every frame —
   animating transform on her would fight it, so she gets the bloom-and-fade
   half only. */
@keyframes crtOff {
  0%   { transform: scale(1, 1);        filter: brightness(1) saturate(1);     opacity: 1; }
  28%  { transform: scale(1.015, 0.07); filter: brightness(2.4) saturate(0.5); opacity: 1; }
  60%  { transform: scale(0.55, 0.025); filter: brightness(3.2) saturate(0.25); opacity: 1; }
  100% { transform: scale(0, 0.02);     filter: brightness(4) saturate(0);     opacity: 0; }
}
@keyframes crtOn {
  0%   { transform: scale(0, 0.02);     filter: brightness(4) saturate(0);     opacity: 0; }
  35%  { transform: scale(0.6, 0.03);   filter: brightness(3) saturate(0.3);   opacity: 1; }
  70%  { transform: scale(1.01, 0.5);   filter: brightness(1.6) saturate(0.7); opacity: 1; }
  100% { transform: scale(1, 1);        filter: brightness(1) saturate(1);     opacity: 1; }
}
@keyframes crtOffFade {
  0%   { filter: brightness(1);   opacity: 0.78; }
  30%  { filter: brightness(2.6); opacity: 0.9; }
  100% { filter: brightness(4);   opacity: 0; }
}
#beny-reef.crt-off, #beny-agent.crt-off {
  transform-origin: center; animation: crtOff 560ms cubic-bezier(0.45, 0, 0.9, 0.25) forwards;
}
#beny-reef.crt-on, #beny-agent.crt-on {
  transform-origin: center; animation: crtOn 460ms cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
}
#cursor-chaser.crt-off { animation: crtOffFade 340ms ease-out forwards; }
.crt-gone { display: none !important; }

/* Beny herself is a pointer feature, so she and her control card go on touch.
   The reef stays: it stops being a pinned corner decoration — which on touch
   would just sit on top of the content — and becomes a block in the flow at the
   foot of the page, above the legal row. Width-capped and centred so it reads as
   a footer flourish rather than a full-bleed panel. */
/* Below 1200 the reef stops being a corner decoration and becomes a block at the
   foot of the page. A fixed 240px decoration cannot stay clear of a centred
   column. It does not need to any more: --reef-w shrinks with the window and
   --container reserves exactly what the reef occupies, so the corner treatment
   holds at every desktop width. This is the touch and phone case only. */
/* Below 1000 the whole interactive Beny system stands down together: the reef
   becomes a block at the foot of the page, the cursor-follower and her controls
   go, and the decorative sun takes her place. One breakpoint for all of it,
   because a reef in the footer while a second Beny still swims the page is two
   Benys. Left-justified on the footer's own left edge rather than centred, so it
   sits on the line like a drawing rather than floating. */
@media (max-width: 1000px), (hover: none), (pointer: coarse) {
  /* Both flanks have left the corners here, so the column must stop reserving
     room for them — held, the reserve is wider than the viewport itself at phone
     widths and the copy collapses to a few dozen pixels. */
  :root { --edge-w: var(--gutter); }
  /* The block spans the footer so the two things in it can go to opposite ends:
     the reef art left-justified at its own width, the sun flush right. Bottom
     margin is zero and the legal row's top margin with it, so the seabed — which
     fills the SVG to its last row — lands on the rule instead of floating above
     it. */
  #beny-reef {
    position: relative; bottom: auto;
    width: 100%; max-width: none; height: auto; aspect-ratio: auto;
    margin: 1.75rem 0 0;
  }
  #beny-reef svg { width: min(300px, 100%); height: auto; }
  #beny-reef .reef-sun {
    display: block; position: absolute; top: 0; right: 0;
    width: clamp(56px, 12vw, 84px); height: auto;
    opacity: 0.85; pointer-events: none;
  }
  #beny-reef + .legal-row { margin-top: 0; }
  #cursor-chaser, #beny-agent, #beny-restore { display: none !important; }
}
@media (prefers-reduced-motion: reduce) {
  #cursor-chaser, #beny-agent, #beny-restore { display: none !important; }
  /* The reef stays and stops moving — the same call this file makes for
     .gradient-bg. Reduced motion asks for less movement, not less artwork. */
  #beny-reef .frond { animation: none !important; }
  /* No CRT collapse — the elements simply go and come back. */
  #beny-reef.crt-off, #beny-agent.crt-off, #beny-reef.crt-on,
  #beny-agent.crt-on, #cursor-chaser.crt-off { animation: none !important; }
}

/* ---- Collapsed-nav menu ----
   Below the collapse breakpoint the text links leave the bar, and there used
   to be nothing in their place — no route anywhere except home and the theme
   toggle. A native <details> disclosure carries the same links: open/close and
   keyboard handling come from the element, navigation closes it by leaving the
   page, and no JavaScript is involved. Hidden wherever the full nav shows. */
.nav-menu { display: none; position: relative; }
@media (max-width: 1180px) { .nav-menu { display: block; } }
.nav-menu summary {
  list-style: none; cursor: pointer;
  font-size: 0.925rem; color: var(--text-muted);
  padding: 0.4rem 0.9rem; border: 1px solid var(--border-strong); border-radius: 999px;
  transition: color var(--t) var(--ease), border-color var(--t) var(--ease);
}
.nav-menu summary::-webkit-details-marker { display: none; }
.nav-menu summary:hover, .nav-menu[open] summary { color: var(--text); border-color: var(--accent); }
.nav-menu-panel {
  position: absolute; right: 0; top: calc(100% + 0.8rem); z-index: 10;
  min-width: 12rem; display: grid; gap: 0.15rem;
  padding: 0.5rem; border-radius: 14px;
  background: var(--header-bg-solid); border: 1px solid var(--border-strong);
  box-shadow: var(--card-shadow);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
}
.nav-menu-panel a { display: block; padding: 0.5rem 0.75rem; border-radius: 8px; }
.nav-menu-panel a:hover { color: var(--text); background: var(--bg-raised); }
/* The Reading group inside the overflow menu: an unlinked label with its links
   indented beneath it, mirroring the wide-screen Reading dropdown. */
.nav-menu-group { display: grid; gap: 0.15rem; }
.nav-menu-group-label {
  display: block; padding: 0.5rem 0.75rem 0.1rem;
  font-size: 0.72rem; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--text-muted);
}
.nav-menu-group a { padding-left: 1.35rem; }

/* ---- "Reading" — the written surfaces (blog, discourse, whitepapers) under one
   wide-screen nav entry. Native <details>, same panel styling as the overflow
   menu. Shown only above the 1180px .hide-sm breakpoint: below it the whole
   dropdown is hidden and its links — still .hide-sm anchors — are carried by the
   ≡ drawer (or the no-JS overflow menu), harvested from the same anchors. */
.nav-reading { position: relative; display: inline-block; }
@media (max-width: 1180px) { .nav-reading { display: none; } }
.nav-reading summary {
  list-style: none; cursor: pointer;
  font-size: 0.925rem; color: var(--text-muted);
  display: inline-flex; align-items: center; gap: 0.34rem;
  transition: color var(--t) var(--ease);
}
.nav-reading summary::-webkit-details-marker { display: none; }
.nav-reading summary::after {
  content: ""; width: 0.4rem; height: 0.4rem;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: translateY(-0.14rem) rotate(45deg);
  transition: transform var(--t) var(--ease);
}
.nav-reading[open] summary::after { transform: translateY(0.06rem) rotate(-135deg); }
.nav-reading summary:hover,
.nav-reading[open] summary,
.nav-reading:has(a[aria-current="page"]) summary { color: var(--text); }
.nav-reading-panel {
  position: absolute; left: 0; top: calc(100% + 0.8rem); z-index: 10;
  min-width: 13rem; display: grid; gap: 0.15rem;
  padding: 0.5rem; border-radius: 14px;
  background: var(--header-bg-solid); border: 1px solid var(--border-strong);
  box-shadow: var(--card-shadow);
  backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
}
.nav-reading-panel a { display: block; padding: 0.5rem 0.75rem; border-radius: 8px; white-space: nowrap; }
.nav-reading-panel a:hover { color: var(--text); background: var(--bg-raised); }

/* Product page — the value-prop list under "What you get". Arrow bullets in the
   accent, left-aligned inside a centred measure. */
.vp-list { list-style: none; display: grid; gap: 1rem; max-width: 62ch; margin: 2rem 0 0; text-align: left; }
.vp-list li { position: relative; padding-left: 1.6rem; color: var(--text-muted); line-height: 1.6; }
.vp-list li::before { content: "→"; position: absolute; left: 0; color: var(--accent); }
.vp-list strong { color: var(--text); }

/* Shared content blocks — the guarantee rows, statement sections, and the
   integration tag list. Historically inlined on the homepage; lifted to the
   shared stylesheet so /product renders them identically. */
.statement.center { text-align: center; }
.statement p.support { color: var(--text-muted); max-width: var(--measure); margin-top: 1.5rem; font-size: 1.12rem; line-height: 1.7; }
.statement.center p.support { margin-left: auto; margin-right: auto; }
.guarantee { display: grid; grid-template-columns: 0.9fr 1.1fr; gap: clamp(1.5rem, 4vw, 4rem); align-items: center; padding: clamp(1.1rem, 2.5vw, 1.8rem) 0; }
.guarantee .g-copy h3 { font-size: 1.5rem; letter-spacing: -0.02em; }
.guarantee .g-copy p { color: var(--text-muted); margin-top: 0.65rem; max-width: 42ch; line-height: 1.65; }
.guarantee .g-copy .kicker { margin-bottom: 0.5rem; }
.g-close { color: var(--text-muted); max-width: var(--measure); margin-top: 2.6rem; font-size: 1.08rem; line-height: 1.7; }
.tags { display: flex; flex-wrap: wrap; gap: 0.7rem; max-width: 700px; }
.tag { display: inline-flex; align-items: center; gap: 0.55rem; font-family: var(--font-mono); font-size: 0.82rem; padding: 0.5rem 1rem; border-radius: 999px; border: 1px solid var(--border-strong); background: var(--bg-raised); color: var(--text-muted); }
.tag::before { content: ""; flex: none; box-sizing: border-box; width: 7px; height: 7px; border-radius: 999px; background: var(--accent); box-shadow: 0 0 5px var(--accent); }
.tags-label { font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-dim); margin: 1.8rem 0 0.8rem; }
.tags-close { color: var(--text-muted); max-width: var(--measure); margin-top: 2.2rem; line-height: 1.7; }
.tags-close a { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: rgba(204, 85, 122, 0.35); transition: color var(--t) var(--ease); }
.tags-close a:hover { color: var(--primary); text-decoration-color: var(--primary); }
@media (max-width: 860px) {
  .guarantee { grid-template-columns: 1fr; gap: 1rem; }
}

/* Homepage "what's built in" teaser — the six guarantees as lay-legible glyph
   cards (no code, no prose); the full detail lives on /product. */
.guarantee-teaser { list-style: none; display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.1rem; margin: 2.6rem 0 0; padding: 0; }
.guarantee-teaser li { display: flex; flex-direction: column; gap: 0.55rem; padding: 1.5rem 1.5rem 1.7rem; border: 1px solid var(--border-soft); border-radius: 14px; background: var(--bg-raised); }
.guarantee-teaser .gt-glyph { color: var(--accent); line-height: 0; }
.guarantee-teaser .gt-glyph svg { width: 32px; height: 32px; display: block; }
.guarantee-teaser .gt-num { font-family: var(--font-mono); font-size: 1.15rem; letter-spacing: 0.1em; color: var(--text-dim); }
.guarantee-teaser h3 { font-size: 1.1rem; letter-spacing: -0.01em; line-height: 1.3; }
.gt-more { text-align: center; margin-top: 2rem; }
@media (max-width: 900px) { .guarantee-teaser { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .guarantee-teaser { grid-template-columns: 1fr; } }

/* End-of-page pointer cards — comparison / roadmap / questions. Mirrors
   .guarantee-teaser's grid and card metrics; each card is a whole-surface link,
   so the "go" line takes the seafoam interactive accent and the card lifts on
   hover. The eyebrow keeps the gold system-label role. */
.end-cards { list-style: none; display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.1rem; margin: 3rem 0 0; padding: 0; }
.end-cards a { display: flex; flex-direction: column; gap: 0.5rem; height: 100%; padding: 1.5rem 1.5rem 1.7rem; border: 1px solid var(--border-soft); border-radius: 14px; background: var(--bg-raised); text-decoration: none; color: var(--text); transition: border-color var(--t) var(--ease), transform var(--t) var(--ease); }
.end-cards a:hover { border-color: var(--accent); transform: translateY(-2px); }
.end-cards .ec-kicker { font-family: var(--font-mono); font-size: 0.82rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--gold); }
.end-cards h3 { font-size: 1.1rem; letter-spacing: -0.01em; line-height: 1.3; }
.end-cards p { color: var(--text-muted); font-size: 0.95rem; line-height: 1.55; flex: 1; margin: 0; }
.end-cards .ec-go { color: var(--accent); font-weight: 500; font-size: 0.9rem; margin-top: 0.2rem; }
.end-cards a:hover .ec-go { text-decoration: underline; text-underline-offset: 3px; }
@media (max-width: 900px) { .end-cards { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .end-cards { grid-template-columns: 1fr; } }

/* Hero scroll affordance — a quiet cue that the page continues past the CTAs,
   so scrolling reads as a way in alongside the buttons. */
.scroll-cue { display: flex; flex-direction: column; align-items: center; gap: 0.35rem; margin-top: 2.6rem; color: var(--text-dim); font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.18em; text-transform: uppercase; }
.scroll-cue svg { width: 20px; height: 20px; }

/* Stacked full-width sections (the /product page) on the shared rhythm: half a
   boundary each side, so adjacent sections sum to one. The page-head and CTA
   carry their own !important padding; the first section after the page-head
   drops its top padding so the head's own bottom padding is the only gap
   there, and the last section drops its bottom padding so the footer's margin
   is the only close. */
.page-stack > section { padding: calc(var(--space-section) / 2) 0; position: relative; z-index: 1; }
.page-stack > .page-head + section { padding-top: 0; }
.page-stack > section:last-child { padding-bottom: 0; }

/* ---- Mobile navigation drawer (injected by nav-menu.js) ----
   The ≡ button takes over exactly where the inline links stop fitting (the 980px
   .hide-sm breakpoint), and slides the same links in from the right. */
#nav-burger {
  display: none;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  color: var(--text);
  cursor: pointer;
  padding: 0.35rem;
  margin: 0;
}
#nav-burger svg { display: block; width: 26px; height: 26px; }
/* The ≡ takes over the whole nav at 1180px — the width at which the inline links
   plus the CTA stop fitting — so nothing (the CTA included) drops without a home. */
@media (max-width: 1180px) { #nav-burger { display: inline-flex; } }

#nav-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 1500;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s ease, visibility 0s 0.25s;
}
#nav-backdrop.open { opacity: 1; visibility: visible; transition: opacity 0.25s ease; }

#nav-drawer {
  position: fixed;
  top: 0;
  right: 0;
  height: 100%;
  width: min(300px, 82vw);
  padding: 1.4rem 1.5rem 2rem;
  background: var(--header-bg-solid); backdrop-filter: var(--glass-blur); -webkit-backdrop-filter: var(--glass-blur);
  border-left: 1px solid var(--border-strong);
  box-shadow: -14px 0 44px rgba(0, 0, 0, 0.3);
  z-index: 1600;
  overflow-y: auto;
  transform: translateX(100%);
  visibility: hidden;
  transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1), visibility 0s 0.28s;
}
#nav-drawer.open { transform: translateX(0); visibility: visible; transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1); }
#nav-drawer ul { list-style: none; margin: 2.75rem 0 0; padding: 0; }
#nav-drawer a {
  display: block;
  padding: 0.95rem 0;
  font-size: 1.05rem;
  color: var(--text);
  text-decoration: none;
  border-bottom: 1px solid var(--border-soft);
  transition: color 0.15s ease;
}
#nav-drawer a:hover { color: var(--accent); }
/* The Reading group inside the drawer: an unlinked label with its links indented
   beneath it, mirroring the header's Reading dropdown. The nested list must not
   re-take the top-level list's 2.75rem clear-the-close-button offset. */
#nav-drawer .nav-drawer-group ul { margin: 0; }
#nav-drawer .nav-drawer-group-label {
  display: block; padding: 0.95rem 0 0.2rem;
  font-size: 0.78rem; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--text-muted);
}
#nav-drawer .nav-drawer-group a { padding-left: 0.9rem; }
.nav-drawer-cta {
  display: block;
  width: 100%;
  margin-top: 1.5rem;
  font: inherit;
  font-size: 0.95rem;
  text-align: center;
  color: var(--accent);
  background: none;
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  padding: 0.85rem 1rem;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.nav-drawer-cta:hover { border-color: var(--accent); }
#nav-drawer-close {
  position: absolute;
  top: 1.1rem;
  right: 1.2rem;
  background: none;
  border: none;
  color: var(--text-muted);
  cursor: pointer;
  padding: 0.3rem;
}
#nav-drawer-close svg { display: block; width: 22px; height: 22px; }
#nav-drawer-close:hover { color: var(--text); }
@media (prefers-reduced-motion: reduce) {
  #nav-drawer, #nav-backdrop { transition: none; }
}
