/* -----------------------------------------------------------------------
   Self-hosted fonts — no third-party requests
   -----------------------------------------------------------------------
   Roboto and Roboto Condensed were loaded from fonts.googleapis.com until
   2026-08-18. That handed every visitor's IP address and user-agent to
   Google on every page load, and was the site's only remaining third-party
   request. David's standing preference is zero links to Google and as few
   outside dependencies as practical (see the project instructions, "No
   analytics, no tracking, no third-party tags").

   Same font files Google served, fetched once and committed here, so
   rendering is unchanged. Roboto is licensed under Apache 2.0, which
   permits redistribution.

   Four files, not ten: Google now ships these as VARIABLE fonts, and its
   own stylesheet pointed the 400/500/700 blocks at one identical file per
   subset (verified by md5). Each family is therefore declared once with a
   `font-weight: 100 900` range and the browser interpolates the weight,
   rather than ten near-duplicate static faces. Latin and latin-ext subsets
   only — the Cyrillic, Greek, Vietnamese, math and symbol subsets Google
   also offers are not needed here.

   unicode-range values are Google's own, unmodified: the browser downloads
   latin-ext only when a page actually contains those characters, so an
   ordinary page costs one file per family. */
@font-face {
  font-family: 'Roboto';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/roboto-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: 'Roboto';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/roboto-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: 'Roboto Condensed';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/roboto-condensed-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: 'Roboto Condensed';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/assets/fonts/roboto-condensed-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;
}

/* ==========================================================================
   NASTAR — Prototype 1 visual system
   ==========================================================================

   Plain, hand-written CSS. No Sass, no build step, no framework — editing
   this file IS the deployment step (same philosophy as content/ being
   flat files: open it, change it, reload).

   Evolves the existing NASTAR brand rather than replacing it — see
   doc/nastar-prototype-findings.md for what was carried forward from the
   current nastar_theme (colors, fonts, general structure) versus what was
   redesigned.

   Breakpoints tested (see findings doc): 390, 430, 768, 1024, 1440px.
   Written mobile-first: base rules target phones, min-width media queries
   layer on tablet/desktop refinements.
   ========================================================================== */

/* -----------------------------------------------------------------------
   Design tokens
   ----------------------------------------------------------------------- */
:root {
  /* Brand colors — carried forward from the existing nastar_theme SCSS
     variables (sites/all/themes/.../nastar_theme/sass/_variables.scss),
     with the gold accent tightened for accessible contrast. */
  --nastar-blue-dark: #1b417d;
  --nastar-blue: #2f6fae;
  --nastar-blue-light: #5a77a4;
  --nastar-gold: #eaaf1d;
  --nastar-gold-dark: #a8760a;

  --gray-900: #222222;
  --gray-700: #46484c;
  --gray-500: #6f7378;
  --gray-300: #c7cbd1;
  --gray-200: #e2e5e9;
  --gray-100: #f4f6f8;
  --white: #ffffff;

  --font-heading: 'Roboto Condensed', 'Arial Narrow', Arial, sans-serif;
  --font-body: 'Roboto', 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;

  --radius: 8px;
  --radius-sm: 4px;
  --shadow: 0 1px 3px rgba(20, 30, 50, 0.12), 0 1px 2px rgba(20, 30, 50, 0.08);
  --shadow-lg: 0 8px 24px rgba(20, 30, 50, 0.16);

  --container-width: 1180px;
  --container-narrow: 760px;

  /* Minimum touch target, per the mobile/WebView requirement. */
  --tap-min: 44px;
}

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

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* Reserve the vertical scrollbar's width even when a page is short enough
     not to need one. Without it, moving between a short page and a long one
     changes the usable width by the scrollbar's ~15px, and every centred
     element in the chrome — the logo badge, the whole nav row — jumps
     sideways by half that. Reported by David 2026-08-18 moving between
     skiracing's Race Record (1240px tall, no scrollbar on a tall window) and
     recentraces (3585px, always scrolls).

     Not a chrome defect; it is the ordinary scrollbar-jump that affects any
     site with pages of differing length, and it applies to both sites.
     Browsers without support (Safari <16.4) ignore it and behave as before,
     so this degrades cleanly. Invisible on platforms with overlay scrollbars,
     which is why headless Chromium measured the two pages as identical. */
  scrollbar-gutter: stable;
}

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  /* Safety net for ORDINARY nastar.com pages: nothing on them is
     *supposed* to overflow the viewport, so this is cheap insurance
     against a stray element doing it by accident.

     Pages that intentionally model wrapping an existing wide
     skiracing.nastar.com page (currently just /results-demo — see "Wide
     results region" below) opt OUT of this via a `.page-allows-wide-content`
     class rendered on BOTH <html> and <body> (app/templates/layout.php) —
     overflow-x has to be overridden on the actual root <html> element, not
     just <body>, or the browser still clips horizontal scroll there. This
     isn't a bug workaround; it's the verified, intentional behavior of the
     real site: see doc/nastar-prototype-findings.md, "Prototype 1.3.2". */
  overflow-x: hidden;
}
html.page-allows-wide-content,
html.page-allows-wide-content body {
  overflow-x: visible;
}

body {
  font-family: var(--font-body);
  color: var(--gray-900);
  font-size: 16px;
  line-height: 1.55;
  background: var(--white);
}

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

a { color: var(--nastar-blue); text-decoration: none; }
a:hover, a:focus { text-decoration: underline; }

h1, h2, h3, h4 {
  font-family: var(--font-heading);
  color: var(--nastar-blue-dark);
  line-height: 1.15;
  margin: 0 0 var(--space-3);
  font-weight: 700;
}
h1 { font-size: clamp(1.9rem, 5vw, 2.75rem); }
h2 { font-size: clamp(1.4rem, 3.2vw, 1.9rem); }
h3 { font-size: 1.15rem; }

p { margin: 0 0 var(--space-4); }

ul, ol { margin: 0 0 var(--space-4); padding-left: 1.25em; }

table { border-collapse: collapse; }

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  background: var(--nastar-blue-dark);
  color: #fff;
  padding: var(--space-3) var(--space-4);
  z-index: 1000;
}
.skip-link:focus { left: var(--space-3); top: var(--space-3); }

