/* ==========================================================================
   A2Amazing waitlist, shared stylesheet
   Tokens sourced from BKM/identity/brand-assets.md (see project README).
   No external fonts, no CDN, system font stack only, per build constraint.
   ========================================================================== */

:root {
  --grad-1: #403676; /* deep blue-purple, corner */
  --grad-2: #6C4C8D; /* purple */
  --grad-3: #6E47C4; /* blue-purple */
  --grad-4: #C9489A; /* pink/magenta, headline-only, never body text */

  --light-bg: #FDFDFD;
  --dark-text: #030303;
  --white: #FFFFFF;

  --accent-panel: rgba(3, 3, 3, 0.32);
  --card-border: rgba(255, 255, 255, 0.14);

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  --radius-lg: 20px;
  --radius-md: 14px;
  --radius-sm: 8px;

  --shadow-soft: 0 20px 60px -20px rgba(64, 54, 118, 0.45);
  --ease: cubic-bezier(0.16, 1, 0.3, 1);

  /* ---------- Type scale (task 1162) ---------------------------------
     Ryan: "make all the sub header text and body text a couple points
     bigger," everything except headlines. Moved to tokens rather than
     patching each selector, so the next size pass touches four lines,
     not eight. Baseline is the unstyled browser default (1rem = 16px);
     nothing here touched .headline, .eyebrow, .section-title, or
     .section-label, none of which were named in the feedback.
       --fs-label: form field labels + checkbox option text.
         0.94-0.95rem (~15-15.2px) -> 1.05rem (16.8px), +~1.7px.
       --fs-body: paragraph copy (.panel p, .bar-callout) and list
         items (.bullets li). 1rem/0.98rem (~16-15.7px) -> 1.125rem
         (18px), +2px, matching Ryan's own "16 to 18" example exactly.
       --fs-subhead: the .subhead line under every headline. Was
         clamp(1.05rem, 2.4vw, 1.25rem) (16.8-20px) -> widened at both
         ends to 18.4-22px, +1.6-2px, same clamp shape so it keeps
         scaling fluidly rather than jumping at a breakpoint.
       --fs-card-title: .panel h3, the group-card titles on Page A.
         1.15rem (18.4px) -> 1.3rem (20.8px), +2.4px. This also crosses
         the WCAG large-text bold threshold (18.66px @ 700 weight): it
         now clears contrast at the 3:1 bar instead of 4.5:1, a safer
         position than before, not a riskier one. See the .panel h3
         rule below and the README verification note.

     ---------- Task 1175 addition: --fs-section-label -------------------
     Bug this task fixed: .section-label (THE TWO GROUPS / WHO THIS IS
     FOR / WHO THIS IS NOT FOR / FIND YOUR TRACK on Page A, plus the
     equivalent overline labels on B/C) was never part of the task 1162
     pass above (that task's own comment says so explicitly: "nothing
     here touched ... .section-label"). It stayed a flat 0.78rem
     (12.48px) while --fs-body grew to 1.125rem (18px) underneath it,
     so every section's own label rendered SMALLER than the body copy
     it introduces, an inverted hierarchy Ryan called out directly.
     Fixed as a ratio against --fs-body, not a second flat constant, so
     a future --fs-body change can't re-cross it the same way this one
     did: --fs-section-label: calc(var(--fs-body) * 1.15). At the
     current --fs-body that resolves to 1.29375rem (~20.7px), clearly
     above body (18px) and clear of the WCAG large-text bold threshold
     (18.66px @ 700 weight) with room, so contrast only needs to clear
     3:1, not 4.5:1, same safety direction as the --fs-card-title note
     above. Uppercase, weight, letter-spacing, and margin on
     .section-label are all unchanged, per Ryan's "keep whatever
     uppercase and letter-spacing treatment they already carry." */
  --fs-label: 1.05rem;
  --fs-body: 1.125rem;
  --fs-subhead: clamp(1.15rem, 2.5vw, 1.375rem);
  --fs-card-title: 1.3rem;
  --fs-section-label: calc(var(--fs-body) * 1.15);
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: var(--font);
  background: var(--light-bg);
  color: var(--dark-text);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img {
  max-width: 100%;
  display: block;
}

a {
  color: inherit;
}

.wrap {
  width: 100%;
  max-width: 720px;
  margin: 0 auto;
  padding: 0 24px;
}

/* ---------- Dark zone ---------------------------------------------------
   The four brand hexes are used unmodified per BKM/identity/brand-assets.md
   §Print-safety note (screen-only, no CMYK conversion applies). A radial
   glow anchored at the darkest stop sits behind text-bearing regions so
   body copy always resolves against a very dark purple regardless of
   where it lands on the diagonal. This guarantees the 4.5:1 body-text
   floor without relying on layout coincidence, while the diagonal sweep
   (dark corner -> pink corner, 135deg, brand-assets.md:44-47 sample order)
   still reads at the page edges, the same "energy" as the flyer.
   -------------------------------------------------------------------- */

.dark-zone {
  position: relative;
  background:
    radial-gradient(ellipse 130% 110% at 50% 30%, rgba(64, 54, 118, 0.92) 0%, rgba(64, 54, 118, 0) 68%),
    linear-gradient(135deg, var(--grad-1) 0%, var(--grad-2) 35%, var(--grad-3) 65%, var(--grad-4) 100%);
  color: var(--white);
  isolation: isolate;
}

.dark-zone > * {
  position: relative;
  z-index: 1;
}

/* ---------- Hero background artwork (task 1164) --------------------------
   Replaces task 1143's inline-SVG circuit/ribbon/sparkle/binary-edge
   approximation (including the .dark-zone::before dot texture, both
   removed outright rather than layered under the photo, since two
   competing treatments fight each other) with Ryan's real generated
   artwork: source assets/hero-bg.{webp,jpg}, from a2a-background.png
   (1376x768), which matches the printed flyer's composition, not an
   approximation of it.

   Two landmarks in that composition drive everything below:
     - the circuit traces split left/right and leave an empty gap dead
       center at the top. .logo-mark sits inside that gap (see each page's
       hero markup) rather than floating at an arbitrary position.
     - a calm, low-detail dark band runs across the image's vertical
       middle. .eyebrow/.headline/.subhead land there; the busy detail
       (traces, ribbons, sparkles) stays pushed to the top edge and the
       left/right ribbons, exactly like the flyer.

   Delivered as WebP with a JPEG fallback via image-set() (the plain
   url() JPEG declaration comes first so browsers that don't parse
   image-set() keep it rather than getting nothing): ~101KB / ~144KB,
   both well under the 400KB budget the 1586KB source PNG blew past.

   Scoped to the hero itself via .hero-photo, not the whole .dark-zone,
   so it works identically whether the hero IS the dark-zone element
   (Pages B/C's <section class="dark-zone hero-pad hero-photo">) or sits
   inside one (Page A's <section class="hero-pad hero-photo"> inside
   <body class="dark-zone">). Page A's below-hero sections keep the
   plain .dark-zone gradient above, unchanged; that is what makes Page A
   "dark end to end" without stretching a 768px-tall photo the full
   scroll length of the page, which would look nothing like native
   resolution by the second screen.

   Contrast: near-black scrim (rgba(5,3,15,...), not a brand hue, so it
   darkens indiscriminately, including the bright white circuit-trace
   nodes and sparkle glints that would otherwise sit under text).
   First pass used a radial ellipse centered on the text column; real
   worst-case pixel testing (see below) showed its edges fall off too
   fast, failing at the outer edge of a 600px-wide subhead line (as low
   as 1.94:1 measured). Replaced with a full-width vertical band so
   alpha never depends on x, only on how far down the hero the text
   sits: solid ~0.6 alpha from 42% to 94% of hero height (covers every
   eyebrow/headline/subhead position across both page layouts, hero
   without an eyebrow and hero with one, at both the 1440px and 375px
   breakpoints, with margin on both ends), fading to 0 above 28% so the
   circuit gap and logo stay untouched, fading to 0.22 at 100% so the
   bottom ribbon crossing keeps some color. Verified by compositing the
   real source pixels through the exact cover-crop + gradient math for
   all 10 real headline/subhead/eyebrow boxes pulled live via
   getBoundingClientRect at both breakpoints: worst case 6.56:1, next
   is 6.67:1, everything else clears 6.5:1+ (several 18-19:1 where text
   sits over the darkest part of the image before any scrim). Full
   numbers in Team Inbox/2026-08-21-senior-developer-task1164-background-art.md.

   Task 1168 re-tune: 42%/94% -> 21%/94%, alpha 0.6 -> 0.7, plateau start
   28% -> 13%. Not a copy-paste of the old stops: this task moved the
   hero from content-driven height to the aspect-ratio-locked height
   above, and pulled hero-pad's top padding way down (see that rule) so
   the logo sits against the trace band, which together push the first
   line of real text (the eyebrow on Page B, the headline on A/C, which
   have no eyebrow) up to ~28% of hero height on mobile, right at the
   OLD fade-in's start rather than past its end. Caught by re-running
   this exact compositing methodology against the new geometry before
   calling it done, not assumed carried over: it surfaced a real
   1.36:1 on Page B's mobile eyebrow (source pixel (199,235,255), the
   bright ribbon glow on the left edge, effectively unscrimmed at that
   height under the old stops). Moving the plateau's start from 42% to
   21% still leaves the 0-13% zone (mobile: logo + most of the wordmark)
   completely untouched, same intent as before; the ~0.6 alpha original
   ceiling was raised to 0.7 on top of that, since 0.6 alone cleared the
   worst real pixel by less than a point (4.89:1) once it was actually
   reached, thinner than every other measured position, so the ceiling
   was raised rather than shipping the one number in this whole set that
   was close to the floor. Re-verified all 13 real positions after
   (Page C has no eyebrow, see the .eyebrow discrepancy task 1166 already
   flagged, still true): worst case now 6.76:1 (Page B mobile eyebrow,
   the same position, now with margin), full table in the task 1168
   writeup. -------------------------------------------------- */

.hero-photo {
  position: relative;
  isolation: isolate;
  /* Task 1168: "all 3 pages have different size area for the top section
     and therefore show differing amounts of the background image. make
     all of them the same. extend the down to show the full image."
     Root cause: height used to come purely from content (padding + logo +
     eyebrow/headline/subhead), which differs by page (Page B carries an
     eyebrow and the longest subhead, Page C's subhead is longer than
     Page A's), so background-size:cover cropped a different slice of the
     same 1376x768 source on each page. Fixed at the geometry level, not
     by trimming copy: aspect-ratio locks this box's height to the image's
     own 1376:768 ratio, so height is a pure function of viewport width,
     identical on all 3 pages regardless of how much text sits inside.
     min-height is the floor for narrow phones, where width * 768/1376
     would otherwise be too short (a 375px-wide box would aspect down to
     209px, nowhere near enough for the logo plus three lines of copy);
     see the ::before comment below for how the image still shows in
     full, uncropped, once this floor is doing the work instead of the
     aspect ratio. [Task 1174: that "uncropped" behavior is what read as
     broken on a real phone, see the ::before comment's task 1174 note;
     this box's own geometry (aspect-ratio, min-height, width:100%) is
     unchanged by that fix, only what the ::before layer does with the
     leftover height below the aspect-ratio line changed.] Content no
     longer determines the box's height at all:
     every page's content sits inside a box that is already tall enough,
     so any leftover room becomes the empty space item 4 asks the text to
     move up into, not a cause of drift between pages. */
  aspect-ratio: 1376 / 768;
  min-height: 560px;
  /* width: 100% (not left as the block default "auto"): without an
     explicit width, Chromium's aspect-ratio + min-height interaction
     resolves width FROM the min-height floor via the aspect ratio
     (560px * 1376/768 = 1003px) instead of filling the viewport, which
     blew the section out to 1003px wide inside a 375px viewport (caught
     live via a real horizontal-overflow sweep, not assumed). Pinning
     width to 100% makes width the definite dimension so aspect-ratio
     only ever solves for height, which is the intended direction. */
  width: 100%;
}

.hero-photo::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-repeat: no-repeat;
  background-position: center top;
  /* Task 1174 re-tune: contain -> cover, mobile only in effect (see
     below for why this is really one rule, not a media-query fork).
     Ryan on a real iPhone: "mobile is way off," three symptoms in one
     screenshot, all traced to task 1168's contain choice. contain fits
     the WHOLE 1376x768 image inside the box; once the 560px min-height
     floor overtakes the aspect-ratio (any width under ~1003.7px, see
     the .hero-photo comment above), that floor makes the box taller
     than the image can fill at the box's own width, and contain's
     answer to "doesn't fit" is empty space, not a crop. At 375px wide
     that empty space is 560 - 375*768/1376 = 560 - 209.3 = 350.7px,
     63% of the box: the image renders across only the top ~37%, then
     hard-stops into flat .dark-zone gradient with a visible seam,
     which is symptom 1, and that seam sits right where the headline
     lands (symptom 2 reads as "collision" partly because of that
     seam, not only the 12px gap itself, confirmed by re-screenshotting
     after this change, see the task 1174 writeup), and the leftover
     floor height below the visible content is symptom 3, a dead band
     of flat color the image never reaches.

     cover fixes all three by construction, not by tuning a number:
     it always fills the box, so there is no box-taller-than-image case
     left to hard-edge into. And it does this as a strict width-only
     swap, never touching desktop: above the ~1003.7px crossover the
     box's own height already equals the image's natural height at that
     width (aspect-ratio locked to 1376:768, same ratio as the source),
     so contain and cover have nothing to disagree about there, and
     literally render identical output; that is the existing "contain
     and cover render identically... where the aspect-ratio wins over
     the min-height floor" fact this comment already established, just
     read in the other direction now that the property is cover instead
     of contain. Desktop screenshots re-confirmed pixel-identical to
     pre-1174 after the swap, see the task 1174 writeup.

     Below the crossover, cover's scale is set by the TALLER
     requirement (box height / image height, since that number is now
     bigger than box width / image width once the floor has taken
     over), which works out to a FIXED 560/768 = 0.729167 for every
     width from 375px up to the crossover: the rendered image is always
     exactly 560px tall (matching the box exactly, so there is zero
     vertical crop or letterbox, only the sides crop, centered per
     background-position's existing "center top"), just an
     increasingly narrow horizontal window into it as the viewport
     narrows. That fixed scale is a better property than contain's old
     viewportWidth/1376 (which shrank the effective zoom on smaller
     phones) because it makes the trace-band mapping below in
     .logo-mark's comment a single constant for the ENTIRE mobile
     range instead of one formula per width: on-screen y for source
     row r is r * 0.729167, full stop, from 375px up to 1003.7px wide.
     Re-verified live at 375 and 430px (iPhone SE and Pro Max width):
     mask-to-band offset now 2-11px depending on which of three
     independent methods locates the band's center (peak-std row 105,
     half-max window 105-144 i.e. center ~124.5, and the intensity
     centroid of the whole excess-std curve at ~125, all measured live
     off the real hero-bg.jpg pixel data via canvas, not assumed
     carried over from task 1168's rows ~40-193/center~116 note, though
     that number falls inside the same cluster). All three land inside
     a single digit to low double-digit offset against hero-pad's
     EXISTING 36px top padding (task 1170's half-inch shift, untouched
     by this task), down from task 1170's 55.74px regression, with no
     padding change needed: cover's fixed mobile scale did that on its
     own. Full numbers in the task 1174 writeup. */
  background-size: cover;
  background-image:
    linear-gradient(180deg, rgba(5, 3, 15, 0) 0%, rgba(5, 3, 15, 0) 13%, rgba(5, 3, 15, 0.7) 21%, rgba(5, 3, 15, 0.7) 94%, rgba(5, 3, 15, 0.22) 100%),
    url("../assets/hero-bg.jpg");
  background-image:
    linear-gradient(180deg, rgba(5, 3, 15, 0) 0%, rgba(5, 3, 15, 0) 13%, rgba(5, 3, 15, 0.7) 21%, rgba(5, 3, 15, 0.7) 94%, rgba(5, 3, 15, 0.22) 100%),
    image-set(url("../assets/hero-bg.webp") type("image/webp"), url("../assets/hero-bg.jpg") type("image/jpeg"));
}

