/* np.css — Single stylesheet for the v2 design system, served at /v2/index and /v2/navygold. Provenance: Claude Design 'Landing page navygold build' export, 2026-09-09. Quick task 260909-ljt. Not part of the production brand system. */

/* ============ 0. FONTS (self-hosted) ============
   Self-hosted woff2 replaces the render-blocking Google Fonts <link>, which
   cost two third-party handshakes before the hero H1 (the LCP element) could
   paint. Only the weights the np pages actually render are shipped, verified
   by probing computed styles on /v2/index, /join-us, /naomi-tran, /private-events:
     Barlow Condensed 300, 700, 800   (600 and 900 were requested but unused)
     Inter 300-700 via ONE variable font (same bytes as a single static weight)
     IBM Plex Mono 400, 600           (500 was requested but unused)
   latin-ext is split out behind unicode-range so it only downloads if the page
   actually needs those glyphs. font-display: swap keeps text visible during load.
   To change weights: re-probe computed styles first, don't guess. */

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('/static/fonts/inter-var-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('/static/fonts/inter-var-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Barlow Condensed';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('/static/fonts/barlow-condensed-300-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Barlow Condensed';
  font-style: normal;
  font-weight: 300;
  font-display: swap;
  src: url('/static/fonts/barlow-condensed-300-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Barlow Condensed';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/barlow-condensed-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Barlow Condensed';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/static/fonts/barlow-condensed-700-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Barlow Condensed';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('/static/fonts/barlow-condensed-800-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Barlow Condensed';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('/static/fonts/barlow-condensed-800-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-600-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/static/fonts/ibm-plex-mono-600-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}


/* ===== TOKENS =====
   Resolved output of a former two-layer token cascade (a base layer fully
   remapped by a second, later :root that overrode every value). These are
   the final rendered values, flattened into one set. Two former slots
   collapsed because they already resolved to the same value: --np-line
   absorbed the old "line-strong" slot (both resolved to the 0.14 subtle
   border; the actual strong 0.35 border lives at --np-line-strong below),
   and --np-accent absorbed the old "gold-ink" slot (both resolved to the
   same court-blue value as --np-accent itself). Do not "fix" these back
   apart — there was never a second value to restore. */
:root {
  --np-navy: #0A1A2F;
  --np-navy-deep: #0C2038;
  --np-footer-bg: #06132A;   /* footer band, one step darker than the page */
  --np-footer-nav: #9DB5D8;  /* big footer nav, muted steel: 9.1:1 on the band */
  --np-footer-link: #6A9DE0; /* footer address + contact links: 6.5:1 on the band */
  --np-surface-raised: #142A4A;
  --np-line: rgba(255,255,255,0.14);
  --np-line-strong: rgba(255,255,255,0.35);
  --np-accent: #6696FF;
  --np-accent-hover: #8FB2FF;
  --np-accent-text: #8FB3FF; /* accent for small text ON navy: 8.4:1 (--np-accent is 6.1:1) */
  --np-accent-press: #5289FF;
  --np-accent-deep: #3A5BA8;
  --np-lime: #D4F94E; /* high-energy CTA fill; navy text on it is ~14.5:1 */
  --np-lime-hover: #C2EA3A;   /* one step down, stays saturated */
  --np-lime-press: #B3DB2E;
  --np-text: #FFFFFF;
  --np-white: #FFFFFF;
  --np-text-on-accent: #0A1A2F;
  --np-text-secondary: #CDCDCD;
  --np-text-muted: #A0A0A0;
  --np-text-faint: #888888;
  --np-danger: #FF8F80;
  --np-font-display: "Barlow Condensed", sans-serif;
  --np-font-body: "Inter", sans-serif;
  --np-font-mono: 'IBM Plex Mono', monospace;
  --np-ease: cubic-bezier(0.215, 0.61, 0.355, 1);
  --np-display-hero: clamp(3rem, 7.5vw, 6.25rem);
  --np-display-h2: clamp(2rem, 5vw, 3.5rem);
  --np-display-h3: clamp(1.5rem, 2.6vw, 2rem);
  --np-eyebrow: 0.8125rem;
  --np-tracking-eyebrow: 0.2em;
  --np-radius-pill: 9999px;
  --np-radius-card: 1.5rem;
  --np-radius-input: 0.75rem;
  --np-reveal-step: 60ms;
  --np-wordmark-size: 1.35rem; /* .np-wordmark size; 1.1rem on phones */
}

/* ===== BASE ===== */
html { scroll-behavior: smooth; }
body { margin: 0; position: relative; background: var(--np-navy); color: var(--np-text); font-family: var(--np-font-body); }
a { color: var(--np-accent); text-decoration: none; }
/* Guarded so iOS/WebKit touch devices never CSS-match :hover on a bare <a>: an
   unqualified :hover color change makes WebKit treat the first tap as "enter
   hover" and require a second tap to navigate. Same fix as .np-btn--lime and
   .np-footer__biglink:hover below; this sitewide rule needed the same guard
   because it matches every anchor, including ones with their own guarded
   :hover rule elsewhere in the cascade. */
@media (hover: hover) {
  a:hover { color: var(--np-accent-hover); }
}
::selection { background: var(--np-accent); color: var(--np-navy); }

/* ===== NAV ===== */
/* Layout only; the surface (background/border) lives in section 3. */
.np-nav {
  position: sticky;
  top: 0;
  z-index: 50;
  padding-top: env(safe-area-inset-top, 0px);
}
.np-nav__inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 12px 20px;
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
}
.np-wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  /* Wordmark only: Barlow Condensed ExtraBold. Condensed faces need a
     larger size than Bricolage to hold the same visual weight, and no
     negative tracking. The court mark is em-sized so it scales with it. */
  font-family: 'Barlow Condensed', var(--np-font-display);
  font-weight: 800;
  font-size: 22px;
  color: var(--np-white);
  letter-spacing: 0.01em;
  border-radius: 6px;
}
.np-wordmark:hover { color: var(--np-white); }
.np-wordmark:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--np-navy), 0 0 0 4px var(--np-accent);
}
/* Court mark scales with the wordmark type (em units) so the mobile size
   keeps it proportional. Color matches the "Pickleball" accent; 14:24
   keeps the invite page's portrait proportions. */
.np-wordmark__court {
  display: block;
  flex: none;
  width: 0.65em;
  height: calc(0.65em * 24 / 14);
  color: var(--np-white);
  opacity: 0.95;
}
/* "Pickleball" drops to Light against ExtraBold "Neighborhood": the weight
   contrast carries the two-part mark, so both words share one color. */
.np-wordmark__text span { font-weight: 300; color: var(--np-white); }
.np-nav__links {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-wrap: wrap;
  margin-left: auto;
}
.np-nav__link {
  color: var(--np-text-secondary);
  font-size: 15px;
  font-weight: 500;
  padding: 10px 12px;
  border-radius: 8px;
}
/* Hover is the same translucent wash as the Sign In pill: over the hero photo
   an opaque navy fill read as a solid object, not a state. Fast task 260912. */
.np-nav__link:hover { color: var(--np-text); background: rgba(255,255,255,0.08); }
/* Sign In: the same quiet outlined pill the root homepage uses (home.templ
   .signin-btn), recoloured with this page's cream. The nav carries no sales
   CTA; "Claim my spot" lives in the hero and the membership card. */
.np-nav__signin {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box; /* root page is border-box via Tailwind; without this the 44px min stacks on padding (66px) */
  min-height: 44px;
  margin-left: 8px;
  padding: 0.625rem 1.25rem;
  border-radius: 9999px;
  border: 1px solid rgba(242, 238, 227, 0.25);
  background: rgba(242, 238, 227, 0.06);
  color: var(--np-text);
  font-size: 0.875rem;
  font-weight: 300;
  line-height: 1.5;
  transition: background 200ms var(--np-ease-nav, cubic-bezier(0.16, 1, 0.3, 1)),
              border-color 200ms var(--np-ease-nav, cubic-bezier(0.16, 1, 0.3, 1)),
              transform 200ms var(--np-ease-nav, cubic-bezier(0.16, 1, 0.3, 1));
}
.np-nav__signin:hover {
  background: rgba(242, 238, 227, 0.12);
  border-color: rgba(242, 238, 227, 0.4);
  color: var(--np-text);
}
.np-nav__signin:active { transform: scale(0.98); }

/* ===== HERO ===== */
.np-hero {
  position: relative;
  isolation: isolate;
  /* 90% of the VISIBLE viewport: the hero now starts at the top of the
     document under a fixed overlay nav (see .np-nav--overlay), so there is
     no 64px nav subtraction. The top of the next section still peeks above
     the fold at 1440x900, signalling scroll. --np-vh is measured by np.js
     (visualViewport / innerHeight) because iOS Safari resolves svh against
     the full screen rather than the visible area. 100svh is the no-JS
     fallback. */
  min-height: clamp(480px, calc(0.9 * var(--np-vh, 100svh)), 860px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
}
.np-hero__media { position: absolute; inset: 0; }
.np-hero__scrim {
  position: absolute;
  inset: 0;
  /* Left-weighted navy wash behind the 600px text column; right ~45% of the
     photo stays clear. The bottom blend runs to solid navy so the photo
     dissolves into the page instead of ending on a hard edge. color-mix on
     the token keeps the blend seamless if the navy value ever changes. The
     top band sits behind the overlay nav so the wordmark and Sign In read
     over the lit facade. */
  background:
    linear-gradient(to bottom,
      color-mix(in srgb, var(--np-navy) 70%, transparent) 0px,
      color-mix(in srgb, var(--np-navy) 35%, transparent) 72px,
      transparent 140px),
    linear-gradient(90deg,
      color-mix(in srgb, var(--np-navy) 90%, transparent) 0%,
      color-mix(in srgb, var(--np-navy) 66%, transparent) 44%,
      transparent 70%),
    linear-gradient(to top,
      var(--np-navy) 0%,
      color-mix(in srgb, var(--np-navy) 85%, transparent) 10%,
      color-mix(in srgb, var(--np-navy) 40%, transparent) 22%,
      transparent 38%);
  pointer-events: none;
}
.np-hero__inner {
  position: relative;
  max-width: 1200px;
  width: 100%;
  margin: 0 auto;
  /* Asymmetric on purpose: the hero centers this block vertically, and the
     extra top padding nudges the headline/subtext/CTA group below the
     optical center so it sits clear of the fixed overlay nav. The phone
     override below replaces this wholesale (the hero bottom-aligns there). */
  padding: 152px 20px 72px;
  box-sizing: border-box;
  pointer-events: none;
}
.np-hero__h1 {
  font-family: var(--np-font-display);
  font-weight: 800;
  font-size: clamp(3rem, 8.5vw, 7rem);
  line-height: 0.98;
  letter-spacing: -0.02em;
  color: #FFFFFF;
  margin: 0 0 24px;
  max-width: 720px;
  text-wrap: balance;
}
/* Desktop subtext runs larger and wider than a body paragraph so it reads
   as part of the hero, not a caption under it: up to 24px in a ~44-character
   measure (about half the 1200px content column). Phone keeps the tighter
   17px / 34ch column in the responsive block below. */
.np-hero__sub {
  font-size: clamp(1.125rem, 2vw, 1.5rem);
  line-height: 1.45;
  color: #E6EDF3;
  margin: 0 0 32px;
  max-width: 44ch;
  text-wrap: pretty;
}
.np-hero__actions {
  display: flex;
  align-items: center;
  gap: 20px;
  flex-wrap: wrap;
  pointer-events: auto;
}
/* Secondary hero action ("See this week's schedule"): the escape hatch for
   visitors not ready to commit. Vertical padding only, so the 48px tap
   target does not shift its x-position next to the button on desktop. */
.np-hero__underline-link {
  display: inline-block;
  padding: 12px 0;
  border-radius: 8px;
  color: #FFFFFF;
  font-weight: 600;
  font-size: 16px;
  line-height: 1.5;
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1.5px;
  text-decoration-color: rgba(255,255,255,0.6);
}
.np-hero__underline-link:hover { color: var(--np-accent-hover); }

/* ===== SECTION LAYOUT ===== */
.np-section {
  max-width: 1200px;
  margin: 0 auto;
  padding: clamp(64px, 9vw, 110px) 20px 0;
  box-sizing: border-box;
}
.np-section--anchor { scroll-margin-top: 80px; }
.np-section--narrow { max-width: 820px; }

.np-h2 {
  font-family: var(--np-font-display);
  font-weight: 700;
  font-size: clamp(30px, 4vw, 46px);
  letter-spacing: -0.015em;
  line-height: 1.1;
  margin: 0;
}
.np-h2--tight { margin: 0 0 8px; }
.np-h2--sm { margin: 0 0 12px; }
.np-h2--lg { margin: 0 0 28px; }
.np-h2--split { flex: 1 1 320px; min-width: 0; }
.np-lede {
  color: var(--np-text-secondary);
  font-size: 17px;
  line-height: 1.6;
  margin: 0;
  text-wrap: pretty;
}
.np-lede--split { flex: 1 1 340px; min-width: 0; }
.np-lede--56ch { margin: 0 0 36px; max-width: 56ch; }
.np-caption--tight { margin: 0 0 32px; }
.np-split {
  display: flex;
  gap: 32px 64px;
  flex-wrap: wrap;
  align-items: flex-end;
  margin-bottom: 36px;
}
.np-grid-pros {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 14px;
}
.np-photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 14px;
}
.np-photo { height: 300px; border-radius: 14px; overflow: hidden; }
/* The golden-hour game shot is half sky above a low horizon. Cover-cropping
   a 4:3 frame into the wider tile trims about a fifth of its height, so the
   crop is anchored to the bottom edge: it loses empty sky and keeps the
   rally, the crowd on the turf and the hills. No zoom; the near players are
   already close and a scale would push the far court out of frame. */
