/* =============================================================================
   READI Homepage 2026 — Home Page Video Template V2
   =============================================================================
   Enqueued ONLY on templates/HomeVideoTemplateV2.php via
   home_video_v2_scripts() (is_page_template() guarded — see functions.php).
   Loads on no other page. Duplicated 2026-09-09 from home-video.css (the
   V1 Home Page Video Template's stylesheet), then cut down substantially
   for Carlos's 2026-09-14 redesign checkpoint -- see that file for the
   full original history this was forked from.

   Everything is scoped under .rd-home-v2. No :root custom properties are
   declared anywhere in this file, per the brief — tokens live on .rd-home-v2.

   2026-09-14 redesign (Carlos) -- this file used to carry the whole V1
   homepage (Gap, Cause, Statement, Outcomes, Meet READI, Quote, Trust,
   Fit, CTA). All of that is gone now: this is a checkpoint build of just
   the hero + one new "Transition Statement" reveal, to be reviewed before
   the rest of the page is built back out. Specifically:
     - The procedural night-drive HTML background animation is gone --
       back to a real <video> background (§4).
     - The scattered application-icon tiles (old §5) are gone. The hero
       video now shrinks straight down into its resting 1440×720 card,
       darkens to solid, and a heading + subtitle ("Transition Statement,"
       Figma node 5786:22957) fade in on top of it -- one continuous
       element inside .rd-hero-video-frame, not a separate section (§4).
     - A small "or scroll down to learn more" label + chevron sits under
       the hero's two CTA buttons (Figma node 5786:22837) (§6).
     - Every other V1 section (The Gap, The Cause + its animation/pillars,
       Statement, Outcomes Stats, Meet READI, Quote, Trust, Fit, CTA) and
       their CSS have been removed outright.

   Section index:
     1. Tokens (on .rd-home-v2, not :root) + base reset
     2. Nav override (page-scoped only — header.php itself is untouched)
     3. Hero wrap / sticky stage
     4. Hero video + scrim + fade-to-card + Transition Statement
     5. Hero content (announcement pill + copy + CTAs + scroll label)
     6. Logo marquee
     7. Your Program Now (bento stat cards)
     8. //04-WHY (identity journey animations + bento cards)
     9. Reduced-motion end state
    10. Responsive (390 / 767 / 899 / 1024)
    11. Mobile perf fallback (rd-hero-stage--degraded)

   2026-09-15 (Carlos): added "Your Program Now" (Figma node 5786:22141) --
   see that section's own in-file comment for the layout/spacing notes and
   template-parts/section-program-now-v2.php for the ACF-driven markup.

   2026-09-15 (Carlos): added "//04-WHY" (Figma node 5823:23979) -- the
   page's "core section," two GPS-path animations (without READI vs. with
   READI) plus outcome bento cards, revealed part by part on scroll. See
   that section's own in-file comment (§8a, right before the old §9
   Reduced-motion block -- kept that block's original number rather than
   renumbering everything, per this file's own stale-in-file-numbers
   convention noted below) and template-parts/section-why-v2.php.

   (In-file section headers below keep their ORIGINAL numbers from when
   this was forked from home-video.css (the V1 stylesheet) -- e.g. the new
   Program Now block is labelled "── 8." in-file even though it's really
   the 7th section left in this file. Only this index above uses the
   real, current count; the in-file numbers exist just so this diff stays
   legible against home-video.css's own numbering.)
   ============================================================================= */

/* ── 1. Tokens + base reset ──────────────────────────────────────────────── */
.rd-home-v2 {
	--rd-black:            #2E2925;
	--rd-white:            #FFFFFF;
	--rd-seasalt:          #F8F9FA;
	--rd-royal-blue:       #3D6BEA;
	--rd-royal-blue-dark:  #1238A0;
	--rd-coral:            #F76E6E;
	--rd-onix:             #343A40;
	--rd-state-grey:       #6C757D;
	--rd-neutral-500:      #8C959D;
	--rd-neutral-300:      #DEE2E6; /* Added 2026-09-03 for The Cause pillars (§14) -- divider lines between pillars */
	--rd-neutral-200:      #E9ECEF;
	--rd-outer-space:      #495057; /* Added 2026-09-03 for The Cause pillars (§14) -- Figma "GRAYS/Outer Space", pillar paragraph text */
	--rd-primary-050:      #EEF3FE; /* Added 2026-09-04 for Outcomes Stats (§16) -- Figma "Brand/Primary/050", the "Success" background option from Carlos's 2026-09-03 box-style spec (§8's file header) -- The Gap used Neutral, this section uses Success */
	--rd-primary-300:      #7D9CF2; /* Added 2026-09-08 for the Fit section (§20) -- Figma "Brand/Primary/300", the Fit cards' border color, shared with the badge icons in Trust (§19) */
	--rd-french-grey:      #ADB5BD; /* Added 2026-09-15 for //04-WHY (§9) -- Figma "GRAYS/French Grey", card body text + the title-divider line fade */
	--rd-danger-300:       #F98B8B; /* Added 2026-09-15 for //04-WHY (§9) -- Figma "Brand/Danger/300", the uncertain-outcome status pills on the "without READI" path -- distinct from the existing --rd-danger (#E51F37) */
	--rd-primary-100:      #D5DFFC; /* Added 2026-09-15 for //07-Orientation (§8d) -- Figma "Brand/Primary/100", the bento cards' border color */
	--rd-primary-200:      #ABBFF7; /* Added 2026-09-24 for //04-Your Program With READI (§9) -- Carlos's exact spec for the "Your IGA+READI" scene label color */
	--rd-danger-200:       #FAA8A8; /* Added 2026-09-16 for Your Program Now's re-checked Figma (§8) -- "Brand/Danger/200", the row cards' border color */
	--rd-danger-100:       #FCC5C5; /* Added 2026-09-16 for Your Program Now's re-checked Figma (§8) -- "Brand/Danger/100", the wide card's border + gradient color */
	--rd-danger-050:       #FEE8E8; /* Added 2026-09-17 for the //04-Your Program With READI rebuild (§9) -- Figma "Brand/Danger/050", the "problem" pill's background -- distinct from the existing --rd-danger-bg (#FDEAEA) */

	/* Added 2026-09-03 for The Cause animation (§13) -- Carlos's spec gives
	   these as literal hex values distinct from the existing --rd-coral
	   (#F76E6E), so new tokens rather than reusing it. --rd-royal-blue
	   above already matches the spec's "Executed" color exactly. */
	--rd-danger:           #E51F37;
	--rd-danger-bg:        #FDEAEA;
	--rd-royal-blue-bg:    #ECF0FD;

	--rd-radius-card:      15px;
	--rd-radius-pill:      30px;
	--rd-nav-height:       104px; /* matches .site-nav --nav-bar-height */
	/* --rd-container-max and --rd-gap-block: dead tokens from the V1
	   Gap section, which this redesign removed entirely (2026-09-14) --
	   left unused rather than pruned, same as the handful of other
	   now-unread tokens above (--rd-danger, --rd-primary-300, etc.). */

	--rd-scrim-base:       rgba(46, 41, 37, .45);
	--rd-scrim-strong:     rgba(46, 41, 37, .72);

	/* --rd-marquee-speed (36s) removed 2026-09-02 -- the marquee now
	   reuses the shared Logo Wall Marquee Component's own track/keyframe
	   (§7), which has its own fixed speed, not a per-page custom
	   property. */

	font-family: 'Roboto', sans-serif;
	color: var(--rd-onix);
	/* NOTE: do NOT add overflow-x (or any overflow) here. .rd-home-v2 is
	   #main-content — the direct parent of .rd-hero-wrap. Per the CSS
	   Overflow spec, setting overflow-x alone forces overflow-y to compute
	   to `auto`, which turns this element into a scroll container and
	   breaks `position: sticky` on the nested .rd-hero-stage (browsers
	   stick relative to the nearest scrolling ancestor; since this element
	   never actually overflows internally, the sticky child never sticks —
	   it just scrolls away after one viewport height, leaving the rest of
	   .rd-hero-wrap's 200vh blank). Fixed 2026-09-01 — this was the root
	   cause of the transition not working at all. .rd-hero-stage's own
	   `overflow: hidden` (§3) already clips the oversized starting video;
	   nothing here needs to. */
}

.rd-home-v2 *,
.rd-home-v2 *::before,
.rd-home-v2 *::after {
	box-sizing: border-box;
}

.rd-home-v2 img {
	display: block;
	max-width: 100%;
}

.rd-home-v2 [aria-hidden="true"] {
	user-select: none;
}

/* ── 2. Nav — now a GLOBAL treatment, not page-scoped ───────────────────────
   2026-09-01/02: this section used to hold .home-video-v2-page-scoped
   overrides (transparent background + #6C757D/#3D6BEA menu colours) built
   up over several rounds on this page alone.

   2026-09-02 -- Carlos: "I see that the nave is different in the home and
   in the other pages ... It should be across all pages," and on the
   follow-up clarifying question chose "the whole transparent-nav
   treatment, everywhere." All of it -- background: transparent AND the
   #6C757D/#3D6BEA menu-item colours (util links, triggers, top-level
   links, plus their hover/focus/active states) -- has been moved to
   assets/css/components/site-nav.css as the sitewide default, so it now
   applies on every page, not just this template. Nothing left to
   override here; .home-video-v2-page inherits the global look as-is.

   Known limitation, not addressed here: nav text/logo colour is fixed
   (#6C757D) and does NOT change based on what's scrolled underneath the
   sticky nav -- on this page that's the video/general-background scrub
   (§3) and later The Gap's off-white background; on other pages it's
   whatever content sits at the top. Carlos confirmed this sitewide
   transparent-nav change explicitly; flagging again here since a page
   with light content near the top could render this grey text
   low-contrast. See site-nav.css for the actual rules and
   home-video-notes.md decisions log. */

/* ── 3. Hero wrap / sticky stage ─────────────────────────────────────────── */
.rd-hero-wrap {
	position: relative;
	/* 2026-09-02 fix -- Carlos: "the app tiles and the video rectangle
	   scroll over the nav, they should scroll placed behind the nav."
	   position:relative alone (no z-index) does NOT create a stacking
	   context, so .rd-hero-stage's internal z-index scheme (1-4, see
	   §4/§5/§6) was being compared directly against the site nav's
	   z-index:1000 -- which should have been a safe win for the nav,
	   but in practice something in this sticky-in-sticky arrangement
	   (nav is itself position:sticky) was letting the stage's contents
	   paint over it during the scrub. Giving the WRAP itself a z-index
	   forces a stacking context boundary right here: everything inside
	   .rd-hero-wrap is now capped at this single z-index, compared as
	   one unit against the nav's 1000, so the nav wins unconditionally
	   no matter what happens inside. */
	z-index: 1;
	height: 200vh; /* scrub distance — shortened on mobile, see §11 */
	/* Fixed 2026-09-01 (3rd pass): pulls the wrap up by the nav's own
	   height so .rd-hero-stage (sticky, top: 0) starts flush with the
	   TRUE viewport top from the very first paint — not just after
	   scrolling past the nav's occupied space, which is when position:
	   sticky would otherwise kick in on its own. Paired with the
	   transparent nav background (§2). .rd-home-v2 (#main-content) has no
	   padding/border, so this margin is free to pull the wrap's box up
	   into the space header.php's nav occupies; .rd-hero-content's own
	   padding-top (§6) already accounts for the nav's height, so hero
	   copy still clears it correctly. */
	margin-top: calc(-1 * var(--rd-nav-height));
}

.rd-hero-stage {
	position: sticky;
	top: 0;
	height: 100dvh;
	min-height: 600px;
	overflow: hidden;
	/* This is the "general background" (Carlos's term, 2026-09-02) --
	   the area revealed behind the video as it scales down on scroll.
	   It's a CONSTANT white, not animated. Previously (3rd pass) this
	   lerped from --rd-black to white via a JS-driven --rd-stage-bg
	   custom property, timed to match the dark .rd-hero-scrim fading
	   out over the same window -- but the scrim was ALSO sized to the
	   full stage (not just the video), so the two overlapping fades
	   combined into a visible grey smear before settling to white
	   ("I can still see a grey overlay fading out to white" — Carlos).
	   Fixed by making this background permanently white (nothing to
	   animate — at progress 0 the video already covers the full stage
	   via scaleStart, so there's nothing to reveal yet anyway) AND
	   scoping the scrim to only the video's own box, not the whole
	   stage (see .rd-hero-video-frame, §4) — so the revealed general
	   background is white from the instant any of it is visible, with
	   no fade-through-grey step. */
	background: var(--rd-white);
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: space-between;
}

/* ── 4. Hero video + scrim ───────────────────────────────────────────────── */
.rd-hero-media {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 2;
	pointer-events: none;
}

.rd-hero-video-frame {
	/* The video's real CSS box IS its end-state size -- a 1440×720 (2:1)
	   card, matching Figma node 5786:22957's "//02-Transition Statement"
	   resting frame. It never changes width/height during the scrub —
	   only `transform: scale()`, driven by --rd-video-scale, which JS
	   sets each frame (starting large enough to cover the viewport,
	   animating down to 1). See home-video-v2.js computeVideoScaleStart().

	   2026-09-14 redesign -- Carlos: "1440px min-width keeping aspect
	   ratio: 1440px x 720px." Scales down at narrower viewports (same
	   24px gutter convention as .rd-hero-container, §5) instead of a hard
	   min-width, so it never overflows small screens -- confirmed with
	   Carlos as "scale down, keep 2:1 ratio." Was 712×400 (Figma node
	   5181:13315) before this redesign. */
	position: relative;
	width: min(1440px, calc(100vw - 48px));
	aspect-ratio: 1440 / 720;
	border-radius: var(--rd-radius-card);
	overflow: hidden; /* clips video + scrim + fade + statement corners together */
	transform: scale(var(--rd-video-scale, 1));
	transform-origin: center center;
}

/* 2026-09-14 redesign -- back to a real <video> background; the
   procedural night-drive animation and its poster <img> fallback layer
   are both gone (see hero-video-v2.php file header). <video poster="...">
   now plays the poster's old role natively -- the prefers-reduced-motion
   fallback, and the first paint before playback starts -- so there's no
   separate poster layer to size here any more. */
.rd-hero-video {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	display: block;
	object-fit: cover;
	background: var(--rd-black);
}

/* will-change is added/removed by JS (rd-hero-stage--scrubbing class) only
   while the scrub is actually in progress, per the brief's performance
   requirements — never left on permanently. */
.rd-hero-stage--scrubbing .rd-hero-video-frame {
	will-change: transform;
}

.rd-hero-scrim {
	position: absolute;
	inset: 0;
	background:
		linear-gradient(180deg, var(--rd-scrim-strong) 0%, var(--rd-scrim-base) 45%, var(--rd-scrim-base) 70%, var(--rd-scrim-strong) 100%);
	pointer-events: none;
	/* No z-index needed -- normal DOM paint order (video, then this, then
	   the fade-to-card layer, then the Transition Statement) already
	   stacks everything correctly, and the frame's own overflow:hidden
	   means none of it can spill past that box. */
	opacity: var(--rd-scrim-opacity, 1);
}

/* ── 4a. Hero two-stage "tunnel vision" vignette (2026-09-16) ────────────
   A radial-gradient overlay on the video, sized/positioned/coloured to
   match each Figma stage's own gradient definition exactly (both stages
   use the same technique -- an SVG radialGradient with a gradientTransform
   turning a unit circle into an ellipse; the numbers below are that
   transform solved out into plain ellipse-size/position/color values):
     - Stage 1 (node 5161:5192): ellipse ~56% of the frame's own
       width/height, centred just below middle (50%, 54%), fully
       transparent at its own center -- Carlos: "you will be able to see
       the video background."
     - Stage 2 (node 5161:9266): a much smaller ellipse (~28%), centred
       low (50%, 82%), and never fully clear even at its own center
       (50% alpha there, vs. Stage 1's 0%) -- "the field of view will
       close, creating a tunnel vision where you will be able to see just
       a small portion [...] of the background."
   Animated by transitioning the custom properties themselves (requires
   @property below to register them as animatable types -- an unregistered
   custom property can't be transitioned, it just jumps), toggled by the
   same .is-stage-2 class initHeroStageLoop() (home-video-v2.js) uses for
   the headline/subtitle/CTA swap, so both move together. Browsers without
   @property support (pre-2024 Firefox/Safari) still work -- the class
   toggle still applies the new values, they just snap instead of easing. */
@property --rd-vig-rx { syntax: '<percentage>'; inherits: false; initial-value: 56%; }
@property --rd-vig-ry { syntax: '<percentage>'; inherits: false; initial-value: 56%; }
@property --rd-vig-cy { syntax: '<percentage>'; inherits: false; initial-value: 54%; }
@property --rd-vig-inner-a { syntax: '<number>'; inherits: false; initial-value: 0; }
@property --rd-vig-mid-a { syntax: '<number>'; inherits: false; initial-value: 0.5; }

.rd-hero-vignette {
	position: absolute;
	inset: 0;
	pointer-events: none;
	background-image: radial-gradient(
		ellipse var(--rd-vig-rx) var(--rd-vig-ry) at 50% var(--rd-vig-cy),
		rgba(6, 6, 6, var(--rd-vig-inner-a)) 0%,
		rgba(26, 24, 22, var(--rd-vig-mid-a)) 50%,
		rgba(46, 41, 37, 1) 100%
	);
	/* 2026-09-16 (Carlos, correction pass): sped way up and switched to
	   ease-out -- "like your headlights are suddenly out" -- most of the
	   motion should read in the first beat, not ease gently. Kept as a
	   (very short) transition rather than an instant snap so it still
	   reads as motion, not a jump-cut. Matches .rd-hero-copy's fade
	   duration below (VIGNETTE_TRANSITION_MS in home-video-v2.js) so the
	   two are perceived as one single event, not two staggered ones. */
	transition:
		--rd-vig-rx 180ms ease-out,
		--rd-vig-ry 180ms ease-out,
		--rd-vig-cy 180ms ease-out,
		--rd-vig-inner-a 180ms ease-out,
		--rd-vig-mid-a 180ms ease-out;
}

.rd-hero-vignette.is-stage-2 {
	/* 2026-09-16 (Carlos, correction pass): "window" (the visible-clear
	   ellipse) made 50% smaller -- was 28% (Figma node 5161:9266's own
	   value), then 14%. Tighter tunnel vision than the original Figma spec.
	   2026-09-24 (Carlos): "make rd-hero-vignette stage 2 visible part a
	   bit bigger" -- 18%, then "bump it up to 25%", then "bump it up a bit
	   more, to 33%" -- now past Figma's original 28% spec, wider than the
	   design ever called for. */
	--rd-vig-rx: 33%;
	--rd-vig-ry: 33%;
	--rd-vig-cy: 82%;
	--rd-vig-inner-a: 0.5;
	--rd-vig-mid-a: 0.75;
}

/* 2026-09-14 redesign -- Carlos: the hero no longer transitions into a
   row of app tiles; instead the video settles into its resting card and
   darkens to solid so the Transition Statement heading/subtitle (below)
   reveal on a plain, legible background rather than the video's last
   frame. Plain --rd-black layer, opacity driven by
   --rd-video-fade-opacity (FADE_TO_CARD_START/END in home-video-v2.js --
   currently the second half of the shrink window). Sits on top of the
   scrim in DOM order (see hero-video-v2.php). */
.rd-video-fade-to-card {
	position: absolute;
	inset: 0;
	background: var(--rd-black);
	pointer-events: none;
	opacity: var(--rd-video-fade-opacity, 0);
}

/* Transition Statement -- the H2 + subtitle that fade in once the video
   has settled into its solid card (STATEMENT_REVEAL_START/END in
   home-video-v2.js). Figma node 5786:22957: 96px horizontal / 48px
   vertical padding, 784px-wide text column, 12px gap between heading and
   subtitle. Hidden outright for prefers-reduced-motion (§7) -- see
   hero-video-v2.php file header for why. */
.rd-transition-statement {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 12px;
	padding: 48px 96px;
	text-align: center;
	opacity: var(--rd-statement-opacity, 0);
	transform: translateY(var(--rd-statement-y, 24px));
	pointer-events: none;
}

.rd-transition-statement__heading {
	font-family: 'Roboto', sans-serif;
	font-weight: 700;
	font-size: 48px;
	line-height: 52px;
	color: var(--rd-white);
	max-width: 720px;
	margin: 0;
}

.rd-transition-statement__subtitle {
	font-family: 'Roboto', sans-serif;
	/* 2026-09-25 (Carlos, stakeholder-review pass): "the subtitle in the
	   transition card must be #ADB5BD font-weight: 400" -- was
	   var(--rd-white)/400 (weight already matched); --rd-french-grey is
	   the existing token for this exact hex. Desktop only -- the mobile
	   override below is a separate, deliberately different decision from
	   earlier today (#6C757D/600, "supersedes the earlier stays white
	   decision") and hasn't been touched here. */
	font-weight: 400;
	font-size: 18px;
	line-height: 24px;
	color: var(--rd-french-grey);
	max-width: 784px;
	margin: 0;
}

