/* =========================================================================
   Nudox landing — foundation + header
   Aesthetic: cyberpunk · futuristic · digital renaissance
   Palette:  071E3D  ·  1F4287  ·  278EA5  ·  21E6C1
   (1F4287 is brand palette but unused in this sheet, so it carries no token.)
   ========================================================================= */

:root {
  /* core palette */
  --navy: #071e3d;
  --teal: #278ea5;
  --neon: #21e6c1;
  --amber: #ffb020;         /* about page — team clearance accent, person 2 */
  --violet: #7c8cff;        /* about page — team clearance accent, person 3 */
  --magenta: #ff4fa3;       /* about page — team clearance accent, person 4 */
  --red: #ff5470;           /* landing #benefits — the "without Nudox" trajectory */

  /* derived tokens */
  --bg-deep: #040d1c;          /* darker floor for the body gradient */
  --text: #c4dae6;             /* default body / nav text */
  --text-bright: #eafffb;      /* hover / emphasis */
  --ink: #04121f;              /* dark text for use on bright fills */

  --mono: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Code", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  --header-h: 4.5rem;
  --wrap-pad: clamp(1rem, 4vw, 2.5rem);
  --ease: cubic-bezier(.22, 1, .36, 1);

  --constellation-blur: 4px;   /* softness of the site-wide graph backdrop */
}

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

/* The page gradient lives on <html>, not <body>, and this is load-bearing.

   The root element's background is what paints the canvas, and the canvas is
   painted before negative-z-index children — so .constellation (z-index: -2)
   draws on top of it and stays visible. Move this to <body> and the gradient
   becomes an ordinary element background, which paints *after* negative-z-index
   descendants and buries the constellation entirely.

   Keeping it here also means the root has a background from the first frame,
   so no stage of loading or overscroll exposes the white default canvas. */
html {
  -webkit-text-size-adjust: 100%;
  color-scheme: dark;              /* dark scrollbars/controls; see also the meta tag in base.html */
  background:
    radial-gradient(120% 80% at 50% -10%, rgba(31, 66, 135, .35), transparent 60%),
    linear-gradient(180deg, var(--navy) 0%, var(--bg-deep) 100%);
  background-attachment: fixed;
}

body {
  margin: 0;
  min-height: 100vh;
  font-family: "Tenor Sans", system-ui, -apple-system, sans-serif;
  color: var(--text);
}

img { max-width: 100%; display: block; }

/* =========================================================================
   Header — sticky, full width, transparent → frosted glass on scroll

   NOTE: this and `footer` below are bare ELEMENT selectors, so they hit any
   <header>/<footer> anywhere in the document, not just the site chrome. Do not
   use those two element names inside a page or template — a nested <header>
   inherits position:sticky, z-index:1000 and this three-column grid, which
   pins it over the page and lays its children out side by side. Use a div with
   a class (see .post-head / .post-foot in the blog block at the end of this
   file). Left unscoped deliberately: base.html's are the only ones on the site.
   ========================================================================= */
header {
  position: sticky;
  top: 0;
  z-index: 1000;
  display: grid;
  grid-template-columns: 1fr auto 1fr;   /* logo | nav | balancing spacer */
  align-items: center;
  gap: 1rem;
  width: 100%;
  min-height: var(--header-h);
  padding-inline: var(--wrap-pad);
  background: transparent;
  border-bottom: 1px solid transparent;
  transition:
    background .35s var(--ease),
    border-color .35s var(--ease),
    box-shadow .35s var(--ease),
    backdrop-filter .35s var(--ease);
}

body.scrolled header {
  background: rgba(7, 30, 61, .72);
  backdrop-filter: blur(12px) saturate(140%);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
  border-bottom-color: rgba(33, 230, 193, .35);
  box-shadow:
    0 10px 30px -12px rgba(0, 0, 0, .6),
    0 1px 0 rgba(33, 230, 193, .15);      /* thin neon lip */
}

/* --- logo (left) ---------------------------------------------------------- */
header > a[aria-label="Nudox home"] {
  grid-column: 1;
  justify-self: start;
  display: inline-flex;
  align-items: center;
  line-height: 0;
}

header > a[aria-label="Nudox home"] img {
  height: 2.75rem;
  width: auto;
  filter: drop-shadow(0 0 10px rgba(33, 230, 193, .35));
  transition: filter .3s var(--ease), transform .3s var(--ease);
}

header > a[aria-label="Nudox home"]:hover img {
  filter: drop-shadow(0 0 16px rgba(33, 230, 193, .6));
  transform: translateY(-1px);
}

/* --- nav (center) --------------------------------------------------------- */
nav {
  grid-column: 2;
  justify-self: center;
  display: flex;
  align-items: center;
  gap: .5rem;
}

nav a {
  position: relative;
  display: inline-flex;
  align-items: center;
  padding: .5rem 1.1rem;
  font-family: var(--mono);
  font-size: .78rem;
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--text);
  background: transparent;
  border: 1px solid transparent;      /* reserved so hover adds no layout shift */
  border-radius: 2px;                 /* rectangular */
  transition:
    color .25s var(--ease),
    background .25s var(--ease),
    border-color .25s var(--ease),
    box-shadow .25s var(--ease),
    transform .25s var(--ease);
}

/* hover / keyboard-focus → highlight + pop out */
nav a:hover, nav a:focus-visible {
  color: var(--text-bright);
  background: linear-gradient(180deg, rgba(39, 142, 165, .22), rgba(31, 66, 135, .28));
  border-color: var(--neon);
  box-shadow:
    0 10px 24px -8px rgba(33, 230, 193, .55),
    inset 0 0 14px rgba(33, 230, 193, .12);
  transform: translateY(-3px);
  outline: none;
}

nav a:active { transform: translateY(-1px); }

/* right spacer balances the centered nav */
header > div[aria-hidden="true"] { grid-column: 3; }

/* =========================================================================
   Mobile — hamburger toggle (checkbox-driven); hidden on desktop
   ========================================================================= */
#menu-toggle, header > label { display: none; }

@media (max-width: 760px) {
  header {
    grid-template-columns: auto 1fr auto;
    /* on mobile the header is the only chrome around the hamburger target —
       show the frosted fill from first paint instead of waiting for scroll */
    background: rgba(7, 30, 61, .72);
    backdrop-filter: blur(12px) saturate(140%);
    -webkit-backdrop-filter: blur(12px) saturate(140%);
    border-bottom-color: rgba(33, 230, 193, .35);
  }

  /* checkbox stays functional but out of sight */
  #menu-toggle {
    display: block;
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
  }

  /* the label precedes the logo in the DOM, so sparse auto-placement would
     push the logo onto an implicit row 2 — pin both to row 1 */
  header > a[aria-label="Nudox home"] { grid-column: 1; grid-row: 1; }
  header > div[aria-hidden="true"] { display: none; }

  /* hamburger button (right) */
  header > label {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
    display: inline-flex;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: 2.5rem;
    height: 2.5rem;
    padding: .5rem;
    cursor: pointer;
    background: rgba(7, 30, 61, .4);
    border: 1px solid rgba(33, 230, 193, .35);
    border-radius: 2px;
  }

  header > label span {
    display: block;
    width: 100%;
    height: 2px;
    background: var(--neon);
    border-radius: 2px;
    transition: transform .3s var(--ease), opacity .2s var(--ease);
  }

  /* hamburger → X */
  #menu-toggle:checked ~ label span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
  #menu-toggle:checked ~ label span:nth-child(2) { opacity: 0; }
  #menu-toggle:checked ~ label span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

  /* nav collapses into a glass panel hanging off the hamburger */
  nav {
    grid-column: 1 / -1;
    position: absolute;
    top: calc(100% + 1.1rem);   /* clears the header's bottom edge by ~8px */
    left: auto;
    right: 0;
    /* base rule centres the nav; anchor it to the icon's edge instead */
    justify-self: end;
    min-width: 11rem;
    max-width: calc(100vw - 2 * var(--wrap-pad));
    flex-direction: column;
    align-items: stretch;
    gap: .35rem;
    padding: .5rem;
    background: rgba(7, 30, 61, .92);
    backdrop-filter: blur(14px) saturate(140%);
    -webkit-backdrop-filter: blur(14px) saturate(140%);
    border: 1px solid rgba(33, 230, 193, .35);
    border-radius: 2px;
    box-shadow: 0 20px 40px -16px rgba(0, 0, 0, .7);
    transform-origin: top right;
    transform: translateY(-8px) scale(.96);
    opacity: 0;
    visibility: hidden;
    transition:
      opacity .25s var(--ease),
      transform .25s var(--ease),
      visibility .25s var(--ease);
  }

  #menu-toggle:checked ~ nav {
    transform: translateY(0) scale(1);
    opacity: 1;
    visibility: visible;
  }

  nav a { justify-content: center; padding: .8rem 1rem; }

  /* no lift inside the dropdown — the fill carries the hover state */
  nav a:hover, nav a:focus-visible { transform: none; }
}

/* =========================================================================
   Hero — two variants, of which only one is currently in use:
   · flow (#hero.flow-hero) — the landing page, and the only page with a hero
     at all: a centred diagram wired top to bottom. Its block lives further
     down this file.
   · editorial (#hero:not(.flow-hero)) — copy pinned hard-left, constellation
     breathing in the open space on the right. This was the blog index's
     opening; the blog now starts on its own title like every other page, so
     nothing selects this today. Kept because it is the one ready-made variant
     for any page that wants a text hero back.
   Every other page starts on its first real section, which carries the page
   title itself.
   Background stack (back → front): .constellation (-2, fixed site-wide
   backdrop — markup lives in templates/base.html, not this page's content)
   · ::before veil (-1, local to #hero) · content (0)
   ========================================================================= */
#hero {
  position: relative;
  overflow: hidden;
  isolation: isolate;                          /* keep the backdrop layers contained */
  margin-top: calc(-1 * var(--header-h));      /* rise up behind the transparent header */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  gap: 0;                                       /* rhythm carried by element margins */
}

/* --- editorial hero (blog) ------------------------------------------------ */
#hero:not(.flow-hero) {
  align-items: flex-start;                     /* pin the content column hard-left */
  justify-content: center;
  padding: calc(var(--header-h) + 4vh) var(--wrap-pad) 8vh clamp(var(--wrap-pad), 7vw, 6rem);
  text-align: left;
}

/* readability veil — dark on the left behind the copy, clearing to the right
   so the constellation stays bright in the open space */
#hero:not(.flow-hero)::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(100deg,
    rgba(4, 13, 28, .85) 0%,
    rgba(4, 13, 28, .6) 30%,
    rgba(4, 13, 28, .15) 55%,
    transparent 72%);
}

/* =========================================================================
   Constellation — pure CSS/SVG graph (no JS). Markup lives in
   templates/base.html as a direct child of <body> so it renders fixed
   behind every page, unclipped by #hero's overflow:hidden.

   Static backdrop: no motion at all. Every element rests at the midpoint
   of the drift/breathe/twinkle/pulse cycles it used to run, and the whole
   field is blurred by --constellation-blur so it reads as texture behind
   the copy rather than as a diagram competing with it. Opacities are
   raised from the old animated values to pay back what the blur spreads.
   ========================================================================= */
.constellation {
  position: fixed;                              /* site-wide backdrop, not confined to #hero */
  inset: -6%;                                  /* oversize so the blur never bares an edge */
  z-index: -2;
  width: 112%;
  height: 112%;
  pointer-events: none;
  opacity: .9;
  transform: scale(1.04);                       /* held at the middle of the old drift */
  filter: blur(var(--constellation-blur));
}

/* edges — thin teal links, held at the peak of the old breathe cycle.
   Shared with the #what-is-nudox graph, which reuses these coordinates. */
.constellation .edges line,
.wn-links line {
  stroke: var(--teal);
  stroke-width: 1.4;
  opacity: .38;
}

/* nodes — soft neon dots */
.constellation .nodes circle:not(.hub) {
  fill: var(--neon);
  opacity: .7;
}

/* hubs — brighter "verified" nodes, glow held at the middle of the old pulse */
.constellation .nodes circle.hub {
  fill: var(--neon);
  opacity: 1;
  filter: drop-shadow(0 0 6px var(--neon));
}

/* --- eyebrow — mono "compiler" voice: neon tick + label + wire into graph -- */
.eyebrow {
  display: flex;
  align-items: center;
  gap: .85rem;
  margin: 0 0 1.15rem;
  width: min(34rem, 62%);
  font-family: var(--mono);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--teal);
}

/* neon tick anchoring the label */
.eyebrow::before {
  content: "";
  flex: none;
  width: 2px;
  height: 1.05em;
  background: linear-gradient(180deg, var(--neon), var(--teal));
  box-shadow: 0 0 10px rgba(33, 230, 193, .6);
}

.eyebrow span { white-space: nowrap; }

/* hairline "wire" running right from the label toward the graph, fading out */
.eyebrow::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, rgba(39, 142, 165, .7), transparent);
}

/* --- title (Truth in gradient) — Tenor Sans display, left, deliberate ragged */
#hero h1 {
  margin: 0 0 1.5rem;
  max-width: 14ch;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: clamp(2.9rem, 7vw, 5.5rem);
  line-height: 1.06;
  letter-spacing: -0.01em;
  color: var(--text-bright);
}

#hero h1 span,
#token-savings h2 span,
#blindspot > h1 span,
#quickstart-steps > div > h2 span,
#contact-form > h1 span, #legal > h1 span, #pricing-grid > h1 span,
#about-background > h1 span, #blog-index > h1 span {
  background: linear-gradient(120deg, var(--teal) 0%, var(--neon) 60%, #7ffff0 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

/* --- content column (description + buttons) ------------------------------- */
#hero:not(.flow-hero) > div {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1.75rem;
}

/* description */
#hero h2 {
  margin: 0;
  max-width: 46ch;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: clamp(1rem, 1.5vw, 1.2rem);
  line-height: 1.65;
  color: var(--text);
}

/* button row */
#hero:not(.flow-hero) > div > div {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: 1rem;
}

/* editorial hero on small screens — stay left, veil goes vertical for contrast */
@media (max-width: 760px) {
  #hero:not(.flow-hero) { padding-top: calc(var(--header-h) + 6vh); }
  #hero:not(.flow-hero)::before {
    background: linear-gradient(180deg,
      rgba(4, 13, 28, .35) 0%,
      rgba(4, 13, 28, .6) 55%,
      rgba(4, 13, 28, .85) 100%);
  }
  #hero h1 { max-width: 16ch; }
  .eyebrow { width: 100%; }
}

/* =========================================================================
   Flow hero (landing) — the page opens as the thing it sells: one query
   descending a graph and fanning out across it. Title → premise → a bus
   carrying four claims → three ways in, every one of them on the same net.

   Geometry contract — every wire is horizontal or vertical, every corner is
   90°, and none of it is SVG. That is the point of the layout: an SVG
   stretched with preserveAspectRatio="none" distorts whatever is drawn in
   it, which is why the old diagonal fan had to fake its vertical wires as
   CSS gradients and its round dots as DOM. Axis-aligned rules have no such
   problem — a 1px rule is 1px at any viewport — so the diagram is now built
   entirely from divs and pseudo-elements, and every junction is exact by
   construction rather than by hand-tuned coordinates:

   · .hf-band is a 2-column grid. Each box is width:min(100%, --hf-box),
     justify-self start (col 1) / end (col 2), so every box centre sits
     exactly --hf-box / 2 in from the band's edge. .hf-bus carries that same
     token as margin-inline, so the bus ends land dead on the box centres.
     (--hf-box maxes out at 14.5rem, well under half the band at the 860px
     breakpoint, so a box never fills its column and shifts its own centre.)
   · The band's middle row is 1px tall with row-gap --hf-stub either side, so
     the bus sits exactly --hf-stub below the upper row and above the lower
     row. Each box's tap is a pseudo-element of exactly that height, so they
     meet whatever the copy does to the boxes' heights.
   · .hf-ctas has padding-top:--hf-stub with the button bus on its ::before
     at top:0, and each .hf-branch::after rises --hf-stub from bottom:100%.
     Same trick, same token.
   · .hf-spine is absolute at left:50% of the band, and .hf-ctas is 3×1fr —
     so the spine, the middle button, and the button bus share one x.

   Timing: one --hf-cycle drives the whole net. A packet leaves the title and
   runs the trunk (0–20%), crosses the band (20–34%), the junction flares
   (34%), two packets run the bus out to both ends (36–56%), the four claims
   light as the taps land (58%), the second trunk carries it down (60–76%),
   and the three landing pads flash (84%). One query fanning out — not seven
   separate signals.
   ========================================================================= */
#hero.flow-hero {
  --hf-cycle: 6.4s;
  --hf-band: 66rem;                          /* outer width of the claim band */
  --hf-box: clamp(10rem, 16vw, 15rem);       /* claim width — the bus reads this */
  --hf-stub: clamp(1.5rem, 3.2vh, 2.4rem);   /* every box↔bus and bus↔button run */
  --hf-stage: 60rem;                         /* button row width */
  --hf-wire: rgba(39, 142, 165, .85);
  --hf-dot: 5px;
  align-items: center;
  /* `safe` matters: on a viewport too short for the diagram this falls back to
     flex-start, so the title can never be centred up under the header and
     clipped by #hero's overflow:hidden. Browsers that don't parse it drop the
     declaration and keep flex-start, which is the same safe answer. */
  justify-content: safe center;
  padding: calc(var(--header-h) + 3vh) var(--wrap-pad) clamp(2rem, 5vh, 3.5rem);
  text-align: center;
}

/* centred readability wash — the editorial hero ramps dark to the left
   because its copy is pinned there; this one is dark under the diagram and
   lets the constellation breathe at the edges. Wider than the fan's was,
   because the claim band now reaches most of the way across. */
#hero.flow-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(68% 56% at 50% 46%,
      rgba(4, 13, 28, .93) 0%,
      rgba(4, 13, 28, .78) 52%,
      rgba(4, 13, 28, .35) 80%,
      transparent 100%),
    linear-gradient(180deg, rgba(4, 13, 28, .55), transparent 40%, rgba(4, 13, 28, .6));
}

/* eyebrow — centred, so the editorial hero's single trailing wire becomes a
   pair and the label reads as a tap on the wire rather than the start of it */
#hero.flow-hero .eyebrow {
  width: min(100%, 34rem);
  justify-content: center;
  margin-bottom: 1.3rem;
}

#hero.flow-hero .eyebrow::before {
  flex: 1;
  width: auto;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(39, 142, 165, .7));
  box-shadow: none;
}

#hero.flow-hero h1 { margin: 0 0 1.05rem; max-width: 18ch; }

/* the premise — the sentence under the title, not a component. It used to
   sit in a bordered box with corner ticks, which made the middle of the
   hero read as one heavy slab; the wiring carries the structure now. */
#hero.flow-hero .hf-lede {
  margin: 0;
  max-width: 46ch;
  text-wrap: balance;                        /* no orphaned last word under a centred lede */
  font-family: "Tenor Sans", var(--sans);
  font-size: clamp(1rem, 1.4vw, 1.15rem);
  line-height: 1.65;
  color: var(--text);
}

/* --- the wire kit --------------------------------------------------------
   Two primitives sharing one 18px dash period (8px on, 10px off): vertical
   runs are 1px wide on a 180deg gradient, horizontal runs 1px tall on a
   90deg one. Each ants keyframe travels exactly one period, so the loop
   seam never shows. */
#hero.flow-hero .hf-trunk, #hero.flow-hero .hf-spine,
#hero.flow-hero .hf-box::before, #hero.flow-hero .hf-branch::after {
  width: 1px;
  background-image: repeating-linear-gradient(180deg, var(--hf-wire) 0 8px, transparent 8px 18px);
  background-size: 1px 18px;
  animation: hf-ants-y 1.5s linear infinite;
}

#hero.flow-hero .hf-bus, #hero.flow-hero .hf-ctas::before {
  height: 1px;
  background-image: repeating-linear-gradient(90deg, var(--hf-wire) 0 8px, transparent 8px 18px);
  background-size: 18px 1px;
  animation: hf-ants-x 1.5s linear infinite;
}

/* solder joint — a dot marks a tee (a third wire joining) or a terminus (a
   wire starting at the title, or ending on a button), never a plain corner
   where one run simply turns. The negative margin is half the dot, so
   `left`/`top` name the joint's centre rather than its top-left. */
#hero.flow-hero .hf-trunk::before, #hero.flow-hero .hf-bus::before,
#hero.flow-hero .hf-bus::after, #hero.flow-hero .hf-j,
#hero.flow-hero .hf-ctas::after, #hero.flow-hero .hf-branch::before {
  content: "";
  position: absolute;
  width: var(--hf-dot);
  height: var(--hf-dot);
  margin: calc(var(--hf-dot) / -2) 0 0 calc(var(--hf-dot) / -2);
  border-radius: 50%;
  background: var(--neon);
  box-shadow: 0 0 8px rgba(33, 230, 193, .7);
}

/* travelling packets — a lit segment on the wire, parked out of frame
   between passes so the wrap back to the start is never visible */
#hero.flow-hero .hf-trunk::after, #hero.flow-hero .hf-spine::after {
  content: "";
  position: absolute;
  left: 0;
  width: 1px;
  height: 26px;
  background: linear-gradient(180deg, transparent, var(--neon));
  box-shadow: 0 0 10px rgba(33, 230, 193, .85);
  opacity: 0;
}

/* --- the trunks ----------------------------------------------------------
   Two of them: title → band, then band → buttons. Their height is capped
   rather than flexed: letting them absorb the viewport's slack turned them
   into 150px of empty dashed line on a 1080-tall screen, which is the very
   emptiness this hero was rebuilt to get rid of. The diagram keeps its own
   proportions and the leftover height becomes margin around it instead. */
#hero.flow-hero .hf-trunk {
  position: relative;
  flex: none;
  height: clamp(2rem, 5vh, 4.5rem);
}

#hero.flow-hero .hf-trunk::before { left: 50%; top: 0; }
#hero.flow-hero .hf-trunk::after { animation: hf-packet-a var(--hf-cycle) linear infinite; }

/* the second trunk is the only .hf-trunk that follows the band */
#hero.flow-hero .hf-band ~ .hf-trunk::after { animation-name: hf-packet-b; }

/* --- the claim band ------------------------------------------------------ */
#hero.flow-hero .hf-band {
  position: relative;
  flex: none;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  grid-template-rows: auto 1px auto;
  row-gap: var(--hf-stub);
  align-content: center;
  width: min(100%, var(--hf-band));
}

#hero.flow-hero .hf-nw { grid-area: 1 / 1; justify-self: start; }
#hero.flow-hero .hf-ne { grid-area: 1 / 2; justify-self: end; }
#hero.flow-hero .hf-sw { grid-area: 3 / 1; justify-self: start; }
#hero.flow-hero .hf-se { grid-area: 3 / 2; justify-self: end; }

/* the query passes straight through the band and on into the second trunk */
#hero.flow-hero .hf-spine {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  margin-left: -.5px;
}

#hero.flow-hero .hf-spine::after { animation: hf-packet-spine var(--hf-cycle) linear infinite; }

/* --- the bus -------------------------------------------------------------
   One horizontal run spanning both columns, pulled in by half a box at each
   end so it terminates on the box centres. Three wires meet at each end —
   bus, tap up, tap down — hence a joint there. */
#hero.flow-hero .hf-bus {
  position: relative;
  grid-area: 2 / 1 / 3 / 3;
  align-self: center;
  margin-inline: calc(var(--hf-box) / 2);
}

#hero.flow-hero .hf-bus::before { left: 0; top: 0; }
#hero.flow-hero .hf-bus::after { left: 100%; top: 0; }

/* where the spine crosses the bus — the one junction the whole net turns on */
#hero.flow-hero .hf-j {
  left: 50%;
  top: 0;
  animation: hf-joint-lit var(--hf-cycle) linear infinite;
}

#hero.flow-hero .hf-p {
  position: absolute;
  top: 0;
  width: 30px;
  height: 1px;
  box-shadow: 0 0 10px rgba(33, 230, 193, .85);
  opacity: 0;
}

#hero.flow-hero .hf-p-l {
  right: 50%;
  background: linear-gradient(90deg, var(--neon), transparent);
  animation: hf-packet-bus-l var(--hf-cycle) linear infinite;
}

#hero.flow-hero .hf-p-r {
  left: 50%;
  background: linear-gradient(90deg, transparent, var(--neon));
  animation: hf-packet-bus-r var(--hf-cycle) linear infinite;
}

/* --- the four claims ----------------------------------------------------- */
#hero.flow-hero .hf-box {
  position: relative;
  width: min(100%, var(--hf-box));
  padding: clamp(.7rem, 1.5vh, 1rem) clamp(.8rem, 1.3vw, 1.15rem);
  text-align: left;
  background: rgba(7, 30, 61, .5);
  border: 1px solid rgba(39, 142, 165, .45);
  border-radius: 2px;
  animation: hf-box-lit var(--hf-cycle) linear infinite;
}

/* the tap. Exactly --hf-stub tall, which is exactly the gap the band leaves
   between this row and the bus — so it lands without a coordinate anywhere. */
