/* ==========================================================================
   SmartSignals — design tokens + reset + shared atoms
   Loaded first by every page. Nothing page-specific lives here.

   THEMING
   -------
   Three states: auto (follow the OS), dark, light. Dark is the brand default,
   so the raw values on :root are the dark ones.

   The light values are declared once as --l-* variables and then *mapped* onto
   the real tokens in two places — the auto media query and the explicit
   [data-theme="light"] selector. Declaring them once is what stops the two
   mappings drifting apart as the palette evolves.

   Resolution:
     no attribute + OS dark   → dark   (base :root)
     no attribute + OS light  → light  (media query)
     data-theme="dark"        → dark   (media query excluded by :not)
     data-theme="light"       → light  (explicit selector)
   ========================================================================== */

:root{
  /* ── Light palette source values ──────────────────────────────────────── */
  /* Declared here so they exist in one place; mapped onto real tokens below. */
  --l-bg:          #f4f6fb;
  --l-bg-2:        #eaeef6;
  --l-panel:       #ffffff;
  --l-panel-2:     #fbfcfe;
  --l-panel-3:     #f1f4fa;
  --l-elevated:    #ffffff;

  /* One hue at three weights. These two were gold, left behind when the accent
     went cyan -- the same leftover the --bloom note describes, and the reason
     every ghost button still had a warm ring round it on a cool page. Taking
     --l-line-soft's hue keeps the family coherent; the alphas are unchanged, so
     nothing gets heavier or lighter, only cooler. */
  --l-line:        rgba(22,32,54,.30);
  --l-line-soft:   rgba(22,32,54,.12);
  --l-line-strong: rgba(22,32,54,.55);

  --l-accent:        #0a6f85;   /* dark enough for small uppercase mono to clear AA */
  --l-accent-lt:     #0d8ba6;
  --l-accent-dk:     #075566;
  --l-accent-glow:   rgba(10,111,133,.12);

  --l-cyan:        #0a6f85;
  --l-cyan-dim:    rgba(10,111,133,.10);
  --l-violet:      #5741b8;
  --l-violet-dim:  rgba(87,65,184,.10);
  /* Darkened from #0b8f5c, which measured 3.94 as text on the phone screen and
     3.81 on the light page - under AA on both. The labels sit on a tinted chip
     rather than the bare screen, so it is darker again than the bare-white
     figure suggested. */
  --l-green:       #09703f;
  --l-green-dim:   rgba(11,143,92,.11);
  /* Same reason as the green above: 4.34 on the tinted chip, just under AA. */
  --l-red:         #b82836;
  --l-red-dim:     rgba(198,47,61,.10);
  --l-amber:       #a86412;

  --l-text:        #121a2b;
  --l-text-2:      #38445c;
  --l-muted:       #5d6b83;
  --l-dim:         #8d9ab0;

  /* Translucent surface fills. On dark these lighten, on light they darken —
     which is why every panel fill has to be a token rather than a literal. */
  --l-fill-1:      rgba(22,32,54,.026);
  --l-fill-2:      rgba(22,32,54,.05);
  --l-fill-3:      rgba(22,32,54,.08);
  --l-hairline:    rgba(22,32,54,.08);

  /* Slightly translucent so the scanlines read *through* the panels, the way
     they do on the dark palette where the surfaces are already see-through.
     Fully opaque cards made the light theme look like a different product. */
  --l-panel-grad:  linear-gradient(165deg,rgba(255,255,255,.88),rgba(245,247,252,.93));
  --l-panel-grad-2:linear-gradient(160deg,rgba(255,255,255,.92),rgba(242,245,251,.95));
  --l-shadow:      0 10px 26px -14px rgba(20,32,60,.28);
  --l-shadow-lg:   0 26px 60px -26px rgba(20,32,60,.34);
  --l-scrim:       rgba(30,40,60,.42);
  --l-chrome:       rgba(255,255,255,.78);
  --l-chrome-solid: rgba(255,255,255,.96);
  /* CRT: on a light tube the scanline is the darker gap between phosphor rows. */
  --l-scan-h:       rgba(20,32,58,.050);
  --l-scan-v:       rgba(20,32,58,.022);
  --l-vignette:     rgba(38,50,78,.15);
  --l-bloom:        rgba(10,111,133,.05);

  /* The device screen in the hero, for when the page is LIGHT — so, the dark
     set. The mockup runs against the page rather than with it. */
  --l-hp-bg-a:      #080d17;
  --l-hp-bg-b:      #0c1322;
  --l-hp-text:      #e9edf6;
  --l-hp-sym:       #ffffff;
  --l-hp-mute:      #8e9bb4;
  --l-hp-dim:       #6d7b93;
  --l-hp-lvl:       #c3ccdd;
  --l-hp-fill:      rgba(255,255,255,.045);
  --l-hp-fill-2:    rgba(255,255,255,.04);
  --l-hp-line:      rgba(255,255,255,.07);
  --l-hp-line-soft: rgba(255,255,255,.05);
  --l-hp-alert:     rgba(16,23,39,.95);
  --l-hp-alert-sub: #9dabc2;
  --l-hp-shadow:    0 12px 28px rgba(0,0,0,.6);
  --l-hp-grid:      rgba(150,175,215,.07);
  --l-hp-wick:      rgba(170,190,220,.42);
  --l-hp-chip:      rgba(9,14,25,.92);

  /* ── Dark palette — the default ───────────────────────────────────────── */
  --bg:          #05070d;
  --bg-2:        #080c15;
  --panel:       #0b111e;
  --panel-2:     #0e1524;
  --panel-3:     #121a2b;
  --elevated:    #151e30;

  /* See the note on --l-line: gold left over from the pre-cyan palette. */
  --line:        rgba(150,170,200,.14);
  --line-soft:   rgba(150,170,200,.09);
  --line-strong: rgba(150,170,200,.34);

  /* Named so the phone can ask for them by name. Its screen is inverted, so it
     needs whichever set the page is not using. */
  --d-accent:      #3de0f5;
  --d-accent-lt:   #7fecff;
  --d-accent-dk:   #1a94a8;
  --d-accent-glow: rgba(61,224,245,.15);

  --accent:        var(--d-accent);
  --accent-lt:     var(--d-accent-lt);
  --accent-dk:     var(--d-accent-dk);
  --accent-glow:   var(--d-accent-glow);

  --cyan:        #3de0f5;
  --cyan-dim:    rgba(61,224,245,.14);
  --violet:      #8b6cff;
  --violet-dim:  rgba(139,108,255,.14);
  --d-green:     #35dd92;
  --d-green-dim: rgba(53,221,146,.13);
  --d-red:       #ff5d6c;
  --d-red-dim:   rgba(255,93,108,.13);

  --green:       var(--d-green);
  --green-dim:   var(--d-green-dim);
  --red:         var(--d-red);
  --red-dim:     var(--d-red-dim);
  --amber:       #ffb454;

  --text:        #e9edf6;
  --text-2:      #b9c2d4;
  --muted:       #76849c;
  --dim:         #4a566b;

  --fill-1:      rgba(255,255,255,.02);
  --fill-2:      rgba(255,255,255,.035);
  --fill-3:      rgba(255,255,255,.07);
  --hairline:    rgba(255,255,255,.045);

  --panel-grad:  linear-gradient(165deg,rgba(18,26,43,.72),rgba(9,14,24,.86));
  --panel-grad-2:linear-gradient(160deg,rgba(18,26,43,.86),rgba(8,12,21,.94));
  --shadow:      0 18px 44px -20px rgba(0,0,0,.9);
  --shadow-lg:   0 34px 90px -28px rgba(0,0,0,.95);
  --scrim:       rgba(0,0,0,.6);
  /* Sticky bars that sit over scrolling content and blur what is behind. */
  --chrome:       rgba(5,7,13,.72);
  --chrome-solid: rgba(5,7,13,.95);
  /* CRT: on a dark tube the rows themselves glow, so the line is the light one. */
  --scan-h:       rgba(190,215,255,.040);
  --scan-v:       rgba(190,215,255,.018);
  --vignette:     rgba(0,0,0,.50);
  /* Phosphor. Left gold when the accent went cyan, which put a warm stain
     in the middle of a cool tube — subtle, and wrong every time. */
  --bloom:        rgba(61,224,245,.055);

  /* ── Type ─────────────────────────────────────────────────────────────── */
  /* IBM Plex Sans throughout. It was drawn for a technology company and shares
     a skeleton with JetBrains Mono below, so the tracked mono labels and the
     running copy read as one system rather than two borrowed faces. It replaces
     DM Serif Display on the wordmark and DM Sans everywhere else; the display
     token is now a weight and tracking difference, not a different family. */
  --f-display: 'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --f-sans:    'IBM Plex Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --f-mono:    'JetBrains Mono', ui-monospace, 'SF Mono', 'Cascadia Mono', Menlo, monospace;

  /* ── Rhythm ───────────────────────────────────────────────────────────── */
  --r-sm: 6px;
  --r:    10px;
  --r-lg: 16px;

  --ease: cubic-bezier(.22,.61,.36,1);
  --nav-h: 62px;

  /* Device screen. These are the LIGHT values, on the dark theme — the mockup
     runs against the page rather than with it, so the device reads as an object
     sitting on the page instead of a panel cut out of it. The mapping below
     hands it the dark set when the page goes light. */
  --hp-bg-a:      #f8fafd;
  --hp-bg-b:      #eef2f9;
  --hp-text:      #101828;
  --hp-sym:       #0a1020;
  --hp-mute:      #6a778d;
  --hp-dim:       #97a2b5;
  --hp-lvl:       #3b4760;
  --hp-fill:      rgba(16,26,48,.05);
  --hp-fill-2:    rgba(16,26,48,.04);
  --hp-line:      rgba(16,26,48,.11);
  --hp-line-soft: rgba(16,26,48,.07);
  --hp-alert:     rgba(255,255,255,.97);
  --hp-alert-sub: #5d6b83;
  --hp-shadow:    0 12px 28px rgba(20,32,56,.24);
  --hp-grid:      rgba(40,60,95,.10);
  --hp-wick:      rgba(40,60,95,.45);
  --hp-chip:      rgba(252,253,255,.94);

  color-scheme: dark;
}
/* The phone screen is the opposite of the page, so its semantics are too.
   Left following the page they land on their own surface: measured 1.41
   for the green on the light screen the dark theme paints. */
