/*! Joe Stocker Fundraiser — flagship campaign styles.
 *
 * THIS FILE IS THE SOURCE OF TRUTH. DO NOT REGENERATE IT.
 *
 * The header used to read "generated — source of truth: design-lab/experiences/
 * campaign/*, regenerate: node scripts/build-plugin-assets.mjs". That is no
 * longer true and following it is destructive: the design-lab stylesheet it
 * names has not been the campaign's source since the production composition
 * passes began, and this file has since diverged from it by roughly a quarter
 * of its content. Running build-plugin-assets.mjs would overwrite the live
 * campaign's composition with a stale prototype — the same class of trap as
 * scripts/build-canvas.mjs against templates/campaign-canvas.html.
 *
 * The campaign's real delivery path is:
 *     templates/campaign-canvas.html  (page markup, pushed to post 3248)
 *   + assets/campaign.css             (this file)
 *   + assets/campaign-film.css        (Chapter II's geometry, loads AFTER this)
 *   + the page custom_css on post 3248 (loads LAST — see THE CAMPAIGN SPINE)
 *
 * design-lab/ is for exploring a materially new direction only. Approved work
 * is written here.
 *
 * Namespaced under .camp so it cannot affect the theme or other Elementor content. */
.camp {
  
  --color-gold:        #daa520;  
  --color-gold-deep:   #7a5e0c;  
  --color-sand:        #eace84;  
  --color-cream:       #fbf6e8;  
  --color-ink:         #23221f;  
  --color-gray:        #65645e;  
  --color-gray-light:  #b3b0aa;
  --color-gray-pale:   #e6e2da;
  --color-black:       #000000;
  --color-off-white:   #f0f0f0;
  --color-white:       #ffffff;

  
  --bg-default:   var(--color-white);
  --bg-warm:      var(--color-cream);
  --bg-dark:      var(--color-ink);
  --bg-neutral:   var(--color-off-white);
  --surface-card: var(--color-white);

  --text-primary: var(--color-ink);
  --text-on-dark: var(--color-off-white);
  --text-muted:   var(--color-gray);

  --accent:        var(--color-gold);       
  --accent-strong: var(--color-gold-deep);  

  --border:     var(--color-gray-pale);
  --focus-ring: var(--color-gold);          

  
  --btn-primary-bg:         var(--color-gold);
  --btn-primary-text:       var(--color-ink);        
  --btn-primary-hover-bg:   var(--color-gold-deep);
  --btn-primary-hover-text: var(--color-white);      

  --bar-donations: var(--color-gold);       
  --bar-match:     var(--color-gold-deep);  

  
  --space-1:  4px;
  --space-2:  8px;
  --space-3:  12px;
  --space-4:  16px;
  --space-6:  24px;
  --space-8:  32px;
  --space-12: 48px;
  --space-16: 64px;
  --space-24: 96px;
  --space-32: 128px;

  
  --section-pad-y: clamp(56px, 9vw, 128px);
  --card-pad:      var(--space-8);

  
  --container-full:    100%;     
  --container-wide:    1440px;   
  --container-content: 1200px;   
  --container-reading: 72ch;     
  --container-narrow:  640px;    
  --gutter:        24px;         
  --gutter-mobile: 16px;

  
  --radius-sm:   6px;    
  --radius-md:   10px;   
  --radius-lg:   16px;   
  --radius-pill: 999px;

  
  --shadow-card:       0 8px 24px rgba(35, 34, 31, 0.08);
  --shadow-card-hover: 0 12px 32px rgba(35, 34, 31, 0.14);
  --shadow-sticky:     0 -4px 16px rgba(35, 34, 31, 0.10);

  
  --z-base:       0;
  --z-sticky-cta: 40;
  --z-header:     50;
  --z-modal:      100;
  --z-toast:      110;

  
  --font-display: "Archivo", "Oswald", system-ui, sans-serif; 
  --font-body:    "Inter", system-ui, -apple-system, sans-serif;

  --fs-display-xl: clamp(2.5rem, 6vw, 4.5rem);
  --fs-h2:         clamp(1.75rem, 3.5vw, 2.75rem);
  --fs-h3:         1.375rem;
  --fs-body:       1.0625rem;
  --fs-body-lg:    1.125rem;
  --fs-small:      0.875rem;

  --lh-display: 1.1;   
  --lh-body:    1.6;   
  --measure:    68ch;  

  
  --dur-fast:  120ms;
  --dur-base:  200ms;
  --dur-slow:  400ms;
  --dur-count: 1400ms;  

  --ease-out:      cubic-bezier(0.16, 1, 0.3, 1);
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --reveal-offset: 12px;
}
.camp .container {
  width: 100%;
  margin-inline: auto;
  padding-inline: var(--gutter);
  /* BLOCK AXIS STATED EXPLICITLY — see THE ELEMENTOR BOUNDARY at the foot of
     this file. The Elementor global kit ships `.container { padding: 18px }`,
     and a rule that declares only `padding-inline` leaves that shorthand's block
     padding standing on every container the campaign owns. Campaign vertical
     rhythm comes from the section bands and the beat margins; no container has
     block padding it did not ask for. Components that DO own theirs
     (`.nav__row`, `.hero__grid`, `#engine .container`) declare it at equal or
     higher specificity below and are unaffected. */
  padding-block: 0;
  max-width: var(--container-content);   
}
.camp .container--full { max-width: var(--container-full);    padding-inline: 0; }
.camp .container--wide { max-width: var(--container-wide); }
.camp .container--content { max-width: var(--container-content); }
.camp .container--reading { max-width: var(--container-reading); }
.camp .container--narrow { max-width: var(--container-narrow); }
@media (max-width: 768px) {
  .camp .container { padding-inline: var(--gutter-mobile); }
}
.camp .full-bleed {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
}
.camp .grid {
  display: grid;
  grid-template-columns: repeat(var(--grid-cols, 12), minmax(0, 1fr));
  gap: var(--gutter);
}
@media (max-width: 1024px) {
  .camp .grid { --grid-cols: 6; }
}
@media (max-width: 768px) {
  .camp .grid { --grid-cols: 4; gap: var(--gutter-mobile); }
}
.camp .section { padding-block: var(--section-pad-y); }
.camp .section--dark { background: var(--bg-dark);    color: var(--text-on-dark); }
.camp .section--warm { background: var(--bg-warm); }
.camp {
  --font-display: "Fraunces", Georgia, serif;
  --font-body: "Inter", system-ui, sans-serif;
  --ink: #23221f;
  --paper: #fbf6e8;
  --warm: #f6efdc;
  --stage: #14120e;              
  --stage-2: #1c1913;
  --gold: #daa520;
  --gold-deep: #7a5e0c;
  --gold-text-light: #5a4a12;    
  --rule: rgba(35, 34, 31, 0.14);
  --rule-strong: rgba(35, 34, 31, 0.26);
  --on-dark: #f4efe2;
  --on-dark-mut: rgba(244, 239, 226, 0.74);
  margin: 0; font-family: var(--font-body); color: var(--ink); background: var(--paper);
  /* THE CAMPAIGN'S DEFAULT LEADING, DECLARED RATHER THAN INHERITED.
     The root never set one, so every block that does not name its own leading
     took whatever the host gave it: 1.5 from the theme on production, `normal`
     (~1.2) anywhere the theme is absent. Chapter II's lede rendered 120px on the
     public page and 100px in the lab from identical markup and identical font
     metrics, and §8 puts a section lede at 1.45-1.55 — so the production value
     was the correct one and the campaign simply was not asking for it.
     Stating 1.5 here changes nothing on production and makes the composition
     independent of whichever theme is under it. */
  line-height: 1.5;
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
}
.camp * { box-sizing: border-box; }
.camp img { display: block; max-width: 100%; }
/* ── SELECTOR SCOPING ─────────────────────────────────────────────────────────
   The three resets below are scoped by the DESCENDANT combinator in front of
   `:where(...)`, i.e. `.camp :where(p)`, and NOT by repeating `.camp` inside the
   list. Repeating it — `.camp :where(h1, .camp p, .camp li, …)` — is what this
   file used to do, and it silently broke every entry after the first: those
   became `.camp .camp p`, demanding TWO nested campaign roots, which never
   exist. The rules therefore matched only `h1` (and `ul`), so the reset and the
   `font-family: inherit` never reached body copy.

   It was masked in production because the page's generated Elementor CSS happens
   to carry a correct copy of the same rules. The plugin must be independently
   correct; do not rely on that.

   `:where()` is used deliberately: it contributes ZERO specificity, so these are
   the weakest possible resets and any component rule below overrides them
   without needing !important. */
.camp :where(h1, h2, h3, h4, h5, h6, p, ul, ol, li, figure, blockquote, dl, dd, dt, fieldset, legend) { margin: 0; padding: 0; }
.camp :where(h1, h2, h3, h4, h5, h6, p, li, blockquote, figcaption, dt, dd, legend, label) { color: inherit; font-family: inherit; }
.camp :where(ul, ol) { list-style: none; }
.camp :where(a) { color: inherit; }
.camp .visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.camp .sim-flag { position: fixed; z-index: 200; bottom: 12px; left: 50%; transform: translateX(-50%);
  background: rgba(20, 18, 14, 0.92); color: var(--on-dark); font-size: 12px; padding: 7px 14px;
  border-radius: var(--radius-pill); box-shadow: var(--shadow-card); backdrop-filter: blur(6px); }
.camp .sim-flag strong { color: var(--color-sand); font-weight: 600; }
.camp .btn { --_bg: transparent; --_fg: currentColor; display: inline-flex; align-items: center; justify-content: center; gap: 0.5em;
  font-family: var(--font-body); font-weight: 600; font-size: 1rem; line-height: 1; padding: 0.95em 1.6em;
  border-radius: var(--radius-pill); border: 1.5px solid transparent; background: var(--_bg); color: var(--_fg);
  text-decoration: none; cursor: pointer; transition: transform var(--dur-fast) var(--ease-out), background var(--dur-base), color var(--dur-base), border-color var(--dur-base); }
.camp .btn--primary { --_bg: var(--gold); --_fg: var(--ink); box-shadow: 0 8px 24px rgba(218, 165, 32, 0.3); }
.camp .btn--primary:hover { --_bg: var(--gold-deep); --_fg: #fff; transform: translateY(-1px); }
.camp .btn--ghost { border-color: currentColor; }
.camp .btn--ghost:hover { background: rgba(255, 255, 255, 0.12); }
.camp .btn--block { width: 100%; }
.camp .btn:focus-visible { outline: 3px solid var(--gold); outline-offset: 3px; }
.camp .nav { position: fixed; inset: 0 0 auto 0; z-index: 100; border-bottom: 1px solid transparent; transition: background var(--dur-slow), border-color var(--dur-slow); }
.camp .nav__row { display: flex; align-items: center; justify-content: space-between; padding-block: 14px; gap: 20px; }
/* PRIMARY MARK — THE APPROVED TAGLINE LOCKUPS, AND THE ARITHMETIC THAT SIZES THEM.
   Measured off the ALPHA channel of the artwork, not guessed from filenames:

     tlm-white-tagline.png  1374x378  aspect 3.6349  wordmark band y0-285 (0.7566)  tagline band h38 (0.1005)
     tlm-dark-tagline.png   2088x616  aspect 3.3896  wordmark band y15-444 (0.6981) tagline band h59 (0.0958)

   These are TWO DIFFERENT LOCKUPS, not one artwork in two colours, so a single
   `height` would render the wordmark at two different optical sizes as the nav
   crosses into its solid state. `--tlm-wordmark` states the height we want the
   WORDMARK to occupy and each lockup's box is derived by dividing by its own
   band ratio — 1/0.7566 = 1.3217 white, 1/0.6981 = 1.4325 dark.

   Tagline cap-height follows from the same measurement:
       white  = wordmark x (38/286)  = wordmark x 0.1329
       dark   = wordmark x (59/430)  = wordmark x 0.1372

   The shipped wordmark heights and what the tagline actually renders at:
       desktop  wordmark 44px -> tagline 5.8px  (box 58 x 212)
       <=1024   wordmark 40px -> tagline 5.3px  (box 53 x 192)
       <=768    wordmark 34px -> tagline 4.5px  (box 45 x 163)

   BE HONEST ABOUT THIS: the donor email's tagline floor is 7px, which here
   needs a 53px wordmark and a 70x255 box — a ~98px navigation bar. A legible
   tagline and a non-dominant nav are near mutually exclusive in a fixed
   masthead, exactly as they were in the email's two-mark row. 44px is the
   judged compromise: the tagline is present, set in the artwork, and readable
   at the 2x device-pixel ratio this hero is viewed at, without the masthead
   competing with "Each dollar, doubled." Raise --tlm-wordmark to 53px if
   leadership prefers the floor over the proportion. */
.camp .nav__brand { --tlm-wordmark: 44px; display: block; flex: none; }
.camp .nav__logo { width: auto; }
.camp .nav__logo--onDark  { height: calc(var(--tlm-wordmark) * 1.3217); }
.camp .nav__logo--onLight { height: calc(var(--tlm-wordmark) * 1.4325); display: none; }
.camp .nav__links { display: flex; align-items: center; gap: 28px; }
/* TARGET SIZE (MINIMUM) - WCAG 2.2 AA 2.5.8. The nav links measured 23px tall
   against a 24px floor. Padding grows the hit area without moving the type: the
   bar is flex-centred, so the links stay exactly where they were drawn. */
.camp .nav__links a { color: var(--on-dark); text-decoration: none; font-size: 0.95rem; font-weight: 500; opacity: 0.88; display: inline-block; padding-block: 7px; }
.camp .nav__links a:hover { opacity: 1; }
.camp .nav__give { border: 1.5px solid rgba(244, 239, 226, 0.5); border-radius: var(--radius-pill); padding: 0.5em 1.1em; opacity: 1 !important; }
.camp .nav__give:hover { background: var(--gold); color: var(--ink) !important; border-color: var(--gold); }
.camp .nav.is-solid { background: rgba(251, 246, 232, 0.92); backdrop-filter: blur(10px); border-color: var(--rule); }
.camp .nav.is-solid .nav__logo--onDark { display: none; }
.camp .nav.is-solid .nav__logo--onLight { display: block; }
.camp .nav.is-solid .nav__links a { color: var(--ink); }
.camp .nav.is-solid .nav__give { border-color: var(--rule-strong); }
/* HERO HEIGHT. At 1440x900 the hero was exactly 100svh: a full screen with no
   evidence that anything followed it. 88dvh keeps the photograph dominant and
   leaves a deliberate ~108px of the next chapter visible, so the fold reads as a
   beginning rather than an ending. dvh (not vh) so mobile browser chrome cannot
   push the donate CTA off-screen; the clamp keeps it sane on short desktops. */
.camp .hero { position: relative; min-height: clamp(560px, 88dvh, 960px); display: flex; align-items: flex-end; overflow: hidden; background: var(--stage); }
.camp .hero__media { position: absolute; inset: 0; }
.camp .hero__media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 38%; }
.camp .hero__scrim { position: absolute; inset: 0; background:
  linear-gradient(180deg, rgba(15, 13, 10, 0.72) 0%, rgba(15, 13, 10, 0.28) 34%, rgba(15, 13, 10, 0.5) 66%, rgba(15, 13, 10, 0.94) 100%),
  radial-gradient(120% 80% at 88% 42%, rgba(218, 165, 32, 0.12), transparent 60%); }
/* PRE-EXISTING DEFECT, fixed here because it is inside the hero: the hero is
   bottom-anchored (`align-items: flex-end`) inside `overflow: hidden`, so on a
   short or narrow viewport the lede grew UPWARD and ran under the fixed nav —
   measured at 375px as 66px of the kicker sitting behind the mark, in the
   deployed layout as well as this one. A top padding costs nothing while the
   content fits and simply stops it passing under the masthead when it does not.
   Nothing about the lede's hierarchy, order or type changes. */
.camp .hero__grid { position: relative; width: 100%; display: grid; grid-template-columns: 1fr auto; align-items: end; gap: clamp(32px, 5vw, 72px); padding-top: clamp(94px, 11vh, 128px); padding-bottom: 52px; color: var(--on-dark); }
/* HEADLINE SCALE. 9vw/8rem rendered 128px at 1440, which is billboard scale:
   it out-measured its own paragraph block, pushed the lede into one continuous
   594px slab and left the photograph carrying type across two thirds of its
   height. 7vw/6.25rem renders 100px at 1440 and 90px at 1280 — still by far the
   largest thing on the page and still the campaign proposition, at editorial
   rather than poster scale. Family, weight, optical sizing, leading, tracking
   and the roman/italic gold relationship are all untouched; only the ramp
   changes. The <=600px block sets its own font-size, so MOBILE IS UNAFFECTED. */
.camp .hero__headline { font-family: var(--font-display); font-weight: 500; font-optical-sizing: auto; font-size: clamp(3.2rem, 7vw, 6.25rem); line-height: 0.92; letter-spacing: -0.02em; margin: 0; }
.camp .hero__headline em { font-style: italic; font-weight: 400; color: var(--gold); }
/* VERTICAL RHYTHM — THREE GROUPS, NOT ONE BLOCK. The gaps were 26 / 30 / 30,
   i.e. effectively uniform, which is why the left column read as one
   continuous slab rather than a sequence. They are now paced so the eye is
   handed along:

     proposition-> explanation   38px   BEAT BREAK
     explanation (p1 -> p2)      18px   tight: two paragraphs, one voice
     explanation-> action        36px   BEAT BREAK

   (A fourth group, the identity eyebrow, sat above the headline at 24px until
   the element was removed outright. The headline is now the lede's first
   child; with the grid bottom-anchored the column simply starts lower and no
   gap is left behind.)

   The two breaks are deliberately larger than they look like they need,
   because `line-height: 0.92` leaves the headline's last line box short of its
   glyphs: 38px nominal measures ~48px of ink gap. Nothing is added and no gap
   is left empty enough to read as a void — the column got 41px SHORTER, not
   taller.

   MEASURE. 44ch rendered 577px at 1440, wider than the headline's own first
   line (~436px at the new scale), so the lede spread past the thing it
   supports. 36ch is ~472px, which sits just past the headline's own measure,
   so the lede reads as supporting it rather than spreading past it. The value
   was picked off the rendered LINE BREAKS, not by eye — 36/38/40/42ch were
   each measured character by character:

     36ch  three well-filled lines, "dollar-for-dollar," intact, no orphan  <-
     38ch  breaks the compound across lines: "dollar-for-" / "dollar,"
     40ch  compound intact, but the last line falls to "up to $100,000."
     42ch  worse: the last line falls to "to $100,000."

   Font size, leading, colour and the two <strong> emphases are untouched. The <=600px block sets its own margins and `max-width: none`, so
   MOBILE IS UNAFFECTED. */
.camp .hero__sub { font-size: clamp(1.05rem, 1.5vw, 1.3rem); line-height: 1.5; max-width: 36ch; margin: 38px 0 0; color: rgba(244, 239, 226, 0.92); }
.camp .hero__sub + .hero__sub { margin-top: 18px; }
.camp .hero__sub strong { color: #fff; }
.camp .hero__cta { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 36px; margin-bottom: 30px; }
.camp .hero__equation { display: flex; align-items: baseline; gap: 0.35em; font-family: var(--font-display); font-size: clamp(1.6rem, 3vw, 2.4rem); margin: 0; color: var(--on-dark); }
.camp .hero__equation i { color: rgba(244, 239, 226, 0.45); font-style: normal; }
.camp .hero__equation b { color: var(--gold); font-weight: 600; }
.camp .hero__equation small { font-family: var(--font-body); font-size: 0.5em; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(244, 239, 226, 0.6); margin-left: 0.4em; align-self: center; }
.camp .ampule { width: 52px; height: 168px; border-radius: 26px; background: linear-gradient(180deg, rgba(255,255,255,0.06), rgba(0,0,0,0.3)); border: 1px solid rgba(218,165,32,0.28); position: relative; overflow: hidden; box-shadow: inset 0 0 30px rgba(0,0,0,0.5); }
.camp .ampule__charge { position: absolute; inset: 4px; border-radius: 22px; background: linear-gradient(180deg, rgba(218,165,32,0.95), rgba(122,94,12,0.55)); box-shadow: 0 0 24px rgba(218,165,32,0.6), inset 0 0 12px rgba(255,240,200,0.5); animation: chargePulse 3.2s var(--ease-standard) infinite; }
@keyframes chargePulse { 0%,100% { opacity: 0.82; } 50% { opacity: 1; } }
.camp .ampule--sm { width: 40px; height: 120px; margin: 0 auto 18px; }
.camp .ampule__label { text-align: center; margin: 0; }
.camp .ampule__label b { display: block; font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--gold); }
.camp .ampule__label span { display: block; font-size: 0.8rem; color: rgba(244, 239, 226, 0.75); margin-top: 4px; }
/* SYSTEM RHYTHM. Measured on the live page, chapters ran clamp(76px,11vw,168px)
   - 176px of empty space above AND below #why, #film and #match at 1440, about
   1,056px of an 8,619px page spent receiving nothing. Chapter V was separately
   tuned to 72px during its composition pass and reads correctly, so that value
   becomes the system rhythm rather than a Section V exception. Chapters that
   need more air take it deliberately, per chapter, not by global default. */
.camp .chapter { padding-block: clamp(48px, 5vw, 78px); position: relative; }
.camp .chapter--paper { background: var(--paper); }
.camp .chapter--warm { background: var(--warm); }
.camp .chapter--stage { background: radial-gradient(120% 90% at 50% 0%, var(--stage-2), var(--stage)); color: var(--on-dark); }
.camp .chapter__head { margin-bottom: clamp(28px, 4vw, 52px); }
.camp .chapter__num { display: block; font-family: var(--font-display); font-size: clamp(2rem, 4vw, 3rem); font-weight: 500; color: var(--gold-text-light); line-height: 1; margin-bottom: 0.3em; padding-bottom: 0.3em; border-bottom: 2px solid var(--gold); width: max-content; min-width: 1.4em; }
.camp .chapter__num--onDark { color: var(--gold); }
.camp .chapter__title { font-family: var(--font-display); font-weight: 500; font-size: clamp(1.9rem, 4vw, 3.4rem); line-height: 1.04; letter-spacing: -0.015em; margin: 0; max-width: 22ch; text-wrap: balance; }
.camp .chapter__title--onDark { color: var(--on-dark); }
.camp .chapter__lede { font-size: clamp(1.05rem, 1.6vw, 1.3rem); color: var(--on-dark-mut); margin: 18px 0 0; max-width: 40ch; }
.camp .prose { font-size: clamp(1.08rem, 1.4vw, 1.28rem); line-height: 1.62; }
.camp .prose p { margin: 0 0 1.1em; max-width: 62ch; }
.camp .prose strong { font-weight: 700; }
.camp .prose .drop::first-letter { font-family: var(--font-display); font-weight: 500; float: left; font-size: 3.6em; line-height: 0.72; padding: 0.06em 0.12em 0 0; color: var(--gold-text-light); }
/* ═══════════════════ CHAPTER I · ONE EDITORIAL SPREAD ════════════════════════
   Two earlier attempts, both wrong in opposite directions, and both corrected
   from the rendered pixels rather than from the source:

     v1  one dense side-by-side block. 32% shorter, but every element landed in
         the same visual plane, so nothing led and nothing followed. A blob.
     v2  five full-width beats with large varied gaps. Clear sequence, but the
         gaps disconnected the beats and the photograph sat marooned in cream.

   THIS IS ONE SPREAD. Two columns, with the photograph carrying the right-hand
   side and the story running continuously down the left:

       row 1   cols 1-8    numeral + headline
       row 2   cols 1-6    opening narrative        | cols 8-13  PHOTOGRAPH,
       row 3   cols 1-6    funding statement + why  |   spanning rows 2-3,
       row 4   cols 1-9    the capability index     |   offset DOWN into row 2
       row 5   cols 1-13   the proof

   So the picture sits BESIDE the narrative and the funding statement rather
   than between them, which is what anchors it to the copy it belongs to. It
   begins lower than the narrative's first line, so the columns are offset
   instead of aligned, and that offset IS the stagger. The capability index then
   runs wide beneath the picture, reaching back under the funding statement it
   supports. The proof closes the spread.

   Nothing is layered, nothing is absolutely positioned, and no copy is ever on
   top of the photograph. This is grid placement and vertical rhythm only.

   ROW GAPS ARE MODEST AND VARIED, and they live on the GRID rather than on the
   items, so no single beat can drift: whitespace separates the ideas without
   disconnecting them.

   The 3:2 of the picture is what sizes the right-hand column, and it is the
   picture's NATIVE aspect, so the spread crops nothing at all.

   No cards, no tiles, no boxes, no dashboard, no new typeface, no decorative
   fills. Fraunces display + Inter body + hairline rules + the paper/stone/gold
   palette already in the campaign. */
.camp .why__grid { display: grid; grid-template-columns: repeat(12, 1fr); column-gap: clamp(26px, 3.4vw, 48px); }
/* SPECIFICITY IS LOAD-BEARING. `.camp .figure { margin: 0 }` sits below this
   block at the same specificity, so a plain `.camp .why__plate { margin-top: … }`
   LOSES the cascade and the photograph collides with the story's last line.
   Every beat is written as `.camp .why .why__…` so the composition cannot be
   silently reset by a later component rule. */
.camp .why .why__grid  { row-gap: clamp(24px, 2.6vw, 38px); }
/* THE RIGHT COLUMN WAS EMPTY FOR ~300px. The headline ran cols 1-9 while the
   photograph waited in row 2, so the opening 300px of Chapter I was a half-empty
   band: type on the left, nothing on the right. The headline now takes cols 1-8
   and the plate rises to row 1, so the classroom sits BESIDE the problem
   statement and reads as its evidence rather than as a later illustration. */
/* THE RIGHT COLUMN RAN OUT OF CONTENT. The photograph closed in row 3 and the
   capability index then ran back across the LEFT of the spread in row 4, so
   above 1024 Chapter I carried ~350px of empty paper down its right-hand third
   and another ~520px beside the index — roughly a third of the chapter
   receiving nothing, in the chapter that has to establish the problem.

   Both halves now run continuously, and each half has one job:

     LEFT  (1-7)   THE ARGUMENT      headline → narrative → the $150,000 ask
     RIGHT (8-13)  THE EVIDENCE      the classroom → what new equipment enables
     FULL  (1-13)  THE PROOF         the four outcome figures close the chapter

   Moving the index into the right column does the second thing Chapter I needed
   as a side effect: the five capabilities were set at display scale across most
   of the page width, which gave a supporting list more presence than the
   $150,000 statement it supports. At the evidence column's measure they read as
   what they are — an annotated index under the photograph — and the chapter
   loses roughly 200px of height without losing a word.

   The photograph is NOT stretched to fill the column. Its 3:2 is the artwork's
   native shape and the crop table below depends on it; filling the column
   vertically would mean cropping a landscape frame to portrait and throwing
   away half of it, which is the error Chapter IV's band already had to undo. */
