/* ============================================================
   ARGameson — Matchday Scoreboard
   Poster/brutalist archetype. Bricolage Grotesque display / Instrument
   Sans body.

   PALETTE (revised 2026-08-06 at client request): logo PURPLE leads,
   carried on a neutral grey ramp and white. Logo MAGENTA is an accent
   only — the client asked for markedly less pink.

   The magenta budget is deliberately small and enumerated. It survives in
   exactly seven places, each earning it by being tiny or semantic:
     1. .eyebrow::before   — the 26x2px section tick
     2. :focus-visible     — accessibility ring, wants maximum salience
     3. .job-card .jc-n    — the four oversized numerals. ⚠️ NARROWED
        2026-08-08: while the pinned route narrative is running, only the
        ACTIVE step's numeral is magenta — spent steps are --purple-lift and
        upcoming ones --grey — so at most ONE is pink at a time instead of
        four. Not a new consumer; a smaller version of this one.
     4. .wv-dot / .wv-ping — the DJX4 station pin on the map
                           (+ .wv-legend .sw--pin, the same pin in the legend)
     5. input:user-invalid — error state
     6. .form-status.err   — error text
     7. the .boxgrid cursor trail — 15% of lit cells, set in js/backdrops.js
        (PINK_SHARE). Added at the client's request 2026-08-06; it earns the
        exception by being transient and cursor-driven rather than static.
   Anything else that reads magenta is a regression. Grep before adding one.
   ============================================================ */

/* ============================ TOKENS ============================ */
:root{
  /* Neutral spine — the greys and white now carry the page.
     Previously these were purple-tinted (#0C0713 etc.); they are true
     neutrals now so purple reads as a deliberate brand mark against them
     rather than as a slightly-more-saturated background. */
  --ink:        #0E0E11;
  --ink-2:      #17171C;
  --ink-3:      #26262E;
  --grey:       #6E6E7A;
  --white:      #FFFFFF;
  --bone:       #F2F2F5;
  --bone-dim:   #B9B9C4;

  /* Brand — purple is primary. #573C81 is the logo value and is too dark
     for text on near-black, so --purple-lift carries every foreground use
     (4.6:1 on --ink, passes AA for normal text); --purple carries fills. */
  --purple:     #573C81;
  --purple-lift:#8B6BC4;

  /* Accent only — see the enumerated budget above. */
  --magenta:    #E81D75;
  --magenta-lift:#FF4D93;

  --display:'Bricolage Grotesque',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
  /* Tactical stencil, hero headline only. Condensed with true stencil breaks, so it
     sets far narrower than Bricolage at the same size — .h1-line-a compensates with a
     larger clamp. Fallback is --display's stack, NOT a generic: if the webfont is slow
     the headline still lands in the brand face rather than Times. */
  --stencil:'Big Shoulders Stencil','Bricolage Grotesque',system-ui,'Segoe UI',Roboto,sans-serif;
  --body:'Instrument Sans',system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
  --mono:ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,monospace;

  --wrap:1240px;
  --nav-h:92px;
  --ease:cubic-bezier(.16,.84,.24,1);
}

*{box-sizing:border-box}
/* Kept, and verified compatible with the pinned sections added 2026-08-08:
   re-introducing it after the pins were fixed reproduced no measurement
   error. It is a common suspect when ScrollTrigger pins misbehave, so if
   pin positions ever look wrong, check refreshPriority in js/main.js FIRST
   (higher refreshes first) before blaming this line. */
html{scroll-behavior:smooth}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

body{
  margin:0;font-family:var(--body);color:var(--bone);background:var(--ink);
  -webkit-font-smoothing:antialiased;line-height:1.6;overflow-x:hidden;
}
h1,h2,h3{line-height:1.05;margin:0;font-family:var(--display)}
a{color:inherit;text-decoration:none}
img{max-width:100%;display:block}
button{font:inherit}
.wrap{max-width:var(--wrap);margin-inline:auto;padding-inline:clamp(20px,5vw,48px)}
.section{padding:clamp(64px,10vw,120px) 0;position:relative}
.section[id],#top{scroll-margin-top:var(--nav-h)}

.eyebrow{font-family:var(--body);font-weight:600;color:var(--purple-lift);font-size:.82rem;
  letter-spacing:.14em;text-transform:uppercase;display:inline-flex;align-items:center;gap:.6rem}
.eyebrow::before{content:"";width:26px;height:2px;background:var(--magenta);display:inline-block}
.sect-title{font-weight:800;color:var(--bone);font-size:clamp(2rem,5vw,3.4rem);margin:.5rem 0 .3rem;
  letter-spacing:-.02em}
.sect-lead{color:var(--bone-dim);max-width:62ch;font-size:1.05rem}

/* ============================ A11Y / FOCUS ============================ */
.skip{position:absolute;left:-999px;top:0;background:var(--purple);color:var(--white);font-weight:700;
  padding:.8rem 1.1rem;z-index:300;border-radius:0 0 10px 0}
.skip:focus{left:0}
:focus-visible{outline:3px solid var(--magenta-lift);outline-offset:3px;border-radius:4px}

/* ============================ BUTTONS ============================ */
.btn{display:inline-flex;align-items:center;gap:.55rem;font-family:var(--display);font-weight:700;
  font-size:.98rem;padding:.9rem 1.6rem;border-radius:8px;cursor:pointer;border:2px solid transparent;
  transition:transform .2s var(--ease),background-color .2s var(--ease),color .2s var(--ease),
    box-shadow .2s var(--ease),border-color .2s var(--ease);white-space:nowrap}
.btn-primary{background:var(--purple);color:var(--white);box-shadow:0 12px 28px -14px rgba(87,60,129,.65)}
.btn-primary:hover{background:var(--purple-lift);transform:translateY(-2px)}
.btn-primary:active{transform:translateY(0) scale(.98)}
.btn-ghost{background:transparent;color:var(--bone);border-color:rgba(242,242,245,.3)}
.btn-ghost:hover{background:rgba(242,242,245,.08);border-color:var(--bone)}
.btn-ghost:active{transform:scale(.98)}
.btn-outline-ink{background:transparent;color:var(--ink);border-color:rgba(14,14,17,.25)}
.btn-outline-ink:hover{background:rgba(14,14,17,.08)}
.btn:active{transform:translateY(1px)}

/* ============================ NAV ============================ */
.nav{position:fixed;inset:0 0 auto 0;z-index:200;height:var(--nav-h);display:flex;align-items:center;
  transition:background-color .3s var(--ease),box-shadow .3s var(--ease),backdrop-filter .3s var(--ease)}
.nav.scrolled{background:rgba(14,14,17,.88);backdrop-filter:blur(14px);box-shadow:0 1px 0 rgba(242,242,245,.1)}
.nav-in{display:flex;align-items:center;justify-content:space-between;width:100%}
.brand{display:inline-flex;align-items:center;gap:.6rem}
.brand-mark{width:30px;height:30px;object-fit:contain;flex-shrink:0}
.brand-word{height:20px;width:auto;display:block}
/* TODO(owner): once argm-wordmark-v1 dimensions are known, consider setting
   explicit width/height attributes on .brand-word to remove the tiny CLS
   risk from an unsized inline image (kept height-only per build spec since
   the wordmark's aspect ratio isn't known yet). */