/* ── 6. Hero content (announcement pill + copy + CTAs) ───────────────────────────────────────── */
.rd-hero-content {
	position: relative;
	z-index: 4;
	width: 100%;
	padding-top: calc(var(--rd-nav-height) + 96px);
	opacity: var(--rd-copy-opacity, 1);
	/* --rd-copy-scale / --rd-copy-y: the h1/subtitle/CTA transform along
	   with the video background like they're one monolithic unit, on the
	   same shrink window (VIDEO_SHRINK_END in home-video-v2.js). The
	   scale range is much subtler than the video's own (1 → ~0.94 vs. the
	   video's cover-size → 1) so headline text stays legible.

	   2026-09-14 redesign -- Carlos: "The [Transition] Statement will
	   appear H2 plus Subtitle" once the hero settles, so the hero's own
	   copy now simply fades out over that same shrink window
	   (--rd-copy-opacity = 1 - shrinkT, home-video-v2.js) instead of
	   timing itself to the app tiles' reveal, which this redesign
	   removes. */
	transform: scale(var(--rd-copy-scale, 1)) translateY(var(--rd-copy-y, 0));
	transform-origin: center top;
}

.rd-hero-container {
	max-width: 864px; /* Hero Column Cap — INSTRUCTIONS.md §4 container tiers */
	margin: 0 auto;
	padding: 0 24px;
	text-align: center;
}

/* Announcement pill — sits directly above the H1, 48px gap (Carlos,
   2026-09-01). Same tag/pill treatment and margin-bottom as the locked
   sitewide Hero-Centred pattern (INSTRUCTIONS.md §4a). */
.rd-hero-announcement {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 8px 16px;
	border-radius: var(--rd-radius-pill);
	background: rgba(255, 255, 255, 0.08);
	border: 1px solid rgba(255, 255, 255, 0.16);
	margin: 0 0 48px;
}

.rd-hero-announcement__text {
	color: rgba(255, 255, 255, 0.87);
	font-size: 14px;
	font-weight: 500;
}

.rd-hero-announcement__link {
	display: inline-flex;
	align-items: center;
	color: var(--rd-royal-blue);
	font-family: 'Space Grotesk', sans-serif;
	font-weight: 700;
	font-size: 14px;
	letter-spacing: 0.28px;
	text-transform: uppercase;
	text-decoration: none;
	white-space: nowrap;
}

.rd-hero-announcement__link i {
	font-size: 18px;
}

/* Wraps the h1/subtitle/CTA that initHeroStageLoop() (home-video-v2.js)
   swaps between stages -- a single set of elements, not two stacked
   blocks (file header explains why), so this only needs a plain opacity
   fade: JS adds .is-fading, waits for this transition to finish, swaps
   the text/link, then removes it. Duration here (180ms) is read back by
   the JS as HERO_STAGE_FADE_MS -- keep the two in sync if either changes.
   2026-09-16 (Carlos, correction pass): sped up from 700ms to 180ms, to
   match the vignette's own new fast transition above -- both now fire
   from the exact same trigger (initHeroStageLoop()'s advance()) so the
   headline change reads as caused by the vignette snapping shut, not as
   two independently-timed animations. */
.rd-hero-copy {
	transition: opacity 180ms ease;
}

.rd-hero-copy.is-fading {
	opacity: 0;
}

.rd-hero-headline {
	font-family: 'Space Grotesk', sans-serif;
	/* 2026-09-18 (Carlos): "roll back the H1 size to 66px so that it
	   matched the style of the rest of the site" -- was 400/48px/54px.
	   Now matches the sitewide hero heading pattern (.hero .main-heading
	   in style_new.css): 700/60px/66px. letter-spacing/color/margin left
	   as this page's own values.
	   2026-09-29 (Carlos: "make the Hero H1 font-weight: 400, just
	   apply to the home page for now"): weight back down to 400 --
	   size/line-height/letter-spacing untouched. This file only loads
	   on HomeVideoTemplateV2 (functions.php's home_video_v2_scripts()
	   guard), so this is already homepage-only; no other template's H1
	   uses .rd-hero-headline. Left the ≤767px mobile override (500,
	   Figma "H1-Mobile" MEDIUM spec) untouched -- a separate, already-
	   deliberate decision, not part of this request. */
	font-weight: 400;
	font-size: 60px;
	line-height: 66px;
	letter-spacing: -0.48px;
	color: var(--rd-white);
	margin: 0 0 24px;
}

.rd-hero-subtitle {
	font-family: 'Roboto', sans-serif;
	/* 2026-09-25 (Carlos, stakeholder-review pass): "the rd-hero-subtitle
	   must be #ADB5BD font-weight: 400" -- was var(--rd-white)/400
	   (weight already matched); --rd-french-grey is the existing token
	   for this exact hex. The mobile override below sets its own
	   font-weight (600, Figma's "Subtitle-Mobile" spec) but no color of
	   its own, so it inherits this new grey automatically. */
	font-weight: 400;
	font-size: 18px;
	line-height: 28px;
	color: var(--rd-french-grey);
	/* 48px (was 12px) -- Carlos, 2026-09-02 (13th pass): "the gap between
	   rd-hero-subtitle must be 48px." Gap to whatever follows -- citation
	   when present, otherwise straight to the CTAs. */
	margin: 0 0 48px;
}

.rd-hero-citation {
	font-family: 'Roboto', sans-serif;
	font-weight: 400;
	font-size: 14px;
	line-height: 20px;
	color: rgba(255, 255, 255, 0.6);
	margin: 0 0 48px;
}

.rd-hero-ctas {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 16px;
	flex-wrap: wrap;
}

/* Small label + chevron below the CTAs -- Figma node 5786:22837
   ("//01-Hero"): 24px gap from the CTA row above (the same flex column
   that groups both), Roboto Regular 12px/18px, 0.24px tracking,
   #E9ECEF (--rd-neutral-200), 6px gap to the chevron. New 2026-09-14;
   reuses the existing bi-chevron-down icon font glyph already used
   elsewhere on this page (announcement/CTA arrows) rather than a new
   exported asset -- the two CTA buttons above are untouched. */
.rd-hero-scroll-label {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: 6px;
	margin-top: 24px;
	padding: 10px 18px;
	color: var(--rd-neutral-200);
	font-family: 'Roboto', sans-serif;
	font-size: 12px;
	line-height: 18px;
	letter-spacing: 0.24px;
	/* 2026-09-18 (Carlos): "add some opacity to the rd-hero-scroll-label
	   and chevron, maybe 50% opacity" -- set on the container so it
	   covers both the label text and the nested chevron (.rd-hero-scroll-label i)
	   in one rule rather than duplicating it. */
	opacity: 0.5;
}

.rd-hero-scroll-label i {
	font-size: 12px;
}

/* ── 7. Logo marquee ──────────────────────────────────────────────────────
   2026-09-02 -- Carlos: "replace the current marquee with the existing
   logo-wall.php with some modifications: (1) transparent background,
   (2) anchored to the bottom of the browser, (3) fades out as soon as
   scrolling starts." This used to be a hand-rolled reimplementation of
   the sitewide Logo Wall Marquee Component (style_new.css) with its own
   BEM-ish classes (.rd-hero-marquee__*) and a dark-tinted copy of the
   same fade-edge/keyframe technique. hero-video.php now renders the REAL
   shared component (template-parts/logo-wall.php, pointed at this page's
   existing marquee_eyebrow/marquee_logos ACF fields via $args) instead --
   so everything below is page-scoped OVERRIDES on that shared component's
   own classes (.logo-wall-section etc., global in style_new.css), the
   same pattern already used for the nav override in §2. Started with
   just the 3 requested changes above; two more follow-up overrides were
   added 2026-09-02 (same day) once Carlos saw it live -- the fade-edge
   gradients removed entirely (rather than just recoloured), and the
   eyebrow colour pinned to #8C959D. Everything else (the -25% 4-set-loop
   animation, sizing) is still inherited as-is from the shared component. */

/* (2) Anchored to the bottom of the browser -- pulled out of
   .rd-hero-stage's normal flex flow (it used to be the 2nd of 2 in-flow
   flex children, positioned by `justify-content: space-between` against
   .rd-hero-content) and absolutely pinned to the stage's bottom edge
   instead, so its position is fixed regardless of how tall the hero copy
   above it happens to render. z-index matches .rd-hero-content (4) since
   both are meaningful (non-decorative, non-aria-hidden) content that
   should stay above the video/tiles if they ever overlap. */
.rd-home-v2 .logo-wall-section {
	position: absolute;
	inset: auto 0 0 0;
	z-index: 4;
	padding: 0 48px 48px;
	/* (1) Transparent background -- was the shared component's solid
	   #FEF7ED (correct for the flat-colour sections it normally sits in,
	   wrong here where it sits over the video). */
	background: transparent;
	/* (3) Fades out as soon as scrolling starts -- reuses the exact same
	   --rd-marquee-opacity custom property the old marquee read (JS:
	   MARQUEE_FADE_END = 0.10, unchanged -- already "as soon as
	   scrolling starts," first 10% of scroll progress). CSS custom
	   properties inherit down from .rd-hero-stage where JS writes them,
	   so this Just Works without any JS changes. */
	opacity: var(--rd-marquee-opacity, 1);
}

/* Fade-edge overlays (global technique, style_new.css) -- 2026-09-02:
   removed entirely. These were first recoloured dark (to match sitting
   over the video/scrim instead of the component's usual flat #FEF7ED
   section background), but Carlos: "there are some gradient of each end
   of the logo wall. please remove." A solid-colour fade-to-transparent
   edge only reads correctly over a flat-colour background anyway -- over
   the video it was never going to blend cleanly regardless of which
   colour it used, so removing it (not just recolouring it again) is the
   right fix. Every other page keeps the shared component's own
   #FEF7ED-gradient edges untouched; this only removes them inside
   .rd-home-v2. */
.rd-home-v2 .logo-marquee-wrapper::before,
.rd-home-v2 .logo-marquee-wrapper::after {
	display: none;
}

/* Eyebrow (subheading) colour -- 2026-09-02, Carlos: "The eyebrow must
   be #8C959D." Overrides the shared component's inherited default (base
   h6 styling, style_new.css -- a red accent colour tuned for the site's
   usual light sections, not this dark video). #8C959D is already a
   named token here (--rd-neutral-500, §1) -- reused rather than
   hardcoded again. */
.rd-home-v2 .logo-wall-subheading {
	color: var(--rd-neutral-500);
}

/* Logo treatment -- white silhouette instead of the component's default
   dimmed-grayscale (which is tuned for the light #FEF7ED section
   background and would be nearly invisible against a dark video). Same
   values the old .rd-hero-marquee__item img rule used. */
.rd-home-v2 .logo-marquee-img {
	filter: grayscale(100%) brightness(0) invert(1);
	opacity: 0.7;
}

/* ── 8. Your Program Now (bento stat cards) ──────────────────────────────
   Figma node 5786:22141 ("//03-Your Program Now"), 2026-09-15. Centred
   eyebrow+heading (48px gap below it, matches the header's own 118px
   frame height vs. the panel's 166px top offset in Figma), then the
   gradient "box" panel -- the same "sections with background" style
   Carlos defined for the V1 Gap section (max-width 1632px, 96px padding,
   15px radius) -- holding a row of problem-stat cards and one wide stat
   card below (12px gap between them, matches the "wo/READI" panel's own
   Figma gap value). Both card shapes share one class (.rd-program-card)
   with a --wide modifier so the scroll-reveal animation (home-video-v2.js
   initProgramNowCardReveal()) can target every card, wide one included,
   with a single selector. */
.rd-program-now__container {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 48px;
}

	/* 2026-09-28 (Carlos: "the headers of the page max width are
	   inconsistent, it needs to be 720px"): was 784px, out of step with
	   the heading's own 720px cap inside it (and with the Final CTA
	   section's header block, which had no cap at all -- see
	   .rd-final-cta__text in style_new.css). All 5 header wrappers now
	   share this same 720px cap. */
.rd-program-now__header {
	max-width: 720px;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 12px;
	text-align: center;
}

	/* 2026-09-28 (code review pass, Carlos: "color only, keep 16px"): desktop eyebrow color brought in line with the eyebrow spec
	   (Space Grotesk, #ADB5BD) already applied on mobile -- size stays 16px here, matching how body copy already keeps one
	   color across breakpoints but a larger desktop size (see __subtitle/__body rules' "paragraph 18px" note). */
.rd-program-now__eyebrow {
	font-family: 'Space Grotesk', sans-serif;
	font-weight: 700;
	font-size: 16px;
	line-height: 22px;
	letter-spacing: 0.8px;
	text-transform: uppercase;
	/* Carlos, 2026-09-15: "It is just the card's eyebrow, not the one in
	   the header. Revert that one back to it previous color." Back to
	   Figma's own spec (#8C959D) for this header-level eyebrow -- the
	   #F76E6E request was for .rd-program-card__eyebrow only. */
	color: #ADB5BD;
	margin: 0;
}

.rd-program-now__heading {
	font-family: 'Roboto', sans-serif;
	font-weight: 700;
	/* 2026-09-24 (Carlos): "the header must be H2 (48px.) and 720px. max
	   width" -- standardized to the shared section-header spec (matches
	   .rd-transition-statement__heading and Figma's DESKTOP/H2 token: 48/52).
	   Markup tag also changed h3 -> h2 in the template. Was 36px/512px. */
	font-size: 48px;
	line-height: 52px;
	color: var(--rd-black);
	max-width: 720px;
	margin: 0;
}

/* 2026-09-24 (Carlos, Figma node 5954:30079): "All subtitles (body) must
   be paragraph (18px.) #6C757D." */
.rd-program-now__subtitle {
	font-family: 'Roboto', sans-serif;
	font-weight: 400;
	font-size: 18px;
	line-height: 24px;
	color: #6C757D;
	margin: 0;
}

.rd-program-now__panel {
	width: 100%;
	max-width: 1632px;
	margin: 0 auto;
	padding: 96px;
	border-radius: var(--rd-radius-card);
	/* Figma: linear-gradient(180deg, rgba(233,236,239,.5) -> rgba(248,249,250,0)) --
	   a translucent tint fading to fully transparent, not one of the two
	   flat "Gradient 01/02" options from Carlos's original box-style spec
	   (see the V1 Gap section comment, home-video.css) -- this panel uses
	   its own gradient instead. */
	background: linear-gradient(180deg, rgba(233, 236, 239, 0.5) 0%, rgba(248, 249, 250, 0) 100%);
	display: flex;
	flex-direction: column;
	gap: 16px;
}

.rd-program-now__row {
	display: flex;
	flex-wrap: wrap;
	gap: 16px;
	width: 100%;
}

/* 2026-09-16 (Carlos, Figma re-check, node 5835:25074): row cards now
   carry a coral border and a 10px radius (was the shared 15px
   --rd-radius-card) -- scoped to cards inside the row specifically so it
   doesn't also apply to the wide card below, which lives outside
   .rd-program-now__row and gets its own border/radius treatment (§8). */
.rd-program-now__row .rd-program-card {
	/* 2026-09-24 (Carlos, polish pass): "remove the stroke from the top
	   cards" -- removes the --rd-danger-050 border added 2026-09-17.
	   border-radius stays (shape, not a stroke); box-shadow stays none
	   (Carlos already removed that on 2026-09-16). */
	border: none;
	border-radius: 10px;
	box-shadow: none;
}

.rd-program-card {
	background: var(--rd-white);
	border-radius: var(--rd-radius-card);
	/* Figma "Shadow/xl": offset (0,20), blur 24, spread -4, 10% black. */
	box-shadow: 0 20px 24px -4px rgba(16, 24, 40, 0.1);
	padding: 48px;
	display: flex;
	flex-direction: column;
	justify-content: space-between;
	gap: 24px;
	flex: 1 1 300px;
	/* 2026-09-21 (Carlos, Figma node 5954:30079): "the cards now have a
	   fix-height of 464px" -- was min-height: 345px. A fixed height (not
	   min-height) so the row + the --wide card below (same base class,
	   see §8 file header) both land on exactly 464px, matching Figma's
	   explicit h-[464px] on both card shapes. Desktop only -- the mobile
	   override at the bottom of this file keeps its own min-height. */
	height: 464px;
	/* Progressive-enhancement scroll-reveal transition -- inert (never
	   triggers) unless JS adds .rd-program-card--pre-reveal below, so a
	   plain CSS reader with no JS/motion context still sees a normal,
	   static, fully-visible card. Same pattern as the V1 Gap section's
	   .rd-gap-stat reveal. */
	transition: opacity 600ms ease-out, transform 600ms ease-out;
}

/* JS-added starting state (initProgramNowCardReveal(), home-video-v2.js):
   added to each card BEFORE observing it, only when JS runs,
   IntersectionObserver exists, and the user hasn't asked for reduced
   motion. If JS never runs, this class is never added, so the card
   simply stays at its normal opacity: 1 default above. */
.rd-program-card.rd-program-card--pre-reveal {
	opacity: 0;
	transform: translateY(24px);
}

.rd-program-card__top {
	display: flex;
	flex-direction: column;
	gap: 24px;
}

/* 2026-09-21 (Carlos, Figma node 5954:30079): "we add icons and
   background to the rd-program-card" -- a small line-icon on its own
   subtle gradient "chip" above the eyebrow/heading. Figma's own
   from-[rgba(253,226,226,.5)] to-[rgba(253,226,226,0)] gradient, inlined
   rather than tokenized since it's a one-off (same convention as the
   wide card's gradient, §8 below).
   2026-09-22 (Carlos): "the icon background must [be] a gradient... it
   should fill the width of the div: rd-program-card__top" -- was a
   fit-content square chip; now a full-width band (width: 100% of
   .rd-program-card__top, the flex column it sits in) with the icon
   pinned to the left edge (justify-content: flex-start, was center) so
   it stays over the gradient's opaque left end rather than drifting to
   the middle of a band that's now much wider than the icon itself.
   2026-09-24 (Carlos, polish pass): "add two more gradients to the
   icon-wrap related with the pill color" -- the single gradient below
   is now the --danger variant; --warning and --success are new. Each
   card gets whichever variant matches its own pill_variant (PHP), so
   the icon chip and the pill beneath it always read as the same color. */
.rd-program-card__icon-wrap {
	display: flex;
	align-items: center;
	justify-content: flex-start;
	width: 100%;
	padding: 24px 24px 24px 12px;
	border-radius: 5px;
}

.rd-program-card__icon-wrap--danger {
	background: linear-gradient(90deg, rgba(253, 226, 226, 0.5) 0%, rgba(253, 226, 226, 0) 100%);
}

.rd-program-card__icon-wrap--warning {
	background: linear-gradient(90deg, rgba(248, 208, 134, 0.25) 0%, rgba(248, 208, 134, 0) 100%);
}

.rd-program-card__icon-wrap--success {
	background: linear-gradient(90deg, rgba(171, 191, 247, 0.25) 0%, rgba(171, 191, 247, 0) 100%);
}

.rd-program-card__icon {
	display: block;
	width: 48px;
	height: 48px;
}

/* 2026-09-22 (Carlos): replaces the scale+glow pulse above -- "I have the
   steps... All icons will have a 3 steps looping animation" -- with the
   exact per-icon 3-step animation Carlos specified (Figma storyboard
   nodes 6065:41892 / 6069:41900 / 6069:41901 "now" and 6069:41902-41904
   "READI" -- he confirmed each READI icon "uses the same mechanism" as
   its "now" counterpart). Each icon's SVG source
   (assets/images/program-now/*.svg, assets/images/why-bento/*.svg) now
   ships its own internal sub-elements with the classes referenced below
   baked in -- rd_inline_svg_icon() (functions.php) only ever touches the
   root <svg> tag, so these classes pass through untouched. Rules are
   written as descendant selectors off .rd-program-card__icon /
   .rd-why-card__icon rather than by card position, so they apply
   wherever a given icon's markup shows up; a custom ACF-uploaded icon
   that doesn't carry these classes just renders static -- nothing
   breaks (same graceful-fallback spirit as the <img> fallback below).

   Shared drawing technique: every "draws in" element carries
   pathLength="1" in its SVG source, so ONE stroke-dashoffset: 1 -> 0
   keyframe (per mechanism) works on any shape -- circle, line, path --
   without hand-computing its real arc length. --rd-icon-cycle is the
   single knob for how fast all three mechanisms loop; keep it identical
   across all three so a row of three cards stays visually calm rather
   than each icon looping at its own odd pace. */
:root {
	--rd-icon-cycle: 4.8s;
}

.rd-icon__draw--a,
.rd-icon__draw--b,
.rd-icon__draw--x,
.rd-icon__draw--circle,
.rd-icon__draw--mark {
	stroke-dasharray: 1;
	stroke-dashoffset: 0;
}

.rd-icon__accent,
.rd-icon__markwrap,
.rd-icon__hand {
	opacity: 1;
}

.rd-icon__row--big {
	transform-origin: center;
	transform: translateY(0);
}

/* 2026-09-22 (Carlos, round 2): "the big rectangle overlays the other
   [rows]; this should behave as if the big rectangle will replace the
   smaller and the small will replace the big from the stage before."
   The top/bottom row brackets used to sit in a permanently-visible
   .rd-icon__static group -- fine when .rd-icon__row--big only ever
   passed behind them, wrong now that it visits their exact slot. Each
   slot now has its own opacity animation (below) that's the exact
   inverse of .rd-icon__row--big's at that slot's phase, so it's always
   either the small bracket OR the big rectangle showing at a given
   position, never both.
   2026-09-22 (Carlos, round 3, Figma "Stage 01" reference, node
   6065:41809): the top-hold offset below used to be a guess (computed
   by symmetry with the real bottom-hold offset, since no real Figma
   data for the top state existed yet) -- that guess put the big
   rectangle's translated bottom edge past where the middle bracket
   starts, overlapping it. Stage 01's real geometry shows the design
   doesn't just slide the big rectangle to the top: the middle bracket
   also shifts down (.rd-icon__row--small-mid gets its own translateY
   animation below) to stay clear of it. The bottom bracket does not
   move at any phase (confirmed identical to its rest position in both
   Stage 01 and Stage 03). */
