/**
 * ProElite Sports — Public League Page Styles
 *
 * Styles for the public-facing league page rendered at /league/{slug}.
 * Uses --league-primary and --league-secondary CSS variables set inline
 * from the league's branding colors.
 */

/* ── Smooth cross-document navigation (MPA View Transitions) ──────────
   These are separate server-rendered pages. On a plain nav click some
   browsers tear the old page down before the new one paints, so the whole
   page — the fixed side/top panels included — flashes blank for a beat
   ("the menu disappears then comes back"). View Transitions holds the old
   page's snapshot until the new one is ready, so nothing ever blanks.
   Browsers without support just navigate normally (graceful).

   NOTE: we deliberately do NOT give .lp-header a `view-transition-name`.
   A named element is promoted to its own snapshot layer, and on Windows text
   on a composited layer loses ClearType subpixel AA → the nav links render
   soft/blurry AT REST (not just during the ~200ms transition). Crisp text wins
   over a pinned header; the whole page (header included) is one snapshot, which
   is why the fade below is zeroed rather than tuned (the old blank-flash was
   fixed separately in the menu-collapse handler, not here). See [[choppy text = composited AA]]. */
@view-transition { navigation: auto; }

/* ...but WITHOUT the cross-fade that comes free with it. The default
   animation fades the old snapshot out while the new one fades in, and since
   the header lives inside that snapshot (above), every click ghosts the
   identical nav through itself — About -> Divisions -> Standings read as a
   fade rather than a page change. Zeroing the duration keeps the snapshot
   (so nothing ever blanks) and swaps straight to the new page. Applies to
   everyone, which is why there is no longer a separate reduced-motion block:
   this IS what that block did. */
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) { animation-duration: 0.001ms !important; }

/* Nav-item icons + the collapse toggle are Read Only-skin features. The markup
   always ships them; hide them on every other Modern-path template so those
   navs stay text-only and unchanged. league-readonly.css re-shows them. */
.lp-nav-ico { display: none; }
.lp-collapse-toggle { display: none; }

/* ── Reset & Base ─────────────────────────────────────── */

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

html {
    scroll-behavior: smooth;
}

:root {
    /* Modern-template font pairing. Overridden per-league by the Pro Typography
       feature (renderPageShell injects new values + loads the Google fonts).
       These defaults preserve the original look when no pairing is chosen. */
    --lp-font-heading: 'Outfit', sans-serif;
    --lp-font-body: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    /* Page Style (Pro) — section backgrounds + shape. Defaults = current look. */
    --lp-bg-footer: #333;
    --lp-bg-about: #f8f9fa;
    /* Robb, 2026-09-21: "I hate the round pills" — buttons are modestly
       rounded, never a lozenge. A league can still choose Pill in
       Settings - Website - Design; this is only the default. */
    --lp-btn-radius: 8px;
    --lp-card-radius: 12px;
    --lp-card-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}

body {
    font-family: var(--lp-font-body);
    color: #333;
    background: #ffffff;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    min-height: 100dvh;
    margin: 0;
}

.lp-content {
    flex: 1;
}

img {
    max-width: 100%;
    height: auto;
}

a {
    color: var(--league-link, var(--league-secondary, #38bdf8));
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

/* ── Container ────────────────────────────────────────── */

.pe-container {
    max-width: 1140px;
    margin: 0 auto;
    padding: 0 24px;
}

/* ── Header ───────────────────────────────────────────── */

.lp-header {
    background: #1a1a2e;
    padding: 0 24px;
    position: sticky;
    top: 0;
    z-index: 100;
}

.lp-header-inner {
    max-width: 1140px;
    margin: 0 auto;
    display: flex;
    align-items: center;
    height: 64px;
    gap: 32px;
}

.lp-logo-link {
    display: flex;
    align-items: center;
    gap: 12px;
    text-decoration: none;
    color: #ffffff;
    flex-shrink: 0;
}

.lp-logo-link:hover {
    text-decoration: none;
}

.lp-logo-img {
    width: 36px;
    height: 36px;
    border-radius: 8px;
    object-fit: cover;
}

.lp-logo-placeholder {
    width: 36px;
    height: 36px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.12);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--lp-font-heading);
    font-weight: 700;
    font-size: 16px;
}

.lp-league-name {
    font-family: var(--lp-font-heading);
    font-weight: 600;
    font-size: 17px;
    color: #ffffff;
}

/* ── Header CTA Button ──────────────────────────────── */

.lp-header-cta {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 20px;
    border-radius: 8px;
    background: var(--league-primary, #1a1a2e);
    color: #ffffff;
    font-family: var(--lp-font-body);
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    flex-shrink: 0;
    transition: opacity 0.2s, transform 0.15s;
}

.lp-header-cta:hover {
    opacity: 0.9;
    text-decoration: none;
}

/* ── Main Content ─────────────────────────────────────── */

/* ── Sections ─────────────────────────────────────────── */

.pe-section {
    padding: 80px 0;
}

.pe-section-title {
    font-family: var(--lp-font-heading);
    font-size: 36px;
    font-weight: 700;
    color: #1a1a2e;
    margin-bottom: 12px;
    text-align: center;
    letter-spacing: -0.3px;
}

.pe-section-subtitle {
    font-size: 16px;
    color: #666;
    text-align: center;
    margin-bottom: 32px;
    max-width: 560px;
    margin-left: auto;
    margin-right: auto;
}

.pe-text {
    font-size: 16px;
    line-height: 1.7;
    color: #555;
    max-width: 680px;
    margin: 0 auto;
    text-align: center;
}

/* ── Row & Columns ────────────────────────────────────── */

.pe-row {
    display: flex;
    gap: 24px;
    flex-wrap: wrap;
}

.pe-col {
    min-height: 40px;
}

.pe-col-4 {
    flex: 1 1 calc(33.333% - 16px);
    min-width: 200px;
}

.pe-col-6 {
    flex: 1 1 calc(50% - 12px);
    min-width: 240px;
}

/* ── Hero Banner ──────────────────────────────────────── */

.pe-hero {
    padding: 112px 24px;
    text-align: center;
    background-size: cover;
    background-position: center;
    position: relative;
}

.pe-hero-image {
    padding: 128px 24px;
}

.pe-hero-image .pe-hero-title,
.pe-hero-image .pe-hero-subtitle {
    text-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}

.pe-hero-inner {
    position: relative;
    z-index: 1;
    max-width: 860px;
    margin: 0 auto;
}

.pe-hero-title {
    font-family: var(--lp-font-heading);
    font-size: 60px;
    font-weight: 700;
    margin-bottom: 20px;
    letter-spacing: -0.5px;
    line-height: 1.1;
}

.pe-hero-subtitle {
    font-size: 20px;
    margin-bottom: 36px;
    max-width: 560px;
    margin-left: auto;
    margin-right: auto;
    line-height: 1.6;
    opacity: 0.9;
}

/* ── Content Flow Sections ────────────────────────────── */

.pe-flow-section {
    padding: 80px 0;
}

.pe-flow-section.pe-flow-alt {
    background: var(--lp-bg-about);
}

.pe-flow-card {
    max-width: 720px;
    margin: 0 auto;
    text-align: center;
}

.pe-flow-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: var(--league-primary, #1a1a2e);
    color: #fff;
    margin-bottom: 20px;
}

.pe-flow-title {
    font-family: var(--lp-font-heading);
    font-size: 30px;
    font-weight: 700;
    color: #1a1a2e;
    margin: 0 0 20px;
    letter-spacing: -0.3px;
}

.pe-flow-body {
    font-size: 16px;
    line-height: 1.8;
    color: #555;
    text-align: left;
}

.pe-flow-empty {
    color: #999;
    font-style: italic;
}

/* Registration CTA Banner */
.pe-flow-cta {
    padding: 80px 0;
    background: var(--league-primary, #1a1a2e);
    color: #fff;
}

.pe-flow-cta-inner {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 48px;
    max-width: 760px;
    margin: 0 auto;
    text-align: center;
}

.pe-flow-cta-title {
    font-family: var(--lp-font-heading);
    font-size: 32px;
    font-weight: 700;
    margin: 0 0 8px;
    letter-spacing: -0.3px;
}

.pe-flow-cta-sub {
    font-size: 17px;
    margin: 0;
    opacity: 0.85;
}

.pe-flow-cta .pe-btn-light {
    white-space: nowrap;
    flex-shrink: 0;
}

/* ── News Carousel ───────────────────────────────────── */

.pe-carousel {
    position: relative;
    overflow: hidden;
    border-radius: 12px;
    max-width: 760px;
    margin: 0 auto;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}

.pe-carousel-track {
    display: flex;
    transition: transform 0.5s ease;
}

.pe-carousel-slide {
    min-width: 100%;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 48px 40px;
}

/* Image slide — background image with dark overlay */
.pe-slide-image {
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    min-height: 340px;
    position: relative;
    color: #fff;
}

.pe-slide-overlay {
    position: relative;
    z-index: 1;
    text-align: center;
    max-width: 600px;
}

.pe-slide-image::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.65));
    z-index: 0;
}