.camp .why .why__head  { grid-column: 1 / 8;  grid-row: 1; margin-bottom: 0; }
.camp .why .why__story { grid-column: 1 / 7;  grid-row: 2; }
.camp .why .why__case  { grid-column: 1 / 7;  grid-row: 3; align-self: start; }
.camp .why .why__plate { grid-column: 8 / 13; grid-row: 1 / 3; align-self: start;
  margin: clamp(16px, 2.2vw, 34px) 0 0; }
.camp .why .why__caps  { grid-column: 8 / 13; grid-row: 3; align-self: start;
  margin-top: clamp(18px, 2.2vw, 30px); }
.camp .why .proofline  { grid-column: 1 / 13; grid-row: 4; margin-top: clamp(30px, 3.4vw, 52px); }

/* THE INDEX AT EVIDENCE SCALE. One column in the evidence measure, a step under
   the body face's display sibling on the left, so it annotates the photograph
   rather than competing with the chapter's ask. Below 1024 the spread collapses
   to a single column and the list goes back to its full-width two-column form,
   which is why this is scoped to the two-column composition only. */
@media (min-width: 1025px) {
  /* ONE COLUMN, not the page-wide two. At the evidence column's measure a
     two-up grid gives each capability ~200px, which broke four of the five
     across two lines ("Current operating / systems & software") and turned an
     index into a ragged block. Single file reads as an index, sets every item
     on one line, and fills the evidence column beneath the photograph — which
     is the void this move exists to close. */
  .camp .why .why__caps .caps { grid-template-columns: minmax(0, 1fr); max-width: none; }
  .camp .why .why__caps .caps li { font-size: clamp(1rem, 1.12vw, 1.16rem); padding: 0.58em 0; gap: 0.8em; }
}
.camp .why { padding-bottom: clamp(56px, 6vw, 92px); }

/* TYPE SCALE, AUDITED AGAINST THE CAMPAIGN.
   `.chapter__title` renders 54.4px at 1440 and is shared with Chapter II (the
   film) and Chapter III (the match) — measured, identical. So this headline was
   never oversized RELATIVE TO THE SYSTEM; it was oversized relative to what
   sits under it, because this chapter carries far more content than the others.

   It is therefore reduced HERE ONLY, by about 17% (54.4px -> 45.4px at 1440,
   30.4px -> 25.7px at 390), and scoped to `.why` so Chapters II and III keep
   the shared scale untouched. Verified by measurement, not by reading the
   clamp: 3.15vw governs at 1440, the 2.9rem ceiling only takes over above
   ~1470px. It remains by a wide margin the largest type in the chapter, so it
   keeps its authority. A deliberate, local divergence from the shared chapter
   scale, not a new typography system. */
.camp .why .chapter__title { font-size: clamp(1.62rem, 3.15vw, 2.9rem); line-height: 1.08; max-width: 26ch; }
.camp .why .chapter__num { font-size: clamp(1.7rem, 3vw, 2.4rem); }

/* THE STORY. One comfortable measure: the previous spread squeezed this to a
   452px column at 20.2px, about 38 characters a line, which reads as a column
   of fragments. 62ch at a slightly smaller grade is the campaign's reading
   measure, and the drop cap comes down from 3.6em to 2.6em so it introduces the
   paragraph instead of shouting over it. */
.camp .why__story { font-size: clamp(1.06rem, 1.32vw, 1.22rem); line-height: 1.6; }
.camp .why__story p { max-width: 62ch; margin: 0; }
.camp .why__story .drop::first-letter { font-size: 3em; line-height: 0.76; padding: 0.05em 0.1em 0 0; }

/* THE PHOTOGRAPH — EXPLICIT RESPONSIVE CROP, NOT ONE DESKTOP CROP LEFT TO BREAK.
   Measured off the artwork (1600x1067, native 3:2): the two students' heads
   occupy x 46-65%, y 27-50% of the frame. `object-position: 55% 40%` is
   centred on them, and every ratio below was checked against those bounds:

     ratio      what is cropped     heads (x 46-65%, y 27-50%)   used at
     3 / 2      NOTHING (native)    fully inside                 >= 768px
     4 / 3      11% of the WIDTH    fully inside (x 6.1-95%)     <= 767px

   The spread and the tablet band show the picture UNCROPPED, which is the
   safest treatment there is for the two faces, and its 3:2 is what sizes the
   right-hand column. Mobile goes TALLER rather than wider so the photograph
   keeps presence on a small screen instead of flattening to a letterbox, and
   the 11% it loses comes off the left edge, which is desk and monitor, never
   off either person. Asserted at all six widths by the QA sweep. */
.camp .why .why__plate img { width: 100%; aspect-ratio: 3 / 2; height: auto; object-fit: cover; object-position: 55% 40%; }
/* `.camp .figure figcaption` further down carries 0.86rem at the same
   specificity and was winning, so this caption rendered at 13.8px at every
   width. Three classes, so it holds. */
.camp .why .why__plate figcaption { font-size: 0.95rem; line-height: 1.5; color: var(--text-muted); padding: 16px 0 0; max-width: 54ch; }

/* BEAT 4 · THE CASE. The funding statement is display type but deliberately
   HALF the headline's size, so the hierarchy inside the chapter stays
   headline > statement > supporting index. It is not a heading element and does
   not pretend to be one. */
/* THE CONCLUSION OF CHAPTER I. At 1440 this rendered 28.8px while the proof
   band's supporting heading rendered 35.2px - the chapter's payoff was quieter
   than a subheading two screens later, so the eye never registered the ask. It
   now sits between the chapter headline (45px) and that subheading (35px), which
   is the order the story actually runs in. */
.camp .why__ask { font-family: var(--font-display); font-weight: 500; font-size: clamp(1.5rem, 2.8vw, 2.5rem);
  line-height: 1.2; letter-spacing: -0.012em; margin: 0; max-width: 26ch; text-wrap: balance; }
.camp .why__kicker { font-family: var(--font-body); font-size: 1.02rem; line-height: 1.55; color: var(--ink);
  margin: clamp(16px, 1.8vw, 22px) 0 0; max-width: 58ch; }
/* THE CAPABILITY INDEX. Supporting information, set in the body face, never a
   run of competing headlines. Two columns at desktop because the beat is wide
   enough here to take them without ragged wrapping; one hairline per row and
   nothing else. */
.camp .caps { list-style: none; padding: 0; margin: clamp(22px, 2.6vw, 30px) 0 0; max-width: none;
  display: grid; grid-template-columns: 1fr 1fr; column-gap: clamp(24px, 3vw, 44px); }
.camp .caps li { font-family: var(--font-body); font-size: 0.97rem; font-weight: 500; line-height: 1.35;
  padding: 10px 0; border-bottom: 1px solid var(--rule); display: grid; grid-template-columns: 2.5em 1fr;
  align-items: baseline; }
.camp .caps li:nth-child(1), .camp .caps li:nth-child(2) { border-top: 1px solid var(--rule); }
/* Five items in two columns leaves the fifth alone in a half-empty row, which
   reads as a mistake. It spans the beat instead, which reads as a decision. */
.camp .caps li:nth-child(5) { grid-column: 1 / -1; }
.camp .caps span { font-family: var(--font-body); font-size: 0.68rem; font-weight: 700; color: var(--gold-text-light);
  letter-spacing: 0.06em; font-variant-numeric: tabular-nums; }

/* BEAT 5 · THE EVIDENCE RAIL.
   NOTE: `.proofline*` had NO layout CSS in this stylesheet — the rules exist in
   the design-lab source but were never carried into the plugin build, so this
   band ships to production as unstyled stacked text. Verified against the live
   site: `proofline__stats` appears 0 times in the deployed campaign.css. The
   layout below is new, not a restyle. */
.camp .proofline { padding-top: clamp(24px, 3vw, 34px); border-top: 1px solid var(--rule-strong); }
.camp .proofline__title { font-family: var(--font-display); font-weight: 500; font-size: clamp(1.2rem, 1.9vw, 1.6rem);
  line-height: 1.14; letter-spacing: -0.01em; margin: 0 0 clamp(22px, 2.6vw, 32px); max-width: 32ch; }
.camp .proofline__stats { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(16px, 2.2vw, 32px); }
.camp .proofline__stats li { padding: 0 clamp(16px, 2.2vw, 30px); border-left: 1px solid var(--rule); }
.camp .proofline__stats li:first-child { border-left: 0; padding-left: 0; }

/* IN-PROGRESS, NOT PART OF THE SECTION V SYNC. These two rules were written
   inside the Section V landscape block of the retired horizontal experiment;
   they are Chapter I/global rules, so they are re-homed here rather than lost
   with it. Uncommitted. */
@media (min-width: 640px) and (max-height: 520px) and (orientation: landscape) {
  .camp .proofline__stats { grid-template-columns: repeat(4, 1fr); gap: 16px; }
  .camp .chapter { padding-block: clamp(48px, 9vw, 72px); }
}

.camp .proofline__stats b { display: block; font-family: var(--font-display); font-weight: 500;
  font-size: clamp(1.65rem, 2.6vw, 2.3rem); line-height: 1; letter-spacing: -0.015em;
  font-variant-numeric: tabular-nums; color: var(--gold-text-light); margin-bottom: 10px; }
.camp .proofline__stats span { display: block; font-size: 0.88rem; line-height: 1.45; color: var(--text-muted); }
.camp .figure { margin: 0; }
.camp .figure--bleed { margin-top: clamp(48px, 7vw, 96px); }
.camp .figure--bleed img { width: 100%; max-height: 76vh; object-fit: cover; }
/* CHERRY-PICKED FROM THE APPROVED DESIGN LAB (design-lab/experiences/campaign/
   campaign.css). The generator (scripts/build-plugin-assets.mjs) is the source
   of truth, but this asset is a hand-hardened v0.1.5 snapshot and a full
   regeneration carries ~550 lines of unreviewed lab drift (including a prism
   rewrite whose markup/JS are not ported). Only the caption rules below are
   brought forward; full regeneration is tracked as reviewed debt.

   Hello Elementor's reset.css ships `figcaption{font-style:italic;line-height:1.4}`.
   An element selector beats inheritance, so the theme restyled every campaign
   caption. font-style/line-height are restated here to match the lab. */
.camp .figure figcaption { font-size: 0.86rem; color: var(--text-muted); padding: 12px 0 0; max-width: 60ch;
  font-style: normal; line-height: normal; }
/* The bleed caption insets itself from the full-bleed edge to align with the
   content container. Under the global `box-sizing: border-box` that gutter is
   subtracted from INSIDE the 60ch cap inherited above, so the text measure was
   `60ch - (viewport - 1200px)`:

        1280 -> 441px      1440 -> 281px      1512 -> 209px
        1600 -> 121px      1680 ->  41px      1720+ ->  0px

   Above ~1600px the caption collapsed into a column of single words and hit
   zero at 1720. The seven-viewport review grid stops at 1440, which is why it
   was never caught. `content-box` fixes it at the cause: the 60ch measure now
   describes the TEXT and the alignment gutter is added outside it, so the
   caption holds one editorial measure at every width and still starts on the
   container's left edge. Gutter follows the container tokens rather than a
   hardcoded 24px so it steps to --gutter-mobile at 768 and below. */
.camp .figure--bleed figcaption {
  box-sizing: content-box;
  padding-inline: max(var(--gutter), calc((100% - var(--container-content)) / 2));
}
.camp .prism { margin-top: clamp(24px, 4vw, 44px); }
.camp .prism__svg { width: 100%; height: auto; overflow: visible; }
.camp .prism__in { fill: none; stroke: var(--gold-text-light); stroke-width: 3; }
.camp .prism__lens { fill: rgba(218,165,32,0.18); stroke: var(--gold); stroke-width: 1.5; }
.camp .prism__out { fill: none; stroke-width: 3; }
.camp .prism__out--donor { stroke: var(--ink); }
.camp .prism__out--match { stroke: var(--gold-text-light); }
.camp .prism__labels { position: relative; display: flex; justify-content: space-between; font-size: 0.78rem; color: var(--text-muted); margin-top: 4px; }
.camp .prism__l--donor, .camp .prism__l--match { color: var(--gold-text-light); font-weight: 600; }
/* .split / .pullquote retired with the #builds recomposition; see the
   EQUIPMENT CHAPTER block below. Both were used by that section only. */
.camp .engine { position: relative; border-radius: var(--radius-lg); }
/* THE APPROVED COMPOSITION. Campaign information left, the vertical instrument
   right, top-aligned so the two share vertical space instead of queueing. The
   instrument column is max-content because the instrument is a fixed-geometry
   drawing: giving it a fr would stretch the tick gutter, not the vessel. */
