/* ─────────────────────────────────────────────────────────────
 * JNCS Site Shell · shared CSS
 * ─────────────────────────────────────────────────────────────
 * Loaded by every top-level page (home / configurator / etc.).
 * Owns the design tokens, the top nav, the footer, and a few
 * primitives that should look the same everywhere.
 *
 * Direction B (Engineered) palette: cool deep dark base, muted
 * hunter green accent, Geist sans + JetBrains Mono for data.
 * Reads as a premium design tool (Linear / Vercel energy)
 * without the soulless-corporate feel — pairs with the
 * 'small team, treats you like a human' brand voice.
 * ───────────────────────────────────────────────────────────── */

/* ── Self-hosted fonts ────────────────────────────────────────
   Geist + JetBrains Mono are served locally from /site/fonts/
   instead of fonts.googleapis.com. This keeps the homepage's
   "no third parties" privacy promise literally true (Google Fonts
   leaks the visitor IP to Google on every load) and drops a
   render-blocking external request. Both are VARIABLE fonts, so a
   single file per family covers the whole 400–700 weight range.
   Latin subset only (US shop); emoji fall back to the system. */
@font-face {
  font-family: 'Geist';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/site/fonts/geist-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('/site/fonts/jetbrains-mono-latin.woff2') format('woff2');
}

:root {
  /* Surfaces */
  --paper:       #0b0c0e;   /* page background */
  --paper-2:     #16181c;   /* card / panel */
  --paper-3:     #1f2228;   /* elevated card */
  --line:        rgba(245,245,247,0.08);
  --line-2:      rgba(245,245,247,0.14);

  /* Ink */
  --ink:         #f5f5f7;
  --ink-70:      rgba(245,245,247,0.78);
  --ink-50:      rgba(245,245,247,0.70);   /* secondary text — raised from .55 for legibility (WCAG) */
  --ink-30:      rgba(245,245,247,0.45);   /* tertiary text — raised from .32 so captions/hints aren't near-invisible */

  /* Accent — muted hunter green */
  --accent:      #4a8b66;
  --accent-2:    #6ba88a;
  --accent-glow: rgba(74,139,102,0.22);
  /* Text color for use ON the accent (CTAs, active pills). Always
     near-black regardless of theme — flipping it with --paper would
     give white-on-green in light mode, which lacks contrast. */
  --ink-on-accent: #0b0c0e;

  /* Semantic */
  --ok:          #4a8b66;
  --warn:        #d4a017;
  --err:         #e88f93;   /* dark-theme legible red; light overrides below */

  /* Type */
  --font-body:    'Geist', 'Inter', system-ui, -apple-system, sans-serif;
  --font-display: 'Geist', 'Inter', system-ui, -apple-system, sans-serif;
  --font-mono:    'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Rhythm */
  --r-1:  4px;
  --r-2:  8px;
  --r-3:  12px;
  --r-4:  16px;
  --r-5:  24px;
  --r-6:  36px;
  --r-7:  56px;
  --r-8:  88px;

  /* Scrollbar thumb — mid-grey pill on both themes; hover goes accent. */
  --scroll-thumb: #2c313a;
}

/* ── Light theme — token overrides ───────────────────────────
   Applied when site-theme.js puts `light` on documentElement.
   Same surface ordering as dark (paper < paper-2 < paper-3
   in lit-ness), just inverted polarity. Accent stays brand-
   constant. Set on :root so it fires before <body> paints —
   no FOUC. */