#hero.flow-hero .hf-box::before {
  content: "";
  position: absolute;
  left: 50%;
  margin-left: -.5px;
  height: var(--hf-stub);
}

#hero.flow-hero .hf-nw::before, #hero.flow-hero .hf-ne::before { top: 100%; }
#hero.flow-hero .hf-sw::before, #hero.flow-hero .hf-se::before { bottom: 100%; }

#hero.flow-hero .hf-box-k {
  margin: 0 0 .3rem;
  font-family: var(--mono);
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .01em;
  color: var(--text-bright);
}

#hero.flow-hero .hf-box-t {
  margin: 0;
  font-family: "Tenor Sans", var(--sans);
  font-size: .85rem;
  line-height: 1.5;
  color: var(--text);
}

#hero.flow-hero .hf-box-t code {
  font-family: var(--mono);
  font-size: .92em;
  color: var(--neon);
}

/* --- the three ways in ---------------------------------------------------
   No column gap, deliberately: a gap narrows the tracks without moving the
   stage, which slides the outer button centres off the 1/6 and 5/6 marks the
   bus is drawn to. The air between the buttons comes from the columns being
   wider than the buttons they centre. */
#hero.flow-hero .hf-ctas {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  align-items: start;
  width: min(100%, var(--hf-stage));
  padding-top: var(--hf-stub);
  gap: 0;
}

/* the button bus — ends on the outer buttons' centres, 1/6 and 5/6 */
#hero.flow-hero .hf-ctas::before {
  content: "";
  position: absolute;
  top: 0;
  left: 16.6667%;
  right: 16.6667%;
}

/* where the second trunk tees into the button bus — the same joint the spine
   makes on the claim bus, so it gets the same dot */
#hero.flow-hero .hf-ctas::after { left: 50%; top: 0; }

#hero.flow-hero .hf-branch { position: relative; display: flex; justify-content: center; }

/* the drop from the bus onto this button */
#hero.flow-hero .hf-branch::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 100%;
  height: var(--hf-stub);
  margin-left: -.5px;
}

/* landing pad — the wire ends here. The pad stays put when the button lifts
   on hover, so the joint reads as soldered rather than broken. */
#hero.flow-hero .hf-branch::before {
  left: 50%;
  top: 0;
  opacity: .4;
  animation: hf-pad-lit var(--hf-cycle) linear infinite;
}

@keyframes hf-ants-y { to { background-position-y: 18px; } }
@keyframes hf-ants-x { to { background-position-x: 18px; } }

@keyframes hf-packet-a {
  0% { top: -26px; opacity: 0; }
  3% { opacity: 1; }
  17% { opacity: 1; }
  20%, 100% { top: 100%; opacity: 0; }
}

@keyframes hf-packet-spine {
  0%, 20% { top: -26px; opacity: 0; }
  23% { opacity: 1; }
  31% { opacity: 1; }
  34%, 100% { top: 100%; opacity: 0; }
}

@keyframes hf-packet-b {
  0%, 60% { top: -26px; opacity: 0; }
  63% { opacity: 1; }
  73% { opacity: 1; }
  76%, 100% { top: 100%; opacity: 0; }
}

@keyframes hf-packet-bus-l {
  0%, 36% { right: 50%; opacity: 0; }
  40% { opacity: 1; }
  52% { opacity: 1; }
  56%, 100% { right: 100%; opacity: 0; }
}

@keyframes hf-packet-bus-r {
  0%, 36% { left: 50%; opacity: 0; }
  40% { opacity: 1; }
  52% { opacity: 1; }
  56%, 100% { left: 100%; opacity: 0; }
}

@keyframes hf-joint-lit {
  0%, 30%, 42%, 100% { transform: scale(1); box-shadow: 0 0 8px rgba(33, 230, 193, .7); }
  34% { transform: scale(1.7); box-shadow: 0 0 16px rgba(33, 230, 193, .95); }
}

@keyframes hf-box-lit {
  0%, 52%, 70%, 100% {
    border-color: rgba(39, 142, 165, .45);
    box-shadow: inset 0 0 30px rgba(33, 230, 193, .04);
  }
  58% {
    border-color: rgba(33, 230, 193, .7);
    box-shadow: inset 0 0 46px rgba(33, 230, 193, .13);
  }
}

@keyframes hf-pad-lit {
  0%, 78%, 92%, 100% { opacity: .4; transform: scale(1); box-shadow: none; }
  84% { opacity: 1; transform: scale(1.5); box-shadow: 0 0 12px rgba(33, 230, 193, .9); }
}

/* Short viewports — the whole point of the hero is that the query reaches the
   buttons on one screen, so on a laptop-height window the type gives ground
   rather than the diagram: the wiring keeps its --hf-stub runs, the title and
   the premise shrink. Scoped above the stacking breakpoint, where the hero is
   allowed to be taller than the viewport anyway. */
@media (min-width: 861px) and (max-height: 820px) {
  #hero.flow-hero { padding-block: calc(var(--header-h) + 2vh) 1.5rem; }
  #hero.flow-hero .eyebrow { margin-bottom: .8rem; }
  #hero.flow-hero h1 { margin-bottom: .7rem; font-size: clamp(2.4rem, 9.5vh, 4rem); }
  #hero.flow-hero .hf-lede { max-width: 62ch; font-size: 1rem; }
}

/* Below this the band cannot flank anything, so the whole net straightens
   into one spine: claims chained top to bottom, then the buttons on the same
   wire. Every connector is still the same --hf-stub run — only the bus and
   the spine, which have nothing left to span, drop out. */
@media (max-width: 860px) {
  #hero.flow-hero { padding-top: calc(var(--header-h) + 5vh); }
  #hero.flow-hero h1 { max-width: 15ch; }

  #hero.flow-hero .hf-band {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
    grid-auto-flow: row;
    width: min(100%, 24rem);
  }

  #hero.flow-hero .hf-bus, #hero.flow-hero .hf-spine { display: none; }

  #hero.flow-hero .hf-nw, #hero.flow-hero .hf-ne,
  #hero.flow-hero .hf-sw, #hero.flow-hero .hf-se {
    grid-area: auto;
    justify-self: stretch;
    width: 100%;
  }

  /* every claim now hangs off the wire above it */
  #hero.flow-hero .hf-nw::before, #hero.flow-hero .hf-ne::before,
  #hero.flow-hero .hf-sw::before, #hero.flow-hero .hf-se::before {
    top: auto;
    bottom: 100%;
  }

  #hero.flow-hero .hf-ctas {
    grid-template-columns: 1fr;
    justify-items: center;
    width: min(100%, 24rem);
    row-gap: var(--hf-stub);
  }

  #hero.flow-hero .hf-ctas::before { display: none; }
}

/* the diagram still reads with the motion off: dashes stay drawn, packets go */
@media (prefers-reduced-motion: reduce) {
  #hero.flow-hero .hf-trunk, #hero.flow-hero .hf-spine, #hero.flow-hero .hf-bus,
  #hero.flow-hero .hf-box, #hero.flow-hero .hf-box::before, #hero.flow-hero .hf-j,
  #hero.flow-hero .hf-ctas::before, #hero.flow-hero .hf-branch::before,
  #hero.flow-hero .hf-branch::after { animation: none; }

  #hero.flow-hero .hf-trunk::after, #hero.flow-hero .hf-spine::after,
  #hero.flow-hero .hf-p { display: none; }
}

/* =========================================================================
   Buttons — rectangular, with an inset outline tracing the border
   Applies to <button> (primary) and <a role="button"> (secondary)
   ========================================================================= */
button, a[role="button"] {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: .95rem 2.2rem;
  font-family: var(--mono);
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--text-bright);
  background: transparent;
  border: 1px solid var(--teal);
  border-radius: 2px;                          /* rectangular */
  cursor: pointer;
  transition:
    color .25s var(--ease),
    background .25s var(--ease),
    border-color .25s var(--ease),
    box-shadow .25s var(--ease),
    transform .25s var(--ease);
}

/* the inner rectangle that traces along the border */
button::before, a[role="button"]::before {
  content: "";
  position: absolute;
  inset: 4px;
  border: 1px solid currentColor;
  border-radius: 1px;
  opacity: .35;
  pointer-events: none;
  transition: opacity .25s var(--ease);
}

/* keep the label above the inset frame */
button > span, a[role="button"] > span { position: relative; z-index: 1; }

/* shared pop-out on hover / focus */
button:hover, a[role="button"]:hover,
button:focus-visible, a[role="button"]:focus-visible {
  transform: translateY(-3px);
  outline: none;
}
button:hover::before, a[role="button"]:hover::before,
button:focus-visible::before, a[role="button"]:focus-visible::before {
  opacity: .6;
}
button:active, a[role="button"]:active { transform: translateY(-1px); }

/* primary (highlighted) — the <button> */
button {
  color: var(--ink);
  background: linear-gradient(180deg, var(--neon), var(--teal));
  border-color: var(--neon);
  box-shadow:
    0 0 0 1px rgba(33, 230, 193, .25),
    0 10px 30px -8px rgba(33, 230, 193, .5);
}
button::before { border-color: rgba(4, 18, 31, .5); opacity: .5; }
button:hover, button:focus-visible {
  box-shadow:
    0 0 0 1px rgba(33, 230, 193, .5),
    0 16px 42px -8px rgba(33, 230, 193, .7);
}

/* secondary (ghost) — the <a role="button"> */
a[role="button"] {
  color: var(--text-bright);
  background: rgba(7, 30, 61, .35);
  border-color: rgba(39, 142, 165, .8);
}
a[role="button"]:hover, a[role="button"]:focus-visible {
  border-color: var(--neon);
  background: linear-gradient(180deg, rgba(39, 142, 165, .22), rgba(31, 66, 135, .3));
  box-shadow: 0 12px 32px -8px rgba(33, 230, 193, .45);
}

/* =========================================================================
   Motion preferences
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; }
  /* .constellation needs no rule here — it is static for everyone now. */
}

/* =========================================================================
   Shared section furniture
   Every full-width section on the site opens the same way: the same vertical
   rhythm against the page gutter, and a centered Tenor Sans title. These were
   eleven and nine near-identical copies respectively; they live here once, and
   the sections below carry only what they genuinely do differently.
   ========================================================================= */
#why, #what-is-nudox, #how-it-works, #benefits, #comparison, #integrations,
#faq, #blindspot, #enterprise-capabilities, #enterprise-integration,
#hosting, #pilot, #analytics, #blog-index {
  padding: clamp(4rem, 9vh, 7rem) var(--wrap-pad);
}

#why > h1, #what-is-nudox > h1, #how-it-works > h1, #benefits > h1,
#comparison > h1, #integrations > h1, #faq > h1, #blindspot > h1,
#enterprise-capabilities > h1, #enterprise-integration > h1, #hosting > h1,
#analytics > h1, #features-grid > h1, #compiler-truth-graph > h1,
#contact-form > h1, #legal > h1, #pricing-grid > h1,
#about-background > h1, #blog-index > h1 {
  margin: 0 auto clamp(2rem, 4vh, 3rem);
  text-align: center;
  max-width: 22ch;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: clamp(1.9rem, 4vw, 3rem);
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--text-bright);
}

/* the four deviations, each for a reason */
#what-is-nudox {
  padding-inline: 0;                  /* the only full-bleed section on the site */
}
#what-is-nudox > h1 {
  margin-bottom: 1rem;
  padding-inline: var(--wrap-pad);    /* the title keeps the gutter the stage gives up */
}
#benefits > h1 { margin-bottom: clamp(1rem, 2vh, 1.5rem); }
#integrations > h1 { margin-bottom: clamp(1.5rem, 3vh, 2rem); }
/* the grid's own gap supplies the space under this one */
#features-grid > h1 { grid-column: 1 / -1; margin-bottom: 0; }
#blindspot > h1, #enterprise-capabilities > h1,
#enterprise-integration > h1, #hosting > h1 { max-width: 24ch; }

/* The sentence under a section title. Quickstart sits it under its masthead
   h2; the pages that used to open with a hero sit it under the title their
   first section now carries. */
.section-lede {
  margin: 0 auto clamp(2.5rem, 5vh, 4rem);
  max-width: 52ch;
  text-align: center;
  font-family: "Tenor Sans", var(--sans);
  font-size: 1.05rem;
  line-height: 1.65;
  color: var(--text);
  opacity: .85;
}

/* the legal pages follow their lede with a revision stamp, so that lede gives
   up its own bottom gap to the stamp */
#legal > .section-lede { margin-bottom: .75rem; }
.section-updated {
  margin: 0 auto clamp(2.5rem, 5vh, 4rem);
  text-align: center;
  font-family: var(--mono);
  font-size: .78rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text);
  opacity: .55;
}

/* =========================================================================
   "Current state of the world" (#why) — two stylized, hand-drawn SVG charts
   Chart A: model-performance bell curve · Chart B: three exponential minis
   Motion is pure CSS/SVG (draw-in · node pulse · travelling data pulse) — no JS
   Renders on the about page, between #about-background and #about-mission:
   it states the problem the mission section then responds to.
   ========================================================================= */
/* two panels, side by side → stacked on narrow */
#why > div {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(1.25rem, 3vw, 2rem);
  align-items: stretch;
}

/* glass panel per figure */
#why figure {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  padding: clamp(1.25rem, 2.5vw, 2rem);
  background: rgba(7, 30, 61, .92);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .05),
    0 20px 40px -24px rgba(0, 0, 0, .6);
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}
#why figure:hover {
  border-color: rgba(33, 230, 193, .6);
  box-shadow:
    inset 0 0 55px rgba(33, 230, 193, .09),
    0 24px 48px -22px rgba(0, 0, 0, .7);
}

#why h3 {
  margin: 0;
  font-family: var(--mono);
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--neon);
}

#why figcaption,
#how-it-works figcaption {
  margin: 0;
  font-family: "Tenor Sans", var(--sans);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--text);
  opacity: .85;
}

/* --- chart primitives, shared site-wide -----------------------------------
   These began as per-section copies inside #why, #how-it-works, #benefits,
   #blindspot and .topo. They are one grammar, so they are declared once here;
   each section below overrides only where it genuinely differs. */
#why .chart, #how-it-works .chart, .bn-chart, .an-chart, #blindspot .chart, .topo { display: block; width: 100%; height: auto; overflow: visible; }

/* left chart grows to fill the panel, matching the right side's height */
#why .perf { flex: 1 1 auto; min-height: clamp(180px, 26vh, 300px); }

#why .grid, .bn-grid { fill: none; stroke: var(--teal); stroke-width: 1; opacity: .12; }
#why .axis-line, .bn-axis { fill: none; stroke: var(--teal); stroke-width: 1.25; opacity: .45; }

#why .line {
  fill: none;
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  filter: drop-shadow(0 0 4px rgba(33, 230, 193, .55));
}
#why .perf .line { stroke: url(#perfLine); }
#why .spark-svg .line { stroke: var(--neon); stroke-width: 2.25; }

#why .area { stroke: none; }
#why .perf .area { fill: url(#perfArea); }
#why .spark-svg .area { fill: rgba(33, 230, 193, .10); }

#why .node { fill: var(--bg-deep); stroke: var(--neon); stroke-width: 2.5; }
#why .node.peak { stroke-width: 3; filter: drop-shadow(0 0 6px var(--neon)); }

#why .pulse { fill: #bffff4; visibility: hidden; filter: drop-shadow(0 0 5px var(--neon)); }
@supports (offset-path: path("M0 0 L1 1")) {
  #why .pulse { visibility: visible; }
}

#why .zone { fill: rgba(33, 230, 193, .06); }
#why .zone-line { fill: none; stroke: var(--neon); stroke-width: 1.5; stroke-dasharray: 6 5; opacity: .7; }
#why .zone-label, .bn-marker text { fill: var(--neon); font-family: var(--mono); font-size: 12px; font-weight: 700; letter-spacing: 2px; }

#why .tick text, .bn-tick text { fill: var(--text); font-family: var(--mono); font-size: 12px; opacity: .7; }
#why .axis-label, .bn-axis-label { fill: var(--text); font-family: var(--sans); font-size: 13px; font-weight: 600; opacity: .8; }

/* --- Chart B: three exponential mini-charts ------------------------------- */
#why .growth-multiples {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 1fr;
  gap: .75rem;
}
#why .spark { display: flex; flex-direction: column; gap: .5rem; min-height: 0; }
#why .spark-label {
  font-family: var(--mono);
  font-size: .6rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--teal);
  line-height: 1.3;
}

/* each mini stretches to fill the panel height (preserveAspectRatio="none") */
#why .spark-svg { flex: 1 1 auto; min-height: clamp(180px, 26vh, 300px); }

/* per-mini year axis (HTML, so it isn't distorted by the SVG stretch) */
#why .spark-years {
  display: flex;
  justify-content: space-between;
  font-family: var(--mono);
  font-size: .58rem;
  letter-spacing: .04em;
  color: var(--text);
  opacity: .55;
}

/* shared axis-title under the trio, mirroring Chart A's x-axis label */
#why .growth-axis {
  margin: 0;
  text-align: center;
  font-family: var(--sans);
  font-size: .7rem;
  font-weight: 600;
  color: var(--text);
  opacity: .8;
}

/* --- motion (pure CSS): draw-in · fade · node pop/pulse · travelling pulse - */
#why .line {
  stroke-dasharray: 1;                          /* paths carry pathLength="1" */
  stroke-dashoffset: 1;
  animation: why-draw 1.6s var(--ease) forwards;
}
#why .spark:nth-child(2) .line { animation-delay: .18s; }
#why .spark:nth-child(3) .line { animation-delay: .36s; }

#why .area, #why .zone {
  opacity: 0;
  animation: why-fade 1.1s var(--ease) .35s forwards;
}

#why .node {
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0;
  animation: why-pop .5s var(--ease) 1.15s forwards;
}
#why .node.peak {
  animation: why-pop .5s var(--ease) 1.35s forwards, why-node-pulse 4s ease-in-out 2s infinite;
}

#why .pulse { animation: why-pulse-run 4.5s linear 1.6s infinite; }
#why .spark:nth-child(2) .pulse { animation-delay: 3.1s; }
#why .spark:nth-child(3) .pulse { animation-delay: 4.6s; }

@keyframes why-draw { to { stroke-dashoffset: 0; } }
@keyframes why-fade { to { opacity: 1; } }
@keyframes why-pop  { from { opacity: 0; transform: scale(0); } to { opacity: 1; transform: scale(1); } }
@keyframes why-node-pulse {
  0%, 100% { filter: drop-shadow(0 0 3px var(--neon)); }
  50%      { filter: drop-shadow(0 0 8px var(--neon)); }
}
@keyframes why-pulse-run {
  0%   { offset-distance: 0%;   opacity: 0; }
  10%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { offset-distance: 100%; opacity: 0; }
}

@media (max-width: 980px) {
  #why > div { grid-template-columns: 1fr; }
}
/* phones — the trio stays on ONE row, scaled down to fit a ~100px column */
@media (max-width: 560px) {
  /* size from content instead of stretching to fill the panel */
  #why .growth-multiples { flex: 0 0 auto; grid-auto-rows: auto; gap: .5rem; }

  #why .spark { gap: .3rem; }

  /* equal-height label block so all three charts start on the same line */
  #why .spark-label { font-size: .5rem; letter-spacing: .06em; min-height: 2.6em; }

  /* fixed, much shorter charts */
  #why .spark-svg { flex: 0 0 auto; min-height: 0; height: clamp(80px, 24vw, 110px); }

  /* five year ticks can't fit in a ~100px column — keep only the endpoints */
  #why .spark-years { font-size: .5rem; }
  #why .spark-years span:nth-child(n+2):nth-last-child(n+2) { display: none; }
}

/* charts render complete + still when motion is reduced */
@media (prefers-reduced-motion: reduce) {
  #why .line { stroke-dashoffset: 0 !important; animation: none !important; }
  #why .area, #why .zone, #why .node { opacity: 1 !important; animation: none !important; }
  #why .pulse { animation: none !important; visibility: hidden !important; }
}

/* =========================================================================
   "What is Nudox?" (#what-is-nudox) — the one interactive section, home page
   only. The backdrop constellation, brought to the foreground: same 16
   vertices, same 22 edges, same treatment. Six of those vertices are real
   <button>s that reveal a panel beside them on hover or focus. No JS.

   Four decisions worth recording, because none is the obvious one:

   1. The geometry is copied verbatim from .constellation in base.html —
      same coordinates, same edge list, same radii — so the two read as
      literally the same graph, one blurred behind the page and one sharp
      in front of it. Only the viewBox y-window is cropped (55..670) to trim
      empty space above and below the figure.
   2. .wn-stage has a fixed aspect-ratio matching that viewBox. This is what
      keeps the drawing undistorted: with preserveAspectRatio="none" the two
      axes scale independently, so any mismatch between stage and viewBox
      shears every angle in the graph. Locking the ratio makes both axes
      scale by the same factor — and it is also what lets a node be placed
      at left:(x/1200) top:((y-55)/615) and land exactly on its vertex.
   3. A node button is a fixed square centred on its vertex, and the label
      and panel are absolutely positioned off it. Centring the whole button
      instead would put the *button box* on the vertex, which sits half a
      label-height below the dot — that was why the dots looked off before.
   4. Nodes are HTML buttons over an aria-hidden SVG rather than <g
      tabindex="0"> inside one: focusing SVG elements is unreliable in
      Safari, and buttons focus everywhere for free — which is also what
      makes this work on touch, since a tap focuses a node.
   ========================================================================= */
/* the standing answer — the section says what Nudox is without being touched */
.wn-lede {
  margin: 0 auto clamp(1.5rem, 3vh, 2.5rem);
  padding-inline: var(--wrap-pad);
  max-width: 60ch;
  text-align: center;
  font-family: var(--sans);
  font-size: clamp(1rem, 1.6vw, 1.1rem);
  line-height: 1.7;
  color: var(--text);
  opacity: .85;
}

/* --- the stage ------------------------------------------------------------ */
.wn-stage {
  position: relative;
  width: 100%;
  aspect-ratio: 1200 / 615;   /* must match the viewBox — see note 2 above */
}

.wn-edges {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

/* edges and filler vertices: the constellation's own values, minus the blur */
.wn-vertices circle {
  fill: var(--neon);
  opacity: .7;
}
.wn-vertices circle.hub {
  opacity: 1;
  filter: drop-shadow(0 0 6px var(--neon));
}

/* --- nodes ---------------------------------------------------------------- */
/* fixed square, centred on the vertex; label and panel hang off it */
.wn-node {
  position: absolute;
  transform: translate(-50%, -50%);
  width: 2.6rem;
  height: 2.6rem;
  display: grid;
  place-items: center;
  margin: 0;
  padding: 0;
  background: none;
  border: none;
  border-radius: 50%;
  cursor: pointer;
  font: inherit;
  color: inherit;
}

/* Undo the site-wide CTA button treatment (the bare `button` rules near the
   top of this file): the inset ::before frame and the neon glow both have to
   go, and `button:hover { transform: translateY(-3px) }` would otherwise
   overwrite the translate that positions every node. */
.wn-node::before { content: none; }
.wn-node { box-shadow: none; }
.wn-node:hover,
.wn-node:focus-within,
.wn-node:active { transform: translate(-50%, -50%); box-shadow: none; }

/* the engaged node and its panel rise above the rest of the mesh */
.wn-node:hover,
.wn-node:focus-within { z-index: 5; }

/* left = x/1200, top = (y-55)/615, straight off the constellation's coords */
.wn-node-1 { left: 25%;     top: 5.69%; }    /* 300,90  */
.wn-node-2 { left: 63.333%; top: 15.45%; }   /* 760,150 */
.wn-node-3 { left: 20.833%; top: 39.84%; }   /* 250,300 */
.wn-node-4 { left: 77.5%;   top: 30.08%; }   /* 930,240 */
.wn-node-5 { left: 16.667%; top: 82.11%; }   /* 200,560 */
.wn-node-6 { left: 68.333%; top: 75.61%; }   /* 820,520 */

.wn-dot {
  position: relative;
  width: .85rem;
  height: .85rem;
  border-radius: 50%;
  background: var(--bg-deep);
  border: 2.5px solid var(--neon);
  transition: background .25s var(--ease), box-shadow .25s var(--ease);
}

/* the halo that says "touch me" — a ring that breathes outward from the dot */
.wn-dot::before {
  content: "";
  position: absolute;
  inset: -.45rem;
  border-radius: 50%;
  border: 1px solid var(--neon);
}

.wn-label {
  position: absolute;
  top: calc(100% - .3rem);
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: 9rem;
  font-family: var(--mono);
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .13em;
  text-transform: uppercase;
  line-height: 1.4;
  text-align: center;
  color: var(--text);
  opacity: .75;
  transition: opacity .25s var(--ease), color .25s var(--ease);
}

/* Engaged state. :focus-within, not the :focus-visible the rest of the site
   pairs with :hover — deliberately. Browsers withhold :focus-visible from
   pointer and touch input, so a tap on a phone would focus the node but
   reveal nothing. :focus-within fires on plain :focus, which is what makes
   tap-to-open work. :focus-visible still governs the keyboard ring below. */
.wn-node:hover .wn-dot,
.wn-node:focus-within .wn-dot {
  background: var(--neon);
  box-shadow: 0 0 16px 2px rgba(33, 230, 193, .8);
}
.wn-node:hover .wn-label,
.wn-node:focus-within .wn-label {
  opacity: 1;
  color: var(--text-bright);
}
.wn-node:focus-visible { outline: none; }
.wn-node:focus-visible .wn-dot::before { border-color: var(--text-bright); }

/* --- the panel, beside its own node --------------------------------------- */
.wn-panel {
  position: absolute;
  z-index: 1;
  display: block;
  width: 21rem;
  padding: 1.25rem 1.4rem;
  background: rgba(7, 30, 61, .96);
  border: 1px solid rgba(39, 142, 165, .45);
  border-radius: 2px;
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .05),
    0 20px 40px -24px rgba(0, 0, 0, .6);
  text-align: left;
  /* opacity alone, deliberately: transitioning visibility (as the menu idiom
     above does) would pull the panel out of the accessibility tree and break
     the aria-describedby link from its node. */
  opacity: 0;
  pointer-events: none;
  transition: opacity .22s var(--ease);
}