/* -----------------------------------------------------------------------
   Layout helpers
   ----------------------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container-width);
  margin-inline: auto;
  padding-inline: var(--space-4);
}
.container--narrow { max-width: var(--container-narrow); }

.section { padding-block: var(--space-7); }
.section--alt { background: var(--gray-100); }
.section__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}
.section__more { font-weight: 600; white-space: nowrap; }

.two-col {
  display: grid;
  gap: var(--space-7);
}
@media (min-width: 900px) {
  .two-col { grid-template-columns: 1.1fr 0.9fr; align-items: start; }
}

/* -----------------------------------------------------------------------
   Buttons
   ----------------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-min);
  padding: 0 var(--space-5);
  border-radius: var(--radius-sm);
  font-weight: 600;
  font-family: var(--font-body);
  border: 2px solid transparent;
  cursor: pointer;
  text-decoration: none !important;
}
.btn--accent { background: var(--nastar-gold); color: var(--gray-900); }
.btn--accent:hover { background: var(--nastar-gold-dark); color: #fff; }
.btn--outline { border-color: var(--nastar-blue); color: var(--nastar-blue); background: transparent; }
.btn--outline:hover { background: var(--nastar-blue); color: #fff; }
.btn--ghost-light { border-color: rgba(255,255,255,0.7); color: #fff; background: transparent; }
.btn--ghost-light:hover { background: rgba(255,255,255,0.15); }

/* -----------------------------------------------------------------------
   Header / navigation — restored blue banner (Prototype 1.2)

   Two stacked bands, reproducing nastar_theme's #header structure:
     .site-header__band  — full-width dark-navy banner (was .navbar-header,
                            background: header-bg.png). Height/colors are
                            copied from the current theme, not invented.
     .site-header__bar   — white nav row below it (was .navbar-nav / #navbar).
   The large NASTAR logo (.site-logo--badge) overlaps the seam between the
   two, matching production's li.logo (absolutely positioned, negative
   top pulling it up out of the white row into the band above it) — see
   doc/nastar-prototype-findings.md "Prototype 1.2" for the exact source
   measurements this was built from.
   ----------------------------------------------------------------------- */
/* Deliberately NOT sticky — do not re-add `position: sticky; top: 0`.

   A sticky rule lived here from the Prototype 1.2 baseline (b54cc62) until
   2026-08-18, but it never rendered on nastar.com: `overflow-x: hidden` on
   <html> (the safety net near the top of this file) silently disables sticky
   positioning. So the approved Prototype 1.3.2 appearance was always a
   non-sticky header — a sticky nastar.com header was never reviewed.

   It DID render on skiracing.nastar.com, whose copy of this theme omits that
   overflow rule. Measured there at 390x844: it cost 125px of an 844px phone
   viewport permanently, grew to ~506px (60% of the screen) with the mobile
   menu open while the document kept scrolling behind it, and — because sticky
   constrains only the block axis — slid horizontally to a 47px sliver as soon
   as a wide race-results table was scrolled right, which is exactly the case
   skiracing exists to serve. Production has never had a sticky header on
   either site.

   `position: relative` rather than `static` is deliberate: it preserves the
   stacking context the sticky rule established, so the desktop dropdown menus
   (z-index 50) keep layering above page content unchanged. A non-stuck sticky
   element renders identically to a relative one, so this is a no-op on
   nastar.com in every scroll state.

   Removed 2026-08-18 by Dave's explicit decision (OPEN DECISION 1). Evidence:
   doc/shared-site-chrome-phase1a.md §2 and §4.2. Keep this file and
   theme_2026/css/nastar.css in sync — they are a matched pair. */
.site-header {
  position: relative;
  z-index: 100;
}

