/* ═══════════════════════════════════════════════════════════════════════════
   RealTimes · About · editorial edition
   One stylesheet, no framework, no CDN. Tokens first, then layout by section.
   Rendering contract lives in DESIGN-NOTES.md.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Typeface ──────────────────────────────────────────────────────────── */
@font-face {
  font-family: 'Geist';
  src: url('../fonts/geist-latin.woff2') format('woff2');
  font-weight: 100 900;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
                 U+2192, U+2194, U+2197, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Geist';
  src: url('../fonts/geist-latin-ext.woff2') format('woff2');
  font-weight: 100 900;
  font-display: swap;
  unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB,
                 U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── Tokens ────────────────────────────────────────────────────────────── */
:root {
  /* colour */
  --paper:      #ffffff;
  --warm:       #f3f0ea;   /* warm off white */
  --ink:        #0c0c0c;   /* near black, body and headlines on paper */
  --ink-soft:   #4a4744;   /* secondary text on paper, 8.2:1 on warm */
  --graphite:   #16181a;   /* dark sections */
  --graphite-2: #1f2225;   /* dark section step */
  --on-dark:    #ffffff;
  --on-dark-2:  #d9d6d1;   /* 11.9:1 on --graphite, safe for small text */
  --rule:       rgba(12, 12, 12, 0.16);
  --rule-light: rgba(255, 255, 255, 0.20);
  --red:        #e31b23;   /* the single accent, used sparingly */
  /* One restrained orange, introduced 2026-09-18 for the introduction's quote
     marker and its 01 numeral. Marks and 11px labels only, never a body run.
     Measures 4.4 on the warm ground, well over the 3.0 a non-text mark needs. */
  --accent:     #c2560f;
  /* Same orange, darkened for TEXT only. #c2560f measures 3.99 on the warm
     ground, which clears the 3.0 a non-text mark needs and misses the 4.5
     that 11px type needs. So the 2px quote marker keeps --accent and the
     small numeral takes --accent-ink at 4.68. Same pattern the design system
     already uses for red: extend with an ink variant, never restyle the mark.
     Measured on the rendered page, not computed from the stylesheet. */
  --accent-ink: #b04e0d;

  /* type */
  --sans: 'Geist', 'Helvetica Neue', Inter, system-ui, -apple-system,
          'Segoe UI', Arial, sans-serif;
  --serif: 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia,
           'Times New Roman', serif;

  --t-display: clamp(2.3rem, 5.6vw, 5.2rem);
  --t-city:    clamp(2.8rem, 9.4vw, 8.6rem);
  --t-stat:    clamp(3rem, 8.2vw, 7.5rem);
  --t-h2:      clamp(1.85rem, 4.4vw, 4rem);
  --t-h3:      clamp(1.6rem, 3.2vw, 2.9rem);
  --t-lead:    clamp(1.1rem, 1.5vw, 1.4rem);
  --t-body:    clamp(1rem, 1.05vw, 1.08rem);
  --t-label:   0.6875rem;

  /* spacing, 8px base */
  --s1: 0.5rem;  --s2: 1rem;   --s3: 1.5rem;  --s4: 2rem;
  --s5: 3rem;    --s6: 4.5rem; --s7: 7rem;    --s8: 10rem;
  --gutter: clamp(1.25rem, 5vw, 6.5rem);
  --measure: 34rem;

  /* motion */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur:  620ms;
}

/* ── Reset ─────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--t-body);
  line-height: 1.62;
  letter-spacing: -0.005em;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;               /* belt: nothing may scroll sideways */
}
img, video { max-width: 100%; display: block; }
h1, h2, h3, p, dl, dd, dt, ul { margin: 0; }
ul { list-style: none; padding: 0; }
a { color: inherit; }

:focus-visible {
  outline: 2px solid var(--red);
  outline-offset: 3px;
}
.skip {
  position: absolute; left: -9999px; top: 0; z-index: 99;
  background: var(--ink); color: #fff; padding: var(--s2) var(--s3);
  font-size: 0.85rem;
}
.skip:focus { left: 0; }

/* ── Shared type atoms ─────────────────────────────────────────────────── */
.label {
  font-size: var(--t-label);
  letter-spacing: 0.24em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--ink-soft);
}
.label-light { color: var(--on-dark-2); }