.np-photo--ground img { object-position: 50% 100%; }
.np-cta-section { padding-bottom: 0; }
.np-btn--auto-top { margin-top: auto; }

.np-photo img,
.np-hero__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 50%;
  display: block;
}

/* Hero-only anchor. Kept separate from the shared .np-photo rule above so
   tuning the hero crop never re-crops the photo grid further down the page.
   The overhead shot centers the courts horizontally; the vertical value
   slides the visible window down the source, which moves the picture up. */
.np-hero__media img {
  object-position: 50% 75%;
}

.np-mono-accent {
  font-family: var(--np-font-mono);
  font-size: 14px;
  color: var(--np-accent);
  margin: 0;
}

/* ===== BUTTONS ===== */
.np-btn {
  display: block;
  text-align: center;
  background: var(--np-accent);
  color: var(--np-navy);
  font-weight: 700;
  font-size: 17px;
  padding: 16px 24px;
  border-radius: 12px;
  border: none;
  cursor: pointer;
}
.np-btn:hover { background: var(--np-accent-press); color: var(--np-navy); }
.np-btn--hero {
  display: inline-block;
  box-shadow: 0 4px 24px rgba(102, 150, 255, 0.35);
  padding: 16px 28px;
  font-size: 17px;
}
.np-btn--hero:hover { background: var(--np-accent-hover); color: var(--np-navy); }
.np-btn--final {
  display: inline-block;
  flex: 0 0 auto;
  font-size: 18px;
  padding: 18px 32px;
  box-shadow: 0 4px 24px rgba(102, 150, 255, 0.3);
}
.np-btn--final:hover { background: var(--np-accent-hover); color: var(--np-navy); }
.np-btn--ghost {
  display: inline-block;
  align-self: flex-start;
  border: 1.5px solid var(--np-accent);
  color: var(--np-accent);
  background: transparent;
  font-weight: 600;
  font-size: 15px;
  padding: 11px 18px;
  border-radius: 10px;
  cursor: pointer;
}
.np-btn--ghost:hover { background: rgba(102, 150, 255, 0.12); color: var(--np-accent-hover); }

/* ===== SESSIONS BOARD ===== */
.np-board {
  scroll-margin-top: 80px;
  background: var(--np-navy-deep);
  border: 1px solid var(--np-line);
  border-radius: 20px;
  padding: clamp(22px, 4vw, 40px);
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
}
.np-board__head {
  display: flex;
  align-items: center;
  gap: 14px 24px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
.np-board__head h3 {
  font-family: var(--np-font-display);
  font-weight: 700;
  font-size: clamp(22px, 3vw, 30px);
  margin: 0;
  flex: 1 1 auto;
}
.np-board__caption {
  font-family: var(--np-font-mono);
  font-size: 12.5px;
  color: var(--np-text-faint);
  margin: 0 0 14px;
}
.np-chips { display: flex; gap: 8px; flex-wrap: wrap; }
.np-chip {
  font-family: var(--np-font-mono);
  font-size: 13.5px;
  font-weight: 500;
  padding: 9px 16px;
  border-radius: 999px;
  cursor: pointer;
  background: transparent;
  color: var(--np-text-secondary);
  border: 1px solid var(--np-line);
}
.np-chip.is-active {
  background: var(--np-accent);
  color: var(--np-navy-deep);
  border-color: var(--np-accent);
}

/* ===== SESSION ROWS ===== */
/* Each row is one link to the session page. The Reserve pill is a visual
   affordance inside it, not a second control, so the whole row is the tap
   target and a dozen sessions stay a list rather than a stack of buttons. */
.np-row {
  display: flex;
  align-items: center;
  gap: 8px 20px;
  flex-wrap: wrap;
  padding: 15px 0;
  border-top: 1px solid var(--np-surface-raised);
  color: var(--np-text);
}
.np-row:hover { color: var(--np-text); }
.np-row:focus-visible { border-radius: 8px; }
/* Chip filter toggles the hidden attribute; the UA [hidden] rule loses to the
   author display above, so restate it. */
.np-row[hidden] { display: none; }
.np-row__date {
  font-family: var(--np-font-mono);
  font-size: 15px;
  font-weight: 600;
  color: var(--np-text);
  flex: 0 0 108px;
}
.np-row__time {
  font-family: var(--np-font-mono);
  font-size: 14px;
  color: var(--np-text-muted);
  flex: 0 0 164px; /* fits "10:00 AM – 12:30 PM" (19 mono chars at 14px) on one line */
}
.np-row__meta {
  flex: 1 1 200px;
  min-width: 160px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.np-row__type { font-weight: 600; font-size: 15.5px; color: var(--np-text); }
.np-row__type--clinic { color: var(--np-accent-hover); }
.np-row__level { font-size: 13.5px; color: var(--np-text-muted); }
/* "Members free" says a member pays nothing while the price column still shows
   what everyone else pays. It must read as a different KIND of fact from the
   muted level beside it without reading as a second link: the row already has
   a blue clinic title and a blue chevron, and the accent blue here made three
   blues fight on one line. So it lifts to near-white at a smaller size and
   heavier weight. That is brighter than the level text (contrast, not hue) and
   leaves the lime CTA fill to the one primary action it owns. */
.np-row__members {
  font-size: 13px;
  font-weight: 600;
  color: var(--np-text-secondary);
  white-space: nowrap;
}
.np-row__price {
  font-family: var(--np-font-mono);
  font-size: 14px;
  color: var(--np-text);
  flex: 0 0 84px;
}
.np-row__spots {
  font-family: var(--np-font-mono);
  font-size: 13px;
  color: var(--np-text-muted);
  flex: 0 0 126px; /* fits "Spots available" on one line */
  white-space: nowrap;
}
.np-row__spots.low { color: var(--np-accent-hover); }
.np-row__join {
  background: var(--np-accent);
  color: var(--np-navy-deep);
  font-weight: 700;
  font-size: 14px;
  padding: 9px 20px;
  border-radius: 9px;
  margin-left: auto;
  /* Same width for Reserve and Details so the price and spots columns don't
     shift on a full row. Both words set to about 98px at this size, so the
     floor sits just above that and neither pill grows past the other. */
  min-width: 100px;
  box-sizing: border-box;
  text-align: center;
}
.np-row:hover .np-row__join { background: var(--np-accent-hover); color: var(--np-navy-deep); }
/* Full session: the row still links through, but as a quiet ghost so it stops
   reading as a call to join. Two classes so it wins over the design-system
   layer's single-class .np-row__join override. */
.np-row__join.np-row__join--full {
  background: transparent;
  color: var(--np-text-muted);
  border: 1px solid var(--np-line);
  padding: 8px 19px;
}
.np-row:hover .np-row__join.np-row__join--full { background: var(--np-surface-raised); color: var(--np-text); }
.np-row--empty {
  color: var(--np-text-muted);
  font-size: 15px;
  padding: 15px 0;
  border-top: 1px solid var(--np-surface-raised);
}

/* ===== PROS ===== */
.np-pro {
  background: var(--np-navy-deep);
  border: 1px solid var(--np-line);
  border-radius: 16px;
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.np-pro__avatar {
  width: 104px;
  height: 104px;
  background: rgba(102, 150, 255, 0.18);
  border: 1px solid var(--np-accent);
  border-radius: 50%;
  /* Initials stand in until photos land; aria-hidden, the h3 carries the name. */
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--np-font-display);
  font-weight: 700;
  font-size: 30px;
  letter-spacing: 0.02em;
  color: var(--np-accent);
  overflow: hidden;
}
/* Photo avatar: the same accent ring, filled by the headshot instead of initials. */
.np-pro__avatar--photo { background: none; }
.np-pro__avatar--photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.np-pro h3 {
  font-family: var(--np-font-display);
  font-weight: 700;
  font-size: 21px;
  margin: 0 0 4px;
}
.np-pro p {
  margin: 0;
  color: var(--np-text-secondary);
  font-size: 14.5px;
  line-height: 1.5;
}
.np-pro .np-mono-accent { font-size: 13px; }

/* ===== FAQ ===== */
.np-faq { display: flex; flex-direction: column; }
.np-faq__item { border-top: 1px solid var(--np-line); }
.np-faq__q {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 20px 4px;
  text-align: left;
  color: var(--np-text);
  font-family: var(--np-font-body);
  font-size: 17px;
  font-weight: 600;
}
.np-faq__q:hover { color: var(--np-accent-hover); }
.np-faq__icon {
  font-family: var(--np-font-mono);
  color: var(--np-accent);
  font-size: 20px;
  flex: 0 0 auto;
}
.np-faq__a {
  margin: 0;
  padding: 0 4px 22px;
  color: var(--np-text-secondary);
  font-size: 15.5px;
  line-height: 1.65;
  max-width: 62ch;
}

/* ===== FINAL CTA ===== */
.np-cta {
  background: linear-gradient(135deg, #0E2848 0%, #10233D 100%);
  border: 1px solid var(--np-line);
  border-radius: 22px;
  padding: clamp(40px, 6vw, 72px);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px 40px;
  flex-wrap: wrap;
}
.np-cta__h2 {
  font-family: var(--np-font-display);
  font-weight: 800;
  font-size: clamp(28px, 4vw, 44px);
  letter-spacing: -0.015em;
  line-height: 1.1;
  margin: 0;
  max-width: 20ch;
  text-wrap: pretty;
}

/* ===== FOOTER ===== */
/* The footer is a full-width band one step darker than the page, opened by
   a hairline. Its side padding centers a 1200px content column that lines
   up with .np-nav__inner, so the two wordmarks share an x position.

   The gap above the band is the margin-top alone now that the band has its
   own background: the hairline marks where the page ends, so the footer no
   longer needs the extra leading it used to borrow from the first label. */
.np-footer {
  margin-top: clamp(64px, 6.1vw, 88px);
  padding: 56px max(20px, calc((100% - 1200px) / 2)) 48px;
  box-sizing: border-box;
  background: var(--np-footer-bg);
  border-top: 1px solid rgba(255, 255, 255, 0.10);
  color: var(--np-text-muted);
  font-size: 14px;
}

/* ===== ACCESSIBILITY ===== */
a:focus-visible, button:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--np-navy), 0 0 0 4px var(--np-accent);
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition: none !important; animation: none !important; }
}