.site-header__band {
  width: 100%;
  /* 124px matches nastar_theme's actual mobile/tablet .navbar-header height
     (sass/xs/_header.scss, sass/sm/_header.scss) — restored in Prototype
     1.3 after Prototype 1.2's compact 56px version read as under-scaled
     next to the real site. See findings doc, "Prototype 1.3". */
  height: 124px;
  background-color: #102c5e;
  background-image: url("/assets/images/header-banner-bg-mobile.jpg");
  background-repeat: no-repeat;
  background-position: center top;
  background-size: cover;
}
.site-header__band-inner {
  max-width: var(--container-width);
  height: 100%;
  margin-inline: auto;
  padding-inline: var(--space-4);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.site-header__bar {
  background: var(--white);
  border-bottom: 1px solid var(--gray-200);
}
.site-header__bar-inner {
  max-width: var(--container-width);
  margin-inline: auto;
  padding-inline: var(--space-4);
}

.site-logo { display: block; }
.site-logo img { display: block; height: auto; }
/* Production renders its mobile logo (nastar-logo-small-2021.png, 200x119)
   at natural size inside the 124px band with no CSS constraint at all —
   confirmed by the file's own dimensions almost exactly filling the band
   height. 100px here is a close, deliberately slightly-trimmed match
   (full band height minus room for the hamburger/padding), not an
   invented smaller scheme. */
/* Width hard-coded (not left to auto-computation) to nastar-logo.svg's
   true 1051x597 aspect ratio at this display height — see the footer-logo
   fix in Prototype 1.3.2 findings for why "width: auto" wasn't trusted to
   reliably preserve aspect ratio here (the HTML width/height attributes on
   this <img> don't match this CSS display size, and not every engine —
   including, potentially, the target Flutter WebView — recomputes width
   from the image's real intrinsic size in that situation). Logo replaced
   2026-08-25 (David) with Katie's new nastar_red_blue_solid set — same
   reasoning, updated ratio (1051:597 vs the old file's 500:297). */
.site-logo--mobile img { height: 100px; width: 176px; }
/* The big center logo badge is a desktop treatment only (production hides
   li.logo at its xs/sm breakpoints too — the mobile band uses the small
   corner logo instead, same as here). */
.site-logo--badge { display: none; }

/* Center the mobile-band NASTAR logo relative to the viewport, matching pair
   commit skiracing.nastar.com 37179e0 ("Center mobile-band NASTAR logo
   relative to the viewport"). The base rule above
   (.site-header__band-inner { justify-content: space-between }) put the logo
   at the left edge and the hamburger at the right, centering nothing. This
   takes the logo out of flex flow and centers it absolutely, with the
   hamburger staying the only flex item, pinned right via the same
   justify-content: flex-end used at >= 992px elsewhere in this file.
   Boundary matches this file's own `min-width: 768px` tablet breakpoint
   exactly.

   `left: 50vw`, not `left: 50%` — deliberate, found 2026-08-23. Both
   nastar.com and skiracing.nastar.com carry the shared `scrollbar-gutter:
   stable` rule above, but nastar.com also sets `overflow-x: hidden` on
   <html>/<body> (its own safety net) while skiracing.nastar.com deliberately
   does not (it needs the whole page, header included, to scroll horizontally
   for wide race-results tables — see the project instructions,
   "Responsive/shared-theme requirement"). That difference makes each site's
   <html>/<body> box resolve to a genuinely different actual width at the
   identical nominal viewport
   width (measured: 375px vs 383px at a 390px viewport), so a `%`-based left
   offset — resolved against that box — centered the logo at a different
   physical pixel on each site: a 4-8px sideways jump crossing between them.
   `50vw` resolves against the browser's nominal viewport size instead, which
   is unaffected by either site's scrollbar-gutter/overflow bookkeeping, so it
   lands at the identical physical pixel on both sites regardless of how much
   width either one's own scrolling mechanics happen to reserve. Verified
   against real DEV skiracing.nastar.com at 390/430px: both logos land at the
   true viewport center (195px / 215px) post-fix, vs a 4-8px mismatch before.
   This still depends on .site-header__band-inner sitting flush at the
   physical left edge below 768px (true today, and already assumed by the
   original percentage-based version of this rule). */
@media (max-width: 767px) {
  .site-header__band-inner {
    position: relative;
    justify-content: flex-end;
  }
  .site-logo--mobile {
    position: absolute;
    left: 50vw;
    top: 50%;
    transform: translate(-50%, -50%);
  }
}

.nav-toggle {
  width: var(--tap-min);
  height: var(--tap-min);
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
}
.nav-toggle__bar {
  width: 24px;
  height: 2px;
  background: #fff;
  display: block;
}

.primary-nav {
  display: none;
  width: 100%;
}
.primary-nav.is-open { display: block; }

/* --- Nav menu: flat list on the surface, but items with children carry
   a nested .nav-submenu. Same markup drives both mobile accordion and
   desktop dropdown — only the CSS differs per breakpoint below. --- */
.nav-menu {
  list-style: none;
  margin: var(--space-3) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.nav-menu__item {
  position: relative;
  border-top: 1px solid var(--gray-200);
}
.nav-menu__item > a,
.nav-menu__toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: var(--space-3) var(--space-2);
  color: var(--gray-900);
  font-weight: 600;
  font-family: var(--font-body);
  font-size: 1rem;
  background: none;
  border: none;
  text-align: left;
  min-height: var(--tap-min);
  cursor: pointer;
}
.nav-menu__caret {
  width: 8px;
  height: 8px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  margin-left: var(--space-2);
  flex-shrink: 0;
  transition: transform 0.15s ease;
}
.has-children.is-open > .nav-menu__toggle .nav-menu__caret { transform: rotate(-135deg); }

/* Submenu: accordion on mobile — hidden until parent is .is-open, then
   flows inline (pushes following items down, never overlays content). */
.nav-submenu {
  list-style: none;
  margin: 0;
  padding: 0 0 var(--space-2) var(--space-4);
  display: none;
}
.has-children.is-open > .nav-submenu { display: block; }
.nav-submenu a {
  display: block;
  padding: var(--space-2);
  color: var(--gray-700);
  font-weight: 500;
  min-height: var(--tap-min);
}

.nav-menu__item--search { border-bottom: 1px solid var(--gray-200); }
/* The logo-badge spacer only exists as a desktop layout device (see
   below) — on mobile it would just be an empty accordion row. */
.nav-menu__logo-gap { display: none; }

/* Persistent logged-in account status (app/lib/Account.php) — a sibling of
   <nav class="primary-nav">, not something inside it, so it is never
   subject to `.primary-nav`'s mobile display:none/.is-open collapse.
   Exactly one instance renders in the markup at every breakpoint (see
   header.php); only its layout changes below 1280px vs. above it. Sits
   directly under the header band on mobile, its own light background and
   bottom border reading as a status row rather than a nav item. */
.account-bar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--tap-min);
  padding: var(--space-2) var(--space-4);
  background: var(--gray-100);
  border-bottom: 1px solid var(--gray-200);
}
.account-bar__icon { width: 18px; height: 18px; flex-shrink: 0; fill: var(--gray-700); }
/* Name/logout links stretch to the row's full height so their actual
   clickable area meets --tap-min, not just the row they're centered in. */
.account-bar__name,
.account-bar__logout {
  display: flex;
  align-items: center;
  min-height: var(--tap-min);
  font-weight: 600;
}
.account-bar__name { color: var(--gray-900); }
.account-bar__name:hover { color: var(--nastar-gold); text-decoration: none; }
.account-bar__sep { color: var(--gray-300); }
.account-bar__logout { color: var(--nastar-blue); font-size: 0.9rem; }
.account-bar__logout:hover { color: var(--nastar-gold); text-decoration: none; }

/* Desktop nav/header breakpoint — 992px, restoring production's value
   (Dave, 2026-08-18; doc/shared-site-chrome-phase1a.md OPEN DECISION 2).

   This was 1280px, chosen in Prototype 1.3.2 on the estimate that the
   desktop row needed ~1120-1150px of content width. That estimate came from
   PHP GD text metrics using DejaVu Sans Bold as a stand-in for Roboto,
   because no headless browser existed on this VM at the time, and it was
   ~200px too high. Measured for real in Chromium on 2026-08-18 with Roboto
   confirmed loaded, on BOTH sites (they are identical):

     eight nav items          644px
     logo badge (.nav-menu__logo-gap)  286px
     ------------------------------------
     row content              930px
     + .site-header__bar-inner padding  32px
     = minimum viewport       962px

   992px therefore fits with ~30px to spare, and matches what production has
   always used — recovering the 992-1279px band (landscape tablets, small
   laptops, half-screen desktop windows) which had been getting the phone
   menu. Logged-out is the worst case; a logged-in racer loses the 67px
   "LOG IN" item.

   CAUTION when changing nav labels: .nav-menu is `flex-wrap: nowrap`, so the
   row cannot wrap to a second line to show you it no longer fits. Past the
   limit it overflows and is silently clipped by the `overflow-x: hidden`
   safety net near the top of this file. Headroom here is ~30px — re-measure
   in a browser before adding or renaming a top-level item.

   The 286px logo badge is 31% of the row, and is what holds this floor at
   962px; without it the menu fits down to a 660px viewport. Relocating it
   into the navy band at intermediate widths, to push the breakpoint lower
   still, is a separate open design question — not decided here. */
