*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

/* Registered-trademark symbols render as superscript everywhere. The
   <sup class="reg">®</sup> wrappers are injected by script.js
   (initRegSuperscript) because ® comes from dynamic product data. */
.reg {
  font-size: 0.6em;
  vertical-align: super;
  line-height: 0;
}

/* ============================================================
   Narrow-desktop safe-zone shift utility
   ------------------------------------------------------------
   The landing page is built on a 1440px Figma canvas centered
   by `margin: 0 auto`. Below 1440 viewport, the canvas overflows
   symmetrically into the .wrap gutters. Decorative children
   (background blobs, full-width photos) are allowed to overflow,
   but text content at small Figma-left coordinates clips off
   the visible viewport on the left.

   `--canvas-overhang` measures how far the canvas extends past
   the left edge of the viewport (0 when vw >= 1440, growing as
   viewport narrows). Add it to a text element's `left` to nudge
   the element rightward by exactly the amount the canvas
   overflows — keeping the text visually anchored to viewport
   left rather than disappearing into the gutter.

   Use via:
     left: max(220px, calc(var(--safe-pad) + var(--canvas-overhang)));

   At vw=1440: overhang=0, left=max(220, 50)=220 (design intent).
   At vw=1010: overhang=215, left=max(220, 265)=265 (transition,
     text just begins to shift right).
   At vw=769:  overhang=335.5, left=max(220, 385.5)=385.5 (text
     sits 50px from viewport left, canvas still centered behind).
   ============================================================ */
:root {
  --canvas-overhang: max(0px, calc((1440px - 100vw) / 2));
  --safe-pad: 50px;
}

body {
  background: var(--page-bg);
  color: var(--text-default);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.4;
  min-width: 320px;
  -webkit-font-smoothing: antialiased; letter-spacing: 0.05em;
}

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

button {
  font: inherit;
  color: inherit;
  cursor: pointer;
  border: 0;
  background: none;
  padding: 0;
}

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

/* ============================================================
   Page spine: vertical stack of full-width sections
   ============================================================ */
.page {
  display: flex;
  flex-direction: column;
  width: 100%;
}

/* ============================================================
   Two-layer wrapper pattern (variations B/C/D/F):
   Outer = full-width, clipped; Inner = fixed 1440 canvas
   for absolutely-positioned children.
   ============================================================ */
.wrap {
  width: 100%;
  display: flex;
  justify-content: center;
  overflow: hidden;
}

.canvas {
  position: relative;
  width: var(--canvas-width);
  flex-shrink: 0;
}

/* ============================================================
   Buttons (red pill-ish rect with pale green text)
   ============================================================ */
.btn {
  /* Per-variant colours via custom properties — modifiers only need to
     override these to retheme the whole flip. Rollover uses a single
     unified soft-green (#A6C0AE) across all buttons so the hover
     language is consistent regardless of surrounding bg. */
  --btn-bg-front: var(--color-red);
  --btn-bg-back: #A6C0AE;
  --btn-text-front: var(--color-pale-green);
  --btn-text-back: var(--color-pale-green);

  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  overflow: hidden;
  /* clip-path duplicates the rounded clip in a form GPU-composited children
     (the translating pseudos) honour reliably — overflow:hidden alone can
     leak a sub-pixel rim at the rounded corners on WebKit. */
  clip-path: inset(0 round var(--button-radius));
  height: var(--button-height);
  padding: 0 12px;
  border-radius: var(--button-radius);
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: uppercase;
  white-space: nowrap;
  background: transparent; /* pseudo faces paint the bg */
  color: transparent; /* native text hidden; pseudo faces render the label */
  transition: transform 0.15s ease;
}

/* Front + back faces. Both bg and text live on the same pseudo, so they
   slide together upward on hover: the front face exits the top, the
   back face rises from below into its place. */
.btn::before,
.btn::after {
  content: attr(data-text);
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: inherit;
  transition: transform 220ms ease;
}

.btn::before {
  background: var(--btn-bg-front);
  color: var(--btn-text-front);
}

.btn::after {
  background: var(--btn-bg-back);
  color: var(--btn-text-back);
  transform: translateY(101%);
}

.btn:hover::before { transform: translateY(-101%); }
.btn:hover::after  { transform: translateY(0); }

.btn:active {
  transform: translateY(1px);
}

/* .btn--on-light was a per-bg variant before the rollover was unified.
   Kept as a no-op hook so existing markup still parses and we have a
   place to differentiate again later if needed. */
.btn--on-light {}

.btn--dark {
  --btn-bg-front: var(--color-black-green-text);
}

.btn--ghost {
  --btn-bg-front: transparent;
  --btn-text-front: var(--color-black-green-text);
  border: 1px solid var(--color-black-green-text);
}

/* ============================================================
   Typography helpers
   ============================================================ */
.eyebrow {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.05em;
  color: var(--color-black-green-text);
}

.display {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 48px;
  line-height: 0.96;
  letter-spacing: 0;
  text-transform: uppercase;
  color: var(--color-black-green-text);
  margin: 0;
}

.display--lg {
  font-size: 56px;
}

.body-lg {
  font-family: var(--font-body);
  font-size: 18px;
  line-height: 1.45;
  color: var(--color-black-green-text);
  margin: 0;
}

/* ============================================================
   Reveal-on-scroll — elements tagged with data-reveal start hidden
   and dropped 20px above their final position; an IntersectionObserver
   adds .is-revealed when they enter the viewport, fading + sliding them
   down into place. Honours prefers-reduced-motion.
   ============================================================ */
[data-reveal] {
  transform: translateY(-20px);
  /* Fade is longer than the drop so the element settles into position
     while still finishing its alpha rise — more deliberate than a single
     unified timing. */
  transition: opacity 500ms ease, transform 220ms ease;
}

/* :not() bumps specificity (0,2,0) so this beats per-title opacity rules
   like .technology__title { opacity: 0.85 } that would otherwise prevent
   the element from starting fully transparent. */
[data-reveal]:not(.is-revealed) {
  opacity: 0;
}

[data-reveal].is-revealed {
  opacity: 0.85;
  transform: translateY(0);
}

/* Sibling pattern: parents marked [data-reveal-lines] stay neutral while
   each <span class="line"> child cascades in. The parent keeps its own
   opacity (typically 0.85 on headers), so the lines fade to opacity 1 and
   the visual still lands at the header's natural ~0.85. Stagger 80ms per
   line, matching the hero/intro page-load cascade. */
[data-reveal-lines] .line {
  opacity: 0;
  transform: translateY(-20px);
  transition: opacity 500ms ease, transform 220ms ease;
}

[data-reveal-lines].is-revealed .line {
  opacity: 1;
  transform: translateY(0);
}

[data-reveal-lines].is-revealed .line:nth-child(2) { transition-delay: 80ms; }
[data-reveal-lines].is-revealed .line:nth-child(3) { transition-delay: 160ms; }
[data-reveal-lines].is-revealed .line:nth-child(4) { transition-delay: 240ms; }
[data-reveal-lines].is-revealed .line:nth-child(5) { transition-delay: 320ms; }

/* Misc card titles — standard reveal (fade + drop), staggered 80ms apart
   so the three titles cascade across the row instead of all firing
   together when the section enters the viewport. */
.misc__cards .misc-card:nth-child(2) .misc-card__title[data-reveal] { transition-delay: 80ms; }
.misc__cards .misc-card:nth-child(3) .misc-card__title[data-reveal] { transition-delay: 160ms; }

/* Nerds stats — same staggered fade + drop, sequenced column-major
   (value→label of stat 1, then stat 2, then stat 3). Labels override the
   shared 0.85 land-opacity since their natural opacity is 1. */
.nerds__stat-label[data-reveal].is-revealed { opacity: 1; }

.nerds__stat:nth-child(1) .nerds__stat-value[data-reveal] { transition-delay: 0ms; }
.nerds__stat:nth-child(1) .nerds__stat-label[data-reveal] { transition-delay: 80ms; }
.nerds__stat:nth-child(2) .nerds__stat-value[data-reveal] { transition-delay: 160ms; }
.nerds__stat:nth-child(2) .nerds__stat-label[data-reveal] { transition-delay: 240ms; }
.nerds__stat:nth-child(3) .nerds__stat-value[data-reveal] { transition-delay: 320ms; }
.nerds__stat:nth-child(3) .nerds__stat-label[data-reveal] { transition-delay: 400ms; }

/* Plan cards — same column-major sequence (title→price of plan 1, then
   plan 2, then plan 3). Prices override the shared 0.85 land-opacity since
   their natural opacity is 1. */
.plan__price[data-reveal].is-revealed { opacity: 1; }

.memberships__plans .plan:nth-child(1) .plan__title[data-reveal] { transition-delay: 0ms; }
.memberships__plans .plan:nth-child(1) .plan__price[data-reveal] { transition-delay: 80ms; }
.memberships__plans .plan:nth-child(2) .plan__title[data-reveal] { transition-delay: 160ms; }
.memberships__plans .plan:nth-child(2) .plan__price[data-reveal] { transition-delay: 240ms; }
.memberships__plans .plan:nth-child(3) .plan__title[data-reveal] { transition-delay: 320ms; }
.memberships__plans .plan:nth-child(3) .plan__price[data-reveal] { transition-delay: 400ms; }

/* Pro shop subtitle draws in 80ms after the title so the right-side
   tagline reads as a follow-on to "New in the Pro Shop." */
.proshop-bottom__subtitle[data-reveal] { transition-delay: 80ms; }

/* Pro shop top section — shared slow-fade treatment for the logotype,
   body copy, CTA, and brand-logos strip. 700ms fade (vs the site's 500ms
   standard) feels more substantial settling in. Items cascade 80ms apart
   to match the rest of the site. */
.proshop-top__header[data-reveal],
.proshop-top__body[data-reveal],
.proshop-top__cta[data-reveal],
.proshop-top__logos[data-reveal] {
  transition: opacity 700ms ease, transform 220ms ease;
}

.proshop-top__header[data-reveal].is-revealed { opacity: 0.95; }
.proshop-top__body[data-reveal].is-revealed   { opacity: 1; }
.proshop-top__cta[data-reveal].is-revealed    { opacity: 1; }
.proshop-top__logos[data-reveal].is-revealed  { opacity: 0.85; }

.proshop-top__body[data-reveal]   { transition-delay: 80ms; }
.proshop-top__cta[data-reveal]    { transition-delay: 160ms; }
.proshop-top__logos[data-reveal]  { transition-delay: 240ms; }

@media (prefers-reduced-motion: reduce) {
  [data-reveal],
  [data-reveal]:not(.is-revealed),
  [data-reveal-lines] .line {
    opacity: 0.85;
    transform: none;
    transition: none;
  }
  [data-reveal-lines] .line { opacity: 1; }
  .nerds__stat-label[data-reveal],
  .plan__price[data-reveal],
  .proshop-top__body[data-reveal],
  .proshop-top__cta[data-reveal] { opacity: 1; }
  .proshop-top__header[data-reveal] { opacity: 0.95; }
}

/* ============================================================
   TOP NAV — fixed overlay (full width, sits above hero)
   ============================================================ */
.top-nav {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 95px;
  z-index: 100;
  pointer-events: none; /* let the click-through happen outside the controls */
}

.top-nav__inner {
  /* ONE gap for every element in the nav row — links, icon, log out, CTA.
     Previously each pair had its own value (36 between links, 40 to the
     icon, 18 after it), so the row read as unevenly spaced. Fluid: shrinks
     with the viewport in the narrow-desktop band, same curve the links
     always used. */
  --nav-gap: max(20px, calc(36px - var(--canvas-overhang) * 0.08));
  /* The account icon and "Log out" are one control, not two nav items — the
     icon is the subject and the word is the action on it. They sit at a third
     of the row gap so they group visually and the pair reads as a single unit
     against the evenly-spaced links either side. Derived from --nav-gap so it
     shrinks on the same curve. */
  --nav-pair-gap: calc(var(--nav-gap) / 3);
  position: relative;
  width: 100%;
  max-width: var(--canvas-width);
  height: 100%;
  margin: 0 auto;
  display: flex;
  align-items: center;
  /* Symmetric padding — 120px at the 1440 desktop, shrinks to
     --safe-pad (50) as the viewport narrows so nav links don't
     get squeezed off-screen in the narrow-desktop band. */
  padding: 0 max(var(--safe-pad), calc(120px - var(--canvas-overhang)));
  pointer-events: auto;
}

/* The script logo SVG has ~15px of empty space inside its bbox
   before the "H" glyph starts. The right-edge BOOK NOW button
   has no such inset, so symmetric CSS padding still reads as
   visually unbalanced. Pull the logo bbox left by 15px so the
   glyph's visual edge sits at the same gutter as the right
   controls. (Mobile has its own offset at -26px further down.) */
.top-nav__logo-link {
  margin-left: -15px;
  transition: opacity 0.15s ease;
}

.top-nav__logo-link:hover {
  opacity: 0.7;
}

/* Logo is a mask-image so its color is set by background-color and can be
   swapped per section theme. Single SVG file, any color. */
.top-nav__logo {
  display: block;
  width: 204px;
  height: 52px;
  background-color: var(--color-pale-green);
  -webkit-mask: url("assets/HourGolf_scriptlogo_ltgreen.svg") no-repeat center / contain;
          mask: url("assets/HourGolf_scriptlogo_ltgreen.svg") no-repeat center / contain;
  transition: background-color 220ms ease;
}

/* margin-left: auto pushes nav links + the trailing Book Now button to the
   right edge of the inner container (the logo-link stays on the left because
   it's the only flex child before this auto margin). Gap and right margin
   shrink fluidly below 1440 so the row doesn't overflow at narrow widths. */
.top-nav__links {
  display: flex;
  gap: var(--nav-gap);
  margin-left: auto;
  margin-right: var(--nav-gap);
  list-style: none;
  padding: 0;
}

.top-nav__link {
  /* Same underline-draw vocabulary as .misc-card__link /
     .site-footer__email — a 1px red gradient pinned to the
     baseline that grows from 0%→100% on hover while the text
     color flips to red. */
  background-image: linear-gradient(var(--color-red), var(--color-red));
  background-position: 0 100%;
  background-repeat: no-repeat;
  background-size: 0% 1px;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--color-pale-green);
  white-space: nowrap;
  transition: color 220ms ease, background-size 220ms ease;
}

.top-nav__link:hover {
  color: var(--color-red);
  background-size: 100% 1px;
}

/* ── Nav scrim ───────────────────────────────────────────────────────────
   The nav is transparent and takes its colour from one data-nav-theme flag per
   section — but eight of the thirteen sections contain photographs, whose
   brightness varies down the section. One flag cannot describe that, so the nav
   was legible over part of a section and not the rest.

   This lays a gradient behind the nav for the type to sit on. Two of them: a
   dark one for dark sections and a light one for light, cross-faded by
   --nav-mix (0 dark → 1 light), which script.js eases across a distance of
   scroll. Switching them at a line — what the class flip does — reads as a
   blink, because the band is a large visible element changing all at once.

   Every value is a custom property so the two breakpoints differ in one place,
   including the easing, which script.js reads back off the computed style.
   Values tuned in the lab on the nav-readability branch.

   The light scrim is deliberately the page colour (#EDF3E3). Over flat cream
   that makes it inert — dark-green type on cream needs no help — and over a
   photograph inside a light section it lifts the image toward the page. It
   only does work where work is needed. */
.top-nav {
  --nav-scrim-h: 185px;
  --nav-scrim-dark-a: 0.45;
  --nav-scrim-fade: 50%;
  --nav-scrim-light-rgb: 237, 243, 227;
  --nav-scrim-light-a: 0.88;
  --nav-ease-px: 330;
  --nav-ease-off: -10;
}

/* Below 1024 the nav is a different object — logo, account icon and hamburger,
   no link row — so it gets its own values rather than inheriting ones tuned
   against four links. 1023 is the nav's own existing breakpoint, not a new one. */
@media (max-width: 1023px) {
  .top-nav {
    --nav-scrim-h: 260px;
    --nav-scrim-dark-a: 0.6;
    --nav-scrim-fade: 0%;
    --nav-scrim-light-a: 1;
    --nav-ease-px: 260;
    --nav-ease-off: 0;
  }
}

.top-nav::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: var(--nav-scrim-h);
  pointer-events: none;
  /* Two stacked gradients, alphas running in opposite directions on --nav-mix.
     Not color-mix: it will not interpolate colours carrying their own alpha and
     pins to one end. It is fine on the solid text colours below. */
  background-image:
    linear-gradient(
      to bottom,
      rgba(var(--nav-scrim-light-rgb), calc(var(--nav-scrim-light-a) * var(--nav-mix, 0))) 0%,
      rgba(var(--nav-scrim-light-rgb), calc(var(--nav-scrim-light-a) * var(--nav-mix, 0))) var(--nav-scrim-fade),
      rgba(var(--nav-scrim-light-rgb), 0) 100%
    ),
    linear-gradient(
      to bottom,
      rgba(0, 0, 0, calc(var(--nav-scrim-dark-a) * (1 - var(--nav-mix, 0)))) 0%,
      rgba(0, 0, 0, calc(var(--nav-scrim-dark-a) * (1 - var(--nav-mix, 0)))) var(--nav-scrim-fade),
      rgba(0, 0, 0, 0) 100%
    );
}

/* The eased text colours only engage once script.js has taken over, marked by
   .has-nav-mix. Without JS the .is-on-light rules below still run exactly as
   before — so a script failure degrades to today's behaviour rather than
   leaving pale type on a cream background. */
.top-nav.has-nav-mix .top-nav__link,
.top-nav.has-nav-mix .top-nav__logout {
  color: color-mix(
    in srgb,
    var(--color-black-green-text) calc(var(--nav-mix, 0) * 100%),
    var(--color-pale-green)
  );
  transition: none;
}

.top-nav.has-nav-mix .top-nav__logo {
  background-color: color-mix(
    in srgb,
    var(--color-dark-green) calc(var(--nav-mix, 0) * 100%),
    var(--color-pale-green)
  );
}

.top-nav.has-nav-mix .top-nav__account,
.top-nav.has-nav-mix .top-nav__login,
.top-nav.has-nav-mix .top-nav__hamburger {
  color: color-mix(
    in srgb,
    var(--color-dark-green) calc(var(--nav-mix, 0) * 100%),
    var(--color-pale-green)
  );
  transition: none;
}

/* Hover still wins over the mix. */
.top-nav.has-nav-mix .top-nav__link:hover,
.top-nav.has-nav-mix .top-nav__logout:hover {
  color: var(--color-red);
}

/* When the section behind the nav is light-bg, flip logo + link colors to dark.
   The .is-on-dark default (pale green) stays for dark sections (HERO, PRO SHOP, etc). */
.top-nav.is-on-light .top-nav__logo {
  background-color: var(--color-dark-green);
}

.top-nav.is-on-light .top-nav__link {
  color: var(--color-black-green-text);
}

.top-nav.is-on-light .top-nav__link:hover {
  color: var(--color-red);
}

/* ============================================================
   Mobile nav menu (hidden on desktop; revealed when hamburger is
   tapped). Lives inside .top-nav (position:fixed) so the menu
   panel sits flush under the nav bar. When `body.is-menu-open`:
     1. Menu expands from max-height 0 → its content height
     2. Each link fades + drops in with an 80ms stagger; an
        underline draws in left→right beneath each link
     3. `.page` translates down by the menu's height so the
        hero/site content visibly slides down out of the way
   ============================================================ */
.top-nav__menu {
  position: absolute;
  top: 95px;
  left: 0;
  width: 100%;
  background: var(--color-pale-green);
  max-height: 0;
  overflow: hidden;
  transition: max-height 350ms ease;
  pointer-events: none;
}

body.is-menu-open .top-nav__menu {
  max-height: 360px;
  pointer-events: auto;
}

.top-nav__menu-list {
  list-style: none;
  margin: 0;
  padding: 26px 30px 36px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.top-nav__menu-link {
  position: relative;
  display: inline-block;
  align-self: flex-start;   /* underline only spans the text, not the row */
  font-family: var(--font-display);
  font-size: 28px;
  line-height: 32px;
  letter-spacing: 0;
  text-transform: uppercase;
  color: var(--color-dark-green);
  opacity: 0;
  transform: translateY(-12px);
  transition: opacity 220ms ease, transform 220ms ease;
}

/* Underline draws in left→right when menu opens. */
.top-nav__menu-link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -3px;
  width: 0;
  height: 1px;
  background: currentColor;
  transition: width 280ms ease;
}

body.is-menu-open .top-nav__menu-link {
  opacity: 0.85;
  transform: translateY(0);
}

body.is-menu-open .top-nav__menu-link::after {
  width: 100%;
}

/* Per-item stagger — 80ms apart, mirrors the site's reveal cadence.
   Underline trails the text by ~80ms so it reads as "link arrives,
   then the line follows underneath." */
body.is-menu-open .top-nav__menu li:nth-child(1) .top-nav__menu-link { transition-delay: 100ms; }
body.is-menu-open .top-nav__menu li:nth-child(2) .top-nav__menu-link { transition-delay: 180ms; }
body.is-menu-open .top-nav__menu li:nth-child(3) .top-nav__menu-link { transition-delay: 260ms; }
body.is-menu-open .top-nav__menu li:nth-child(4) .top-nav__menu-link { transition-delay: 340ms; }
body.is-menu-open .top-nav__menu li:nth-child(5) .top-nav__menu-link { transition-delay: 420ms; }

body.is-menu-open .top-nav__menu li:nth-child(1) .top-nav__menu-link::after { transition-delay: 180ms; }
body.is-menu-open .top-nav__menu li:nth-child(2) .top-nav__menu-link::after { transition-delay: 260ms; }
body.is-menu-open .top-nav__menu li:nth-child(3) .top-nav__menu-link::after { transition-delay: 340ms; }
body.is-menu-open .top-nav__menu li:nth-child(4) .top-nav__menu-link::after { transition-delay: 420ms; }
body.is-menu-open .top-nav__menu li:nth-child(5) .top-nav__menu-link::after { transition-delay: 500ms; }

/* Book Now CTA in the menu — opacity ramps via the .btn flip
   transitions, not the link draw-in. Drop the underline. */
