@import url("/tokens.css");

/* ===========================================================================
   R5-MINOR · CUPERTINO RAIL · round 4 with every correction applied
   ===========================================================================

   This is r4-cupertino-rail after Jakob's review. The chassis, the palette,
   the card anatomy and the sticky folio are unchanged. What changed:

     HERO      taller. A 16:9 photograph in a 2.86:1 window was cropping the
               landing gear and the tail away, which is why the aircraft read
               small. Height, not width, is what gives the machine back.
     FOLIO     the 0N / 05 counter is gone. The label stays.
     ICONS     a sprite exists now. Round 4 had zero inline SVG.
     01        three industry cards with route diagrams, matched in height to
               the record rail. Real supporter logos replace the placeholder.
     02        the false "full-scale aircraft flown" claim is corrected
               (ADR-0012). Figures rail matched to the film. Timeline at six
               rows, flanked by two photographs at one height.
     03        an Investors invitation beside Pilot partners (ADR-0013).
     04        roles lose "Co-Founder", gain a line of record, become equal.
     05        "The latest." No press count, no explanation of what news is.
     06, 07    new: the questions, and the newsletter.

   ONE SECTION ANATOMY (round-2 rule 5) · identical in all seven sections:

     [folio] sticky: [mark] label
     [head]  a two-track row. LEFT (5 of 12): the h2, 40/48, weight 300.
             RIGHT (7 of 12): the section prose at 17/28, and under it that
             section's one action. Nothing else ever enters the head.
     [body]  one calm card group on a 12-column grid, 16px gutters. Cards are
             white, radius 20px, shadow level 2, padding 32px. Media fills its
             card corner to corner and the caption sits in the padded space
             below it. Column spans change per section, the card does not.
     Head to body is 32px (unrelated blocks), inside a card 16px (grouped).

   HEIGHTS · .body keeps align-items:start by default, because a short card
     beside a tall rail otherwise leaves 150px of empty white. Three rows now
     have to end flush, so they opt in with .body-match rather than the rule
     being flipped globally.

   PALETTE AND WCAG (computed pairs, AA or better everywhere)
     ink      #141414 on grey-50 #f8f9f7   17.44:1  AAA  h1, h2, labels
     ink      #141414 on white    #ffffff  18.42:1  AAA  card headings, values
     grey-700 #4a4d4a on white    #ffffff   8.57:1  AAA  prose in cards
     grey-700 #4a4d4a on grey-50  #f8f9f7   8.11:1  AAA  section prose
     grey-600 #6b6e6b on grey-50  #f8f9f7   4.89:1  AA   captions, months, roles
     grey-600 #6b6e6b on white    #ffffff   5.16:1  AA   captions inside cards
     green-700 #556f1c on grey-50 #f8f9f7   5.41:1  AA   link actions
     white    #ffffff on grey-950 #0b0d0a  19.51:1  AAA  the dark card
     white    #ffffff on grey-800 #2e302e  13.31:1  AAA  the invitation boxes
     grey-300 #cfd2cf on grey-800 #2e302e   8.73:1  AAA  invitation prose
     text-2   #4a4d4a on white              8.57:1  AAA  the legal-page prose
     grey-300 #cfd2cf on grey-950 #0b0d0a  12.80:1  AAA  dark-card prose
     green-400 #aaca5f on grey-950 #0b0d0a 10.52:1  AAA  the teaser accent rule
     ink      #141414 on green-500 #98bc40  8.42:1  AAA  the boxed action
     white    #ffffff on grey-900 #1c1d1c  16.91:1  AAA  footer
     grey-500 is never used for text anywhere. Nothing sits below 4.5:1.
     The hero headline is measured against composited pixels, never inspected.

   SIZE FLOORS · every label, caption, month and role is 16px computed. The two
     exceptions are mandated by the chassis: nav links and buttons at 14px.

   ICONS · 24x24 symbols, 1.5 stroke, no fill, currentColor. An icon takes the
     colour of the line it sits on, so ink on light, white on the dark card,
     grey in the figure rows, with no per-context variant. Green stays reserved
     for its two jobs (the teaser rule, the button) and is never an icon.
   ========================================================================= */

:root {
  /* semantic layer, mapped onto the primitive tokens */
  --ground: var(--grey-50);
  --card: #ffffff;
  --text: var(--ink);
  --text-2: var(--grey-700);
  --text-3: var(--grey-600);
  --hair: var(--grey-200);
  --edge: var(--grey-300);
  --accent: var(--green-700);
  --dark: var(--grey-950);
  --dark-2: var(--grey-900);
  --sans: "Albert Sans", "Helvetica Neue", Arial, sans-serif;
  --nav-h: 76px;
  --r-card: var(--r-6);
}

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

html { -webkit-text-size-adjust: 100% }

body {
  margin: 0;
  background: var(--ground);
  color: var(--text);
  font-family: var(--sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
}

img, video { max-width: 100% }
h1, h2, h3, p, dd, dl, ul, ol { margin: 0 }
ul, ol { padding: 0; list-style: none }
a { color: inherit }

/* Round 9, SEO. Sub-headings across the site were `<p class="card-h">` and
   `<p class="use-h">`, News year groups and Blog titles were paragraphs too, so
   two pages had no h2 at all. They are real headings now.

   The point of this rule: a heading marks a section of the DOCUMENT. Its level
   is never chosen for its size. The live Wix site does the opposite — nine <h6>
   used as section eyebrows, <h5> around spec values like "350 kg", and the
   caption "*conceptual render" marked <h2> four times a page — which is why the
   rebuild does not copy its heading pattern.

   h2/h3 inherit type here so the existing classes keep doing the styling and
   nothing moved a pixel when the tags changed. */
h2, h3 { font-size: inherit; font-weight: inherit; line-height: inherit }

/* the sprite is markup, not picture: it must be in the document for <use> to
   resolve, and must occupy nothing */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden }
.ic { width: 20px; height: 20px; flex: none }
/* Visually hidden, still in the accessibility tree. The e350 rows show an icon
   and a value only, so this is what stops a screen reader announcing "350 kg"
   with no idea what is 350 kg. Never display:none, which removes it for
   everyone. */
.vh {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.skip {
  position: absolute; left: var(--sp-4); top: -100px; z-index: 30;
  background: var(--ink); color: #fff; text-decoration: none;
  padding: var(--sp-3) var(--sp-6); border-radius: var(--r-4);
  font-size: var(--fs-announce); line-height: var(--lh-announce);
}
.skip:focus { top: var(--sp-4) }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--r-2);
}
.sec-dark :focus-visible, .foot :focus-visible,
.nav:not(.scrolled) :focus-visible { outline-color: var(--green-400) }

.wrap { width: min(1120px, 100% - 80px); margin-inline: auto }

/* --- nav: transparent over the hero, light and blurred once past it ------- */
.nav {
  position: fixed; inset: 0 0 auto; z-index: 20;
  height: var(--nav-h);
  transition: background var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
  border-bottom: 1px solid transparent;
}
.nav.scrolled {
  background: rgba(248, 249, 247, 0.88);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom-color: var(--hair);
}
.nav-in {
  width: min(1120px, 100% - 80px); margin-inline: auto; height: 100%;
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-8);
}
/* The lockup is horizontal now. Stacked, the wordmark's second line rendered
   at about nine pixels and was unreadable, which Jakob flagged. Laying the
   two parts side by side lets the mark grow AND the words grow, inside the
   same 76px nav. Both parts are the original artwork. */
.brand { display: flex; align-items: center; gap: var(--sp-4); min-height: 44px }
.brand-mark { display: block; width: auto; height: 42px }
.brand-word { display: block; width: auto; height: 26px }
.brand-dark { display: none }
.nav.scrolled .brand-light { display: none }
.nav.scrolled .brand-dark { display: block }

