/* Empower Mississippi homepage — reference build (the current direction).

   Site chrome (header, mobile nav, footer) and the shared accessibility
   overrides live in css/site.css, which every homepage option loads first.
   This file holds only the sections of THIS homepage. */

/* ---------- Local tokens ----------
   --scrim-step and --scrim-step-open were REFERENCED by .em-process__scrim
   and defined nowhere in this repo — not in tokens/, not in components/, not
   here. An undefined custom property with no fallback resolves to nothing, so
   the scrim painted fully transparent and every step title in the solutions
   chevron was white text sitting directly on a photograph. Over the first
   step's sunset grass that is unreadable, and over any bright source it fails
   SC 1.4.3 outright.

   The audit's contrast sweep could not have caught it: that pass composites a
   text node against its computed background, and there is no computed
   background behind text sitting over an <img>. Same structural blind spot
   that hid the ::placeholder failure.

   Defined here rather than in tokens/, which is upstream and never edited.
   rgba(0,41,53,…) is --em-blue at a measured alpha; CSS cannot express that
   through a colour token without color-mix support this build does not assume,
   which is the same exception css/option-*.css already relies on.

   Gradients, not flat washes, because the copy sits in the bottom third of a
   440px step — a flat scrim heavy enough to carry the title would grey out the
   whole photograph.

   Measured, not estimated, and measured AT THE COPY rather than at the darkest
   stop — the title band sits around 65% down, where the base gradient is at
   alpha .65, not the .88 it reaches at the very bottom. Each of the five
   sources was sampled for its BRIGHTEST pixel inside the title's own box
   (worst case for white text), composited through the scrim at that height:

     Define the problem      5.36:1     Advocate for change    5.06:1
     Conduct research        6.00:1     Policy implementation  5.13:1
     Craft policy solution   8.69:1

   Worst case 5.06:1 against AA's 4.5:1 for normal text — and these titles are
   large bold, where the bar is 3:1, so there is real headroom. The open state
   adds the second layer over the same pixels: the Real Solution paragraph
   lands on 13.8–14.1:1 for white and 8.6–8.7:1 for --text-inverse-muted.

   If the stand-in photography is ever replaced, re-run that sampling. A
   brighter source under the title is the one change that can put this back
   under AA, and no test in this repo can see it. */
:root{
  /* The 44% midpoint left the step NUMBER stranded. It sits at 51% of the
     step, just above the title, where that ramp was only at alpha .59 — and
     over the brightest pixel in these sources no text colour at any opacity,
     including solid white, could reach 4.5:1 there. Solved by darkening the
     band the copy actually occupies rather than by chasing the text colour:
     the ramp now reaches .72 by 58%, so everything from the number down sits
     on the same footing as the title. The top half is untouched, so the
     photograph still opens light. */
  --scrim-step:linear-gradient(180deg,
    rgba(0,41,53,.10) 0%,
    rgba(0,41,53,.42) 32%,
    rgba(0,41,53,.72) 58%,
    rgba(0,41,53,.88) 100%);
  /* The open layer sits ON TOP of --scrim-step, so the two alphas compound:
     1-(1-a_base)(1-a_open). At .74 over .55 that reached .93 combined at the
     title and .99 at the foot — the photograph went almost black the moment
     you hovered it, which is the opposite of what expanding a step is for.
     These values are chosen so the COMBINED result lands around .79 at the
     title and .86 behind the Real Solution paragraph: enough to carry the
     copy, little enough that the picture is still the reason to look. */
  --scrim-step-open:linear-gradient(180deg,
    rgba(0,41,53,.06) 0%,
    rgba(0,41,53,.26) 40%,
    rgba(0,41,53,.56) 100%);
}

.em-hero__copy{min-width:0}

/* SC 2.5.8: standalone links need a 24x24 target. */
.em-process__detail a{display:inline-flex;align-items:center;min-height:24px;min-width:24px}

/* ---------- Hero ---------- */
.em-hero{display:grid;
  grid-template-columns:minmax(0,calc(max(0px,(100vw - var(--container-max)) / 2) + 680px)) 1fr;
  align-items:stretch;background:var(--surface-page)}
.em-hero__copy{display:flex;flex-direction:column;justify-content:center;align-items:flex-start;
  padding:var(--section-y-lg) var(--space-11) var(--section-y-lg) max(var(--gutter),calc((100vw - var(--container-max)) / 2 + var(--gutter)))}
