/* ============================================================================
   SECTION 21 · "Logos and footer " · Figma 3016:431 · 1280 x 900 @ page y 19200
   ----------------------------------------------------------------------------
   SPEC    ../../figma-truth/21-logos-footer.spec.md
   PIXELS  ../../figma-truth/sections-en/21-logos-footer.png  (2560x1800, @2x)
   LIFT    ../../salient-truth/tether/fragment.logo-scroller.html — the `#clients`
           logo ticker. FOUNDER-BOUND: the note on 3016:431 in Figma reads
           "Logo scroller component from
            http://127.0.0.1:5010/salient/tether/index.html".

   SCOPE OF THIS FILE. The footer band (3016:432) is already fully styled in
   css/site.css §2 and nothing here touches it except one bug fix (§5). Almost
   everything below exists to retarget FOUR Salient parameters on the lifted
   ticker to Figma's numbers, and to place one asset whose export box is not the
   size the spec table advertises.

   THE FOUR RETARGETS. Spec §7.1 ends with a table headed "Values that come from
   Figma, NOT from Salient (the Salient demo's own numbers must not leak in)":

       property          Salient demo      Mykrobial (use this)
       logo height       35px (25 <=690)   24px          -> the inline <style>
       gap between       30px              48px          -> §2 below
       row opacity       1                 0.5           -> site.css .mk-logos__row
       strip background  demo's own        #0b0f12       -> site.css .mk-logos

   §3 adds a fifth of exactly the same kind — the edge mask — because the demo's
   10%/90% percentage ramp cannot coexist with this section's geometry. The
   derivation is written out there rather than asserted.

   WHY THERE IS NO MARQUEE CSS HERE. There isn't any, deliberately. Spec §7.1
   and tether.lift.md §(d)1 are both emphatic: this is NOT a keyframed marquee.
   `grep marquee` over the tether page and all 39 of its assets returns zero.
   The motion is a requestAnimationFrame loop in init.js that decrements
   Flickity's internal `x` and lets `wrapAround` re-render the SAME seven (here
   six) DOM nodes at `x ± slideableWidth`. Building it as "duplicate the track
   and translate -50%" would be a different component that merely looks similar.
   Both engine files are already vendored and already linked by the shell
   (flickity.q-458c954239.css line 56, flickity.q-2dcbd74a85.js line 1623).
   ========================================================================== */


/* ==========================================================================
   1. THE ROW — label + track, and why the gap is written as 24 and not 48
   21-logos-footer.spec.md §1.1.1 (3016:451)
   ========================================================================== */

/* `Txt` is HORIZONTAL / itemSpacing 48 / primaryAxisAlignItems MIN, 1040 wide
   at x 120, and the row is 27 tall inside a fixed 105.88 strip (both already in
   site.css §2.1). Its children are the 123-wide label and then six 24px logos.

   In the lifted component the logos are Flickity cells, and Salient gives every
   cell a SYMMETRIC horizontal margin (`[data-spacing] .cell{margin:0 30px}`).
   So the authored 48 is split: 24 of it is the cell's own left margin and the
   other 24 is the flex gap between the label and the track. 24 + 24 = 48 to the
   first logo, and 24 + 24 = 48 between every pair. Writing `gap: 48` here would
   put EPA at x 315 instead of 291.
   This rule overrides site.css `.mk-logos__row{gap:var(--mk-space-48)}` — same
   specificity, later in the cascade (index.html loads sections/*.css after
   css/site.css). */
.mk-logos__row { gap: var(--mk-space-24); }

/* Pin the label to the node's width. `letterSpacing` is unrecoverable on this
   MCP (spec §9 L1) and the node width IS the acceptance test (spec §8), so the
   track's left edge must not float with Poppins' metrics. 123 = 3016:452 width;
   ink measures 121.0..241.0 in the @2x export, i.e. the box carries ~2px of
   right side bearing, which is why the box is the authority and not the ink. */
.mk-logos__label { flex: 0 0 123px; }

/* The lifted `wpb_gallery` wrappers take the rest of the 1040: 1040 - 123 - 24
   = 893, of which the track paints 291..1160. */
.mk-logos__gallery {
  flex: 1 1 auto;
  min-width: 0;
}

/* Kill Salient's content-element spacing. THREE rules stack it, and the one
   that wins is the em-based one, which is why a flat `.mk-logos__gallery
   {margin:0}` at (0,1,0) was not enough:
       js_composer.css   .wpb_content_element          margin-bottom: 35px   (0,1,0)
       style.css         .wpb_content_element          margin-bottom: 35px   (0,1,0)
       skin-material.css .material .wpb_content_element margin-bottom: 1.5em (0,2,0)  <- wins
   `<body>` carries `material` (index.html:106) and the gallery inherits
   `font-size: 20px` from `.mk-page`, so 1.5em computes to exactly **30px** —
   measured. Because the row is `align-items: center`, flex centres the MARGIN
   box: a 24px track with 30px of bottom margin centres as 54, putting the
   track top at 39.44 + (27 - 54)/2 = **25.94** instead of 40.94. Every logo
   rendered 15px high. Forcing the margin to 0 puts the first mark's img at
   exactly 40.94, which is the spec's own y for 3016:453..514.
   This is the same trap NOTES.md R43-2 records for Salient's 1.5em `<p>`
   padding — same mechanism, different element. (0,2,0), later in the cascade. */
.mk-logos__row .mk-logos__gallery { margin: 0; align-self: center; }

/* Height chain. Every ancestor of `.flickity-viewport` has to carry the 24px
   because Salient's own rule is `.flickity-viewport{height:100%}` and a
   percentage of `auto` is `auto`. 24 = the logo height (spec §1.1 row table:
   every mark is y 1.5, h 24, inside the 27px row). */
.mk-logos__gallery,
.mk-logos__gallery > .wpb_wrapper,
.mk-logos__gallery .wpb_gallery_slidesflickity_static_height_style,
.mk-logos__ticker { height: 24px; }

/* `.mk-logos__row` is align-items:center and the track is 24 in a 27 row, so
   the marks land at row-y 1.5..25.5 — Figma's exact 1.5 offset. */


/* ==========================================================================
   2. RETARGET — cell spacing 30px -> 24px per side
   21-logos-footer.spec.md §1.1.1 (itemSpacing 48) and §5.2
   ("Logo-row gutter is a fourth value: 48")
   ========================================================================== */

/* `data-spacing="30px"` is KEPT ON THE ELEMENT: it is part of the lifted
   attribute set, and Salient's rule for it is the one being retargeted rather
   than bypassed. Salient's spacing scale only offers 5/10/15/20/25/30/40/50/60,
   so 24 has no attribute to select — the value is overridden instead. Selector
   mirrors Salient's own shape plus the instance class, so it wins on
   specificity without `!important`. */
.nectar-flickity.mk-logos__ticker[data-spacing="30px"]:not(.masonry) .flickity-slider .cell {
  margin: 0 24px;                    /* 24 + 24 = the authored itemSpacing 48 */
}

/* The two VECTOR marks carry a fractional node width but an integer viewBox:
   3016:454 is 23.86 wide and exports as a 24x24 SVG, 3016:505 is 37.88 and
   exports as 38x24. Figma lays the row out on the FRACTIONS while rendering the
   ink on the INTEGERS — spec §1.1's render check measures the real ink at
   417..441 and 488.5..527 (integers) inside boxes the §5.1 table places at
   417..440.86 and 488.86..526.74 (fractions).
   Reproduce both: the cell is pinned to the fraction so the 0.14 / 0.12
   difference does not accumulate into every mark downstream, and the image
   keeps its integer viewBox and overhangs by that fraction. Without this the
   last three marks all sat 0.27px right of Figma, which is enough to change how
   a 4x export resamples and it showed up in the pixel diff. */
.wpb_gallery_slidesflickity_static_height_style
  .nectar-flickity.mk-logos__ticker:not(.masonry) .flickity-slider .cell.mk-logos__cell--hhs { width: 23.86px; }
