/* ==========================================================================
   tokens.css — "Signal Grid" (v2)

   Values below were read off the live reference build at
   https://lozada-crafts-code.lovable.app/ — computed styles, not estimates.
   The concept's own names are the source of truth; the compatibility block at
   the bottom maps v1's names onto them so CSS copied from ../site/ still
   renders.

   Dark only. Exactly ONE palette block — no light counterpart, no
   prefers-color-scheme. A duplicated palette is how one theme silently stops
   picking up changes.

   The type scale is STEPPED, not fluid. The reference steps at Tailwind's
   breakpoints (sm 640, md 768, lg 1024) rather than interpolating, so a
   clamp() here would be visibly wrong between stops.
   ========================================================================== */

:root {
  color-scheme: dark;

  /* --- Palette (verbatim) ---
     Accent hue is 309, a violet sampled from the reference art. It replaced a
     cyan at 210; lightness and chroma did not move, so every contrast
     relationship on the page survived the swap untouched (the accent sits at
     ~10.9:1 on --background, where the cyan was ~12.2:1).

     The .14 chroma is deliberately past what sRGB holds at this lightness —
     browsers gamut-map it down to about #dfaaff on an sRGB screen and render
     it fuller on P3. Specifying the intent rather than the sRGB fallback is
     what keeps the palette honest on better displays.

     --success stays green at 155 and --destructive red at 25. Both are far
     enough from 309 to still read as status rather than decoration, which is
     the reason the accent did not become a green. */
  --radius: .25rem;

  --background: oklch(12% .025 255);
  --foreground: oklch(97% .01 225);

  --card: oklch(16% .03 248);
  --card-foreground: oklch(97% .01 225);

  --primary: oklch(82% .14 309);
  --primary-foreground: oklch(12% .025 255);

  --secondary: oklch(20% .035 248);
  --secondary-foreground: oklch(90% .02 225);

  --muted: oklch(20% .025 248);
  --muted-foreground: oklch(68% .035 235);

  --accent: oklch(82% .14 309);
  --accent-foreground: oklch(12% .025 255);

  --destructive: oklch(58% .22 25);
  --destructive-foreground: oklch(99% 0 0);

  --border: oklch(42% .075 319 / .45);
  --input: oklch(30% .055 329);
  --ring: oklch(82% .14 309);

  --signal: oklch(82% .14 309);
  --signal-soft: oklch(82% .14 309 / .12);

  --panel: oklch(14.5% .03 252 / .88);
  --panel-strong: oklch(18% .035 248);

  --success: oklch(77% .16 155);

  --focus-ring: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);

  /* --- Light and depth ---
     The page has exactly ONE glow colour and it is --signal. These name the
     strengths it was already being used at, so a fourth cannot be invented by
     accident: the 40px/8% literal was written out in three separate files
     before this block existed and there was nothing stopping a fifth.

     --rim is the specular top edge - one inset hairline across the top, which
     is what makes a square unlit box read as a physical edge rather than a
     drawn rectangle. --sheen is the same light travelling ACROSS the face.
     Both come off --foreground, never raw white: this palette's near-white
     carries a trace of the blue the page is built on, and a pure #fff
     highlight is the tell of a card borrowed from somewhere else.

     Surfaces layer base -> elevated -> floating. --shadow-e1 is a card at
     rest, --shadow-e2 the same card lifted. Both are layered and tinted; a
     flat single-blur shadow is not used anywhere on this page. --shadow-e2 is
     the archive card's existing hover triple moved here verbatim - the hover
     did not change, it just stopped being a literal. */
  --glow:       0 0 40px color-mix(in oklab, var(--signal) 8%, transparent);
  --glow-node:  0 0 12px color-mix(in oklab, var(--signal) 55%, transparent);
  --glow-flare: 0 0 64px color-mix(in oklab, var(--signal) 26%, transparent);

  /* These strengths were picked against the render, not reasoned to. The first
     pass used 9% and 6% — arithmetically sensible, and invisible: the panel
     underneath is oklch(14.5%), so six per cent of a near-white over it is
     about a point and a half of lightness and the card still read flat. A/B at
     9/6, 14/10, 20/14 and 26/18 put the usable answer at 20/14. The step above
     it stops reading as light catching an edge and starts reading as a grey
     wash laid over the corner. */
  --rim:  inset 0 1px 0 color-mix(in oklab, var(--foreground) 20%, transparent);
  --edge: color-mix(in oklab, var(--signal) 42%, transparent);
  --sheen: linear-gradient(155deg,
             color-mix(in oklab, var(--foreground) 14%, transparent) 0%,
             color-mix(in oklab, var(--foreground) 4%, transparent) 42%,
             transparent 72%);

  --shadow-e1:
    0 1px 0 color-mix(in srgb, black 30%, transparent),
    0 10px 24px -14px color-mix(in srgb, black 70%, transparent),
    0 2px 10px -6px color-mix(in oklab, var(--signal) 14%, transparent);
  --shadow-e2:
    0 0 0 1px color-mix(in oklab, var(--signal) 40%, transparent),
    0 18px 44px -18px color-mix(in oklab, var(--signal) 38%, transparent),
    0 14px 30px -18px color-mix(in srgb, black 75%, transparent);

  /* --- Typography ---
     Three faces, three jobs: Space Grotesk displays, DM Sans reads,
     JetBrains Mono annotates. */
  --font-sans: "DM Sans", ui-sans-serif, system-ui, sans-serif;
  --font-display: "Space Grotesk", ui-sans-serif, system-ui, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "Cascadia Code", Consolas, monospace;

  --text-h1: 3rem;      /* 48 -> 72 @640 -> 96 @1024 */
  --text-h2: 2.25rem;   /* 36 -> 48 @640, via --text-h2-lg */
  --text-h2-lg: 3rem;   /* the @640 step. Five rules stepped h2 independently
                           with a bare 3rem before this existed, so changing
                           h2 sizing meant finding all five. */
  --text-h3: 1.25rem;   /* 20, flat at every width — confirmed against the live
                           reference with `_ref.mjs scale`, which reports 20px
                           at 390 through 1920. Do not give it a step. */
  --text-lead: 1.125rem;/* 18. The hero's lede alone steps to 20 at 640, in
                           home.css; every other lede stays 18 at all widths.
                           This said "18 -> 20 @640" and described a step three
                           of the four ledes never took. */
  --text-body: 1rem;
  --text-sm: .875rem;
  --text-label: .75rem; /* mono section labels */
  --text-micro: .625rem;/* badge + nav CTA */

  --lh-display: .95;
  --lh-heading: 1.1;
  --lh-body: 1.5;
  --lh-relaxed: 1.625;

  --track-label: .05em;   /* tracking-wider  */
  --track-widest: .1em;   /* tracking-widest */

  /* --- Spacing --- */
  --space-3xs: .25rem; --space-2xs: .5rem;  --space-xs: .75rem; --space-sm: 1rem;
  --space-md: 1.5rem;  --space-lg: 2rem;    --space-xl: 3rem;   --space-2xl: 4rem;
  --space-3xl: 5rem;   --space-4xl: 7rem;

  /* --- Radius. The concept is square; --radius is the ceiling. --- */
  --radius-xs: 0px;
  --radius-sm: 0px;
  --radius-md: var(--radius);
  --radius-full: 999px;

  /* --- Motion --- */
  --ease-in-out: cubic-bezier(.4, 0, .2, 1);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --dur-fast: 150ms; --dur-base: 220ms; --dur-slow: 380ms;

  /* --- Layout --- */
  --container: 72rem;    /* max-w-6xl */
  --gutter: 1.5rem;      /* px-6 */
  --nav-h: 4rem;

  /* ======================================================================
     v1 component compatibility. Lets CSS copied from ../site/ render here.
     Author NEW v2 rules against the concept names above, not these.
     ====================================================================== */
  --paper-base: var(--background);
  --paper-raised: var(--card);
  --paper-sunken: oklch(9% .02 255);
  --ink: var(--foreground);
  --ink-secondary: var(--secondary-foreground);
  --ink-muted: var(--muted-foreground);
  --draft: var(--primary);
  /* 85% / .09, not the 89% / .1 the other hues use. These two are the primary
     button's hover state, and violet is the tightest corner of sRGB at high
     lightness: at 89% this hue tops out around .066 chroma, so the hover went
     near-white and stopped reading as a colour at all. Dropping 4 points of
     lightness buys back the chroma and keeps hover visibly brighter than rest,
     which is the only thing the state has to communicate. */
  --draft-deep: oklch(85% .09 304);
  --draft-tint: var(--secondary);
  --draft-soft: oklch(55% .09 314);
  --signal-deep: oklch(85% .09 304);
  --signal-tint: var(--secondary);
  --rule: var(--border);
  --rule-strong: oklch(50% .09 319 / .6);
}

