/* ============================================================================
   nucleus — SHARED SITE CHROME.  Load this on EVERY page, always:
       <link rel="stylesheet" href="/platform/assets/site.css">
   and pair it with:
       <script src="/platform/assets/langselect.js" defer></script>

   Same layout system as web/dispatch (a sibling site on the same host); only the
   palette differs. Nucleus is cyan + indigo, taken from its own logo.

   It owns the design tokens, the header (desktop + mobile), the nav + hamburger,
   and the footer — so a page only writes its OWN content styles and can never
   ship a broken header again. Start new pages from  web/nucleus-site/_template.html.

   Loaded AFTER a page's inline <style>, so these rules win on equal specificity.
   ============================================================================ */

/* ── design tokens ─────────────────────────────────────────────────────────── */
:root{
  --bg:#0A0C12; --surface:#11141C; --surface-2:#161A24; --raise:#1C2130;
  --line:#232838; --line-strong:#303749;
  --text:#F3F5F9; --muted:#8B93A7; --faint:#586078;
  --accent:#22D3EE; --accent-dim:rgba(34,211,238,.12); --accent-line:rgba(34,211,238,.34);
  --accent-2:#818CF8; --accent-2-dim:rgba(129,140,248,.12); --accent-2-line:rgba(129,140,248,.34);
  --danger:#FF4D4D; --warn:#FF7A45; --stale:#E0796B;
  /* langselect.js is shared verbatim with the dispatch site and reads --gold*. Alias them
     to the accent so the language menu matches this site instead of falling back to gold. */
  --gold:var(--accent); --gold-dim:var(--accent-dim); --gold-line:var(--accent-line);
  --sans:-apple-system,BlinkMacSystemFont,"SF Pro Text","SF Pro Display","Helvetica Neue",Arial,sans-serif;
  --mono:ui-monospace,"SF Mono","SFMono-Regular",Menlo,Monaco,"Cascadia Code",monospace;
  --wrap:1080px;
  --bar-h:62px;
}

/* ── base ──────────────────────────────────────────────────────────────────── */
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--text);font-family:var(--sans);line-height:1.55;
  -webkit-font-smoothing:antialiased;min-height:100vh}
