@charset "UTF-8";
/* =====================================================================
   MarkNtel Advisors - Home page
   Design handoff stylesheet. Markup and styling only; no behaviour.

   Palette is lifted from public/img/logo_new.svg, the same values the
   report description page uses:
     #040B34 navy - #004E79 ocean - #0B8FA3 teal - #05ABC4 cyan
   Everything the page authors is scoped under .mn-home so the shared
   header and footer are untouched.

   NO @layer here, deliberately. The five legacy app stylesheets this page
   still loads are unlayered, and an unlayered rule beats a layered one at
   any specificity - `a{color:#1d4ed8}` in style.css would win over
   `.mn-home a`. The sheet is ordered reset / base / bands / components
   instead, and the reset uses :where() so it carries class-level
   specificity without the type-selector trap that bit the report page.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. One gutter for the whole page

   Declared on :root, not on .mn-home, because the header and footer
   containers re-measured below sit outside .mn-home and must resolve the
   same value - otherwise the site chrome and the page drift apart. Same
   token, same value as the report description page, so the two pages
   share a left edge.
   --------------------------------------------------------------------- */
:root{--mn-gutter:clamp(18px,3vw,48px);}

/* ---------------------------------------------------------------------
   2. Shared footer patch - REMOVED 2026-09-04

   This section used to re-measure the vendor .footer's Bootstrap
   .container against this page's 1560px cap (the footer's own comment
   said, the day the header was redone, to remove its half of this block
   "when the footer is redesigned for real" - this is that day). The
   footer is now the shared .site-footer component
   (footer/footer-partial.html, styled in ../assets/css/components.css
   section 2) - it sizes itself off --mn-measure/--mn-gutter directly,
   the same shared tokens the header's own container uses, with no
   Bootstrap .container underneath it left to patch. Every selector this
   block used to target (.footer, .footer .container, .footer a,
   .footer p) matches nothing on this page any more.
   --------------------------------------------------------------------- */

/* ---------------------------------------------------------------------
   3. Tokens
   --------------------------------------------------------------------- */
.mn-home{
  /* Brand, sampled from the logo gradient */
  --ink:#040B34;
  /* The surface the dark bands sit on, separated from --ink so the two
     jobs can move independently: --ink is a TEXT colour on the light
     bands and stays the near-black navy the logo gives; --ground is a
     SURFACE and was lifted to a teal-leaning deep blue on 2026-08-26
     when the hero was (see the hero rules). Leaving the other two dark
     bands at #040B34 while the hero moved would have read as two
     unrelated darks on one page. Revert by setting this to var(--ink).
     Measured on #0A2A46: white 14.5:1, --on-dark 7.8:1, --aqua 9.4:1,
     --on-dark-hi 10.6:1 - every dark-band text style clears AA. */
  --ground:#0A2A46;
  --ocean:#004E79;
  --teal:#0B8FA3;
  --cyan:#05ABC4;
  --aqua:#6FE0EE;

  /* Neutrals, cool-tinted out of the navy so nothing reads as stock gray */
  --paper:#FFFFFF;
  --mist:#F1F5F8;
  --tint:#E9F6F9;
  --line:#DBE4EB;
  --rule:#C9D6E1;
  --slate:#41505E;
  --on-dark:#AFC0D4;
  --on-dark-hi:#CFDEED;
  --dark-rule:rgba(175,192,212,.20);

  /* --amber is absent on purpose. On the report page it is reserved for
     commerce; this page sells nothing, so the commerce colour never
     appears. See DESIGN-PLAN.md. */

  /* Source Serif 4 + Inter, chosen 2026-08-27, replacing Merriweather +
     IBM Plex Mono. Two families still, and one FEWER file: the mono is
     gone entirely, because every place it was used turned out to be the
     same small-caps label role that Inter now takes.

     THE SIZES BELOW ARE NOT THE OLD SIZES. Measured per em, at weight
     300/400:

                        x-height   cap-height   "0" advance
       Merriweather      0.555       0.745        0.6455
       Source Serif 4    0.455       0.675        0.470

     Source Serif 4's x-height is 18% smaller and it sets 15% narrower,
     so at an unchanged px value it would read a size and a half smaller
     than the page was drawn at. Every --text size is therefore scaled by
     1.12 and every ch measure by 1.22 - see the note above the type
     scale. Inter's caps are 4% LARGER than IBM Plex Mono's, so the label
     sizes are unchanged and only their tracking came down. */
  --text:'Source Serif 4',Georgia,'Times New Roman',serif;
  /* Named --ui, not --mono: it is a proportional sans now and a token
     that lies about its own contents is worse than a rename. */
  --ui:'Inter',system-ui,-apple-system,'Segoe UI','Roboto',sans-serif;

  --r:4px;
  /* Band rhythm. The brief asks for large, spacious sections; this is
     where that lives, and it is one token so the rhythm cannot drift
     band to band. */
  --pad:clamp(72px,10vw,152px);
  /* The spine's distance inboard of the content edge. Always smaller
     than --mn-gutter's own minimum (18px), so the hairline holds the
     same relationship from 320px to 1920px and never switches off. */
  --spine-inset:clamp(9px,1.5vw,42px);
  --spine:var(--rule);
  /* Thickened 2026-09-03 (requested) from a 1px hairline. One token, so
     the dot's own centring math (section 5) derives from it and the two
     cannot drift apart if this is retuned again. */
  --spine-width:2px;
  --spine-dot:var(--teal);
  --focus:var(--teal);

  color:var(--slate);
  background:var(--paper);
  font-family:var(--text);
  /* 16 -> 18px. Source Serif 4 at 16px has an x-height of 7.28px against
     Merriweather's 8.88px - genuinely too small for body copy. 18px puts
     it at 8.19px, within 8% of where the page was drawn, without pushing
     every derived size past what the layout can hold. Full x-height
     parity would need 19.5px and would break the measures. */
  font-size:18px;
  line-height:1.7;
  /* Long report codes, product names and addresses must not push the
     document past the viewport at any width. */
  overflow-wrap:break-word;
}

/* ---------------------------------------------------------------------
   4. Reset, scoped

   :where() contributes nothing to specificity, so each of these lands at
   (0,1,0) - enough to beat every element selector in the legacy sheets,
   and equal to a single-class component rule, which then wins on source
   order because it comes later. That is the whole reason the report
   page's `.mn-rd p, .mn-rd li` reset (0,1,1) could silently beat
   component rules and this one cannot.
   --------------------------------------------------------------------- */
.mn-home,
.mn-home :where(*,*::before,*::after){box-sizing:border-box;}
.mn-home :where(h1,h2,h3,h4,p,ul,ol,dl,dd,figure,blockquote){margin:0;}
.mn-home :where(ul,ol){padding:0;list-style:none;}
.mn-home :where(img,svg){display:block;max-inline-size:100%;block-size:auto;}
.mn-home :where(a){color:inherit;text-decoration:none;}
.mn-home :where(strong){font-weight:700;}
/* --cyan is the obvious brand choice for a focus ring and it fails:
   #05ABC4 measures 2.72:1 against white, under the 3:1 WCAG 1.4.11 asks
   of a focus indicator. --teal clears it at 3.83 on white and 3.41 on the
   mist band, and still reads at 4.96 on the navy. On the dark bands the
   ring's neighbour on both sides is the navy itself, so aqua can take
   over there at 12.3:1 and the indicator stays a brand colour rather
   than going grey. */
.mn-home :focus-visible{outline:2px solid var(--focus);outline-offset:4px;border-radius:2px;}

/* ---------------------------------------------------------------------
   5. Bands

   Every top-level section runs full bleed and carries its own grid, so
   there is no wrapper div anywhere on the page. --measure resolves to
   exactly the chrome's content box, which is what keeps the logo, the
   headline and the footer columns on one left edge.
   --------------------------------------------------------------------- */
.mn-band{
  --measure:min(calc(1560px - 2 * var(--mn-gutter)),calc(100% - 2 * var(--mn-gutter)));
  /* The spine dot's ring needs to fade into whatever this band is
     actually sitting on (section below) - --paper by default, since a
     plain .mn-band carries no background of its own and inherits
     .mn-home's. Each surface modifier restates this alongside its own
     background so the two can never say something different. */
  --band-bg:var(--paper);
  /* Split off the top half of the rhythm so a tint-on-tint seam (below)
     can tighten it without touching --pad itself - the dot and the
     --last hairline both read from this, not from --pad, so they stay
     level with wherever the top padding actually lands. */
  --pad-top:var(--pad);
  position:relative;
  display:grid;
  grid-template-columns:
    [full-start] minmax(0,1fr)
    [content-start] var(--measure)
    [content-end] minmax(0,1fr) [full-end];
  padding-block:var(--pad-top) var(--pad);
}
.mn-band>*{grid-column:content;}

/* Every band after the hero shares one tint ground (.mn-band--tint's
   2026-09-11 note) - between two of them there is no colour change to
   carry the seam, so a full --pad stacked on both sides of it reads as
   one dead gap rather than a break between sections. Scaled down on both
   bands framing an interior seam so it reads as a break without doubling
   the rhythm. First tried at *.5 (144px at 1440px, "fourteenth" entry);
   requested back up to *.75 (216px at 1440px) the same day - *.5 read as
   too tight once seen against the rest of the page; trimmed to *.66
   (190px at 1440px) on 2026-09-14, requested. Stays a share of --pad
   rather than a fixed 190px so the seam keeps scaling with the rhythm -
   at 320px a literal 190px would be wider than a whole band's own
   padding. The hero->first-band seam is untouched (it has a colour
   change to do the work) and so is the last band's own closing pad (no
   next sibling matches). */
.mn-band--tint + .mn-band--tint{--pad-top:calc(var(--pad) * .66);}
.mn-band--tint:has(+ .mn-band--tint){padding-block-end:calc(var(--pad) * .66);}

/* The spine. Two pseudo-elements, no DOM: the hairline down the gutter,
   and the dot that marks where this band starts. `(100% - measure)/2` is
   the band's own left margin, so the offset is derived rather than
   measured and nothing needs re-syncing when the cap changes. */
.mn-band::before,
.mn-band::after{
  content:"";
  position:absolute;
  inset-inline-start:calc((100% - var(--measure)) / 2 - var(--spine-inset));
}
.mn-band::before{inset-block:0;inline-size:var(--spine-width);background:var(--spine);}
/* The dot sits in a ring, 2026-09-03 (requested) - a halo built from two
   box-shadow layers rather than a second DOM node or a third
   pseudo-element (::before and ::after are both already spent). The
   first shadow is solid, in the band's OWN background (--band-bg above),
   which reads as a gap rather than a ring in its own right - the same
   trick a focus ring uses to clear the control it surrounds. The second
   is the actual ring: --spine-dot itself, mixed down to 40% so it glows
   rather than reading as a second, harder dot.
   margin-inline-start is `(spine-width - dot width) / 2`, not a literal,
   so the dot stays centred on the line above if either is retuned. */
.mn-band::after{
  inset-block-start:calc(var(--pad-top) + 5px);
  inline-size:7px;block-size:7px;
  margin-inline-start:calc((var(--spine-width) - 7px) / 2);
  border-radius:50%;
  background:var(--spine-dot);
  box-shadow:
    0 0 0 3px var(--band-bg),
    0 0 0 5px color-mix(in srgb,var(--spine-dot) 40%,transparent);
}
/* The last band's hairline stops just past its dot rather than running
   into the footer, so the line reads as having an end rather than being
   cut off by the chrome. */
.mn-band--last::before{inset-block-end:auto;block-size:calc(var(--pad-top) + 9px);}

.mn-band--dark{
  background:var(--ground);
  color:var(--on-dark);
  --spine:var(--dark-rule);
  --spine-dot:var(--aqua);
  --focus:var(--aqua);
  --band-bg:var(--ground);
}
/* NO BAND USES THIS SINCE 2026-09-11 - the data assets band was its last
   user and is now on --tint with everything else after the hero. Kept as
   the third member of the surface set rather than deleted, the same call
   .mn-h2--wide got when it lost its last user; --mist itself is still
   live as the practice rail's --mnp-fill. */
.mn-band--mist{background:var(--mist);--band-bg:var(--mist);}
/* The engagement band, 2026-08-27. It was a white band directly after the
   hero and directly before another white band, which is most of why it
   read as flat: nothing separated the first thing after the headline from
   the thing after that. --tint was already declared and was the only
   surface token no band was using.

   SINCE 2026-09-11 IT IS THE GROUND FOR EVERY BAND AFTER THE HERO
   (requested). The page is the hero and then one continuous surface;
   bands are told apart by the spine, the dots and their own content, not
   by alternating fills. Declared per band rather than once on .mn-home so
   that --band-bg (the spine dot's ring gap) stays stated beside the
   background it has to match - see .mn-band::after. */
.mn-band--tint{background:var(--tint);--band-bg:var(--tint);}

/* ---------------------------------------------------------------------
   6. Shared type
   --------------------------------------------------------------------- */
/* THE SMALL RULE BEFORE EVERY EYEBROW is now the base, not a modifier
   (2026-09-03, requested) - it used to be .mn-eyebrow--rule, applied to
   two of the page's bands ("01 — What we do", "Global reach"), taken
   from the 2a artboard. Every other band's eyebrow ran plain, which read
   as inconsistent once several were sitting on the same page - the
   request was to make it "formal across the page", so the drawn rule
   moved onto .mn-eyebrow itself and the modifier is gone.

   THE LINE STARTS FROM THE SPINE'S OWN RING (2026-09-03, corrected on
   request - a first pass drew a fixed 28px dash floating in the content
   column, disconnected from the dot beside it; "the line must start from
   the ring, as it is starting on Hero section"). This is the same
   mechanism the centred hero already uses for its own eyebrow rule
   (hero-centered.css, ".mn-hero--center>.mn-eyebrow--values::before"),
   simplified for a single left-aligned line instead of a centred,
   flex-balanced one: the pseudo-element is pulled left by
   `margin-inline-start:calc(-1 * var(--spine-inset))`, the exact distance
   .mn-band::before/::after use to offset the spine from the content
   edge, so its start lands on the same inset-inline-start the dot's ring
   is centred on - not merely alongside it. Its own inline-size is
   `--spine-inset` too, so it reaches from there back to the text with
   nothing left over. A negative margin on a flex item shifts only that
   item's own edge; gap and the text after it are undisturbed, so nothing
   here moves the eyebrow text (or its alignment with the h2 and lede
   below it) from where it already was. Fluid with --spine-inset, so this
   holds from 320px to 1920px without a breakpoint - "on desktop and
   mobile both".

   :not(.mn-eyebrow--values) IS LOAD-BEARING. The hero's own eyebrow -
   "Clarity | Strategy | Results" - carries the base .mn-eyebrow class
   too, and on the centred hero variant draws its own considerably more
   elaborate version of this exact idea already, at higher specificity,
   so it would win regardless - but excluding it here as well means a
   plain, non-centred hero (index.html's own left-aligned variant, which
   has no such override) does not pick up a second, unwanted line of its
   own in front of "Clarity". */
.mn-eyebrow{
  display:flex;
  align-items:center;
  gap:12px;
  font-family:var(--ui);
  font-size:11px;font-weight:500;
  letter-spacing:.14em;text-transform:uppercase;line-height:1.4;
  color:var(--ocean);
  margin-block-end:clamp(20px,2.6vw,34px);
}
.mn-band--dark .mn-eyebrow{color:var(--aqua);}
/* A drawn rule rather than an em dash: it takes a real length and sits on
   the caps' centre line instead of on the font's idea of one, and it
   costs no element. */
.mn-eyebrow:not(.mn-eyebrow--values)::before{
  content:"";
  inline-size:var(--spine-inset);
  /* Matched to the spine's own thickness (section 5) - 2px, not a plain
     1px - so the line reads as one continuous stroke turning the corner
     at the ring rather than as a thick spine narrowing into a thin one. */
  block-size:var(--spine-width);
  flex:none;
  margin-inline-start:calc(-1 * var(--spine-inset));
  /* Coloured to match the VERTICAL spine line itself, 2026-09-03
     (corrected on request - it had briefly matched the ring's colour
     instead, color-mix(--spine-dot at 40%), which read as a mismatch
     against the line it is actually the continuation of). --spine is the
     exact token .mn-band::before paints the vertical hairline with
     (section 5) - --rule on light bands, --dark-rule on dark ones via
     .mn-band--dark - so naming it here is what keeps the two literally
     identical rather than two rules that happen to compute the same
     colour today. */
  background:var(--spine);
}

/* The values eyebrow - "Clarity | Strategy | Results" (2026-08-27,
   requested, replacing "Strategic research and advisory").

   Three words of equal weight separated by a delimiter is a list, so it
   is a <ul> of three <li> rather than one text node with two typed pipe
   characters in it. That buys three things: the words are three items to
   a screen reader instead of one run-on string; the delimiter is drawn
   rather than typed, so it is a hairline sized to the caps rather than a
   glyph sized to the font's own idea of a bar; and the spacing either
   side of it is a token instead of whatever the space character happens
   to measure in the mono face.

   It is a modifier on .mn-eyebrow, not a new component: the family,
   weight, uppercase and colour - including the aqua on dark bands - all
   still come from the base rule.

   SIZE. It is the base eyebrow size, 11px, the same as every section
   label on the page (2026-08-27, requested). It was briefly a ramp of its
   own up to 17px; at that size it read as a second headline competing
   with the h1 rather than as the label it is. Family, weight, tracking
   and colour all come from .mn-eyebrow now, so this modifier carries
   nothing but the layout of the delimiter - one size, one voice, and the
   spine dot needs no hero-specific offset either.

   Two lines, never. The whole string measures 209px at 320px against
   ~284px of content column, so it is set nowrap: a wrapped item would
   start a line with a dangling separator, and there is no CSS that
   suppresses a rule only at the start of a flex line. */
.mn-eyebrow--values{
  /* One token for the two halves of the gap, so the separator sits on
     the centre line between the words by construction. */
  --values-gap:clamp(11px,1.5vw,20px);
  display:flex;
  flex-wrap:nowrap;
  align-items:center;
  gap:var(--values-gap);
}
.mn-eyebrow--values>li{position:relative;}
/* The delimiter. A rule, not a "|", so it takes a real height and a real
   position instead of the glyph's. */
.mn-eyebrow--values>li+li::before{
  content:"";
  position:absolute;
  /* Half the gap back, then half the tracking again: letter-spacing is
     added AFTER the last letter of the preceding word too, so centring
     on the gap alone leaves the rule ~2px adrift toward the next word. */
  inset-inline-start:calc(var(--values-gap) / -2 - .06em);
  /* .34em top and bottom is the leading the 1.4 line-height puts above
     and below a .72em cap, so the rule measures the caps rather than the
     line box. */
  inset-block:.34em;
  inline-size:1px;
  background:currentColor;
  opacity:.6;
}