/* Placement per node, chosen so no panel runs off the stage: the two upper
   nodes drop theirs downward, the two middle nodes push out sideways, and
   the two lower nodes lift theirs up. */
.wn-node-1 .wn-panel { top: calc(100% + .4rem); left: 50%; }
.wn-node-2 .wn-panel { top: calc(100% + .4rem); right: 50%; }
.wn-node-3 .wn-panel { left: calc(100% + .5rem); top: 50%; transform: translateY(-50%); }
.wn-node-4 .wn-panel { right: calc(100% + .5rem); top: 50%; transform: translateY(-50%); }
.wn-node-5 .wn-panel { bottom: calc(100% + .4rem); left: 50%; }
.wn-node-6 .wn-panel { bottom: calc(100% + .4rem); right: 50%; }

.wn-node:hover .wn-panel,
.wn-node:focus-within .wn-panel {
  opacity: 1;
  pointer-events: auto;   /* so the text can be read and selected */
}

.wn-kicker {
  display: flex;
  align-items: center;
  gap: .65rem;
  margin: 0 0 .75rem;
  font-family: var(--mono);
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--neon);
}
.wn-kicker-dot {
  flex: none;
  width: .55rem;
  height: .55rem;
  border-radius: 50%;
  background: var(--neon);
  box-shadow: 0 0 8px var(--neon);
}

.wn-body {
  display: block;
  margin: 0;
  font-family: var(--sans);
  font-size: .92rem;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  line-height: 1.65;
  color: var(--text);
  opacity: .9;
}
.wn-body code {
  font-family: var(--mono);
  font-size: .9em;
  color: var(--text-bright);
}

/* --- motion --------------------------------------------------------------- */
/* The mesh fades in as two groups rather than drawing edge by edge; group
   opacity animates, so each child keeps its resting value. The backdrop is
   entirely static, and this stays close to that — leaving the node halos as
   the only continuing motion, which is the thing inviting a hover. */
.wn-links,
.wn-vertices {
  opacity: 0;
  animation: wn-fade-in 1.1s var(--ease) forwards;
}
.wn-vertices { animation-delay: .35s; }

/* the invitation, staggered so the six read as a sequence rather than a
   pulse. Negative delays so the loop is already underway on first paint. */
.wn-dot::before { animation: wn-invite 3.2s var(--ease) infinite; }
.wn-node-2 .wn-dot::before { animation-delay: -2.67s; }
.wn-node-3 .wn-dot::before { animation-delay: -2.13s; }
.wn-node-4 .wn-dot::before { animation-delay: -1.60s; }
.wn-node-5 .wn-dot::before { animation-delay: -1.07s; }
.wn-node-6 .wn-dot::before { animation-delay: -0.53s; }

/* the halo is an invitation, so it stops once the invitation is accepted */
.wn-node:hover .wn-dot::before,
.wn-node:focus-within .wn-dot::before {
  animation: none;
  opacity: 1;
  transform: scale(1);
}

@keyframes wn-fade-in { to { opacity: 1; } }

@keyframes wn-invite {
  0%   { transform: scale(.85); opacity: 0; }
  35%  { opacity: .9; }
  100% { transform: scale(1.7); opacity: 0; }
}

/* --- narrow: the mesh stops being readable, so stack it ------------------- */
@media (max-width: 860px) {
  .wn-stage {
    aspect-ratio: auto;
    display: flex;
    flex-direction: column;
    gap: .5rem;
    padding-inline: var(--wrap-pad);
  }
  .wn-edges { display: none; }   /* the geometry it described no longer exists */

  .wn-node {
    position: static;
    transform: none;
    width: 100%;
    height: auto;
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: .8rem;
    padding: .7rem .25rem;
    text-align: left;
  }
  .wn-node:hover,
  .wn-node:focus-within,
  .wn-node:active { transform: none; }

  .wn-label {
    position: static;
    transform: none;
    max-width: none;
    text-align: left;
  }

  /* the panel becomes a disclosure under its own row, not a floating card */
  .wn-panel,
  .wn-node-1 .wn-panel, .wn-node-2 .wn-panel, .wn-node-3 .wn-panel,
  .wn-node-4 .wn-panel, .wn-node-5 .wn-panel, .wn-node-6 .wn-panel {
    position: static;
    transform: none;
    grid-column: 1 / -1;
    width: 100%;
    margin-top: .6rem;
    display: none;
  }
  .wn-node:hover .wn-panel,
  .wn-node:focus-within .wn-panel { display: block; }
}

@media (prefers-reduced-motion: reduce) {
  /* freeze finished, not merely stopped: mesh fully present, halo at rest */
  #what-is-nudox .wn-links,
  #what-is-nudox .wn-vertices { opacity: 1 !important; animation: none !important; }
  #what-is-nudox .wn-dot::before {
    animation: none !important;
    transform: scale(1) !important;
    opacity: .55 !important;
  }
}

/* =========================================================================
   #analytics — one agent session, drawn to scale ("session tape")
   Markup lives in content/_index.md. No JS, same as every other figure here.

   The claim this section makes is a RATIO, so it is encoded as a length: the
   two tapes share one x-axis, and the "with" tape is exactly one fifth as
   long as the "without" tape. Nothing is toggled — #benefits already owns the
   Without/With switch, and repeating it would say the same thing twice, so
   both states are simply present at once.

   Chart space: viewBox 0 0 720 300. Track x 60 → 690 (630 units = the full
   "without" run); the "with" tape ends at x 186, i.e. 60 + 630/5. Upper tape
   y 70→104, lower tape y 170→204, shared axis at y 250. Each tape is revealed
   by its own clipPath rect whose width CSS animates — the same
   geometry-property-via-CSS approach #benefits uses for `d` and `cy`. The
   full extent of both tapes is drawn unclipped as .an-outline, so the ratio
   is readable at rest and wherever the animation does not run.

   The motion is a race, and its honesty rests on one thing: both wipes
   advance at the SAME rate — 100 user units per second, so the "with" tape
   finishes in a fifth of the time rather than merely ending up a fifth as
   long. That is why both use `linear` and a shared 12s cycle rather than
   var(--ease): per-interval easing over different keyframe spans would give
   the two tapes different velocities mid-race and quietly break the claim.
   If the cycle length changes, both keyframe sets have to move together.
   ========================================================================= */
#analytics > h1 span { color: var(--neon); }

.an-lede {
  max-width: 62ch;
  margin: 0 auto clamp(2rem, 4vh, 3rem);
  text-align: center;
  font-family: "Tenor Sans", var(--sans);
  font-size: clamp(1rem, 1.6vw, 1.12rem);
  line-height: 1.65;
  color: var(--text);
  opacity: .85;
}

/* --- the window: chart, legend, totals, caveat — one column --------------- */
.an-window {
  max-width: 72rem;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: clamp(1rem, 2.5vh, 1.6rem);
  padding: clamp(1.25rem, 2.5vw, 2rem);
  background: rgba(7, 30, 61, .92);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .05),
    0 20px 40px -24px rgba(0, 0, 0, .6);
}

.an-task {
  margin: 0;
  font-family: var(--mono);
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--teal);
}

/* --- chart furniture ------------------------------------------------------ */
.an-track-label {
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
}
.an-track-label.an-off { fill: #ff9aa8; }
.an-track-label.an-on { fill: var(--neon); }

/* the empty tapes: the length ratio is legible before a single pixel fills */
.an-outline { fill: none; stroke: rgba(39, 142, 165, .4); }
.an-axis { fill: none; stroke: rgba(39, 142, 165, .45); }
.an-axis-label {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  fill: var(--text);
  opacity: .7;
}

/* --- tape segments -------------------------------------------------------- */
/* Same red/teal/neon language as #benefits: red is the exploration tax, neon
   is work that moves the task forward, teal is the graph query that replaces
   the tax. The two tapes carry the same palette — only the proportions differ,
   which is the entire argument. */
.an-seg { stroke: none; }
.an-explore { fill: rgba(255, 84, 112, .32); }
.an-query { fill: rgba(39, 142, 165, .55); }
.an-work { fill: rgba(33, 230, 193, .34); }

.an-tick { fill: none; stroke: var(--red); stroke-width: 2.5; stroke-linecap: round; }

/* The session total, right-aligned to its own tape's end and living INSIDE
   the clipped group — so each number arrives exactly when its tape finishes
   rather than sitting there ahead of the run. 50K against 10K is the same 5×
   the two lengths already carry; if either figure changes, the tape geometry
   (630 vs 126 units) has to change with it or the picture starts lying. */
.an-amount {
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.an-amount.an-off { fill: #ff9aa8; }
.an-amount.an-on { fill: var(--neon); }

/* where each session ran out, marked down at the axis */
.an-guide { fill: none; stroke-width: 1.5; stroke-dasharray: 4 4; }
.an-guide.an-off { stroke: var(--red); opacity: .8; }
.an-guide.an-on { stroke: var(--neon); opacity: .9; }

/* --- the race ------------------------------------------------------------- */
/* 12s cycle: 6.3s of run for the long tape (1.26s for the short one, same
   100 units/s), a hold, then a short dark beat before it replays — the same
   rhythm the dependency graph in #how-it-works uses. */
@keyframes an-race-off {
  0% { width: 0; }
  52.5% { width: 630px; }
  95% { width: 630px; }
  95.01%, 100% { width: 0; }
}
@keyframes an-race-on {
  0% { width: 0; }
  10.5% { width: 126px; }
  95% { width: 126px; }
  95.01%, 100% { width: 0; }
}
/* fades the filled tapes out before the wipes snap back, so the reset is a
   beat rather than a flicker */
@keyframes an-replay {
  0%, 90% { opacity: 1; }
  95%, 100% { opacity: 0; }
}

.an-wipe-off { animation: an-race-off 12s linear infinite; }
.an-wipe-on { animation: an-race-on 12s linear infinite; }
.an-tapes { animation: an-replay 12s linear infinite; }

/* --- legend --------------------------------------------------------------- */
.an-legend {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.5rem;
  font-family: var(--mono);
  font-size: .66rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--text);
  opacity: .8;
}
.an-key { display: inline-flex; align-items: center; gap: .55rem; }
.an-swatch { flex: none; width: 1.1rem; height: .7rem; border-radius: 1px; }
.an-swatch.an-explore { background: rgba(255, 84, 112, .32); }
.an-swatch.an-query { background: rgba(39, 142, 165, .55); }
.an-swatch.an-work { background: rgba(33, 230, 193, .34); }
/* the defect key is the same ✕ the chart draws, built out of two gradients */
.an-swatch.an-defect {
  width: .8rem;
  height: .8rem;
  background:
    linear-gradient(45deg, transparent 44%, var(--red) 44%, var(--red) 56%, transparent 56%),
    linear-gradient(-45deg, transparent 44%, var(--red) 44%, var(--red) 56%, transparent 56%);
}

/* --- the two figures ------------------------------------------------------ */
.an-totals {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(1rem, 3vw, 2rem);
  padding-top: clamp(.75rem, 2vh, 1.25rem);
  border-top: 1px solid rgba(39, 142, 165, .25);
}
/* centered against the label, not baseline-aligned: the label wraps to two
   lines and the figure is ~3× its size, so a shared baseline hangs the number
   off the label's first line instead of reading as one unit. */
.an-stat { margin: 0; display: flex; align-items: center; gap: .7rem; }
.an-figure {
  font-family: "Tenor Sans", var(--sans);
  font-size: clamp(2rem, 5vw, 3.2rem);
  line-height: 1;
  color: var(--neon);
  text-shadow: 0 0 18px rgba(33, 230, 193, .35);
}
.an-stat-label {
  max-width: 16ch;
  font-family: var(--mono);
  font-size: .68rem;
  font-weight: 600;
  letter-spacing: .12em;
  line-height: 1.5;
  text-transform: uppercase;
  color: var(--text);
  opacity: .85;
}

/* --- narrow ---------------------------------------------------------------- */
/* The tapes stay horizontal at every width — the length ratio IS the figure,
   so it never stacks. Only the SVG text needs sizing up as the box shrinks,
   the same way #benefits handles it. */
@media (max-width: 640px) {
  .an-totals { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 700px) {
  .an-track-label { font-size: 17px; }
  .an-axis-label, .an-amount { font-size: 15px; }
}
@media (max-width: 440px) {
  .an-track-label { font-size: 23px; }
  .an-axis-label, .an-amount { font-size: 20px; }
}

/* both tapes render complete and still when motion is reduced — the ratio
   reads perfectly well without the race */
@media (prefers-reduced-motion: reduce) {
  .an-wipe-off, .an-wipe-on, .an-tapes { animation: none !important; }
  .an-wipe-off { width: 630px; }
  .an-wipe-on { width: 126px; }
  .an-tapes { opacity: 1; }
}

/* =========================================================================
   "How Nudox Works" (#how-it-works) — single dependency-graph diagram
   Same hand-drawn SVG + CSS motion approach as #why (no JS)
   ========================================================================= */
/* single glass panel, centered, capped so the diagram keeps its proportions */
#how-it-works figure {
  margin: 0 auto;
  max-width: 56rem;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  padding: clamp(1.5rem, 3vw, 2.5rem);
  background: rgba(7, 30, 61, .92);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .05),
    0 20px 40px -24px rgba(0, 0, 0, .6);
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}
#how-it-works figure:hover {
  border-color: rgba(33, 230, 193, .6);
  box-shadow:
    inset 0 0 55px rgba(33, 230, 193, .09),
    0 24px 48px -22px rgba(0, 0, 0, .7);
}


/* --- dependency graph primitives ------------------------------------------ */

#how-it-works .edge {
  fill: none;
  stroke: var(--neon);
  stroke-width: 2.25;
  stroke-linecap: round;
  filter: drop-shadow(0 0 3px rgba(33, 230, 193, .45));
}
#how-it-works .arrow-head { fill: var(--neon); }

#how-it-works .node-box rect {
  fill: var(--bg-deep);
  stroke: var(--neon);
  stroke-width: 2;
}
#how-it-works .node-label {
  fill: var(--text);
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 600;
  opacity: .9;
}

/* the hub — the symbol every other node relates to — picked out like the bell curve's peak node */
#how-it-works .hub rect {
  stroke-width: 3;
  filter: drop-shadow(0 0 7px var(--neon));
}
#how-it-works .hub .node-label { fill: var(--text-bright); opacity: 1; }

/* --- motion (pure CSS): edges draw in, boxes pop in ----------------------- */
#how-it-works .edge {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: how-draw 1.1s var(--ease) forwards;
}
#how-it-works .edges .edge:nth-child(2) { animation-delay: .1s; }
#how-it-works .edges .edge:nth-child(3) { animation-delay: .2s; }
#how-it-works .edges .edge:nth-child(4) { animation-delay: .3s; }
#how-it-works .edges .edge:nth-child(5) { animation-delay: .4s; }
#how-it-works .edges .edge:nth-child(6) { animation-delay: .5s; }
#how-it-works .edges .edge:nth-child(7) { animation-delay: .6s; }

/* The boxes pop in once, then a single light walks the graph on a loop:
   TokenStore, out to its test, into the hub, then round the five dependents
   clockwise — the traversal the caption describes.

   The pop lives on the <g> and the light lives on the <rect>, deliberately.
   Putting both on the <g> forces animation-delay to become a two-value list in
   a rule whose animation shorthand contains var(--ease), and a shorthand
   carrying var() turns its longhands into pending-substitution values — not a
   cascade to bet a staggered sequence on. Split across two elements, each rule
   owns one animation and one plain delay. */
#how-it-works .node-box {
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0;
  animation: how-pop .45s var(--ease) .9s forwards;
}
#how-it-works .hub { animation: how-pop .45s var(--ease) .55s forwards; }

#how-it-works .node-box rect { animation: how-node-lit 6s linear infinite; }
/* must follow the rule above — equal specificity, so source order decides */
#how-it-works .hub rect { animation-name: how-node-lit-hub; }

/* arrivals .6s apart, then a 1.8s dark beat before the light comes round
   again. nth-child indexes are markup order, which is not traversal order —
   each line names the node it selects. */
#how-it-works .nodes .node-box:nth-child(4) rect { animation-delay: 0s; }     /* TokenStore */
#how-it-works .nodes .node-box:nth-child(3) rect { animation-delay: -5.4s; }  /* tests/auth.rs */
#how-it-works .nodes .node-box:nth-child(5) rect { animation-delay: -4.8s; }  /* AuthToken::refresh — the hub */
#how-it-works .nodes .node-box:nth-child(1) rect { animation-delay: -4.2s; }  /* router.rs */
#how-it-works .nodes .node-box:nth-child(2) rect { animation-delay: -3.6s; }  /* user_svc.rs */
#how-it-works .nodes .node-box:nth-child(6) rect { animation-delay: -3s; }    /* session.rs */
#how-it-works .nodes .node-box:nth-child(8) rect { animation-delay: -2.4s; }  /* api/auth.rs */
#how-it-works .nodes .node-box:nth-child(7) rect { animation-delay: -1.8s; }  /* middleware.rs */

@keyframes how-draw { to { stroke-dashoffset: 0; } }
@keyframes how-pop  { from { opacity: 0; transform: scale(.9); } to { opacity: 1; transform: scale(1); } }

/* A flare, not a bloom. The earlier version animated only a drop-shadow and a
   brightness() bump, which on a box that already carries a neon stroke was too
   subtle to read as anything. This floods the fill, drives the stroke to
   near-white and thickens it, then falls back over ~1.2s. Dark states below
   match each rule's static values exactly, so nothing shifts at the seam. */
@keyframes how-node-lit {
  0%, 100% {
    fill: #0f3f38;
    stroke: var(--text-bright);
    stroke-width: 3.5;
    filter: drop-shadow(0 0 14px rgba(33, 230, 193, .95));
  }
  20%, 92% {
    fill: var(--bg-deep);
    stroke: var(--neon);
    stroke-width: 2;
    filter: drop-shadow(0 0 0px rgba(33, 230, 193, 0));
  }
}

/* identical flare, resting at the hub's permanently heavier stroke and glow */
@keyframes how-node-lit-hub {
  0%, 100% {
    fill: #0f3f38;
    stroke: var(--text-bright);
    stroke-width: 4.5;
    filter: drop-shadow(0 0 16px rgba(33, 230, 193, .95));
  }
  20%, 92% {
    fill: var(--bg-deep);
    stroke: var(--neon);
    stroke-width: 3;
    filter: drop-shadow(0 0 7px var(--neon));
  }
}

@media (prefers-reduced-motion: reduce) {
  #how-it-works .edge { stroke-dashoffset: 0 !important; animation: none !important; }
  /* the rect carries the loop, the g carries the pop — both must be stopped */
  #how-it-works .node-box, #how-it-works .node-box rect { opacity: 1 !important; animation: none !important; }
}

/* =========================================================================
   #benefits — the "Without Nudox / With Nudox" comparison window
   Two radios drive the whole section through ~ , so there is no JS here,
   same as #what-is-nudox. Markup lives in content/_index.md.

   Chart space: viewBox 0 0 720 420, plot area x 70→690, y 30→350 (350 = zero).
   Both trajectories are authored with an IDENTICAL command list (M + 6 C) —
   that is the precondition for CSS to interpolate `d`, and it is why the
   curve bends downward on click instead of cutting. Browsers without the
   CSS `d` property crossfade the .bn-curves-alt layer instead, which carries
   the same two shapes; see the @supports pair below.
   ========================================================================= */
#benefits > h1 span { color: var(--neon); }

.bn-lede {
  max-width: 62ch;
  margin: 0 auto clamp(2rem, 4vh, 3rem);
  text-align: center;
  font-family: "Tenor Sans", var(--sans);
  font-size: clamp(1rem, 1.6vw, 1.12rem);
  line-height: 1.65;
  color: var(--text);
  opacity: .85;
}

/* --- the window: chart left, the two options in a rail on the right ------- */
.bn-window {
  /* Runs to the page gutters, with a ceiling only because the chart's height is
     tied to its width by the viewBox (h ≈ 0.58w) — uncapped, a 2560px monitor
     would give a chart taller than the viewport. */
  max-width: 100rem;
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(15rem, 23rem);
  gap: clamp(1.25rem, 2.5vw, 2rem);
  align-items: start;
  padding: clamp(1.25rem, 2.5vw, 2rem);
  background: rgba(7, 30, 61, .92);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .05),
    0 20px 40px -24px rgba(0, 0, 0, .6);
}

/* Each radio lives INSIDE the option it controls, which is why the state
   selectors above go through :has() rather than a ~ combinator. It has to be
   this way round: a radio parked elsewhere in the window is still what receives
   focus when its label is clicked, and the browser then scrolls that radio into
   view — which yanked the page upward on every click. Focus now lands inside
   the thing the reader just clicked, so there is nothing to scroll to.

   Clipped, not hidden: display:none and visibility:hidden would both drop the
   radios out of the focus order, and the arrow-key group is how this section
   works without a mouse. */
.bn-opt input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

.bn-plot { min-width: 0; display: flex; flex-direction: column; gap: .9rem; }