/* Horizontal menu from 768px, not 992px. Matched pair with
   theme_2026/css/nastar.css on skiracing.nastar.com — these two blocks must
   stay identical.

   Splits the old single desktop block in two, because the two things it did
   have different width requirements:

     >= 768px  the horizontal menu itself (this block). The eight nav items
               need only 644px + 32px padding = 676px, so they fit here
               comfortably (~92px spare at 768px).
     >= 992px  the full desktop header identity (block further below): the
               82px banner band and the 286px logo badge straddling the seam.
               The badge is what forces 962px; it is 31% of the row.

   Between 768 and 991px the header keeps the compact logo in the band — the
   treatment phones already use — and gains the real menu instead of the
   hamburger. Nothing above 992px or below 768px changes.

   Approved by Dave 2026-08-18 after reviewing it rendered on both sites.
   See doc/shared-site-chrome-phase1a.md OPEN DECISION 2. */
@media (min-width: 768px) {
  .nav-toggle { display: none; }
  /* Centre the compact logo in the band. On phones it sits left with the
     hamburger on the right, which balances it; with the hamburger gone that
     leaves a wide empty band. Centring also echoes where the badge sits at
     >= 992px, so crossing that breakpoint reads as the logo settling rather
     than jumping to the corner. The >= 992px block below overrides this back
     to flex-end, where the centred badge occupies the middle instead. */
  .site-header__band-inner { justify-content: center; }

  /* .account-bar (if present) and .primary-nav share this row — order
     below puts the nav first visually regardless of their markup order
     (account-bar comes first in the HTML so it has a stable position on
     mobile; see header.php). */
  .site-header__bar-inner { display: flex; align-items: stretch; }
  .primary-nav {
    display: flex !important;
    flex: 1 1 auto;
    width: auto;
    order: 1;
  }
  .nav-menu {
    flex-direction: row;
    align-items: stretch;
    justify-content: center;
    flex-wrap: nowrap;
    gap: 0;
    margin: 0;
    width: 100%;
  }
  /* flex-shrink: 0 keeps every item at its natural (nowrap) width instead
     of the flex row silently compressing items below their content size —
     that compression, not a lack of white-space:nowrap alone, was the
     actual cause of "Log In" wrapping onto two lines in Prototype 1.3. */
  .nav-menu__item { border-top: none; border-left: 1px solid var(--gray-200); flex-shrink: 0; }
  .nav-menu__item:first-child { border-left: none; }
  /* Font size, letter-spacing, and row height below match nastar_theme's
     actual desktop nav (sass/lg/_header.scss: `.navbar-nav { height: 49px }`,
     `li a { letter-spacing: 1px }`, breakpoint font-sizes 12-16px) — Prototype
     1.2's 64px row / 0.85rem / 0.06em read noticeably larger than the real
     site and was corrected in Prototype 1.3. Padding tightened further in
     1.3.1 (12px → 10px, closer to production's own md-tier 6px) to leave
     more headroom before the row's total width can exceed its container. */
  .nav-menu__item > a,
  .nav-menu__toggle {
    padding: 0 10px;
    min-height: 49px;
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 1px;
    justify-content: center;
    /* The direct fix for "Log In" (and any other label) wrapping onto two
       lines — Prototype 1.3.1. */
    white-space: nowrap;
  }
  .nav-menu__item > a:hover,
  .nav-menu__toggle:hover,
  .has-children.is-open > .nav-menu__toggle {
    color: var(--nastar-gold);
    text-decoration: none;
  }

  /* Same row height/rhythm as the nav links beside it (49px, a left
     divider matching .nav-menu__item's own border-left pattern) so it
     reads as an intentional part of the bar — but no text-transform/
     letter-spacing (a name shouldn't be forced into ALL CAPS). Pushed to
     the end of the row via order, independent of its markup position. */
  /* The logged-in account bar sits in the navy band, not in the nav row.

     It used to share the row with the menu, and the row cannot hold both.
     Logged in, the desktop row needs 862px of nav (including the 286px logo
     badge) + 244px of account bar + 32px padding = 1138px, and the
     intermediate row needs 852px — both well above the widths those ranges
     start at. The symptoms were LOGOUT pushed up to 87px off the right edge
     between 992 and ~1090px (silently clipped by the overflow-x safety net
     on nastar.com, and page overflow on skiracing), and in the intermediate
     range the RESULTS dropdown starting at -36px with its left characters
     cut off.

     Widening the breakpoints instead would not have worked: the bar is
     168px for a short name and 377px for a long one, so the row's real
     requirement moves with whoever is logged in and no fixed number is
     correct for everyone. Taking the bar out of the row removes the
     competition rather than guessing.

     The band has the space — it holds only the centred logo — and the bar
     is racer status rather than navigation, so it reads sensibly there.
     Colours flip to light-on-navy: measured 10.8:1 contrast against the
     lightest pixel of the band artwork behind it, where WCAG AA wants 4.5. */
  .account-bar {
    position: absolute;
    top: 0;
    right: var(--space-4);
    height: 124px;
    min-height: 0;
    padding: 0;
    background: none;
    border: none;
    z-index: 30;
  }
  .account-bar__icon { fill: rgba(255,255,255,0.9); }
  /* min-height:0 matters: the base rule stretches these to a 44px tap
     target and the desktop rules to 49px. Left in place, a block-level name
     renders its text at the top of that box while LOGOUT stays centred in
     its own, so the two sit visibly out of line. */
  .account-bar__name,
  .account-bar__logout {
    min-height: 0;
    white-space: nowrap;
    color: #fff;
  }
  .account-bar__name {
    /* display:block overrides the base rule's flex — text-overflow does not
       apply to a flex container's own text, so the name hard-clipped
       mid-letter instead of showing an ellipsis. */
    display: block;
    max-width: 150px;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .account-bar__sep { color: rgba(255,255,255,0.45); }
  .account-bar__name:hover,
  .account-bar__logout:hover { color: var(--nastar-gold); }
  .account-bar__logout { font-size: 13px; }

  /* Desktop dropdown: a clean, self-contained floating panel — click-to-open
     (not hover-only, matching production's own click-toggle behavior and
     staying touch/WebView-friendly) but deliberately NOT a reproduction of
     production's own dropdown presentation (a full-width flyout panel with
     an embedded ad-unit region) — instructed to use ordinary, professional
     styling here rather than copy Drupal's dropdown treatment. */
  .nav-submenu {
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    min-width: 200px;
    background: var(--white);
    border: 1px solid var(--gray-200);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lg);
    padding: var(--space-2) 0;
    z-index: 50;
  }
  .nav-submenu a {
    color: var(--gray-900);
    padding: var(--space-2) var(--space-4);
    min-height: auto;
    font-size: 0.85rem;
    text-transform: none;
    letter-spacing: normal;
    font-weight: 500;
  }
  .nav-submenu a:hover { background: var(--gray-100); text-decoration: none; }

  .nav-menu__item--search { border-bottom: none; }

}