.statement {
  font-size: var(--t-h2);
  line-height: 1.08;
  letter-spacing: -0.032em;
  font-weight: 500;
  text-wrap: balance;
}
.lead { font-size: var(--t-lead); line-height: 1.5; color: var(--ink); }
/* .emphasis retired 2026-09-18: the introduction pull quote is now
   .intro-quote, which is not boxed and carries no left border. */

.cta {
  display: inline-flex; align-items: baseline; gap: 0.5em;
  font-size: 0.95rem; font-weight: 500; letter-spacing: -0.01em;
  text-decoration: none;
  padding-bottom: 0.3em;
  border-bottom: 1px solid var(--rule);
  transition: border-color 220ms var(--ease), color 220ms var(--ease);
}
.cta:hover, .cta:focus-visible { border-bottom-color: var(--red); color: var(--red); }
.cta span { transition: transform 240ms var(--ease); }
.cta:hover span { transform: translateX(4px); }
.cta-light { color: var(--on-dark); border-bottom-color: var(--rule-light); }
.cta-light:hover, .cta-light:focus-visible { color: var(--on-dark); border-bottom-color: var(--red); }
.cta-sm { font-size: 0.85rem; }

/* ── Photographs ───────────────────────────────────────────────────────── */
/* Every image on this page is a real graded WebP wired statically in the
   markup, so there is no placeholder state and no JS-applied .ok class to wait
   for. Images are visible at rest: an image that starts at opacity 0 and waits
   for a script is an image that disappears when the script does. The
   .ph / .ph-cities rules and the img.ok gate were removed 2026-09-18 with the
   placeholder machinery in scripts/about.js. */

/* ── Topbar ────────────────────────────────────────────────────────────── */
.topbar::before {
  content: ''; position: absolute; inset: -1px 0 auto 0; height: 220%;
  background: linear-gradient(to bottom, rgba(8,9,10,0.78) 0%, rgba(8,9,10,0.42) 55%, rgba(8,9,10,0) 100%);
  pointer-events: none; z-index: -1;
}
.topbar {
  position: absolute; top: 0; left: 0; right: 0; z-index: 10;
  isolation: isolate;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s3);
  padding: clamp(1rem, 2.4vw, 2rem) var(--gutter);
  padding-top: calc(clamp(1rem, 2.4vw, 2rem) + env(safe-area-inset-top, 0px));
}
.topbar-logo img { width: clamp(104px, 9vw, 132px); height: auto; }
.topbar-nav { display: flex; gap: clamp(1rem, 2.4vw, 2.4rem); }
.topbar-nav a {
  font-size: var(--t-label); letter-spacing: 0.2em; text-transform: uppercase;
  color: #fff; text-decoration: none; padding: 0.4em 0;
  border-bottom: 1px solid transparent;
  text-shadow: 0 1px 14px rgba(0,0,0,0.85);
}
.topbar-nav a:hover, .topbar-nav a[aria-current] { border-bottom-color: var(--red); }

/* ── 1 · HERO ──────────────────────────────────────────────────────────── */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex; align-items: flex-end;
  padding: var(--s6) var(--gutter) clamp(2rem, 5vh, 4rem);
  padding-bottom: calc(clamp(2rem, 5vh, 4rem) + env(safe-area-inset-bottom, 0px));
  overflow: hidden;
  background: var(--graphite);
  color: var(--on-dark);
  isolation: isolate;
}
.hero-media { position: absolute; inset: 0; z-index: -2; }
.hero-media img, .hero-media video {
  width: 100%; height: 100%; object-fit: cover; object-position: center 40%;
}
/* Scrim: strong enough under the type block for AA white on photography,
   measured at >= 8:1 against the darkest grade we expect. */
/* Worst case for AA is a pure white photograph underneath. At 0.70 alpha the
   composite is rgb(83,83,83) and white type on it is 6.9:1, so every stop that
   sits behind a text run is held at 0.70 or darker. Verified in qa/contrast.json. */
