/* ============================================================
   WHATLABS DESIGN SYSTEM - tokens/core.css
   The shared foundation. Product identity lives in tokens/<product>.css,
   layered ON TOP of this file (load core.css first, then the product file).
   Everything here is product-agnostic: type, spacing, radii, shadow, motion,
   breakpoints, and the self-hosted Lato family. No colour with brand meaning
   lives in this file except neutral ink/paper/grey, which every product may
   override.

   Provenance: distilled from ~/Desktop/CEC/cec3-design/assets/cec3.css,
   the CEC3 WEx Intelligence demo - the quality reference for this system.

   THE PRODUCT TOKEN CONTRACT
   Every tokens/<product>.css file must define this exact set of custom
   properties on :root, layered after this file. Nothing else - a product
   file is a palette, not a second component library.

     --brand           dominant colour (buttons, links, eyebrow dot, focus ring)
     --brand-deep      darkest brand tone (headings, hero gradients, footer)
     --brand-light     lightest brand tone still saturated (gradients, hover)
     --paper           page background (white unless the product's idiom is dark)
     --tint            a pale wash of --brand used for subtle fills (KPI stat bg)
     --accent-1..4     2-4 secondary accents (status, chips, subject spectrum)
     --line            hairline colour: low-alpha --brand-deep, e.g. rgba(x,x,x,.10)
     --line-strong     same, ~0.16 alpha
     --surface-tint    same, ~0.035 alpha, for hover washes
     --brand-08/14/20  --brand at 8/14/20% alpha, for focus rings & badges
     --shadow-tint-rgb the r,g,b (no alpha) of --brand-deep, for shadow recipes
     --need-low / --need-low-ink
     --need-medium / --need-medium-ink
     --need-high / --need-high-ink
     --need-very-high / --need-very-high-ink
                       the 4-step need/status ramp, mapped to the product's own
                       palette rather than reusing CEC teal->coral literally
     --footer-grad     a `linear-gradient(...)` for the footer accent line/band
     --on-brand        OPTIONAL. Text colour for the rare flat-brand-fill chip.
                       Omit to default to white (components.css falls back to
                       var(--on-brand, var(--white))). Only set this when
                       --brand itself is too pale/bright for white text to
                       clear 4.5:1 AND dark ink clears it instead (checked
                       case by case on colour.html) - e.g. Earthrise:Live's
                       gold and WhatNursery's orange both set --on-brand: ink.

   A product that keeps its own idiom (Quorum's white clock-face, Careermash's
   dark ecosystem, Governance Register's editorial claim hierarchy) still
   defines this contract - it just sets --paper to its own dark/white choice
   and --brand to its own system colour. See the Adoption Map in the
   guidelines site for which products adopt fully vs adopt-grammar-keep-idiom.
   ============================================================ */

/* ---------------- Self-hosted Lato ----------------
   Zero external dependencies: a bid/demo network may be locked down, and a
   Google Fonts request is one more thing that can fail on the day. */
@font-face {
  font-family: "Lato";
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url("fonts/lato-300.woff2") format("woff2");
}
@font-face {
  font-family: "Lato";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/lato-400.woff2") format("woff2");
}
@font-face {
  font-family: "Lato";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("fonts/lato-700.woff2") format("woff2");
}
@font-face {
  font-family: "Lato";
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url("fonts/lato-900.woff2") format("woff2");
}