/* ===== RESPONSIVE ===== */
@media (max-width: 767px) {
  /* Phone hero: text block bottom-aligned over a bottom-up scrim so the top
     half of the photo stays clean; CTAs stack with the primary full-width. */
  /* Phones: the hero fills the visible height exactly (no 10% peek: under a
     browser toolbar it is dead space), and the tighter bottom padding sits
     the schedule link ~36px above the toolbar. --np-vh comes from np.js. The
     overlay nav is fixed, so there is no 64px nav subtraction here either. */
  .np-hero { justify-content: flex-end; min-height: clamp(480px, var(--np-vh, 100svh), 860px); }
  .np-hero__inner { padding: 96px 20px 24px; }
  .np-hero__h1 { margin-bottom: 20px; }
  .np-hero__sub { font-size: 1.0625rem; line-height: 1.5; margin-bottom: 28px; max-width: 34ch; }
  .np-hero__scrim {
    background:
      linear-gradient(to bottom,
        color-mix(in srgb, var(--np-navy) 70%, transparent) 0px,
        color-mix(in srgb, var(--np-navy) 35%, transparent) 72px,
        transparent 140px),
      linear-gradient(to top,
        var(--np-navy) 0%,
        color-mix(in srgb, var(--np-navy) 74%, transparent) 40%,
        color-mix(in srgb, var(--np-navy) 40%, transparent) 62%,
        transparent 100%);
  }
  .np-hero__actions { flex-direction: column; align-items: stretch; gap: 6px; }
  .np-btn--hero { text-align: center; }
  /* Stacked under the full-width button: same width, centered text, 48px
     tall. The 6px gap plus 12px padding keeps the visual distance from the
     button at 18px while the whole row is tappable. */
  .np-hero__underline-link { align-self: stretch; text-align: center; padding: 12px 16px; }
  /* Final CTA matches the hero: full-width button under the headline. */
  .np-btn--final { flex: 1 1 100%; text-align: center; }
}

@media (max-width: 640px) {
  .np-nav__inner { flex-wrap: nowrap; }
  .np-nav__link { display: none; }

  /* Phone board: each session collapses to two lines.
       date   time                       $10  ›
       type · level              3 left
     The whole row is already the link, so the filled Join pill goes away
     and a chevron in a shared right column says "tap". Price sits beside
     the chevron, centered on both lines, so the row's two "answers" (what
     it costs, tap to join) read as one group. Spots keeps its own column
     so a wide spots cell can't starve the time.
     Twelve sessions fit in about one screen instead of six. */
  .np-row {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto auto auto;
    grid-template-areas:
      "date time time price chev"
      "meta meta spots price chev";
    column-gap: 8px;
    row-gap: 3px;
    align-items: baseline;
    padding: 12px 0;
  }
  .np-row--empty { display: block; }
  .np-row__date { grid-area: date; font-size: 13.5px; }
  .np-row__time { grid-area: time; font-size: 12.5px; }
  .np-row__price { grid-area: price; align-self: center; font-size: 14px; justify-self: end; margin-left: 4px; }
  .np-row__spots { grid-area: spots; font-size: 12.5px; justify-self: end; }
  /* "Spots available" is the default state and just noise in a compact list;
     only the signals that change a decision ("3 left", "Full") stay. */
  .np-row__spots:not(.low) { display: none; }
  .np-row:has(.np-row__join--full) .np-row__spots { display: block; }
  .np-row__meta {
    grid-area: meta;
    min-width: 0;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0 6px;
  }
  .np-row__type { font-size: 14px; }
  .np-row__level { font-size: 13px; }
  .np-row__level:not(:empty)::before { content: "\00B7"; margin-right: 6px; color: var(--np-text-faint); }
  /* At 375px the meta line runs out of room and the flag wraps to its own
     line. A leading "·" would then dangle at the start of that line, so the
     separator hangs in the gap (right of the level) instead of prefixing the
     flag. The level keeps its own prefix dot because it never wraps first. */
  .np-row__members { font-size: 12.5px; }
  .np-row__level:not(:empty)::after { content: "\00B7"; margin-left: 6px; color: var(--np-text-faint); }
  /* Only draw that trailing dot when a flag actually follows it. */
  .np-row__meta:not(:has(.np-row__members)) .np-row__level::after { content: none; }
  .np-row__join { display: none; }
  .np-row[data-type]::after {
    content: "";
    grid-area: chev;
    align-self: center;
    width: 8px;
    height: 14px;
    margin-left: 2px;
    background: currentColor;
    color: var(--np-accent);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 14' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1l6 6-6 6'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 14' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1l6 6-6 6'/%3E%3C/svg%3E") center / contain no-repeat;
  }
  /* Full session: the chevron goes quiet, matching the ghost Details pill on desktop. */
  .np-row:has(.np-row__join--full)::after { color: var(--np-text-faint); }
}

/* ===== DESIGN SYSTEM LAYER ===== */

body { line-height: 1.55; -webkit-font-smoothing: antialiased; }
a { color: var(--np-accent); }
/* Guarded so iOS/WebKit touch devices never CSS-match :hover on a bare <a>: see
   the matching guard on the BASE a:hover rule above for the full rationale. */
@media (hover: hover) {
  a:hover { color: var(--np-accent-hover); }
}
::selection { background: var(--np-accent); color: var(--np-text-on-accent); }

/* ============ 2. TYPE ============ */
.np-wordmark,
.np-hero__h1,
.np-h2,
.np-cta__h2,
.np-board__head h3,
.np-pro h3 {
  font-family: var(--np-font-display);
  font-weight: 800;
  text-transform: uppercase;
  line-height: 0.95;
  letter-spacing: 0.01em;
}
.np-hero__h1 { font-size: var(--np-display-hero); }
/* Short viewports (landscape phones, small laptops): the 480px hero floor
   only helps if the headline shrinks with it. Extra top padding hands some
   photo-air back to the content instead of the nav crowding the h1. */
@media (max-height: 700px) {
  .np-hero__h1 { font-size: clamp(36px, 10vw, 44px); }
  .np-hero { padding-top: 24px; }
}
.np-h2, .np-cta__h2 { font-size: var(--np-display-h2); }
.np-board__head h3, .np-pro h3 { font-size: var(--np-display-h3); }
.np-wordmark { font-size: var(--np-wordmark-size); letter-spacing: 0.02em; }

/* Eyebrows replace the export's mono captions; mono stays on data. */
.np-board__caption,
.np-mono-accent {
  font-family: var(--np-font-body);
  font-size: var(--np-eyebrow);
  letter-spacing: var(--np-tracking-eyebrow);
  text-transform: uppercase;
  font-weight: 500;
}
/* The caption's one job is the member fact: the $15 in the price column is the
   non-member drop-in, and a member reading eight of those needs telling it is
   not them. The date range it used to carry is already legible in the rows, so
   this sits at the secondary text colour rather than the faint caption grey it
   used when it was the quiet half of a pair. */
.np-board__caption { color: var(--np-text-secondary); }
.np-mono-accent { color: var(--np-accent); }
.np-btn, .np-btn--ghost, .np-nav__signin, .np-nav__link, .np-chip, .np-row__join {
  font-family: var(--np-font-body);
}

/* ============ 3. SURFACES, BORDERS, RADII ============ */
.np-nav {
  background: var(--np-navy-deep);
  backdrop-filter: none;
  border-bottom: 1px solid var(--np-line);
}
.np-nav__link { border-radius: var(--np-radius-pill); }
/* Current page reads at full ink at rest; hover wash still applies. */
.np-nav__link[aria-current="page"] { color: var(--np-text); }
.np-nav__signin { border-color: var(--np-line); background: rgba(255,255,255,0.04); color: var(--np-text); }
.np-nav__signin:hover { border-color: var(--np-line-strong); background: rgba(255,255,255,0.08); color: var(--np-text); }

/* Landing hero: the nav overlays the photo. Transparent with no hairline at
   the top of the page (the hero's top scrim band keeps the wordmark and
   Sign In legible); np.js adds .is-scrolled once the nav-height sentinel
   leaves the viewport and the solid bar returns. /v2/index and
   /private-events use this modifier; /join-us keeps the plain solid .np-nav.
   Quick task 260912-gmn; events hero 2026-09-17. */
.np-nav--overlay {
  position: fixed;
  left: 0;
  right: 0;
  background: transparent;
  border-bottom-color: transparent;
  transition: background-color 220ms ease, border-color 220ms ease;
}
.np-nav--overlay.is-scrolled {
  background: var(--np-navy-deep);
  border-bottom-color: var(--np-line);
}
/* 64px-tall invisible marker at the top of the document. When it has fully
   left the viewport the user has scrolled past the nav, so the overlay nav
   goes solid. Absolute so it takes no space in the flow. */
.np-nav-sentinel {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  height: 64px;
  pointer-events: none;
}

.np-pro,
.np-board,
.np-cta,
.np-photo {
  border-radius: var(--np-radius-card);
}
.np-board { box-shadow: none; }               /* zero decoration */
.np-cta { background: var(--np-navy-deep); }      /* flat, no gradient */

/* Pro avatar: accent tint */
.np-pro__avatar { background: rgba(102,150,255,0.12); border-color: var(--np-accent); }

/* Sessions board rows and chips */
.np-row { border-top-color: var(--np-line); }
.np-row__type--clinic { color: var(--np-accent); }
.np-row__spots.low { color: var(--np-accent); }
.np-chip {
  border-radius: var(--np-radius-pill);
  border-color: var(--np-line);
  color: var(--np-text-secondary);
  transition: all 150ms var(--np-ease);
}
.np-chip.is-active,
.np-chip[aria-pressed="true"] {
  background: var(--np-accent);
  color: var(--np-text-on-accent);
  border-color: var(--np-accent);
  font-weight: 600;
}

/* Hero photo: full color, warm-graded in the asset itself (no CSS filter);
   the base navy scrim applies unchanged. */

/* ============ 4. BUTTONS + HOVER / MOTION ============ */
.np-btn,
.np-btn--hero,
.np-btn--final,
.np-row__join {
  background: var(--np-accent);
  color: var(--np-text-on-accent);
  border-radius: var(--np-radius-pill);
  box-shadow: none;
  font-weight: 600;
  transition: transform 150ms var(--np-ease), box-shadow 150ms var(--np-ease), background 150ms var(--np-ease);
}
.np-btn:hover,
.np-btn--hero:hover,
.np-btn--final:hover,
.np-row:hover .np-row__join {
  background: var(--np-accent);
  color: var(--np-text-on-accent);
  transform: translateY(-1px);
  box-shadow: 0 8px 24px rgba(102,150,255,0.28);
}