/* the adoption line only means anything in the "with" state */
.bn-marker { opacity: 0; transition: opacity .45s var(--ease); }
.bn-marker line { stroke: var(--neon); stroke-width: 1.5; stroke-dasharray: 6 5; opacity: .75; }
.bn-window:has(#bn-on:checked) .bn-marker { opacity: 1; }

/* --- curve primitives ----------------------------------------------------- */
.bn-line { fill: none; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
.bn-line.bn-bugs { stroke-width: 2.25; }
.bn-area { stroke: none; }
.bn-dot { stroke: var(--bg-deep); stroke-width: 2; }

/* resting palette = the "without" trajectory */
.bn-curves .bn-line.bn-tokens { stroke: var(--red); filter: drop-shadow(0 0 4px rgba(255, 84, 112, .5)); }
.bn-curves .bn-line.bn-bugs   { stroke: #ff9aa8; }
.bn-curves .bn-area.bn-tokens { fill: rgba(255, 84, 112, .14); }
.bn-curves .bn-area.bn-bugs   { fill: rgba(255, 84, 112, .07); }
.bn-curves .bn-dot.bn-tokens  { fill: var(--red); }
.bn-curves .bn-dot.bn-bugs    { fill: #ff9aa8; }

/* the fallback layer carries the "with" trajectory and its palette. Equal
   specificity to the block above, so source order decides — keep it after. */
.bn-curves-alt { opacity: 0; }
.bn-curves-alt .bn-line.bn-tokens { stroke: var(--neon); filter: drop-shadow(0 0 4px rgba(33, 230, 193, .55)); }
.bn-curves-alt .bn-line.bn-bugs   { stroke: var(--teal); }
.bn-curves-alt .bn-area.bn-tokens { fill: rgba(33, 230, 193, .13); }
.bn-curves-alt .bn-area.bn-bugs   { fill: rgba(39, 142, 165, .10); }
.bn-curves-alt .bn-dot.bn-tokens  { fill: var(--neon); }
.bn-curves-alt .bn-dot.bn-bugs    { fill: var(--teal); }

/* No CSS `d`: crossfade the two layers. Correct, just not a morph.
   :not() is needed because .bn-curves-alt also carries .bn-curves. */
@supports not (d: path("M 0 0")) {
  .bn-curves { transition: opacity .5s var(--ease); }
  .bn-window:has(#bn-on:checked) .bn-curves:not(.bn-curves-alt) { opacity: 0; }
  .bn-window:has(#bn-on:checked) .bn-curves-alt { opacity: 1; }
}

/* CSS `d`: drop the second layer entirely and bend the real curves instead. */
@supports (d: path("M 0 0")) {
  .bn-curves-alt { display: none; }

  .bn-curves .bn-line,
  .bn-curves .bn-area {
    transition: d .85s var(--ease), stroke .5s linear, fill .5s linear, filter .5s linear;
  }
  .bn-curves .bn-dot { transition: cy .85s var(--ease), fill .5s linear; }

  .bn-window:has(#bn-on:checked) .bn-line.bn-tokens {
    stroke: var(--neon);
    filter: drop-shadow(0 0 4px rgba(33, 230, 193, .55));
    d: path("M 70.0 305.0 C 87.2 303.7, 138.9 301.0, 173.3 297.0 C 207.8 293.0, 242.2 285.8, 276.7 281.0 C 311.1 276.2, 345.6 265.2, 380.0 268.0 C 414.4 270.8, 448.9 289.7, 483.3 298.0 C 517.8 306.3, 552.2 312.7, 586.7 318.0 C 621.1 323.3, 672.8 328.0, 690.0 330.0");
  }
  .bn-window:has(#bn-on:checked) .bn-area.bn-tokens {
    fill: rgba(33, 230, 193, .13);
    d: path("M 70.0 305.0 C 87.2 303.7, 138.9 301.0, 173.3 297.0 C 207.8 293.0, 242.2 285.8, 276.7 281.0 C 311.1 276.2, 345.6 265.2, 380.0 268.0 C 414.4 270.8, 448.9 289.7, 483.3 298.0 C 517.8 306.3, 552.2 312.7, 586.7 318.0 C 621.1 323.3, 672.8 328.0, 690.0 330.0 L 690.0 350.0 L 70.0 350.0 Z");
  }
  .bn-window:has(#bn-on:checked) .bn-line.bn-bugs {
    stroke: var(--teal);
    d: path("M 70.0 325.0 C 87.2 324.2, 138.9 322.3, 173.3 320.0 C 207.8 317.7, 242.2 313.5, 276.7 311.0 C 311.1 308.5, 345.6 302.8, 380.0 305.0 C 414.4 307.2, 448.9 318.7, 483.3 324.0 C 517.8 329.3, 552.2 333.7, 586.7 337.0 C 621.1 340.3, 672.8 342.8, 690.0 344.0");
  }
  .bn-window:has(#bn-on:checked) .bn-area.bn-bugs {
    fill: rgba(39, 142, 165, .10);
    d: path("M 70.0 325.0 C 87.2 324.2, 138.9 322.3, 173.3 320.0 C 207.8 317.7, 242.2 313.5, 276.7 311.0 C 311.1 308.5, 345.6 302.8, 380.0 305.0 C 414.4 307.2, 448.9 318.7, 483.3 324.0 C 517.8 329.3, 552.2 333.7, 586.7 337.0 C 621.1 340.3, 672.8 342.8, 690.0 344.0 L 690.0 350.0 L 70.0 350.0 Z");
  }
  .bn-window:has(#bn-on:checked) .bn-dot.bn-tokens { fill: var(--neon); cy: 330px; }
  .bn-window:has(#bn-on:checked) .bn-dot.bn-bugs   { fill: var(--teal); cy: 344px; }
}

/* --- legend --------------------------------------------------------------- */
.bn-legend {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.5rem;
  font-family: var(--mono);
  font-size: .66rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--text);
  opacity: .8;
}
.bn-key { display: inline-flex; align-items: center; gap: .55rem; }
.bn-swatch {
  width: 1.7rem;
  height: 0;
  border-top: 3px solid var(--red);
  transition: border-color .5s linear;
}
.bn-swatch.bn-bugs { border-top-color: #ff9aa8; }   /* colour alone, same weight as tokens */
.bn-window:has(#bn-on:checked) .bn-swatch.bn-tokens { border-top-color: var(--neon); }
.bn-window:has(#bn-on:checked) .bn-swatch.bn-bugs   { border-top-color: var(--teal); }

/* --- the two options ------------------------------------------------------ */
.bn-rail { min-width: 0; display: flex; flex-direction: column; gap: .85rem; }

.bn-opt {
  position: relative;                 /* contains its clipped radio */
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas:
    "dot title"
    ".   sub";
  gap: .25rem .7rem;
  align-items: center;
  padding: 1rem 1.1rem;
  cursor: pointer;
  background: rgba(4, 13, 28, .55);
  border: 1px solid rgba(39, 142, 165, .3);
  border-left: 3px solid var(--accent);
  border-radius: 2px;
  transition:
    background .3s var(--ease),
    border-color .3s var(--ease),
    box-shadow .3s var(--ease),
    transform .3s var(--ease);
}
.bn-opt-off { --accent: var(--red); }
.bn-opt-on  { --accent: var(--neon); }

.bn-opt-dot {
  grid-area: dot;
  width: .7rem;
  height: .7rem;
  border-radius: 50%;
  background: var(--accent);
  opacity: .4;
  transition: opacity .3s var(--ease), box-shadow .3s var(--ease);
}
.bn-opt-title {
  grid-area: title;
  font-family: var(--mono);
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--text);
}
.bn-opt-sub {
  grid-area: sub;
  font-family: "Tenor Sans", var(--sans);
  font-size: .82rem;
  line-height: 1.45;
  color: var(--text);
  opacity: .55;
}

/* hover restates border-left-color: the border-color shorthand resets it */
.bn-opt:hover {
  background: rgba(4, 13, 28, .8);
  border-color: rgba(33, 230, 193, .45);
  border-left-color: var(--accent);
  transform: translateX(2px);
}

.bn-window:has(#bn-off:checked) .bn-opt-off,
.bn-window:has(#bn-on:checked) .bn-opt-on {
  background: rgba(4, 13, 28, .95);
  border-color: var(--accent);
  box-shadow: inset 0 0 26px -6px var(--accent);
}
.bn-window:has(#bn-off:checked) .bn-opt-off .bn-opt-dot,
.bn-window:has(#bn-on:checked) .bn-opt-on .bn-opt-dot {
  opacity: 1;
  box-shadow: 0 0 10px var(--accent);
  animation: none;
}
.bn-window:has(#bn-off:checked) .bn-opt-off .bn-opt-title,
.bn-window:has(#bn-on:checked) .bn-opt-on .bn-opt-title { color: var(--text-bright); }
.bn-window:has(#bn-off:checked) .bn-opt-off .bn-opt-sub,
.bn-window:has(#bn-on:checked) .bn-opt-on .bn-opt-sub { opacity: .85; }

/* the radio is clipped, so the ring has to be drawn on its label instead */
.bn-opt:has(:focus-visible) {
  outline: 2px solid var(--neon);
  outline-offset: 3px;
}

/* --- caption, one per state, stacked in a single cell --------------------- */
.bn-captions {
  grid-column: 1 / -1;
  display: grid;
  margin-top: .3rem;
  padding-top: 1.1rem;
  border-top: 1px solid rgba(39, 142, 165, .25);
}
.bn-captions > p {
  grid-area: 1 / 1;                   /* both share one cell; taller one sizes it */
  margin: 0;
  max-width: 74ch;
  font-family: "Tenor Sans", var(--sans);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--text);
  opacity: 0;
  visibility: hidden;                 /* also keeps the inactive one off screen readers */
  transition: opacity .4s var(--ease), visibility .4s var(--ease);
}
.bn-window:has(#bn-off:checked) .bn-cap-off,
.bn-window:has(#bn-on:checked) .bn-cap-on { opacity: .85; visibility: visible; }

/* --- motion: draw-in on the solid line, fade for everything else ---------- */
/* Only the tokens line draws in — the bugs line spends its stroke-dasharray
   on being dashed, so a dash-based reveal would fight the dash pattern. */
.bn-line.bn-tokens {
  stroke-dasharray: 1;                /* paths carry pathLength="1" */
  stroke-dashoffset: 1;
  animation: bn-draw 1.5s var(--ease) forwards;
}
.bn-area, .bn-line.bn-bugs, .bn-dot {
  opacity: 0;
  animation: bn-fade 1s var(--ease) .3s forwards;
}
.bn-line.bn-bugs { stroke-dasharray: 7 6; }

/* the unchosen option keeps breathing so it reads as clickable */
.bn-opt-dot { animation: bn-invite 2.6s ease-in-out infinite; }

@keyframes bn-draw { to { stroke-dashoffset: 0; } }
@keyframes bn-fade { to { opacity: 1; } }
@keyframes bn-invite { 0%, 100% { opacity: .35; } 50% { opacity: .9; } }

/* --- narrow ---------------------------------------------------------------- */
/* Collapses well before the window itself runs out of room, and deliberately so:
   a 23rem rail alongside the chart leaves the plot column around 400–650px in
   the 900–1180 range, and the chart's labels are drawn in viewBox units, so they
   shrink with it. Stacking early keeps the plot wide and the labels legible. */
@media (max-width: 1180px) {
  .bn-window { grid-template-columns: minmax(0, 1fr); }
  .bn-rail { flex-direction: row; }
  .bn-opt { flex: 1 1 0; min-width: 0; }
}
/* two options side by side stop fitting well before the chart does */
@media (max-width: 520px) {
  .bn-rail { flex-direction: column; }
}
/* SVG text scales with the viewBox, so it has to be sized up as the box
   shrinks. Rendered size ≈ font-size × (chart px width ÷ 720); one bump is not
   enough, because that ratio runs from ~1.05 at 900px down to ~0.33 on a phone.
   These four steps hold every label in a 10–14px rendered band across it. */
@media (max-width: 820px) {
  .bn-tick text { font-size: 15px; }
  .bn-axis-label { font-size: 16px; }
  .bn-marker text { font-size: 14px; }
}
@media (max-width: 680px) {
  .bn-tick text { font-size: 19px; }
  .bn-axis-label { font-size: 20px; }
  .bn-marker text { font-size: 17px; letter-spacing: 1.5px; }
}
/* Below here the y-axis furniture no longer fits the 70-unit gutter it sits in,
   and scaling it further pushes it outside the chart box. Drop it: the shape of
   the two curves is the point, and the legend and caption still name them. */
@media (max-width: 540px) {
  .bn-tick, .bn-axis-y { display: none; }
  .bn-axis-label { font-size: 26px; }
  .bn-marker text { font-size: 21px; letter-spacing: 1px; }
}
@media (max-width: 440px) {
  .bn-axis-label { font-size: 32px; }
  .bn-marker text { font-size: 31px; }
}

/* everything renders complete and still when motion is reduced */
@media (prefers-reduced-motion: reduce) {
  .bn-line.bn-tokens { stroke-dashoffset: 0 !important; animation: none !important; }
  .bn-area, .bn-line.bn-bugs, .bn-dot { opacity: 1 !important; animation: none !important; }
  .bn-opt-dot { animation: none !important; }
  .bn-curves, .bn-curves .bn-line, .bn-curves .bn-area, .bn-curves .bn-dot,
  .bn-marker, .bn-swatch, .bn-opt, .bn-captions > p { transition: none !important; }
}

/* =========================================================================
   "Nudox Query Engine" — mac-style code window + a stack of "sticky" callout
   cards (any <dl> in .stickies is a sticky). The pair sits side by side inside
   #integrations on the features page; .code-window on its own is reused by
   #compiler-truth-graph, #legal, #contact-form and #about-background.
   ========================================================================= */
#integrations .query-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(1.5rem, 3vw, 2.5rem);
  align-items: start;
  max-width: 128rem;
  margin: 0 auto clamp(2.5rem, 5vh, 3.5rem);
}

/* --- the window itself ----------------------------------------------------- */
.code-window {
  overflow: hidden;
  background: var(--bg-deep);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 8px;
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .05),
    0 20px 40px -24px rgba(0, 0, 0, .6);
}

.window-bar {
  position: relative;
  display: flex;
  align-items: center;
  gap: .5rem;
  padding: .8rem 1rem;
  background: rgba(7, 30, 61, .6);
  border-bottom: 1px solid rgba(39, 142, 165, .35);
}

.window-bar .dot {
  width: .65rem;
  height: .65rem;
  border-radius: 50%;
  flex: none;
}
.dot-a { background: rgba(196, 218, 230, .35); }
.dot-b { background: var(--teal); }
.dot-c {
  background: var(--neon);
  box-shadow: 0 0 6px rgba(33, 230, 193, .7);
}

.window-title {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  font-family: var(--mono);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--text);
  opacity: .7;
}

.code-window pre {
  margin: 0;
  padding: clamp(1.25rem, 2.5vw, 1.75rem);
  overflow-x: auto;
  font-family: var(--mono);
  font-size: .90rem;
  line-height: 1.7;
  color: var(--text);
}
.code-window code { font-family: inherit; }

.code-window .cm  { color: var(--teal); opacity: .8; font-style: italic; }
.code-window .kw  { color: var(--neon); font-weight: 600; }
.code-window .str { color: var(--text-bright); }

/* --- stickies ---------------------------------------------------------- */
.stickies {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}

.stickies dl {
  margin: 0;
  padding: clamp(1.1rem, 2vw, 1.5rem);
  background: rgba(7, 30, 61, .5);
  border: 1px solid rgba(39, 142, 165, .3);
  border-left: 3px solid var(--neon);
  border-radius: 2px;
}

.stickies dt {
  margin: 0 0 .5rem;
  font-family: var(--mono);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .06em;
  color: var(--text-bright);
}

.stickies dd {
  margin: 0;
  font-family: "Tenor Sans", var(--sans);
  font-size: .92rem;
  line-height: 1.6;
  color: var(--text);
  opacity: .85;
}

@media (max-width: 980px) {
  #integrations .query-pair { grid-template-columns: 1fr; }
}

/* =========================================================================
   "How NuDox compares" (#comparison) — capability comparison table
   ========================================================================= */
/* glass panel card, scrollable so the table never breaks layout on narrow screens */
#comparison > div {
  max-width: 128rem;
  margin: 0 auto;
  overflow-x: auto;
  background: rgba(7, 30, 61, .92);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .05),
    0 20px 40px -24px rgba(0, 0, 0, .6);
}

#comparison table {
  width: 100%;
  min-width: 680px;
  border-collapse: collapse;
}

#comparison caption {
  caption-side: top;
  padding: clamp(1.25rem, 2.5vw, 2rem) clamp(1.25rem, 2.5vw, 2rem) 0;
  text-align: left;
  font-family: "Tenor Sans", var(--sans);
  font-size: .92rem;
  line-height: 1.6;
  color: var(--text);
  opacity: .75;
}

#comparison th, #comparison td {
  padding: 1rem clamp(1rem, 2vw, 1.5rem);
  text-align: left;
  white-space: nowrap;
  border-bottom: 1px solid rgba(39, 142, 165, .18);
}

#comparison thead th {
  font-family: var(--mono);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--teal);
  border-bottom: 1px solid rgba(39, 142, 165, .4);
}

#comparison tbody th {
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: .95rem;
  color: var(--text-bright);
}

#comparison tbody tr:last-child th,
#comparison tbody tr:last-child td { border-bottom: none; }

#comparison tbody tr:hover { background: rgba(33, 230, 193, .04); }

/* icon + label per cell */
#comparison td .icon { display: inline-block; margin-right: .6rem; font-weight: 700; }
#comparison td span:last-child {
  font-family: var(--mono);
  font-size: .8rem;
  letter-spacing: .04em;
  color: var(--text);
  opacity: .75;
}

/* reuse the site's teal (partial) / neon (verified) language from the charts */
#comparison .icon-no      { color: var(--text); opacity: .35; }
#comparison .icon-partial { color: var(--teal); }
#comparison .icon-yes     { color: var(--neon); filter: drop-shadow(0 0 4px rgba(33, 230, 193, .5)); }

/* NuDox is the answer — pick out the whole column */
#comparison thead th:last-child { color: var(--text-bright); }
#comparison tbody td:last-child { background: rgba(33, 230, 193, .07); }
#comparison tbody td:last-child span:last-child {
  color: var(--text-bright);
  opacity: 1;
  font-weight: 600;
}

/* -------------------------------------------------------------------------
   Phones/tablets — the 680px table can't fit, and a sideways scroller hides
   the NuDox column. Each row becomes its own card: capability as the heading,
   the three tools as a compact row underneath (label / icon / verdict).
   ------------------------------------------------------------------------- */
@media (max-width: 760px) {
  /* the glass panel moves off the wrapper and onto each row-card */
  #comparison > div {
    overflow-x: visible;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }

  #comparison table { display: block; min-width: 0; }
  /* display:block is required — a table-caption inside a display:block table
     shrink-wraps to a narrow column */
  #comparison caption { display: block; padding: 0 0 1.25rem; }
  #comparison thead { display: none; }          /* column names live in ::before now */
  #comparison tbody { display: grid; gap: .75rem; }

  /* one card per capability — same panel recipe the wrapper used to carry */
  #comparison tbody tr,
  #comparison tbody tr:hover {                  /* :hover would otherwise wipe the fill */
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: .5rem;
    padding: 1rem;
    background: rgba(7, 30, 61, .92);
    border: 1px solid rgba(39, 142, 165, .35);
    border-radius: 2px;
    box-shadow:
      inset 0 0 40px rgba(33, 230, 193, .05),
      0 20px 40px -24px rgba(0, 0, 0, .6);
  }

  /* capability name = card heading, spanning the three verdict columns.
     :last-child is re-stated because the base rule strips its border. */
  #comparison tbody th,
  #comparison tbody tr:last-child th {
    grid-column: 1 / -1;
    padding: 0 0 .8rem;
    white-space: normal;                        /* base rule sets nowrap on every cell */
    border-bottom: 1px solid rgba(39, 142, 165, .18);
  }

  /* each verdict stacks: TOOL / icon / word */
  #comparison tbody td,
  #comparison tbody tr:last-child td {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .3rem;
    padding: .7rem .25rem .5rem;
    text-align: center;
    border-bottom: 0;
    border-radius: 2px;
  }

  #comparison tbody td::before {
    content: attr(data-label);
    font-family: var(--mono);
    font-size: .55rem;
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--teal);
  }

  #comparison td .icon { margin-right: 0; font-size: 1.15rem; line-height: 1; }
  #comparison td span:last-child { font-size: .7rem; }
}

/* =========================================================================
   "Drops into your agent" (#integrations) — tool grid + MCP install command
   ========================================================================= */
#integrations > p {
  margin: 0 auto clamp(2.5rem, 5vh, 3.5rem);
  max-width: 46ch;
  text-align: center;
  font-family: "Tenor Sans", var(--sans);
  font-size: 1rem;
  line-height: 1.65;
  color: var(--text);
  opacity: .85;
}

/* --- tool grid -------------------------------------------------------- */
#integrations ul {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1rem;
  max-width: 128rem;
  margin: 0 auto clamp(3.5rem, 7vh, 5rem);
  padding: 0;
  list-style: none;
}

#integrations li {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.1rem 1rem;
  text-align: center;
  font-family: var(--mono);
  font-size: .85rem;
  font-weight: 600;
  letter-spacing: .04em;
  color: var(--text);
  background: rgba(7, 30, 61, .92);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
}

/* --- install command ---------------------------------------------------- */
#integrations .install-card {
  max-width: 72rem;
  margin: 0 auto clamp(3.5rem, 7vh, 5rem);
  overflow: hidden;
  background: var(--bg-deep);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 8px;
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .05),
    0 20px 40px -24px rgba(0, 0, 0, .6);
}

#integrations .install-label {
  display: block;
  padding: .8rem 1.25rem;
  font-family: var(--mono);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--neon);
  background: rgba(7, 30, 61, .6);
  border-bottom: 1px solid rgba(39, 142, 165, .35);
}

#integrations .install-card pre {
  margin: 0;
  padding: clamp(1.25rem, 2.5vw, 1.75rem);
  overflow-x: auto;
  font-family: var(--mono);
  font-size: .88rem;
  line-height: 1.7;
  color: var(--text);
}
#integrations .install-card code { font-family: inherit; }
#integrations .install-card .kw  { color: var(--neon); font-weight: 600; }
#integrations .install-card .str { color: var(--text-bright); }

@media (max-width: 560px) {
  #integrations ul { grid-template-columns: repeat(2, 1fr); }
  #integrations li { font-size: .78rem; padding: 1rem .75rem; }
}

/* =========================================================================
   FAQ (#faq) — accordion with a smooth, CSS-only expand/collapse.
   <details> is kept for native semantics (keyboard, aria-expanded, no-JS
   fallback); the animation is the grid-template-rows 0fr→1fr trick, which
   is well supported without needing any bleeding-edge CSS or JS.
   ========================================================================= */
#faq > div {
  max-width: 96rem;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: .85rem;
}

#faq details {
  background: rgba(7, 30, 61, .92);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}
#faq details[open] {
  border-color: rgba(33, 230, 193, .6);
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .05),
    0 20px 40px -24px rgba(0, 0, 0, .6);
}

#faq summary {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1.1rem clamp(1.1rem, 2.5vw, 1.75rem);
  cursor: pointer;
  list-style: none;
  font-family: "Tenor Sans", var(--sans);
  font-size: 1rem;
  color: var(--text);
  transition: color .25s var(--ease);
  -webkit-tap-highlight-color: transparent;
}
#faq summary::-webkit-details-marker { display: none; }
#faq summary:hover,
#faq details[open] summary { color: var(--text-bright); }

/* custom + / × indicator, rotates instead of swapping glyphs */
#faq summary::after {
  content: "";
  flex: none;
  width: .8rem;
  height: .8rem;
  background:
    linear-gradient(var(--neon), var(--neon)) center / 100% 1.5px no-repeat,
    linear-gradient(var(--neon), var(--neon)) center / 1.5px 100% no-repeat;
  transition: transform .35s var(--ease);
}
#faq details.is-open summary::after { transform: rotate(45deg); }

/* --- height animation ------------------------------------------------------
   Handled in JS (templates/base.html) via the Web Animations API for
   reliable, symmetric open/close motion in both directions — the CSS-only
   grid-rows trick fought the browser's own <details> show/hide timing and
   never animated the close direction. Native <details> toggling (instant,
   no animation) is the no-JS fallback. */
#faq details { overflow: hidden; }

#faq .faq-body-inner p {
  margin: 0;
  padding: 0 clamp(1.1rem, 2.5vw, 1.75rem) 1.25rem;
  font-family: "Tenor Sans", var(--sans);
  font-size: .95rem;
  line-height: 1.65;
  color: var(--text);
  opacity: .85;
}

#faq .faq-body-inner a {
  color: var(--neon);
  text-decoration: underline;
  text-underline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  #faq summary::after { transition: none !important; }
}

/* =========================================================================
   Legal pages (#legal) — Privacy / Terms, shared markup+CSS. Terminal-
   log document reusing .code-window/.window-bar chrome (see
   #about-background) with each clause as a <details> accordion reusing
   #faq's card look + JS-driven open/close animation (base.html widens
   that handler's selector to "#faq details, #legal details"). Clause
   numbering is a pure CSS counter — no content-file text for it.
   ========================================================================= */
#legal {
  padding: clamp(3rem, 7vh, 5rem) var(--wrap-pad);
}

#legal .code-window {
  position: relative;
  max-width: 68rem;
  margin: 0 auto;
}

#legal .code-window::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 30%;
  top: -30%;
  background: linear-gradient(180deg, transparent, rgba(33, 230, 193, .06), transparent);
  animation: team-scan 7s ease-in-out infinite;
  pointer-events: none;
}

#legal .terminal-body {
  padding: clamp(1.75rem, 3.5vw, 2.75rem) clamp(1.5rem, 3vw, 2.5rem) clamp(2rem, 4vw, 3rem);
  counter-reset: legal-clause;
}

#legal .terminal-line {
  margin: 0 0 1.5rem;
  font-family: var(--mono);
  font-size: .85rem;
  color: var(--teal);
}
#legal .terminal-line:last-child { margin: 1.75rem 0 0; }

#legal .cursor {
  display: inline-block;
  width: .55em;
  height: 1em;
  background: var(--neon);
  vertical-align: -0.15em;
  animation: cursor-blink 1s steps(1) infinite;
}

#legal details {
  counter-increment: legal-clause;
  background: rgba(7, 30, 61, .92);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
  margin: 0 0 .75rem;
  overflow: hidden;
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}
#legal details:last-of-type { margin-bottom: 0; }
#legal details[open] {
  border-color: rgba(33, 230, 193, .6);
  box-shadow: inset 0 0 30px rgba(33, 230, 193, .05);
}

#legal summary {
  position: relative;
  display: flex;
  align-items: center;
  gap: .85rem;
  padding: 1rem clamp(1.1rem, 2.5vw, 1.5rem);
  cursor: pointer;
  list-style: none;
  font-family: "Tenor Sans", var(--sans);
  font-size: 1rem;
  color: var(--text);
  transition: color .25s var(--ease);
  -webkit-tap-highlight-color: transparent;
}
#legal summary::-webkit-details-marker { display: none; }
#legal summary:hover,
#legal details[open] summary { color: var(--text-bright); }

#legal summary::before {
  content: "[" counter(legal-clause, decimal-leading-zero) "]";
  flex: none;
  font-family: var(--mono);
  font-size: .8rem;
  color: var(--neon);
  opacity: .8;
}

#legal summary::after {
  content: "";
  margin-left: auto;
  flex: none;
  width: .8rem;
  height: .8rem;
  background:
    linear-gradient(var(--neon), var(--neon)) center / 100% 1.5px no-repeat,
    linear-gradient(var(--neon), var(--neon)) center / 1.5px 100% no-repeat;
  transition: transform .35s var(--ease);
}
#legal details.is-open summary::after { transform: rotate(45deg); }