.nav-links { display: flex; align-items: center; gap: var(--sp-8) }
.nav-list { display: flex; align-items: center; gap: var(--sp-8) }
.nav-list a {
  display: flex; align-items: center; min-height: 44px;
  font-size: var(--fs-label); line-height: var(--lh-label);
  font-weight: 300; letter-spacing: 0.1em; text-transform: uppercase;
  text-decoration: none; color: rgba(255, 255, 255, 0.86);
  transition: color var(--dur-fast) var(--ease-out);
}
.nav-list a:hover { color: #fff; text-decoration: underline; text-underline-offset: 5px }
.nav.scrolled .nav-list a { color: var(--text-2) }
.nav.scrolled .nav-list a:hover { color: var(--text) }

.nav-toggle {
  display: none; min-height: 44px; padding: var(--sp-3) var(--sp-6);
  font: 300 var(--fs-label)/var(--lh-label) var(--sans);
  letter-spacing: 0.1em; text-transform: uppercase;
  color: #fff; background: none; border: 1px solid rgba(255, 255, 255, 0.5);
  border-radius: var(--r-4); cursor: pointer;
}
.nav.scrolled .nav-toggle { color: var(--text); border-color: var(--edge) }

/* --- actions: one boxed button, one link form, 2:1 padding ---------------- */
.btn {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: 44px; padding: var(--btn-pad-y) var(--btn-pad-x);
  font-size: var(--fs-button); line-height: var(--lh-button); font-weight: 500;
  letter-spacing: 0.04em; text-decoration: none;
  border-radius: var(--r-5); border: 1px solid transparent;
  background: var(--green-500); color: var(--ink);
  transition: background var(--dur-fast) var(--ease-out);
  cursor: pointer; font-family: var(--sans);
}
.btn:hover { background: var(--green-400) }
.btn-sm {
  padding: 12px 24px; background: none; color: #fff;
  border-color: rgba(255, 255, 255, 0.5);
}
/* Heavier on hover in both states. Jakob: "it does change the colour, but only
   very slightly — I would like just a tiny bit heavier dark contrast." It stays
   an outline button: he was asked whether it should become a filled primary and
   said no, so ADR-0007's quiet-contact lock holds and the prominence he wants
   for pilot partners and investors is carried by the two invitation boxes. */
.btn-sm:hover { background: rgba(255, 255, 255, 0.22); border-color: rgba(255, 255, 255, 0.85) }
.nav.scrolled .btn-sm { color: var(--text); border-color: var(--edge) }
.nav.scrolled .btn-sm:hover { background: var(--grey-300); border-color: var(--grey-600) }

.act { margin-top: var(--gap-grouped) }
.link {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: 44px; color: var(--accent); text-decoration: none;
  font-size: var(--fs-body); font-weight: 500;
}
.link:hover { text-decoration: underline; text-underline-offset: 4px }
.link.sm { font-size: var(--fs-announce) }
.gl { font-size: 0.9em }

/* --- type ---------------------------------------------------------------- */
/* 46rem, not the old 950px: the headline gained 12 characters, and at the old
   measure it broke to five lines and swallowed the hero. This holds it to
   four and still stops well inside the scrim's 64% plateau. */
.h1 {
  font-size: clamp(2.5rem, 5vw, 4rem); line-height: 1.125;
  font-weight: 300; letter-spacing: -0.022em; max-width: 46rem;
}
/* The h2 is a statement, not a label, so it runs two or three lines instead
   of one. It keeps the 40/48 display step and gains a max-width, so a long
   statement breaks on its own terms rather than filling the column. */
.h2 {
  font-size: clamp(1.75rem, 3.13vw, 2.5rem); line-height: 1.2;
  font-weight: 300; letter-spacing: -0.018em; max-width: 20ch;
  text-wrap: balance;
}
.lede { color: var(--text-2); max-width: 40em }
/* paragraphs are short and there are two in places, so they need the grouped
   gap between them (Jakob: no long paragraphs) */
.lede + .lede { margin-top: var(--gap-grouped) }
.credentials {
  margin-top: var(--gap-grouped);
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--text-3);
}

/* --- the award seals -----------------------------------------------------
   The awards used to be a grey run-on line beside the founding date, which
   made the two strongest credentials on the page read as small print. The
   real CEL and BNEW badge artwork is not on the live site, so this is a
   typographic seal in the house type. It swaps for real artwork without any
   other change: the year block and the two text lines keep their places. */
/* Unboxed. Jakob: "we don't need the box… otherwise it looks like we're just
   sprinkling boxes all around the website." Two hairline-separated rows that
   read as a continuation of the paragraph above them, with the awarding
   body's own mark hard right. */
.seals { margin-top: var(--gap-block) }
.seal {
  display: flex; align-items: center; gap: var(--sp-5);
  padding: var(--sp-5) 0; border-top: 1px solid var(--hair);
}
.seal:first-child { border-top: 0; padding-top: var(--sp-3) }
.seal-y {
  font-size: 1.375rem; line-height: 1.2; font-weight: 300; flex: none;
  letter-spacing: -0.01em; font-variant-numeric: tabular-nums;
  color: var(--text-3);
}
.seal-t {
  display: grid; gap: 2px;
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  font-weight: 500;
}
.seal-t em { font-style: normal; font-weight: 400; color: var(--text-3) }
/* the awarding bodies ship white-on-transparent artwork, same as the
   supporter set, so the same inversion applies */
.seal-logo {
  margin-left: auto; flex: none; display: block;
  width: auto; max-height: 34px; filter: invert(1); opacity: 0.72;
}

/* --- hero ----------------------------------------------------------------
   28rem made a 2.86:1 letterbox out of a 16:9 photograph at 1280, so only
   62% of the frame survived and the landing gear and tail were cut off. At
   52vw the window is 1.92:1 and 92% of the frame is visible, which is the
   whole aircraft. The crop can also relax from 38% back toward the middle,
   because there is far less to lose now. */
/* The headline sits on the FLOOR of the frame now, not the middle. Jakob:
   "let's move it down to the bottom of the video so that you can actually see
   the video." That hands the whole upper frame back to the aircraft, and it
   moves the type into a different part of the scrim, which is why the
   gradient below had to be re-tuned and re-measured. */
/* Taller again. Jakob: "it can even be a higher height because I know that the
   video in theory is higher, so it can cover even more of the screen." He is
   describing a measurement: at 1440 the hero was 704px where the full 16:9
   frame is 810px, so the page was cropping 13% of the picture off. 56.25vw IS
   16:9 of the viewport width, so at 1440 and below nothing is cropped at all;
   52rem caps it so an ultrawide desktop does not get a hero taller than a
   laptop screen. On a 1440x900 laptop that is 810px, 90% of the window. */
.hero {
  position: relative; min-height: clamp(30rem, 56.25vw, 52rem);
  display: flex; align-items: flex-end;
  padding: calc(var(--nav-h) + var(--sp-10)) 0 var(--sp-section);
  overflow: hidden;
}
.hero-img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 46%;
}
/* The film hero. Same box, same scrim, same crop discipline as the still. The
   video carries its own poster, so the frame is filled before a single byte
   of video arrives and there is no flash of empty box. --hero-scale is set by
   script.js from scroll position: the frame creeps in as the page moves, so
   the opener has depth rather than sitting flat behind the type. */
.hero-video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 46%;
  transform: scale(var(--hero-scale, 1));
  transform-origin: 50% 52%;
}
/* Two problems, one layer.
   The horizontal gradient carries the type: the left 64% gets the depth, and
   the right third, where the tail fin and its roundel sit, stays almost
   untouched. The vertical one is the nav's own band plus a floor, and its
   middle stop is deeper than round 4's because a taller hero drops the
   headline further down the frame. Every stop here is set by sampling the
   composited pixels under each line box, never by reading the gradient. */