.camp .engine__stage { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, max-content); gap: clamp(28px, 3.2vw, 56px); align-items: start; }
.camp .engine__eyebrow { font-size: 0.76rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--color-sand); margin: 0 0 14px; font-weight: 600; }
.camp .engine__total { font-family: var(--font-display); font-weight: 500; font-variant-numeric: tabular-nums; font-size: clamp(3rem, 5.2vw, 5rem); line-height: 0.92; letter-spacing: -0.02em; color: #fff; margin: 0; }
.camp .engine__of { font-variant-numeric: tabular-nums; color: var(--on-dark-mut); margin: 14px 0 0; font-size: 1.02rem; }
.camp .engine__zone { display: flex; align-items: center; gap: 10px; margin: 22px 0 0; font-size: 0.98rem; line-height: 1.45; color: var(--color-sand); }
.camp .engine__zone i { width: 8px; height: 8px; margin-top: 0.42em; border-radius: 50%; background: var(--gold); box-shadow: 0 0 10px var(--gold); flex: none; }
.camp .engine__stats { display: grid; gap: 0; margin: clamp(22px, 2.4vw, 30px) 0 0; border-top: 1px solid rgba(244, 239, 226, 0.16); }
.camp .engine__stats > div { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; padding: 10px 0; border-bottom: 1px solid rgba(244, 239, 226, 0.12); }
.camp .engine__stats dt { margin: 0; color: var(--on-dark-mut); font-size: 0.95rem; }
.camp .engine__stats dd { margin: 0; font-family: var(--font-display); font-weight: 500; font-variant-numeric: tabular-nums; font-size: 1.5rem; color: #fff; }
.camp .engine__instrument { display: flex; align-items: stretch; align-self: start; gap: 22px; height: clamp(400px, 44vh, 470px); }
.camp .core { position: relative; display: flex; flex-direction: column; align-items: center; }
.camp .core__ticks { position: absolute; inset: 0 auto 0 0; left: -6px; width: 100%; pointer-events: none; }
/* The scale labels live in the 84px gutter left of the tube; without a cap the
   longest of them ran under the connector rule and into the tube itself. */
.camp .core__ticks span { position: absolute; left: 0; transform: translateY(50%); display: flex; flex-direction: column; align-items: flex-start; gap: 0; line-height: 1.15; font-variant-numeric: tabular-nums; max-width: 92px; }
.camp .core__ticks em { font-style: normal; font-size: 0.72rem; color: rgba(244, 239, 226, 0.6); }
.camp .core__ticks small { font-size: 0.58rem; letter-spacing: 0.08em; text-transform: uppercase; color: rgba(244, 239, 226, 0.42); }
.camp .core__ticks span::before { content: ""; position: absolute; left: 82px; top: 50%; width: 10px; height: 1px; background: rgba(244, 239, 226, 0.2); }
.camp .core__tick--cap em { color: var(--gold); }
.camp .core__tick--cap small { color: var(--color-sand); }
.camp .core__tick--cap::before { background: var(--gold) !important; height: 2px !important; }
.camp .core__tube { position: relative; width: clamp(104px, 13vw, 148px); flex: 1; margin-left: 96px; border-radius: 74px;
  background: linear-gradient(180deg, rgba(255,255,255,0.05), rgba(0,0,0,0.35));
  border: 1px solid rgba(218, 165, 32, 0.18); overflow: hidden;
  box-shadow: inset 0 0 40px rgba(0,0,0,0.6), inset 6px 0 16px rgba(255,255,255,0.04); }
.camp .core__tube::after { content: ""; position: absolute; top: 6px; left: 12%; width: 10%; height: 88%; border-radius: 40px; background: linear-gradient(180deg, rgba(255,255,255,0.14), transparent 40%); z-index: 3; }
.camp .core__fill { position: absolute; left: 0; right: 0; bottom: 0; border-radius: 0 0 74px 74px;
  display: flex; flex-direction: column-reverse; transition: height var(--dur-slow) var(--ease-out); }
.camp .core__seg { display: block; width: 100%; transition: height var(--dur-slow) var(--ease-out); }
.camp .core__seg--donor { background: linear-gradient(180deg, rgba(246, 211, 107, 0.22), rgba(120, 96, 30, 0.42)); }
.camp .core__seg--match { background: linear-gradient(180deg, rgba(218, 165, 32, 0.62), rgba(218, 165, 32, 0.34)); border-top: 1px solid rgba(255, 240, 200, 0.35); box-shadow: 0 -1px 10px rgba(218,165,32,0.35); }
.camp .core__meniscus { position: absolute; top: -2px; left: 0; right: 0; height: 4px; z-index: 2; background: linear-gradient(90deg, #f6d36b, #daa520, #f6d36b); box-shadow: 0 0 18px 2px rgba(218, 165, 32, 0.85); border-radius: 3px; }
.camp .core__lens { position: absolute; left: 8%; right: 8%; bottom: 12%; height: 2px; background: var(--gold); box-shadow: 0 0 16px 3px rgba(218, 165, 32, 0.7); opacity: 0.5; }
.camp .core__inlet { position: absolute; left: 50%; bottom: 0; width: 14px; height: 14px; transform: translateX(-50%); border-radius: 50%; background: radial-gradient(circle, #ffe9a8, rgba(218,165,32,0)); opacity: 0.7; }
.camp .core__beam { position: absolute; bottom: 12%; width: 2px; height: 0; opacity: 0; }
.camp .core__beam--donor { left: 38%; background: linear-gradient(180deg, #fff, transparent); }
.camp .core__beam--match { left: 62%; background: linear-gradient(180deg, var(--gold), transparent); }
.camp .core__legend { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 6px; font-size: 0.68rem; letter-spacing: 0.04em; text-transform: uppercase; color: rgba(244, 239, 226, 0.6); margin: 14px 0 0; text-align: center; line-height: 1.7; }
/* key + label travel together, so a swatch can never be orphaned at a line end */
.camp .core__legend-item { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.camp .core__key { width: 11px; height: 11px; border-radius: 3px; display: inline-block; }
.camp .core__key--donor { background: linear-gradient(180deg, rgba(246,211,107,0.5), rgba(120,96,30,0.6)); }
.camp .core__key--match { background: var(--gold); }
.camp .reservoir { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 10px; padding-bottom: 26px; }
.camp .reservoir__tube { position: relative; width: 26px; flex: 1; border-radius: 14px; background: rgba(0,0,0,0.3); border: 1px solid rgba(218,165,32,0.16); overflow: hidden; box-shadow: inset 0 0 16px rgba(0,0,0,0.6); }
/* HANGING SUPPLY — the visual inverse of the core vessel. The core fills UPWARD
   from the bottom as impact accumulates; the remaining match is anchored at the
   TOP and is drawn down from beneath, so the empty space grows under it as the
   match is consumed. The gradient mirrors the core's: brightest at the free
   edge (here the bottom), falling away into the anchored mass. At $0 remaining
   the fill is zero-height and the tube's faint gold outline is all that is left,
   so the original $100,000 capacity stays legible. */
.camp .reservoir__fill { position: absolute; left: 0; right: 0; top: 0; background: linear-gradient(180deg, #f6d36b, rgba(122,94,12,0.6)); box-shadow: 0 0 14px rgba(218,165,32,0.5); transition: height var(--dur-slow) var(--ease-out); }
.camp .reservoir__label { font-size: 0.62rem; letter-spacing: 0.1em; text-transform: uppercase; color: rgba(244, 239, 226, 0.5); text-align: center; margin: 0; line-height: 1.3; }
.camp .reservoir__val { font-family: var(--font-display); font-variant-numeric: tabular-nums; font-size: 0.95rem; color: var(--color-sand); margin: 0; }
.camp .engine__controls { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 18px; margin-top: 40px; padding-top: 24px; border-top: 1px solid rgba(244, 239, 226, 0.14); }
.camp .engine__steps { display: flex; gap: 8px; flex-wrap: wrap; }
.camp .engine__step { font: inherit; font-size: 0.8rem; padding: 0.5em 0.9em; border-radius: var(--radius-pill); cursor: pointer; background: transparent; color: rgba(244, 239, 226, 0.75); border: 1px solid rgba(244, 239, 226, 0.24); transition: all var(--dur-base); }
.camp .engine__step:hover { border-color: var(--gold); color: #fff; }
.camp .engine__step.is-active { background: var(--gold); color: var(--ink); border-color: var(--gold); font-weight: 600; }
.camp .engine__demos { display: flex; gap: 8px; flex-wrap: wrap; }
.camp .engine__demo { font: inherit; font-size: 0.8rem; padding: 0.5em 0.9em; border-radius: var(--radius-pill); cursor: pointer; background: rgba(244,239,226,0.06); color: var(--on-dark-mut); border: 1px solid rgba(244, 239, 226, 0.18); transition: all var(--dur-base); }
.camp .engine__demo:hover { color: #fff; border-color: rgba(244,239,226,0.4); }
.camp .engine__demo--gift { background: rgba(218,165,32,0.14); color: var(--color-sand); border-color: rgba(218,165,32,0.4); font-weight: 600; }
.camp .engine__demo--gift:hover { background: rgba(218,165,32,0.24); color: #fff; }
.camp .engine__note { font-size: 0.8rem; line-height: 1.5; color: rgba(244, 239, 226, 0.7); margin: 22px 0 0; max-width: 72ch; }
.camp .engine__note code { color: var(--color-sand); }
.camp .engine__loading { position: absolute; inset: 0; z-index: 5; display: flex; align-items: center; justify-content: center; gap: 14px; border-radius: var(--radius-lg); background: rgba(20, 18, 14, 0.72); backdrop-filter: blur(4px); color: var(--on-dark-mut); font-size: 0.95rem; }
.camp .engine__loading[hidden] { display: none; }
.camp .engine__spinner { width: 26px; height: 26px; border: 2px solid rgba(244,239,226,0.2); border-top-color: var(--gold); border-radius: 50%; animation: spin 0.8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.camp .engine__stale { display: flex; align-items: center; gap: 8px; margin-bottom: 18px; padding: 10px 16px; border-radius: var(--radius-md); background: rgba(218, 165, 32, 0.12); border: 1px solid rgba(218, 165, 32, 0.34); color: var(--color-sand); font-size: 0.86rem; }
.camp .engine__stale[hidden] { display: none; }
.camp .engine__stale::before { content: "⚠"; }
.camp .seg { display: inline-flex; background: rgba(35, 34, 31, 0.06); border-radius: var(--radius-pill); padding: 4px; gap: 4px; }
.camp .seg__btn { font: inherit; font-weight: 600; font-size: 0.95rem; padding: 0.6em 1.4em; border: 0; border-radius: var(--radius-pill); background: transparent; color: var(--ink); cursor: pointer; transition: all var(--dur-base); }
.camp .seg__btn.is-active { background: #fff; box-shadow: var(--shadow-card); }
.camp .seg__btn[disabled] { color: var(--color-gray-light); cursor: not-allowed; }
.camp .seg__btn small { font-size: 0.62em; letter-spacing: 0.1em; text-transform: uppercase; vertical-align: super; margin-left: 3px; color: var(--gold-text-light); }
.camp .foot { background: #100e0b; color: rgba(244, 239, 226, 0.62); padding-block: 44px; }
.camp .foot__row { display: grid; grid-template-columns: auto 1fr auto; gap: 32px; align-items: center; }
.camp .foot__brand { display: flex; align-items: center; gap: 14px; }
.camp .foot__eyebrow { font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase; color: rgba(244, 239, 226, 0.5); white-space: nowrap; }
.camp .foot__brand img { height: 30px; width: auto; opacity: 0.9; }
.camp .foot__patriot { filter: brightness(0) invert(1); opacity: 0.55 !important; }
.camp .foot__legal { font-size: 0.78rem; line-height: 1.5; margin: 0; max-width: 60ch; }
.camp .foot__back { color: var(--color-sand); text-decoration: none; font-weight: 600; white-space: nowrap; }
.camp .foot__back:hover { color: var(--gold); }
.camp .handoff { position: fixed; inset: 0; z-index: 300; display: grid; place-items: center; background: rgba(15, 13, 10, 0.9); backdrop-filter: blur(8px); }
.camp .handoff[hidden] { display: none; }
.camp .handoff__card { text-align: center; color: var(--on-dark); padding: 40px; }
.camp .handoff__title { font-family: var(--font-display); font-size: 1.5rem; margin: 0 0 6px; }
.camp .handoff__sub { color: var(--color-sand); margin: 0; }
.camp .thanks { min-height: 100svh; display: grid; align-content: center; background: radial-gradient(120% 90% at 50% 0%, var(--stage-2), var(--stage)); color: var(--on-dark); padding-block: clamp(96px, 14vh, 160px); }
.camp .thanks__kicker { font-size: 0.82rem; letter-spacing: 0.22em; text-transform: uppercase; font-weight: 600; color: var(--color-sand); margin: 0 0 22px; }
.camp .thanks__headline { font-family: var(--font-display); font-weight: 500; font-size: clamp(2.6rem, 7vw, 5.6rem); line-height: 0.98; letter-spacing: -0.02em; margin: 0; }
.camp .thanks__headline em { font-style: italic; font-weight: 400; color: var(--gold); }
.camp .thanks__lede { font-size: clamp(1.05rem, 1.6vw, 1.3rem); line-height: 1.5; color: rgba(244, 239, 226, 0.9); max-width: 52ch; margin: 26px 0 0; }
.camp .thanks__eq { display: flex; align-items: baseline; gap: 0.35em; font-family: var(--font-display); font-size: clamp(1.6rem, 3vw, 2.4rem); margin: 34px 0 0; }
.camp .thanks__eq i { color: rgba(244, 239, 226, 0.45); font-style: normal; }
.camp .thanks__eq b { color: var(--gold); font-weight: 600; }
.camp .thanks__eq small { font-family: var(--font-body); font-size: 0.5em; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(244, 239, 226, 0.6); margin-left: 0.4em; align-self: center; }
.camp .thanks__cta { display: flex; gap: 14px; flex-wrap: wrap; margin-top: 38px; }
.camp .thanks__note { margin-top: 34px; font-size: 0.82rem; color: var(--on-dark-mut); max-width: 56ch; }
.camp .reveal { opacity: 1; }
.camp.reveals-armed .reveal { opacity: 0; transform: translateY(16px); }
.camp.reveals-armed .reveal.is-in { opacity: 1; transform: none; transition: opacity 0.6s var(--ease-out), transform 0.6s var(--ease-out); }
@media (max-width: 1024px) {
  .camp .hero__grid { grid-template-columns: 1fr; }
  .camp .nav__brand { --tlm-wordmark: 40px; }
  /* CHAPTER I · TABLET AND BELOW, PROGRESSIVELY SIMPLIFIED.
     The two-column spread needs about 1080px before the left column stops
     shredding the narrative into four-word lines, so below that it resolves
     into ONE editorial column in exactly the intended reading order:

         headline · narrative · image · caption · funding · why · capabilities · proof

     The grid rows are released (`grid-row: auto`) so the items flow, and the
     spacing is controlled from one place, the grid's row-gap, so this cannot
     develop the dead zones the five-beat version had. */
  .camp .why .why__grid { row-gap: clamp(22px, 3vw, 30px); }
  .camp .why .why__head,
  .camp .why .why__story,
  .camp .why .why__plate,
  .camp .why .why__case,
  .camp .why .why__caps,
  .camp .why .proofline { grid-column: 1 / -1; grid-row: auto; }
  .camp .why .why__plate { margin-top: clamp(10px, 1.6vw, 18px); }
  .camp .why .why__case  { margin-top: clamp(10px, 1.6vw, 18px); }
  .camp .why .why__caps  { margin-top: 0; }
  .camp .why .proofline  { margin-top: clamp(26px, 3.2vw, 40px); }
  .camp .why .chapter__title { max-width: 24ch; }
  .camp .why .why__story p, .camp .why .why__kicker { max-width: 64ch; }
  /* Section V no longer stacks at 1024 - the width still carries two columns,
     and the stack now begins at 900. See the CHAPTER V block. */
}
/* ── 768-1079 · the picture keeps its NATIVE 3:2 across the whole tablet band,
      uncropped, at full column width, with nothing beside it. The proof goes
      2x2 here so the numerals stay large. ──────────────────────────────────── */
@media (max-width: 1079px) {
  /* THE PICTURE MUST NOT BECOME A BANNER. Left at full column width a 3:2
     plate is 650px tall at 1024, which is bigger than it ever is on the
     spread and reads as a standalone slab. It is capped and pushed to the
     RIGHT edge of the column instead, so the single-column flow keeps a trace
     of the spread's asymmetry and the caption stays with the picture. */
  .camp .why .why__plate { max-width: min(100%, 640px); margin-inline-start: auto; }
  .camp .why .why__plate img { aspect-ratio: 3 / 2; }
  .camp .proofline__stats { grid-template-columns: repeat(2, 1fr); row-gap: clamp(24px, 3.4vw, 34px); }
  .camp .proofline__stats li { padding: 0 0 0 22px; border-left: 1px solid var(--rule); }
  .camp .proofline__stats li:first-child { padding-left: 22px; border-left: 1px solid var(--rule); }
}
@media (max-width: 768px) {
  /* Approved lab: below 768 the caption gutter follows --gutter-mobile so it
     stays in step with .container's mobile padding. */
  .camp .figure--bleed figcaption { padding-inline: var(--gutter-mobile); }
  .camp .nav__links a:not(.nav__give) { display: none; }
  /* THE TOP-RIGHT "GIVE" IS REDUNDANT OVER THE HERO and competes with the
     Donate CTA a few hundred pixels below it. It is not removed: the nav
     already has an `is-solid` state that campaign-flagship.js switches on past
     72% of the first viewport, so Give simply waits for it. Over the hero,
     Donate is the only gold action; past the hero, Give is the only one. Same
     mechanism the nav already used to swap its logo and link colours, so no new
     pattern is introduced and no function is lost. */
  /* GIVE IS AVAILABLE AT LOAD. It used to wait for `.is-solid` (past 72% of the
     first viewport) on the argument that it was redundant over the hero. Measured,
     that argument failed at short viewports: at 375x667 the hero's own donate CTA
     lands at y764 against a 667px fold, so between load and first scroll there was
     NO visible way to give. Availability beats tidiness on the one control the
     page exists for.

     It stays SUBORDINATE without a second design: the nav control keeps its ghost
     treatment - 1.5px cream rule, transparent ground - against the hero's solid
     gold Donate, so the primary action is still unambiguous when both are on
     screen. Contrast holds at rest: below 600 the masthead band is solid --stage,
     and from 600-768 the hero scrim opens at 0.55 under the bar. `.is-solid` still
     swaps the border colour, so the transparent/solid transition is unchanged. */
  .camp .nav__brand { --tlm-wordmark: 34px; }

  /* ── MOBILE (<=768) · THE SAME EDITORIAL SYSTEM, DESIGNED FOR A PHONE ──────
     Not the desktop grid stacked. What changes, and why:

       · the indent is dropped. Below 768 there is no width to spend on a
         diagonal, and a 12px indent reads as a mistake rather than a stagger.
         Rhythm is carried by VARIED VERTICAL SPACE instead, which is the same
         idea in the one axis a phone actually has.
       · the headline comes down again and is capped at 18ch so it sets 3 short
         lines instead of dominating the first screen.
       · the drop cap is REMOVED. At this measure a 2.6em initial swallows two
         lines of a five-line paragraph.
       · the photograph goes TALLER (4:3, not wider) and gets the largest
         gaps in the chapter above and below it, so it reads as its own clean
         moment rather than as a band between two slabs of text.
       · the capability index goes single column. The labels are phrases, so two
         columns wrapped half of them to three lines each. It is also the
         quietest thing in the chapter here: 0.94rem, and the row rule is the
         only divider used.
       · the proof goes 2x2 so the numerals stay large. A four-across row at
         390px renders ~14px figures, which is the "tiny metrics" failure. */
  .camp .why__story, .camp .why__case { padding-left: 0; }
  .camp .why .chapter__title { font-size: clamp(1.5rem, 6.6vw, 1.75rem); max-width: 18ch; }
  .camp .why .chapter__num { font-size: 1.6rem; }
  .camp .why .why__story { margin-top: clamp(20px, 5vw, 28px); font-size: 1.06rem; }
  .camp .why__story .drop::first-letter { float: none; font-size: inherit; font-family: inherit;
    line-height: inherit; padding: 0; color: inherit; }
  .camp .why .why__plate { max-width: 100%; margin-inline-start: 0; }
  .camp .why .why__plate img { aspect-ratio: 4 / 3; }
  .camp .why .why__plate figcaption { font-size: 0.9rem; padding-top: 12px; max-width: none; }
  .camp .why .why__case { margin-top: clamp(34px, 9vw, 44px); }
  .camp .why__ask { font-size: 1.22rem; max-width: none; }
  .camp .why__kicker { font-size: 0.94rem; margin-top: 14px; max-width: none; }
  .camp .caps { grid-template-columns: 1fr; margin-top: 20px; }
  .camp .caps li { padding: 9px 0; font-size: 0.94rem; }
  .camp .caps li:nth-child(2) { border-top: 0; }
  /* Chapter I's own bottom padding, scoped to #why so nothing else moves. The
     evidence rail hands off into the film's dark stage, which brings its own
     generous top padding, so 76px of cream on top of that was two paddings
     doing one job. */
  .camp .why { padding-bottom: clamp(52px, 8vw, 96px); }
  .camp .why .proofline { margin-top: clamp(34px, 8vw, 46px); } .camp .proofline { padding-top: clamp(22px, 3vw, 30px); }
  .camp .proofline__title { font-size: 1.2rem; margin-bottom: 20px; }
  .camp .proofline__stats { grid-template-columns: 1fr 1fr; row-gap: clamp(20px, 4.5vw, 26px); }
  .camp .proofline__stats li { padding: 0 0 0 16px; border-left: 1px solid var(--rule); }
  .camp .proofline__stats li:first-child { padding-left: 16px; border-left: 1px solid var(--rule); }
  .camp .proofline__stats b { font-size: clamp(1.55rem, 7vw, 1.95rem); margin-bottom: 7px; }
  .camp .proofline__stats span { font-size: 0.84rem; }
  .camp .foot__row { grid-template-columns: 1fr; gap: 20px; }
  .camp .engine__controls { flex-direction: column; align-items: stretch; }
}
@media (max-width: 480px) {
  .camp .hero { min-height: 94svh; }
  .camp .hero__equation { flex-wrap: wrap; }
  .camp .engine__instrument { height: 400px; gap: 14px; }
  .camp .core__tube { margin-left: 78px; }
  .camp .core__ticks span::before { left: 66px; }
  .camp .engine__total { font-size: clamp(3rem, 15vw, 4.2rem); }
}

/* ══════════════ MOBILE HERO (<= 600px) · REBUILT AS TWO ZONES ════════════════
   This supersedes every previous <=600px hero pass. It is not a correction of
   the overlay hero; the overlay ARCHITECTURE is what was wrong and it is gone.

   ─── WHAT WAS REJECTED, AND WHY IT COULD NOT BE PATCHED ───────────────────
   The photograph was the background canvas for the WHOLE hero (`top: -15%;
   height: 115%` on .hero__media, .hero at 100svh, everything layered over it).
   At 375 that put an eyebrow (since removed), a 2-line display headline, two paragraphs, a
   gold pill, a text link and a film thumbnail on top of two seated students —
   copy printed across the foreground student's chest and the second student's
   torso, and the scrim crushed to 0.90/0.97 to buy legibility, which deleted
   the bottom half of the picture. Smaller type, darker overlays and different
   object-position values are all patches to the wrong architecture: there is
   simply more copy here than a 375px-wide photograph can host.

   THE INERT-CROP TRAP, stated once so it is not re-entered. `object-fit:
   cover` only overflows the axis in which the SOURCE is proportionally longer
   than the BOX. The artwork is 1600x1066 (1.501); any mobile hero box is
   narrower than that, so cover crops WIDTH ONLY and the full 1066 of height is
   always shown. Every `object-position` Y ever written for this hero — the
   deployed `26%`, and the lab's derived `85.5%` — was therefore INERT, which
   is why the back row of standing visitors and the ceiling kept appearing no
   matter what the Y term said. The only way to art-direct a mobile crop is to
   give the image a real two-axis window, which is what section 1 does.

   ─── THE COMPOSITION NOW ──────────────────────────────────────────────────
   Two zones, ONE hero, one continuous --stage ground:

     [MASTHEAD]      the tagline-bearing TLM lockup on solid --stage
     [ZONE 1 photo]  an art-directed window on the classroom, full-bleed width,
                     a crisp top edge under the masthead, dissolving into
                     --stage at its lower edge
     [ZONE 2 story]  the same --stage, carrying every beat in order:
                     EACH DOLLAR, DOUBLED. -> the two campaign paragraphs ->
                     Donate -> See why it matters -> Watch Joe's story
                     (the identity eyebrow that used to open this zone has
                     been removed; the headline is now its first element)

   THE LOGO IS NOT OVER THE PHOTOGRAPH, and that is a decision, not an
   oversight. The band's top edge sits at the masthead's lower edge, so the
   nav's one remaining mark (Give waits for `.is-solid`, see the 768 block) is
   on near-black at full contrast and the picture opens clean. The alternative
   was measured and rejected: the two subjects' heads are at source y215 and
   y262 while the standing back row ends at y~205, so ANY crop that excludes
   the back row puts both heads within 60px of the frame top — i.e. directly
   under the fixed 74px masthead. Holding a white wordmark AND its tagline
   there needs ~0.70 alpha (white on a light cubicle panel otherwise fails
   4.5:1), and 0.70 across the top quarter of the band lands on the foreground
   student's beanie and the second student's face. Protecting the faces and
   darkening them to carry a logo are the same decision; this takes the faces.

   ─── THE CROP, A REAL TWO-AXIS WINDOW ────────────────────────────────────
   One window on the 1600x1066 original:  x 440 · y 200 · 710 x 640.
   The image is sized past the box and translated, so BOTH axes crop and every
   number below is a source pixel coordinate rather than a magic offset:

     width      1600/710   = 225.3521% of the band
     translateX  -440/1600 = -27.5%     (% of the image's own width)
     translateY  -200/1066 = -18.7617%  (% of the image's own height)
     band height  640/710  = 90.1408% of the hero's width

   Width-derived, so the composition is IDENTICAL at 320/360/367/375/390/430
   and only the frame widens. Nothing here is a function of device height, so
   Safari's collapsing chrome cannot reflow the picture.

   WHY THIS WINDOW:
     top    y200 is the seam between the standing back row (which ends ~y205)
            and the classroom furniture. Above it is a band of decapitated
            torsos and a blown-out ceiling; below it the frame opens on the
            cubicle line and the monitor row, which read as a deliberate top
            edge instead of an arbitrary slice.
     left   x440. x340 was tried first and rejected on the render: at y200 it
            caught the lower half of a SECOND-ROW student's head (x330-430),
            i.e. a partial face in the top-left corner. His head ends at y265
            and the foreground beanie begins at y262, so there is no top edge
            that excludes him vertically — the fix has to be horizontal. The
            foreground student's left sleeve now exits the frame, which is what
            sleeves do; his head (x560-760) is untouched.
     right  x1150, for the same reason at the other corner: a standing figure
            at x1180-1260 was clipped at the shoulders. This also drops the
            press/camera crew (x1400+) and the fourth student (x1270-1400),
            whose head the old x1250 edge cut in half. The large foreground
            monitor bleeds off this edge, which is depth, not damage.
     bottom y840 ends the picture on the desk plane, under the keyboard.

   The two corner exclusions are why the band is nearly square (1.11) rather
   than letterboxed: excluding partial people costs width, and width is what
   sets the band's height. A clean frame was worth more than a wider one.

   Where the subjects fall inside the band (fraction = (srcY-200)/640):

     2nd student head top   src 215  ->  2.3%
     2nd student face       src 240-350 ->  6.3%-23.4%
     foreground beanie top  src 262  ->  9.7%
     foreground face        src 300-440 -> 15.6%-37.5%
     2nd student's hands    src 560-600 -> 56.3%-62.5%
     PROTECTED FLOOR        src 661  -> 72.0%  <- nothing above this is touched
     foreground forearm     src 700-840 -> 78.1%-100%    the dissolve's zone

   ─── THE SCRIM HAS TWO JOBS AND NO THIRD ─────────────────────────────────
   It buys NO legibility, because no type is on the photograph. It seats the
   crisp top edge (0.14 -> 0 over the first 5%, a whisper — the second
   student's head starts at 2.2%, so anything heavier would darken a face),
   and it dissolves the lower edge into --stage from the protected floor down.
   The 5%-72% span — both faces, both bodies, the monitors and the second
   student's hands on his keyboard — is left at ZERO.

   The dissolve resolves to solid --stage exactly at the band's bottom, and the
   hero's own background IS --stage, so photography becomes campaign ground with
   no seam, no border, no radius, no shadow and no second section. The scrim
   then runs 190px PAST the band carrying one warm bloom centred on the
   dissolve line, so the room's light spills onto the story surface instead of
   the two zones meeting at a colour change.

   ─── HEIGHT ───────────────────────────────────────────────────────────────
   The hero is in normal flow and grows with its content: no min-height, no
   svh cage (the base 100svh and the 480-block's 94svh are both released here).
   The one height term is a 66svh ceiling on the band, which is slack at every
   portrait phone (280 vs 536 at 375x812) and engages only in a short window,
   where the window simply shows less of the desk.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 600px) {
  /* ── 0 · the numbers the whole composition derives from ────────────────── */
  .camp .hero {
    /* the fixed masthead's real rendered height: nav__row is 14px + a 45px
       tagline lockup + 14px, plus the 1px border. The band starts here. */
    --hero-mast: 74px;
    /* 680/910 of the HERO'S OWN WIDTH — the crop window's own aspect. `cqw`,
       not `vw`, deliberately: the band, the image inside it and the story
       column's top padding must all derive from the same number, and `100vw`
       is not that number wherever the hero is narrower than the viewport (a
       classic scrollbar, any body margin — the wp-preview harness has one, and
       it desynced the crop from its box by 16px). `container-type` below makes
       the hero its own query container, so 1cqw is 1% of the hero. */
    /* THE svh CEILING NOW ENGAGES. At 390x844 the width term resolved to 351px
       and 66svh to 557px, so the ceiling never bound and the band was purely
       width-driven: the photograph, the masthead and the gap pushed Zone 2 down
       to y453, and the donate CTA landed at y902 against an 844px fold - the
       primary action off-screen on one of the most common phones in use. 36svh
       binds on a portrait phone (304px at 844) and binds hard on a short one
       (240px at 667) while the width term still governs wide/short tablets. The
       crop is unchanged in KIND: the image width and translate are both calc'd
       off --hero-band, so the window scales rather than re-framing. */
    --hero-band: min(calc(100cqw * 640 / 710), 34svh);
    /* the breath between the dissolve finishing and the first line of type */
    --hero-gap: clamp(18px, 5vw, 28px);

    /* inline-size containment only: the block axis still grows with content */
    container-type: inline-size;

    /* the cage is gone: top-aligned, normal flow, grows with its content */
    min-height: 0;
    align-items: flex-start;
    background: var(--stage);
  }

  /* ── 1 · ZONE ONE — the photograph as an art-directed window ───────────── */
  .camp .hero__media {
    inset: var(--hero-mast) 0 auto 0;
    height: var(--hero-band);
    overflow: hidden;
  }
  /* Sized past the box and translated, so the window crops in BOTH axes.
     `max-width: none` releases the `.camp img { max-width: 100% }` reset, which
     would otherwise clamp the image back to the box and re-inflict the inert
     crop. The max() keeps full-width coverage if the svh ceiling engages. */
  .camp .hero__media img {
    position: absolute;
    top: 0;
    left: 0;
    width: max(calc(var(--hero-band) * 1600 / 640), 225.3521%);
    height: auto;
    max-width: none;
    transform: translate(-27.5%, -18.7617%);
  }

  /* Stops are calc'd off --hero-band, not given as percentages of the scrim's
     own height, because the scrim is taller than the band: a percentage stop
     would drift against the photograph as the viewport widened. */
  .camp .hero__scrim {
    inset: var(--hero-mast) 0 auto 0;
    height: calc(var(--hero-band) + 190px);
    background:
      linear-gradient(180deg,
        rgba(15, 13, 10, 0.14) 0,
        rgba(15, 13, 10, 0)    calc(var(--hero-band) * 0.05),
        /* ── open photograph: faces, bodies, monitors, untouched ── */
        rgba(15, 13, 10, 0)    calc(var(--hero-band) * 0.72),
        /* the dissolve, from the protected floor down */
        rgba(15, 13, 10, 0.22) calc(var(--hero-band) * 0.78),
        rgba(15, 13, 10, 0.52) calc(var(--hero-band) * 0.87),
        rgba(15, 13, 10, 0.84) calc(var(--hero-band) * 0.94),
        var(--stage)           var(--hero-band),
        var(--stage)           100%),
      /* the room's light spilling onto the story surface, centred on the
         dissolve line and kept off both faces */
      radial-gradient(150% 190px at 78% var(--hero-band),
        rgba(218, 165, 32, 0.075), transparent 70%);
  }

  /* ── 2 · ZONE TWO — the story column, in flow BELOW the photograph ─────── */
  .camp .hero__grid {
    grid-template-columns: 1fr;
    align-items: start;
    /* the one gap left in this grid is lede -> film entry */
    gap: 14px;
    /* percentage padding resolves against the containing block's WIDTH, which
       is the same width the band derives from, so the two can never desync */
    padding-top: calc(var(--hero-mast) + var(--hero-band) + var(--hero-gap));
    padding-bottom: calc(clamp(28px, 7vw, 44px) + env(safe-area-inset-bottom, 0px));
  }

  /* ── 3 · BEAT ONE — the dominant statement ─────────────────────────────── */
  /* NOT shrunk to make room: the deployed 11.4vw/3rem cap rendered 43px, which
     read as a subheading. Dominance is bought with the space around it, not
     with size alone. Same family, same weights, same
     italic gold "doubled." against the cream roman — the desktop contrast,
     preserved. Leading and tracking tighten as the size falls, which is how
     the desktop clamp already behaves. */
  .camp .hero__headline {
    font-size: clamp(3rem, 15.4vw, 4.6rem);
    line-height: 0.9;
    letter-spacing: -0.03em;
    margin: 0;
  }

  /* ── 4 · BEAT TWO — why the match matters ─────────────────────────────── */
  /* Two paragraphs, kept as two paragraphs: the deployed 6px tuck merged them
     into one nine-line slab. 1rem/1.56 is one responsive step under the
     desktop clamp's 1.05rem floor and holds a comfortable ~42ch at 390. Body
     copy is NOT shrunk to pay for the layout — the layout gave it a surface.
     margin-top is the headline -> copy BEAT break, and is larger than it looks
     like it needs because `line-height: 0.9` leaves the headline's last line
     box short of its glyphs: 34px nominal measures ~48px of ink gap, against
     the 20px between the two paragraphs. */
  .camp .hero__sub {
    font-size: 1rem;
    line-height: 1.56;
    max-width: none;
    margin: 34px 0 0;
    color: rgba(244, 239, 226, 0.9);
  }
  .camp .hero__sub + .hero__sub { margin-top: 20px; }

  /* ── 5 · BEATS THREE, FOUR, FIVE — three actions at three weights ──────── */
  .camp .hero__cta {
    flex-direction: column;
    align-items: flex-start;
    flex-wrap: nowrap;
    gap: 0;
    margin: 32px 0 0;
  }
  /* PRIMARY — the one saturated object in the hero. Sized to its CONTENT, not
     stretched: a full-width gold slab at this width reads as a form submit and
     flattens the difference between it and the two navigation actions under
     it. ~237px of a 343px measure at 375, and 51px tall, so the touch target
     clears 44px on its own without a min-height. */
  .camp .hero__cta .btn--primary {
    width: auto;
    font-size: 1.02rem;
    padding: 1.05em 1.9em;
    box-shadow: 0 10px 30px rgba(218, 165, 32, 0.3);
  }
  /* SECONDARY — deliberately subordinate: an editorial text action, not a
     second slab. The pill is dropped (a matched outlined box read as a peer of
     Donate). The hairline is `text-decoration`, not a border on the box, so it
     hugs the words while the box still carries a 44px target. */
  .camp .hero__cta .btn--ghost {
    width: auto;
    min-height: 44px;
    margin-top: 8px;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    font-size: 0.92rem;
    font-weight: 500;
    color: rgba(244, 239, 226, 0.82);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 5px;
    text-decoration-color: rgba(244, 239, 226, 0.34);
  }
  .camp .hero__cta .btn--ghost:hover {
    background: none;
    color: var(--on-dark);
    text-decoration-color: rgba(244, 239, 226, 0.7);
  }
}

/* ══════════ PHONE LANDSCAPE (short + wide) · A THIRD COMPOSITION ═════════════
   Portrait phone and desktop are both APPROVED and are not touched by this
   block. This adds the one viewport class that had no composition of its own.

   ─── WHAT WAS BROKEN ──────────────────────────────────────────────────────
   A landscape phone is WIDER than the 600px portrait breakpoint, so it fell
   through to the tablet/desktop rules and got the overlay hero: the photograph
   as the canvas for everything. Measured at 667x375 it put the headline across
   a standing figure's head, both paragraphs across the foreground student's
   face and chest, the wordmark and the headline fighting in the upper left,
   and Donate roughly 400px below a 375px-tall viewport. Width alone cannot
   classify this device: 667 looks like a small tablet and behaves like a
   phone turned sideways.

   ─── THE TEST, NOT THE DEVICE LIST ────────────────────────────────────────
   Matched on SHAPE — landscape orientation, phone-scale width, and a short
   viewport — so it is one rule rather than seven device hacks:

       (orientation: landscape) and (max-width: 960px) and (max-height: 440px)

   That covers the whole class with headroom at both ends (568x320 · 640x360 ·
   667x375 · 740x360 · 844x390 · 852x393 · 896x414) and excludes everything
   else by construction: portrait phones fail `orientation`, tablets in
   landscape fail `max-height` (1024x768), and desktops fail `max-width`.

   It is authored AFTER the <=600px block on purpose. 568x320 and 640x360 are
   narrow enough to match the portrait block too, so landscape has to win on
   source order; every portrait declaration it needs to beat is re-set below.

   ─── THE COMPOSITION ──────────────────────────────────────────────────────
   The same principle that fixed portrait, turned ninety degrees: THE
   PHOTOGRAPH IS NOT THE CANVAS FOR THE COPY. Portrait stacks the two regions;
   landscape has width to spend, so it sets them side by side.

     [MASTHEAD]   the tagline lockup on solid --stage, full width, so the
                  logo owns its own space and is never over the picture
     [LEFT 44%]   the photograph, the same art-directed window as portrait,
                  dissolving into --stage at its lower edge
     [RIGHT 56%]  the story column on --stage: EACH DOLLAR, DOUBLED. -> the
                  two campaign paragraphs -> Donate -> See why it matters ->
                  Watch Joe's story

   No type is ever over the photograph, so the scrim buys no legibility here
   either. The column is allowed to run past the fold and scroll; nothing is
   shrunk to force a 375px-tall viewport to hold everything.

   ─── THE CROP IS THE APPROVED ONE, UNCHANGED ─────────────────────────────
   Same source window as portrait — x 440 · y 200 · 710 x 640 — so this is the
   same three students, the same top edge on the monitor row, the same
   excluded back row and camera crew. Only the frame it is seen through
   changes. The image is sized off the VISUAL REGION'S HEIGHT rather than its
   width, because here height is the fixed term (viewport minus masthead) and
   width is what varies with the device:

     width  max(--lsp-vis * 2.5, 225.3521%)   2.5 = (1066/640)*(1600/1066)
     translate  -27.5% , -18.7617%            = -440/1600 , -200/1066

   The max() is the coverage guard: the first branch fills the region
   vertically, the second fills it horizontally (1600/710), and whichever is
   larger wins, so no device in the class can expose an uncovered edge.
   Measured at 667x375 the visible window is source x440-1078 by y200-840 —
   both faces, both bodies, the monitors, no ceiling.
   ══════════════════════════════════════════════════════════════════════════ */
@media (orientation: landscape) and (max-width: 960px) and (max-height: 440px) {
  .camp .hero {
    /* the fixed masthead, as in portrait: 14 + 45 + 14 + 1px border */
    --lsp-mast: 74px;
    /* the visual region's height — the one fixed term in this composition */
    --lsp-vis: calc(100svh - var(--lsp-mast));

    display: grid;
    /* minmax(0,...) so a long word can never blow the column out sideways */
    grid-template-columns: minmax(0, 44%) minmax(0, 1fr);
    align-items: start;
    min-height: 100svh;          /* releases the portrait block's min-height: 0 */
    background: var(--stage);
  }

  /* ── LEFT REGION · the photograph ──────────────────────────────────────── */
  /* Both layers are grid items in the same cell, so they stack without either
     one being taken out of flow; the scrim follows the media in the DOM and
     therefore paints over it. */
  .camp .hero__media,
  .camp .hero__scrim {
    position: relative;
    inset: auto;
    grid-column: 1;
    grid-row: 1;
    align-self: start;
    margin-top: var(--lsp-mast);
    height: var(--lsp-vis);
    overflow: hidden;
  }
  .camp .hero__media img {
    position: absolute;
    top: 0;
    left: 0;
    width: max(calc(var(--lsp-vis) * 2.5), 225.3521%);
    height: auto;
    max-width: none;
    transform: translate(-27.5%, -18.7617%);
  }
  /* Two jobs, neither of them legibility: dissolve the lower edge into the
     stage so the picture ends intentionally, and carry one warm bloom off the
     room's light. The right edge is left CRISP — a soft edge there would have
     to darken the second and third students to work, and a clean vertical
     boundary is what makes this read as two regions rather than a faded
     backdrop. The dissolve starts at 74% of the window, which is source y674:
     below both faces and below the second student's hands at y600. */
  .camp .hero__scrim {
    background:
      linear-gradient(180deg,
        rgba(15, 13, 10, 0) 0,
        rgba(15, 13, 10, 0) calc(var(--lsp-vis) * 0.74),
        rgba(15, 13, 10, 0.42) calc(var(--lsp-vis) * 0.87),
        rgba(15, 13, 10, 0.82) calc(var(--lsp-vis) * 0.95),
        var(--stage) 100%),
      radial-gradient(120% 70% at 82% 18%, rgba(218, 165, 32, 0.09), transparent 64%);
  }

  /* ── RIGHT REGION · the story column ───────────────────────────────────── */
  .camp .hero__grid {
    grid-column: 2;
    grid-row: 1;
    grid-template-columns: 1fr;
    align-items: start;
    gap: 14px;
    max-width: none;
    padding-top: calc(var(--lsp-mast) + 16px);
    padding-right: max(20px, env(safe-area-inset-right, 0px));
    padding-bottom: calc(26px + env(safe-area-inset-bottom, 0px));
    padding-left: 22px;
  }

  /* BEAT ONE — the proposition. Sized for a ~350px column at 667: large
     enough to stay unmistakably the campaign statement, nowhere near the
     portrait ramp (15.4vw would render 102px here and eat the viewport). */
  .camp .hero__headline {
    font-size: clamp(1.8rem, 4.8vw, 2.5rem);
    line-height: 0.95;
    letter-spacing: -0.025em;
    margin: 0;
  }

  /* BEAT TWO — the explanation. 0.95rem is a responsive step of the desktop
     clamp, not a shrink-to-fit: it holds ~40 characters in this column. */
  .camp .hero__sub {
    font-size: 0.95rem;
    line-height: 1.5;
    max-width: 42ch;
    margin: 16px 0 0;
    color: rgba(244, 239, 226, 0.9);
  }
  .camp .hero__sub + .hero__sub { margin-top: 12px; }

  /* BEATS THREE, FOUR, FIVE — stacked, because Donate and the Why link laid
     side by side measure ~400px against a ~350px column. Same three weights
     as portrait: one saturated control, one underlined text action, one
     thumbnail entry. */
  .camp .hero__cta {
    flex-direction: column;
    align-items: flex-start;
    flex-wrap: nowrap;
    gap: 0;
    margin: 18px 0 0;
  }
  .camp .hero__cta .btn--primary {
    width: auto;
    font-size: 0.97rem;
    padding: 0.95em 1.7em;
    box-shadow: 0 10px 26px rgba(218, 165, 32, 0.28);
  }
  .camp .hero__cta .btn--ghost {
    width: auto;
    min-height: 44px;
    margin-top: 6px;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    font-size: 0.88rem;
    font-weight: 500;
    color: rgba(244, 239, 226, 0.82);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 5px;
    text-decoration-color: rgba(244, 239, 226, 0.34);
  }
  .camp .hero__cta .btn--ghost:hover {
    background: none;
    color: var(--on-dark);
    text-decoration-color: rgba(244, 239, 226, 0.7);
  }
}

@media (prefers-reduced-motion: reduce) {
  .camp *, .camp *::before, .camp *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; }
}

/* ── THE APPROVED PRISM (SIGNATURE III) ───────────────────────────────────────
   Ported from the approved Design Lab (design-lab/experiences/campaign/
   campaign.css). kses strips <svg> written through Elementor, so the page ships
   a semantic `[data-prism]` placeholder and campaign-flagship.js builds the
   geometry at runtime; these rules style what it builds. Two compositions of one
   mechanism — --h left→right on desktop, --v top→bottom on mobile. The lens is a
   DOM element so the rail's non-uniform stretch cannot skew it. */
.camp .doubling { max-width: 980px; }
.camp .doubling__flow { position: relative; margin-inline: clamp(-26px, -1.6vw, -10px); }
/* The pre-rewrite background prism is retired: the plugin now BUILDS the prism
   into [data-prism], so this span draws nothing and would only contribute an
   empty 40px line box between the rails and their label. Production already
   hides it (the page's own APPROVED PRISM MIGRATION block); the rule belongs
   here now that the plugin, not the page, owns the prism. */
.camp .doubling__prism { display: none; }
.camp .prism__svg { width: 100%; height: clamp(120px, 13vw, 190px); display: block; overflow: visible; }
.camp .prism__in, .camp .prism__out { fill: none; stroke-linecap: round; vector-effect: non-scaling-stroke; stroke-width: 3.5; }
.camp .prism__in { stroke: var(--rule-strong); }
.camp .prism__out--donor { stroke: var(--rule-strong); }
.camp .prism__out--match { stroke: var(--gold); }
.camp .prism__lens-el {
  position: absolute; left: 50%; top: 50%; width: clamp(34px, 3.4vw, 46px); aspect-ratio: 1;
  transform: translate(-50%, -50%) rotate(45deg);
  background: linear-gradient(135deg, rgba(255, 245, 214, 0.95), rgba(218, 165, 32, 0.35));
  border: 1.5px solid var(--gold); border-radius: 5px;
  box-shadow: 0 0 26px rgba(218, 165, 32, 0.5), inset 0 0 14px rgba(255, 255, 255, 0.7);
}
/* Name the two beams — without this the viewer must guess which line is which. */
.camp .doubling__beamlbl { position: absolute; right: 2%; font-size: 0.72rem; letter-spacing: 0.13em;
  text-transform: uppercase; font-weight: 600; white-space: nowrap; line-height: normal; }
.camp .doubling__beamlbl--donor { top: 8%; color: var(--text-muted); }
.camp .doubling__beamlbl--match { bottom: 8%; color: var(--gold-text-light); }
/* Desktop: the label sits centred under the rail, overlaid rather than in flow.
   Centred with left/right/text-align rather than translateX(-50%): the page's
   reveal base state sets `transform: translateY(14px)` on revealed children,
   which would clobber a centring transform and leave the label starting at the
   midpoint instead of centred on it. Verified in the cascade fixture. */
.camp .doubling__flow-label { position: absolute; left: 0; right: 0; bottom: -6px; text-align: center; white-space: nowrap; line-height: normal; }
.camp .prism__svg--v { display: none; }

/* MOBILE RECOMPOSITION — the mechanism turns vertical and the whole equation
   centres. This is an intentional recomposition, not desktop squeezed smaller. */
@media (max-width: 768px) {
  .camp .doubling { flex-direction: column; align-items: center; text-align: center; gap: 0; }
  .camp .doubling__side, .camp .doubling__side--sum { align-items: center; text-align: center; }
  .camp .doubling__lbl { margin-inline: auto; }
  .camp .doubling__flow { width: 100%; margin-inline: 0; display: block; }
  .camp .prism__svg--h { display: none; }
  .camp .prism__svg--v { display: block; width: min(240px, 62vw); height: clamp(190px, 46vw, 260px); margin: 10px auto 0; }
  .camp .prism__lens-el { top: 50%; }
  .camp .doubling__flow-label { position: static; transform: none; display: block; text-align: center; margin-top: 10px; }
  .camp .doubling__beamlbl { position: static; display: inline-block; margin: 8px 10px 0; }
  .camp .doubling__beamlbl--donor { transform: translateX(-14%); }
  .camp .doubling__beamlbl--match { transform: translateX(14%); }
}

/* ── Checkout refusal ──────────────────────────────────────────────────────────
   A gift the server will not accept has to be SEEN. Ink on a warm alarm ground,
   not gold: gold is the campaign's affirmative colour and reads as success. */
/* The CTA is inert while the server mints the Session — one click, one gift. */
.camp [data-checkout].is-busy{opacity:.72;pointer-events:none;cursor:progress;}

/* ── Anchor landings must clear the fixed nav ──────────────────────────────────
   The campaign nav is position:fixed, 77px tall, z-index 100. Any anchor jump
   (#donate — including the Stripe cancel_url a donor returns through) scrolls the
   target flush to the viewport top, i.e. underneath it. scroll-margin-top is the
   supported fix: it changes where scrolling STOPS, so nothing is repositioned and
   no z-index or pointer-events games are needed. Generous enough to leave the
   section heading visible, not just clear of the bar. */
.camp [id],
.camp [data-scene],

/* ── Anchor landings must also be SMOOTH ───────────────────────────────────────
   `scroll-margin-top` above fixes WHERE an anchor jump stops; this fixes HOW it
   travels. "See why it matters" (#why) is a bare <a> with no JS behind it, so
   without this it teleported while the hero's film entry — which has its own
   handler in campaign-flagship.js — eased. Two navigation actions sitting one
   above the other behaved differently.

   Scrolling is a property of the scroll CONTAINER, which is the root element,
   not anything inside `.camp`; `:has()` keeps it scoped to campaign pages so no
   other template on the site inherits it. Gated on `no-preference` rather than
   overridden under `reduce`, so a visitor who asked for no motion gets an
   immediate jump — including through the film handler, which re-reads the
   preference at activation time. Nothing here depends on JS. */
@media (prefers-reduced-motion: no-preference) {
  html:has(.camp) { scroll-behavior: smooth; }
}


/* ══════════════════════════════════════════════════════════════════════════
   EQUIPMENT CHAPTER (#builds) — "What the equipment makes possible."

   This chapter answers the question Joe asks in Chapter II, so it is built on
   Chapter II's geometry rather than on the legacy 133px spine it used before.
   One spine, one deliberate break:

     --b-col   780px   headline block, body, index, closing quote
     --b-wide  940px   the photograph, and ONLY the photograph
     --b-read  660px   reading measures: the lede and John's line

   At 1440 that puts marker, headline, lede, index and quote on x=319 (the same
   x as the film above) while the picture breaks to 239 — 80px proud on each
   side. It previously ran three competing left edges at 133 / 32 / 809 with the
   .split grid spanning 1402 of a 1416 viewport, which is why the chapter read
   as four stacked modules rather than one composition.

   The old layout also vertically centred a 773px text column against an 881px
   image, so neither column's top edge related to the other. The composition is
   now stacked and top-aligned by construction: there is no second column to
   float against.
   ══════════════════════════════════════════════════════════════════════════ */
.camp #builds {
  --b-col: 780px;          /* spine column: head, lede, quote */
  --b-band: 940px;         /* the one deliberate break: the evidence band */
  --b-read: 660px;         /* Chapter II's reading measure */
  padding-block: clamp(48px, 5vw, 78px);
}
/* +48 = the container's own gutter either side, so each block's CONTENT box
   lands on the spine rather than 24px inside it. */
.camp #builds .chapter__head,
.camp #builds .builds__body,
.camp #builds .builds__close { max-width: calc(var(--b-col) + 48px); }
.camp #builds .builds__band  { max-width: calc(var(--b-band) + 48px); }

/* HEADLINE — was 54.4px at 1440: larger than Chapter II's question (38.2) and
   than Chapter I's head (45.4), so the answer arrived louder than the question.
   2.5vw reads 36px, one step UNDER Joe's question, which is the correct
   relationship for a chapter that is replying to it. */
.camp #builds .chapter__head { margin-bottom: 0; }
.camp #builds .chapter__title { font-size: clamp(1.5rem, 2.5vw, 2.3rem); line-height: 1.1; max-width: 18em; }

.camp #builds .builds__body { margin-top: clamp(22px, 2.4vw, 34px); }
.camp #builds .builds__lede {
  margin: 0; max-width: var(--b-read);
  font-size: clamp(1.02rem, 1.28vw, 1.15rem); line-height: 1.55; text-wrap: pretty;
}

/* THE BAND — evidence and what it enables, side by side and TOP-ALIGNED. The
   old .split centred a 773px text column against an 881px image so neither
   top edge related to the other; align-items:start is the fix, and it is also
   what keeps the chapter from becoming a vertical stack. Stacked, this same
   content measured 1678px — TALLER than the 1486 it replaced. */
.camp #builds .builds__band {
  margin-top: clamp(28px, 3.2vw, 46px);
  display: grid; grid-template-columns: minmax(0, 1.43fr) minmax(0, 1fr);
  gap: clamp(28px, 3.9vw, 56px); align-items: start;
}
.camp #builds .builds__frame { margin: 0; }
.camp #builds .builds__frame img {
  width: 100%; height: auto; aspect-ratio: 3 / 2; object-fit: cover; object-position: 42% 50%;
  display: block; border-radius: var(--radius-md);
}
.camp #builds .builds__frame figcaption {
  margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--rule);
  font-size: 0.84rem; line-height: 1.45; color: var(--text-muted);
}

/* THE INDEX — was seven unstyled lines at body scale: indistinguishable from
   the paragraph above them and unscannable. Numerals are structural here, one
   hairline per row, single column because it is already a narrow band member. */
.camp #builds .builds__index-eyebrow {
  margin: 0; font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase;
  font-weight: 600; color: var(--text-muted);
}
.camp #builds .builds__index { list-style: none; margin: clamp(10px, 1.2vw, 16px) 0 0; padding: 0; }
.camp #builds .builds__index li {
  display: grid; grid-template-columns: 2.4em 1fr; align-items: baseline;
  padding: 9px 0; border-bottom: 1px solid var(--rule);
  font-size: 0.94rem; line-height: 1.35; font-weight: 500;
}
.camp #builds .builds__index li:first-child { border-top: 1px solid var(--rule); }
.camp #builds .builds__index span {
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.08em;
  color: var(--gold-text-light); font-variant-numeric: tabular-nums;
}

/* JOHN'S CLOSE — was 36.8px gold italic behind a 3px gold bar, glued 0.6em
   under the last bullet: a second headline, not a conclusion. Chapter II's
   closing-ask treatment exactly. */
.camp #builds .builds__close { margin: clamp(32px, 3.6vw, 52px) auto 0; }
.camp #builds .builds__close blockquote {
  margin: 0; max-width: var(--b-read);
  font-family: var(--font-display); font-weight: 500; font-style: normal;
  font-size: clamp(1.3rem, 2vw, 1.85rem); line-height: 1.3; letter-spacing: -0.012em;
  color: var(--ink); text-wrap: pretty;
}
.camp #builds .builds__close figcaption {
  margin-top: clamp(12px, 1.3vw, 18px); display: flex; flex-direction: column; gap: 4px;
}
.camp #builds .builds__close figcaption b {
  font-family: var(--font-display); font-weight: 500; font-size: clamp(1rem, 1.1vw, 1.12rem); color: var(--ink);
}
.camp #builds .builds__close figcaption span {
  font-size: 0.74rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-muted);
}

/* TABLET AND BELOW — the band stacks, but the index does NOT become a long
   single file: at these widths it takes the two balanced columns it could not
   afford beside the picture, so seven items stay one glance rather than seven
   scrolls. That is the intentional transformation, not a collapse. */
@media (max-width: 900px) {
  .camp #builds .builds__band { grid-template-columns: 1fr; gap: clamp(24px, 3.4vw, 34px); }
  .camp #builds .builds__index { display: grid; grid-template-columns: 1fr 1fr; column-gap: clamp(20px, 3vw, 40px); }
  .camp #builds .builds__index li:nth-child(2) { border-top: 1px solid var(--rule); }
  .camp #builds .builds__index li:last-child { grid-column: 1 / -1; }
}
@media (max-width: 620px) {
  .camp #builds .builds__index { grid-template-columns: 1fr; }
  .camp #builds .builds__index li:nth-child(2) { border-top: 0; }
}
/* SHORT LANDSCAPE — a phone on its side gets 390px of height; the picture at
   full column width rendered 474px there, 122% of the viewport, so the evidence
   alone was more than one screen. Portrait phones never match this query. */
@media (max-height: 460px) and (orientation: landscape) {
  /* Scale the frame, never re-crop it: aspect-ratio 3/2 is retained and the
     width follows the height, so the student, the headphones and the screen
     survive exactly as they do everywhere else. Setting aspect-ratio:auto here
     letterboxed the picture to 2.74:1 and cut the top of his head. */
  .camp #builds .builds__frame img { height: 58vh; width: auto; max-width: 100%; }
}


/* ══════════════════════════════════════════════════════════════════════════
   CHAPTER V (#engine) — "Where the campaign stands."

   PRESENTATION ONLY. Every figure on screen still arrives from the same
   authoritative source: [data-engine] and the data-eng-* bindings are untouched,
   as are the ledger, /totals, the webhook verification, the match cap and the
   campaign math. Nothing here hard-codes a value.

   The chapter previously ran three competing left edges — head 133, engine 32,
   instrument 960 — with the engine spanning 1354 of a 1416 viewport, which is
   what made a campaign result read as an analytics dashboard. It now uses the
   geometry the locked chapters share:

     --e-col   780px   head, lede: the spine at x=319
     --e-band  940px   the one deliberate break, at x=239, same as Chapter IV

   The stage inside that band is a two-region composition, TOP-ALIGNED: the
   story on the left, the instrument as supporting evidence on the right. It was
   align-items:center, which floated a 410px readout against a 522px instrument
   so neither top edge related to the other and the space between the lede and
   the total opened into dead air.
   ══════════════════════════════════════════════════════════════════════════ */
.camp #engine {
  --e-col: 780px; --e-band: 940px; --e-read: 660px;
  padding-block: clamp(48px, 5vw, 78px);
}
/* ONE LEFT EDGE. The head used to sit in the narrower --e-col measure while the
   body sat in --e-band, so the numeral and title hung 56px right of the total
   and the breakdown. Both now share the band. */
