/* ==========================================================================
   Design tokens
   Palette values are sampled pixel-for-pixel from /img/logo.png, not eyeballed.
     navy   #001235   "COPTERS" in the wordmark
     azure  #097CE4   "GYRO" in the wordmark, and the accent stroke
     silver #B8C0C9   the mid-tone speed rule

   Re-sample after any logo change. The current mark carries far more blue than
   the previous one, since GYRO is set in azure rather than navy, so the accent
   now reads as a primary brand colour rather than a highlight.
   ========================================================================== */

:root {
  /* -- Core palette ------------------------------------------------------ */
  --navy-900: #00081F;
  --navy-800: #001235;   /* sampled */
  --navy-700: #061F4C;
  --navy-600: #0E2E63;
  --navy-500: #18407B;

  --azure-600: #0A6ECB;
  --azure-500: #097CE4;  /* sampled */
  --azure-400: #3B98EB;
  --azure-300: #7CBEF4;

  --silver-500: #8792A1;
  --silver-400: #B8C0C9;  /* sampled */
  --silver-300: #D2D8DF;
  --silver-200: #E6EAEF;
  --silver-100: #F3F5F8;

  --paper: #FFFFFF;

  /* -- Semantic surfaces ------------------------------------------------- */
  --bg-page:      var(--paper);
  --bg-sunken:    var(--silver-100);
  --bg-inverse:   var(--navy-800);
  --bg-inverse-2: var(--navy-900);

  --ink-strong: var(--navy-900);
  --ink-body:   #2B3648;
  --ink-muted:  #5B6779;
  --ink-invert: #EDF2F8;
  --ink-invert-muted: #A9B7CB;

  --rule:        color-mix(in oklab, var(--silver-400) 55%, transparent);
  /* Used for the boundary of form controls and outlined buttons, so it is held
     to the 3:1 non-text contrast minimum. The logo silver (#B8C0C9) manages
     only 1.84:1 on white and is kept for decorative rules alone. */
  --rule-strong: #7C8798;   /* 3.64:1 on white */
  --rule-invert: color-mix(in oklab, var(--silver-400) 26%, transparent);

  /* Two blues, deliberately.
   *
   * --brand-azure is the logo colour, used where contrast rules do not apply:
   * rotor arcs, section markers, the leading rule on a kicker.
   *
   * --accent is the interactive colour for links, buttons and controls. The
   * logo azure reaches 4.19:1 on white, which still fails the 4.5:1 minimum
   * for body text, so interactive blue stays a darkened version of it. Using
   * the brand colour directly for links would make every link on the site
   * non-conformant.
   *
   * The current mark's blue is darker than the previous one, so the gap has
   * closed: --accent is now only a step or so off the brand colour rather
   * than an obviously different blue. */
  --brand-azure:  var(--azure-500);
  --accent:       #0A6ECB;   /* 5.11:1 on white, and white on it */
  --accent-hover: #08579F;   /* 7.31:1 */
  --accent-ink:   #FFFFFF;

  /* Focus ring must clear 3:1 against both the light page and the navy
     sections, so it is drawn as a dual ring rather than a single colour. */
  --focus-inner: #FFFFFF;
  --focus-outer: var(--azure-500);

  /* Status colours carry white text, so each is dark enough to clear 4.5:1
     against it. Every status is also labelled in words, because colour alone
     is never the carrier of meaning. */
  --state-live:    #0F7A39;  /* 5.44:1 with white */
  /* Wrong-answer marker and error states. Darkened from a plain red until it
     clears 4.5:1 against white text, since it is used as a filled chip. */
  --state-alert:   #A8321E;  /* 6.69:1 with white */
  --state-sold:    #B4453C;  /* 5.44:1 with white */
  --state-caution: #8A5410;  /* 5.61:1 with white */

  /* -- Typography -------------------------------------------------------- */
  /* System stacks only. No webfont request blocks first paint, and nothing
     leaves the origin, which keeps the CSP tight and the GDPR story simple. */
  --font-display: "Helvetica Neue", "Arial Narrow", "Roboto Condensed", system-ui, sans-serif;
  --font-body: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono", Menlo, monospace;

  /* Fluid scale. Each step interpolates between a 360px and a 1600px viewport.
     The display step is a starting point only: headings marked .u-fit are
     re-measured by JS and given an exact fitted size. */
  --step--1: clamp(0.82rem, 0.79rem + 0.14vw, 0.92rem);
  --step-0:  clamp(1.00rem, 0.96rem + 0.20vw, 1.13rem);
  --step-1:  clamp(1.20rem, 1.11rem + 0.42vw, 1.48rem);
  --step-2:  clamp(1.44rem, 1.27rem + 0.76vw, 1.94rem);
  --step-3:  clamp(1.73rem, 1.44rem + 1.29vw, 2.55rem);
  --step-4:  clamp(2.07rem, 1.61rem + 2.06vw, 3.34rem);
  --step-5:  clamp(2.49rem, 1.76rem + 3.24vw, 4.39rem);

  --leading-tight: 0.92;
  --leading-snug:  1.15;
  --leading-body:  1.62;

  --tracking-display: -0.02em;
  --tracking-kicker:   0.18em;

  /* -- Space ------------------------------------------------------------- */
  --space-3xs: clamp(0.25rem, 0.24rem + 0.05vw, 0.31rem);
  --space-2xs: clamp(0.50rem, 0.48rem + 0.10vw, 0.63rem);
  --space-xs:  clamp(0.75rem, 0.72rem + 0.15vw, 0.94rem);
  --space-s:   clamp(1.00rem, 0.96rem + 0.20vw, 1.25rem);
  --space-m:   clamp(1.50rem, 1.44rem + 0.30vw, 1.88rem);
  --space-l:   clamp(2.00rem, 1.86rem + 0.61vw, 2.50rem);
  --space-xl:  clamp(3.00rem, 2.79rem + 0.91vw, 3.75rem);
  --space-2xl: clamp(4.00rem, 3.53rem + 2.07vw, 5.63rem);
  --space-3xl: clamp(6.00rem, 5.16rem + 3.72vw, 9.00rem);

  /* -- Structure --------------------------------------------------------- */
  --measure: 68ch;
  --gutter: clamp(1.25rem, 4vw, 4rem);
  --shell-max: 96rem;

  /* The oblique motif. The wordmark leans; every accent rule, tab and marker
     on the site leans by the same angle so the geometry reads as one system. */
  --lean: -11deg;

  --radius-xs: 2px;
  --radius-s: 4px;
  --radius-m: 8px;
  --radius-l: 16px;

  --shadow-low:  0 1px 2px rgb(3 22 59 / 0.06), 0 2px 8px rgb(3 22 59 / 0.05);
  --shadow-mid:  0 2px 6px rgb(3 22 59 / 0.07), 0 12px 28px rgb(3 22 59 / 0.09);
  --shadow-high: 0 4px 12px rgb(3 22 59 / 0.09), 0 28px 64px rgb(3 22 59 / 0.16);

  /* -- Motion ------------------------------------------------------------ */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-fast: 140ms;
  --dur-base: 260ms;
  --dur-slow: 520ms;

  /* Mega menu timing. The open delay stops the panel firing as the pointer
     crosses the bar on its way somewhere else; the close delay gives a
     diagonal pointer path time to reach the panel. */
  --megamenu-open-delay: 130ms;
  --megamenu-close-delay: 260ms;

  /* Layered chrome. Kept in one place so nothing gets a random z-index. */
  --z-base: 1;
  --z-sticky: 40;
  --z-header: 60;
  --z-megamenu: 70;
  --z-dock: 80;
  --z-drawer: 90;
  --z-a11y: 100;
  --z-skip: 110;
}