.wpb_gallery_slidesflickity_static_height_style
  .nectar-flickity.mk-logos__ticker:not(.masonry) .flickity-slider .cell.mk-logos__cell--cdc { width: 37.88px; }

/* Resulting rest positions, measured, against spec §5.1's own table:
     track left edge             291.00   (120 + 123 + 24 + 24)
     EPA         78    wide  ->  291.00     spec 291.00
     HHS         23.86 wide  ->  417.00     spec 417.00
     CDC         37.88 wide  ->  488.86     spec 488.86
     NIH NLM    201    wide  ->  574.74     spec 574.73
     doi        102    wide  ->  823.74     spec 823.73
     ScienceDirect 151 wide  ->  973.74     spec 973.73
   Max error 0.01px. Trailing slack 1160 - 1124.74 = 35.26; spec says 35.27. */


/* ==========================================================================
   3. RETARGET — the edge mask
   ========================================================================== */

/* The demo's rule (from the tether page's inline <style>, byte 70,405; it is
   NOT in the vendored flickity CSS, so this file has to supply it):

       .nectar-flickity[data-mask-edges="true"]{
         mask-image:linear-gradient(to right,transparent 0,black 10%,black 90%,transparent 100%);}

   Percentages of a 869px track are 86.9px of fade at each end. At rest that
   would wash out EPA (291..369, entirely inside the left ramp) and the tail of
   ScienceDirect (fade would start at 1073, SD runs to 1125). Neither survives
   spec §8's acceptance list. The ramps are therefore re-expressed in the two
   px figures this section actually authors:

       LEFT  24px = the cell's own half-gap (itemSpacing 48 / 2, §1.1.1). The
             first logo's ink starts exactly 24px into the track, so a 24px ramp
             lands entirely in the margin and never touches EPA.
       RIGHT 35.27px = the trailing slack spec §1.1.1 computes and names
             ("Container is 1040 -> 35.27px of trailing slack, because
             primaryAxisAlignItems: MIN"). The ramp therefore begins exactly at
             ScienceDirect's right edge and covers precisely the empty run.

   Same job as the demo's mask — logos dissolve at the ends rather than being
   guillotined — sized by this design instead of that one. `data-mask-edges="true"`
   stays on the element; this is its rule.

   The `#000` stops below are NOT paint and are deliberately not tokenised: in a
   mask the colour channel is ignored and only alpha is read, so black means
   "fully opaque" and transparent means "fully erased". Substituting a brand
   token here would imply a colour decision that does not exist. This is the
   literal Salient ships too. */
