/**
 * Canadian Alumni landing page — complete stylesheet.
 *
 * Delivered as one file, in four parts:
 *
 *   1. Reset      Baseline normalisation. Declares no tokens, reads none.
 *   2. Tokens     Raw scales and the semantic aliases components read.
 *   3. Base       Global element rules. Webfaces, links, focus, icons.
 *   4. Components Sections and components, mobile-first, document order.
 *
 * Breakpoints are 30rem, 48rem, 64rem and 75rem. Component rules never read
 * a colour literal — every value comes from part 2, so the contrast mode can
 * move all of them in one place.
 */

/* ══════════════════════════════════════════════════════════════
   1. RESET
   ══════════════════════════════════════════════════════════════ */

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

ul,
ol {
  list-style: none;
}

blockquote,
q {
  quotes: none;
}

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

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

button {
  background: none;
  cursor: pointer;
}

/**
 * Content that should be announced but not seen. Clipped rather than hidden,
 * because a removed subtree breaks <use> references pointing into it.
 */
.visually-hidden {
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  position: absolute;
  white-space: nowrap;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
}

/**
 * The universal selector is deliberate here: the preference has to reach
 * every element to be respected reliably, and accessibility outranks the
 * general preference against universal selectors.
 */
@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;
  }
}

/* ══════════════════════════════════════════════════════════════
   2. TOKENS
   ══════════════════════════════════════════════════════════════ */

:root {

  /* ── Raw scales ─────────────────────────────────────────── */

  --color-white: #ffffff;
  --color-gray-05: #f8f8f8;
  --color-gray-15: #e2e2e2;
  --color-gray-60: #6e6e6e;
  --color-gray-75: #484848;
  --color-gray-95: #0d0c0c;

  --color-red-35: #ff4d57;
  --color-red-55: #ed1c27;
  --color-red-65: #a52227;
  --color-red-80: #7d1a1e;

  --color-white-alpha-08: rgba(255, 255, 255, 0.08);
  --color-white-alpha-10: rgba(255, 255, 255, 0.1);
  --color-white-alpha-18: rgba(255, 255, 255, 0.18);
  --color-white-alpha-48: rgba(255, 255, 255, 0.48);
  --color-white-alpha-52: rgba(255, 255, 255, 0.52);
  --color-white-alpha-58: rgba(255, 255, 255, 0.58);
  --color-white-alpha-65: rgba(255, 255, 255, 0.65);
  --color-white-alpha-72: rgba(255, 255, 255, 0.72);
  --color-white-alpha-85: rgba(255, 255, 255, 0.85);
  --color-white-alpha-90: rgba(255, 255, 255, 0.9);
  --color-white-alpha-96: rgba(255, 255, 255, 0.96);

  --color-black-alpha-08: rgba(0, 0, 0, 0.08);
  --color-black-alpha-16: rgba(0, 0, 0, 0.16);

  --color-gray-95-alpha-80: rgba(13, 12, 12, 0.8);
  --color-gray-95-alpha-84: rgba(13, 12, 12, 0.84);
  --color-gray-95-alpha-88: rgba(13, 12, 12, 0.88);

  --color-red-alpha-06: rgba(237, 28, 39, 0.06);
  --color-red-alpha-08: rgba(237, 28, 39, 0.08);
  --color-red-alpha-10: rgba(237, 28, 39, 0.1);
  --color-red-65-alpha-30: rgba(165, 34, 39, 0.3);

  /* ── Semantic colour ───────────────────────────────────── */

  --color-text: var(--color-gray-95);
  --color-text-secondary: var(--color-gray-75);
  --color-text-footnote: var(--color-gray-60);
  --color-text-inverse: var(--color-white);

  /**
   * Four accents, because one red cannot carry every surface.
   *
   *   accent             Decorative fills only — rules, glows, ghost words,
   *                      the scroll runner. Never carries text.
   *   accent-text        Red text and button surfaces on light. 7.4:1 with
   *                      white on it, 7.4:1 as text on white.
   *   accent-text-strong Button hover, one step darker again.
   *   accent-on-inverse  Red on the always-dark panels, where the bright
   *                      brand red is the legible one (4.4:1 on #0d0c0c).
   *   accent-on-media    Red over the hero video, which is a light-ground
   *                      animation — the scrim floors it dark, so the red
   *                      has to move lighter to clear 3:1.
   */
  --color-accent: var(--color-red-55);
  --color-accent-text: var(--color-red-65);
  --color-accent-text-strong: var(--color-red-80);
  --color-accent-on-inverse: var(--color-red-55);
  --color-accent-on-media: var(--color-red-35);

  /**
   * Button and skip-link fills. Split out of accent-text, which was doing
   * two jobs: red *text* on the page surface, and a red *surface* with
   * white text on it.
   *
   * On a light page one value satisfies both — #a52227 is 7.4:1 as text
   * on white and 7.4:1 with white on it — so the conflation cost nothing
   * and was invisible. It stops being free the moment the page surface
   * can be dark: text has to move lighter to stay legible, a fill has to
   * stay dark enough to keep white text on it, and those are opposite
   * directions. Two tokens, so a theme can move them apart.
   *
   * Identical values to before on light, so this is a no-op here.
   */
  --color-accent-fill: var(--color-red-65);
  --color-accent-fill-hover: var(--color-red-80);

  --color-accent-glow: var(--color-red-alpha-06);
  --color-accent-glow-strong: var(--color-red-alpha-10);
  --color-accent-line: var(--color-red-alpha-08);

  --color-surface-page: var(--color-white);
  --color-surface-sunken: var(--color-gray-05);
  --color-surface-inverse: var(--color-gray-95);
  --color-surface-header: var(--color-white-alpha-96);

  --color-border: var(--color-gray-15);
  --color-border-header: var(--color-black-alpha-16);
  --color-border-on-inverse: var(--color-white-alpha-10);
  --color-border-on-inverse-subtle: var(--color-white-alpha-08);

  /**
   * Text on the always-dark panels. These are alphas of white rather than
   * solid ink, which team-accessibility §5 asks us not to do — kept to
   * preserve the original rendering, and each measured against #0d0c0c:
   * muted 7.3:1, subtle 6.8:1, secondary 5.7:1, faint 4.8:1.
   */
  --color-text-on-inverse: var(--color-white);
  --color-text-on-inverse-muted: var(--color-white-alpha-72);
  --color-text-on-inverse-subtle: var(--color-white-alpha-58);
  --color-text-on-inverse-secondary: var(--color-white-alpha-52);
  --color-text-on-inverse-faint: var(--color-white-alpha-48);
  --color-link-on-inverse: var(--color-white-alpha-65);
  --color-link-on-inverse-hover: var(--color-white-alpha-90);

  --color-text-on-media: var(--color-white);
  --color-text-on-media-subtle: var(--color-white-alpha-85);
  --color-track-on-media: var(--color-white-alpha-18);

  /**
   * The hero footage is a logo animation on a white ground, so the scrim's
   * worst case is also its normal case. The title sits over the middle stop,
   * where the composite over white is rgb(61, 61, 61): white text measures
   * 10.9:1 there and the lightened accent 3.35:1. Anything below alpha 0.78
   * drops the accent under the 3:1 large-text threshold.
   */
  --color-scrim: var(--color-gray-95-alpha-80);
  --color-scrim-strong: var(--color-gray-95-alpha-84);
  --color-scrim-strongest: var(--color-gray-95-alpha-88);

  --color-focus-ring: var(--color-accent);

  /* ── Type ───────────────────────────────────────────────── */

  --font-display-family: "DM Serif Display", "DM Serif Fallback", Georgia, serif;
  --font-body-family: "DM Sans", "DM Sans Fallback", system-ui, sans-serif;

  --font-h1-size-min: 2.75rem;
  --font-h1-size-fluid: 6.2vw;
  --font-h1-size-max: 5.75rem;

  --font-display-size-min: 3rem;
  --font-display-size-fluid: 7vw;
  --font-display-size-max: 6rem;

  --font-h2-size-min: 2.125rem;
  --font-h2-size-fluid: 4.2vw;
  --font-h2-size-max: 3.75rem;

  --font-h2-wide-size-min: 2.25rem;
  --font-h2-wide-size-fluid: 4.8vw;
  --font-h2-wide-size-max: 4.125rem;

  --font-lead-size-min: 1.0625rem;
  --font-lead-size-fluid: 1.9vw;
  --font-lead-size-max: 1.3125rem;

  --font-body-size-min: 1rem;
  --font-body-size-fluid: 1.35vw;
  --font-body-size-max: 1.0625rem;

  /**
   * Type floor. Nothing on the page is set below --font-size-min, and only
   * eyebrows and the scroll label sit on it. Supporting copy uses
   * --font-size-small; running prose starts at --font-body-size-min (1rem).
   */
  --font-size-min: 0.8125rem;
  --font-size-small: 0.875rem;
  --font-eyebrow-size: var(--font-size-min);

  /* ── Space ──────────────────────────────────────────────── */

  --space-xxx-small: 0.1875rem;
  --space-xx-small: 0.5rem;
  --space-x-small: 0.75rem;
  --space-small: 1rem;
  --space-medium: 1.25rem;
  --space-large: 1.5rem;
  --space-x-large: 2rem;
  --space-xx-large: 2.75rem;

  /* Page gutter and section rhythm. Both widen with the viewport. */
  --space-gutter: 1rem;
  --space-section-y: clamp(5.5rem, 11vw, 10rem);

  /* ── Size ───────────────────────────────────────────────── */

  --size-content-max: 77.5rem;
  --size-header-height: 4.5rem;
  --size-hit-target: 2.75rem;
  --size-hit-target-dense: 2.25rem;

  /* In em so the gap tracks the type size it sits under */
  --size-underline-offset: 0.22em;

  /**
   * Narrowest layout the page is designed to hold. Below this the body keeps
   * its width and the viewport scrolls, rather than every column collapsing.
   * Deliberately in px: a rem value would grow with the user's font setting
   * and introduce horizontal scrolling at a 320px viewport, which WCAG
   * 1.4.10 Reflow forbids. 320px is also the width reflow is measured at.
   */
  --size-page-min: 320px;

  /* ── Border ─────────────────────────────────────────────── */

  --border-width: 1px;
  --border-width-accent: 1.5px;
  --border-width-strong: 2px;
  --border-width-accent-strong: 3px;
  --link-decoration: none;

  /* ── Shadow ─────────────────────────────────────────────── */

  --shadow-button-hover: 0 0.5rem 1.75rem var(--color-red-65-alpha-30);
  --shadow-menu: 0 0.75rem 2rem var(--color-black-alpha-08);

  /**
   * Easing. There is no easing category in the team token scheme, so this
   * follows the closest documented shape — see README, Convention gaps.
   */
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);

  /* ── Breakpoints ────────────────────────────────────────── */

  /**
   * Custom properties cannot be used inside a media query condition, so
   * these exist to document the scale in one place; each query below
   * repeats the literal value.
   */
  --bp-x-small: 30rem;
  --bp-medium: 48rem;
  --bp-large: 64rem;
  --bp-x-large: 75rem;

}