.phone{
  --accent:var(--l-accent); --accent-lt:var(--l-accent-lt);
  --accent-dk:var(--l-accent-dk); --accent-glow:var(--l-accent-glow);
  --green:var(--l-green); --green-dim:var(--l-green-dim);
  --red:var(--l-red); --red-dim:var(--l-red-dim);
}


/* The light mapping, written once as a variable list and applied twice. */
@media (prefers-color-scheme: light){
  :root:not([data-theme="dark"]){
    --bg:var(--l-bg); --bg-2:var(--l-bg-2);
    --panel:var(--l-panel); --panel-2:var(--l-panel-2); --panel-3:var(--l-panel-3);
    --elevated:var(--l-elevated);
    --line:var(--l-line); --line-soft:var(--l-line-soft); --line-strong:var(--l-line-strong);
    --accent:var(--l-accent); --accent-lt:var(--l-accent-lt); --accent-dk:var(--l-accent-dk);
    --accent-glow:var(--l-accent-glow);
    --cyan:var(--l-cyan); --cyan-dim:var(--l-cyan-dim);
    --violet:var(--l-violet); --violet-dim:var(--l-violet-dim);
    --green:var(--l-green); --green-dim:var(--l-green-dim);
    --red:var(--l-red); --red-dim:var(--l-red-dim);
    --amber:var(--l-amber);
    --text:var(--l-text); --text-2:var(--l-text-2);
    --muted:var(--l-muted); --dim:var(--l-dim);
    --fill-1:var(--l-fill-1); --fill-2:var(--l-fill-2); --fill-3:var(--l-fill-3);
    --hairline:var(--l-hairline);
    --panel-grad:var(--l-panel-grad); --panel-grad-2:var(--l-panel-grad-2);
    --shadow:var(--l-shadow); --shadow-lg:var(--l-shadow-lg); --scrim:var(--l-scrim);
    --chrome:var(--l-chrome); --chrome-solid:var(--l-chrome-solid);
    --scan-h:var(--l-scan-h); --scan-v:var(--l-scan-v);
    --vignette:var(--l-vignette); --bloom:var(--l-bloom);
    --hp-bg-a:var(--l-hp-bg-a); --hp-bg-b:var(--l-hp-bg-b);
    --hp-text:var(--l-hp-text); --hp-sym:var(--l-hp-sym);
    --hp-mute:var(--l-hp-mute); --hp-dim:var(--l-hp-dim); --hp-lvl:var(--l-hp-lvl);
    --hp-fill:var(--l-hp-fill); --hp-fill-2:var(--l-hp-fill-2);
    --hp-line:var(--l-hp-line); --hp-line-soft:var(--l-hp-line-soft);
    --hp-alert:var(--l-hp-alert); --hp-alert-sub:var(--l-hp-alert-sub);
    --hp-shadow:var(--l-hp-shadow);
    --hp-grid:var(--l-hp-grid); --hp-wick:var(--l-hp-wick); --hp-chip:var(--l-hp-chip);
    color-scheme: light;
  }
  /* The phone screen is the opposite of the page, so its semantics are too.
     Left following the page they land on their own surface: measured 1.41
     for the green on the light screen the dark theme paints. */
  :root:not([data-theme="dark"]) .phone{
    --accent:var(--d-accent); --accent-lt:var(--d-accent-lt);
    --accent-dk:var(--d-accent-dk); --accent-glow:var(--d-accent-glow);
    --green:var(--d-green); --green-dim:var(--d-green-dim);
    --red:var(--d-red); --red-dim:var(--d-red-dim);
  }

  /* The mark goes a step darker on a light nav. --l-accent is tuned to clear AA
     for small mono text, which leaves these thin diagonal strokes at 5.70
     against 12.64 in the dark theme -- half the contrast, and that is what
     reads as blur rather than as a lighter colour. --accent-dk takes it to
     8.26. Scoped to the light branches: in the dark theme the same token is
     #1a94a8, which on a dark nav would be a step the wrong way. */
  :root:not([data-theme="dark"]) .logo-mark{color:var(--accent-dk)}

}

