/* ============================================================================
   HexP — Design Tokens
   SINGLE SOURCE OF TRUTH. Every page imports this file. Nothing else defines
   a colour, a spacing value, a radius, a shadow or a duration.

   Extracted from index.html (the approved v3 landing page) on 2026-07-30.
   Colour + type are faithful 1:1. Spacing / radius / shadow / motion were
   collapsed onto scales — see TOKEN-DIFF.md for every value that moved.

   RULE: if you need a value that isn't here, add it here first.
         Never inline a raw value in a page.
   ========================================================================= */

:root {

  /* ==========================================================================
     1. SURFACES
     ====================================================================== */
  --bg:        #FFFFFF;
  --bg-alt:    #F8FAFC;   /* was --bg2 */
  --surface:   #FFFFFF;   /* was --card */
  --surface-inverse: #0D0D12;  /* dark browser-chrome mockup bg */


  /* ==========================================================================
     2. NEUTRALS — opacity of black, never solid grey.
     Values taken from index.html (the live page), NOT the old
     design-system.html, which had drifted 6 of 9 steps lighter.
     ====================================================================== */
  --n-900: rgba(0,0,0,.94);
  --n-800: rgba(0,0,0,.85);
  --n-700: rgba(0,0,0,.75);
  --n-600: rgba(0,0,0,.60);
  --n-500: rgba(0,0,0,.48);
  --n-400: rgba(0,0,0,.35);
  --n-300: rgba(0,0,0,.22);
  --n-200: rgba(0,0,0,.12);
  --n-100: rgba(0,0,0,.06);

  /* neutral roles */
  --ink:   var(--n-900);   /* primary text   */
  --muted: var(--n-700);   /* secondary text */
  --faint: var(--n-400);   /* tertiary text  */
  --line:  var(--n-100);   /* hairline borders */


  /* ==========================================================================
     3. BRAND — Purple (primary) & Orange (secondary)
     ====================================================================== */
  --p-100:#EDE6FA; --p-200:#D6C4F2; --p-300:#B99AE9; --p-400:#9668DE;
  --p-500:#6B26D9; --p-600:#591CBA; --p-700:#481797; --p-800:#371173;
  --p-900:#2A0D59;

  --o-100:#FDEDE2; --o-200:#FAD6BC; --o-300:#F7B88C; --o-400:#F39553;
  --o-500:#F97415; --o-600:#E65E00; --o-700:#BD4E00; --o-800:#943D00;
  --o-900:#763000;
  /* --o-500 set to #F97415 (was #F97316) — matches the canonical value in
     assets/gradients/test-1.svg. Perceptual distance 2.5: invisible on screen,
     but now there is one number instead of two. */

  /* brand roles */
  --primary:      var(--p-500);
  --primary-dark: var(--p-700);
  --primary-tint: var(--p-100);
  --accent:       var(--o-500);
  --accent-tint:  var(--o-100);

  /* RESOLVED 2026-07-30 — the wordmark's #771DF5 / #FF7B00 were drift, not
     brand. It uses --primary and --accent like everything else. There is no
     separate logo colour. */

  /* RESOLVED 2026-07-30 — no solid greys, anywhere, no exceptions.
     #E2E8F0 -> --n-200, #F1F5F9 -> --n-100. Both were Tailwind slate and
     carried a blue cast (+14 and +8 blue over red) into a palette that had
     deliberately dropped blue. Neutrals are opacity of black. Full stop. */


  /* ==========================================================================
     4. SEMANTIC STATE — function only, never decoration.
     Renamed from hue-names (--grn/--pink/--lav/--yel) to role-names, because
     that is how index.html actually uses them: inside product-UI mockups to
     signal status. --pink was especially misleading; it is a burnt orange-red.
     ====================================================================== */
  --success:      #16A249;  --success-tint: #E5FAED;
  --danger:       #C2410C;  --danger-tint:  #FCEBE3;
  --info:         #8B5CF6;  --info-tint:    #EDE9FE;
  --warning:      #D97706;  --warning-tint: #FDF1E2;

  /* Back-compat aliases — index.html still uses these.
     Remove once the page is migrated. */
  --grn: var(--success);  --grn-l: var(--success-tint);
  --pink:var(--danger);   --pink-l:var(--danger-tint);
  --lav: var(--info);     --lav-l: var(--info-tint);
  --yel: var(--warning);  --yel-l: var(--warning-tint);
  --org: var(--accent);   --org-l: var(--accent-tint);
  --primary-d: var(--primary-dark);
  --primary-l: var(--primary-tint);
  --card: var(--surface);
  --bg2:  var(--bg-alt);


  /* ==========================================================================
     5. ILLUSTRATIONS — no separate palette.

     REMOVED 2026-07-30. Illustrations use the brand scales. An off-scale
     colour is an override and must be explicitly justified and recorded here.
     There is no standing licence for illustration-only colours.

     The hub graphic currently violates this. It is generated in JavaScript
     with hexes baked into template strings (index.html L1094-1122), so CSS
     cannot see them and no audit catches them. Three snap cleanly:

       #DCC9F3 -> --p-200      #D9C7F5 -> --p-200      #49189B -> --p-700

     Six do not — they sit in the gap between --p-200 (#D6C4F2) and
     --p-300 (#B99AE9), which is a wide step:

       #F4F0FD  #E7DAF9  #C9B6EF  #CBBBEF  #C9AEF3  #A97FEE

     These need a decision during the illustration rework, not a token here.
     Either the graphic is redrawn on the scale, or specific overrides get
     approved and listed in this block. Until then they remain raw hexes in
     the JS, flagged, and deliberately NOT legitimised as tokens.
     ====================================================================== */
  --grad-core: radial-gradient(150% 150% at 100% 0%,
                               var(--p-300) 0%, var(--p-500) 55%, var(--p-900) 100%);


  /* ==========================================================================
     6. TYPOGRAPHY
     Fluid clamp(min, Xvw, max). Holds its floor from ~680px down, reaches its
     ceiling by ~1140px. Carried over unchanged — this scale was already
     correct and is the strongest part of the existing system.
     ====================================================================== */
  --font-display: "Schibsted Grotesk", system-ui, -apple-system, sans-serif;
  --font-body:    "DM Sans", system-ui, -apple-system, sans-serif;

  --fs-h1:      clamp(44px,   5vw,    64px);
  --fs-h2:      clamp(28px,   4.2vw,  48px);
  --fs-h3:      clamp(20px,   2.8vw,  32px);
  --fs-h4:      clamp(18px,   2.1vw,  24px);   /* was --fs-h3-sm */
  --fs-body-lg: clamp(15.5px, 1.25vw, 17.5px);
  --fs-body:    clamp(14.5px, 1.1vw,  16px);
  --fs-sm:      14px;
  --fs-xs:      12px;
  --fs-h3-sm:   var(--fs-h4);   /* back-compat alias — index.html still uses this */

  --fw-regular: 400;  --fw-medium: 500;  --fw-semibold: 600;
  --fw-bold:    700;  --fw-black:  800;

  --lh-tight: 1.1;  --lh-snug: 1.25;  --lh-normal: 1.5;  --lh-relaxed: 1.65;
  --tracking-tight: -.02em;  --tracking-normal: 0;  --tracking-wide: .04em;


  /* ==========================================================================
     7. SPACING — 4px base. Replaces 48 ad-hoc values.
     90% of existing declarations were already within 2px of this scale.
     ====================================================================== */
  --sp-0:   0;
  --sp-hair: 2px;   /* hairlines / optical nudges only */
  --sp-1:   4px;
  --sp-2:   8px;
  --sp-3:   12px;
  --sp-4:   16px;
  --sp-5:   20px;
  --sp-6:   24px;
  --sp-8:   32px;
  --sp-10:  40px;
  --sp-12:  48px;
  --sp-14:  56px;
  --sp-16:  64px;
  --sp-20:  80px;
  --sp-24:  96px;
  --sp-32:  128px;

  /* layout */
  --container-max: 1600px;
  --content-max:   1180px;
  --measure:       420px;   /* max width for readable paragraph text */


  /* ==========================================================================
     SCOPE OF THE SPACING SCALE

     Governs LAYOUT ONLY — padding, margin, gap between elements.

     ILLUSTRATIONS ARE EXEMPT. SVG internals — path coordinates, gradient
     stops, hub geometry — are their own coordinate system. Never snap them
     to this scale.

     NO COMPOSITION LAYER. There is deliberately no --card-pad / --section-y /
     --gutter. Named layout roles were considered and dropped: in Astro they
     belong to components (a <Card> with p-6 baked in enforces itself, a CSS
     variable name does not). One-off structural values — .hero-text's 154px
     top offset, .device's 110px margin — stay local to their component and
     do not have to justify themselves against this scale.

     TAILWIND NOTE: every value above exists in Tailwind's stock scale
     (--sp-1 = p-1, --sp-6 = p-6, --sp-32 = p-32). Do NOT extend theme.spacing.
     OVERRIDE it with exactly these values, so the in-between steps Tailwind
     ships by default — p-2.5 (10px), p-3.5 (14px), p-7 (28px), p-9 (36px),
     p-11 (44px) — cease to exist. Those are the values that produced the
     original 48. The discipline here is subtraction, not addition.
     ====================================================================== */


  /* ==========================================================================
     8. RADIUS — replaces 17 ad-hoc values.
     Clusters were already present; 9/11/13px were hand-edit drift, not intent.
     ====================================================================== */
  --rad-xs:   4px;
  --rad-md:   12px;
  --rad-lg:   20px;
  --rad-xl:   32px;
  --rad-full: 999px;


  /* ==========================================================================
     9. ELEVATION — replaces 21 ad-hoc shadows.
     sh-1..sh-5 = neutral depth ramp. glow = brand-coloured, CTA only.
     ====================================================================== */
  --sh-1: 0 1px 2px rgba(15,19,25,.04), 0 10px 26px -16px rgba(15,19,25,.16);
  --sh-2: 0 18px 34px -20px rgba(15,19,25,.20);
  --sh-3: 0 20px 46px -28px rgba(15,19,25,.26);
  --sh-4: 0 30px 70px -42px rgba(15,19,25,.35);
  --sh-5: 0 44px 100px -44px rgba(15,19,25,.55);

  --sh-glow:    0 10px 24px -10px rgba(107,38,217,.50);
  --sh-glow-lg: 0 0 0 8px rgba(107,38,217,.10), 0 16px 44px -10px rgba(107,38,217,.50);
  --sh-glow-xl: 0 0 0 17px rgba(107,38,217,.045), 0 24px 56px -8px rgba(107,38,217,.70);

  --sh-inset: inset 0 0 0 1px color-mix(in srgb, currentColor 32%, transparent);
  --sh-none:  none;


  /* ==========================================================================
     10. MOTION — replaces 10 durations and 6 easings.
     ====================================================================== */
  --dur-fast:   .18s;   /* hovers, taps, colour shifts */
  --dur-base:   .30s;   /* the default */
  --dur-slow:   .60s;   /* scroll reveals, large transforms */
  --dur-ambient: 1.3s;  /* ambient / decorative only */

  --ease-out:      cubic-bezier(.2,.8,.2,1);      /* default UI easing */
  --ease-entrance: cubic-bezier(.16,.84,.44,1);   /* scroll reveals */
  --ease-spring:   cubic-bezier(.34,1.56,.64,1);  /* playful overshoot */
  --ease-standard: cubic-bezier(.4,0,.2,1);
}