.nav-links{display:flex;align-items:center;gap:1.8rem}
.nav-links a:not(.btn){font-family:var(--display);font-weight:600;font-size:.95rem;color:var(--bone-dim);
  position:relative;padding:.2rem 0;transition:color .2s var(--ease)}
.nav-links a:not(.btn)::after{content:"";position:absolute;left:0;bottom:-3px;width:0;height:2px;
  background:var(--purple-lift);transition:width .25s var(--ease)}
.nav-links a:not(.btn):hover,.nav-links a:not(.btn):focus-visible{color:var(--bone)}
.nav-links a:not(.btn):hover::after,.nav-links a:not(.btn):focus-visible::after{width:100%}
.nav-cta-group{display:flex;align-items:center;gap:1.1rem}
/* The nav CTA floats over the hero chevron, which is painted in --purple, so
   the standard purple .btn-primary lost its edges entirely against it. Bone on
   ink holds up over the chevron, the bare dark nav, and the scrolled backdrop
   alike — and it makes the single most important button on the page the
   highest-contrast thing in the header. Scoped to .nav: every other
   .btn-primary sits on flat ink and stays purple. */
.nav .btn-primary{background:var(--bone);color:var(--ink);
  box-shadow:0 10px 26px -12px rgba(0,0,0,.7)}
.nav .btn-primary:hover{background:var(--white);color:var(--ink)}
.nav-tel{font-family:var(--display);font-weight:600;font-size:.9rem;color:var(--bone-dim);
  display:none;align-items:center;gap:.4rem}
.nav-tel svg{width:16px;height:16px;flex-shrink:0}
@media (min-width:980px){.nav-tel{display:inline-flex}}
/* Employee Login is a utility destination, not a marketing link — it is
   deliberately quieter than the four section links and sits inside the CTA
   group so it reads as "account", not "content". Hidden below 980px, where
   the mobile panel carries it instead. */
/* No divider rule. It sat between this link and the Apply button and read as a
   stray 1px artifact rather than a separator — the .nav-cta-group gap already
   does the separating. The lock icon carries the "this is an account thing"
   signal on its own. */
.nav-util{display:none;align-items:center;gap:.4rem;font-family:var(--display);
  font-weight:600;font-size:.86rem;color:var(--bone-dim);white-space:nowrap;
  transition:color .2s}
.nav-util svg{width:15px;height:15px;flex-shrink:0}
.nav-util:hover{color:var(--bone)}
@media (min-width:901px){.nav-util{display:inline-flex}}
/* Panel links are 1.6rem/700; the utility link must not compete with them.
   :not(.btn) is carried here purely for specificity — `.mobile-panel a:not(.btn)`
   below is 0,2,1 and is declared LATER in the file, so an equal-specificity rule
   loses on source order and the link renders at the full 1.6rem. */
.mobile-panel a.mobile-panel-util:not(.btn){font-family:var(--display);font-weight:600;
  font-size:1rem;color:var(--bone-dim);margin-top:.4rem}
.nav-toggle{display:none;background:none;border:0;cursor:pointer;padding:8px;color:var(--bone)}
.nav-toggle svg{width:26px;height:26px}

/* ============================ MOBILE MENU ============================ */
.mobile-panel{position:fixed;inset:0;z-index:210;background:var(--ink);display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:1.6rem;transform:translateY(-100%);
  transition:transform .35s var(--ease);padding:2rem}
.mobile-panel.open{transform:translateY(0)}
.mobile-panel a:not(.btn){font-family:var(--display);font-weight:700;font-size:1.6rem;color:var(--bone)}
.mobile-panel a.btn{margin-top:1rem}
.mobile-panel-close{position:absolute;top:20px;right:20px;background:none;border:0;color:var(--bone);
  cursor:pointer;padding:10px}
.mobile-panel-close svg{width:26px;height:26px}

/* ============================ HERO ============================ */
.hero{position:relative;z-index:0;padding-top:calc(var(--nav-h) + clamp(28px,6vw,64px));
  padding-bottom:clamp(40px,7vw,80px);overflow:hidden;background:var(--ink)}
.hero-chevron{position:absolute;z-index:0;right:-22%;bottom:-26%;width:120vw;max-width:1500px;
  aspect-ratio:1;background-color:var(--purple);
  -webkit-mask-image:url('/assets/img/argm-mark-mask-v1.png');mask-image:url('/assets/img/argm-mark-mask-v1.png');
  -webkit-mask-size:contain;mask-size:contain;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:center;mask-position:center;opacity:.85;pointer-events:none;will-change:transform}
/* Scrim: keeps the copy column on solid ink so the chevron emerges to the RIGHT of
   the text instead of cutting a hard purple/black seam through the sub-headline and
   body paragraph. Large bone display type over purple is fine (7.7:1); the small
   bone-dim copy is what needed rescuing. Sits above the chevron (z0), below .hero-in (z2). */
.hero::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(90deg,rgb(14,14,17) 0%,rgb(14,14,17) 40%,rgba(14,14,17,.85) 58%,rgba(14,14,17,0) 80%)}
@media (max-width:900px){.hero::after{display:none}}
.hero-in{position:relative;z-index:2}
.hero-eyebrow{color:var(--purple-lift);font-family:var(--body);font-weight:700;font-size:.8rem;
  letter-spacing:.16em;text-transform:uppercase;display:block;margin-bottom:1.1rem}
.hero-h1{margin:0}
/* ⚠️ NO `overflow: clip` here. It used to carry one, which made the headline
   fail silently and unrecoverably: whenever the rendered line was wider than
   the column, the tail of the line — the period first — was simply cut off
   instead of wrapping. That happens more often than it looks, because the font
   is loaded `display=optional`: if Bricolage isn't already cached the browser
   renders the FALLBACK for the whole pageview and never swaps, and the
   fallback sets ~25% wider than Bricolage at wdth 80. So the same headline is
   comfortable on a machine with the font cached and clipped on one without.
   Without the clip it wraps instead, which is graceful at every width.
   The reveal does not need this: SplitText is created with mask:"chars" and
   builds its own per-character clipping wrappers. */
.h1-line{display:block}
/* GSAP SplitText (mask:"chars"/"words") wraps each piece in a div it sets to
   `overflow: clip`, sized to the character's ADVANCE width. This headline runs
   letter-spacing:-.03em — about -5px at 168px — which pulls that box in tighter
   than the glyph ink, so any character whose ink reaches its advance edge is
   sliced vertically. In Bricolage the 'S' terminal and the full stop are the
   two worst: the period rendered as a D instead of a dot.
   Widening the padding box moves the clip edge out without moving the glyph or
   changing the advance (the negative margin gives back exactly what the padding
   adds). Deliberately NOT overflow-clip-margin, which is newer and which I
   can't test in Frank's Safari.
   Safe for the reveal: chars animate in from a full line BELOW (~188px), which
   is vertical, and this only widens the box horizontally. */
.h1-line > div{padding-inline:.08em;margin-inline:-.08em}
/* line-height must stay ABOVE the font's ink extent (~1.06em at this weight and
   width), because SplitText's mask:"chars" wrappers inherit the line box as
   their clip box — at the old .92 the caps were sliced flat across the top and
   the full stop lost its foot. The box is 1.12 and the extra .20em is pulled
   back with negative margins, so the headline occupies exactly the same .92em
   of layout as before and nothing below it shifts.
   ⚠️ If the font, weight, wdth axis or letter-spacing changes, re-check that
   scrollHeight <= clientHeight on .h1-line-a before assuming this still holds. */