.rd-icon__row--small-top,
.rd-icon__row--small-bottom {
	opacity: 1;
}

.rd-icon__row--small-mid {
	opacity: 0;
	transform-origin: center;
	transform: translateY(0);
}

.rd-icon__mark-sync {
	transform-origin: center;
	transform: translateY(0);
}

.rd-icon__pop--check {
	transform-origin: center;
	transform-box: fill-box;
	transform: scale(1);
	opacity: 1;
}

/* Joiner Access (icon-joiner-access.svg, both color variants): the
   circle badge draws in, then the line/checkmark inside it draws in,
   then both fade out together and the cycle resets. The person glyph
   (.rd-icon__static) never animates.
   2026-09-22 (Carlos, round 2): "there is an extra stage... after the
   animation ends there is a brief moment on which the circle with the
   check mark appears and disappear." The draw keyframes used to stay
   at stroke-dashoffset: 0 (fully drawn) all the way to 100%, only
   resetting to 1 (undrawn) at the exact instant the loop wraps back to
   0% -- the same instant .rd-icon__accent's opacity also jumps 0 -> 1.
   Two independent animations on different elements snapping at the
   exact same instant is exactly the kind of boundary a browser can
   render a stray frame of (opacity already 1, dashoffset not yet
   reset, or vice versa) -- that stray frame is the flash Carlos saw.
   Now the draw keyframes reset to undrawn at 75%, the same point
   .rd-icon-joiner-fade already reaches opacity: 0, so both properties
   sit safely in their hidden state together for the entire 75%-100%
   pause -- the loop wrap has nothing left to change on either side of
   it. */
.rd-program-card__icon .rd-icon__accent,
.rd-why-card__icon .rd-icon__accent {
	animation: rd-icon-joiner-fade var(--rd-icon-cycle) ease-in-out infinite;
}

.rd-program-card__icon .rd-icon__draw--a,
.rd-why-card__icon .rd-icon__draw--a {
	animation: rd-icon-joiner-draw-a var(--rd-icon-cycle) ease-in-out infinite;
}

.rd-program-card__icon .rd-icon__draw--b,
.rd-why-card__icon .rd-icon__draw--b {
	animation: rd-icon-joiner-draw-b var(--rd-icon-cycle) ease-in-out infinite;
}

@keyframes rd-icon-joiner-draw-a {
	0% { stroke-dashoffset: 1; }
	25%, 74% { stroke-dashoffset: 0; }
	75%, 100% { stroke-dashoffset: 1; }
}

@keyframes rd-icon-joiner-draw-b {
	0%, 25% { stroke-dashoffset: 1; }
	45%, 74% { stroke-dashoffset: 0; }
	75%, 100% { stroke-dashoffset: 1; }
}

@keyframes rd-icon-joiner-fade {
	0%, 62% { opacity: 1; }
	75%, 100% { opacity: 0; }
}

/* Service Desk (icon-service-desk.svg, both color variants): the big
   row loops top -> middle -> bottom (the same shape, repositioned --
   the export only gave us its "middle" geometry, so the top/bottom
   offsets below are measured from the two small static row brackets it
   passes over), and the accent mark (an X on "now", a check-in-circle
   on READI) redraws next to it at every step. */
.rd-program-card__icon .rd-icon__row--big,
.rd-why-card__icon .rd-icon__row--big {
	animation:
		rd-icon-service-big-pos var(--rd-icon-cycle) ease-in-out infinite,
		rd-icon-service-big-opacity var(--rd-icon-cycle) ease-in-out infinite;
}

.rd-program-card__icon .rd-icon__row--small-top,
.rd-why-card__icon .rd-icon__row--small-top {
	animation: rd-icon-service-small-top var(--rd-icon-cycle) ease-in-out infinite;
}

.rd-program-card__icon .rd-icon__row--small-mid,
.rd-why-card__icon .rd-icon__row--small-mid {
	animation:
		rd-icon-service-small-mid var(--rd-icon-cycle) ease-in-out infinite,
		rd-icon-service-mid-pos var(--rd-icon-cycle) ease-in-out infinite;
}

.rd-program-card__icon .rd-icon__row--small-bottom,
.rd-why-card__icon .rd-icon__row--small-bottom {
	animation: rd-icon-service-small-bottom var(--rd-icon-cycle) ease-in-out infinite;
}

.rd-program-card__icon .rd-icon__markwrap,
.rd-why-card__icon .rd-icon__markwrap {
	animation: rd-icon-service-mark-visibility var(--rd-icon-cycle) ease-in-out infinite;
}

/* 2026-09-22 (Carlos, Figma "Stage 03" reference, node 6065:41821): the
   X (or check-in-circle, READI) isn't fixed in place -- it travels with
   the big rectangle, so it re-draws beside wherever the big rectangle
   currently sits. Reuses the exact same keyframe as .rd-icon__row--big
   (§8) so the two stay pixel-locked to each other. */
.rd-program-card__icon .rd-icon__mark-sync,
.rd-why-card__icon .rd-icon__mark-sync {
	animation: rd-icon-service-big-pos var(--rd-icon-cycle) ease-in-out infinite;
}

.rd-program-card__icon .rd-icon__draw--x,
.rd-why-card__icon .rd-icon__draw--circle {
	animation: rd-icon-service-x-draw var(--rd-icon-cycle) ease-in-out infinite;
}

.rd-why-card__icon .rd-icon__pop--check {
	animation: rd-icon-service-pop var(--rd-icon-cycle) ease-in-out infinite;
}

@keyframes rd-icon-service-big-pos {
	0%, 32.9% { transform: translateY(-14.1754px); }
	33%, 65.9% { transform: translateY(0); }
	66%, 100% { transform: translateY(13.8252px); }
}

@keyframes rd-icon-service-mid-pos {
	0%, 32.9% { transform: translateY(11.001px); }
	33%, 100% { transform: translateY(0); }
}

@keyframes rd-icon-service-big-opacity {
	0%, 3% { opacity: 0; }
	6%, 28% { opacity: 1; }
	32%, 32.9% { opacity: 0; }
	36%, 61% { opacity: 1; }
	65%, 65.9% { opacity: 0; }
	69%, 94% { opacity: 1; }
	98%, 100% { opacity: 0; }
}

/* Exact inverse of rd-icon-service-big-opacity at each slot's own
   phase window; flat (fully visible) everywhere else, since the big
   rectangle only ever visits one slot at a time. */
@keyframes rd-icon-service-small-top {
	0%, 3% { opacity: 1; }
	6%, 28% { opacity: 0; }
	32%, 100% { opacity: 1; }
}

@keyframes rd-icon-service-small-mid {
	0%, 32%, 32.9% { opacity: 1; }
	36%, 61% { opacity: 0; }
	65%, 100% { opacity: 1; }
}

@keyframes rd-icon-service-small-bottom {
	0%, 65%, 65.9% { opacity: 1; }
	69%, 94% { opacity: 0; }
	98%, 100% { opacity: 1; }
}

@keyframes rd-icon-service-mark-visibility {
	0%, 2% { opacity: 0; }
	6%, 28% { opacity: 1; }
	32%, 32.9% { opacity: 0; }
	36%, 61% { opacity: 1; }
	65%, 65.9% { opacity: 0; }
	69%, 94% { opacity: 1; }
	98%, 100% { opacity: 0; }
}

@keyframes rd-icon-service-x-draw {
	0%, 2% { stroke-dashoffset: 1; }
	6%, 28% { stroke-dashoffset: 0; }
	32%, 32.9% { stroke-dashoffset: 1; }
	36%, 61% { stroke-dashoffset: 0; }
	65%, 65.9% { stroke-dashoffset: 1; }
	69%, 94% { stroke-dashoffset: 0; }
	98%, 100% { stroke-dashoffset: 1; }
}

@keyframes rd-icon-service-pop {
	0%, 4% { transform: scale(0.4); opacity: 0; }
	7%, 28% { transform: scale(1); opacity: 1; }
	32%, 32.9% { transform: scale(0.4); opacity: 0; }
	37%, 61% { transform: scale(1); opacity: 1; }
	65%, 65.9% { transform: scale(0.4); opacity: 0; }
	70%, 94% { transform: scale(1); opacity: 1; }
	98%, 100% { transform: scale(0.4); opacity: 0; }
}

/* Leaver Removal (icon-leaver-removal.svg, both color variants): the
   hand fades in, then the accent mark (a checkmark on "now", an X on
   READI) draws in, then both fade out together. The box/slot bracket
   (.rd-icon__static) never animates. */
.rd-program-card__icon .rd-icon__hand,
.rd-why-card__icon .rd-icon__hand {
	animation: rd-icon-leaver-hand var(--rd-icon-cycle) ease-in-out infinite;
}

.rd-program-card__icon .rd-icon__draw--mark,
.rd-why-card__icon .rd-icon__draw--mark {
	animation: rd-icon-leaver-mark var(--rd-icon-cycle) ease-in-out infinite;
}

@keyframes rd-icon-leaver-hand {
	0% { opacity: 0; }
	15%, 55% { opacity: 1; }
	70%, 100% { opacity: 0; }
}

@keyframes rd-icon-leaver-mark {
	0%, 15% { stroke-dashoffset: 1; opacity: 0; }
	16% { opacity: 1; }
	38%, 55% { stroke-dashoffset: 0; opacity: 1; }
	70%, 100% { opacity: 0; }
}

/* 2026-09-22 (Carlos, final polish pass): "Remove the icons animations,
   just leave static icons at their final state." The 3-step icon
   animation above is purely decorative, so it's switched off
   unconditionally here rather than only under prefers-reduced-motion.
   The base (non-animated) values set alongside each @keyframes rule
   above -- stroke-dashoffset: 0 (fully drawn), opacity: 1 (fully
   visible), transform: translateY(0)/scale(1) (resting position) -- are
   exactly the icon's normal, "finished" look, so turning off animation
   here is enough; nothing needs a value override too. !important
   because every per-element animation rule above (".rd-program-card__icon
   .rd-icon__draw--x" etc.) is two classes deep -- MORE specific than a
   plain ".rd-program-card__icon *" universal-selector override would be
   (the "*" contributes no specificity of its own), so without it this
   rule would silently lose that fight and the icons would keep
   animating. This used to live only inside @media
   (prefers-reduced-motion: reduce); now it applies always, so that
   block's copy of the same rule has been removed. */
.rd-program-card__icon *,
.rd-why-card__icon * {
	animation: none !important;
}

.rd-program-card__kicker {
	display: flex;
	flex-direction: column;
	gap: 12px;
	color: var(--rd-coral);
}

/* 2026-09-24 (Carlos, Figma node 5954:30079): the plain-text eyebrow is
   now a colored pill/badge above the heading -- ACF field name kept as
   "eyebrow" (see functions.php) but rendered with this class instead.
   Base rule is shape/type only; each variant below sets its own colors
   (Figma's two tag styles, plus a third "neutral" gray used on the
   bottom-left card). */
.rd-program-card__pill {
	display: inline-flex;
	align-items: center;
	align-self: flex-start;
	padding: 6px 12px;
	border-radius: 5px;
	font-family: 'Space Grotesk', sans-serif;
	font-weight: 700;
	font-size: 16px;
	line-height: 22px;
	letter-spacing: 0.8px;
	text-transform: uppercase;
	white-space: nowrap;
	margin: 0;
}

.rd-program-card__pill--warning {
	background: #F3B61F;
	color: #2E2925;
}

.rd-program-card__pill--danger {
	background: #E51F37;
	color: #FEE8E8;
}

/* 2026-09-24 (Carlos, polish pass): third pill color -- "Pill Color:
   Success (blue#3D6BEA & #FFFFFF text)". Reuses the existing
   --rd-royal-blue token rather than a new hardcoded hex, since it's the
   same blue already used for the CTA link and the grid's active cells. */
.rd-program-card__pill--success {
	background: var(--rd-royal-blue);
	color: var(--rd-white);
}

.rd-program-card__pill--neutral {
	background: var(--rd-neutral-200);
	color: var(--rd-neutral-500);
}

.rd-program-card__heading {
	font-family: 'Roboto', sans-serif;
	font-weight: 700;
	/* 2026-09-21 (Carlos, Figma node 5954:30079): "I increased the font
	   size of the rd-program-card_header" -- back to H3 (36/42), re-
	   reverting the 2026-09-17 rollback to H4 (24/30). Third time this
	   scale has flipped -- Figma is now the source of truth. */
	font-size: 36px;
	line-height: 42px;
	/* 2026-09-17 (Carlos, Figma re-check, node 5835:25074): heading color
	   corrected from --rd-coral to --rd-black (Figma's text-[#2e2925]) --
	   the eyebrow above keeps --rd-coral, only the heading changed. */
	color: var(--rd-black);
	/* 2026-09-24 (Carlos): "the rd-program-card_heading is 512px width,
	   it should fill the width of the div: rd-program-card_kicker" --
	   dropped the max-width cap entirely. .rd-program-card__kicker is a
	   column flex container with no align-items override, so its
	   default stretch already sizes the heading to the kicker's full
	   width once nothing here constrains it. */
	margin: 0;
}

/* 2026-09-24 (Carlos, polish pass): "the card body text must be
   #6C757D" -- was --rd-onix (#343A40). Matches the site-wide 18px/
   #6C757D body-copy spec from the header/subtitle pass earlier the same
   day. --rd-state-grey is that same #6C757D value, already tokenized. */
.rd-program-card__body {
	font-family: 'Roboto', sans-serif;
	font-weight: 400;
	font-size: 18px;
	line-height: 24px;
	color: var(--rd-state-grey);
	margin: 0;
}

/* Now redundant with the base rule above (both #6C757D) -- kept as a
   harmless no-op rather than removed, in case the bottom-left card's
   body copy ever needs to diverge from the row cards' again. */
.rd-program-card__body--plain {
	color: var(--rd-state-grey);
}

/* 2026-09-24 (Carlos, polish pass): "the Bold Lead-in must stay as is"
   -- .rd-program-card__body just went from --rd-onix to the lighter
   --rd-state-grey; without its own color the inherited <strong> lead-in
   (e.g. "73%") would have lightened right along with it. Locks it back
   to the original dark body color. */
.rd-program-card__stat {
	font-weight: 800;
	color: var(--rd-onix);
}

/* Exact values reused from the V1 Gap section's .rd-gap-stat__source. */
.rd-program-card__citation {
	font-family: 'Roboto', sans-serif;
	font-weight: 400;
	font-size: 12px;
	line-height: 18px;
	letter-spacing: 0.24px;
	color: var(--rd-onix);
	margin: 0;
}

.rd-program-card__citation a {
	color: inherit;
	text-decoration: underline;
}

/* 2026-09-24 (Carlos, Figma node 5954:30079): the bottom row is now 2
   cards side by side (replaces the old single full-width "coral
   gradient" wide card, previously documented here -- see git history on
   this branch for that markup/CSS if it's ever needed again). */
/* "The left bottom card is 2/3 of the 1440px max width container" --
   Carlos, 2026-09-24. CSS Grid rather than flex here: a flex-basis:0 +
   flex-grow 2:1 split looked right until real padding/box-sizing:border-
   box entered the picture -- each card's own 48px padding becomes a
   fixed "floor" that flex-grow only distributes space ABOVE, skewing a
   nominal 2:1 grow ratio to roughly 64:36 in practice. Grid's `fr` unit
   doesn't have that quirk -- minmax(0, Nfr) (the 0 stops each track from
   also flooring at its content's min-content width) gives a clean,
   padding-independent 2:1 split.
   2026-09-24 (Carlos, correction): "the bottom right card must be
   exactly the same width as the top cards: 469.33px at 1440px
   container." A plain 2fr:1fr split gives ~474.67px instead (1424px of
   space, minus the one 16px gap here, split 1:2 -- not the same math
   as the top row's three-way split with two 16px gaps). Right column
   is now sized with the exact same formula as .rd-program-now__row's
   flex-grown cards -- (100% - 2 gaps) / 3 -- so it's pixel-identical
   to a top-row card at every width, not just 1440px; left column keeps
   taking the remainder via 1fr. */
.rd-program-now__bottom-row {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, calc((100% - 2 * 16px) / 3));
	align-items: stretch;
	gap: 16px;
	width: 100%;
}

/* 2026-09-24 (Carlos, polish pass): "remove the drop shadows from the
   bottom cards" -- overrides the base .rd-program-card's Shadow/xl,
   same pattern already used for the top row above. */
.rd-program-now__bottom-row .rd-program-card {
	box-shadow: none;
}

/* Otherwise a normal card: inherits .rd-program-card's white bg/shadow/
   464px height, no special border (Figma shows no colored border on
   either bottom card, unlike the row cards above). .rd-program-card's
   own `flex: 1 1 300px` is harmless dead weight here -- these are grid
   items now, flex properties on a grid item have no effect. */
.rd-program-card--bottom-right {
	align-items: center;
	justify-content: center;
}

.rd-program-card__grid-wrap {
	position: relative;
	width: 100%;
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
}

/* 2026-09-24 (Carlos, polish pass): "the rd-program-card_grid isn't
   fitted inside the 48px padding -- adjust so the grid don't overflow."
   The old rule sized the grid off 100% WIDTH with aspect-ratio:1/1, but
   on desktop the card's height is fixed (464px) while its width flexes
   with the viewport -- once the card got wide enough, a width-driven
   square grid (plus the CTA's own row below it) taller-than-available
   overflowed the bottom padding. Sizing off height instead (height:100%,
   width:auto, same aspect-ratio:1/1) guarantees the square never exceeds
   the card's available vertical space; .rd-program-card__grid-wrap's
   justify-content:center re-centers it horizontally if that leaves
   spare width. The CTA no longer needs its own row below the grid (see
   .rd-program-card__grid-cta below) so there's no extra height to budget
   for beyond the grid itself. */
.rd-program-card__grid {
	position: relative;
	width: auto;
	height: 100%;
	aspect-ratio: 1 / 1;
	border-radius: 4px;
	/* 2026-09-24 (Carlos, polish pass): "the background grid must look as
	   the SVG linked" (Figma node 6081:42278) -- that reference is a soft
	   radial wash (Brand/Neutral/300 #DEE2E6 center fading to white)
	   sitting BEHIND a grid of plain white cells; the faint "grid lines"
	   people actually see are this gradient showing through the gaps
	   between cells, not a stroke on each cell. Approximates Figma's
	   rotated/elliptical gradientTransform with a simple centered radial
	   -- close enough at this size that the rotation isn't perceptible. */
	background: radial-gradient(ellipse at center, var(--rd-neutral-300) 0%, var(--rd-white) 60%);
}

.rd-program-card__grid-cells {
	display: grid;
	grid-template-columns: repeat(10, 1fr);
	grid-template-rows: repeat(10, 1fr);
	gap: 3px;
	width: 100%;
	height: 100%;
}

/* 2026-09-24 (Carlos, polish pass): "the animated squares shouldn't
   have a fill, or they must be #FFFFFF filled" -- tried transparent
   first, but that also hid the static background grid Carlos asked
   for (see next comment), so cells now take the #FFFFFF option
   Carlos offered as the alternative.
   2026-09-24 (Carlos, correction pass): Figma's real final-state
   reference (node 6093:48155) also puts a blue border on active cells
   -- border-width is set here (not just on the active rule below) with
   a transparent starting color, box-sizing:border-box so the border
   never changes the cell's box size, so only border-color needs to
   transition on activation.
   2026-09-24 (Carlos, 2nd correction): "I cannot see the subtle grid
   in the background" -- Figma's static-grid reference (node
   6093:47937) isn't a separate line-drawing; it's this exact 10x10
   cell layout with white cells and ~1-3px gaps, where the gaps reveal
   the radial-gradient card background underneath as thin grid lines
   (with slightly darker dots where a horizontal and vertical gap
   cross). Transparent cells removed that contrast entirely -- the
   gradient showed through the cells too, so nothing read as a grid.
   Filling cells #FFFFFF restores the contrast, matching Figma. */
.rd-program-card__grid-cell {
	border-radius: 4px;
	background: var(--rd-white);
	border: 1px solid transparent;
	box-sizing: border-box;
	transition: background-color 350ms ease-in-out, border-color 350ms ease-in-out;
}

/* 2026-09-24 (Carlos, polish pass): "the 10th row of square real estate
   must be used for the link CTA" -- matches Figma's own reference
   (node 6081:42278), which leaves its 10th row completely empty for the
   same reason. Cells 90-99 (10th row, 0-indexed) are the 91st-100th
   .rd-program-card__grid-cell children; visibility:hidden (not
   display:none) keeps their grid track reserved so the CTA below has a
   full row's worth of space to sit in, without actually painting them. */
.rd-program-card__grid-cell:nth-child(n + 91) {
	visibility: hidden;
}

/* 2026-09-24 (Carlos, correction): "you got the animation wrong... you
   need to animate the 56 squares" -- was 46 (a misreading of "stopping
   on the 6th square on the 5th row" from the original brief). Figma's
   real final-animation-state reference (node 6093:48155) shows 5 full
   rows plus 6 more in the 6th row = 56 cells exactly, matching the "56%
   of applications governed only using the IGA" stat 1:1 (56 of 100
   cells). That same reference also adds a blue border on active cells
   (Figma: border-[#3d6bea]) alongside the white fill -- border-color
   starts transparent so it only transitions in, not adding a step. */