:root {
  /* ---------------- Type ----------------
     One family, four weights, meaning carried by WEIGHT not size.
     300 display headlines · 400 body · 700 section heads / labels ·
     900 numerals, eyebrows, tags. */
  --font: "Lato", "Calibri", Arial, sans-serif;

  --w-display: 300;
  --w-body: 400;
  --w-head: 700;
  --w-numeral: 900;

  /* Fluid type scale. Sizes are a starting ramp - products may nudge but
     should not add new steps. */
  --text-eyebrow: 0.72rem;      /* 11.5px @16 root - uppercase, tracked, 900 */
  --text-sm: 0.8rem;            /* captions, meta */
  --text-body: 0.9375rem;       /* running body copy */
  --text-lede: 1.08rem;         /* intro paragraph under a page head */
  --text-h3: 1.08rem;           /* card / subsection heads, 700 */
  --text-h2: 1.5rem;            /* section heads, 700 */
  --text-h1: clamp(1.9rem, 3.4vw, 2.6rem); /* page head, 300 */
  --text-display: clamp(2rem, 3.4vw, 2.75rem); /* hero / auth-brand, 300 */

  /* ---- Dashboard grid tokens (ported from cec3.css, 4th August 2026) ----
     --cell must be computed from VIEWPORT units, not a percentage: a % read
     inside grid-auto-rows resolves against the grid's own indefinite height
     rather than its width, so vw is the only unit that means the same thing
     in both axes and is therefore the only way to derive a true row-height
     token from a width formula. Nine columns, so a 1x1 tile is square. */
  --wrap-max: 1220px;
  --wrap-pad: 1.75rem;
  --grid-gap: 1.1rem;
  --cell: calc((min(100vw, var(--wrap-max)) - 2 * var(--wrap-pad) - 8 * var(--grid-gap)) / 9);
  /* Numeral size follows the tile's declared SPAN, never hand-set per tile -
     this is the rule that stops dashboards drifting into random sizes.
     --num-sm must fit a six-character value ("452.6K", "42/100") inside a
     1x1 tile. */
  --num-sm: 1.4rem;
  --num-lg: clamp(1.9rem, 2.6vw, 2.35rem);

  --text-numeral: clamp(1.9rem, 2.6vw, 2.35rem); /* KPI numerals, 900-weight optical but set 300 for CEC3 parity on big numerals - see components.css .kpi-num which intentionally uses --w-display for a lighter numeral read; small numerals (tags, deltas) use --w-numeral */

  --lh-tight: 1.1;
  --lh-snug: 1.35;
  --lh-body: 1.55;

  --tracking-eyebrow: 0.08em;
  --tracking-label: 0.05em;
  --tracking-tight: -0.01em;

  /* ---------------- Spacing scale ---------------- */
  --space-3xs: 0.2rem;
  --space-2xs: 0.35rem;
  --space-xs: 0.5rem;
  --space-sm: 0.75rem;
  --space-md: 1.1rem;
  --space-lg: 1.5rem;
  --space-xl: 2rem;
  --space-2xl: 2.75rem;
  --space-3xl: 3.5rem;

  /* ---------------- Radii ---------------- */
  --radius-lg: 18px;
  --radius: 14px;
  --radius-sm: 10px;
  --radius-xs: 7px;
  --radius-pill: 999px;

  /* ---------------- Neutral ink / paper (product overrides these two only
     if it truly needs to - most products keep white/near-white paper and
     a near-black ink; the BRAND colour is what moves) ---------------- */
  --ink: #0f2323;
  --grey: #585856;
  --white: #ffffff;

  /* ---------------- Two-layer soft shadow recipe ----------------
     Ambient + key, always tinted with the product's own --shadow-tint
     (a low-alpha version of --brand-deep). Falls back to a neutral ink
     tint if a product file hasn't defined one. */
  --shadow-tint: rgba(15, 35, 35, 0.5);
  --shadow-xs: 0 1px 2px rgba(15, 35, 35, 0.06);
  --shadow-sm: 0 1px 1px rgba(15, 35, 35, 0.04), 0 4px 12px rgba(15, 35, 35, 0.07);
  --shadow-md: 0 2px 4px rgba(15, 35, 35, 0.05), 0 12px 28px rgba(15, 35, 35, 0.11);
  --shadow-lg: 0 6px 16px rgba(15, 35, 35, 0.10), 0 24px 56px rgba(15, 35, 35, 0.16);
  --shadow-key: 0 2px 6px rgba(15, 35, 35, 0.08);

  /* ---------------- Hairline rule recipe ----------------
     Never a flat grey border. A hairline is a low-opacity tint of the
     product's own deep brand colour - product files override --line and
     --line-strong once --brand-deep is known. */
  --line: rgba(15, 35, 35, 0.10);
  --line-strong: rgba(15, 35, 35, 0.16);
  --surface-tint: rgba(15, 35, 35, 0.035);

  /* ---------------- Motion ---------------- */
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-snap: cubic-bezier(0.34, 1.56, 0.64, 1);
  --dur: 200ms;
  --dur-slow: 320ms;
  --dur-panel: 420ms;
  --dur-countup: 900ms;

  /* ---------------- Breakpoints (for reference in comments; CSS has no
     custom-property media queries, but components.css uses these exact
     values so keep them in sync if you change one) ----------------
     --bp-tablet: 980px;
     --bp-mobile: 620px; */
}

