/* =========================================================================
   tokens.css — design system.

   Direction: "Dossier" (locked at Gate 1) with the §3 structural model layered
   on: a THREE-TIER paper ground, a hatch treatment on the `absent` state, and
   radius capped at 3px so surfaces read as document pages rather than app
   chrome.

   Three-tier ground (§3): the page sits on --paper, cards lift to the LIGHTER
   --paper-card, and alternating section bands drop to the DARKER --paper-band.
   Sections therefore read as a sequence of sheets.

   Every colour pair is machine-verified — WCAG AA (4.5:1 text, 3:1 graphics)
   and present/absent separation under protanopia/deuteranopia/tritanopia
   (Machado 2009, CIE76 ΔE ≥ 20). Re-run scratchpad/palette-check.js after ANY
   colour edit.

   Scoped to [data-direction="dossier"] on <html>.
   ========================================================================= */

/* ----------------------------------------------------------- @font-face */
/* All SIL OFL 1.1. Licenses: /assets/fonts/LICENSES/. Self-hosted, so there
   is no render-blocking font CDN (§3). */
@font-face { font-family: 'Fraunces'; font-style: normal; font-weight: 600;
  font-display: swap; src: url('/assets/fonts/fraunces-latin-600-normal.woff2') format('woff2'); }
@font-face { font-family: 'Fraunces'; font-style: normal; font-weight: 700;
  font-display: swap; src: url('/assets/fonts/fraunces-latin-700-normal.woff2') format('woff2'); }
@font-face { font-family: 'Source Sans 3'; font-style: normal; font-weight: 400;
  font-display: swap; src: url('/assets/fonts/source-sans-3-latin-400-normal.woff2') format('woff2'); }
@font-face { font-family: 'Source Sans 3'; font-style: normal; font-weight: 600;
  font-display: swap; src: url('/assets/fonts/source-sans-3-latin-600-normal.woff2') format('woff2'); }
@font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 400;
  font-display: swap; src: url('/assets/fonts/ibm-plex-mono-latin-400-normal.woff2') format('woff2'); }
@font-face { font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 500;
  font-display: swap; src: url('/assets/fonts/ibm-plex-mono-latin-500-normal.woff2') format('woff2'); }

/* ===================================================== SHARED SCALES ==== */
:root {
  /* -- spacing: 4px base -- */
  --s-0: 0;      --s-1: .25rem;  --s-2: .5rem;   --s-3: .75rem;
  --s-4: 1rem;   --s-5: 1.5rem;  --s-6: 2rem;    --s-7: 3rem;
  --s-8: 4rem;   --s-9: 6rem;    --s-10: 8rem;

  /* -- radius --
     The 3px cap came from the original §3 brief and was a measured cause of the
     page reading austere rather than premium: the category sits at 8-16px
     (Profound 8, Athena 8/16, Brandlight 16/10/12, Rankscale 12/16).
     Lifted to match, while staying tighter than a consumer app. */
  --r-1: 4px;  --r-2: 8px;  --r-3: 12px;  --r-4: 16px;  --r-pill: 999px;

  /* -- type scale (1.200 minor third; fluid at the display end) -- */
  --t-xs:   .75rem;    /* provenance labels, eyebrows, chips */
  --t-sm:   .875rem;   /* mono queries, captions, source links */
  --t-base: 1rem;
  --t-md:   1.125rem;  /* lede */
  --t-lg:   1.375rem;
  --t-xl:   1.75rem;
  --t-2xl:  2.25rem;
  /* Display sizes. Measured competitor h1: Semrush 100px, Rankscale 96,
     Evertune 73.6, Athena 72, Peec 64, Brandlight 64. Ours was 57.6 — roughly
     30% under the category and the second-largest cause of the flat feel.
     Raised to 84px, which is as far as the hero column carries without
     dropping to four words a line. */
  --t-3xl:  clamp(2.1rem, 1.4rem + 2.6vw, 3.5rem);   /* section headlines → 56px */
  --t-4xl:  clamp(2.6rem, 1.6rem + 4.2vw, 4.75rem); /* the one h1 → 76px */
  --t-num:  clamp(3rem, 2rem + 5vw, 5.5rem);        /* statistics band */

  --lh-tight: 1.06;  --lh-snug: 1.24;  --lh-normal: 1.55;  --lh-loose: 1.68;
  --tracking-tight: -0.022em;  --tracking-wide: 0.14em;

  /* -- motion (§4). Four durations, ONE easing. -- */
  --dur-1: 150ms;    /* hover, focus, press */
  --dur-2: 300ms;    /* reveal, accordion, tab change */
  --dur-3: 600ms;    /* draw-on, count-up */
  --dur-4: 1100ms;   /* signature moments */
  --ease: cubic-bezier(.2, .7, .2, 1);
  --ease-linear: linear;

  /* -- the single reveal pattern (§4.6) -- */
  --reveal-distance: 12px;
  --reveal-stagger: 60ms;

  /* -- layout -- */
  --container: 1152px;
  --container-narrow: 760px;
  --header-h: 66px;
}