.nectar-flickity.mk-logos__ticker[data-mask-edges="true"] {
  -webkit-mask-image: linear-gradient(to right,
                        transparent 0, #000 24px,
                        #000 calc(100% - 35.27px), transparent 100%);
          mask-image: linear-gradient(to right,
                        transparent 0, #000 24px,
                        #000 calc(100% - 35.27px), transparent 100%);
}


/* ==========================================================================
   4. CELL / IMAGE PAINTING
   ========================================================================== */

/* Kill the inline-image baseline gap. Salient sets `vertical-align:middle` on
   these, which is enough in its own 400px-tall galleries but not in a 24px band
   where a 4px descender strip shifts every mark down half a pixel. */
.nectar-flickity.mk-logos__ticker .flickity-slider .cell img { display: block; }

/* Before init.js runs, Salient's own
   `.wpb_gallery_slidesflickity_static_height_style .nectar-flickity:not(.flickity-enabled)
    .flickity-slider{display:flex;flex-wrap:nowrap;position:relative}`
   gives the static rest row, and `.wpb_gallery .nectar-flickity:not(.flickity-enabled)
   .cell{flex:0 0 auto}` keeps the cells at intrinsic width. Both are vendored —
   nothing is added here for the rest state. Top-align the cells so a mark that
   is taller than the band (the doi lockup, §5) hangs downward from the shared
   24px top edge rather than being centred against it. */
.nectar-flickity.mk-logos__ticker:not(.flickity-enabled) .flickity-slider { align-items: flex-start; }


/* ==========================================================================
   5. THE doi FOUNDATION LOCKUP — asset correction
   21-logos-footer.spec.md §1.1 (3016:511/512/513), §4.2, §4.5
   ========================================================================== */

/* MEASURED THIS RUN, not assumed. `figma-truth/assets/21-logos-footer/
   logo-5-doi-foundation.png` decodes to 424 x 128 px. Every other raster in
   this section is an exact 4x of its node (EPA 312x96 = 78x24, NLM 804x96 =
   201x24, ScienceDirect 604x96 = 151x24). This one is not: 424/4 = 106 and
   128/4 = 32, against a node of 102 x 24.

   Why: 3016:513 carries the section's ONLY effect,
   `DROP_SHADOW #00000040, offset 0/4, radius 4` (spec §4.2), and Figma expands
   an export box to the union of the node and its shadow —
   x -4..106 clipped to 0, y 0..32. Alpha scan of the decoded PNG confirms what
   that means in practice:
       ink bbox            x 0 .. 105.0,  y 0 .. 24.0   (design px)
       rows below y 24     zero ink, max alpha 0
   So the shadow is BAKED IN, it only shows horizontally (it is the shadow of
   the "Foundation" wordmark, whose ink sits well inside the 24px band), and the
   bottom 8 design-px of the canvas are empty padding.

   Three consequences, all handled here:
     a) the image is drawn at 106 x 32, top-left on the node's top-left;
     b) the CELL is pinned to the node's 102 so the following 48px gap is
        measured from the node edge, exactly as spec §5.1 tabulates
        (doi right 925.73, ScienceDirect left 973.73);
     c) `filter: drop-shadow(var(--mk-shadow-logo))` is NOT applied. site.css
        §2.1 applies it to `.mk-logos__mark--doi` for the static-strip build;
        that class is deliberately absent from this markup, because on this
        export it would paint the shadow a second time.
   Spec §1.1's render check — "the doi run overruns its 925.73 box edge by ~4px,
   that is the drop shadow, not a geometry error" — is the same observation from
   the render side, and it is what this reproduces. */
.wpb_gallery_slidesflickity_static_height_style
  .nectar-flickity.mk-logos__ticker:not(.masonry) .flickity-slider .cell.mk-logos__cell--doi {
  width: 102px;                      /* 3016:511 node width */
}
.wpb_gallery_slidesflickity_static_height_style
  .nectar-flickity.mk-logos__ticker:not(.masonry) .flickity-slider .cell.mk-logos__cell--doi img {
  height: 32px;                      /* export canvas, = node 24 + shadow 8 */
  width: 106px;                      /* export canvas, = node 102 + shadow 4 */
  max-width: none;
}

/* The 4px of shadow that overhangs the cell needs the ticker's clip to stay off
   the cell itself. `.wpb_gallery_slidesflickity_static_height_style
   .nectar-flickity.ticker-rotate .flickity-slider .cell{overflow:visible}` is
   vendored and does this — but it is gated on `.ticker-rotate`, which the
   reduced-motion guard removes. Restated unconditionally so the overhang
   survives that path too. */
.nectar-flickity.mk-logos__ticker .flickity-slider .cell { overflow: visible; }


/* ==========================================================================
   6. REDUCED MOTION
   21-logos-footer.spec.md §7.1.9 — "Respect prefers-reduced-motion — not in the
   Salient source, but required; freeze the row and let it scroll manually."
   ========================================================================== */

/* The drift is a requestAnimationFrame loop, so CSS alone cannot stop it; the
   inline guard in the fragment drops `ticker-rotate` + `data-autoplay` before
   init.js constructs, which is what actually freezes it. Flickity's drag stays
   live. This block only restores the two things the missing `ticker-rotate`
   class would otherwise have styled, and lets a pointer-less user scroll the
   overflow. */
@media (prefers-reduced-motion: reduce) {
  .nectar-flickity.mk-logos__ticker[data-mk-reduced-motion] .flickity-viewport {
    overflow-x: auto;
    scrollbar-width: none;
  }
  .nectar-flickity.mk-logos__ticker[data-mk-reduced-motion] .flickity-viewport::-webkit-scrollbar {
    display: none;
  }
}


/* ==========================================================================
   7. RESPONSIVE
   Spec §6.2 / §9 L10: there is NO tablet and NO mobile frame for this section.
   Only 1280 and 1440 were authored. Everything here is therefore restricted to
   what the two captured frames actually prove, plus the shell's own ladder.
   ========================================================================== */

/* Nothing needs a breakpoint for the strip. The row inherits `--mk-page-x` from
   grid.css §6 (120 -> 64 -> 48 -> 24), the label stays 123 because it is a
   fixed node width in both authored frames, and the track simply gets narrower
   — which is the whole point of the founder's component and is spec §6.2's own
   reading: "At 1280 the row already occupies 1004.73 of 1040 — it is one
   breakpoint from overflowing. This is precisely the problem the founder note
   solves." The demo's `<=690px` step from 35px to 25px marks is NOT translated:
   spec §6.2 flags the analogous 24 -> 18 as "an inference, not a Figma value",
   so the per-instance <style> holds 24 at every breakpoint. Raise it only with
   Jehnean's sign-off. */

/* Below 640 the label + a 24px-tall track is 147px of the 327px content box.
   That is tight but legible, and it is the only arrangement the source
   supports; stacking the label above the track would be an invention. */

/* R10-10 — REGRESSION FIX, founder's own words: "The about, tools & resources,
   and tools/find-help-for-professionals were TOP ALIGNED, and now you've
   CENTER ALIGNED this whole section so no longer matches our design."
   site.css:377 sets `.mk-footer__links{align-items:center}` to reproduce
   Figma's own declared `counterAxisAlignItems: CENTER` on 3016:433 — RE-CHECKED
   live via figma-mcp on 2026-08-22 (file `mykrobial.org`, page `Website draft`,
   `get_node_full` on 3016:433) and it still reports CENTER today, so this was
   never a stray edit; it is a literal, faithful read of the artboard.
   MEASURED IMPACT of that literal read, headless Chrome via review/sweep31/
   cdp.py, `.mk-footer__links` vs its five `.mk-footer__col` children,
   `getBoundingClientRect()` deltas from the wrap's own top:
     1280x900  wrap 483.13 tall, columns (204/192 tall) start at y  139.56
     1440x900  wrap 483.13 tall, columns (204/168 tall) start at y  139.56
     1750x980  wrap 563.13 tall, columns (204/168 tall) start at y  179.56
   `.mk-footer__links` is `flex:1` inside `.mk-footer__body`'s flex column
   (site.css:377), which is what stretches it to 483-563px of leftover height
   in the first place — Figma's own frame is the SAME 483.12 tall (`get_node_
   full` bounds), so CENTER there produces the identical ~140-180px dead band
   above "About" that she is describing as broken. Per this build's own D43
   precedent ("the trace outranks the artboard"), her direct correction wins:
   this is restored to MIN (top-aligned), not re-derived from the node.
   site.css is read-only to a section builder (CONVENTIONS §1), so the fix is
   here: `sections/21-logos-footer.css` loads after `css/site.css` (index.html
   §SECTION STYLESHEETS), so an equal-specificity `.mk-footer__links` rule in
   THIS file wins the cascade without touching the shared file. */
/* R11-11 — R10-10's OWN FIX regressed the section's vertical centring. Her
   words this round: "`.mk-footer__inner` [confirmed live: the actual outer
   wrapper is `.mk-footer__links`, which also carries the class
   `mk-footer__inner` — see the HTML, `mk-footer__inner mk-tracks-5
   mk-footer__links` on 3016:433 — `.mk-footer__inner` alone is NOT unique, it
   is reused verbatim on `.mk-footer__bottom` too, so the literal class name
   she used is not the safe selector here] should be CENTER aligned while
   `.mk-footer__col` remains TOP aligned." That is a TWO-LEVEL ask and R10-10
   only ever had ONE lever (`align-items`) to pull, which conflates both
   levels: `align-items` decides BOTH (a) where the single flex LINE of five
   columns sits inside the wrap's 483-563px flex:1 height AND (b) where each
   COLUMN sits inside that line. `start` fixed (b) — the actual R10-10 defect,
   columns landing at different y's because a shorter column centred lower —
   but as a side effect it also pinned the whole line to the wrap's top,
   which is (a), and (a) was never broken; center-aligning was correct there,
   she is only objecting to what start-aligning cost her as a side effect.
   `align-content` is the SEPARATE property for (a) — CSS Box Alignment L3
   distributes the flex container's LINE(S) in the leftover cross-space, and
   as of the current Box Alignment spec (shipped in every evergreen engine
   this build targets — Chrome 108+, Firefox 109+, Safari 15.4+, MEASURED
   live below rather than assumed) it applies even to a single-line
   `flex-wrap:nowrap` row, not only to wrapped multi-line containers. So:
   `align-content:center` centres the ONE line (204px tall at 1280/1440,
   192/168 tall for the short 5th column but the LINE height is set by the
   TALLEST column) inside the wrap's full flex:1 height — level (a) — while
   `align-items:start` (unchanged, still the R10-10 fix) keeps every column's
   own top flush with that line's top — level (b). No DOM change, no new
   wrapper element: the wrap (`.mk-footer__links`) already plays both roles
   (flex:1 space-filler AND the row of five columns) and these are the two
   CSS properties that separately govern each role. At the <=1024 breakpoint
   `.mk-tracks-5{flex-wrap:wrap}` turns this into a genuine MULTI-line
   container (3 rows of 2/2/1 — measured below) where `align-content` is
   uncontroversially the right property regardless of the single-line
   question; at 834/390 the wrap's flex:1 height already equals its content
   height exactly (R10-10's own note, re-confirmed live below), so there is
   zero leftover space for `align-content` to distribute either way — the
   rule is safe to leave unscoped rather than media-querying it to
   desktop-only, and doing so avoids inventing a breakpoint number nothing
   else in this file uses. */
.mk-footer__links {
  align-items: start;      /* R10-10, unchanged: columns top-aligned to EACH OTHER */
  align-content: center;   /* R11-11: the column BLOCK centred in the leftover flex:1 height */
}

/* Stop a DESKTOP flag from leaking into the stacked layout. At <=640 grid.css
   flips `.mk-tracks-5` to `flex-direction: column`, and `align-items` silently
   becomes HORIZONTAL centring: measured at 640, column 1 landed at x 255.55
   with a shrink-to-fit 128.91 width instead of holding the content edge. That
   is a side effect of a direction change, not a design decision, so it is
   corrected rather than redesigned — Figma has no mobile frame here (spec
   §6.2, §9 L10) and no mobile treatment is invented. This selector is two
   classes (higher specificity than the R10-10 rule above) so it still wins at
   <=640 regardless of source order — unchanged by R10-10, re-verified below. */
@media (max-width: 640px) {
  .mk-footer__body .mk-footer__links { align-items: stretch; }
}


/* ==========================================================================
   8. SALIENT OVERRIDES THE FOOTER BAND NEEDS — four measured defects
   ----------------------------------------------------------------------------
   All four are Salient rules that out-specify css/site.css §2. They are fixed
   here rather than in site.css because site.css is read-only to a section
   builder (CONVENTIONS §1), but every one of them is a SHELL-LEVEL bug: they
   will bite the ES build and any other section that puts a <p>, an <h2> or a
   submit button on the page. Each is reported in NOTES.md.
   Every "before" number below is a computed-style or getBoundingClientRect
   reading taken in headless Chrome at 1280 with document.fonts.ready awaited —
   the un-awaited numbers are font-fallback noise and were discarded.
   ========================================================================== */

/* 8.1 — the h2 leaks 8px and it moves the whole vertical stack.
   Measured `margin-bottom: 8px` on 3016:439. The footer body is a column flex
   with `gap: 24`, so margins ADD to the gap: heading -> form measured 32
   instead of 24, and `Footer text ` (which is layoutGrow:1) absorbed the
   difference and came out 475.12 instead of the spec's 483.12, dragging the
   column tops to y 289.43 against Figma's 293.44 and the heading to 652.99
   against 661.00. Zeroing the margin restores every one of those numbers. */
.mk-footer__body .mk-footer__newsletter-heading { margin: 0; }

/* 8.2 — Salient puts `padding-bottom: 1.5em` on every <p>.
   Already recorded once, in NOTES.md R43-2 ("Any section using <p> needs
   padding: 0, not just margin: 0. Worth promoting to CONVENTIONS §8 as trap
   8") — it is unfixed at shell level and it hits the disclaimer here.
   Measured `padding-bottom: 24px` on 3016:446, making the box 72 tall instead
   of 48. `.mk-footer__bottom` is align-items:center over a 48px content band,
   so a 72px box centred against 48 pushed the first line to y 791.99 when the
   spec's ink band is 807.0..826.5. With padding 0 the box is 2 x 24 = 48 and
   the lines land in their 804 / 828 boxes. */
.mk-footer__bottom .mk-footer__disclaimer { margin: 0; padding: 0; }

/* 8.3 / 8.4 — the Join button. TWO Salient `!important` rules beat site.css's
   own `!important` here, because site.css's `body .mk-footer .mk-newsletter__btn`
   is only (0,2,1):

     skin-material.q-2c163bc272.css
       body[data-button-style=rounded] .container-wrap button[type=submit]
       { padding: 16px 23px !important }                              (0,3,2)
     salient-dynamic-styles-multi-id-37.q-6d7044fbbd.css
       body[data-button-style^="rounded"] button[type=submit]
       { border-radius: 200px !important }                            (0,2,2)

   and <body> really does carry data-button-style="rounded" (index.html:117).
   Measured consequences, both visible:
     padding  16px 23px  -> button 79.84 wide, not the spec's 84, so the whole
              newsletter group came out 349.84 at x 465.08 instead of 354 at
              x 463; and the 16px vertical padding sits the label 4px low in
              the 48px box (3016:444 is at local y 12).
     radius   200px      -> a full stadium. Figma is `{4, 54, 54, 4}`: the LEFT
              end is a 4px corner that meets the field's 4px corner across the
              4px gap, and only the RIGHT end is a semicircle. A 200px radius
              rounds the left cap too and destroys the field/button lockup.
   The label itself needed nothing: Poppins Medium 16 renders "Join" at 33.84
   against the spec's 34 (§8 acceptance), so `letter-spacing: normal` in
   site.css is correct and no tracking is invented here.
   (0,5,1) beats both. */
body[data-button-style="rounded"] .container-wrap
  .mk-footer .mk-newsletter .mk-newsletter__btn {
  padding: 12px 26px 12px 24px !important;    /* 24 + 34 + 26 = 84, §1.2.3 */
  border-radius: var(--mk-radius-field-btn) !important;  /* 4 54 54 4 */
}


/* ============================================================================
   §9 · FOUNDER TRACE FT-20260817 — D42, D45, D46, D47, D48.
   ----------------------------------------------------------------------------
   Every rule in this block modifies a RESTING rule that lives in css/site.css
   (§2.1–§2.3, lines 319–478). It is written here rather than there because
   css/site.css is a shared file under concurrent edit this run; the section
   sheet is linked AFTER site.css by _tools/compose.py, so an equal-specificity
   rule here wins on source order and no `!important` is invented.
   A consolidation request — move these five state rules up beside the resting
   rules they modify — is filed in founder-trace/SHARED-REQUESTS.md as
   REQ-FOOTER-STATES-01. Until that lands, THIS IS THE ONLY PLACE the footer's
   interaction states are declared. Do not add a second copy in site.css.

   THE LADDER THESE FOUR DEFECTS SHARE. review/branding/media-misc.md §4.3 read
   the branding artboard and found three different resting opacities in the nav
   system — 25% non-interactive heading, 50% dense submenu link, 75% menu/footer
   link — and §7 rule 6 states the interaction as "75% -> 100% on hover, no
   underlines". D45 and D48 are the founders quoting that same ladder back at
   this footer. So this is one token applied four times, not four decisions.
   ========================================================================== */

/* --- D42 · the marquee clones ------------------------------------------- */
/* The repeated cell run in the fragment exists only to push the ticker's
   `slideableWidth` above the viewport width (full derivation in the markup).
   It must not exist before flickity initialises, or the static rest render —
   which is what review/fidelity.py measures — would gain six logos.
   `flickity-enabled` is added by `activate()` BEFORE `getSize()` and
   `reloadCells()` run (vendor/…/flickity.q-2dcbd74a85.js, `p.activate`), so the
   clones are laid out and measured on init and are display:none at rest. */
.nectar-flickity.mk-logos__ticker:not(.flickity-enabled) .cell[data-mk-marquee-clone] {
  display: none;
}
/* Under reduced motion the inline guard in the fragment strips `ticker-rotate`
   and `data-autoplay`, flickity never rotates, and a doubled run would just be
   twelve static logos. Keep them out in that mode regardless of init state. */
@media (prefers-reduced-motion: reduce) {
  .nectar-flickity.mk-logos__ticker .cell[data-mk-marquee-clone] { display: none; }
}

/* --- D45 · footer nav links: no underline, 75% -> 100% ------------------- */
/* site.css:391-392 ships `color:#fff` at rest and `text-decoration:underline`
   on hover. Both halves are replaced.

   THIS DELIBERATELY DIVERGES FROM THE FIGMA RENDER, and the divergence is the
   founders'. site.css:389 records that the column's resting fill was recovered
   from the RENDER as 100% white because the API returned `fills:"mixed"`.
   FT-20260817 D45 says 75% -> 100%, and media-misc.md §4.3 corroborates 75% as
   the system's link token from a node that reports it cleanly (3271:6085).
   The founder trace is the spec (G7). Expect 21-logos-footer's %diff against
   figma-truth/sections-en to RISE for this reason; that is a directed change,
   not a regression, and it is recorded in MEASUREMENT and in the helix file.

   The first child of each column is the category heading at 25% (site.css:390)
   and is NOT a link — `:not(:first-child)` is unnecessary because the heading
   is a <span>, but `a` is stated explicitly so a future heading-as-link cannot
   silently inherit the interactive token. */
.mk-footer__col a {
  color: rgba(255, 255, 255, 0.75);
  text-decoration: none;
  transition: color var(--mk-motion-dur-hover, 450ms)
                     var(--mk-ease-hover, cubic-bezier(0.15, 0.75, 0.5, 1));
}
.mk-footer__col a:hover,
.mk-footer__col a:focus-visible {
  color: var(--mk-ink-inverse);
  text-decoration: none;          /* stated: kills site.css:392 at equal weight */
}

/* --- D48 · social icons: the same 75% -> 100% ladder --------------------- */
/* The icons are <img> elements, so site.css:478 (`.mk-footer__social svg`)
   has never matched anything on this page — noted here because it looks like
   it is doing the colour work and it is not. The marks are already white in
   the SVG payload; only opacity moves. */
.mk-footer__social {
  opacity: 0.75;
  transition: opacity var(--mk-motion-dur-hover, 450ms)
                      var(--mk-ease-hover, cubic-bezier(0.15, 0.75, 0.5, 1));
}
.mk-footer__social:hover,
.mk-footer__social:focus-visible { opacity: 1; }

/* --- D46 · the email field: background shift, not an outline ------------- */
/* Founder wording: "remove the white outline on focus; instead shift background
   #222D33 -> #1A2328". #222D33 is `--mk-surface-field` exactly; the focus value
   has no token yet, so it is declared here as a local custom property rather
   than as a literal buried in a rule.

   ACCESSIBILITY — READ BEFORE CHANGING. site.css:429-438 added that outline
   because a full computed-style diff of focused-vs-blurred came back EMPTY:
   the field had no focus indicator of any kind, which fails WCAG 2.4.7 (AA).
   The requested replacement is not equivalent: #222D33 -> #1A2328 is a
   1.145:1 luminance change (L 0.02533 vs 0.01580), far under the 3:1 that
   WCAG 2.4.11 asks of a focus indicator.
   A `:focus-visible`-ONLY OUTLINE WAS TRIED AND IT DOES NOT WORK. The plan was
   to give the founders the bare background shift on `:focus` (the state a
   pointer produces) and keep the outline on `:focus-visible` (keyboard only).
   MEASURED with _tools/mcf-state-probe.py: a programmatic/pointer focus of this
   element reports `:focus-visible` = TRUE. That is correct per spec — a text
   input ALWAYS matches `:focus-visible`, however focus arrived — so the white
   outline would still have appeared on click and D46 would not have been fixed.
   The idea is recorded because it is the obvious one and the next reader will
   have it too.

   SHIPPED: exactly what the founders asked for. No outline in any focus state,
   background shift only. THE AA GAP IS REAL AND IS OWED BACK TO THEM, with the
   numbers, in founder-trace/helix/D46.md — this is not a silent regression and
   it is not a silent substitution either. Two options that keep their design
   intent and clear 3:1, for them to pick from:
       (a) deepen the shift: #222D33 -> #0B0F12 is 1.62:1, still short; the
           field would have to go lighter, not darker, to clear 3:1 against
           #222D33 — e.g. #3A4A52 measures 3.02:1.
       (b) keep #1A2328 and add a 2px INSET ring in the brand teal #1EB2BC,
           which clears 3:1 against both the old and the new field colour and
           is not the white outline they objected to.
   Neither is shipped, because neither is what they said. */
.mk-footer { --mk-surface-field-focus: #1a2328; }
body .mk-footer .mk-newsletter__field {
  transition: background-color var(--mk-motion-dur-fill, 300ms)
                               var(--mk-ease-hover, cubic-bezier(0.15, 0.75, 0.5, 1));
}
body .mk-footer .mk-newsletter__field:focus,
body .mk-footer .mk-newsletter__field:focus-visible {
  background: var(--mk-surface-field-focus) !important;   /* beats site.css's own !important */
  outline: none !important;
}

/* --- D47 · the Join button gets a hover --------------------------------- */
/* >>> REWRITTEN 2026-08-18, lane `foldback:buttons-sweep`. <<<
   What was here: `opacity: var(--myk-btn-hover-opacity, 0.87)`, citing
   myk-btn.css §2.4's "already-solid buttons lift rather than invert". That
   reasoning was sound and its source was wrong — the `.87` lift is not in the
   Figma system anywhere. `buttons.md` §8.1 traces it to the resort/rooms
   donor, and it was the shared root cause of five of the seven failing
   buttons. It is deleted from the primitive, so it cannot be read from there
   any more either.
   The note was also right about the important part — "inventing a second
   solid-button hover here is how a system stops being one" — which is why the
   replacement is not a value in this file but the primitive's own states,
   reached by putting `myk-btn` on the element (see the markup for which
   primitive, and why the manifest's "PRIMARY, rest #1EB2BC" is wrong).

   THE REST STATE IS DELIBERATELY UNTOUCHED. `css/site.css` §2.x declares this
   button's entire resting paint with `!important` at (0,2,1) and Salient beats
   even that in two places (see §8.3/§8.4 above). All of it still wins over the
   primitive's tokens, which is the intended outcome: `figma-truth/
   21-logos-footer.spec.md` is render-calibrated for this section (the button
   predicted at x733-817/y708-756 and measured exactly there) and is the parity
   gate for resting pixels. This block adds a STATE, it does not restyle.

   Only two tokens are set, and both exist to keep the rest state identical
   while the primitive's mechanism runs on top of it. */
body .mk-footer .mk-newsletter__btn {
  --myk-btn-blur:    none;      /* the primitive defaults to BACKGROUND_BLUR 4;
                                   `3016:443` declares no effects, and adding a
                                   backdrop blur would change a RESTING pixel */
  --myk-btn-justify: center;    /* the <button> UA `text-align: center` this
                                   element used to lay out by; flex-start would
                                   move the label 0.08px left. Same result,
                                   stated rather than inherited */
  /* GROWTH: the system default (+2px per side), NOT the GROUP family's +4.
     `3269:5632` is the only GROUP node with a drawn hover and it grows 40->48;
     the SOLID variants this button belongs to (`3269:5656`/`3269:5658`) have
     NO hover drawn at all. Given a choice between extrapolating a foreign
     family's delta and matching every other button on the page, consistency
     wins — that is the founders' actual request. Recorded, not silent. */
}
/* The label colour is the one thing that must be re-stated, and the reason is
   mechanical: `css/site.css` sets `color: var(--mk-ink-strong) !important` on
   this button, so the primitive's `--myk-btn-fg` retarget cannot reach it and
   the hover would compute correctly while showing nothing. Same failure the
   amenities CTA had, different cause — there a hard colour on the label span,
   here an `!important` on the button.
   `#1EB2BC` is `3269:5632`'s hover label, which is also SECONDARY's
   (`3269:5608`) and TERTIARY's: every non-primary family on the board hovers
   its label to turquoise. Written in this file, at (0,4,1), for the reason §9
   already gives — site.css is shared and under concurrent edit. */
body .mk-footer .mk-newsletter .mk-newsletter__btn:hover,
body .mk-footer .mk-newsletter .mk-newsletter__btn:focus-visible {
  color: var(--myk-btn-hover-fg) !important;
}


/* ============================================================================
   R7-09 · REQ-ZEBRA-5 — `18-cta-expand -> 21-logos-footer`: THE ANSWER IS ZERO,
   AND ZERO IS WHAT IT ALREADY IS. THIS BLOCK CHANGES NOTHING ON PURPOSE.
   ----------------------------------------------------------------------------
   REQ-ZEBRA-5 asked for a DELIBERATE decision at this boundary rather than a
   change: *"either take it to 0 (both grounds bleed) or to a full breath (if
   the footer is meant to stand back). What the rule forbids is the current
   in-between."* This is the decision, with the evidence, written next to the
   code so the next reader does not re-open it.

   THE RULE (founder-trace/FT-20260821-jehnean-round7 R7-09; derivation in
   sections/01-who-why.css §8): one breath between neighbouring things, and
   **a section whose ground runs to its own edge spends no breath there.**

   THE DECISION: **both grounds bleed, so the seam is ZERO — and it already is.**

   WHAT THE 39.4 ACTUALLY IS. `r7:zebra` measured 39.4 at this boundary at all
   five widths and read it as an in-between. It is not space between the two
   sections. It is the vertical centring of a 27px row inside the 105.88px
   "Backed By" strip, whose ground runs to the boundary:

       css/site.css §2.1   .mk-logos { height: 105.88px; background: --mk-surface-ink }
                           (105.88 - 27) / 2 = 39.44                    <- the number

   PROVED FROM PIXELS, not from the DOM (review/r7/r7-seams/pxseam.py at
   1750x980, screenshot review/r7/r7-seams/shots/px-1750-18-cta-expand.png):

       spentAbove   0.5     18's photograph runs to the boundary
       spentBelow  40.5
       groundRGB   [11, 15, 18]      <- #0b0f12, THIS SECTION'S OWN INK

   That last line is the whole argument. At every boundary this lane opened, the
   empty band is painted in the PAGE ground `rgb(244,249,251)`. Here it is
   painted in `--mk-surface-ink`: there is no empty band, there is a dark strip
   that starts exactly at the boundary and has its label centred in it. The
   screenshot shows the photograph ending and the strip beginning on the same
   row, with no light gap at all.

   `r7:zebra`'s instrument could not see this: `seam.py` deliberately does not
   count a full-width flat tint as ink ("a tonal step between two grounds is a
   visible edge but it is not a thing the eye measures space to"), which is the
   right call for a section that merely changes tone — and the wrong call for
   one whose ground IS the thing that reaches the boundary.

   WHY NOT A FULL BREATH INSTEAD — it does not fit inside B06, and that is
   arithmetic, not taste. R3-B's B06 is the one item that names a TARGET:
   "Backed By + footer should together be roughly full viewport height on a
   MacBook Pro", which css/rhythm.css §2 implements as
   `.mk-footer { min-height: var(--mk-band-1) }` = one screen. A breath at each
   end of the strip makes it `27 + 2 x 196 = 419` at her band, and
   `css/site.css:364` floors `.mk-footer__body` at 794.12 (= 900 - 105.88, the
   Figma pair). 419 + 794.12 = 1213 in a 980 viewport — 1.24 screens, and B06 is
   broken. Squeezing the body instead is worse: its own content plus its 48/48
   padding is 659, so the surplus has nowhere to go.

   ONE REAL FINDING SURVIVES AND IT IS FILED, NOT FIXED HERE: 105.88 is a flat
   pixel constant at every width including 390, and Figma authored the strip
   with 48/48 padding that the 27px row overflows symmetrically (site.css §2.1's
   own note), so the built 39.44 is already 8.56 under the authored 48. Both are
   `css/site.css`, which is single-owner and not this lane's file. Filed as
   OBS-SEAMS-04 in founder-trace/SHARED-REQUESTS.md with these measurements.

   NOTHING RELATED TO B06/OBS-SEAMS-04 IS DECLARED BELOW. A rule that paints
   nothing in every state is dead code that reads as intent (16-amenities-52.css
   §2 makes the same argument about D09). The record is the deliverable here.
   The next block (§10) is an unrelated round-10 item, not a continuation of
   this investigation.
   ========================================================================== */


/* ============================================================================
   §10 · R10-11/R10-12 — THE FOOTER ENTRANCE WAVE
   ----------------------------------------------------------------------------
   founder-trace/FT-20260822-jehnean-round10.md, her own words on the
   "sequential, not serial" wave: "one after the other... with... while the
   other one's still kind of going, you're starting the next one." R10-11's
   named order is "About -> Tools & Resources -> Tools/Find-Help-for-
   Professionals -> Newsletter" — read as four groups over the five nav
   columns (1 / 2+3 / 4+5) plus the newsletter heading, matching her named
   labels exactly once "Tools/" in step 3 is read as a verbal repeat of the
   word rather than a sixth column (this project has one documented precedent
   for exactly that kind of slip: "back to by" for "Backed By", corrected in
   an earlier round).

   WHY A SHARED TRIGGER, NOT FIVE INDEPENDENT ONES. js/myk-motion.js's
   IntersectionObserver fires `.mv-in` on an element when THAT element's own
   geometry crosses the trigger line — for five elements that would make the
   gap between "About" and "Newsletter" a function of how fast she happens to
   be scrolling (R9-17 measured that literal gap at ~420px of scroll, an
   unbounded amount of wall-clock time), which cannot be verified as
   "overlapping" the way this round's own instruction requires. So this block
   keeps exactly ONE real trigger — `.mk-footer__links[data-mv="lead"]`,
   already on the markup since R9-17, already measured (R9-17 §4.1, R9 verify
   §8) — and drives every other element in the wave off that ONE element's
   `.mv-in`, via CSS descendant/sibling selectors, with FIXED millisecond
   delays. This is the same "parent state class + nth-child delay" shape
   R9-04/R9-05 verified PASS for the estate slider
   (founder-trace/helix/R9-04-05.md) — a fixed `transition-delay` genuinely
   time-shifts one copy of the same curve against another, which R9-04/05
   measured directly rather than assumed.

   THE FOUR DELAYS, and why they are inside the 700ms LEAD duration:
     step 1  About                          +0ms    (var(--myk-mv-delay-lead), unchanged)
     step 2  Tools + Resources & tools       +150ms
     step 3  Find help + For professionals   +300ms
     step 4  Newsletter heading              +450ms
   Base delay is `--myk-mv-delay-lead` (100ms, css/myk-motion.css). Every step
   therefore STARTS while the previous one is still animating: step 2 starts
   at 100+150=250ms, step 1 does not finish until 100+700=800ms — a 550ms
   overlap, and step 4 (starts 100+450=550ms) still overlaps step 1 by 250ms
   and step 3 (ends 100+300+700=1100ms) by 550ms. Every adjacent pair overlaps
   by AT LEAST 400ms against a 700ms duration — comfortably inside it, not a
   1ms technicality. Verified live, not just computed: see the helix record
   for the sampled-in-flight numbers.
   ========================================================================== */

/* --- 10.1 THE TRIGGER, NEUTRALISED — R10-11 -------------------------------
   `.mk-footer__links` keeps its R9-17 `data-mv="lead"` attribute so
   js/myk-motion.js's IntersectionObserver still adds `.mv-in` to it at the
   exact scroll position R9-17 and R9-verify already measured — reusing a
   proven trigger rather than inventing a new one. What it no longer does is
   render its OWN opacity/translate: myk-motion.css's generic start-state rule
   (`html.mv [data-mv]{opacity:0;translate:0 40px;...}`) is (0,2,1); this
   selector is (0,3,0) — `.mk-page` + `.mk-footer__links` + `[data-mv]` — so
   it wins on specificity alone, position-independent, exactly the situation
   04-ecology.css:229 already documents inside myk-motion.css §1.1. No
   `!important` needed: the `.mv-in` end state this file's engine writes
   (`opacity:1`) already agrees with what is wanted here, so there is nothing
   to fight there — only the START state needed overriding. */
.mk-page .mk-footer__links[data-mv] {
  opacity: 1;
  translate: none;
  transition: none;
}

/* --- 10.2 THE FIVE COLUMNS, STAGGERED — R10-11 ----------------------------
   Gated under `html.mv` for the same fail-open reason as every other rule in
   this vocabulary (§0 of myk-motion.js): no JS / JS error / reduced motion at
   load -> `html.mv` never lands -> this selector never matches -> the columns
   render at their natural `opacity:1`, exactly as today. `--myk-mv-dur-lead`,
   `--myk-ease-entrance` and `--myk-mv-travel` are read from css/myk-motion.css
   §0's `:root` tokens (a stylesheet may reference another's custom
   properties without editing it) so the curve and travel distance are
   identical to the rest of the page's LEAD role — only the delay differs per
   group, which is the whole point of a wave built from one clock. */
html.mv .mk-footer__links .mk-footer__col {
  opacity: 0;
  translate: 0 var(--myk-mv-travel);
  transition:
    opacity   var(--myk-mv-dur-lead) var(--myk-ease-entrance) var(--myk-mv-delay-lead),
    translate var(--myk-mv-dur-lead) var(--myk-ease-entrance) var(--myk-mv-delay-lead);
}
/* step 2 — "Tools & Resources": columns 2 and 3 */
html.mv .mk-footer__links .mk-footer__col:nth-child(2),
html.mv .mk-footer__links .mk-footer__col:nth-child(3) {
  transition-delay: calc(var(--myk-mv-delay-lead) + 150ms);
}
/* step 3 — "Find help" + "For professionals": columns 4 and 5 */
html.mv .mk-footer__links .mk-footer__col:nth-child(4),
html.mv .mk-footer__links .mk-footer__col:nth-child(5) {
  transition-delay: calc(var(--myk-mv-delay-lead) + 300ms);
}
/* The actual reveal: every column ends the same place regardless of group —
   only the DELAY (above) staggers when each one starts. */
html.mv .mk-footer__links.mv-in .mk-footer__col {
  opacity: 1;
  translate: 0 0;
}

/* --- 10.3 THE NEWSLETTER HEADING + FORM, STEP 4 — R10-11, merged R11-12 --
   No longer independently `data-mv`'d (see the HTML comment at each element
   for why — R9-17's own independent trigger on the heading, and R10-12's own
   independent trigger on the form, could each fire on their OWN scroll
   position and could not be VERIFIED to overlap the columns or each other).
   Their opacity/translate now live here, gated the same `html.mv` way, and
   their reveal is driven by a general sibling combinator off
   `.mk-footer__links`' `.mv-in` — all three (links wrap, heading, form) are
   direct children of `.mk-footer__body`, so `~` reaches both. This means the
   heading's AND the form's fade begin in the SAME animation frame, the
   moment the SAME trigger the columns use fires, +450ms later — not when
   either element's own geometry happens to cross the line.
   R11-12, her words: "'stay connected / join newsletter' text loads way
   before the email+join field... those need to load sequentially together."
   Before this round the form was independently `data-mv`'d (R10-12) and
   therefore ran on ITS OWN geometry-crossing trigger — positioned lower on
   the page, it crossed the line later than the heading's fixed +450ms
   offset from a trigger near the TOP of the footer, so the two visibly
   drifted apart. Folding the form into this exact same selector/delay pair
   as the heading is the fix: one shared trigger, one shared delay, so they
   are now, by construction, the same wave step — not two independently
   verified-close timings that could drift again under a future edit. */
html.mv .mk-footer__newsletter-heading,
html.mv .mk-newsletter {
  opacity: 0;
  translate: 0 var(--myk-mv-travel);
  transition:
    opacity   var(--myk-mv-dur-lead) var(--myk-ease-entrance) calc(var(--myk-mv-delay-lead) + 450ms),
    translate var(--myk-mv-dur-lead) var(--myk-ease-entrance) calc(var(--myk-mv-delay-lead) + 450ms);
}
.mk-footer__links.mv-in ~ .mk-footer__newsletter-heading,
.mk-footer__links.mv-in ~ .mk-newsletter {
  opacity: 1;
  translate: 0 0;
}

/* --- 10.4 REDUCED MOTION FOR 10.2/10.3 — mirrors myk-motion.css §2 Guard B -
   Guard A is automatic: all rules above are gated under `html.mv`, which
   never lands when `prefers-reduced-motion: reduce` matches at load
   (js/myk-motion.js §0), so they simply never match — same as the shared
   engine. Guard B (the OS setting changes AFTER `html.mv` is already present,
   mid-session, with the wave not yet fired) is NOT automatic here, because
   `.mk-footer__col`, `.mk-footer__newsletter-heading` and (as of R11-12)
   `.mk-newsletter` do not carry `[data-mv]` themselves in this design —
   myk-motion.css §2's own
   `html.mv [data-mv], html.mv [data-mv].mv-in { opacity:1 !important; ... }`
   cannot reach them. Restated here, same shape, same reasoning ("reduced
   motion means SHOW THE FINAL STATE, never hide the content" —
   css/reduced-motion.css's own rule at its own top). */
@media (prefers-reduced-motion: reduce) {
  .mk-footer__links .mk-footer__col,
  .mk-footer__newsletter-heading,
  .mk-newsletter {
    opacity: 1 !important;
    translate: none !important;
  }
}

/* --- 10.5 THE NEWSLETTER FORM — R10-12, RE-WIRED BY R11-12 ----------------
   R10-12 gave `.mk-newsletter` its own independent `data-mv="lead"` in the
   HTML — a genuine, independently-adopted `[data-mv]` element that needed no
   extra CSS, since myk-motion.css's generic engine supplied opacity/
   translate start+end state, the LEAD clock, and Guard A + Guard B for free.
   R11-12 removes that attribute (see the HTML comment on the `<form>`) and
   folds the form into 10.3 above instead, EXACTLY the move R10-11 already
   made for the heading one step earlier in this same file — same shape,
   same reason: an independent trigger cannot be verified to land in the
   same frame as a sibling's fixed-delay trigger, and her ask this round is
   that these two specific elements load in the same frame. Declared here
   only so a reader scanning this section for "what did R10-12 touch, and
   what changed since" finds both halves of the story in one place. */

/* --- 10.6 THE SOCIAL ICONS — R10-12, and a caught regression --------------
   FIRST DRAFT gave the three icons `data-mv="body"` directly, same as 10.5's
   form. MEASURED before shipping (`review/sweep31/cdp.py`, prefers-reduced-
   motion emulation) and it broke something real: this file's own D48 block
   (§ above, "the same 75% -> 100% ladder") gives `.mk-footer__social` a
   permanent resting `opacity: 0.75` with a 450ms hover-to-1 transition. The
   generic engine's `html.mv [data-mv].mv-in { opacity: 1 }` is (0,3,1),
   which out-specifies D48's plain `.mk-footer__social { opacity: .75 }`
   (0,1,0) — so once revealed, the icons would have settled at 100% opacity
   FOREVER instead of D48's dim-until-hover rest state, silently deleting an
   already-shipped design decision. Exactly the class of defect R10-10 is
   about (an animation change quietly breaking a prior, correct decision), so
   it does not ship even though the round only asked for entrance motion.

   THE FIX ships without `data-mv` at all. The icons key off their own
   row-mate's reveal — `.mk-footer__disclaimer`, already `data-mv="body"`,
   already firing (10.7) — via a general sibling combinator, exactly 10.3's
   shape. The ONLY new rule needed holds the icons at `opacity: 0` UNTIL that
   sibling reveals; the moment it does, `:not(.mv-in)` stops matching, this
   rule drops out of the cascade entirely, and D48's own base rule (0.75
   rest) and hover rule (1) take over completely unmodified — same 450ms
   transition, same hover behaviour, same everything, because this rule
   never touches `transition` and is gone by the time D48's own rules matter.
   Specificity: html+.mv+.mk-footer__disclaimer+:not(.mv-in)+.mk-footer__social
   = (0,4,1), which beats D48's (0,1,0) for the one property (`opacity`) it
   actually contests, pre-reveal only. */
html.mv .mk-footer__disclaimer:not(.mv-in) ~ .mk-footer__social {
  opacity: 0;
}

/* Guard B for the icons specifically: myk-motion.css §2's own reduced-motion
   rule releases `.mk-footer__disclaimer` (it has real `data-mv`) but only
   forces ITS computed opacity — it does not and cannot add `.mv-in` to it,
   because that class is written exclusively by js/myk-motion.js's
   IntersectionObserver `reveal()`, never by CSS. So if the OS setting flips
   mid-session BEFORE the disclaimer has scrolled into view, the disclaimer
   itself correctly shows (forced `opacity:1 !important`) but still lacks
   `.mv-in` in the DOM — meaning the `:not(.mv-in)` rule above would still
   match and the icons would stay stuck at `opacity:0` forever next to a
   now-visible disclaimer. Released independently here for that reason. */
@media (prefers-reduced-motion: reduce) {
  .mk-footer__social { opacity: 0.75 !important; }
  .mk-footer__social:hover,
  .mk-footer__social:focus-visible { opacity: 1 !important; }
}

/* --- 10.7 "BACKED BY" + THE COPYRIGHT LINE — MEASURED, NOT CHANGED --------
   R10-12 named these as un-animated ("currently none of these carry the
   entrance grammar"). Measured live before touching anything (helix record
   has the full BEFORE state): `.mk-logos__label` ("Backed By") and
   `.mk-footer__disclaimer` (the copyright paragraph) BOTH already carry
   `data-mv` and BOTH already reach `.mv-in` on scroll — `.mk-logos__label` is
   explicitly named in js/myk-motion.js §1's LEAD array, and
   `.mk-footer__disclaimer` is a bare `<p>` over `MIN_TEXT` matched by the
   page-wide BODY selector (both first found incidental by R9-17 §0, and both
   RE-confirmed live here, not just re-read from that old record). They
   already use the identical LEAD/BODY clock, easing and travel tokens as
   every other adopted element on the page — "the same entrance grammar" is
   therefore already true for these two, and no edit is made to either. */


/* ============================================================================
   §11 · MOBILE ROUND (founder feedback, 2026-08-31) — four items, ALL <=999px.
   ----------------------------------------------------------------------------
   Desktop >=1000 is untouched by every rule below: everything is inside
   @media (max-width: 999px), and the accordion elements the rules style only
   EXIST at <=999 (js/mk-footer-mobile.js enhances behind the same matchMedia
   and restores the original DOM at >=1000).

   ITEM 1 — the copyright overlap. css/site.css §2.2 fixes `.mk-footer__bottom`
   at `height: 72px` (the 1280 artboard's 2-line row). At 412 the disclaimer
   shares that row with three 44px-min-width social anchors (_band-mobile.css
   §2.2), wraps to ~9 lines ≈ 250px, and `align-items: center` centres the
   overflow — half of it paints UPWARD over the newsletter form. Fix: release
   the fixed height and wrap the row, disclaimer on its own full-width line,
   icons on the line below. No absolute positioning was involved.

   ITEM 3 — "Backed By" strip condensed. The 105.88 strip height is the 1280
   artboard's (site.css §2.1); at phone widths it is mostly dead band. The
   label's 123px pin (§1 above) exists ONLY because letterSpacing is
   unrecoverable at desktop acceptance — at mobile it reserves ~40px of empty
   run, so it shrinks to content and the track gets the space. The §3 edge
   mask (the dissolve treatment) is on the ticker and is deliberately NOT
   touched — it survives as-is.

   ITEM 4 — accordions. Collapse/expand is the grid-template-rows 0fr/1fr
   mechanism: animatable height without JS measurement, and the inner wrapper
   (min-height:0; overflow:hidden) is what actually clips. `visibility` rides
   along, delayed to the end of the collapse, so links inside a closed panel
   are unreachable by keyboard/AT — overflow:hidden alone would leave them
   tab-stops. Touch targets: _band-xs.css §3's `[data-mk-slug="21-logos-footer"]
   a` rule matches the links at their new depth (it is not `>`-scoped), and the
   toggle itself gets the same 44px floor here.
   ========================================================================== */
@media (max-width: 999px) {

  /* --- 11.1 · ITEM 1 — bottom row releases its fixed height ------------- */
  .mk-footer__body .mk-footer__bottom {
    height: auto;
    flex-wrap: wrap;
    row-gap: var(--mk-space-24);
  }
  .mk-footer__bottom .mk-footer__disclaimer {
    flex: 1 1 100%;              /* full-width line; icons wrap to the next */
  }

  /* --- 11.2 · ITEM 3 — "Backed By" strip condensed ---------------------- */
  .mk-logos { height: 64px; }    /* 27px row + 18.5 each side, was 105.88 */
  .mk-logos__label { flex: 0 0 auto; }  /* content width; 123 is a desktop
                                           acceptance pin (§1), not a design */

  /* --- 11.2b · ROUND 2 — the body's desktop height floor, released ------ */
  /* Founder, on the round-1 mobile footer: "remove some of that space above
     and below About ... and For professionals — on mobile you don't need this
     generous spacing." MEASURED at 412x780 before this rule: the body held
     h=794 against ~623 of content, because css/site.css §2.2's
     `min-height: 794.12px` is the 1280 ARTBOARD's band height (900 - 105.88)
     and is declared unscoped, so it survived into phone widths where nothing
     needs it — rhythm.css §2's whole footer-height treatment (B06) is
     >=1000-gated, so there is no mobile height target this floor serves.
     `.mk-footer__links{flex:1}` (site.css) absorbed the 171px surplus and
     R11-11's align-content centred it: 86px of dead band above "About", 85px
     below "For professionals" — her exact two complaints. Releasing the floor
     lets the body hug content: above "About" only the body's own 24px padding
     remains, below "For professionals" only the body's 24px flex gap.
     Same-specificity override, later in the cascade; desktop keeps 794.12. */
  .mk-footer__body { min-height: 0; }

  /* --- 11.3 · ITEM 4 — the five columns become full-width rows ---------- */
  /* TWO track systems own `.mk-tracks-5` and BOTH are overridden, because
     which one wins is a stylesheet-order fact this file must not depend on:
     grid-fluid.css:429 makes it display:GRID (repeat(2,1fr) at <=999 — the
     one actually winning today, measured at 768: About/Tools two-across with
     inert flex-direction:column computing alongside) and grid.css §<=1024
     still declares the FLEX wrap ladder. One column in either vocabulary.
     gap/row-gap 0 so a COLLAPSED row is button-tall only; the open panel's
     own padding restores the rhythm. */
  .mk-footer__body .mk-footer__links {
    grid-template-columns: 1fr;      /* grid-fluid's repeat(2,1fr) at <=999 */
    row-gap: 0;
    flex-direction: column;          /* grid.css's flex ladder, if it wins */
    flex-wrap: nowrap;
    align-items: stretch;
    gap: 0;
    /* R2-FOOTER (mobile round 2, founder: "remove some of that space above
       and below About ... and For professionals"): this file's own R11-11
       `align-content: center` is a DESKTOP decision — there it centres the
       five-column block in the body's flex:1 leftover. At <=999 the same
       rule centred the five COLLAPSED accordion rows in the leftover the
       794.12px body floor created (measured 412x780: 86px of dead band above
       "About", 85px below "For professionals"). (0,2,0) beats the unscoped
       (0,1,0) R11-11 rule, mobile only; desktop keeps center untouched. */
    align-content: start;
  }
  .mk-footer__body .mk-footer__links > .mk-footer__col {
    flex: 0 0 auto;
    width: 100%;
  }
  .mk-footer__col[data-mk-accordion] { gap: 0; }

  /* The heading <span> now sits inside the button, still the col's first
     child's content — site.css's `.mk-footer__col > :first-child` 25% white
     lands on the button and inherits through. font/color/text-align restate
     the UA button defaults back to the column's own text style. */
  .mk-footer__col-toggle {
    appearance: none;
    background: none;
    border: 0;
    margin: 0;
    padding: 10px 0;
    width: 100%;
    min-height: 44px;            /* same touch-target floor as _band-xs §3 */
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--mk-space-24);
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
  }
  /* Chevron: pure CSS, currentColor, points down at rest, up when open. */
  .mk-footer__col-toggle::after {
    content: "";
    flex: 0 0 auto;
    width: 8px;
    height: 8px;
    margin-right: 2px;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: translateY(-2px) rotate(45deg);
    transition: transform 300ms ease;
  }
  .mk-footer__col-toggle[aria-expanded="true"]::after {
    transform: translateY(2px) rotate(225deg);
  }

  /* Collapse mechanism. */
  .mk-footer__col-panel {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 300ms ease;
  }
  .mk-footer__col-panel-inner {
    min-height: 0;
    overflow: hidden;
    visibility: hidden;                 /* closed links: no tab stops */
    transition: visibility 0s 300ms;    /* hide only after the collapse ends */
    display: flex;
    flex-direction: column;
    gap: var(--mk-para-gap);            /* the column's own 12px advance */
  }
  .mk-footer__col-toggle[aria-expanded="true"] + .mk-footer__col-panel {
    grid-template-rows: 1fr;
  }
  .mk-footer__col-toggle[aria-expanded="true"] + .mk-footer__col-panel
    .mk-footer__col-panel-inner {
    visibility: visible;
    transition: visibility 0s;
    padding-bottom: var(--mk-para-gap);
  }
}

/* Accordion motion under reduced motion: state flips, nothing slides. */
@media (max-width: 999px) and (prefers-reduced-motion: reduce) {
  .mk-footer__col-panel,
  .mk-footer__col-panel-inner,
  .mk-footer__col-toggle::after { transition: none; }
}