.hero-scrim {
  position: absolute; inset: 0;
  background:
    linear-gradient(90deg,
      rgba(11, 13, 10, 0.58) 0%,
      rgba(11, 13, 10, 0.52) 64%,
      rgba(11, 13, 10, 0.16) 84%,
      rgba(11, 13, 10, 0) 100%),
    /* Re-tuned for the headline's move to the floor. The old curve bottomed
       out at 0.28 around 42% and only recovered to 0.36, which was fine under
       centred type and far too thin under type sitting at the bottom. It now
       thins where the aircraft is (38%) and deepens toward the floor where
       the words are. Every stop set by sampling composited pixels across ten
       frames of the loop, never by reading the gradient. */
    linear-gradient(180deg,
      rgba(11, 13, 10, 0.62) 0%,
      rgba(11, 13, 10, 0.52) 14%,
      rgba(11, 13, 10, 0.20) 38%,
      rgba(11, 13, 10, 0.36) 64%,
      rgba(11, 13, 10, 0.60) 100%);
}
.hero-in { position: relative; color: #fff }

/* --- section shell ------------------------------------------------------- */
.sec { padding-top: var(--sp-section) }
.sec-last { padding-bottom: var(--sp-section) }

/* --- the sticky folio ----------------------------------------------------
   Two of Jakob's asks turned out to be one mechanism. He wanted the section
   labels moved next to the logo, and he wanted Flight Log's sliding bar that
   carries the label down with you. A sticky folio row does both: it replaces
   the centred mark dividers AND travels inside its section before handing off
   to the next one.

   It sticks to the section, not the page, because each <section> is its own
   containing block. It must stay a direct child of .wrap: sticky inside a
   grid or flex child resolves against that child, which is the usual way
   this breaks.

   The 0N / 05 counter is gone. Jakob: "I don't want that there's any
   numbering. Don't need it on any website." */
/* It sticks flush to the nav, with no gap. Jakob: "the sections don't close
   directly to the top menu strip, so there is a small space — when you're
   scrolling, you always see the background still running through." He was
   reading a real 16px band: the folio stuck at nav-height PLUS sp-4 (92px)
   against a 76px bar, so a strip of live content scrolled past below the
   blurred glass with nothing over it. At exactly --nav-h the folio's own
   opaque ground meets the bar's bottom edge and the gap closes. */
.folio {
  position: sticky; z-index: 5;
  top: var(--nav-h);
  display: flex; align-items: center; gap: var(--sp-4);
  padding-top: var(--sp-3); margin-bottom: var(--gap-grouped);
  border-top: 1px solid var(--hair);
  /* the ground travels with it, so prose never shows through as it slides */
  background: var(--ground);
}
.folio-mark { height: 20px; width: auto; opacity: 0.55 }
.folio-label {
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  font-weight: 500; color: var(--text);
}

.head {
  display: grid; grid-template-columns: 5fr 7fr; gap: var(--gap-block);
  align-items: start;
}
.body {
  display: grid; grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--gap-grouped); margin-top: var(--gap-block);
  /* Cards keep their own height instead of stretching to the tallest in the
     row, because a short card beside a tall rail otherwise leaves 150px of
     empty white. Rows that must end flush opt in below. */
  align-items: start;
}
/* Jakob asked for four pairs of boxes to be exactly the same height. This is
   that, scoped: the industries against the record rail, the figures against
   the film, the three cards of the timeline row, and the three founders. */
.body-match { align-items: stretch }

/* Section actions moved out of the head and onto the floor of the section,
   hard right. Jakob: "I want that all these arrows and buttons are underneath
   the section on the right side, so in the lower right corner… I felt like on
   the lower right is more intuitive." He is right, and for a reason worth
   writing down: these three links all mean "continue", and continue belongs at
   the END of the reading path, not beside its beginning.

   `1 / -1` rather than a span class: .body is a 12-column grid but .body-team
   is a 3-column one, and -1 resolves to the last line of whichever it lands in.
   One rule covers all three sections and the News markup drops its span-12.
   align-items:center is here because .body-match stretches its children. */
.body > .act {
  grid-column: 1 / -1;
  display: flex; align-items: center; justify-content: flex-end;
}

.span-3 { grid-column: span 3 }
.span-4 { grid-column: span 4 }
.span-5 { grid-column: span 5 }
.span-6 { grid-column: span 6 }
.span-7 { grid-column: span 7 }
.span-8 { grid-column: span 8 }
.span-12 { grid-column: span 12 }

.card {
  background: var(--card); border: 1px solid var(--hair);
  border-radius: var(--r-card); box-shadow: var(--shadow-2);
  padding: var(--sp-8); margin: 0;
}
.card-media, .card-film, .card-person { padding: 0; overflow: hidden }
.card-media img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 55%;
}
/* the carry photograph puts its subject left of centre and low */
.card-media img.pos-carry { object-position: 45% 52% }

/* --- the rail: the right-hand information card ---------------------------
   The one thing carried over from round 3, and the only part of that round
   Jakob kept: "the twenty percent is actually only that right section." An
   ordinary Cupertino card, four of twelve columns, content stacked because
   the column is narrow.

   .rail-spread is the version that now has to match the height of the card
   beside it. Rather than sit short with a gap under it, its content spreads:
   the figure rows distribute, and the logo plate sits on the floor. */
.rail { align-content: start }
.rail-spread { display: flex; flex-direction: column }
.rail-spread .plate { margin-top: auto }
/* .gains joins .figs here rather than getting its own rule. Jakob had the
   mission rail's trailing traction line removed and asked to "spread the three
   points above out just slightly" — which is the job this idiom already does
   for the e350 figures in the next section. Same mechanism, one more selector. */
.rail-spread .figs, .rail-spread .gains {
  flex: 1; min-height: 0;
  display: flex; flex-direction: column; justify-content: space-evenly;
}
.card-h {
  display: flex; align-items: center; gap: var(--sp-3);
  font-weight: 500;
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  margin-bottom: var(--gap-grouped);
}

/* --- metrics: stacked, because they live in a 4-column rail -------------- */
.metrics { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-4) }
.metrics li {
  display: flex; flex-direction: column; gap: var(--sp-1);
  border-top: 1px solid var(--hair); padding-top: var(--sp-3);
}
.metrics li:first-child { border-top: 0; padding-top: 0 }
.m-n {
  font-size: 2.5rem; line-height: 1.2; font-weight: 300;
  letter-spacing: -0.02em; font-variant-numeric: tabular-nums;
}
/* The .m-w step-down for "Barcelona" is deleted. It was written on the
   assumption that a nine-letter word could not hold the column, and Jakob
   caught the result: "Barcelona appears to be not the same font and font size
   as the rest of the numbers." Measured — at 2.5rem it needs 174px and the
   rail's inner width is 297px. It always fit. */
.m-l {
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--text-3);
}

/* --- the supporter plate -------------------------------------------------
   These are the real files off the live Wix site, which shows them on a dark
   band, so every one of them is a white silhouette on transparency. On this
   ground they would be invisible. They are pure monochrome with an alpha
   channel, so invert(1) turns them black without touching transparency and
   opacity tunes them to the quiet grey the design wanted anyway. Had any
   carried brand colour, invert would have produced wrong hues and we would
   need real dark-variant artwork instead. ENISA is already black on white
   and opts out, blending its white ground into the plate with multiply. */
.plate {
  background: var(--grey-100); border: 1px solid var(--hair);
  border-radius: var(--r-5); padding: var(--sp-5);
  margin-top: var(--sp-6);
}
.plate-l {
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--text-3);
}
.logos {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-4) var(--sp-5); margin-top: var(--sp-4); align-items: center;
}
.logos li { display: flex; align-items: center; justify-content: center; min-height: 30px }
.logos img {
  display: block; width: auto; max-width: 100%; max-height: 30px;
  filter: invert(1); opacity: 0.68;
}
.logos img.no-invert { filter: none; opacity: 0.82; mix-blend-mode: multiply }

/* --- the e350's designed figures, inside the rail ------------------------
   Label left, value hard right, so the four values read as a column. Every
   number is already public on the live site, and the panel says outright that
   they are designed rather than measured (ADR-0010 amendment 2, ADR-0012). */
.figs .row {
  grid-template-columns: minmax(0, auto) minmax(0, 1fr); gap: var(--sp-4);
  padding: 12px 0;
}
.figs .row dt {
  display: flex; align-items: center; gap: var(--sp-3);
  color: var(--text-3);
}
.figs .row dt, .figs .row dd {
  font-size: var(--fs-announce); line-height: var(--lh-announce);
}
.figs .row dd {
  text-align: right; color: var(--text); font-variant-numeric: tabular-nums;
}
.figs-more {
  margin-top: var(--gap-grouped);
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--text-2);
}
/* the caveat is a different kind of statement from the figures above it, so it
   sits below a hairline rather than reading as one more grey line of them */