.camp #engine .chapter__head { max-width: calc(var(--e-band) + 48px); margin-bottom: 0; }
.camp #engine > .container:not(.chapter__head) { max-width: calc(var(--e-band) + 48px); }

/* HEADING — was 54.4px, hero volume for a chapter that reports a result.
   36px is Chapter IV's head exactly, and one step under Joe's question. */
.camp #engine .chapter__title { font-size: clamp(1.5rem, 2.5vw, 2.3rem); line-height: 1.1; max-width: 18em; }
.camp #engine .chapter__lede {
  max-width: var(--e-read); margin-top: clamp(14px, 1.4vw, 20px);
  font-size: clamp(1.02rem, 1.28vw, 1.15rem); line-height: 1.55; text-wrap: pretty;
}
.camp #engine .engine { margin-top: clamp(28px, 3.2vw, 46px); }

/* MOBILE — the reading order is the story first, the instrument second, so the
   visitor understands the result before meeting the visualisation. The
   instrument keeps a real but bounded height rather than becoming a full-screen
   object, and the legend is allowed to wrap instead of clipping. */
/* 900 AND BELOW — two columns would cramp the breakdown, so the story stacks
   above the instrument. The instrument centres under it and keeps a real but
   bounded height rather than becoming a full-screen object. */
@media (max-width: 900px) {
  .camp #engine .engine__stage { grid-template-columns: minmax(0, 1fr); gap: clamp(30px, 5vw, 44px); }
  .camp #engine .engine__instrument { justify-self: center; height: clamp(380px, 52vh, 460px); gap: 16px; }
  .camp #engine .core__tube { margin-left: 76px; }
  .camp #engine .engine__total { font-size: clamp(3rem, 9vw, 5rem); }
}
/* NARROW PHONE - the vessel and its tick gutter step down together so the
   scale labels keep their own column instead of running under the tube. */
@media (max-width: 560px) {
  .camp #engine .core__tube { width: 66px; margin-left: 70px; }
  .camp #engine .engine__stats > div { padding: 9px 0; }
}
/* LANDSCAPE PHONE — the width is there, so the two sides stay beside each
   other rather than falling into the portrait stack. The instrument takes a
   FIXED column so the legend cannot push it wider than the information beside
   it, and the intro steps down so it does not eat the whole short viewport.
   Portrait phones never match this query. */
@media (min-width: 640px) and (max-height: 520px) and (orientation: landscape) {
  .camp #engine .engine__stage { grid-template-columns: minmax(0, 1fr) 290px; gap: 20px; }
  .camp #engine .engine__instrument { justify-self: end; height: clamp(220px, 60vh, 270px); gap: 12px; }
  .camp #engine .core__tube { width: 84px; margin-left: 66px; }
  .camp #engine > .container .core__ticks span::before { left: 52px; }
  .camp #engine .core__ticks em { font-size: 0.62rem; }
  .camp #engine .core__ticks small { font-size: 0.5rem; }
  .camp #engine .core__legend { max-width: 290px; font-size: 0.58rem; gap: 4px; margin-top: 10px; }
  .camp #engine .reservoir__label { font-size: 0.55rem; }
  .camp #engine { padding-block: clamp(40px, 7vw, 56px); }
  .camp #engine .container { padding-block: 6px; }
  .camp #engine .engine { margin-top: 8px; }
  .camp #engine .chapter__head { margin-bottom: 16px; }
  .camp #engine .chapter__num { font-size: 1.3rem; margin-bottom: 0.16em; padding-bottom: 0.16em; }
  .camp #engine .chapter__title { font-size: 1.4rem; }
  .camp #engine .chapter__lede { font-size: 0.84rem; max-width: 56ch; margin-top: 8px; }
  .camp #engine .engine__total { font-size: clamp(2.1rem, 6vw, 2.9rem); }
  .camp #engine .engine__stats dd { font-size: 1.15rem; }
}


/* ════════════════════════════════════════════════════════════════════════════
   CHAPTER VI · THE GIVING INSTRUMENT — B · THE MEASURE
   Approved design direction. Carried verbatim from the surface that was
   approved in pixels (design-lab/wp-preview/section-vi.css, now retired).

   Reference: The Last Mile's own donation form (thelastmile.org/donate). What
   that form gets right and this keeps — one contained giving surface rather
   than an infographic; a vertical stack of financial decisions; amounts as
   real controls with real boundaries; the custom amount as a NESTED, labelled
   field ($ … USD); one unmistakable primary action; legal sitting quietly
   beneath the transaction. What it does NOT keep: the frequency toggle (this
   campaign takes one-time gifts only — nothing to choose, so nothing is
   shown), the dedication control, and its generic grey SaaS execution.

   MATERIAL. The instrument is cut from the campaign's own stage material and
   set on the warm paper, so Chapter V's dark standing carries straight into
   the object the donor acts on, and the paper marks the turn from watching to
   giving.

   SELECTION LAW. Selection INVERTS the control's material and is SEALED WITH
   GOLD: a chosen amount goes paper-solid with an ink numeral and a 4px gold
   seat across its lower edge. That is why selection reads instantly at a
   glance, why it can never be confused with the gold focus ring, and why gold
   never has to become a large field of colour.
   ════════════════════════════════════════════════════════════════════════════ */

/* ── THE V → VI HANDOFF ─────────────────────────────────────────────────────
   Chapter V ends with its own locked padding; the shared .chapter rule then
   added a second full pad before "VI", so the donor crossed a third of a
   screen of empty cream and it read as a gap rather than a handoff. Scoped to
   #give ONLY — Chapters I-V keep the approved rhythm untouched. The material
   change from stage to paper is what announces the turn from watching to
   giving; the chapter should land on it, not a beat later. */
.camp #give { padding-top: clamp(56px, 7vw, 104px); }

/* ── composition ────────────────────────────────────────────────────────────
   Editorial introduction left, instrument right — the same asymmetry the rest
   of the campaign uses for head + figure, and the same composition the TLM
   page uses (story left, form right). VI still reads as a chapter; the
   transaction is its culmination, not a detour. */
.camp .give6 {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 560px);
  gap: clamp(32px, 6vw, 88px);
  align-items: start;
}
.camp .give6__head { padding-top: 6px; }
.camp .give6 .chapter__title { max-width: 18ch; }
.camp .give6__lede {
  font-size: clamp(1rem, 1.25vw, 1.15rem); line-height: 1.6; color: var(--text-muted);
  margin: clamp(18px, 2vw, 26px) 0 0; max-width: 38ch;
}
.camp .give6__lede strong { color: var(--ink); font-weight: 600; }

/* Live match availability. Campaign state — SILENT until /totals verifies it,
   so the block occupies nothing at all while the runtime leaves it empty.
   Muted ink, never gold body copy. */
/* ── MATCH AVAILABILITY · CHAPTER V's SCARCITY, AT THE DECISION ──────────────
   MEASURED PROBLEM (live, 1440): the head block ranked
       title 54px Fraunces · lede 18px Inter · AVAILABILITY 15px Inter · legal 12px
   so the one live, conversion-relevant fact in Chapter VI — how much of Joe's
   match is still on the table — was set SMALLER than the supporting paragraph
   above it and in the same grey as the legal copy. Chapter V spends a whole
   dark chapter establishing that the match is being consumed and that there is
   still room; that momentum arrived at the decision as a footnote.

   THE RANK IS NOW FOUR CLEAN STEPS: 54 → 34 → 18 → 12.
       "Make your gift — and watch it double."   54px   the chapter, unchallenged
       $77,759                                   34px   the live campaign state
       the lede                                  18px   supporting
       trust + legal                             12px   subordinate

   The figure leads on its own line and the sentence completes beneath it, so
   the number is what ranks while the words stay at supporting scale and cannot
   read as a second headline. Same sentence, same order, same punctuation — the
   only change is that the figure is now an element type can address.

   NOT A CARD. No background, no border box, no radius, no shadow. One gold rule
   on the spine — the campaign's existing signal for "match" — and type. Gold is
   on the numeral only, which is the campaign's rule for gold everywhere else
   (the proof rail's figures, the equation's $2, the measure's total).

   It does not compete with the giving instrument: it is paper-side, left of the
   dark surface, and finishes well above the amount controls in reading order at
   every width. On mobile the chapter is one column and DOM order gives the
   requested sequence directly — headline, availability, amounts, the measure,
   checkout, trust.

   #7a5e0c on #f6efdc is 5.33:1 — AA at any size, so the promotion does not
   spend the contrast budget the old 15px grey was already close to. */