:root.light {
  /* Surface separation in light can't come from lightness steps the way
     dark does (there's nowhere above white to go), so the page sits a
     notch deeper + cooler and cards ride lighter on top of it — the gap
     is what reads as depth. Borders also do more work in light, so the
     hairlines are a touch stronger (ux-common-region-boundaries). */
  --paper:       #e5e9f1;   /* page — deeper/cooler so cards pop */
  --paper-2:     #f7f9fc;   /* card — clearly lifted off the page */
  --paper-3:     #ffffff;   /* elevated — pure white */
  --line:        rgba(11,12,14,0.12);
  --line-2:      rgba(11,12,14,0.20);
  --ink:         #0b0c0e;
  --ink-70:      rgba(11,12,14,0.74);
  --ink-50:      rgba(11,12,14,0.68);   /* light theme (Jerry) — raised from .55 for contrast */
  --ink-30:      rgba(11,12,14,0.45);   /* light theme — raised from .32 */
  --accent-glow: rgba(74,139,102,0.18);
  --err:         #c8434a;
  --scroll-thumb: #b9c2d0;

  /* Neutral elevation scale — light theme only. Dark keeps its own
     inline per-element shadows (shipped + approved); these exist so the
     light pages can re-point heavy pure-black shadows at something that
     reads as soft depth on a near-white surface instead of a grey smudge.
     Tinted off the ink (11,12,14) not pure black (visual-no-pure-black-
     shadow); two layers each = tight contact + soft ambient (visual-
     layered-shadows); all offsets +y, one light source (visual-shadow-
     direction); rising blur/alpha = rising elevation (visual-shadow-
     matches-elevation). */
  --shadow-sm: 0 1px 2px rgba(11,12,14,0.06), 0 2px 6px rgba(11,12,14,0.08);
  --shadow-md: 0 2px 6px rgba(11,12,14,0.07), 0 8px 18px rgba(11,12,14,0.11);
  --shadow-lg: 0 6px 16px rgba(11,12,14,0.10), 0 18px 44px rgba(11,12,14,0.14);
  --shadow-xl: 0 10px 24px rgba(11,12,14,0.11), 0 28px 64px rgba(11,12,14,0.16);
}
/* Sticky-nav glass background also needs a polarity flip; it's
   hardcoded RGBA in the dark rule below and can't be tokenized
   without changing the saturation feel. */
:root.light .site-nav { background: rgba(248,250,252,0.78); }

/* ── Cross-document view transitions ─────────────────────────
   Same-origin page-to-page navigation crossfades instead of
   flashing blank: the browser holds the old page's pixels until
   the new page is ready to paint. Progressive — browsers without
   support just do a normal load. The sticky nav is identical on
   every page, so it reads as never leaving. */
@view-transition { navigation: auto; }
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
}

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
html { background: var(--paper); }

/* ── Scrollbar ────────────────────────────────────────────────
   scrollbar-gutter reserves the bar's lane even on pages too short
   to scroll, so navigating between short and long pages no longer
   shifts the whole layout sideways by the bar's width. The bar
   itself is themed: transparent track (the page paper shows
   through), mid-grey pill thumb, brand green on hover/drag. */
html { scrollbar-gutter: stable; }
/* Chromium + Safari — pill thumb inset off the edge */
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--scroll-thumb);
  border: 3px solid var(--paper);   /* inset: reads as a floating pill */
  border-radius: 8px;
}
::-webkit-scrollbar-thumb:hover,
::-webkit-scrollbar-thumb:active { background: var(--accent); }
::-webkit-scrollbar-corner { background: transparent; }
/* Firefox — no pseudo-element styling; thin + recolored is its ceiling */
@supports (-moz-appearance: none) {
  html { scrollbar-width: thin; scrollbar-color: var(--scroll-thumb) transparent; }
}
body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
a { color: inherit; text-decoration: none; }
button { font-family: inherit; }

/* ── Custom cursors (site-wide, 2026-06-29) ───────────────────────
   Brand-green vector cursors so the pointer matches the rest of the
   identity. Default = white arrow with a green outline (reads on both
   the dark and light --paper); interactive = green-filled arrow with a
   white outline (the "go" state). Text fields keep the native caret so
   editing affordance isn't lost. Every rule falls back to the system
   keyword if a browser rejects SVG cursors (hotspot at the arrow tip).
   Component-specific clickables that set their own `cursor: pointer`
   keep the system hand unless listed here — extend the pointer rule as
   needed. */