@media (min-width: 30rem) {
  :root {
    --space-gutter: 1.25rem;
  }
}

@media (min-width: 48rem) {
  :root {
    --space-gutter: clamp(1.5rem, 5vw, 4.5rem);
  }
}

/**
 * High contrast is a token layer, not a restyle, and it is scheme-agnostic.
 * It deliberately does not change text size: that is a separate axis with
 * its own decision.
 */
@media (prefers-contrast: more) {
  :root {
    --color-text-secondary: var(--color-text);
    --color-text-footnote: var(--color-text);
    --color-border: var(--color-text);
    --color-border-header: var(--color-text);
    --color-border-on-inverse: var(--color-text-inverse);
    --color-border-on-inverse-subtle: var(--color-text-inverse);
    --color-text-on-inverse-muted: var(--color-text-inverse);
    --color-text-on-inverse-subtle: var(--color-text-inverse);
    --color-text-on-inverse-secondary: var(--color-text-inverse);
    --color-text-on-inverse-faint: var(--color-text-inverse);
    --color-link-on-inverse: var(--color-text-inverse);
    --color-text-on-media-subtle: var(--color-text-inverse);
    --border-width: 2px;
    --border-width-accent: 3px;
    --border-width-accent-strong: 4px;
    --link-decoration: underline;
    --size-hit-target: 3rem;
    --size-hit-target-dense: 3rem;
  }
}

/**
 * The same layer as a class pair, which is what lets a choice overrule the
 * system setting — `team-accessibility` §8(b): a media query has no "off",
 * so `contrast-standard` is mandatory rather than decoration. No class
 * means follow the OS. The review harness in tools/ sets these on the page
 * it frames, so the mode can be checked without changing an OS setting.
 *
 * There is no `ui-dark`/`ui-light` pair to match: the page is light-only
 * by decision, so `prefers-color-scheme` is not read at all.
 */
.contrast-high {
  --color-text-secondary: var(--color-text);
  --color-text-footnote: var(--color-text);
  --color-border: var(--color-text);
  --color-border-header: var(--color-text);
  --color-border-on-inverse: var(--color-text-inverse);
  --color-border-on-inverse-subtle: var(--color-text-inverse);
  --color-text-on-inverse-muted: var(--color-text-inverse);
  --color-text-on-inverse-subtle: var(--color-text-inverse);
  --color-text-on-inverse-secondary: var(--color-text-inverse);
  --color-text-on-inverse-faint: var(--color-text-inverse);
  --color-link-on-inverse: var(--color-text-inverse);
  --color-text-on-media-subtle: var(--color-text-inverse);
  --border-width: 2px;
  --border-width-accent: 3px;
  --border-width-accent-strong: 4px;
  --link-decoration: underline;
  --size-hit-target: 3rem;
  --size-hit-target-dense: 3rem;
}

/* The escape: the standard values again, beating the media query above */
.contrast-standard {
  --color-text-secondary: var(--color-gray-75);
  --color-text-footnote: var(--color-gray-60);
  --color-border: var(--color-gray-15);
  --color-border-header: var(--color-black-alpha-16);
  --color-border-on-inverse: var(--color-white-alpha-10);
  --color-border-on-inverse-subtle: var(--color-white-alpha-08);
  --color-text-on-inverse-muted: var(--color-white-alpha-72);
  --color-text-on-inverse-subtle: var(--color-white-alpha-58);
  --color-text-on-inverse-secondary: var(--color-white-alpha-52);
  --color-text-on-inverse-faint: var(--color-white-alpha-48);
  --color-link-on-inverse: var(--color-white-alpha-65);
  --color-text-on-media-subtle: var(--color-white-alpha-85);
  --border-width: 1px;
  --border-width-accent: 1.5px;
  --border-width-accent-strong: 3px;
  --link-decoration: none;
  --size-hit-target: 2.75rem;
  --size-hit-target-dense: 2.25rem;
}

/* ══════════════════════════════════════════════════════════════
   3. BASE
   ══════════════════════════════════════════════════════════════ */

/**
 * Self-hosted faces. The project binary comes first and local() last,
 * because the browser stops at the first source that resolves and our file
 * is the one that was tested. DM Sans is a variable face, so it carries a
 * weight range; DM Serif Display ships as two static cuts.
 */
@font-face {
  font-family: "DM Sans";
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src:
    url("../fonts/dm-sans-variable.woff2") format("woff2"),
    local("DM Sans");
}

/**
 * Metric-matched stand-ins, so the swap from fallback to webfont changes
 * the letterforms without moving anything. Each figure was measured from
 * the real faces rather than estimated: DM Sans sets 94.37% the width of
 * Arial over the same string, DM Serif Display 98.80% of Georgia, and the
 * ascent and descent overrides are each the real face's value divided by
 * that ratio. Without these, font-display: swap reflows every heading and
 * paragraph the moment the webfont lands.
 */
@font-face {
  font-family: "DM Sans Fallback";
  src: local("Arial"), local("Helvetica Neue"), local("Liberation Sans");
  size-adjust: 94.37%;
  ascent-override: 104.91%;
  descent-override: 32.85%;
  line-gap-override: 0%;
}

@font-face {
  font-family: "DM Serif Fallback";
  src: local("Georgia"), local("Times New Roman"), local("Liberation Serif");
  size-adjust: 98.8%;
  ascent-override: 105.26%;
  descent-override: 34.41%;
  line-gap-override: 0%;
}

@font-face {
  font-family: "DM Serif Display";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src:
    url("../fonts/dm-serif-display-regular.woff2") format("woff2"),
    local("DM Serif Display");
}

@font-face {
  font-family: "DM Serif Display";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src:
    url("../fonts/dm-serif-display-italic.woff2") format("woff2"),
    local("DM Serif Display Italic");
}

html {
  scroll-behavior: smooth;
  /* Keeps an anchored section clear of the fixed header */
  scroll-padding-top: calc(var(--size-header-height) + var(--space-small));
  -webkit-font-smoothing: antialiased;
}

body {
  min-width: var(--size-page-min);
  font-family: var(--font-body-family);
  color: var(--color-text);
  background-color: var(--color-surface-page);
}

/**
 * A <small> used as a real element rather than as size styling inherits its
 * size; the UA default drops it below the type floor.
 */
small {
  font-size: inherit;
}

a {
  color: inherit;
  text-decoration: var(--link-decoration);
}

a:hover {
  color: var(--color-accent-text);
}

/**
 * The focus ring is specified once, globally, and drawn with outline so it
 * never affects layout. :focus-visible needs a newer browser than the
 * medium-term tier guarantees, so :focus carries the rest and is suppressed
 * again wherever :focus-visible is understood.
 */
:focus {
  outline: 3px solid var(--color-focus-ring);
  outline-offset: 3px;
}

:focus:not(:focus-visible) {
  outline: none;
}

:focus-visible {
  outline: 3px solid var(--color-focus-ring);
  outline-offset: 3px;
}

/**
 * Drops onto the page gutter and stands as tall as the header row, so it
 * lands over the logo rather than beside it. Sits above the header, which
 * is the only other stacked layer at the top.
 */
.skip {
  display: inline-flex;
  align-items: center;
  min-height: var(--size-header-height);
  padding: 0 var(--space-medium);
  position: absolute;
  top: 0;
  left: var(--space-gutter);
  z-index: 400;
  font-size: var(--font-size-small);
  font-weight: 600;
  color: var(--color-text-inverse);
  background-color: var(--color-accent-fill);
  border-radius: 0 0 0.25rem 0.25rem;
  transform: translateY(-100%);
  transition: transform 200ms linear;
}

.skip:focus {
  color: var(--color-text-inverse);
  transform: translateY(0);
}

/**
 * UI icons. Size and colour come from CSS: icons inherit currentColor, and
 * the size scale keeps them consistent. Symbols are inlined at the top of
 * the document from images/icons.svg.
 */