/* Full desktop header identity — the 82px banner band and the overlapping
   logo badge. Held at 992px because the badge reserves 286px inside the nav
   row, which is what makes the row need 962px of viewport; the menu itself
   needs only 676px. Below this, the band keeps the compact logo instead. */
@media (min-width: 992px) {
  .site-header__band {
    /* 82px matches nastar_theme's real desktop .navbar-header height
       (sass/lg/_header.scss) — was 64px in Prototype 1.2. */
    height: 82px;
    background-image: url("/assets/images/header-banner-bg.png");
    /* no-repeat + cover, matching nastar_theme's actual .navbar-header rule
       (sass/lg/_header.scss) and this file's own mobile band rule above —
       NOT repeat-x. header-banner-bg.png is a vignette/gradient (its left
       edge is a visibly different color than its right edge — sampled with
       GD), so tiling it produces a hard seam at every repeat boundary on
       wide viewports. Production never tiles it; a single cover-scaled copy
       fills the full-width band with no seam at any width. See
       doc/nastar-prototype-findings.md, "Wide-viewport banner / seam fix". */
    background-repeat: no-repeat;
    background-size: cover;
  }
  @media (min-resolution: 2dppx), (-webkit-min-device-pixel-ratio: 2) {
    .site-header__band { background-image: url("/assets/images/header-banner-bg@2x.png"); }
  }
  .site-header__band-inner { justify-content: flex-end; }
  .site-logo--mobile { display: none; }
  /* Band is 82px here rather than 124px, so the account bar's own height
     follows it to stay vertically centred. There is more room beside the
     centred badge at these widths, so the name cap can be looser. */
  .account-bar { height: 82px; }
  .account-bar__name { max-width: 220px; }

  /* Logo badge: reproduces production's li.logo — a reserved-width gap in
     the nav row with the actual logo absolutely positioned to overlap
     upward into the blue band above (and, per the real image height vs.
     the ~49px row, downward into the page content below the header — the
     "slight overlap into the hero/carousel area" this is matching).
     Numbers below are production's own lg/md values
     (sass/lg/_header.scss / sass/md/_header.scss): li.logo width 286px,
     top: -78px. Prototype 1.2 had shrunk these (150px/-26px/96px tall). */
  .nav-menu__logo-gap {
    display: block;
    width: 286px;
    flex: 0 0 auto;
    border-left: none;
  }
  .site-logo--badge {
    display: block;
    position: absolute;
    top: -78px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 20;
  }
  /* Sized to fill the 286px .nav-menu__logo-gap slot exactly, at the logo's
     true 1051x597 aspect ratio (height derived from width, not the other
     way around, unlike the mobile/footer logos) — because this logo's
     badge <a> sits inside .nav-menu__item, which is position:relative for
     the dropdown submenus, making that 286px-wide <li> its containing
     block. The page-wide `img, svg, video { max-width: 100% }` reset
     therefore silently caps this image's rendered width at 286px
     regardless of what's declared here. That was already true of the old
     291px value (production's own li.logo width is 286px too, per the
     comment above) and was a harmless ~2% clip at the old 500:297 ratio —
     but the new logo's more elongated 1051:597 ratio made the same clamp
     visibly squash it when height was held at the old 173px. Found and
     fixed 2026-08-25 during the logo replacement: sized to fill the slot
     exactly (286x162, correct ratio) and the reset's max-width overridden
     back to none so that declared size actually renders instead of being
     clamped again by whatever's left of the padding/flex box math. */
  .site-logo--badge img { height: 162px; width: 286px; max-width: none; }
}

/* --- Racer/site search flyout (toggled via the "Search" nav item) --- */
.search-region {
  border-top: 1px solid var(--gray-200);
  background: var(--gray-100);
}
.search-region[hidden] { display: none; }
.search-region__inner {
  padding-block: var(--space-5);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
}
.search-region__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-width: 360px;
}
.search-region__form label { font-weight: 600; font-size: 0.9rem; }
.search-region__form input[type="text"] {
  min-height: var(--tap-min);
  padding: 0 var(--space-3);
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
  font-size: 1rem;
}

/* -----------------------------------------------------------------------
   Footer — background restored to production's actual near-black
   (sass/lg/_footer.scss: `#footer { background: #07060b; }`), not the
   medium blue Prototype 1/1.2/1.3 had used. Prototype 1.3.1.
   ----------------------------------------------------------------------- */
.site-footer {
  background: #07060b;
  color: rgba(255,255,255,0.85);
  margin-top: var(--space-8);
}
.site-footer__main {
  max-width: var(--container-width);
  margin-inline: auto;
  padding: var(--space-7) var(--space-4);
  display: grid;
  gap: var(--space-6);
}
/* The footer logo is deliberately a different file from the header's, as of
   2026-08-26: NASTARLogo_Black_Outlined_150w.png (black-and-white outlined
   mark), while the header uses nastar-logo.svg (the red/blue full-color
   mark). Both ship their own contrast/detail rather than depending on the
   page background, so no filter is needed here; the black outlined mark
   reads fine against the footer's near-black background. */
/* Width hard-coded to the footer logo's approximate 1050x600-class aspect
   ratio (Prototype 1.3.2 — was squished: only `height` was set, and this
   <img>'s HTML width/height attributes don't match this display size, so the
   rendered width wasn't reliably preserving proportions). 70x40 (1.75:1)
   remains a close match to NASTARLogo_Black_Outlined_150w.png's true
   150x85 (1.765:1) — same tolerance already accepted for the header logo. */
.site-footer__brand img { height: 40px; width: 70px; margin-bottom: var(--space-3); }
.site-footer__brand p { max-width: 40ch; color: rgba(255,255,255,0.7); }
/* One wrapping row of links, not labelled columns. The earlier
   heading-per-column grid (NASTAR / Policies / Account) was dropped
   2026-08-18 along with the headings themselves: production's own footer has
   no visible column headings, and at five links a heading for each pair of
   items outweighs what it labels. Wrapping keeps it correct on a phone
   without a breakpoint of its own. See app/templates/partials/footer.php. */