.hero-photo > * {
  position: relative;
  z-index: 1;
}

.logo-mark {
  /* Task 1162: "make logo 2x again," on top of task 1143's 128px -> 256px
     pass. Desktop ceiling is 512px, deliberately not higher: the source
     file (assets/logo.png) is 1024x1024, so 512px at a 2x/Retina pixel
     ratio lands exactly on native resolution. Anything past 512 upscales
     and softens. Kept the same 34vw mid-scaling factor as task 1143 so
     the fluid-growth window (the same ~494-753px range, just against
     bigger endpoints now) still feels like the same system, not a
     different one, meaning most laptop widths (1280-1440px) land in the
     435-490px range and only screens >=1506px wide hit the literal 512.

     Mobile floor is 220px, not a literal re-doubling of 168px (336px
     would run 90% of a 375px screen, the exact "absurd" case flagged in
     the brief). Applied the same discipline as task 1143 instead: that
     pass grew mobile 31% (128->168) while desktop grew 100% (128->256),
     a ~0.31 ratio of mobile-growth to desktop-growth. Desktop is again
     growing 100% (256->512) this round, so mobile grows by that same
     ~0.31 ratio: 168 * 1.3125 = 220.5, rounded to 220px (+31%, 58.7% of
     a 375px viewport). --logo-l carries that same clamp() forward so
     .logo-mask/.logo-word below (task 1168) can size off the identical
     value without duplicating the clamp() expression.

     Task 1168, "the mask, not the whole logo lockup including the
     wordmark, centers on it": .logo-mark used to be a single <img>
     rendering the full 1024x1024 file, which put the mask's own visual
     center 36.4% down the element (measured off the file's alpha
     channel: mask rows 134-612 of 1024, wordmark rows 622-791, only
     fully-transparent gap at 613-621), not 50%, and meant ~13% of the
     element's own height (rows 0-134) was dead air sitting above the
     mask before it even starts. Centering the mask on the trace band
     (band measured at source rows ~40-193 of 768 in hero-bg.jpg, center
     ~116, see .hero-photo's comment for how that maps to on-screen px
     at any width: 116 * viewportWidth/1376) through that dead air was
     the geometry conflict flagged in the previous version of this
     comment: it would have required this element's top edge to sit
     above the hero's own top edge by 49-57px, i.e. clipping the top of
     the mask off the artwork entirely.

     Fixed by splitting the single <img> into two background-image
     "sprite window" elements (.logo-mask, .logo-word below), same
     source file, same rendered scale (both size the file at var(--logo-l)
     square and use background-position to reveal just their own row
     range), so neither slice carries the other's dead space anymore.
     .logo-mask's own box is now exactly the mask's bounding box, so its
     center is that box's actual midpoint, not a fraction of a taller
     combined image, cutting the theoretical minimum offset (at
     hero-pad top padding = 0) roughly in half, to ~-20px at 375px wide
     and ~+7px at 1440px wide. hero-pad's actual top padding (4px
     mobile, 8px desktop, see that rule) lands a hair above those
     floors so the mask never touches the hero's literal top edge;
     measured live via getBoundingClientRect after: the mask's own
     center sits 23.7px below the band's true center at 375px wide and
     0.87px below it at 1440px wide, both worked through in the task
     1168 writeup, without shrinking the logo itself, which this task
     was never asked to do. .logo-mark itself is now just the
     positioning/sizing wrapper (width + centering + the shared rise
     animation and drop-shadow live on the slices, see below), not an
     image on its own. */
  --logo-l: clamp(220px, 34vw, 512px);
  width: var(--logo-l);
  margin: 0 auto;
  position: relative;
  z-index: 1;
}

