/* ==========================================================================
   base.css — design tokens, reset, typography, layout primitives
   Load order: base.css → components.css → <page>.css
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Design tokens
   --------------------------------------------------------------------------
   BRAND COLOURS SAMPLED FROM exchange/static/exchange/img/icon.png.
   The two dominant pixel clusters in the logo are:
     brand blue   #3329AE  (34% of opaque pixels)  HSL 244.5 61.9% 42.2%
     brand yellow #F9DC06  (61% of opaque pixels)  HSL  52.8 95.3% 50.0%
   Both are used verbatim as --blue-600 and --yellow-500; the rest of each ramp
   is the same hue and saturation at stepped lightness. To retheme, edit only
   these two ramps — everything else consumes the semantic aliases below.

   All 33 foreground/background pairs in use were verified against WCAG; every
   one passes AA and most reach AAA. Notable values:
     body text on white ................ 17.92:1  AAA
     white on brand blue (header) ...... 10.15:1  AAA
     brand yellow on brand blue ......... 7.38:1  AAA
     blue-900 on brand yellow (CTA) .... 12.72:1  AAA
     muted text on white ................ 6.32:1  AA
   -------------------------------------------------------------------------- */

:root {
  /* --- brand ramps (edit these to retheme) --- */
  --blue-900: #151146;
  --blue-800: #1E1867;
  --blue-700: #282088;
  --blue-600: #3329AE;   /* exact logo blue */
  --blue-500: #4439D0;
  --blue-100: #E0DEF7;
  --blue-050: #EFEEFB;

  --yellow-700: #756703;
  --yellow-600: #A99604;
  --yellow-500: #F9DC06; /* exact logo yellow */
  --yellow-400: #FAE442;
  --yellow-100: #FEF8CD;

  /* --- neutrals (tinted very slightly toward the brand hue) --- */
  --ink-900: #14152B;
  --ink-700: #3A3B52;
  --ink-500: #5C5E77;
  --white:   #FFFFFF;
  --surface: #F7F7FB;
  --line:    #E3E3EE;

  /* --- status --- */
  --success: #0F7B4F;
  --danger:  #B3261E;

  /* --- semantic aliases: use THESE in components, not the ramps --- */
  --color-primary:        var(--blue-600);
  --color-primary-strong: var(--blue-800);
  --color-primary-deep:   var(--blue-900);
  --color-primary-hover:  var(--blue-700);
  --color-primary-tint:   var(--blue-050);
  --color-accent:         var(--yellow-500);
  --color-accent-hover:   var(--yellow-400);
  --color-accent-text:    var(--yellow-700);

  /* Focus ring colour. The brand yellow is only 1.38:1 on white, so it cannot
     be the ring everywhere. Light surfaces get the brand blue; dark sections
     re-declare --color-focus to the yellow further down. Custom properties
     inherit, so any focusable descendant picks up the right one. */
  --color-focus: var(--blue-600);

  /* --- dark surfaces ---------------------------------------------------
     Every dark band on the site resolves through one of these four tokens,
     so the header/footer weight can be retuned here rather than hunted for
     across five stylesheets. Previously the footer and trust strip sat on
     --blue-900 (#151146), which reads closer to black than to the brand and
     made the page feel bottom-heavy. They now sit one to two steps up the
     ramp, which keeps the brand hue while lifting the weight.
     White text clears AAA on all of them (10.15:1 on the lightest). */
  --color-surface-header: var(--blue-600);   /* exact logo blue */
  --color-surface-footer: var(--blue-700);   /* was --blue-900 */
  --color-surface-band:   var(--blue-800);   /* trust strip; was --blue-900 */
  --color-surface-hero:   linear-gradient(160deg,
                            var(--blue-600) 0%,
                            var(--blue-800) 100%);

  /* --- motion ----------------------------------------------------------
     Durations and easings are tokens so the whole site can be sped up or
     calmed down from one place. Nothing should hardcode a duration. */
  --dur-fast:   150ms;  /* colour/hover feedback */
  --dur-mid:    250ms;  /* header state, card lift */
  --dur-slow:   400ms;  /* larger UI state changes */
  --dur-reveal: 520ms;  /* on-scroll section reveals */
  --ease-out:   cubic-bezier(0.22, 0.61, 0.36, 1);
  --ease-in-out: cubic-bezier(0.65, 0.05, 0.36, 1);
  /* Travel distance for reveal/entrance transforms. */
  --rise: 16px;

  --color-text:        var(--ink-900);
  --color-text-muted:  var(--ink-500);
  --color-text-invert: var(--white);
  --color-bg:          var(--white);
  --color-bg-subtle:   var(--surface);
  --color-border:      var(--line);

  /* --- spacing scale (4px rhythm) --- */
  --space-1: 0.25rem;   /*  4px */
  --space-2: 0.5rem;    /*  8px */
  --space-3: 0.75rem;   /* 12px */
  --space-4: 1rem;      /* 16px */
  --space-5: 1.5rem;    /* 24px */
  --space-6: 2rem;      /* 32px */
  --space-7: 3rem;      /* 48px */
  --space-8: 4rem;      /* 64px */
  --space-9: 6rem;      /* 96px */

  /* --- type scale --- */
  --font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
               Arial, "Noto Sans", sans-serif;
  --text-xs:   0.75rem;   /* 12px */
  --text-sm:   0.875rem;  /* 14px */
  --text-base: 1rem;      /* 16px */
  --text-md:   1.125rem;  /* 18px */
  --text-lg:   1.25rem;   /* 20px */
  --text-xl:   1.5rem;    /* 24px */
  --text-2xl:  2rem;      /* 32px */
  --text-3xl:  2.5rem;    /* 40px */

  --leading-tight: 1.2;
  --leading-snug:  1.35;
  --leading-body:  1.65;

  /* --- radii, shadow, layout --- */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-pill: 999px;

  --shadow-sm: 0 1px 2px rgba(10, 27, 61, 0.06),
               0 1px 3px rgba(10, 27, 61, 0.08);
  --shadow-md: 0 2px 4px rgba(10, 27, 61, 0.06),
               0 8px 20px rgba(10, 27, 61, 0.10);

  --container: 1200px;
  --container-pad: var(--space-4);

  /* --- z-index scale --- */
  --z-base: 0;
  --z-sticky: 100;
  --z-nav: 200;
  --z-skip: 1000;
}