.em-hero__lede{font-size:var(--fs-lead);line-height:1.5;color:var(--text-body);max-width:46ch;
  margin:0 0 var(--space-9)}
.em-hero h1{margin:0 0 var(--space-6);font-size:var(--fs-hero)}
.em-hero__actions{display:flex;align-items:center;gap:var(--space-7);flex-wrap:wrap}
.em-hero__link{font-family:var(--font-display);font-weight:var(--fw-semibold);font-size:var(--fs-small);
  text-decoration:none;color:var(--em-blue)}
.em-hero__link:hover{color:var(--em-orange)}

/* ---------- Solutions model ---------- */
.em-solutions{background:var(--surface-tint)}
.em-solutions__head{display:grid;grid-template-columns:1fr minmax(0,460px);gap:var(--space-11);
  align-items:end;margin-bottom:var(--space-9)}
.em-solutions__head h2{margin:0}
.em-solutions__head .em-rule{margin:var(--space-5) 0 0}
.em-solutions__label{font-family:var(--font-display);font-weight:var(--fw-semibold);
  font-size:var(--fs-eyebrow);letter-spacing:var(--ls-eyebrow);text-transform:uppercase;
  color:var(--text-muted);margin:0 0 var(--space-5)}

.em-process{display:flex;align-items:stretch;height:440px;margin:0;padding:0;list-style:none}
/* min-width is a TEXT floor, not a taste value: "IMPLEMENTATION" is a single
   unbreakable 153px word, and it has to fit between the step's two gutters
   without breaking mid-word. Those gutters are no longer symmetric — see the
   note on .em-process__inner below — so the floor is 40 + 153 + 30 = 223px,
   and 224px is that floor plus clearance.

   It was 218px against symmetric 30px gutters, which clipped the copy against
   the chevron notch, and 238px before that against symmetric 40px, which left
   the strip only 98px of total slack to redistribute on hover — so the panel
   grew by ~20px and the movement read as a twitch rather than an expansion.
   At 224px the slack is 168px and the hovered panel travels ~42px: a little
   less than the 218px version's ~50px, and the price of copy that is not
   sliced down its left edge. */
.em-process__step{position:relative;flex:1 1 0;min-width:224px;overflow:hidden;
  clip-path:polygon(0 0,calc(100% - 34px) 0,100% 50%,calc(100% - 34px) 100%,0 100%,34px 50%)}
.em-process__step:first-child{clip-path:polygon(0 0,calc(100% - 34px) 0,100% 50%,calc(100% - 34px) 100%,0 100%,0 100%)}
.em-process__step + .em-process__step{margin-left:-34px}
/* flex-grow alone, not the `flex` shorthand — only one value needs to
   interpolate, and animating flex-basis alongside it is what makes the strip
   feel like it is settling rather than moving. */
.em-process__step:hover,.em-process__step:focus-within{flex-grow:3.4}

/* ---------- Why this transition is restated here ----------
   css/motion.css declares
     [data-reveal="on"] [data-reveal]{transition:opacity,transform,clip-path}
   and every step carries data-reveal="rise". That selector is BOTH more
   specific than .em-process__step and later in the cascade, so it replaced the
   whole shorthand and the chevron's width change lost its transition
   altogether — the panels snapped. It also inherited the reveal group's
   `transition-delay:calc(var(--reveal-i) * 70ms)`, so the fifth step waited
   280ms before snapping. That combination is what read as jumpy.

   Both rules below match motion.css's own shape so they win on specificity,
   not on !important. The first keeps the entrance reveal intact and adds
   flex-grow; the second takes over once the step has been revealed, when the
   stagger delay must not apply to a hover. */
[data-reveal="on"] .em-process__step[data-reveal]{
  transition:opacity var(--dur-reveal) var(--ease-entrance),
             transform var(--dur-reveal) var(--ease-entrance),
             flex-grow var(--dur-slow) var(--ease-out)}
[data-reveal="on"] .em-process__step[data-reveal].is-revealed{
  transition:flex-grow var(--dur-slow) var(--ease-out);
  transition-delay:0s}
/* Without JS the reveal layer never engages, so the plain rule has to carry
   the transition on its own. */
.em-process__step{transition:flex-grow var(--dur-slow) var(--ease-out)}

.em-process__bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}

/* See the local-tokens block at the top of this file for why these two exist
   and how the values were measured. */