.top-nav__menu-cta {
  margin-top: 8px;
  /* Book now on the left, Log out on the right — the two things you can do
     from the menu that aren't navigation, at opposite ends of the row. */
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* Log out, bottom-right of the mobile menu. Quiet next to the CTA: body font
   rather than the menu links' display face, so it reads as an action on your
   session rather than a fourth place to go. */
.top-nav__menu-logout {
  display: none;
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.05em;
  /* Same green and the same 0.85 as .top-nav__menu-link — everything in this
     panel is --color-dark-green, and the old --color-black-green-text read as
     a grey that belonged to a different system. */
  color: var(--color-dark-green);
  cursor: pointer;
  /* Same reveal as the CTA beside it, same 420ms delay, so the row lands as
     one thing at the end of the link stagger. */
  opacity: 0;
  transform: translateY(-12px);
  transition: opacity 220ms ease, transform 220ms ease, color 220ms ease;
}

.top-nav[data-auth="in"] .top-nav__menu-logout {
  display: inline-block;
}

body.is-menu-open .top-nav__menu-logout {
  opacity: 0.85;
  transform: translateY(0);
  transition-delay: 420ms;
}

.top-nav__menu-logout:hover {
  color: var(--color-red);
}

.top-nav__menu-cta .btn {
  opacity: 0;
  transform: translateY(-12px);
  transition: opacity 220ms ease, transform 220ms ease;
}

body.is-menu-open .top-nav__menu-cta .btn {
  opacity: 1;
  transform: translateY(0);
  transition-delay: 420ms;
}

/* Push the rest of the page down so the menu doesn't cover the
   hero. translate matches the menu's max-height so the menu
   panel and pushed content move in lockstep. */
.page {
  transition: transform 350ms ease;
  will-change: transform;
}

body.is-menu-open .page {
  transform: translateY(360px);
}

/* When the menu is open, the top nav stays fixed in place over
   the pale-green menu panel. Force the logo + login icon +
   hamburger/X to dark-green so they're visible regardless of
   which section the nav was over when the menu opened. Same
   color treatment .is-on-light gives the nav over light
   sections. */
body.is-menu-open .top-nav__logo {
  background-color: var(--color-dark-green);
}

body.is-menu-open .top-nav__login,
body.is-menu-open .top-nav__account,
body.is-menu-open .top-nav__hamburger {
  color: var(--color-dark-green);
}

/* Fill the top nav strip with the same pale-green as the menu
   panel below so the two areas read as one continuous menu —
   otherwise the section's photo/bg bleeds through behind the
   nav controls and the logo + icons sit on a busy backdrop. */
.top-nav {
  transition: background-color 220ms ease;
}

body.is-menu-open .top-nav {
  background-color: var(--color-pale-green);
}

/* Login icon sits on both desktop and mobile (desktop: left of
   Book now; mobile: right edge next to hamburger). Hamburger
   stays hidden until the mobile breakpoint. Color inherits via
   `currentColor` on the SVGs so they swap with the nav theme
   just like the links. */
.top-nav__login {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-pale-green);
  margin-right: var(--nav-gap);
  transition: opacity 0.15s ease, color 220ms ease;
}

.top-nav__hamburger {
  display: none;
  align-items: center;
  justify-content: center;
  color: var(--color-pale-green);
  transition: opacity 0.15s ease, color 220ms ease;
}

.top-nav__login:hover,
.top-nav__hamburger:hover {
  opacity: 0.7;
}

.top-nav__login-icon {
  width: 22px;
  height: 21px;
  display: block;
}

/* Logged-in account control. Mirrors the login icon's placement and
   theme-color inheritance, but the glyph is FILLED and carries a small
   green status dot. Links to the member account page. Which control is
   visible is driven by [data-auth] on .top-nav (see below) — set to
   "in" to show this, "out" (default) to show the login icon. */
.top-nav__account {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Always green to signal the logged-in state (overrides the nav's
     light/dark theme swap that the logged-out login icon follows). */
  color: var(--color-dark-green);
  margin-right: var(--nav-pair-gap);
  transition: opacity 0.15s ease, color 220ms ease;
}

.top-nav__account:hover {
  opacity: 0.7;
}

.top-nav__account-icon {
  width: 23px;
  height: 22px;
  display: block;
}

/* Auth-state switch. Default (logged out) shows the login icon and hides
   the account control; data-auth="in" flips them. */
.top-nav__account {
  display: none;
}

.top-nav[data-auth="in"] .top-nav__login {
  display: none;
}

.top-nav[data-auth="in"] .top-nav__account {
  display: flex;
}

/* Log out. Same visibility rule as the account icon — there is no way to
   sign out of this host otherwise (see the note in the markup). Styled as a
   quiet text button so it reads as secondary to Book now. */
.top-nav__logout {
  display: none;
  background: none;
  border: none;
  /* No padding: the gaps on both sides come from --nav-gap alone, so the
     control sits evenly between the icon and the button. The old 14px left
     padding stacked on top of the icon's margin and made the left gap wider
     than the right. */
  padding: 0;
  /* Same single gap as every other pair in the row. Because the links carry
     margin-left:auto, the added width pushes the links and icon left rather
     than moving the button. */
  margin-right: var(--nav-gap);
  /* Mobile has far less room between the logo and the hamburger, and
     "Log out" was wrapping to two stacked lines there. It's a two-word
     control — it should never wrap. */
  white-space: nowrap;
  /* Typography matches .top-nav__link exactly — it sits in the same row and
     should read as the same class of thing. It was 600/0.02em against the
     links' 700/0.05em, coloured off currentColor rather than the pale-green
     token, and dimmed to 0.75 opacity, which is why it read as a different,
     greyer colour. font-family is explicit because buttons don't inherit it. */
  /* Same underline-draw vocabulary as .top-nav__link — a 1px red gradient
     pinned to the baseline that grows 0%→100% on hover while the text flips
     to red. It sits in the same row as the links and should answer to the
     pointer the same way. */
  background-image: linear-gradient(var(--color-red), var(--color-red));
  background-position: 0 100%;
  background-repeat: no-repeat;
  background-size: 0% 1px;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: var(--color-pale-green);
  cursor: pointer;
  transition: color 220ms ease, background-size 220ms ease;
}

/* Follows the nav's light/dark theme switch exactly as the links do. */
.top-nav.is-on-light .top-nav__logout {
  color: var(--color-black-green-text);
}

.top-nav__logout:hover,
.top-nav.is-on-light .top-nav__logout:hover {
  color: var(--color-red);
  background-size: 100% 1px;
}

.top-nav[data-auth="in"] .top-nav__logout {
  display: inline-block;
}

/* Hamburger as 3 absolutely-positioned bars that morph into an X
   when the button toggles `.is-open` (or aria-expanded="true"):
   top bar rotates 45° down to center, middle fades out, bottom
   rotates -45° up to center. Middle bar is 4px wider than the
   outer two per David's design. All bars share the same width in
   the open state so the X reads symmetrically. */
.top-nav__hamburger {
  position: relative;
  width: 36px;
  height: 15px;
}

.top-nav__hamburger-line {
  position: absolute;
  left: 50%;
  height: 2px;
  background: currentColor;
  border-radius: 1px;
  transition: top 220ms ease, bottom 220ms ease,
              transform 220ms ease, opacity 220ms ease,
              width 220ms ease;
}

.top-nav__hamburger-line--top {
  top: 0;
  width: 32px;
  transform: translateX(-50%);
}

.top-nav__hamburger-line--mid {
  top: 6.5px;
  width: 36px;
  transform: translateX(-50%);
}

.top-nav__hamburger-line--bot {
  bottom: 0;
  width: 32px;
  transform: translateX(-50%);
}

/* Open state — X morph. Top + bottom converge on the centerline
   (top: 6.5px / bottom: 6.5px) and rotate to form the diagonals;
   middle fades out. */
.top-nav__hamburger.is-open .top-nav__hamburger-line--top {
  top: 6.5px;
  width: 36px;
  transform: translateX(-50%) rotate(45deg);
}

.top-nav__hamburger.is-open .top-nav__hamburger-line--mid {
  opacity: 0;
  transform: translateX(-50%) scaleX(0.2);
}

.top-nav__hamburger.is-open .top-nav__hamburger-line--bot {
  bottom: 6.5px;
  width: 36px;
  transform: translateX(-50%) rotate(-45deg);
}

.top-nav.is-on-light .top-nav__login,
.top-nav.is-on-light .top-nav__account,
.top-nav.is-on-light .top-nav__hamburger {
  color: var(--color-dark-green);
}

/* Hero page-load cascade. Nav appears instantly (it's a tool, not content
   — users need it on first frame for orientation/muscle memory). Only the
   four hero elements stagger in: eyebrow → title → buttons, 80ms apart,
   400ms fade over a 220ms drop. Whole sequence settles in ~640ms so the
   user can read and act quickly. */
/* Each line in a multi-line headline is wrapped in <span class="line">; the
   span is block-level so the lines stack the way the original <br/> did, and
   each line gets its own delay so they cascade in one after the other. */
.line {
  display: block;
}

/* Per-breakpoint .line variants. Some titles have different
   hard-break structures on desktop vs mobile (e.g. hero +
   signoff are 2 lines on desktop / 3 lines on mobile with
   breaks crossing the desktop span boundaries), so we keep both
   sets in the DOM and toggle visibility per breakpoint. */
.line--mobile {
  display: none;
}

/* `<br class="break-mobile">` inside a .line span is hidden by default;
   the mobile media query flips it to `display: inline` so the same span
   can render on one line on desktop and two on mobile, without needing
   separate desktop/mobile span sets. */
.break-mobile {
  display: none;
}

/* `<br class="break-desktop">` — inverse: visible on desktop, hidden on
   mobile. Useful when desktop has a manual line break that mobile should
   let wrap naturally. */
.break-desktop {
  display: inline;
}

.hero__eyebrow,
.hero__title .line,
.hero__actions .btn,
.intro__title .line {
  animation:
    hero-cascade-fade 400ms ease var(--reveal-delay, 0ms) both,
    hero-cascade-drop 220ms ease var(--reveal-delay, 0ms) both;
}

/* Only `from` is declared so the implicit `to` resolves to each element's
   natural cascade opacity — avoids the bug where var() inside @keyframes
   doesn't reliably read the per-element custom property and the element
   would briefly land at 1 before snapping back to its real value. */
@keyframes hero-cascade-fade {
  from { opacity: 0; }
}

@keyframes hero-cascade-drop {
  from { transform: translateY(-20px); }
  to   { transform: translateY(0); }
}

/* Tighter line-to-line stagger (80ms) inside a single headline; longer
   "beat" pauses (160–240ms) between distinct content blocks so the eye
   can land on each one before the next arrives. */
.hero__eyebrow                          { --reveal-delay: 0ms; }
.hero__title .line:nth-child(1)         { --reveal-delay: 240ms; }
.hero__title .line:nth-child(2)         { --reveal-delay: 320ms; }
/* Mobile-variant spans (3 lines instead of 2) — delays continue
   the cascade. The hidden desktop pair just no-ops on mobile;
   conversely mobile spans no-op on desktop. */
.hero__title .line:nth-child(3)         { --reveal-delay: 240ms; }
.hero__title .line:nth-child(4)         { --reveal-delay: 320ms; }
.hero__title .line:nth-child(5)         { --reveal-delay: 400ms; }
.hero__actions .btn:nth-of-type(1)      { --reveal-delay: 560ms; }
.hero__actions .btn:nth-of-type(2)      { --reveal-delay: 720ms; }
/* Intro h2 sits just below the hero — joining the cascade keeps it from
   the awkward "halfway-revealed-by-scroll" state when it loads close to
   the viewport edge. Each line cascades 80ms after the previous. */
.intro__title .line:nth-child(1)        { --reveal-delay: 960ms; }
.intro__title .line:nth-child(2)        { --reveal-delay: 1040ms; }

@media (prefers-reduced-motion: reduce) {
  .hero__eyebrow,
  .hero__title .line,
  .hero__actions .btn,
  .intro__title .line {
    animation: none;
  }
}

/* ============================================================
   HERO — full-bleed dark photo with overlay text
   ============================================================ */
.hero {
  position: relative;
  width: 100%;
  height: 741px;
  overflow: hidden;
  background: #0a0e0c;
}

.hero__photo {
  position: absolute;
  top: -15%;
  left: 0;
  width: 100%;
  height: 130%;
  object-fit: cover;
  object-position: center;
  will-change: transform;
  transform: translateY(var(--parallax-y, 0px));
}

.hero__inner {
  position: relative;
  width: 100%;
  max-width: var(--canvas-width);
  height: 100%;
  margin: 0 auto;
  padding: 0 var(--content-inset);
}

.hero__eyebrow {
  position: absolute;
  top: 262px;
  left: var(--content-inset);
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 400;
  line-height: 19px;
  letter-spacing: 0.05em;
  color: var(--color-pale-green);
  margin: 0;
  max-width: 220px;
}

.hero__title {
  position: absolute;
  top: 320px;
  left: var(--content-inset);
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 50px;
  line-height: 48px;
  letter-spacing: 0;
  text-transform: uppercase;
  color: var(--color-pale-green);
  opacity: 0.85;
  margin: 0;
  white-space: nowrap;
}

.hero__actions {
  position: absolute;
  top: 440px;
  left: var(--content-inset);
  display: flex;
  gap: 18px;
}

/* ============================================================
   INTRO — simple fluid centered section (Variation A)
   ============================================================ */
.intro {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 112px 20px 80px;
  background: var(--page-bg);
}

.intro__icon {
  width: 60px;
  height: auto;
  margin-bottom: 27px;
}

.intro__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 50px;
  line-height: 48px;
  letter-spacing: 0;
  text-transform: uppercase;
  color: var(--color-dark-green);
  opacity: 0.85;
  margin: 0 0 32px;
}

.intro__body {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 19px;
  letter-spacing: 0.05em;
  color: var(--color-black-green-text);
  max-width: 524px;
  margin: 0 0 19px;
}

.intro__body:last-child {
  margin-bottom: 0;
}

/* ============================================================
   SPACE FOR GOLF NERDS — photo overlapping green blob
   ============================================================ */
.nerds__canvas {
  height: 1238px;
}

.nerds__blob {
  position: absolute;
  left: 63.56px;
  top: 444px;
  width: 1330.88px;
  height: auto;
  pointer-events: none;
}

/* Photo frame is the visible 1000×603.6 clipping window. The actual image
   inside renders taller than the frame (using its natural aspect at 1000
   wide ≈ 680px tall) so there's ~76px of extra vertical content that gets
   clipped. JS shifts the image translateY by ~±19px during section scroll
   for a subtle parallax effect (~50% of the available hidden range). */
.nerds__photo-frame {
  position: absolute;
  left: 220px;
  top: 74.32px;
  width: 1000px;
  height: 603.6px;
  border-radius: 15px;
  overflow: hidden;
}

.nerds__photo {
  display: block;
  width: 100%;
  height: auto;
  /* Static -38px centers the taller image (natural ~680) in the 603 frame.
     JS adds --parallax-y for the scroll-driven shift. */
  transform: translateY(calc(-38px + var(--parallax-y, 0px)));
  will-change: transform;
}

.nerds__content {
  position: absolute;
  /* Original Figma at 220; nudged to 270 (+50) so the text sits
     inside the green-blob's negative space. The max() keeps that
     270 intent at wide viewports but shifts the block right by
     --canvas-overhang at narrow viewports so the headline stays
     ~--safe-pad (50px) from the viewport's left edge instead of
     clipping into the canvas gutter. Photo and blob behind stay
     anchored to their Figma canvas positions and overflow. */
  left: max(270px, calc(var(--safe-pad) + var(--canvas-overhang)));
  top: 755px;
  width: 561px;
  color: var(--color-pale-green);
}

.nerds__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 50px;
  line-height: 48px;
  text-transform: uppercase;
  color: var(--color-pale-green);
  opacity: 0.85;
  margin: 0 0 27px;
}

.nerds__body {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 19px;
  letter-spacing: 0.05em;
  color: var(--color-pale-green);
  margin: 0 0 38px;
}

.nerds__stats {
  display: flex;
  gap: 80px;
  list-style: none;
  padding: 0;
  margin: 0;
}

.nerds__stat {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.nerds__stat-value {
  font-family: var(--font-display);
  font-size: 32px;
  line-height: 1;
  color: var(--color-red);
  opacity: 0.85;
  text-transform: uppercase;
}

.nerds__stat-label {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 19px;
  letter-spacing: 0.05em;
  color: var(--color-pale-green);
}

/* ============================================================
   MEMBERSHIPS — wallpaper bg, invite card, all-include panel,
   three plans on a light-green rect, non-members row
   ============================================================ */
.memberships__canvas {
  height: 1460px;
}

/* Pond_large (Figma 12:680). The layer in Figma has a 90° rotation
   applied via its wrapper (see design context). The displayed bbox is
   1118.84 × 1138.76 at left=155.66, top=888.26 — but to make the image
   content orient correctly, the inner img is rotated 90° CW around center,
   sized to the SWAPPED dimensions (1138.76 wide × 1118.84 tall pre-rotation).
   The pond extends ~567px past the section bottom; the wrap's
   overflow:hidden does the bottom clip. */
.memberships__pond {
  position: absolute;
  left: 155.66px;
  top: 888.26px;
  width: 1118.84px;
  height: 1138.76px;
  overflow: hidden;
  pointer-events: none;
  z-index: 1;
}

.memberships__pond-img {
  position: absolute;
  width: 1138.76px;
  height: 1118.84px;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%) rotate(90deg);
  max-width: none;
}

/* Wallpaper frame (Figma 5:156): visible rounded crop is the inner Rectangle 3
   (5:159) — 1200×580 at frame offset (5.13, 160). Frame is at section coords
   (115, -13), so visible rect lands at (120.13, 147). The actual photo
   (5:157) is 1379×932 positioned at frame offset (-97.32, -7), which puts it
   at (-102.45, -167) inside the visible crop. */
.memberships__wallpaper {
  position: absolute;
  left: 120.13px;
  top: 147px;
  width: 1200px;
  height: 580px;
  border-radius: 15px;
  overflow: hidden;
  z-index: 2;
}

.memberships__wallpaper-photo {
  position: absolute;
  left: -102.45px;
  top: -167px;
  width: 1379.32px;
  height: 932px;
  max-width: none;
  will-change: transform;
  transform: translateY(var(--parallax-y, 0px));
}

/* Invite (Figma 5:168) — 484.78×711.48. Sits at section coords (153, 74.5).
   Wallpaper visible crop spans y=147-727, so invite top (74.5) overlaps
   wallpaper top by ~73px and invite bottom (786) overlaps wallpaper bottom
   by ~59px — exactly the "poke over both edges" effect. */
.memberships__invite {
  position: absolute;
  left: 153px;
  top: 74.5px;
  width: 484.78px;
  height: 711.48px;
  z-index: 5;
}

.memberships__title {
  position: absolute;
  left: 720px;
  top: 380px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 50px;
  line-height: 48px;
  text-transform: uppercase;
  color: var(--color-pale-green);
  opacity: 0.85;
  margin: 0;
  z-index: 4;
}

.memberships__all {
  position: absolute;
  left: 720px;
  top: 540px;
  width: 400px;
  height: 230px;
  border-radius: 15px;
  overflow: hidden;
  z-index: 3;
}

.memberships__all-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.85;
}

.memberships__all-content {
  position: relative;
  padding: 32px 36px;
  color: var(--color-pale-green);
}

.memberships__all-title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 26px;
  line-height: 24px;
  text-transform: uppercase;
  color: var(--color-pale-green);
  opacity: 0.95;
  margin: 0 0 16px;
}

.memberships__all-list {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 22px;
  letter-spacing: 0.05em;
  color: var(--color-pale-green);
  list-style: none;
  padding: 0;
  margin: 0;
}

.memberships__all-list li::before {
  content: "- ";
}

/* Unified light-green pricing panel (Figma 5:207) — single 1000×480 rect
   that visually contains the 3 plans, the footnote divider, and the
   non-members row. Plans content lives inside this div; footnote and
   non-members are absolutely positioned siblings layered over the same rect. */
.memberships__plans {
  position: absolute;
  /* Plans rect anchors at Figma left:220, width:1000 on wide viewports.
     Below ~1140 viewport it would clip on the left (the PATRON "$"
     was the visible casualty). The max() shifts the rect right by
     --canvas-overhang so its left edge stays at --safe-pad from the
     viewport, and the min() narrows it so the right edge also stays
     inside the viewport's safe gutter. The invite card (z-index 5,
     left:153) is left at its Figma position and is allowed to
     overhang off the left edge at narrow viewports. */
  left: max(220px, calc(var(--safe-pad) + var(--canvas-overhang)));
  top: 705px;
  width: min(1000px, calc(100vw - var(--safe-pad) * 2));
  /* Height covers the 3 plan cards (top), the footnote + divider
     (middle), and the restored 2-col non-members card (bottom).
     Sized so the bottom margin inside the panel (space between the
     non-members card's bottom edge and the panel's bottom edge) is
     ~50px — comfortable breathing room without feeling oversized. */
  height: 490px;
  background: rgba(209, 223, 203, 0.9); /* --color-light-green @ 0.9 */
  border-radius: 15px;
  padding: 114px 50px 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 50px;
  z-index: 1;
}

.plan {
  display: flex;
  flex-direction: column;
}

.plan__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 26px;
  line-height: 24px;
  text-transform: uppercase;
  color: var(--color-medium-green);
  opacity: 0.85;
  margin: 0 0 12px;
}

.plan__price {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 19px;
  letter-spacing: 0.05em;
  font-weight: 700;
  color: var(--color-red);
  margin: 0 0 16px;
}

.plan__list {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 19px;
  letter-spacing: 0.05em;
  color: var(--color-black-green-text);
  list-style: none;
  padding: 0;
  margin: 0 0 14px;
}

.plan__list li::before {
  content: "- ";
}

.plan__cta {
  align-self: flex-start;
}