.site-footer__links ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
}
.site-footer__links li { margin-bottom: 0; }
.site-footer__links a { color: rgba(255,255,255,0.75); }
.site-footer__links a:hover { color: #fff; }

.site-footer__prototype {
  border-top: 1px solid rgba(255,255,255,0.2);
  padding: var(--space-4);
  font-size: 0.85rem;
  text-align: center;
  color: rgba(255,255,255,0.7);
}
.site-footer__prototype a { color: var(--nastar-gold); margin: 0 var(--space-1); }

.site-footer__legal {
  text-align: center;
  padding: var(--space-3);
  font-size: 0.8rem;
  color: rgba(255,255,255,0.5);
  border-top: 1px solid rgba(255,255,255,0.1);
}

@media (min-width: 900px) {
  .site-footer__main { grid-template-columns: 1.2fr 2fr; }
}

/* -----------------------------------------------------------------------
   Hero carousel — real, production-matched featured content.
   Content/order comes from config/homepage.json (app/lib/Homepage.php),
   not from this template — see doc/nastar-prototype-findings.md
   "Homepage curation". Layout below is prototype exploration; the
   editorial selection is deliberately NOT being redesigned yet.
   ----------------------------------------------------------------------- */
.hero-carousel {
  position: relative;
  /* width must stay explicit — with width left auto, a browser computing
     this box's preferred aspect ratio treats max-height as authoritative
     once it clamps below the aspect-ratio height, and BACK-SOLVES width
     from that clamped height instead of stretching to fill the container
     (confirmed empirically: at a 2400px viewport this rendered at exactly
     520 * 21/9 = 1213px wide, flush-left, not 2400px — see
     doc/nastar-prototype-findings.md, "Carousel still not filling the
     window: the real root cause"). Pre-existing since the aspect-ratio
     was first added; unrelated to the peek-slide work below. */
  width: 100%;
  aspect-ratio: 16 / 9;
  max-height: 520px;
  overflow: hidden;
  background: var(--nastar-blue-dark);
}
@media (min-width: 700px) {
  .hero-carousel { aspect-ratio: 21 / 9; }
}
.hero-carousel__slide {
  position: absolute;
  inset: 0;
  display: none;
}
.hero-carousel__slide.is-active { display: block; }

/* Desktop wide-screen presentation, matching production's actual carousel
   mechanism — read directly from the live Drupal config (Slick optionset
   "manual_carousels_default": centerMode:1, centerPadding:"15%",
   slidesToShow:1, infinite:1), not guessed. The active slide is centered
   at 70% of the full-bleed container's width (100% - 2*15%); the real
   previous/next slides sit immediately adjacent and are cropped by the
   container's own overflow:hidden (above) to a 15%-wide sliver on each
   side. This is genuine neighboring slide content peeking through — not a
   background image, not a duplicate of the active image, not a
   pseudo-element trick — matching production's own manual_carousels.css
   dimming (`.slick-track div.slide{opacity:.7}` + an rgba(0,0,0,.6)
   scrim; the current slide gets neither).
   Scoped to this carousel's own existing 700px breakpoint (same one that
   already switches the aspect ratio to 21:9) rather than to 900/1280px —
   production's own Slick optionset has no responsive breakpoint overrides
   at all, so it shows this same peeking presentation at every width,
   including mobile; 700px was chosen here as a deliberate, narrower scope
   than production so the already-approved single-slide mobile treatment
   is left unchanged, per explicit instruction — not a fidelity gap, a
   scoped simplification. */
@media (min-width: 700px) {
  .hero-carousel__slide {
    display: block;
    width: 70%;
    left: 50%;
    /* The base (mobile) rule above sets this via `inset: 0`, which this
       block never otherwise touches — left + width + a still-active
       right:0 left the box over-constrained, which is what was actually
       causing the "doesn't reach the window edge" symptom below (this
       comment is intentionally short; see doc/nastar-prototype-findings.md
       "Carousel fix wasn't visually appearing" for the full diagnosis). */
    right: auto;
    transform: translateX(-50%);
    opacity: 0;
    z-index: 1;
    transition: opacity 0.4s ease, transform 0.4s ease;
  }
  .hero-carousel__slide.is-active {
    opacity: 1;
    z-index: 3;
  }
  .hero-carousel__slide.is-prev,
  .hero-carousel__slide.is-next {
    opacity: 0.7;
    z-index: 2;
  }
  .hero-carousel__slide.is-prev { transform: translateX(-50%) translateX(-100%); }
  .hero-carousel__slide.is-next { transform: translateX(-50%) translateX(100%); }
  .hero-carousel__slide.is-prev::after,
  .hero-carousel__slide.is-next::after {
    content: "";
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.6);
  }
  .hero-carousel__slide:not(.is-active):not(.is-prev):not(.is-next) {
    display: none;
  }
}
.hero-carousel__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Caption panel: a solid translucent dark bar anchored to the bottom edge
   of the slide, not a full-image gradient — this is production's actual
   treatment (sites/all/modules/.../ussa_core/assets/css/manualcarousels/
   manual_carousels.css: `.slide-description { position:absolute; bottom:0;
   left:0; right:0; padding: 20px 2%; }` + `.slick-current .slide-description
   { background: rgba(0,0,0,0.7); }`). Prototype 1.2 had used a full-height
   gradient scrim instead of this bounded panel — restored in Prototype 1.3
   to match the real site, per doc/nastar-prototype-findings.md. */
.hero-carousel__caption {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 20px 2%;
  background: rgba(0, 0, 0, 0.7);
  color: #fff;
  text-align: center;
}
.hero-carousel__eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  color: var(--nastar-gold);
  font-size: 0.8rem;
  margin-bottom: var(--space-2);
}
.hero-carousel__title {
  margin: 0 auto;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-transform: uppercase;
  font-weight: 400;
}
.hero-carousel__title a { color: #fff; text-decoration: none; }
.hero-carousel__title a:hover { text-decoration: underline; }

.hero-carousel__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: var(--tap-min);
  height: var(--tap-min);
  border-radius: 50%;
  border: none;
  background: rgba(10,20,40,0.45);
  color: #fff;
  font-size: 1.1rem;
  cursor: pointer;
  z-index: 5;
}
.hero-carousel__nav:hover { background: rgba(10,20,40,0.7); }
.hero-carousel__nav--prev { left: var(--space-3); }
.hero-carousel__nav--next { right: var(--space-3); }

.hero-carousel__dots {
  position: absolute;
  right: var(--space-4);
  bottom: var(--space-4);
  display: flex;
  gap: var(--space-2);
  z-index: 5;
}
.hero-carousel__dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,0.8);
  background: transparent;
  padding: 0;
  cursor: pointer;
}
.hero-carousel__dot.is-active { background: var(--nastar-gold); border-color: var(--nastar-gold); }

.prototype-inline-note {
  font-size: 0.78rem;
  color: var(--gray-500);
  font-style: italic;
  padding-block: var(--space-2);
}