.hero-scrim {
  position: absolute; inset: 0; z-index: 2;
  background:
    linear-gradient(to top,
      rgba(8,9,10,0.94) 0%,
      rgba(8,9,10,0.88) 55%,
      rgba(8,9,10,0.70) 78%,
      rgba(8,9,10,0.34) 100%);
}
.hero-inner { position: relative; z-index: 3; width: 100%; max-width: 80rem; }
.hero-label { color: #fff; opacity: 0.86; margin-bottom: var(--s3); }
.hero-title {
  font-size: var(--t-display);
  line-height: 0.95;
  letter-spacing: -0.045em;
  font-weight: 500;
  margin: 0 0 var(--s4);
}
.hero-title .ln { display: block; overflow: hidden; }
.hero-title .ln:last-child { color: #cfcbc5; }
.hero-sub {
  font-size: var(--t-lead); line-height: 1.5; max-width: 40rem;
  color: #ffffff;
}
.hero-cue {
  display: inline-flex; align-items: center; gap: 0.75em;
  margin-top: var(--s5);
  font-size: var(--t-label); letter-spacing: 0.22em; text-transform: uppercase;
  text-decoration: none; color: #fff;
  padding: 0.6em 0; border-bottom: 1px solid var(--rule-light);
}
.hero-cue:hover { border-bottom-color: var(--red); }
.hero-cue-arrow { animation: cue 2.6s var(--ease) infinite; }
@keyframes cue { 0%,100% { transform: translateY(0); } 50% { transform: translateY(5px); } }

/* ── 2 · INTRODUCTION ──────────────────────────────────────────────────── */
/* Redesigned 2026-09-18. Asymmetric 37/63, never centred and never equal.
   No cards, no gradients, no rounded containers, no icons, no extra
   backgrounds, no borders except the one hairline above the strap. The only
   decoration on the whole section is a 2px orange marker beside the quote. */
.intro { background: var(--warm); padding: var(--s8) var(--gutter) var(--s6); }

.intro-grid {
  display: grid;
  grid-template-columns: 1fr;          /* mobile: one column, photo above text */
  gap: var(--s4);
  max-width: 82rem; margin: 0 auto;
}

/* Left column ─────────────────────────────────────────────────────────── */
.intro-eyebrow {
  display: block;
  margin-bottom: var(--s3);
  letter-spacing: 0.28em;              /* wider than the page label, per brief */
  color: var(--ink-soft);
}
.intro-num { color: var(--accent-ink); font-weight: 600; }

.intro-shot { margin: 0; }
.intro-shot img {
  width: 100%; height: auto;
  aspect-ratio: 2 / 3; object-fit: cover;
}

/* Right column ────────────────────────────────────────────────────────── */
.intro-body { display: grid; gap: var(--s4); align-content: start; }

/* The statement is the dominant element on the section. Capped at 20ch so it
   breaks to five or six lines at desktop rather than running to three long
   ones: a statement that fits on three lines reads as a sentence, and this is
   meant to read as a position. */
.intro-body .statement { color: var(--ink); max-width: 20ch; }

/* 19px at 375, 22px at 1440. */
.intro-body .lead {
  font-size: clamp(1.1875rem, 0.36vw + 1.1rem, 1.375rem);
  line-height: 1.55; color: var(--ink); max-width: 52ch;
}
/* Smaller and quieter, the third step down. --ink-soft measures 8.1 on this
   ground, so quieter here is a tone change and never a contrast compromise. */
.intro-body .quiet {
  font-size: var(--t-body); line-height: 1.68;
  color: var(--ink-soft); max-width: 58ch;
}

/* Pull quote ──────────────────────────────────────────────────────────── */
/* Breaks the grid: spans both columns, starts at the left edge under the
   photograph and runs wider than either column. Not a box, not a card, no
   background, no border. The only mark is a 2px orange rule whose top end
   rides up over the photograph's bottom edge, which is where the overlap
   lives. The glyphs themselves clear the picture: see the note in index.html
   and DESIGN-NOTES.md section 7. */
.intro-quote {
  grid-column: 1 / -1;
  position: relative; z-index: 2;
  margin: var(--s4) 0 0;
  padding-left: var(--s3);
}
.intro-quote::before {
  content: '';
  position: absolute; left: 0; top: 0; bottom: 0.4em;
  width: 2px; background: var(--accent);
  /* the marker is the element that overlaps the photograph, so it paints above it */
  z-index: 3;
}
.intro-quote p {
  font-family: var(--serif);
  font-size: clamp(2rem, 2.6vw, 2.625rem);   /* 32px at 375, 42px at 1440 */
  font-style: italic;
  line-height: 1.26; letter-spacing: -0.012em;
  /* Wide enough to cross out of the photograph's column and into the text
     column, which is what "spans both columns" has to mean visually. At 30ch
     it sat inside the left third and left a hole under the body copy. */
  color: var(--ink); max-width: 42ch;
}

/* Strap ───────────────────────────────────────────────────────────────── */
.strip {
  display: flex; flex-wrap: wrap;
  gap: clamp(1.5rem, 6vw, 6rem);
  max-width: 82rem; margin: var(--s7) auto 0;
  padding-top: var(--s4);
  border-top: 1px solid var(--rule);
  font-size: var(--t-label); letter-spacing: 0.28em; text-transform: uppercase;
  color: var(--ink-soft);
}

/* ── 3 · TWO SIDES ─────────────────────────────────────────────────────── */
.sides { background: var(--paper); padding: var(--s8) 0 var(--s7); }
.sides-title {
  max-width: 82rem; margin: 0 auto var(--s7); padding: 0 var(--gutter);
  font-size: var(--t-h2); line-height: 1.06; letter-spacing: -0.034em;
  font-weight: 500;
}
.sides-title .ln { display: block; }
.sides-title .em { color: var(--ink-soft); }

.side {
  display: grid; grid-template-columns: 1fr; gap: var(--s4);
  max-width: 82rem; margin: 0 auto var(--s7); padding: 0 var(--gutter);
  align-items: center;
}
.side:last-child { margin-bottom: 0; }
/* 3:2 matches the exported crop in assets/img/MANIFEST.json. A 4:5 box here
   would centre-crop a third of the frame away in the browser, which is the one
   thing DESIGN-SYSTEM 5.2 forbids. */
.side-media { position: relative; aspect-ratio: 3 / 2; background: var(--warm); overflow: hidden; }
.side-media img { width: 100%; height: 100%; object-fit: cover; }
.side-text { display: grid; gap: var(--s2); align-content: center; }
.side-text .label { margin-bottom: var(--s1); }
.side-h {
  font-size: var(--t-h3); line-height: 1.08; letter-spacing: -0.03em;
  font-weight: 500;
}
.side-text p { color: var(--ink-soft); max-width: var(--measure); }
.side-text .cta { margin-top: var(--s2); justify-self: start; }

/* ── 4 · INTERNATIONAL PRESENCE ────────────────────────────────────────── */
.cities {
  position: relative; overflow: hidden; isolation: isolate;
  background: var(--graphite); color: var(--on-dark);
  padding: var(--s8) var(--gutter) var(--s7);
}
.cities-bg { position: absolute; inset: 0; z-index: -1; background: var(--graphite); }
.cities-bg .city-img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0;
  transition: opacity 900ms var(--ease), transform 1400ms var(--ease);
  transform: scale(1.03);
}
.cities-bg .city-img.is-on { opacity: 0.50; transform: scale(1); }
.cities-bg::after {
  content: ''; position: absolute; inset: 0;
  /* Opened up 2026-09-18, once the real frames landed. At 0.34 image opacity
     under an 0.88/0.66 overlay the photograph composited to rgb(31-41), which
     is a near-solid dark slab: the frame was paid for and then hidden, which
     rendered-deliverable-qa.md forbids by name. The frame now carries 0.50 and
     the overlay runs 0.78 to 0.48, so the photograph reads.
     The ceiling is measured, not guessed: the tightest run on this band is
     16px body text at #d9d6d1, which needs 4.5 and therefore a backdrop no
     brighter than about rgb(92). Worst measured pixel after this change is
     rgb(70) across all four city frames at both viewports, verified against the
     RENDERED page in qa/contrast-cities.json, never from this stylesheet. */
  background: linear-gradient(to right, rgba(12,13,14,0.78), rgba(12,13,14,0.48));
}

.cities-inner { position: relative; z-index: 2; max-width: 82rem; margin: 0 auto; }
.cities-title {
  font-size: var(--t-h2); line-height: 1.06; letter-spacing: -0.034em;
  font-weight: 500; max-width: 24ch; margin: var(--s3) 0 var(--s6);
  color: #fff;
}
.city-list { display: grid; gap: 0; border-top: 1px solid var(--rule-light); }
.city-list li { border-bottom: 1px solid var(--rule-light); }
.city {
  display: block; width: 100%;
  appearance: none; background: none; border: 0; cursor: pointer;
  padding: clamp(0.35rem, 1.2vw, 0.9rem) 0;
  font-family: var(--sans);
  font-size: var(--t-city); line-height: 1.02; letter-spacing: -0.05em;
  font-weight: 500; text-align: left; text-transform: uppercase;
  color: #c2beb8;
  transition: color 320ms var(--ease), transform 420ms var(--ease);
}
.city:hover, .city:focus-visible { color: #fff; }
.city[aria-pressed='true'] { color: #fff; }
.city[aria-pressed='true']::after {
  content: ''; display: inline-block; vertical-align: middle;
  width: 0.34em; height: 0.34em; margin-left: 0.3em;
  /* Square, not round: the red square over the i in the RealTimes logo is the
     brand's own mark, so every red marker on this page is a square. 2026-09-18. */
  background: var(--red); border-radius: 0;
}
.cities-foot {
  display: grid; gap: var(--s3); max-width: 40rem; margin-top: var(--s6);
}
.cities-foot p { color: var(--on-dark-2); }
.cities-foot .cta { justify-self: start; }

/* ── 5 · IN NUMBERS ────────────────────────────────────────────────────── */
.numbers {
  background: var(--graphite-2); color: var(--on-dark);
  padding: var(--s8) var(--gutter) var(--s7);
}
.numbers-title { max-width: 82rem; margin: 0 auto var(--s6); }
.numbers-grid {
  display: grid; grid-template-columns: 1fr; gap: var(--s5);
  max-width: 82rem; margin: 0 auto;
}
.stat { border-top: 1px solid var(--rule-light); padding-top: var(--s3); }
.stat-v {
  margin: 0; font-size: var(--t-stat); line-height: 0.92; letter-spacing: -0.05em;
  font-weight: 500; color: #fff; font-variant-numeric: tabular-nums;
}
.stat-l {
  margin-top: var(--s2);
  font-size: var(--t-label); letter-spacing: 0.22em; text-transform: uppercase;
  color: #ffffff; font-weight: 500;
}
/* One stat, international markets, is medium confidence in facts/NUMBERS.json.
   It carries a qualifying note so the figure is never read as harder than it
   is. Caption size, pure white, per the dark-band contract. */
.stat-note {
  margin-top: var(--s1);
  font-size: 0.8125rem; line-height: 1.5; letter-spacing: 0;
  color: #ffffff; max-width: 26ch;
}
.numbers-note {
  max-width: 82rem; margin: var(--s5) auto 0;
  font-size: 0.8rem; color: #ffffff; letter-spacing: 0.02em;
  padding-top: var(--s3); border-top: 1px solid var(--rule-light);
}

/* ── 6 · EXECUTIVE TEAM ────────────────────────────────────────────────── */
.team { background: var(--paper); padding: var(--s8) var(--gutter) var(--s7); }
.team-title { max-width: 82rem; margin: 0 auto var(--s6); }
.team-grid {
  display: grid; grid-template-columns: 1fr; gap: var(--s6);
  max-width: 82rem; margin: 0 auto;
}
.person { display: grid; gap: var(--s3); }
.person-portrait {
  position: relative; display: block; aspect-ratio: 4 / 5;
  background: var(--warm); overflow: hidden;
}
/* Portraits: colour file, greyed at rest, full colour on hover AND on keyboard
   focus. The source files in img-colour/ are colour; grayscale(1) here is the
   rest state, so there is only ever one image and one request per portrait.
   Both properties animated are compositor-only, so nothing reflows. */
.person-portrait img {
  width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(1);
  transition: transform 900ms var(--ease), filter 450ms var(--ease);
}
/* Hover on the portrait itself, and focus ANYWHERE in the person's card.
   The portrait's own wrapper is aria-hidden and tabindex="-1", so it never
   receives focus: the focusable elements are the name link and the LinkedIn
   link in the text column beside it. Scoping to .person means a keyboard user
   gets exactly what a mouse user gets, which is the whole point. */
.person:hover .person-portrait img,
.person:focus-within .person-portrait img { filter: none; }
/* The subtle lift stays on direct portrait hover only. 2.5 percent inside an
   overflow-hidden box: felt, not seen, and it shifts no layout. */
.person-portrait:hover img { transform: scale(1.025); }
.person-text { display: grid; gap: var(--s1); }
.person-name {
  font-size: clamp(1.35rem, 2.1vw, 1.9rem); line-height: 1.12;
  letter-spacing: -0.028em; font-weight: 500;
}
.person-name a { text-decoration: none; border-bottom: 1px solid var(--rule); padding-bottom: 0.08em; }
.person-name a:hover { border-bottom-color: var(--red); }
.person-role { margin-bottom: var(--s2); }
.person-text p { color: var(--ink-soft); max-width: var(--measure); }
.person-text .cta { margin-top: var(--s2); justify-self: start; }

/* ── 7 · EDITORIAL MOMENT ──────────────────────────────────────────────── */
.moment {
  position: relative; min-height: 100svh;
  display: flex; align-items: center;
  padding: var(--s7) var(--gutter);
  background: var(--graphite); color: var(--on-dark);
  overflow: hidden; isolation: isolate;
}
.moment-media { position: absolute; inset: 0; z-index: -1; }
.moment-media img { width: 100%; height: 100%; object-fit: cover; }
.moment-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(to bottom, rgba(8,9,10,0.66), rgba(8,9,10,0.78));
}
.moment-line {
  position: relative; z-index: 2;
  max-width: 82rem; margin: 0 auto; width: 100%;
  font-family: var(--serif);
  font-size: clamp(1.75rem, 4.6vw, 4.2rem);
  line-height: 1.16; letter-spacing: -0.02em;
  color: #fff; max-width: 20ch; text-wrap: balance;
}

/* ── 8 · FINAL CTA + FOOTER ────────────────────────────────────────────── */
.final {
  min-height: 100svh; display: flex; flex-direction: column; justify-content: center;
  background: var(--graphite); color: var(--on-dark);
  padding: var(--s8) var(--gutter) var(--s7);
}
.final-title {
  max-width: 82rem; width: 100%; margin: 0 auto var(--s6);
  font-size: var(--t-h2); line-height: 1.04; letter-spacing: -0.036em;
  font-weight: 500; color: #fff;
}
.final-links {
  max-width: 82rem; width: 100%; margin: 0 auto;
  border-top: 1px solid var(--rule-light);
}
.final-links li { border-bottom: 1px solid var(--rule-light); }
.final-links a {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  padding: clamp(1.1rem, 2.6vw, 2rem) 0;
  font-size: clamp(1.25rem, 2.9vw, 2.4rem); line-height: 1.16;
  letter-spacing: -0.03em; font-weight: 500;
  text-decoration: none; color: #fff;
  transition: color 240ms var(--ease), padding-left 320ms var(--ease);
}
.final-links a:hover, .final-links a:focus-visible { color: var(--red); padding-left: 0.5rem; }

.foot {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3) var(--s5);
  background: var(--graphite); color: var(--on-dark-2);
  padding: var(--s4) var(--gutter);
  padding-bottom: calc(var(--s4) + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--rule-light);
  font-size: 0.82rem;
}
.foot-contact { margin-right: auto; }
.foot a { color: var(--on-dark-2); text-decoration: none; border-bottom: 1px solid var(--rule-light); }
.foot a:hover { color: #fff; border-bottom-color: var(--red); }
.foot-logo { border: 0; }
.foot-logo img { width: 112px; height: auto; }

/* ── Scroll reveal ─────────────────────────────────────────────────────── */
/* transform + opacity only, so nothing reflows and CLS stays at 0 */
.js .reveal { opacity: 0; transform: translateY(20px); }
.js .reveal.in {
  opacity: 1; transform: none;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
/* The photograph is revealed by a vertical mask rather than a slide: the
   picture is uncovered top to bottom and never moves. clip-path animates on
   the compositor and changes no layout, so it cannot shift the page. */
.js .reveal-mask img { clip-path: inset(0 0 100% 0); }
.js .reveal-mask.in img {
  clip-path: inset(0 0 0 0);
  transition: clip-path 760ms var(--ease);
}
/* The quote arrives after the statement it belongs to. */
.js .intro-quote.reveal.in { transition-delay: 160ms; }

.js .hero-title .ln-in { display: block; transform: translateY(105%); }
.js .hero-title.in .ln-in {
  transform: none;
  transition: transform 900ms var(--ease);
}
.js .hero-title.in .ln:nth-child(2) .ln-in { transition-delay: 110ms; }

/* ── Desktop ───────────────────────────────────────────────────────────── */
@media (min-width: 760px) {
  /* 37/63. Never 50/50, never centred. */
  .intro-grid {
    grid-template-columns: 37fr 63fr;
    column-gap: clamp(2.5rem, 5vw, 5.5rem);
    row-gap: 0;
    align-items: start;
  }
  /* The quote rides up over the photograph's bottom edge. The negative margin
     is what produces the overlap; the orange marker is the part that actually
     crosses the image. Row 1's height is set by the photograph, which is
     deliberately the taller of the two columns. */
  /* -3rem pulls the BLOCK up over the photograph; the 3.25rem of padding puts
     the first line back below the image's bottom edge. So the orange marker
     crosses the picture and the letters never sit on it. Measured on the
     render, not assumed: marker top 853, image bottom 881, first glyph 904. */
  .intro-quote { margin-top: -3rem; padding-top: 3.25rem; }
  .side { grid-template-columns: 1.05fr 1fr; gap: clamp(2.5rem, 6vw, 7rem); }
  .side-text { padding-right: clamp(0rem, 3vw, 3rem); }
  .side-rev .side-media { order: 2; }
  .side-rev .side-text  { order: 1; padding-right: 0; padding-left: clamp(0rem, 3vw, 3rem); }
  .numbers-grid { grid-template-columns: repeat(2, 1fr); gap: var(--s6) var(--s5); }
  .team-grid { grid-template-columns: repeat(2, 1fr); gap: clamp(2.5rem, 5vw, 5rem); }
  .sides-title { margin-bottom: var(--s8); }
}
@media (min-width: 1100px) {
  .numbers-grid { grid-template-columns: repeat(4, 1fr); }
  .hero-media img { object-position: center 45%; }
}

/* ── Reduced motion ────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .js .reveal { opacity: 1; transform: none; }
  .js .hero-title .ln-in { transform: none; }
  .js .reveal-mask img, .js .reveal-mask.in img { clip-path: none; transition: none; }
  .js .intro-quote.reveal.in { transition-delay: 0s; }
  .hero-cue-arrow { animation: none; }
  .cities-bg .city-img { transform: none; }
  /* The colour still arrives on hover and on focus. Only the travel goes:
     the filter swaps instantly and the scale is dropped entirely. Colour is
     not motion, so removing it would remove the interaction, not the animation. */
  .person-portrait img { transition: none; }
  .person-portrait:hover img { transform: none; }
}

/* ── Print ─────────────────────────────────────────────────────────────── */
@media print {
  .hero, .cities, .numbers, .moment, .final, .foot { background: #fff; color: #000; }
  .hero-media, .cities-bg, .moment-media, .topbar { display: none; }
  .hero { min-height: auto; }
}

/* ── Touch targets ─────────────────────────────────────────────────────── */
/* iOS HIG minimum is 44px. Desktop keeps the tighter editorial rhythm; any
   pointer-coarse or narrow viewport grows every small link to 44px of height
   without changing the type size or the line it sits on. */
@media (hover: none), (max-width: 760px) {
  /* No hover on a touch screen, and on a narrow one the portrait is the whole
     column. A greyed portrait with no way to un-grey it is just a greyed
     portrait, so these viewports get the colour at rest. */
  .person-portrait img { filter: none; }

  .topbar-logo,
  .topbar-nav a,
  .cta,
  .foot a,
  .person-name a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
  .topbar-nav { gap: 1.1rem; }
  .cta { align-items: center; }
  .foot { gap: var(--s2) var(--s4); }
  .hero-cue { min-height: 44px; }
}