.icon {
  width: 1em;
  height: 1em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.icon-large {
  font-size: 1.125rem;
}

.icon-x-large {
  font-size: 1.5rem;
}

/**
 * Some visitors find translucency and blur disorienting, and it is a
 * documented preference rather than a taste. The header is the only
 * element on the page that uses either.
 */
@media (prefers-reduced-transparency: reduce) {

  .header {
    background-color: var(--color-surface-page);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

}

/**
 * Windows High Contrast and other forced-colour modes replace every colour
 * with a user-chosen palette and drop most backgrounds. Three things here
 * need help: the focus ring loses its accent colour, the video control
 * loses the scrim it sits on, and the marquee's fade masks become opaque
 * blocks. The masked footer logo cannot be rescued from CSS — it needs an
 * SVG of the mark. See README, Accessibility findings.
 */
@media (forced-colors: active) {

  :focus,
  :focus-visible {
    outline-color: Highlight;
  }

  .hero-video-toggle {
    background-color: Canvas;
    border-color: ButtonText;
  }

  /* Its own background is dropped, so it needs an edge to stay readable */
  .tooltip {
    background-color: Canvas;
    border: var(--border-width) solid CanvasText;
  }

  .marquee::before,
  .marquee::after {
    display: none;
  }

}

/* ══════════════════════════════════════════════════════════════
   4. COMPONENTS
   ══════════════════════════════════════════════════════════════ */

/* ── Layout primitives ─────────────────────────────────────── */

/**
 * The skip target carries tabindex="-1" so the link actually moves focus.
 * Without it the browser scrolls to the landmark but leaves focus on the
 * link, and the next Tab goes back into the header — the classic silent
 * skip-link failure. No ring: this is a whole region rather than a
 * control, and an outline around the page reads as a fault. The same
 * applies to the footnotes, which are jump targets for the reference
 * marks.
 */
.main:focus,
.different-footnote:focus {
  outline: none;
}

.container {
  max-width: var(--size-content-max);
  margin: 0 auto;
  padding: 0 var(--space-gutter);
}

.section {
  padding: var(--space-section-y) 0;
}

/* ── Type roles ────────────────────────────────────────────── */

.heading-display {
  margin-bottom: var(--space-x-large);
  font-family: var(--font-display-family);
  font-size: clamp(
    var(--font-display-size-min),
    var(--font-display-size-fluid),
    var(--font-display-size-max)
  );
  font-weight: 400;
  line-height: 1;
  color: var(--color-text);
}

.heading-section {
  margin-bottom: 1.75rem;
  font-family: var(--font-display-family);
  font-size: clamp(
    var(--font-h2-size-min),
    var(--font-h2-size-fluid),
    var(--font-h2-size-max)
  );
  font-weight: 400;
  line-height: 1.08;
  color: var(--color-text);
}

.text-body {
  font-size: clamp(
    var(--font-body-size-min),
    var(--font-body-size-fluid),
    var(--font-body-size-max)
  );
  line-height: 1.82;
  color: var(--color-text-secondary);
}

/* ── Clip reveal wrapper ───────────────────────────────────── */

/* The inner element is animated up from translate: 0 115% by the motion layer */
.clip {
  display: block;
  padding-bottom: 0.05em;
  overflow: hidden;
}

.clip-inner {
  display: block;
}

/* ── Decorative ghost words and rings ──────────────────────── */

.ghost {
  position: absolute;
  right: -0.375rem;
  bottom: -0.5rem;
  font-family: var(--font-display-family);
  font-size: clamp(3.25rem, 16vw, 4.5rem);
  line-height: 1;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--color-accent);
  opacity: 0.04;
  pointer-events: none;
  user-select: none;
  will-change: transform;
}

.ring {
  position: absolute;
  border: var(--border-width-accent) solid var(--color-accent-line);
  border-radius: 50%;
  pointer-events: none;
  will-change: transform;
}

.ring-cta {
  width: 27.5rem;
  height: 27.5rem;
  left: -5rem;
  bottom: -8.75rem;
}

/* ── Buttons ───────────────────────────────────────────────── */

/**
 * Every button carries the hover line; only some carry the outer glow, so
 * the glow is a slot the variants fill. Both states declare the same three
 * shadows at the same spreads and differ only in colour, so the line fades
 * in where it sits instead of growing inward from the edge. Inset shadows
 * follow border-radius, so the line is rounded with the button.
 */
.button {
  --shadow-button-glow: 0 0 0 0 transparent;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--size-hit-target);
  font-weight: 600;
  color: var(--color-text-inverse);
  background-color: var(--color-accent-fill);
  border-radius: 0.25rem;
  box-shadow:
    inset 0 0 0 var(--space-xxx-small) transparent,
    inset 0 0 0 calc(var(--space-xxx-small) + var(--border-width)) transparent,
    var(--shadow-button-glow);
  transition:
    background-color 250ms linear,
    box-shadow 250ms linear;
}

/**
 * Restated so the global a:hover accent colour cannot win here. The first
 * ring masks the outer edge in the fill colour and the white ring beneath
 * shows only where that mask stops — an edge line is lost against a white
 * page, so it needs the fill on both sides of it to read.
 */
.button:hover {
  color: var(--color-text-inverse);
  background-color: var(--color-accent-fill-hover);
  box-shadow:
    inset 0 0 0 var(--space-xxx-small) var(--color-accent-fill-hover),
    inset 0 0 0 calc(var(--space-xxx-small) + var(--border-width))
      var(--color-text-inverse),
    var(--shadow-button-glow);
}

/* Untransitioned: a press should register at once, not ease */
.button:active {
  transform: translateY(1px);
}

.button-primary {
  padding: var(--space-small) var(--space-xx-large);
  font-size: 0.9375rem;
  letter-spacing: 0.04em;
}

/* Fills the glow slot only while hovered, so it fades in with the line */
.button-primary:hover {
  --shadow-button-glow: var(--shadow-button-hover);
}

/* ── Text links ────────────────────────────────────────────── */

/**
 * Every plain text link underlines on hover as well as shifting colour.
 * Colour alone is the weakest possible affordance: it is easy to miss, it
 * is invisible to anyone who cannot separate the two hues, and it vanishes
 * entirely in forced-colours mode, where the palette is replaced. The
 * underline survives all three.
 *
 * Grouped rather than repeated on each component, because the treatment is
 * one decision and should change in one place. The links that already carry
 * a rule at rest — the email address, the buttons, the header controls —
 * are deliberately not in this list.
 */
.primary-nav-link:hover,
.menu-link:hover,
.menu-language:hover,
.footer-link:hover {
  text-decoration: underline;
  text-decoration-thickness: var(--border-width-accent);
  text-underline-offset: var(--size-underline-offset);
}

/* ── Header ────────────────────────────────────────────────── */

/**
 * Stacked above the hero video and the decorative layers beneath it, which
 * is the only reason this element carries a z-index at all.
 */
.header {
  height: var(--size-header-height);
  position: fixed;
  top: 0;
  right: 0;
  left: 0;
  z-index: 300;
  background-color: var(--color-surface-header);
  border-bottom: var(--border-width) solid var(--color-border-header);
  backdrop-filter: blur(1rem);
  -webkit-backdrop-filter: blur(1rem);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-xx-small);
  max-width: var(--size-content-max);
  height: 100%;
  margin: 0 auto;
  padding: 0 var(--space-gutter);
}

.header-logo-wrap {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  min-height: var(--size-hit-target);
  border-radius: 0.25rem;
  text-decoration: var(--link-decoration);
  transition: opacity 250ms linear;
}

.header-logo-wrap:hover {
  opacity: 0.7;
}

/* Untransitioned, matching the header's other controls */
.header-logo-wrap:active {
  transform: translateY(1px);
  transition: none;
}

/* Never shrinks: the mark is sized by height and has a fixed aspect ratio */
.header-logo {
  width: auto;
  height: 1.625rem;
  max-width: none;
}

.header-actions {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
  gap: var(--space-xx-small);
}

/* Moves into the menu panel below 30rem, where the row runs out of width */
.header-cta {
  display: none;
  padding: 0 var(--space-small);
  font-size: var(--font-size-small);
  letter-spacing: 0.03em;
  white-space: nowrap;
}

/* Inline row of section links. Replaced by the menu below 75rem. */
.primary-nav {
  display: none;
}

.primary-nav-list {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-small);
}

.primary-nav-link {
  display: flex;
  align-items: center;
  min-height: var(--size-hit-target-dense);
  font-size: var(--font-size-small);
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--color-text-secondary);
  text-align: center;
  text-decoration: var(--link-decoration);
  transition: color 250ms linear;
}

.primary-nav-link:hover {
  color: var(--color-accent-text);
}

/**
 * Shared box for the two icon-sized header controls, so the language badge
 * and the menu toggle read as a pair.
 */
.header-control {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--size-hit-target);
  min-height: var(--size-hit-target);
  position: relative;
  color: var(--color-text-secondary);
  background-color: transparent;
  border: var(--border-width) solid var(--color-border);
  border-radius: 0.25rem;
  text-decoration: var(--link-decoration);
  transition:
    color 250ms linear,
    border-color 250ms linear;
}

.header-control:hover {
  color: var(--color-accent-text);
  border-color: var(--color-accent-text);
}

/* Untransitioned: a press should register at once, not ease */
.header-control:active {
  transform: translateY(1px);
}

/**
 * Tooltip naming a control's destination. Purely an affordance — the same
 * words are part of the control's accessible name, so it is faded rather
 * than hidden and stays in the accessibility tree. The page script nudges
 * --tooltip-shift when a centred tooltip would run past the viewport.
 */
.tooltip {
  padding: var(--space-xx-small) var(--space-x-small);
  position: absolute;
  top: calc(100% + var(--space-xx-small));
  left: 50%;
  z-index: 1;
  font-size: var(--font-size-min);
  font-weight: 500;
  line-height: 1.3;
  letter-spacing: 0;
  white-space: nowrap;
  color: var(--color-text-inverse);
  background-color: var(--color-surface-inverse);
  border-radius: 0.25rem;
  box-shadow: var(--shadow-menu);
  opacity: 0;
  pointer-events: none;
  transform: translateX(calc(-50% + var(--tooltip-shift, 0px)));
  transition: opacity 200ms linear;
}