/* -----------------------------------------------------------------------
   Racing Today stats band (mock data — see app/data/fixtures/racing-today.json)
   ----------------------------------------------------------------------- */
.racing-today { padding-block: var(--space-6); }
.racing-today__eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  color: var(--nastar-blue);
  font-size: 0.8rem;
  margin-bottom: var(--space-3);
}
.hero__stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);
}
.hero__stat {
  background: var(--gray-100);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  padding: var(--space-4);
  text-align: center;
}
.hero__stat-value {
  display: block;
  font-family: var(--font-heading);
  font-size: 1.8rem;
  font-weight: 700;
  color: var(--nastar-blue-dark);
}
.hero__stat-label { display: block; font-size: 0.8rem; color: var(--gray-500); margin-top: var(--space-1); }
.hero__mock-note {
  margin-top: var(--space-3);
  font-size: 0.75rem;
  color: var(--gray-500);
  font-style: italic;
}

@media (min-width: 700px) {
  .hero__stats { grid-template-columns: repeat(4, 1fr); }
}

/* -----------------------------------------------------------------------
   Race cards (Recent Races)
   ----------------------------------------------------------------------- */
.race-cards {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: 1fr;
}
.race-card {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--space-1) var(--space-4);
  align-items: baseline;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  padding: var(--space-4);
  box-shadow: var(--shadow);
  color: var(--gray-900);
}
.race-card:hover { border-color: var(--nastar-blue); text-decoration: none; }
.race-card__date {
  grid-row: 1 / 3;
  font-family: var(--font-heading);
  font-weight: 700;
  color: var(--nastar-blue-dark);
  background: var(--gray-100);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  align-self: center;
  text-align: center;
  min-width: 64px;
}
.race-card__resort { font-weight: 700; }
.race-card__name { color: var(--gray-700); grid-column: 2; }
.race-card__participants {
  grid-column: 2;
  font-size: 0.85rem;
  color: var(--gray-500);
}

@media (min-width: 700px) {
  .race-cards { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1050px) {
  .race-cards { grid-template-columns: repeat(3, 1fr); }
}

/* -----------------------------------------------------------------------
   Featured Racer / Rankings
   ----------------------------------------------------------------------- */
.featured-racer__card {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  padding: var(--space-5);
  box-shadow: var(--shadow);
}
@media (min-width: 480px) {
  .featured-racer__card { flex-direction: row; align-items: flex-start; }
}

.avatar {
  flex-shrink: 0;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--nastar-blue), var(--nastar-blue-dark));
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-heading);
  font-weight: 700;
}
.avatar--lg { width: 88px; height: 88px; font-size: 1.6rem; }

.featured-racer__hometown { color: var(--gray-500); margin-top: -8px; margin-bottom: var(--space-3); }
.featured-racer__stats {
  list-style: none;
  padding: 0;
  display: flex;
  gap: var(--space-5);
  margin: var(--space-4) 0;
}
.featured-racer__stats li { display: flex; flex-direction: column; }
.featured-racer__stats strong { font-family: var(--font-heading); font-size: 1.3rem; color: var(--nastar-blue-dark); }
.featured-racer__stats span { font-size: 0.75rem; color: var(--gray-500); }

.rankings__division { font-size: 0.85rem; font-weight: 400; color: var(--gray-500); }
.rankings__table {
  width: 100%;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: var(--space-3);
}
.rankings__table th, .rankings__table td {
  padding: var(--space-3);
  text-align: left;
  border-bottom: 1px solid var(--gray-200);
  font-size: 0.92rem;
}
.rankings__table thead { background: var(--gray-100); }
.rankings__table tbody tr:last-child td { border-bottom: none; }
.rankings__table td:first-child { font-weight: 700; color: var(--nastar-blue-dark); }

/* -----------------------------------------------------------------------
   Badge grid
   ----------------------------------------------------------------------- */
.badge-grid {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(2, 1fr);
}
.badge-card a {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  padding: var(--space-4);
  color: var(--gray-900);
  box-shadow: var(--shadow);
  min-height: var(--tap-min);
}
.badge-pill {
  align-self: flex-start;
  font-size: 0.7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 2px var(--space-2);
  border-radius: 999px;
  color: #fff;
}
.badge-pill--platinum { background: #6b7280; }
.badge-pill--gold { background: var(--nastar-gold-dark); }
.badge-pill--silver { background: #8a94a3; }
.badge-card__name { font-weight: 700; }
.badge-card__resort { font-size: 0.85rem; color: var(--gray-500); }

@media (min-width: 700px) {
  .badge-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1050px) {
  .badge-grid { grid-template-columns: repeat(6, 1fr); }
}

/* -----------------------------------------------------------------------
   News cards
   ----------------------------------------------------------------------- */
.news-cards {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: 1fr;
}
.news-card {
  display: block;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  overflow: hidden;
  color: var(--gray-900);
  box-shadow: var(--shadow);
}
.news-card:hover { text-decoration: none; border-color: var(--nastar-blue); }
.news-card__image { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; background: var(--gray-100); }
.news-card__title { display: block; padding: var(--space-4); font-weight: 700; font-family: var(--font-heading); font-size: 1.05rem; }

@media (min-width: 700px) {
  .news-cards { grid-template-columns: repeat(2, 1fr); }
  .news-cards--secondary { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1050px) {
  .news-cards { grid-template-columns: repeat(3, 1fr); }
}

/* -----------------------------------------------------------------------
   Pagination (/news) — plain page-number links, no JS. A small window of
   pages around the current one plus first/last keeps this readable even
   with dozens of pages (see paginate() in public/index.php).
   ----------------------------------------------------------------------- */
.pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  margin-top: var(--space-6);
}
.pagination__pages { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-1); }
.pagination__link,
.pagination__page {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--tap-min);
  min-height: var(--tap-min);
  padding: 0 var(--space-3);
  border-radius: var(--radius-sm);
  font-weight: 600;
  color: var(--nastar-blue);
}
.pagination__link:hover, .pagination__page:hover { background: var(--gray-100); text-decoration: none; }
.pagination__page--current {
  background: var(--nastar-blue);
  color: #fff;
}
.pagination__page--current:hover { background: var(--nastar-blue); }
.pagination__ellipsis {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--space-5);
  color: var(--gray-500);
}

/* -----------------------------------------------------------------------
   Article / page body content ("richtext" — faithfully migrated HTML)
   ----------------------------------------------------------------------- */