.figs-cap {
  margin-top: var(--sp-4); padding-top: var(--sp-3);
  border-top: 1px solid var(--hair);
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--text-3);
}
.row {
  display: grid; grid-template-columns: 180px 1fr; gap: var(--sp-6);
  padding: 18px 0; border-top: 1px solid var(--hair);
}
.row:first-child { border-top: 0 }
.row dt { font-weight: 500; color: var(--text) }
.row dd { color: var(--text-2) }

/* --- the industries ------------------------------------------------------
   Three definition-list rows became three cards. Jakob: "right now you really
   have to read everything." So each card carries an icon on its title and a
   route diagram of the movement it describes, and the reader can take the
   shape of it without reading a word. The stack matches the rail's height
   through .body-match plus three equal rows. */
.stack {
  display: grid; grid-template-rows: repeat(3, minmax(0, 1fr));
  gap: var(--gap-grouped);
}
/* align-content, not just align-items: the card is stretched to a third of
   the stack, and auto rows in a grid absorb that slack by growing, which
   opened a hole between the title and its sentence. Centring the row block
   keeps the two lines together and puts the slack outside them. */
.use {
  display: grid; grid-template-columns: minmax(0, 1fr) 232px;
  align-items: center; align-content: center;
  gap: var(--sp-3) var(--sp-8);
  padding: var(--sp-6) var(--sp-8);
}
/* THE INDUSTRY COLOUR CODE (ADR-0014, amends DESIGN.md's "green appears twice
   per page"). Three steps of the brand ramp, light to dark, so the three cases
   are told apart at a glance instead of by reading. It lives in exactly two
   places per card, the icon chip and the route, and nowhere else on the page.
   One hue in three depths rather than three hues: a second colour would need a
   new ramp in tokens.css and would push the page toward a dashboard. */
.u-cold       { --tint: var(--green-100); --tone: var(--green-800) }
.u-industrial { --tint: var(--green-200); --tone: var(--green-900) }
.u-consumer   { --tint: var(--green-300); --tone: var(--green-950) }

.use-h {
  grid-column: 1; display: flex; align-items: center; gap: var(--sp-4);
  font-weight: 500;
}
.chip {
  display: flex; align-items: center; justify-content: center; flex: none;
  width: 40px; height: 40px; border-radius: var(--r-4);
  background: var(--tint); color: var(--tone);
}
.use-p {
  grid-column: 1; align-self: start;
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--text-2); max-width: 44ch;
}
.route {
  grid-column: 2; grid-row: 1 / span 2;
  display: block; width: 232px; max-width: 100%; height: auto;
  color: var(--text-2);
}
.route-arc {
  fill: none; stroke: currentColor; stroke-width: 1.4;
  stroke-dasharray: 3 4; stroke-linecap: round; opacity: 0.85;
}
/* what flies is the load, not an aircraft glyph, so it carries the card's tone */
.route-load { color: var(--tone, var(--text)) }

/* --- the mission ---------------------------------------------------------
   Same anatomy as "what we built": a wide image on eight columns, the reading
   matter on the rail. The three gains are the value proposition in the order
   Jakob says it, money then time then emissions. */
/* A captioned media card cannot use .card-media's `height: 100%` on the image.
   That rule exists for figures holding nothing but a picture (the wheel, the
   carry shot), where filling the stretched row is exactly right. Here it gave
   the image the whole figure and pushed the caption past `overflow: hidden`,
   where it vanished. The image takes what is left instead. */
.card-caption { display: flex; flex-direction: column }
.card-caption img {
  flex: 1; min-height: 0; height: auto;
  object-position: 50% 54%;
}
.card-caption figcaption {
  padding: var(--sp-4) var(--sp-6) var(--sp-5);
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--text-3);
}
.gains { display: grid; gap: var(--sp-5) }
.gains li { display: flex; align-items: flex-start; gap: var(--sp-4) }
.gains .ic { margin-top: 2px; color: var(--text-3) }
.g-t {
  display: grid; gap: 2px; font-weight: 500;
  font-size: var(--fs-announce); line-height: var(--lh-announce);
}
.g-t em { font-style: normal; font-weight: 400; color: var(--text-2) }

/* --- the click-to-play facade -------------------------------------------
   The broadcast report lives on YouTube and self-hosting it is not ours to
   do. A poster and a button stand in until the button is pressed, so the
   page makes no third-party request at all unless the visitor asks for one.
   script.js swaps in the iframe. */
.facade { position: relative; display: block; background: var(--grey-950) }
.facade img {
  display: block; width: 100%; height: auto; aspect-ratio: 16 / 9;
  object-fit: cover;
}
.facade-btn {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-4); padding: var(--sp-6);
  font: 500 var(--fs-announce)/var(--lh-announce) var(--sans);
  color: #fff; text-align: center; cursor: pointer;
  background: rgba(11, 13, 10, 0.42); border: 0;
  transition: background var(--dur-base) var(--ease-out);
}
.facade-btn:hover { background: rgba(11, 13, 10, 0.24) }
.facade-ic {
  width: 30px; height: 30px; flex: none; padding: 14px 12px 14px 16px;
  box-sizing: content-box;
  border: 1px solid rgba(255, 255, 255, 0.75); border-radius: 50%;
  transition: background var(--dur-fast) var(--ease-out);
}
.facade-btn:hover .facade-ic { background: rgba(255, 255, 255, 0.16) }
.facade iframe { display: block; width: 100%; aspect-ratio: 16 / 9; border: 0 }

/* --- the supporter band --------------------------------------------------
   No bottom padding. Jakob: "it feels like more space underneath the logos than
   underneath the other sections." It was: 48px of band padding-bottom PLUS the
   next section's own 64px of padding-top, so this one boundary was 112px where
   every other boundary on the page is 64px. The next section already supplies
   the gap, so the band stops adding a second one. */
.band { padding: var(--sp-section) 0 0 }
.band-l {
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--text-3); text-align: center;
  padding-top: var(--sp-6); border-top: 1px solid var(--hair);
}
.band-logos {
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--sp-8) var(--sp-6); margin-top: var(--sp-7);
}
.band-logos img { max-height: 38px; opacity: 0.55 }
.band-logos img.no-invert { opacity: 0.72 }
.band-logos li { min-height: 42px }
/* Climate KIC and UPM are two ordinary cells now (Jakob, round 8). The shared
   cell and its hairline are gone; what stays is their own height caps, because
   max-height equalises HEIGHT and the eye reads AREA. Measured at the band's
   38px the wide wordmarks rendered 5,100-7,800px² while the Climate KIC lockup
   (1.42:1) came out at 2,050 and UPM at 2,673 — the two smallest things in the
   band by a factor of two. These bring both to the ~5,300 median, and the two
   numbers differ from each other because the squarer mark needs more height to
   weigh the same.

   Scoped to .band-logos on purpose: `.band-logos img` is 0,1,1 and a bare
   `.lg-ck` would be 0,1,0, so the 38px cap would win and the fix would silently
   do nothing. (The old `.pair img:first-child` cleared it at 0,2,1.) */
.band-logos .lg-ck  { max-height: 60px }   /* 1.42:1 -> 5,112px² */
.band-logos .lg-upm { max-height: 52px }   /* 1.98:1 -> 5,366px² */

/* --- film and the record ------------------------------------------------- */
.card-film video {
  display: block; width: 100%; height: auto; aspect-ratio: 16 / 9;
  background: var(--grey-100);
}
.card-film figcaption {
  padding: var(--sp-4) var(--sp-6) var(--sp-5);
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--text-3);
}
/* Six rows, and no card. Jakob: "I don't want the timeline to be within a box…
   that box is a little bit too much." It sits directly on the ground between
   the two photographs, still setting the row height that both of them fill.
   Losing the card means losing the card's padding too, so the rows get their
   own inset and a hover tint that reaches past the text. */