/* Light at large, semibold at small: Source Serif 4 at 300 and display
   size is quiet and expensive, at 600 it is a report heading. That switch
   is the page's whole type argument, so both display steps sit at 300 -
   and it is a stronger argument in Source Serif than it was in
   Merriweather, whose 300 is barely lighter than its 400. */
.mn-h2{
  font-weight:300;
  font-size:clamp(30px,4vw,51.5px);
  line-height:1.16;
  letter-spacing:-.022em;
  color:var(--ink);
  max-inline-size:23ch;
}
.mn-band--dark .mn-h2{color:#FFFFFF;}
.mn-h2--wide{max-inline-size:32ch;}

.mn-lede{
  margin-block-start:clamp(20px,2.4vw,30px);
  max-inline-size:68ch;
  font-size:clamp(18px,1.3vw,20.5px);
  line-height:1.78;
}
.mn-band--dark .mn-lede{color:var(--on-dark);}
/* A second lede paragraph is a continuation of the same body copy, not a
   new block - so it takes a same-scale text gap (1em, scaling with the
   paragraph's own clamped font-size) rather than the larger block rhythm
   .mn-lede itself opens with above the h2. */
.mn-lede+.mn-lede{margin-block-start:1em;}

/* ---------------------------------------------------------------------
   7. Buttons

   The gradients are the report page's, unchanged, because their contrast
   was measured there: #0A7F91 is the lightest stop and carries white at
   4.71:1, improving toward the navy end.
   --------------------------------------------------------------------- */
.mn-btn{
  display:inline-flex;align-items:center;gap:10px;
  min-block-size:48px;
  padding:13px 26px;
  border:1px solid transparent;border-radius:var(--r);
  font-family:var(--text);font-size:17px;font-weight:600;line-height:1.3;
  letter-spacing:.005em;
  text-align:center;
  transition:transform .2s cubic-bezier(.22,.75,.3,1),background .2s ease,border-color .2s ease,color .2s ease;
}
.mn-btn:hover{transform:translateY(-1px);}
.mn-btn__icon{
  inline-size:17px;block-size:17px;flex:none;
  fill:none;stroke:currentColor;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;
}
.mn-btn--solid{
  background:linear-gradient(135deg,#0A7F91 0%,#026C86 48%,#004E79 100%);
  color:#FFFFFF;
  box-shadow:0 8px 22px rgba(0,78,121,.28);
}
.mn-btn--solid .mn-btn__icon{color:var(--aqua);}
.mn-btn--solid:hover{background:linear-gradient(135deg,#0C8EA2 0%,#037795 48%,#005887 100%);color:#FFFFFF;}
.mn-btn--ghost{border-color:rgba(175,192,212,.42);color:#FFFFFF;}
.mn-btn--ghost:hover{border-color:var(--aqua);color:var(--aqua);background:rgba(111,224,238,.07);}

/* ---------------------------------------------------------------------
   8. Hero
   --------------------------------------------------------------------- */
.mn-hero{
  /* THE LEDE SIZE, as a token, because two rules in two files need the
     SAME value: .mn-hero__lede below, and .mn-cycle in hero-centered.css,
     whose 54ch measure only lands on the lede's edges while both resolve
     ch against an identical font-size. It was written out twice; it is
     now written once and inherited.

     Reduced on phones (2026-09-10, requested), and only on phones. The
     headline's new fluid floor drops it to 26.3px at 320, which left the
     18.5px lede sitting too close behind it - 1.42x, where the pairing
     was drawn at about 2x. This term brings the lede down with it:
     16px at 320, back to the original 18.5px by 447px, and from there up
     every width is exactly the value it was before.

     16px is deliberately the floor. The ratio at 320 comes out 1.64
     rather than the 2.05 the desktop pairing has; matching it would mean
     a 12.8px lede, and phone body copy does not go under 16. See NOTES.md.

     Read as: 9.65px + 1.98vw is the phone ramp (16px at 320 -> 18.5px at
     447), min() stops it at 18.5, max() hands over to the original
     1.45vw where that overtakes at 1276, and the clamp caps both ends. */
  --mn-hero-lede:clamp(16px,max(1.45vw,min(18.5px,calc(9.65px + 1.98vw))),22.5px);
  /* --pad, not a bare padding-block, because the spine dot is positioned
     from it. Overriding the padding alone would leave the hero's dot
     29px below its eyebrow while every other band's sat level with it. */
  --pad:clamp(72px,9vw,128px);
  /* Bottom padding cut back (2026-09-10, requested: less blank space
     between the CTA and the foot of the band). Was
     clamp(80px,10vw,140px), which put 140px under the button on a
     desktop and 80px on a phone - a gap sized for the two-button row
     that used to sit there. It is now about a third less at both ends,
     and reads under the top padding rather than over it: 96px against
     the 128px above at desktop, 56px against 72px on a phone.

     Still a padding, not a height: the band is sized by its content, so
     nothing here can crop the copy at any width or zoom level. */
  padding-block-end:clamp(56px,6vw,96px);
  /* Lifted off the near-black navy (2026-08-26, requested - "lighter
     gradient of teal, eye soothing"). It used to be --ink (#040B34) with
     two faint washes over it, which measured beautifully and read as a
     black band. The ground is now its own teal-leaning ramp, roughly
     #0B2E4F to #187E92, with the cyan bloom top-right and an ocean wash
     bottom-left.

     The band is still comfortably dark where the words are: at 35% across,
     where the lede ends, the composite is about rgb(13,57,91) and white
     measures 12:1, --on-dark 6.5:1. The bright end only ever sits under
     the evidence panel, which carries its own ground - see --panel-ground
     below, which is what had to change to make this legal. */
  background-color:#0C3355;
  background-image:
    radial-gradient(115% 135% at 90% 6%,rgba(23,150,172,.40) 0%,rgba(12,51,85,0) 62%),
    radial-gradient(100% 125% at 2% 98%,rgba(9,66,102,.50) 0%,rgba(12,51,85,0) 58%),
    linear-gradient(118deg,#0B2E4F 0%,#103C60 44%,#145B79 76%,#187E92 100%);
}
/* The hero has no wrapper elements at all: every child sits directly in
   the band's content column, which is what keeps the deepest text node on
   this page at six levels.

   Two columns from 1024px up (2026-08-26, fixed after review). The first
   version stacked the copy and then dropped the stratigraphy figure into
   the lower right of the band with `justify-self:end`. Read as intended
   that is an asymmetric composition; read cold it is six lines of text
   sitting in the corner of a banner with nothing holding them there, and
   that is how it read - it was reported as a formatting fault, which is
   the only verdict that matters.

   The tracks are calculated rather than fr-based because the two of them
   have to add up to --measure exactly, or the content stops sharing a
   left edge with the header and the footer. .58/.42 leaves the headline
   more room than it needs at every width the query applies to: at 1024
   the copy column is 532px against the h1's 520px at 14ch, and the margin
   only grows from there. */
@media (min-width:1024px){
  .mn-hero{
    --hero-gap:clamp(40px,4.4vw,88px);
    grid-template-columns:
      [full-start] minmax(0,1fr)
      [content-start] calc((var(--measure) - var(--hero-gap)) * .58)
      var(--hero-gap)
      calc((var(--measure) - var(--hero-gap)) * .42)
      [content-end] minmax(0,1fr) [full-end];
    align-items:start;
  }
  /* (0,2,0), so these beat the band's own `.mn-band>*{grid-column:content}`
     at (0,1,0) without needing !important or an id. */
  .mn-hero>.mn-eyebrow,
  .mn-hero>h1,
  .mn-hero>.mn-hero__lede,
  .mn-hero>.mn-hero__cta{grid-column:2;}
  .mn-hero>.mn-strata{
    grid-column:4;
    grid-row:1 / span 4;
    align-self:center;
    inline-size:100%;
    margin-block-start:0;
  }
}
.mn-hero h1{
  font-weight:300;
  /* THE TYPE RAMP. Three parts, all of them measured (2026-09-10):

     - 89px cap, requested. Was 92px; the longer headline copy was
       filling too much of the band.
     - 7.4vw growth, unchanged. Reaches the 89px cap at 1203px.
     - A FLUID FLOOR instead of the flat 38px that was here. The flat
       floor is what made the headline break to three lines on a phone:
       "Driving Business Growth." needs about 10.2x its own font-size in
       width, so at 38px it needs 386px, and a 320px phone offers 284px
       between the gutters. The floor now asks how much room there is
       and takes what fits.

     The divisor is measured, not guessed. Binary-searched in the browser
     for the largest size the line holds on one line:

       320px viewport, 284px measure -> 27.00px   (ratio 10.52)
       360px          , 324px         -> 31.25px   (ratio 10.37)
       390px          , 354px         -> 34.25px   (ratio 10.34)
       414px          , 378px         -> 37.00px   (ratio 10.22)

     The ratio is not constant - the face sets wider per em at small
     sizes - so 10.8 is used, which sits under every measured ratio and
     leaves 2-4% of slack at each width.

     The floor reads the gutter token rather than repeating its value, so
     it cannot drift from the page's one gutter. Below 600px the gutter
     is at its 18px floor, which is the range this term governs; it hands
     over to 7.4vw at about 446px and never bites above it. */
  font-size:clamp(min(38px,calc((100vw - 2 * var(--mn-gutter)) / 10.8)),7.4vw,89px);
  line-height:1.04;
  letter-spacing:-.028em;
  color:#FFFFFF;
  /* THE MEASURE. 23ch, re-measured for the copy of 2026-09-10, "Industry
     Insights." / "Driving Business Growth.".

     22ch was tried first and is too tight to trust. At the 89px cap
     1ch is 41.3px, so 22ch is 908px against the 852px the long line
     needs - comfortable. But ch tracks font-size while the line's width
     per em GROWS as the size falls, and the two meet almost exactly at
     38px: 22ch resolves to 386.4px there and the line needs 386.35px.
     A 0.05px margin is not a margin. 23ch is 404px at 38px and 949px at
     89px, which holds 4-11% of slack across the whole ramp.

     The break itself does not depend on this number; it is in the
     markup. The cap only decides whether each authored line survives as
     one line. */
  max-inline-size:23ch;
}
/* Placed AFTER the rule above, not inside the two-column query further up
   this file: both selectors are (0,1,1), so at equal specificity the later
   one wins and the earlier one would have been dead. This file uses no
   @layer, so source order is the cascade. */
@media (min-width:1024px){
  /* The headline ramp comes down inside this query, and ONLY inside it
     (2026-08-27, with the new copy). Not a size retuned at a breakpoint
     for its own sake - the layout genuinely changes shape here, from one
     full-measure column to a copy column of .58, and the headline has to
     fit the narrower one.

     NOTE 2026-09-10: the copy named below is superseded - the headline
     now reads "Industry Insights." / "Driving Business Growth.". This
     query still only matches .mn-hero:not(.mn-hero--center), and no such
     page exists any more, so the ramp is dormant; the reasoning is kept
     verbatim for whoever restores a two-column hero.

     "Make better decisions." was 22 characters against the old headline's
     longest line of 17, and it no longer fitted: measured at 7.4vw it needs
     635px in a 529px column at 1024, and stays 60-119px over the column
     all the way to about 1490 - which drew it as THREE lines, breaking
     "Make better / decisions.". 5.9vw is the steepest ramp that clears
     the column at every width in that range, with about 4% to spare, and
     it still reaches the 92px cap - just at 1559 instead of 1243.

     The other two ways out were both worse. Widening the split to the
     .72 the headline wants leaves the stratigraphy panel 255px at 1024,
     which is not enough for its five lines. Cutting the ramp globally
     would shrink the headline on phones and tablets, where it already
     fits.

     The :not() is load-bearing. index-centered.html cancels the two
     columns in hero-centered.css, so its headline has the full measure
     and does not need this - but it still carries .mn-hero, so a plain
     .mn-hero>h1 would shrink it for nothing. Excluded here rather than
     re-raised in the variant sheet, so the ramp lives in one place. */
  .mn-hero:not(.mn-hero--center)>h1{font-size:clamp(38px,5.9vw,92px);}
}
/* The headline is two lines, and the break is in the markup (copy of
   2026-09-10: "Industry Insights." / "Driving Business Growth."). Each line
   is its own block, so the break holds at every width instead of
   depending on where a ch cap happens to fall - which is what the old
   single-sentence headline relied on, and what made its cap so delicate.

   The spans guarantee AT LEAST this break, not exactly it: below the
   width where a line stops fitting, it wraps further, which is what has
   to happen on a phone. */
.mn-hero__h-line{display:block;}
.mn-hero__lede{
  margin-block-start:clamp(22px,2.6vw,32px);
  max-inline-size:54ch;
  font-size:var(--mn-hero-lede);
  line-height:1.72;
  color:var(--on-dark);
}
/* ONE hero call to action (2026-09-10, requested). This replaces
   .mn-hero__actions, the flex row that held two buttons; the second one
   ("How we work") was removed from the markup, and a flex row with a
   single item is a wrapper with nothing to do.

   justify-self, not a margin or a text-align: .mn-band is display:grid at
   every width and the anchor is a direct child of it, so it IS a grid
   item. .mn-btn is inline-flex, which blockifies to flex on a grid item
   and would otherwise stretch the button across the whole content
   column. The centred variant overrides this line - see hero-centered.css.

   Which call to action appears at a given width remains the client's
   decision, taken page by page: this file adds, removes, reorders and
   re-labels nothing at a breakpoint. */
.mn-hero__cta{
  justify-self:start;
  margin-block-start:clamp(30px,3.6vw,46px);
}

/* --- The stratigraphy: the headline, drawn ---
   Line one is the published market size; lines two to five are what a
   mandate actually looks at. The rail down its leading edge is the logo
   gradient run vertically, which is where the page's spine comes from.

   It is a bounded panel rather than loose text on a rule (2026-08-26).
   A single hairline down one side is not enough to make six lines of type
   read as an exhibit; a box, a border on all four sides and its own
   ground are. It also earns its keep on the textured hero variant, where
   the figure sits over the bright end of the gradient and needs a ground
   of its own to stay legible - see assets/css/hero-textured.css. */
/* Below 1024 the hero is a single column and the panel simply fills it.
   It used to be capped and pushed right with `justify-self:end`, which
   is what put it in the corner of the band with nothing beside it - the
   same fault the two-column query fixes above 1024, and it needed fixing
   at tablet widths too, not only on desktop. */
.mn-strata{
  /* The rail's own gradient is a token so the variant sheet can restyle
     it without using the `background` shorthand on ::before. The shorthand
     would reset background-image, -size and -position, and the scan node
     below is a second background layer on exactly those properties - the
     textured hero silently lost its travelling node the first time this
     was written as a plain override. */
  --rail-gradient:linear-gradient(180deg,rgba(111,224,238,0) 0%,var(--cyan) 34%,var(--aqua) 100%);
  position:relative;
  inline-size:100%;
  margin-block-start:clamp(48px,5.5vw,92px);
  padding:clamp(22px,2.4vw,30px) clamp(20px,2.2vw,30px) clamp(20px,2.2vw,26px) clamp(26px,2.8vw,36px);
  border:1px solid rgba(175,192,212,.20);
  border-radius:var(--r);
  /* .90, not the .34 this started at. The panel sits over the brightest
     part of whichever hero ground is in play, and both of them are now
     bright enough there to matter. Worst case on this hero is about
     rgb(22,126,151); the panel composites to rgb(6,23,62) over it and the
     faint first stratum - the binding constraint, at .70 alpha - measures
     5.3:1. On the textured hero the same calculation lands at 5.0:1.
     One value covers both, so the variant sheet does not override it.
     Do not lighten it without re-running that first stratum. */
  background:var(--panel-ground,rgba(4,11,52,.90));
}
/* The rail is a pseudo-element, not a div: it is the logo gradient run
   vertically and it carries no content. Inset by -1px on three sides so
   it sits over the panel's own border rather than beside it. */
.mn-strata::before{
  content:"";
  position:absolute;
  inset-block:-1px;
  inset-inline-start:-1px;
  inline-size:3px;
  border-radius:2px 0 0 2px;
  background:var(--rail-gradient);
  transform-origin:top;
}
.mn-strata__list li{
  position:relative;
  padding-block:clamp(12px,1.3vw,17px);
  font-size:clamp(17px,1.3vw,20px);
  line-height:1.4;
  letter-spacing:-.01em;
  color:var(--on-dark);
  border-block-end:1px solid rgba(175,192,212,.14);
}
/* The tick that ties each stratum back to the rail. The offset mirrors
   the panel's inline-start padding, so the tick starts on the rail. */
.mn-strata__list li::before{
  content:"";
  position:absolute;
  inset-inline-start:calc(-1 * clamp(26px,2.8vw,36px) - 1px);
  inset-block-start:calc(50% - 1px);
  inline-size:clamp(11px,1.2vw,16px);block-size:1px;
  background:rgba(175,192,212,.34);
}
/* The top line is what a report gives you, so it is set at the weight the
   page reserves for asides. The bottom line is the argument. */
/* .70 rather than the .62 this started at: composited over the navy that
   is #6E7E97 at .62, which measures 4.62:1 and leaves nothing in hand
   once the hero's radial gradients lighten the ground underneath it.
   At .70 it composites to #7C8AA4 and measures 5.44:1. */
.mn-strata__list li:first-child{color:rgba(175,192,212,.70);}
.mn-strata__list li:last-child{color:#FFFFFF;font-weight:600;border-block-end:0;}
.mn-strata__list li:last-child::before{background:var(--aqua);inline-size:clamp(15px,1.7vw,22px);}

/* One orchestrated moment on load: the rail draws down and the strata
   arrive under it. Then one continuous one (2026-08-26, requested): a
   bright node runs down the rail on an 8s loop and each stratum's tick
   reaches out as the node passes it, so the panel reads as something
   scanning down the six lines. Everything else on the page is still.

   WHY THE TEXT DOES NOT MOVE, AND WHY NOTHING WASHES OVER IT

   The obvious reading of "like it is scrolling" is a marquee - loop the
   six lines upward through a shorter window. Three things rule it out:
   the six lines are an argument that reads top to bottom, and a loop
   destroys the order; a window short enough to scroll hides three of the
   six at any moment, including the payoff line; and content that moves
   for more than five seconds needs a pause control under WCAG 2.2.2,
   which cannot be built without script. See NOTES.md.

   A light sweep across the whole panel was the next idea and it fails on
   arithmetic. The faint first stratum sits at 4.8:1 over the panel. A
   sweep at the .15 alpha it needs to be visible lifts the ground under
   that line and drops it to 3.86:1 - under AA. Solving for the alpha
   that keeps it legal gives .05, which is too faint to read as anything.

   So the motion is on the rail and the ticks, which are decorative marks
   rather than text. They can be as bright as they like, the six lines
   stay exactly where they are and exactly as legible, and the effect
   still reads as a scan travelling down them.
   --------------------------------------------------------------------- */
@media (prefers-reduced-motion:no-preference){
  /* Two layers now: the travelling node over the rail's own gradient.
     The base rule outside this query keeps the plain single-layer rail,
     so a reduced-motion visitor gets exactly what shipped before. */
  .mn-strata::before{
    background-image:
      linear-gradient(180deg,rgba(234,249,253,0) 0%,#EAF9FD 46%,rgba(234,249,253,0) 100%),
      var(--rail-gradient);
    background-repeat:no-repeat;
    background-size:100% 20%,100% 100%;
    background-position:0 -30%,0 0;
    animation:
      mn-rail 900ms cubic-bezier(.32,.72,.32,1) both,
      mn-scan 8s linear 1.4s infinite;
  }
  .mn-strata__list li{
    animation:mn-stratum 620ms cubic-bezier(.22,.75,.3,1) both;
    animation-delay:calc(260ms + var(--i) * 110ms);
  }
  /* The tick pulse animates transform only, never `background` or
     `inline-size`. That is what lets the last stratum keep its aqua tick
     and its wider base size while taking the same pulse - and it keeps
     the whole effect off the main thread and off the layout. */
  .mn-strata__list li::before{
    transform-origin:0 50%;
    animation:mn-tick 8s ease-in-out infinite;
    /* Timed against the node rather than evenly spaced, and the timing is
       derived rather than eyeballed. The node's centre sits at panel
       fraction c = 0.8p + 0.1, where p is its background-position; p runs
       -30% to 130% between the 4% and 96% keyframe holds. The ticks sit at
       roughly c = .15 .30 .46 .62 .78, which solves to peaks at 1.99s,
       2.85s, 3.77s, 4.69s and 5.61s into the node's own 8s cycle. This
       animation peaks at 8% of 8s, so each delay is that figure plus the
       node's 1.4s lead, minus 0.64s.

       The first attempt used a flat calc(1.4s + i * .88s) and every tick
       fired about 1.3s early - measured, the node was at the top of the
       rail while the SECOND tick was mid-pulse. The sync stays approximate
       by nature, since the panel's height changes with the column, but it
       is now approximately right rather than approximately wrong. */
    animation-delay:calc(2.75s + var(--i) * .9s);
  }
  /* --i in the sheet rather than an inline style attribute: these are
     fixed positions in a fixed list, not values the server injects. */
  .mn-strata__list li:nth-child(1){--i:0;}
  .mn-strata__list li:nth-child(2){--i:1;}
  .mn-strata__list li:nth-child(3){--i:2;}
  .mn-strata__list li:nth-child(4){--i:3;}
  .mn-strata__list li:nth-child(5){--i:4;}
  @keyframes mn-rail{from{transform:scaleY(0);}to{transform:scaleY(1);}}
  @keyframes mn-stratum{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:none;}}
  /* The node starts fully above the rail and ends fully below it. With a
     20% band, background-position -30% puts its bottom edge at -4% of the
     rail and 130% puts its top edge at 104%, so it never sits parked at
     either end. The holds at 4% and 96% are the beat between passes. */
  @keyframes mn-scan{
    0%,4%{background-position:0 -30%,0 0;}
    96%,100%{background-position:0 130%,0 0;}
  }
  @keyframes mn-tick{
    0%,3%{transform:scaleX(1);}
    8%{transform:scaleX(2.1);}
    22%,100%{transform:scaleX(1);}
  }
}

/* ---------------------------------------------------------------------
   9. What we do

   Rebuilt 2026-09-01 (requested) from the 2a artboard of the Claude
   Design project "Homepage What We Do". The layout is that artboard's:
   the section title, lede and a single CTA hold a sticky left column,
   and four offers sit as a 2x2 tile grid on the right.

   ONLY the layout came across. The artboard's ink/amber palette and its
   Newsreader + Archivo type were dropped at the door - the band keeps
   the --tint surface it already had, this page's tokens, this page's two
   families, and the same 0.46 / 0.54 split it used for the triangle. The
   artboard's 1440px fixed board becomes the page's fluid --measure.

   WHAT THIS REPLACED. .mn-modes - three <li> clipped into one triangle -
   is deleted rather than left unused: it drew three engagement modes and
   this band now carries four offers, so no element on either home page
   file can still use it. The geometry and the reasoning behind it are in
   NOTES.md.

   COLOUR. Tiles are --paper on the --tint band, ruled with --line. The
   icon badge is --ground carrying --aqua at 9.4:1. On --paper: the tag
   in --ocean at 8.87:1, the term in --ink at 19.05:1, the note in
   --slate at 8.29:1. Every one clears AA at the size it is set. The tile
   ramp of accent colours the triangle used is gone with it - four tiles
   in a grid are peers, and a four-step ramp across peers is decoration
   that says something untrue about their order.
   --------------------------------------------------------------------- */

/* The CTA, ruled off the lede the way the artboard has it. The artboard
   drew a label and an arrow; a label that navigates is an <a>, so this
   is a real link to the contact page and not a scroll target.

   ONE CTA, and it is the same one at every width - the band does not
   gain, lose or re-label an action at a breakpoint. */
.mn-what__cta{
  margin-block-start:clamp(26px,3vw,40px);
  padding-block-start:clamp(20px,2.4vw,28px);
  border-block-start:1px solid var(--rule);
}
.mn-what__link{
  display:inline-flex;
  align-items:center;
  gap:10px;
  font-family:var(--ui);
  font-size:12.5px;font-weight:600;
  letter-spacing:.12em;text-transform:uppercase;
  line-height:1.4;
  color:var(--ink);
  /* 44px of touch target on a 17.5px line box, taken back out of the
     flow so the rule above still sits where the design put it and the
     label keeps the band's left edge. */
  padding-block:13px;
  margin-block:-13px;
}
.mn-what__link:hover{color:var(--ocean);}
.mn-what__arrow{
  inline-size:20px;block-size:20px;flex:none;
  fill:none;stroke:var(--teal);stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;
}

/* Narrow first: four tiles in one column. */
.mn-offers{
  margin-block-start:clamp(40px,5vw,72px);
  display:grid;
  grid-template-columns:minmax(0,1fr);
  gap:clamp(16px,2vw,28px);
}
.mn-offers>li{
  display:flex;
  flex-direction:column;
  gap:clamp(12px,1.3vw,18px);
  padding:clamp(24px,2.6vw,36px) clamp(20px,2.4vw,34px);
  background:var(--paper);
  border:1px solid var(--line);
  border-radius:var(--r);
}
.mn-offers>li:hover{border-color:var(--cyan);box-shadow:0 10px 24px rgba(4,11,52,.09);}

/* The 2x2. A container query, so the grid answers to the band's width
   and not the viewport's - which is what keeps it a 2x2 inside the
   0.54 track above 1080px as well as across the full-width band below
   it. 520px is where two tiles still hold a 230px measure; under that
   the longest term ("Procurement & category intelligence") sets on four
   lines and the tiles stack instead. A shape change, not a retune. */
@container mn-ctx (min-width:520px){
  .mn-offers{grid-template-columns:repeat(2,minmax(0,1fr));}
}

/* Tag and icon on one line. The icon carries nothing the tag does not
   already say in words, so it is decorative - and the artboard's 40px
   circular badge is painted on the <svg> itself rather than on a span
   wrapped round it. */
.mn-offers__tag{
  display:flex;
  align-items:center;
  gap:clamp(10px,1.1vw,14px);
  font-family:var(--ui);
  font-size:11px;font-weight:600;
  letter-spacing:.14em;text-transform:uppercase;
  line-height:1.4;
  color:var(--ocean);
}
.mn-offers__icon{
  inline-size:20px;block-size:20px;flex:none;
  padding:10px;
  box-sizing:content-box;
  border-radius:50%;
  background:var(--ground);
  color:var(--aqua);
  fill:none;stroke:currentColor;stroke-width:1.3;
}
.mn-offers__term{
  font-size:clamp(19px,1.6vw,24px);
  font-weight:600;
  line-height:1.25;
  letter-spacing:-.015em;
  color:var(--ink);
}
.mn-offers__note{
  font-size:clamp(15px,1.1vw,16.5px);
  line-height:1.65;
  color:var(--slate);
}

@media (prefers-reduced-motion:no-preference){
  .mn-offers>li{transition:border-color .25s ease,transform .25s ease,box-shadow .25s ease;}
  .mn-offers>li:hover{transform:translateY(-2px);}
  .mn-what__arrow{transition:transform .2s cubic-bezier(.22,.75,.3,1);}
  .mn-what__link:hover .mn-what__arrow{transform:translateX(4px);}
}

/* The split band. Two columns cut out of --measure itself rather than
   out of a wrapper: the copy takes the first track, the tiles the third,
   and the middle track is the gutter between them. The tracks sum to
   --measure exactly, so `grid-column: content` still spans all three and
   the band's left edge is the same left edge every other band uses.

   1080px is where the tile track is still 525px - two 250px tiles, which
   is where the terms stop wrapping badly. Below it the band is one
   column again and the tiles sit under the copy, still 2x2. A shape
   change rather than a retune.

   A MEDIA QUERY, NOT A CONTAINER QUERY, and this is the one place on the
   page where that is not a lapse: the section itself carries
   `container: mn-ctx / inline-size`, and a container query never applies
   to the element that establishes the container - only to its
   descendants. The rules for .mn-offers inside it can and do stay
   container queries; the band's own template cannot. If this band is
   ever dropped into a narrower column when it is ported, this is the
   line that has to be re-checked. */
@media (min-width:1080px){
  .mn-band--split{
    --split-gap:clamp(40px,4vw,88px);
    /* The ratio is a pair of tokens rather than two literals, so a band
       can take the template without taking this band's proportions.
       0.46 / 0.54 is what the 2a artboard drew and is the default; band
       10 sets 0.62 / 0.38 on itself, because a quote panel that takes
       the larger half stops reading as a card. The two must sum to 1. */
    --split-copy:.46;
    --split-figure:.54;
    grid-template-columns:
      [full-start] minmax(0,1fr)
      [content-start] minmax(0,calc((var(--measure) - var(--split-gap)) * var(--split-copy)))
      [copy-end] var(--split-gap)
      [figure-start] minmax(0,calc((var(--measure) - var(--split-gap)) * var(--split-figure))) [content-end]
      minmax(0,1fr) [full-end];
    align-items:start;
  }
  /* Sticky, as the artboard has it. It works here only because the band
     start-aligns its items: a stretched grid item is already as tall as
     its row and has nowhere to travel. The tile column is the taller of
     the two at every width above 1080, so the copy is what moves. */
  .mn-band--split>.mn-what__copy{
    grid-column:content-start/copy-end;
    position:sticky;
    inset-block-start:clamp(24px,4vh,48px);
  }
  .mn-band--split>.mn-offers{
    grid-column:figure-start/content-end;
    margin-block-start:0;
  }
}

/* ---------------------------------------------------------------------
   9b. Container context

   Bands whose inner layout changes shape declare containment here rather
   than each component inventing its own name. One name, different
   thresholds per component, and every one of those blocks survives being
   dropped into a narrower column when this is ported.
   --------------------------------------------------------------------- */
.mn-ctx{container:mn-ctx / inline-size;}

/* ---------------------------------------------------------------------
   10. The questions we are brought

   Replaces the twelve-sector index (2026-08-26, requested). The capability
   is the small-caps label and the decision is the display line, in that
   order of prominence, because a list of capabilities is a brochure and a
   list of decisions is a demonstration.

   Six questions in two columns, three each. One column of six left the
   right half of every hairline empty at desktop widths - the same fault
   the mandate sequence had before it took a third column - and a 44ch
   question in a 1464px band reads as an unfinished line rather than as
   deliberate air.

   The 480px floor is arithmetic, not taste: three tracks plus two gaps
   need 1600px and the content column caps at 1464, so a third column can
   never appear and the three-and-three split cannot become five-and-one.
   --------------------------------------------------------------------- */
.mn-asks{
  margin-block-start:clamp(44px,5vw,76px);
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,480px),1fr));
  column-gap:clamp(36px,4vw,80px);
  border-block-end:1px solid var(--line);
}
.mn-asks>li{
  display:grid;
  align-content:start;
  gap:clamp(9px,1vw,13px);
  padding-block:clamp(24px,2.8vw,36px);
  border-block-start:1px solid var(--line);
}
.mn-asks__tag{
  font-family:var(--ui);
  font-size:11px;font-weight:500;letter-spacing:.14em;text-transform:uppercase;
  line-height:1.4;
  color:var(--ocean);
}
/* 300, like the h1 and the h2, so a question reads at the register the
   page reserves for what it is actually saying rather than at the weight
   it reserves for labels. */
.mn-asks__q{
  /* 40ch only bites at the widest column the two-up grid can produce
     (692px at 1920), where an unchecked line would run past 42 characters.
     Below that the column is the measure and this does nothing. */
  max-inline-size:49ch;
  font-size:clamp(21px,2.1vw,30px);
  font-weight:300;
  line-height:1.3;
  letter-spacing:-.018em;
  color:var(--ink);
}

/* ---------------------------------------------------------------------
   11. Evidence

   Source on the left, what it tells us on the right - a label/value pair,
   so a <dl>. The dt and dd alternate into the two grid columns on their
   own; there is no wrapper element per pair.
   --------------------------------------------------------------------- */
.mn-evidence{
  margin-block-start:clamp(44px,5vw,76px);
  display:grid;
  grid-template-columns:minmax(0,1fr);
  border-block-start:1px solid var(--dark-rule);
}
.mn-evidence dt{
  padding-block:clamp(18px,1.9vw,24px) 0;
  font-size:clamp(19px,1.7vw,23.5px);
  font-weight:600;
  line-height:1.35;
  letter-spacing:-.015em;
  color:#FFFFFF;
}
.mn-evidence dd{
  margin:0;
  padding-block:6px clamp(18px,1.9vw,24px);
  font-size:17.5px;
  line-height:1.7;
  color:var(--on-dark);
  border-block-end:1px solid var(--dark-rule);
}
@container mn-ctx (min-width:720px){
  .mn-evidence{grid-template-columns:minmax(0,0.9fr) minmax(0,1.3fr);}
  .mn-evidence dt{
    padding-block:clamp(18px,1.9vw,24px);
    padding-inline-end:clamp(24px,3vw,56px);
    border-block-end:1px solid var(--dark-rule);
  }
  .mn-evidence dd{padding-block:clamp(18px,1.9vw,24px);}
}
/* The primary/secondary statement is the point of the whole band, so it
   is set as a rule rather than as another paragraph of lede: aqua, wider
   leading, and a hanging gradient kicker of the kind the report page uses
   to mark an editorial aside. */
.mn-evidence__rule{
  position:relative;
  margin-block-start:clamp(34px,4vw,52px);
  max-inline-size:76ch;
  padding-block-start:clamp(20px,2.2vw,28px);
  font-size:clamp(18px,1.4vw,21.5px);
  line-height:1.75;
  color:var(--on-dark-hi);
}
.mn-evidence__rule::before{
  content:"";
  position:absolute;
  inset-block-start:0;inset-inline-start:0;
  inline-size:44px;block-size:3px;border-radius:2px;
  background:linear-gradient(90deg,var(--cyan),var(--aqua));
}
.mn-coverage__note{
  margin-block-start:clamp(26px,3vw,38px);
  max-inline-size:63ch;
  font-family:var(--ui);
  font-size:11px;font-weight:500;letter-spacing:.14em;text-transform:uppercase;
  line-height:1.8;
  color:var(--on-dark-hi);
}

/* ---------------------------------------------------------------------
   11b. Standing data (2026-08-28)

   Nine proprietary datasets. Numbered 11b rather than 12 so the sections
   below keep the numbers they have had since the page was built - same
   reason 9b exists.

   THE SCALE IS THE DISPLAY LINE, THE NAME IS UNDER IT. Six of the nine
   are counts of countries and three are counts of things inside them, so
   the figures are what makes the set read as coverage rather than as a
   product list. Setting them at display weight 300 puts them in the same
   voice as the h1 and the h2 - the page's argument is that its own
   claims are set light and large, and this band is a claim.

   DOM order is the visual order: scale, then name. Nothing here is moved
   by CSS, so the reading order a screen reader gets is the one on screen
   and there is no 1.3.2 question to answer.

   THE 336px FLOOR AND THE 48px GAP CAP ARE ONE PIECE OF ARITHMETIC, and
   neither number is taste. Nine items divide cleanly into three columns
   and into one, so the layout is worth tuning to land on three as early
   as it can and never on four.

     no fourth column, ever   content caps at 1464px (1560 - 2 x 48
                              gutter) and the gap caps at 48, so four
                              tracks need 4 x 336 + 3 x 48 = 1488px.
                              24px of margin, and it cannot be closed by
                              a wider viewport because both caps are hard.
     three columns by 1152    the scaled-desktop width most laptops
                              actually report. Content there is 1082px
                              and the gap has not left its 30px floor, so
                              three tracks need 3 x 336 + 60 = 1068px.
                              This is why the gap ramps on 2.6vw rather
                              than the 3.4vw it started at: at 3.4vw the
                              gap is 39px at 1152, the row needs 1086,
                              and 1152 falls to two columns by 4px.
     two columns from 748     3 x 336 + 60 no longer fits, 2 x 336 + 30
                              still does.

   Measured on the ladder: one column to 745, two from 748 to 1137, three
   from 1145 up, and three at 1920 with 456px tracks. Between 748 and 1137
   the ninth item sits alone on a fifth row. That is accepted rather than
   patched: there is no
   CSS that spans an item only when its row is short, and the alternatives
   - a media query pinning three columns down into widths that cannot hold
   them, or dropping to one column on tablets - both cost more than a
   ragged last row does. It is also why there is no border under the list:
   with a top hairline only, an unfilled cell is invisible instead of
   sitting above a closing rule that spans nothing.
   --------------------------------------------------------------------- */
.mn-assets{
  margin-block-start:clamp(44px,5vw,76px);
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,336px),1fr));
  column-gap:clamp(30px,2.6vw,48px);
}
.mn-assets>li{
  display:grid;
  align-content:start;
  gap:clamp(6px,.8vw,11px);
  padding-block:clamp(22px,2.6vw,34px);
  /* --rule, not --line. The band's ground is --mist, and --line is only
     4% off it - the hairlines that carry the grid were disappearing. */
  border-block-start:1px solid var(--rule);
}
/* 300 and large, like the h1, the h2 and the questions band. The cap is
   on the item because `ch` resolves against the element's own font-size -
   written on the list it would measure against the page's 18px and come
   out roughly half the intended width. Same trap as .mn-decline. */
.mn-assets__scale{
  max-inline-size:19ch;
  font-size:clamp(24px,2.2vw,32px);
  font-weight:300;
  line-height:1.25;
  letter-spacing:-.018em;
  color:var(--ink);
  /* Two of the nine carry two figures each and wrap to a second line on
     purpose; lining figures keep the digits on one optical line rather
     than letting the serif's default old-style shapes hang below it. */
  font-variant-numeric:lining-nums;
}
.mn-assets__name{max-inline-size:34ch;font-size:17.5px;line-height:1.6;}

/* ---------------------------------------------------------------------
   12. People

   Three claims, one column, no label track - deliberately unlike the
   engagement modes above it and the deliverables below it, both of which
   are multi-column with a term. The claim is a full sentence at heading
   weight so the band reads as three statements rather than three cards.
   --------------------------------------------------------------------- */
.mn-people{
  margin-block-start:clamp(44px,5vw,76px);
  display:grid;
  gap:clamp(30px,3.4vw,46px);
}
.mn-people>li{
  display:grid;
  align-content:start;
  gap:10px;
  padding-inline-start:clamp(18px,2vw,28px);
  border-inline-start:2px solid var(--teal);
}
.mn-people__claim{
  font-size:clamp(21px,2vw,28px);
  font-weight:600;
  line-height:1.3;
  letter-spacing:-.018em;
  color:var(--ink);
}
.mn-people .mn-people__body{max-inline-size:54ch;font-size:17.5px;line-height:1.78;}
/* Three explicit columns, not auto-fit. With three items auto-fit passes
   through a two-then-one state on tablet widths, which reads as a
   mistake; the count has to be one or three. 1180px of container is
   where a claim at 21px still holds two lines in its third of the row. */
@container mn-ctx (min-width:1180px){
  .mn-people{grid-template-columns:repeat(3,minmax(0,1fr));gap:clamp(28px,3vw,56px);}
  .mn-people__claim{font-size:clamp(20px,1.6vw,23.5px);}
}
.mn-people__note{
  margin-block-start:clamp(34px,4vw,52px);
  font-family:var(--ui);
  font-size:11px;font-weight:500;letter-spacing:.14em;text-transform:uppercase;
  line-height:1.7;
  color:var(--ocean);
}

/* ---------------------------------------------------------------------
   13. What arrives
   --------------------------------------------------------------------- */
.mn-arrives{
  margin-block-start:clamp(44px,5vw,76px);
  display:grid;
  gap:clamp(34px,3.6vw,56px) clamp(36px,4vw,80px);
  /* 440, so four items land as 2x2 and never as three-then-one: three
     tracks plus two 80px gaps need 1480px and the content column caps at
     1464. A 320px floor did fit three, and left one item stranded. */
  grid-template-columns:repeat(auto-fit,minmax(min(100%,440px),1fr));
}
.mn-arrives>li{display:grid;align-content:start;gap:11px;}
/* A short gradient kicker rather than a rule across the top: the modes
   band already owns the 2px top rule, and this is the device the report
   page uses to open an editorial block. */
.mn-arrives__term{
  font-size:clamp(20px,1.8vw,26px);
  font-weight:600;
  line-height:1.3;
  letter-spacing:-.015em;
  color:var(--ink);
}
.mn-arrives__term::before{
  content:"";
  display:block;
  inline-size:38px;block-size:3px;border-radius:2px;
  margin-block-end:16px;
  background:linear-gradient(90deg,var(--ocean),var(--cyan));
}
.mn-arrives .mn-arrives__body{max-inline-size:51ch;font-size:17.5px;line-height:1.75;}

/* ---------------------------------------------------------------------
   14. Method: the sequence

   Every elapsed time is gone (2026-08-26, requested) - there is no
   standard timetable in this work, so the week column that used to sit
   on the left went with it. The step name takes that column instead,
   which keeps the schedule's shape without claiming a schedule.
   --------------------------------------------------------------------- */
.mn-seq{margin-block-start:clamp(44px,5vw,76px);display:grid;}
.mn-seq>li{
  display:grid;
  grid-template-columns:minmax(0,1fr);
  gap:8px;
  padding-block:clamp(22px,2.6vw,34px);
  border-block-start:1px solid var(--line);
}
.mn-seq>li:last-child{border-block-end:1px solid var(--line);}
.mn-seq__step{
  font-size:clamp(20px,1.7vw,24.5px);
  font-weight:600;line-height:1.3;letter-spacing:-.015em;
  color:var(--ink);
}
.mn-seq .mn-seq__body{max-inline-size:73ch;font-size:17.5px;line-height:1.75;}
/* Above 720px of container width the step name moves into its own column
   and the two align on their first baseline. A container query, not a
   media query, so the band survives being dropped into a narrower column
   when this is ported into the app's templates. */
@container mn-ctx (min-width:720px){
  .mn-seq>li{
    grid-template-columns:minmax(140px,22%) minmax(0,1fr);
    column-gap:clamp(24px,3vw,56px);
    align-items:baseline;
  }
}

/* ---------------------------------------------------------------------
   15. How we choose

   The quietest band on the page. No rules, no columns, no supporting
   copy under each line - three sentences at display weight and a lot of
   air. A refusal stated plainly carries further than one shouted on a
   dark ground, which is why this one is not a dark band.
   --------------------------------------------------------------------- */
.mn-decline{
  margin-block-start:clamp(40px,4.6vw,68px);
  display:grid;
  gap:clamp(22px,2.4vw,34px);
}
/* The cap sits on the item, not on the list. `ch` resolves against the
   element's own font-size, and the list inherits the page's 16px - a cap
   written there measured 300px instead of the 700 it was meant to, and
   broke every statement over three lines. */
.mn-decline>li{
  max-inline-size:54ch;
  font-size:clamp(22.5px,2.6vw,36px);
  font-weight:300;
  line-height:1.35;
  letter-spacing:-.018em;
  color:var(--ink);
}
/* No bullet, no rule, no marker of any kind. Every line opens with the
   same word, which is enough to make three sentences read as a list, and
   anything added here would be the accessory this band is meant to be
   without. */
.mn-decline__note{
  margin-block-start:clamp(36px,4vw,54px);
  padding-block-start:clamp(18px,2vw,26px);
  max-inline-size:56ch;
  border-block-start:1px solid var(--line);
  font-family:var(--ui);
  font-size:11px;font-weight:500;letter-spacing:.14em;text-transform:uppercase;
  line-height:1.8;
  color:var(--ocean);
}
/* ---------------------------------------------------------------------
   16. Global reach, single bench

   New band (2026-09-02, requested), built from slide 11 of the homepage
   deck. The slide has the claim and three supporting lines on the left
   and a grey holding box labelled "Clients testemonial" on the right;
   this is that layout, with the deck's own three quotes (slide 10) in
   the box.

   SURFACE. The band itself is --tint since 2026-09-11, --paper before
   that; either way it is a light ground and the dark is spent on the
   quote card instead, which is the one thing in the band that has to be
   looked at rather than read past. Nothing in this section was retuned
   for the change - every contrast figure below is measured against the
   card's own --ground, not against the band.

   THE ORIGINAL REASON GIVEN HERE IS NO LONGER TRUE, and is recorded
   rather than quietly deleted: this said "a fourth dark BAND on a page
   that already has three would flatten the hero and the close." That
   was written 2026-09-02, before eight bands were removed the next day
   ("Sections discontinued 2026-09-03"). The page has carried exactly
   ONE dark band since - the hero. A second was tried on the data-assets
   band 2026-09-03 and reverted the same day (see that section's comment
   on --mns-field, and NOTES.md), so the count is back to one and this
   band's argument for staying light no longer has anything to rest on
   either way - it stays light on the reason above regardless.
   Anything proposing a dark band anywhere on this page should re-count
   first rather than trusting a comment.

   THE CARD WAS REDRAWN 2026-09-03 (requested) to the client's own
   Antigravity artboard, replacing the first card and its three-stop
   switch entirely. What the artboard gives:

     - a rule that frames the card and BREAKS TWICE - a top-right L and a
       bottom-left L, so the frame is open at the top-left and the
       bottom-right rather than closed;
     - an opening and a closing quotation mark standing in the two
       breaks, on the left and the right edge;
     - the quote centred between them;
     - a footer that carries the source on the left and a previous/next
       pair on the right.

   What it does not give is its palette. The artboard is #042663 with 2px
   white strokes and translucent white buttons; here the card is
   --ground, the frame and the marks are --aqua held well back, and the
   arrows take the same treatment the page's other dark-surface controls
   do. Nothing new was added to the token set.

   NO JAVASCRIPT, still. A radio group is the native "one of these
   three": it arrows, it announces its position, and one option is always
   chosen, so the card can never end up empty. :has() reads which one.
   The arrows are the interesting part - a stateless language cannot say
   "next", so each of the three states carries its OWN pair of arrow
   labels pointing at the previous and the next radio in the ring, and
   only the pair belonging to the chosen state is displayed. Six labels,
   two visible.

   All three quotes are real markup, so they are crawlable and they all
   survive with CSS off; visibility - not opacity - keeps the two that
   are not shown out of the accessibility tree as well as out of sight.

   CONTRAST, measured on --ground #0A2A46: quote white 14.5:1, the role
   line --on-dark-hi 10.6:1, the organisation --on-dark 7.8:1, the arrow
   chevrons --on-dark-hi 10.6:1 and their rings 3.7:1 against the card,
   over the 3:1 WCAG 1.4.11 asks of a control boundary. The frame and the
   quotation marks are decoration and carry no information, so no ratio
   is claimed for them. --focus is re-pointed to --aqua on the card, the
   way .mn-band--dark does it, because the card is a dark surface sitting
   on a light band.
   --------------------------------------------------------------------- */

/* The left column's type. Was stepped down 2026-09-03 (requested:
   "decrease the text from left size, including the reduce heading
   text"), scoped to .mn-reach__copy only rather than at .mn-h2's own
   source - see the comment this replaced, and NOTES.md, "The eyebrow
   line's colour matched to the ring" for the exact history.

   THE H2 HALF OF THAT OVERRIDE IS REVERTED, 2026-09-03 (requested: apply
   the page's one .mn-h2 rule to every section, "make font size formal").
   This heading now takes the shared clamp(30px,4vw,51.5px) every other
   .mn-h2 does; the note at the time ("say the word and it either reverts
   or goes page-wide") anticipated exactly this request. The .mn-lede
   override below was not named in the request and is left as it was -
   see NOTES.md if it should go too. */
.mn-reach__copy .mn-lede{
  font-size:clamp(16px,1.15vw,18px);
  line-height:1.75;
  max-inline-size:72ch;
}

/* The three supporting lines. The deck prints an em dash before each;
   a dash used as a bullet is a drawn rule, so CSS draws it - the same
   device .mn-eyebrow's own rule uses (section 6), and no extra element. */
.mn-reach__facts{
  margin-block-start:clamp(28px,3vw,42px);
  padding-block-start:clamp(20px,2.4vw,28px);
  border-block-start:1px solid var(--rule);
  display:grid;
  gap:clamp(12px,1.4vw,18px);
}
/* The cap sits on the item, not the list: ch resolves against the
   element's own font-size, and the list inherits 18px. */
.mn-reach__facts>li{
  position:relative;
  padding-inline-start:38px;
  /* 68ch, widened from 62 when the type came down on 2026-09-03. A ch is
     relative to this element's own font-size, so the smaller size had
     already narrowed the physical measure; at 62ch the second line
     ("Primary research in 14 languages through a vetted network") broke
     onto two at 1440 while its neighbours held one, and three facts only
     read as a set while they set the same way. */
  max-inline-size:68ch;
  font-size:15.5px;
  line-height:1.72;
}
.mn-reach__facts>li::before{
  content:"";
  position:absolute;
  inset-inline-start:0;
  inset-block-start:.85em;
  inline-size:22px;
  block-size:1px;
  background:var(--ocean);
}

/* The card.

   480px is the artboard's own cap and it is the right one here: below
   the split the card would otherwise run the whole band - 958px at a
   1024 viewport - and a 958px dark rectangle is a slab, not a card. It
   is a cap rather than a breakpoint, so under 480 it is simply the
   column width and the shape glides instead of switching.

   position:relative because the frame, the two quotation marks and the
   arrow cluster are all placed against this box.
   --------------------------------------------------------------------- */
.mn-voices{
  --focus:var(--aqua);
  /* One token per axis, and a PERCENTAGE rather than a vw clamp: the
     padding has to answer to the card, and above 1080 the card's
     containing block IS its grid track, so 9% is 9% of the card. (A vw
     clamp got this wrong in exactly the place it matters - at a 600px
     viewport the card is at its 540px cap and a viewport-derived padding
     resolved to its minimum, leaving the quote 15px clear of the frame
     instead of 52.) Percentage padding resolves against the inline size
     on both axes, so the two read the same at every width.

     RAISED 2026-09-03 from 9%/36 to 12%/56 (requested: "there should be
     gap between card and message area"). The quote is not given a box of
     its own to sit in - the gap IS the padding, so the message stays a
     block of text on the card's ground rather than a panel inside a
     panel. Measured clear of the frame line, quote to rule: 26px at the
     1080 card, 34px at 1440, 38px at the 540px cap - was 15-22px. */
  --voices-pad-i:min(12%,56px);
  --voices-pad-b:min(12%,50px);
  /* Held well back on purpose: at full strength an aqua frame competes
     with the quote, which is the only thing in the card anyone is meant
     to read. */
  --voices-frame:color-mix(in srgb,var(--aqua) 42%,transparent);
  --voices-arrow-line:color-mix(in srgb,var(--on-dark) 60%,transparent);
  /* The one place on this page that does not take --r (4px), and a
     deliberate exception rather than a drift: requested 2026-09-03,
     "make edges rounded". It is a local custom property so the value
     cannot leak - the frame's own corners derive from it below, and
     nothing else on the page reads it. */
  --voices-r:18px;
  position:relative;
  /* Its own container, not mn-ctx: the card is under a third of the band
     above 1080 and the whole of it below, so nothing about the card's
     width can be inferred from the band's. Every internal size that has
     to answer to the card - the frame inset, the mark size, the quote
     ramp - is written in cqi against this. */
  container:mn-voices / inline-size;
  /* The band's rhythm again: below the split the card follows the copy
     column directly and starts a block of its own. (It was briefly the
     small gap under a heading that stood outside the card.) */
  margin-block-start:clamp(40px,5vw,72px);
  max-inline-size:540px;
  padding:var(--voices-pad-b) var(--voices-pad-i);
  background:var(--ground);
  border-radius:var(--voices-r);
  /* Two layers, not one: a short contact shadow that says the card is
     resting on the band, and a long soft one that gives it the height.
     A single 40px blur read as a smudge under a 4px radius and reads as
     nothing at all under an 18px one. */
  box-shadow:
    0 2px 6px rgba(4,11,52,.10),
    0 26px 60px rgba(4,11,52,.26);
}

/* The card's heading, 2026-09-03: moved inside the card and re-worded to
   the band's own label (requested, superseding the standalone <h3> above
   it that this replaces).

   "Use same font" is taken at its word: it IS .mn-eyebrow, the same
   component the band's label uses, rule and all - not a lookalike sized
   by hand. Only two things are re-stated here. The colour, because the
   shared rule paints --ocean for a light band and the page's own
   convention on a dark ground is --aqua (`.mn-band--dark .mn-eyebrow`
   does exactly this; the selector cannot be reused because the BAND is
   paper and only the card is dark). And the bottom margin, because the
   shared 20-34px is a band's rhythm and this one has a card's.

   9.4:1 on --ground. It is an <h3> carrying an eyebrow's styling, which
   is the right way round: heading level is an outline decision and font
   size is not one. */
.mn-voices__head{
  /* Small, because the frame's own top inset adds to it - between the
     heading and the frame's top rule there is this margin plus --fi,
     which is 15-24px on its own. */
  margin-block-end:clamp(6px,.8vw,12px);
  /* Inter, the same face the band's label uses - that part of "use same
     font" stands. What does not carry over is the label TREATMENT: 20
     characters of .14em-tracked uppercase at 24px measure 330px, and the
     narrowest card has 216px of content, so at heading size the caps and
     the tracking have to go or the title wraps at every width. Sentence
     case, tracking back to a hair under zero. */
  font-family:var(--ui);
  /* cqi against the card, so the title steps with the box it titles.
     Measured one line at EVERY width on the ladder: 18px in 216px of
     content at 320, 20.2px in 315px at 1440, 24px in 428px at the cap. */
  font-size:clamp(18px,6.4cqi,24px);
  font-weight:500;
  letter-spacing:-.008em;
  line-height:1.3;
  /* Centred to sit over the centred quote, which is the axis the card is
     drawn on. The source line and the arrows keep the footer's own
     left/right alignment - they are the card's furniture, not its
     title. */
  text-align:center;
  color:var(--aqua);
}

/* The frame, as two L-shaped boxes rather than as an SVG: a bordered box
   with two of its four border widths set is exactly an L, it takes a
   corner radius for free, and it costs no element. ::before is the
   top-right L, ::after the bottom-left one; the gaps between their ends
   are the breaks the quotation marks stand in.

   The percentages are the artboard's own path co-ordinates converted
   from its 400-unit box - its top rule starts at x=65 (16%), its right
   rule stops at y=210 (52%), its left rule starts at y=180 (45%) and its
   bottom rule stops at x=250 (62%) - then re-scaled on the inline axis
   on 2026-09-03, when the frame moved off the card and onto the message
   area below the heading. A percentage there resolves against a box two
   paddings narrower, so 17/38 became 22/46 to keep the two breaks where
   they were drawn relative to the card. Measured at a 540px card: the
   top rule starts 69px in, the bottom rule stops 148px short of the
   right edge - 70 and 158 before.

   --fi is declared on each pseudo-element rather than on the card,
   because a cqi length is resolved where it is written and the card's
   own container is the band, not itself.

   BOTTOM RULE AND CLOSE MARK MOVED OFF PERCENTAGES, 2026-09-11. The three
   short placeholder quotes these numbers were tuned against centred
   comfortably inside .mn-voices__voice's fixed-height box (margin-block:
   auto on .mn-voices__quote absorbs whatever is left over below the
   floor); the five real testimonials that replaced them run longer, and
   at a PURE percentage of that box a longer quote eats the auto-margin
   slack a shorter one left the corner and the mark sitting in, so both
   ended up drawn over the quote's own last line ("attention to detail
   were", "with our product portfolio.").

   A pure percentage cannot fix this: .mn-voices__by's own height is a
   fixed number of pixels (13px UI type, not the cqi-scaled quote), so as
   the box's own height ranges from ~215px (a narrow phone) up to the
   330-then-400px floor, by's SHARE of that box keeps changing - there is
   no single percentage that clears both the quote and the caption at
   every box height a real quote can produce. calc(50% + Npx) is: the
   percentage half tracks the box the way 49/45/56% did, the pixel half is
   the constant that the two content pieces' relative sizing failed to
   supply. Tuned against the two longest quotes (ntec, tougas) and the
   floor raise below (140cqi/400px, up from 100cqi/330px) that gives them
   room to be tuned against at all; checked at 320-1920 and the split. */
.mn-voices::before,
.mn-voices::after{
  content:"";
  position:absolute;
  border:0 solid var(--voices-frame);
  pointer-events:none;
}
.mn-voices::before{
  --fi:clamp(15px,6cqi,24px);
  inset-block:var(--fi) calc(50% - 5px);
  inset-inline:22% var(--fi);
  border-block-start-width:2px;
  border-inline-end-width:2px;
  border-start-end-radius:clamp(12px,6cqi,22px);
}
.mn-voices::after{
  --fi:clamp(15px,6cqi,24px);
  inset-block:calc(50% - 20px) var(--fi);
  inset-inline:var(--fi) 46%;
  border-block-end-width:2px;
  border-inline-start-width:2px;
  border-end-start-radius:clamp(12px,6cqi,22px);
}

/* The two quotation marks, sitting astride the frame line in its breaks.
   Decorative, so aria-hidden in the markup; tinted here rather than in
   the file, so the pair moves with the token. */
.mn-voices__mark{
  position:absolute;
  inline-size:clamp(21px,8cqi,30px);
  fill:color-mix(in srgb,var(--aqua) 55%,transparent);
  pointer-events:none;
}
.mn-voices__mark--open{
  inset-inline-start:clamp(4px,2cqi,9px);
  inset-block-start:33%;
}
/* calc(50% + 38px), not 56% - see the retune note on the frame above:
   the fixed half of the sum is what clears the longest quotes' own last
   line on one side and .mn-voices__by's role/company lines on the other,
   at every box height between the narrowest phone and the split. */
.mn-voices__mark--close{
  inset-inline-end:clamp(4px,2cqi,9px);
  inset-block-start:calc(50% + 38px);
}

/* A column, so the quote can take the free space between the top of the
   card and the source line and centre itself in it. In the no-:has()
   fallback there is no free space, the auto margins resolve to zero, and
   the caption simply follows the quote.

   The floor keeps the card a card when the chosen quote is the short
   one: without it the box collapses to the height of two lines of text
   and the frame stops reading as a frame. It is written in cqi so it
   tracks the card's own width and is capped so the widest card does not
   turn into a square of empty navy.

   NOTE the cqi basis: container query units resolve against the
   container's CONTENT box, so at the 480px card they measure 408, not
   480. Every cqi ramp in this card - this floor, the quote's ramp, the
   frame inset, the mark size - was tuned against that, which is why the
   percentages read higher than a border-box reading would suggest. */
.mn-voices__voice{
  display:flex;
  flex-direction:column;
  /* RAISED with the type step-down of 2026-09-03, from 78cqi/320. A
     smaller quote sets in fewer lines, and left alone the floor would
     have taken about 80px off a card the client had just asked to make
     bigger. The floor now carries the height the type used to, so the
     card holds its size and the quote sits in more air.

     RAISED AGAIN, 100cqi/330px to 140cqi/400px, 2026-09-11 (the five real
     testimonials replacing the three placeholders - see the frame/mark
     retune above). The old cap only reached its own 330px once the card
     itself was wide enough for 100% of its width to equal 330px, which
     left a band of narrower widths where the floor was smaller than 330
     - and the close mark's calc(50% + 38px) needs the WHOLE 330-400px
     range to actually be reached, not approached, to clear the longest
     quotes. 140cqi reaches the (also raised) 400px cap by ~286px of card
     content width, i.e. by very early in the range this card ever runs
     at, instead of drifting up to it past 1000px of viewport. */
  min-block-size:min(140cqi,400px);
}
.mn-voices__quote{
  margin-block:auto;
  /* Clear of the quotation marks at every card width. */
  padding-inline:min(4cqi,18px);
  text-align:center;
  /* cqi, not vw: the card is under a third of the band above 1080 and
     the full band below it, so its width does not track the viewport's.
     The quote answers to the box it is set in.

     Stepped down 2026-09-03 (requested) from 19/8cqi/28. The card was
     NOT allowed to shrink with it - see the floor on .mn-voices__voice,
     which was raised in the same move so the box the client asked to
     enlarge keeps its size and the smaller quote sits in more air.

     STEPPED DOWN AGAIN, 17/7cqi/24 to 14/5.2cqi/18, 2026-09-11. The three
     placeholder quotes this card was built for set in 2-3 lines; the five
     real testimonials that replaced them run to 20-25 words and, at the
     old size, four of the five set in 4 lines - long enough to run the
     quote into the close mark and the frame's own corner (see above).
     Fewer, smaller-set characters per line was the more direct fix than
     retuning the frame around a taller quote: at this size every voice
     sets in 2-3 lines at every card width, which is what the frame was
     actually tuned against. Still comfortably above body-text floors -
     this is a decorative pull-quote face, not running copy. */
  font-size:clamp(14px,5.2cqi,18px);
  /* Real italic, not a slant. Source Serif 4's italic is a separate
     design - it is the only italic on this page, and the font request in
     the <head> of both files now carries the `ital` axis for it. Without
     that axis the browser synthesises an oblique by shearing the roman,
     which on a serif at 24px is plainly wrong. Requested 2026-09-03.

     line-height is up from 1.5: an italic serif's ascenders and
     descenders reach further, and at 1.5 the closing quotation mark on
     the last line was touching the line above it. */
  font-style:italic;
  font-weight:300;
  line-height:1.58;
  letter-spacing:-.006em;
  color:#FFFFFF;
}
.mn-voices__by{
  padding-block-start:clamp(14px,1.6vw,20px);
  font-family:var(--ui);
  font-size:13px;
  font-weight:600;
  letter-spacing:.02em;
  line-height:1.55;
  color:var(--on-dark-hi);
}
/* <cite> is the right element for the source of a quotation, and its UA
   default is italic, which this page does not use for names. */
.mn-voices__org{
  display:block;
  font-style:normal;
  font-weight:400;
  color:var(--on-dark);
}

/* The control. Hidden until :has() proves it can do something - see the
   @supports block below. With no :has() all three quotes show, stacked,
   and an arrow that changes nothing is worse than no arrow.

   Bootstrap's reboot floats the legend, sets it at 1.5rem and gives the
   fieldset its own padding and min-width, so every value this group
   depends on is stated here rather than inherited from whichever legacy
   sheet loads. */
.mn-voices__pick{
  display:none;
  gap:10px;
  align-items:center;
  min-inline-size:0;
  margin:0;
  padding:0;
  border:0;
}
/* 40px, over the 24 WCAG 2.2 asks of a target and inside the space a
   340px card can spare beside the source line. Raised from 38 with the
   card, so the pair keeps its weight against a bigger quote. */
.mn-voices__arrow{
  display:none;
  place-items:center;
  inline-size:40px;
  block-size:40px;
  border:1.5px solid var(--voices-arrow-line);
  border-radius:50%;
  color:var(--on-dark-hi);
  cursor:pointer;
}
/* A square with two adjacent borders, turned - the chevron the artboard
   draws as an SVG path, without the path or the element to hang it on.
   No optical nudge: rotation is about the box's own centre, so the
   turned square's bounding box stays centred in the circle. */
.mn-voices__arrow::before{
  content:"";
  inline-size:8px;
  block-size:8px;
  border-block-start:2px solid currentcolor;
  border-inline-start:2px solid currentcolor;
}
.mn-voices__arrow--prev::before{rotate:-45deg;}
.mn-voices__arrow--next::before{rotate:135deg;}
@media (hover:hover){
  .mn-voices__arrow:hover{
    border-color:var(--aqua);
    background:color-mix(in srgb,var(--aqua) 14%,transparent);
    color:#FFFFFF;
  }
}
.mn-voices__arrow:active{scale:.94;}

/* The radios are visually hidden, so the focus ring is drawn on the
   thing the reader can actually see standing for the group: the arrow
   pair. Arrowing through a radio group moves the checked state with the
   focus, so the pair under the ring is always the pair that belongs to
   the voice on screen. */
.mn-voices:has(.mn-voices__radio:focus-visible) .mn-voices__pick{
  outline:2px solid var(--focus);
  outline-offset:6px;
  border-radius:999px;
}

/* Air between quotes, and ONLY where they are actually stacked. As a
   plain adjacent-sibling rule this fired on the visible quote whenever
   the one before it was hidden, and the card grew depending on which
   voice was chosen. */
@supports not selector(:has(*)){
  .mn-voices__voice+.mn-voices__voice{margin-block-start:clamp(26px,3vw,40px);}
  /* The marks stand at 33% and 56% of the card because the card holds
     ONE quote. Around a stack of three they float in the middle of the
     second one, which reads as a mistake rather than as decoration. The
     frame still draws - it frames whatever the card contains. */
  .mn-voices__mark{display:none;}
}

/* The whole switch, and only the switch, is gated on :has(). Without it
   the browser gets three stacked quotes and no arrows, which reads and
   crawls; with it, one quote and a working control. */
@supports selector(:has(*)){
  /* Three rows: the heading, the card body, and the footer the arrows
     sit in. All three quotes stay IN FLOW, stacked in the one area and
     spanning the lower two,
     so the area is exactly as tall as the tallest quote at that width
     and nothing moves on selection. A reserved min-block-size was the
     first attempt on the old card and it cannot be right at more than
     one width.

     visibility, not opacity: an opacity-0 quote stays in the
     accessibility tree and a screen reader would read all three. */
  .mn-voices{
    display:grid;
    grid-template-rows:auto 1fr auto;
  }
  .mn-voices__head{grid-row:1;}
  /* THE FRAME AND THE MARKS ARE PLACED AGAINST THE MESSAGE AREA, not
     against the card (2026-09-03: "shift it outside the message area but
     within the card"). An absolutely positioned child of a grid
     container that has a definite grid position takes THAT GRID AREA as
     its containing block, not the padding box - so giving the two
     pseudo-elements and the two marks `grid-row: 2 / -1` moves every
     inset and every percentage in them off the card and onto the rows
     the quote and the footer occupy. The heading is then outside the
     frame by construction, and stays outside it when it wraps to two
     lines at 320 - no offset to keep in sync with the heading's height.

     Measured at 1440: the heading's baseline is 24px above the frame's
     top rule; at 320, where the heading takes two lines, it is the same
     24px. Outside the @supports guard the card is not a grid, the
     placement is ignored, and the frame falls back to the card box,
     which is the right degradation for decoration. */
  .mn-voices::before,
  .mn-voices::after,
  .mn-voices__mark{
    grid-row:2/-1;
    /* 1 / -1, not 1. For an ABSOLUTELY POSITIONED grid child an `auto`
       grid line resolves to the grid container's PADDING edge, not to a
       track edge - so `grid-column: 1` gave a containing block running
       from the content-box left to the padding-box right, one padding
       wider than the area on one side only, and the frame and the
       closing mark hung 47px past the card. Both lines stated. */
    grid-column:1/-1;
    /* The grid area is the card's CONTENT box, and the frame belongs on
       the card's own edges - so the block is pulled back out over the
       inline padding with a negative margin. Only the block axis is
       meant to move; the inline axis must stay where it was. */
    margin-inline:calc(var(--voices-pad-i) * -1);
  }
  /* Same on the block-end, so the frame still sits --fi above the card's
     bottom edge rather than --fi above the content box's. The block
     START is deliberately left alone: that is the inset that now
     measures from under the heading instead of from the card's top. */
  .mn-voices::after{margin-block-end:calc(var(--voices-pad-b) * -1);}
  .mn-voices__voice{
    grid-row:2/-1;
    grid-column:1;
    visibility:hidden;
    opacity:0;
  }
  .mn-voices:has([data-voice="east-island"]:checked) .mn-voices__voice[data-voice="east-island"],
  .mn-voices:has([data-voice="ey"]:checked) .mn-voices__voice[data-voice="ey"],
  .mn-voices:has([data-voice="ntec"]:checked) .mn-voices__voice[data-voice="ntec"],
  .mn-voices:has([data-voice="tougas"]:checked) .mn-voices__voice[data-voice="tougas"],
  .mn-voices:has([data-voice="fayn"]:checked) .mn-voices__voice[data-voice="fayn"]{
    visibility:visible;
    opacity:1;
  }
  /* The arrows share the footer row with the source line rather than
     sitting under it: the source line belongs to the quote and travels
     with it, the arrows do not, so they cannot be siblings. Both are
     bottom-aligned to the same edge and the source line reserves the
     width of the pair so the two never meet. */
  .mn-voices__pick{
    display:flex;
    grid-row:3;
    grid-column:1;
    place-self:end;
  }
  .mn-voices__by{padding-inline-end:100px;}
  .mn-voices:has([data-voice="east-island"]:checked) .mn-voices__arrow[data-from="east-island"],
  .mn-voices:has([data-voice="ey"]:checked) .mn-voices__arrow[data-from="ey"],
  .mn-voices:has([data-voice="ntec"]:checked) .mn-voices__arrow[data-from="ntec"],
  .mn-voices:has([data-voice="tougas"]:checked) .mn-voices__arrow[data-from="tougas"],
  .mn-voices:has([data-voice="fayn"]:checked) .mn-voices__arrow[data-from="fayn"]{
    display:grid;
  }
}

/* On a phone the arrows take their own row under the source line.

   THE QUERY IS ON THE BAND, NOT ON THE CARD, and that is the whole
   point. Keyed to the card, the threshold that catches a 320px viewport
   (216px of card content) sits within a few pixels of the narrowest
   DESKTOP card (258px at 1080), so the rule fires on one layout while
   aiming at the other and any later nudge to the padding moves it across
   the line. Keyed to the band, the two cases separate cleanly and the
   nearest thing on the other side of the threshold is the split itself
   at 1080.

   NOTE what mn-ctx actually measures: .mn-reach runs full bleed, so its
   inline size is the VIEWPORT's, not --measure's. 380px here is a 380px
   viewport - so 320 and 360 stack and 390 up does not. The container
   form is still the right one: drop the band into a narrower column when
   this is ported and the rule follows the column, which a media query
   would not.

   It survives porting, too: drop the band into a narrow column and the
   rule fires, which is what it is for.

   Without it the source line is left 116px of measure at 320 and sets
   "European specialty chemicals group" in six two-word lines beside the
   arrows. With it the line takes the full card and sets in two. */
@container mn-ctx (max-width:380px){
  /* The quote stops spanning the footer row, so the row becomes the
     arrows' own rather than something they are laid over. */
  .mn-voices__voice{grid-row:2;min-block-size:400px;}
  .mn-voices__by{padding-inline-end:0;}
  .mn-voices__pick{place-self:start;padding-block-start:18px;}

  /* Frame and marks still span grid-row:2/-1 (unchanged - the split above
     1080 still wants that combined reach), but .mn-voices__voice just
     above dropped to row 2 alone, so their containing block below this
     width is row 2 PLUS row 3 (the arrows, now their own row) - a taller
     box than voice's own, by exactly .mn-voices__pick's height (58px:
     18px padding-block-start + the 40px arrow). calc(50% + Npx) on the
     base rule above answers to voice's own height only, so it is the
     wrong shape here; these three restate it against the wider box.

     min-block-size is restated too, at the SAME 400px the base rule
     caps at, because 140cqi does not reach 400px on its own until the
     card is wider than this query allows - at 320px wide the card's
     content box is ~216px, and 140% of that is ~302px, well short. The
     longest quotes still need the full 400px to have anywhere free to
     put the mark once the arrows' row is added to what it shares.
     Checked at 320-380 with all five voices. */
  .mn-voices::before{inset-block:var(--fi) 56%;}
  .mn-voices::after{inset-block:54% var(--fi);}
  .mn-voices__mark--close{inset-block-start:59%;}
}

@media (prefers-reduced-motion:no-preference){
  .mn-voices__arrow{transition:border-color .2s ease,background-color .2s ease,color .2s ease,scale .12s ease;}
  /* One moment, on the thing that changed - and only on the INCOMING
     quote. The three sit in one area, so a symmetrical crossfade
     superimposes two blocks of text for the length of it, which reads as
     a rendering fault rather than as a transition. Carrying the
     transition on the chosen figure alone makes the outgoing quote go at
     once (its own rule declares none) and the new one fade up in its
     place. */
  .mn-voices:has([data-voice="east-island"]:checked) .mn-voices__voice[data-voice="east-island"],
  .mn-voices:has([data-voice="ey"]:checked) .mn-voices__voice[data-voice="ey"],
  .mn-voices:has([data-voice="ntec"]:checked) .mn-voices__voice[data-voice="ntec"],
  .mn-voices:has([data-voice="tougas"]:checked) .mn-voices__voice[data-voice="tougas"],
  .mn-voices:has([data-voice="fayn"]:checked) .mn-voices__voice[data-voice="fayn"]{
    transition:opacity .2s ease;
  }
}

/* The split. .mn-band--split carries the template; this band restates it
   with a card track that is a length rather than a share of the measure.

   THE CARD IS ABOUT A THIRD OF THE BAND. Set at 25-30% earlier on
   2026-09-03, then asked to grow ("increase the size of the card") later
   the same day; 32% floored at 320 and capped at 460 is the smallest
   move that reads as bigger while staying within a point or two of the
   share that was signed off. It is still a long way from the artboard's
   0.54 and from the 0.38 of 2026-09-02.

   A flat share alone cannot do it: 32% of the measure is 311px at a 1080
   viewport, which sets the quote in lines of eleven or twelve
   characters, and it is 440px at 2560, which starts to stop being a
   card. So the track is a LENGTH, and the copy column takes exactly what
   is left - the two still sum to --measure, which is what keeps
   content-start and content-end on the page's own left and right edges.

   Measured: 320px at 1080 (31.5% of the measure), 415px at 1440 (30.6%),
   444px at 1920 (30.3%) and 440px at 2560 (30.1%).

   The card does NOT take the sticky treatment .mn-what__copy has: there
   the tile column is the taller of the two at every width, and here the
   card is much the shorter, so there would be nothing to travel
   against. .mn-band--split already start-aligns its items, which is what
   keeps the card its own height rather than the height of the row. */
@media (min-width:1080px){
  .mn-reach{
    --voices-track:clamp(340px,calc((var(--measure) - var(--split-gap)) * .32),460px);
    grid-template-columns:
      [full-start] minmax(0,1fr)
      [content-start] minmax(0,calc(var(--measure) - var(--split-gap) - var(--voices-track)))
      [copy-end] var(--split-gap)
      [figure-start] var(--voices-track) [content-end]
      minmax(0,1fr) [full-end];
  }
  /* Back to two children and one row. The card's heading briefly lived
     out here as a third grid item, which needed `grid-template-rows:
     auto 1fr` and a spanning copy column to stop the taller column
     inflating row 1 and pushing the card below its own heading. The
     heading is inside the card now, so all of that comes out. */
  .mn-band--split>.mn-reach__copy{grid-column:content-start/copy-end;}
  .mn-band--split>.mn-voices{
    grid-column:figure-start/content-end;
    margin-block-start:0;
  }
}

/* ---------------------------------------------------------------------
   17. Clients
   --------------------------------------------------------------------- */
/* The column count has to divide eight or the last logo is orphaned on a
   row of its own - auto-fit gave seven then one at 1280, which reads as a
   mistake rather than a wrap. So the counts are stated: 2, then 4, then
   8 across one row once the cells reach the 126px the images cap at.
   These are shape changes, which is what a media query is for. */
.mn-clients__row{
  display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(28px,3.4vw,54px);
  align-items:center;
  margin-block-start:clamp(48px,6vw,80px);
}
@media (min-width:560px){
  .mn-clients__row{grid-template-columns:repeat(4,minmax(0,1fr));}
}
@media (min-width:1440px){
  .mn-clients__row{grid-template-columns:repeat(8,minmax(0,1fr));}
}
.mn-clients__row li{display:flex;align-items:center;justify-content:center;}
.mn-clients__row img{inline-size:100%;max-inline-size:126px;block-size:auto;}

/* ---------------------------------------------------------------------
   17b. Recently published (2026-09-03)

   Numbered 17b, not 18, so Closing and Utilities below keep the numbers
   they already had — same convention "11b. Standing data" set.

   Built against the client's own Antigravity reference,
   ~/Downloads/Laravel Report List Cards Section (Pure HTML & CSS).html,
   staged first in report-list-section/ (see that folder's NOTES.md for
   the full history, including a real writing-mode bug caught there).
   ON THIS PAGE ONLY, by request — index.html and index-textured.html do
   not carry this band or these rules exercise nothing on them.

   Six reports, one <a> per card (title, month/year, and the industry
   name set vertically on the right edge — the reference's one
   identifying device, kept). Forced to three columns at desktop via a
   container query rather than an auto-fit floor, so the grid reads as
   3-over-3: six divides cleanly by 2 and by 3, so unlike the three-item
   case .mn-people documents, the two-column middle step is a normal
   rhythm rather than an orphan to design around.

   THE CARD'S GROUND IS THE HERO'S, verbatim (requested, after --ground
   was tried first and the hero was liked better next to it) — same base
   colour, same two radial blooms, same diagonal ramp as .mn-hero above.
   Repeated here rather than pulled into a shared token, because .mn-hero
   does not expose one either; each card resolves the two blooms against
   its own ~427x220px box, so every card carries the full recipe at card
   scale instead of one wash stretched across all six.

   The vertical tag is --aqua: the logistics of "which logo colour goes
   on --ground" are already answered everywhere else on the page
   (dark-band eyebrows, the quote card's frame, dark-band focus rings),
   and this reuses that answer rather than inventing a new one.

   Radius is a LOCAL --reports-r:12px, kept from the reference as a
   deliberate exception to the page's single --r (4px) - the same kind
   of exception --voices-r already is on the quote card.

   Title is Source Serif 4, not the reference's all-Inter - the same
   face every other card-like heading on the page uses (.mn-arrives__term,
   .mn-people__claim). Inter stays on the two uppercase micro-labels
   (date, industry), both at 600 rather than the reference's 700: this
   page's Google Fonts link only requests Inter 400..600, and two small
   labels are not reason enough to add a heavier weight file to the
   page's font budget. */
.mn-reports{
  margin-block-start:clamp(44px,5vw,76px);
  display:grid;
  grid-template-columns:minmax(0,1fr);
  gap:clamp(20px,2.2vw,28px);
}
@container mn-ctx (min-width:620px){
  .mn-reports{grid-template-columns:repeat(2,minmax(0,1fr));}
}
@container mn-ctx (min-width:1000px){
  .mn-reports{grid-template-columns:repeat(3,minmax(0,1fr));}
}

/* One <a> per card - title, date and the vertical tag are all inside
   it, so the whole card is one link, one tab stop, and the page's own
   :focus-visible ring (section 4) draws around the card's own border
   box with no relocation needed. */
.mn-reports__card{
  --reports-r:12px;
  position:relative;
  display:flex;
  flex-direction:column;
  justify-content:space-between;
  gap:20px;
  /* 220px in the design handoff, where the industry tag was one-word
     sample copy. Raised 2026-09-11, when the tag became the real category
     name: the tag runs VERTICALLY up this card, and the longest category,
     "Buildings, Construction, Metals & Mining" (177 live reports, so it
     reaches this band regularly), needs a 205px run to break into two
     lines — its best word break is "Buildings, Construction," at 204.4px
     and "Metals & Mining" at 127.1px, both measured in the browser. Below
     that it takes three lines; there is no 2-line split narrower than 205.
     254 = that 206px cap + the tag's own 20px bottom offset + this card's
     28px top padding, so the tag ends exactly on the padding edge. */
  min-block-size:254px;
  padding-block:clamp(24px,2.2vw,28px) clamp(20px,2vw,24px);
  padding-inline:clamp(24px,2.2vw,28px) clamp(20px,2vw,24px);
  border-radius:var(--reports-r);
  background-color:#0C3355;
  background-image:
    radial-gradient(115% 135% at 90% 6%,rgba(23,150,172,.40) 0%,rgba(12,51,85,0) 62%),
    radial-gradient(100% 125% at 2% 98%,rgba(9,66,102,.50) 0%,rgba(12,51,85,0) 58%),
    linear-gradient(118deg,#0B2E4F 0%,#103C60 44%,#145B79 76%,#187E92 100%);
  box-shadow:0 10px 22px -5px rgba(4,38,99,.32);
  transition:transform .2s cubic-bezier(.22,.75,.3,1),box-shadow .2s ease;
  overflow:hidden;
}
.mn-reports__card:hover,
.mn-reports__card:focus-visible{
  transform:translateY(-4px);
  box-shadow:0 18px 34px -6px rgba(4,38,99,.45);
}

.mn-reports__title{
  padding-inline-end:34px;
  font-family:var(--text);
  font-size:clamp(17px,1.35vw,19px);
  font-weight:600;
  line-height:1.42;
  letter-spacing:-.008em;
  color:#FFFFFF;
}

.mn-reports__date{
  font-family:var(--ui);
  font-size:12px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;
  line-height:1.4;
  color:var(--on-dark-hi);
}

/* writing-mode, not rotated horizontal text, so the string still
   selects and reads correctly, not just looks right.

   PHYSICAL right/bottom, against the page's own "logical properties
   throughout" rule: inset-inline-end / inset-block-end resolve against
   the WRITING MODE OF THE ELEMENT THEY ARE SET ON. Once vertical-rl
   applies below, its inline axis runs top-to-bottom and its block axis
   runs right-to-left, so the logical forms silently point at the wrong
   physical edges - caught in report-list-section/ before this landed
   here. Physical keywords say what is actually meant. */
.mn-reports__industry{
  position:absolute;
  right:18px;
  bottom:20px;
  writing-mode:vertical-rl;
  transform:rotate(180deg);
  font-family:var(--ui);
  font-size:11.5px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;
  line-height:1.3;
  color:var(--aqua);

  /* THE TAG WRAPS RATHER THAN OVERRUNNING THE CARD (2026-09-11). Once the
     industry name is real data instead of one-word sample copy it can be
     long — "Buildings, Construction, Metals & Mining" measures 335.8px of
     vertical run — and the card is overflow:hidden, so without a cap the
     tail is silently clipped with nothing to show it happened.

     `max-inline-size`, NOT max-block-size: this element is vertical-rl, so
     its INLINE axis runs top to bottom and its block axis runs across. The
     cap is what makes it wrap; the block size is what grows per line.

     206px is derived, not chosen: it is the narrowest cap at which the
     longest category still breaks into TWO lines (see the min-block-size
     note on .mn-reports__card). Every other category measures 165.1px or
     less — "Aerospace & Defense" is the runner-up — so all eleven of them
     stay on one line at this cap. Re-measure both numbers if the font,
     the size or the tracking here ever changes. */
  max-inline-size:206px;
}

/* Two lines of vertical text are ~30px wide against one line's ~15px, so
   the gutter the title reserves has to grow with it or the two collide.
   The modifier is on the CARD, not the tag, because the rule it drives
   applies to a sibling; HomeController decides which cards carry it, from
   the length of the category name. */
.mn-reports__card--industry-2 .mn-reports__title{padding-inline-end:52px;}

/* MOVED TO THE RIGHT, 2026-09-11 (requested). justify-self, not a margin
   and not a text-align - the same call .mn-hero__cta and .mn-stats__grid
   make, for the same reason: .mn-band is display:grid at every width and
   this anchor is a direct child, so it IS a grid item and `end` lands its
   edge on the band's right measure edge, the one the header, the footer
   and the card grid above it already share.

   It also FIXES A STRETCH that was there before. inline-flex blockifies
   to flex on a grid item, and with justify-self at its `stretch` default
   the anchor was filling the whole content column (571px of box behind
   259px of link at a 612px viewport) - which meant the hover underline,
   drawn with border-block-end, ran the full width of the band rather
   than the width of the words. `end` sizes the box to fit-content, so
   the rule now underlines the link itself. */
.mn-reports__all{
  display:inline-flex;
  justify-self:end;
  align-items:center;
  gap:8px;
  margin-block-start:clamp(30px,3.2vw,44px);
  font-family:var(--ui);
  font-size:15px;font-weight:600;
  color:var(--ocean);
  border-block-end:1px solid transparent;
  transition:border-color .2s ease;
}
.mn-reports__all:hover{border-block-end-color:currentColor;}
.mn-reports__all svg{
  inline-size:16px;block-size:16px;flex:none;
  fill:none;stroke:currentColor;stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;
  transition:transform .2s ease;
}
.mn-reports__all:hover svg{transform:translateX(3px);}

/* ---------------------------------------------------------------------
   18. Closing
   --------------------------------------------------------------------- */
/* The closing band takes the hero's radial lighting, mirrored: the hero's
   cyan sits top-right, this one's bottom-left. It makes the page's two
   navy bands read as a deliberate pair rather than as the same slab used
   twice, and it separates the band from the footer's brighter #0A2E76,
   which otherwise runs straight into it. */
.mn-band--last{
  background-image:
    radial-gradient(120% 140% at 6% 96%,rgba(23,150,172,.24) 0%,rgba(10,42,70,0) 58%),
    radial-gradient(90% 130% at 100% 0%,rgba(9,66,102,.55) 0%,rgba(10,42,70,0) 62%);
}
.mn-close__h{
  font-weight:300;
  font-size:clamp(33.5px,5.15vw,69.5px);
  line-height:1.1;letter-spacing:-.026em;
  color:#FFFFFF;
  max-inline-size:20ch;
}
.mn-close__actions{
  display:flex;flex-wrap:wrap;align-items:center;
  gap:clamp(18px,2.4vw,40px);
  margin-block-start:clamp(34px,4vw,50px);
}
.mn-close__direct{
  display:flex;flex-wrap:wrap;gap:6px clamp(16px,2vw,28px);
  font-family:var(--ui);
  font-size:12.5px;letter-spacing:.06em;
  line-height:1.6;
  color:var(--on-dark-hi);
}
/* Underline rather than a border, and padding to a 28px box: these are
   the page's two smallest targets and WCAG 2.5.8 asks 24x24 of anything
   that is not a link inside a sentence. */
.mn-close__direct a{
  display:inline-block;
  padding-block:4px;
  text-decoration:underline;
  text-decoration-color:rgba(175,192,212,.42);
  text-underline-offset:5px;
}
.mn-close__direct a:hover{color:var(--aqua);text-decoration-color:var(--aqua);}

/* ---------------------------------------------------------------------
   19. Utilities
   --------------------------------------------------------------------- */
.mn-vh{
  position:absolute;
  inline-size:1px;block-size:1px;
  padding:0;margin:-1px;
  overflow:hidden;clip-path:inset(50%);
  white-space:nowrap;border:0;
}
.mn-skip{
  position:absolute;inset-inline-start:-9999px;inset-block-start:0;
  z-index:100;
  padding:12px 20px;
  background:#040B34;color:#FFFFFF;
  /* The stack is restated rather than var(--text) on purpose: the skip
     link is the first focusable element and can be used before the
     webfont has swapped in, so it must not depend on a token whose value
     is a font that may not have arrived. Kept in sync by hand. */
  font-family:'Source Serif 4',Georgia,'Times New Roman',serif;
  font-size:17px;font-weight:600;text-decoration:none;
  border-radius:0 0 4px;
}
.mn-skip:focus{inset-inline-start:0;}
/* The skip link sits outside .mn-home, so the scoped focus ring does not
   reach it and it was falling back to the UA default. Teal rather than
   aqua because this ring has the link's navy on its inside edge and
   whatever the page shows on its outside; teal is the one value that
   clears 3:1 against both. */
.mn-skip:focus-visible{outline:3px solid #0B8FA3;outline-offset:2px;}

/* ---------------------------------------------------------------------
   20. Practice rail

   Incorporated 2026-09-03 (requested) from section/index.html - see that
   folder's NOTES.md for the full account of the band's own history
   (rebuilt from a Gemini Antigravity draft; markup, narrow layout and
   JavaScript rewritten 2026-09-01; type and colour merged onto this
   page's system 2026-09-02).

   FOLDED IN, NOT FORKED: this used to be section/assets/css/section.css,
   a second stylesheet for a standalone staging page. That page is gone;
   these are the same rules, in this file, with the parts that existed
   only to make the band reviewable on its own removed:

   - The card chrome - border, radius, shadow, background, max-width and
     44px padding on .mn-practice itself - is gone. The section is now
     .mn-band like every other one on the page: full-bleed, sharing
     --measure, the gutter and the spine, padding-block:var(--pad) doing
     the vertical rhythm no band authors for itself. This is section/
     NOTES.md's open point 4, and it settles by removal rather than by
     reconciliation - there is no longer a card to reconcile.
   - The scoped box-sizing:border-box rule is gone. .mn-home already
     declares it for every descendant (section 4 above); it was only
     needed standalone.
   - APPENDED LAST, deliberately, not folded in near section 9 ("What we
     do") where it sits in the page. .mn-home :focus-visible (section 4)
     and .mn-practice__summary:focus-visible both land at (0,2,0)
     specificity, so source order decides, and section/NOTES.md already
     flagged that this band's ring must win. Last in the file guarantees
     it without depending on where a future edit inserts a section
     between them.

   RADIUS IS THE BAND'S OWN, 4 / 8 / 14px, KEPT ON REQUEST, 2026-09-03 -
   NOT flattened to this page's --r:4px. The explicit go-ahead colour got
   on 2026-09-02 (see the --mnp-* token block) now covers radius too.
   --mnp-r-lg (14px) is dropped rather than kept unused: it was only ever
   the card's own corner, and the card is gone with it.

   THE INTRO KEEPS ITS OWN TWO-COLUMN GRID - title above eyebrow in one
   column, the lede beside them in a fixed 380px column, bottom-aligned -
   on one wrapper, .mn-practice__intro. Same justification as
   .mn-what__copy in section 9: the template addresses a single box, and
   the eyebrow/title pair is two children of it. Everything below the
   wrapper - the rail, the four brief dialogs - is unchanged from
   section/section.css.

   THE EYEBROW TEXT LOST ITS "02 — " PREFIX, kept everything else. The
   number was the band's index in its own standalone preview; this page
   runs no such scheme (only band 1's eyebrow, "01 — What we do", carries
   a number, and no other band does). The eyebrow still repeats the <h2>
   almost word for word - section/NOTES.md flagged this as a real copy
   defect the day the copy was written - and that is NOT fixed here: the
   <h2> is protected copy and the eyebrow's wording is the client's call,
   same reasoning NOTES.md already gave it.

   STILL OPEN, carried over from section/NOTES.md and not decided here:
   whether these four practices are the right four commercially: the
   brief CTAs' contact-us destination; the source photographs' resolution
   (1.5-4x upscaled at the rail's own sizes); the panel CTA's 37px
   height, under this project's 44px primary-action target but not this
   band's primary action; the dialog not locking background scroll. See
   NOTES.md, "Practice rail incorporated".
   --------------------------------------------------------------------- */
.mn-practice{
  /* Colour, 2026-09-02, requested, EVERY TOKEN DEFERS TO THE PAGE'S OWN
     FIRST. Now that the band lives inside .mn-home rather than being
     reviewed standalone, that fallback after the comma is dead weight
     kept for documentation - .mn-home always defines the left side. */
  --mnp-page:var(--paper,#fff);
  --mnp-card:var(--paper,#fff);
  --mnp-fill:var(--mist,#f1f5f8);
  /* --ground, NOT --ink - see section 3's note on the two staying apart. */
  --mnp-dark:var(--ground,#0a2a46);
  --mnp-ink:var(--ink,#040b34);
  --mnp-ink-2:var(--slate,#41505e);
  --mnp-ink-3:var(--ocean,#004e79);
  --mnp-on-dark:var(--on-dark,#afc0d4);
  --mnp-on-dark-hi:var(--on-dark-hi,#cfdeed);
  /* Two accents, one per surface, no third - --amber stays out on
     purpose, it is the report page's commerce colour. */
  --mnp-accent:var(--ocean,#004e79);
  --mnp-accent-dark:var(--aqua,#6fe0ee);
  --mnp-line:var(--line,#dbe4eb);
  --mnp-line-dark:var(--dark-rule,rgb(175 192 212 / 20%));
  --mnp-scrim:linear-gradient(
    180deg,
    color-mix(in srgb,var(--mnp-dark) 15%,transparent) 0%,
    color-mix(in srgb,var(--mnp-dark) 94%,transparent) 85%
  );
  --mnp-panel-line-open:color-mix(in srgb,var(--mnp-accent-dark) 38%,transparent);
  --mnp-panel-line-hover:color-mix(in srgb,var(--mnp-on-dark) 25%,transparent);
  --mnp-tag-line:color-mix(in srgb,var(--mnp-accent-dark) 30%,transparent);
  --mnp-tag-fill:color-mix(in srgb,var(--mnp-accent-dark) 16%,transparent);
  --mnp-chip-line:color-mix(in srgb,var(--mnp-on-dark) 15%,transparent);
  --mnp-chip-fill:color-mix(in srgb,var(--mnp-on-dark) 8%,transparent);
  --mnp-backdrop:color-mix(in srgb,var(--mnp-ink) 70%,transparent);
  --mnp-shadow:color-mix(in srgb,var(--mnp-ink) 4%,transparent);
  --mnp-shadow-modal:color-mix(in srgb,var(--mnp-ink) 25%,transparent);
  /* Type, 2026-09-02, requested - the page's finalised faces, each
     deferring to .mn-home's own token first. */
  --mnp-font-sans:var(--ui,'Inter',system-ui,-apple-system,'Segoe UI','Roboto',sans-serif);
  --mnp-font-serif:var(--text,'Source Serif 4','Georgia','Times New Roman',serif);
  /* Radius, kept on request, 2026-09-03. No --mnp-r-lg: that was the
     card's own corner and the card is gone. */
  --mnp-r-sm:4px;
  --mnp-r-md:8px;
  --mnp-ease:cubic-bezier(0.16,1,0.3,1);
  /* The width at which the rail stops being a rail. Declared once so the
     media query and the notes cannot drift apart. */
  --mnp-rail-min:1100px;
}

/* The intro. One wrapper for the same reason .mn-what__copy is one (see
   above): the two-column template addresses a single box, and the
   eyebrow/title pair is two of its children. */
.mn-practice__intro{
  display:grid;
  grid-template-columns:1fr 380px;
  gap:8px 32px;
  align-items:start;
}

/* Brought in line with the page's own eyebrow rule 2026-09-03 (requested,
   "formal across the page"): this used to draw its own trailing rule
   (::after, flexing out to 50px) instead of the page's small leading
   one. Font, weight, tracking and colour were already byte-for-byte
   page.css's .mn-eyebrow (see section/NOTES.md, "Which face plays which
   role" - this was matched on purpose during the practice rail's own
   type merge); only the rule direction now matches too. gap is 12px, not
   the 10px this shipped at, for the same reason - one gap value for the
   one motif everywhere it appears. */
.mn-practice__eyebrow{
  grid-area:1 / 1;
  display:flex;
  align-items:center;
  gap:12px;
  margin:0;
  color:var(--mnp-ink-3);
  font-family:var(--mnp-font-sans);
  font-size:11px;
  font-weight:500;
  line-height:1.4;
  letter-spacing:.14em;
  text-transform:uppercase;
}
/* Starts from the spine's ring, same mechanism and same reason as
   .mn-eyebrow's own rule in section 6 - see the comment there. */
.mn-practice__eyebrow::before{
  content:"";
  inline-size:var(--spine-inset);
  block-size:var(--spine-width);
  flex:none;
  margin-inline-start:calc(-1 * var(--spine-inset));
  /* Matched to the vertical spine line, same as .mn-eyebrow's own rule -
     see section 6. */
  background:var(--spine);
}

/* Typography moved onto the shared .mn-h2 (markup: class="mn-h2
   mn-practice__title"), 2026-09-03 (requested: apply the page's one .mn-h2
   rule to every section). This band's title ran its own fixed 34px
   rather than the page's clamp(30px,4vw,51.5px) - a deliberate choice at
   the time (section/NOTES.md: "a fixed size, not a clamp... the type has
   a fixed box to fill") that the current request supersedes. Only the
   grid placement stays here, since .mn-h2 knows nothing about this
   band's two-column intro. */
.mn-practice__title{
  grid-area:2 / 1;
  margin:0;
}

/* Spans both header rows and bottom-aligns inside them: the lede's last
   line sits on the h2's last line, and the eyebrow keeps its 8px above
   the h2 rather than being pushed apart by the taller column. */
.mn-practice__lede{
  grid-area:1 / 2 / 3 / 3;
  align-self:end;
  margin:0;
  color:var(--mnp-ink-2);
  font-size:14.5px;
  line-height:1.7;
}

/* The rail. A direct child of .mn-band, so it takes the content column
   automatically; the 28px above the rule and the 28px below it are two
   equal gaps either side of the border, unchanged from the band's own
   design. */
.mn-practice__rail{
  display:flex;
  gap:14px;
  block-size:calc(460px + 28px + 1px);
  margin-block-start:28px;
  padding-block-start:28px;
  border-block-start:1px solid var(--mnp-line);
  list-style:none;
  overflow:hidden;
}

/* ---------------------------------------------------------------------
   The four panels

   Each panel is <li> > <details>. The <details> is what opens; the <li>
   is the flex item that grows, selected with :has() so the growth is
   driven by the disclosure's own state and needs no JavaScript and no
   class to be toggled. <details name> is an exclusive accordion in the
   platform: opening one closes the rest, with no script at all.
   --------------------------------------------------------------------- */
.mn-practice__panel{
  position:relative;
  flex:1.2;
  min-inline-size:0;
  border:1px solid var(--mnp-line-dark);
  border-radius:var(--mnp-r-md);
  background:var(--mnp-dark);
  color:var(--mnp-on-dark-hi);
  overflow:hidden;
  /* flex-grow, NOT the shorthand - flex-basis 0% <-> auto is not
     interpolable, so transitioning the shorthand across the 1100px
     boundary leaves flex-grow stranded at 0. flex-grow alone is a number
     and always resolves. */
  transition:
    flex-grow .5s var(--mnp-ease),
    border-color .3s;
}
.mn-practice__panel:has([open]){
  flex:3.4;
  /* --aqua: an open panel is a state, and --aqua is the colour the
     page's dark bands reserve for state (--spine-dot, --focus). */
  border-color:var(--mnp-panel-line-open);
}
@media (hover: hover){
  .mn-practice__panel:hover:not(:has([open])){
    border-color:var(--mnp-panel-line-hover);
  }
  .mn-practice__panel:hover:not(:has([open])) .mn-practice__img{
    opacity:.42;
  }
}

.mn-practice__img{
  position:absolute;
  inset:0;
  inline-size:100%;
  block-size:100%;
  object-fit:cover;
  object-position:center;
  opacity:.35;
  transition:
    transform .6s var(--mnp-ease),
    opacity .3s;
}
.mn-practice__panel:has([open]) .mn-practice__img{
  opacity:.46;
  transform:scale(1.04);
}

.mn-practice__panel::after{
  content:"";
  position:absolute;
  inset:0;
  background:var(--mnp-scrim);
  pointer-events:none;
}

.mn-practice__disclosure{
  position:absolute;
  inset:0;
}

.mn-practice__summary{
  position:absolute;
  inset:0;
  z-index:2;
  cursor:pointer;
  list-style:none;
}
.mn-practice__summary::-webkit-details-marker{display:none;}
/* --aqua, what .mn-band--dark sets --focus to. This band cannot inherit
   the page's --focus here: it is a LIGHT band containing dark panels, so
   --focus would resolve to --teal, and --teal on --ground measures
   3.8:1, under the 4.5 a ring wants against its own surround. */
.mn-practice__summary:focus-visible{
  outline:2px solid var(--mnp-accent-dark);
  outline-offset:-4px;
  border-radius:var(--mnp-r-md);
}

.mn-practice__label{
  position:absolute;
  inset-block-end:28px;
  inset-inline-start:28px;
  display:block;
  color:#fff;
  font-family:var(--mnp-font-sans);
  font-size:12.5px;
  font-weight:600;
  letter-spacing:.12em;
  text-transform:uppercase;
  white-space:nowrap;
  transform:rotate(-90deg);
  transform-origin:left bottom;
  transition:opacity .3s;
}
.mn-practice__panel:has([open]) .mn-practice__label{opacity:0;}

/* ---------------------------------------------------------------------
   The opened panel's body. <details> hides everything but the summary
   when closed, so this content leaves the accessibility tree with the
   panel.
   --------------------------------------------------------------------- */
.mn-practice__body{
  position:absolute;
  inset:0;
  z-index:3;
  display:flex;
  flex-direction:column;
  justify-content:flex-end;
  gap:12px;
  padding:32px 28px;
  overflow:hidden;
  /* Above the summary so it is legible, but lets clicks through to it -
     only the CTA is interactive. */
  pointer-events:none;
  /* Fixed 2026-09-03 (reported): in the wide rail, .mn-practice__panel's
     inline-size is what animates (flex-grow, .5s) when a panel opens, and
     this element is 100% of that inline-size at every frame - so the
     heading and body copy were re-wrapping continuously for half a
     second, reading as laggy/unreadable rather than as a smooth open.
     Fading the text in only once the box is mostly done growing hides
     the reflow instead of showing it: nothing to read for the first
     300ms (still just the panel widening), then a clean 250ms fade once
     there is a final-width box for it to sit in.

     THIS IS OPACITY:0 BY DEFAULT, MADE VISIBLE BY page.js ADDING
     .is-visible - not @starting-style. Tried @starting-style first, since
     the brief dialog below already leans on it; verified with
     document.getAnimations() that it never actually ran here. <details>
     shows and hides this element through its own internal mechanism, not
     through a `display` value this stylesheet sets, and that did not
     count as a "starting style" boundary in testing - so nothing was
     transitioning and the text was simply visible at opacity:1 from the
     first frame, the fix having no effect. The double-rAF class toggle in
     page.js forces the browser to commit the opacity:0 frame before
     asking for opacity:1, which is what actually makes the transition
     run. */
  opacity:0;
  transition:opacity .25s ease .3s;
}
.mn-practice__body.is-visible{
  opacity:1;
}

/* ONE TREATMENT, NOT FOUR - section/NOTES.md, "The four tag hues
   collapsed to one": there is no multi-hue accent set to map four
   practices onto, and the rail is an exclusive accordion, so at most one
   tag is ever on screen and a colour code nothing can be compared
   against is not a code. */
.mn-practice__tag{
  inline-size:fit-content;
  margin:0;
  padding:3px 8px;
  border:1px solid var(--mnp-tag-line);
  border-radius:var(--mnp-r-sm);
  background:var(--mnp-tag-fill);
  color:var(--mnp-accent-dark);
  font-family:var(--mnp-font-sans);
  font-size:11px;
  font-weight:600;
  letter-spacing:.14em;
  text-transform:uppercase;
}

.mn-practice__heading{
  margin:0;
  color:#fff;
  font-family:var(--mnp-font-serif);
  font-size:26px;
  font-weight:600;
  line-height:1.18;
  letter-spacing:-.015em;
}

.mn-practice__text{
  margin:0;
  color:var(--mnp-on-dark);
  font-size:13.5px;
  line-height:1.7;
}

.mn-practice__chips{
  display:flex;
  flex-wrap:wrap;
  gap:8px;
  margin:2px 0 0;
  padding:0;
  list-style:none;
}
.mn-practice__chips>li{
  display:flex;
  align-items:center;
  gap:5px;
  padding:4px 10px;
  border:1px solid var(--mnp-chip-line);
  border-radius:16px;
  background:var(--mnp-chip-fill);
  color:var(--mnp-on-dark-hi);
  font-size:11.5px;
  font-weight:500;
  /* Serif, inherited, deliberately not pinned to Inter: the chips read
     as sentence-case content, not uppercase labels. */
  white-space:nowrap;
}
.mn-practice__chips svg{
  flex:none;
  color:var(--mnp-on-dark);
}

.mn-practice__cta{
  display:inline-flex;
  align-items:center;
  gap:6px;
  inline-size:fit-content;
  margin-block-start:6px;
  padding:8px 16px;
  border:1px solid var(--mnp-card);
  border-radius:var(--mnp-r-sm);
  background:var(--mnp-card);
  color:var(--mnp-ink);
  font-family:inherit;
  font-size:12.5px;
  /* A <button> takes the UA's `normal` line-height by default; stated so
     the control keeps its designed height exactly. */
  line-height:1.55;
  font-weight:600;
  letter-spacing:.005em;
  cursor:pointer;
  pointer-events:auto;
  transition:
    background-color .2s ease,
    transform .2s ease;
}
@media (hover: hover){
  .mn-practice__cta:hover{
    background-color:var(--mnp-fill);
    transform:translateX(3px);
  }
  .mn-practice__cta:hover .mn-practice__arrow{
    transform:translateX(2px);
  }
}
.mn-practice__cta:focus-visible{
  outline:2px solid var(--mnp-accent-dark);
  outline-offset:3px;
}
.mn-practice__arrow{
  flex:none;
  transition:transform .2s ease;
}

/* ---------------------------------------------------------------------
   The brief. A real <dialog> opened with showModal(), which supplies the
   backdrop, the focus trap, Escape-to-close and the inert background.
   --------------------------------------------------------------------- */
.mn-practice__brief{
  inline-size:480px;
  max-inline-size:85vw;
  block-size:100%;
  max-block-size:100%;
  margin:0 0 0 auto;
  padding:36px 32px;
  border:0;
  background:var(--mnp-card);
  box-shadow:0 20px 50px var(--mnp-shadow-modal);
  color:var(--mnp-ink);
  font-family:var(--mnp-font-serif);
  overflow-y:auto;
  /* The slide-in. A <dialog> goes from display:none to display:block, so
     the transition needs the discrete properties allowed and a
     @starting-style to animate from - otherwise the panel simply
     appears. If a browser supports neither, [open] still sets
     translateX(0) and the panel lands in the right place unanimated. */
  transform:translateX(100%);
  transition:
    transform .35s var(--mnp-ease),
    overlay .35s allow-discrete,
    display .35s allow-discrete;
}
.mn-practice__brief[open]{transform:translateX(0);}
@starting-style{
  .mn-practice__brief[open]{transform:translateX(100%);}
}
.mn-practice__brief::backdrop{
  background:var(--mnp-backdrop);
  backdrop-filter:blur(4px);
}

.mn-practice__close{
  position:absolute;
  inset-block-start:20px;
  inset-inline-end:20px;
  display:flex;
  align-items:center;
  justify-content:center;
  inline-size:36px;
  block-size:36px;
  border:1px solid var(--mnp-line);
  border-radius:50%;
  background:var(--mnp-fill);
  color:var(--mnp-ink);
  cursor:pointer;
  transition:background-color .2s var(--mnp-ease),color .2s var(--mnp-ease);
}
@media (hover: hover){
  .mn-practice__close:hover{
    background:var(--mnp-dark);
    color:var(--mnp-on-dark-hi);
  }
}

.mn-practice__brief-tag{
  display:inline-block;
  margin-block-end:10px;
  color:var(--mnp-accent);
  font-family:var(--mnp-font-sans);
  font-size:11px;
  font-weight:600;
  letter-spacing:.14em;
  text-transform:uppercase;
}

.mn-practice__brief-title{
  margin:0 0 14px;
  font-family:var(--mnp-font-serif);
  font-size:28px;
  font-weight:600;
  line-height:1.18;
  letter-spacing:-.015em;
}

.mn-practice__brief-lede{
  margin:0 0 24px;
  color:var(--mnp-ink-2);
  font-size:14px;
  line-height:1.7;
}

.mn-practice__brief-sub{
  margin:0 0 14px;
  color:var(--mnp-ink);
  font-family:var(--mnp-font-sans);
  font-size:11px;
  font-weight:500;
  line-height:1.4;
  letter-spacing:.14em;
  text-transform:uppercase;
}

.mn-practice__deliverables{
  display:grid;
  gap:10px;
  margin:0 0 28px;
}
.mn-practice__deliverable{
  display:grid;
  grid-template-columns:auto 1fr;
  gap:2px 12px;
  padding:14px;
  border:1px solid var(--mnp-line);
  border-radius:var(--mnp-r-sm);
  background:var(--mnp-fill);
}
.mn-practice__deliverable svg{
  grid-area:1 / 1 / span 2 / auto;
  margin-block-start:2px;
  color:var(--mnp-accent);
}
.mn-practice__deliverables dt{
  font-size:13.5px;
  font-weight:600;
}
.mn-practice__deliverables dd{
  margin:0;
  color:var(--mnp-ink-2);
  font-size:12.5px;
  line-height:1.65;
}

.mn-practice__brief-cta{
  display:block;
  inline-size:100%;
  padding:12px;
  border:1px solid var(--mnp-dark);
  border-radius:var(--mnp-r-sm);
  background:var(--mnp-dark);
  color:#fff;
  font-size:13.5px;
  line-height:normal;
  font-weight:600;
  letter-spacing:.005em;
  text-align:center;
  text-decoration:none;
  transition:background-color .2s var(--mnp-ease);
}
@media (hover: hover){
  .mn-practice__brief-cta:hover{
    /* --ocean, the step of the page's own primary-button hover ramp
       (.mn-btn--solid:hover) the palette actually names. */
    background:var(--mnp-accent);
  }
}

/* ---------------------------------------------------------------------
   Narrow layout. Below 1100px the rail stacks: the collapsed panel is a
   64px bar with the label upright (a 64px touch target) and the open
   panel is that bar plus its content, sized by what the content needs
   rather than by a fixed height. See section/NOTES.md, "The narrow
   layout is new, not preserved", for the full account of the draft's
   layout not existing below 1100px at all.
   --------------------------------------------------------------------- */
@media (max-width: 1099px){
  .mn-practice__intro{
    grid-template-columns:1fr;
    /* Stays 8px, the eyebrow's own gap - the header's stacked gap is not
       the same number as the gap inside the eyebrow column, so a single
       larger value here would space the eyebrow off its heading. */
    gap:8px;
  }
  .mn-practice__eyebrow,
  .mn-practice__title,
  .mn-practice__lede{
    grid-area:auto;
  }
  /* 8px row-gap + 12px = the draft's 20px stacked gap. */
  .mn-practice__lede{
    margin-block-start:12px;
  }

  .mn-practice__rail{
    flex-direction:column;
    block-size:auto;
  }
  /* THE FIX for 1px panels below 1100px: flex-basis back to auto, so the
     panel is sized by its content instead of by a zero basis. */
  .mn-practice__panel{
    flex:none;
    transition:border-color .3s;
  }
  .mn-practice__panel:has([open]){
    flex:none;
  }
  .mn-practice__disclosure{
    position:static;
  }
  .mn-practice__summary{
    position:relative;
    inset:auto;
    display:flex;
    align-items:center;
    min-block-size:64px;
    padding:12px 20px;
  }
  .mn-practice__summary:focus-visible{
    outline-offset:-3px;
  }
  /* Open, the label is hidden (the body's h3 says the same thing), so
     the bar would otherwise be 64px of blank. It collapses to a 28px
     strip: still a full-width control for closing the panel again, and
     it gives the body the top padding the wide layout gets from its own
     box. */
  .mn-practice__panel:has([open]) .mn-practice__summary{
    min-block-size:0;
    block-size:28px;
    padding:0;
  }
  .mn-practice__label{
    position:static;
    white-space:normal;
    transform:none;
  }
  .mn-practice__body{
    position:relative;
    inset:auto;
    padding:0 20px 24px;
  }
  .mn-practice__heading{
    font-size:22px;
  }
  .mn-practice__brief{
    padding:32px 24px;
  }
}
/* ---------------------------------------------------------------------
   Motion. Every transition above is a progressive nicety on a layout
   that is already correct without it, so reduced motion switches them
   off rather than substituting a different design.
   --------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce){
  .mn-practice *,
  .mn-practice *::before,
  .mn-practice *::after{
    transition-duration:.01ms !important;
    animation-duration:.01ms !important;
  }
  .mn-practice__panel:has([open]) .mn-practice__img{
    transform:none;
  }
  .mn-practice__cta:hover{
    transform:none;
  }
}

/* ---------------------------------------------------------------------
   21. Data assets

   Incorporated 2026-09-03 (requested) from stats-section/index.html - see
   that folder's NOTES.md for the full account (a Gemini Antigravity
   delivery, rebuilt with this page's type, colour and band grid already
   anticipated by name in its own stylesheet's header comment).

   THE CLOSEST TO A LITERAL TRANSPLANT OF THE THREE BANDS MERGED TODAY.
   Unlike the practice rail, this one was staged already assuming the
   merge: its own comments said "on merge the layout and type
   declarations below are dropped and the section takes class=mn-band
   mn-band--mist" and "on merge these three rules [eyebrow/title/lede]
   are deleted and the markup takes the page's own class names" - both
   done exactly as specified, nothing improvised.

   DROPPED ENTIRELY, and why:
   - The scoped box-sizing:border-box rule and the two margin/list resets
     - .mn-home's own reset (section 4) already covers all three for every
       descendant, standalone-review duplicates now that this always
       renders inside .mn-home.
   - --mns-band (mist) and the whole "3. The band itself" block (--measure,
     display:grid, the four-line grid-template-columns, padding-block,
     background, color, font, overflow-wrap) - .mn-band supplies the grid
     and padding-block:var(--pad); the surface modifier on the section
     supplies the background (.mn-band--mist at the merge, .mn-band--tint
     since 2026-09-11). Restating either here would be a second, competing
     declaration. A dark-ground version of this band was tried and
     reverted 2026-09-03 - see the comment on --mns-field above and
     NOTES.md - so this stays a plain restatement rather than an
     exception.
   - .mn-stats__eyebrow / __title / __lede - the markup now uses
     .mn-eyebrow / .mn-h2 / .mn-lede directly, per the file's own note.

   RADIUS IS THE BAND'S OWN, 0 (flush squares), NOT flattened to this
   page's --r:4px. The practice rail got this exact question asked and
   answered on request earlier the same day (see section 20); applied
   here without asking again as the same category of decision, already
   made once. Revert is one token, --mns-radius.

   THE SPLIT AT 1100px IS NOT A SHARE AT ALL (changed 2026-09-14; it was
   0.36/0.64 via --mns-intro-share, and .mn-band--split's 0.46/0.54 before
   that). The right column is sized to --mns-grid-w, the width the tiles
   actually occupy, and the intro takes the remainder - a share always
   left the difference as void between the two. This is still its own
   split rather than a reuse of .mn-band--split, and works the same way:
   a same-specificity (0,1,0) rule on .mn-stats itself, cascading after
   section 5's base .mn-band template and winning on source order at
   >=1100px only, falling back to the plain full-bleed content column
   below it. Same mechanism, different proportions.
   --------------------------------------------------------------------- */
.mn-stats{
  /* Surfaces. --ocean is the tile, --teal the single accent cell. The
     band's own ground is .mn-band--tint, the page's one ground since
     2026-09-11 - it was .mn-band--mist's plain --mist until then, and
     that grey is what the client twice called off-theme. A dark-ground
     version (--mns-ground:#06152f, tiles sitting ON the field, a 1px
     --aqua hairline replacing the drop shadow) was tried 2026-09-03 on
     request and reverted the same day ("looks weird"). See NOTES.md,
     "Data assets band moved to a dark ground, then reverted" for the
     full construction if a dark ground is wanted again - the reasoning
     there (why --mist read as the palette's one neutral surface, why
     --ocean tiles cannot go directly on .mn-band--dark's --ground) still
     holds even though the direction was not kept. Nothing below this
     line was retuned for --tint: the tiles carry their own opaque
     surfaces and their contrast is measured against those, not against
     the band. */
  --mns-field:var(--ocean,#004e79);
  --mns-accent:var(--teal,#0b8fa3);
  /* The ground behind a photograph while it decodes, so a slow image
     does not flash white inside a wall of colour. */
  --mns-photo-ground:var(--ink,#040b34);
  /* Ink. --mns-on-field is white on --ocean at 8.87:1; --mns-on-accent is
     --ink on --teal at 4.97:1. The accent inverts because white on --teal
     is 3.84:1 and fails the 0.8rem note line. */
  --mns-on-field:#fff;
  --mns-on-accent:var(--ink,#040b34);
  /* Hairline under the micro-label. Two values because the accent cell's
     ink is dark, not light. */
  --mns-rule-on-field:rgb(255 255 255 / 25%);
  --mns-rule-on-accent:rgb(4 11 52 / 25%);
  --mns-radius:0;
  --mns-focus:var(--focus,#0b8fa3);
  /* Elevation. Navy-tinted, not black - page.css shadows with
     rgba(4,11,52,.09) on the deliverable tiles and rgba(0,78,121,.28)
     under the solid button, and a neutral grey shadow on the cool ground
     this band sits on reads as dirt. (That ground was --mist when this
     was written and is --tint since 2026-09-11; both are cool, so the
     navy tint still holds and these values were not retuned.) Two layers: a contact shadow
     that seats the tile, and a soft ambient one that gives it height.
     THE AMBIENT BLUR IS THE GAP TOKEN ITSELF (var(--mns-gap)), not a
     number copied from it - a grid item's shadow paints before the next
     item's opaque background, so any shadow wider than the gap is
     clipped by the neighbour to its right and below. Referencing the
     token keeps that holding at every width, including down at the 6px
     clamp floor. The hover value is exempt on purpose: it is deliberately
     wider than the gap, and :hover raises z-index so it paints over its
     neighbours rather than under them. */
  --mns-shadow:
    0 1px 2px rgb(4 11 52 / 7%),
    0 calc(var(--mns-gap) * 0.4) var(--mns-gap) rgb(4 11 52 / 12%);
  --mns-shadow-raised:
    0 2px 4px rgb(4 11 52 / 9%),
    0 12px 24px rgb(4 11 52 / 16%);
  /* Faces. Serif is the base voice, Inter is the micro-label only. */
  --mns-text:var(--text,'Source Serif 4','Georgia','Times New Roman',serif);
  --mns-ui:var(--ui,'Inter',system-ui,-apple-system,'Segoe UI','Roboto',sans-serif);
  /* The gap between cells - fluid, never a breakpoint. Drives three
     things: the gap itself, the grid's width (so the tile stays exactly
     --mns-tile), and the ambient shadow's blur above. Change this and
     all three follow, rather than three numbers kept in step by hand. */
  --mns-gap:clamp(6px,0.8vw,10px);
  /* The tile's own edge. 180px since 2026-09-03 (requested): the band
     stood 876px tall in a 1280x720 viewport - a 1920 laptop at Windows'
     recommended 150% scaling - and did not finish inside one screen.
     Reduced 10% from the 200px the merged draft carried. The grid
     follows on its own because both inline-size caps below are written
     against this token rather than against the 400px/600px constants
     they used to spell out, so revert is this one line. */
  --mns-tile:180px;
  /* The grid's own width, written once. The three-track cap below spells
     the same arithmetic for the grid's inline-size; naming it here lets
     the SPLIT size its right column to exactly what the tiles occupy
     instead of guessing at a share. Both now move together off --mns-tile
     and --mns-gap. */
  --mns-grid-w:calc(3 * var(--mns-tile) + 2 * var(--mns-gap));
  /* The split gutter. Was clamp(36px,4vw,76px) with the intro taking a
     0.36 SHARE of the measure; both changed 2026-09-14 on request ("the
     left content bar is shrinked, and there is visible gap"). The share
     was the real fault - it handed the right column 64% of the measure
     while the tiles only ever occupy --mns-grid-w, so at a 1536 viewport
     321.7px of that column was dead space, and the intro was squeezed to
     495.9px beside it. The intro now takes the remainder and the right
     column is sized to the grid, so no track carries slack; this gutter
     is the whole of the space between them. Reduced as well, since it no
     longer has 300px of dead column sitting next to it. */
  --mns-split-gap:clamp(28px,2.6vw,48px);
}

/* The intro. One wrapper for the same reason .mn-what__copy and
   .mn-practice__intro are: in split mode it is a single grid column, and
   a column of three separately-placed children would need three
   placement rules that must not drift apart. */
/* ---------------------------------------------------------------------
   The grid

   TWO COLUMNS IS THE BASE, THREE IS THE WIDE CASE, and the count is a
   media query rather than auto-fit on purpose: nine items divide by
   three and by one only. repeat(auto-fit, minmax(...)) would strand the
   ninth at four columns and the eighth and ninth at two - the same
   arithmetic that caught the logo row and the deliverables grid
   elsewhere on this page.
   --------------------------------------------------------------------- */
.mn-stats__grid{
  margin-block-start:clamp(44px,5vw,76px);
  display:grid;
  grid-template-columns:minmax(0,1fr);
  gap:var(--mns-gap);
}

/* THE TILE IS A FIXED --mns-tile FROM 460px UP. THE WIDTH IS WRITTEN
   AGAINST BOTH TOKENS, not against numbers - 3 * tile + 2 * gap for three
   tracks and 2 * tile + gap for two - so each track resolves to exactly
   the tile size whatever the gap is doing, and neither can drift the way
   they did when these were hand-copied constants. Because the width and
   not the track decides the size, the tile is the same at 460, 700, 1100
   and 1920 - the grid gains air around it rather than growing.

   min(<cap>,100%), NOT max-inline-size - under the justify-self:end the
   split applies below, a max-width leaves the grid sized to fit-content
   and the tracks collapse onto their items' max-content instead of
   staying at the tile size. */
@media (min-width: 460px){
  .mn-stats__grid{
    grid-template-columns:repeat(2,minmax(0,1fr));
    inline-size:min(calc(2 * var(--mns-tile) + var(--mns-gap)),100%);
  }
}
/* 700px: the measure is 658px, comfortably past the 552px three-track
   cap (3 * 180 + 2 * 6, the gap at this width), so three columns fit
   without squeezing the tile. More headroom now that the cap is down from
   632px, and is left where it is rather than moved - three 180px tiles
   would fit a little earlier, but the intro's measure below 700px is
   what sets this line, not the grid's. */
@media (min-width: 700px){
  .mn-stats__grid{
    grid-template-columns:repeat(3,minmax(0,1fr));
    inline-size:min(var(--mns-grid-w),100%);
  }
}

/* ---------------------------------------------------------------------
   The split - title left, grid right. 1100px, not 1280: a 1920 laptop
   running Windows at 150% scaling reports a 1280px viewport, so a split
   gated at 1280 would be invisible to a large share of desktop visitors.
   A media query, not a container query: a container query never applies
   to the element that establishes the container, and this section is
   that element - the same constraint .mn-band--split has.
   --------------------------------------------------------------------- */
@media (min-width: 1100px){
  .mn-stats{
    /* THE RIGHT COLUMN IS THE GRID'S OWN WIDTH, NOT A SHARE (2026-09-14).
       It used to be (measure - gap) * 0.64 while the grid inside it was
       only --mns-grid-w, so every pixel of the difference became void
       between the title and the tiles - 321.7px of it at a 1536 viewport,
       on top of the gutter. Sizing the track to the thing it contains
       means no track can carry slack, and the intro simply takes what is
       left. The tiles do NOT grow: --mns-grid-w is fixed off --mns-tile,
       which was cut to 180px on request so the band finishes inside a
       1280x720 screen. That constraint is why the leftover width goes to
       the text rather than to the squares. */
    grid-template-columns:
      [full-start] minmax(0,1fr)
      [content-start] minmax(0,calc(var(--measure) - var(--mns-split-gap) - var(--mns-grid-w)))
      [intro-end] var(--mns-split-gap)
      [grid-start] var(--mns-grid-w) [content-end]
      minmax(0,1fr) [full-end];
    align-items:start;
  }
  .mn-stats__intro{
    grid-column:content-start / intro-end;
  }
  /* The intro's text caps are raised FOR THIS BAND ONLY, so the wider
     column is actually used rather than just pushing the ragged edge
     further from the tiles. .mn-lede's global 68ch and .mn-h2's global
     23ch are untouched everywhere else on the page - both are tuned for
     the narrower columns the other bands give them. 74ch is still inside
     a readable measure at this font size; do not push it further to close
     the remaining space, that is what the measure is for. */
  .mn-stats__intro .mn-lede{max-inline-size:74ch;}
  .mn-stats__intro .mn-h2{max-inline-size:26ch;}
  .mn-stats__grid{
    grid-column:grid-start / content-end;
    margin-block-start:0;
    /* The track is now exactly the grid's width, so this only guards
       against sub-pixel rounding - but it is what keeps the grid's right
       edge on the band's right measure edge, the same edge the header and
       footer use. Keep it. */
    justify-self:end;
  }
}

/* ---------------------------------------------------------------------
   The cells

   SINGLE-COLUMN MODE IS NOT A SQUARE. Below 460px a square is 284px of
   height carrying five short lines, so the figure cells are auto-height
   cards and the photographs are 3/1 strips - the alternation survives,
   nothing is hidden or cramped.

   NO overflow:hidden ON THE FIGURE CELLS. An aspect-ratio box whose
   overflow is not visible resolves min-block-size to 0 rather than auto,
   so a fixed overflow:hidden on the base cell rule silently clipped 5px
   off the four longer notes at 320px instead of letting the cell grow.
   Clipping now lives only on the photograph cell, where the hover scale
   needs it. */
.mn-stats__cell{
  position:relative;
  border-radius:var(--mns-radius);
  /* Resting elevation, deliberately outside the hover/reduced-motion
     guard below - a static style, not motion, so a reduced-motion or
     touch visitor sees the same raised tiles as everyone else. */
  box-shadow:var(--mns-shadow);
}
@media (min-width: 460px){
  .mn-stats__cell{
    aspect-ratio:1 / 1;
  }
}

/* Nine items in two tracks leave the ninth alone on a fifth row; it
   spans both instead, so the ragged row is a designed wide tile rather
   than a stranding. Only in two-column mode - nine divides by three and
   by one, so neither neighbouring mode has an orphan to resolve.

   AFTER the square rule above, not beside the grid rules where it
   belongs by subject - every selector in this band is (0,1,0), so source
   order alone decides aspect-ratio, and written earlier this lost to
   aspect-ratio:1/1 and came out 564x564 instead of 564x282. */
@media (min-width: 460px) and (max-width: 699.98px){
  .mn-stats__cell--last{
    grid-column:1 / -1;
    aspect-ratio:2 / 1;
  }
}

/* Label pinned to the top, figure and note centred as a pair in what is
   left. Flex with a pair of auto margins rather than grid rows: the
   figure and the note share one optical centre, and a grid needs either
   a splitting third row or a wrapper div that exists only to be
   centred. margin-block-start:auto on the figure and margin-block-end:
   auto on the note absorb equal free space above and below the pair. */
.mn-stats__cell--data{
  display:flex;
  flex-direction:column;
  align-items:center;
  padding:clamp(14px,1.5vw,20px) clamp(12px,1.2vw,16px);
  background:var(--mns-field);
  color:var(--mns-on-field);
  text-align:center;
}
/* The accent cell. --teal at 3.84:1 against white cannot carry the note
   line, so the ink inverts to --ink at 4.97:1 rather than the surface
   being abandoned. */
.mn-stats__cell--accent{
  background:var(--mns-accent);
  color:var(--mns-on-accent);
  --mns-rule-on-field:var(--mns-rule-on-accent);
}

.mn-stats__tag{
  flex:none;
  inline-size:85%;
  padding-block-end:6px;
  border-block-end:1px solid var(--mns-rule-on-field);
  font-family:var(--mns-ui);
  font-size:clamp(10.5px,0.62vw,11.6px);
  font-weight:600;
  line-height:1.3;
  letter-spacing:.12em;
  text-transform:uppercase;
}

/* Bold and large by request - the one place on the page the serif is
   heavy at 700 rather than the page's 300 display weight (h1, every h2,
   the practice rail's own title). 700 is also the ceiling the loaded
   font carries (wght 300..700). A fixed size, not a clamp: from 460px up
   the tile is a fixed --mns-tile square, a fixed box the type should not
   drift inside of; below 460px the cell is a card at least 284px across,
   which holds 34px comfortably. LEFT AT 34px when the tile came down to
   180px on 2026-09-03 - the request was the grid's dimensions, not the
   type's, and the longest figure ("5,000+ ") measures 118px inside a
   148px content box, so nothing is squeezed by holding it.

   The figure and its unit are one paragraph and one reading unit -
   "100+ Countries" - split onto two optical lines by the span rather
   than by two elements a screen reader would read as two facts. */
.mn-stats__figure{
  margin-block-start:auto;
  font-size:34px;
  font-weight:700;
  line-height:1.1;
  letter-spacing:-.018em;
  /* Lining figures: the serif's default old-style shapes hang below the
     optical line and read as a typo at display size. */
  font-variant-numeric:lining-nums;
}
.mn-stats__unit{
  display:block;
  margin-block-start:.18em;
  font-family:var(--mns-ui);
  font-size:clamp(11.5px,0.72vw,13.5px);
  font-weight:600;
  line-height:1.25;
  letter-spacing:.06em;
  text-transform:uppercase;
}
.mn-stats__note{
  margin-block:.55em auto;
  max-inline-size:22ch;
  font-family:var(--mns-ui);
  font-size:clamp(11.5px,0.68vw,13px);
  line-height:1.4;
}

/* ---------------------------------------------------------------------
   The photographs. Decorative - alt="" in the markup, announced by
   nothing, carry no claim. Grayscale is the draft's, and is what makes
   four unrelated stock photographs read as one set against the field.
   --------------------------------------------------------------------- */
.mn-stats__cell--image{
  overflow:hidden;
  background:var(--mns-photo-ground);
}
/* Single-column mode only, scoped to a max-width query rather than
   written plainly: .mn-stats__cell--image and .mn-stats__cell are both
   (0,1,0), so an unscoped 16/9 here would win by source order and
   flatten the photographs at every width. 3/1, not 16/9: at 16/9 the
   strip stood 199px against a 114px figure card and the decoration
   outweighed the data on the one screen where the data has least room. */
@media (max-width: 459.98px){
  .mn-stats__cell--image{
    aspect-ratio:3 / 1;
  }
}
.mn-stats__cell--image img{
  display:block;
  inline-size:100%;
  block-size:100%;
  object-fit:cover;
  filter:grayscale(100%) contrast(110%) brightness(95%);
}

/* ---------------------------------------------------------------------
   Motion and focus
   --------------------------------------------------------------------- */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference){
  .mn-stats__cell{
    transition:transform .3s ease,box-shadow .3s ease;
  }
  .mn-stats__cell:hover{
    /* Above its siblings, so the raised shadow - deliberately wider than
       the gap - paints over the neighbouring tiles instead of being
       clipped under the ones that follow it in the DOM. */
    z-index:1;
    transform:translateY(-4px);
    box-shadow:var(--mns-shadow-raised);
  }
  .mn-stats__cell--image img{
    transition:filter .4s ease,transform .4s ease;
  }
  .mn-stats__cell--image:hover img{
    filter:grayscale(0%) contrast(100%);
    transform:scale(1.04);
  }
}

/* Nothing in this band is focusable today - no link, no button. Kept so
   that if a cell is ever made a link on merge, the ring is already
   correct and is not removed by the overflow:hidden above it. */
.mn-stats :where(a, button):focus-visible{
  outline:3px solid var(--mns-focus);
  outline-offset:2px;
}