html {
  cursor: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%2226%22%20height=%2226%22%20viewBox=%220%200%2026%2026%22%3E%3Cpath%20d=%22M4%203%20L4%2020.5%20L8.7%2016.1%20L11.8%2022.6%20L14.7%2021.3%20L11.7%2015%20L18%2015%20Z%22%20fill=%22%23ffffff%22%20stroke=%22%234a8b66%22%20stroke-width=%221.6%22%20stroke-linejoin=%22round%22/%3E%3C/svg%3E") 4 3, auto;
  /* The clickable cursor — a smooth green pointing hand (SVG, not the OS
     bitmap finger). Defined once here as a variable so EVERY clickable uses
     it: the shared rule below + every component `cursor: pointer` (which is
     swept to var(--cursor-pointer) across the pages). Falls back to the
     system pointer if a browser rejects SVG cursors; hotspot at the fingertip. */
  --cursor-pointer: url("data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%2230%22%20height=%2232%22%20viewBox=%220%200%2024%2026%22%3E%3Cpath%20d=%22M8.5%202.2c-.86%200-1.55.69-1.55%201.55v7.6l-1.3-1.05c-.6-.49-1.48-.42-1.99.16-.46.52-.45%201.3.03%201.81l3.4%203.63c.76.81%201.82%201.27%202.93%201.27h3.04c1.78%200%203.22-1.44%203.22-3.22v-4.69c0-.8-.65-1.45-1.45-1.45-.27%200-.52.07-.74.2-.13-.65-.71-1.15-1.4-1.15-.32%200-.62.11-.86.29-.18-.55-.69-.94-1.3-.94-.23%200-.45.06-.64.15V3.75c0-.86-.69-1.55-1.55-1.55z%22%20fill=%22%23ffffff%22%20stroke=%22%234a8b66%22%20stroke-width=%221.5%22%20stroke-linejoin=%22round%22/%3E%3C/svg%3E") 10 3, pointer;
}
a, button, .btn, [role="button"], summary, label[for], select,
input[type="checkbox"], input[type="radio"], input[type="submit"],
input[type="button"], input[type="reset"], .site-nav-link {
  cursor: var(--cursor-pointer);
}
input[type="text"], input[type="email"], input[type="tel"], input[type="search"],
input[type="number"], input[type="password"], input[type="url"], textarea,
[contenteditable="true"] { cursor: text; }

/* Keyboard-focus ring. The UA default outline washes out on the deep-dark
   --paper base, so give every focusable element a visible accent ring — but
   only for keyboard nav (:focus-visible), so mouse clicks don't show it. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }

/* Inline per-field validation hints for quote forms (shared — JNCSForm in
   site-theme.js writes the text; empty = hidden). Matches the homepage modal. */
.q-hint { font-size: 12px; line-height: 1.4; margin-top: 4px; color: var(--err); font-weight: 500; }
.q-hint:empty { display: none; }
input.is-error, textarea.is-error, select.is-error { border-color: var(--err) !important; }

/* ── Skip link ────────────────────────────────────────────────
   First focusable element on every page. Off-screen until a
   keyboard user tabs to it, then drops into view; jumps focus
   past the nav to the page's #main landmark. */
.site-skip {
  position: absolute;
  left: 12px; top: -52px;
  z-index: 200;
  padding: 10px 18px;
  background: var(--accent);
  color: var(--ink-on-accent);
  font: 600 14px/1 var(--font-body);
  text-decoration: none;
  border-radius: 0 0 8px 8px;
  transition: top 0.16s ease;
}
.site-skip:focus { top: 0; outline: 2px solid var(--ink); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .site-skip { transition: none; } }

/* ── Top nav ──────────────────────────────────────────────────
   Sits at the top of every page. Brand left, primary nav right.
   Sticky so it's available while scrolling long pages. Mostly
   transparent — a faint hairline separates it from content. */
.site-nav {
  position: sticky; top: 0; z-index: 50;
  display: flex;
  align-items: center;
  gap: var(--r-5);
  padding: 18px clamp(20px, 5vw, 48px);
  background: rgba(11,12,14,0.78);
  border-bottom: 1px solid var(--line);
  backdrop-filter: saturate(140%) blur(10px);
  -webkit-backdrop-filter: saturate(140%) blur(10px);
  /* One-time entrance: the bar settles down softly on page load. */
  animation: navEnter 0.55s cubic-bezier(0.22, 1, 0.36, 1) both;
}
/* "One-time" means once per tab-session, not once per page — replaying
   the entrance on every navigation blinked the whole header out for
   half a second and made moving between pages feel like a flash.
   site-theme.js stamps nav-seen (sessionStorage) before first paint. */
html.nav-seen .site-nav, html.nav-seen .site-nav-link { animation: none; }
@keyframes navEnter {
  from { opacity: 0; transform: translateY(-10px); }
  to   { opacity: 1; transform: translateY(0); }
}
.site-nav-brand {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 18px;
  letter-spacing: 0.18em;
  color: var(--ink);
  flex-shrink: 0;
  transition: color 0.18s, text-shadow 0.18s;
}
.site-nav-brand:hover {
  color: var(--accent);
  text-shadow: 0 0 18px var(--accent-glow);
}
/* SVG wordmark (injected by site-theme.js — plain text without JS).
   All four letter paths rest in the ink color; hovering cascades them
   to brand green left-to-right (per-path delays below — the delays live
   ONLY in the hover rule, so un-hovering reverts together, no reverse
   ripple). Strokes are pinned to var(--ink) rather than currentColor
   so the link-level hover color above (for the text fallback) can't
   tint the whole mark at once and spoil the cascade. The hover grow is
   a gentle spring; click squishes, release springs back. The padding/
   negative-margin pair widens the link's hit area to ~42px without
   moving the mark. */