.logo-mask,
.logo-word {
  width: 100%;
  background-image: url("../assets/logo.png");
  background-repeat: no-repeat;
  background-size: var(--logo-l) var(--logo-l);
  filter: drop-shadow(0 8px 24px rgba(0, 0, 0, 0.35));
  animation: rise 0.7s var(--ease) both;
}

.logo-mask {
  /* Window onto source rows 134-612 of 1024 (134/1024 = 0.130859,
     478/1024 = 0.466797). background-position-y shifts the full-size
     background image up by the offset so only this row range falls
     inside the box. */
  height: calc(var(--logo-l) * 0.466797);
  background-position: 0 calc(var(--logo-l) * -0.130859);
}

.logo-word {
  /* Window onto source rows 622-791 of 1024 (622/1024 = 0.607422,
     169/1024 = 0.165039). margin-top reproduces the ~10px (1024-space)
     gap the source art already has between the mask and the wordmark,
     scaled the same way, so the two slices read as one lockup, not two
     stacked images with a seam. margin-bottom is the task 1166 gap to
     the eyebrow/headline block, carried over unchanged from the old
     combined .logo-mark (was clamp(12px, 2.5vw, 26px) there). */
  height: calc(var(--logo-l) * 0.165039);
  background-position: 0 calc(var(--logo-l) * -0.607422);
  margin: calc(var(--logo-l) * 0.009766) auto clamp(12px, 2.5vw, 26px);
  animation-delay: 0.05s;
}

