/* =============================================================
   Design tokens

   THIS IS A REFACTOR, NOT A REDESIGN.

   THE COLOUR VALUES BELOW ARE NEUTRAL PLACEHOLDERS, NOT A PALETTE.
   They exist so the template builds and passes contrast checks with
   no client attached. Replace every one during scaffold.

   Every value must be EXTRACTED from the client's rendered site, not
   invented. Procedure:

     1. `node tools/intake/extract-tokens.mjs <url>` reads computed
        styles from the live original and writes
        intake/tokens.candidate.css.
     2. Review the candidates. They are candidates, never a drop-in
        replacement for this file.
     3. Paste the real value in against each role.
     4. Where an extracted colour fails 4.5:1 contrast, adjust it and
        record the change in docs/AUDIT.md with a TOK- id. THAT IS THE
        ONLY LEGITIMATE REASON FOR A VALUE TO DIFFER FROM THE ORIGINAL.
        If a value changes for any other reason, that is a bug.

   Roles, not names: "brand-strong" survives a rebrand, "dark-blue"
   does not.

   No hex value may appear anywhere outside this file.
   ============================================================= */

/* =============================================================
   Webfonts — solved 27 Aug 2026 (AUDIT.md TOK-5).

   Named but never loaded until now: --font-display/--font-body below
   named Roboto/Lato, but there was no @font-face, no font files, and
   the <link> in base.njk was commented out — everyone got a system-ui
   fallback, which didn't match the live site's actual typography.

   Self-hosted, not loaded from Google's CDN, because the CDN would
   have added a live third-party request on every page load —
   contradicting the "no fonts, scripts or embeds from other
   companies" claim on /privacy/ the moment it shipped. Self-hosting
   keeps that claim true: these two files are the only network
   requests either font ever makes, both same-origin.

   Files sourced from fonts.gstatic.com, latin subset only — the
   language subsets Google also serves (cyrillic, greek, vietnamese,
   math, symbols) aren't needed for this site's English/Afrikaans
   copy, and shipping them would be exactly the unused-weight bloat
   BASELINE.md §4 flags builder output for. Both are SIL Open Font
   License 1.1, confirmed by reading it directly out of each file's
   own name table (fontTools), not assumed — full text and
   attribution in src/assets/fonts/OFL.txt, which travels with the
   files as the license requires.

   Roboto is a variable font (wght axis 100–900) — ONE file
   legitimately covers the 400/500/700 below; Google's own generated
   CSS serves the identical file for all three weight declarations,
   confirmed by diffing the URLs. Lato only ships weight 400 from
   Google at all; there is no font-weight: 200 file to load, which is
   the other half of the bug this replaces (see --weight-normal below).
   ============================================================= */