.camp .give6__avail {
  margin: clamp(30px, 3.4vw, 46px) 0 0; max-width: 30ch;
  font-family: var(--font-body);
  font-size: clamp(0.97rem, 1.1vw, 1.04rem); line-height: 1.5; color: var(--text-muted);
  padding-left: clamp(14px, 1.2vw, 18px); border-left: 3px solid var(--gold);
  /* `pretty` only protects the last line from a single word and still left
     "available." alone under a full line. `balance` evens both lines, so the
     sentence completes as "of the $100,000 match / is still available." */
  text-wrap: balance;
}
.camp .give6__avail b {
  display: block;
  font-family: var(--font-display); font-weight: 500;
  /* THE FLOOR IS LOAD-BEARING. At 1.7rem the figure held 27px while Chapter
     VI's headline had already come down to 30px on portrait phones — a 3px
     gap, which is not a rank, it is a tie. 1.45rem keeps the headline clearly
     ahead at every audited width: 54/34 · 41/24 · 31/23 · 30/23. */
  font-size: clamp(1.45rem, 2.35vw, 2.15rem); line-height: 1.02;
  letter-spacing: -0.015em; color: var(--gold-text-light);
  margin-bottom: clamp(5px, 0.5vw, 8px);
  font-variant-numeric: tabular-nums;
}
/* Unverified renders NO child nodes, so the whole block collapses. An
   unverified match figure must never occupy space, let alone be read. */
.camp .give6__avail:empty { display: none; }

/* ── the instrument ─────────────────────────────────────────────────────────
   Cut from the campaign's own stage material and set on the warm paper, so
   Chapter V's dark standing carries straight into the object the donor acts
   on, and the paper marks the turn from watching to giving. */