.em-process__scrim{position:absolute;inset:0;background:var(--scrim-step)}
/* background-image does not interpolate, so the open state cannot be a second
   `background` declaration — it would snap mid-expansion while the panel is
   still moving. It is a second layer whose OPACITY animates instead. */
.em-process__scrim::after{content:"";position:absolute;inset:0;
  background:var(--scrim-step-open);
  opacity:0;transition:opacity var(--dur-slow) var(--ease-out)}
.em-process__step:hover .em-process__scrim::after,
.em-process__step:focus-within .em-process__scrim::after{opacity:1}

/* ASYMMETRIC gutters — 40px left, 30px right — and the asymmetry is geometry,
   not taste. The step is clipped to a chevron whose LEFT edge notches inward
   to a point 34px in at half height:

     polygon(0 0, 100%-34px 0, 100% 50%, 100%-34px 100%, 0 100%, 34px 50%)
                                                          ^^^^^^^^^^^^^^^

   so the left boundary at any height y is 34 * (1 - |y - h/2| / (h/2)). The
   copy sits just below the middle, which is exactly where that notch is at its
   deepest: the step number lands at y=214–234 of 440, where the boundary is
   31.8–33.1px. Against a 30px gutter the leading digit was being sliced off —
   visible on steps 2–5 and not step 1, which is the one step whose clip-path
   has a flat left edge. 40px clears the notch by 6–8px everywhere the copy
   goes.

   The right gutter stays 30px because the right edge bulges the other way —
   it reaches 100% at half height — so there is nothing to clear there, and
   spending 10px on it would push the text floor up for no reason. */
.em-process__inner{position:relative;height:100%;display:flex;flex-direction:column;
  justify-content:flex-end;padding:var(--space-7) 30px 152px 40px}
.em-process__mark{width:46px;height:46px;object-fit:contain;margin-bottom:auto}
/* opacity:1, not the .55 this shipped with. White at .55 over the step's own
   background measured 2.38:1 — a straightforward SC 1.4.3 failure on text that
   carries the step order, and it was invisible for the same reason the missing
   scrim was: no computed background behind text over an <img>. Solving for the
   minimum that clears 4.5:1 against the brightest pixel in each source gives
   .95, so there is no dimmed resting state to be had here. The hover cue it
   used to provide is carried by the photograph returning to colour, the rule
   above the title, and the Real Solution paragraph opening. */
.em-process__num{margin:0 0 var(--space-3);font-family:var(--font-display);font-weight:var(--fw-semibold);
  font-size:var(--fs-caption);letter-spacing:var(--ls-eyebrow);color:var(--white);opacity:1}
.em-process__title{margin:0;font-family:var(--font-display);font-weight:var(--fw-bold);
  font-size:var(--fs-h5);line-height:1.22;letter-spacing:.02em;text-transform:uppercase;
  color:var(--white);overflow-wrap:break-word}
/* letter-spacing reduced from --ls-caps so "IMPLEMENTATION" — a single
   unbreakable 153px word — fits the 158px of text column an unhovered step
   leaves at min-width:218px with 30px gutters, without a mid-word break. */
/* No width: left+right constrain this. A fixed width clips once the container goes
   fluid below ~1248px. */
/* Timed against the width, not independently of it. The panel widens over
   --dur-slow; the copy starts a beat later and finishes with it, so the two
   read as one movement rather than as a resize followed by a fade. An 80ms
   delay on a full --dur-slow fade overshot the panel and was part of what
   made the interaction feel disjointed. */
/* left:40px matches .em-process__inner's left gutter. The detail sits low
   enough that the chevron notch is only ~5–11px deep there, so 30px would not
   have clipped it — but it would have set the paragraph 10px to the left of
   the title it belongs to, which reads as a mistake either way. */
.em-process__detail{position:absolute;left:40px;right:30px;bottom:var(--space-7);
  opacity:0;transform:translateY(10px);pointer-events:none;
  transition:opacity 300ms var(--ease-out) 100ms,transform 300ms var(--ease-out) 100ms}
.em-process__step:hover .em-process__detail,
.em-process__step:focus-within .em-process__detail{opacity:1;transform:none;pointer-events:auto}
.em-process__detail p{margin:0 0 var(--space-4);font-size:var(--fs-small);line-height:1.55;
  color:var(--text-inverse-muted)}