.miles {
  padding: var(--sp-2) var(--sp-6);
  display: flex; flex-direction: column; justify-content: center;
}
.miles li {
  display: flex; align-items: baseline; gap: var(--sp-4);
  padding: 18px var(--sp-4); margin: 0 calc(var(--sp-4) * -1);
  border-top: 1px solid var(--hair); border-radius: var(--r-3);
  transition: background var(--dur-fast) var(--ease-out);
}
.miles li:first-child { border-top: 0 }
/* tint, not weight: Jakob suggested "the text becomes bold or something like
   this", and bold reflows the line, which reads as a jitter rather than a
   response. The tint and the year darkening do the same job without moving
   a single glyph. */
.miles li:hover { background: var(--grey-100) }
.miles li:hover .mi-y { color: var(--text) }
.mi-y { min-width: 48px; color: var(--text-3); font-variant-numeric: tabular-nums }
.mi-t { color: var(--text) }

/* --- the one dark object ------------------------------------------------- */
.dark-card {
  background: var(--dark); color: #fff;
  border-radius: var(--r-card); padding: var(--sp-10);
}
.dark-card .h2 { color: #fff }
.dark-card .lede { color: var(--grey-300) }
.teaser {
  padding-left: var(--sp-6); border-left: 1px solid var(--green-400);
  font-size: var(--fs-sub); line-height: var(--lh-sub); color: #fff;
  max-width: 68ch;
}
/* Two invitations, side by side and equal: pilot partners, and investors.
   The second reverses ADR-0010 amendment 3 on Jakob's instruction (ADR-0013).

   Lifted a full step in round 8. Jakob: "they should be even more light… pilot
   partners and investors potentially could stand out even a bit more." He also
   chose to keep the nav's Contact us button quiet, which means these two boxes
   carry the whole job of saying the company wants to be contacted.

   grey-800 over the grey-950 card raises the surface separation from 1.09:1 to
   1.46:1, and the prose steps grey-400 -> grey-300 with it, measured at 8.73:1
   so the card's AAA claim survives. Both are real stops from tokens.css, which
   allows no others; grey-900 was the obvious pick and is exactly the colour the
   card itself wears before it reveals, so the boxes would vanish for 560ms
   every time the section arrived. */
.invite {
  display: flex; flex-direction: column;
  background: var(--grey-800);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--r-5);
  padding: var(--sp-6) var(--sp-7) var(--sp-7);
}
.invite .card-h { color: #fff }
.invite-p {
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--grey-300);
}
/* Row gap only. Jakob: "the two boxes could be the same distance from the text
   above as the text above is from the section above" — head-to-teaser is 32px
   and teaser-to-boxes was 16px. Setting `gap` would have widened the space
   BETWEEN the two boxes by the same amount, which is not what he asked for. */
.dark-card .body { row-gap: var(--gap-block) }
.invite .act { margin-top: auto; padding-top: var(--sp-5) }

/* --- the team ------------------------------------------------------------
   "Co-Founder" three times said nothing the heading had not already said. The
   role is the role now, and one line of record underneath makes the three
   cards equal, which is what Jakob actually noticed. */
/* full width, not 83.3%: at ten of twelve columns there was dead space to the
   right of Miguel, which Jakob read as the row being misplaced against the
   rest of the page */
.body-team { grid-template-columns: repeat(3, minmax(0, 1fr)) }
.card-person { display: flex; flex-direction: column }
.card-person img {
  display: block; width: 100%; height: auto; aspect-ratio: 1 / 1;
  object-fit: cover; object-position: 50% 26%;
}
.p-name { margin-top: var(--sp-5); padding: 0 var(--sp-6); font-weight: 500 }
.p-role {
  padding: 0 var(--sp-6);
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--text-3);
}
.p-cred {
  margin-top: var(--sp-2); padding: 0 var(--sp-6);
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--text-2);
}
.card-person .act { margin: auto 0 0; padding: var(--sp-4) var(--sp-6) var(--sp-5) }

/* --- news ---------------------------------------------------------------- */
.news { padding: var(--sp-6) var(--sp-8) var(--sp-2) }
.yr { font-weight: 500; font-variant-numeric: tabular-nums }
.items li a {
  display: flex; align-items: center; gap: var(--sp-5); min-height: 52px;
  border-top: 1px solid var(--hair); text-decoration: none;
}
.items { margin-top: var(--sp-2) }
.it-m { min-width: 44px; color: var(--text-3) }
.it-t { color: var(--text) }
.items li a:hover .it-t { text-decoration: underline; text-underline-offset: 4px }
.yr-group { margin-top: var(--sp-6) }
.yr-group summary {
  display: flex; align-items: center; gap: var(--sp-3); min-height: 52px;
  border-top: 1px solid var(--hair); cursor: pointer; list-style: none;
}
.yr-group summary::-webkit-details-marker { display: none }
.yr-group summary::after {
  content: ""; width: 8px; height: 8px; margin-left: var(--sp-1);
  border-right: 1.5px solid var(--text-3); border-bottom: 1.5px solid var(--text-3);
  transform: rotate(45deg) translateY(-2px);
  transition: transform var(--dur-fast) var(--ease-out);
}
.yr-group[open] summary::after { transform: rotate(-135deg) translateY(-2px) }

/* --- the questions -------------------------------------------------------
   Same <details> mechanism as the news years, so the page has one disclosure
   behaviour rather than two. The chevron is the same two rotated borders. */
.faq { padding: var(--sp-2) var(--sp-8) }
.q { border-top: 1px solid var(--hair) }
.q:first-child { border-top: 0 }
.q summary {
  display: flex; align-items: center; gap: var(--sp-4); min-height: 60px;
  cursor: pointer; list-style: none;
}
.q summary::-webkit-details-marker { display: none }
.q summary::after {
  content: ""; width: 8px; height: 8px; margin-left: auto; flex: none;
  border-right: 1.5px solid var(--text-3); border-bottom: 1.5px solid var(--text-3);
  transform: rotate(45deg) translateY(-2px);
  transition: transform var(--dur-fast) var(--ease-out);
}
.q[open] summary::after { transform: rotate(-135deg) translateY(-2px) }
.q-t { font-weight: 500 }
.q-a { padding-bottom: var(--sp-6); max-width: 62ch }
.q-a p { color: var(--text-2) }

/* --- the newsletter ------------------------------------------------------ */
.sub { padding: var(--sp-8) }
.sub-form { max-width: 620px }
.sub-label {
  display: flex; align-items: center; gap: var(--sp-3);
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  font-weight: 500;
}
.sub-row {
  display: flex; flex-wrap: wrap; gap: var(--gap-grouped);
  margin-top: var(--sp-3);
}
.sub-input {
  flex: 1 1 16rem; min-width: 0; min-height: 52px; padding: 0 var(--sp-5);
  font: 400 var(--fs-body)/var(--lh-body) var(--sans);
  color: var(--text); background: var(--card);
  border: 1px solid var(--edge); border-radius: var(--r-5);
}
.sub-input::placeholder { color: var(--text-3) }
.sub-form .btn { min-height: 52px }
/* off-screen rather than display:none, and out of the tab order */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden }
.sub-note {
  margin-top: var(--sp-4);
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--text-3);
}
.sub-ok {
  margin-top: var(--sp-3);
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  font-weight: 500; color: var(--accent);
}

/* --- footer: dark and physical ------------------------------------------ */
.foot {
  background: var(--dark-2); color: #fff;
  padding: var(--sp-section-sm) 0 var(--sp-10);
}
.foot-cols {
  display: grid; grid-template-columns: 4fr 3fr 2fr 3fr; gap: var(--gap-block);
}
.f-col { align-content: start }
.f-brand img { display: block; width: auto; height: 34px }
.f-brand p {
  margin-top: var(--sp-4); max-width: 22em;
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--grey-400);
}
.f-h {
  min-height: 22px; font-size: var(--fs-announce); line-height: var(--lh-announce);
  font-weight: 500; color: #fff;
}
.f-col ul { margin-top: var(--sp-1) }
.f-col li a {
  display: flex; align-items: center; gap: var(--sp-2); min-height: 44px;
  font-size: var(--fs-announce); color: var(--grey-400); text-decoration: none;
}
.f-col li a:hover { color: #fff; text-decoration: underline; text-underline-offset: 4px }
.foot-legal {
  margin-top: var(--sp-6); padding-top: var(--sp-6);
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--grey-400);
}