:root[data-theme="light"]{
  --bg:var(--l-bg); --bg-2:var(--l-bg-2);
  --panel:var(--l-panel); --panel-2:var(--l-panel-2); --panel-3:var(--l-panel-3);
  --elevated:var(--l-elevated);
  --line:var(--l-line); --line-soft:var(--l-line-soft); --line-strong:var(--l-line-strong);
  --accent:var(--l-accent); --accent-lt:var(--l-accent-lt); --accent-dk:var(--l-accent-dk);
  --accent-glow:var(--l-accent-glow);
  --cyan:var(--l-cyan); --cyan-dim:var(--l-cyan-dim);
  --violet:var(--l-violet); --violet-dim:var(--l-violet-dim);
  --green:var(--l-green); --green-dim:var(--l-green-dim);
  --red:var(--l-red); --red-dim:var(--l-red-dim);
  --amber:var(--l-amber);
  --text:var(--l-text); --text-2:var(--l-text-2);
  --muted:var(--l-muted); --dim:var(--l-dim);
  --fill-1:var(--l-fill-1); --fill-2:var(--l-fill-2); --fill-3:var(--l-fill-3);
  --hairline:var(--l-hairline);
  --panel-grad:var(--l-panel-grad); --panel-grad-2:var(--l-panel-grad-2);
  --shadow:var(--l-shadow); --shadow-lg:var(--l-shadow-lg); --scrim:var(--l-scrim);
  --chrome:var(--l-chrome); --chrome-solid:var(--l-chrome-solid);
  --scan-h:var(--l-scan-h); --scan-v:var(--l-scan-v);
  --vignette:var(--l-vignette); --bloom:var(--l-bloom);
  --hp-bg-a:var(--l-hp-bg-a); --hp-bg-b:var(--l-hp-bg-b);
  --hp-text:var(--l-hp-text); --hp-sym:var(--l-hp-sym);
  --hp-mute:var(--l-hp-mute); --hp-dim:var(--l-hp-dim); --hp-lvl:var(--l-hp-lvl);
  --hp-fill:var(--l-hp-fill); --hp-fill-2:var(--l-hp-fill-2);
  --hp-line:var(--l-hp-line); --hp-line-soft:var(--l-hp-line-soft);
  --hp-alert:var(--l-hp-alert); --hp-alert-sub:var(--l-hp-alert-sub);
  --hp-shadow:var(--l-hp-shadow);
  --hp-grid:var(--l-hp-grid); --hp-wick:var(--l-hp-wick); --hp-chip:var(--l-hp-chip);
  color-scheme: light;
}
/* The phone screen is the opposite of the page, so its semantics are too.
   Left following the page they land on their own surface: measured 1.41
   for the green on the light screen the dark theme paints. */