::selection{background:var(--accent);color:#0A0C12}
.wrap{max-width:var(--wrap);margin:0 auto;padding:0 24px}
.mono{font-family:var(--mono)}
.eyebrow{font-family:var(--mono);font-size:11.5px;letter-spacing:.22em;text-transform:uppercase;
  color:var(--accent);font-weight:600}
.label{font-family:var(--mono);font-size:10.5px;letter-spacing:.2em;text-transform:uppercase;color:var(--faint)}

/* ── header ────────────────────────────────────────────────────────────────── */
/* `header.bar` (not `.bar`) so this beats any page-level `header.bar` rule. */
header.bar{position:sticky;top:0;z-index:100;background:rgba(10,12,18,.72);
  -webkit-backdrop-filter:saturate(140%) blur(12px);backdrop-filter:saturate(140%) blur(12px);
  border-bottom:1px solid var(--line)}
/* Pinned width so the bar is identical regardless of a page's own .wrap. */
header.bar .wrap{max-width:var(--wrap);margin:0 auto;padding:0 24px}
.bar-in{position:relative;display:flex;align-items:center;gap:20px;height:var(--bar-h)}

.brand{display:flex;align-items:center;gap:11px;font-weight:600;letter-spacing:-.01em;
  text-decoration:none;color:var(--text)}
.brand:hover{color:var(--text)}
.brand img{width:30px;height:30px;display:block;flex:none}   /* never let the logo render full-size */
.brand b{font-size:16.5px;color:var(--text);font-weight:600} /* explicit — kills default link purple */

.nav{margin-left:auto;display:flex;align-items:center;gap:22px}
.nav a{font-size:13.5px;color:var(--muted);text-decoration:none;transition:color .15s}
.nav a:hover,.nav a:focus-visible{color:var(--text)}

/* Sign in: the last nav link, a pill on desktop; on mobile it is a plain menu item. */
.nav a.nav-signin{color:var(--bg);background:var(--accent);font-weight:600;padding:7px 14px;border-radius:999px}
.nav a.nav-signin:hover,.nav a.nav-signin:focus-visible{color:var(--bg);background:#67E8F9}
/* hamburger — injected by langselect.js as the FIRST child of .bar-in */
.navtoggle{display:none;background:none;border:0;color:var(--text);cursor:pointer;
  padding:6px;border-radius:8px;line-height:0}
.navtoggle svg{width:24px;height:24px;display:block}
.navtoggle:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* language selector (langselect.js appends it last) */
.dl-lang.in-bar{margin-left:auto}

/* ── header · mobile: hamburger LEFT, brand CENTERED, language RIGHT ───────── */
@media (max-width:840px){
  .navtoggle{display:inline-flex;align-items:center;order:1}
  /* absolute centring is immune to the differing widths of the two side items */
  .brand{position:absolute;left:50%;transform:translateX(-50%);order:2}
  .dl-lang.in-bar{margin-left:auto;order:3}

  /* A floating rounded card, inset from the edges — the SAME treatment as the
     language menu (.dl-lang-menu), so the two dropdowns match. `fixed` so no
     ancestor can clip it; it scrolls rather than truncating if it gets tall. */
  .nav{position:fixed;top:calc(var(--bar-h) + 8px);left:16px;right:16px;z-index:99;margin:0;
    flex-direction:column;align-items:stretch;gap:2px;
    max-height:calc(100vh - var(--bar-h) - 24px);overflow-y:auto;
    background:var(--surface);border:1px solid var(--line);border-radius:14px;
    padding:6px;display:none;
    box-shadow:0 26px 55px -28px rgba(0,0,0,.92)}
  header.bar.nav-open .nav{display:flex}
  .nav a{font-size:15px;padding:13px 12px;border-radius:9px;color:var(--text)}
  .nav a:hover,.nav a:active,.nav a:focus-visible{background:var(--accent-dim);color:var(--accent)}
  .nav a.nav-signin{margin-top:4px;text-align:center}
}

/* ── footer ────────────────────────────────────────────────────────────────── */
footer{border-top:1px solid var(--line);padding:26px 0;color:var(--faint)}
.foot-in{display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;
  font-family:var(--mono);font-size:11px;letter-spacing:.04em}

@media (prefers-reduced-motion:reduce){
  .nav a,.brand,.navtoggle{transition:none}
}

/* ── scroll reveal ─────────────────────────────────────────────────────────────
   Blocks fade + rise as they enter the viewport. Everything is scoped under
   html.reveal-ready, which reveal.js adds to <html> itself — so if the script
   never runs (JS off, parse error, reduced-motion bail) nothing is hidden and
   the page renders exactly as it did before. Never author the hidden state
   outside that scope.

   The .rise override matters: use-case pages define .rise with a load-time
   `animation:rise … forwards`, which fires on page load regardless of scroll
   position. site.css is linked *after* those <style> blocks, so cancelling the
   animation here lets the same elements reveal on scroll instead. Their inline
   animation-delay values go inert with it; reveal.js re-staggers via --rv-d. */
html.reveal-ready .rv{opacity:0;transform:translateY(16px);
  transition:opacity .7s cubic-bezier(.22,.7,.2,1),transform .7s cubic-bezier(.22,.7,.2,1);
  transition-delay:var(--rv-d,0s)}
html.reveal-ready .rv.rv-in{opacity:1;transform:none}
html.reveal-ready .rise{animation:none}

/* Blocks already on screen when the script runs get .rv and .rv-in in the same
   tick, so they simply render visible — no transition, because there is no
   painted start state to animate from. Only scrolled-to blocks animate, which
   is the whole point of the effect.
   An earlier version gave that first batch a keyframe so it would fade in on
   load too. Don't: an engine that isn't advancing its animation clock (headless
   under a virtual time budget, and by extension anything that throttles
   animations) holds `animation-fill-mode: both` at the `from` state — opacity 0
   — and the entire above-the-fold page renders blank. Instant is correct here;
   nothing that hides content may depend on an animation actually running. */