/* ===========================================================================
   HOVER AND FOCUS · one vocabulary, applied everywhere something responds
   ===========================================================================
   Jakob: "anything that I actually can click should be responsive in some way…
   if I go on the latest and I'm hovering over the arrow next to 2025, I need
   to click on the arrow so it actually unfolds, but I should actually see
   something when I'm hovering over it." And, in the same breath: "not to do
   this randomly or just too much."

   So there are exactly three responses on the whole page, not one per element:
     LIFT   a card rises 2px and its shadow deepens          (cards)
     TINT   a row takes the grey-100 wash                    (rows, disclosures)
     LEAN   a glyph moves 2px the way it would take you      (arrows, chevrons)

   Two rules that make this safe rather than decorative:
   1. Only transform, opacity, box-shadow and background are animated. All four
      are compositor-friendly, so none of this can cause a reflow.
   2. Every hover has a matching :focus-visible, because an affordance a
      keyboard cannot see is not an affordance. The transform half is gated on
      reduced motion; the colour half is not, so the response survives that
      preference rather than disappearing with it.
   ========================================================================= */
.card-person,
.use,
.invite,
.card-media,
.card-film { transition: box-shadow var(--dur-base) var(--ease-out),
                         transform var(--dur-base) var(--ease-out),
                         border-color var(--dur-base) var(--ease-out) }

.card-person:hover, .use:hover, .card-media:hover, .card-film:hover {
  box-shadow: var(--shadow-3); border-color: var(--edge);
}
.invite:hover { border-color: rgba(255, 255, 255, 0.26) }

/* rows and disclosures take the tint. The news years and the questions had no
   hover at all, which is exactly the case Jakob named. */
.yr-group summary, .q summary, .items li a {
  border-radius: var(--r-3);
  padding-inline: var(--sp-4); margin-inline: calc(var(--sp-4) * -1);
  transition: background var(--dur-fast) var(--ease-out);
}
.yr-group summary:hover, .q summary:hover, .items li a:hover,
.yr-group summary:focus-visible, .q summary:focus-visible, .items li a:focus-visible {
  background: var(--grey-100);
}
.yr-group summary::after, .q summary::after {
  transition: transform var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
}
.yr-group summary:hover::after, .q summary:hover::after,
.yr-group summary:focus-visible::after, .q summary:focus-visible::after {
  border-color: var(--text);
}

/* the logos come up out of the wash when you look at one */
.logos img { transition: opacity var(--dur-fast) var(--ease-out) }
.logos li:hover img { opacity: 1 }

/* actions lean the way they would take you */
.link .gl, .btn .gl { transition: transform var(--dur-fast) var(--ease-out) }

/* --- motion ------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  .hero-in > * { animation: rise var(--dur-slow) var(--ease-out) both }
  .h1 { animation-delay: 60ms }

  /* the movement half of the hover vocabulary. The colour half lives outside
     this guard on purpose, so someone who has asked for less motion still
     gets told what is clickable. */
  .card-person:hover, .use:hover, .card-media:hover, .card-film:hover {
    transform: translateY(-2px);
  }
  .link:hover .gl, .btn:hover .gl { transform: translateX(3px) }
  .link.sm:hover .gl { transform: translate(2px, -2px) }

  .js .sec > .wrap > .head,
  .js .sec > .wrap > .body > *,
  .js .dark-card {
    opacity: 0; transform: translateY(10px);
    transition: opacity var(--dur-base) var(--ease-out),
                transform var(--dur-base) var(--ease-out),
                background var(--dur-slow) var(--ease-out);
  }
  .js .dark-card { background: var(--dark-2) }
  .js .is-in > .wrap > .head,
  .js .is-in > .wrap > .body > *,
  .js .is-in .dark-card {
    opacity: 1; transform: none;
  }
  .js .is-in .dark-card { background: var(--dark) }
  .js .is-in > .wrap > .body > *:nth-child(2) { transition-delay: 60ms }
  .js .is-in > .wrap > .body > *:nth-child(3) { transition-delay: 120ms }
  .js .is-in > .wrap > .body > *:nth-child(4) { transition-delay: 180ms }
  .js .is-in > .wrap > .body > *:nth-child(5) { transition-delay: 240ms }

  /* ---- the major version's extra choreography -------------------------
     Jakob, on Manna's site: "the whole website is a motion website where
     everything is unfolding as you scroll." Three additions, each one tied
     to something the page is already saying rather than decoration.

     1. The route dashes travel their path once as the card arrives, so the
        diagram reads as movement instead of a static picture. The dash
        period is 7 units (3 on, 4 off), so 42 is exactly six dashes of
        travel and the animation lands on the pattern it started from. */
  .js .is-in .route-arc { animation: flow 2.4s var(--ease-out) both }

  /*  2. The timeline builds a row at a time, which is what a timeline is. */
  .js .is-in .miles li { animation: rise 480ms var(--ease-out) both }
  .js .is-in .miles li:nth-child(1) { animation-delay: 120ms }
  .js .is-in .miles li:nth-child(2) { animation-delay: 200ms }
  .js .is-in .miles li:nth-child(3) { animation-delay: 280ms }
  .js .is-in .miles li:nth-child(4) { animation-delay: 360ms }
  .js .is-in .miles li:nth-child(5) { animation-delay: 440ms }
  .js .is-in .miles li:nth-child(6) { animation-delay: 520ms }

  /*  3. The supporter band arrives as one object rather than ten. */
  .js .band .band-logos { opacity: 0; transition: opacity var(--dur-slow) var(--ease-out) }
  .js .band.is-in .band-logos { opacity: 1 }
}
@keyframes flow {
  from { stroke-dashoffset: 42 }
  to { stroke-dashoffset: 0 }
}

/* DESIGN.md's rule is "no animation, no transition, nothing withheld" under
   reduced motion. The reveal system already honours that by living inside the
   no-preference guard, but the chrome transitions predate it: the nav fade,
   the button fills, the disclosure chevrons and now the hover vocabulary all
   sat outside. This neutralises the DURATION of every one of them while
   leaving the end states intact, so a visitor who asked for less motion still
   sees which things respond, they just arrive instantly. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }
}
@keyframes rise {
  from { opacity: 0; transform: translateY(14px) }
  to { opacity: 1; transform: none }
}

/* --- responsive: 320 to 1440, no overflow ------------------------------- */
@media (max-width: 1080px) {
  .wrap, .nav-in { width: min(1120px, 100% - 48px) }
  .head { grid-template-columns: 1fr; gap: var(--gap-grouped) }
  .h2 { max-width: 28ch }
  .span-3, .span-4, .span-5, .span-6, .span-7, .span-8 { grid-column: span 12 }
  .card-media img { aspect-ratio: 3 / 2; height: auto }
  .body-team { max-width: none }
  /* the stack is full width now, so its rows size to their own content */
  .stack { grid-template-rows: auto }
  /* four across instead of two, because the rail is the page width here */
  .logos { grid-template-columns: repeat(4, minmax(0, 1fr)) }
  .rail-spread .figs, .rail-spread .gains { display: block }
  .rail-spread .gains > li + li { margin-top: var(--sp-5) }
  .rail-spread .plate { margin-top: var(--sp-6) }
  .band-logos { grid-template-columns: repeat(4, minmax(0, 1fr)) }
}
@media (max-width: 880px) {
  .nav-toggle { display: inline-flex; align-items: center; justify-content: center }
  .nav-links {
    position: absolute; inset: var(--nav-h) 0 auto; flex-direction: column;
    align-items: flex-start; gap: var(--sp-2);
    background: rgba(248, 249, 247, 0.97); backdrop-filter: blur(20px);
    border-bottom: 1px solid var(--hair); padding: var(--sp-4) 40px var(--sp-6);
  }
  .nav-links[hidden] { display: none }
  .nav-list { flex-direction: column; align-items: flex-start; gap: 0 }
  .nav-list a, .nav.scrolled .nav-list a { color: var(--text-2) }
  .btn-sm, .nav.scrolled .btn-sm { color: var(--text); border-color: var(--edge) }
  .foot-cols { grid-template-columns: 1fr 1fr }
}
@media (max-width: 640px) {
  .wrap, .nav-in { width: calc(100% - 40px) }
  .row { grid-template-columns: 1fr; gap: var(--sp-1) }
  /* the figure rows keep label-left value-right: two short strings, so
     stacking them would waste four lines for nothing */
  .figs .row { grid-template-columns: minmax(0, auto) minmax(0, 1fr); gap: var(--sp-4) }
  .body-team { grid-template-columns: 1fr }
  .card, .miles, .news, .faq, .sub { padding-left: var(--sp-6); padding-right: var(--sp-6) }
  .dark-card { padding: var(--sp-8) var(--sp-6) }
  .nav-links { padding-left: 20px; padding-right: 20px }
  .foot-cols { grid-template-columns: 1fr }
  /* the route diagram drops under its text rather than squeezing beside it */
  .use { grid-template-columns: minmax(0, 1fr); gap: var(--sp-4) }
  .use-h, .use-p, .route { grid-column: 1 }
  .route { grid-row: auto }
  .logos { grid-template-columns: repeat(2, minmax(0, 1fr)) }
  .band-logos { grid-template-columns: repeat(2, minmax(0, 1fr)) }
  .seal { flex: 1 1 100% }
}
/* NOT on Jakob's round-7 list — found while checking 320px, pre-existing since
   round 6. The horizontal lockup needs 240px and the Menu button 95px, so below
   about 410px of viewport they no longer both fit. The wordmark is an <img>, so
   it does not shrink to fit: it overflowed .brand and printed straight across
   the Menu button.

   The mark alone carries the brand at this width, which is what almost every
   site does on a phone, and nothing is lost to a screen reader because the
   accessible name lives on the <a> ("Grasshopper Air Mobility, home"). Shrinking
   the wordmark instead was the other option and is the wrong one: it would put
   the "AIR MOBILITY" line back under nine pixels, which is the exact complaint
   that produced this lockup in round 6. */
