/* ==========================================================================
   Local overrides for the ghost-training-nj mirror.
   Loaded after the Tailwind bundle, so these rules win on source order.
   Re-apply with `python3 apply-overrides.py` after any re-scrape.
   ========================================================================== */

/* --- One page container -------------------------------------------------
   The mirrored build already standardises on max-w-7xl (1280px) for 49 of
   its 50 containers, but the side padding drifts: the nav uses
   `px-4 sm:px-6 lg:px-8` while every content section uses a flat `px-4`.
   That means the logo and the content below it never line up on wide
   screens. These two tokens are the single source of truth instead.

   Change --page-max to retune the whole site's width in one place
   (76rem = 1216px, 80rem = 1280px, 90rem = 1440px). */
:root {
  --page-max: 80rem;
  --page-pad: 1rem;
}
@media (min-width: 640px) {
  :root { --page-pad: 1.5rem; }
}
@media (min-width: 1024px) {
  :root { --page-pad: 2rem; }
}

/* Every container on the 7xl system gets the same width and the same
   responsive gutter, so section edges align vertically down the page. */
.max-w-7xl {
  max-width: var(--page-max);
  padding-left: var(--page-pad);
  padding-right: var(--page-pad);
}

/* --- The athlete grid ("TRUSTED BY THE BEST") ---------------------------
   Shipped with no container at all, so it ran edge-to-edge while its own
   heading sat in a max-w-7xl box. Opt it into the same container.

   This is done in CSS rather than by adding `max-w-7xl mx-auto` to the
   markup, because that className also lives in the RSC flight payload
   embedded in the page, React hydrates from the payload and would revert
   any markup edit the moment the JS runs. */
.grid-cols-2.lg\:grid-cols-5 {
  max-width: var(--page-max);
  margin-inline: auto;
  padding-left: var(--page-pad);
  padding-right: var(--page-pad);
}

/* --- Athlete grids: 3-over-2, bottom row centred ------------------------
   Shared by the homepage "TRUSTED BY THE BEST" grid (ships as lg:grid-cols-5)
   and the results page's athlete cards (ships as lg:grid-cols-3). Both hold the
   same five athletes and now read identically.

   Six tracks with every card spanning two gives three per row. Cards 4 and 5 are
   then shifted one track inward so the short bottom row centres under the top
   one instead of hanging left.

   Six spanned tracks are exactly as wide as three real columns, track widths
   work out to (C - 5g)/6, so a two-track card plus its inner gap equals
   C/3 - 2g/3, the same as grid-cols-3. Only the centring changes, not the sizing.

   Keyed on `.gt-athletes`, a marker `content.py` puts on exactly these two
   grids. It used to match on their Tailwind column combos instead, on the
   grounds that no other page used them, which held right up until the strength
   page wanted an ordinary three-up grid and inherited a centred orphan row. */
@media (min-width: 1024px) {
  .gt-athletes {
    grid-template-columns: repeat(6, minmax(0, 1fr));
  }
  .gt-athletes > * {
    grid-column: span 2;
  }
  .gt-athletes > :nth-child(4) {
    grid-column: 2 / span 2;
  }
  .gt-athletes > :nth-child(5) {
    grid-column: 4 / span 2;
  }
}

/* At tablet width the results grid is two-up, which leaves the fifth card alone
   on its own row. Same principle: four tracks, cards spanning two, and the
   orphan shifted in by one so it centres. (The homepage grid is three-up here
   and has no orphan, so it is left alone.) */