/* ⚠️ Big Shoulders Stencil, NOT Bricolage — the client asked for a tactical
   treatment on this line only (line B stays Bricolage for contrast).
   Three things changed together and none of them is optional:
   • No "wdth" axis on this family (it ships opsz 10..72 + wght 100..900 only).
     Leaving the old wdth 80 in would be ignored here but still applies to the
     Bricolage fallback, so the axes are declared per-font, not shared.
   • opsz caps at 72, not 96. Asking for 96 clamps silently to 72; stated
     explicitly so the value isn't mistaken for a typo later.
   • letter-spacing moved -.03em -> .005em. The face is already condensed, and
     negative tracking closes the stencil breaks until the counters read as
     solid ink at hero size — the whole point of the treatment is those gaps. */
.h1-line-a{font-family:var(--stencil);font-weight:800;color:var(--bone);
  /* Sized by measurement, not by eye. 'THE GAME IS ON.' sets 6.02x its font-size
     wide in this face at this tracking, and the hero line box is viewport-296px
     (capping at 1144px once .wrap hits its max-width). 12.2vw keeps it inside
     that at every width, and the 11.5rem ceiling holds it under the 190px that
     the capped line box allows on wide screens. Anything larger wraps and
     orphans the 'N.' onto its own line. Re-measure if the string ever changes. */
  letter-spacing:.005em;font-size:clamp(2.6rem,12.2vw,11.5rem);
  /* MEASURED, do not eyeball: this face's ink extent is 1.127em at wght 800
     (scrollHeight 198px against a 175.7px font-size). SplitText's mask:"chars"
     wrappers inherit this line box as their clip box, so a line-height under
     that slices the cap tops and the full stop's foot — the same failure the
     Bricolage note above describes.
     MEASURE IT AFTER SplitText HAS RUN, not before: the split wrappers carry the
     padding-inline hack below, and the post-split extent is 1.19em, not the
     1.127em the raw text reports. 1.26 clears the real number with the same
     ~.07em margin the old Bricolage value carried. The negative margin-block
     gives back the extra .34em so the headline still occupies .92em of layout
     and nothing below it moves. */
  line-height:1.26;margin-block:-.17em;
  font-variation-settings:"wght" 800,"opsz" 72}
@supports not (font-variation-settings:normal){.h1-line-a{font-variation-settings:normal}}
.h1-line-b{font-weight:600;color:var(--bone-dim);letter-spacing:-.01em;font-family:var(--display);
  font-size:clamp(1.15rem,2.7vw,2.1rem);line-height:1.2;margin-top:.6rem}
.hero-sub{font-size:1.08rem;color:var(--bone-dim);max-width:56ch;margin:1.6rem 0 0}
.hero-cta{display:flex;gap:1rem;flex-wrap:wrap;margin-top:2rem}
.hero-cta .btn svg{width:18px;height:18px}
.hero-chips{display:flex;flex-wrap:wrap;gap:.7rem;margin-top:2.2rem;list-style:none;padding:0}
.hero-chip{font-family:var(--display);font-weight:600;font-size:.82rem;color:var(--bone-dim);
  background:var(--ink-2);border:1px solid var(--ink-3);border-radius:999px;padding:.55rem 1.1rem;
  letter-spacing:.01em}

/* ============================ SPLIT-FLAP BOARD ============================ */
.flapboard-wrap{position:relative;z-index:3;margin-top:clamp(-30px,-4vw,-16px)}
.flapboard{background:var(--ink-2);border-top:1px solid var(--ink-3);border-bottom:1px solid var(--ink-3);
  padding:clamp(14px,2.6vw,24px) 0;box-shadow:0 -20px 40px -30px rgba(0,0,0,.6)}
.flapboard-track{display:flex;justify-content:center;flex-wrap:wrap;gap:clamp(3px,.6vw,6px)}
.flap-cell{position:relative;width:clamp(15px,4.3vw,36px);height:clamp(24px,6.6vw,54px);
  background:var(--ink);border-radius:3px;overflow:hidden;perspective:220px;
  box-shadow:inset 0 0 0 1px var(--ink-3)}
.flap-cell::after{content:"";position:absolute;left:0;right:0;top:50%;height:1px;
  background:rgba(0,0,0,.55);box-shadow:0 1px 0 rgba(242,242,245,.06);z-index:3;pointer-events:none}
.flap-static,.flap-front{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-family:var(--mono);font-weight:700;color:var(--bone);
  font-size:clamp(.72rem,2.2vw,1.5rem);letter-spacing:0;user-select:none}
.flap-static{z-index:1}
.flap-flip{position:absolute;inset:0;z-index:2;transform:rotateX(0deg);transform-origin:center;
  transform-style:preserve-3d;backface-visibility:hidden;
  transition:transform .13s cubic-bezier(.6,.04,.98,.335)}
.flap-flip.is-flipping{transform:rotateX(-179.9deg)}
.flap-flip.no-anim{transition:none!important}
.flapboard.is-static .flap-flip{display:none}

/* ============================ TICKER / MARQUEE ============================ */
.ticker{background:var(--purple);color:var(--white);overflow:hidden;padding:.85rem 0;position:relative;z-index:3}
.ticker-track{display:inline-flex;white-space:nowrap;font-family:var(--display);font-weight:700;
  font-size:.92rem;letter-spacing:.05em;text-transform:uppercase;
  animation:march var(--march-dur,26s) linear infinite;animation-play-state:paused;will-change:transform}
.ticker-track.is-ready{animation-play-state:running}
.ticker-track span::after{content:"\25C6";margin-inline:2.2rem;opacity:.6}
@keyframes march{to{transform:translateX(-50%)}}

/* ============================ WHAT WE DO ============================ */
.wwd-lede{max-width:70ch}
.wwd-lede p{font-size:1.08rem;color:var(--bone-dim);margin:0 0 1.1rem}
.wwd-lede p strong{color:var(--bone);font-weight:700}
.wwd-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(28px,5vw,64px);align-items:center;
  margin-top:2.6rem}
.wwd-blocks{display:grid;gap:1rem}
/* 🚨 Hover lifts on .batch-card elements ride the independent `translate`
   property, NOT `transform` — and the transition must name `translate` to
   match. These cards get their entrance from the ScrollTrigger.batch
   gsap.fromTo() in main.js, which writes `transform`. A CSS transition on
   `transform` makes every value GSAP writes lag ~250ms behind, and GSAP then
   samples that lagging computed value as the card's resting position, so the
   entrance finishes with the card parked anywhere from 3px to the full 22px
   off its grid row — visibly misaligned until a reload happens to land the
   sampling on a clean frame. `translate` composes with `transform` at paint
   time, so the two never touch the same property. The managed sections solved
   the same collision by killing hover instead (.is-narrative / .is-road
   below); these cards have no managed mode, so they need their own lane. */
.wwd-block{background:var(--ink-2);border:1px solid var(--ink-3);border-radius:14px;padding:1.3rem 1.4rem;
  transition:translate .25s var(--ease),border-color .25s var(--ease)}