.em-process__detail a{font-family:var(--font-display);font-weight:var(--fw-bold);
  font-size:var(--fs-caption);letter-spacing:var(--ls-caps);text-transform:uppercase;
  color:var(--white);text-decoration:none}
.em-process__detail a:hover{color:var(--orange-300)}

.em-hero__media{position:relative;min-height:640px}
.em-hero__media>img{width:100%;height:100%;object-fit:cover}
.em-hero__northstar{position:absolute;left:-96px;bottom:76px;width:320px;margin:0;
  background:var(--surface-card);border-radius:var(--radius-card);padding:var(--space-6) var(--space-7);
  box-shadow:var(--shadow-lg)}
.em-hero__northstar-label{font-family:var(--font-display);font-weight:var(--fw-bold);
  font-size:var(--fs-eyebrow);letter-spacing:var(--ls-eyebrow);text-transform:uppercase;
  color:var(--em-orange);margin:0 0 var(--space-3)}
.em-hero__northstar p{margin:0;font-family:var(--font-display);font-weight:var(--fw-semibold);
  font-size:var(--fs-h5);line-height:1.4;letter-spacing:var(--ls-heading);color:var(--text-strong)}

/* ---------- Foundations ---------- */
.em-foundations__head{display:grid;grid-template-columns:1fr minmax(0,380px);gap:var(--space-11);
  align-items:end;margin-bottom:var(--space-9)}
.em-foundations__head h2{margin:0}
.em-foundations__head .em-rule{margin:var(--space-5) 0 0}

.em-bento{display:grid;grid-template-columns:1fr 1.06fr;gap:var(--space-6);align-items:stretch}
.em-bento__col{display:grid;grid-template-rows:1fr 1fr;gap:var(--space-6)}
.em-bento__tall{padding-bottom:0;overflow:hidden}
.em-bento__media{width:calc(100% + var(--space-8) * 2);max-width:none;margin:auto calc(var(--space-8) * -1) 0;
  height:236px;object-fit:cover}
.em-bento .em-btn{align-self:flex-start;margin-top:var(--space-2)}

/* ---------- Stories ---------- */
.em-stories{background:var(--surface-navy)}
.em-stories__head{display:grid;grid-template-columns:1fr auto;gap:var(--space-8);align-items:end;
  margin-bottom:var(--space-9)}
.em-stories__head h2{color:var(--text-inverse);margin:0 0 var(--space-4)}
.em-stories__head .em-eyebrow{color:var(--orange-300)}
.em-stories__head .em-lead{color:var(--text-inverse-muted);max-width:62ch;margin:0}

.em-stories__feature{display:grid;grid-template-columns:1.35fr 1fr;gap:var(--space-6);align-items:stretch}
.em-stories__lead-card{display:grid;grid-template-columns:256px 1fr;background:var(--surface-card);
  border-radius:var(--radius-card);overflow:hidden}
.em-stories__lead-card>img{width:100%;height:100%;object-fit:cover;min-height:336px}
.em-stories__lead-body{padding:var(--space-9) var(--space-8);display:flex;flex-direction:column;
  justify-content:center}
.em-stories__lead-body blockquote{margin:0 0 var(--space-6)}
.em-stories__lead-body blockquote p{margin:0;font-family:var(--font-display);
  font-weight:var(--fw-semibold);font-size:var(--fs-h3);line-height:1.4;
  letter-spacing:var(--ls-heading);color:var(--text-strong)}

.em-stories__attr{margin:0;font-family:var(--font-display);font-weight:var(--fw-bold);
  font-size:var(--fs-caption);letter-spacing:var(--ls-eyebrow);text-transform:uppercase;
  color:var(--em-orange)}
.em-stories__attr span{display:block;font-weight:var(--fw-medium);letter-spacing:var(--ls-caps);
  color:var(--text-muted)}
.em-stories__attr--sm{font-size:11px}

.em-stories__col{display:grid;grid-template-rows:1fr 1fr;gap:var(--space-6)}
.em-stories__mini{display:flex;align-items:center;gap:var(--space-5);background:var(--surface-card);
  border-radius:var(--radius-card);padding:var(--space-6)}
.em-stories__mini>img{width:80px;height:80px;flex:0 0 auto;border-radius:var(--radius-pill);
  object-fit:cover}
.em-stories__mini p{margin:0 0 var(--space-3);font-size:var(--fs-small);line-height:1.5;
  color:var(--text-body)}