.rd-program-card__grid-cell--wave.rd-program-card__grid-cell--active {
	background: var(--rd-white);
	border-color: var(--rd-royal-blue);
}

/* 2026-09-24 (Carlos, polish pass): "the CTA link must also be aligned
   with the bottom padding edge, 48px from the bottom of the card" --
   moved from a flex sibling below the grid (which is what was pushing
   the card past its padding, see .rd-program-card__grid above) to an
   absolutely-positioned overlay INSIDE the grid itself, sitting in the
   reserved-but-invisible 10th row. Since .rd-program-card__grid already
   fills the card's full padded content box (height:100% of
   .rd-program-card__grid-wrap, itself height:100% of the padded card),
   left:0/bottom:0 here lands exactly on the card's own 48px padding
   edges with no extra math needed. */
.rd-program-card__grid-cta {
	position: absolute;
	/* 2026-09-25 (Carlos): "align the rd-program-card__grid-cta centred
	   to the grid" -- was left:0 (flush with the grid's own left edge,
	   inherited from when this sat left-aligned under the grid). The
	   grid itself is centered within the card (.rd-program-card__grid-
	   wrap's justify-content:center), so centering the CTA the same way
	   keeps it aligned to the grid rather than the card's padding edge. */
	left: 50%;
	transform: translateX(-50%);
	bottom: 0;
	/* left:50% alone leaves the browser only "grid width - 50%" of
	   space to the right before wrapping, since it still shrink-fits
	   within that remaining box before the transform shifts it back --
	   without this the CTA text wraps to 2 lines despite having room. */
	white-space: nowrap;
	display: inline-flex;
	align-items: center;
	gap: 2px;
	font-family: 'Space Grotesk', sans-serif;
	font-weight: 700;
	font-size: 16px;
	line-height: 22px;
	letter-spacing: 0.8px;
	text-transform: uppercase;
	color: var(--rd-royal-blue);
	text-decoration: none;
}

/* 2026-09-30 (Carlos): "this link doesn't have the arrow animation from
   the other buttons." Matches the sitewide a.com-btn/.com-btn-outlined
   hover treatment (style_new.css: transition: transform 0.3s ease; hover
   translateX(10px) on the icon) -- applied to the icon itself rather than
   the whole link, since .rd-program-card__grid-cta already carries its
   own transform (translateX(-50%), for centering under the grid) that a
   hover transform on the link itself would clobber. */
.rd-program-card__grid-cta i {
	font-size: 20px;
	line-height: 1;
	transition: transform 0.3s ease;
}

.rd-program-card__grid-cta:hover {
	text-decoration: underline;
}

.rd-program-card__grid-cta:hover i {
	transform: translateX(10px);
}

/* Scroll-reveal (initProgramNowCardReveal()) fades/rises the whole
   bottom-right card in like every other card -- the grid's OWN wave
   animation (initProgramNowGridAnimation(), home-video-v2.js) only
   starts once that card is visible; see the IntersectionObserver gate
   there. prefers-reduced-motion stops the wave from ever starting (same
   convention as runWhySceneLoop()) but leaves the grid itself visible in
   its idle/faint state. */


/* ── 8a. //04-Your Program With READI (sphere + two-path animation + bento
   cards, was "//04-WHY") ─────────────────────────────────────────────────
   Figma node 5835:25755, rebuilt 2026-09-17 replacing the original dark
   "identity journey" design (Figma node 5823:23979, which Carlos called
   "the most complicated section of the whole page ... the core section
   in the page") per his 2026-09-17 brief:
     1. Keep the header row.
     2. Revert to a plain white background -- no more the light-to-dark
        background transition the old design animated on scroll.
     3. A sphere ("blackout zone") sits in the middle of the scene.
     4. Fixed labels sit in the middle of the sphere's bottom half:
        Joiner Access / Service Desk / Leaver Removal.
     5. The first ("without READI") path animates left to right and
        passes behind the sphere, its 3 problem labels arriving in sync
        with the line.
     6. Then the second ("with READI") path animates the same way, its
        3 outcome labels arriving in sync.
     7. The bento cards below reveal on view with the same animation as
        Your Program Now's cards.
     8. The bottom (wide) card is 100% width.

   Section background is now plain white, always -- only the header, the
   two drawn paths + their labels, and the bento cards animate on scroll.
   Reveal mechanics, all one-shot IntersectionObserver triggers
   (home-video-v2.js initWhySectionAnimation()), same progressive-
   enhancement convention used throughout this page: the CSS default
   below is always the fully-revealed end state; JS adds a
   "--pre-reveal" modifier class BEFORE observing an element, only when
   JS runs, IntersectionObserver exists, and prefers-reduced-motion
   isn't set, then removes it once, so a no-JS or reduced-motion visitor
   simply sees the finished section with nothing ever hidden.
   `transition` itself always lives on the un-modified base selector
   (never on the --pre-reveal rule) so removing the modifier class is
   what animates the change.
     - .rd-why__header--pre-reveal -- header fades/rises in.
     - .rd-why__scene, its pills (.rd-why__pill--revealed) and dots
       (.rd-why__path-dot--revealed) -- 2026-09-17 (Carlos, round 2):
       no longer a one-shot reveal-on-scroll. home-video-v2.js's
       runWhySceneLoop() hand-drives stroke-dashoffset on both paths
       through a paused, loopable, reversible timeline instead -- see
       that function's own comment for the full sequence.
     - .rd-why-card--pre-reveal -- bento cards (row + wide), identical
       pattern to .rd-program-card--pre-reveal.
   The 3 category tags and their connector hairlines are Carlos's "fix
   labels" -- always visible, no transition of their own, never part of
   the reveal choreography.

   Both paths are one exported Figma shape (nodes 5894:26025 /
   5894:26019 -- identical `d`, only the stroke gradient + dot
   decoration differ), reproduced verbatim in section-why-v2.php per the
   asset-fidelity rule rather than redrawn by hand. The red ("without
   READI") instance is that same shape flipped vertically
   (`.rd-why__path--wo { transform: scaleY(-1) }`) into an upward hump
   instead of a downward dip -- exactly Figma's own -scale-y-100
   treatment of this asset.

   Each label/dot's transition-delay is computed in section-why-v2.php
   from where it actually sits along the path's real arc length (not
   assumed from x-distance alone, since the path curves) -- see that
   file's docblock for how those fractions were measured. */
.rd-why {
	position: relative;
	width: 100%;
	overflow: hidden;
	padding: 96px 0;
	background: var(--rd-white);
}

.rd-why__header-row {
	display: flex;
	justify-content: center;
	padding: 0 24px;
}

	/* 2026-09-28 (Carlos: "the headers of the page max width are
	   inconsistent, it needs to be 720px"): was 784px, out of step with
	   the heading's own 720px cap inside it (and with the Final CTA
	   section's header block, which had no cap at all -- see
	   .rd-final-cta__text in style_new.css). All 5 header wrappers now
	   share this same 720px cap. */
.rd-why__header {
	max-width: 720px;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 12px;
	text-align: center;
	transition: opacity 700ms ease-out, transform 700ms ease-out;
}

.rd-why__header.rd-why__header--pre-reveal {
	opacity: 0;
	transform: translateY(16px);
}

	/* 2026-09-28 (code review pass, Carlos: "color only, keep 16px"): desktop eyebrow color brought in line with the eyebrow spec
	   (Space Grotesk, #ADB5BD) already applied on mobile -- size stays 16px here, matching how body copy already keeps one
	   color across breakpoints but a larger desktop size (see __subtitle/__body rules' "paragraph 18px" note). */
.rd-why__eyebrow {
	font-family: 'Space Grotesk', sans-serif;
	font-weight: 700;
	font-size: 16px;
	line-height: 22px;
	letter-spacing: 0.8px;
	text-transform: uppercase;
	color: #ADB5BD;
	margin: 0;
}

.rd-why__heading {
	font-family: 'Roboto', sans-serif;
	font-weight: 700;
	/* 2026-09-24 (Carlos): "the header must be H2 (48px.) and 720px. max
	   width" -- standardized to the shared section-header spec. Was 36px/512px. */
	font-size: 48px;
	line-height: 52px;
	color: var(--rd-black);
	max-width: 720px;
	margin: 0;
}

/* ---- The sphere + two-path scene --------------------------------------
   2026-10-01 (Carlos, Figma node 5835:25757): "The Max width must be
   1632px. and the path must start and end at the edge of the
   zone--danger and zone--primary." That node's own "animation" frame
   shows the zones/path/sphere/labels all living inside a 1632px-wide
   content box (x 144 to 1776 of its ~1920px outer frame), not the wider
   1904px canvas this scene used to be locked to (Figma node
   5894:26038, an older/different export) -- was max-width:1904px with
   .rd-why__zone itself inset to 1632px (85.7143%) inside it, which is
   also why .rd-why__path (left:0, width:100% of THIS scene, unchanged
   below) rendered wider than the zone instead of flush with its edges.
   Narrowing the scene itself to 1632px removes that extra margin, so
   .rd-why__zone can become a plain 0%/100% fill (see below) and
   .rd-why__path's existing 0%/100% automatically lands on the zone's
   own edges too -- both asks are the same underlying fix. Height stays
   locked to the same 734px reference (Carlos asked for max-WIDTH only);
   every other horizontal-axis percentage in this scene (.rd-why__zone,
   .rd-why__blackout, .rd-why__scene-label, and $why_col_x_pct in
   section-why-v2.php) is rescaled below to keep its current absolute
   pixel position/size unchanged under the new, narrower 1632px
   reference width -- only .rd-why__zone's own size actually changes. */
.rd-why__scene {
	position: relative;
	width: 100%;
	max-width: 1632px;
	margin: 0 auto;
	aspect-ratio: 1632 / 734;
}

/* 2026-09-24 (Carlos, Figma node 5835:25757): "add a gradient
   backgrounds as shown in the node" -- two soft tint zones framing the
   sphere, sized/positioned as % of the same 1904x734 scene as
   everything else here. Figma's own layer for the lower zone is a
   180deg-rotated copy of the same shape (so it can reuse one gradient
   asset for both halves); reproduced directly here instead as its own
   un-rotated linear-gradient with the color order flipped (strong tint
   at the end nearest the sphere, fading out toward the page edge) --
   same rendered result without the rotate() detour. Both sit at
   z-index:0 (below the sphere/paths/labels) so they read as a
   background wash. */
.rd-why__zone {
	position: absolute;
	/* 2026-09-29 (Carlos: "the background gradients behind the graphic
	   animation are skewed to the right, not aligned with the bento
	   section below"): was left: 8.0357% (153px), copied verbatim from
	   Figma's own layer position -- but that's 17px right of center for
	   an 1632px-wide zone inside the (then) 1904px scene ((1904-1632)/2
	   = 136px, not 153px). Measured live: the zone's own centre sat at
	   x=977 in a 1920px viewport while .rd-why__bento (also 1632px, also
	   margin:0 auto) and the scene box itself both centre on x=960 --
	   a visible rightward shift against the bento panel right below it.
	   2026-10-01 (Carlos, Figma node 5835:25757): "The Max width must be
	   1632px." -- .rd-why__scene itself is now 1632px (was 1904px, see
	   its own comment above), i.e. exactly this zone's own width, so the
	   136px centering inset above is gone too -- the zone simply fills
	   its parent edge-to-edge, same as .rd-why__path already does
	   (left:0/width:100%, unchanged), which is what makes the path land
	   flush on the zone's own edges now. */
	left: 0;
	width: 100%;
	pointer-events: none;
	z-index: 0;
}

/* Upper zone, behind the red "without READI" path -- Figma's own
   rgba(252,197,197,*) is this file's --rd-danger-100 (#FCC5C5) at 0%
   opacity (top, away from the sphere) fading to 25% (bottom, at the
   sphere). Rounded on the bottom edge only (facing the sphere/the gap
   between the two zones), matching Figma's rounded-bl/br.
   2026-09-24 (Carlos, correction): "bring the gradients down so the
   gap between them is perfectly centred aligned with the labels
   Joiner Access, Service Desk and Leaver removal" -- both zones shifted
   down 81px (their heights unchanged), moving the seam between them
   from its old center (434px) to sit exactly on .rd-why__tag's own
   rendered center (~515px, measured via Playwright: tag spans
   67.30%-73.02% of the 734px scene). */
.rd-why__zone--danger {
	/* 2026-10-01 (Carlos, correction): "There is a problem with the
	   gradient backgrounds. You need to bring these two down so the gap
	   between them sits in the centre of the mid labels." -- the
	   earlier same-day "bumped up a bit" edit (16px up, was 57.0px) had
	   knocked the gap off the .rd-why__tag row's (Leaver Removal/Joiner
	   Access/Service Desk) own center, re-opening exactly the
	   misalignment the 2026-09-24 correction below had already fixed.
	   Measured live (Playwright): tag row center sits at 506.97px of
	   the 734px scene; the two zones' gap center was at 490.99px --
	   15.98px (~16px) short. Shifted back down by that amount, landing
	   within 0.03px of the pre-bump 73.0px/9.9455% position -- i.e. the
	   16px "bump" is reverted; the 2026-09-24 centering was correct all
	   along. */
	top: 9.9423%; /* 72.98px / 734px */
	height: 58.3106%; /* 428px / 734px */
	border-radius: 0 0 15px 15px;
	/* 2026-10-01 (Carlos): "Okay, that didn't work. Revert back to the
	   original backgrounds." -- the #FCC5C5-at-85%-then-tuned-to-42.5%
	   experiment (see git history) is reverted; back to the original
	   0/0.25 opacity wash. Position fix above (top: 9.9423%, confirmed
	   "looks great") is untouched -- only this background reverts.
	   2026-10-01 (Carlos, follow-up): "Reduce the gradient opacity a bit
	   more, to 25% opacity" -- this zone's strong stop was already
	   exactly 0.25 (25%), so no change needed here; see
	   .rd-why__zone--primary below, whose 0.5 stop is the one actually
	   brought down to match. */
	background: linear-gradient(180deg, rgba(252, 197, 197, 0) 0%, rgba(252, 197, 197, 0.25) 100%);
}

/* Lower zone, behind the blue "with READI" path -- Figma's
   rgba(213,223,252,*) is the same #D5DFFC/50% tint already used
   elsewhere in this file (see --rd-primary-100). Strong tint at the
   top (nearest the sphere) fading to transparent at the bottom
   (nearest the page edge) -- the flipped equivalent of Figma's
   rotate(180deg) copy, see file-header comment above. Rounded on the
   top edge (facing the sphere), matching Figma's rendered corners
   after that same rotation.
   2026-09-24 (Carlos, correction): shifted down 81px, same reasoning
   as .rd-why__zone--danger above. */
.rd-why__zone--primary {
	/* 2026-10-01 (Carlos, correction): same re-centering fix as
	   .rd-why__zone--danger above -- shifted back down ~16px, undoing
	   the earlier same-day "bumped up a bit" nudge that had pulled the
	   zone gap off the .rd-why__tag row's own center. */
	top: 69.8878%; /* 512.98px / 734px */
	height: 27.5204%; /* 202px / 734px */
	border-radius: 15px 15px 0 0;
	/* 2026-10-01 (Carlos): "Okay, that didn't work. Revert back to the
	   original backgrounds." -- the #ABBFF7-at-100%-then-tuned-to-50%
	   experiment (see git history) is reverted; back to the original
	   #D5DFFC/50%-to-transparent wash. Position fix above (top:
	   69.8878%, confirmed "looks great") is untouched -- only this
	   background reverts.
	   2026-10-01 (Carlos, follow-up): "Reduce the gradient opacity a bit
	   more, to 25% opacity" -- strong stop brought down from 0.5 to
	   0.25, matching .rd-why__zone--danger's own 0.25 max (which needed
	   no change, see above) so both zones now share the same 25% peak
	   opacity. */
	background: linear-gradient(180deg, rgba(213, 223, 252, 0.25) 0%, rgba(213, 223, 252, 0) 100%);
}

/* 2026-09-22 (Carlos): "replace rd-why__blackout with another graphic,
   just 4 concentrical circles with a gradient stroke and no fill" --
   Figma node 5835:25758 -> 6069:41927 ("blackout zone"), 505x505 in the
   1904x734 scene. Now an inline <svg> (markup in section-why-v2.php)
   instead of a CSS radial-gradient div, so this rule is layout-only;
   the 4 circles/gradients/opacities are the SVG's own, copied verbatim
   from the Figma export. Width/height still expressed as % of the same
   aspect-ratio-locked box (not one shared % value) so the circles stay
   circular at any viewport width, same reasoning as before.
   2026-09-24 (Carlos): overflow:visible added so the rings' new 4px
   blur (markup in section-why-v2.php) can bleed past the SVG's own
   viewBox instead of clipping at its edge. */
/* 2026-09-24 (Carlos, correction): "bring the circles down, so the top
   red path passes through the centre of the circles" -- shifted down
   55px (top only, height unchanged) so the sphere's own vertical
   center (was 300.48px) lands on the red "wo" path's actual rendered
   y at its horizontal center (355.4px, measured via
   getPointAtLength()/getScreenCTM() against the live page, same
   technique used to place the scene labels above) instead of sitting
   well above it. */
.rd-why__blackout {
	position: absolute;
	/* 2026-10-01 (Carlos, Figma node 5835:25757): "The Max width must be
	   1632px" -- .rd-why__scene narrowed from 1904px to 1632px (see its
	   own comment above), so this sphere's left/width (horizontal-axis
	   percentages only -- top/height stay as-is, same 734px height
	   reference throughout) are rescaled to keep its absolute pixel
	   position/size unchanged: was 707px of the old 1904px scene, i.e.
	   571px from the zone's own left edge (707 - 136px inset, see
	   .rd-why__zone's old comment); 571/1632 = 34.988%. Width (505px,
	   unchanged) is now 505/1632 = 30.943%. */
	left: 34.988%; /* 571px / 1632px */
	top: 14.0286%; /* (48 + 55)px / 734px */
	width: 30.943%; /* 505px / 1632px */
	height: 68.8010%; /* 505px / 734px */
	overflow: visible;
	pointer-events: none;
	z-index: 2;
}

.rd-why__path {
	position: absolute;
	left: 0;
	width: 100%;
	overflow: visible;
	z-index: 1; /* below the sphere, so it visibly draws "behind" it */
}

.rd-why__path--wo {
	top: 47.33%; /* recentred: this path's own visual middle now sits on the blackout sphere's vertical center */
	height: 14.05%; /* 103.134px / 734px */
	transform: scaleY(-1);
}

.rd-why__path--w {
	top: 76.43%; /* 561px / 734px */
	height: 15.26%; /* 112px / 734px */
}

/* Stage 1 "track" lines (Figma node 5921:29959): a thicker, grey,
   ALWAYS fully-drawn duplicate of each path, rendered before (so behind,
   per SVG paint order) the colored .rd-why__path-line -- never touched
   by the draw-loop's own hidden/revealed state, so it is visible from
   first paint and stays visible after the colored line finishes drawing
   over it, reading as a permanent "track" the colored line travels
   along. */
.rd-why__path-line-track {
	fill: none;
}

/* pathLength="100" on the <path> itself (section-why-v2.php) normalizes
   the dash math below regardless of viewBox units.
   2026-09-17 (Carlos, round 2): the draw is now a hand-rolled, pausable,
   loopable JS animation (home-video-v2.js's runWhySceneLoop()) rather
   than a single CSS `transition` -- it needs to pause mid-draw at each
   column, wait a beat, resume, eventually reverse, and repeat forever,
   none of which a plain `transition` can express. JS sets
   `stroke-dashoffset` directly on this element every frame; this rule
   only supplies the dash pattern plus the safe no-JS/reduced-motion
   fallback (Stage 1's fully-hidden look, same default-is-Stage-1
   reasoning as .rd-why__pill below -- see the reduced-motion/mobile
   overrides that force this back to fully-drawn for those cases). */
/* 2026-09-17 (Carlos, round 2): "a small color dot at the end of each
   animated path[], like if they don't disappear completely" -- a
   zero-length dash with stroke-linecap:round still paints a full circle
   at that single point (both round caps overlap into a dot), which is
   exactly what a fully-hidden dashoffset (100, or -100 after retract)
   produces here. Fixed in the markup (section-why-v2.php) by setting
   these two paths' own stroke-linecap to "butt" instead of "round" --
   the grey track underneath is unaffected and keeps its rounded ends. */
.rd-why__path-line {
	fill: none;
	stroke-dasharray: 100;
	stroke-dashoffset: 100;
}

.rd-why__path-dot {
	fill: var(--rd-royal-blue);
	transform-box: fill-box;
	transform-origin: center;
	opacity: 0;
	transform: scale(0.4);
	transition: opacity 300ms ease-out, transform 300ms ease-out;
}

/* JS adds this to a column's dot the moment the blue line's draw
   reaches it (same trigger as that column's primary pill), and removes
   it again on every reverse/reset -- mirrors .rd-why__pill--revealed's
   default-is-placeholder pattern below. */
.rd-why__path-dot--revealed {
	opacity: 1;
	transform: none;
}

.rd-why__labels {
	position: absolute;
	inset: 0;
	pointer-events: none;
	z-index: 3; /* above the sphere, so the tags/pills over it stay legible */
}