.pe-slide-image .pe-slide-title {
    color: #fff;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}

.pe-slide-image .pe-slide-body {
    color: rgba(255, 255, 255, 0.9);
}

.pe-slide-image .pe-slide-link {
    color: #fff;
    border-color: rgba(255, 255, 255, 0.6);
}

.pe-slide-image .pe-slide-link:hover {
    background: rgba(255, 255, 255, 0.15);
    border-color: #fff;
}

/* Text-only slide — white bg, dark text */
.pe-slide-text {
    background: #fff;
    color: #333;
    min-height: 280px;
    text-align: center;
}

.pe-slide-title {
    font-family: var(--lp-font-heading);
    font-size: 24px;
    font-weight: 700;
    color: #1a1a2e;
    margin: 0 0 16px;
}

.pe-slide-body {
    font-size: 15px;
    line-height: 1.7;
    color: #555;
    margin: 0 0 20px;
    max-width: 560px;
}

.pe-slide-link {
    display: inline-block;
    padding: 10px 24px;
    border: 2px solid var(--league-primary, #1a1a2e);
    border-radius: var(--lp-btn-radius);
    color: var(--league-primary, #1a1a2e);
    font-family: var(--lp-font-body);
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    transition: background 0.2s, color 0.2s;
}

.pe-slide-link:hover {
    background: var(--league-primary, #1a1a2e);
    color: #fff;
}

/* Prev / Next arrows */
.pe-carousel-prev,
.pe-carousel-next {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: none;
    background: rgba(255, 255, 255, 0.85);
    color: #333;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
    transition: background 0.2s, transform 0.2s;
}

.pe-carousel-prev { left: 12px; }
.pe-carousel-next { right: 12px; }

.pe-carousel-prev:hover,
.pe-carousel-next:hover {
    background: #fff;
    transform: translateY(-50%) scale(1.05);
}

/* Dot indicators */
.pe-carousel-dots {
    position: absolute;
    bottom: 16px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 8px;
    z-index: 2;
    background: rgba(0, 0, 0, 0.3);
    padding: 6px 12px;
    border-radius: 20px;
}

.pe-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, 0.8);
    background: transparent;
    cursor: pointer;
    padding: 0;
    transition: background 0.2s;
}

.pe-dot.active {
    background: #fff;
}

.pe-dot:hover {
    background: rgba(255, 255, 255, 0.5);
}

/* ── Buttons ──────────────────────────────────────────── */

.pe-btn {
    display: inline-block;
    padding: 14px 32px;
    border-radius: var(--lp-btn-radius);
    font-family: var(--lp-font-body);
    font-size: 15px;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    border: none;
    transition: opacity 0.2s, transform 0.15s;
    text-align: center;
}

.pe-btn:hover {
    opacity: 0.9;
    text-decoration: none;
}

.pe-btn-primary {
    background: var(--league-btn-bg, var(--league-secondary, #38bdf8));
    color: var(--league-btn-text, #ffffff);
}

.pe-btn-light {
    background: #ffffff;
    color: var(--league-primary, #1a1a2e);
}

/* ── Event Cards ──────────────────────────────────────── */

.pe-event-grid {
    display: grid;
    /* minmax(0,1fr), never bare 1fr — a card with a long unbroken token would
       otherwise refuse to shrink and push the grid past the viewport. */
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 24px;
    margin-top: 32px;
}

.pe-event-card {
    background: #ffffff;
    border-radius: var(--lp-card-radius);
    overflow: hidden;
    box-shadow: var(--lp-card-shadow);
    transition: transform 0.2s, box-shadow 0.2s;
}

.pe-event-card:hover {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}

.pe-event-card-header {
    padding: 16px 20px;
    display: flex;
    align-items: center;
    justify-content: flex-end;
}

.pe-event-badge {
    font-size: 12px;
    font-weight: 600;
    color: #fff;
    background: rgba(255, 255, 255, 0.2);
    padding: 4px 12px;
    border-radius: 20px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.pe-event-card-body {
    padding: 20px;
}

.pe-event-title {
    font-family: var(--lp-font-heading);
    font-size: 20px;
    font-weight: 600;
    color: #1a1a2e;
    margin-bottom: 8px;
}

.pe-event-dates {
    font-size: 14px;
    color: #666;
    margin-bottom: 8px;
}

.pe-event-desc {
    font-size: 14px;
    color: #777;
    margin-bottom: 16px;
    line-height: 1.5;
}

.pe-event-details {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 16px;
    padding-top: 12px;
    border-top: 1px solid #f0f0f0;
}

.pe-event-price {
    font-family: var(--lp-font-heading);
    font-size: 24px;
    font-weight: 700;
    color: var(--league-primary, #1a1a2e);
}

.pe-event-ages {
    font-size: 13px;
    color: #888;
    background: #f5f5f5;
    padding: 4px 10px;
    border-radius: 4px;
}

.pe-event-card .pe-btn {
    display: block;
    width: 100%;
    text-align: center;
}

/* ── FAQ ──────────────────────────────────────────────── */

.pe-faq-list {
    max-width: 720px;
    margin: 32px auto 0;
}

.pe-faq-item {
    border: 1px solid #e8e8e8;
    border-radius: 8px;
    margin-bottom: 12px;
    overflow: hidden;
}

.pe-faq-item[open] {
    border-color: var(--league-secondary, #38bdf8);
}

.pe-faq-q {
    padding: 16px 20px;
    font-family: var(--lp-font-heading);
    font-size: 16px;
    font-weight: 600;
    color: #1a1a2e;
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.pe-faq-q::after {
    content: '+';
    font-size: 20px;
    color: #999;
    transition: transform 0.2s;
}

.pe-faq-item[open] .pe-faq-q::after {
    content: '\2212';
    color: var(--league-secondary, #38bdf8);
}

.pe-faq-q::-webkit-details-marker {
    display: none;
}

.pe-faq-a {
    padding: 0 20px 16px;
    font-size: 15px;
    color: #555;
    line-height: 1.6;
}

/* ── Contact ──────────────────────────────────────────── */

.pe-contact-grid {
    display: flex;
    justify-content: center;
    gap: 48px;
    margin-top: 32px;
    flex-wrap: wrap;
}

.pe-contact-item {
    text-align: center;
    min-width: 200px;
}

.pe-contact-icon {
    font-size: 32px;
    margin-bottom: 8px;
}

.pe-contact-item h4 {
    font-family: var(--lp-font-heading);
    font-size: 16px;
    font-weight: 600;
    color: #1a1a2e;
    margin-bottom: 4px;
}

.pe-contact-item p {
    font-size: 14px;
    color: #666;
}

.pe-contact-item a {
    color: var(--league-secondary, #38bdf8);
}

/* ── CTA Banner ───────────────────────────────────────── */

.pe-cta {
    padding: 80px 24px;
    text-align: center;
}

.pe-cta-inner {
    max-width: 600px;
    margin: 0 auto;
}

.pe-cta-title {
    font-family: var(--lp-font-heading);
    font-size: 36px;
    font-weight: 700;
    margin-bottom: 16px;
    letter-spacing: -0.3px;
}

.pe-cta-text {
    font-size: 17px;
    margin-bottom: 32px;
}

/* ── Divider ──────────────────────────────────────────── */

.pe-divider {
    border: none;
    border-top: 1px solid #e8e8e8;
    margin: 32px auto;
    max-width: 720px;
}

/* ── Spacer ───────────────────────────────────────────── */

.pe-spacer {
    width: 100%;
}

/* ── Scroll to Top Button ────────────────────────────── */

.lp-scroll-top {
    position: fixed;
    bottom: 32px;
    right: 32px;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    border: 1px solid #ddd;
    background: #fff;
    color: #333;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 90;
    opacity: 0;
    visibility: hidden;
    transform: translateY(12px);
    transition: opacity 0.3s, visibility 0.3s, transform 0.3s, background 0.2s;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}

.lp-scroll-top.visible {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.lp-scroll-top:hover {
    background: #f5f5f5;
    border-color: #ccc;
}

/* ── Footer ──────────────────────────────────────────── */
.lp-footer {
    background: var(--lp-bg-footer);
    color: #adb5bd;
    padding: 48px 24px 0;
    margin-top: 64px;
}

.lp-footer-inner {
    max-width: 1140px;
    margin: 0 auto;
}

.lp-footer-grid {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr 1fr;
    gap: 40px;
    padding-bottom: 32px;
}

.lp-footer-col {
    min-width: 0;
}

.lp-footer-brand {
    padding-right: 24px;
}

.lp-footer-logo-wrap {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 16px;
}

.lp-footer-logo-img {
    width: 40px;
    height: 40px;
    border-radius: 8px;
    object-fit: cover;
}

.lp-footer-logo-placeholder {
    width: 40px;
    height: 40px;
    border-radius: 8px;
    background: rgba(255, 255, 255, 0.1);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 18px;
    color: #fff;
}

.lp-footer-name {
    font-family: var(--lp-font-heading);
    font-weight: 700;
    font-size: 16px;
    color: #fff;
}

.lp-footer-about {
    font-size: 14px;
    line-height: 1.7;
    color: #8a919a;
    margin: 0;
}

.lp-footer-heading {
    font-family: var(--lp-font-heading);
    font-size: 14px;
    font-weight: 600;
    color: #fff;
    margin: 0 0 16px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.lp-footer-links {
    list-style: none;
    padding: 0;
    margin: 0;
}

.lp-footer-links li {
    margin-bottom: 10px;
}

.lp-footer-links a {
    color: #8a919a;
    font-size: 14px;
    text-decoration: none;
    transition: color 0.15s;
}

.lp-footer-links a:hover {
    color: #fff;
}

.lp-footer-social {
    display: flex;
    gap: 12px;
}

.lp-social-icon {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.08);
    display: flex;
    align-items: center;
    justify-content: center;
    color: #adb5bd;
    transition: background 0.15s, color 0.15s;
    text-decoration: none;
}

.lp-social-icon svg {
    width: 18px;
    height: 18px;
}

.lp-social-icon:hover {
    background: rgba(255, 255, 255, 0.15);
    color: #fff;
}

.lp-footer-no-social {
    font-size: 13px;
    color: #6c757d;
    margin: 0;
}

.lp-footer-bottom {
    padding: 20px 0;
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 13px;
    color: #6c757d;
}

.lp-footer-bottom a {
    color: #adb5bd;
    text-decoration: none;
}

.lp-footer-bottom a:hover {
    color: #fff;
}

.lp-powered-by a {
    color: #adb5bd;
    text-decoration: none;
    font-weight: 500;
}

.lp-powered-by a:hover {
    color: #fff;
}

/* ── Header Navigation ────────────────────────────────── */

.lp-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    flex: 1;
    min-width: 0;
}

.lp-nav-link {
    font-size: 14px;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.7);
    text-decoration: none;
    padding: 6px 14px;
    border-radius: 6px;
    transition: background 0.15s, color 0.15s;
    white-space: nowrap;
}

.lp-nav-link:hover {
    background: rgba(255, 255, 255, 0.08);
    color: #ffffff;
    text-decoration: none;
}

/* Mobile-menu items — hidden by default on EVERY template/width. `!important`
   is required to beat Modern's `.tpl-modern .lp-nav-link { display:block }`
   (0,2,0) so they never leak into a desktop nav bar. Two flavours, revealed
   only inside an OPEN hamburger overlay below 901px (rules further down):
     .lp-nav-extra      — Active Events + Accessibility. These duplicate what
                          Modern (and the RO/Editorial skins) keep in the header
                          on mobile, so they enter the menu ONLY on Default
                          (miniport), which hides the header CTA + a11y icon.
     .lp-nav-footerlink — footer Quick-Links with no header home (Coach/Referee
                          signup). The footer Quick Links column is hidden on
                          mobile, so these enter EVERY template's overlay. */
.lp-nav-extra,
.lp-nav-footerlink {
    display: none !important;
}
.lp-nav-ada {
    font: inherit;
    text-align: left;
    background: none;
    border: none;
    cursor: pointer;
    width: 100%;
}
@media (max-width: 900px) {
    /* Active Events → the menu on EVERY template (its header button is hidden
       on mobile, just below). Accessibility → the menu on Default only; Modern
       and the Read Only / Editorial skins keep their header a11y icon. */
    .lp-nav.open .lp-nav-extra:not(.lp-nav-ada) {
        display: block !important;
    }
    .tpl-miniport .lp-nav.open .lp-nav-ada {
        display: block !important;
    }
    .lp-nav.open .lp-nav-footerlink {
        display: block !important;
    }
    /* Active Events now lives in the menu on mobile, not as a header button. */
    .lp-header-cta {
        display: none !important;
    }
    /* Footer Quick Links column is redundant on mobile — those links live in
       the hamburger now (Modern-path footer only; Bold has its own footer). */
    .lp-footer-quick {
        display: none;
    }
    /* Uniform menu link colour. miniport / modern / the base overlay each set a
       different opacity (0.78 / #fff / 0.85), so items read as mismatched. Force
       one weight for all; only the current page brightens to full white. */
    .lp-nav.open .lp-nav-link {
        color: rgba(255, 255, 255, 0.85) !important;
    }
    .lp-nav.open .lp-nav-link.lp-nav-active,
    .lp-nav.open .lp-nav-link:hover {
        color: #ffffff !important;
    }
}

.lp-hamburger {
    display: none;
    background: none;
    border: none;
    cursor: pointer;
    color: #ffffff;
    padding: 8px;
    border-radius: 8px;
    flex-shrink: 0;
}

.lp-hamburger:hover {
    background: rgba(255, 255, 255, 0.1);
}

.lp-nav-close {
    display: none;
}

/* ── Breadcrumbs ─────────────────────────────────────── */

.lp-breadcrumbs {
    margin-bottom: 16px;
}

.lp-breadcrumbs ol {
    list-style: none;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px;
    padding: 0;
    margin: 0;
}

.lp-breadcrumbs li {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 14px;
}

.lp-breadcrumb-link {
    color: var(--league-secondary, #38bdf8);
    text-decoration: none;
}

.lp-breadcrumb-link:hover {
    text-decoration: underline;
}

.lp-breadcrumb-sep {
    color: #ccc;
    margin: 0 2px;
}

.lp-breadcrumb-current {
    color: #888;
    font-weight: 500;
}

/* ── Page Header (sub-pages) ─────────────────────────── */

.lp-page-header {
    padding: 32px 0 24px;
    background: #f8f9fa;
    border-bottom: 1px solid #e8e8e8;
}

.lp-page-title {
    font-family: var(--lp-font-heading);
    font-size: 32px;
    font-weight: 700;
    color: #1a1a2e;
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

/* ── Season Detail Styles ────────────────────────────── */

.lp-season-header {
    margin-top: 8px;
}

.lp-season-title-row {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 8px;
}

.lp-season-dates {
    font-size: 16px;
    color: #666;
    margin-bottom: 12px;
}

.lp-season-desc {
    font-size: 15px;
    color: #555;
    line-height: 1.6;
    max-width: 720px;
}

/* ── About This Season ──────────────────────────────── */

.lp-season-about {
    padding: 40px 0;
    background: #f9fafb;
    border-bottom: 1px solid #e5e7eb;
}

.lp-about-title {
    font-family: var(--lp-font-heading);
    font-size: 22px;
    font-weight: 700;
    color: #1a1a2e;
    margin-bottom: 20px;
    position: relative;
    padding-bottom: 12px;
}

.lp-about-title::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    width: 48px;
    height: 3px;
    border-radius: 2px;
    background: var(--league-primary, #38bdf8);
}

/* Full container width (Robb, 2026-09-21: "the text needs to use the entire
   width of that div"). It was capped at 780px inside a 1140px container,
   which left a third of the row empty while every other band on the season
   page — the tabs, the rows, the register card — ran full width, so the
   Overview read as broken rather than as a measure. */
.lp-about-content {
    font-size: 15px;
    line-height: 1.7;
    color: #444;
}

.lp-about-content h2 {
    font-family: var(--lp-font-heading);
    font-size: 20px;
    font-weight: 700;
    color: #1a1a2e;
    margin: 24px 0 10px;
}

.lp-about-content h2:first-child {
    margin-top: 0;
}

.lp-about-content h3 {
    font-family: var(--lp-font-heading);
    font-size: 17px;
    font-weight: 600;
    color: #1e293b;
    margin: 20px 0 8px;
}

.lp-about-content p {
    margin: 0 0 14px;
}

.lp-about-content p:last-child {
    margin-bottom: 0;
}

.lp-about-content strong,
.lp-about-content b {
    font-weight: 700;
    color: #1e293b;
}

.lp-about-content em,
.lp-about-content i {
    font-style: italic;
}

.lp-about-content u {
    text-decoration: underline;
    text-underline-offset: 2px;
}

.lp-about-content ul,
.lp-about-content ol {
    margin: 10px 0 14px 24px;
    padding: 0;
}

.lp-about-content li {
    margin-bottom: 6px;
}

.lp-about-content a {
    color: var(--league-primary, #38bdf8);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.lp-about-content a:hover {
    opacity: 0.8;
}

.lp-division-badge {
    display: inline-block;
    font-size: 12px;
    font-weight: 600;
    color: var(--league-secondary, #38bdf8);
    background: rgba(56, 189, 248, 0.1);
    padding: 4px 12px;
    border-radius: 20px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

/* ── Status Badges ───────────────────────────────────── */

.lp-status-badge {
    display: inline-block;
    font-size: 12px;
    font-weight: 600;
    padding: 4px 12px;
    border-radius: 20px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.lp-status-open {
    color: #166534;
    background: #dcfce7;
}

.lp-status-closed {
    color: #9a3412;
    background: #fed7aa;
}

.lp-status-full {
    color: #991b1b;
    background: #fee2e2;
}

/* ── Season Cards (landing page) ─────────────────────── */

.lp-season-card {
    display: block;
    text-decoration: none;
    color: inherit;
}

.lp-season-card:hover {
    text-decoration: none;
}

.lp-season-card .pe-event-card-body {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.lp-team-count {
    font-size: 13px;
    color: #888;
}

.lp-view-details {
    font-size: 13px;
    font-weight: 600;
    color: var(--league-secondary, #38bdf8);
}

/* ── Pre-Registration Season Card ──────────────────────── */

.lp-season-card.pre-registration {
    opacity: 0.6;
    cursor: default;
    pointer-events: none;
}
.lp-season-card.pre-registration:hover {
    opacity: 0.75;
    transform: none;
}

.lp-status-upcoming {
    color: #1e40af;
    background: #dbeafe;
}

.pre-reg-countdown {
    font-size: 13px;
    font-weight: 500;
    color: var(--league-primary, #3b82f6);
    padding: 6px 0 2px;
    display: flex;
    align-items: center;
}

/* ── Registration Card ───────────────────────────────── */

.lp-reg-section {
    padding: 24px 0;
}

.lp-reg-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
    padding: 24px 28px;
    background: #ffffff;
    border: 1px solid #e8e8e8;
    border-radius: 12px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}

.lp-reg-info h3 {
    font-family: var(--lp-font-heading);
    font-size: 18px;
    font-weight: 600;
    color: #1a1a2e;
    margin-bottom: 4px;
}

.lp-reg-dates {
    font-size: 14px;
    color: #666;
}

/* ── Registration Banner (season detail page) ──── */
/* The price in the split card's side panel.
   It was #fff, which was right while the card was a navy band and made the
   figure INVISIBLE on the light panel that replaced it — the one thing a
   DOM check for the text cannot catch, because the text was there.
   The Registrations page overrides size and colour in its own <style>, so
   this rule now serves the season card alone. */
.lp-reg-price {
    font-family: var(--lp-font-heading);
    font-size: 34px;
    font-weight: 700;
    line-height: 1;
    color: #111827;
}
/* ── The season register card: a split card ───────────────────────────
   Robb picked this (option C of four, 2026-09-23) after seeing them side by
   side. It replaced a navy band whose sub-line repeated the page title and
   whose facts were three stacked lines of progressively fainter grey.

   Prose and facts on the left, the price and the one button in a panel of
   their own on the right, with a colour rail down the leading edge. The words
   come from seasonRegLead(), shared with Bold; only the drawing is here.

   The rail takes --league-secondary, so it is the league's colour rather than
   a fixed one. */
.lp-reg-split {
    display: flex;
    align-items: stretch;
    background: #fff;
    border: 1px solid #d7dde6;
    border-radius: 14px;
    overflow: hidden;
}
.lp-reg-rail {
    flex: 0 0 auto;
    width: 7px;
    background: var(--league-secondary, #2563eb);
}
.lp-reg-split-main {
    flex: 1 1 auto;
    min-width: 0;
    padding: 26px 30px;
    display: flex;
    flex-direction: column;
    gap: 12px;
}
/* ⚠ DOUBLE CLASS, deliberately. `.tpl-modern h1..h6` is (0,1,1) and a bare
   `.lp-reg-split-title` is (0,1,0), so the skin's editorial heading style won
   and the card's headline rendered in Playfair Display, weight 600, league
   navy — not the bold sans near-black it is meant to be. The navy banner had
   `.tpl-modern .lp-reg-banner-title` for exactly this reason; it was removed as
   dead along with the banner, and the reason did not come across with it. */
.lp-reg-split .lp-reg-split-title {
    font-family: var(--lp-font-heading);
    font-size: 22px;
    font-weight: 700;
    color: #111827;
    margin: 0;
}
.lp-reg-facts {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 7px;
}
.lp-reg-fact {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    font-size: 14px;
    color: #374151;
}
/* The icon must not shrink when a fact wraps, and it sits on the first line
   rather than centring against the whole block. */
.lp-reg-fact-ico {
    flex: 0 0 auto;
    margin-top: 2px;
    color: #4b5563;
}
.lp-reg-split-side {
    flex: 0 0 auto;
    width: 300px;
    padding: 26px 30px;
    background: #f5f7fa;
    border-left: 1px solid #e3e8ef;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 13px;
}
.lp-reg-price-box {
    display: flex;
    align-items: baseline;
    gap: 7px;
}
.lp-reg-price-unit {
    font-size: 13px;
    color: #4b5563;
}
/* The side panel holds ONE action, so it spans the panel — a narrow button in
   a 300px column reads as unfinished. */
.lp-reg-split-side .pe-btn {
    display: block;
    width: 100%;
    text-align: center;
}

/* Under 860 the fixed 300px panel leaves the prose too narrow, so the card
   stacks and the rail becomes a top edge: a 7px vertical line beside a stacked
   card is a stray mark. */
@media (max-width: 859px) {
    .lp-reg-split { flex-direction: column; }
    .lp-reg-rail { width: auto; height: 6px; }
    .lp-reg-split-side {
        width: auto;
        border-left: none;
        border-top: 1px solid #e3e8ef;
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        gap: 16px;
    }
    .lp-reg-split-side .pe-btn { width: auto; }
}
@media (max-width: 559px) {
    .lp-reg-split-side { flex-direction: column; align-items: stretch; }
    .lp-reg-split-side .pe-btn { width: 100%; }
}

.pe-btn[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
    transform: none;
}

.pe-btn[disabled]:hover {
    opacity: 0.5;
    transform: none;
}

/* ── Team Grid (season page) ─────────────────────────── */

/* ── Grade Grouping ────────────────────────────────── */
.lp-grade-group {
    margin-top: 28px;
}
.lp-grade-group:first-child {
    margin-top: 8px;
}
.lp-grade-group-title {
    font-family: var(--lp-font-heading);
    font-size: 20px;
    font-weight: 600;
    color: #1a1a2e;
    margin: 0 0 4px 0;
    padding-bottom: 10px;
    border-bottom: 2px solid var(--league-primary, #1a1a2e);
    display: flex;
    align-items: center;
    gap: 10px;
}
.lp-grade-group-title::before {
    content: '';
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--league-primary, #1a1a2e);
    flex-shrink: 0;
}

.lp-team-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    margin-top: 16px;
}

.lp-team-card {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding: 20px;
    background: #ffffff;
    border: 1px solid #e8e8e8;
    border-radius: 12px;
    text-decoration: none;
    color: inherit;
    transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s;
}

.lp-team-card:hover {
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
    border-color: var(--league-secondary, #38bdf8);
    text-decoration: none;
}

.lp-team-card-top {
    margin-bottom: 14px;
}

.lp-team-card-name {
    font-family: var(--lp-font-heading);
    font-size: 18px;
    font-weight: 600;
    color: #1a1a2e;
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 4px;
}

.lp-team-color-dot {
    display: inline-block;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    flex-shrink: 0;
    border: 1px solid rgba(0, 0, 0, 0.1);
}

.lp-team-coach {
    font-size: 14px;
    color: #666;
}

.lp-team-card-bottom {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding-top: 12px;
    border-top: 1px solid #f0f0f0;
}

.lp-team-roster-info {
    display: flex;
    flex-direction: column;
    gap: 6px;
    flex: 1;
}

.lp-roster-label {
    font-size: 13px;
    color: #888;
}

.lp-roster-bar-wrap {
    width: 100%;
    height: 4px;
    background: #f0f0f0;
    border-radius: 2px;
    overflow: hidden;
}

.lp-roster-bar {
    height: 100%;
    background: var(--league-secondary, #38bdf8);
    border-radius: 2px;
    transition: width 0.3s;
}

.lp-grade-badge {
    font-size: 12px;
    color: #666;
    background: #f5f5f5;
    padding: 4px 10px;
    border-radius: 4px;
    white-space: nowrap;
}

/* ── Team Detail Page ────────────────────────────────── */

.lp-team-header {
    margin-top: 8px;
}

.lp-team-meta {
    display: flex;
    gap: 24px;
    flex-wrap: wrap;
    margin-top: 12px;
}

.lp-team-detail {
    font-size: 15px;
    color: #555;
}

.lp-team-detail strong {
    color: #333;
}

/* ── Roster Table ────────────────────────────────────── */

.lp-roster-wrap {
    max-width: 540px;
    margin: 0 auto;
}

.lp-roster-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 15px;
}

.lp-roster-table thead {
    border-bottom: 2px solid #e8e8e8;
}

.lp-roster-table th {
    font-family: var(--lp-font-heading);
    font-weight: 600;
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: #888;
    padding: 12px 16px;
    text-align: left;
}

.lp-roster-table td {
    padding: 12px 16px;
    border-bottom: 1px solid #f0f0f0;
    color: #333;
}

.lp-roster-table tbody tr:last-child td {
    border-bottom: none;
}

.lp-roster-table tbody tr:hover {
    background: #f8f9fa;
}

/* ── Empty State ─────────────────────────────────────── */

.lp-empty-state {
    text-align: center;
    padding: 48px 24px;
}

.lp-empty-icon {
    margin-bottom: 16px;
}

.lp-empty-state h3 {
    font-family: var(--lp-font-heading);
    font-size: 20px;
    font-weight: 600;
    color: #1a1a2e;
    margin-bottom: 8px;
}

.lp-empty-state p {
    font-size: 15px;
    color: #888;
    max-width: 400px;
    margin: 0 auto;
}

/* ── Schedule Stub ───────────────────────────────────── */

.lp-schedule-stub {
    background: #f8f9fa;
}

/* ── Back Link ───────────────────────────────────────── */

.lp-back-section {
    padding: 24px 0 48px;
}

.lp-back-link {
    font-size: 15px;
    font-weight: 500;
    color: var(--league-secondary, #38bdf8);
    text-decoration: none;
    transition: opacity 0.2s;
}

.lp-back-link:hover {
    text-decoration: underline;
}

/* ── Sponsors (Public Page) ──────────────────────────── */

.pe-sponsor-grid {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 28px;
    padding: 8px 0;
}

.pe-sponsor-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    text-decoration: none;
    color: inherit;
    transition: transform 0.2s, opacity 0.2s;
    min-width: 100px;
    max-width: 160px;
}

.pe-sponsor-item:hover {
    opacity: 0.85;
}

.pe-sponsor-logo {
    max-height: 60px;
    max-width: 140px;
    object-fit: contain;
}

.pe-sponsor-placeholder {
    width: 60px;
    height: 60px;
    border-radius: 10px;
    background: #e2e8f0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 24px;
    font-weight: 700;
    color: #94a3b8;
}

.pe-sponsor-name {
    font-size: 13px;
    font-weight: 500;
    color: #555;
    text-align: center;
}

/* ── Subpage Common ──────────────────────────────────── */
.lp-page-section {
    max-width: 1140px;
    margin: 0 auto;
    padding: 60px 24px 64px;
}

.lp-page-header-bar {
    text-align: center;
    margin-bottom: 56px;
}

.lp-page-title {
    font-family: var(--lp-font-heading);
    font-size: 36px;
    font-weight: 700;
    color: #1a1a2e;
    margin: 0 0 16px;
}

.lp-page-subtitle {
    font-size: 16px;
    color: #666;
    margin: 0;
}

/* ── Tab strip (Registrations, Divisions) ────────────── */
/* Robb, 2026-09-22: "a right corner-oriented tab system". A folder tab —
   square-shouldered, the active one joined to the panel below by a shared
   bottom edge. Shared by the Registrations page and a division page rather
   than defined inline on each, which is how the two would drift.
   Nothing lifts on hover; the border and colour carry the state. */
.lp-reg-tabs {
    display: flex;
    align-items: flex-end;
    gap: 4px;
    flex: 0 0 auto;
    align-self: flex-end;
}

.lp-reg-tab {
    appearance: none;
    cursor: pointer;
    font-family: var(--lp-font-body);
    font-size: 14px;
    font-weight: 700;
    letter-spacing: 0.01em;
    padding: 11px 22px;
    min-height: 44px;
    border: 1px solid #e2e5ea;
    border-bottom-color: transparent;
    border-radius: var(--lp-btn-radius) var(--lp-btn-radius) 0 0;
    background: #f4f6f8;
    color: #64748b;
    transition: background 0.2s, color 0.2s, border-color 0.2s;
}

.lp-reg-tab:hover {
    background: #eef1f5;
    color: #334155;
}

.lp-reg-tab.is-active {
    background: #fff;
    color: var(--league-heading, #1a1a2e);
    border-color: #e2e5ea;
    border-bottom-color: #fff;
    box-shadow: 0 -2px 0 0 var(--league-btn-bg, var(--league-secondary, #38bdf8)) inset;
}

/* A division page's header bar is `text-align: center` with a flex row inside
   it, not a flex row itself — so `align-self` on the strip did nothing and the
   tabs sat under the title at their intrinsic size. This makes the bar the flex
   row when, and only when, it carries tabs. */
.lp-page-header-bar.lp-has-tabs {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 16px 32px;
    flex-wrap: wrap;
}

.lp-page-header-bar.lp-has-tabs > .lp-header-top {
    flex: 1 1 320px;
    min-width: 0;
}

@media (max-width: 600px) {
    /* Full width and side by side on a phone, rather than two tabs squeezed
       against the right edge. */
    .lp-reg-tabs { width: 100%; }
    .lp-reg-tab { flex: 1 1 0; padding: 11px 12px; }
}

/* ── Sponsors Page ───────────────────────────────────── */
.lp-sp-grid {
    display: grid;
    /* minmax(0, 1fr), never bare 1fr — a sponsor with a long unbroken name
       would otherwise refuse to shrink and push the grid past the viewport. */
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 24px;
}

/* Tier groups (mig 291). The SIZE of the card is the tier benefit: the top
   tier is what a Gold sponsor paid for, so it gets the room. Keyed off rank in
   PublicLeagueController::sponsorGridSizeClass, never off the tier's name —
   leagues type their own names and "Gold" is only a convention. */
.lp-sp-tier-group + .lp-sp-tier-group {
    margin-top: 40px;
}

/* The untiered group carries no heading (Robb, 2026-09-21), which is fine when
   it stands alone but reads as a CONTINUATION of the tier above it when it does
   not — a sponsor with no tier sitting under "Bronze" looks bronze. A rule line
   says "this is something else" without inventing a label nobody asked for.
   Scoped to an untiered group that FOLLOWS one, so a league using no tiers at
   all never sees it. */
.lp-sp-tier-group + .lp-sp-tier-group:not(:has(.lp-sp-tier-head)) {
    margin-top: 44px;
    padding-top: 36px;
    border-top: 1px solid #e2e5ea;
}

.lp-sp-tier-head {
    display: flex;
    align-items: center;
    gap: 14px;
    margin: 0 0 18px;
}

.lp-sp-tier-title {
    font-family: var(--lp-font-heading);
    font-size: 15px;
    font-weight: 800;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--league-heading, #1a1a2e);
    margin: 0;
    /* The heading is the league's own tier name, so it can be long. */
    min-width: 0;
}

.lp-sp-tier-rule {
    flex: 1;
    height: 1px;
    background: #e2e5ea;
}

.lp-sp-grid--lg {
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
}

.lp-sp-grid--lg .lp-sp-card-logo-wrap {
    height: 180px;
}

.lp-sp-grid--sm {
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 18px;
}

.lp-sp-grid--sm .lp-sp-card {
    padding: 18px;
}

.lp-sp-grid--sm .lp-sp-card-logo-wrap {
    height: 100px;
}

.lp-sp-grid--sm .lp-sp-card-name {
    font-size: 15px;
}

.lp-sp-card {
    background: #fff;
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    padding: 24px;
    text-align: center;
    transition: box-shadow 0.2s;
    /* Column + the spacer margin on the button below: cards in a row line up
       whether or not the sponsor name wraps to two lines. */
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.lp-sp-card:hover {
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
}

/* The well is the full width of the card, not a small square: most sponsor
   logos are wide wordmarks and an 80px box squeezed them to nothing. */
.lp-sp-card-logo-wrap {
    width: 100%;
    height: 130px;
    border-radius: 12px;
    overflow: hidden;
    margin: 0 auto 16px;
    padding: 14px;
    background: #f8fafc;
    border: 1px solid #e5e7eb;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* max-*, not width/height 100% — a tall logo and a wide one both have to fit
   the same well without being stretched to its shape. */
.lp-sp-card-logo {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

.lp-sp-card-letter {
    font-size: 40px;
    font-weight: 700;
    color: #94a3b8;
}

.lp-sp-card-name {
    font-family: var(--lp-font-heading);
    font-size: 17px;
    font-weight: 600;
    color: var(--league-heading, #1a1a2e);
    margin: 0 0 14px;
}

/* "Visit Site" button — replaces the raw URL, which wrapped onto three lines
   and blew the card out of the grid on a long sponsor link.
   Full width and pushed to the bottom of the card (`margin-top: auto` against
   the card's flex column) so a row of cards squares off no matter how long the
   sponsor names run. */
.lp-sp-card-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    margin-top: auto;
    min-height: 44px;
    padding: 12px 18px;
    border-radius: var(--lp-btn-radius);
    border: 1px solid var(--league-btn-bg, var(--league-secondary, #38bdf8));
    background: transparent;
    color: var(--league-btn-bg, var(--league-secondary, #38bdf8));
    font-family: var(--lp-font-body);
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    transition: background 0.2s, color 0.2s;
}

.lp-sp-card-btn svg {
    width: 15px;
    height: 15px;
    flex: none;
}

.lp-sp-card-btn:hover {
    background: var(--league-btn-bg, var(--league-secondary, #38bdf8));
    color: var(--league-btn-text, #ffffff);
    text-decoration: none;
}

/* ── Services Page ("What We Offer" marketing cards) ─── */
/* ── The services grid lays on TWELVE tracks, not on a column count ───────
   Rows hold different numbers of cards — five services is two then three —
   so there is no single `grid-template-columns` that works. Twelve divides
   by 1, 2, 3 and 4, every row size the plan produces, so a card just spans
   12/<row size> and the rows come out flush with no per-row container.

   It replaces `repeat(auto-fill, minmax(300px, 1fr))`, which fitted as many
   300px columns as it could and let the remainder wrap: four services came
   out three across with the fourth stranded at a third width, on its own, on
   the left (Robb, 2026-10-18). `auto-fill` keeps the empty tracks, which is
   why the orphan never stretched.

   PublicLeagueController::serviceSpans() decides the spans; the classes are
   the only thing it needs to say. */
.lp-svc-grid {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: 28px;
}

/* ⚠ THE SPANS ARE DESKTOP ONLY, and the three steps go narrow → wide on
   purpose: a later rule at equal specificity wins, so the phone rule has to
   come first or it would be overruled by nothing and silently never apply. */
.lp-svc-grid > * { grid-column: span 12; }
@media (min-width: 600px) {
    .lp-svc-grid > * { grid-column: span 6; }
}
@media (min-width: 1024px) {
    /* The fallback is three across — what the page did before, for anything
       the server renders without a span class. */
    .lp-svc-grid > * { grid-column: span 4; }
    .lp-svc-grid > .pe-svc-col-12 { grid-column: span 12; }
    .lp-svc-grid > .pe-svc-col-6  { grid-column: span 6; }
    .lp-svc-grid > .pe-svc-col-4  { grid-column: span 4; }
    .lp-svc-grid > .pe-svc-col-3  { grid-column: span 3; }
}

/* ── A single service goes side-on ────────────────────────────────────────
   One card stretched across the whole band would put a 675px-tall photo on
   the page, because .lp-svc-card-img is 16/9 of whatever width it is given.
   Side-on, the image takes half the width and whatever height the words
   need, so one service reads as a feature rather than a mistake.

   The body is `flex: 1 1 50%` rather than `width: 50%` so that a service
   with NO image still fills the card instead of leaving half of it empty. */
@media (min-width: 1024px) {
    .lp-svc-card.is-feature {
        flex-direction: row;
        align-items: stretch;
        min-height: 320px;
    }
    .lp-svc-card.is-feature .lp-svc-card-img {
        flex: 0 0 50%;
        width: 50%;
        /* ⚠ The 16/9 has to go, or the ratio drives the height and the card
           grows to 675px again. Stretched + cover crops to the box instead. */
        aspect-ratio: auto;
        align-self: stretch;
        height: auto;
    }
    .lp-svc-card.is-feature .lp-svc-card-body {
        flex: 1 1 50%;
        justify-content: center;
        padding: 36px 40px;
    }
}

.lp-svc-card {
    background: #fff;
    border: none;
    border-radius: 16px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    box-shadow: 0 14px 34px rgba(15, 27, 51, 0.12);
    transition: box-shadow 0.2s, transform 0.2s;
}

.lp-svc-card:hover {
    box-shadow: 0 20px 44px rgba(15, 27, 51, 0.18);
}

.lp-svc-card-img {
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    display: block;
    background: #0f1b33;
}

.lp-svc-card .lp-svc-card-link.pe-btn {
    padding-left: 26px;
    padding-right: 26px;
}

.lp-svc-card-body {
    padding: 22px 24px 24px;
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
}

.lp-svc-card-name {
    font-family: var(--lp-font-heading);
    font-size: 20px;
    font-weight: 700;
    color: #1a1a2e;
    margin: 0 0 10px;
}

/* Rich text since 2026-09-21, so it is a `<div>` and no longer a `<p>`, and
   `white-space: pre-line` had to go with it: the stored value is HTML now, and
   pre-line would render the newlines BETWEEN its tags as real line breaks —
   every paragraph gaining a blank line above it. It also pairs with
   `.lp-rt-flush`, which kills the first/last child's outer margin so the card
   spacing is unchanged. */
.lp-svc-card-desc {
    font-size: 15px;
    line-height: 1.6;
    color: #555;
    margin: 0 0 18px;
}

/* Centred, not left (Robb, 2026-09-23: "Please move the Learn More button to
   the center of the card. Home page service area and the services page").
   ONE rule covers both, because renderServicesPage and buildModernServices
   render the same .lp-svc-card. `margin-top: auto` is what keeps the button on
   the card's floor when the descriptions are different lengths. */
.lp-svc-card-link {
    margin-top: auto;
    align-self: center;
}

/* ── Empty State ─────────────────────────────────────── */
.lp-empty-state {
    text-align: center;
    padding: 64px 24px;
    color: #94a3b8;
}

.lp-empty-state svg {
    margin-bottom: 16px;
    opacity: 0.5;
}

.lp-empty-state h3 {
    font-size: 20px;
    font-weight: 600;
    color: #64748b;
    margin: 0 0 8px;
}

.lp-empty-state p {
    font-size: 15px;
    margin: 0;
    max-width: 400px;
    margin: 0 auto;
}

/* ── Contact page: two-column layout ─────────────────────
   Contact details as a left-aligned list on the left, the message form on the
   right. Collapses to a single column at <=900px. */
.lp-contact-layout {
    display: grid;
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
    gap: 48px;
    align-items: start;
    margin-top: 40px;
}

/* Left column — the base .pe-contact-grid is a centred wrapping row; inside the
   layout, stack the items into a left-aligned list with a boxed accent icon. */
.lp-contact-layout .pe-contact-grid {
    flex-direction: column;
    justify-content: flex-start;
    gap: 22px;
    margin-top: 0;
}
.lp-contact-layout .pe-contact-item {
    display: flex;
    align-items: flex-start;
    gap: 14px;
    text-align: left;
    min-width: 0;
}
.lp-contact-layout .pe-contact-icon {
    flex-shrink: 0;
    margin: 0;
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    background: color-mix(in srgb, var(--league-secondary, #38bdf8) 12%, transparent);
    color: var(--league-secondary, #38bdf8);
}
.lp-contact-layout .pe-contact-item strong {
    display: block;
    font-family: var(--lp-font-heading);
    color: #1a1a2e;
    margin-bottom: 2px;
}

/* Right column — let the form card fill the column rather than the centred
   640px cap it uses in the single-column layout. */
.lp-contact-layout .lp-contact-form {
    max-width: none;
    width: 100%;
    margin: 0;
}

/* Send button sits at the bottom-right of the form. */
.lp-form-actions {
    display: flex;
    justify-content: flex-end;
    margin-top: 4px;
}

@media (max-width: 900px) {
    .lp-contact-layout {
        grid-template-columns: minmax(0, 1fr);
        gap: 32px;
    }
}

/* ── Contact Form ──────────────────────────────────────
   Base width also used by the standalone Coach/Referee signup cards. The real
   contact page overrides this to width:100% via .lp-contact-layout above. */
/* ── Sponsorship packages (Become a Sponsor) ───────────────────────────────
   The tiers used to exist only inside the form's "Preferred tier" select, so a
   sponsor read "Silver — $500" with no idea what it bought. Same card tokens
   as the rest of this template; auto-fit so three tiers sit in a row and a
   league with five or one still looks deliberate. */
.lp-tier-intro { max-width: 880px; margin: 0 auto 18px; text-align: center; }
.lp-tier-heading { margin: 0 0 6px; font-family: var(--lp-font-heading); font-size: 22px; font-weight: 700; color: #1a1a2e; }
.lp-tier-note { margin: 0; font-size: 15px; opacity: .75; }
/* The packages row and the inquiry form under it are ONE column of content, so
   they share one width token. They were 1080 and 800: the form sat visibly
   narrower than the cards it belongs to (Robb, 2026-09-13). Keep them here
   together — two separate max-widths is how they drifted in the first place. */
.lp-page-section { --lp-sponsor-width: 1080px; }
.lp-tier-grid {
    display: grid; gap: 18px; margin: 0 auto 34px; max-width: var(--lp-sponsor-width, 1080px);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}
.lp-tier-card {
    background: #ffffff; border-radius: var(--lp-card-radius);
    /* Deeper than --lp-card-shadow (0 2px 12px /.08): a white card on a white page
       with that shadow all but disappears. See the border note below — together
       they make the card an object you can pick, without shouting. */
    box-shadow: 0 3px 14px rgba(0,0,0,.10); padding: 22px 22px 24px;
    display: flex; flex-direction: column; gap: 6px;
}
.lp-tier-name { font-size: 17px; font-weight: 700; color: #1a1a2e; }
.lp-tier-price { font-family: var(--lp-font-heading); font-size: 26px; font-weight: 700; color: var(--league-primary, #1a1a2e); line-height: 1.2; }
.lp-tier-benefits { margin: 4px 0 0; font-size: 14.5px; line-height: 1.6; opacity: .8; }
/* The cards are <button>s (they pick a tier), so reset the UA button look and
   give the chosen one a ring in the league's own colour rather than a colour
   we picked. text-align:left because a button centres its text by default. */
.lp-tier-card { border: 2px solid rgba(26,26,46,.06); font: inherit; color: inherit; text-align: left; width: 100%; cursor: pointer; transition: transform .15s, box-shadow .15s, border-color .15s; }
/* The hairline edge is the other half of "stand out a bit more, not too much"
   (Robb, 2026-09-13). It stays 2px wide so .is-picked below only changes its
   COLOUR — a width change there would shift every card by a pixel. */
.lp-tier-card:hover { box-shadow: 0 10px 24px rgba(0,0,0,.13); }
.lp-tier-card:focus-visible { outline: none; border-color: var(--league-primary, #1a1a2e); box-shadow: 0 0 0 3px rgba(0,0,0,.08); }
.lp-tier-card.is-picked { border-color: var(--league-primary, #1a1a2e); }

/* The Become-a-Sponsor form only — the Contact page's form keeps its own,
   narrower measure, which suits a page that has no cards above it. */
.lp-contact-form.lp-sponsor-form { max-width: var(--lp-sponsor-width, 1080px); }
.lp-contact-form {
    max-width: 800px;
    margin: 48px auto 0;
    background: #fff;
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    padding: 32px;
}

.lp-contact-form-title {
    font-family: var(--lp-font-heading);
    font-size: 20px;
    font-weight: 600;
    color: #1a1a2e;
    margin: 0 0 24px;
}

.lp-form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
}

.lp-form-group {
    margin-bottom: 16px;
}

.lp-form-group label {
    display: block;
    font-size: 14px;
    font-weight: 600;
    color: #374151;
    margin-bottom: 6px;
}

.lp-form-input {
    width: 100%;
    padding: 10px 14px;
    border: 1px solid #d1d5db;
    border-radius: 8px;
    font-size: 14px;
    font-family: var(--lp-font-body);
    color: #1a1a2e;
    background: #fff;
    transition: border-color 0.15s;
    box-sizing: border-box;
}

.lp-form-input:focus {
    outline: none;
    border-color: #38bdf8;
    box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.1);
}

/* iOS Safari zooms the page when a focused control is < 16px — pin the public
   form controls to 16px on phones so the contact/registration forms don't lurch. */
@media (max-width: 599px) {
    .lp-form-input { font-size: 16px; }
}

/* ── Coach / Referee public signup forms (Modern shell) ──────────────────
   These two forms share ONE markup for both templates, built with Bold's
   .form/.field/.btn class names (styled in league-v2.css). On the Modern shell
   they render inside .lp-contact-form, where those classes were unstyled — so
   they fell back to bare browser inputs. Style them here to match the contact
   form. Scoped to .lp-contact-form; the real contact form uses .lp-form-* and
   is unaffected. */
.lp-contact-form .field { margin-bottom: 16px; }
.lp-contact-form .field > label {
    display: block; font-size: 14px; font-weight: 600; color: #374151; margin-bottom: 6px;
}
.lp-contact-form .field input,
.lp-contact-form .field select,
.lp-contact-form .field textarea {
    width: 100%; padding: 10px 14px; border: 1px solid #d1d5db; border-radius: 8px;
    font-size: 14px; font-family: 'DM Sans', sans-serif; color: #1a1a2e; background: #fff;
    box-sizing: border-box; transition: border-color 0.15s;
}
.lp-contact-form .field textarea { min-height: 110px; resize: vertical; }
.lp-contact-form .field input:focus,
.lp-contact-form .field select:focus,
.lp-contact-form .field textarea:focus {
    outline: none; border-color: #38bdf8; box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.1);
}
@media (max-width: 599px) {
    .lp-contact-form .field input,
    .lp-contact-form .field select,
    .lp-contact-form .field textarea { font-size: 16px; }
}
/* Submit button — mirror .pe-btn / .pe-btn-primary, full-width. */
.lp-contact-form .btn {
    display: inline-block; width: 100%; padding: 14px 32px; border: none; border-radius: var(--lp-btn-radius);
    font-family: var(--lp-font-body); font-size: 15px; font-weight: 600; text-align: center;
    cursor: pointer; transition: opacity 0.2s, transform 0.15s;
}
.lp-contact-form .btn:hover { opacity: 0.9; }
.lp-contact-form .btn-primary {
    background: var(--league-btn-bg, var(--league-secondary, #38bdf8));
    color: var(--league-btn-text, #ffffff);
}
/* Referee sport pills + age note, tuned for the light theme. */
.lp-contact-form .ref-pill { color: #374151; background: #fff; border-color: #d1d5db; }
.lp-contact-form .ref-pill:hover { border-color: #38bdf8; }
.lp-contact-form .ref-pill input { accent-color: var(--league-secondary, #38bdf8); }
.lp-contact-form .ref-age-note { color: #475569; }

.lp-form-textarea {
    resize: vertical;
    min-height: 120px;
    line-height: 1.6;
}

.lp-form-select {
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg width='12' height='8' viewBox='0 0 12 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1.5L6 6.5L11 1.5' stroke='%2394a3b8' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 14px center;
    padding-right: 40px;
    cursor: pointer;
}
.lp-form-req {
    color: #dc2626;
    font-weight: 400;
}
.lp-form-hint {
    font-size: 12px;
    color: #94a3b8;
    margin-top: 12px;
}

/* ── Divisions Page ──────────────────────────────────── */
.lp-division-group {
    margin-bottom: 40px;
}

.lp-division-title {
    font-family: var(--lp-font-heading);
    font-size: 22px;
    font-weight: 600;
    color: #1a1a2e;
    margin: 0 0 16px;
    padding-bottom: 12px;
    border-bottom: 2px solid #e5e7eb;
}

.lp-division-desc {
    font-size: 15px;
    line-height: 1.6;
    color: #555;
    margin: -8px 0 20px;
}

.lp-division-empty {
    font-size: 14px;
    color: #94a3b8;
    padding: 16px 0;
}

/* ── Registration Page ───────────────────────────────── */
.lp-reg-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 20px 24px;
    background: #fff;
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    margin-bottom: 12px;
    transition: box-shadow 0.2s;
}

.lp-reg-card:hover {
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
}

.lp-reg-card-info {
    flex: 1;
    min-width: 0;
}

.lp-reg-card-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 4px;
}

.lp-reg-card-name {
    font-size: 16px;
    font-weight: 600;
    color: #1a1a2e;
    margin: 0;
}

.lp-reg-badge {
    font-size: 11px;
    font-weight: 600;
    padding: 2px 10px;
    border-radius: 50px;
    text-transform: uppercase;
    letter-spacing: 0.3px;
}

.lp-reg-badge-open {
    background: #dcfce7;
    color: #166534;
}

.lp-reg-badge-closed {
    background: #f3f4f6;
    color: #6b7280;
}

.lp-reg-dates {
    font-size: 13px;
    color: #666;
    margin: 4px 0 0;
}

.lp-reg-card-action {
    flex-shrink: 0;
}

/* ── Responsive: Tablet (600-1023px) ──────────────────── */

@media (max-width: 1023px) {
    .pe-hero {
        padding: 80px 24px;
    }

    .pe-hero-image {
        padding: 96px 24px;
    }

    .pe-hero-title {
        font-size: 42px;
    }

    .pe-hero-subtitle {
        font-size: 17px;
    }

    .pe-flow-section {
        padding: 56px 0;
    }

    .pe-flow-cta {
        padding: 56px 0;
    }

    .pe-carousel-slide {
        padding: 40px 32px;
    }

    .pe-slide-image {
        min-height: 300px;
    }

    .pe-slide-text {
        min-height: 240px;
    }

    .pe-event-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .pe-section {
        padding: 56px 0;
    }

    .pe-section-title {
        font-size: 28px;
    }

    .pe-cta-title {
        font-size: 28px;
    }

    .lp-team-grid {
        grid-template-columns: repeat(2, 1fr);
    }

    .lp-page-title {
        font-size: 26px;
    }

    /* Header: hide nav text, keep CTA visible */
    .lp-nav:not(.open) {
        display: none;
    }

    .lp-hamburger {
        display: block;
    }

    .lp-header-inner {
        gap: 16px;
    }

    .lp-header-cta {
        padding: 7px 16px;
        font-size: 13px;
    }

    /* Full-screen nav overlay on tablet */
    .lp-nav.open {
        display: flex;
        flex-direction: column;
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background: #1a1a2e;
        z-index: 200;
        padding: 80px 24px 24px;
        gap: 0;
        overflow-y: auto;
    }

    .lp-nav.open .lp-nav-close {
        display: flex;
        align-items: center;
        justify-content: center;
        position: absolute;
        top: 16px;
        right: 16px;
        width: 44px;
        height: 44px;
        background: none;
        border: none;
        cursor: pointer;
        color: #ffffff;
        border-radius: 50%;
    }

    .lp-nav.open .lp-nav-close:hover {
        background: rgba(255, 255, 255, 0.08);
    }

    .lp-nav.open .lp-nav-link {
        padding: 16px 0;
        font-size: 20px;
        font-weight: 500;
        color: rgba(255, 255, 255, 0.85);
        border-bottom: 1px solid rgba(255, 255, 255, 0.08);
        border-radius: 0;
    }

    .lp-nav.open .lp-nav-link:hover {
        color: #ffffff;
        background: transparent;
    }

    .lp-footer-grid {
        grid-template-columns: 1fr 1fr;
        gap: 32px;
    }
}

/* ── Responsive: Mobile (<600px) ──────────────────────── */

@media (max-width: 599px) {
    .lp-header-inner {
        height: 56px;
        gap: 12px;
    }

    .lp-league-name {
        font-size: 15px;
    }

    .lp-header-cta {
        padding: 6px 14px;
        font-size: 12px;
    }

    .pe-flow-section {
        padding: 48px 0;
    }

    .pe-flow-title {
        font-size: 22px;
    }

    .pe-flow-cta {
        padding: 48px 0;
    }

    .pe-flow-cta-inner {
        flex-direction: column;
        text-align: center;
    }

    .pe-flow-cta-title {
        font-size: 24px;
    }

    .pe-carousel-slide {
        padding: 32px 24px;
    }

    .pe-slide-image {
        min-height: 260px;
    }

    .pe-slide-text {
        min-height: 220px;
    }

    .pe-slide-title {
        font-size: 20px;
    }

    .pe-slide-body {
        font-size: 14px;
    }

    .pe-carousel-prev,
    .pe-carousel-next {
        width: 34px;
        height: 34px;
    }

    .pe-carousel-prev { left: 8px; }
    .pe-carousel-next { right: 8px; }

    .pe-carousel-prev svg,
    .pe-carousel-next svg {
        width: 16px;
        height: 16px;
    }

    /* Hamburger shown, nav hidden by default on mobile/tablet */
    .lp-hamburger {
        display: block;
    }

    .lp-nav:not(.open) {
        display: none;
    }

    .lp-nav.open {
        display: flex;
        flex-direction: column;
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background: #1a1a2e;
        z-index: 200;
        padding: 80px 24px 24px;
        gap: 0;
        overflow-y: auto;
    }

    .lp-nav.open .lp-nav-close {
        display: flex;
        align-items: center;
        justify-content: center;
        position: absolute;
        top: 16px;
        right: 16px;
        width: 44px;
        height: 44px;
        background: none;
        border: none;
        cursor: pointer;
        color: #ffffff;
        border-radius: 50%;
    }

    .lp-nav.open .lp-nav-close:hover {
        background: rgba(255, 255, 255, 0.08);
    }

    .lp-nav.open .lp-nav-link {
        padding: 16px 0;
        font-size: 20px;
        font-weight: 500;
        color: rgba(255, 255, 255, 0.85);
        border-bottom: 1px solid rgba(255, 255, 255, 0.08);
        border-radius: 0;
    }

    .lp-nav.open .lp-nav-link:hover {
        color: #ffffff;
        background: transparent;
    }

    .pe-hero {
        padding: 64px 16px;
    }

    .pe-hero-image {
        padding: 72px 16px;
    }

    .pe-hero-title {
        font-size: 32px;
    }

    .pe-hero-subtitle {
        font-size: 15px;
        margin-bottom: 28px;
    }

    .pe-section {
        padding: 48px 0;
    }

    .pe-section-title {
        font-size: 24px;
        margin-bottom: 16px;
    }

    .pe-event-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    .pe-row {
        flex-direction: column;
    }

    .pe-col-4,
    .pe-col-6 {
        flex: 1 1 100%;
        min-width: unset;
    }

    .pe-contact-grid {
        flex-direction: column;
        gap: 24px;
    }

    .pe-cta {
        padding: 56px 16px;
    }

    .pe-cta-title {
        font-size: 24px;
    }

    .pe-cta-text {
        font-size: 15px;
    }

    .pe-container {
        padding: 0 16px;
    }

    /* Team grid → single column */
    .lp-team-grid {
        grid-template-columns: 1fr;
    }

    /* Page header adjustments */
    .lp-page-header {
        padding: 24px 0 20px;
    }

    .lp-page-title {
        font-size: 22px;
    }

    .lp-season-title-row {
        flex-direction: column;
        align-items: flex-start;
        gap: 8px;
    }

    /* Registration card stacks — left-aligned with a full-width action button.
       The old text-align:center made long clinic titles wrap awkwardly around a
       centered axis; left reads far cleaner on a phone. */
    .lp-reg-card {
        flex-direction: column;
        align-items: stretch;
        text-align: left;
        padding: 18px;
        gap: 12px;
    }
    /* Keep the badge pinned to the top when the title wraps to two lines. */
    .lp-reg-card-header {
        align-items: flex-start;
    }
    .lp-reg-card-name {
        flex: 1;
    }
    .lp-reg-card-action {
        width: 100%;
    }
    .lp-reg-card-action .pe-btn {
        width: 100%;
    }

    /* About section responsive */
    .lp-season-about {
        padding: 28px 0;
    }
    .lp-about-title {
        font-size: 19px;
    }
    .lp-about-content {
        font-size: 14px;
    }


    /* Team meta stacks */
    .lp-team-meta {
        flex-direction: column;
        gap: 8px;
    }

    /* Roster table */
    .lp-roster-table th,
    .lp-roster-table td {
        padding: 10px 12px;
        font-size: 14px;
    }

    /* Breadcrumbs smaller */
    .lp-breadcrumbs li {
        font-size: 13px;
    }

    /* Sponsors: tighter grid on mobile */
    .pe-sponsor-grid {
        gap: 20px;
    }
    .pe-sponsor-item {
        min-width: 80px;
        max-width: 120px;
    }
    .pe-sponsor-logo {
        max-height: 48px;
        max-width: 100px;
    }

    /* Footer responsive */
    .lp-footer-grid {
        grid-template-columns: 1fr;
        gap: 32px;
    }

    .lp-footer-brand {
        padding-right: 0;
    }

    .lp-footer-bottom {
        flex-direction: column;
        gap: 8px;
        text-align: center;
    }

    /* New page styles responsive */
    .lp-page-title {
        font-size: 28px;
    }

    .lp-form-row {
        grid-template-columns: 1fr;
    }

    /* Reclaim horizontal room inside the card on a phone. */
    .lp-contact-form {
        padding: 20px;
    }

    /* Sponsor grids on a phone. minmax(0, …) on every track: a sponsor name is
       free text and one long unbroken word in a bare 1fr track grows the whole
       page sideways. The tier hierarchy survives the shrink — the top tier goes
       one-up and full width, the rest pair off. */
    .lp-sp-grid {
        grid-template-columns: repeat(auto-fill, minmax(min(100%, 150px), 1fr));
        gap: 16px;
    }

    .lp-sp-grid--lg {
        grid-template-columns: minmax(0, 1fr);
    }

    .lp-sp-grid--lg .lp-sp-card-logo-wrap {
        height: 150px;
    }

    .lp-sp-grid--sm {
        grid-template-columns: repeat(auto-fill, minmax(min(100%, 140px), 1fr));
    }

    .lp-sp-card-logo-wrap {
        height: 110px;
    }

    .lp-sp-tier-title {
        font-size: 13px;
    }
}

/* ═══════════════════════════════════════════════════════════
   ═══ MODERN TEMPLATE (.tpl-modern) ═════════════════════════

   Ported from the Luke & Spencer design (P:\Luke-Spencer\...\prd).
   Structure and rhythm are taken literally; every colour that was
   part of *their* brand is driven off the league's own settings:

       L&S --color-primary-green  (accent rules, hovers, underline)
           → var(--league-secondary)
       L&S --color-primary-dark   (the dark band behind the cards)
           → var(--league-primary)

   The page chrome — header bar, footer, body neutrals — stays neutral
   charcoal exactly as the original does. The header there is grey/
   charcoal, NOT the brand green, and copying that is what keeps a
   league whose primary colour is a loud red from shipping a loud red
   navbar.

   Typography is a two-family system, and it is not "serif everywhere":
   Playfair Display carries the headings, but section titles and card
   titles deliberately drop to the UI sans (DM Sans), uppercase,
   wide-tracked, over a short accent rule.

   Modern is the only template on this stylesheet since migration 098
   (Bold is a self-contained document on league-v2.css), so these
   rules can restyle the shared lp- and pe- components directly.
   ═══════════════════════════════════════════════════════════ */

.tpl-modern {
    --mod-charcoal: #333333;
    /* Admin-customizable colors: --mod-heading (h1-h6 + heading classes) and
       --mod-text (body/paragraph) derive from the league's chosen colors,
       injected in :root by PublicLeagueController and defaulting to today's
       look. --mod-charcoal stays fixed for chrome (footer bg, outline buttons,
       ADA widget) so those never move with the text colors. */
    --mod-heading: var(--league-heading, var(--mod-charcoal));
    --mod-text: var(--league-text, #5a5a5a);
    --mod-bg: #ffffff;
    --mod-bg-alt: #f5f5f5;
    /* Robb, 2026-09-20: "make the top menu stand out better when the colors
       dont align well". #555555 was a mid grey that no league colour ever
       touched, and against a bright hero photo it read as haze rather than a
       band. Darker separates from light imagery, and the accent rule on
       .lp-header below is what ties it to the league. Scrolled stays DARKER
       than base -- it was #333 under a #555 base, so dropping the base without
       this would have inverted the scroll into a lightening. */
    --mod-header: #2b2b2b;
    --mod-header-scrolled: #1c1c1c;
    --mod-header-h: 72px;
    --mod-footer-text: #e8e0d5;

    --mod-accent: var(--league-secondary, #38bdf8);
    --mod-band: var(--league-primary, #1a1a2e);

    --mod-font-heading: 'Playfair Display', Georgia, serif;
    --mod-font-body: 'DM Sans', Arial, Helvetica, sans-serif;

    --mod-sp-xs: 0.5rem;
    --mod-sp-sm: 1rem;
    --mod-sp-md: 1.5rem;
    --mod-sp-lg: 2rem;
    --mod-sp-xl: 3rem;
    --mod-sp-xxl: 4rem;

    background: var(--mod-bg);
    font-family: var(--mod-font-body);
    color: var(--mod-text);
    line-height: 1.6;
    /* The header is fixed, so the document starts below it. The landing hero
       cancels this out (negative margin) to sit flush under the header. */
    padding-top: var(--mod-header-h);
}

.tpl-modern h1,
.tpl-modern h2,
.tpl-modern h3,
.tpl-modern h4,
.tpl-modern h5,
.tpl-modern h6 {
    font-family: var(--mod-font-heading);
    font-weight: 600;
    line-height: 1.3;
    color: var(--mod-heading);
}

.tpl-modern .lp-content {
    padding: 0;
}

/* ── Header ──────────────────────────────────────────────
   Fixed charcoal bar. At scroll-top it is inset to a 1400px
   column; once scrolled it spreads to the full viewport, darkens,
   and the league logo fades in from zero width. That "spread on
   scroll" move is the original's signature and is worth keeping. */
.tpl-modern .lp-header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 1000;
    background: var(--mod-header);
    /* The league's colour as a 3px rule along the bottom, plus a stronger drop
       shadow so the bar sits ON the hero instead of blending into it.

       INSET, not border-bottom, and that is not a style preference: .lp-header-inner
       pins its height to --mod-header-h and <body> offsets itself by the same
       variable (see the note on that height). A real border adds 3px to the
       header and nothing to the offset, so a hero set to sit flush below it
       would show a 3px strip of body background -- the exact failure that
       comment warns about. An inset shadow paints inside the existing box. */
       --mod-header-rule is the accent LIFTED when it is too dark to read here
       (PublicLeagueController::accentOnDark); it falls back to the raw accent
       for any page that does not inject it. */
    box-shadow: inset 0 -3px 0 var(--mod-header-rule, var(--mod-accent)), 0 2px 10px rgba(0, 0, 0, 0.28);
    transition: background 0.4s ease;
}

.tpl-modern .lp-header.lp-header-scrolled {
    background: var(--mod-header-scrolled);
}

.tpl-modern .lp-header-inner {
    display: flex;
    align-items: center;
    gap: var(--mod-sp-sm);
    /* L&S signature: at the top the content sits in a centred 1400px column;
       on scroll it spreads to the full-width edges (see the .lp-header-scrolled
       rule below) while the logo fades in on the left. */
    max-width: 1400px;
    margin: 0 auto;
    /* Height is pinned to --mod-header-h (not left to content + padding) so the
       header is EXACTLY as tall as the body's padding-top offset. If it renders
       shorter, a strip of body background shows between the header and a
       flush-below "fit" hero. Vertical padding is zeroed for the same reason. */
    height: var(--mod-header-h);
    padding: 0 var(--mod-sp-md);
    transition: max-width 0.4s ease, padding 0.4s ease;
}

/* Scrolled state: spread to the full-width edges — logo (fades in) + Register on
   the far left, nav on the far right. Ported from L&S
   `.site-header.is-scrolled .header-inner` (max-width 1400 -> 100%, pad sm -> xl).
   Vertical padding stays 0 (the flush-below "fit" hero needs the header exactly
   --mod-header-h tall). */
.tpl-modern .lp-header-scrolled .lp-header-inner {
    max-width: 100%;
    padding: 0 var(--mod-sp-xl);
}
/* Mobile: keep the header padding constant on scroll so the logo + hamburger +
   a11y icon stay pinned at the edges (the desktop sp-md -> sp-xl growth pushed
   them toward the centre on a phone). */
@media (max-width: 900px) {
    .tpl-modern .lp-header-scrolled .lp-header-inner { padding: 0 var(--mod-sp-md); }
}

.tpl-modern .lp-logo-link {
    display: flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    /* min-width:0 so a long league name can ellipsis instead of
       forcing the flex row wider than the viewport. */
    min-width: 0;
}

.tpl-modern .lp-logo-img {
    height: 40px;
    width: auto;
    opacity: 0;
    max-width: 0;
    overflow: hidden;
    transition: opacity 0.4s ease, max-width 0.4s ease;
}

.tpl-modern .lp-header-scrolled .lp-logo-img {
    opacity: 1;
    max-width: 50px;
}

.tpl-modern .lp-logo-placeholder {
    width: 36px;
    height: 36px;
    flex-shrink: 0;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--mod-accent);
    color: #fff;
    font-family: var(--mod-font-body);
    font-weight: 700;
    font-size: 1rem;
    /* Like the logo image, the initial-letter mark is hidden at the top and
       fades in on scroll (so a no-logo league still gets the L&S animation). */
    opacity: 0;
    max-width: 0;
    overflow: hidden;
    transition: opacity 0.4s ease, max-width 0.4s ease;
}

.tpl-modern .lp-header-scrolled .lp-logo-placeholder {
    opacity: 1;
    max-width: 36px;
}

.tpl-modern .lp-league-name {
    font-family: var(--mod-font-body);
    font-size: 0.875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: #fff;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tpl-modern .lp-nav {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--mod-sp-xs);
    /* The base .lp-nav is flex:1 + justify-content:center, which stretches the
       nav across the middle and centres the links. Modern wants brand on the
       left and the links grouped on the right next to the Register button, so
       stop the nav growing and let margin-left:auto push it (and the CTA) over. */
    flex: 0 1 auto;
    margin-left: auto;
}

.tpl-modern .lp-nav-link {
    position: relative;
    display: block;
    padding: var(--mod-sp-xs) var(--mod-sp-sm);
    font-size: 0.875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: #fff;
    text-decoration: none;
    border-radius: 4px;
    transition: color 0.2s ease;
}

.tpl-modern .lp-nav-link::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 50%;
    width: 0;
    height: 2px;
    background: var(--mod-accent);
    transform: translateX(-50%);
    transition: width 0.3s ease;
}

.tpl-modern .lp-nav-link:hover::after,
.tpl-modern .lp-nav-link:focus::after {
    width: 80%;
}

/* Active nav item (current page). Desktop: accent text + the underline stays
   lit. The mobile dropdown gets its own treatment in the <=900 block. */
/* The current page reads white; the accent moves to the underline, which is a
   2px bar beside white rather than text that has to be legible on its own.

   This was accent-coloured TEXT on the grey bar, so how readable it was
   depended entirely on which accent a league picked -- and measured against
   the old #555555 almost nothing cleared it: red 1.28:1, royal blue 1.11,
   dark green 1.05, purple 1.31, against a 4.5 floor. Sky blue and amber
   reached ~3.5, which is large-text-only. White is 7.46:1 there and better on
   the darker bar, and it does not vary by league.

   Not a new invention: Default has done exactly this since it shipped
   (league-miniport.css, .lp-nav-active -> #fff with the accent in an inset
   underline), and the mobile dropdown already forces white at line ~1095.
   This is Modern's desktop bar catching up with both. */
.tpl-modern .lp-nav-link.lp-nav-active {
    color: #fff;
}
.tpl-modern .lp-nav-link.lp-nav-active::after {
    width: 80%;
}

.tpl-modern .lp-header-cta {
    flex-shrink: 0;
    padding: var(--mod-sp-xs) var(--mod-sp-md);
    font-family: var(--mod-font-body);
    font-size: 0.8125rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: #fff;
    text-decoration: none;
    background: var(--mod-accent);
    border: 2px solid var(--mod-accent);
    border-radius: var(--lp-btn-radius);
    white-space: nowrap;
    transition: background 0.2s ease, color 0.2s ease;
}

.tpl-modern .lp-header-cta:hover,
.tpl-modern .lp-header-cta:focus {
    background: transparent;
    color: #fff;
}

/* Header icon buttons (hamburger + mobile accessibility). Hidden on desktop;
   the <=900 block turns them on. .lp-mobile-only covers the in-menu Register
   item and the a11y button — anything that only exists in the mobile header. */
.tpl-modern .lp-hamburger,
.tpl-modern .lp-ada-mobile {
    display: none;
    align-items: center;
    justify-content: center;
    background: transparent;
    border: none;
    /* White by default and on hover (admin-configurable via --header-icon-color). */
    color: var(--header-icon-color, #fff);
    cursor: pointer;
    padding: var(--mod-sp-xs);
    line-height: 1;
}

.tpl-modern .lp-hamburger:hover,
.tpl-modern .lp-ada-mobile:hover {
    color: var(--header-icon-color, #fff);
}

.tpl-modern .lp-mobile-only {
    display: none;
}

/* Hamburger morphs to an X when its menu is open (aria-expanded). */
.tpl-modern .lp-hamburger .lp-ham-x {
    display: none;
}
.tpl-modern .lp-hamburger[aria-expanded="true"] .lp-ham-bars {
    display: none;
}
.tpl-modern .lp-hamburger[aria-expanded="true"] .lp-ham-x {
    display: block;
}

/* ── Hero ────────────────────────────────────────────────
   Full-bleed image band. The league logo is the hero mark (the
   original sets its wordmark art here), with the tagline beneath
   as a wide-tracked slogan. Black base shows through if no hero
   image is set, and a solid bar closes the section off. */
.tpl-modern .pe-mod-hero {
    position: relative;
    min-height: 60vh;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Cancel the body's header offset so the image runs up behind the fixed
       header (no white strip between them), then pad the content back down so
       the title clears the header. */
    margin-top: calc(-1 * var(--mod-header-h));
    padding: calc(var(--mod-header-h) + var(--mod-sp-lg)) var(--mod-sp-md) var(--mod-sp-xxl);
    background: #000;
    background-size: cover;
    background-position: center;
    overflow: visible;
}

.tpl-modern .pe-mod-hero::before {
    content: '';
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
}

.tpl-modern .pe-mod-hero-inner {
    position: relative;
    z-index: 2;
    text-align: center;
    max-width: 900px;
}

/* "Fit" hero — show the ENTIRE uploaded image with no crop. The image is a
   responsive <img>, so it scales to the viewport width and the hero band is
   exactly as tall as the image at that width: nothing is zoomed or thrown away
   on any screen. It sits BELOW the header (no under-header pull) so the top of
   the artwork isn't hidden, and there's no darkening overlay over the design.
   Uses the compound `.pe-mod-hero.pe-mod-hero-fit` (0,3,0) so it beats the
   base AND the per-breakpoint `.pe-mod-hero` height/padding rules. */
.tpl-modern .pe-mod-hero.pe-mod-hero-fit {
    display: block;
    position: relative;
    min-height: 0;
    margin-top: 0;
    padding: 0;
    /* Whole image painted as a background (no savable <img>); the colour is the
       fallback if it fails to load. The band is sized by the hidden sizer below
       to the image's exact aspect, so cover fills it with no crop/letterbox. */
    background-color: var(--league-primary, #1a1a2e);
    /* ALWAYS `cover`, and the band is ALWAYS full-bleed. Inside the sizer's
       70vh cap the band carries the image's own aspect, so cover crops nothing
       and the whole banner shows edge to edge. Past the cap it crops from the
       centre. It was briefly `contain`, which pillarboxed the banner between
       two bars of league colour on a wide screen — correct arithmetic, bad
       page. Robb, 2026-10-18: "You think this is the fix? No, it's not."
       **Bars are always worse than a small crop. Never go back to contain.** */
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    overflow: hidden;
}
/* The scrim is sized by whether there is TEXT ON the image, not by any display
   mode — that is the whole reason the Fit/Fill dropdown could be deleted
   (2026-10-18). A league name over a bright photo needs 40% to stay readable;
   a designed banner with its branding baked in needs the artwork left alone, so
   it gets 15% for a touch of depth under the header. `.has-hero-title` is set
   by buildModernHero when hero_title_visible is on. It sits above the
   background and below the title/slogan (z-index:2); content/position/inset
   come from the base .pe-mod-hero::before rule. */
.tpl-modern .pe-mod-hero.pe-mod-hero-fit::before {
    background: rgba(0, 0, 0, 0.15);
}
.tpl-modern .pe-mod-hero.pe-mod-hero-fit.has-hero-title::before {
    background: rgba(0, 0, 0, 0.4);
}
/* Same file as the background, kept only to give the band the image's height at
   any width. Hidden from paint and from pointer/right-click (visibility:hidden
   drops it from hit-testing, so a right-click lands on the section, not an
   image — no "Save image" menu).

   OFF by default: in Fill mode the band sizes itself from 60vh and the section
   is a flex ROW, so a stray sizer would sit beside the title and knock it off
   centre. Fit turns it on below; the phone block turns it on for a mobile hero. */
.tpl-modern .pe-mod-hero-sizer { display: none; }

.tpl-modern .pe-mod-hero-fit .pe-mod-hero-sizer {
    display: block;
    visibility: hidden;
    pointer-events: none;
}
/* THE ONE SIZING RULE, and the reason the Fit/Fill dropdown is gone.
   The band is as tall as the image wants to be at this width, up to
   --hero-max-h. Inside the cap the band carries the image's own aspect, so the
   whole banner shows with no crop; past it the band holds at the cap and the
   `cover` background trims from the centre. That one line covers both things
   the dropdown used to ask about: a wide designed banner stays whole (old
   "Fit"), a tall photo crops to a sane band (old "Fill").

   `width:100%` is deliberate — the band must be FULL-BLEED at every width.
   `max-height` is what bounds it. An earlier pass made this `width:auto` so the
   band stopped at the image's own width; the arithmetic was right and the page
   was worse — a 1600px banner marooned between two bars of league colour.

   What this does NOT fix, because CSS cannot: browser zoom scales every CSS
   unit equally, so a band measured in % of width or in vh keeps the same share
   of the window at every zoom level. "I scaled to 33% and the hero stays the
   same" is inherent to a full-bleed band; the cap is what keeps that share
   sane (≤70%) instead of 120%. */
.tpl-modern .pe-mod-hero-fit .pe-mod-hero-sizer img {
    display: block;
    width: 100%;
    height: auto;
    max-height: var(--hero-max-h, 70vh);
    margin-inline: auto;
}
.tpl-modern .pe-mod-hero-fit .pe-mod-hero-inner {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    /* The base .pe-mod-hero-inner caps this at 900px. Absolutely positioned with
       left:0 AND right:0 AND a width cap is over-constrained, so the browser drops
       `right` and pins the box to the LEFT edge — the title sat against the left
       of the band at every width over 900px. `margin-inline:auto` is what resolves
       it to centred (measured 2026-10-18: left 0 in a 2545px band, want 823). */
    margin-inline: auto;
}

/* Optional phone hero image (migration 130): under 700px the section swaps to
   the mobile image, shown WHOLE — like desktop Fit — so a purpose-made phone
   graphic with baked-in branding (logo, tagline, etc.) is never cropped. The
   mobile sizer <img> sets the band to the image's exact aspect, and the image
   paints as a cover background of that exact-aspect box, so nothing is cropped
   or letterboxed. The band sits BELOW the fixed header (no under-header pull),
   so the top of the artwork clears the nav bar. Works from either desktop mode
   (Fit or Fill). */
@media (max-width: 700px) {
    .tpl-modern .pe-mod-hero.has-mobile-hero {
        display: block !important;
        min-height: 0 !important;
        margin-top: 0 !important;   /* start below the fixed header, not under it */
        padding: 0 !important;
        background-image: var(--hero-mobile-img) !important;
        background-size: cover !important;   /* box carries the image's own aspect → no crop */
        background-position: center !important;
        background-repeat: no-repeat !important;
        overflow: hidden !important;
    }
    /* Light-touch overlay only — a designed banner shouldn't be muddied. */
    .tpl-modern .pe-mod-hero.has-mobile-hero::before { background: rgba(0, 0, 0, 0.12) !important; }
    /* The sizer is on here even in Fill mode — the band's height is the phone
       image's. Its <picture> has already swapped to the phone file on this same
       700px breakpoint, so no second element is needed. width:100% rather than
       the desktop cap: a phone is always narrower than a purpose-made phone
       banner, so there is nothing to cap, and a small one is better slightly
       upscaled than pillarboxed between two colour bars on a 375px screen. */
    .tpl-modern .pe-mod-hero.has-mobile-hero .pe-mod-hero-sizer {
        display: block !important;
        visibility: hidden !important;
        pointer-events: none !important;
    }
    .tpl-modern .pe-mod-hero.has-mobile-hero .pe-mod-hero-sizer img {
        display: block !important;
        width: 100% !important;
        max-width: 100% !important;
        height: auto !important;
    }
    /* Any kept title/slogan overlays the art, centred (hidden if the league
       turned the hero title off — the usual case for a baked-in poster). */
    .tpl-modern .pe-mod-hero.has-mobile-hero .pe-mod-hero-inner {
        position: absolute !important;
        inset: 0 !important;
        display: flex !important;
        flex-direction: column !important;
        align-items: center !important;
        justify-content: center !important;
    }
}

.tpl-modern .pe-mod-hero-logo {
    max-width: 620px;
    width: 90%;
    margin: 0 auto;
    height: auto;
    display: block;
}

.tpl-modern .pe-mod-hero-title {
    font-family: var(--mod-font-heading);
    font-size: clamp(2rem, 5vw, 3.25rem);
    font-weight: 600;
    color: #fff;
    text-shadow: 1px 2px 6px rgba(0, 0, 0, 0.45);
    margin: 0;
}

.tpl-modern .pe-mod-hero-slogan {
    font-family: var(--mod-font-body);
    font-size: 1.35rem;
    font-weight: 300;
    color: rgba(255, 255, 255, 0.9);
    letter-spacing: 0.15em;
    text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.4);
    margin-top: var(--mod-sp-md);
}

.tpl-modern .pe-mod-hero-cta {
    margin-top: var(--mod-sp-lg);
}

/* ── Section bands ───────────────────────────────────────
   The page alternates a white band and a league-coloured dark
   band, each opening with a centred title + description. */
.tpl-modern .pe-mod-section {
    padding: var(--mod-sp-xxl) var(--mod-sp-md);
}

.tpl-modern .pe-mod-light {
    background: var(--mod-bg);
}

.tpl-modern .pe-mod-tint {
    background: var(--mod-bg-alt);
}

.tpl-modern .pe-mod-dark {
    background: var(--mod-band);
    color: #fff;
}

/* White the dark band's OWN headings (the section title + intro) only — NOT
   every descendant heading. The season/event cards live on this band but are
   white cards, and a blanket `.pe-mod-dark h3` was turning their h3 titles
   white-on-white (invisible — that was the "empty" card top in the feedback). */
.tpl-modern .pe-mod-dark > .pe-container > .pe-mod-section-header .pe-mod-section-title,
.tpl-modern .pe-mod-dark > .pe-container > .pe-mod-intro {
    color: #fff;
}

.tpl-modern .pe-mod-dark .pe-mod-section-title::after {
    background: #fff;
}

.tpl-modern .pe-mod-section-header {
    text-align: center;
    margin-bottom: var(--mod-sp-xl);
}

.tpl-modern .pe-mod-section-title {
    position: relative;
    display: inline-block;
    font-family: var(--mod-font-body);
    font-size: clamp(1.6rem, 4vw, 2.5rem);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.15em;
    color: var(--mod-heading);
    padding-bottom: 15px;
    margin: 0;
}

.tpl-modern .pe-mod-section-title::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    width: 80px;
    height: 3px;
    background: var(--mod-accent);
}

.tpl-modern .pe-mod-section-desc {
    max-width: 800px;
    margin: var(--mod-sp-md) auto 0;
    font-size: 1rem;
    line-height: 1.8;
}

.tpl-modern .pe-mod-dark .pe-mod-section-desc {
    color: rgba(255, 255, 255, 0.9);
}

/* ── Feature carousel (league_news_items) ────────────────
   A scroll-snap carousel: the viewport is the scroll container, cards snap
   to its left edge, and --spv (slides per view, set by JS: 3/2/1) drives the
   card width via calc. Native horizontal scroll is the no-JS fallback; the
   arrows and dots are wired up in the shell script and hide when all cards fit.
   Image band on top, body below, action pinned to the bottom so a row of cards
   keeps its buttons on one line regardless of how much body copy each carries. */
.tpl-modern .pe-mod-carousel {
    position: relative;
    max-width: 1100px;
    margin: 0 auto;
}

.tpl-modern .pe-mod-carousel-viewport {
    display: flex;
    align-items: stretch;
    gap: var(--mod-sp-lg);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    -webkit-overflow-scrolling: touch;
    /* overflow-x:auto clips vertical overflow too, which would shear the card
       drop-shadow into a hard grey line. This padding gives the shadow room to
       render inside the scroll area on every side. */
    padding: 22px 6px 34px;
}

/* Hide the scrollbar. scrollbar-width is gated behind the Firefox @supports
   check on purpose — setting it unconditionally kills ::-webkit-scrollbar on
   Chromium 121+. */
.tpl-modern .pe-mod-carousel-viewport::-webkit-scrollbar {
    display: none;
}
@supports (-moz-appearance: none) {
    .tpl-modern .pe-mod-carousel-viewport {
        scrollbar-width: none;
    }
}

.tpl-modern .pe-mod-carousel .pe-mod-feature-card {
    flex: 0 0 calc((100% - (var(--spv, 3) - 1) * var(--mod-sp-lg)) / var(--spv, 3));
    scroll-snap-align: start;
}

.tpl-modern .pe-mod-carousel-arrow {
    position: absolute;
    top: 40%;
    transform: translateY(-50%);
    width: 46px;
    height: 46px;
    border-radius: 50%;
    border: 1px solid #e2e2e2;
    background: #fff;
    color: var(--mod-charcoal);
    font-size: 1.6rem;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
    z-index: 2;
    transition: background 0.2s, color 0.2s, border-color 0.2s;
}

.tpl-modern .pe-mod-carousel-arrow:hover {
    background: var(--mod-accent);
    color: #fff;
    border-color: var(--mod-accent);
}

.tpl-modern .pe-mod-carousel-arrow[hidden] {
    display: none;
}

.tpl-modern .pe-mod-carousel-prev {
    left: -8px;
}

.tpl-modern .pe-mod-carousel-next {
    right: -8px;
}

.tpl-modern .pe-mod-carousel-dots {
    display: flex;
    justify-content: center;
    gap: 8px;
    margin-top: var(--mod-sp-lg);
}

.tpl-modern .pe-mod-carousel-dot {
    width: 9px;
    height: 9px;
    padding: 0;
    border: none;
    border-radius: 999px;
    background: #d2d2d6;
    cursor: pointer;
    transition: width 0.25s ease, background 0.25s ease;
}

.tpl-modern .pe-mod-carousel-dot.is-active {
    width: 26px;
    background: var(--mod-accent);
}

.tpl-modern .pe-mod-feature-card {
    display: flex;
    flex-direction: column;
    min-width: 0;
    background: #fff;
    border-radius: 16px;
    overflow: hidden;
    /* Soft, light drop shadow — a gentle lift on clean white, matching the
       L&S look. Kept subtle on purpose so the shadows don't pool into a grey
       wash behind the cards. The viewport pads itself so it isn't clipped. */
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.07);
    text-align: center;
    transition: transform 0.3s ease, box-shadow 0.3s ease;
}

.tpl-modern .pe-mod-feature-card:hover {
    box-shadow: 0 8px 22px rgba(0, 0, 0, 0.12);
}

/* The news image frame is 16:9 — the shape the AI Images "News slide"
   (HeroCompositor 'card' preset, 1600×900) is made at — so the whole slide
   shows at any card width. A fixed height here cropped the headline off a
   16:9 slide the moment the card was wider than 356px. Other shapes still
   fill the frame (cover) and lose their edges, as before. */
.tpl-modern .pe-mod-feature-image {
    width: 100%;
    aspect-ratio: 16 / 9;
    background-size: cover;
    background-position: center;
    background-color: var(--mod-bg-alt);
}

.tpl-modern .pe-mod-feature-body {
    display: flex;
    flex-direction: column;
    flex-grow: 1;
    padding: var(--mod-sp-lg);
}

.tpl-modern .pe-mod-feature-title {
    font-family: var(--mod-font-body);
    font-size: 1.1rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--mod-band);
    margin: 0 0 var(--mod-sp-sm);
    /* Clamp to two lines so a long program name doesn't shove one card taller
       than its neighbours. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    min-height: 2.6em;
}

.tpl-modern .pe-mod-feature-text {
    font-size: 0.95rem;
    color: #666;
    margin-bottom: var(--mod-sp-md);
    /* Admin-authored copy can contain a long URL or unbroken token. */
    overflow-wrap: anywhere;
    /* Truncate to a fixed number of lines so every card is the same height
       regardless of how much an admin wrote. No flex-grow here — it would
       fight -webkit-box; the button's margin-top:auto pins it to the bottom
       and align-items:stretch on the viewport equalises the card heights. */
    display: -webkit-box;
    -webkit-line-clamp: 6;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.tpl-modern .pe-mod-feature-body .pe-btn {
    margin-top: auto;
    align-self: center;
}

/* The whole card opens the item's drawer (2026-10-24). The "Read more" button
   is the accessible control; its ::after is stretched over the card, so the
   card needs to be the containing block. The card's own hover already changes
   the shadow — nothing lifts. */
.tpl-modern .pe-mod-feature-card {
    position: relative;
    cursor: pointer;
}

.tpl-modern .pe-mod-feature-open {
    margin-top: auto;
    align-self: center;
    appearance: none;
    border: 0;
    background: none;
    padding: 6px 0;
    font: inherit;
    font-size: 0.9rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--mod-band);
    cursor: pointer;
}

.tpl-modern .pe-mod-feature-open::after {
    content: "";
    position: absolute;
    inset: 0;
}

.tpl-modern .pe-mod-feature-card:hover .pe-mod-feature-open {
    text-decoration: underline;
}

.tpl-modern .pe-mod-feature-open:focus-visible {
    outline: none;
}

.tpl-modern .pe-mod-feature-card:has(.pe-mod-feature-open:focus-visible) {
    outline: 2px solid var(--mod-band);
    outline-offset: 2px;
}

.tpl-modern .pe-mod-programs-cta {
    margin-top: var(--mod-sp-xl);
    text-align: center;
}

/* Available to any template on this sheet: keeps a heading in the
   document for screen readers and outline tools when the visible
   mark is an image (the hero logo). */
.lp-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ── Intro paragraph on a dark band ──────────────────── */
.tpl-modern .pe-mod-intro {
    max-width: 900px;
    margin: 0 auto var(--mod-sp-xl);
    text-align: center;
    line-height: 1.8;
    color: rgba(255, 255, 255, 0.9);
}

/* ── Contact strip ──────────────────────────────────── */
.tpl-modern .pe-mod-contact-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
    gap: var(--mod-sp-lg);
    max-width: 1000px;
    margin: 0 auto;
    text-align: center;
}

.tpl-modern .pe-mod-contact-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--mod-sp-xs);
    min-width: 0;
}

.tpl-modern .pe-mod-contact-icon {
    width: 56px;
    height: 56px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--mod-bg-alt);
    color: var(--mod-band);
    margin-bottom: var(--mod-sp-xs);
}

.tpl-modern .pe-mod-contact-value {
    font-size: 0.95rem;
    color: var(--mod-text);
    text-decoration: none;
    overflow-wrap: anywhere;
}

.tpl-modern .pe-mod-contact-value:hover {
    color: var(--mod-accent);
}

/* ── Buttons ─────────────────────────────────────────────
   Outline by default, filling on hover — the original's ghost
   button. .pe-btn is shared with every sub-page, so this restyle
   reaches registration, contact, season and team pages too. */
.tpl-modern .pe-btn {
    display: inline-block;
    padding: var(--mod-sp-sm) var(--mod-sp-lg);
    font-family: var(--mod-font-body);
    font-size: 0.875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    text-decoration: none;
    border: 2px solid var(--mod-charcoal);
    /* var, not a literal: this rule outranks .pe-btn, so a hardcoded value
       here silently defeated the league's own Button shape setting. */
    border-radius: var(--lp-btn-radius);
    background: transparent;
    color: var(--mod-charcoal);
    cursor: pointer;
    transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}

.tpl-modern .pe-btn:hover,
.tpl-modern .pe-btn:focus {
    background: var(--mod-charcoal);
    color: #fff;
}

.tpl-modern .pe-btn-primary {
    border-color: var(--league-btn-bg, var(--mod-accent));
    background: var(--league-btn-bg, var(--mod-accent));
    color: var(--league-btn-text, #fff);
}

/* Darken the button on hover (keeps the label legible for ANY custom button
   colour — the old transparent+band hover went blank once buttons could be
   recoloured). Mirrors the season-banner button hover below. */
.tpl-modern .pe-btn-primary:hover,
.tpl-modern .pe-btn-primary:focus {
    background: color-mix(in srgb, var(--league-btn-bg, var(--mod-accent)) 85%, #000);
    border-color: color-mix(in srgb, var(--league-btn-bg, var(--mod-accent)) 85%, #000);
    color: var(--league-btn-text, #fff);
}

/* Season CTA banner sits on the navy --league-primary band. Its heading is the
   registration status (seasonRegLead), not a fixed phrase.
   Two Modern defaults break it there: the h3 heading rule paints the title
   --mod-charcoal (dark → invisible on navy), and the generic primary-button
   hover goes transparent with navy text (blank on navy). Scope both to stay
   legible on the dark band. */

/* ⚠ SCOPED TO THE DARK BAND. Unscoped, this reverse styling reached every
   .pe-btn-light on a Modern page, including the two on the Registrations
   card — white text, transparent background, white border, on a white
   card. Measured contrast 1.00: the upcoming row's Remind me and the
   closed row's View were present, sized and clickable, and painted
   nothing. Robb reported it as "no button". Same fault as
   .lp-reg-price{color:#fff} after the season card went light.
   Do NOT delete the rule instead: .pe-mod-programs-cta's View Divisions
   really is on the navy band and needs it. */
.tpl-modern .pe-mod-dark .pe-btn-light {
    border-color: #fff;
    color: #fff;
    background: transparent;
}

.tpl-modern .pe-mod-dark .pe-btn-light:hover,
.tpl-modern .pe-mod-dark .pe-btn-light:focus {
    background: #fff;
    color: var(--mod-band);
}

/* ── Shared sub-page components ──────────────────────────
   Divisions, seasons, teams, registration, contact and sponsors
   all render through .pe-section / .pe-section-title / card
   classes. Pulling them onto the Modern type scale is what keeps
   the sub-pages from looking like a different website. */
.tpl-modern .pe-section {
    padding: var(--mod-sp-xxl) var(--mod-sp-md);
}

.tpl-modern .pe-section-title {
    position: relative;
    display: inline-block;
    font-family: var(--mod-font-body);
    font-size: clamp(1.5rem, 3.5vw, 2.25rem);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.15em;
    color: var(--mod-heading);
    padding-bottom: 14px;
}

.tpl-modern .pe-section-title::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    width: 70px;
    height: 3px;
    background: var(--mod-accent);
}

/* Season / event / team cards. These render on the dark Programs band on the
   landing page AND on white sub-page backgrounds, so they're built as clean
   white cards that read on either: no coloured header strip (that used to be
   league-primary and vanished into the matching dark band), a slim accent bar
   at the top instead, and the status badge floated top-right of the body. */
.tpl-modern .pe-event-card,
.tpl-modern .lp-season-card,
.tpl-modern .lp-team-card {
    background: #fff;
    border-radius: 16px;
    overflow: hidden;
    box-shadow: 0 14px 34px rgba(0, 0, 0, 0.18);
    border: none;
    border-top: 3px solid var(--mod-accent);
    min-width: 0;
}

/* Drop the old coloured header band down to a plain badge row. The badge keeps
   its solid status colour (lp-status-open/closed), so it still reads on white. */
.tpl-modern .pe-event-card-header {
    background: transparent;
    padding: 16px 18px 0;
    min-height: 0;
    justify-content: flex-end;
}

.tpl-modern .pe-event-card-body {
    padding: 12px 20px 20px;
}

.tpl-modern .pe-event-title,
.tpl-modern .lp-season-title,
.tpl-modern .lp-team-name {
    font-family: var(--mod-font-body);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    /* Charcoal, not the league colour — a league whose primary is pale would
       otherwise render the title unreadably light on the white card. */
    color: var(--mod-heading);
}

.tpl-modern .pe-event-details {
    border-top-color: #eee;
}

.tpl-modern .lp-view-details {
    color: var(--mod-accent);
}

/* Sits the division/type pill just under the title with a little breathing room. */
.tpl-modern .lp-season-card .lp-division-badge {
    align-self: flex-start;
    margin: 2px 0 2px;
}

/* ── Program card, "bold band" style (Robb's pick) ───────
   Scoped to .lp-season-card (season + event cards). Team cards use
   .lp-team-card and keep their own look. Header = sky accent band with the
   sport/division tag on the left and a white status chip on the right; white
   body with a full-width CTA button pinned to the bottom so a row of cards
   keeps its buttons aligned. Overrides the earlier accent-top-border look. */
.tpl-modern .lp-season-card {
    display: flex;
    flex-direction: column;
    border-top: none;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.16);
}
.tpl-modern .lp-season-card:hover {
    box-shadow: 0 14px 30px rgba(0, 0, 0, 0.22);
}
/* Navy header band = the league's own primary colour (matches the dark
   Programs section, so it melts into it there and reads as a bold band on the
   white division pages). Sport tag in the league accent; status as a SOLID
   colour pill with white text — pops on the navy and stays crisp. */
.tpl-modern .lp-season-card .pe-event-card-header {
    background: var(--league-card-header, var(--league-primary, #16233f));
    padding: 11px 15px;
    display: flex;
    align-items: center;
    gap: 8px;
    justify-content: flex-start;
    min-height: 0;
}
.tpl-modern .lp-season-card .pe-event-tag {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--mod-accent, #7dd3fc);
}
.tpl-modern .lp-season-card .pe-event-badge {
    margin-left: auto;
    background: #64748b;
    color: #fff;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
/* "Registration Open" pill follows the league's accent (secondary) colour when
   the page shell injects --lp-badge-open; falls back to the semantic green. Text
   colour is auto-picked (dark/white) server-side for legibility on any accent. */
.tpl-modern .lp-season-card .pe-event-badge.lp-status-open     { background: var(--lp-badge-open, #16a34a); color: var(--lp-badge-open-text, #fff); }
.tpl-modern .lp-season-card .pe-event-badge.lp-status-active   { background: #d97706; }
.tpl-modern .lp-season-card .pe-event-badge.lp-status-upcoming { background: #2563eb; }
.tpl-modern .lp-season-card .pe-event-badge.lp-status-full     { background: #dc2626; }
.tpl-modern .lp-season-card .pe-event-badge.lp-status-closed   { background: #64748b; }
.tpl-modern .lp-season-card .pe-event-card-body {
    padding: 15px;
    display: flex;
    flex-direction: column;
    gap: 7px;
    flex-grow: 1;
}
.tpl-modern .lp-season-card .pe-event-title {
    font-size: 15px;
    line-height: 1.25;
    margin: 0;
}
.tpl-modern .lp-season-card .pe-event-dates {
    font-size: 12.5px;
    color: #64748b;
    margin: 0;
}
.tpl-modern .lp-season-card .pe-event-meta {
    font-size: 15px;
    font-weight: 800;
    color: var(--mod-charcoal, #1a2233);
    margin: 0;
}
.tpl-modern .lp-season-card .pe-card-cta {
    display: block;
    text-align: center;
    margin-top: auto;
    padding: 10px;
    border-radius: 8px;
    font-size: 11px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    background: var(--mod-accent, #38bdf8);
    color: #04263a;
}
.tpl-modern .lp-season-card .pe-card-cta.is-muted {
    background: transparent;
    border: 1.5px solid #cbd5e1;
    color: #64748b;
}

.tpl-modern .lp-page-title {
    font-family: var(--mod-font-body);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--mod-heading);
}

/* ── Rich text (policies page, sanitised admin HTML) ─────
   A readable long-form column. The tag set matches the sanitizer allow-list
   (h2/h3, p, ul/ol/li, strong/em/u, a). */
.tpl-modern .lp-richtext {
    max-width: 760px;
    margin: 0 auto;
    color: var(--mod-text);
    font-size: 1rem;
    line-height: 1.75;
}
.tpl-modern .lp-richtext h2 {
    font-family: var(--mod-font-body);
    font-size: 1.4rem;
    font-weight: 700;
    color: var(--mod-heading);
    margin: 2em 0 0.6em;
}
.tpl-modern .lp-richtext h3 {
    font-family: var(--mod-font-body);
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--mod-heading);
    margin: 1.6em 0 0.5em;
}
.tpl-modern .lp-richtext h2:first-child,
.tpl-modern .lp-richtext h3:first-child {
    margin-top: 0;
}
.tpl-modern .lp-richtext p {
    margin: 0 0 1em;
}
.tpl-modern .lp-richtext ul,
.tpl-modern .lp-richtext ol {
    margin: 0 0 1em;
    padding-left: 1.4em;
}
.tpl-modern .lp-richtext li {
    margin-bottom: 0.4em;
}
.tpl-modern .lp-richtext a {
    color: var(--mod-accent);
    text-decoration: underline;
    overflow-wrap: anywhere;
}

/* ── Footer ──────────────────────────────────────────────
   Four columns over a charcoal band, then a social/legal row,
   then a darker copyright strip — the original's three-tier
   footer. Column content is CMS data the league already has;
   a column whose data is empty is dropped server-side. */
.tpl-modern .lp-footer {
    background: var(--mod-charcoal);
    color: var(--mod-footer-text);
    padding: 0;
}

.tpl-modern .lp-footer-inner {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0;
}

.tpl-modern .lp-footer-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
    gap: 40px;
    padding: 50px 30px 30px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

.tpl-modern .lp-footer-col {
    min-width: 0;
}

.tpl-modern .lp-footer-heading {
    font-family: var(--mod-font-body);
    font-size: 1rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--mod-footer-text);
    margin-bottom: 20px;
}

.tpl-modern .lp-footer-col p,
.tpl-modern .lp-footer-col address,
.tpl-modern .lp-footer-links a {
    font-style: normal;
    font-size: 0.9rem;
    line-height: 1.7;
    color: var(--mod-footer-text);
    text-decoration: none;
    overflow-wrap: anywhere;
}

.tpl-modern .lp-footer-links {
    list-style: none;
    margin: 0;
    padding: 0;
    /* Two side-by-side columns so the six quick links don't run down one long
       thin strip. Multicol balances them (3 + 3) and re-flows to whatever count
       a league's page-visibility toggles leave. */
    columns: 2;
    column-gap: 32px;
}

.tpl-modern .lp-footer-links li {
    padding: 6px 0;
    break-inside: avoid;
}

.tpl-modern .lp-footer-links a:hover,
.tpl-modern .lp-footer-links a:focus,
.tpl-modern .lp-footer-col a:hover,
.tpl-modern .lp-footer-col a:focus {
    color: var(--mod-accent);
}

.tpl-modern .lp-footer-about {
    font-size: 0.9rem;
    line-height: 1.7;
    color: var(--mod-footer-text);
    opacity: 0.9;
}

.tpl-modern .lp-footer-name {
    font-family: var(--mod-font-body);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: #fff;
}

.tpl-modern .lp-footer-social {
    display: flex;
    gap: 16px;
}

.tpl-modern .lp-social-icon {
    color: var(--mod-footer-text);
    transition: color 0.3s ease;
}

.tpl-modern .lp-social-icon svg {
    width: 22px;
    height: 22px;
}

.tpl-modern .lp-social-icon:hover {
    color: var(--mod-accent);
}

.tpl-modern .lp-footer-bottom {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 8px 20px;
    /* Same charcoal as the rest of the footer — no darker sub-bar. A thin
       hairline keeps the copyright row visually separated instead. */
    background: var(--mod-charcoal);
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    padding: 20px 30px;
    text-align: center;
    font-size: 0.875rem;
    color: var(--mod-footer-text);
}

.tpl-modern .lp-footer-middle {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    max-width: 1200px;
    margin: 0 auto;
    padding: 26px 30px;
}

.tpl-modern .lp-footer-legal {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    font-size: 0.875rem;
}

.tpl-modern .lp-footer-legal a {
    color: var(--mod-footer-text);
    text-decoration: none;
}

.tpl-modern .lp-footer-legal a:hover,
.tpl-modern .lp-footer-legal a:focus {
    color: var(--mod-accent);
}

.tpl-modern .lp-footer-legal span {
    color: #b0b0b0;
}

/* An empty social block would otherwise leave the legal links stranded
   in the middle of the row rather than at its end. */
.tpl-modern .lp-footer-middle:not(:has(.lp-footer-social)) .lp-footer-legal {
    margin-left: auto;
}

.tpl-modern .lp-footer-logo-wrap {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: var(--mod-sp-sm);
}

.tpl-modern .lp-footer-logo-img {
    height: 40px;
    width: auto;
}

.tpl-modern .lp-footer-logo-placeholder {
    width: 40px;
    height: 40px;
    flex-shrink: 0;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--mod-accent);
    color: #fff;
    font-weight: 700;
}

.tpl-modern .lp-powered-by a {
    color: var(--mod-footer-text);
    text-decoration: none;
}

/* ── Skip link ─────────────────────────────────────────
   Off-screen until focused, then drops below the fixed header. */
.tpl-modern .lp-skip-link {
    position: absolute;
    top: -100px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 2000;
    padding: 10px 18px;
    background: var(--mod-band);
    color: #fff;
    font-size: 0.875rem;
    font-weight: 600;
    text-decoration: none;
    border-radius: 0 0 6px 6px;
    transition: top 0.2s ease;
}

.tpl-modern .lp-skip-link:focus {
    top: 0;
}

/* ── Scroll-to-top ─────────────────────────────────────── */
/* Back-to-top, matched to Luke & Spencer: a charcoal rounded SQUARE (not a
   blue circle) with a white up-triangle, sitting bottom-right. */
.tpl-modern .lp-scroll-top {
    width: 50px;
    height: 50px;
    bottom: 30px;
    right: 30px;
    border-radius: 12px;
    border: none;
    background: var(--mod-header);
    color: #fff;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}

.tpl-modern .lp-scroll-top:hover {
    background: #444;
    border-color: transparent;
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3);
}

@media (max-width: 480px) {
    .tpl-modern .lp-scroll-top {
        width: 44px;
        height: 44px;
        bottom: 15px;
        right: 15px;
        border-radius: 10px;
    }
}

/* ═══════════════════════════════════════════════════════════
   ═══ ADA ACCESSIBILITY WIDGET ══════════════════════════════
   Side tab + flyout panel, ported from the source design.

   Dark mode is deliberately NOT among the toggles. Every other
   feature here is a filter or a text metric that works against
   any markup; a real dark mode needs a hand-written override for
   every component on every page, and a half-done one is worse
   than none. The rest stand on their own.
   ═══════════════════════════════════════════════════════════ */

.tpl-modern .ada-widget {
    /* No positioning here. The fixed/transform lives on .ada-trigger below (which
       has an OPAQUE background), so the composited layer is opaque and the browser
       keeps crisp subpixel text — a transparent transformed wrapper forced the
       coarser grayscale anti-aliasing that made the vertical label look choppy. */
    display: contents;
}

/* A TAB ON THE LEFT EDGE, icon only (Robb picked option 8 of eight,
   2026-09-23).

   This is a partial reversal and worth saying so. It was a vertical tab on the
   RIGHT edge; on 2026-09-22 Robb moved it to the lower-left corner reading
   horizontally ("move the Accessibility to the lower left corner of each page,
   and have it open on the left side – to right"), and the word was kept then
   because dropping it was a change nobody had asked for. He has now asked.
   What survives from that decision is the SIDE: the tab is on the left, and the
   panel still opens left-to-right, so the control and the thing it opens are on
   the same edge. Only the corner-versus-edge placement changed.

   No vertical text, so the writing-mode that used to make this tab awkward is
   gone with it — the choppy-subpixel note above was about rotated glyphs and no
   longer applies. The button keeps an OPAQUE background regardless, because the
   transform below composites it.

   The accessible name lives on aria-label, which the button has always carried;
   an icon-only control with no name would be a poor joke on this widget in
   particular.

   Bottom-left is clear on this skin: .tpl-modern .lp-scroll-top is bottom-RIGHT
   (30/30), so the two sit diagonally opposite rather than on top of each other.
   Worth stating because league-readonly.css carries a note about a bottom-left
   FAB that DID collide with Back-to-top on that skin's layout. */
.tpl-modern .ada-trigger {
    position: fixed;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    z-index: 1100;
    display: flex;
    align-items: center;
    justify-content: center;
    /* 44 wide is the tap target; 88 tall is what makes it read as an edge tab
       rather than a square button that happens to touch the edge. */
    width: 44px;
    height: 88px;
    padding: 0;
    background: var(--mod-band);
    color: #fff;
    border: none;
    /* Square against the edge, rounded where it leaves it. */
    border-radius: 0 10px 10px 0;
    cursor: pointer;
    box-shadow: 2px 0 12px rgba(0, 0, 0, 0.2);
    transition: background 0.2s ease, width 0.2s ease;
}
/* The word is gone from the tab; aria-label carries the name. */
.tpl-modern .ada-trigger-label { display: none; }
.tpl-modern .ada-trigger-icon { width: 24px; height: 24px; }

/* The universal accessibility symbol beside the word (Robb, 2026-09-23, asked
   for icon options; this is #3 of eight). The button was already flex with an
   8px gap and simply had nothing to put in it.

   SYMBOL PLUS WORD, no responsive collapse. The symbol is what every
   accessibility widget uses and is recognised without reading; the word is
   what makes it usable for someone who does not know the symbol, which is
   exactly the audience.

   There is no phone variant because THIS BUTTON HAS NO PHONE. The <=900 block
   below sets `.tpl-modern .ada-widget { display: none }` and swaps in the
   header's `.lp-ada-mobile` instead, so the floating widget only ever renders
   above 900px, where there is ample room for both. A 599px rule was written
   here first and was dead on arrival — measured at 390px, the button computed
   to 48x48 and still drew nothing, because its PARENT was already hidden. */
.tpl-modern .ada-trigger-icon { flex-shrink: 0; }

/* No phone inset rule: an edge tab has no corner to inset from, and the widget
   is hidden below 900 anyway (see the block further down that swaps in the
   header button). A rule here would only contradict the one above. */

.tpl-modern .ada-trigger:hover {
    background: var(--mod-accent);
}

.tpl-modern .ada-panel {
    position: fixed;
    /* top+bottom instead of height:100% — unambiguous full height with no
       dependence on a resolved parent height. */
    top: 0;
    bottom: 0;
    /* Opens from the LEFT, to match the trigger's new corner. */
    left: 0;
    right: auto;
    width: 320px;
    max-width: 90vw;
    background: #fff;
    box-shadow: 4px 0 24px rgba(0, 0, 0, 0.25);
    transform: translateX(-100%);
    transition: transform 0.3s ease;
    display: flex;
    flex-direction: column;
    z-index: 1101;
}

.tpl-modern .ada-panel.open {
    transform: translateX(0);
}

.tpl-modern .ada-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--mod-sp-sm) var(--mod-sp-md);
    background: var(--mod-band);
    color: #fff;
    flex-shrink: 0;
}

.tpl-modern .ada-panel-header h2 {
    font-family: var(--mod-font-body);
    font-size: 1rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #fff;
    margin: 0;
}

.tpl-modern .ada-close {
    background: transparent;
    border: none;
    color: #fff;
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
    padding: 4px 8px;
}

.tpl-modern .ada-panel-body {
    padding: var(--mod-sp-md);
    overflow-y: auto;
    /* min-height:0 lets this flex child shrink below its content and scroll,
       instead of overflowing the panel and pushing the header off-screen. */
    flex: 1 1 auto;
    min-height: 0;
}

.tpl-modern .ada-section {
    margin-bottom: var(--mod-sp-md);
}

.tpl-modern .ada-section-title {
    font-size: 0.75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #999;
    margin-bottom: var(--mod-sp-xs);
}

.tpl-modern .ada-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 0;
    border-bottom: 1px solid #eee;
}

.tpl-modern .ada-row:last-child {
    border-bottom: none;
}

.tpl-modern .ada-label {
    font-size: 0.875rem;
    color: var(--mod-charcoal);
    min-width: 0;
}

.tpl-modern .ada-toggle {
    position: relative;
    width: 44px;
    height: 24px;
    flex-shrink: 0;
    border-radius: 999px;
    background: #ccc;
    cursor: pointer;
    transition: background 0.2s ease;
}

.tpl-modern .ada-toggle.active {
    background: var(--mod-accent);
}

.tpl-modern .ada-toggle::after {
    content: '';
    position: absolute;
    top: 3px;
    left: 3px;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    background: #fff;
    transition: transform 0.2s ease;
}

.tpl-modern .ada-toggle.active::after {
    transform: translateX(20px);
}

.tpl-modern .ada-slider {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
}

.tpl-modern .ada-slider button {
    width: 28px;
    height: 28px;
    border: 1px solid #ddd;
    border-radius: 6px;
    background: #f7f7f7;
    color: var(--mod-charcoal);
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}

.tpl-modern .ada-slider button:hover {
    background: var(--mod-accent);
    border-color: var(--mod-accent);
    color: #fff;
}

.tpl-modern .ada-slider span {
    min-width: 44px;
    text-align: center;
    font-size: 0.8125rem;
    color: var(--mod-text);
}

.tpl-modern .ada-reset {
    width: 100%;
    padding: 12px;
    margin-top: var(--mod-sp-sm);
    border: 2px solid var(--mod-charcoal);
    border-radius: 4px;
    background: transparent;
    color: var(--mod-charcoal);
    font-family: var(--mod-font-body);
    font-size: 0.8125rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    cursor: pointer;
}

.tpl-modern .ada-reset:hover {
    background: var(--mod-charcoal);
    color: #fff;
}

/* ── ADA feature classes, applied to <body> ──────────────
   Scoped to body (not .tpl-modern) because the toggles set them
   on the body element itself. */
body.ada-grayscale .lp-content,
body.ada-grayscale .lp-header,
body.ada-grayscale .lp-footer {
    filter: grayscale(100%);
}

body.ada-invert .lp-content,
body.ada-invert .lp-header,
body.ada-invert .lp-footer {
    filter: invert(100%) hue-rotate(180deg);
}

body.ada-highlight-links a {
    text-decoration: underline !important;
    outline: 2px solid #ffd400 !important;
    outline-offset: 2px;
    background: rgba(255, 212, 0, 0.18) !important;
}

body.ada-readable-font,
body.ada-readable-font h1,
body.ada-readable-font h2,
body.ada-readable-font h3,
body.ada-readable-font h4,
body.ada-readable-font p,
body.ada-readable-font a,
body.ada-readable-font span,
body.ada-readable-font li,
body.ada-readable-font button {
    font-family: 'Comic Sans MS', 'Trebuchet MS', Verdana, sans-serif !important;
    letter-spacing: 0.02em;
}

body.ada-large-cursor,
body.ada-large-cursor * {
    cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 48 48'%3E%3Cpath d='M6 2 L6 38 L15 29 L21 43 L28 40 L22 26 L34 26 Z' fill='%23fff' stroke='%23000' stroke-width='2'/%3E%3C/svg%3E") 4 2, auto !important;
}

body.ada-focus-indicators *:focus {
    outline: 3px solid #2563eb !important;
    outline-offset: 2px !important;
}

body.ada-pause-animations *,
body.ada-pause-animations *::before,
body.ada-pause-animations *::after {
    animation-play-state: paused !important;
    transition: none !important;
    scroll-behavior: auto !important;
}

/* ═══ Modern Responsive ═════════════════════════════════════
   Project's 3-breakpoint system: desktop 1024+, tablet 600-1023,
   mobile <600. The nav collapses at 900px rather than 1024 —
   with five league nav items plus a CTA it runs out of room
   before the tablet boundary.
   ═══════════════════════════════════════════════════════════ */

/* ── Mobile header + dropdown menu (<=900px) ─────────────
   Ported from the Luke & Spencer header: brand on the left; an accessibility
   icon + hamburger on the right; the Register CTA and the nav links live in a
   full-width dropdown that accordions down from under the header. The desktop
   Register button and the floating accessibility side-tab are both hidden.
   Breakpoint is 900px — the inline nav (brand + 5 links + Register) is cramped
   below that. */
@media (max-width: 900px) {
    /* Active Events button anchors the left; margin-right:auto pushes the
       accessibility + hamburger icons to the right edge. When the league hides
       Active Events the button isn't rendered, so the hamburger also carries a
       margin-left:auto — the icons stay flush right either way. */
    .tpl-modern .lp-header-cta {
        margin-right: auto;
    }
    .tpl-modern .lp-hamburger {
        margin-left: auto;
    }
    /* The outer .lp-header carries 24px padding AND the inner carries 24px, so
       the icons ended up double-inset 48px from the edge on a phone (floaty,
       not flush). Zero the outer padding so a single 24px inset (from the
       inner) governs — icons sit a clean thumb's-width from the edge. */
    .tpl-modern .lp-header {
        padding-left: 0;
        padding-right: 0;
    }

    /* The initial-letter placeholder (leagues with no logo image) fades in on
       scroll just like the image, so the top of the page stays clean. */
    .tpl-modern .lp-logo-placeholder {
        opacity: 0;
        max-width: 0;
        overflow: hidden;
        transition: opacity 0.4s ease, max-width 0.4s ease;
    }
    .tpl-modern .lp-header-scrolled .lp-logo-placeholder {
        opacity: 1;
        max-width: 40px;
    }

    /* Floating a11y side-tab off; header icons on. The Register button is now
       the left anchor of the header, so it stays visible at every width. */
    .tpl-modern .ada-widget {
        display: none;
    }
    .tpl-modern .lp-hamburger,
    .tpl-modern .lp-ada-mobile {
        display: flex;
    }

    /* The in-menu Register item (hidden on desktop, where the header button
       carries it) appears in the dropdown. */
    .tpl-modern .lp-nav .lp-mobile-only {
        display: block;
    }

    /* Full-width dropdown under the header (position:absolute against the
       relatively-positioned header-inner). max-height accordion, and
       justify-content:flex-start so the items sit at the TOP, not the bottom
       (the old drawer inherited the desktop justify-content:flex-end).
       Every property the vestigial shared `.lp-nav.open` overlay sets
       (display/position/top/bottom/background/padding/z-index) is set here
       too so nothing from that old classic-shell rule leaks in. */
    .tpl-modern .lp-nav {
        display: flex;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        bottom: auto;
        z-index: 60;
        flex-direction: column;
        align-items: stretch;
        justify-content: flex-start;
        gap: 0;
        margin: 0;
        padding: 0;
        background: var(--mod-header-scrolled);
        max-height: 0;
        overflow: hidden;
        /* Smooth decelerating drop. JS sets max-height to the exact content
           height so the ease maps to the real reveal (see the shell script). */
        transition: max-height 0.42s cubic-bezier(0.22, 1, 0.36, 1);
        box-shadow: 0 14px 26px rgba(0, 0, 0, 0.28);
    }

    /* 80vh is the no-JS fallback; JS overrides it with the exact height inline. */
    .tpl-modern .lp-nav.open {
        max-height: 80vh;
        overflow-y: auto;
        border-top: 1px solid rgba(255, 255, 255, 0.1);
    }

    /* Scoped `.tpl-modern .lp-nav .lp-nav-link` (0,3,0) so it out-orders the
       shared `.lp-nav.open .lp-nav-link` overlay rule of the same specificity. */
    .tpl-modern .lp-nav .lp-nav-link {
        padding: 16px 20px;
        text-align: center;
        font-size: 0.95rem;
        font-weight: 600;
        color: #fff;
        border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    }

    .tpl-modern .lp-nav .lp-nav-link::after {
        display: none;
    }

    /* Current page in the dropdown: accent text, a faint accent wash, and a
       left accent bar (inset shadow, so it doesn't shift the centred text). */
    .tpl-modern .lp-nav .lp-nav-link.lp-nav-active {
        color: var(--mod-accent);
        background: rgba(56, 189, 248, 0.10);
        background: color-mix(in srgb, var(--mod-accent) 11%, transparent);
        box-shadow: inset 3px 0 0 var(--mod-accent);
    }

    /* Register reads like the other links, with just a faint accent wash so it
       registers as the CTA without shouting — the toned-down "less bold blue"
       Robb asked for, matching L&S's subtly-tinted login link. */
    .tpl-modern .lp-nav .lp-nav-cta {
        background: rgba(56, 189, 248, 0.13); /* fallback for no color-mix */
        background: color-mix(in srgb, var(--mod-accent) 15%, transparent);
    }
    .tpl-modern .lp-nav .lp-nav-cta:hover {
        background: color-mix(in srgb, var(--mod-accent) 24%, transparent);
    }
}

/* Tablet */
@media (max-width: 1023px) {
    /* Tighter edge inset than the desktop full-width xl padding — the phone/
       tablet header would otherwise sit 48px off each edge. */
    .tpl-modern .lp-header-inner {
        padding: 0 var(--mod-sp-md);
    }

    .tpl-modern .pe-mod-section,
    .tpl-modern .pe-section {
        padding: var(--mod-sp-xl) var(--mod-sp-md);
    }

    .tpl-modern .lp-footer-grid {
        gap: 28px;
        padding: 40px 24px 24px;
    }
}

/* Mobile */
@media (max-width: 599px) {
    .tpl-modern {
        --mod-header-h: 64px;
    }

    /* The shared mobile rule pins .lp-header-inner to height:56px, but Modern's
       body offset + hero pull-up use --mod-header-h (64px). The 8px difference
       left a white strip under the header on sub-pages (no hero to cover it).
       Match the header to the offset and let its height, not padding, size it. */
    .tpl-modern .lp-header-inner {
        height: var(--mod-header-h);
        padding-top: 0;
        padding-bottom: 0;
    }

    .tpl-modern .pe-mod-hero {
        min-height: 46vh;
        /* Same flush-under-header treatment as desktop, tighter spacing. */
        padding: calc(var(--mod-header-h) + var(--mod-sp-md)) var(--mod-sp-sm) var(--mod-sp-xl);
    }

    .tpl-modern .pe-mod-hero-logo {
        max-width: 240px;
    }

    .tpl-modern .pe-mod-hero-slogan {
        font-size: 1rem;
        letter-spacing: 0.1em;
    }

    .tpl-modern .pe-mod-section,
    .tpl-modern .pe-section {
        padding: var(--mod-sp-lg) var(--mod-sp-sm);
    }

    .tpl-modern .pe-mod-section-title {
        letter-spacing: 0.08em;
    }

    .tpl-modern .pe-mod-carousel-viewport {
        gap: var(--mod-sp-md);
    }

    /* Arrows would overlap a single full-width card awkwardly on a phone;
       the dots and swipe carry navigation there. */
    .tpl-modern .pe-mod-carousel-arrow {
        display: none;
    }

    /* (no height override for .pe-mod-feature-image: the 16:9 frame scales) */

    .tpl-modern .lp-league-name {
        font-size: 0.75rem;
    }

    .tpl-modern .lp-footer-grid {
        grid-template-columns: minmax(0, 1fr);
        gap: 24px;
        padding: 32px 20px 20px;
    }

    .tpl-modern .lp-footer-bottom {
        flex-direction: column;
        gap: 6px;
    }

    /* Accessibility on phones lives in the header (see the <=900 block, which
       hides .ada-widget); no floating side tab down here. */
}

/* ── Footer on mobile (<900): Location beside Contact ────
   The default modern footer grid auto-fits and collapses to a single column on
   phones, stacking every block. Below 900, force two columns with the brand and
   quick-links spanning full width, so Location and Contact pair up on one row.
   End-of-file so it wins source order over the auto-fit grid at overlapping
   widths. */
@media (max-width: 900px) {
    .tpl-modern .lp-footer-grid {
        grid-template-columns: 1fr 1fr;
        gap: 30px 24px;
    }
    .tpl-modern .lp-footer-brand,
    .tpl-modern .lp-footer-quick {
        grid-column: 1 / -1;
    }
    /* Long emails/addresses wrap instead of overflowing a narrow column. */
    .tpl-modern .lp-footer-col p,
    .tpl-modern .lp-footer-col address,
    .tpl-modern .lp-footer-col a {
        overflow-wrap: break-word;
    }
}

/* ── Accessibility statement modal (footer link) ─────────
   Native <dialog>: a centered white card over a dimmed ::backdrop; Esc and
   backdrop-click close it. Holds the platform accessibility statement so the
   footer link opens it in place instead of navigating to /accessibility. */
.lp-ally-dialog {
    /* Explicit centering: the global `* { margin: 0 }` reset kills the UA
       dialog:modal `margin: auto`, which would otherwise centre it — without
       this the modal pins to the top-left. fit-content height (capped) so it
       centres vertically too; the body scrolls if the statement is long. */
    position: fixed;
    inset: 0;
    margin: auto;
    height: fit-content;
    max-height: 85vh;
    max-width: 640px;
    width: calc(100% - 32px);
    border: none;
    border-radius: 14px;
    padding: 0;
    color: #1a2233;
    background: #fff;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35);
}
.lp-ally-dialog::backdrop {
    background: rgba(0, 0, 0, 0.55);
}
/* ── Service detail: a DRAWER, not a centered card (Robb, 2026-09-22) ────────
   The third public overlay, converted after the event and calendar ones. It
   holds more than either of them — a 16:9 image, a rich-text description, a
   FULL rich-text detail_html, the linked open/upcoming registrations, and a
   CTA — and it was already scrolling internally at `max-height: 70vh`, which
   is the symptom the drawers exist to remove.

   It stays a native <dialog>. The .pcm-* overlays are plain divs with an Esc
   listener bolted on; <dialog> + showModal() gives focus trapping, an inert
   background and ::backdrop for free, so converting this one to match their
   MARKUP would trade accessibility for uniformity. The geometry is shared
   instead — same 600px split, same keyframe names as pcmStyles().

   These rules used to be INLINE on the dialog element, which put a copy in
   the markup of every service on the page and made them impossible to
   override from a stylesheet. Anything positional belongs here now. */
.lp-svc-modal {
    position: fixed;
    inset: 0;
    /* ⚠ REQUIRED, and only since the panel can be non-modal (2026-10-18).
       showModal() puts a <dialog> in the TOP LAYER, which paints above
       everything and ignores z-index — so this rule was unnecessary while
       the panel was always modal. show() does NOT: the dialog becomes an
       ordinary fixed element at z-index auto, and .lp-header (z-index 100)
       painted straight over the panel header, hiding the title and BOTH
       controls. Robb: "when it is smaller, there is no way to make it
       bigger or close it out." 9999 matches .pcm-overlay and .rmd-overlay. */
    z-index: 9999;
    border: 0;
    padding: 0;
    background: #fff;
    overflow: hidden;
    box-shadow: 0 24px 70px rgba(15, 23, 42, 0.32);
}

/* The scrolling half. A drawer is full height, so the body takes the space the
   image and heading do not, rather than being capped at a fraction of the
   viewport the way a centered card had to be. */
/* ⚠ overscroll-behavior: reaching the END of the writeup must not carry on
   into the page behind. Wide is masked by the body lock, but a NARROWED
   panel deliberately leaves the page scrollable — without this, hitting the
   bottom starts moving the page under you. */
.lp-svc-modal-body {
    padding: 24px 28px 28px;
    overflow-y: auto;
    overscroll-behavior: contain;
    flex: 1 1 auto;
    min-height: 0;
}

/* The header bar. A flex item of the dialog, NOT of the scrolling body, so it
   holds its place while the writeup moves under it. `flex: 0 0 auto` because a
   column flex container shrinks its children by default and a long title would
   otherwise squeeze the bar instead of wrapping. */
.lp-svc-modal-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    flex: 0 0 auto;
    padding: 14px 16px 14px 24px;
    background: #fff;
    border-bottom: 1px solid #e8ecf3;
}

.lp-svc-modal-title {
    margin: 0;
    min-width: 0;
    font-size: 1.05rem;
    font-weight: 800;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #0f172a;
}

/* A short rule under the title, the width of a word — the Luke & Spencer
   panels' one piece of ornament, and it does real work: it tells the eye where
   the heading ends when the title is long enough to wrap. */
.lp-svc-modal-title .lp-svc-rule {
    display: block;
    width: 54px;
    height: 3px;
    margin-top: 9px;
    border-radius: 2px;
    background: var(--league-secondary, #38bdf8);
}

.lp-svc-modal-tools { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

/* Grey chips, 44px (the phone minimum), radius 12 — ported from L&S. */
.lp-svc-modal-btn {
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 12px;
    background: #f5f5f5;
    color: #666;
    cursor: pointer;
    padding: 0;
    transition: background 0.2s ease, color 0.2s ease;
}
.lp-svc-modal-btn svg { width: 18px; height: 18px; }
/* NOTHING lifts on hover — background and colour only. */
.lp-svc-modal-btn:hover { background: #e0e0e0; color: #333; }
.lp-svc-modal-btn:focus-visible { outline: 2px solid var(--league-secondary, #38bdf8); outline-offset: 2px; }

/* One button, two glyphs. The panel opens WIDE, so the glyph it opens showing
   is "contract" — which is also what tells you the wide state is the default
   rather than something you did. */
.pe-dico-out { display: none; }
.is-narrow .pe-dico-in { display: none; }
.is-narrow .pe-dico-out { display: block; }

/* ── The mobile grab bar ──────────────────────────────────────────────────
   Only meaningful on the bottom sheet, so it is hidden on the desktop drawer
   where there is nothing to drag. Decorative: the close button and Escape stay
   the accessible ways out, so it is aria-hidden in the markup. */
.lp-svc-modal-handle { display: none; }
@media (max-width: 599px) {
    .lp-svc-modal-handle {
        display: block;
        flex: 0 0 auto;
        padding: 10px 0 4px;
        background: #fff;
        cursor: grab;
        /* Take the vertical gesture over; without this the browser scrolls the
           page instead of letting the sheet follow the finger. */
        touch-action: none;
    }
    .lp-svc-modal-handle::before {
        content: '';
        display: block;
        width: 40px; height: 4px; margin: 0 auto;
        border-radius: 999px; background: #d2d2d2;
    }
    .lp-svc-modal-handle:active { cursor: grabbing; }
    /* While a finger is down the sheet tracks it exactly, so the easing that
       animates open and close is suspended. */
    .lp-svc-modal.is-dragging { transition: none; animation: none; }
}

.lp-svc-modal-img {
    width: 100%;
    /* ⚠ NO max-height here. A numeric cap beside an aspect-ratio WINS for the
       used height, and object-fit:cover then crops the difference out of every
       image of that ratio. `max-height:240px` made the box 560x240 (2.333)
       while the source is 16:9 (1.778): measured 38px off the TOP and 38 off
       the bottom of every banner, which is where the wordmark sits. Robb
       reported it twice. The ratio alone gives 560x315 — exactly the source,
       nothing lost. The cap was saving 75px of a 900px panel and the body
       scrolls either way. Keep the ratio: it still bounds a freak portrait
       upload. bin/test-public-drawers.php sweeps for the combination. */
    aspect-ratio: 16 / 9;
    object-fit: cover;
    display: block;
    flex: 0 0 auto;
    /* Air between the picture and the first line of the write-up (Robb,
       2026-10-24: "add some whitespace below the image and the content"). */
    margin-bottom: 22px;
}

.lp-svc-modal-cta { margin-top: 20px; text-align: center; }

.lp-svc-modal-cta > a {
    display: inline-block;
    padding: 11px 22px;
    border-radius: 8px;
    background: var(--league-secondary, #38bdf8);
    color: #fff;
    text-decoration: none;
    font-weight: 700;
}

/* ── The scrim: a real ELEMENT beside the dialog (2026-10-19) ─────────────
   The panel opens with show(), never showModal(), so it has no ::backdrop.
   The first stand-in was a 100vmax spread box-shadow on the panel — a dim
   that swallowed NOTHING: a click on the dimmed page landed on the page
   (measured on dev: a click on the hero button under the dim closed the
   drawer AND navigated), it could not fade (a transition does not run out
   of display:none), and the phone sheet, whose shadow rule sat in the
   >=600px block, had no dim at all.

   So service-drawer.js builds one fixed div per page and mirrors the open
   dialog's `is-narrow` / `is-closing` onto it: the same shape the four
   .pcm-* / .rmd-* overlays use. It sits one step UNDER the dialog's 9999 —
   above the page, below the panel. Keyframes, not a transition, because
   `hidden` cannot be transitioned out of. */
.lp-svc-scrim {
    position: fixed;
    inset: 0;
    z-index: 9998;
    background: rgba(0, 0, 0, 0.55);
    animation: peScrimIn 0.26s ease;
    transition: background .32s cubic-bezier(.65, 0, .35, 1);
}
.lp-svc-scrim[hidden] { display: none; }
.lp-svc-scrim.is-closing { animation: peScrimOut 0.26s ease forwards; pointer-events: none; }
@keyframes peScrimIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes peScrimOut { from { opacity: 1; } to { opacity: 0; } }
/* A NARROWED panel hands the page back: the scrim clears over the same
   0.32s the width takes, and stops intercepting the pointer. Drawer only —
   a phone sheet has no narrow state and keeps its dim, so a tap above the
   sheet closes it the way the .pcm-* sheets do. */
@media (min-width: 600px) {
    .lp-svc-scrim.is-narrow { background: rgba(0, 0, 0, 0); pointer-events: none; }
}

/* ── A service's division chips ───────────────────────────────────────────
   Were inline-styled, which cost the same two things the Register rows below
   them did: a hardcoded colour that could not know which skin it landed on,
   and no way for the phone block to give them a 44px hit area. */
.lp-svc-divs { margin-top: 20px; }
.lp-svc-divs-h {
    font-size: .8rem; text-transform: uppercase; letter-spacing: .06em;
    margin: 0 0 8px; color: #64748b;
}
.lp-svc-divchip {
    display: inline-block; padding: 5px 12px; border-radius: 999px;
    font-size: .85rem; font-weight: 600; text-decoration: none;
    margin: 0 6px 6px 0;
    background: #f1f5f9; color: #0f172a;
}
/* NOTHING lifts on hover — background only. */
.lp-svc-divchip:hover { background: #e2e8f0; }
@media (max-width: 599px) {
    /* 44px is the floor for anything you tap. The chip is a word on a line,
       so the PADDING grows with it rather than a min-height leaving the text
       floating at the top of a taller pill. */
    .lp-svc-divchip { padding: 11px 14px; }
}
/* ── A service drawer's linked registrations ──────────────────────────────
   These rows were built with inline styles, which cost two things. The event
   name carried a hardcoded `color:#0f172a` — near-black, and the Bold panel is
   #16181f, so on that skin the name was invisible. And an inline style cannot
   carry a `:hover`, so the Register button had no feedback at all.

   The "Opens <date>" cell is a LINK now (Robb, 2026-10-18), to the event's own
   drawer on the Registrations page. It is deliberately the quieter sibling of
   Register — outline rather than filled — because it does not register you, it
   takes you to the listing where the details and the Remind me button are. */
.lp-svc-reg { margin-top: 22px; }
.lp-svc-reg-h {
    font-size: .8rem; text-transform: uppercase; letter-spacing: .06em;
    margin: 0 0 4px;
}
.lp-svc-reg-row {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; padding: 12px 0;
}
.lp-svc-reg-row:last-child { border-bottom: none; }
.lp-svc-reg-name { font-weight: 600; min-width: 0; }
.lp-svc-reg-go, .lp-svc-reg-opens {
    display: inline-block; padding: 8px 16px; border-radius: 8px;
    font-weight: 700; font-size: .9rem; text-decoration: none;
    white-space: nowrap; border: 1px solid transparent;
}
.lp-svc-reg-go { background: var(--league-secondary, #38bdf8); color: #fff; }
/* NOTHING lifts on hover — brightness and background only. */
.lp-svc-reg-go:hover { filter: brightness(.94); }
.lp-svc-reg-opens {
    background: transparent;
    color: var(--league-secondary, #38bdf8);
    border-color: var(--league-secondary, #38bdf8);
}
.lp-svc-reg-opens:hover {
    background: color-mix(in srgb, var(--league-secondary, #38bdf8) 14%, transparent);
}
/* The fallback for an event that has no card on the Registrations page — a
   season's event lives under /divisions, so there is nowhere to send it. */
.lp-svc-reg-soon { font-weight: 600; font-size: .9rem; white-space: nowrap; }
/* The empty state of the Register block: no links, or nothing live yet. */
.lp-svc-reg-none { margin: 10px 0 0; font-size: .95rem; color: #64748b; }
.lp-svc-reg-h { color: #64748b; }
.lp-svc-reg-row { border-bottom: 1px solid #eef2f7; }
.lp-svc-reg-name { color: #0f172a; }
.lp-svc-reg-soon { color: #94a3b8; }

/* Drawer, >=600px. `margin: 0 0 0 auto` is what pins a <dialog> to the right
   edge — it has no overlay to justify-content against. */
@media (min-width: 600px) {
    .lp-svc-modal[open] {
        display: flex;
        flex-direction: column;
        margin: 0 0 0 auto;
        /* ⚠ THE SAME TOKEN THE OTHER FOUR DRAWERS USE. This panel was 900
           while they were 680, which side by side reads as two different
           components rather than one with more in it. */
        width: min(var(--pe-drawer-w, 680px), 94vw);
        max-width: min(var(--pe-drawer-w, 680px), 94vw);
        height: 100%;
        max-height: 100%;
        border-radius: 0;
        /* The accent edge: the only thing separating a white panel from a white
           page, and it carries the league's colour rather than a grey hairline.
           5px, from L&S — at 4 it reads as a border, at 5 as a deliberate mark. */
        border-left: 5px solid var(--league-secondary, #38bdf8);
        /* Its own compositor layer while it slides, as L&S does. */
        will-change: transform;
        animation: pcmDrawerIn 0.38s cubic-bezier(0.32, 0.72, 0, 1);
        /* ⚠ RESIZING IS A TRANSITION, NOT A JUMP (Robb, 2026-10-18: "I want a
           smooth transition"). Expand/contract only swaps `is-narrow`, so
           without this the panel teleports between 680 and 520 in one frame.
           Same easing and duration as the admin drawers in app.css. */
        transition: width .32s cubic-bezier(.65, 0, .35, 1), max-width .32s cubic-bezier(.65, 0, .35, 1);
    }
    /**
     * ⚠ WIDE IS THE DEFAULT. The panel holds a league's full writeup, and the
     * question "how wide should it be" had already been answered wrong twice
     * before the control existed. Opening wide answers it the way the content
     * wants and leaves narrowing to the reader.
     *
     * `is-narrow` is the opt-out, not `is-wide` the opt-in — which is also why
     * the button opens showing the CONTRACT glyph.
     */
    .lp-svc-modal[open].is-narrow {
        width: min(var(--pe-drawer-w-narrow, 520px), 94vw);
        max-width: min(var(--pe-drawer-w-narrow, 520px), 94vw);
    }

    /* The panel's own shadow and nothing more. The dim is `.lp-svc-scrim`,
       a real element (see that block) — it used to be a 100vmax spread on
       this shadow, which dimmed the page without ever standing between the
       pointer and it. */
    .lp-svc-modal[open] { box-shadow: -18px 0 48px rgba(0, 0, 0, .28); }
    /* The slide-in is an entrance; re-running it on a width change would make
       the panel fly out and back when the button is pressed. */
    .lp-svc-modal[open].no-anim { animation: none; }
    /* Width IS transitioned now — see the block above. Reflowing the text for
       0.32s on one button press is the cost, and it is the thing that reads as
       smooth; a jump reads as the panel being replaced. */
    .lp-svc-modal.is-closing {
        animation: pcmDrawerOut 0.26s cubic-bezier(0.32, 0.72, 0, 1) forwards;
        pointer-events: none;
    }
}

/* Bottom sheet, <600px — what a phone gets, same as the other two overlays. */
@media (max-width: 599px) {
    .lp-svc-modal[open] {
        display: flex;
        flex-direction: column;
        margin: auto 0 0;
        width: 100%;
        max-width: 100%;
        max-height: 92vh;
        max-height: 92dvh;
        border-radius: 20px 20px 0 0;
        animation: pcmSheetUp 0.35s cubic-bezier(0.32, 0.72, 0, 1);
    }
    /* No grab handle here: the rounded top corners and the rise from the bottom
       carry the affordance. */
    .lp-svc-modal-body { padding: 18px 20px 24px; }
    .lp-svc-modal-head { padding: 10px 10px 10px 18px; }
    .lp-svc-modal-title { font-size: 0.95rem; }
    /* A bottom sheet is already the full width of the screen. */
    .lp-svc-modal-wide { display: none; }
}

/* pcmStyles() also declares these, but that block is only emitted on pages
   carrying a .pcm-* overlay and the services page is not one of them. Naming
   them the same keeps one vocabulary; duplicate @keyframes of identical name
   and content are harmless. */
@keyframes pcmDrawerIn  { from { transform: translateX(100%); } to { transform: none; } }
@keyframes pcmDrawerOut { from { transform: none; } to { transform: translateX(100%); } }
@keyframes pcmSheetUp   { from { transform: translateY(100%); } to { transform: none; } }

@media (prefers-reduced-motion: reduce) {
    .lp-svc-modal[open], .lp-svc-modal.is-closing { animation: none; transition: none; }
    .lp-svc-scrim { animation: none; transition: none; }
}
.lp-ally-dialog-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 18px 24px;
    border-bottom: 1px solid #eef1f5;
    position: sticky;
    top: 0;
    background: #fff;
    border-radius: 14px 14px 0 0;
}
.lp-ally-dialog-head h2 {
    font-family: var(--mod-font-body, 'DM Sans', sans-serif);
    font-size: 1.15rem;
    font-weight: 700;
    margin: 0;
    color: #16233f;
}
.lp-ally-dialog-close {
    background: none;
    border: none;
    font-size: 30px;
    line-height: 1;
    cursor: pointer;
    color: #64748b;
    padding: 0 4px;
}
.lp-ally-dialog-close:hover {
    color: #16233f;
}
.lp-ally-dialog-body {
    padding: 20px 24px 28px;
    max-height: 70vh;
    overflow-y: auto;
    line-height: 1.65;
}
.lp-ally-dialog-body h2 {
    font-size: 1rem;
    font-weight: 700;
    margin: 20px 0 8px;
    color: #16233f;
}
.lp-ally-dialog-body p {
    margin: 0 0 12px;
    font-size: 0.95rem;
}
.lp-ally-dialog-body ul {
    margin: 0 0 14px;
    padding-left: 22px;
}
.lp-ally-dialog-body li {
    font-size: 0.95rem;
    margin-bottom: 6px;
}
.lp-ally-dialog-body a {
    color: #0369a1;
}

/* Tournament card/badge artwork (mig 206). The Modern registration card is a
   horizontal row, so the art is a fixed-width thumbnail on the left rather
   than a banner — and it collapses out of the way on a phone, where the row
   itself stacks. */
.lp-reg-card-art { width: 132px; flex-shrink: 0; aspect-ratio: 16 / 9; border-radius: 10px; overflow: hidden; background: #f1f5f9; }
.lp-reg-card-art img { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (max-width: 640px) { .lp-reg-card-art { width: 100%; } }

/* ── Lists inside sanitized rich text, wherever it is rendered (2026-09-10) ──
   The global reset zeroes list padding, and only .lp-richtext restored it — so a
   numbered list in a service modal, an about card or a document ran flush left with
   no indent. Every rich-text wrapper gets the same treatment here. Unscoped on
   purpose: the Pro skins load on top of this sheet and inherit it. */
.lp-svc-modal-rich ul, .lp-svc-modal-rich ol,
.pe-richtext ul, .pe-richtext ol,
.lp-doc-rule-body ul, .lp-doc-rule-body ol,
.lp-about-content ul, .lp-about-content ol,
.lp-ally-dialog-body ul, .lp-ally-dialog-body ol { margin: 0.5em 0 1.1em; padding-left: 1.6em; list-style-position: outside; }
.lp-svc-modal-rich ul, .pe-richtext ul, .lp-doc-rule-body ul, .lp-about-content ul, .lp-ally-dialog-body ul { list-style-type: disc; }
.lp-svc-modal-rich ol, .pe-richtext ol, .lp-doc-rule-body ol, .lp-about-content ol, .lp-ally-dialog-body ol { list-style-type: decimal; }
.lp-svc-modal-rich li, .pe-richtext li, .lp-doc-rule-body li, .lp-about-content li, .lp-ally-dialog-body li { margin-bottom: 0.35em; padding-left: 0.15em; line-height: 1.55; }
.lp-svc-modal-rich p, .pe-richtext p { margin: 0 0 0.9em; }
.lp-svc-modal-rich p:last-child, .pe-richtext p:last-child { margin-bottom: 0; }

/* ── Where we play ────────────────────────────────────────────────
   One card per VENUE with its areas listed inside — a row per area
   would print the same address three times. Shared by the division
   and season pages in BOTH templates, so the two skins cannot drift
   into different layouts for the same facts (2026-09-20).
   minmax(0, 1fr): a long address must not push the grid wider than
   the page. */
.lp-venue-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 14px; }
.lp-venue-card { min-width: 0; }
@media (max-width: 599px) { .lp-venue-grid { grid-template-columns: 1fr; } }
/* The same cards, above the season calendar, as its venue legend (2026-09-21). */
.lp-venue-legend { margin: 0 0 20px; }

/* ── Season card: name in the header, three facts in the body ─────
   Robb, 2026-09-20. The header used to carry the DIVISION name, which
   on a division page repeats the heading you just read; it now carries
   the season's own name on the left and its status on the right. The
   body became three labelled rows (grade / season / registration) and
   the cards grew to hold them.

   These sit BELOW the .tpl-modern block earlier in this file on
   purpose: same specificity, later wins, so the Modern skin picks them
   up without a !important. */
.lp-season-card .pe-event-card-header,
.tpl-modern .lp-season-card .pe-event-card-header {
    padding: 15px 20px;
    gap: 12px;
    align-items: flex-start;
    justify-content: space-between;
}
/* The season name. Wraps to two lines rather than truncating — a name
   like "BOYS: 27-SPRING-BASKETBALL" is the whole point of the header. */
.pe-event-cardname {
    flex: 1 1 auto;
    min-width: 0;
    font-family: var(--lp-font-heading);
    font-size: 17px;
    font-weight: 700;
    line-height: 1.3;
    /* Was #fff, for a header band that ONLY .tpl-modern ever painted
       (league-page.css:3764). On Default / Left Menu / Right Menu the
       header background was transparent over a white card, so the season
       name rendered white on white and was invisible. The rail card below
       drops the band on every template, so dark is right everywhere now.
       Only renderSeasonCard emits this class - both of its cards carry
       .lp-scard - so nothing else depended on the white. */
    color: #1a1a2e;
    overflow-wrap: anywhere;
}
.tpl-modern .lp-season-card .pe-event-badge { margin-left: 0; flex: none; }

.lp-season-card .pe-event-card-body {
    padding: 18px 20px 20px;
    gap: 0;
}
/* Label left, value right, on one line each. min-width:0 on the value so
   a long date range wraps instead of widening the card. */
.pe-card-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 14px;
    padding: 9px 0;
    border-bottom: 1px solid #eef1f5;
    font-size: 14px;
}
.pe-card-row:first-child { padding-top: 0; }
.pe-card-row-k { flex: none; color: #64748b; }
.pe-card-row-v { min-width: 0; text-align: right; font-weight: 600; color: #1a1a2e; }
.lp-season-card .pe-card-cta {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;   /* the house touch floor; the whole card is the link, but this reads as a button */
    margin-top: 16px;
    padding: 10px 16px;
    border: 1px solid #cbd5e1;
    border-radius: 10px;
    text-align: center;
    font-size: 14px;
    font-weight: 700;
    color: #1a1a2e;
    text-transform: none;
    letter-spacing: 0;
}
.lp-season-card:hover .pe-card-cta { border-color: var(--league-primary, #1a1a2e); }
.lp-season-card .pe-card-cta.is-muted { color: #64748b; }
@media (max-width: 599px) {
    /* Stack the row so a long value keeps the full width on a phone. */
    .pe-card-row { flex-direction: column; align-items: flex-start; gap: 2px; }
    .pe-card-row-v { text-align: left; }
}

/* Wider season cards (Robb, 2026-09-20). The grid was a fixed 3-up, which at
   ~1100px gave 337px cards — too narrow now they carry four labelled rows, so
   "Registration" and its date range fought for the same line.
   :has() scopes this to grids holding SEASON cards, leaving the event grids
   (which still carry short cards) at three across. A browser without :has()
   keeps today's 3-up, which is a fair degradation rather than a break. */
.pe-event-grid:has(.lp-season-card) {
    grid-template-columns: repeat(auto-fill, minmax(420px, 1fr));
}
@media (max-width: 899px) {
    .pe-event-grid:has(.lp-season-card) { grid-template-columns: 1fr; }
}

/* ── Season sub-page tabs ────────────────────────────────────────────────
   The season split into Overview / Teams / Schedule / Results / Standings /
   Documents (2026-09-20). These are LINKS, not JS tabs — each sub-page is a
   real URL — so the bar is styled, not scripted.

   It scrolls sideways rather than wrapping: six tabs wrap to two ragged rows
   at ~380px, and a wrapped tab bar reads as two separate navs. */
/* NOT sticky. .lp-header is already sticky at top:0 / z-index:100, so a sticky
   tab bar slides underneath it and vanishes. Pinning it below the header would
   mean hardcoding a height that changes at two breakpoints and again in the
   Modern scrolled state, so the bar just scrolls with the page. */
.lp-stabs-bar {
    border-bottom: 1px solid #e5e7eb;
    background: #fff;
}
.lp-stabs {
    display: flex;
    gap: 2px;
    /* Centred, but SAFE-centred: when six tabs overflow a phone, plain
       `center` pushes the first one off the left edge into space the
       scroller cannot reach. `safe center` falls back to flex-start the
       moment it overflows. A browser without the keyword drops the whole
       declaration and gets flex-start too, which is what this was. */
    justify-content: safe center;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}
.lp-stabs::-webkit-scrollbar { display: none; }
.lp-stab {
    flex: none;
    padding: 14px 14px 12px;
    font-size: 15px;
    font-weight: 600;
    color: #6b7280;
    text-decoration: none;
    border-bottom: 2px solid transparent;
    white-space: nowrap;
    transition: color .15s, border-color .15s;
}
.lp-stab:hover { color: #111827; }
.lp-stab.is-on {
    color: #111827;
    border-bottom-color: var(--league-primary, #2563eb);
}
/* Bold is a separate shell loading league-v2.css ONLY — its copy of these
   rules lives there, not here. */

/* A results row is a three-column line whose ends can be long team names;
   min-width:0 keeps a long name from pushing the page wider than the phone
   (the grid-card rule, same cause). */
.lp-results { margin-top: 4px; }
.lp-result-row > div > span { min-width: 0; overflow-wrap: anywhere; }
@media (max-width: 599px) {
    .lp-stab { font-size: 16px; padding: 13px 12px 11px; }
}

/* ── Season / division ROWS ──────────────────────────────────────────────
   Robb, 2026-09-21. Cards made near-identical siblings hard to compare: three
   Jr. Jazz seasons differing only in grade meant re-reading four labelled rows
   to find the one field that moved. As rows in a column, the difference is the
   only thing that moves.

   Anatomy: colour rail | name over meta | status cell | price | CTA.
   The rail is the DIVISION's hue (assigned by id, see DIVISION_HUES) so every
   season under one division reads as a set before a word is read. */
.lp-srows {
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    overflow: hidden;
    background: #fff;
}
.lp-srow {
    display: grid;
    grid-template-columns: 6px minmax(0, 1fr) auto auto auto auto;
    align-items: center;
    gap: 14px;
    padding: 14px 16px;
    border-bottom: 1px solid #f1f3f5;
}
.lp-srow:last-child { border-bottom: 0; }
/* No lift — shading only. See bin/test-no-hover-lift.php. */
.lp-srow:hover { background: #fafbfc; }

.lp-srow-rail { width: 6px; height: 34px; border-radius: 3px; }

.lp-srow-main { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.lp-srow-name {
    font-size: 16px;
    font-weight: 700;
    color: #111827;
    text-decoration: none;
    overflow-wrap: anywhere;
}
.lp-srow-name:hover { text-decoration: underline; }
.lp-srow-meta { font-size: 13px; color: #6b7280; }

/* The status CELL — solid, not an outline pill. Colour is the whole signal, so
   only `open` and `soon` carry one and they are mutually exclusive. */
.lp-srow-status {
    flex: none;
    font-size: 12px;
    font-weight: 600;
    padding: 6px 10px;
    border-radius: 6px;
    white-space: nowrap;
    text-align: center;
}
.lp-tone-open  { background: #15803d; color: #fff; }
.lp-tone-soon  { background: #fef3c7; color: #92400e; }
.lp-tone-quiet { background: #f3f4f6; color: #4b5563; }

.lp-srow-price { font-size: 15px; font-weight: 700; color: #111827; white-space: nowrap; text-align: right; }
.lp-srow-price.is-empty { min-width: 1px; }
.lp-srow-from { font-size: 12px; font-weight: 500; color: #6b7280; }

.lp-srow-cta {
    flex: none;
    display: inline-block;
    background: #2563eb;
    color: #fff;
    font-size: 14px;
    font-weight: 600;
    padding: 9px 18px;
    border-radius: 8px;
    text-decoration: none;
    white-space: nowrap;
}
.lp-srow-cta:hover { background: #1d4ed8; text-decoration: none; }
/* Sign up + View Season share the CTA cell, so the row keeps its six columns
   instead of growing a seventh (2026-09-21). */
.lp-srow-actions { display: inline-flex; align-items: center; gap: 8px; }
/* Secondary: same shape as .lp-srow-cta so the pair reads as one control,
   outlined so the primary action still wins the eye. No lift on hover. */
.lp-srow-view {
    flex: none;
    display: inline-block;
    background: #fff;
    color: #1f2937;
    font-size: 14px;
    font-weight: 600;
    padding: 8px 16px;
    border: 1px solid #d1d5db;
    border-radius: 8px;
    text-decoration: none;
    white-space: nowrap;
}
.lp-srow-view:hover { border-color: #9ca3af; background: #f9fafb; text-decoration: none; }
.lp-srow-link {
    flex: none;
    color: #2563eb;
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    padding: 9px 4px;
}
.lp-srow-link:hover { text-decoration: underline; }

/* Phone: the actions drop to a row of THEIR OWN and the content gets the full
   width (Robb, 2026-09-21, option A — "These don't look good on a mobile
   device").
 *
 * The previous layout kept a CTA column beside the content, and at 375px there
 * is no room for both. Two things broke, and they were the same bug:
 *
 *  - The price DISAPPEARED under the button. Columns were
 *    `6px auto minmax(0,1fr) auto` — the status pill took what it needed, the
 *    button took what it needed, and the price was left the 1fr, which
 *    collapses toward zero. `.lp-srow-price` is `white-space: nowrap`, so
 *    rather than wrap it overflowed its track and ran beneath the CTA. On the
 *    divisions page "from $50" sat behind "Registrations →".
 *  - Names and dates WRAPPED mid-phrase ("Youth Flag / Football", "Jr. Jazz – /
 *    Kindergarten"), because the stacked Sign up + View Season pair sized the
 *    CTA column to the wider of the two and ate ~140px of a 375px screen.
 *
 * Giving the actions their own row costs about 40px of height on a page that
 * is already a scrolling list, and buys back the whole width for the name.
 * The nowrap on the price is left alone deliberately — "from $50" must never
 * break across lines; what was wrong was the track, not the nowrap. */
@media (max-width: 720px) {
    .lp-srow {
        grid-template-columns: 6px auto minmax(0, 1fr);
        grid-template-areas:
            "rail main   main"
            "rail status price"
            "rail batt   batt"
            "rail cta    cta";
        row-gap: 8px;
        column-gap: 12px;
    }
    .lp-srow-rail   { grid-area: rail; height: 100%; min-height: 44px; }
    .lp-srow-main   { grid-area: main; }
    .lp-srow-status { grid-area: status; justify-self: start; }
    .lp-srow-price  { grid-area: price; justify-self: start; text-align: left; }
    /* A lone button owns the row's full width; the pair splits it evenly, so
       the two read as one control rather than two ragged stacked ones. */
    /* DIRECT children only. `.lp-srow-actions` is a grid now, and an
       unscoped `grid-area: cta` on the nested Sign up names an area that grid
       does not have — it lands in an implicit row of its own and the pair
       stops sitting side by side. */
    .lp-srow > .lp-srow-cta { grid-area: cta; justify-self: stretch; text-align: center; margin-top: 2px; }
    .lp-srow > .lp-srow-actions {
        grid-area: cta;
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 8px;
        margin-top: 2px;
    }
    .lp-srow-actions > a { text-align: center; }
    /* The text link is not a button and does not stretch to the full width —
       a full-bleed underlined word reads as a broken button. It keeps its own
       width and earns the 44px hit area from its own box. */
    .lp-srow > .lp-srow-link {
        grid-area: cta;
        justify-self: start;
        display: inline-flex;
        align-items: center;
        min-height: 44px;
    }
}
@media (max-width: 599px) {
    .lp-srow-name { font-size: 16px; }
    .lp-srow-meta { font-size: 12px; }
    .lp-srow-cta  { font-size: 16px; padding: 10px 16px; }
    .lp-srow-link { font-size: 16px; }
    /* 16px + a 44px hit area on a phone, per the mobile pass. */
    .lp-srow-view { font-size: 16px; padding: 10px 16px; }
}


/* ── Page header: outdented, with the search on the title's line ─────────
   Robb, 2026-09-21: "move all headers ... to offset a bit more to the left, so
   it's wider than the rest of the content", and the search "on the same line as
   division (the headers) — also make it offset but to the right".

   The outdent is a NEGATIVE MARGIN, not a wider max-width, so the content below
   keeps its own column and only the header hangs outside it.

   -24px is the safe baseline: .lp-page-section has 24px of side padding, so this
   pulls the header exactly to the section's edge and can never overflow. Wider
   viewports have real slack outside the 1140px column, so they hang further. */
/* ONE knob for how far the header furniture hangs outside the content column.
   -24px is the safe baseline (it equals the section padding, so it can never
   overflow); wider viewports have real slack outside the 1140px column; a
   phone gets none, because there the padding IS the gutter. */
:root { --lp-outdent: 24px; }
@media (min-width: 1280px) { :root { --lp-outdent: 52px; } }
@media (max-width: 720px)  { :root { --lp-outdent: 0px; } }

.lp-page-header-bar {
    margin-left: calc(var(--lp-outdent) * -1);
    margin-right: calc(var(--lp-outdent) * -1);
}

/* The BREADCRUMB hangs with the title it belongs to, never indented inside it
   (Robb, 2026-09-21: "more offset than the header - it looks weird like this").
   Two layouts carry a trail, and both are covered here so no page is left with
   a crumb at one edge and its h1 at another:
     - the division page, where the crumb is a direct child of .lp-page-section
     - the season and team pages, where it sits in the grey .lp-page-header band
       beside .lp-season-header / .lp-team-header */
.lp-page-section > .lp-breadcrumbs {
    margin-left: calc(var(--lp-outdent) * -1);
    margin-right: calc(var(--lp-outdent) * -1);
}
.lp-page-header .lp-breadcrumbs,
.lp-page-header .lp-season-header,
.lp-page-header .lp-team-header {
    margin-left: calc(var(--lp-outdent) * -1);
}

/* A header that carries a control: title left, control right, ONE line, with
   the subtitle underneath spanning the full width. */
.lp-header-search { text-align: left; }
.lp-header-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
    flex-wrap: wrap;
}
.lp-header-search .lp-page-title { margin: 0; }
.lp-header-search .lp-page-subtitle {
    margin: 12px 0 0;
    max-width: 70ch;
}

.lp-searchwrap {
    position: relative;
    flex: 0 1 320px;
    min-width: 220px;
}
.lp-search-ico {
    position: absolute;
    left: 12px;
    top: 50%;
    transform: translateY(-50%);
    width: 18px;
    height: 18px;
    color: #94a3b8;
    pointer-events: none;
}
.lp-search {
    width: 100%;
    /* 16px is the floor below 600px or iOS zooms on focus and stays zoomed; it
       is the size at every width here, so there is no mobile override to miss. */
    font: inherit;
    font-size: 16px;
    min-height: 44px;
    padding: 10px 14px 10px 38px;
    border: 1px solid #d5dbe3;
    border-radius: 10px;
    background: #fff;
    color: #111827;
}
.lp-search::placeholder { color: #9aa4b2; }
.lp-search:focus {
    outline: none;
    border-color: var(--league-primary, #2563eb);
    box-shadow: 0 0 0 3px rgba(37, 99, 235, .14);
}
.lp-search::-webkit-search-cancel-button { cursor: pointer; }

.lp-search-none {
    margin: 18px 0 0;
    color: #6b7280;
    font-size: 15px;
}

/* ── The list toolbar: Group by left, search right, ABOVE the list ───────
   Robb, 2026-10-22, choosing between four placements: the controls belong on
   the list they act on, not on the heading. Crowding them against the title
   was also what made them wrap into a stack on a laptop, and on a phone it
   pushed the subtitle — the one line that tells a first-time parent what the
   page is for — below two form controls.

   Reading order is now heading, what it is for, the tools, the list. The same
   markup stacks on a phone, so there is no second mobile treatment to keep in
   step. */
.lp-listtools {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin: 0 0 16px;
}
.lp-groupwrap { display: flex; align-items: center; gap: 8px; }
/* The calendar's division filter is the SAME control, one page over. Only its
   width differs: "All divisions" is short, and without a floor the field
   shrinks to the width of whatever is selected and jumps as you change it. */
.lp-cal-divselect { min-width: 210px; max-width: 100%; }
/* The native select chrome is the one thing on this page that still looks like
   1998 — a beveled OS arrow in a field that does not match the search box
   beside it. `appearance:none` drops the arrow and the bevel; the chevron comes
   back as a background SVG, which is the only way to draw one INSIDE a <select>
   (its options are rendered by the OS, so a pseudo-element or a sibling icon
   cannot sit over the box without eating the click).

   ⚠ Keep all three appearance properties. Safari needs `-webkit-`, and without
   it the arrow stays AND the background chevron is drawn beside it — two
   arrows, which looks worse than leaving it alone. */
.lp-group-select {
    font: inherit;
    /* Same 16px floor and 44px target as .lp-search — one control beside
       another, and iOS zooms on a focused select under 16px just as it does on
       an input. */
    font-size: 16px;
    font-weight: 600;
    min-height: 44px;
    /* Right padding clears the chevron; without it a long option name runs
       underneath it. */
    padding: 10px 38px 10px 14px;
    border: 1px solid #d8dee7;
    border-radius: 10px;
    background-color: #fff;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    background-size: 16px 16px;
    color: #111827;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    transition: border-color .12s, box-shadow .12s;
}
/* Shading and border only — nothing lifts on hover. */
.lp-group-select:hover { border-color: #aab6c6; }
.lp-group-select:focus-visible,
.lp-group-select:focus {
    outline: none;
    border-color: var(--league-primary, #2563eb);
    box-shadow: 0 0 0 3px rgba(37, 99, 235, .14);
}
/* Firefox paints a dotted inner ring on a focused select; the box-shadow above
   is already the focus signal. */
.lp-group-select::-moz-focus-inner { border: 0; }
.lp-group-select:-moz-focusring { color: transparent; text-shadow: 0 0 0 #111827; }

/* ── A group heading INSIDE the rows card ───────────────────────────────── */
.lp-rowgroup-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 16px 10px;
    border-bottom: 1px solid #f1f3f5;
    background: #f8fafc;
}
/* The first heading already has the card's own top edge above it. */
.lp-srows > .lp-rowgroup-head:first-child { border-top: 0; }
.lp-srows > .lp-rowgroup-head + .lp-rowgroup-head,
.lp-srows > .lp-srow + .lp-rowgroup-head { border-top: 1px solid #e5e7eb; }
.lp-rowgroup-name {
    font-size: 13px;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: #334155;
}
.lp-rowgroup-count {
    font-size: 12px;
    font-weight: 600;
    color: #64748b;
    background: #e8edf3;
    border-radius: 6px;
    padding: 1px 7px;
}

@media (max-width: 720px) {
    .lp-header-top { align-items: stretch; gap: 14px; }
    .lp-searchwrap { flex: 1 1 100%; }
    /* Two full-width rows rather than a wrapped pair: a wrapped flex row leaves
       the search at its 220px minimum against the left edge with dead space
       beside it, which reads as a broken column rather than a stack. */
    .lp-listtools { flex-direction: column; align-items: stretch; gap: 10px; }
    .lp-groupwrap { width: 100%; }
    .lp-group-select { flex: 1; min-width: 0; }
}

/* ── The spots battery ───────────────────────────────────────────────────
   Robb, 2026-09-21. A capacity meter on a row: a thin track with the places
   left underneath. Drawn ONLY when the season sets max_players — see
   renderSpotsBattery() for why an uncapped one gets nothing rather than an
   empty bar.

   Colour is urgency, not fullness: green while there is room, amber from 85%,
   grey once there is none. The label carries the same fact in words, so the
   bar is decoration for anyone who cannot use colour. */
.lp-batt {
    display: flex;
    flex-direction: column;
    gap: 5px;
    width: 118px;
    flex: none;
}
/* The empty span holds the grid cell so rows with and without a meter keep
   their columns aligned — a ragged column of prices reads as broken. */
.lp-batt.is-empty { width: 118px; }
.lp-batt-track {
    display: block;
    height: 6px;
    border-radius: 3px;
    background: #eef1f5;
    overflow: hidden;
}
.lp-batt-fill {
    display: block;
    height: 100%;
    border-radius: 3px;
}
.lp-batt-label {
    font-size: 12px;   /* the phone floor for meta text; no override needed */
    color: #6b7280;
    white-space: nowrap;
}
.lp-batt-room  .lp-batt-fill { background: #16a34a; }
.lp-batt-tight .lp-batt-fill { background: #f59e0b; }
.lp-batt-tight .lp-batt-label { color: #b45309; font-weight: 600; }
.lp-batt-full  .lp-batt-fill { background: #94a3b8; }
.lp-batt-full  .lp-batt-label { color: #64748b; font-weight: 600; }

@media (max-width: 720px) {
    .lp-batt { grid-area: batt; width: 100%; max-width: 240px; }
    /* Nothing to show and no column to hold on a phone — the rows are stacked. */
    .lp-batt.is-empty { display: none; }
}

/* `display: grid` on .lp-srow is an AUTHOR rule, and it beats the browser's own
   `[hidden] { display: none }` — so a filtered-out row stayed on screen while the
   script believed it had hidden it. The search then showed "Nothing matches"
   underneath a full list (Robb, 2026-09-21). Any future display rule on a row
   needs this line to stay below it. */
.lp-srow[hidden] { display: none; }

/* The spots meter inside the register card.
   Its bright palette used to be scoped to the navy banner; the card is white
   now, so the meter takes the ordinary light-surface .lp-batt rules -- with
   ONE exception that has to survive the move.

   AT CAPACITY IS NOT A DEAD END. A full season still takes waitlist sign-ups
   and the button beside it still works, so the default grey (#94a3b8) reads
   as closed and lies. Amber says notice. Deleting the banner override took
   this with it and a guard in test-season-subpages caught the regression. */
.lp-reg-split .lp-batt-full .lp-batt-fill  { background: #f59e0b; }
.lp-reg-split .lp-batt-full .lp-batt-label { color: #b45309; }

/* Registration held: the league has not finished connecting Stripe, so a PAID
   season's button becomes an inert label rather than a styled-down link — a
   link still navigates, and where it navigates to is a checkout that cannot
   charge. Sits on the dark register banner, so it is a light outline rather
   than a grey fill, which would vanish into it. */
.lp-reg-held {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, .10);
    border: 1px solid rgba(255, 255, 255, .34);
    color: #fff;
    cursor: default;
    pointer-events: none;
    white-space: nowrap;
}

/* ── Custom-page layout blocks (mig 281, phase 2 — 2026-09-21) ───────────
   The first custom-page blocks that need real CSS. Phase 1 got away with
   inline styles; a column that does not stack is a horizontal scrollbar on a
   phone, and a media query cannot be inlined.

   DEFINED IN BOTH STYLESHEETS ON PURPOSE. Bold loads league-v2.css alone, so
   a class written once renders unstyled in the other skin. Layout is identical
   in the two copies; only the colours differ. bin/test-custom-pages.php fails
   if they drift.

   The column count comes from data-cols, not an inline grid-template: an
   inline style would beat the media query and the columns would never stack. */
.lp-blk-cols { display: grid; gap: 22px; margin: 0 0 22px; }
.lp-blk-cols[data-cols="2"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.lp-blk-cols[data-cols="3"] { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.lp-blk-col { min-width: 0; }
.lp-blk-col > :first-child { margin-top: 0; }
.lp-blk-col > :last-child  { margin-bottom: 0; }

.lp-blk-acc { margin: 0 0 22px; }
.lp-blk-acc-item { border: 1px solid #e3e7ec; border-radius: 10px; background: #fff; margin-bottom: 8px; }
.lp-blk-acc-sum {
    cursor: pointer;
    list-style: none;
    padding: 13px 16px;
    font-size: 15px;
    font-weight: 700;
    color: #0b2545;
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 44px;
    box-sizing: border-box;
}
.lp-blk-acc-sum::-webkit-details-marker { display: none; }
/* Our own marker, rotated when open. Drawn with a border rather than a glyph
   so it cannot pick up a font that lacks it. */
.lp-blk-acc-sum::before {
    content: "";
    flex: none;
    width: 7px; height: 7px;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(-45deg);
    transition: transform .15s;
}
.lp-blk-acc-item[open] > .lp-blk-acc-sum::before { transform: rotate(45deg); }
/* No lift — shading only. See bin/test-no-hover-lift.php. */
.lp-blk-acc-sum:hover { background: #f8fafc; border-radius: 10px; }
.lp-blk-acc-body { padding: 0 16px 14px; }
.lp-blk-acc-body > :first-child { margin-top: 0; }

.lp-blk-check { margin: 0 0 22px; }
.lp-blk-check-list { list-style: none; padding: 0; margin: 0; display: grid; gap: 9px; }
.lp-blk-check-item { display: flex; align-items: flex-start; gap: 10px; font-size: 15px; line-height: 1.55; color: #334155; }
.lp-blk-check-ico { flex: none; margin-top: 3px; color: #16a34a; }

@media (max-width: 720px) {
    /* One column on a phone, whatever the block asked for. */
    .lp-blk-cols[data-cols="2"],
    .lp-blk-cols[data-cols="3"] { grid-template-columns: minmax(0, 1fr); gap: 16px; }
}

/* ── "Order by" on a section heading's line (Robb, 2026-09-21) ───────────
   Written into BOTH stylesheets: Bold loads league-v2.css alone. */
.lp-sec-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}
/* Flex centres the MARGIN box. The heading carries a top margin the control
   does not, so with it in place the two never sit on the same line. The
   spacing belongs to the container instead. */
.lp-sec-head { margin: 28px 0 14px; }
.lp-sec-head > .lp-div-sec-title,
.lp-sec-head > .section-title { margin: 0; }
/* Search + order-by travel together on the right of a heading; as separate
   children of a space-between flex the second one wraps to its own line. */
.lp-header-tools { display: flex; align-items: center; gap: 14px; flex-wrap: nowrap; }
/* The SEARCH yields, not the order-by: the search has a comfortable width
   it can give up, while a select that shrinks truncates its own labels.
   Without this the pair needed 555px, the heading left it 457, and the
   order-by wrapped onto its own line under the title. */
.lp-header-tools > .lp-searchwrap { flex: 1 1 180px; min-width: 0; }
.lp-header-tools > .lp-sortwrap { flex: none; }
@media (max-width: 899px) {
    /* Below this the two genuinely do not fit beside a heading. */
    .lp-header-tools { flex-wrap: wrap; }
}
.lp-sortwrap { display: flex; align-items: center; gap: 8px; flex: none; }
.lp-sort-label { font-size: 12.5px; font-weight: 600; color: #64748b; white-space: nowrap; }
.lp-sort {
    font-size: 14px;
    padding: 8px 30px 8px 11px;
    min-height: 40px;
    border: 1px solid #d5dce6;
    border-radius: 8px;
    background: #fff;
    color: #0b2545;
    cursor: pointer;
    /* Our own chevron; the native one differs per platform and sits oddly
       against a bordered control. */
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='none' stroke='%2364748b' stroke-width='2' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
}
.lp-sort:focus-visible { outline: 2px solid #2563eb; outline-offset: 1px; }
@media (max-width: 599px) {
    /* The heading keeps the line and the control drops below it, full width —
       a label plus a select will not share 375px with a heading. */
    .lp-sec-head { align-items: stretch; }
    .lp-sortwrap { width: 100%; }
    .lp-sort { flex: 1; font-size: 16px; min-height: 44px; }
}

/* Order-by inside the Registrations filter toolbars. The toolbar foot
   already holds the count and Clear; the select joins them and the whole
   foot wraps as a unit on a narrow screen. */
.lp-reg-toolbar-foot, .v2f-foot { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.lp-reg-toolbar-foot > .lp-sortwrap, .v2f-foot > .lp-sortwrap { flex: none; }
@media (max-width: 640px) {
    .lp-reg-toolbar-foot > .lp-sortwrap, .v2f-foot > .lp-sortwrap { width: 100%; }
    .lp-reg-toolbar-foot .lp-sort, .v2f-foot .lp-sort { flex: 1; }
}

/* ── A division row has no meter and no status pill ──────────────────────
   Robb, 2026-09-21. `.lp-srow` is a SIX column grid shared with the season
   and event rows (rail, main, batt, status, price, cta); a division row now
   emits four cells, so without this its price and CTA would land in the
   batt and status columns.

   Appended last on purpose: `.lp-srow-lean` and `.lp-srow` are both one
   class, so the tie is broken by source order and the phone block above
   would otherwise win back the six-column areas. */
.lp-srow-lean { grid-template-columns: 6px minmax(0, 1fr) auto auto; }
@media (max-width: 720px) {
    .lp-srow-lean {
        grid-template-columns: 6px minmax(0, 1fr);
        grid-template-areas:
            "rail main"
            "rail price"
            "rail cta";
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   SEASON REGISTRATION CARD - "accent rail" (Robb, 2026-09-20)
   ═══════════════════════════════════════════════════════════════════════
   Replaces the tall panel card (dark header band, four divider rows, boxed
   outline button) with a slim one: a 4px league-coloured rail, a 2x2 grid
   of facts, and a text CTA. Roughly half the height.

   SCOPED TO .lp-scard, NOT .lp-season-card. renderEventCard's two cards
   carry .lp-season-card as well, but their body holds a title/dates/meta -
   no .pe-card-row - so a 2-column body grid would wreck them. They keep
   today's look deliberately; Robb asked about the season cards only.

   BOLD IS UNTOUCHED. It renders through boldSeasonCard against
   league-v2.css, which contains zero references to .pe-event-card,
   .pe-card-row, .pe-event-badge or .pe-card-cta.

   SPECIFICITY, the part that bites: league-miniport.css, -readonly.css and
   -editorial.css are injected AFTER this file, and miniport styles
   `.tpl-miniport .lp-season-card` (0,2,0) and `.tpl-miniport .pe-card-cta`
   (0,2,0). An equal-specificity selector here LOSES on sheet order, so the
   card-level rules stack three classes (0,3,0) and descendants hang off
   `.lp-scard.lp-season-card` (0,3,x). That also clears the .tpl-modern
   rules earlier in this file without an !important. */
.pe-event-card.lp-season-card.lp-scard {
    border: 1px solid #e8ecf1;
    /* Radius 0 is deliberate: a 4px single-sided border bends visibly
       against a rounded corner. */
    border-radius: 0;
    border-left: 4px solid var(--league-secondary, var(--league-primary, #16233f));
}

/* No band on any template now - the header is the top of the white card. */
.lp-scard.lp-season-card .pe-event-card-header {
    background: transparent;
    padding: 13px 16px 0;
    gap: 10px;
    align-items: baseline;
    justify-content: space-between;
    min-height: 0;
}
.lp-scard.lp-season-card .pe-event-cardname {
    font-size: 15px;
    font-weight: 700;
}

/* Status as a coloured dot plus a plain label - no pill, no fill, no caps.
   The dot inherits currentColor, so each status sets one property. */
.lp-scard.lp-season-card .pe-event-badge {
    flex: none;
    margin-left: auto;
    padding: 0;
    background: none;
    border-radius: 0;
    font-size: 11px;
    font-weight: 700;
    text-transform: none;
    letter-spacing: 0;
    white-space: nowrap;
    color: #64748b;
}
.lp-scard.lp-season-card .pe-event-badge::before {
    content: '';
    display: inline-block;
    width: 6px;
    height: 6px;
    margin-right: 5px;
    border-radius: 50%;
    background: currentColor;
    vertical-align: 1px;
}
/* These need the four-class weight to clear .tpl-modern's solid-pill
   overrides at (0,4,1); `background: none` is repeated because those set it. */
.lp-scard.lp-season-card .pe-event-badge.lp-status-open     { background: none; color: #15803d; }
.lp-scard.lp-season-card .pe-event-badge.lp-status-active   { background: none; color: #b45309; }
.lp-scard.lp-season-card .pe-event-badge.lp-status-upcoming { background: none; color: #1d4ed8; }
.lp-scard.lp-season-card .pe-event-badge.lp-status-full     { background: none; color: #b91c1c; }
.lp-scard.lp-season-card .pe-event-badge.lp-status-closed   { background: none; color: #64748b; }

/* The facts, two across, label above value. The rows and the CTA are
   siblings in the body, so the body itself is the grid and the CTA spans
   it - that keeps the markup as renderSeasonCard already emits it. */
.lp-scard.lp-season-card .pe-event-card-body {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px 14px;
    padding: 12px 16px 14px;
}
.lp-scard.lp-season-card .pe-card-row {
    display: block;
    min-width: 0;
    padding: 0;
    border-bottom: none;
    font-size: 12px;
}
.lp-scard.lp-season-card .pe-card-row-k {
    display: block;
    font-size: 11px;
    color: #64748b;
}
.lp-scard.lp-season-card .pe-card-row-v {
    display: block;
    text-align: left;
    font-weight: 600;
    color: #1a1a2e;
}

/* CTA as a text link. It loses the 44px floor the boxed button carried,
   which is fine here and not a regression against the mobile rules: the
   whole card is the <a>, so the tap target is the card, not this span. */
.lp-scard.lp-season-card .pe-card-cta {
    grid-column: 1 / -1;
    display: block;
    min-height: 0;
    margin: 2px 0 0;
    padding: 0;
    border: none;
    border-radius: 0;
    /* .tpl-modern fills this with var(--mod-accent) and .is-muted gives it a
       1.5px outline - both have to go, not just the colour, or the label
       renders accent-on-accent. */
    background: none;
    text-align: left;
    font-size: 12px;
    font-weight: 700;
    text-transform: none;
    letter-spacing: 0;
    color: var(--league-secondary, #d6323b);
}
.lp-scard.lp-season-card .pe-card-cta::after { content: ' \2192'; }
.lp-scard.lp-season-card .pe-card-cta.is-muted {
    background: none;
    border: none;
    color: #64748b;
}
/* Hover changes the underline only - NOTHING lifts. */
.lp-scard.lp-season-card:hover .pe-card-cta { text-decoration: underline; }

/* The pre-registration card shares the body grid, so its countdown has to
   span both columns the way the CTA does. */
.lp-scard.lp-season-card .pre-reg-countdown {
    grid-column: 1 / -1;
    padding: 2px 0 0;
}

@media (max-width: 599px) {
    /* House rule: meta text floors at 12px on a phone, so the label goes up
       rather than the value coming down. Two columns still fit - at 375px
       the card is ~343px wide, so each column is ~150px and a date range
       like "Sep 8 - Nov 15" sits on one line. */
    .lp-scard.lp-season-card .pe-card-row-k { font-size: 12px; }
    .lp-scard.lp-season-card .pe-event-card-body { gap: 9px 12px; }
}

/* ══ Images and tables in rich text (Robb, 2026-09-21) ════════════════════
   GENERATED — see bin/test-richtext-wrappers.php. This block is IDENTICAL in
   league-page.css and league-v2.css and must stay that way; the guard fails
   the build if it is not.

   THE WRAPPER LIST IS THE POINT, and it is the thing that goes wrong. Editor
   output does not land in one class: the season Overview renders inside
   `.lp-about-content`, Bold's About inside a bare `.section-lead`, a service
   detail inside `.pe-richtext`, a rules document inside one of three
   `*-doc-rule-body` classes. Scoping to `.lp-richtext` alone shipped a table
   with no borders, no padding and no phone scroll — twice, once per skin,
   because the two files were maintained separately and neither list was
   complete. **Add a wrapper here whenever a renderer gains one**, and let the
   generator write both files.

   Images: four presets, not a free width box, so there is no way to publish a
   40px photo or blow out the column. The sanitizer allows these four class
   names and nothing else. left/right float on desktop and go FULL WIDTH on a
   phone — a 45% float in a 375px column leaves about 160px of text beside it,
   which reads as a broken layout rather than a design.

   Tables: rectangular by construction — the sanitizer allows no colspan or
   rowspan and the dialog cannot make one, so there is no merged cell to
   misalign. On a phone the TABLE itself scrolls rather than a wrapper div;
   adding `div` to the allow-list to hold one would widen a security boundary
   for a layout problem. `overscroll-behavior: contain` is not optional —
   without it a horizontal pan on iOS triggers browser back-swipe, the same
   reason app.css pins it on .table-wrap. */
.lp-richtext img, .lp-about-content img, .pe-richtext img, .v2-richtext img, .section-lead img, .lp-doc-rule-body img, .v2-doc-rule-body img, .pcm-doc-rule-body img, .lp-ally-dialog-body img { max-width: 100%; height: auto; border-radius: 8px; display: block; }
.lp-richtext img.pe-img-full, .lp-about-content img.pe-img-full, .pe-richtext img.pe-img-full, .v2-richtext img.pe-img-full, .section-lead img.pe-img-full, .lp-doc-rule-body img.pe-img-full, .v2-doc-rule-body img.pe-img-full, .pcm-doc-rule-body img.pe-img-full, .lp-ally-dialog-body img.pe-img-full { width: 100%; margin: 1.4em 0; }
.lp-richtext img.pe-img-half, .lp-about-content img.pe-img-half, .pe-richtext img.pe-img-half, .v2-richtext img.pe-img-half, .section-lead img.pe-img-half, .lp-doc-rule-body img.pe-img-half, .v2-doc-rule-body img.pe-img-half, .pcm-doc-rule-body img.pe-img-half, .lp-ally-dialog-body img.pe-img-half { width: 50%;  margin: 1.4em 0; }
.lp-richtext img.pe-img-left, .lp-about-content img.pe-img-left, .pe-richtext img.pe-img-left, .v2-richtext img.pe-img-left, .section-lead img.pe-img-left, .lp-doc-rule-body img.pe-img-left, .v2-doc-rule-body img.pe-img-left, .pcm-doc-rule-body img.pe-img-left, .lp-ally-dialog-body img.pe-img-left { width: 45%; float: left;  margin: 0.4em 1.4em 1em 0; }
.lp-richtext img.pe-img-right, .lp-about-content img.pe-img-right, .pe-richtext img.pe-img-right, .v2-richtext img.pe-img-right, .section-lead img.pe-img-right, .lp-doc-rule-body img.pe-img-right, .v2-doc-rule-body img.pe-img-right, .pcm-doc-rule-body img.pe-img-right, .lp-ally-dialog-body img.pe-img-right { width: 45%; float: right; margin: 0.4em 0 1em 1.4em; }
/* A float must not escape the block that holds it. */
.lp-richtext::after, .lp-about-content::after, .pe-richtext::after, .v2-richtext::after, .section-lead::after, .lp-doc-rule-body::after, .v2-doc-rule-body::after, .pcm-doc-rule-body::after, .lp-ally-dialog-body::after { content: ""; display: table; clear: both; }

@media (max-width: 599px) {
    .lp-richtext img.pe-img-half, .lp-richtext img.pe-img-left, .lp-richtext img.pe-img-right, .lp-about-content img.pe-img-half, .lp-about-content img.pe-img-left, .lp-about-content img.pe-img-right, .pe-richtext img.pe-img-half, .pe-richtext img.pe-img-left, .pe-richtext img.pe-img-right, .v2-richtext img.pe-img-half, .v2-richtext img.pe-img-left, .v2-richtext img.pe-img-right, .section-lead img.pe-img-half, .section-lead img.pe-img-left, .section-lead img.pe-img-right, .lp-doc-rule-body img.pe-img-half, .lp-doc-rule-body img.pe-img-left, .lp-doc-rule-body img.pe-img-right, .v2-doc-rule-body img.pe-img-half, .v2-doc-rule-body img.pe-img-left, .v2-doc-rule-body img.pe-img-right, .pcm-doc-rule-body img.pe-img-half, .pcm-doc-rule-body img.pe-img-left, .pcm-doc-rule-body img.pe-img-right, .lp-ally-dialog-body img.pe-img-half, .lp-ally-dialog-body img.pe-img-left, .lp-ally-dialog-body img.pe-img-right { width: 100%; float: none; margin: 1.2em 0; }
}

.lp-richtext table, .lp-about-content table, .pe-richtext table, .v2-richtext table, .section-lead table, .lp-doc-rule-body table, .v2-doc-rule-body table, .pcm-doc-rule-body table, .lp-ally-dialog-body table {
    width: 100%; margin: 1.4em 0;
    border-collapse: collapse;
    font-size: 0.95em;
}
.lp-richtext caption, .lp-about-content caption, .pe-richtext caption, .v2-richtext caption, .section-lead caption, .lp-doc-rule-body caption, .v2-doc-rule-body caption, .pcm-doc-rule-body caption, .lp-ally-dialog-body caption {
    caption-side: top; margin-bottom: 0.5em;
    font-size: 0.85em; font-weight: 700; text-align: left;
    letter-spacing: 0.02em; text-transform: uppercase; opacity: 0.7;
}
.lp-richtext th, .lp-richtext td, .lp-about-content th, .lp-about-content td, .pe-richtext th, .pe-richtext td, .v2-richtext th, .v2-richtext td, .section-lead th, .section-lead td, .lp-doc-rule-body th, .lp-doc-rule-body td, .v2-doc-rule-body th, .v2-doc-rule-body td, .pcm-doc-rule-body th, .pcm-doc-rule-body td, .lp-ally-dialog-body th, .lp-ally-dialog-body td { padding: 9px 12px; text-align: left; vertical-align: top; }
.lp-richtext th, .lp-about-content th, .pe-richtext th, .v2-richtext th, .section-lead th, .lp-doc-rule-body th, .v2-doc-rule-body th, .pcm-doc-rule-body th, .lp-ally-dialog-body th { font-weight: 700; border-bottom: 2px solid currentColor; }
.lp-richtext td, .lp-about-content td, .pe-richtext td, .v2-richtext td, .section-lead td, .lp-doc-rule-body td, .v2-doc-rule-body td, .pcm-doc-rule-body td, .lp-ally-dialog-body td { border-bottom: 1px solid currentColor; }
.lp-richtext th, .lp-richtext td, .lp-about-content th, .lp-about-content td, .pe-richtext th, .pe-richtext td, .v2-richtext th, .v2-richtext td, .section-lead th, .section-lead td, .lp-doc-rule-body th, .lp-doc-rule-body td, .v2-doc-rule-body th, .v2-doc-rule-body td, .pcm-doc-rule-body th, .pcm-doc-rule-body td, .lp-ally-dialog-body th, .lp-ally-dialog-body td { border-color: rgba(128, 128, 128, 0.28); }

/* Alignment (2026-09-23): the two classes Quill writes, on any block or
   cell; left is the absence of one. A rule, for the editor's Horizontal
   rule button. Both scoped to the wrappers like everything else here. */
.lp-richtext .ql-align-center, .lp-about-content .ql-align-center, .pe-richtext .ql-align-center, .v2-richtext .ql-align-center, .section-lead .ql-align-center, .lp-doc-rule-body .ql-align-center, .v2-doc-rule-body .ql-align-center, .pcm-doc-rule-body .ql-align-center, .lp-ally-dialog-body .ql-align-center { text-align: center; }
.lp-richtext .ql-align-right, .lp-about-content .ql-align-right, .pe-richtext .ql-align-right, .v2-richtext .ql-align-right, .section-lead .ql-align-right, .lp-doc-rule-body .ql-align-right, .v2-doc-rule-body .ql-align-right, .pcm-doc-rule-body .ql-align-right, .lp-ally-dialog-body .ql-align-right { text-align: right; }
.lp-richtext hr, .lp-about-content hr, .pe-richtext hr, .v2-richtext hr, .section-lead hr, .lp-doc-rule-body hr, .v2-doc-rule-body hr, .pcm-doc-rule-body hr, .lp-ally-dialog-body hr { border: 0; border-top: 1px solid rgba(128, 128, 128, 0.35); margin: 1.6em 0; }
/* Headings (2026-10-19): MARGINS ONLY, at (0,0,1) via :where/:is. Every
   wrapper had p/ul/li rules and no heading rule at all, so the sheet's `*`
   reset left each h2/h3 at margin 0 — flush against the next paragraph in
   every public drawer. :where() keeps this below any wrapper's own heading
   rule (.lp-about-content h2, .tpl-modern .lp-richtext h2, .v2-richtext h2
   all keep theirs) while still beating the reset. The :first-child form is
   (0,1,0) for the same reason. */
:where(.lp-richtext, .lp-about-content, .pe-richtext, .v2-richtext, .section-lead, .lp-doc-rule-body, .v2-doc-rule-body, .pcm-doc-rule-body, .lp-ally-dialog-body) :is(h1, h2, h3, h4) { margin: 1.4em 0 0.5em; line-height: 1.25; }
:where(.lp-richtext, .lp-about-content, .pe-richtext, .v2-richtext, .section-lead, .lp-doc-rule-body, .v2-doc-rule-body, .pcm-doc-rule-body, .lp-ally-dialog-body) :where(h1, h2, h3, h4):first-child { margin-top: 0; }
/* A quote: a rule down the left in the text colour, inset, slightly quieter.
   currentColor and opacity, so it reads on both skins without a colour of its own. */
.lp-richtext blockquote, .lp-about-content blockquote, .pe-richtext blockquote, .v2-richtext blockquote, .section-lead blockquote, .lp-doc-rule-body blockquote, .v2-doc-rule-body blockquote, .pcm-doc-rule-body blockquote, .lp-ally-dialog-body blockquote { margin: 1.4em 0; padding: 0.3em 0 0.3em 1.1em; border-left: 4px solid currentColor; opacity: 0.85; }
/* Text size: three fixed steps (Quill's class form), relative to the wrapper's
   own size so a "huge" in a card blurb is not a huge in a page heading. app.css
   gives the editor the same three numbers. */
.lp-richtext .ql-size-small, .lp-about-content .ql-size-small, .pe-richtext .ql-size-small, .v2-richtext .ql-size-small, .section-lead .ql-size-small, .lp-doc-rule-body .ql-size-small, .v2-doc-rule-body .ql-size-small, .pcm-doc-rule-body .ql-size-small, .lp-ally-dialog-body .ql-size-small { font-size: 0.85em; }
.lp-richtext .ql-size-large, .lp-about-content .ql-size-large, .pe-richtext .ql-size-large, .v2-richtext .ql-size-large, .section-lead .ql-size-large, .lp-doc-rule-body .ql-size-large, .v2-doc-rule-body .ql-size-large, .pcm-doc-rule-body .ql-size-large, .lp-ally-dialog-body .ql-size-large { font-size: 1.25em; }
.lp-richtext .ql-size-huge, .lp-about-content .ql-size-huge, .pe-richtext .ql-size-huge, .v2-richtext .ql-size-huge, .section-lead .ql-size-huge, .lp-doc-rule-body .ql-size-huge, .v2-doc-rule-body .ql-size-huge, .pcm-doc-rule-body .ql-size-huge, .lp-ally-dialog-body .ql-size-huge { font-size: 1.6em; line-height: 1.25; }
/* A highlight is one of six LIGHT tints (RichText::HIGHLIGHTS). Bold's text is
   light, so the words over a tint are forced near-black on both skins; an
   inline palette colour on the same span still wins, being inline. */
.lp-richtext span[style*="background-color"], .lp-about-content span[style*="background-color"], .pe-richtext span[style*="background-color"], .v2-richtext span[style*="background-color"], .section-lead span[style*="background-color"], .lp-doc-rule-body span[style*="background-color"], .v2-doc-rule-body span[style*="background-color"], .pcm-doc-rule-body span[style*="background-color"], .lp-ally-dialog-body span[style*="background-color"] { color: #1a1a2e; padding: 0 0.15em; border-radius: 3px; -webkit-box-decoration-break: clone; box-decoration-break: clone; }
/* Nested lists (2026-09-23): RichText::unquillLists builds REAL sub-lists from
   Quill's indent classes, so the browser numbers each level; this only picks
   the markers and closes the gap the wrapper's own list margin would open. */
.lp-richtext ul ul, .lp-about-content ul ul, .pe-richtext ul ul, .v2-richtext ul ul, .section-lead ul ul, .lp-doc-rule-body ul ul, .v2-doc-rule-body ul ul, .pcm-doc-rule-body ul ul, .lp-ally-dialog-body ul ul, .lp-richtext ul ol, .lp-about-content ul ol, .pe-richtext ul ol, .v2-richtext ul ol, .section-lead ul ol, .lp-doc-rule-body ul ol, .v2-doc-rule-body ul ol, .pcm-doc-rule-body ul ol, .lp-ally-dialog-body ul ol, .lp-richtext ol ol, .lp-about-content ol ol, .pe-richtext ol ol, .v2-richtext ol ol, .section-lead ol ol, .lp-doc-rule-body ol ol, .v2-doc-rule-body ol ol, .pcm-doc-rule-body ol ol, .lp-ally-dialog-body ol ol, .lp-richtext ol ul, .lp-about-content ol ul, .pe-richtext ol ul, .v2-richtext ol ul, .section-lead ol ul, .lp-doc-rule-body ol ul, .v2-doc-rule-body ol ul, .pcm-doc-rule-body ol ul, .lp-ally-dialog-body ol ul { margin: 0.3em 0 0.3em; }
.lp-richtext ul ul, .lp-about-content ul ul, .pe-richtext ul ul, .v2-richtext ul ul, .section-lead ul ul, .lp-doc-rule-body ul ul, .v2-doc-rule-body ul ul, .pcm-doc-rule-body ul ul, .lp-ally-dialog-body ul ul { list-style-type: circle; }
.lp-richtext ul ul ul, .lp-about-content ul ul ul, .pe-richtext ul ul ul, .v2-richtext ul ul ul, .section-lead ul ul ul, .lp-doc-rule-body ul ul ul, .v2-doc-rule-body ul ul ul, .pcm-doc-rule-body ul ul ul, .lp-ally-dialog-body ul ul ul { list-style-type: square; }
.lp-richtext ol ol, .lp-about-content ol ol, .pe-richtext ol ol, .v2-richtext ol ol, .section-lead ol ol, .lp-doc-rule-body ol ol, .v2-doc-rule-body ol ol, .pcm-doc-rule-body ol ol, .lp-ally-dialog-body ol ol { list-style-type: lower-alpha; }
.lp-richtext ol ol ol, .lp-about-content ol ol ol, .pe-richtext ol ol ol, .v2-richtext ol ol ol, .section-lead ol ol ol, .lp-doc-rule-body ol ol ol, .v2-doc-rule-body ol ol ol, .pcm-doc-rule-body ol ol ol, .lp-ally-dialog-body ol ol ol { list-style-type: lower-roman; }
/* A video (YouTube/Vimeo only — RichText::VIDEO_SRC) is a 16:9 box the width
   of the column. aspect-ratio, so no wrapper div has to be admitted for it. */
.lp-richtext iframe.ql-video, .lp-about-content iframe.ql-video, .pe-richtext iframe.ql-video, .v2-richtext iframe.ql-video, .section-lead iframe.ql-video, .lp-doc-rule-body iframe.ql-video, .v2-doc-rule-body iframe.ql-video, .pcm-doc-rule-body iframe.ql-video, .lp-ally-dialog-body iframe.ql-video { display: block; width: 100%; aspect-ratio: 16 / 9; height: auto; border: 0; border-radius: 8px; margin: 1.4em 0; background: #000; }
/* An image inside a table cell fills the cell and never floats: a 45% float
   in a 60px column is a sliver beside three words. The editor draws it the
   same way (app.css). */
.lp-richtext td img, .lp-richtext th img, .lp-about-content td img, .lp-about-content th img, .pe-richtext td img, .pe-richtext th img, .v2-richtext td img, .v2-richtext th img, .section-lead td img, .section-lead th img, .lp-doc-rule-body td img, .lp-doc-rule-body th img, .v2-doc-rule-body td img, .v2-doc-rule-body th img, .pcm-doc-rule-body td img, .pcm-doc-rule-body th img, .lp-ally-dialog-body td img, .lp-ally-dialog-body th img { width: 100%; float: none; margin: 0.3em 0; }

@media (max-width: 599px) {
    .lp-richtext table, .lp-about-content table, .pe-richtext table, .v2-richtext table, .section-lead table, .lp-doc-rule-body table, .v2-doc-rule-body table, .pcm-doc-rule-body table, .lp-ally-dialog-body table {
        display: block;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        overscroll-behavior: contain;
    }
    .lp-richtext th, .lp-richtext td, .lp-about-content th, .lp-about-content td, .pe-richtext th, .pe-richtext td, .v2-richtext th, .v2-richtext td, .section-lead th, .section-lead td, .lp-doc-rule-body th, .lp-doc-rule-body td, .v2-doc-rule-body th, .v2-doc-rule-body td, .pcm-doc-rule-body th, .pcm-doc-rule-body td, .lp-ally-dialog-body th, .lp-ally-dialog-body td { white-space: nowrap; }
}

/* ── .lp-rt-flush ─────────────────────────────────────────────────────────
   For a rich-text container that USED to be a `<p>` and owns its own outer
   margin — a service card blurb, a news slide, a division writeup.

   The editor emits block children, so the container cannot stay a `<p>`
   (a `<p>` inside a `<p>` is broken out by the parser and the layout goes
   with it). Turning it into a `<div>` then leaves the FIRST child's
   margin-top and the LAST child's margin-bottom stacking on top of the
   container's own — the text visibly drops away from whatever sits above
   it, on every card at once. This is the one line that keeps the swap
   invisible. */
.lp-rt-flush > :first-child { margin-top: 0; }
.lp-rt-flush > :last-child  { margin-bottom: 0; }

/* ── Programme heading (mig 290) ─────────────────────────────────────────────
   "StarBabies & Tots" above the five sessions inside it. The sessions are the
   cards; this only names the set they belong to.

   grid-column: 1 / -1 is the load-bearing line. Both containers this lands in
   are CSS grids (.pe-event-grid is three columns, .lp-srows a row list), and a
   heading dropped into a grid becomes an ordinary grid ITEM — it would sit in
   the first cell with two session cards beside it, reading as a card with no
   content rather than as a heading over the row.

   Defined in league-page.css AND league-v2.css: Bold loads its own stylesheet
   only, so a class written once renders unstyled in the other template. */
.lp-prog-title {
    grid-column: 1 / -1;
    margin: 26px 0 10px;
    font-size: 15px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--lp-text-muted, #64748b);
}
/* The first one carries the section heading above it, so it does not need the
   gap that separates one programme from the previous programme's last card. */
.lp-prog-title:first-child { margin-top: 0; }

@media (max-width: 599px) {
    /* The phone floor for meta-weight text (bin/test-mobile-css.php). */
    .lp-prog-title { font-size: 13px; margin: 20px 0 8px; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   MOBILE PASS — the public pages (2026-09-21)
   ═══════════════════════════════════════════════════════════════════════════
   app.css and marketing.css have carried this floor since the 2026-09-13
   audit. league-page.css never did, and league-page.css is the PARENT-facing
   surface: the public Registrations search box was 14px, so an iPhone zoomed
   on focus and stayed zoomed on the one page a family has to use.

   `!important` because the per-page rules that set a smaller size live in a
   `<style>` block the controller writes INTO THE DOCUMENT, after this file.
   Ordinary specificity there beats any rule here whatever its selector; a
   declaration with !important does not, and nothing on these pages writes an
   !important of its own.

   Keep this block LAST in this file, for the same reason app.css keeps its
   own last, and assert any new rule in bin/test-mobile-css.php.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 599px) {
    input:not([type="checkbox"]):not([type="radio"]),
    select,
    textarea { font-size: 16px !important; }
}

/* ── Season Documents tab: one list, signables first (2026-09-23) ────────────
   Robb's Option B. The tab used to stack a "Documents" section over a "What
   you'll sign" section — two headings over three rows — and the rows were
   UNSTYLED, because .lp-doc-ic / .lp-doc-label / the <details> marker rules
   live inline in the division page's own <style> block and this tab never
   loaded them. A bare `▶` and a run-together "RulesYouth Basketball…" is what
   that looks like. These rules live in the stylesheet so both pages that need
   them can have them.

   The ROW is the button. A 13px "View" link would be a 17px tap target on the
   one page a parent reads on a phone; the whole row clears 44px and the "View"
   on the right is the affordance, not the hit area. */
.lp-dl {
    display: flex;
    flex-direction: column;
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    overflow: hidden;
    background: #fff;
}

.lp-dl-row {
    display: flex;
    align-items: center;
    gap: 14px;
    width: 100%;
    min-height: 64px;
    padding: 14px 18px;
    margin: 0;
    border: 0;
    border-bottom: 1px solid #eef2f7;
    background: #fff;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
    transition: background .15s;
}

.lp-dl-row:last-child { border-bottom: 0; }
/* No lift — background only. */
.lp-dl-row:hover { background: #f8fafc; }
.lp-dl-row:focus-visible { outline: 2px solid var(--league-secondary, #2563eb); outline-offset: -2px; }
.lp-dl-row.is-flat { cursor: default; }
.lp-dl-row.is-flat:hover { background: #fff; }

.lp-dl-ico {
    flex: none;
    width: 22px;
    height: 22px;
    color: #94a3b8;
}

.lp-dl-ico-sign { color: var(--league-secondary, #2563eb); }

.lp-dl-main { flex: 1 1 auto; min-width: 0; }

.lp-dl-title {
    display: block;
    font-size: 15px;
    font-weight: 600;
    color: #111827;
    line-height: 1.35;
}

.lp-dl-sub {
    display: block;
    margin-top: 2px;
    font-size: 13px;
    color: #64748b;
}

.lp-dl-chip {
    flex: none;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .03em;
    padding: 3px 9px;
    border-radius: 6px;
    background: #f1f5f9;
    color: #64748b;
}

.lp-dl-chip.is-req { background: #fff7ed; color: #b45309; }
.lp-dl-chip.is-opt { background: #f1f5f9; color: #64748b; }

.lp-dl-view {
    flex: none;
    font-size: 13px;
    font-weight: 700;
    color: var(--league-secondary, #2563eb);
}

/* The embedded file viewer inside the drawer. Tall, because a drawer is. */
.lp-dl-frame {
    width: 100%;
    height: 62vh;
    min-height: 320px;
    border: 1px solid #e5e7eb;
    border-radius: 8px;
    background: #fff;
}

/* ── A document on a phone is a tap-through, not a shrunken page ──────────
   See the note in doc-drawer.js: a letter page at 335px renders its body text
   at about 6px. The frame is not built at all below 600px, so this is what
   stands in its place. */
@media (max-width: 599px) {
    .lp-dl-tap {
        display: flex; flex-direction: column; align-items: center;
        gap: 12px; padding: 30px 12px 10px; text-align: center;
    }
    .lp-dl-tap svg { width: 40px; height: 40px; color: #94a3b8; }
    .lp-dl-tap p {
        margin: 0; font-size: 14px; line-height: 1.55;
        color: #5b6472; max-width: 32ch;
    }
    /* One action, and it is the whole point of the panel here. */
    #doc-drawer .pcm-foot .pcm-btn { width: 100%; box-sizing: border-box; }
}
/* ── The document drawer's PDF frame fills the panel ──────────────────────
   At a fixed 62vh inside a FULL-HEIGHT drawer the frame stopped short and
   left a band of empty white between it and the footer. Growing it uses the
   room the drawer already has, which is the point of a full-height panel.

   ⚠ Desktop only. The phone sheet sizes itself to its content, so a growing
   child there has nothing to grow into and the fixed 54vh is right.
   `.pcm-box > *` sets flex-shrink: 0 on every child; the id beats it. */
@media (min-width: 600px) {
    #doc-drawer .pcm-desc { flex: 1 1 auto; min-height: 0; display: flex; }
    #doc-drawer .lp-dl-frame { flex: 1 1 auto; height: auto; min-height: 320px; }
}
@media (max-width: 599px) {
    .lp-dl-row { padding: 13px 14px; gap: 11px; }
    /* The chip and the word "View" together are more than a 375px row can hold
       beside a two-line title. The chip carries the meaning; "View" is the one
       that goes, since the whole row is the button anyway. */
    .lp-dl-view { display: none; }
    .lp-dl-frame { height: 54vh; }
}

/* ── Division page: writeup left, registrations right (2026-09-23) ───────────
   Robb's Option C. The page stacked a five-paragraph writeup above the list of
   seasons, so the thing a parent came for — the sign-ups — sat below ~600px of
   prose.

   Two columns alone would not have fixed it: the writeup is far taller than the
   list, so the right column would end level with paragraph two and leave a
   400px gutter. So the intro is CLAMPED to a few lines with a Read more, and
   the two columns start level. That is the actual problem — the mismatch, not
   the column count.

   ORDER IS DELIBERATE AND REVERSES ON A PHONE. Desktop reads left-to-right,
   prose then sign-ups. Stacked on a phone the sign-ups come FIRST: the action
   before the context, because a parent on a phone is not reading five
   paragraphs to find a button. */
.lp-div-split {
    display: flex;
    flex-direction: column;
    gap: 22px;
}

/* Phone order: registrations, then the writeup. */
.lp-div-reg  { order: 1; }
.lp-div-main { order: 2; }

@media (min-width: 900px) {
    .lp-div-split {
        display: grid;
        grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
        gap: 34px;
        align-items: start;
    }
    /* Source order again — the grid places them, so neither needs an order. */
    .lp-div-reg, .lp-div-main { order: 0; }
    /* Follows the reader down a long description. `top` clears the sticky
       header the public shell already has. */
    .lp-div-reg { position: sticky; top: 20px; }
}

/* The clamp. Applied by JS only when the prose is actually taller than it,
   so a two-line division never grows a Read more it does not need.

   THE HEIGHT IS A VARIABLE BECAUSE THE RAIL DECIDES IT. A fixed clamp left
   523px of white below the prose on a division with eight seasons — text
   hidden behind Read more while half a screen sat empty beside it. The JS
   raises --lp-div-clamp to the registration rail's height so the two
   columns end level; 11.6em is the FLOOR for a one-season division whose
   rail is shorter than that, and the value the phone keeps, where there is
   no rail to match and the clamp's job is just to keep the page short. */
.lp-div-writeup.is-clamped {
    max-height: var(--lp-div-clamp, 11.6em);
    overflow: hidden;
}

.lp-div-more {
    display: inline-block;
    margin-top: 10px;
    padding: 0;
    border: 0;
    background: none;
    font-family: inherit;
    font-size: 14px;
    font-weight: 700;
    color: var(--league-secondary, #2563eb);
    cursor: pointer;
}

.lp-div-more:hover { text-decoration: underline; }

/* The rail's own heading line, carrying the sort control and (when the
   division holds both kinds) the Seasons/Events tabs. They sit over the
   REGISTRATIONS column, not over the page: a strip above the whole page
   appears to do nothing to the left-hand side when you switch it. */
.lp-div-reg-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.lp-div-reg .lp-srows {
    border: 1px solid #e5e7eb;
    border-radius: 12px;
    overflow: hidden;
    background: #fff;
}

/* ⚠ The stacked row form, repeated for the rail.

   These declarations are the same ones the `@media (max-width: 720px)` block
   above applies, and they cannot be shared with it: a media query keys off the
   VIEWPORT, and this is a ~430px COLUMN on a 1280px screen, which that query
   never sees. Container queries would collapse the two; until then, a change to
   the stacked form has to be made in both places.

   The alternative — making stacked the default and the wide form the
   enhancement — is the better refactor and a much larger one, since `.lp-srow`
   is shared with pages this change does not touch. */
.lp-div-reg .lp-srow {
    /* TWO lines, not four. The phone template stacks name / status / price /
       buttons because a 375px screen has no width to share; a 432px rail
       does. Four lines made each season ~177px and the rail 886px against a
       253px writeup — the same imbalance the clamp had just fixed, flipped
       to the other side. */
    grid-template-columns: 6px auto auto minmax(0, 1fr);
    grid-template-areas:
        "rail main   main  main"
        "rail batt   batt  batt"
        "rail status price cta";
    row-gap: 9px;
    column-gap: 10px;
    padding: 13px 14px;
}

.lp-div-reg .lp-srow-rail   { grid-area: rail; height: 100%; min-height: 44px; }
.lp-div-reg .lp-srow-main   { grid-area: main; }
.lp-div-reg .lp-srow-status { grid-area: status; justify-self: start; }
.lp-div-reg .lp-srow-price  { grid-area: price; justify-self: start; text-align: left; }
.lp-div-reg .lp-batt        { grid-area: batt; width: 100%; max-width: 240px; }
.lp-div-reg .lp-batt.is-empty { display: none; }
/* An empty status must not reserve a row of its own in the rail — with the
   badge gone from open seasons, that was a blank line on every one of them. */
.lp-div-reg .lp-srow-status.is-empty { display: none; }
.lp-div-reg .lp-srow > .lp-srow-cta { grid-area: cta; justify-self: end; }
.lp-div-reg .lp-srow > .lp-srow-actions {
    grid-area: cta;
    display: inline-flex;
    justify-self: end;
    gap: 8px;
}
.lp-div-reg .lp-srow-actions > a { text-align: center; }

/* No writeup: one column, as the page was. */
@media (min-width: 900px) {
    .lp-div-split.is-single { display: block; }
    .lp-div-split.is-single .lp-div-reg { position: static; }
}

/* Show all / Show fewer, under a capped row list (2026-09-23). Five rows
   then a toggle — see rowCapJs() for why this is not numbered pages. */
.lp-srow.is-capped { display: none; }

.lp-row-more {
    display: block;
    width: 100%;
    min-height: 44px;
    margin-top: 10px;
    padding: 11px 14px;
    border: 1px solid #e5e7eb;
    border-radius: 10px;
    background: #fff;
    font-family: inherit;
    font-size: 14px;
    font-weight: 700;
    color: #2563eb;
    cursor: pointer;
}

/* No lift. */
.lp-row-more:hover { border-color: #2563eb; }
.lp-row-more[hidden] { display: none; }

/* ── A custom page's two columns ──────────────────────────────────────
   What the role involves on the left, the application on the right
   (Robb, 2026-09-23). 60/40, and the form used to sit UNDERNEATH the prose,
   so on a page of any length the thing you came to do was below the fold.

   PROSE FIRST WHEN STACKED. The division page deliberately reverses its two
   columns on a phone, putting the sign-up above the description — but that
   is a BUTTON, and this is a fifteen-field form. Putting it first would bury
   the page that explains whether you want to fill it in. */
.lp-page-split {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 32px;
}
.lp-page-split-side { min-width: 0; }
.lp-page-split-main { min-width: 0; }

@media (min-width: 900px) {
    .lp-page-split {
        grid-template-columns: minmax(0, 60fr) minmax(0, 40fr);
        gap: 44px;
        align-items: start;
    }
}