.wwd-block:hover{translate:0 -4px;border-color:var(--purple-lift)}
.wwd-block .wb-n{font-family:var(--display);font-weight:800;color:var(--purple-lift);font-size:.85rem;
  letter-spacing:.08em;text-transform:uppercase}
.wwd-block h3{font-size:1.15rem;color:var(--bone);margin:.4rem 0 .4rem}
.wwd-block p{margin:0;color:var(--bone-dim);font-size:.95rem}
.photo-frame{position:relative;width:100%;border-radius:16px;overflow:hidden;background:var(--ink-2);
  border:1px solid var(--ink-3)}
.photo-frame img{width:100%;height:100%;object-fit:cover;display:block}
.photo-frame.van-photo{aspect-ratio:4/3}
.photo-frame.driver-photo{aspect-ratio:3/4}

/* ============================ THE JOB ============================ */
.job-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:1.2rem;margin-top:2.8rem}
.job-card{background:var(--ink-2);border:1px solid var(--ink-3);border-radius:16px;
  padding:1.7rem 1.4rem;transition:transform .25s var(--ease),box-shadow .25s var(--ease),border-color .25s var(--ease)}
.job-card:hover{transform:translateY(-6px);box-shadow:0 20px 40px -24px rgba(87,60,129,.45);
  border-color:var(--purple)}
.job-card:nth-child(2){margin-top:1.6rem}
.job-card:nth-child(4){margin-top:1.6rem}
.job-card .jc-n{font-family:var(--display);font-weight:800;font-size:2.2rem;color:var(--magenta);
  letter-spacing:-.02em;line-height:1}
.job-card h3{font-size:1.15rem;color:var(--bone);margin:.7rem 0 .5rem}
.job-card p{margin:0;color:var(--bone-dim);font-size:.94rem}

/* ============================ WHY ARGAMESON ============================ */
.why-layout{display:grid;grid-template-columns:.8fr 1.2fr;gap:clamp(24px,4vw,48px);align-items:start;
  margin-top:2.6rem}
.why-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:1.1rem;margin-top:0}
/* `translate`, not `transform` — see the .wwd-block note above. This is the
   grid Frank saw land out of alignment. */
.why-card{background:var(--ink-2);border:1px solid var(--ink-3);border-radius:14px;padding:1.5rem 1.5rem;
  display:flex;gap:1rem;align-items:flex-start;transition:translate .25s var(--ease),border-color .25s var(--ease)}
.why-card:hover{translate:0 -4px;border-color:var(--purple-lift)}
.why-card .wc-ic{width:42px;height:42px;border-radius:10px;background:var(--ink-3);display:grid;
  place-items:center;flex-shrink:0}
.why-card .wc-ic svg{width:22px;height:22px;color:var(--purple-lift)}
.why-card h3{font-size:1.05rem;color:var(--bone);margin:0 0 .3rem}
.why-card p{margin:0;color:var(--bone-dim);font-size:.92rem}

/* ---- pay module (built, shipped commented out — see index.html) ---- */
.pay-block{background:var(--purple);border-radius:18px;padding:clamp(24px,3.4vw,36px);margin-top:2.4rem;
  color:var(--bone)}
.pay-block h3{color:var(--bone);font-size:1.3rem;margin:0 0 .6rem}
.pay-block p{color:var(--bone);opacity:.92;margin:0 0 1rem}
.pay-figures{display:flex;gap:2rem;flex-wrap:wrap}
.pay-figure .pf-label{font-size:.78rem;letter-spacing:.1em;text-transform:uppercase;opacity:.8}
.pay-figure .pf-value{font-family:var(--display);font-weight:800;font-size:2rem}

/* ============================ REQUIREMENTS ============================ */
.req-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1.1rem;margin-top:2.6rem}
.req-card{background:var(--ink-2);border:1px solid var(--ink-3);border-radius:14px;padding:1.4rem 1.5rem;
  display:flex;gap:.9rem;align-items:flex-start;transition:transform .25s var(--ease),border-color .25s var(--ease)}
.req-card:hover{transform:translateY(-4px);border-color:var(--purple)}
.req-card svg{width:22px;height:22px;color:var(--purple-lift);flex-shrink:0;margin-top:.1rem}
.req-card p{margin:0;color:var(--bone-dim);font-size:.95rem}

/* ============================ ABOUT US ============================
   Client-requested section. Purple only — the magenta budget at the top of this
   file is closed, and the eyebrow rule already spends its allocation. */
.about-grid{display:grid;grid-template-columns:1.15fr .85fr;gap:clamp(28px,5vw,64px);
  align-items:start;margin-top:2.4rem}
.about-lede p+p{margin-top:1.1rem;color:var(--bone-dim)}
.about-facts{list-style:none;padding:0;margin:0;display:grid;grid-template-columns:1fr 1fr;gap:1rem}
.about-fact{background:var(--ink-2);border:1px solid var(--ink-3);border-left:3px solid var(--purple);
  border-radius:14px;padding:1.2rem 1.15rem;display:grid;gap:.3rem}
.about-fact .af-v{font-family:var(--display);font-weight:800;font-size:1.75rem;color:var(--bone);
  line-height:1}
.about-fact .af-l{font-size:.85rem;color:var(--bone-dim);line-height:1.35}
/* Mission/Values pair — live CSS for a block that ships commented out in the HTML.
   Kept here on purpose so enabling it is a pure markup edit with no styling work. */
.about-mv{display:grid;grid-template-columns:1fr 1fr;gap:1.2rem;margin-top:2.4rem}
.mv-card{background:var(--ink-2);border:1px solid var(--ink-3);border-radius:18px;
  padding:clamp(22px,3vw,30px)}
.mv-card p{margin:.9rem 0 0;color:var(--bone-dim)}
.mv-list{list-style:none;padding:0;margin:.9rem 0 0;display:grid;gap:.7rem}
.mv-list li{position:relative;padding-left:1.6rem;color:var(--bone-dim)}
.mv-list li::before{content:"";position:absolute;left:0;top:.55em;width:8px;height:8px;
  border-radius:2px;background:var(--purple-lift)}

/* ============================ THE ROLE (job ad) ============================
   Ported from the Stork Logistics build. Structure is intentionally identical;
   only the tokens changed (amber/indigo -> purple, paper -> ink). */
.role{background:var(--ink-2)}
.role-head{max-width:66ch}
.role-specs{display:grid;grid-template-columns:repeat(4,1fr);gap:1.1rem;margin-top:2.4rem}
.spec{background:var(--ink);border:1px solid var(--ink-3);border-top:3px solid var(--purple);
  border-radius:16px;padding:1.35rem 1.3rem}
.spec .sl{font-family:var(--display);font-size:.74rem;font-weight:700;letter-spacing:.09em;
  text-transform:uppercase;color:var(--purple-lift)}
.spec .sv{font-family:var(--display);font-weight:700;color:var(--bone);line-height:1.12;
  font-size:clamp(1.15rem,2.1vw,1.4rem);margin:.5rem 0 .3rem}
.spec .sn{margin:0;color:var(--bone-dim);font-size:.88rem}
.role-perks{display:flex;gap:.7rem;flex-wrap:wrap;margin-top:1.7rem}
.role-perks span{font-family:var(--display);font-weight:600;font-size:.85rem;color:var(--bone);
  background:var(--ink);border:1px solid var(--ink-3);border-radius:999px;padding:.5rem 1rem}