.header-control:hover .tooltip,
.header-control:focus-visible .tooltip {
  opacity: 1;
}

/**
 * Language toggle. First class at every width: the compact FR badge sits in
 * the header between the call to action and the menu, and the menu carries
 * the same destination spelled out.
 */
.language-switch {
  padding: 0 var(--space-xx-small);
  font-size: var(--font-size-small);
  font-weight: 700;
  letter-spacing: 0.08em;
}

/* ── Menu (disclosure) ─────────────────────────────────────── */

/**
 * T1: a native <details> disclosure, so the section links are reachable with
 * no JavaScript at any width. T2 adds Escape, click-away and closing on
 * selection — see the page script.
 */
.menu {
  display: block;
}

.menu-toggle {
  cursor: pointer;
  list-style: none;
}

/* Safari draws its own disclosure triangle unless both rules are present */
.menu-toggle::-webkit-details-marker {
  display: none;
}

.menu-toggle::marker {
  content: "";
}

.menu-icon-close {
  display: none;
}

.menu[open] .menu-icon {
  display: none;
}

.menu[open] .menu-icon-close {
  display: block;
}

/**
 * Fixed rather than absolute: the header is fixed too, so the panel hangs
 * from the same origin and can never be wider than the viewport.
 */
.menu-panel {
  max-height: calc(100vh - var(--size-header-height));
  max-height: calc(100dvh - var(--size-header-height));
  padding: var(--space-xx-small) var(--space-gutter) var(--space-large);
  position: fixed;
  top: var(--size-header-height);
  left: 0;
  right: 0;
  background-color: var(--color-surface-page);
  border-bottom: var(--border-width) solid var(--color-border);
  box-shadow: var(--shadow-menu);
  overflow-y: auto;
}

.menu-list {
  display: flex;
  flex-direction: column;
}

.menu-link {
  display: flex;
  align-items: center;
  min-height: var(--size-hit-target);
  padding: var(--space-xx-small) 0;
  font-size: 1rem;
  font-weight: 500;
  color: var(--color-text);
  border-bottom: var(--border-width) solid var(--color-border);
  text-decoration: var(--link-decoration);
  transition: color 250ms linear;
}

.menu-link:hover {
  color: var(--color-accent-text);
}

/* Carries the call to action below 30rem, where the header cannot */
.menu-cta {
  display: flex;
  width: 100%;
  margin-top: var(--space-medium);
}

.menu-language {
  display: flex;
  align-items: center;
  min-height: var(--size-hit-target);
  margin-top: var(--space-x-small);
  font-size: var(--font-size-small);
  font-weight: 600;
  color: var(--color-text-secondary);
  text-decoration: var(--link-decoration);
  transition: color 250ms linear;
}

.menu-language:hover {
  color: var(--color-accent-text);
}

/* ── Hero ──────────────────────────────────────────────────── */

.hero {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  min-height: 100dvh;
  position: relative;
  background-color: var(--color-surface-inverse);
  overflow: hidden;
}

.hero-video {
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  object-fit: cover;
  object-position: center center;
}

/**
 * Sets a luminance floor under the headline, so it holds its contrast
 * ratio against a white-ground animation. The separation blur that used to
 * sit here as backdrop-filter is now baked into the footage, so this layer
 * is a plain gradient and costs nothing per frame.
 */
.hero-overlay {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background: linear-gradient(
    160deg,
    var(--color-scrim-strong) 0%,
    var(--color-scrim) 55%,
    var(--color-scrim-strongest) 100%
  );
}

.hero-content {
  max-width: 53.75rem;
  padding: clamp(6.25rem, 12vw, 10rem) 0 clamp(4rem, 8vw, 6.25rem);
  position: relative;
  z-index: 1;
  text-align: center;
}

.hero-eyebrow {
  display: block;
  margin-bottom: 1.75rem;
  font-size: var(--font-eyebrow-size);
  font-weight: 700;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--color-text-on-media-subtle);
}

.hero-title {
  margin-bottom: var(--space-large);
  font-family: var(--font-display-family);
  font-size: clamp(
    var(--font-h1-size-min),
    var(--font-h1-size-fluid),
    var(--font-h1-size-max)
  );
  font-weight: 400;
  line-height: 1.03;
  color: var(--color-text-inverse);
}
.hero-title:lang(fr) {
  --font-h1-size-max: 5rem;
}

.hero-title-line .clip-inner span {
  display: block;
  color: var(--color-accent-on-media);
}

.hero-subtitle {
  max-width: 31.25rem;
  margin: 0 auto var(--space-medium);
  font-size: clamp(1rem, 1.5vw, 1.1875rem);
  line-height: 1.72;
  color: var(--color-text-on-media);
}

.hero-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-large);
}

.scroll-hint {
  display: flex;
  align-items: center;
  gap: 0.875rem;
}

.scroll-label {
  font-size: var(--font-size-min);
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-text-on-media-subtle);
}

.scroll-track {
  width: 3.25rem;
  height: 1px;
  position: relative;
  background-color: var(--color-track-on-media);
  overflow: hidden;
}

.scroll-runner {
  width: 35%;
  position: absolute;
  top: 0;
  bottom: 0;
  left: -35%;
  background-color: var(--color-accent-on-media);
  animation: scroll-runner 2400ms ease-in-out infinite;
}

@keyframes scroll-runner {
  from {
    left: -35%;
  }

  to {
    left: 110%;
  }
}

/**
 * Pause control for the hero video (WCAG 2.2.2). Sits inside the hero
 * rather than over the headline, so it never overlaps the text, and is
 * always rendered — the video is decorative but the control is not, so it
 * is the one thing in the hero that is never aria-hidden.
 */
.hero-video-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--size-hit-target);
  height: var(--size-hit-target);
  position: absolute;
  right: var(--space-gutter);
  bottom: var(--space-gutter);
  z-index: 2;
  color: var(--color-text-on-media);
  background-color: var(--color-scrim-strongest);
  border: var(--border-width-strong) solid var(--color-text-on-media);
  border-radius: 50%;
  transition:
    border-color 250ms linear,
    border-width 250ms linear;
}

/**
 * The border takes the bright on-media red, not the darker text red: it
 * sits over the hero's own scrim, where the lighter value is the legible
 * one. The fill stays dark so the circle holds its size and weight.
 */
.hero-video-toggle:hover {
  border-color: var(--color-accent-on-media);
  border-width: var(--border-width-accent-strong);
}

/* Transform rather than font-size, so the growth does not relayout */
.hero-video-toggle .icon {
  transition: transform 200ms var(--ease-out-expo);
}

.hero-video-toggle:hover .icon {
  transform: scale(1.25);
}

/* Untransitioned: a press should register at once, not ease */
.hero-video-toggle:active {
  transform: translateY(1px);
}

/* Whichever state the video is not in is the one the button offers */
.hero-video-toggle .icon-play {
  display: none;
}

.hero-video-toggle[data-video-paused] .icon-play {
  display: block;
}

.hero-video-toggle[data-video-paused] .icon-pause {
  display: none;
}

/* ── Purpose ───────────────────────────────────────────────── */

.purpose-section {
  padding-top: clamp(4rem, 9vw, 8rem);
  padding-bottom: clamp(4rem, 8vw, 7.5rem);
  position: relative;
  overflow: hidden;
}

/* Radial accent wash off the top-right corner */
.purpose-section::before {
  content: "";
  width: 43.75rem;
  height: 43.75rem;
  position: absolute;
  top: -6.25rem;
  right: -6.25rem;
  background: radial-gradient(
    circle,
    var(--color-accent-glow) 0%,
    transparent 65%
  );
  pointer-events: none;
}

.purpose-layout {
  max-width: 51.25rem;
  position: relative;
  z-index: 1;
}

.purpose-rule {
  width: 3.25rem;
  height: var(--space-xxx-small);
  margin-bottom: var(--space-x-large);
  background: linear-gradient(
    to right,
    var(--color-accent-text),
    var(--color-accent)
  );
  transform-origin: left;
}

.purpose-lead {
  max-width: 42.5rem;
  font-size: clamp(
    var(--font-lead-size-min),
    var(--font-lead-size-fluid),
    var(--font-lead-size-max)
  );
  line-height: 1.82;
  color: var(--color-text-secondary);
}

/* ── Marquee ───────────────────────────────────────────────── */

.marquee {
  padding: 2.25rem 0;
  position: relative;
  background-color: var(--color-surface-page);
  border-top: var(--border-width) solid var(--color-border);
  border-bottom: var(--border-width) solid var(--color-border);
  overflow: hidden;
}

/* Both fades stack above the scrolling text, hence the z-index */
.marquee::before,
.marquee::after {
  content: "";
  width: 7.5rem;
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 1;
  pointer-events: none;
}

.marquee::before {
  left: 0;
  background: linear-gradient(
    to right,
    var(--color-surface-page),
    transparent
  );
}

.marquee::after {
  right: 0;
  background: linear-gradient(
    to left,
    var(--color-surface-page),
    transparent
  );
}

.marquee-inner {
  display: inline-block;
  white-space: nowrap;
  will-change: transform;
}

.marquee-text {
  display: inline-block;
  font-family: var(--font-display-family);
  font-size: clamp(2rem, 5vw, 4.25rem);
  line-height: 1;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--color-text);
}

/* ── Community ─────────────────────────────────────────────── */

.meaning-section {
  padding-top: clamp(4rem, 9vw, 7.5rem);
  padding-bottom: clamp(4rem, 9vw, 7.5rem);
  position: relative;
  overflow: hidden;
}

.meaning-grid {
  display: grid;
  grid-template-columns: 1fr;
  align-items: center;
  gap: clamp(2.5rem, 5vw, 5rem);
  position: relative;
  z-index: 1;
}