.np-btn--ghost {
  background: transparent;
  color: var(--np-text);
  border: 1px solid var(--np-line);
  border-radius: var(--np-radius-pill);
  font-weight: 500;
  transition: border-color 150ms var(--np-ease), transform 150ms var(--np-ease);
}
.np-btn--ghost:hover {
  background: transparent;
  color: var(--np-text);
  border-color: var(--np-line-strong);
  transform: translateY(-1px);
}

.np-faq__q:hover { color: var(--np-accent-hover); }
.np-faq__icon { color: var(--np-accent); }

a:focus-visible, button:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--np-navy), 0 0 0 4px var(--np-accent);
}

/* Scroll reveal with the design system's 60ms stagger. */
.reveal { opacity: 0; transform: translateY(14px); transition: opacity 500ms var(--np-ease), transform 500ms var(--np-ease); }
.reveal.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  .np-btn:hover, .np-btn--hero:hover, .np-btn--final:hover,
  .np-row:hover .np-row__join, .np-btn--ghost:hover { transform: none; }
}

@media (max-width: 640px) {
  /* Condensed caps wordmark runs wide; keep wordmark + Sign In on one row. */
  :root { --np-wordmark-size: 1.1rem; }
}

/* ===== JOIN-US / FORMS + CARDS ===== */

.np-card {
  background: var(--np-navy-deep);
  border: 1px solid var(--np-line);
  border-radius: var(--np-radius-card);
  padding: clamp(24px, 4vw, 40px);
  box-sizing: border-box;
}

.np-eyebrow {
  font-family: var(--np-font-body);
  font-size: var(--np-eyebrow);
  letter-spacing: var(--np-tracking-eyebrow);
  text-transform: uppercase;
  font-weight: 600;
  color: var(--np-accent-text); /* .np-card--sage and .np-band--cream override this per surface */
  display: block;
  margin: 0 0 12px;
}

.np-benefit__title {
  font-family: var(--np-font-display);
  font-weight: 700;
  font-size: clamp(20px, 2.2vw, 24px);
  text-transform: uppercase;
  line-height: 1;
  margin: 0 0 8px;
  color: var(--np-text);
}

.np-benefit__body {
  color: var(--np-text-secondary);
  font-size: 16px;
  line-height: 1.6;
  margin: 0;
}
/* Phones: these rows are read at arm's length between games; 17px keeps
   them comfortable without wrapping the titles. */
@media (max-width: 640px) {
  .np-benefit__body { font-size: 17px; }
}

.np-price {
  display: flex;
  align-items: baseline;
  gap: 8px;
  /* The lede above has no bottom margin, so without this the $99 cap sat
     on the paragraph's line box while the note below it got 8px plus the
     figure's leading. A gap wider than the h1/lede 16px marks the shift
     from pitch to price and gives the heaviest glyphs on the page some air. */
  margin-top: 28px;
}

.np-price__figure {
  font-family: var(--np-font-display);
  font-weight: 800;
  font-size: clamp(56px, 8vw, 88px);
  line-height: 0.95;
  letter-spacing: -0.01em;
  color: var(--np-text);
}

.np-price__unit {
  font-size: 17px;
  font-weight: 500;
  color: var(--np-text-muted);
}

.np-price__note {
  color: var(--np-text-secondary);
  font-size: 15px;
  margin: 8px 0 0;
}

/* Price anchor: one line of break-even math under the $99. Reads as a
   sentence, not a stat, so it sits a step below the note in weight. */
.np-price__anchor {
  color: var(--np-text-secondary);
  font-size: 14px;
  line-height: 1.5;
  margin: 14px 0 0;
  max-width: 34ch;
  text-wrap: pretty;
}

.np-divider {
  height: 1px;
  background: var(--np-line);
  margin: 22px 0;
}

.np-checklist {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.np-checklist li {
  display: flex;
  /* Top-aligned so a bullet that wraps on a phone keeps its check on the
     first line; the check's margin-top centers it on that line. */
  align-items: flex-start;
  gap: 12px;
  font-size: 16px;
  line-height: 1.5;
  color: var(--np-text);
}

.np-check {
  width: 20px;
  height: 20px;
  margin-top: 2px;
  border-radius: var(--np-radius-pill);
  /* Page accent, court blue on every page. */
  background: var(--np-page-accent, var(--np-accent));
  color: var(--np-navy);
  font-size: 11px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
}

.np-footnote {
  color: var(--np-text-faint);
  font-size: 13px;
  text-align: center;
  margin: 18px 0 0;
}

.np-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.np-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.np-label {
  font-size: 13px;
  font-weight: 600;
  color: var(--np-text-secondary);
  letter-spacing: 0.02em;
}

.np-input {
  width: 100%;
  box-sizing: border-box;
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--np-line);
  border-radius: var(--np-radius-input);
  color: var(--np-text);
  padding: 14px 16px;
  font-size: 16px;
  font-family: var(--np-font-body);
  transition: border-color 150ms var(--np-ease), box-shadow 150ms var(--np-ease);
}

.np-input::placeholder {
  color: var(--np-text-faint);
}

.np-input:hover {
  border-color: var(--np-line-strong);
}

.np-input:focus, .np-input:focus-visible {
  outline: none;
  border-color: var(--np-accent);
  box-shadow: 0 0 0 3px rgba(102,150,255,0.25);
}

.np-input.is-invalid {
  border-color: var(--np-danger);
}

.np-field__error {
  font-size: 13px;
  color: var(--np-danger);
  margin: 0;
}

.np-form__error {
  background: rgba(255,143,128,0.10);
  border: 1px solid var(--np-danger);
  color: var(--np-text);
  border-radius: var(--np-radius-input);
  padding: 12px 14px;
  font-size: 14px;
  margin: 0 0 4px;
}

.np-btn--block {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: 9px;
}

/* Lime CTA: the single high-energy button on /join-us ("Claim my spot").
   Hover darkens one step and lifts; press darkens again and settles; focus
   is an outline (not a box-shadow) so it never fights the hover glow. The
   unconditional :hover rule neutralizes the shared blue .np-btn:hover for
   touch devices, where the @media (hover: hover) rule does not apply.
   Declared before :disabled so the disabled grey still wins when hovered. */
.np-btn--lime {
  background: var(--np-lime);
  color: var(--np-navy);
  transition: background-color 120ms ease, transform 120ms ease, box-shadow 120ms ease;
}
.np-btn--lime:hover {
  background: var(--np-lime);
  color: var(--np-navy);
  transform: none;
  box-shadow: none;
}
.np-btn--lime:focus-visible {
  outline: 2px solid var(--np-lime);
  outline-offset: 3px;
  box-shadow: none; /* drop the shared blue focus ring */
}
@media (hover: hover) {
  .np-btn--lime:hover {
    background: var(--np-lime-hover);
    transform: translateY(-1px);
    box-shadow: 0 8px 24px rgba(212, 249, 78, 0.22);
  }
}
.np-btn--lime:active {
  background: var(--np-lime-press);
  transform: translateY(0);
  box-shadow: 0 3px 10px rgba(212, 249, 78, 0.18);
}
@media (prefers-reduced-motion: reduce) {
  .np-btn--lime { transition: background-color 120ms ease; }
  .np-btn--lime:hover, .np-btn--lime:active { transform: none; }
}

.np-btn:disabled {
  background: var(--np-surface-raised);
  color: var(--np-text-muted);
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.np-btn.is-loading {
  pointer-events: none;
  opacity: 0.85;
}

.np-spinner {
  width: 1em;
  height: 1em;
  border-radius: var(--np-radius-pill);
  border: 2px solid transparent;
  border-top-color: currentColor;
  animation: np-spin 0.6s linear infinite;
  flex: none;
}

@keyframes np-spin {
  to { transform: rotate(360deg); }
}

/* ---- shared content column for /join-us sections ---- */
.np-section--page { max-width: 960px; }

/* ---- page header ---- */
.np-page-head { padding-top: clamp(48px, 7vw, 80px); }
.np-page-head__h1 { font-family: var(--np-font-display); font-weight: 800; text-transform: uppercase; font-size: var(--np-display-h2); line-height: 0.95; letter-spacing: 0.01em; margin: 0 0 16px; max-width: 18ch; text-wrap: balance; color: var(--np-text); }
.np-page-head__sub { color: var(--np-text-secondary); font-size: 17px; line-height: 1.6; margin: 0; max-width: 48ch; text-wrap: pretty; }

/* ---- pricing + form panel ---- */
.np-section--panel { padding-top: clamp(32px, 5vw, 48px); }
.np-panel { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); background: var(--np-navy-deep); border: 1px solid var(--np-line); border-radius: var(--np-radius-card); overflow: hidden; }
.np-panel__col { padding: clamp(24px, 4vw, 40px); min-width: 0; }
.np-panel__col + .np-panel__col { border-left: 1px solid var(--np-line); }
.np-panel__h { font-family: var(--np-font-display); font-weight: 700; text-transform: uppercase; font-size: var(--np-display-h3); line-height: 1; letter-spacing: 0.01em; margin: 0 0 8px; color: var(--np-text); }
.np-panel__sub { color: var(--np-text-secondary); font-size: 15px; line-height: 1.55; margin: 0 0 22px; max-width: 46ch; text-wrap: balance; }
@media (max-width: 899px) { .np-panel { grid-template-columns: 1fr; } .np-panel__col + .np-panel__col { border-left: 0; border-top: 1px solid var(--np-line); } }

/* ---- left-aligned footnote variant (panel columns) ---- */
.np-footnote--left { text-align: left; margin-top: 16px; }

/* ---- photo band + benefits list ---- */
.np-photo-band { width: 100%; aspect-ratio: 21 / 9; max-height: 380px; border-radius: var(--np-radius-card); overflow: hidden; margin: 28px 0 8px; }
.np-photo-band img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 38%; display: block; }
.np-benefit-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 48px; }
.np-benefit-list li { padding: 22px 0; border-top: 1px solid var(--np-line); }
@media (max-width: 639px) { .np-benefit-list { grid-template-columns: 1fr; } }

/* ---- instructor cards (/private-events, /{pro-slug}) ---- */
/* Photo bleeds to the card's left edge: the card carries no padding, the
   body column does, and overflow:hidden lets the card radius clip the image.
   Same 5/7 split as .np-panel. */
.np-instructor { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); background: var(--np-navy-deep); border: 1px solid var(--np-line); border-radius: var(--np-radius-card); overflow: hidden; }
.np-instructor + .np-instructor { margin-top: 24px; }
.np-instructor__photo { position: relative; min-height: 100%; }
.np-instructor__photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 30%; display: block; }
.np-instructor__body { padding: clamp(24px, 4vw, 40px); min-width: 0; }
.np-instructor__name { font-family: var(--np-font-display); font-weight: 700; text-transform: uppercase; font-size: var(--np-display-h3); line-height: 1; margin: 0 0 6px; color: var(--np-text); }
.np-instructor__title { color: var(--np-text-secondary); font-size: 15px; margin: 0 0 10px; }
/* Title row: "Pro" plus an optional social glyph. The glyph is a 20px
   outline (same paths as the home footer's Instagram mark) inside a 44px
   hit area; the negative vertical margin keeps that hit area from
   inflating the row. Accent at rest so it reads as a link, not an icon. */
.np-instructor__meta { display: flex; align-items: center; gap: 2px; margin: 0 0 10px; }
.np-instructor__meta .np-instructor__title { margin: 0; }
.np-instructor__social { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; margin: -12px 0; color: var(--np-accent); border-radius: 8px; transition: color 150ms cubic-bezier(0.16, 1, 0.3, 1); }
.np-instructor__social:hover { color: var(--np-accent-hover); }
.np-instructor__social svg { display: block; }
.np-instructor__bio { color: var(--np-text-secondary); font-size: 15px; line-height: 1.6; margin: 0 0 14px; max-width: 60ch; text-wrap: pretty; }
/* Inline links inside a bio paragraph: the site's accent underline treatment,
   regular weight so a linked phrase sits in the sentence instead of shouting. */