@font-face {
  font-family: Roboto;
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/assets/fonts/roboto-latin-variable.1404ca34.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: Lato;
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/fonts/lato-latin-400.918b7dc3.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* --- Colour: roles, not names.
         "brand-strong" survives a rebrand; "dark-blue" does not.

         EXTRACTED from the SiteJet original's own stylesheets by
         tools/intake/extract-tokens.mjs, then confirmed against the
         rendered pages. Scores below are how often each value appeared
         in SITE css, which is where the client's real choices live, as
         opposed to the builder's runtime defaults.

           #1c2b39  375 occurrences  -> brand-strong
           #1a1a1a  345             -> text
           #f5f5f5  305             -> bg
           #a78a29  101             -> brand
           #cacccb   30             -> border

         THIS IS A REFACTOR, NOT A REDESIGN. Three values deviate from
         what was measured, all three for contrast, all three recorded
         in docs/AUDIT.md. Nothing else changed. --- */
  --color-bg:            #f5f5f5;
  --color-bg-subtle:     #f0f2f1;
  --color-surface:       #fff;
  --color-text:          #1a1a1a;  /* 15.96:1 on --color-bg */
  --color-text-muted:    #595959;  /* 6.38:1 on --color-bg */
  --color-text-inverse:  #f5f5f5;  /* 13.25:1 on --color-brand-strong */

  /* TOK-1 — DEVIATION. Extracted #a78a29 measures 4.34:1 against
     --color-brand-strong, and layout.css puts footer headings in this
     colour on that background. Just under the 4.5:1 requirement, and
     "just under" still fails. Lightened to #cead36: 6.64:1 there, and
     8.00:1 as a button background behind --color-text, which is the
     role's other job. */
  --color-brand:         #cead36;
  --color-brand-strong:  #1c2b39;

  /* TOK-2 — DEVIATION. The brand gold cannot also be the text accent:
     #a78a29 is 3.05:1 on --color-bg and #cead36 is worse. This is a
     darkened variant of the same hue, used only where the background is
     light. 5.26:1. Keep the two separate when substituting — one is a
     surface colour, the other is an ink. */
  --color-accent:        #7a6414;

  /* TOK-3 — DEVIATION, by splitting rather than changing. Extracted
     #cacccb is 1.48:1 on the page, which is fine for a decorative
     hairline and not fine for a form input's boundary, where WCAG 2.2
     SC 1.4.11 wants 3:1. The extracted value is kept for decoration;
     UI boundaries use the second token. */
  --color-border:        #cacccb;
  --color-border-strong: #888;     /* 3.25:1 on --color-bg */

  /* Focus indicator — solved 26 Aug 2026 (AUDIT.md TOK-3). No single flat
     colour in this palette clears 3:1 (WCAG 1.4.11) against both the page
     background AND --color-brand-strong (used as the footer background):
     the maths only leaves a mid-grey band (luminance 0.17–0.27) as a
     colour that passes both, and a mid-grey focus ring is weak and off-
     brand. Using a two-ring halo instead — an inner ring in brand-strong
     navy, an outer ring in text-inverse — reusing the two colours already
     validated elsewhere in this file. On a light background the navy ring
     provides ~13:1 contrast and the halo is invisible (blends into the
     page). On the dark navy footer the navy ring blends in, but the halo
     provides that same ~13:1 contrast instead. Every real background in
     this palette has at least one visible ring. See the *:focus-visible
     rule below. Still needs the manual pass in ACCESSIBILITY-TEST-SCRIPT.md
     once real pages exist to test it on — the maths says it works, a
     screen and a keyboard should confirm it. */
  --color-focus:         var(--color-brand-strong);
  --color-focus-halo:    var(--color-text-inverse);
  --color-error:         #b3261e;
  --color-success:       #1b5e20;

  /* Third semantic tone the error/success pair never needed until now —
     article report chips and tables have a genuine third status
     ("open"/"in progress"), not just pass/fail. Same darkened-for-AA-text
     treatment as the two above: a legible ink derived from --color-brand
     rather than the raw accent, which is typically too light for body
     text on --color-bg. */
  --color-warning:       #7a6414;

  /* --- Type ---
         Extracted 26 Aug 2026, actually loaded 27 Aug 2026 (TOK-5,
         @font-face block above). The live site requests Lato at
         font-weight 200 for body copy, but Google Fonts only serves
         Lato at 400 — no font file for 200 exists to load, so the
         live site's "200" was always a browser-synthesised fake-thin
         weight, never a real design decision. Using the weight that
         actually exists: 400. --- */
  --font-display: "Roboto", system-ui, sans-serif;
  --font-body:    "Lato", system-ui, sans-serif;

  /* Fluid scale. Every clamp() has a rem min and a rem max so it
     still responds to the user's browser font size (WCAG 1.4.4). */
  --text-xs:   clamp(0.75rem, 0.72rem + 0.15vw, 0.8125rem);
  --text-sm:   clamp(0.875rem, 0.85rem + 0.15vw, 0.9375rem);
  --text-base: clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
  --text-lg:   clamp(1.125rem, 1.05rem + 0.4vw, 1.375rem);
  --text-xl:   clamp(1.375rem, 1.2rem + 0.8vw, 1.875rem);
  --text-2xl:  clamp(1.75rem, 1.4rem + 1.6vw, 2.75rem);
  --text-3xl:  clamp(2.25rem, 1.6rem + 3vw, 4rem);
  --leading-tight: 1.15;
  --leading-body:  1.6;   /* 1.5 minimum for WCAG 1.4.12 */
  --weight-normal: 400;

  /* Corrected 27 Aug 2026, alongside solving TOK-5. Extracted as 600 from
     h2's computed style, but Roboto only ships 100/300/400/500/700/900 —
     600 was never a real, loadable weight, just like the Lato 200 finding
     above. Now that the real variable font is loaded, asking for 600 would
     make the browser synthesise a fake weight again — exactly the bug this
     was meant to fix. 500 is the true Roboto weight closest to what was
     extracted. */
  --weight-medium: 500;
  --weight-bold:   700;

  /* --- Space: 4px base --- */
  --space-3xs: 0.25rem;
  --space-2xs: 0.5rem;
  --space-xs:  0.75rem;
  --space-s:   1rem;
  --space-m:   1.5rem;
  --space-l:   2rem;
  --space-xl:  3rem;
  --space-2xl: 4rem;
  --space-3xl: 6rem;

  /* --- Layout --- */
  --measure: 65ch;              /* reading width */
  --container-max: 75rem;
  --container-gutter: var(--space-m);

  /* --- Interaction --- */
  --radius-s: /* TODO extract — no small-radius element (card/input) observed yet */ 4px;
  --radius-m: /* TODO extract — no medium-radius element observed yet */ 8px;
  --radius-pill: /* extracted from live buttons/links (64px, i.e. fully round) */ 999px;

  /* Organic "blob" masks for photography, extracted 27 Aug 2026 from real
     figures on the live site (docs/AUDIT.md section I) — SiteJet's
     distinctive treatment for photos, each corner a different radius %.
     Two real samples found; using both for variety rather than repeating
     one shape on every image. */
  --radius-organic-1: 50% 40% 80% 30%;  /* live Collaboration page */
  --radius-organic-2: 20% 20% 20% 10%;  /* live Impact page */
  --target-min: 24px;           /* WCAG 2.2 SC 2.5.8 minimum */
  --target-comfortable: 44px;   /* what to actually aim for */
  --focus-width: 3px;
  --focus-offset: 2px;
  --shadow-s: /* TODO extract */ 0 1px 2px rgb(0 0 0 / 8%);
  --shadow-m: /* TODO extract */ 0 4px 12px rgb(0 0 0 / 10%);
  --duration-fast: 120ms;
  --duration-base: 200ms;
  --ease: cubic-bezier(0.2, 0, 0.2, 1);
}

/* Motion is opt-out at the token level, so no component has to remember. */
@media (prefers-reduced-motion: reduce) {
  :root {
    --duration-fast: 0.01ms;
    --duration-base: 0.01ms;
  }
}

/* -------------------------------------------------------------
   Non-negotiable base rules. Do not override these per component.
   ------------------------------------------------------------- */

*:focus-visible {
  outline: var(--focus-width) solid var(--color-focus);
  outline-offset: var(--focus-offset);

  /* The halo ring — see --color-focus-halo comment above. */
  box-shadow: 0 0 0 calc(var(--focus-width) + var(--focus-offset) * 2) var(--color-focus-halo);
}

/* The live site has a working skip link. Keep it working:
   off-screen until focused, then visible and in front of everything. */
.skip-link {
  position: absolute;
  top: var(--space-2xs);
  left: var(--space-2xs);
  z-index: 999;
  padding: var(--space-2xs) var(--space-s);
  background: var(--color-surface);
  color: var(--color-text);
  transform: translateY(-200%);
}

.skip-link:focus {
  transform: translateY(0);
}

/* Reserve space for media so nothing shifts on load (CLS). */
img, svg, video, iframe {
  max-width: 100%;
  height: auto;
}