.site-nav-brand { display: inline-flex; align-items: center; padding: 6px 10px; margin: -6px -10px; position: relative; }
.site-nav-brand svg {
  display: block; height: 30px; width: auto;
  transform-origin: left center;
  /* Zero-alpha twin of the charge glow — filter can't interpolate
     from `none`, so this is what lets it fade instead of pop. */
  filter: drop-shadow(0 0 0 rgba(245,197,24,0));
  transition-property: transform, filter;
  transition-duration: 0.42s, 0.5s;
  transition-timing-function: cubic-bezier(0.22, 1.35, 0.36, 1), ease;
}
.site-nav-brand svg path { stroke: var(--ink); transition: stroke 0.16s ease; }
.site-nav-brand:hover svg {
  transform: scale(1.06);
  /* "Charging up": the electric-yellow glow waits out the green cascade
     (done ~0.37s in) and only then fades up — it rewards a held hover.
     The delay lives here only, so un-hovering drops the glow at once. */
  filter: drop-shadow(0 0 9px rgba(245,197,24,0.5));
  transition-delay: 0s, 0.42s;
}
.site-nav-brand:hover svg path { stroke: var(--accent); }
.site-nav-brand:hover svg path:nth-of-type(1) { transition-delay: 0s; }
.site-nav-brand:hover svg path:nth-of-type(2) { transition-delay: 0.07s; }
.site-nav-brand:hover svg path:nth-of-type(3) { transition-delay: 0.14s; }
.site-nav-brand:hover svg path:nth-of-type(4) { transition-delay: 0.21s; }
.site-nav-brand:active svg { transform: scale(0.95); transition-duration: 0.12s; }
/* Press sparks — site-theme.js spawns a handful of these on pointerdown,
   each with its own angle (--sa), travel (--sd), and start point (--sx/
   --sy). The spark is a tiny dash oriented along its flight line; it
   flies out, shrinks, and fades. Removed from the DOM on animationend.
   Keyframes only (a one-shot burst, not a retargetable state). */
.brand-spark {
  position: absolute;
  left: var(--sx); top: var(--sy);
  width: var(--sw, 5px); height: 2px;
  border-radius: 2px;
  background: #f5c518;   /* electric yellow — matches the charge glow, reads on both themes */
  pointer-events: none;
  transform: rotate(var(--sa)) translateX(3px);
  /* 0.28s, steep front-loaded curve: sparks read as a crack, not a drip
     (timing-under-300ms — the 0.55s cut read "liquid"), and the whole
     burst finishes inside the brand click's 260ms navigation hold. */
  animation: brandSpark 0.28s cubic-bezier(0.2, 0.85, 0.3, 1) forwards;
}
@keyframes brandSpark {
  0%   { opacity: 1; }
  55%  { opacity: 0.85; }   /* stay lit through the flight, die at the end */
  100% { transform: rotate(var(--sa)) translateX(var(--sd)) scaleX(0.3); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .site-nav-brand svg, .site-nav-brand svg path { transition: none; }
  .site-nav-brand:hover svg, .site-nav-brand:active svg { transform: none; }
  .brand-spark { display: none; }
}
.site-nav-links {
  display: flex;
  gap: var(--r-3);
  margin-left: auto;
  align-items: center;
  position: relative;   /* anchor for the sliding indicator */
}
/* Links cascade in with a tiny stagger (driven by --nav-i, set by
   site-theme.js; harmless 0-delay default without JS). */
.site-nav-link {
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-70);
  white-space: nowrap;   /* multi-word links ("Find your build") must not fold into a tall stack near the burger gate */
  padding: 8px 12px;
  border-radius: var(--r-2);
  letter-spacing: 0.01em;
  transition: color 0.16s, background 0.16s, transform 0.16s;
  animation: navLinkEnter 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
  animation-delay: calc(0.08s + var(--nav-i, 0) * 0.05s);
}
@keyframes navLinkEnter {
  from { opacity: 0; transform: translateY(-7px); }
  to   { opacity: 1; transform: translateY(0); }
}
.site-nav-link:hover {
  color: var(--ink);
  background: var(--paper-2);
  transform: translateY(-1px);
}
.site-nav-link.active {
  color: var(--ink);
}
/* Static active underline — the no-JS fallback. When site-theme.js
   activates the sliding indicator it adds .nav-slider-on to <html>
   and this is suppressed in favor of the glider. */