.np-instructor__bio a { color: var(--np-accent); white-space: nowrap; text-decoration: underline; text-underline-offset: 4px; text-decoration-thickness: 1.5px; text-decoration-color: rgba(102, 150, 255, 0.5); border-radius: 3px; transition: color 150ms cubic-bezier(0.16, 1, 0.3, 1), text-decoration-color 150ms cubic-bezier(0.16, 1, 0.3, 1); }
.np-instructor__bio a:hover { color: var(--np-accent-hover); text-decoration-color: currentColor; }
@media (max-width: 639px) { .np-instructor { grid-template-columns: 1fr; } .np-instructor__photo { aspect-ratio: 4 / 3; min-height: 0; } }

/* ---- select/textarea (/private-events) ----
   Appended at the very end of the file on purpose: the "3. SURFACES" block
   (~line 837) overrides earlier same-specificity rules, so anything meant to
   layer on top of .np-input must come after it. These modifiers are always
   used alongside .np-input (e.g. class="np-input np-input--select") and only
   set what a native select/textarea needs on top of the shared input look. */
.np-input--select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%238FA3B0' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 16px center;
  padding-right: 40px;
}
.np-input--select option {
  color: var(--np-navy);
  background: #fff;
}
.np-input--textarea {
  min-height: 120px;
  resize: vertical;
  line-height: 1.5;
}
.np-form--request { margin-top: 24px; }
/* iOS Safari sizes input[type="date"]'s shadow datetime-edit content at its
   own intrinsic width and ignores width:100% on a flex child unless the
   native control chrome is reset via appearance:none, which pushes the
   input past the card edge (same bug + same fix shape as events.templ
   commits d244a49/1e2b1b8/6cc7bb7, ported here for the np design system). */
.np-input[type="date"] {
  color-scheme: dark;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  min-width: 0;
  min-height: 44px;
  font-size: 16px !important;
  text-align: left !important;
}
@media (min-width: 640px) {
  .np-input[type="date"] {
    min-height: auto;
  }
}

/* Scheduled events from the admin schedule tool are static board lines: they
   have no detail page, so no Join pill, no pointer, and (same specificity as
   .np-row[data-type]::after, later in source) no mobile chevron. */
.np-row.np-row--static { cursor: default; }
.np-row.np-row--static::after { content: none; }

/* Price note under the two "Become a member" buttons (hero and final CTA).
   Mono at caption size, the same utility voice as .np-mono-accent, so the
   figure reads as a fact beside the button rather than a second headline.
   In the hero the note hangs below the button out of flow, so the actions
   row keeps its height and the schedule link stays centered on the button;
   the 72px hero bottom padding absorbs it. On phones the note joins the
   flow between the stacked button and the schedule link. */
.np-cta-stack {
  position: relative;
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}
.np-cta-stack__note {
  font-family: var(--np-font-mono);
  font-size: 13px;
  letter-spacing: 0.02em;
  line-height: 1.4;
  color: rgba(255, 255, 255, 0.72);
  pointer-events: none;
}
.np-hero__actions .np-cta-stack__note {
  position: absolute;
  white-space: nowrap;
  top: 100%;
  left: 0;
  right: 0;
  margin-top: 10px;
  text-align: center;
}
@media (max-width: 767px) {
  .np-cta-stack { align-self: stretch; }
  .np-cta-stack > .np-btn { display: block; align-self: stretch; }
  .np-hero__actions .np-cta-stack__note { position: static; margin-top: 0; }
  /* The final CTA's full-width rule moves from the button to its stack. */
  .np-cta > .np-cta-stack { flex: 1 1 100%; }
  /* Band CTA (private events): block-level so the button fills the column. */
  .np-cta-stack--band { display: flex; }
  .np-cta-stack--band .np-cta-stack__note { align-self: center; }
}


/* ===== FOOTER BIG NAV =====
   Appended at EOF, downstream of SURFACES (959) and BUTTONS (1033), for the
   same cascade reason as every other footer rule: the SURFACES block
   re-declares colors at the same specificity and would otherwise win.

   Adapted from the "big nav, address only" design mockup. Two substitutions
   from that mockup, both deliberate: the display face is Barlow Condensed
   700 (self-hosted, and the same face as the wordmark) rather than Anton,
   and the mockup's lime "Get directions" is court blue, because court blue
   is the brand's only accent. */

/* Top row: wordmark left, contact block right, top-aligned. The wordmark
   sits a step larger than the header's so it reads as a sign-off, not a
   repeat of the nav bar. */
.np-footer__top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 48px;
  margin-bottom: 48px;
}
.np-footer .np-wordmark { font-size: 1.6rem; }
.np-footer__bignav {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  min-width: 0;
}
/* Sized in vw (clamped) so the stack scales with the page instead of
   stepping at breakpoints. The 6.5rem cap is the mockup's 104px;
   "Private Events", the longest label, is ~650px at that size and fits the
   1200px column with room. Muted steel blue, white on hover, so the stack
   reads as wayfinding under the content rather than a second headline. */
.np-footer__biglink {
  font-family: 'Barlow Condensed', var(--np-font-display);
  font-weight: 700;
  font-size: clamp(2.6rem, 7.6vw, 6.5rem);
  line-height: 0.95;
  text-transform: uppercase;
  letter-spacing: -0.005em;
  color: var(--np-footer-nav);
  text-decoration: none;
  border-radius: 6px;
  transition: color 150ms var(--np-ease);
}
/* Hit-box separation, scoped to touch devices ONLY. At line-height: 0.95 the
   painted glyphs overflow each link's own line box, and with the links stacked
   edge-to-edge (zero gap) a tap near a link's top/bottom edge hit-tested to the
   ADJACENT link -- confirmed via elementFromPoint in real WebKit, and the cause
   of the reported "needs two taps" bug. Desktop keeps the original tight stack
   (the design intent: a dense wayfinding block), because a mouse click is a
   single point and never suffers the ambiguity. `hover: none` targets touch
   input rather than a width breakpoint, so it follows the actual input type. */
@media (hover: none) {
  /* `gap` is a property of the flex CONTAINER, so an `em` value here would
     resolve against the container's own (inherited, body-sized) font-size --
     ~1.7px, not the link size. Use the same clamp() the links use so the
     separation tracks the rendered type, and express it as a fraction of that. */
  .np-footer__bignav { gap: calc(clamp(2.6rem, 7.6vw, 6.5rem) * 0.12); }
  .np-footer__biglink { padding: 0.06em 0; }
}
/* Guarded so iOS/WebKit touch devices never CSS-match :hover: an unqualified
   :hover color change on a plain <a> with no click handler makes WebKit treat
   the first tap as "enter hover" and require a second tap to navigate. Same
   fix as .np-btn--lime above. */
@media (hover: hover) {
  .np-footer__biglink:hover { color: var(--np-white); }
}

/* Mono, right-aligned, top of the band. The whole block is footer blue,
   links included: it is one contact card, not text with links in it. */
.np-footer__address {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
  gap: 6px;
  flex: none;
  font-family: var(--np-font-mono);
  font-style: normal;
  font-size: clamp(13px, 1.1vw, 16px);
  line-height: 1.65;
  color: var(--np-footer-link);
}
/* One treatment for every action in the contact block: accent, no underline.
   Directions, the Instagram DM and the email are three of the same kind of
   thing (ways to reach the club), so they get one style rather than the three
   different ones this block accumulated. Applied via .np-footer__link; the
   plain address lines stay unstyled text so the links are the only colored
   items and colour alone is not the sole cue. */
.np-footer__link {
  color: var(--np-footer-link);
  text-decoration: none;
  border-radius: 4px;
  transition: color 150ms var(--np-ease);
}
.np-footer__link:hover { color: var(--np-white); }
/* The postal address is itself the Maps link; its two lines stack inside the
   one anchor. Text alignment is inherited from the block (right on desktop,
   left on phones), so nothing here needs to know which. */
.np-footer__where span { display: block; }

/* Contact rows under the postal block: the email, then the Instagram DM,
   one per line. The extra top margin (on top of the block's 6px gap)
   separates "where we are" from "how to reach us"; they share the
   <address> but answer different questions. */
.np-footer__contact {
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}
/* The email is one ~33ch token; a mailto broken across lines is unreadable
   and untappable. */
.np-footer__email { white-space: nowrap; }

/* Phone: the top row stacks (wordmark, then address) and the address flips
   to the left edge, so everything in the band shares one left margin. */
@media (max-width: 640px) {
  .np-footer__top {
    flex-direction: column;
    align-items: stretch;
    gap: 24px;
    margin-bottom: 36px;
  }
  .np-footer .np-wordmark { font-size: 1.35rem; }
  .np-footer__address {
    align-items: flex-start;
    text-align: left;
  }
  /* The contact rows flip with the block they sit in; without this their
     own flex-end would keep them right-aligned under left-aligned address
     lines. The email fits the column at the shared size (~33ch of mono is
     ~257px against a ~360px column), so it does not need stepping down. */
  .np-footer__contact { align-items: flex-start; }
  /* Touch targets. These links are a 22px line box, so on a phone they need
     padding to reach 44px. Padding alone would make the hit areas overlap, so
     the rows are spaced as well: each link gets a 44px box with a few px of
     dead space between, instead of neighbours that steal each other's taps. */
  /* Spacing groups the block instead of flattening it. Two groups: where we
     are (the address, one link across two lines) and how to reach us (the DM
     and the email). What you see between lines of text:

       address line -> address line   leading only  (one sentence, two lines)
       address      -> DM             ~36px         (the group break)
       DM           -> email          ~12px         (one group)

     line-height drops from 1.7 to 1.35 here: at 1.7 each row carries ~7px of
     leading that reads as extra space, which made the block feel loose no
     matter what the gaps were set to.

     Touch targets. Every link is padded to 44px tall and the padding is
     cancelled by an equal negative margin, so it grows the hit area without
     moving any text. The address is already ~35px over two lines and needs
     only 5px a side. The DM and the email are ~17.5px line boxes at this
     line-height that need ~27px of padding each, and if that were split
     evenly the two hit areas would collide unless the text sat ~27px apart,
     which is what forced the old gap. Splitting it 21px/6px instead, mirrored
     on the two links, puts the bulk of each hit area on its OUTER edge (the
     first row reaches up into the group break, the last reaches down toward
     the nav) and leaves only 6px+6px between them, so the text can sit 12px
     apart with zero overlap. */
  .np-footer__address {
    gap: 0;
    line-height: 1.35;
  }
  .np-footer__where {
    padding: 5px 0;
    margin: -5px 0;
  }
  .np-footer__contact {
    margin-top: 30px;
    row-gap: 12px;
  }
  .np-footer__contact > .np-footer__link:first-child {
    padding: 21px 0 6px;
    margin: -21px 0 -6px;
  }
  .np-footer__contact > .np-footer__link:last-child {
    padding: 6px 0 21px;
    margin: -6px 0 -21px;
  }
}

/* ===== PAGE IDENTITY (per-area accents, active nav, page openings) =====
   Appended at the very end of the file on purpose, same reason as the
   .np-input--select block above: "3. SURFACES" (line 946) and "4. BUTTONS
   + HOVER / MOTION" (line 1020) re-declare properties that earlier
   same-specificity rules already set, and win on source order. Anything
   meant to override them has to come after them. Quick task 260916-vfg. */

