/* ============================================================================
   LIL-ECHOES design tokens: the single source of truth.
   Type and spacing came from the Figma draft, corrected for contrast: the
   mock's 13px body text failed WCAG 1.4.3 as drawn.

   RULE: components consume tokens; they never invent values.

   A surface that changes the background underneath the content may re-bind a
   token to another token, which is what custom properties are for: .hero sets
   --c-focus to --c-focus-light because the near-black ring is invisible on a
   photograph. Re-binding to a token is allowed. Writing a raw colour anywhere
   outside this file is not. See docs/design-system.md.
   ========================================================================== */

:root {
  /* --- colour ------------------------------------------------------------ */

  /* The brand palette. Every ratio below is measured, not estimated.

     The four brand colours are saturated, so they work at size and not as
     text: #ff8c00 is 2.33:1 on white and #ffb852 is 1.72:1. The text colours
     are therefore deep shades and pale tints of the same two hues, and the
     saturated four are used for fills.

     RULE, measured: #0d75ff is 4.20:1 against white and 5.01:1 against black,
     so no BODY text can sit on it. Large text can: WCAG 1.4.3 asks 3:1 at 24px
     or more, or 18.7px bold, and white clears that. So it is a surface for
     headings only, and anything placed on it carries a size dependency. See
     the page banner in components.css. */

  --c-bg: #e1ecfd;          /* page, ink 11.9:1, accent 5.5:1 */
  --c-surface: #ffffff;     /* cards, ink 14.2:1, accent 6.6:1 */
  --c-surface-alt: #d3e3fb; /* alternating bands, placeholders, tags */
  --c-border: #5d85bd;      /* 3.2:1 on the page and 3.8:1 on a card */

  /* Two values of one blue rather than two different blues. */
  --c-ink: #0d2b52;         /* headings and body, 11.9:1 on --c-bg */
  --c-ink-muted: #1b3644;   /* secondary text, 10.6:1 on --c-bg */
  --c-brand-blue: #0d75ff;  /* FILL ONLY, see the rule above */
  --c-brand-orange: #ff8c00;      /* button fill, takes --c-ink at 6.1:1 */
  --c-brand-orange-soft: #ffb852; /* accents on a dark ground, 8.2:1 on --c-ink */

  /* There is no third orange. The brand orange cannot be small text anywhere
     here: 2.33:1 on a card, 1.96:1 on the page, 1.79:1 on a band, against the
     4.5 it needs. Darkening it until it passes produces a brown that is
     indistinguishable from the pre-brand accent, which is what happened.

     So orange is never text at small size. It is a fill, and on a link it is
     the rule underneath: the text itself is the ink at 14.15:1 and the orange
     is decoration, which carries no contrast requirement.

     Orange still means you can do something. Links and the primary action
     carry it; labels take the ink. The current page in the menu used to carry
     it too, as a bar, until 2.33:1 proved too faint for a state indicator; it
     is an ink bar now (header.css). */
  --c-link: var(--c-ink);         /* link text, 14.15:1 on a card */
  --c-link-rule: var(--c-brand-orange); /* the underline, decorative */
  --c-focus: var(--c-ink);        /* 14.15:1 on a card, 11.9:1 on the page */
  --c-focus-light: #ffffff; /* for dark surfaces, see .hero */
  /* 0.62 is the floor, not a taste: over a worst case pure white pixel it is
     what puts the hero's white headline and lede at 5.72:1. At 0.58 it drops
     under 4.5:1. */
  --c-scrim: rgb(6 0 79 / 0.62);
  /* The hero's purple: the scrim over the dark left of the photograph,
     sampled at about #120a3a. Behind the photo, so on a wide screen the sides
     it fades into match its left edge instead of turning navy. */
  --c-hero-edge: #120a3a;

  /* Two treatments, not four colours. Always paired with a text label, never
     colour alone. The warm pair is the brand orange on a dark chip, which is
     the one place small orange text is legal, at 6.1:1. The cool pair is the
     ink on the pale chip. */
  --c-tag-event: var(--c-brand-orange);     /* 6.1:1 on --c-tag-warm-bg */
  --c-tag-publication: var(--c-brand-orange);
  --c-tag-warm-bg: var(--c-ink);
  --c-tag-research: var(--c-ink);           /* 10.9:1 on --c-surface-alt */
  --c-tag-milestone: var(--c-ink);
  --c-tag-cool-bg: var(--c-surface-alt);

  /* Deliverable status as four values of one hue, which is what carries the
     progression: a ramp reads as a sequence, four hues read as four unrelated
     categories. Each measured on --c-surface-alt. */
  --c-status-planned: #1b3644;   /* 9.7:1 */
  --c-status-progress: #0d2b52;  /* 10.9:1 */
  --c-status-submitted: #202d85; /* 9.1:1 */
  --c-status-approved: #0057ba;  /* 5.3:1 */

  /* --- type -------------------------------------------------------------- */
  --f-sans: "Clarity City", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --f-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;

  --t-xs: 0.8125rem;   /* 13px, metadata only, never body copy */
  --t-sm: 0.9375rem;   /* 15px */
  --t-base: 1rem;      /* 16px floor, up from the mock's 13px */
  --t-lg: 1.125rem;
  --t-xl: 1.375rem;
  --t-2xl: 1.75rem;
  --t-3xl: clamp(1.75rem, 1.2rem + 2vw, 2.5rem);
  --t-4xl: clamp(2.25rem, 1.4rem + 3.4vw, 3.5rem);

  --lh-tight: 1.15;
  --lh-snug: 1.3;
  --lh-body: 1.6;

  --fw-normal: 400;
  --fw-medium: 500;
  --fw-bold: 700;

  /* --- space (4px base) --------------------------------------------------- */
  --s-1: 0.25rem;
  --s-2: 0.5rem;
  --s-3: 0.75rem;
  --s-4: 1rem;
  --s-5: 1.5rem;
  --s-6: 2rem;
  --s-7: 3rem;
  --s-8: 4rem;
  --s-9: 6rem;

  /* --- structure --------------------------------------------------------- */
  --radius: 8px;
  --radius-lg: 16px;
  --radius-pill: 999px;
  --shadow-card: 0 1px 2px rgb(31 58 61 / 0.06), 0 4px 12px rgb(31 58 61 / 0.05);
  --shadow-raised: 0 2px 4px rgb(31 58 61 / 0.08), 0 12px 24px rgb(31 58 61 / 0.08);

  /* The reading measure, consumed by .prose, .lede, .card--note and
     .footnote p. A ceiling, not a target.

     ch is the advance width of the digit "0", which in a proportional face runs
     about 10% wider than the average prose character. So the rendered line
     holds MORE characters than the number says: 68ch was already around 75, and
     72ch is around 80, which is the limit WCAG 1.4.8 sets. 80ch would be about
     88 and would break it, which is the trap in reading this unit as
     "characters".

     Raised from 68ch after consortium feedback that the About page used about
     half the page width. The larger part of that fix was structural: see the
     qualities list in about.njk, which is a grid and should never have been
     inside a .prose block. */
  --measure: 72ch;
  --container: 1200px;
  /* The sticky header's height with a little room, for scroll-padding-top.
     One 72px row from 375px up; it wraps to two below that, hence the larger
     value in the query that follows this block. */
  --header-offset: 6rem;
  /* For a page that is mostly running text, used by .container--narrow. Wide
     enough for the nine qualities to sit three to a row, close enough to
     --measure that a paragraph no longer has a 400px void beside it. */
  --container-narrow: 900px;
  --gutter: clamp(1rem, 4vw, 2.5rem);

  /* EU visibility: nothing on this site may be taller than the EU emblem.
     Every other logo is capped at these values, which makes "at least as large
     as the biggest other logo" structurally true rather than a thing someone
     has to remember when partner #18 is added in year 3.

     Both values matter. The emblem itself is 36px below 36em and 48px above it,
     so the cap on every other logo has to switch at the same breakpoint. Capping
     other logos at 48px flat left them taller than a 36px emblem on a small
     phone, which is the breach this was meant to make impossible.
     See docs/eu-visibility.md */
  --logo-max-h: 48px;
  --logo-max-h-mobile: 36px;
  --logo-max-h-footer: 80px;
  /* The project wordmark in the header on a phone, below the emblem's 36px.
     The emblem cannot shrink, so on a 375px screen the room for one header row
     has to come from the wordmark, the menu button and the gaps (#9). Smaller
     than the emblem, so the rule above still holds. */
  --logo-max-h-brand-mobile: 28px;
}