/* Beats .em-stories__mini p specificity to restore orange colour and caption size */
.em-stories__mini .em-stories__attr{margin:0;color:var(--em-orange);font-size:var(--fs-caption)}
.em-stories__mini .em-stories__attr--sm{font-size:11px}

/* ---------- Insights ---------- */
.em-insights{display:grid;grid-template-columns:minmax(0,340px) 1fr;gap:var(--space-13);
  align-items:start}
.em-insights__aside h2{margin:0}
.em-insights__aside .em-rule{margin:var(--space-5) 0 var(--space-5)}
.em-insights__lede{font-size:var(--fs-lead);line-height:1.55;color:var(--text-body);
  margin:0 0 var(--space-7)}
.em-insights__podcast{margin-top:var(--space-9);grid-template-columns:auto 1fr;
  background:var(--surface-subtle)}
.em-insights__podcast .em-podcast__show,
.em-insights__podcast .em-podcast__title,
.em-insights__podcast .em-podcast__meta{display:block}
.em-insights__podcast .em-podcast__meta{margin-top:var(--space-3);font-size:var(--fs-small);
  line-height:1.55;color:var(--text-muted)}

.em-insights__rows{display:flex;flex-direction:column}
.em-insights__row{display:grid;grid-template-columns:184px 1fr;gap:var(--space-8);
  padding:var(--space-7) 0;border-bottom:1px solid var(--border-subtle)}
.em-insights__row:first-child{padding-top:0}
.em-insights__row:last-child{border-bottom:0;padding-bottom:0}
.em-insights__row>img{width:184px;height:172px;object-fit:cover;border-radius:var(--radius-media)}
.em-insights__row .em-article__meta{margin:0 0 var(--space-4);gap:var(--space-4)}
.em-insights__row .em-article__title{margin:0 0 var(--space-3);max-width:26ch}
.em-insights__row .em-article__excerpt{margin:0 0 var(--space-4);max-width:52ch}
.em-insights__row .em-article__more{text-decoration:none}

/* ---------- Join Us ----------
   Deliberately NOT the head-grid-plus-two-column-split every other section
   uses. Foundations, Stories and the previous Join layout all rendered as
   "one dominant panel left, two stacked cards right", which made the closing
   section read as a repeat of the Stories section directly above it. Here the
   page's last beat is a single stacked composition instead: one navy slab
   carrying the whole ask, then two quiet, un-carded ways in beneath it. The
   h2 lives inside the slab, so this section has no separate heading block. */
/* The tint starts a quarter of the way DOWN the slab, so the slab's top edge
   overhangs onto the white section above it instead of floating inside a
   coloured box. Same straddle the banner band and the solutions chevron use.

   Done with a margin pair rather than a gradient, because the line has to
   track the slab's own height and CSS cannot read that:

     wrap  margin-top   =  --section-y + overlap   (pushes the tint edge down)
     slab  margin-top   =  -overlap                (pulls the slab back up)

   The two cancel exactly. The slab lands where it always did, the row's outer
   height shrinks by the same overlap it was pulled up by, so the ways beneath
   it and every following section keep their spacing and the page height does
   not change. Only the colour edge moves. padding-top goes to 0 because the
   margin now carries that gap.

   The overlap is capped rather than a literal 25%: the slab is 299px at 1440
   but 583px once it stacks at 390px, and a true quarter there would be 146px
   of navy climbing into the section above. clamp() keeps it at a quarter where
   the composition was designed and lets it become a smaller lip on narrow
   screens, which is the proportion that still looks deliberate. */
.em-join-wrap{--join-overlap:clamp(40px,5.2vw,76px);
  background:var(--surface-tint);
  padding-top:0;
  margin-top:calc(var(--section-y) + var(--join-overlap))}
.em-join__slab{margin-top:calc(var(--join-overlap) * -1)}
.em-join{display:grid;gap:clamp(var(--space-9),4.5vw,var(--space-12))}

/* --surface-navy-deep, not the --surface-navy the Stories band and the footer
   use — the slab has to read as its own object on the tint, not as a third
   instance of the same navy panel. */
.em-join__slab{position:relative;isolation:isolate;overflow:hidden;
  display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,1fr);
  gap:clamp(var(--space-8),4vw,var(--space-12));align-items:center;
  background:var(--surface-navy-deep);border-radius:var(--radius-panel);
  padding:clamp(var(--space-9),4.5vw,var(--space-13)) clamp(var(--space-7),3.5vw,var(--space-12));
  --pattern-ink:var(--blue-600)}