:root {
  --np-sage: #C8D2CB;     /* /join-us signup panel and /private-events light bands; navy text 11.3:1, #2A5063 body 5.6:1, #9E1F18 error 5.1:1 */
  /* The page accent is court blue on every np page; volt (--np-lime) is
     reserved for primary CTAs via .np-btn--lime; eyebrows are
     --np-accent-text on navy and --np-navy on sage (see
     .np-band--cream .np-eyebrow and the sage panel rules). */
  --np-page-accent: var(--np-accent);
  --np-page-accent-hover: var(--np-accent-hover);
}

/* Active nav state: the visible signal the pages lack today. The color rule
   already exists at line 954 ([aria-current="page"] { color: var(--np-text) })
   this adds the underline in the page accent. Home has no self-link in its
   nav (Schedule points at #schedule), so Home correctly shows no active item. */
.np-nav__link[aria-current="page"] {
  text-decoration: underline;
  text-decoration-color: var(--np-page-accent);
  text-decoration-thickness: 2px;
  text-underline-offset: 6px;
}

/* ---- Join split hero ---- */
.np-hero-split {
  display: grid;
  /* Even split: the pitch on the left carries the headline, price and
     benefits, so it needs as much room as the form. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(32px, 5vw, 64px);
  align-items: start;
}
@media (max-width: 899px) {
  .np-hero-split { grid-template-columns: 1fr; }
}
/* .np-card--sage: the same sage form surface as the /join-us signup panel,
   for a form card on a page-shaped route (the pro pages' lesson request).
   Every rule below is shared so the two never drift. */
.np-hero-split__panel,
.np-card--sage {
  background: var(--np-sage);
  color: var(--np-navy);
  border-radius: var(--np-radius-card);
  padding: clamp(24px, 4vw, 40px);
}
/* .np-card carries a navy hairline border; on sage it would read as a dark
   ring, and the join-us panel has none. */
.np-card--sage { border-color: transparent; }
/* Inherited dark-theme text colors are wrong on the sage panel; override
   just inside it. #2A5063 on sage is 5.6:1. */
.np-hero-split__panel .np-label,
.np-hero-split__panel .np-panel__h,
.np-hero-split__panel .np-panel__sub,
.np-hero-split__panel .np-footnote,
.np-hero-split__panel .np-eyebrow,
.np-card--sage .np-label,
.np-card--sage .np-benefit__title,
.np-card--sage .np-benefit__body,
.np-card--sage .np-eyebrow {
  color: var(--np-navy);
}
.np-hero-split__panel .np-panel__sub,
.np-hero-split__panel .np-footnote,
.np-card--sage .np-benefit__body {
  color: #2A5063;
}
.np-hero-split__panel .np-input,
.np-card--sage .np-input {
  background: #FFFFFF;
  /* Hairline a step darker than the sage panel so the field edges read as
     deliberate and give the focus state somewhere to go. */
  border-color: #A9B6AE;
  color: var(--np-navy);
}
.np-hero-split__panel .np-input:hover,
.np-card--sage .np-input:hover {
  border-color: #8C9C92;
}
/* Focus: navy edge for the 3:1 change WCAG wants (lime alone on white is
   about 1.2:1) plus a lime ring that ties the field to the button. No
   browser blue anywhere on this page. */
.np-hero-split__panel .np-input:focus,
.np-hero-split__panel .np-input:focus-visible,
.np-card--sage .np-input:focus,
.np-card--sage .np-input:focus-visible {
  border-color: var(--np-navy);
  box-shadow: 0 0 0 3px var(--np-lime);
}
/* Light, sage-leaning gray: clearly an example next to navy entered text. */
.np-hero-split__panel .np-input::placeholder,
.np-card--sage .np-input::placeholder {
  color: #8A9790;
}
/* Darker red than --np-danger so invalid-state copy stays AA on the sage
   panel (#9E1F18 is 5.0:1). */
.np-hero-split__panel .np-input.is-invalid,
.np-card--sage .np-input.is-invalid {
  border-color: #9E1F18;
}
.np-hero-split__panel .np-field__error,
.np-card--sage .np-field__error {
  color: #9E1F18;
}
/* Form-level error box: the dark-theme version is a translucent coral wash
   with light text; on sage it needs the same deep red edge as the fields
   and navy copy. */
.np-card--sage .np-form__error {
  background: rgba(158, 31, 24, 0.08);
  border-color: #9E1F18;
  color: var(--np-navy);
}
/* The select chevron is drawn for dark fields (#8FA3B0); on a white field
   over sage it needs to be navy. Full shorthand on purpose: the
   .np-card--sage .np-input rule above sets `background: #FFFFFF`, which at
   equal specificity resets repeat/position and would tile the chevron. */
.np-card--sage .np-input--select {
  background: #FFFFFF url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%230A1A2F' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 16px center;
}

/* ---- Events full-bleed hero ---- */
/* Reuses .np-hero / .np-hero__media / .np-hero__scrim / .np-hero__inner /
   .np-hero__h1 / .np-hero__sub from line 262; only what differs from Home's
   hero is added here. */
.np-hero--events {
  /* Shorter than Home's full-viewport hero so the two heroes do not read as
     the same shape. The nav overlays the top 64px (same fixed .np-nav--overlay
     as Home), so the photo runs edge to edge from the top of the document.
     Copy bottom-aligns so there is no dead band of fading photo under the
     CTA; the short bottom fade below does the rest. */
  min-height: clamp(460px, 62vh, 620px);
  justify-content: flex-end;
}
@media (min-width: 641px) {
  /* Bottom padding is a step shorter than Home's so the copy sits lower in
     the frame, over the deck at the photo's lower left rather than the
     bright courts above it. */
  .np-hero--events .np-hero__inner { padding: 120px 20px 48px; }
  /* First section after the hero: shorter top gap than the page rhythm so
     the content starts where the photo ends instead of after another band
     of navy. */
  .np-hero--events + .np-section { padding-top: clamp(40px, 5vw, 64px); }
}
.np-hero--events .np-hero__scrim {
  /* Top band keeps the wordmark and Sign In legible over the photo behind
     the transparent nav; the left wash sits behind the text column so the
     headline never lands on the pale court surface (same idea as Home's
     hero); the bottom blend dissolves into the page. */
  background:
    linear-gradient(to bottom,
      color-mix(in srgb, var(--np-navy) 70%, transparent) 0px,
      color-mix(in srgb, var(--np-navy) 35%, transparent) 72px,
      transparent 140px),
    /* Left wash: lighter than Home's (55% vs 90% at the edge) and gone by
       the 62% mark, so the lounge crowd on the right stays clear. The
       ellipse alone thinned to about 45% navy under the right end of the
       headline, which lands on the pale blue courts; layered on it, the
       wash lifts that to the mid-50s without touching the right half. */
    linear-gradient(90deg,
      color-mix(in srgb, var(--np-navy) 55%, transparent) 0%,
      color-mix(in srgb, var(--np-navy) 38%, transparent) 40%,
      transparent 62%),
    /* Corner wash: one ellipse anchored where the copy lives (lower left),
       fading diagonally toward the upper right. No full-width bottom fade,
       so the photo ends on real detail at its lower edge; the band below
       meets it as a clean line. */
    radial-gradient(ellipse 96% 100% at 4% 96%,
      color-mix(in srgb, var(--np-navy) 88%, transparent) 0%,
      color-mix(in srgb, var(--np-navy) 78%, transparent) 40%,
      color-mix(in srgb, var(--np-navy) 58%, transparent) 62%,
      color-mix(in srgb, var(--np-navy) 22%, transparent) 82%,
      transparent 100%);
}
.np-hero--events .np-hero__h1 {
  font-size: var(--np-display-h2);
}
/* The events source runs vertically: lit facade at the top, courts in the
   middle, the lounge crowd down the right, empty deck bottom-left. A wide
   desktop hero shows under half of it, so the window sits just above
   center: party and courts in, most of the empty deck out, a strip of
   golden facade along the top. */
.np-hero--events .np-hero__media img {
  object-position: 50% 55%;
}
/* Phones: the hero is taller than the image is wide, so the overflow is
   horizontal and the x value picks the slice. Anchored right of center so
   the picture sits further left in the frame. The scrim drops the left wash
   (it covered most of a 400px frame) and keeps only the top band and a
   bottom fade under the bottom-aligned copy, like Home's phone scrim. */
@media (max-width: 640px) {
  .np-hero--events .np-hero__media img {
    object-position: 60% 100%;
  }
  .np-hero--events .np-hero__scrim {
    /* Deeper top band than Home's: the lit facade behind the wordmark on
       this photo is brighter than Home's overhead shot. */
    background:
      linear-gradient(to bottom,
        color-mix(in srgb, var(--np-navy) 88%, transparent) 0px,
        color-mix(in srgb, var(--np-navy) 62%, transparent) 64px,
        color-mix(in srgb, var(--np-navy) 28%, transparent) 120px,
        transparent 180px),
      linear-gradient(to top,
        var(--np-navy) 0%,
        color-mix(in srgb, var(--np-navy) 70%, transparent) 36%,
        color-mix(in srgb, var(--np-navy) 30%, transparent) 58%,
        transparent 100%);
  }
}

/* ---- Sage contrast band ---- */
.np-band--cream {
  width: 100%;
  background: var(--np-sage);
  color: var(--np-navy);
  /* .np-section carries top padding only and leans on the next section's
     top padding for the gap below it. A full-width band breaks that chain,
     so the band supplies the gap above itself and pads its own bottom. */
  margin-top: clamp(64px, 9vw, 110px);
}
.np-band--cream > .np-section { padding-bottom: clamp(64px, 9vw, 110px); }
/* A band directly under the events hero: the photo's short fade already
   lands on navy, so no extra navy gap before the band; the band's inner
   section takes the same shorter top gap the first section used to. */
.np-hero--events + .np-band--cream { margin-top: 0; }
.np-hero--events + .np-band--cream > .np-section { padding-top: clamp(48px, 6vw, 72px); }
.np-band--cream .np-lede { color: #2A5063; }
.np-band--cream .np-benefit__title { color: var(--np-navy); }
.np-band--cream .np-benefit__body { color: #2A5063; }
.np-band--cream .np-benefit-list li { border-top-color: rgba(10, 26, 47, 0.14); }
/* Eyebrows on sage use the same navy as the /join-us sage form panel and
   .np-card--sage, so every sage surface reads as one system. */
.np-band--cream .np-eyebrow { color: var(--np-navy); }

/* ===== PRO PAGE SPLIT HERO =====
   Appended after the "3. SURFACES" block on purpose: it overrides earlier
   same-specificity rules, so anything layered on shared surfaces must come
   after it. The hero grid itself is the shared .np-hero-split. */
.np-hero-split--center { align-items: center; }
.np-page-head__media img {
  width: 100%;
  height: auto;
  display: block;
  border-radius: var(--np-radius-card);
  object-fit: cover;
  aspect-ratio: 4 / 3;
  object-position: 50% 30%;
}
/* Credential line under the pitch: quiet, with the Instagram glyph riding
   the same row (the glyph's 44px hit area is pulled in by its own negative
   margin, see .np-instructor__social). */
.np-page-head__cred {
  display: flex;
  align-items: center;
  gap: 4px;
  margin: 14px 0 0;
  color: var(--np-text-muted);
  font-size: 14px;
  letter-spacing: 0.01em;
}
/* CTA row: the button plus a one-line price reassurance. On phones the row
   stacks and the button keeps its full-width .np-btn behaviour; on the
   two-column layout the button shrinks to its label and the price sits
   beside it. */
.np-page-head__actions {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 24px;
}
.np-page-head__from {
  font-size: 14px;
  color: var(--np-text-muted);
  text-align: center;
}
@media (min-width: 900px) {
  .np-page-head__actions { flex-direction: row; align-items: center; gap: 20px; }
  .np-page-head__cta { display: inline-block; }
  .np-page-head__from { text-align: left; }
}

/* ---- bio card: story left, focus list right ---- */
.np-bio {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: clamp(28px, 4vw, 56px);
  align-items: start;
}
.np-bio__story .np-instructor__bio:last-child { margin-bottom: 0; }
/* Rate rows beside the story: player count left, per-person price right
   with the group total as its caption. Hairlines, not tiles, so the price
   reads as part of the coach card rather than a second card inside it. */
.np-rate-list { list-style: none; margin: 0; padding: 0; }
.np-rate-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 0;
  border-top: 1px solid var(--np-line);
}
.np-rate-row:last-child { border-bottom: 1px solid var(--np-line); }
.np-rate-row__label { font-size: 15px; color: var(--np-text); }
.np-rate-row__price {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
  font-family: var(--np-font-display);
  font-weight: 700;
  font-size: 1.625rem;
  line-height: 1;
  letter-spacing: 0.01em;
  color: var(--np-text);
}
.np-rate-row__sub {
  font-family: var(--np-font-body);
  font-weight: 400;
  font-size: 13px;
  line-height: 1.4;
  letter-spacing: 0;
  color: var(--np-text-muted);
  margin-top: 4px;
}
.np-rate-note { margin: 14px 0 0; font-size: 14px; line-height: 1.5; color: var(--np-text-secondary); text-wrap: pretty; }
@media (max-width: 767px) {
  .np-bio { grid-template-columns: 1fr; }
}

/* ---- request form: name + email share a row above 640px ---- */
.np-form__row { display: grid; grid-template-columns: 1fr; gap: 16px; }
@media (min-width: 640px) { .np-form__row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ---- lesson-request confirmation page (/{pro-slug}/requested) ---- */
/* One sage card, capped so the prose stays a comfortable measure. The
   heading is the page's h1, so it takes the display face at h2 scale; navy
   on sage like every other text in the card. */
.np-confirm { max-width: 640px; }
.np-confirm__h1 {
  font-family: var(--np-font-display);
  font-weight: 800;
  text-transform: uppercase;
  font-size: var(--np-display-h2);
  line-height: 0.95;
  letter-spacing: 0.01em;
  margin: 0 0 14px;
  color: var(--np-navy);
}
.np-confirm .np-benefit__body { max-width: 52ch; text-wrap: pretty; }
.np-confirm .np-benefit__body + .np-benefit__body { margin-top: 12px; }
/* Text link, not a button: the request is done and this is the way back,
   not a next action. Accent blue fails contrast on sage, so it stays navy. */
.np-confirm__link {
  display: inline-block;
  margin-top: 22px;
  color: var(--np-navy);
  font-weight: 600;
  font-size: 15px;
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1.5px;
  text-decoration-color: rgba(10, 26, 47, 0.4);
  border-radius: 3px;
  transition: text-decoration-color 150ms cubic-bezier(0.16, 1, 0.3, 1);
}
.np-confirm__link:hover { color: var(--np-navy); text-decoration-color: currentColor; }

/* ============ HOME: FEATURED COACH ============ */
/* /v2/index #pros: one coach instead of a three-card grid. Photo left,
   pitch right; the whole block collapses to a single column under 900px,
   the same breakpoint as .np-hero-split. Appended after the SURFACES block
   on purpose: same-specificity rules there would otherwise win (see the
   np.css source-order note). */
.np-coach__h2 { margin-bottom: clamp(28px, 4vw, 48px); }
.np-coach {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  gap: clamp(32px, 5vw, 64px);
  align-items: start;
}
@media (max-width: 899px) {
  .np-coach { grid-template-columns: 1fr; }
}
.np-coach__media {
  aspect-ratio: 4 / 5;
  border-radius: var(--np-radius-card);
  overflow: hidden;
  background: var(--np-navy-deep);
  border: 1px solid var(--np-line);
}
.np-coach__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 20%;
  display: block;
}
@media (max-width: 899px) {
  /* A 4:5 portrait at full phone width is taller than the viewport; go
     squarer so the pitch below it stays within one scroll. */
  .np-coach__media { aspect-ratio: 1 / 1; max-width: 480px; }
}
.np-coach__body { min-width: 0; }
.np-coach__lede {
  margin: 0 0 32px;
  font-size: clamp(17px, 1.6vw, 20px);
  line-height: 1.5;
  color: var(--np-text);
  max-width: 48ch;
  text-wrap: pretty;
}
.np-coach__method-eyebrow { margin-bottom: 14px; }
/* Outcomes: what a student leaves with, as a hairline-ruled list. Title
   in the display face on the left, a one-line body on the right; both
   collapse to a stack on phones. The list closes with its own rule and
   the CTA row sits under it. */
.np-coach__outcomes {
  margin: 0 0 36px;
  padding: 0;
  border-top: 1px solid var(--np-line);
}
.np-coach__outcome {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 8px 32px;
  align-items: baseline;
  padding: 20px 0;
  border-bottom: 1px solid var(--np-line);
}
.np-coach__outcome dt {
  margin: 0;
  font-family: var(--np-font-display);
  font-weight: 700;
  font-size: clamp(20px, 2vw, 24px);
  text-transform: uppercase;
  line-height: 1;
  color: var(--np-text);
}
.np-coach__outcome dd {
  margin: 0;
  font-size: 16px;
  line-height: 1.5;
  color: var(--np-text-secondary);
}
@media (max-width: 599px) {
  .np-coach__outcome { grid-template-columns: 1fr; padding: 16px 0; }
  .np-coach__outcome dd { font-size: 17px; }
}
/* Pull quote, magazine style, shared by the coach section's client quote
   and the "Six floors up" press line: the opening mark is a large lime
   decoration hung in the left margin, so the quote's first letter sits on
   the same left edge as the lede and the cards above. The closing mark is
   ordinary punctuation, set in the paragraph's own face and color; the
   two marks differ in size on purpose, which is what makes the color
   difference read as intended rather than as a mismatch. */
.np-quote {
  position: relative;
  margin: 0;
  padding: 0;
}
/* The coach quote closes its section under the photo/body grid; the
   .np-coach__quote wrapper carries the rule and the gap above it so the
   figure's own box (where the mark is positioned) starts at the text. In
   the split header .np-split's margin handles the gap instead. */
.np-coach__quote {
  margin-top: clamp(32px, 4vw, 48px);
  padding-top: clamp(28px, 3.5vw, 44px);
  border-top: 1px solid var(--np-line);
}
.np-quote--split { flex: 1 1 340px; min-width: 0; }
.np-quote blockquote { margin: 0; padding: 0; }
.np-quote p {
  margin: 0;
  font-size: clamp(20px, 2.2vw, 28px);
  line-height: 1.3;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--np-text);
  max-width: 34ch;
  text-wrap: pretty;
}
.np-quote p::after { content: "\201D"; }
.np-quote::before {
  content: "\201C";
  position: absolute;
  top: -0.1em;
  left: -0.08em;
  transform: translateX(-100%);
  font-family: var(--np-font-display);
  font-weight: 800;
  /* 40px floor: the condensed glyph is ~0.33em wide, so at the floor it
     hangs ~16px into the phone's 20px gutter without touching the edge. */
  font-size: clamp(40px, 6vw, 84px);
  line-height: 1;
  color: var(--np-accent);
  pointer-events: none;
  user-select: none;
}
/* Coach variant: alone on its row, so the mark sits inside the column
   (hanging it in the gutter would put it on the phone's screen edge) and
   the paragraph indents past it and may run wider than the default 34ch.
   These sit after the base rules on purpose: same specificity, so source
   order decides. */
.np-quote--coach { padding-left: clamp(30px, 3.4vw, 46px); }
.np-quote--coach::before { left: 0; transform: none; font-size: clamp(40px, 5vw, 64px); }
.np-quote--coach p { max-width: 60ch; font-size: clamp(18px, 1.8vw, 23px); }
.np-quote__cite {
  display: block;
  margin-top: 12px;
  font-family: var(--np-font-mono);
  font-size: 13.5px;
  color: var(--np-text-muted);
}
/* Press byline: publication and month set as a small-caps credit, the way
   a pull quote credits its source. Same face, size and color as the
   client cite, so the two quotes still read as one treatment. */
.np-quote__cite--press { text-transform: uppercase; letter-spacing: 0.08em; }
/* The whole byline is the link. Inherits the cite's muted color, with a
   thin offset underline in the same tone so it reads as a link at rest;
   text goes white on hover. Vertical padding pulled back by an equal
   negative margin gives a taller tap target without moving the line. */
.np-quote__link {
  display: inline-block;
  padding: 10px 0;
  margin: -10px 0;
  border-radius: 6px;
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 4px;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--np-text-faint);
  transition: color 150ms var(--np-ease);
}
.np-quote__link:hover { color: var(--np-text); text-decoration-color: currentColor; }
.np-coach__actions {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-top: 4px;
}
.np-coach__cta { box-sizing: border-box; border-radius: 999px; }
.np-coach__price {
  margin: 0;
  font-size: 16px;
  line-height: 1.5;
  color: var(--np-text-secondary);
  text-align: center;
  text-wrap: pretty;
}
.np-coach__price strong { display: block; font-weight: 600; color: var(--np-text); text-wrap: balance; }
@media (min-width: 900px) {
  .np-coach__actions { flex-direction: row; align-items: center; gap: 24px; }
  .np-coach__cta { display: inline-block; flex: 0 0 auto; }
  .np-coach__price { text-align: left; }
}