/* --------------------------------------------------------------------------
   Accessibility panel states. Every one of these is a user preference applied
   to <html>, persisted to localStorage, and readable by every component
   because they all consume the same semantic tokens above.
   -------------------------------------------------------------------------- */

/* High contrast: forces pure black on white, removes tinted surfaces, and
   thickens every rule. Verified to exceed 7:1 on body text. */
html[data-contrast="high"] {
  --bg-page: #FFFFFF;
  --bg-sunken: #FFFFFF;
  --bg-inverse: #000000;
  --bg-inverse-2: #000000;
  --ink-strong: #000000;
  --ink-body: #000000;
  --ink-muted: #1A1A1A;
  --ink-invert: #FFFFFF;
  --ink-invert-muted: #FFFFFF;
  --rule: #000000;
  --rule-strong: #000000;
  --rule-invert: #FFFFFF;
  --accent: #00429A;
  --accent-hover: #002F6E;
  --accent-ink: #FFFFFF;
  --shadow-low: none;
  --shadow-mid: none;
  --shadow-high: none;
}

html[data-contrast="high"] * {
  text-shadow: none !important;
}

html[data-contrast="high"] :is(.c-card, .c-panel, .c-dock, .c-megamenu__panel) {
  border: 2px solid var(--rule-strong) !important;
}