.camp .inst {
  border-radius: 14px; padding: clamp(22px, 2.2vw, 32px); position: relative; max-width: 100%;
  background: linear-gradient(180deg, var(--stage-2, #1c1913), var(--stage, #14120e));
  border: 1px solid rgba(244, 239, 226, 0.14);
  color: var(--on-dark, #f4efe2);
  box-shadow: 0 36px 70px -40px rgba(20, 18, 14, 0.85);
}
/* A fieldset sizes to its children's min-content unless min-width is reset —
   without this the amount grid pushes the instrument wider than the phone. */
.camp .inst__amounts { border: 0; padding: 0; margin: 0; min-width: 0; width: 100%; }
.camp .inst__label {
  display: block; float: none; padding: 0; margin: 0 0 12px;
  font-family: var(--font-body); font-size: 0.72rem; letter-spacing: 0.16em;
  text-transform: uppercase; font-weight: 600; color: rgba(244, 239, 226, 0.66);
}

/* ── amount controls · real buttons, real boundaries, tabular figures ─────── */
.camp .amount-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
.camp .amount {
  position: relative; overflow: hidden; min-height: 62px; padding: 0 6px; width: auto;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-display); font-weight: 500; font-size: clamp(1.15rem, 1.5vw, 1.45rem);
  font-variant-numeric: tabular-nums; letter-spacing: -0.01em; border-radius: 10px; cursor: pointer;
  background: rgba(244, 239, 226, 0.03); color: var(--on-dark, #f4efe2);
  border: 1.5px solid rgba(244, 239, 226, 0.22); box-shadow: none;
  transition: background 200ms ease, color 200ms ease, border-color 200ms ease,
              transform 200ms cubic-bezier(.16,1,.3,1), box-shadow 200ms ease;
}
.camp .amount > span { position: relative; z-index: 1; }
.camp .amount::after {                    /* the gold seat — the seal on a chosen amount */
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 4px;
  background: var(--gold, #daa520); transform: scaleX(0); transform-origin: 50% 100%;
  transition: transform 220ms cubic-bezier(.16,1,.3,1);
}
.camp .amount.is-active::after { transform: scaleX(1); }
.camp .amount:hover {
  border-color: rgba(244, 239, 226, 0.5); background: rgba(244, 239, 226, 0.08);
  transform: translateY(-1px);
}
.camp .amount.is-active {
  background: var(--paper, #fbf6e8); color: var(--ink, #23221f); border-color: var(--paper, #fbf6e8);
  box-shadow: 0 12px 26px -14px rgba(0, 0, 0, 0.8);
}
.camp .amount:active { transform: translateY(0) scale(0.985); }
.camp .amount:focus-visible,
.camp .cta6:focus-visible,
.camp .amount-custom__field:focus-within { outline: 3px solid var(--gold, #daa520); outline-offset: 3px; }

/* ── other amount ─────────────────────────────────────────────────────────── */
.camp .amount-custom {
  display: block; margin-top: 12px; border-radius: 10px; padding: 12px 14px 14px;
  position: relative; overflow: hidden;
  border: 1.5px solid rgba(244, 239, 226, 0.22); background: rgba(244, 239, 226, 0.03);
}
.camp .amount-custom::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 4px;
  background: var(--gold, #daa520); transform: scaleX(0); transform-origin: 50% 100%;
  transition: transform 220ms cubic-bezier(.16,1,.3,1);
}
.camp .amount-custom.is-on::after { transform: scaleX(1); }
.camp .amount-custom.is-on { border-color: var(--paper, #fbf6e8); }
.camp .amount-custom__lbl {
  display: block; font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase;
  font-weight: 600; color: rgba(244, 239, 226, 0.66); margin-bottom: 9px;
}
.camp .amount-custom__field {
  display: flex; align-items: center; gap: 10px; min-height: 54px; padding: 0 14px; border-radius: 8px;
  /* OPAQUE, not rgba(0,0,0,.32). A translucent fill leaves the effective
     background of the amount input ambiguous — over the instrument's gradient
     it composited to a mid grey, and the cream figure measured 1.95:1 against
     it on the live page. A financial field the donor types a gift into must
     have unambiguous, genuinely sufficient contrast. #12100c is the same
     visual weight, resolved. */
  background: #12100c; border: 1px solid rgba(244, 239, 226, 0.12);
}
.camp .amount-custom__sign { font-family: var(--font-display); font-size: 1.25rem; line-height: 1; color: rgba(244, 239, 226, 0.6); }
.camp .amount-custom input {
  flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; color: var(--on-dark, #f4efe2);
  font-family: var(--font-display); font-weight: 500; font-size: 1.35rem;
  font-variant-numeric: tabular-nums; padding: 0.6em 0; caret-color: var(--gold, #daa520);
}
.camp .amount-custom input::placeholder { color: rgba(244, 239, 226, 0.3); }
.camp .amount-custom__cur {
  font-size: 0.72rem; letter-spacing: 0.12em; text-transform: uppercase; font-weight: 600;
  color: rgba(244, 239, 226, 0.6);
}

/* ── THE MEASURE ────────────────────────────────────────────────────────────
   Three quantities, ONE shared scale. The donor does not read that the match
   doubles the gift — they see the second measure arrive alongside the first
   and the two join into the third. The fractions are the production runtime's
   own figures, read back (section-vi-review.js); there is no second copy of
   the match model anywhere in this file. */
.camp .measure {
  margin-top: clamp(20px, 2vw, 26px); padding-top: clamp(16px, 1.7vw, 22px);
  border-top: 1px solid rgba(244, 239, 226, 0.16);
}
.camp .measure__row {
  display: grid; grid-template-columns: 1fr auto; align-items: baseline;
  column-gap: 16px; row-gap: 10px; padding: 11px 0;
}
.camp .measure__k { font-size: 0.95rem; line-height: 1.3; color: rgba(244, 239, 226, 0.74); }
.camp .measure__v {
  font-family: var(--font-display); font-weight: 500; font-size: clamp(1.35rem, 1.9vw, 1.7rem);
  font-variant-numeric: tabular-nums; line-height: 1; justify-self: end; color: var(--on-dark, #f4efe2);
}
.camp .measure__row--match .measure__v { color: var(--gold, #daa520); }
.camp .measure__row--total { margin-top: 10px; padding-top: 16px; border-top: 1px solid rgba(244, 239, 226, 0.2); }
.camp .measure__row--total .measure__k { font-weight: 600; font-size: 1rem; color: var(--paper, #fbf6e8); }
.camp .measure__row--total .measure__v {
  font-weight: 600; font-size: clamp(2.2rem, 3.5vw, 3.05rem); letter-spacing: -0.02em; color: var(--gold, #daa520);
}

.camp .measure__bar {
  display: block; grid-column: 1 / -1; height: 6px; border-radius: 3px;
  background: rgba(244, 239, 226, 0.1); overflow: hidden;
}
.camp .measure__bar i {
  display: block; height: 100%; width: 0; border-radius: 3px;
  transition: width 520ms cubic-bezier(.16,1,.3,1);
}
/* the donor's dollars, then the dollars that answer them, at the same scale */
.camp .measure__bar--gift  i { width: calc(var(--gift-frac, 0.5) * 100%);  background: var(--on-dark, #f4efe2); }
.camp .measure__bar--match i { width: calc(var(--match-frac, 0.5) * 100%); background: var(--gold, #daa520); }
/* the third measure is the first two joined end to end — the split is set from
   the REAL gift/match split, so a capped match shows as a short gold tail
   rather than a tidy half */
.camp .measure__bar--total { height: 8px; border-radius: 4px; }
.camp .measure__bar--total i {
  width: 100%;
  background: linear-gradient(90deg,
    var(--on-dark, #f4efe2) 0 calc(var(--gift-frac, 0.5) * 100%),
    var(--gold, #daa520)    calc(var(--gift-frac, 0.5) * 100%) 100%);
}

.camp .measure__cap {
  margin: 14px 0 0; font-size: 0.84rem; line-height: 1.5; padding: 11px 13px; border-radius: 6px;
  background: rgba(218, 165, 32, 0.1); color: #f0d79a; border-left: 3px solid var(--gold, #daa520);
}
.camp .measure__cap[hidden] { display: none; }

/* ── the action · sits directly with the decision it acts upon ───────────── */
.camp .cta6 {
  text-decoration: none; display: flex; align-items: center; justify-content: center; width: 100%;
  min-height: 64px; margin-top: clamp(20px, 2.2vw, 26px); padding: 0 20px;
  font-family: var(--font-body); font-weight: 600; font-size: 1.04rem;
  background: var(--gold, #daa520); color: var(--ink, #23221f); border: 0; border-radius: 10px; cursor: pointer;
  box-shadow: 0 14px 30px -18px rgba(0, 0, 0, 0.9);
  transition: background 200ms ease, color 200ms ease, transform 200ms cubic-bezier(.16,1,.3,1);
}
.camp .cta6:hover { background: var(--gold-deep, #7a5e0c); color: #fff; transform: translateY(-1px); }
.camp .cta6:active { transform: translateY(0) scale(0.995); }
/* the refusal surface — a donor must never learn a gift was refused from an alert */
.camp .cta6__error {
  margin: 12px 0 0; font-size: 0.86rem; line-height: 1.5; color: #f0b784;
  padding-left: 12px; border-left: 3px solid #b4633a;
}
.camp .cta6__error[hidden] { display: none; }

.camp .inst__trust { margin: 16px 0 0; font-size: 0.78rem; line-height: 1.6; color: rgba(244, 239, 226, 0.6); }
.camp .inst__trust-2 { display: block; margin-top: 3px; }

/* ── RESPONSIVE ART DIRECTION ───────────────────────────────────────────────
   The instrument becomes a focused single column, exactly as the TLM form does
   on a phone. DOM order is already the required order, so nothing is
   re-ordered and checkout can never precede the amount. */
@media (max-width: 1080px) {
  .camp .give6 { grid-template-columns: minmax(0, 1fr); gap: clamp(30px, 5vw, 48px); }
  .camp .give6__lede { max-width: 52ch; }
  .camp .give6 .chapter__title { max-width: 24ch; }
  .camp .inst { max-width: 620px; }
}
@media (max-width: 640px) {
  .camp .inst { padding: 20px 18px 22px; border-radius: 12px; }
  .camp .amount-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 9px; }
  .camp .amount { min-height: 60px; font-size: 1.3rem; }
  .camp .give6__avail { margin-top: 22px; max-width: none; }
  .camp .measure__k { font-size: 0.9rem; }
  .camp .measure__row--total .measure__v { font-size: 2.35rem; }
  .camp .cta6 { min-height: 60px; font-size: 1rem; }
}
/* landscape phone — the instrument must not become a scroll marathon */
@media (max-width: 900px) and (max-height: 460px) and (orientation: landscape) {
  .camp .give6 { grid-template-columns: minmax(0, 1fr) minmax(0, 420px); gap: 32px; }
  .camp .inst { padding: 18px; }
  .camp .amount { min-height: 52px; font-size: 1.1rem; }
  .camp .amount-custom__field { min-height: 46px; }
  .camp .cta6 { min-height: 52px; }
}

/* Nothing in Chapter VI needs motion to be understood: reduced motion keeps
   every state, snapped rather than travelled. */
@media (prefers-reduced-motion: reduce) {
  .camp .amount, .camp .amount::after, .camp .amount-custom::after,
  .camp .cta6, .camp .measure__bar i { transition: none !important; }
  .camp .amount:hover, .camp .cta6:hover { transform: none; }
}

/* PORTRAIT PHONE FOOTER. The page custom_css carried a byte-identical copy of
   this rule; retiring it (so the landscape 2-up could win) exposed that the
   plugin's own copy sits in a block that does not reach 390px, so the footer
   fell back to its base `auto 1fr auto` - 417px of columns in a 390px viewport,
   the only true horizontal overflow on the site. Declared here, immediately
   before the landscape block, so portrait stacks and landscape still goes 2-up. */
@media (max-width: 768px) {
  .camp .foot__row { grid-template-columns: 1fr; gap: 20px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   LANDSCAPE PHONE (short viewport, real width) — DENSITY

   Measured at 667x375 the page ran 7,382px = 19.69 screens, by far the worst
   surface in the product. The cause was not "mobile styling": it was PORTRAIT
   ASSUMPTIONS surviving into a viewport that has width but no height.

     · #why was 4.76 screens, and 531px of it was one photograph - the plate
       image rendered 635x476 in a 375px-tall viewport, 1.27 screens of picture.
       Media here must be capped against the SHORT axis, not the long one.
     · the doubling figure ($1 -> prism -> $2) was flex-direction:column at 667px
       of available width, stacking a composition that reads across. 562px tall.
     · the proof band and the footer row were still stacked for a phone held
       upright.

   Nothing is shrunk for its own sake and no narrative beat is removed; each rule
   below spends the width the viewport actually has. Placed last in the sheet so
   it lands after the <=768 portrait block it corrects. Portrait phones never
   match this query, and the 600px floor keeps it off small portrait devices
   rotated into a narrow landscape.
   ══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 600px) and (max-height: 520px) and (orientation: landscape) {
  /* PHOTOGRAPHY, CAPPED TO THE SHORT AXIS. object-fit keeps the focal crop; only
     the window gets shorter, so the subject is not scaled down or letterboxed. */
  .camp .why .why__plate img { aspect-ratio: auto; height: clamp(150px, 52svh, 240px); width: 100%; object-fit: cover; }

  /* THE MECHANISM READS ACROSS. It is an equation; stacking it at 667px of width
     turned three terms into three screens. */
  /* .camp .chapter … (three classes) rather than .camp … (two). The portrait
     stack for this figure is declared TWICE in the page custom_css - once in its
     <=768 block and again in the hand-written APPROVED PRISM MIGRATION block
     after MANAGED_END - and that sheet loads last. Retiring those copies was
     rejected: the figure's numeral type scale exists ONLY there, so deleting it
     would shrink the $1/$2 numerals on portrait phones. One deliberate
     specificity step is the honest trade; it is scoped to this media query. */
  .camp .chapter .doubling { flex-direction: row; align-items: center; gap: clamp(16px, 3vw, 28px); text-align: left; }
  .camp .chapter .doubling__side--sum { align-items: flex-end; text-align: right; }
  .camp .chapter .doubling__flow { width: auto; align-items: center; display: flex; margin-inline: 0; }
  .camp .chapter .doubling__amt { font-size: clamp(2rem, 7vw, 3rem); }
  .camp .chapter .doubling__lbl { max-width: 16ch; margin-inline: 0; }

  /* ONE PROOF LINE. 667px carries four metrics; two rows of two was a portrait
     decision inherited from the <=768 block. */
  .camp .proofline__stats { grid-template-columns: repeat(4, 1fr); row-gap: 0; }
  .camp .proofline__stats li { padding: 0 0 0 16px; }
  .camp .proofline__stats li:first-child { padding-left: 0; border-left: 0; }
  .camp .proofline__stats b { font-size: clamp(1.5rem, 4.4vw, 2.1rem); }
  .camp .proofline__stats span { font-size: 0.82rem; }

  /* the capability list is a grid that had collapsed to one column at 667px of
     width - 244px of single-file rows. Two columns halve it without touching
     type or order. */
  .camp .caps { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: clamp(20px, 4vw, 36px); }

  /* the footer is a row again */
  .camp .foot__row { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px 28px; }
}

/* ════════════════════════════════════════════════════════════════════════════
   THE CAMPAIGN SPINE
   ══════════════════

   MEASURED PROBLEM (qa/product/_spine.mjs against the live page, 1440x900):
   the campaign presented TEN distinct left edges — 0, 24, 144, 184, 226, 230,
   250, 286, 330, 736 — and SEVEN of them carried narrative type. Every chapter
   had been composed correctly in isolation and had built its own local measure
   system (--f-col in II, --b-col in IV, --e-band in V, the 12-col grid in I and
   VI), each one centred inside its own container. Centring a different width
   inside the same viewport produces a different left edge, so the reading
   column WANDERED down the page: 144 → 330 → 144 → 286 → 330 → 250 → 144.

   WHY IT HURT THE DONOR: nothing communicates "these are chapters of one
   document" more cheaply than a shared left edge, and nothing says "these are
   separate modules bolted together" more loudly than an edge that moves. A
   major donor scanning the page was reading seven compositions, not one story.

   THE SYSTEM: one left edge, four reach lengths.

     --spine        the single left edge — 144 at 1440, collapsing to the
                    gutter below 1200 so nothing has to be re-tuned per width
     --reach-read   660   ledes, quotes, captions — the reading measure
     --reach-text   780   chapter titles and narrative columns
     --reach-band   940   evidence bands: the film, the equipment band, the
                          campaign instrument
     --reach-full  1152   full instruments: the proof rail, the giving surface

   Every block starts at --spine. Blocks differ ONLY in how far right they
   reach. Nothing is centred, so no width can move a left edge ever again.

   This is not a new invention: it is Chapter VI's geometry — head on the spine,
   instrument reaching right off it — generalised to the whole campaign. VI is
   the chapter that already reads as a finished product, which is the evidence
   the system is the right one.

   The per-chapter measures ARE PRESERVED. --f-col/--b-col/--e-band and the type
   scales tuned against them are untouched; only the anchoring changes, from
   "centred in a local container" to "hung on the campaign spine". No type
   scale, no column width and no photograph crop moves as a result.

   GUARD: qa/product/_spine.mjs must report ONE narrative left edge per
   viewport. Run it after any composition change.
   ════════════════════════════════════════════════════════════════════════════ */
.camp {
  --spine:      max(var(--gutter), calc((100% - var(--container-content)) / 2 + var(--gutter)));
  --reach-read: 660px;
  --reach-text: 780px;
  --reach-band: 940px;
  --reach-full: 1152px;
}

/* THE HANGERS. Each of these is a section-level container that used to centre
   itself; it now starts at the spine and reaches right. `padding-inline: 0`
   retires the container's own gutter because the spine already contains it —
   keeping both would double-count it and push the spine 24px right of Chapter
   I's grid, which is the exact class of drift this block exists to end.

   EVERY SELECTOR HERE IS DELIBERATELY THREE CLASSES DEEP. The page's own
   custom_css redeclares the entire container system — `.camp .container`,
   `.camp .container--wide` and friends, at two classes — and it is the LAST
   stylesheet the page loads, so a two-class rule here loses the cascade to it
   silently and the block simply does nothing. Naming each hanger's section
   (`.camp .hero .hero__grid` rather than `.camp .hero__grid`) buys the third
   class honestly, without !important, and documents which chapter owns it. */
.camp .nav .nav__row,
.camp .hero .hero__grid,
.camp .film > .container,
.camp #match > .container,
.camp #builds > .container,
.camp #engine > .container,
.camp .foot .foot__row {
  /* BOTH SIDES, NOT `<spine> auto`. With an `auto` right margin and `width:
     auto`, the box fills to the viewport's right EDGE — so every hung container
     had a left gutter and no right one. Wrapped copy hid it; the first
     right-aligned thing exposed it, and it was Section V's financial figures
     sitting flush against the bezel on a phone. Reserving the spine on both
     sides is also simply what a spine is. */
  margin-inline: var(--spine);
  padding-inline: 0;
  /* `width: auto` IS LOAD-BEARING. `.container` declares `width: 100%`, which
     with a left margin makes the box 100% WIDE STARTING AT THE SPINE — i.e. it
     overhangs the viewport by exactly the spine, and every viewport at or below
     1200 scrolled sideways. Auto width fills what is left of the line box
     instead, and --reach-full caps it once there is room to cap.

     `flex-grow` AND `min-width` ARE PART OF THE SAME FIX. `.hero` is itself a
     flex container, and an auto-width FLEX ITEM shrinks to its content rather
     than filling the line — which collapsed the hero grid to the width of its
     copy and dropped the film card out of the right margin into the middle of
     the photograph, on top of the student's face. Growing the item restores
     the fill that `width: 100%` used to provide, without the overhang. Both
     declarations are simply ignored on the block-level hangers. */
  width: auto;
  flex-grow: 1;
  min-width: 0;
  max-width: var(--reach-full);
}

/* Chapter I and Chapter VI already sit on the spine via the 12-column content
   grid; they are normalised here so all seven chapters derive from one token
   rather than from two coincidentally-equal systems. */
.camp .why .why__grid,
.camp #give .give6 {
  margin-inline: var(--spine);
  padding-inline: 0;
  width: auto;
  flex-grow: 1;
  min-width: 0;
  max-width: var(--reach-full);
}

/* THE REACHES. A block that is ITSELF a section-level container is already hung
   by the rule above, so it only declares how far right it reaches — it must NOT
   restate margin-inline, or it would reset the spine back to zero. Only blocks
   NESTED inside a hung container (the film's three) zero their own margin, to
   stop centring themselves inside their parent. */
.camp .film > .container.chapter__head,
.camp #match > .container.chapter__head,
.camp #builds > .container.chapter__head,
.camp #engine > .container.chapter__head { max-width: var(--reach-text); }

.camp .film .chapter__lede,
.camp #builds .builds__lede { max-width: var(--reach-read); }

/* nested inside .container--wide, so these do zero their margin */
.camp .film .film__shell,
.camp .film .film__credit { max-width: var(--reach-band); margin-inline: 0; }
.camp .film .film__handoff { max-width: var(--reach-text); margin-inline: 0; }

.camp #builds > .builds__body,
.camp #builds > .builds__close { max-width: var(--reach-text); }
.camp #builds > .builds__band  { max-width: var(--reach-band); }

.camp #engine > .container:not(.chapter__head) { max-width: var(--reach-band); }

.camp #match > .container.prose { max-width: var(--reach-read); }

/* BELOW THE CONTENT CONTAINER the spine is simply the gutter, so the max() in
   the token resolves it with no breakpoint of its own. The one thing that does
   need a breakpoint is the narrower mobile gutter. */
@media (max-width: 768px) {
  .camp { --spine: var(--gutter-mobile); }
}

/* THE EQUATION'S BLEED NEEDS SLACK TO BLEED INTO. `.doubling__flow` carries a
   deliberate negative inline margin so the prism closes the optical gap to the
   $1 and $2 numerals. That bleed used to be absorbed by the container's own
   24px gutter; on the spine the figure reaches --reach-full with no gutter, so
   between 769px and the width at which --reach-full starts capping, the bleed
   had nothing to bleed into and pushed the diagram 8px past the viewport.
   Above 1240 the figure is narrower than the line and the slack is real; below
   it the figure fills the line, so the bleed is simply switched off — the gap
   it was closing is proportionally smaller at those widths anyway. */
@media (max-width: 1240px) {
  .camp #match .doubling__flow { margin-inline: 0; }
}

/* ════════════════════════════════════════════════════════════════════════════
   CHAPTER MARKERS — THE TRANSITION DEVICE
   ═══════════════════════════════════════

   MEASURED PROBLEM: of the seven chapter boundaries, only the colour inversions
   (I→II, IV→V, V→VI) registered at all, and they registered as butt joints — one
   ground stops, the next starts. III→IV was invisible: --warm (#f6efdc) meets
   --paper (#fbf6e8), a 2% step, so the mechanism chapter and the capability
   chapter read as one undifferentiated field of cream. HERO→I gave the donor a
   fragment of a drop cap as its only cue that anything continued.

   THE DEVICE: a hairline on the spine, running the chapter's full reach, at the
   head of every chapter. It is the one mark the campaign repeats, and it says
   exactly one thing — A CHAPTER BEGINS HERE — at every boundary, whether or not
   the ground colour changed. It is drawn, not spaced: the brief for this pass
   was explicitly that 72px of air is not a transition.

   WHY THIS AND NOT A BANNER, AN OVERLAP OR A MOTION CUE: the campaign is set as
   an editorial document — Fraunces display, one spine, hairline rules, no cards
   — and a chapter rule is that form's own answer to this exact problem. It
   costs one pixel of height, needs no script, survives reduced motion and
   images-off, and cannot read as chrome because it draws nothing but a line.

   It also finally gives HERO→I a real cue. The hero stops short of the viewport
   on purpose so the next chapter shows; what showed was a cropped glyph. What
   shows now is the chapter rule with the gold numeral tick beginning under it,
   which is legible AS A CHAPTER OPENING at a glance and at any crop.

   The rule is a section pseudo-element rather than part of .chapter__head
   because the head is capped at --reach-text: the mark has to run the chapter's
   whole measure to read as the page's rule rather than the headline's underline.
   ════════════════════════════════════════════════════════════════════════════ */
.camp .chapter::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--spine);
  right: var(--spine);
  max-width: var(--reach-full);
  height: 1px;
  background: var(--rule);
  pointer-events: none;
}
/* On the dark stage the paper rule is invisible; the on-dark hairline is the
   same weight against its own ground, so the mark reads identically in both
   halves of the campaign's palette. */
.camp .chapter--stage::before { background: rgba(244, 239, 226, 0.16); }

/* CHAPTER VI CLOSES THE DOCUMENT, so it takes the mark like every other
   chapter; the footer is not a chapter and deliberately does not. */

/* THE EQUATION HANGS ON THE SPINE LIKE EVERY OTHER BLOCK. The page's own
   custom_css centres `.doubling` with `margin-left/right: auto !important`,
   which at 1440 happens to land on the spine (--reach-full centred in the
   viewport IS the spine at that width) and at every narrower width does not:
   at 1024 and below the equation lost its gutter entirely and ran edge to edge
   while the chapter's own headline stayed inset.

   Beating an `!important` from a later sheet needs specificity, not order, so
   this is deliberately scoped to the chapter and to both of the figure's own
   classes — (1,3,0) against the page's (0,2,0). It is the only !important in
   the spine system, and it exists solely to undo one. */
.camp #match > .doubling.container {
  margin-left: var(--spine) !important;
  /* WAS `auto`, WHICH IS NOT A GUTTER. Undoing the page layer's left margin and
     leaving the right one auto let the equation fill to the parent's right edge
     at every width below --reach-full: a left inset and a bezel-flush right
     edge, on the one figure in the chapter. The spine is symmetric. */
  margin-right: var(--spine) !important;
}

/* THE EQUATION NEEDS ITS OWN AIR NOW THAT IT LEADS. With the figure moved above
   the prose, the flow's "one gift, doubled" caption — display italic, and the
   figure's own last line — landed 22px above the paragraph's first line, so the
   caption read as that paragraph's opening rather than as the diagram's label.
   The gap below the figure is set to match the gap above it, which is what makes
   the equation a self-contained object between the headline and the copy. */
.camp #match > .container.prose { margin-top: clamp(34px, 4vw, 58px); }

/* ════════════════════════════════════════════════════════════════════════════
   CHAPTER V · INSTRUMENT PROPORTION AND GROUPING
   ══════════════════════════════════════════════

   Section V's design is locked and nothing here changes what any instrument
   MEASURES, what it is made of, or what it says. Two reported problems are
   corrected — the vessel was fat, and the chapter read as separate objects
   standing near each other rather than one instrument panel.

   SCOPED TO WHERE THE PROBLEM ACTUALLY WAS. The old width was
   clamp(104px, 13vw, 148px), which only resolves fat once 13vw clears ~117px:
   2.49:1 at 1440 and 2.77:1 at 1024. At 768 it floors at 104 = 4.12:1 and on a
   narrow phone a separate rule gives 66px = 5.79:1 — both already correct. So
   the two-column composition (901+) and phone landscape are the only places
   touched; every portrait width below 901 keeps production's exact geometry.
   A first pass changed those too and it was wrong: it narrowed the tablet and
   made the narrow phone FATTER (66 -> 72), on widths nobody complained about.

   1 · THE VESSEL. 148w x 368h with border-radius 74 — exactly half its width —
       drew a lozenge, and the 22% fill read as a shelf across a large empty
       slab. The design's own system already says what the proportion should be
       (4.12:1 at 768, 5.79:1 at 390), so 6.2vw capped at 88 brings 1440 to
       4.55:1, in line with the rest. The capsule silhouette, the glass, the
       inner shadow, the meniscus, the segments and every tick are untouched.

   2 · THE LEGEND WAS PUSHING THE TWO GAUGES APART — the real cause of "two
       things next to each other". `.core__legend` is a wide flex row INSIDE
       `.core`, so the caption, not the vessel, set `.core`'s width: 351px
       around a 148px tube. The match gauge was shoved 95px clear of the vessel
       by a caption that then sat under the vessel alone, orphaning the gauge
       from the key explaining it. Out of flow, `.core` sizes to its instrument,
       the gauges close up, and the key centres beneath the PAIR.

   3 · PROXIMITY NOW RANKS THE GROUPING. Tubes 31px apart inside a cluster that
       stands 57px off the readout. Before, the tubes were 49px apart while the
       whole cluster sat 40px from the text — the two gauges were further from
       each other than either was from the prose, which is exactly why they read
       as unrelated.

   4 · THE SCALE WAS STRANDED in a 96px gutter with leaders stopping short of
       the glass. The gutter closes to 62px; the labels keep their full measure
       by hanging left into space that was empty anyway, so the wording still
       sets on two lines and the leaders now meet the tube.

   DELIBERATELY NOT DONE: the two tubes still have DIFFERENT HEIGHTS and do not
   share a baseline. They are different scales — the vessel runs 0 to $200,000
   against labelled ticks, the reservoir shows what remains of a $100,000 cap —
   so one matched column would invite the reservoir's fill to be read against
   the vessel's ticks and misstate the campaign by tens of thousands of dollars.
   Grouping them is a composition problem; equalising them would be a financial
   claim. The rejected horizontal ruler is not resurrected either.

   Several rules below are three classes deep on purpose: the page's own
   custom_css declares `.camp #engine …` at the same weight and loads last, so
   the two-class form silently loses and the rule simply never applies.
   ════════════════════════════════════════════════════════════════════════════ */
@media (min-width: 901px) {
  .camp #engine .core__tube { width: clamp(80px, 6.2vw, 88px); margin-left: 62px; }
  .camp #engine .core__ticks span { left: -34px; max-width: 92px; }
  .camp #engine > .container .core__ticks span::before { left: 82px; }
  .camp #engine .core { position: relative; }
  .camp #engine > .container .core__legend {
    position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
    /* one line — at 320 the key broke "= TOTAL IMPACT" onto a second row and
       stopped reading as a single equation */
    width: max-content; max-width: 440px; margin-top: 14px;
  }
  /* padded by exactly the scale's overhang, so the gap the stage declares is
     the gap that is actually seen between the readout and the instrument */
  .camp #engine .engine__instrument { gap: 12px; padding-left: 34px; }
  .camp #engine > .container .engine__stage { column-gap: clamp(36px, 4.4vw, 64px); }
}

/* PHONE LANDSCAPE — its own composition, and its vessel was the other fat one:
   84px against a ~225px column is 2.68:1, the same lozenge the desktop had. It
   also cannot leave the key in flow: the whole instrument gets ~225px of height
   there, and a key inside `.core` ate the column and drove the vessel to 1.9:1,
   fatter than what this change set out to fix. */
@media (min-width: 640px) and (max-height: 520px) and (orientation: landscape) {
  .camp #engine > .container .core__tube { width: 52px; margin-left: 56px; }
  .camp #engine > .container .core__ticks span { left: -30px; max-width: 84px; }
  .camp #engine > .container .core__ticks span::before { left: 76px; }
  .camp #engine > .container .engine__instrument { padding-left: 30px; gap: 10px; }
  .camp #engine > .container .core { position: relative; }
  .camp #engine > .container .core__legend {
    position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
    /* the cluster sits near the right edge here, so the desktop measure ran
       flush to the bezel; 260 buys a gutter and costs one wrapped line of a key
       that is out of flow anyway */
    width: max-content; max-width: 260px; margin-top: 10px;
  }
}

/* ════════════════════════════════════════════════════════════════════════════
   MASTHEAD — RESTORING THE BRAND'S PRESENCE
   ════════════════════════════════════════

   The header did not need new numbers. It already had a fully reasoned system
   a few hundred lines above this one: `--tlm-wordmark`, each lockup's box
   derived from its OWN measured wordmark band (1/0.7566 white, 1/0.6981 dark)
   so the mark holds one optical size as the nav crosses into its solid state,
   with the tagline cap-height computed at every breakpoint.

   NONE OF IT WAS REACHING THE PAGE. The page's own custom_css declares
   `.camp .nav__logo { height: 26px }` at two classes and loads last, so it beat
   `.camp .nav__logo--onDark` — also two classes — and the entire block has been
   dead. The campaign has been shipping a 26px mark where its own design system
   specifies a 58px one: 45% of the intended size, which is why the masthead
   read as utility chrome rather than as the institution behind a $100,000
   campaign. The tagline was rendering at 2.6px, i.e. as noise.

   The fix is specificity, not redesign. Naming the parent (`.camp .nav
   .nav__logo--onDark`) buys the third class honestly and the original
   arithmetic applies as written:

       desktop   wordmark 44px -> lockup box 58 x 212, nav row ~94px
       <=1024    wordmark 40px -> 53 x 192
       <=768     wordmark 34px -> 45 x 163

   Two things are genuinely new, both of them consequences:

   · NAVIGATION TYPE. 0.95rem (15.2px) was set against a 26px mark. Beside a
     58px one it reads as a footnote, so it goes to 1.02rem — still clearly
     subordinate to the campaign, no longer utility chrome.

   · THE GIVE CONTROL measured 35px tall, under the 44px comfortable-touch
     target and visually slight for the page's one persistent call to action.
     It now clears 44 at every width without becoming a button that competes
     with the hero's own primary CTA.
   ════════════════════════════════════════════════════════════════════════════ */
.camp .nav .nav__logo--onDark  { height: calc(var(--tlm-wordmark) * 1.3217); }
.camp .nav .nav__logo--onLight { height: calc(var(--tlm-wordmark) * 1.4325); }
.camp .nav .nav__links a { font-size: 1.02rem; }
.camp .nav .nav__give {
  padding: 0.62em 1.35em;
  min-height: 44px;
  display: inline-flex; align-items: center;
}
/* The mobile hero derives its crop band from the masthead's REAL height
   (--hero-mast). That constant was written for a 45px lockup inside 14px
   padding; the page serves 18px, so the band started 8px under the bar. */
@media (max-width: 600px) {
  .camp .hero { --hero-mast: 82px; }
}

/* ════════════════════════════════════════════════════════════════════════════
   CHAPTER RHYTHM — CLOSURE, INTRODUCTION, AND WHERE THE GROUND DOES THE WORK
   ═════════════════════════════════════════════════════════════════════════

   MEASURED: every chapter on the page opened with 72px and closed with 72px at
   1440, and 48/48 on a phone. ONE value, used seven times. That is the whole
   reason the page reads as sections stacked rather than chapters paced — there
   was no spacing vocabulary at all, so nothing could say "this idea is
   finished" differently from "these two lines are related".

   THE SYSTEM, and the principle behind it:

     --chapter-open    a new idea begins
     --chapter-close   the previous idea is complete — deliberately the LARGER
                       of the two, so an argument lands before the next one
                       opens rather than being cut off by it

   and then, the part that is actually a judgement rather than a token:

     TRANSITION SPACE IS INVERSELY PROPORTIONAL TO WHAT THE GROUND ALREADY DOES.

   Five of this campaign's seven boundaries are colour inversions — paper to
   stage, stage to warm — and an inversion is already an unmistakable signal.
   Spending the same space there as at a boundary with no signal at all is what
   made the pacing feel mechanical. So:

     III -> IV   warm (#f6efdc) meets paper (#fbf6e8): a 2% step, no signal.
                 Gets the most space on the page, plus the chapter rule.
     V  -> VI    inverts, so it does not NEED space — but it is the page's one
                 narrative hinge, from watching the campaign to acting on it,
                 and it gets ceremony for that reason and not for contrast.
     the rest    the inversion carries it; they take the base values.

   The page gets taller. That is the point — this pass was asked for because it
   was compressed, and total height is not a quality metric.
   ════════════════════════════════════════════════════════════════════════════ */
.camp {
  --chapter-open:  clamp(58px, 7vw, 112px);
  --chapter-close: clamp(70px, 8.6vw, 138px);
}
.camp .chapter { padding-block: var(--chapter-open) var(--chapter-close); }

/* Chapter I closes the problem AND hands off to Joe; it already carried a
   bespoke larger bottom and keeps the same intent against the new scale. */
.camp .why { padding-bottom: calc(var(--chapter-close) * 1.1); }

/* III -> IV — the boundary with no ground change. Both sides of it open up, so
   the chapter rule lands in real space instead of between two touching blocks. */
.camp #match  { padding-bottom: calc(var(--chapter-close) * 1.35); }
.camp #builds { padding-top:    calc(var(--chapter-open)  * 1.3); }

/* V -> VI — the hinge. Momentum finishes, then the decision is introduced.
   This is the one place on the page where the space itself is the narrative. */
.camp #engine { padding-bottom: calc(var(--chapter-close) * 1.45); }
.camp #give   { padding-top:    calc(var(--chapter-open)  * 1.45); }

/* THE FOOTER IS THE CONCLUSION, NOT LEFTOVER CHROME. 44px top and bottom made
   it read as a strip bolted under the giving decision. `footer.foot` adds an
   element to the selector so it outranks the page layer's own `.camp .foot`. */
.camp footer.foot { padding-block: clamp(52px, 5.6vw, 88px) clamp(44px, 4.4vw, 68px); }

/* THE ID-SCOPED CHAPTERS HAD TO BE NAMED. `#builds` and `#engine` each declare
   their own `padding-block` shorthand, and an id outranks `.camp .chapter`
   however late it is written — so those two kept the old uniform 72/48 on the
   side the rules above did not restate, which is exactly the boundary that
   needed the room. Stated in full here so no shorthand can half-apply. */
.camp #builds { padding-block: calc(var(--chapter-open) * 1.3) var(--chapter-close); }
.camp #engine { padding-block: var(--chapter-open) calc(var(--chapter-close) * 1.45); }
.camp #match  { padding-block: var(--chapter-open) calc(var(--chapter-close) * 1.35); }
.camp #give   { padding-block: calc(var(--chapter-open) * 1.45) var(--chapter-close); }

/* Chapter II's geometry lives in campaign-film.css, which loads AFTER this
   file, so its own `padding-block` wins on load order. Two classes here, and
   the section carries both. */
.camp .chapter.film { padding-block: var(--chapter-open) var(--chapter-close); }

/* HERO CROP — CLEARING THE SUBJECT, NOT SHRINKING THE HEADLINE.
   At 1440 the descender and comma of "Each dollar," landed on the student's
   beanie and ear. His face is clear and his gaze runs right, away from the
   type, so the relationship is close to correct — it is off by about 40px.

   The fix is the photograph, not the typography: shrinking the campaign's
   signature line to dodge a crop would be solving a composition problem with
   the wrong instrument. `object-position` alone cannot do it here, because the
   image is exactly as wide as the hero and cover leaves NO horizontal slack to
   pan into. A 1.08 scale creates 57px of slack on each side and the translate
   spends 57 of it moving the frame right, so the subject clears the text and
   nothing uncovers at the edges. 1600px of source rendered into 1538 is still
   downscaling, so no sharpness is spent buying this.

   Scoped above 600px: the portrait hero is a different composition entirely,
   with its own crop band derived from --hero-mast, and it does not have this
   collision. */
@media (min-width: 601px) {
  .camp .hero__media img { transform: scale(1.08) translateX(4%); }
}

/* ════════════════════════════════════════════════════════════════════════════
   CHAPTER V — ROOM TO READ THE CAMPAIGN BEFORE READING THE INSTRUMENT
   ══════════════════════════════════════════════════════════════════

   The approved design is unchanged: stone ground, gold V and rule, the same
   copy, the same readout, the vertical vessel, its four ticks and cap marker,
   the match reservoir, the legend. Nothing is removed, nothing becomes a card,
   and the rejected horizontal ruler stays rejected.

   What changes is the SPACE the chapter occupies, because the composition was
   asking the donor to do two things at once. "$44,482 of $200,000 · 22% funded"
   is a fact to read. The vessel and reservoir are a picture to interpret. They
   sat 64px apart with the readout's own beats stacked tight beneath each other,
   so the eye arrived at both simultaneously and settled on neither.

   · THE PRIMARY GUTTER nearly doubles. It is the single most important gap in
     the chapter: it is the pause between "where the campaign stands" and "here
     is what that looks like", and it now reads as a deliberate move of the eye
     rather than two columns that happen to be adjacent.
   · THE READOUT'S OWN RHYTHM opens up — the total gets air above and below it
     instead of being clamped between the lede and the breakdown, so $44,482
     lands as the chapter's headline figure.
   · THE INSTRUMENT GROWS. It was capped at 44vh; at 52vh the vessel is a
     full editorial object rather than a diagram squeezed beside a column.

   The chapter gets taller. It was asked to.
   ════════════════════════════════════════════════════════════════════════════ */
@media (min-width: 901px) {
  .camp #engine > .container .engine__stage { column-gap: clamp(56px, 7.6vw, 112px); }
  .camp #engine > .container .engine__instrument { height: clamp(440px, 52vh, 560px); }
}
/* the readout's internal beats — lede → eyebrow → total → of/callout → breakdown */
.camp #engine .engine__eyebrow { margin-bottom: clamp(10px, 1vw, 15px); }
.camp #engine .engine__total   { margin-bottom: clamp(10px, 1.1vw, 16px); }
.camp #engine .engine__zone    { margin-top: clamp(20px, 2.2vw, 32px); }
.camp #engine .engine__stats   { margin-top: clamp(30px, 3.4vw, 48px); }
.camp #engine .chapter__head   { margin-bottom: clamp(34px, 4.2vw, 62px); }

/* FOOTER — THE STORY'S CONCLUSION, NOT A UTILITY BAR.
   Three items sat in one row at effectively one weight, with the partner's mark
   at 30px and 55% opacity — smaller and fainter than the legal paragraph beside
   it — so the campaign ended on a strip of chrome rather than on the
   partnership that made it possible.

   The mark is given real presence (it is the only place Patriot appears after
   Chapter II) and its eyebrow is lifted just clear of legal scale so the
   partnership reads as a statement. Nothing is added and nothing moves: the
   same three items, the same order, the same words. All three selectors are
   parented so they outrank the page layer's own copies. */
.camp .foot .foot__brand { gap: 18px; }
.camp .foot .foot__brand img { height: 44px; }
.camp .foot .foot__patriot { opacity: 0.78 !important; }
.camp .foot .foot__eyebrow {
  font-size: 0.76rem; letter-spacing: 0.18em;
  color: rgba(244, 239, 226, 0.62);
}
.camp .foot .foot__row { gap: clamp(32px, 4.4vw, 64px); align-items: center; }
.camp .foot .foot__legal { font-size: 0.8rem; line-height: 1.6; opacity: 0.82; }

/* SHORT PHONES — KEEPING THE GIVING ACTION IN THE FIRST SCREEN.
   The masthead's restoration cost the hero about 10px of vertical budget (the
   bar itself, plus --hero-mast following it), and at 375x667 that was enough to
   push "Donate to the campaign" from 8px inside the fold to 33px below it. A
   campaign whose primary action is not visible on the smallest common phone has
   lost something real, so the space is taken back from the picture rather than
   from the type or the button.

   --hero-band already carries an svh ceiling for exactly this reason; on a
   short viewport it simply needs a lower one. 27svh instead of 34 shows the
   same crop of the same photograph in a shallower window — it re-scales, it
   does not re-frame, because the image width is derived from the band. The
   lede's paragraph spacing closes by a few px with it.

   Scoped by HEIGHT, not width, so tall phones keep the full band. */
@media (max-width: 600px) and (max-height: 700px) {
  .camp .hero {
    --hero-band: min(calc(100cqw * 640 / 710), 27svh);
    --hero-gap: 14px;
  }
  .camp .hero .hero__sub { margin-block: 14px; }
  .camp .hero .hero__cta { margin-top: 20px; }
}

/* ════════════════════════════════════════════════════════════════════════════
   CHAPTER V — SPATIAL COMPOSITION
   ═══════════════════════════════

   WHAT WAS ACTUALLY WRONG. Every previous pass tuned the thermometer in
   isolation and the section kept failing, because the defect was never the
   instrument. Measured on production at 1440 (qa/section-v/compose-audit.mjs):

     composition frame      144px of spine on the LEFT, 356px of nothing on the
                            RIGHT. The section was hung on --reach-band (940),
                            so it stopped 212px short of the campaign's own
                            right boundary. Clutter inside, a void outside.
     labels -> vessel       10px. Annotations touching the thing they annotate,
                            which is how a scale turns into clutter.
     vessel -> match gauge  32px between a 26px tube and an 88px tube. A second
                            skinny bar glued to the first.
     vessel proportion      88 x 520 = 5.91:1. The skinny-tube failure state.
     the key                a 351px flex row centred on a 290px column, ending
                            200px OUTSIDE the container's right edge.
     the bottom             the financial column ended 104px above the
                            instrument and nothing closed the section, leaving
                            an unshaped empty quadrant under the table.

   So: the space is designed, and the instrument is left substantial.

   THE SPACING LADDER. Every gap in this section comes from one ratio series
   built on the campaign's own --space-* scale. No gap is eyeballed.

     MICRO     8    a label and its own value
     SMALL    16    rows inside one financial group
     MEDIUM   32    groups within one region
     LARGE    64    major information movements
     MOVEMENT 96    head -> stage, stage -> resolution
     XL      128    THE BREATHING ZONE: narrative -> instrument
     SECTION 144    composition -> section boundary, and it is exactly --spine,
                    so the frame around Chapter V is the same measure as the
                    campaign's left edge on all four sides.

   THE THREE ZONES at 1440, inside a 1152 reach that now runs 144 -> 1296:

     ZONE A  560   the financial story, at a DEFINED measure that does not
                   stretch toward the instrument
     ZONE B  128   breathing space. Real design space, not leftover.
     ZONE C  464   the instrument's own field, itself composed as
                   scale 100 | air 40 | vessel 150 | air 40 | state 134

   The vessel is 150 x 470 = 3.13:1 — between the two failure states the design
   kept cycling through (2.49:1 lozenge, 5.91:1 skinny tube). It is WIDER than
   it was, not smaller, and it is not taller; the room came from the frame.
   ════════════════════════════════════════════════════════════════════════════ */
.camp #engine {
  --v-micro:  var(--space-2);    /*   8 */
  --v-sm:     var(--space-4);    /*  16 */
  --v-md:     var(--space-8);    /*  32 */
  --v-lg:     var(--space-16);   /*  64 */
  --v-move:   var(--space-24);   /*  96 */
  --v-xl:     var(--space-32);   /* 128 */

  --v-zone-a:   560px;   /* the financial narrative's readable measure */
  --v-scale-w:  100px;   /* the scale-label block */
  --v-scale-air: 40px;   /* labels -> vessel: annotation distance */
  --v-vessel-w: 150px;
  --v-vessel-h: 470px;
  --v-state-air: 40px;   /* vessel -> match-remaining readout */
  --v-state-w:  134px;
  --v-legend-w:  380px;  /* the key's own measure, kept on one line */
  --v-legend-reserve: 52px;  /* the key is out of flow; its room is booked here */
}

/* ── THE FRAME ──────────────────────────────────────────────────────────────
   The band takes --reach-full, the reach Chapter VI's giving surface already
   uses — this is the campaign's other full instrument, and it was the only one
   not reaching the full measure. 144 of spine on the left, 144 on the right.
   padding-block matches, so the composition is framed by one value on all four
   sides instead of 101 top / 198 bottom / 0 between sections. */
/* THE VERTICAL FRAME IS NOT THE SPINE.
   It was `padding-block: var(--spine)`, which reads well at 1440 (144 on all
   four sides) and collapses below 1200, because --spine is defined as
   `max(gutter, ...)` and bottoms out at the 24px GUTTER. At 1024 that put the
   resolution row 24px off the section boundary — the horizontal bleed guard
   standing in for a section break. The composition gate caught it at exactly
   that width.

   Vertical framing gets its own ramp: it still resolves to the spine's 144 at
   1440, but it floors at 80 instead of at the gutter, so the section keeps a
   real boundary at every width. Left/right stay on the gutter, as every other
   chapter does. */
.camp #engine.chapter--stage { padding-block: clamp(80px, 10vw, 144px); }
.camp #engine > .container:not(.chapter__head) { max-width: var(--reach-full); }

@media (min-width: 1200px) {
  /* head -> stage is a MOVEMENT, not a paragraph break */
  .camp #engine > .container.chapter__head { margin-bottom: var(--v-move); }

  /* ── ZONE A | ZONE B | ZONE C ──────────────────────────────────────────
     Zone A is a FIXED measure. It was `1fr`, which is what let the financial
     rows stretch toward the instrument and eat the breathing zone the moment
     the section got any wider. The column gap IS Zone B, declared as one
     token, so the separation between the explanation and the instrument can
     never again be whatever happened to be left over. */
  /* THREE CLASSES DEEP, and `display: grid` is restated here on purpose. The
     page's own custom_css declares `.camp .engine__stage { display: flex }` at
     two classes and loads LAST, so the grid this file has declared since the
     chapter was built has never once reached the page — the stage has been a
     flex row the whole time, which is exactly why Zone A kept stretching: a
     `flex: 1 1 440px` readout absorbs every pixel the instrument does not use.
     A fixed grid column is the only thing that can hold a measure. */
  .camp #engine > .container .engine__stage {
    display: grid;
    grid-template-columns: var(--v-zone-a) minmax(0, 1fr);
    column-gap: var(--v-xl);
    align-items: start;
  }

  /* ZONE A internal hierarchy, straight off the ladder. */
  .camp #engine > .container .engine__eyebrow { margin-bottom: var(--v-sm); }
  .camp #engine > .container .engine__total   { margin-bottom: var(--v-sm); }
  .camp #engine > .container .engine__of      { margin-top: 0; }
  .camp #engine > .container .engine__zone    { margin-top: var(--v-md); }
  .camp #engine > .container .engine__stats   { margin-top: var(--v-md); }
  .camp #engine > .container .engine__stats > div { padding: var(--v-sm) 0; }

  /* ── ZONE C ────────────────────────────────────────────────────────────
     The instrument is three things in a row — scale, vessel, state — and the
     vessel owns the middle with equal air on both sides. `height` sets the
     vessel; `margin-bottom` books the out-of-flow key's room so the
     resolution row below cannot be drawn through it. */
  .camp #engine > .container .engine__instrument {
    display: flex;
    align-items: stretch;
    align-self: start;
    gap: var(--v-state-air);
    padding-left: 0;
    height: var(--v-vessel-h);
    margin-bottom: var(--v-legend-reserve);
  }
  .camp #engine > .container .core {
    position: relative;
    flex: 0 0 calc(var(--v-scale-w) + var(--v-scale-air) + var(--v-vessel-w));
  }

  /* THE SCALE. Its own block, its own measure, standing a full MEDIUM+ clear
     of the glass. The leader is what makes 40px of air read as "annotation
     of" rather than "floating near" — it is a hairline, and it is the only
     thing allowed to cross the protected field. */
  .camp #engine > .container .core__ticks { left: 0; }
  .camp #engine > .container .core__ticks span { left: 0; max-width: var(--v-scale-w); }
  .camp #engine > .container .core__ticks span::before {
    left: calc(var(--v-scale-w) + 6px);
    width: calc(var(--v-scale-air) - 6px);
  }

  /* THE VESSEL. Wider, not taller: 150 x 470. */
  .camp #engine > .container .core__tube {
    width: var(--v-vessel-w);
    margin-left: calc(var(--v-scale-w) + var(--v-scale-air));
  }

  /* THE KEY, centred on the VESSEL — not on the column, and NOT CENTRED WITH A
     TRANSFORM. The key is a <p>, and campaign-film.css sets
     `transform: none !important` on every <p> inside a settled scene under
     prefers-reduced-motion. So `left: 50%; translateX(-50%)` — which this file
     has used since the chapter was built — silently collapses to `left: 50%`
     for every reduced-motion visitor, which is exactly why the key measures
     200px OUTSIDE the container's right edge on production today.

     The offset is computed instead: a box of the key's own measure, hung half
     the difference left of the vessel column, has the vessel's centre for its
     own — arithmetic that cannot be switched off. */
  .camp #engine > .container .core__legend {
    position: absolute; top: 100%;
    left: calc(var(--v-scale-w) + var(--v-scale-air)
               + (var(--v-vessel-w) - var(--v-legend-w)) / 2);
    right: auto;
    width: var(--v-legend-w);
    /* one line. Broken over two it stops reading as a single equation and sets
       a ragged block under a capsule with a perfectly straight axis. */
    max-width: none; flex-wrap: nowrap;
    margin: var(--v-md) 0 0;
    transform: none;
  }

  /* THE STATE — top-aligned to the vessel, one air gap clear of the glass. */
  .camp #engine > .container .engine__state { flex: 0 0 var(--v-state-w); align-self: flex-start; }
}

/* ── MATCH REMAINING — the readout, at every viewport ──────────────────────── */
.camp #engine > .container .engine__state-k {
  margin: 0; font-size: 0.66rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: rgba(244, 239, 226, 0.5); line-height: 1.4;
}
.camp #engine > .container .engine__state-v {
  margin: var(--v-micro) 0 0;
  font-family: var(--font-display); font-weight: 500; font-variant-numeric: tabular-nums;
  font-size: clamp(1.45rem, 1.9vw, 1.85rem); line-height: 1.05;
  letter-spacing: -0.01em; color: var(--color-sand);
}
.camp #engine > .container .engine__state-note {
  margin: var(--v-sm) 0 0; font-size: 0.66rem; line-height: 1.5;
  color: rgba(244, 239, 226, 0.38);
}

/* ── THE RESOLUTION ────────────────────────────────────────────────────────
   Spans the full reach beneath BOTH zones, so the composition closes across
   its whole width and the section ends on its action rather than trailing into
   an unshaped empty quadrant under the financial table. A link, not a second
   gold button: Chapter VI is the next thing on the page. */
.camp #engine > .container .engine__close {
  display: flex; flex-wrap: wrap; align-items: baseline;
  justify-content: space-between; gap: var(--v-md) var(--v-lg);
  /* LARGE, not MOVEMENT: the stage already ends 52px below the vessel to clear
     the key, so a MOVEMENT on top of that opened a second void under the
     financial table — the exact failure this pass exists to remove. */
  margin-top: var(--v-lg);
  padding-top: var(--v-md);
  border-top: 1px solid rgba(244, 239, 226, 0.14);
}
.camp #engine > .container .engine__close-line {
  margin: 0; font-size: 1.02rem; line-height: 1.5; color: var(--on-dark-mut);
  max-width: 54ch;
}
/* THE RESOLUTION'S ACTION IS A PRIMARY CAMPAIGN CTA.
   It was a gold underline — a bespoke fourth treatment for one of the three
   sentences leadership names as the primary action family ("Give", "Donate to
   the campaign", "Continue to secure checkout"). It now carries `.btn
   .btn--primary` in the markup and adds nothing here but its place in the row.
   This is the journey's THIRD deliberate action: hero, film, here. */
.camp #engine > .container .engine__cta { flex: none; }

/* ── CHAPTER V RESPONSIVE — RECOMPOSED, NOT SHRUNK ─────────────────────────
   The desktop arrangement is not proportionally scaled down until it becomes
   illegible; each tier gets its own field. What is HELD CONSTANT is the
   instrument's proportion — the vessel stays between 3.1:1 and 3.3:1 at every
   width, so it is never the skinny tube and never the lozenge — and the
   ladder, which simply steps down a rung. What CHANGES is the arrangement
   around it: the breathing zone becomes horizontal, then vertical; the state
   readout moves from beside the vessel to beneath it; the resolution row goes
   from a split row to a stack. */

/* 901–1199 — the two zones survive, on a shorter ladder. */
@media (min-width: 901px) and (max-width: 1199px) {
  .camp #engine {
    --v-scale-w: 88px;  --v-scale-air: 28px;
    --v-vessel-w: 120px; --v-vessel-h: 390px;   /* 3.25:1 */
    --v-state-air: 28px; --v-state-w: 120px;
    --v-legend-w: 360px; --v-legend-reserve: 48px;   /* 320 broke "= total impact" onto a second line */
  }
  .camp #engine > .container.chapter__head { margin-bottom: var(--v-lg); }
  .camp #engine > .container .engine__stage {
    display: grid;
    grid-template-columns: minmax(0, 1fr)
      calc(var(--v-scale-w) + var(--v-scale-air) + var(--v-vessel-w)
           + var(--v-state-air) + var(--v-state-w));
    column-gap: var(--v-lg);          /* Zone B, one rung down */
    align-items: start;
  }
  .camp #engine > .container .engine__instrument {
    display: flex; align-items: stretch; align-self: start;
    gap: var(--v-state-air); padding-left: 0;
    height: var(--v-vessel-h); margin-bottom: var(--v-legend-reserve);
  }
  .camp #engine > .container .core {
    position: relative;
    flex: 0 0 calc(var(--v-scale-w) + var(--v-scale-air) + var(--v-vessel-w));
  }
  .camp #engine > .container .engine__state { flex: 0 0 var(--v-state-w); align-self: flex-start; }
  .camp #engine > .container .engine__state-note { display: none; }
}

/* THE INSTRUMENT'S INTERNAL GEOMETRY — one set of rules, driven entirely by the
   tokens each tier declares, so no breakpoint re-derives the scale gutter by
   hand the way six separate `margin-left` overrides used to. */
@media (min-width: 901px) {
  .camp #engine > .container .core__ticks { left: 0; }
  .camp #engine > .container .core__ticks span { left: 0; max-width: var(--v-scale-w); }
  .camp #engine > .container .core__ticks span::before {
    left: calc(var(--v-scale-w) + 6px); width: calc(var(--v-scale-air) - 6px);
  }
  .camp #engine > .container .core__tube {
    width: var(--v-vessel-w);
    margin-left: calc(var(--v-scale-w) + var(--v-scale-air));
  }
  .camp #engine > .container .core__legend {
    position: absolute; top: 100%; right: auto; transform: none;
    left: calc(var(--v-scale-w) + var(--v-scale-air)
               + (var(--v-vessel-w) - var(--v-legend-w)) / 2);
    width: var(--v-legend-w); max-width: none; flex-wrap: nowrap;
    margin: var(--v-md) 0 0;
  }
}

/* ≤900 — THE STACK. Story, then a real pause, then the instrument. The pause is
   the same XL rung the desktop spends horizontally: on a phone the breathing
   zone is simply vertical. The state readout comes to rest BESIDE the vessel
   still — it is 110px of type against a 110px vessel, which fits, and moving it
   under the key would separate the figure from the object it describes. */
@media (max-width: 900px) {
  .camp #engine {
    --v-scale-w: 76px;  --v-scale-air: 20px;
    --v-vessel-w: 110px; --v-vessel-h: 350px;   /* 3.18:1 */
    --v-state-air: 24px;
    --v-legend-reserve: 56px;
  }
  .camp #engine.chapter--stage { padding-block: var(--v-move); }
  .camp #engine > .container.chapter__head { margin-bottom: var(--v-lg); }

  .camp #engine > .container .engine__stage {
    display: grid; grid-template-columns: minmax(0, 1fr);
    justify-items: start;
    row-gap: var(--v-xl);            /* ZONE B, now vertical */
  }
  .camp #engine > .container .engine__instrument {
    display: flex; align-items: stretch; align-self: start; justify-self: start;
    gap: var(--v-state-air); padding-left: 0;
    height: var(--v-vessel-h); margin-bottom: var(--v-legend-reserve);
    width: 100%;
  }
  .camp #engine > .container .core {
    position: relative;
    flex: 0 0 calc(var(--v-scale-w) + var(--v-scale-air) + var(--v-vessel-w));
  }
  .camp #engine > .container .core__ticks { left: 0; }
  .camp #engine > .container .core__ticks span { left: 0; max-width: var(--v-scale-w); }
  .camp #engine > .container .core__ticks span::before {
    left: calc(var(--v-scale-w) + 4px); width: calc(var(--v-scale-air) - 4px);
  }
  .camp #engine > .container .core__tube {
    width: var(--v-vessel-w);
    margin-left: calc(var(--v-scale-w) + var(--v-scale-air));
  }
  /* the key spans the column and is allowed to wrap; nothing to centre on a
     vessel that is already near the spine */
  .camp #engine > .container .core__legend {
    position: absolute; top: 100%; left: 0; right: auto; transform: none;
    width: 100%; max-width: none; flex-wrap: wrap;
    justify-content: flex-start; text-align: left;
    margin: var(--v-md) 0 0;
  }
  .camp #engine > .container .engine__state {
    flex: 1 1 auto; min-width: 0; align-self: flex-start; padding-top: 2px;
  }
  /* the note repeats the gold callout in Zone A verbatim; at this width it is
     five ragged lines of 0.66rem beside the figure it is meant to support */
  .camp #engine > .container .engine__state-note { display: none; }

  .camp #engine > .container .engine__close {
    flex-direction: column; align-items: flex-start;
    gap: var(--v-md); margin-top: var(--v-lg);
  }
}