/* ==========================================================================
   Base
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  tab-size: 4;
  scroll-behavior: smooth;
  scroll-padding-top: var(--nav-h);
  /* Reserve the scrollbar gutter permanently. The dialog locks scrolling by
     hiding overflow, and without a stable gutter the whole page jumps sideways
     by the scrollbar width the moment it opens. */
  scrollbar-gutter: stable;
}

/* Native <dialog> makes the rest of the page inert but does NOT stop it
   scrolling, so the background slides around behind an open modal. */
html.modal-lock { overflow: hidden; }

body {
  margin: 0;
  background-color: var(--background);
  color: var(--foreground);
  font-family: var(--font-sans);
  font-size: var(--text-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;

  /* The ground is drawn on a canvas now, not here: see .circuitbg in
     site.css and initCircuit() in motion.js. This background-color is
     still load-bearing - it propagates to the viewport canvas and paints
     UNDER the circuit layer, which sits at z-index -1. */
}

h1, h2, h3, h4 {
  margin: 0;
  font-family: var(--font-display);
  line-height: var(--lh-heading);
  color: var(--foreground);
  /* The reference balances its headings; without this a three-line h2 breaks
     one word later and the ragged edge reads differently. */
  text-wrap: balance;
}

p { margin: 0; }
ul, ol, dl, dd { margin: 0; padding: 0; }
img, picture, svg, video { display: block; max-width: 100%; }
a { color: inherit; }
button { font: inherit; }

::selection {
  background: var(--signal);
  color: var(--primary-foreground);
}

:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}