.article__header { padding-block: var(--space-6) var(--space-4); }
.article__byline { color: var(--gray-500); font-size: 0.9rem; }
.article__hero { margin: 0 0 var(--space-5); }
.article__hero img { border-radius: var(--radius); width: 100%; object-fit: cover; max-height: 460px; }
.article__body { padding-bottom: var(--space-7); }

/* Historical body HTML is preserved as faithfully as practical (see
   doc/nastar-modernization-decisions.md §11) — these rules style
   whatever structure already exists in the migrated markup, they don't
   assume a particular one. */
.richtext { font-size: 1.05rem; color: var(--gray-700); }
.richtext img { border-radius: var(--radius-sm); margin-block: var(--space-3); }
.richtext iframe { max-width: 100%; border-radius: var(--radius-sm); }
.richtext h2, .richtext h3 { margin-top: var(--space-6); }
.richtext a { color: var(--nastar-blue); text-decoration: underline; }
.richtext blockquote {
  border-left: 4px solid var(--nastar-gold);
  margin: var(--space-5) 0;
  padding: var(--space-2) var(--space-5);
  color: var(--gray-700);
  font-style: italic;
}
.richtext table { width: 100%; margin-block: var(--space-4); }
.richtext table td, .richtext table th { padding: var(--space-2); border: 1px solid var(--gray-200); }

.prototype-note {
  margin-top: var(--space-5);
  padding: var(--space-4);
  background: #fff7e6;
  border: 1px solid var(--nastar-gold);
  border-radius: var(--radius);
  font-size: 0.9rem;
}

.not-found { padding-block: var(--space-8); text-align: center; }
.not-found .btn + .btn { margin-left: var(--space-3); }

.related-galleries {
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: 1px solid var(--gray-200);
}
.related-galleries__heading { font-size: 1.1rem; margin-bottom: var(--space-2); }
.related-galleries__list { list-style: none; padding: 0; margin: 0; }
.related-galleries__list li { margin-bottom: var(--space-1); }

/* -----------------------------------------------------------------------
   Photo gallery + lightbox
   ----------------------------------------------------------------------- */
.gallery-page { padding-bottom: var(--space-8); }
.gallery-grid {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(2, 1fr);
  margin-top: var(--space-5);
}
.gallery-grid__item {
  display: block;
  border-radius: var(--radius-sm);
  overflow: hidden;
  aspect-ratio: 1 / 1;
  background: var(--gray-100);
}
.gallery-grid__item img { width: 100%; height: 100%; object-fit: cover; }

@media (min-width: 600px) {
  .gallery-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 900px) {
  .gallery-grid { grid-template-columns: repeat(4, 1fr); }
}

.lightbox {
  position: fixed;
  inset: 0;
  background: rgba(10, 14, 22, 0.94);
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
}
.lightbox[hidden] { display: none; }
.lightbox__figure { max-width: 100%; max-height: 100%; margin: 0; text-align: center; }
.lightbox__image { max-height: 80vh; max-width: 100%; margin-inline: auto; border-radius: var(--radius-sm); }
.lightbox__caption { color: rgba(255,255,255,0.8); margin-top: var(--space-3); font-size: 0.9rem; }

.lightbox__close, .lightbox__prev, .lightbox__next {
  position: absolute;
  background: rgba(255,255,255,0.1);
  color: #fff;
  border: none;
  border-radius: 50%;
  width: var(--tap-min);
  height: var(--tap-min);
  font-size: 1.3rem;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lightbox__close:hover, .lightbox__prev:hover, .lightbox__next:hover { background: rgba(255,255,255,0.25); }
.lightbox__close { top: var(--space-4); right: var(--space-4); }
.lightbox__prev { left: var(--space-3); top: 50%; transform: translateY(-50%); }
.lightbox__next { right: var(--space-3); top: 50%; transform: translateY(-50%); }

/* ==========================================================================
   Wide results region — CRITICAL requirement, corrected in Prototype 1.3.2.

   This proves the shared theme can eventually wrap skiracing.nastar.com's
   existing race-results pages, some of which contain tables legitimately
   wider than a phone screen.

   Earlier prototype rounds (1.0-1.3.1) modeled this as a CONTAINED region
   scroll: `.results-scroll { overflow-x: auto }` around the table, page
   itself never scrolling. That was an unverified assumption. Direct
   inspection of the actual dev skiracing.nastar.com race-results page
   (see doc/nastar-prototype-findings.md, "Prototype 1.3.2") found no such
   containment anywhere in its CSS/markup — the wrapping `#scroll-fix` div
   on the real page has no CSS rule attached to it at all. The real
   behavior is simpler and different: nothing contains the table, so the
   whole document grows to the table's natural width and the BROWSER's own
   page-level horizontal scrollbar is what the user drags — including the
   shared header/nav/footer, since none of them are position:fixed/sticky
   horizontally (our header's `position: sticky; top: 0` only pins it
   vertically; it still moves left/right with the rest of the page).

   Corrected model:
   - No inner scroll container. `.results-table` sits directly in normal
     page flow with `white-space: nowrap` cells (columns are still never
     hidden, wrapped, or shrunk — that part of the original requirement
     was correct) and a natural `width: max-content`.
   - The PAGE is allowed to grow wider than the viewport on this page only
     — see `.page-allows-wide-content` above, applied via
     `app/templates/layout.php` `$pageRootClass`, opting this specific page
     out of the site-wide overflow-x:hidden safety net that every other
     (ordinary-content) page keeps.
   - Nothing here relies on JavaScript; this is pure CSS.
   ========================================================================== */
.results-table {
  width: max-content;
  border-collapse: collapse;
  font-size: 0.9rem;
  white-space: nowrap; /* columns keep their natural width, never wrap */
  margin-block: var(--space-4);
}
.results-table th, .results-table td {
  padding: var(--space-3) var(--space-4);
  text-align: left;
  border-bottom: 1px solid var(--gray-200);
}
.results-table thead th {
  background: var(--nastar-blue-dark);
  color: #fff;
  font-family: var(--font-heading);
  font-weight: 700;
  /* Deliberately NOT position:sticky here (earlier rounds had this sticky
     within the now-removed `.results-scroll` container). With no inner
     scroll container, "sticky" would mean sticky to the whole page's
     vertical scroll instead — a different behavior nobody asked for, and
     one that would visually collide with the site header's own sticky
     top:0. The real site's own results table isn't sticky either. */
}
.results-table tbody tr:nth-child(even) { background: var(--gray-100); }
.results-table tbody tr:hover { background: #eaf1fb; }

.results-scroll__hint {
  font-size: 0.8rem;
  color: var(--gray-500);
}