@media (min-width: 768px) {
  :root {
    --container-pad: var(--space-6);
    --text-3xl: 3rem;   /* 48px */
  }
}

/* --------------------------------------------------------------------------
   2. Reset
   -------------------------------------------------------------------------- */

*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body,
h1, h2, h3, h4, p,
figure, blockquote,
dl, dd, ul, ol {
  margin: 0;
  padding: 0;
}

ul[class],
ol[class] { list-style: none; }

img,
picture,
svg {
  max-width: 100%;
  display: block;
}

input,
button,
select,
textarea { font: inherit; color: inherit; }

button { cursor: pointer; }

table {
  border-collapse: collapse;
  width: 100%;
}

address { font-style: normal; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  /* Collapsing durations is not enough on its own: anything whose resting
     state is opacity:0 waiting on a transition would stay invisible. Every
     motion pattern below is force-resolved to its final state here, and
     motion.js additionally skips the observer entirely. */
  .reveal,
  .reveal-stagger > *,
  .hero__content > *,
  .hero__rates {
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
  }
}

/* --------------------------------------------------------------------------
   3. Typography
   -------------------------------------------------------------------------- */

body {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading-body);
  color: var(--color-text);
  background-color: var(--color-bg);
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  /* Safety net against a sideways scroll on phones. `clip`, not `hidden`:
     hidden would turn <body> into a scroll container and break the sticky
     header. Nothing on the site is expected to reach this — it is here so a
     single long word or wide row can never drag the whole page sideways. */
  overflow-x: clip;
}

main { flex: 1 0 auto; }

h1, h2, h3, h4 {
  color: var(--color-primary-strong);
  line-height: var(--leading-tight);
  font-weight: 700;
  text-wrap: balance;
}

h1 { font-size: var(--text-3xl); letter-spacing: -0.02em; }
h2 { font-size: var(--text-2xl); letter-spacing: -0.01em; }
h3 { font-size: var(--text-lg); }
h4 { font-size: var(--text-base); }

p { text-wrap: pretty; }

a { color: var(--color-primary); }
a:hover { color: var(--color-primary-hover); }

strong { font-weight: 700; }

/* --------------------------------------------------------------------------
   4. Focus & accessibility helpers
   -------------------------------------------------------------------------- */