/* LANDSCAPE PHONE keeps its own composition; only the tokens change, so the
   rules above resolve to a short, wide field instead of being overridden. */
@media (min-width: 640px) and (max-height: 520px) and (orientation: landscape) {
  .camp #engine {
    --v-scale-w: 64px;  --v-scale-air: 16px;
    --v-vessel-w: 78px; --v-vessel-h: 250px;    /* 3.21:1 */
    --v-state-air: 18px; --v-state-w: 104px;
    --v-legend-w: 280px; --v-legend-reserve: 40px;
  }
  .camp #engine > .container .engine__stage {
    display: grid;
    grid-template-columns: minmax(0, 1fr)
      calc(var(--v-scale-w) + var(--v-scale-air) + var(--v-vessel-w)
           + var(--v-state-air) + var(--v-state-w));
    column-gap: var(--v-md); row-gap: 0; align-items: start;
  }
  .camp #engine > .container .engine__instrument {
    height: var(--v-vessel-h); margin-bottom: var(--v-legend-reserve); width: auto;
  }
  .camp #engine > .container .engine__state { flex: 0 0 var(--v-state-w); }
  .camp #engine > .container .engine__state-v { font-size: 1.15rem; }
  .camp #engine > .container .core__legend { font-size: 0.58rem; }
  .camp #engine > .container .engine__close { flex-direction: row; margin-top: var(--v-md); }
}

/* 561–900 — THE WIDE STACK, and the tier that failed its own review first time.
   A 110px vessel hung on the spine of a 720px column reproduced the exact 1440
   defect at another width: the instrument marooned in the left third with an
   abandoned right side, and the match readout floating in the middle of it.

   Two fixes, both compositional. The vessel takes a size proportionate to the
   COLUMN it is in rather than inheriting the phone's. And the cluster is
   centred, because once the story is stacked above it the instrument is a
   discrete object in its own row, not a column hung off a narrative edge — so
   the space around it becomes a frame instead of a void. The state readout is
   sized to match the scale gutter opposite it, which puts the vessel on the
   cluster's own axis. */
@media (min-width: 561px) and (max-width: 900px) {
  .camp #engine {
    --v-scale-w: 92px;  --v-scale-air: 32px;
    --v-vessel-w: 160px; --v-vessel-h: 500px;   /* 3.13:1 — the desktop ratio */
    --v-state-air: 32px; --v-state-w: 124px;
  }
  .camp #engine > .container .engine__instrument {
    width: auto; justify-self: center;
  }
  .camp #engine > .container .engine__state {
    flex: 0 0 var(--v-state-w); padding-top: 2px;
  }
  /* the key centres on the vessel again, as it does on desktop */
  .camp #engine > .container .core__legend {
    left: calc(var(--v-scale-w) + var(--v-scale-air)
               + (var(--v-vessel-w) - var(--v-legend-w)) / 2);
    width: var(--v-legend-w); max-width: none;
    justify-content: center; text-align: center; flex-wrap: wrap;
  }
  .camp #engine { --v-legend-w: 360px; }
}

/* ≤360 — THE SMALLEST REAL PHONE. The match-remaining figure is a single
   unbreakable word roughly 86px wide at this type size, so at 320 the state
   readout could not shrink into the 58px the rest of the cluster left it, and
   it pushed the document 7px sideways. The cluster is re-budgeted against the
   actual 288px measure instead of inheriting the 390 one. 375 and above keep
   the fuller vessel — this tier is for 320-360 only.

   NO FIGURE IS NAMED HERE ON PURPOSE: the number comes from the ledger and
   changes with every gift. The constraint is the WIDTH OF A CURRENCY WORD at
   this size, which is what the budget is built against. */
@media (max-width: 360px) {
  .camp #engine {
    --v-scale-w: 68px;  --v-scale-air: 16px;
    --v-vessel-w: 96px; --v-vessel-h: 305px;    /* 3.18:1 — held */
    --v-state-air: 20px;
  }
  .camp #engine > .container .engine__state { flex: 0 1 auto; min-width: 0; }
  .camp #engine > .container .engine__state-v { font-size: 1.2rem; }
  .camp #engine > .container .engine__state-k { font-size: 0.6rem; letter-spacing: 0.1em; }
}

/* LANDSCAPE PHONE, RESTATED LAST. It shares its width range with the wide-stack
   tier above, so it has to be the later rule or the stack's tokens win and the
   instrument runs taller than the whole viewport. */
@media (min-width: 640px) and (max-height: 520px) and (orientation: landscape) {
  .camp #engine {
    --v-scale-w: 64px;  --v-scale-air: 16px;
    --v-vessel-w: 78px; --v-vessel-h: 250px;    /* 3.21:1 */
    --v-state-air: 18px; --v-state-w: 104px;
    --v-legend-w: 280px; --v-legend-reserve: 40px;
  }
  .camp #engine > .container .engine__instrument { width: auto; justify-self: end; }
  .camp #engine > .container .engine__state { flex: 0 0 var(--v-state-w); }

  /* THE KEY MUST NOT CROSS THE BREATHING ZONE.
     Centred on the vessel, a 280px key hung under a 78px vessel reaches 101px
     to its left — far enough that at 667x375 it came within 11px of the
     financial column and quietly filled the gap the composition depends on.
     The declared column gap was intact the whole time; an absolutely
     positioned child was standing in it, which is exactly the failure mode the
     verification gate measures to the nearest PAINTED thing to catch.

     Here the key hangs from the instrument field's own left edge instead of
     centring on the vessel, so it can never reach back past the instrument. */
  .camp #engine > .container .core__legend {
    left: 0; width: max-content; max-width: var(--v-legend-w);
    justify-content: flex-start; text-align: left; flex-wrap: wrap;
  }
  /* two lines at this measure, so the key's room is booked for two */
  .camp #engine { --v-legend-reserve: 76px; }
}

/* ════════════════════════════════════════════════════════════════════════════
   FINAL LANDING CONVERGENCE
   ════════════════════════

   One pass reconciling the leadership edits and the design-system drift a
   full-page audit found. Nothing here is a new art direction: the chapters,
   the photography, the instrument and the giving surface are the approved ones.
   ════════════════════════════════════════════════════════════════════════════ */

/* ── THE MASTHEAD MARK ──────────────────────────────────────────────────────
   Leadership: increase the site logo's prominence. The lockup is derived from
   --tlm-wordmark (each artwork sized off its OWN measured wordmark band so the
   wordmark stays optically identical across the nav's dark and solid states),
   so prominence is one token and the tagline's cap-height maths follows it.
   44 -> 52 takes the rendered mark from 58px to 69px.

   DECLARED ON `.nav__brand`, NOT ON `.camp`: that is where the token is owned,
   and the two breakpoint step-downs below it (40 at 1200, 34 at 600) are
   written against the same selector. Setting it on the root would be shadowed
   by all three. */
.camp .nav .nav__brand { --tlm-wordmark: 52px; }
@media (max-width: 1200px) { .camp .nav .nav__brand { --tlm-wordmark: 46px; } }
@media (max-width: 600px)  { .camp .nav .nav__brand { --tlm-wordmark: 38px; } }

/* ── CHAPTER I · THE RECOMPOSITION ──────────────────────────────────────────
   Leadership asked for three things here and they are one move, not three: the
   photograph roughly twice as tall, the capability list gone, and the proof
   band brought up out of the dead space beneath them.

   The right column used to hold the photograph for two grid rows and the 01-05
   index for a third, which left the picture floating above a list and the proof
   band stranded ~500px below the picture's bottom edge. Now the photograph owns
   the ENTIRE right column for every row of the chapter and stretches to the
   text column's own height — so it is about twice as tall as it was, the dead
   space it used to sit above no longer exists, and the proof band moves up to
   close the chapter directly under both columns.

   NO DISTORTION: the frame changes, the picture does not. `object-fit: cover`
   crops; `aspect-ratio` is released so the frame follows the column rather than
   forcing the image into a shape. The crop stays deliberate — the two subjects
   sit at x 46-65%, y 27-50% of the artwork, so 55%/38% keeps both of them in
   frame as the box gets tall. */
@media (min-width: 901px) {
  .camp .why .why__story { grid-column: 1 / 7; grid-row: 2; }
  .camp .why .why__case  { grid-column: 1 / 7; grid-row: 3; align-self: start; }
  .camp .why .why__caps  { grid-column: 1 / 7; grid-row: 4; align-self: start; }
  .camp .why .why__plate {
    grid-column: 8 / 13; grid-row: 1 / 5; align-self: stretch;
    display: flex; flex-direction: column; margin-top: 0;
  }
  .camp .why .why__plate img {
    flex: 1 1 auto; width: 100%; height: 100%;
    aspect-ratio: auto; min-height: clamp(420px, 40vw, 600px); max-height: 640px;
    object-fit: cover; object-position: 55% 38%;
  }
  .camp .why .proofline { grid-column: 1 / 13; grid-row: 5; margin-top: clamp(26px, 2.6vw, 40px); }
}

/* THE POINT THE LIST WAS STANDING IN FOR. Display type for the distinction,
   body type for the support — the same two-part shape as `.why__ask` +
   narrative, so this beat belongs to the chapter's existing hierarchy rather
   than introducing a fourth voice. */
.camp .why .why__catch {
  font-family: var(--font-display); font-weight: 500;
  font-size: clamp(1.18rem, 1.75vw, 1.6rem); line-height: 1.2; letter-spacing: -0.01em;
  color: var(--ink); margin: 0;
}
.camp .why .why__catch-sub {
  font-family: var(--font-body); font-size: 1.02rem; line-height: 1.6;
  color: var(--text-muted); margin: 14px 0 0; max-width: 54ch;
}

/* ── CHAPTER II · THE FILM SIZED BY THE FIELD ───────────────────────────────
   HISTORY, because this is the third answer to the same question. The film ran
   at --reach-band (940 x 529 at 1440); leadership said it was being shown
   small; it was then given a bleed PAST the spine — 1288 x 725, the only block
   on the page wider than --reach-full and the only one that did not start at
   the spine. Measured on the hosted page that is 89%vw and 90%vh at 1440x900,
   93% and 94% at 1280x800, and 112%vh at 1440x720, where it does not fit on
   the screen at all.

   WHAT WAS ACTUALLY WRONG was never the width on its own. It was that the
   shell is TALLER THAN THE FIELD IT IS SHOWN IN. The masthead is opaque and
   110px deep, so the usable field at 1440x900 is 790px against an 808px shell:
   the frame's top edge was permanently under the nav and its bottom edge
   permanently below the fold. A donor scrolling the film into view never saw
   the object — only picture, edge to edge, with no top and no bottom. That is
   the whole of "it feels like a giant embedded YouTube player": not size, but
   an object whose boundaries are never visible.

   THE RULE, therefore, is a rule about the FIELD and not about the spine:

     the film is the largest 16:9 frame that fits WHOLE — picture, slate and
     transport together — inside the viewport, clear of the masthead, on a
     stage of dark ground; and never wider than --reach-full, because nothing
     on this page reaches past the campaign's widest reach.

   Every term below is a measured part of the composition, not a ratio picked
   to land on a number. At 1440x900 this resolves to 1003 x 647: the shell's
   rounded corners and its gold hairline are visible on all four sides at once,
   with 293px of dark ground to its right doing the work a cinema wall does.
   At 1280x800 it is 875 x 575, at 1440x720 it is 772 x 517 — the film gets
   smaller as the field gets shorter, which is the only behaviour that keeps a
   frame whole, and width-only logic cannot express.

   `min()` carries 100% as its first term so this is a NO-OP wherever the
   container is already the narrower constraint — every viewport at or below
   1024, where the film sits at the gutter exactly as before. `max()` floors
   the field so a landscape phone shrinks the frame instead of collapsing it.

   THE CHAPTER HEAD, THE CREDIT, THE QUOTE AND THE ACTION DO NOT MOVE, and the
   film now starts on the spine with them: one left edge, no exceptions. */
.camp .film {
  --film-nav:   110px;  /* the masthead is opaque; this much field is not ours */
  --film-rail:   84px;  /* slate + transport, below the picture, inside the shell */
  --film-air:    72px;  /* the stage above and below the frame — assigned space */
  /* The floor stops the frame collapsing on a short field. It is expressed in
     vh and not in px for a reason found by measuring: a fixed 232px floor is
     TALLER than the whole field on a landscape phone, so the one term whose
     job was to protect the frame was the only term that could crop it. A
     vh floor cannot outrun the field it floors. */
  --film-field:  max(28vh, calc(100vh - var(--film-nav) - var(--film-rail) - var(--film-air) * 2));
}
.camp .film > .container .film__shell {
  max-width: none;
  width: min(100%, var(--reach-full), calc(var(--film-field) * 16 / 9));
  margin-inline: 0;
  margin-top: clamp(36px, 3.4vw, 52px);
}

/* THE CREDIT IS PART OF THE FRAME, NOT A BAND BESIDE IT. The hairline and the
   Patriot lockup under the picture are its attribution: they belong to the
   film's width, whatever the field resolves that to. Pinned at --reach-band
   they were 59px short of the frame at 1440x900 — a near-miss reads as an
   error where a clear difference would have read as a decision — and 119px
   PROUD of it at 1280x800, 261px at 1440x720, because the frame now moves and
   the band did not. One expression, used twice, cannot drift apart. */
.camp .film > .container .film__credit {
  max-width: none;
  width: min(100%, var(--reach-full), calc(var(--film-field) * 16 / 9));
}

/* SHORT LANDSCAPE. The masthead is `fixed` and stays 103px deep however short
   the field gets, so on a landscape phone it is 27% of everything the donor
   has. Measured, not assumed. The stage either side of the frame is the term
   to spend here — 72px of ceremony above and below is a desktop luxury on a
   375px field — so the air comes down and the frame keeps its size. Section V
   already treats this viewport as its own composition; this is the same move
   for Chapter II, and it changes tokens only: the rule above still resolves. */
@media (orientation: landscape) and (max-height: 520px) {
  .camp .film { --film-nav: 103px; --film-air: 16px; }
}

/* MID-JOURNEY ACTION — on the spine, closing the chapter. */
.camp .film .film__act { margin-top: clamp(30px, 3.4vw, 46px); }

/* ── THE ACTION SYSTEM ──────────────────────────────────────────────────────
   The audit found three radius languages (999 / 0 / 10) and three type families
   (Inter / Fraunces / an inherited Arial) across things a donor clicks. Two of
   those were accidents of sections being authored separately.

   THE CAMPAIGN HAS TWO INTENTIONAL GEOMETRIES, and the difference is a
   difference of JOB:

     ACTIONS  pill (--radius-pill), Inter 600, gold ground for primary, hairline
              for secondary. "Give", "Donate to the campaign", "Continue to
              secure checkout" — anything that moves the donor forward.
     CONTROLS 10px, because they are values being SELECTED, not actions being
              taken: the amount chips and the film transport. A chip that reads
              as a pill reads as four competing Donate buttons.

   The amount chips keep Fraunces deliberately: they are FINANCIAL NUMBERS, the
   same role as the figures in Chapter V's ledger and the impact total, and they
   are set in the same face as those. That is the explicit visual reason the
   brief asks for, and it survives the full-page review because the chips read
   as money either side of the $ column, not as buttons in a different font. */
.camp #give .cta6 { border-radius: var(--radius-pill); }
/* the film's transport joins the CONTROL family instead of sitting at 0 */
.camp .film .film__start { border-radius: var(--radius-md); }
/* the invisible play surface covers the picture; it has no ground of its own,
   so it had no font either and fell back to the UA's Arial the moment anything
   inside it rendered text */
.camp .film .film__surface { font: inherit; font-family: var(--font-body); }
/* secondary text actions share one treatment */
/* WCAG 2.2 AA 2.5.8 asks for 24px. This link is one line of 0.95rem body type,
   which paints 23px tall, and it is the only target on the page under the
   floor. Padding rather than a min-height, so the extra 2px sit inside the
   link's own box and the footer row's baseline does not move: the row is
   aligned on its items' text, not on their boxes. */
.camp .foot .foot__back { font-family: var(--font-body); font-size: 0.95rem;
  display: inline-block; padding-block: 3px; }

/* ── LABEL / EYEBROW ROLE ───────────────────────────────────────────────────
   The audit counted ten different size/tracking pairs across things doing the
   same job. Two roles, one token each: LABEL for a section's eyebrow, LABEL-SM
   for an annotation inside an instrument. Ledger figures, display type and body
   are untouched. */