.memberships__footnote {
  position: absolute;
  /* Footnote sits inside the plans rect, inset 51.66px from rect's
     left edge. Mirror the rect's fluid pattern: same overhang shift,
     plus the rect's internal 51.66 inset; width caps at the original
     898.94 but shrinks to keep ~50px inset on each side as rect
     narrows. */
  left: max(271.66px, calc(var(--safe-pad) + var(--canvas-overhang) + 51.66px));
  top: 1018.64px;
  width: min(898.94px, calc(100vw - var(--safe-pad) * 2 - 101.06px));
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 19px;
  letter-spacing: 0.05em;
  color: var(--color-black-green-text);
  margin: 0;
  padding-bottom: 11px;
  border-bottom: 1px solid var(--color-black-green-text);
  opacity: 0.85;
  z-index: 1;
}

/* Non-members card — now structurally identical to a .plan article
   (h3.plan__title / p.plan__price / ul.plan__list / a.plan__cta).
   The .memberships__nonmembers wrapper class is positioning-only;
   .plan provides the grid layout, .plan__* classes style the bits. */
.memberships__nonmembers {
  position: absolute;
  /* Same horizontal extent as .memberships__plans (sits visually
     inside it). Mirror the same fluid pattern so the non-members
     row stays in step with the rect above as viewport narrows. */
  left: max(220px, calc(var(--safe-pad) + var(--canvas-overhang)));
  top: 1053px;
  width: min(1000px, calc(100vw - var(--safe-pad) * 2));
  padding: 26px 50px 36px;
  z-index: 1;
}

/* Desktop-only side-by-side layout for the non-members card. Restores
   the pre-mobile-rebuild treatment: title + button + price stacked on
   the left, bullet list filling the right half. Mobile (≤768) keeps
   the stacked .plan layout from the mobile media query below — this
   override only applies above 768. */
@media (min-width: 1024px) {
  .memberships__nonmembers {
    /* 3-col grid mirrors the .memberships__plans grid above (3 equal
       columns with 50px gap) so the non-members card's contents align
       horizontally with the plan cards: title + button + price share
       col 1 (Patron alignment), bullet list lives in col 2 (Starter
       alignment), col 3 stays empty (where Green Jacket sits above).
       align-content: start keeps rows packed at the top rather than
       distributing them to match the list's spanned height. */
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: auto auto;
    align-content: start;
    column-gap: 50px;
    align-items: start;
  }

  .memberships__nonmembers .plan__title {
    grid-column: 1;
    grid-row: 1;
  }

  .memberships__nonmembers .plan__cta {
    grid-column: 1;
    grid-row: 2;
    align-self: start;
    /* justify-self: start prevents the button from stretching to the
       full column width (grid items default to justify-self: stretch).
       Button sits at its intrinsic .btn width, flush with the title's
       left edge. */
    justify-self: start;
    margin: 0;
  }

  .memberships__nonmembers .plan__price {
    /* Share the same cell as the button (col 1, row 2) but slide right
       via margin-left so it sits next to the button. ~145px = button
       intrinsic width ("Book now." at 14px display font + 24px padding
       ≈ 110px) + ~35px breathing gap. Retune if the button label or
       button font changes. */
    grid-column: 1;
    grid-row: 2;
    justify-self: start;
    align-self: center;
    margin: 0 0 0 145px;
    white-space: nowrap;    /* keep "$35 / hour" on one line */
  }

  .memberships__nonmembers .plan__list {
    /* Starts at col 2 (aligns with Starter card above) but spans into
       col 3 so the longest bullet ("Staffed hours (10am–5pm most days)")
       has room to render on a single line instead of wrapping. */
    grid-column: 2 / -1;
    grid-row: 1 / -1;
    margin: 0;
    align-self: start;
  }
}

/* ============================================================
   PRO SHOP TOP — full-bleed dark photo with overlay text
   ============================================================ */
.proshop-top {
  position: relative;
  width: 100%;
  height: 742px;
  overflow: hidden;
  background: #1a1d1b;
}

.proshop-top__photo {
  position: absolute;
  top: -15%;
  left: 0;
  width: 100%;
  height: 130%;
  object-fit: cover;
  object-position: center;
  will-change: transform;
  /* height:130% replaces the previous transform:scale(1.15) — the bigger
     box gives both the zoom-in look AND extra excess for parallax. */
  transform: translateY(var(--parallax-y, 0px));
}

.proshop-top__overlay {
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse at center,
    rgba(0, 0, 0, 0.25) 0%,
    rgba(0, 0, 0, 0.55) 60%,
    rgba(0, 0, 0, 0.7) 100%
  );
}

.proshop-top__content {
  position: relative;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0 30%;
  color: var(--color-pale-green);
}

.proshop-top__header {
  display: block;
  width: 524.5px;
  height: 169.3px;
  margin-bottom: 30px;
  /* Mask trick (same approach as .top-nav__logo): SVG defines the
     shape, background-color paints it pale-green so it reads as
     part of the brand palette over the dark photo (not a flat
     white logo). */
  background-color: var(--color-pale-green);
  -webkit-mask: url("assets/proshop_header.svg") no-repeat center / contain;
          mask: url("assets/proshop_header.svg") no-repeat center / contain;
}

.proshop-top__body {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 19px;
  letter-spacing: 0.05em;
  color: var(--color-pale-green);
  max-width: 560px;
  margin: 0 0 30px;
}

.proshop-top__logos {
  /* Intrinsic 1132×70 — explicit height enforces aspect ratio inside the
     flex column where height:auto on an img can collapse/stretch.
     max-width override defeats the global img { max-width: 100% } that
     would otherwise cap this at the content area's ~576px width. */
  width: 700px;
  height: 43.3px;
  max-width: none;
  margin-top: 45px;
  object-fit: contain;
}

/* ============================================================
   PRO SHOP BOTTOM — green textured bg, logo strip, 4 products
   ============================================================ */
/* Canvas height = top edge → cards (146px, the grid's `top` offset)
   + card height (~408px: square 221 photo + body with 2-line title
   reserve + 32 shadow clearance) + matching bottom space (146px).
   So 146 + 408 + 146 = 700. Gives equal green breathing room above
   and below the cards regardless of how titles wrap. */
.proshop-bottom__canvas {
  height: 700px;
}

/* Bg image breaks out of the 1440 canvas to span the full browser width
   (mirrors how proshop-top is full-bleed). The 100vw escape centers the
   element to the viewport while staying inside .wrap, which clips at the
   browser edge. */
.proshop-bottom__bg {
  position: absolute;
  top: 0;
  left: calc(50% - 50vw);
  width: 100vw;
  height: 700px;
  max-width: none; /* override the global img max-width:100% so 100vw applies */
  object-fit: cover;
}

.proshop-bottom__title {
  position: absolute;
  left: 220px;
  top: 87px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 36px;
  line-height: 24px;
  text-transform: uppercase;
  color: var(--color-pale-green);
  opacity: 0.85;
  margin: 0;
}

/* Two title variants for mobile/desktop swap — mobile Figma uses
   "New Arrivals:" instead of "New in the Pro Shop." Desktop hides
   the mobile one; the mobile media query inverts. */
.proshop-bottom__title--mobile {
  display: none;
}

.proshop-bottom__subtitle {
  position: absolute;
  left: 778px;
  top: 91px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 26px;
  line-height: 24px;
  text-transform: uppercase;
  color: var(--color-medium-green);
  opacity: 0.85;
  margin: 0;
}

/* Carousel viewport: edge-to-edge horizontal scroller. Native scroll
   for accessibility + touch (kbd arrow keys, trackpad, phone swipe),
   plus a click-and-drag handler in script.js for desktop mouse users
   who expect to grab the strip directly. The scrollbar is hidden;
   discoverability comes from the grab cursor + items partially clipped
   at the right edge implying "more this way". */
.proshop-bottom__grid {
  position: absolute;
  left: 0;
  right: 0;
  top: 146px;
  overflow-x: auto;
  /* overflow-y: hidden is required (browsers force both axes when one
     is set to auto/scroll), but it clips the hover box-shadow at the
     bottom edge of the strip. padding-bottom adds clearance INSIDE the
     clip zone so the dark shadow has room to render past the cards. */
  overflow-y: hidden;
  padding-bottom: 32px;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  cursor: grab;
  /* user-select off so dragging doesn't accidentally select text on
     desktop. Re-enabled inside card content via :where overrides if
     needed (links, headings stay selectable when not dragging). */
  user-select: none;
}

.proshop-bottom__grid::-webkit-scrollbar {
  display: none;
}

/* Scroll affordance for the pro shop strip.
   The strip deliberately hides its scrollbar, so nothing signalled that the
   row continues past the right edge — people never reached the trailing
   "Go to the Pro Shop" card. This is a circular forward arrow pinned over
   the right edge of the strip.
   Sits OUTSIDE .proshop-bottom__grid (a child would scroll away with the
   content) and is vertically centred on the card row: grid top 146px +
   roughly half a card. JS unhides it only when the strip actually overflows,
   and hides it again at the end. */
.proshop-bottom__scroll {
  position: absolute;
  right: 24px;
  top: 396px;
  width: 48px;
  height: 48px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--cream, #EDF3E3);
  color: var(--primary, #4C8D73);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.22);
  display: grid;
  place-items: center;
  cursor: pointer;
  z-index: 3;
  transition: transform 0.15s ease, opacity 0.2s ease, box-shadow 0.15s ease;
}

.proshop-bottom__scroll[hidden] {
  display: none;
}

.proshop-bottom__scroll svg {
  width: 22px;
  height: 22px;
  display: block;
}

.proshop-bottom__scroll:hover {
  transform: scale(1.08);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.28);
}

.proshop-bottom__scroll:active {
  transform: scale(0.96);
}

/* Touch devices already have swipe as an obvious affordance, and screen
   real estate is tighter — keep the arrow to pointer devices. */
@media (hover: none) {
  .proshop-bottom__scroll {
    display: none;
  }
}

/* While the user is actively dragging the strip, swap the cursor to
   "grabbing" and disable smooth-scroll so the strip follows the mouse
   1:1 without easing. Class is toggled by initNewArrivalsCarousel(). */
.proshop-bottom__grid.is-dragging {
  cursor: grabbing;
  scroll-behavior: auto;
}

.proshop-bottom__track {
  display: flex;
  gap: 38.5px;
  width: max-content;
  padding-left: 220px;            /* align first card with the title */
  padding-right: 220px;           /* matching trailing inset so the last card has breathing room past the scroll-end */
}

/* ----------------------------------------------------------
   .product-card overrides — shrink the shop card down to the
   homepage carousel's 221px footprint while keeping every other
   bit of the shop card vocabulary (colors, fonts, hover lift,
   photo zoom, NEW / SALE badges). Loaded by shop.css; these
   selectors only override sizing inside the homepage carousel.
   ---------------------------------------------------------- */
.proshop-bottom__track .product-card {
  flex-shrink: 0;
  width: 221px;
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.125);
  transition: box-shadow 220ms ease;
}

.proshop-bottom__track .product-card__photo-wrap {
  /* Square — matches the shop cards + modal so all product images
     are designed against a single 1:1 spec. */
  aspect-ratio: 1 / 1;
}

/* Tighten body padding + type a notch so the card scales down
   gracefully to carousel size without feeling cramped or showing
   the larger shop-card padding. */
.proshop-bottom__track .product-card__body {
  padding: 16px;
}

.proshop-bottom__track .product-card__title {
  font-size: 15px;
  /* Reserve space for two lines so cards stay the same height whether
     the title is "Polo" (1 line) or "Honors Nylon Jacket" (2 lines).
     2.6em = line-height (1.3 from .product-card__title base) × 2 lines.
     1-line titles sit top-aligned in the reserved area; the price +
     subtitle still align across every card in the row. */
  min-height: 2.6em;
}

.proshop-bottom__track .product-card__price {
  font-size: 20px;
}

.proshop-bottom__track .product-card__subtitle {
  font-size: 12px;
  margin-bottom: 12px;
}

/* Hover shadow override — the shop page's default lift shadow uses
   rgba(53, 68, 59, 0.14) which disappears against the dark-green
   proshop section background. Bump to a darker, larger glow so the
   hover lift reads on the dark surface. Scoped to the carousel only
   so the shop page's lighter shadow stays as-is. */
.proshop-bottom__track .product-card:hover {
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45);
}

/* CTA card — sits at the end of the carousel. Same .product-card shell
   (so it shares hover lift + carousel sizing), but the body becomes a
   centered Biden-Bold label + arrow instead of photo + brand + title.
   Width matches the product cards; height stretches to the row (flex
   default align-items: stretch on the track).

   Light-green fill (same token as the .courses__panel box behind the
   course thumbnails) with dark-green type — sibling-but-different from
   the white product cards, color-tied to the courses section. */
.proshop-bottom__track .product-card--cta {
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 32px 24px;
  text-align: center;
  background: var(--color-light-green);
}

.proshop-bottom__track .product-card__cta-label {
  /* var(--font-display) is "DDC Biden Bold" — already the bold cut by
     name, no font-weight needed. */
  font-family: var(--font-display);
  font-size: 30px;
  line-height: 1.05;
  letter-spacing: 0;
  text-transform: uppercase;
  color: var(--color-dark-green);
  margin: 0;
}

.proshop-bottom__track .product-card__cta-arrow {
  width: 36px;
  height: auto;
  color: var(--color-dark-green);
  transition: transform 220ms ease;
}

/* Slide the arrow a hair on hover — same vocabulary as a CTA button
   pulling the eye toward the action it represents. */
.proshop-bottom__track .product-card--cta:hover .product-card__cta-arrow {
  transform: translateX(4px);
}

.product__photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.product__brand {
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 18px;
  text-transform: uppercase;
  color: var(--color-medium-green);
  opacity: 0.85;
  margin: 18px 0 6px;
}

.product__name {
  font-family: var(--font-body);
  font-size: 21px;
  line-height: 21px;
  letter-spacing: 0.05em;
  color: var(--color-pale-green);
  margin: 0 0 12px;
}

.product__price {
  font-family: var(--font-display);
  font-size: 26px;
  line-height: 18px;
  color: var(--color-black-green-text);
  opacity: 0.85;
  margin: 0 0 14px;
}

.product__detail {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 15px;
  letter-spacing: 0.05em;
  color: var(--color-pale-green);
  min-height: 30px;
  margin: 0 0 18px;
}

.product__cta {
  align-self: flex-start;
}

/* ============================================================
   TECHNOLOGY — photo left, feature list right
   ============================================================ */
.technology__canvas {
  height: 773px;
}

/* Wrap holds the position/size/clip; the img inside is taller
   than the wrap so the [data-parallax] JS has range to translate
   against. Same pattern as .nerds__photo-frame / .gift-cards__photo-wrap. */
.technology__photo-wrap {
  position: absolute;
  left: 270px;
  top: 201.43px;
  width: 450px;
  height: 448.43px;
  border-radius: 15px;
  overflow: hidden;
}

.technology__photo {
  position: absolute;
  top: -15%;
  left: 0;
  width: 100%;
  height: 130%;
  object-fit: cover;
  transform: translateY(var(--parallax-y, 0px));
  will-change: transform;
}

.technology__content {
  position: absolute;
  left: 775px;
  top: 221.25px;
  width: 482px;
}

.technology__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 36px;
  line-height: 36px;
  text-transform: uppercase;
  color: var(--color-dark-green);
  opacity: 0.85;
  margin: 0 0 17px;
}

.tech-features {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.tech-feature {
  margin: 0;
}

.tech-feature__title {
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 18px;
  text-transform: uppercase;
  color: var(--color-medium-green);
  opacity: 0.85;
  margin: 0;
}

.tech-feature__body {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 18px;
  letter-spacing: 0.05em;
  color: var(--color-black-green-text);
  margin: 0;
}

.technology__cta {
  margin-top: 30px;
}

/* ============================================================
   GIFT CARDS — green-tinted golf-course bg + tree overlapping
   the TECHNOLOGY section above
   ============================================================ */
.gift-cards.wrap {
  overflow-x: clip;
  overflow-y: visible;
}

.gift-cards__canvas {
  height: 664px;
}

.gift-cards__photo-wrap {
  position: absolute;
  left: 120px;
  top: 108.65px;
  width: 1200px;
  height: 495.62px;
  border-radius: 15px;
  overflow: hidden;
}

.gift-cards__photo {
  position: absolute;
  top: -15%;
  left: 0;
  width: 100%;
  height: 130%;
  object-fit: cover;
  will-change: transform;
  transform: translateY(var(--parallax-y, 0px));
}

.gift-cards__title {
  position: absolute;
  left: 320px;
  top: 249.48px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 36px;
  line-height: 36px;
  text-transform: uppercase;
  color: var(--color-pale-green);
  opacity: 0.85;
  margin: 0;
}

.gift-cards__body {
  position: absolute;
  left: 320px;
  top: 369.76px;
  width: 437px;
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 19px;
  letter-spacing: 0.05em;
  color: var(--color-pale-green);
  margin: 0;
}

.gift-cards__cta {
  position: absolute;
  left: 320px;
  top: 442.98px;
}

.gift-cards__tree {
  position: absolute;
  left: 1050.78px;
  top: -144.84px;
  width: 328px;
  height: auto;
  pointer-events: none;
  z-index: 5;
  /* Tree visually overlaps the gift-cards photo; inherit the section's
     parallax variable so it shifts in lockstep with the photo. */
  transform: translateY(var(--parallax-y, 0px));
  will-change: transform;
}

/* ============================================================
   COURSES — sand + pond blobs, hero course, panel of thumbs
   ============================================================ */
.courses__canvas {
  height: 1496px;
}

.courses__sandtrap {
  position: absolute;
  left: -3.58px;
  top: 42.54px;
  width: 1053.16px;
  height: auto;
  pointer-events: none;
}

.courses__pond {
  position: absolute;
  left: 892.93px;
  top: 198.74px;
  width: 484.58px;
  height: auto;
  pointer-events: none;
}

.courses__icon {
  position: absolute;
  left: 685px;
  top: 196.81px;
  width: 59.9px;
  height: auto;
  z-index: 3;
}

.courses__title {
  position: absolute;
  left: 0;
  right: 0;
  top: 327.74px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 36px;
  line-height: 36px;
  text-align: center;
  text-transform: uppercase;
  color: var(--color-dark-green);
  opacity: 0.85;
  margin: 0;
  z-index: 3;
}

.courses__body {
  position: absolute;
  left: 401.97px;
  top: 411.63px;
  width: 636px;
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 19px;
  letter-spacing: 0.05em;
  text-align: center;
  color: var(--color-black-green-text);
  margin: 0;
  z-index: 3;
}

.courses__panel {
  position: absolute;
  left: 220px;
  top: 980.41px;
  width: 1000px;
  height: 429.38px;
  background: var(--color-light-green);
  opacity: 0.9;
  border-radius: 15px;
}

.courses__featured-wrap {
  position: absolute;
  left: 319.91px;
  top: 657.68px;
  width: 800.18px;
  height: 451.7px;
  border-radius: 15px;
  overflow: hidden;
  z-index: 3;
  display: grid;
}

.courses__featured-slide {
  grid-area: 1 / 1;
  position: relative;
  width: 100%;
  height: 100%;
  opacity: 0;
  visibility: hidden;
  transition: opacity 220ms ease, visibility 0s linear 220ms;
}

.courses__featured-slide.is-active {
  opacity: 1;
  visibility: visible;
  transition: opacity 220ms ease, visibility 0s;
}

.courses__featured {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.courses__featured-label {
  position: absolute;
  left: 33px;
  bottom: 26px;
  font-family: var(--font-display);
  font-size: 26px;
  line-height: 36px;
  text-transform: uppercase;
  text-align: center;
  color: var(--color-pale-green);
  opacity: 0.85;
  margin: 0;
}

/* The arrow SVGs already include their own pale-green circle background, so
   the button is just a positioned wrapper at the SVG's 36×36 size — no extra
   circle/bg here. Both SVGs ship with a left-pointing arrow, so the next
   button is flipped horizontally. */
.courses__nav {
  position: absolute;
  top: 1201px;
  width: 36px;
  height: 36px;
  padding: 0;
  background: none;
  z-index: 2;
  transition: opacity 0.15s ease;
}

.courses__nav:hover {
  opacity: 0.85;
}

.courses__nav img {
  display: block;
  width: 36px;
  height: 36px;
}

.courses__nav--prev {
  left: 340.4px;
}

/* Symmetric to prev: rightmost thumb ends at x=1040, mirror left-side spacing
   (thumb-edge to arrow-edge = 23.6px) → arrow left edge at x=1063.6. */
.courses__nav--next {
  left: 1063.6px;
}

.courses__nav--next img {
  transform: scaleX(-1);
}

.courses__thumbs {
  position: absolute;
  left: 400px;
  top: 1163.26px;
  width: 640.01px;
  overflow: hidden;
}

.courses__thumbs-track {
  display: flex;
  gap: 23.56px;
  transform: translateX(0);
  transition: transform 220ms ease;
  will-change: transform;
}

.course-thumb {
  width: 197.63px;
  flex-shrink: 0;
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
  font: inherit;
  color: inherit;
  text-align: center;
  display: block;
  transition: opacity 220ms ease;
}

.course-thumb:hover {
  opacity: 0.65;
}

.course-thumb img {
  width: 197.63px;
  height: 111.56px;
  object-fit: cover;
  border-radius: 10px;
  display: block;
}

.course-thumb__label {
  display: block;
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 18px;
  text-transform: uppercase;
  text-align: center;
  color: var(--color-dark-green);
  opacity: 0.85;
  margin: 12px 0 0;
}

.courses__dots {
  position: absolute;
  left: 50%;
  top: 1340px;
  transform: translateX(-50%);
  display: flex;
  gap: 11px;
  z-index: 3;
}

.courses__dot {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--color-medium-green);
  opacity: 0.6;
  border: none;
  padding: 0;
  cursor: pointer;
  transition: opacity 220ms ease, background-color 220ms ease;
}

.courses__dot:hover {
  opacity: 0.85;
}

.courses__dot.is-active {
  background: var(--color-dark-green);
  opacity: 1;
}

/* ============================================================
   MISC — duotone swing photo + 3 link cards
   ============================================================ */
.misc__canvas {
  height: 643px;
}

.misc__photo {
  position: absolute;
  left: 220px;
  top: 86.84px;
  width: 1000px;
  height: 346.79px;
  object-fit: cover;
  border-radius: 15px;
}

.misc__cards {
  position: absolute;
  left: 220px;
  top: 457px;
  width: 1000px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  column-gap: 50px;
}

.misc-card__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 26px;
  line-height: 36px;
  text-transform: uppercase;
  color: var(--color-dark-green);
  opacity: 0.85;
  margin: 0 0 10px;
}