.meaning-visual {
  display: flex;
  align-items: center;
  justify-content: center;
  max-width: 31.25rem;
  position: relative;
  border-radius: 0.5rem;
}

.meaning-visual picture {
  display: block;
  width: 100%;
}

.meaning-image {
  width: 100%;
  height: auto;
  border-radius: 0.5rem;
  transform: scale(1.01);
}

/* ── Our difference ────────────────────────────────────────── */

.different-section {
  padding: var(--space-section-y) 0 clamp(3rem, 6vw, 5rem);
  position: relative;
  background-color: var(--color-surface-page);
  border-top: var(--border-width) solid var(--color-border);
  overflow: hidden;
}

.different-title {
  max-width: 45rem;
  margin-bottom: clamp(1.75rem, 3vw, 2.5rem);
  font-family: var(--font-display-family);
  font-size: clamp(
    var(--font-h2-wide-size-min),
    var(--font-h2-wide-size-fluid),
    var(--font-h2-wide-size-max)
  );
  font-weight: 400;
  line-height: 1.08;
  color: var(--color-text);
}

.different-grid {
  display: grid;
  grid-template-columns: 1fr;
  align-items: start;
  gap: clamp(3rem, 7vw, 7rem);
  position: relative;
  z-index: 1;
}

.different-text {
  font-size: clamp(
    var(--font-body-size-min),
    var(--font-body-size-fluid),
    var(--font-body-size-max)
  );
  line-height: 1.78;
  color: var(--color-text-secondary);
}

.different-stats {
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 7.5rem;
}

.different-stat {
  padding: clamp(1.375rem, 2.6vw, 2.125rem) 0;
  border-top: var(--border-width) solid var(--color-border);
}

.different-stat:last-child {
  border-bottom: var(--border-width) solid var(--color-border);
}

.different-stat-figure {
  margin-bottom: var(--space-x-small);
  font-family: var(--font-display-family);
  font-size: clamp(2.75rem, 5.5vw, 4.75rem);
  font-weight: 400;
  line-height: 1;
  color: var(--color-accent-text);
}

/* Two of the three figures are long enough to need a step down in size */
.different-stat-figure-long {
  font-size: clamp(2rem, 3.8vw, 3.25rem);
}

@media (min-width: 48rem) {
  .different-stat-figure-long {
    white-space: nowrap;
  }
}

.different-stat-text {
  font-size: clamp(0.9375rem, 1.2vw, 1rem);
  line-height: 1.65;
  color: var(--color-text-secondary);
}

.different-footnotes {
  display: flex;
  flex-direction: column;
  gap: var(--space-xx-small);
  margin-top: clamp(1.5rem, 3vw, 2.25rem);
}

.different-footnote {
  font-size: var(--font-size-small);
  line-height: 1.5;
  color: var(--color-text-footnote);
}

.different-footnote sup {
  margin-right: 0.125rem;
  font-size: 0.75em;
  vertical-align: super;
}

/**
 * The reference marks link down to their notes, which is the only way a
 * keyboard or screen-reader user can reach them — a bare "1" announces as
 * a stray digit. Padding rather than height, so the marker keeps its
 * position on the baseline.
 */
.footnote-ref {
  padding: 0 var(--space-xxx-small);
  color: var(--color-accent-text);
  text-decoration: var(--link-decoration);
}

.footnote-ref:hover {
  text-decoration: underline;
  text-underline-offset: var(--size-underline-offset);
}

/* Marks the note that was jumped to, so the target is obvious on arrival */
.different-footnote:target {
  color: var(--color-text);
}

/* ── Built to give back ────────────────────────────────────── */

.give-section {
  padding: var(--space-section-y) 0;
  position: relative;
  background-color: var(--color-surface-sunken);
  border-top: var(--border-width) solid var(--color-border);
  border-bottom: var(--border-width) solid var(--color-border);
}

.give-intro {
  max-width: 45rem;
  margin-bottom: clamp(3.25rem, 7vw, 5rem);
}

.give-title {
  margin-bottom: var(--space-medium);
  font-family: var(--font-display-family);
  font-size: clamp(
    var(--font-h2-wide-size-min),
    var(--font-h2-wide-size-fluid),
    var(--font-h2-wide-size-max)
  );
  font-weight: 400;
  line-height: 1.08;
  color: var(--color-text);
}

.give-rule {
  width: 3.25rem;
  height: var(--space-xxx-small);
  margin-bottom: var(--space-large);
  background: linear-gradient(
    to right,
    var(--color-accent-text),
    var(--color-accent)
  );
  transform-origin: left;
}

.give-sub {
  margin-bottom: var(--space-small);
  font-family: var(--font-display-family);
  font-size: clamp(1.125rem, 1.9vw, 1.5rem);
  font-style: italic;
  font-weight: 400;
  line-height: 1.3;
  color: var(--color-text);
}

.give-body {
  font-size: clamp(
    var(--font-body-size-min),
    var(--font-body-size-fluid),
    var(--font-body-size-max)
  );
  line-height: 1.78;
  color: var(--color-text-secondary);
}

.give-rows {
  display: flex;
  flex-direction: column;
  border-top: var(--border-width) solid var(--color-border);
}

.give-item {
  display: grid;
  grid-template-columns: 1fr;
  align-items: center;
  gap: clamp(2rem, 5vw, 5rem);
  padding: clamp(1.75rem, 3.5vw, 2.75rem) 0;
  border-bottom: var(--border-width) solid var(--color-border);
}

.give-item-title {
  font-family: var(--font-display-family);
  font-size: clamp(1.375rem, 2vw, 1.625rem);
  font-weight: 400;
  line-height: 1.2;
  color: var(--color-accent-text);
}

.give-item-desc {
  font-size: clamp(0.9375rem, 1.25vw, 1rem);
  line-height: 1.78;
  color: var(--color-text-secondary);
}

/* ── Our values ────────────────────────────────────────────── */

.offers-section {
  padding: clamp(4.5rem, 9vw, 8rem) 0 clamp(5.5rem, 11vw, 10rem);
  position: relative;
  background:
    radial-gradient(
      ellipse at 0% 100%,
      var(--color-accent-glow-strong) 0%,
      transparent 52%
    ),
    var(--color-surface-inverse);
  overflow: hidden;
}

/* Accent rule across the top edge of the section */
.offers-section::before {
  content: "";
  height: var(--space-xxx-small);
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  background: linear-gradient(
    to right,
    var(--color-accent-text),
    var(--color-accent),
    var(--color-accent-text)
  );
}

/* Arrow watermark. The artwork has not been delivered yet — see README */
.offers-section::after {
  content: "";
  width: clamp(16.25rem, 34vw, 30rem);
  height: clamp(16.25rem, 34vw, 30rem);
  position: absolute;
  top: 50%;
  right: -3.75rem;
  transform: translateY(-50%);
  background: url("../images/logo-canadian-alumni-arrow.png")
    center / contain no-repeat;
  mix-blend-mode: screen;
  opacity: 0.07;
  pointer-events: none;
}

/* Lifts the content clear of the watermark and the accent wash */
.offers-container {
  position: relative;
  z-index: 1;
}

.offers-intro {
  margin-bottom: clamp(3rem, 6vw, 5rem);
}

.offers-title {
  font-family: var(--font-display-family);
  font-size: clamp(2.25rem, 4.5vw, 3.875rem);
  font-weight: 400;
  line-height: 1.08;
  color: var(--color-text-on-inverse);
}

.offers-subtitle {
  margin-top: 0.875rem;
  font-size: clamp(1rem, 1.2vw, 1.0625rem);
  letter-spacing: 0.01em;
  color: var(--color-text-on-inverse-secondary);
}

.pillar-divider {
  height: 1px;
  background-color: var(--color-border-on-inverse);
  transform-origin: left;
}

.pillar-row {
  display: grid;
  grid-template-columns: 1fr;
  align-items: center;
  gap: var(--space-medium);
  padding: clamp(1.375rem, 3.2vw, 2.5rem) 0;
}

.pillar-row-clip {
  padding-bottom: 0.28em;
  overflow: hidden;
}

.pillar-row-name {
  display: block;
  font-family: var(--font-display-family);
  font-size: clamp(2rem, 9vw, 3.25rem);
  font-weight: 400;
  line-height: 1.15;
  color: var(--color-text-on-inverse);
  transition: color 350ms linear;
}

.pillar-row:hover .pillar-row-name {
  color: var(--color-accent-on-inverse);
}

.pillar-row-desc {
  margin-top: 0.625rem;
  font-size: clamp(0.9375rem, 1.4vw, 1rem);
  line-height: 1.72;
  color: var(--color-text-on-inverse-muted);
}

/* ── How it works ──────────────────────────────────────────── */

.how-section {
  background-color: var(--color-surface-sunken);
  border-top: var(--border-width) solid var(--color-border);
  border-bottom: var(--border-width) solid var(--color-border);
}

.how-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(2rem, 4vw, 3.5rem);
  margin-top: clamp(3rem, 6vw, 4.5rem);
}

.how-step {
  display: flex;
  flex-direction: column;
}

.how-num {
  margin-bottom: var(--space-medium);
  font-family: var(--font-display-family);
  font-size: clamp(2.25rem, 4.5vw, 3.5rem);
  font-style: italic;
  line-height: 1;
  color: var(--color-accent-text);
}

.how-name {
  margin-bottom: var(--space-small);
  padding-bottom: 0.875rem;
  font-family: var(--font-display-family);
  font-size: clamp(1.375rem, 1.9vw, 1.625rem);
  font-weight: 400;
  line-height: 1.2;
  color: var(--color-text);
  text-wrap: balance;
  border-bottom: var(--border-width-strong) solid var(--color-accent-text);
}

.how-name:lang(fr) {
  text-wrap: balance;
}
@media (min-width: 48rem) {
  .how-name:lang(fr) {
    min-height: 3em;
  }
}