.camp { --label: 0.72rem; --label-track: 0.14em; --label-sm: 0.62rem; --label-sm-track: 0.12em; }

/* LABEL — a section's or a control group's eyebrow. Eleven different
   size/tracking pairs were doing this one job, because each chapter set its own
   when it was authored. Every selector is parented so it outranks both the
   chapter-local rule it replaces and the page's own custom_css layer. */
.camp .why .why__grid .proofline__stats span,
.camp #builds .builds__index-eyebrow,
.camp #give .inst__label,
.camp #engine > .container .engine__eyebrow,
.camp .foot .foot__eyebrow,
.camp .film .film__credit-text span,
.camp .film .film__start-meta,
.camp #match .doubling__lbl {
  font-size: var(--label) !important;
  letter-spacing: var(--label-track) !important;
}

/* LABEL-SM — an annotation INSIDE an instrument, where the label is subordinate
   to a figure sitting next to it. */
.camp .hero .heromark__k,
.camp #engine > .container .engine__state-k,
.camp #engine > .container .core__legend,
.camp #engine > .container .core__ticks small,
.camp #match .doubling__beamlbl,
.camp #give .amount-custom__lbl,
.camp #give .amount-custom__cur {
  font-size: var(--label-sm) !important;
  letter-spacing: var(--label-sm-track) !important;
}

/* ════════════════════════════════════════════════════════════════════════════
   PRODUCT COMPOSITION PASS — THE PAGE AS ONE DOCUMENT
   ═══════════════════════════════════════════════════

   This layer is not a set of corrections. It is the campaign's own recovered
   design grammar — docs/design/layout-system/01-SYSTEM.md, written against this
   product and never implemented below the horizontal axis — applied to the
   whole page at once.

   WHAT WAS ALREADY RIGHT AND IS NOT TOUCHED: the spine and the four reaches
   (§2), the chapter rule (the transition device), Chapter V's approved
   composition, Chapter VI's approved giving instrument, every figure's binding
   to the verified ledger, the photography, the type faces, and the copy.

   WHAT THIS LAYER OWNS:
     1  the vertical scale — four named bands replacing six ad-hoc multipliers
     2  the hero, recomposed as one field instead of three floating objects
     3  Chapter I, recomposed so evidence follows argument with no void between
     4  Chapter III's diagram, given optical mass proportional to the type
     5  Chapter IV, brought onto the page's own right bound
     6  Chapter VI's editorial column, closed against the instrument beside it
     7  the responsive composition at every width the QA matrix carries

   Every selector is at least three classes deep, for the cascade reason the
   CAMPAIGN SPINE block documents: the page's own custom_css loads last.
   ════════════════════════════════════════════════════════════════════════════ */


/* ── 1 · THE VERTICAL SCALE ─────────────────────────────────────────────────
   REPLACES: --chapter-open/--chapter-close plus the ×1.1 ×1.3 ×1.35 ×1.45
   multipliers. Those encoded the right pacing JUDGEMENTS and the wrong pacing
   SYSTEM: six distinct numbers, none of them nameable, none assertable, and
   two of them reachable only through an id-scoped shorthand.

   01-SYSTEM §5.3 gives four named bands and §6.2 gives the rule for choosing
   between them — section padding is a property of the BOUNDARY, not of the
   section:

     the ground inverts          -> --band-tight   the inversion is the signal;
                                                   space would say it twice
     same ground, <=5% luminance -> --band-wide    nothing else marks it, so
                                                   space does the whole job
     a narrative hinge           -> --band-hinge   the space IS the content
     everything else             -> --band

   Measured at 1440 the seven boundaries now resolve to 100 · 208 · 172 · 272 ·
   200 · 248 · 172 px of air. Before this layer they were 66 · 237 · 225 · 315 ·
   268 · 290 · 205 — which is four effectively identical values with an
   outlier at each end, i.e. no rhythm, just padding. The two widest are now the
   two that earn it: III→IV, where the ground does not change at all, and
   V→VI, the page's one hinge from watching to acting.

   §6.3 THE BAND CAP is honoured by assignment rather than by arithmetic: no
   chapter on this page is short enough for open+close to exceed half its
   content height. The thank-you product, which is, carries --band-scale. */
.camp {
  --band-tight: clamp(48px,  5.0vw,  72px);
  --band:       clamp(64px,  7.0vw, 100px);
  --band-wide:  clamp(88px,  9.5vw, 136px);
  --band-hinge: clamp(112px,12.0vw, 176px);
}

/* Each chapter declares two values and nothing else. Stated as full
   padding-block shorthands, and id-scoped where the chapter is, so no earlier
   shorthand can half-apply — the bug the previous layer had to write a second
   block to undo. */
.camp .chapter.why      { padding-block: var(--band)       var(--band-wide);  }
.camp .chapter.film     { padding-block: var(--band-tight) var(--band-tight); }
.camp .chapter#match    { padding-block: var(--band)       var(--band-wide);  }
.camp .chapter#builds   { padding-block: var(--band-wide)  var(--band);       }
.camp .chapter#engine   { padding-block: var(--band)       var(--band-hinge); }
.camp .chapter#give     { padding-block: var(--band-tight) var(--band);       }
.camp footer.foot       { padding-block: var(--band-tight) var(--band-tight); }


/* ── 2 · THE HERO, RECOMPOSED ───────────────────────────────────────────────
   WHAT WAS WRONG, stated as composition and not as pixels: the hero was a
   photograph with three unrelated objects standing on it. A copy block on the
   left, a translucent progress panel below it in a material used nowhere else
   on the page, and a small framed video card in the right margin. Three
   objects, three visual languages, no shared edge, no shared baseline, and no
   relationship between any two of them.

   IT IS NOW ONE FIELD. A single column on the spine, reading top to bottom:
   who · the proposition · the action · where the campaign stands. Everything
   hangs off one left edge and closes on one hairline. The film card is gone
   (see the canvas) and the panel is gone; what remains is drawn with the
   page's own materials — rule, type, instrument, air. */
.camp .hero .hero__grid {
  grid-template-columns: minmax(0, 1fr);
  align-items: end;
  max-width: var(--reach-full);
}
.camp .hero .hero__lede { max-width: var(--reach-text); }

/* THE SUBJECT-ANCHORED SCRIM (§9.3). The scrim was a vertical wash: darkest at
   the bottom, fading up. A subject is not distributed vertically, so a vertical
   wash cannot protect type that runs horizontally across one — which is why the
   headline could average comfortably and still put its last two glyphs on a lit
   shoulder. This adds a HORIZONTAL component keyed to the side the type
   occupies, so the reading half of the frame carries its own ground and the
   photograph keeps its light on the half nobody is reading across. */
/* SCOPED TO THE COMPOSITION THAT NEEDS IT, WHICH IS THE ONE WHERE TYPE SITS ON
   THE PHOTOGRAPH. The hero has three compositions and only the desktop one
   overlays the headline on the picture. Below 601px the photograph is a BAND
   with the type underneath it, and the landscape-phone hero splits the two into
   side-by-side regions — both already carry their own carefully authored scrims
   whose whole point is to leave the picture OPEN ("faces, bodies, monitors,
   untouched") and dissolve only its lower edge into the stage.

   Unscoped, this rule was three classes deep and later in the file than both of
   them, so it won everywhere: the mobile hero's photograph was carrying a 0.86
   horizontal wash and a 0.72 foot that were designed to protect display type
   that is not there, and the picture went dark. A scrim exists to make type
   legible over an image; where there is no type over the image there is nothing
   for it to do.

   The query is the exact complement of the landscape-phone case within 601px+:
   that case is `landscape AND max-width 960 AND max-height 440`, so anything
   961px or wider, or 441px or taller, is the desktop composition. */
@media (min-width: 961px), (min-width: 601px) and (min-height: 441px) {
  .camp .hero .hero__scrim {
    background:
      linear-gradient(90deg, rgba(10,9,6,0.86) 0%, rgba(10,9,6,0.72) 34%,
                              rgba(10,9,6,0.30) 62%, rgba(10,9,6,0.06) 100%),
      linear-gradient(0deg,  rgba(10,9,6,0.72) 0%, rgba(10,9,6,0.20) 34%,
                              rgba(10,9,6,0.06) 62%, rgba(10,9,6,0.42) 100%);
  }
}

/* CROP (§9.2). The comma of "Each dollar," was landing on the student's beanie
   — a tangent, which the eye reads as a collision it cannot resolve, and the
   worst available relationship between type and a subject. The existing
   correction bought 57px of slack and spent it all; the subject needed more.
   Panning further right is free here because his gaze already runs right, away
   from the type, so the added clearance also improves the reading of the
   photograph. The campaign's signature line is not shrunk to dodge a crop. */
@media (min-width: 601px) {
  .camp .hero .hero__media img { transform: scale(1.14) translateX(7.5%); }
}

/* THE CAMPAIGN POSITION — the hero's closing line.
   Hairline on the spine, label, figure, instrument, annotation. No ground, no
   border, no radius, no blur: the four properties that made the old treatment
   read as a widget. It reaches --reach-read, so it closes the hero on the same
   measure the ledes above it use. */
.camp .hero .heromark {
  display: block;
  max-width: var(--reach-read);
  margin-top: clamp(30px, 3.4vw, 46px);
  padding-top: clamp(16px, 1.6vw, 22px);
  border-top: 1px solid rgba(244, 239, 226, 0.34);
  text-decoration: none;
  color: var(--on-dark);
}
.camp .hero .heromark__k {
  display: block;
  font-size: var(--label-sm); letter-spacing: var(--label-sm-track);
  text-transform: uppercase; font-weight: 600;
  color: rgba(244, 239, 226, 0.68);
}
/* The figure and the instrument share one baseline — that shared baseline is
   what makes them one object without a box around them. */
.camp .hero .heromark__body {
  display: flex; align-items: flex-end; gap: clamp(14px, 1.4vw, 20px);
  margin-top: 8px;
}
.camp .hero .heromark__v {
  font-family: var(--font-display); font-weight: 500;
  font-variant-numeric: tabular-nums;
  font-size: clamp(2.1rem, 3.2vw, 2.9rem); line-height: 0.92;
  letter-spacing: -0.02em; color: #fff;
}
/* THE CHAPTER V VESSEL AT EDITORIAL SCALE. Same capsule, same donor band under
   match band, same lit meniscus, same $100k cap hairline at the same half
   height. It is the instrument reporting; Chapter V is the instrument
   explaining. One visual language, two scales. */
.camp .hero .heromark__vessel {
  position: relative; display: block; flex: none;
  width: 20px; height: clamp(56px, 5.6vw, 76px);
  border-radius: 999px; overflow: hidden;
  background: rgba(244, 239, 226, 0.10);
  box-shadow: inset 0 0 0 1px rgba(244, 239, 226, 0.22);
  margin-bottom: 0.14em;
}
.camp .hero .heromark__fill {
  position: absolute; left: 0; right: 0; bottom: 0; height: 0;
  display: flex; flex-direction: column-reverse;
  transition: height var(--dur-slow) var(--ease-out);
}
.camp .hero .heromark__seg { display: block; width: 100%; transition: height var(--dur-slow) var(--ease-out); }
.camp .hero .heromark__seg--donor { background: linear-gradient(180deg, rgba(246,211,107,0.30), rgba(120,96,30,0.46)); }
.camp .hero .heromark__seg--match { background: linear-gradient(180deg, rgba(218,165,32,0.72), rgba(218,165,32,0.40)); }
.camp .hero .heromark__meniscus {
  position: absolute; left: 0; right: 0; top: -1px; height: 2px;
  background: var(--color-sand); box-shadow: 0 0 8px rgba(218,165,32,0.75);
}
.camp .hero .heromark__cap {
  position: absolute; left: 0; right: 0; bottom: 50%; height: 1px;
  background: rgba(218, 165, 32, 0.55);
}
/* THE CAP MARK, ANNOTATED OUTSIDE THE VESSEL. Chapter V prints "$100k · FULL
   MATCH UNLOCKED" against this same half-height line. The hero cannot carry
   that label — a thumbnail reports — but an unlabelled hairline inside a 20px
   capsule is invisible, so the mark is extended into the air beside it as a
   tick. It is the one piece of the chapter's tick scale the hero keeps, and it
   is the piece that makes the vessel readable as a MEASURE rather than as a
   decorated bar. */
.camp .hero .heromark__cap::after {
  content: ""; position: absolute; left: 100%; bottom: 0;
  width: 9px; height: 1px; background: rgba(218, 165, 32, 0.55);
}
.camp .hero .heromark__of {
  display: block; margin-top: 10px;
  font-size: 0.88rem; line-height: 1.4; color: rgba(244, 239, 226, 0.70);
}
.camp .hero .heromark__of i { font-style: normal; font-variant-numeric: tabular-nums; color: var(--color-sand); }
.camp .hero .heromark:hover .heromark__v { color: var(--color-sand); }
.camp .hero .heromark:hover { border-top-color: rgba(218, 165, 32, 0.6); }
.camp .hero .heromark:focus-visible { outline: 3px solid var(--gold); outline-offset: 6px; }


/* ── 3 · CHAPTER I — STORY → EVIDENCE, WITH NOTHING IN BETWEEN ──────────────
   MEASURED (1440): the argument column ran 144→696 and finished at y2094. The
   evidence plate ran 844→1296 and its photograph finished at y1888. The proof
   band opened at y2168. So the chapter's right-hand third carried ~240px of
   nothing between the photograph and the proof, and its container carried a
   further ~200px of nothing inside itself — the plate box was 846 tall around a
   640 tall picture. Leadership asked for the Chapter I image to become more
   substantial and for proof to move up. Both are the same defect: the
   photograph was not doing the job its column was sized for.

   THE PHOTOGRAPH NOW RUNS THE FULL HEIGHT OF THE ARGUMENT IT ILLUSTRATES.
   Not "taller by 80px" — bound to the argument, so the two columns begin
   together and end together and the proof band follows immediately as the
   conclusion of both. That is the STORY → EVIDENCE relationship the chapter
   was written for, and it removes the void by giving the space a job rather
   than by deleting it.

   THE CROP BAND IS RESPECTED. The plate is already composed portrait at this
   width (452x640 measured, not the 3/2 the artwork ships); stretching it
   extends the same crop downward through desk and monitor, never across either
   student. object-position keeps the two faces at the same place in the frame
   they occupy today. */
@media (min-width: 1025px) {
  .camp .why .why__grid { align-items: stretch; }
  .camp .why .why__plate {
    grid-row: 1 / 5;
    align-self: stretch;
    display: flex; flex-direction: column;
  }
  .camp .why .why__plate img {
    /* `flex: 1 1 0` with `min-height: 0` is what actually binds the picture to
       the column. `flex: 1 1 auto` + `height: 100%` resolves the basis against
       the whole box and then refuses to grow past it, which left the picture at
       its own intrinsic height inside a taller container — the void moved
       rather than closing. */
    flex: 1 1 0;
    /* THE CAP WAS THE VOID. The pass above this one already bound the plate to
       the column — and then capped the picture at `max-height: 640px`, which is
       shorter than the argument beside it at every width above 1024. So the
       frame stretched, the picture stopped at 640, and the ~175px it refused to
       fill became the dead field between the photograph and the proof band.
       Releasing the cap is the entire remaining fix; the crop, the column, the
       object-position and the grid placement are all already correct. */
    max-height: none;
    min-height: clamp(480px, 42vw, 720px);
    aspect-ratio: auto;
    height: auto;
    object-fit: cover;
    /* THE CROP SHOWS THE EQUIPMENT, BECAUSE THE EQUIPMENT IS THE CAMPAIGN.
       The artwork is 3:2 and the plate is 452x846 — a window on 36% of its
       width. The two students sit at 37-75% of that width; the workstation they
       are using sits at 16-33%. So any anchor centred on the pair excludes the
       computer completely, and the chapter about replacing classroom hardware
       was being illustrated by two backs and an office chair.

       No anchor holds both students AND the monitor in this window: that needs
       59% of the width and the box gives 36. The choice is therefore which
       subject this chapter is about, and it is about the machines. 38% frames
       one student at his station with the audio session open on the monitor
       beside him, the headphones he is working in, the coiled cable and the
       notebooks on the desk.

       The second student is outside the DESKTOP frame only. The tablet (16:9)
       and mobile (4:3) crops are 89-100% of the artwork's width and still carry
       both, so they need no equivalent decision and are left alone.

       The <img> alt in the canvas was rewritten with this crop, not after it. */
    object-position: 38% 42%;
  }
  /* The caption belongs to the picture (cluster, --s-3), and the picture now
     ends where the argument ends, so the caption is the last beat of BOTH
     columns rather than a line hanging under a short plate. */
  .camp .why .why__plate figcaption { flex: none; padding-top: 14px; }

  /* THE PROOF IS THE EVIDENCE THE ARGUMENT GENERATED, so it follows the
     argument at one movement gap — not at a gap plus whatever the short column
     left over. The previous 30-52px was measured from the taller column and
     read as a fourth, unnamed value. */
  .camp .why .why__grid .proofline { margin-top: clamp(40px, 4.4vw, 64px); }
}


/* ── 4 · CHAPTER III — THE DIAGRAM MUST CARRY ITS OWN WEIGHT ────────────────
   R10.3 / C4: "a hairline across 1152px is not a counterweight." The mechanism
   was drawn at 3.5px of non-scaling stroke either side of a 46px lens, opposite
   two numerals set at 5rem of Fraunces. The type had all the ink and the
   mechanism had none, so the chapter read as "$1 ... $2" with decoration
   between them rather than as one measured elevation.

   The stroke doubles and the field it is drawn in grows, so the divergence
   becomes a visible EVENT at the lens instead of a change of angle. Nothing
   else in the figure moves: same geometry, same labels, same copy, same colours,
   and the gold still marks only the matched half. */
.camp #match .doubling .prism__in,
.camp #match .doubling .prism__out { stroke-width: 7; }
.camp #match .doubling .prism__svg { height: clamp(150px, 15.5vw, 228px); }

/* THE CHAPTER CLOSES ON ITS BAND, NOT ON A DEAD FOOT. The confirming sentence
   under the figure sat ~180px above the section's own close, so III ended twice
   — once where the argument stopped and again where the padding did. The prose
   is bound to the figure at one evidence gap and the band owns everything
   after it. */
.camp #match > .container.prose { margin-top: clamp(40px, 4.4vw, 60px); margin-bottom: 0; }


/* ── 5 · CHAPTER IV — ON THE PAGE'S OWN RIGHT BOUND ─────────────────────────
   MEASURED: Chapter IV's evidence band stopped at x1084 while Chapters I, III,
   V and VI all close at x1296. One chapter 212px short of the page's right edge
   reads as a chapter that did not finish, and it is the only place on the page
   where the eye has to ask whether something is missing.

   The band takes --reach-full like the other instruments, and the whole 212px
   goes to the photograph — which was 520x347 against a seven-item index, the
   lighter half of a split that is supposed to be evidence. Column proportion
   and the 3/2 crop are unchanged; only the band's total measure grows. */
@media (min-width: 901px) {
  .camp #builds > .builds__band { max-width: var(--reach-full); }
  .camp #builds > .builds__band .builds__frame { flex: 1 1 auto; }
}


/* ── 6 · CHAPTER VI — TWO COLUMNS THAT END TOGETHER ─────────────────────────
   MEASURED: the editorial column finished at y7848 and the giving instrument at
   y8097 — a 249px dead foot under the chapter's own headline, in the chapter
   where the donor is deciding. R10.4: the bottom of a section is composed.

   The live availability line becomes the column's CLOSING beat rather than a
   third thing in the middle of it, and it is drawn in the chapter's own
   editorial voice — a hairline and a figure on the spine — instead of in the
   gold-rule treatment it carried, which was a third visual language for
   campaign progress after the hero mark and Chapter V's vessel. One instrument,
   one restatement, one voice.

   It is still written only by the ledger (`data-remaining-match`, empty until
   /totals answers) and still collapses to nothing when empty. */
/* WHY IT IS NOT PUSHED TO THE COLUMN'S FOOT. Bottom-aligning it does make the
   two columns terminate on the same baseline — and opens a 280px hole in the
   middle of the editorial column to do it. R10.1 is explicit that a split is
   rebalanced by re-assigning or resizing content, never by adding space, and a
   void bounded by painted content at both ends is worse than one below the last
   line: the first reads as something missing, the second reads as the eye being
   handed to the instrument.

   So the availability line closes the head CLUSTER at one group gap, the
   editorial column ends where its argument ends, and the space under it is
   FOCAL ISOLATION (§P2) around the one object in this chapter the donor is
   meant to act on. This is the deliberate exception R10.5 requires be written
   down. */
@media (min-width: 901px) {
  .camp #give .give6 .give6__head { max-width: var(--reach-read); }
  .camp #give .give6 .give6__avail { margin-top: clamp(34px, 3.6vw, 48px); }
}
.camp #give .give6 .give6__avail {
  border-left: 0;
  border-top: 1px solid var(--rule-strong);
  padding-left: 0;
  max-width: var(--reach-read);
  color: var(--text-muted);
}
.camp #give .give6 .give6__avail b {
  display: block;
  font-family: var(--font-display); font-weight: 500;
  font-variant-numeric: tabular-nums;
  font-size: clamp(1.7rem, 2.4vw, 2.2rem); line-height: 1;
  letter-spacing: -0.02em; color: var(--ink);
  margin-bottom: 8px;
}


/* ── 7 · CHAPTER II — THE POSTER IS ART-DIRECTED, NOT A FRAME GRAB ──────────
   The film is the page's largest visual event and it was opening on an
   unretouched video-conference frame with the source's own burned-in lower
   third across it: a solid gold plate roughly 1288x100 carrying "JOE STOCKER /
   FOUNDER, CEO of PATRIOT CONSULTING" in condensed caps. That plate was the
   single largest area of gold anywhere on the page — larger than the hero CTA,
   the chapter numerals, the match beam and Chapter V's fill combined — spent on
   information the credit block eight pixels below it already states in the
   campaign's own typography.

   C2 is explicit that gold marks match, energy, action and identity, and is
   never a ground. A broadcast name plate is a ground.

   THE CORRECTION IS THE CROP, NOT THE ARTWORK. The frame is pushed in and up
   so the lower third falls outside the picture and Joe fills more of it — which
   is also what the frame wanted compositionally: he was a small figure in the
   middle of a room. 1600px of source into a 1288px frame leaves the headroom
   this costs, so no sharpness is spent. The identification the plate carried is
   not lost: `.film__slate` prints it inside the picture in the campaign's own
   type, and the credit block states the firm directly beneath. */
.camp .film .film__viewport .film__poster {
  transform: scale(1.19) translateY(-7.5%);
  transform-origin: 50% 42%;
}
/* THE FOOT IS GRADED, NOT CROPPED. Measured on the rendered composite, the
   burned-in plate occupies the bottom 3%-20% of the picture. Cropping it out
   needs a 2.5x scale — which would throw away the room Joe is sitting in and
   soften a web-conference source past use. So the picture keeps its frame and
   the FILM'S OWN FOOT is graded to full stage black through that band: the same
   move a title sequence makes, in the same material the transport rail and the
   slate already sit on. The gold plate stops existing as a visual event; the
   identification it carried is printed inside the picture by `.film__slate` in
   the campaign's type, with the firm named again in the credit beneath. */
.camp .film .film__viewport .film__grade {
  background:
    linear-gradient(0deg,
      rgba(10,9,6,1) 0%, rgba(10,9,6,1) 16%,
      rgba(10,9,6,0.97) 21%, rgba(10,9,6,0.58) 28%,
      rgba(10,9,6,0.20) 40%, rgba(10,9,6,0) 56%),
    linear-gradient(90deg, rgba(10,9,6,0.34) 0%, rgba(10,9,6,0) 40%);
}


/* ── 9 · THE ELEMENTOR BOUNDARY — A GLOBAL `.container` WAS INFLATING EVERY
        BEAT OF THE PAGE BY 36px ─────────────────────────────────────────────
   Found by rendering the PUBLIC page against the approved candidate, not by
   reading CSS. Every campaign block carrying `.container` — every chapter head,
   the reading columns, the film credit, the equipment band, Chapter III's
   confirming line, the footer row — measured exactly 36px taller on production
   than in the lab.

   The cause is a class-name collision, not the campaign:

       post-33.css   .container { width: 100%; padding: 18px; }

   the Elementor global kit's own generic container. `.camp .container` only ever
   restated `padding-inline`, so that SHORTHAND's 18px block padding survived on
   both sides of every container on the page: the headline→lede cluster gained
   18px it was not designed with, every chapter head gained 36, and the vertical
   scale this pass exists to establish was quietly loosened by ~300px down the
   page. R14.1 asks a cluster's internal spacing to be at most half its external;
   an unowned 18px inside every cluster is how that stops being true.

   THE FIX IS DECLARED ON `.camp .container` ITSELF (see §GRID at the top of this
   file), not as a higher-specificity override down here. Two classes already
   outrank a bare `.container` at one, so stating the block axis there is enough
   — and it is the ONLY form of the fix that leaves the four campaign components
   which legitimately own their block padding (`.nav__row` 14px, `.hero__grid`
   94/52, `#engine .container` 6px, and anything added later) still able to
   declare it. A three-class blanket reset silently flattened all four; that is
   the shape of bug this whole pass exists to remove, so it is not the shape the
   fix is allowed to take.
   ════════════════════════════════════════════════════════════════════════════ */


/* ── 10 · THE LEDGER ROWS — RECLAIMED FROM A CORRUPTED custom_css RULE ──────
   Chapter V's three ledger rows measured 57px on the public page and 65px in
   the approved candidate, from identical markup, identical fonts and an
   identical Section V harness result. The cause is not Section V:

       page custom_css   .camp .engine__stats &gt; div { … padding:14px 0 … }

   The child combinator is HTML-ENTITY ESCAPED in the stored custom_css, and
   Elementor prints it to the page still escaped — so that selector never
   matches anything on production and the rows silently fall back to this
   file's 10px. The lab replica holds the same stylesheet UNESCAPED, which is
   why the intended 14px is what the composition was approved at.

   14px is therefore the approved value and it is claimed HERE, in the campaign's
   own stylesheet, where it cannot be lost to a transport artefact again. The
   custom_css rule is left exactly as it is: it is inert, and rewriting the page
   layer is a larger blast radius than this needs.

   §6.1 reading: the rows are one instrument's internal rhythm (--s-6 between
   rows of one instrument), not three separate blocks; 14/14 is what makes the
   ledger read as one object against the display figure above it. */
.camp #engine .engine__stats > div { padding: 14px 0; }