/* ============================================================================
   11. BREAKPOINTS
   CSS custom properties CANNOT be used inside @media queries — these are
   documented constants, not tokens. Use exactly these three. The old page had
   nine (520/560/680/820/900/980/1024/1180/1340), which is the root cause of
   the responsiveness problems: each was added reactively to patch one break.

     --bp-desktop  1180px   layout tier change  (was 1340, 1180, 1024)
     --bp-tablet    820px   the real breakpoint (was 1024, 980, 900, 820)
     --bp-phone     560px   single-column       (was 680, 560, 520)

   Write:  @media (max-width: 820px) { ... }

   APPROVED 2026-07-30. Three tiers is the standing decision.

   CHANGE PROCESS — a fourth breakpoint is not a bug fix, it is a system
   change. Before adding one, the burden of proof is:
     1. Name the component that breaks and the exact width it breaks at.
     2. Show it cannot be solved with a container query, clamp(), flex-wrap,
        or an intrinsic grid (`repeat(auto-fit, minmax(...))`). Nearly always
        it can — which is how the previous nine accumulated.
     3. If a new tier is still needed, it applies site-wide, not to one page.
   Adding a breakpoint to patch one page is what produced the original nine.
   ========================================================================= */


/* ============================================================================
   12. BASE RESET — minimal, token-driven
   ========================================================================= */
*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  line-height: var(--lh-tight);
  letter-spacing: var(--tracking-tight);
  margin: 0;
}
h1 { font-size: var(--fs-h1); font-weight: var(--fw-black);    }
h2 { font-size: var(--fs-h2); font-weight: var(--fw-bold);     }
h3 { font-size: var(--fs-h3); font-weight: var(--fw-semibold); }
h4 { font-size: var(--fs-h4); font-weight: var(--fw-semibold); }

/* Accessibility — every interactive element gets a visible focus ring.
   index.html currently has none. */
:where(a, button, [role="button"], input, select, textarea):focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--rad-xs);
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}