.rd-why__col {
	position: absolute;
	top: 0;
	height: 100%;
	transform: translateX(-50%);
}

.rd-why__pill,
.rd-why__tag {
	position: absolute;
	left: 50%;
	transform: translateX(-50%);
	margin: 0;
	white-space: nowrap;
	border-radius: 5px;
	padding: 6px 12px;
	font-family: 'Roboto', sans-serif;
	/* 2026-09-22 (Carlos, Figma node 5835:25758 re-check): labels bumped
	   to Figma's h3 Heading/Medium spec -- 18px/28px, weight 500 (was
	   12px/18px, weight 600).
	   2026-09-24 (Carlos, "small adjustments" pass): "adjust the fonts to
	   be font-weight: 400" -- 500 -> 400, both the danger/primary pills
	   and the always-visible Joiner Access/Service Desk/Leaver Removal
	   tags below share this one rule. */
	font-weight: 400;
	/* 2026-10-01 (Claude, tablet consistency pass, Carlos: "issues with
	   the responsiveness ... intermediate viewports"): between roughly
	   768px and 1004px viewport width, these 3 always-visible column
	   labels (Leaver Removal/Joiner Access/Service Desk) and the 6
	   revealed path-pill labels sharing this rule visually overlapped --
	   their column anchors (.rd-why__col, 36.15%/50.49%/64.89% of the
	   scene) scale fluidly with the scene's width, but their text stayed
	   a fixed 18px, so the gap between anchors eventually got narrower
	   than two adjacent labels' combined half-widths. Verified live
	   (Playwright) at 1024px viewport the original fixed 18px already
	   clears every column pair with room to spare, so this clamp only
	   needs to shrink text below that width -- it resolves to exactly
	   18px (unchanged) at any viewport >=1024px, matching desktop
	   (>=1440px) exactly, and this whole block is display:none under
	   the 767px mobile breakpoint (see below) regardless of font-size,
	   so neither boundary Carlos asked to preserve is touched. 12px is
	   the measured safe floor at 768px (the narrowest width this block
	   is still visible at) for the tightest pair (Leaver Removal/Joiner
	   Access). */
	font-size: clamp(12px, calc(2.34375vw - 6px), 18px);
	line-height: 28px;
	text-align: center;
	/* 2026-09-22 (Carlos, crispness check): explicit smoothing so this
	   text renders as thin/crisp on Retina WebKit as it does in Figma,
	   instead of falling back to the OS/browser default (verified this
	   was unset -- computed webkitFontSmoothing was "auto"). */
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
}

/* Stage 1 default: every pill renders collapsed to a small "?" badge in
   the grey placeholder colors (Figma's 6 grey badges, ~33x34px) --
   overflow:hidden + a small max-width clips the real text (already
   server-rendered inside, see section-why-v2.php) down to just the
   overlay's solid-background "?" on top of it. home-video-v2.js's
   runWhySceneLoop() adds .rd-why__pill--revealed to a column's pill the
   moment that path's draw reaches it, which is what grows max-width
   open, fades the "?" overlay, and (via the --danger/--primary +
   --revealed rules below) crossfades the colors to Stage 2's final
   danger/primary styling -- all in one transition, matching Carlos's
   "container grows, changes styles ... and text appears". The same
   function removes it again on every reverse/reset, snapping the pill
   back to this Stage 1 default. */
.rd-why__pill {
	border: 1px solid var(--rd-neutral-200);
	background: var(--rd-seasalt);
	color: var(--rd-french-grey);
	overflow: hidden;
	max-width: 34px;
	transition: max-width 500ms cubic-bezier(0.4, 0, 0.2, 1), background-color 400ms ease-out, border-color 400ms ease-out, color 400ms ease-out;
}

.rd-why__pill-overlay {
	position: absolute;
	inset: 0;
	display: flex;
	align-items: center;
	justify-content: center;
	background: inherit;
	color: inherit;
	font-size: 14px;
	line-height: 20px;
	transition: opacity 300ms ease-out;
}

.rd-why__pill--revealed {
	max-width: 240px; /* generous ceiling -- box settles at the real text's own width, never actually reaches 240px */
}

.rd-why__pill--revealed .rd-why__pill-overlay {
	opacity: 0;
	pointer-events: none;
}

.rd-why__pill--danger {
	top: 39.24%; /* 288px / 734px */
}

.rd-why__pill--primary {
	/* 2026-09-30 (Carlos, "pink squares" spacing pass): "Bring ... the
	   labels Removed, Minutes and Automated up to match the distances
	   from the labels on top." His annotated screenshot marked a
	   reference gap ("G") sized to the existing danger pill-to-spinner
	   gap (.rd-why__pill--danger bottom 330.02px to .rd-why__spinner top
	   347.17px = 17.16px of this 734px scene) and showed it applied
	   again, symmetrically, above and below the "with READI" path's own
	   dot (.rd-why__path-dot, measured at its REVEALED size -- r=9,
	   ~17.53px rendered diameter -- since that's the size Carlos's
	   screenshot shows, not the idle/hidden scale(0.4) state). Was
	   95.50% (700.97px, measured via Playwright, matching the old
	   comment's math): new top = dot-bottom (672.98px) + G (17.16px) =
	   690.14px / 734px. */
	top: 94.0246%;
}

.rd-why__pill--danger.rd-why__pill--revealed {
	border-color: var(--rd-danger-300);
	background: var(--rd-danger-050);
	color: var(--rd-danger);
}

.rd-why__pill--primary.rd-why__pill--revealed {
	border-color: var(--rd-primary-300);
	background: var(--rd-primary-050);
	color: var(--rd-royal-blue);
}

/* 2026-09-30 (Carlos, Figma node 5835:25757): the loader spinner shown at
   each of the 3 danger-path columns for 1s right before that column's
   label reveals (home-video-v2.js's runWhySceneLoop() toggles
   --active) -- see section-why-v2.php's own comment for the full spec and
   the placeholder-artwork note. Sits between the danger pill (top 39.24%)
   and the always-visible "Your IGA today" caption (top 51.91%, center) --
   measured against the pills' real rendered edges the same way every
   other element in this scene was (see .rd-why__tag/.rd-why__scene-label's
   own comments), landing just under the pill with only a few px to spare
   before the caption -- Figma's own reference asset (the "Union" icon at
   this same node) sits exactly that tight against its own caption too, so
   this isn't a placeholder-only quirk. Danger-red tone (--rd-danger-300,
   #F98B8B) matches that same Figma asset's fill exactly. */
.rd-why__spinner {
	position: absolute;
	left: 50%;
	top: 47.3%;
	transform: translateX(-50%);
	width: 16px;
	height: 16px;
	opacity: 0;
	pointer-events: none;
	transition: opacity 200ms ease-out;
}

.rd-why__spinner--active {
	opacity: 1;
}

.rd-why__spinner-svg {
	display: block;
	width: 100%;
	height: 100%;
}

.rd-why__spinner-ring {
	fill: none;
	stroke: var(--rd-danger-300);
	stroke-linecap: round;
	transform-origin: 12px 12px;
	animation-play-state: paused;
	animation-timing-function: linear;
	animation-iteration-count: infinite;
}

.rd-why__spinner--active .rd-why__spinner-ring {
	animation-play-state: running;
}

/* r=10 circumference ~62.8; r=6 circumference ~37.7 -- each dasharray
   draws roughly a 3/4 ring (rest transparent) so the spin is visible,
   matching a standard loading-spinner glyph. Carlos: "the interior circle
   will spin clockwise and the outer circle will spin counter clockwise". */
.rd-why__spinner-ring--outer {
	stroke-width: 1.5;
	stroke-dasharray: 47 16;
	animation-name: rd-why-spin-ccw;
	animation-duration: 1.1s;
}

.rd-why__spinner-ring--inner {
	stroke-width: 1.5;
	stroke-dasharray: 28 10;
	animation-name: rd-why-spin-cw;
	animation-duration: 0.85s;
}

@keyframes rd-why-spin-cw {
	from { transform: rotate(0deg); }
	to { transform: rotate(360deg); }
}

@keyframes rd-why-spin-ccw {
	from { transform: rotate(0deg); }
	to { transform: rotate(-360deg); }
}

/* 2026-09-18 -- an ancestor-class transition-duration override
   (.rd-why__scene--resetting) used to live here, meant to slow the
   pills'/dots' reset to match the lines' 1000ms retract. It verified
   correctly under Playwright/WebKit but didn't actually take effect on
   real Safari (confirmed via screen recording), so it's been replaced
   with an inline-style override set directly by resetToStageOne()
   (home-video-v2.js) -- see the comment there. Nothing to keep in sync
   here any more; the JS is now the only place that duration is set. */

/* Fixed content -- Carlos: "some fix labels" -- always visible, never
   animated, no transition of their own.
   2026-09-24 (Carlos, correction): "Remove the rd-why__connectors" --
   the per-column vertical hairline between the danger pill and the
   tag pill below it is gone (markup in section-why-v2.php); rule
   removed entirely rather than left unused. */

.rd-why__tag {
	top: 66.3456%; /* 2026-09-24 (Carlos, correction): "centred the Joiner
	   Access, Service Desk, and Leaver Removal to the gap between the two
	   gradients" -- was 67.30% (494px); re-measured via Playwright against
	   the two zones' new (post -8px) rendered edges (danger bottom
	   500.97px, primary top 512.98px, gap center 506.98px) and shifted so
	   the tag's own rendered vertical center (was 513.97px) lands there:
	   new top = 493.97 + (506.98 - 513.97) = 486.98px / 734px. */
	/* 2026-09-22 (Carlos, Figma node 5835:25758 re-check): these 3 middle
	   labels flipped from a dark pill (--rd-onix/--rd-black/--rd-neutral-300)
	   to Figma's light grey pill -- GRAYS/Anti-Flash White #E9ECEF bg,
	   Brand/Neutral/300 #DEE2E6 border, Brand/Neutral/500 #8C959D text.
	   2026-09-24 (Carlos, correction): "remove the strokes and make
	   these labels background color #FFFFFF" -- border dropped, bg
	   swapped from the grey #E9ECEF to plain white so the pill now
	   floats on the gradient zones (§ above) with no outline.
	   2026-09-24 (Carlos, "small adjustments" pass): "verify the color of
	   Joiner Access / Service Desk / Leaver Removal -- it must be
	   #6c757d" -- was --rd-neutral-500 (#8C959D), close but not an exact
	   match; corrected to --rd-state-grey, the existing token for that
	   exact hex. */
	background: var(--rd-white);
	border: none;
	color: var(--rd-state-grey);
}

/* 2026-09-24 (Carlos, Figma node 5835:25757): "add the labels 'YOUR
   IGA TODAY' and 'YOUR IGA WITH READI'" -- fixed content, same
   always-visible category as .rd-why__tag above (not part of the draw
   animation). Reuses .rd-why__eyebrow's exact
   type recipe (Space Grotesk Bold 16/22, 0.8px tracking, uppercase) --
   the same "section eyebrow" treatment, just recolored per path and
   placed inside the scene instead of the header. */
.rd-why__scene-label {
	position: absolute;
	/* 2026-09-25 (Carlos): "aren't fully centred aligned with the
	   composition" -- was left:50% (the scene box's raw mathematical
	   center), which sits ~8px left of this composition's actual visual
	   center. Every other centered element in this scene (.rd-why__col,
	   which positions .rd-why__tag/.rd-why__pill -- see
	   section-why-v2.php's $why_col_x_pct[1]) already anchors to
	   50.42%, not 50%, matching where the with/without-READI SVG paths
	   and .rd-why__blackout's own rendered center actually sit. These
	   two labels were the only element in the scene still using the raw
	   50% -- switched to the same 50.42% anchor so they line up with
	   Service Desk/the sphere like everything else here.
	   2026-10-01 (Carlos, Figma node 5835:25757): rescaled along with
	   every other horizontal-axis percentage in this scene for the
	   1904px -> 1632px max-width change (see .rd-why__scene's own
	   comment) -- was 960px of the old 1904px scene, i.e. 824px from the
	   zone's own left edge; 824/1632 = 50.4902%, matching
	   $why_col_x_pct[1]'s own rescaled value exactly, same as before. */
	left: 50.4902%;
	/* translateY centers the label ON its `top` value (set per-modifier
	   below as the midpoint of its available gap), not just its top
	   edge -- needed since that gap is tight on both sides. */
	transform: translate(-50%, -50%);
	margin: 0;
	white-space: nowrap;
	font-family: 'Space Grotesk', sans-serif;
	font-weight: 700;
	font-size: 16px;
	line-height: 22px;
	letter-spacing: 0.8px;
	text-transform: uppercase;
	text-align: center;
	pointer-events: none;
}

/* 2026-09-24 (correction): the new node's raw y (298/550) assumes a
   more spacious pill<->curve gap than this scene actually has --
   placed as-is, "Your IGA today" landed squarely on top of the danger
   pill row. First tried centering in the pill<->curve gap instead;
   Carlos's follow-up correction below replaced that with an explicit
   48px offset from each pill row, which is what's implemented now.
   2026-09-24 (Carlos, 2nd correction): "Bring the 'Your IGA Today' tag
   down 48px away from the red labels on top" / "Bring the YOUR IGA +
   READI up, 48px away from the blue labels at the bottom" -- top
   values below are each label's CENTER (the rule above centers on
   `top` via translate(-50%,-50%)), so top = pill edge +/- 48px +/-
   half the 22px line-height, measured from the pills' own rendered
   edges (danger pill bottom 44.96% = 330.02px; primary pill top
   95.50% = 701.17px, both of the 734px scene). */
.rd-why__scene-label--danger {
	/* 2026-09-30 (Carlos, "pink squares" spacing pass): "bring the YOUR
	   IGA TODAY label down to align with the bottom of the pink square
	   so it matches the space from the bottom of the Backlogged label
	   and the top of the spinner." His screenshot showed that same gap
	   (.rd-why__pill--danger bottom 330.02px to .rd-why__spinner top
	   347.17px = 17.16px "G") applied a second time, right after the
	   spinner (363.17px bottom), as the target gap before this label
	   starts -- i.e. this label's top edge should land at spinner-bottom
	   + G. Was 51.9101% (381.02px center, top edge 370.02px -- only
	   6.84px below the spinner, much tighter than G); new top edge =
	   363.17 + 17.16 = 380.33px, center (this rule's `top` via
	   translate(-50%,-50%)) = 380.33 + 11 (half line-height) =
	   391.33px / 734px. */
	top: 53.3145%;
	/* 2026-09-24 (Carlos, "small adjustments" pass): "make 'Your IGA
	   Today' #FAA8A8" -- was --rd-danger (#E51F37, the line/pill color);
	   --rd-danger-200 is the existing token for the exact hex asked for. */
	color: var(--rd-danger-200);
}

/* 2026-09-24 (Carlos, correction): "bring 'YOUR IGA+READI' up around
   24px more" -- was 642.17px (the 48px-from-pill position above);
   nudged up an additional 24px to 618.17px.
   2026-09-24 (Carlos, correction): "bring the two gradients and the
   centred labels up (18px.)" then "too much, bring it 10px. down" --
   net 8px up from the 618.17px position, landing at 610.17px. */
.rd-why__scene-label--primary {
	/* 2026-09-30 (Carlos, "pink squares" spacing pass): "Bring the YOUR
	   IGA+READI label down ... to match the distances from the labels
	   on top" -- same reference gap G (17.16px, see .rd-why__scene-
	   label--danger's own comment) applied symmetrically around the
	   "with READI" path's dot (.rd-why__path-dot, measured at its
	   REVEALED size -- r=9, ~17.53px rendered diameter, matching what
	   the screenshot shows -- top 655.45px / bottom 672.98px of this
	   734px scene): this label's bottom edge should land G above the
	   dot's top. Was 83.1294% (610.17px center, bottom edge 621.17px --
	   34px above the dot, well past G); new bottom edge = 655.45 - 17.16
	   = 638.30px, center = 638.30 - 11 (half line-height) = 627.30px /
	   734px. */
	top: 85.4629%;
	/* 2026-09-24 (Carlos, "small adjustments" pass): "make 'Your
	   IGA+READI' #ABBFF7" -- was --rd-royal-blue (#3D6BEA, the line
	   color); new --rd-primary-200 token added above for this exact hex. */
	color: var(--rd-primary-200);
}

/* ---- Bento cards (Figma node 5835:25805) -------------------------------
   Same structural pattern as Your Program Now's cards (§8) -- reveal
   animation, wide-card modifier -- now recolored to match the light
   theme this section shares with the rest of the page (was dark cards +
   a white "spotlight" wide card in the old design). */

.rd-why__bento {
	margin-top: 96px;
	width: 100%;
	max-width: 1632px;
	margin-left: auto;
	margin-right: auto;
	padding: 0 96px 96px;
	border-radius: var(--rd-radius-card);
	background: linear-gradient(180deg, rgba(255, 255, 255, 0.5) 0%, rgba(213, 223, 252, 0.5) 100%);
}

.rd-why__row {
	display: flex;
	flex-wrap: wrap;
	gap: 16px;
	width: 100%;
}

.rd-why-card {
	background: var(--rd-white);
	/* 2026-09-24 (Carlos): "remove the stroke from boxes on the
	   rd-why_bento" -- was border: 2px solid var(--rd-primary-050).
	   Figma's own export for these cards (e.g. node 5835:25284's "Small-
	   content block") never had a border, just bg-white + rounded-10px.
	   Shared class, so this also removes the same stroke from
	   Orientation's pillar cards (§8d) and the validation stat card kept
	   its own separate border override (§8c) untouched. */
	border-radius: 10px;
	box-shadow: none;
	padding: 48px;
	display: flex;
	flex-direction: column;
	justify-content: space-between;
	gap: 24px;
	flex: 1 1 300px;
	/* 2026-09-21 (Carlos, Figma node 5835:25805): "these cards must be
	   adjusted exactly as the card under rd-now" -- same fixed 464px
	   height as .rd-program-card (§8), was min-height: 345px. Desktop
	   only -- the mobile override at the bottom of this file keeps its
	   own min-height. */
	height: 464px;
	transition: opacity 600ms ease-out, transform 600ms ease-out;
}

.rd-why-card.rd-why-card--pre-reveal {
	opacity: 0;
	transform: translateY(24px);
}

.rd-why-card__top {
	display: flex;
	flex-direction: column;
	gap: 24px;
}

/* 2026-09-21 (Carlos, Figma node 5835:25805): same icon-chip treatment as
   .rd-program-card__icon-wrap (§8), just this section's own blue tint --
   Figma's from-[rgba(171,191,247,.33)] to-[rgba(171,191,247,0)].
   2026-09-22 (Carlos): same full-width-band fix as
   .rd-program-card__icon-wrap (§8) -- see that rule's comment. */
.rd-why-card__icon-wrap {
	display: flex;
	align-items: center;
	justify-content: flex-start;
	width: 100%;
	/* 2026-09-24 (Carlos): "verify that the padding of the
	   rd-why-card__icon-wrap is 12px all around" -- was 24px 24px 24px
	   12px. Figma's own export for this chip (node 5835:25284's icon
	   chip, 6069:41983) is p-[12px] uniform; corrected to match. */
	padding: 12px;
	border-radius: 5px;
	background: linear-gradient(90deg, rgba(171, 191, 247, 0.18) 0%, rgba(171, 191, 247, 0.06) 100%);
}

.rd-why-card__icon {
	display: block;
	width: 48px;
	height: 48px;
	/* The 3-step animation itself is defined once, in §8 above
	   (.rd-program-card__icon / .rd-why-card__icon are both targeted by
	   every rd-icon-* rule there), since Carlos's spec uses "the same
	   mechanism" on both sides of each icon. */
}

.rd-why-card__kicker {
	display: flex;
	flex-direction: column;
	gap: 6px;
}

/* 2026-09-24 (Carlos, "fix the bento box", Figma node 5835:25805): the
   eyebrow used to render as plain small text; Figma's current design
   renders it as a solid pill/badge, exactly Program Now's
   .rd-program-card__pill treatment (Space Grotesk Bold 16/22, 0.8px
   tracking, uppercase) just this section's single royal-blue color
   (Figma has no warning/danger/success variants here -- every row-card
   tag is the same blue). --light is the bottom-left card's own tag
   (Figma: light-blue bg, royal-blue text -- the inverse of the row
   cards' solid-blue tag). */
.rd-why-card__pill {
	display: inline-flex;
	align-items: center;
	align-self: flex-start;
	padding: 6px 12px;
	border-radius: 5px;
	font-family: 'Space Grotesk', sans-serif;
	font-weight: 700;
	font-size: 16px;
	line-height: 22px;
	letter-spacing: 0.8px;
	text-transform: uppercase;
	white-space: nowrap;
	margin: 0;
	background: var(--rd-royal-blue);
	color: var(--rd-primary-050);
}

.rd-why-card__pill--light {
	background: var(--rd-primary-050);
	color: var(--rd-royal-blue);
}

/* 2026-09-24 (Carlos, "fix the bento box", Figma node 5835:25805): this
   used to render the card's big display stat (48px, blue) -- Figma's
   current design uses this slot for the card's real sentence heading
   (36px Bold, dark); the stat moved to a bold lead-in inside the body
   paragraph (.rd-why-card__stat below), exactly Program Now's
   .rd-program-card__heading treatment. max-width dropped (was 512px) for
   the same reason Program Now's was: fill the kicker's full width, no
   arbitrary wrap point. */