.misc-card__body {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 19px;
  letter-spacing: 0.05em;
  color: var(--color-black-green-text);
  max-width: 273px;
  margin: 0;
}

.misc-card__link {
  color: var(--color-red);
  font-weight: 700;
  margin-left: 0.25em;
}

/* Shared text-link hover: same-color underline draws in left→right while the
   text dims slightly. background-image trick = a 1px tall gradient bar pinned
   to the bottom; animating background-size from 0% → 100% gives the draw-in. */
.misc-card__link,
.contact__email,
.contact__directions,
.site-footer__email {
  background-image: linear-gradient(currentColor, currentColor);
  background-position: 0 100%;
  background-repeat: no-repeat;
  background-size: 0% 1px;
  transition: background-size 220ms ease;
}

.misc-card__link:hover,
.contact__email:hover,
.contact__directions:hover,
.site-footer__email:hover {
  background-size: 100% 1px;
}

/* ============================================================
   TESTIMONIALS — wavy-masked seaside photo + carousel panel
   ============================================================ */
.testimonials__canvas {
  height: 1183px;
}

/* Mobile-only decorative pond behind the title. Hidden on
   desktop (desktop has its own pond treatment via the existing
   photo-wrap setup). Mobile media query unhides + positions it. */
.testimonials__pond {
  display: none;
}

.testimonials__photo-wrap {
  position: absolute;
  left: 320.18px;
  top: 78.42px;
  width: 799.93px;
  height: 607.41px;
  z-index: 2;
  -webkit-mask-image: url("assets/testimonials-mask.svg?v=2");
          mask-image: url("assets/testimonials-mask.svg?v=2");
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
}

.testimonials__photo {
  position: absolute;
  top: -15%;
  left: 0;
  width: 100%;
  height: 130%;
  object-fit: cover;
  will-change: transform;
  transform: translateY(var(--parallax-y, 0px));
}

.testimonials__panel {
  position: absolute;
  left: 220px;
  top: 631.38px;
  width: 1000px;
  height: 488.66px;
  background: var(--color-light-green);
  opacity: 0.9;
  border-radius: 15px;
  padding: 132px 50px 0;
  text-align: center;
}

.testimonials__title {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 36px;
  line-height: 36px;
  text-transform: uppercase;
  color: var(--color-dark-green);
  opacity: 0.85;
  margin: 0 0 28px;
}

.testimonials__slides {
  display: grid;
  min-height: 130px;
}

.testimonial {
  grid-area: 1 / 1;
  margin: 0;
  opacity: 0;
  visibility: hidden;
  transition: opacity 220ms ease, visibility 0s linear 220ms;
}

.testimonial.is-active {
  opacity: 1;
  visibility: visible;
  transition: opacity 220ms ease, visibility 0s;
}

.testimonial__quote {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 19px;
  letter-spacing: 0.05em;
  color: var(--color-black-green-text);
  max-width: 552px;
  margin: 0 auto 24px;
  /* Lock to 3 lines (the longest quote we have) so shorter quotes leave
     empty space below rather than pulling the name + arrows row up. Keeps
     the name aligned with the fixed-position arrows across all slides. */
  min-height: calc(19px * 3);
}

.testimonial__name {
  font-family: var(--font-display);
  font-size: 36px;
  line-height: 36px;
  text-transform: uppercase;
  color: var(--color-medium-green);
  opacity: 0.85;
  margin: 0 0 4px;
}

.testimonial__meta {
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 19px;
  letter-spacing: 0.05em;
  font-weight: 700;
  color: var(--color-red);
  margin: 0;
}

.testimonials__nav {
  position: absolute;
  top: 313px;
  width: 36px;
  height: 36px;
  padding: 0;
  background: none;
  transition: opacity 0.15s ease;
}

.testimonials__nav:hover {
  opacity: 0.85;
}

.testimonials__nav img {
  display: block;
  width: 36px;
  height: 36px;
}

/* Arrow midpoints sit on the panel's horizontal centre (x=500) so the
   row of arrow–name–arrow reads centred on the page. */
.testimonials__nav--prev {
  left: 280.7px;
}

.testimonials__nav--next {
  left: 683.3px;
}

.testimonials__nav--next img {
  transform: scaleX(-1);
}

.testimonials__dots {
  position: absolute;
  left: 50%;
  top: 388.66px;
  transform: translateX(-50%);
  display: flex;
  gap: 11px;
}

.testimonials__dot {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--color-medium-green);
  opacity: 0.6;
  transition: opacity 220ms ease, background-color 220ms ease;
}

.testimonials__dot:hover {
  opacity: 0.85;
}

.testimonials__dot.is-active {
  background: var(--color-dark-green);
  opacity: 1;
}

/* ============================================================
   CONTACT / SIGN OFF — layered: storefront, clubhouse,
   blue blob with text, address, map, big green blob CTA
   ============================================================ */
.contact__canvas {
  height: 1783px;
}

/* Signoff bg blob. Resized + repositioned per the updated Figma
   (12:485 → green3b-rotate 1): tighter footprint than before so
   it reads as a defined panel rather than spanning most of the
   lower section. */
.contact__green-blob {
  position: absolute;
  left: 169.4px;
  top: 1139.45px;
  width: 923.75px;
  height: 636.83px;
  pointer-events: none;
}

.contact__photo {
  position: absolute;
  left: 220px;
  top: 108.95px;
  width: 480px;
  height: 430px;
  border-radius: 15px;
  overflow: hidden;
}

/* Inner image fills the frame and gets a subtle scroll-driven zoom
   (--parallax-zoom set by initParallax). transform-origin center so it
   scales in place; overflow:hidden on the frame above contains it. */
.contact__photo-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(var(--parallax-zoom, 1));
  transform-origin: center;
  will-change: transform;
}

.contact__clubhouse {
  position: absolute;
  left: 786.18px;
  top: 122.44px;
  width: 441.82px;
  height: auto;
  pointer-events: none;
}

.contact__blue-blob {
  position: absolute;
  left: 631.27px;
  top: 311.4px;
  width: 520.84px;
  height: auto;
  pointer-events: none;
  z-index: 2;
}

.contact__blue-blob-text {
  position: absolute;
  left: 635.23px;
  top: 315.95px;
  width: 511.88px;
  height: auto;
  pointer-events: none;
  z-index: 3;
}

.contact__details {
  position: absolute;
  left: 282.92px;
  top: 632.55px;
  width: 300px;
  font-family: var(--font-body);
  font-style: normal;
  font-size: 16px;
  line-height: 19px;
  letter-spacing: 0.05em;
  text-align: center;
  color: var(--color-black-green-text);
}

.contact__email,
.contact__directions {
  font-size: 16px;
  line-height: 19px;
  font-weight: 700;
  color: var(--color-red);
}

/* Live Google Maps embed via the free iframe URL (no API key required).
   Sized and positioned per Figma's GOOGLE MAP INSET frame. */
.contact__map {
  position: absolute;
  left: 320px;
  top: 899.14px;
  width: 800px;
  height: 395px;
  border: 0;
  border-radius: 15px;
  box-shadow: inset 0 4px 4px rgba(12, 12, 13, 0.05);
  display: block;
}

.contact__title {
  position: absolute;
  left: 319px;
  top: 1400px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 50px;
  line-height: 48px;
  text-transform: uppercase;
  color: var(--color-pale-green);
  opacity: 0.85;
  margin: 0;
}

.contact__body {
  position: absolute;
  left: 319px;
  top: 1504.73px;
  width: 401px;
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 19px;
  letter-spacing: 0.05em;
  color: var(--color-black-green-text);
  margin: 0;
}

.contact__cta {
  position: absolute;
  left: 320px;
  top: 1569px;
}

/* ============================================================
   FOOTER
   ============================================================ */
.site-footer__canvas {
  height: 519px;
  color: var(--color-black-green-text);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 19px;
  letter-spacing: 0.05em;
}

.site-footer__logo-link {
  position: absolute;
  left: 246.28px;
  top: 112.65px;
  display: block;
  width: 250.2px;
}

.site-footer__logo {
  width: 100%;
  height: auto;
}

.site-footer__tagline {
  position: absolute;
  left: 270px;
  top: 195.8px;
  font-family: var(--font-display);
  font-size: 26px;
  line-height: 48px;
  text-transform: uppercase;
  color: var(--color-medium-green);
  opacity: 0.85;
  margin: 0;
}

.site-footer__links {
  position: absolute;
  top: 243.8px;
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.site-footer__links a {
  display: inline-block;
  padding: 0;
}

/* Column links get the same underline-draw + red hover as the
   top nav (and the email links, which keep their own red rule
   from the shared text-link block above). */
.site-footer__links a:not(.site-footer__email) {
  background-image: linear-gradient(var(--color-red), var(--color-red));
  background-position: 0 100%;
  background-repeat: no-repeat;
  background-size: 0% 1px;
  transition: color 220ms ease, background-size 220ms ease;
}

.site-footer__links a:not(.site-footer__email):hover {
  color: var(--color-red);
  background-size: 100% 1px;
}

.site-footer__links--col1 {
  left: 271.26px;
  width: 160px;
}

.site-footer__links--col2 {
  left: 465.5px;
  width: 160px;
}

.site-footer__email {
  color: var(--color-red);
  font-weight: 700;
  margin-top: 19px;
}

.site-footer__clubhouse {
  position: absolute;
  left: 725.6px;
  top: 193.52px;
  width: 420px;
  height: auto;
  display: block;
}

.site-footer__clubhouse img {
  width: 100%;
  height: auto;
  display: block;
}

.site-footer__copyright {
  position: absolute;
  left: 271.26px;
  top: 410.58px;
  font-size: 13px;
  line-height: 19px;
  margin: 0;
}

.site-footer__legal {
  position: absolute;
  right: 270px;
  top: 410.58px;
  display: flex;
  gap: 30px;
  list-style: none;
  font-size: 13px;
  line-height: 19px;
  padding: 0;
  margin: 0;
}

/* Bottom-row links (Legal, Terms + the temp My Account / Admin Login) get
   the same red underline-draw rollover as the column links and the top nav. */
.site-footer__legal a {
  background-image: linear-gradient(var(--color-red), var(--color-red));
  background-position: 0 100%;
  background-repeat: no-repeat;
  background-size: 0% 1px;
  transition: color 220ms ease, background-size 220ms ease;
}

.site-footer__legal a:hover {
  color: var(--color-red);
  background-size: 100% 1px;
}

/* Divider above bottom row (replaces imgLine2) */
.site-footer__canvas::before {
  content: "";
  position: absolute;
  left: 270px;
  top: 396px;
  width: 898.94px;
  height: 1px;
  background: var(--color-black-green-text);
  opacity: 0.5;
}

/* ============================================================
   Floating HELP widget — fixed bottom-right popover. Trigger is
   a circular button; tapping it expands a pale-green panel
   anchored above the trigger. The panel has a stack of screens:
   a main menu of 5 items and one detail screen per item. The
   back button in the header lets users return to the main menu;
   the close button collapses the whole panel.

   Mirrors the help widget in the booking app at hourgolf.ourlee.co
   so we can iterate on the brand styling here, then port the
   markup + CSS back into the React codebase.
   ============================================================ */
.help {
  position: fixed;
  right: 24px;
  bottom: 105px;
  z-index: 200;
}

/* Trigger — red circle with pale-green "?" mark. Uses the same
   two-face flip rollover as .btn: ::before is the front (red),
   ::after is the back (soft-green) initially translated below;
   on hover both slide up 101% so the back takes the front's
   place. Glyph lives on each face so it slides with the bg. */
.help__trigger {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 69px;
  height: 69px;
  border-radius: 50%;
  background: transparent; /* pseudo faces paint the bg */
  color: transparent; /* native span hidden; pseudos render the glyph */
  border: 0;
  cursor: pointer;
  overflow: hidden;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
  transition: transform 0.15s ease;
}

.help__trigger::before,
.help__trigger::after {
  content: "?";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 34px;
  line-height: 1;
  font-weight: 400;
  letter-spacing: 0;
  color: var(--color-pale-green);
  /* Optical tweak — display font's "?" sits low; nudge up
     so it reads centered in the circle on each face. */
  padding-bottom: 2px;
  transition: transform 220ms ease;
}

.help__trigger::before {
  background: #cbc060;
}

.help__trigger::after {
  background: #A6C0AE;
  transform: translateY(101%);
}

.help__trigger:hover::before { transform: translateY(-101%); }
.help__trigger:hover::after  { transform: translateY(0); }

.help__trigger:active {
  transform: translateY(1px);
}

/* Original glyph span hidden — pseudos render the "?" now. */
.help__trigger-icon {
  display: none;
}

/* Panel — pale-green floating card anchored above the trigger.
   Closed: scaled down + faded + non-interactive. Open: full size
   with shadow. The transform-origin: bottom right makes it look
   like it "blooms" out of the trigger button. */
.help__panel {
  position: absolute;
  right: 0;
  bottom: 76px;        /* trigger height 60 + 16 gap */
  width: 360px;
  max-width: calc(100vw - 48px);
  max-height: min(560px, calc(100vh - 120px));
  background: var(--color-pale-green);
  border-radius: 18px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.2);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  opacity: 0;
  transform: translateY(12px) scale(0.96);
  transform-origin: bottom right;
  transition: opacity 220ms ease, transform 220ms ease, visibility 0s linear 220ms;
  pointer-events: none;
  visibility: hidden;
}

.help.is-open .help__panel {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
  visibility: visible;
  transition: opacity 220ms ease, transform 220ms ease, visibility 0s;
}

/* Header — title + close. Back button shows only on detail
   screens (toggled via .help.is-on-detail). */
.help__panel-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 20px 20px 16px;
  border-bottom: 1px solid rgba(53, 68, 59, 0.12);
}

.help__panel-title {
  flex: 1;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 20px;
  line-height: 1.1;
  letter-spacing: 0;
  text-transform: uppercase;
  color: var(--color-black-green-text);
  margin: 0;
}

.help__back,
.help__close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  background: none;
  border: 0;
  color: var(--color-black-green-text);
  cursor: pointer;
  border-radius: 50%;
  opacity: 0.6;
  transition: opacity 220ms ease, background 220ms ease;
}

.help__back:hover,
.help__close:hover {
  opacity: 1;
  background: rgba(53, 68, 59, 0.08);
}

/* Back button hidden on the main screen, shown on detail. */
.help__back {
  display: none;
}

.help.is-on-detail .help__back {
  display: flex;
}

/* Screens container scrolls within the panel. */
.help__screens {
  position: relative;
  flex: 1;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

.help__screen {
  display: none;
  padding: 16px 16px 24px;
}

.help__screen.is-active {
  display: block;
}

/* Main menu list — pill-shaped items on light-green. */
.help__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.help__item {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 14px 18px;
  background: var(--color-light-green);
  border: 0;
  border-radius: 14px;
  color: var(--color-black-green-text);
  font-family: var(--font-body);
  font-size: 15px;
  letter-spacing: 0.05em;
  text-align: left;
  cursor: pointer;
  transition: background 220ms ease, transform 220ms ease;
}

.help__item:hover {
  background: var(--color-medium-green);
  color: var(--color-pale-green);
}

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

.help__item-icon {
  font-size: 20px;
  line-height: 1;
  flex-shrink: 0;
}

.help__item-label {
  flex: 1;
  font-weight: 500;
}

.help__item-chevron {
  font-family: var(--font-display);
  font-size: 22px;
  line-height: 1;
  flex-shrink: 0;
  opacity: 0.5;
}

/* Detail screen body. */
.help__detail {
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
  letter-spacing: 0.05em;
  color: var(--color-black-green-text);
}

.help__detail p {
  margin: 0 0 14px;
}

.help__detail p:last-child {
  margin-bottom: 0;
}

.help__email {
  color: var(--color-red);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ============================================================
   Accordion FAQ list — used inside category screens.
   Each item: question button (full-width) + collapsible answer.
   The answer slides open via max-height + opacity transition.
   Walkthrough launchers reuse the question row with a chevron
   instead of a + toggle and skip the answer panel entirely.
   ============================================================ */
.help__faq-list {
  display: flex;
  flex-direction: column;
}

.help__faq-item {
  border-bottom: 1px solid rgba(53, 68, 59, 0.12);
}

.help__faq-item:last-child {
  border-bottom: 0;
}

.help__faq-q {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 18px 4px;
  background: none;
  border: 0;
  color: var(--color-black-green-text);
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.05em;
  text-align: left;
  cursor: pointer;
  transition: color 220ms ease;
}

.help__faq-q > span:first-child {
  flex: 1;
}

.help__faq-q:hover {
  color: var(--color-dark-green);
}

.help__faq-toggle {
  font-family: var(--font-display);
  font-size: 22px;
  line-height: 1;
  color: var(--color-dark-green);
  flex-shrink: 0;
  transition: transform 220ms ease;
}

.help__faq-item.is-open .help__faq-q {
  color: var(--color-dark-green);
}

/* Swap the "+" glyph to "−" when open. The character itself stays
   the same; we just rotate so the vertical stroke disappears. */
.help__faq-item.is-open .help__faq-toggle {
  transform: rotate(45deg);
}

/* Walkthrough launcher rows use a "›" chevron, not a "+", and
   don't rotate. */
.help__faq-item--link .help__faq-toggle {
  transform: none !important;
}

.help__faq-a {
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transition: max-height 320ms ease, opacity 220ms ease, padding 220ms ease;
}

.help__faq-item.is-open .help__faq-a {
  /* Generous cap — answers run a few lines max. */
  max-height: 400px;
  opacity: 1;
  padding-bottom: 16px;
}

.help__faq-a p {
  margin: 0;
  padding: 0 4px;
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
  letter-spacing: 0.05em;
  color: var(--color-dark-green);
}

/* ============================================================
   Walkthrough — multi-step branching flow inside a single screen.
   Steps stack as children of the section; only one carries
   .is-active at a time.
   ============================================================ */
.help__screen--walkthrough {
  padding: 20px 16px 24px;
}

.help__step {
  display: none;
}

.help__step.is-active {
  display: block;
}

.help__step-label {
  margin: 0 0 14px;
  font-family: var(--font-body);
  font-size: 12px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-medium-green);
}

.help__step-title {
  margin: 0 0 14px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 20px;
  line-height: 1.15;
  letter-spacing: 0;
  text-transform: uppercase;
  color: var(--color-black-green-text);
}

.help__step-body {
  margin: 0 0 22px;
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
  letter-spacing: 0.05em;
  color: var(--color-dark-green);
}

.help__actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* ============================================================
   Buttons inside walkthrough steps and terminals.
   Primary = filled medium-green, secondary = outlined.
   Display font, uppercase, big touch target.
   ============================================================ */
.help__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  padding: 14px 20px;
  border-radius: 999px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 15px;
  line-height: 1;
  letter-spacing: 0;
  text-transform: uppercase;
  cursor: pointer;
  transition: background 220ms ease, color 220ms ease, border-color 220ms ease,
    transform 220ms ease;
}

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

.help__btn--primary {
  background: var(--color-medium-green);
  color: var(--color-pale-green);
  border: 1.5px solid var(--color-medium-green);
}

.help__btn--primary:hover {
  background: var(--color-dark-green);
  border-color: var(--color-dark-green);
}

.help__btn--secondary {
  background: transparent;
  color: var(--color-black-green-text);
  border: 1.5px solid var(--color-medium-green);
}

.help__btn--secondary:hover {
  background: rgba(139, 181, 160, 0.18);
}

/* For terminal screens, the close button is centered and narrower. */
.help__btn--inline {
  width: auto;
  align-self: center;
  padding: 12px 28px;
}

/* ============================================================
   Terminal screens — success ("You're all set!") and contact
   ("Let's get you help"). Centered emoji + heading + body.
   ============================================================ */
.help__screen--terminal {
  padding: 24px 16px;
}

.help__terminal {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 6px;
  padding-top: 12px;
}

.help__terminal-emoji {
  font-size: 56px;
  line-height: 1;
  margin-bottom: 12px;
}

.help__terminal-heading {
  margin: 0 0 4px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 18px;
  line-height: 1.25;
  letter-spacing: 0.05em;
  color: var(--color-black-green-text);
}

.help__terminal-body {
  margin: 0;
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.5;
  letter-spacing: 0.05em;
  color: var(--color-dark-green);
}

.help__terminal-phone {
  margin: 10px 0 4px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 30px;
  line-height: 1;
  letter-spacing: 0;
  color: var(--color-dark-green);
}

.help__terminal-phone a {
  color: inherit;
  text-decoration: none;
}

.help__terminal-phone a:hover {
  color: var(--color-medium-green);
}

.help__terminal .help__btn {
  margin-top: 16px;
}

/* When the mobile menu is open, hide the help widget so it
   doesn't fight with the menu overlay. */
body.is-menu-open .help {
  opacity: 0;
  pointer-events: none;
  transition: opacity 220ms ease;
}

/* ============================================================
   Account login modal — opened by the nav login icon. Compact
   card on a blurred green backdrop, matching the shop product
   modal recipe (rgba(53,68,59,.55) + blur(4px), white panel,
   18px radius, circular close). Entrance is a 220ms fade + rise
   so it never instant-toggles (house transition convention).
   ============================================================ */
.login-modal {
  position: fixed;
  inset: 0;
  z-index: 300; /* above nav (which lifts on scroll) + FABs */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  opacity: 0;
  transition: opacity 220ms ease;
}

.login-modal[hidden] {
  display: none;
}

.login-modal.is-open {
  opacity: 1;
}