.eyebrow {
  text-align: center;
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: rgba(255, 255, 255, 0.82);
  max-width: 560px;
  margin: 0 auto 18px;
  animation: rise 0.7s 0.05s var(--ease) both;
}

.headline {
  text-align: center;
  font-size: clamp(2.4rem, 7vw, 3.6rem);
  line-height: 1.05;
  font-weight: 800;
  letter-spacing: -0.02em;
  margin: 0 0 18px;
  text-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
  animation: rise 0.7s 0.1s var(--ease) both;
}

.subhead {
  text-align: center;
  font-size: var(--fs-subhead);
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.92);
  max-width: 600px;
  margin: 0 auto;
  animation: rise 0.7s 0.15s var(--ease) both;
}

/* Task 1179: task 1175's .light-zone .subhead override (an ancestor
   selector rooted at <body class="light-zone">) matched every .subhead
   on the page, not just the "Find your track" one it was written for.
   All three pages carry <body class="light-zone"> with the hero's
   <section class="dark-zone hero-pad hero-photo"> nested inside it, so
   the hero's own .subhead is ALSO a descendant of .light-zone; the
   override's higher specificity (0,0,2,0 vs. the base rule's 0,0,1,0)
   won and turned the hero subhead near-black on a dark background on
   all three pages, invisible except by selecting the text. Root cause
   was scoping the override to an ancestor (.light-zone), which says
   nothing about what's between that ancestor and the element, rather
   than to the element's own role. Fixed structurally, not with
   !important or a deeper selector: .subhead is now two rules, base
   (hero, dark, unchanged) and an explicit on-light modifier applied
   only to the one paragraph that actually sits on a light background,
   the same pattern .bullets.on-light already uses two rules up. A
   selector can never again win by ancestry it didn't ask for; it only
   applies where the class is written on the element itself. */