.rd-why-card__heading {
	font-family: 'Roboto', sans-serif;
	font-weight: 700;
	font-size: 36px;
	line-height: 42px;
	color: var(--rd-black);
	margin: 0;
}

/* 2026-09-24 (Carlos, "fix the bento box"): body color corrected to
   Figma's Brand/Neutral/600 (#6C757D, --rd-state-grey) -- was --rd-onix
   (#343A40), matches Program Now's own body-copy correction earlier the
   same day. */
.rd-why-card__body {
	font-family: 'Roboto', sans-serif;
	font-weight: 400;
	font-size: 18px;
	line-height: 24px;
	color: var(--rd-state-grey);
	margin: 0;
}

/* Bottom-left card's body has no bold lead-in (plain sentence) -- same
   harmless "explicitly plain" marker as Program Now's own
   .rd-program-card__body--plain. */
.rd-why-card__body--plain {
	color: var(--rd-state-grey);
}

/* The bold stat lead-in moved out of its own big heading (above) into
   here, inline at the start of the body paragraph -- exactly Program
   Now's .rd-program-card__stat (extrabold weight, stays the original
   dark body color rather than lightening with the rest of the
   paragraph). */
.rd-why-card__stat {
	font-weight: 800;
	color: var(--rd-onix);
}

/* 2026-09-24 (Carlos, "fix the bento box"): corrected to Figma's actual
   DESKTOP/Footer type spec (10/14, 0.2px tracking) -- was a borrowed
   12/18 0.24px treatment that never matched this section's own Figma
   export. */
.rd-why-card__citation {
	font-family: 'Roboto', sans-serif;
	font-weight: 400;
	font-size: 10px;
	line-height: 14px;
	letter-spacing: 0.2px;
	color: var(--rd-onix);
	margin: 0;
}

.rd-why-card__citation a {
	color: inherit;
	text-decoration: underline;
}

/* 2026-09-24 (Carlos, "fix the bento box", Figma node 5835:25805): the
   old single full-width wide-stat card (.rd-why-card--wide/__wide-*, a
   two-layer box-fill shell) is replaced by a 2-card row -- left: a
   flexible-width text card reusing the plain .rd-why-card shape (pill +
   heading + body + citation, same rules as the row-3 cards above);
   right: a fixed-width "disconnected apps" grid card. Column formula
   matches Program Now's own bottom row exactly (Task F, same day): the
   right card's width is pinned to the row-3 cards' own per-card width
   via the same calc(), so the two rows stay pixel-identical at any
   viewport width. */
.rd-why__bottom-row {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, calc((100% - 2 * 16px) / 3));
	align-items: stretch;
	gap: 16px;
	width: 100%;
	margin-top: 16px;
}

.rd-why-card--bottom-right {
	align-items: center;
	justify-content: center;
}

.rd-why-card__grid-wrap {
	position: relative;
	width: 100%;
	height: 100%;
	display: flex;
	align-items: center;
	justify-content: center;
}

/* Same height-driven sizing as .rd-program-card__grid (§8): the card's
   height is fixed (464px, inherited from .rd-why-card) while its width
   flexes with the viewport, so sizing the square off height (not width)
   guarantees it never overflows the card's 48px padding. */
.rd-why-card__grid {
	position: relative;
	width: auto;
	height: 100%;
	aspect-ratio: 1 / 1;
	border-radius: 4px;
	background: radial-gradient(ellipse at center, var(--rd-neutral-300) 0%, var(--rd-white) 60%);
}

.rd-why-card__grid-cells {
	display: grid;
	grid-template-columns: repeat(10, 1fr);
	grid-auto-rows: 1fr;
	gap: 3px;
	width: 100%;
	height: 100%;
}

/* 2026-09-24 (Carlos, "fix the animation on the right bottom card"):
   "the initial state is exactly the same as in the rd-now bottom right
   card" -- reverted from the first pass's permanent-blue-border/opacity
   treatment to Program Now's own exact idle style
   (.rd-program-card__grid-cell, §8): white fill, a transparent 1px
   border that only transitions to royal-blue once the wave activates
   the cell. */
.rd-why-card__grid-cell {
	border-radius: 4px;
	background: var(--rd-white);
	border: 1px solid transparent;
	box-sizing: border-box;
	transition: border-color 350ms ease-in-out;
}

.rd-why-card__grid-cell--wave.rd-why-card__grid-cell--active {
	border-color: var(--rd-royal-blue);
}

/* 2026-09-24 (Carlos, "fix the animation on the right bottom card";
   reworked again same day): the 4 named connector cells (Governed
   Through / Its API / Smart Connector / Its Data) used to be permanent,
   always-visible anchors, excluded from the wave entirely.
   Carlos's latest brief: "the 100 squares should animate first and then
   the first square of every word enlarge to the right creating the
   space or field for the phrase to appear" -- two sequential phases,
   not one flat wave. section-why-v2.php now renders 100 plain
   .rd-why-card__grid-cell--wave squares (auto-placed, filling the grid
   with no gaps) FIRST, then these 4 .rd-why-card__grid-cell--label
   overlays AFTER, each explicitly placed via inline grid-row/
   grid-column at its word's own starting square (Figma node
   6093:49373) rather than taking a spot in the auto-flow sequence --
   being later in DOM order, each paints on top of the plain square(s)
   it overlaps once grown. initWhyBentoGridAnimation()
   (home-video-v2.js) runs the 100 squares' wave to completion first,
   THEN grows these 4 open in top-to-bottom order, so "squares first,
   then the words" falls out of the JS's own two-phase sequence -- this
   CSS only needs to describe what --active looks like on each cell
   type, not when it happens.
   The outer .rd-why-card__grid-cell--label element renders no box of
   its own (transparent/no border) -- it's just a positioning container
   sized to its word's full N-column footprint from the start (so the
   grid track layout never shifts once the word grows). The actual
   "square that grows into a pill" is the inner __fill: absolutely
   positioned at the cell's own left edge, idle width one square's share
   of the merged span (100% / --rd-cell-span, the same inline custom
   property) -- i.e. "the first square of every word", indistinguishable
   from its 99 neighbors during phase 1 since the plain square
   underneath (not this collapsed, transparent-background overlay) is
   what's actually visible then -- growing to 100% (the full span) on
   activation. `overflow: hidden` on __fill clips its __text child, so
   the word is progressively uncovered as the box grows open left-to-
   right. __text's own opacity fade (slightly delayed) keeps early
   growth frames from showing a sliver of clipped lettering before the
   box is wide enough to read cleanly. */
/* 2026-09-24 (self-correction): was placed as a real CSS Grid item
   (`grid-row`/`grid-column`, set inline from PHP) sized to its word's
   full span -- but an explicitly-placed grid item makes the grid's
   auto-placement algorithm skip those cells entirely when it flows the
   100 plain squares, regardless of DOM order, so the plain squares
   Carlos's "100 squares first" phase needs never actually existed under
   each word. Taken out of grid-item placement entirely: this is now a
   plain absolutely-positioned overlay INSIDE .rd-why-card__grid
   (a sibling of .rd-why-card__grid-cells, not a child of it -- see
   section-why-v2.php), so it never claims a cell from the grid at all.
   left/top/width/height replicate the grid's own geometry by hand
   (10 columns/rows, 3px gaps, --rd-word-row/--rd-word-col 0-indexed,
   --rd-cell-span, all set inline from PHP): each cell's share of the
   row/column is (100% - 9 gaps) / 10, a position N cells in sits past N
   of those shares plus N gaps, and this box's own WIDTH spans its
   word's full --rd-cell-span cells (N shares + (N-1) internal gaps,
   merged into one box) -- height stays a single row, since no word
   wraps to a second line. This full-span footprint (not just a single
   cell) is what __fill's own idle/grown width below (100% / --rd-cell-
   span, growing to 100%) is a fraction OF, same as before. */
.rd-why-card__grid-cell--label {
	position: absolute;
	left: calc( var( --rd-word-col ) * ( ( 100% - 27px ) / 10 ) + var( --rd-word-col ) * 3px );
	top: calc( var( --rd-word-row ) * ( ( 100% - 27px ) / 10 ) + var( --rd-word-row ) * 3px );
	width: calc( var( --rd-cell-span ) * ( ( 100% - 27px ) / 10 ) + ( var( --rd-cell-span ) - 1 ) * 3px );
	height: calc( ( 100% - 27px ) / 10 );
	background: transparent;
	border: none;
	padding: 0;
	z-index: 1;
}

.rd-why-card__grid-cell__fill {
	position: absolute;
	top: 0;
	left: 0;
	height: 100%;
	width: calc( 100% / var( --rd-cell-span, 1 ) );
	overflow: hidden;
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--rd-white);
	border: 1px solid transparent;
	border-radius: 4px;
	box-sizing: border-box;
	transition: width 350ms ease-in-out, border-color 350ms ease-in-out;
}

/* 2026-09-24 (Carlos, from a screenshot): "those empty cell must also
   be filled" -- at the end of phase 1, the plain square underneath each
   word (its data-square-index twin, home-video-v2.js) is already
   --active/blue-bordered, but __fill sits on top of that exact square
   at its idle collapsed size and, until now, only ever took a border
   color from the WORD's own --active (phase 2's grow). So each word's
   first square visually looked un-lit against its now-fully-lit
   neighbors for the whole gap between "squares done" and "this word's
   own turn to grow". --square-lit is toggled by JS in exact lockstep
   with that specific twin square (not a blanket "phase 1 is over" flag
   -- a word early in the sweep, e.g. row 1, would otherwise still read
   as unlit for ~1.6s after its own square activated, waiting on the
   full 100-cell sweep to finish), so __fill's border now matches its
   twin square the whole time it's just sitting there collapsed --
   phase 2's --active (below) still owns the width/text reveal. */
.rd-why-card__grid-cell--label.rd-why-card__grid-cell--square-lit .rd-why-card__grid-cell__fill {
	border-color: var(--rd-royal-blue);
}

.rd-why-card__grid-cell--label.rd-why-card__grid-cell--active .rd-why-card__grid-cell__fill {
	width: 100%;
	border-color: var(--rd-royal-blue);
}

/* 2026-09-24 (Carlos, follow-up): "the words most be the same styles of
   the eyebrows and they most be centred in their container" -- was a
   small 9px/0.4px left-anchored label; now the exact type spec as
   .rd-why-card__pill/.rd-program-card__pill (Space Grotesk Bold 16/22,
   0.8px tracking, uppercase), centered by __fill's flex layout above
   rather than pinned to the left edge. Padding tightened to 8px
   (Figma's own reference chip padding, node 6093:49373) rather than the
   pill's 12px, since the 2-square "Its API" cell has much less width to
   give text than a free-standing eyebrow tag does. */
.rd-why-card__grid-cell__text {
	padding: 0 8px;
	font-family: 'Space Grotesk', sans-serif;
	font-weight: 700;
	font-size: 16px;
	line-height: 22px;
	letter-spacing: 0.8px;
	text-transform: uppercase;
	color: var(--rd-royal-blue);
	white-space: nowrap;
	opacity: 0;
	transition: opacity 200ms ease-in-out 150ms;
}

.rd-why-card__grid-cell--label.rd-why-card__grid-cell--active .rd-why-card__grid-cell__text {
	opacity: 1;
}

/* ── 8b. //05-Questions (was "//05-CTA (mid-page CTA)") ────────────────────
   2026-09-30 (Carlos, Figma node 5835:25268): "Right now it should be
   called rd-questions" -- renamed from .rd-mid-cta throughout (CSS classes,
   template file, section markup) to match. The underlying ACF field NAMES
   (mid_cta_eyebrow/heading/body/button_primary/button_secondary) are left
   unchanged on purpose -- there's no WP-CLI/DB access in this environment
   to migrate existing postmeta, so renaming the field names would silently
   blank out Carlos's already-entered eyebrow/heading/body/CTA copy. Flagged
   to him; only the group's admin-facing title changed (functions.php).
   Structure per the Figma node: the existing eyebrow/heading/body block
   still sits at top (still gated so nothing prints if unfilled -- see
   template-parts/section-questions-v2.php), a new 1196px-wide light-grey
   box (.rd-questions__list, §8b below) holds the 3 question links, and the
   two buttons (.com-btn / .com-btn-outlined, style_new.css) still sit at
   the bottom. */
.rd-questions {
	position: relative;
	width: 100%;
	padding: 96px 0;
	background: var(--rd-white);
}

.rd-questions__inner {
	max-width: 1440px;
	margin: 0 auto;
	padding: 0 24px;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 48px;
	box-sizing: border-box;
}

	/* 2026-09-28 (Carlos: "the headers of the page max width are
	   inconsistent, it needs to be 720px"): was 784px, out of step with
	   the heading's own 720px cap inside it (and with the Final CTA
	   section's header block, which had no cap at all -- see
	   .rd-final-cta__text in style_new.css). All 5 header wrappers now
	   share this same 720px cap. */
.rd-questions__text {
	max-width: 720px;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 12px;
	text-align: center;
}

	/* 2026-09-28 (code review pass, Carlos: "color only, keep 16px"): desktop eyebrow color brought in line with the eyebrow spec
	   (Space Grotesk, #ADB5BD) already applied on mobile -- size stays 16px here, matching how body copy already keeps one
	   color across breakpoints but a larger desktop size (see __subtitle/__body rules' "paragraph 18px" note). */
.rd-questions__eyebrow {
	font-family: 'Space Grotesk', sans-serif;
	font-weight: 700;
	font-size: 16px;
	line-height: 22px;
	letter-spacing: 0.8px;
	text-transform: uppercase;
	color: #ADB5BD;
	margin: 0;
}

.rd-questions__heading {
	font-family: 'Roboto', sans-serif;
	font-weight: 700;
	/* 2026-09-24 (Carlos): "the header must be H2 (48px.) and 720px. max
	   width" -- standardized to the shared section-header spec. Was 36px/512px. */
	font-size: 48px;
	line-height: 52px;
	color: var(--rd-black);
	max-width: 720px;
	margin: 0;
}

/* 2026-09-25 (Carlos): "add a body ACF field to the rd-questions section,
   same style as in the orientation section" -- copied verbatim from
   .rd-orientation__body (section-orientation-v2.php's own body paragraph),
   including its #6C757D "paragraph" color convention for light-background
   body copy. */
.rd-questions__body {
	font-family: 'Roboto', sans-serif;
	font-weight: 400;
	font-size: 18px;
	line-height: 24px;
	color: #6C757D;
	margin: 0;
}

/* 2026-10-01 (Carlos, Figma node 5835:25268 -- redesign): "The rd-questions
   changed ... these still be links but they have no hover effect if the
   links ACF fields aren't filled." Figma's own node now shows each
   question as its OWN separate light-grey rounded box (bg #E9ECEF /
   --rd-neutral-200, 15px radius, 24px vertical / 120px horizontal padding,
   bold 36/42 text) stacked with a 12px gap, replacing the previous
   2026-09-30 build (one shared --rd-seasalt box containing plain 48px
   regular-weight text, underline-only on hover). .rd-questions__list is
   now just the flex column/gap wrapper; each .rd-questions__link is its
   own card/background. Still a real <a> (template-parts/section-
   questions-v2.php already falls back to href="#" when the ACF url field
   is empty, unconditionally -- the hover below is plain CSS :hover with
   no [href] condition either, so it fires the same whether or not Carlos
   has filled in a real URL yet). Hover adds the background darken to
   --rd-neutral-300 (an existing token, one step darker than the card's
   own --rd-neutral-200) plus the underline this section has used since
   2026-09-30, both of which also survive this redesign.
   2026-10-01 (Carlos, follow-up): "the rd-questions_list must be
   max-width: 956px" -- was 1196px (Carlos's own explicit 2026-09-30 cap,
   now superseded by this narrower figure), matching the "questions"
   wrapper's own 956px frame width in the Figma node. */
.rd-questions__list {
	width: 100%;
	max-width: 956px;
	box-sizing: border-box;
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: 12px;
}

.rd-questions__link {
	display: block;
	width: 100%;
	box-sizing: border-box;
	background: var(--rd-neutral-200);
	border-radius: 15px;
	padding: 24px 120px;
	font-family: 'Roboto', sans-serif;
	font-weight: 700;
	font-size: 36px;
	line-height: 42px;
	text-align: center;
	color: var(--rd-black);
	text-decoration: none;
	text-decoration-skip-ink: none;
	transition: background-color 0.2s ease;
}

.rd-questions__link:hover,
.rd-questions__link:focus-visible {
	background: var(--rd-neutral-300);
	text-decoration: underline;
}

.rd-questions__ctas {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 24px;
	flex-wrap: wrap;
}

/* ── 8c. //06-Validation (quote / stat slider) ──────────────────────────────
   A 2-up flex track inside a viewport that does NOT clip its own overflow
   (Figma node 5823:24113: the outer 1440px frame does not clip its
   2896px-wide track) -- at rest the quote card fills the 1440px column and
   the stat card bleeds past its right edge, off the edge of the browser
   window if the window isn't wide enough. JS (initValidationSlider() in
   home-video-v2.js) measures the viewport's pixel width and slides the
   track by that amount + the 16px gap; nothing needs to change there since
   the un-clipped viewport still reports the same CSS width. CSS default
   (no JS / reduced motion) leaves the track at rest, so the quote slide --
   the first child -- is what a no-JS visitor always sees, fully styled,
   just without the ability to switch to the stat slide. */
/* 2026-09-17 (Carlos, Figma re-check, node 5835:25270): was a 2-slide
   tab-switched carousel (one card visible, ~524px min-height, 192px
   horizontal padding, 48px quote text) -- now a static side-by-side row,
   quote 2/3 + stat 1/3 with a 16px gap, per Carlos's own fractions. Both
   cards' padding/type sizes also came down to match the new, smaller
   Figma frame (96px vertical / 48px horizontal padding; 36px quote text;
   a differentiated 42px/24px stat value+text instead of one shared 48px
   size for both). No more JS -- see home-video-v2.js's removed
   initValidationSlider(). */
.rd-validation {
	position: relative;
	width: 100%;
	max-width: 1632px;
	/* 2026-09-17 (Carlos, round 2): "Add 96px top and bottom margins". */
	margin: 96px auto;
	/* 2026-10-01 (Claude, consistency pass, Carlos: "the quote and ROI
	   cards and the orientation cards don't behave the same ... We need
	   to make this consistent"): this section had no horizontal gutter at
	   exactly 1440px viewport width, since its old 1440px cap exactly
	   equals that viewport and it carried no padding -- Program Now's own
	   panel only gets its gutter there from its 96px padding (its 1632px
	   cap doesn't bite until the viewport exceeds it). Matching cap +
	   padding here instead of just padding alone, so this section's cap
	   also only engages past 1632px, same as Program Now and Orientation. */
	padding: 0 96px;
	box-sizing: border-box;
}

.rd-validation__row {
	display: flex;
	align-items: stretch;
	gap: 16px;
	width: 100%;
}

.rd-validation__quote-card {
	/* 2/3 of the row (Carlos: "the quote will take 2/3 ... the stat
	   1/3"), fluid rather than a literal 954px so it still works at any
	   viewport up to the 1440px container. */
	flex: 2 1 0%;
	min-width: 0;
	background: var(--rd-outer-space);
	border-radius: var(--rd-radius-card);
	padding: 96px 48px;
	display: flex;
	align-items: center;
	box-sizing: border-box;
}

.rd-validation__quote {
	margin: 0;
	display: flex;
	flex-direction: column;
	gap: 24px;
}

.rd-validation__quote-text {
	font-family: 'Roboto', sans-serif;
	font-weight: 700;
	font-size: 36px;
	line-height: 42px;
	color: var(--rd-seasalt);
	margin: 0;
}

.rd-validation__quote-author {
	margin: 0;
	display: flex;
	flex-direction: column;
}

.rd-validation__quote-author-name {
	font-family: 'Roboto', sans-serif;
	font-weight: 700;
	font-size: 18px;
	line-height: 24px;
	letter-spacing: 0.36px;
	color: var(--rd-seasalt);
	margin: 0;
}

.rd-validation__quote-author-title {
	font-family: 'Roboto', sans-serif;
	font-weight: 400;
	font-size: 18px;
	line-height: 24px;
	color: var(--rd-seasalt);
	margin: 0;
}

/* 2026-09-24 (Carlos, Figma node 5835:25276): "exactly the same as the
   rd-why-card rd-why-card-bottom-left ... the same ACF fields ... the
   only difference is that padding-top and padding-bottom is now 96px" --
   markup (section-validation-v2.php) now reuses .rd-why-card's own
   component classes (pill/heading/body/citation, §8b above) instead of
   this rule's old __stat-top/__stat-value/__stat-text/__stat-citation
   children (removed below -- their 48px-blue-stat/24px-blue-text/12px
   type specs had drifted from Figma's actual export over time: node
   5835:25276 is 36px dark heading / 18px grey body / 10px citation,
   exactly .rd-why-card__heading/__body/__citation's own specs). Kept as
   its own class purely for what's genuinely different from a plain
   .rd-why-card here: this row's own sizing (flex 1/3 of the row, no
   fixed 464px height -- .rd-why-card--bottom-left's height only makes
   sense inside the 3-card grid row it shares with taller cards), this
   card's own border (1px --rd-primary-300, Figma's border-[#7d9cf2], vs.
   rd-why-card's 2px --rd-primary-050), and the deeper 96px vertical
   padding node 5835:25276 actually uses (py-[96px], vs. rd-why-card's
   uniform 48px) -- desktop only; mobile already gets the exact same
   48px/345px rd-why-card treatment from the .rd-why-card mobile rule
   itself (Carlos, 2026-09-22: "the boxes under validation must be
   exactly the same as ... Now and Why READI") once this card carries
   that class, so no separate mobile override is needed here any more
   either (the old .rd-validation__stat-card entry in that mobile rule,
   and the __stat-value/__stat-text mobile overrides, are both removed
   -- see that rule below). background/border-radius(10px)/flex-column/
   gap/justify-between all come from .rd-why-card unchanged. */