/* Text scaling. Applied at the root so every rem-based value scales together
   and no layout depends on a fixed pixel body size. */
html[data-textscale="110"] { font-size: 110%; }
html[data-textscale="125"] { font-size: 125%; }
html[data-textscale="150"] { font-size: 150%; }
html[data-textscale="200"] { font-size: 200%; }

/* Readable-font mode.
   OpenDyslexic and Atkinson Hyperlegible are self-hosted in /assets/fonts.
   Until those files are present this rule falls through to the tail of the
   stack, which is a genuinely more legible system face plus the letter and
   word spacing that carries most of the measured benefit. */
html[data-readable="on"] {
  --font-display: "OpenDyslexic", "Atkinson Hyperlegible", Verdana, Tahoma, sans-serif;
  --font-body: "OpenDyslexic", "Atkinson Hyperlegible", Verdana, Tahoma, sans-serif;
  --leading-body: 1.85;
  --leading-snug: 1.35;
  --leading-tight: 1.1;
  --tracking-display: 0.01em;
}

html[data-readable="on"] body {
  letter-spacing: 0.045em;
  word-spacing: 0.16em;
}

/* Readable mode and the fit-to-width headline treatment fight each other:
   one wants generous spacing, the other compresses to fill a box. Legibility
   wins, so fitting is switched off and headings wrap normally. */
html[data-readable="on"] :is(.u-fit, .u-fit__line) {
  font-size: var(--step-2) !important;
  letter-spacing: normal !important;
  white-space: normal !important;
  overflow: visible !important;
  display: inline;
}

/* Reflow safety net.
 *
 * WCAG 2.2 success criterion 1.4.10 requires content to reflow without
 * horizontal scrolling at 320 CSS pixels. A single long word at a large text
 * scale cannot satisfy that by wrapping, because there is no space to wrap at.
 * Breaking the word is the only remaining option, so the no-mid-word-break
 * rule for display headings yields here. It applies only once the user has
 * actually scaled the text or asked for the readable face; at default settings
 * the fitter guarantees a clean single line and this never engages.
 */
html:is([data-textscale], [data-readable="on"]) :is(h1, h2, h3, .c-kicker, .u-fit, .u-fit__line) {
  overflow-wrap: break-word;
  hyphens: auto;
}

/* Wide tracking costs horizontal room that a scaled-up viewport does not have. */
html:is([data-textscale="150"], [data-textscale="200"], [data-readable="on"]) .c-kicker {
  --tracking-kicker: 0.06em;
  letter-spacing: var(--tracking-kicker);
}
html:is([data-textscale="150"], [data-textscale="200"]) .c-kicker::before { width: 1rem; }

/* Link highlighting for users who cannot rely on colour alone. */
html[data-links="highlight"] a:not(.c-btn) {
  text-decoration: underline !important;
  text-decoration-thickness: 2px !important;
  text-underline-offset: 3px !important;
  background-color: color-mix(in oklab, var(--accent) 12%, transparent);
  outline: 1px solid color-mix(in oklab, var(--accent) 40%, transparent);
  outline-offset: 1px;
}

/* Motion suppression, from either the OS setting or the panel toggle. */
@media (prefers-reduced-motion: reduce) {
  html:not([data-motion="full"]) {
    --dur-fast: 1ms;
    --dur-base: 1ms;
    --dur-slow: 1ms;
  }
}

html[data-motion="reduced"] {
  --dur-fast: 1ms;
  --dur-base: 1ms;
  --dur-slow: 1ms;
}

html[data-motion="reduced"] *,
html[data-motion="reduced"] *::before,
html[data-motion="reduced"] *::after {
  animation-duration: 1ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 1ms !important;
  scroll-behavior: auto !important;
}