.subhead.on-light {
  color: rgba(3, 3, 3, 0.7);
}

/* Task 1168: 40px -> 4px mobile / 64px -> 8px desktop (see the
   responsive block below; started this pass at 16px/24px, then measured
   the mask-to-band offset at that value, wasn't close enough to call it
   "moved up more," and pushed further to these final numbers, see the
   .logo-mark comment for the achieved offsets). Two things changed at
   once, both pushing top padding toward the floor rather than an
   arbitrary smaller number:
     - "move the logo up more for the head to be centered in the
       horizontal line area." The trace band sits in source rows ~40-193
       of 768 (measured off the real pixels, see the .logo-mark comment
       below), i.e. very close to the top of the image. The logo element
       is now much taller than that band (512px desktop ceiling vs. a
       ~150px-tall band at native scale), so getting the mask's own
       center as close to the band's center as physically possible, short
       of letting the mask overflow above the hero's own top edge, means
       taking hero-pad's top padding as close to zero as still reads as
       intentional spacing rather than a clipped edge.
     - "the text under the logo need to be shifted up into the empty
       space." Because .hero-photo's height is now aspect-ratio-locked
       (see above) rather than content-driven, the hero is dramatically
       taller than before at every breakpoint regardless of page, so the
       old 40/64px paddings that used to matter for overall hero height
       now just add dead air above the logo for no reason; cutting them
       moves the whole content block (logo, eyebrow, headline, subhead)
       up as one unit, which is what actually shifts the text into the
       empty space below, since the text's own position is anchored to
       the logo via .logo-mark's margin-bottom, not to hero-pad directly.
   Bottom padding also cut, 64px -> 32px mobile / 96px -> 48px desktop:
   with height no longer coming from padding, a smaller bottom padding
   is enough to keep the subhead off the box's own lower edge; the
   photo's real bottom margin is now whatever aspect-ratio height leaves
   over, not this number. Re-verified against the 4.5:1 contrast floor
   at every real position after this shift; see Team Inbox writeup.

   Task 1170: "shift the logo and text down about half an inch," one
   distance for both (unlike task 1166, where text moved further than
   the logo on purpose). Top padding only, 4px -> 36px mobile / 8px ->
   56px desktop (+32px / +48px). One lever, not two: per the paragraph
   above, the text block's own position is anchored to the logo via
   .logo-mark's margin-bottom, not to hero-pad directly, so a single
   top-padding increase moves the logo and the whole eyebrow/headline/
   subhead block down by the identical amount, which is what "one
   distance for both" requires. Bottom padding untouched, same as task
   1166's "out of scope" call on that space.

   +48px at desktop is 96dpi's literal half inch, matching Ryan's own
   framing. +32px at mobile is not a literal carry-over of that number:
   applying 48px unscaled against a 375px-wide, 560px-tall hero would
   eat a much bigger share of the box than the same 48px does against a
   1440-wide, 803.72px-tall one. Scaled instead by the 2/3 ratio task
   1166 already established for this exact property (that task's own
   top-padding cut was 16px mobile / 24px desktop, 16/24 = 0.667), the
   same ratio this file's own hero-pad bottom padding already carries
   (32px / 48px = 0.667, two lines below) -- three independent signals
   on the same lever converging on one ratio, not a fraction picked to
   look tidy. 48 * 0.667 = 32px, which also lands on a whole number with
   no rounding.

   Headroom check before shipping (live getBoundingClientRect, hero
   bottom minus subhead bottom, the tightest real case per page/bp):
   Page A desktop 274.84px, Page A mobile 230.48px, Page B desktop
   187.75px, Page B mobile 120.45px (the tightest of the four, still
   3.8x the 32px mobile shift). All four clear the shift with room to
   spare; nothing collides with or overflows the hero's lower edge at
   any of the six page/breakpoint combinations. See the task 1170
   writeup for the post-deploy re-measurement and the mask-to-band
   offset this necessarily grows. */