/* ---------- Brand honeycomb ----------
   patterns/hex-lattice.svg is a true tile: 120 x 69.28 is the hexagon lattice's
   own period, so it repeats without a seam at any size. It is applied as a MASK,
   not as a background image, which is why one file serves every colour — the
   paint is --pattern-ink and the skin can retint it. That replaces the supplied
   assets/pattern-blue.png + pattern-orange.png pair, which are baked
   compositions rather than tiles (their empty half meets their dense half at
   every repeat) and need one raster per brand colour.

   The lattice is graduated, not one flat strength: it runs on the slab's
   diagonal, densest in the empty bottom-right corner and gone by the time it
   reaches the headline in the top-left. `to top left` follows the corner
   diagonal at whatever aspect the slab happens to be, so the direction holds
   from 1440 down to 320 without a per-breakpoint angle. The
   graduation lives in the PAINT, not in a second mask layer — a gradient
   background showing through the tile mask. Doing it with `mask-composite`
   would be the obvious route, but where that property is unsupported the two
   mask layers add instead of intersecting, and the fallback is a solid ink
   blob across the slab rather than a pattern.

   Contrast, measured over a lattice stroke rather than over the flat slab:
   white sits at 11.75:1 and --text-inverse-muted at 7.24:1. Even with the ink
   at full strength it is 6.56:1 for white, so the lattice cannot put the
   slab's copy under AA at any opacity. */
.em-join__slab::before{content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(to top left,
    var(--pattern-ink) 0%, var(--pattern-ink) 14%, transparent 66%);
  opacity:.7;
  -webkit-mask-image:url('../patterns/hex-lattice.svg');
  mask-image:url('../patterns/hex-lattice.svg');
  -webkit-mask-size:75px;mask-size:75px;
  -webkit-mask-repeat:repeat;mask-repeat:repeat}
.em-join__slab h2{margin:0 0 var(--space-5);color:var(--text-inverse);line-height:1.12}
/* Light type on dark reads a weight lighter; 1.65 against the 1.6 used for
   the same lead size on light surfaces. */
.em-join__lead{margin:0;font-size:var(--fs-lead);line-height:1.65;
  color:var(--text-inverse-muted);max-width:44ch}

.em-join__signup{gap:var(--space-4)}
.em-join__signup .em-newsletter__form{max-width:520px}
.em-join__signup .em-input{flex:1 1 220px}

.em-join__ways{display:grid;grid-template-columns:1fr 1fr;
  gap:clamp(var(--space-6),2.5vw,var(--space-8))}
/* The two descriptions run to different lengths. Without this the two actions
   sit at different heights and the pair reads as misaligned rather than as
   two peers; the paragraph absorbs the difference instead. */
/* A real surface, so the wash below has something to be contained by: without
   it the masked photo's right and bottom edges cut off in mid-air against the
   tint and read as a broken image rather than as atmosphere. */
.em-join__way{position:relative;isolation:isolate;overflow:hidden;
  background:var(--surface-card);border-radius:var(--radius-panel);
  padding:clamp(var(--space-7),3vw,var(--space-10));
  display:flex;flex-direction:column;align-items:flex-start;
  transition:box-shadow var(--dur) var(--ease-out),transform var(--dur) var(--ease-out)}
.em-join__way:hover,.em-join__way:focus-within{box-shadow:var(--shadow-md);
  transform:translateY(var(--lift-hover))}

/* Photographic wash, not a photo: the image is masked to a radial gradient
   anchored in the far corner, so it fades out well before it reaches the
   heading and the action. Both files are already loaded higher up the page,
   so this adds no bytes. Peak opacity is bounded on purpose. Measured, not
   assumed: the darkest source pixel anywhere under either paragraph,
   composited at the .26 hover opacity over --surface-card and treating the
   mask as fully opaque there (it isn't), leaves --text-body at 4.89:1 and
   5.09:1 against AA's 4.5:1. Raising the opacity past ~.3 breaks that. The partial
   desaturation is what keeps one golden-hour photo and one black-and-white
   one reading as a pair rather than as two unrelated pictures. */