.login-modal__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(53, 68, 59, 0.55);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.login-modal__panel {
  position: relative;
  width: 100%;
  max-width: 400px;
  background: var(--color-pale-green);
  border-radius: 18px;
  box-shadow: 0 24px 60px rgba(53, 68, 59, 0.25);
  padding: 44px 40px 36px;
  /* Rise into place; pairs with the wrapper's opacity fade. */
  transform: translateY(12px);
  transition: transform 220ms ease;
}

.login-modal.is-open .login-modal__panel {
  transform: translateY(0);
}

.login-modal__close {
  position: absolute;
  top: 16px;
  right: 16px;
  appearance: none;
  border: 0;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--color-white);
  color: var(--color-black-green-text);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 220ms ease, color 220ms ease;
}

.login-modal__close:hover {
  color: var(--color-red);
}

.login-modal__head {
  text-align: center;
  margin-bottom: 28px;
}

.login-modal__building {
  display: block;
  width: 300px;
  height: auto;
  margin: 0 auto 14px;
}

.login-modal__title {
  font-family: var(--font-display);
  font-size: 38px;
  line-height: 0.95;
  color: var(--color-dark-green);
  margin: 0 0 10px;
}

.login-modal__sub {
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.4;
  color: var(--color-black-green-text);
  opacity: 0.7;
  margin: 0;
}

.login-modal__form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.login-modal__field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.login-modal__label {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-black-green-text);
}

.login-modal__input {
  font-family: var(--font-body);
  font-size: 15px;
  color: var(--color-black-green-text);
  background: var(--color-white);
  border: 1px solid transparent;
  border-radius: 8px;
  padding: 12px 14px;
  width: 100%;
  transition: border-color 220ms ease, background-color 220ms ease;
}

.login-modal__input::placeholder {
  color: var(--color-medium-green);
}

.login-modal__input:focus {
  outline: none;
  border-color: var(--color-dark-green);
}

/* Both modal links borrow the top-nav link vocabulary: a 1px red
   underline pinned to the baseline that grows 0%→100% on hover
   while the text flips to red. No underline at rest. Some are <button>
   (view toggles), so each rule resets button chrome but keeps the
   gradient underline (don't use the `background` shorthand — it would
   wipe background-image). */
.login-modal__forgot {
  align-self: flex-end;
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  background-color: transparent;
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 700;
  color: var(--color-dark-green);
  text-decoration: none;
  margin-top: -4px;
  background-image: linear-gradient(var(--color-red), var(--color-red));
  background-position: 0 100%;
  background-repeat: no-repeat;
  background-size: 0% 1px;
  transition: color 220ms ease, background-size 220ms ease;
}

.login-modal__forgot:hover {
  color: var(--color-red);
  background-size: 100% 1px;
}

/* Inline auth error — sits above the submit button. */
.login-modal__error {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.4;
  color: var(--color-red);
  background: rgba(201, 47, 31, 0.08);
  border-radius: 8px;
  padding: 10px 12px;
  margin: 0;
}

.login-modal__error[hidden] {
  display: none;
}

/* Inline success (e.g. "reset link sent"). Green counterpart to the error. */
.login-modal__success {
  font-family: var(--font-body);
  font-size: 13px;
  line-height: 1.4;
  color: var(--color-dark-green);
  background: rgba(76, 141, 115, 0.1);
  border-radius: 8px;
  padding: 10px 12px;
  margin: 0;
}

.login-modal__success[hidden] {
  display: none;
}

/* Full-width version of the standard flip button. */
.login-modal__submit {
  height: 46px;
  width: 100%;
  font-size: 15px;
  border-radius: var(--button-radius);
  margin-top: 4px;
}

/* Submitting state — dim + lock the button while the request is in flight. */
.login-modal__submit:disabled {
  opacity: 0.6;
  pointer-events: none;
}

.login-modal__foot {
  font-family: var(--font-body);
  font-size: 14px;
  text-align: center;
  color: var(--color-black-green-text);
  margin: 24px 0 0;
}

.login-modal__foot-link {
  appearance: none;
  -webkit-appearance: none;
  border: 0;
  background-color: transparent;
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--color-dark-green);
  font-weight: 700;
  text-decoration: none;
  background-image: linear-gradient(var(--color-red), var(--color-red));
  background-position: 0 100%;
  background-repeat: no-repeat;
  background-size: 0% 1px;
  transition: color 220ms ease, background-size 220ms ease;
}

.login-modal__foot-link:hover {
  color: var(--color-red);
  background-size: 100% 1px;
}

@media (prefers-reduced-motion: reduce) {
  .login-modal,
  .login-modal__panel {
    transition: none;
  }
  .login-modal__panel {
    transform: none;
  }
}

@media (max-width: 500px) {
  .login-modal__panel {
    padding: 40px 26px 30px;
  }
  .login-modal__title {
    font-size: 32px;
  }
}

/* ============================================================
   Sticky BOOK NOW button — primary CTA stacked above the HELP
   trigger in the bottom-right. Same 60px diameter as HELP; the
   12px gap puts its bottom at HELP's top + 12 (24 bottom + 60
   height + 12 gap = 96). Same two-face flip rollover as .btn /
   .help__trigger: BOOKNOW.svg rides each face as a background
   image so the text slides with the bg color.
   ============================================================ */
.book-now-fab {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 200;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 69px;
  height: 69px;
  border-radius: 50%;
  overflow: hidden;
  background: transparent;
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.125);
  text-decoration: none;
  transition: transform 0.15s ease;
}

.book-now-fab::before,
.book-now-fab::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url("assets/BOOKNOW.svg");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 72% auto;
  transition: transform 220ms ease;
}

.book-now-fab::before {
  background-color: var(--color-red);
}

.book-now-fab::after {
  background-color: #A6C0AE;
  transform: translateY(101%);
}

.book-now-fab:hover::before { transform: translateY(-101%); }
.book-now-fab:hover::after  { transform: translateY(0); }

.book-now-fab:active {
  transform: translateY(1px);
}

/* Original <img> hidden — pseudos render the SVG now. */
.book-now-fab__label {
  display: none;
}

/* No mobile right-margin override — both FABs stay at 24px from
   the right edge across all viewports, matching the desktop
   corner-hug. */

/* Hide when the mobile menu is open (matches HELP behavior). */
body.is-menu-open .book-now-fab {
  opacity: 0;
  pointer-events: none;
  transition: opacity 220ms ease;
}

/* ============================================================
   Narrow-desktop band (1024–1199px)
   ============================================================
   The mobile system now owns everything ≤1023 (its --canvas-width
   clamp scales up to 1023), so this band only ever scales the 1024
   design UPWARD toward 1199 — no squeeze below the anchor.
   Re-anchors the page to David's 1024 Figma frame
   (`BACKGROUND LT GREEN`, node 264:23). Same strategy as the
   mobile block: swap the canvas/content-inset tokens, then
   per-element overrides at the 1024 coords. Canvas fills the
   viewport (capped 1199) so absolutely-positioned children
   centre on the viewport via left:50%/translateX or sit at the
   60px gutter via --content-inset. CSS-only (no zoom) so the
   nav theme switcher, parallax and reveals are untouched.

   Token overrides:
     --canvas-width:  1199px cap (canvas = viewport content width)
     --content-inset: 60px (the 1024 frame's gutter, was 220)
   Fluid widths use calc(100% - 120px) = canvas minus both 60px
   gutters (canvas-relative, so no 100vw scrollbar overflow).
   ============================================================ */
@media (min-width: 1024px) and (max-width: 1199px) {
  :root {
    --canvas-width: 1199px;
    --content-inset: 60px;
  }

  /* Canvas fills the viewport content box, capped at the band top. */
  .canvas {
    width: 100%;
    max-width: var(--canvas-width);
  }

  /* ---- COURSES (290:1339) ---------------------------------- */
  .courses__canvas { height: 1146px; }

  /* Sand texture spans the section; pond decal is absent in the
     1024 frame, so drop it. */
  .courses__sandtrap {
    left: 50%;
    transform: translateX(-50%);
    width: max(1027.58px, 100%);
    top: 52px;
  }
  .courses__pond { display: none; }

  .courses__icon {
    left: 50%;
    transform: translateX(-50%);
    top: 59.13px;
  }
  .courses__title { top: 190.06px; }
  .courses__body {
    left: 50%;
    transform: translateX(-50%);
    top: 273.95px;
    width: min(636px, calc(100% - 120px));
  }
  .courses__featured-wrap {
    left: 50%;
    transform: translateX(-50%);
    top: 384.47px;
    width: min(800.18px, calc(100% - 120px));
  }
  .courses__panel {
    left: 50%;
    transform: translateX(-50%);
    top: 707.41px;
    width: min(904px, calc(100% - 120px));
    height: 380.63px;
  }
  /* Carousel internals keep their Figma dimensions (thumb 197.63,
     gap 23.56 → STEP 221.19 identical to desktop), so script.js
     needs no change — we only re-centre the cluster. */
  /* Nav cluster moved up ~34px: closer to the featured image above,
     more breathing room under the dots. */
  .courses__thumbs {
    left: 50%;
    transform: translateX(-50%);
    top: 856.26px;
    width: 640.01px;
  }
  .courses__nav { top: 894px; }
  .courses__nav--prev { left: calc(50% - 379.6px); }
  .courses__nav--next { left: calc(50% + 343.6px); }
  .courses__dots { top: 1026px; }

  /* ---- PRO SHOP top (290:1230) ----------------------------- */
  /* Full-bleed photo stays; widen the centred content box from the
     desktop 0 30% (which over-squeezes at 1024) to the 60px gutter
     so the header/body match the frame's width. */
  .proshop-top__content {
    padding: 0 var(--content-inset);
  }

  /* ---- PRO SHOP bottom (290:1313) -------------------------- */
  /* Bg already bleeds 100vw. Title left-inset, subtitle right-
     inset, card track padded to the gutter. The live .product-card
     carousel is already JS-injected here — that satisfies the
     frame's "USE THE CARDS FROM THE HOMEPAGE" note (290:1319). */
  .proshop-bottom__canvas { height: 624px; }
  .proshop-bottom__title {
    left: var(--content-inset);
    top: 72px;
  }
  /* 25% smaller than the desktop 26px so it clears the title at 1024
     (at 26px the two ran into each other). Top nudged 75 → 77 to keep
     the subtitle's baseline on the title's. */
  .proshop-bottom__subtitle {
    left: auto;
    right: var(--content-inset);
    top: 77px;
    font-size: 19.5px;
  }
  .proshop-bottom__track {
    padding-left: var(--content-inset);
    padding-right: var(--content-inset);
  }

  /* ---- MISC (290:1364) ------------------------------------- */
  /* Fixed + centered (no flex). Canvas pinned to 1197 (the .wrap centers
     + clips it); photo + cards fixed at 904, centered → 146.5px margins.
     Cards keep the desktop 3-col grid (gap 50). */
  .misc__canvas {
    width: 1197px;
    max-width: none;
    /* 610 (was 567) so the long Lessons copy isn't clipped by the wrap. */
    height: 610px;
  }
  .misc__photo {
    left: 146.5px;
    top: 67.92px;
    width: 904px;
    height: 313.49px;
  }
  .misc__cards {
    left: 146.5px;
    top: 406px;
    width: 904px;
  }

  /* ---- TOP NAV (290:1430) ---------------------------------- */
  /* Nav already self-adjusts: its padding/gap/margins are driven by
     --canvas-overhang (still live in the band), resolving to ~50px
     gutters + 20px gaps at 1024 — which matches the frame (logo x41,
     Book pill x854). No override needed. */

  /* ---- HERO (290:1152) ------------------------------------- */
  .hero { height: 666px; }
  .hero__eyebrow { left: 180px; top: 262px; }
  .hero__title {
    left: 180px;
    top: 320px;
    white-space: normal;
    max-width: min(405px, calc(100% - 240px));
  }
  .hero__actions { left: 180px; top: 461px; }

  /* ---- INTRO (290:1166) ------------------------------------ */
  /* Variation A (centred flow) already adapts; just cap widths to the
     frame so the title/body don't run too wide near 1199. */
  .intro__title { max-width: 744px; }

  /* ---- NERDS (290:1174) ------------------------------------ */
  /* 972 → 896: trims 76px of empty tail below the blob, which is 40% of
     the 189px gap that sat between the blob and the memberships invite
     card. The blob's bottom offset drops by the same 76 so it keeps its
     exact position relative to the copy. */
  .nerds__canvas { height: 896px; }
  /* Body's 38px bottom margin halved — the only thing between the copy
     and the stats row, so this moves the red values + their labels up
     19px and leaves the title and body where they are. */
  .nerds__body { margin-bottom: 19px; }
  /* Anchored by its BOTTOM (5px up from the canvas end) instead of its
     top. The blob is width-capped to the canvas, so its height changes
     across the band — bottom-anchoring keeps the stats sitting well
     inside the green at every width and drops the top edge so it
     overlaps the photo less. */
  /* Fixed size through the whole band: pinned to the width it renders at
     1199 (max-width: none defeats the global img cap that was shrinking
     it with the viewport). Below 1199 the right side simply crops off —
     intended, so the shape never rescales mid-band. */
  .nerds__blob {
    left: 20.8px;
    top: auto;
    bottom: 5px;
    width: 1199px;
    max-width: none;
  }
  /* Photo spans the same margins as the copy below it (179 each side)
     rather than holding a fixed 664 while the gutters grow. Height is
     unchanged, so the wider box scales the image up via object-fit:
     cover instead of letterboxing it. */
  .nerds__photo-frame {
    left: 179px;
    right: 179px;
    transform: none;
    top: 74.64px;
    width: auto;
    height: 400.79px;
  }
  /* Photo fills the shorter frame via cover (the desktop -38px centre
     offset was tuned for the 1000/603 frame). */
  .nerds__photo {
    height: 100%;
    object-fit: cover;
    transform: translateY(var(--parallax-y, 0px));
  }
  .nerds__content {
    left: 179px;
    top: 512.31px;
    width: min(560px, calc(100% - 120px));
  }

  /* ---- MEMBERSHIPS (513:40 — 1024–1199 FIXED layout) --------
     David's 1197 frame is PINNED at a fixed size and centered — it does
     NOT scale or reflow anywhere in the band. The canvas is locked to
     1197px (flex-shrink:0 + the .wrap's justify-content:center +
     overflow:hidden keep it centered and clip the empty side gutters
     below 1197). Every box is a fixed px from the frame; no min()/clamp/
     100%-math. Layout only — buttons + copy are the shared build's. */
  .memberships__canvas {
    width: 1197px;
    max-width: none;
    height: 1216px;
  }
  /* Pond decoration isn't in the frame for this band. */
  .memberships__pond { display: none; }
  .memberships__wallpaper {
    left: 115px;
    top: 155px;
    width: 967px;
    height: 485px;
    border-radius: 20px;
    /* clip-path clips the parallax-transformed <img> to the rounded box in
       BOTH Chrome and Safari (border-radius+overflow does not clip a
       transformed child reliably). */
    clip-path: inset(0 round 20px);
  }
  .memberships__wallpaper-photo {
    /* Oversized (~100px of margin above/below the box) so the parallax
       translate never uncovers the box — same tactic as the desktop rule.
       Corners come from the container's clip-path, not here. */
    left: 0;
    top: -100px;
    width: 100%;
    height: calc(100% + 200px);
    object-fit: cover;
    max-width: none;
    will-change: transform;
    transform: translateY(var(--parallax-y, 0px));
  }
  .memberships__invite {
    left: 145px;
    top: 108px;
    width: 388px;
    height: 569px;
  }
  .memberships__title {
    left: 596px;
    top: 237px;
    width: 422px;
  }
  /* MEMBERSHIPS INCLUDE — frosted panel over the wallpaper (desktop
     .memberships__all-bg + content kept). */
  .memberships__all {
    left: 567px;
    top: 401px;
    width: 432px;
    height: 184px;
  }
  /* "MEMBERSHIPS INCLUDE:" on one line inside the 432px panel (real Biden
     renders wider than the Figma preview, so 24px matches the frame). */
  .memberships__all-title {
    font-size: 24px;
    margin-bottom: 10px;
  }
  /* Center the include block vertically in the 184px box (was top-aligned
     with 32px padding, which overflowed the bottom). */
  .memberships__all-content {
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding-top: 0;
    padding-bottom: 0;
  }
  /* Single pale-green panel behind the 3 plan columns. Fixed box +
     Figma 37px inset / 32px gap → 254px columns, so "GREEN JACKET."
     always sits on one line at the desktop 26px title size. */
  .memberships__plans {
    left: 149px;
    top: 612px;
    width: 900px;
    height: 480px;
    padding: 98px 37px 0;
    gap: 32px;
  }
  /* Footnote + divider, inset inside the panel. */
  .memberships__footnote {
    left: 188px;
    top: 926px;
    width: 816px;
  }
  /* NON MEMBERS — full-width row inside the panel; desktop min-width:1024
     3-col grid aligns it to the plan columns (gap matched to 32px). */
  .memberships__nonmembers {
    left: 186px;
    right: auto;
    top: 999px;
    width: 826px;
    column-gap: 32px;
    /* Drop the desktop 50px inset so the heading aligns left under PATRON
       and the copy under STARTER. */
    padding: 0;
  }

  /* ---- TECHNOLOGY (1024–1199 — FIXED, centered) -----------
     No flex: the photo + copy keep fixed sizes and sit centered as a
     group with the desktop's 55px photo↔copy gap. Canvas pinned to 1197
     (the .wrap centers + clips it), same tactic as the memberships band.
     Group = 450 + 55 + 460 = 965, centered → 116px margins. */
  .technology__canvas {
    width: 1197px;
    max-width: none;
    height: 600px;
  }
  .technology__photo-wrap {
    left: 116px;
    top: 138.37px;
    width: 450px;
    height: 448.43px;
    max-width: none;
  }
  .technology__content {
    left: 621px;
    right: auto;
    top: 158.19px;
    width: 460px;
    max-width: none;
  }

  /* ---- GIFT CARDS (1024–1199 — FIXED, centered) ----------
     The desktop composition fit to the band: a wide photo centered with
     equal margins, copy overlaid left, tree poking out top-right. Canvas
     pinned to 1197 (the .wrap centers + clips it), no flex. */
  .gift-cards__canvas {
    width: 1197px;
    max-width: none;
    height: 664px;
  }
  /* Photo margins matched to the technology photo above (116 each side,
     so 1197 - 232 = 965 wide). Copy + tree shift with it so their inset
     from the photo edges is unchanged. */
  .gift-cards__photo-wrap {
    left: 116px;
    top: 108.65px;
    width: 965px;
    height: 495.62px;
    max-width: none;
  }
  .gift-cards__title {
    left: 296px;
    top: 249.48px;
    width: 520px;
  }
  .gift-cards__body {
    left: 296px;
    top: 369.76px;
    width: 437px;
  }
  .gift-cards__cta { left: 296px; top: 442.98px; }
  /* Tree keeps its original base position (bottom ~330) but is 15% smaller
     (328->279 wide), so its top lands ~74px up — poking above the photo
     into the section gap without reaching the tech copy above. Left nudged
     +25 to keep the trunk over the same spot as it narrows. */
  .gift-cards__tree {
    left: 854px;
    top: -74px;
    width: 279px;
  }

  /* ---- TESTIMONIALS (290:1372) ----------------------------- */
  /* Photo ~20% shorter; panel moved up to keep its ~50px overlap; the
     three internal vertical gaps (above title, name->dots, below dots)
     each halved; canvas tightened to match. */
  .testimonials__canvas { height: 998px; }
  .testimonials__photo-wrap {
    left: 50%;
    transform: translateX(-50%);
    top: 73.46px;
    width: min(800px, calc(100% - 120px));
    height: 486px;
  }
  .testimonials__panel {
    left: 50%;
    transform: translateX(-50%);
    top: 510px;
    width: min(664px, calc(100% - 120px));
    /* padding-top 90 = the ~49px it tucks under the photo + a halved
       (~41px) visible gap above the title. Height 373 → 401 puts the
       full (un-halved) 56px of green back under the dots; the canvas
       grew to match so the gap to the next section is unchanged. */
    height: 401px;
    padding-top: 90px;
  }
  .testimonials__nav { top: 271px; }
  .testimonials__nav--prev { left: 126px; }
  .testimonials__nav--next { left: 528px; }
  .testimonials__dots { top: 325px; }

  /* ---- CONTACT / SIGN-OFF (290:1394) ----------------------- */
  /* Fixed + centred (no fluidity) — the desktop 1440 composition
     shrunk as ONE GROUP to k = 0.88 so it fits 1024 with ~46px
     margins. Every graphic (photo, clubhouse, pond blob + its text,
     map, green blob) keeps its desktop size + position RELATIVE to
     the others; only the group scale changes. Copy is NOT scaled —
     details / title / body / CTA keep desktop type sizes and widths,
     so the text stack keeps its desktop vertical rhythm and only its
     anchor point moves with the group.
     Canvas pinned to 1197 (the .wrap centres + clips it); group
     origin x = 132.7 (= (1197 - 1058.6 * 0.88) / 2). */
  .contact__canvas {
    width: 1197px;
    max-width: none;
    /* Tall enough to leave an 80px breather between the green blob and
       the footer edge (desktop leaves ~7px, which read as touching). */
    height: 1596px;
  }
  .contact__photo {
    left: 177.2px;
    top: 95.83px;
    width: 422.4px;
    height: 378.4px;
  }
  .contact__clubhouse {
    left: 675.48px;
    right: auto;
    top: 107.71px;
    width: 388.78px;
  }
  .contact__blue-blob {
    left: 539.17px;
    top: 274.03px;
    width: 458.34px;
  }
  .contact__blue-blob-text {
    left: 542.6px;
    top: 277.99px;
    width: 450.47px;
  }
  /* Address block: desktop width + type size kept, centre-point moved
     with the group (desktop centre 432.9 → 364.6). */
  .contact__details {
    left: 214.6px;
    top: 556.6px;
    width: 300px;
  }
  .contact__map {
    left: 265.2px;
    transform: none;
    top: 791.2px;
    width: 704px;
    height: 347.6px;
  }
  /* Blob + signoff stack move up together by 46.6 — half the old gap
     between the map's bottom edge and the title (93.2 → 46.6). */
  .contact__green-blob {
    left: 132.7px;
    top: 956.07px;
    width: 812.94px;
    height: 560.38px;
  }
  /* Signoff stack: anchored at the scaled title position, then the
     desktop gaps (104.7 / 64.3) added unscaled so the full-size type
     can't collide. */
  .contact__title {
    left: 264.3px;
    top: 1185.4px;
    width: auto;
  }
  .contact__body {
    left: 264.3px;
    top: 1290.1px;
    width: 401px;
  }
  .contact__cta { left: 265.2px; top: 1354.4px; }

  /* ---- FOOTER (290:1412) ----------------------------------- */
  /* Frame left-aligns the footer at the 60px gutter (desktop centres
     it). Clubhouse + legal anchor to the right gutter. */
  .site-footer__logo-link { left: 40px; top: 18.88px; }
  .site-footer__tagline { left: 64px; top: 102px; }
  .site-footer__links { top: 150px; }
  .site-footer__links--col1 { left: 65px; }
  .site-footer__links--col2 { left: 259px; }
  .site-footer__clubhouse {
    left: auto;
    right: var(--content-inset);
    top: 99.75px;
    width: min(466px, calc(100% - 540px));
  }
  .site-footer__copyright { left: 65px; top: 316.8px; }
  .site-footer__legal { right: var(--content-inset); top: 316.8px; }
  /* Divider spans the full content width: from the copy column (64) to
     the right gutter the legal row + clubhouse sit on. Was capped at the
     desktop 898.94 by a min(), so it stopped ~160px short at 1199. */
  .site-footer__canvas::before {
    left: 64px;
    right: var(--content-inset);
    top: 302.4px;
    width: auto;
  }
}