.hero-pad {
  padding: 36px 0 32px;
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

/* text-carrying panels inside a dark zone, guarantees contrast regardless
   of gradient position beneath (see .dark-zone comment above) */
.panel {
  background: var(--accent-panel);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-lg);
  padding: 28px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

.panel + .panel {
  margin-top: 20px;
}

.panel h3 {
  margin: 0 0 10px;
  font-size: var(--fs-card-title);
  font-weight: 700;
}

.panel p {
  margin: 0;
  font-size: var(--fs-body);
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.92);
}

/* Task 1175: .panel was written for a dark field only (see the comment
   above .panel), which is why background/border/text were never zone-
   scoped before now. Page A's group cards move onto white with this
   task, so they need a real light variant, not a transparent tweak:
   mirrors the existing .form-card light-card treatment (solid white,
   hairline border, --shadow-soft) rather than inventing a new surface
   language. backdrop-filter is turned off, not just left in place, since
   blur has nothing to do on top of a solid background and only costs
   paint. Text: rgba(3, 3, 3, 0.72) on white measures 9.3:1, well past
   the 4.5:1 floor for the --fs-body copy inside. */
.light-zone .panel {
  background: var(--white);
  border: 1px solid rgba(3, 3, 3, 0.08);
  box-shadow: var(--shadow-soft);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.light-zone .panel p {
  color: rgba(3, 3, 3, 0.72);
}

.bullets {
  list-style: none;
  margin: 0;
  padding: 0;
  max-width: 640px;
  display: grid;
  gap: 12px;
}

.bullets li {
  position: relative;
  padding-left: 28px;
  font-size: var(--fs-body);
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.92);
}

.bullets.on-light li,
.bullets.on-light li::before {
  color: var(--dark-text);
}

.bullets li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.5em;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.55;
}

.bullets.not-for li::before {
  border-radius: 2px;
}