.site-nav-link.active::after {
  content: '';
  display: block;
  height: 1px;
  background: var(--accent);
  margin-top: 4px;
  margin-left: 12px;
  margin-right: 12px;
}
html.nav-slider-on .site-nav-link.active::after { display: none; }

/* ── Sliding accent indicator ────────────────────────────────
   A 2px bar that rests under the active link and glides to
   whichever link is hovered (injected by site-theme.js). The
   spring-ish easing gives it the 'design tool' feel without
   being bouncy. Hidden in the mobile dropdown, where links
   stack vertically. */
.site-nav-glider {
  position: absolute;
  bottom: 0;
  left: 0;
  height: 2px;
  border-radius: 2px;
  background: var(--accent);
  box-shadow: 0 0 10px var(--accent-glow);
  opacity: 0;
  pointer-events: none;
  transition:
    transform 0.32s cubic-bezier(0.3, 1.2, 0.35, 1),
    width 0.32s cubic-bezier(0.3, 1.2, 0.35, 1),
    opacity 0.2s ease;
}
.site-nav-glider.is-on { opacity: 1; }
@media (max-width: 900px) { .site-nav-glider { display: none; } }

/* Respect users who've asked for less motion: kill the entrance
   choreography and the glider's travel (it still appears, it just
   doesn't animate). Hover transforms drop too. */
@media (prefers-reduced-motion: reduce) {
  .site-nav, .site-nav-link { animation: none; }
  .site-nav-glider { transition: opacity 0.2s ease; }
  .site-nav-link:hover { transform: none; }
}

/* ── Mobile nav (hamburger) ───────────────────────────────────
   Without this the .site-nav-links row overflows horizontally on
   phones (every site page → horizontal page scroll). The burger
   button is injected by site-theme.js and toggles `.nav-open` on
   the nav; below 900px the links collapse into a dropdown.
   (900, not the 760 house step: the 7-link row + brand + toggle
   measures ~860px un-wrapped, so lower gates left a band with
   horizontal page scroll. 900 is already a tuned gate here — the
   home hero grid and the configurator phone wizard both collapse
   at it.) Hidden (display:none) above the breakpoint. */
.site-nav-burger {
  display: none;
  margin-left: auto;
  width: 40px; height: 40px;
  flex-direction: column; align-items: center; justify-content: center;
  gap: 4px;
  background: transparent;
  border: 1px solid var(--line-2);
  border-radius: var(--r-2);
  cursor: var(--cursor-pointer);
  padding: 0;
  flex-shrink: 0;
}
.site-nav-burger span {
  display: block;
  width: 18px; height: 2px;
  background: var(--ink-70);
  border-radius: 2px;
  transition: transform 0.25s ease, opacity 0.2s ease, background 0.12s;
}
.site-nav-burger:hover { border-color: var(--accent); }
.site-nav-burger:hover span { background: var(--ink); }

@media (max-width: 900px) {
  .site-nav { flex-wrap: wrap; gap: var(--r-3); }
  .site-nav-burger { display: inline-flex; }
  .site-nav-links {
    flex-basis: 100%;
    margin-left: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
  }
  /* Collapse into the burger dropdown ONLY when JS has injected the burger
     (.nav-has-burger). With JS off there's no burger, so the links stay
     visible + stacked and mobile navigation still works (progressive
     enhancement instead of a JS-dependent hidden menu). */
  .site-nav.nav-has-burger .site-nav-links {
    max-height: 0;
    overflow: hidden;
    opacity: 0;
    pointer-events: none;
    transition: max-height 0.3s ease, opacity 0.2s ease;
  }
  .site-nav.nav-open .site-nav-links {
    max-height: 80vh;
    overflow-y: auto;
    opacity: 1;
    pointer-events: auto;
    padding: var(--r-2) 0 var(--r-3);
  }
  .site-nav-links .site-nav-link {
    padding: 12px 8px;
    border-radius: var(--r-2);
  }
  .site-nav-link.active::after { display: none; }
  /* burger bars morph into an X when the menu is open */
  .site-nav.nav-open .site-nav-burger span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
  .site-nav.nav-open .site-nav-burger span:nth-child(2) { opacity: 0; }
  .site-nav.nav-open .site-nav-burger span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
}