@media (max-width: 430px) {
  /* .nav.scrolled .brand-dark sets display:block at 0,3,0, so a bare
     .brand-word would lose to it the moment the nav inverts and the wordmark
     would come back on scroll. This ties on specificity and wins on order. */
  .brand-word, .nav.scrolled .brand-word { display: none }
}

/* ===========================================================================
   THE SUBPAGES · round 8
   ===========================================================================
   Seven pages join Home on this same stylesheet: e350, Company, News, Contact,
   Blog, Privacy, Cookies. They reuse the chassis whole — folio, head, body, the
   card anatomy, .miles, .items/.yr-group, .facade, the hover vocabulary and the
   reveal system all work unchanged. What follows is only what Home had no need
   of, because Home opens on a hero and none of them do.
   ========================================================================= */

/* The page opener. Home's hero supplies its own clearance under the fixed bar;
   a subpage that starts on the light ground has to make its own, or the h1
   renders behind the nav. */
.page-top { padding-top: calc(var(--nav-h) + var(--sp-section)) }
.page-h {
  font-size: clamp(2.25rem, 4.2vw, 3.25rem); line-height: 1.14;
  font-weight: 300; letter-spacing: -0.02em; max-width: 22ch;
  text-wrap: balance;
}
.page-lede {
  margin-top: var(--gap-block);
  font-size: var(--fs-sub); line-height: var(--lh-sub);
  color: var(--text-2); max-width: 46ch;
}
/* the opener is a two-track row like .head, but the left track carries an h1 */
.page-intro {
  display: grid; grid-template-columns: 5fr 7fr; gap: var(--gap-block);
  align-items: start;
}

/* Long-form legal text. The only genuinely new component on any of the seven.
   68ch because these run to thousands of words and the card's own 12-column
   grid would otherwise set them at 1,120px, which is roughly twice a readable
   measure. Numbered clauses keep the source's own numbering — never renumber
   legal text, the clause numbers are referenced from inside the document. */
/* The measure caps the TEXT, not the card. Capping the card left a 640px white
   panel floating in a 1,120px grid, which reads as a layout mistake; the FAQ
   card already does it this way with .q-a's 62ch. */
.prose { padding: var(--sp-8) }
.prose > * { max-width: 68ch }
.prose > * + * { margin-top: var(--gap-grouped) }
.prose h2 {
  margin-top: var(--sp-10); font-size: 1.375rem; line-height: 1.3;
  font-weight: 500; letter-spacing: -0.01em;
}
.prose h2:first-child { margin-top: 0 }
.prose p, .prose li { color: var(--text-2) }
.prose ul { margin-top: var(--gap-grouped) }
.prose li {
  position: relative; padding-left: var(--sp-6); margin-top: var(--sp-3);
}
/* a real bullet, not a list-style: the reset stripped list-style globally and
   putting it back here would fight the reset everywhere else */
.prose li::before {
  content: ""; position: absolute; left: 6px; top: 11px;
  width: 4px; height: 4px; border-radius: 50%; background: var(--edge);
}
/* `anywhere`, not `break-word`: it also shrinks the element's min-content size,
   which is what actually stops the grid track being forced wide. An address
   like dataprotection@grasshopperairmobility.com measures 349px and pushed the
   document to 394px inside a 320px viewport.

   The inline links in these paragraphs are under 44px tall and that is correct:
   WCAG 2.5.8 exempts targets "in a sentence or block of text". Do not pad them
   to 44px — it would tear the prose apart for no accessibility gain. */
.prose a {
  color: var(--accent); text-decoration: underline; text-underline-offset: 3px;
  overflow-wrap: anywhere;
}
.prose strong { font-weight: 500; color: var(--text) }
.prose .lead { color: var(--text-3); font-size: var(--fs-announce); line-height: var(--lh-announce) }
/* the live legal text carries defects; they are reproduced, and marked */
.prose .defect { border-left: 2px solid var(--green-300); padding-left: var(--sp-4) }

/* Contact: one row per address. Not a form — ADR-0006 locked this page as a
   sentence, a button and a plain address, and Jakob confirmed it in round 8. */
.chans { display: grid; gap: var(--gap-grouped) }
.chan {
  display: flex; align-items: center; gap: var(--sp-5);
  padding: var(--sp-5) var(--sp-6);
  background: var(--card); border: 1px solid var(--hair);
  border-radius: var(--r-5); box-shadow: var(--shadow-1);
  text-decoration: none;
  transition: box-shadow var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.chan:hover, .chan:focus-visible { box-shadow: var(--shadow-3); border-color: var(--edge) }
.chan .ic { color: var(--text-3) }
.chan-t { display: grid; gap: 2px; font-weight: 500 }
.chan-t em {
  font-style: normal; font-weight: 400; color: var(--text-2);
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  /* partnerships@grasshopperairmobility.com is 312px of unbreakable string */
  overflow-wrap: anywhere;
}
.chan .gl { margin-left: auto; color: var(--accent); transition: transform var(--dur-fast) var(--ease-out) }

/* Blog: the list of posts. Same row rhythm as the news items, plus a summary
   line, so the two archives read as one family. */
.posts { display: grid; gap: var(--sp-2) }
.post-row {
  display: block; padding: var(--sp-5) var(--sp-4); margin-inline: calc(var(--sp-4) * -1);
  border-top: 1px solid var(--hair); border-radius: var(--r-3);
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease-out);
}
.posts > :first-child { border-top: 0 }
.post-row:hover, .post-row:focus-visible { background: var(--grey-100) }
/* both blocks: they are spans inside a block <a>, so without this the slug runs
   on from the title instead of sitting under it */
.post-t { display: block; font-weight: 500 }
.post-row:hover .post-t { text-decoration: underline; text-underline-offset: 4px }
.post-u {
  display: block;
  margin-top: 2px; font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--text-3); font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

/* The chained "next" footer (ADR-0006). Every subpage ends by handing over to
   the next one rather than dead-ending. */