#legal .legal-body-inner {
  padding: 0 clamp(1.1rem, 2.5vw, 1.5rem) 1.25rem;
}
#legal .legal-body-inner p,
#legal .legal-body-inner li {
  margin: 0 0 .75rem;
  font-family: "Tenor Sans", var(--sans);
  font-size: .95rem;
  line-height: 1.65;
  color: var(--text);
  opacity: .85;
}
#legal .legal-body-inner p:last-child,
#legal .legal-body-inner ul:last-child { margin-bottom: 0; }
#legal .legal-body-inner ul { margin: 0 0 .75rem; padding-left: 1.25rem; }
#legal .legal-body-inner strong { color: var(--text-bright); font-weight: 600; }
#legal .legal-body-inner a {
  color: var(--neon);
  text-decoration: underline;
  text-underline-offset: 2px;
}

@media (max-width: 700px) {
  #legal .terminal-body { padding: 1.5rem 1.25rem 1.75rem; }
}

@media (prefers-reduced-motion: reduce) {
  #legal .cursor { animation: none !important; opacity: 1 !important; }
  #legal .code-window::after { animation: none !important; display: none !important; }
  #legal summary::after { transition: none !important; }
}

/* =========================================================================
   Contact form (#contact-form) — terminal-prompt form. Reuses
   .code-window/.window-bar chrome (see #about-background) and the
   team-scan scanline (see #legal). Every interactive state (field
   validity, submit-button arming) is native CSS (:user-valid /
   :user-invalid, :has(), :valid on <form>). The one JS hook is the
   submit handler in content/contact.md: it disables the submit button,
   sets aria-busy on the form while sending, and sets data-state on
   #contact-form, which picks the line shown in the send-result toast.
   ========================================================================= */
#contact-form {
  padding: clamp(3rem, 7vh, 5rem) var(--wrap-pad);
}

#contact-form .code-window {
  position: relative;
  max-width: 44rem;
  margin: 0 auto;
  animation: contact-window-in .6s var(--ease) both;
}
@keyframes contact-window-in {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}

#contact-form .code-window::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 30%;
  top: -30%;
  background: linear-gradient(180deg, transparent, rgba(33, 230, 193, .06), transparent);
  animation: team-scan 8s ease-in-out infinite;
  pointer-events: none;
}

#contact-form .terminal-body {
  padding: clamp(1.75rem, 3.5vw, 2.75rem) clamp(1.5rem, 3vw, 2.5rem) clamp(2rem, 4vw, 3rem);
}

#contact-form h2,
#about-background h2,
#about-mission h2,
#about-goal h2 {
  margin: 0 0 1.25rem;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: clamp(1.7rem, 3.4vw, 2.4rem);
  color: var(--text-bright);
}

#contact-form hr {
  margin: 0 0 1.75rem;
  width: 3rem;
  height: 2px;
  border: none;
  background: linear-gradient(90deg, var(--teal), var(--neon));
}

#contact-form-el {
  display: flex;
  flex-direction: column;
  gap: 1.35rem;
}

#contact-form .field label {
  display: flex;
  align-items: center;
  gap: .4rem;
  margin: 0 0 .5rem;
  font-family: var(--mono);
  font-size: .82rem;
  letter-spacing: .02em;
  color: var(--teal);
}
#contact-form .field label::before {
  content: ">";
  color: var(--neon);
  font-weight: 600;
}
#contact-form .field label small {
  color: var(--text);
  opacity: .6;
  font-size: .78rem;
}

#contact-form input,
#contact-form select,
#contact-form textarea {
  width: 100%;
  padding: .75rem .9rem;
  background: rgba(4, 13, 28, .8);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
  font-family: "Tenor Sans", var(--sans);
  font-size: .95rem;
  color: var(--text-bright);
  caret-color: var(--neon);
  transition: border-color .25s var(--ease), box-shadow .25s var(--ease);
}
#contact-form textarea { resize: vertical; min-height: 8rem; }
#contact-form select { cursor: pointer; }

#contact-form input::placeholder,
#contact-form textarea::placeholder {
  color: var(--text);
  opacity: .4;
}

#contact-form input:focus,
#contact-form select:focus,
#contact-form textarea:focus {
  outline: none;
  border-color: var(--teal);
  box-shadow: 0 0 0 3px rgba(39, 142, 165, .15);
}

/* --- validation states, native CSS only -------------------------------- */
#contact-form .field:has(:user-valid) input,
#contact-form .field:has(:user-valid) select,
#contact-form .field:has(:user-valid) textarea {
  border-color: var(--neon);
  box-shadow: 0 0 0 3px rgba(33, 230, 193, .12);
}
#contact-form .field:has(:user-valid) label::after {
  content: "✓";
  color: var(--neon);
}

#contact-form .field:has(:user-invalid) input,
#contact-form .field:has(:user-invalid) select,
#contact-form .field:has(:user-invalid) textarea {
  border-color: var(--amber);
  box-shadow: 0 0 0 3px rgba(255, 176, 32, .12);
}
#contact-form .field:has(:user-invalid) label::after {
  content: "⚠";
  color: var(--amber);
}

/* --- submit -------------------------------------------------------------- */
#contact-form .field-submit {
  margin-top: .5rem;
  text-align: center;
}

#contact-form button[type="submit"] {
  padding: .85rem 1.75rem;
  background: transparent;
  border: 1px solid rgba(33, 230, 193, .35);
  border-radius: 2px;
  font-family: var(--mono);
  font-size: .9rem;
  letter-spacing: .02em;
  color: var(--text);
  opacity: .55;
  cursor: pointer;
  transition: opacity .3s var(--ease), border-color .3s var(--ease), box-shadow .3s var(--ease), color .3s var(--ease);
}

#contact-form-el:valid button[type="submit"] {
  opacity: 1;
  color: var(--neon);
  border-color: var(--neon);
  box-shadow: 0 0 24px -8px rgba(33, 230, 193, .6);
}

#contact-form button[type="submit"]:hover:not(:disabled), #contact-form button[type="submit"]:focus-visible {
  opacity: 1;
}

/* locked while sending and after a send — no pop-out on hover */
#contact-form button[type="submit"]:disabled { cursor: not-allowed; transform: none; }
#contact-form-el[aria-busy="true"] button[type="submit"] { cursor: progress; }

/* honeypot: off-screen rather than display:none, which some bots skip */
#contact-form .field-trap {
  position: absolute;
  left: -10000px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* --- send-result toast ------------------------------------------------------
   Always rendered, because it is the aria-live region; data-state on
   #contact-form (sent | limited | error) picks the line that shows. Showing
   slides in; hiding is instant so the box never collapses mid-fade. */
#contact-form .contact-toast {
  --toast-accent: var(--neon);
  position: fixed;
  right: clamp(1rem, 3vw, 2rem);
  bottom: clamp(1rem, 3vw, 2rem);
  z-index: 1100;                               /* above the sticky header (1000) */
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  max-width: min(26rem, calc(100vw - 2rem));
  padding: 1rem 1rem 1rem 1.25rem;
  background: var(--bg-deep);
  border: 1px solid rgba(39, 142, 165, .35);
  border-left: 2px solid var(--toast-accent);
  border-radius: 8px;
  box-shadow: inset 0 0 40px rgba(33, 230, 193, .05), 0 20px 40px -24px rgba(0, 0, 0, .6);
  font-family: var(--mono);
  font-size: .85rem;
  line-height: 1.5;
  opacity: 0;
  transform: translateY(1rem);
  pointer-events: none;
}
#contact-form:is([data-state="limited"], [data-state="error"]) .contact-toast { --toast-accent: var(--amber); }

#contact-form:is([data-state="sent"], [data-state="limited"], [data-state="error"]) .contact-toast {
  opacity: 1;
  transform: none;
  pointer-events: auto;
  transition: opacity .35s var(--ease), transform .35s var(--ease);
}

#contact-form .contact-toast p, #contact-form .contact-toast button { display: none; }
#contact-form[data-state="sent"] [data-for="sent"], #contact-form[data-state="limited"] [data-for="limited"], #contact-form[data-state="error"] [data-for="error"] { display: block; }
#contact-form:is([data-state="sent"], [data-state="limited"], [data-state="error"]) .contact-toast button { display: inline-flex; }

#contact-form .contact-toast p {
  margin: 0;
  color: var(--text-bright);
}
#contact-form .contact-toast p::before {
  content: "> ";
  color: var(--toast-accent);
  font-weight: 600;
}

/* dismiss — strip the global button chrome (gradient, inset frame, lift) */
#contact-form .contact-toast button {
  flex: none;
  margin-left: auto;
  padding: 0 .25rem;
  background: none;
  border: none;
  box-shadow: none;
  font-size: 1.1rem;
  letter-spacing: 0;
  line-height: 1.2;
  color: var(--text);
  opacity: .7;
}
#contact-form .contact-toast button::before { display: none; }
#contact-form .contact-toast button:hover, #contact-form .contact-toast button:focus-visible {
  opacity: 1;
  color: var(--neon);
  box-shadow: none;
  transform: none;
}
#contact-form .contact-toast button:focus-visible { outline: 1px solid var(--teal); outline-offset: 2px; }

@media (max-width: 700px) {
  #contact-form .terminal-body { padding: 1.5rem 1.25rem 1.75rem; }
  #contact-form .contact-toast { left: 1rem; right: 1rem; max-width: none; }
}

@media (prefers-reduced-motion: reduce) {
  #contact-form .code-window { animation: none !important; opacity: 1 !important; transform: none !important; }
  #contact-form .code-window::after { animation: none !important; display: none !important; }
  #contact-form .contact-toast { transition: none !important; transform: none !important; }
}

/* =========================================================================
   "Ready to ground your agents in truth?" (#cta) — closing call to action
   ========================================================================= */
#cta {
  position: relative;
  padding: clamp(5rem, 12vh, 9rem) var(--wrap-pad);
  text-align: center;
}

/* thin glowing divider echoing the section above, without a hard border */
#cta::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: min(70%, 42rem);
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(33, 230, 193, .5), transparent);
}

#cta > div {
  max-width: 40rem;
  margin: 0 auto;
}

#cta h1 {
  margin: 0 0 1.25rem;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: clamp(2rem, 4.5vw, 3.25rem);
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--text-bright);
}

#cta p {
  margin: 0 auto clamp(2rem, 4vh, 2.5rem);
  max-width: 42ch;
  font-family: "Tenor Sans", var(--sans);
  font-size: 1.05rem;
  line-height: 1.65;
  color: var(--text);
  opacity: .85;
}

#cta > div > div {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1rem;
}

/* =========================================================================
   Footer — brand + nav + copyright
   ========================================================================= */
footer {
  position: relative;
  padding: clamp(3rem, 7vh, 4rem) var(--wrap-pad) clamp(2rem, 4vh, 2.5rem);
  background: rgba(4, 13, 28, .6);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 1.5rem;
}

/* thin neon seam along the top edge, matching the cta divider above it */
footer::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(33, 230, 193, .45), transparent);
}

/* logo, stacked above the tagline */
footer > div:first-of-type {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .75rem;
}
footer > div:first-of-type img {
  height: 2.25rem;
  width: auto;
  filter: drop-shadow(0 0 8px rgba(33, 230, 193, .3));
}
footer > div:first-of-type span {
  font-family: "Tenor Sans", var(--sans);
  font-size: .92rem;
  color: var(--text);
  opacity: .8;
}

footer nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1.75rem;
}
footer nav a {
  font-family: var(--mono);
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--text);
  opacity: .75;
  transition: color .25s var(--ease), opacity .25s var(--ease);
}
footer nav a:hover,
footer nav a:focus-visible {
  color: var(--neon);
  opacity: 1;
}

footer > div:last-of-type {
  padding-top: 1.5rem;
  width: 100%;
  max-width: 24rem;
  border-top: 1px solid rgba(39, 142, 165, .15);
  font-family: var(--mono);
  font-size: .7rem;
  letter-spacing: .04em;
  color: var(--text);
  opacity: .45;
}


/* =========================================================================
   "Compiler-Truth Graph" (#compiler-truth-graph) — titled code window,
   features page only. Uses the shared .code-window component (see
   .code-window above).
   ========================================================================= */
#compiler-truth-graph {
  padding: clamp(3rem, 7vh, 5rem) var(--wrap-pad);
}

#compiler-truth-graph .code-window {
  max-width: 44rem;
  margin: 0 auto;
}

/* --- typewriter -----------------------------------------------------------
   Pure-CSS terminal typing. Every code line is its own block that grows from
   0 to its exact character count, stepped one glyph at a time, with
   border-right standing in for the caret. All six lines ride the same 12.6s
   cycle so they stay in sync: ~1s of blinking empty prompt, ~8.6s of typing,
   3s with the finished snippet on screen, then an instant restart.
   -------------------------------------------------------------------------- */
#compiler-truth-graph pre code {
  display: block;
  white-space: normal; /* the newlines between the line spans collapse away */
}

#compiler-truth-graph .ln {
  display: block;
  box-sizing: content-box; /* the caret border must not eat 2px of the text */
  width: max-content;
  white-space: pre;
  overflow: hidden;
  border-right: 2px solid transparent;
}

#compiler-truth-graph .ln-1 { animation: ctg-type-1 12.6s steps(34) infinite, ctg-caret-1 12.6s steps(1) infinite; }
#compiler-truth-graph .ln-2 { animation: ctg-type-2 12.6s steps(58) infinite, ctg-caret-2 12.6s steps(1) infinite; }
#compiler-truth-graph .ln-3 { animation: ctg-type-3 12.6s steps(41) infinite, ctg-caret-3 12.6s steps(1) infinite; }
#compiler-truth-graph .ln-4 { animation: ctg-type-4 12.6s steps(51) infinite, ctg-caret-4 12.6s steps(1) infinite; }
#compiler-truth-graph .ln-5 { animation: ctg-type-5 12.6s steps(28) infinite, ctg-caret-5 12.6s steps(1) infinite; }
#compiler-truth-graph .ln-6 { animation: ctg-type-6 12.6s steps(55) infinite, ctg-caret-6 12.6s steps(1) infinite; }

/* width: 0 -> full, only inside each line's own slot of the shared cycle */
@keyframes ctg-type-1 { 0%, 7.94%  { width: 0; } 14.95%, 100% { width: 34ch; } }
@keyframes ctg-type-2 { 0%, 16.70% { width: 0; } 28.67%, 100% { width: 58ch; } }
@keyframes ctg-type-3 { 0%, 32.63% { width: 0; } 41.10%, 100% { width: 41ch; } }
@keyframes ctg-type-4 { 0%, 42.84% { width: 0; } 53.37%, 100% { width: 51ch; } }
@keyframes ctg-type-5 { 0%, 57.33% { width: 0; } 63.11%, 100% { width: 28ch; } }
@keyframes ctg-type-6 { 0%, 64.86% { width: 0; } 76.21%, 100% { width: 55ch; } }

/* caret: one line owns it at a time, so it reads as a single cursor moving
   down the block. steps(1) makes every colour change a hard on/off. */
@keyframes ctg-caret-1 {
  0%     { border-right-color: var(--neon); }
  3.17%  { border-right-color: transparent; }
  5.56%  { border-right-color: var(--neon); }
  16.70% { border-right-color: transparent; }
}
@keyframes ctg-caret-2 {
  0%     { border-right-color: transparent; }
  16.70% { border-right-color: var(--neon); }
  32.63% { border-right-color: transparent; }
}
@keyframes ctg-caret-3 {
  0%     { border-right-color: transparent; }
  32.63% { border-right-color: var(--neon); }
  42.84% { border-right-color: transparent; }
}
@keyframes ctg-caret-4 {
  0%     { border-right-color: transparent; }
  42.84% { border-right-color: var(--neon); }
  57.33% { border-right-color: transparent; }
}
@keyframes ctg-caret-5 {
  0%     { border-right-color: transparent; }
  57.33% { border-right-color: var(--neon); }
  64.86% { border-right-color: transparent; }
}
/* last line keeps the caret through the 3s hold, blinking again */
@keyframes ctg-caret-6 {
  0%     { border-right-color: transparent; }
  64.86% { border-right-color: var(--neon); }
  79.19% { border-right-color: transparent; }
  82.17% { border-right-color: var(--neon); }
  85.15% { border-right-color: transparent; }
  88.13% { border-right-color: var(--neon); }
  91.11% { border-right-color: transparent; }
  94.09% { border-right-color: var(--neon); }
  97.07% { border-right-color: transparent; }
}

@media (prefers-reduced-motion: reduce) {
  #compiler-truth-graph .ln {
    animation: none !important;
    border-right-color: transparent !important;
  }
}

/* =========================================================================
   "Features" (#features-grid) — 3 parallel capability cards, side by side
   ========================================================================= */
#features-grid {
  padding: clamp(3rem, 7vh, 5rem) var(--wrap-pad);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.25rem, 3vw, 2rem);
  align-items: stretch;
}

#features-grid > div {
  padding: clamp(1.5rem, 3vw, 2.25rem);
  background: rgba(7, 30, 61, .92);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .05),
    0 20px 40px -24px rgba(0, 0, 0, .6);
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}
#features-grid > div:hover {
  border-color: rgba(33, 230, 193, .6);
  box-shadow:
    inset 0 0 55px rgba(33, 230, 193, .09),
    0 24px 48px -22px rgba(0, 0, 0, .7);
}

#features-grid h2 {
  margin: 0 0 1rem;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: 1.3rem;
  color: var(--text-bright);
}

#features-grid hr {
  margin: 0 0 1.1rem;
  width: 2.5rem;
  height: 2px;
  border: none;
  background: linear-gradient(90deg, var(--neon), transparent);
}

#features-grid p {
  margin: 0;
  font-family: var(--sans);
  font-size: .95rem;
  line-height: 1.65;
  color: var(--text);
  opacity: .85;
}

@media (max-width: 860px) {
  #features-grid { grid-template-columns: 1fr; }
}

/* =========================================================================
   "Most efficient way to save tokens" (#token-savings) — full-width
   pull-quote band; opens the features page on its own headline, with the
   How It Works timeline below it
   ========================================================================= */
#token-savings {
  position: relative;
  padding: clamp(3.5rem, 8vh, 6rem) var(--wrap-pad);
}

#token-savings::after {
  content: "";
  position: absolute;
  left: 50%;
  width: min(40rem, 80%);
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--teal), transparent);
  transform: translateX(-50%);
  bottom: 0;
}

#token-savings h2 {
  margin: 0 auto;
  max-width: 32ch;
  text-align: center;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: clamp(1.6rem, 3.4vw, 2.4rem);
  line-height: 1.3;
  color: var(--text-bright);
}


#token-savings p {
  margin: 1.5rem auto 0;
  max-width: 46ch;
  text-align: center;
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.65;
  color: var(--text);
  opacity: .85;
}

/* =========================================================================
   "How It Works" steps (#how-it-works-steps) — alternating zigzag timeline,
   4 steps either side of a central spine. Features page only — do not
   confuse with the homepage's separate #how-it-works dependency-graph
   section (different id, different content).
   ========================================================================= */
#how-it-works-steps {
  padding: clamp(5rem, 12vh, 9rem) var(--wrap-pad);
}

#how-it-works-steps > div > h2 {
  padding: 0;
  margin: 0 auto clamp(2.5rem, 5vh, 4rem);
  text-align: center;
  max-width: 22ch;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: clamp(1.9rem, 4vw, 3rem);
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--text-bright);
}

#how-it-works-steps > div > hr {
  margin: 0 auto clamp(2.5rem, 5vh, 4rem);
  width: 3rem;
  height: 2px;
  border: none;
  background: linear-gradient(90deg, var(--teal), var(--neon));
}

#how-it-works-steps ol {
  position: relative;
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: 56rem;
}

#how-it-works-steps ol::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 2px;
  background: linear-gradient(180deg, var(--teal), var(--neon));
  transform: translateX(-50%);
}

#how-it-works-steps li {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 3rem 1fr;
  align-items: center;
  column-gap: clamp(1.5rem, 4vw, 3rem);
  padding-block: clamp(1.75rem, 5vh, 3rem);
}

#how-it-works-steps li::before {
  content: "";
  grid-column: 2;
  justify-self: center;
  width: .85rem;
  height: .85rem;
  border-radius: 50%;
  background: var(--neon);
  box-shadow: 0 0 12px rgba(33, 230, 193, .7);
}

#how-it-works-steps li > div {
  grid-column: 1;
  text-align: right;
}
#how-it-works-steps li:nth-child(even) > div {
  grid-column: 3;
  text-align: left;
}

#how-it-works-steps .step-num {
  display: block;
  margin-bottom: .5rem;
  font-family: var(--mono);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--teal);
}

#how-it-works-steps li > div > p > strong {
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: 1.3rem;
  color: var(--text-bright);
}

#how-it-works-steps li > div > p > span:last-child {
  display: block;
  margin-top: .5rem;
  font-family: var(--sans);
  font-size: .95rem;
  line-height: 1.6;
  color: var(--text);
  opacity: .85;
}

/* --- motion: the light walks the spine, step 01 down to step 04 -----------
   Same idiom as the homepage graph's node sequence (#how-it-works): the peak
   sits on the 0%/100% boundary, which is what lets each dot be placed with
   nothing but a negative delay of -(1 - arrival) x cycle. Arrivals are .8s
   apart, then a 1.6s dark beat before the light returns to the top. Markup
   order is traversal order here, so the nth-child indexes read straight. */
#how-it-works-steps li::before { animation: steps-lit 4s linear infinite; }
#how-it-works-steps li:nth-child(2)::before { animation-delay: -3.2s; }
#how-it-works-steps li:nth-child(3)::before { animation-delay: -2.4s; }
#how-it-works-steps li:nth-child(4)::before { animation-delay: -1.6s; }

/* the dark state repeats the static values above exactly, so the loop seam is
   invisible. Both shadows carry an explicit spread so the two keyframes
   interpolate as the same shape. */
@keyframes steps-lit {
  0%, 100% {
    background: var(--text-bright);
    box-shadow: 0 0 22px 3px rgba(33, 230, 193, .95);
    transform: scale(1.35);
  }
  18%, 94% {
    background: var(--neon);
    box-shadow: 0 0 12px 0 rgba(33, 230, 193, .7);
    transform: scale(1);
  }
}

@media (max-width: 700px) {
  #how-it-works-steps ol::before { left: 1.5rem; }
  #how-it-works-steps li {
    grid-template-columns: 3rem 1fr;
    column-gap: 1.25rem;
  }
  #how-it-works-steps li::before { grid-column: 1; }
  #how-it-works-steps li > div,
  #how-it-works-steps li:nth-child(even) > div {
    grid-column: 2;
    text-align: left;
  }
}

@media (prefers-reduced-motion: reduce) {
  #how-it-works-steps li::before { animation: none !important; }
}

/* =========================================================================
   "Project Background" (#about-background) — full-width terminal
   boot-log, about page only. Reuses the shared .code-window / .window-bar
   chrome (see .code-window above / features.md #compiler-truth-graph)
   but stretched to the widest content box on the page — this section is
   deliberately the "big wide" one.
   ========================================================================= */
#about-background {
  padding: clamp(3rem, 7vh, 5rem) var(--wrap-pad);
}

#about-background .code-window {
  max-width: 76rem;          /* deliberately wider than #about-goal/#team's 68rem — the "big wide" section */
  margin: 0 auto;
}

#about-background .terminal-body {
  padding: clamp(1.75rem, 3.5vw, 2.75rem) clamp(1.5rem, 3vw, 2.5rem) clamp(2rem, 4vw, 3rem);
}

#about-background .terminal-line {
  margin: 0 0 1rem;
  font-family: var(--mono);
  font-size: .85rem;
  color: var(--teal);
}
#about-background .terminal-line:last-child { margin: 1.5rem 0 0; }

#about-background .cursor {
  display: inline-block;
  width: .55em;
  height: 1em;
  background: var(--neon);
  vertical-align: -0.15em;
  animation: cursor-blink 1s steps(1) infinite;
}
@keyframes cursor-blink { 50% { opacity: 0; } }


#about-background hr,
#about-mission hr,
#about-goal hr {
  margin: 0 0 1.5rem;
  width: 3rem;
  height: 2px;
  border: none;
  background: linear-gradient(90deg, var(--teal), var(--neon));
}

#about-background .terminal-body > p:not(.terminal-line) {
  margin: 0 0 1rem;
  max-width: 68ch;
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.75;
  color: var(--text);
  opacity: .85;
}

@media (max-width: 700px) {
  #about-background .terminal-body {
    padding: 1.5rem 1.25rem 1.75rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  #about-background .cursor { animation: none !important; opacity: 1 !important; }
}

/* =========================================================================
   "Project Mission" (#about-mission) — a manifesto card, about page only.
   Sits between the problem charts (#why) and the reticle row (#about-goal)
   — the answer to what #why just showed — so it borrows .work-order
   (#pilot) rather than inventing a third panel style: same fill, same teal
   border, same 27px clipped top-right corner, same mono eyebrow + dot.
   Width steps down from #about-background's 76rem to 60rem so it reads as
   a narrowing of focus rather than a competing "big wide" block.
   ========================================================================= */