.section-label {
  text-transform: uppercase;
  font-size: var(--fs-section-label);
  font-weight: 700;
  letter-spacing: 0.09em;
  color: var(--grad-1);
  margin: 0 0 14px;
}

.dark-zone .section-label {
  color: rgba(255, 255, 255, 0.7);
}

section {
  padding: 48px 0;
}

.section-title {
  font-size: clamp(1.5rem, 4vw, 1.9rem);
  font-weight: 800;
  letter-spacing: -0.01em;
  margin: 0 0 22px;
}

/* ---------- Light zone --------------------------------------------------- */

.light-zone {
  background: var(--light-bg);
  color: var(--dark-text);
}

/* ---------- Buttons ------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 17px 24px;
  border-radius: var(--radius-md);
  font-size: 1.02rem;
  font-weight: 700;
  text-decoration: none;
  border: none;
  cursor: pointer;
  transition: transform 0.18s var(--ease), box-shadow 0.18s var(--ease), filter 0.18s var(--ease);
  font-family: inherit;
}

.btn:active {
  transform: scale(0.98);
}

.btn-primary {
  background: linear-gradient(135deg, var(--grad-3), var(--grad-4));
  color: var(--white);
  box-shadow: 0 14px 30px -10px rgba(110, 71, 196, 0.55);
}

.btn-primary:hover {
  filter: brightness(1.06);
  box-shadow: 0 18px 38px -10px rgba(110, 71, 196, 0.65);
  transform: translateY(-1px);
}

/* Task 1179, replaces .btn-ghost (task 1117 original, task 1175 light-
   zone patch, both removed). Ryan on the two Page A fork buttons: "they
   are both equally great choice just for different people. let make
   them look equal." A filled gradient next to a ghost outline reads as
   primary-and-fallback, which is the opposite of the fork's own point
   (a $2k/mo seller choosing Basecamp should feel as welcome as a
   $50k/mo seller choosing the Accelerator). Also closes the class of
   bug .subhead just hit: .btn-ghost needed a .light-zone override to
   stay visible at all, which meant its look depended on an ancestor
   class rather than being correct by itself. .btn-secondary is filled
   and self-contained instead, same shape as .btn-primary: same padding,
   radius, weight, shadow depth, and hover lift (see .btn-primary above),
   so neither reads as the default. The two brand hexes NOT used by
   .btn-primary's gradient (var(--grad-1) deep blue-purple -> var(--grad-2)
   purple, vs. .btn-primary's var(--grad-3) -> var(--grad-4)) carry the
   fill: still "the purple gradient" Ryan said to keep, still one four-
   stop brand system, but the other half of it, so the two buttons read
   as siblings, not a copy-paste. White label text on grad-1/grad-2 (the
   two darkest of the four stops per the README's own gradient contrast
   table) clears 4.5:1 with more margin than .btn-primary's own grad-3/
   grad-4 fill does, and the solid fill's edge against any page
   background clears the 3:1 non-text UI-component floor by a wide
   margin. Not scoped to .light-zone or .dark-zone: like .btn-primary,
   it is a filled shape with its own shadow and reads the same on
   either background, so it never depends on ancestry to stay visible. */
.btn-secondary {
  background: linear-gradient(135deg, var(--grad-1), var(--grad-2));
  color: var(--white);
  box-shadow: 0 14px 30px -10px rgba(64, 54, 118, 0.55);
}

.btn-secondary:hover {
  filter: brightness(1.08);
  box-shadow: 0 18px 38px -10px rgba(64, 54, 118, 0.65);
  transform: translateY(-1px);
}

.btn:focus-visible {
  outline: 3px solid var(--grad-4);
  outline-offset: 3px;
}

.track-buttons {
  display: grid;
  gap: 14px;
  margin-top: 28px;
}

/* ---------- Form ----------------------------------------------------------- */

.form-card {
  background: var(--white);
  border: 1px solid rgba(3, 3, 3, 0.08);
  border-radius: var(--radius-lg);
  padding: 32px;
  box-shadow: var(--shadow-soft);
}

.field {
  margin-bottom: 20px;
}

.field:last-of-type {
  margin-bottom: 0;
}

.field label {
  display: block;
  font-weight: 600;
  font-size: var(--fs-label);
  margin-bottom: 8px;
  line-height: 1.4;
}

.field .req {
  color: var(--grad-4);
  font-weight: 700;
}

.field input[type="text"],
.field input[type="email"],
.field select,
.field textarea {
  width: 100%;
  padding: 13px 14px;
  border: 1.5px solid rgba(3, 3, 3, 0.14);
  border-radius: var(--radius-sm);
  font-size: 1rem;
  font-family: inherit;
  color: var(--dark-text);
  background: var(--light-bg);
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}

.field textarea {
  resize: vertical;
  min-height: 88px;
}

.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--grad-3);
  box-shadow: 0 0 0 4px rgba(110, 71, 196, 0.15);
}