/* ---------------- Base reset ---------------- */
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
html, body {
  margin: 0; padding: 0;
  font-family: var(--font);
  font-size: 16px;
  line-height: var(--lh-body);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: "tnum" 1, "lnum" 1;
}

a { text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible {
  outline: none;
  /* the 1px ring is --brand-deep, not --brand: brand-deep tests AAA against
     paper for all 12 product palettes, so the ring stays visible even for a
     pale/bright brand colour (e.g. Earthrise:Live's gold on cream). */
  box-shadow: 0 0 0 3px var(--brand-20, rgba(15,35,35,0.14)), 0 0 0 1px var(--brand-deep, var(--ink));
  border-radius: var(--radius-xs);
}

h1, h2, h3, h4 { font-family: var(--font); color: var(--brand-deep, var(--ink)); margin: 0 0 var(--space-xs); letter-spacing: var(--tracking-tight); }
h1 { font-size: var(--text-h1); font-weight: var(--w-display); line-height: var(--lh-tight); }
h2 { font-size: var(--text-h2); font-weight: var(--w-head); letter-spacing: -0.008em; }
h3 { font-size: var(--text-h3); font-weight: var(--w-head); }
p { margin: 0 0 var(--space-sm); color: var(--grey); font-size: var(--text-body); }
.lede { font-size: var(--text-lede); color: var(--ink); font-weight: var(--w-body); }
strong { color: var(--ink); }
.tnum { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

/* Eyebrow - the 12px uppercase tracked label that precedes a headline or
   marks a section. First-class citizen of the type system.
   Text colour is --brand-deep, not --brand: measured against all 12 product
   palettes, --brand-deep on --paper is AAA (9:1 to 20:1) in every case,
   while flat --brand on paper fails AA text contrast for roughly half of
   them (the lighter, more saturated flat-UI palettes). The dot uses the
   same deep tone for the same reason - see colour.html for the numbers. */
.eyebrow {
  text-transform: uppercase;
  letter-spacing: var(--tracking-eyebrow);
  font-size: var(--text-eyebrow);
  font-weight: var(--w-numeral);
  color: var(--brand-deep, var(--ink));
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  margin-bottom: var(--space-xs);
}
.eyebrow::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--brand-deep, var(--ink)); flex: none; }

/* Accent modifier -- CEC3's "gold accent on eyebrow marks", for the one
   or two places per screen that should read as a deliberately designed
   accent rather than plain brand chrome (see tokens/<product>.css's
   --accent-1). Opt-in per eyebrow, not a global override: most eyebrows
   on most screens stay --brand-deep exactly as before. Added 3rd August
   2026 -- --accent-1 existed in every product's palette but was reachable
   from precisely one place in the whole estate before this.

   Recolours the DOT only, not the text. Checked 3rd August 2026 against
   this suite's own palette: --accent-1 (#b8862f) on white paper measures
   3.24:1 -- clears AA-large (3:1) but fails AA for normal text (4.5:1),
   and this eyebrow text sits at 0.72rem/900-weight, which does not
   qualify as "large text" under WCAG's own size test. Recolouring the
   6px dot only keeps every eyebrow's actual words at brand-deep's 14:1+,
   so the accent is decorative flair on a mark that carries no reading
   burden, never a contrast regression on text a person has to read. */
.eyebrow.accent::before { background: var(--accent-1, var(--brand-deep)); }

.wrap { max-width: 1220px; margin: 0 auto; padding: 0 1.75rem; }
.wrap-narrow { max-width: 780px; margin: 0 auto; padding: 0 1.75rem; }

/* ---------------- Layout helpers ---------------- */
.section { padding: var(--space-lg) 0; }
.grid { display: grid; gap: var(--space-lg); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 980px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 620px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
}
.split-main { display: grid; grid-template-columns: 2fr 1fr; gap: var(--space-lg); align-items: start; }
@media (max-width: 980px) { .split-main { grid-template-columns: 1fr; } }
.sticky-col { position: sticky; top: 5.5rem; }

.divider { height: 1px; background: var(--line); margin: var(--space-xl) 0; border: none; }
.muted { color: var(--grey); font-size: var(--text-sm); }
.flex-between { display: flex; justify-content: space-between; align-items: center; gap: var(--space-md); flex-wrap: wrap; }

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