.role-cols{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,5vw,64px);margin-top:2.8rem}
.role-list h3{font-size:1.2rem;color:var(--bone)}
.role-list ul{list-style:none;padding:0;margin:1.1rem 0 0;display:grid;gap:.85rem}
.role-list li{position:relative;padding-left:1.9rem;color:var(--bone-dim);font-size:.99rem}
/* Border-drawn check rather than an icon font or inline SVG per bullet — no extra
   markup, and it disappears cleanly under forced-colors. */
.role-list li::before{content:"";position:absolute;left:1px;top:.4em;width:9px;height:5px;
  border-left:2.2px solid var(--purple-lift);border-bottom:2.2px solid var(--purple-lift);
  transform:rotate(-45deg)}
.role-cta{margin-top:2.8rem}

/* ============================ WHERE WE RUN ============================ */
.where-grid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,5vw,64px);align-items:center;
  margin-top:2.4rem}
.where-card{background:var(--ink-2);border:1px solid var(--ink-3);border-radius:18px;
  padding:clamp(24px,3vw,34px)}
/* align-items:baseline is the alignment fix, not the grid itself — the grid was
   already here. dt renders at .82rem uppercase and dd at 1rem, so with the
   default `stretch` each cell laid its text out from its own line-box top and
   the label sat a few px above the value it belonged to. Baseline locks the two
   to a shared baseline per row; where the address wraps, it binds to the first
   line, which is the intended read. */
.where-card dl{display:grid;grid-template-columns:auto 1fr;gap:.7rem 1.2rem;
  align-items:baseline;margin:1.2rem 0 0}
.where-card dt{font-family:var(--display);font-weight:700;font-size:.82rem;letter-spacing:.06em;
  text-transform:uppercase;color:var(--purple-lift)}
.where-card dd{margin:0;color:var(--bone-dim)}
.where-map-link{display:inline-flex;align-items:center;gap:.5rem;margin-top:1.6rem;color:var(--bone);
  font-family:var(--display);font-weight:700;font-size:.95rem;position:relative}
.where-map-link svg{width:18px;height:18px}
.where-map-link::after{content:"";position:absolute;left:0;bottom:-3px;width:0;height:2px;
  background:var(--purple-lift);transition:width .25s var(--ease)}
.where-map-link:hover::after,.where-map-link:focus-visible::after{width:100%}
.where-visual-wrap{display:flex;flex-direction:column;align-items:center;gap:1.1rem}
/* aspect-ratio tracks the SVG viewBox — regenerate both together with
   _scratch/build_service_map.py. The radial hotspot is centred on the DJX4
   pin at (281,161) of the 560x828 box = 50% 19%. */
.where-visual{position:relative;width:100%;max-width:380px;aspect-ratio:560/828;
  border-radius:18px;border:1px solid var(--ink-3);padding:clamp(14px,2vw,22px);
  background:
    linear-gradient(to right,rgba(139,107,196,.10) 1px,transparent 1px) 0 0/44px 44px,
    linear-gradient(to bottom,rgba(139,107,196,.10) 1px,transparent 1px) 0 0/44px 44px,
    radial-gradient(circle at 50% 19%,rgba(87,60,129,.24),transparent 52%),
    var(--ink-2)}
.wv-map{width:100%;height:100%}
.wv-county{fill:rgba(87,60,129,.45);stroke:var(--purple-lift);stroke-width:2.5;
  stroke-linejoin:round}
/* Duval holds station DJX4 but is NOT a delivery area. Drawn as a muted dashed
   outline so the map can say "based here, we deliver there" without implying
   coverage it doesn't have. */
.wv-county--base{fill:rgba(110,110,122,.10);stroke:var(--grey);stroke-width:1.75;
  stroke-dasharray:7 6}
.wv-county-label{fill:var(--bone-dim);opacity:.5;font-family:var(--display);font-weight:700;
  font-size:21px;letter-spacing:.14em}
.wv-county-label--base{opacity:.3;font-size:17px}
.wv-legend{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;justify-content:center;
  gap:.35rem 1.1rem}
.wv-legend li{display:inline-flex;align-items:center;gap:.45rem;font-family:var(--display);
  font-weight:600;font-size:.76rem;letter-spacing:.03em;text-transform:uppercase;
  color:var(--bone-dim)}
.wv-legend .sw{width:14px;height:14px;border-radius:3px;flex-shrink:0}
.wv-legend .sw--area{background:rgba(87,60,129,.45);border:1.5px solid var(--purple-lift)}
.wv-legend .sw--base{background:rgba(110,110,122,.10);border:1.5px dashed var(--grey)}
.wv-legend .sw--pin{width:11px;height:11px;border-radius:50%;background:var(--magenta);
  box-shadow:0 0 0 2px var(--bone)}
.wv-leader{stroke:var(--bone-dim);stroke-width:2;opacity:.55}
.wv-pin-label{fill:var(--bone);font-family:var(--display);font-weight:700;font-size:27px;
  letter-spacing:.05em}
.wv-dot{fill:var(--magenta);stroke:var(--bone);stroke-width:2.5}
.wv-ping{fill:none;stroke:var(--magenta-lift);stroke-width:2.5;
  transform-box:fill-box;transform-origin:50% 50%;animation:wv-ping 2.6s var(--ease) infinite}
@keyframes wv-ping{
  0%{transform:scale(.5);opacity:.9}
  70%{transform:scale(1.85);opacity:0}
  100%{transform:scale(1.85);opacity:0}
}

/* ============================ APPLY ============================ */
.apply{background:var(--ink-2)}
.apply-head{max-width:66ch}
.apply-grid{display:grid;grid-template-columns:1.15fr .85fr;gap:1.4rem;align-items:start;margin-top:2.6rem}
.apply-card,.apply-form{background:var(--ink);border:1px solid var(--ink-3);border-radius:20px;
  padding:clamp(24px,3vw,34px);display:flex;flex-direction:column;gap:1rem;
  transition:transform .25s var(--ease),box-shadow .25s var(--ease),border-color .25s var(--ease)}
.apply-card:hover{transform:translateY(-4px);border-color:var(--purple-lift)}
/* Icon and title share a row now (client asked for the title beside the mark,
   not under it). flex-shrink:0 on .ic so a wrapped two-word title can't squeeze
   the square into a rectangle. */
.apply-hd{display:flex;align-items:center;gap:.9rem}
.apply-hd h3{margin:0}
.apply-card .ic,.apply-form .ic{flex-shrink:0;width:50px;height:50px;border-radius:12px;display:grid;place-items:center;
  background:var(--ink-3)}
.apply-card .ic svg,.apply-form .ic svg{width:24px;height:24px;color:var(--purple-lift)}
.apply-card h3,.apply-form h3{font-size:1.25rem;color:var(--bone)}
.apply-card p,.apply-form>p:not(.form-status){margin:0;color:var(--bone-dim);flex:1}