/* The header wraps to two rows below about 375px, so the scroll offset grows
   with it. */
@media (max-width: 23.4em) {
  :root {
    --header-offset: 9rem;
  }
}

/* ---------------------------------------------------------------- reset --- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

body,
h1, h2, h3, h4, p, figure, blockquote, dl, dd, ul, ol {
  margin: 0;
}

ul[class],
ol[class] {
  list-style: none;
  padding: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  /* The header is sticky, so anything scrolled to, a #link target or an
     element receiving focus, would otherwise land underneath it (WCAG 2.2,
     2.4.11 Focus Not Obscured). The offset is the header's height plus room. */
  scroll-padding-top: var(--header-offset);
}

body {
  min-height: 100vh;
  background: var(--c-bg);
  color: var(--c-ink);
  font-family: var(--f-sans);
  font-size: var(--t-base);
  line-height: var(--lh-body);
  text-rendering: optimizeSpeed;
  /* The title band bleeds full width with calc(50% - 50vw), and 100vw counts a
     classic scrollbar, so on Windows the page scrolled sideways by its width
     (1.4.10). clip, not hidden: hidden would make body a scroll container and
     stop the sticky header sticking. */
  overflow-x: clip;
}

img,
picture,
svg {
  max-width: 100%;
  display: block;
}

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

/* ------------------------------------------------------------- utilities -- */

.visually-hidden:not(:focus):not(:active) {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  width: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
}

/* A single, strong focus ring everywhere. The mock designed no focus states
   at all; this is the replacement. */
:focus-visible {
  outline: 3px solid var(--c-focus);
  outline-offset: 2px;
  border-radius: 2px;
}

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