:root[data-theme="light"] .phone{
  --accent:var(--d-accent); --accent-lt:var(--d-accent-lt);
  --accent-dk:var(--d-accent-dk); --accent-glow:var(--d-accent-glow);
  --green:var(--d-green); --green-dim:var(--d-green-dim);
  --red:var(--d-red); --red-dim:var(--d-red-dim);
}
/* The mark goes a step darker on a light nav. --l-accent is tuned to clear AA
   for small mono text, which leaves these thin diagonal strokes at 5.70
   against 12.64 in the dark theme -- half the contrast, and that is what
   reads as blur rather than as a lighter colour. --accent-dk takes it to
   8.26. Scoped to the light branches: in the dark theme the same token is
   #1a94a8, which on a dark nav would be a step the wrong way. */
:root[data-theme="light"] .logo-mark{color:var(--accent-dk)}



/* ── Reset ────────────────────────────────────────────────────────────────── */

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

html{
  scroll-behavior:smooth;
  -webkit-text-size-adjust:100%;
  scroll-padding-top:calc(var(--nav-h) + 20px);
}

body{
  background:var(--bg);
  color:var(--text);
  font-family:var(--f-sans);
  font-weight:400;
  font-size:16px;
  line-height:1.65;
  overflow-x:hidden;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility;
  transition:background-color .3s var(--ease),color .3s var(--ease);
}

img,svg,video,canvas{display:block;max-width:100%}
a{color:inherit;text-decoration:none}
button,input,select,textarea{font:inherit;color:inherit}
button{background:none;border:none;cursor:pointer}
table{border-collapse:collapse;width:100%}
::selection{background:var(--accent-glow);color:var(--text)}

:focus-visible{
  outline:2px solid var(--cyan);
  outline-offset:2px;
  border-radius:3px;
}

/* Scrollbars — thin and on-brand rather than default chrome */
*{scrollbar-width:thin;scrollbar-color:var(--line-strong) transparent}
*::-webkit-scrollbar{width:8px;height:8px}
*::-webkit-scrollbar-track{background:transparent}
*::-webkit-scrollbar-thumb{background:var(--line-strong);border-radius:4px}
*::-webkit-scrollbar-thumb:hover{background:var(--accent)}

/* ── The mark ─────────────────────────────────────────────────────────────
   A slow pulse travelling left to right through the five candles, so the logo
   reads as something live rather than a static badge. Each candle rests a
   little under full strength and comes up to it as the pulse passes, which is
   light moving through the mark — a dip would read as a flicker, as something
   broken.

   The delays are not in DOM order. The paths are stored 1093, 1384, 1204,
   1076, 1299 across the viewBox, so a naive nth-child stagger would scatter
   the pulse instead of sweeping it. Visual order is 4, 1, 3, 5, 2. */
/* Full opacity. These held .72 between flashes of the old per-candle ripple,
   so the mark spent almost all its time at 72% -- which on strokes this thin at
   22px does not read as restraint, it reads as out of focus. The beat on the
   whole mark now carries the motion, and the candles stay solid. */
.logo-mark path{opacity:1}

/* The candles carry the accent; the wordmark beside them keeps --text, which is
   why the colour is set on the mark rather than on .nav-logo -- currentColor in
   the SVG picks it up. It follows the theme: cyan on the dark page, the darker
   --l-accent on the light one, which is what clears contrast against it. */