/* ============================================================
   Mobile layout (≤768px)
   ============================================================
   Single-HTML strategy: keep the desktop DOM, swap layout via
   media query. Mobile Figma frame is 430-wide (`BACKGROUND LT
   GREEN mobile`, node 111:2). We re-anchor children inside
   `.canvas` / section roots using Figma's mobile pixel coords.

   Token overrides cascade:
     --canvas-width: 430px (was 1440)
     --content-inset:  30px (was 220)
   so any rule using `left: var(--content-inset)` or
   `width: var(--canvas-width)` re-anchors automatically.

   `.canvas` is capped (max-width 430), not fixed — on phones
   narrower than 430 the canvas fits the viewport instead of
   overflowing, and left-anchored content stays visible.
*/
@media (max-width: 1023px) {
  :root {
    /* Smoothly scale from the 430-wide phone design up to 768.
       Sub-430 hits the floor so the existing phone view stays
       pixel-identical; above 430 the canvas + gutters grow with
       viewport so content doesn't strand at 430-center when the
       breakpoint fires on a small tablet / resized desktop.
       Two gutter scales: logo-inset (nav outer, tighter) and
       content-inset (body content, looser) — matches the "logo
       margin tighter than content margin" proportion. */
    --canvas-width: clamp(430px, 100vw, 1023px);
    --content-inset: clamp(30px, calc(30px + (100vw - 430px) * 0.148), 80px);
    --logo-inset: clamp(30px, calc(30px + (100vw - 430px) * 0.059), 50px);
    /* Contact photo grows modestly from 385 (at 430) to 500
       (at 768) — was a more aggressive 688 to match the desktop
       480/430 aspect ratio, but that blew up the section. The
       new growth keeps it visually closer to a "wider banner"
       shape (aspect ~1.53) which feels right at 768. Everything
       absolutely positioned below the photo adds this shift
       to its top so the layout stays clean as the photo grows. */
    --contact-shift: clamp(0px, calc((100vw - 430px) * 0.34), 115px);
  }

  /* Canvas adapts to viewport up to the 430-wide mobile design. */
  .canvas {
    width: 100%;
    max-width: var(--canvas-width);
  }

  /* TOP NAV — desktop links + Book button hide on mobile; the
     login icon and hamburger reveal in their place per Figma
     mobile (111:330). Logo stays left-anchored. Scoped to
     `.top-nav__inner` so the menu's own Book Now CTA isn't
     caught by this rule. */
  .top-nav__links,
  .top-nav__inner > .btn {
    display: none;
  }

  /* Log out is desktop-only. The mobile nav is a designed three-element row
     (logo, account icon, hamburger) with no room for a text control — it was
     wrapping to two stacked lines between the icon and the burger. Needs the
     [data-auth="in"] selector to outrank the rule that reveals it.
     NOTE: this leaves phone users with no way to end a session on this host
     (the cookie is host-scoped to hour.golf and the portal's logout lives on
     ourlee.co). The fix, when we want it, is a logout row inside the
     hamburger menu rather than in the nav strip. */
  .top-nav[data-auth="in"] .top-nav__logout {
    display: none;
  }

  /* With Log out hidden the icon is no longer half of a pair, so it takes the
     full row gap back rather than sitting a third of the way off the burger. */
  .top-nav__account {
    margin-right: var(--nav-gap);
  }

  /* Mobile nav: outer gutter scales with --logo-inset (30 at
     430-viewport, 50 at 768-viewport). Logo gets an additional
     negative margin so its bounding box pulls roughly halfway
     toward the viewport edge to compensate for the script logo's
     optical inset (its left-most letterform reads further in than
     the bbox edge). Login + hamburger are unaffected — they're
     right-anchored via margin-left: auto. */
  .top-nav__inner {
    padding: 0 var(--logo-inset);
  }

  .top-nav__logo-link {
    margin-left: -26px;
  }

  /* Logo: 201×51 per Figma. */
  .top-nav__logo {
    width: 201px;
    height: 51px;
  }

  /* NOTE: the desktop/mobile line-break swap (.break-desktop,
     .break-mobile, .line--desktop, .line--mobile) used to live in this
     ≤1023 block. It now sits in its own ≤768 block further down, so
     769–1023 keeps the DESKTOP break structure — at ~1000px the phone
     breaks were splitting headlines into 4 short lines with room to
     spare. */

  /* Login + hamburger pushed right; small gap between them
     matches Figma (login ends ~349, hamburger starts ~369).
     Hamburger sits at the logo's optical baseline (margin-top
     -6). Login is 7px taller than the hamburger so its bbox
     centerline sits lower — extra -2 lifts its bottom edge to
     match the hamburger's bottom. Drop the desktop margin-right
     since the hamburger's margin-left handles the gap here. */
  .top-nav__login,
  .top-nav__account {
    margin-left: auto;
    margin-right: 0;
    margin-top: -8px;
  }

  .top-nav__hamburger {
    display: flex;
    margin-left: 18px;
    margin-top: -6px;
  }

  /* HERO --------------------------------------------------- */
  .hero__inner {
    max-width: var(--canvas-width);
  }

  .hero__eyebrow {
    top: 292px;
    max-width: 200px;
  }

  /* Mobile title: 45/43 per Figma, wraps to 3 lines. Span 1
     ("It's tee time,") gets an internal break-mobile to split
     into "It's tee" / "time,"; span 2 "all the time." stays on
     one line. Drop desktop's nowrap so the wrap actually fires. */
  .hero__title {
    top: 345px;
    font-size: 45px;
    line-height: 43px;
    white-space: normal;
    max-width: 280px;
  }

  /* Hero photo positioning math:
       Source: 2400×1381 (aspect 1.738)
       Box (mobile): 430×963 (430 wide × 130% of 741 hero height)
       object-fit: cover height-fits → image renders 1674×963
       Horizontal excess: 1674 − 430 = 1244
     Lower % = window sits further LEFT on the source image,
     so the photographed content (flag, green) shifts further
     RIGHT in the viewport. At 768 the box is wider so the
     same % produces a smaller visual shift; phone view (≤430)
     therefore gets a separate, deeper shift below. */
  .hero__photo {
    object-position: 56% center;
  }

  .hero__actions {
    top: 489px;
    gap: 16px;
  }

  /* INTRO -------------------------------------------------- */
  /* Figma mobile: icon top 57, title top 198.83, body top 364.11,
     section height 614 → padding 57 top / ~99 bottom. */
  .intro {
    padding: 85.5px 30px 74.25px;
  }

  .intro__icon {
    width: 54px;
    margin-bottom: 23px;
  }

  /* 4-line title on mobile: "LET'S JUST / admit it, / we're ALL /
     GOLF ADDICTS." Two `.line` spans, each split mid-span by an
     activated `<br class="break-mobile">`. */
  .intro__title {
    font-size: 40px;
    line-height: 38px;
    margin-bottom: 13px;
  }

  .intro__body {
    font-size: 14px;
    line-height: 18px;
    max-width: 300px;
  }

  /* NERDS -------------------------------------------------- */
  /* Mobile layout: full-width hallway photo on top (407 tall),
     dark-green panel below carrying title + body + 3-col stat
     row. Section height 902 per Figma frame 111:35. */
  .nerds__canvas {
    height: 902px;
  }

  /* Photo inset to the --content-inset gutter (30 at 430, ~80 at
     768) so its edges align with the hero copy above and the
     section text panel below. 15px rounded corners match the
     desktop framed-image vocabulary; parallax is wired via
     [data-parallax] on the frame in HTML. */
  .nerds__photo-frame {
    left: var(--content-inset);
    top: 0;
    width: calc(100% - 2 * var(--content-inset));
    height: 407px;
    border-radius: 15px;
  }

  /* Photo taller than its 407 frame so parallax has range to
     animate against (desktop pattern: top:-15% height:130%). */
  .nerds__photo {
    top: -15%;
    height: 130%;
    object-fit: cover;
    object-position: center;
    transform: translateY(var(--parallax-y, 0px));
  }

  /* Desktop blob has a different shape — hide it on mobile and
     paint the mobile-specific blob via ::before instead. */
  .nerds__blob {
    display: none;
  }

  /* Mobile blob bg: organic pond-shaped duotone fill at 1437×760
     positioned per Figma (left:-170, top:100). Sits behind text
     content so the pale-green title/body/stats become readable. */
  .nerds__canvas::before {
    content: "";
    position: absolute;
    left: -170px;
    top: 100px;
    width: 1437px;
    height: 760px;
    background: url('/_next/image?url=%2Fhg%2Fassets%2Fnerds-mobile-bg.png&w=1920&q=75') no-repeat center / 100% 100%;
    pointer-events: none;
    z-index: 0;
  }

  /* Content block flows from y=456; left/right edges share the
     same --content-inset gutter as the photo above so the
     title, body, and 3-stat row line up vertically with the
     photo's edges. Phone (430) resolves to 30 — matching the
     first-pass mobile layout. */
  .nerds__content {
    left: var(--content-inset);
    top: 456px;
    right: var(--content-inset);
    width: auto;
  }

  .nerds__title {
    font-size: 43px;
    line-height: 41px;
    margin-bottom: 24px;
  }

  .nerds__body {
    font-size: 14px;
    line-height: 18px;
    margin-bottom: 24px;
  }

  /* 3 stats spread edge-to-edge instead of desktop's 80px gap. */
  .nerds__stats {
    gap: 0;
    justify-content: space-between;
  }

  /* Tighter gap between each red stat value and its label below
     (8 → 4, half the desktop value) so the number + caption read
     as a single unit. */
  .nerds__stat {
    gap: 4px;
  }

  /* MEMBERSHIPS -------------------------------------------- */
  /* Mobile layout (section height 1356 — Figma's 1306 + 50px to
     make room for the 3-line ALL MEMBERSHIPS INCLUDE title):
       0   – 339  YOU'RE INVITED title + wallpaper + invite card
       339 – 639  ALL MEMBERSHIPS INCLUDE panel (300 tall, was 250)
       639 –1356  Plans + footnote + non-members (everything below
                  the green panel shifted +50 to accommodate)
     Desktop's 3-column plan grid collapses to a vertical stack;
     each plan card flips to a 2-column row (title+bullets on
     left, button+price on right). */
  .memberships__canvas {
    /* --wp-h is the fluid wallpaper height. Reused below so every
       element under the wallpaper tracks its bottom edge cleanly —
       no overlap between wallpaper and the medium-green panel. */
    --wp-h: clamp(304px, calc(304px + (100vw - 430px) * 0.136), 350px);
    height: calc(1119px + var(--wp-h));
  }

  /* Pond is a desktop-only decorative graphic. */
  .memberships__pond {
    display: none;
  }

  /* Top-section wallpaper: full-width band y=40-344 at phone,
     fluid-scales +15% taller by 768 (304→350) so the photo
     reads more cinematic at the wider viewport. Height uses the
     --wp-h variable defined on .memberships__canvas so downstream
     elements can track its bottom edge. */
  .memberships__wallpaper {
    left: 0;
    top: 40px;
    width: 100%;
    height: var(--wp-h);
    border-radius: 0;
  }

  /* Wallpaper photo taller than its 304 wrap so parallax (with
     amount 0.3 on the wrap) has range to shift against. */
  .memberships__wallpaper-photo {
    left: 0;
    top: -15%;
    width: 100%;
    height: 130%;
    object-fit: cover;
  }

  /* Invite card grows +20% by 768 (294→353 wide, 401→481 tall)
     and re-centers on the right half of the viewport (left:400
     puts the wider card's center at 576 = right-half center).
     Phone (430) keeps the original 294×401 at left:228 so it
     bleeds intentionally past the right edge per Figma. Slight
     CW rotation matches the tilted card design. */
  .memberships__invite {
    left: clamp(228px, calc(228px + (100vw - 430px) * 0.509), 400px);
    top: 0;
    width: clamp(294px, calc(294px + (100vw - 430px) * 0.175), 353px);
    height: clamp(401px, calc(401px + (100vw - 430px) * 0.237), 481px);
    transform: rotate(4deg);
    transform-origin: center center;
  }

  /* Title "YOU'RE INVITED TO THE CLUB." — 4 lines on mobile per
     Figma (43px / 41px, pale-green over the wallpaper photo).
     Anchored to --content-inset so the title aligns with the
     hero copy and the other section text at 768. */
  .memberships__title {
    left: var(--content-inset);
    top: 145px;
    font-size: 43px;
    line-height: 41px;
    color: var(--color-pale-green);
  }

  /* All-memberships panel: full-width band, medium-green bg.
     Top tracks the wallpaper's bottom (40px offset + --wp-h) so
     it butts cleanly against the wallpaper with no overlap. */
  .memberships__all {
    left: 0;
    top: calc(40px + var(--wp-h));
    width: 100%;
    height: 300px;
    border-radius: 0;
  }

  .memberships__all-content {
    padding: 44px var(--content-inset);
  }

  /* "ALL MEMBERSHIPS INCLUDE:" — 28/26 pale-green per Figma.
     Capped so it wraps to two lines ("Memberships" / "include:").
     On one line it runs 725px at 900 and disappears under the invite
     card, which sits at x349 with a higher z-index. */
  .memberships__all-title {
    font-size: 28px;
    line-height: 26px;
    margin-bottom: 18px;
    max-width: 300px;
  }

  .memberships__all-list {
    font-size: 16px;
    line-height: 22px;
  }

  /* Plans panel = the long light-green bg covering plans +
     footnote + non-members. Top shifted to 639 (was 589) to
     follow the taller all-memberships box above. Inner gutter
     follows --content-inset so each plan row aligns with the
     hero copy at 768 while staying at 30 on phone. */
  .memberships__plans {
    left: 0;
    top: calc(340px + var(--wp-h));
    width: 100%;
    height: 779px;
    border-radius: 0;
    padding: 50px var(--content-inset) 0;
    display: flex;
    flex-direction: column;
    gap: 41px;
  }

  /* Each plan becomes a 2-col row: title+bullets left, CTA+price
     right. CTA aligns to the title row top (`align-self: start`)
     so it stays put when title wraps to 2 lines (Green Jacket).
     Price is absolute-positioned below the button so it sits 15px
     under the button regardless of how tall the title is. */
  .plan {
    position: relative;
    display: grid;
    grid-template-columns: 1fr auto;
    column-gap: 12px;
    align-items: start;
  }

  /* Plan card titles 36/32 medium-green per Figma (was 24/30
     dark-green — pretty far off the spec). */
  .plan__title {
    grid-column: 1;
    grid-row: 1;
    font-size: 36px;
    line-height: 32px;
    color: var(--color-medium-green);
    margin: 0 0 10px;
  }

  .plan__cta {
    grid-column: 2;
    grid-row: 1;
    justify-self: end;
  }

  /* Price absolute-positioned 15px below the button's bottom edge.
     Button height is --button-height (25px), so top = 25 + 15 = 40. */
  .plan__price {
    position: absolute;
    top: 40px;
    right: 0;
    margin: 0;
  }

  /* Phone keeps the default 13/19 bullet size; the 16/22 bump is
     scoped to mid-mobile (501-768) only. */
  .plan__list {
    grid-column: 1;
    grid-row: 2;
    margin: 0;
  }

  /* Footnote + divider — shifted +50 to follow the plans panel
     down. Divider's relative top stays at 505 (within plans), so
     it lands at canvas y = 639 + 505 = 1144. Horizontal inset
     follows --content-inset so footnote / divider / non-members
     all align with the plan cards above. */
  /* Footnote positioned to sit ~41px below the last plan card
     (matching the header-like spacing between plan cards), with
     its bottom edge ~8px above the divider line below. */
  .memberships__footnote {
    left: var(--content-inset);
    top: calc(837px + var(--wp-h));
    width: auto;
    max-width: none;
    white-space: nowrap;
    padding-bottom: 0;
    border-bottom: none;
  }

  /* Divider sits ~8px below the footnote, between it and the
     non-members card. Relative to plans panel top (340+wp-h),
     524 puts it at canvas y = 864+wp-h. */
  .memberships__plans::after {
    content: "";
    position: absolute;
    left: var(--content-inset);
    top: 524px;
    width: calc(100% - 2 * var(--content-inset));
    height: 1px;
    background: var(--color-black-green-text);
    opacity: 0.5;
  }

  /* Non-members card — positioned ~41px below the divider so the
     rhythm matches the plan-card flex gap. All inner layout comes
     from .plan and .plan__* selectors above. */
  .memberships__nonmembers {
    position: absolute;
    left: var(--content-inset);
    top: calc(936px + var(--wp-h));
    width: calc(100% - 2 * var(--content-inset));
    padding: 0;
  }

  /* PRO SHOP TOP ------------------------------------------- */
  /* Section shortens from 742 to 493 — content tightens around
     the header. Brand logos strip removed on mobile per Figma. */
  .proshop-top {
    height: 493px;
  }

  /* Reset desktop's 0 30% padding (way too wide on a 430 frame). */
  .proshop-top__content {
    padding: 0 30px;
  }

  /* Pro shop SVG header — starts at the 344×111 phone size and
     scales up another 20% (→ 413×133) by 768. Aspect ratio
     (3.1) preserved via parallel clamps on width + height. */
  .proshop-top__header {
    width: clamp(344px, calc(344px + (100vw - 430px) * 0.204), 413px);
    height: clamp(111px, calc(111px + (100vw - 430px) * 0.0651), 133px);
    margin-bottom: 12px;
  }

  .proshop-top__body {
    font-size: 14px;
    line-height: 18px;
    max-width: 305px;
    margin-bottom: 24px;
  }

  .proshop-top__logos {
    display: none;
  }

  /* PRO SHOP BOTTOM ---------------------------------------- */
  /* Section shortens from 666 to 532. Subtitle drops on mobile.
     Product grid (desktop: 4-col grid) becomes a horizontal
     touch-scroll strip — 3rd card peeking off-right per Figma
     is the swipe affordance. All 4 products live in the strip. */
  .proshop-bottom__canvas {
    /* 494 = 136 (grid top) + 329 (card height) + 29 (match the 29px gap
       between cards, so the strip is inset from the section edge by the same
       amount it's inset from its neighbours). Was 471, which left only 6px. */
    height: 494px;
  }

  .proshop-bottom__bg {
    height: 471px;
  }

  /* Logged-in members get an extra member-discount line on every carousel
     card, making the cards taller than the non-member layout this section was
     sized for. Grow the canvas (and its green bg) so the card bottoms aren't
     clipped by the section. Keyed off body[data-auth="in"], set by
     initAuthState() after the session check. */
  body[data-auth="in"] .proshop-bottom__canvas,
  body[data-auth="in"] .proshop-bottom__bg {
    /* Same +23px as above; signed-in cards carry the member-price row and are
       correspondingly taller, so the two heights move together. */
    height: 533px;
  }

  /* Show "New arrivals:" mobile title, hide the desktop one. */
  .proshop-bottom__title--desktop {
    display: none;
  }

  .proshop-bottom__title--mobile {
    display: block;
  }

  .proshop-bottom__title {
    left: var(--content-inset);
    top: 60px;
    font-size: 36px;
    line-height: 24px;
  }

  .proshop-bottom__subtitle {
    display: block;
    left: var(--content-inset);
    top: 96px;
    font-size: 17px;
    line-height: 19px;
  }

  /* Mobile inherits the same drag-scroll carousel as desktop. Touch
     swipe already works because of overflow-x: auto. We tighten the
     gap, reset the inset to match the mobile content-inset, and shrink
     the card to the 153px mobile footprint. Scroll snap helps a
     thumb-driven swipe land cleanly on a card boundary. */
  .proshop-bottom__grid {
    left: 0;
    top: 136px;
    right: 0;
    width: auto;
    padding-bottom: 28px;            /* clear the hover shadow on tap-and-hold */
    cursor: auto;                    /* no grab cursor on touch devices */
  }

  .proshop-bottom__track {
    gap: 29px;
    padding-left: var(--content-inset);
    padding-right: var(--content-inset);
    scroll-snap-type: x mandatory;
  }

  .proshop-bottom__track .product-card {
    width: 153px;
    scroll-snap-align: start;
  }

  .proshop-bottom__track .product-card__body {
    padding: 12px;
  }

  .proshop-bottom__track .product-card__title {
    font-size: 13px;
  }

  .proshop-bottom__track .product-card__price {
    font-size: 17px;
  }

  .proshop-bottom__track .product-card__subtitle {
    font-size: 11px;
  }

  .proshop-bottom__track .product-card--cta {
    padding: 20px 14px;
    gap: 12px;
  }

  .proshop-bottom__track .product-card__cta-label {
    font-size: 21px;
  }

  .proshop-bottom__track .product-card__cta-arrow {
    width: 28px;
  }

  .product__photo-wrap {
    width: 153px;
    height: 153px;
  }

  .product__brand {
    font-size: 14px;
    line-height: 18px;
    margin: 16px 0 4px;
  }

  .product__name {
    font-size: 17px;
    line-height: 21px;
    margin: 0 0 6px;
  }

  .product__price {
    font-size: 22px;
    line-height: 22px;
    margin: 0 0 8px;
  }

  .product__detail {
    font-size: 11px;
    line-height: 14px;
    margin: 0 0 14px;
    min-height: 28px;
  }

  /* TECHNOLOGY --------------------------------------------- */
  /* Mobile (part of Figma's combined TECH/GIFT-CARDS wrapper
     111:215, height 1368). In our HTML these are separate
     sections — TECHNOLOGY ends ~y=873 in the combined wrapper.
     Bumped to 920 so the 4th feature body (Swing Optix) doesn't
     get clipped when text wraps to extra lines on narrower
     viewports. */
  .technology__canvas {
    height: 920px;
  }

  /* Photo wrap inset by --content-inset on both sides. */
  .technology__photo-wrap {
    left: var(--content-inset);
    top: 87px;
    width: calc(100% - 2 * var(--content-inset));
    height: 363px;
  }

  /* Content stack starts below the photo, same inset. */
  .technology__content {
    left: var(--content-inset);
    top: 473px;
    width: calc(100% - 2 * var(--content-inset));
  }

  .technology__title {
    font-size: 36px;
    line-height: 36px;
    margin-bottom: 17px;
  }

  /* Tech features keep desktop sizes (16/13) since the metadata
     reads the same on mobile. Gaps slightly larger. */
  .tech-features {
    gap: 22px;
  }

  /* Feature subheads 30% larger on mobile (16 → 21). */
  .tech-feature__title {
    font-size: 21px;
    line-height: 23px;
  }

  /* No CTA in mobile Figma — hide desktop's button. */
  .technology__cta {
    display: none;
  }

  /* GIFT CARDS --------------------------------------------- */
  /* At mobile the photo card goes SQUARE (width = height = panel
     width). Canvas height is symmetric: 39px above the photo +
     square photo + 39px below. Type stack sits centered vertically
     and horizontally inside the photo area. */
  .gift-cards__canvas {
    height: calc(118px + var(--canvas-width) - 2 * var(--content-inset));
  }

  /* Square photo-wrap. width = panel width via 100% of the canvas
     (which is --canvas-width) minus content-inset gutters on each
     side. aspect-ratio: 1/1 enforces the matching height. */
  .gift-cards__photo-wrap {
    left: var(--content-inset);
    top: 39px;
    width: calc(100% - 2 * var(--content-inset));
    aspect-ratio: 1 / 1;
    height: auto;
    border-radius: 15px;
  }

  /* Photo taller than its wrap so the parallax JS has range to
     animate against — same top:-15% height:130% pattern as desktop. */
  .gift-cards__photo {
    top: -15%;
    height: 130%;
  }

  /* Title / body / CTA left-aligned, the stack vertically centred on
     the canvas 50% line via pre-computed translateY offsets. */
  .gift-cards__title {
    left: calc(var(--content-inset) + 32px);
    /* 291.5 = the CTA's 17.5 offset + its 25 height + 17 gap + 60 body + 17 gap
       + 180 title, i.e. the whole stack measured up from the card's bottom. */
    top: calc(100vw - 2 * var(--content-inset) - 291.5px);
    bottom: auto;
    transform: none;
    width: auto;
    text-align: left;
    font-size: 36px;
    line-height: 36px;
  }

  .gift-cards__body {
    left: calc(var(--content-inset) + 32px);
    /* 94.5 = 17.5 + 25 (CTA) + 17 (gap) + 60 (four lines at 15) */
    top: calc(100vw - 2 * var(--content-inset) - 94.5px);
    bottom: auto;
    transform: none;
    width: 235px;
    text-align: left;
    font-size: 13px;
    line-height: 15px;
  }

  /* Position via top, not transform: .btn:active sets transform:
     translateY(1px) for the press nudge, which would otherwise wipe out a
     translateY used for positioning and make the button leap ~116px on tap. */
  .gift-cards__cta {
    left: calc(var(--content-inset) + 32px);
    /* 17.5 = the CTA's 25px height + the 32px bottom margin, less the 39px the
       card sits below the canvas top — so the button clears the card's bottom
       edge by 32px, matching its 32px left inset, at any phone width. */
    top: calc(100vw - 2 * var(--content-inset) - 17.5px);
    bottom: auto;
  }

  /* Tree is desktop-only — at 768 and under the gift-cards
     section is narrower and the tree doesn't sit cleanly above
     the photo, so we hide it across the whole mobile range. */
  .gift-cards__tree {
    display: none;
  }

  /* COURSES ------------------------------------------------ */
  /* Mobile drops the carousel entirely. Just shows the featured
     slide as a top banner with title + body below — no thumbs,
     dots, or arrows. The JS still runs but its controls are
     hidden. */
  .courses__canvas {
    height: 553px;
  }

  /* Desktop-only decorative + carousel UI all hidden. */
  .courses__sandtrap,
  .courses__pond,
  .courses__icon,
  .courses__panel,
  .courses__nav,
  .courses__thumbs,
  .courses__dots {
    display: none;
  }

  /* Featured slide becomes a full-width top banner (~243 tall). */
  .courses__featured-wrap {
    left: 0;
    top: 0;
    width: 100%;
    height: 243px;
    border-radius: 0;
  }

  .courses__featured-label {
    left: 32px;
    bottom: 18px;
    width: 168px;
    font-size: 18px;
    line-height: 18px;
    text-align: left;
  }

  /* Phone keeps the original 30/30 size; the 36/36 bump is
     scoped to mid-mobile (501-768) where the wider canvas
     handles it cleanly. */
  .courses__title {
    top: 285px;
    font-size: 30px;
    line-height: 30px;
  }

  /* Body anchors to the 30px insets for natural wrap. */
  .courses__body {
    left: 30px;
    right: 30px;
    width: auto;
    top: 385px;
    font-size: 14px;
    line-height: 18px;
  }

  /* MISC --------------------------------------------------- */
  /* Canvas height extended past Figma's 432 to add breathing
     room between the last card and the testimonials pond above. */
  .misc__canvas {
    height: 480px;
  }

  /* Photo stretches edge-to-edge at 768 (full viewport). Phone
     view restores the Figma-style overflow positioning in the
     sub-500 block below. */
  .misc__photo {
    left: 0;
    top: 0;
    width: 100%;
    height: 238px;
    border-radius: 0;
    max-width: none;
  }

  /* Three cards (including First Time?) in a 3-col grid below
     the photo, aligned to --content-inset. Phone view drops
     back to a 2-col grid + hidden first card. */
  .misc__cards {
    left: var(--content-inset);
    top: 256px;
    width: calc(100% - 2 * var(--content-inset));
    grid-template-columns: repeat(3, 1fr);
    column-gap: 33px;
  }

  .misc-card__title {
    font-size: 26px;
    line-height: 30px;
    margin-bottom: 6px;
  }

  .misc-card__body {
    font-size: 13px;
    line-height: 19px;
    max-width: none;
  }

  /* TESTIMONIALS ------------------------------------------- */
  /* Mobile section: 814 tall. Layout:
       y=13–419  blue "pond" blob (decorative bg, below title)
       y=83–191  title overlaying the pond
       y=216–500 wavy-masked seaside photo (overlaps pond)
       y=501–814 light-green panel with quote/name/arrows/dots
     Title is inside the panel but pulled up via negative top to
     overlay the pond. Name + meta are absolutely positioned so
     they align with the asymmetric arrow row regardless of
     quote length. */
  .testimonials__canvas {
    height: 814px;
  }

  /* Pond blob — sits behind title at the top. Display set
     explicitly to undo the desktop `display: none` default. */
  .testimonials__pond {
    display: block;
    position: absolute;
    left: -55px;
    top: 13px;
    /* 485 is the 430-frame value, which ended exactly at that viewport's
       right edge — on a wider phone it left a gap. The max() keeps 430
       byte-identical and stretches to the right edge above it. */
    width: max(485px, calc(100% + 55px));
    height: 406px;
    max-width: none;
    z-index: 0;
  }

  /* Masked seaside photo — sized so wrap aspect matches the mask
     SVG's native 800×607 (≈1.318) ratio, so mask renders without
     horizontal stretching. Wrap is wider than the 430 viewport
     (500px) and centered, letting the wave's left + right edges
     bleed off-screen while the natural top curve is preserved.
     Also extends 93px past the panel top (594 > 501) so there's
     no 1px gap between photo and panel. */
  .testimonials__photo-wrap {
    left: 50%;
    top: 215px;
    width: 500px;
    height: 379px;
    transform: translateX(-50%);
    z-index: 3;
  }

  /* Panel becomes fully opaque on mobile so the photo's overlap
     into the panel area doesn't bleed through the 0.9 desktop
     opacity. */
  .testimonials__panel {
    opacity: 1;
  }

  /* Re-enable parallax range (desktop's top:-15% height:130%
     applies — image is taller than the wrap, JS shifts the image
     within it on scroll, and the static -15% shift shows the
     image's upper portion). */
  .testimonials__photo {
    top: -15%;
    height: 130%;
  }

  /* Panel: full-width bottom band, light-green. z-index lowered
     so it sits BEHIND the photo above (panel extends up behind
     the photo's wavy bottom). */
  .testimonials__panel {
    left: 0;
    top: 501px;
    width: 100%;
    height: 313px;
    padding: 0 30px;
    border-radius: 0;
    z-index: 1;
  }

  /* Title escapes the panel via negative `top` so it sits at
     y=83 of the section, overlaid on the pond. -418 = panel
     top (501) − target y (83). Phone keeps the original 28/28
     size; the 36/36 bump is scoped to mid-mobile (501-768). */
  .testimonials__title {
    position: absolute;
    top: -438px;
    left: 0;
    right: 0;
    margin: 0;
    color: var(--color-pale-green);
    font-size: 28px;
    line-height: 28px;
  }

  /* On mobile the name + meta are absolutely positioned at fixed
     panel coords, so when two slides crossfade their names land
     at the EXACT same pixel position — produces a momentary
     stacked-text artifact. On desktop the names land at slightly
     different y per slide so the overlap reads as a smooth fade.
     Mobile fix: drop the 220ms visibility delay on non-active
     slides so the outgoing slide hides instantly (its opacity
     still transitions, invisibly), and only the incoming slide's
     content is on screen during the fade-in. */
  .testimonial {
    transition: opacity 220ms ease, visibility 0s;
  }

  /* Quote starts just below panel top (y=556 → panel-y 55). */
  .testimonial__quote {
    font-size: 14px;
    line-height: 18px;
    max-width: none;
    min-height: 0;
    margin: 55px 0 0;
  }

  /* Name + meta absolute-positioned within the panel so they
     align with the asymmetric arrow row (arrow-left y=667,
     arrow-right y=704). Name at panel-y 169 → canvas y=670;
     meta at panel-y 209 → canvas y=710. */
  .testimonial__name {
    position: absolute;
    top: 169px;
    left: 0;
    right: 0;
    text-align: center;
    font-size: 32px;
    line-height: 36px;
  }

  .testimonial__meta {
    position: absolute;
    top: 209px;
    left: 0;
    right: 0;
    text-align: center;
    font-size: 14px;
    line-height: 18px;
  }

  /* Arrows aligned at panel-y 166 so they flank the name row
     evenly (Figma had them asymmetric — David wants symmetric).
     Both pushed inward from 30 → 50 so the name area is tighter
     between them. */
  .testimonials__nav {
    top: 166px;
  }

  .testimonials__nav--prev {
    left: 75px;
  }

  .testimonials__nav--next {
    left: auto;
    right: 75px;
  }

  /* Dots near the panel bottom. */
  .testimonials__dots {
    top: 240px;     /* y=741 - panel top 501 */
  }

  /* CONTACT + SIGNOFF -------------------------------------- */
  /* Map is dropped 54px lower than Figma's original 1003 + grows
     to a 1:1 square ratio. Pond's natural 425-tall bbox (top=668,
     ends ~1093) now overlaps the map's new top (1057) by ~36px —
     double the previous gentle overlap. Everything below the map
     shifts +165 (54 down move + 111 added map height). Canvas
     also grows with --contact-shift so the taller photo at 768
     doesn't truncate. */
  .contact__canvas {
    height: calc(1921px + var(--contact-shift));
  }

  /* Signoff bg: organic blob, anchored 165px lower than original. */
  .contact__canvas::after {
    content: "";
    position: absolute;
    left: -270px;
    top: calc(1071px + var(--contact-shift));
    width: 1208px;
    height: 833px;
    background: url('/_next/image?url=%2Fhg%2Fassets%2Fsignoff-bg.png&w=1920&q=75') no-repeat center / 100% 100%;
    clip-path: inset(417px 0 0 0);
    pointer-events: none;
    z-index: 0;
  }

  /* Decorative blob + clubhouse aren't in mobile Figma. */
  .contact__green-blob,
  .contact__clubhouse {
    display: none;
  }

  /* Storefront photo: full-width banner at top. Height fluid-
     scales from 385 at phone to 500 at 768 (subtle growth that
     reads "wider banner" rather than blowing up to the
     desktop's near-square 688). Subsequent positioned elements
     use --contact-shift so they slide down with the photo. */
  .contact__photo {
    left: 0;
    top: 0;
    width: 100%;
    height: clamp(385px, calc(385px + (100vw - 430px) * 0.34), 500px);
    border-radius: 15px;
  }

  /* Address: widened from 268 to 320 so "Tuesday – Sunday 9am
     to 4pm," fits on a single line (was auto-wrapping the comma
     onto its own line). Shifts with --contact-shift so it
     stays below the photo at 768. */
  .contact__details {
    left: 50%;
    transform: translateX(-50%);
    top: calc(431px + var(--contact-shift));
    width: 320px;
  }

  /* "We're a space for golf nerds — come in and say hello" blob.
     Restored to Figma y=668 so the gap above the pond (address
     ends ~605, pond starts at 668 → 63px breathing room) is
     preserved. The pond's 425-tall bbox naturally overlaps the
     map below — map is pushed down enough that the overlap is
     gentle (~18px) rather than the original 90. */
  /* Centred rather than pinned at left: 50 — that was the 430 frame's
     value, which happened to centre there and drifted left on any wider
     phone. Both layers share the same centre line, so they stay
     registered. */
  .contact__blue-blob {
    left: 50%;
    transform: translateX(-50%);
    top: calc(668px + var(--contact-shift));
    width: 330px;
  }

  .contact__blue-blob-text {
    left: 50%;
    transform: translateX(-50%);
    top: calc(671px + var(--contact-shift));
    width: 324px;
  }

  /* Map: full-width band at top:1057 (54px past Figma's y=1003).
     Height capped at 431px so the map's bottom edge lines up with
     where the green signoff bg's visible (clipped) area begins at
     y=1488 — no more map bleeding past the wavy boundary into the
     signoff section. Shifts with --contact-shift. */
  .contact__map {
    left: 0;
    top: calc(1057px + var(--contact-shift));
    width: 100%;
    height: 431px;
    border-radius: 0;
  }

  /* SIGNOFF content — uses --content-inset so it lines up with the
     footer's left gutter (which also uses --content-inset). Title
     uses dual-span sets for the 3-line mobile break. */
  .contact__title {
    left: var(--content-inset);
    top: calc(1564px + var(--contact-shift));
    font-size: 43px;
    line-height: 41px;
    color: var(--color-pale-green);
    z-index: 1;
  }

  .contact__body {
    left: var(--content-inset);
    top: calc(1698px + var(--contact-shift));
    width: calc(100% - 2 * var(--content-inset));
    max-width: 325px;
    font-size: 14px;
    line-height: 18px;
    color: var(--color-pale-green);
    z-index: 1;
  }

  .contact__cta {
    left: var(--content-inset);
    top: calc(1755px + var(--contact-shift));
    z-index: 1;
  }

  /* FOOTER ------------------------------------------------- */
  /* Mobile (430 × 678). Logo top-left, clubhouse drawing
     centered below, tagline + 2 link columns, divider, then
     copyright + legal on stacked lines. */
  .site-footer__canvas {
    height: 678px;
  }

  /* Logo + clubhouse anchored at (content-inset − ~19) so the
     visible glyph aligns with the content gutter — the script
     logo has an optical inset of ~19px from its bbox left, so
     subtracting from content-inset puts the glyph edge where
     the text content starts. */
  .site-footer__logo-link {
    left: calc(var(--content-inset) - 19px);
    top: 38px;
    width: 211px;
  }

  .site-footer__clubhouse {
    left: calc(var(--content-inset) - 18px);
    top: 113px;
    width: 324px;
  }

  /* Tagline 23/48 medium-green per Figma — smaller than desktop
     26px so it fits on a single line at 430-wide. */
  .site-footer__tagline {
    left: var(--content-inset);
    top: 231px;
    font-size: 23px;
    line-height: 48px;
  }

  .site-footer__links {
    top: 275px;
  }

  .site-footer__links--col1 {
    left: var(--content-inset);
    width: 130px;
  }

  /* col2 follows col1 with a constant 55px gap (col1 width 130 +
     gap 55 = 185 offset from content-inset). */
  .site-footer__links--col2 {
    left: calc(var(--content-inset) + 185px);
    width: 188px;
  }

  /* Divider line: spans content width. */
  .site-footer__canvas::before {
    left: var(--content-inset);
    top: 421px;
    width: calc(100% - 2 * var(--content-inset));
  }

  /* Footer legal copy: 13/19 per Figma (smaller than desktop). */
  .site-footer__copyright {
    left: var(--content-inset);
    top: 434px;
    width: calc(100% - 2 * var(--content-inset));
    font-size: 13px;
    line-height: 19px;
  }

  /* Legal links: move below copyright, inline with " - " between. */
  .site-footer__legal {
    left: var(--content-inset);
    top: 453px;
    right: auto;
    gap: 0;
    font-size: 13px;
    line-height: 19px;
  }

  .site-footer__legal li:not(:last-child)::after {
    content: " - ";
    margin: 0 6px;
  }

  /* Column-link font matches desktop body so menus stay readable
     at 16/19. */
  .site-footer__links {
    font-size: 16px;
    line-height: 19px;
  }

  /* COURSES swipe carousel ---------------------------------- */
  /* Mobile re-enables the carousel as a horizontal scroll-snap
     strip (the JS-driven carousel is hidden along with thumbs/
     arrows/dots). All 7 slides sit side-by-side; users swipe to
     navigate. Each slide takes the full viewport width. */
  .courses__featured-wrap {
    display: flex;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }

  .courses__featured-wrap::-webkit-scrollbar {
    display: none;
  }

  .courses__featured-slide {
    flex-shrink: 0;
    width: 100%;
    height: 100%;
    scroll-snap-align: start;
    opacity: 1;
    visibility: visible;
    transition: none;
  }

  /* When desktop's JS toggles .is-active, mobile slides keep
     full opacity (active state is whichever slide the user has
     scrolled to). */
  .courses__featured-slide:not(.is-active) {
    opacity: 1;
    visibility: visible;
  }
}