#about-mission {
  padding: clamp(3rem, 7vh, 5rem) var(--wrap-pad);
}

.manifesto {
  position: relative;
  max-width: 60rem;
  margin: 0 auto;
  padding: clamp(1.75rem, 4vw, 2.75rem);
  background: rgba(7, 30, 61, .92);
  border: 1px solid rgba(33, 230, 193, .45);
  border-radius: 2px;
  clip-path: polygon(0 0, calc(100% - 27px) 0, 100% 27px, 100% 100%, 0 100%);
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .06),
    0 20px 44px -24px rgba(0, 0, 0, .7);
}

/* eyebrow — same recipe as .wo-head / .wo-dot on #pilot */
.mf-head {
  display: flex;
  align-items: center;
  gap: .65rem;
  margin: 0 0 1.4rem;
  font-family: var(--mono);
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--neon);
}
.mf-dot {
  width: .6rem;
  height: .6rem;
  border-radius: 50%;
  background: var(--neon);
  box-shadow: 0 0 8px var(--neon);
}



/* the mission statement itself — the loudest sentence in the card */
.mf-lead {
  margin: 0 0 1.5rem;
  max-width: 34ch;
  font-family: "Tenor Sans", var(--sans);
  font-size: clamp(1.25rem, 2.4vw, 1.7rem);
  line-height: 1.35;
  color: var(--text-bright);
}

/* body copy — same treatment as the sibling about sections */
.manifesto > p:not(.mf-head):not(.mf-lead) {
  margin: 0;
  max-width: 62ch;
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.75;
  color: var(--text);
  opacity: .85;
}

@media (max-width: 700px) {
  .manifesto { padding: 1.5rem 1.25rem 1.75rem; }
}

/* =========================================================================
   "Project Goal" (#about-goal) — 3 HUD targeting-reticle bullets in a
   staggered row, about page only. Deliberately asymmetric (not a plain
   equal-height grid like #features-grid) via nth-child margin offsets.
   ========================================================================= */
#about-goal {
  padding: clamp(3rem, 7vh, 5rem) var(--wrap-pad);
}

#about-goal > div {
  max-width: 68rem;
  margin: 0 auto;
}



#about-goal > div > p {
  margin: 0 0 3rem;
  max-width: 62ch;
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.75;
  color: var(--text);
  opacity: .85;
}

#about-goal ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: flex-start;
}

#about-goal li {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
#about-goal li:nth-child(2) { margin-top: 2.25rem; }
#about-goal li:nth-child(3) { margin-top: .5rem; }

#about-goal .reticle {
  position: relative;
  width: 5rem;
  height: 5rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 0;
}
#about-goal .reticle-line {
  width: 1px;
  height: 1.25rem;
  margin-bottom: 1rem;
  background: linear-gradient(180deg, var(--neon), transparent);
}
#about-goal .reticle::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1px dashed rgba(33, 230, 193, .45);
  animation: reticle-spin 14s linear infinite;
}
#about-goal .reticle::after {
  content: "";
  position: absolute;
  inset: -8px;
  border-radius: 50%;
  border: 1px solid rgba(39, 142, 165, .3);
}
@keyframes reticle-spin { to { transform: rotate(360deg); } }

#about-goal .reticle span {
  position: relative;
  width: 3.1rem;
  height: 3.1rem;
  border-radius: 50%;
  background: rgba(7, 30, 61, .92);
  border: 1px solid var(--neon);
  box-shadow: 0 0 18px rgba(33, 230, 193, .45), inset 0 0 12px rgba(33, 230, 193, .15);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 1rem;
  color: var(--neon);
}

#about-goal li p {
  margin: 0;
  max-width: 26ch;
  font-family: var(--sans);
  font-size: .95rem;
  line-height: 1.65;
  color: var(--text);
  opacity: .85;
}

@media (max-width: 700px) {
  #about-goal ul { flex-direction: column; gap: 2.5rem; }
  #about-goal li:nth-child(2),
  #about-goal li:nth-child(3) { margin-top: 0; }
}

@media (prefers-reduced-motion: reduce) {
  #about-goal .reticle::before { animation: none !important; }
}

/* =========================================================================
   "Meet the Team" (#team) — holographic clearance-badge cards, one row.
   About page only. Each card gets a distinct accent color + a distinct
   clipped corner via nth-child so the row doesn't read as 3 identical
   clones — see spec table (docs/superpowers/specs/2026-07-26-about-us-
   page-design.md) for which person gets which accent/corner.
   ========================================================================= */
#team {
  padding: clamp(3rem, 7vh, 5rem) var(--wrap-pad) clamp(4rem, 9vh, 6rem);
}

#team > div {
  max-width: 68rem;
  margin: 0 auto;
}

#team h1 {
  margin: 0 0 .75rem;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: clamp(1.9rem, 4vw, 3rem);
  color: var(--text-bright);
}

#team > div > p {
  margin: 0 0 3rem;
  max-width: 60ch;
  font-family: var(--sans);
  font-size: 1rem;
  line-height: 1.7;
  color: var(--text);
  opacity: .85;
}

.team-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(1.25rem, 3vw, 2rem);
  align-items: stretch;
}

.team-card {
  --accent: var(--neon);
  position: relative;
  background: rgba(7, 30, 61, .92);
  border: 1px solid var(--accent);
  border-radius: 2px;
  padding: clamp(1.5rem, 3vw, 2rem) clamp(1.25rem, 2.5vw, 1.75rem) clamp(1.5rem, 3vw, 1.75rem);
  overflow: hidden;
  box-shadow:
    0 20px 40px -24px rgba(0, 0, 0, .7),
    inset 0 0 30px rgba(33, 230, 193, .08);
}
.team-grid > .team-card:nth-child(1) {
  --accent: var(--neon);
  clip-path: polygon(0 0, calc(100% - 18px) 0, 100% 18px, 100% 100%, 0 100%);
}
.team-grid > .team-card:nth-child(2) {
  --accent: var(--amber);
  clip-path: polygon(0 0, 100% 0, 100% 100%, 18px 100%, 0 calc(100% - 18px));
}
.team-grid > .team-card:nth-child(3) {
  --accent: var(--violet);
  clip-path: polygon(18px 0, 100% 0, 100% 100%, 0 100%, 0 18px);
}
.team-grid > .team-card:nth-child(4) {
  --accent: var(--magenta);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 18px), calc(100% - 18px) 100%, 0 100%);
}

.team-card::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 40%;
  top: -40%;
  background: linear-gradient(180deg, transparent, color-mix(in srgb, var(--accent) 22%, transparent), transparent);
  animation: team-scan 5s ease-in-out infinite;
  pointer-events: none;
}
@keyframes team-scan { 0% { top: -40%; } 60%, 100% { top: 120%; } }

.team-clearance {
  display: flex;
  align-items: center;
  gap: .45rem;
  margin: 0 0 1.25rem;
  font-family: var(--mono);
  font-size: .68rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--accent);
}
.team-clearance-dot {
  width: .4rem;
  height: .4rem;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent);
}

.team-avatar-ring {
  width: 4.5rem;
  height: 4.5rem;
  border-radius: 50%;
  border: 1px solid var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 1.1rem;
  box-shadow: 0 0 16px color-mix(in srgb, var(--accent) 55%, transparent);
}
.team-avatar-ring > div {
  width: 3.6rem;
  height: 3.6rem;
  border-radius: 50%;
  background: rgba(4, 13, 28, .9);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--mono);
  font-size: 1.05rem;
  color: var(--accent);
}

.team-card h3 {
  margin: 0 0 .3rem;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: 1.15rem;
  color: var(--text-bright);
}

.team-card .team-role {
  display: block;
  margin: 0 0 .9rem;
  font-family: var(--mono);
  font-size: .72rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--accent);
}

.team-card .team-bio {
  margin: 0 0 1.1rem;
  font-family: var(--sans);
  font-size: .87rem;
  line-height: 1.6;
  color: var(--text);
  opacity: .82;
}

.team-bars {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 1rem;
  opacity: .55;
}
.team-bars span { width: 2px; background: var(--accent); }
.team-bars span:nth-child(1) { height: 100%; }
.team-bars span:nth-child(2) { height: 60%; }
.team-bars span:nth-child(3) { height: 80%; }
.team-bars span:nth-child(4) { height: 40%; }
.team-bars span:nth-child(5) { height: 90%; }
.team-bars span:nth-child(6) { height: 55%; }
.team-bars span:nth-child(7) { height: 70%; }

@media (max-width: 860px) {
  .team-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 540px) {
  .team-grid { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  .team-card::after { animation: none !important; display: none !important; }
}

/* =========================================================================
   Pricing tiers (#pricing-grid) — holographic "access tier" cards,
   pricing page only. Reuses the Team page's clearance-badge shell
   (accent color, team-scan sweep, barcode strip — see
   .team-card) under fresh #pricing-grid-scoped classes so this page
   doesn't couple to #team's rules. All three share the same rectangular
   shell and differ only by accent colour; the featured (Indie) card is
   what stands out, via a heavier border, an accent halo and a transform
   lift above its neighbours, while a shared "circuit trace" line runs
   beneath the row, connecting all three at their common
   (align-items: end) baseline. Card scale +50% and collapse breakpoint
   raised accordingly (bigger cards need more room before 3-across gets
   cramped); button text drops the site-wide uppercase/wide-tracking
   treatment for these specifically.
   ========================================================================= */
#pricing-grid {
  /* top padding also clears the featured card's lift */
  padding: clamp(4.5rem, 10vh, 7rem) var(--wrap-pad) clamp(3rem, 7vh, 5rem);
}

.tier-grid {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(2.25rem, 4.5vw, 3.4rem);
  align-items: end;
  max-width: 111rem;
  margin: 0 auto;
  padding-bottom: 3.75rem;
}

.tier-grid::before {
  content: "";
  position: absolute;
  left: 4%;
  right: 4%;
  bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(39, 142, 165, .5) 15%, rgba(39, 142, 165, .5) 85%, transparent);
}

.tier-card {
  --accent: var(--neon);
  position: relative;
  z-index: 0;
  display: flex;
  flex-direction: column;
  background: rgba(7, 30, 61, .92);
  border: 1px solid var(--accent);
  border-radius: 2px;
  padding: clamp(2.6rem, 4.5vw, 3.4rem) clamp(2.25rem, 4.5vw, 2.8rem) clamp(2.6rem, 4.5vw, 3rem);
  overflow: hidden;
  box-shadow:
    0 20px 40px -24px rgba(0, 0, 0, .7),
    inset 0 0 30px color-mix(in srgb, var(--accent) 8%, transparent);
}

/* the three tiers differ by accent colour only — same rectangular shell */
.tier-grid > .tier-card:nth-child(1) { --accent: var(--teal); }
.tier-grid > .tier-card:nth-child(2) { --accent: var(--neon); }
.tier-grid > .tier-card:nth-child(3) { --accent: var(--violet); }

.tier-card-featured {
  z-index: 1;
  transform: translateY(-3.75rem);
  border-width: 2px;
  box-shadow:
    0 30px 60px -18px rgba(0, 0, 0, .85),
    0 0 0 1px var(--accent),
    0 0 46px -6px color-mix(in srgb, var(--accent) 45%, transparent),
    inset 0 0 55px color-mix(in srgb, var(--accent) 18%, transparent);
}

.tier-card::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 40%;
  top: -40%;
  background: linear-gradient(180deg, transparent, color-mix(in srgb, var(--accent) 20%, transparent), transparent);
  animation: team-scan 6s ease-in-out infinite;
  pointer-events: none;
}

.tier-card .tier-clearance {
  display: flex;
  align-items: center;
  gap: .68rem;
  margin: 0 0 1.65rem;
  font-family: var(--mono);
  font-size: 1.02rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--accent);
}
.tier-card .tier-clearance-dot {
  width: .6rem;
  height: .6rem;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent);
}

.tier-card h2 {
  margin: 0 0 .45rem;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: 1.95rem;
  color: var(--text-bright);
}

/* --- price block ---------------------------------------------------------- */
/* Sits between the tier name and the accent rule, so name and number read as
   one unit AND so `.tier-card > hr + p` below still resolves to the
   description paragraph rather than to a price. The quota line is set nearly
   as loudly as the price because it is the number that actually separates the
   tiers — 500 vs 5,000 vs unlimited is the decision, the dollar figure is not.
   Both inherit --accent from the card, so each tier keeps its own colour. */
.tier-price {
  display: flex;
  align-items: baseline;
  gap: .4rem;
  margin: 0 0 .5rem;
}
.tier-amount {
  font-family: "Tenor Sans", var(--sans);
  font-size: 3.1rem;
  line-height: 1;
  color: var(--accent);
  text-shadow: 0 0 22px color-mix(in srgb, var(--accent) 35%, transparent);
}
.tier-period {
  font-family: var(--mono);
  font-size: .95rem;
  letter-spacing: .08em;
  color: var(--text);
  opacity: .75;
}
.tier-quota {
  margin: 0 0 1.6rem;
  font-family: var(--mono);
  font-size: 1.02rem;
  letter-spacing: .06em;
  color: var(--text-bright);
  opacity: .9;
}

.tier-card hr {
  margin: 0 0 1.9rem;
  width: 3.75rem;
  height: 2px;
  border: none;
  background: var(--accent);
}

.tier-card > hr + p {
  margin: 0 0 2.25rem;
  font-family: "Tenor Sans", var(--sans);
  font-size: 1.35rem;
  line-height: 1.6;
  color: var(--text);
  opacity: .8;
}

.tier-specs ul {
  list-style: none;
  margin: 0 0 2.25rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: .83rem;
}
.tier-specs li {
  position: relative;
  padding-left: 1.65rem;
  font-family: var(--mono);
  font-size: 1.23rem;
  line-height: 1.5;
  color: var(--text);
  opacity: .85;
}
.tier-specs li::before {
  content: "▸";
  position: absolute;
  left: 0;
  color: var(--accent);
}

.tier-bars {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 1.5rem;
  margin: 0 0 2.25rem;
  opacity: .55;
}
.tier-bars span { width: 3px; background: var(--accent); }
.tier-bars span:nth-child(1) { height: 100%; }
.tier-bars span:nth-child(2) { height: 60%; }
.tier-bars span:nth-child(3) { height: 80%; }
.tier-bars span:nth-child(4) { height: 40%; }
.tier-bars span:nth-child(5) { height: 90%; }
.tier-bars span:nth-child(6) { height: 55%; }
.tier-bars span:nth-child(7) { height: 70%; }

.tier-card button {
  margin-top: auto;
  width: 100%;
  padding: 1.4rem 2.5rem;
  font-size: 1.23rem;
  text-transform: none;
  letter-spacing: normal;
  border-color: var(--accent);
  background: linear-gradient(180deg, var(--accent), color-mix(in srgb, var(--accent) 55%, var(--navy)));
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--accent) 30%, transparent),
    0 10px 26px -10px color-mix(in srgb, var(--accent) 60%, transparent);
}
.tier-card button a {
  color: var(--ink);
  text-decoration: none;
}
.tier-card button:hover,
.tier-card button:focus-visible {
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--accent) 55%, transparent),
    0 16px 36px -10px color-mix(in srgb, var(--accent) 75%, transparent);
}

@media (max-width: 1180px) {
  .tier-grid {
    grid-template-columns: 1fr;
    padding-bottom: 0;
  }
  .tier-grid::before { display: none; }
  .tier-card-featured { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .tier-card::before { animation: none !important; display: none !important; }
}


/* =========================================================================
   ENTERPRISE PAGE
   Sections below belong to /enterprise/, in page order:
   blindspot · capabilities · integration · hosting · trust · pilot · cta extras.
   #blindspot used to render on the home page too, but no longer does — it is
   now enterprise-only, like everything else in this block.
   The page reuses #faq and #cta wholesale, so those need nothing here.
   ========================================================================= */

/* --- shared: section titles + the site's gradient highlight ---------------- */

/* --- shared: promoted link-button -----------------------------------------
   The site's primary CTA is a <button>, but every enterprise CTA navigates,
   so it has to be an <a>. .is-primary lifts a[role="button"] to the primary
   fill rather than nesting an <a> inside a <button>.
   ------------------------------------------------------------------------- */
a[role="button"].is-primary {
  color: var(--ink);
  background: linear-gradient(180deg, var(--neon), var(--teal));
  border-color: var(--neon);
  box-shadow:
    0 0 0 1px rgba(33, 230, 193, .25),
    0 10px 30px -8px rgba(33, 230, 193, .5);
}
a[role="button"].is-primary::before { border-color: rgba(4, 18, 31, .5); opacity: .5; }
a[role="button"].is-primary:hover,
a[role="button"].is-primary:focus-visible {
  background: linear-gradient(180deg, var(--neon), var(--teal));
  border-color: var(--neon);
  box-shadow:
    0 0 0 1px rgba(33, 230, 193, .5),
    0 16px 42px -8px rgba(33, 230, 193, .7);
}

/* =========================================================================
   "The code your agents have never seen" (#blindspot) — opens the enterprise
   page. One SVG split by a dashed line:
   a lit, fully connected cluster of public code on the left; a field of
   dashed unknowns on the right. Three edges reach across and die at the
   boundary. Same CSS/SVG motion vocabulary as #why and #how-it-works —
   no JS, and deliberately no invented statistics.
   ========================================================================= */
/* glass panel, same recipe as #why figure / #how-it-works figure */
#blindspot figure {
  margin: 0 auto;
  max-width: 60rem;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  padding: clamp(1.5rem, 3vw, 2.5rem);
  background: rgba(7, 30, 61, .92);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .05),
    0 20px 40px -24px rgba(0, 0, 0, .6);
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}
#blindspot figure:hover {
  border-color: rgba(33, 230, 193, .6);
  box-shadow:
    inset 0 0 55px rgba(33, 230, 193, .09),
    0 24px 48px -22px rgba(0, 0, 0, .7);
}

#blindspot figcaption {
  font-family: "Tenor Sans", var(--sans);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--text);
  opacity: .85;
}
#blindspot figcaption p { margin: 0; }

/* --- blind-spot graph primitives ------------------------------------------ */

#blindspot .bs-region {
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
}
#blindspot .bs-region-known { fill: var(--neon); }
#blindspot .bs-region-dark { fill: var(--text); opacity: .45; }

#blindspot .bs-boundary {
  stroke: var(--teal);
  stroke-width: 1.5;
  stroke-dasharray: 7 7;
  opacity: .55;
}
#blindspot .bs-boundary-label {
  fill: var(--text);
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: .12em;
  text-transform: uppercase;
  opacity: .45;
}

/* left — public code the model has actually seen */
#blindspot .bs-known-edges line { stroke: var(--neon); stroke-width: 1.75; opacity: .55; }
#blindspot .bs-known-nodes circle {
  fill: var(--bg-deep);
  stroke: var(--neon);
  stroke-width: 2.5;
  filter: drop-shadow(0 0 5px rgba(33, 230, 193, .5));
}

/* edges that reach toward private code and fade out at the boundary */
#blindspot .bs-cross line { stroke: url(#bsFade); stroke-width: 2; stroke-linecap: round; }

/* right — private code: present, connected, and entirely unknown */
#blindspot .bs-dark-edges line {
  stroke: var(--text);
  stroke-width: 1.25;
  stroke-dasharray: 4 5;
  opacity: .2;
}
#blindspot .bs-dark-nodes circle {
  fill: rgba(4, 13, 28, .8);
  stroke: var(--text);
  stroke-width: 1.5;
  stroke-dasharray: 3 4;
}
#blindspot .bs-dark-nodes text {
  fill: var(--text);
  font-family: var(--mono);
  font-size: 14px;
  font-weight: 700;
}

/* --- motion: the known side draws itself in; the unknown side only wavers -- */
#blindspot .bs-known-edges line {
  stroke-dasharray: 400;
  stroke-dashoffset: 400;
  animation: bs-draw 1s var(--ease) forwards;
}
#blindspot .bs-known-edges line:nth-child(2) { animation-delay: .07s; }
#blindspot .bs-known-edges line:nth-child(3) { animation-delay: .14s; }
#blindspot .bs-known-edges line:nth-child(4) { animation-delay: .21s; }
#blindspot .bs-known-edges line:nth-child(5) { animation-delay: .28s; }
#blindspot .bs-known-edges line:nth-child(6) { animation-delay: .35s; }
#blindspot .bs-known-edges line:nth-child(7) { animation-delay: .42s; }
#blindspot .bs-known-edges line:nth-child(8) { animation-delay: .49s; }
#blindspot .bs-known-edges line:nth-child(9) { animation-delay: .56s; }

#blindspot .bs-known-nodes circle {
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0;
  animation: bs-pop .4s var(--ease) .55s forwards;
}

#blindspot .bs-cross line {
  stroke-dasharray: 600;
  stroke-dashoffset: 600;
  animation: bs-draw 1.4s var(--ease) .8s forwards;
}

#blindspot .bs-dark-nodes g { animation: bs-waver 7s ease-in-out infinite; }
#blindspot .bs-dark-nodes g:nth-child(2) { animation-delay: -1.1s; }
#blindspot .bs-dark-nodes g:nth-child(3) { animation-delay: -2.3s; }
#blindspot .bs-dark-nodes g:nth-child(4) { animation-delay: -3.4s; }
#blindspot .bs-dark-nodes g:nth-child(5) { animation-delay: -4.2s; }
#blindspot .bs-dark-nodes g:nth-child(6) { animation-delay: -5.1s; }
#blindspot .bs-dark-nodes g:nth-child(7) { animation-delay: -6s; }
#blindspot .bs-dark-nodes g:nth-child(8) { animation-delay: -2.8s; }

@keyframes bs-draw { to { stroke-dashoffset: 0; } }
@keyframes bs-pop { from { opacity: 0; transform: scale(0); } to { opacity: 1; transform: scale(1); } }
@keyframes bs-waver {
  0%, 100% { opacity: .42; }
  38% { opacity: .22; }
  44% { opacity: .5; }
  52% { opacity: .26; }
}

/* --- two packets circling the known side ---------------------------------
   The point of the whole figure is that traffic stops at the boundary, so
   nothing moves on the right: these run only on the bright cluster. Two is
   the ceiling here — the left graph is seven nodes, and a third packet turns
   a diagram into a screensaver.

   Each route is a closed loop over edges that actually exist in the markup:
   A 95,150 -> 215,95 -> 320,180 -> 180,235 -> back    (upper, 538 units)
   B 180,235 -> 85,315 -> 160,385 -> 275,330 -> back   (lower, 489 units)
   They share the 180,235 node, so the two occasionally cross there. The
   periods are close but not harmonic (13s / 11.5s), which keeps them from
   settling into a visible lockstep. */
#blindspot .bs-packet {
  fill: var(--neon);
  filter: drop-shadow(0 0 6px rgba(33, 230, 193, .85));
  animation: bs-packet-a 13s linear infinite, bs-fade .5s var(--ease) 1.1s backwards;
}

/* base transform = each route's first node, so the packet is never caught at
   the SVG origin on the first frame or when animation is off */
#blindspot .bs-packet-a { transform: translate(95px, 150px); }
#blindspot .bs-packet-b {
  transform: translate(180px, 235px);
  animation: bs-packet-b 11.5s linear -3.5s infinite, bs-fade .5s var(--ease) 1.3s backwards;
}

/* keyframe percentages are weighted by segment length rather than split evenly
   four ways, so the packet holds one speed instead of lurching between the
   long edges and the short ones */
@keyframes bs-packet-a {
  0% { transform: translate(95px, 150px); }
  24.55% { transform: translate(215px, 95px); }
  49.67% { transform: translate(320px, 180px); }
  77.65% { transform: translate(180px, 235px); }
  100% { transform: translate(95px, 150px); }
}
@keyframes bs-packet-b {
  0% { transform: translate(180px, 235px); }
  25.42% { transform: translate(85px, 315px); }
  46.41% { transform: translate(160px, 385px); }
  72.5% { transform: translate(275px, 330px); }
  100% { transform: translate(180px, 235px); }
}
@keyframes bs-fade { from { opacity: 0; } }

/* --- three claims, stated without numbers --------------------------------- */
#blindspot .stakes-claims {
  list-style: none;
  margin: clamp(2.5rem, 5vh, 3.5rem) auto 0;
  padding: 0;
  max-width: 60rem;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.5rem, 3vw, 2.5rem);
}
#blindspot .stakes-claims li {
  display: flex;
  flex-direction: column;
  gap: .55rem;
  padding-top: 1.1rem;
  border-top: 1px solid rgba(39, 142, 165, .35);
}
#blindspot .claim-idx {
  font-family: var(--mono);
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .18em;
  color: var(--neon);
}
#blindspot .stakes-claims strong {
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: 1.15rem;
  color: var(--text-bright);
}
#blindspot .stakes-claims li > span:last-child {
  font-family: var(--sans);
  font-size: .92rem;
  line-height: 1.6;
  color: var(--text);
  opacity: .78;
}