.logo-mark{
  color:var(--accent);
  /* Scale, not a glow. A drop-shadow reads as a light leak around the mark
     rather than as the mark doing something; size is the thing the eye picks up
     at 22px. Transform does not reflow, so the nav row does not move. */
  transform-origin:center;
  animation:markBeat 4.2s var(--ease) infinite;
}

/* Two beats and a rest, not a sine: a heartbeat is what "live engine" means
   here, and an even swell just looks like the page is breathing. */
@keyframes markBeat{
  0%   {transform:scale(1)}
  7%   {transform:scale(1.16)}
  15%  {transform:scale(1)}
  23%  {transform:scale(1.09)}
  33%  {transform:scale(1)}
  100% {transform:scale(1)}
}

/* Hovering the logo runs the sweep through once, quickly. */
.nav-logo:hover .logo-mark,
.ck-brand:hover .logo-mark{animation-duration:1.5s}

@media (prefers-reduced-motion: reduce){
  .logo-mark{animation:none;transform:none}
}

/* ── Typography ───────────────────────────────────────────────────────────── */

/* Sans throughout. The display serif is now reserved for the wordmark, where it
   does brand work rather than setting the page's voice.
   The sizes come down a little with it: a tight 700-weight sans carries more
   visual mass than a 400-weight serif at the same measurement. */
h1,h2,h3,h4{font-family:var(--f-sans);font-weight:700;line-height:1.08;letter-spacing:-.03em}
h1{font-size:clamp(2.4rem,5.6vw,4rem);line-height:1.04}
h2{font-size:clamp(1.75rem,3.4vw,2.65rem)}
h3{font-size:1.15rem;font-weight:600;letter-spacing:-.008em;line-height:1.3}
h4{font-size:1rem;font-weight:600;letter-spacing:-.008em;line-height:1.35}
em{font-style:italic;color:var(--accent-lt)}