.rd-validation__stat-card {
	flex: 1 1 0%;
	min-width: 0;
	height: auto;
	padding: 96px 48px;
	border: 1px solid var(--rd-primary-300);
	box-sizing: border-box;
}

/* ── 8d. //07-Orientation ───────────────────────────────────────────────── */
.rd-orientation {
	position: relative;
	width: 100%;
	padding: 96px 0 0;
	background: var(--rd-white);
}

.rd-orientation__header-row {
	display: flex;
	justify-content: center;
	padding: 0 24px;
}

	/* 2026-09-28 (Carlos: "the headers of the page max width are
	   inconsistent, it needs to be 720px"): was 784px, out of step with
	   the heading's own 720px cap inside it (and with the Final CTA
	   section's header block, which had no cap at all -- see
	   .rd-final-cta__text in style_new.css). All 5 header wrappers now
	   share this same 720px cap. */
.rd-orientation__header {
	max-width: 720px;
	display: flex;
	flex-direction: column;
	align-items: center;
	/* 2026-09-24 (Carlos): "the gap in the header is inconsistent with the
	   rest of the page, it must be 12px" -- was 24px, the only section
	   header on this template still using it; .rd-program-now__header and
	   .rd-why__header both already use 12px. */
	gap: 12px;
	text-align: center;
}

	/* 2026-09-28 (code review pass, Carlos: "color only, keep 16px"): desktop eyebrow color brought in line with the eyebrow spec
	   (Space Grotesk, #ADB5BD) already applied on mobile -- size stays 16px here, matching how body copy already keeps one
	   color across breakpoints but a larger desktop size (see __subtitle/__body rules' "paragraph 18px" note). */
.rd-orientation__eyebrow {
	font-family: 'Space Grotesk', sans-serif;
	font-weight: 700;
	font-size: 16px;
	line-height: 22px;
	letter-spacing: 0.8px;
	text-transform: uppercase;
	color: #ADB5BD;
	margin: 0;
}

.rd-orientation__heading {
	font-family: 'Roboto', sans-serif;
	font-weight: 700;
	/* 2026-09-24 (Carlos): "the header must be H2 (48px.) and 720px. max
	   width" -- standardized to the shared section-header spec. Was 36px/512px. */
	font-size: 48px;
	line-height: 52px;
	color: var(--rd-black);
	max-width: 720px;
	margin: 0;
}

.rd-orientation__body {
	font-family: 'Roboto', sans-serif;
	font-weight: 400;
	font-size: 18px;
	line-height: 24px;
	/* 2026-09-24 (Carlos): "All subtitles (body) must be paragraph (18px.)
	   #6C757D" -- was --rd-outer-space (#495057). Only applies to
	   light-background body copy like this one; rd-hero-subtitle and
	   rd-transition-statement__subtitle stay white -- they sit on the
	   dark hero video background. */
	color: #6C757D;
	margin: 0;
}

/* Bento panel: gradient background, rounded only at the bottom (Figma) --
   the two rows inside stay capped at 1440px and centered, same as the
   panel's own 1632px cap centers under the full-bleed-by-default
   #main-content (see §1's note). */
.rd-orientation__bento {
	margin-top: 48px;
	margin-bottom: 96px;
	width: 100%;
	max-width: 1632px;
	margin-left: auto;
	margin-right: auto;
	/* 2026-10-01 (Claude, consistency pass): horizontal padding added to
	   match Program Now's panel gutter (§8) -- this box already shares
	   Program Now's 1632px cap but had no padding, so at 1440px viewport
	   width (narrower than the cap) it went flush to the viewport edges
	   while Program Now did not. */
	padding: 0 96px 96px;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 16px;
	background: linear-gradient(to bottom, rgba(255, 255, 255, 0.5), rgba(213, 223, 252, 0.5));
	border-radius: 0 0 var(--rd-radius-card) var(--rd-radius-card);
	box-sizing: border-box;
}

.rd-orientation__row {
	width: 100%;
	max-width: 1440px;
	display: flex;
	flex-wrap: wrap;
	gap: 16px;
	justify-content: center;
	/* 2026-09-18 (Carlos): "remove the padding from the rd-orientation
	   bento rows" -- was 0 24px. */
	padding: 0;
	box-sizing: border-box;
}

/* 2026-09-24 (Carlos, Figma node 5835:25284): "the cards on top must be
   consistent with the cards on the previous versions" -- markup
   (section-orientation-v2.php) now reuses .rd-why-card's own component
   classes (icon-wrap/top/kicker/pill/heading/body, §8b/§9 above) instead
   of this section's old parallel __icon-wrap/__icon/__text/__heading/
   __body rules (all removed below), which had no pill at all and had
   drifted on both the card shell (15px radius + --rd-primary-100 border
   + min-height:345-even-on-desktop, vs. rd-why-card's 10px radius +
   --rd-primary-050 border + fixed 464px height) and the type inside it
   (48px blue heading + onix body, vs. rd-why-card's 36px dark heading +
   grey body -- matches Figma's actual DESKTOP/H3 + Neutral/600 spec for
   this node). .rd-orientation-card itself now carries no CSS of its own
   -- kept in the markup purely as a marker/debugging hook for "this is
   an orientation pillar card", every visual property comes from
   .rd-why-card, desktop and mobile alike (its mobile rule already gives
   the 48px padding + 345px min-height Carlos separately asked these
   cards to match on mobile, so no orientation-specific mobile override
   is needed either -- see that block below). */

.rd-orientation-partner {
	background: var(--rd-white);
	/* 2026-09-24 (Carlos): "remove the stroke from boxes on the
	   rd-orientation_bento" -- was border: 2px solid var(--rd-primary-100). */
	border-radius: var(--rd-radius-card);
	/* 2026-10-01 (Carlos, Figma node 5835:25284 -- redesign): "I changed
	   the look and feel of the second row of cards ... I've added a
	   gradient background to the logos" -- Figma's own card padding is
	   now a flat 48px all around (was the asymmetric pt-120/px-48/pb-96
	   below, which existed only to vertically center the flat logo with
	   no box of its own; the new .rd-orientation-partner__logo-wrap
	   below is itself flex:1 and grows to fill the remaining space, so a
	   plain symmetric padding centers everything correctly without the
	   old asymmetry). */
	padding: 48px;
	display: flex;
	flex-direction: column;
	align-items: center;
	/* 2026-09-24 (Carlos): "make the gap between the partner logos and
	   the text below 24px" -- was justify-content: space-between, which
	   ignored the gap value below and stretched the logo/caption apart
	   to fill the fixed 464px height (a ~190px visual gap). Centering the
	   logo+caption as one group lets `gap` set the actual visual spacing. */
	justify-content: center;
	gap: 24px;
	flex: 1 1 300px;
	/* 2026-09-24 (Carlos): "make the rd-orientation-partner cards same
	   size of the card on top: 464px" -- was min-height: 345px (desktop),
	   now a fixed height matching .rd-why-card/.rd-program-card's own
	   464px, same as the pillar row right above it. Desktop only -- the
	   mobile override below keeps its own height:auto/min-height. */
	height: 464px;
	box-sizing: border-box;
}

/* 2026-10-01 (Carlos, Figma node 5835:25284): the new gradient wash behind
   each partner logo -- Figma's own diagonal
   linear-gradient(132.85deg, rgba(171,191,247,0.18) 0%, rgba(254,232,232,0.36) 99.952%)
   (a blue-to-pink wash, matching this page's --rd-primary-ish/--rd-danger-
   ish tint family already used elsewhere, e.g. the rd-why scene's own two
   zone gradients), 12px padding, 5px corner radius, full card width.
   flex:1/min-height:0 so it grows to fill the card's remaining height
   above the caption (see .rd-orientation-partner's own comment on why its
   padding went symmetric for this). */
.rd-orientation-partner__logo-wrap {
	flex: 1 1 0;
	min-height: 0;
	width: 100%;
	box-sizing: border-box;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 12px;
	border-radius: 5px;
	background: linear-gradient(132.85deg, rgba(171, 191, 247, 0.18) 0%, rgba(254, 232, 232, 0.36) 99.952%);
}

.rd-orientation-partner__logo {
	max-height: 40px;
	width: auto;
}

.rd-orientation-partner__caption {
	font-family: 'Roboto', sans-serif;
	font-weight: 400;
	font-size: 12px;
	line-height: 18px;
	letter-spacing: 0.24px;
	text-align: center;
	color: var(--rd-onix);
	margin: 0;
}

/* ── 8e. //08-CTA (closing, dark) ──────────────────────────────────────────
   2026-09-17 (Carlos): moved to a shared global component -- CSS now
   lives in style_new.css → === Final CTA Component — Shared === (no
   longer page-scoped to .rd-home-v2). See template-parts/final-cta.php. */

/* Shared screen-reader-only utility -- used by the validation slider's tab
   buttons, which are otherwise unlabeled pills. Scoped under .rd-home-v2
   like everything else in this file rather than added globally. */