.how-desc {
  font-size: clamp(1rem, 1.15vw, 1.0625rem);
  line-height: 1.75;
  color: var(--color-text-secondary);
}

/* ── Call to action ────────────────────────────────────────── */

.cta-section {
  padding: clamp(5.5rem, 11vw, 9.5rem) 0;
  position: relative;
  border-top: var(--border-width) solid var(--color-border);
  overflow: hidden;
}

/* Radial accent wash off the top-right corner */
.cta-section::after {
  content: "";
  width: 37.5rem;
  height: 37.5rem;
  position: absolute;
  top: -5rem;
  right: -5rem;
  background: radial-gradient(
    circle,
    var(--color-accent-glow) 0%,
    transparent 65%
  );
  pointer-events: none;
}

.cta-inner {
  max-width: 43.75rem;
  position: relative;
  z-index: 1;
}

.cta-title {
  margin-bottom: 1.375rem;
  font-family: var(--font-display-family);
  font-size: clamp(2.375rem, 5.5vw, 4.75rem);
  font-weight: 400;
  line-height: 1.05;
  color: var(--color-text);
}

.cta-body {
  max-width: 31.25rem;
  margin-bottom: var(--space-xx-large);
  font-size: clamp(1rem, 1.4vw, 1.0625rem);
  line-height: 1.75;
  color: var(--color-text-secondary);
}

.cta-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-medium);
}

.cta-contact {
  max-width: 35rem;
  margin-top: clamp(3.25rem, 7vw, 5.5rem);
}

.cta-contact-rule {
  width: 2.5rem;
  height: var(--border-width-strong);
  margin-bottom: clamp(1.75rem, 3.5vw, 2.5rem);
  background-color: var(--color-border);
}

.cta-contact-question {
  margin-bottom: 0.875rem;
  font-family: var(--font-display-family);
  font-size: clamp(1.25rem, 2.2vw, 1.75rem);
  font-weight: 400;
  color: var(--color-text);
}

.cta-contact-sub {
  margin-bottom: var(--space-x-small);
  font-size: clamp(1rem, 1.3vw, 1.0625rem);
  line-height: 1.72;
  color: var(--color-text-secondary);
}

/**
 * The link carries the hit target and the inner span carries the underline,
 * so meeting 2.75rem does not detach the rule from the text.
 */
.cta-email-link {
  display: inline-flex;
  align-items: center;
  min-height: var(--size-hit-target);
  font-size: clamp(1rem, 1.3vw, 1.0625rem);
  font-weight: 500;
  color: var(--color-text);
  transition: color 200ms linear;
}

.cta-email-link-text {
  padding-bottom: 0.125rem;
  border-bottom: var(--border-width-accent) solid var(--color-border);
  transition:
    border-color 200ms linear,
    border-width 200ms linear;
}

.cta-email-link:hover {
  color: var(--color-accent-text);
}

.cta-email-link:hover .cta-email-link-text {
  border-color: var(--color-accent-text);
  border-bottom-width: var(--border-width-strong);
}

/* ── Legal pages ───────────────────────────────────────────── */

/**
 * Prose pages — privacy policy, terms of service. Same header and footer
 * as the landing page, with the section nav replaced by a single link home
 * and no motion of any kind: these pages load neither the .js class nor
 * any reveal attributes, so the motion layer never applies to them.
 */

/**
 * No menu here, so the call to action has to hold its place at every width.
 * Nothing in this row can shrink — the logo and the actions are both
 * flex: 0 0 auto — so below 30rem the mark and the button step down to keep
 * logo + Sign in + language badge inside the 320px floor. Restored at
 * 30rem and 48rem alongside the values they override.
 */
.legal-page .header-cta {
  display: inline-flex;
  padding: 0 var(--space-xx-small);
}

.legal-page .header-logo {
  height: 1.375rem;
}

/* Below 30rem the logo is the way home; the text link returns with the row */
.legal-nav {
  display: none;
}

.legal-main {
  padding: calc(var(--size-header-height) + clamp(2.5rem, 6vw, 4.5rem)) 0
    clamp(4rem, 8vw, 6rem);
}

.legal-head {
  max-width: 51.25rem;
  margin-bottom: clamp(2.5rem, 5vw, 3.5rem);
  padding-bottom: clamp(1.5rem, 3vw, 2rem);
  border-bottom: var(--border-width) solid var(--color-border);
}

.legal-title {
  font-family: var(--font-display-family);
  font-size: clamp(2.25rem, 4.5vw, 3.5rem);
  font-weight: 400;
  line-height: 1.08;
  color: var(--color-text);
}

.legal-meta {
  margin-top: var(--space-x-small);
  font-size: var(--font-size-small);
  color: var(--color-text-footnote);
}

/* Marks the copy as unapproved. Removed with the placeholder text. */
.legal-draft {
  margin-top: var(--space-medium);
  font-size: var(--font-size-small);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--color-accent-text);
}

.legal-body {
  display: flex;
  flex-direction: column;
  gap: clamp(2.25rem, 4vw, 3rem);
  max-width: 51.25rem;
}

.legal-section {
  display: flex;
  flex-direction: column;
  gap: var(--space-medium);
}

.legal-heading {
  font-family: var(--font-display-family);
  font-size: clamp(1.375rem, 2.2vw, 1.75rem);
  font-weight: 400;
  line-height: 1.2;
  color: var(--color-text);
}

/* Body face rather than display, so it reads as a step below the heading */
.legal-subheading {
  font-size: clamp(1rem, 1.3vw, 1.0625rem);
  font-weight: 600;
  line-height: 1.4;
  color: var(--color-text);
}

.legal-text {
  font-size: clamp(
    var(--font-body-size-min),
    var(--font-body-size-fluid),
    var(--font-body-size-max)
  );
  line-height: 1.78;
  color: var(--color-text-secondary);
}

/**
 * Block flow rather than a flex column: a flex item draws no ::marker, so
 * the bullets would disappear.
 */
.legal-list {
  margin-left: var(--space-medium);
  font-size: clamp(
    var(--font-body-size-min),
    var(--font-body-size-fluid),
    var(--font-body-size-max)
  );
  line-height: 1.78;
  color: var(--color-text-secondary);
  list-style: disc outside;
}

.legal-list li + li {
  margin-top: var(--space-xx-small);
}

/* Underlined at rest: a link inside prose has no other affordance */
.legal-link {
  color: var(--color-accent-text);
  text-decoration: underline;
  text-underline-offset: var(--size-underline-offset);
}

.legal-link:hover {
  color: var(--color-accent-text-strong);
  text-decoration-thickness: var(--border-width-accent);
}

/**
 * Editor output. page-legal.php renders the document with the_content(),
 * so the copy arrives as bare elements with no classes; these rules give
 * them the same treatment the class-based ones above give the static
 * pages. They are the only element selectors in the stylesheet, and the
 * reason is this one: the markup is not ours to write.
 */
.legal-content {
  gap: clamp(1.25rem, 2vw, 1.75rem);
}

.legal-content h2 {
  margin-top: clamp(1rem, 2vw, 1.5rem);
  font-family: var(--font-display-family);
  font-size: clamp(1.375rem, 2.2vw, 1.75rem);
  font-weight: 400;
  line-height: 1.2;
  color: var(--color-text);
}

.legal-content h3 {
  font-size: clamp(1rem, 1.3vw, 1.0625rem);
  font-weight: 600;
  line-height: 1.4;
  color: var(--color-text);
}

.legal-content > :first-child {
  margin-top: 0;
}

.legal-content p {
  font-size: clamp(
    var(--font-body-size-min),
    var(--font-body-size-fluid),
    var(--font-body-size-max)
  );
  line-height: 1.78;
  color: var(--color-text-secondary);
}

.legal-content ul,
.legal-content ol {
  margin-left: var(--space-medium);
  font-size: clamp(
    var(--font-body-size-min),
    var(--font-body-size-fluid),
    var(--font-body-size-max)
  );
  line-height: 1.78;
  color: var(--color-text-secondary);
}

.legal-content ul {
  list-style: disc outside;
}

.legal-content ol {
  list-style: decimal outside;
}

.legal-content li + li {
  margin-top: var(--space-xx-small);
}

.legal-content a {
  color: var(--color-accent-text);
  text-decoration: underline;
  text-underline-offset: var(--size-underline-offset);
}

.legal-content a:hover {
  color: var(--color-accent-text-strong);
  text-decoration-thickness: var(--border-width-accent);
}

/* ── Footer ────────────────────────────────────────────────── */

.footer {
  padding: 4rem 0 3rem;
  background-color: var(--color-surface-inverse);
}

.footer-top {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-medium);
  margin-bottom: var(--space-xx-large);
  padding-bottom: 2.5rem;
  border-bottom: var(--border-width) solid var(--color-border-on-inverse-subtle);
}

/**
 * Painted through a mask rather than shipped as an <img>, because the mark
 * is tinted to white here and an <img> cannot take a colour.
 */
.footer-logo-link {
  display: inline-flex;
  align-items: center;
  min-height: var(--size-hit-target);
  transition: opacity 250ms linear;
}

/* No border to recolour, so it fades — matching the header mark */
.footer-logo-link:hover {
  opacity: 0.7;
}

/* Untransitioned: a press should register at once, not ease */
.footer-logo-link:active {
  transform: translateY(1px);
}

.footer-logo {
  display: block;
  width: 8.5rem;
  height: 1.5rem;
  background-color: var(--color-text-inverse);
  -webkit-mask: url("../images/logo-canadian-alumni-dark.png")
    left center / contain no-repeat;
  mask: url("../images/logo-canadian-alumni-dark.png")
    left center / contain no-repeat;
}