/* ============================================================
   Phone-only overrides (≤500). Picks up where the broader
   mobile (≤768) block leaves off — applied last so it wins
   the cascade for narrow viewports. Breakpoint set to 500 (not
   430) so it catches newer iPhone Pro Max viewports (~440 CSS
   px) too.
   ============================================================ */
/* ============================================================
   Mid-mobile (501–768). Restores the full desktop courses
   carousel at this range — the broader mobile block above
   strips it down to a banner+text for the 430 phone Figma
   frame. Hand-tuned positions for a 768 viewport (no
   transform:scale) so text + buttons stay readable.
   ============================================================ */
@media (min-width: 501px) and (max-width: 1023px) {
  /* Decorative blobs + the centered icon are sized for the
     1440 desktop canvas and don't translate cleanly to 768 —
     hide them at this range and let the carousel UI speak. */
  .courses__sandtrap,
  .courses__pond,
  .courses__icon {
    display: none;
  }

  /* Grows with the taller featured image: panel top + 260 panel + 60
     tail = 586 + Hf. */
  .courses__canvas {
    height: calc(586px + 0.5645 * (var(--canvas-width) - 168px));
  }

  /* Title centered at top — matches the gift-cards header size
     (36/36) for a consistent display-headline scale at mobile. */
  .courses__title {
    top: 60px;
    font-size: 36px;
    line-height: 36px;
  }

  /* Body centered, narrower than desktop so it wraps cleanly.
     Body top bumped 130 → 150 to clear the taller (36px) title
     bottom at ~132 with a comfortable gap. */
  .courses__body {
    left: 50%;
    transform: translateX(-50%);
    top: 150px;
    width: 500px;
    max-width: calc(100% - 60px);
    right: auto;
    text-align: center;
    font-size: 14px;
    line-height: 18px;
  }

  /* Featured slide as a large rounded image, on the desktop's
     800.18 × 451.7 ratio (1.7715) rather than the old squat 300px
     box. Top 230 → 246 for more air under the copy. Everything below
     it keys off this height: Hf = 0.5645 × (card width). */
  .courses__featured-wrap {
    left: 84px;
    top: 246px;
    width: calc(100% - 168px);
    aspect-ratio: 800.18 / 451.7;
    height: auto;
    border-radius: 15px;
  }

  .courses__featured-label {
    left: 24px;
    bottom: 18px;
    width: auto;
    font-size: 22px;
    line-height: 26px;
    text-align: left;
  }

  /* Light-green backdrop panel under the carousel's lower row
     (thumbs / nav / dots). Sits behind those elements; the
     featured slide above floats on the page bg. */
  .courses__panel {
    display: block;
    left: 30px;
    /* featured top 246 + Hf + 20 */
    top: calc(266px + 0.5645 * (var(--canvas-width) - 168px));
    width: calc(100% - 60px);
    height: 260px;
    background: var(--color-light-green);
    opacity: 0.9;
    border-radius: 15px;
  }

  /* Thumbs strip centered inside the panel: 3 visible thumbs
     at 170×96 with 16px gaps (510 + 32 = 542 wide). script.js
     measures the step off the DOM, so the window shows exactly
     three whole thumbs — no part-cropped ones at the edges. */
  .courses__thumbs {
    display: block;
    left: 50%;
    transform: translateX(-50%);
    top: calc(306px + 0.5645 * (var(--canvas-width) - 168px));
    width: 542px;
    overflow: hidden;
  }

  .courses__thumbs-track {
    gap: 16px;
  }

  .course-thumb {
    width: 170px;
  }

  .course-thumb img {
    width: 170px;
    height: 96px;
  }

  .course-thumb__label {
    font-size: 14px;
    line-height: 16px;
    margin-top: 8px;
  }

  /* Nav arrows at the thumb row's vertical midpoint, tucked 20px off
     the strip's edges (271 half-width + 20 gap + 32 arrow = 323)
     instead of way out on the panel sides. */
  .courses__nav {
    display: flex;
    top: calc(338px + 0.5645 * (var(--canvas-width) - 168px));
    width: 32px;
    height: 32px;
  }

  .courses__nav img {
    width: 32px;
    height: 32px;
  }

  .courses__nav--prev {
    left: calc(50% - 323px);
  }

  .courses__nav--next {
    left: auto;
    right: calc(50% - 323px);
  }

  /* Dots near the panel bottom, centered. */
  .courses__dots {
    display: flex;
    top: calc(476px + 0.5645 * (var(--canvas-width) - 168px));
    gap: 10px;
  }

  .courses__dot {
    width: 16px;
    height: 16px;
  }

  /* TESTIMONIALS at 501-768: hand-tuned scaled-down desktop
     layout. Wavy-masked photo on top with a slight bleed into
     the light-green panel below; title + carousel content
     flow inside the panel just like desktop. Phone view's
     pond + custom absolute name/meta positions are overridden
     here. */
  /* Canvas extended (800 → 950) to give a real section gap below
     the panel before the contact section starts. */
  .testimonials__canvas {
    height: 950px;
  }

  .testimonials__pond {
    display: none;
  }

  /* Photo: edge inset to --content-inset, kept close to the
     desktop aspect (800/607 ≈ 1.32). z-index lifted to 3 so the
     photo sits IN FRONT of the panel below — the panel can now
     extend up behind the photo's wavy bottom. */
  .testimonials__photo-wrap {
    left: var(--content-inset);
    top: 80px;
    width: calc(100% - 2 * var(--content-inset));
    height: 455px;
    transform: none;
    -webkit-mask-image: url("assets/testimonials-mask.svg");
            mask-image: url("assets/testimonials-mask.svg");
    -webkit-mask-size: 100% 100%;
            mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center;
            mask-position: center;
    z-index: 3;
  }

  /* Panel extends up behind the photo by ~25% of the photo's
     height (114px). Top: 460 → 421 (up 39, on top of the existing
     75px overlap). Padding-top bumped by the same 39 so flow
     content stays at the same canvas-y position. z-index lowered
     to 1 so the photo sits in front. Height grown to give room
     for the bigger title + spacing. */
  .testimonials__panel {
    left: 30px;
    top: 421px;
    width: calc(100% - 60px);
    /* Hugs its content instead of a fixed 480 — the old height left a
       large empty band under the dots once the quote reflowed shorter
       at these widths. */
    height: auto;
    padding: 129px 40px 60px;
    background: var(--color-light-green);
    opacity: 1;
    border-radius: 15px;
    text-align: center;
    z-index: 1;
  }

  /* Title at gift-cards header size (36/36) instead of 28/28. */
  .testimonials__title {
    position: static;
    top: auto;
    left: auto;
    right: auto;
    color: var(--color-dark-green);
    font-size: 36px;
    line-height: 36px;
    margin: 0 0 24px;
    text-align: center;
  }

  /* Quote bottom margin widened (20 → 50) so the dots (absolute
     panel-y 335) sit cleanly between the quote and the name. */
  .testimonial__quote {
    margin: 0 auto 50px;
    max-width: 500px;
    font-size: 14px;
    line-height: 19px;
    min-height: calc(19px * 3);
  }

  /* Name bumped to match the title scale (36/36), with more
     breathing room between it and the meta below. */
  .testimonial__name {
    position: static;
    top: auto;
    left: auto;
    right: auto;
    font-size: 36px;
    line-height: 36px;
    margin: 0 0 10px;
  }

  .testimonial__meta {
    position: static;
    top: auto;
    left: auto;
    right: auto;
    font-size: 14px;
    line-height: 19px;
  }

  /* Arrows flank the name, as they do on desktop (which insets them
     ~183px from the panel's centre line). Anchored to the panel's
     BOTTOM: everything below the quote — name, margins, dots, padding —
     is fixed, so one offset keeps them on the name row at every width,
     however many lines the quote reflows to. */
  .testimonials__nav {
    top: auto;
    bottom: 116px;
    width: 32px;
    height: 32px;
  }

  .testimonials__nav img {
    width: 32px;
    height: 32px;
  }

  .testimonials__nav--prev {
    left: calc(50% - 215px);
  }

  .testimonials__nav--next {
    left: auto;
    right: calc(50% - 215px);
  }

  /* Dots run in flow after the slides, like the desktop order
     (quote → name → dots). They were absolutely placed at panel-y 335,
     which was tuned for the 501–768 stack and landed on top of the name
     once the quote reflowed shorter at wider viewports. */
  .testimonials__dots {
    position: static;
    left: auto;
    top: auto;
    transform: none;
    justify-content: center;
    margin: 28px 0 0;
    gap: 10px;
  }

  .testimonials__dot {
    width: 16px;
    height: 16px;
  }

  /* INTRO body — open up the line width at mid-mobile (501–768).
     Broader-mobile caps it at 300px (tight for phones); here we
     let it stretch to 65% of the section's content area for a
     more breathable read at tablet portrait. */
  .intro__body {
    max-width: 65%;
  }

  /* MEMBERSHIPS invite card +15% AND moved to the left in this
     range only (mirrors the desktop layout where the invite sits
     on the left side of the section). Phone (≤500) keeps the
     original right-side positioning via the broader-768 block. */
  .memberships__invite {
    /* Right-side overlap pattern. Shifted 30px left from the
       broader-768 block (198 instead of 228 at the phone end,
       370 instead of 400 at the 768 end). +12% size on top of
       the earlier +15% bump (so 338→379 / 406→455 wide, 461→516
       / 553→619 tall). */
    left: clamp(198px, calc(198px + (100vw - 430px) * 0.509), 370px);
    width: clamp(379px, calc(379px + (100vw - 501px) * 0.285), 455px);
    height: clamp(516px, calc(516px + (100vw - 501px) * 0.386), 619px);
  }

  /* CONTACT side-by-side at mid-mobile (501–768).
     Mirrors desktop's layout: address on the LEFT, pond + text on
     the RIGHT, both aligned vertically. The pond's top stays at its
     current y=668 so its bottom keeps overlapping the map widget
     below — only the horizontal position changes. The address gets
     pulled down from y=431 to y=668 to sit beside the pond. */
  .contact__details {
    left: var(--content-inset);
    transform: none;
    top: calc(668px + var(--contact-shift));
    width: 250px;
    text-align: left;
  }

  .contact__blue-blob {
    left: auto;
    right: var(--content-inset);
    width: 330px;
  }

  .contact__blue-blob-text {
    left: auto;
    right: calc(var(--content-inset) + 3px);
    width: 324px;
  }

  /* TECHNOLOGY photo +15% (363 → 417) at mid-mobile so the
     image reads with more presence at the wider canvas. Content
     stack shifts down 54px to preserve the photo-to-title gap;
     canvas height grows the same 54px to keep the 4th feature
     body from clipping. Phone (≤500) keeps the original 363
     height via the broader-768 block. */
  .technology__canvas {
    height: 974px;
  }

  .technology__photo-wrap {
    height: 417px;
  }

  .technology__content {
    top: 527px;
  }

  /* MEMBERSHIPS plan bullets bumped 13/19 → 16/22 at mid-mobile
     for easier reading on tablet portrait. Phone keeps the default
     13/19 via the global rule. */
  .plan__list {
    font-size: 16px;
    line-height: 22px;
  }
}

