:root {
  --promo-bar-height: 210px; /* 57px top pad + 96px logo + 57px bottom pad – keep in sync with .promo-bar/.promo-bar-logo. Was 26/88 (asymmetric, pushing the logo/search toward the top) to reserve clearance below the logo for the hero backdrop rising up into this space – needed back when --hero-rise was tall enough to intrude into the promo bar's own box. --hero-rise is now calibrated (2026-08-31) to land exactly at this box's bottom edge, not into it, so that clearance is no longer needed and the padding can go back to centering the content properly. */
  --navy-950: #020913;
  --navy-900: #061B3A;
  --navy-850: #082249;
  --navy-800: #0B2D59;
  --blue: #0A84FF;
  --blue-a: #1685F8;
  --teal: #22C7B8;
  --cyan: #35D4E8;
  --orange: #F5A623;
  --purple: #B65CFA;
  --yellow: #F7D84A;
  --ink: #151922;
  --slate: #596273;
  --muted: #8791A2;
  --line: #DCE3EC;
  --surface: #FFFFFF;
  --surface-soft: #F5F8FC;
  --surface-mid: #EEF2F7;
  --radius-sm: 14px;
  --radius-md: 22px;
  --radius-lg: 30px;
  --radius-xl: 42px;
  --shadow-sm: 0 10px 30px rgba(5, 24, 52, 0.08);
  --shadow-md: 0 24px 70px rgba(5, 24, 52, 0.13);
  --shadow-lg: 0 36px 110px rgba(1, 9, 24, 0.34);
  --page: min(1460px, calc(100% - 48px));
  --ease: cubic-bezier(.2,.8,.2,1);
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; overflow-anchor: none; }

/* Site-wide scrollbar, matching adobe.com's own: thinner than the
   browser default and a flat dark gray regardless of what section is
   behind it, rather than the light default track/thumb this page
   inherited from the OS. Firefox gets the standard scrollbar-width/
   -color properties; everything WebKit/Blink (Chrome, Safari, Edge)
   gets the ::-webkit-scrollbar pseudo-elements, since there's no single
   property both engines share for this. */
html {
  scrollbar-width: thin;
  scrollbar-color: #5a5a5a transparent;
}
html::-webkit-scrollbar { width: 11px; }
html::-webkit-scrollbar-track { background: transparent; }
html::-webkit-scrollbar-thumb {
  background: #5a5a5a;
  border-radius: 8px;
  border: 3px solid transparent;
  background-clip: padding-box;
}
html::-webkit-scrollbar-thumb:hover { background: #767676; }

body {
  margin: 0;
  min-width: 320px;
  color: var(--ink);
  background:
    linear-gradient(180deg, #05060f 0%, #070915 30%, #0a0b1c 55%, #080a17 80%, #060711 100%);
  font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  line-height: 1.55;
  overflow-x: clip;
}
button, input { font: inherit; }
button { color: inherit; }
a { color: inherit; }
img { max-width: 100%; display: block; }
svg { width: 1.15em; height: 1.15em; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.svg-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
[hidden] { display: none !important; }

.skip-link {
  position: fixed;
  left: 16px;
  top: -90px;
  z-index: 1000;
  background: white;
  color: var(--navy-900);
  padding: 12px 18px;
  border-radius: 12px;
  transition: top .2s;
}
.skip-link:focus { top: 16px; }
.sr-only {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0,0,0,0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}

.ambient { position: absolute; border-radius: 999px; filter: blur(70px); pointer-events: none; opacity: .55; z-index: 0; }
.ambient-one { width: 380px; height: 380px; background: rgba(10,132,255,.36); top: 40px; left: -140px; }
.ambient-two { width: 460px; height: 460px; background: rgba(34,199,184,.24); top: 450px; right: -190px; }
.ambient-three { width: 330px; height: 330px; background: rgba(182,92,250,.16); top: 860px; left: 45%; }

.glass {
  background: linear-gradient(135deg, rgba(255,255,255,.16), rgba(255,255,255,.07));
  border: 1px solid rgba(255,255,255,.22);
  box-shadow: var(--shadow-lg), inset 0 1px 0 rgba(255,255,255,.24);
  backdrop-filter: blur(28px) saturate(145%);
  -webkit-backdrop-filter: blur(28px) saturate(145%);
}
.section-shell { width: var(--page); margin-inline: auto; position: relative; z-index: 1; }

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  padding: 18px 0 0;
  /* The promo bar is now position:fixed (a stationary layer that never
     scrolls) – this margin reserves its height so the header/hero start
     right below it at rest, instead of hidden underneath it. As the page
     scrolls, this header rises normally and – once it reaches the top –
     sticks there, which is what visually covers the fixed promo bar. No
     JS-driven z-index flip needed; it's just two elements at the same
     scroll position, one fixed and one sticky. */
  margin-top: var(--promo-bar-height, 148px);
}

/* ---- Promo bar: Adobe's top offer strip, repurposed as a persistent
   search entry point instead of a limited-time deal. White by deliberate
   choice – the one bright moment against an otherwise dark page, the same
   job Adobe's black contrast sections do in reverse. Not sticky: like
   Adobe's own bar, it scrolls away and the nav (already sticky) takes over
   the top of the viewport once you pass it. ---- */
.promo-bar { background: var(--surface); padding: 57px 0; position: fixed; top: 0; left: 0; right: 0; z-index: 5; }
/* hero-solid-backdrop/hero-photo's rounded top corners (16px) now land
   exactly at this bar's own bottom edge (see --hero-rise, calibrated so
   the nav-compact transition is seamless) – right where the border-radius
   carves its little quarter-circle cutout out of the corner. Nothing else
   was positioned behind that cutout at that exact spot, so it exposed
   body's own dark background as a small black notch instead of reading
   as a clean rounded corner. This just extends the promo bar's own white
   a bit further down (well past the 16px radius needs) so that cutout has
   something correctly-colored behind it, without changing the bar's own
   measured height (offsetHeight, which is-compact and the header's own
   margin-top both depend on) – a pseudo-element doesn't count. */
.promo-bar::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -24px;
  height: 24px;
  background: var(--surface);
}
/* .hero-photo has several z-index:0 declarations scattered across older
   breakpoint-specific blocks (all predating the promo bar going fixed) –
   rather than hunt down and edit each one, this settles the stacking order
   definitively: the photo (and whatever sits on it, i.e. the nav at rest)
   must outrank the fixed promo bar (z-index 5) to actually cover it once
   scrolled, and the headline text must stay above the photo either way. */
.hero-photo, .hero-photo-layer { z-index: 6 !important; }
.hero-copy { z-index: 7 !important; }
/* Scroll-linked fade to black as the framework section rises to cover the
   hero (2026-08-31) – client storyboard: the whole slideshow, photo and
   text together, fades to solid dark as "Four areas" slides up over it,
   finishing the fade slightly before the slide itself finishes. Driven
   entirely by JS setting --hero-fade (0 to 1) in the same scroll handler
   that already runs for the nav's covering effect – this is a separate,
   independent overlay rather than animating .hero-photo's own opacity,
   since that opacity is already doing double duty for the photo crossfade
   and fighting over one property between two different scroll-driven
   effects was asking for a repeat of that bug. */
.hero-fade-overlay {
  position: absolute; inset: 0;
  background: var(--navy-950);
  opacity: var(--hero-fade, 0);
  pointer-events: none;
  z-index: 8;
}
/* .hero uses .section-shell, which caps its width at --page and centers it
   – meaning anything positioned with inset:0 inside .hero (the photo, its
   crossfade layer, and the solid backdrop) was only ever as wide as that
   centered, max-width box, not the actual viewport. On any screen wider
   than --page's cap, that left a real gap on both sides where the fixed
   promo bar was never covered at all. .hero-photo already had a fix for
   this further down (the "truly edge-to-edge" block, using
   left: calc(50% - 50vw)) – .hero-photo-layer and .hero-solid-backdrop
   need the exact same technique, not a second, different one layered on
   top (an earlier attempt here used left:50% + margin-left:-50vw, which
   doubled up with the existing calc() fix and shoved everything an extra
   50vw to the left). */
.hero-photo-layer, .hero-solid-backdrop, .hero-fade-overlay {
  left: calc(50% - 50vw) !important;
  right: auto !important;
  width: 100vw !important;
}
/* Per the client's storyboard: once fully covered, the slideshow's rounded
   top corner should permanently sit at the very top of the viewport – not
   just briefly, on the way past. The backdrop switches from "absolutely
   positioned inside .hero" to fixed-at-the-top the moment the nav goes
   glass/persistent, so its rounded edge becomes the page's new top edge
   for the rest of the scroll, instead of scrolling away with the rest of
   the hero's content. */
.site-header.is-compact ~ main .hero-solid-backdrop {
  position: fixed !important;
  top: 0 !important;
  bottom: auto !important;
  height: 100vh !important;
  border-radius: 16px 16px 0 0 !important;
  z-index: 4 !important;
}
/* .hero-photo and its crossfade layer must move in lockstep with the
   backdrop above – otherwise, once compact, the backdrop is correctly
   fixed with a rounded corner but the photo (still scrolling normally at
   this point, since it wasn't included in the fix above) ends up as a
   plain flat-edged rectangle sitting past its own rounded corner already.
   Being translucent, that flat edge tints straight across the backdrop's
   otherwise-clear notch with its own wash color, muddying what should be
   a clean white showing through. Same fixed position, same radius, so all
   three layers share one consistent cutout. */
.site-header.is-compact ~ main .hero-photo,
.site-header.is-compact ~ main .hero-photo-layer {
  position: fixed !important;
  top: 0 !important;
  bottom: auto !important;
  height: 100vh !important;
  border-radius: 16px 16px 0 0 !important;
}
/* 2026-09-02: opacity is scroll-driven (--hero-photo-fade, see styles
   further down and app.js) once compact – the .9s transition on
   .hero-photo's base rule exists for its own pre-compact photo-to-photo
   slideshow crossfade, and left active here it made the scroll-driven
   fade visibly lag behind actual scroll position (confirmed directly:
   scrolled to where the fade variable read exactly 0.000, computed
   opacity was still 0.046, mid-transition rather than actually there).
   Every other scroll-linked effect on this page is deliberately
   transition-free for the same reason; this brings hero-photo's fade in
   line with that once it takes over. */
.site-header.is-compact ~ main .hero-photo { transition: none !important; }
/* .hero-fade-overlay must go fixed/100vh in lockstep with the two layers
   above, for the same reason .hero-photo-layer does: left on its normal
   absolute positioning (sized to .hero's own shrinking in-flow box), its
   opacity – still a smooth 0-to-1 scroll-driven value – was only ever being
   painted over .hero's current, ever-shrinking box, not the full fixed
   area it's meant to be darkening. The darkening itself stayed gradual;
   what broke was where it was allowed to apply, so it stopped dead at
   .hero's bottom edge instead of continuing across the rest of the fixed
   photo/backdrop, reading as a hard line rather than a fade. */
.site-header.is-compact ~ main .hero-fade-overlay {
  position: fixed !important;
  top: 0 !important;
  bottom: auto !important;
  height: 100vh !important;
}
/* The release: once "Four areas" has comfortably scrolled past, these go
   back to plain absolute positioning tied to .hero's own (long since
   scrolled-away) box, instead of staying fixed over everything below. */
.site-header.hero-released ~ main .hero-photo,
.site-header.hero-released ~ main .hero-photo-layer,
.site-header.hero-released ~ main .hero-solid-backdrop {
  position: absolute !important;
  height: auto !important;
}
/* .hero's isolation:isolate + z-index:6 !important (needed so its own
   fixed layers can cover the fixed promo bar, z-index 5) means every
   section that follows it in <main> needs its OWN z-index above 6 too, or
   it stacks below .hero – via .section-shell's un-competitive baseline of
   1 – everywhere the two happen to overlap on screen. That overlap isn't
   just framework-section's deliberate -56px underlap (where it showed up
   as that section's rounded top corner reading as hard and square, hero's
   flat-bottomed layers painted over it); it's the entire scroll stretch
   from is-compact engaging to the hero actually releasing, since the
   photo/backdrop/fade trio stay fixed at 100vh for that whole stretch –
   long enough that later sections (preview-section, etc.) scroll into
   view and would otherwise vanish under the fixed hero the same way
   framework-section did. z-index comfortably clears .hero's 6 and stays
   well under the sticky nav's 75. */
main > section:not(.hero), .site-footer {
  position: relative;
  z-index: 10;
}
/* .hero-photo is a deliberate low-opacity wash (not a solid image), so on
   its own it can never fully hide the fixed promo bar behind it – ~40% of
   whatever's under it always shows through. This backdrop is the actual
   opaque covering surface: same position/offset as the photo, solid fill,
   sitting immediately behind it in paint order (equal z-index, earlier in
   the DOM) so the photo's wash and the headline text keep layering on top
   exactly as before. */
.hero-solid-backdrop {
  position: absolute;
  top: calc(-1 * var(--hero-rise));
  /* Matches .hero-photo's own height fix (100vh, not auto) – see the
     comment on .hero-photo's base rule. Kept in lockstep so the backdrop
     never falls short of the photo sitting on top of it. */
  height: 100vh;
  background: var(--navy-950);
  z-index: 6;
  pointer-events: none;
  border-radius: 16px 16px 0 0;
}
/* .hero itself has isolation:isolate (line ~4179), which traps every
   z-index inside it into a local stacking context – meaning hero-photo's
   z-index above only matters once .hero's OWN z-index also clears the
   promo bar's. Without this, no z-index set on anything inside .hero could
   ever visually outrank the promo bar, isolation contains it completely. */
.hero { z-index: 6 !important; }
.promo-bar-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px; flex-wrap: wrap;
  padding: 0 14px 0 22px;
}
.promo-bar-logos { display: flex; align-items: center; gap: 20px; flex: none; min-width: 0; }
.promo-bar-logo { height: clamp(48px, 18vw, 96px); width: auto; display: block; }
/* 2026-09-06: optical alignment. The two marks are box-aligned, but the eye
   aligns on the WHITE letterforms, not on the bounding box – and this logo
   carries a hard black drop shadow that leads the "h" in from the left. The
   asset was measured rather than eyeballed: the first white pixel column in
   heg-logo-sm.webp is 94 of 2500, so the shadow occupies the leading 3.76%
   of the file's own width, while logo-hehabits-white.webp starts its white
   at column 0. Pulling this mark left by exactly that fraction puts the two
   "h" stems on the same vertical.
   translateX(%) rather than a negative margin on purpose: percentage
   transforms resolve against the ELEMENT's own width, so this stays correct
   at every size the clamp() above produces, whereas a percentage margin
   resolves against the container and would drift as the viewport changes.
   It also keeps the correction purely visual – the flex layout, and so the
   gap to the Minimap logo, is untouched. */
.promo-bar-logo-heg { transform: translateX(-3.76%); }
.promo-bar-search {
  display: flex; align-items: center; gap: 10px;
  background: var(--surface-soft); border: 1px solid var(--line);
  border-radius: 999px; padding: 6px 6px 6px 24px;
  flex: 0 1 760px;
}
.promo-bar-search input {
  flex: 1; border: 0; background: transparent; font: inherit;
  font-size: .96rem; color: var(--ink); min-width: 0;
}
.promo-bar-search input:focus { outline: none; }
.promo-bar-search input::placeholder { color: var(--muted); }
.promo-bar-search .button {
  white-space: nowrap; padding: 10px 22px; font-size: .86rem;
  display: inline-flex; align-items: center; gap: 8px;
  border-radius: 999px;
}
.promo-bar-search .button svg { width: 15px; height: 15px; }
@media (max-width: 760px) {
  .promo-bar-inner { flex-direction: column; align-items: stretch; text-align: center; }
  .promo-bar-search { flex: none; }
}

.header-shell {
  width: var(--page);
  min-height: 78px;
  margin: 0 auto;
  padding: 10px 14px 10px 22px;
  border-radius: 16px;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 20px;
  background: rgba(4, 21, 47, .76);
  transition: transform .3s var(--ease), background .08s;
}
.brand { display: inline-flex; align-items: center; gap: 14px; text-decoration: none; min-width: max-content; }
.brand img { width: 92px; height: 44px; object-fit: contain; }
.brand-copy { display: grid; color: white; line-height: 1.15; }
.brand-copy strong { font-size: .96rem; letter-spacing: .01em; }
.brand-copy small { margin-top: 4px; color: rgba(255,255,255,.58); font-size: .68rem; white-space: nowrap; }

.primary-nav { display: flex; align-items: center; justify-content: flex-start; gap: 26px; }
.nav-link, .site-footer button {
  border: 0;
  background: transparent;
  cursor: pointer;
}
.nav-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: #fff;
  padding: 10px 13px;
  border-radius: 12px;
  font-size: .8rem;
  font-weight: 700;
  letter-spacing: .055em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: color .25s var(--ease), background .25s var(--ease);
}
.nav-link::after {
  content: "";
  position: absolute;
  left: 14px; right: 14px; bottom: 5px;
  height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--cyan), var(--purple));
  transform: scaleX(0);
  opacity: 0;
  transition: transform .3s var(--ease), opacity .3s var(--ease);
}
.nav-link:hover, .nav-link:focus-visible { color: #fff; }
.nav-link:hover::after, .nav-link:focus-visible::after { transform: scaleX(1); opacity: 1; }
.nav-link.is-active { color: #fff; }
.nav-link.is-active::after { transform: scaleX(1); opacity: 1; }

.header-actions { display: inline-flex; align-items: center; gap: 12px; justify-self: end; }
/* Map the Game only appears in the nav once it's a separate, persistent bar
   – at rest it would just duplicate the promo bar's own search and the
   hero's search panel, both already visible at that scroll position. */
.nav-search { display: none; }
.site-header.is-compact .nav-search { display: flex; }
.header-shell .nav-search { display: none !important; }
.site-header.is-compact .header-shell .nav-search { display: flex !important; }
.nav-search {
  align-items: center; gap: 8px;
  background: var(--surface-soft); border: 1px solid var(--line);
  border-radius: 999px; padding: 4px 4px 4px 16px;
}
.nav-search input {
  border: 0; background: transparent; font: inherit;
  font-size: .86rem; color: var(--ink); width: 330px; min-width: 0;
}
.nav-search input:focus { outline: none; }
.nav-search input::placeholder { color: var(--muted); }
.nav-search .button {
  white-space: nowrap; padding: 8px 16px; font-size: .8rem;
  min-height: 0; border-radius: 999px;
  display: inline-flex; align-items: center; gap: 6px;
}
.nav-search .button svg { width: 13px; height: 13px; }

.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  min-height: 48px;
  padding: 12px 19px;
  border: 1px solid transparent;
  border-radius: 15px;
  cursor: pointer;
  font-weight: 760;
  text-decoration: none;
  transition: transform .22s var(--ease), box-shadow .22s, background .22s, border-color .22s;
}
.button:hover { transform: translateY(-2px); }
.button:active { transform: translateY(0); }
.button:focus-visible, button:focus-visible, input:focus-visible, a:focus-visible { outline: 3px solid rgba(53,212,232,.62); outline-offset: 3px; }
.button-primary { color: white; background: linear-gradient(135deg, var(--blue), var(--teal)); box-shadow: 0 12px 28px rgba(17,163,195,.28); }
.button-primary:hover { box-shadow: 0 17px 35px rgba(17,163,195,.38); }
.button-small { min-height: 48px; padding: 10px 17px; border-radius: 14px; font-size: .88rem; }
.button-quiet { background: rgba(255,255,255,.8); color: var(--navy-900); border-color: rgba(9,36,71,.12); box-shadow: var(--shadow-sm); }
.icon-button { width: 44px; height: 44px; display: inline-grid; place-items: center; padding: 0; border: 1px solid rgba(255,255,255,.14); border-radius: 14px; background: rgba(255,255,255,.08); color: white; cursor: pointer; }

.hero { min-height: 780px; padding: 88px 14px 86px 22px; display: grid; grid-template-columns: minmax(0, 1fr); align-items: center; gap: 32px; color: white; }
/* Search moved to the promo bar (2026-08-30) – the hero's own search panel
   is hidden, not removed, since #gameSearchForm is still the real form the
   promo bar's compact input submits through (see initPromoBarSearch in
   app.js). Deleting it would break that relay along with every bit of
   autocomplete/confirmation logic already wired to its exact element ids. */
.hero-right { display: none; }
.hero-copy { max-width: 920px; }
.eyebrow { display: inline-flex; align-items: center; gap: 10px; text-transform: uppercase; letter-spacing: .13em; font-size: .76rem; font-weight: 850; color: var(--cyan); }
.eyebrow span { width: 30px; height: 2px; border-radius: 99px; background: linear-gradient(90deg, var(--blue), var(--teal)); }
.hero h1 { margin: 22px 0 0; font-family: 'Sora', Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; font-size: clamp(3.7rem, 7.2vw, 8.1rem); line-height: .9; letter-spacing: -.02em; font-weight: 600; max-width: 960px; }
/* "Protect the fun." – a true sibling of the H1, not nested inside it.
   Necessary, not just stylistic: testing found CSS keyframe animations
   on descendants of this specific H1 silently don't apply (confirmed
   via the Web Animations API – the animation reports "running" and
   correctly advances currentTime, but the computed style never
   reflects it) while animations on the H1 itself, and on its siblings,
   both work correctly. Rather than ship a gradient "flow" animation
   that's inert, this line moved out from under the H1. Font-size and
   friends are kept in sync with the H1's own (which has ~15 separate
   responsive overrides across breakpoints) via JS rather than
   duplicating every one of those rules here – see syncHeroTitleLine2
   in app.js. Screen readers get the full "Map the game. Protect the
   fun." via the H1's aria-label; this line is aria-hidden so its text
   isn't announced twice. */
/* Several other .hero h1 rules across different breakpoints set their
   own margin-bottom with !important – those predate this restructure
   and were creating an unwanted gap between "Map the game." and
   "Protect the fun." now that the latter is a separate element rather
   than a <br>. Zeroed here, specifically for #heroTitle, so it doesn't
   affect any other h1 those rules might also reach. */
#heroTitle { margin-bottom: 0 !important; }
.hero-title-line2 {
  margin: 0 0 22px;
  font-family: 'Sora', Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: clamp(3.7rem, 7.2vw, 8.1rem);
  line-height: .9;
  letter-spacing: -.02em;
  font-weight: 600;
  max-width: 960px;
}
.hero h1 span { color: transparent; background: linear-gradient(90deg, var(--cyan), var(--teal) 45%, #7BE7BC); background-clip: text; -webkit-background-clip: text; }
.hero-lead { font-size: clamp(1.05rem, 1.5vw, 1.3rem); max-width: 900px; color: rgba(255,255,255,.72); margin: 0 0 30px; }

.search-panel { border-radius: 26px; padding: 10px; background: rgba(255,255,255,.1); position: relative; }
.search-field-wrap { position: relative; display: grid; grid-template-columns: auto 1fr auto; gap: 12px; align-items: center; background: rgba(255,255,255,.96); border-radius: 18px; padding: 8px 8px 8px 18px; box-shadow: 0 18px 50px rgba(0,0,0,.18); }
.search-icon { color: var(--blue); width: 24px; height: 24px; }
.search-field-wrap input { min-width: 0; border: 0; outline: 0; color: var(--ink); background: transparent; font-size: 1.05rem; padding: 12px 0; }
.search-field-wrap input::placeholder { color: #8C95A5; }
.analyze-button { min-height: 52px; padding-inline: 22px; }
.search-footer { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; padding: 10px 10px 2px; color: rgba(255,255,255,.55); font-size: .81rem; }
.search-footer button { color: rgba(255,255,255,.78); border: 0; background: transparent; cursor: pointer; padding: 3px 6px; border-radius: 8px; }
.search-footer button:hover { color: white; background: rgba(255,255,255,.08); }
.search-message { color: #FFD5D5; font-size: .88rem; margin: 7px 12px 0; min-height: 0; }
.suggestion-list { position: absolute; top: calc(100% + 12px); left: 0; right: 0; z-index: 60; border-radius: 20px; padding: 7px; max-height: 340px; overflow-y: auto; overscroll-behavior: contain; background: linear-gradient(180deg, rgba(20,22,52,.97), rgba(11,12,34,.98)); color: #fff; box-shadow: 0 34px 90px rgba(4,4,16,.62), 0 2px 0 rgba(255,255,255,.05) inset; border: 1px solid rgba(255,255,255,.14); backdrop-filter: blur(34px) saturate(150%); -webkit-backdrop-filter: blur(34px) saturate(150%); transform-origin: top center; animation: suggest-in .3s var(--ease-spring) both; }
.suggestion-list::-webkit-scrollbar { width: 8px; }
.suggestion-list::-webkit-scrollbar-thumb { background: rgba(255,255,255,.16); border-radius: 8px; }
.suggestion-option { display: grid; grid-template-columns: 44px 1fr auto; align-items: center; gap: 13px; width: 100%; padding: 9px 11px; border: 0; border-radius: 14px; background: transparent; text-align: left; cursor: pointer; color: #fff; transition: background .18s ease, box-shadow .18s ease; }
.suggestion-option:hover, .suggestion-option[aria-selected="true"] { background: rgba(255,255,255,.08); }
.suggestion-option[aria-selected="true"] { box-shadow: inset 0 0 0 1px rgba(53,212,232,.4); }
.suggestion-cover { width: 44px; height: 44px; border-radius: 13px; display: grid; place-items: center; color: white; font-weight: 850; box-shadow: 0 8px 20px rgba(0,0,0,.42); background: linear-gradient(145deg, var(--blue), var(--navy-900)); }
.suggestion-option strong { display: block; font-size: .92rem; color: #fff; }
.suggestion-option small { display: block; color: rgba(255,255,255,.56); margin-top: 2px; font-size: .78rem; }
.suggestion-option > small { white-space: nowrap; color: rgba(255,255,255,.44); font-variant-numeric: tabular-nums; }
@keyframes suggest-in { from { opacity: 0; transform: translateY(-8px) scale(.98); } to { opacity: 1; transform: none; } }

.hero-trust-grid { margin-top: 28px; display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.hero-trust-grid article { display: flex; align-items: center; gap: 12px; padding: 13px 0; }
.mini-icon { width: 42px; height: 42px; border-radius: 13px; display: grid; place-items: center; background: rgba(255,255,255,.09); color: var(--cyan); border: 1px solid rgba(255,255,255,.1); }
.hero-trust-grid strong { display: block; font-size: .86rem; }
.hero-trust-grid small { display: block; color: rgba(255,255,255,.48); font-size: .72rem; margin-top: 3px; }
.privacy-line { display: flex; align-items: center; gap: 8px; margin-top: 10px; color: rgba(255,255,255,.52); font-size: .8rem; }
.privacy-line svg { color: var(--teal); }

.hero-visual { min-height: 630px; position: relative; display: grid; place-items: center; overflow: clip; border-radius: 44px; }
.hero-visual > img { width: 112%; max-width: none; transform: translateX(3%); filter: drop-shadow(0 40px 70px rgba(0,0,0,.32)); }
.visual-glow { position: absolute; width: 78%; aspect-ratio: 1; background: radial-gradient(circle, rgba(34,199,184,.28), transparent 68%); filter: blur(20px); }
.decision-card { position: absolute; width: 300px; right: -10px; top: 80px; padding: 22px; border-radius: 24px; background: rgba(4,18,42,.74); color: white; }
.decision-card-heading { display: flex; gap: 12px; align-items: center; padding-bottom: 16px; border-bottom: 1px solid rgba(255,255,255,.1); }
.shield-orb { width: 46px; height: 46px; border-radius: 15px; display: grid; place-items: center; color: var(--cyan); background: rgba(53,212,232,.12); }
.decision-card-heading small { display: block; color: rgba(255,255,255,.54); font-size: .69rem; }
.decision-card-heading strong { display: block; font-size: .95rem; margin-top: 2px; }
.decision-card ul { list-style: none; padding: 12px 0 7px; margin: 0; display: grid; gap: 9px; }
.decision-card li { display: flex; align-items: center; gap: 9px; font-size: .84rem; color: rgba(255,255,255,.78); }
.decision-card li svg { color: var(--teal); }
.decision-footer { display: flex; justify-content: space-between; gap: 10px; padding-top: 13px; border-top: 1px solid rgba(255,255,255,.1); font-size: .73rem; color: rgba(255,255,255,.45); }
.decision-footer strong { color: white; }
.floating-chip { position: absolute; display: inline-flex; align-items: center; gap: 8px; padding: 10px 14px; border-radius: 14px; color: white; background: rgba(7,28,60,.72); border: 1px solid rgba(255,255,255,.15); box-shadow: 0 18px 42px rgba(0,0,0,.2); font-size: .77rem; font-weight: 750; backdrop-filter: blur(20px); }
.floating-chip svg { color: var(--cyan); }
.chip-one { left: 5%; bottom: 112px; }
.chip-two { right: 18%; bottom: 44px; }

.how-section { padding: 96px 0 92px; }
.section-heading { display: flex; justify-content: space-between; align-items: end; gap: 28px; margin-bottom: 34px; }
.section-heading.centered { display: block; text-align: center; max-width: 740px; margin: 0 auto 44px; }
.section-heading h2 { margin: 10px 0 0; font-size: clamp(2.3rem, 4vw, 4.5rem); line-height: 1.02; letter-spacing: -.05em; color: var(--navy-900); }
.section-heading > p, .section-heading > div + p, .section-heading.centered p { max-width: 590px; color: var(--slate); font-size: 1.05rem; margin: 12px 0 0; text-wrap: pretty; }
.hero-lead { text-wrap: pretty; }
.section-heading .eyebrow { color: var(--blue); }
.steps-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 14px; }
.step-card { position: relative; min-height: 230px; padding: 23px; border-radius: 25px; background: rgba(255,255,255,.78); border: 1px solid rgba(9,36,71,.08); box-shadow: var(--shadow-sm); transition: transform .25s var(--ease), box-shadow .25s; }
.step-card:hover { transform: translateY(-6px); box-shadow: var(--shadow-md); }
.step-number { position: absolute; right: 18px; top: 16px; color: #D1D9E5; font-size: 1.1rem; font-weight: 850; }
.step-icon { width: 58px; height: 58px; border-radius: 18px; display: grid; place-items: center; margin-bottom: 42px; }
.step-icon svg { width: 25px; height: 25px; }
.step-blue { color: var(--blue); background: rgba(10,132,255,.12); }
.step-cyan { color: var(--teal); background: rgba(34,199,184,.13); }
.step-purple { color: var(--purple); background: rgba(182,92,250,.12); }
.step-orange { color: var(--orange); background: rgba(245,166,35,.14); }
.step-green { color: #17A874; background: rgba(23,168,116,.12); }
.step-card h3 { margin: 0 0 7px; color: var(--navy-900); font-size: 1.06rem; }
.step-card p { margin: 0; color: var(--slate); font-size: .88rem; }

.framework-section {
  padding: 100px 0 92px;
  position: relative;
  /* Needs to outrank .hero's stacking context so it isn't hidden beneath
     the (possibly fixed) hero layers – see the shared main > section rule
     below the hero-released block for why, and why every section after
     this one needs the same treatment. */
  background: linear-gradient(180deg, var(--navy-850) 0%, var(--navy-900) 55%, var(--navy-950) 100%);
  /* Matches header-shell's own border-radius (16px) and hero-photo/
     hero-solid-backdrop's top corners (also 16px) – this section sits
     directly beneath both in the same top-of-page reveal, so its corner
     needs to read as part of the same shape language, not a separate,
     much-larger one. */
  border-radius: 16px 16px 0 0;
  box-shadow: 0 -40px 90px rgba(1, 9, 24, 0.45);
  margin-top: -56px;
}
/* This section also carries .section-shell (for the z-index:1 baseline –
   now overridden above – and to match its siblings' markup), which caps
   it at var(--page) and centers it, same as every other section. Every
   OTHER section is fine staying that width, because there's nothing of
   theirs that needs to reach the true screen edge. This one does: its
   whole point is to visually replace the full-bleed hero behind it, and a
   capped width left real gutters down both sides where hero-photo and
   hero-solid-backdrop – still fixed underneath, for the whole pre-release
   stretch – kept showing through even after "Four areas" had otherwise
   taken over. Go edge-to-edge the same way those hero layers do (see the
   left: calc(50% - 50vw) block above), and move the actual heading/grid
   content into an inner wrapper that keeps the normal capped width –
   the same relationship .promo-bar has with .promo-bar-inner. */
.framework-section.section-shell {
  width: 100vw;
  max-width: 100vw;
  margin-inline: 0;
  left: calc(50% - 50vw);
  right: auto;
}
.framework-inner.section-shell {
  position: static;
  z-index: auto;
  /* Matches .hero and .header-shell's own horizontal padding exactly, so
     "Four areas" content lines up with the nav/headline text above it
     instead of starting flush with the centered page box's raw edge. */
  padding: 0 14px 0 22px;
}
.framework-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 17px; }
.framework-card { position: relative; min-height: 360px; overflow: hidden; padding: 26px; border-radius: 30px; color: white; box-shadow: var(--shadow-md); transition: transform .3s var(--ease), box-shadow .3s; }
.framework-card::after { content: ""; position: absolute; width: 260px; height: 260px; border-radius: 50%; right: -90px; bottom: -105px; background: rgba(255,255,255,.12); box-shadow: -35px -25px 0 rgba(255,255,255,.05); }
.framework-card:hover { transform: translateY(-8px); box-shadow: 0 30px 70px rgba(5,24,52,.22); }
.category-a { background: linear-gradient(155deg, #1B90FF, #0757CC 68%, #043C90); }
.category-b { background: linear-gradient(155deg, #2BC4B6, #0A8E89 68%, #086863); }
.category-c { background: linear-gradient(155deg, #F7B23D, #E7791F 68%, #B5520E); }
.category-d { background: linear-gradient(155deg, #C16FFF, #8F3ADD 68%, #6320A6); }
.category-e { background: linear-gradient(155deg, #4C86FF, #2563EB 68%, #1E3A8A); }
.category-top { display: flex; align-items: center; justify-content: space-between; }
.category-top > svg { width: 34px; height: 34px; opacity: .85; }
.category-letter { width: 48px; height: 48px; border-radius: 15px; display: grid; place-items: center; background: rgba(255,255,255,.18); font-size: 1.25rem; font-weight: 900; border: 1px solid rgba(255,255,255,.24); }
.framework-card h3 { margin: 40px 0 13px; max-width: 260px; font-size: 1.45rem; line-height: 1.12; letter-spacing: -.025em; }
.framework-card p { color: rgba(255,255,255,.74); font-size: .92rem; max-width: 280px; }
.framework-card button { position: absolute; left: 26px; bottom: 25px; z-index: 2; display: inline-flex; gap: 8px; align-items: center; color: white; border: 0; background: transparent; padding: 6px 0; cursor: pointer; font-weight: 760; font-size: .86rem; }

.preview-section {
  padding: 94px 0 40px;
  /* 2026-09-02: matches framework-section's own ending color (navy-950)
     exactly, per request – was fully transparent, relying on the page's
     own (differently-toned) gradient, which created a visible color seam
     right where framework's section ends. Turns out this same gap was
     also why hero's fixed layers could still show through here despite
     framework and this section both sitting at a higher z-index: z-index
     only controls paint *order*, not opacity – a transparent section at
     z-index 10 still lets whatever's underneath at z-index 6 show through
     wherever its own fill doesn't cover. An opaque background closes
     both problems at once.
     2026-09-03: rounded bottom corners added – this is the actual
     mechanic behind adobe.com's "garage door" transition into their
     next section (confirmed by pixel-measuring their own reference
     video directly: the white panel preceding their dark section has a
     genuinely rounded bottom-left corner – transition point shifts from
     y=792 up near the corner to a flat y=808 just 20px further in,
     which only happens with real border-radius, not a sharp edge). It's
     a static CSS property on this section, not a JS-driven animation –
     as this opaque, full-bleed panel scrolls up and out of view during
     ordinary scrolling, its curved bottom corners are what create the
     reveal effect around them, uncovering .methodology-hero (now always
     fully opaque underneath, see that rule) rather than any fade/slide
     applied to the hero itself. */
  border-radius: 0 0 40px 40px;
  background: var(--navy-950);
}
/* 2026-09-02: the opaque background above only closed part of the gap –
   .preview-section still carries .section-shell, which caps it at
   var(--page) and centers it (1460px of 1512px viewport width at desktop,
   confirmed directly), leaving real ~26px gutters down both sides where
   the background fill never reached at all. Hero's fixed layers kept
   showing through those gutters specifically – confirmed with a pixel
   sample right at the edge: (37,40,48), nowhere close to the section's
   own (2,9,19) navy fill just a few px further in. This is the exact
   same issue .framework-section.section-shell already solved (see its
   own comment, ~line 556) – full-bleed width on the section itself, with
   an inner wrapper (.preview-inner, matching .framework-inner) keeping
   the actual content at the normal capped width. Preview-section just
   never got the same treatment since it's newer than that fix. */
.preview-section.section-shell {
  width: 100vw;
  max-width: 100vw;
  margin-inline: 0;
  left: calc(50% - 50vw);
  right: auto;
}
.preview-inner.section-shell {
  position: static;
  z-index: auto;
}
/* Report showcase heading – matched to Four Areas' own centered treatment
   exactly (~line 6987), per request: same h2 font-size/weight/letter-
   spacing, same p font-size/line-height/max-width/alignment. Duplicated
   as its own scoped rule rather than widening the original .framework-
   section-only selector, same approach already used for the Game Minimap
   Breakdown heading a couple rounds back. */
.preview-section .section-heading.centered h2 {
  font-size: clamp(2.1rem, 3.6vw, 3.1rem);
  font-weight: 400;
  letter-spacing: -.01em;
}
.preview-section .section-heading.centered p {
  max-width: 1000px;
  margin: 20px auto 0;
  font-size: clamp(1.05rem, 1.3vw, 1.2rem);
  line-height: 1.6;
}
/* 2026-09-02: per request – headline is one unbroken line now ("actually
   use" is no longer a separate colored span, see index.html), which needs
   more room than the base .centered wrapper's 740px to actually stay on
   one line; matched to Four Areas' own widened wrapper (~line 6984)
   rather than inventing a new value. Eyebrow uncapitalized and its
   decorative line removed, scoped to this section only – .eyebrow is
   used elsewhere (report page, etc.) and keeps its usual treatment there. */
.preview-section .section-heading.centered { max-width: 1100px; }
.preview-section .eyebrow { text-transform: none; }
.methodology-section .eyebrow { text-transform: none; }
.preview-section .section-heading.centered .eyebrow::after { content: none; }
/* Same treatment for the Methodology section's heading – widened so the
   shortened one-line headline and two-line copy (per request) have
   enough room at in-between viewport widths, and the eyebrow's
   decorative connector line removed (per request – "remove the line
   above the headline"), same as Sample Report's eyebrow above. */
.methodology-section .section-heading.centered { max-width: 900px; }
.methodology-section .section-heading.centered .eyebrow::after { content: none; }
/* 2026-09-03: two fixes per direct feedback with a screenshot –
   (1) headline font-weight matched to the other section headlines
   (framework/preview both get weight:400 via their own scoped rules;
   methodology never got the equivalent override, so its h2 was still
   on whatever heavier weight the base rule resolves to);
   (2) copy visibly off-center – the base .section-heading p rule sets
   max-width:590px but only margin:12px 0 0 (no auto), so once the
   paragraph is narrower than its container it sits at the container's
   left edge as a block even though its own text is center-aligned
   within that block – center-aligned text in an off-center box reads
   as "off-center" overall, exactly what the screenshot showed.
   preview-section already carries the fix (margin:20px auto 0);
   methodology-section never got the equivalent. */
.methodology-section .section-heading.centered h2 {
  font-size: clamp(2.1rem, 3.6vw, 3.1rem);
  font-weight: 400;
  letter-spacing: -.01em;
}
.methodology-section .section-heading.centered p {
  max-width: 640px;
  margin: 16px auto 0;
  font-size: clamp(1.05rem, 1.3vw, 1.2rem);
  line-height: 1.6;
}
.faq-section .section-heading.centered h2 {
  font-size: clamp(2.1rem, 3.6vw, 3.1rem);
  font-weight: 400;
  letter-spacing: -.01em;
  color: #fff;
}
.faq-section .section-heading.centered p {
  max-width: 640px;
  margin: 16px auto 0;
  font-size: clamp(1.05rem, 1.3vw, 1.2rem);
  line-height: 1.6;
  color: rgba(255,255,255,.6);
}
/* .preview-window and its descendants (.preview-sidebar, .preview-tabs,
   .mini-category, etc., continuing below) styled the old hand-built fake
   report mockup that .showcase-* replaces. Left in place, now unused,
   rather than risking a wider cleanup pass unrelated to this change. */

/* ---- report showcase: real screenshot, Adobe-style "fills the screen,
   then shrinks as you scroll" – replaces the old fake preview-window
   mockup.

   2026-09-02 (rebuilt): the first version pinned the image with
   position:sticky and shrank it across a tall artificial scroll track –
   which meant the shrink happened *after* the image had already arrived
   and stopped moving, while the user kept scrolling past a stationary
   image. Per feedback, the shrink needs to happen *during* the entrance
   itself, so it's already settled by the time the image reaches its
   natural reading position – no pinning, no artificial track. The image
   just sits in normal document flow at its one, constant, settled size
   (that's its real layout box); --showcase-progress (0 while still
   entering from below -> 1 once it's reached its resting position) is
   set continuously in app.js (initShowcaseScroll) from the image's own
   getBoundingClientRect() as it scrolls, same as any other element would.
   transform:scale() applies the enlarged "still arriving" look on top of
   that constant box without touching layout – the box itself never
   changes size, so nothing around it has to reflow as it shrinks. */
/* 2026-09-02: mat frame around the image – a solid background-color on a
   content-sized wrapper, revealed as a thin edge via 1px padding. Chosen
   specifically as a mechanism with nothing in common with the box-shadow
   approaches already tried and already implicated by direct video
   evidence (see .showcase-image below): no inset, no alpha-blended
   color, no shadow-clipping-against-border-radius interaction at all –
   just an opaque rectangle slightly larger than the image, showing
   through around its edges. width:fit-content means this wrapper's own
   size simply follows the image's continuously-changing width each
   frame rather than needing its own copy of that formula. Color is the
   solid, pre-blended equivalent of the previous rgba(255,255,255,.14)
   stroke over the section's own navy-950 background (rgb(2,9,19)),
   computed directly rather than left as a transparency composite.
   2026-09-02 (radius fixed): was calc(28px * progress + 1px), animating
   in step with the image below – now a flat 29px, see that rule for why. */
.showcase-frame {
  display: block;
  width: fit-content;
  margin: 0 auto;
  padding: 1px;
  background: rgb(37, 43, 52);
  border-radius: 29px;
  /* 2026-09-03: position:relative + z-index:0 together establish a new
     stacking context, which the ::before glow below needs for its own
     negative z-index to correctly mean "behind this element's
     background" rather than "behind the page's entire background stack"
     – position:relative alone does NOT create a stacking context by
     itself (z-index has to actually be set, even to 0); without this,
     the glow was invisible entirely, confirmed directly by temporarily
     forcing it to a high positive z-index instead, which made it fill
     the whole viewport – ruling out overflow-clipping and confirming
     this was purely a missing stacking context. */
  position: relative;
  z-index: 0;
}
/* 2026-09-03: a soft purple/pink radial glow behind the image – sits
   behind the frame's own opaque background (negative z-index, within
   the stacking context .showcase-frame's position:relative + z-index:0
   create above), extending beyond its edges so it reads as ambient light
   rather than a hard-edged shape. Percentage inset rather than a fixed
   pixel value so the glow scales proportionally with the frame's own
   continuously-changing size during the entrance animation, instead of
   staying a fixed size while the image around it grows/shrinks.
   pointer-events:none so it can't intercept the play-button click or
   any other interaction with the image above it. */
/* 2026-09-06: caption under the sample screenshot. States plainly that the
   sample is a real dated report rather than a mock-up – which is the whole
   claim the product rests on – and that games change, so a report run today
   may differ. That second half also ages gracefully: it explains any drift
   between the sample and a fresh report without the caption becoming wrong.
   Narrow and centred to match the section heading above it. */
.showcase-note {
  /* 2026-09-06: widened 62ch to 86ch so the sentence lands on two lines
     instead of three. Capped in ch rather than px because the limit that
     matters is characters per line — readability, not pixels — and a ch cap
     tracks the font size if it ever changes.
     Italic to set it apart as an aside about the sample rather than another
     piece of the page's copy. */
  max-width: 86ch;
  margin: 18px auto 0;
  text-align: center;
  font-style: italic;
  font-size: .86rem;
  line-height: 1.5;
  color: rgba(255,255,255,.55);
}
/* The date phrase is a third route to the same PDF. No underline at rest,
   per request — it is set apart by being noticeably brighter than the 55%
   white around it. The underline returns on hover AND on keyboard focus:
   focus in particular is not optional, because a keyboard user has no
   pointer to reveal the target and needs to see where they are. */
.showcase-note a {
  color: rgba(255,255,255,.9);
  text-decoration: none;
}
.showcase-note a:hover,
.showcase-note a:focus-visible {
  color: #fff;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: #A259FF;
}

/* 2026-09-06: free sample download, sat over the showcase screenshot.
   "Try before you buy" – a 10-page PDF answers "is this worth $1.99?" far
   better than any amount of copy can.

   SIZED UP after the first version was scrolled straight past: it was 460px
   on a 1100px-wide screenshot, covering 3% of it, and read as a tooltip
   rather than an offer.

   Frosted glass matching the persistent nav (same gradient, blur and inset
   highlight), with one addition: a dark scrim sits UNDER the blur. Over
   dense report text, blur alone leaves the label fighting what is behind it.

   The WRAPPER exists because of the glow. backdrop-filter makes the card its
   own stacking context, so a `::before` at z-index -1 inside it would be
   hidden behind the card's own background. Giving the glow a sibling layer
   inside a shared wrapper sidesteps that entirely, and keeps the anchor a
   plain <a download> – working with JS off and keyboard reachable. */
@property --ss-angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}
/* 2026-09-06: the wash is driven by a registered <percentage> rather than by
   animating mask-position. mask-position was measured SNAPPING straight from
   0% to 100% instead of interpolating – the reveal happened in one frame and
   read as a flash, not a wash. A registered custom property interpolates
   reliably, and dropping it into the gradient's colour stops moves the soft
   edge smoothly. */
@property --ss-wash {
  syntax: '<percentage>';
  initial-value: 100%;
  inherits: false;
}
.showcase-sample-wrap {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  width: min(68%, 720px);
}
/* TWO glow layers, doing two different jobs.

   .ss-halo – the arrival. A big blurred conic sitting behind the card, which
   is the loud "look here" moment. It blooms, turns once, and fades out.
   Because the card is frosted glass its colour comes through the panel while
   it runs; that is the point for a two-second flare, and it is exactly why
   it must not stay.

   .ss-glow – the perpetual state. The same conic masked down to the stroke
   using the two-mask exclusion trick: the element is padded by the stroke
   thickness, then masked by two copies of itself, one clipped to the content
   box and one to the border box, composited to EXCLUDE. What survives is a
   ring exactly `padding` wide on the same rounded corners, so colour travels
   around the edge with nothing behind the panel.

   They are separate elements because one must be masked and the other must
   not. --ss-angle is declared `inherits: false`, so each element carries its
   own copy and the two rotations cannot interfere. */
.ss-halo {
  position: absolute;
  /* 2026-09-06, third attempt at this, and the previous two are instructive.

     A blurred SOLID RECTANGLE keeps its silhouette however hard you blur –
     the interior stays opaque right up to the blur band, so it reads as a
     coloured shape, not light. Fading the alpha radially fixed that, but
     introduced a new problem: a radial gradient's stops are proportional to
     each radius, and this card is 720x150. The same percentage therefore
     means a very different DISTANCE horizontally and vertically, so the glow
     sat tight to the sides and far off the top and bottom.

     The fix is to stop inventing a shape at all. ::before is set to exactly
     the card's box and radius, and blurred – a Gaussian spreads by the same
     amount in every direction, so a blurred copy of the card's own rounded
     rectangle produces an even halo on all four sides by construction.

     The parent stays oversized purely to host the wash mask: a mask clips at
     its element's box, so if the mask lived on the blurred layer it would
     shear the spill off. -80px is comfortably past the ~60px the blur
     actually reaches. */
  inset: -80px;
  opacity: 0;
  pointer-events: none;
  /* The wash. A soft-edged sweep whose solid end starts beyond the right
     edge and travels off the left, so the lit area grows right-to-left.
     A gradient mask rather than clip-path: clip gives a hard vertical edge,
     which is a wipe, not light washing across. */
  -webkit-mask-image: linear-gradient(to right, transparent calc(var(--ss-wash) - 38%), #000 var(--ss-wash));
  mask-image: linear-gradient(to right, transparent calc(var(--ss-wash) - 38%), #000 var(--ss-wash));
}
.ss-halo::before {
  content: '';
  position: absolute;
  /* exactly the card: 80px back in from the oversized parent */
  inset: 80px;
  border-radius: 24px;
  background: conic-gradient(from var(--ss-angle),
    #FF8A3D, #FF3D77, #A259FF, #3DD9FF, #A259FF, #FF3D77, #FF8A3D);
  filter: blur(38px);
}

.ss-glow {
  position: absolute;
  inset: -1.5px;
  border-radius: 26px;
  padding: 2.5px;
  background: conic-gradient(from var(--ss-angle),
    #FF8A3D, #FF3D77, #A259FF, #3DD9FF, #A259FF, #FF3D77, #FF8A3D);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  filter: blur(1.4px);
  opacity: 0;
  pointer-events: none;
}

/* .is-in is TOGGLED by the observer rather than added once, so leaving the
   card and returning – from either scroll direction – replays the arrival. */
/* Three animations, each owning a different property so none can fight:
   the wash (mask position), the turn (conic angle) and the fade (opacity).
   The wash is the shortest – it lands well before the halo starts dying, so
   the card is fully lit for a beat rather than fading out mid-sweep. */
.showcase-sample-wrap.is-in .ss-halo {
  animation: ssHaloWash 0.7s ease-out both,
             ssHaloTurn 1.8s linear both,
             ssHaloFade 1.8s ease-out both;
}
@keyframes ssHaloWash {
  /* The solid end of the gradient starts past the right edge and travels off
     the left, so the lit region grows right-to-left. The 38% soft ramp ahead
     of it is what makes it a wash rather than a wipe. */
  from { --ss-wash: 104%; }
  to   { --ss-wash: -42%; }
}
/* The ring waits for the halo, then runs for good. Faster than the halo
   (2.6s a turn against 2.8s for a single pass) because a thin stroke needs
   more speed to read as movement – the same angular rate that looks lively
   on a 700px halo looks static on a 2.5px line. */
.showcase-sample-wrap.is-in .ss-glow {
  animation: ssRingTurn 2.6s linear 1.35s infinite,
             ssRingIn .6s ease-out 1.35s both;
}
@keyframes ssHaloTurn { to { --ss-angle: 360deg; } }
@keyframes ssHaloFade {
  0%   { opacity: 0; }
  14%  { opacity: .95; }
  62%  { opacity: .9; }
  100% { opacity: 0; }
}
@keyframes ssRingTurn { to { --ss-angle: 360deg; } }
@keyframes ssRingIn { from { opacity: 0; } to { opacity: 1; } }

.showcase-sample {
  position: relative;
  display: flex;
  align-items: center;
  gap: clamp(16px, 2vw, 26px);
  width: 100%;
  padding: clamp(24px, 3.2vw, 40px) clamp(24px, 3.4vw, 44px);
  border-radius: 24px;
  text-decoration: none;
  color: #fff;
  background:
    linear-gradient(135deg, rgba(255,255,255,.16), rgba(255,255,255,.07)),
    rgba(6,12,28,.72);
  border: 1px solid rgba(255,255,255,.22);
  box-shadow: var(--shadow-lg), inset 0 1px 0 rgba(255,255,255,.24);
  backdrop-filter: blur(28px) saturate(145%);
  -webkit-backdrop-filter: blur(28px) saturate(145%);
  transition: transform .2s ease, border-color .2s ease;
}
.showcase-sample:hover,
.showcase-sample:focus-visible {
  transform: scale(1.02);
  border-color: rgba(255,255,255,.4);
}
.showcase-sample .ss-icon {
  flex: none;
  display: grid;
  place-items: center;
  width: clamp(52px, 5.4vw, 72px);
  height: clamp(52px, 5.4vw, 72px);
  border-radius: 16px;
  background: rgba(162,89,255,.24);
  color: #C6A6FF;
}
.showcase-sample .ss-icon svg { width: 50%; height: 50%; }
.showcase-sample .ss-text { display: grid; gap: 6px; min-width: 0; }
.showcase-sample strong { font-size: clamp(1.15rem, 1.7vw, 1.6rem); line-height: 1.15; letter-spacing: -.01em; }
.showcase-sample small { font-size: clamp(.9rem, 1.05vw, 1.02rem); line-height: 1.4; color: rgba(255,255,255,.82); }
@media (max-width: 640px) {
  .showcase-sample-wrap { width: 88%; }
  .showcase-sample { gap: 14px; padding: 20px 18px; border-radius: 18px; }
  .ss-glow { border-radius: 24px; }
  .showcase-sample .ss-icon { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  /* No sweep. A soft static halo stays so the card is still picked out from
     the screenshot behind it, but nothing moves or rotates. */
  /* Static halo: present so the card is still picked out, but neither
     blooming nor travelling. */
  /* No bloom, no travel. The ring is simply present so the card is still
     picked out from the screenshot; the halo never appears at all. */
  .showcase-sample-wrap .ss-halo { display: none; }
  .showcase-sample-wrap .ss-glow { animation: none; opacity: .85; }
}
@media print { .showcase-sample-wrap { display: none; } }

.showcase-frame::before {
  content: '';
  position: absolute;
  inset: -6%;
  z-index: -1;
  pointer-events: none;
  /* 2026-09-03 (much smaller + softer, second pass): still reading as
     too present even after the first softening pass (more gradient
     stops + a moderate blur). Two changes together this time rather
     than pushing blur alone further, which mostly just smears a big
     shape rather than making it feel small: inset pulled way in
     (-32% -> -6%, so the glow's own footprint barely extends past the
     frame instead of ballooning out around it) and opacity dropped
     roughly by half at every stop, so what's left is a faint hint of
     color at the very edges rather than a shape in its own right.
     Blur increased again on top of that (28px -> 56px) so even that
     smaller, fainter shape has no discernible boundary of its own. */
  background: radial-gradient(circle,
    rgba(162,89,255,.14) 0%,
    rgba(190,92,205,.09) 25%,
    rgba(220,95,178,.05) 48%,
    rgba(235,97,163,.02) 70%,
    transparent 100%);
  filter: blur(56px);
}
.showcase-image {
  display: block;
  /* 2026-09-02 (rebuilt): was width: min(1100px, 94vw) with a separate
     transform: scale() for the entrance enlargement. Despite three
     rounds of fixes (border → inset box-shadow, will-change, then
     backface-visibility/translateZ as a defensive addition), a corner
     artifact kept being reported – border-radius clipping not perfectly
     tracking a continuously-changing transform:scale() every frame is a
     genuinely fragile combination across browsers, and no amount of
     compositing hints fully eliminates that class of issue. Removing
     transform entirely removes the risk itself rather than continuing to
     patch around it: width now directly encodes the same 1x-to-1.38x
     range that scale() used to, so the browser is just laying out a
     normally-sized (if larger) box each frame – border-radius and
     box-shadow are computed fresh against that real, current size, with
     no second rendering system (the transform/compositing layer) that
     could fall out of sync with it. */
  /* 2026-09-02 (size change reduced): .38 -> .15, per request – a
     smaller entrance-to-settled size swing (was 138% down to 100%, now
     115% down to 100%) on the chance that a smaller magnitude of change
     reduces whatever's producing the still-reported corner artifact.
     Framed differently by request as "start at ~85%, end at ~75%" of
     some reference size – this is the same idea (a much narrower range
     than before), just expressed relative to the settled width being
     100% rather than a separate percentage scale.
     2026-09-02 (increased again): .15 -> .30 (130% down to 100%), per
     request for a more dramatic entrance now that the corner artifact
     is confirmed fixed (traced to the fixed-size image itself – see the
     removed-stroke and mat-frame history below – which doesn't depend
     on how large this multiplier is). Settled size unchanged; only how
     much larger the image starts. */
  width: calc(min(1100px, 94vw) * (1 + .30 * (1 - var(--showcase-progress, 1))));
  height: auto;
  /* 2026-09-02 (removed): the inset white stroke (a border, then an
     inset box-shadow after that turned out to have its own artifact –
     see history above) kept being reported as a white corner artifact
     even after the transform-vs-width fix, and even at a fully settled,
     non-animating state where there's no continuously-changing property
     left for anything to fall out of sync with. Checked this
     environment's own rendering pixel by pixel and found nothing above
     single-digit noise, meaning this is very likely a Safari/WebKit-
     specific difference in how inset box-shadow clips against
     border-radius that can't be reproduced or debugged further from
     here. It was the only white-colored property left on this element,
     so removing it removes the most plausible remaining source rather
     than continuing to guess at more defensive fixes for the same
     effect. The section's own navy background (see .preview-section)
     still gives the image enough contrast to read as a distinct object
     without it. */
  /* 2026-09-02 (removed again, with evidence this time): the previous
     removal was reverted because a static screenshot's "white corner"
     turned out to be the browser window's own shadow, unrelated to this
     element. But a follow-up screen recording, examined at native
     resolution (frames extracted directly from the video rather than
     eyeballing playback), shows a genuine crescent-shaped white artifact
     at the image's corner during the entrance transition – confirmed
     directly, not inferred. That recording was made with this inset
     stroke back in place, and it remains the only white-colored property
     on this element, making it the strongest remaining candidate.
     Removed it and rebuilt the "stands out" effect as .showcase-frame
     above (a background-color mat, no box-shadow at all) instead.
     2026-09-02 (still happening – border-radius/box-shadow now flat):
     a THIRD recording, confirmed on Edge (Blink engine) rather than
     Safari (WebKit), still showed the identical crescent artifact
     against this exact mat-wrapper build – ruling out both the inset
     stroke specifically and any one browser engine's own quirk. The one
     thing every attempt so far has shared: border-radius and box-shadow
     both continuously recalculating from --showcase-progress every
     frame, alongside width, on a real <img> element. Flattening both to
     constant values isolates width as the only property still animating
     – if the artifact persists, it points at width/img-decoding itself;
     if it's gone, the interaction between multiple simultaneously-
     animated properties was the actual cause all along, not any single
     one of them in isolation. */
  border-radius: 28px;
  box-shadow: 0 60px 140px rgba(6, 10, 25, .35);
}
@media (max-width: 980px) {
  /* Same formula as the base rule, just with 640px swapped in as the
     settled-size base – was a plain static width override, which broke
     entirely now that the entrance scale factor lives inside width
     itself rather than a separate transform. */
  .showcase-image { width: calc(min(640px, 94vw) * (1 + .30 * (1 - var(--showcase-progress, 1)))); }
}
@media (max-width: 640px) {
  /* Same reasoning as before: not enough room on a screen this narrow for
     "large" and "settled" to read as meaningfully different sizes, so the
     image just sits at its normal size with no animation at all.
     showcase-frame also needs width:100% here (not its usual
     fit-content) – the image itself goes to width:100% at this
     breakpoint, and a fit-content parent has no intrinsic size for a
     100%-width child to resolve against, which would collapse both to
     zero. */
  .showcase-frame { width: 100%; border-radius: 21px; }
  .showcase-image { width: 100%; max-width: 100%; border-radius: 20px; box-shadow: 0 30px 70px rgba(6,10,25,.28); }
}
@media (prefers-reduced-motion: reduce) {
  .showcase-frame { border-radius: 29px; }
  .showcase-image { width: min(1100px, 94vw); border-radius: 28px; box-shadow: 0 60px 140px rgba(6,10,25,.35); }
}

/* ====================================================================
   Methodology preview section – the six-card grid beneath the hero
   reveals via a clip-path stage (--methodology-grid-reveal, see
   .methodology-grid-clip further down), matching the "garage door"
   style grid reveal referenced from adobe.com's "Tools that work for
   you" section.
   2026-09-03: restructured per request – image moved from below the
   heading to a full-bleed backdrop behind it (text overlaid on top,
   image opacity reduced for legibility, bottom gradient lengthened to
   blend further into the page background). Full-bleed technique copied
   from .framework-section.section-shell (same width:100vw + negative-
   left-margin approach), with .methodology-hero-content and
   .methodology-inner re-constraining their own width via their own
   .section-shell class, same as .framework-inner does inside
   .framework-section.
   2026-09-03 (hero reveal removed entirely): per direct feedback with
   two side-by-side comparison videos – the JS-driven opacity+translate
   fade this used to have was fundamentally the wrong mechanic, not
   just mistimed. adobe.com's own reference recording, pixel-measured
   directly, shows the hero-equivalent content there is always fully
   opaque and never moves at all – what LOOKS like a reveal is actually
   the *previous* section (a static, opaque panel with genuinely rounded
   bottom corners, confirmed by measuring where white-to-dark transition
   points shift near the corner vs. staying flat further from it)
   scrolling away during perfectly ordinary scrolling, with its curved
   corners uncovering what's behind it. No JS, no scroll-linked
   animation on this element at all now – see .preview-section's own
   border-radius comment for where the actual effect lives. */
.methodology-section.section-shell {
  width: 100vw;
  left: calc(50% - 50vw);
}
.methodology-section {
  padding-top: 94px;
  padding-bottom: clamp(90px, 10vw, 150px);
  /* 2026-09-05: establishes the positioning context for .methodology-bg
     below – see that rule for the full explanation of this rebuild. */
  position: relative;
}
/* 2026-09-05, COMPLETE REBUILD of how the controller image extends behind
   the card grid. Two earlier attempts at this both failed live: the first
   grew .methodology-hero itself and pulled the card grid up with a
   negative margin, which requires correctly guessing BOTH where the text
   ends AND where the cards begin – got that wrong, and the two started
   overlapping. Also created a second, conflicting .methodology-hero-content
   rule in the process (a real bug, not just an imprecise value – the two
   rules' properties combined into a much larger downward shift than either
   one specified alone).

   This version doesn't predict either position at all. The image and its
   fade are now their own layer, absolutely positioned against
   .methodology-section itself (not the hero), with a fixed height that
   extends well past the hero's natural, content-driven height and into
   the card grid area. The hero text and the card grid both stay in
   completely ordinary document flow – neither is resized, repositioned,
   or shifted for this at all, so there's no way for them to collide with
   each other as a side effect of this change. The "peek behind the cards"
   effect comes entirely from this background layer being tall enough to
   still be underneath whatever ends up there, whatever height the text
   and cards naturally come out to. */
.methodology-bg {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: clamp(520px, 52vw, 640px);
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}

.methodology-hero {
  position: relative;
  z-index: 1;
  display: flex;
  /* 2026-09-06: was `center`. The copy sat high in the band – 89px above it,
     138px below at 1440px – reading as a block floating rather than sitting
     on anything, and wasting the top of the artwork.
     Anchored to the bottom instead of centred because the band's height is
     pinned by a min-height !important further down this file, so with
     `center` the content is centred inside a fixed box and every padding
     change moves it only half as far, which makes the spacing awkward to
     reason about. With flex-end the gap beneath the copy IS padding-bottom,
     stated once and directly. */
  align-items: flex-end;
  justify-content: center;
  min-height: clamp(190px, 20vw, 260px);
  padding-top: 16px;
  padding-bottom: clamp(30px, 3.2vw, 46px);
}
.methodology-hero-content {
  position: relative;
  z-index: 1;
}
.methodology-hero-image {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* "Drop the image opacity slightly" – dims it enough for the overlaid
     white heading text to stay comfortably legible without losing the
     photo entirely. */
  opacity: .62;
  /* 2026-09-03: the parallax translate this used to have is gone –
     measured directly (tracking a specific high-contrast feature within
     the image itself against the headline's own movement, frame by
     frame) and found a literal 1:1 ratio in the actual recording: zero
     effective parallax despite the CSS variable being set correctly,
     which is what the "still static" feedback was catching. Went back
     to the Adobe reference with the same frame-tracking approach and
     found the real mechanic isn't a scroll-speed reduction at all – the
     revealed content doesn't move (or move slower); it's completely
     motionless for a multi-second stretch while the *previous* section
     scrolls beneath it, which is what position:sticky produces natively.
     That's now implemented on .preview-section itself (see
     .preview-sticky-wrapper below) instead of here. */
}
.methodology-hero-fade {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 1;
  pointer-events: none;
  /* 2026-09-05, found the ACTUAL bug via pixel sampling on an actual
     render, not by reasoning about percentages again: the previous 4-stop
     curve (0/35/65/100% at alpha 0/.25/.6/1) left the image essentially
     unfaded – visually indistinguishable from full brightness – for
     roughly its first two-thirds, then compressed all of the real
     darkening into the last third. That reads exactly like a hard line
     at whatever point the alpha finally climbs enough to matter,
     regardless of how much physical distance the gradient spans, which
     is why stretching it taller last round didn't fix anything. This
     version starts darkening almost immediately and adds more
     intermediate stops so the increase itself is gradual throughout,
     not backloaded. Also now covers the full height of .methodology-bg
     (was 82%, leaving a fully-unfaded band at the top) – same color
     throughout (#070915, matches the page's dominant background tone). */
  height: 100%;
  background: linear-gradient(to bottom,
    transparent 0%,
    rgba(7,9,21,.12) 15%,
    rgba(7,9,21,.30) 32%,
    rgba(7,9,21,.50) 48%,
    rgba(7,9,21,.68) 62%,
    rgba(7,9,21,.84) 76%,
    rgba(7,9,21,.95) 88%,
    #070915 100%);
}
.methodology-hero-content .section-heading h2,
.methodology-hero-content .section-heading p {
  /* Extra insurance alongside the image's own reduced opacity above –
     keeps the text readable even over whatever the brightest part of
     the photo happens to sit behind at a given viewport width. */
  text-shadow: 0 2px 20px rgba(0,0,0,.4);
}
.methodology-inner {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.methodology-grid-clip {
  width: 100%;
  max-width: 1100px;
}
.methodology-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  width: 100%;
}
.methodology-card {
  background: #2A3850; /* same neutral slate-blue as the Four Areas cards at rest */
  border: 1px solid rgba(255,255,255,.08);
  border-radius: 20px;
  padding: clamp(22px, 2.4vw, 30px);
  /* 2026-09-03: simple fade+slide-in, triggered by .methodology-grid.is-in
     (added via IntersectionObserver in app.js once the grid scrolls into
     view) rather than the previous scroll-position-driven clip-path,
     which turned out not to match what adobe.com's own cards actually do
     (confirmed via frame-by-frame video analysis: no special animation
     there at all, just normal scroll reveal) and added a layer of scroll
     math this page didn't need. Per-card transition-delay below gives a
     modest cascade across the six cards rather than all firing at once. */
  opacity: 0;
  transform: translateY(28px);
  transition: opacity .6s ease, transform .6s ease, background-color .25s ease, border-color .25s ease;
}
.methodology-grid.is-in .methodology-card {
  opacity: 1;
  transform: translateY(0);
}
/* 2026-09-03: hover state – purple fill (site's own --brand-purple, same
   token used for active nav dots and report kicker accents elsewhere),
   with a slight scale-up and brightened body text for contrast against
   the new fill. Also on :focus-within so keyboard users get the same
   feedback tabbing through, not just mouse hover.
   Specificity note: has to be written as
   .methodology-grid.is-in .methodology-card:hover, not the shorter
   .methodology-card:hover, because .methodology-grid.is-in
   .methodology-card (the entrance animation's settled state, three
   classes) outranks a plain two-class hover selector and would silently
   win the transform property, leaving hover with no visible scale at
   all – confirmed this is the same specificity trap documented
   elsewhere on this page for the framework cards. */
.methodology-grid.is-in .methodology-card:hover,
.methodology-grid.is-in .methodology-card:focus-within {
  background: var(--brand-purple);
  border-color: var(--brand-purple);
  transform: translateY(0) scale(1.035);
}
.methodology-grid.is-in .methodology-card:hover p,
.methodology-grid.is-in .methodology-card:focus-within p {
  color: #fff;
}
.methodology-card:nth-child(1) { transition-delay: 0ms; }
.methodology-card:nth-child(2) { transition-delay: 70ms; }
.methodology-card:nth-child(3) { transition-delay: 140ms; }
.methodology-card:nth-child(4) { transition-delay: 210ms; }
.methodology-card:nth-child(5) { transition-delay: 280ms; }
.methodology-card:nth-child(6) { transition-delay: 350ms; }
.methodology-card h3 {
  margin: 0 0 10px;
  font-size: 1.12rem;
  font-weight: 700;
  letter-spacing: -.01em;
  color: #fff;
}
.methodology-card p {
  margin: 0;
  font-size: .94rem;
  line-height: 1.58;
  color: rgba(255,255,255,.66);
  transition: color .25s ease;
}
@media (max-width: 900px) {
  .methodology-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) {
  .methodology-grid { grid-template-columns: 1fr; }
}
@media (prefers-reduced-motion: reduce) {
  .methodology-card { opacity: 1; transform: none; transition: none; }
}

.faq-section { padding: 40px 0 clamp(90px, 10vw, 150px); }
.faq-section .section-heading.centered { max-width: 700px; }
.faq-list {
  display: grid;
  gap: 14px;
  max-width: 1100px;
  margin: 0 auto;
}
.faq-item {
  background: #2A3850; /* same neutral slate-blue as the methodology/Four Areas cards */
  border: 1px solid rgba(255,255,255,.08);
  border-radius: 16px;
  overflow: hidden;
}
.faq-question {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 20px clamp(20px, 2.5vw, 28px);
  cursor: pointer;
  list-style: none;
  font-size: 1.02rem;
  font-weight: 700;
  color: #fff;
  letter-spacing: -.01em;
  transition: color .18s ease;
}
.faq-question::-webkit-details-marker { display: none; }
.faq-question::marker { content: ''; }
.faq-question:hover { color: var(--brand-purple); }
.faq-question:focus-visible {
  outline: 2px solid var(--brand-purple);
  outline-offset: -2px;
}
.faq-question svg {
  flex: none;
  width: 20px;
  height: 20px;
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition: transform .25s ease;
}
.faq-item[open] .faq-question svg { transform: rotate(180deg); }
.faq-item[open] .faq-question { color: var(--brand-purple); }
.faq-answer {
  padding: 0 clamp(20px, 2.5vw, 28px) 22px;
  font-size: .96rem;
  line-height: 1.6;
  color: rgba(255,255,255,.66);
}
.faq-answer p { margin: 0; }
@media (max-width: 620px) {
  .faq-question { font-size: .96rem; padding: 16px 18px; }
  .faq-answer { padding: 0 18px 18px; }
}

.preview-window { display: grid; grid-template-columns: 300px 1fr; min-height: 690px; overflow: hidden; border-radius: 34px; background: rgba(255,255,255,.84); border-color: rgba(255,255,255,.75); box-shadow: 0 40px 100px rgba(6,27,58,.18); }
.preview-sidebar { background: linear-gradient(180deg, #061B3A, #0B2D59); padding: 25px; color: white; display: flex; flex-direction: column; }
.demo-cover { aspect-ratio: 4 / 4.9; border-radius: 22px; padding: 24px; display: flex; flex-direction: column; justify-content: flex-end; background: radial-gradient(circle at 25% 18%, rgba(53,212,232,.85), transparent 28%), linear-gradient(150deg, #0A84FF, #061B3A 58%, #22C7B8); box-shadow: inset 0 0 0 1px rgba(255,255,255,.18), 0 20px 40px rgba(0,0,0,.2); }
.demo-cover span { font-size: .78rem; letter-spacing: .14em; }
.demo-cover strong { font-size: 2rem; line-height: .95; margin: 10px 0; }
.demo-cover small { color: rgba(255,255,255,.58); }
.preview-meta { padding: 24px 3px 18px; }
.preview-meta > small { color: var(--cyan); text-transform: uppercase; letter-spacing: .12em; font-weight: 800; font-size: .68rem; }
.preview-meta h3 { font-size: 1.13rem; line-height: 1.3; margin: 8px 0 20px; }
.preview-meta dl { display: grid; gap: 12px; margin: 0; }
.preview-meta dl > div { display: flex; justify-content: space-between; gap: 10px; font-size: .76rem; border-bottom: 1px solid rgba(255,255,255,.08); padding-bottom: 9px; }
.preview-meta dt { color: rgba(255,255,255,.44); }
.preview-meta dd { margin: 0; text-align: right; }
.preview-sidebar .button { margin-top: auto; }
.preview-main { min-width: 0; background: transparent; }
.preview-tabs { min-height: 70px; display: flex; align-items: center; gap: 4px; padding: 0 26px; border-bottom: 1px solid var(--line); overflow-x: auto; }
.preview-tabs span { white-space: nowrap; padding: 11px 12px; color: var(--muted); font-size: .78rem; font-weight: 700; border-radius: 11px; }
.preview-tabs span.active { color: var(--blue); background: rgba(10,132,255,.09); }
.preview-content { padding: 34px; }
.preview-content-head { display: flex; justify-content: space-between; gap: 20px; align-items: center; }
.preview-content-head small { color: var(--blue); text-transform: uppercase; letter-spacing: .12em; font-weight: 800; font-size: .68rem; }
.preview-content-head h3 { margin: 4px 0 0; font-size: 2.3rem; letter-spacing: -.04em; color: var(--navy-900); }
.status-pill { display: inline-flex; align-items: center; gap: 7px; border: 1px solid #D7E6F1; background: #F3F8FB; padding: 8px 11px; border-radius: 999px; font-size: .73rem; color: var(--slate); }
.status-pill i { width: 7px; height: 7px; border-radius: 50%; background: var(--teal); box-shadow: 0 0 0 4px rgba(34,199,184,.12); }
.preview-content > p { color: var(--slate); max-width: 900px; }
.preview-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 11px; margin: 28px 0; }
.preview-stats > div { background: var(--surface-soft); border: 1px solid #E8EEF4; border-radius: 18px; padding: 18px; }
.preview-stats svg { color: var(--blue); }
.preview-stats strong { display: block; margin-top: 15px; font-size: 1.35rem; color: var(--navy-900); }
.preview-stats small { color: var(--slate); }
.preview-category-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.mini-category { min-height: 180px; padding: 18px; border-radius: 20px; display: flex; flex-direction: column; color: white; }
.mini-category span { width: 34px; height: 34px; display: grid; place-items: center; border-radius: 10px; background: rgba(255,255,255,.18); font-weight: 900; }
.mini-category strong { margin-top: auto; line-height: 1.2; }
.mini-category small { margin-top: 7px; color: rgba(255,255,255,.65); }
.mini-a { background: linear-gradient(145deg, #1685F8, #0757CC); }
.mini-b { background: linear-gradient(145deg, #2BC4B6, #087E78); }
.mini-c { background: linear-gradient(145deg, #F5A623, #D76517); }
.mini-d { background: linear-gradient(145deg, #B65CFA, #7A2BC4); }

.report-app { padding: 70px 0 100px; }
.report-shell { display: grid; grid-template-columns: 300px minmax(0, 1fr); align-items: start; gap: 20px; }
/* 2026-09-02: belt-and-suspenders for top alignment – align-items:start
   on the parent grid should already guarantee this, but every fix so far
   has been "confirmed correct in code, reported misaligned anyway,"
   which smells like something environment-dependent (most likely: Inter
   was declared everywhere in this file but never actually requested from
   Google Fonts until just now, so every visitor was silently rendering
   in whatever their OS substitutes instead – see index.html). Setting
   align-self directly on each of the three top-level pieces means their
   own top alignment no longer depends on the parent's align-items alone. */
.report-sidebar, .report-main > .report-content, .report-main > .report-nav {
  align-self: start;
}
/* Radius standardized to 16px, 2026-09-01: matches .header-shell (line ~327)
   and .hero-photo's top corners – the nav bar / "section tops" reference
   the request asked for. report-sidebar/cover/main/nav were each carrying
   their own original one-off value (28/20/30/20px) from before that
   standard existed and were never brought in line. */
.report-sidebar { position: sticky; top: 112px; padding: 20px; border-radius: 16px; color: white; background: linear-gradient(180deg, #061B3A, #092B58); box-shadow: var(--shadow-md); }
.report-cover { aspect-ratio: 4 / 4.8; border-radius: 16px; overflow: hidden; display: flex; flex-direction: column; justify-content: flex-end; padding: 21px; color: white; background: var(--cover, linear-gradient(145deg,#22C7B8,#061B3A)); position: relative; }
/* ::before (a radial highlight + bottom-darkening scrim) removed
   2026-09-01: existed to keep overlaid title text legible, and that text
   was removed a few rounds back (both the synchronous render and the
   async IGDB path – see app.js). With nothing left to protect, it was
   just dimming and tinting real box art for no reason. Per request: the
   art should show in its actual, unaltered color. */
.report-cover > * { position: relative; }
.report-cover img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* .cover-overlay no longer exists in the DOM as of 2026-09-01 – app.js
   stopped creating it (both render paths) once the art no longer needed
   a scrim for text legibility. This rule and the other .cover-overlay
   rules below (~2802, ~2843, ~2852) are dead code now, kept rather than
   deleted in case an overlay is ever needed again for some other reason. */
.report-cover img + .cover-overlay { position: absolute; inset: 0; background: linear-gradient(0deg, rgba(2,9,19,.88), rgba(2,9,19,.02) 65%); }
.report-cover small { text-transform: uppercase; letter-spacing: .12em; font-weight: 800; color: var(--cyan); }
.report-cover strong { font-size: 1.7rem; line-height: 1.02; margin-top: 8px; }
.report-game-info { padding: 21px 2px 10px; }
.report-game-info h2 { font-size: 1.16rem; line-height: 1.27; margin: 0 0 18px; }
.report-game-info dl { display: grid; gap: 10px; margin: 0; }
.report-game-info dl > div { display: grid; grid-template-columns: 86px 1fr; gap: 10px; font-size: .76rem; padding-bottom: 9px; border-bottom: 1px solid rgba(255,255,255,.08); }
.report-game-info dt { color: rgba(255,255,255,.43); }
.report-game-info dd { margin: 0; text-align: right; }
.report-mode { display: flex; align-items: start; gap: 8px; margin: 13px 0; padding: 11px; border-radius: 13px; background: rgba(247,216,74,.1); color: #FFF4AD; font-size: .7rem; }
.report-mode svg { flex: 0 0 auto; margin-top: 1px; }
.report-sidebar-actions { display: grid; gap: 8px; margin-top: 14px; }
.report-sidebar .button { width: 100%; }
.report-main { min-width: 0; background: white; border-radius: 16px; box-shadow: var(--shadow-md); overflow: hidden; border: 1px solid #E9EEF5; }
/* .report-nav's original declaration lived here (position/background/
   backdrop-filter/border-bottom) – removed 2026-09-02, fully superseded
   by the consolidated rebuild, see "REPORT SECTIONS NAV – REBUILT" below. */
.report-content {
  /* Named so report-hero (below) can cancel it exactly via negative
     margin, without the two ever being able to drift out of sync. */
  --content-pad: clamp(25px, 4vw, 50px);
  padding: var(--content-pad);
  /* Top zeroed 2026-09-01: this uniform padding put report-hero's card
     ~50px below report-nav's, which only has ~14px above its own content –
     the two boxes read as misaligned even though they share the same grid
     row. report-hero has its own padding now, so nothing here needed the
     extra top gap anyway; left/right/bottom stay as they were. */
  padding-top: 0;
}
.report-hero {
  padding-bottom: 32px; border-bottom: 1px solid var(--line); scroll-margin-top: 120px;
  /* 2026-09-01: the "14px between the 3 elements, like the Four Areas
     cards" fix (--report-rail-gap, ~line 5619) only ever set the grid
     gap itself – it didn't account for report-content's own left/right
     padding stacking on top of that gap, so the real on-screen distance
     from the cover art to this card (and from this card to the TOC
     rail) was 14px of grid gap PLUS up to 50px of report-content
     padding on each side: 64-84px total, not 14px. Pulling report-hero
     out to the true column edge with a negative margin exactly
     cancelling --content-pad fixes the visible gap without touching
     every other section's padding, which still needs it.
     border-radius is repeated here because canceling the padding also
     un-clips the corners inherited from report-content's containing
     box in some browsers. */
  margin-left: calc(-1 * var(--content-pad));
  margin-right: calc(-1 * var(--content-pad));
}
.report-kicker { display: flex; justify-content: space-between; gap: 14px; align-items: center; }
.report-kicker small { color: var(--blue); text-transform: uppercase; letter-spacing: .12em; font-weight: 850; }
.report-hero h1 {
  margin: 10px 0 14px; color: var(--navy-900);
  /* 2026-09-02: matches "Four areas, one complete picture." exactly
     (same clamp() and weight) per request – was clamp(1.9rem,3vw,3rem)
     at the browser's default h1 weight (700/bold); this game's own H1
     shouldn't be visually heavier than page-level section headings. */
  font-size: clamp(2.1rem, 3.6vw, 3.1rem);
  font-weight: 400;
  line-height: 1.03; letter-spacing: -.04em;
}
.report-hero > p { max-width: 950px; color: var(--slate); font-size: 1.05rem; }
.report-date { white-space: nowrap; color: var(--slate); font-size: .76rem; }
.quick-stats { margin-top: 28px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 11px; }
.quick-stat { padding: 18px; border-radius: 18px; background: var(--surface-soft); border: 1px solid #E7EDF4; }
.quick-stat svg { color: var(--blue); }
.quick-stat strong { display: block; margin: 11px 0 1px; color: var(--navy-900); font-size: 1.28rem; }
.quick-stat span { color: var(--slate); font-size: .74rem; }
.report-section { padding: 38px 0; border-bottom: 1px solid var(--line); scroll-margin-top: 170px; }
.report-section:last-child { border-bottom: 0; }
/* 2026-09-04: removes the divider directly above Practical Recommendations
   (minimap-section's own bottom border) and the one directly below it
   (recommendations-section's own bottom border, i.e. the line that sat
   just above Complete Source List) – both by explicit request. Other
   dividers on the page are untouched; only these two were called out. */
#minimap-section, #recommendations-section { border-bottom: 0; }
.report-section-head { display: flex; justify-content: space-between; align-items: start; gap: 22px; margin-bottom: 22px; }
.report-section-head h2 { margin: 0; color: var(--navy-900); font-size: clamp(1.65rem, 2.5vw, 2.55rem); letter-spacing: -.04em; }
.report-section-head p { max-width: 580px; color: var(--slate); margin: 4px 0 0; }
/* 2026-09-02: originally scoped to Game Minimap Breakdown only (comment
   used to read "not every .report-section-head – Sources/Methodology keep
   their own size", a deliberate choice at the time) – two changes per that
   request: matches "Four areas, one complete picture."'s size/weight
   exactly (same clamp() and font-weight:400, ~line 6891), and shifts left
   by the same amount as the realigned category boxes so this heading's
   copy starts at the same x-position as everything else instead of
   report-content's default, unadjusted padding edge.

   2026-09-04: that scoping is reversed by explicit request – Practical
   Recommendations and Complete Source List should match Game Minimap
   Breakdown's copy exactly, and their content should align to the same
   margins. Widened from #minimap-section to every .report-section-head
   (there are only ever three: minimap, recommendations, sources – this
   isn't leaving anything unintended in scope). Kept as one rule rather
   than three copies so a future change to any of these only has one place
   to happen. */
.report-section-head {
  margin-left: calc(-1 * (var(--content-pad) - 28px));
}
.report-section-head h2 {
  /* 2026-09-02: was matched exactly to Four Areas (clamp(2.1rem,3.6vw,
     3.1rem), 49.6px resolved) – now sized halfway between that and the
     site's body copy (.report-hero > p, 1.05rem/16.8px): (49.6+16.8)/2
     = 33.2px, landing this clamp's resolved value at 33.6px. Weight
     stays 400 - unchanged, since both endpoints already share it. */
  font-size: clamp(1.7rem, 2.6vw, 2.1rem);
  font-weight: 400;
  letter-spacing: -.01em;
}
.summary-grid { display: grid; grid-template-columns: 1.05fr .95fr; gap: 15px; }
.insight-card { border: 1px solid #E5EBF2; border-radius: 22px; padding: 24px; background: #FBFCFE; }
.insight-card h3 { margin: 0 0 12px; color: var(--navy-900); font-size: 1.04rem; }
.insight-card ul { padding: 0; margin: 0; list-style: none; display: grid; gap: 10px; }
.insight-card li { display: flex; gap: 9px; color: var(--slate); font-size: .9rem; }
.insight-card li::before { content: ""; flex: 0 0 7px; width: 7px; height: 7px; margin-top: 8px; border-radius: 50%; background: var(--teal); }
.concern-card li::before { background: var(--orange); }
.discussion-card li::before { background: var(--purple); }
.report-category { --cat: var(--blue-a); position: relative; overflow: hidden; padding: 28px; border: 1px solid #E4EAF2; border-radius: 25px; background: linear-gradient(135deg, color-mix(in srgb, var(--cat) 7%, white), white 52%); }
.report-category + .report-category { margin-top: 18px; }
.report-category::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 6px; background: var(--cat); }
.report-category.cat-a { --cat: var(--blue-a); }
.report-category.cat-b { --cat: var(--teal); }
.report-category.cat-c { --cat: var(--orange); }
.report-category.cat-d { --cat: var(--purple); }
.category-heading { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 15px; }
.category-badge { width: 50px; height: 50px; border-radius: 15px; display: grid; place-items: center; background: var(--cat); color: white; font-size: 1.25rem; font-weight: 900; box-shadow: 0 12px 24px color-mix(in srgb, var(--cat) 25%, transparent); }
.category-heading small { color: var(--cat); text-transform: uppercase; letter-spacing: .1em; font-weight: 850; font-size: .68rem; }
.category-heading h3 { margin: 3px 0 0; color: var(--navy-900); font-size: clamp(1.25rem, 2vw, 1.7rem); }
.category-toggle { width: 42px; height: 42px; border-radius: 13px; border: 1px solid #DDE5EF; background: white; display: grid; place-items: center; cursor: pointer; }
.category-toggle svg { transition: transform .24s; }
.report-category.expanded .category-toggle svg { transform: rotate(180deg); }
.category-summary { margin: 17px 0 0 65px; color: var(--slate); }
.category-details { margin: 22px 0 0 65px; display: grid; gap: 0; }

/* FLATTENED, 2026-08-21.
   This was a card, inside a card, inside a card. One category panel therefore
   presented four different left edges – the badge, the topline text, this
   block's own border, and the text inside it – and no two of them lined up.
   Removing the box is the fix: every line in a category now shares a single
   column, and a hairline rule does the separating that a border and a tinted
   background were doing before. Less chrome, and the alignment falls out of
   the structure instead of needing to be maintained. */
.detail-block { padding: 20px 0 0; background: none; border: 0; border-top: 1px solid #E7ECF3; border-radius: 0; }
.detail-block:first-child { padding-top: 0; border-top: 0; }
.detail-block h4 { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; color: var(--navy-900); font-size: .95rem; letter-spacing: .01em; }
.detail-block h4 svg { color: var(--cat); flex: 0 0 auto; }
.detail-block p { margin: 0; color: var(--slate); }
.detail-block ul { margin: 0; padding-left: 20px; color: var(--slate); display: grid; gap: 7px; }

/* A shared label stated once, with its findings beneath, instead of the same
   44 characters repeating at the head of three consecutive bullets. */
.finding-group + .finding-group { margin-top: 13px; }
.finding-group-label {
  margin: 0 0 6px;
  font-size: .78rem;
  font-weight: 800;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--cat);
}
.finding-group .finding-list { padding-left: 20px; }
.finding-list + .finding-group { margin-top: 13px; }
.finding-group + .finding-list { margin-top: 13px; }
/* The gaps block keeps the amber signal but not the box – a tinted card here
   would indent its text past every other line in the category, which is the
   misalignment this pass removed. */
.gap-block h4 { color: #B26A00; }
.gap-block h4 svg { color: var(--orange); }
/* Sources are a reference list, not a set of cards. Boxing each one indented
   its text past the column everything else in the category sits on, and gave
   nine near-identical panels where nine lines would do. */
.source-list { display: grid; gap: 0; }
.source-row { display: grid; grid-template-columns: 1fr auto; gap: 15px; padding: 11px 0; border-radius: 0; background: none; border: 0; border-top: 1px solid #EDF1F6; }
.source-row:first-child { border-top: 0; padding-top: 2px; }
.source-row strong { display: block; color: var(--navy-900); font-size: .93rem; }
.source-row span { display: block; color: var(--slate); font-size: .81rem; margin-top: 2px; }
.source-row small { display: block; color: var(--muted); margin-top: 4px; font-size: .74rem; }
.source-row a { align-self: center; display: inline-grid; place-items: center; width: 32px; height: 32px; border-radius: 9px; color: var(--blue); background: #EDF6FF; }
.disclaimer-box { margin-top: 22px; display: flex; gap: 12px; padding: 18px; border-radius: 17px; background: #FFFBE8; color: #655B22; border: 1px solid #F2E7A4; }
.disclaimer-box svg { flex: 0 0 auto; margin-top: 3px; }
.report-actions-band { display: flex; justify-content: space-between; align-items: center; gap: 20px; padding: 24px; border-radius: 22px; background: linear-gradient(135deg, var(--navy-900), #0B3768); color: white; }
/* 2026-09-05: full-width breakout to match every other section on this
   page (.report-category, .insight-card, .source-list all already do
   this – see .report-category's own comment for the exact reasoning).
   This band was the one section that never got it, which is exactly why
   it read as narrower than everything above it. Same two values, same
   left/right asymmetry (left edge lines up with where the copy above
   starts, right edge matches the outer card edge). */
@media screen {
  .report-actions-band {
    margin-left: calc(-1 * (var(--content-pad) - 28px));
    margin-right: calc(-1 * var(--content-pad));
  }
}
.report-actions-band h3 { margin: 0 0 4px; }
.report-actions-band p { margin: 0; color: rgba(255,255,255,.62); }
/* 2026-09-05: explicit column + right-align, rather than leaving this to
   flex-wrap (row direction wrapping onto two lines because the container
   was narrow) – with wrap alone, a narrower second button (Share) sitting
   under a wider first one (Print or Save PDF) would left-align within
   whatever width the row naturally computed, not necessarily flush with
   the band's own right edge the way the paragraph on the left is flush
   with its left edge. This guarantees both buttons' right edges land at
   the same, band-relative position. */
.report-actions { display: flex; flex-direction: column; align-items: flex-end; gap: 9px; }

.cta-section { padding: 20px 0 110px; }
.cta-card { display: grid; grid-template-columns: auto 1fr auto; gap: 22px; align-items: center; padding: 34px; color: white; border-radius: 30px; background: radial-gradient(circle at 5% 0, rgba(53,212,232,.26), transparent 30%), linear-gradient(135deg, #061B3A, #0A3262); box-shadow: var(--shadow-lg); }
.cta-icon { width: 70px; height: 70px; display: grid; place-items: center; border-radius: 21px; color: var(--cyan); background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.12); }
.cta-icon svg { width: 32px; height: 32px; }
.cta-card h2 { margin: 0 0 6px; font-size: clamp(1.45rem, 2.3vw, 2.35rem); line-height: 1.08; letter-spacing: -.035em; }
.cta-card p { margin: 0; color: rgba(255,255,255,.58); }

.site-footer { background: var(--navy-950); color: white; padding: 70px 0 0; }
/* 2026-09-04: footer-brand widened from 1.5fr so the (now larger) logo
   lockup has real room instead of competing for space with everything
   else in a narrower column – my first instinct here was to equalize all
   five columns to 1fr each, which actually would have SHRUNK this column
   from 1.5fr, the opposite of what "bigger logos" needs. Caught before
   shipping it. If this isn't the width relationship meant by "that whole
   section should be the same width," tell me what it should match and
   I'll adjust the actual ratio rather than guess again. */
/* 2026-09-04: switched from grid (5 equal-width columns) to flex with
   space-between. Equal-width columns are NOT the same thing as "evenly
   spaced" once each column holds a different amount of content – Trust's
   four short words and Built For's wrapped pills fill their equal-width
   tracks completely differently, so the columns were mathematically even
   but never looked it. This instead lets each group size to its own
   natural content width (flex: 0 1 auto on each, below) and distributes
   the leftover space evenly BETWEEN them – like placing five differently-
   sized objects on a shelf with equal gaps, rather than five equal-width
   bins that happen to hold different amounts. flex-wrap stays on as a
   safety net for narrow viewports, though the five groups' combined
   natural width should comfortably fit one row at normal desktop widths. */
.footer-grid { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-start; gap: 34px 24px; }
.footer-brand, .footer-col, .footer-news { flex: 0 1 auto; min-width: 0; }
/* 2026-09-04: equalized from 2fr .9fr .9fr .9fr 1.7fr per explicit
   request ("each of the five sections should be evenly spaced") – safe to
   do now that footer-brand has min-width:0 (below) and the logos wrap
   instead of overflowing if this column ends up narrower than their
   combined natural width. */
/* 2026-09-04, ROOT CAUSE of the logos overflowing past the column: a grid
   track's default minimum width is its content's min-content size, not 0 –
   so once the bigger logos' combined natural width exceeded this column's
   2fr share, the COLUMN grew to fit them instead of .footer-logos wrapping
   the way flex-wrap is supposed to let it. min-width:0 here is the standard
   fix (same pattern already used elsewhere in this file, e.g.
   .report-main > .report-content) – it tells the track it's allowed to
   shrink below its content's natural width, so wrapping can actually kick
   in and the logos stay inside the column instead of pushing past it. */
.footer-brand { min-width: 0; }
.footer-brand img { width: 118px; height: 58px; object-fit: contain; }
/* 2026-09-04: nowrap explicitly requested ("one line underneath") – worth
   flagging honestly that this is the same property I removed two rounds
   ago specifically because it let this text overflow past .footer-brand's
   edge into Product's column. With the logos now sized to actually fit
   their column (see .footer-logos above), there's more room for this text
   too, but at narrow desktop widths it may still slightly overrun the
   column edge rather than wrap. If that reads as a bigger problem than a
   two-line tagline, say so and I'll drop nowrap again. */
.footer-brand p { color: rgba(255,255,255,.5); max-width: 340px; font-size: .88rem; white-space: nowrap; }
.footer-grid > div:not(.footer-brand) { display: flex; flex-direction: column; align-items: start; gap: 9px; }
.footer-grid strong { margin-bottom: 7px; }
.footer-grid button, .footer-grid span { color: rgba(255,255,255,.55); padding: 0; font-size: .84rem; }
.footer-grid button:hover { color: white; }
/* 2026-09-04: switched from flexbox to grid with explicit minmax()
   columns. The flex version (see the two comments this replaces, kept in
   spirit below) tried to solve the squeeze with flex-basis/min-width, and
   that turned out not to be reliable enough in practice – the legal text
   was still getting crushed into 1-2 words per line in the next
   screenshot. Grid's minmax() is a harder guarantee: each side gets AT
   LEAST its minimum before either can take more, not a content-based
   auto-calculation that depends on how long the other side's text
   happens to be. Also now three grid children instead of two nested divs
   worth of flex – .footer-legal and .footer-credits both get real floors. */
.footer-bottom { margin-top: 48px; padding-top: 20px; display: grid; grid-template-columns: minmax(280px, 1fr) minmax(220px, 1fr); align-items: flex-start; gap: 20px; color: rgba(255,255,255,.35); border-top: 1px solid rgba(255,255,255,.08); font-size: .77rem; }
.footer-legal { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.fb-copyright a { color: inherit; text-decoration: underline; }
.footer-credits { display: flex; flex-direction: column; gap: 4px; min-width: 0; text-align: right; }
.footer-credits a { color: inherit; text-decoration: underline; }

/* 2026-09-04: trust badges – the Health-e-Habits equivalent of adobe.com's
   own "Featured products" row, which sits in this same spot just above
   their copyright bar. Adapted to what this site actually has to say
   instead of a product lineup: what a report costs, and the promises that
   go with that price. */
.footer-trust { margin-top: 44px; padding-top: 28px; border-top: 1px solid rgba(255,255,255,.08); }
.ft-label { display: block; font-size: .74rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: rgba(255,255,255,.4); margin-bottom: 14px; }
.footer-trust-badges { display: flex; flex-wrap: wrap; gap: 12px; }
.ft-badge { display: inline-flex; align-items: center; gap: 8px; padding: 9px 14px; border-radius: 999px; background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.09); font-size: .82rem; font-weight: 600; color: rgba(255,255,255,.75); }
/* 2026-09-04: Built For labels (Families, Schools, ...) get the same pill
   treatment as the Trust & Pricing badges above, by explicit request –
   same padding/radius/background/border/type scale, just no icon (wasn't
   asked for one, and inventing one per audience felt like scope creep). */
/* 2026-09-04, ROOT CAUSE of the pills reading as "touching the copy" – a
   pre-existing rule, .footer-col span { padding: 0; line-height: 1.5 },
   has higher specificity (a class plus a type selector) than a class alone
   like .footer-audience-pill, so it silently won regardless of what
   padding I set here, no matter where in the file this rule sits. Scoped
   under .footer-col explicitly now (two classes: 0,2,0) to outrank it.
   Also gave the pills row its own top margin, matching how .ft-label
   keeps a real gap before .footer-trust-badges below it, rather than
   inheriting .footer-col's tighter 10px row rhythm meant for a plain link
   list. */
.footer-audience-pills { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; margin-top: 6px; }
.footer-col .footer-audience-pill { display: inline-flex; align-items: center; padding: 9px 14px; border-radius: 999px; background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.09); font-size: .82rem; font-weight: 600; line-height: 1; color: rgba(255,255,255,.75); }
.ft-badge svg { width: 15px; height: 15px; color: var(--brand-purple); flex: none; }

/* 2026-09-04: the big closing wordmark, matching adobe.com's own footer.
   Re-confirmed against the reference at 30fps (up from the original 20fps
   pass) specifically to check whether the logo alone locks while the page
   keeps scrolling past it, or whether the whole page simply runs out of
   room. It's the latter: pixel-diffing the frame where the motion first
   stops against the final frame of the clip shows them essentially
   identical (mean channel difference 0.05 out of 255) -- not just the
   logo, the footer links and copyright line above it are frozen too. That
   only happens if scrolling itself has stopped, not if one element is
   pinned while its neighbors keep moving past it. So this needs no
   position:sticky, no fixed positioning, no JS, and no calculated lock
   offset -- all of which were solving a takeover-style problem that isn't
   what's actually happening here.
   The mechanic itself: a plain, normal-flow, completely undistorted image
   (full width, natural aspect ratio, no cropping) sitting inside a
   container that's deliberately SHORTER than the image's own rendered
   height, with overflow:hidden. The container's own short height is what
   the page's total scrollable length is built from (it's the box that
   actually occupies space in normal flow) -- the portion of the image
   below that height exists and renders normally, entirely undistorted,
   but the page simply never has enough scrollable room to bring it into
   view, because scrolling is bounded by the container's own box, not by
   how tall its (clipped) contents happen to be. That produces exactly
   what the reference shows: the logo scrolls up from below completely
   normally, at 1:1 with scroll position, right up until the page hits its
   own natural end -- which happens to land partway through the logo,
   not after it.
   24.5vw specifically: measured directly in the source PNG that the
   "healthe" wordmark occupies the top ~60% of the file's own height and
   the "HABITS" badge sits in the bottom third, with a clean gap between
   them (rows 1628-1924 of 2981 are fully transparent). 24.5vw against the
   image's own natural height (39.86vw at this file's aspect ratio) shows
   the full wordmark plus a couple of pixels teasing the top of "HABITS" --
   legible and intentional-looking, not an arbitrary mid-letter crop, but
   still clearly "cut off" rather than a clean, suspiciously round stop. */
/* 2026-09-04: reverted the scroll-cropped reveal effect described in the
   comment directly above (kept in place, not deleted – the crop math in it
   was correct and whoever revisits this later shouldn't have to re-derive
   it from scratch). Client feedback, live: show the complete logo (both the
   "healthe" wordmark and the "HABITS" badge beneath it, previously clipped
   off), no scroll-tied reveal effect for now. Centered and capped at a sane
   max-width instead of the previous edge-to-edge 100vw, so it reads as a
   signature/brand touch rather than dominating the whole footer.

   Same day, second look: the max-width cap above was a guess at what
   "toned down" should mean and it guessed wrong – the client liked the
   original full screen width, just not the crop/scroll effect. Back to
   edge-to-edge, still uncropped, still no animation. */
/* 2026-09-06: fade-and-rise, replacing the scroll-tied clip reveal.

   The clip version was measured carefully against the Adobe reference and
   opened correctly here in testing, but did not read on the real site, the
   same as the methodology garage door. Both attempts were scroll-position
   maths layered onto a page that already has a working entrance pattern, so
   this now uses THAT pattern instead of a third bespoke one: the same
   fade + translateY(28px) the methodology cards use, triggered by an
   IntersectionObserver adding .is-in.

   Matching the existing mechanism matters more than matching Adobe. It is
   one behaviour to understand, it already works on this page, and it fails
   safe: no JS means the wordmark simply shows, because .is-in is only ever
   additive. */
.footer-wordmark {
  display: flex;
  justify-content: center;
  margin-top: 56px;
  width: 100%;
}
.footer-wordmark img {
  display: block;
  width: 100%;
  height: auto;
  opacity: 0;
  transform: translateY(28px);
  transition: opacity .6s ease, transform .6s ease;
}
.footer-wordmark.is-in img {
  opacity: 1;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  /* Shown at once, no movement. The logo is content; the entrance is not. */
  .footer-wordmark img { opacity: 1; transform: none; transition: none; }
}

.modal-backdrop, .analysis-overlay { position: fixed; inset: 0; z-index: 100; padding: 24px; display: grid; place-items: center; background: rgba(2,9,19,.72); backdrop-filter: blur(18px); }
.modal { width: min(720px, 100%); max-height: min(850px, calc(100vh - 48px)); overflow: auto; border-radius: 28px; padding: 26px; background: white; box-shadow: 0 40px 120px rgba(0,0,0,.42); }
.modal-wide { width: min(1120px, 100%); }
.modal-header { display: flex; align-items: start; justify-content: space-between; gap: 20px; }
.modal-header small { color: var(--blue); text-transform: uppercase; letter-spacing: .12em; font-weight: 850; font-size: .7rem; }
.modal-header h2 { margin: 5px 0 0; color: var(--navy-900); font-size: 2rem; letter-spacing: -.04em; }
.modal .icon-button { color: var(--navy-900); background: var(--surface-soft); border-color: #E1E7EF; }
.modal-intro { color: var(--slate); }
.modal-content { color: var(--slate); }
.modal-content h3 { color: var(--navy-900); margin-top: 22px; }
.modal-content ul { padding-left: 20px; }
.modal-content .domain-cloud { display: flex; flex-wrap: wrap; gap: 7px; }
.modal-content .domain-cloud span { padding: 7px 10px; border-radius: 999px; background: #EDF6FF; color: #0757CC; font-size: .78rem; }
.modal-footer { margin-top: 24px; padding-top: 18px; border-top: 1px solid var(--line); display: flex; justify-content: flex-end; gap: 10px; }
.game-match-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 13px; margin-top: 22px; }
.game-match { position: relative; overflow: hidden; border: 2px solid transparent; border-radius: 21px; background: var(--surface-soft); cursor: pointer; text-align: left; padding: 0; transition: transform .2s, border-color .2s, box-shadow .2s; }
.game-match:hover { transform: translateY(-3px); box-shadow: var(--shadow-sm); }
.game-match.selected { border-color: var(--blue); box-shadow: 0 0 0 4px rgba(10,132,255,.11), var(--shadow-sm); }
.game-match-cover { aspect-ratio: 16/8.5; display: grid; place-items: center; overflow: hidden; color: white; background: linear-gradient(145deg, var(--match-accent, var(--blue)), var(--navy-900)); }
.game-match-cover img { width: 100%; height: 100%; object-fit: cover; }
.game-match-cover span { font-size: 2rem; font-weight: 900; opacity: .82; }
.game-match-body { padding: 16px; }
.game-match-body strong { display: block; color: var(--navy-900); line-height: 1.25; }
.game-match-body small { display: block; color: var(--slate); margin-top: 6px; }
.game-match-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 11px; }
.game-match-tags span { padding: 5px 7px; border-radius: 8px; color: var(--slate); background: white; border: 1px solid #E3E9F0; font-size: .65rem; }

.analysis-card { width: min(660px, 100%); max-height: calc(100vh - 40px); max-height: calc(100dvh - 40px); display: flex; flex-direction: column; padding: 28px 32px; border-radius: 30px; text-align: center; color: white; background: rgba(5,25,54,.9); animation: analysis-in .55s var(--ease-spring) both; }
/* 2026-09-04: .analysis-orbit below is no longer referenced from index.html
   – replaced by .scan-stage (further down), a magnifying glass sweeping
   over a pixel-art controller. (This comment previously named .run-stage,
   an intermediate runner-and-e-logos version that .scan-stage replaced on
   2026-09-06; the runner's CSS was removed with it rather than left as
   dead weight, since it had no reuse value outside that one concept.)
   .analysis-orbit is left in place rather than deleted: it's a
   complete, working ring animation someone may want again for a different
   spot, and deleting it would lose that for no real benefit (it costs
   nothing sitting unused). */
.analysis-orbit { position: relative; width: 88px; height: 88px; margin: 0 auto 12px; flex: none; display: grid; place-items: center; border-radius: 50%; background: radial-gradient(circle, rgba(53,212,232,.22), transparent 68%); }
.analysis-orbit > svg { width: 26px; height: 26px; color: var(--cyan); }
.analysis-orbit span { position: absolute; inset: 4px; border-radius: 50%; border: 1px solid rgba(255,255,255,.13); animation: orbit-spin 5s linear infinite; }
.analysis-orbit span:nth-child(2) { inset: 15px; border-color: rgba(34,199,184,.34); animation-direction: reverse; animation-duration: 3.6s; }
.analysis-orbit span:nth-child(3) { inset: 26px; border-color: rgba(10,132,255,.4); animation-duration: 2.8s; }
.analysis-orbit span::before { content: ""; position: absolute; width: 8px; height: 8px; border-radius: 50%; top: -5px; left: 50%; background: var(--cyan); box-shadow: 0 0 18px var(--cyan); }
@keyframes orbit-spin { to { transform: rotate(360deg); } }

/* ---- scan-stage: the analysis-card's loading animation ------------------
   2026-09-06, replaces the running-character-and-e-logos version (which
   itself replaced .analysis-orbit): a magnifying glass sweeping back and
   forth over a pixel-art game controller, with the controller swelling
   slightly as the lens passes over it.

   Built from plain positioned shapes rather than an image or a hand-placed
   pixel grid, same as the runner sprite before it and for the same reason:
   it can be checked against a real render and nudged, and it costs no
   extra request.

   ON THE MAGNIFICATION: the honest way to magnify would be a second, larger
   copy of the controller clipped to the lens circle and counter-translated
   so it stays registered with the original underneath. That needs two
   animations kept in perfect sync, and any drift between them reads as a
   broken effect rather than a subtle one. At this size the lens (44px) is
   nearly as wide as the whole controller (52px), so scaling the controller
   as a single unit while the lens is over it is visually equivalent and
   cannot desynchronise – there is only one thing moving. The scale peaks
   are timed to the two moments the lens is centred (see padMagnify).

   PERF: transform only, no filter/blur, consistent with the splash perf
   pass that deliberately stripped filters out of this file. */
.scan-stage { height: 74px; margin: 0 auto 6px; flex: none; }
.scan-track { position: relative; width: 260px; height: 100%; margin: 0 auto; }
/* --- the controller, drawn from the #px-controller pixel sprite --- */
/* The global `svg { fill: none; stroke: currentColor; stroke-width: 1.8 }`
   rule near the top of this file is for the site's line icons. These two
   sprites are filled pixel art, and their rects carry their own fill
   attributes – but with no stroke of their own they inherit that 1.8-unit
   stroke, which on a 1-unit pixel is wide enough to swallow the whole
   sprite into a solid silhouette. Cancelling stroke here leaves the fills
   to do their job. */
.scan-pad, .sg-art { stroke: none; }
.scan-pad {
  position: absolute; left: 50%; top: 50%;
  width: 64px; height: 34px; margin: -17px 0 0 -32px;
}

/* --- the magnifying glass ---
   .scan-glass is a zero-size anchor at the track centre carrying the sweep;
   everything else hangs off it so lens, rim and handle move as one piece.

   TRUE MAGNIFICATION, and why it stays in sync. The nesting composes to
       final = T_glass(t)  ·  S(k)  ·  T_world(-t)
   so a point p of the copy lands at  t + k(p - t)  – which is exactly what
   a lens centred at t with magnification k does: whatever sits at the lens
   centre stays put, everything off-centre is pushed outward by k. The
   earlier worry about two animations drifting apart does not apply to the
   arrangement below, because the SCALE IS STATIC. Only the two translates
   animate, they are exact negations of one another, and they share a
   duration, an easing function and a start time, so the browser derives
   both from the same clock – there is no independent state to fall out of
   step. Both are transform-only and therefore composited the same way, so
   they cannot diverge under load either. */
.scan-glass {
  position: absolute; left: 50%; top: 50%;
  width: 0; height: 0;
  animation: glassSweep 4s ease-in-out infinite;
  z-index: 2;
}
/* Clipping circle. Radius 20px, slightly inside the sprite's own rim so the
   rim covers the cut edge and no seam shows. */
.sg-disc {
  position: absolute; left: -20px; top: -20px;
  width: 40px; height: 40px;
  border-radius: 50%;
  overflow: hidden;
}
/* Static scale about the disc centre, which IS the lens centre. */
.sg-zoom { position: absolute; inset: 0; transform: scale(1.6); transform-origin: 50% 50%; }
/* Counter-translation: cancels the glass's own movement so the copy stays
   pinned to the world, which is what makes the lens look like it is passing
   over a stationary controller rather than dragging one along with it. */
.sg-world { position: absolute; inset: 0; animation: worldCounter 4s ease-in-out infinite; }
/* Positioned so the copy's centre sits exactly on the lens centre when the
   sweep is at zero: the disc's top-left is at lens-local (-20,-20), so
   left -12px puts the copy's 64px-wide centre at -20 + -12 + 32 = 0, and
   top 3px puts its 34px-tall centre at -20 + 3 + 17 = 0. */
.sg-world .scan-pad { left: -12px; top: 3px; margin: 0; }
/* The glass sprite sits on top; its lens fill is translucent, so the
   magnified copy shows through it and picks up the blue tint. Offset by
   0.3654 of its own width because the lens centre is at unit 9.5 of the
   sprite's 26-unit box, not at its middle. */
.sg-art { position: absolute; width: 66px; height: 66px; left: -24.12px; top: -24.12px; }

/* Sweep spans +/-74px: the track is 260px and the controller is 64px
   centred, so the lens fully clears the controller at both ends while its
   own width stays inside the track. */
@keyframes glassSweep {
  0%   { transform: translateX(-74px); }
  50%  { transform: translateX(74px); }
  100% { transform: translateX(-74px); }
}
@keyframes worldCounter {
  0%   { transform: translateX(74px); }
  50%  { transform: translateX(-74px); }
  100% { transform: translateX(74px); }
}
@media (max-width: 430px) {
  .scan-track { width: 220px; }
  @keyframes glassSweep {
    0%   { transform: translateX(-62px); }
    50%  { transform: translateX(62px); }
    100% { transform: translateX(-62px); }
  }
  @keyframes worldCounter {
    0%   { transform: translateX(62px); }
    50%  { transform: translateX(-62px); }
    100% { transform: translateX(62px); }
  }
}
@media (prefers-reduced-motion: reduce) {
  /* Stopping both leaves the glass centred over the controller with the
     magnification still showing, which is a better still frame than the
     glass parked off to one side. */
  .scan-glass, .sg-world { animation: none !important; }
}
.analysis-card > small { color: var(--cyan); text-transform: uppercase; letter-spacing: .14em; font-weight: 850; font-size: .67rem; flex: none; }
.analysis-card h2 { font-size: clamp(1.45rem, 3vw, 2.1rem); margin: 6px 0 3px; letter-spacing: -.03em; flex: none; }
.analysis-card > p { color: rgba(255,255,255,.62); font-size: .9rem; margin: 0; flex: none; }
/* 2026-09-06: `text-wrap: balance` evens out the two lines instead of
   filling the first and leaving a short remainder. It is the refinement,
   not the guarantee – the hard protection against a one-word last line is
   the &nbsp; binding "switch" to "tabs." in the markup, which works in
   every browser. Where balance is unsupported this simply falls back to
   normal wrapping with that pair still held together. max-width keeps the
   measure from growing so wide on a large screen that the sentence tries
   to fit on one line and strands a word anyway. */
.analysis-note { font-size: .8rem; color: rgba(255,255,255,.48); margin: 2px 0 10px !important; text-wrap: balance; max-width: 46ch; margin-left: auto !important; margin-right: auto !important; }
.analysis-progress { height: 6px; width: 100%; max-width: 400px; margin: 15px auto 2px; border-radius: 999px; background: rgba(255,255,255,.08); overflow: hidden; flex: none; }
.analysis-progress span { display: block; height: 100%; width: 8%; border-radius: 999px; background: linear-gradient(90deg, var(--blue), var(--cyan)); box-shadow: 0 0 16px rgba(53,212,232,.5); transition: width .6s var(--ease-spring); }
.analysis-stages { list-style: none; padding: 4px; margin: 13px 0 0; display: grid; gap: 6px; text-align: left; overflow-y: auto; min-height: 0; flex: 1 1 auto; }
.analysis-stages::-webkit-scrollbar { width: 7px; }
.analysis-stages::-webkit-scrollbar-thumb { background: rgba(255,255,255,.14); border-radius: 7px; }
.analysis-stages li { display: grid; grid-template-columns: 20px 1fr auto; align-items: center; gap: 11px; padding: 7px 12px; border-radius: 12px; color: rgba(255,255,255,.4); background: rgba(255,255,255,.035); transition: color .3s, background .3s; }
.stage-dot { width: 15px; height: 15px; justify-self: center; border-radius: 50%; border: 2px solid rgba(255,255,255,.22); position: relative; transition: border-color .3s, background .3s; }
.analysis-stages li strong { font-weight: 650; font-size: .83rem; }
.stage-meta { color: rgba(255,255,255,.3); font-size: .64rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
.analysis-stages li.active { color: #fff; background: rgba(53,212,232,.1); }
.analysis-stages li.active .stage-dot { border-color: var(--cyan); box-shadow: 0 0 0 4px rgba(53,212,232,.14); }
.analysis-stages li.active .stage-dot::after { content: ""; position: absolute; inset: 2px; border-radius: 50%; background: var(--cyan); animation: dot-pulse 1.2s ease-in-out infinite; }
.analysis-stages li.active .stage-meta { color: var(--cyan); }
.analysis-stages li.complete { color: rgba(255,255,255,.7); }
.analysis-stages li.complete .stage-dot { border-color: var(--teal); background: var(--teal); }
.analysis-stages li.complete .stage-dot::after { content: ""; position: absolute; left: 3.5px; top: 1px; width: 3.5px; height: 7px; border: solid #05122b; border-width: 0 2px 2px 0; transform: rotate(45deg); }
.analysis-stages li.complete .stage-meta { color: var(--teal); }
.analysis-actions { margin-top: 15px; display: flex; justify-content: center; flex: none; }
.analysis-actions .button-quiet { background: rgba(255,255,255,.07); color: white; border-color: rgba(255,255,255,.14); box-shadow: none; }
@keyframes analysis-in { from { opacity: 0; transform: translateY(14px) scale(.96); } to { opacity: 1; transform: none; } }
@keyframes dot-pulse { 0%,100% { transform: scale(.72); opacity: .7; } 50% { transform: scale(1); opacity: 1; } }
/* 2026-09-06: flex-wrap added. The toast is a fixed 380px row, and the
   failure action link ("Email hello@health-e-habits.org") cannot break – so
   without wrapping it claimed its full width from the same row and squeezed
   the message down to roughly one word per line. Wrapping lets the link drop
   to its own line beneath the message instead. */
.toast { position: fixed; right: 22px; bottom: 22px; z-index: 150; max-width: 380px; display: flex; flex-wrap: wrap; align-items: flex-start; gap: 10px; color: white; background: rgba(6,27,58,.95); border: 1px solid rgba(255,255,255,.15); box-shadow: var(--shadow-lg); padding: 14px 40px 14px 18px; border-radius: 15px; }
/* Basis of 0 with grow keeps the message sharing the first row with the close
   button; the action below takes a full row of its own. */
.toast-msg { flex: 1 1 0; min-width: 0; }
/* 2026-09-06: the action link on a failure toast. Underlined rather than
   colour-only so it is identifiable as a link without relying on colour
   perception, and given its own full-width row so it never competes with the
   message for space. */
.toast-action { flex: 1 0 100%; color: var(--purple); font-weight: 700; text-decoration: underline; text-underline-offset: 3px; }
.toast-action:hover { color: #fff; }
/* Persistent (failure/incomplete-state) toasts get a slightly stronger
   border so they read as "needs your attention" rather than a passing
   confirmation – see showToast's own 2026-09-04 comment for why these
   don't auto-dismiss. */
.toast-persistent { border-color: rgba(255,138,101,.55); }
/* 2026-09-06: absolutely positioned. The toast wraps now (see .toast), and an
   in-flow close button lands on a row of its own beneath the action link,
   bottom-left, which is not where anyone looks for a dismiss control. The
   40px right padding on .toast reserves its space so the message cannot run
   underneath it. */
.toast-close { position: absolute; top: 12px; right: 12px; flex: none; width: 20px; height: 20px; border: 0; background: none; color: rgba(255,255,255,.55); font-size: 1.1rem; line-height: 1; cursor: pointer; border-radius: 6px; }
.toast-close:hover { color: white; background: rgba(255,255,255,.12); }
.noscript { position: fixed; inset: auto 18px 18px; z-index: 200; padding: 15px; color: white; background: #9E2C2C; border-radius: 12px; }

@media (max-width: 1240px) {
  /* horizontal nav collapses into the drawer below 980px */
  .hero { grid-template-columns: 1fr; }
  .decision-card { right: 0; }
  .steps-grid { grid-template-columns: repeat(5, 1fr); }
  .step-card { min-width: 0; }
  .framework-grid { grid-template-columns: repeat(2, 1fr); }
  .preview-category-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 980px) {
  :root { --page: min(100% - 32px, 920px); }
  .header-shell { grid-template-columns: auto 1fr auto; }
  .primary-nav { display: none; }
  .hero { grid-template-columns: 1fr; padding-top: 70px; }
  .hero-copy { max-width: 850px; }
  .hero-visual { min-height: 550px; }
  .hero-visual > img { width: 96%; transform: none; }
  .decision-card { right: 4%; top: 55px; }
  .chip-one { left: 9%; }
  .section-heading { align-items: start; flex-direction: column; }
  .preview-window { grid-template-columns: 240px 1fr; }
  .preview-stats { grid-template-columns: repeat(2, 1fr); }
  .report-shell { grid-template-columns: 1fr; }
  /* 2026-09-04: was a 180px-thumbnail-beside-text grid down here, same as
     the 720px rule below – cramped at this width, and worth a second look:
     nothing anywhere in this component is height-constrained with hidden
     overflow (checked every rule touching .report-sidebar), so there was
     never a genuine "trapped" scroll region on mobile the way the desktop
     sticky sidebar has (see the maxheight/overflow rule further down,
     which stays – that one's real and scoped to min-width:981px only).
     Simplifying to a single column regardless, since a cramped thumbnail
     next to squeezed text isn't better than a full-width cover with room
     to breathe underneath, and removes any doubt that everything -
     including the four action buttons - is reachable by plain page
     scroll, nothing hidden inside a sub-box. */
  .report-sidebar { position: relative; top: auto; display: block; }
  .report-sidebar-actions { grid-template-columns: repeat(3, 1fr); }
  .report-nav { top: 104px; }
  .cta-card { grid-template-columns: auto 1fr; }
  .cta-card .button { grid-column: 1 / -1; justify-self: start; }
  .footer-grid { display: grid; grid-template-columns: 1.4fr repeat(2, 1fr); }
  .footer-grid > div:last-child { grid-column: 2 / -1; }
  .game-match-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 720px) {
  :root { --page: calc(100% - 22px); }
  body { background: linear-gradient(180deg, #05060f, #070c1f 30%, #0a0b1c 60%, #060711 100%); }
  .site-header { padding-top: 9px; }
  .header-shell { min-height: 66px; border-radius: 20px; padding: 8px 9px 8px 12px; grid-template-columns: auto 1fr auto; gap: 8px; }
  .brand img { width: 72px; height: 36px; }
  .brand-copy { display: none; }
  .new-analysis-button { min-width: 44px; width: 44px; padding: 0; }
  .new-analysis-button span, .new-analysis-button { font-size: 0; }
  .new-analysis-button svg { width: 18px; height: 18px; }
  .hero { padding: 62px 0 55px; min-height: auto; }
  .hero h1 { font-size: clamp(3rem, 15vw, 5rem); }
  .hero-lead { font-size: 1rem; }
  .search-field-wrap { grid-template-columns: auto 1fr; padding: 7px 9px 7px 14px; }
  .analyze-button { grid-column: 1 / -1; width: 100%; }
  .search-footer { gap: 2px; }
  .hero-trust-grid { grid-template-columns: 1fr; gap: 0; }
  .hero-trust-grid article { padding-block: 8px; }
  .hero-visual { min-height: 440px; }
  .hero-visual > img { width: 115%; }
  .decision-card { width: min(285px, 82vw); right: 0; top: 25px; transform: scale(.9); transform-origin: top right; }
  .chip-one { left: 2%; bottom: 70px; }
  .chip-two { right: 3%; bottom: 18px; }
  .section-heading h2 { font-size: 2.65rem; }
  .how-section, .framework-section, .preview-section { padding-block: 70px; }
  .framework-grid { grid-template-columns: 1fr; }
  .framework-card { min-height: 310px; }
  .preview-window { grid-template-columns: 1fr; }
  .preview-sidebar { display: grid; grid-template-columns: 120px 1fr; gap: 15px; }
  .demo-cover { grid-row: span 2; padding: 12px; }
  .demo-cover strong { font-size: 1.1rem; }
  .preview-meta { padding: 4px 0; }
  .preview-sidebar .button { grid-column: 2; align-self: end; }
  .preview-tabs { padding-inline: 15px; }
  .preview-content { padding: 22px 16px; }
  .preview-content-head { align-items: start; }
  .preview-stats, .preview-category-grid { grid-template-columns: 1fr 1fr; }
  .mini-category { min-height: 150px; }
  .report-sidebar { padding: 14px; }
  .report-cover { padding: 12px; }
  .report-cover strong { font-size: .9rem; }
  .report-game-info { padding: 3px 0; }
  .report-game-info h2 { font-size: 1rem; }
  .report-game-info dl > div { grid-template-columns: 70px 1fr; }
  .report-sidebar-actions { grid-template-columns: 1fr; }
  .report-nav { top: 80px; }
  .report-content { padding: 22px 16px; }
  .report-kicker { align-items: start; flex-direction: column; }
  .quick-stats { grid-template-columns: 1fr 1fr; }
  .summary-grid { grid-template-columns: 1fr; }
  .category-heading { grid-template-columns: auto 1fr auto; gap: 10px; }
  .category-badge { width: 42px; height: 42px; }
  .category-summary, .category-details { margin-left: 0; }
  .source-row { grid-template-columns: 1fr auto; }
  .report-actions-band { align-items: start; flex-direction: column; }
  /* 2026-09-07: cancel the full-width breakout at this width. MEASURED at
     390px: every other section sat at 16px from each edge of .report-content,
     while this band sat at 19px left and -9px right, i.e. hanging 9px PAST
     the content box and 2px off the screen edge, which reads as the whole
     card being shoved right.
     The breakout (see the @media screen rule on .report-actions-band above)
     offsets by var(--content-pad), and that only lines up while --content-pad
     equals .report-content's actual padding. It stops being true right here:
     the rule a few lines below sets .report-content's padding to 22px 16px,
     but --content-pad is still clamp(25px, 4vw, 50px) and computes to 25px.
     Hence 25 - 16 = 9px of overhang.
     Zeroed rather than re-pointed at 16px, because the breakout's deliberate
     28px-left / 0px-right asymmetry is a desktop composition and at 390px it
     just looks lopsided. Matching the neighbours is what was asked for. */
  .report-actions-band { margin-left: 0; margin-right: 0; }
  .report-actions { align-items: stretch; width: 100%; }
  .cta-card { grid-template-columns: 1fr; padding: 27px; }
  .cta-card .button { grid-column: auto; }
  .footer-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 34px 20px; }
  .footer-brand { grid-column: 1 / -1; }
  .footer-grid > div:last-child { grid-column: auto; }
  .footer-bottom { display: flex; align-items: start; flex-direction: column; }
  .footer-credits { text-align: inherit; }
  .game-match-grid { grid-template-columns: 1fr; }
  .modal-backdrop, .analysis-overlay { padding: 10px; }
  .modal, .analysis-card { border-radius: 22px; padding: 20px; max-height: calc(100vh - 20px); }
  .analysis-stages li { font-size: .82rem; }
}

@media (max-width: 430px) {
  .preview-sidebar { grid-template-columns: 94px 1fr; }
  .preview-stats, .preview-category-grid, .quick-stats { grid-template-columns: 1fr; }
  .hero-visual { min-height: 390px; }
  .decision-card { right: -22px; }
  .floating-chip { font-size: .68rem; }
  .footer-grid { display: grid; grid-template-columns: 1fr; }
  .footer-grid > div:last-child { grid-column: auto; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

@media print {
  @page { margin: 0.7in 0.6in; }
  * { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }
  html, body { background: #fff !important; color: #1a1f2b !important; font-size: 10.5pt; line-height: 1.5; }
  /* 2026-09-05, ROOT CAUSE of the exported PDF containing extra pages of
     methodology cards and FAQ accordion items before the actual report,
     with an overlapping logo/search-bar mess across the top of several
     pages – this is a single-page app, so the homepage's methodology
     section and FAQ section sit in the same DOM as the report, and this
     hide-list is what's supposed to keep everything except the report
     itself out of a print job. Two sections were missing from it
     (.methodology-section, .faq-section), so they printed as their own
     pages ahead of the report. Separately, .promo-bar (the sticky top
     search bar) is position:fixed and was ALSO never hidden here –
     fixed-position elements don't respect normal page flow when printed,
     so it was rendering on top of whatever content landed on each page
     instead of appearing once, which is exactly the overlapping logos/
     search-box/button mess bleeding across the top of pages 1-3. */
  .site-header, .hero, .how-section, .story-section, .framework-section, .preview-section, .methodology-section, .faq-section, .promo-bar, .cta-section, .site-footer, .report-nav, .report-sidebar-actions, .toast, .ambient, .scroll-progress, .analysis-overlay, .nav-drawer, .nav-drawer-backdrop, .modal-backdrop, .skip-link, .svg-sprite { display: none !important; }
  .report-app { display: block !important; width: 100%; padding: 0; }
  .section-shell { width: 100%; }

  /* Letterhead */
  .report-shell { display: block; }
  .report-sidebar { position: static; display: grid; grid-template-columns: 1.1in 1fr; gap: 0.22in; align-items: center; color: #1a1f2b; background: #fff; box-shadow: none; border: 0; border-bottom: 2px solid #0A84FF; border-radius: 0; padding: 0 0 0.16in; margin-bottom: 0.3in; max-height: none; overflow: visible; break-inside: avoid; break-after: avoid; }
  .report-cover { min-height: 1.35in; border-radius: 10px; }
  .report-cover small, .report-cover strong { color: #fff !important; }
  .report-cover strong { font-size: 1rem; }
  .report-game-info { padding: 0; }
  .report-game-info h2 { color: #0A1B3A !important; font-size: 16pt; margin: 0 0 9px; }
  .report-game-info dl { display: grid; grid-template-columns: 1fr 1fr; gap: 3px 22px; }
  .report-game-info dl > div { grid-template-columns: auto 1fr; gap: 8px; border-bottom: 0; padding: 1px 0; font-size: 8.5pt; }
  .report-game-info dt { color: #6b7480 !important; }
  .report-game-info dd { color: #1a1f2b !important; text-align: left; }
  .report-mode { grid-column: 1 / -1; margin: 9px 0 0; color: #5a521f !important; background: #FFFBE8 !important; border: 1px solid #E5D98A !important; font-size: 8pt; }

  .report-main { box-shadow: none; border: 0; border-radius: 0; background: #fff !important; }
  .report-content { padding: 0; }
  .report-kicker { margin-bottom: 2px; }
  .report-kicker small { color: #0A84FF !important; }
  .report-date { color: #6b7480 !important; }
  .report-hero { padding-bottom: 16px; border-bottom: 1px solid #dfe5ec; }
  .report-hero h1 { color: #0A1B3A !important; font-size: 22pt; margin: 6px 0 10px; }
  .report-hero > p { color: #38424f !important; font-size: 10.5pt; }

  .quick-stats { margin: 14px 0; gap: 8px; }
  .quick-stat { padding: 11px; border: 1px solid #dfe5ec !important; background: #f7f9fc !important; border-radius: 9px; }
  .quick-stat svg { color: #0A84FF !important; }
  .quick-stat strong { color: #0A1B3A !important; font-size: 13pt; margin: 6px 0 0; }
  .quick-stat span { color: #6b7480 !important; }
  .disclaimer-box { background: #FFFBE8 !important; color: #5a521f !important; border: 1px solid #E5D98A !important; font-size: 9pt; }

  .report-section { padding: 16px 0; border-bottom: 1px solid #eef2f6; }
  .report-section:last-child { border-bottom: 0; }
  .report-section-head { margin-bottom: 12px; break-after: avoid; }
  .report-section-head h2 { color: #0A1B3A !important; font-size: 15pt; }
  .report-section-head p { color: #6b7480 !important; }
  h2, h3, h4 { break-after: avoid; }

  .summary-grid { gap: 10px; }
  .insight-card, .detail-block { background: #f7f9fc !important; border: 1px solid #e3e9f0 !important; box-shadow: none; break-inside: avoid; }
  .insight-card h3, .detail-block h4 { color: #0A1B3A !important; }
  .insight-card li, .detail-block p, .detail-block ul { color: #38424f !important; }
  .insight-card li::before { background: var(--teal) !important; }
  .concern-card li::before { background: var(--orange) !important; }
  .discussion-card li::before { background: var(--purple) !important; }

  .report-category { break-inside: avoid; box-shadow: none; border: 1px solid #e3e9f0 !important; background: #fff !important; padding: 16px 20px; }
  .report-category::before { background: var(--cat) !important; }
  .category-badge { background: var(--cat) !important; color: #fff !important; box-shadow: none; }
  .category-heading small { color: var(--cat) !important; }
  .category-heading h3 { color: #0A1B3A !important; }
  .category-summary { color: #38424f !important; }
  .category-details { display: grid !important; }
  .category-toggle { display: none; }
  .detail-block h4 svg { color: var(--cat) !important; }

  .source-row { break-inside: avoid; box-shadow: none; background: #fff !important; border: 1px solid #e3e9f0 !important; }
  .source-row strong { color: #0A1B3A !important; }
  .source-row span, .source-row small { color: #6b7480 !important; }
  a { text-decoration: none; }
  .source-row a { width: auto; height: auto; background: transparent !important; border: 0 !important; color: #0A84FF; }
  .source-row a::after { content: attr(href); display: block; margin-top: 3px; font-size: 8pt; color: #0A84FF; word-break: break-all; }

  .report-actions-band { display: none; }
}

/* Premium visual upgrade: layered glass, purple energy, motion and depth */
@keyframes hehAurora {
  0%, 100% { transform: translate3d(-4%, -2%, 0) scale(1); }
  50% { transform: translate3d(5%, 4%, 0) scale(1.08); }
}
@keyframes hehFloat {
  0%, 100% { translate: 0 0; }
  50% { translate: 0 -12px; }
}
@keyframes hehShimmer {
  from { transform: translateX(-160%) skewX(-18deg); }
  to { transform: translateX(260%) skewX(-18deg); }
}
@keyframes hehPulse {
  0%, 100% { opacity: .55; transform: scale(.9); }
  50% { opacity: 1; transform: scale(1.12); }
}
@keyframes hehRing {
  to { transform: rotate(360deg); }
}
@keyframes hehGradient {
  0%, 100% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
}

@media screen {
  :root {
    --purple: #A855F7;
    --purple-bright: #C084FC;
    --purple-deep: #6D28D9;
    --violet: #7C3AED;
    --magenta: #E879F9;
    --glass-line: rgba(255,255,255,.24);
    --glass-fill: rgba(255,255,255,.12);
    --liquid-shadow: 0 30px 90px rgba(2, 5, 20, .34), inset 0 1px 0 rgba(255,255,255,.34);
    --ease-spring: cubic-bezier(.16, 1, .3, 1);
  }

  html { scroll-padding-top: 112px; }
  body {
    background:
      radial-gradient(circle at 74% 4%, rgba(124,58,237,.32), transparent 32rem),
      radial-gradient(circle at 14% 12%, rgba(10,132,255,.26), transparent 29rem),
      radial-gradient(circle at 58% 86%, rgba(34,199,184,.12), transparent 32rem),
      linear-gradient(180deg, #05060f 0%, #070c1f 26%, #0a0b1c 55%, #080a17 82%, #060711 100%);
  }
  body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    opacity: .24;
    background-image:
      radial-gradient(circle at 30% 20%, rgba(255,255,255,.7) 0 1px, transparent 1.2px),
      radial-gradient(circle at 70% 60%, rgba(255,255,255,.5) 0 1px, transparent 1.2px);
    background-size: 34px 34px, 52px 52px;
    mask-image: linear-gradient(to bottom, black 0 34%, transparent 62%);
  }

  .scroll-progress {
    position: fixed;
    inset: 0 auto auto 0;
    width: 100%;
    height: 3px;
    z-index: 300;
    transform: scaleX(0);
    transform-origin: left;
    background: linear-gradient(90deg, var(--blue), var(--purple-bright), var(--teal));
    box-shadow: 0 0 20px rgba(192,132,252,.72);
  }

  .ambient { opacity: .72; mix-blend-mode: screen; animation: hehAurora 12s ease-in-out infinite; }
  .ambient-one { width: 530px; height: 530px; background: rgba(10,132,255,.34); filter: blur(95px); }
  .ambient-two { width: 600px; height: 600px; background: rgba(34,199,184,.22); filter: blur(110px); }
  .ambient-three { width: 580px; height: 580px; background: rgba(168,85,247,.27); filter: blur(105px); top: 690px; left: 42%; animation-delay: -5s; }

  .glass {
    background:
      linear-gradient(135deg, rgba(255,255,255,.20), rgba(255,255,255,.07) 48%, rgba(156,110,255,.08)),
      rgba(10,18,42,.16);
    border-color: rgba(255,255,255,.24);
    box-shadow: var(--liquid-shadow);
    backdrop-filter: blur(32px) saturate(180%);
    -webkit-backdrop-filter: blur(32px) saturate(180%);
  }

  .site-header { padding-top: 14px; transition: padding .5s var(--ease-spring); }
  .header-shell {
    position: relative;
    overflow: hidden;
    background: linear-gradient(135deg, rgba(7,18,43,.76), rgba(21,19,52,.66));
    border-color: rgba(255,255,255,.20);
    box-shadow: 0 18px 60px rgba(0,0,0,.25), inset 0 1px 0 rgba(255,255,255,.22);
    transition: min-height .5s var(--ease-spring), background .5s, box-shadow .5s, transform .5s var(--ease-spring);
  }
  .header-shell::before {
    content: "";
    position: absolute;
    inset: -1px;
    pointer-events: none;
    border-radius: inherit;
    background: linear-gradient(115deg, rgba(255,255,255,.3), transparent 28%, rgba(192,132,252,.2) 67%, transparent 90%);
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
    padding: 1px;
  }
  .site-header.is-compact .header-shell {
    min-height: 66px;
    background: linear-gradient(135deg, rgba(4,12,31,.88), rgba(24,17,55,.82));
    box-shadow: 0 14px 42px rgba(0,0,0,.3), inset 0 1px 0 rgba(255,255,255,.18);
    transform: translateY(-2px);
  }
  .site-header.is-compact { padding-top: 8px; }
  .brand img { filter: drop-shadow(0 6px 18px rgba(53,212,232,.14)); }
  .nav-link { transition: color .25s, background .25s, transform .25s var(--ease-spring); }
  .nav-link:hover { transform: translateY(-1px); }

  .button { position: relative; overflow: hidden; isolation: isolate; transition-duration: .38s; transition-timing-function: var(--ease-spring); }
  .button::before {
    content: "";
    position: absolute;
    inset: -40% auto -40% -30%;
    width: 30%;
    z-index: -1;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.55), transparent);
    transform: translateX(-160%) skewX(-18deg);
  }
  .button:hover::before { animation: hehShimmer .85s ease; }
  .button-primary {
    background: linear-gradient(120deg, #087BFF, #7C3AED 53%, #20C7B7);
    background-size: 180% 180%;
    box-shadow: 0 16px 38px rgba(92,65,220,.32), inset 0 1px 0 rgba(255,255,255,.32);
    animation: hehGradient 9s ease infinite;
  }
  .button-primary:hover { box-shadow: 0 22px 50px rgba(114,76,225,.44), 0 0 0 1px rgba(255,255,255,.18) inset; }

  .hero {
    min-height: 890px;
    padding-top: 92px;
    padding-bottom: 118px;
    grid-template-columns: minmax(0, 1fr) minmax(500px, .98fr);
    gap: clamp(30px, 5vw, 84px);
  }
  .hero::before {
    content: "";
    position: absolute;
    width: 720px;
    height: 720px;
    right: -170px;
    top: 10px;
    border-radius: 50%;
    background: conic-gradient(from 210deg, rgba(10,132,255,.16), rgba(168,85,247,.22), rgba(34,199,184,.14), rgba(10,132,255,.16));
    filter: blur(52px);
    animation: hehRing 28s linear infinite;
    pointer-events: none;
  }
  .hero-copy { position: relative; z-index: 4; }
  .hero h1 { text-wrap: balance; }
  .hero h1 span {
    background-image: linear-gradient(90deg, #71E6FF 0%, #36D4C1 35%, #C084FC 72%, #F0ABFC 100%);
    background-size: 200% auto;
    animation: hehGradient 8s ease infinite;
  }
  .hero-lead { max-width: 700px; color: rgba(241,247,255,.73); }
  .eyebrow { color: #B9F4FF; }
  .eyebrow span { background: linear-gradient(90deg, var(--blue), var(--purple-bright), var(--teal)); box-shadow: 0 0 16px rgba(192,132,252,.42); }

  .search-panel {
    border-radius: 30px;
    padding: 11px;
    background: linear-gradient(145deg, rgba(255,255,255,.17), rgba(143,91,255,.08));
    border-color: rgba(255,255,255,.28);
    box-shadow: 0 28px 80px rgba(0,0,0,.28), 0 0 0 1px rgba(145,104,255,.12) inset;
  }
  .search-panel::after {
    content: "";
    position: absolute;
    inset: -1px;
    z-index: -1;
    border-radius: inherit;
    background: linear-gradient(110deg, rgba(255,255,255,.48), transparent 24%, rgba(168,85,247,.32) 62%, rgba(34,199,184,.24));
    filter: blur(14px);
    opacity: .34;
  }
  .search-field-wrap {
    border: 1px solid rgba(255,255,255,.72);
    background: linear-gradient(145deg, rgba(255,255,255,.98), rgba(244,244,255,.94));
    box-shadow: 0 16px 60px rgba(0,0,0,.24), inset 0 1px 0 white;
  }
  .search-field-wrap:focus-within { box-shadow: 0 18px 72px rgba(66,49,161,.32), 0 0 0 4px rgba(168,85,247,.14), inset 0 1px 0 white; }
  .search-icon { color: var(--violet); }
  .search-footer button:hover { background: rgba(168,85,247,.16); }
  .suggestion-list { border-color: rgba(139,61,235,.32); box-shadow: 0 40px 100px rgba(8,5,26,.72), 0 0 0 1px rgba(139,61,235,.14) inset; }

  .hero-trust-grid article {
    padding: 14px 12px;
    border-radius: 18px;
    transition: background .3s, transform .3s var(--ease-spring);
  }
  .hero-trust-grid article:hover { background: rgba(255,255,255,.055); transform: translateY(-3px); }
  .mini-icon { background: linear-gradient(145deg, rgba(255,255,255,.13), rgba(168,85,247,.11)); color: #C8F8FF; }

  .hero-visual { min-height: 680px; overflow: visible; isolation: isolate; }
  .hero-photo-frame {
    position: absolute;
    inset: 38px 24px 60px 42px;
    margin: 0;
    overflow: hidden;
    border-radius: 46px;
    background: #080B18;
    border: 1px solid rgba(255,255,255,.26);
    box-shadow: 0 52px 120px rgba(0,0,0,.48), inset 0 1px 0 rgba(255,255,255,.28);
    transform-style: preserve-3d;
  }
  .hero-photo-frame::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2;
    background:
      linear-gradient(180deg, rgba(6,9,22,.58) 0%, transparent 17%, transparent 44%, rgba(3,6,17,.9) 100%),
      radial-gradient(125% 96% at 52% 40%, transparent 40%, rgba(6,10,30,.62) 100%),
      linear-gradient(140deg, rgba(34,199,184,.16), rgba(10,132,255,.1) 42%, rgba(124,58,237,.42) 100%);
    mix-blend-mode: normal;
    pointer-events: none;
  }
  .hero-photo-frame img { width: 100%; height: 100%; object-fit: cover; object-position: 54% center; filter: saturate(.9) contrast(1.06) brightness(.92); transform: scale(1.035); }
  .hero-photo-frame figcaption {
    position: absolute;
    left: 20px;
    right: auto;
    bottom: 18px;
    z-index: 3;
    max-width: 340px;
    padding: 11px 15px;
    border-radius: 14px;
    color: rgba(255,255,255,.88);
    font-size: .8rem;
    line-height: 1.42;
    background: rgba(9,11,28,.52);
    border: 1px solid rgba(255,255,255,.13);
    backdrop-filter: blur(16px);
    box-shadow: 0 14px 34px rgba(0,0,0,.38);
  }
  .photo-reflection {
    position: absolute;
    z-index: 4;
    inset: -35% 42% 20% -25%;
    transform: rotate(18deg);
    background: linear-gradient(100deg, transparent, rgba(255,255,255,.15), transparent);
    filter: blur(8px);
    pointer-events: none;
  }
  .visual-glow { width: 96%; background: radial-gradient(circle, rgba(168,85,247,.38), rgba(10,132,255,.12) 43%, transparent 72%); filter: blur(36px); }
  .hero-orbit { position: absolute; z-index: -1; border-radius: 50%; border: 1px solid rgba(192,132,252,.22); box-shadow: inset 0 0 70px rgba(168,85,247,.06); }
  .orbit-one { width: 610px; height: 610px; right: -80px; top: 5px; animation: hehRing 34s linear infinite; }
  .orbit-two { width: 480px; height: 480px; right: 20px; top: 78px; border-style: dashed; animation: hehRing 25s linear infinite reverse; }
  .decision-card { right: -16px; top: 72px; width: 308px; background: linear-gradient(145deg, rgba(17,18,49,.74), rgba(31,15,61,.66)); border-color: rgba(255,255,255,.22); box-shadow: 0 28px 70px rgba(0,0,0,.38), inset 0 1px 0 rgba(255,255,255,.25); }
  .shield-orb { color: #D9B6FF; background: linear-gradient(145deg, rgba(168,85,247,.24), rgba(53,212,232,.12)); }
  .floating-chip { background: linear-gradient(145deg, rgba(10,22,52,.76), rgba(38,20,74,.72)); border-color: rgba(255,255,255,.22); box-shadow: 0 20px 50px rgba(0,0,0,.32), inset 0 1px 0 rgba(255,255,255,.2); }
  .floating-chip svg { color: #D8B4FE; }
  .chip-one { left: 0; bottom: 124px; }
  .chip-two { right: 15%; bottom: 32px; }
  .floating-score {
    position: absolute;
    left: 18px;
    top: 76px;
    z-index: 5;
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 11px 14px;
    border-radius: 18px;
    color: white;
    background: linear-gradient(145deg, rgba(8,20,48,.7), rgba(48,19,84,.68));
  }
  .floating-score > span { width: 36px; height: 36px; display: grid; place-items: center; border-radius: 12px; background: linear-gradient(145deg, var(--blue), var(--purple)); font-weight: 900; box-shadow: 0 8px 24px rgba(124,58,237,.35); }
  .floating-score small, .floating-score strong { display: block; line-height: 1.2; }
  .floating-score small { color: rgba(255,255,255,.52); font-size: .64rem; }
  .floating-score strong { margin-top: 4px; font-size: .76rem; }

  .how-section, .framework-section, .preview-section, .story-section { position: relative; }
  /* light-theme section veil & card fills removed for the dark redesign */
  .step-card, .framework-card, .preview-window, .report-category, .insight-card {
    --mx: 50%;
    --my: 50%;
  }

  .story-section { padding: 84px 0 112px; }
  .story-grid {
    display: grid;
    grid-template-columns: minmax(0, .9fr) minmax(520px, 1.1fr);
    gap: clamp(50px, 8vw, 120px);
    align-items: center;
    padding: 68px;
    border-radius: 52px;
    color: white;
    background:
      radial-gradient(circle at 80% 15%, rgba(168,85,247,.28), transparent 32%),
      linear-gradient(145deg, #06152F, #11103A 54%, #071F3B);
    box-shadow: 0 42px 110px rgba(21,19,72,.28), inset 0 1px 0 rgba(255,255,255,.12);
    overflow: hidden;
  }
  .story-copy { position: relative; z-index: 2; }
  .story-copy h2 { margin: 18px 0; font-size: clamp(2.6rem, 4.6vw, 5.2rem); line-height: .98; letter-spacing: -.055em; text-wrap: balance; }
  .story-copy > p { color: rgba(255,255,255,.64); font-size: 1.05rem; max-width: 600px; }
  .story-points { display: grid; gap: 13px; margin-top: 30px; }
  .story-points article { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 15px; padding: 16px 18px; border-radius: 18px; background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1); transition: background .25s, border-color .25s, transform .25s; }
  .story-points article:hover { background: rgba(255,255,255,.07); border-color: rgba(124,58,237,.35); transform: translateX(3px); }
  .sp-icon { width: 52px; height: 52px; display: grid; place-items: center; border-radius: 14px; color: #8fbaff; background: rgba(10,132,255,.15); border: 1px solid rgba(10,132,255,.3); }
  .sp-icon.alt { color: #cbb0ff; background: rgba(168,85,247,.16); border-color: rgba(168,85,247,.32); }
  .sp-icon svg { width: 22px; height: 22px; }
  .sp-title { display: flex; align-items: baseline; gap: 12px; }
  .sp-num { color: var(--blue); font-weight: 800; font-size: 1.05rem; }
  .story-points article:nth-child(2) .sp-num { color: var(--purple); }
  .sp-body strong { display: block; color: #fff; font-size: 1.06rem; }
  .sp-body small { display: block; margin-top: 5px; color: rgba(255,255,255,.55); font-size: .9rem; }
  .sp-arrow { width: 18px; height: 18px; color: rgba(255,255,255,.4); transform: rotate(-90deg); }
  .story-collage { min-height: 570px; position: relative; }
  .story-photo { margin: 0; overflow: hidden; border: 1px solid rgba(255,255,255,.22); box-shadow: 0 34px 90px rgba(0,0,0,.38); }
  .story-photo img { width: 100%; height: 100%; object-fit: cover; }
  .story-photo-main { position: absolute; inset: 20px 90px 55px 0; border-radius: 38px; transform: rotate(-2.3deg); }
  .story-photo-main::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, transparent 55%, rgba(5,8,20,.45)); }
  .story-photo-small { position: absolute; width: 58%; height: 46%; right: 0; bottom: 0; border-radius: 30px; transform: rotate(4deg); background: rgba(11,16,42,.7); padding: 8px; }
  .story-photo-small img { border-radius: 23px; }
  .story-photo-small figcaption { position: absolute; left: 22px; right: 18px; bottom: 18px; display: flex; align-items: center; gap: 8px; padding: 9px 12px; border-radius: 14px; color: white; background: rgba(7,10,28,.67); backdrop-filter: blur(18px); font-size: .69rem; }
  .pulse-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--teal); box-shadow: 0 0 0 6px rgba(34,199,184,.13); animation: hehPulse 2.1s ease-in-out infinite; }
  .story-orb { position: absolute; width: 430px; height: 430px; right: -120px; top: -80px; border-radius: 50%; background: radial-gradient(circle, rgba(168,85,247,.3), transparent 67%); filter: blur(25px); }

  .framework-card {
    border: 1px solid rgba(255,255,255,.32);
    box-shadow: 0 26px 70px rgba(40,27,91,.2), inset 0 1px 0 rgba(255,255,255,.25);
    transform-style: preserve-3d;
  }
  .framework-card::before {
    content: "";
    position: absolute;
    inset: 0;
    opacity: .6;
    background:
      radial-gradient(circle at var(--mx) var(--my), rgba(255,255,255,.28), transparent 32%),
      linear-gradient(125deg, rgba(255,255,255,.13), transparent 38%);
    pointer-events: none;
  }
  .framework-card .category-top, .framework-card h3, .framework-card p, .framework-card button { transform: translateZ(22px); }
  .category-a { background: linear-gradient(155deg, #248EFF, #3156D9 58%, #312E81); }
  .category-b { background: linear-gradient(155deg, #22C7B8, #147B91 62%, #174D7B); }
  .category-c { background: linear-gradient(155deg, #FFB23E, #E86134 58%, #9B2C67); }
  .category-d { background: linear-gradient(155deg, #D17BFF, #8B45E8 55%, #4C1D95); }
  .framework-card:hover { box-shadow: 0 36px 90px rgba(56,32,122,.28), 0 0 46px rgba(168,85,247,.12); }

  .preview-window {
    border-color: rgba(255,255,255,.1);
    background: linear-gradient(160deg, rgba(22,18,48,.94), rgba(10,12,30,.96));
    box-shadow: 0 48px 130px rgba(4,3,20,.5), inset 0 1px 0 rgba(255,255,255,.08);
  }
  .preview-sidebar { background: radial-gradient(circle at 25% 10%, rgba(168,85,247,.3), transparent 30%), linear-gradient(180deg, #0a1330, #15113D 66%, #0a1c3d); }
  .demo-cover { box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 22px 50px rgba(0,0,0,.32); }
  .preview-tabs { background: rgba(255,255,255,.02); backdrop-filter: blur(18px); }
  .status-pill i { background: var(--purple); box-shadow: 0 0 0 5px rgba(168,85,247,.12); }

  .cta-card {
    position: relative;
    overflow: hidden;
    background:
      radial-gradient(circle at 85% 20%, rgba(168,85,247,.32), transparent 30%),
      linear-gradient(125deg, #061B3A, #15113D 56%, #073452);
    border: 1px solid rgba(255,255,255,.14);
    box-shadow: 0 38px 100px rgba(24,21,78,.28), inset 0 1px 0 rgba(255,255,255,.13);
  }
  .cta-card::after { content: ""; position: absolute; width: 420px; height: 420px; right: -180px; bottom: -240px; border-radius: 50%; border: 1px solid rgba(255,255,255,.15); box-shadow: 0 0 0 42px rgba(255,255,255,.025), 0 0 0 90px rgba(168,85,247,.025); }
  .cta-card > * { position: relative; z-index: 2; }
  .cta-icon { background: linear-gradient(145deg, rgba(168,85,247,.3), rgba(53,212,232,.14)); color: #E9D5FF; }

  .site-footer { background: radial-gradient(circle at 70% 0, rgba(168,85,247,.14), transparent 32%), #020815; }
  .footer-credits { color: rgba(255,255,255,.38); }
  .footer-credits a { color: rgba(216,180,254,.74); }

  .modal-backdrop, .analysis-overlay { background: rgba(2,5,15,.62); backdrop-filter: blur(18px) saturate(120%); }
  .modal { background: linear-gradient(145deg, rgba(255,255,255,.97), rgba(246,243,255,.95)); border: 1px solid rgba(255,255,255,.9); box-shadow: 0 50px 140px rgba(4,4,19,.48); }
  .analysis-card { background: radial-gradient(circle at 50% 0, rgba(168,85,247,.27), transparent 34%), linear-gradient(145deg, rgba(5,22,53,.94), rgba(24,15,58,.92)); }
  .analysis-orbit > svg { color: #D8B4FE; }
  .analysis-orbit span:nth-child(2) { border-color: rgba(168,85,247,.45); }

  .reveal-ready {
    opacity: 0;
    translate: 0 26px;
    scale: .992;
    filter: blur(3px);
    transition: opacity .7s cubic-bezier(.16,1,.3,1), translate .75s cubic-bezier(.16,1,.3,1), scale .75s cubic-bezier(.16,1,.3,1), filter .6s ease;
    transition-delay: var(--reveal-delay, 0ms);
  }
  .reveal-ready.is-visible { opacity: 1; translate: 0 0; scale: 1; filter: blur(0); }
  [data-parallax] { will-change: transform; }
  .tilt-card { --rx: 0deg; --ry: 0deg; --lift: 0px; transform: perspective(900px) translateY(var(--lift)) rotateX(var(--rx)) rotateY(var(--ry)); transition: transform .28s var(--ease-spring), box-shadow .3s; }
  .tilt-card:hover { --lift: -7px; }
}

@media screen and (max-width: 1240px) {
  .hero { grid-template-columns: 1fr; gap: 18px; }
  .hero-photo-frame { inset: 55px 0 70px 18px; }
  .floating-score { left: 0; }
  .story-grid { grid-template-columns: .9fr 1.1fr; padding: 54px; }
}

@media screen and (max-width: 980px) {
  .hero { min-height: auto; padding-bottom: 92px; }
  .hero-visual { min-height: 610px; }
  .hero-photo-frame { inset: 20px 5% 50px; }
  .decision-card { right: 2%; }
  .floating-score { left: 5%; top: 52px; }
  .story-grid { grid-template-columns: 1fr; padding: 50px; }
  .story-collage { min-height: 520px; }
}

@media screen and (max-width: 720px) {
  body { background: radial-gradient(circle at 70% 4%, rgba(124,58,237,.28), transparent 24rem), linear-gradient(180deg, #05060f, #070c1f 30%, #0a0b1c 60%, #060711 100%); }
  .hero { padding-top: 54px; }
  .hero h1 { letter-spacing: -.055em; }
  .hero-visual { min-height: 520px; }
  .hero-photo-frame { inset: 28px 0 56px; border-radius: 32px; }
  .hero-photo-frame figcaption { left: 20px; right: 20px; bottom: 18px; font-size: .72rem; }
  .decision-card { top: 52px; right: -18px; transform: scale(.83); }
  .floating-score { left: -4px; top: 18px; transform: scale(.88); transform-origin: left top; }
  .chip-one { left: -3px; bottom: 72px; }
  .chip-two { right: 3%; bottom: 20px; }
  .how-section::before, .framework-section::before, .preview-section::before { inset-inline: -4px; border-radius: 34px; }
  .story-section { padding-top: 60px; }
  .story-grid { padding: 34px 22px; border-radius: 34px; }
  .story-copy h2 { font-size: 3rem; }
  .story-collage { min-height: 410px; }
  .story-photo-main { inset: 0 42px 48px 0; border-radius: 28px; }
  .story-photo-small { width: 62%; height: 44%; border-radius: 22px; padding: 6px; }
  .story-photo-small img { border-radius: 17px; }
  .framework-card .category-top, .framework-card h3, .framework-card p, .framework-card button { transform: none; }
}

@media screen and (max-width: 430px) {
  .hero-visual { min-height: 450px; }
  .decision-card { right: -36px; top: 45px; transform: scale(.74); }
  .floating-score { transform: scale(.76); }
  .floating-chip { padding: 8px 10px; }
  .story-grid { padding-inline: 18px; }
  .story-copy h2 { font-size: 2.55rem; }
  .story-collage { min-height: 350px; }
  .story-photo-small figcaption { font-size: .59rem; left: 12px; right: 10px; bottom: 12px; }
}

@media (prefers-reduced-motion: reduce) {
  .ambient, .button-primary, .hero h1 span, .pulse-dot, .hero-orbit { animation: none !important; }
  .reveal-ready { opacity: 1 !important; transform: none !important; filter: none !important; }
  [data-parallax], .tilt-card { transform: none !important; }
}
@media screen {
  [data-parallax] { translate: 0 var(--parallax-y, 0px); transition: translate .12s linear; }
  .visual-glow { transition: translate .7s cubic-bezier(.16,1,.3,1); }
}
@media (prefers-reduced-motion: reduce) {
  [data-parallax] { translate: none !important; }
}
/* reveal handled by the consolidated .reveal-ready rule above */
@media (prefers-reduced-motion: reduce) {
  .reveal-ready { translate: none !important; scale: 1 !important; }
}

/* ============================================================
   ELEVATED NAVIGATION  (dropdown · drawer · toggle · states)
   ============================================================ */

/* Compact / scrolled state – applies regardless of motion preference */
.site-header.is-compact .brand img { width: 78px; }
.site-header.is-compact .brand-copy small { opacity: 0; max-height: 0; margin-top: 0; overflow: hidden; }
.site-header.is-compact .header-shell { min-height: 64px; }
/* let the Methodology dropdown escape the header shell (overridden from the motion media query) */
.header-shell { overflow: visible; }

/* Circular menu toggle (persistent quick-menu, matches the design system) */
.menu-toggle {
  position: relative;
  width: 46px; height: 46px;
  flex: 0 0 auto;
  display: none; place-items: center;
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,.18);
  background: rgba(255,255,255,.06);
  color: #fff;
  cursor: pointer;
  transition: border-color .25s var(--ease), background .25s var(--ease), box-shadow .25s var(--ease);
}
.menu-toggle:hover, .menu-toggle:focus-visible {
  border-color: rgba(182,92,250,.6);
  background: rgba(182,92,250,.16);
  box-shadow: 0 0 0 4px rgba(182,92,250,.14), 0 10px 26px rgba(124,58,237,.28);
}
.menu-toggle svg { position: absolute; width: 20px; height: 20px; transition: opacity .2s var(--ease), transform .3s var(--ease-spring, var(--ease)); }
.menu-toggle .mt-close { opacity: 0; transform: rotate(-45deg) scale(.7); }
.menu-toggle[aria-expanded="true"] .mt-open { opacity: 0; transform: rotate(45deg) scale(.7); }
.menu-toggle[aria-expanded="true"] .mt-close { opacity: 1; transform: rotate(0) scale(1); }
.menu-toggle[aria-expanded="true"] { border-color: rgba(182,92,250,.6); background: rgba(182,92,250,.18); }

/* New Analysis – blue → violet gradient to match the mock */
.new-analysis-button.button-primary,
.drawer-cta.button-primary {
  background: linear-gradient(135deg, var(--blue) 0%, #6D5CF5 55%, var(--purple) 100%);
  box-shadow: 0 12px 30px rgba(124,58,237,.35);
}

/* Methodology dropdown */
.nav-item.has-dropdown { position: relative; display: inline-flex; }
.nav-caret { width: 15px; height: 15px; opacity: .8; transition: transform .3s var(--ease-spring, var(--ease)); }
.nav-item.has-dropdown:hover .nav-caret,
.nav-item.has-dropdown:focus-within .nav-caret,
.nav-item.has-dropdown.open .nav-caret { transform: rotate(180deg); }
.nav-dropdown-trigger::after { display: none; }
.nav-item.has-dropdown:hover .nav-dropdown-trigger,
.nav-item.has-dropdown:focus-within .nav-dropdown-trigger,
.nav-item.has-dropdown.open .nav-dropdown-trigger {
  color: #fff;
  background: linear-gradient(135deg, rgba(124,58,237,.5), rgba(182,92,250,.32));
  box-shadow: inset 0 0 0 1px rgba(182,92,250,.4), 0 10px 30px rgba(124,58,237,.35);
}

.nav-dropdown {
  position: absolute;
  top: calc(100% + 14px);
  left: 50%;
  width: 320px;
  transform: translate(-50%, 8px);
  padding: 10px;
  border-radius: 20px;
  display: grid;
  gap: 4px;
  background: linear-gradient(160deg, rgba(26,20,54,.97), rgba(14,12,34,.97));
  border: 1px solid rgba(182,92,250,.28);
  box-shadow: 0 26px 70px rgba(6,4,20,.6), inset 0 0 0 1px rgba(255,255,255,.04), 0 0 44px rgba(124,58,237,.22);
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
  z-index: 60;
  transition: opacity .26s var(--ease), transform .3s var(--ease-spring, var(--ease)), visibility .26s;
}
.nav-dropdown::before {
  content: "";
  position: absolute;
  top: -7px; left: 50%;
  width: 14px; height: 14px;
  transform: translateX(-50%) rotate(45deg);
  background: rgba(26,20,54,.97);
  border-left: 1px solid rgba(182,92,250,.28);
  border-top: 1px solid rgba(182,92,250,.28);
}
.nav-item.has-dropdown:hover .nav-dropdown,
.nav-item.has-dropdown:focus-within .nav-dropdown,
.nav-item.has-dropdown.open .nav-dropdown {
  opacity: 1; pointer-events: auto; visibility: visible;
  transform: translate(-50%, 0);
}
.nav-dropdown-item {
  display: grid;
  grid-template-columns: 40px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 11px 12px;
  border: 0;
  border-radius: 14px;
  background: transparent;
  color: #fff;
  text-align: left;
  cursor: pointer;
  transition: background .2s var(--ease), transform .2s var(--ease);
}
.nav-dropdown-item:hover, .nav-dropdown-item:focus-visible { background: rgba(255,255,255,.07); transform: translateX(2px); }
.ndi-icon { width: 40px; height: 40px; display: grid; place-items: center; border-radius: 12px; background: rgba(124,58,237,.22); color: #d7c6ff; }
.ndi-icon svg { width: 19px; height: 19px; }
.ndi-icon.icon-a, .ndi-icon.icon-b, .ndi-icon.icon-c { background: linear-gradient(135deg, rgba(162,89,255,.28), rgba(232,121,249,.22)); color: #e3cfff; }
.ndi-text { display: grid; line-height: 1.25; }
.ndi-text strong { font-size: .9rem; font-weight: 700; }
.ndi-text small { color: rgba(255,255,255,.6); font-size: .76rem; }
.ndi-arrow { width: 15px; height: 15px; color: rgba(255,255,255,.4); transform: rotate(-90deg); transition: color .2s var(--ease), transform .2s var(--ease); }
.nav-dropdown-item:hover .ndi-arrow { color: #fff; transform: rotate(-90deg) translateY(2px); }

/* Slide-out drawer */
.nav-drawer-backdrop { position: fixed; inset: 0; z-index: 70; background: rgba(4,6,20,.5); backdrop-filter: blur(3px); -webkit-backdrop-filter: blur(3px); opacity: 0; transition: opacity .3s var(--ease); }
.nav-drawer-backdrop.open { opacity: 1; }
.nav-drawer {
  position: fixed;
  top: 90px;
  right: max(20px, calc((100vw - var(--page)) / 2));
  z-index: 80;
  width: min(330px, calc(100vw - 32px));
  padding: 16px 14px 14px;
  border-radius: 24px;
  display: grid;
  gap: 6px;
  background: linear-gradient(165deg, rgba(24,19,52,.98), rgba(12,11,30,.98));
  border: 1px solid rgba(182,92,250,.26);
  box-shadow: 0 30px 80px rgba(4,3,16,.62), 0 0 46px rgba(124,58,237,.22);
  opacity: 0;
  transform: translateY(-12px) scale(.98);
  transform-origin: top right;
  pointer-events: none;
  visibility: hidden;
  transition: opacity .3s var(--ease), transform .34s var(--ease-spring, var(--ease)), visibility .3s;
}
.nav-drawer.open { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; visibility: visible; }
.nav-drawer-grabber { width: 40px; height: 4px; border-radius: 4px; background: rgba(255,255,255,.2); margin: 2px auto 8px; }
.nav-drawer-list { display: grid; gap: 2px; }
.drawer-link {
  display: grid;
  grid-template-columns: 38px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 12px;
  border: 0;
  border-radius: 14px;
  background: transparent;
  color: #fff;
  font-size: .95rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  transition: background .2s var(--ease), transform .2s var(--ease);
}
.drawer-link:hover, .drawer-link:focus-visible { background: rgba(255,255,255,.07); transform: translateX(2px); }
.dl-icon { width: 38px; height: 38px; display: grid; place-items: center; border-radius: 11px; background: rgba(255,255,255,.06); color: #c9d3e6; transition: background .2s var(--ease), color .2s var(--ease); }
.dl-icon svg { width: 18px; height: 18px; }
.drawer-link:hover .dl-icon { color: #d7c6ff; background: rgba(124,58,237,.2); }
.dl-text { justify-self: start; }
.dl-arrow { width: 16px; height: 16px; color: rgba(255,255,255,.32); }
.dl-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--purple); box-shadow: 0 0 10px var(--purple); justify-self: end; }
.drawer-cta { width: 100%; margin-top: 8px; }

@media (max-width: 720px) {
  .nav-drawer { top: 78px; right: 12px; left: 12px; width: auto; }
  .menu-toggle { width: 42px; height: 42px; }
  .header-actions { gap: 8px; }
}
@media (prefers-reduced-motion: reduce) {
  .nav-dropdown { transform: translate(-50%, 0) !important; transition: opacity .2s linear, visibility .2s linear; }
  .nav-drawer { transform: none !important; transition: opacity .2s linear, visibility .2s linear; }
  .nav-drawer-backdrop { transition: opacity .2s linear; }
  .menu-toggle svg { transition: opacity .15s linear; }
}

/* ============================================================
   MOCKUP REDESIGN – dark sections, glowing cards, trust bars
   ============================================================ */

/* Section headings on dark */
.how-section .section-heading h2,
.framework-section .section-heading h2,
.preview-section .section-heading h2,
.methodology-section .section-heading h2,
.story-copy h2 { color: #fff; }
.how-section .section-heading p,
.framework-section .section-heading p,
.preview-section .section-heading p,
.methodology-section .section-heading p { color: rgba(255,255,255,.6); }
.section-heading h2 span,
.story-copy h2 span { background: linear-gradient(100deg, var(--blue), #8b5cf6, var(--purple)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.section-heading h2 { font-size: clamp(2.4rem, 5vw, 4rem); letter-spacing: -.03em; line-height: 1.02; }
.section-heading.centered { text-align: center; align-items: center; }

/* Eyebrow – teal, uppercase, connector line(s) */
.eyebrow { display: inline-flex; align-items: center; gap: 12px; color: var(--cyan); text-transform: uppercase; letter-spacing: .2em; font-size: .74rem; font-weight: 800; }
.eyebrow span { width: 34px; height: 2px; border-radius: 2px; background: linear-gradient(90deg, var(--blue), var(--cyan)); }
.section-heading.centered .eyebrow { justify-content: center; }
.section-heading.centered .eyebrow::after { content: ""; width: 34px; height: 2px; border-radius: 2px; background: linear-gradient(90deg, var(--purple), var(--blue)); }

/* --- How It Works --- */
.how-section { padding: 100px 0 96px; }
.steps-timeline { position: relative; display: grid; grid-template-columns: repeat(5, 1fr); align-items: center; height: 30px; margin: 34px 0 4px; }
.timeline-line { position: absolute; left: 10%; right: 10%; top: 50%; height: 2px; transform: translateY(-50%); background: linear-gradient(90deg, var(--blue), var(--cyan) 26%, var(--purple) 52%, var(--orange) 78%, #2ee6a0); box-shadow: 0 0 18px rgba(124,58,237,.45); opacity: .85; }
.tnode { justify-self: center; width: 13px; height: 13px; border-radius: 50%; position: relative; z-index: 1; }
.tnode::after { content: ""; position: absolute; inset: -5px; border-radius: 50%; border: 1px solid rgba(255,255,255,.16); }
.tnode.c1 { background: var(--blue); box-shadow: 0 0 16px var(--blue); }
.tnode.c2 { background: var(--cyan); box-shadow: 0 0 16px var(--cyan); }
.tnode.c3 { background: var(--purple); box-shadow: 0 0 16px var(--purple); }
.tnode.c4 { background: var(--orange); box-shadow: 0 0 16px var(--orange); }
.tnode.c5 { background: #2ee6a0; box-shadow: 0 0 16px #2ee6a0; }

.steps-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; }
.step-card {
  --c: var(--blue);
  position: relative; min-height: 300px; padding: 26px 22px 24px;
  border-radius: 24px;
  background: linear-gradient(180deg, rgba(255,255,255,.055), rgba(255,255,255,.02));
  border: 1px solid rgba(255,255,255,.09);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.06), 0 20px 50px rgba(0,0,0,.35);
  overflow: hidden;
  transition: transform .3s var(--ease), box-shadow .3s, border-color .3s;
}
.step-card::before { content: ""; position: absolute; inset: 0 0 auto; height: 130px; background: radial-gradient(150px 90px at 32% -20%, color-mix(in srgb, var(--c) 42%, transparent), transparent 72%); opacity: .55; pointer-events: none; }
.step-card:hover { transform: translateY(-6px); border-color: color-mix(in srgb, var(--c) 45%, transparent); box-shadow: inset 0 1px 0 rgba(255,255,255,.08), 0 26px 60px rgba(0,0,0,.45), 0 0 42px color-mix(in srgb, var(--c) 26%, transparent); }
.step-1 { --c: var(--blue); }
.step-2 { --c: var(--teal); }
.step-3 { --c: var(--purple); }
.step-4 { --c: var(--orange); }
.step-5 { --c: #2ee6a0; }
.step-number { position: absolute; right: 20px; top: 18px; color: var(--c); font-size: 1.15rem; font-weight: 800; }
.step-icon { width: 84px; height: 84px; border-radius: 50%; display: grid; place-items: center; margin: 8px 0 30px; color: var(--c); background: radial-gradient(circle at 50% 40%, color-mix(in srgb, var(--c) 26%, transparent), color-mix(in srgb, var(--c) 7%, transparent)); border: 1px solid color-mix(in srgb, var(--c) 42%, transparent); box-shadow: 0 0 32px color-mix(in srgb, var(--c) 22%, transparent), inset 0 0 20px color-mix(in srgb, var(--c) 12%, transparent); }
.step-icon svg { width: 32px; height: 32px; }
.step-card h3 { margin: 0 0 14px; color: #fff; font-size: 1.25rem; position: relative; padding-bottom: 13px; }
.step-card h3::after { content: ""; position: absolute; left: 0; bottom: 0; width: 34px; height: 3px; border-radius: 3px; background: var(--c); box-shadow: 0 0 10px var(--c); }
.step-card p { margin: 0; color: rgba(255,255,255,.58); font-size: .92rem; }

/* Trust bar (shared: How It Works, Framework, Preview) */
.trust-bar { margin-top: 28px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 0; padding: 18px 8px; border-radius: 22px; background: rgba(255,255,255,.035); border: 1px solid rgba(255,255,255,.08); }
.trust-item { display: flex; align-items: center; gap: 13px; justify-content: center; padding: 4px 14px; }
.trust-item:not(:last-child) { border-right: 1px solid rgba(255,255,255,.08); }
.trust-ic { width: 44px; height: 44px; flex: 0 0 auto; border-radius: 50%; display: grid; place-items: center; color: #cbb9ff; background: rgba(124,58,237,.16); border: 1px solid rgba(124,58,237,.3); }
.trust-ic svg { width: 20px; height: 20px; }
.trust-item strong { display: block; color: #fff; font-size: .92rem; }
.trust-item small { color: rgba(255,255,255,.5); font-size: .8rem; }

/* --- Story trust row --- */
.story-trust { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-top: 26px; }
.st-item { display: flex; align-items: center; gap: 12px; }
.st-ic { width: 44px; height: 44px; flex: 0 0 auto; border-radius: 12px; display: grid; place-items: center; }
.st-ic svg { width: 20px; height: 20px; }
.st-a { color: #a9c7ff; background: rgba(10,132,255,.15); }
.st-b { color: #cbb0ff; background: rgba(168,85,247,.16); }
.st-c { color: #7fe6f2; background: rgba(53,212,232,.15); }
.st-item strong { display: block; color: #fff; font-size: .9rem; }
.st-item small { display: block; color: rgba(255,255,255,.5); font-size: .78rem; margin-top: 2px; }

/* Story trust: keep titles on one line; photo-frame fallbacks for preview */
.st-item strong { white-space: nowrap; }
.story-photo { background: linear-gradient(150deg, #201a4d 0%, #0d1234 60%, #0a1a3a 100%); }
.story-photo-main { background: linear-gradient(150deg, #2a1e5e, #101538 65%, #0c2247); }

/* --- Framework cards enhancements --- */
.framework-card h3::after { content: ""; display: block; width: 46px; height: 3px; margin-top: 13px; border-radius: 3px; background: rgba(255,255,255,.9); box-shadow: 0 0 12px rgba(255,255,255,.35); }
.framework-card h3 { margin-bottom: 16px; }
.framework-card .category-top, .framework-card h3, .framework-card p { position: relative; z-index: 1; }
.card-wm { position: absolute; right: -18px; bottom: -22px; width: 208px; height: 208px; opacity: .12; color: #fff; pointer-events: none; z-index: 0; }
.framework-card button { gap: 13px !important; }
.explore-arrow { width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center; border: 1px solid rgba(255,255,255,.55); background: rgba(255,255,255,.14); transition: background .25s, transform .25s; }
.explore-arrow svg { width: 15px; height: 15px; }
.framework-card button:hover .explore-arrow { background: rgba(255,255,255,.28); transform: translateX(3px); }
/* framework heading description accent bar */
.framework-section .section-heading p { color: rgba(255,255,255,.62); max-width: 430px; }

/* --- Preview section (dark redesign) --- */
.preview-tabs { border-bottom: 1px solid rgba(255,255,255,.09); }
.preview-tabs span { color: rgba(255,255,255,.5); }
.preview-tabs span.active { color: #fff; background: rgba(124,58,237,.28); }
.preview-content-head small { color: var(--cyan); }
.preview-content-head h3 { color: #fff; }
.preview-content > p { color: rgba(255,255,255,.6); }
.status-pill { border: 1px solid rgba(124,58,237,.32); background: rgba(124,58,237,.15); color: #cbb9ff; }
.status-pill svg { width: 14px; height: 14px; }

.preview-stats > div { background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.08); border-radius: 16px; padding: 16px; box-shadow: none; }
.pv-ic { width: 38px; height: 38px; display: grid; place-items: center; border-radius: 11px; margin-bottom: 12px; }
.pv-ic svg { width: 19px; height: 19px; }
.ic-blue { color: #7fb2ff; background: rgba(10,132,255,.16); }
.ic-teal { color: #7fe6d6; background: rgba(34,199,184,.16); }
.ic-purple { color: #cbb0ff; background: rgba(168,85,247,.16); }
.ic-orange { color: #ffcf8f; background: rgba(245,166,35,.16); }
.preview-stats strong { color: #fff; margin-top: 0; font-size: 1.55rem; }
.preview-stats small { color: rgba(255,255,255,.55); }

.preview-category-grid { margin-top: 22px; }
.mini-category { position: relative; min-height: 168px; overflow: hidden; }
.mini-category .mc-ic { width: 40px; height: 40px; display: grid; place-items: center; border-radius: 12px; background: rgba(255,255,255,.18); color: #fff; font-weight: 400; }
.mini-category .mc-ic svg { width: 20px; height: 20px; }
.mini-category strong { color: #fff; font-size: 1.02rem; }
.mini-category small { max-width: 80%; color: rgba(255,255,255,.78); }
.mini-category .mc-arrow { position: absolute; right: 15px; bottom: 15px; width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center; background: rgba(255,255,255,.18); border: 1px solid rgba(255,255,255,.32); }
.mini-category .mc-arrow svg { width: 15px; height: 15px; }

.preview-trust { grid-column: 1 / -1; margin: 0; border: 0; border-top: 1px solid rgba(255,255,255,.08); border-radius: 0; background: rgba(255,255,255,.02); }

.demo-cover { position: relative; overflow: hidden; }
.cover-pad { position: absolute; right: -16px; top: -12px; width: 128px; height: 128px; opacity: .28; color: #fff; }
.demo-cover small { display: inline-flex; align-items: center; gap: 6px; }
.demo-cover small svg { width: 14px; height: 14px; color: var(--cyan); }
.preview-try.button-primary { background: linear-gradient(135deg, var(--blue), #6D5CF5, var(--purple)); box-shadow: 0 12px 28px rgba(124,58,237,.3); }

/* --- CTA light band + gradient headline --- */
.cta-section { position: relative; padding: 78px 0 120px; }
.cta-section::before { content: ""; position: absolute; z-index: -1; left: 50%; transform: translateX(-50%); width: 100vw; top: -20px; bottom: -1px; background: linear-gradient(180deg, rgba(240,241,251,0) 0, #eef0fb 96px, #f4f5fe 100%); }
.cta-card h2 span { background: linear-gradient(100deg, #4da0ff, #b17bff); -webkit-background-clip: text; background-clip: text; color: transparent; }
.cta-analyze.button-primary { background: linear-gradient(135deg, var(--blue), #6D5CF5, var(--purple)); box-shadow: 0 14px 32px rgba(124,58,237,.4); }

/* --- Footer redesign --- */
/* 2026-09-04: dropped the circle (border-radius, background, border) per
   explicit request to try plain icon glyphs instead – kept size, color,
   and the hover lift/color-shift so hover feedback still works the same
   way, just without a chip shape around each icon. */
.footer-social { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 20px; }
/* 2026-09-04: swapped in the actual brand icon images provided (each a
   grey rounded-square badge, white glyph, baked into the PNG itself) in
   place of the hand-drawn SVG glyphs – no color/hover-tint needed anymore
   since the icon's own color is part of the image, not currentColor. One
   thing worth knowing: the Instagram asset specifically has a solid
   square background with no rounded corners (checked the actual pixel
   data – its corners are fully opaque, unlike the other three, which have
   transparent corners around a rounded shape) – that inconsistency is in
   the source file, not something introduced here, and would need a
   corrected export from wherever these came from to fix. */
/* 2026-09-04: sized to match .fn-form button (the email signup arrow),
   which is 48x48px per its own !important rule further down – per
   explicit request. */
.social-btn { width: 43.2px; height: 43.2px; display: grid; place-items: center; transition: transform .25s, opacity .25s; opacity: .85; }
.social-btn img { width: 100%; height: 100%; object-fit: contain; }
.social-btn:hover { opacity: 1; transform: translateY(-2px); }
.footer-col strong { display: flex; align-items: center; gap: 8px; color: #fff; }
.fc-ic { width: 18px; height: 18px; color: var(--purple); }
/* 2026-09-04: explicit width added for the new flex-based footer-grid
   (see above) – without one, this card's "natural" width would be
   whatever an unconstrained input field prefers, which isn't predictable
   the way an equal grid column's width was. */
/* 2026-09-04: border/background box removed per explicit request. Padding
   removed too – without a visible box there's nothing for it to pad away
   from, and keeping it would've offset this column's content start point
   relative to Product/Trust/Built For, which have none. */
.footer-news { width: 320px; flex-shrink: 1; align-items: stretch !important; gap: 14px !important; }
/* 2026-09-04: two fixes together, since they were the same underlying
   cause. (1) Box/background/border removed per explicit request, to match
   the plain social icons below. (2) This icon was 42x42px against
   .fc-ic's 18x18px (the icon used in Product/Trust/Built For's own
   headings) – that size difference was almost certainly why "Stay
   Informed" sat visibly lower than the other three column headers even
   with align-items:flex-start on the grid: a much taller icon box shifts
   where the row's own content starts. Matched to .fc-ic's exact size and
   .footer-col strong's alignment so all four headers sit on the same
   line. */
.fn-head { display: flex; gap: 8px; align-items: center; }
.fn-ic { width: 18px; height: 18px; flex: 0 0 auto; display: grid; place-items: center; color: var(--purple); }
/* 2026-09-05, ACTUAL ROOT CAUSE of this icon showing white instead of
   purple – confirmed empirically with a headless-browser cascade check,
   not guessed: ".footer-grid button, .footer-grid span" (an older rule
   further down this file) sets color on every span in this grid, and as
   a class+type selector it outranks a single class like .fn-ic
   regardless of which one comes later in the file. .fc-ic never had this
   problem because it's an <svg> element, not a <span> – that selector
   simply never matched it, which is exactly why one icon rendered purple
   and the other didn't. Scoped here the same way the Built For pill
   padding bug was fixed earlier: an extra ancestor class the other rule
   doesn't have, so this one wins instead. */
.footer-news .fn-ic { color: var(--purple); }
.fn-ic svg { width: 18px; height: 18px; }
.fn-head strong { color: #fff; display: block; margin: 0; }
.fn-head small { color: rgba(255,255,255,.55); font-size: .82rem; display: block; margin-top: 3px; line-height: 1.4; }
.fn-form { display: flex; gap: 8px; }
.fn-form input { flex: 1; min-width: 0; padding: 11px 14px; border-radius: 12px; background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.12); color: #fff; font-size: .88rem; }
.fn-form input::placeholder { color: rgba(255,255,255,.4); }
.fn-form input:focus { outline: none; border-color: rgba(124,58,237,.6); background: rgba(255,255,255,.08); }
.fn-form button { width: 46px; flex: 0 0 auto; border: 0; border-radius: 12px; display: grid; place-items: center; color: #fff; background: linear-gradient(135deg, var(--blue), var(--purple)); cursor: pointer; }
.fn-form button svg { width: 18px; height: 18px; }
.fn-note { color: rgba(255,255,255,.42) !important; font-size: .76rem !important; }
/* 2026-09-06: sits directly under .fn-note, same muted treatment so it reads
   as a quiet footnote rather than competing with the signup field above it.
   display:block because both are <small>, which is inline by default and
   would otherwise run the two lines together. */
.fn-contact { display: block; margin-top: 6px; color: rgba(255,255,255,.42) !important; font-size: .76rem !important; }
.fn-contact a { color: var(--purple); text-decoration: none; }
.fn-contact a:hover { text-decoration: underline; }
/* 2026-09-04, ACTUAL ROOT CAUSE of the bottom-left/right footer text
   rendering as broken fragments instead of normal paragraphs – found by
   grepping every single rule that touches these spans, not by guessing at
   container widths again. This rule turned EVERY span inside
   .footer-bottom into its own flex container. For .fb-copyright,
   .fb-nonprofit, and .footer-credits's two lines, that means: the text
   before a link, the <a> itself, and the text after it each became
   separate flex items placed side by side (gap:8px, align-items:center)
   instead of one continuous, wrapping sentence – which is exactly what
   "your donations" sitting oddly next to "are fully tax-deductible." was.
   None of the last three fixes (flex-basis tuning, grid minmax, removing
   max-width) touched this because they were all reasoning about "is the
   container wide enough," not "why is this paragraph being split into
   pieces in the first place." Confirmed this rule is truly vestigial
   before removing it, not just inconvenient: .fb-ic, the icon class it
   sits next to, isn't referenced anywhere in the current HTML – this
   dates from an older footer layout (short icon+text pairs) that no
   longer exists, and nothing today depends on it. */
.fb-ic { width: 15px; height: 15px; color: var(--teal); }
.fb-ic.heart { color: var(--purple); }

/* --- Mobile polish: trust bars stack --- */
@media (max-width: 760px) {
  .trust-bar { grid-template-columns: 1fr 1fr; gap: 6px 4px; }
  .trust-item { border-right: 0 !important; justify-content: flex-start; padding: 8px 10px; }
  .story-trust { grid-template-columns: 1fr; }
}

/* ============================================================
   MODAL SYSTEM – dark redesign (game picker + info modals)
   ============================================================ */
.modal-backdrop, .analysis-overlay { background: rgba(3,4,14,.72); backdrop-filter: blur(20px) saturate(120%); }
.modal {
  background: linear-gradient(165deg, rgba(24,19,52,.985), rgba(12,11,30,.99));
  border: 1px solid rgba(182,92,250,.22);
  box-shadow: 0 50px 140px rgba(3,2,16,.72), 0 0 64px rgba(124,58,237,.14), inset 0 1px 0 rgba(255,255,255,.06);
  color: rgba(255,255,255,.7);
}
.modal-header small { color: var(--brand-purple); }
.modal-header h2 { color: #fff; }
.modal .icon-button { color: #fff; background: rgba(255,255,255,.07); border-color: rgba(255,255,255,.14); }
.modal .icon-button:hover { background: rgba(255,255,255,.13); border-color: rgba(255,255,255,.2); }
.modal-intro { color: rgba(255,255,255,.6); }
.modal-content { color: rgba(255,255,255,.66); }
.modal-content h3 { color: #fff; }
.modal-content .domain-cloud span { background: rgba(10,132,255,.14); color: #8fbaff; border: 1px solid rgba(10,132,255,.26); }
.modal-footer { border-top: 1px solid rgba(255,255,255,.1); }
.modal-footer .button-quiet { background: rgba(255,255,255,.07); color: #fff; border-color: rgba(255,255,255,.14); box-shadow: none; }
.modal-footer .button-quiet:hover { background: rgba(255,255,255,.13); }
#confirmGameButton.button-primary { background: linear-gradient(135deg, var(--blue), #6D5CF5, var(--purple)); box-shadow: 0 12px 30px rgba(124,58,237,.4); }

/* Game match cards */
.game-match { background: rgba(255,255,255,.045); border: 2px solid rgba(255,255,255,.09); }
.game-match:hover { border-color: rgba(255,255,255,.18); box-shadow: 0 18px 44px rgba(0,0,0,.45); }
.game-match.selected { border-color: var(--blue); box-shadow: 0 0 0 4px rgba(10,132,255,.18), 0 18px 46px rgba(10,132,255,.22); }
.game-match-body strong { color: #fff; }
.game-match-body small { color: rgba(255,255,255,.55); }
.game-match-tags span { color: rgba(255,255,255,.7); background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.13); }

/* ============================================================
   HERO VISUAL – elevated frame, cards, and motion
   ============================================================ */
.hero-photo-frame {
  border: 1px solid rgba(180,130,255,.3) !important;
  box-shadow: 0 52px 120px rgba(0,0,0,.5), 0 0 90px rgba(124,58,237,.22), inset 0 1px 0 rgba(255,255,255,.3) !important;
}
.hero-photo-frame::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none; border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.1), inset 0 -70px 90px rgba(124,58,237,.2);
}
.hero-photo-frame figcaption { color: rgba(255,255,255,.84); }

.decision-card {
  background: linear-gradient(150deg, rgba(27,22,60,.84), rgba(18,14,44,.82)) !important;
  border: 1px solid rgba(180,130,255,.32) !important;
  box-shadow: 0 30px 74px rgba(4,3,20,.5), 0 0 52px rgba(124,58,237,.2), inset 0 1px 0 rgba(255,255,255,.22) !important;
  backdrop-filter: blur(22px);
}
.decision-footer { display: flex; justify-content: space-between; align-items: center; margin-top: 12px; padding-top: 12px; border-top: 1px solid rgba(255,255,255,.1); }
.decision-footer strong { color: #fff; font-size: .82rem; }
.decision-footer span { color: rgba(255,255,255,.5); font-size: .74rem; }
.decision-card li svg { color: var(--teal); filter: drop-shadow(0 0 6px rgba(34,199,184,.5)); }

.shield-orb { position: relative; box-shadow: 0 0 24px rgba(124,58,237,.3); }
.floating-score { border: 1px solid rgba(180,130,255,.3); box-shadow: 0 22px 54px rgba(4,3,20,.42), 0 0 40px rgba(124,58,237,.18); backdrop-filter: blur(20px); }
.floating-score > span { background: linear-gradient(135deg, var(--blue), #6D5CF5, var(--purple)); }
.floating-chip { box-shadow: 0 20px 50px rgba(0,0,0,.34), 0 0 30px rgba(124,58,237,.14), inset 0 1px 0 rgba(255,255,255,.2); }

@keyframes heroPop { from { opacity: 0; transform: translateY(16px) scale(.955); } to { opacity: 1; transform: translateY(0) scale(1); } }
@keyframes heroFloatY { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-11px); } }
@keyframes heroZoom { 0%,100% { transform: scale(1.04); } 50% { transform: scale(1.085); } }
@keyframes heroShine { 0% { transform: translateX(-230%) rotate(18deg); } 55%,100% { transform: translateX(360%) rotate(18deg); } }
@keyframes heroRingPulse { 0% { transform: scale(1); opacity: .65; } 75%,100% { transform: scale(1.6); opacity: 0; } }
@keyframes heroLiPop { from { opacity: 0; transform: translateX(-8px); } to { opacity: 1; transform: translateX(0); } }

@media (prefers-reduced-motion: no-preference) {
  .hero-photo-frame img { animation: heroZoom 18s ease-in-out infinite; }
  .photo-reflection { animation: heroShine 7.5s ease-in-out infinite; animation-delay: 2s; }

  .floating-chip, .floating-score, .decision-card {
    animation: heroPop .8s cubic-bezier(.16,1,.3,1) both, heroFloatY 7s ease-in-out infinite 1s;
  }
  .decision-card { animation-delay: .15s, 1.1s; animation-duration: .8s, 9s; }
  .floating-score { animation-delay: .3s, 1.2s; animation-duration: .8s, 7.5s; }
  .chip-one { animation-delay: .45s, 1s; animation-duration: .8s, 6s; }
  .chip-two { animation-delay: .6s, 1.4s; animation-duration: .8s, 8s; }

  .floating-score > span { background-size: 200% 200%; animation: hehGradient 5.5s ease infinite; }
  .shield-orb::after { content: ""; position: absolute; inset: -1px; border-radius: inherit; border: 1px solid rgba(180,130,255,.55); animation: heroRingPulse 2.8s ease-out infinite; }

  .decision-card li { opacity: 0; animation: heroLiPop .5s ease-out forwards; }
  .decision-card li:nth-child(1) { animation-delay: .6s; }
  .decision-card li:nth-child(2) { animation-delay: .72s; }
  .decision-card li:nth-child(3) { animation-delay: .84s; }
  .decision-card li:nth-child(4) { animation-delay: .96s; }
  .decision-card li:nth-child(5) { animation-delay: 1.08s; }
}

/* ============================================================
   REPORT – dark redesign (screen only; print stays light)
   ============================================================ */
html { overflow-x: clip; }
@media screen {
  /* border/box-shadow explicitly killed 2026-09-01, not just omitted: this
     rule used to set its own (rgba(255,255,255,.1) border, a heavy drop
     shadow) which wrapped the entire content+TOC area in a third outer
     frame on top of report-hero's and report-nav's own boxes – a real,
     if faint, nested-box effect once those two had their own card
     treatment. Simply removing the declarations here let the BASE rule's
     border/shadow (#E9EEF5 / var(--shadow-md), from the original light-
     theme version of this selector) show through instead of nothing, so
     both are set to none explicitly rather than left unset. */
  .report-main { background: linear-gradient(165deg, rgba(20,17,46,.94), rgba(11,12,29,.96)); border: none; box-shadow: none; }
  /* .report-nav's dark-redesign background/border-bottom/button colors
     lived here – removed 2026-09-02, superseded by the consolidated
     rebuild (all of .report-nav's button styling, active state included,
     now lives in one place instead of layered across passes like this). */

  .report-kicker small { color: var(--brand-purple); }
  .report-date { color: rgba(255,255,255,.5); }
  .report-hero {
    background: var(--surface-1, #161926);
    border: 1px solid var(--line-1, rgba(255,255,255,.09));
    border-radius: 16px;
    /* 2026-09-01: 2x the 14px rail gap, per client request, so the copy's
       inset reads as a clear multiple of the same spacing unit rather than
       an unrelated number (was 30px/32px, picked with no particular tie
       to anything else on the page). */
    padding: 28px;
    margin-bottom: 28px;
  }
  .report-hero h1 { color: #fff; }
  .report-hero > p { color: rgba(255,255,255,.64); }

  .quick-stat { background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.09); }
  .quick-stat svg { color: var(--cyan); }
  .quick-stat strong { color: #fff; }
  .quick-stat span { color: rgba(255,255,255,.5); }

  .disclaimer-box { background: rgba(247,216,74,.08); color: #efe4a8; border: 1px solid rgba(247,216,74,.22); }

  .report-section { border-bottom: 1px solid rgba(255,255,255,.09); }
  .report-section-head h2 { color: #fff; }
  .report-section-head p { color: rgba(255,255,255,.55); }

  .insight-card { background: rgba(255,255,255,.035); border: 1px solid rgba(255,255,255,.09); }
  .insight-card h3 { color: #fff; }
  .insight-card li { color: rgba(255,255,255,.72); }

  .report-category { background: linear-gradient(135deg, color-mix(in srgb, var(--cat) 15%, rgba(18,16,42,.92)), rgba(12,12,30,.94) 60%); border: 1px solid color-mix(in srgb, var(--cat) 28%, rgba(255,255,255,.06)); }
  .category-heading h3 { color: #fff; }
  .category-summary { color: rgba(255,255,255,.64); }
  .category-toggle { background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.16); color: #fff; }
  .detail-block { background: none; border: 0; border-top: 1px solid rgba(255,255,255,.10); }
  .detail-block:first-child { border-top: 0; }
  .detail-block h4 { color: #fff; }
  .detail-block p, .detail-block ul { color: rgba(255,255,255,.7); }
  /* No tinted card any more – the amber heading and its icon carry the
     warning, and keeping the block flat keeps it in the same column as
     everything else in the category. */
  .detail-block.gap-block h4 { color: #F5A623; }
  .detail-block.gap-block h4 svg { color: #F5A623 !important; }

  .source-row { background: none; border: 0; border-top: 1px solid rgba(255,255,255,.08); }
  .source-row:first-child { border-top: 0; }
  .source-row strong { color: #fff; }
  .source-row span { color: rgba(255,255,255,.6); }
  .source-row small { color: rgba(255,255,255,.4); }
  .source-row a { color: #8fbaff; background: rgba(10,132,255,.15); border: 1px solid rgba(10,132,255,.28); }


  /* 2026-09-05: shadow tightened (was 0 24px 60px + a 44px outer glow) –
     best explanation I have for the "artifacts in the top corners":
     .report-main has overflow:hidden, and a shadow this large very likely
     extends past its boundary and gets abruptly clipped right at the
     rounded corners, which reads as a visible seam rather than a soft
     shadow. That risk is larger now that the band reaches report-main's
     actual edge (the full-width fix above), not just report-content's
     inset. Flagging this honestly: I can't render this live to confirm
     it's fixed, only that the mechanism is real and this reduces the
     chance of it. */
  .report-actions-band { background: linear-gradient(135deg, rgba(27,22,62,.92), rgba(13,42,82,.88)); border: 1px solid rgba(180,130,255,.26); box-shadow: 0 10px 26px rgba(4,3,20,.35); }
}

/* ============================================================
   BUTTERY MOTION – report entrance + micro-interactions
   ============================================================ */
@media (prefers-reduced-motion: no-preference) {
  .report-shell { animation: reportIn .75s cubic-bezier(.16,1,.3,1) both; }
  @keyframes reportIn { from { opacity: 0; transform: translateY(22px); } to { opacity: 1; transform: none; } }
  .step-card, .mini-category, .source-row, .insight-card, .story-points article, .game-match, .quick-stat {
    transition: transform .32s cubic-bezier(.16,1,.3,1), box-shadow .32s ease, border-color .32s ease, background .3s ease;
  }
  .mini-category:hover, .quick-stat:hover { transform: translateY(-3px); }
}
.button { transition: transform .3s cubic-bezier(.16,1,.3,1), box-shadow .3s ease, filter .3s ease, background .3s ease; }
.button:hover { filter: brightness(1.06); }
.button:active { transform: scale(.985); }

/* ============================================================
   CARD & COVER POLISH – game-match cards, report cover, nav
   ============================================================ */
@media screen {
  /* .report-nav lived here too (position:static, its own background,
     border-radius) – removed 2026-09-02, superseded by the consolidated
     rebuild. Worth noting: this had no width qualifier at all, applying
     at every screen size, and it was setting position:static – directly
     undermining the sticky positioning every other pass was trying to
     establish. */
}
.report-app { scroll-margin-top: 116px; }

/* Game-match cover – gaming texture + monogram */
.game-match-cover { position: relative; aspect-ratio: 16 / 7.6; display: grid; place-items: center; overflow: hidden; color: #fff; background: linear-gradient(152deg, var(--match-accent, #0A84FF) 0%, #0a1a3a 78%, #060f24 100%); }
.game-match-cover::before { content: ""; position: absolute; inset: 0; background: url('./assets/hero-cockpit.svg') center / 158% no-repeat; opacity: .22; mix-blend-mode: soft-light; }
.game-match-cover::after { content: ""; position: absolute; inset: 0; background: radial-gradient(120% 90% at 28% 0%, rgba(255,255,255,.24), transparent 55%), linear-gradient(0deg, rgba(4,10,26,.55), transparent 58%); }
.game-match-cover span { position: relative; z-index: 1; font-size: 2.3rem; font-weight: 900; letter-spacing: .01em; color: #fff; text-shadow: 0 4px 22px rgba(0,0,0,.42); }
.game-match-cover img { position: relative; z-index: 1; }

/* Game-match body – refined spacing */
.game-match-body { padding: 17px 19px 19px; display: flex; flex-direction: column; }
.game-match-body strong { font-size: 1.02rem; line-height: 1.24; letter-spacing: -.01em; }
.game-match-body small { margin-top: 5px; font-size: .8rem; line-height: 1.4; color: rgba(255,255,255,.6); }
.game-match-tags { margin-top: 14px; gap: 8px; row-gap: 8px; }
.game-match-tags span { padding: 5px 10px; border-radius: 9px; font-size: .68rem; line-height: 1.35; white-space: nowrap; }

/* Report cover – same premium treatment */
.report-cover { aspect-ratio: 4 / 4.4; }
/* .report-cover::after (the hero-cockpit.svg decorative overlay) removed
   2026-09-01: it's a complex multi-shape illustration blended at 18%
   opacity via mix-blend-mode:soft-light, and its own background rect
   (inset from the SVG viewBox edges, rx:84) was visibly bleeding through
   at the corners of real cover art as what read like rendering artifacts
   – a rounded shape cut into a sharp rectangle, tinted by whatever art
   was underneath. It was never a bug in the sense of "broken code," just
   a decoration that doesn't hold up over real photographic/box-art
   content the way it might over a flat gradient placeholder. */
.report-cover small, .report-cover strong { position: relative; z-index: 1; }

/* Hero decision card – checkmarks draw in with a pop */
@media (prefers-reduced-motion: no-preference) {
  @keyframes checkPop { 0% { opacity: 0; transform: scale(.2) rotate(-12deg); } 60% { transform: scale(1.16) rotate(0); } 100% { opacity: 1; transform: scale(1); } }
  .decision-card li svg { animation: checkPop .5s cubic-bezier(.2,1.5,.4,1) both; }
  .decision-card li:nth-child(1) svg { animation-delay: .68s; }
  .decision-card li:nth-child(2) svg { animation-delay: .8s; }
  .decision-card li:nth-child(3) svg { animation-delay: .92s; }
  .decision-card li:nth-child(4) svg { animation-delay: 1.04s; }
  .decision-card li:nth-child(5) svg { animation-delay: 1.16s; }
}

/* Hamburger: mobile only (desktop uses the horizontal nav) */
@media (max-width: 980px) { .menu-toggle { display: inline-grid; } }

/* Source chips – neutral but prominent */
.modal-content .domain-cloud { display: flex; flex-wrap: wrap; gap: 8px; }
.modal-content .domain-cloud span {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 13px 8px 12px;
  border-radius: 11px;
  background: rgba(255,255,255,.05) !important;
  color: rgba(255,255,255,.82) !important;
  border: 1px solid rgba(255,255,255,.12) !important;
  font-size: .8rem; font-weight: 600; letter-spacing: .005em;
  transition: background .22s var(--ease), border-color .22s var(--ease), transform .22s var(--ease), color .22s var(--ease), box-shadow .22s var(--ease);
}
.modal-content .domain-cloud span::before {
  content: ""; flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%;
  background: linear-gradient(135deg, var(--cyan), var(--purple));
  box-shadow: 0 0 9px rgba(124,58,237,.55);
}
.modal-content .domain-cloud span:hover {
  background: rgba(255,255,255,.09) !important;
  border-color: rgba(124,58,237,.4) !important;
  color: #fff !important; transform: translateY(-2px);
  box-shadow: 0 8px 20px rgba(4,3,20,.4);
}

/* ============================================================
   RESPONSIVE HARDENING
   ============================================================ */
/* Collapse the horizontal nav into the drawer before uppercase items crowd */
@media (max-width: 1100px) {
  .primary-nav { display: none; }
  .menu-toggle { display: inline-grid; }
  .steps-grid { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
  .steps-timeline { display: none; }
}
@media (max-width: 560px) {
  .step-card { min-height: 210px; }
}

/* Mobile/tablet hero: copy-focused; decorative visual hidden to prevent overlap */
@media (max-width: 980px) {
  .hero-visual { display: none !important; }
  .hero { min-height: 0 !important; padding-bottom: 44px !important; }
  .hero-copy { max-width: 100% !important; }
}

/* ============================================================
   MOBILE AUDIT FIXES (390–430px sweep, 2026-07-21)
   Appended last so these win the cascade at equal specificity.
   ============================================================ */

/* Old-browser fallback: some engines ignore overflow-x: clip */
@supports not (overflow: clip) {
  html, body { overflow-x: hidden; }
}

/* 1 · Hero must collapse to one column below 980px.
   (The theme layer's 1240px rule re-imposed two columns because it
   appears later in the file than the original 980px collapse.) */
@media (max-width: 980px) {
  .hero { grid-template-columns: 1fr; }
  .hero-copy { max-width: 100%; }
  .search-panel { width: 100%; }
}

/* 2 · Phone-scale hero type: the 15vw clamp rendered ~59px lines
   with one word per line at 390px. */
@media (max-width: 720px) {
  .hero h1 { font-size: clamp(2.6rem, 11.5vw, 3.6rem); line-height: .98; letter-spacing: -.045em; }
  .hero { padding-top: 46px; }
  .hero::before { width: 420px; height: 420px; right: -160px; filter: blur(64px); }
}
@media (max-width: 430px) {
  .hero h1 { font-size: clamp(2.35rem, 11vw, 2.9rem); }
  .section-heading h2 { font-size: 2.1rem; }
  .story-copy h2 { font-size: 2.2rem; }
}

/* 3 · Decorative layers may never widen the page on phones. */
@media (max-width: 720px) {
  .ambient { max-width: 92vw; }
  .ambient-two { right: -34vw; }
  .ambient-three { left: 30%; max-width: 80vw; }
  .story-orb { width: min(430px, 88vw); height: min(430px, 88vw); right: -18vw; }
  .card-wm { right: 4px; bottom: -14px; width: 168px; height: 168px; }
  .hero-visual { overflow: clip; border-radius: 34px; }
}

/* 4 · Tap targets: 44px minimum on touch-critical controls. */
.search-footer { gap: 6px 2px; }
.search-footer button {
  min-height: 44px; padding: 10px 12px; border-radius: 12px;
  background: rgba(255,255,255,.05);
}
.footer-grid button {
  min-height: 44px; display: inline-flex; align-items: center;
  padding: 10px 0; margin: -5px 0;
}
.footer-grid > div:not(.footer-brand) { gap: 2px; }
/* social-btn's own rule already sets 46px, which clears this 44px tap-
   target minimum on its own – redundant declaration removed 2026-09-04 so
   it can't silently shrink the intentionally-larger size back down. */
.report-nav button { min-height: 44px; padding: 11px 13px; }
.preview-tabs span { padding-block: 13px; }
.brand { padding: 4px 6px; margin-left: -6px; border-radius: 12px; }
@media (max-width: 720px) {
  .menu-toggle { width: 44px; height: 44px; }
  .new-analysis-button { min-width: 46px; width: 46px; min-height: 46px; }
}

/* 5 · Breathing room for the stacked hero on phones. */
@media (max-width: 720px) {
  .hero { gap: 26px; }
  .hero-visual { min-height: 400px; }
  .decision-card { right: -6px; }
}

/* 6 · Footer credits link needs a comfortable touch size. */
.footer-credits a { display: inline-block; padding: 12px 4px; margin: -10px 0; }

/* ============================================================
   MOBILE PASS 2 (2026-07-22): app-like viewport lock + nav fixes
   ============================================================ */

/* 1 · Kill pinch-zoom and zoom-panning at the gesture level.
   pan-y still allows normal vertical scrolling; manipulation on
   controls removes double-tap-to-zoom and the 300ms tap delay. */
html, body { touch-action: pan-y; }
button, a, input, select, textarea, [role="button"], [role="tab"] { touch-action: pan-x pan-y; }
body {
  overscroll-behavior-y: none;   /* no pull-to-refresh / rubber-band pan */
  -webkit-tap-highlight-color: transparent;
}

/* 2 · iOS auto-zooms into inputs below 16px – never trigger it. */
.fn-form input { font-size: 16px; }

/* 3 · Keep the header (and its ☰/✕ toggle) above the drawer backdrop
   so the button stays tappable while the menu is open.
   (backdrop z-70, drawer z-80, header was z-50 → buried.) */
.site-header { z-index: 75; }

/* 4 · Drawer must never run off short screens; scroll inside instead. */
.nav-drawer {
  max-height: calc(100vh - 96px);
  max-height: calc(100dvh - 96px);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

/* 5 · Scrollable tab strips shouldn't chain-scroll the page. */
.preview-tabs, .report-nav { overscroll-behavior-x: contain; touch-action: pan-x; }

/* ============================================================
   MOBILE PASS 3 (2026-07-22): overlap fixes + buttery motion
   ============================================================ */

/* 1 · Framework cards: the "Explore category" button is pinned to the
   card bottom, so the card must always reserve that zone – long
   descriptions were flowing underneath the label. */
.framework-card { padding-bottom: 84px; }
.framework-card p { margin-bottom: 0; }
@media (max-width: 720px) {
  .framework-card { min-height: 0; }  /* let content + reserved zone size the card */
}

/* 2 · Buttery reveals: small fade + rise, short and soft. Blur and scale
   transitions are dropped – they read as chunky and are expensive on
   mobile GPUs. Above-the-fold content never gets .reveal-ready at all. */
@media screen {
  .reveal-ready {
    opacity: 0;
    translate: 0 12px;
    scale: 1;
    filter: none;
    transition: opacity .45s cubic-bezier(.25,.46,.45,.94),
                translate .5s cubic-bezier(.22,.61,.21,1);
    transition-delay: var(--reveal-delay, 0ms);
  }
  .reveal-ready.is-visible { opacity: 1; translate: 0 0; }
}

/* 3 · Swipeable strips: fade the clipped edge so truncated tabs read as
   "more to scroll", not broken text.

   2026-09-06: .report-nav removed from this rule and re-applied below only
   at the widths where it IS a swipeable strip. Above the rail breakpoint the
   report nav is a stationary vertical list – nothing is clipped and nothing
   scrolls horizontally – so the fade had no job to do there and simply
   dissolved the right-hand side of a solid card, taking the background,
   border and the active item's fill with it. A horizontal edge-fade only
   makes sense on something with a horizontal edge to hide. */
.preview-tabs {
  -webkit-mask-image: linear-gradient(90deg, #000 90%, transparent 99%);
  mask-image: linear-gradient(90deg, #000 90%, transparent 99%);
}
@media screen and (max-width: 1279px) {
  .report-nav {
    -webkit-mask-image: linear-gradient(90deg, #000 90%, transparent 99%);
    mask-image: linear-gradient(90deg, #000 90%, transparent 99%);
  }
}

/* 4 · Touch devices get gentler card motion (no hover-lift jumps). */
@media (hover: none) {
  .framework-card:hover, .step-card:hover { transform: none; }
  .hero-trust-grid article:hover { transform: none; }
}

/* ============================================================
   BRAND PASS (2026-07-22): official healthe Gaming logo
   ============================================================ */
.brand img { height: auto; }            /* keep the wordmark's true ratio */
@media (max-width: 720px) {
  .brand img { width: 78px; height: auto; }
}
.footer-brand img { height: auto; }

/* ============================================================
   NAV PASS (2026-07-22): premium drawer + clear "+" action
   ============================================================ */

/* Safe areas: keep the floating header out of the notch and give the
   sheet breathing room above the home indicator. */
@media (max-width: 720px) {
  .site-header { padding-top: max(9px, env(safe-area-inset-top)); }
}
.nav-drawer { padding-bottom: max(14px, env(safe-area-inset-bottom)); }

/* Drawer surface: deeper glass, purple hairline, springier drop-in. */
.nav-drawer {
  background:
    radial-gradient(circle at 80% 0%, rgba(124,58,237,.28), transparent 42%),
    linear-gradient(165deg, rgba(26,20,56,.97), rgba(11,10,28,.97));
  border: 1px solid rgba(182,92,250,.34);
  box-shadow: 0 34px 90px rgba(4,3,16,.7), 0 0 52px rgba(124,58,237,.26),
              inset 0 1px 0 rgba(255,255,255,.09);
  -webkit-backdrop-filter: blur(24px) saturate(140%);
  backdrop-filter: blur(24px) saturate(140%);
  transform: translateY(-16px) scale(.96);
  transition: opacity .3s var(--ease),
              transform .44s cubic-bezier(.3, 1.35, .4, 1),
              visibility .3s;
}
.nav-drawer.open { transform: translateY(0) scale(1); }
.nav-drawer-backdrop {
  background: rgba(3, 4, 14, .62);
  -webkit-backdrop-filter: blur(6px) saturate(120%);
  backdrop-filter: blur(6px) saturate(120%);
}
.nav-drawer-grabber { opacity: .6; }

/* Gentle stagger: items settle in one after another on open,
   reset instantly on close. */
.nav-drawer .drawer-link, .nav-drawer .drawer-cta {
  opacity: 0;
  translate: 0 -8px;
}
.nav-drawer.open .drawer-link, .nav-drawer.open .drawer-cta {
  opacity: 1;
  translate: 0 0;
  transition: opacity .3s ease, translate .38s cubic-bezier(.22,.61,.21,1),
              background .18s var(--ease), transform .18s var(--ease);
}
.nav-drawer.open .drawer-link:nth-child(1) { transition-delay: 40ms; }
.nav-drawer.open .drawer-link:nth-child(2) { transition-delay: 80ms; }
.nav-drawer.open .drawer-link:nth-child(3) { transition-delay: 120ms; }
.nav-drawer.open .drawer-link:nth-child(4) { transition-delay: 160ms; }
.nav-drawer.open .drawer-link:nth-child(5) { transition-delay: 200ms; }
.nav-drawer.open .drawer-cta { transition-delay: 250ms; }

/* Clear active state for the current section. */
.drawer-link.is-active {
  background: linear-gradient(90deg, rgba(124,58,237,.26), rgba(124,58,237,.08));
}
.drawer-link.is-active .dl-icon { background: rgba(124,58,237,.34); color: #E7D9FF; }

/* Touch feedback: quick, tactile presses. */
.drawer-link:active { background: rgba(124,58,237,.3); transform: scale(.98); transition-duration: .08s; }
.menu-toggle:active { transform: scale(.9); transition: transform .1s var(--ease); }
.new-analysis-button:active { transform: scale(.94); transition: transform .1s var(--ease); }

/* "+" button: labelled at every size – icon-only squares read as mystery
   buttons. Desktop keeps "New Analysis", phones get a compact "+ New". */
.na-short { display: none; }
@media (max-width: 720px) {
  .new-analysis-button {
    width: auto; min-width: 0;
    padding: 0 15px 0 12px; gap: 6px;
    font-size: .84rem;
  }
  .new-analysis-button svg { width: 16px; height: 16px; }
  .na-long { display: none; }
  .na-short { display: inline; font-size: .84rem; }
}

/* Reduced motion: everything appears instantly. */
@media (prefers-reduced-motion: reduce) {
  .nav-drawer .drawer-link, .nav-drawer .drawer-cta {
    opacity: 1 !important; translate: none !important; transition: none !important;
  }
}

/* NAV PASS fixups: the old `.new-analysis-button span { font-size: 0 }`
   rule outranked .na-short – match its specificity. */
@media (max-width: 720px) {
  .new-analysis-button .na-short { font-size: .84rem; display: inline; }
  .new-analysis-button .na-long { display: none; }
}

/* ============================================================
   PERFORMANCE PASS (2026-07-22): 60fps mobile
   ============================================================ */

/* 1 · Phones: no infinite paint/composite work.
   background-position animations repaint every frame (flagged by
   Lighthouse as non-composited), and huge blurred/blended layers tax
   mobile GPUs – freeze them to their resting state on small screens. */
@media (max-width: 720px) {
  .hero h1 span,
  .button-primary,
  .cta-analyze,
  .floating-score > span { animation: none !important; }          /* hehGradient */
  .ambient { animation: none !important; mix-blend-mode: normal; opacity: .5; filter: blur(70px); }
  .hero::before { animation: none !important; }                    /* rotating blurred ring */
  .orbit-one, .orbit-two { animation: none !important; }
  .hero-photo-frame img { animation: none !important; }            /* slow Ken Burns zoom */
  .photo-reflection { animation: none !important; }
  .decision-card { animation-name: heroPop !important; }           /* keep entrance, drop endless float */
}

/* 2 · Header shrink-on-scroll animated layout (padding/min-height) every
   frame at the scroll boundary – phones get a fixed-size header and
   cheap transitions only. */
@media (max-width: 720px) {
  .site-header { transition: none; }
  .site-header.is-compact { padding-top: max(9px, env(safe-area-inset-top)); }
  .site-header.is-compact .header-shell { min-height: 66px; }
  .header-shell { transition: background .35s ease, border-color .35s ease; }
}

/* 3 · Instant tactile feedback (<100ms) on every control. */
.button:active,
.suggestion-option:active,
.game-match:active,
.search-footer button:active,
.footer-grid button:active,
.nav-link:active,
.category-toggle:active,
.report-nav button:active {
  transform: scale(.97);
  transition: transform .07s ease-out;
}

/* 4 · Hover-only shadow/lift work should never bind on touch devices. */
@media (hover: none) {
  .button, .step-card, .framework-card, .game-match, .suggestion-option {
    transition-property: transform, background, border-color, color, opacity;
  }
}

/* ============================================================
   DARK CONSISTENCY PASS (2026-07-22): no light sections
   ============================================================ */

/* The CTA band was the last light surface on the page – a full-width
   #eef0fb→#f4f5fe panel behind the closing card. It now matches the
   navy/purple system: a soft violet bloom over a deep panel. */
.cta-section::before {
  background:
    radial-gradient(46rem 22rem at 50% 30%, rgba(124, 58, 237, .18), transparent 66%),
    radial-gradient(30rem 16rem at 12% 85%, rgba(34, 199, 184, .08), transparent 60%),
    linear-gradient(180deg, rgba(10, 11, 30, 0), rgba(17, 15, 42, .78) 96px, rgba(9, 10, 26, .92) 100%);
}

/* Card treatment tuned for the dark surface: purple hairline + glow so it
   still lifts off the background the way it did off the light band. */
.cta-card {
  border: 1px solid rgba(182, 92, 250, .24);
  box-shadow: 0 34px 90px rgba(4, 3, 16, .6), 0 0 46px rgba(124, 58, 237, .16),
              inset 0 1px 0 rgba(255, 255, 255, .1);
}

/* ============================================================
   FOOTER MOBILE PASS (2026-07-22): compact, intentional stack
   ============================================================ */
@media (max-width: 720px) {
  /* Centered brand header */
  .footer-grid > div.footer-brand {
    grid-column: 1 / -1;
    display: flex; flex-direction: column; align-items: center;
    text-align: center; gap: 4px;
  }
  .footer-brand .footer-social { justify-content: center; }

  /* Product + Trust side by side instead of an endless single column */
  .footer-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 30px 18px; }

  /* "Built for" becomes a full-width chip row (they're labels, not links) */
  .footer-grid > div:nth-child(4) {
    grid-column: 1 / -1;
    flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px;
  }
  .footer-grid > div:nth-child(4) strong { flex-basis: 100%; margin-bottom: 4px; }
  .footer-grid > div:nth-child(4) span {
    padding: 8px 14px; border-radius: 999px;
    background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.11);
    font-size: .79rem; color: rgba(255,255,255,.62);
  }

  /* Newsletter card spans full width below the links */
  .footer-grid > div.footer-news { grid-column: 1 / -1; }

  /* Legal block: centered, calm rhythm, clear of the home indicator */
  .footer-bottom {
    flex-direction: column; align-items: center; text-align: center;
    gap: 12px; margin-top: 40px; padding-top: 22px;
  }
  .site-footer { padding-bottom: max(22px, calc(env(safe-area-inset-bottom) + 16px)); }
}

/* ============================================================
   SPLASH SCREEN (2026-07-22): pixel-art loader, fades when ready
   ============================================================ */
html.splash-lock { overflow: hidden; }
.splash {
  position: fixed; inset: 0; z-index: 400;
  display: grid; place-items: center;
  overflow: hidden;
  /* 2026-09-06: the coloured swirls are gone. This went nebula -> rotating
     galaxy -> plain dark sky with a pixel starfield, the last step per
     explicit request: the splash is a Space Invaders homage, and Invaders
     had a flat black field, not a nebula. The colour was competing with the
     pixel art in front of it rather than supporting it. Kept as a very dark
     gradient rather than flat black so the logo still sits on something
     with a little depth. */
  background: linear-gradient(165deg, #0a0518, #140A24 45%, #0b0518 75%, #060210);
  opacity: 1;
  transition: opacity .45s ease;
}

/* --- pixel starfield ---
   Stars are <rect>s on an integer grid inside an SVG, so every one is a hard
   square with no anti-aliased edge, at any screen size. preserveAspectRatio
   "slice" scales uniformly and crops the overflow, which keeps the stars
   SQUARE; the default "none" would stretch them into rectangles on a wide
   viewport, which is exactly what would give the pixel illusion away.

   The same global `svg { stroke: currentColor; stroke-width: 1.8 }` rule
   that swallowed the controller sprite would swallow these too – a 1.8-unit
   stroke on a 1-unit star. Cancelled here for the same reason, and the
   width/height overrides undo the global 1.15em icon sizing. */
.splash-stars {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  z-index: 0;
  pointer-events: none;
  stroke: none;
  shape-rendering: crispEdges;
}
/* Occasional, not rhythmic. Each star is dark for only a sliver of its own
   cycle (about 4%), so with long durations and staggered delays the field
   reads as the odd star winking rather than everything pulsing together –
   which is what a shared animation on one parent element would have looked
   like, and why the blinking stars are individual rects instead. */
@keyframes starBlink {
  0%, 38%, 100% { opacity: 1; }
  41%           { opacity: .08; }
  44%           { opacity: 1; }
}
.ss-blink rect { animation: starBlink 3s steps(1, end) infinite; }
/* Delays and durations spread by hand rather than generated, so no two
   neighbours blink close together. Primes-ish spacing avoids the whole set
   drifting back into phase the way round numbers would. */
.ss-blink rect:nth-child(1) { animation-delay: 0s; animation-duration: 2.6s; }
.ss-blink rect:nth-child(2) { animation-delay: 0.7s; animation-duration: 3.4s; }
.ss-blink rect:nth-child(3) { animation-delay: 1.5s; animation-duration: 2.2s; }
.ss-blink rect:nth-child(4) { animation-delay: 2.1s; animation-duration: 4.1s; }
.ss-blink rect:nth-child(5) { animation-delay: 0.4s; animation-duration: 3.1s; }
.ss-blink rect:nth-child(6) { animation-delay: 1.9s; animation-duration: 2.8s; }
.ss-blink rect:nth-child(7) { animation-delay: 1.1s; animation-duration: 3.7s; }
.ss-blink rect:nth-child(8) { animation-delay: 2.5s; animation-duration: 2.4s; }
.ss-blink rect:nth-child(9) { animation-delay: 0.2s; animation-duration: 4.4s; }
.ss-blink rect:nth-child(10) { animation-delay: 1.3s; animation-duration: 2.9s; }
.ss-blink rect:nth-child(11) { animation-delay: 2.3s; animation-duration: 3.3s; }
.ss-blink rect:nth-child(12) { animation-delay: 0.9s; animation-duration: 4.6s; }
.ss-blink rect:nth-child(13) { animation-delay: 1.7s; animation-duration: 2.5s; }
.ss-blink rect:nth-child(14) { animation-delay: 2.8s; animation-duration: 3.9s; }
.splash.hide { opacity: 0; pointer-events: none; }
/* Above the starfield, which is an absolutely positioned sibling in the same
   stacking context. */
.splash-inner { position: relative; z-index: 1; display: grid; justify-items: center; gap: 26px; padding: 20px; }

/* Pixel gamepad sprite: one node, one paint; only transform animates. */
.px-pad { --p: clamp(9px, 2.4vw, 14px); filter: drop-shadow(0 12px 26px rgba(124,58,237,.45)); width: calc(var(--p)*16); height: calc(var(--p)*9);
  animation: pxHop 1.1s steps(2, jump-none) infinite; }
.px-pad i {
  display: block; width: var(--p); height: var(--p);
  box-shadow:
    calc(var(--p)*2) calc(var(--p)*0) 0 .5px #241744,
    calc(var(--p)*3) calc(var(--p)*0) 0 .5px #241744,
    calc(var(--p)*4) calc(var(--p)*0) 0 .5px #241744,
    calc(var(--p)*5) calc(var(--p)*0) 0 .5px #241744,
    calc(var(--p)*10) calc(var(--p)*0) 0 .5px #241744,
    calc(var(--p)*11) calc(var(--p)*0) 0 .5px #241744,
    calc(var(--p)*12) calc(var(--p)*0) 0 .5px #241744,
    calc(var(--p)*13) calc(var(--p)*0) 0 .5px #241744,
    calc(var(--p)*1) calc(var(--p)*1) 0 .5px #241744,
    calc(var(--p)*2) calc(var(--p)*1) 0 .5px #8B3DEB,
    calc(var(--p)*3) calc(var(--p)*1) 0 .5px #8B3DEB,
    calc(var(--p)*4) calc(var(--p)*1) 0 .5px #8B3DEB,
    calc(var(--p)*5) calc(var(--p)*1) 0 .5px #8B3DEB,
    calc(var(--p)*6) calc(var(--p)*1) 0 .5px #241744,
    calc(var(--p)*9) calc(var(--p)*1) 0 .5px #241744,
    calc(var(--p)*10) calc(var(--p)*1) 0 .5px #8B3DEB,
    calc(var(--p)*11) calc(var(--p)*1) 0 .5px #8B3DEB,
    calc(var(--p)*12) calc(var(--p)*1) 0 .5px #8B3DEB,
    calc(var(--p)*13) calc(var(--p)*1) 0 .5px #8B3DEB,
    calc(var(--p)*14) calc(var(--p)*1) 0 .5px #241744,
    calc(var(--p)*0) calc(var(--p)*2) 0 .5px #241744,
    calc(var(--p)*1) calc(var(--p)*2) 0 .5px #8B3DEB,
    calc(var(--p)*2) calc(var(--p)*2) 0 .5px #8B3DEB,
    calc(var(--p)*3) calc(var(--p)*2) 0 .5px #8B3DEB,
    calc(var(--p)*4) calc(var(--p)*2) 0 .5px #8B3DEB,
    calc(var(--p)*5) calc(var(--p)*2) 0 .5px #8B3DEB,
    calc(var(--p)*6) calc(var(--p)*2) 0 .5px #8B3DEB,
    calc(var(--p)*7) calc(var(--p)*2) 0 .5px #241744,
    calc(var(--p)*8) calc(var(--p)*2) 0 .5px #241744,
    calc(var(--p)*9) calc(var(--p)*2) 0 .5px #8B3DEB,
    calc(var(--p)*10) calc(var(--p)*2) 0 .5px #8B3DEB,
    calc(var(--p)*11) calc(var(--p)*2) 0 .5px #8B3DEB,
    calc(var(--p)*12) calc(var(--p)*2) 0 .5px #8B3DEB,
    calc(var(--p)*13) calc(var(--p)*2) 0 .5px #8B3DEB,
    calc(var(--p)*14) calc(var(--p)*2) 0 .5px #8B3DEB,
    calc(var(--p)*15) calc(var(--p)*2) 0 .5px #241744,
    calc(var(--p)*0) calc(var(--p)*3) 0 .5px #241744,
    calc(var(--p)*1) calc(var(--p)*3) 0 .5px #8B3DEB,
    calc(var(--p)*2) calc(var(--p)*3) 0 .5px #C084FC,
    calc(var(--p)*3) calc(var(--p)*3) 0 .5px #71E6FF,
    calc(var(--p)*4) calc(var(--p)*3) 0 .5px #8B3DEB,
    calc(var(--p)*5) calc(var(--p)*3) 0 .5px #8B3DEB,
    calc(var(--p)*6) calc(var(--p)*3) 0 .5px #8B3DEB,
    calc(var(--p)*7) calc(var(--p)*3) 0 .5px #8B3DEB,
    calc(var(--p)*8) calc(var(--p)*3) 0 .5px #8B3DEB,
    calc(var(--p)*9) calc(var(--p)*3) 0 .5px #8B3DEB,
    calc(var(--p)*10) calc(var(--p)*3) 0 .5px #8B3DEB,
    calc(var(--p)*11) calc(var(--p)*3) 0 .5px #8B3DEB,
    calc(var(--p)*12) calc(var(--p)*3) 0 .5px #F0ABFC,
    calc(var(--p)*13) calc(var(--p)*3) 0 .5px #8B3DEB,
    calc(var(--p)*14) calc(var(--p)*3) 0 .5px #8B3DEB,
    calc(var(--p)*15) calc(var(--p)*3) 0 .5px #241744,
    calc(var(--p)*0) calc(var(--p)*4) 0 .5px #241744,
    calc(var(--p)*1) calc(var(--p)*4) 0 .5px #8B3DEB,
    calc(var(--p)*2) calc(var(--p)*4) 0 .5px #71E6FF,
    calc(var(--p)*3) calc(var(--p)*4) 0 .5px #71E6FF,
    calc(var(--p)*4) calc(var(--p)*4) 0 .5px #71E6FF,
    calc(var(--p)*5) calc(var(--p)*4) 0 .5px #8B3DEB,
    calc(var(--p)*6) calc(var(--p)*4) 0 .5px #8B3DEB,
    calc(var(--p)*7) calc(var(--p)*4) 0 .5px #8B3DEB,
    calc(var(--p)*8) calc(var(--p)*4) 0 .5px #8B3DEB,
    calc(var(--p)*9) calc(var(--p)*4) 0 .5px #8B3DEB,
    calc(var(--p)*10) calc(var(--p)*4) 0 .5px #8B3DEB,
    calc(var(--p)*11) calc(var(--p)*4) 0 .5px #F0ABFC,
    calc(var(--p)*12) calc(var(--p)*4) 0 .5px #8B3DEB,
    calc(var(--p)*13) calc(var(--p)*4) 0 .5px #F0ABFC,
    calc(var(--p)*14) calc(var(--p)*4) 0 .5px #8B3DEB,
    calc(var(--p)*15) calc(var(--p)*4) 0 .5px #241744,
    calc(var(--p)*0) calc(var(--p)*5) 0 .5px #241744,
    calc(var(--p)*1) calc(var(--p)*5) 0 .5px #8B3DEB,
    calc(var(--p)*2) calc(var(--p)*5) 0 .5px #C084FC,
    calc(var(--p)*3) calc(var(--p)*5) 0 .5px #71E6FF,
    calc(var(--p)*4) calc(var(--p)*5) 0 .5px #8B3DEB,
    calc(var(--p)*5) calc(var(--p)*5) 0 .5px #8B3DEB,
    calc(var(--p)*6) calc(var(--p)*5) 0 .5px #8B3DEB,
    calc(var(--p)*7) calc(var(--p)*5) 0 .5px #8B3DEB,
    calc(var(--p)*8) calc(var(--p)*5) 0 .5px #8B3DEB,
    calc(var(--p)*9) calc(var(--p)*5) 0 .5px #8B3DEB,
    calc(var(--p)*10) calc(var(--p)*5) 0 .5px #8B3DEB,
    calc(var(--p)*11) calc(var(--p)*5) 0 .5px #8B3DEB,
    calc(var(--p)*12) calc(var(--p)*5) 0 .5px #F0ABFC,
    calc(var(--p)*13) calc(var(--p)*5) 0 .5px #8B3DEB,
    calc(var(--p)*14) calc(var(--p)*5) 0 .5px #8B3DEB,
    calc(var(--p)*15) calc(var(--p)*5) 0 .5px #241744,
    calc(var(--p)*0) calc(var(--p)*6) 0 .5px #241744,
    calc(var(--p)*1) calc(var(--p)*6) 0 .5px #8B3DEB,
    calc(var(--p)*2) calc(var(--p)*6) 0 .5px #8B3DEB,
    calc(var(--p)*3) calc(var(--p)*6) 0 .5px #8B3DEB,
    calc(var(--p)*4) calc(var(--p)*6) 0 .5px #8B3DEB,
    calc(var(--p)*5) calc(var(--p)*6) 0 .5px #8B3DEB,
    calc(var(--p)*6) calc(var(--p)*6) 0 .5px #8B3DEB,
    calc(var(--p)*7) calc(var(--p)*6) 0 .5px #8B3DEB,
    calc(var(--p)*8) calc(var(--p)*6) 0 .5px #8B3DEB,
    calc(var(--p)*9) calc(var(--p)*6) 0 .5px #8B3DEB,
    calc(var(--p)*10) calc(var(--p)*6) 0 .5px #8B3DEB,
    calc(var(--p)*11) calc(var(--p)*6) 0 .5px #8B3DEB,
    calc(var(--p)*12) calc(var(--p)*6) 0 .5px #8B3DEB,
    calc(var(--p)*13) calc(var(--p)*6) 0 .5px #8B3DEB,
    calc(var(--p)*14) calc(var(--p)*6) 0 .5px #8B3DEB,
    calc(var(--p)*15) calc(var(--p)*6) 0 .5px #241744,
    calc(var(--p)*0) calc(var(--p)*7) 0 .5px #241744,
    calc(var(--p)*1) calc(var(--p)*7) 0 .5px #8B3DEB,
    calc(var(--p)*2) calc(var(--p)*7) 0 .5px #8B3DEB,
    calc(var(--p)*3) calc(var(--p)*7) 0 .5px #8B3DEB,
    calc(var(--p)*4) calc(var(--p)*7) 0 .5px #241744,
    calc(var(--p)*5) calc(var(--p)*7) 0 .5px #241744,
    calc(var(--p)*6) calc(var(--p)*7) 0 .5px #8B3DEB,
    calc(var(--p)*7) calc(var(--p)*7) 0 .5px #8B3DEB,
    calc(var(--p)*8) calc(var(--p)*7) 0 .5px #8B3DEB,
    calc(var(--p)*9) calc(var(--p)*7) 0 .5px #8B3DEB,
    calc(var(--p)*10) calc(var(--p)*7) 0 .5px #241744,
    calc(var(--p)*11) calc(var(--p)*7) 0 .5px #241744,
    calc(var(--p)*12) calc(var(--p)*7) 0 .5px #8B3DEB,
    calc(var(--p)*13) calc(var(--p)*7) 0 .5px #8B3DEB,
    calc(var(--p)*14) calc(var(--p)*7) 0 .5px #8B3DEB,
    calc(var(--p)*15) calc(var(--p)*7) 0 .5px #241744,
    calc(var(--p)*1) calc(var(--p)*8) 0 .5px #241744,
    calc(var(--p)*2) calc(var(--p)*8) 0 .5px #241744,
    calc(var(--p)*3) calc(var(--p)*8) 0 .5px #241744,
    calc(var(--p)*6) calc(var(--p)*8) 0 .5px #241744,
    calc(var(--p)*7) calc(var(--p)*8) 0 .5px #241744,
    calc(var(--p)*8) calc(var(--p)*8) 0 .5px #241744,
    calc(var(--p)*9) calc(var(--p)*8) 0 .5px #241744,
    calc(var(--p)*12) calc(var(--p)*8) 0 .5px #241744,
    calc(var(--p)*13) calc(var(--p)*8) 0 .5px #241744,
    calc(var(--p)*14) calc(var(--p)*8) 0 .5px #241744;
}
@keyframes pxHop { 0%,100% { transform: translateY(0) rotate(-2deg); } 50% { transform: translateY(-7%) rotate(2deg); } }

/* Wordmark caption */
.splash-word { font-weight: 850; letter-spacing: .34em; font-size: clamp(.72rem, 2vw, .9rem);
  color: rgba(255,255,255,.85); text-transform: uppercase; padding-left: .34em; }
.splash-word b { color: #C084FC; }

/* Chunky pixel progress bar */
.px-bar { position: relative; width: min(240px, 60vw); height: calc(clamp(9px,2.4vw,14px)*1.15);
  border: 2px solid rgba(255,255,255,.22); border-radius: 3px; overflow: hidden;
  background: rgba(255,255,255,.05); }
.px-bar span { display: block; height: 100%; width: 10%;
  background: linear-gradient(90deg, #71E6FF, #8B3DEB 70%, #C084FC);
  transition: width .35s steps(6, jump-none); }
.px-bar::after { content: ""; position: absolute; inset: 0; width: 40%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.22), transparent);
  transform: translateX(-120%); animation: pxSheen 1.2s linear infinite; }

/* Drifting pixel motes */
.px-mote { position: absolute; width: 8px; height: 8px; opacity: .5; }
.px-mote.m1 { left: 16%; top: 24%; background: #71E6FF; animation: moteDrift 3.2s ease-in-out infinite; }
.px-mote.m2 { right: 14%; top: 34%; background: #C084FC; animation: moteDrift 4.1s ease-in-out infinite reverse; }
.px-mote.m3 { left: 24%; bottom: 22%; background: #8B3DEB; animation: moteDrift 3.7s ease-in-out infinite .6s; }
.px-mote.m4 { right: 22%; bottom: 16%; background: #F0ABFC; animation: moteDrift 4.5s ease-in-out infinite .3s; }
@keyframes moteDrift { 0%,100% { transform: translate(0,0); } 50% { transform: translate(10px,-16px); } }
@keyframes pxSheen { to { transform: translateX(340%); } }

@media (prefers-reduced-motion: reduce) {
  .px-pad, .px-bar::after, .px-mote { animation: none !important; }
  /* The galaxy layers keep their colour but stop rotating – a slow
     full-viewport rotation is precisely the sort of large-field motion this
     setting is meant to suppress. */
  /* The starfield stays visible, it just stops winking. */
  .ss-blink rect { animation: none !important; }
  .splash { transition: opacity .2s linear; }
}

/* ============================================================
   SPLASH PERF REWORK (2026-07-22): compositor-only splash
   - sprite pre-rendered to an SVG layer (no box-shadow art, no filter)
   - glow is a static radial-gradient layer (no drop-shadow, no blur())
   - bar fills via scaleX transform (no width/layout)
   - all motion = stepped/eased transforms + one opacity fade
   ============================================================ */
.px-pad i { display: none; }
.px-pad {
  filter: none !important;
  background: center / contain no-repeat url('data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2016%209%22%20shape-rendering=%22crispEdges%22%3E%3Crect%20x=%222%22%20y=%220%22%20width=%224%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%2210%22%20y=%220%22%20width=%224%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%221%22%20y=%221%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%222%22%20y=%221%22%20width=%224%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%226%22%20y=%221%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%229%22%20y=%221%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%2210%22%20y=%221%22%20width=%224%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%2214%22%20y=%221%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%220%22%20y=%222%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%221%22%20y=%222%22%20width=%226%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%227%22%20y=%222%22%20width=%222%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%229%22%20y=%222%22%20width=%226%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%2215%22%20y=%222%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%220%22%20y=%223%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%221%22%20y=%223%22%20width=%221%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%222%22%20y=%223%22%20width=%221%22%20height=%221%22%20fill=%22%23C084FC%22/%3E%3Crect%20x=%223%22%20y=%223%22%20width=%221%22%20height=%221%22%20fill=%22%2371E6FF%22/%3E%3Crect%20x=%224%22%20y=%223%22%20width=%228%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%2212%22%20y=%223%22%20width=%221%22%20height=%221%22%20fill=%22%23F0ABFC%22/%3E%3Crect%20x=%2213%22%20y=%223%22%20width=%222%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%2215%22%20y=%223%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%220%22%20y=%224%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%221%22%20y=%224%22%20width=%221%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%222%22%20y=%224%22%20width=%223%22%20height=%221%22%20fill=%22%2371E6FF%22/%3E%3Crect%20x=%225%22%20y=%224%22%20width=%226%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%2211%22%20y=%224%22%20width=%221%22%20height=%221%22%20fill=%22%23F0ABFC%22/%3E%3Crect%20x=%2212%22%20y=%224%22%20width=%221%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%2213%22%20y=%224%22%20width=%221%22%20height=%221%22%20fill=%22%23F0ABFC%22/%3E%3Crect%20x=%2214%22%20y=%224%22%20width=%221%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%2215%22%20y=%224%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%220%22%20y=%225%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%221%22%20y=%225%22%20width=%221%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%222%22%20y=%225%22%20width=%221%22%20height=%221%22%20fill=%22%23C084FC%22/%3E%3Crect%20x=%223%22%20y=%225%22%20width=%221%22%20height=%221%22%20fill=%22%2371E6FF%22/%3E%3Crect%20x=%224%22%20y=%225%22%20width=%228%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%2212%22%20y=%225%22%20width=%221%22%20height=%221%22%20fill=%22%23F0ABFC%22/%3E%3Crect%20x=%2213%22%20y=%225%22%20width=%222%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%2215%22%20y=%225%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%220%22%20y=%226%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%221%22%20y=%226%22%20width=%2214%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%2215%22%20y=%226%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%220%22%20y=%227%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%221%22%20y=%227%22%20width=%223%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%224%22%20y=%227%22%20width=%222%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%226%22%20y=%227%22%20width=%224%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%2210%22%20y=%227%22%20width=%222%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%2212%22%20y=%227%22%20width=%223%22%20height=%221%22%20fill=%22%238B3DEB%22/%3E%3Crect%20x=%2215%22%20y=%227%22%20width=%221%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%221%22%20y=%228%22%20width=%223%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%226%22%20y=%228%22%20width=%224%22%20height=%221%22%20fill=%22%23241744%22/%3E%3Crect%20x=%2212%22%20y=%228%22%20width=%223%22%20height=%221%22%20fill=%22%23241744%22/%3E%3C/svg%3E');
  image-rendering: pixelated;
  will-change: transform;
}
.px-wrap { position: relative; display: grid; place-items: center; }
.px-glow {
  position: absolute; width: 240%; height: 260%;
  background: radial-gradient(closest-side, rgba(124,58,237,.42), rgba(124,58,237,.12) 55%, transparent 72%);
  pointer-events: none;
}
.px-mote { will-change: transform; }
.px-bar span {
  width: 100% !important;
  transform-origin: left center;
  transform: scaleX(.08);
  transition: transform .35s steps(6, jump-none);
  will-change: transform;
}
.splash { will-change: opacity; }
@media (prefers-reduced-motion: reduce) {
  .px-bar span { transition: none; }
}

/* ============================================================
   MENU 60FPS PASS (2026-07-22): compositor-only nav system
   ============================================================ */

/* Drawer surface: its background is already 97% opaque, so the 24px
   backdrop-filter added invisible cost – WebKit re-samples the backdrop
   every frame of the spring animation. Gone. Layer-promote the sheet and
   its staggered children for their animation lifetimes. */
.nav-drawer {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  will-change: transform, opacity;
}
.nav-drawer .drawer-link, .nav-drawer .drawer-cta { will-change: translate, opacity; }

/* Backdrop dim: full-screen backdrop-filter while opacity-fading is the
   classic mobile WebKit repaint trap – swap for a slightly deeper plain
   dim, which composites as a single opacity-fading layer. */
.nav-drawer-backdrop {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  background: rgba(3, 4, 14, .74);
  will-change: opacity;
}

/* Hamburger→X: icon swap is transform/opacity (already compositor).
   The button's border/background/box-shadow transitions are hover-only
   paint on a 46px circle – restricted to cheap props on touch. */
@media (hover: none) {
  .menu-toggle { transition: transform .1s var(--ease); }
}

/* Same treatment for the other overlay dims + heavy glass on phones:
   modal backdrop, analysis overlay, and the suggestions dropdown all have
   near-opaque fills; the sticky header keeps its glass but at a cheaper
   radius while scrolling. */
@media (max-width: 720px) {
  .modal-backdrop, .analysis-overlay {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background: rgba(2, 4, 12, .84);
  }
  .suggestion-list { -webkit-backdrop-filter: none; backdrop-filter: none; }
  .header-shell {
    -webkit-backdrop-filter: blur(14px) saturate(135%);
    backdrop-filter: blur(14px) saturate(135%);
  }
}

/* Backdrop participates via visibility (discrete) + opacity (composited)
   instead of display flips – no layout on open/close. */
.nav-drawer-backdrop { visibility: hidden; transition: opacity .3s var(--ease), visibility .3s; }
.nav-drawer-backdrop.open { visibility: visible; opacity: 1; }

/* ============================================================
   RAWG ARTWORK (2026-07-22): credit line + cover art fit
   ============================================================ */
.rawg-credit { display: block; margin-top: 10px; font-size: .68rem; color: rgba(255,255,255,.38); max-width: 100%; box-sizing: border-box; overflow-wrap: break-word; }
.rawg-credit a { color: rgba(255,255,255,.55); text-decoration: underline; }
.report-sidebar .rawg-credit { margin-top: 8px; }
.game-match-cover img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 0; }
.game-match-cover img + span, .game-match-cover span { z-index: 1; }
.game-match-cover:has(img) > span:not(.gm-play) { display: none; }  /* initials yield to art */
.report-cover img { z-index: 0; }
.report-cover .cover-overlay { z-index: 0; }

/* ============================================================
   IGDB ART QA (2026-07-23): portrait box art done right
   ============================================================ */

/* Match tiles: blurred art fills the landscape tile; the sharp portrait
   cover sits contained on top with its own card treatment. Both fade in
   (opacity only) once loaded – the tile box is already reserved, so
   there is zero layout shift. */
.game-match-cover img.gm-art-bg {
  position: absolute; inset: -10%;
  width: 120%; height: 120%;
  object-fit: cover;
  filter: blur(16px) saturate(1.2) brightness(.82);
  border-radius: 0;
  opacity: 0; z-index: 0;
}
.game-match-cover img.gm-art {
  position: relative; z-index: 2;
  width: auto; height: 86%;
  max-width: 56%;
  object-fit: contain;
  border-radius: 9px;
  border: 1px solid rgba(255,255,255,.22);
  box-shadow: 0 12px 30px rgba(0,0,0,.5);
  opacity: 0;
}
.game-match-cover.has-art img.gm-art,
.game-match-cover.has-art img.gm-art-bg {
  opacity: 1;
  transition: opacity .4s ease;
}
.game-match-cover.has-art > span:not(.gm-play) { display: none; }   /* initials yield */
.game-match-cover.has-art::before { opacity: 0; }      /* drop cockpit watermark behind real art */

/* Report cover: portrait 2x art in a portrait tile (object-fit: cover is
   now a near-native fit), stronger scrim so the title stays legible on
   light or busy art, plus a fade-in. */
.report-cover img { opacity: 0; }
.report-cover.has-art img { opacity: 1; transition: opacity .4s ease; }
.report-cover img + .cover-overlay {
  background:
    linear-gradient(0deg, rgba(2, 9, 19, .94) 0%, rgba(2, 9, 19, .55) 34%, rgba(2, 9, 19, .16) 60%, rgba(2, 9, 19, .1) 100%);
}
.report-cover.has-art small { color: #7BE7FF; text-shadow: 0 1px 8px rgba(0,0,0,.65); }
.report-cover.has-art strong { text-shadow: 0 2px 12px rgba(0,0,0,.7); }

/* Report cover scrim v2: top band keeps the eyebrow label readable on
   light art; the bottom rises higher for long, wrapped titles. */
.report-cover img + .cover-overlay {
  background: linear-gradient(180deg,
    rgba(2, 9, 19, .58) 0%, rgba(2, 9, 19, .14) 20%,
    rgba(2, 9, 19, .2) 42%, rgba(2, 9, 19, .72) 60%,
    rgba(2, 9, 19, .96) 100%);
}

/* ============================================================
   SELECTED-GAME PREVIEW ART (2026-07-23)
   ============================================================ */
.demo-cover { position: relative; overflow: hidden; }
.demo-cover img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; z-index: 0;
}
.demo-cover.has-art img { opacity: 1; transition: opacity .4s ease; }
.demo-cover.has-art .cover-pad,
.demo-cover.has-art span,
.demo-cover.has-art strong { display: none; }  /* labels live outside the art now */
.demo-cover.has-art { box-shadow: inset 0 0 0 1px rgba(255,255,255,.24), 0 20px 44px rgba(0,0,0,.34); }

/* "Sample report" as a chip beside the section label */
.pm-top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.sample-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 11px; border-radius: 999px;
  font-size: .67rem; font-weight: 750; letter-spacing: .04em;
  color: #7BE7FF; background: rgba(53, 212, 232, .12);
  border: 1px solid rgba(53, 212, 232, .3);
}
.sample-chip svg { width: 11px; height: 11px; }

/* Meta rows: slightly larger, calmer rhythm beside real art */
.preview-meta h3 { margin-top: 10px; }
.preview-meta dl > div { font-size: .8rem; padding-bottom: 10px; }
.preview-meta dd { font-weight: 650; }
.preview-meta .rawg-credit { margin-top: 12px; }
@media (max-width: 720px) {
  .preview-sidebar { grid-template-columns: 132px 1fr; }
  .demo-cover { aspect-ratio: 3 / 4; align-self: start; }
}

/* Preview credit: subtle, like everywhere else (not section-label caps) */
.preview-meta > small.rawg-credit {
  text-transform: none; letter-spacing: 0;
  color: rgba(255,255,255,.38); font-weight: 500;
}

/* ============================================================
   PRINT OVERHAUL (2026-07-23): branded, premium PDF report
   ============================================================ */
.print-letterhead, .print-footer { display: none; }   /* print-only */

@media print {
  @page { margin: 15mm 13mm 20mm; }

  /* --- Branded letterhead: navy banner, real logo, purple accent --- */
  .print-letterhead {
    display: grid !important;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 0.22in;
    background: linear-gradient(120deg, #0B0A22 0%, #171233 55%, #241744 100%) !important;
    border-bottom: 3px solid #7C3AED !important;
    border-radius: 10px;
    padding: 0.16in 0.22in;
    margin-bottom: 0.26in;
    break-inside: avoid;
    break-after: avoid;
  }
  .print-letterhead img { width: 1.15in; height: auto; }
  .print-letterhead .plh-text strong {
    display: block; color: #fff !important;
    font-size: 13pt; letter-spacing: .02em;
  }
  .print-letterhead .plh-text small { color: #B9A8E8 !important; font-size: 8pt; letter-spacing: .06em; text-transform: uppercase; }
  .print-letterhead .plh-meta { text-align: right; }
  .print-letterhead .plh-meta span { display: block; color: #E7DFFF !important; font-size: 9.5pt; font-weight: 700; }
  .print-letterhead .plh-meta small { color: #8f83b8 !important; font-size: 7.5pt; }

  /* --- Repeating footer on every page (fixed repeats in print) --- */
  /* Chromium doesn't repeat fixed elements per printed page, so the
     footer is a document colophon: always present, never misplaced.
     (Per-page numbers come from the browser's own print header/footer.) */
  .print-footer {
    display: flex !important;
    justify-content: space-between; gap: 12px;
    border-top: 2px solid #7C3AED !important;
    margin-top: 0.28in;
    padding: 8px 2px 0;
    font-size: 7.5pt; color: #6b6486 !important;
    break-inside: avoid;
  }

  /* --- Game identity row: real box art as a proper 3:4 plate --- */
  .report-sidebar {
    border-bottom: 2px solid #7C3AED !important;
    grid-template-columns: 1.2in 1fr;
  }
  .report-cover {
    aspect-ratio: 3 / 4 !important;
    min-height: 0 !important;
    width: 1.2in;
    border-radius: 8px;
    border: 1px solid #d9d2ea !important;
    overflow: hidden;
  }
  .report-cover img { opacity: 1 !important; }
  .report-cover.has-art small, .report-cover.has-art strong { text-shadow: 0 1px 6px rgba(0,0,0,.8); }

  /* --- Purple/navy hierarchy instead of the old blue accents --- */
  .report-kicker small { color: #7C3AED !important; letter-spacing: .1em; }
  .report-hero h1 { color: #191046 !important; }
  .report-section-head h2 { color: #191046 !important; position: relative; padding-left: 12px; }
  .report-section-head h2::before {
    content: ""; position: absolute; left: 0; top: 4px; bottom: 4px; width: 4px;
    border-radius: 2px; background: linear-gradient(180deg, #7C3AED, #35D4E8) !important;
  }
  .quick-stat { border: 1px solid #e2dcf2 !important; background: #f8f6fe !important; }
  .quick-stat svg { color: #7C3AED !important; }
  .quick-stat strong { color: #191046 !important; }
  .insight-card h3, .detail-block h4 { color: #191046 !important; }
  .report-game-info h2 { color: #191046 !important; }
  .category-heading h3 { color: #191046 !important; }
  .source-row a { color: #6D28D9 !important; }
  .source-row a::after { color: #6D28D9 !important; }
  .report-hero { border-bottom: 1px solid #e2dcf2; }
  .report-section { border-bottom: 1px solid #eee9f8; }

  /* --- Page-break hygiene: long categories may flow across pages,
         but headings never orphan and blocks stay whole --- */
  .report-category { break-inside: auto; }
  .category-heading { break-after: avoid; break-inside: avoid; }
  .detail-block, .quick-stats, .insight-card, .source-row { break-inside: avoid; }
  .report-sidebar { break-inside: avoid; }

  /* keep interactive-only chrome out of the PDF */
  .rawg-credit { display: none; }              /* covered by the footer attribution */
  .sample-chip { border: 1px solid #C4B5FD !important; color: #6D28D9 !important; background: #F5F1FF !important; }
}

/* ============================================================
   WINDOW-FIT PASS (2026-07-23): every overlay fits the *visible*
   viewport on iOS – dvh instead of vh, safe-area padding, and
   internal scrolling for tall content.
   ============================================================ */
@media (max-width: 720px) {
  .modal, .analysis-card {
    max-height: calc(100vh - 20px);                                    /* old-browser fallback */
    max-height: calc(100dvh - 16px - env(safe-area-inset-bottom));     /* visible viewport */
  }
  .modal-backdrop, .analysis-overlay {
    padding: 10px 10px calc(10px + env(safe-area-inset-bottom));
  }
}
/* Analysis overlay: the 9-stage list scrolls inside the card, so the
   Cancel button can never fall off-screen. */
.analysis-stages { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
.analysis-card > .button, .analysis-card .analysis-progress { flex: none; }

/* Confirm modal internals: grid scrolls, footer buttons stay pinned. */
.modal-wide .game-match-grid { overscroll-behavior: contain; }

/* ============================================================
   STICKY CONFIRM FOOTER (2026-07-23): Back / Confirm & Analyze
   always on screen while the match grid scrolls behind them.
   ============================================================ */
.modal-footer {
  position: sticky;
  bottom: 0;
  z-index: 3;
  margin-left: -26px; margin-right: -26px; margin-bottom: -26px;
  padding: 14px 26px calc(16px + env(safe-area-inset-bottom));
  background: linear-gradient(180deg, rgba(12,11,30,0), rgba(12,11,30,.96) 30%, rgba(12,11,30,.99));
}
@media (max-width: 720px) {
  .modal-footer { margin-left: -20px; margin-right: -20px; margin-bottom: -20px; padding-left: 20px; padding-right: 20px; }
  .modal-footer .button { flex: 1; justify-content: center; }
}

/* ============================================================
   PRINT SECTION BREAKS
   2026-07-23: major sections and A/B/C/D category cards each
   opened at the top of a fresh page – "clean starts beat tightly
   packed pages".
   2026-08-21: REVERSED, on measurement. The intent was right but
   the mechanism was too blunt: a forced break before every
   category gave a whole sheet to a 200-word section, and where a
   section happened to end near a page boundary the extra break
   produced a completely empty page. Measured on the same report:

       sections + categories break   16 pages, 1 blank
       sections break only           14 pages, 1 blank
       nothing forced                12 pages, 0 blank

   Airiness is worth paying for; blank pages are not. Sections now
   flow, and headings are instead glued to the content they
   introduce so nothing is ever stranded at the foot of a page –
   which is what "clean starts" was actually after.
   ============================================================ */
@media print {
  .report-section { break-before: auto; }
  .report-category { break-before: auto; break-inside: auto; }
  /* A heading alone at the bottom of a page is the thing to prevent. */
  .report-section-head { break-after: avoid; }
  .category-heading { break-after: avoid; break-inside: avoid; }
  .print-footer { break-before: auto; }
}

/* ============================================================
   SUGGESTION THUMBS (2026-07-23): real covers in the dropdown
   ============================================================ */
.suggestion-cover { position: relative; overflow: hidden; }
.suggestion-cover img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0; animation: thumbIn .3s ease forwards;
}
@keyframes thumbIn { to { opacity: 1; } }
.suggestion-cover:has(img) { color: transparent; }  /* initials yield to art */

/* ============================================================
   TRAILER SECTION (2026-07-23): privacy-first click-to-load
   ============================================================ */
.report-trailer { margin: 26px 0 6px; }
.trailer-stage {
  position: relative; width: 100%; aspect-ratio: 16 / 9;
  border-radius: 18px; overflow: hidden;
  background: #0a0b1c; border: 1px solid rgba(255,255,255,.1);
  box-shadow: 0 24px 60px rgba(4,3,20,.4);
}
.trailer-facade {
  position: absolute; inset: 0; width: 100%; height: 100%;
  border: 0; cursor: pointer; padding: 0;
  background-size: cover; background-position: center;
  display: grid; place-items: center;
}
.trailer-facade::before {
  content: ""; position: absolute; inset: 0;
  background: radial-gradient(circle at 50% 45%, rgba(6,8,20,.15), rgba(6,8,20,.6));
  transition: background .3s var(--ease);
}
.trailer-facade:hover::before { background: radial-gradient(circle at 50% 45%, rgba(6,8,20,.05), rgba(6,8,20,.5)); }
.trailer-play {
  position: relative; z-index: 1;
  width: 74px; height: 74px; border-radius: 50%;
  display: grid; place-items: center; color: #fff;
  background: linear-gradient(135deg, var(--blue), #6D5CF5 55%, var(--purple));
  box-shadow: 0 12px 34px rgba(124,58,237,.5), 0 0 0 8px rgba(255,255,255,.08);
  transition: transform .2s var(--ease-spring, var(--ease));
}
.trailer-facade:hover .trailer-play { transform: scale(1.08); }
.trailer-play svg { width: 30px; height: 30px; margin-left: 3px; }
.trailer-caption {
  position: absolute; left: 14px; bottom: 12px; z-index: 1; max-width: 80%;
  font-size: .8rem; font-weight: 650; color: #fff; text-align: left;
  text-shadow: 0 2px 10px rgba(0,0,0,.7);
}
.trailer-frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.trailer-strip { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.trailer-thumb {
  position: relative; width: 108px; aspect-ratio: 16 / 9; flex: none;
  border: 1px solid rgba(255,255,255,.12); border-radius: 10px; overflow: hidden;
  cursor: pointer; padding: 0;
  background-size: cover; background-position: center;
  display: grid; place-items: center; color: #fff;
  transition: border-color .2s var(--ease), transform .2s var(--ease);
}
.trailer-thumb::before { content: ""; position: absolute; inset: 0; background: rgba(4,6,18,.35); }
.trailer-thumb svg { position: relative; z-index: 1; width: 18px; height: 18px; opacity: .92; }
.trailer-thumb.active { border-color: var(--purple); box-shadow: 0 0 0 2px rgba(124,58,237,.4); }
.trailer-thumb:hover { transform: translateY(-2px); }
@media (max-width: 720px) { .trailer-thumb { width: 92px; } }

/* Never in the PDF – trailers are interactive, screen-only */
@media print { .report-trailer { display: none !important; } }

/* ============================================================
   TRAILER ON THE GAME CARD (2026-07-23): play overlay + lightbox
   ============================================================ */
.demo-cover.has-trailer, .report-cover.has-trailer { cursor: default; }
.cover-play {
  position: absolute; z-index: 3;
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 58px; height: 58px; border: 0; border-radius: 50%; cursor: pointer;
  display: grid; place-items: center; color: #fff;
  background: linear-gradient(135deg, var(--blue), #6D5CF5 55%, var(--purple));
  box-shadow: 0 10px 30px rgba(124,58,237,.55), 0 0 0 6px rgba(255,255,255,.12);
  transition: transform .2s var(--ease-spring, var(--ease)), box-shadow .2s var(--ease);
}
.cover-play svg { width: 24px; height: 24px; margin-left: 2px; }
.cover-play:hover, .cover-play:focus-visible { transform: translate(-50%, -50%) scale(1.09); box-shadow: 0 14px 38px rgba(124,58,237,.7), 0 0 0 8px rgba(255,255,255,.16); }
@media (max-width: 720px) { .cover-play { width: 50px; height: 50px; } .cover-play svg { width: 21px; height: 21px; } }

/* Lightbox */
.trailer-lightbox { position: fixed; inset: 0; z-index: 300; display: grid; place-items: center; padding: 16px; }
.tl-backdrop { position: absolute; inset: 0; background: rgba(3,4,14,.86); -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px); }
.tl-dialog { position: relative; z-index: 1; width: min(960px, 100%); display: grid; gap: 10px; }
.tl-stage { position: relative; width: 100%; aspect-ratio: 16 / 9; border-radius: 16px; overflow: hidden; background: #05060f; box-shadow: 0 40px 120px rgba(0,0,0,.6), 0 0 0 1px rgba(255,255,255,.08); }
.tl-frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.tl-close {
  position: absolute; z-index: 2; top: -14px; right: -6px;
  width: 44px; height: 44px; border-radius: 50%; border: 1px solid rgba(255,255,255,.2); cursor: pointer;
  display: grid; place-items: center; color: #fff; background: rgba(18,16,40,.9);
  -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
}
.tl-close svg { width: 20px; height: 20px; }
.tl-close:hover { background: rgba(124,58,237,.5); }
.tl-strip { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }
.tl-thumb { position: relative; width: 104px; aspect-ratio: 16/9; flex: none; border: 1px solid rgba(255,255,255,.14); border-radius: 9px; overflow: hidden; cursor: pointer; padding: 0; background-size: cover; background-position: center; display: grid; place-items: center; color: #fff; }
.tl-thumb::before { content: ""; position: absolute; inset: 0; background: rgba(4,6,18,.4); }
.tl-thumb svg { position: relative; z-index: 1; width: 16px; height: 16px; }
.tl-thumb.active { border-color: var(--purple); box-shadow: 0 0 0 2px rgba(124,58,237,.45); }
@media (max-width: 560px) { .tl-close { top: -8px; right: 0; } }

/* Match-card trailer chip (top-right of the candidate cover) */
.game-match-cover .gm-play {
  position: absolute; z-index: 4; top: 8px; right: 8px; opacity: 1;
  display: inline-flex; align-items: center; gap: 5px;
  padding: 5px 10px 5px 8px; border-radius: 999px; cursor: pointer;
  color: #fff; font-size: .72rem; font-weight: 750;
  background: linear-gradient(135deg, rgba(33,118,255,.95), rgba(124,58,237,.95));
  box-shadow: 0 6px 18px rgba(4,6,20,.5), 0 0 0 1px rgba(255,255,255,.18) inset;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  transition: transform .15s var(--ease), box-shadow .15s var(--ease);
}
.gm-play svg { width: 13px; height: 13px; margin-left: 1px; }
.gm-play:hover, .gm-play:focus-visible { transform: scale(1.05); box-shadow: 0 8px 22px rgba(124,58,237,.6), 0 0 0 1px rgba(255,255,255,.28) inset; outline: none; }

/* Analysis loader – live elapsed timer + rotating status */
.analysis-elapsed { margin: 8px 0 0; font-size: .82rem; font-variant-numeric: tabular-nums; color: rgba(255,255,255,.62); min-height: 1.1em; letter-spacing: .01em; }

/* Print: hide interactive trailer controls on the cover */
@media print { .cover-play, .gm-play, .trailer-lightbox { display: none !important; } }

/* ============================================================
   AT-A-GLANCE VERDICT CARD + typeahead thumbs (2026-07-23)
   ============================================================ */
.verdict-card {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;
  margin: 4px 0 20px;
}
@media (max-width: 620px) { .verdict-card { grid-template-columns: 1fr; } }
.verdict-item {
  display: flex; align-items: center; gap: 11px;
  padding: 13px 15px; border-radius: 15px;
  background: rgba(255,255,255,.03); border: 1px solid rgba(255,255,255,.08);
  border-left: 4px solid var(--v, #2176FF);
}
.verdict-item svg { width: 22px; height: 22px; color: var(--v, #2176FF); flex: none; }
.verdict-item strong { display: block; font-size: 1rem; line-height: 1.1; color: #fff; }
.verdict-item span { display: block; font-size: .74rem; color: rgba(255,255,255,.55); margin-top: 2px; }
.verdict-item.tone-good { --v: #22C7B8; }
.verdict-item.tone-neutral { --v: #4A8BFF; }
.verdict-item.tone-watch { --v: #F7B23D; }

/* Print: verdict card stays, in light theme */
@media print {
  .verdict-card { grid-template-columns: repeat(3, 1fr) !important; break-inside: avoid; margin: 6px 0 14px; }
  .verdict-item { background: #f7f9fc !important; border: 1px solid #e3e9f0 !important; border-left: 4px solid var(--v) !important; }
  .verdict-item strong { color: #191046 !important; }
  .verdict-item span { color: #5a6472 !important; }
}

/* Typeahead cover thumbnails */
.suggestion-cover { position: relative; overflow: hidden; }
.suggestion-cover img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* ============================================================
   iOS SCROLL/PAN FIX (2026-07-24): eliminate horizontal overflow
   so the page can never pan into a stuck, left-clipped position.
   ============================================================ */
/* Decorative aura: fixed, clipped to the viewport – cannot extend the
   document's scroll width (the real cause of the iOS left-pan + freeze). */
.ambient-layer {
  position: fixed; inset: 0; overflow: hidden;
  pointer-events: none; z-index: 0;
}
/* Clip every section's off-screen decoratives at their own box (each of
   these is position:relative, so this reliably contains absolute children
   without creating a scroll container – overflow:clip keeps y visible). */
.hero, .how-section, .story-section, .story-collage,
.framework-section, .preview-section, .cta-section {
  overflow-x: clip;
}
/* Hard stop: the document itself can never be wider than the viewport. */
html, body { max-width: 100%; }
@media print { .ambient-layer { display: none !important; } }

/* ============================================================
   CLIENT BRAND PASS (2026-07-31) – Video Game Minimap
   Standard palette: purple #A259FF (main), green #81C884 (secondary),
   pink #FF6B6B + yellow #FFD93E (tertiary). Gradients blend
   purple↔green or yellow↔pink.
   ============================================================ */
:root, html {
  --brand-purple: #A259FF;
  --brand-green:  #81C884;
  --brand-pink:   #FF6B6B;
  --brand-yellow: #FFD93E;
  --grad-primary: linear-gradient(120deg, var(--brand-purple), var(--brand-green));
  --grad-accent:  linear-gradient(120deg, var(--brand-yellow), var(--brand-pink));
}
/* Re-point the legacy tokens so every existing component adopts the palette */
@media screen {
  :root {
    --purple: #A259FF;
    --purple-bright: #BB86FF;
    --purple-deep: #7B3FD4;
    --violet: #A259FF;
    --magenta: #FF6B6B;
    --blue: #A259FF;
    --teal: #FF6B6B;
    --cyan: #FF6B6B;
    --orange: #FFD93E;
  }
}

/* --- Tracking: bold headers were far too tight --- */
.hero h1,
.section-heading h2,
.story-copy h2,
.preview-content-head h3,
.report-hero h1,
.report-section-head h2,
.cta-card h2,
.modal-header h2,
.analysis-card h2 {
  letter-spacing: -0.012em;
}
@media (max-width: 720px) {
  .hero h1 { letter-spacing: -0.008em; }
  .section-heading h2, .story-copy h2 { letter-spacing: -0.008em; }
}

/* --- Primary actions / gradients on brand --- */
.button-primary,
.analyze-button.button-primary,
.cta-analyze.button-primary,
.drawer-cta.button-primary {
  background: linear-gradient(120deg, #A259FF, #8E63E8 55%, #FF6B6B) !important;
}
.hero h1 span {
  background-image: linear-gradient(100deg, #A259FF 0%, #9B76F0 45%, #FF6B6B 100%) !important;
}

/* ============================================================
   HERO RESTRUCTURE (2026-07-31) – single stacked column so the
   search bar sits ABOVE the imagery, per client note.
   ============================================================ */
@media screen {
  .hero {
    grid-template-columns: 1fr !important;
    min-height: 0 !important;
    padding-bottom: 72px;
    gap: 34px;
  }
  .hero::before { display: none; }           /* rotating blur ring not needed in stacked layout */
  .hero-copy { max-width: 900px; }
  .hero .search-panel { max-width: 860px; }
  .hero-visual { min-height: 0 !important; overflow: visible; }
}

/* Eyebrow: line removed, larger type (client note) */
.eyebrow.eyebrow-lg {
  font-size: clamp(1rem, 2.2vw, 1.35rem);
  letter-spacing: .06em;
  color: var(--brand-purple);
}
.eyebrow.eyebrow-lg span { display: none; }

/* Nav brand: logo lockup + reserved slot for the forthcoming Minimap logo */
.brand { gap: 10px; }
.brand-slot[data-logo="minimap"] { display: inline-block; width: 0; height: 40px; }
@media (min-width: 981px) { .brand-slot[data-logo="minimap"] { width: 8px; } }

/* ---- Rotating hero slideshow ---- */
.hero-slideshow {
  position: relative; width: 100%; aspect-ratio: 16 / 9;
  border-radius: 32px; overflow: hidden;
  background: #0a0b1c; border: 1px solid rgba(255,255,255,.12);
  box-shadow: 0 40px 110px rgba(4,3,20,.5);
}
.hero-slide { position: absolute; inset: 0; margin: 0; opacity: 0; transition: opacity .9s ease; }
.hero-slide.is-active { opacity: 1; }
.hero-slide img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hero-slide-dots { position: absolute; left: 0; right: 0; bottom: 14px; display: flex; gap: 8px; justify-content: center; z-index: 2; }
.hs-dot {
  width: 34px; height: 5px; border-radius: 99px; border: 0; cursor: pointer; padding: 0;
  background: rgba(255,255,255,.34); transition: background .25s, transform .25s;
}
.hs-dot.is-active { background: var(--brand-purple); transform: scaleY(1.4); }
@media (max-width: 720px) { .hero-slideshow { aspect-ratio: 4 / 3; border-radius: 24px; } .hs-dot { width: 26px; } }

/* Checklist card sits over the slideshow on desktop, below it on phones */
@media screen and (min-width: 981px) {
  .hero-visual { position: relative; }
  .decision-card { position: absolute; right: 3%; top: 50%; transform: translateY(-50%); width: min(360px, 38%); }
}
@media (max-width: 980px) {
  .decision-card { position: static !important; transform: none !important; width: auto; margin-top: 16px; }
}

/* ============================================================
   FRAMEWORK SECTION (client notes): solid brand colours, no
   gradients, no corner icons; gradient only on "complete picture."
   ============================================================ */
@media screen {
  /* 2026-09-02: background !important rules removed – this whole block
     predates the later "flat category colors" pass (~line 7357+) that
     actually governs these cards now, and was still silently winning
     over it purely because of !important despite being superseded.
     category-a itself is entirely unused in the HTML (confirmed: zero
     matches), so that line is just dead weight. b/c/d's colors are now
     set by the hover/focus-scoped rules below instead, as part of the
     neutral-at-rest, color-on-hover scheme. */
  .framework-card::before { display: none !important; }           /* kill sheen/gradient overlay */
  .framework-card { box-shadow: 0 22px 60px rgba(10,6,30,.28); }
}
/* 2026-09-02: the hard-coded dark-text !important overrides that used to
   live here for category-d and category-b are removed – both were
   fighting the same var(--card-heading-ink) the newer pass already sets
   correctly per-state (light at rest, dark on hover), and unconditionally
   forcing dark text would have made the resting (now dark, neutral)
   background unreadable. */
.framework-card.category-d h3::after { background: rgba(23,18,59,.85) !important; }
.framework-card.category-d .explore-arrow { background: rgba(23,18,59,.14); }
.framework-card.category-d .explore-arrow svg { color: #17123B; }
.framework-card.category-b h3::after { background: rgba(16,40,26,.85) !important; }
.framework-card.category-b .explore-arrow { background: rgba(16,40,26,.14); }
.framework-card.category-b .explore-arrow svg { color: #10281A; }
/* letter badges removed with the icons – keep spacing tidy */
.framework-card .category-top { justify-content: flex-start; }

/* Gradient reserved for the framework headline's second line */
.section-heading h2 span {
  background: linear-gradient(100deg, #A259FF, #FF6B6B) !important;
  -webkit-background-clip: text; background-clip: text; color: transparent !important;
}

/* ============================================================
   CTA + FOOTER (client notes)
   ============================================================ */
/* Looser tracking on the closing headline */
.cta-card h2 { letter-spacing: -0.005em; }

/* Footer logo lockup – Health-e-Habits (flat yellow) slot then Health-e-Gaming */
/* 2026-09-04, ROOT CAUSE of the two footer logos wrapping onto separate
   lines: flex-wrap:wrap was still set here from the original version –
   min-width:0 on .footer-brand (added earlier) let the grid column shrink
   below the logos' combined natural width as intended, but nothing told
   THIS row not to wrap when that happened, so the browser did exactly
   what flex-wrap:wrap is for. Explicit request: these two must always sit
   side by side. nowrap forces that; the logo height below is sized down
   somewhat to make sure they still fit in an equal 1/5 column at typical
   desktop widths without the row overflowing instead of wrapping. */
/* 2026-09-04: was a hardcoded 228px matching only the icon row's own
   width – but the tagline below (white-space:nowrap, so it renders at its
   full unwrapped width) turned out to be wider than that, which is what
   made the logos look narrower than everything below them. width:100%
   instead of a guessed pixel value means this always matches whatever
   .footer-brand's actual widest child computes to, tagline or icons,
   without needing to hardcode which one wins. */
.footer-logos { display: flex; align-items: center; justify-content: space-between; width: 100%; gap: 14px; flex-wrap: nowrap; }
.footer-logo { height: 52px; width: auto; }
.footer-logo-slot[data-logo="hehabits-yellow"] { display: none; }  /* revealed when the yellow logo asset lands */

/* Subscribe button: match the input height exactly, arrow centred */
.fn-form { align-items: stretch; }
.fn-form input { height: 48px; box-sizing: border-box; }
.fn-form button {
  height: 48px; width: 48px; flex: none;
  display: grid; place-items: center; padding: 0;
  border-radius: 12px;
  background: linear-gradient(120deg, #A259FF, #FF6B6B);
  border: 0; cursor: pointer; color: #fff;
}
.fn-form button svg { width: 18px; height: 18px; display: block; }

/* Non-profit line reads as fine print, full width on small screens */
/* 2026-09-04: dropped the 62ch cap entirely. It was meant to keep this
   reading as a comfortable paragraph rather than one full-width line, but
   after two rounds of this text still rendering wrong live, removing every
   width constraint that isn't the grid track itself is the safer move –
   one less thing that could interact with the column width in a way I
   can't verify without a live browser. The grid column (see
   .footer-bottom's minmax()) is what actually controls this now. */
.fb-nonprofit { line-height: 1.5; }

/* Donate button (replaces New Analysis in the header) */
.donate-button { text-decoration: none; padding-inline: 18px; }
@media (max-width: 720px) {
  .donate-button { width: auto !important; min-width: 0 !important; padding: 0 15px !important; font-size: .84rem !important; }
  .donate-button span { font-size: .84rem !important; }
}

/* Client logo lockup – Health-e-Habits + Health-e-Gaming (+ Minimap slot) */
.brand { align-items: center; gap: 9px; }
.brand-mark { height: 46px; width: auto; display: block; }
.brand-divider { width: 1px; height: 26px; background: rgba(255,255,255,.18); flex: none; }
@media (max-width: 980px) { .brand-mark { height: 38px; } }
@media (max-width: 720px) {
  .brand-mark { height: 30px; }
  .brand-divider { height: 18px; }
}
@media (max-width: 420px) { .brand-mark { height: 26px; } }
.footer-logos { gap: 16px; }
.footer-logo { height: 44px; width: auto; }
@media (max-width: 720px) { .footer-logo { height: 38px; } }

/* Splash: pixel-invaders canvas replaces the old box-shadow sprite */
#splashCanvas { display: block; image-rendering: pixelated; filter: drop-shadow(0 14px 30px rgba(162,89,255,.4)); }
.px-pad { display: none !important; }
.splash-word b { color: var(--brand-yellow); }

/* ============================================================================
   REFINEMENT SYSTEM (2026-07-31)
   Goal: clean, calm, credible. Restrained colour, generous space, subtle
   depth. Decoration is reduced – never content. No copy is altered.
   ========================================================================= */
@media screen {
  :root {
    /* Calm dark canvas (approved dark direction, lower saturation) */
    --surface-0: #0E1018;      /* page            */
    --surface-1: #161926;      /* raised panels   */
    --surface-2: #1D2130;      /* cards           */
    --line-1: rgba(255,255,255,.09);
    --line-2: rgba(255,255,255,.14);
    --text-1: #F2F4F8;         /* primary         */
    --text-2: #B7BECD;         /* secondary       */
    --text-3: #8A93A6;         /* tertiary/meta   */
    /* spacing scale */
    --s1:4px; --s2:8px; --s3:12px; --s4:16px; --s5:24px; --s6:32px; --s7:48px; --s8:72px; --s9:104px;
    --radius: 14px;
    --radius-lg: 20px;
    --shadow-1: 0 1px 2px rgba(0,0,0,.28);
    --shadow-2: 0 6px 20px rgba(0,0,0,.28);
  }

  /* ---- 1 · Quiet the canvas ------------------------------------------- */
  body {
    background: var(--surface-0) !important;
    color: var(--text-2);
  }
  .ambient-layer { display: none !important; }          /* decorative blur orbs */
  .hero::before, .visual-glow, .hero-orbit { display: none !important; }
  .how-section::before, .framework-section::before,
  .preview-section::before, .cta-section::before { display: none !important; }
  .story-orb { display: none !important; }

  /* Flatten heavy glass/gradient panels into calm surfaces */
  .glass, .header-shell, .preview-window, .modal, .nav-drawer, .analysis-card {
    background: var(--surface-1) !important;
    border: 1px solid var(--line-1) !important;
    box-shadow: var(--shadow-2) !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
  }
  .site-footer {
    background: var(--surface-1) !important;
    border-top: 1px solid var(--line-1);
  }
  .site-footer::before { display: none !important; }

  /* ---- 2 · Typography ------------------------------------------------- */
  body { font-size: 16px; line-height: 1.6; }
  .hero h1 { font-weight: 700; line-height: 1.06; }
  .section-heading h2 { font-weight: 750; line-height: 1.12; color: var(--text-1); }
  .hero-lead, .section-heading p { color: var(--text-2); }
  .eyebrow, .eyebrow-lg {
    font-size: .78rem !important; font-weight: 700; letter-spacing: .14em !important;
    text-transform: uppercase; color: var(--text-3) !important;
  }

  /* ---- 3 · Section rhythm --------------------------------------------- */
  .framework-section, .preview-section, .cta-section { padding-block: var(--s9) !important; }
  .section-heading { margin-bottom: var(--s7); }

  /* ---- 4 · Restrained colour ------------------------------------------ */
  /* Framework cards: quiet surfaces with a coloured spine, not saturated fills */
  .framework-card {
    background: var(--surface-2) !important;
    border: 1px solid var(--line-1);
    border-left: 3px solid var(--accent, #A259FF) !important;
    box-shadow: none !important;
    color: var(--text-2) !important;
    min-height: 0 !important;
    padding: var(--s5) !important;
  }
  .framework-card.category-a { --accent: #A259FF; }
  .framework-card.category-b { --accent: #81C884; }
  .framework-card.category-c { --accent: #FF6B6B; }
  .framework-card.category-d { --accent: #FFD93E; }
  .framework-card h3, .framework-card.category-b h3, .framework-card.category-d h3 { color: var(--text-1) !important; font-size: 1.12rem; }
  .framework-card p, .framework-card.category-b p, .framework-card.category-d p { color: var(--text-2) !important; font-size: .94rem; }
  .framework-card h3::after { display: none !important; }
  .framework-card .category-letter {
    background: transparent !important; color: var(--accent) !important;
    font-size: .8rem; font-weight: 800; letter-spacing: .1em; padding: 0;
    width: auto; height: auto; box-shadow: none; border: 0;
  }
  .framework-card button, .framework-card.category-b button, .framework-card.category-d button { color: var(--accent) !important; }
  .framework-card .explore-arrow { background: transparent !important; }
  .framework-card .explore-arrow svg { color: var(--accent) !important; }
  .framework-card:hover { border-color: var(--line-2); transform: none !important; }

  /* Buttons: one dominant action, quieter secondaries */
  .button-primary, .analyze-button.button-primary, .cta-analyze.button-primary, .drawer-cta.button-primary {
    background: #A259FF !important;
    box-shadow: none !important;
  }
  .button-primary:hover { background: #B06BFF !important; box-shadow: none !important; }
  #infoModalDone {
    background: var(--modal-accent, #A259FF) !important;
    color: var(--modal-accent-text, #fff) !important;
  }
  #infoModalDone:hover { background: var(--modal-accent, #B06BFF) !important; }
  .button-primary::before { display: none !important; }   /* shine sweep */
  .button-quiet, .button-secondary {
    background: transparent !important; border: 1px solid var(--line-2) !important; color: var(--text-1) !important;
  }

  /* ---- 5 · Motion: subtle only ---------------------------------------- */
  .reveal-ready { translate: 0 8px; transition-duration: .35s, .4s; }
  .tilt-card { transform: none !important; transition: border-color .2s ease; }
  .tilt-card:hover { --lift: 0px; }
  .scroll-progress { display: none; }
}

/* ---- 6 · Accessibility: focus is always obvious ----------------------- */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid #A259FF;
  outline-offset: 2px;
  border-radius: 6px;
}

/* ---- 7 · Hero: search is the centre of gravity ----------------------- */
@media screen {
  .hero {
    padding-block: clamp(56px, 9vw, 112px) var(--s9) !important;
    text-align: center;
    justify-items: center;
  }
  .hero-copy { max-width: 860px; margin-inline: auto; }
  .hero .eyebrow-lg { margin-bottom: var(--s4); }
  .hero h1 {
    font-size: clamp(2.4rem, 6vw, 4rem) !important;
    margin-bottom: var(--s6);
    color: var(--text-1);
  }
  .hero h1 span { color: #A259FF; }

  /* The search panel: largest, brightest, highest-contrast object on the page */
  .search-panel {
    background: transparent !important;
    padding: 0 !important;
    max-width: 720px;
    margin: 0 auto var(--s6);
  }
  .search-field-wrap {
    background: #FFFFFF !important;
    border-radius: var(--radius-lg) !important;
    padding: 10px 10px 10px 22px !important;
    gap: var(--s3) !important;
    box-shadow: 0 10px 40px rgba(0,0,0,.38) !important;
    border: 2px solid transparent;
    transition: border-color .18s ease, box-shadow .18s ease;
  }
  .search-field-wrap:focus-within {
    border-color: #A259FF;
    box-shadow: 0 10px 40px rgba(0,0,0,.38), 0 0 0 4px rgba(162,89,255,.25) !important;
  }
  .search-field-wrap input {
    font-size: 1.14rem !important;
    padding: 16px 0 !important;
    font-weight: 500;
  }
  .search-field-wrap .search-icon { width: 22px; height: 22px; color: #5A6478; }
  .analyze-button {
    height: 56px; padding-inline: var(--s5) !important;
    font-size: 1rem !important; font-weight: 700;
    border-radius: 12px !important;
  }

  /* Supporting copy sits below the search, quieter */
  .hero-lead {
    max-width: 68ch; margin: 0 auto;
    font-size: 1.02rem !important; line-height: 1.7;
    color: var(--text-3) !important;
  }
  .search-footer { justify-content: center; color: var(--text-3); }
  .search-footer button {
    background: transparent; border: 1px solid var(--line-2);
    color: var(--text-2); border-radius: 999px; padding: 6px 14px; font-size: .86rem;
  }
  .search-footer button:hover { border-color: #A259FF; color: var(--text-1); }

  /* Imagery supports the search – it no longer competes with it */
  .hero-visual { max-width: 940px; margin: var(--s8) auto 0; width: 100%; }
  .hero-slideshow { border-radius: var(--radius-lg) !important; border: 1px solid var(--line-1) !important; }
  .decision-card { margin-top: var(--s5); }
}

@media screen and (max-width: 760px) {
  .hero { padding-block: var(--s7) var(--s8) !important; }
  .search-field-wrap { grid-template-columns: auto 1fr; padding: 12px 14px !important; row-gap: var(--s3); }
  .search-field-wrap input { font-size: 1.05rem !important; padding: 10px 0 !important; }
  .analyze-button { grid-column: 1 / -1; width: 100%; justify-content: center; }
  .hero-visual { margin-top: var(--s7); }
}

/* ---- 8 · Report: consistent, scannable components -------------------- */
@media screen {
  .report-shell, .report-main, .report-sidebar { background: transparent; }

  /* Accordion header is the whole row */
  .category-heading {
    display: grid; grid-template-columns: auto 1fr auto; gap: var(--s4);
    align-items: center; width: 100%; text-align: left;
    background: transparent; border: 0; cursor: pointer;
    /* Right padding added 2026-09-02: the toggle chevron sat almost flush
       (~1px) against the box's right border once the boxes were widened
       to align with report-hero's outer edge – per mockup, it needs
       breathing room from that edge, not to sit right on top of it.
       2026-09-06: changed from var(--s4) to a literal 28px so it matches
       .category-details' own right margin exactly. Those two values are
       the same edge as far as a reader is concerned: the chevron and the
       external-link buttons in the source rows below it sit in the same
       vertical column, and at var(--s4) the chevron's centre was 12px to
       the right of theirs (measured), which reads as a misalignment rather
       than as two separate systems. Tied to 28px, not to --s4, because it
       must track the detail column's inset – if --s4 is retuned for
       spacing reasons this alignment should not silently break. */
    padding: var(--s5) 28px var(--s5) 0; color: inherit; font: inherit;
  }
  .category-heading:hover .ch-text h3 { color: #A259FF; }
  .ch-text small {
    display: block; font-size: .72rem; letter-spacing: .14em; text-transform: uppercase;
    color: var(--text-3); margin-bottom: 4px;
  }
  .ch-text h3 { font-size: 1.18rem; color: var(--text-1); margin: 0; transition: color .16s ease; }
  .category-badge {
    width: 34px; height: 34px; display: grid; place-items: center;
    border-radius: 9px; font-weight: 800; font-size: .9rem;
    background: rgba(255,255,255,.05); color: var(--cat, #A259FF);
    border: 1px solid var(--line-1); box-shadow: none;
  }
  .report-category {
    border-bottom: 1px solid var(--line-1); border-radius: 0; background: transparent !important; box-shadow: none !important; padding: 0 !important;
    /* 2026-09-02: realigned per mockup – left edge matches report-hero's
       own text inset (hero bleeds out by --content-pad, then pads back in
       by 28px, so its copy sits content-pad-minus-28 to the left of
       report-content's normal padding edge); right edge matches
       report-hero's outer edge exactly (the full content-pad bleed, same
       as report-hero itself). Two different amounts on purpose – the
       box's left edge lines up with where the copy above it starts, but
       its right border lines up with the analysis card's own right edge. */
    margin-left: calc(-1 * (var(--content-pad) - 28px));
    margin-right: calc(-1 * var(--content-pad));
  }
  /* 2026-09-04: Practical Recommendations and Complete Source List never
     got the same treatment as the category boxes above – their content
     stayed at report-content's plain, unadjusted padding edge, which is
     exactly why the recommendations box read as narrower than everything
     else and didn't reach the true right margin. Same two values, same
     reasoning, applied to whatever wraps each section's content. */
  .insight-card, .source-list {
    margin-left: calc(-1 * (var(--content-pad) - 28px));
    margin-right: calc(-1 * var(--content-pad));
  }
  /* 2026-09-06: but NOT when the source list is inside a category. The rule
     above exists for the standalone "Complete Source List" section, which
     sits directly in .report-content and genuinely should reach the true
     page margin. A category's "Evidence and Sources" block is already inside
     .category-details, which sets its own column (50px left, 28px right), so
     breaking out a second time pushed those rows wider than the
     .detail-block separators bracketing them: measured 15px past the card's
     own right edge, which is what clipped the link buttons, and equally far
     left of the border lines, which is why those lines looked like they
     stopped short. Zeroing it here lines the rows up with the rest of the
     detail column and lets the separators span the same width. */
  .category-details .source-list { margin-left: 0; margin-right: 0; }
  .report-category.cat-a { --cat: #A259FF; } .report-category.cat-b { --cat: #81C884; }
  .report-category.cat-c { --cat: #FF6B6B; } .report-category.cat-d { --cat: #FFD93E; }
  .category-toggle { display: grid; place-items: center; width: 32px; height: 32px; color: var(--text-3); transition: transform .2s ease; }
  .report-category.expanded .category-toggle { transform: rotate(180deg); }
  .category-summary { margin: 0 28px var(--s5) 50px; color: var(--text-2); max-width: 72ch; }
  .category-expand-more {
    display: block; margin: -10px 0 var(--s5) 50px;
    background: none; border: 0; padding: 0; cursor: pointer;
    font-size: .84rem; font-weight: 750; color: var(--cat, #A259FF);
    text-decoration: underline; text-underline-offset: 3px; text-decoration-color: rgba(255,255,255,.3);
  }
  .category-expand-more:hover { text-decoration-color: currentColor; }
  /* Once the card is actually open, the details are right there – the
     prompt has nothing left to do, so it hides rather than sitting above
     the content it was pointing at. */
  .report-category.expanded .category-expand-more { display: none; }
  /* 2026-09-06: right margin added. This was `margin: 0 0 var(--s6) 50px`
     – 50px on the LEFT (to align the detail column under the heading text,
     past the 34px badge + 16px gap) and nothing on the right, so every
     line of body copy ran the full remaining width and pressed right up
     against the card's own border while the left side had 50px of air. The
     asymmetry is what read as "bumping the margin": the text was not
     overflowing, it just had no breathing room on one side. 28px keeps it
     clear of the border without pulling the right edge in so far that the
     measure gets narrow. */
  .category-details { margin: 0 28px var(--s6) 50px !important; gap: 0 !important; }

  /* FLATTENED, 2026-08-21 – the alignment fix.
     The badge is 34px and the gap is 16px, so the heading text, the topline
     and this column all begin at 50px. This block was the odd one out: as a
     bordered, padded card starting at 50px, its TEXT began at 75px. That is
     why one category panel showed four different left edges.
     Removing the box lines everything up by construction, and a hairline rule
     separates the blocks better than a border and a fill did. */
  .detail-block {
    background: none; border: 0; border-radius: 0;
    border-top: 1px solid var(--line-1);
    /* 2026-09-06: bottom padding added. This was `var(--s5) 0 0` – space
       above the heading but none below the content, so the border-top drawn
       by the NEXT block landed flush on the previous paragraph's last line
       (measured: 0px clearance). The separator needs air on both sides, not
       just the side it belongs to. */
    padding: var(--s5) 0;
  }
  .detail-block:first-child { border-top: 0; padding-top: 0; }
  /* Nothing follows the last block, so its bottom padding would just add a
     gap before the card's own edge. */
  .detail-block:last-child { padding-bottom: 0; }
  .detail-block h4 {
    display: flex; align-items: center; gap: var(--s2);
    font-size: .78rem; letter-spacing: .12em; text-transform: uppercase;
    color: var(--text-3); margin: 0 0 var(--s4);
  }
  .detail-block h4 svg { width: 15px; height: 15px; }
  .detail-block li, .detail-block p { color: var(--text-2); line-height: 1.65; }
  .finding-group-label {
    font-size: .72rem; letter-spacing: .12em; text-transform: uppercase;
    color: var(--cat, #A259FF); margin: 0 0 var(--s2); font-weight: 800;
  }
  /* 2026-09-06: .finding-list + .finding-list was the one adjacency missing
     from this list, and it is by far the most common one on a real report.
     labelledList() in app.js gives every SINGLE-item labelled finding its
     own <ul class="finding-list"> (only multi-item groups get wrapped in a
     .finding-group), so a category like Gameplay Mechanics – where each of
     "The Core Loop", "Play Type", "Match Length & Exit Friction" is one
     labelled item – renders as three consecutive sibling <ul>s. Every other
     combination here had a margin; this one fell through to 0, so those
     bullets ran straight into each other with no gap at all while the
     group-to-group spacing right beside them looked fine. */
  .finding-group + .finding-group,
  .finding-list + .finding-group,
  .finding-group + .finding-list,
  .finding-list + .finding-list { margin-top: var(--s4); }
  /* No left border: it would sit hard against the text with the block now
     flat, and shift this one section out of the shared column. */
  .detail-block.gap-block h4 { color: #FFD93E; }
}

@media screen and (max-width: 760px) {
  /* 2026-09-06: margin-right reset alongside margin-left. The desktop rules
     add 28px on the right to keep copy off the card border; on a narrow
     screen the card's own padding already does that job, and keeping 28px
     here would eat the measure from both sides at once. */
  .category-summary, .category-details { margin-left: 0 !important; margin-right: 0 !important; }
  .category-heading { gap: var(--s3); }
}

/* ---- 9 · Corrections pass ------------------------------------------- */
@media screen {
  /* The search must float free – no panel behind it */
  .search-panel.glass, form.search-panel {
    background: transparent !important;
    border: 0 !important;
    box-shadow: none !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
  }

  /* Kill every leftover shine/sweep pseudo-element on buttons */
  .button::before, .button-quiet::before, .button-secondary::before,
  .drawer-cta::before, .cta-analyze::before { display: none !important; }
  .button-quiet {
    background: var(--surface-2) !important;
    border: 1px solid var(--line-1) !important;
    color: var(--text-1) !important;
    box-shadow: none !important;
  }
  .button-quiet:hover { border-color: var(--line-2) !important; background: #232839 !important; }

  /* Report CTA band: flat surface, not a gradient slab */
  .report-cta, .report-footer-cta, .report-content .cta-band {
    background: var(--surface-1) !important;
    border: 1px solid var(--line-1) !important;
    box-shadow: none !important;
  }
  .cta-section .cta-inner, .cta-card {
    background: var(--surface-1) !important;
    border: 1px solid var(--line-1) !important;
    box-shadow: none !important;
  }

  /* Methodology / overview tiles share the one card shape */
  .method-card, .overview-cat, .quick-stat {
    background: var(--surface-1) !important;
    border: 1px solid var(--line-1) !important;
    box-shadow: none !important;
  }

  /* Comfortable measure for the supporting paragraph */
  .hero-lead { max-width: 60ch !important; }
}

/* ---- 10 · Framework cards: specificity-matched calm surfaces ---------- */
@media screen {
  .framework-card.category-a, .framework-card.category-b,
  .framework-card.category-c, .framework-card.category-d {
    background: var(--surface-2) !important;
    border: 1px solid var(--line-1) !important;
    border-left: 3px solid var(--accent) !important;
    color: var(--text-2) !important;
    box-shadow: none !important;
  }
  .framework-card.category-a h3, .framework-card.category-b h3,
  .framework-card.category-c h3, .framework-card.category-d h3 { color: var(--text-1) !important; }
  .framework-card.category-a p, .framework-card.category-b p,
  .framework-card.category-c p, .framework-card.category-d p,
  .framework-card.category-a li, .framework-card.category-b li,
  .framework-card.category-c li, .framework-card.category-d li { color: var(--text-2) !important; }
  .framework-card.category-a .category-letter, .framework-card.category-b .category-letter,
  .framework-card.category-c .category-letter, .framework-card.category-d .category-letter {
    background: transparent !important; color: var(--accent) !important; box-shadow: none !important;
  }
}

/* ---- 11 · Regression fixes ------------------------------------------ */
@media screen {
  /* Gradient-clipped headings had lost background-clip, rendering the words
     invisible over a solid bar. Restore legibility with a flat accent. */
  .section-heading h2 span,
  .framework-section .section-heading h2 span,
  .preview-section .section-heading h2 span,
  .cta-section h2 span,
  .hero h1 span {
    background: none !important;
    background-image: none !important;
    -webkit-background-clip: border-box !important;
    background-clip: border-box !important;
    -webkit-text-fill-color: #A259FF !important;
    color: #A259FF !important;
  }

  /* Framework card: honest column flow so the CTA can never overlap copy */
  .framework-card, .framework-card.category-a, .framework-card.category-b,
  .framework-card.category-c, .framework-card.category-d {
    display: flex !important;
    flex-direction: column !important;
    align-items: flex-start;
    gap: var(--s3);
    min-height: 0 !important;
    overflow: hidden;
  }
  .framework-card > * { position: static !important; }
  .framework-card p { flex: 1 1 auto; margin: 0 0 var(--s4); }
  .framework-card > button {
    position: static !important;
    margin-top: auto;
    display: inline-flex; align-items: center; gap: var(--s2);
    background: transparent; border: 0; padding: 0; cursor: pointer;
  }
  .framework-card::before, .framework-card::after { display: none !important; }
  .framework-card .category-top { margin-bottom: var(--s2); }
}

/* ---- 11b · Category E: 5th framework card, same calm-surface language -- */
@media screen {
  .framework-card.category-e {
    --accent: #4C86FF;
    background: var(--surface-2) !important;
    border: 1px solid var(--line-1) !important;
    border-left: 3px solid var(--accent) !important;
    color: var(--text-2) !important;
    box-shadow: none !important;
    display: flex !important;
    flex-direction: column !important;
    align-items: flex-start;
    gap: var(--s3);
    min-height: 0 !important;
    overflow: hidden;
  }
  .framework-card.category-e h3 { color: var(--text-1) !important; }
  .framework-card.category-e p,
  .framework-card.category-e li { color: var(--text-2) !important; }
  .framework-card.category-e button { color: var(--accent) !important; }
  .framework-card.category-e .category-letter {
    background: transparent !important; color: var(--accent) !important; box-shadow: none !important;
  }
  .framework-card.category-e::before, .framework-card.category-e::after { display: none !important; }
}

/* ---- 12 · Touch targets --------------------------------------------- */
@media screen {
  .framework-card > button { min-height: 44px; padding-block: var(--s2); }
  .search-footer button { min-height: 40px; }
  .hs-dot { min-width: 24px; min-height: 24px; }
}

/* ---- 13 · Sample-report tiles match the framework card language ------- */
@media screen {
  .mini-category, .mini-category.mini-a, .mini-category.mini-b,
  .mini-category.mini-c, .mini-category.mini-d {
    background: var(--surface-2) !important;
    background-image: none !important;
    border: 1px solid var(--line-1) !important;
    border-left: 3px solid var(--mc, #A259FF) !important;
    box-shadow: none !important;
    color: var(--text-2) !important;
  }
  .mini-category.mini-a { --mc: #A259FF; }
  .mini-category.mini-b { --mc: #81C884; }
  .mini-category.mini-c { --mc: #FF6B6B; }
  .mini-category.mini-d { --mc: #FFD93E; }
  .mini-category strong { color: var(--text-1) !important; }
  .mini-category small  { color: var(--text-3) !important; }
  .mini-category .mc-ic, .mini-category .mc-arrow {
    background: transparent !important; box-shadow: none !important; border: 0 !important;
  }
  .mini-category .mc-ic svg, .mini-category .mc-arrow svg { color: var(--mc) !important; }
  .mini-category::before, .mini-category::after { display: none !important; }

  /* Quick-stat icon chips: tint the glyph, drop the filled blocks */
  .pv-ic { background: rgba(255,255,255,.05) !important; box-shadow: none !important; border: 1px solid var(--line-1) !important; }
  .pv-ic.ic-teal svg { color: #81C884 !important; }
  .pv-ic.ic-purple svg { color: #A259FF !important; }
  .pv-ic.ic-orange svg { color: #FF6B6B !important; }
}

/* ---- 14 · Hero breathing room + wider search ------------------------- */
@media screen {
  .hero { padding-block: clamp(72px, 11vw, 148px) var(--s9) !important; }
  .hero-copy { max-width: 1060px !important; }

  .hero .eyebrow-lg { margin-bottom: var(--s5) !important; }
  .hero h1 {
    font-size: clamp(2.8rem, 6.8vw, 4.9rem) !important;
    margin-bottom: clamp(40px, 5vw, 64px) !important;
  }

  /* A wider, more confident search bar */
  .search-panel { max-width: 940px !important; margin-bottom: clamp(40px, 5vw, 64px) !important; }
  .search-field-wrap { padding: 12px 12px 12px 26px !important; }
  .search-field-wrap input { font-size: 1.2rem !important; padding: 19px 0 !important; }
  .analyze-button { height: 60px; padding-inline: var(--s6) !important; }

  .search-footer { margin-top: var(--s5); gap: var(--s3); }
  .hero-lead { margin-top: 0 !important; }
  .hero-visual { margin-top: clamp(72px, 9vw, 128px) !important; }
}

@media screen and (min-width: 1100px) {
  /* Keep the eye moving down the centre line without feeling column-locked */
  .hero-lead { max-width: 72ch !important; }
}

@media screen and (max-width: 760px) {
  .hero { padding-block: var(--s8) var(--s8) !important; }
  .hero h1 { margin-bottom: var(--s6) !important; }
  .search-panel { margin-bottom: var(--s6) !important; }
  .search-field-wrap input { font-size: 1.08rem !important; padding: 12px 0 !important; }
  .hero-visual { margin-top: var(--s8) !important; }
}

/* ---- 15 · Never let label text sit on top of box art ------------------ */
@media screen {
  .demo-cover:has(img) span,
  .demo-cover:has(img) strong,
  .demo-cover:has(img) .cover-pad { display: none !important; }
}

/* ---- 16 · One clean, regular texture across the whole site ------------
   Was two dot grids at 34px and 52px. Those tile sizes don't share a
   common factor, so the layers drifted in and out of phase and the dots
   clustered unevenly. Replaced with a single grid on an exact 32px pitch,
   dot centred in its cell, applied evenly the full height of the page. */
@media screen {
  body::before {
    opacity: 1 !important;
    background-image:
      radial-gradient(circle at 50% 50%, rgba(255,255,255,.075) 0 1px, transparent 1.4px) !important;
    background-size: 32px 32px !important;
    background-position: 0 0 !important;
    background-repeat: repeat !important;
    mask-image: none !important;
    -webkit-mask-image: none !important;
  }
  /* Nothing else may paint a competing pattern */
  .hero::after, .framework-section::after, .preview-section::after,
  .cta-section::after, .section-shell::before { background-image: none !important; }
}

@media screen and (max-width: 760px) {
  body::before { background-size: 26px 26px !important; }
}


/* ============================================================================
   SEARCH CONSOLE (kept from the HUD concept – the rest was reverted)
   Notched frame, gradient hairline border, dark fill, glyph chips.
   ========================================================================= */
@media screen {
  .search-panel { position: relative; }

  /* side bracket accents */
  .search-panel::before, .search-panel::after {
    content: ""; position: absolute; top: 22px;
    width: 4px; height: 52px; border-radius: 3px;
    background: linear-gradient(180deg, #35D4E8, #A259FF);
    box-shadow: 0 0 12px rgba(120,190,255,.75);
  }
  .search-panel::before { left: -18px; }
  .search-panel::after  { right: -18px; }

  /* The gradient lives on the wrapper and acts as the border; the dark fill is
     an inset layer. (A z-index:-1 pseudo cannot sit behind the parent here –
     clip-path creates a stacking context.) */
  .search-field-wrap {
    position: relative;
    background: linear-gradient(100deg, #35D4E8, #6E7BFF 42%, #A259FF 78%, #35D4E8) !important;
    border: 0 !important;
    border-radius: 20px !important;
    padding: 15px 15px 15px 18px !important;
    gap: var(--s3) !important;
    box-shadow: 0 0 34px rgba(120,80,220,.28) !important;
    clip-path: polygon(22px 0, calc(100% - 22px) 0, 100% 22px, 100% calc(100% - 22px),
                       calc(100% - 22px) 100%, 22px 100%, 0 calc(100% - 22px), 0 22px);
    transition: box-shadow .18s ease;
  }
  .search-field-wrap::before { display: none !important; }
  .search-field-wrap::after {
    content: ""; position: absolute; inset: 1.8px; z-index: 0;
    background: #0D0A20; clip-path: inherit; border-radius: inherit;
  }
  .search-field-wrap > * { position: relative; z-index: 1; }
  .search-field-wrap:focus-within { box-shadow: 0 0 46px rgba(140,90,255,.48) !important; }

  .search-field-wrap .search-icon {
    width: 54px !important; height: 54px !important; padding: 15px;
    border-radius: 50%; color: #DCE4F5 !important;
    background: rgba(255,255,255,.055);
    border: 1px solid rgba(150,180,255,.28);
    box-sizing: border-box; flex: none;
  }
  .search-field-wrap input {
    color: #EEF2FA !important; background: transparent !important;
    font-size: 1.14rem !important; padding: 16px 0 16px 8px !important;
  }
  .search-field-wrap input::placeholder { color: #8891AA !important; }

  .analyze-button {
    position: relative;
    background: linear-gradient(100deg, #8B45FF, #A259FF 55%, #B45CFF) !important;
    border-radius: 14px !important; height: 58px;
    box-shadow: 0 6px 24px rgba(140,70,255,.42) !important;
  }
  .analyze-button:hover { background: linear-gradient(100deg, #9A5BFF, #B06BFF 55%, #C077FF) !important; }
  .analyze-button::before {
    content: ""; position: absolute; left: -16px; top: 12%; height: 76%;
    width: 1px; background: rgba(160,180,230,.22); display: block !important;
  }

  /* try-chips */
  .search-footer button {
    display: inline-flex; align-items: center; gap: 9px;
    background: rgba(255,255,255,.045) !important;
    border: 1px solid rgba(150,175,235,.20) !important;
    color: #D3DAEA !important; border-radius: 11px !important; padding: 9px 15px !important;
    min-height: 42px;
  }
  .search-footer button::before {
    content: ""; width: 21px; height: 21px; border-radius: 6px; flex: none;
    background: var(--chip, #4F7DF3) var(--glyph) center / 12px 12px no-repeat;
    box-shadow: inset 0 0 0 1px rgba(255,255,255,.18);
  }
  /* Neutral placeholder glyphs – deliberately NOT the games' trademarked logos. */
  .search-footer button:nth-of-type(1) { --chip: #4F7DF3;
    --glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23fff'%3E%3Cpath d='M6 3h12v4h-7v3h6v4h-6v7H6z'/%3E%3C/svg%3E"); }
  .search-footer button:nth-of-type(2) { --chip: #5DBB63;
    --glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23fff'%3E%3Cpath d='M3 3h8v8H3zM13 3h8v8h-8zM3 13h8v8H3zM13 13h8v8h-8z' opacity='.9'/%3E%3C/svg%3E"); }
  .search-footer button:nth-of-type(3) { --chip: #D0D4DC;
    --glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23222'%3E%3Cpath d='M6 4l14 2-2 14-14-2z'/%3E%3Crect x='10' y='9' width='5' height='5' fill='%23D0D4DC'/%3E%3C/svg%3E"); }
  .search-footer button:nth-of-type(4) { --chip: #8A93A6;
    --glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23fff'%3E%3Cpath d='M12 2l8 4v6c0 5-3.4 8.6-8 10-4.6-1.4-8-5-8-10V6z'/%3E%3C/svg%3E"); }
  .search-footer button:nth-of-type(5) { --chip: #E8C46A;
    --glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%233A2A05'%3E%3Cpath d='M12 2l2.2 7.3L21 12l-6.8 2.7L12 22l-2.2-7.3L3 12l6.8-2.7z'/%3E%3C/svg%3E"); }
  .search-footer button:hover { border-color: rgba(162,89,255,.55) !important; }
}

@media screen and (max-width: 760px) {
  .search-panel::before, .search-panel::after { display: none; }
  .search-field-wrap { clip-path: none !important; border-radius: 16px !important; padding: 12px !important; }
  .search-field-wrap::after { inset: 1.6px; }
  .search-field-wrap .search-icon { width: 42px !important; height: 42px !important; padding: 11px; }
  .search-field-wrap input { font-size: 1.05rem !important; padding: 10px 0 10px 6px !important; }
  .analyze-button::before { display: none !important; }
}

/* ---- SEARCH CONSOLE · fixes -----------------------------------------
   1. `> *` had overridden .suggestion-list's absolute positioning, so the
      dropdown took up flow space and ballooned the bar.
   2. clip-path on the wrapper clips descendants – it was hiding the
      typeahead dropdown entirely. The notched frame now lives on pseudo
      elements so the wrapper itself never clips. */
@media screen {
  .search-field-wrap {
    background: transparent !important;
    clip-path: none !important;
    overflow: visible !important;
  }
  .search-field-wrap::before {
    content: "" !important; display: block !important;
    position: absolute; inset: 0; z-index: 0;
    background: linear-gradient(100deg, #35D4E8, #6E7BFF 42%, #A259FF 78%, #35D4E8);
    border-radius: 20px;
    clip-path: polygon(22px 0, calc(100% - 22px) 0, 100% 22px, 100% calc(100% - 22px),
                       calc(100% - 22px) 100%, 22px 100%, 0 calc(100% - 22px), 0 22px);
  }
  .search-field-wrap::after {
    content: ""; position: absolute; inset: 1.8px; z-index: 0;
    background: #0D0A20; border-radius: 19px;
    clip-path: polygon(21px 0, calc(100% - 21px) 0, 100% 21px, 100% calc(100% - 21px),
                       calc(100% - 21px) 100%, 21px 100%, 0 calc(100% - 21px), 0 21px);
  }
  /* every child except the dropdown gets lifted above the frame */
  .search-field-wrap > *:not(.suggestion-list) { position: relative; z-index: 1; }
  .search-field-wrap > .suggestion-list {
    position: absolute !important; z-index: 40 !important;
    left: 0; right: 0; top: calc(100% + 10px);
  }
}

@media screen and (max-width: 760px) {
  .search-field-wrap::before { border-radius: 16px; clip-path: none; }
  .search-field-wrap::after  { border-radius: 15px; clip-path: none; }
}

/* ---- Hero lead · HUD frame ------------------------------------------ */
@media screen {
  .hero-lead-frame {
    position: relative;
    max-width: 900px;
    margin: clamp(56px, 6vw, 88px) auto 0;
    padding: clamp(30px, 3.4vw, 44px) clamp(32px, 4vw, 60px);
    border: 1px solid rgba(150,110,255,.26);
    border-radius: 20px;
    background: linear-gradient(180deg, rgba(120,90,220,.055), rgba(120,90,220,.015));
  }
  .hero-lead-frame > .hero-lead {
    margin: 0 !important;
    max-width: 74ch !important;
    color: #B9C0D4 !important;
    text-align: center;
  }
  /* corner brackets: two from the frame, two from the helper span */
  .hero-lead-frame::before, .hero-lead-frame::after,
  .hlf-corners::before, .hlf-corners::after {
    content: ""; position: absolute; width: 46px; height: 46px;
    border-color: rgba(178,132,255,.85); border-style: solid; border-width: 0;
    pointer-events: none;
  }
  .hero-lead-frame::before { top: -1px; left: -1px; border-top-width: 2px; border-left-width: 2px; border-radius: 20px 0 0 0; }
  .hero-lead-frame::after  { bottom: -1px; right: -1px; border-bottom-width: 2px; border-right-width: 2px; border-radius: 0 0 20px 0; }
  .hlf-corners::before { top: -1px; right: -1px; border-top-width: 2px; border-right-width: 2px; border-radius: 0 20px 0 0; }
  .hlf-corners::after  { bottom: -1px; left: -1px; border-bottom-width: 2px; border-left-width: 2px; border-radius: 0 0 0 20px; }
  .hlf-corners { position: absolute; inset: 0; display: block; }
}
@media screen and (max-width: 760px) {
  .hero-lead-frame { padding: 24px 20px; margin-top: var(--s7); border-radius: 16px; }
  .hero-lead-frame::before, .hero-lead-frame::after,
  .hlf-corners::before, .hlf-corners::after { width: 26px; height: 26px; }
}

/* ---- Try-chips: real game cover art ---------------------------------- */
@media screen {
  .search-footer button .chip-art {
    width: 22px; height: 22px; border-radius: 6px; flex: none;
    object-fit: cover; object-position: center 22%;
    box-shadow: inset 0 0 0 1px rgba(255,255,255,.18), 0 1px 3px rgba(0,0,0,.4);
    background: #1A1E2C;
  }
  /* the placeholder glyph steps aside once real art has loaded */
  .search-footer button.has-art::before { display: none !important; }
}

/* ---- Search console · corner artifact --------------------------------
   border-radius + clip-path were both shaping the corners: the radius
   rounded the box, then the polygon cut across it, leaving a dark wedge
   outside the angled edge. The polygon alone defines the notch. */
@media screen {
  .search-field-wrap::before,
  .search-field-wrap::after { border-radius: 0 !important; }
  .search-field-wrap { border-radius: 0 !important; }
}
@media screen and (max-width: 760px) {
  /* no notch on phones – plain rounded rect, so the radius comes back */
  .search-field-wrap { border-radius: 16px !important; }
  .search-field-wrap::before { border-radius: 16px !important; clip-path: none !important; }
  .search-field-wrap::after  { border-radius: 15px !important; clip-path: none !important; }
}

/* ---- Search console · drop the notch ---------------------------------
   The angled corners kept producing dark wedges where the clip polygon met
   the frame. Plain rounded rectangle instead – no clipping anywhere. */
@media screen {
  .search-field-wrap { border-radius: 18px !important; clip-path: none !important; }
  .search-field-wrap::before { border-radius: 18px !important; clip-path: none !important; }
  .search-field-wrap::after  { border-radius: 16.5px !important; clip-path: none !important; }
}
@media screen and (max-width: 760px) {
  .search-field-wrap { border-radius: 16px !important; }
  .search-field-wrap::before { border-radius: 16px !important; }
  .search-field-wrap::after  { border-radius: 14.5px !important; }
}

/* ============================================================================
   "Map the Game" – glossy 3D arcade button
   Pill bezel, lit face, hard bottom edge that depresses on press.
   ========================================================================= */
@media screen {
  .search-field-wrap .analyze-button,
  .analyze-button.button-primary {
    border-radius: 999px !important;
    height: 60px;
    padding: 0 34px !important;
    border: 3px solid #C79BFF !important;
    background: linear-gradient(180deg, #C892FF 0%, #A85FFF 42%, #9147F5 100%) !important;
    color: #fff !important;
    font-weight: 800 !important;
    letter-spacing: .01em;
    text-shadow: 0 2px 0 rgba(74,20,140,.55);
    box-shadow:
      inset 0 3px 0 rgba(255,255,255,.50),      /* top gloss          */
      inset 0 -5px 0 rgba(88,26,168,.42),       /* inner bottom shade */
      0 6px 0 #6C2BC6,                          /* solid base edge    */
      0 12px 22px rgba(0,0,0,.45) !important;   /* cast shadow        */
    transition: transform .10s ease, box-shadow .10s ease, background .16s ease;
    will-change: transform;
  }
  /* the divider line belongs to a flat button, not a raised one */
  .search-field-wrap .analyze-button::before { display: none !important; }

  .search-field-wrap .analyze-button:hover,
  .analyze-button.button-primary:hover {
    background: linear-gradient(180deg, #D3A5FF 0%, #B370FF 42%, #9C55FF 100%) !important;
    transform: translateY(-1px);
    box-shadow:
      inset 0 3px 0 rgba(255,255,255,.55),
      inset 0 -5px 0 rgba(88,26,168,.42),
      0 7px 0 #6C2BC6,
      0 14px 26px rgba(0,0,0,.48) !important;
  }

  /* press: the button travels down onto its base */
  .search-field-wrap .analyze-button:active,
  .analyze-button.button-primary:active {
    transform: translateY(5px);
    box-shadow:
      inset 0 3px 0 rgba(255,255,255,.35),
      inset 0 -2px 0 rgba(88,26,168,.42),
      0 1px 0 #6C2BC6,
      0 3px 8px rgba(0,0,0,.40) !important;
  }

  .search-field-wrap .analyze-button svg { filter: drop-shadow(0 2px 0 rgba(74,20,140,.5)); }
}

@media screen and (max-width: 760px) {
  .search-field-wrap .analyze-button { height: 54px; padding: 0 26px !important; }
}
@media (prefers-reduced-motion: reduce) {
  .search-field-wrap .analyze-button { transition: none; }
  .search-field-wrap .analyze-button:hover { transform: none; }
}

/* ---- Arcade button: smaller, uppercase, shared with Donate ------------ */
@media screen {
  .search-field-wrap .analyze-button,
  .analyze-button.button-primary,
  .donate-button.button-primary,
  .drawer-cta.button-primary {
    border-radius: 999px !important;
    border: 3px solid #C79BFF !important;
    background: linear-gradient(180deg, #C892FF 0%, #A85FFF 42%, #9147F5 100%) !important;
    color: #fff !important;
    font-weight: 800 !important;
    text-transform: uppercase !important;
    letter-spacing: .045em !important;
    text-shadow: 0 2px 0 rgba(74,20,140,.55);
    box-shadow:
      inset 0 3px 0 rgba(255,255,255,.50),
      inset 0 -4px 0 rgba(88,26,168,.42),
      0 5px 0 #6C2BC6,
      0 10px 18px rgba(0,0,0,.42) !important;
    transition: transform .10s ease, box-shadow .10s ease, background .16s ease;
    will-change: transform;
  }

  /* smaller primary action */
  .search-field-wrap .analyze-button,
  .analyze-button.button-primary {
    height: 50px !important;
    padding: 0 26px !important;
    font-size: .92rem !important;
  }
  .search-field-wrap .analyze-button svg { width: 16px; height: 16px; }

  /* header donate, scaled to the nav */
  .donate-button.button-primary {
    height: 44px !important;
    padding: 0 20px !important;
    font-size: .82rem !important;
  }
  .drawer-cta.button-primary { height: 50px !important; font-size: .9rem !important; }

  .search-field-wrap .analyze-button:hover,
  .analyze-button.button-primary:hover,
  .donate-button.button-primary:hover,
  .drawer-cta.button-primary:hover {
    background: linear-gradient(180deg, #D3A5FF 0%, #B370FF 42%, #9C55FF 100%) !important;
    transform: translateY(-1px);
    box-shadow:
      inset 0 3px 0 rgba(255,255,255,.55),
      inset 0 -4px 0 rgba(88,26,168,.42),
      0 6px 0 #6C2BC6,
      0 12px 22px rgba(0,0,0,.46) !important;
  }

  .search-field-wrap .analyze-button:active,
  .analyze-button.button-primary:active,
  .donate-button.button-primary:active,
  .drawer-cta.button-primary:active {
    transform: translateY(4px);
    box-shadow:
      inset 0 3px 0 rgba(255,255,255,.35),
      inset 0 -2px 0 rgba(88,26,168,.42),
      0 1px 0 #6C2BC6,
      0 3px 8px rgba(0,0,0,.38) !important;
  }
}

@media screen and (max-width: 760px) {
  .search-field-wrap .analyze-button { height: 48px !important; padding: 0 20px !important; font-size: .88rem !important; }
  .donate-button.button-primary { height: 40px !important; padding: 0 15px !important; font-size: .76rem !important; letter-spacing: .03em !important; }
}
@media (prefers-reduced-motion: reduce) {
  .analyze-button.button-primary, .donate-button.button-primary, .drawer-cta.button-primary { transition: none; }
  .analyze-button.button-primary:hover, .donate-button.button-primary:hover, .drawer-cta.button-primary:hover { transform: none; }
}

/* ---- Splash: the Health-e-Habits wordmark, now the sole logo (was
   paired side-by-side with the Health-e-Gaming logo and a divider between
   them; removed per request, with this one made larger to fill the
   space and "presents" added beneath it) ------------------------------- */
.splash-logos {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: clamp(6px, 1.6vw, 10px);
  margin: clamp(14px, 3vw, 22px) 0 clamp(16px, 3vw, 24px);
}
.splash-logos img {
  /* 2026-09-03: 42/9.6vw/64px -> 78/17.7vw/118px (~1.84x across the
     board), per request to match the logo's width to the pixel-art "e"
     shape above it. Measured directly rather than eyeballing: the
     canvas renders at 600px wide, but the visible shape within it (scanned
     for non-background pixels) only spans ~249px, centered – the
     previous logo width (135px, at this same 1512px-viewport
     measurement) was well short of that. 118px height, at this logo's
     fixed 2.117 aspect ratio, renders at ~250px wide, matching. */
  height: clamp(78px, 17.7vw, 118px);
  width: auto; display: block;
  /* nothing to fade in against – keep them crisp and instant */
}
.splash-presents {
  font-family: 'Sora', Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: clamp(.72rem, 1.6vw, .86rem);
  font-weight: 600;
  font-style: italic;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: rgba(255,255,255,.55);
}
@media (max-width: 380px) {
  .splash-logos img { height: 70px; }
}

/* ============================================================================
   CLIENT NOTES PT.2 (2026-07-31)
   ========================================================================= */

/* ---- 1 · No dot texture anywhere ------------------------------------- */
@media screen {
  body::before { display: none !important; }
}

/* ---- 2 · Nav: no glow behind the logos, flat buttons ------------------ */
@media screen {
  .brand { display: flex; align-items: center; gap: 14px; }
  .brand-link { display: inline-flex; align-items: center; text-decoration: none; }
  .brand-mark,
  .brand-link img,
  .footer-logo {
    filter: none !important;
    box-shadow: none !important;
    text-shadow: none !important;
  }
  .brand::before, .brand::after, .brand-link::before, .brand-link::after { display: none !important; }
  .brand-divider { width: 1px; height: 30px; background: rgba(255,255,255,.20); flex: none; }

  /* Flat nav buttons – yellow Donate, purple Map the Game */
  .header-shell .nav-map-btn,
  .header-shell .donate-button.button-primary {
    border-radius: 10px !important;
    border: 0 !important;
    box-shadow: none !important;
    text-shadow: none !important;
    text-transform: none !important;
    letter-spacing: 0 !important;
    font-weight: 700 !important;
    font-size: .86rem !important;
    height: 42px !important;
    padding: 0 18px !important;
    display: inline-flex; align-items: center; justify-content: center;
    text-decoration: none;
    transition: filter .15s ease;
  }
  .header-shell .nav-map-btn {
    background: #A259FF !important;
    color: #fff !important;
  }
  .header-shell .donate-button.button-primary {
    background: #FFD93E !important;
    color: #2A1E00 !important;
  }
  .header-shell .nav-map-btn:hover,
  .header-shell .donate-button.button-primary:hover { filter: brightness(1.08); transform: none !important; }
  .header-shell .nav-map-btn:active,
  .header-shell .donate-button.button-primary:active { transform: none !important; filter: brightness(.95); }
  .header-actions { display: flex; align-items: center; gap: 10px; }
}

/* ---- 3 · Bottom CTA headline on one line ------------------------------ */
@media screen {
  .cta-section h2, .cta-card h2, .cta-inner h2 {
    font-size: clamp(1.05rem, 2.1vw, 1.6rem) !important;
    line-height: 1.25 !important;
    white-space: nowrap;
  }
}
@media screen and (max-width: 900px) {
  .cta-section h2, .cta-card h2, .cta-inner h2 { white-space: normal; }
}

/* ---- 4 · Footer fixes ------------------------------------------------- */
@media screen {
  /* socials sit under the tagline with even spacing */
  .footer-brand p { margin-bottom: 18px; }
  .footer-social { display: flex; gap: 12px; flex-wrap: wrap; }
  .footer-social .social-btn { width: 43.2px; height: 43.2px; display: grid; place-items: center; }

  /* Built For column matches the rhythm of Product / Trust */
  .footer-col { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
  .footer-col strong { margin-bottom: 4px; }
  .footer-col span, .footer-col button { line-height: 1.5; padding: 0; }

  /* 2026-09-04: was "centred, italic" per an earlier request – now left-
     aligned, upright, and no longer auto-centered as a block, per a
     follow-up request reversing that. Kept the max-width (still reads
     better as a wrapped paragraph than a full-width line) but dropped
     margin-inline:auto, which was what centered the whole block. */
  .footer-bottom .fb-nonprofit {
    text-align: left !important;
    font-style: normal;
  }
  .fb-nonprofit a { color: inherit; text-decoration: underline; }
  /* footer-credits link spacing */
  .footer-bottom .footer-credits { letter-spacing: .002em; }
  /* 2026-09-04: the extra 2px margin around each link here (tuned for the
     old "Site By X · Photography From Y" copy) was adding visibly more
     gap around linked words than the plain spaces around unlinked ones in
     the new credits line – removed rather than re-tuned, since the text's
     own natural spacing is already even without it. */

  /* Stay Informed: label optically centred against the mail icon */
  .fn-head { display: flex; align-items: center; gap: 12px; }
  .fn-head .fn-ic { display: grid; place-items: center; flex: none; }
  .fn-head > div { display: flex; align-items: center; }
  /* 2026-09-05, ACTUAL ROOT CAUSE of "Stay Informed" sitting too high
     relative to Product/Trust/Built For – verified by direct DOM
     measurement: all four headings' containers sit within 1px of each
     other at the top, so the misalignment isn't a positioning problem at
     all. It's that this rule gave "Stay Informed" a tight line-height:1
     (16px) while the other three headings inherit a taller, more open
     line-height (~25.6px) – same top edge, but a shorter line-box means
     the glyphs themselves sit higher within it. Removed rather than
     tuned: .fn-head's own align-items:center (above) already centers the
     icon against the text regardless of the text's line-height, so this
     wasn't actually needed for the icon-centering goal it was written
     for, and removing it lets the text inherit the same line-height as
     its siblings instead. */

  /* arrow button was riding high against the field */
  .fn-form { display: flex; align-items: stretch; gap: 10px; }
  .fn-form input { height: 48px; box-sizing: border-box; }
  .fn-form button {
    height: 48px !important; width: 48px !important; flex: none;
    align-self: stretch; margin: 0 !important;
    display: grid; place-items: center;
  }
}

/* ---- 5 · Hero: copy left, search console right ------------------------
   hero-right (the search console this 2-column split was for) has been
   display:none since search moved to the promo bar – the second column
   was still reserving roughly half of hero-copy's width for nothing,
   which is why the headline and paragraph read narrower than the actual
   available space. Single column now; hero-left gets the room the empty
   column was wasting. */
@media screen and (min-width: 1000px) {
  .hero-copy {
    display: grid !important;
    grid-template-columns: minmax(0, 1fr);
    gap: clamp(36px, 4.5vw, 72px);
    align-items: center;
    max-width: 1240px !important;
    text-align: left;
  }
  .hero { text-align: left !important; justify-items: stretch !important; }
  .hero-left { text-align: left; }
  .hero-left .eyebrow-lg { text-align: left; }
  .hero-right { justify-self: stretch; }
}
@media screen {
  .hero h1 { text-align: left; margin-bottom: clamp(22px, 2.6vw, 32px) !important; }
  .hero .eyebrow-lg { text-align: left; }

  /* the description now reads as the subhead: larger, left aligned */
  .hero-lead-frame { margin: 0 !important; max-width: none !important; padding: clamp(24px,2.6vw,32px) clamp(24px,3vw,36px); }
  .hero-lead-frame > .hero-lead {
    text-align: left !important;
    font-size: clamp(1.04rem, 1.35vw, 1.2rem) !important;
    line-height: 1.62 !important;
    max-width: none !important;
    color: #C2C9DB !important;
  }

  .search-panel { max-width: none !important; margin: 0 auto !important; }
  .search-footer { justify-content: flex-start; }

  /* search CTA: gradient, matching the footer newsletter button */
  .search-field-wrap .analyze-button,
  .analyze-button.button-primary {
    background: linear-gradient(120deg, #A259FF, #FF6B6B) !important;
    border: 0 !important;
    border-radius: 12px !important;
    box-shadow: none !important;
    text-shadow: none !important;
    text-transform: none !important;
    letter-spacing: 0 !important;
    font-weight: 700 !important;
    height: 50px !important;
  }
  .search-field-wrap .analyze-button:hover,
  .analyze-button.button-primary:hover {
    background: linear-gradient(120deg, #B370FF, #92D495) !important;
    filter: brightness(1.03); transform: none !important; box-shadow: none !important;
  }
  .search-field-wrap .analyze-button:active,
  .analyze-button.button-primary:active { transform: none !important; box-shadow: none !important; filter: brightness(.96); }
  .search-field-wrap .analyze-button svg { filter: none; }
}
@media screen and (max-width: 999px) {
  .hero-copy { display: block !important; }
  .hero-right { margin-top: clamp(30px, 5vw, 44px); }
  .search-footer { justify-content: center; }
}
/* bracket accents belonged to the centred hero; they read as artifacts now */
@media screen { .search-panel::before, .search-panel::after { display: none !important; } }

/* ---- 6 · Hero background photo (rotates per load) + checklist ---------- */
@media screen {
  .hero { position: relative; overflow: hidden; isolation: isolate; }
  .hero-photo {
    position: absolute; inset: 0; z-index: 0;
    background-size: cover; background-position: center;
    opacity: 0; transition: opacity .9s ease;
    pointer-events: none;
  }
  .hero-photo.is-on { opacity: .09; }
  .hero-photo::after {
    content: ""; position: absolute; inset: 0;
    background: linear-gradient(180deg, rgba(14,16,24,.55), rgba(14,16,24,.88));
  }
  .hero-copy { position: relative; z-index: 1; }

  /* checklist returns, sitting under the search console */
  .hero-right .decision-card {
    margin-top: clamp(20px, 2.4vw, 28px);
    background: rgba(22,25,38,.72) !important;
    border: 1px solid rgba(255,255,255,.09) !important;
    border-radius: 14px; padding: 20px 22px;
    -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  }
  .hero-right .decision-card ul { display: grid; gap: 11px; margin: 0; padding: 0; list-style: none; }
  .hero-right .decision-card li {
    display: flex; align-items: center; gap: 11px;
    color: #C4CBDD; font-size: .95rem;
  }
  .hero-right .decision-card li svg { width: 16px; height: 16px; color: #81C884; flex: none; }
}

/* ---- 7 · Nav buttons: flat, capitalised ------------------------------- */
@media screen {
  .header-shell .nav-map-btn,
  .header-shell .donate-button.button-primary {
    text-transform: uppercase !important;
    letter-spacing: .05em !important;
    font-size: .8rem !important;
  }
}
@media (prefers-reduced-motion: reduce) { .hero-photo { transition: none; } }
/* the checklist was absolutely positioned for the old slideshow overlay */
@media screen {
  .hero-right .decision-card {
    position: static !important;
    inset: auto !important;
    width: auto !important;
    max-width: none !important;
    transform: none !important;
  }
}
/* ---- Search CTA: solid, no gradient ----------------------------------- */
@media screen {
  .search-field-wrap .analyze-button,
  .analyze-button.button-primary {
    background: #A259FF !important;
    background-image: none !important;
  }
  .search-field-wrap .analyze-button:hover,
  .analyze-button.button-primary:hover {
    background: #B06BFF !important;
    background-image: none !important;
    filter: none !important;
  }
  .search-field-wrap .analyze-button:active,
  .analyze-button.button-primary:active { background: #9350EE !important; filter: none !important; }
}

/* ---- Hero photo: full-bleed and more visible --------------------------- */
@media screen {
  /* .hero is a constrained section-shell – break the photo out to the viewport */
  .hero-photo {
    left: 50% !important; right: auto !important;
    width: 100vw !important;
    transform: translateX(-50%);
    max-width: none !important;
  }
  .hero-photo.is-on { opacity: .24; }
  /* scrim still guarantees text contrast, just lighter than before */
  .hero-photo::after {
    background:
      linear-gradient(180deg, rgba(14,16,24,.42) 0%, rgba(14,16,24,.58) 55%, rgba(14,16,24,.80) 100%),
      linear-gradient(90deg, rgba(14,16,24,.55) 0%, rgba(14,16,24,.10) 60%, rgba(14,16,24,.35) 100%);
  }
}

/* ---- Hero photo: truly edge-to-edge -----------------------------------
   .hero is a width-constrained .section-shell with overflow:hidden, which
   cropped the 100vw photo back to the content column. Let the photo escape
   the shell; the hero still clips nothing else because only the photo
   extends past it. */
@media screen {
  .hero { overflow: visible !important; }
  .hero-photo {
    left: calc(50% - 50vw) !important;
    right: auto !important;
    width: 100vw !important;
    transform: none !important;
  }
  /* guard: a 100vw child must never create a horizontal scrollbar */
  html, body { overflow-x: clip; }
}

/* ---- Hero photo: brighter, soft-faded, no black block ------------------
   Previous pass darkened the right edge for text contrast, which read as a
   black bar, and the photo ended on a hard horizontal line at the section
   boundary. Text protection is now concentrated on the left where the copy
   actually sits, and the photo dissolves into the page instead of stopping. */
@media screen {
  .hero-photo.is-on { opacity: .41; }

  /* 2026-09-02: fades hero-photo to fully 0% as framework's own top edge
     reaches the top of the viewport – per request, it should be genuinely
     gone by then, not just covered. --hero-photo-fade is set continuously
     in app.js (1 = normal .41 opacity, all the way down to 0 = invisible)
     in the same scroll handler as the card/heading reveals. Written as
     its own rule after the base one above (not folded into it) so the
     relationship to the un-faded .41 value stays explicit and this can't
     silently drift out of sync with it. */
  .hero-photo.is-on { opacity: calc(.41 * var(--hero-photo-fade, 1)); }

  /* soft dissolve at the bottom (and a touch at the top) – no hard edge */
  .hero-photo {
    -webkit-mask-image: linear-gradient(180deg, rgba(0,0,0,.55) 0%, #000 18%, #000 55%, rgba(0,0,0,.55) 78%, rgba(0,0,0,0) 100%);
    mask-image: linear-gradient(180deg, rgba(0,0,0,.55) 0%, #000 18%, #000 55%, rgba(0,0,0,.55) 78%, rgba(0,0,0,0) 100%);
  }

  /* scrim: gentle overall, stronger only behind the left-hand copy */
  .hero-photo::after {
    background:
      linear-gradient(90deg, rgba(12,14,22,.72) 0%, rgba(12,14,22,.46) 34%, rgba(12,14,22,.10) 58%, rgba(12,14,22,0) 100%),
      linear-gradient(180deg, rgba(12,14,22,.30) 0%, rgba(12,14,22,.12) 40%, rgba(12,14,22,.34) 100%) !important;
  }
}
/* eyebrow sat at 4.40:1 over the brighter photo – just under AA for small text */
@media screen { .hero .eyebrow-lg { color: #C6CDDC !important; } }
/* The 72% left scrim crushed the left edge to near page-background, which read
   as a black band on wide screens. The copy sits in a framed panel and the
   headline is large white type, so it needs far less protection than that. */
@media screen {
  .hero-photo::after {
    background:
      linear-gradient(90deg, rgba(12,14,22,.42) 0%, rgba(12,14,22,.24) 30%, rgba(12,14,22,.06) 55%, rgba(12,14,22,0) 100%),
      linear-gradient(180deg, rgba(12,14,22,.22) 0%, rgba(12,14,22,.08) 40%, rgba(12,14,22,.30) 100%) !important;
  }
}
/* purple headline measured 3.07:1 on the brighter photo – passes large-text AA
   but with no margin. Slightly lifted tone + a soft shadow for real legibility. */
@media screen {
  .hero h1 span { color: #BB86FF !important; -webkit-text-fill-color: #BB86FF !important; }
}

/* Client request (2026-09-01): brand color into brand purple, not a flat
   color. 2026-09-02: shadow removed on request – flagged the legibility
   trade-off when adding it, client decided against it anyway. Compared
   against yellow/purple and green/purple; pink/purple is the one that
   stuck. 2026-09-02: softened both colors a touch (mixed a little toward
   white rather than the fully-saturated brand hex) and added a very slow
   flowing animation – a 3-stop gradient (pink-purple-pink) at 200% width
   so it can pan seamlessly, animating background-position slowly back
   and forth rather than changing the colors themselves. Retargeted from
   .hero h1 span to #heroTitleGradient once that line moved out from
   under the H1 (see the comment on .hero-title-line2 in the CSS and
   heroTitle/heroTitleGradient markup in index.html for why). */
@media screen {
  #heroTitleGradient {
    background-image: linear-gradient(90deg, #FF7089, #AD75FF, #FF7089) !important;
    background-size: 220% 100% !important;
    background-clip: text !important;
    -webkit-background-clip: text !important;
    color: transparent !important;
    -webkit-text-fill-color: transparent !important;
    text-shadow: none !important;
    animation: hero-gradient-flow 22s ease-in-out infinite;
  }
}
@keyframes hero-gradient-flow {
  0%, 100% { background-position: 0% 50%; }
  50% { background-position: 100% 50%; }
}
@media (prefers-reduced-motion: reduce) {
  #heroTitleGradient { animation: none; }
}

/* ---- "Try:" chips – tidy grid, readable over the photo ---------------- */
@media screen {
  .search-footer {
    display: grid !important;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px;
    margin-top: var(--s5);
    align-items: stretch;
  }
  /* the label gets its own line so the chips form an even block */
  .search-footer > span:first-child {
    grid-column: 1 / -1;
    font-size: .72rem; font-weight: 700; letter-spacing: .14em;
    text-transform: uppercase; color: #C6CDDC;
    margin-bottom: 2px;
  }
  .search-footer button {
    width: 100%; justify-content: flex-start;
    background: rgba(16,19,30,.92) !important;
    border: 1px solid rgba(255,255,255,.18) !important;
    color: #E8ECF5 !important;
    font-size: .9rem !important; font-weight: 600;
    padding: 10px 14px !important;
    border-radius: 12px !important;
    -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
    box-shadow: 0 2px 10px rgba(0,0,0,.30);
    transition: border-color .16s ease, background .16s ease;
  }
  .search-footer button:hover {
    background: rgba(26,30,46,.96) !important;
    border-color: rgba(162,89,255,.65) !important;
  }
  .search-footer button::before { width: 24px; height: 24px; }
}
@media screen and (max-width: 999px) {
  .search-footer { justify-items: stretch; }
}
@media screen and (max-width: 560px) {
  .search-footer { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media screen { .search-footer button .chip-art { width: 24px; height: 24px; border-radius: 7px; } }

/* ---- Splash: 2x size + cinematic treatment ---------------------------
   Every effect is a static CSS layer over the canvas, so the render loop
   is untouched and the animation still runs on the compositor. */
.splash-screen {
  position: relative;
  display: block;
  border-radius: 14px;
  overflow: hidden;
  max-width: 100%;
  box-shadow: 0 30px 80px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.06);
}
.splash-screen > canvas { display: block; max-width: 100%; height: auto; }

/* CRT scanlines */
.sc-scan {
  position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(
    180deg, rgba(0,0,0,.28) 0 1px, rgba(0,0,0,0) 1px 3px);
  mix-blend-mode: multiply;
  opacity: .55;
  animation: scFlicker 3.6s steps(2, end) infinite;
}
@keyframes scFlicker { 0%,92%,100% { opacity: .55; } 94% { opacity: .40; } 96% { opacity: .62; } }

/* lens vignette + a soft cool bloom in the corners */
.sc-vignette {
  position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(120% 92% at 50% 46%, rgba(0,0,0,0) 42%, rgba(4,5,12,.62) 100%),
    radial-gradient(60% 50% at 50% 40%, rgba(162,89,255,.16), rgba(0,0,0,0) 70%);
}

/* letterbox bars */
.sc-bar {
  position: absolute; left: 0; right: 0; height: 7.5%;
  background: #05060C; pointer-events: none;
  box-shadow: 0 0 18px rgba(0,0,0,.7);
}
.sc-bar-top { top: 0; }
.sc-bar-bottom { bottom: 0; }

#splashCanvas { filter: drop-shadow(0 10px 26px rgba(162,89,255,.34)) saturate(1.08) contrast(1.06); }

@media (max-width: 680px) {
  .splash-screen { border-radius: 10px; }
  .sc-bar { height: 6%; }
}
@media (prefers-reduced-motion: reduce) { .sc-scan { animation: none; } }
/* Perf: a per-frame CSS filter and a blend mode over an animating canvas
   forced full-surface repaints and cost ~11fps. Glow moved to the static
   wrapper; scanlines use plain alpha instead of multiply. */
#splashCanvas { filter: none !important; }
.splash-screen {
  box-shadow: 0 30px 80px rgba(0,0,0,.55), 0 0 0 1px rgba(255,255,255,.06),
              0 12px 46px rgba(162,89,255,.30);
}
.sc-scan { mix-blend-mode: normal !important; opacity: .40; }
@keyframes scFlicker { 0%,92%,100% { opacity: .40; } 94% { opacity: .28; } 96% { opacity: .48; } }

/* ---- Splash: drop the frame ------------------------------------------
   The canvas is transparent apart from the sprites, so any full-rect
   overlay (scanlines, vignette, letterbox) painted a visible rectangle
   over the page – that was the box. Frame and overlays removed; the glow
   follows the artwork instead of a box edge. */
.splash-screen {
  border-radius: 0 !important;
  overflow: visible !important;
  box-shadow: none !important;
  background: transparent !important;
}
.sc-scan, .sc-vignette, .sc-bar { display: none !important; }
#splashCanvas {
  filter: drop-shadow(0 12px 28px rgba(162,89,255,.38)) !important;
}

/* ======================================================================
   CLIENT UPDATE PASS – 2026-08-11
   Implements the nine items in "Health e Gaming – Webpage Update
   Instructions". Appended last so it wins the cascade without unpicking
   the earlier passes. Numbers below match the brief.
   ====================================================================== */

/* ---- 02 · Minimap intro: headline, bigger copy, no decorative frame ---- */
@media screen {
  .hero-lead-frame {
    border: 0 !important;
    background: none !important;
    padding: 0 !important;
    border-radius: 0 !important;
    max-width: none !important;
    margin-top: clamp(26px, 3vw, 38px) !important;
  }
  /* corner brackets were drawn by the frame's own pseudo-elements and a
     helper span; the span is gone from the markup, these are the rest */
  .hero-lead-frame::before,
  .hero-lead-frame::after,
  .hlf-corners { display: none !important; }

  .hero-lead-title {
    margin: 0 0 clamp(12px, 1.2vw, 18px);
    font-size: clamp(1.55rem, 2.15vw, 2.1rem);
    line-height: 1.16;
    letter-spacing: -.025em;
    font-weight: 800;
    color: #FFFFFF;
    text-align: left;
    text-shadow: 0 2px 16px rgba(6,8,16,.5);
  }
  .hero-lead-frame > .hero-lead {
    font-size: clamp(1.14rem, 1.5vw, 1.35rem) !important;
    line-height: 1.68 !important;
    color: #D3D9E7 !important;
    text-align: left !important;
    /* Narrowed from 76ch to reliably wrap to 4 lines instead of 3, per
       client request – with some margin so it stays 4 lines even with
       minor font-rendering differences across browsers/platforms. */
    max-width: 62ch !important;
  }
}
@media screen and (max-width: 760px) {
  .hero-lead-title { font-size: clamp(1.35rem, 5.4vw, 1.7rem); }
  .hero-lead-frame > .hero-lead { font-size: 1.06rem !important; }
}

/* ---- 04 · Category modal: no divider seam through the action area ----- */
@media screen {
  .modal-footer {
    border-top: 0 !important;
    background: transparent !important;
    box-shadow: none !important;
  }
}

/* ---- 05 · "Empowering families" card sits centred in its band ---------
   The preview section already contributes its own 104px bottom padding, so
   the section's matching top padding doubled the space above the card
   (208px above vs 104px below). Dropping the top padding balances it. */
@media screen {
  .cta-section { padding-top: 0 !important; }
}

/* ---- 06 · Footer logos use the header assets and treatment ------------
   The footer was loading the yellow/purple logo variants at a different
   aspect and scale (118x56 vs the header's 92x43) because `.footer-brand
   img` set an explicit width. Both lockups now use the same two assets and
   are sized by one shared rule, so they can't drift apart again.

   2026-09-04: that pairing is intentionally broken here – explicit request
   to make the footer logos bigger, which by definition means they no
   longer match the header's size. Same assets, same treatment otherwise,
   just their own height now instead of sharing .brand-mark's. */
@media screen {
  .brand .brand-mark {
    height: 44px !important;
    width: auto !important;
    display: block;
    object-fit: contain;
  }
  /* 2026-09-04: sized down from 54px – with the row now forced to nowrap
     (see .footer-logos above) and the column an equal 1/5 share instead of
     the wider 1.5-2fr it used to get, 54px left the two logos combined
     wider than the column at typical desktop widths, which is what pushed
     wrapping in the first place. This is the largest height that reliably
     keeps both logos on one line without the row overflowing past the
     column instead. If this still isn't the balance you want between
     "bigger" and "side by side, evenly spaced columns," those two asks are
     genuinely in tension here – tell me which one to bend on. */
  .site-footer .footer-logo {
    height: 42px !important;
    width: auto !important;
    display: block;
    object-fit: contain;
  }
  .footer-logos { gap: 12px; align-items: center; }
  .footer-logo-divider {
    width: 1px; height: 30px; flex: none; display: block;
    background: rgba(255,255,255,.20);
  }
}
@media screen and (max-width: 980px) {
  .brand .brand-mark { height: 38px !important; }
  .site-footer .footer-logo { height: 37px !important; }
}
@media screen and (max-width: 720px) {
  .brand .brand-mark { height: 30px !important; }
  .site-footer .footer-logo { height: 29px !important; }
  .footer-logo-divider { height: 18px; }
}
@media screen and (max-width: 420px) {
  .brand .brand-mark { height: 26px !important; }
  .site-footer .footer-logo { height: 25px !important; }
}

/* ---- 07 · Social row spans the tagline width --------------------------
   Both boxes share one max-width so the row and the tagline always line up
   ("Healthier habits online, happier minds IRL." measures ~265px). */
@media screen {
  .footer-brand p { max-width: 17rem !important; }
  .footer-social {
    width: 100%;
    max-width: 17rem;
    justify-content: space-between;
    gap: 8px;
  }
}

/* ---- 08 · Hero shares the navigation's content boundaries -------------
   .hero-copy was capped at 1240px and auto-centred inside the 1392px
   shell, so it sat 76px inside the nav on both sides. Align it to the
   header shell's own inner padding instead. */
@media screen {
  .hero-copy {
    max-width: none !important;
    width: auto !important;
    /* .hero sets justify-items:center, which sizes the item to its content
       instead of the column – stretch it so the margins define the edges */
    justify-self: stretch !important;
    /* mirrors .header-shell's own inner padding, so the hero's left edge
       lands on the logo and its right edge on the header actions */
    margin-inline: 22px 14px !important;
  }
}
@media screen and (max-width: 720px) {
  /* .header-shell padding drops to 8px 9px 8px 12px at this breakpoint */
  .hero-copy { margin-inline: 12px 9px !important; }
  /* .framework-inner needs to track the same drop, or "Four areas" content
     drifts out of alignment with the hero/nav text above it again on
     narrow screens even though it matches at desktop widths. */
  .framework-inner.section-shell { padding: 0 9px 0 12px; }
}

/* ---- 09 · Hero image runs behind the navigation -----------------------
   The photo is absolutely positioned inside .hero, which starts below the
   sticky header – leaving a solid dark band at the top of the page. Pull
   its top edge up past the header (no layout shift, since only the
   absolutely positioned photo moves) and let the nav sit on glass.
   --hero-rise is calibrated to the header's own rendered height (measured,
   not guessed) so the photo's top edge lands exactly at the is-compact
   trigger point (the promo bar's height) – any mismatch here is what
   caused the visible "hop": the browser jumping from wherever scroll had
   left the (still-absolute) photo positioned to the compact state's
   fixed top:0 the instant is-compact flips, since those two would
   otherwise disagree about where the photo's top edge belongs at that
   exact scroll position. */
@media screen {
  :root { --hero-rise: 92px; }

  .hero-photo {
    top: calc(-1 * var(--hero-rise)) !important;
    /* Height matches the is-compact fixed state exactly (100vh) instead of
       being driven by .hero's own (shorter) box – see the 2026-08-31
       "hero fills to the page break" pass, which made .hero's own height
       viewport-relative but shorter than 100vh (it excludes the promo bar
       and header). Left as height:auto/bottom:0 here, the photo's own box
       was ~150-300px shorter at rest than it becomes the instant is-compact
       flips it to height:100vh – and since background-size:cover crops
       differently at different box heights, that size jump reads as the
       image itself visibly hopping/re-cropping the moment the nav locks
       in. Matching the height in both states means the crop is identical
       throughout; only the positioning mode changes at the threshold. */
    height: 100vh !important;
  }
  /* the old mask faded the top out; keep it solid where the nav sits */
  .hero-photo {
    -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 55%, rgba(0,0,0,.55) 78%, rgba(0,0,0,0) 100%) !important;
    mask-image: linear-gradient(180deg, #000 0%, #000 55%, rgba(0,0,0,.55) 78%, rgba(0,0,0,0) 100%) !important;
  }

  .header-shell {
    background: transparent !important;
    border: 1px solid transparent !important;
    box-shadow: none !important;
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
  }
  .header-shell::before { opacity: 0; }
  .site-header.is-compact .header-shell::before { opacity: 0; }
  /* Once scrolled clear of the white promo bar, the nav becomes a
     full-bleed, genuinely opaque light glass bar – Adobe's own real
     behavior, not a dark-navy translucent one. Since the bar goes light,
     nav text and the logo both flip to their dark/black versions so they
     stay legible (see .brand-mark-white/.brand-mark-black crossfade and
     the .nav-link color override below). */
  /* Nav items must not shift position when this triggers – width, margin,
     padding and corner radius all stay exactly as they are at rest. Only
     the background, border, shadow and text color change. */
  .site-header.is-compact .header-shell {
    /* 2026-09-02: opacity 68% -> 55% (less opaque, per request) so more of
       the glass distortion/blur shows through underneath. */
    background: rgba(255,255,255,.55) !important;
    border: 1px solid rgba(15,23,42,.08) !important;
    /* 2026-09-02 (later): pulled back from the previous bold increase –
       client sent an actual adobe.com screenshot as reference, and its
       shadow at the same rounded-corner edge is notably subtle, not
       dramatic. Matching that directly rather than guessing. */
    /* 2026-09-02 (even later): nudged stronger again, per request – kept
       modest on purpose given the note directly above; this is "a little
       stronger," not a reversal of that earlier, reference-matched call. */
    box-shadow: 0 3px 8px rgba(15,23,42,.19), 0 12px 30px rgba(15,23,42,.15) !important;
    /* !important added 2026-09-02: this was always the intended light
       glass treatment, but the "calm surfaces" pass (~line 3364) sets
       backdrop-filter:none !important unconditionally on .header-shell,
       which silently won over this rule's non-!important blur the whole
       time. A later, separate attempt to fix the same dead-blur symptom
       added its own override rather than recognizing this rule already
       existed – with dark colors and a much heavier 60-72px blur, which
       is why it showed up dark and (per client feedback) over-blurred.
       That duplicate rule is removed; this is the one true source now,
       simply given the specificity to actually win.
       2026-09-02 (later): client sent an actual adobe.com screenshot –
       sampled it directly. Their nav fully abstracts whatever's behind
       it into soft, textureless color washes (no discernible shapes at
       all), noticeably stronger than the 24px this had. Pushed to 44px
       and nudged opacity up to .8 (from .7) to match how light/white
       their bar reads even over a heavily saturated red hero image.
       Pulled back down to .68, request after seeing it live: at .8 the
       blur/saturate underneath was essentially fully washed out by the
       white fill, so the "glass" reads as a flat off-white bar rather
       than something with visible distortion behind it. Blur/saturate
       amounts are untouched – this is only the fill's alpha. */
    -webkit-backdrop-filter: blur(44px) saturate(160%) !important;
    backdrop-filter: blur(44px) saturate(160%) !important;
  }
  /* Logo crossfade: both sit stacked in the same spot, opacity swaps which
     one is visible. Two images rather than swapping one <img>'s src, since
     CSS can transition opacity but can't animate a src change. */
  .brand-mark-stack { position: relative; display: inline-block; }
  .brand-mark-black { position: absolute; inset: 0; opacity: 0; transition: opacity .3s ease; }
  .site-header.is-compact .brand-mark-white { opacity: 0; }
  .site-header.is-compact .brand-mark-black { opacity: 1; }
  /* Nav text and controls go dark to stay legible on the new light bar. */
  .site-header.is-compact .nav-link,
  .site-header.is-compact .nav-caret { color: var(--ink) !important; }
}
@media screen and (max-width: 980px) { :root { --hero-rise: 6px; } }
@media screen and (max-width: 760px) { :root { --hero-rise: 0px; } }
@media (prefers-reduced-motion: reduce) { .hero-photo { transition: none; } }

/* ======================================================================
   CLIENT UPDATE PASS 2 – 2026-08-11 (Update Sheet 2)
   ====================================================================== */

/* ---- S2·02 · No dark band across the game selection card --------------
   .modal-footer was `position: sticky; bottom: 0` with a dark gradient, so
   it floated OVER the match grid and blacked out whatever scrolled beneath
   it – the publisher, year and edition rows in the review screenshot.
   Making the modal a flex column and letting the grid own the scroll means
   the buttons never overlap content, so the footer needs no scrim at all
   and the whole modal reads as one continuous surface. */
@media screen {
  .modal-backdrop .modal {
    display: flex;
    flex-direction: column;
    overflow: hidden;                 /* the inner region scrolls, not the modal */
  }
  .modal-backdrop .modal > .modal-header,
  .modal-backdrop .modal > .modal-intro,
  .modal-backdrop .modal > .modal-footer { flex: none; }

  .modal-backdrop .modal > .modal-content,
  .modal-backdrop .modal-wide > .game-match-grid {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }
  /* Once the grid has a definite height (it's now a flex item), the default
     `align-content: normal` compresses the auto rows to fit instead of
     letting them overflow and scroll – which flattened each card to ~41px
     on narrow viewports. `start` keeps rows at their content height. */
  .modal-backdrop .modal-wide > .game-match-grid {
    align-content: start;
    grid-auto-rows: max-content;
    padding-bottom: 6px;   /* room for the last row's focus ring / shadow */
  }

  .modal-backdrop .modal > .modal-footer {
    position: static;                 /* was sticky – that's what overlapped */
    background: transparent !important;
    border-top: 0 !important;
    box-shadow: none !important;
  }
}

/* ---- S2·03 · Six suggested games in a balanced 3 x 2 grid -------------
   No CSS needed: `.search-footer` is already a 3-column grid with the
   "Try:" label spanning all three ("Try: chips – tidy grid" block above).
   Five chips left the second row half empty; the sixth (Rocket League, in
   index.html) completes it as 3 + 3. Cover art arrives at runtime from
   igdbDecorateExampleChips(), so the new chip matches the others with no
   asset work. Below 560px the grid drops to 2 columns = 3 even rows.

   The one thing the sixth chip DID need: a placeholder glyph. The set only
   defined :nth-of-type(1)–(5), and with `--glyph` unset the `background`
   shorthand became invalid and the swatch rendered empty. Neutral rocket
   mark, deliberately not the game's trademarked logo – same as the rest. */
@media screen {
  .search-footer button:nth-of-type(6) { --chip: #E07B39;
    --glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23fff'%3E%3Cpath d='M12 2c2.8 2.4 4.2 5.7 4.2 9.2L12 14.4 7.8 11.2C7.8 7.7 9.2 4.4 12 2z'/%3E%3Cpath d='M9 15.6l3 1.6 3-1.6-1 4.4-2 1.4-2-1.4z' opacity='.85'/%3E%3C/svg%3E"); }
}

/* ======================================================================
   GAME MINIMAP REPORTING FRAMEWORK – 2026-08-11
   Report area restructured to the "Updated Game Minimap Parameters &
   Structure" document: an ESRB Rating topline, then each parameter as a
   Topline plus named, labelled findings.
   ====================================================================== */
@media screen {
  /* "Topline:" prefix on every section summary */
  .topline-label {
    color: var(--cat, #A259FF);
    font-weight: 850;
    letter-spacing: .01em;
    margin-right: 2px;
  }

  /* Findings are "Label: detail" – the label is the framework parameter */
  .finding-list .finding-label {
    color: var(--navy-900, #0B0F1C);
    font-weight: 800;
  }
  .finding-list li { margin-bottom: 7px; }
  .finding-list li:last-child { margin-bottom: 0; }

  /* Section 1: ESRB Rating – topline only, no expander (per the document,
     "no bullet points needed here, just the official rating") */
  /* DEAD: overridden by the .cat-esrb rule later in this same @media screen
     block. Left in place rather than deleted so the history is visible, but do
     not edit this one expecting a change. */
  .report-category.cat-esrb { --cat: #E0A33A; }
  .category-heading-static {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: 15px;
  }
  .category-badge svg { width: 24px; height: 24px; }
  .esrb-block .category-summary { margin-bottom: var(--s5); }
}

/* Dark report surface: keep the labels legible against the dark theme */
@media screen {
  .report-main .finding-list .finding-label,
  .report-content .finding-list .finding-label { color: inherit; }
}

/* ---- Report disclaimer: icon + modal instead of an inline banner --------
   The yellow disclaimer block dominated the top of every report. The text
   now lives behind an info icon in the report kicker; the original block is
   kept in the DOM for PRINT only, so a printed or PDF'd report still
   carries the notice. */
@media screen {
  .disclaimer-print-only { display: none !important; }

  /* the kicker is space-between; with a third child the date drifted to the
     middle. Push the eyebrow left so the date and its icon stay paired. */
  .report-kicker { align-items: center; justify-content: flex-start; }
  .report-kicker > small { margin-right: auto; }
  .report-info-btn {
    flex: none;
    width: 28px; height: 28px;
    display: inline-grid; place-items: center;
    margin-left: 8px; padding: 0;
    border-radius: 50%;
    border: 1px solid rgba(247,216,74,.34);
    background: rgba(247,216,74,.10);
    color: #F3DC72;
    cursor: pointer;
    transition: background .18s ease, border-color .18s ease, transform .18s ease;
  }
  .report-info-btn svg { width: 15px; height: 15px; }
  .report-info-btn:hover {
    background: rgba(247,216,74,.20);
    border-color: rgba(247,216,74,.6);
    transform: translateY(-1px);
  }
  .report-info-btn:focus-visible { outline: 2px solid #F3DC72; outline-offset: 2px; }
}
@media print {
  .report-info-btn { display: none !important; }
  .disclaimer-print-only { display: flex !important; }
}

/* ---- Quick stats: unknown values link out instead of dead-ending --------
   IGDB carries no playtime (its time-to-beat endpoint 404s), so main-story
   and completionist hours come from a curated table in app.js. When a title
   isn't in it the card becomes a link to that game's HowLongToBeat search
   rather than showing an invented number. */
@media screen {
  .quick-stat-link { text-decoration: none; cursor: pointer; position: relative; }
  .quick-stat-link:hover { border-color: rgba(162,89,255,.55); }
  .quick-stat-link .qs-hint {
    display: block;
    margin-top: 6px;
    font-size: .7rem;
    letter-spacing: .04em;
    color: #A98BFF;
  }
  .quick-stat-link:focus-visible { outline: 2px solid #A259FF; outline-offset: 2px; }
}
@media print {
  .quick-stat-link .qs-hint { display: none; }
}

/* ---- Quick stats: row adapts when the playtime cards collapse ----------
   Main story + Completionist merge into a single "Playtime" card when
   neither has a confirmed value, leaving three cards instead of four. */
@media screen {
  .quick-stats { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
}
@media screen and (max-width: 900px) {
  .quick-stats { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
}

/* ---- Quick stats: label is the headline, value the supporting line -----
   Matches the verdict cards. The value now sits in the <span>, so it needs
   readable size rather than the old caption treatment. */
@media screen {
  .quick-stat strong { font-size: 1.06rem; letter-spacing: -.01em; }
  .quick-stat span { font-size: .92rem; line-height: 1.45; }
}
@media print {
  .quick-stat strong { font-size: 11pt; }
  .quick-stat span { font-size: 9.5pt; }
}

/* ======================================================================
   REPORT SECTIONS NAV – REBUILT 2026-09-02
   This component had accumulated five separate passes across this file
   (an original base rule, a dark-redesign pass, a "card & cover polish"
   pass with no width qualifier that was silently setting position:static,
   the 2026-08-11 sticky-positioning fix, and two more recent shadow/blur
   cleanups that each only caught one breakpoint at a time). Fixing the
   shadow/blur in one spot kept leaving it reproducible from another –
   three rounds of "remove it" each only found part of the problem.
   Consolidated into one place. box-shadow, filter, and backdrop-filter
   are forced to none as the FIRST declarations below, unconditionally,
   so no earlier rule anywhere else in this file can reintroduce any of
   them regardless of specificity or !important – this component no
   longer depends on being the last word in the cascade to stay clean.
   The four older fragments are left in place but emptied, each with a
   short pointer here, rather than deleted outright.
   ====================================================================== */
@media screen {
  /* Unconditional kill switch – deliberately not scoped to a breakpoint,
     not using a value some later rule could out-specificity, just "this
     never has any of these three effects, full stop." */
  .report-nav, .report-nav::before, .report-nav::after {
    box-shadow: none !important;
    filter: none !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
  }

  .report-nav button {
    text-transform: uppercase;
    letter-spacing: .07em;
    font-size: .715rem;
    font-weight: 800;
    color: rgba(255,255,255,.56);
    min-height: 44px;
    border: 0;
    background: transparent;
    cursor: pointer;
    transition: color .16s ease, background .16s ease;
  }
  .report-nav button:hover { color: #fff; background: rgba(255,255,255,.07); }
  .report-nav button:active { transform: scale(.97); transition: transform .07s ease-out; }
  .report-nav button.active {
    color: #fff;
    /* 2026-09-06: was linear-gradient(120deg, rgba(162,89,255,.34),
       rgba(255,107,107,.20)). Two problems with it, both visible once the
       edge mask above stopped hiding the right-hand side of this rail: the
       fill drifted from purple to red across a single small pill, and the
       red end sat at lower opacity than the purple end, so the item read as
       fading out rather than as one selected block. A single flat purple
       states "this is the section you are on" without implying a direction.
       .30 sits between the old stops so the overall weight is unchanged. */
    background: rgba(162,89,255,.30);
    /* inset only – never bleeds past the button's own edge */
    box-shadow: inset 0 0 0 1px rgba(255,255,255,.14) !important;
  }
  /* headings the nav jumps to shouldn't land under the sticky chrome */
  #overview, #minimap-section, #sources-section,
  .report-category { scroll-margin-top: 120px; }
}

/* ---- stacked form (below the rail breakpoint): horizontal sticky bar - */
@media screen and (max-width: 1279px) {
  .report-nav {
    position: sticky !important;
    top: 96px;
    z-index: 30;
    display: flex;
    gap: 5px;
    padding: 12px 16px;
    overflow-x: auto;
    background: #12102B !important;
    border-bottom: 1px solid var(--line-1, rgba(255,255,255,.09)) !important;
    isolation: isolate;
    contain: paint;
  }
}
@media screen and (max-width: 720px) {
  .report-nav { top: 78px; }
}

/* ---- rail form: a sticky column to the right of the report -----------
   NOT `position: fixed`: `html, body { overflow-x: clip }` (the guard that
   stops the 100vw hero photo causing sideways scroll) makes the body a
   containing block for fixed descendants, so a fixed rail resolves `top:50%`
   against the whole document and scrolls away. Sticky inside a grid column
   is immune to that, and `.report-main`'s overflow is unpinned above so the
   stickiness actually takes effect. */
@media screen and (min-width: 1280px) {
  .report-main {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 236px;
    gap: 28px;
    align-items: start;
  }
  .report-main > .report-nav { grid-column: 2; grid-row: 1; }
  .report-main > .report-content { grid-column: 1; grid-row: 1; min-width: 0; }

  .report-nav {
    position: sticky !important;
    top: 108px;
    z-index: 20;
    display: flex;
    flex-direction: column;
    gap: 3px;
    max-height: min(78vh, 640px);
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 14px 12px 12px;
    border-radius: 16px;
    /* 2026-09-02: matched to .report-hero's own border exactly (1px,
       var(--line-1) ~9% white) per request – the earlier 2px/45%
       treatment was a defensive over-correction from chasing the
       shadow/blur perception, and ended up visibly heavier than every
       other card on the page, including the one right next to it. */
    border: 1px solid var(--line-1, rgba(255,255,255,.09)) !important;
    /* Fully opaque now, not 90% – no alpha blending with anything behind
       this element at all, so there is nothing left for scroll position,
       sticky stacking, or a scaled-down view of this image to blend with,
       whatever the actual cause of the softness turns out to be. */
    background: #12102B !important;
    /* Paint containment + isolation: this element's rendering can no
       longer be affected by anything outside its own box, and nothing
       outside can be affected by it either – removes any possibility of
       cross-element visual interaction regardless of z-index or scroll
       position. */
    isolation: isolate;
    contain: paint;
  }
  .report-nav::before {
    content: "Report Sections";
    display: block;
    padding: 2px 10px 10px;
    font-size: .64rem;
    font-weight: 850;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: rgba(255,255,255,.42);
    border-bottom: 1px solid rgba(255,255,255,.08);
    margin-bottom: 8px;
  }
  .report-nav button {
    width: 100%;
    text-align: left;
    padding: 10px 11px;
    border-radius: 11px;
    white-space: normal;
    line-height: 1.32;
    position: relative;
  }
  /* marker on the active row so position reads at a glance */
  .report-nav button.active::before {
    content: "";
    position: absolute;
    left: 0; top: 50%;
    width: 3px; height: 17px;
    transform: translateY(-50%);
    border-radius: 0 3px 3px 0;
    background: linear-gradient(180deg, #A259FF, #FF6B6B);
  }
  .report-nav::-webkit-scrollbar { width: 6px; }
  .report-nav::-webkit-scrollbar-thumb { background: rgba(255,255,255,.16); border-radius: 3px; }
}
/* 2026-09-05: removed a stale `@media print { .report-nav { display: none
   !important; } }` that lived here – it directly contradicted the later
   print redesign (search "CONTENTS" further down) that deliberately shows
   this element as a print-friendly Contents box. Since that later rule
   comes after this one in the cascade, it already won regardless – this
   was dead weight that just made it look like there was an open question
   about whether .report-nav should print, when there wasn't one. */

/* ---- why the nav never stayed put --------------------------------------
   Two ancestors were cancelling it, which is why `position: sticky` looked
   applied but behaved like `static`:
     .report-shell has a transform  -> becomes the containing block for
                                       `fixed`, so the rail scrolled away
     .report-main  has overflow:hidden -> becomes the scroll container for
                                       `sticky`, so the bar never stuck
   Both are neutralised for screen only; print hides the nav regardless. */
@media screen {
  /* `.report-shell`'s transform is left alone – it comes from the reportIn
     entrance animation, and the rail is sticky rather than fixed now, so it
     no longer matters. Only the sticky-blocking overflow has to go. */
  .report-main { overflow: visible !important; }
}

/* ---- sticky sidebar taller than the viewport --------------------------
   `.report-sidebar` is sticky at top:112px and taller than the screen, so
   everything past the fold – including Print, Share and New Search – was
   pinned out of reach until you had scrolled the entire right-hand column.
   Capping it to the viewport and letting it scroll internally makes the
   whole card reachable at any scroll position. */
@media screen and (min-width: 981px) {
  .report-sidebar {
    align-self: start;
    max-height: calc(100dvh - 132px);
    /* 2026-09-04 – likely root cause of the IGDB credit line's leading "G"
       getting clipped: setting overflow-y alone leaves overflow-x
       unspecified, but per spec that doesn't mean it stays `visible` –
       when one axis is non-visible and the other is visible, the visible
       one is forced to compute as `auto` too (confirmed against MDN's
       overflow-x/overflow-y reference, not assumed). Declaring
       `overflow-x: visible` explicitly here would NOT fix it – the same
       coercion applies whether visible was implicit or written out, so
       that isn't the fix. What actually matters is that nothing inside
       this box can be wider than it, since an auto axis only clips
       something that genuinely overflows. Flagging this honestly: I
       could not visually confirm the credit line was the specific thing
       overflowing without a live browser, so this pairs with a defensive
       max-width/wrap rule on .rawg-credit itself (see its own rule) rather
       than resting on this comment alone.
    */
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: rgba(255,255,255,.22) transparent;
  }
  .report-sidebar::-webkit-scrollbar { width: 7px; }
  .report-sidebar::-webkit-scrollbar-thumb {
    background: rgba(255,255,255,.20);
    border-radius: 4px;
  }
  .report-sidebar::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,.34); }
}

/* Keep Print / Share / New Search pinned to the base of the sidebar so the
   primary actions are reachable without scrolling inside the card at all.
   Compact sizing, because at full button height the block ate most of the
   viewport and pushed the game details out of view. */
@media screen and (min-width: 981px) {
  /* 2026-09-01: right padding zeroed too - it was contributing 20px of the
     84px cover-to-hero gap (left/top/bottom were already zeroed earlier
     for the nav-bar-alignment and top-alignment fixes; right was never
     touched since nothing had asked for it until now). */
  .report-sidebar { padding-bottom: 0; padding-left: 0; padding-top: 0; padding-right: 0; }
  /* 2026-09-05, ACTUAL ROOT CAUSE of the credit line's leading "G" reading
     as clipped – not the overflow-x quirk I chased two rounds ago. The
     rule directly above zeroes ALL of .report-sidebar's padding (done
     deliberately, for cover-image alignment, per the comment below it).
     Every other visible child compensates with its own padding (buttons
     have it baked into their own style, .report-cover has 21px of its
     own for its internal content) – .rawg-credit is plain text with none
     of its own, so it was sitting flush against the now-zero-padding
     edge with no breathing room at all, on both sides. Restoring just
     enough here, on the one element that needs it, rather than
     re-padding the whole sidebar and reintroducing the gap that padding
     removal was fixing. */
  .rawg-credit { padding: 0 20px; }

  .report-sidebar-actions {
    position: sticky;
    bottom: 0;
    z-index: 2;
    gap: 6px;
    margin-top: 14px;
    padding: 10px 0 14px;
    /* solid, not a gradient: at ~0.9 alpha the rows underneath showed
       through the gaps between buttons. #070915 rather than the previous
       #0E1018: the page background is itself a gradient (body, ~line 60:
       #05060f -> #070915 -> #0a0b1c -> #080a17 -> #060711), and #0E1018
       sat visibly lighter than every one of those stops – a permanent,
       if subtle, seam around this panel at any scroll position, not
       something that only showed up at one spot. #070915 is close to
       all five stops rather than exactly matching any single one, since
       this panel is sticky and can't know which stop it's currently
       sitting against. */
    background: #070915;
  }
  /* short fade so content dissolves into the bar instead of cutting off */
  .report-sidebar-actions::before {
    content: "";
    position: absolute;
    left: 0; right: 0; top: -24px;
    height: 24px;
    pointer-events: none;
    background: linear-gradient(180deg, rgba(7,9,21,0), #070915);
  }

  .report-sidebar-actions .button {
    min-height: 0;
    padding: 9px 12px;
    font-size: .82rem;
    font-weight: 700;
    border-radius: 11px;
    gap: 8px;
  }
  .report-sidebar-actions .button svg { width: 15px; height: 15px; }
}

/* ======================================================================
   MOBILE PASS – 2026-08-11
   At 390px the header packed two logos, "Map the Game" and "Donate" into
   the bar, which pushed .menu-toggle to 384→428 – entirely off-screen.
   The hamburger was unreachable, so Methodology / Sources / Privacy /
   Disclaimer could not be opened on a phone at all.
   ====================================================================== */
@media screen and (max-width: 720px) {
  /* the two CTAs move to the drawer (both exist there) so the toggle fits */
  .header-actions .nav-map-btn,
  .header-actions .nav-search,
  .header-actions .donate-button { display: none !important; }

  .header-actions {
    justify-self: end;
    gap: 6px;
    min-width: 0;
  }
  .menu-toggle {
    display: grid !important;
    place-items: center;
    width: 44px;
    height: 44px;
    flex: none;
  }
  .header-shell {
    grid-template-columns: minmax(0, 1fr) auto !important;
    overflow: hidden;
  }
  .brand { min-width: 0; overflow: hidden; }

  /* tap targets: the logo links measured 55x26 */
  .brand-link { padding-block: 9px; }

  /* the report disclaimer icon was 28x28 */
  .report-info-btn { width: 36px; height: 36px; }
  .report-info-btn svg { width: 17px; height: 17px; }

  /* drawer: the added Map the Game sits above Donate */
  .drawer-cta { min-height: 46px; }
  .drawer-cta-quiet {
    background: rgba(255,255,255,.07);
    border: 1px solid rgba(255,255,255,.16);
    color: #fff;
  }
}

/* ---- mobile: report cards had their padding zeroed ---------------------
   `.report-category` is padded 28px on desktop but 0 on narrow screens, so
   every heading, Topline and finding sat flush against the card edge – and
   the 6px `::before` accent bar painted straight over the first characters
   ("Topline:" rendering as "opline:"). Restore padding, with extra on the
   left so nothing runs under the accent bar. */
@media screen and (max-width: 980px) {
  .report-category {
    padding: 18px 16px 18px 22px !important;
    border-radius: 18px;
  }
  .report-category .category-summary { margin-bottom: 0; }
  .report-category .category-details { margin-top: 14px; }
  /* the badge and chevron were also hard against the edges */
  .category-heading { gap: 11px; }
  .category-badge { width: 38px; height: 38px; border-radius: 11px; font-size: 1rem; }
  .category-badge svg { width: 19px; height: 19px; }
  .category-heading h3 { font-size: 1.12rem; line-height: 1.25; }
  .detail-block { padding-inline: 0; }
}

/* ---- mobile: consistent inset + rhythm inside report cards -------------
   `.detail-block` was `padding: 24px 0`, so each panel's content sat flush
   against its own edges while the card around it had padding – the nesting
   read as inconsistent. One inset value for every level, and slightly
   tighter vertical gaps now that the cards are narrower. */
@media screen and (max-width: 980px) {
  .category-details { gap: 14px !important; }

  .detail-block {
    padding: 16px 15px !important;
    border-radius: 14px;
    background: rgba(255,255,255,.035);
    border: 1px solid rgba(255,255,255,.07);
  }
  .detail-block h4 { margin: 0 0 10px; }
  .detail-block > p { margin: 0; }
  .detail-block ul { margin: 0; padding-left: 18px; }
  .detail-block li + li { margin-top: 9px; }

  .source-row { padding: 13px 14px; }
  .detail-block .source-row + .source-row { margin-top: 9px; }

  .insight-card { padding: 18px 16px; }
  .summary-grid { gap: 12px; }

  .report-section + .report-section { margin-top: 26px; }
  .report-section-head { margin-bottom: 14px; }
  .report-category + .report-category { margin-top: 14px; }
}

/* ======================================================================
   DEVELOPER NOTES pt3 – 2026-08-12
   ====================================================================== */

/* ---- #2 · halve the nav→copy gap, reuse it between sections ---------- */
@media screen {
  .hero { padding-block: clamp(38px, 5vw, 74px) 20px !important; }
  .framework-section { padding-block: clamp(70px, 9vw, 150px) !important; }
  .preview-section { padding-top: clamp(70px, 9vw, 150px) !important; padding-bottom: 40px !important; }
}

/* ---- #2b · match the preview→methodology gap to framework→preview ----
   2026-09-04: the framework→preview transition above got matching
   clamp(70px,9vw,150px) padding on both sides of the seam, by design – but
   preview-section's OWN bottom (40px, set in the same pass above) and
   methodology-section's top (94px fixed, untouched since before that pass)
   were never brought into that same symmetry, so this next transition
   landed at a visibly tighter ~134px total instead of the ~2×70-150px the
   first one gets.

   First attempt at this put the same clamp() on methodology-section's own
   padding-top, which was wrong in a way only visible live: that padding
   sits BEFORE .methodology-hero (the controller-image block) even starts,
   since the image is only inside the hero, not the section itself – so it
   showed up as a large flat gap of empty background, then the image,
   instead of reading as generous breathing room the way the framework→
   preview gap does (where the same padding sits inside each section's own
   gradient background, not in front of it).

   Second attempt grew .methodology-hero itself instead, on the theory that
   its absolutely-positioned, object-fit:cover image would fill the space
   with more image rather than blank gap. Right idea, but I only added that
   growth – I never actually removed the padding that caused the first
   problem. preview-section's bottom stayed at the increased clamp() value
   AND methodology-section's own top stayed at its original 94px, both
   still rendering as flat background (preview-section's is a SOLID
   var(--navy-950) fill, confirmed by reading its own rule, not a gradient
   – nothing to visually distinguish it from empty space), while the hero
   grew on top of both. Three sources of space stacked instead of one being
   replaced, which is exactly the large flat gap the next screenshot showed.

   Fixed for real this time: both padding sources pulled back down close to
   their original size, and the hero's own growth is now the ONLY thing
   standing in for the wider gap – actual image instead of flat color. */
@media screen {
  .preview-section { padding-bottom: 40px !important; }
  .methodology-section { padding-top: 24px !important; }
  .methodology-hero { min-height: calc(clamp(190px, 20vw, 260px) + clamp(70px, 9vw, 150px)) !important; }
}

/* ---- #3 · first sentence is the heading, rest is body ---------------- */
@media screen {
  .hero-lead-frame > .hero-lead-lead {
    font-size: clamp(1.5rem, 2.1vw, 2.05rem) !important;
    line-height: 1.22 !important;
    font-weight: 800;
    letter-spacing: -.02em;
    color: #FFFFFF !important;
    margin: 0 0 clamp(12px, 1.2vw, 18px) !important;
    text-shadow: 0 2px 16px rgba(6,8,16,.5);
    max-width: 26ch !important;
  }
  .hero-lead-frame > .hero-lead:not(.hero-lead-lead) {
    font-size: clamp(1.26rem, 1.55vw, 1.4rem) !important;
    line-height: 1.65 !important;
    color: #C9D0E0 !important;
    margin: 0 !important;
  }
}
@media screen and (max-width: 760px) {
  .hero-lead-frame > .hero-lead-lead { font-size: clamp(1.3rem, 5.2vw, 1.62rem) !important; max-width: none !important; }
  .hero-lead-frame > .hero-lead:not(.hero-lead-lead) { font-size: 1rem !important; }
}

/* ---- #4 · search panel sits level with the headline ------------------ */
@media screen and (min-width: 981px) {
  .hero-copy { align-items: start; }
  .hero-right { padding-top: clamp(56px, 7vw, 104px); }
}

/* ---- #7 / #8 · verdict colours follow the category ------------------- */
/* ESRB → purple (mechanics) · multiplayer → pink (safety) · purchases → green */
@media screen {
  .verdict-item:nth-child(1) { --vc: #A259FF; }
  .verdict-item:nth-child(2) { --vc: #FF5FA8; }
  .verdict-item:nth-child(3) { --vc: #4FD37A; }
  .verdict-item {
    border-left: 3px solid var(--vc, #A259FF) !important;
    box-shadow: none !important;
  }
  .verdict-item svg { color: var(--vc, #A259FF) !important; }
  .verdict-item .topline-label { color: var(--vc); }
  /* the nav marker follows the purple; the ESRB block is blue (see below) */
  .report-category.cat-esrb { --cat: #3B82F6; }  /* 2026-09-11: was #A259FF, identical to Parent Engagement */
}

/* ---- #9 · let subheads use the full column before wrapping ----------- */
@media screen and (min-width: 981px) {
  .report-section-head p, .section-heading p { max-width: 78ch; }
}

/* ---- #10 · breathing room under the ESRB topline --------------------- */
@media screen {
  /* 2026-09-06: this rule pair used to zero the summary's margin and add
     26px of padding to the block instead. The padding half never applied –
     it computes to 0, beaten by a later `.report-category` padding rule of
     equal specificity – so the net effect was margin 0 + padding 0, and the
     topline sat 1px off the bottom border while every category card below
     had 25px. Measured, not guessed.
     Fixed at the level that works: the summary keeps its own bottom margin,
     which is the same var(--s5) that puts 25px under a category's "Expand
     for more". Matching the neighbours is the point, so this deliberately
     tracks the shared token rather than restating a number. */
  .esrb-block .category-summary { margin-bottom: var(--s5); }
}

/* ---- #13 · sidebar buttons evenly spaced ----------------------------- */
@media screen and (min-width: 981px) {
  .report-sidebar-actions { gap: 10px; padding-block: 14px 18px; }
}

/* ---- #14 · bottom search copy larger, closer to the button ----------- */
@media screen {
  .cta-card h2 { margin-bottom: 0 !important; }
  .cta-card { gap: 18px; }
  .cta-card .button { margin-left: 4px; }
}

/* ---- #5 · hero carousel: slow cross-fade between the client photos -----
   `.hero-photo` keeps its background; `.hero-photo-layer` sits on top and
   fades in with the next image, so the two layers alternate. Both inherit
   the same mask and scrim, so the treatment never changes between frames. */
@media screen {
  .hero-photo-layer {
    position: absolute;
    inset: 0;
    z-index: 0;
    background-size: cover;
    background-position: center;
    opacity: 0;
    transition: opacity 2.4s ease-in-out;
    pointer-events: none;
  }
  .hero-photo-layer.is-on { opacity: 1; }
  /* the scrim must paint above both layers */
  .hero-photo::after { z-index: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-photo-layer { transition: none; }
}

/* ---- #6 · hero carousel: slow continuous zoom (2026-08-30) --------------
   Client wants the photo wash to move the way Adobe's marquee does: never
   perfectly still. Driven from app.js via Element.animate() on each paint()
   call rather than a CSS class toggle – the class-toggle approach was tried
   first and silently failed to render despite the Animation object
   reporting as active, for reasons that didn't resolve under investigation.
   Element.animate() sidesteps that entirely: transform-origin here, the
   actual keyframes are passed directly from JS. Frozen on phones and for
   reduced-motion from app.js too (checked before the animation is ever
   created, not fought via a CSS override after the fact). */
.hero-photo, .hero-photo-layer { transform-origin: center; will-change: transform; }
/* Rounded top corners on the slideshow itself, Adobe-style – applied to
   the photo layers directly since they're what's actually visible; the
   .hero-photo negative top offset (behind the nav) pushes this corner up
   near the header, so this only shows once you scroll enough for it to
   clear the header's own opaque area. */
.hero-photo, .hero-photo-layer { border-radius: 16px 16px 0 0; }

/* ---- category colors, matched to the "Four areas" framework cards -----
   Previously five colors (A-E) left over from the old five-category
   scheme, and not the same colors as the cards above them on the page
   even for the categories that did match up. research-engine.js has only
   ever produced A-D since the two 2026-08-30/09-02 consolidation passes
   (see its CATEGORY comment) and framework-card.category-b/c/d/e (the
   "Flat category colors" pass, ~line 6872) already established the real,
   client-approved colors for these same four topics – this just points
   the report's own category badges at the identical values instead of a
   separately-invented set. */
@media screen {
  .report-category.cat-a { --cat: #68CA7D; }   /* Gameplay Mechanics – matches framework-card.category-b */
  .report-category.cat-b { --cat: #FF6B6B; }   /* Monetization – matches framework-card.category-c */
  .report-category.cat-c { --cat: #FFD93E; }   /* Safety & Privacy – matches framework-card.category-d */
  .report-category.cat-d { --cat: #A259FF; }   /* Engagement – matches framework-card.category-e */

  /* 2026-09-04: the heading (h3) text itself never actually followed --cat,
     even though the badge, left border, and topline label all already do
     (see .category-badge, ::before, and .category-heading small above).
     Several older, conflicting colors for .category-heading h3 accumulated
     across earlier passes elsewhere in this file (navy, white, a
     hover-only purple, one marked !important) – which one actually
     rendered depended on which of those blocks happened to apply, not on
     which category it was. Forced here at !important, colocated with the
     color values themselves, so a report's heading, badge, border, and
     topline for a given category are always the same color as each other.
     Screen-only on purpose: print already gives the heading a neutral ink
     color instead (see the @media print category block further down) and
     reserves the bright category color for the border/badge/topline only,
     which is the right call for a printed page and shouldn't change. */
  .category-heading h3 { color: var(--cat) !important; }
}

/* =====================================================================
   CENTRED READING COLUMN – 2026-08-13
   ---------------------------------------------------------------------
   The report is three columns at desktop: the game sidebar, the reading
   column, and the sticky "Report Sections" rail. The two outer rails were
   different widths, so the middle column was not centred:

     left  = 300px sidebar + 20px gap = 320px
     right =  28px gap  + 236px rail  = 264px      -> 56px off-centre

   The rail is now an exact mirror of the sidebar – same width, same gap –
   driven by two custom properties so the pair cannot drift apart again.
   Changing either variable keeps both sides equal automatically.

   Side effect, and a welcome one: the rail gains 64px, so the longer
   section names ("Parent Engagement Guide") stop wrapping to two lines.
   ===================================================================== */
:root {
  --report-rail: 300px;      /* width of BOTH outer columns */
  /* 2026-09-01: matched to .framework-grid's own gap ("Four areas" cards
     above, confirmed 14px via computed style) per client request – the
     report's three top-level elements (art, analysis card, TOC rail)
     should read as the same rhythm as the cards above them, not a
     different spacing scale invented separately for this one area. */
  --report-rail-gap: 14px;   /* gutter between each rail and the middle */
}

@media screen and (min-width: 981px) {
  .report-shell {
    grid-template-columns: var(--report-rail) minmax(0, 1fr);
    gap: var(--report-rail-gap);
  }
}

@media screen and (min-width: 1280px) {
  .report-main {
    grid-template-columns: minmax(0, 1fr) var(--report-rail);
    gap: var(--report-rail-gap);
  }
}

/* =====================================================================
   HERO: FLAT 25% SCRIM + COPY ON THE NAV'S LEFT EDGE – 2026-08-13
   ---------------------------------------------------------------------
   1. Scrim. Earlier passes used two stacked gradients – a left-heavy one
      for text contrast and a vertical one for depth – which made the
      photo read at a different darkness across its width. Replaced with
      a single flat 25% black, as asked.

   2. Alignment. Item 08 (2026-08-11) set `.hero-copy { margin-inline:
      22px 14px }` to mirror .header-shell's *inner* padding, so the copy
      landed on the logo. Measured, that put the text at x=46 while the
      nav bar's own left edge is x=24 – a 22px step that reads as a
      misalignment. The copy now starts on the nav bar's outer edge.
      The right margin is untouched: it is an internal gutter against the
      hero photo, not a page edge.
   ===================================================================== */
@media screen {
  .hero-photo::after {
    background: rgba(0, 0, 0, .25) !important;
  }

  .hero-copy {
    margin-inline-start: 0 !important;
  }
}

/* =====================================================================
   HERO LOGO – 2026-08-13
   ---------------------------------------------------------------------
   The client's Video Game Minimap lockup replaces the "The Video Game
   Minimap" eyebrow above the headline – the eyebrow said exactly what
   the logo says, so keeping both printed the name twice. The alt text
   carries the words for screen readers and for a failed image load.

   The supplied artwork is the 3-colour version: a black mark with white
   counters and a purple gradient tile, and a white wordmark carrying a
   heavy black outline. On this dark hero the black reads as a drop
   shadow and the white comes forward, which is exactly the reversed
   treatment the hero needs – so no separate knockout file is required.
   ===================================================================== */
@media screen {
  .hero .hero-logo {
    /* Optical alignment. The artwork's black outline bleeds 6.6% of the
       image width past the white mark on the left. On this dark ground the
       black is invisible, so a flush-left image reads as indented by that
       much against the headline. Pulling the box left by the same 6.6%
       lands the *visible* white edge on the headline's stem. One variable
       drives both, so a width change can't reintroduce the offset. */
    --vgm-w: min(300px, 62%);
    /* how far left the pull may go before the artwork leaves the viewport:
       the page shell's own side margin, which is all the room there is */
    --vgm-room: 24px;
    display: block;
    width: var(--vgm-w);
    height: auto;
    margin: 0 0 clamp(14px, 1.6vw, 22px);
    /* max() of two negatives = the smaller pull, so on narrow screens the
       logo stops at the viewport edge instead of being clipped there */
    margin-left: max(calc(-1 * var(--vgm-w) * 0.066), calc(-1 * var(--vgm-room)));
    /* the artwork's own black outline is its shadow on dark ground; this
       only lifts it off the brighter frames of the photo carousel */
    filter: drop-shadow(0 6px 22px rgba(0, 0, 0, .45));
  }
}

@media screen and (max-width: 980px) {
  .hero .hero-logo { --vgm-w: min(260px, 62%); --vgm-room: 16px; }
}

@media screen and (max-width: 720px) {
  .hero .hero-logo { --vgm-w: min(220px, 70%); }
}

@media screen and (max-width: 600px) {
  .hero .hero-logo { --vgm-room: 11px; }
}

/* the eyebrow is centred on small screens; the logo is a left-aligned
   block, so it must not inherit that centring from any earlier rule */
@media screen and (max-width: 900px) {
  .hero .hero-logo { margin-right: auto; }
}

@media print {
  .hero .hero-logo { width: 2in; filter: none; }
}

/* =====================================================================
   CONFIDENCE + SOURCE TIER – 2026-08-14
   ---------------------------------------------------------------------
   Master Source of Truth, CONFIDENCE SYSTEM and SOURCE HIERARCHY.

   High confidence carries no chip on purpose. It is the expected standard
   for a published finding, so badging it would put a mark on nearly every
   line and the badge would stop meaning anything. Medium and Low are the
   exceptions a parent needs to see, so only those are drawn.

   Tier pills are muted rather than colour-coded by rank – a Tier 4 source
   is not a warning, it is a different kind of evidence, and colouring it
   red would misrepresent the hierarchy as a quality score.
   ===================================================================== */
@media screen {
  .confidence-pill {
    display: inline-block;
    margin-left: 8px;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: .62rem;
    font-weight: 800;
    letter-spacing: .04em;
    text-transform: uppercase;
    vertical-align: 1px;
    white-space: nowrap;
    border: 1px solid transparent;
  }
  .confidence-pill.conf-medium {
    color: #F5C451;
    background: rgba(245, 196, 81, .12);
    border-color: rgba(245, 196, 81, .3);
  }
  .confidence-pill.conf-low {
    color: #FF9F7A;
    background: rgba(255, 159, 122, .12);
    border-color: rgba(255, 159, 122, .32);
  }

  .source-tier {
    display: inline-block;
    margin-left: 8px;
    padding: 2px 8px;
    border-radius: 6px;
    font-size: .6rem;
    font-weight: 800;
    letter-spacing: .05em;
    text-transform: uppercase;
    white-space: nowrap;
    color: rgba(255, 255, 255, .62);
    background: rgba(255, 255, 255, .07);
    border: 1px solid rgba(255, 255, 255, .12);
  }
  /* Tier 1 earns the only accent – it is the one the framework tells a
     reader to trust over the others when sources disagree. */
  .source-tier.tier-1 {
    color: #7FE3A6;
    background: rgba(127, 227, 166, .1);
    border-color: rgba(127, 227, 166, .26);
  }
}

/* light context (print) */
@media print {
  .confidence-pill { border: 1px solid #999; color: #333; background: none; }
  .source-tier { border: 1px solid #bbb; color: #444; background: none; }
}

@media screen and (max-width: 600px) {
  .confidence-pill, .source-tier { margin-left: 6px; font-size: .58rem; padding: 2px 6px; }
}

/* =====================================================================
   REQUEST A FULL RESEARCHED REPORT – 2026-08-17
   ---------------------------------------------------------------------
   Promoted from a single quiet button to a full card.

   The earlier restraint was correct when this was only a vote: the report
   already labelled its own tier twice, and a headed callout on top read as
   an apology for the report existing.

   What changed is what sits behind the button. It is now the entry point
   to a materially better product, so it earns real estate – an accent
   edge, a heading, and four lines naming the questions a researched
   report answers that a database-built one cannot. "Researched report"
   means nothing to a parent; "what quitting mid-game costs" does.

   Still not a klaxon. No filled brand-colour block, no animation. It sits
   inside the report as a considered offer, not an interruption.
   ===================================================================== */
@media screen {
  .research-request {
    display: block;
    margin: 26px 0 6px;
    padding: 22px 24px;
    border-radius: 18px;
    background: linear-gradient(150deg, rgba(162, 89, 255, .10), rgba(162, 89, 255, .03) 60%, transparent);
    border: 1px solid rgba(162, 89, 255, .30);
    border-left: 3px solid var(--purple, #A259FF);
  }

  .research-request h3 {
    display: flex;
    align-items: center;
    gap: 9px;
    margin: 0 0 8px;
    font-size: 1.06rem;
    font-weight: 750;
    letter-spacing: -.01em;
    color: #fff;
  }
  .research-request h3 svg { width: 18px; height: 18px; color: var(--purple, #A259FF); flex: 0 0 auto; }

  .research-request .rr-lede {
    margin: 0 0 12px !important;
    font-size: .93rem;
    line-height: 1.55;
    color: rgba(255, 255, 255, .74) !important;
  }

  .research-request .rr-list {
    margin: 0 0 14px !important;
    padding-left: 0 !important;
    list-style: none;
    display: grid;
    gap: 7px;
  }
  .research-request .rr-list li {
    position: relative;
    padding-left: 22px;
    font-size: .9rem;
    line-height: 1.5;
    color: rgba(255, 255, 255, .82);
  }
  /* A tick rather than a bullet: these are things you get, not things to do. */
  .research-request .rr-list li::before {
    content: "";
    position: absolute;
    left: 3px; top: .55em;
    width: 9px; height: 5px;
    border-left: 2px solid var(--purple, #A259FF);
    border-bottom: 2px solid var(--purple, #A259FF);
    transform: rotate(-45deg);
  }

  /* Lead-in to the three "what to expect" points. */
  .research-request .rr-note-lead {
    margin: 2px 0 10px !important;
    font-size: .9rem;
    line-height: 1.5;
    color: rgba(255, 255, 255, .82);
  }
  /* The three expectations: free / takes a few minutes / saved for everyone.
     Icon + bold lead + plain text, so a parent scans them in a glance. */
  .research-request .rr-expect {
    margin: 0 0 14px !important;
    padding: 12px 14px !important;
    list-style: none;
    display: grid;
    gap: 9px;
    background: rgba(162, 89, 255, .07);
    border: 1px solid rgba(162, 89, 255, .18);
    border-radius: 12px;
  }
  .research-request .rr-expect li {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    font-size: .88rem;
    line-height: 1.5;
    color: rgba(255, 255, 255, .74);
  }
  .research-request .rr-expect li svg {
    width: 16px; height: 16px; flex: 0 0 auto;
    margin-top: 2px;
    color: #5FD08A;
  }
  .research-request .rr-expect strong { color: #fff; font-weight: 700; }

  /* Shown when a full report is free to the reader – sets the expectation
     that it takes time, without implying they owe anything. */
  .research-request .rr-note-quiet {
    margin: 0 0 16px !important;
    font-size: .86rem;
    line-height: 1.5;
    color: rgba(255, 255, 255, .56) !important;
  }

  /* Shown only when PAID_REQUESTS is on. Deliberately plain – a price is a
     fact to state, not something to dress up on a site for parents. */
  .research-request .rr-price {
    margin: 0 0 16px !important;
    padding: 10px 13px;
    border-radius: 10px;
    font-size: .88rem;
    line-height: 1.5;
    color: #FFD79A !important;
    background: rgba(245, 166, 35, .10);
    border: 1px solid rgba(245, 166, 35, .26);
  }

  .research-request .rr-button {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    min-height: 46px;
    padding: 12px 20px;
    border-radius: 12px;
    border: 1px solid transparent;
    background: var(--purple, #A259FF);
    color: #fff;
    font-size: .93rem;
    font-weight: 700;
    font-family: inherit;
    cursor: pointer;
    transition: transform .16s ease, box-shadow .2s ease, background .2s ease;
  }
  .research-request .rr-button:hover {
    background: #B478FF;
    box-shadow: 0 10px 26px rgba(162, 89, 255, .28);
  }
  .research-request .rr-button:active { transform: translateY(1px); }
  .research-request .rr-button:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
  .research-request .rr-button svg { width: 16px; height: 16px; }

  /* Quiet reassurance under the pay button: names the processor and sets the
     one expectation that matters – you leave to Stripe and come right back. */
  .research-request .rr-secure {
    display: flex; align-items: center; gap: 7px;
    margin: 11px 0 0 !important;
    font-size: .8rem; line-height: 1.4;
    color: rgba(255, 255, 255, .5) !important;
  }
  .research-request .rr-secure svg { width: 14px; height: 14px; flex: 0 0 auto; opacity: .85; }

  /* The status line now carries three kinds of message – progress, refusal
     ("you have had today's report"), and success – so the base state is
     neutral and only .rr-note-ok is green. A "come back tomorrow" printed in
     success-green reads as though something worked. */
  .research-request .rr-note {
    display: flex;
    align-items: center;
    gap: 9px;
    margin: 10px 0 0 !important;
    font-size: .9rem;
    font-weight: 600;
    line-height: 1.5;
    color: #D7DEEA !important;
  }
  .research-request .rr-note-ok { color: #9FE3B4 !important; }

  /* ---- research progress panel ------------------------------------------
     Shown while a run is in flight. Every moving part here is driven by real
     stage data from the worker; nothing animates on a timer pretending to be
     progress, so if a run stalls the panel visibly stalls too. */
  .research-progress {
    margin-top: 16px;
    padding: 16px;
    border-radius: 14px;
    background: rgba(10, 16, 32, .55);
    border: 1px solid rgba(162, 89, 255, .28);
  }
  .research-progress .rp-head { display: flex; align-items: center; gap: 14px; margin-bottom: 14px; }
  .research-progress .rp-title { margin: 0 !important; font-size: 1rem; font-weight: 700; color: #F2F5FA; }
  .research-progress .rp-eta { margin: 2px 0 0 !important; font-size: .85rem; color: #9AA6BC; }

  /* Three staggered dots. Cheap, calm, and obviously "working" without
     implying a specific completion time the way a spinning arc does. */
  .research-progress .rp-spinner { display: flex; gap: 5px; flex: 0 0 auto; }
  .research-progress .rp-spinner i {
    width: 9px; height: 9px; border-radius: 50%;
    background: #A259FF;
    animation: rp-pulse 1.25s ease-in-out infinite;
  }
  .research-progress .rp-spinner i:nth-child(2) { animation-delay: .18s; background: #7B6CFF; }
  .research-progress .rp-spinner i:nth-child(3) { animation-delay: .36s; background: #FF6B6B; }
  @keyframes rp-pulse {
    0%, 100% { transform: scale(.65); opacity: .35; }
    50%      { transform: scale(1);   opacity: 1; }
  }

  .research-progress .rp-bar {
    height: 6px; border-radius: 999px; overflow: hidden;
    background: rgba(255, 255, 255, .09);
  }
  .research-progress .rp-fill {
    height: 100%; border-radius: 999px;
    background: linear-gradient(90deg, #A259FF, #FF6B6B);
    /* Long transition because real progress arrives in jumps – easing them
       out stops the bar snapping between stages. */
    transition: width 1.2s cubic-bezier(.4, 0, .2, 1);
    position: relative;
  }
  .research-progress .rp-fill::after {
    content: ''; position: absolute; inset: 0;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.45), transparent);
    animation: rp-sheen 1.9s linear infinite;
  }
  @keyframes rp-sheen { from { transform: translateX(-100%); } to { transform: translateX(100%); } }

  .research-progress .rp-steps { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 9px; }
  .research-progress .rp-step { display: flex; align-items: center; gap: 10px; font-size: .88rem; color: #7C8798; }
  .research-progress .rp-dot {
    width: 15px; height: 15px; border-radius: 50%; flex: 0 0 auto;
    border: 2px solid rgba(255,255,255,.18);
    position: relative; transition: border-color .3s ease, background .3s ease;
  }
  .research-progress .rp-step.is-active { color: #F2F5FA; font-weight: 600; }
  .research-progress .rp-step.is-active .rp-dot {
    border-color: #A259FF;
    animation: rp-ring 1.6s ease-out infinite;
  }
  @keyframes rp-ring {
    0%   { box-shadow: 0 0 0 0 rgba(162, 89, 255, .5); }
    100% { box-shadow: 0 0 0 9px rgba(162, 89, 255, 0); }
  }
  .research-progress .rp-step.is-done { color: #9FE3B4; }
  .research-progress .rp-step.is-done .rp-dot { border-color: #3FBF7F; background: #3FBF7F; }
  .research-progress .rp-step.is-done .rp-dot::after {
    content: ''; position: absolute; left: 3px; top: .5px;
    width: 4px; height: 8px; border: solid #06131F;
    border-width: 0 2px 2px 0; transform: rotate(45deg);
  }

  .research-progress .rp-allowance {
    margin: 14px 0 0 !important;
    padding-top: 12px;
    border-top: 1px solid rgba(255, 255, 255, .09);
    font-size: .84rem;
    font-weight: 700;
    color: #C9B6FF;
  }
  .research-request .rr-allowance { color: #E8ECF3; }
  .research-progress .rp-foot {
    margin: 14px 0 0 !important;
    font-size: .82rem; line-height: 1.55; color: #7C8798;
  }

  /* Motion is decorative here – the stage text and the checkmarks carry the
     same information without it. */
  @media (prefers-reduced-motion: reduce) {
    .research-progress .rp-spinner i,
    .research-progress .rp-fill::after,
    .research-progress .rp-step.is-active .rp-dot { animation: none; }
    .research-progress .rp-fill { transition: none; }
    .research-progress .rp-spinner i { opacity: .9; transform: none; }
  }
  .research-request .rr-button[disabled] {
    opacity: .6;
    cursor: progress;
    box-shadow: none;
  }
  .research-request .rr-note svg { width: 16px; height: 16px; flex: 0 0 auto; }
}

@media screen and (max-width: 600px) {
  .research-request { padding: 18px 16px; margin: 20px 0 4px; }
  .research-request h3 { font-size: 1rem; }
  .research-request .rr-button { width: 100%; justify-content: center; }
}

/* the request is an on-screen action; it means nothing on paper */
/* =====================================================================
   PLATFORM GUIDES – 2026-08-15
   ---------------------------------------------------------------------
   Section 5 now carries two different kinds of claim: what the GAME does,
   and what the CONSOLE can do about it. Those must not look alike. A
   parent who reads "voice chat can be set to Friends only" as a Minecraft
   setting will go hunting for it inside Minecraft, where it does not
   exist – it is an Xbox account setting that applies to every game.

   So each platform gets its own card, keyed by an accent colour drawn
   from the platform record, sitting inside the Safety section but visibly
   nested one level in from the game's own findings.

   Name and topline are always visible because that is the payload – the
   single most useful sentence about that console. The full control list
   sits behind a native <details>: Minecraft matches three platforms, and
   expanding all of them by default would bury the game's own safety
   findings under about 28 platform ones.
   ===================================================================== */
@media screen {
  .platform-block { border-color: rgba(162, 89, 255, .2) !important; }

  .pg-intro {
    margin: 0 0 14px !important;
    font-size: .88rem;
    color: rgba(255, 255, 255, .58) !important;
  }

  .platform-guide {
    position: relative;
    margin-top: 12px;
    padding: 15px 16px 14px 18px;
    border-radius: 14px;
    background: rgba(255, 255, 255, .035);
    border: 1px solid rgba(255, 255, 255, .09);
    border-left: 3px solid var(--pg-accent, #A259FF);
  }

  .pg-head {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 4px 10px;
    margin: 0 0 6px !important;
  }
  .pg-name {
    font-weight: 750;
    font-size: 1rem;
    color: #fff;
    letter-spacing: -.01em;
  }
  .pg-system {
    font-size: .78rem;
    font-weight: 600;
    padding: 2px 8px;
    border-radius: 999px;
    color: var(--pg-accent, #A259FF);
    background: color-mix(in srgb, var(--pg-accent, #A259FF) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--pg-accent, #A259FF) 30%, transparent);
  }

  .pg-topline {
    margin: 0 !important;
    font-size: .92rem;
    line-height: 1.5;
    color: rgba(255, 255, 255, .78) !important;
  }

  /* The disclosure control. Quiet by default – it is an invitation, not a
     call to action, and it repeats on every platform card. */
  .pg-details { margin-top: 11px; }
  .pg-details > summary {
    display: flex;
    align-items: center;
    gap: 7px;
    width: fit-content;
    padding: 5px 10px 5px 11px;
    border-radius: 9px;
    cursor: pointer;
    list-style: none;
    font-size: .83rem;
    font-weight: 650;
    color: rgba(255, 255, 255, .62);
    border: 1px solid rgba(255, 255, 255, .13);
    transition: color .18s ease, border-color .18s ease, background .18s ease;
  }
  .pg-details > summary::-webkit-details-marker { display: none; }
  .pg-details > summary:hover {
    color: #fff;
    border-color: color-mix(in srgb, var(--pg-accent, #A259FF) 55%, transparent);
    background: color-mix(in srgb, var(--pg-accent, #A259FF) 10%, transparent);
  }
  .pg-details > summary:focus-visible {
    outline: 2px solid var(--pg-accent, #A259FF);
    outline-offset: 2px;
  }
  .pg-details > summary svg {
    width: 14px;
    height: 14px;
    transition: transform .2s ease;
  }
  .pg-details[open] > summary svg { transform: rotate(180deg); }

  .pg-body { padding-top: 13px; }
  .pg-setup {
    margin: 0 0 12px !important;
    padding: 10px 12px;
    border-radius: 10px;
    font-size: .86rem;
    line-height: 1.5;
    color: rgba(255, 255, 255, .72) !important;
    background: rgba(255, 255, 255, .04);
    border: 1px solid rgba(255, 255, 255, .07);
  }
  .pg-setup strong { color: #fff; }

  .pg-body .finding-list { font-size: .9rem; }

  .pg-sub { margin-top: 14px; }
  .pg-sub h5 {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 0 0 8px;
    font-size: .84rem;
    color: rgba(255, 255, 255, .82);
  }
  .pg-sub h5 svg { width: 15px; height: 15px; }
  .pg-sub.gap-block {
    padding: 12px 13px;
    border-radius: 11px;
    background: rgba(245, 166, 35, .07);
    border: 1px solid rgba(245, 166, 35, .2);
  }

  /* 2026-09-06: "In The News" – same card language as .platform-guide above
     (subtle fill, thin border, accent-colored left edge) but keyed to --cat
     rather than a hardcoded accent, since this only ever attaches to
     category D and should just pick up that category's own purple rather
     than needing its own color decision. found:false renders as plain text,
     not a card – an absence of news isn't a distinct "thing" the way an
     actual article is, so it doesn't get the same visual weight. */
  .news-empty {
    margin: 0 !important;
    font-size: .88rem;
    line-height: 1.55;
    color: rgba(255, 255, 255, .58) !important;
  }
  .news-article {
    display: block;
    margin-top: 10px;
    padding: 13px 15px;
    border-radius: 12px;
    background: rgba(255, 255, 255, .035);
    border: 1px solid rgba(255, 255, 255, .09);
    border-left: 3px solid var(--cat, #A259FF);
    text-decoration: none;
    transition: background .18s ease, border-color .18s ease;
  }
  .news-article:first-child { margin-top: 0; }
  .news-article:hover, .news-article:focus-visible {
    background: color-mix(in srgb, var(--cat, #A259FF) 8%, transparent);
    border-color: color-mix(in srgb, var(--cat, #A259FF) 40%, transparent);
  }
  .na-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    margin: 0 0 4px !important;
  }
  .na-source {
    font-size: .78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--cat, #A259FF);
  }
  .na-date {
    font-size: .76rem;
    color: rgba(255, 255, 255, .42);
    flex: none;
  }
  .na-title {
    margin: 0 0 4px !important;
    font-size: .95rem;
    font-weight: 650;
    color: #fff;
    line-height: 1.4;
  }
  .na-relevance {
    margin: 0 !important;
    font-size: .86rem;
    line-height: 1.5;
    color: rgba(255, 255, 255, .62) !important;
  }
  .pg-sub.gap-block h5 svg { color: var(--orange); }
  .pg-sub.gap-block ul { font-size: .85rem; }
}

@media screen and (max-width: 600px) {
  .platform-guide { padding: 13px 13px 12px 14px; }
  .pg-details > summary { width: 100%; justify-content: space-between; }
}

/* Paper cannot be expanded, so every disclosure is forced open and the
   control that opens it is removed. A printed report that hid a third of
   Section 5 behind a chevron would be worse than one that never had it. */
@media print {
  .pg-details > summary { display: none !important; }
  .pg-details > .pg-body,
  .pg-details[open] > .pg-body { display: block !important; padding-top: 4px !important; }
  .pg-details { display: block !important; }

  .platform-guide {
    background: #f7f9fc !important;
    border: 1px solid #e3e9f0 !important;
    border-left: 3px solid var(--pg-accent, #A259FF) !important;
    break-inside: avoid;
    margin-top: 10px;
  }
  .pg-name { color: #0A1B3A !important; }
  .pg-intro, .pg-topline, .pg-setup { color: #38424f !important; }
  .pg-setup { background: #fff !important; border: 1px solid #e3e9f0 !important; }
  .pg-setup strong { color: #0A1B3A !important; }
  .pg-sub h5 { color: #0A1B3A !important; }
  .pg-sub.gap-block { background: #FFFBF2 !important; border: 1px solid #F7E6B9 !important; }
}

/* The request card is an on-screen action and means nothing on paper. */
@media print { .research-request { display: none !important; } }

/* The print frame is a table ONLY for paged media – it exists so Chrome will
   repeat the running header and footer and reserve their space, which
   position:fixed cannot do. On screen every part of it is a plain block, so it
   changes nothing there, and the running furniture is not rendered at all. */
@media screen {
  .print-frame,
  .print-frame > tbody,
  .print-frame > tbody > tr,
  .print-frame > tbody > tr > td {
    display: block !important;
    width: auto !important;
    padding: 0 !important;
    border: 0 !important;
    vertical-align: baseline;
  }
  .print-frame-head,
  .print-frame-foot { display: none !important; }
}

/* ======================================================================
   PRINTED REPORT – designed for print, 2026-08-21 (v2)
   ======================================================================
   Goal: an exported PDF that reads as a designed report, not a web page that
   was converted. Rebuilt from the earlier minimal pass, which fixed the
   mechanics (12 clean pages, running furniture, no blank pages) but was too
   bare to look intentional.

   Two principles hold the whole thing together:

   1. STRUCTURE FROM INK THAT ALWAYS PRINTS. Browsers strip background-color
      and box-shadow when "Background graphics" is off in the print dialog, but
      NEVER strip borders or text colour. So every section is defined by a
      border, a coloured heading and spacing – legible with zero backgrounds.
      Tints and bands are layered on top via print-color-adjust as enhancement;
      if they print, better, if not, nothing is lost and nothing goes invisible.
      (No white-text-on-a-fill anywhere, which would vanish without the fill.)

   2. ONE CONTAINER PER SECTION, NEVER NESTED. The previous heavy-card version
      misaligned because a card sat inside a card inside a card, giving four
      left edges. Here each section is a single padded block; its sub-parts are
      separated by spacing and rules, not their own boxes. Design and alignment
      both fall out of that rule.

   Page numbers use @page margin boxes – verified to render in the print
   engine, so "Page X of Y" is real, not left to the browser dialog.
   ====================================================================== */
@media print {
  :root {
    /* Print-safe accent per category. Chosen with distinct lightness so they
       stay distinguishable in grayscale, and dark enough to read as text. */
    --pc-ink:    #14181f;
    --pc-body:   #2b313b;
    --pc-mute:   #667085;
    --pc-line:   #d7dce3;
    --pc-hair:   #e8ebf0;
    --pc-tint:   #f6f8fb;
    --pc-brand:  #6d28d9;
  }

  @page {
    size: A4;
    margin: 16mm 14mm 17mm;
    /* Real page numbers – this engine renders margin-box counters. */
    @bottom-center {
      content: "Page " counter(page) " of " counter(pages);
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
      font-size: 7.5pt;
      color: #8a929c;
    }
  }

  * { -webkit-print-color-adjust: exact !important; print-color-adjust: exact !important; }

  html, body {
    background: #fff !important;
    color: var(--pc-body) !important;
    font-size: 9.5pt;
    line-height: 1.5;
    -webkit-font-smoothing: antialiased;
  }

  /* ---- running header / footer (table thead/tfoot: repeats + reserves) ---- */
  .print-frame { width: 100%; border-collapse: collapse; }
  .print-frame > tbody > tr > td { padding: 0; border: 0; vertical-align: top; }

  .print-frame-head th,
  .print-frame-foot td {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 8mm; text-align: left; font-weight: 400;
  }
  .print-frame-head th {
    padding: 0 0 2mm; margin: 0 0 5mm;
    border-bottom: 0.75pt solid var(--pc-line);
    font-size: 7.2pt; letter-spacing: .1em; text-transform: uppercase;
    color: var(--pc-mute) !important;
  }
  .print-frame-head .prh-game { color: var(--pc-ink) !important; font-weight: 700; }
  .print-frame-foot td {
    padding: 2mm 0 0; margin-top: 5mm;
    border-top: 0.75pt solid var(--pc-line);
    font-size: 7pt; color: var(--pc-mute) !important;
  }
  .print-frame-foot .prf-url { white-space: nowrap; }
  .print-footer { display: none !important; }

  /* ====================================================================
     PAGE ONE – MASTHEAD + TITLE
     ==================================================================== */
  .print-letterhead {
    display: block !important;
    padding: 0 0 3.5mm; margin: 0 0 6mm;
    border: 0 !important; border-bottom: 2pt solid var(--pc-brand) !important;
    border-radius: 0 !important; background: none !important; box-shadow: none !important;
    break-after: avoid;
  }
  /* Top logo bar: the Video Game Minimap product mark on the left, the two
     health-e brand marks paired on the right. */
  .print-letterhead .plh-logos {
    display: flex !important; align-items: center; justify-content: space-between;
    gap: 6mm; margin: 0 0 3.5mm;
  }
  .print-letterhead .plh-minimap { width: 44mm; height: auto; }
  .print-letterhead .plh-brandpair { display: flex !important; align-items: center; gap: 6mm; }
  /* 2026-09-06: the Health-e-Habits mark is now wrapped in an anchor so the
     saved PDF carries a real clickable link to health-e-habits.org. The
     anchor becomes the flex item in place of the img, so it needs to size to
     its own content rather than stretching; the descendant `img` rule below
     still sets the mark's width either way. */
  .print-letterhead .plh-link { display: inline-flex; align-items: center; text-decoration: none !important; border: 0 !important; }
  .print-letterhead .plh-brandpair img { width: 25mm; height: auto; }
  .print-letterhead .plh-row {
    display: flex !important; align-items: flex-end; justify-content: space-between; gap: 6mm;
  }
  .print-letterhead .plh-text { flex: 1; }
  .print-letterhead .plh-text strong {
    display: block; font-size: 13pt; letter-spacing: -.01em; color: var(--pc-ink) !important;
  }
  .print-letterhead .plh-text small {
    display: block; font-size: 7pt; letter-spacing: .13em; text-transform: uppercase; color: var(--pc-mute) !important;
  }
  .print-letterhead .plh-meta { text-align: right; }
  .print-letterhead .plh-meta span { display: block; font-size: 8.5pt; font-weight: 700; color: var(--pc-ink) !important; }
  .print-letterhead .plh-meta small { display: block; font-size: 7pt; color: var(--pc-mute) !important; }

  .report-shell { display: block !important; }
  .report-sidebar {
    display: block !important; border: 0 !important; padding: 0 !important;
    margin: 0 0 6mm !important; background: none !important;
    max-height: none !important; overflow: visible !important;
    break-inside: avoid; break-after: avoid;
  }

  /* Cover thumbnail – small, tidy, rounded, floated so the title reads first. */
  .report-cover {
    float: right; width: 30mm; height: 38mm; min-height: 0 !important;
    margin: 1mm 0 3mm 6mm; border-radius: 3pt; overflow: hidden;
    border: 0.75pt solid var(--pc-line); break-inside: avoid;
  }
  .report-cover strong, .report-cover small, .cover-overlay { display: none !important; }
  .report-cover img { width: 100%; height: 100%; object-fit: cover; }

  .report-game-info h2 {
    font-size: 25pt !important; line-height: 1.08; letter-spacing: -.02em;
    margin: 0 0 4mm !important; color: var(--pc-ink) !important;
  }
  /* Fact strip as a clean bordered card. */
  .report-game-info dl {
    display: block !important; margin: 0;
    border: 0.75pt solid var(--pc-line); border-radius: 4pt; overflow: hidden;
    max-width: 118mm;
  }
  .report-game-info dl > div {
    display: grid !important; grid-template-columns: 26mm 1fr; gap: 0 4mm !important;
    padding: 2.2mm 4mm !important; border: 0 !important;
    border-bottom: 0.5pt solid var(--pc-hair) !important;
    font-size: 8.6pt !important; break-inside: avoid; align-items: baseline;
    background: none;
  }
  .report-game-info dl > div:last-child { border-bottom: 0 !important; }
  .report-game-info dl > div:nth-child(even) { background: var(--pc-tint) !important; }
  .report-game-info dt {
    color: var(--pc-mute) !important; text-transform: uppercase; letter-spacing: .05em;
    font-size: 6.8pt !important; padding-top: .4mm;
  }
  .report-game-info dd { color: var(--pc-ink) !important; margin: 0; text-align: left !important; font-weight: 500; }

  .report-mode {
    display: inline-flex !important; align-items: center; gap: 2mm;
    margin: 4mm 0 0 !important; padding: 1.4mm 3mm !important;
    background: var(--pc-tint) !important; border: 0.75pt solid var(--pc-line) !important;
    border-radius: 999px !important; font-size: 7.6pt !important; color: var(--pc-body) !important;
    clear: both;
  }
  .report-mode svg { display: none !important; }
  .report-mode strong { color: var(--pc-ink) !important; }
  .report-mode strong::after { content: "  ·  "; color: var(--pc-mute); font-weight: 400; }
  .report-mode br { display: none; }

  /* ---- lead / overview ------------------------------------------------ */
  .report-hero h1, .report-kicker { display: none !important; }
  .report-hero { padding: 0 !important; border: 0 !important; margin: 5mm 0 0 !important; }
  .report-hero > p {
    font-size: 11pt !important; line-height: 1.55; color: var(--pc-ink) !important;
    margin: 0 0 5mm !important; max-width: none;
    padding: 0 0 0 4mm; border-left: 2.5pt solid var(--pc-brand);
  }

  /* ---- AT A GLANCE – stat tiles --------------------------------------- */
  .quick-stats {
    display: grid !important; grid-template-columns: repeat(auto-fit, minmax(36mm, 1fr));
    gap: 3mm !important;
    margin: 0 0 6mm !important; padding: 0 !important; border: 0 !important; break-inside: avoid;
  }
  .quick-stat {
    padding: 3mm 3.5mm !important; margin: 0 !important;
    background: var(--pc-tint) !important; border: 0.75pt solid var(--pc-line) !important;
    border-radius: 4pt !important; break-inside: avoid;
  }
  .quick-stat svg { display: none !important; }
  .quick-stat strong {
    display: block; font-size: 6.8pt !important; margin: 0 0 1.2mm !important;
    text-transform: uppercase; letter-spacing: .07em; color: var(--pc-mute) !important; font-weight: 700;
  }
  .quick-stat span { font-size: 8.6pt !important; color: var(--pc-ink) !important; line-height: 1.35; font-weight: 500; }
  /* When only two or three stat cards survive, keep them from stretching oddly. */
  .quick-stats.q-2 { grid-template-columns: repeat(2, 1fr); }
  .quick-stats.q-3 { grid-template-columns: repeat(3, 1fr); }

  /* ---- disclaimer callout --------------------------------------------- */
  .disclaimer-box {
    background: var(--pc-tint) !important; border: 0 !important;
    border-left: 2.5pt solid var(--pc-mute) !important; border-radius: 0 3pt 3pt 0 !important;
    padding: 2.5mm 3.5mm !important; margin: 0 0 6mm !important;
    color: var(--pc-body) !important; font-size: 8pt !important; line-height: 1.5; break-inside: avoid;
  }
  .disclaimer-box svg { display: none !important; }

  /* ====================================================================
     CONTENTS
     ==================================================================== */
  /* 2026-09-05, likely cause of "Contents" printing twice – the screen
     version of .report-nav (~line 6387) is capped at max-height:min(78vh,
     640px) with overflow-y:auto, a scroll container for the on-screen
     sidebar. This print rule resets display/position/width but never
     touched max-height/overflow-y, so the print render may still have
     been fighting a height cap and hidden overflow that mean nothing on
     a medium with no scrolling – a very plausible way to end up with
     content rendered once (clipped) and then again as the engine tried
     to paginate what didn't fit. Explicit reset here removes that
     conflict entirely regardless of whether it was the exact mechanism. */
  .report-nav {
    display: block !important; position: static !important; width: auto !important;
    max-height: none !important; overflow: visible !important;
    margin: 0 0 6mm !important; padding: 3.5mm 4.5mm !important;
    border: 0.75pt solid var(--pc-line) !important; border-radius: 4pt !important;
    background: var(--pc-tint) !important; box-shadow: none !important;
    break-inside: avoid; break-after: avoid;
  }
  .report-nav::before {
    content: "Contents"; display: block;
    font-size: 7.2pt; font-weight: 800; letter-spacing: .14em; text-transform: uppercase;
    color: var(--pc-mute); margin-bottom: 2.5mm;
  }
  .report-nav button {
    display: inline-block !important; background: none !important; border: 0 !important;
    padding: 0 4mm 1mm 0 !important; margin: 0 !important;
    font-size: 8.8pt !important; color: var(--pc-ink) !important; text-align: left !important; width: auto !important;
  }
  .report-nav button svg, .report-nav .rn-dot { display: none !important; }

  /* ====================================================================
     SECTIONS  (each a defined area)
     ==================================================================== */
  .report-section {
    padding: 0 !important; margin: 0 0 7mm !important; border: 0 !important;
    break-inside: auto;
  }
  .report-section-head {
    margin: 0 0 3.5mm !important; padding: 0 0 2mm !important;
    border-bottom: 1pt solid var(--pc-ink) !important; break-after: avoid;
    display: block;
  }
  .report-section-head h2 {
    font-size: 14pt !important; letter-spacing: -.01em; color: var(--pc-ink) !important; margin: 0 !important;
  }
  .report-section-head p { font-size: 8.4pt !important; color: var(--pc-mute) !important; margin: 1mm 0 0 !important; }

  /* ---- summary insight cards (Key Findings, Positives, Concerns, ...) --- */
  .summary-grid { display: block !important; }
  .insight-card {
    background: none !important; border: 0 !important; border-radius: 0 !important;
    padding: 0 0 0 4mm !important; margin: 0 0 4mm !important;
    border-left: 2.5pt solid var(--pc-line) !important; box-shadow: none !important;
    break-inside: avoid; orphans: 3; widows: 3;
  }
  .insight-card.concern-card { border-left-color: #b3541e !important; }
  .insight-card.discussion-card { border-left-color: #2563eb !important; }
  .summary-grid > .insight-card:first-child > h3 { display: none !important; }
  .insight-card h3 {
    font-size: 9pt !important; color: var(--pc-ink) !important; margin: 0 0 1.5mm !important;
    letter-spacing: .02em; break-after: avoid;
  }
  .insight-card ul { margin: 0 !important; }
  .insight-card li {
    display: list-item !important; list-style: disc outside !important;
    margin: 0 0 1.4mm !important; break-inside: avoid; padding-left: 0;
  }
  .insight-card li::before { display: none !important; }
  .insight-card ul { padding-left: 4.5mm !important; list-style: disc outside !important; }

  /* ====================================================================
     CATEGORY BLOCKS  (self-contained, coloured heading, one container)
     ==================================================================== */
  .report-category.cat-a { --pc: #2563eb; }
  .report-category.cat-b { --pc: #0e7c66; }
  .report-category.cat-c { --pc: #b3541e; }
  .report-category.cat-d { --pc: #6d28d9; }
  .report-category.cat-e { --pc: #9d174d; }
  .report-category { --pc: #475467; }

  .report-category {
    break-inside: auto !important;
    border: 0.75pt solid var(--pc-line) !important;
    border-top: 2.5pt solid var(--pc) !important;
    border-radius: 4pt !important;
    background: none !important; box-shadow: none !important;
    padding: 4mm 5mm 4.5mm !important; margin: 0 0 5mm !important;
  }
  .report-category::before { display: none !important; }

  .category-heading {
    display: grid !important; grid-template-columns: 8mm 1fr; gap: 0 3mm !important;
    align-items: center; padding: 0 !important; margin: 0 0 3mm !important;
    background: none !important; border: 0 !important;
    border-bottom: 0.75pt solid var(--pc-hair) !important; padding-bottom: 2.5mm !important;
    -webkit-appearance: none; appearance: none; break-after: avoid;
  }
  .category-badge {
    width: 8mm !important; height: 8mm !important; place-items: center !important;
    display: grid !important; background: none !important;
    border: 1.25pt solid var(--pc) !important; border-radius: 3pt !important;
    box-shadow: none !important; color: var(--pc) !important;
    font-size: 10pt !important; font-weight: 800;
  }
  .ch-text small, .category-heading small {
    font-size: 6.6pt !important; letter-spacing: .14em; color: var(--pc) !important;
    font-weight: 700; margin-bottom: .5mm !important; text-transform: uppercase;
  }
  .ch-text h3, .category-heading h3 {
    font-size: 12.5pt !important; color: var(--pc-ink) !important; margin: 0 !important; letter-spacing: -.01em;
  }
  .category-toggle { display: none !important; }

  .category-summary {
    margin: 0 0 3.5mm !important; padding: 2.5mm 3mm !important;
    color: var(--pc-ink) !important; font-size: 9.4pt !important; max-width: none !important;
    background: var(--pc-tint) !important; border-radius: 3pt !important;
    border-left: 2pt solid var(--pc) !important; break-inside: avoid;
  }
  .topline-label { color: var(--pc) !important; font-weight: 700; }

  .category-details { margin: 0 !important; display: block !important; gap: 0 !important; }

  .detail-block {
    background: none !important; border: 0 !important; border-radius: 0 !important;
    padding: 3mm 0 0 !important; margin: 3mm 0 0 !important;
    border-top: 0.5pt solid var(--pc-hair) !important;
    break-inside: auto; orphans: 3; widows: 3;
  }
  .detail-block:first-child { border-top: 0 !important; padding-top: 0 !important; margin-top: 0 !important; }
  .detail-block h4 {
    font-size: 7.2pt !important; letter-spacing: .13em; text-transform: uppercase;
    color: var(--pc-mute) !important; margin: 0 0 1.8mm !important; break-after: avoid;
    display: flex; align-items: center; gap: 1.5mm;
  }
  .detail-block h4 svg { display: none !important; }
  .detail-block p, .detail-block li { color: var(--pc-body) !important; font-size: 9.2pt !important; }
  .detail-block ul { padding-left: 4.5mm !important; gap: 0 !important; display: block !important; }
  .detail-block li {
    display: list-item !important; list-style: disc outside !important;
    margin: 0 0 1.4mm !important; break-inside: avoid;
  }
  .detail-block li::before { display: none !important; }

  .finding-group { margin: 0 0 2.8mm !important; break-inside: auto; }
  .finding-group-label {
    font-size: 7pt !important; letter-spacing: .1em; color: var(--pc) !important;
    font-weight: 700; margin: 0 0 1mm !important; break-after: avoid; text-transform: uppercase;
  }
  .finding-label { color: var(--pc-ink) !important; font-weight: 700; }
  .gap-block h4 { color: #a15c00 !important; }
  .gap-block { border-top-color: #f0d9a8 !important; }

  /* ====================================================================
     SOURCES – numbered reference list
     ==================================================================== */
  .source-list { display: block !important; counter-reset: src; }
  .source-row {
    display: grid !important; grid-template-columns: 6mm 1fr; gap: 0 1mm;
    counter-increment: src; background: none !important; border: 0 !important;
    border-bottom: 0.5pt solid var(--pc-hair) !important; border-radius: 0 !important;
    padding: 2mm 0 !important; margin: 0 !important; break-inside: avoid; font-size: 8.2pt !important;
  }
  .source-row:last-child { border-bottom: 0 !important; }
  .source-row::before {
    content: counter(src) "."; color: var(--pc-mute); font-size: 7.8pt; font-weight: 700; padding-top: .2mm;
  }
  .source-row > div, .source-row > a { grid-column: 2; }
  .source-row strong { font-size: 8.6pt !important; color: var(--pc-ink) !important; display: inline !important; }
  .source-row span { display: inline !important; color: var(--pc-body) !important; }
  .source-row strong::after { content: " – "; color: var(--pc-mute); }
  .source-row small { display: block !important; color: var(--pc-mute) !important; font-size: 7pt !important; margin-top: .5mm !important; }
  .source-row a {
    display: block !important; width: auto !important; height: auto !important;
    background: none !important; border: 0 !important; color: #3459a8 !important;
    font-size: 7pt !important; margin-top: .4mm; text-align: left !important; place-items: start !important;
  }
  .source-row a svg { display: none !important; }
  .source-row a::after { content: attr(href); display: block; color: #3459a8 !important; word-break: break-all; font-size: 7pt; }
  .source-tier-pill { display: none !important; }

  /* ---- methodology grid ------------------------------------------------ */

  /* ---- platform guidance ----------------------------------------------- */
  .platform-guide {
    background: none !important; border: 0 !important; border-radius: 0 !important;
    border-top: 0.5pt solid var(--pc-hair) !important; padding: 3mm 0 0 !important;
    margin: 3mm 0 0 !important; break-inside: auto;
  }
  .platform-guide details, .platform-guide summary { display: block !important; }
  .platform-guide summary::-webkit-details-marker { display: none; }
  .platform-guide summary, .platform-guide .pg-head, .platform-guide h4, .platform-guide h5 { break-after: avoid; }
  .pg-sub {
    background: none !important; border: 0 !important; border-left: 1pt solid var(--pc-line) !important;
    border-radius: 0 !important; padding: 0 0 0 3mm !important; margin: 0 0 2mm !important;
  }
  .pg-sub.gap-block { background: none !important; border: 0 !important; border-left: 1pt solid #e0cf9a !important; }
  .pg-sub.gap-block h5 { color: #a15c00 !important; }

  /* ---- page-break rhythm: flow, but keep headings with their content --- */
  .report-section { break-before: auto; }
  .report-category { break-before: auto; }
  .report-section-head, .category-heading { break-after: avoid; }
  h1, h2, h3, h4, h5 { break-after: avoid; page-break-after: avoid; }
  p, li { orphans: 3; widows: 3; }
  img { break-inside: avoid; }

  /* ---- never print ----------------------------------------------------- */
  .research-request, .research-progress, .report-actions, .report-actions-band,
  .report-sidebar-actions, .report-info-btn, .cover-play, .report-trailer,
  .trailer-lightbox, .quick-stat-link .qs-hint { display: none !important; }

  a { text-decoration: none; color: inherit; }
}

/* Screen: keep the print frame invisible/neutral (unchanged behaviour). */
@media screen {
  .print-frame, .print-frame > tbody, .print-frame > tbody > tr, .print-frame > tbody > tr > td {
    display: block; width: auto; padding: 0; border: 0; vertical-align: baseline;
  }
}

/* ======================================================================
   CLIENT UPDATE PASS – 2026-08-31 (2)
   Center hero-copy within the *visible* photo area, not .hero's full box.
   ====================================================================== */
/* .hero centers hero-copy (align-items:center) within its own full
   padding box – but framework-section's -56px margin-top overlap covers
   the bottom 56px of that box, so what's actually visible below the nav
   is 56px shorter than what the grid centers against. That leftover 56px
   pushes the centered text down, away from true visual center between
   the nav and where the photo actually ends. Shifting up by half of it
   (28px) corrects for exactly that gap; tied to framework's overlap
   amount, not to any breakpoint-specific padding value, so it holds
   regardless of screen size. */
.hero-copy { transform: translateY(-28px); }
/* ---- Hero slideshow height matches Adobe's own marquee: each of their
   sections reframes to the full browser window height, so the next
   section stays completely offscreen until the user actually scrolls –
   not just "mostly filled with a sliver of the next section already
   creeping into view." The earlier version of this rule capped at 860px
   so the hero wouldn't stretch too tall on large monitors – but that
   cap is exactly what let "Four areas" peek in at rest on anything
   bigger than a laptop screen. Removed: no ceiling now, so the hero
   always extends to the actual bottom of the viewport, on any size
   screen. The 2026-07-31 restructure pass (~line 3165) intentionally
   zeroed the hero's min-height for the stacked single-column layout –
   this doesn't revert that, it adds height back on top of it, appended
   last so it wins the tie against that equally-specific !important
   rule. 302px = the promo bar's reserved height (--promo-bar-height,
   210px) plus the header's own rendered height (92px), so "100vh" lands
   at the actual page break rather than 100vh below a promo bar that's
   already covered. The 480px floor only protects against the hero
   getting uncomfortably squeezed on very short windows – since it's a
   floor, not a ceiling, it can only make the hero taller than the
   available space (meaning "Four areas" stays offscreen a little
   longer, never sooner), so it can't reintroduce the peek. The +56px
   cancels out framework-section's own -56px margin-top overlap (its
   rounded-corner reveal, see framework-section's own rule) – without
   it, that overlap alone still let a sliver of framework's rounded top
   corner show above the fold even with the hero otherwise filling the
   viewport exactly. Scoped to min-width:981px – the white promo bar
   itself doesn't show past that breakpoint (see the mobile-only
   search/nav treatment below 980px), and mobile's shorter,
   content-driven hero height was a deliberate part of the stacked
   layout, not something this should override. */
@media screen and (min-width: 981px) {
  .hero {
    /* 2026-09-02 (retuned): was +140px (added the previous round for
       "more breathing room"), which turned out to directly cause a
       separate, more specific problem: framework was landing roughly
       317px of scroll distance after the hero text had already fully
       scrolled behind the nav, when it should have landed at or before
       that point. Measured precisely (hero-copy's own bottom edge
       reaching the header's bottom edge, vs framework.top reaching 0)
       and iterated on both this offset and hero's own bottom padding
       (see the padding-block rule above) together, since dropping this
       formula's value alone bottoms out at hero's natural content height
       – below that, min-height can't force the element any shorter.
       2026-09-02 (reverted): that -260px offset directly broke this
       rule's own, more fundamental purpose – reported and reproduced
       directly at a shorter viewport height (1512x750): with hero
       shrunk below "exactly fills the viewport," framework's navy
       section was visibly peeking in at the very bottom of the screen
       with zero scrolling, promo bar still fully showing. The two
       requirements are mathematically incompatible below 100vh of hero
       height: "framework lands within less than one viewport-height of
       scroll" and "framework never peeks in before any scrolling" can't
       both hold, since scrolling exactly 100vh is what it takes to go
       from "hero fills the screen exactly" to "framework's top edge
       reaches the screen's top edge" – there's no shorter path that
       doesn't leave a gap for framework to show through at rest. No-peek
       is the more fundamental, visually-obvious requirement (a persistent
       artifact on every page load, not just a timing preference), so
       reverted the offset to 0 and the floor back to 480. The
       hero-padding reduction from that same round is kept, since it
       doesn't affect this guarantee – min-height was already the
       binding constraint at that padding value regardless. */
    min-height: max(480px, calc(100vh - var(--promo-bar-height, 210px) - 92px + 56px)) !important;
  }
}

/* ======================================================================
   CLIENT UPDATE PASS – 2026-08-31 (3)
   "Four areas" redesigned after adobe.com's "Everything you need to make
   anything" section: centered heading, and cards that slide in together
   and expand into an accordion on hover instead of a static grid.
   ====================================================================== */

/* ---- Heading: centered, larger, a touch bolder ------------------------
   .section-heading.centered already existed in the stylesheet but was
   never actually used anywhere in the markup – this is the first section
   to use it. Sized up from the shared clamp(2.4rem,5vw,4rem)/700 default
   read as too close to the left-aligned hero h1 next to it; a centered
   headline needs a bit more presence to hold the middle of the page. */
.framework-section .section-heading.centered {
  max-width: 1100px;
  margin: -13px auto 69px;
}
.framework-section .section-heading.centered h2 {
  font-size: clamp(2.1rem, 3.6vw, 3.1rem);
  font-weight: 400;
  letter-spacing: -.01em;
  color: #fff;
}
.framework-section .section-heading.centered p {
  max-width: 1000px;
  margin: 20px auto 0;
  font-size: clamp(1.05rem, 1.3vw, 1.2rem);
  line-height: 1.6;
}

/* ---- Grid: flex row of tall panels, not a fixed 4-column grid ---------
   Needed so the hover-expand accordion below can actually redistribute
   width between siblings – a CSS grid's tracks don't respond to a single
   item's hover the way flex-grow does. */
.framework-grid {
  display: flex;
  gap: 14px;
  height: 560px;
  align-items: stretch;
  /* flex-grow transitions force a full layout recalculation every frame
     (unlike transform/opacity, which the compositor can handle alone) –
     contain:layout tells the browser those recalculations stop at this
     element's own boundary instead of potentially rippling into layout
     work for the rest of the page below it, which is the more likely
     cause of an uneven "bump" on some cards and not others (browsers
     don't guarantee identical frame timing card to card once layout
     work is involved) than anything about the cards themselves. */
  contain: layout;
  /* Breaks out of the same 22px/14px content padding the heading above
     it uses, so the row's outer edges land flush with the persistent
     nav's own left/right edges (header-shell) instead of sitting inset
     to match body copy width – the client specifically wants these
     cards wider than that established column. */
  margin: 0 -14px 0 -22px;
}

/* ---- Cards: colored frame around an inset photo-placeholder box, title
   above it, description collapsing in below it on hover -----------------
   Matches the reference: the "photo" is a full-width black box inset
   from the card's own top/bottom (title above, description below) but
   flush with the card's left/right edges – not a full-bleed background
   with text overlaid on top of it, which is what the previous pass did. */
/* The 2026-08-11 "honest column flow" pass (~line 3673) already makes
   .framework-card a flex column with children in normal static flow –
   exactly what this design wants, so it's left alone rather than fought
   with !important overrides the way the previous (overlay-based) version
   of this section needed to. */
.framework-card,
.framework-card.category-b, .framework-card.category-c,
.framework-card.category-d, .framework-card.category-e {
  position: relative;
  flex: 1 1 0%;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  padding: 20px 22px 22px !important;
  border-radius: 28px;
  border: 0 !important;
  box-shadow: var(--shadow-md);
  cursor: pointer;
  align-items: stretch !important;
  gap: 0 !important;
  /* 2026-09-02: paint -> layout paint style. Hover doesn't just resize
     this card (flex-grow) – it also grows .framework-card-desc's own
     max-height inside it, which in turn compresses .framework-card-media
     (flex:1 1 auto, so it absorbs whatever height the description no
     longer leaves it) and forces that image to be re-cropped by
     object-fit every frame. Two layout-affecting transitions running at
     once, one of them cascading into a third property changing as a
     side effect. contain:layout isolates all of that recalculation to
     this card's own subtree instead of letting it potentially ripple
     into the rest of the page's layout – doesn't reduce the actual work
     happening inside the card, but keeps it from being more expensive
     than it needs to be. */
  contain: layout paint style;
  will-change: flex-grow;
  /* 2026-09-02: .55s -> .75s, per request for an even smoother in/out
     feel. Kept in sync with .framework-card-desc's own max-height/
     margin-top duration below (same reasoning as when these were first
     synchronized: mismatched durations between the card's width and the
     description's height read as janky even when neither individually
     drops a frame). */
  /* 2026-09-02: box-shadow added – was the one property among this set
     (flex-grow, background-color, color) with no transition of its own,
     so it snapped instantly the moment :hover/:focus-within engaged
     rather than fading in step with everything else. Declared here on
     the base rule since .framework-card:hover itself only sets the
     shadow's value, not its own transition – a transition has to live
     on the rule that's active both before and after the state change to
     apply symmetrically in both directions. */
  transition: flex-grow .75s cubic-bezier(.16,1,.3,1), box-shadow .4s ease;
}
.framework-card:hover,
.framework-card:focus-within { box-shadow: 0 30px 70px rgba(5,24,52,.28); }
.framework-card::after, .framework-card::before { content: none !important; } /* old corner-circle + scrim – nothing sits on top of the photo now, so neither is needed */
/* Cards slide up and fade into place one by one as the section is
   scrolled into view – driven directly by scroll position (see
   updateScrollEffects in app.js, which sets --card-reveal every scroll
   tick, the same pattern already used for --hero-fade and the parallax
   items) rather than a fixed-duration transition triggered once by
   IntersectionObserver. A timed transition can finish before the user
   has actually scrolled the cards into view; tying opacity/position
   directly to scroll position instead means it's physically impossible
   to scroll past this section without seeing it happen, at any speed.
   Deliberately no CSS transition on these two properties – they're
   recalculated on every scroll tick already, so scrolling itself is
   what makes the motion smooth, the same way native scroll-driven
   parallax works elsewhere on this page. The --card-reveal,1 fallback
   means cards render fully visible if this rule somehow doesn't apply
   (e.g. prefers-reduced-motion, which skips setting the property at
   all) instead of silently staying invisible.
   2026-09-02: direction switched from horizontal (36px sideways) to
   vertical (56px up), and the distance increased – per storyboard,
   "Four areas" should read as physically rising to cover the hero
   beneath it, not sliding in sideways; matches the section's own
   upward-cast shadow and rising motion as you scroll. */
/* 2026-09-02 (rebuilt again): clip-path was a misread of the storyboard
   – the staggered heights shown there were cards still physically
   translating up from below, with whatever portion hadn't yet crossed
   the frame's bottom edge simply not visible yet (ordinary geometry of
   an object sliding up into a bounded view), not an actual growing-
   height reveal effect. Correction, per explicit clarification: cards
   translate the whole way as constant-sized units (this part), and
   separately ramp from 0% to 100% opacity specifically during the final
   approach to landing – not evenly across the whole motion, so they're
   invisible while still far away and only become visible as they get
   close. The opacity ramp below remaps the back half of --card-reveal's
   range (0.5-1) to 0-1, clamped with max()/min() since calc() alone
   can't clamp; translate still uses the raw value across the full 0-1
   range, so the two are deliberately on different curves sharing one
   underlying progress variable rather than needing two separately
   computed properties from app.js. */
/* 2026-09-02 (distance increased): 56px wasn't enough to read as a
   visible slide – translate runs linearly across the full 0-1 range
   while opacity only starts ramping at the halfway point (0.5), so the
   portion of the translate that's actually visible (overlapping the
   opacity ramp) is always just half the total distance. At 56px that
   was 28px, and measured directly mid-scroll: by the time a card had
   enough --card-reveal to be even partially visible, its translate had
   already fallen to a barely-perceptible ~12px residual – reported as
   "not coming in from the bottom." 220px makes the visible half ~110px,
   large enough to read clearly as sliding up while fading in. */
.framework-card {
  opacity: max(0, min(1, calc((var(--card-reveal, 1) - 0.5) / 0.5)));
  translate: 0 calc((1 - var(--card-reveal, 1)) * 220px);
}
/* Section heading slides up alongside the cards (same --heading-reveal
   scroll handler, no per-card stagger, so it settles slightly ahead of
   them, matching reading order) but does NOT fade – per explicit
   request, only the cards should go through the opacity ramp; the
   heading text stays at full, constant opacity throughout, translate
   only. */
.framework-section .section-heading {
  translate: 0 calc((1 - var(--heading-reveal, 1)) * 56px);
}
.framework-grid:hover .framework-card,
.framework-grid:focus-within .framework-card { flex-grow: .85; }
.framework-card:hover,
.framework-card:focus-within {
  flex-grow: 1.6 !important;
}

/* Flat category colors (2026-08-31), matching the client's reference
   image and replacing the earlier per-category gradients. Title,
   description and button all share one dark-tinted-same-hue ink per
   category (a darker shade of that card's own color, not a plain
   near-black/white split) – the client wants the peeking description
   at rest to read in the same tone as the heading above it. */
/* 2026-09-02: --card-heading-ink and --card-ink-soft registered via
   @property so they interpolate smoothly as colors (CSS custom
   properties don't transition by default – without this, each
   property's value would snap instantly even with `transition: color`
   declared on the elements reading it, undercutting the "buttery
   smooth" hover color change this whole change is for). Browsers
   without @property support simply keep the old instant-snap behavior,
   so this is a safe progressive enhancement rather than something that
   can break anything. */
@property --card-heading-ink {
  syntax: '<color>';
  inherits: true;
  initial-value: #F4F6FA;
}
@property --card-ink-soft {
  syntax: '<color>';
  inherits: true;
  initial-value: rgba(244,246,250,.8);
}
@property --card-chip {
  syntax: '<color>';
  inherits: true;
  initial-value: rgba(244,246,250,.12);
}
/* Neutral resting color shared by all four cards – a dark, desaturated
   slate-blue rather than a plain gray, chosen to sit comfortably next to
   the section's own navy background (var(--navy-850) / var(--navy-900))
   without either blending into it or clashing. Each category's real
   color (see the :hover/:focus-within rules below) now only applies on
   hover, matching the reference: cards read as a neutral, cohesive row
   at rest and only reveal their individual color as you engage with one
   – rather than all four competing for attention before you've even
   looked at them.
   2026-09-02 (bug fixed): transition is a shorthand property – when this
   selector (more specific than the base .framework-card rule above, so
   it wins outright) declares its own transition, that completely
   replaces the base rule's transition:flex-grow for this element rather
   than adding to it. CSS doesn't merge separate transition declarations
   across different selectors matching the same element; whichever rule
   wins for the `transition` property wins for the whole value. This
   meant flex-grow had no real transition duration of its own since the
   color scheme was added – confirmed directly: getComputedStyle showed
   transition-duration as "0.6s, 0.6s" (the color transition's two
   durations) with no third value for flex-grow at all, and a card's
   width was landing on its final value near-instantly instead of
   easing over time. flex-grow now included directly in this rule (the
   one that actually applies), matching the base rule's own duration so
   both stay in sync. */
.framework-card.category-b, .framework-card.category-c,
.framework-card.category-d, .framework-card.category-e {
  background: #2A3850 !important;
  --card-heading-ink: #F4F6FA;
  --card-ink: var(--card-heading-ink); --card-ink-soft: rgba(244,246,250,.8); --card-chip: rgba(244,246,250,.12);
  transition: flex-grow .75s cubic-bezier(.16,1,.3,1), background-color .6s ease, color .6s ease, box-shadow .4s ease;
}
.framework-card.category-b:hover, .framework-card.category-b:focus-within {
  background: #68CA7D !important;
  --card-heading-ink: #153D1E;
  --card-ink: var(--card-heading-ink); --card-ink-soft: rgba(21,61,30,.8); --card-chip: rgba(21,61,30,.12);
}
.framework-card.category-c:hover, .framework-card.category-c:focus-within {
  background: #FF6B6B !important;
  --card-heading-ink: #620000;
  --card-ink: var(--card-heading-ink); --card-ink-soft: rgba(98,0,0,.8); --card-chip: rgba(98,0,0,.12);
}
.framework-card.category-d:hover, .framework-card.category-d:focus-within {
  background: #FFD93E !important;
  --card-heading-ink: #564500;
  --card-ink: var(--card-heading-ink); --card-ink-soft: rgba(86,69,0,.8); --card-chip: rgba(86,69,0,.12);
}
.framework-card.category-e:hover, .framework-card.category-e:focus-within {
  background: #A259FF !important;
  --card-heading-ink: #29005D;
  --card-ink: var(--card-heading-ink); --card-ink-soft: rgba(41,0,93,.8); --card-chip: rgba(41,0,93,.12);
}


.framework-card h3,
.framework-card.category-b h3, .framework-card.category-c h3,
.framework-card.category-d h3, .framework-card.category-e h3 {
  transform: none !important;
  flex: none;
  margin: 0 0 14px;
  max-width: none;
  /* Sized so "Monetization & Psychological Hooks" – the longest of the
     four titles – fits on one line from ~1280px up; same size applies
     to all four regardless of their own length, per the client's
     request to match sizes across cards rather than size each title to
     its own text. Below ~1280px it still wraps to two lines rather than
     shrinking further – past that point the card is narrow enough that
     a one-line fit would need genuinely illegible text. */
  font-size: clamp(0.85rem, calc(1.5vw - .35rem), 1.1rem);
  line-height: 1.22;
  letter-spacing: -.02em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--card-heading-ink) !important;
  /* 2026-09-02: transition declared here, not just on .framework-card –
     color is h3's own property, so it needs its own transition to
     animate; the parent's transition (background-color) has no bearing
     on how this element's color property behaves. Without this, the
     underlying --card-heading-ink variable was interpolating smoothly
     (thanks to @property above) but nothing was actually watching that
     change animate, so the text still snapped instantly even as the
     background visibly faded – confirmed directly: background sampled
     as a smooth gradient of intermediate values through the transition,
     while h3's color was already at its final value within the first
     ~50ms. */
  transition: color .6s ease;
}
/* 2026-09-03: white-space:normal now driven by a JS-added class
   (.title-revealed – see initFrameworkCardTitleTiming in app.js) rather
   than :hover directly. An earlier attempt used transition-delay on
   white-space itself to hold the switch off – that had no effect
   whatsoever (confirmed directly: identical wrap/unwrap regardless of
   what delay was set), because white-space isn't a property CSS
   transitions can animate, even as a discrete flip; the browser was
   just applying the new value immediately and silently ignoring the
   delay/duration entirely.
   Root cause (measured via requestAnimationFrame polling at sub-frame
   resolution): swapping directly from one hovered card to an adjacent
   one starts the newly-hovered card from its narrower "other" share
   (flex-grow:.85, ~298px at a 1512px viewport) rather than true rest
   (flex-grow:1, ~354px) – .85-share is too narrow for "Monetization &
   Psychological Hooks" to fit on one line. Switching straight to
   white-space:normal at that instant wrapped the title to two lines
   immediately, then it snapped back to one line ~50ms later once the
   card had widened past the threshold – that snap is the reported
   bump, reachable only via this narrower starting point (a fresh hover
   from a fully-idle grid starts from the wider true-rest share instead,
   which is why it was never reported for hovers "from below"). JS now
   holds the class off for the first 180ms of the transition – well
   past the ~50ms crossover measured above – so by the time the full
   title is revealed the card is already wide enough for it to fit on
   one line without ever wrapping. The class is removed immediately (no
   delay) on hover-out, so shrinking back never risks the same wrap in
   reverse. */
.framework-card.title-revealed h3 {
  white-space: normal;
  overflow: visible;
}

/* The inset "photo" – full width (cancels the card's own side padding
   with a negative margin), inset from top/bottom only, own rounded
   corners since it isn't flush with the card's corners. */
.framework-card-media {
  /* 2026-09-02: static -> relative – the image inside is now absolutely
     positioned (see below), which needs this as its containing block. */
  position: relative !important;
  flex: 1 1 auto;
  min-height: 0;
  margin: 0 -22px;
  border-radius: 20px;
  background: #000;
  overflow: hidden;
  /* 2026-09-02: this is where the compound resize actually lands – the
     card's own flex-grow changes its width, the sibling description's
     max-height changes how much height this has left (flex:1 1 auto
     absorbing the difference), and object-fit re-crops the image inside
     to match both every frame. contain:paint isolates that repainting
     to just this box. */
  contain: paint;
}
.framework-card-media img {
  /* 2026-09-02 (rebuilt): was width:100%; height:100%; object-fit:cover.
     Reported as images "jumping" on hover – confirmed directly from a
     recording: as the card resizes (width +47% via flex-grow, height
     -9% as the sibling description's max-height compresses this area),
     object-fit:cover recalculates its crop every frame to match the
     container's continuously-changing aspect ratio, so a visibly
     different portion of the same photo becomes the "visible" one as
     the transition plays – the actual content appears to pan/shift, not
     just resize. The purple (last) card reads as fine mainly because its
     photo (two centered faces, no important detail near the edges)
     happens to tolerate a shifting crop without anything specific
     visibly relocating – the underlying mechanism is identical on all
     four.
     Fix: give the image itself a fixed size, set via CSS custom
     properties from JS (see initFrameworkCardMedia in app.js) sized to
     safely cover the largest the container gets in both dimensions
     across rest and hovered states, generous margin included. With its
     own size constant, object-fit's crop amount is now also constant –
     nothing about the image's own rendering needs to be recalculated as
     the card animates. Only the visible *window* into it changes, via
     the parent's own overflow:hidden, which is just a clip rectangle
     adjustment rather than a re-crop of the image's content. Absolutely
     positioned and centered (transform: translate(-50%,-50%)) so it
     stays centered within that clip regardless of which edge the
     container is currently animating from. */
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--card-media-img-w, 100%);
  height: var(--card-media-img-h, 100%);
  max-width: none;
  translate: -50% -50%;
  object-fit: cover;
  display: block;
}
.framework-card-media svg {
  width: 40px; height: 40px;
  color: rgba(255,255,255,.4);
  stroke-width: 1.4;
}
.framework-card-placeholder-label {
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: rgba(255,255,255,.4);
}
.framework-card:focus-visible {
  outline: 3px solid rgba(255,255,255,.85);
  outline-offset: 3px;
}

/* Description + link: a short peek at rest (matching the reference –
   the card shows the start of the copy, cut off, as a hint there's more)
   that opens wide to the full text once the card is hovered/focused
   wide enough to read it – the photo box's flex:1 yields height to this
   sibling automatically as it grows. */
.framework-card-desc {
  position: static !important;
  flex: none;
  max-height: 45px;
  opacity: 1;
  overflow: hidden;
  margin-top: 14px;
  /* 2026-09-02 (max-height reduced): was 220px, a generous "safe" value
     against content this actually never needed – measured all four
     cards directly and the tallest natural content height is 104px.
     With an ease-out curve (cubic-bezier(.16,1,.3,1), which front-loads
     motion and eases off toward the end) animating across the full
     45-220px range, the description's real, visible growth to ~104px
     happened almost instantly – confirmed directly: its rendered height
     had already reached its final value within the first ~60ms of this
     .55s transition, while ~116px of the declared range past that point
     was invisible overshoot the content never used. Meanwhile the
     card's own width (flex-grow, a separate transition) was still
     gradually easing open across the full .55s. Two parts of the same
     hover animation moving at visibly different speeds is very likely
     what read as "janky" – not dropped frames, a timing mismatch. 108px
     keeps a small margin above the tallest measured card while making
     the curve's visible motion span close to its actual duration instead
     of finishing in the first tenth of it.
     .5s -> .55s, matching .framework-card's own flex-grow duration
     exactly (was slightly mismatched) so the card widening and the
     description growing land at the same moment instead of one
     finishing a beat after the other. will-change added since this
     resize is what pushes .framework-card-media to compress every frame
     in turn (flex:1 1 auto absorbing whatever height this no longer
     leaves it) – this is the actual property changing, not just a
     downstream effect. */
  will-change: max-height;
  /* 2026-09-02: .55s -> .75s, matching .framework-card's own flex-grow
     duration exactly again, per request for an even smoother in/out feel. */
  transition: max-height .75s cubic-bezier(.16,1,.3,1), margin-top .75s cubic-bezier(.16,1,.3,1);
}
.framework-card:hover .framework-card-desc,
.framework-card:focus-within .framework-card-desc {
  max-height: 108px;
  margin-top: 16px;
}
.framework-card-desc p,
.framework-card.category-b .framework-card-desc p, .framework-card.category-c .framework-card-desc p,
.framework-card.category-d .framework-card-desc p, .framework-card.category-e .framework-card-desc p {
  transform: none !important;
  margin: 0;
  max-width: none;
  color: var(--card-ink-soft) !important;
  font-size: .92rem;
  line-height: 1.5;
  transition: color .6s ease;
}
.framework-card-cta,
.framework-card.category-b .framework-card-cta, .framework-card.category-c .framework-card-cta,
.framework-card.category-d .framework-card-cta, .framework-card.category-e .framework-card-cta {
  margin-top: 12px;
  display: inline-flex; gap: 8px; align-items: center;
  color: var(--card-ink) !important;
  font-weight: 760; font-size: .86rem;
  transition: color .6s ease;
}
.framework-card:hover .explore-arrow,
.framework-card:focus-visible .explore-arrow { transform: translateX(3px); }
.framework-card .explore-arrow {
  display: inline-grid; place-items: center;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--card-chip) !important;
  transition: transform .2s, background-color .6s ease;
}
.framework-card .explore-arrow svg,
.framework-card.category-b .explore-arrow svg, .framework-card.category-c .explore-arrow svg,
.framework-card.category-d .explore-arrow svg, .framework-card.category-e .explore-arrow svg {
  width: 13px; height: 13px; color: var(--card-ink) !important;
  transition: color .6s ease;
}

/* Below 981px, hover has nowhere to happen on a touch screen – stack
   instead, sized for touch, with the description always open rather
   than gated behind an interaction the device can't perform. */
@media screen and (max-width: 980px) {
  .framework-grid { flex-direction: column; height: auto; gap: 16px; }
  .framework-card { flex: none; height: 340px; }
  .framework-card-desc {
    max-height: none !important;
    opacity: 1 !important;
    margin-top: 16px !important;
    overflow: visible;
  }
}
@media screen and (max-width: 600px) {
  .framework-card { height: 320px; }
}

/* ---- Scroll-down hint: bottom of the hero, prompts the first scroll ---
   Fixed to the viewport (not the hero's own box) since the ask was "at
   the bottom of the screen" specifically. Fades out via .is-hidden,
   toggled in app.js as soon as the user actually starts scrolling
   (see updateScrollEffects) – once they've scrolled, the hint has done
   its job and gets out of the way rather than lingering into the next
   section. The bounce is deliberately periodic, not continuous: two
   quick nudges downward, then a pause, on a loop – a constant bounce
   reads as busy/nervous rather than a calm "hey, scroll" prompt. */
.scroll-hint {
  position: fixed;
  left: 50%;
  bottom: clamp(20px, 4vh, 44px);
  translate: -50% 0;
  z-index: 20;
  width: 84px;
  height: 40px;
  border: 0;
  border-radius: 999px;
  background: rgba(30,34,44,.5);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  display: grid;
  place-items: center;
  cursor: pointer;
  animation: scroll-hint-bounce 3.4s ease-in-out infinite;
  transition: opacity .35s ease, visibility .35s ease;
}
.scroll-hint:hover { background: rgba(30,34,44,.68); }
.scroll-hint svg {
  width: 18px; height: 18px;
  color: #fff;
  rotate: 90deg;
  stroke-width: 2.4;
}
.scroll-hint.is-hidden {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
@keyframes scroll-hint-bounce {
  0%, 45%, 100% { translate: -50% 0; }
  15% { translate: -50% 9px; }
  30% { translate: -50% 0; }
  38% { translate: -50% 5px; }
}
@media (prefers-reduced-motion: reduce) {
  .scroll-hint { animation: none; }
}
@media screen and (max-width: 600px) {
  .scroll-hint { width: 68px; height: 34px; bottom: 18px; }
  .scroll-hint svg { width: 15px; height: 15px; }
}

/* Glass blur on the persistent nav: see the !important added directly
   to .site-header.is-compact .header-shell (~line 4859) for the fix and
   full history. No separate override needed here anymore. */

/* ---- keep-your-report notice (2026-09-07) -----------------------------
   Sits at the top of a researched report. Reason it exists: there are no
   accounts, so a report lives in this browser and nowhere else the reader
   controls. The notice is only true because readRecentReport() and
   markReportInUrl() in app.js now restore it - do not ship one without the
   other, or the copy becomes a promise the code does not keep.
   Researched reports only. A demonstration report is reproducible from its
   own link and costs nothing to regenerate, so the warning would be noise. */
.keep-notice {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  margin: 18px 0 4px;
  padding: 14px 16px;
  border: 1px solid rgba(250, 204, 21, .28);
  border-radius: 12px;
  background: rgba(250, 204, 21, .07);
  color: #FDE68A;
  font-size: .93rem;
  line-height: 1.55;
}
.keep-notice svg { flex: 0 0 auto; width: 18px; height: 18px; margin-top: 2px; }
.keep-notice strong { display: block; margin-bottom: 3px; color: #FEF3C7; }
.keep-notice span { color: rgba(253, 230, 138, .82); }
/* Irrelevant on paper: a printed copy IS the durable copy the notice asks
   the reader to make. */
@media print { .keep-notice { display: none !important; } }

/* 2026-09-09: "Expand for more" must not print. It is a real control on
   screen - it reveals the detail block under a category topline - but on paper
   every category is already fully expanded, so the words sit there inviting a
   reader to click a piece of paper. Hidden rather than removed, because the
   button is doing its job in the browser. */
@media print { .category-expand-more { display: none !important; } }

/* ---- category badge, supplied artwork (2026-09-09) ---------------------
   The badge used to be a solid var(--cat) tile with a white glyph. The
   supplied icons are themselves coloured - green controller, coral dollar,
   yellow eye, purple shield - so a solid tile in the same colour would hide
   them. The tile is now a soft tint of the category colour with a matching
   hairline, and the artwork carries the colour, which is what the reference
   screenshot shows.

   The icons are square 256px canvases with symmetric padding, so `contain`
   plus centring lands them dead centre with no per-icon adjustment. width and
   height are set on the <img> as well, so the box is reserved before the file
   loads and the heading does not shift. */
.category-badge {
  background: color-mix(in srgb, var(--cat) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--cat) 30%, transparent);
  box-shadow: none;
  overflow: hidden;
}
.category-badge img {
  /* 2026-09-09 v2: 76% is now the ACTUAL rendered size of the artwork, because
     the files are full-bleed. Before, this 62% multiplied with a 68.75% inset
     baked into each PNG and the glyph came out at ~41% of the tile — neither
     number described what you saw. One padding, one lever.
     76% leaves 12% clear on each side, which is the "little padding" asked for
     and keeps the artwork off the tile's rounded corners. */
  width: 76%;
  height: 76%;
  object-fit: contain;
  display: block;
}
/* The one remaining sprite glyph (Parent Engagement, no artwork supplied)
   takes the category colour so it sits with the set rather than white-on-tint. */
.category-badge svg { color: var(--cat); }

@media print {
  /* On paper the tint is nearly invisible, so the tile keeps a light fill and
     a real border. Artwork prints as-is; background-colour printing is
     unreliable, a border is not. */
  .category-badge {
    background: color-mix(in srgb, var(--cat) 12%, white) !important;
    border: 1px solid color-mix(in srgb, var(--cat) 45%, white) !important;
    color: var(--cat) !important;
  }
  .category-badge img { width: 76%; height: 76%; }
}

/* ---- card content is inset from the BAR, not the card edge (2026-09-09) --
   MEASURED. At >=981px the flat card design sets
   `.report-category { padding: 0 !important }`, so content began 1px from the
   card's left edge — but `.report-category::before` is a 6px category accent
   bar absolutely positioned at left:0, and being positioned it painted over
   whatever sat beneath. The badge lost 5 of its 34px underneath it, so an
   icon centred in the badge read as pushed right.

   The correct rule, and the one this now follows: the left margin is measured
   from the RIGHT edge of the bar. Padding the card by exactly the bar's width
   re-bases every child — badge, title, topline, detail column — onto that
   edge, so they keep the relative alignment they were designed with and
   simply stop starting underneath the stripe.

   Padding rather than z-index: raising the badge above the bar would have
   hidden the symptom while leaving the content still measured from the wrong
   edge, and the bar would have run behind the tile.

   The 6px MUST track `.report-category::before`'s width (styles.css ~1691).
   If the bar is ever retuned, this moves with it. */
@media screen and (min-width: 981px) {
  .report-category { padding-left: 6px !important; }
}

/* ---- band print button on one line (2026-09-09) -----------------------
   The icon was removed from this button, so "Print or Save PDF" now fits.
   It was wrapping to two lines because .report-actions-band is a flex row
   whose text column is free to grow, squeezing the button track until the
   label broke. nowrap makes the button's intrinsic width the floor the flex
   layout has to respect, so the text column yields instead.

   Scoped to the band. The sidebar keeps its icon and its full-width buttons,
   where wrapping was never a problem. */
.report-actions-band .report-actions .button { white-space: nowrap; }

/* ---- VGM logo returns to the start (2026-09-09) -----------------------
   The anchor must not change the promo bar's layout: .promo-bar-logos is a
   flex row sizing its children, and an inline <a> around an <img> introduces
   a text baseline and a descender gap that would nudge the logo off the
   centreline it shares with the Health-e-Habits mark. inline-flex removes the
   baseline; the height rule keeps the anchor exactly the image's box. */
.promo-bar-home,
.promo-bar-heh {
  display: inline-flex;
  align-items: center;
  line-height: 0;
  border-radius: 8px;
  transition: opacity .16s ease;
}
.promo-bar-home:hover,
.promo-bar-heh:hover { opacity: .82; }
.promo-bar-home:focus-visible,
.promo-bar-heh:focus-visible {
  outline: 2px solid var(--brand-purple, #A259FF);
  outline-offset: 3px;
}
/* href="./" drops any ?t= in the address bar, which is the point: the logo
   returns to a clean start rather than reloading the report being read. */

/* 2026-09-09 — DO NOT RAISE .promo-bar's z-index.
   It was briefly changed to 12 here on the theory that the bar being
   unclickable below the top of the page was a bug. It is not. The bar is
   position:fixed at z-index 5 SO THAT the sticky .site-header and the page
   sections scroll over and cover it — see the comments at .site-header and
   .promo-bar above, which describe that as the whole mechanism ("no
   JS-driven z-index flip needed"). Raising it left a white block pinned over
   the page at every scroll position.
   The bar is only meant to be visible, and therefore only meant to be
   clickable, at rest at the top of the page. Hit-testing it while it is
   covered measures something that is not on screen. */

/* 2026-09-09: a flex-wrap + centre rule lived here to tidy the short final row
   left when the Change Over Time card was removed. A sixth card (Enforcement)
   was added, so the grid tiles 3x2 again and the rule is gone. If a card is
   ever removed again, the hole at the bottom right is the thing to solve -
   `repeat(3, 1fr)` leaves one with 5 cards, and it reads as a missing card
   rather than a deliberate end. */


/* 2026-09-09: the Framework dropdown item uses a supplied gear PNG instead of
   the #i-flask sprite glyph. Sized to match `.ndi-icon svg` exactly so the two
   remaining items stay optically level.

   The source art is navy rgb(23,63,95), which measured 1.12:1 against this
   tile — effectively invisible, since the tile is a purple tint over a
   near-black panel. Recoloured at export to #e3cfff, the same lavender its
   sibling icons inherit via `color`, which measures 8.55:1. The shape is the
   client's; only the colour changed. */
.ndi-icon img { width: 19px; height: 19px; display: block; }

/* ---- FAQ paragraph spacing (2026-09-10) -------------------------------
   `.faq-answer p { margin: 0 }` above zeroes every margin, which is right for
   the LAST paragraph — the container has its own bottom padding and a trailing
   margin would double it. But it also removed the gap BETWEEN paragraphs, so
   multi-paragraph answers ran together at normal line spacing and read as one
   block. That was already true of "Why $1.99?" (2 paragraphs) and the missing
   game answer (4); the 2026-09-10 copy revision made it obvious by taking four
   of the eight answers multi-paragraph.

   `p + p` adds the gap only between siblings, so the last paragraph still
   sits flush against the container padding. */
.faq-answer p + p { margin-top: 14px; }

/* ---- methodology grid: rows must match each other (2026-09-10) --------
   A CSS grid sizes each ROW to its own tallest item, so the two rows only
   matched while both wrapped to the same number of lines. Below about 1050px
   the top three cards wrapped to 5 lines while the bottom three stayed at 4,
   giving rows of 209px and 186px — visibly uneven, and the reason the top row
   looked taller.

   Evening out the copy lengths (all six now within 148-150 chars) fixes the
   common case. `grid-auto-rows: 1fr` guarantees it: every row gets the same
   height whatever the wrapping does at an untested width. */
.methodology-grid { grid-auto-rows: 1fr; }

/* ---- inline links inside FAQ answers (2026-09-10) ---------------------
   The sample-report link added on 2026-09-10 inherited the answer's muted
   body colour and relied on the browser's default underline alone. That does
   read as a link, but every other inline link on the page (the footer email)
   carries the brand purple, so this matched nothing.

   Purple plus the underline, rather than purple alone: the footer email drops
   its underline because it sits on its own line, whereas this one sits mid
   sentence in a muted paragraph, where colour by itself is a weaker cue. */
.faq-answer a {
  color: var(--brand-purple, #A259FF);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
  transition: color .16s ease;
}
.faq-answer a:hover { color: #C08BFF; }
.faq-answer a:focus-visible { outline: 2px solid var(--brand-purple, #A259FF); outline-offset: 2px; border-radius: 3px; }

/* ---- framework card hover: no bump in the media (2026-09-10) ----------
   MEASURED. The card grows via flex-grow on hover (338px -> 497px at 1440),
   and `.framework-card-desc p` carries `max-width: none`, so the paragraph
   widens with it and RE-WRAPS mid-animation. Monetization went 4 lines to 2,
   the others 4 to 3. Because the desc used `max-height` its height followed
   that reflowed content, and `.framework-card-media` (flex: 1) absorbed the
   difference — so the image jumped at the moment the text re-wrapped. That is
   the bump.

   It was not there before 2026-09-10 because the old copy was ~100 chars and
   already fitted the clipped 45px at rest; the plain-language rewrite took the
   descriptions to ~140 and made the reflow visible.

   Fix is `height` rather than `max-height`, so the box is 45px at rest and
   108px hovered NO MATTER what the text inside does. The media's height is
   then deterministic and animates smoothly; text re-wrapping inside a
   fixed box moves nothing.

   Capping the paragraph width was the other candidate and was rejected: the
   resting width runs 294px at 1440 down to 184px at 1000, so any fixed cap
   binds on wide screens and stops binding on narrow ones, where the reflow
   would return. Verified the hovered text fits inside 108px at 1440, 1280,
   1150 and 1040. */
.framework-card-desc { height: 45px; }
.framework-card:hover .framework-card-desc,
.framework-card:focus-within .framework-card-desc { height: 108px; }

/* Netlify Forms honeypot: must exist in the DOM and be submittable, so it is
   hidden visually rather than with display:none or hidden, either of which
   some browsers skip on submit. aria-hidden + tabindex="-1" keep it away from
   screen readers and the tab order. */
.fn-hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ---- every toast carries the ring (2026-09-10) ------------------------
   Same technique as .ss-glow on the showcase card: a conic gradient painted
   into a ::before, masked so only the border strip shows, with --ss-angle
   turning forever. --ss-angle is registered with @property further up, which
   is what lets the conic interpolate instead of snapping.

   Started as opt-in for the signup confirmation, then applied to ALL toasts
   by request: anything that pops up to alert the reader gets it, so the
   treatment is consistent rather than one message looking special.

   NO `position` here. .toast is position:fixed (right/bottom 22px) and this
   would land on the same element — setting position:relative overrode the
   fixed placement and dropped the toast into the document flow, measured at
   x:-22 y:1843 instead of pinned to the corner. `fixed` already anchors the
   ::before, so only the stacking context is needed.

   The ring is inset by 1px and blurred slightly less than the card's (1px
   against 1.4px): at toast scale the card's blur reads as a smudge rather
   than a stroke. */
.toast { isolation: isolate; }
.toast::before {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  padding: 2px;
  background: conic-gradient(from var(--ss-angle),
    #FF8A3D, #FF3D77, #A259FF, #3DD9FF, #A259FF, #FF3D77, #FF8A3D);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  filter: blur(1px);
  pointer-events: none;
  z-index: -1;
  animation: ssRingTurn 2.6s linear infinite, ssRingIn .5s ease-out both;
}
.toast-title {
  display: block;
  font-weight: 700;
  letter-spacing: .01em;
  margin-bottom: 3px;
  color: #fff;
}
/* Failure toasts turn the ring red rather than losing it. The full rainbow
   reads as celebratory, which pulls against a message saying something went
   wrong — .toast-persistent already carries an amber border for exactly that
   reason. Same geometry, same rotation, different palette, so the motion
   language stays consistent while the colour carries the meaning.

   Amber through to deep red rather than a flat red: a single hue has nothing
   to rotate THROUGH, so the conic would turn invisibly and the ring would sit
   static beside the animated ones. */
.toast-persistent::before {
  background: conic-gradient(from var(--ss-angle),
    #FFB13D, #FF6B3D, #E5484D, #B32530, #E5484D, #FF6B3D, #FFB13D);
}
/* Interior matches the ring. The base toast is navy (rgba(6,27,58,.95)),
   which under a red ring read as two unrelated colours rather than one
   warning. Deep red at the same alpha so the blur behind it behaves
   identically. Kept dark rather than saturated: white body text sits on this,
   and a bright red panel would drop the contrast well below AA. */
.toast-persistent {
  background: rgba(58, 12, 18, .95);
  border-color: rgba(255, 138, 101, .55);
}

@media (prefers-reduced-motion: reduce) {
  /* Keep the ring, stop the turn — the colour still reads as a flourish. */
  .toast::before { animation: ssRingIn .5s ease-out both; }
}

/* ---- footer links, one colour (2026-09-10) ----------------------------
   MEASURED: the legal column's links inherited their parent's muted white
   (rgba(255,255,255,.35)) while the credits column's were purple
   (rgba(216,180,254,.74)). Two link treatments in one footer row, side by
   side, reading as two different kinds of thing.

   Matched to the credits colour rather than the other way round: the purple
   is the one that actually looks like a link, and it is the brand accent used
   for inline links elsewhere (see .faq-answer a).

   Scoped to the two columns rather than `.site-footer a`, which would also
   catch the nav-style buttons in Product/Trust and the social icons. */
.footer-legal a,
.footer-credits a {
  color: rgba(216, 180, 254, .74);
  text-decoration: underline;
  text-underline-offset: 2px;
  transition: color .16s ease;
}
.footer-legal a:hover,
.footer-credits a:hover { color: #E9D5FF; }

/* ---- promo bar reserve must follow the bar's real height (2026-09-11) ---
   BUG, reported from a friend's laptop and reproduced: below ~1320px the
   promo bar's search box WRAPS to a second row under the logos, taking the
   bar from 210px to 296px. `--promo-bar-height` is hardcoded to 210px and is
   what gives .site-header its margin-top, so the header stayed at 210 and slid
   over the bottom half of the search box. The input was sliced through the
   middle and "Map the Game" was clipped — the search looked like it had fallen
   behind the hero.

   Measured to the pixel: one row at 1315px, two rows at 1314px, so the bar is
   296px from 1314 down to the 981px phone breakpoint. A first attempt used
   1320px and over-reserved by 86px at 1315-1320 — a gap rather than a covered
   search, but still wrong. Affects 1280 — one of the most
   common laptop widths there is — and any window that is not maximised.

   A media query rather than measuring in JS: the wrap point is deterministic,
   and a CSS fix cannot flash the broken layout while a script loads. 1320px
   is the measured changeover, not a guess.

   IF THE PROMO BAR'S CONTENTS CHANGE, RE-MEASURE BOTH NUMBERS. They are the
   bar's real height in each state; nothing derives them automatically. */
@media (max-width: 1314px) and (min-width: 981px) {
  :root { --promo-bar-height: 296px; }
}

/* ---- ESRB block colour, print (2026-09-11) ----------------------------
   The screen rules above are inside `@media screen`, so in PRINT `.cat-esrb`
   had no rule at all and `--cat` fell through to the default — the same
   `--blue-a` (#1685F8) that `.cat-a` uses. So the PDF had ESRB Rating and
   Gameplay Mechanics in identical blue: the same duplication the client
   flagged on screen, one pair over.

   Slate blue rather than the screen's #3B82F6: print Gameplay is already the
   vivid #1685F8, so a second bright blue would read as the same colour on
   paper. #4A5B7C stays in the blue family as asked while sitting 130 away from
   the nearest print colour.

   NOTE the screen and print palettes are wholly different schemes, not tuned
   variants — Monetization is red (#FF6B6B) on screen and TEAL (#22C7B8) in
   print; Safety is yellow on screen and orange in print. That divergence
   predates this change and is left alone deliberately; aligning the two is a
   bigger decision than removing a duplicate. */
@media print {
  .report-category.cat-esrb { --cat: #4A5B7C; }
}