.apply-form{gap:1.1rem}
.field-row{display:grid;grid-template-columns:1fr 1fr;gap:1rem}
.field{display:flex;flex-direction:column;gap:.4rem}
.field>span{font-family:var(--display);font-weight:600;font-size:.85rem;color:var(--bone);letter-spacing:.02em}
.field input{font:inherit;font-size:1rem;color:var(--bone);background:var(--ink-2);border:1px solid var(--ink-3);
  border-radius:10px;padding:.8rem .95rem;width:100%;
  transition:border-color .2s var(--ease),background-color .2s var(--ease),box-shadow .2s var(--ease)}
.field input::placeholder{color:#7B7B86}
.field input:hover{border-color:var(--purple-lift)}
.field input:focus{outline:none;background:var(--ink);border-color:var(--purple-lift);
  box-shadow:0 0 0 3px rgba(139,107,196,.30)}
.field input:user-invalid{border-color:var(--magenta)}

.checks{border:1px solid var(--ink-3);background:var(--ink-2);border-radius:14px;padding:1rem 1.1rem;margin:0;
  display:flex;flex-direction:column;gap:.7rem}
.checks legend{font-family:var(--display);font-weight:600;font-size:.85rem;color:var(--bone);
  letter-spacing:.02em;padding:0 .4rem;margin-left:-.4rem}
.check{display:flex;align-items:flex-start;gap:.65rem;cursor:pointer;color:var(--bone-dim);font-size:.94rem;
  line-height:1.45}
.check input{accent-color:var(--purple-lift);width:19px;height:19px;margin:1px 0 0;flex-shrink:0;cursor:pointer}
.check:hover{color:var(--bone)}

.hp{position:absolute;left:-9999px;width:1px;height:1px;opacity:0;pointer-events:none}

.apply-form button[type=submit]{justify-content:center;width:100%}
.apply-form button[disabled]{opacity:.65;cursor:progress}
.form-status{margin:0;font-size:.92rem;font-weight:600;color:var(--bone-dim)}
.form-status:empty{display:none}
.form-status.err{color:var(--magenta-lift)}
.form-status.ok{color:#6FCF97}
.form-done{display:flex;flex-direction:column;align-items:center;text-align:center;gap:.7rem;
  padding:1.6rem 0 .8rem}
.form-done svg{width:46px;height:46px;color:var(--purple-lift)}
.form-done h3{font-size:1.35rem;color:var(--bone)}
.form-done h3:focus,.form-done h3:focus-visible{outline:none}
.form-done p{margin:0;color:var(--bone-dim)}
.apply-response-note{font-size:.88rem;color:var(--bone-dim);margin:0}

/* ============================ FOOTER ============================ */
.footer{background:var(--ink);color:var(--bone-dim);padding:3.4rem 0 2.2rem;border-top:1px solid var(--ink-3)}
.footer-in{display:grid;grid-template-columns:1.2fr 1fr 1fr;gap:2.4rem}
.footer-brand{display:flex;flex-direction:column;gap:1rem}
.footer-brand .brand{color:var(--bone)}
.footer-nap dt{font-family:var(--display);font-weight:700;font-size:.78rem;letter-spacing:.08em;
  text-transform:uppercase;color:var(--purple-lift);margin-top:.9rem}
.footer-nap dt:first-child{margin-top:0}
.footer-nap dd{margin:.2rem 0 0;color:var(--bone-dim)}
.footer-nap dd,.where-card dd{overflow-wrap:anywhere}
.footer-links{display:flex;flex-direction:column;gap:.7rem}
.footer-links a{color:var(--bone-dim);font-weight:600;font-size:.94rem}
.footer-links a:hover{color:var(--purple-lift)}
.footer-cta{display:flex;flex-direction:column;gap:1rem;align-items:flex-start}
.footer-legal{margin-top:2.4rem;border-top:1px solid var(--ink-3);padding-top:1.4rem;
  display:flex;justify-content:space-between;align-items:center;gap:1rem;flex-wrap:wrap;
  font-size:.82rem;color:var(--bone-dim)}

/* FPM credit — one pure-alpha lockup asset tinted per site via CSS mask,
   so it never lands as black-and-white on a client's palette. */
.fpm-credit{display:inline-flex;align-items:center;gap:.55rem;text-decoration:none}
.fpm-credit__label{font-size:.78rem;color:var(--bone-dim);opacity:.72;
  transition:opacity .18s var(--ease),color .18s var(--ease)}
.fpm-credit__mark{display:block;width:183px;height:36px;background-color:var(--bone-dim);
  -webkit-mask-image:url(/assets/img/fpm-credit-lockup-v1.png);mask-image:url(/assets/img/fpm-credit-lockup-v1.png);
  -webkit-mask-size:contain;mask-size:contain;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:center;mask-position:center;
  opacity:.72;transition:opacity .18s var(--ease),background-color .18s var(--ease)}
.fpm-credit:hover .fpm-credit__mark,.fpm-credit:focus-visible .fpm-credit__mark{opacity:1;background-color:var(--purple-lift)}
.fpm-credit:hover .fpm-credit__label,.fpm-credit:focus-visible .fpm-credit__label{opacity:1;color:var(--purple-lift)}
@media (max-width:700px){.fpm-credit__mark{width:152px;height:30px}.fpm-credit__label{font-size:.74rem}}

/* ============================ STICKY MOBILE ACTION BAR ============================ */
.mobile-bar{position:fixed;left:0;right:0;bottom:0;z-index:150;display:none;gap:.7rem;
  background:rgba(14,14,17,.96);backdrop-filter:blur(10px);border-top:1px solid var(--ink-3);
  padding:.7rem clamp(16px,4vw,28px);padding-bottom:calc(.7rem + env(safe-area-inset-bottom));
  transform:translateY(100%);transition:transform .3s var(--ease)}
.mobile-bar.is-visible{transform:translateY(0)}
.mobile-bar .btn{flex:1;justify-content:center}
@media (max-width:768px){.mobile-bar{display:flex}}

/* ============================ SECTION BACKDROPS ============================
   Two components from 21st.dev, ported to vanilla and retinted to the
   purple/grey palette (no magenta — see the accent budget at the top):

   .warp    — "Warp Background" by @dillionverma / Magic UI. Four grid planes
              rotated into a perspective tunnel with beams travelling down
              them. Near-direct port: pure CSS transforms + keyframes, JS only
              seeds each beam's random x / aspect / delay / duration.

   .boxgrid — "Background Boxes" by @manuarora700 / Aceternity. NOT a 1:1 DOM
              port on purpose: the original builds 150x100 = 15,000 divs plus
              ~3,750 inline SVGs, which is a non-starter on a page whose LCP
              is 122ms. Same visual from three CSS gradient layers (h-lines,
              v-lines, and a plus glyph on every other intersection) plus a
              recycled pool of 14 highlight cells, so DOM cost is flat no
              matter how large the grid gets. The plus tile is 128x64 with the
              glyph at (64,32) — exactly a grid intersection, which reproduces
              the original's every-other-cell placement.

   Both are decorative and aria-hidden. Neither listens on itself: the boxgrid
   binds pointermove on its section, and both are pointer-events:none. Under
   reduced motion both render as static grids (see the block below).
   ========================================================================== */
.has-backdrop{position:relative;overflow:hidden;isolation:isolate}
.has-backdrop>.wrap{position:relative;z-index:1}

/* ⚠️ SAFARI/WebKit: do NOT move perspective back onto .warp. Grouping
   properties (mask, clip-path, overflow, and the `contain: layout` implied by
   container-type) flatten the 3D context in WebKit, and a plane at
   rotateX(-90deg) with no perspective is exactly edge-on — zero height — so
   the entire tunnel disappears rather than degrading. That is what shipped on
   2026-08-06 and rendered as a flat empty band in Safari while working in
   Chrome. The clipping/masking now lives on .warp and the 3D context lives
   alone on .warp__stage.
   Sizes are plain px custom properties written by backdrops.js rather than
   container-query units, so nothing depends on cq resolution (especially not
   inside @keyframes, which is the least-trodden path of all). Fallback values
   keep the grid visible if the JS never runs. */
.warp{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden;
  -webkit-mask-image:radial-gradient(125% 80% at 50% 50%,#000 30%,transparent 76%);
  mask-image:radial-gradient(125% 80% at 50% 50%,#000 30%,transparent 76%)}
.warp__stage{position:absolute;inset:0;perspective:130px;
  --beam-size:5%;--grid-color:rgba(169,169,182,.13)}
.warp__side{position:absolute;
  background:
    linear-gradient(var(--grid-color) 0 1px,transparent 1px var(--beam-size)) 50% -.5px/var(--beam-size) var(--beam-size),
    linear-gradient(90deg,var(--grid-color) 0 1px,transparent 1px var(--beam-size)) 50% 50%/var(--beam-size) var(--beam-size)}
.warp__side--top{top:0;left:0;height:var(--long,150%);width:var(--vw,100%);
  transform-origin:50% 0%;transform:rotateX(-90deg)}
.warp__side--bottom{top:100%;left:0;height:var(--long,150%);width:var(--vw,100%);
  transform-origin:50% 0%;transform:rotateX(-90deg)}
.warp__side--left{top:0;left:0;height:var(--long,150%);width:var(--vh,100%);
  transform-origin:0% 0%;transform:rotate(90deg) rotateX(-90deg)}
.warp__side--right{top:0;right:0;height:var(--long,150%);width:var(--vh,100%);
  transform-origin:100% 0%;transform:rotate(-90deg) rotateX(-90deg)}
.warp__beam{position:absolute;top:0;left:0;
  background:linear-gradient(var(--beam-tint),transparent);
  will-change:transform;animation:warp-beam var(--dur) linear var(--delay) infinite}
@keyframes warp-beam{
  from{transform:translate(-50%,var(--travel,900px))}
  to{transform:translate(-50%,-100%)}
}

.boxgrid{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden;
  --cell-w:64px;--cell-h:32px;--line:rgba(169,169,182,.14);
  -webkit-mask-image:radial-gradient(115% 85% at 50% 42%,#000 22%,transparent 72%);
  mask-image:radial-gradient(115% 85% at 50% 42%,#000 22%,transparent 72%)}
/* Centred and heavily oversized on purpose: the skew maps the visible box to a
   large, rotated region in the plane's own coordinate space, and any part of
   that region outside the plane's box would show highlight cells sitting on
   no grid lines. 420% covers it at every viewport we support. */
.boxgrid__plane{position:absolute;top:50%;left:50%;width:420%;height:420%;
  transform-origin:50% 50%;
  transform:translate(-50%,-50%) skewX(-48deg) skewY(14deg) scale(.675);
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='128' height='64'%3E%3Cpath d='M64 26v12M58 32h12' fill='none' stroke='%23A9A9B6' stroke-opacity='.24' stroke-width='1' stroke-linecap='round'/%3E%3C/svg%3E") 0 0/calc(var(--cell-w)*2) calc(var(--cell-h)*2),
    linear-gradient(90deg,var(--line) 0 1px,transparent 1px) 0 0/var(--cell-w) var(--cell-h),
    linear-gradient(var(--line) 0 1px,transparent 1px) 0 0/var(--cell-w) var(--cell-h)}
.boxgrid__cell{position:absolute;width:var(--cell-w);height:var(--cell-h);opacity:0;
  will-change:opacity}
.boxgrid__cell.lit{animation:boxgrid-fade 2s var(--ease) forwards}
@keyframes boxgrid-fade{from{opacity:.8}to{opacity:0}}

/* ======================= SCROLL CHOREOGRAPHY =======================
   Added 2026-08-08. Every state below is applied by js/main.js and ONLY
   when the pinned sequences actually run — i.e. a viewport at least
   992x640 with prefers-reduced-motion not set. Nothing here has an
   effect on the plain document, so mobile and reduced-motion users get
   exactly the layout that shipped before this pass.

   No new magenta. Purple is the only accent introduced, per the budget
   enumerated at the top of this file.
   ================================================================== */

/* A section that ScrollTrigger pins has to fit the viewport it is pinned
   into, or the pin simply parks content off-screen. Vertical padding
   collapses to a vh-relative value and the content centres itself. */
.section.is-pinned{min-height:100vh;display:flex;flex-direction:column;justify-content:center;
  padding-block:clamp(40px,6vh,72px)}

/* ---- #the-job: pinned route narrative ---- */
.route-rail{position:relative;height:2px;background:var(--ink-3);border-radius:2px;
  margin:2.4rem 0 .6rem}
.route-rail__fill{position:absolute;inset:0;background:var(--purple-lift);border-radius:2px;
  transform:scaleX(0);transform-origin:0 50%}
.route-rail__tick{position:absolute;top:50%;width:10px;height:10px;margin:-5px 0 0 -5px;
  border-radius:50%;background:var(--ink-3);border:2px solid var(--ink-2);
  transition:background-color .35s var(--ease),box-shadow .35s var(--ease)}
.route-rail__tick.is-lit{background:var(--purple-lift);
  box-shadow:0 0 0 4px rgba(139,107,196,.18)}

.job-grid.is-narrative{margin-top:1.6rem}
/* The staggered nth-child offsets are a static-layout flourish; in the
   narrative the four steps have to sit on one baseline so the spotlight
   reads as moving sideways rather than diagonally. */
.job-grid.is-narrative .job-card{margin-top:0;opacity:.35;
  transition:opacity .45s var(--ease),transform .45s var(--ease),
    border-color .45s var(--ease),background .45s var(--ease),box-shadow .45s var(--ease)}
.job-grid.is-narrative .job-card.is-spent{opacity:.55}
.job-grid.is-narrative .job-card.is-active{opacity:1;transform:translateY(-10px);
  border-color:var(--purple-lift);
  background:linear-gradient(180deg,rgba(87,60,129,.34),rgba(87,60,129,.10)),var(--ink-2);
  box-shadow:0 26px 52px -28px rgba(87,60,129,.75)}
/* Hover fights the spotlight for the same transform — drop it while the
   narrative owns these cards. */
.job-grid.is-narrative .job-card:hover{transform:none;box-shadow:none;border-color:var(--ink-3)}
.job-grid.is-narrative .job-card.is-active:hover{transform:translateY(-10px);
  border-color:var(--purple-lift);box-shadow:0 26px 52px -28px rgba(87,60,129,.75)}
/* Budget item 3 (the oversized numerals) is UNCHANGED in count but
   reduced in simultaneity: in narrative mode exactly ONE numeral is
   magenta at a time instead of all four, so on-screen pink goes down.
   Spent steps carry purple, upcoming steps are grey. */
.job-grid.is-narrative .job-card .jc-n{color:var(--grey);transition:color .45s var(--ease)}
.job-grid.is-narrative .job-card.is-spent .jc-n{color:var(--purple-lift)}
.job-grid.is-narrative .job-card.is-active .jc-n{color:var(--magenta)}

/* ---- #requirements: the route ribbon ---- */
/* Top-down schematic, and NOT pinned — see the long note in main.js for why
   a second pinned scrub was the wrong answer here. Height is written inline
   by JS (stops x row height); everything else is declarative.

   All of this is scoped to .is-road, which only JS adds. The plain .req-grid
   above stays a normal CSS grid, so mobile, reduced motion and no-JS all get
   the layout that shipped before this pass. */
.req-grid.is-road{display:block;position:relative;margin-top:3.4rem}
/* Both ends dissolve rather than stopping dead or being clipped square. The
   -webkit- pair is not optional: Frank reviews in Safari, and unprefixed
   mask-image alone leaves the ribbon hard-cut there. */
.road{position:absolute;top:0;left:0;pointer-events:none;
  -webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 72px,
    #000 calc(100% - 96px),transparent 100%);
  mask-image:linear-gradient(to bottom,transparent 0,#000 72px,
    #000 calc(100% - 96px),transparent 100%)}
/* Asphalt is a hair lighter than --ink-3 so the ribbon reads against the
   boxgrid backdrop without competing with the cards sitting on it. */
.road__asphalt{fill:none;stroke:#2E2E38;stroke-width:38;stroke-linecap:round}
/* Light neutral rather than --grey: these dashes have to read against BOTH
   the grey asphalt ahead of the marker and the purple trail behind it. */
.road__dash{fill:none;stroke:var(--bone-dim);stroke-width:2;stroke-linecap:round;
  stroke-dasharray:14 20;opacity:.4}
/* The travelled portion. Sits above the asphalt and below the marker, and is
   the only progress cue in the section — the cards park on top of the stops,
   so a per-stop pip would be hidden underneath one. */
.road__trail{fill:none;stroke:var(--purple);stroke-width:38;stroke-linecap:round;
  opacity:.85}
.road__van-glow{fill:var(--purple-lift);opacity:.22}
.road__van-mark{fill:var(--bone)}

/* Cards are absolutely positioned and centred on their stop by GSAP
   (xPercent/yPercent -50), so top/left stay at 0 and every pixel of
   placement comes from the one path both the ribbon and the cards share. */
.req-grid.is-road .req-card{position:absolute;top:0;left:0;
  width:clamp(240px,30%,320px);min-height:0;
  flex-direction:column;align-items:flex-start;gap:.9rem;padding:1.4rem 1.5rem;
  background:var(--ink-2);border-color:var(--ink-3);
  box-shadow:0 22px 44px -26px rgba(0,0,0,.85);will-change:transform}
.req-grid.is-road .req-card svg{width:30px;height:30px;margin-top:0}
.req-grid.is-road .req-card p{margin:0;font-family:var(--display);font-weight:700;
  font-size:1.06rem;line-height:1.25;color:var(--bone)}
/* Hover fights MotionPath for the same transform — .req-card's base rule
   translates on hover, and these cards are held in place by x/y. */
.req-grid.is-road .req-card:hover{transform:none}
/* Reached = the marker has driven past this stop. Border and icon only; no
   transform, so it can never disturb the parked position. */
.req-grid.is-road .req-card.is-reached{border-color:var(--purple-lift);
  box-shadow:0 22px 44px -22px rgba(87,60,129,.7)}
.req-grid.is-road .req-card.is-reached svg{color:var(--bone)}
.req-grid.is-road .req-card,
.req-grid.is-road .req-card svg{
  transition:border-color .4s var(--ease),box-shadow .4s var(--ease),color .4s var(--ease)}
/* Zero-size scroll markers at each stop. They exist because MotionPath moves
   the cards, so a card cannot be its own trigger. */
.road-anchor{position:absolute;left:0;width:1px;height:1px;pointer-events:none;
  visibility:hidden}

/* ---- #where-we-run: the map draws itself ---- */
/* .is-undrawn is added by JS at setup and removed once the draw timeline
   finishes, so the station ping never pulses at a pin that hasn't been
   placed yet. Under reduced motion the class is never added at all. */
.where-visual.is-undrawn .wv-ping{animation:none;opacity:0}
.wv-dot{transform-box:fill-box;transform-origin:50% 50%}

/* ============================ RESPONSIVE ============================ */
@media (max-width:980px){
  .wwd-grid{grid-template-columns:1fr}
  .wwd-grid .photo-frame{order:-1}
  .job-grid{grid-template-columns:repeat(2,1fr)}
  .job-card:nth-child(2),.job-card:nth-child(4){margin-top:0}
  .job-card:nth-child(2),.job-card:nth-child(3){margin-top:1.4rem}
  .why-layout{grid-template-columns:1fr}
  .why-grid{grid-template-columns:1fr}
  .req-grid{grid-template-columns:1fr 1fr}
  .apply-grid{grid-template-columns:1fr}
  .where-grid{grid-template-columns:1fr}
  .footer-in{grid-template-columns:1fr 1fr}
  .about-grid{grid-template-columns:1fr}
  .role-specs{grid-template-columns:repeat(2,1fr)}
  .role-cols{grid-template-columns:1fr}
  .about-mv{grid-template-columns:1fr}
}
@media (max-width:900px){
  .nav-links{display:none}
  .nav-toggle{display:inline-flex}
}
@media (max-width:640px){
  .req-grid{grid-template-columns:1fr}
  .field-row{grid-template-columns:1fr}
  .footer-in{grid-template-columns:1fr}
  .pay-figures{gap:1.2rem}
  .role-specs{grid-template-columns:1fr}
  .about-facts{grid-template-columns:1fr}
}

/* ============================ REDUCED MOTION ============================ */
@media (prefers-reduced-motion:reduce){
  .ticker-track{animation:none!important}
  .flap-flip{transition:none!important}
  .hero-chevron{transition:none!important}
  .wv-ping{animation:none!important;opacity:.55}
  /* Backdrops fall back to static grids. backdrops.js also skips seeding the
     beams and wiring pointermove entirely, so this is belt-and-braces. */
  .warp__beam{animation:none!important;display:none}
  .boxgrid__cell{animation:none!important;opacity:0!important}
  /* Belt-and-braces for the 2026-08-08 scroll choreography. main.js never
     applies any of these classes under reduced motion (the whole block
     returns early), so this only matters if the setting is toggled
     mid-session before matchMedia reverts. */
  .section.is-pinned{min-height:0;display:block}
  .route-rail{display:none}
  .job-grid.is-narrative .job-card{opacity:1!important;transform:none!important}
  .job-grid.is-narrative .job-card .jc-n{color:var(--magenta)!important}
  /* The ribbon is never built under reduced motion — main.js returns before
     the isWide block — so this only catches a mid-session toggle, in the
     window before matchMedia reverts. Cards go back into normal flow and the
     inline height JS wrote is overridden, or they would all stack on top of
     each other at the container origin. */
  .req-grid.is-road{display:grid;grid-template-columns:repeat(3,1fr);
    height:auto!important;position:static}
  .req-grid.is-road .req-card{position:static;transform:none!important;
    opacity:1!important;visibility:visible!important;width:auto}
  .road,.road-anchor{display:none}
}