/* ============ HOME: MEMBERSHIP ============ */
/* /v2/index #membership. Two stacked grids that share one 12-column
   rhythm: the pitch and the price card on top (7/5), the photo and the
   2x2 benefit rows below (5/7). Both collapse to one column under 900px,
   the same breakpoint as .np-hero-split. Appended after the SURFACES
   block on purpose (see the np.css source-order note). */
.np-membership__top,
.np-membership__body {
  display: grid;
  gap: clamp(28px, 4vw, 48px) clamp(32px, 5vw, 64px);
  align-items: start;
}
.np-membership__top { grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); align-items: center; margin-bottom: clamp(32px, 5vw, 56px); }
.np-membership__body { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: stretch; }
@media (max-width: 899px) {
  .np-membership__top,
  .np-membership__body { grid-template-columns: 1fr; }
}
.np-membership__h2 { margin: 0 0 20px; }
.np-membership__lede { margin: 0; max-width: 46ch; }
/* Price card: the /join-us price block on a navy card, with the lime CTA
   filling the card's width so it reads as the section's one action. */
.np-membership__card { padding: clamp(24px, 3.5vw, 40px); }
.np-membership__price { margin-top: 0; }
.np-membership__initiation {
  font-family: var(--np-font-mono);
  font-size: 14px;
  color: var(--np-text-muted);
  margin: 10px 0 0;
}
.np-membership__covers {
  color: var(--np-text-secondary);
  font-size: 15.5px;
  line-height: 1.55;
  margin: 18px 0 22px;
  text-wrap: pretty;
}
.np-membership__cta { box-sizing: border-box; width: 100%; border-radius: 999px; }
/* Photo: on desktop it stretches to the benefit grid's height (the img is
   absolutely positioned so its intrinsic size can't inflate the row); on
   one column it takes a fixed 16:10 instead. */