/* ================================================== DOSSIER · light ==== */
[data-direction='dossier'] {
  --font-display: 'Fraunces', Georgia, 'Times New Roman', serif;
  --font-body: 'Source Sans 3', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --font-mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;
  --display-weight: 700;

  /* three-tier ground — WARMED 2026-08-01.
     The old set read as grey despite being warm in HUE: its chroma was tiny.
     #F4F2EC has an R-B spread of 8/255, which at 94% lightness the eye simply
     resolves as neutral. Warmth here was a CHROMA problem, not a hue one, so
     the fix is more colour rather than a different colour.

     OKLCh chroma 0.0083 -> 0.0168, i.e. twice as warm, for no contrast cost:
     ink 14.96 / ink-2 7.38 / muted 6.32 / brand 5.17 on the new page ground,
     and 13.21 / 6.51 / 5.58 / 4.56 on the darkest tier. Brand vs absent stays
     separable under CVD — worst case protanopia ΔE 29.6 against a >20 target.

     Still nowhere near yellow: 0.0168 is about a tenth of a saturated colour's
     chroma. It reads as manila stock, which is what a dossier is filed in. */
  --paper:       #F7F2E6;   /* page ground — warm manila stock, not white */
  --paper-card:  #FDFAF2;   /* raised: cards, report chrome */
  --paper-band:  #ECE4D2;   /* recessed: alternating bands, marquee strips */

  --ink:         #1B1E23;
  --ink-2:       #4A4F57;
  /* Darkened twice. First from #676D76, to clear 4.5:1 on the DARKEST tier
     (--paper-band) rather than only on --paper. Then from #5F656E, because a
     contrast audit of the whole page found the body copy was strong (6.5-7.8:1)
     while THIS tier sat at 5.25 / 5.58 / 4.67 — and it is the tier used for
     every eyebrow, caption and mono label, i.e. the first line of every
     section. The page read as faded because its lead-ins were its weakest text.
     Now 6.31 / 6.71 / 5.61 across paper / card / band.
     Not taken further: #4E545D and darker close to within 0.01 luminance of
     --ink-2, which collapses three ink tiers into two. */
  --ink-muted:   #535962;
  --rule:        #D6D1C6;
  --rule-strong: #BEB8A9;

  /* the evidence marker — annotation ONLY. Never a button, border,
     gradient or section fill. (§3) */
  --marker:      #FBE38E;
  --marker-edge: #E4BE4E;

  /* Product ontology AND the brand accent — deliberately the same hue.
     Every competitor with a strong page commits to one saturated accent
     (#4f39f6, #2f4fff, #f7594e, #c190ff, #13492e); ours was a muted teal used
     sparingly, so the page had no colour event. Punched up from #14615A and
     promoted to carry the primary CTA. Being PRESENT in the answer is the
     product, so the brand colour and the `present` state are one thing.
     Verified: 5.16:1 on paper, 5.48:1 on card, 5.48:1 for card-on-brand
     (button fills), and present/absent separation holds under all three CVD
     types (worst dE 36.6). */
  --brand:        #0D7360;
  --brand-bright: #1FA88A;   /* decorative only — gradients and glow */
  --brand-deep:   #06483C;   /* dark gradient stops */
  --present:      #0D7360;
  --present-soft: #DCEBE8;
  --absent:       #A6400E;
  --absent-soft:  #F7E3D8;

  /* Depth. Peec ships 159 shadowed elements, Brandlight 140, Rankscale 87 —
     we had three tokens barely used. Deepened so cards actually lift. */
  --shadow-1: 0 1px 2px rgba(27,30,35,.05), 0 1px 3px rgba(27,30,35,.04);
  --shadow-2: 0 2px 4px rgba(27,30,35,.05), 0 10px 24px rgba(27,30,35,.08);
  --shadow-3: 0 4px 10px rgba(27,30,35,.07), 0 22px 48px rgba(27,30,35,.13);
  --shadow-brand: 0 10px 30px rgba(13,115,96,.22);

  /* Gradients. The category leans on these hard (Peec 230 elements,
     Rankscale 117); we had zero, which is why large areas read as dead space. */
  --wash-hero: radial-gradient(120% 90% at 78% 12%, rgba(31,168,138,.16) 0%, rgba(31,168,138,.05) 38%, transparent 70%),
               radial-gradient(90% 70% at 8% 0%, rgba(251,227,142,.20) 0%, transparent 60%);
  --wash-band: linear-gradient(180deg, rgba(13,115,96,.05) 0%, transparent 42%);
  --wash-cta:  radial-gradient(90% 140% at 50% 0%, rgba(31,168,138,.14) 0%, transparent 62%);

  --focus: #0D7360;
}