@media (max-width: 760px) {
  #blindspot .stakes-claims { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  #blindspot .bs-known-edges line,
  #blindspot .bs-cross line { stroke-dashoffset: 0 !important; animation: none !important; }
  #blindspot .bs-known-nodes circle { opacity: 1 !important; animation: none !important; }
  #blindspot .bs-dark-nodes g { opacity: .42 !important; animation: none !important; }
  /* hidden rather than frozen — a packet parked mid-edge reads as a stray node */
  #blindspot .bs-packet { display: none; }
}

/* =========================================================================
   "What Enterprise includes" (#enterprise-capabilities) — a technical
   dossier rather than a third card grid (#features-grid and .tier-grid
   already own that shape, and repeating it would make this page
   forgettable). Sticky mono group label on the left, dot-leader spec rows
   on the right. Security deliberately does NOT appear here — it lives in
   #enterprise-trust alone, so the two sections never restate each other.
   ========================================================================= */
.dossier {
  max-width: 62rem;
  margin: 0 auto;
  border-top: 1px solid rgba(39, 142, 165, .3);
}

.dossier-group {
  display: grid;
  grid-template-columns: minmax(10rem, 1fr) 2.6fr;
  gap: clamp(1.25rem, 4vw, 3rem);
  padding: clamp(1.75rem, 3.5vh, 2.5rem) 0;
  border-bottom: 1px solid rgba(39, 142, 165, .3);
}

.dossier .group-label {
  position: sticky;
  top: calc(var(--header-h) + 1.25rem);
  align-self: start;
  display: flex;
  align-items: baseline;
  gap: .7rem;
  margin: 0;
  font-family: var(--mono);
  font-size: .84rem;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--text-bright);
}
.dossier .group-idx {
  font-size: 1.6rem;
  font-weight: 700;
  letter-spacing: 0;
  line-height: 1;
  color: var(--neon);
  opacity: .55;
}

.dossier dl {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: .9rem;
}

.spec-row {
  display: flex;
  align-items: baseline;
  gap: .75rem;
  flex-wrap: wrap;
}
.spec-row dt {
  display: flex;
  align-items: baseline;
  gap: .75rem;
  flex: 1 1 14rem;
  min-width: 0;
  font-family: var(--sans);
  font-size: .97rem;
  color: var(--text-bright);
}

/* the dot leader, so the block reads as a datasheet rather than a list */
.spec-row dt::after {
  content: "";
  flex: 1 1 auto;
  min-width: 1.5rem;
  height: 0;
  transform: translateY(-.24rem);
  border-bottom: 1px dotted rgba(39, 142, 165, .55);
}

.spec-row dd {
  margin: 0;
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: .77rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--neon);
  opacity: .85;
}

/* rows still waiting on a real answer read amber, never neon */
.spec-row.is-todo dd { color: var(--amber); opacity: 1; }

@media (max-width: 760px) {
  .dossier-group { grid-template-columns: 1fr; gap: 1.25rem; }
  .dossier .group-label { position: static; }
}
@media (max-width: 560px) {
  .spec-row dt { flex: 1 1 100%; }
  .spec-row dt::after { display: none; }
}

/* =========================================================================
   "How Nudox lands in your org" (#enterprise-integration) — two-lane
   rollout timeline. A horizontal rail carries the phase markers, with what
   Nudox does above it and what the customer's team does below, so the split
   of work is legible at a glance. Deliberately not the single vertical rail
   of #how-it-works-steps. Under 980px the rail turns vertical and each lane
   picks up an inline tag (::before) in place of the two edge labels.
   A single dot walks the rail on a loop and each phase marker flares as the
   dot reaches it, so the rollout reads as a sequence rather than five
   simultaneous claims. Pure CSS — no observers, no JS.
   ========================================================================= */
.rollout {
  /* single knob for the whole traversal — dot travel and node pulses derive
     their timing from it, so changing this keeps them in sync */
  --rollout-cycle: 8s;
  position: relative;
  max-width: 74rem;
  margin: 0 auto;
  padding: 1.9rem 0;
}

.rollout .lane-label {
  position: absolute;
  left: 0;
  margin: 0;
  font-family: var(--mono);
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--teal);
  opacity: .75;
}
.rollout .lane-label-a { top: 0; }
.rollout .lane-label-b { bottom: 0; }

.rollout ol {
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
}

/* the rail — runs behind every phase marker */
.rollout ol::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(33, 230, 193, .55) 8%, rgba(33, 230, 193, .55) 92%, transparent);
}

/* the signal — one dot walking the rail left to right, trailing a short comet
   glow. It covers 0%→100% of the rail over the first 80% of the cycle, so the
   five column centres (10%, 30%, 50%, 70%, 90%) are reached at 8%, 24%, 40%,
   56% and 72% of it; the remaining 20% is the dark beat before it restarts. */
.rollout ol::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  width: .55rem;
  height: .55rem;
  margin: -.275rem 0 0 -.275rem;
  border-radius: 50%;
  background: var(--neon);
  box-shadow:
    0 0 10px 2px rgba(33, 230, 193, .85),
    -16px 0 20px -6px rgba(33, 230, 193, .5);
  animation: rollout-dot var(--rollout-cycle) linear infinite;
}

@keyframes rollout-dot {
  0%   { left: 0;    opacity: 0; }
  4%   { opacity: 1; }
  74%  { opacity: 1; }
  80%  { left: 100%; opacity: 0; }
  100% { left: 100%; opacity: 0; }
}

.rollout li {
  display: grid;
  grid-template-rows: 1fr auto 1fr;
  align-items: center;
  justify-items: center;
  padding: 0 clamp(.4rem, 1vw, .9rem);
  text-align: center;
}

.rollout .lane { display: flex; }
.rollout .lane-a { grid-row: 1; align-items: flex-end; padding-bottom: 1.5rem; }
.rollout .lane-b { grid-row: 3; align-items: flex-start; padding-top: 1.5rem; }

.rollout .lane p {
  margin: 0;
  font-family: var(--sans);
  font-size: .87rem;
  line-height: 1.55;
  color: var(--text);
  opacity: .82;
}

/* inline lane tags — hidden until the rail goes vertical and the lanes stack */
.rollout .lane p::before {
  display: none;
  margin-right: .55rem;
  font-family: var(--mono);
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--teal);
}
.rollout .lane-a p::before { content: "Nudox"; }
.rollout .lane-b p::before { content: "Your team"; }

/* the phase marker sits on the rail, in a box borrowed from the graph nodes.
   Its resting state is deliberately dim — the light is spent by the dot as it
   arrives, not held permanently. */
.rollout .phase {
  grid-row: 2;
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: baseline;
  gap: .5rem;
  margin: 0;
  padding: .5rem .85rem;
  background: var(--bg-deep);
  border: 1px solid rgba(33, 230, 193, .3);
  border-radius: 2px;
  font-family: var(--mono);
  font-size: .69rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--text-bright);
  white-space: nowrap;
  box-shadow: 0 0 12px -4px rgba(33, 230, 193, .2);
  animation: rollout-node var(--rollout-cycle) linear infinite;
}
.rollout .phase b { font-size: .88rem; color: var(--neon); }

/* peak sits on the 0%/100% boundary so each marker can be tuned purely with a
   negative delay: -(1 - arrival) x cycle, arrival being the fraction of the
   cycle at which the dot reaches that column's centre. */
@keyframes rollout-node {
  0%, 100% {
    border-color: var(--neon);
    box-shadow: 0 0 26px -2px rgba(33, 230, 193, .8), inset 0 0 18px -8px rgba(33, 230, 193, .7);
    text-shadow: 0 0 12px rgba(33, 230, 193, .45);
    transform: scale(1.045);
  }
  9% {
    border-color: rgba(33, 230, 193, .8);
    box-shadow: 0 0 20px -3px rgba(33, 230, 193, .5), inset 0 0 18px -8px rgba(33, 230, 193, 0);
    text-shadow: 0 0 12px rgba(33, 230, 193, .2);
    transform: scale(1.015);
  }
  26%, 94% {
    border-color: rgba(33, 230, 193, .3);
    box-shadow: 0 0 12px -4px rgba(33, 230, 193, .2), inset 0 0 18px -8px rgba(33, 230, 193, 0);
    text-shadow: 0 0 12px rgba(33, 230, 193, 0);
    transform: scale(1);
  }
}

.rollout li:nth-child(1) .phase { animation-delay: calc(var(--rollout-cycle) * -.92); }
.rollout li:nth-child(2) .phase { animation-delay: calc(var(--rollout-cycle) * -.76); }
.rollout li:nth-child(3) .phase { animation-delay: calc(var(--rollout-cycle) * -.6); }
.rollout li:nth-child(4) .phase { animation-delay: calc(var(--rollout-cycle) * -.44); }
.rollout li:nth-child(5) .phase { animation-delay: calc(var(--rollout-cycle) * -.28); }

@media (max-width: 980px) {
  .rollout { padding: 0; }
  .rollout .lane-label { display: none; }
  .rollout ol { grid-template-columns: 1fr; gap: 2.25rem; }
  .rollout ol::before {
    top: 0;
    bottom: 0;
    left: .8rem;
    right: auto;
    width: 1px;
    height: auto;
    background: linear-gradient(180deg, transparent, rgba(33, 230, 193, .55) 5%, rgba(33, 230, 193, .55) 95%, transparent);
  }
  .rollout li {
    grid-template-rows: auto auto auto;
    justify-items: start;
    text-align: left;
    padding: 0 0 0 2rem;
    gap: .7rem;
  }
  .rollout .phase { grid-row: 1; margin-left: -2rem; }
  .rollout .lane-a { grid-row: 2; padding: 0; }
  .rollout .lane-b { grid-row: 3; padding: 0; }
  .rollout .lane p::before { display: inline; }
  /* the rail is vertical here and the markers no longer sit at predictable
     fractions of it, so the dot is dropped — the node pulses still cascade */
  .rollout ol::after { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .rollout ol::after { display: none; }
  .rollout .phase {
    animation: none !important;
    border-color: var(--neon);
    box-shadow: 0 0 14px -2px rgba(33, 230, 193, .45);
    text-shadow: none;
    transform: none;
  }
}

/* =========================================================================
   "Where the graph lives" (#hosting) — the page's centrepiece. Two static
   topology diagrams side by side, each drawing the customer's network as a
   dashed boundary so the only question that matters — which side of it the
   graph is compiled on — is shown rather than argued. Cloud reads teal,
   self-hosted reads neon; neither column is picked out as the winner (unlike
   #comparison), and the table keeps the trade honest in both directions.
   ========================================================================= */
.hosting-intro {
  max-width: 48rem;
  margin: 0 auto clamp(2.5rem, 5vh, 3.5rem);
  text-align: center;
  font-family: "Tenor Sans", var(--sans);
  font-size: 1.05rem;
  line-height: 1.65;
  color: var(--text);
  opacity: .85;
}

.deploy-pair {
  max-width: 64rem;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(1.25rem, 3vw, 2rem);
  align-items: stretch;
}

.deploy {
  --accent: var(--teal);
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  padding: clamp(1.25rem, 2.5vw, 2rem);
  background: rgba(7, 30, 61, .92);
  border: 1px solid color-mix(in srgb, var(--accent) 38%, transparent);
  border-radius: 2px;
  box-shadow:
    inset 0 0 40px color-mix(in srgb, var(--accent) 6%, transparent),
    0 20px 40px -24px rgba(0, 0, 0, .6);
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}
.deploy-self { --accent: var(--neon); }
.deploy:hover {
  border-color: color-mix(in srgb, var(--accent) 70%, transparent);
  box-shadow:
    inset 0 0 55px color-mix(in srgb, var(--accent) 10%, transparent),
    0 24px 48px -22px rgba(0, 0, 0, .7);
}

.deploy-tag {
  display: flex;
  align-items: center;
  gap: .65rem;
  margin: 0;
  font-family: var(--mono);
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--accent);
}
/* the status LED beside each deployment name — a double blink per cycle, which
   reads as "live" where a single fade would read as decoration */
.deploy-dot {
  width: .6rem;
  height: .6rem;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent);
  animation: deploy-blink 3s ease-out infinite;
}
/* half a cycle apart, so the two cards read as two independent deployments
   rather than one synchronised effect */
.deploy-self .deploy-dot { animation-delay: -1.5s; }

@keyframes deploy-blink {
  0%, 100% { opacity: .5; box-shadow: 0 0 5px -1px var(--accent); }
  7%       { opacity: 1;  box-shadow: 0 0 14px 1px var(--accent); }
  15%      { opacity: .5; box-shadow: 0 0 5px -1px var(--accent); }
  23%      { opacity: 1;  box-shadow: 0 0 14px 1px var(--accent); }
  33%      { opacity: .5; box-shadow: 0 0 5px -1px var(--accent); }
}

.deploy-best {
  margin: -.55rem 0 0;
  font-family: var(--sans);
  font-size: .87rem;
  line-height: 1.5;
  color: var(--text);
  opacity: .7;
}

.deploy figcaption {
  margin-top: auto;
  font-family: "Tenor Sans", var(--sans);
  font-size: .97rem;
  line-height: 1.6;
  color: var(--text);
  opacity: .85;
}
.deploy figcaption p { margin: 0; }

/* --- topology primitives — same visual grammar as #how-it-works ----------- */

.topo .boundary {
  fill: none;
  stroke: var(--teal);
  stroke-width: 1.5;
  stroke-dasharray: 8 7;
  opacity: .8;
}
.topo .boundary.is-neon { stroke: var(--neon); }

.topo .boundary-label {
  fill: var(--teal);
  font-family: var(--mono);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
}
.topo .boundary-label.is-neon { fill: var(--neon); }

.topo .topo-box rect { fill: var(--bg-deep); stroke: var(--accent); stroke-width: 2; }
.topo .topo-label { fill: var(--text-bright); font-family: var(--mono); font-size: 15px; font-weight: 600; }

.topo .topo-edge {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2.25;
  stroke-linecap: round;
  filter: drop-shadow(0 0 3px color-mix(in srgb, var(--accent) 45%, transparent));
}
.topo .arrow-head { fill: var(--accent); }

/* the license call-home: thin, dashed, and dropped entirely when air-gapped */
.topo .topo-edge.is-optional {
  stroke: var(--text);
  stroke-width: 1.5;
  opacity: .4;
  filter: none;
}

.topo .topo-cloud {
  fill: rgba(4, 13, 28, .7);
  stroke: var(--accent);
  stroke-width: 1.5;
  stroke-dasharray: 3 5;
}
.topo .topo-cloud.is-optional { stroke: var(--text); opacity: .35; }

.topo .topo-cloud-label {
  fill: var(--accent);
  font-family: var(--mono);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
}
.topo .topo-cloud-label.is-optional { fill: var(--text); opacity: .5; letter-spacing: .1em; }

/* the note naming the trade each deployment makes — amber where code
   crosses the boundary, neon where it does not */
.topo .topo-note {
  fill: var(--amber);
  font-family: var(--mono);
  font-size: 12.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
}
.topo .topo-note.is-neon { fill: var(--neon); opacity: .8; }

/* --- motion: boxes settle, then the whole topology runs -------------------
   Every loop here is the same trick — a fixed stroke-dasharray with
   stroke-dashoffset marching one full period per cycle — so the diagram reads
   as one system rather than four separate effects. Only the speeds differ, and
   they carry meaning: the live edges move fastest (~16 user units/s, that is
   traffic), the boundary and cloud perimeters crawl (~6, they are enclosures),
   and the optional license path is slowest of all (~3-5, it is idle.)
   Offsets are negative so every pattern travels *with* the path direction —
   for the edges that means toward the marker-end arrowhead, never away. */
.topo .topo-box {
  transform-box: fill-box;
  transform-origin: center;
  opacity: 0;
  animation: topo-pop .45s var(--ease) .15s forwards;
}

/* marching ants around the dashed perimeters */
.topo .boundary { animation: topo-ants 2.5s linear infinite; }
.topo .topo-cloud { animation: topo-ants-tight 1.2s linear infinite; }
.topo .topo-cloud.is-optional { animation-duration: 2.6s; }

/* the edges. The old one-shot draw-in is gone — a repeating dash cannot be
   "drawn" — so the reveal is carried by opacity on the same delay it used. */
.topo .topo-edge {
  stroke-dasharray: 10 8;
  stroke-dashoffset: 0;
  animation: topo-flow 1.1s linear infinite, topo-fade .55s var(--ease) .5s backwards;
}
.topo .topo-edge.is-optional {
  stroke-dasharray: 5 5;
  animation: topo-flow-optional 2s linear infinite, topo-fade .6s ease-out 1.4s backwards;
}

/* The self-hosted diagram is a straight vertical pipeline, so its three arrows
   are not three separate hops — they are one signal, drawn three times, each
   running the WHOLE pipeline top to bottom and restarting at the top.

   Geometry, in SVG user units. The three edges sit 72 apart (110, 182, 254)
   and each arrow spans 20 (18 of path plus ~2 of arrowhead past the end). A
   pass runs -24 (parked inside "your repos") to +168 (parked inside "your
   agents"), 192 in total. Because boxes are painted after the edges, the arrow
   is occluded whenever it is inside one — so a single pass surfaces exactly
   three times, once in each 24-unit corridor, and reads as a packet dropping
   through indexer, graph and agents in turn.

   --hop-offset cancels each path's own starting y, so all three spawn at the
   same spot at the top rather than in their own corridors. */
.deploy-self .topo-edge:not(.is-optional) {
  --hop-offset: 0px;
  stroke-dasharray: none;
  animation: topo-shoot 6s linear infinite;
}

/* staggered a third of a cycle apart, so a new arrow enters the top every 2s
   instead of all three spawning together. Delays are negative so the stagger
   is already settled on the first frame — a positive delay would leave the
   arrow parked at its spawn point until its turn came round. */
.deploy-self path.topo-edge:nth-of-type(2) { --hop-offset: 72px; animation-delay: -2s; }
.deploy-self path.topo-edge:nth-of-type(3) { --hop-offset: 144px; animation-delay: -4s; }

/* both ends of the cycle sit at opacity 0 — and, being parked inside a box,
   are occluded as well — so the wrap from bottom back to top is invisible */
@keyframes topo-shoot {
  0% { transform: translateY(calc(-24px - var(--hop-offset))); opacity: 0; }
  8% { opacity: 1; }
  92% { opacity: 1; }
  100% { transform: translateY(calc(168px - var(--hop-offset))); opacity: 0; }
}

/* each `to` is exactly one dash period of its rule's dasharray, so the loop
   seams are invisible — change a dasharray and its keyframe must follow */
@keyframes topo-flow { to { stroke-dashoffset: -18; } }
@keyframes topo-flow-optional { to { stroke-dashoffset: -10; } }
@keyframes topo-ants { to { stroke-dashoffset: -15; } }
@keyframes topo-ants-tight { to { stroke-dashoffset: -8; } }

@keyframes topo-pop { from { opacity: 0; transform: scale(.92); } to { opacity: 1; transform: scale(1); } }
@keyframes topo-fade { from { opacity: 0; } }

/* --- the comparison table ------------------------------------------------- */
.hosting-table {
  max-width: 64rem;
  margin: clamp(2rem, 4vh, 3rem) auto 0;
  overflow-x: auto;
  background: rgba(7, 30, 61, .92);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .05),
    0 20px 40px -24px rgba(0, 0, 0, .6);
}

.hosting-table table { width: 100%; min-width: 640px; border-collapse: collapse; }

.hosting-table caption {
  caption-side: top;
  padding: clamp(1.25rem, 2.5vw, 2rem) clamp(1.25rem, 2.5vw, 2rem) 0;
  text-align: left;
  font-family: "Tenor Sans", var(--sans);
  font-size: .92rem;
  line-height: 1.6;
  color: var(--text);
  opacity: .75;
}

.hosting-table th, .hosting-table td {
  padding: .95rem clamp(1rem, 2vw, 1.5rem);
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid rgba(39, 142, 165, .18);
}

.hosting-table thead th {
  font-family: var(--mono);
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  border-bottom: 1px solid rgba(39, 142, 165, .4);
}
.hosting-table thead th:nth-child(2) { color: var(--teal); }
.hosting-table thead th:nth-child(3) { color: var(--neon); }

.hosting-table tbody th {
  width: 30%;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: .95rem;
  color: var(--text-bright);
}
.hosting-table tbody td {
  font-family: var(--mono);
  font-size: .79rem;
  line-height: 1.55;
  letter-spacing: .03em;
  color: var(--text);
  opacity: .82;
}

/* hairline seam between the two deployments, so each claim is unambiguously
   attached to a column without highlighting either as the answer */
.hosting-table thead th:nth-child(2),
.hosting-table tbody td:nth-child(2) { border-right: 1px solid rgba(39, 142, 165, .18); }

.hosting-table tbody tr:hover { background: rgba(33, 230, 193, .04); }
.hosting-table tbody tr:last-child th,
.hosting-table tbody tr:last-child td { border-bottom: none; }

/* the closing "best for" row reads as the verdict, not another spec */
.hosting-table tbody tr:last-child td { color: var(--text-bright); opacity: 1; }

.hosting-table .is-todo { color: var(--amber); opacity: 1; }

@media (max-width: 980px) {
  .deploy-pair { grid-template-columns: 1fr; }
  .deploy .topo { max-width: 26rem; margin: 0 auto; }
}
@media (max-width: 560px) {
  .hosting-table th, .hosting-table td { padding: .85rem 1rem; }
}

@media (prefers-reduced-motion: reduce) {
  .topo .topo-edge, .topo .boundary, .topo .topo-cloud, .topo .topo-box, .deploy-dot { animation: none !important; }
  /* edges go solid again, but the optional link stays dashed — that dash is
     meaning, not motion */
  .topo .topo-edge { stroke-dasharray: none; stroke-dashoffset: 0; }
  .topo .topo-edge.is-optional { stroke-dasharray: 5 5; }
  .topo .topo-box { opacity: 1; }
}

/* =========================================================================
   "Security & data handling" (#enterprise-trust) — an interstitial band
   rather than another card grid, seamed top and bottom like #cta and the
   footer. Split in two on purpose: what is true by construction, and what
   still has to be confirmed. The second column is amber so an unfilled
   PLACEHOLDER is impossible to miss in review.
   ========================================================================= */
#enterprise-trust {
  position: relative;
  padding: clamp(3.5rem, 8vh, 5.5rem) var(--wrap-pad);
  background: rgba(4, 13, 28, .55);
}
#enterprise-trust::before, #enterprise-trust::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(33, 230, 193, .45), transparent);
}
#enterprise-trust::before { top: 0; }
#enterprise-trust::after { bottom: 0; }

.trust-inner { max-width: 64rem; margin: 0 auto; }

#enterprise-trust h2 {
  margin: 0 0 clamp(1.75rem, 4vh, 2.5rem);
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: clamp(1.6rem, 3vw, 2.2rem);
  letter-spacing: -0.01em;
  color: var(--text-bright);
}

.trust-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(1.75rem, 4vw, 3rem);
}

.trust-col { --accent: var(--neon); }
.trust-col-todo { --accent: var(--amber); }

.trust-head {
  display: flex;
  align-items: center;
  gap: .65rem;
  margin: 0 0 1.15rem;
  padding-bottom: .8rem;
  border-bottom: 1px solid color-mix(in srgb, var(--accent) 32%, transparent);
  font-family: var(--mono);
  font-size: .76rem;
  font-weight: 600;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--accent);
}
.trust-dot {
  width: .55rem;
  height: .55rem;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent);
}

.trust-col ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: .85rem;
}
.trust-col li {
  position: relative;
  padding-left: 1.6rem;
  font-family: var(--sans);
  font-size: .93rem;
  line-height: 1.6;
  color: var(--text);
  opacity: .85;
}
.trust-col li::before {
  content: "✓";
  position: absolute;
  left: 0;
  top: 0;
  font-family: var(--mono);
  font-weight: 700;
  color: var(--accent);
}
.trust-col-todo li { font-family: var(--mono); font-size: .81rem; letter-spacing: .04em; }
.trust-col-todo li::before { content: "?"; }

@media (max-width: 760px) {
  .trust-cols { grid-template-columns: 1fr; }
}

/* =========================================================================
   "Start with one repository" (#pilot) — a work-order card: ruled term rows
   and explicit exit criteria instead of a pitch. It stands in for the
   customer logos and case studies the site does not have yet and should not
   invent. Borrows the cut corner from .tier-card so it still reads as part
   of the same system.
   ========================================================================= */
.work-order {
  position: relative;
  max-width: 48rem;
  margin: 0 auto;
  padding: clamp(1.75rem, 4vw, 2.75rem);
  background: rgba(7, 30, 61, .92);
  border: 1px solid rgba(33, 230, 193, .45);
  border-radius: 2px;
  clip-path: polygon(0 0, calc(100% - 27px) 0, 100% 27px, 100% 100%, 0 100%);
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .06),
    0 20px 44px -24px rgba(0, 0, 0, .7);
}

.wo-head {
  display: flex;
  align-items: center;
  gap: .65rem;
  margin: 0 0 1.4rem;
  font-family: var(--mono);
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--neon);
}
.wo-dot {
  width: .6rem;
  height: .6rem;
  border-radius: 50%;
  background: var(--neon);
  box-shadow: 0 0 8px var(--neon);
}