.mono{font-family:var(--f-mono);font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.num{font-family:var(--f-mono);font-variant-numeric:tabular-nums;font-feature-settings:'tnum' 1}

.eyebrow{
  font-family:var(--f-mono);
  font-size:.68rem;
  letter-spacing:.22em;
  text-transform:uppercase;
  color:var(--accent);
  display:flex;align-items:center;gap:10px;
  margin-bottom:16px;
}
.eyebrow::after{
  content:'';height:1px;flex:1;
  background:linear-gradient(90deg,var(--line-strong),transparent);
}
.eyebrow.center{justify-content:center}
.eyebrow.center::after,.eyebrow.center::before{
  content:'';height:1px;width:56px;flex:none;
  background:linear-gradient(90deg,transparent,var(--line-strong));
}
.eyebrow.center::after{background:linear-gradient(90deg,var(--line-strong),transparent)}

.lead{color:var(--muted);font-size:1.04rem;max-width:60ch}

/* ── Layout ───────────────────────────────────────────────────────────────── */

.wrap{width:min(1240px,calc(100% - 48px));margin-inline:auto}
.wrap-narrow{width:min(880px,calc(100% - 48px));margin-inline:auto}
section{padding:clamp(72px,9vw,124px) 0}

/* ── CRT surface ──────────────────────────────────────────────────────────────
   Replaces the old technical grid. A cathode tube reads as four things stacked:
   horizontal scanlines, a much fainter vertical aperture grille, a vignette
   from the curvature of the glass, and a slow refresh roll drifting down.

   Applied by giving a positioned element the .crt class; it paints into its own
   ::before (lines + vignette) and ::after (roll), so the host element keeps its
   own background. Everything is pointer-events:none and purely decorative.
   ────────────────────────────────────────────────────────────────────────── */

.crt{pointer-events:none;overflow:hidden}

.crt::before,
.crt::after{content:'';position:absolute;inset:0;pointer-events:none}

.crt::before{
  background-image:
    /* scanlines: 1px row, 2px gap */
    repeating-linear-gradient(180deg,
      var(--scan-h) 0px, var(--scan-h) 1px, transparent 1px, transparent 3px),
    /* aperture grille: finer, and far fainter — it should be felt, not seen */
    repeating-linear-gradient(90deg,
      var(--scan-v) 0px, var(--scan-v) 1px, transparent 1px, transparent 4px),
    /* phosphor bloom toward the centre of the tube */
    radial-gradient(ellipse 80% 60% at 50% 42%, var(--bloom), transparent 70%),
    /* vignette from the curvature of the glass */
    radial-gradient(ellipse 96% 92% at 50% 50%, transparent 42%, var(--vignette) 100%);
  animation:crtFlicker 5.5s steps(1,end) infinite;
}

/* The refresh roll: one very soft band, slower than the eye tracks. */
.crt::after{
  background:linear-gradient(180deg,
    transparent 0%,
    var(--scan-h) 42%,
    var(--scan-h) 58%,
    transparent 100%);
  height:38%;
  opacity:.55;
  animation:crtRoll 9s linear infinite;
}

/* A tube never sits perfectly still, but anything the eye can name as a flash
   is a distraction on a page full of numbers — so the steps are tiny. */
@keyframes crtFlicker{
  0%,100%{opacity:1}
  48%    {opacity:.97}
  52%    {opacity:1}
  76%    {opacity:.985}
}
@keyframes crtRoll{
  0%  {transform:translateY(-100%)}
  100%{transform:translateY(360%)}
}

@media (prefers-reduced-motion:reduce){
  .crt::before{animation:none}
  .crt::after{display:none}
}

/* ── Brand mark ───────────────────────────────────────────────────────────── */

/* Inherits, so the mark takes whatever colour the lockup around it is given.
   It used to pin itself to the accent, which is why "make the logo white" only
   turned the wordmark white and left the candles coloured. */
.logo-mark{height:26px;width:auto;flex:none}
.logo-lockup{height:auto;width:auto;color:var(--text)}

/* ── Shared atoms ─────────────────────────────────────────────────────────── */

.pill{
  display:inline-flex;align-items:center;gap:7px;
  padding:5px 12px;border-radius:100px;
  font-family:var(--f-mono);font-size:.66rem;
  letter-spacing:.13em;text-transform:uppercase;
  border:1px solid var(--line);
  background:var(--accent-glow);
  color:var(--accent-lt);
  white-space:nowrap;
}
.pill.live{border-color:var(--green);background:var(--green-dim);color:var(--green)}
/* The cockpit's live light. Gated on .stale rather than on .live alone: the
   label flips to "Reconnecting" when a poll fails while the pill keeps its
   class, and a green dot still blinking beside that word would be claiming
   something the page has just discovered is untrue. */
.pill.live:not(.stale) .dot{animation:dotBlink 1.9s var(--ease) infinite}
.pill.live.stale{border-color:var(--amber);background:transparent;color:var(--amber)}
.pill.live.stale .dot{animation:none;opacity:.5}
.pill.bull{border-color:var(--green);background:var(--green-dim);color:var(--green)}
.pill.bear{border-color:var(--red);background:var(--red-dim);color:var(--red)}
.pill.neut{border-color:var(--line-soft);background:var(--fill-2);color:var(--muted)}
.pill.ai{border-color:var(--violet);background:var(--violet-dim);color:var(--violet)}

/* The dot that makes the whole product feel alive. */
.dot{
  width:7px;height:7px;border-radius:50%;
  background:currentColor;flex:none;
  position:relative;
}
.dot::after{
  content:'';position:absolute;inset:-3px;border-radius:50%;
  border:1px solid currentColor;opacity:.55;
  animation:dotPulse 2.1s var(--ease) infinite;
}
@keyframes dotPulse{
  0%{transform:scale(.75);opacity:.7}
  70%{transform:scale(2.1);opacity:0}
  100%{transform:scale(2.1);opacity:0}
}
/* The hero badge claims the engine is live, so its dot behaves like a status
   LED and not only a radar ring: mostly lit, with a short dip. Scoped to the
   badge -- .dot is also the quiet marker in .hero-proof and on the feed chips,
   and blinking those would turn the page into a christmas tree. */
.hero-badge .dot{animation:dotBlink 1.9s var(--ease) infinite}
@keyframes dotBlink{
  0%,68%   {opacity:1}
  78%      {opacity:.18}
  88%,100% {opacity:1}
}


.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:9px;
  padding:13px 26px;border-radius:var(--r-sm);
  font-size:.78rem;font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;
  transition:transform .18s var(--ease),background .2s,border-color .2s,box-shadow .2s;
  white-space:nowrap;cursor:pointer;
}
.btn-primary{
  background:linear-gradient(135deg,var(--accent-lt),var(--accent));
  color:#0a0c10;
  box-shadow:0 10px 30px -12px var(--accent-glow);
}
/* Both light branches, not just the explicit one. The theme has three states and
   the un-stamped default -- system light, toggle never touched -- is the common
   one; it was falling through to the base #0a0c10, which is near-black on a
   gradient that in this theme runs #0d8ba6 to #0a6f85. Every other light
   override in this file already covers both; this was the only one that did not. */