@media (min-width: 768px) and (max-width: 1023.98px) {
  .gt-athletes.md\:grid-cols-2 {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
  .gt-athletes.md\:grid-cols-2 > * {
    grid-column: span 2;
  }
  .gt-athletes.md\:grid-cols-2 > :nth-child(5) {
    grid-column: 2 / span 2;
  }
}

/* On a phone the homepage grid's two columns leave each card ~164px wide, and
   a 3:4 portrait in that is a thumbnail: the face is a few dozen pixels across
   and the draft line under it wraps to three. One column, as /results/ already
   does below `md`, and the two grids read the same at every width. */
@media (max-width: 767.98px) {
  .gt-athletes.grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* AJ Gracia's photo is the one that is not square: 1055x1629, against a 3:4
   slot, so `object-cover` scales it to the width and crops the difference off
   the top and the bottom equally. There is only ~2% of frame above his helmet,
   so the 10% that comes off the top took the top of his head with it. Anchor
   the crop to the top instead and it all comes off the bottom, which is his
   shins. The other four are 1:1 and crop left-to-right, where centred is right. */
img[src*="aj-gracia"] {
  object-position: 50% 0;
}

/* --- Menu items removed -------------------------------------------------
   Shop is dropped from both the desktop and the mobile menu: the nav's Shop
   pointed at an external subdomain and the footer's at an internal /shop that
   404s, and neither is wanted.

   Contact used to go with it, on the grounds that START NOW covered the same
   ground. START NOW now opens the questionnaire instead, which is a different
   errand, so Contact is a menu item again. It sits last because the build
   already renders it after every other link, so the two items enhance.js
   inserts after Results (Locations, FAQ) still land above it. */
nav a[href^="https://shop."] {
  display: none;
}

/* --- Alternating media rows (the subpage image/copy pairs) --------------
   Each row is a photo and a card. Sized to their own content and vertically
   centred, as they shipped, the two columns end at different heights and the
   shorter one floats in the middle of the taller one's block, which reads as a
   layout accident rather than a pairing. Stretching them to a common height
   fixes it, and the min-height stops a short card collapsing the photo beside
   it into a letterbox.

   Done here rather than with utilities because the compiled Tailwind bundle
   carries neither `items-stretch` nor any `min-h-*` beyond `min-h-screen`.

   Only from lg, below that the grid is a single column and each block should
   keep its own natural height, the photo included, hence the untouched
   `aspect-[4/3]` in the markup. */
@media (min-width: 1024px) {
  .gt-split {
    align-items: stretch;
  }
  .gt-split .gt-split__media {
    aspect-ratio: auto;
    height: 100%;
    min-height: 22rem;
  }
}

/* --- Location cards ------------------------------------------------------
   Each card used to be a photo of the facility with the name set over it in a
   gradient. Both labs have their own mark, and the mark carries the town, so
   the card is now the logo on the card ground with the address and the
   directions link under it, and the burned-in heading is gone rather than
   saying the same thing twice.

   The two marks are very different shapes, one square, one nearly 2:1, so the
   plate is a fixed height and each is contained inside it. Sizing on width
   instead would leave the Eatontown mark towering over the Egg Harbor one. */
.gt-lab-card__mark {
  height: 11rem;
  margin-bottom: 1.75rem;
}

@media (min-width: 768px) {
  .gt-lab-card__mark {
    height: 13rem;
  }
}

/* --- Photo placeholders -------------------------------------------------
   For an athlete whose picture is not ready. The build's own placeholder is
   the word PHOTO at text-white/5, which is invisible against the card and
   reads as a rendering fault; this reads as a slot that is waiting for
   something. Hatching rather than a flat panel, so it cannot be mistaken for
   a photo that failed to load. */
[data-gt-placeholder] {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  background: repeating-linear-gradient(
      135deg,
      rgb(255 255 255 / 3%) 0 10px,
      transparent 10px 20px
    ),
    #101013;
}
[data-gt-placeholder] b {
  font-size: clamp(1.5rem, 5vw, 2.5rem);
  font-weight: 900;
  letter-spacing: -0.04em;
  color: rgb(255 255 255 / 22%);
}
[data-gt-placeholder] span {
  font-size: 0.5625rem;
  font-weight: 900;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgb(255 255 255 / 30%);
}

/* --- Desktop menu breathing room ---------------------------------------
   The menu is twelve items now, and the bar is tightest exactly where it first
   appears: at 1280, the xl breakpoint, the row starts one pixel after the logo.
   One pixel is not clearance, it is luck, and a font that renders a shade wider
   would put the two on top of each other. The gaps close up between xl and the
   width where the default spacing fits again.

   No upper bound, because there is no width where the default spacing comes
   right: the container is capped at max-w-7xl, so past 1280 the bar and the
   logo simply sit flush at every size instead of overlapping at one.

   Written against the same `> :not(:last-child)` shape Tailwind compiles
   `space-x-6` into, so it overrides rather than fights it. */
@media (min-width: 1280px) {
  nav .space-x-6 > :not(:last-child) {
    margin-inline-end: 0.875rem;
  }
}

/* --- Footer ------------------------------------------------------------
   The build only renders a footer on the homepage; the other seven pages
   ship none at all (true of the live site too). Rather than maintain two
   footers, the homepage's React one is hidden and enhance.js mounts a single
   identical footer on every page. See enhance.js for why it must be JS. */
main > footer {
  display: none;
}

/* The footer goes four columns at md, which at 768 leaves each one 144px, and
   the email address is a single 182px token that cannot break. That pushed the
   page 14px wider than the viewport and put a horizontal scrollbar on every
   page at tablet portrait width.

   Both declarations are needed. `anywhere` lets the address break mid-token,
   and it is `anywhere` rather than `break-word` because only the former also
   shrinks the element's min-content width, which is what the grid track is
   sized from. `min-width: 0` overrides the `auto` minimum that grid items get,
   without which the track refuses to go below its content regardless. */
[data-gt-footer] > div > * {
  min-width: 0;
}

[data-gt-footer] ul {
  overflow-wrap: anywhere;
}

/* --- Contact heading: one line ------------------------------------------
   "Ready to Level Up?" shipped at 72px (md:text-7xl) needing 704px, inside a
   560px column, so it always broke as "READY TO LEVEL / UP?". It fits on one
   line at 57px or below; these sizes keep ~6% headroom at every width.

   The class combo below is unique to the contact page: every other page's h1
   uses md:text-8xl.

   Sized in vw rather than with white-space:nowrap deliberately. Forcing nowrap
   would overflow the column if the text or layout ever changed, whereas an
   uncapped size just wraps again, the harmless failure. */
h1.text-5xl.md\:text-7xl {
  /* 7.8vw, not 8.9: nested containers each add a gutter, so the heading's real
     width at 375px is 311px rather than the 343px the outer container suggests.
     Measured headroom: ~5% at 320px, ~8% at 375px. */
  font-size: min(46px, 7.8vw);
}
@media (min-width: 1024px) {
  /* Two-column layout starts here, so the available width roughly halves. 1024
     is the tight case, the column is narrowest right at the breakpoint, before
     it grows with the viewport. 4vw leaves ~7% there and ~11% at 1280+. */
  h1.text-5xl.md\:text-7xl {
    font-size: min(54px, 4vw);
  }
}

/* --- Athlete photos (results page) --------------------------------------
   The cards ship empty "PHOTO" placeholders; enhance.js fills them with the
   same images the homepage grid uses. The injected <img> is appended after the
   placeholder label, so it paints over it and the label needs no hiding. */
[data-gt-photo] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* --- Maps ---------------------------------------------------------------
   Shared by the contact page and /locations/. These live here rather than in
   locations.css because overrides.css is the only stylesheet loaded on every
   page: the contact map is mounted by enhance.js into a React page, which never
   loads locations.css, so keeping the pin and dark-chrome rules there left it
   with a zero-height box and Leaflet's default grey basemap. */
[data-gt-map] {
  margin-top: 1.5rem;
}
[data-gt-map] [data-gt-map-canvas] {
  height: 280px;
  border: 1px solid rgb(255 255 255 / 10%);
  border-radius: 1rem;
  overflow: hidden;
  /* Matching ground colour: a slow or blocked tile server degrades to a dark
     panel rather than a grey flash. */
  background: #0b0b0d;
}
[data-gt-map] p {
  margin-top: 0.5rem;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #6b7280;
}

/* --- Scrollbar ----------------------------------------------------------
   Lives here rather than in animate.css on purpose: this is chrome styling, not
   motion, so it must not sit behind the JS opt-in or the reduced-motion switch.

   color-scheme tells the browser the page is dark, which fixes the native
   scrollbar, form controls and overscroll glow even where the custom rules below
   are unsupported. It is the one line that matters most here.

   Both syntaxes are declared: the standard properties (Firefox, and newer
   Chromium) and the ::-webkit- pseudo-elements (Safari, Chromium). They coexist
   fine, whichever the browser understands wins. */
:root {
  color-scheme: dark;
  scrollbar-width: thin;
  /* thumb, then track */
  scrollbar-color: #ff3e3e #09090b;
}

::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

::-webkit-scrollbar-track {
  background: #09090b;
}

::-webkit-scrollbar-thumb {
  background: #ff3e3e;
  border-radius: 999px;
  /* Border in the track colour insets the thumb, so it reads as a slim pill
     rather than filling the gutter edge to edge. */
  border: 2px solid #09090b;
  background-clip: padding-box;
}

::-webkit-scrollbar-thumb:hover {
  background: #ff6b6b;
  background-clip: padding-box;
}

::-webkit-scrollbar-corner {
  background: #09090b;
}

/* --- Header logo ---------------------------------------------------------
   The logo art is 500x200 (2.5:1) inside a 192x48 box (4:1). `object-contain`
   fits it to the height, leaving ~72px of slack that was being split evenly
   left and right, visually indenting the logo past the container edge.
   Pin it to the left so it starts on the container's gutter. */
nav a .object-contain {
  object-position: left center;
}

/* --- The bar: no stray borders, and no containing block for the drawer ---
   `.glass` sets `border-width: 1px` on all four sides and the bar only ever
   wanted the `border-b` its markup also carries. Invisible in itself, at the
   three screen edges, but the nav's `backdrop-blur-xl` makes it the containing
   block for fixed descendants, and the drawer is one: its `inset: 0` resolves
   to the nav's *padding* box, which those borders inset by 1px. The drawer came
   out 1px narrow and 1px low, showing a hairline of the page down both sides of
   an overlay that is meant to cover everything.

   Dropping the three borders fixed that 1px, but the dependency is the bug: any
   border, any sub-pixel inset, or a browser that measures a backdrop-filtered
   containing block differently puts the hairline back, and it is the worst kind
   of bug to chase because it only shows where the page behind is bright (the
   red CTA band).

   So the blur moves to a pseudo-element and the nav stops being a containing
   block at all: `inset: 0` on the drawer is the viewport again, on every page
   and in every engine. Same trick, same reason, as `.legal-nav` in legal.css,
   which is where the hand-written pages learned it.

   `::before` rather than the nav's own background because the bar keeps its
   tint: `z-index: -1` paints the blur above the bar's background and below its
   content, and the drawer at z-40 is a positive-z sibling above both. */
nav.glass {
  border-top-width: 0;
  border-left-width: 0;
  border-right-width: 0;
  /* The scrolled state re-adds this in animate.css; see the note there. */
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

nav.glass::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
}

/* --- Mobile drawer menu --------------------------------------------------
   The drawer ships with inline `top:80px; height:calc(100vh - 80px)`, which
   causes three problems on a phone:

   1. 100vh on mobile browsers is the viewport with the URL bar *retracted*, so
      the drawer runs taller than the visible area and its lower links can sit
      under the browser chrome. 100dvh is the visible height. The plain 100vh
      line stays first as a fallback for browsers without dvh.
   2. top:80px is pinned to the nav's resting height, but the nav condenses to
      4rem on scroll, leaving a 16px strip of the page showing through between
      the nav and the drawer. Anchoring the drawer to the top of the viewport
      instead means it cannot desync: the bar simply sits over it, and the
      padding keeps the links clear of it.
   3. Thirteen items at text-2xl with 2rem between them come to 950px, which
      overflows every phone in portrait. Sized off dvh they fit, and the links
      still land near 44px tall on a normal screen.

   Inline styles, so !important is the only way to reach them. */
nav div.fixed.z-40 {
  top: 0 !important;
  height: 100vh !important;
  height: 100dvh !important;
  padding-top: clamp(4.25rem, 10.5dvh, 6rem);
  padding-bottom: max(1rem, env(safe-area-inset-bottom));
  gap: clamp(0.15rem, 0.7dvh, 0.6rem);
  /* `safe` degrades to flex-start the moment the content overflows. Plain
     `center` overflows equally in both directions and the overflow above the
     top edge of a scroll container cannot be scrolled to, which is how the
     first link came to be unreachable on a short viewport. */
  justify-content: flex-start;
  justify-content: safe center;
  overflow-y: auto;
  /* Don't let a scroll that runs out of drawer chain to the page behind it. */
  overscroll-behavior: contain;
}

/* space-y-8's 2rem is what the gap above replaces. It is written as
   `:where(.space-y-8 > :not(:last-child)) { margin-block-end: … }`, so it has
   no specificity at all and carries on the end edge, not the start: matching
   the start edge, however specific, leaves it entirely alone. */
nav div.fixed.z-40 > * {
  margin-block-end: 0;
}

/* The drawer is a child of <nav>, so the nav's z-50 orders the pair against the
   page, not against each other: within the nav's stacking context the drawer is
   positioned with z-40 and the bar carrying the logo and the close X is not
   positioned at all, so the drawer painted straight over the one control that
   dismisses it. Pinning the drawer to the top of the viewport (above) made that
   overlap total, and from then on the menu could only be left by navigating.
   Put the bar back on top. */
nav:not(.legal-nav) > div:not(.fixed) {
  position: relative;
  z-index: 50;
}

/* Thirteen items in a single column need about 580px of viewport to fit, and
   between there and ~700px they fit only by a hair — less slack than a
   safe-area inset or a slightly larger default font would eat. Short viewports
   are not rare: a small phone (320x568), a landscape one, a squarish desktop
   window, a phone with the URL bar showing. Below 720px of height the column
   splits in two and the CTA spans both, which turns ~660px of content into
   ~370px; below 460px, three. The alternative is a menu that scrolls, and a
   scrolling menu is what this pass set out to be rid of.

   The `max-width` half is not decoration. The drawer is hidden on desktop by
   `xl:hidden`, which is a single class, (0,1,0); `nav div.fixed.z-40` is
   (0,3,1) and a bare `display: grid` here beat it, so on any desktop window
   under 720px tall the drawer came back to life, laid out full-size and parked
   off-screen by its own transform. Both queries stop at the width the drawer
   itself stops at. */
@media (max-width: 1279.98px) and (max-height: 720px) {
  nav div.fixed.z-40 {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: 0.75rem;
    justify-items: center;
    align-content: flex-start;
    align-content: safe center;
  }
  nav div.fixed.z-40 > a:last-child {
    grid-column: 1 / -1;
  }
}

@media (max-width: 1279.98px) and (max-height: 460px) {
  nav div.fixed.z-40 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* The toggle is the close button while the drawer is open (React swaps the
   hamburger for an X), but white-on-black over a full-screen menu reads as part
   of the menu. Give it a rim so it reads as a control. */
html.gt-drawer-open nav:not(.legal-nav) button[aria-label="Toggle menu"] {
  border-radius: 9999px;
  background: rgb(255 255 255 / 8%);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 20%);
}

/* Scroll lock while the drawer is open. Deliberately not behind html.gt-anim:
   this is behaviour, not decoration, and reduced-motion visitors need it too.
   enhance.js also pins the body, which is the lock iOS Safari actually honours,
   and restores the scroll position on the way out; this rule is what stops the
   page in Blink the moment the class lands, before the script gets there.

   `overscroll-behavior: none` is the belt: with the root locked there is
   nothing to chain to, and it also kills the rubber-band that otherwise shows
   the page sliding under the overlay on a trackpad. */
html.gt-drawer-open,
html.gt-drawer-open body {
  overflow: hidden;
  overscroll-behavior: none;
}

/* --- Touch targets -------------------------------------------------------
   Measured on a 375px viewport: the hamburger was 32x32, the drawer links 32px
   tall, and the legal-page nav links 20px. All are comfortably tappable only by
   accident. WCAG 2.5.8 sets 24x24 as the floor and both Apple and Google
   recommend ~44px, which is what these aim at.

   The hamburger and drawer are mobile-only UI, so they need no media query. The
   legal nav shows at every width, so its sizing is scoped to coarse pointers, no reason to make the desktop nav taller. */
/* `:not(.legal-nav)` because the hand-written pages' own toggle carries the
   same aria-label and is styled in full by legal.css, where it is hidden above
   1280px. This selector is (0,1,2) and `.legal-nav__toggle` is (0,1,0), so
   unscoped it forced a hamburger into their desktop header as well. */
nav:not(.legal-nav) button[aria-label="Toggle menu"] {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  /* Pull back the added bulk so the icon stays optically on the container edge. */
  margin-right: -0.5rem;
}

/* Full-width rows: the tap area should be the row, not just the glyphs. The
   sizes are viewport-relative so thirteen of them always fit the drawer without
   it scrolling; on a 375x812 phone that lands at ~45px a row, over the ~44px
   both Apple and Google ask for. Written against the drawer's own classes
   rather than [data-gt-drawer], which enhance.js adds: if the script never
   runs, the menu still has to be usable. */
nav div.fixed.z-40 a {
  width: 100%;
  text-align: center;
  font-size: clamp(0.95rem, 2.1dvh, 1.3rem);
  line-height: 1.25;
  padding-block: clamp(0.4rem, 1.5dvh, 0.8rem);
}

/* START NOW is a button, not a row: it keeps its pill shape and its own gap. */
nav div.fixed.z-40 > a:last-child {
  width: auto;
  min-width: 12rem;
  margin-top: clamp(0.5rem, 1.8dvh, 1.25rem) !important;
  padding-block: clamp(0.45rem, 1.4dvh, 0.85rem);
  font-size: clamp(0.95rem, 2.1dvh, 1.125rem);
}

@media (pointer: coarse) {
  .legal-nav__links a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* --- Mobile viewport height ---------------------------------------------
   min-h-screen resolves to 100vh, which on a phone is the viewport with the URL
   bar retracted, so the hero runs taller than the screen and pushes its own CTAs
   below the fold. dvh is the height actually visible. Plain vh stays as the
   fallback for browsers without dvh. */
@media (max-width: 767px) {
  .min-h-screen {
    min-height: 100vh;
    min-height: 100dvh;
  }
}

/* --- CTA contrast -------------------------------------------------------
   White text on the brand red #ff3e3e is 3.49:1, under the 4.5:1 WCAG AA needs
   for normal-size text, and the CTAs are 14px and 18px bold, so neither counts as
   "large text" at the relaxed 3:1 threshold. Lighthouse flags both START NOW and
   TRAIN WITH US for it.

   Only the *button background* darkens, to the nearest red that clears the bar:
   #e02020 measures 4.78:1 against white. The accent red is untouched, as text on
   black #ff3e3e is already 6.01:1, so eyebrows, links and the scrollbar keep the
   original colour and the brand still reads as itself. */
.bg-brand-primary {
  background-color: #e02020;
}

/* --- Mobile headline sizes ----------------------------------------------
   The subpage headings ship at text-6xl (60px) below the md breakpoint. Their
   longest single words do not fit a phone: "PERFORMANCE" measures 441px and
   "DEVELOPMENT" 427px against 343px of available width at 375px. The hero section
   has overflow:hidden, so instead of scrolling the page they were simply sliced off
   at the right edge, which is why a page-overflow check never caught it.

   Sized off the worst word rather than the longest line, since a single word cannot
   wrap. "PERFORMANCE" needs 7.353px of width per px of font-size, so at 375px the
   ceiling is 46.6px; 11.4vw gives 42.8px, about 8% clear. The cap keeps the
   original 60px once the viewport is wide enough to afford it. */
h1.text-6xl.md\:text-8xl {
  font-size: min(60px, 11.4vw);
}

/* The homepage heading is text-5xl (48px) and fits at 375px with only 4% to spare, "BALLPLAYERS." is 329px against 343px, so it clips on a 320px screen. Same
   treatment, its own ratio (6.854). */
h1.text-5xl.md\:text-8xl {
  font-size: min(48px, 12.5vw);
}

@media (min-width: 768px) {
  /* md:text-8xl is 96px, which needs 706px for "PERFORMANCE" against 720px
     available at exactly 768px, a 2% margin that a fallback font could eat. The
     fluid value backs it off to 92px there and reaches the full 96px by 800px. */
  h1.text-6xl.md\:text-8xl,
  h1.text-5xl.md\:text-8xl {
    font-size: min(96px, 12vw);
  }
}

/* --- Homepage hero: emphasise "ELITE" on mobile -------------------------
   The word already carries a white-to-translucent gradient (text-transparent +
   bg-clip-text); this scales it up so it reads as the emphasis in
   "WE DEVELOP ELITE BALLPLAYERS."

   Mobile only. At desktop the headline is already 96px and the emphasis reads
   without help, so the word stays at its inherited size there.

   `bg-clip-text` occurs exactly once in the build, on this span, so the
   selector cannot catch anything else.

   Sized in em rather than px so it tracks the heading's own fluid font-size across
   the phone range instead of needing its own breakpoints. The line-height
   compensation keeps the taller inline box from prising the two headline lines
   apart: 0.9/1.22 returns the line box to roughly the 0.9 the heading asks for. */
@media (max-width: 767px) {
  h1 .bg-clip-text {
    font-size: 1.22em;
    line-height: 0.738;
  }
}

/* --- Accent feature cards -----------------------------------------------
   The two "Custom Plans" / "Expert Staff" cards ship as
   `glass p-4 rounded-xl border-l-4 border-l-brand-primary`, a 4px red left
   accent bar. Neither the width nor the colour was rendering: `.glass` sets
   `border-width: 1px` and `border-color: white/10`, and since `border-color`
   writes all four longhands, a same-specificity `.glass` later in the bundle
   was overriding `border-l-4` and `border-l-brand-primary` both. The cards were
   drawing a flat 1px grey box instead of the designed accent.

   Naming all three classes takes the specificity to (0,3,0), which beats `.glass`
   on its own regardless of source order.

   Then a little more: a lit-from-above surface, a soft red bleed off the accent
   edge, and a lift in surface brightness on hover, brightness only, no movement,
   because these cards are not clickable. */
.glass.border-l-4.border-l-brand-primary {
  border-left-width: 4px;
  border-left-color: #ff3e3e;
  background-image: linear-gradient(
    180deg,
    rgb(255 255 255 / 7%),
    rgb(255 255 255 / 0%) 62%
  );
  box-shadow: -9px 0 26px -14px rgb(255 62 62 / 50%);
  transition: background-color 0.3s ease, box-shadow 0.3s ease,
    border-left-color 0.3s ease;
}

.glass.border-l-4.border-l-brand-primary:hover {
  background-color: rgb(255 255 255 / 9%);
  border-left-color: #ff6b6b;
  box-shadow: -11px 0 30px -12px rgb(255 62 62 / 72%);
}

/* --- Global CTA band ----------------------------------------------------
   Mounted by enhance.js above the footer on every non-legal page. Bright, so it
   reads as the end of the page rather than more content.

   Text is black on the brand red: 6.0:1, where white on the same red is 3.49:1
   and fails AA. */
[data-gt-cta] {
  background: linear-gradient(135deg, #ff3e3e 0%, #ff5c3e 100%);
  color: #000;
  padding: 4rem 0;
}

[data-gt-cta] .gt-cta__wrap {
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--page-pad);
  display: grid;
  gap: 2.5rem;
  grid-template-columns: 1fr;
  align-items: center;
}
@media (min-width: 900px) {
  [data-gt-cta] .gt-cta__wrap {
    grid-template-columns: 1.1fr 1fr;
    gap: 4rem;
  }
}

[data-gt-cta] h2 {
  margin: 0 0 0.5rem;
  font-size: clamp(1.75rem, 5vw, 2.75rem);
  font-weight: 900;
  letter-spacing: -0.03em;
  line-height: 1.02;
  text-transform: uppercase;
}
[data-gt-cta] .gt-cta__sub {
  margin: 0 0 1.5rem;
  font-size: 1rem;
  line-height: 1.55;
  /* Black at 78% keeps it secondary to the headline while still clearing AA
     against this red. */
  color: rgb(0 0 0 / 78%);
  max-width: 34rem;
}

[data-gt-cta] .gt-cta__btn {
  display: inline-block;
  padding: 1rem 2.25rem;
  background: #000;
  color: #fff;
  font-weight: 900;
  font-size: 0.9375rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  border-radius: 0.5rem;
  transition: transform 0.2s ease, background-color 0.2s ease;
}
[data-gt-cta] .gt-cta__btn:hover {
  background: #17171a;
  transform: translateY(-2px);
}

/* --- Newsletter --------------------------------------------------------- */
[data-gt-cta] .gt-news__label {
  display: block;
  margin-bottom: 0.6rem;
  font-size: 0.75rem;
  font-weight: 900;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgb(0 0 0 / 70%);
}
[data-gt-cta] form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
}
[data-gt-cta] input[type="email"] {
  flex: 1 1 14rem;
  min-width: 0;
  padding: 0.9rem 1.1rem;
  /* 16px floor, or iOS zooms the page when the field takes focus. */
  font-size: 1rem;
  font-family: inherit;
  color: #000;
  background: rgb(255 255 255 / 92%);
  border: 2px solid rgb(0 0 0 / 15%);
  border-radius: 0.5rem;
}
[data-gt-cta] input[type="email"]::placeholder {
  color: #6b7280;
}
[data-gt-cta] input[type="email"]:focus {
  outline: none;
  border-color: #000;
  background: #fff;
}
[data-gt-cta] button {
  padding: 0.9rem 1.75rem;
  background: #000;
  color: #fff;
  font-family: inherit;
  font-size: 0.9375rem;
  font-weight: 900;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  border: 0;
  border-radius: 0.5rem;
  cursor: pointer;
  transition: background-color 0.2s ease;
}
[data-gt-cta] button:hover {
  background: #17171a;
}
[data-gt-cta] .gt-news__note {
  margin: 0.75rem 0 0;
  font-size: 0.8125rem;
  color: rgb(0 0 0 / 68%);
}
[data-gt-cta] .gt-news__note[data-state="done"] {
  font-weight: 700;
  color: #000;
}

/* Leaflet's own chrome, restyled for a dark page. Generic selectors so both the
   contact map and the /locations/ map pick them up.

   Every one is written as a descendant of .leaflet-container, which is not
   decoration: /locations/ links vendor/leaflet.css in <head>, before this file,
   but the homepage and contact page load Leaflet lazily and append its
   stylesheet at runtime, so there it lands AFTER this one. At equal
   specificity that made Leaflet win, and the attribution came out as its
   default white pill on a black map. The extra class settles it whatever the
   order turns out to be. */
.leaflet-container {
  background: #0b0b0d;
  font-family: var(--font-geist-sans), system-ui, sans-serif;
  /* Leaflet stacks its panes from z-index 400 and its controls from 1000, and
     it leaves the container itself at `auto`, so none of that is contained:
     the panes join the root stacking context and outrank the nav at z-50. The
     map painted over the header, and over the open mobile menu with it. A
     z-index of any kind on the (already relative) container makes it a
     stacking context, and 0 keeps the map exactly where it sits in the page. */
  z-index: 0;
}
.leaflet-container .leaflet-control-attribution {
  background: rgb(0 0 0 / 65%);
  color: #6b7280;
  font-size: 10px;
}
.leaflet-container .leaflet-control-attribution a {
  color: #9ca3af;
}
.leaflet-container .leaflet-bar {
  border: 1px solid rgb(255 255 255 / 14%);
  box-shadow: none;
}
.leaflet-container .leaflet-bar a {
  background: #17171a;
  color: #e5e7eb;
  border-bottom-color: rgb(255 255 255 / 10%);
}
.leaflet-container .leaflet-bar a:hover {
  background: #232327;
  color: #fff;
}
.leaflet-container .leaflet-popup-content-wrapper,
.leaflet-container .leaflet-popup-tip {
  background: #17171a;
  color: #e5e7eb;
  border: 1px solid rgb(255 255 255 / 12%);
  box-shadow: 0 10px 30px rgb(0 0 0 / 55%);
}
.leaflet-container .leaflet-popup-content {
  margin: 0.75rem 0.9rem;
  font-size: 0.8125rem;
  line-height: 1.5;
}
.leaflet-container .leaflet-popup-content strong {
  display: block;
  color: #fff;
  font-weight: 900;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin-bottom: 0.25rem;
}
.leaflet-container .leaflet-popup-close-button {
  color: #6b7280 !important;
}

/* Every pin offers directions. #ff3e3e on the popup's #17171a is 5.6:1, so it
   passes AA at this size without dropping the brand colour. */
.lp-pin__link {
  display: inline-block;
  margin-top: 0.5rem;
  color: #ff3e3e;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.lp-pin__link:hover {
  color: #fff;
}

/* --- Dark basemap ------------------------------------------------------
   The tiles are OpenStreetMap's own, which are light. CARTO's dark_all used to
   do this job without a key; they ended that, and now serve every keyless tile
   stamped "API KEY REQUIRED", so the maps were showing watermarked tiles to
   real visitors rather than failing in a way anyone would notice.

   Inverted and taken fully monochrome: OSM's standard style paints motorways in
   salmon and landuse in green, and inverting that without desaturating leaves a
   map that fights the page for attention. Grayscale drops the hue-rotate with
   it, which only existed to undo the inversion's colour shift.

   The low contrast is the point rather than a side effect. OSM draws every
   road there is, and inverted they come out as bright lines over dark land;
   pulling the contrast down lets them settle back toward the ground, so the map
   reads as a shape with two pins on it instead of a road atlas. The labels
   still clear it, being near-white to begin with. */
.leaflet-tile-pane {
  filter: invert(1) grayscale(1) brightness(0.7) contrast(0.75);
}

/* Markers are DivIcons, so they are styled here rather than shipped as images,
   which keeps them exactly on brand and avoids another request. */
.lp-pin {
  width: 22px;
  height: 22px;
  border-radius: 999px;
  background: #ff3e3e;
  border: 2px solid rgb(255 255 255 / 85%);
  box-shadow: 0 0 0 6px rgb(255 62 62 / 18%), 0 0 22px rgb(255 62 62 / 65%);
}

/* A site that is not open yet reads as an outline rather than a solid: showing
   both as identical would tell people they can turn up at a facility that does
   not exist. */
.lp-pin--soon {
  background: rgb(255 62 62 / 22%);
  border-color: rgb(255 62 62 / 90%);
  box-shadow: 0 0 0 5px rgb(255 62 62 / 10%);
}

/* The homepage map sits between the section heading and the location cards, so it
   needs its own height and a gap below it. */
[data-gt-map-home] {
  margin: 0 0 2rem;
}
[data-gt-map-home] [data-gt-map-canvas] {
  height: clamp(18rem, 42vh, 26rem);
}