.rd-home-v2 .sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* ── 9. Reduced-motion end state ────────────────────────────────────────────
   Same decision as the V1 build (Carlos, 2026-09-01 -- see
   home-video-notes.md #13): "skip the transition entirely" -- reduced-
   motion users get a normal static full-bleed hero (the <video poster>
   attribute is the only thing they ever see, since hero-video-v2.php's
   inline script never sets a real `src` for this media query) with the
   full H1/subtitle/citation/CTA/scroll-label copy always visible and no
   scrub distance. JS never starts its rAF loop when this media query
   matches, so .rd-hero-content / .logo-wall-section simply keep their CSS
   default (fully visible, untransformed) values -- only the video frame
   sizing needs an explicit override here, plus hiding the Transition
   Statement + its fade-to-card layer outright (2026-09-14 redesign -- see
   hero-video-v2.php file header for why: it's a scroll-transition effect
   with no equivalent "settled" moment to reveal it at for a visitor who
   never sees the transition run). The marquee's own animation already
   stops correctly with no page-specific override needed: its reduced-
   motion handling is inherited from the shared component's own global
   rule in style_new.css. */
@media (prefers-reduced-motion: reduce) {
	.rd-hero-wrap {
		height: 100dvh;
	}

	.rd-hero-video-frame {
		width: 100%;
		height: 100%;
		aspect-ratio: auto;
		border-radius: 0;
		transform: none;
	}

	.rd-video-fade-to-card,
	.rd-transition-statement {
		display: none;
	}

	/* Defense-in-depth alongside home-video-v2.js's own prefersReducedMotion
	   guard in initProgramNowCardReveal() (which won't add this class in
	   the first place): if the OS-level setting changes after that check
	   already ran, this still guarantees the cards render at their normal,
	   fully-visible resting state rather than getting stuck invisible. */
	.rd-program-card.rd-program-card--pre-reveal {
		opacity: 1;
		transform: none;
	}

	/* //04-Your Program With READI -- force every --pre-* modifier
	   straight to its revealed end state, same defense-in-depth
	   reasoning as Your Program Now's own block just above (in case the
	   OS-level setting flips after home-video-v2.js's own
	   prefersReducedMotion guard already ran and decided not to add any
	   of these classes in the first place). */
	.rd-why__header.rd-why__header--pre-reveal {
		opacity: 1;
		transform: none;
	}

	.rd-why__path-line {
		stroke-dashoffset: 0;
	}

	.rd-why__path-dot {
		opacity: 1;
		transform: none;
	}

	/* Pills no longer use opacity for their reveal (see .rd-why__pill /
	   .rd-why__pill--revealed above) -- for reduced motion, skip Stage 1's
	   grey "?" placeholder (and the whole draw-loop, per
	   runWhySceneLoop()'s own prefersReducedMotion guard) entirely and
	   force every pill straight to its Stage 2 end state, same
	   defense-in-depth reasoning as the other rules in this block. */
	.rd-why__pill {
		max-width: 240px;
	}

	.rd-why__pill-overlay {
		opacity: 0;
		pointer-events: none;
	}

	.rd-why__pill--danger {
		border-color: var(--rd-danger-300);
		background: var(--rd-danger-050);
		color: var(--rd-danger);
	}

	.rd-why__pill--primary {
		border-color: var(--rd-primary-300);
		background: var(--rd-primary-050);
		color: var(--rd-royal-blue);
	}

	.rd-why-card.rd-why-card--pre-reveal {
		opacity: 1;
		transform: none;
	}

}

/* ── 10. Responsive ──────────────────────────────────────────────────────── */

/* ≤1024px — tighten a touch, still desktop-shaped */
@media (max-width: 1024px) {
	.rd-hero-headline {
		font-size: 40px;
		line-height: 46px;
	}

	.rd-transition-statement {
		padding: 48px 64px;
	}

	.rd-transition-statement__heading {
		font-size: 40px;
		line-height: 46px;
	}

	/* Narrower horizontal padding at tablet width -- mirrors the V1 Gap
	   section's own tightening of this same box style. */
	.rd-program-now__panel {
		padding: 96px 32px;
	}

	/* 2026-10-01 (Claude, consistency pass): same tablet tightening as
	   Program Now's panel just above, applied to the other two sections
	   now sharing its gutter pattern. */
	.rd-validation {
		padding: 0 32px;
	}

	.rd-orientation__bento {
		padding: 0 32px 96px;
	}

	/* 2026-09-21 (Carlos, icons/height pass): the fixed height: 464px (§8)
	   is a desktop-width value (matches Figma's own frame at that width) --
	   no tablet Figma frame exists to confirm it still fits at this
	   narrower width with the row still wrapping only past it, so fall
	   back to the pre-existing min-height: 345px floor here instead of
	   forcing 464px, same "no mobile frame yet" convention used elsewhere
	   on this page. */
	.rd-program-card {
		height: auto;
		min-height: 345px;
	}

	/* //04-Your Program With READI -- tighten bento gutters/card padding,
	   same proportional tightening as Your Program Now's own §10 rules
	   just above. */
	.rd-why__bento {
		padding: 0 32px 64px;
	}

	.rd-why-card {
		height: auto;
		min-height: 345px;
		padding: 32px;
	}

	/* 2026-09-24 (Carlos, "fix the bento box"): .rd-why-card__heading is
	   already 36/42 at desktop size now (was a 48px stat that this rule
	   shrank to 36px at this breakpoint) -- nothing left to override here.
	   The old wide-card rules (.rd-why-card__wide-content/-stat) are gone
	   too, replaced by the 2-card bottom row -- .rd-why-card's own
	   min-height:345px/32px-padding rule above already covers the new
	   bottom-left card (it's a plain .rd-why-card); the bottom-right grid
	   card needs no extra override here. */
}

/* ≤899px — mobile transition behaviour (brief's "Below 900px" threshold):
   shorten scrub distance. No tile-density/simplification rules left here
   any more (the old app tiles this handled are gone) -- JS's own
   isMobileViewport() mobile-perf branching (§9) is unaffected. */
@media (max-width: 899px) {
	.rd-hero-wrap {
		height: 150vh;
	}
}

/* ≤767px */
@media (max-width: 767px) {
	/* //01-Hero + //02-Transition Statement -- 2026-09-21 (Carlos, mobile
	   pass, Figma node 6051:38549 "READI homepage-2026_v6.3(Mobile)"):
	   "no animation transition between the hero and the transition
	   statement. Just plain scroll." The Figma mobile frame has no video/
	   tile scroll-scrub at all -- Hero (top:72px, h:802px) is a plain
	   static dark card, and Transition Statement (top:874px, h:874px,
	   i.e. starting exactly where Hero ends) is its own separate stacked
	   card directly below it. home-video-v2.js's isMobileNoScrub guard
	   (same 767px breakpoint) skips initHeroTransition()'s scroll
	   listener/rAF loop entirely at this width, so nothing ever writes
	   the --rd-video-scale / --rd-copy-* / --rd-statement-* custom
	   properties these rules would otherwise be driven by -- this block
	   replaces the desktop sticky-scrub layout with the plain stacked
	   one instead of just leaving those properties at their unset
	   defaults, matching the JS/CSS pairing pattern the reduced-motion
	   block above already uses. */
	.rd-hero-wrap {
		height: auto;
	}

	/* 2026-09-22 (Carlos, correction pass): "The hero must be always
	   full screen and the video must play in the background. The logo
	   wall must be anchored to the bottom of the browser." Replaces the
	   earlier "flat dark card, video hidden" reading of the mobile
	   Figma frame -- .rd-hero-stage is a plain (non-sticky, no scrub)
	   screen whose real height comes from .rd-hero-media below (100dvh
	   of video + the Transition Statement's own screen after it);
	   .rd-hero-content and the logo wall are pulled out of flow and
	   absolutely pinned against this box instead, so both land on just
	   the FIRST 100dvh of it regardless of how tall their own content
	   renders. */
	.rd-hero-stage {
		position: relative;
		height: auto;
		min-height: 0;
		background: var(--rd-black);
		display: block;
	}

	/* Normally an absolute, full-bleed background layer confined to
	   .rd-hero-stage's own box (home-video-v2.css §4) -- on mobile it
	   stays in normal flow (position:static) instead so its real
	   height, driven entirely by .rd-hero-video-frame below, gives
	   .rd-hero-stage genuine scrollable height spanning both the hero
	   screen and the Transition Statement screen after it, without
	   moving anything in the PHP markup (Carlos's hard constraint: no
	   structural changes unless asked). */
	.rd-hero-media {
		position: static;
		display: block;
		width: 100%;
		z-index: auto;
		pointer-events: none;
	}

	/* No longer the small centred 1440x720 card the desktop shrink-to-
	   card scrub sizes it as -- on mobile this is simply the tall box
	   .rd-hero-media's real height comes from: exactly 100dvh of
	   full-bleed video/scrim/vignette (the hero screen, video actually
	   playing per Carlos's note above), immediately followed by the
	   Transition Statement, pushed down (margin-top) to start exactly
	   where that 100dvh ends so it reads as a plain-scroll second
	   screen instead of overlapping the video. overflow:hidden gives
	   this box its own block-formatting context so that margin-top
	   doesn't collapse up through it and shove the whole hero down. */
	.rd-hero-video-frame {
		position: relative;
		width: 100%;
		height: auto;
		aspect-ratio: auto;
		transform: none;
		border-radius: 0;
		overflow: hidden;
	}

	.rd-hero-video,
	.rd-hero-scrim,
	.rd-hero-vignette,
	.rd-video-fade-to-card {
		position: absolute;
		top: 0;
		left: 0;
		right: 0;
		bottom: auto;
		height: 100dvh;
	}

	/* The Transition Statement's own screen -- 2026-09-22 (Carlos,
	   correction pass): "The transition statement must be in the
	   middle of the viewport." margin-top pushes it past the hero's
	   100dvh (see .rd-hero-video-frame above); min-height:100dvh + flex
	   centering then centres the heading/subtitle within that second
	   screen instead of just stacking them at its top. Always visible,
	   no JS reveal to gate, same as before. */
	.rd-transition-statement {
		position: static;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
		opacity: 1;
		transform: none;
		pointer-events: auto;
		margin-top: 100dvh;
		min-height: 100dvh;
		box-sizing: border-box;
		background: var(--rd-black);
		border-radius: 0;
		padding: 48px;
		gap: 24px;
	}

	/* 2026-09-23 (Carlos, round 4): "review all headers, the __heading
	   font must be always 28px" -- was 30/36. */
	/* 2026-09-24 (Carlos, round 5): "make the __headings a max-width of
	   320px" -- was 368px; now matches the other section headings
	   below, all newly given the same cap this round. */
	.rd-transition-statement__heading {
		font-family: 'Roboto', sans-serif;
		font-weight: 600;
		font-size: 28px;
		line-height: 34px;
		max-width: 320px;
	}

	/* 2026-09-25 (Carlos): body copy spec -- Roboto 14px, #6C757D --
	   applies here too even on this section's dark background;
	   supersedes the earlier "stays white" decision (see the note
	   on the desktop rule above). */
	.rd-transition-statement__subtitle {
		font-family: 'Roboto', sans-serif;
		font-weight: 600;
		font-size: 14px;
		line-height: 20px;
		letter-spacing: 0.28px;
		color: #6C757D;
		max-width: 368px;
	}

	.rd-hero-content {
		position: absolute;
		top: 0;
		left: 0;
		right: 0;
		padding-top: calc(var(--rd-nav-height) + 24px);
	}

	/* Figma H1-Mobile (node 6051:38549) originally called for Space
	   Grotesk MEDIUM here, but 2026-09-30 (Carlos): "make the Hero H1
	   font-weight: 400, just apply to the home page for now" -- same
	   request already applied to the desktop base rule earlier today
	   (see that rule's own comment) now extended to this mobile
	   override too, at Carlos's explicit ask. Font-loading is already
	   covered: the home-page-only Space Grotesk 400 <link> added to
	   header.php earlier today loads sitewide on the home page, mobile
	   included, so no separate font change is needed here. 36/38,
	   letter-spacing 0 unchanged. */
	.rd-hero-headline {
		font-weight: 400;
		font-size: 36px;
		line-height: 38px;
		letter-spacing: 0;
	}

	/* Figma Subtitle-Mobile: Roboto SEMIBOLD (not the desktop Regular
	   this inherits otherwise), 14/20, 0.28px tracking. */
	.rd-hero-subtitle {
		font-weight: 600;
		font-size: 14px;
		line-height: 20px;
		letter-spacing: 0.28px;
	}

	.rd-hero-citation {
		margin-bottom: 32px;
	}

	.rd-hero-ctas {
		flex-direction: column;
		align-items: stretch;
	}

	.rd-hero-ctas .com-btn {
		justify-content: center;
	}

	.rd-hero-scroll-label {
		margin-top: 16px;
	}

	/* Normally absolutely pinned to the stage's bottom edge, independent
	   of scroll (§6 above) -- with .rd-hero-stage no longer
	   position:relative/sticky on mobile (it's position:static now, no
	   scrub to pin against), this would otherwise position itself
	   against whatever ancestor IS still positioned further up the tree
	   instead, landing wherever that happens to place it rather than
	   right after the CTA/scroll-label like Figma's mobile Hero card
	   shows. Back to a normal flex item (like it originally was, per the
	   file's own §6 comment) restores that -- .rd-hero-media's
	   `order: 1` (this same breakpoint, hero section) already pushes the
	   Transition Statement after both .rd-hero-content and this, so
	   putting this back in flow is enough on its own to land it between
	   them, no order override needed here too. */
	/* 2026-09-22 (Carlos, correction pass): "The logo wall must be
	   anchored to the bottom of the browser." Back to an absolute pin
	   -- bottom:100dvh (not 0) anchors its bottom edge to the boundary
	   between the hero's own 100dvh screen and the Transition
	   Statement's screen after it, i.e. the bottom of the hero screen
	   itself, matching the desktop base rule's intent (§6) rather than
	   the page's very end. */
	.rd-home-v2 .logo-wall-section {
		position: absolute;
		top: auto;
		right: 0;
		bottom: 100dvh;
		left: 0;
		opacity: 1;
		padding: 0 24px 32px;
	}

	/* 2026-09-23 (Carlos, round 4): "remove the padding right and left
	   from the class=container" under the logo wall -- the sitewide
	   .container (style_new.css) carries its own 20px side padding,
	   which this scopes away just here rather than touching the shared
	   rule everything else on the site still relies on. */
	.rd-home-v2 .logo-wall-section .container {
		padding-left: 0;
		padding-right: 0;
	}

	.rd-home-v2 .logo-wall-subheading {
		font-size: 13px;
		margin-bottom: 16px;
	}

	/* Shared component spaces logos via padding on each item, not a gap
	   on a list container -- 24px each side (was .rd-hero-marquee__list's
	   gap: 40px, a similar proportional tightening from the desktop
	   default of 48px each side, §6). */
	.rd-home-v2 .logo-marquee-item {
		padding: 0 24px;
	}

	.rd-home-v2 .logo-marquee-img {
		max-height: 24px;
		max-width: 88px;
	}

	/* 2026-09-22 (Carlos, round 3): "rd-program-now padding-top: 48px.
	   and remove the padding bottom" -- the section itself had no
	   padding of its own before this; unrelated to __panel's own
	   internal 64px/20px inset just below. */
	.rd-program-now {
		padding-top: 48px;
		padding-bottom: 0;
	}

	.rd-program-now__panel {
		padding: 64px 20px;
	}

	/* 2026-09-30 (Carlos): "the rd-program-now__header must have left and
	   right padding of 20px". */
	.rd-program-now__header {
		padding-left: 20px;
		padding-right: 20px;
	}

	/* 2026-09-25 (Carlos): mobile eyebrow spec -- Space Grotesk 10px,
	   #ADB5BD. */
	.rd-program-now__eyebrow {
		font-size: 10px;
		line-height: 16px;
		letter-spacing: 0.5px;
		color: #ADB5BD;
	}

	/* 2026-09-24 (Carlos, round 5): "make the __headings a max-width of
	   320px". */
	.rd-program-now__heading {
		font-size: 28px;
		line-height: 34px;
		max-width: 320px;
	}

	.rd-program-now__row {
		flex-direction: column;
	}

	/* 2026-09-22 (Carlos, correction pass): "the stats boxes must be
	   identical in height ... 345px" -- restores the desktop base
	   rule's own min-height (was zeroed here); cascades to
	   .rd-program-card--wide too (same class), matching request #3
	   ("make the 46% box same height as the other boxes on top").
	   2026-09-21 (Carlos, icons/height pass): the desktop base rule (§8)
	   now sets a fixed height: 464px instead of min-height -- height: auto
	   here keeps that from also locking this mobile card to 464px, same
	   as min-height: 345px still does its own (now-restored) job below. */
	.rd-program-card {
		flex-basis: auto;
		height: auto;
		min-height: 345px;
		padding: 48px;
	}

	/* 2026-09-21 (Carlos, icons/height pass): H3 mobile scale (28/34),
	   matching the desktop bump back to H3 (§8) -- same mobile scale
	   already used for .rd-program-now__heading's own H3. Supersedes the
	   2026-09-17 H4 mobile scale (20/26), which matched desktop's H4 at
	   the time. */
	.rd-program-card__heading {
		font-size: 28px;
		line-height: 34px;
	}

	/* 2026-09-24 (Carlos, Figma node 5954:30079): no mobile Figma frame
	   exists yet for the new 2-card bottom row -- same "no separate
	   mobile markup, proportional tightening" convention used elsewhere
	   on this page: stack the two cards instead of sitting them side by
	   side (matches .rd-program-now__row's own mobile stacking above),
	   each taking the full width. Single grid column, not flex-direction
	   (the desktop rule is display:grid -- flex-direction has no effect
	   there). */
	.rd-program-now__bottom-row {
		grid-template-columns: 1fr;
	}

	.rd-program-card--bottom-left,
	.rd-program-card--bottom-right {
		width: 100%;
		height: auto;
		min-height: 345px;
		padding: 48px;
	}

	/* Mobile cards are height:auto (content-driven, not the desktop's
	   fixed 464px), so the desktop grid's height:100%/width:auto sizing
	   (§8) has nothing definite to size off -- flip back to the more
	   ordinary width-driven direction here: a definite width derives the
	   height via the same aspect-ratio:1/1, and there's no fixed-height
	   ceiling to overflow on mobile in the first place. */
	.rd-program-card__grid {
		width: 100%;
		max-width: 280px;
		height: auto;
	}

	/* //04-Your Program With READI -- 2026-09-24 (Carlos): "the SVG
	   doesn't upload properly, but this is the code" -- Carlos supplied
	   the actual exported static diagram (Figma node 6051:38549's mobile
	   frame asset, 402x389 viewBox) as raw SVG markup, closing out the
	   original 2026-09-17 request ("This is not what i meant with the
	   animation. The animation will be static. I've attached the SVG").
	   Supersedes the freeze-the-animated-end-states approach this section
	   used until now (2026-09-21 note, removed): instead of leaving the
	   sphere/path/labels DOM running and forcing every animated class to
	   its revealed state via CSS, the whole animated composition is
	   hidden on mobile and .rd-why__scene shows the static SVG asset
	   (assets/images/why-scene-static-mobile.svg) as a background image
	   instead, sized to the asset's own aspect ratio. home-video-v2.js's
	   initWhySectionAnimation() already skips runWhySceneLoop() below
	   this same 767px breakpoint (isWhySceneMobile guard), so nothing
	   here needs a JS change -- there's simply nothing left to animate. */
	/* 2026-09-22 (Carlos, round 3): "padding top 48px" + "remove ...
	   padding bottom" -- was 64px 0. */
	.rd-why {
		padding: 48px 0 0;
	}

	.rd-why__eyebrow {
		font-family: 'Space Grotesk', sans-serif;
		font-weight: 700;
		font-size: 10px;
		line-height: 16px;
		letter-spacing: 0.5px;
	}

	/* 2026-09-23 (Carlos, round 4): "review all headers, the __heading
	   font must be always 28px" -- was 22/28. */
	/* 2026-09-24 (Carlos, round 5): "make the __headings a max-width of
	   320px". */
	.rd-why__heading {
		font-size: 28px;
		line-height: 34px;
		max-width: 320px;
	}

	.rd-why__zone,
	.rd-why__blackout,
	.rd-why__path,
	.rd-why__labels {
		display: none;
	}

	/* 2026-09-24 (Carlos): "animate this in the same sequence we
	   animated the desktop version" -- the flat SVG can't be staged
	   granularly the way desktop's path-draw/dot-travel/pill-reveal
	   sequence is, so it gets the same one-shot fade+rise
	   reveal-on-scroll used for .rd-why__header and .rd-why-card around
	   it (home-video-v2.js's initWhySectionAnimation(), Stage 2),
	   keeping it part of the same overall entrance sequence.
	   2026-09-25: this asset is served with cache-control: public,
	   max-age=604800 and has no query string, so a browser that has
	   ever loaded it caches it for a week regardless of new deploys.
	   The ?v= below is a manual cache-bust -- bump it (date or
	   incrementing int, doesn't matter which) any time this specific
	   SVG's *content* changes, or visitors with a warm cache won't see
	   the update until it expires on its own. */
	.rd-why__scene {
		margin-top: 32px;
		aspect-ratio: 408 / 365;
		background-image: url('../images/why-scene-static-mobile.svg?v=20260930');
		background-repeat: no-repeat;
		background-position: center top;
		background-size: contain;
		transition: opacity 700ms ease-out, transform 700ms ease-out;
	}

	.rd-why__scene.rd-why__scene--pre-reveal {
		opacity: 0;
		transform: translateY(24px);
	}

	/* 2026-09-23 (Carlos, round 4): padding-bottom 48px -> 24px. */
	.rd-why__bento {
		margin-top: 64px;
		padding: 0 24px 24px;
	}

	.rd-why__row {
		flex-direction: column;
	}

	/* 2026-09-22 (Carlos, correction pass): same 345px restore as
	   .rd-program-card above -- cascades to .rd-why-card--wide too.
	   2026-09-21 (Carlos, icons/height pass): height: auto added, same
	   reasoning as .rd-program-card's own mobile override above. */
	.rd-why-card {
		flex-basis: auto;
		height: auto;
		min-height: 345px;
		padding: 48px;
	}

	.rd-why-card__heading {
		font-size: 32px;
		line-height: 36px;
	}

	/* 2026-09-24 (Carlos, "fix the bento box"): the old wide-card mobile
	   rules are gone along with the wide card itself (2-card bottom row
	   now, same structure as the row-3 cards) -- stacks to one column,
	   same pattern as Program Now's own bottom row just above. */
	.rd-why__bottom-row {
		grid-template-columns: 1fr;
	}

	.rd-why-card--bottom-left,
	.rd-why-card--bottom-right {
		width: 100%;
		height: auto;
		min-height: 345px;
		padding: 48px;
	}

	/* Mobile cards are height:auto (content-driven), so the desktop
	   grid's height:100%/width:auto sizing has nothing definite to size
	   off -- flip to width-driven, same as Program Now's own grid. */
	.rd-why-card__grid {
		width: 100%;
		max-width: 280px;
		height: auto;
	}

	/* 2026-09-24 (Carlos, follow-up: "same style as the eyebrows... must
	   be centred"): that 16px/22px eyebrow-matched type spec (§8b) fits
	   comfortably at desktop's ~34px-per-column grid, but at this
	   breakpoint's 280px-capped grid (~25px per column) it clips --
	   "GOVERNED THROUGH"/"ITS API" ran past their cell edges. Scaled back
	   down to roughly the size these labels used before the eyebrow-style
	   pass (9px, tighter tracking/padding) so every word still reads
	   cleanly at this width; centering (justify-content on __fill, §8b)
	   is unaffected either way. */
	.rd-why-card__grid-cell__text {
		padding: 0 3px;
		font-size: 9px;
		line-height: 1.2;
		letter-spacing: 0.3px;
	}

	/* //05-Questions */
	.rd-questions__inner {
		padding: 0 24px;
		gap: 32px;
	}

	/* 2026-09-30 (Carlos, Figma node 5835:25268): the 1196px box and its
	   48px question links proportionally tightened at mobile width, same
	   convention as every other section's own mobile pass on this page --
	   no separate mobile frame given, so scaled by feel against the other
	   48px headings' own mobile sizes (e.g. .rd-questions__heading below).
	   2026-10-01 (Carlos, Figma node 5835:25268 -- redesign): the
	   padding/border-radius that used to style the single shared box move
	   to .rd-questions__link below (now 3 separate per-question cards);
	   this rule just keeps the stack's own gap, tightened to match the
	   new smaller 12px desktop gap. */
	.rd-questions__list {
		gap: 12px;
	}

	/* 2026-09-30 (Carlos): "decrease the font size of the rd-questions__link
	   to 22px" -- line-height brought down to 28px to match, the same
	   H3-Mobile 22/28 pairing already used elsewhere on this page (e.g.
	   .rd-validation__quote-text just below), rather than leaving the old
	   30px line-height sized for the previous 24px. Still correct after
	   the 2026-10-01 card redesign -- only padding/radius/background are
	   new here, scaled down from the 24px/120px/15px desktop card by the
	   same "by feel" convention noted above (no separate mobile frame in
	   Figma for this node). */
	.rd-questions__link {
		padding: 20px 24px;
		border-radius: 10px;
		font-size: 22px;
		line-height: 28px;
	}

	/* 2026-09-25 (Carlos): mobile eyebrow spec -- Space Grotesk 10px,
	   #ADB5BD. */
	.rd-questions__eyebrow {
		font-size: 10px;
		line-height: 16px;
		letter-spacing: 0.5px;
		color: #ADB5BD;
	}

	/* 2026-09-24 (Carlos, round 5): "make the __headings a max-width of
	   320px". */
	.rd-questions__heading {
		font-size: 28px;
		line-height: 34px;
		max-width: 320px;
	}

	.rd-questions__ctas {
		flex-direction: column;
		align-items: stretch;
		width: 100%;
	}

	/* 2026-09-24 (Carlos, round 5): "something wrong with the button
	   under rd-questions, make it match the button CTA of the hero and
	   final CTA." The round-4 fix above (zeroed padding, forced width)
	   was wrong -- .rd-hero-ctas .com-btn and .rd-final-cta__ctas
	   .com-btn (style_new.css) never touch the button's own padding at
	   all; full width on mobile comes purely from the CONTAINER's
	   align-items: stretch (already set on .rd-questions__ctas above),
	   with the button keeping its normal 7px/26px pill padding. Matching
	   that exactly: just re-center the label, nothing else. */
	.rd-questions__ctas .com-btn,
	.rd-questions__ctas .com-btn-outlined {
		justify-content: center;
	}

	/* //06-Validation -- 2026-09-17: the row stacks (quote above stat,
	   each full width) below tablet width, same "no separate mobile
	   frame, proportional tightening" convention used elsewhere on this
	   page -- Carlos's 2/3+1/3 split is a desktop-width layout call.
	   2026-09-21 (Carlos, mobile pass, Figma node 6051:38549): real
	   mobile values -- 18px section side padding, py-96 px-48 cards,
	   H3-Mobile (22/28) for quote + stat text, H2-Mobile (30/36) for
	   the stat value, matching the wide-card treatment above. */
	/* 2026-09-22 (Carlos, round 3): "remove the rd-validation padding
	   top" / "remove the rd-validation bottom padding" -- was 64px 18px 0
	   (bottom was already 0; top now matches).
	   2026-09-23 (Carlos, round 4): also remove margin-top -- the
	   desktop base rule's `margin: 96px auto` (§8c) was still landing
	   its 96px top margin on mobile since nothing here overrode it. */
	.rd-validation {
		padding: 0 18px 0;
		margin-top: 0;
	}

	.rd-validation__row {
		flex-direction: column;
	}

	/* 2026-09-22 (Carlos, correction pass): "the boxes under validation
	   must be exactly the same as of the ones under the Now and Why
	   READI" -- swaps the desktop 96px/48px padding for the same 32px/
	   345px-min-height recipe .rd-program-card/.rd-why-card use on
	   mobile (border-radius already matches --rd-radius-card).
	   2026-09-24: .rd-validation__stat-card removed from this list -- it
	   now carries the .rd-why-card class itself (§8b/§8c), which already
	   applies this exact same 48px/345px/border-box mobile treatment, so
	   the two rules would just be duplicating each other's values. */
	.rd-validation__quote-card {
		padding: 48px;
		min-height: 345px;
		box-sizing: border-box;
	}

	.rd-validation__quote-text {
		font-size: 22px;
		line-height: 28px;
	}

	/* 2026-09-30 (Carlos): "the rd-why-card__heading in rd-validation must
	   be 22px" -- .rd-why-card__heading is a shared component class (the
	   Validation section's stat card reuses .rd-why-card's own
	   pill/heading/body markup, see that class's own 2026-09-24 comment
	   above), and its plain mobile rule (32/36) also feeds the Why READI
	   bento cards and the Orientation partner card, which Carlos did NOT
	   ask to change -- so this only touches the instance inside
	   .rd-validation, via descendant scoping, rather than editing the
	   shared rule. 22/28 matches the H3-Mobile pairing this same card's
	   own quote-text sibling just above already uses. */
	.rd-validation .rd-why-card__heading {
		font-size: 22px;
		line-height: 28px;
	}

	/* //07-Orientation -- 2026-09-22 (Carlos, round 3): "padding top:
	   48px" (section) + "24px padding left and right" (bento). */
	.rd-orientation {
		padding-top: 48px;
	}

	/* 2026-09-23 (Carlos, round 4): "the rd-orientation_header is bigger
	   than the other headers" -- .rd-orientation__heading had no mobile
	   override at all (unlike every other section heading), so it was
	   still rendering at the desktop 36/42 scale. Same 28/34 norm as
	   point 2 above. */
	/* 2026-09-25 (Carlos): mobile eyebrow spec -- Space Grotesk 10px,
	   #ADB5BD. */
	.rd-orientation__eyebrow {
		font-size: 10px;
		line-height: 16px;
		letter-spacing: 0.5px;
		color: #ADB5BD;
	}

	/* 2026-09-24 (Carlos, round 5): "make the __headings a max-width of
	   320px". */
	.rd-orientation__heading {
		font-size: 28px;
		line-height: 34px;
		max-width: 320px;
	}

	/* 2026-09-23 (Carlos, round 4): "add 24px padding bottom". */
	.rd-orientation__bento {
		gap: 16px;
		padding: 0 24px 24px;
	}

	.rd-orientation__row {
		padding: 0;
	}

	/* 2026-09-24: .rd-orientation-card's own padding/min-height override
	   and .rd-orientation-card__heading's 30/36 override are both removed
	   -- the card now carries .rd-why-card (§8b/§9), whose own mobile
	   rule already gives it the exact same 48px/345px/height:auto
	   treatment, and .rd-why-card__heading's own mobile rule already
	   covers the heading at 32/36 -- genuinely consistent with "the
	   cards on the previous versions" rather than a close parallel copy
	   that happened to land 2px off (30 vs 32).
	   2026-10-01 (Carlos, Figma node 5835:25284 -- redesign): padding
	   simplified to match the new desktop 48px-flat rule (see
	   .rd-orientation-partner's own comment) -- the old asymmetric
	   pt-120/pb-96 was only ever there to center the flat logo and no
	   longer applies now that .rd-orientation-partner__logo-wrap does
	   that with flex:1. */
	.rd-orientation-partner {
		padding: 48px;
		/* 2026-09-24: desktop is now a fixed height: 464px (was
		   min-height: 345px), so mobile needs an explicit height: auto
		   to cancel it -- min-height: 0 alone no longer does that. */
		height: auto;
		min-height: 0;
	}

	/* 2026-10-01 (Carlos, Figma node 5835:25284): on mobile the card has
	   no fixed height for the gradient wrap's flex:1 to grow against (see
	   height:auto above), so it needs its own min-height or it would
	   collapse flush around the 40px logo with no visible gradient --
	   180px scaled by feel against the desktop box's own roughly
	   464-96(padding)-24(gap)-18(caption) =~ 326px, no separate mobile
	   frame given for this node. */
	.rd-orientation-partner__logo-wrap {
		min-height: 180px;
	}

	/* //08-CTA -- 2026-09-17: moved to the shared global component's own
	   mobile block in style_new.css (see the desktop-block note above). */
}

/* ≤390px — smallest tested viewport; tighten container gutters further */
@media (max-width: 390px) {
	.rd-hero-container {
		padding: 0 16px;
	}

	.rd-hero-announcement {
		padding: 6px 12px;
	}
}

/* ── 11. Mobile perf fallback (rd-hero-stage--degraded) ──────────────────
   Applied by home-video.js only if the frame-rate watchdog decides a
   narrow viewport can't hold the per-frame scrub. Swaps to a single CSS
   transition triggered by IntersectionObserver instead of continuous rAF
   writes — see initHeroTransition() → engageDegradedMode(). */
.rd-hero-stage--degraded .rd-hero-video-frame,
.rd-hero-stage--degraded .rd-hero-content,
.rd-hero-stage--degraded .rd-video-fade-to-card,
.rd-hero-stage--degraded .rd-transition-statement,
.rd-hero-stage--degraded .logo-wall-section {
	transition: transform 500ms ease, opacity 500ms ease;
}