.footer-tagline {
  margin-top: 0.25rem;
  font-size: var(--font-size-small);
  color: var(--color-text-on-inverse-faint);
}

.footer-bottom {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.875rem;
}

.footer-copyright {
  font-size: var(--font-size-small);
  color: var(--color-text-on-inverse-subtle);
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-large);
}

.footer-link {
  display: flex;
  align-items: center;
  min-height: var(--size-hit-target);
  font-size: var(--font-size-small);
  color: var(--color-link-on-inverse);
  text-decoration: var(--link-decoration);
  transition: color 250ms linear;
}

.footer-link:hover {
  color: var(--color-link-on-inverse-hover);
}

/* ══ Extra small and up ════════════════════════════════════════ */

@media (min-width: 30rem) {

  .header-logo {
    height: 1.75rem;
  }

  .legal-page .header-logo {
    height: 1.75rem;
  }

  .header-cta {
    display: inline-flex;
  }

  .legal-page .header-cta {
    padding: 0 var(--space-small);
  }

  .legal-nav {
    display: block;
  }

  .menu-cta {
    display: none;
  }

}

/* ══ Medium and up ═════════════════════════════════════════════ */

@media (min-width: 48rem) {

  .header-inner {
    gap: var(--space-small);
  }

  .header-logo {
    height: 1.875rem;
  }

  .legal-page .header-logo {
    height: 1.875rem;
  }

  .header-cta {
    padding: 0 var(--space-large);
  }

  .legal-page .header-cta {
    padding: 0 var(--space-large);
  }

  .ghost {
    top: 50%;
    right: auto;
    bottom: auto;
    transform: translateY(-50%);
  }

  .ghost-purpose {
    right: -2.5rem;
    font-size: clamp(5rem, 16vw, 13.75rem);
  }

  .ghost-meaning {
    right: -1.25rem;
    font-size: clamp(4.375rem, 14vw, 12.5rem);
  }

  .ghost-different {
    right: -3.75rem;
    font-size: clamp(4.375rem, 13vw, 11.875rem);
  }

  .ghost-cta {
    left: 50%;
    right: auto;
    font-size: clamp(6.25rem, 20vw, 18.75rem);
    transform: translate(-50%, -50%);
  }

  .meaning-grid {
    grid-template-columns: 1fr 1fr;
  }

  .meaning-visual {
    max-width: none;
  }

  .different-grid {
    grid-template-columns: 1fr 1fr;
  }

  /**
   * The heading is two or three words and the summary is a sentence, so
   * the split is not even. 0.6/1.4 rather than 0.75/1.25 — the headings
   * had room they were not using and the summaries were breaking early.
   */
  .give-item {
    grid-template-columns: 0.6fr 1.4fr;
  }

  .pillar-row-name {
    font-size: clamp(2.75rem, 7vw, 5.25rem);
  }

  .how-grid {
    grid-template-columns: repeat(3, 1fr);
  }

  .footer-top,
  .footer-bottom {
    flex-direction: row;
  }

  /**
   * Both rows centre once they are rows. The base rule sets
   * `align-items: flex-start`, which is what keeps the stacked layout
   * left-aligned — but in a row the cross axis is vertical, so the same
   * value was top-aligning the mark against the tagline beside it.
   */
  .footer-top,
  .footer-bottom {
    align-items: center;
  }

  /**
   * The 0.25rem exists to tighten the tagline under the mark when they
   * are stacked. Side by side it just pushes the text off centre.
   */
  .footer-tagline {
    margin-top: 0;
  }

  .footer-link {
    min-height: var(--size-hit-target-dense);
  }

}

/* ══ Large and up ══════════════════════════════════════════════ */

@media (min-width: 64rem) {

  /**
   * The description column. Was clamp(9.375rem, 20vw, 16.25rem) — 150px
   * to 260px, which set four or five words to a line and ran the longest
   * paragraph to eleven lines beside a name only two lines tall. Now
   * 240px–368px, which brings that worst case to about six.
   *
   * **The 23rem cap is not cosmetic — do not raise it.** The name track
   * cannot defend itself here. A `1fr` track normally floors at its
   * min-content width, but `.pillar-row-clip` is `overflow: hidden`,
   * which sets the item's automatic minimum size to zero: the grid will
   * happily shrink the name below the width of its own word, and
   * because the clip hides the overflow the word is silently cut off
   * rather than spilling somewhere visible.
   *
   * At the top of the type scale the name is set at 100px, where
   * "Personalization" measures about 687px. The two tracks share roughly
   * 1076px at 1440px and above, so anything over about 389px for the
   * description eats into the word. 28rem (448px) was tried and clipped
   * it by 59px at 1440px and 1920px.
   *
   * 23rem (368px) clears that threshold by **21px** — about 3% of the
   * word. That is the whole margin, and it is why this is a hard cap
   * rather than a preference: a slightly wider type scale, a longer
   * value name, or a wider container spends all of it.
   *
   * Re-measure this if the type scale, the longest value name, or the
   * container width changes.
   */
  .pillar-row {
    grid-template-columns: 1fr clamp(15rem, 34vw, 23rem);
  }

  .pillar-row-desc {
    margin-top: 0;
    font-size: clamp(0.9375rem, 1.1vw, 1rem);
    text-align: right;
  }

  .meaning-grid {
    grid-template-columns: 0.85fr 1fr;
  }

}

/* ══ Extra large and up ════════════════════════════════════════ */

/**
 * The inline nav needs about 1050px before its six labels stop competing
 * with the logo and the call to action, so it replaces the menu here rather
 * than at the medium breakpoint.
 */
@media (min-width: 75rem) {

  .header-inner {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
  }

  .header-actions {
    justify-self: end;
  }

  .primary-nav {
    display: block;
  }

  .menu {
    display: none;
  }

}

/* ══ Print ═════════════════════════════════════════════════════ */

/**
 * Paper is a surface like any other, so it is handled the way the contrast
 * and transparency modes are — by moving the token layer, not by restyling
 * components. Everything below the token block is either chrome that has
 * no meaning on paper, or a pagination rule.
 *
 * Accents resolve to black rather than brand red: a landing page printed
 * for reference does not need colour ink, and red text on a monochrome
 * printer greys out to roughly the contrast we spent this project fixing.
 */
@media print {

  :root {
    --color-text: #000000;
    --color-text-secondary: #000000;
    --color-text-footnote: #000000;
    --color-text-inverse: #000000;
    --color-text-on-inverse: #000000;
    --color-text-on-inverse-muted: #000000;
    --color-text-on-inverse-subtle: #000000;
    --color-text-on-inverse-secondary: #000000;
    --color-text-on-inverse-faint: #000000;
    --color-link-on-inverse: #000000;
    --color-accent: #000000;
    --color-accent-text: #000000;
    --color-accent-text-strong: #000000;
    --color-accent-fill: #000000;
    --color-accent-fill-hover: #000000;
    --color-accent-on-inverse: #000000;
    --color-surface-page: transparent;
    --color-surface-sunken: transparent;
    --color-surface-inverse: transparent;
    --color-border: #999999;
    --color-border-on-inverse: #999999;
    --color-border-on-inverse-subtle: #999999;
    --space-section-y: 1.5rem;
  }

  @page {
    margin: 15mm;
  }

  /* Chrome with no meaning on paper */
  .skip,
  .header,
  .hero-video,
  .hero-overlay,
  .hero-video-toggle,
  .scroll-hint,
  .marquee,
  .ghost,
  .ring,
  .menu,
  .language-switch,
  .header-cta,
  .footer-logo,
  .footer-logo-link {
    display: none;
  }

  /* Decorative washes, watermarks and edge fades */
  .purpose-section::before,
  .cta-section::after,
  .offers-section::before,
  .offers-section::after {
    display: none;
  }

  body {
    min-width: 0;
  }

  .hero {
    min-height: 0;
    padding: 0;
  }

  .hero-content {
    max-width: none;
    padding: 0;
    text-align: left;
  }

  /**
   * The motion layer leaves elements mid-animation, so paper has to undo
   * those states. !important carries it, so nothing further down the
   * cascade can leave an element hidden on the printed page.
   */
  [data-reveal],
  [data-reveal-scale],
  .clip-inner,
  .pillar-row-name,
  .pillar-row-desc,
  .offers-title,
  .offers-subtitle {
    opacity: 1 !important;
    translate: none !important;
    transform: none !important;
    scale: none !important;
  }

  .purpose-rule,
  .give-rule,
  .pillar-divider {
    scale: none !important;
  }

  /* Keep a unit and its heading together */
  .give-item,
  .how-step,
  .different-stat,
  .pillar-row,
  .cta-contact,
  .different-footnote {
    break-inside: avoid;
  }

  h1,
  h2,
  h3 {
    break-after: avoid;
  }

  .give-section,
  .how-section,
  .offers-section {
    border: 0;
  }

  /**
   * A printed link is a dead end unless its destination is written out.
   * In-page fragments are skipped — the target is in the same document.
   */
  .main a[href^="http"]::after,
  .main a[href^="mailto"]::after {
    content: " (" attr(href) ")";
    font-size: 0.875em;
    word-break: break-all;
  }

  .footer-links,
  .footer-link {
    break-inside: avoid;
  }

}


/**
 * Motion layer. Additive: it introduces no colours, spacing or layout,
 * only the states things animate from and the timelines they run on.
 *
 * Three mechanisms, in order of how much they can fail:
 *
 *   1. Entrance    CSS animations on a delay. No JS, no observers.
 *   2. Reveals     A class added by the page script when the element
 *                  enters view; CSS transitions do the work.
 *   3. Parallax    Scroll-driven CSS animations. No JS at all.
 *
 * Everything here sits inside prefers-reduced-motion: no-preference, so a
 * visitor who asks for less motion is served the static page rather than a
 * neutralised version of an animated one — nothing is ever hidden, so
 * nothing needs revealing.
 *
 * The pre-animation states additionally require .js on <html>, because an
 * element hidden in CSS and revealed in JS is only safe while the JS is
 * running. See the head of the page template for how that class is set
 * and withdrawn.
 */