#pilot h2 {
  margin: 0 0 .6rem;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: clamp(1.5rem, 3vw, 2.1rem);
  color: var(--text-bright);
}
#pilot hr { margin: 0 0 1.5rem; width: 3.75rem; height: 2px; border: none; background: var(--neon); }

.wo-intro {
  margin: 0 0 2rem;
  font-family: "Tenor Sans", var(--sans);
  font-size: 1rem;
  line-height: 1.65;
  color: var(--text);
  opacity: .85;
}

.wo-terms { margin: 0 0 2rem; border-top: 1px solid rgba(39, 142, 165, .28); }
.wo-terms > div {
  display: grid;
  grid-template-columns: 9rem 1fr;
  gap: 1rem;
  padding: .85rem 0;
  border-bottom: 1px solid rgba(39, 142, 165, .28);
}
.wo-terms dt {
  padding-top: .12rem;
  font-family: var(--mono);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--teal);
}
.wo-terms dd {
  margin: 0;
  font-family: var(--sans);
  font-size: .95rem;
  line-height: 1.55;
  color: var(--text-bright);
}
.wo-terms dd.is-todo { font-family: var(--mono); font-size: .81rem; color: var(--amber); }

.wo-exit-head {
  margin: 0 0 1rem;
  font-family: var(--mono);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--teal);
}

.wo-exit {
  list-style: none;
  margin: 0 0 2rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: .8rem;
}
.wo-exit li {
  position: relative;
  padding-left: 1.6rem;
  font-family: var(--sans);
  font-size: .95rem;
  line-height: 1.6;
  color: var(--text);
  opacity: .85;
}
.wo-exit li::before {
  content: "✓";
  position: absolute;
  left: 0;
  top: 0;
  font-family: var(--mono);
  font-weight: 700;
  color: var(--neon);
}

.wo-foot { margin: 0; }
.wo-foot a {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding-bottom: .2rem;
  border-bottom: 1px solid rgba(33, 230, 193, .4);
  font-family: var(--mono);
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--neon);
  transition: color .25s var(--ease), border-color .25s var(--ease), gap .25s var(--ease);
}
.wo-foot a::after { content: "→"; }
.wo-foot a:hover, .wo-foot a:focus-visible { gap: .9rem; color: var(--text-bright); border-color: var(--neon); }

@media (max-width: 560px) {
  .wo-terms > div { grid-template-columns: 1fr; gap: .35rem; }
}

/* =========================================================================
   Enterprise closing CTA — reuses #cta wholesale (see above). This adds only
   the "what happens next" strip, scoped to .next-steps, which does not exist
   on the homepage, so #cta there is untouched.
   ========================================================================= */
#cta .next-steps {
  list-style: none;
  margin: 0 auto clamp(2rem, 4vh, 2.5rem);
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: .6rem 1.1rem;
}
#cta .next-steps li {
  display: flex;
  align-items: center;
  gap: .55rem;
  font-family: var(--mono);
  font-size: .73rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--text);
  opacity: .8;
}
#cta .next-steps li > span { color: var(--neon); }
#cta .next-steps li + li::before {
  content: "→";
  margin-right: .55rem;
  color: var(--teal);
  opacity: .7;
}

/* =========================================================================
   Quickstart (#quickstart-steps) — left-spine timeline of numbered cards.
   Sibling of the features page's #how-it-works-steps zigzag: same spine,
   same lit-dot vocabulary (it reuses the steps-lit keyframe), but every step
   here carries an action — a button, a download list, or a placeholder for
   one — so the cards stay in a single column beside the spine instead of
   alternating across it, and each is a glass panel rather than bare copy.
   ========================================================================= */
/* This section opens the page — there is no hero above it — so the top pad
   clears the sticky header on its own. */
#quickstart-steps {
  padding: clamp(2.5rem, 7vh, 4.5rem) var(--wrap-pad) clamp(4rem, 10vh, 7rem);
}

/* the shared hero eyebrow, re-centered for a section masthead: its trailing
   wire is a fixed stub here instead of stretching to fill a left-aligned row */
#quickstart-steps .eyebrow {
  width: max-content;
  max-width: 100%;
  margin: 0 auto 1.1rem;
  justify-content: center;
}
#quickstart-steps .eyebrow::after { flex: none; width: 3rem; }

#quickstart-steps > div > h2 {
  margin: 0 auto clamp(1.25rem, 3vh, 2rem);
  text-align: center;
  max-width: 22ch;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: clamp(2.2rem, 5vw, 3.4rem);
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--text-bright);
}


#quickstart-steps > div > hr {
  margin: 0 auto clamp(1.5rem, 3vh, 2rem);
  width: 3rem;
  height: 2px;
  border: none;
  background: linear-gradient(90deg, var(--teal), var(--neon));
}

#quickstart-steps ol {
  position: relative;
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: 52rem;
}

/* the spine, stopped short at both ends so it runs dot-to-dot rather than
   past the first and last markers */
#quickstart-steps ol::before {
  content: "";
  position: absolute;
  left: 1.5rem;
  top: 2.5rem;
  bottom: 2.5rem;
  width: 2px;
  background: linear-gradient(180deg, var(--teal), var(--neon));
  transform: translateX(-50%);
}

#quickstart-steps ol > li {
  display: grid;
  /* minmax(0, …) rather than 1fr: the card holds a <pre> whose min-content
     width is a long line, and an auto-minimum track would grow to it and push
     the page sideways instead of letting .code-window scroll internally. */
  grid-template-columns: 3rem minmax(0, 1fr);
  column-gap: clamp(1rem, 3vw, 2rem);
  padding-block: clamp(1rem, 2.5vh, 1.75rem);
}

/* marker dot, vertically aligned with the card's step label. Scoped to the
   ol's own children — the step-02 download chips are <li>s too. */
#quickstart-steps ol > li::before {
  content: "";
  grid-column: 1;
  justify-self: center;
  margin-top: 2.35rem;      /* lines the dot up with the card's step label */
  width: .85rem;
  height: .85rem;
  border-radius: 50%;
  background: var(--neon);
  box-shadow: 0 0 12px rgba(33, 230, 193, .7);
}

/* glass panel, same recipe as #features-grid > div */
#quickstart-steps .step-card {
  grid-column: 2;
  padding: clamp(1.5rem, 3vw, 2.25rem);
  background: rgba(7, 30, 61, .92);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .05),
    0 20px 40px -24px rgba(0, 0, 0, .6);
  transition: border-color .3s var(--ease), box-shadow .3s var(--ease);
}
#quickstart-steps .step-card:hover {
  border-color: rgba(33, 230, 193, .6);
  box-shadow:
    inset 0 0 55px rgba(33, 230, 193, .09),
    0 24px 48px -22px rgba(0, 0, 0, .7);
}

/* the not-yet-written step reads as a draft: dashed edge, no hover lift */
#quickstart-steps .step-card.is-placeholder {
  background: rgba(7, 30, 61, .55);
  border-style: dashed;
  border-color: rgba(39, 142, 165, .45);
}
#quickstart-steps .step-card.is-placeholder:hover {
  border-color: rgba(39, 142, 165, .45);
  box-shadow:
    inset 0 0 40px rgba(33, 230, 193, .05),
    0 20px 40px -24px rgba(0, 0, 0, .6);
}

/* the label sits alone in a markdown-emitted <p>; the spacing below it is the
   .step-num's own, so that wrapper must not add any of its own */
#quickstart-steps .step-card > p:first-child { margin: 0 !important; }

#quickstart-steps .step-num {
  display: block;
  margin-bottom: .55rem;
  font-family: var(--mono);
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--teal);
}

#quickstart-steps h3 {
  margin: 0 0 1rem;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: clamp(1.25rem, 2.4vw, 1.6rem);
  color: var(--text-bright);
}

#quickstart-steps .step-card p {
  margin: 0 0 1.25rem;
  max-width: 62ch;
  font-family: var(--sans);
  font-size: .95rem;
  line-height: 1.65;
  color: var(--text);
  opacity: .85;
}
#quickstart-steps .step-card p strong { color: var(--text-bright); font-weight: 600; }

#quickstart-steps .code-window { margin: 0 0 1.25rem; }
#quickstart-steps .code-window pre { font-size: .82rem; }

/* the aside under a step's payload — quieter than body copy, mono voice */
#quickstart-steps .step-hint {
  padding-left: .9rem;
  border-left: 2px solid rgba(39, 142, 165, .45);
  font-family: var(--mono) !important;
  font-size: .76rem !important;
  line-height: 1.6;
  letter-spacing: .02em;
  opacity: .7 !important;
}

/* --- step 02: platform builds — direct GitHub Release downloads */
#quickstart-steps .download-options {
  list-style: none;
  margin: 0 0 1.25rem;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
}

/* each chip is the whole link, straight to a real per-platform download. */
#quickstart-steps .download-options a {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .7rem 1.1rem;
  background: rgba(4, 13, 28, .6);
  border: 1px solid rgba(39, 142, 165, .5);
  border-radius: 2px;
  text-decoration: none;
  transition:
    background .25s var(--ease),
    border-color .25s var(--ease),
    box-shadow .25s var(--ease),
    transform .25s var(--ease);
}
#quickstart-steps .download-options a:hover,
#quickstart-steps .download-options a:focus-visible {
  background: linear-gradient(180deg, rgba(39, 142, 165, .22), rgba(31, 66, 135, .3));
  border-color: var(--neon);
  box-shadow: 0 12px 32px -8px rgba(33, 230, 193, .45);
  transform: translateY(-3px);
  outline: none;
}
#quickstart-steps .download-options a:active { transform: translateY(-1px); }

#quickstart-steps .dl-os {
  font-family: var(--mono);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--text-bright);
  opacity: .8;
  transition: opacity .25s var(--ease);
}
#quickstart-steps .download-options a:hover .dl-os,
#quickstart-steps .download-options a:focus-visible .dl-os { opacity: 1; }

/* --- step 03: explicit "not written yet" marker --------------------------- */
#quickstart-steps .placeholder-note {
  margin: 0 !important;
  display: flex;
  align-items: center;
  gap: .7rem;
  padding: .85rem 1.1rem;
  background: rgba(4, 13, 28, .5);
  border: 1px dashed rgba(39, 142, 165, .45);
  border-radius: 2px;
  font-family: var(--mono) !important;
  font-size: .78rem !important;
  opacity: .75 !important;
}
#quickstart-steps .placeholder-note > span {
  flex: none;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--neon);
}

#quickstart-steps .step-actions {
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
}
#quickstart-steps .step-actions a[role="button"] { padding: .8rem 1.6rem; }

/* motion: the light walks the spine, step 01 down to step 03 — the
   #how-it-works-steps loop with one fewer stop (see steps-lit above) */
#quickstart-steps ol > li::before { animation: steps-lit 4s linear infinite; }
#quickstart-steps ol > li:nth-child(2)::before { animation-delay: -3.2s; }
#quickstart-steps ol > li:nth-child(3)::before { animation-delay: -2.4s; }

@media (max-width: 700px) {
  #quickstart-steps ol > li { grid-template-columns: 2rem minmax(0, 1fr); column-gap: 1rem; }
  #quickstart-steps ol::before { left: 1rem; }
}

@media (prefers-reduced-motion: reduce) {
  #quickstart-steps ol > li::before { animation: none !important; }
}

/* =========================================================================
   Quickstart upgrade band (#upgrade-path) — sits below the closing #cta on
   the quickstart page: what the paid plans add once the first library is in,
   and a way through to pricing. Deliberately quieter than the #cta above it —
   one panel, no buttons, a single arrow link out.
   ========================================================================= */
#upgrade-path {
  padding: 0 var(--wrap-pad) clamp(4rem, 9vh, 6.5rem);
}

#upgrade-path .upgrade-panel {
  max-width: 52rem;
  margin: 0 auto;
  padding: clamp(1.75rem, 4vw, 2.75rem);
  background: rgba(7, 30, 61, .55);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
  box-shadow: inset 0 0 40px rgba(33, 230, 193, .05);
}

/* the shared hero eyebrow again, left-aligned here with a stub wire (the
   panel is narrow enough that the full stretched wire would dominate it) */
#upgrade-path .eyebrow {
  width: max-content;
  max-width: 100%;
  margin: 0 0 .9rem;
}
#upgrade-path .eyebrow::after { flex: none; width: 2.5rem; }

#upgrade-path h2 {
  margin: 0 0 1rem;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: clamp(1.5rem, 3vw, 2.1rem);
  line-height: 1.15;
  letter-spacing: -0.01em;
  color: var(--text-bright);
}

#upgrade-path .upgrade-lede {
  margin: 0 0 clamp(1.5rem, 3vh, 2rem);
  max-width: 58ch;
  font-family: var(--sans);
  font-size: .95rem;
  line-height: 1.65;
  color: var(--text);
  opacity: .85;
}

#upgrade-path .upgrade-list {
  list-style: none;
  margin: 0 0 clamp(1.5rem, 3vh, 2rem);
  padding: 0;
  display: grid;
  gap: 1rem;
}

/* neon tick down the left of each capability, same voice as the .stickies
   left rule and the eyebrow tick */
#upgrade-path .upgrade-list li {
  padding-left: 1.1rem;
  border-left: 2px solid rgba(33, 230, 193, .45);
}

#upgrade-path .upgrade-list strong {
  display: block;
  margin-bottom: .3rem;
  font-family: var(--mono);
  font-size: .8rem;
  font-weight: 600;
  letter-spacing: .06em;
  color: var(--text-bright);
}

#upgrade-path .upgrade-list span {
  display: block;
  font-family: var(--sans);
  font-size: .92rem;
  line-height: 1.6;
  color: var(--text);
  opacity: .8;
}

/* arrow link out to pricing — same idiom as the enterprise .wo-foot link */
#upgrade-path .upgrade-foot { margin: 0; }
#upgrade-path .upgrade-foot a {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding-bottom: .2rem;
  border-bottom: 1px solid rgba(33, 230, 193, .4);
  font-family: var(--mono);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--neon);
  transition: color .25s var(--ease), border-color .25s var(--ease), gap .25s var(--ease);
}
#upgrade-path .upgrade-foot a::after { content: "→"; }
#upgrade-path .upgrade-foot a:hover,
#upgrade-path .upgrade-foot a:focus-visible {
  gap: .9rem;
  color: var(--text-bright);
  border-color: var(--neon);
}

/* =========================================================================
   Blog — index (#blog-index) and post (#post)
   These are the only pages on the site whose body is generated from markdown
   instead of hand-written HTML, so they are the only ones that need
   element-level typography: a post is bare <h2>/<p>/<ul>/<pre> with no classes
   to hook. That typography is therefore scoped inside .post-body and never
   above it — every other section here styles its own bare elements, and a
   global `h2 {}` would collide with all eleven of them at once.
   Markup: templates/blog.html and templates/blog-post.html. Neither page has
   a hero; both open on their own title. The index's title and lede come from
   content/blog/_index.md and use the shared section h1 + .section-lede rules,
   so only the list below them is styled here.

   .post-head and .post-foot are divs, not <header>/<footer>: those element
   names are claimed globally by the site chrome at the top of this file.
   ========================================================================= */

/* --- index: the post list ------------------------------------------------ */
.post-list {
  max-width: 52rem;
  margin: 0 auto;
  padding: 0;
  list-style: none;
}

.post-item + .post-item { margin-top: 1rem; }

/* the whole card is the link — no separate "read more" hit target */
.post-link {
  display: block;
  padding: clamp(1.25rem, 3vw, 1.9rem) clamp(1.25rem, 3vw, 2rem);
  background: rgba(7, 30, 61, .92);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
  text-decoration: none;
  transition:
    border-color .3s var(--ease),
    box-shadow .3s var(--ease),
    transform .3s var(--ease);
}
#blog-index .post-link:hover,
#blog-index .post-link:focus-visible {
  border-color: rgba(33, 230, 193, .6);
  box-shadow: inset 0 0 30px rgba(33, 230, 193, .05);
  transform: translateY(-2px);
}

.post-item-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  margin: 0 0 .6rem;
  font-family: var(--mono);
  font-size: .78rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--teal);
}

.post-item h2 {
  margin: 0 0 .55rem;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: clamp(1.3rem, 2.4vw, 1.75rem);
  line-height: 1.25;
  color: var(--text-bright);
}

.post-item-desc {
  margin: 0;
  max-width: 62ch;
  font-family: "Tenor Sans", var(--sans);
  font-size: .97rem;
  line-height: 1.65;
  color: var(--text);
  opacity: .85;
}

.post-item-more {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  margin-top: .9rem;
  font-family: var(--mono);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--neon);
  transition: gap .25s var(--ease);
}
.post-item-more::after { content: "→"; }
#blog-index .post-link:hover .post-item-more,
#blog-index .post-link:focus-visible .post-item-more { gap: .9rem; }

/* only reachable with every post drafted or the folder emptied */
.post-empty {
  max-width: 52rem;
  margin: 0 auto;
  text-align: center;
  font-family: var(--mono);
  font-size: .85rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--text);
  opacity: .55;
}

/* --- post: shell and masthead -------------------------------------------- */
/* No hero on a post: it opens on its own title, like every non-landing page.
   The header is sticky rather than fixed, so it needs no clearance here. */
#post {
  padding: clamp(3rem, 7vh, 5rem) var(--wrap-pad) clamp(4rem, 9vh, 7rem);
}

/* One measure for the whole article — masthead, body and footer all inherit
   it, so no child needs its own max-width. ~46rem lands near 70 characters at
   this size, which is the single biggest lever on long-form readability. */
.post {
  max-width: 46rem;
  margin: 0 auto;
}

.post-head {
  margin-bottom: clamp(2rem, 4vh, 3rem);
  padding-bottom: clamp(1.5rem, 3vh, 2rem);
  border-bottom: 1px solid rgba(39, 142, 165, .3);
}

.post-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .5rem;
  margin: 0 0 .9rem;
  font-family: var(--mono);
  font-size: .78rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--teal);
}

/* deliberately left-aligned, unlike the centred section titles elsewhere —
   a centred heading over a left-aligned column of prose reads as a mistake */
.post-head h1 {
  margin: 0;
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  font-size: clamp(2rem, 5vw, 3.1rem);
  line-height: 1.12;
  letter-spacing: -0.01em;
  color: var(--text-bright);
}

.post-lede {
  margin: 1rem 0 0;
  font-family: "Tenor Sans", var(--sans);
  font-size: 1.1rem;
  line-height: 1.6;
  color: var(--text);
  opacity: .8;
}

/* --- post: the markdown body --------------------------------------------- */
.post-body {
  font-family: "Tenor Sans", var(--sans);
  font-size: 1.05rem;
  line-height: 1.8;
  color: var(--text);
}

.post-body > :first-child { margin-top: 0; }
.post-body > :last-child { margin-bottom: 0; }

.post-body p { margin: 0 0 1.4rem; }

/* Headings take much more space above than below, so each binds visually to
   the text it introduces rather than floating between two blocks. A post body
   starts at h2 — the h1 is the title, rendered by the template. */
.post-body h2,
.post-body h3,
.post-body h4 {
  font-family: "Tenor Sans", var(--sans);
  font-weight: 400;
  line-height: 1.25;
  color: var(--text-bright);
}
.post-body h2 {
  margin: 2.75rem 0 1rem;
  font-size: clamp(1.5rem, 3vw, 1.95rem);
  letter-spacing: -0.01em;
}
.post-body h3 {
  margin: 2.25rem 0 .85rem;
  font-size: clamp(1.2rem, 2.2vw, 1.4rem);
}
.post-body h4 {
  margin: 1.9rem 0 .7rem;
  font-family: var(--mono);
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--teal);
}

.post-body a {
  color: var(--neon);
  text-decoration: underline;
  text-underline-offset: 2px;
  transition: color .25s var(--ease);
}
.post-body a:hover,
.post-body a:focus-visible { color: var(--text-bright); }

.post-body strong { color: var(--text-bright); font-weight: 600; }
.post-body em { font-style: italic; }
.post-body del { opacity: .6; }

.post-body ul,
.post-body ol { margin: 0 0 1.4rem; padding-left: 1.5rem; }
.post-body li { margin-bottom: .5rem; }
.post-body li:last-child { margin-bottom: 0; }
/* a nested list belongs to the item above it, not to the gap after it */
.post-body li > ul,
.post-body li > ol { margin: .5rem 0 0; }
.post-body ul { list-style: square; }
.post-body li::marker { color: var(--teal); }

.post-body blockquote {
  margin: 0 0 1.4rem;
  padding: .25rem 0 .25rem 1.25rem;
  border-left: 2px solid var(--teal);
  color: var(--text-bright);
  font-style: italic;
  opacity: .9;
}
.post-body blockquote > :last-child { margin-bottom: 0; }

/* Inline code only. `:not(pre) >` keeps this off the <code> inside a fenced
   block, which giallo already colours and which would otherwise get a second
   background and padding nested inside the first. */
.post-body :not(pre) > code {
  padding: .12em .4em;
  background: rgba(4, 13, 28, .8);
  border: 1px solid rgba(39, 142, 165, .3);
  border-radius: 2px;
  font-family: var(--mono);
  font-size: .87em;
  color: var(--neon);
}

/* Fenced blocks. The colours — including the background — come from
   giallo.css (.z-code), which config.toml's highlighter emits; only the box
   around them is ours, so a theme change stays a one-line config edit. */
.post-body pre {
  margin: 0 0 1.4rem;
  padding: clamp(.9rem, 2vw, 1.25rem);
  border: 1px solid rgba(39, 142, 165, .35);
  border-radius: 2px;
  overflow-x: auto;
  font-family: var(--mono);
  font-size: .85rem;
  line-height: 1.6;
  -webkit-overflow-scrolling: touch;
}
.post-body pre code {
  font-family: inherit;
  font-size: inherit;
}

.post-body hr {
  margin: 2.5rem 0;
  border: 0;
  height: 1px;
  background: linear-gradient(90deg, rgba(39, 142, 165, .7), transparent);
}

/* Images. `height: auto` is not cosmetic: the figure shortcode stamps real
   width/height attributes on the <img> so the browser reserves the box before
   the file arrives, and without this the global `img { max-width: 100% }`
   would shrink the width against a fixed attribute height and squash the
   picture on every narrow screen. The dark plate behind it stops a PNG with
   transparency from dissolving into the page. */
.post-body img {
  height: auto;
  border-radius: 2px;
}

.post-body p > img {
  margin: 0 0 1.4rem;
  border: 1px solid rgba(39, 142, 165, .25);
  background: rgba(4, 13, 28, .5);
}

.post-figure {
  margin: 2rem 0;
}

.post-figure img {
  width: 100%;
  border: 1px solid rgba(39, 142, 165, .25);
  background: rgba(4, 13, 28, .5);
}

.post-figure figcaption {
  margin-top: .75rem;
  font-family: var(--mono);
  font-size: .78rem;
  line-height: 1.6;
  color: var(--text);
  opacity: .6;
}

/* width="wide" — break out of the article's 46rem measure for a screenshot or
   diagram that is unreadable at text width, while staying inside the viewport.
   left/transform rather than negative margins so it stays centred whatever the
   container is; the 2rem deduction keeps it clear of a classic scrollbar,
   which 100vw would otherwise sit underneath. */
.post-figure-wide {
  width: min(64rem, calc(100vw - 2rem));
  max-width: calc(100vw - 2rem);
  position: relative;
  left: 50%;
  transform: translateX(-50%);
}

.post-figure-wide figcaption {
  max-width: 46rem;
  margin-inline: auto;
}

/* wrapped so a wide table scrolls inside the column instead of widening the
   page — .post is a hard measure and nothing may break out of it */
.post-body table {
  display: block;
  width: 100%;
  margin: 0 0 1.4rem;
  overflow-x: auto;
  border-collapse: collapse;
  font-size: .93rem;
}
.post-body th,
.post-body td {
  padding: .6rem .9rem;
  text-align: left;
  border-bottom: 1px solid rgba(39, 142, 165, .25);
}
.post-body th {
  font-family: var(--mono);
  font-size: .76rem;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--teal);
  white-space: nowrap;
}
.post-body tbody tr:last-child td { border-bottom: 0; }

/* --- post: footer -------------------------------------------------------- */
.post-foot {
  margin-top: clamp(2.5rem, 5vh, 3.5rem);
  padding-top: clamp(1.5rem, 3vh, 2rem);
  border-top: 1px solid rgba(39, 142, 165, .3);
}

.post-foot a {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  padding-bottom: .2rem;
  border-bottom: 1px solid rgba(33, 230, 193, .4);
  font-family: var(--mono);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--neon);
  transition: color .25s var(--ease), border-color .25s var(--ease), gap .25s var(--ease);
}
.post-foot a::before { content: "←"; }
.post-foot a:hover,
.post-foot a:focus-visible {
  gap: .9rem;
  color: var(--text-bright);
  border-color: var(--neon);
}

@media (prefers-reduced-motion: reduce) {
  #blog-index .post-link:hover,
  #blog-index .post-link:focus-visible { transform: none; }
}
