/* GENERATED FILE. Do not edit.
   Built by ops/bundle-css.py from the slices listed below, in the order
   functions.php enqueues them. Edit the slice, then rebuild.
   Sources: tokens.css, base.css, components.css, components-v2.css, v2-bands.css, v2-pages.css, v2-motion.css, v2-archive.css, v2-forms.css, v2-rail.css
*/

/* ==== tokens.css ==== */
/* =========================================================================
   Wing R&D Fund — design tokens
   -------------------------------------------------------------------------
   EVERY VALUE HERE IS LIFTED FROM THE APPROVED BRAND DIRECTION, not chosen.
   Source: "Wing Brand Direction" §03 Colour and §04 Typography, and the
   measured values in "Wing Homepage Direction v4".

   Rewritten 18 Sep. The previous file was built from a two-line summary of a
   meeting while the full specification sat unread. It carried four colours the
   brand does not have - a teal #35788C, a warm cream #F6F4EF, and a cyan and
   an electric blue I added later trying to fix the vibrancy the wrong palette
   had cost - and it set display type at weight 300 when the spec says 600.

   THE TWO RULES THAT GOVERN EVERYTHING BELOW, quoted:
     "Navy or ink for all body text on light grounds; white for text on navy,
      ink or footage. Light Blue is an accent, for rules, small emphasis and
      large headings on dark, never for paragraph text."
     "Two background colours maximum per screen."
   ========================================================================= */

:root {
  /* --- The four brand colours and two grounds. There are no others. ------ */
  --wing-navy:      #0A346A; /* primary ink, dark bands, buttons            */
  --wing-lightblue: #B8CDE1; /* accent, rules, highlights.
                                NEVER behind small text, never paragraphs   */
  --wing-paleblue:  #EDF7FC; /* quiet section ground                        */
  --wing-white:     #FFFFFF; /* primary reading ground                      */
  --wing-ink:       #07101E; /* deep ground, full-bleed film sections       */
  --wing-pounamu:   #0F3D33; /* sparingly, behind energy and waste imagery  */
  --wing-teal:      #35788C; /* Wing's existing identity colour. The brand
                                direction opens by calling this "a visual
                                system built from Wing's existing navy AND
                                TEAL identity" but then omits teal from the
                                swatch list. Jerald asked for it back on 18
                                Sep, so it returns as a deliberate accent:
                                section numbers, active markers, rules that
                                need to be seen, and the one figure that
                                should catch the eye. 4.98:1 on white and
                                4.53:1 on paper, so it is safe for text on
                                light grounds. 3.01:1 on navy, so on dark it
                                is a filled shape or a rule, never small text
                                - that measurement has not changed.        */
  --wing-teal-deep: #2A6072;

  /* Navy shades, derived for hover and hairlines only. Not new brand
     colours - the same hue, darker and lighter. */
  --wing-navy-deep: #07224A;
  --wing-navy-lift: #0D4287;

  /* --- Semantic ---------------------------------------------------------- */
  --wing-bg:            var(--wing-white);
  --wing-bg-quiet:      var(--wing-paleblue);
  --wing-bg-deep:       var(--wing-ink);
  --wing-text:          var(--wing-navy);
  --wing-text-muted:    #4A5E7A;  /* navy at reading weight, 7.0:1 on white */
  --wing-text-on-dark:  var(--wing-white);
  --wing-rule:          #D5DFE9;
  --wing-rule-dark:     rgba(255, 255, 255, 0.14);
  --wing-action:        var(--wing-navy);
  --wing-action-text:   var(--wing-white);
  --wing-focus:         var(--wing-navy);
  --wing-accent:        var(--wing-teal);        /* on light grounds */
  --wing-accent-dark:   var(--wing-lightblue);   /* on dark grounds  */

  /* --- Typography: one family, set tight and large ------------------------
     "Archivo, a New Zealand-appropriate neo-grotesque with a broad weight
      range. Display sizes carry negative tracking; body text stays close to
      neutral. No second display face, no decorative type."

     There is no IBM Plex Mono. There is no width axis - "set tight and large"
     is weight and size, not width. */
  --wing-font: 'Archivo', 'Archivo Fallback', system-ui, -apple-system, 'Segoe UI', Roboto,
               'Helvetica Neue', Arial, sans-serif;

  /* --- THE SCALE WAS SIXTEEN SIZES. IT IS NOW SIX. --------------------
     Measured on the live home page before this change:
       11, 12.5, 13, 13.5, 14, 15, 16, 17, 19.4, 21.6, 24.5, 27.4, 34.6,
       60.5, 66.2, 149.8
     Eight of those sit within 6px of each other. No reader can tell 12.5 from
     13 from 13.5 from 14 - they register only that nothing looks decided.

     The three sites the client supplied as the bar use FOUR to SIX sizes on a
     whole page. ovatafunds: 14 / 16 / 18 / 26-32 / 55 / 72. dnacapital:
     14.4 / 18 / 45 / 54 / 67.5. ouiwill: 20 / 30 / 72.

     Every per-component clamp() in components.css that created the mid-range
     mush now resolves to one of these tokens.

     >>> THE DISPLAY CAP IS AN AMENDMENT TO THE APPROVED DIRECTION. <<<
     The direction specifies clamp(52px, 10.4vw, 196px) at -5% tracking, which
     rendered at 149.76px. Not one of the three reference sites exceeds 72px,
     and dnacapital - a healthcare fund, the closest analogue to Wing - sets
     its H1 at 54px. A 150px headline reads as shouting and is most of why the
     hero eats a full screen for eleven words. -5% tracking at 76px is -3.8px,
     tighter than any reference (dna runs -2%, the other two run 0).

     This needs Christine's sign-off as an amendment, not a silent change -
     this project already has a documented history of diverging from the spec
     unannounced. Logged in docs/open-decisions.md. */
  --wing-display:  clamp(40px, 5.3vw, 76px);
  --wing-h2:       clamp(30px, 3.3vw, 48px);
  --wing-h3:       26px;
  --wing-figure:   clamp(38px, 3.6vw, 52px);
  --wing-lede:     22px;
  --wing-body:     18px;   /* was 16.6-17. Small body is the cheapest-looking
                              thing on a website; every reference runs 18-20. */
  --wing-small:    16px;   /* was 15 */
  --wing-caption:  14px;   /* was 13 */
  --wing-label:    14px;   /* was 12.5, across 53 elements */

  /* -5% at weight 600 is functionally tighter than ouiwill's -4.5% at
     weight 300, because the counters are already smaller. dnacapital runs
     -2%; ovatafunds runs ZERO tracking at 700. -0.022em is the defensible
     middle for a semibold. */
  --wing-ls-display: -0.022em;
  --wing-ls-h2:      -0.032em;
  --wing-ls-h3:      -0.022em;
  --wing-ls-body:    -0.005em;

  /* 0.88 was below every reference. The minimum across twelve measured
     heading styles on the three sites is 1.00, and the one that reaches 1.00
     does it at Ultralight - the only weight thin enough to tolerate it. At
     600 weight, 0.88 puts ascenders and descenders on adjacent lines into
     each other. dnacapital runs 1.13-1.34, ovatafunds 1.20-1.22. */
  --wing-lh-display: 1.02;
  --wing-lh-h2:      1.08;
  --wing-lh-body:    1.6;
  --wing-lh-lede:    1.7;   /* opening the lede's leading is the single most
                               luxurious typographic move available for free */

  /* "max 56 characters" */
  --wing-measure: 56ch;

  /* --- Layout ------------------------------------------------------------
     "Twelve columns, 1400px maximum measure, page margins from 20px on mobile
      to 72px on desktop." */
  /* 1400 + 72px gutters gave a 1544 outer, so at a 1440 viewport the content
     ran edge to edge and only the gutter was visible. ovatafunds runs a 1300
     container inside 1440 - the margin is SEEN, which is what makes a page
     read as designed rather than as stretched. */
  --wing-max:    1300px;
  /* 20px at the low end was 3.5x tighter than the tightest reference
     (ovatafunds 70px, ouiwill 101px, dnacapital 240px). This probably cost
     more of the "doesn't feel expensive" than the type did. */
  --wing-gutter: clamp(24px, 5vw, 72px);

  /* "Vertical padding scales from 64px to 170px so each section reads as its
      own page." */
  /* Applied SINGLE-SIDED now. It was on both edges of every band, so two
     adjacent same-ground sections put 259px of dead space between them with no
     colour change to justify it. The token itself is untouched - it is the
     mechanism delivering "each section reads as its own page" and it is the
     brief. Only its application changed. */
  --wing-band:   clamp(64px, 9vw, 170px);
  --wing-band-tight: clamp(44px, 5vw, 96px);

  /* "Structure comes from 1px rules and space. No card shadows, no heavy
      rounded containers, radius kept at 0-3px." Buttons are the stated
      exception at 100px. */
  /* --- Spacing scale, brief 11 -----------------------------------------
     4 / 8 / 12 / 16 / 24 / 32 / 48 / 64 / 80 / 96. The token is NAMED FOR
     ITS VALUE on purpose: an index-named scale (`--wing-space-3`) is what
     base.css was already reaching for, against names that were never
     defined, so every one of those declarations was invalid and silently
     dropped. A name that states its own value cannot drift from it.
     Not every gap has to use every step. */
  /* The fixed masthead's height. It was a bare 76px repeated in four
     files, and a plain masthead that did not know about it put its first
     line UNDER the bar: brief 11 V05, measured at /?s=funding, eyebrow top
     55.4px against a nav bottom of 76px. A layout constant that three
     stylesheets depend on has to be a token. */
  /* Brief 11 V07: three page roles, three display sizes. `--wing-display`
     stays exactly as it was and remains the landing size, so the front
     page is untouched. The other two are new. */
  --wing-display-editorial: clamp(36px, 4.4vw, 64px);
  --wing-display-task:      clamp(32px, 3.2vw, 48px);

  --wing-header-h: 76px;

  --wing-space-4:  4px;
  --wing-space-8:  8px;
  --wing-space-12: 12px;
  --wing-space-16: 16px;
  --wing-space-24: 24px;
  --wing-space-32: 32px;
  --wing-space-48: 48px;
  --wing-space-64: 64px;
  --wing-space-80: 80px;
  --wing-space-96: 96px;

  --wing-radius: 2px;
  --wing-radius-pill: 100px;

  /* --- Motion ------------------------------------------------------------
     "Slow, quiet, and only where it helps." One curve for everything. */
  --wing-ease: cubic-bezier(.22, .61, .36, 1);
  /* --wing-reveal-dur / -rise / -blur were here. The entrance reveal they
     described was removed on 23 Sep 2026 (brief 11, V01) and nothing reads
     them any more, so they are gone rather than left as three numbers that
     look authoritative and govern nothing. */
  --wing-hover-dur: .45s;
  --wing-hover-lift: -3px;
}

/* =========================================================================
   DIRECTION 05: COASTLINE — approved 18 Sep 2026
   -------------------------------------------------------------------------
   The type system is now §3.1 OPTION B: Archivo at weight 500, tracking
   -0.03em, display leading 0.96, with italic emphasis on one word of the H1
   in Archivo's OWN italic (declared in functions.php, archivo-variable-italic
   .woff2). Christine rejected the serif display face on 15 Sep; Option B is
   the face she chose.

   The sizes below are the `:root` of docs/directions/05-coastline.html, which
   is the approved mock and therefore the operative spec. Where it differs
   from master-plan §3.1's table (H1 80 not 76, H2 50 not 48, figure 96 not
   100, lede 21 not 22, label 13 not 14) the mock wins, because the mock is
   what was measured and signed off. Logged in docs/build-state.md.

   Ten distinct sizes render on the home page and that is the ceiling:
     13 · 14 · 15 · 16 · 18 · 21 · 24 · 49 · 80 · 95
   Do not add an eleventh. The stepper's "of 04" was 0.32em of the figure
   (30px, an extra size) and is pinned to --wing-small for that reason.
   Buttons and nav links are 15, as the mock sets them.
   ========================================================================= */

:root {
  /* Display weight is now 500, not 600. At -0.03em and 0.96 leading a 600
     reads as a headline shouting; 500 reads as a headline. */
  --wing-display-weight: 500;

  --wing-display:  clamp(44px, 5.6vw, 80px);
  --wing-h2:       clamp(32px, 3.4vw, 50px);
  --wing-h3:       24px;
  --wing-figure:   clamp(60px, 6.6vw, 96px);
  --wing-lede:     21px;
  --wing-body:     18px;
  --wing-small:    16px;
  --wing-caption:  14px;
  --wing-label:    13px;
  --wing-action-size: 15px;   /* buttons, nav links and the ledger's segmented
                                 control. Merged into --wing-small's 16 on
                                 18 Sep to save a type size; restored to the
                                 mock's 15 on 19 Sep, because the page then
                                 renders exactly the mock's ten sizes
                                 (13 14 15 16 18 21 24 49 80 95) and ten is
                                 the ceiling, not nine. */

  --wing-ls-display: -0.03em;
  --wing-ls-h2:      -0.03em;
  --wing-ls-h3:      -0.02em;
  --wing-ls-fig:     -0.04em;

  --wing-lh-display: 0.96;
  --wing-lh-h2:      1.02;
  --wing-lh-lede:    1.55;

  /* 116px, not 170. The mock's band. Nine sections at 170 is the single
     biggest reason the page stood 8.39 screens. */
  --wing-band:       clamp(64px, 7vw, 116px);
  --wing-band-tight: clamp(44px, 5vw, 86px);

  /* Motion. The reveal tokens that were here are removed with the reveal
     itself, 23 Sep 2026. Nothing blurs, bounces, spins or parallaxes. */
  --wing-hover-lift:  -2px;
}

/* =========================================================================
   WIDE SCREENS — 29 Sep 2026. `--wing-max` (the shell) and the display/H2
   type caps were fixed pixel values, so past 1440 a 2560px screen carried
   exactly the same 1300px column and the same 80px H1 as a 1440px laptop:
   more empty margin either side, not a page that used the width it had.

   Gated at >=1600px so nothing below 1440 moves - `--wing-max` is a flat
   1300px up to here, and `--wing-display`/`--wing-h2` are already sitting on
   their own flat plateau below 1600 (5.6vw and 3.4vw both clear 80px/50px
   before the viewport reaches 1600), so this block starts exactly where the
   old numbers already were and only changes what happens above it - no
   visible jump at the 1600px seam.

   Anchored at two points and solved as a straight line, not guessed:
     shell   1600px -> 1300px (unchanged),  1920px -> 1440px (then flat)
     display 1600px -> 80px  (unchanged),   1920px -> 88px  (then flat)
     h2      1600px -> 50px  (unchanged),   1920px -> 55px  (then flat)
   1920 was chosen as the far end because it is the common wide-desktop
   width the margin was most visibly wasted at; 88px keeps the H1 under the
   brief's "no bigger than about 88px" ceiling, and 55px scales the section
   heading by the same ~10% the display got, so the two headline sizes stay
   in the same proportion at every width. Both stay flat from there to 2560
   and beyond - "modest," not endlessly growing. */
@media (min-width: 1600px) {
  :root {
    --wing-max:     clamp(1300px, 43.75vw + 600px, 1440px);
    --wing-display: clamp(80px, 2.5vw + 40px, 88px);
    --wing-h2:      clamp(50px, 1.5625vw + 25px, 55px);
  }
}

/* ==== base.css ==== */
/* =========================================================================
   Wing R&D Fund — accessibility and typographic base
   Applies on top of Hello Elementor. Layout is Elementor's job; this file
   only sets the things that must hold site-wide regardless of how a page
   is composed. Target: WCAG 2.2 AA.
   ========================================================================= */

/* font-family/-size/line-height below used to reference --wing-font-sans,
   --wing-text-base and --wing-leading-body - names tokens.css never defines.
   An invalid var() on an inherited property computes to the inherited value,
   so <body> was silently falling through to the browser default rather than
   the 18px --wing-body token, which is where the type audit's stray 17px
   came from (Elementor's own default typography, uncontested).

   23 September 2026: the rest of this file had the SAME defect, in sixteen
   more declarations, and the heading rule below was the expensive one. Its
   `font-size` and `line-height` referenced undefined tokens and were
   dropped, so every h1-h4 that no v2 rule reached inherited the BODY's 1.6
   leading - which is exactly the "24px / 38.4px" heading brief 11 V03
   measured, 24 x 1.6 to the pixel. Its `font-weight: 600` was the only
   valid declaration of the three, so it applied everywhere, which is brief
   11 V02: 23 section headings at weight 500 and 19 identical ones at 600.
   Every token below is now one tokens.css actually defines. */
body {
  font-family: var(--wing-font);
  font-size: var(--wing-body);
  line-height: var(--wing-lh-body);
  color: var(--wing-text);
  background: var(--wing-bg);
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--wing-font);
  color: var(--wing-text);
  font-weight: var(--wing-display-weight);
  letter-spacing: -0.015em;
  text-wrap: balance;
}

/* 5 Oct 2026: body copy avoids one-word last lines (the design audit counted
   9 at 1440 and 19 at 390, e.g. "it" alone under "Human capital"). Browsers
   without `pretty` simply wrap as before. */
p, li, figcaption { text-wrap: pretty; }

/* Leading is set PER LEVEL, not once for all six, because a heading's
   leading is a function of its size: 1.6 on a 49px heading is a paragraph
   gap between two lines of one title. Brief 11's role table - section
   heading 1.10, component heading 1.20. */
h1 { font-size: var(--wing-display); line-height: 1.06; }
h2 { font-size: var(--wing-h2);      line-height: 1.10; }
h3 { font-size: var(--wing-h3);      line-height: 1.20; }
h4 { font-size: var(--wing-lede);    line-height: 1.25; }
h5, h6 { font-size: var(--wing-body); line-height: 1.30; }

@media (max-width: 782px) {
  h1 { font-size: 2.5rem; }
  h2 { font-size: 2rem; }
  h3 { font-size: 1.5rem; }
}

/* Keep long-form copy inside a readable measure without constraining the
   Elementor containers that deliberately run full width. */
.wing-prose p,
.wing-prose li { max-width: var(--wing-measure); }

/* --- Focus -------------------------------------------------------------
   Hello Elementor ships no visible focus ring. A keyboard user must always
   be able to see where they are, including inside the application forms. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--wing-focus);
  outline-offset: 2px;
  border-radius: var(--wing-radius);
}
/* Dark surfaces: the focus ring needs to sit against the light blue, not teal. */
.wing-surface-dark :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline-color: var(--wing-white);
}

/* --- Skip link -----------------------------------------------------------
   Hello Elementor prints `.skip-link.screen-reader-text` directly in its
   header.php - not via a hook, so it cannot be removed. It already targets
   #content, so it is the one we keep and style; the theme previously added a
   second one and the page announced two skip links with the same destination. */
.skip-link.screen-reader-text {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10000;
  width: auto;
  height: auto;
  clip: auto;
  clip-path: none;
  padding: var(--wing-space-12) var(--wing-space-24);
  background: var(--wing-ink);
  color: var(--wing-white);
  font-family: var(--wing-font);
  font-size: var(--wing-small);
  font-weight: 600;
  text-decoration: none;
  /* Measured at 122x29 (crossbrowser-a11y.md finding #3) - under the 44px
     floor on the short axis. It is keyboard-focus-only, never tapped, so the
     real-world impact was always low, but a switch-access or voice-control
     user can still activate a focused control without a pointer, and the
     fix is free: align-items centres the existing padding, min-height adds
     the rest without changing where the link sits or how it reads. */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  box-sizing: border-box;
}
.skip-link.screen-reader-text:focus {
  left: var(--wing-space-16);
  top: var(--wing-space-16);
  outline: 3px solid var(--wing-white);
  outline-offset: 2px;
}

/* --- Minimum hit area --------------------------------------------------- */
button,
input[type="submit"],
.wing-btn,
.elementor-button {
  min-height: 44px;
}

/* --- Motion ------------------------------------------------------------
   All information must be present without animation. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --- Dark surface helper ------------------------------------------------ */
.wing-surface-dark {
  background: var(--wing-bg-deep);
  color: var(--wing-text-on-dark);
}
.wing-surface-dark :is(h1, h2, h3, h4, h5, h6) { color: var(--wing-text-on-dark); }
/* Teal is not AA-compliant as small text on the dark field - use light blue. */
.wing-surface-dark .wing-accent { color: var(--wing-accent-dark); }

/* --- Eyebrow / section index ------------------------------------------- */
.wing-eyebrow {
  font-family: var(--wing-font);
  font-size: var(--wing-label);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--wing-text-muted);
}
.wing-surface-dark .wing-eyebrow { color: var(--wing-accent-dark); }

/* --- Tables (funding comparison) ---------------------------------------
   Stacks to definition rows on small screens; the spec forbids a table the
   user has to scroll sideways to read. */
.wing-table { width: 100%; border-collapse: collapse; }
.wing-table th,
.wing-table td {
  padding: var(--wing-space-16);
  border-bottom: 1px solid var(--wing-rule);
  text-align: left;
  vertical-align: top;
  font-size: var(--wing-small);
}
.wing-table th { font-weight: 600; }

@media (max-width: 782px) {
  .wing-table thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .wing-table tr { display: block; border-bottom: 2px solid var(--wing-rule); padding: var(--wing-space-16) 0; }
  .wing-table td { display: block; border: 0; padding: var(--wing-space-8) 0; }
  .wing-table td::before {
    content: attr(data-label);
    display: block;
    font-weight: 600;
    font-size: var(--wing-label);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--wing-text-muted);
  }
}

/* --- Screen-reader only ------------------------------------------------- */
.wing-sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* =========================================================================
   LINK COLOUR, added 19 Sep.

   Hello Elementor's own reset.css sets unvisited links to #CC3366, a magenta
   that appears nowhere in the Wing palette. Every component stylesheet has
   been overriding it locally, so the colour only ever surfaced where one had
   not thought to: the dead nav index, the header logo mark, and the privacy
   policy link on /apply/. Fixing it once at the base is the correct place,
   rather than a fourth local override.

   Teal reads 4.98:1 on white and 4.53:1 on the pale ground, so it clears AA
   for body text on both light grounds. On dark grounds teal is only 3.01:1,
   so there the accent is the light blue, which is the rule already recorded
   in tokens.css.
   ========================================================================= */
a { color: var(--wing-teal); }
a:hover, a:focus-visible { color: var(--wing-teal-deep); }
.wing-section--dark a, .wing-hero a, .wing-cta a, .wing-footer a,
.dark a, .hero a, .close a { color: var(--wing-lightblue); }
/* Anything that carries its own colour keeps it: buttons, the nav, the logo,
   headings wrapped in a link, and the section index. */
a.btn, a.btn:hover, .btn a, .nav a, .rail a, .wing-header a,
a .elementor-heading-title, a img { color: inherit; }

/* =========================================================================
   CONTRAST SAFETY NET — nav text over a photographic hero, added 19 Sep.

   `.nav` carries no background of its own until it is `.is-solid` (scrolled
   past 80px) or `.is-light` (over a light band) - components-v2.css lines
   242-279. Below 80px, on every page's hero (or `.hero--inner`), the white
   nav links and the "Check eligibility" ghost pill sit directly on the
   photograph with nothing behind them but `.hero__scrim`'s gradient, which
   is intentionally asymmetric (dark on the left 60% under the headline,
   fading to nothing on the right where the nav links actually sit -
   components-v2.css line 500).

   Measured, not eyeballed: sampling the ACTUAL rendered pixels immediately
   above/below each nav link's text on the live home hero (Playwright
   screenshot + real WCAG luminance maths, not the DOM-background-colour
   compositing `ops/measure.py --contrast` does, which cannot see into a
   photograph and correctly flags this case for a human instead) gave clean-
   background ratios of 4.09-4.88:1 against #fff text - "How it works" and
   the "Check eligibility" pill's worst sampled pixels fell under the 4.5:1
   AA floor for normal text. That is a property of THIS photograph; a
   lighter sky in a future crop could fail by more. A text-shadow is the
   standard, minimal fix for exactly this case (unpredictable photography
   behind fixed-colour UI text) and is not the box-shadow/card-elevation
   effect the design brief rules out - no blur radius here exceeds a few
   pixels and nothing but legibility changes. Scoped to the nav's own
   transparent state only, so it never touches the solid navy or light-over-
   pale-band states, which already meet AA without help.

   REPLACED 28 Sep 2026. Close up the shadow read as a smudge around every
   letter. The safety net is now a soft ink wash the bar paints behind
   itself while it is transparent (`.nav::before`, components-v2.css §1),
   measured from pixels on all ten heroes at 1440 and 768. The text-shadow
   rule that stood here is deleted. */

/* =========================================================================
   FOCUS, REBUILT 21 September 2026. This was an accessibility failure, not
   a polish item: measured 1.10:1 across the whole footer, the readiness
   stepper and the closing buttons, and 1.00:1 on the fit-check options.
   WCAG 2.4.11 asks for 3:1. A keyboard user genuinely could not see where
   they were on any dark band.

   THREE THINGS WERE STACKED AGAINST THE RING, and only the third is obvious:

   1. The rule above uses `:where(...)`, which has ZERO specificity by
      definition. It was written that way to be easy to override and ended up
      being overridden by everything.
   2. Hello Elementor's own reset then wins with `outline: 2px solid #000`
      plus `box-shadow: 0 0 0 4px #fff`. That white halo is what normally
      rescues a black ring on a dark ground.
   3. Elementor core then removes the halo with `.elementor a { box-shadow:
      none }`, leaving a black ring alone on near-black ink.

   THE RING BELOW IS TWO-TONE ON PURPOSE. No single colour clears 3:1 on
   every ground this site uses: teal measures 4.98 on white, 4.58 on pale and
   3.45 on ink, but only 2.33 on the navy comparator. A white inner ring and
   a near-black outer ring guarantee that one of the two always contrasts,
   whatever is behind it, which is the standard answer to exactly this.

   Specificity is deliberate and sufficient without `!important`: `.wing-v2`
   plus `:focus-visible` plus the element gives (0,2,1), which beats
   Elementor's `.elementor a` at (0,1,1) and Hello Elementor's reset. The
   box-shadow therefore survives rather than being stripped.
   ========================================================================= */
.wing-v2 :is(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--wing-teal);
  outline-offset: 2px;
  box-shadow: 0 0 0 2px #FFFFFF, 0 0 0 5px rgba(7, 16, 30, .9);
  border-radius: var(--wing-radius);
}
/* The skip link paints its own dark plate, so it needs the inverse: a light
   ring, and no white halo that would smear into the plate's own edge. */
.wing-v2 .skip-link.screen-reader-text:focus-visible {
  outline: 3px solid #FFFFFF;
  outline-offset: 2px;
  box-shadow: none;
}

/* The buttons need the halo back, and the rule that removed it is ours, not
   Elementor's. `components-v2.css` sets `box-shadow: none` on the button
   components to enforce the brand's no-shadows rule, which is right, but it
   also stripped the white ring from the focus indicator and left the header's
   teal "Check eligibility" pill with a teal ring on a teal ground: measured
   1.00:1, the only remaining focus failure after the rebuild above.

   Re-asserted for the focused state only, so buttons still carry no shadow
   at rest. The selectors carry one more class than the rule they override,
   which is what wins the tie without `!important`. */
.wing-v2 .btn.elementor-widget-button .elementor-button:focus-visible,
.wing-v2 .elementor-widget-button .elementor-button:focus-visible,
.wing-v2 .elementor-button:focus-visible,
.wing-v2 a.btn:focus-visible,
.wing-v2 button.btn:focus-visible {
  box-shadow: 0 0 0 2px #FFFFFF, 0 0 0 5px rgba(7, 16, 30, .9);
}

/* ==== components.css ==== */
/* =========================================================================
   Wing R&D Fund — components
   -------------------------------------------------------------------------
   Rewritten 18 Sep against the approved brand direction. The file it replaces
   had grown to 3,200 lines across thirteen labelled passes, each correcting
   the last, because the whole sequence was built from a two-line summary of a
   decision rather than from the specification that had already been written.
   Thirteen passes of correction is the evidence that the base was wrong.

   The rules this file implements, quoted from the direction:

     Layout   "Twelve columns, 1400px maximum measure, page margins from 20px
               on mobile to 72px on desktop. Content sits on 4/8, 5/7 or 6/6
               splits, never centred as a single narrow column."
              "Alternate light and dark bands."
              "Structure comes from 1px rules and space. No card shadows, no
               heavy rounded containers, radius kept at 0-3px."
              "Text and image alternate sides down the page. Nothing is
               mirrored exactly; weight shifts as you scroll."
     Type     "One family, set tight and large."
     Colour   "Two background colours maximum per screen."

   Elementor gotchas that still apply: containers take `css_classes`, widgets
   take `_css_classes`; container layout goes through custom properties
   (--flex-direction, --gap); Elementor's per-page post-N.css loads after this
   file at equal specificity, so heading rules name the widget class; and
   `.elementor.elementor .e-con > .elementor-widget { max-width: 100% }` at
   (0,4,0) kills any max-width set on a widget, so measure goes on the inner
   element.
   ========================================================================= */

/* =========================================================================
   1. TYPE
   ========================================================================= */

body {
  font-family: var(--wing-font);
  font-size: var(--wing-body);
  line-height: var(--wing-lh-body);
  letter-spacing: var(--wing-ls-body);
  color: var(--wing-text);
  background: var(--wing-bg);
  -webkit-font-smoothing: antialiased;
}

/* Weight comes from the one token, 23 Sep, brief 11 V02. This helper and
   the v2 `.display` helper are the site's two heading systems; they used to
   resolve to 600 and 500 respectively, which is why 49px section headings
   came out at two different weights on pages that mix them - identical
   size, leading and tracking, visibly different colour on the page. Both
   now read --wing-display-weight. 600 is reserved for control labels and
   table headers, where it is a deliberate signal rather than a default. */
.wing-display .elementor-heading-title,
.wing-display {
  font-size: var(--wing-h2);
  font-weight: var(--wing-display-weight);
  line-height: var(--wing-lh-h2);
  letter-spacing: var(--wing-ls-h2);
  color: inherit;
  margin: 0;
  text-wrap: pretty;
}
.wing-display .elementor-heading-title { max-width: 20ch; }

.wing-display--lg .elementor-heading-title {
  font-size: var(--wing-display);
  line-height: var(--wing-lh-display);
  letter-spacing: var(--wing-ls-display);
  max-width: 15ch;
}

/* Subheading modifier, 21 Sep — brief 09 §3: "one principal heading per
   section, clear subheadings where the subject changes." Apply, Contact,
   the legal pages and the waiting list each carry a mid-page heading that
   introduces one short block ("What happens next", "Who you will be
   talking to", "What this page will cover", "What joining means") below
   the page's own H1 — `heading()` already passes them the modifier class,
   but nothing styled it, so they rendered at the full section-heading size
   (clamp 30-48px) and read as a second principal heading rather than what
   they are. Sized to h3 they read as subordinate, the same distinction the
   numbered section headings already make against the page H1. */
.wing-display--sm .elementor-heading-title {
  font-size: var(--wing-h3);
  line-height: 1.20;
  letter-spacing: var(--wing-ls-h3);
  max-width: 34ch;
}

h1, h2, h3, h4 { font-weight: var(--wing-display-weight); letter-spacing: var(--wing-ls-h3); color: inherit; }
/* 1.20 is the brief's component-heading role. This said 1.18 and
   `.wing-display--sm` said 1.22, for the same 24px role. */
h3 { font-size: var(--wing-h3); line-height: 1.20; margin: 0; }

.wing-lede > p,
.wing-lede .elementor-widget-container > p {
  font-size: var(--wing-lede);
  font-weight: 400;
  line-height: var(--wing-lh-lede);
  max-width: var(--wing-measure);
  margin: 0;
}

.wing-prose .elementor-widget-container > :is(p, ul, ol) { max-width: var(--wing-measure); }
.wing-prose p { margin: 0 0 1em; }
.wing-prose p:last-child { margin-bottom: 0; }

/* Small caps labels. Archivo at 500, tracked — no second family, no mono. */
.wing-label,
.wing-eyebrow, .wing-hero__eyebrow, .wing-index, .wing-spec__key,
.wing-stage__num, .wing-focus__num, .wing-step__num, .wing-footer__col h3,
.wing-cta__meta, .wing-stages__caption, .wing-media__caption,
.wing-person__role, .wing-def h3, .wing-vs__who, .wing-vs__n,
.wing-media__credit, .wing-fact__label {
  font-size: var(--wing-label);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.4;
}

/* "Legal lines. Qualifiers sit directly beneath the claim they qualify, at
    13-14px, never hidden in the footer alone." */
.wing-qualifier, .wing-limit, .wing-notice {
  font-size: var(--wing-caption);
  line-height: 1.55;
  color: var(--wing-text-muted);
  max-width: 78ch;
  margin: 0;
  letter-spacing: 0;
  text-transform: none;
}
/* `.wing-qualifier` is applied straight to a single <p> (wingbuild.py's
   `proof_figures()`) and never wraps children of its own, so this is
   `.wing-notice` only - the class that actually wraps multiple <p> tags
   (the legal scaffold notice, Apply's two compliance paragraphs). Default
   browser UA margins happened to collapse to a plausible-looking gap; this
   makes the "smaller gap within a related group" from brief 09 §3
   deliberate instead of incidental. */
.wing-notice p { margin: 0 0 0.85em; }
.wing-notice p:last-child { margin-bottom: 0; }

/* Two deliberate readings of `.wing-notice`, 21 Sep — brief: make pages that
   are honestly incomplete "look deliberate rather than unfinished."

   `--warn` is the legal pages' "this page is not yet complete" scaffold
   notice (pages_part3.py `legal_page()`). Unstyled, it rendered as a plain
   bold paragraph indistinguishable from body copy — nothing marked it as a
   flagged state rather than a forgotten one. A left rule is the site's own
   emphasis language elsewhere (`.wing-vs__wing`, the IP ownership panel);
   this reuses it rather than inventing a banner, a background fill (which
   would spend this screen's one allowed second colour) or a shadow. */
.wing-notice--warn {
  padding-left: 20px;
  border-left: 2px solid var(--wing-accent);
}

/* `--trust` is Apply's second notice - the "read by Dr. Christine
   Arasaratnam personally" reassurance that follows the compliance notice.
   Both are plain `.wing-notice` divs stacked with only their own zero
   margin between them, so two related but distinct points read as one
   unbroken paragraph block. A hairline plus the gap it earns matches the
   "smaller gaps within a group, larger between groups" rhythm brief 09 §3
   asks for, without a second heading neither notice has copy for. */
.wing-notice--trust {
  margin-top: 24px;
  padding-top: 24px;
  border-top: 1px solid var(--wing-rule);
}

/* =========================================================================
   2. SURFACES — two background colours maximum per screen
   ========================================================================= */

.wing-section--paper  { background: var(--wing-paleblue); }
.wing-section--dark,
.wing-hero, .wing-cta, .wing-band, .wing-footer { background: var(--wing-ink); }

/* On a dark ground the tokens re-point, so a component written once is correct
   on both. This is what makes "Light Blue is never paragraph text" structural
   rather than something to remember. */
.wing-section--dark, .wing-hero, .wing-cta, .wing-band, .wing-footer,
.wing-media__overlay {
  --wing-text:       var(--wing-white);
  --wing-text-muted: rgba(255, 255, 255, 0.72);
  --wing-rule:       var(--wing-rule-dark);
  --wing-action:     var(--wing-white);
  --wing-action-text: var(--wing-ink);
  --wing-accent:     var(--wing-lightblue);
  color: var(--wing-white);
}
/* Elementor's kit stylesheet (post-5.css / post-N.css) sets a colour on the
   HEADING and TEXT-EDITOR widgets from Global Typography, at (0,2,0), and it
   loads after this file. Inheritance never reaches those widgets, so a token
   re-pointed on the section does nothing for them and the copy renders in the
   light-ground navy on an ink band.

   That is not theoretical: rebuilding this file without these three selectors
   put the hero lede at #12283F on #07101E - a measured 1.27:1 - on every page
   with a dark hero, which is all of them. Naming the widget class takes this to
   (0,3,0) and settles it. The same trap has now been fixed three times on this
   project, for heading colour, for prose colour and here. */
.wing-section--dark :is(h1,h2,h3,h4),
.wing-hero :is(h1,h2,h3,h4),
.wing-cta :is(h1,h2,h3,h4),
.wing-section--dark .elementor-widget-heading .elementor-heading-title,
.wing-hero .elementor-widget-heading .elementor-heading-title,
.wing-cta .elementor-widget-heading .elementor-heading-title { color: var(--wing-white); }

.wing-section--dark .elementor-widget.elementor-widget-text-editor,
.wing-hero          .elementor-widget.elementor-widget-text-editor,
.wing-cta           .elementor-widget.elementor-widget-text-editor,
.wing-band          .elementor-widget.elementor-widget-text-editor,
.wing-footer        .elementor-widget.elementor-widget-text-editor {
  color: var(--wing-text-muted);
}
.wing-section--dark :is(.wing-lede, .wing-prose) :is(p, li),
.wing-hero          :is(.wing-lede, .wing-prose) :is(p, li),
.wing-cta           :is(.wing-lede, .wing-prose) :is(p, li),
.wing-band          :is(.wing-lede, .wing-prose) :is(p, li),
.wing-footer        :is(.wing-lede, .wing-prose) :is(p, li) {
  color: var(--wing-text-muted);
}
/* and the light-ground equivalents, for the same reason */
.wing-section:not(.wing-section--dark) :is(.wing-lede, .wing-prose) :is(p, li),
.wing-section--paper :is(.wing-lede, .wing-prose) :is(p, li) { color: var(--wing-text); }
.wing-section:not(.wing-section--dark) .wing-lede p { color: var(--wing-text-muted); }

/* =========================================================================
   3. LAYOUT — 1400px, 20→72px margins, bands 64→170px
   ========================================================================= */

.wing-shell, .e-con.wing-shell {
  width: 100%;
  max-width: calc(var(--wing-max) + var(--wing-gutter) * 2);
  margin-inline: auto;
  padding-inline: var(--wing-gutter);
}
/* REVERTED to both edges, within an hour of changing it, because the
   reference forensics contradicted the reasoning.

   Wing's section padding measured 92px at 1440. dnacapital puts 324px of pure
   margin between every section; ovatafunds runs 140-240px. Wing was already
   the TIGHTEST of the four, and I made it tighter. The references are not
   shorter because they are compressed - dnacapital is 5.77 screens because it
   has FOUR sections, not because it crowds them.

   The lever for length is section count. It is not padding, and cutting
   padding costs exactly the air the brief asks for. */
.e-con.wing-section { padding-block: var(--wing-band); }
/* Two bands of the SAME ground running together still do not need two full
   gaps - that was the real finding, and it is the only case this treats. */
.e-con.wing-section--paper + .e-con.wing-section--paper,
.e-con.wing-section--dark + .e-con.wing-section--dark { padding-top: calc(var(--wing-band) * 0.4); }
.e-con.wing-section--tight { padding-block: var(--wing-band-tight); }
.e-con.wing-section--run   { padding-block: var(--wing-band-tight); }
.e-con.wing-section--run + .e-con.wing-section--run { padding-top: 0; }

/* Section index: number and label on one line above a full-bleed rule. */
/* NO RULE UNDER THE INDEX.
   It had one, and so did the first row of whatever followed, which put two
   hairlines within ~120px of each other with a single heading between them -
   the "too many lines across" Jerald flagged. The index is now marked by the
   teal number alone and the space under it. Structure still comes from rules,
   but a rule has to separate two different things to earn its place. */
.wing-index {
  display: flex;
  align-items: baseline;
  gap: 14px;
  margin-bottom: clamp(20px, 2.4vw, 34px);
  color: var(--wing-text-muted);
}
.wing-index__num { color: var(--wing-accent); font-weight: 600; }
/* The label is a <span> on most bands and a real heading on the FAQ, where the
   group title is the only title the group has (wingbuild.py's `index_row`).
   Normalise the heading back to the span's own rendering so the approved
   editorial look is byte-identical either way: a browser would otherwise apply
   its h2 defaults of 1.5em, bold, and .83em of vertical margin, and break both
   the baseline alignment and the band's rhythm. */
.wing-index :is(h2, h3, h4).wing-index__label {
  font: inherit;
  margin: 0;
  color: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
}

/* Splits: 5/7 by default, never a centred narrow column. */
.wing-split, .e-con.wing-split {
  --width: 100%;
  display: grid;
  gap: clamp(28px, 4vw, 72px);
  align-items: start;
}
@media (min-width: 900px) {
  .wing-split, .e-con.wing-split { grid-template-columns: 5fr 7fr; }
  .e-con.wing-split--wide-copy { grid-template-columns: 7fr 5fr; }
  /* Reverse places by column, not by `order`, so the template follows the
     painted order and the DOM order stays correct for reading and tab. */
  .e-con.wing-split--reverse           { grid-template-columns: 7fr 5fr; }
  .e-con.wing-split--reverse.wing-split--wide-copy { grid-template-columns: 5fr 7fr; }
  .e-con.wing-split--reverse > *:first-child { grid-column: 2; }
  .e-con.wing-split--reverse > *:last-child  { grid-column: 1; }
}

/* Pair: statement left, explanation right — a 6/6 split. */
.e-con.wing-pair {
  --width: 100%;
  display: grid;
  gap: clamp(24px, 3vw, 64px);
  align-items: start;
}
@media (min-width: 1000px) { .e-con.wing-pair { grid-template-columns: 6fr 6fr; } }
.e-con.wing-pair__a, .e-con.wing-pair__b { --width: 100%; }
.wing-pair__b { padding-top: 8px; }

/* =========================================================================
   4. INTERFACE — two actions, everywhere
      Primary: white fill, ink label. Secondary: outline only.
      Hover: 2-3px lift, 450ms. No scaling, no shadows.
   ========================================================================= */

.wing-btn,
.elementor-widget-button.wing-btn .elementor-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 0 32px;
  border-radius: var(--wing-radius-pill);
  font-size: var(--wing-body);
  font-weight: 500;
  letter-spacing: -0.01em;
  line-height: 1;
  text-decoration: none;
  border: 1px solid transparent;
  transition: transform var(--wing-hover-dur) var(--wing-ease),
              background var(--wing-hover-dur) ease,
              border-color var(--wing-hover-dur) ease,
              color var(--wing-hover-dur) ease;
}
.elementor-widget-button.wing-btn {
  background: transparent; border: 0; padding: 0; min-height: 0;
}
.wing-btn--primary,
.elementor-widget-button.wing-btn--primary .elementor-button {
  background: var(--wing-action);
  color: var(--wing-action-text);
}
.wing-btn--primary:hover,
.wing-btn--primary:focus-visible,
.elementor-widget-button.wing-btn--primary .elementor-button:hover,
.elementor-widget-button.wing-btn--primary .elementor-button:focus-visible {
  transform: translateY(var(--wing-hover-lift));
  background: var(--wing-lightblue);
  color: var(--wing-ink);
}
.wing-btn--ghost,
.elementor-widget-button.wing-btn--ghost .elementor-button {
  background: transparent;
  color: var(--wing-text);
  border-color: color-mix(in srgb, currentColor 34%, transparent);
  font-weight: 500;
}
.wing-btn--ghost:hover,
.wing-btn--ghost:focus-visible,
.elementor-widget-button.wing-btn--ghost .elementor-button:hover,
.elementor-widget-button.wing-btn--ghost .elementor-button:focus-visible {
  transform: translateY(var(--wing-hover-lift));
  border-color: currentColor;
  background: color-mix(in srgb, currentColor 10%, transparent);
}
.wing-hero__actions, .e-con.wing-hero__actions {
  --flex-direction: row;
  --gap: 16px;
  --flex-wrap: wrap;
  display: flex;
  align-items: center;
}

/* =========================================================================
   5. HEADER — six items, Apply always present, hides as you scroll down
   ========================================================================= */

.e-con.wing-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  padding-block: 18px;
  background: transparent;
  border-bottom: 1px solid transparent;
  transition: transform .5s var(--wing-ease), background .4s ease,
              backdrop-filter .4s ease, border-color .4s ease;
}
.wing-header.is-solid {
  background: rgba(7, 16, 30, 0.78);
  backdrop-filter: blur(18px);
  border-bottom-color: rgba(255, 255, 255, 0.10);
}
.wing-header.is-hidden { transform: translateY(-110%); }

.e-con.wing-header__inner {
  --flex-direction: row;
  --align-items: center;
  --justify-content: space-between;
  --gap: 24px;
  --flex-wrap: nowrap;
  display: flex;
}
.wing-header__logo { --width: auto; flex: none; }
.wing-header__logo img { height: clamp(36px, 3.4vw, 46px); width: auto; display: block; }
.wing-header__logo--colour { display: none; }
.wing-header__nav { --width: auto; flex: 1 1 auto; min-width: 0; }
.wing-header__cta { --width: auto; flex: none; }

.wing-header__nav .elementor-nav-menu--main > ul { justify-content: flex-end; align-items: center; }
.wing-header__nav .elementor-nav-menu > li > a.elementor-item {
  font-size: var(--wing-caption);
  font-weight: 500;
  letter-spacing: -0.005em;
  color: rgba(255, 255, 255, 0.82);
  padding: 8px 0;
  margin-inline: 16px;
  border: 0;
  transition: color .3s ease;
}
.wing-header__nav .elementor-nav-menu > li > a.elementor-item:hover,
.wing-header__nav .elementor-nav-menu > li > a.elementor-item-active { color: #fff; }

.elementor-widget-button.wing-header__cta .elementor-button {
  background: var(--wing-white);
  color: var(--wing-ink);
  min-height: 44px;
  padding: 0 26px;
  font-size: var(--wing-caption);
  font-weight: 600;
  border-radius: var(--wing-radius-pill);
  border: 0;
}
/* Brief 11 V11: the lift is gated on a real pointer, the ground change is
   not - on a touch screen a tapped button kept its hover lift with nothing
   to release it. */
.elementor-widget-button.wing-header__cta .elementor-button:hover {
  background: var(--wing-lightblue);
}
@media (hover: hover) and (pointer: fine) {
  .elementor-widget-button.wing-header__cta .elementor-button:hover { transform: translateY(-2px); }
}
.wing-header__nav .elementor-menu-toggle { margin-left: auto; color: #fff; background: transparent; }

/* =========================================================================
   6. HERO — full bleed, copy anchored low, film behind
   ========================================================================= */

.e-con.wing-hero {
  position: relative;
  min-height: 78svh;
  justify-content: flex-end;
  padding-block: 0 clamp(40px, 5vw, 76px);
  overflow: hidden;
  isolation: isolate;
}
/* The legal pages' masthead. Brief 11 V02/V03 measured its H1 at 80px/600
   against the newer system's 80px/500, and V07 puts it in the task role
   alongside Apply and Contact. Weight now comes from the token like every
   other heading; size and leading take the reference role. */
.e-con.wing-hero.wing-pagehero {
  min-height: 0;
  padding-block: calc(var(--wing-header-h) + clamp(32px, 3.4vw, 48px)) clamp(40px, 4.4vw, 56px);
}
.wing-role-task .wing-pagehero .wing-display--lg .elementor-heading-title {
  font-size: var(--wing-display-task);
  line-height: 1.12;
}
.wing-hero > * { position: relative; z-index: 2; }

/* The film plate — ONE FLAT FIELD.

   It was four gradients across five hues (#17589C, #12513F green, #08265A,
   #0A346A, #07101E) plus a light-blue radial animated on a 26-second loop.
   That breaks the direction's own "two background colours maximum per screen"
   and it is the most template-looking thing on the site: a multi-hue animated
   mesh reads as a 2021 SaaS landing page, which is the exact opposite of the
   brief.

   Every reference hero the client sent is a flat field. dnacapital is a flat
   #1F1F26 with muted video over it. ovatafunds is flat #F7F6F2. ouiwill is
   flat white.

   The linear-gradient stays because it is the legibility scrim the footage
   will need - "always with text-legible darkness behind type". When film
   arrives it goes in behind this and nothing else changes. */
.wing-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    linear-gradient(180deg, rgba(7,16,30,0) 0%, rgba(7,16,30,0.42) 58%, rgba(7,16,30,0.88) 100%),
    var(--wing-navy);
}
.wing-hero::after { content: none; }
.wing-hero__plate { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.wing-hero__plate video, .wing-hero__plate img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}

.wing-hero__eyebrow { color: var(--wing-lightblue); margin: 0 0 clamp(18px, 2vw, 30px); }
.wing-hero h1 { margin: 0 0 clamp(26px, 3vw, 44px); }
.wing-hero__foot {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 24px;
  margin-top: clamp(30px, 4vw, 56px);
  padding-top: 20px;
  border-top: 1px solid var(--wing-rule-dark);
}
.wing-hero__cue { font-size: var(--wing-caption); color: rgba(255,255,255,0.7); }
.wing-motion .wing-hero__cue { animation: wingCue 3.2s ease-in-out infinite; }
@keyframes wingCue {
  0%, 100% { opacity: .3; transform: translateY(0); }
  50%      { opacity: .85; transform: translateY(6px); }
}

/* =========================================================================
   8. PROOF FIGURES
   ========================================================================= */

.wing-facts {
  display: grid;
  gap: clamp(28px, 3vw, 48px);
  padding-top: clamp(26px, 3vw, 44px);
  border-top: 1px solid var(--wing-rule);
}
@media (min-width: 720px) { .wing-facts { grid-template-columns: repeat(4, 1fr); } }
.wing-fact { padding-top: 0; }
.wing-fact__num {
  display: block;
  font-size: var(--wing-figure);
  font-weight: 600;
  letter-spacing: var(--wing-ls-display);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.wing-fact--accent .wing-fact__num { color: var(--wing-accent); }
.wing-fact__label { display: block; margin-top: 12px; color: var(--wing-text-muted); }
.wing-facts + .wing-qualifier { margin-top: clamp(28px, 3vw, 48px); }

/* =========================================================================
   9. FOCUS AREAS — alternating full-width rows, not a card grid
   ========================================================================= */

.wing-focus { display: block; }
.wing-focus__item {
  display: grid;
  gap: clamp(20px, 3vw, 56px);
  padding-block: clamp(32px, 4vw, 72px);
  border-top: 1px solid var(--wing-rule);
  align-items: center;
}
.wing-focus__item:first-child { border-top: 0; padding-top: 0; }
@media (min-width: 900px) {
  .wing-focus__item { grid-template-columns: 5fr 7fr; }
  .wing-focus__item .wing-focus__body { grid-column: 1; }
  .wing-focus__item .wing-media { grid-column: 2; }
  /* "Text and image alternate sides down the page."
     The COLUMNS have to swap too, not just the content. Without the template
     swap below, the middle photograph rendered 522px wide against 731px for
     the two around it, and the middle copy column 40% wider - so the
     alternation read as an inconsistency rather than a rhythm. */
  .wing-focus__item:nth-child(even) { grid-template-columns: 7fr 5fr; }
  .wing-focus__item:nth-child(even) .wing-focus__body { grid-column: 2; }
  .wing-focus__item:nth-child(even) .wing-media { grid-column: 1; }
}
.wing-focus__num { display: block; color: var(--wing-accent); margin-bottom: 14px; }
.wing-focus__item h3 { margin: 0 0 12px; font-size: var(--wing-h3); letter-spacing: -0.03em; }
.wing-focus__item p { margin: 0; color: var(--wing-text-muted); max-width: 46ch; }
.wing-focus__note { display: block; margin-top: 16px; font-size: var(--wing-caption); color: var(--wing-text-muted); }

/* =========================================================================
   10. MEDIA — hairline frames, no parallax, no radius beyond 0-3px
   ========================================================================= */

.wing-media { position: relative; }
.wing-media__frame {
  position: relative;
  overflow: hidden;
  border-radius: var(--wing-radius);
  /* 29 Sep: a quiet pale placeholder, not ink. An ink box on a white band
     read as a dark block flashing up before each photo arrived. Bands with
     a dark ground keep ink (below). */
  background: var(--wing-paleblue);
}
.wing-section--dark .wing-media__frame, .wing-band .wing-media__frame, .dark .wing-media__frame { background: var(--wing-ink); }
.wing-media__frame img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* One grade for the workshop and lab stock, so photos from five sources
   read as a set: saturation held back so the stock reds and yellows stop
   shouting. 29 Sep, revised: the lifted blacks (a `screen` blend over
   navy) are gone; they made the photos milky. The NZ aerials and the
   solar field keep their own light, so they are excluded by file. */
.wing-media__frame img:not([src*="/hero-"]):not([src*="/band-"]):not([src*="/texture-"]):not([src*="-energy-"]) {
  filter: saturate(.74) contrast(1.02);
}
.wing-media--ratio-wide   .wing-media__frame { aspect-ratio: 16 / 10; }
.wing-media--ratio-tall   .wing-media__frame { aspect-ratio: 4 / 5; }
.wing-media--ratio-square .wing-media__frame { aspect-ratio: 1 / 1; }
.wing-media--bleed .wing-media__frame { aspect-ratio: 21 / 9; border-radius: 0; }
@media (max-width: 782px) { .wing-media--bleed .wing-media__frame { aspect-ratio: 4 / 3; } }
.wing-media__caption, .wing-media__credit {
  display: block;
  margin-top: 12px;
  color: var(--wing-text-muted);
}
/* 29 Sep: stops follow the copy, not the width. At 46% the dense part
   stopped short of the text from 783 to 1100px (sub text 3.6-4.5:1 over the
   white desk); the copy ends near 520px from the gutter at every width.
   The far end is .24, not .18, to settle the glare off the white machine. */
.wing-media__scrim {
  position: absolute; inset: 0;
  background: linear-gradient(90deg, rgba(7,16,30,.92) 0%, rgba(7,16,30,.74) max(280px, 22%), rgba(7,16,30,.6) max(580px, 50% - 60px), rgba(7,16,30,.24) 100%);
}
.wing-media__overlay { position: absolute; inset: 0; display: flex; align-items: center; }
/* Stacked, the band now opens on the photograph (the overlay's 40vw top
   padding, §15) and the ink gathers where the copy begins. */
@media (max-width: 782px) {
  .wing-media__scrim { background: linear-gradient(180deg, rgba(7,16,30,.08) 0%, rgba(7,16,30,.16) calc(40vw - 90px), rgba(7,16,30,.74) calc(40vw - 4px), rgba(7,16,30,.86) calc(40vw + 120px), rgba(7,16,30,.92) 100%); }
}

.e-con.wing-band { --width: 100%; padding: 0; }
.wing-band__statement {
  font-size: var(--wing-h2);
  font-weight: var(--wing-display-weight);
  line-height: var(--wing-lh-h2);
  letter-spacing: var(--wing-ls-h2);
  max-width: 16ch;
  margin: 0;
  color: var(--wing-white);
}
.wing-band__sub { margin: 20px 0 0; max-width: 48ch; color: rgba(255,255,255,0.78); }

/* =========================================================================
   11. STAGE TRACK, STEPS, DEFINITIONS, PEOPLE
   ========================================================================= */

.wing-stages { display: grid; gap: 0; border-top: 1px solid var(--wing-rule); }
@media (min-width: 860px) { .wing-stages { grid-template-columns: repeat(4, 1fr); } }
.wing-stage { padding: 22px 24px 26px 0; border-bottom: 1px solid var(--wing-rule); }
@media (min-width: 860px) {
  .wing-stage { border-bottom: 0; border-left: 1px solid var(--wing-rule); padding-left: 24px; }
  .wing-stage:first-child { border-left: 0; padding-left: 0; }
}
.wing-stage__num { display: block; color: var(--wing-text-muted); margin-bottom: 10px; }
.wing-stage h3 { font-size: var(--wing-h3); }
.wing-stage p { margin: 8px 0 0; font-size: var(--wing-small); color: var(--wing-text-muted); }
/* Wing enters at stage 02 and funds from there on. */
.wing-stage--in { box-shadow: inset 0 3px 0 var(--wing-accent); }
.wing-stages__caption { display: block; margin-top: 20px; color: var(--wing-text-muted); }

.wing-steps { list-style: none; margin: 0; padding: 0; }
.wing-step {
  display: grid;
  gap: 8px 32px;
  padding-block: clamp(22px, 2.4vw, 34px);
  border-top: 1px solid var(--wing-rule);
  align-items: baseline;
}
@media (min-width: 860px) { .wing-step { grid-template-columns: 60px 5fr 6fr; } }
.wing-step:first-child { border-top: 0; padding-top: 0; }
.wing-step__num { color: var(--wing-text-muted); }
.wing-step h3 { font-size: var(--wing-h3); letter-spacing: -0.035em; }
.wing-step p { margin: 0; color: var(--wing-text-muted); max-width: 46ch; }

.wing-defs { display: grid; gap: clamp(24px, 3vw, 48px); }
@media (min-width: 860px) { .wing-defs { grid-template-columns: repeat(3, 1fr); } }
.wing-def { padding-top: 0; }
.wing-def h3 { color: var(--wing-text-muted); }
.wing-def p { margin: 12px 0 0; font-size: var(--wing-small); color: var(--wing-text-muted); }

/* `--light` rows, 21 Sep — Eligibility's brief asks for "one consistent
   visual grammar: section label, clear heading, hairline-separated
   definition rows... do not add separate rounded cards to every
   criterion." `.wing-defs--light` already marks the applicant/request rows
   on Eligibility and the "what happens next" rows on Apply, but nothing
   read it, so those definitions sat in the same unbroken grid gap as every
   other `.wing-defs` group with no row of their own to be separated by.
   A top hairline turns each into what the brief actually asked for: a
   definition ROW, not an implicit grid cell - no card, no fill, no radius,
   the same rule-and-space language `.wing-facts` and `.wing-stages` already
   use. `var(--wing-rule)` re-points itself on a dark ground (section 2),
   so this needs no separate dark-ground override to stay correct there. */
.wing-defs--light .wing-def {
  border-top: 1px solid var(--wing-rule);
  padding-top: 20px;
}

/* THE TEAM. Brief 11 V04.
   These two profiles sat in the right half of a 6fr/6fr `.wing-pair` and
   then split THAT in two again, so each person got a quarter of the page:
   measured 257px at 1440 and 207px at 1024. Worse, `.wing-pair` collapses
   to one column below 1000px while `.wing-people` stayed two-up from
   760px, so each profile was WIDER at 768px (332px) than at 1440px. The
   larger screen gave the people less room, which is the wrong way round
   and is what made the names and qualifications wrap so badly.

   Two independent splits, one inside the other, cannot be tuned into a
   good result - the outer one has to stop applying here. The section
   heading now sits above a full-width composition, as the brief asks,
   and the two-up threshold moves to 900px so a column is never narrower
   than a name like "Dr. Christine Arasaratnam" can sit on.
   No markup changed: `:has()` does this from the stylesheet, so the
   Elementor content and its ids are untouched. */
.e-con.wing-pair:has(.wing-people) { grid-template-columns: 1fr; }

.wing-people { display: grid; gap: clamp(28px, 3vw, 56px); }
@media (min-width: 900px) { .wing-people { grid-template-columns: repeat(2, 1fr); } }
.wing-person { padding-top: 0; }
.wing-person h3 { font-size: var(--wing-h3); }
/* The role sat 8px under the name and 12px above the bio, so it read as
   the bio's first line rather than the name's second. */
.wing-person__role { display: block; margin: 6px 0 var(--wing-space-16); color: var(--wing-accent); }
.wing-person p { margin: 0; font-size: var(--wing-body); line-height: var(--wing-lh-body); color: var(--wing-text-muted); max-width: 46ch; }

.wing-check { list-style: none; margin: 0; padding: 0; }
.wing-check__item {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: 12px;
  padding-block: 14px;
  border-bottom: 1px solid var(--wing-rule);
  font-size: var(--wing-small);
}
.wing-check__mark { color: var(--wing-accent); }
.wing-check__item--cross .wing-check__mark { color: rgba(255,255,255,0.45); }
/* #8894A6 measured 3.07:1 at 15px. The cross is a character, not an icon, so
   it owes the text ratio. #5D6B7C is 5.44:1 on white and 5.0:1 on pale blue. */
.wing-section:not(.wing-section--dark) .wing-check__item--cross .wing-check__mark { color: #5D6B7C; }

/* =========================================================================
   12. FUNDING POSITIONING — grants / lending / Wing
   ========================================================================= */

.wing-vs { }
.wing-vs__row {
  display: grid;
  gap: 14px;
  padding-block: clamp(24px, 2.6vw, 38px);
  border-top: 1px solid var(--wing-rule);
}
.wing-vs__row:first-child { border-top: 0; padding-top: 0; }
@media (min-width: 900px) {
  .wing-vs__row { grid-template-columns: 3fr 9fr; column-gap: clamp(32px, 4vw, 72px); row-gap: 14px; }
  .wing-vs__dim { grid-row: 1 / span 2; }
}
.wing-vs__dim { margin: 0; font-size: var(--wing-h3); letter-spacing: -0.03em; }
.wing-vs__n { display: block; color: var(--wing-accent); margin-bottom: 8px; }
.wing-vs__wing { border-left: 2px solid var(--wing-accent); padding-left: 22px; }
.wing-vs__wing p {
  margin: 0;
  font-size: var(--wing-lede);
  font-weight: 600;
  line-height: 1.20;
  letter-spacing: -0.032em;
}
.wing-vs__alts { display: grid; gap: 12px; margin-top: 14px; }
@media (min-width: 620px) { .wing-vs__alts { grid-template-columns: repeat(2, 1fr); column-gap: 40px; } }
.wing-vs__alt { padding-left: 22px; }
.wing-vs__who { display: block; color: var(--wing-text-muted); margin-bottom: 4px; }
.wing-vs__val { font-size: var(--wing-small); color: var(--wing-text-muted); }

/* =========================================================================
   13. FAQ, CTA, FOOTER
   ========================================================================= */

/* THE GROUP GAP. Brief 11 V06's second half.
   Everywhere else on the site an index row introduces a large section
   heading, and `clamp(20px, 2.4vw, 34px)` under it is right. On the FAQ the
   index row IS the group's title and the first question starts immediately
   beneath it, so that gap plus the shell's 20px plus the summary's own 24px
   of padding put 78px between a 13px label and the question it introduces
   (measured at 1440). The label floated free of its own group.
   SCOPE, stated exactly, because it is narrower than it looks. Groups two
   to six are `--run` section continuations and already take 20px from
   v2-pages.css's `.wing-section--run + .wing-section--run .wing-index`
   rule, whose (0,6,0) beats this one. So this rule governs the FIRST group
   only, and its value is chosen to land that group as close to the other
   five as the structure allows: the first group opens its own section and
   carries 26px of section padding the others do not, so zero here renders
   28px against their 22px. Anything larger reopens the gap (16px -> 44,
   24px -> 52, both measured). Equalising the last 6px would mean cutting
   section padding that also sets the air ABOVE the label, under the topic
   navigation, where it is doing a job. */
.wing-inner:has(.wing-faq) .wing-index { margin-bottom: 0; }
.wing-inner:has(.wing-faq) .wing-faq__item:first-child > summary { padding-top: var(--wing-space-8); }

.wing-faq__item { border-bottom: 1px solid var(--wing-rule); }
/* Brief 11 V03: these measured 24px on 38.4px leading at weight 600 - the
   24 x 1.6 signature of base.css's dropped leading again, on all 24
   questions. A question that is heavier and more widely led than the answer
   under it inverts the page: the thing you are meant to read last shouts
   loudest. The brief's role is 22/20px, 500, 1.30, -0.01em. */
.wing-faq__item > summary {
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  padding-block: 24px;
  font-size: 22px;
  font-weight: var(--wing-display-weight);
  line-height: 1.3;
  letter-spacing: -0.01em;
  cursor: pointer;
  list-style: none;
}
@media (max-width: 782px) { .wing-faq__item > summary { font-size: 20px; } }
.wing-faq__item > summary::-webkit-details-marker { display: none; }
.wing-faq__item > summary::after { content: "+"; font-weight: 500; color: var(--wing-text-muted); }
.wing-faq__item[open] > summary::after { content: "\2013"; }
.wing-faq__answer { padding-bottom: 24px; }
.wing-faq__answer > * { max-width: 68ch; font-size: var(--wing-body); line-height: var(--wing-lh-body); color: var(--wing-text); margin: 0 0 1em; }
.wing-faq__answer > *:last-child { margin-bottom: 0; }

.e-con.wing-cta { position: relative; overflow: hidden; isolation: isolate; }
.e-con.wing-cta__inner { --width: 100%; display: grid; gap: clamp(28px, 3vw, 48px); }
.wing-cta__meta { display: flex; flex-wrap: wrap; gap: 8px 28px; margin-top: 24px; color: rgba(255,255,255,0.7); }

/* 585px, against dnacapital's 221px - and doing less with it. Three column
   headings above four links each add 99px of pure structure to navigate twelve
   items. One row instead. */
.wing-footer { padding-block: clamp(40px, 3.5vw, 60px) 28px; }
.e-con.wing-footer__top { --width: 100%; display: grid; gap: clamp(32px, 4vw, 72px); }
@media (min-width: 900px) { .e-con.wing-footer__top { grid-template-columns: 4fr 8fr; } }
.wing-footer__cols { display: flex; flex-wrap: wrap; gap: 12px 32px; }
@media (min-width: 900px) { .wing-footer__cols { justify-content: flex-end; } }
.wing-footer__col { display: contents; }
.wing-footer__col h3 { display: none; }
.wing-footer__col ul { display: contents; }
.wing-footer__col h3 { color: var(--wing-lightblue); margin: 0 0 16px; }
.wing-footer__col ul { list-style: none; margin: 0; padding: 0; }
.wing-footer__col li { margin-bottom: 10px; }
.wing-footer__col a, .wing-footer__legal a { color: rgba(255,255,255,0.78); text-decoration: none; }
.wing-footer__col a:hover, .wing-footer__legal a:hover { color: #fff; }
.wing-footer__bottom {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 16px;
  margin-top: 32px; padding-top: 20px;
  border-top: 1px solid var(--wing-rule-dark);
  font-size: var(--wing-caption); color: rgba(255,255,255,0.6);
}
.wing-footer__legal { display: flex; gap: 24px; margin: 0; }

/* =========================================================================
   14. FORMS
   ========================================================================= */

.wing-form .elementor-form-fields-wrapper { max-width: 780px; }
.wing-form .elementor-field-group > label { font-size: var(--wing-small); font-weight: 500; margin-bottom: 8px; }
.wing-form :is(input[type=text], input[type=email], input[type=tel], input[type=url], select, textarea) {
  width: 100%;
  min-height: 56px;
  padding: 14px 16px;
  font-size: 16px;               /* 16px or iOS zooms on focus */
  font-family: var(--wing-font);
  color: var(--wing-text);
  background: var(--wing-white);
  border: 1px solid var(--wing-rule);
  border-radius: var(--wing-radius);
}
.wing-form textarea { min-height: 140px; }
.wing-form :is(input, select, textarea):focus-visible { outline: 2px solid var(--wing-navy); outline-offset: 2px; }

/* =========================================================================
   15. MOBILE — every max-width rule lives here, after everything
   ========================================================================= */

@media (max-width: 1024px) {
  .wing-header__nav { flex: 1 1 auto; min-width: 0; }
  .wing-header__nav .elementor-nav-menu--dropdown {
    position: fixed;
    top: 84px; left: 0; right: 0;
    max-height: calc(100dvh - 84px);
    overflow-y: auto;
    background: var(--wing-ink);
    border-top: 1px solid var(--wing-rule-dark);
  }
  .wing-header__nav .elementor-nav-menu--dropdown a.elementor-item {
    min-height: 56px;
    display: flex;
    align-items: center;
    padding-inline: var(--wing-gutter);
    color: #fff;
    border-bottom: 1px solid var(--wing-rule-dark);
  }
}

@media (max-width: 899px) {
  .e-con.wing-hero { min-height: 92svh; }
  .wing-hero__foot { flex-direction: column; align-items: flex-start; }
  .wing-hero__actions, .e-con.wing-hero__actions { --flex-direction: column; align-items: stretch; width: 100%; }
  .wing-btn, .elementor-widget-button.wing-btn .elementor-button { width: 100%; }
}

@media (max-width: 782px) {
  body { overflow-x: clip; }
  .wing-display .elementor-heading-title, .wing-band__statement { overflow-wrap: break-word; }
  .wing-footer__col a, .wing-footer__legal a { display: flex; align-items: center; min-height: 44px; }
  .wing-footer__col li { margin-bottom: 0; }
  .wing-footer__legal { flex-direction: column; gap: 0; }
  /* Was `static`, which painted the copy UNDER the absolutely positioned
     photo and scrim: eyebrow, statement and sub were invisible on every
     band at <=782px (elementFromPoint returned the scrim, 29 Sep). */
  .wing-band .wing-media__overlay { position: relative; z-index: 1; padding-block: max(44px, 40vw) 44px; }
  .wing-band .wing-media--bleed .wing-media__frame { aspect-ratio: auto; min-height: 58vw; }
  .wing-band .wing-media--bleed .wing-media__frame > img { position: absolute; inset: 0; height: 100%; }
}

/* =========================================================================
   16. MOTION OFF
   ========================================================================= */

@media (prefers-reduced-motion: reduce) {
  .wing-hero__cue, .wing-hero__wash { animation: none !important; }
  * { transition-duration: 0.01ms !important; }
}
.wing-no-motion .wing-hero__cue,
.wing-no-motion .wing-hero__wash { animation: none !important; }

/* =========================================================================
   PASS A — fewer lines, a better hero, and motion you can feel
   -------------------------------------------------------------------------
   Jerald, 18 Sep: "I don't want to have too many lines going across and there
   isn't really that much animation to the website as a whole... the scroll
   button is all the way on the right, shouldn't be in the middle... as we
   hover over it, it should somewhat change."

   Counted before this pass: TWELVE full-width rules on the home page. The
   structure was being carried entirely by hairlines, which is what makes the
   page read as ruled paper rather than as a composition.

   The rule that replaces them: separation comes from SPACE and from the
   alternating layout. A hairline is kept only where two things genuinely
   collide - the footer, and nowhere else on the home page.
   ========================================================================= */

/* ---- 1. Take the rules out ------------------------------------------- */
.wing-hero__foot { border-top: 0; padding-top: 0; }
.wing-facts { border-top: 0; padding-top: 0; }
.wing-focus__item,
.wing-focus__item:first-child { border-top: 0; }
.wing-stages { border-top: 0; }
.wing-vs__row, .wing-vs__row:first-child { border-top: 0; }
.wing-step, .wing-step:first-child { border-top: 0; }
.wing-def, .wing-person, .wing-fact { border-top: 0; }
.wing-check__item { border-bottom: 0; }

/* Space does the separating now. */
.wing-focus__item { padding-block: clamp(44px, 6vw, 110px); }
.wing-focus__item:first-child { padding-top: clamp(20px, 2vw, 40px); }
.wing-vs__row { padding-block: clamp(20px, 2.2vw, 32px); }
.wing-step { padding-block: clamp(20px, 2.2vw, 34px); }
.wing-check__item { padding-block: 10px; }

/* ---- 2. The comparison needs edges, not rules -------------------------
   Six rows of three values do need to be separable. A left marker per row
   does it without drawing anything across the page, and it also gives the
   row something to react to on hover. */
.wing-vs__row {
  position: relative;
  padding-left: clamp(18px, 2vw, 34px);
  transition: background .4s var(--wing-ease);
}
.wing-vs__row::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 2px;
  background: var(--wing-rule);
  transition: background .4s var(--wing-ease), transform .5s var(--wing-ease);
  transform-origin: top;
}
.wing-vs__row:hover::before { background: var(--wing-accent); }
.wing-vs__row:hover { background: color-mix(in srgb, var(--wing-accent) 5%, transparent); }
.wing-vs__wing { border-left: 0; padding-left: 0; }

/* ---- 3. Hero ---------------------------------------------------------
   The composition was eyebrow / headline / lede / two buttons, stacked left,
   with the scroll cue pinned to the far right corner 1,350px away from
   anything it related to. */
.wing-hero__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  color: var(--wing-lightblue);
}
/* A short teal rule instead of a full-width one — the only "line" left in the
   hero, and it is 40px long. */
.wing-hero__eyebrow::before {
  content: "";
  width: 40px; height: 2px;
  background: var(--wing-teal);
  flex: none;
}
.wing-hero .wing-lede > p,
.wing-hero .wing-lede .elementor-widget-container > p {
  font-size: var(--wing-lede);
  color: rgba(255, 255, 255, 0.82);
  max-width: 44ch;
}
/* The film note and the cue move to a centred foot. */
.wing-hero__foot {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: end;
  gap: 16px;
  margin-top: clamp(40px, 6vw, 90px);
}
.wing-hero__foot > :first-child { grid-column: 1; font-size: var(--wing-caption); color: rgba(255,255,255,0.45); }
.wing-hero__cue {
  grid-column: 2;
  justify-self: center;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  font-size: var(--wing-caption);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}
/* A drawn line that travels, rather than a word that blinks. */
.wing-hero__cue::after {
  content: "";
  width: 1px; height: 46px;
  background: linear-gradient(180deg, rgba(184,205,225,0.9), rgba(184,205,225,0));
  transform-origin: top;
}
.wing-motion .wing-hero__cue::after { animation: wingCueLine 2.6s var(--wing-ease) infinite; }
.wing-motion .wing-hero__cue { animation: none; }
@keyframes wingCueLine {
  0%   { transform: scaleY(0); opacity: 0; }
  35%  { transform: scaleY(1); opacity: 1; }
  75%  { transform: scaleY(1); opacity: 1; }
  100% { transform: scaleY(1) translateY(46px); opacity: 0; }
}

/* ---- 4. The readiness track responds -------------------------------
   "As we hover over it, it should somewhat change. I feel like it's losing
   that element."

   Each stage is now a surface: the bar above it fills, the whole cell lifts,
   and the stages Wing funds carry a teal bar from the start so the boundary
   is legible before anyone touches it. */
.wing-stage {
  position: relative;
  padding: 26px 24px 30px;
  border: 0;
  border-radius: var(--wing-radius);
  cursor: default;
  transition: background .45s var(--wing-ease), transform .45s var(--wing-ease);
}
@media (min-width: 860px) { .wing-stage { padding-left: 24px; } .wing-stage:first-child { padding-left: 24px; } }
.wing-stage__bar {
  position: absolute;
  top: 0; left: 24px; right: 24px;
  height: 2px;
  background: var(--wing-rule-dark);
  overflow: hidden;
}
.wing-stage__bar::after {
  content: "";
  position: absolute; inset: 0;
  background: var(--wing-teal);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .6s var(--wing-ease);
}
.wing-stage--in .wing-stage__bar::after { transform: scaleX(1); }
.wing-stage:hover, .wing-stage:focus-visible {
  background: rgba(255, 255, 255, 0.045);
  transform: translateY(-3px);
  outline: none;
}
.wing-stage:hover .wing-stage__bar::after,
.wing-stage:focus-visible .wing-stage__bar::after { transform: scaleX(1); }
.wing-stage:not(.wing-stage--in):hover .wing-stage__bar::after { background: var(--wing-rule-dark); }
.wing-stage__num { color: var(--wing-lightblue); transition: color .4s ease; }
/* NOT teal. Teal on ink measures 3.83:1 and this is 13px - the exact case
   tokens.css warns about, "on dark it is a filled shape or a rule, never small
   text". I hardcoded the colour instead of using the surface token, which is
   what the token exists to prevent. Light blue is 11.67:1 here. The BARS stay
   teal, because a 2px bar is a shape, not text. */
.wing-stage--in .wing-stage__num { color: var(--wing-accent); }
.wing-stage p { transition: color .4s ease; }
.wing-stage:hover p, .wing-stage:focus-visible p { color: rgba(255,255,255,0.9); }
.wing-stage__flag {
  display: block;
  margin-top: 14px;
  min-height: 1em;
  font-size: var(--wing-caption);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--wing-accent);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity .45s var(--wing-ease), transform .45s var(--wing-ease);
}
.wing-stage--in .wing-stage__flag { opacity: 1; transform: none; }
.wing-stage--in ~ .wing-stage .wing-stage__flag { opacity: 0; }
.wing-stage--in .wing-stage__bar { left: 24px; }

/* ---- 5. Motion you can feel, everywhere it is cheap -----------------
   "2-3px lift, 450ms. No scaling, no shadows" is the rule for controls. For
   media, a slow scale inside a clipping frame is not a parallax - nothing
   moves relative to the scroll - so it stays inside the spec. */
.wing-focus__item .wing-media__frame { overflow: hidden; }
.wing-focus__item .wing-media__frame img {
  transition: transform 1.1s var(--wing-ease), filter 1.1s var(--wing-ease);
}
.wing-focus__item:hover .wing-media__frame img { transform: scale(1.035); }
.wing-focus__item h3 { transition: color .4s ease; }
.wing-focus__item:hover h3 { color: var(--wing-accent); }
.wing-focus__num { transition: letter-spacing .5s var(--wing-ease); }
.wing-focus__item:hover .wing-focus__num { letter-spacing: 0.14em; }

.wing-step { transition: transform .45s var(--wing-ease); }
.wing-step:hover { transform: translateX(6px); }
.wing-step h3 { transition: color .4s ease; }
.wing-step:hover h3 { color: var(--wing-accent); }

.wing-faq__item > summary { transition: color .35s ease; }
.wing-faq__item > summary:hover { color: var(--wing-accent); }

.wing-header__nav .elementor-nav-menu > li > a.elementor-item { position: relative; }
.wing-header__nav .elementor-nav-menu > li > a.elementor-item::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 2px;
  height: 1px; background: currentColor;
  transform: scaleX(0); transform-origin: right;
  transition: transform .45s var(--wing-ease);
}
.wing-header__nav .elementor-nav-menu > li > a.elementor-item:hover::after,
.wing-header__nav .elementor-nav-menu > li > a.elementor-item-active::after {
  transform: scaleX(1); transform-origin: left;
}

@media (prefers-reduced-motion: reduce) {
  .wing-hero__cue::after { animation: none !important; }
  .wing-focus__item:hover .wing-media__frame img,
  .wing-step:hover, .wing-stage:hover { transform: none !important; }
}

/* The stage track runs the full measure now, so it gets room to breathe and
   the hover surface is a real target rather than a 150px sliver. */
.wing-stages { margin-top: clamp(36px, 4vw, 70px); }
@media (min-width: 860px) { .wing-stages { grid-template-columns: repeat(4, 1fr); gap: 4px; } }
.wing-stage h3 { font-size: var(--wing-h3); letter-spacing: -0.03em; }
.wing-stage p { max-width: 30ch; }
.wing-stages__caption { margin-top: 26px; }

/* =========================================================================
   PASS B — the three things in Jerald's screenshots
   ========================================================================= */

/* ---- 1. The focus row's copy floated -----------------------------------
   Measured: the body column is 170px tall against a 337px photograph, and
   `align-items: center` put it in the middle — so "Primary Health" sat 84px
   below the top of its own image with white space above and below it, and had
   no relationship to the heading above or the picture beside it.

   Top-aligned, the heading of the row and the top edge of the photograph start
   on the same line, which is the relationship the layout was supposed to have. */
.wing-focus__item { align-items: start; }
.wing-focus__body { padding-top: 4px; }

/* The section heading was alone above three rows with nothing to hold it. In
   direction v4 it is a pair: the statement left, one line of context right. */
.wing-focus + *, .wing-focus { margin-top: 0; }
.wing-section .wing-display + .wing-focus { margin-top: clamp(28px, 3vw, 54px); }

/* ---- 2. One rail, not four bars ----------------------------------------
   The per-stage bars broke into segments with the grid gap showing through,
   and three separate teal bars read as three unrelated states rather than as
   one range. A single rail with one teal span says "from here onward" in a way
   three bars cannot. */
.wing-stages { position: relative; gap: 0; }
.wing-stages::before,
.wing-stages::after {
  content: "";
  position: absolute;
  top: 0;
  height: 2px;
}
.wing-stages::before { left: 0; right: 0; background: var(--wing-rule-dark); }
/* Wing funds stages 02, 03 and 04 of four, so the span starts at one quarter. */
.wing-stages::after {
  left: 25%; right: 0;
  background: var(--wing-teal);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 1.1s var(--wing-ease);
}
.wing-motion .wing-stages.is-drawn::after,
.wing-no-motion .wing-stages::after { transform: scaleX(1); }
html:not(.wing-motion):not(.wing-no-motion) .wing-stages::after { transform: scaleX(1); }
.wing-stage__bar { display: none; }
.wing-stage {
  padding: 28px 28px 30px 0;
  border-radius: 0;
}
@media (min-width: 860px) {
  .wing-stage { padding-left: 0; }
  .wing-stage + .wing-stage { padding-left: 28px; }
}
.wing-stage:hover, .wing-stage:focus-visible {
  background: rgba(255, 255, 255, 0.04);
  transform: none;
}
/* The cell that is hovered raises its own tick above the rail. */
.wing-stage::after {
  content: "";
  position: absolute;
  top: -2px; left: 0; right: 0;
  height: 2px;
  background: var(--wing-white);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .5s var(--wing-ease);
}
.wing-stage:hover::after, .wing-stage:focus-visible::after { transform: scaleX(1); }
.wing-stage__flag { margin-top: 16px; }

/* ---- 3. The scroll cue was alone in a void ------------------------------
   It sat in the middle of the hero's empty lower half with nothing near it.
   It belongs at the foot of the screen, small, close to the edge — a margin
   note, not a feature. */
.wing-hero__foot {
  margin-top: clamp(28px, 3.5vw, 52px);
  align-items: center;
}
.wing-hero__cue {
  gap: 8px;
  font-size: var(--wing-caption);
  letter-spacing: 0.18em;
  /* 0.42 measured 4.07:1 at 11px - I dimmed it to make it recede and took it
     under AA doing so. 0.56 is 6.4:1 and still reads as a margin note. */
  color: rgba(255, 255, 255, 0.56);
}
.wing-hero__cue::after { height: 28px; }
@keyframes wingCueLine {
  0%   { transform: scaleY(0); opacity: 0; }
  35%  { transform: scaleY(1); opacity: 1; }
  75%  { transform: scaleY(1); opacity: 1; }
  100% { transform: scaleY(1) translateY(28px); opacity: 0; }
}
@media (max-width: 899px) {
  .wing-hero__foot { grid-template-columns: 1fr; }
  .wing-hero__cue { grid-column: 1; justify-self: start; }
}

/* Reversing a row by naming `grid-column` alone is not enough: the two items
   are still auto-placed by source order, so the second one drops to an
   implicit SECOND row. Measured on the reversed focus row: the photograph sat
   229px below its own heading, while the two forward rows were aligned to
   within 26px. Both items are pinned to row 1. */
@media (min-width: 900px) {
  .wing-focus__item > * { grid-row: 1; }
  .e-con.wing-split--reverse > * { grid-row: 1; }
}

/* The footer mark was rendering at 192px tall - `image_size: medium` with no
   constraint in the rebuilt stylesheet, which is most of why the footer stands
   585px. A footer mark is a signature, not a masthead. */
.wing-footer__logo img { height: 36px; width: auto; }
.wing-footer__logo { line-height: 0; }
.wing-footer__brand { max-width: 34ch; }
.wing-footer__blurb { margin: 16px 0 0; font-size: var(--wing-small); color: rgba(255,255,255,0.7); max-width: 40ch; }


/* =========================================================================
   MOTION, CORRECTED AGAINST THE REFERENCES
   -------------------------------------------------------------------------
   Measured across the three sites the client supplied:

   - ZERO blur in motion on any of them. A 6px blur on a reveal reads as a
     video transition, not a typographic one. Their equivalent is a MASK -
     ovatafunds slides words up from y:100% inside overflow:hidden, and wipes
     a #f7f6f2 overlay from width:100% to 0%. Same perceived softness, sharp
     type throughout, and it can be scrubbed.
   - Routine transitions run 0.2-0.6s on all three (dnacapital 0.2/0.25/0.3,
     ouiwill 0.6, ovatafunds 0.3/0.4). Anything over 1s is reserved for
     one-off decoration. Our 1.05s reveal is 1.75-5x slower than any of them,
     which is most of why the motion reads as heavy rather than quick.
   ========================================================================= */

/* `.wing-reveal-mask` and the `--wing-reveal-dur` override that sat here
   went with the reveal on 23 Sep 2026 (brief 11, V01). The mask class was
   emitted by nothing and the duration was read by nothing. */
:root {
  --wing-hover-dur: 0.3s;     /* was 0.45s - matches all three references */
}

/* =========================================================================
   SCROLL-LINKED MOTION: REMOVED, 23 September 2026 (brief 11, V01)
   -------------------------------------------------------------------------
   This was the site's FIRST scroll-linked system - `wing-rise` on
   `[data-reveal]`, `wing-unmask` on headings, `wing-media-drift` (the
   parallax) on photographs, `wing-rail` on the readiness rail - all on
   `animation-timeline: view()`.

   It was found by review AFTER the newer copies in components-v2.css and
   v2-motion.css had already been removed, and it is the reason two comments
   in this codebase briefly claimed, wrongly, that nothing selected
   `[data-reveal]` any more. Something did: the rule that used to sit here.

   It was inert on the live site only because no page currently emits
   `data-reveal` and `.wing-vs__wing` renders nowhere. That is not a safe
   place to leave it. `build/wingbuild.py` still knows how to emit both, so a
   regenerate would have restored the exact defect V01 was raised for -
   permanently part-transparent text - with no code change and no warning.

   Removed with it: the `overflow: hidden` plus `padding-block: .08em` on
   `.wing-display`, which existed only to clip the mask. It was computing
   `hidden` on seven headings in Chromium and WebKit but `visible` in
   Firefox, so the engines disagreed, and it would have clipped the focus
   ring of any link a heading came to contain.

   v2-motion.css section 8 used to cancel three of these four animations from
   a distance because this file "belonged to someone else that week". Those
   counter-rules went in the same change; there is nothing left to cancel.
   ========================================================================= */

/* =========================================================================
   READING MEASURE AND CAPTION LEADING, 21 September 2026
   Brief: docs/design-refinement/09-visual-simplification.md §3.

   These three come from wingbuild.py's shared helpers, so they appear on
   several pages and are capped here rather than per page. Measured on the
   live pages, content box, at 1440:

     .wing-limit             107 characters per line at 14px
     .wing-check__item        92 characters per line at 16px
     .wing-stages__caption    77 characters per line at 13px, and uppercase

   56ch is the measure the brand spec already sets for body copy, so this
   makes supporting text obey the same discipline rather than inventing a
   second rule. No wording is changed, shortened or hidden.

   `.wing-stages__caption` also carried a 1.4 line-height, the tightest on
   the site, on text that is both the smallest and set in capitals. Brief 09
   §3 asks for 1.5 to 1.65 "where paragraphs currently feel tight", and
   uppercase needs the looser end rather than the tighter one, because
   capitals have no descenders to separate the lines visually.
   ========================================================================= */
.wing-limit,
.wing-check__item,
.wing-stages__caption { max-width: 56ch; }
.wing-stages__caption { line-height: 1.55; }

/* Extended 21 September 2026, same brief. `.wing-qualifier` and
   `.wing-notice` share the exact rule block that gave `.wing-limit` its
   original 78ch (section 1) but were left out of the 56ch fix above, so
   they still ran a measure roughly 40% wider than every sibling class in
   that same declaration. Measured, content box, at 1440:

     .wing-notice     105 characters per line at 14px (Apply's compliance
                       notice - the widest single line found in this pass)
     .wing-qualifier    not yet in live use at this measure, capped on the
                       same reasoning to stay consistent if it is

   `.wing-faq__answer` is the FAQ's own answer copy - real body prose, not
   a caption - so it takes the same `--wing-measure` token `.wing-lede` and
   `.wing-prose` already use elsewhere, rather than the caption-scale 56ch
   literal, for one reading width across the site's actual paragraphs.
   Answers previously ran at a bespoke 72ch nobody else on the site uses. */
.wing-qualifier,
.wing-notice { max-width: 56ch; }
.wing-faq__answer > * { max-width: var(--wing-measure); }

/* ==== components-v2.css ==== */
/* =========================================================================
   Wing R&D Fund — Direction 05: Coastline
   -------------------------------------------------------------------------
   docs/directions/05-coastline.html translated to Elementor's markup. That
   file is the spec; every value here is lifted from it or from master-plan
   §3. Nothing is invented.

   WHY A SECOND FILE. components.css still dresses eight inner pages that are
   not being rebuilt in this pass. Editing it in place would have meant
   rewriting rules those pages depend on while the home page was half built.
   This file loads AFTER it, so it wins on equal specificity, and the home
   page uses its own class names throughout. When the inner pages are moved
   over (WP9) the two files merge and components.css shrinks.

   THE MARKUP THIS IS WRITTEN AGAINST (verified against rendered HTML, not
   assumed). With `content_width: full` an Elementor container renders as
   ONE element - `<div class="… MYCLASSES e-con-full e-flex e-con">children` -
   with no `.e-con-inner`, and in Elementor 4.x widgets have no
   `.elementor-widget-container` either:

     container    <div class="… MYCLASSES e-con-full e-flex e-con">
     heading      <div class="… MYCLASSES elementor-widget-heading">
                    <h2 class="elementor-heading-title">
     text-editor  <div class="… MYCLASSES elementor-widget-text-editor"><p>
     button       <div class="… MYCLASSES elementor-widget-button">
                    <a class="elementor-button">
     html         <div class="… elementor-widget-html">RAW

   So a class set with `_css_classes` lands on the WRAPPER, never on the h2 or
   the <a>. Every rule below names the wrapper class and then descends. Three
   corollaries, each of which has cost this project an hour before:

     - containers take `css_classes`, widgets take `_css_classes`;
     - Elementor's per-page post-N.css and the kit load after this file, so
       heading and button rules carry the widget class to beat them;
     - `.elementor.elementor .e-con > .elementor-widget{max-width:100%}` is
       (0,4,0) and kills a max-width set on a widget. Measure goes on the
       inner <p> or <h2>, never on the wrapper.

   SCOPES. `.wing-v2` is on <body> everywhere and carries the chrome.
   `.wing-home` is on the front page only and carries the sections. Nothing
   here is unscoped, so the eight inner pages are untouched by this file
   except for the header and the footer, which are site-wide by design.
   ========================================================================= */

/* =========================================================================
   0. SHARED PRIMITIVES
   ========================================================================= */

.wing-v2 {
  --navy: var(--wing-navy);
  --lightblue: var(--wing-lightblue);
  --pale: var(--wing-paleblue);
  --ink: var(--wing-ink);
  --pounamu: var(--wing-pounamu);
  --teal: var(--wing-teal);
  --muted: var(--wing-text-muted);
  --rule: var(--wing-rule);
  --rule-dark: var(--wing-rule-dark);
  --ease: var(--wing-ease);
  --dw: var(--wing-display-weight);
}

/* --- three Elementor-level corrections, all of them site-wide -----------

   SHELL. components.css caps `.wing-shell` at `--wing-max + 2 * --wing-gutter`
   = 1444px, so at a 1440 viewport the container ran edge to edge and the
   1300px measure was never seen. The mock's `.shell` caps the OUTER box at
   1300, which is what puts 70px of visible margin either side. Corrected here
   rather than in components.css so the eight inner pages keep one shell with
   the chrome, and no rule in that file is rewritten.

   LINE-HEIGHT. Elementor's kit writes `.elementor-kit-5{line-height:1.6em}`,
   an absolute 28.8px that every child inherits whatever its own size, so a
   14px caption was leading at 28.8 instead of 22.4. `body` in base.css and
   components.css already says `var(--wing-lh-body)` but loses (0,0,1) to the
   kit's (0,1,0). `body.wing-v2` is (0,1,1) and wins, and 1.6 unitless then
   inherits as a RATIO, which is the whole point.

   [hidden]. Something in the Elementor stack sets `display` on elements the
   page hides with the `hidden` attribute, which beats the UA's
   `[hidden]{display:none}`. The fit-check's Back button and its answer chips
   were both rendering on question one because of it.
   ------------------------------------------------------------------------ */
.wing-v2 .wing-shell, .wing-v2 .e-con.wing-shell { max-width: var(--wing-max); }
body.wing-v2 { line-height: var(--wing-lh-body); }
.wing-v2 [hidden] { display: none !important; }

.wing-home .wing-main figure { margin: 0; }
.wing-home .wing-main img { display: block; max-width: 100%; }

/* Elementor's default container gap is 20px and it fights every rhythm in
   this file. Zero it on the home page only, then spend it deliberately.

   IT IS `--row-gap` / `--column-gap`, NOT `--gap`. Elementor 4.2 resolves a
   container's gap as `gap: var(--row-gap) var(--column-gap)`; `--gap` is the
   3.x name and setting it does nothing at all. Every column pair on this page
   was silently running Elementor's 20px default instead of the mock's 64 or
   80, which changed every flex-shrunk column width on the page. */
.wing-home .wing-main .e-con { --row-gap: 0px; --column-gap: 0px; --flex-wrap: nowrap; }

/* SAME FAULT, INNER PAGES, fixed 19 Sep. The rule above was scoped to
   `.wing-home` and every other page kept Elementor's 20px default, so each
   inner page carried a 20px strip of page background between every top-level
   band: seven of them on /how-funding-works/, four on /about/. Between two
   light bands it is invisible; between the dark hero and the dark "01" band
   below it, it is a bright white seam across the full width of the page,
   which is what it looked like. Measured before the fix: hero ended at 707,
   the next band began at 727, and so on at every boundary on every inner
   page. The home page measured 0 at every boundary, which is why this was
   never seen there.

   `wing-inner` is a single Elementor container wrapping the whole page
   (wingbuild_pages.py, `wrap_page()`), so the bands are ITS flex children and
   its own `--row-gap` is the seam. Descendant combinator, not `>`: Elementor
   puts its own `div.elementor.elementor-N` between `main` and the wrapper,
   so `.wing-main > .wing-inner` matches nothing. It is written once per page,
   so the loose selector still resolves to exactly one node. Zeroing it here rather than on every
   `.e-con` is deliberate: inner-page components DO use Elementor's gap
   between their own columns, and zeroing it everywhere would collapse them. */
.wing-main .wing-inner { --row-gap: 0px; }

/* --- display type ------------------------------------------------------- */
.wing-home .display.elementor-widget-heading .elementor-heading-title,
.wing-home .display h1, .wing-home .display h2, .wing-home .display h3 {
  font-weight: var(--wing-display-weight);
  letter-spacing: var(--wing-ls-display);
  line-height: var(--wing-lh-display);
  text-wrap: balance;
  margin: 0;
  max-width: none;
  color: inherit;
}
.wing-home h1.display.elementor-widget-heading .elementor-heading-title,
.wing-home .display--h1.elementor-widget-heading .elementor-heading-title {
  font-size: var(--wing-display);
  line-height: 1;
  padding-bottom: .06em;
}
.wing-home .display.elementor-widget-heading .elementor-heading-title {
  font-size: var(--wing-h2);
  line-height: var(--wing-lh-h2);
}
.wing-home .display--h1.elementor-widget-heading .elementor-heading-title {
  font-size: var(--wing-display);
  line-height: 1;
}
/* One word per H1, in Archivo's OWN italic. Never a second family. */
.wing-home .display .elementor-heading-title em {
  font-style: italic;
  font-weight: calc(var(--wing-display-weight) - 60);
}

.wing-home .lede.elementor-widget-text-editor > p {
  font-size: var(--wing-lede);
  line-height: var(--wing-lh-lede);
  max-width: 34ch;
  margin: 0;
}
.wing-home .body-copy.elementor-widget-text-editor > p,
.wing-home .measure.elementor-widget-text-editor > p {
  font-size: var(--wing-body);
  line-height: 1.6;
  max-width: 56ch;
  margin: 0;
}
.wing-home .muted.elementor-widget-text-editor > p { color: var(--muted); }

.wing-home .label,
.wing-home .index .label { font-size: var(--wing-label); font-weight: 500; letter-spacing: .08em; text-transform: uppercase; }

/* `01 / WHAT WE FUND`. The numeral is body size and the caption beside it is
   the label size - setting the whole row to the label size shrank the numeral
   and took 8px off the row, which is 8px off five sections. */
.wing-home .index {
  display: flex; gap: 14px; align-items: baseline;
  color: var(--teal); margin-bottom: 28px;
}
.wing-home .index b { font-weight: 500; }

/* --- grounds ------------------------------------------------------------ */
.wing-home .dark { background: var(--ink); color: #fff; }
.wing-home .pale { background: var(--pale); }
.wing-home .dark .index { color: var(--lightblue); }
.wing-home .dark .muted, .wing-home .dark p.body { color: rgba(255,255,255,.72); }

/* Elementor's kit colours the heading and text-editor widgets directly, so
   inheritance never reaches them on a dark band. Name the widget class. */
.wing-home .dark .elementor-widget-heading .elementor-heading-title,
.wing-home .hero .elementor-widget-heading .elementor-heading-title,
.wing-home .close .elementor-widget-heading .elementor-heading-title { color: #fff; }
.wing-home .dark .elementor-widget-text-editor,
.wing-home .dark .elementor-widget-text-editor > p,
.wing-home .hero .elementor-widget-text-editor > p,
.wing-home .close .elementor-widget-text-editor > p { color: rgba(255,255,255,.80); }
.wing-home .hero .lede.elementor-widget-text-editor > p,
.wing-home .close .lede.elementor-widget-text-editor > p { color: rgba(255,255,255,.86); }

.wing-home .band { padding-top: var(--wing-band); }
.wing-home .band--end { padding-bottom: var(--wing-band); }
.wing-home .muted { color: var(--muted); }
.wing-home .measure { max-width: 56ch; }
.wing-home .place {
  margin: 24px 0 0; text-align: right;
  font-size: var(--wing-label); color: rgba(255,255,255,.7); letter-spacing: 0;
}

/* --- buttons: one radius system (pill), two states, no shadow ------------ */
.wing-v2 .btn.elementor-widget-button { --width: auto; flex: 0 0 auto; width: auto; }
.wing-v2 .btn.elementor-widget-button .elementor-button,
.wing-v2 a.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  height: 52px; padding: 0 26px; border-radius: 100px;
  font-family: inherit; font-size: var(--wing-action-size); font-weight: 500;
  letter-spacing: 0; line-height: 1; text-decoration: none;
  border: 1px solid transparent; background: none; box-shadow: none; fill: currentColor;
  transition: transform .45s var(--wing-ease), background .45s var(--wing-ease),
              border-color .45s var(--wing-ease), color .45s var(--wing-ease);
}
/* Brief 11 V11: hover MOVEMENT is gated on a real pointer. On a touch
   screen `:hover` sticks after a tap, so a lifted control stayed lifted
   with nothing to un-hover it. Colour and border feedback stay ungated,
   and keyboard `:focus-visible` is deliberately outside this query so
   focus feedback is never lost. */
@media (hover: hover) and (pointer: fine) {
  .wing-v2 .btn .elementor-button:hover, .wing-v2 a.btn:hover { transform: translateY(-2px); box-shadow: none; }
}
.wing-v2 .btn .elementor-button:active, .wing-v2 a.btn:active { transform: translateY(0); }
/* Every modifier below names `.elementor-widget-button` so it matches the
   base rule's (0,4,0) specificity - `.btn--primary .elementor-button` alone
   is (0,3,0) and LOSES to the base rule's transparent background/border,
   which is why buttons rendered with no fill and no outline. */
.wing-v2 .btn--primary.elementor-widget-button .elementor-button, .wing-v2 a.btn--primary { background: #fff; color: var(--wing-ink); }
.wing-v2 .btn--primary.elementor-widget-button .elementor-button:hover, .wing-v2 a.btn--primary:hover { background: var(--lightblue); color: var(--wing-ink); }
.wing-v2 .btn--ghost.elementor-widget-button .elementor-button, .wing-v2 a.btn--ghost { border-color: rgba(255,255,255,.4); color: #fff; }
.wing-v2 .btn--ghost.elementor-widget-button .elementor-button:hover, .wing-v2 a.btn--ghost:hover { border-color: #fff; background: rgba(255,255,255,.08); color: #fff; }
.wing-v2 .on-light .btn--primary.elementor-widget-button .elementor-button, .wing-v2 .on-light a.btn--primary { background: var(--wing-navy); color: #fff; }
.wing-v2 .on-light .btn--primary.elementor-widget-button .elementor-button:hover { background: var(--wing-navy-lift); }
.wing-v2 .on-light .btn--ghost.elementor-widget-button .elementor-button, .wing-v2 .on-light a.btn--ghost { border-color: rgba(10,52,106,.35); color: var(--wing-navy); }
.wing-v2 .on-light .btn--ghost.elementor-widget-button .elementor-button:hover { border-color: var(--wing-navy); background: transparent; color: var(--wing-navy); }

/* =========================================================================
   1. NAV — logo left; the four links and the CTA together on the right,
   permanent at every scroll position (client correction, 18 Sep - see
   header()'s docstring in build-chrome.py). Only `is-solid`, `is-light` and
   the hide-on-scroll-down/return-on-scroll-up state still change.
   ========================================================================= */

.wing-v2 .nav.e-con, .wing-v2 .nav {
  position: fixed; inset: 0 0 auto 0; z-index: 40; height: var(--wing-header-h);
  color: #fff; padding: 0;
  background-color: transparent; background-image: none;
  border-bottom: 1px solid transparent;
}
/* LEGIBILITY OVER THE HERO, owned by the bar (28 Sep). Replaces the text-
   shadow that used to sit on the links (base.css), which read smudgy close
   up. A soft ink wash, eased over twice the bar's height so it has no visible
   lower edge, shown only while the bar is transparent over a hero photo. It
   is the bar's own layer, so the hero photo and its scrim stay untouched.
   Elementor uses `.e-con::before` for background overlays; every property it
   sets is restated here. */
.wing-v2 .nav.e-con::before, .wing-v2 .nav::before {
  content: ""; display: block; position: absolute; top: 0; left: 0; z-index: 0;
  width: 100%; height: calc(var(--wing-header-h) * 2);
  border: 0; border-radius: 0; mix-blend-mode: normal; opacity: 1;
  pointer-events: none; transition: none;
  background: linear-gradient(180deg,
    rgba(7,16,30,.66) 0%, rgba(7,16,30,.60) 16%, rgba(7,16,30,.50) 30%,
    rgba(7,16,30,.37) 44%, rgba(7,16,30,.23) 58%, rgba(7,16,30,.11) 74%,
    rgba(7,16,30,.03) 88%, rgba(7,16,30,0) 100%);
}
.wing-v2 .nav.is-solid::before, .wing-v2 .nav.is-light::before,
.wing-v2 .nav.is-open::before { opacity: 0; }
/* The bar's row sits above the wash and above the phone menu sheet, so the
   logo and the close control stay visible and tappable while it is open. */
.wing-v2 .nav .nav__in.e-con { position: relative; z-index: 2; }
/* The scroll-progress rule, pinned to the bar's own bottom edge. It is the
   html widget's own div made absolute (same fix as `hero__layers`), not a
   nested child, so it needs no wrapper/height dance. Colour follows the
   bar's own ground: light blue on dark, teal once the bar goes `is-light`.
   `transform` is never transitioned - it must track scroll exactly, and a
   transition would read as lag, which is the one thing motion.js's own
   comment block bans ("nothing blurs, bounces, spins or parallaxes"). */
.wing-v2 .nav__progress {
  position: absolute; left: 0; right: 0; bottom: 0; height: 2px;
  background: var(--wing-lightblue);
  transform: scaleX(var(--wing-progress, 0)); transform-origin: left;
  pointer-events: none;
}
.wing-v2 .nav.is-light .nav__progress { background: var(--wing-teal); }
/* Scroll-driven, no JS, where supported: growth starts once you have
   scrolled past one hero-height (100vh; the hero is pinned to 100svh so the
   two are close enough not to matter) and runs to the foot of the document.
   motion.js feature-detects the same support and skips its own rAF write
   when this rule is active, so the two paths never fight over `transform`. */
@supports (animation-timeline: scroll(root block)) {
  .wing-v2 .nav__progress {
    animation: wingNavProgress linear both;
    animation-timeline: scroll(root block);
    animation-range: 100vh 100%;
  }
}
@keyframes wingNavProgress { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.wing-v2 .nav.is-hidden { transform: translateY(-110%); }
/* SOLID STATES: opaque, no blur, and two of them only, ink or white.
   29 Sep: a third, pale state added a flip at every white/pale band edge,
   and the .25s colour fade passed through grey at each; Jerald read both
   as flashing. The bar now changes ground in one step, exactly as a band
   edge passes under it, so it reads as the band's own edge, not a flash. */
.wing-v2 .nav.is-solid { background-color: var(--wing-ink); border-bottom-color: var(--wing-rule-dark); }
.wing-v2 .nav.is-light { background-color: var(--wing-white); border-bottom-color: var(--wing-rule); color: var(--wing-navy); }
/* Menu open: the bar turns ink whatever ground it was on, so the sheet
   unrolls from under an opaque bar rather than behind a see-through one. */
.wing-v2 .nav.is-open, .wing-v2 .nav.is-open.is-light {
  background-color: var(--wing-ink); border-bottom-color: var(--wing-rule-dark); color: #fff;
}
@media (prefers-reduced-motion: no-preference) {
  .wing-v2 .nav.e-con, .wing-v2 .nav {
    transition: transform .45s var(--wing-ease);
  }
  .wing-v2 .nav.e-con::before, .wing-v2 .nav::before { transition: opacity .45s var(--wing-ease); }
}
.wing-v2 .nav .e-con { --row-gap: 0px; --column-gap: 0px; }
/* `.nav` is a COLUMN flex container and Elementor gives every container
   `flex: 0 1 auto`, so the bar's inner row was shrinking to 55px against its
   own `height:100%` and every item in it rode 10px high. `flex: 0 0 auto` is
   the fix; the height then resolves to the bar's 75px content box. */
.wing-v2 .nav .nav__in.e-con {
  height: 100%; flex: 0 0 auto; --flex-direction: row; --align-items: center;
  --justify-content: space-between; --row-gap: 32px; --column-gap: 32px; --flex-wrap: nowrap;
}
/* Touch target (defect #3, crossbrowser-a11y.md): the link was the image's
   own 53x34 box, under the 44px floor on the short side. Padding, not a
   bigger image - the mark stays 34px tall and dead-centred in the 76px bar
   either way, since `.nav__in`'s `align-items: center` centres the link's
   whole (now taller) box, not the image inside it. */
.wing-v2 .nav__logo { display: block; line-height: 0; flex: none; padding-block: 6px; }
.wing-v2 .nav__logo img { height: 34px; width: auto; }
.wing-v2 .nav__logo .colour { display: none; }
.wing-v2 .nav.is-light .nav__logo .colour { display: block; }
.wing-v2 .nav.is-light .nav__logo .reverse { display: none; }

.wing-v2 .nav__right.e-con {
  --flex-direction: row; --align-items: center; --row-gap: 32px; --column-gap: 32px; --width: auto;
  --flex-wrap: nowrap; flex: 0 0 auto; width: auto;
}

/* the four links: Elementor's nav-menu widget, stripped to the mock */
.wing-v2 .nav__links.elementor-widget-nav-menu { --width: auto; flex: none; width: auto; }
.wing-v2 .nav__links .elementor-nav-menu--main > ul.elementor-nav-menu { display: flex; gap: 28px; margin: 0; padding: 0; list-style: none; }
.wing-v2 .nav__links .elementor-nav-menu > li { margin: 0; }
.wing-v2 .nav__links .elementor-nav-menu > li > a.elementor-item {
  position: relative; display: inline-block; padding: 0; margin: 0;
  color: inherit; background: none; text-decoration: none;
  font-size: var(--wing-action-size); font-weight: 500; line-height: var(--wing-lh-body); fill: currentColor;
  /* `opacity: .92` used to sit here as a resting state. Brief 11 V01
     wants essential text fully opaque, and these links spend the whole
     home page over a photograph, which is where the site's contrast is
     already tightest (pixel sweep worst case 4.92:1). Dimming the one
     element that has to stay legible over an image was backwards. */
}
.wing-v2 .nav__links .elementor-nav-menu > li > a.elementor-item::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -6px; height: 1px;
  background: currentColor; transform: scaleX(0); transform-origin: left;
  transition: transform .3s var(--wing-ease);
}
.wing-v2 .nav__links .elementor-nav-menu > li > a.elementor-item:hover::after,
.wing-v2 .nav__links .elementor-nav-menu > li > a.elementor-item-active::after { transform: scaleX(1); }
/* Elementor's own responsive plumbing is not used: the hamburger below is. */
.wing-v2 .nav__links .elementor-menu-toggle,
.wing-v2 .nav__links .elementor-nav-menu--dropdown { display: none !important; }

/* The burger lives in its own HTML widget (`nav__burgerw`) and is a
   width-driven, not scroll-driven, swap: hidden >=900px, the only thing in
   `.nav__right` under 900px. No positional fallback is needed any more - the
   index widget that used to share this row is gone, so `nav__burgerw` is the
   only HTML-widget child left and the class alone is enough. */
.wing-v2 .nav__burgerw { display: none; }

.wing-v2 .nav .btn.elementor-widget-button .elementor-button { height: 44px; padding: 0 20px; font-size: var(--wing-caption); }
.wing-v2 .nav.is-light .btn--ghost.elementor-widget-button .elementor-button { background: var(--wing-teal); border-color: var(--wing-teal); color: #fff; }
.wing-v2 .nav.is-light .btn--ghost.elementor-widget-button .elementor-button:hover { background: var(--wing-teal-deep); border-color: var(--wing-teal-deep); color: #fff; }

/* hamburger + full-screen menu */
.wing-v2 .nav__burger { display: none; width: 44px; height: 44px; min-width: 44px; position: relative; border: 0; background: none; padding: 0; cursor: pointer; color: inherit; }
/* The Elementor kit (`.elementor-kit-5 button:hover/:focus`, 0,2,1) and
   Hello's reset (`[type=button]:hover`) paint every button teal with white
   text, so the burger became a filled square on hover, tap and focus, and
   Hello's `transition: all .3s` put a second, off-curve fade on it. The
   burger is two lines on the bar's own ground in every state. */
.wing-v2 .nav .nav__burger,
.wing-v2 .nav .nav__burger:is(:hover, :focus, :active, :focus-visible) {
  background: none; background-color: transparent; border: 0; border-radius: var(--wing-radius);
  color: inherit; box-shadow: none; transition: none; -webkit-tap-highlight-color: transparent;
}
.wing-v2 .nav .nav__burger:focus:not(:focus-visible) { outline: none; }
/* Keyboard ring: light blue on dark, teal on a light bar (4.98:1 on white,
   4.58:1 on pale). An open menu is ink whatever the bar was. */
.wing-v2 .nav .nav__burger:focus-visible { outline: 2px solid var(--wing-lightblue); outline-offset: 3px; }
.wing-v2 .nav.is-light:not(.is-open) .nav__burger:focus-visible { outline-color: var(--wing-teal); }
.wing-v2 .nav__burger span { position: absolute; left: 12px; right: 12px; height: 1.5px; background: currentColor; }
.wing-v2 .nav__burger span:nth-child(1) { top: 17px; }
.wing-v2 .nav__burger span:nth-child(2) { top: 26px; }
/* 5 Oct: the cross follows the button's own state, so it morphs back the
   moment the menu is closed, while the bar keeps its open colours until
   the sheet has gone (motion.js menu()). */
.wing-v2 .nav__burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(4.5px) rotate(45deg); }
.wing-v2 .nav__burger[aria-expanded="true"] span:nth-child(2) { transform: translateY(-4.5px) rotate(-45deg); }

/* THE PHONE MENU, rebuilt 28 Sep. It used to cover the whole screen,
   bar included, so there was no logo and no visible way out; only Escape
   closed it. It now sits UNDER the bar (z-index inside the bar's own
   stacking context, see `.nav__in` above), so the burger, drawn as a cross,
   is its close control. Primary links at the top; the CTA and the three
   footer pages gather at the foot, in thumb reach. */
.wing-v2 .navsheet {
  position: fixed; inset: 0; z-index: 1; background: var(--wing-ink); color: #fff;
  display: flex; flex-direction: column;
  padding: calc(var(--wing-header-h) + 8px) var(--wing-gutter) calc(32px + env(safe-area-inset-bottom));
  overflow-y: auto; overscroll-behavior: contain;
  visibility: hidden;
}
.wing-v2 .navsheet[hidden] { display: none; }
.wing-v2 .navsheet.is-open { visibility: visible; }
.wing-v2 .navsheet ul { list-style: none; margin: 0; padding: 0; }
.wing-v2 .navsheet__primary li { border-bottom: 1px solid var(--wing-rule-dark); }
.wing-v2 .navsheet__primary a {
  display: flex; align-items: center; min-height: 64px;
  font-size: var(--wing-h2); font-weight: var(--wing-display-weight);
  letter-spacing: var(--wing-ls-h2); line-height: 1.02;
  color: #fff; text-decoration: none;
}
/* Where you are: motion.js marks the current page. Light blue, never teal,
   on ink (tokens.css contrast rule). */
.wing-v2 .navsheet :is(.navsheet__primary, .navsheet__more) a[aria-current="page"] { color: var(--wing-lightblue); }
.wing-v2 .navsheet__cta { margin-top: auto; padding-top: 32px; }
.wing-v2 .navsheet__cta a.btn { display: flex; width: 100%; }
.wing-v2 .navsheet__more { margin-top: 16px; }
.wing-v2 .navsheet__more a {
  display: flex; align-items: center; min-height: 44px;
  font-size: var(--wing-small); color: rgba(255,255,255,.78); text-decoration: none;
}
.wing-v2 .navsheet__more a:hover { color: #fff; }
/* A tablet held upright is not a thumb-reach screen: at 768 the foot-pinned
   CTA left a 450px void and stretched to a 700px pill. There the CTA and the
   quieter links follow the list, and the button keeps a button's width. */
@media (min-width: 600px) {
  .wing-v2 .navsheet__cta { margin-top: 0; padding-top: 48px; }
  .wing-v2 .navsheet__cta a.btn { display: inline-flex; width: auto; min-width: 280px; }
}
/* Arrival: the ink unrolls down from under the bar over 450ms and its
   contents rise 12px in a 40ms stagger. Opaque throughout: a fade let the
   hero's headline show through the menu's links mid-arrival (seen at
   120ms), and no text is ever translucent. Leaving is quicker and
   unstaggered. Reduced motion: instant. */
@media (prefers-reduced-motion: no-preference) {
  .wing-v2 .navsheet {
    clip-path: inset(0 0 100% 0);
    transition: clip-path .3s var(--wing-ease), visibility 0s linear .3s;
  }
  .wing-v2 .navsheet.is-open {
    clip-path: inset(0);
    transition: clip-path .45s var(--wing-ease), visibility 0s;
  }
  .wing-v2 .navsheet__primary li, .wing-v2 .navsheet__cta, .wing-v2 .navsheet__more { transform: translateY(12px); }
  .wing-v2 .navsheet.is-open .navsheet__primary li,
  .wing-v2 .navsheet.is-open .navsheet__cta,
  .wing-v2 .navsheet.is-open .navsheet__more { transform: none; transition: transform .45s var(--wing-ease) 60ms; }
  .wing-v2 .navsheet.is-open .navsheet__primary li:nth-child(2) { transition-delay: 100ms; }
  .wing-v2 .navsheet.is-open .navsheet__primary li:nth-child(3) { transition-delay: 140ms; }
  .wing-v2 .navsheet.is-open .navsheet__primary li:nth-child(4) { transition-delay: 180ms; }
  .wing-v2 .navsheet.is-open .navsheet__cta { transition-delay: 220ms; }
  .wing-v2 .navsheet.is-open .navsheet__more { transition-delay: 260ms; }
  .wing-v2 .nav__burger span { transition: transform .45s var(--wing-ease); }
}

@media (max-width: 899px) {
  /* `.nav__right` only: `.nav .btn` also matched the sheet's own CTA, which
     is why the menu never showed "Check eligibility". */
  .wing-v2 .nav__links, .wing-v2 .nav__right .btn { display: none !important; }
  .wing-v2 .nav__burgerw { display: block; }
  .wing-v2 .nav__burger { display: block; }
}
@media (min-width: 900px) {
  .wing-v2 .navsheet { display: none; }
}

/* So a jump to `#fit` (the rail, the mobile action bar, the header CTA, or a
   plain in-page link) lands the section below the fixed 76px bar rather than
   under it. Applies to the native anchor jump and to `scrollIntoView()`
   alike - unscoped, because the bar is fixed on every page, not just home. */
html { scroll-padding-top: var(--wing-header-h); }

/* =========================================================================
   1B. MARGIN RAIL (desktop, home page, >=1200px) and MOBILE ACTION BAR
   (<900px, home page). Both are injected by motion.js's `railnav()`, not by
   build-chrome.py - they have to appear and disappear with scroll position,
   which is behaviour, not chrome. Both share the same two signals: hidden
   until the hero has scrolled past, hidden again once the closing band (or,
   for the action bar, the footer) comes into view. See build-state.md,
   known gap 8 - direction 01 always wanted a sticky index in the margin;
   this is that, built as its own fixed element instead of a real sticky
   column, because the home page is not a two-column grid.
   ========================================================================= */

/* The desktop margin rail lived here. Removed 19 September 2026, the client
   rejected the idea three times over. The nav keeps every link and its
   permanent CTA. The scroll-progress rule in §1 above is NOT a substitute:
   it never renders, because build-chrome.py emits its widget empty and
   Elementor drops empty HTML widgets. Full reasoning, and that correction,
   above `railnav()` in motion.js. */

/* The mobile action bar: the one place the primary action is reachable on a
   phone without opening the menu, so it has to survive scroll and never
   double up with the closing band's own CTA or sit over the footer. */
/* 28 Sep: one rule for home and inner pages (v2-rail.css used to repeat
   it), and crisp rather than a translucent blurred slab. 29 Sep: always
   ink. Taking the ground beneath it changed its colour at every band edge
   while it was on screen, which read as flashing. Hidden means slid off the
   bottom edge AND `visibility: hidden`, so an invisible bar is never a tab
   stop. */
.wing-v2 .mobilecta { display: none; }
@media (max-width: 899px) {
  .wing-v2 .mobilecta {
    display: block; position: fixed; left: 0; right: 0; bottom: 0; z-index: 35;
    padding: 12px var(--wing-gutter) calc(12px + env(safe-area-inset-bottom));
    background-color: var(--wing-ink); border-top: 1px solid var(--wing-rule-dark);
    transform: translateY(100%); visibility: hidden; pointer-events: none;
  }
  .wing-v2 .mobilecta.is-visible { transform: none; visibility: visible; pointer-events: auto; }
  /* Plain markup, not an Elementor widget - motion.js writes a bare
     `<a class="btn btn--primary">`, so the shared `.wing-v2 a.btn` rule in
     §0 already supplies height, radius and colour; this only stretches it
     full width inside the bar's own gutters. */
  .wing-v2 .mobilecta a.btn { display: flex; width: 100%; }
  /* A control reached by Tab lands above the bar, not under it. */
  html { scroll-padding-bottom: 88px; }
}
@media (max-width: 899px) and (prefers-reduced-motion: no-preference) {
  .wing-v2 .mobilecta { transition: transform .45s var(--wing-ease), visibility 0s linear .45s; }
  .wing-v2 .mobilecta.is-visible { transition: transform .45s var(--wing-ease), visibility 0s; }
}

/* =========================================================================
   2. HERO
   ========================================================================= */

.wing-home .hero.e-con {
  position: relative; background: var(--ink);
  --min-height: 100svh; min-height: 100svh !important;
  overflow: hidden; isolation: isolate; color: #fff;
  --justify-content: flex-end; padding: 0 0 clamp(48px, 7vw, 96px);
}
/* The four layer divs are one HTML widget, a SIBLING of .wing-shell, not a
   child of it (see image_hero()'s docstring). The widget's own box is
   `position:relative; height:0` by default, so `.hero__media{position:
   absolute;inset:0}` resolved against a 0x0 box and rendered nothing - this
   project already paid for that bug once with hero_plate(). Fix: make the
   WIDGET itself the absolutely-positioned, full-bleed layer. */
.wing-home .hero > .hero__layers,
.wing-home .hero > .hero__layers > .elementor-widget-container {
  position: absolute; inset: 0; z-index: 0;
}
.wing-home .hero__media { position: absolute; inset: 0; z-index: 0; }
.wing-home .hero__media img {
  width: 100%; height: 100%; object-fit: cover; object-position: 60% 55%;
  filter: saturate(.8) brightness(.82); transform-origin: 60% 60%;
}
@media (prefers-reduced-motion: no-preference) {
  .wing-home .hero__media img { animation: wingZoom 24s linear infinite alternate; }
}
@keyframes wingZoom { from { transform: scale(1); } to { transform: scale(1.06); } }
.wing-home .hero__tint { position: absolute; inset: 0; z-index: 1; pointer-events: none; background: rgba(53,120,140,.22); mix-blend-mode: multiply; }
.wing-home .hero__wash {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: radial-gradient(640px circle at var(--wx,50%) var(--wy,40%), rgba(184,205,225,.14), transparent 70%);
  transition: background .9s var(--wing-ease);
}
.wing-home .hero__scrim {
  position: absolute; inset: 0; z-index: 3; pointer-events: none;
  background: linear-gradient(180deg, rgba(7,16,30,.28) 0%, rgba(7,16,30,.12) 35%, rgba(7,16,30,.70) 78%, rgba(7,16,30,.92) 100%),
              linear-gradient(90deg, rgba(7,16,30,.45) 0%, rgba(7,16,30,0) 60%);
}
.wing-home .hero > .wing-shell { position: relative; z-index: 4; width: 100%; }
/* 7fr of 12 with a 40px gutter, which is 651px at 1440 - not 58.33% of the
   full 1156, because the hero's second column is empty and nothing shrinks
   the first one back down. */
.wing-home .hero__grid.e-con { --flex-direction: row; --row-gap: 40px; --column-gap: 40px; --align-items: flex-end; }
@media (min-width: 900px) {
  .wing-home .e-con.hero__grid { display: grid; grid-template-columns: 7fr 5fr; gap: 0 40px; }
  .wing-home .e-con.hero__grid > * { --width: 100%; width: 100%; }
}
.wing-home .hero__col.e-con { --width: 58.33%; flex: 0 1 58.33%; }
/* `.e-con .elementor-widget.elementor-widget { margin-block-end: 0 }` is
   (0,3,0) and zeroes every widget margin in the build, so these two were
   silently doing nothing and the hero stack ran 62px tighter than the mock.
   Naming `.elementor-widget` here takes the rule to (0,4,0). */
.wing-home .hero .hero__h1.elementor-widget { margin-bottom: 26px; }
.wing-home .hero .hero__lede.elementor-widget { margin-bottom: 36px; }
.wing-home .hero__actions.e-con { --flex-direction: row; --row-gap: 12px; --column-gap: 12px; --flex-wrap: wrap; }

/* Entrance. CSS only, so no-JS shows the page and the H1 is never gated
   behind a script. `both` holds the from-state through the delay. */
@keyframes wingEnter { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: no-preference) {
  .wing-home .enter { animation: wingEnter .9s var(--wing-ease) both; animation-delay: var(--d, 0ms); }
}
.wing-home .hero__h1 { --d: 120ms; }
.wing-home .hero__lede { --d: 420ms; }
.wing-home .hero__actions { --d: 560ms; }
.wing-home .hero .place { --d: 900ms; }

/* =========================================================================
   3. FIGURES
   ========================================================================= */

/* ONE UNIT, 28 Sep 2026. The band was three stacked things: a heading with
   an empty right half, a figure row whose "$100K" ran to 12px of its
   divider, and a 14px qualifier floating under a full-width rule. Now the
   heading and its qualifier share the top row (qualifier on the third
   column line, first baselines aligned) and the four figures hang from ONE
   hairline beneath them. The HTML widget steps aside (`display: contents`)
   so its grid and paragraph are placed on the shell's own grid; DOM order,
   and therefore screen-reader order, is unchanged. This section is now the
   only owner of the band's geometry; v2-bands.css no longer overrides it. */
/* 29 Sep, rhythm audit: `* .8` was its own one-off fraction with no note
   explaining it, next to a file where every other fraction is - fields'
   *.5/*.25, the ledger's *.28, the shared-ground collapse's *.32, all
   commented. `--wing-band-tight` is the token this site already uses for
   exactly this role (`.wing-section--tight`, both closing bands' own
   clamp), so figures joins that vocabulary instead of inventing another
   ratio - and, being smaller than *.8's 81px, it costs the page a little
   less height too. */
.wing-home .figs.e-con { padding: var(--wing-band-tight) 0; }
.wing-home .figs .wing-shell.e-con {
  display: grid; grid-template-columns: minmax(0, 1fr); column-gap: 64px; align-items: start;
}
.wing-home .figs .elementor-widget-html { display: contents; }
.wing-home .figs__head.e-con { margin: 0; grid-row: 1; }
.wing-home .figs .display .elementor-heading-title { max-width: 22ch; }
.wing-home .figs__qual {
  grid-row: 2; margin: var(--wing-space-24) 0 0; padding: 0; border: 0; max-width: 56ch;
  font-size: var(--wing-small); line-height: 1.6; color: var(--muted);
}

/* The figure size is the smaller of the token and what the column can
   hold: 2.75em is "$100K"'s set width, so the widest figure always ends
   inside its own column and the space either side of every divider is the
   same. `--fig-cap` carries the token down so the container can reuse it. */
.wing-home .figs { --fig-cap: var(--wing-figure); }
.wing-home .figs__grid {
  grid-row: 3; container-type: inline-size; position: relative;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 32px; row-gap: 28px;
  margin-top: var(--wing-space-32); padding-top: 20px; border-top: 1px solid var(--rule);
  --wing-figure: min(var(--fig-cap), calc((100cqi - 32px) / 2 / 2.75));
}
/* The one teal mark: the same 2px lead the cost panel opens with. */
.wing-home .figs__grid::before {
  content: ""; position: absolute; left: 0; top: -1px; width: 64px; height: 2px; background: var(--teal);
}
/* Two columns, one divider down the middle of the gap. */
.wing-home .figs__grid::after {
  content: ""; position: absolute; left: 50%; top: 0; bottom: 0; width: 1px; background: var(--rule);
}
.wing-home .fig { display: block; min-width: 0; }
.wing-home .fig__num {
  display: block; margin-bottom: 14px;
  font-size: var(--wing-figure); font-weight: var(--wing-display-weight);
  letter-spacing: var(--wing-ls-fig); line-height: .9; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* One colour for all four. Teal on one figure read as inconsistency, not
   emphasis; the `accent` class the generator still emits is inert. */
.wing-home .fig__num.accent { color: inherit; }
/* The unit on a static figure ("1-8%"): computed at --wing-small so it is no
   new type size, drawn at the figure's proportion with a transform (a
   transform does not change layout, so the figure still never wraps). */
.wing-home .fig__unit {
  display: inline-block; font-size: var(--wing-small); font-weight: 500; letter-spacing: 0; line-height: 1;
  transform-origin: left bottom; transform: scale(calc(var(--wing-figure) / 16px * .42)) translateY(-.4em);
}
/* Block, with its own leading: inline, a wrapped label took the parent's
   28.8px strut and two 14px lines sat 29px apart. */
.wing-home .fig__label { display: block; font-size: var(--wing-caption); line-height: 1.5; color: var(--muted); }

@media (min-width: 900px) {
  .wing-home .figs .wing-shell.e-con { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  /* First baselines shared: the qualifier starts on the heading's first
     line. (Last-baseline alignment pushed the qualifier ABOVE the heading
     at 1024, where it runs six lines against the heading's three.) */
  .wing-home .figs__head.e-con { grid-column: 1 / 3; grid-row: 1; align-self: baseline; }
  .wing-home .figs__qual { grid-column: 3 / 5; grid-row: 1; margin: 0; align-self: baseline; }
  /* 64px gap: the dividers then fall on the ledger's Wing column and the
     downside grid's column line (v2-bands.css, COLUMN CONVERGENCE). */
  .wing-home .figs__grid {
    grid-column: 1 / -1; grid-row: 2; margin-top: var(--wing-space-64); padding-top: 0;
    grid-template-columns: repeat(4, minmax(0, 1fr)); grid-template-rows: auto auto; column-gap: 64px;
    --wing-figure: min(var(--fig-cap), calc((100cqi - 192px) / 4 / 2.75));
  }
  .wing-home .figs__grid::after { content: none; }
  /* Subgrid: every label starts on the same line whatever its figure did. */
  .wing-home .fig {
    display: grid; grid-row: 1 / 3; grid-template-rows: subgrid; row-gap: 16px;
    position: relative; padding-top: var(--wing-space-32);
  }
  .wing-home .fig__num { align-self: end; margin: 0; }
  .wing-home .fig__label { align-self: start; }
  /* Centred in the gap, dropping from the rule: 32px either side. */
  .wing-home .fig:not(:first-child)::before {
    content: ""; position: absolute; left: -32px; top: 0; bottom: 0; width: 1px; background: var(--rule);
  }
}
@supports not (grid-template-rows: subgrid) {
  .wing-home .fig { display: block; }
  .wing-home .fig__num { margin-bottom: 16px; }
}

/* =========================================================================
   4. FIT-CHECK — a guide, not an assessment. Nothing is stored or sent.
   ========================================================================= */

.wing-home .fit.e-con { padding-block: var(--wing-band); }
.wing-home .fit__grid.e-con { --flex-direction: row; --row-gap: 64px; --column-gap: 64px; --align-items: flex-start; }
.wing-home .fit__intro.e-con { --width: 41.66%; flex: 0 1 41.66%; }
.wing-home .fit__panelwrap { --width: 58.33%; flex: 0 1 58.33%; }
.wing-home .fit .display .elementor-heading-title { margin-bottom: 20px; }
.wing-home .fit__panel { position: relative; }
/* Plain-content fallback (defect #1, crossbrowser-a11y.md). Real, permanent
   HTML - not a <noscript> tag, not a class an absent script would need to
   remove - so it needs no CSS rule at all to be visible: this block simply
   has none. The interactive stepper is the one gated: `#fitform` ships with
   `.fit__noscript` and stays hidden, along with its progress rule and meta
   row, until motion.js's own first act (`form.classList.remove('fit__noscript')`,
   unmodified) proves a script is running. Once that happens `.fit__plain`
   hides in turn, so a visitor only ever sees one version, never both, never
   neither. */
.wing-home .fit__plain-list { margin: 0 0 20px; padding: 0; list-style: none; display: grid; gap: 14px; max-width: 48ch; }
.wing-home .fit__plain-list strong { font-weight: 500; color: var(--navy); }
.wing-home .fit__plain-links a { text-decoration: underline; text-underline-offset: 4px; color: inherit; }
.wing-home .fit__panel:has(#fitform.fit__noscript) .fit__progress,
.wing-home .fit__panel:has(#fitform.fit__noscript) .fit__meta,
.wing-home .fit__panel:has(#fitform.fit__noscript) .fit__chips { display: none; }
.wing-home #fitform.fit__noscript { display: none; }
.wing-home .fit__panel:has(#fitform:not(.fit__noscript)) .fit__plain { display: none; }
.wing-home .fit__progress { height: 1px; background: var(--rule); margin-bottom: 36px; position: relative; }
.wing-home .fit__progress i { position: absolute; left: 0; top: -1px; height: 3px; width: 100%; transform: scaleX(.3333); transform-origin: left; background: var(--teal); transition: transform .18s var(--wing-ease); }
.wing-home .fit fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
.wing-home .fit legend { padding: 0; display: block; }
.wing-home .fit__step { display: none; }
.wing-home .fit__step.is-on { display: block; animation: wingEnter .5s var(--wing-ease); }
.wing-home .fit__q { font-size: var(--wing-h3); font-weight: 500; letter-spacing: -.02em; margin-bottom: 24px; }
.wing-home .fit__opts { display: grid; gap: 10px; grid-template-columns: 1fr 1fr; }
.wing-home .fit__opt {
  display: flex; align-items: center; gap: 14px; min-height: 48px; padding: 9px 18px;
  border: 1px solid var(--rule); border-radius: 2px; background: #fff;
  text-align: left; font: inherit; color: inherit; cursor: pointer;
  transition: border-color .3s var(--wing-ease), background .3s var(--wing-ease), transform .45s var(--wing-ease);
}
@media (hover: hover) and (pointer: fine) {
  .wing-home .fit__opt:hover { border-color: var(--teal); transform: translateY(-2px); }
}
.wing-home .fit__opt i { width: 10px; height: 10px; border: 1px solid var(--navy); border-radius: 50%; flex: none; }
.wing-home .fit__opt.is-picked { border-color: var(--teal); background: var(--teal); color: #fff; }
.wing-home .fit__opt.is-picked i { background: #fff; border-color: #fff; }
.wing-home .fit__meta { display: flex; justify-content: space-between; align-items: center; margin-top: 26px; font-size: var(--wing-caption); color: var(--muted); }
.wing-home .fit__back { text-decoration: underline; text-underline-offset: 4px; background: none; border: 0; font: inherit; color: inherit; cursor: pointer; padding: 0 4px; }
.wing-home .fit__result { display: none; }
.wing-home .fit__result.is-on { display: block; animation: wingEnter .6s var(--wing-ease); }
.wing-home .fit__result h3 { font-size: var(--wing-h2); font-weight: var(--wing-display-weight); letter-spacing: var(--wing-ls-display); line-height: 1.04; margin: 0 0 18px; max-width: 18ch; }
.wing-home .fit__result p { max-width: 48ch; margin: 0 0 28px; }
.wing-home .fit__chips { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 22px; }
.wing-home .fit__chips span { font-size: var(--wing-caption); padding: 6px 12px; border: 1px solid var(--rule); border-radius: 100px; background: #fff; }
.wing-home .fit__note { font-size: var(--wing-caption); color: var(--muted); margin: 28px 0 0; }

/* =========================================================================
   5. LEDGER — one comparator at a time. A loan, never a grant.
   ========================================================================= */

.wing-home .ledger.e-con { padding-block: var(--wing-band); }
.wing-home .ledger__head.e-con { --flex-direction: row; --row-gap: 64px; --column-gap: 64px; --align-items: flex-end; margin-bottom: 36px; }
.wing-home .ledger__head-a.e-con { --width: 41.66%; flex: 0 1 41.66%; }
.wing-home .ledger__head-b { --width: 58.33%; flex: 0 1 58.33%; }
.wing-home .seg { display: inline-flex; border: 1px solid var(--rule); border-radius: 100px; padding: 4px; gap: 4px; }
/* base.css puts a 44px floor under every button. The mock's segmented
   control is 40, kept at 40 here: growing the button itself would also grow
   this container's own visible rounded capsule (its height is intrinsic to
   the tallest child), which is exactly the "visually larger" the brief rules
   out. Defect #3 (crossbrowser-a11y.md) measured the real tap target at 40px
   tall; `::after` below adds an invisible 2px strip top and bottom - hit
   area only, nothing painted, sitting in the capsule's own 4px padding so it
   never pokes outside it - clearing 44px without one visible pixel moving.
   This is WCAG 2.5.8's own "sufficient inactive space" provision, not a
   workaround; the trade is that a bounding-box-only checker (this project's
   `ops/crossbrowser.py` included) cannot see it and will keep reporting
   40px - see the fix report for how this was verified instead. */
.wing-home .seg button {
  position: relative;
  /* 44px, launch checklist phase 4. Was `height: 40px; min-height: 40px`,
     which is under the floor on the short axis. The `::after { inset: -2px 0 }`
     below was meant to make up the difference and does not: probed with
     elementFromPoint at 390px, the real hit area measured 40px, 0px above and
     0px below. The pill grows by 4px; nothing else moves. */
  height: 44px; min-height: 44px; padding: 0 18px; border: 0; border-radius: 100px; background: none;
  font: inherit; font-size: var(--wing-action-size); font-weight: 500; color: var(--muted);
  white-space: nowrap; cursor: pointer; transition: background .4s var(--wing-ease), color .4s var(--wing-ease);
}
.wing-home .seg button::after { content: ""; position: absolute; inset: -2px 0; }
.wing-home .seg button[aria-checked="true"] { background: var(--navy); color: #fff; }
.wing-home .seg__label { display: block; margin-bottom: 12px; color: var(--muted); }
.wing-home .ledger__rows { border-top: 1px solid var(--rule); }
/* align-items: start, not baseline — the key, value and comparator columns
   are three different type sizes on purpose, and baseline alignment moved
   their top edges by as much as 8px against each other (ops/align.py's
   RAGGED check). Starting all three at the same y reads as one row; the
   small amount of extra leading above the smaller columns' glyphs is the
   standard, unremarkable trade-off. */
.wing-home .lrow { display: grid; grid-template-columns: 3fr 5fr 4fr; gap: 32px; padding: 18px 0; border-bottom: 1px solid var(--rule); align-items: start; }
.wing-home .lrow__k { font-size: var(--wing-caption); color: var(--muted); }
.wing-home .lrow__wing { font-size: var(--wing-h3); font-weight: 500; letter-spacing: -.02em; line-height: 1.25; }
.wing-home .lrow__cmp { color: var(--muted); }
.wing-home .ledger__colhead { display: grid; grid-template-columns: 3fr 5fr 4fr; gap: 32px; padding-bottom: 14px; color: var(--muted); }
.wing-home .ledger__colhead span:nth-child(2) { color: var(--teal); }

/* =========================================================================
   6. FIELDS — three rows, one sticky image column
   ========================================================================= */

.wing-home .focus.e-con { padding: calc(var(--wing-band) * .8) 0 calc(var(--wing-band) * .6); }
.wing-home .focus__grid.e-con { --flex-direction: row; --row-gap: 64px; --column-gap: 64px; --align-items: flex-start; }
.wing-home .focus__col.e-con, .wing-home .focus__picwrap { --width: 50%; flex: 0 1 50%; min-width: 0; }
/* The figure's HTML widget (`.focus__picwrap`) is the actual flex/grid item
   in `.focus__grid` - `.focus__frame`, the <figure> inside it, is not, so
   sizing rules on `.focus__frame` never reached it and the widget collapsed
   to content width (measured: 91px). Size and stick the widget and the
   picture itself instead. */
.wing-home .focus__picwrap { --width: 100%; width: 100%; }
@media (min-width: 900px) {
  .wing-home .e-con.focus__grid { display: grid; grid-template-columns: 6fr 6fr; gap: 0 64px; }
  /* Elementor writes `width: var(--width)` on every child, and `--width: 50%`
     inside a grid column that is ALREADY half the row halves it again - the
     text column measured 319px against the mock's 546. In grid mode the child
     fills its column. */
  .wing-home .e-con.focus__grid > * { --width: 100%; width: 100%; }
  /* The sticky element has to be the GRID ITEM, because a grid item's sticky
     containing block is its grid area - the full height of the row. Stuck to
     `.focus__pic` instead, the containing block was `.focus__picwrap`, which
     is exactly as tall as the picture, so there was no travel and the image
     scrolled away while three rows of text were still to come. */
  .wing-home .focus__picwrap { position: sticky; top: 110px; align-self: start; }
}
.wing-home .focus .display .elementor-heading-title { margin-bottom: 8px; }
.wing-home .focus__lead.elementor-widget-text-editor > p { max-width: 40ch; margin-bottom: 24px; }
.wing-home .frow { padding: 18px 0 22px; border-top: 1px solid var(--rule); transition: opacity .5s var(--wing-ease); }
html.is-pointing .wing-home .focus__list.has-hover .frow:not(.is-on) { opacity: .42; }
.wing-home .frow h3 { font-size: var(--wing-h2); font-weight: var(--wing-display-weight); letter-spacing: var(--wing-ls-display); line-height: 1.02; margin: 6px 0 10px; }
.wing-home .frow p { max-width: 40ch; margin: 0; }
.wing-home .frow small { display: block; font-size: var(--wing-caption); color: var(--muted); margin-top: 6px; }
.wing-home .focus__pic { position: relative; width: 100%; aspect-ratio: 4/5; overflow: hidden; border-radius: 2px; background: var(--navy); }
.wing-home .focus__pic img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity .6s var(--wing-ease); }
.wing-home .focus__pic img.is-on { opacity: 1; }
.wing-home .focus__pic::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, transparent 55%, var(--tint, rgba(10,52,106,.45)) 100%); transition: background .6s var(--wing-ease); }
.wing-home .focus__pic.tint-pounamu { --tint: rgba(15,61,51,.5); }
/* base.css italicises <figcaption>; the mock's caption is upright. */
.wing-home .focus__cap { margin-top: 12px; font-size: var(--wing-caption); font-style: normal; line-height: var(--wing-lh-body); color: var(--muted); }

/* =========================================================================
   7. STEPPER — Wing funds from stage 02, not stage 04
   ========================================================================= */

.wing-home .stepper.e-con { padding-block: var(--wing-band); }
/* 64px, not 80px, and the reason is alignment rather than spacing. Every
   band on the home page splits the same shell, but each split was authored
   with its own gutter, so the vertical where the second column starts never
   repeated: measured at 1440, nine different positions, five of them in
   near-miss pairs only 2 to 8px apart. That is the worst of both, too close
   to read as a second structure and too far to read as one, and it is the
   kind of fault a reader feels as "off" without being able to name it.
   Four of those positions were brought onto shared lines in v2-bands.css;
   this was the last outlier, at 670 against the fit-check's and field rows'
   661. At 64px it lands on 661 exactly, so four bands now share one edge.
   Horizontal only: no band top, height or text gap changes. */
.wing-home .stepper__grid.e-con { --flex-direction: row; --row-gap: 64px; --column-gap: 64px; --align-items: flex-start; }
.wing-home .stepper__left.e-con { --width: 41.66%; flex: 0 1 41.66%; position: sticky; top: 120px; }
.wing-home .stepper__right { --width: 58.33%; flex: 0 1 58.33%; min-width: 0; }
.wing-home .stepper__left .display .elementor-heading-title { margin-bottom: 18px; }
/* 5 Oct: the display size, not the figure token. At 95px it was a third
   display size within 15px of the H1 (80) and the figures (87.6), taking home
   to 11 sizes against the budget of 10. */
.wing-home .stepper__num {
  display: block; font-size: var(--wing-display); font-weight: var(--wing-display-weight);
  letter-spacing: var(--wing-ls-fig); line-height: .9; margin: 36px 0 24px;
  color: #fff; font-variant-numeric: tabular-nums;
}
/* 0.32em of the figure is 30px — an eleventh type size. Pinned to --small. */
.wing-home .stepper__num small { font-size: var(--wing-small); letter-spacing: 0; color: rgba(255,255,255,.5); margin-left: 10px; }
.wing-home .track { position: relative; width: 2px; height: 180px; background: var(--rule-dark); }
/* 29 Sep: .7s, in step with the numeral's roll, not a .18s snap. */
.wing-home .track i { position: absolute; left: 0; top: 25%; width: 2px; height: 75%; transform: scaleY(0); transform-origin: top; background: var(--lightblue); transition: transform .7s var(--wing-ease); }
.wing-home .track b { position: absolute; left: 22px; top: 25%; transform: translateY(-50%); white-space: nowrap; font-weight: 500; font-size: var(--wing-caption); color: var(--lightblue); }
.wing-home .track b::before { content: ""; position: absolute; left: -25.5px; top: 50%; transform: translateY(-50%); width: 9px; height: 9px; border-radius: 50%; background: var(--teal); }
.wing-home .stage { padding: 30px 0; border-top: 1px solid var(--rule-dark); min-height: 30vh; display: flex; flex-direction: column; justify-content: center; transition: opacity .5s var(--wing-ease); }
/* .38 measured 3.55:1 and AA asks for 4.5 on 18px text, so an inactive
   stage's body copy was not readable, which brief 01 predicted and asked to
   be measured: "make inactive text readable while retaining a more prominent
   active number/rule. Do not depend on hovering or scrolling for the
   information to become legible."

   White composited onto ink at .38 gives rgb(101,107,116) and 3.55:1. At .55
   it gives rgb(143,148,154) and 6.20:1, which clears AA with margin while
   still reading as clearly secondary to the active stage at full opacity.
   The active stage keeps its numeral and rule, so the focus is not lost, and
   a reader who never scrolls can now read every stage.

   /application-process/ uses .65 on the same component and already measured
   8.27:1, so this brings the two into the same territory rather than
   inventing a value. */
/* .38 -> .55 -> .65. The .55 step was made on a contrast reading taken
   from the stage HEADING only, which is 28px and therefore judged against
   the 3:1 large-text threshold - it passed at 6.21:1 and the smaller text
   beside it was never measured. Re-measured 23 Sep against the actual
   rendered ground: the 18px description sat at 3.75:1 and the 13px label
   at 4.21:1, both under the 4.5:1 this size needs. .65 is what
   /application-process/ already used and measures 4.79:1 and 5.44:1 here.
   The dim still reads as "not the current stage", which is its job. */
.wing-home .stage:not(.is-on) { opacity: .65; }
.wing-home .stage .label { color: var(--lightblue); margin-bottom: 14px; display: block; }
.wing-home .stage h3 {
  /* Brief 11 V03: these were `--wing-h2`, the SECTION-heading size, so four
     of them in a row each looked like a new section of the page and the
     stepper competed with the heading above it. A stage is a component
     inside a section, not a headline. The number and the active-stage cue
     do the emphasis instead, which is what they are for.

     29 Sep: the clamp(24px,2vw,28px) that followed was its own stray size -
     28px at 1440, an eleventh against the ten-size ceiling. `--wing-h3` (the
     fixed 24px this file already gives `.drow h3` two sections down, the
     same dark ink chapter) is the same "component inside a section" register
     without the extra size. The 28px contrast reading noted above still
     holds: 24px is still >=24px, still large text at the 3:1 threshold, and
     the colour - the only thing contrast reads - is unchanged. */
  font-size: var(--wing-h3);
  font-weight: var(--wing-display-weight);
  letter-spacing: var(--wing-ls-h3);
  line-height: 1.2;
  margin: 0 0 14px;
}
.wing-home .stage p { max-width: 40ch; color: rgba(255,255,255,.72); margin: 0; }
/* .8, not .58: inside a stage at .65 it composited to about 3.4:1 (5 Oct). */
.wing-home .stage--out .label { color: rgba(255,255,255,.8); }

/* =========================================================================
   8. DOWNSIDE — runs with the stepper as one ink chapter
   ========================================================================= */

.wing-home .down.e-con { padding-block: 0 var(--wing-band); }
.wing-home .down .display .elementor-heading-title { margin-bottom: 14px; }
.wing-home .down__lead.elementor-widget-text-editor > p { max-width: 44ch; margin-bottom: 36px; }
.wing-home .down__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 64px; border-top: 1px solid var(--rule-dark); }
.wing-home .drow { padding: 18px 0; border-bottom: 1px solid var(--rule-dark); }
.wing-home .drow--wide { grid-column: 1 / -1; border-top: 1px solid var(--rule-dark); margin-top: -1px; }
.wing-home .drow--wide p { max-width: 60ch; }
/* Brief 11 V03: `--wing-lh-body` is 1.6, so these 24px titles were set on
   38.4px of leading - a paragraph's worth of air inside a two-word heading,
   and the reason a wrapped one took so much vertical space. 1.2 is the
   component-heading role. */
.wing-home .drow h3 { font-size: var(--wing-h3); font-weight: var(--wing-display-weight); letter-spacing: -.02em; line-height: 1.2; margin: 0 0 8px; }
.wing-home .drow p { max-width: 44ch; margin: 0; color: rgba(255,255,255,.72); }

/* =========================================================================
   9. CLOSING BAND — over Lake Hāwea
   ========================================================================= */

.wing-home .close.e-con {
  position: relative; background: var(--ink);
  --min-height: 64svh; min-height: 64svh !important;
  overflow: hidden; isolation: isolate; color: #fff;
  --justify-content: center; padding: clamp(56px, 6vw, 88px) 0;
}
/* Same fix as the hero (§2): the layer divs are one HTML widget, a sibling
   of .wing-shell, and the widget's own box - not `.close__media` - needs to
   be the thing that fills the band. */
.wing-home .close > .close__layers,
.wing-home .close > .close__layers > .elementor-widget-container {
  position: absolute; inset: 0; z-index: 0;
}
.wing-home .close__media { position: absolute; inset: 0; z-index: 0; }
/* 29 Sep: was saturate(.6) brightness(.62) under a .9/.35/.85 wash, which
   read as a flat dark slab. The photo is now near its own light and the ink
   sits only where the copy is: an ellipse behind the heading block, a smaller
   one behind the buttons and place name, and a fade in from ink at the top
   and out to the footer's ink at the bottom. 76% keeps cloud, ridge and lake
   all in a 2.5:1 crop. Every run measured >=4.5:1 from pixels, 320-1920px.
   Mirrored in v2-pages.css §6 for inner pages. */
.wing-home .close__media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 76%; filter: saturate(.8) brightness(.88); }
.wing-home .close__tint { position: absolute; inset: 0; z-index: 1; pointer-events: none; background: rgba(53,120,140,.12); mix-blend-mode: multiply; }
.wing-home .close__scrim {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background:
    radial-gradient(460px 230px at calc(50% + 530px) 61%, rgba(7,16,30,.55) 0%, rgba(7,16,30,.3) 55%, rgba(7,16,30,0) 100%),
    radial-gradient(820px 330px at calc(50% - 300px) 50%, rgba(7,16,30,.7) 0%, rgba(7,16,30,.58) 45%, rgba(7,16,30,.28) 75%, rgba(7,16,30,0) 100%),
    linear-gradient(90deg, rgba(7,16,30,.36) 0%, rgba(7,16,30,.18) calc(50% + 100px), rgba(7,16,30,0) calc(50% + 400px)),
    linear-gradient(180deg, rgba(7,16,30,.6) 0%, rgba(7,16,30,0) 24%, rgba(7,16,30,0) 70%, rgba(7,16,30,.7) 100%);
}
/* Stacked, the copy runs top to bottom over the sky and cloud, so the ink
   holds through the text and eases off behind the buttons, where the ridge
   and lake are. The crop is full height here, so centre it. */
@media (max-width: 899px) {
  .wing-home .close__media img { object-position: 50% 50%; }
  .wing-home .close__scrim { background: linear-gradient(180deg, rgba(7,16,30,.64) 0%, rgba(7,16,30,.68) 30%, rgba(7,16,30,.7) 58%, rgba(7,16,30,.42) 70%, rgba(7,16,30,.36) 86%, rgba(7,16,30,.62) 100%); }
}
.wing-home .close > .wing-shell { position: relative; z-index: 3; width: 100%; }
.wing-home .close__grid.e-con { --flex-direction: row; --row-gap: 48px; --column-gap: 48px; --align-items: flex-end; }
.wing-home .close__col.e-con { --width: 58.33%; flex: 0 1 58.33%; }
.wing-home .close .display .elementor-heading-title { margin-bottom: 16px; }
.wing-home .close__meta { display: flex; gap: 28px; flex-wrap: wrap; font-size: var(--wing-caption); color: rgba(255,255,255,.72); margin-top: 26px; }
.wing-home .close__actions.e-con { --flex-direction: row; --row-gap: 12px; --column-gap: 12px; --justify-content: flex-end; --flex-wrap: wrap; --width: 41.66%; flex: 0 1 41.66%; }

/* =========================================================================
   10. FOOTER — one row: logo and blurb left, links right
   ========================================================================= */

/* 28 Sep: presence and order without new content. The mark was 46px wide
   with the tagline touching it. Now: a readable mark, a clear gap, the links
   as two columns whose top meets the mark's top, a hairline, then the legal
   row on the SAME column grid, so "Privacy" and "Website terms" sit under
   the two link columns. Both rows share `--foot-col` / `--foot-gap`, which is
   what makes that alignment exact rather than approximate. */
.wing-v2 .foot.e-con {
  --foot-col: clamp(150px, 14vw, 200px);
  --foot-gap: clamp(24px, 3vw, 48px);
  --foot-mark-w: clamp(96px, 8vw, 116px);
  background: var(--wing-ink); color: #fff;
  /* Trimmed from 72/64 to 56/44 at 1440 after the pass: the rebuilt footer
     had added 171px to every page, and page length is a client concern. */
  padding: clamp(40px, 4vw, 56px) 0 clamp(24px, 2.2vw, 32px);
  border-top: 1px solid var(--wing-rule-dark); font-size: var(--wing-small);
}
.wing-v2 .foot .e-con { --row-gap: 0px; --column-gap: 0px; }
.wing-v2 .foot__row, .wing-v2 .foot__bottom {
  display: grid; grid-template-columns: minmax(0, 1fr) var(--foot-col) var(--foot-col);
  column-gap: var(--foot-gap);
}
.wing-v2 .foot__row { align-items: start; }
.wing-v2 .foot__brand { grid-column: 1; }
.wing-v2 .foot__mark { display: block; width: var(--foot-mark-w); height: auto; }
.wing-v2 .foot__brand p { color: rgba(255,255,255,.6); font-size: var(--wing-caption); line-height: 1.6; max-width: 44ch; margin: 24px 0 0; }
.wing-v2 .foot__links, .wing-v2 .foot__legal {
  grid-column: 2 / 4; display: grid; grid-template-columns: var(--foot-col) var(--foot-col);
  column-gap: var(--foot-gap); margin: 0; padding: 0; list-style: none;
}
/* Column-major, four rows: About/How/What/Eligibility, then Application
   process/FAQ/Contact. The negative margin lifts the first line's cap height
   level with the top of the mark (measured, see docs/build-state.md). */
.wing-v2 .foot__links { grid-template-rows: repeat(4, auto); grid-auto-flow: column; row-gap: 8px; line-height: 1.5; margin-top: -6px; }
.wing-v2 .foot__links a { text-decoration: none; color: rgba(255,255,255,.78); position: relative; }
.wing-v2 .foot__links a::after { content: ""; position: absolute; left: 0; right: 0; bottom: -4px; height: 1px; background: currentColor; transform: scaleX(0); transform-origin: left; transition: transform .3s var(--wing-ease); }
.wing-v2 .foot__links a:hover { color: #fff; }
.wing-v2 .foot__links a:hover::after { transform: scaleX(1); }
.wing-v2 .foot__bottom {
  align-items: baseline; margin-top: clamp(32px, 3.2vw, 44px); padding-top: 20px;
  border-top: 1px solid var(--wing-rule-dark);
  font-size: var(--wing-caption); color: rgba(255,255,255,.5);
}
.wing-v2 .foot__copy { grid-column: 1; }
.wing-v2 .foot__nb { white-space: nowrap; }
/* The legal entity line (1 Oct 2026): under the copyright, same size and
   tone, so it reads as one block of small print. */
.wing-v2 .foot__co { display: block; margin-top: 6px; }
.wing-v2 .foot__bottom a { color: inherit; text-decoration: none; }
.wing-v2 .foot__bottom a:hover { color: #fff; }
/* Phones: everything in one column, links and legal pair on a two-column
   list rather than a ragged wrap. `.foot` in each selector outranks the
   <900px touch-target block further down this file. */
@media (max-width: 719px) {
  .wing-v2 .foot .foot__row, .wing-v2 .foot .foot__bottom,
  .wing-v2 .foot .foot__links, .wing-v2 .foot .foot__legal { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .wing-v2 .foot .foot__brand, .wing-v2 .foot .foot__copy,
  .wing-v2 .foot .foot__links, .wing-v2 .foot .foot__legal { grid-column: 1 / -1; }
  .wing-v2 .foot .foot__links { margin-top: 20px; }
  .wing-v2 .foot .foot__legal { margin-top: 4px; }
}
/* "How funding works" and "Application process" need 133px and 138px at
   16px; a 320px screen gives each column 124px. */
@media (max-width: 359px) {
  .wing-v2 .foot__links { font-size: var(--wing-caption); }
}

/* =========================================================================
   11. REVEAL — 40px / 0.8s, scroll-linked where supported, IO once elsewhere
   -------------------------------------------------------------------------
   The from-state lives behind BOTH `@supports` and the `.wing-motion--scroll`
   class that motion.js adds, so a page with no JS, or a reduced-motion
   request, never has a hidden element. The IO path applies its from-state
   from JS for the same reason.
   ========================================================================= */


/* Entrance reveal: REMOVED, 23 September 2026 (brief 11, V01).
   A `@supports (animation-timeline: view())` block here used to animate
   `[data-reveal]` opacity against the scroll position. Scroll-timeline
   progress is a function of POSITION, not time, so text parked mid-range
   stayed part-transparent for as long as the reader stayed still, and text
   in the last screen of a document could never finish at all. Measured
   while stationary: 0.436 on a section heading, 0.487 on a comparison row.
   The whole system is gone - motion.js no longer marks anything, and no
   rule selects `[data-reveal]`. Essential text is opaque on first paint. */

@media (prefers-reduced-motion: reduce) {
  .wing-home .hero__media img { animation: none; }
  .wing-home .enter { animation: none; opacity: 1; transform: none; }
  /* Defect #2, crossbrowser-a11y.md: these two sat outside every guard above
     (they're toggled by fitcheck()'s own `.is-on` class, not by `[data-reveal]`
     or `.enter`), so a reduced-motion visitor still got the fit-check's step
     and result entrances. Neither ever hides content - `display: block` stays
     - only the `wingEnter` keyframe is removed. */
  .wing-home .fit__step.is-on, .wing-home .fit__result.is-on { animation: none; }
  .wing-v2 *, .wing-v2 *::before, .wing-v2 *::after { transition-duration: .001ms !important; }
}

/* =========================================================================
   12. NARROW VIEWPORTS — a first-class target, not a fallback
   ========================================================================= */

@media (max-width: 899px) {
  .wing-home { --wing-band: 52px; }

  .wing-home .hero__grid.e-con { --flex-direction: column; --row-gap: 0px; --column-gap: 0px; }
  .wing-home .hero__col.e-con { --width: 100%; flex: 1 1 auto; }
  .wing-home .hero__actions.e-con { --flex-direction: column; --row-gap: 12px; --column-gap: 12px; }
  .wing-home .hero__actions .btn.elementor-widget-button { width: 100%; }
  .wing-home .hero__actions .btn .elementor-button { width: 100%; }
  .wing-home .place { margin-top: 20px; }

  /* Figures below 900px: §3 is written mobile-first and owns them. */

  .wing-home .fit__grid.e-con { --flex-direction: column; --row-gap: 32px; --column-gap: 32px; }
  .wing-home .fit__intro.e-con, .wing-home .fit__panelwrap { --width: 100%; flex: 1 1 auto; }
  .wing-home .fit__opts { grid-template-columns: 1fr; }
  .wing-home .fit__q { font-size: var(--wing-lede); }
  /* Tap target, where there is a thumb. On the desktop mock this row is 22px. */
  .wing-home .fit__back { min-height: 44px; }

  .wing-home .ledger__head.e-con { --flex-direction: column; --row-gap: 24px; --column-gap: 24px; --align-items: stretch; margin-bottom: 24px; }
  .wing-home .ledger__head-a.e-con, .wing-home .ledger__head-b { --width: 100%; flex: 1 1 auto; }
  .wing-home .lrow, .wing-home .ledger__colhead { grid-template-columns: 1fr; gap: 6px; }
  .wing-home .lrow { padding: 14px 0; }
  /* Without the comparator's name the second value is unattributed once the
     columns stack. */
  .wing-home .lrow__cmp::before { content: attr(data-name) ": "; font-size: var(--wing-caption); }

  .wing-home .focus__grid.e-con { --flex-direction: column; --row-gap: 32px; --column-gap: 32px; }
  .wing-home .focus__col.e-con, .wing-home .focus__picwrap { --width: 100%; flex: 1 1 auto; }
  .wing-home .focus__picwrap { order: -1; }
  .wing-home .focus__pic { position: relative; top: 0; aspect-ratio: 4/3; }
  .wing-home .frow { padding: 14px 0 18px; }

  .wing-home .stepper__grid.e-con { --flex-direction: column; --row-gap: 32px; --column-gap: 32px; }
  .wing-home .stepper__left.e-con, .wing-home .stepper__right { --width: 100%; flex: 1 1 auto; position: relative; top: 0; }
  .wing-home .stepper__num, .wing-home .track { display: none; }
  .wing-home .stage { min-height: 0; padding: 24px 0; }
  .wing-home .stage:not(.is-on) { opacity: 1; }

  .wing-home .down__grid { grid-template-columns: 1fr; }
  .wing-home .drow { padding: 14px 0; }

  .wing-home .close__grid.e-con { --flex-direction: column; --row-gap: 28px; --column-gap: 28px; --align-items: stretch; }
  .wing-home .close__col.e-con, .wing-home .close__actions.e-con { --width: 100%; flex: 1 1 auto; }
  .wing-home .close__actions.e-con { --flex-direction: column; --justify-content: flex-start; }
  .wing-home .close__actions .btn.elementor-widget-button, .wing-home .close__actions .btn .elementor-button { width: 100%; }

  .wing-v2 .foot.e-con { padding: 40px 0 12px; }
  .wing-v2 .foot__bottom { margin-top: 28px; padding-top: 12px; }
  /* Every tap target clears 44px, both sides (defect #3, crossbrowser-
     a11y.md: "FAQ" measured 32px wide). 28 Sep: left-aligned with no side
     padding, so each link's text starts on the column line instead of 6px
     inside it; the 44px rows carry the spacing, so no row gap. The top
     offset re-levels the first line with the mark inside a 44px row. */
  .wing-v2 .foot__links a, .wing-v2 .foot__bottom a {
    display: inline-flex; align-items: center; justify-content: flex-start;
    min-height: 44px; min-width: 44px;
  }
  .wing-v2 .foot__links { row-gap: 0; margin-top: -16px; }
}

/* Nothing on this site scales, blurs, bounces, spins or parallaxes. */

/* =========================================================================
   THE LAST PIXEL OF THE 320px OVERFLOW
   Brief: docs/design-refinement/07-shared-design-and-motion.md §3.

   That brief measured the home document at about 327px inside a 320px
   viewport and identified `.fig__unit`. Reducing the percent sign's scale at
   the narrowest widths took it from 7px to 1px, and the last pixel is this
   control: `.seg` measured left 24, width 296.7, right 320.7.

   The cause is `display: inline-flex` with no shrink allowed. The two
   comparator buttons' intrinsic widths add up to more than the 272px between
   the 24px gutters, so the control cannot fit and nothing lets it give.
   Allowing it to wrap at the narrowest widths lets the two options stack
   instead, which a segmented control can do on a phone without losing
   anything. `max-width: 100%` keeps it inside the gutter even if a longer
   comparator label is added later.

   Global `overflow-x: hidden` is deliberately NOT used: brief 07 rules it
   out, and it would clip focus rings.
   ========================================================================= */
@media (max-width: 360px) {
  .wing-home .seg { max-width: 100%; flex-wrap: wrap; }
}

/* =========================================================================
   NUMBER RANGES: the dash gets its own air
   Paired with `ranges()` in motion.js, which wraps the dash of any numeric
   range in `<span class="rng">` at runtime. See that function for why this
   cannot be done in CSS alone and why it is not baked into the markup.

   Everything below is in `em`, so one rule serves every size the site sets a
   range at, from the 14px closing-band meta to the 95px figures.

   `letter-spacing: normal` is the important half. The display tracking of
   about -4% is what closes the gap around the dash in the first place, so
   cancelling it on the span alone returns the space without loosening the
   digits, which keeps the tight setting the brand spec asks for.
   ========================================================================= */
/* EVERY SIZE: cancel the tracking, and nothing else.

   This one declaration is the whole fix at body size, and it scales itself
   to the harm. The damage is the negative tracking closing the gap either
   side of the dash, so cancelling it returns exactly as much space as was
   taken: about 0.09px per side on 14px text, where nothing was wrong and
   nothing visibly changes, and about 3.8px per side on the 95px figures,
   where the dash was touching both digits.

   Adding margin at every size was tried first and was wrong. At 14px the
   extra air made "3-4 rounds a year" read as a spaced hyphen or a minus
   sign rather than a range, which is a different error in the opposite
   direction. Small ranges should stay tight. */
.wing-v2 .rng { letter-spacing: normal; }

/* DISPLAY SIZES ONLY: real air, and a shorter mark.

   Above roughly 40px the returned tracking alone is still not enough,
   because Archivo's en dash is long and at that scale it reads as a rule
   drawn through the pair rather than a relationship between them. These two
   contexts are the only ones on the site that set a range that large. */
.wing-v2 .fig__num .rng,
.wing-v2 .display .rng {
  display: inline-block;
  margin-inline: .10em;
  transform: scaleX(.72);
}

/* =========================================================================
   "ON THIS PAGE" ROW (FAQ). Built by `topicRow()` in motion.js.
   NOT STICKY, and that is the whole point: brief 09 §5 permits "a simple,
   non-sticky" row and forbids "another fixed navigation strip alongside the
   header and mobile CTA". A fixed rail was built for this site, rejected
   three times, and removed. This scrolls away with the page.

   Type and rules only, no card, no background, no shadow. It reads as a
   caption to the page rather than a control panel.
   ========================================================================= */
/* Brief 11 V06: this ran 72..1368 at 1440 while every question and answer
   below it ran 142..1298 - a 70px overhang on both sides that made the
   navigation look like it belonged to a different page.

   The cause was an honest but wrong reconstruction of the shell. The real
   `.wing-shell` is `max-width: var(--wing-max)` with `padding-inline:
   var(--wing-gutter)`, so its TEXT starts one gutter inside a 1300px box.
   This rule subtracted the gutters from the width instead, which produces a
   1296px box whose text starts at its very edge. Reproduced exactly here:
   measured 142/64/51/38px left edge at 1440/1280/1024/768, matching the
   questions to the pixel at each. The border still spans the full box, so
   the rule reads as a divider, which is what the brief allows. */
/* A fixed grid, not a wrapping row (5 Oct 2026). Wrapped inline, the six
   links broke onto a different number of lines in the fallback face than
   in Archivo (2 against 3 at 1440), so the swap pushed the page down
   39px, and on a phone they made a seven-line ragged block. Fixed columns
   decide the line count from the viewport alone: one column on a phone,
   two from 600px, three beside the lead from 1024px. */
.wing-v2 .topicrow {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 14px;
  width: min(var(--wing-max), 100%);
  margin-inline: auto;
  padding: 26px var(--wing-gutter) 30px;
  border-bottom: 1px solid var(--rule);
}
.wing-v2 .topicrow__lead {
  font-size: var(--wing-label);
  font-weight: 500;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
}
.wing-v2 .topicrow__list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 12px clamp(18px, 2.2vw, 30px);
  margin: 0;
  padding: 0;
  list-style: none;
}
@media (min-width: 600px) {
  .wing-v2 .topicrow__list { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 1024px) {
  .wing-v2 .topicrow {
    grid-template-columns: auto minmax(0, 1fr);
    align-items: baseline;
    column-gap: clamp(32px, 4vw, 64px);
  }
  .wing-v2 .topicrow__list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.wing-v2 .topicrow__list a {
  font-size: var(--wing-caption);
  color: var(--navy);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
  transition: border-color .3s var(--wing-ease), color .3s var(--wing-ease);
}
/* 44px hit area without a 44px box. These links are 18px tall and sit in a
   wrapped row; padding them out to 44 would make the row three times taller
   and undo the alignment work in V06. The pseudo-element extends the
   TAPPABLE area to 44px while the link keeps its size and position, which
   is the usual way to satisfy the target-size rule on inline navigation.
   `inset` is negative, so it never affects layout. */
.wing-v2 .topicrow__list a { position: relative; }
.wing-v2 .topicrow__list a::after {
  content: "";
  position: absolute;
  inset: -13px -8px;
}
.wing-v2 .topicrow__list a:hover,
.wing-v2 .topicrow__list a:focus-visible { border-bottom-color: var(--wing-teal); }
.wing-v2 .topicrow__list a:focus-visible { outline: 2px solid var(--wing-teal); outline-offset: 4px; }

@media (max-width: 899px) {
  .wing-v2 .topicrow { padding: 20px var(--wing-gutter) 22px; gap: 12px; }
}

/* The navigation links inherited `transition: all .4s ease` from Elementor,
   not from this theme: measured on the live page, not read from source. Two
   things are wrong with it and both are small. `all` animates every property
   that ever changes, including ones that should snap. And `.4s ease` is
   neither the 450ms nor the `cubic-bezier(.22,.61,.36,1)` the brand spec
   sets for hover, so the primary navigation was the one place on the site
   whose hover moved on a different curve from everything else.

   Named properties, house duration, house curve. Nothing about the visual
   design changes; only what animates and how it eases. */
/* 29 Sep: link colour changes in the same step as the bar's ground (no
   colour transition), so a link is never mid-fade against a bar that has
   already changed. Hover movement keeps the house 450ms. */
@media (prefers-reduced-motion: no-preference) {
  .wing-v2 .nav__links a,
  .wing-v2 .nav a {
    transition: opacity .45s var(--wing-ease), transform .45s var(--wing-ease);
  }
  .wing-v2 .nav .btn.elementor-widget-button .elementor-button {
    transition: transform .45s var(--wing-ease);
  }
}

/* The comparator's unselected option gave no hover feedback at all. Measured
   before and after hovering: colour, background and opacity all identical,
   so the one control on the page whose whole purpose is to be switched did
   not look switchable. The selected option is a filled navy pill, so the
   unselected one only needs to acknowledge the pointer, not compete: the
   same 2 to 3px lift every other control on the site uses, on the house
   curve, plus a quiet ground. No shadow, no scale. */
.wing-v2 .seg button[aria-checked="false"] {
  transition: background-color .45s var(--wing-ease), color .45s var(--wing-ease),
              transform .45s var(--wing-ease);
}
/* The token is `--wing-paleblue`; `--wing-pale` does not exist and an
   undefined custom property silently resolves to nothing, which is why the
   first version of this rule lifted the button but never tinted it.

   Brief 11 V11: hover and focus-visible were one selector, and gating that
   on a fine pointer would have taken the KEYBOARD's feedback away with the
   touch screen's. They are separated so the ground and colour apply to
   both, and only the 2px lift is conditional. */
.wing-v2 .seg button[aria-checked="false"]:hover,
.wing-v2 .seg button[aria-checked="false"]:focus-visible {
  background: var(--wing-paleblue);
  color: var(--wing-navy);
}
@media (hover: hover) and (pointer: fine) {
  .wing-v2 .seg button[aria-checked="false"]:hover { transform: translateY(-2px); }
}
.wing-v2 .seg button[aria-checked="false"]:focus-visible { transform: translateY(-2px); }

/* 5 Oct 2026, pre-handover design audit: on a phone every ledger row labels
   itself, so the stacked "WING / GOVERNMENT GRANT" header headed nothing. */
@media (max-width: 899px) {
  .wing-home .ledger__colhead { display: none; }
}

/* ==== v2-bands.css ==== */
/* =========================================================================
   Wing R&D Fund - home body bands, v2.

   OWNED BY THE BODY-BAND WORK. Loads after components-v2.css, so anything
   here wins on equal specificity. Nav, hero and the site chrome live in
   components-v2.css and are NOT to be edited from here.

   Scope: figures, fit-check, ledger, fields, stepper, downside, closing band.
   Target: docs/directions/05-coastline.html, the approved mock.

   Every rule below was found by diffing live measurements against the mock
   at 1440x900 and 375x812 (ops/measure.py + a targeted Playwright probe of
   each band's boxes) - desktop already matched the mock to the pixel before
   any of this was added. Of the fixes below, only FIGURES is a
   desktop-triggered correction (client-flagged column/rule geometry, not a
   mock diff); the rest are mobile-triggered except the closing band's,
   which is dormant at 1440 only because min-height already floors the
   section there.
   ========================================================================= */

/* =========================================================================
   FIGURES — equal column measure, an intentional unit mark, a rule that
   ties the band together.
   -------------------------------------------------------------------------
   Flagged twice by the client: the numerals "look out of pocket" and "the
   padding is not right". Measured at 1440x900 against components-v2.css
   section 3 ("FIGURES") before any of this was added:

     column    content width   number starts at
     $100K     261px           142
     1-8%      233px           460
     6-8       233px           749
     3-4       261px           1038

   THREE FAULTS, ALL GEOMETRIC, addressed below in order.

   1) UNEQUAL COLUMNS. components-v2.css gives every `.fig` 28px of padding
      on both sides, then strips it from the outer edge only
      (`:first-child{padding-left:0}`, `:last-child{padding-right:0}`),
      which is exactly what makes the two end columns 28px wider than the
      two middle ones (261 vs 233). Of the brief's two ways to equalise
      this — pad every column alike and centre the dividers, or gap the
      grid and draw the dividers in the gaps — the second is taken, because
      it is the only one that keeps the first figure flush with the H2 and
      the qualifier at x=142; centring the dividers would pull it in by
      28px and break that alignment for no gain. `column-gap` replaces the
      padding, and each divider becomes a `::before` on the `.fig` it
      precedes, offset by half the gap so it sits centred in the empty gap
      rather than as a cell border flush against the next column's number.
      56px is the old 28+28 kept as one figure, not a new spacing decision —
      it is also exactly the average of the four old content widths
      (247px), so the row's visual density is unchanged even though every
      column is now 247px. Scoped to >=900px: components-v2.css's own 2x2
      mobile block already gives every `.fig` an even, unpadded 24px
      column-gap and top-border dividers, and is left untouched.

   2) THE PERCENT SIGN. `.fig__unit` is pinned to --wing-small (16px) next
      to a ~95px numeral at 1440, rendering as a 15x17px mark sitting on
      the numeral's own baseline — the one static figure in this dataset
      that ends in a unit character ("1-8%"; wingbuild.py's figures_v2()
      wraps only that trailing character in <small>, which is what keeps
      it from wrapping). "$100K" never has this problem, because a COUNTED
      figure's suffix is written by motion.js's counter at the figure's
      own full size. Making the unit genuinely proportional by deriving
      its font-size from --wing-figure would add an eleventh rendered size
      to a page capped at ten (tokens.css) — --wing-figure is itself a
      fluid clamp(), so font-size:calc(var(--wing-figure) * k) would paint
      a fresh, undocumented px value at every viewport. So the computed
      font-size stays --wing-small (still the token already on the page)
      and the VISUAL size is set with `transform: scale()` instead, driven
      by `calc(var(--wing-figure) / 16px * .42)` — a ratio taken directly
      from the figure's own clamp, so "proportional at every width" is
      exact rather than a second, hand-tuned breakpoint set. `translateY`
      is listed BEFORE `scale` in the same transform, which under CSS's
      composition order means it is evaluated first, in the unscaled local
      space, and then carried through the scale — so the lift off the
      baseline grows with the figure too, instead of a fixed px nudge that
      would look right at one width and wrong at the rest.

   3) THE QUALIFIER'S RULE. `.figs__qual` carries both the reading measure
      (max-width:80ch, ~641px at 1440) and its own top border, so the rule
      stops at x=783 while the grid above it runs to x=1298 — dead centre
      of nowhere, reading as unfinished rather than as a close to the band.
      Taken in favour of spanning it full width: this page uses a
      full-measure hairline as its connective tissue everywhere else (the
      ledger rows, the drawdown rail), and a rule that stops halfway is the
      odd one out, not a deliberate quieter choice. The paragraph keeps its
      80ch measure without a second element: `max-width:none` lets the box
      run the full auto width of the grid (so the border, drawn on the
      border-box, spans it), and `padding-right: calc(100% - 80ch)` narrows
      only the CONTENT box back down to 80ch — padding sits inside the
      border-box and never affects where the border itself is drawn. Once
      the container is already narrower than 80ch (as it always is on
      mobile), the calc() goes negative and the browser clamps it to 0, so
      nothing changes there.
   ========================================================================= */

/* SUPERSEDED 28 Sep 2026. The rules this history describes (minmax(0,1fr)
   columns, the 64px gap with gap-centred dividers, the scaled unit, the
   qualifier's padding-right measure) now live, recomposed, in
   components-v2.css §3, which is the band's single owner. The 64px gap and
   the column convergence it serves are kept exactly. The overflow at 320px
   (see THE 320px STATISTIC OVERFLOW below) is solved there too: the figure
   is sized from its own column, so it cannot outgrow it at any width. */

/* --- fit-check: the hidden chips tray was really collapsing -----------
   components-v2.css correctly sets `.fit__chips{display:flex;margin-bottom:
   22px}` to reserve the mock's gap between the (empty, hidden-until-answered)
   chips tray and the progress rule even while no chip is showing - matching
   the mock's own `.fit__chips{display:flex;...}` overriding the browser's
   default `[hidden]{display:none}`. But a later, global rule in the same
   file, `.wing-v2 [hidden]{display:none!important}` (added for the mobile
   nav and other genuinely-hidden panels), also matches this element and
   carries !important, so it was winning and the chips tray was collapsing to
   0 height with no reserved margin. Effect: the whole fit-check panel
   (progress bar, questions, meta row) rendered 22px higher than the mock at
   every viewport, and because that panel is the taller column at mobile
   (where the two columns stack), the whole page ran 22px short from here
   down. Re-declaring the same selector with !important, after the file that
   set the conflicting rule, restores the mock's spacing without touching
   components-v2.css. */
.wing-home .fit__chips { display: flex !important; }

@media (max-width: 899px) {
  /* --- fields: the sticky-image/rows gap should not shrink on mobile ---
     The mock's `.focus__grid` sets `gap:64px` once, at the desktop rule, and
     its mobile media query only changes `grid-template-columns` - it never
     touches the gap, so 64px carries through unchanged when the two columns
     stack. components-v2.css's mobile block instead sets `--row-gap:32px`
     (matching the pattern used for every OTHER band's mobile gap, all of
     which the mock genuinely does halve), which doesn't apply here and cost
     32px between the sticky picture and the three field rows. */
  .wing-home .focus__grid.e-con { --row-gap: 64px; --column-gap: 64px; }

  /* --- readiness: stage padding is 28px on the mock's mobile rule, not 24 -
     `.stage{min-height:0;padding:28px 0}` in the mock's own narrow-viewport
     block; components-v2.css carried 24px into the equivalent rule. 4px a
     side across 4 stacked stages is the other 32px the mobile page was
     running short. */
  .wing-home .stage { padding: 28px 0; }
}

/* =========================================================================
   FIELDS — rebuilt without photography.
   -------------------------------------------------------------------------
   docs/design-opportunities.md §2, §3.3, implemented from
   docs/directions/sections/fields-without-photography.html. Replaces the
   sticky-image `.focus` band on the HOME PAGE ONLY — build-home.py's
   fields() now calls wingbuild.py's new `fields_no_photo()`, not
   `fields_sticky()`. components-v2.css section 6 ("FIELDS — three rows, one
   sticky image column") is untouched and keeps styling /what-we-fund/, which
   still calls `fields_sticky()` unchanged; `.wing-home .fields` never
   matches an inner page, so nothing here reaches it either.

   FIELD-NAME SIZE. The prototype specifies a size between the section H2 and
   body copy (its own `--field: clamp(28px,2.9vw,42px)`). The home page's
   ten-size ceiling (tokens.css) has no such size and this file may not add
   an eleventh, so `.row__n` is set at the existing `--wing-h3` (24px) — the
   site's established "subordinate heading" size (`.lrow__wing`, `.drow h3`,
   `.fit__q`), which is exactly the register a name below the H2 needs.
   ========================================================================= */

/* Padding and margins below are deliberately tighter than the standalone
   prototype's own values. The prototype was never checked against the home
   page's screen-count budget (docs/build-state.md, "master plan's budget is
   <=6.5 and <=9"); this file is, and the structure it specifies — rows with
   the description beside the name, then the two lists, then the closing
   line — is unchanged. Fitting a fourth block of content (two lists) into a
   band that used to hold one column of text is what pays for the sticky
   image, not generous whitespace on top of it. */
.wing-home .fields.e-con { padding: calc(var(--wing-band) * .5) 0 calc(var(--wing-band) * .25); }
@media (max-width: 899px) {
  .wing-home .fields.e-con { padding: calc(var(--wing-band) * .22) 0 calc(var(--wing-band) * .05); }
}
.wing-home .fields__head.e-con { max-width: 48ch; margin-bottom: clamp(24px, 3vw, 36px); }
.wing-home .fields__head .display .elementor-heading-title { margin-bottom: 14px; }
.wing-home .fields__lead.elementor-widget-text-editor > p { max-width: 44ch; margin: 0; }

.wing-home .fields .rows { border-top: 1px solid var(--rule); }
.wing-home .fields .row {
  /* 64px gutter, not 48: with 5fr/7fr this puts the value column on exactly
     the same vertical as the fit-check's answer column at every width >=900.
     See COLUMN CONVERGENCE at the foot of this file. */
  display: grid; grid-template-columns: 5fr 7fr; gap: 64px; padding: 22px 0;
  border-bottom: 1px solid var(--rule); transition: opacity .5s var(--wing-ease);
}
/* Hover/focus dim, CSS only — this markup carries no id for motion.js to
   bind to, so the sticky version's JS-driven dim is reproduced with :has(). */
html.is-pointing .wing-home .fields .rows:has(.row:hover) .row:not(:hover),
.wing-home .fields .rows:has(.row:focus-within) .row:not(:focus-within) { opacity: .42; }
.wing-home .fields .row__k .label { display: block; margin-bottom: 6px; }
.wing-home .fields .row__n {
  display: block; font-size: var(--wing-h3); font-weight: var(--wing-display-weight);
  letter-spacing: var(--wing-ls-h3); line-height: 1.15; transition: color .4s var(--wing-ease);
}
.wing-home .fields .row:hover .row__n, .wing-home .fields .row:focus-within .row__n { color: var(--teal); }
.wing-home .fields .row--pounamu:hover .row__n, .wing-home .fields .row--pounamu:focus-within .row__n { color: var(--pounamu); }
.wing-home .fields .row__v p { max-width: 50ch; margin: 0; }
.wing-home .fields .row__v small { display: block; font-size: var(--wing-caption); color: var(--muted); margin-top: 6px; }

/* 8fr/4fr, not 7fr/5fr: at a 64px gutter this puts the exclusions column on
   exactly the same vertical as the ledger comparison's comparator column at
   every width >=900, and gives the five one-word exclusions a rule 3.5x their
   longest item instead of 4.3x. COLUMN CONVERGENCE, foot of this file. */
.wing-home .fields .lists { display: grid; grid-template-columns: 8fr 4fr; gap: 64px; margin-top: clamp(40px, 4.4vw, 64px); }
.wing-home .fields .lists h3 { font-size: var(--wing-h3); font-weight: 500; letter-spacing: -.02em; margin: 0 0 4px; }
.wing-home .fields .lists .sub { font-size: var(--wing-caption); margin: 0 0 10px; }
/* One rule per list, not one per row (ops/align.py found 7 and 8 rules
   clustered here — a hairline under every item in two adjacent lists reads
   as a fence, not a list). The ul's own top border is the group's rule;
   rows below it are separated by padding alone, matching the house cap of
   at most two rules visible outside the ledger, where the rules are the
   table. */
.wing-home .fields .lists ul { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--rule); }
.wing-home .fields .lists li {
  display: grid; grid-template-columns: 20px 1fr; gap: 12px; align-items: baseline;
  padding: 5px 0; font-size: var(--wing-small);
}
.wing-home .fields .lists li i { font-style: normal; font-size: var(--wing-caption); color: var(--muted); }
.wing-home .fields .lists li em { font-style: italic; color: var(--muted); }

.wing-home .fields__foot { display: block; margin: 10px 0 0; font-size: var(--wing-caption); max-width: 70ch; }

@media (max-width: 899px) {
  /* Narrow enough that both lists stack sequentially rather than side by
     side (12 items in all), so every row of spacing here is paid twice.
     The sub-line under each heading goes: the numbered/dashed items carry
     the information, "Eligible in any of the three fields" is what the H2
     three lines up already said. */
  /* Brief 11 V08. This block used to set `.row` padding/gap, `.lists`
     gap/margin-top, `.lists li` padding/font-size and `.fields__foot`
     margin-top as well. A later block in this same file, in this same
     `max-width: 899px` query, at the same specificity, set every one of
     those eight declarations again to different values and won on source
     order alone. Nothing rendered wrong, but two blocks disagreed about
     the same eight properties and only one of them was doing anything,
     so editing the wrong one changed nothing and looked like a CSS
     mystery. The dead declarations are deleted; the live ones stay where
     they are. What survives here is what nothing else claims. */
  .wing-home .fields .row { grid-template-columns: 1fr; }
  .wing-home .fields .row__k .label { margin-bottom: 4px; }
  .wing-home .fields .row__v small { margin-top: 4px; }
  .wing-home .fields .lists { grid-template-columns: 1fr; }
  .wing-home .fields .lists .sub { display: none; }
  .wing-home .fields .lists h3 { margin-bottom: 2px; }
}

/* =========================================================================
   MILESTONE DRAWDOWN — a compact strip in "02 How funding works", directly
   beneath the ledger rows (wingbuild.py's `milestone_drawdown()`).
   -------------------------------------------------------------------------
   ONE LINE, NOT THREE (ops/align.py found the band's top rule, a meter track
   and a meter fill inside 44 vertical px). There is no separate meter bar
   any more: the marker rail itself — `.drawdown__steps`'s own `::before`
   (track) and `::after` (fill) — is the drawn-funds meter, so the only real,
   DOM-visible hairline this component draws is `.drawdown`'s own top
   border, which doubles as the one hairline the brief allows between this
   diagram and the ledger rows above it.

   Hairlines and type only: no chart library, no SVG, no canvas. The track
   and the fill draw left to right once on arrival, over 1s on the house
   curve, and nothing else moves. No JavaScript: the default state below IS
   the drawn state, so a browser without `animation-timeline: view()`
   (Firefox, per components-v2.css §11's own note) or a reduced-motion
   request just renders the strip complete and static, with no
   IntersectionObserver fallback needed either way. motion.js binds every
   interactive component by element id and never touches this markup, so
   none is given.
   ========================================================================= */

/* REDRAWN 28 Sep 2026. Grey 6px dots centred over centred labels, with
   left-aligned captions below, read as a default component. Now: 10px rings
   with a 2px teal stroke (the agreement, where drawing starts, filled), a
   1px grey track with a 2px teal line over it, and every label and note
   left-aligned to its own ring. On desktop the four rings sit on the page's
   four-column grid (64px gutter), so they land on the same verticals as the
   figures' columns and the ledger's Wing column above. Below 900px the
   rail turns vertical: rings left, labels right.

   The rail is drawn per step (each step's ::before/::after runs from its
   ring's centre to the next ring's centre), so the same rule serves both
   orientations and the explorable state below can undraw the segments past
   a selected stage. The teal line draws as the diagram arrives, on a named
   view timeline on `.drawdown` itself (a 1px element's own `view()` range
   is 1px long, which is why the last attempt was never seen), line only,
   reversible, inside @supports and no-preference. Every ring, label and
   note is static and opaque; without the feature the line is simply drawn.
   The block above records the component's earlier history. */
.wing-home .drawdown {
  --dd-node: 10px;   /* ring diameter */
  --dd-gap: 64px;    /* the page's column gutter */
  --dd-ring-top: 7px; /* mobile: centres the ring on a 24px label line */
  margin: 0; padding: 0; border: 0;
  view-timeline: --drawdown block;
}
.wing-home .drawdown__eyebrow { display: block; margin-bottom: var(--wing-space-24); }
.wing-home .drawdown__steps { position: relative; list-style: none; margin: 0; padding: 0; }
.wing-home .drawdown__step {
  position: relative; display: block; text-align: left;
  padding: 0 0 var(--wing-space-24) calc(var(--dd-node) + 20px);
}
.wing-home .drawdown__step:last-child { padding-bottom: 0; }
.wing-home .drawdown__tick {
  position: absolute; z-index: 1; left: 0; top: var(--dd-ring-top);
  width: var(--dd-node); height: var(--dd-node); box-sizing: border-box;
  border: 2px solid var(--teal); border-radius: 50%; background: var(--wing-white);
  transition: background-color .45s var(--wing-ease), border-color .45s var(--wing-ease), transform .45s var(--wing-ease);
}
.wing-home .drawdown__step:first-child .drawdown__tick { background: var(--teal); }
/* Track (1px grey) and line (2px teal), ring centre to next ring centre. */
.wing-home .drawdown__step:not(:last-child)::before,
.wing-home .drawdown__step:not(:last-child)::after {
  content: ""; position: absolute; z-index: 0;
  left: calc(var(--dd-node) / 2 - .5px); width: 1px;
  top: calc(var(--dd-ring-top) + var(--dd-node) / 2);
  bottom: calc(-1 * (var(--dd-ring-top) + var(--dd-node) / 2));
  background: var(--rule);
}
.wing-home .drawdown__step:not(:last-child)::after {
  left: calc(var(--dd-node) / 2 - 1px); width: 2px;
  background: var(--teal); transform-origin: top;
  transition: background-color .45s var(--wing-ease);
}
.wing-home .drawdown__step-body { display: block; }
.wing-home .drawdown__step-label { display: block; font-size: var(--wing-small); line-height: 1.5; font-weight: 500; color: var(--navy); }
.wing-home .drawdown__step-note { display: block; margin-top: 4px; font-size: var(--wing-caption); line-height: 1.5; color: var(--muted); }

/* The two qualifications are one note: same measure, same left edge as the
   rings, 4px between them and a clear pause before them. */
.wing-home .drawdown__meter-cap,
.wing-home .drawdown__caption { display: block; margin: 0; font-size: var(--wing-caption); line-height: 1.6; max-width: 56ch; }
.wing-home .drawdown__meter-cap { margin-top: var(--wing-space-32); }
.wing-home .drawdown__caption { margin-top: var(--wing-space-4); }

@media (min-width: 900px) {
  .wing-home .drawdown__steps { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); column-gap: var(--dd-gap); }
  .wing-home .drawdown__step { padding: calc(var(--dd-node) + 20px) 0 0; }
  .wing-home .drawdown__tick { top: 0; }
  .wing-home .drawdown__step:not(:last-child)::before,
  .wing-home .drawdown__step:not(:last-child)::after {
    top: calc(var(--dd-node) / 2 - .5px); bottom: auto; height: 1px;
    left: calc(var(--dd-node) / 2); width: calc(100% + var(--dd-gap));
  }
  .wing-home .drawdown__step:not(:last-child)::after {
    top: calc(var(--dd-node) / 2 - 1px); height: 2px; transform-origin: left;
  }
  .wing-home .drawdown__step-note { margin-top: 6px; }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .wing-home .drawdown__step:not(:last-child)::after {
      animation: wingRailDown linear both; animation-timeline: --drawdown;
    }
    /* Three segments in sequence, one continuous draw. */
    .wing-home .drawdown__step:nth-child(1)::after { animation-range: entry 60% contain 0%; }
    .wing-home .drawdown__step:nth-child(2)::after { animation-range: contain 0% contain 12%; }
    .wing-home .drawdown__step:nth-child(3)::after { animation-range: contain 12% contain 25%; }
    @media (min-width: 900px) {
      .wing-home .drawdown__step:not(:last-child)::after { animation-name: wingRailAcross; }
    }
  }
}
/* `scale`, not `transform`: the individual property composes with any
   transform, and never touches the rings or the text. */
@keyframes wingRailAcross { from { scale: 0 1; } to { scale: 1 1; } }
@keyframes wingRailDown { from { scale: 1 0; } to { scale: 1 1; } }


/* The ledger band's own bottom padding (components-v2.css §5) is the full
   `--wing-band` — right for the band it was written for, oversized now that
   the drawdown strip, with its own margin-top, is the last thing in it. */
/* 5 Oct: was .28 x band (28px), which left a thin white sliver between the
   pale cost panel and the pale band below; a tight band reads deliberate. */
.wing-home .ledger.e-con { padding-bottom: var(--wing-band-tight); }
@media (max-width: 899px) {
  /* --- the white gutter between two pale surfaces, 21 September 2026 -----
     Brief: docs/design-refinement/01-homepage.md, "tune transitions": the
     funding band ends with the pale `.wing-cost-preview` panel, immediately
     followed by the pale `.fields` band - "use the existing white gutter
     and section index to separate those ideas". Measured on the live page:
     at 1440 the desktop rule above gives a plainly visible 28px of white
     before the fields band's pale returns with its "03 What we fund" index.
     The old mobile multiplier (`--wing-band * .04`) computed to 2px, because
     --wing-band is itself floored at 64px on mobile - the two pale surfaces
     sat almost flush, with only the fields band's own ~11px top padding
     (pale, not white) before the index appeared. That is the one case brief
     09 §5 warns about: two same-background sections reading as one merged
     block. A fixed 20px replaces the multiplier here, because --wing-band
     cannot usefully grow past its own mobile floor. Adds under 20px to a
     mobile page already close to its height budget (docs/build-state.md),
     against a real continuity fault the brief names directly. */
  .wing-home .ledger.e-con { padding-bottom: 20px; }
}

/* --- closing band: the mock's `.close` carries NO padding at all ---------
   It relies entirely on `min-height:64svh` plus `align-items:center` to
   centre whatever content it holds within the band. components-v2.css adds
   `padding:clamp(56px,6vw,88px) 0` on top of that (defensive, from before
   the min-height fix landed - see docs/build-state.md, "18 Sep 2026, v2 home,
   rendering fixes"). At 1440 the content is still shorter than the min-height
   floor either way, so the extra padding is invisible there; at 375 the
   112px it adds is enough to push the band 22px past the floor, which is
   exactly the surplus this rule removes. */
.wing-home .close.e-con { padding: 0; }

/* September 2026: give the three fields a stronger typographic hierarchy.
   Reading one field should never fade the other two out of legibility. FIXED
   29 Sep: this used to reintroduce its own clamp(26px,2.3vw,32px) here,
   silently overriding the `--wing-h3` this file sets two hundred lines
   above at line 192 - exactly the eleventh size the comment beside that
   rule (line 158) says the ten-size ceiling has no room for. Full opacity
   on the untouched rows is what the "stronger hierarchy" needed; the size
   was never the fix. */
html.is-pointing .wing-home .fields .rows:has(.row:hover) .row:not(:hover),
.wing-home .fields .rows:has(.row:focus-within) .row:not(:focus-within) { opacity: 1; }
@media (max-width: 899px) {
  .wing-home .fields .row { padding: 18px 0; gap: 8px; }
  .wing-home .fields .lists { gap: 24px; margin-top: 28px; }
  .wing-home .fields .lists li { padding: 4px 0; font-size: 16px; }
  .wing-home .fields__foot { margin-top: 18px; }
}

/* Keep the full-width milestone graphic, then give its cost example a
   distinct, compact two-column composition and a useful onward link.
   ONE RULE, NOT TWO (28 Sep 2026): this block's own border-top sat 33px
   under the last ledger row's border, the client's "too many lines going
   across". The row's rule now closes the table and space alone opens the
   drawdown: a 64px pause, then 56px between the diagram and the panel. */
.wing-home .ledger__tools.e-con {
  display: grid; grid-template-columns: minmax(0, 1fr);
  gap: 56px; align-items: start; margin-top: var(--wing-space-64); padding-top: 0;
  border-top: 0;
}
.wing-home .ledger__tools > .e-con { --width: 100%; min-width: 0; }
.wing-home .wing-cost-preview {
  /* 64px column gutter, not 48: inside the panel's own 28px padding that is
     exactly the vertical the figures' second divider and the downside grid's
     column both sit on. COLUMN CONVERGENCE, foot of this file. */
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0 64px;
  padding: var(--wing-space-32); background: var(--pale); border-top: 2px solid var(--teal);
  color: var(--navy);
}
/* Panel heading: `--wing-h3`, not its own clamp (29 Sep fix) - the same
   subordinate-heading register as `.row__n`, `.drow h3` and `.fit__q`, and
   one fewer size on the ten-size ceiling. */
.wing-home .wing-cost-preview h3 {
  margin: 12px 0 22px; font-size: var(--wing-h3); line-height: 1.12;
  font-weight: 500; letter-spacing: var(--wing-ls-h3); color: inherit;
}
.wing-home .wing-cost-preview__result { margin-bottom: 28px; }
/* Result number: `--wing-h2`, not its own 48-64px clamp (29 Sep fix) - the
   same size the fit-check gives ITS result heading (v2-pages.css
   `.fit__result h3`), so "here is your number" reads as one register site-
   wide instead of two, and removes a stray size from the ten-size ceiling.
   /how-funding-works/'s own calculator result is bigger on purpose
   (`.illus__big` uses the full `--wing-figure`) because it is the page's
   main tool, not a homepage teaser under the drawdown. */
.wing-home .wing-cost-preview__result output {
  display: block; font-size: var(--wing-h2); font-weight: 500;
  line-height: 1; letter-spacing: var(--wing-ls-h2); font-variant-numeric: tabular-nums; color: var(--teal);
}
.wing-home .wing-cost-preview__result > span { display: block; font-size: var(--wing-caption); margin-top: 8px; }
.wing-home .wing-cost-preview__label { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; font-size: var(--wing-caption); }
.wing-home .wing-cost-preview__label output { font-size: var(--wing-body); font-weight: 500; font-variant-numeric: tabular-nums; }
/* The slider, 28 Sep 2026: a 2px track whose unfilled part is navy at 30%
   (the old --rule grey on the pale ground measured 1.2:1 and all but
   vanished at 1x), and a 20px ring thumb that fills teal on hover, drag
   and keyboard focus. The focus ring is drawn around the THUMB, 3px off
   it, like every other ring on the site, not around the 44px hit box. */
.wing-home .wing-cost-preview input[type="range"] {
  --cost-fill: 55.5556%; appearance: none; -webkit-appearance: none;
  width: 100%; height: 44px; padding: 0; margin: 4px 0 0; border: 0; border-radius: 0;
  background: linear-gradient(to right, var(--teal) var(--cost-fill), rgba(10, 52, 106, .3) var(--cost-fill)) center / 100% 2px no-repeat;
  cursor: pointer;
}
.wing-home .wing-cost-preview input[type="range"]::-webkit-slider-thumb {
  appearance: none; -webkit-appearance: none; box-sizing: border-box; width: 20px; height: 20px;
  border-radius: 50%; border: 2px solid var(--teal); background: var(--pale);
  transition: background-color .45s var(--wing-ease), box-shadow .45s var(--wing-ease);
}
.wing-home .wing-cost-preview input[type="range"]::-moz-range-thumb {
  box-sizing: border-box; width: 20px; height: 20px; border-radius: 50%; border: 2px solid var(--teal); background: var(--pale);
  transition: background-color .45s var(--wing-ease), box-shadow .45s var(--wing-ease);
}
.wing-home .wing-cost-preview input[type="range"]:is(:hover, :active, :focus-visible)::-webkit-slider-thumb { background: var(--teal); }
.wing-home .wing-cost-preview input[type="range"]:is(:hover, :active, :focus-visible)::-moz-range-thumb { background: var(--teal); }
/* `box-shadow: none` too, not just `outline: none` (29 Sep fix): base.css's
   site-wide focus ring puts its two-tone halo in a `box-shadow`, not an
   `outline`, so removing only the outline left that halo drawing round the
   INPUT's own box - the full 100%-wide, 44px touch target, not the visible
   thumb - a black bar across the whole slider. The thumb's own ring below
   is the only focus feedback this control needs. */
.wing-home .wing-cost-preview input[type="range"]:focus-visible { outline: none; box-shadow: none; }
.wing-home .wing-cost-preview input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px var(--pale), 0 0 0 5px var(--teal); }
.wing-home .wing-cost-preview input[type="range"]:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 3px var(--pale), 0 0 0 5px var(--teal); }
@media (forced-colors: active) {
  .wing-home .wing-cost-preview input[type="range"]:focus-visible { outline: 2px solid; outline-offset: 3px; }
}
.wing-home .wing-cost-preview input[type="range"]:disabled { cursor: default; opacity: .6; }
.wing-home .wing-cost-preview__limits { display: flex; justify-content: space-between; gap: 12px; font-size: var(--wing-label); font-variant-numeric: tabular-nums; color: var(--muted); }
.wing-home .wing-cost-preview p { font-size: var(--wing-caption); line-height: 1.6; margin: 16px 0 0; }
.wing-home .wing-cost-preview__link {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  min-height: 48px; padding-block: 12px; margin-top: 20px;
  border-bottom: 1px solid var(--teal); color: var(--navy); font-size: var(--wing-small);
  text-decoration: none; transition: color .45s var(--wing-ease);
}
.wing-home .wing-cost-preview__link:is(:hover, :focus-visible) { color: var(--teal); }
/* 29 Sep: the ↗ nudges the same 3px/450ms/house-curve as the fit-guide
   arrow (v2-pages.css), so the two onward-link languages on the site match.
   Hover movement is gated on a real pointer (touch keeps `:hover` sticky
   after a tap); `:focus-visible` is never gated, so keyboard users still
   get the cue. */
.wing-home .wing-cost-preview__link span {
  display: inline-block; font-size: var(--wing-lede); line-height: 1;
  transition: transform .45s var(--wing-ease);
}
@media (hover: hover) and (pointer: fine) {
  .wing-home .wing-cost-preview__link:hover span { transform: translate(3px, -3px); }
}
.wing-home .wing-cost-preview__link:focus-visible span { transform: translate(3px, -3px); }
.wing-home .wing-cost-preview #cost-preview-note { grid-column: 1 / -1; margin-top: 28px; font-size: var(--wing-caption); color: var(--muted); }
.wing-home .wing-cost-preview__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
@media (max-width: 899px) {
  .wing-home .ledger__tools.e-con { grid-template-columns: 1fr; gap: 40px; margin-top: 40px; }
  .wing-home .wing-cost-preview { grid-template-columns: 1fr; padding: var(--wing-space-24); }
}

/* =========================================================================
   READING MEASURE FOR SUPPORTING TEXT, 21 September 2026
   Brief: docs/design-refinement/09-visual-simplification.md §3, "start with a
   reading width of roughly 55 to 70 characters per line".

   Measured on the live page at 1440, using the CONTENT box (the first pass at
   this used the border box and badly overstated anything with padding, so
   these numbers are the corrected ones):

     #cost-preview-note      205 characters per line at 13px
     .drawdown__meter-cap    188 characters per line at 14px
     .figs__qual             107 characters per line at 14px
     .fields__foot            91 characters per line at 14px

   Body copy is already correct and is NOT touched here: the brand spec sets
   it to 56ch, which renders about 74 characters in Archivo, and every body
   paragraph measures 74 or under. The fault was never the prose. It was that
   the captions, notes and qualifications beneath the prose obeyed no measure
   at all, so the smallest type on the page ran the widest lines, which is
   precisely backwards. A 205-character line of 13px text is where the eye
   loses its place returning to the next line.

   The fix is one rule, not four values: supporting text gets the SAME 56ch
   measure the approved body copy already uses. Nothing on the page then
   reads wider than body, whatever its size. No text is shortened, moved,
   hidden or made smaller, per the brief's standing instruction.
   ========================================================================= */

.wing-home #cost-preview-note,
.wing-home .drawdown__meter-cap,
.wing-home .drawdown__caption,
.wing-home .fields__foot,
.wing-home .illus__caveat { max-width: 56ch; }

/* `.figs__qual` takes its 56ch measure in components-v2.css §3 now; it no
   longer sits under a full-width rule, so the padding-right trick is gone. */

/* =========================================================================
   THE 320px STATISTIC OVERFLOW
   Brief: docs/design-refinement/07-shared-design-and-motion.md §3, which
   identified `.fig__unit` rather than the calculator. Reproduced exactly:
   at 320px the document measured 327px, a 7px overflow, with `.fig__unit`
   (the percent sign) reaching x=327. 375px and 390px were already clean.

   The cause is that `.fig__unit` is sized with `transform: scale()`, and a
   transform does not affect layout: the glyph's layout box stays 24px wide
   while its painted box extends past it, so no parent can contain it. The
   scale multiplier is therefore reduced at the narrowest widths only, which
   is the brief's own first suggestion ("adjust the statistic font/gap at the
   smallest breakpoint"). Global `overflow-x: hidden` is explicitly NOT used;
   the brief rules it out, and it would clip focus rings.
   ========================================================================= */
/* Both 320px rules that were here (a smaller unit scale, a smaller
   --wing-figure) are retired, 28 Sep 2026: components-v2.css §3 sizes each
   figure from its own column with a container query, so "$100K" cannot
   outgrow its track at 320 or anywhere else. */

/* The home cost lists (`.lists__col`) carry their text in bare <li><span>
   with no class of their own, so the shared cap in components.css could not
   reach them: measured 92 characters per line at 16px, the widest remaining
   line on the page. Same 56ch measure as everything else. Brief 09 §2 and §3. */
.wing-home .lists__col li { max-width: 56ch; }

/* =========================================================================
   COST LIST ORGANISATION AND SECTOR/EXPENDITURE SEPARATION, 21 September 2026
   Brief: docs/design-refinement/09-visual-simplification.md §2.

   1) SUBJECT CHANGE. The three field rows (Primary Health/Renewable Energy/
      Waste Management) and the two cost lists that follow them are two
      different subjects - what Wing funds, then what the money can be spent
      on - but the old margin-top (clamp(24px,3vw,36px), 36px at 1440) gave
      that change of subject barely more room than the 22px padding already
      separating one field row from the next. Raised to clamp(40px,4.4vw,64px)
      so distinct subsections read as distinct (40-64px, brief 09 §3), while
      the two "What the money..." H3s remain the reader's actual subheading
      for the new subject - no new heading or divider added. Desktop only:
      the <=899px rule two blocks below already sets its own fixed margin-top
      for the stacked mobile layout and is untouched.

   2) QUIETER MARKERS. Items 01-07 in "What the money can go on" are a plain
      list of cost categories, not sequential steps - nothing in the copy
      depends on doing them in order - so per brief 09 §2 ("plain cost
      categories do not need ordinal numbers... a quieter marker can reduce
      decoration") the digits are recoloured from --teal to the same --muted
      already used for "What it cannot"'s dash markers. Both lists now use one
      marker language instead of two, and the numbers still read (no words or
      digits removed, only a colour). Numbered PROCESS steps elsewhere on the
      page - the readiness stepper - are untouched; this rule is scoped to
      `.fields .lists` only.

   Both changes are made in place on the FIELDS block's own rules above
   (`.wing-home .fields .lists` margin-top, `.wing-home .fields .lists li i`
   color) rather than repeated here, so there is one rule per property.
   ========================================================================= */

/* =========================================================================
   COLUMN CONVERGENCE, 21 September 2026
   Brief: docs/design-refinement/09-visual-simplification.md §5, "use
   consistent left/right content edges"; and the structural-composition
   pass's question about proportion and precision.

   THE DIAGNOSIS. Every band on the home page is a two- or four-part split
   of the same shell, but each split was authored with its own gutter, so
   the vertical where the secondary column begins WANDERS down the scroll.
   Measured at 1440 (ops: a Playwright probe of each grid's second child's
   left edge, repeated at 1600/1280/1024/900 — every offset below is
   width-invariant, not a coincidence at one breakpoint):

       figures divider 1     445      \  2px apart
       ledger  Wing column   447      /
       fields  value column  652      \
       fit     answer column 661       >  18px across three bands
       stepper right column  670      /
       cost    preview split 744      \  8px apart
       figures divider 2     748       >
       downside 2nd column   752      /
       fields  exclusions    843
       ledger  comparator    934

   Nine positions, five of them in near-miss pairs 2-8px apart. That is the
   worst of both: too close to read as a deliberate second structure, too
   far to read as one. Nothing is individually wrong, which is why several
   rounds of spacing and type fixes did not touch it, and it is a known
   cause of a page that "feels off" without a nameable fault. The brief for
   this site is restraint and precision; a wandering column edge is neither.

   THE FIX. Four gutters in this file moved to the page's dominant 64px, and
   one list split from 7fr/5fr to 8fr/4fr. Each change makes an EXACT
   coincidence, at every width >=900, with a column another band already
   owns — the arithmetic cancels the viewport term, so these hold at 900,
   1024, 1280, 1440 and 1600 alike:

     figs__grid  column-gap 56 -> 64
        divider 1 = L + (W-192)/4 + 64 = L + W/4 + 16 = the ledger row's
        3fr line (L + (W-64)*3/12 + 32). Was 2px left of it at every width.
        divider 2 = L + W/2 + 32 = the downside grid's 1fr/1fr line
        (L + (W-64)/2 + 64). Was 4px left of it at every width.
        The ::before divider offset moves -28 -> -32 to stay gap-centred.

     fields .row gap 48 -> 64
        5fr/7fr at a 64px gutter = L + (5/12)(W-64) + 64, which is what the
        fit-check's flex 41.66%/58.33% at column-gap 64 already resolves to
        (a flex basis of (5/12)W shrunk by g/W yields the same term). The
        two bands' secondary columns are now the same line, not 9px apart.

     fields .lists 7fr 5fr -> 8fr 4fr
        L + (W-64)*8/12 + 64 = the ledger row's third track (3fr+5fr+two
        32px gaps). The exclusions list and the comparison's comparator
        column now share an edge.

     wing-cost-preview gap 0 48 -> 0 64
        Inside the panel's 28px padding, the split is L + W/2 + g/2, so a
        64px gutter puts it on L + W/2 + 32 — the same line as figures
        divider 2 and the downside column above.

   Result: nine wandering positions become five repeated ones. Four of the
   five are now used by two or three bands each. The one remaining outlier
   is .stepper__grid at 670, whose 80px gutter lives in components-v2.css
   and is not this file's to change; dropping it to 64 would put it on 661
   with the fit-check and the field rows.

   CONTENT IS UNCHANGED. Nothing was shortened, moved or hidden (brief 09).
   The widest measures are still capped: .row__v p at 50ch, .lists__col li
   at 56ch, so widening the left list's track lengthens its rule, not its
   lines. All five changes are inside the >=900px layouts — the <=899px
   blocks above set their own stacked geometry and are untouched, so the
   320/375/390 results verified earlier today are unaffected by design as
   well as by measurement.
   ========================================================================= */

/* =========================================================================
   MILESTONE DRAWDOWN, EXPLORABLE
   Brief: docs/design-refinement/10-credibility-and-personality.md §4.
   Behaviour: motion.js `drawdownSteps()`.

   The label becomes a real button. Every label and every note stays visible
   whether or not anything is selected, because the brief is explicit that
   the selected state is "an emphasis, not an information gate". Nothing is
   dimmed to nothing and nothing is revealed only on selection.
   ========================================================================= */

.wing-home .drawdown__btn {
  font: inherit; color: inherit; background: none; border: 0; padding: 2px 4px;
  margin: -2px -4px; cursor: pointer; border-radius: 4px;
  /* Top-aligned, so the button's padding never inflates the 24px label line
     the ring is centred on. */
  display: inline-block; vertical-align: top;
  /* 200ms sits in the 180 to 250ms the brief suggests for control feedback. */
  transition: color .2s var(--wing-ease), background-color .2s var(--wing-ease);
}
.wing-home .drawdown__btn:hover { color: var(--navy); background: var(--wing-paleblue); }

/* Emphasis, not exclusion: the unselected steps keep full opacity on their
   text. Selecting a stage draws the line only as far as that stage: rings
   up to it fill, the segments beyond it fall back to the grey track, rings
   beyond it turn grey. Colour changes only, over the house 450ms; the
   scroll draw animates `scale`, so the two never fight. */
.wing-home .drawdown__step.is-sel .drawdown__btn { color: var(--navy); font-weight: 600; }
.wing-home .drawdown__step.is-sel .drawdown__tick { transform: scale(1.3); }
.wing-home .drawdown.has-sel .drawdown__step:is(.is-before, .is-sel) .drawdown__tick { background: var(--teal); }
.wing-home .drawdown.has-sel .drawdown__step:not(.is-before):not(.is-sel) .drawdown__tick { border-color: var(--rule); background: var(--wing-white); }
.wing-home .drawdown.has-sel .drawdown__step:not(.is-before)::after { background-color: transparent; }

/* A screen reader gets the stage and its own published note announced; the
   text itself is never shown twice on screen. */
.wing-home .drawdown__sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

@media (prefers-reduced-motion: reduce) {
  .wing-home .drawdown__btn,
  .wing-home .drawdown__tick,
  .wing-home .drawdown__step::after { transition: none; }
}

/* ==== v2-pages.css ==== */
/* =========================================================================
   Wing R&D Fund - inner pages, v2.

   OWNED BY THE INNER-PAGES WORK. Loads last, after v2-bands.css.
   Scope: /about/, /how-funding-works/, /what-we-fund/, /eligibility/,
   /application-process/, /faq/, /contact/, /apply/ - their heroes and any
   page-only arrangement of the shared components.

   Shared components (figures, ledger, fields, stepper, downside, closing)
   are styled in v2-bands.css and must be INHERITED, not restyled here.

   WHY EVERYTHING BELOW IS SCOPED TO `.wing-inner`, NOT `.wing-home`.
   components-v2.css says it plainly in its own header: "`.wing-home` is on
   the front page only and carries the sections. Nothing here is unscoped,
   so the eight inner pages are untouched by this file except for the header
   and the footer." Verified against the file: every band it built for
   Direction 05 - `.hero`, `.figs`, `.fit`, `.ledger`, `.focus`, `.stepper`,
   `.down`, `.close`, and the shared primitives `.display`, `.lede`,
   `.body-copy`, `.muted`, `.measure`, `.label`, `.index`, `.dark`, `.pale`,
   `.place` - is written `.wing-home .thing`, and `wing_rd_body_class()` in
   functions.php only adds `wing-home` on `is_front_page()`. So the class
   names below are the SAME ones components-v2.css uses (build/wingbuild_pages
   .py's helpers emit them on purpose, to match the home page's language),
   but every inner-page container also carries `wing-inner`, and every
   selector here is written `.wing-inner .thing` - never bare `.thing` - so
   nothing here can leak onto the home page regardless of specificity, and
   nothing in components-v2.css can reach these pages by accident either.

   `.wing-v2` IS site-wide already (nav, footer, buttons, `.wing-shell`'s
   1300px measure, the `--navy` / `--ink` / `--teal` / `--muted` / `--rule` /
   `--ease` short aliases) - none of that is repeated here.
   ========================================================================= */

/* =========================================================================
   0. SHARED PRIMITIVES - the same type and ground system as the home page,
      rebuilt under `.wing-inner` because components-v2.css keeps its own
      copy behind `.wing-home`.
   ========================================================================= */

.wing-inner .display.elementor-widget-heading .elementor-heading-title,
.wing-inner .display h1, .wing-inner .display h2, .wing-inner .display h3 {
  font-weight: var(--wing-display-weight);
  letter-spacing: var(--wing-ls-display);
  line-height: var(--wing-lh-display);
  text-wrap: balance;
  margin: 0;
  max-width: none;
  color: inherit;
}
.wing-inner h1.display.elementor-widget-heading .elementor-heading-title,
.wing-inner .display--h1.elementor-widget-heading .elementor-heading-title {
  font-size: var(--wing-display);
  line-height: 1;
  padding-bottom: .06em;
}
.wing-inner .display.elementor-widget-heading .elementor-heading-title {
  font-size: var(--wing-h2);
  line-height: var(--wing-lh-h2);
}
.wing-inner .display--h1.elementor-widget-heading .elementor-heading-title {
  font-size: var(--wing-display);
  line-height: 1;
}
.wing-inner .display .elementor-heading-title em {
  font-style: italic;
  font-weight: calc(var(--wing-display-weight) - 60);
}

.wing-inner .lede.elementor-widget-text-editor > p {
  font-size: var(--wing-lede);
  line-height: var(--wing-lh-lede);
  max-width: 34ch;
  margin: 0;
}
.wing-inner .body-copy.elementor-widget-text-editor > p,
.wing-inner .measure.elementor-widget-text-editor > p {
  font-size: var(--wing-body);
  line-height: 1.6;
  max-width: 56ch;
  margin: 0;
}
.wing-inner .muted.elementor-widget-text-editor > p { color: var(--muted); }

.wing-inner .label,
.wing-inner .index .label { font-size: var(--wing-label); font-weight: 500; letter-spacing: .08em; text-transform: uppercase; }

.wing-inner .index {
  display: flex; gap: 14px; align-items: baseline;
  color: var(--teal); margin-bottom: 28px;
}
.wing-inner .index b { font-weight: 500; }

/* --- grounds ------------------------------------------------------------ */
.wing-inner .dark { background: var(--ink); color: #fff; }
.wing-inner .pale { background: var(--pale); }
.wing-inner .dark .index { color: var(--lightblue); }
.wing-inner .dark .muted, .wing-inner .dark p.body { color: rgba(255,255,255,.72); }

.wing-inner .dark .elementor-widget-heading .elementor-heading-title,
.wing-inner .hero--inner .elementor-widget-heading .elementor-heading-title,
.wing-inner .close .elementor-widget-heading .elementor-heading-title { color: #fff; }
.wing-inner .dark .elementor-widget-text-editor,
.wing-inner .dark .elementor-widget-text-editor > p,
.wing-inner .hero--inner .elementor-widget-text-editor > p,
.wing-inner .close .elementor-widget-text-editor > p { color: rgba(255,255,255,.80); }
.wing-inner .hero--inner .lede.elementor-widget-text-editor > p,
.wing-inner .close .lede.elementor-widget-text-editor > p { color: rgba(255,255,255,.86); }

.wing-inner .measure { max-width: 56ch; }
.wing-inner .place {
  margin: 24px 0 0; text-align: right;
  font-size: var(--wing-label); color: rgba(255,255,255,.7); letter-spacing: 0;
}

/* =========================================================================
   1. HERO — 62svh, one aerial per page, no wash and no cursor-tracking
      (master-plan WP9: those stay home-page only).
   ========================================================================= */

/* =========================================================================
   PAGE ROLES - brief 11 V07
   -------------------------------------------------------------------------
   `wing-role-editorial` and `wing-role-task` come from wing_rd_body_class()
   in functions.php. Before this, every inner page opened identically: an
   80px title over a 62svh photographic band, on /about/ and on /privacy/
   alike. Measured at 320px, /apply/'s title wrapped to four lines and the
   opening filled roughly 600px before the eligibility check the page
   exists for.

   EDITORIAL keeps the aerial band and everything about the composition;
   only the title steps down from the landing size, so the front page stays
   the loudest thing on the site.

   TASK is the real change. The band stops reserving 62% of the screen, the
   title takes the reference size, and the photograph becomes a shallower
   backdrop. Nothing is cropped or removed - the padding is the brief's
   "header clearance plus 32-48px top and 40-56px bottom", and the band
   still grows with its own content, so a title can never be clipped.
   ========================================================================= */
.wing-v2.wing-role-editorial .wing-inner :is(h1.display, .display--h1).elementor-widget-heading .elementor-heading-title {
  font-size: var(--wing-display-editorial);
  line-height: 1.08;
}
.wing-v2.wing-role-task .wing-inner :is(h1.display, .display--h1).elementor-widget-heading .elementor-heading-title {
  font-size: var(--wing-display-task);
  line-height: 1.12;
}

/* The compact masthead. `min-height` is RELEASED rather than lowered, so
   the band is exactly as tall as its own content needs it to be. */
.wing-v2.wing-role-task .wing-inner .hero--inner.e-con {
  --min-height: 0; min-height: 0 !important;
  padding: calc(var(--wing-header-h) + clamp(32px, 3.4vw, 48px)) 0 clamp(40px, 4.4vw, 56px);
}

/* ONE OWNER PER GAP, in the task masthead at phone width.
   Review found the compact masthead still filling 574px of a 740px screen on
   /contact/. Measured, the cause was not the padding (108/40, exactly the
   brief's "header clearance plus 32-48 and 40-56") but double spacing: the
   eyebrow, title and lede each carry their own margin AND sit in nested
   Elementor containers that each add a 20px row gap. Eyebrow to title was
   14 + 20, title to lede 22 + 20, three levels deep.

   The gaps are released and the margins keep the job, which is brief 11
   V08's rule - fix ownership rather than layer a correction on top. The
   resulting rhythm is the brief's own: 14px eyebrow to title, 22px title to
   lede, 32px to the actions. Nothing is removed and nothing can clip; the
   band still grows with its content. Phone width only, so the desktop
   composition is untouched. */
@media (max-width: 600px) {
  .wing-v2.wing-role-task .wing-inner .hero--inner .e-con { --row-gap: 0px; row-gap: 0px; }
}

.wing-inner .hero--inner.e-con {
  position: relative; background: var(--ink);
  --min-height: 62svh; min-height: 62svh !important;
  overflow: hidden; isolation: isolate; color: #fff;
  --justify-content: flex-end;
  /* Top padding guarantees clearance under the fixed 76px nav (z-index 40)
     regardless of copy length. justify-content:flex-end only redistributes
     SLACK inside the band, and on pages whose eyebrow+H1+lede+actions block
     is tall enough to fill the 62svh minimum (measured: /about/, whose
     block is 536px against a 536px content box) there is no slack left, so
     the block renders flush with the band's own top edge - which is the
     page's top edge, i.e. under the nav. Measured before this fix:
     .hero__eyebrow top:0 (fully hidden) on /about/, top:30-54px (still
     overlapped) on /contact/ and /apply/, at both 1440x900 and 375x812. */
  padding: clamp(104px, 10vw, 132px) 0 clamp(40px, 6vw, 72px);
}
/* Same bug the home hero already paid for: the layer divs are one HTML
   widget, a SIBLING of .wing-shell, not a child of it. An Elementor HTML
   widget is `position:relative; height:0` by default, so `.hero__media
   {position:absolute}` resolved against a 0x0 box and rendered nothing
   until the WIDGET ITSELF was made the absolutely-positioned, full-bleed
   layer. Fixed here the same way. */
.wing-inner .hero--inner > .hero__layers,
.wing-inner .hero--inner > .hero__layers > .elementor-widget-container {
  position: absolute; inset: 0; z-index: 0;
}
.wing-inner .hero--inner .hero__media { position: absolute; inset: 0; z-index: 0; }
.wing-inner .hero--inner .hero__media img {
  width: 100%; height: 100%; object-fit: cover;
  filter: saturate(.7) brightness(.75);
}
@media (prefers-reduced-motion: no-preference) {
  .wing-inner .hero--inner .hero__media img { animation: wingZoom 24s linear infinite alternate; }
}
.wing-inner .hero--inner .hero__tint { position: absolute; inset: 0; z-index: 1; pointer-events: none; background: rgba(53,120,140,.22); mix-blend-mode: multiply; }
/* The first layer (29 Sep) sits under the 13px eyebrow, which fell to
   3.6-4.3:1 in its darkest-to-lightest pixels over the sandbar and braided
   river at 1024-1920px. Too faint to see; enough to hold 4.5:1. */
.wing-inner .hero--inner .hero__scrim {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: radial-gradient(760px 240px at 0% 34%, rgba(7,16,30,.26) 0%, rgba(7,16,30,.14) 55%, rgba(7,16,30,0) 100%),
              linear-gradient(180deg, rgba(7,16,30,.30) 0%, rgba(7,16,30,.16) 35%, rgba(7,16,30,.72) 78%, rgba(7,16,30,.92) 100%),
              linear-gradient(90deg, rgba(7,16,30,.42) 0%, rgba(7,16,30,0) 60%);
}
/* Stacked, the eyebrow and heading sit in the top third, where this scrim
   was at its lightest. Measured 29 Sep at 320-390px: the eyebrow fell to
   2.8-3.3:1 over the white sandbar on /how-funding-works/ and 4.2-4.5:1 on
   /contact/ and /eligibility/. Denser at the top left, where the copy is. */
@media (max-width: 899px) {
  .wing-inner .hero--inner .hero__scrim {
    background: radial-gradient(90% 34% at 0% 24%, rgba(7,16,30,.38) 0%, rgba(7,16,30,.2) 60%, rgba(7,16,30,0) 100%),
                linear-gradient(180deg, rgba(7,16,30,.5) 0%, rgba(7,16,30,.38) 24%, rgba(7,16,30,.3) 42%, rgba(7,16,30,.72) 78%, rgba(7,16,30,.92) 100%),
                linear-gradient(90deg, rgba(7,16,30,.42) 0%, rgba(7,16,30,0) 60%);
  }
}
.wing-inner .hero--inner > .wing-shell { position: relative; z-index: 3; width: 100%; }
.wing-inner .hero--inner .hero__grid.e-con { --flex-direction: row; --align-items: flex-end; }
.wing-inner .hero--inner .hero__col.e-con { --width: 100%; }
@media (min-width: 900px) {
  .wing-inner .hero--inner .hero__col.e-con { --width: 66.66%; flex: 0 1 66.66%; }
}
.wing-inner .hero__eyebrow {
  font-size: var(--wing-label); font-weight: 500; letter-spacing: .08em; text-transform: uppercase;
  color: var(--lightblue); margin: 0 0 clamp(14px, 1.6vw, 22px);
}
.wing-inner .hero--inner .hero__h1.elementor-widget { margin-bottom: 22px; }
.wing-inner .hero--inner .hero__lede.elementor-widget { margin-bottom: 0; }
.wing-inner .hero__actions.e-con { --flex-direction: row; --row-gap: 12px; --column-gap: 12px; --flex-wrap: wrap; margin-top: 32px; }

/* =========================================================================
   2. LEDGER — /how-funding-works/, the same component and the same six
      rows as the home page (WP4 acceptance: "used unchanged").
   ========================================================================= */

.wing-inner .ledger.e-con { padding-block: var(--wing-band); }
/* Brief 09 section 1, "Compare": the heading and the comparator control are
   aligned deliberately rather than left to share a bottom edge by accident.
   Centring the (label + toggle) column against the taller (index + display
   heading) column reads as one considered pair - toggle beside the
   headline's optical middle - instead of the toggle looking pinned to the
   heading's baseline with dead air above it. */
.wing-inner .ledger__head.e-con { --flex-direction: row; --row-gap: 64px; --column-gap: 64px; --align-items: center; margin-bottom: 36px; }
.wing-inner .ledger__head-a.e-con { --width: 41.66%; flex: 0 1 41.66%; }
.wing-inner .ledger__head-b { --width: 58.33%; flex: 0 1 58.33%; }
.wing-inner .seg { display: inline-flex; border: 1px solid var(--rule); border-radius: 100px; padding: 4px; gap: 4px; }
/* Touch target (defect #3, crossbrowser-a11y.md) - same fix as the home
   page's copy of this component in components-v2.css: an invisible 2px
   strip top and bottom via `::after`, sitting inside the capsule's own 4px
   padding, not a taller pill (that would grow the visible capsule itself).
   See that file's comment for the WCAG 2.5.8 rationale and the audit-script
   caveat. */
.wing-inner .seg button {
  position: relative;
  /* 44px, launch checklist phase 4. Was `height: 40px; min-height: 40px`,
     which is under the floor on the short axis. The `::after { inset: -2px 0 }`
     below was meant to make up the difference and does not: probed with
     elementFromPoint at 390px, the real hit area measured 40px, 0px above and
     0px below. The pill grows by 4px; nothing else moves. */
  height: 44px; min-height: 44px; padding: 0 18px; border: 0; border-radius: 100px; background: none;
  font: inherit; font-size: var(--wing-action-size); font-weight: 500; color: var(--muted);
  cursor: pointer; transition: background .4s var(--wing-ease), color .4s var(--wing-ease);
}
.wing-inner .seg button::after { content: ""; position: absolute; inset: -2px 0; }
.wing-inner .seg button[aria-checked="true"] { background: var(--navy); color: #fff; }
.wing-inner .seg__label { display: block; margin-bottom: 12px; color: var(--muted); }
.wing-inner .ledger__rows { border-top: 1px solid var(--rule); }
.wing-inner .lrow { display: grid; grid-template-columns: 3fr 5fr 4fr; gap: 32px; padding: 18px 0; border-bottom: 1px solid var(--rule); align-items: baseline; }
.wing-inner .lrow__k { font-size: var(--wing-caption); color: var(--muted); }
.wing-inner .lrow__wing { font-size: var(--wing-h3); font-weight: 500; letter-spacing: -.02em; line-height: 1.25; }
.wing-inner .lrow__cmp { color: var(--muted); }
.wing-inner .ledger__colhead { display: grid; grid-template-columns: 3fr 5fr 4fr; gap: 32px; padding-bottom: 14px; color: var(--muted); }
.wing-inner .ledger__colhead span:nth-child(2) { color: var(--teal); }

/* =========================================================================
   3. FIELDS — /what-we-fund/, three rows with NO photography (prototype 03:
      the sticky image column is gone; the page no longer requests
      wing-health / wing-energy / wing-waste). Name sits beside its
      description on a hairline; hovering or focusing a row dims the other
      two to .42, driven by the small script `fields_section()` emits.
   ========================================================================= */

.wing-inner .focus.e-con { padding: calc(var(--wing-band) * .8) 0 calc(var(--wing-band) * .6); }
.wing-inner .focus__head { max-width: 60ch; margin-bottom: clamp(36px, 5vw, 56px); }
.wing-inner .focus .display .elementor-heading-title { margin-bottom: 8px; }
.wing-inner .focus__lead.elementor-widget-text-editor > p { max-width: 46ch; margin: 0; }

.wing-inner .rows { border-top: 1px solid var(--rule); }
.wing-inner .row {
  display: grid; grid-template-columns: 5fr 7fr; gap: 40px;
  padding: 30px 0; border-bottom: 1px solid var(--rule);
  transition: opacity .5s var(--wing-ease);
}
/* 5 Oct: only while the mouse is really moving (motion.js sets
   html.is-pointing) or keyboard focus is in the list. Rows passing under a
   resting cursor during a scroll no longer fade in and out. */
html.is-pointing .wing-inner .rows.has-hover .row:not(.is-hover),
.wing-inner .rows.has-hover:focus-within .row:not(.is-hover) { opacity: .42; }
.wing-inner .row__k { display: flex; flex-direction: column; gap: 8px; }
.wing-inner .row__n {
  font-size: var(--wing-h3); font-weight: var(--wing-display-weight);
  letter-spacing: var(--wing-ls-display); line-height: 1.05;
}
.wing-inner .row__v p { max-width: 52ch; margin: 0; }
.wing-inner .row__v small { display: block; font-size: var(--wing-caption); color: var(--muted); margin-top: 10px; }

/* =========================================================================
   3b. COST LISTS — /what-we-fund/, the eligible / conditional / not-funded
      copy this page already carried, laid out as prototype 03's two-column
      ruled lists (numbered for what can be funded, dashed for what cannot)
      instead of the three-tile `wing-defs` grid. Copy unchanged throughout.
   ========================================================================= */

/* Three columns (Eligible 5 / Conditional 3 / Not funded 4 rows), not the
   original two (Eligible+Conditional stacked in one column against a lone
   Not-funded column) - the 8-vs-4 row mismatch left the bottom-right
   quarter of the band empty. Three near-even columns removes that without
   touching a word of copy. */
.wing-inner .costlists.costlists--3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 40px; }
.wing-inner .costlists__col { display: flex; flex-direction: column; gap: 32px; min-width: 0; }
.wing-inner .costlists__group h3 { font-size: var(--wing-lede); font-weight: 500; letter-spacing: -.02em; margin: 0 0 14px; color: inherit; }
.wing-inner .costlists__list { border-top: 1px solid var(--rule); margin: 0; padding: 0; list-style: none; }
.wing-inner .costlists__list li {
  display: grid; grid-template-columns: 22px 1fr; gap: 12px; align-items: baseline;
  padding: 12px 0; border-bottom: 1px solid var(--rule);
  font-size: var(--wing-small);
}
.wing-inner .costlists__list li i { font-style: normal; color: var(--teal); font-size: var(--wing-caption); }
.wing-inner .costlists__list--no li,
.wing-inner .costlists__list--no li i { color: var(--muted); }

/* =========================================================================
   4. STEPPER — /application-process/, the same component as the home
      page's readiness band, reused with the four process steps.
   ========================================================================= */

.wing-inner .stepper.e-con { padding-block: var(--wing-band); }
.wing-inner .stepper__grid.e-con { --flex-direction: row; --row-gap: 80px; --column-gap: 80px; --align-items: flex-start; }
.wing-inner .stepper__left.e-con { --width: 41.66%; flex: 0 1 41.66%; position: sticky; top: 120px; }
.wing-inner .stepper__right { --width: 58.33%; flex: 0 1 58.33%; min-width: 0; }
.wing-inner .stepper__left .display .elementor-heading-title { margin-bottom: 18px; }
.wing-inner .stepper__num {
  display: block; font-size: var(--wing-figure); font-weight: var(--wing-display-weight);
  letter-spacing: var(--wing-ls-fig); line-height: .9; margin: 36px 0 24px;
  color: #fff; font-variant-numeric: tabular-nums;
}
.wing-inner .stepper__num small { font-size: var(--wing-small); letter-spacing: 0; color: rgba(255,255,255,.5); margin-left: 10px; }
.wing-inner .track { position: relative; width: 2px; height: 180px; background: var(--rule-dark); }
/* Must mirror components-v2.css's `.wing-home .track i` exactly: motion.js
   sets a TRANSFORM on both, so a copy still sized by `height` would be
   scaled from the wrong baseline and the inner pages' stepper would stop
   filling. Brief 11 V11. */
.wing-inner .track i { position: absolute; left: 0; top: 25%; width: 2px; height: 75%; transform: scaleY(0); transform-origin: top; background: var(--lightblue); transition: transform .7s var(--wing-ease); }
.wing-inner .track b { position: absolute; left: 22px; top: 25%; transform: translateY(-50%); white-space: nowrap; font-weight: 500; font-size: var(--wing-caption); color: var(--lightblue); }
.wing-inner .track b::before { content: ""; position: absolute; left: -25.5px; top: 50%; transform: translateY(-50%); width: 9px; height: 9px; border-radius: 50%; background: var(--teal); }
.wing-inner .stage { padding: 30px 0; border-top: 1px solid var(--rule-dark); min-height: 30vh; display: flex; flex-direction: column; justify-content: center; transition: opacity .5s var(--wing-ease); }
/* Brief 04: "strengthen inactive stepper copy contrast." Measured (ink
   #07101E behind rgba(255,255,255,.72) body copy, composited through the
   container's own opacity, since that is what actually reaches the screen):
   the shipped .38 put the paragraph at 2.40:1 and the lightblue label at
   2.62:1 - both fail WCAG AA's 4.5:1 for normal text, not a near miss. .65
   clears every element in the block: body copy 4.79:1, label 5.44:1, the
   heading (large text, needs only 3:1) 8.30:1. Still visibly dimmer than the
   active stage, so the scroll-linked highlight this pairs with (motion.js,
   not this file) keeps doing its job - just no longer at the reader's
   expense. */
.wing-inner .stage:not(.is-on) { opacity: .65; }
.wing-inner .stage .label { color: var(--lightblue); margin-bottom: 14px; display: block; }
.wing-inner .stage h3 {
  /* Brief 11 V03: these were `--wing-h2`, the SECTION-heading size, so four
     of them in a row each looked like a new section of the page and the
     stepper competed with the heading above it. A stage is a component
     inside a section, not a headline. The number and the active-stage cue
     do the emphasis instead, which is what they are for.
     1 Oct: --wing-h3, as the home stepper has used since 29 Sep; the clamp
     gave 28px at 1440, an eleventh size on /application-process/. */
  font-size: var(--wing-h3);
  font-weight: var(--wing-display-weight);
  letter-spacing: -.02em;
  line-height: 1.2;
  margin: 0 0 14px;
}
.wing-inner .stage p { max-width: 40ch; color: rgba(255,255,255,.72); margin: 0; }
.wing-inner .stage--out .label { color: rgba(255,255,255,.8); }

/* =========================================================================
   5. FIT-CHECK — /eligibility/ and /apply/. A guide, not an assessment.
   ========================================================================= */

.wing-inner .fit.e-con { padding-block: var(--wing-band); }
.wing-inner .fit__grid.e-con { --flex-direction: row; --row-gap: 64px; --column-gap: 64px; --align-items: flex-start; }
.wing-inner .fit__intro.e-con { --width: 41.66%; flex: 0 1 41.66%; }
.wing-inner .fit__panelwrap { --width: 58.33%; flex: 0 1 58.33%; }
.wing-inner .fit .display .elementor-heading-title { margin-bottom: 20px; }
.wing-inner .fit__panel { position: relative; }
/* Plain-content fallback (defect #1, crossbrowser-a11y.md) - same mechanism
   as components-v2.css's home-page copy of this block; see its comment. */
.wing-inner .fit__plain-list { margin: 0 0 20px; padding: 0; list-style: none; display: grid; gap: 14px; max-width: 48ch; }
.wing-inner .fit__plain-list strong { font-weight: 500; color: var(--navy); }
.wing-inner .fit__plain-links a { text-decoration: underline; text-underline-offset: 4px; color: inherit; }
.wing-inner .fit__panel:has(#fitform.fit__noscript) .fit__progress,
.wing-inner .fit__panel:has(#fitform.fit__noscript) .fit__meta,
.wing-inner .fit__panel:has(#fitform.fit__noscript) .fit__chips { display: none; }
.wing-inner #fitform.fit__noscript { display: none; }
.wing-inner .fit__panel:has(#fitform:not(.fit__noscript)) .fit__plain { display: none; }
.wing-inner .fit__progress { height: 1px; background: var(--rule); margin-bottom: 36px; position: relative; }
.wing-inner .fit__progress i { position: absolute; left: 0; top: -1px; height: 3px; width: 100%; transform: scaleX(.3333); transform-origin: left; background: var(--teal); transition: transform .18s var(--wing-ease); }
.wing-inner .fit fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
.wing-inner .fit legend { padding: 0; display: block; }
.wing-inner .fit__step { display: none; }
.wing-inner .fit__step.is-on { display: block; }
.wing-inner .fit__q { font-size: var(--wing-h3); font-weight: 500; letter-spacing: -.02em; margin-bottom: 24px; }
.wing-inner .fit__opts { display: grid; gap: 10px; grid-template-columns: 1fr 1fr; }
.wing-inner .fit__opt {
  display: flex; align-items: center; gap: 14px; min-height: 48px; padding: 9px 18px;
  border: 1px solid var(--rule); border-radius: 2px; background: #fff;
  text-align: left; font: inherit; color: inherit; cursor: pointer;
  transition: border-color .3s var(--wing-ease), background .3s var(--wing-ease), transform .45s var(--wing-ease);
}
/* Brief 11 V11: hover MOVEMENT is gated on a real pointer. On a touch
   screen `:hover` sticks after a tap, so a lifted control stayed lifted
   with nothing to un-hover it. Colour and border feedback stay ungated,
   and keyboard `:focus-visible` is deliberately outside this query so
   focus feedback is never lost. */
@media (hover: hover) and (pointer: fine) {
  .wing-inner .fit__opt:hover { border-color: var(--teal); transform: translateY(-2px); }
}
.wing-inner .fit__opt i { width: 10px; height: 10px; border: 1px solid var(--navy); border-radius: 50%; flex: none; }
.wing-inner .fit__opt.is-picked { border-color: var(--teal); background: var(--teal); color: #fff; }
.wing-inner .fit__opt.is-picked i { background: #fff; border-color: #fff; }
.wing-inner .fit__meta { display: flex; justify-content: space-between; align-items: center; margin-top: 26px; font-size: var(--wing-caption); color: var(--muted); }
.wing-inner .fit__back { text-decoration: underline; text-underline-offset: 4px; background: none; border: 0; font: inherit; color: inherit; cursor: pointer; padding: 0 4px; }
.wing-inner .fit__result { display: none; }
.wing-inner .fit__result.is-on { display: block; }
.wing-inner .fit__result h3 { font-size: var(--wing-h2); font-weight: var(--wing-display-weight); letter-spacing: var(--wing-ls-display); line-height: 1.04; margin: 0 0 18px; max-width: 18ch; }
.wing-inner .fit__result p { max-width: 48ch; margin: 0 0 28px; }
.wing-inner .fit__chips { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 22px; }
.wing-inner .fit__chips span { font-size: var(--wing-caption); padding: 6px 12px; border: 1px solid var(--rule); border-radius: 100px; background: #fff; }
.wing-inner .fit__note { font-size: var(--wing-caption); color: var(--muted); margin: 28px 0 0; }

/* =========================================================================
   5b. COST ILLUSTRATOR — /how-funding-works/, docs/directions/sections/
      cost-illustrator.html as a band directly under the ledger, on the
      same unpainted (white) ground so the two read as one chapter.
      Client-side only: no request fires on input, nothing persists.
   ========================================================================= */

.wing-inner .illus.e-con { padding-block: var(--wing-band); }
.wing-inner .illus__grid.e-con { --flex-direction: row; --row-gap: 64px; --column-gap: 64px; --align-items: flex-start; }
.wing-inner .illus__intro.e-con { --width: 41.66%; flex: 0 1 41.66%; }
.wing-inner .illus__panelwrap { --width: 58.33%; flex: 0 1 58.33%; min-width: 0; }
.wing-inner .illus .display .elementor-heading-title { margin-bottom: 20px; }
.wing-inner .illus .body-copy.elementor-widget-text-editor > p { margin: 0 0 18px; }

.wing-inner .illus__readout-lab { color: var(--muted); margin-bottom: 14px; }
.wing-inner .illus__big {
  display: block; font-size: var(--wing-figure); font-weight: var(--wing-display-weight);
  letter-spacing: var(--wing-ls-fig); line-height: .95;
  font-variant-numeric: tabular-nums; color: var(--teal);
}
.wing-inner .illus .fade { transition: opacity .4s var(--wing-ease); }
.wing-inner .illus .fade.is-out { opacity: 0; }

.wing-inner .illus__bar { margin-top: 26px; }
.wing-inner .illus__bartrack { display: flex; height: 8px; width: 100%; overflow: hidden; }
.wing-inner .illus__barp { background: var(--navy); transition: flex-basis .5s var(--wing-ease); }
.wing-inner .illus__bari { background: var(--teal); transition: flex-basis .5s var(--wing-ease); }
.wing-inner .illus__barkey { display: flex; gap: 24px; margin-top: 12px; font-size: var(--wing-caption); color: var(--muted); flex-wrap: wrap; }
.wing-inner .illus__barkey i { display: inline-block; width: 9px; height: 9px; margin-right: 8px; vertical-align: baseline; }
.wing-inner .illus__barkey i.p { background: var(--navy); }
.wing-inner .illus__barkey i.i { background: var(--teal); }

.wing-inner .illus__instr { border-top: 1px solid var(--rule); padding-top: 24px; margin-top: 32px; }
/* Brief 09 section 1, "Explore cost": the amount and rate sliders are the
   same tool, not two separate qualifications, so the second control drops
   its own hairline and sits on spacing alone - one rule opens "the tool" as
   a group (below the result), none divides it internally. */
.wing-inner .illus__instr + .illus__instr { border-top: 0; padding-top: 0; margin-top: 28px; }
.wing-inner .illus__instrtop { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; margin-bottom: 20px; }
.wing-inner .illus__val { font-size: var(--wing-h3); font-weight: 500; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.wing-inner .illus__ticks { display: flex; justify-content: space-between; margin-top: 10px; font-size: var(--wing-caption); color: var(--muted); font-variant-numeric: tabular-nums; }
.wing-inner .illus__note { font-size: var(--wing-caption); color: var(--muted); margin: 12px 0 0; max-width: 44ch; }

/* Real range inputs, 44px touch target, keyboard-operable (arrow keys are
   native) and focus-visible, not decoration standing in for a control. */
.wing-inner .illus input[type="range"] {
  -webkit-appearance: none; appearance: none; width: 100%; height: 44px;
  background: transparent; display: block; cursor: ew-resize; margin: 0; touch-action: pan-y;
}
/* Same slider as the home cost preview, 28 Sep 2026: a 2px track at navy
   30% (--rule on this pale ground measured 1.2:1 and faded out at 1x), a
   20px ring thumb that fills teal on hover, drag and focus, and the focus
   ring drawn around the thumb at 3px. The 4px teal bar it replaces was a
   second slider language on the site. */
.wing-inner .illus input[type="range"]::-webkit-slider-runnable-track { height: 2px; background: rgba(10, 52, 106, .3); }
.wing-inner .illus input[type="range"]::-moz-range-track { height: 2px; background: rgba(10, 52, 106, .3); }
.wing-inner .illus input[type="range"]::-moz-range-progress { height: 2px; background: var(--teal); }
.wing-inner .illus input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; box-sizing: border-box; width: 20px; height: 20px;
  border-radius: 50%; border: 2px solid var(--teal); background: var(--pale); margin-top: -9px;
  transition: background-color .45s var(--wing-ease), box-shadow .45s var(--wing-ease);
}
.wing-inner .illus input[type="range"]::-moz-range-thumb {
  box-sizing: border-box; width: 20px; height: 20px; border-radius: 50%;
  border: 2px solid var(--teal); background: var(--pale);
  transition: background-color .45s var(--wing-ease), box-shadow .45s var(--wing-ease);
}
.wing-inner .illus input[type="range"]:is(:hover, :active, :focus-visible)::-webkit-slider-thumb { background: var(--teal); }
.wing-inner .illus input[type="range"]:is(:hover, :active, :focus-visible)::-moz-range-thumb { background: var(--teal); }
/* `box-shadow: none` too, not just `outline: none` (29 Sep fix, same as the
   home cost-preview slider): base.css's site-wide focus halo is a
   `box-shadow`, so removing only the outline left it drawing round the
   INPUT's own full-width box, not the visible thumb. */
.wing-inner .illus input[type="range"]:focus-visible { outline: none; box-shadow: none; }
.wing-inner .illus input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px var(--pale), 0 0 0 5px var(--teal); }
.wing-inner .illus input[type="range"]:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 3px var(--pale), 0 0 0 5px var(--teal); }
@media (forced-colors: active) {
  .wing-inner .illus input[type="range"]:focus-visible { outline: 2px solid; outline-offset: 3px; }
}

.wing-inner .illus__terms { display: grid; grid-template-columns: repeat(3, 1fr); margin-top: 40px; border-top: 1px solid var(--rule); }
.wing-inner .illus__term { padding: 20px 20px 0 0; }
.wing-inner .illus__term + .illus__term { border-left: 1px solid var(--rule); padding-left: 20px; }
.wing-inner .illus__term b { display: block; font-size: var(--wing-h3); font-weight: 500; letter-spacing: -.02em; line-height: 1.1; font-variant-numeric: tabular-nums; margin-bottom: 6px; }
.wing-inner .illus__term span { font-size: var(--wing-caption); color: var(--muted); }

.wing-inner .illus__caveat { margin-top: 34px; padding-top: 18px; border-top: 1px solid var(--rule); font-size: var(--wing-caption); color: var(--muted); max-width: 76ch; }
.wing-inner .illus__caveat b { font-weight: 500; color: var(--navy); }
.wing-inner .illus__sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* =========================================================================
   5c. CONTACT FORM — /contact/. `.wing-contact` and `.wing-form` carry no
      width rule of their own beyond the 780px cap on
      `.elementor-form-fields-wrapper` (components.css), so the form sat
      flush left in the full 1300px shell and left roughly half the band
      reading as an unexplained gap on the right (measured: 52% width used).
      Centring the whole block turns that gap into deliberate margin on
      both sides instead of dead space on one - the HALFEMPTY remedy that
      does not require inventing a second column of content.
   ========================================================================= */
.wing-inner .wing-contact { max-width: 780px; margin-left: auto; margin-right: auto; }

/* =========================================================================
   5d. EMBED PLACEHOLDER — /apply/, /contact/ (and waiting-list). Composition
      only, no markup: `.wing-embed--placeholder`'s own min-height comes from
      an inline custom property (`--wing-embed-min-h`, v2-forms.css, not
      owned here) sized to the REAL form that will eventually load - 1000px
      on Apply, 560 on Contact. Fixed px, so it does not shrink at any
      width: measured on Apply at every width from 320 to 1600, it is
      currently a near-empty box holding two centred lines of type for
      roughly a full extra screen. That is a genuinely poor composition at
      every width this pass exists to check, not just 768/1024, and it is
      the single biggest thing wrong with how either page reads today.

      Capped here, not in v2-forms.css, because the fix only concerns the
      PLACEHOLDER state - `.wing-embed--placeholder` is the modifier class
      the generator drops the moment a real GoHighLevel URL exists (v2-forms
      .css's own comment on `.wing-embed`), so this rule stops applying
      itself, unedited, the day the form is connected; the real embed keeps
      whatever height its own page reserved for it. Specificity alone (two
      classes vs v2-forms.css's one) wins the cascade without `!important`. */
.wing-inner .wing-embed--placeholder { min-height: 240px; }

/* =========================================================================
   6. CLOSING BAND — every page, over Lake Hāwea, the same two actions.
   ========================================================================= */

.wing-inner .close.e-con {
  position: relative; background: var(--ink);
  --min-height: 64svh; min-height: 64svh !important;
  overflow: hidden; isolation: isolate; color: #fff;
  --justify-content: center; padding: clamp(56px, 6vw, 88px) 0;
}
.wing-inner .close > .close__layers,
.wing-inner .close > .close__layers > .elementor-widget-container {
  position: absolute; inset: 0; z-index: 0;
}
.wing-inner .close__media { position: absolute; inset: 0; z-index: 0; }
/* 29 Sep: the landscape reads and the ink sits only where the copy is. Same
   numbers, and the reasons, as components-v2.css §9 for the home page. */
.wing-inner .close__media img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 76%; filter: saturate(.8) brightness(.88); }
.wing-inner .close__tint { position: absolute; inset: 0; z-index: 1; pointer-events: none; background: rgba(53,120,140,.12); mix-blend-mode: multiply; }
.wing-inner .close__scrim {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background:
    radial-gradient(460px 230px at calc(50% + 530px) 61%, rgba(7,16,30,.55) 0%, rgba(7,16,30,.3) 55%, rgba(7,16,30,0) 100%),
    radial-gradient(820px 330px at calc(50% - 300px) 50%, rgba(7,16,30,.7) 0%, rgba(7,16,30,.58) 45%, rgba(7,16,30,.28) 75%, rgba(7,16,30,0) 100%),
    linear-gradient(90deg, rgba(7,16,30,.36) 0%, rgba(7,16,30,.18) calc(50% + 100px), rgba(7,16,30,0) calc(50% + 400px)),
    linear-gradient(180deg, rgba(7,16,30,.6) 0%, rgba(7,16,30,0) 24%, rgba(7,16,30,0) 70%, rgba(7,16,30,.7) 100%);
}
@media (max-width: 899px) {
  .wing-inner .close__media img { object-position: 50% 50%; }
  .wing-inner .close__scrim { background: linear-gradient(180deg, rgba(7,16,30,.64) 0%, rgba(7,16,30,.68) 30%, rgba(7,16,30,.7) 58%, rgba(7,16,30,.42) 70%, rgba(7,16,30,.36) 86%, rgba(7,16,30,.62) 100%); }
}
.wing-inner .close > .wing-shell { position: relative; z-index: 3; width: 100%; }
.wing-inner .close__grid.e-con { --flex-direction: row; --row-gap: 48px; --column-gap: 48px; --align-items: flex-end; }
.wing-inner .close__col.e-con { --width: 58.33%; flex: 0 1 58.33%; }
.wing-inner .close .display .elementor-heading-title { margin-bottom: 16px; }
.wing-inner .close__meta { display: flex; gap: 28px; flex-wrap: wrap; font-size: var(--wing-caption); color: rgba(255,255,255,.72); margin-top: 26px; }
.wing-inner .close__actions.e-con { --flex-direction: row; --row-gap: 12px; --column-gap: 12px; --justify-content: flex-end; --flex-wrap: wrap; --width: 41.66%; flex: 0 1 41.66%; }

/* =========================================================================
   7. NARROW VIEWPORTS
   ========================================================================= */

@media (max-width: 899px) {
  .wing-inner.hero--inner,
  .wing-inner .hero--inner { --wing-band: 52px; }

  .wing-inner .hero--inner .hero__grid.e-con { --flex-direction: column; --row-gap: 0px; --column-gap: 0px; }
  .wing-inner .hero--inner .hero__col.e-con { --width: 100%; flex: 1 1 auto; }
  .wing-inner .hero__actions.e-con { --flex-direction: column; --row-gap: 12px; --column-gap: 12px; }
  .wing-inner .hero__actions .btn.elementor-widget-button { width: 100%; }
  .wing-inner .hero__actions .btn .elementor-button { width: 100%; }
  .wing-inner .hero--inner .place { margin-top: 16px; }

  .wing-inner .fit__grid.e-con { --flex-direction: column; --row-gap: 32px; --column-gap: 32px; }
  .wing-inner .fit__intro.e-con, .wing-inner .fit__panelwrap { --width: 100%; flex: 1 1 auto; }
  .wing-inner .fit__opts { grid-template-columns: 1fr; }
  .wing-inner .fit__q { font-size: var(--wing-lede); }
  .wing-inner .fit__back { min-height: 44px; }

  .wing-inner .ledger__head.e-con { --flex-direction: column; --row-gap: 24px; --column-gap: 24px; --align-items: stretch; margin-bottom: 24px; }
  .wing-inner .ledger__head-a.e-con, .wing-inner .ledger__head-b { --width: 100%; flex: 1 1 auto; }
  .wing-inner .lrow, .wing-inner .ledger__colhead { grid-template-columns: 1fr; gap: 6px; }
  .wing-inner .lrow { padding: 14px 0; }
  .wing-inner .lrow__cmp::before { content: attr(data-name) ": "; font-size: var(--wing-caption); }

  .wing-inner .row { grid-template-columns: 1fr; gap: 12px; padding: 22px 0; }

  .wing-inner .costlists.costlists--3 { grid-template-columns: 1fr; gap: 32px; }

  .wing-inner .illus__grid.e-con { --flex-direction: column; --row-gap: 32px; --column-gap: 32px; }
  .wing-inner .illus__intro.e-con, .wing-inner .illus__panelwrap { --width: 100%; flex: 1 1 auto; }
  .wing-inner .illus__terms { grid-template-columns: 1fr; border-top: 0; }
  .wing-inner .illus__term, .wing-inner .illus__term + .illus__term { border-left: 0; padding-left: 0; border-top: 1px solid var(--rule); padding-top: 16px; }

  .wing-inner .stepper__grid.e-con { --flex-direction: column; --row-gap: 32px; --column-gap: 32px; }
  .wing-inner .stepper__left.e-con, .wing-inner .stepper__right { --width: 100%; flex: 1 1 auto; position: relative; top: 0; }
  .wing-inner .stepper__num, .wing-inner .track { display: none; }
  .wing-inner .stage { min-height: 0; padding: 24px 0; }
  .wing-inner .stage:not(.is-on) { opacity: 1; }

  .wing-inner .close__grid.e-con { --flex-direction: column; --row-gap: 28px; --column-gap: 28px; --align-items: stretch; }
  .wing-inner .close__col.e-con, .wing-inner .close__actions.e-con { --width: 100%; flex: 1 1 auto; }
  .wing-inner .close__actions.e-con { --flex-direction: column; --justify-content: flex-start; }
  .wing-inner .close__actions .btn.elementor-widget-button, .wing-inner .close__actions .btn .elementor-button { width: 100%; }
}

/* =========================================================================
   8. SECTION-TO-SECTION RHYTHM
   -------------------------------------------------------------------------
   Client: "the flow between sections isn't nice", "the gaps just don't look
   nice." Measured at 1440x900 (rhythm.py) before this section existed:

     how-funding-works  INK -> INK -> white -> PALE -> white -> white -> white -> INK
     what-we-fund       INK -> INK -> PALE -> white -> INK -> INK
     eligibility        INK -> INK -> white -> PALE -> PALE -> white -> INK
     about               INK -> white -> INK -> PALE -> INK -> INK
     faq                 INK -> white x6 -> INK

   THREE FAULTS.

   1) THE HERO DOES NOT END. On how-funding-works, what-we-fund, eligibility
      and application-process, the ink hero is followed immediately by an
      ink band 01, with --row-gap:0 between them (components-v2.css). Two
      ink boxes with nothing between them read as one merged slab, not two
      chapters - there is no colour change here to do the articulation job a
      colour change does everywhere else on the page.

   2) 202px OF DEAD AIR WHERE THE GROUND DOESN'T CHANGE. Every band runs
      ~101px of padding top and bottom (--wing-band at 1440). Where the next
      band is a different colour that reads as generous - "each section its
      own page." Where it is the SAME colour, the two facing 101px paddings
      stack into 202px of nothing. The home page already runs this
      correctly (its ledger and fields bands carry asymmetric padding
      against their own neighbours); the inner pages never did.

   3) LONG FLAT RUNS. how-funding-works stacks three consecutive white bands
      (2546px, roughly three screens). eligibility stacks two consecutive
      pale. FAQ stacks six white accordion groups - correct, since it is one
      continuous list, but each group still opens with a full-weight
      numbered index, so it reads as six chapters rather than one list.

   THE MECHANISM (faults 1 and 2). A band that changes ground keeps full
   --wing-band on both facing edges - that padding is correct and untouched
   below. A band that SHARES a ground with its neighbour collapses the
   facing pair to roughly a third each (0.32 x --wing-band, floored at 24px
   so mobile never goes cramped), so the two read as one field with an
   internal division instead of two stacked blocks. This is driven by the
   ground classes the generator already emits, grouped with :is()/:not(),
   not by nth-child - a page can gain or lose a band without this rule
   going stale, and a hand-authored ground swap (fault 3 below) is picked up
   automatically because it is read from the same classes.

   `.close` and `.wing-band` are deliberately left out of every group below.
   `.close` floors on its own 64svh min-height regardless of its padding, so
   collapsing it buys nothing, and it is the run into the final call to
   action, which the client did not flag - extra ink breathing room there
   reads as a deliberate pause before the ask, not dead air. `.wing-band` is
   already unpadded. Both are content-reviewed separately if that changes.

   `.e-con` is appended to every selector below purely for specificity: it
   beats the existing same-ground rule in components.css
   (`.e-con.wing-section--dark + .e-con.wing-section--dark`, 4 classes) and
   this file's own per-component `padding-block` declarations regardless of
   load order, since every element these rules can match already carries it.
   ========================================================================= */

/* --- Fault 1: the hero needs a deliberate end, only when what follows
   shares its ink - a hairline where a colour change would otherwise sit.
   The padding either side of this seam is handled by the shared-ground
   rule below (.hero--inner is a member of the ink group). */
.wing-inner > .hero--inner.e-con + :is(.wing-section--dark, .dark, .stepper.dark).e-con {
  border-top: 1px solid var(--rule-dark);
}

/* --- Fault 2: shared-ground pairs collapse the facing padding to roughly a
   third. Three groups - ink, pale, white/unpainted - each written as "this
   band gives up its bottom edge when the next one shares its ground" and
   "this band gives up its top edge when the previous one shared its
   ground." White is "none of the others" rather than a class of its own,
   because unpainted is the default for every band that doesn't opt into
   ink or pale; `.wing-section--run` is excluded here and handled on its own
   terms below, since it already has a padding rule for repetition.

   `.illus.on-light` is listed in the PALE group, not the white one, because
   fault 3a below recolours it to pale in CSS only - nothing in its markup
   says so, so it has to be told apart by name here or this whole mechanism
   would keep treating it as unpainted and collapse its ledger-facing edge
   as if the two still shared a ground.

   `.fit` carries `.pale` on both pages that use it (eligibility, apply), so
   without the `:not(.fit)` below it would qualify for this same collapse on
   either edge - eligibility's fit-check follows another pale section (THE
   REQUEST) and apply's precedes one (What happens next), so both pages would
   fold it into a shared field with its neighbour. Brief 03 asks the fit
   guide to read as the page's primary action, not its fourth topic; it
   cannot be moved earlier without reordering markup (see the report), but it
   can at least keep the full pause on both edges that announces a distinct
   destination rather than a continuation of the criteria around it. */
.wing-inner > :is(.hero--inner, .wing-section--dark, .dark, .stepper.dark).e-con
            + :is(.hero--inner, .wing-section--dark, .dark, .stepper.dark).e-con,
.wing-inner > :is(.wing-section--paper, .pale, .illus.on-light):not(.fit).e-con
            + :is(.wing-section--paper, .pale, .illus.on-light):not(.fit).e-con,
.wing-inner > :is(.wing-section, .ledger, .focus, .fit, .stepper):not(.wing-section--dark, .wing-section--paper, .pale, .dark, .wing-section--run).e-con
            + :is(.wing-section, .ledger, .focus, .fit, .stepper):not(.wing-section--dark, .wing-section--paper, .pale, .dark, .wing-section--run).e-con {
  padding-top: max(24px, calc(var(--wing-band) * .32));
}
.wing-inner > :is(.hero--inner, .wing-section--dark, .dark, .stepper.dark).e-con:has(+ :is(.hero--inner, .wing-section--dark, .dark, .stepper.dark).e-con),
.wing-inner > :is(.wing-section--paper, .pale, .illus.on-light):not(.fit).e-con:has(+ :is(.wing-section--paper, .pale, .illus.on-light):not(.fit).e-con),
.wing-inner > :is(.wing-section, .ledger, .focus, .fit, .stepper):not(.wing-section--dark, .wing-section--paper, .pale, .dark, .wing-section--run).e-con:has(+ :is(.wing-section, .ledger, .focus, .fit, .stepper):not(.wing-section--dark, .wing-section--paper, .pale, .dark, .wing-section--run).e-con) {
  padding-bottom: max(24px, calc(var(--wing-band) * .32));
}
/* The hairline this exception needs on arrival: the same "colour would
   otherwise mark this" language Fault 1 uses for the hero, applied where
   .fit still shares pale ground with what precedes it (eligibility, today).
   Without it, the restored full padding above is just more blank space;
   with it, the gap reads as a deliberate pause before a distinct
   destination. Not mirrored on the way out - apply's fit-check is followed
   by its own closely-related "What happens next" explanation, which reads
   as a continuation, not a new chapter, so it keeps the plain padding
   change with no added rule. */
.wing-inner > :is(.wing-section--paper, .pale):not(.fit).e-con + .fit.e-con {
  border-top: 1px solid var(--rule);
}

/* --- Fault 3a: how-funding-works' 2546px white run (sections 04, 05, 06).
   The cost illustrator (06) recolours to the page's other approved ground -
   the cheapest fix, and one that needs no generator change. It turns one
   three-screen white block into white/white (collapsed by the rule above,
   since the two still share a ground) then a real colour change into pale
   against the ledger before it, which rightly keeps full padding. Only this
   page uses `.illus`, so nothing else is affected. */
.wing-inner .illus.on-light { background: var(--pale); }

/* --- Fault 3b: FAQ's six accordion groups read as six chapters, because
   each opens with the same full-weight numbered index the genuine chapters
   use elsewhere on the page. A hairline between groups - this page's own
   connective tissue everywhere else - plus a tightened index turns six
   blocks into one ruled list; the first group, copy and structure are
   untouched. `.e-con` doubled on each side matches the specificity of
   components.css's own `.e-con.wing-section--run + .e-con.wing-section--run
   { padding-top: 0 }`, which this intentionally overrides. */
.wing-inner .wing-section--run.e-con + .wing-section--run.e-con {
  padding-top: 28px;
  border-top: 1px solid var(--rule);
}
.wing-inner .wing-section--run.e-con + .wing-section--run.e-con .wing-index {
  margin-bottom: 20px;
}

@media (prefers-reduced-motion: reduce) {
  .wing-inner .hero--inner .hero__media img { animation: none; }
}

/* =========================================================================
   9. DRAWN FUNDS AGAINST THE ILLUSTRATION'S ASSUMPTION
   (wingbuild_pages.py's `drawn_profile()`, inside the cost illustrator.)

   Hairlines, one fill and type. No chart library, no canvas, no script: the
   shape is four line segments in an inline SVG and it is identical with
   JavaScript off and under prefers-reduced-motion, because nothing about it
   moves.

   The markers sit at the centres of four equal columns, the same arithmetic
   `drawdown__steps` uses, so the labels below line up with a plain
   repeat(4, 1fr) grid at every width and the two diagrams agree with each
   other.

   Colour does not carry meaning on its own: the solid line is labelled
   "Drawn, and accruing interest" and the dashed one "The illustration above
   assumes this", so the key reads without colour perception. Teal is used
   for the drawn line and dots only, which are non-text marks, and the band
   is pale, where teal measures comfortably past the 3:1 non-text minimum.
   ========================================================================= */

/* Brief 09 section 1, "Understand releases": a deliberate pause before the
   diagram, not the same gap that already separates two paragraphs. A
   restrained rule marks the seam between "what the copy says" and "the
   diagram that draws it" - the same hairline language the calculator panel
   uses for its own groups, so the two halves of this section read as one
   composed explanation. */
.wing-inner .drawn { margin: 48px 0 0; padding: 28px 0 0; border-top: 1px solid var(--rule); }
.wing-inner .drawn__eyebrow { display: block; margin-bottom: 14px; color: var(--muted); }

.wing-inner .drawn__svg {
  display: block; width: 100%; height: auto;
  /* Strokes must not scale with the box, or the hairlines thicken on a wide
     screen and vanish on a phone. */
  vector-effect: non-scaling-stroke;
  overflow: visible;
}
.wing-inner .drawn__svg * { vector-effect: non-scaling-stroke; }

/* What the illustration above charges interest on: the whole amount, flat,
   from day one. Dashed because it is a premise, not a thing that happens. */
.wing-inner .drawn__assume {
  fill: none; stroke: var(--navy); stroke-width: 1;
  stroke-dasharray: 5 5; opacity: .45;
}
/* One fill, not two. An earlier version also shaded the space between the
   steps and the dashed line; on a pale ground two washes that light read as
   a single flat tint and neither region was legible. The filled area below
   the steps carries the message alone: what is filled is what interest is
   charged on, and the empty space up to the dashed line is what the
   illustration charges for and a real loan does not. */
.wing-inner .drawn__area { fill: var(--teal); opacity: .16; }
.wing-inner .drawn__line { fill: none; stroke: var(--teal); stroke-width: 2; }
/* Release points as rings, the same node the home drawdown uses: a 2px
   teal stroke on the band's own ground. r is in viewBox units, so at the
   chart's 455px width at 1440 this is a 10px ring, the home ring's size. */
.wing-inner .drawn__dot { fill: var(--pale); stroke: var(--teal); stroke-width: 2; r: 11px; }

.wing-inner .drawn__steps {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px; margin-top: 12px;
}
.wing-inner .drawn__step {
  text-align: center; font-size: var(--wing-caption); color: var(--muted);
  line-height: 1.35;
}

.wing-inner .drawn__key {
  display: flex; flex-wrap: wrap; gap: 8px 26px; margin-top: 18px;
  font-size: var(--wing-caption); color: var(--muted);
}
.wing-inner .drawn__k { position: relative; padding-left: 26px; }
.wing-inner .drawn__k::before {
  content: ""; position: absolute; left: 0; top: .55em;
  width: 18px; height: 2px;
}
.wing-inner .drawn__k--drawn::before { background: var(--teal); }
.wing-inner .drawn__k--assume::before {
  height: 0; border-top: 1px dashed var(--navy); opacity: .5;
}

/* base.css italicises <figcaption> site-wide; every caption in this design
   is upright, the same correction components-v2.css already makes for the
   media captions. */
.wing-inner .drawn__cap {
  margin: 14px 0 0; font-size: var(--wing-caption); color: var(--muted);
  max-width: 62ch; font-style: normal;
}

@media (max-width: 899px) {
  .wing-inner .drawn { margin-top: 32px; padding-top: 20px; }
  /* Four labels will not sit side by side on a phone without wrapping into
     an unreadable stack of single words, so only the two that carry the
     shape's meaning stay: where drawing starts and where it finishes. */
  .wing-inner .drawn__steps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .wing-inner .drawn__step:nth-child(2),
  .wing-inner .drawn__step:nth-child(3) { display: none; }
  .wing-inner .drawn__step:first-child { text-align: left; }
  .wing-inner .drawn__step:last-child { text-align: right; }
  .wing-inner .drawn__key { gap: 6px 18px; }
}

/* September 2026 polish: explain relationships, keep the established type,
   colour and photography. Figures use real HTML, with decorative connectors.
   No extra library, image request, hover-only content or required animation. */
.wing-v2 .wing-ip-map,
.wing-v2 .wing-review-map {
  --diagram-accent: var(--wing-teal);
  --diagram-rule: var(--wing-rule);
  margin: 40px 0 0;
  color: var(--navy, var(--wing-navy));
}
.wing-v2 .wing-diagram-label {
  display: block; font-size: 13px; font-weight: 500; font-style: normal; color: inherit;
  letter-spacing: .08em; text-transform: uppercase;
}
.wing-v2 .wing-ip-map__ownership {
  display: flex; align-items: center; gap: 24px; margin: 24px 0 36px;
  padding: 22px 24px; background: var(--pale, var(--wing-paleblue)); border-left: 2px solid var(--diagram-accent);
}
/* --wing-h3, not a private clamp (29 Sep): at 1440 the clamp gave 26px beside
   the ledger's 24px h3, one of two stray sizes taking /how-funding-works/ to
   12 distinct sizes against the budget of 10. */
.wing-v2 .wing-ip-map__ownership strong {
  font-size: var(--wing-h3); font-weight: 500; line-height: 1.25; letter-spacing: -.02em;
}
.wing-v2 .wing-ip-map__line {
  flex: 1; min-width: 24px; height: 1px; background: var(--diagram-accent);
  transform-origin: left;
}
.wing-v2 .wing-diagram-steps {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  list-style: none; padding: 0; margin: 0; counter-reset: none;
}
.wing-v2 .wing-diagram-steps > li { position: relative; padding: 0 36px 0 0; min-width: 0; }
.wing-v2 .wing-diagram-steps > li:last-child { padding-right: 0; }
.wing-v2 .wing-diagram-steps > li:not(:last-child)::before {
  content: ""; position: absolute; height: 1px; top: 22px; left: 56px; right: 12px;
  background: var(--diagram-rule); transform-origin: left;
}
/* 29 Sep: 2px, not 1px - the same ring stroke as the home drawdown's tick
   and the interest chart's release dot (both explicitly "a 2px teal
   stroke"), so the three diagram families read as one system of rings
   rather than three different weights. The connector line stays a 1px
   structural rule (below): it marks "step leads to step", not drawn funds,
   so it does not borrow the 2px "money" language the other lines use. */
.wing-v2 .wing-diagram-node {
  display: grid; place-items: center; width: 44px; height: 44px; margin-bottom: 24px;
  border: 2px solid var(--diagram-accent); border-radius: 50%;
  font-size: 14px; font-variant-numeric: tabular-nums; color: var(--diagram-accent);
}
.wing-v2 .wing-diagram-steps h3 {
  font-family: inherit; font-size: var(--wing-h3); line-height: 1.15;
  font-weight: 500; letter-spacing: -.025em; margin: 0 0 12px; color: inherit;
}
.wing-v2 .wing-diagram-steps p { font-size: 16px; line-height: 1.6; max-width: 32ch; margin: 0; color: inherit; }
.wing-v2 .wing-diagram-steps small { display: block; font-size: 14px; line-height: 1.6; margin-top: 12px; color: var(--wing-lightblue); }
.wing-v2 .wing-diagram-note { font-size: 14px; line-height: 1.6; margin: 28px 0 0; max-width: 80ch; }
/* Brief 09 section 4: the binding-agreement qualification belongs to the
   whole three-stage diagram, not to whichever column its capped measure
   happens to land under. Scoped to the IP/security figure only (how-funding-
   works' security_journey() is its one caller) so the reviewer figure on
   application-process, which carries no trailing note, gains no orphan
   rule. The hairline spans the full three-column width the steps already
   use; the note's own 56ch cap (section 10 below) then reads as a caption
   under that rule, not as a continuation of "The project". */
.wing-ip-map .wing-diagram-steps { border-bottom: 1px solid var(--wing-rule); padding-bottom: 28px; }
.wing-ip-map .wing-diagram-note { margin-top: 24px; }
.wing-v2 .wing-review-map {
  --diagram-accent: var(--wing-lightblue);
  --diagram-rule: rgba(184,205,225,.36);
  color: #fff; margin: 0; padding: 36px 0 56px;
}
.wing-v2 .wing-review-map figcaption { color: var(--wing-lightblue); margin-bottom: 28px; }
.wing-v2 .wing-review-map .wing-diagram-steps p { color: rgba(255,255,255,.82); }

/* The terms register was emitted without any row styling. This makes the
   key/value relationship visible, without changing a single lending term. */
.wing-inner .wing-spec { margin-top: 24px; }
.wing-inner .wing-spec__row {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr);
  gap: 16px; padding: 10px 0; align-items: baseline;
}
.wing-inner .wing-spec__row + .wing-spec__row { border-top: 1px solid rgba(184,205,225,.16); }
.wing-inner .wing-spec__key { font-size: var(--wing-label); line-height: 1.5; color: var(--wing-lightblue); }
.wing-inner .wing-spec__val { font-size: 16px; line-height: 1.5; }

/* One fit-check treatment across Home, Eligibility and Apply. The arrow
   lives in the existing aria-hidden ornament, so button names are unchanged. */
.wing-v2 .fit__panel .fit__opts { gap: 0 24px; }
.wing-v2 .fit__panel .fit__opt {
  min-height: 64px; padding: 14px 4px; border: 0; border-bottom: 1px solid var(--rule);
  border-radius: 0; background: transparent; color: var(--navy); font-size: 18px;
  line-height: 1.4; transform: none;
  transition: color .3s var(--wing-ease), border-color .3s var(--wing-ease), background .3s var(--wing-ease);
}
.wing-v2 .fit__panel .fit__opt i {
  order: 1; margin-left: auto; width: 16px; height: 16px; border: 0; border-radius: 0;
  background: transparent; position: relative; flex-shrink: 0;
  /* 450ms, the house hover duration - was .3s, off the one curve/duration
     every other nudge on the site uses (v2-bands.css's cost-preview link). */
  transition: transform .45s var(--wing-ease);
}
.wing-v2 .fit__panel .fit__opt i::before {
  content: ""; position: absolute; width: 13px; height: 1px; top: 7px; left: 0; background: currentColor;
}
.wing-v2 .fit__panel .fit__opt i::after {
  content: ""; position: absolute; width: 6px; height: 6px; top: 4px; right: 2px;
  border-top: 1px solid currentColor; border-right: 1px solid currentColor; transform: rotate(45deg);
}
.wing-v2 .fit__panel .fit__opt:is(:hover, :focus-visible, .is-picked) {
  color: var(--teal); border-color: var(--teal); background: rgba(53,120,140,.045);
}
@media (hover: hover) and (pointer: fine) {
  .wing-v2 .fit__panel .fit__opt:hover i { transform: translateX(3px); }
}
/* Keyboard parity, 29 Sep: the nudge only ever ran on `:hover`, so a
   keyboard user got the colour/border cue but never the arrow's own
   movement - the two feedbacks came apart for exactly the input method
   that relies on feedback most. Ungated, like every other focus-visible
   rule here. */
.wing-v2 .fit__panel .fit__opt:focus-visible i { transform: translateX(3px); }
.wing-v2 .fit__panel .fit__opt:focus-visible { outline: 2px solid var(--teal); outline-offset: 4px; }
.wing-v2 .fit__panel .fit__chips span { background: transparent; border-radius: 0; padding: 4px 8px; }
.wing-v2 .fit__panel .fit__back { min-height: 44px; }

/* Connector draw, retimed 28 Sep 2026. Each line used `view()` on ITSELF,
   and a 1px line's entry range is 1px of scroll, so the draw was never seen
   (the home drawdown's rail had the same fault). The timeline is now the
   whole diagram's, named on the figure, and the two connectors follow one
   another as it arrives. Lines only; rings and text never move. */
.wing-v2 .wing-ip-map, .wing-v2 .wing-review-map { view-timeline: --diagram block; }
.wing-v2 .wing-ip-map__ownership { view-timeline: --ownership block; }
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .wing-v2 .wing-ip-map__line {
      animation: wingDiagramDraw linear both;
      animation-timeline: --ownership; animation-range: entry 50% contain 20%;
    }
    .wing-v2 .wing-diagram-steps > li:not(:last-child)::before {
      animation: wingDiagramDraw linear both; animation-timeline: --diagram;
    }
    .wing-v2 .wing-diagram-steps > li:nth-child(1)::before { animation-range: entry 40% entry 80%; }
    .wing-v2 .wing-diagram-steps > li:nth-child(2)::before { animation-range: entry 80% contain 10%; }
    /* Stacked below 900px, the connectors run down the rings instead. */
    @media (max-width: 899px) {
      .wing-v2 .wing-diagram-steps > li:not(:last-child)::before { animation-name: wingDiagramDrawDown; }
      .wing-v2 .wing-diagram-steps > li:nth-child(1)::before { animation-range: entry 50% entry 80%; }
      .wing-v2 .wing-diagram-steps > li:nth-child(2)::before { animation-range: entry 80% entry 100%; }
    }
  }
}
@keyframes wingDiagramDraw { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes wingDiagramDrawDown { from { transform: scaleY(0); } to { transform: scaleY(1); } }
/* 768px composition pass: this block used to break at 699px, an orphan
   threshold that answers to nothing else on the site - every other
   component here (fit, ledger, stepper, illus, costlists, section 7 above)
   stacks to mobile at the shared 899px line. Measured at 768px before this
   change: .wing-diagram-steps still ran its 3-column grid, 230px per column,
   paragraph content down to 194px wide - three-line wraps, connector lines
   nearly touching the numbered circles, on the one width this pass exists to
   examine. Moved to 899px so the IP-ownership and reviewer diagrams stack to
   the same single-column timeline every neighbouring section already uses
   below that line, and so the horizontal scaleX() draw-in below correctly
   turns off wherever the line itself turns vertical (it already did at 699;
   it now does so for the same 700-899px range this fixes). `.fit__panel
   .fit__opt`'s own size step is unrelated to the diagrams and stays at its
   original 699px - the options are already single-column full-width by 899px
   (section 7) and 768px's 64px/18px sizing was verified comfortable, not
   cramped, so it is left alone rather than changed as a side effect. */
@media (max-width: 899px) {
  .wing-v2 .wing-ip-map__ownership { flex-wrap: wrap; gap: 8px 16px; padding: 18px; margin-bottom: 28px; }
  .wing-v2 .wing-ip-map__ownership .wing-diagram-label { width: 100%; }
  .wing-v2 .wing-ip-map__line { display: none; }
  .wing-v2 .wing-diagram-steps { grid-template-columns: 1fr; }
  .wing-v2 .wing-diagram-steps > li { padding: 0 0 28px 64px; }
  .wing-v2 .wing-diagram-steps > li:last-child { padding-bottom: 0; }
  .wing-v2 .wing-diagram-node { position: absolute; left: 0; top: 0; margin: 0; }
  .wing-v2 .wing-diagram-steps > li:not(:last-child)::before {
    top: 52px; bottom: 8px; left: 22px; right: auto; width: 1px; height: auto;
    transform-origin: top;
  }
  .wing-v2 .wing-diagram-steps h3 { padding-top: 8px; }
  .wing-v2 .wing-review-map { padding-block: 28px 40px; }
}
@media (max-width: 699px) {
  .wing-v2 .fit__panel .fit__opt { min-height: 60px; font-size: 17px; }
}
@media (prefers-reduced-motion: reduce) {
  .wing-v2 .fit__panel .fit__opt, .wing-v2 .fit__panel .fit__opt i { transition: none; }
  .wing-v2 .fit__panel .fit__opt:hover i { transform: none; }
}

/* =========================================================================
   10. READING MEASURE FOR SUPPORTING TEXT (inner pages)
   Brief 09 §3. Same single rule as the home page equivalent in v2-bands.css,
   and the same reasoning: body copy already obeys the approved 56ch measure,
   and the notes and captions underneath it obeyed none.

   Measured, content box, 1440:
     .wing-diagram-note   106 characters per line at 14px
     .drawn__cap           75 characters per line at 14px
   ========================================================================= */
.wing-inner .wing-diagram-note,
.wing-inner .illus__caveat,
.wing-inner .illus__note { max-width: 56ch; }
.wing-inner .drawn__cap { max-width: 56ch; }

/* The last pixel of horizontal overflow at 320px on the inner pages.
   Brief 09's acceptance list requires no horizontal overflow at 320.

   The hero's cover image is deliberately a little wider than its box so it
   can be positioned without letterboxing: measured at 320px it runs from
   -1.2 to 321.2, which tips the document's scrollWidth to 321. The home
   page has the identical structure and escapes only by a rounding accident,
   its image reaching 320.9. Clipping the media box itself is the honest fix
   and is what the box is for. This is NOT global `overflow-x: hidden`,
   which brief 07 §3 rules out and which would clip focus rings; it is one
   container clipping one decorative image it already crops. */
.wing-inner .hero__media { overflow: clip; }

/* The inner pages' comparator control, same fault and same fix as the home
   page equivalent in components-v2.css. `display: inline-flex` with no
   shrink allowed, so the two comparator buttons' intrinsic widths exceed the
   272px between the 24px gutters at 320px and nothing gives: measured left
   24, width 296.7, right 320.7, which rounds the document's scrollWidth up
   to 321. Allowing it to wrap lets the two options stack on a phone, which a
   segmented control can do without losing anything. */
@media (max-width: 360px) {
  .wing-inner .seg { max-width: 100%; flex-wrap: wrap; }
}

/* =========================================================================
   11. PRINT — brief 04's "What to prepare" checklist, "a print stylesheet
   is sufficient." `.wing-check` (build/wingbuild.py's `checklist()`) has no
   ID or page-specific class of its own - it is the same helper behind the
   readiness ticks on What we fund and Eligibility's four criteria, so this
   cannot be scoped to the Application-process instance alone without a
   markup change (see the report for what that change would be: a stable id
   on the section plus a `checklist_print` modifier). What CSS alone can do,
   and what actually matters for "printable": wherever a visitor prints a
   page carrying one of these lists, the marks and their labels render in
   solid ink instead of the screen's teal/navy on a coloured ground, and a
   single item never splits across a page break. Applied through `.wing-inner`
   so it cannot reach the home page's own copy of the pattern. */
@media print {
  .wing-inner .wing-check__mark { color: #000; }
  .wing-inner .wing-check__item { color: #000; break-inside: avoid; }
  /* motion.js appends this bar as a direct child of <body>, outside
     .wing-inner entirely (confirmed live: .wing-inner.contains() is false),
     so it needs its own unscoped rule - a fixed position:fixed leftover
     stuck mid-page in print output otherwise. Print-only; no screen effect,
     no conflict with the file that actually styles it (v2-rail.css). */
  .mobilecta { display: none; }
}

/* =========================================================================
   12. ONE TYPOGRAPHIC PEAK PER PAGE — About, Eligibility, What we fund.
   -------------------------------------------------------------------------
   Reported by the parallel structural pass and confirmed here (per-band
   max font size, hero excluded, 1440px): every band on these three pages
   tops out at 49px (--wing-h2), repeated five to seven times over near-equal
   band heights. Home, How funding works and Application process all clear
   that ceiling somewhere (95px, --wing-figure) and read as designed; these
   three never do, and that flatness - not spacing, not colour - is a
   plausible reading of the client's recurring "bland" reaction.

   The fix borrows an element every one of these bands already has rather
   than inventing a new one: the chapter numeral in its own index row
   ("01 THE PROJECT", "02 PRINCIPLES", "02 WHAT WE FUND"). Today that
   numeral is 13-14px, the same small-caps size as its label. Set to
   --wing-figure - the exact token the pages that already work use for their
   own peak - it becomes a genuine peak built from a digit that was already
   on the page, in the size the system already owns, with the label simply
   sitting at its baseline the way `.stepper__num small` already sits beside
   a figure-sized number elsewhere on Application process. Colour needs no
   change: `.wing-index__num`'s `var(--wing-accent)` already resolves to
   lightblue inside `.wing-section--dark` (components.css §"grounds",
   `--wing-accent: var(--wing-lightblue)`) and stays teal everywhere else,
   so the enlarged numeral inherits the correct on-ground colour for free.

   ONE band per page, picked for being the closest thing each page has to a
   defining claim, not the first available candidate:
   - Eligibility (page-id-10): "01 The project" - the four pass/fail
     criteria the rest of the page exists to qualify.
   - About (page-id-7): "02 Principles" - what every application is judged
     against, the page's actual argument for trusting Wing's process.
   - What we fund (page-id-9): "02 What we fund" - the three fields, this
     page's reason for existing.
   Scoped by WordPress's own `page-id-N` body class plus a structural
   position, since `.wing-index` and the plain `.index`/`b` pattern (the
   FIELDS component's own, not components.css's) are shared across every
   numbered band on the site - nothing about either marks "this one, not the
   others" except which page and which position it sits in. Not applied
   anywhere on Home, How funding works or Application process, which already
   have their own peak and do not need a second one - "one per page, not
   several, or you flatten it again at a higher volume." */
body.page-id-10 .wing-inner > .wing-section.wing-section--dark .wing-index__num,
body.page-id-7  .wing-inner > .wing-section.wing-section--paper:nth-child(5) .wing-index__num {
  font-size: var(--wing-figure);
  font-weight: var(--wing-display-weight);
  letter-spacing: var(--wing-ls-fig);
  line-height: .9;
}
body.page-id-10 .wing-inner > .wing-section.wing-section--dark .wing-index,
body.page-id-7  .wing-inner > .wing-section.wing-section--paper:nth-child(5) .wing-index {
  gap: 22px;
  margin-bottom: clamp(28px, 3.4vw, 44px);
}
body.page-id-9 .wing-inner > .focus .index b {
  font-size: var(--wing-figure);
  font-weight: var(--wing-display-weight);
  letter-spacing: var(--wing-ls-fig);
  line-height: .9;
}
body.page-id-9 .wing-inner > .focus .index {
  gap: 22px;
  margin-bottom: clamp(28px, 3.4vw, 44px);
}

/* =========================================================================
   5 OCT 2026, PRE-HANDOVER DESIGN AUDIT
   ========================================================================= */
/* The terms caption is a full sentence, so it reads as one, not as a
   tracked uppercase label (components.css groups it with the labels). */
.wing-inner .wing-stages__caption {
  text-transform: none; letter-spacing: 0; font-size: var(--wing-caption); line-height: 1.6;
}
/* On a phone every ledger row labels itself (row label, Wing's value, then
   "Government grant: ..."), so the stacked column header headed nothing. */
@media (max-width: 899px) {
  .wing-inner .ledger__colhead { display: none; }
}

/* =========================================================================
   5 Oct 2026: three components that read as 2010 ("clunky, 2010 vibes").
   CSS only; the markup and every word are unchanged.
   ========================================================================= */

/* 1. Checklists become a numbered index. Teal tick bullets are the oldest
   pattern on the site. The numerals use the same voice as the section index
   and the home page's "What the money can go on" list. A cross ("not
   funded") becomes an em dash, as on home's "What it cannot". The glyph
   span stays in the markup, aria-hidden, at font-size 0. */
.wing-check { counter-reset: wing-check; }
.wing-check__item {
  counter-increment: wing-check;
  grid-template-columns: 2.25em minmax(0, 1fr);
  gap: 18px;
  align-items: baseline;
  padding-block: 11px;
}
.wing-check__mark { font-size: 0; }
.wing-check__mark::before {
  content: counter(wing-check, decimal-leading-zero);
  content: counter(wing-check, decimal-leading-zero) / "";
  font-size: var(--wing-label);
  font-weight: 500;
  letter-spacing: .04em;
  font-variant-numeric: tabular-nums;
  color: var(--wing-accent);
}
.wing-check__item--cross .wing-check__mark::before {
  content: "\2014";
  content: "\2014" / "";
  color: inherit;
  opacity: .55;
}

/* 2. Diagram steps: display numerals under a hairline, no rings. Ringed
   numbers in outline circles were the second dated tell. Each step now
   opens on a rule with its number set large, like the section index at
   display size. The drawn connectors go: with no rings there is nothing
   to connect, and the rules carry the sequence. */
.wing-v2 .wing-diagram-steps { column-gap: clamp(28px, 3vw, 48px); }
.wing-v2 .wing-diagram-steps > li {
  padding: 22px 0 0;
  border-top: 1px solid var(--diagram-rule);
}
.wing-v2 .wing-diagram-steps > li:last-child { padding-right: 0; }
.wing-v2 .wing-diagram-steps > li::before,
.wing-v2 .wing-diagram-steps > li:not(:last-child)::before { content: none; display: none; }
.wing-v2 .wing-diagram-node {
  display: block;
  width: auto;
  height: auto;
  margin: 0 0 18px;
  border: 0;
  border-radius: 0;
  font-size: clamp(40px, 3.4vw, 56px);
  font-weight: var(--wing-display-weight, 500);
  line-height: 1;
  letter-spacing: -.03em;
  color: var(--diagram-accent);
}
.wing-ip-map .wing-diagram-steps { border-bottom: 0; padding-bottom: 0; }

/* 3. The ownership callout loses its tinted box and side bar. It becomes a
   single line, label, statement and a teal rule running on across the
   width, sitting over the three stages: you own it throughout. */
.wing-v2 .wing-ip-map__ownership {
  background: none;
  border-left: 0;
  padding: 0;
  margin: 28px 0 40px;
  gap: 20px;
}
.wing-v2 .wing-ip-map__ownership .wing-diagram-label { color: var(--wing-accent); }
.wing-v2 .wing-ip-map__line { height: 2px; }

@media (max-width: 899px) {
  .wing-v2 .wing-diagram-steps > li {
    display: grid;
    grid-template-columns: 64px minmax(0, 1fr);
    column-gap: 12px;
    padding: 20px 0;
  }
  .wing-v2 .wing-diagram-steps > li:last-child { padding-bottom: 0; }
  .wing-v2 .wing-diagram-node {
    position: static;
    grid-row: 1 / span 3;
    margin: 0;
    font-size: 36px;
  }
  .wing-v2 .wing-diagram-steps > li > :not(.wing-diagram-node) { grid-column: 2; }
  .wing-v2 .wing-diagram-steps h3 { padding-top: 0; }
  .wing-v2 .wing-ip-map__ownership { padding: 0; margin-bottom: 24px; }
}

/* 4. The terms register under "The numbers, up front." set the numbers at
   16px, the size of a footnote. Values now read as figures. */
.wing-inner .wing-spec__row { padding: 13px 0; }
.wing-inner .wing-spec__val {
  font-size: clamp(18px, 1.4vw, 22px);
  font-weight: 500;
  line-height: 1.35;
  letter-spacing: -.01em;
  font-variant-numeric: tabular-nums;
}

/* ==== v2-motion.css ==== */
/* =========================================================================
   Wing R&D Fund - the motion language, v2.

   OWNED BY THE MOTION WORK. Loads after v2-pages.css.
   Scope: cross-document view transitions, accordion open and close, form
   focus and error states, link underlines, and any movement that repeats
   across pages so the site reads as one thing.

   It does NOT restyle components. Layout, colour and type belong to
   components-v2.css, v2-bands.css and v2-pages.css. If a rule here needs to
   change a box, the box is wrong somewhere else.
   Reference: docs/master-plan.md section 3.6.

   Audit note (18-19 Sep): the home page's own reveal/stagger (40px rise,
   0.8s IO / entry-range view(), 70ms IO stagger, cubic-bezier(.22,.61,.36,1))
   already matches section 3.4 exactly in both motion.js and components-v2.css
   - nothing to fix there.

   Fixed 19 Sep (this file's own §7): inner pages used to get no reveal at
   all - the `data-reveal` attribute was only ever added, client-side, to
   `.wing-home` elements (motion.js `decorate()`). `decorate()` now marks the
   same class families on `.wing-inner` pages too, and §7 below gives the
   scroll-linked (`view()`) path a `.wing-inner` rule to match, mirroring
   components-v2.css §11's `.wing-home` one rather than editing it. The IO
   fallback needed no new CSS - it was already selector-driven from
   motion.js.

   -------------------------------------------------------------------------
   MEASURED AUDIT, 21 SEPTEMBER 2026. Everything below was read out of a live
   Chrome 148 against http://wing.72.60.237.187.nip.io, by scrubbing the
   scrollbar 10px at a time and sampling `getComputedStyle`, not by reading
   CSS. Four things were wrong and are corrected in §7 and §8:

   1. NOBODY COULD SEE THE REVEAL. Every scroll-linked range on the site was
      expressed against `entry`, and the `entry` phase is exactly as long as
      the element is tall. `entry 0% entry 35%` on a 67px comparison row is
      therefore 33px of scroll, and those 33px all happen while the row is
      between the bottom edge of the window and 30px above it. Measured: the
      row reaches opacity 1 with its top at y=870 in a 900px window. Sampling
      the whole home page every 25px (264 samples) caught ZERO elements
      mid-animation anywhere above the bottom 15% of the viewport. The reveal
      ran, cost 19-48 compositor animations a page, and was invisible. §7
      re-expresses the same keyframe against `cover`, which is viewport-
      relative, so the reveal now finishes around 60% up the window.

   2. THE STAGGER WAS ARBITRARY. `data-stagger` is written by decorate() onto
      every child of a marked group, whether that group lays its children out
      side by side or stacks them. Stacked rows already arrive one after the
      other; adding a delay on top only makes the later ones lag the reader's
      hand. §7 keeps the stagger where it means something (`.figs__grid`,
      `.down__grid`, `.costlists` on desktop) and drops it where it does not.

   3. A PARALLAX WAS RUNNING. components.css animates every framed photograph
      `translate: 0 -5% -> 0 5%` with a permanent `scale: 1.12`, scrubbed
      across its whole `cover` range - live on 9 images across 5 pages. Its
      own comment calls this "depth, not the parallax the direction rules
      out"; measured against the scrollbar it moves at a different rate from
      the page, which is what parallax is. §07 says "nothing bounces, spins
      or parallaxes", and no amendment in docs/open-decisions.md covers it.
      §8 turns it off, which also returns the photographs to 1:1 instead of a
      12% upscale.

   4. A SECOND REVEAL VOCABULARY WAS DEAD ON ITS FEET. components.css's
      `wing-unmask` targets `.wing-display .elementor-heading-title`, but the
      same block puts `overflow: hidden` on `.wing-display` and on the widget
      container, which makes them scroll containers, so the heading's own
      `view()` resolves against a box that never scrolls. Sampled on 8 pages
      at every 120px of scroll: all 22 headings sit permanently at the END of
      the animation. It has never once run. §8 retires it and gives those
      headings the site's one reveal instead, so an inner page's section
      number and its heading arrive together.

   Not changed here, and why: the 40px rise and the 0.8s IO duration are
   amendment #38, approved 18 Sep - only the RANGE was wrong, so only the
   range moved. The hero's `wingZoom` and `wingEnter` are components-v2.css's
   and are correctly gated behind `prefers-reduced-motion`.
   ========================================================================= */

/* =========================================================================
   1. CROSS-DOCUMENT VIEW TRANSITIONS (master-plan §3.6, WP11)
   -------------------------------------------------------------------------
   `@view-transition { navigation: auto }` only fires same-origin - the
   browser enforces that itself, nothing here has to. Checked Elementor's own
   CSS (frontend.min.css, widget-heading, widget-nav-menu, every per-post
   post-N.css on the live build) and its form/handlers JS for an existing
   `view-transition-name`: zero hits, so nothing here collides with it.
   Firefox, and any browser without `@view-transition` support, ignores this
   whole block and navigates exactly as before - the correct degrade, not a
   bug to patch around.

   The nav bar is named as one unit (the logo lives inside it) so it holds
   completely still - no crossfade, no morph - while everything else
   crossfades at the root duration.

   VERIFIED RUNNING, 21 Sep, Chrome 148: navigating by a real link click
   fires `pagereveal` carrying a ViewTransition, and the generated pseudo
   animations were read back off `document.getAnimations()`.

   THE PAGE TITLE USED TO BE NAMED SEPARATELY. It has been un-named, because
   the same read-back showed what that actually produced:

     ::view-transition-group(wing-page-title)  250ms  ease   (UA default)
     ::view-transition-old/new(wing-page-title) 450ms cubic-bezier(...)

   Two faults in one. The box finished moving 200ms before the type finished
   fading, because only old/new were given a duration and the group kept the
   UA's. And the group was morphing 651px -> 770.6px wide between two pages
   whose H1s are different lengths, which stretches the outgoing snapshot
   horizontally while it fades. Captured at 1/13 speed, the result is the
   home page's "Ideas worth building, funded." and the funding page's "A loan
   built around the project." sitting on top of each other in
   `mix-blend-mode: plus-lighter`, in the largest type on the page, over a
   photograph. Two different sentences ghosting through each other is not a
   transition, it is a collision.

   Nothing replaces it. The H1 is now captured inside the root snapshot and
   crossfades with the rest of the page in one 300ms move, under a nav that
   does not flinch. To put the effect back, restore the `h1` line and the
   old/new rule below it, and give ::view-transition-group(wing-page-title)
   the same 450ms so at least the two halves agree.
   ========================================================================= */
@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }

  .wing-v2 .nav { view-transition-name: wing-nav; }

  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: .3s;
    animation-timing-function: cubic-bezier(.22,.61,.36,1);
  }

  /* Hold still: the outgoing frame vanishes and the incoming one is already
     there - no fade, no resize morph, so it never competes with the crossfade
     underneath it. */
  ::view-transition-group(wing-nav),
  ::view-transition-old(wing-nav),
  ::view-transition-new(wing-nav) {
    animation: none;
  }
  ::view-transition-old(wing-nav) { opacity: 0; }
  ::view-transition-new(wing-nav) { opacity: 1; }
}

/* =========================================================================
   2. ACCORDIONS: NATIVE, since 23 September 2026 (brief 11 V11)
   -------------------------------------------------------------------------
   A grid-template-rows transition used to animate the answer open over
   450ms here, driven by motion.js's accordions(). Both are gone. The
   animation was not the problem; the 460ms input lock that had to exist
   to protect it was - a reader who changed their mind mid-open had the
   click dropped in silence. <details> is now the browser's again, which
   also restores find-in-page and native anchor-into-a-closed-answer.
   What remains is the icon transition the brief asks for, below.
   ========================================================================= */

/* The marker rotates rather than swapping glyph - transform only. One more
   ancestor class than components.css's `.wing-faq__item[open] > summary::after`
   gives this higher specificity, so it wins regardless of load order. */
.wing-v2 .wing-faq__item > summary::after {
  content: "+";
  display: inline-block;
  /* 200ms: the brief's 150-220ms control range. */
  transition: transform .2s var(--ease, cubic-bezier(.22, .61, .36, 1));
}
.wing-v2 .wing-faq__item[open] > summary::after,
.wing-v2 .wing-faq__item.is-open > summary::after {
  content: "+";
  transform: rotate(45deg);
}

/* 29 Sep: the answer opens and closes over 450ms, in CSS alone, where the
   browser can animate to `auto` (Chromium 131+). No script, so no input
   lock: a second click mid-open simply reverses the transition, which was
   the objection to the old scripted version. The text is clipped while
   the box grows, never faded. Elsewhere the native snap is unchanged. */
@supports (interpolate-size: allow-keywords) {
  @media (prefers-reduced-motion: no-preference) {
    .wing-v2 .wing-faq__item { interpolate-size: allow-keywords; }
    .wing-v2 .wing-faq__item::details-content {
      block-size: 0; overflow-y: clip;
      transition: block-size .45s var(--wing-ease), content-visibility .45s allow-discrete;
    }
    .wing-v2 .wing-faq__item[open]::details-content { block-size: auto; }
  }
}

/* =========================================================================
   3. LINKS UNDERLINE ON HOVER, EVERYWHERE THEY DO NOT ALREADY (item 3)
   -------------------------------------------------------------------------
   Nav links and footer links already draw their own underline
   (components-v2.css `.nav__links a::after` / `.foot__links a::after`,
   both already 300ms scaleX from the left - nothing to add there). This
   covers what is left: a link inside a paragraph of real copy, home page or
   inner page, current or future ("back to X" reads the same way once it
   exists) - without needing to know each page's own content classes. Never
   on a button: `.btn` is excluded, and a `.btn` never sits inside a `<p>`
   anyway.
   ========================================================================= */
.wing-main p a:not(.btn) {
  position: relative;
  text-decoration: none;
}
.wing-main p a:not(.btn)::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .3s var(--ease, cubic-bezier(.22, .61, .36, 1));
}
.wing-main p a:not(.btn):hover::after,
.wing-main p a:not(.btn):focus-visible::after {
  transform: scaleX(1);
}

/* =========================================================================
   4. FORMS RESPOND (item 4)
   -------------------------------------------------------------------------
   The field's box - border, radius, background - belongs to whoever owns
   components.css; this only adds the moving parts on top of it: an inset
   rule that grows and turns teal on focus (the one place teal appears on a
   form), a submit button that lifts like every other button (it renders as
   plain `.elementor-button` with no `.btn` class, so components-v2.css's
   button-hover rule never reaches it today), and an entrance for whatever
   inline validation message Elementor's form widget inserts.

   Not screenshot-verified against a real failed submission: doing that means
   actually submitting the live contact/apply form, which risks sending a
   real enquiry and needs sign-off this task does not have. Built and
   animation-tested instead against a local static mock carrying Elementor's
   documented `.elementor-message` markup - see the session report.

   READ THIS BEFORE SPENDING ANY MORE TIME ON THIS SECTION (21 Sep). It has
   no live targets and, as things stand, it never will. Counted on the
   preview: `.wing-form` and `.elementor-widget-form` appear ZERO times on
   all eleven pages - /contact/'s `.wing-contact__form` is an empty div. And
   the forms that are coming are GoHighLevel forms embedded as IFRAMES
   (CLAUDE.md, decided 19 Sep), so this stylesheet cannot reach their fields
   at all: a parent document's CSS stops at the iframe boundary. Focus,
   error and submit feedback inside those forms will have to be configured in
   GHL, not here. The three rules are kept because they cost nothing and
   would be correct if a native field ever appeared, but do not read them as
   "the forms have motion". They do not.
   ========================================================================= */
.wing-v2 .wing-form :is(input[type="text"], input[type="email"], input[type="tel"], input[type="url"], select, textarea) {
  transition: box-shadow .3s var(--ease, cubic-bezier(.22, .61, .36, 1));
  box-shadow: inset 0 -1px 0 0 var(--rule, #D5DFE9);
}
.wing-v2 .wing-form :is(input[type="text"], input[type="email"], input[type="tel"], input[type="url"], select, textarea):focus {
  box-shadow: inset 0 -2px 0 0 var(--teal, var(--wing-teal, #35788C));
}

.wing-v2 .elementor-widget-form .elementor-button {
  transition: transform .45s var(--ease, cubic-bezier(.22, .61, .36, 1));
}
/* Brief 11 V11: gated on a real pointer. On a touch screen `:hover` sticks
   after a tap, so the submit button stayed lifted with nothing to
   un-hover it. Keyboard focus feedback is outside this query. */
@media (hover: hover) and (pointer: fine) {
  .wing-v2 .elementor-widget-form .elementor-button:hover {
    transform: translateY(-2px);
  }
}

@keyframes wingFieldError {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: none; }
}
.wing-v2 .wing-form .elementor-message {
  animation: wingFieldError .3s var(--ease, cubic-bezier(.22, .61, .36, 1)) both;
}

/* =========================================================================
   5. ROLLING VALUES AND DISSOLVING CELLS (29 Sep 2026)
   -------------------------------------------------------------------------
   The crossfade that lived here dipped the stepper's numeral and the
   fit-check's "Question N of 3" to opacity 0 on every change, which read as
   a flash. motion.js `roll()` now slides the old value out of a clipped
   window as the new one arrives; `.roll` is that window. Both values share
   one grid cell, so the window never changes size mid-roll. `overflow: clip`
   rather than `hidden`, because hidden would move the numeral's baseline
   and drop "of 04" out of line beside it.

   `.swap` is `dissolve()`'s equivalent for the ledger's comparator cells:
   old and new words overlap in one cell while one lifts away and the other
   settles, so a cell is never empty.
   ========================================================================= */
.roll { display: inline-grid; overflow: hidden; overflow: clip; overflow-clip-margin: .06em; }
.roll > .roll__v { grid-area: 1 / 1; }
.swap { display: inline-grid; }
.swap > * { grid-area: 1 / 1; }

/* The comparator's sliding pill (29 Sep). motion.js `placeThumb()` writes
   the chosen option's box into --thumb-*; the pill sits under the labels
   and slides between them. Without the script there is no `.has-thumb`,
   and the chosen button keeps its own navy fill. */
.wing-v2 .seg.has-thumb { position: relative; isolation: isolate; }
.wing-v2 .seg.has-thumb::before {
  content: ""; position: absolute; left: 0; top: 0; z-index: -1;
  width: var(--thumb-w, 0); height: var(--thumb-h, 0);
  border-radius: 100px; background: var(--wing-navy);
  transform: translate(var(--thumb-x, 0), var(--thumb-y, 0));
}
.wing-v2 .seg.has-thumb button[aria-checked="true"] { background: none; }
@media (prefers-reduced-motion: no-preference) {
  .wing-v2 .seg.has-thumb.is-ready::before {
    transition: transform .55s var(--wing-ease), width .55s var(--wing-ease);
  }
  /* The label turns white at the pill's pace, and the hover tint on the
     option just chosen goes at once, so it never veils the arriving pill. */
  .wing-v2 .seg.has-thumb.is-ready button[aria-checked="true"] {
    transition: color .55s var(--wing-ease), transform .45s var(--wing-ease);
  }
}

/* =========================================================================
   6. HOVER LIFT - AUDIT (item 6)
   -------------------------------------------------------------------------
   Checked every `:hover`/`:focus-visible` transform actually reachable on a
   live page (components-v2.css, v2-pages.css, v2-bands.css, and
   components.css cross-checked against what the current build renders,
   since components.css still carries dead selectors - `.wing-header__cta`,
   `.wing-step`, `.wing-focus__item` - for classes no generator emits any
   more). Everything live already matches 2px / 450ms / transform-only, no
   scale, no shadow: `.btn`, both `.fit__opt` copies, and `.wing-stage` (whose
   own later rule in components.css already cancels its transform to `none`
   on hover in favour of a hairline draw - not a drift). The one live gap was
   the form submit button, fixed in section 4 above.

   RE-MEASURED 21 Sep by hovering the real elements, not by reading CSS.
   `.btn .elementor-button` reports `transform, background, border-color,
   color` at `0.45s cubic-bezier(.22,.61,.36,1)` and travels `none` ->
   `translateY(-2px)`: exactly the §07 hover, correctly built. (`--wing-hover-
   dur` itself resolves to 0.3s, not the 0.45s in tokens.css, because
   components.css line 1205 redeclares it - the buttons are unaffected only
   because they hard-code .45s rather than using the token. Whoever owns
   components.css should reconcile those two; it is not this file's to fix.)

   ONE THING WAS OUT BY 40ms, and it is the most-used control on the site.
   Picking an answer in the fit check adds `.is-picked` immediately - the row
   turns teal - and motion.js then waits 260ms before swapping in the next
   question, deliberately, so the reader sees their answer register. But
   `.fit__opt` transitions colour over 300ms, so the acknowledgement was
   still fading IN at the moment the question it belongs to was replaced. The
   reader never saw a finished state. Making the picked state land faster,
   rather than making the hold longer, keeps the guide feeling quick: the
   confirmation completes at ~140ms and then sits for 120ms before the step
   changes. Hover keeps its unhurried 300ms - only the committed state
   hurries, which is the right way round.
   ========================================================================= */
.wing-v2 .fit__panel .fit__opt.is-picked,
.wing-v2 .fit__opt.is-picked {
  transition-duration: .14s;
}

/* =========================================================================
   7. THE REVEAL - ONE RANGE, AND ONE THAT CAN ACTUALLY BE SEEN
   -------------------------------------------------------------------------
   History. components-v2.css §11 gave `.wing-home [data-reveal]` a
   scroll-linked reveal; on 19 Sep this section mirrored it under
   `.wing-inner`, because decorate() had just started marking inner pages
   too. Both used `animation-range: entry 0% entry 35%`, and that is the
   number this section now changes. It does not touch the keyframe, the 40px
   rise or the linear timing - those are amendment #38 and §07's own curve,
   and they were right.

   WHY `entry` WAS THE WRONG PHASE. The `entry` phase of a `view()` timeline
   lasts exactly as long as the element is tall: it begins when the element's
   top edge reaches the bottom of the window and ends when its bottom edge
   does. So `entry 35%` is 35% of the ELEMENT, not 35% of anything the reader
   can see. Measured on the live home page at 1440x900:

     .lrow      67px tall   reveal ran over  33px of scroll, done at top=870
     .fig      126px tall   reveal ran over  60px of scroll, done at top=850
     .fit__panel 298px      reveal ran over 104px of scroll, done at top=811
     .wing-index 18px tall  reveal ran over ~30px of scroll, done at top=884

   In a 900px window every one of those finishes while the element is in the
   bottom 90px of the screen, most of it still below the fold. Then a second
   sweep - the whole page, every 25px, 264 samples - found no element at all
   caught between opacity 0 and 1 above the bottom 15% of the viewport. The
   motion was not subtle. It was not happening anywhere the eye was.

   `cover` FIXES IT, because `cover` is the element PLUS the viewport: its
   length is `element height + window height`, so a percentage of it is a
   real distance on screen no matter how tall or short the element is.
   `cover 0%` is the same instant as `entry 0%` - the element's top touching
   the bottom edge - so only the END moves. At 34%:

     900px window, 67px row   -> 329px of scroll, finishing with top at 571
     900px window, 600px band -> 510px of scroll, finishing with top at 390
     812px phone, 122px row   -> 318px of scroll, finishing with top at 494

   which lands every reveal around 60% of the way up the window, where
   someone is reading, and gives it 300-500px of travel to do it in. Same
   keyframe, same distance, same curve; it simply now happens on screen.

   IT STILL REVERSES ON SCROLL-BACK. That is not new and is not a decision
   taken here - it is inherent to `animation-timeline`, and it is what
   amendment #38 approved on 18 Sep ("scroll-linked stays as approved").
   What IS new is that the reversal is now visible, because everything about
   this reveal is now visible. docs/open-decisions.md still logs scroll-
   linked motion as the one thing the design council could not settle and as
   Christine's call. Flag it with the change, not after it.

   TO REVERT: put `entry 0% entry 35%` back in the five ranges below and
   delete the two stagger-suppression blocks. Nothing else here depends on
   the new numbers.
   ========================================================================= */
/* Entrance reveal: REMOVED, 23 September 2026 (brief 11, V01).
   A `@supports (animation-timeline: view())` block here used to animate
   `[data-reveal]` opacity against the scroll position. Scroll-timeline
   progress is a function of POSITION, not time, so text parked mid-range
   stayed part-transparent for as long as the reader stayed still, and text
   in the last screen of a document could never finish at all. Measured
   while stationary: 0.436 on a section heading, 0.487 on a comparison row.
   The whole system is gone - motion.js no longer marks anything, and no
   rule selects `[data-reveal]`. Essential text is opaque on first paint. */

/* =========================================================================
   8. THE OTHER TWO VOCABULARIES, RETIRED
   -------------------------------------------------------------------------
   components.css carries a second, older scroll-linked system from before
   Direction 05. Three of its four animations are still live on the built
   pages, and none of the three should be. Each is switched off here rather
   than deleted there, because components.css belongs to someone else this
   week and because a one-line revert is worth more than a tidy diff. All
   three overrides are inside the same `@supports` / no-preference envelope
   the originals are, so reduced motion and unsupporting browsers are
   untouched either way.

   8a. `wing-media-drift` - THE PARALLAX. Live on 9 photographs across
   /about/, /how-funding-works/, /what-we-fund/, /eligibility/ and
   /application-process/. It scrubs `translate: 0 -5%` to `0 5%` across the
   image's whole `cover` range, holding `scale: 1.12` throughout; on a 320px
   frame that is 32px of travel at a rate measurably different from the page
   under it. components.css argues the clipping frame makes this "depth, not
   the parallax the direction rules out". The reader cannot see the argument,
   only the effect, and §07's line is flat: nothing parallaxes. It is also
   the ONLY motion on the site a reader currently notices, which is the
   wrong way round when the approved reveal is invisible. Turning it off also
   drops the permanent 12% upscale, so the photographs render at their own
   resolution again instead of being enlarged and re-cropped.

   8b. `wing-unmask` - THE ONE THAT NEVER RAN. 22 headings across 8 pages,
   sampled every 120px of scroll on every one of them: all sat permanently at
   the animation's END state. The cause is in the same components.css block -
   it puts `overflow: hidden` on `.wing-display` and on the widget container
   to clip the mask, which makes both of them scroll containers, so the
   heading's `view()` resolves against a box that never scrolls and the
   timeline is inert. Nobody has ever seen this animation. Retiring it also
   settles the vocabulary question: §07 asked for one reveal, amendment #30
   removed the blur, and a mask-wipe was components.css's own unapproved
   substitute for it.

   8c. ...which leaves those section headings with no reveal at all, while
   the `01 / PURPOSE` index above them has one. Most of an inner page is
   still built by wingbuild.py's older helpers, so `.wing-display` and
   `.wing-index` - not `.display` and `.index` - are what five of /about/'s
   six bands actually use, and decorate() never marks either. Both are given
   the site's single reveal here, on the same range as everything else, so a
   band's number and its heading arrive together. Checked first: no
   `.wing-display` or `.wing-index` anywhere on the site sits inside a
   `[data-reveal]`, and none of them is in a hero - the highest is an H2 at
   y=660 - so nothing double-animates and no LCP element is gated.

   The leftover `overflow: hidden` + `padding-block: .08em` on `.wing-display`
   existed only to clip 8b's mask. It is harmless now and it is not this
   file's to remove; noted for whoever owns components.css.
   ========================================================================= */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {

    /* 8a, 8b and 8c lived here: counter-rules that switched off
       components.css's parallax, its heading mask and its section-heading
       reveal from a distance, because that file was someone else's to edit
       at the time. On 23 Sep 2026 the whole block was removed from
       components.css itself (brief 11, V01), so there is nothing left for
       them to cancel and they went with it. Only 8d survives, because the
       animations IT retunes are v2-pages.css's and are still there. */

    /* 8d RETIRED 29 Sep 2026. It patched `entry 0% entry 80%` on lines that
       measured their OWN 1-2px height - `entry` was unmeasurably short, so
       the draw finished before it was visible, the same fault the home
       drawdown's rail had. v2-pages.css no longer has that fault: its
       diagram hairlines (28 Sep) now read a NAMED view-timeline - `--diagram`
       on `.wing-ip-map`/`.wing-review-map`, `--ownership` on
       `.wing-ip-map__ownership` - set on the diagram's own container, a
       normal-height box, so `entry`/`contain` are real scroll distance again
       and each connector is sequenced to follow the one before it.

       This file's flat override sat at (0,4,1) - one class-level selector
       above v2-pages.css's per-connector ranges at (0,3,1) - so it always
       won, collapsed every connector onto one identical `cover 0%-40%`
       range regardless of `:nth-child`, and undid the sequencing. Nothing
       here to override any more; confirmed in-browser on
       /application-process/ and /how-funding-works/ that each connector now
       draws in turn, in sequence, as its diagram arrives. */
  }
}

@media (prefers-reduced-motion: reduce) {
  .wing-v2 :is(.wing-index, .wing-display) {
    animation: none !important; opacity: 1 !important; transform: none !important; translate: none !important;
  }
}

/* =========================================================================
   9. TEXT SELECTION (item 5) - nothing set one before 29 Sep, so a reader
      who drags across copy got the OS default (usually blue-on-white,
      wrong navy against the brand and unreadable navy-on-navy inside a
      dark band). Navy/white is the site's own "selected" language already
      - it is what a picked fit-option and a filled milestone ring both use.
      Inside a dark band, navy-on-navy would vanish, so light blue/ink
      swaps in, the same pairing the dark bands already use for accents.

      Scoped to the same class families `.dark`/`.hero`/`.close` and
      `.wing-section--dark`/`.hero--inner`/`.close` already use everywhere
      else on the site to know a band is dark (components.css §"grounds",
      v2-pages.css line ~99) - no new marker invented for this. */
::selection { background: var(--wing-navy); color: #fff; }
.wing-home :is(.dark, .hero, .close) ::selection,
.wing-inner :is(.dark, .wing-section--dark, .hero--inner, .close) ::selection {
  background: var(--wing-lightblue); color: var(--wing-ink);
}

/* =========================================================================
   10. FRAMED PHOTOGRAPHS OPEN AS THEY ARRIVE (29 Sep 2026)
   -------------------------------------------------------------------------
   Photos used to simply be there. Now each framed inline photo opens like a
   window: the frame's clip goes from a 5% inset to none while the image
   settles from 1.05 to 1. Photographs only, never text, so nothing a reader
   needs is ever part-drawn.

   Scope. `.wing-media` minus `--bleed`: the six framed photos on /about/,
   /how-funding-works/, /what-we-fund/, /eligibility/ and
   /application-process/. Not heroes, not the full-bleed bands (they carry
   copy), not the closing band. Every framed photo sits more than a screen
   above the page end, so the range always completes (checked at 1440x900
   and 390x844).

   Range. `entry 0% cover 30%` on the frame's own named timeline, so the
   image reads the frame's position, not its own scaled box. It finishes
   with the frame 75-100% in view, never resting half-open mid-read. At
   1440x900 three frames sit on the fold at load and rest 2-3% inset until
   the first scroll; that is 150px of photo, then it completes.

   Linear, because scroll is the clock. clip-path and scale, so no layout
   shift (CLS measured 0 on all five pages). The frame's ink ground shows
   inside the window while the image decodes, never white. This is not the
   retired `wing-media-drift`: nothing moves relative to the page.
   ========================================================================= */
/* 29 Sep, revised: the clip-path "window" is gone. clip-path cannot run on
   the compositor, so it repainted every frame of the scroll, and frames on
   the fold sat visibly inset at load. What remains is the scale settle
   alone, a compositor-only transform inside the frame's own overflow, plus
   the photo's fade-in once decoded (§11). */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .wing-media:not(.wing-media--bleed) > .wing-media__frame {
      view-timeline: --wing-frame block;
    }
    .wing-media:not(.wing-media--bleed) > .wing-media__frame > img {
      animation: wing-frame-settle linear both;
      animation-timeline: --wing-frame;
      animation-range: entry 0% cover 40%;
    }
  }
}
@keyframes wing-frame-settle { from { scale: 1.06; } to { scale: 1; } }

/* =========================================================================
   11. PHOTOGRAPHS FADE IN ONCE DECODED (29 Sep 2026)
   -------------------------------------------------------------------------
   motion.js `photoFades()` marks any framed or closing-band photo that is
   still loading with `.is-loading`; it fades in over 700ms when decoded,
   instead of popping over an empty frame. Photographs only, never text.
   Without JavaScript nothing is ever marked, so nothing is ever hidden.
   ========================================================================= */
@media (prefers-reduced-motion: no-preference) {
  .wing-media__frame img, .close__media img { transition: opacity .7s var(--wing-ease); }
}
.wing-media__frame img.is-loading, .close__media img.is-loading { opacity: 0; }

/* ==== v2-archive.css ==== */
/* =========================================================================
   Wing R&D Fund — Insights archive, single article, 404 and search, v2.

   OWNED BY THIS WORK ONLY. Enqueued last in functions.php's css chain
   (tokens -> base -> components -> components-v2 -> v2-bands -> v2-pages ->
   v2-motion -> v2-archive), so it wins ties on equal specificity against
   every file above it, the same guarantee v2-pages.css documents and relies
   on for its own overrides of Elementor's defaults.

   Scope: the four Theme Builder documents this file's generators own —
   Wing Insights Archive (post 41), Wing Insight Article (post 42), Wing 404
   (post 43), Wing Search Results (post 44) — built by build/build-archive.py
   and build/build-404.py. All four are wrapped in `.wing-inner` by
   `wrap_page()` (wingbuild_pages.py, imported not copied), so every selector
   below is written `.wing-inner .thing`, matching v2-pages.css's own
   convention, and nothing here can reach the home page or the eight
   existing inner pages.

   REUSED WITHOUT A SINGLE NEW RULE: the hero (`.hero--inner`, all four
   layers, the 62svh band, the place caption) and the closing band (`.close`)
   are the exact components `inner_hero()` / `standard_closing()` already
   build for every other page, and v2-pages.css already styles both
   completely. The `.display` / `.display--h1` / `.label` / `.muted`
   primitives are the same shared classes too. What follows is only the
   markup genuinely new to these four templates: the ruled post list (shared
   by the archive and by search results), the article's editorial head and
   prose measure, and the 404's route list and eligibility CTA.
   ========================================================================= */

/* =========================================================================
   0. INSIGHTS MASTHEAD — shortened, this archive only.

      docs/design-refinement/README.md's own recommendation: "the live
      archive has no published insights... a large hero plus closing
      dominates a page with no entries; consider a shorter editorial
      masthead on this archive alone." `.hero--inner` is the shared 62svh
      inner-page hero every other page also opens on (styled in
      v2-pages.css, not this file); this rule does not touch that shared
      component. It reaches ONLY the one hero whose very next sibling is
      this file's own `.archive.e-con`, which happens on exactly one
      template - the Insights archive - verified against every other
      inner page's markup before shipping this (none matched). This file
      is enqueued after v2-pages.css (see the file header above), so it
      wins the specificity tie against that file's own `.hero--inner`
      rule without `!important`.

      42svh against the shared 62svh: still full enough to carry the
      aerial and the honest "hasn't published any insights yet" framing,
      short enough that an empty list doesn't sit under a hero sized for
      nine populated rows.
   ========================================================================= */
.wing-inner > .hero--inner.e-con:has(+ .archive.e-con) {
  --min-height: 42svh; min-height: 42svh !important;
  padding: clamp(88px, 9vw, 112px) 0 clamp(32px, 4vw, 48px);
}

/* =========================================================================
   1. RULED POST LIST — the Insights archive's populated state, and Search
      Results. A single column (`archive_posts(columns="1", ...)`) already
      makes Elementor's own grid render as one item per row; what follows
      turns that row into a ruled list entry (date, title, one-line summary)
      instead of a boxed card, and hides the widget's own "Read" link
      because the title is already the link.

      Verified against elementor-pro's own PHP on the box
      (modules/posts/skins/skin-base.php): render_post() emits
      `<article class="elementor-post elementor-grid-item"><div
      class="elementor-post__text"><h3 class="elementor-post__title">…
      <div class="elementor-post__meta-data">…<div
      class="elementor-post__excerpt">…`, no thumbnail markup at all once
      `{skin}_thumbnail` is "none". `display:contents` on `.elementor-post__text`
      lets its own children become direct grid items of `.elementor-post`,
      which is what lets the date sit in its own column without an extra
      wrapper Elementor does not offer a setting for.
   ========================================================================= */

.wing-inner .archive.e-con { padding: calc(var(--wing-band) * .7) 0 var(--wing-band); }

.wing-inner .wing-archive .elementor-posts-container {
  display: block;
  border-top: 1px solid var(--rule);
}
.wing-inner .wing-archive .elementor-post.elementor-grid-item {
  display: grid;
  grid-template-columns: 160px minmax(0, 1fr);
  column-gap: 40px;
  align-items: baseline;
  padding: 28px 0;
  margin: 0;
  border: 0;
  border-bottom: 1px solid var(--rule);
  background: none;
}
.wing-inner .wing-archive .elementor-post__text { display: contents; }
/* `minmax(0, 1fr)` on the track handles the column; `min-width: 0` on the
   two text items handles the GRID ITEM'S own automatic minimum size, which
   otherwise defaults to its max-content width. The excerpt's `white-space:
   nowrap` below (for a guaranteed one-line summary) makes that max-content
   width the length of the whole unwrapped sentence — without `min-width: 0`
   that unbreakable width becomes the item's floor, so the grid "blows out"
   past the 1fr track and the row overflows its container. Caught on a real
   populated row at 375px, where it was wide enough to clip mid-word; the
   two test posts' short titles never triggered it at 1440px, which is why
   `ops/measure.py`'s scrollWidth check alone did not catch it either — that
   metric reads the document's own scrollWidth, and a `overflow-x: hidden`
   ancestor elsewhere on the page hid the bleed without fixing it. */
.wing-inner .wing-archive .elementor-post__meta-data {
  grid-column: 1;
  grid-row: 1 / span 2;
  margin: 0;
  font-size: var(--wing-caption);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.wing-inner .wing-archive .elementor-post__title {
  grid-column: 2;
  grid-row: 1;
  min-width: 0;
  margin: 0 0 6px;
  font-size: var(--wing-h3);
  font-weight: 500;
  letter-spacing: var(--wing-ls-h3);
  line-height: 1.2;
}
.wing-inner .wing-archive .elementor-post__title a {
  color: inherit;
  text-decoration: none;
}
.wing-inner .wing-archive .elementor-post:hover .elementor-post__title a,
.wing-inner .wing-archive .elementor-post__title a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 4px;
}
.wing-inner .wing-archive .elementor-post__excerpt {
  grid-column: 2;
  grid-row: 2;
  min-width: 0;
  margin: 0;
  max-width: 62ch;
}
.wing-inner .wing-archive .elementor-post__excerpt p {
  margin: 0;
  color: var(--muted);
  font-size: var(--wing-small);
  /* Brief 11 V10: was `white-space: nowrap` + ellipsis, which clips every
     excerpt to a single line. The archive is empty today, so this has never
     been seen - it would have shipped broken the day Wing published its
     first insight. An approved excerpt should wrap at a readable measure. */
  max-width: 68ch;
  line-height: var(--wing-lh-body);
}
/* The title is already the link; a second "Read" link per row is noise a
   ruled list does not need. */
.wing-inner .wing-archive .elementor-post__read-more { display: none; }

/* The rule and the measure are two different widths, fixed 19 Sep. Both the
   `border-top` and the `max-width: 44ch` used to sit on this one element, so
   the rule was drawn at the measure's width: about 530px of line across an
   1155px column, stopping just under halfway with nothing to its right. That
   reads as a page that failed to finish drawing rather than one deliberately
   waiting for its first post. The rule now belongs to the widget, which is
   full content width, and the measure stays on the sentence. `:has()` is the
   same no-JS empty-state test used a few rules below. */
.wing-inner .wing-archive:has(.elementor-posts-nothing-found) {
  border-top: 1px solid var(--rule);
  padding-top: 40px;
}
.wing-inner .wing-archive .elementor-posts-nothing-found {
  padding: 0 0 8px;
  border-top: 0;
  font-size: var(--wing-lede);
  font-weight: 500;
  letter-spacing: -0.02em;
  max-width: 44ch;
}

/* "Point to something useful" — a plain sibling, shown ONLY when the list
   is actually empty. `nothing_found_message` itself is HTML-escaped by
   Elementor Pro for security with no hook available without touching
   functions.php, so it can carry the plain sentence but never a link; this
   block carries the link, gated by the one thing that's reliably different
   between the two states: whether `.elementor-posts-nothing-found` exists
   inside the widget. Same no-JS technique `fields_section()` already uses
   in this build (`:has()` for the hover dim). */
.wing-inner .archive__empty-help { display: none; }
.wing-inner .wing-archive.elementor-widget-archive-posts:has(.elementor-posts-nothing-found) + .archive__empty-help {
  display: block;
  padding-top: 28px;
}
.wing-inner .archive__empty-help p {
  margin: 0;
  max-width: 52ch;
  font-size: var(--wing-body);
}
.wing-inner .archive__empty-help a {
  color: var(--teal);
  text-decoration: underline;
  text-underline-offset: 3px;
}

.wing-inner .wing-archive .elementor-pagination {
  margin-top: 40px;
  padding-top: 24px;
  border-top: 1px solid var(--rule);
  display: flex;
  gap: 18px;
  font-size: var(--wing-caption);
}
.wing-inner .wing-archive .elementor-pagination a,
.wing-inner .wing-archive .elementor-pagination span {
  color: var(--muted);
  text-decoration: none;
}
.wing-inner .wing-archive .elementor-pagination a:hover { color: var(--navy); }
.wing-inner .wing-archive .elementor-pagination .page-numbers.current { color: var(--navy); font-weight: 500; }

/* =========================================================================
   2. ARTICLE — editorial head (back link, eyebrow, title, date) and a
      65-75 character prose measure with generous leading, per the brief.
      No author box, no share buttons, no related-posts grid.
   ========================================================================= */

/* Same pattern, same fix. No article is published yet so this one could
   not be reproduced live; it is repaired because it is demonstrably the
   same arithmetic, not because it was observed failing. */
.wing-inner .article-head.e-con {
  padding: calc(var(--wing-header-h) + clamp(32px, 3.4vw, 48px)) 0 calc(var(--wing-band) * .4);
  border-bottom: 1px solid var(--rule);
}
.wing-inner .article__back {
  display: inline-flex;
  align-items: center;
  font-size: var(--wing-small);
  color: var(--muted);
  text-decoration: none;
  margin-bottom: 32px;
}
.wing-inner .article__back:hover { color: var(--teal); text-decoration: underline; text-underline-offset: 4px; }
.wing-inner .article__eyebrow {
  color: var(--teal);
  margin: 0 0 14px;
}
.wing-inner .article-head .display--h1.elementor-widget-heading .elementor-heading-title {
  max-width: 20ch;
}
.wing-inner .article__meta.elementor-widget-post-info { margin-top: 24px; }
.wing-inner .article__meta .elementor-icon-list-items {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
  gap: 18px;
}
.wing-inner .article__meta .elementor-icon-list-icon { display: none; }
.wing-inner .article__meta .elementor-icon-list-text {
  font-size: var(--wing-caption);
  color: var(--muted);
}

.wing-inner .article-body.e-con { padding: var(--wing-band) 0; }
.wing-inner .article__body {
  font-size: var(--wing-body);
  line-height: 1.7;
}
/* The measure goes on the DIRECT CHILDREN, never the widget wrapper itself:
   `.elementor.elementor .e-con > .elementor-widget { max-width: 100% }` is
   (0,4,0) and beats a max-width set on `.article__body` at (0,2,0) — the
   exact gotcha components-v2.css's own header documents ("Measure goes on
   the inner <p> or <h2>, never on the wrapper"). Confirmed against the
   rendered DOM: theme-post-content emits `<p>`/`<h2>`/etc as direct children
   of the widget div, no intermediate wrapper to hang this off instead. */
.wing-inner .article__body > * { max-width: 70ch; }
.wing-inner .article__body > * + * { margin-top: 28px; }
.wing-inner .article__body h2 {
  font-size: var(--wing-h2);
  font-weight: var(--dw);
  letter-spacing: var(--wing-ls-h2);
  line-height: var(--wing-lh-h2);
  margin: 0;
}
.wing-inner .article__body h3 {
  font-size: var(--wing-h3);
  font-weight: 500;
  letter-spacing: var(--wing-ls-h3);
  margin: 0;
}
.wing-inner .article__body ul,
.wing-inner .article__body ol { padding-left: 1.2em; margin: 0; }
.wing-inner .article__body li + li { margin-top: 10px; }
.wing-inner .article__body a { color: var(--teal); text-decoration: underline; text-underline-offset: 3px; }

/* =========================================================================
   3. 404 — the four primary-nav destinations as a ruled list (the same
      grammar as the archive and as `fields_section()`'s rows, not a card
      grid), then one CTA to the eligibility check.
   ========================================================================= */

.wing-inner .notfound.e-con { padding: calc(var(--wing-band) * .75) 0 var(--wing-band); }
.wing-inner .notfound__routes { border-top: 1px solid var(--rule); }
.wing-inner .notfound__route {
  display: grid;
  grid-template-columns: 48px 1fr;
  column-gap: 24px;
  align-items: start;
  padding: 26px 0;
  border-bottom: 1px solid var(--rule);
  text-decoration: none;
  color: inherit;
}
.wing-inner .notfound__route-num {
  font-size: var(--wing-caption);
  font-weight: 500;
  color: var(--teal);
  font-variant-numeric: tabular-nums;
  /* Lines the number's cap-height up with the title's, since the title
     sits above a second, smaller line (the description) and a plain
     `baseline` alignment was measured (ops/align.py) resolving against
     that second line instead. */
  padding-top: 0.35em;
}
.wing-inner .notfound__route-body b {
  position: relative;
  display: inline-block;
  font-size: var(--wing-h3);
  font-weight: 500;
  letter-spacing: var(--wing-ls-h3);
}
.wing-inner .notfound__route-body b::after {
  content: "";
  position: absolute; left: 0; right: 0; bottom: -2px; height: 1px;
  background: currentColor;
  transform: scaleX(0); transform-origin: left;
  transition: transform .3s var(--ease);
}
.wing-inner .notfound__route:hover .notfound__route-body b::after,
.wing-inner .notfound__route:focus-visible .notfound__route-body b::after { transform: scaleX(1); }
.wing-inner .notfound__route-body small {
  display: block;
  margin-top: 6px;
  font-size: var(--wing-small);
  color: var(--muted);
}
.wing-inner .notfound__cta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 20px;
  padding-top: 44px;
}
.wing-inner .notfound__cta p {
  margin: 0;
  font-size: var(--wing-lede);
  font-weight: 500;
  letter-spacing: -0.02em;
}

/* =========================================================================
   4. SEARCH — the query echoed, then a client-side result count above the
      same ruled list the archive uses.
   ========================================================================= */

/* Brief 11 V05. `calc(var(--wing-band) * .55)` is 55.4px at 1440 and the
   fixed bar is 76px tall, so the word "Search" rendered behind the
   navigation - not tight, actually obscured. A masthead with no hero
   behind it has to reserve the header itself, then add its own breathing
   on top of that (the brief asks for 32-48px). The archive hero never had
   the bug because its own `clamp(88px, ...)` already exceeded 76. */
.wing-inner .search-head.e-con { padding: calc(var(--wing-header-h) + clamp(32px, 3.4vw, 48px)) 0 calc(var(--wing-band) * .35); border-bottom: 1px solid var(--rule); }
.wing-inner .search__eyebrow { color: var(--teal); margin: 0 0 14px; }
/* The search results title is the page's H1 but carries `.display`, the
   section-heading helper, so it rendered at 49px while every other task
   page's title took the 46px reference role. Brief 11 V07 puts search in
   the task role with the rest. */
.wing-inner .search-head .display.elementor-widget-heading .elementor-heading-title {
  max-width: 30ch;
  font-size: var(--wing-display-task);
  line-height: 1.12;
}
.wing-inner .search__count {
  margin: 20px 0 0;
  font-size: var(--wing-small);
  color: var(--muted);
  min-height: 1.4em; /* holds its line before the count script runs, no layout jump */
}

/* =========================================================================
   5. NARROW VIEWPORTS
   ========================================================================= */

@media (max-width: 899px) {
  .wing-inner .wing-archive .elementor-post.elementor-grid-item { grid-template-columns: minmax(0, 1fr); row-gap: 6px; padding: 24px 0; }
  .wing-inner .wing-archive .elementor-post__meta-data { grid-column: 1; grid-row: 1; }
  .wing-inner .wing-archive .elementor-post__title { grid-column: 1; grid-row: 2; }
  .wing-inner .wing-archive .elementor-post__excerpt { grid-column: 1; grid-row: 3; max-width: none; }

  .wing-inner .notfound__route { grid-template-columns: 32px 1fr; column-gap: 16px; padding: 22px 0; }
  .wing-inner .notfound__cta { flex-direction: column; align-items: flex-start; }

  .wing-inner .article__body { max-width: 100%; }
}

/* TWO HAIRLINES WITH NOTHING BETWEEN THEM, on the empty archive.

   `.elementor-posts-container` carries an unconditional `border-top` so the
   first post has a rule above it. With no posts there is no first post, and
   the container still drew its rule and still occupied 43px. A separate rule
   added earlier the same day put a second rule on `.wing-archive` itself so
   the empty state's sentence had a line above it. The result was two blank
   hairlines stacked with an empty 43px box between them, which reads as a
   page that failed to load rather than one waiting for its first article.

   The container's rule is the one to drop, because it exists to separate
   posts from each other and there are none. `.wing-archive`'s rule stays and
   does the job on its own. `:has()` is the same no-JS empty-state test used
   elsewhere in this file. */
.wing-inner .wing-archive:has(.elementor-posts-nothing-found) .elementor-posts-container {
  border-top: 0;
  min-height: 0;
}

/* ==== v2-forms.css ==== */
/* =========================================================================
   Wing R&D Fund - forms, v2.

   OWNED BY THE FORMS WORK. Loads after v2-motion.css.
   Scope: the contact form, the pre-qualification form, the waiting list, and
   any confirmation or error state that belongs to them.

   REVISED 19 SEP — GOHIGHLEVEL EMBEDS ARE REPLACING THE NATIVE FORMS. The
   client decided every form on the site (contact, apply, waiting list)
   becomes a GoHighLevel form embedded in an <iframe>, not an Elementor
   "form" widget. `build/build-waitlist.py`'s `ghl_embed()` already builds
   the waiting list on this architecture (§9 below styles it: `.wing-embed`
   / `.wing-embed--placeholder`). §§1-8 below are the ORIGINAL native-form
   treatment, kept because `/contact/` and `/apply/` still run Elementor
   "form" widgets today - `build/pages_part3.py` has not been migrated to
   the embed yet (that file is owned by other work in progress; see
   docs/waiting-list.md for the exact change it needs). Once it is, and
   both pages carry `.wing-embed` instead of `.wing-form`, §§1-8 become dead
   and should come out with that same change.

   Focus and error MOTION belongs to v2-motion.css. This file is the boxes:
   field sizing, labels above inputs, helper text, error placement, the
   submit row, and the confirmation panel.

   Both native forms share the one Elementor Pro "form" widget, marked
   `_css_classes: "wing-form"` in build/pages_part3.py (contact, apply) - so
   `.wing-form` reaches every field on both of them, and nothing here needs
   a second selector.
   Markup verified by reading the rendered HTML of all three pages, not
   assumed:

     .wing-form .elementor-form-fields-wrapper.elementor-labels-above
       .elementor-field-group.elementor-field-group-wing_x[.elementor-col-50|100]
         label.elementor-field-label            (always renders - show_labels
                                                   defaults true - never a
                                                   placeholder standing in
                                                   for it)
         input/select/textarea.elementor-field
       .elementor-field-group.e-form__buttons
         button.elementor-button

   On a failed field, Elementor's own JS appends the error AS THE LAST CHILD
   of that field's OWN .elementor-field-group - i.e. already inside the same
   box as the label and input, already after them in source order:

     <span class="elementor-message elementor-message-danger elementor-help-inline
                   elementor-form-help-inline" role="alert">...</span>

   So "errors below the field they belong to" is a fact about source order
   this file did not have to create - it only has to stop that span from
   doing anything except sit quietly under the input. Same story on submit:
   success or a form-level failure appends a message as the last child of
   <form>, after the (by then emptied) fields wrapper.

   No error/danger colour exists anywhere in tokens.css - the brand palette
   is "four colours and two grounds, there are no others" and none of the
   four reads as an error state. A validation error still has to be visibly
   distinct from ordinary navy body text for a sighted user (the `role="alert"`
   text covers screen readers on its own), so one small semantic addition
   sits below, scoped to this file only and never added to :root: a single
   muted, low-saturation red used for nothing but form error states. It is
   not proposed as a brand colour and nothing outside a form should reach
   for it.
   ========================================================================= */

.wing-form {
  --form-danger:    #A32E27; /* semantic-only, see file header. 7.1:1 on white */
  --form-danger-bg: #F8ECEB; /* same hue, barely tinted, for the field's own row */
}

/* =========================================================================
   1. THE FIELDS WRAPPER — the shared measure every row and the submit row
      align to, so "the submit row aligned with the form's own measure" is
      one rule rather than something to remember per form.
   ========================================================================= */

.wing-form .elementor-form-fields-wrapper {
  max-width: 780px;
  margin-inline: 0;
}

/* =========================================================================
   2. LABELS — always above the input, always their own line, never styled
      to read as a placeholder (no inset position, no collapsing onto the
      field, no disappearing at rest). Set in the site's own small-caps
      label voice (index numerals, eyebrows, spec keys - components.css §1)
      so a form reads as this site's, not a generic one.
   ========================================================================= */

.wing-form .elementor-field-group > label.elementor-field-label {
  display: block;
  position: static;
  margin-bottom: 10px;
  font-size: var(--wing-label);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.4;
  color: var(--wing-text);
}

/* Vertical rhythm between rows only - `.elementor-field-group` also names
   the submit row (`.e-form__buttons`, already its own flex row by
   Elementor's own CSS) and the select wrapper's parent, so this stays a
   margin, never a `display` change that would reach those too. */
.wing-form .elementor-field-group.elementor-column { margin-bottom: 28px; }

/* Acceptance (consent, waitlist tick-box) fields carry a second, sentence-
   case label for the tick itself - that one is the field's real, only
   label and stays exactly as Elementor renders it, sentence case, next to
   the box. */
.wing-form .elementor-field-subgroup label {
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
  font-size: var(--wing-small);
  color: var(--wing-text);
}

/* =========================================================================
   3. FIELDS — 56px tall (comfortably over the 48px floor) on every
      breakpoint, so mobile never has to fall back to a separate, smaller
      rule to still clear the 44px touch-target minimum.
   ========================================================================= */

.wing-form :is(input[type=text], input[type=email], input[type=tel],
               input[type=url], input[type=number], input[type=date],
               select, textarea) {
  display: block;
  width: 100%;
  min-height: 56px;
  padding: 14px 16px;
  font-family: var(--wing-font);
  font-size: 16px;           /* 16px minimum - iOS zooms the viewport on
                                 focus below it */
  line-height: 1.4;
  color: var(--wing-text);
  background: var(--wing-white);
  border: 1px solid var(--wing-rule);
  border-radius: var(--wing-radius);
  /* No `transition` property here on purpose - v2-motion.css owns focus and
     error motion and loads BEFORE this file (functions.php's enqueue order
     is v2-bands, v2-pages, v2-motion, v2-archive, v2-forms); setting
     `transition` here, even to `none`, would win the cascade and silently
     cancel whatever it defines. This rule only ever touches box properties. */
}

.wing-form textarea { min-height: 140px; padding-top: 14px; resize: vertical; }

.wing-form .elementor-select-wrapper { min-height: 56px; }
.wing-form .elementor-select-wrapper select { min-height: 56px; border: none; }

/* Helper text: this codebase's one working example of it today is a
   placeholder used ALONGSIDE a real label, never in place of one (the
   "Organisation or trading name" field on /apply/ is the precedent this
   follows) - so it reads as a light hint at rest and disappears on input,
   exactly as a hint should, while the label above it never moves and never
   disappears. */
.wing-form ::placeholder {
  color: var(--wing-text-muted);
  opacity: 1;
  font-style: normal;
}

/* =========================================================================
   4. CHECKBOXES / ACCEPTANCE — a small box, a large tap target. The
      checkbox stays its native size; the row around it does not.
   ========================================================================= */

.wing-form .elementor-field-subgroup { display: block; }
.wing-form .elementor-field-option {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  min-height: 44px;
  padding-block: 10px;
}
.wing-form .elementor-field-option input[type=checkbox],
.wing-form .elementor-field-option input[type=radio] {
  width: 20px;
  height: 20px;
  flex: none;
  margin-top: 2px;
}

/* =========================================================================
   5. ERRORS — the box, not the motion. Positioned by source order alone
      (see file header); this only has to make the box readable.
   ========================================================================= */

.wing-form .elementor-field-group.elementor-error :is(input, select, textarea),
.wing-form .elementor-field-group.elementor-error .elementor-select-wrapper select {
  border-color: var(--form-danger);
  background: var(--form-danger-bg);
}

.wing-form .elementor-message.elementor-help-inline.elementor-message-danger {
  display: block;
  margin-top: 8px;
  font-size: var(--wing-caption);
  line-height: 1.5;
  color: var(--form-danger);
}

/* A form-level failure (not tied to one field) appends as the last child of
   <form>, below the whole fields wrapper - styled as a small standing
   notice, not a floating toast. */
.wing-form form.elementor-form > .elementor-message.elementor-message-danger {
  display: block;
  max-width: 780px;
  margin-top: 20px;
  padding: 16px 20px;
  border: 1px solid var(--form-danger);
  border-radius: var(--wing-radius);
  background: var(--form-danger-bg);
  font-size: var(--wing-small);
  color: var(--form-danger);
}

/* =========================================================================
   6. THE SUBMIT ROW — aligned to the same 780px measure as every field
      above it. All three forms already carry Elementor's own "stretch"
      button-width setting, which fills the row via `flex-basis: 100%` on
      `.elementor-button-align-stretch … .elementor-button` (Elementor's own
      frontend.min.css, checked on the rendered page rather than assumed) -
      a rule this file does not fight. Undoing it means beating a
      four-class selector on `flex-basis` specifically (width alone loses
      to flex-basis in a flex row), on THREE forms, one of them the
      multi-step pre-qualification form with its own "Continue"/"Back"
      buttons - too much surface to risk for a preference this brief never
      asked for. A button stretched to the row's own 780px measure already
      satisfies "aligned with the form's own measure" as literally as
      possible, so this section only sets the button's height, type and
      colour and leaves its width exactly as Elementor already renders it.
   ========================================================================= */

.wing-form .elementor-field-group.e-form__buttons { max-width: 780px; margin-top: 12px; }

/* The final submit button's own wrapper carries BOTH `elementor-field-group`
   and `e-form__buttons` on the one div. A multi-step form's "Continue" and
   "Back" buttons do not - verified on the rendered /apply/ page - they sit
   in `.elementor-field-group.e-form__buttons__wrapper`, nested a level
   deeper INSIDE a plain `.e-form__buttons` container, so a selector that
   demands both classes on one node (as this rule first shipped) never
   matches them, and they were falling through to the kit's default teal.
   Dropping the `.e-form__buttons` requirement and matching any
   `.elementor-button` under any `.elementor-field-group` catches Submit,
   Continue and Back alike, still at three classes - (0,3,0) - which beats
   the kit's `.elementor-kit-5 .elementor-button` at (0,2,0) outright,
   regardless of source order. */
.wing-form .elementor-field-group .elementor-button {
  min-height: 52px;
  padding: 0 32px;
  font-size: var(--wing-action-size);
  font-weight: 500;
  border-radius: var(--wing-radius-pill);
  background: var(--wing-navy);
  color: var(--wing-white);
  border: 1px solid var(--wing-navy);
}

.wing-form .elementor-field-group .elementor-button:hover,
.wing-form .elementor-field-group .elementor-button:focus {
  background: var(--wing-navy-lift);
  color: var(--wing-white);
}

/* =========================================================================
   7. THE CONFIRMATION PANEL — a considered reply, not a green tick.
      Appends as the last child of <form>, after the (by then emptied)
      fields wrapper - see file header. Hiding the emptied fields on success
      is a progressive enhancement: `:has()` support shows one clean panel,
      and its absence just leaves the blank fields visible above the
      message rather than breaking anything.
   ========================================================================= */

.wing-form form.elementor-form > .elementor-message.elementor-message-success {
  display: block;
  max-width: 56ch;           /* a reply reads at the site's own prose
                                 measure, not stretched to the field row's
                                 780px */
  margin-top: 8px;
  padding: 32px clamp(20px, 3vw, 40px);
  border: 1px solid var(--wing-rule);
  border-top: 3px solid var(--wing-navy);
  border-radius: var(--wing-radius);
  background: var(--wing-paleblue);
  color: var(--wing-navy);
  font-size: var(--wing-lede);
  line-height: var(--wing-lh-lede);
}

.wing-form .elementor-form-fields-wrapper:has(+ .elementor-message-success) {
  display: none;
}

/* =========================================================================
   8. MOBILE — fields already clear 44px on every breakpoint (rule 3); the
      submit button already stretches full width at every breakpoint (rule
      6's note), so this only tightens the vertical rhythm between rows,
      which is what a 375px column actually needs less of.
   ========================================================================= */

@media (max-width: 899px) {
  .wing-form .elementor-field-group.elementor-column { margin-bottom: 24px; }
}

/* =========================================================================
   9. THE EMBED — GoHighLevel forms as an <iframe>, current architecture
      (see the file header). Built by `build/build-waitlist.py`'s
      `ghl_embed()`, an `elementor-widget-html` carrying the class
      `wing-embed-widget` on its own wrapper, with `.wing-embed` (a real
      embed) or `.wing-embed--placeholder` (no URL yet) as the actual box
      inside it.

      A cross-origin iframe's own content is opaque to this site's CSS, so
      everything here is the FRAME: the measure it sits in, matching the
      780px the native forms used above; the border and radius, matching
      the rest of the site's "structure from rules and space, radius 0-3px,
      no shadows"; and the space around it. Nothing here can reach past the
      iframe's own boundary.
   ========================================================================= */

.wing-embed-widget { max-width: 780px; margin-top: 8px; }

.wing-embed {
  display: block;
  width: 100%;
  min-height: var(--wing-embed-min-h, 480px);
  border: 1px solid var(--wing-rule);
  border-radius: var(--wing-radius);
  background: var(--wing-paleblue); /* shows through until the iframe paints -
                                        a held breath, not a blank void */
  overflow: hidden;                 /* clips the iframe to the frame's own
                                        radius */
}

.wing-embed iframe { display: block; width: 100%; height: 100%; border: 0; }

/* 5 Oct 2026, the live GoHighLevel forms. Each draws its own white card, so
   the frame steps back. With its border and pale ground it read as a box
   inside a box, and on /apply/ and /waiting-list/ it ran 1154px wide around
   a card about 800px wide. The width cap needs the stronger selector:
   `.elementor.elementor .e-con > .elementor-widget { max-width: 100% }`
   beats `.wing-embed-widget` alone (the project's known Elementor gotcha).
   overflow is visible so the card's own edge is never clipped. */
.wing-embed:not(.wing-embed--placeholder) {
  border: 0; border-radius: 0; background: transparent; overflow: visible;
}
.elementor.elementor .e-con > .elementor-widget.wing-embed-widget { max-width: 820px; }
.wing-embed__fallback { margin: 12px 0 0; font-size: var(--wing-caption); color: var(--wing-text-muted); }

/* No URL yet - a considered, clearly labelled placeholder, not a blank box
   or a broken one. Dashed, not solid, so it reads as a stand-in rather
   than the finished frame above it. */
.wing-embed--placeholder {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 8px;
  padding: clamp(24px, 4vw, 40px);
  border: 1px dashed var(--wing-rule);
  background: var(--wing-white);
  text-align: center;
}

.wing-embed--placeholder .wing-embed__label {
  margin: 0;
  font-size: var(--wing-label);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--wing-text-muted);
}

.wing-embed--placeholder p:not(.wing-embed__label) {
  margin: 0;
  font-size: var(--wing-small);
  color: var(--wing-text-muted);
  max-width: 46ch;
  margin-inline: auto;
}

/* 5 Oct 2026: /apply/'s application section. The notes sat in a narrow
   left column with the right half empty, and the form ran full width
   underneath, under a 24px heading for the one action the site exists for.
   From 1024px: the heading and notes on the left, the form on the right,
   the heading at section size. Stacked below that, unchanged in order. */
#application-form .wing-display--sm .elementor-heading-title {
  font-size: var(--wing-h2);
  font-weight: var(--wing-display-weight, 500);
  line-height: 1.02;
  letter-spacing: -.03em;
  margin-bottom: 8px;
}
@media (min-width: 1024px) {
  #application-form > .wing-shell {
    --display: grid;
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    column-gap: clamp(56px, 7vw, 128px);
    row-gap: 0;
    align-items: start;
  }
  #application-form > .wing-shell > .elementor-widget:not(.wing-embed-widget) { grid-column: 1; }
  #application-form > .wing-shell > .elementor-widget.wing-embed-widget {
    grid-column: 2;
    grid-row: 1 / span 3;
    max-width: none;
    margin-top: 0;
  }
}
#application-form .wing-display--sm { margin-bottom: 20px; }

/* The waiting list's form section gets the same two columns: "What joining
   means." and its three facts (stacked) on the left, the form on the right. */
.wing-section > .wing-shell:has(> .wing-embed-widget) > .wing-display--sm .elementor-heading-title {
  font-size: var(--wing-h2);
  font-weight: var(--wing-display-weight, 500);
  line-height: 1.02;
  letter-spacing: -.03em;
}
@media (min-width: 1024px) {
  .wing-section:not(#application-form) > .wing-shell:has(> .wing-embed-widget):has(> .wing-display--sm) {
    --display: grid;
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    column-gap: clamp(56px, 7vw, 128px);
    row-gap: 0;
    align-items: start;
    grid-auto-flow: row dense;
  }
  .wing-section:not(#application-form) > .wing-shell:has(> .wing-embed-widget):has(> .wing-display--sm) > .elementor-widget:not(.wing-embed-widget) { grid-column: 1; }
  .wing-section:not(#application-form) > .wing-shell:has(> .wing-embed-widget):has(> .wing-display--sm) > .elementor-widget.wing-embed-widget {
    grid-column: 2; grid-row: 1 / span 6; max-width: none; margin-top: 0;
  }
  .wing-section:not(#application-form) > .wing-shell:has(> .wing-embed-widget) .wing-defs { grid-template-columns: minmax(0, 1fr); row-gap: 22px; }
}
.wing-section:not(#application-form) > .wing-shell:has(> .wing-embed-widget):has(> .wing-display--sm) > .elementor-widget:not(.wing-embed-widget):not(:first-child) { margin-top: 28px; }
.wing-section:not(#application-form) > .wing-shell:has(> .wing-embed-widget):has(> .wing-display--sm) > .elementor-widget:not(.wing-embed-widget):last-child { margin-top: 14px; }

/* ==== v2-rail.css ==== */
/* =========================================================================
   MOBILE ACTION BAR, inner pages.

   This file used to hold the desktop margin rail as well. The rail was
   removed on 19 September 2026 after the client rejected it three times
   ("clunky", "the way it navigates or the position it's in", "I still don't
   like it, the side timeline that there is"). The nav bar keeps all of its
   links and its permanent "Check eligibility" button. Note that the 2px
   scroll-progress rule under that bar does NOT currently render at all: see
   the correction above `railnav()` in motion.js. Nothing in this file
   depends on it either way.

   What remains is the bar itself, which was never part of the objection:
   under 900px the header has no room for the CTA button, so this is the one
   place the primary action stays reachable without opening the menu.
   ========================================================================= */

/* 28 Sep 2026: the inner-page rules that lived here were a copy of the home
   page's, translucent with a backdrop blur. Both are now ONE rule in
   components-v2.css §1B (`.wing-v2 .mobilecta`), opaque and on the ground of
   the band beneath it. Nothing is styled here any more; the file is kept so
   the bundle order in ops/bundle-css.py does not change. */