:root {

  /**
   * Easing. Approximations of power4.out, power3.out and power2.inOut.
   */
  --ease-out-quart: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-out-cubic: cubic-bezier(0.33, 1, 0.68, 1);
  --ease-in-out-quad: cubic-bezier(0.65, 0, 0.35, 1);

  /* Step between staggered siblings */
  --stagger-step: 130ms;

  /**
   * Values section. Each row arrives as a pair — its name, then its own
   * description — rather than every name followed by every description,
   * so the two halves of a row read together. --pair-step is the gap
   * between rows, --pair-gap the gap between a name and its description.
   */
  --pair-step: 380ms;
  --pair-gap: 240ms;

}

/* ══ Entrance ══════════════════════════════════════════════════ */

/**
 * Composed with the `translate` property rather than `transform`, so the
 * centring transforms main.css puts on the ghost words and the image are
 * left intact — the two compose instead of overwriting each other.
 */
@keyframes rise-in {

  from {
    opacity: 0;
    translate: 0 var(--rise-from, 1rem);
  }

  to {
    opacity: 1;
    translate: 0 0;
  }

}

@keyframes clip-up {

  from {
    translate: 0 115%;
  }

  to {
    translate: 0 0;
  }

}

@media (prefers-reduced-motion: no-preference) {

  /**
   * The hero sequence. Delays are absolute because a CSS animation has no
   * equivalent of a timeline's relative offsets.
   */
  .js .hero-eyebrow {
    --rise-from: 1rem;

    animation: rise-in 700ms var(--ease-out-quart) 300ms both;
  }

  .js .hero-title-line .clip-inner {
    animation: clip-up 1050ms var(--ease-out-quart) 550ms both;
  }

  .js .hero-subtitle {
    --rise-from: 1.25rem;

    animation: rise-in 800ms var(--ease-out-quart) 1270ms both;
  }

  .js .hero-actions {
    --rise-from: 1.125rem;

    animation: rise-in 700ms var(--ease-out-quart) 1520ms both;
  }

}

/* ══ Reveals ═══════════════════════════════════════════════════ */

/**
 * The page script adds .is-revealed once the element has entered view,
 * then stops watching it. Per-element delays arrive as --reveal-delay,
 * read from the data-reveal-delay attributes in the markup.
 */
@media (prefers-reduced-motion: no-preference) {

  .js [data-reveal] {
    opacity: 0;
    translate: 0 2.75rem;
    transition:
      opacity 1000ms var(--ease-out-cubic),
      translate 1000ms var(--ease-out-cubic);
    transition-delay: var(--reveal-delay, 0ms);
  }

  .js [data-reveal].is-revealed {
    opacity: 1;
    translate: 0 0;
  }

  .js [data-reveal-scale] {
    opacity: 0;
    scale: 0.92;
    transition:
      opacity 1100ms var(--ease-out-cubic),
      scale 1100ms var(--ease-out-cubic);
  }

  .js [data-reveal-scale].is-revealed {
    opacity: 1;
    scale: 1;
  }

  /**
   * Two-value scale: draws out along x while keeping its height.
   *
   * Both section rules behave identically and are grouped so they cannot
   * drift. Each already carries `transform-origin: left` on its base
   * rule, so the line grows from the margin rather than from its centre.
   */
  .js .purpose-rule,
  .js .give-rule {
    scale: 0 1;
    transition: scale 1100ms var(--ease-out-cubic);
  }

  /**
   * Revealed from the parent, not from the rule itself.
   *
   * `scale: 0 1` collapses the element's box to zero width, and an
   * IntersectionObserver never reports a zero-area target as
   * intersecting — it does not even fire its initial callback. Observing
   * the rule directly therefore meant the class was never added and the
   * line stayed invisible for good. That was live for `.purpose-rule`.
   *
   * So the observer watches the block the rule sits in, which always has
   * area, and the rule reveals from its ancestor's class. The selectors
   * in scripts/*.js must stay in step with the two parents named here.
   */
  .js .purpose-layout.is-revealed .purpose-rule,
  .js .give-intro.is-revealed .give-rule {
    scale: 1 1;
  }

  /* ── Values section ──────────────────────────────────────── */

  /**
   * One observer watches the section and the children follow on a stagger.
   * The page script numbers each set with --stagger rather than the stylesheet
   * counting :nth-child, so reordering the markup cannot silently break
   * the sequence.
   */
  .js .offers-section .offers-title {
    opacity: 0;
    translate: 0 2.25rem;
    transition:
      opacity 800ms var(--ease-out-cubic),
      translate 800ms var(--ease-out-cubic);
  }

  .js .offers-section .offers-subtitle {
    opacity: 0;
    translate: 0 1.25rem;
    transition:
      opacity 600ms var(--ease-out-cubic) 300ms,
      translate 600ms var(--ease-out-cubic) 300ms;
  }

  .js .offers-section .pillar-divider {
    scale: 0 1;
    transform-origin: left;
    transition: scale 700ms var(--ease-in-out-quad);
    transition-delay: calc(600ms + var(--stagger, 0) * var(--stagger-step));
  }

  .js .offers-section .pillar-row-desc {
    opacity: 0;
    translate: 1.5rem 0;
    transition:
      opacity 600ms var(--ease-out-cubic),
      translate 600ms var(--ease-out-cubic);
    transition-delay: calc(
      1220ms + var(--pair-gap) + var(--stagger, 0) * var(--pair-step)
    );
  }

  .js .offers-section.is-revealed .offers-title,
  .js .offers-section.is-revealed .offers-subtitle,
  .js .offers-section.is-revealed .pillar-row-desc {
    opacity: 1;
    translate: 0 0;
  }

  .js .offers-section.is-revealed .pillar-divider {
    scale: 1 1;
  }

}

/**
 * Clipping the names up only reads well where each sits on one line, so
 * below this width they are simply present.
 */
@media (prefers-reduced-motion: no-preference) and (min-width: 48rem) {

  .js .offers-section .pillar-row-name {
    translate: 0 112%;
    transition: translate 1050ms var(--ease-out-quart);
    transition-delay: calc(1220ms + var(--stagger, 0) * var(--pair-step));
  }

  .js .offers-section.is-revealed .pillar-row-name {
    translate: 0 0;
  }

}

/* ══ Parallax ══════════════════════════════════════════════════ */

/**
 * Scroll-driven CSS animations: no JS, and no main-thread work while
 * scrolling. Firefox is the last engine without support and has it landing
 * imminently, so the single unsupported case is a browser one version
 * behind, which sees the decorations sit still.
 *
 * The @supports guard is load-bearing rather than politeness. Without a
 * recognised timeline these declarations would fall back to the document
 * timeline at a zero duration and snap straight to their end state, so an
 * unsupporting browser would show every decoration permanently displaced
 * instead of simply static.
 */
@supports (animation-timeline: view()) {

  @media (prefers-reduced-motion: no-preference) {

    /**
     * Each section publishes a view timeline that its own decorations
     * read, so the drift is driven by the section's pass through the
     * viewport rather than the small element's.
     */
    .purpose-section,
    .meaning-section,
    .different-section,
    .cta-section,
    .marquee {
      view-timeline-name: --section-view;
    }

    .ghost,
    .ring-cta,
    .meaning-image,
    .marquee-inner {
      animation: drift linear both;
      animation-timeline: --section-view;
      will-change: translate;
    }

    .ghost-purpose {
      --drift-x: 1.25rem;
      --drift-y: -3.75rem;
    }

    .ghost-meaning,
    .ghost-different,
    .ghost-cta {
      --drift-y: -3.125rem;
    }

    .meaning-image {
      --drift-y: -2rem;
    }

    .ring-cta {
      --drift-y: -1.875rem;
      --drift-rotate: -20deg;

      animation-name: drift-rotate;
    }

    .marquee-inner {
      --drift-x: -14%;
    }

    /* Scrolls across the hero's first viewport-height of travel */
    .hero-video {
      animation: hero-push linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 100dvh;
    }

  }

}

/**
 * Distances are read from the animated element, so an unset one resolves
 * to no movement on that axis rather than breaking the keyframe.
 */
@keyframes drift {

  from {
    translate: 0 0;
  }

  to {
    translate: var(--drift-x, 0) var(--drift-y, 0);
  }

}

@keyframes drift-rotate {

  from {
    translate: 0 0;
    rotate: 0deg;
  }

  to {
    translate: var(--drift-x, 0) var(--drift-y, 0);
    rotate: var(--drift-rotate, 0deg);
  }

}

@keyframes hero-push {

  to {
    scale: 1.08;
  }

}

/* ══ Print ═════════════════════════════════════════════════════ */

/**
 * The print block above handles paper for the page as a whole. The motion
 * layer hides things before revealing them, so on paper those states have
 * to be undone here too — the selectors below carry the .js prefix that the
 * pre-animation states do, so they match at the same specificity and win
 * on order.
 */
@media print {

  .js [data-reveal],
  .js [data-reveal-scale],
  .js .hero-eyebrow,
  .js .hero-subtitle,
  .js .hero-actions,
  .js .hero-title-line .clip-inner,
  .js .offers-section .offers-title,
  .js .offers-section .offers-subtitle,
  .js .offers-section .pillar-row-name,
  .js .offers-section .pillar-row-desc {
    opacity: 1;
    translate: none;
    scale: none;
    animation: none;
    transition: none;
  }

  .js .purpose-rule,
  .js .give-rule,
  .js .offers-section .pillar-divider {
    scale: none;
  }

}