.np-membership__media {
  position: relative;
  min-height: 320px;
  border-radius: var(--np-radius-card);
  overflow: hidden;
  background: var(--np-navy-deep);
  border: 1px solid var(--np-line);
}
.np-membership__media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 45% 50%; display: block; }
@media (max-width: 899px) {
  .np-membership__media { aspect-ratio: 16 / 10; min-height: 0; }
}
/* The benefit rows keep their top rules from .np-benefit-list; the first
   row's rule doubles as the grid's top edge, matching the photo's top. */
.np-membership__benefits li:first-child,
.np-membership__benefits li:nth-child(2) { padding-top: 0; border-top: 0; }
.np-membership__benefits li { padding-top: 26px; padding-bottom: 26px; }
@media (max-width: 639px) {
  .np-membership__benefits li:nth-child(2) { padding-top: 22px; border-top: 1px solid var(--np-line); }
}

/* ============ HOME: PRIVATE EVENTS STRIP ============ */
/* /v2/index #events: one card before the FAQ. Text left, outlined CTA
   right; the CTA stays blue so lime remains the membership color on this
   page. Stacks under 700px with the button full width. */
.np-events-strip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px 48px;
  background: var(--np-navy-deep);
  border: 1px solid var(--np-line);
  border-radius: var(--np-radius-card);
  padding: clamp(28px, 4vw, 44px);
  box-sizing: border-box;
}
.np-events-strip__text { min-width: 0; max-width: 62ch; }
.np-events-strip__h2 { font-size: clamp(26px, 3.2vw, 36px); margin: 0 0 12px; }
.np-events-strip__body {
  margin: 0;
  color: var(--np-text-secondary);
  font-size: 16px;
  line-height: 1.6;
  text-wrap: pretty;
}
.np-events-strip__cta {
  align-self: center;
  flex: 0 0 auto;
  text-align: center;
  padding: 14px 24px;
  font-size: 16px;
  border-radius: 999px;
  box-sizing: border-box;
}
@media (max-width: 699px) {
  .np-events-strip { flex-direction: column; align-items: stretch; }
  .np-events-strip__cta { align-self: stretch; width: 100%; }
}

/* ============ HOME: COURTS PHOTO CAPTIONS ============ */
/* /v2/index "Six floors up": each photo tile sits in a figure with a
   one-line caption beneath it. The figure wraps .np-photo rather than
   becoming it, so the tile's fixed 300px height and overflow clip keep
   applying to the image alone and never swallow the caption. Mono at cite
   size, muted (same voice as .np-quote__cite): a label for the photo, not a
   second lede. Left-aligned to the tile's edge; no period, like the board's
   "Next two weeks". */
.np-figure { margin: 0; min-width: 0; }
.np-figure__caption {
  margin: 10px 0 0 2px;
  font-family: var(--np-font-mono);
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--np-text-muted);
}

/* ============ PRIVATE EVENTS: PRICE ANCHOR, MID-PAGE CTA ============ */

/* Hero fact line: from-price and typical length between the subhead and
   the button. Any hero may add one; the subhead tightens its gap when a
   fact follows (overrides the phone rule's 28px as well). */
.np-hero__sub:has(+ .np-hero__fact) { margin-bottom: 14px; }
.np-hero__fact {
  font-family: var(--np-font-mono);
  font-size: 14px;
  letter-spacing: 0.02em;
  line-height: 1.5;
  color: rgba(255, 255, 255, 0.82);
  margin: 0 0 28px;
  max-width: 44ch;
  text-wrap: pretty;
}

/* Navy fill for the two mid-page "Request a date" steps on the sage
   bands (lime stays hero + submit). Shape, radius, and motion come from
   the base .np-btn; only the colors and the secondary size differ. */
.np-btn--navy {
  display: inline-block;
  background: var(--np-navy);
  color: var(--np-text);
  padding: 14px 26px;
  font-size: 16px;
  box-sizing: border-box;
}
.np-btn--navy:hover {
  background: var(--np-navy-deep);
  color: var(--np-text);
  box-shadow: 0 8px 24px rgba(10, 26, 47, 0.28);
}
.np-btn--navy:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--np-sage), 0 0 0 4px var(--np-navy);
}

/* Mid-page CTA stack under a benefit list; note recolors per surface
   like the band's lede and body do. */
.np-cta-stack--band { align-items: flex-start; margin-top: 36px; }
.np-band--cream .np-cta-stack__note { color: #2A5063; }

/* ============ HOME: PRIVATE EVENTS STRIP, PHOTO VARIANT ============ */
/* Events hero photo behind the strip under a left-weighted navy scrim, so
   the text keeps its contrast over the bright courts on the right. */
.np-events-strip--photo {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: clamp(40px, 6vw, 80px) clamp(28px, 4vw, 48px);
  min-height: 340px;
  border-color: rgba(255, 255, 255, 0.2);
}
.np-events-strip__media { position: absolute; inset: 0; z-index: -2; }
.np-events-strip__media img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  object-position: 55% 62%; /* the lounge crowd, not the parapet at the bottom edge */
}
.np-events-strip__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(90deg,
      color-mix(in srgb, var(--np-navy) 90%, transparent) 0%,
      color-mix(in srgb, var(--np-navy) 78%, transparent) 42%,
      color-mix(in srgb, var(--np-navy) 52%, transparent) 100%),
    linear-gradient(to top,
      color-mix(in srgb, var(--np-navy) 55%, transparent) 0%,
      transparent 55%);
}
.np-events-strip--photo .np-events-strip__body { color: #E6EDF3; }
.np-events-strip--photo .np-events-strip__cta { box-shadow: 0 4px 24px rgba(0, 0, 0, 0.35); }
@media (max-width: 699px) {
  .np-events-strip--photo { min-height: 0; }
}

/* ===== Compact pro card (cross-nav "Other pros") =====
   The stacked card (avatar over name over a "View {Name}" button) left a
   tall dead zone beside the 104px avatar and ran ~280px per card. This
   variant lays it out horizontally, drops the redundant button in favour of
   making the whole card the link, and shrinks the avatar: same information,
   roughly half the height. Appended after the section-3 surface rules so the
   border/background here win on source order. */
.np-pro--compact {
  flex-direction: row;
  align-items: center;
  gap: 14px;
  padding: 14px 16px;
  text-decoration: none;
  color: inherit;
  transition: border-color 160ms cubic-bezier(0.16, 1, 0.3, 1),
              background-color 160ms cubic-bezier(0.16, 1, 0.3, 1),
              transform 160ms cubic-bezier(0.16, 1, 0.3, 1);
}
.np-pro--compact .np-pro__avatar {
  width: 60px;
  height: 60px;
  flex: 0 0 60px;
}
.np-pro__body { min-width: 0; }               /* let long credentials ellipsize */
.np-pro--compact .np-pro__body h3 {
  font-size: 18px;
  margin: 0 0 2px;
}
.np-pro--compact .np-pro__body p {
  font-size: 13.5px;
  line-height: 1.35;
}
.np-pro__chevron {
  margin-left: auto;
  flex: 0 0 auto;
  font-size: 26px;
  line-height: 1;
  color: var(--np-text-secondary);
  transition: color 160ms cubic-bezier(0.16, 1, 0.3, 1),
              transform 160ms cubic-bezier(0.16, 1, 0.3, 1);
}
.np-pro--compact:hover {
  border-color: var(--np-accent);
  background: rgba(102, 150, 255, 0.06);
  transform: translateY(-1px);
}
.np-pro--compact:hover .np-pro__chevron {
  color: var(--np-accent);
  transform: translateX(2px);
}
.np-pro--compact:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--np-navy), 0 0 0 4px var(--np-accent);
}
@media (prefers-reduced-motion: reduce) {
  .np-pro--compact,
  .np-pro__chevron { transition: none; }
  .np-pro--compact:hover { transform: none; }
  .np-pro--compact:hover .np-pro__chevron { transform: none; }
}

/* ===== Homepage "Also teaching here" pair =====
   The block sat directly under the coach quote's figcaption with no top
   spacing of its own, so on a phone the eyebrow read as part of the quote
   rather than as a new group. .np-coach__also gives it the same vertical
   rhythm .np-coach__quote already uses to separate itself from the grid
   above it, and space between the eyebrow and the cards.

   The cards also ran full-width and tall: two names eating most of a phone
   screen. .np-grid-pros--pair keeps them 2-up at every width (the base
   .np-grid-pros minmax(250px,1fr) floor collapses to one column below
   ~534px, which is exactly the phone case we want side-by-side), and
   .np-pro--stack switches the card from a horizontal row to a vertical
   stack on narrow screens, where a 60px avatar + name + credential + chevron
   cannot fit in the ~163px each column gets at 400px. */
.np-coach__also { margin-top: clamp(32px, 4vw, 48px); }
.np-coach__also .np-eyebrow {
  display: block;
  margin-bottom: clamp(14px, 1.8vw, 20px);
}
.np-grid-pros--pair { grid-template-columns: repeat(2, minmax(0, 1fr)); }

@media (max-width: 680px) {
  /* Vertical card: avatar over first name over credential. The chevron is
     decorative (the whole card is the link) and there is no room for it
     beside the text at this width, so it is dropped rather than shrunk.

     680px, not 560px: the horizontal row needs a ~320px card to hold a full
     name and credential on one line each, which a 2-up grid only reaches
     around a 700px viewport. Measured at 600px the row layout gave 273px
     cards and wrapped BOTH "Santhosh Narayanan" and his credential to two
     lines. Keep the stacked card until the row can actually fit its text. */
  .np-pro--compact.np-pro--stack {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 10px;
    padding: 16px 12px;
  }
  .np-pro--compact.np-pro--stack .np-pro__avatar {
    width: 64px;
    height: 64px;
    flex: 0 0 64px;
  }
  .np-pro--compact.np-pro--stack .np-pro__body { width: 100%; }
  .np-pro--compact.np-pro--stack .np-pro__body h3 { font-size: 17px; }
  /* Let the credential wrap instead of truncating: losing "Fresno State" to
     an ellipsis would cost a real credibility signal. */
  .np-pro--compact.np-pro--stack .np-pro__body p {
    font-size: 12.5px;
    line-height: 1.3;
    text-wrap: pretty;
  }
  .np-pro--compact.np-pro--stack .np-pro__chevron { display: none; }
  /* First name only on a phone: the surname would wrap the ~175px column and
     push the card taller. Desktop keeps the full name, where a 423px card has
     the room and a lone first name reads thin. Hidden rather than omitted from
     the markup so the h3 stays one text node; the anchor's aria-label carries
     the full name and credential regardless. */
  .np-pro--compact.np-pro--stack .np-pro__surname { display: none; }
  .np-grid-pros--pair { gap: 10px; }
}

/* ---- Instagram link with a visible handle (/{pro-slug} hero) ----
   The icon-only .np-instructor__social is a fixed 44x44 square. With a
   handle beside the glyph the link has to grow horizontally, so this
   variant drops the fixed width and pads instead, keeping the 44px touch
   height. Appended after the section-3 surfaces so it wins on source
   order. */
.np-instructor__social--labelled {
  width: auto;
  gap: 6px;
  padding: 0 8px 0 6px;
  border-radius: 10px;
}
.np-instructor__handle {
  font-size: 14px;
  line-height: 1;
  white-space: nowrap;
}
.np-instructor__social--labelled:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--np-navy), 0 0 0 4px var(--np-accent);
}
/* The credential row holds two whole units (credential, @handle). Without
   wrapping, a long credential breaks mid-phrase and orphans a word beside
   the handle. Let the row wrap instead and keep each unit intact, so a
   narrow phone stacks them cleanly rather than splitting one. */
.np-page-head__cred { flex-wrap: wrap; row-gap: 2px; }
.np-page-head__cred > span { flex: 0 1 auto; }
.np-instructor__social--labelled { flex: 0 0 auto; }