@media (prefers-color-scheme: light){
  :root:not([data-theme="dark"]) .btn-primary{color:#fffdf6}
}
:root[data-theme="light"] .btn-primary,
[data-theme="light"] .btn-primary{color:#fffdf6}
.btn-primary:hover{transform:translateY(-2px)}
.btn-ghost{
  border:1px solid var(--line-strong);
  color:var(--accent-lt);
  background:var(--accent-glow);
}

/* The demo sits between a gold primary and a gold ghost, so it cannot stand out
   by being more gold. It stands out by shape: a filled play mark makes it read
   as "watch" before the label is read at all. */
.btn-demo{
  border:1px solid var(--line-strong);
  background:var(--fill-3);
  color:var(--text);
  padding-left:8px;gap:11px;
}
.btn-demo:hover{background:var(--elevated);border-color:var(--accent)}
.btn-demo-play{
  width:27px;height:27px;border-radius:50%;flex:none;
  display:inline-flex;align-items:center;justify-content:center;
  background:linear-gradient(150deg,var(--accent-lt),var(--accent));
  color:#1b1509;
}
/* No margin nudge: the optical offset a play triangle needs belongs in the
   viewBox, where it scales with the glyph. A fixed 2px was a different
   correction on the 27px disc than on the 21px one, and it stacked with the
   path already sitting right of centre in its own box — 1.81px off on a 21px
   circle, which reads. */

/* Shared by the marketing bar and the cockpit bar: both reveal an action once
   the one the page was showing has scrolled past. */
@keyframes jumpIn{
  from{opacity:0;transform:translateY(-5px)}
  to{opacity:1;transform:none}
}
.btn-ghost:hover{transform:translateY(-2px);border-color:var(--accent)}
/* --accent-lt is a highlight on dark but the paler of the two on light, where it
   sits on a tinted ground — use the darker gold so the label keeps its weight. */
@media (prefers-color-scheme: light){
  :root:not([data-theme="dark"]) .btn-ghost{color:var(--accent-dk)}
}
:root[data-theme="light"] .btn-ghost{color:var(--accent-dk)}

/* On mobile the demo button is restyled as a second ghost, so it has to inherit
   the ghost's light-theme colour too -- without this the two secondaries sit on
   --accent-lt and --accent-dk and read as two different controls, which is the
   whole thing the mobile restyle exists to stop. Scoped to that width: on
   desktop the demo button keeps --text and its own shape. */
@media (max-width:860px){
  @media (prefers-color-scheme: light){
    :root:not([data-theme="dark"]) .hero-cta .btn-demo{color:var(--accent-dk)}
  }
  :root[data-theme="light"] .hero-cta .btn-demo{color:var(--accent-dk)}
}
.btn-dark{border:1px solid var(--line-soft);color:var(--text-2);background:var(--fill-1)}
.btn-dark:hover{border-color:var(--line-strong);color:var(--accent-lt)}
.btn-sm{padding:9px 16px;font-size:.7rem}

/* ── Attention pulse ──────────────────────────────────────────────────────────
   A ring that expands out of the button and fades. Only on the beta CTA — the
   whole point is that one action stands out from every other button on the page,
   so applying it more widely would cancel it out.

   The ring is drawn on the border only (no fill), which is why it can overlap
   the button's own edge for the first frames without muddying the label. */
.btn.pulse{position:relative}

/* Two layers, one rhythm:
     ::after  — a ring that barely leaves the button's edge
     ::before — a soft halo bloom underneath, sized by box-shadow spread
   Keeping the travel short (6%) means the eye reads it as the button breathing
   rather than as something flying off it. */
.btn.pulse::after{
  content:'';
  position:absolute;inset:-1px;
  border-radius:inherit;
  border:1.5px solid var(--accent);
  pointer-events:none;
  animation:ctaRing 2.4s var(--ease) infinite;
}
.btn.pulse::before{
  content:'';
  position:absolute;inset:0;
  border-radius:inherit;
  pointer-events:none;
  box-shadow:0 0 0 0 var(--accent);
  animation:ctaHalo 2.4s var(--ease) infinite;
}
@keyframes ctaRing{
  0%   {transform:scale(1);    opacity:.7}
  65%  {transform:scale(1.06); opacity:0}
  100% {transform:scale(1.06); opacity:0}
}
@keyframes ctaHalo{
  0%   {box-shadow:0 0 0 0 var(--accent-glow), 0 0 0 0 var(--accent-glow); opacity:.9}
  65%  {box-shadow:0 0 14px 7px var(--accent-glow), 0 0 4px 1px var(--accent-glow); opacity:0}
  100% {box-shadow:0 0 14px 7px var(--accent-glow), 0 0 4px 1px var(--accent-glow); opacity:0}
}

/* Once someone is on the button the pulse has done its job — and a ring
   expanding under the cursor reads as a glitch rather than an invitation. */
.btn.pulse:hover::after,  .btn.pulse:focus-visible::after,
.btn.pulse:hover::before, .btn.pulse:focus-visible::before{animation:none;opacity:0}

/* Nothing should keep pulsing behind an open dialog. */
body.modal-open .btn.pulse::after,
body.modal-open .btn.pulse::before{animation:none;opacity:0}

@media (prefers-reduced-motion:reduce){
  .btn.pulse::after,
  .btn.pulse::before{display:none}
}

.card{
  background:var(--panel-grad);
  border:1px solid var(--line-soft);
  border-radius:var(--r);
  position:relative;
}

/* ── Motion ───────────────────────────────────────────────────────────────── */

.reveal{
  opacity:0;transform:translateY(22px);
  transition:opacity .7s var(--ease),transform .7s var(--ease);
}
.reveal.in{opacity:1;transform:none}

@keyframes sweep{
  0%{transform:translateX(-100%)}
  100%{transform:translateX(100%)}
}
@keyframes blink{50%{opacity:.28}}
@keyframes spin{to{transform:rotate(360deg)}}

/* Everything decorative is opt-out for people who ask for less motion. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
    scroll-behavior:auto !important;
  }
  .reveal{opacity:1;transform:none}
}

/* ── Theme switcher ───────────────────────────────────────────────────────── */

.theme-switch{
  display:inline-flex;align-items:center;gap:2px;
  padding:3px;border-radius:100px;
  border:1px solid var(--line-soft);
  background:var(--fill-1);
  flex:none;
}
.theme-switch button{
  width:28px;height:26px;border-radius:100px;
  display:flex;align-items:center;justify-content:center;
  color:var(--dim);
  transition:color .2s var(--ease),background .2s var(--ease);
}
.theme-switch button:hover{color:var(--muted)}
.theme-switch button[aria-pressed="true"]{
  background:var(--accent-glow);
  color:var(--accent);
  box-shadow:inset 0 0 0 1px var(--line);
}
.theme-switch svg{width:14px;height:14px}

/* ── Social row ───────────────────────────────────────────────────────────── */

.social{display:inline-flex;align-items:center;gap:4px}
.social a{
  width:30px;height:30px;border-radius:8px;
  display:flex;align-items:center;justify-content:center;
  color:var(--muted);
  transition:color .2s var(--ease),background .2s var(--ease),transform .2s var(--ease);
}
.social a:hover{color:var(--accent-lt);background:var(--fill-2);transform:translateY(-1px)}
.social svg{width:16px;height:16px}
.social.lg a{width:36px;height:36px}
.social.lg svg{width:18px;height:18px}

/* ── Screen-reader only ───────────────────────────────────────────────────── */
.sr{
  position:absolute;width:1px;height:1px;
  padding:0;margin:-1px;overflow:hidden;
  clip:rect(0,0,0,0);white-space:nowrap;border:0;
}
.skip{
  position:absolute;top:-60px;left:12px;z-index:400;
  background:var(--accent);color:#0a0c10;
  padding:10px 18px;border-radius:var(--r-sm);
  font-size:.8rem;font-weight:600;
  transition:top .2s var(--ease);
}
.skip:focus{top:12px}

/* ── Freshness stamp ───────────────────────────────────────────────────────
   Dated from the feed, so the dot is a real claim: it only lights when the
   reading is under 26 hours old. See ss_freshness(). */
.freshness{
  display:inline-flex;align-items:center;gap:7px;
  font-family:var(--f-mono);font-size:.66rem;letter-spacing:.06em;
  color:var(--dim);white-space:nowrap;
}
.freshness i{
  width:6px;height:6px;border-radius:50%;flex:none;
  background:var(--dim);
}
.freshness.is-fresh{color:var(--muted)}
.freshness.is-fresh i{background:var(--green);position:relative}

/* The halo is a scaled ring rather than an animated box-shadow so it inherits
   --green with the theme. A shadow needs a literal rgba to fade out, which
   would have kept the dark-theme green on the light page. */
.freshness.is-fresh i::after{
  content:'';position:absolute;inset:0;border-radius:50%;
  background:var(--green);
  animation:freshPulse 2.6s var(--ease) infinite;
}
@keyframes freshPulse{
  0%  {transform:scale(1);   opacity:.55}
  70% {transform:scale(3.1); opacity:0}
  100%{transform:scale(3.1); opacity:0}
}
@media (prefers-reduced-motion:reduce){ .freshness.is-fresh i{animation:none} }

/* ── Share row ─────────────────────────────────────────────────────────────
   Shared by the marketing pages and the cockpit; both load this file. */
.share-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.share-lbl{
  font-family:var(--f-mono);font-size:.62rem;letter-spacing:.16em;
  text-transform:uppercase;color:var(--dim);
}
.share-nets{display:flex;align-items:center;gap:7px}

.share-btn{
  position:relative;
  display:inline-flex;align-items:center;justify-content:center;gap:7px;
  width:34px;height:34px;border-radius:10px;
  border:1px solid var(--line);background:var(--panel);
  color:var(--muted);flex:none;
  transition:color .18s var(--ease),border-color .18s var(--ease),
             background .18s var(--ease),transform .18s var(--ease);
}
.share-btn svg{width:15px;height:15px}
.share-btn:hover{
  color:var(--accent);border-color:var(--accent-dk);
  background:var(--accent-glow);transform:translateY(-2px);
}
.share-btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px}

/* Both are pills rather than squares, because both carry a word. */
.share-native{width:auto;padding:0 14px;color:var(--text-2)}
.share-copy{width:auto;padding:0 14px}
.share-native span,.share-word{
  font-family:var(--f-mono);font-size:.68rem;letter-spacing:.08em;
}
/* Once the sheet is there the row's own "Share" label says the same thing
   twice, so it stands down and lets the pill speak. */
.share-row.has-sheet .share-lbl{display:none}

/* "Copied" replaces the icon in place — no layout shift, no toast to place. */
.share-done{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  border-radius:inherit;background:var(--accent);color:#0a0c10;
  font-family:var(--f-mono);font-size:.55rem;letter-spacing:.04em;font-weight:700;
  opacity:0;pointer-events:none;transition:opacity .16s var(--ease);
}
.share-copy.is-done .share-done{opacity:1}

/* Two controls, not six. The sheet already contains X, Telegram, WhatsApp and
   LinkedIn — along with Signal, SMS, Mail and whatever else is installed — so
   the brand icons were four extra targets for destinations already one tap
   away. Copy stays: the sheet does not reliably offer it.

   They are hidden rather than removed because the sheet is not everywhere.
   navigator.share is absent on desktop Firefox, and deleting the icons outright
   would have left those visitors with nothing but Copy. Where there is no
   sheet, the four reappear and do its job. */
.share-row.has-sheet .share-x,
.share-row.has-sheet .share-telegram,
.share-row.has-sheet .share-whatsapp,
.share-row.has-sheet .share-linkedin{display:none}