.next {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--gap-block); flex-wrap: wrap;
  margin-top: var(--sp-section); padding-top: var(--sp-6);
  border-top: 1px solid var(--hair);
}
.next-l { font-size: var(--fs-announce); line-height: var(--lh-announce); color: var(--text-3) }
.next-t { font-size: 1.375rem; line-height: 1.3; font-weight: 300; letter-spacing: -0.01em }

@media (max-width: 1080px) {
  .page-intro { grid-template-columns: 1fr; gap: var(--gap-grouped) }
  .page-h { max-width: 28ch }
}
@media (max-width: 640px) {
  .prose { padding: var(--sp-6) }
  .chan { padding: var(--sp-4) var(--sp-5) }
}

/* ===========================================================================
   ROUND 9 · the advisers, the archive previews, and a smaller contact row
   ========================================================================= */

/* Advisers and the collaborator. Five people, no photographs available, sitting
   in the same row shape as the three founders — so the card needs a plate that
   carries initials instead of a face. Anything else (a grey box, a generic
   silhouette) reads as a missing image rather than a deliberate one. */
.card-person.no-photo .p-plate {
  display: flex; align-items: center; justify-content: center;
  aspect-ratio: 1 / 1; background: var(--grey-100);
  border-bottom: 1px solid var(--hair);
  font-size: 2.5rem; font-weight: 300; letter-spacing: 0.06em;
  color: var(--text-3);
}
/* the role line carries Advisor vs Collaborator, so it has to read as a label.
   "Advisor", with an o, from round 10 — Jakob's spelling. The section id stays
   `#advisers` because it is not published text and renaming it would only risk
   a link nobody has checked. */
.p-kind {
  padding: 0 var(--sp-6); margin-top: var(--sp-5);
  font-size: var(--fs-label); line-height: var(--lh-label);
  font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--accent);
}
.card-person.no-photo .p-name { margin-top: var(--sp-2) }
.body-people { grid-template-columns: repeat(5, minmax(0, 1fr)) }

/* Archive rows with a preview. News and Blog share this so the two archives
   read as one family (Jakob: the blog rows should work "similarly to the news").
   The thumbnail is optional on purpose — the ten 2024 news items have no image
   anywhere on the live site, so a row without one has to look intended rather
   than broken. Grid, not flex: `auto` collapses the media column to nothing when
   there is no <img>, and the text simply takes the full width. */
.arc { display: grid; gap: var(--sp-2) }
.arc-row {
  display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start;
  gap: var(--sp-5) var(--sp-6);
  padding: var(--sp-5) var(--sp-4); margin-inline: calc(var(--sp-4) * -1);
  border-top: 1px solid var(--hair); border-radius: var(--r-4);
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease-out);
}
.arc > :first-child { border-top: 0 }
.arc-row:hover, .arc-row:focus-visible { background: var(--grey-100) }
.arc-img {
  display: block; width: 132px; height: 88px; object-fit: cover;
  border-radius: var(--r-3); background: var(--grey-100);
}
.arc-t { font-weight: 500 }
.arc-row:hover .arc-t { text-decoration: underline; text-underline-offset: 4px }
.arc-d {
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--text-3); font-variant-numeric: tabular-nums;
}
.arc-e {
  margin-top: var(--sp-2); max-width: 62ch;
  font-size: var(--fs-announce); line-height: var(--lh-announce);
  color: var(--text-2);
}
/* the year heading now that it is an <h2> rather than a paragraph */
.arc-y {
  padding-top: var(--sp-2); margin-bottom: var(--sp-3);
  font-size: 1.375rem; line-height: 1.3; font-weight: 300;
  letter-spacing: -0.01em; color: var(--text-3);
  font-variant-numeric: tabular-nums;
}

/* Contact rows, smaller, and the address is no longer printed. Jakob: show only
   the title, and the row itself is the mailto. The address still has to reach a
   screen reader, so it lives in a .vh span rather than being deleted — a link
   that announces only "Sales" tells a non-sighted visitor nothing about where it
   goes. */
.chan { padding: var(--sp-4) var(--sp-5); gap: var(--sp-4) }
.chan-t { font-size: var(--fs-announce); line-height: var(--lh-announce) }

/* ===========================================================================
   ROUND 10 · ADR-0020
   ========================================================================= */

/* Contact's two columns end flush now that Sales and Careers are gone (Jakob).
   The section opts into .body-match like the four pairs before it; this pushes
   the card's own action to the floor so the LinkedIn link does not float in the
   middle of a card that is taller than its content.

   Left unscoped deliberately: Contact's is the ONLY .rail-spread card on the
   site that contains an .act — the others end on .figs-cap or .metrics. */
.rail-spread > .act { margin-top: auto }

/* The four reasons on the e350 page carry an icon each (Jakob, round 10).
   The chip is reused from Home's industry cards but never their green:
   ADR-0014 scopes the colour code to those three industries and says it lives
   in exactly two places per card and nowhere else. A green chip here would
   claim these cards belong to that system. Neutral tint, so the icon reads as
   a marker rather than a category. */
.why { --tint: var(--grey-100); --tone: var(--text-3) }
.why .card-h { gap: var(--sp-4) }

/* The e350's closing CTA reuses .invite-p, which on Home only ever sits inside
   a narrow .invite box and so never needed a measure. Spanning all 12 columns
   of the dark card it ran to 124 characters a line. 68ch matches the .teaser
   directly above it. A no-op for Home's boxes, which are already narrower. */
.dark-card .invite-p { max-width: 68ch }

/* The news archive, collapsed. Thirty rows each carrying a thumbnail, a date
   and a summary is what Jakob read as crowded. A row is now a month and a
   headline; the thumbnail, the full date and the summary arrive on hover.

   REVEAL is a FOURTH hover response, and ADR-0014 capped the vocabulary at
   three (LIFT/TINT/LEAN) precisely to stop it growing — hence the ADR rather
   than a quiet addition.

   No transition, on purpose. Height is layout-bound, and the rule the whole
   hover vocabulary rests on animates only transform, opacity, box-shadow and
   background. The row still answers immediately through the TINT it already
   had, so nothing is lost by snapping.

   The left column is a FIXED width in both states. Left as `auto` it measures
   zero while the image is hidden, and every headline would jump 156px sideways
   on hover. The date lives in that column, which is the same date rail Home's
   .items already uses.

   Growth is downward from a fixed top edge, so moving the pointer down the list
   cannot drop it out of the row it just entered. */
.arc-row { grid-template-columns: 132px minmax(0, 1fr) }
.arc-c1 { display: grid; gap: var(--sp-3); align-content: start }
/* The captured news bodies open with the publisher's domain, so an excerpt can
   begin "dronelogisticsecosystem.net | We are honored…" — 27 unbreakable
   characters in a 130px column at 320px, which overflowed the document by 35px.
   `anywhere`, NOT `break-word`: only `anywhere` shrinks the element's
   min-content size, which is what actually releases the grid track. Same fix,
   same reason, as the email addresses on Contact in round 8. */
.arc-t, .arc-e { overflow-wrap: anywhere }
.arc-dd, .arc-yy, .arc-img, .arc-e { display: none }
.arc-row:hover .arc-dd, .arc-row:focus-visible .arc-dd,
.arc-row:hover .arc-yy, .arc-row:focus-visible .arc-yy { display: inline }
.arc-row:hover .arc-img, .arc-row:focus-visible .arc-img,
.arc-row:hover .arc-e, .arc-row:focus-visible .arc-e { display: block }

@media (max-width: 1080px) {
  .body-people { grid-template-columns: repeat(3, minmax(0, 1fr)) }
}
/* Touch has no hover, so below the mobile-nav breakpoint every row is simply
   open. Without this the thumbnails, dates and summaries are unreachable on a
   phone — the information would exist only for a pointer. */
@media (max-width: 880px) {
  .arc-dd, .arc-yy { display: inline }
  .arc-img, .arc-e { display: block }
}
@media (max-width: 640px) {
  .body-people { grid-template-columns: repeat(2, minmax(0, 1fr)) }
  .arc-img { width: 84px; height: 60px }
  /* the date rail narrows with the thumbnail it holds */
  .arc-row { gap: var(--sp-4); grid-template-columns: 84px minmax(0, 1fr) }
}