.em-join__wash{position:absolute;inset:0;z-index:-1;width:100%;height:100%;
  object-fit:cover;object-position:center 34%;opacity:.22;filter:grayscale(.45);
  -webkit-mask-image:radial-gradient(135% 120% at 108% 112%,black 12%,transparent 74%);
  mask-image:radial-gradient(135% 120% at 108% 112%,black 12%,transparent 74%);
  transition:opacity var(--dur-slow) var(--ease-out),transform var(--dur-slow) var(--ease-out)}
.em-join__way:hover .em-join__wash,
.em-join__way:focus-within .em-join__wash{opacity:.26;transform:scale(1.04)}
/* Per-photo, because the two sources need different handling to read as the
   same treatment. The community photo is a tall frame whose subject sits low,
   so the crop is pulled down to the ball and the grass rather than to a
   cropped torso. The workshop photo is already light and would disappear into
   a white panel, so it is darkened a touch instead of made more opaque. */
.em-join__wash--community{object-position:center 62%}
.em-join__wash--work{object-position:center 40%;filter:grayscale(.45) brightness(.9) contrast(1.1)}
@media (prefers-reduced-motion:reduce){
  .em-join__wash{transition:opacity var(--dur) var(--ease-out)}
  .em-join__way:hover,.em-join__way:focus-within{transform:none}
  .em-join__way:hover .em-join__wash,
  .em-join__way:focus-within .em-join__wash{transform:none}
}
.em-join__way h3{margin:0 0 var(--space-4);font-size:var(--fs-h3)}
.em-join__way p{margin:0 0 var(--space-6);font-size:var(--fs-body);line-height:1.6;
  color:var(--text-body);max-width:44ch}
.em-join__way p:last-of-type{margin-bottom:auto;padding-bottom:var(--space-6)}

/* flex-end, not center: the label wraps to two lines in a narrow panel, and a
   centred arrow then floats in the gap beside it instead of trailing the last
   word. */
.em-join__action{display:inline-flex;align-items:flex-end;min-height:44px;
  font-family:var(--font-display);font-weight:var(--fw-semibold);font-size:var(--fs-lead);
  color:var(--text-strong);text-decoration:none;
  transition:color var(--dur) var(--ease-out)}
/* The rule belongs to the label, not the whole link: an underline inherited by
   the arrow draws a second stub under the glyph and follows it as it moves. */
.em-join__action-label{text-decoration:underline;text-decoration-color:var(--em-orange);
  text-decoration-thickness:2px;text-underline-offset:6px;
  transition:text-decoration-color var(--dur) var(--ease-out)}
.em-join__action:hover{color:var(--em-orange-ink)}
.em-join__action:hover .em-join__action-label{text-decoration-color:var(--em-orange-ink)}
.em-join__arrow{margin-left:var(--space-3);
  transition:transform var(--dur) var(--ease-out)}
.em-join__action:hover .em-join__arrow{transform:translateX(4px)}
@media (prefers-reduced-motion:reduce){
  .em-join__arrow{transition:none}
  .em-join__action:hover .em-join__arrow{transform:none}
}

/* ---------- Responsive ---------- */
/* Breakpoints are an assumption — the source design is desktop-1440-only.
   .em-process is the exception: its five steps carry min-width:218px and
   -34px overlaps, so the strip needs ~954px to render horizontally
   (5*218 - 4*34). The container (--container-max 1200px minus two
   --gutter of 24px) only clears that floor once the viewport is >=~1002px,
   so the chevron keeps its own, higher stacking breakpoint of 1150px —
   comfortably above the measured overflow threshold — while every other
   section keeps the 1200/900/600 set. */
@media (max-width:1200px){
  .em-hero{grid-template-columns:1fr 1fr}
  .em-hero__copy{padding:var(--section-y) var(--space-8)}
  .em-hero__northstar{left:auto;right:var(--space-6);bottom:var(--space-6);width:280px}
  .em-insights{grid-template-columns:minmax(0,300px) 1fr;gap:var(--space-11)}
}

@media (max-width:1150px){
  /* Five-across chevrons cannot survive here. Deliberate substitution:
     a vertical numbered stack with the detail always visible. */
  .em-process{flex-direction:column;height:auto}
  .em-process__step,.em-process__step:first-child{clip-path:none;min-height:200px;min-width:0}
  .em-process__step + .em-process__step{margin-left:0;margin-top:var(--space-3)}
  .em-process__step:hover,.em-process__step:focus-within{flex:1 1 auto}
  .em-process__inner{padding:var(--space-6) var(--space-6) var(--space-6)}
  .em-process__detail{position:static;width:auto;opacity:1;transform:none;pointer-events:auto;
    margin-top:var(--space-4)}
  .em-process__num{opacity:1}
}