:where(a, button, select, input, textarea, [tabindex]):focus-visible {
  outline: 3px solid var(--color-focus);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* Dark bands: swap the ring to the brand yellow, which reads clearly on blue
   (7.38:1) where the blue ring would vanish into the background. */
.site-header,
.site-footer,
.hero,
.trust,
.cta {
  --color-focus: var(--yellow-500);
}

.skip-link {
  position: absolute;
  left: var(--space-4);
  top: var(--space-4);
  z-index: var(--z-skip);
  padding: var(--space-3) var(--space-4);
  background: var(--color-accent);
  color: var(--color-primary-deep);
  font-weight: 700;
  border-radius: var(--radius-md);
  transform: translateY(-200%);
  transition: transform 150ms ease-out;
}

.skip-link:focus {
  transform: translateY(0);
  color: var(--color-primary-deep);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   5. Layout primitives
   -------------------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}

.page-head {
  background: var(--color-primary-tint);
  border-bottom: 1px solid var(--color-border);
  padding-block: var(--space-6) var(--space-6);
}

.page-head__title { margin-bottom: var(--space-3); }

.page-head__lead {
  color: var(--color-text-muted);
  font-size: var(--text-md);
  max-width: 60ch;
}

.section-head {
  max-width: 60ch;
  margin-bottom: var(--space-6);
}

.section-head__title { margin-bottom: var(--space-3); }

.section-head__lead {
  color: var(--color-text-muted);
  font-size: var(--text-md);
}

@media (min-width: 768px) {
  .page-head { padding-block: var(--space-8); }
  .section-head { margin-bottom: var(--space-7); }
}

/* --------------------------------------------------------------------------
   6. Motion primitives
   --------------------------------------------------------------------------
   Two patterns, both driven by js/motion.js:

     .reveal          one element fades and rises into place
     .reveal-stagger  a container whose direct children do the same, offset

   Both are gated behind `.js` on <html>, so with JavaScript disabled the
   content is simply visible — nothing is hidden behind a script that may
   never run. The `.is-ready` gate exists so that content already inside the
   viewport on first paint appears instantly rather than animating: motion.js
   marks those visible BEFORE adding .is-ready, and the transition only exists
   once .is-ready is present. That keeps the rate figures static on arrival.
   -------------------------------------------------------------------------- */

.js .reveal {
  opacity: 0;
  transform: translate3d(0, var(--rise), 0);
  will-change: opacity, transform;
}

.js .reveal.is-ready {
  transition: opacity var(--dur-reveal) var(--ease-out),
              transform var(--dur-reveal) var(--ease-out);
}

.js .reveal.is-visible {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* Dropped once the element has arrived, so the compositor is not asked to
   hold a layer for every section on the page for the rest of the session. */
.js .reveal.is-settled { will-change: auto; }

.js .reveal-stagger > * {
  opacity: 0;
  transform: translate3d(0, var(--rise), 0);
}

.js .reveal-stagger.is-ready > * {
  transition: opacity var(--dur-reveal) var(--ease-out),
              transform var(--dur-reveal) var(--ease-out);
}

.js .reveal-stagger.is-visible > * {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* 70ms apart: enough to read as a sequence, short enough that the last tile
   in a six-up grid still lands within ~900ms of the first. */
.js .reveal-stagger.is-visible > *:nth-child(1) { transition-delay: 0ms; }
.js .reveal-stagger.is-visible > *:nth-child(2) { transition-delay: 70ms; }
.js .reveal-stagger.is-visible > *:nth-child(3) { transition-delay: 140ms; }
.js .reveal-stagger.is-visible > *:nth-child(4) { transition-delay: 210ms; }
.js .reveal-stagger.is-visible > *:nth-child(5) { transition-delay: 280ms; }
.js .reveal-stagger.is-visible > *:nth-child(6) { transition-delay: 350ms; }
.js .reveal-stagger.is-visible > *:nth-child(n + 7) { transition-delay: 420ms; }

/* --------------------------------------------------------------------------
   7. Page entrance
   --------------------------------------------------------------------------
   Deliberately applied to <main> and the footer rather than <body>: the header
   must not fade, both because the nav should feel immediately anchored and
   because an opacity animation on an ancestor of the sticky header would give
   it a new containing block. Short and opacity-only — this is here to take the
   hard edge off navigation, not to be noticed.
   -------------------------------------------------------------------------- */

@keyframes page-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.js main,
.js .site-footer {
  animation: page-in 220ms var(--ease-out) both;
}