/* =================================================== DOSSIER · dark ==== */
[data-direction='dossier'][data-theme='dark'] {
  --paper:       #17191D;
  --paper-card:  #1F2228;
  --paper-band:  #101216;

  --ink:         #F2F0EA;
  --ink-2:       #C3C6CC;
  --ink-muted:   #949AA4;
  --rule:        #2E323A;
  --rule-strong: #3E434C;

  --marker:      #6B5A1F;
  --marker-edge: #8A7529;

  --brand:        #3FBFA4;
  --brand-bright: #5FD9BE;
  --brand-deep:   #0A5645;
  --present:      #3FBFA4;
  --present-soft: #10332C;
  --absent:       #F0915A;
  --absent-soft:  #3A2115;

  --shadow-1: 0 1px 2px rgba(0,0,0,.5);
  --shadow-2: 0 2px 4px rgba(0,0,0,.5), 0 10px 24px rgba(0,0,0,.45);
  --shadow-3: 0 4px 10px rgba(0,0,0,.55), 0 22px 48px rgba(0,0,0,.6);
  --shadow-brand: 0 10px 30px rgba(63,191,164,.18);

  --wash-hero: radial-gradient(120% 90% at 78% 12%, rgba(63,191,164,.14) 0%, rgba(63,191,164,.04) 38%, transparent 70%),
               radial-gradient(90% 70% at 8% 0%, rgba(107,90,31,.22) 0%, transparent 60%);
  --wash-band: linear-gradient(180deg, rgba(63,191,164,.06) 0%, transparent 42%);
  --wash-cta:  radial-gradient(90% 140% at 50% 0%, rgba(63,191,164,.12) 0%, transparent 62%);

  --focus: #3FBFA4;
}

/* =================================================== PRIMITIVES ======== */

/* Every figure on the site. Tabular numerals are a token, not a per-component
   afterthought: without them count-ups jitter and columns fail to align. */
.u-num {
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: 'tnum' 1, 'lnum' 1;
}

/* Buyer queries, source domains, provenance labels — the recurring motif. */
.u-mono { font-family: var(--font-mono); font-size: .92em; letter-spacing: -.01em; }

/* The evidence marker. A mark made ON evidence — nothing else. */
.u-mark { position: relative; isolation: isolate; padding: 0 .18em; white-space: nowrap; }
.u-mark::before {
  content: ''; position: absolute; z-index: -1;
  left: 0; right: 0; top: 8%; bottom: 8%;
  background: var(--marker);
  box-shadow: inset 0 -1px 0 var(--marker-edge);
  transform: scaleX(var(--mark-scale, 1)); transform-origin: left center;
  transition: transform var(--dur-2) var(--ease);
}

/* Wrap-safe marker, for annotating a phrase inside a headline. Uses a cloned
   background rather than a positioned pseudo-element, so it paints correctly on
   every line fragment when the phrase wraps — the failure mode `.u-mark` has.
   Static decoration: it is revealed by its parent's reveal, it does not sweep. */
.u-mark--wrap {
  background-image: linear-gradient(to bottom,
    transparent 6%, var(--marker) 6%, var(--marker) 94%, transparent 94%);
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
  padding: 0 .12em;
  box-shadow: inset 0 -1px 0 var(--marker-edge);
}

/* Semantic states. Colour is never the only signal: each carries a glyph and a
   word, and `absent` additionally carries a 135° hatch (§3) so it survives
   greyscale, CVD, and a black-and-white print of the page. */
.u-state {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-xs); font-weight: 600; letter-spacing: var(--tracking-wide);
  text-transform: uppercase; padding: .3em .6em; border-radius: var(--r-2);
  border: 1px solid currentColor;
}
.u-state--present { color: var(--present); background: var(--present-soft); }
.u-state--absent {
  color: var(--absent);
  background-color: var(--absent-soft);
  background-image: repeating-linear-gradient(135deg,
    transparent 0 5px, color-mix(in srgb, var(--absent) 22%, transparent) 5px 6px);
}

/* The hatch as a standalone fill, for chart geometry. */
.u-hatch {
  background-color: var(--absent-soft);
  background-image: repeating-linear-gradient(135deg,
    transparent 0 5px, color-mix(in srgb, var(--absent) 34%, transparent) 5px 7px);
}

.u-provenance {
  font-family: var(--font-mono); font-size: var(--t-xs);
  color: var(--ink-muted); letter-spacing: 0;
}

/* --------------------------------------------- the ONE reveal pattern -- */
/* §4.6: 12px rise + fade, 300ms, 60ms sibling stagger, fires once.
   transform + opacity only. There is no second reveal style on this site. */
.js [data-reveal] {
  opacity: 0;
  transform: translate3d(0, var(--reveal-distance), 0);
  transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease);
}
.js [data-reveal].in-view { opacity: 1; transform: none; }

/* ------------------------------------------------------- focus visible -- */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: var(--r-2);
}

/* ------------------------------------------------------ reduced motion -- */
/* §4: reduced motion renders the FINAL SETTLED FRAME of every animation —
   no typing, no cycling, no marquee movement, no parallax, no autoplay. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .js [data-reveal] { opacity: 1 !important; transform: none !important; }
  .u-mark::before { transform: scaleX(1) !important; }
}