@media (max-width:900px){
  .em-solutions__head,.em-foundations__head,.em-stories__head{
    grid-template-columns:1fr;gap:var(--space-6)}

  .em-hero{grid-template-columns:1fr}
  .em-hero__copy{padding:var(--section-y) var(--gutter);order:1}
  .em-hero__media{order:2;min-height:380px}
  .em-hero__northstar{position:static;width:auto;margin:var(--space-6) var(--gutter) 0}

  .em-bento{grid-template-columns:1fr}
  .em-bento__col{grid-template-rows:auto auto}
  .em-stories__feature{grid-template-columns:1fr}
  .em-stories__col{grid-template-rows:auto auto}
  .em-insights{grid-template-columns:1fr;gap:var(--space-9)}
  .em-join__slab{grid-template-columns:1fr;gap:var(--space-8)}
  .em-join__lead{max-width:none}
  .em-join__ways{grid-template-columns:1fr;gap:var(--space-5)}
  /* Stacked, the panel is taller relative to its width and the corner
     gradient reads differently: anchored just below the bottom edge it keeps
     the wash under and beside the action instead of climbing behind the
     paragraph, and the wider extent stops it disappearing altogether on the
     narrowest phones. Slightly more opaque because far less of it is visible.
     .24 stays under the .26 the contrast measurement was taken at. */
  .em-join__wash{opacity:.24;
    -webkit-mask-image:radial-gradient(145% 95% at 96% 108%,black 10%,transparent 74%);
    mask-image:radial-gradient(145% 95% at 96% 108%,black 10%,transparent 74%)}
}

@media (max-width:600px){
  .em-hero h1{font-size:var(--fs-h1)}
  .em-stories__lead-card{grid-template-columns:1fr}
  .em-stories__lead-card>img{height:220px;min-height:0}
  .em-stories__mini{flex-direction:column;align-items:flex-start}
  .em-insights__row{grid-template-columns:1fr;gap:var(--space-5)}
  .em-insights__row>img{width:100%}
  /* The subscribe button drops below the field rather than sharing the row
     with it — .em-newsletter__form wraps, but a 220px flex basis keeps the
     input on the button's line until it cannot fit at all. */
  .em-join__signup .em-input{flex:1 1 100%}
  /* At --fs-lead the ambassador label needs ~250px against a ~263px panel and
     wraps on the tightest phones. One step down keeps it on a single line. */
  .em-join__action{font-size:var(--fs-body)}
}

/* ---------- Accessibility overrides, colour half ----------
   Placed last on purpose: several rules above set --em-orange on these same
   selectors, so this has to win on source order. See the block at the top of
   this file for the measurements and the rationale.

   --em-orange-ink is the brand orange at 80% lightness: 5.17:1 on white and
   4.54:1 on --surface-tint, against 3.59:1 and 3.15:1 for the original.
   Applied ONLY to small orange text on light surfaces. Orange fills,
   orange-on-navy (--orange-300) and the focus ring keep the exact brand
   value. */
.em-hero__northstar-label,
.em-insights__podcast .em-podcast__show,
.em-stories__mini .em-stories__attr,
.em-stories__lead-body .em-stories__attr{color:var(--em-orange-ink)}
.em-hero__link:hover{color:var(--em-orange-ink)}
/* On navy, the original orange ramp already passes (5.44:1) — keep it. */
.em-stories__head .em-eyebrow{color:var(--orange-300)}

/* ---------- Section eyebrow rhythm ----------
   All six sections shipped the same 13px uppercase tracked orange kicker,
   which is the saturated landing-page tell regardless of the copy being real.
   The hero keeps it as the brand kicker; the stories section keeps it on navy.
   The remaining sections become a quieter sentence-case lead-in, so the
   pattern reads as deliberate rather than as scaffolding. Every string from
   the roadmap copy deck is preserved. Join Us now carries no kicker at all —
   its headline opens the slab directly. */
.em-solutions__head .em-eyebrow,
.em-foundations__head .em-eyebrow,
.em-insights__aside .em-eyebrow{font-family:var(--font-body);font-weight:var(--fw-semibold);
  font-size:var(--fs-small);letter-spacing:normal;text-transform:none;color:var(--text-body)}