.field input:invalid.touched,
.field select:invalid.touched,
.field textarea:invalid.touched {
  border-color: #d13b5c;
  box-shadow: 0 0 0 4px rgba(209, 59, 92, 0.12);
}

.error-msg {
  display: none;
  color: #b8264a;
  font-size: 0.85rem;
  margin-top: 6px;
  font-weight: 600;
}

.field.has-error .error-msg {
  display: block;
}

.checkbox-group {
  display: grid;
  gap: 10px;
}

.checkbox-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 14px;
  border: 1.5px solid rgba(3, 3, 3, 0.12);
  border-radius: var(--radius-sm);
  background: var(--light-bg);
  cursor: pointer;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease);
}

.checkbox-row:hover {
  border-color: var(--grad-3);
}

.checkbox-row input {
  margin-top: 3px;
  width: 18px;
  height: 18px;
  accent-color: var(--grad-3);
  flex-shrink: 0;
}

.checkbox-row span {
  font-size: var(--fs-label);
  line-height: 1.45;
}

.form-foot-note {
  font-size: 0.82rem;
  color: rgba(3, 3, 3, 0.55);
  margin-top: 16px;
  line-height: 1.5;
}

/* ---------- Success + error views -------------------------------------------- */

.success-view,
.error-view {
  display: none;
}

.success-view.is-active,
.error-view.is-active {
  display: block;
  animation: rise 0.5s var(--ease) both;
}

.form-view.is-hidden {
  display: none;
}

.success-check,
.error-mark {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.15);
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto 20px;
}

.error-mark {
  background: rgba(184, 38, 74, 0.1);
}

.error-view .form-card {
  border-color: rgba(184, 38, 74, 0.18);
}

.error-contact {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  margin-top: 24px;
  padding: 14px 18px;
  border-radius: var(--radius-sm);
  border: 1.5px solid rgba(184, 38, 74, 0.25);
  background: rgba(184, 38, 74, 0.05);
  color: #b8264a;
  text-decoration: none;
  font-weight: 700;
  font-size: 0.95rem;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease);
}

.error-contact:hover {
  border-color: #b8264a;
  background: rgba(184, 38, 74, 0.1);
}

.error-retry {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  margin-top: 12px;
  padding: 12px 18px;
  border-radius: var(--radius-sm);
  border: 1.5px solid rgba(3, 3, 3, 0.14);
  background: transparent;
  color: var(--dark-text);
  font-weight: 600;
  font-size: 0.9rem;
  font-family: inherit;
  cursor: pointer;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease);
}

.error-retry:hover {
  border-color: rgba(3, 3, 3, 0.3);
  background: rgba(3, 3, 3, 0.03);
}

.success-links h4 {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: rgba(3, 3, 3, 0.5);
  margin: 24px 0 12px;
}

.success-links h4:first-of-type {
  margin-top: 28px;
}

.link-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 10px;
}

.link-list a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
  border-radius: var(--radius-sm);
  border: 1.5px solid rgba(3, 3, 3, 0.1);
  text-decoration: none;
  color: var(--dark-text);
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1.4;
  transition: border-color 0.15s var(--ease), transform 0.15s var(--ease), background 0.15s var(--ease);
  background: var(--light-bg);
}

.link-list a:hover {
  border-color: var(--grad-3);
  transform: translateX(2px);
  background: #fff;
}

.link-list a .arrow {
  flex-shrink: 0;
  opacity: 0.4;
  transition: transform 0.15s var(--ease), opacity 0.15s var(--ease);
}

.link-list a:hover .arrow {
  opacity: 1;
  transform: translateX(3px);
}

/* ---------- Footer ---------------------------------------------------------- */

.site-footer {
  padding: 32px 0 48px;
  text-align: center;
  font-size: 0.82rem;
  color: rgba(3, 3, 3, 0.45);
}

.dark-zone .site-footer {
  color: rgba(255, 255, 255, 0.5);
}

/* ---------- Bar callout ------------------------------------------------------ */

.bar-callout {
  background: rgba(64, 54, 118, 0.06);
  border-left: 4px solid var(--grad-1);
  border-radius: var(--radius-sm);
  padding: 20px 22px;
  max-width: 640px;
  line-height: 1.6;
  font-size: var(--fs-body);
}

/* ---------- Responsive --------------------------------------------------- */

@media (min-width: 720px) {
  .wrap {
    max-width: 760px;
  }
  .form-card {
    padding: 40px;
  }
  section {
    padding: 64px 0;
  }
  .hero-pad {
    /* Task 1170: 8px -> 56px (+48px, half an inch at 96dpi). See the base
       .hero-pad rule's comment above for the mobile scaling. */
    padding: 56px 0 48px;
  }
}

@media (max-width: 480px) {
  .wrap {
    padding: 0 18px;
  }
  .form-card {
    padding: 22px;
    border-radius: var(--radius-md);
  }
  .panel {
    padding: 20px;
  }
}

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