/* ── Footer ──────────────────────────────────────────────────── */
.site-foot {
  margin-top: var(--r-8);
  padding: var(--r-6) clamp(20px, 5vw, 48px) var(--r-5);
  border-top: 1px solid var(--line);
  color: var(--ink-50);
  font-size: 13px;
  display: flex;
  flex-wrap: wrap;
  gap: var(--r-3);
  align-items: baseline;
  justify-content: space-between;
}
.site-foot a { color: var(--ink-70); }
.site-foot a:hover { color: var(--ink); }
.site-foot-brand {
  font-family: var(--font-mono);
  letter-spacing: 0.12em;
  font-size: 11px;
  text-transform: uppercase;
  color: var(--ink-30);
}

/* ── Page container helpers ──────────────────────────────────── */
.page-wrap {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 clamp(20px, 5vw, 48px);
}

.kicker {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-50);
}
.h-display {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.05;
}

/* ── Buttons ────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 16px;
  border-radius: var(--r-2);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: 600;
  border: 1px solid transparent;
  cursor: var(--cursor-pointer);
  transition: filter 0.12s, background 0.12s, border-color 0.12s, color 0.12s;
}
.btn-primary {
  background: var(--accent);
  color: var(--ink-on-accent);
}
.btn-primary:hover { filter: brightness(1.08); }
.btn-ghost {
  background: transparent;
  color: var(--ink-70);
  border-color: var(--line-2);
}
.btn-ghost:hover {
  color: var(--ink);
  border-color: var(--accent);
}

/* ── Theme toggle (floating) ─────────────────────────────────
   Injected by site-theme.js. Sits in the top-right corner over
   the sticky nav (z-index > nav's 50). Reads as a nav element
   visually; works the same on pages without a nav. */
.site-theme-toggle {
  position: fixed;
  top: 14px;
  right: clamp(20px, 5vw, 48px);
  z-index: 60;
  width: 38px; height: 38px;
  border-radius: 50%;
  background: var(--paper-2);
  color: var(--ink-70);
  /* Green-tinted resting border — branded at rest, louder on hover.
     Same chip language as the home page's privacy button. */
  border: 1px solid var(--accent-glow);
  cursor: var(--cursor-pointer);
  display: inline-flex; align-items: center; justify-content: center;
  transition: color 0.16s, border-color 0.16s, box-shadow 0.3s,
              transform 0.42s cubic-bezier(0.22, 1.35, 0.36, 1);
  font-family: inherit;
  padding: 0;
}
.site-theme-toggle:hover {
  color: var(--accent);
  border-color: var(--accent);
  box-shadow: 0 0 12px var(--accent-glow);
  transform: scale(1.08);
}
.site-theme-toggle:active { transform: scale(0.92); transition-duration: 0.16s, 0.16s, 0.3s, 0.12s; }
/* The sun/moon is a stroke SVG in the wordmark's language (round caps,
   same weight). syncBtn() swaps the node on toggle, so the entrance
   animation replays on every switch — a little quarter-spin in. */
.site-theme-toggle svg { display: block; animation: themeIconIn 0.4s cubic-bezier(0.22, 1.35, 0.36, 1); }
@keyframes themeIconIn {
  from { transform: rotate(-75deg) scale(0.55); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .site-theme-toggle { transition: none; }
  .site-theme-toggle:hover, .site-theme-toggle:active { transform: none; }
  .site-theme-toggle svg { animation: none; }
}
/* Inline variant — sits in .site-nav-links before the CTA. Drop the
   fixed-positioning so flex layout owns placement; the nav's gap
   handles spacing. Slightly smaller to feel like a nav element. */
.site-theme-toggle-inline {
  position: static;
  top: auto; right: auto;
  width: 34px; height: 34px;
  z-index: auto;
  background: transparent;
}

/* ── Deep footer (home + landing pages) — centered brand / contact / copy ── */
.h-foot-deep {
  margin-top: var(--r-7);
  padding: var(--r-6) clamp(20px, 5vw, 48px) var(--r-5);
  border-top: 1px solid var(--line);
  text-align: center;
}
.h-foot-brand {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 16px;
  letter-spacing: 0.04em;
  color: var(--ink);
  margin-bottom: var(--r-3);
}
.h-foot-contact {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--r-5);
  font-size: 13px; color: var(--ink-70);
  line-height: 1.6;
  margin-bottom: var(--r-4);
}
.h-foot-contact a { color: var(--ink-70); }
.h-foot-contact a:hover { color: var(--accent); }
.h-foot-copy {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-30);
}