/* ============================================================
   769–1023 — mobile layout, DESKTOP line breaks
   The break swap below fires at ≤768, so this zone renders the desktop
   span structure inside the mobile block's type scale. One title needs
   its mobile width cap lifted to suit: the hero is capped at 280px for
   the phone's 3-line structure, which forced the 2 desktop lines
   ("It's tee time," / "all the time.", 364px each at 45px) to wrap into
   4 and run into the buttons.
   ============================================================ */
@media (min-width: 769px) and (max-width: 1023px) {
  .hero__title {
    max-width: none;
  }

  /* Memberships is the one headline that has to keep the phone breaks
     here: the invite card sits at x349 with a higher z-index, and the
     2-line desktop structure runs 391px wide — 122px of it disappeared
     under the card. The 4-line stack clears it. */
  .memberships__title .break-mobile {
    display: inline;
  }

}

/* ============================================================
   LINE-BREAK SWAP — ≤768 only
   Headlines keep both break structures in the DOM and toggle them per
   breakpoint. This swap used to fire at ≤1023, which meant a ~1000px
   viewport got the phone's 4-line headlines with plenty of width left
   over. Scoped to ≤768 so 769–1023 renders the desktop break structure
   while still using the mobile block's type scale and layout.
   ============================================================ */
@media (max-width: 768px) {
  /* Hide desktop-only manual breaks. */
  .break-desktop {
    display: none;
  }

  /* Show mobile-only line breaks inside titles (`<br class="break-mobile">`).
     Lets a single `.line` span render as one visual line on desktop and
     stack into multiple lines on mobile without duplicating spans. */
  .break-mobile {
    display: inline;
  }

  /* Mobile-only / desktop-only .line variants for titles whose
     break structure differs across breakpoints. */
  .line--desktop {
    display: none;
  }

  .line--mobile {
    display: block;
  }
}

/* 501–768 runs the 3-line headline break structure, so the testimonials
   title takes an extra line and the quote reflows to four at the narrow
   end — the content-hugging panel runs ~537 tall there and overshot the
   950 canvas. */
@media (min-width: 501px) and (max-width: 768px) {
  .testimonials__canvas {
    height: 1010px;
  }
}

/* Stat row across the whole 501–1023 zone: grouped left on one fixed
   50px gap, no scaling through the range. The mobile block spreads them
   edge-to-edge (space-between), which is right at 430 but at ~1000 threw
   100% out to the far right, past the green. 50 rather than the desktop
   80 because 80 doesn't fit at the bottom of the range — at 501 the
   content column is ~420 wide and the three blocks already eat 308 of
   it. Phone (≤500) keeps the Figma edge-to-edge spread. */
@media (min-width: 501px) and (max-width: 1023px) {
  .nerds__stats {
    justify-content: flex-start;
    gap: 50px;
  }

  /* CONTACT -------------------------------------------------
     A miniature of the desktop composition rather than the phone's
     stack: photo top-left, clubhouse top-right, pond overlapping
     between them, address under the photo. All four keep their desktop
     sizes and positions RELATIVE to each other — the group just scales
     to the content width.

     --cg-w is the group's width (desktop 1008: photo left 220 →
     clubhouse right 1228). Every offset below is that desktop measure
     ÷ 1008, so one variable drives the whole cluster. Group height is
     0.868 × width (108.95 → pond bottom 984).

     The address is the exception: type can't shrink with the group and
     stay readable, so it scales down to a 12px floor. */
  .contact__canvas {
    --cg-w: calc(var(--canvas-width) - 2 * var(--content-inset));
    --c-map-top: calc(100px + 0.868 * var(--cg-w));
    height: calc(864px + var(--c-map-top));
  }

  .contact__photo {
    left: var(--content-inset);
    top: 40px;
    width: calc(0.47619 * var(--cg-w));
    height: calc(0.42659 * var(--cg-w));
    border-radius: 15px;
  }

  /* Building illustration is hidden on phone; it's part of the
     composition here. */
  .contact__clubhouse {
    display: block;
    left: calc(var(--content-inset) + 0.56169 * var(--cg-w));
    right: auto;
    top: calc(40px + 0.01338 * var(--cg-w));
    width: calc(0.43831 * var(--cg-w));
    height: auto;
  }

  .contact__blue-blob {
    left: calc(var(--content-inset) + 0.40801 * var(--cg-w));
    top: calc(40px + 0.20084 * var(--cg-w));
    width: calc(0.51671 * var(--cg-w));
    /* undoes the phone block's centring */
    transform: none;
  }

  .contact__blue-blob-text {
    left: calc(var(--content-inset) + 0.41194 * var(--cg-w));
    top: calc(40px + 0.20536 * var(--cg-w));
    width: calc(0.50782 * var(--cg-w));
    transform: none;
  }

  .contact__details {
    left: calc(var(--content-inset) + 0.06242 * var(--cg-w));
    top: calc(40px + 0.51944 * var(--cg-w));
    /* 180 floor: below it "Tuesday – Sunday 9am to 4pm," breaks up and
       the block grows tall enough to run into the map. */
    width: max(180px, calc(0.29762 * var(--cg-w)));
    transform: none;
    font-size: clamp(12px, calc(0.01587 * var(--cg-w)), 16px);
    line-height: 1.19;
  }

  /* Everything below the cluster keeps the phone stack's internal
     spacing, re-anchored to the map (desktop offsets from map top:
     signoff bg +14, title +507, body +641, CTA +698, canvas +864). */
  .contact__map {
    top: var(--c-map-top);
  }

  .contact__canvas::after {
    top: calc(14px + var(--c-map-top));
  }

  .contact__title {
    top: calc(507px + var(--c-map-top));
  }

  .contact__body {
    top: calc(641px + var(--c-map-top));
  }

  .contact__cta {
    top: calc(698px + var(--c-map-top));
  }

  /* GIFT CARDS ---------------------------------------------
     The mobile block makes the photo card square, which reads far too
     tall once the column is 700px+ wide. Landscape 2:1.4 here; phone
     keeps its square (that's the 430 frame). */
  .gift-cards__photo-wrap {
    aspect-ratio: 2 / 1.4;
  }

  .gift-cards__canvas {
    height: calc(118px + 0.7 * (var(--canvas-width) - 2 * var(--content-inset)));
  }

  /* Copy brought up to the site's mobile body size (14/18 — same as
     nerds, intro, courses and pro shop; it was the only 13/15 on the
     page) and sat 24px under the headline, the same header→copy gap
     those sections use. It was 89px adrift.
     Stack = title 108 + 24 + body 54 + 24 + CTA 25 = 235, centred on
     the PHOTO's midpoint, which sits 20px above the canvas midpoint
     these offsets are measured from. */
  .gift-cards__title {
    transform: translateY(-146.5px);
  }

  /* The headline's phone breaks (FACT: / THE BEST / GIFT TO GIVE /
     A GOLFER / IS GOLF.) stack 5 lines deep — too tall for the shorter
     landscape card. Off below 769 too, so the whole zone runs the
     3-line structure. */
  .gift-cards__title .break-mobile {
    display: none;
  }

  .gift-cards__body {
    font-size: 14px;
    line-height: 18px;
    width: 320px;
    transform: translateY(-14.5px);
  }

  .gift-cards__cta {
    top: calc(50% + 81.5px);
  }
}

/* Bottom of the gift-cards range: below ~680 the card is too narrow for
   "THE BEST GIFT TO GIVE" at 36px, so the headline wraps to 5 lines and
   runs into the copy. Title drops to 26/26 — which holds the 3-line
   structure down to 501 — and the stack offsets are recomputed for the
   shorter title (78 + 24 + 54 + 24 + 25 = 205, centred on the photo). */
@media (min-width: 501px) and (max-width: 679px) {
  .gift-cards__title {
    font-size: 26px;
    line-height: 26px;
    transform: translateY(-131.5px);
  }

  .gift-cards__body {
    transform: translateY(-29.5px);
  }

  .gift-cards__cta {
    top: calc(50% + 66.5px);
  }
}

/* COURSES below 769. The boundary is 768 because that's where the
   headline switches to its 3-line phone break structure — the whole
   section has to make room for the extra line, and the thumb strip and
   arrows no longer fit the narrower panel. */
@media (min-width: 501px) and (max-width: 768px) {
  /* Headline stays at 36/36; it just takes three lines here, ending at
     168 instead of 132. So the copy moves to 192 (the same 24 under it)
     and the whole lower cluster shifts down 64 to keep the air above the
     featured image. */
  .courses__body {
    top: 192px;
  }

  .courses__featured-wrap {
    top: 310px;
  }

  .courses__panel {
    top: calc(330px + 0.5645 * (var(--canvas-width) - 168px));
  }

  .courses__thumbs {
    top: calc(370px + 0.5645 * (var(--canvas-width) - 168px));
  }

  .courses__dots {
    top: calc(540px + 0.5645 * (var(--canvas-width) - 168px));
  }

  .courses__canvas {
    height: calc(650px + 0.5645 * (var(--canvas-width) - 168px));
  }

  /* The 542px thumb strip (3 × 170 + gaps) is wider than the
     panel down here, which pushed the arrows off-canvas. Thumbs step to
     110×62 on a 12px gap → 354 strip, leaving room for the arrows to
     stay outside it (177 + 12 + 32 = 221). script.js measures the step
     off the DOM, so the 3-up window still lands on whole thumbs. */
  .course-thumb,
  .course-thumb img {
    width: 110px;
  }

  .course-thumb img {
    height: 62px;
  }

  .course-thumb__label {
    font-size: 12px;
    line-height: 14px;
    margin-top: 6px;
  }

  .courses__thumbs-track {
    gap: 12px;
  }

  .courses__thumbs {
    width: 354px;
  }

  /* Shorter thumbs → arrows re-centred on the 62px row (+64 shift). */
  .courses__nav {
    top: calc(385px + 0.5645 * (var(--canvas-width) - 168px));
  }

  .courses__nav--prev {
    left: calc(50% - 215px);
  }

  .courses__nav--next {
    left: auto;
    right: calc(50% - 215px);
  }
}

@media (max-width: 500px) {
  /* Hero photo matches the 768 baseline (56%). Earlier
     phone-only shifts (45% / 51% / 54%) cropped the flag too
     aggressively, so the phone view now uses the same value
     as 768. */
  .hero__photo {
    object-position: 56% center;
  }

  /* Hallway photo: full-bleed banner at phone (no inset, no
     radius). At 768 it stays inset by --content-inset with a
     15px radius via the broader mobile rule. */
  .nerds__photo-frame {
    left: 0;
    width: 100%;
    border-radius: 0;
  }

  /* Top nav right edge tightened so the hamburger + login
     icon sit at the same right gutter as the corner circles
     (19px from viewport edge). Left padding still uses
     --logo-inset for the logo's optical anchor. */
  .top-nav__inner {
    padding-right: 19px;
  }

  /* Corner circles nudged down 20px and pulled 5px closer to
     the right edge. BOOK NOW is the bottom-anchored circle;
     HELP stacks 12px above it (4 + 69 + 12 = 85). */
  .help {
    right: 19px;
    bottom: 85px;
  }

  .book-now-fab {
    right: 19px;
    /* 8px came from the 430 Figma frame, which has no browser chrome below it.
       On a real phone that puts the button hard against the bottom edge — and
       in Chrome on iOS, where the address bar lives at the bottom, it reads as
       sitting too low. Match the desktop 24px so it clears the bar in Chrome
       and simply sits better in Safari. (The textbook fix is
       env(safe-area-inset-bottom), but that returns 0 without viewport-fit=cover
       on the viewport meta, and adding that changes layout site-wide — not
       worth it for one button.) */
    bottom: 24px;
  }

  /* Tree is desktop / 768-only — hide on phone to keep the
     phone view identical to the original Figma frame. */
  .gift-cards__tree {
    display: none;
  }

  /* Contact storefront photo stays full-bleed banner (no
     radius) at phone to match the original Figma frame. The
     aspect ratio resolves to ~385 high at 430 wide so the
     layout below is identical to the old fixed-height value. */
  .contact__photo {
    border-radius: 0;
  }

  /* MISC phone view restored: Figma-style overflow photo +
     2-col card grid + hidden "First Time?" card. */
  .misc__photo {
    left: -242px;
    width: 686px;
  }

  .misc__cards {
    left: 30px;
    width: calc(100% - 60px);
    grid-template-columns: 1fr 1fr;
  }

  .misc__cards .misc-card:nth-child(2) {
    display: none;
  }

  /* ---- Phone polish batch (2026-06-03) ---- */

  /* Invite card 10% smaller (scales about its centre, keeps the tilt). */
  .memberships__invite {
    transform: translate(-10px, 10px) rotate(4deg) scale(0.9);
  }

  /* Footnote was white-space:nowrap in the broader block and ran off
     the right edge — let it wrap at the content margin. */
  .memberships__footnote {
    white-space: normal;
    width: calc(100% - 2 * var(--content-inset));
  }

  /* Testimonials header: 3-line break (via .line--mobile spans) +
     25% larger (28 → 35). */
  .testimonials__title {
    font-size: 40px;
    line-height: 40px;
  }

  /* Duotone photo ~20% shorter (cropped from the bottom) so it clears
     the quote; the panel still extends up behind its wavy bottom, so
     no gap appears. Quote nudged down for breathing room under it. */
  .testimonials__photo-wrap {
    height: 303px;
  }

  .testimonial__quote {
    margin-top: 60px;
  }

  /* Arrows further apart (was 75 each). */
  .testimonials__nav--prev { left: 45px; }
  .testimonials__nav--next { right: 45px; }

  /* Divider moved below the now-wrapped footnote (was top:524). */
  .memberships__plans::after {
    top: 545px;
  }

  /* Non-members bullets span the full card width so bullet #2 stops
     wrapping. */
  .memberships__nonmembers .plan__list {
    grid-column: 1 / -1;
  }

  /* Plan + non-members buttons nudged down toward the title's
     first-line baseline. */
  .plan__cta {
    margin-top: 5px;
  }
}
