/* ============================================================================
   IBFUK — Lovable layout, recoloured to the logo palette. Bebas Neue + Barlow.
   ========================================================================== */

/* ---- Self-hosted fonts (no external CDN, CSP-safe) ---- */
@font-face { font-family: "Bebas Neue"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/static/fonts/bebasneue-400.woff2") format("woff2"); }
@font-face { font-family: "Barlow"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/static/fonts/barlow-400.woff2") format("woff2"); }
@font-face { font-family: "Barlow"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("/static/fonts/barlow-500.woff2") format("woff2"); }
@font-face { font-family: "Barlow"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("/static/fonts/barlow-600.woff2") format("woff2"); }
@font-face { font-family: "Barlow"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("/static/fonts/barlow-700.woff2") format("woff2"); }
@font-face { font-family: "Barlow"; font-style: normal; font-weight: 800; font-display: swap;
  src: url("/static/fonts/barlow-800.woff2") format("woff2"); }

:root {
  /* Brand — from the IBFUK logo: red wordmark, blue silhouettes, gold shuttle accent */
  --navy: #0a1e3d;
  --navy-2: #10294f;
  --navy-3: #1d3d6b;
  --blue: #1f8fe0;      /* silhouette blue */
  --blue-2: #0f6fbd;
  --red: #e2231a;       /* IBFUK wordmark red */
  --red-2: #c01810;
  --red-3: #ff4a3d;
  --red-soft: #ffe7e5;
  --gold: #f5b301;      /* shuttlecock fan accent */
  --gold-2: #d99a00;
  --gold-3: #ffcf4d;
  --gold-soft: #fff4d6;

  /* Light theme (default) */
  --bg: #f5f7fb;
  --bg-2: #eef2f9;
  --surface: #ffffff;
  --surface-2: #f0f4fa;
  --ink: #0c1626;
  --ink-2: #26364d;
  --muted: #61728c;
  --line: #e4eaf3;
  --line-strong: #d3dcea;

  /* Primary = logo red; secondary depth = navy/blue */
  --primary: var(--red);
  --primary-2: var(--red-2);
  --primary-3: var(--red-3);
  --primary-soft: var(--red-soft);
  --accent: var(--gold);
  --accent-ink: #3a2b00;
  --brand-bg: var(--navy);

  --shadow-xs: 0 1px 2px rgba(10, 30, 61, 0.06);
  --shadow-sm: 0 2px 10px rgba(10, 30, 61, 0.06);
  --shadow: 0 12px 30px rgba(10, 30, 61, 0.10);
  --shadow-lg: 0 24px 60px rgba(10, 30, 61, 0.16);
  --glow-gold: 0 8px 30px rgba(245, 179, 1, 0.28);

  --radius-sm: 10px;
  --radius: 16px;
  --radius-lg: 22px;
  --radius-xl: 28px;

  /* Spacing scale — use these for margin/padding/gap instead of raw px, the same
     way --radius-*/--shadow-* work. 1=4 2=8 3=12 4=16 5=24 6=32 7=48 8=64. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;

  /* ── Responsive breakpoint scale ──────────────────────────────────────────
     Three canonical widths. Use ONLY these in @media (max-width) queries so a
     given breakpoint means the same thing on every page. (@media can't read
     var(), so this is the documented convention, not a live variable.)
       sm  560px — phone: grids/toolbars/tables collapse to a single column
       md  860px — tablet: nav becomes the drawer; two-col layouts stack
       lg 1024px — small desktop: multi-column grids step down
     Values like 620/660/720/780/800/900/1160px elsewhere are component
     max-widths, NOT breakpoints — leave those alone. */

  --maxw: 1180px;
  --header-h: 68px;
  /* Lovable typography: Barlow body, Bebas Neue display */
  --font: "Barlow", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-display: "Bebas Neue", "Barlow", Impact, sans-serif;
  /* Brand gradient (logo red → gold), echoing Lovable's gradient-brand text */
  --brand-gradient: linear-gradient(120deg, var(--red), var(--gold));

  /* Fluid type scale */
  --fs-hero: clamp(2.35rem, 6vw, 4rem);
  --fs-h1: clamp(1.9rem, 4vw, 2.6rem);
  --fs-h2: clamp(1.5rem, 3vw, 2.1rem);
  --fs-lead: clamp(1.05rem, 2.2vw, 1.3rem);

  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

:root[data-theme="dark"] {
  --bg: #060d1c;
  --bg-2: #0a1424;
  --surface: #0e1a30;
  --surface-2: #14243f;
  --ink: #eef3fb;
  --ink-2: #c7d3e6;
  --muted: #93a4bd;
  --line: #1d3153;
  --line-strong: #274067;
  --brand-bg: #05101f;

  --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-sm: 0 2px 12px rgba(0, 0, 0, 0.4);
  --shadow: 0 14px 34px rgba(0, 0, 0, 0.5);
  --shadow-lg: 0 28px 64px rgba(0, 0, 0, 0.6);
  --glow-gold: 0 8px 34px rgba(245, 179, 1, 0.22);
}

/* ============================================================================
   COLOUR PRESETS (admin-selectable via data-preset on <html>).
   Each preset remaps the brand variables the whole system is keyed off:
   --primary(+2/3/soft), --navy(+2/3), --blue(+2), --gold(+2/3/soft),
   --accent, --brand-bg, --brand-gradient.  'rednavy' is the :root default.
   ========================================================================== */

/* Ocean Teal (Lovable) */
:root[data-preset="ocean"] {
  --navy: #0c2340; --navy-2: #123055; --navy-3: #1a4a6e;
  --blue: #5cbdb9; --blue-2: #2d8a9e;
  --primary: #1a4a6e; --primary-2: #123551; --primary-3: #2d7fa8; --primary-soft: #e2eef5;
  --gold: #5cbdb9; --gold-2: #3ea39e; --gold-3: #7fd3cf; --gold-soft: #e3f5f3;
  --accent: #5cbdb9; --accent-ink: #06323a; --brand-bg: #0c2340;
  --brand-gradient: linear-gradient(120deg, #1a4a6e, #5cbdb9);
}
/* Emerald Green */
:root[data-preset="emerald"] {
  --navy: #0b2e22; --navy-2: #0f3a2b; --navy-3: #175340;
  --blue: #34c98a; --blue-2: #1f9d6b;
  --primary: #0f9d58; --primary-2: #0b7d46; --primary-3: #35c07d; --primary-soft: #e0f5ea;
  --gold: #f5b301; --gold-2: #d99a00; --gold-3: #ffcf4d; --gold-soft: #fff4d6;
  --accent: #f5b301; --accent-ink: #2a2000; --brand-bg: #0b2e22;
  --brand-gradient: linear-gradient(120deg, #0f9d58, #7bd389);
}
/* Royal Purple */
:root[data-preset="purple"] {
  --navy: #241a45; --navy-2: #2e2156; --navy-3: #493a7a;
  --blue: #8a6fd4; --blue-2: #6a4ec0;
  --primary: #5b3fa3; --primary-2: #472f85; --primary-3: #7a5fc6; --primary-soft: #ece7f7;
  --gold: #d4a04a; --gold-2: #b8862f; --gold-3: #e8bd6a; --gold-soft: #f7edd9;
  --accent: #d4a04a; --accent-ink: #2b1e00; --brand-bg: #241a45;
  --brand-gradient: linear-gradient(120deg, #5b3fa3, #d4a04a);
}
/* Sunset Orange */
:root[data-preset="sunset"] {
  --navy: #3a1f14; --navy-2: #4a2718; --navy-3: #6b3a22;
  --blue: #f2a65a; --blue-2: #d97e2e;
  --primary: #e85d3a; --primary-2: #c6462a; --primary-3: #ff7d5c; --primary-soft: #ffe8e0;
  --gold: #f2a65a; --gold-2: #d9871f; --gold-3: #ffc98a; --gold-soft: #fdf0df;
  --accent: #f2a65a; --accent-ink: #3a2100; --brand-bg: #3a1f14;
  --brand-gradient: linear-gradient(120deg, #e85d3a, #f2a65a);
}
/* Slate & Amber */
:root[data-preset="slate"] {
  --navy: #1e293b; --navy-2: #263449; --navy-3: #334155;
  --blue: #60a5fa; --blue-2: #3b82f6;
  --primary: #334155; --primary-2: #1e293b; --primary-3: #4b5d76; --primary-soft: #e8edf3;
  --gold: #f5b301; --gold-2: #d99a00; --gold-3: #ffcf4d; --gold-soft: #fff4d6;
  --accent: #f5b301; --accent-ink: #2a2000; --brand-bg: #1e293b;
  --brand-gradient: linear-gradient(120deg, #334155, #f5b301);
}

/* ---- Newer palettes (fresher than the six above) --------------------------- */

/* Modern/vibrant — Indigo Pop: electric indigo + bright cyan */
:root[data-preset="indigo"] {
  --navy: #1b1246; --navy-2: #241a5e; --navy-3: #372a86;
  --blue: #22d3ee; --blue-2: #0891b2;
  --primary: #4f46e5; --primary-2: #3f38c0; --primary-3: #6d64f0; --primary-soft: #e8e7fb;
  --gold: #22d3ee; --gold-2: #06b6d4; --gold-3: #67e8f9; --gold-soft: #dff8fc;
  --accent: #22d3ee; --accent-ink: #062b31; --brand-bg: #1b1246;
  --brand-gradient: linear-gradient(120deg, #4f46e5, #22d3ee);
}
/* Modern/vibrant — Rose Magenta: hot rose + violet */
:root[data-preset="rose"] {
  --navy: #3a0f2e; --navy-2: #4c153c; --navy-3: #6d1f57;
  --blue: #c084fc; --blue-2: #a855f7;
  --primary: #e11d74; --primary-2: #be175d; --primary-3: #f04d97; --primary-soft: #fce4f0;
  --gold: #f472b6; --gold-2: #ec4899; --gold-3: #f9a8d4; --gold-soft: #fde7f3;
  --accent: #f472b6; --accent-ink: #3a0722; --brand-bg: #3a0f2e;
  --brand-gradient: linear-gradient(120deg, #e11d74, #a855f7);
}
/* Deep/premium — Charcoal & Gold: near-black + rich gold */
:root[data-preset="charcoal"] {
  --navy: #17181c; --navy-2: #202227; --navy-3: #2e3138;
  --blue: #d4af37; --blue-2: #b8941f;
  --primary: #2e3138; --primary-2: #202227; --primary-3: #454952; --primary-soft: #eceef1;
  --gold: #d4af37; --gold-2: #b8941f; --gold-3: #e8cd6a; --gold-soft: #f8f1d8;
  --accent: #d4af37; --accent-ink: #241d00; --brand-bg: #17181c;
  --brand-gradient: linear-gradient(120deg, #6b6f78, #d4af37);
}
/* Deep/premium — Wine: deep burgundy + rosé */
:root[data-preset="wine"] {
  --navy: #2c0b16; --navy-2: #3d0f1e; --navy-3: #5a172d;
  --blue: #e6a4b4; --blue-2: #c97a8f;
  --primary: #8b1e3f; --primary-2: #6f1832; --primary-3: #ad3357; --primary-soft: #f6e3e9;
  --gold: #d4a04a; --gold-2: #b8862f; --gold-3: #e8bd6a; --gold-soft: #f7edd9;
  --accent: #d4a04a; --accent-ink: #2b1e00; --brand-bg: #2c0b16;
  --brand-gradient: linear-gradient(120deg, #8b1e3f, #e6a4b4);
}
/* Sport/energetic — Electric: bright blue + lime */
:root[data-preset="electric"] {
  --navy: #0a1733; --navy-2: #0f2148; --navy-3: #16336b;
  --blue: #a3e635; --blue-2: #84cc16;
  --primary: #2563eb; --primary-2: #1d4fc4; --primary-3: #4d82f3; --primary-soft: #e2ebfd;
  --gold: #a3e635; --gold-2: #84cc16; --gold-3: #bef264; --gold-soft: #eef8d8;
  --accent: #a3e635; --accent-ink: #1c2b06; --brand-bg: #0a1733;
  --brand-gradient: linear-gradient(120deg, #2563eb, #a3e635);
}
/* Sport/energetic — Blaze: punchy orange-red + amber */
:root[data-preset="blaze"] {
  --navy: #2b1205; --navy-2: #3a1808; --navy-3: #57240c;
  --blue: #fbbf24; --blue-2: #f59e0b;
  --primary: #f2400c; --primary-2: #cc330a; --primary-3: #ff5f2e; --primary-soft: #ffe4d8;
  --gold: #fbbf24; --gold-2: #f59e0b; --gold-3: #fcd34d; --gold-soft: #fef2d5;
  --accent: #fbbf24; --accent-ink: #331e00; --brand-bg: #2b1205;
  --brand-gradient: linear-gradient(120deg, #f2400c, #fbbf24);
}

/* ============================================================================
   LAYOUT THEMES — genuinely different page STRUCTURES (data-layout on <html>).
   classic  = top nav, full-width stacked (default; minimal overrides here)
   sidebar  = fixed left navigation rail, content offset to the right
   magazine = wide split header, split-screen hero, asymmetric editorial blocks
   Frame markup lives in partials/nav.ejs; these rules re-flow it per layout.
   ========================================================================== */

/* ---------------- SIDEBAR: fixed left rail + offset content ---------------- */
:root[data-layout="sidebar"] { --rail-w: 248px; }
@media (min-width: 921px) {
  /* Admin pages keep the classic top-nav frame (they have their own sidebar). */
  :root[data-layout="sidebar"]:not([data-admin]) body { padding-left: var(--rail-w); }
  :root[data-layout="sidebar"]:not([data-admin]) .site-header {
    position: fixed; inset: 0 auto 0 0; width: var(--rail-w); height: 100vh;
    border-right: 1px solid var(--line); border-bottom: 0; background: var(--surface);
    box-shadow: var(--shadow-sm); z-index: 60;
  }
  :root[data-layout="sidebar"]:not([data-admin]) .header-inner {
    flex-direction: column; align-items: stretch; height: 100%; gap: 0;
    padding: 22px 16px; overflow-y: auto;
  }
  :root[data-layout="sidebar"]:not([data-admin]) .brand { margin-bottom: 22px; }
  :root[data-layout="sidebar"]:not([data-admin]) .brand.has-logo .brand-logo { height: 34px; }
  :root[data-layout="sidebar"]:not([data-admin]) .main-nav {
    flex-direction: column; align-items: stretch; gap: 3px; margin: 0 0 auto;
  }
  :root[data-layout="sidebar"]:not([data-admin]) .main-nav a {
    padding: 11px 13px; border-radius: 10px; text-transform: none; letter-spacing: 0;
    font-size: .95rem; font-weight: 600; color: var(--ink-2);
  }
  :root[data-layout="sidebar"]:not([data-admin]) .main-nav a:hover { background: var(--surface-2); }
  :root[data-layout="sidebar"]:not([data-admin]) .main-nav a.active { background: var(--primary-soft); color: var(--primary); }
  :root[data-layout="sidebar"]:not([data-admin]) .main-nav a.active::after { display: none; }
  /* Turn the "More" dropdown into inline rail items */
  :root[data-layout="sidebar"]:not([data-admin]) .nav-more { flex-direction: column; align-items: stretch; }
  :root[data-layout="sidebar"]:not([data-admin]) .nav-more-btn { display: none; }
  :root[data-layout="sidebar"]:not([data-admin]) .nav-more-menu {
    position: static; display: flex !important; flex-direction: column; box-shadow: none;
    border: 0; padding: 0; background: none; min-width: 0;
  }
  :root[data-layout="sidebar"]:not([data-admin]) .nav-more-menu a { text-transform: none; letter-spacing: 0; font-weight: 600; padding: 11px 13px; }
  :root[data-layout="sidebar"]:not([data-admin]) .header-actions {
    flex-direction: column; align-items: stretch; gap: 8px; margin-top: 16px; padding-top: 16px;
    border-top: 1px solid var(--line);
  }
  :root[data-layout="sidebar"]:not([data-admin]) .header-actions .btn { width: 100%; }
  :root[data-layout="sidebar"]:not([data-admin]) .nav-toggle { display: none; }
  /* Full-height hero; svh keeps it stable on mobile browser chrome. */
  :root[data-layout="sidebar"] .hero { min-height: calc(100svh - var(--header-h)); border-radius: 0; margin: 0; }
  :root[data-layout="sidebar"] .hero h1 { font-size: clamp(2.2rem, 4vw, 3.4rem); }
  /* Content becomes a padded panel */
  :root[data-layout="sidebar"] .container { max-width: 1160px; }
}

/* ---------------- MAGAZINE: wide split header + split hero ----------------- */
:root[data-layout="magazine"] { --maxw: 1240px; }
:root[data-layout="magazine"]:not([data-admin]) .site-header { background: var(--surface); border-bottom: 2px solid var(--ink); }
:root[data-layout="magazine"] .header-inner { height: 74px; }
:root[data-layout="magazine"] .brand { font-size: 1.4rem; }
:root[data-layout="magazine"] .main-nav a { text-transform: uppercase; letter-spacing: .1em; font-size: .78rem; }

/* Split-screen hero: text left, image right */
:root[data-layout="magazine"] .hero {
  min-height: auto; display: grid; grid-template-columns: 1.05fr .95fr; gap: 0; text-align: left;
  padding: 0; background: var(--navy); align-items: stretch; overflow: hidden;
}
:root[data-layout="magazine"] .hero::before,
:root[data-layout="magazine"] .hero::after { display: none; }
:root[data-layout="magazine"] .hero .container {
  max-width: none; padding: clamp(48px, 7vw, 96px) clamp(28px, 5vw, 72px);
  display: flex; flex-direction: column; justify-content: center; align-items: flex-start;
}
:root[data-layout="magazine"] .hero h1 { font-size: clamp(2.6rem, 5.5vw, 4.6rem); text-align: left; }
:root[data-layout="magazine"] .hero p.lead { margin-left: 0; text-align: left; }
:root[data-layout="magazine"] .hero-cta { justify-content: flex-start; }
/* The image half */
:root[data-layout="magazine"] .hero::marker { content: none; }
:root[data-layout="magazine"] .hero .hero-visual {
  background: url("/static/img/hero-badminton.jpg") center / cover no-repeat;
  position: relative;
}
:root[data-layout="magazine"] .hero .hero-visual::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, var(--navy), transparent 30%);
}
/* Asymmetric editorial section titles: left-aligned with a rule */
:root[data-layout="magazine"] .section-title { text-align: left; display: flex; align-items: center; gap: 16px; }
:root[data-layout="magazine"] .section-title::before {
  content: ""; width: 40px; height: 4px; background: var(--primary); border-radius: 3px; flex: 0 0 auto;
}
:root[data-layout="magazine"] .section-sub { text-align: left; margin-left: 0; }
/* Asymmetric feature cards: first card spans wider */
:root[data-layout="magazine"] .feature-cards { grid-template-columns: 1.4fr 1fr 1fr; }
:root[data-layout="magazine"] .card { border-radius: 4px; }
:root[data-layout="magazine"] .card::before { display: none; }
:root[data-layout="magazine"] .stats-band { border-top: 2px solid var(--ink); }

/* Both alt layouts: page-hero (inner pages) left-aligned to match */
:root[data-layout="magazine"] .page-hero .container { text-align: left; }

/* Magazine hero collapses to stacked (text over image) on smaller screens */
/* Consolidated to the canonical md (860px) breakpoint — was 760px. */
@media (max-width: 860px) {
  :root[data-layout="magazine"] .hero { grid-template-columns: 1fr; }
  /* aspect-ratio (not a fixed min-height) so the stacked image keeps its shape
     instead of being squashed into a 220px band. Capped so it never dominates. */
  :root[data-layout="magazine"] .hero .hero-visual {
    order: -1; aspect-ratio: 16 / 10; max-height: 42vh; min-height: 200px;
  }
  :root[data-layout="magazine"] .hero .hero-visual::after {
    background: linear-gradient(180deg, transparent 40%, var(--navy));
  }
  /* Feature cards: the magazine base rule (0,2,0) pins 3 columns and beats the
     plain .cards media rules — reset it here (matching specificity) so cards
     step down to 2, then 1 at 560px below. */
  :root[data-layout="magazine"] .feature-cards { grid-template-columns: repeat(2, 1fr); }
}

/* Magazine editorial blocks (homepage) */
.mag-editorial { display: grid; grid-template-columns: 1.6fr 1fr; gap: 40px; align-items: start; }
.mag-lead { font-size: 1.18rem; line-height: 1.7; color: var(--ink-2); }
.mag-lead p:first-child::first-letter {
  font-family: var(--font-display); font-size: 3.4em; float: left; line-height: .8;
  margin: 6px 12px 0 0; color: var(--primary);
}
.mag-aside { position: sticky; top: 90px; }
.mag-quote { border-left: 4px solid var(--primary); padding: 6px 0 6px 22px; }
.mag-quote-mark { font-family: var(--font-display); font-size: 3rem; color: var(--primary); line-height: .5; display: block; }
.mag-quote p { font-size: 1.15rem; font-style: italic; color: var(--ink); margin: 8px 0 0; }
.hero .hero-visual { display: none; } /* only shown in magazine layout (below) */
:root[data-layout="magazine"] .hero .hero-visual { display: block; }

/* Sidebar dashboard homepage */
.dash-grid { display: grid; grid-template-columns: 1.7fr 1fr; gap: 20px; align-items: start; }
.dash-main, .dash-side { display: flex; flex-direction: column; gap: 20px; }
.dash-card { padding: 22px; }
.dash-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 14px; padding-bottom: 12px; border-bottom: 1px solid var(--line); }
.dash-card-head h2 { font-size: 1.15rem; margin: 0; }
.quick-links { display: flex; flex-direction: column; gap: 4px; }
.quick-links a { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-radius: 10px; color: var(--ink-2); font-weight: 600; transition: background .13s; }
.quick-links a:hover { background: var(--surface-2); color: var(--primary); text-decoration: none; }
.quick-links a span { font-size: 1.2rem; }
.grade-grid-mini { grid-template-columns: repeat(4, 1fr); gap: 8px; }
.grade-grid-mini .grade-chip { padding: 12px 6px; }
.grade-grid-mini .g { font-size: 1.2rem; }

/* Consolidated to the canonical lg (1024px) grid-collapse breakpoint — was 920px. */
@media (max-width: 1024px) {
  .mag-editorial, .dash-grid { grid-template-columns: 1fr; }
  .mag-aside { position: static; }
}

/* ---- UI variant: corner style ---- */
:root[data-corners="sharp"] {
  --radius-sm: 3px; --radius: 4px; --radius-lg: 6px; --radius-xl: 8px;
}
/* ---- UI variant: density ---- */
:root[data-density="compact"] {
  --header-h: 58px;
}
:root[data-density="compact"] .section { padding: clamp(32px, 5vw, 52px) 0; }
:root[data-density="compact"] .section-tight { padding: clamp(24px, 4vw, 38px) 0; }
:root[data-density="compact"] .card-body { padding: 16px; }
:root[data-density="compact"] .container { padding-left: 16px; padding-right: 16px; }
:root[data-density="compact"] .member-card { padding: 10px 12px; }
:root[data-density="compact"] table.data th, :root[data-density="compact"] table.data td { padding: 9px 12px; }

* { box-sizing: border-box; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font-family: var(--font);
  color: var(--ink);
  background: var(--bg);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  transition: background .3s var(--ease), color .3s var(--ease);
  overflow-x: hidden;
  /* Sticky footer: body is a full-height flex column of header / .page-wrap /
     footer. .page-wrap flex-grows to absorb the slack on short pages (see below),
     so the footer pins to the bottom and the leftover space carries the PAGE
     background instead of showing through above the dark footer. */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100svh;
}
/* The content region between header and footer. flex:1 makes it eat the slack on
   short pages; its background is the page background, so a short page shows page
   colour (not bare body / a mismatched band) all the way down to the footer. */
.page-wrap { flex: 1 0 auto; background: var(--bg); }
a { color: var(--primary); text-decoration: none; transition: color .15s; }
:root[data-theme="dark"] a { color: var(--red-3); }
a:hover { text-decoration: underline; }
img { max-width: 100%; display: block; }
h1, h2, h3, h4 { line-height: 1.08; margin: 0 0 .5em; font-weight: 800; }
/* Display headings use Bebas Neue (condensed) — needs positive tracking + bigger size */
.hero h1, .section-title, .page-header h1, .error-page .code, .stat-num, .grade-chip .g,
.display { font-family: var(--font-display); font-weight: 400; letter-spacing: .01em; }

/* width:100% is required because body is a flex column (sticky footer): without
   it, .container is a flex item that shrinks to its content width, so pages with
   narrow content rendered narrower and off-centre. With it, .container always
   fills up to --maxw and centres consistently. */
.container { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 20px; }
.back-link { display: inline-block; font-size: .9rem; font-weight: 600; color: var(--muted); }
.hide-mobile { display: inline-flex; }

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; scroll-behavior: auto !important; }
}

/* ============================ Header ==================================== */
.site-header {
  position: sticky; top: 0; z-index: 60;
  background: color-mix(in srgb, var(--surface) 82%, transparent);
  border-bottom: 1px solid transparent;
  transition: border-color .25s, box-shadow .25s, background .25s;
}
/* The frosted-glass blur lives on a pseudo-element, NOT on .site-header itself.
   A non-none backdrop-filter makes an element the containing block for its
   position:fixed descendants (same as transform) — with it on the header, the
   mobile nav drawer resolved top/right/bottom against the 68px header box
   instead of the viewport and collapsed into an empty strip. */
.site-header::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  backdrop-filter: saturate(1.4) blur(14px);
  -webkit-backdrop-filter: saturate(1.4) blur(14px);
}
.site-header.is-scrolled { border-bottom-color: var(--line); box-shadow: var(--shadow-sm); }
.header-inner { display: flex; align-items: center; gap: 18px; height: var(--header-h); }
.brand { display: flex; align-items: center; gap: 10px; font-weight: 900; font-size: 1.3rem; color: var(--ink); letter-spacing: -0.03em; }
.brand:hover { text-decoration: none; }
/* Logo image (when present) replaces the text lockup */
.brand-logo { height: 42px; width: auto; display: block; }
.brand-logo-badge { height: 44px; width: auto; }
.has-logo .brand-text, .has-logo .brand-mark { display: none; }
.brand-mark {
  width: 36px; height: 36px; border-radius: 11px; flex: 0 0 auto;
  background: linear-gradient(140deg, var(--navy-3), var(--navy));
  color: var(--gold); display: flex; align-items: center; justify-content: center; font-size: 1.15rem;
  box-shadow: inset 0 0 0 1.5px rgba(245,179,1,.55), var(--shadow-xs);
}
/* Footer wide-logo fallback */
.footer-logo { height: 46px; width: auto; margin-bottom: 4px; }

.main-nav { display: flex; gap: 4px; margin: 0 auto; align-items: center; }
.main-nav a, .nav-more-btn {
  color: var(--navy); font-weight: 700; font-size: .95rem; text-transform: uppercase; letter-spacing: .06em;
  padding: 9px 14px; border-radius: 10px; transition: background .15s, color .15s; white-space: nowrap;
  background: none; border: 0; cursor: pointer; font-family: inherit;
}
:root[data-theme="dark"] .main-nav a, :root[data-theme="dark"] .nav-more-btn { color: var(--ink-2); }
.main-nav a:hover, .nav-more-btn:hover { color: var(--primary); text-decoration: none; }
.main-nav a.active { color: var(--primary); }
.main-nav a.active::after {
  content: ""; display: block; height: 2px; border-radius: 2px; background: var(--primary); margin-top: 3px;
}

/* MORE dropdown */
.nav-more { position: relative; display: flex; align-items: center; }
.nav-more.active > .nav-more-btn { color: var(--primary); }
.nav-more-btn .caret { font-size: .7em; transition: transform .2s; display: inline-block; }
.nav-more.open .nav-more-btn .caret { transform: rotate(180deg); }
.nav-more-menu {
  position: absolute; top: calc(100% + 10px); right: 0; min-width: 220px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-lg); padding: 10px; display: none; flex-direction: column; gap: 2px; z-index: 90;
}
.nav-more.open .nav-more-menu { display: flex; animation: dropIn .18s var(--ease); }
@keyframes dropIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
.nav-more-menu a {
  display: block; padding: 11px 14px; border-radius: 10px; margin: 0;
  color: var(--navy); font-weight: 700; font-size: .82rem; text-transform: uppercase; letter-spacing: .06em;
}
:root[data-theme="dark"] .nav-more-menu a { color: var(--ink-2); }
.nav-more-menu a:hover { background: var(--surface-2); color: var(--primary); text-decoration: none; }
.nav-more-menu a.active { color: var(--primary); background: var(--primary-soft); }

.drawer-head, .drawer-actions { display: none; }

.header-actions { display: flex; gap: 8px; align-items: center; }
.theme-toggle {
  background: var(--surface-2); border: 1px solid var(--line); color: var(--ink);
  width: 44px; height: 44px; border-radius: 12px; cursor: pointer; font-size: 1.05rem;
  display: inline-flex; align-items: center; justify-content: center; transition: background .15s, transform .15s;
}
.theme-toggle:hover { background: var(--primary-soft); transform: rotate(-8deg); }

/* Hamburger */
.nav-toggle {
  display: none; width: 44px; height: 44px; border: 1px solid var(--line); background: var(--surface-2);
  border-radius: 12px; cursor: pointer; flex-direction: column; align-items: center; justify-content: center; gap: 5px;
}
.nav-toggle span { display: block; width: 20px; height: 2px; background: var(--ink); border-radius: 2px; transition: transform .28s var(--ease), opacity .2s; }
body.nav-open .nav-toggle span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
body.nav-open .nav-toggle span:nth-child(2) { opacity: 0; }
body.nav-open .nav-toggle span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Must stay BELOW .site-header's z-index (60). The drawer is a child of the
   header, and the header is position:sticky with a z-index — that makes it a
   stacking context, so the drawer's z-index:80 is only ever resolved *inside*
   the header and cannot rise above 60. A backdrop at 70 therefore painted over
   the drawer instead of behind it, greying out the whole menu. */
.nav-backdrop {
  position: fixed; inset: 0; background: rgba(6, 13, 28, 0.55); backdrop-filter: blur(2px);
  opacity: 0; visibility: hidden; transition: opacity .3s, visibility .3s; z-index: 50;
}
body.nav-open .nav-backdrop { opacity: 1; visibility: visible; }

/* ============================ Buttons ================================== */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  background: linear-gradient(135deg, var(--primary-3), var(--primary)); color: #fff; font-weight: 700;
  padding: 12px 22px; border-radius: 12px; border: 0; cursor: pointer; font-size: .95rem; line-height: 1;
  min-height: 46px; transition: transform .14s var(--ease), box-shadow .18s, filter .15s;
  box-shadow: 0 8px 26px rgba(226,35,26,.30);
}
.btn:hover { text-decoration: none; transform: translateY(-2px); filter: brightness(1.05); box-shadow: 0 12px 34px rgba(226,35,26,.40); }
.btn:active { transform: translateY(0); }
.btn-sm { padding: 8px 14px; font-size: .85rem; min-height: 38px; box-shadow: none; }
.btn-ghost { background: transparent; color: var(--ink); border: 1px solid var(--line-strong); box-shadow: none; }
.btn-ghost:hover { background: var(--surface-2); filter: none; }
.btn-navy { background: linear-gradient(135deg, var(--navy-3), var(--navy)); color: #fff; box-shadow: var(--shadow-sm); }
.btn-navy:hover { filter: brightness(1.12); }
.btn-danger { background: linear-gradient(135deg, #e05a4d, #c0392b); color: #fff; box-shadow: none; }
.btn-block { width: 100%; }

/* ============================ Flash ==================================== */
.flash { padding: 13px 18px; border-radius: 12px; margin: 18px 0; font-weight: 600; display: flex; gap: 8px; }
.flash-success { background: #e3f7ec; color: #14663a; border: 1px solid #b7e6cb; }
.flash-error { background: #fdecec; color: #b0332a; border: 1px solid #f4c3bf; }
:root[data-theme="dark"] .flash-success { background: rgba(20,102,58,.24); color: #7ee0a6; border-color: rgba(20,102,58,.5); }
:root[data-theme="dark"] .flash-error { background: rgba(176,51,42,.24); color: #f4a19a; border-color: rgba(176,51,42,.5); }

/* ============================ Hero ===================================== */
.hero {
  position: relative; color: #fff; text-align: center; overflow: hidden;
  /* Full viewport height minus the sticky header. svh keeps it stable on mobile. */
  min-height: calc(100svh - var(--header-h));
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: clamp(48px, 8vw, 96px) 0;
  /* Hero photo lives in one variable, set by the admin's Hero image choice via
     the [data-hero-image] rules below. The overlay above it tints with the active
     colour theme: the directional darkening uses the theme's --navy, and the
     top-right glow uses --primary (both via color-mix), so the wash matches the
     palette instead of a fixed grey/red. */
  --hero-image: url("/static/img/hero-badminton.jpg");
  --hero-tint: color-mix(in srgb, var(--navy) 88%, transparent);
  --hero-glow: color-mix(in srgb, var(--primary) 10%, transparent);
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--navy) 8%, transparent) 0%, transparent 45%, color-mix(in srgb, var(--navy) 24%, transparent) 100%),
    radial-gradient(1000px 500px at 80% 20%, var(--hero-glow), transparent 66%),
    var(--hero-image) center 42% / cover no-repeat,
    linear-gradient(150deg, var(--navy) 0%, var(--navy-2) 52%, #06132b 100%);
  background-attachment: scroll;
}
/* Crisp text rendering over the photo (avoids a fuzzy/pixelated look on the
   large condensed Bebas Neue heading). A tight, low-blur shadow reads sharper
   than a wide soft one. */
.hero h1, .hero p.lead, .hero .eyebrow {
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}
.hero h1 {
  text-shadow: 0 1px 2px rgba(6,16,32,0.45), 0 3px 24px rgba(6,16,32,0.35);
}
.hero p.lead, .hero .eyebrow { text-shadow: 0 1px 8px rgba(6,16,32,0.5); }
/* Dark-mode: a deeper overlay so the theme still reads as dark. Same theme-tinted
   approach as light mode, but heavier — the darkening layer mixes the theme --navy
   with black, and the glow is stronger. */
:root[data-theme="dark"] .hero {
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--navy) 55%, #000 45%) 0%, color-mix(in srgb, var(--navy) 42%, #000 58%) 45%, color-mix(in srgb, var(--navy) 75%, #000 25%) 100%),
    radial-gradient(1000px 500px at 80% 20%, color-mix(in srgb, var(--primary) 22%, transparent), transparent 60%),
    var(--hero-image) center 42% / cover no-repeat,
    linear-gradient(150deg, var(--navy) 0%, var(--navy-2) 52%, #06132b 100%);
}
/* Fallback for browsers without svh support */
@supports not (min-height: 100svh) {
  .hero { min-height: calc(100vh - var(--header-h)); }
}
/* Admin-selectable hero photo (data-hero-image on <html>). Sets the --hero-image
   variable the .hero background above consumes; 'badminton' is the default and
   needs no rule. Keeps the URL out of inline styles (CSP-friendly). */
:root[data-hero-image="smash"] .hero { --hero-image: url("/static/img/test-hero-1.jpg"); }
:root[data-hero-image="racket"] .hero { --hero-image: url("/static/img/test-hero-2.jpg"); }
/* Custom upload: head.ejs sets --hero-image-custom inline on <html> (its URL is
   dynamic, so it can't be a static rule). This rule pulls that inherited value
   into .hero, overriding .hero's own default --hero-image declaration. */
:root[data-hero-image="custom"] .hero { --hero-image: var(--hero-image-custom, url("/static/img/hero-badminton.jpg")); }
/* UI variant: solid / gradient hero (no photo) */
:root[data-hero="solid"] .hero,
:root[data-hero="solid"][data-theme="dark"] .hero {
  background:
    radial-gradient(1100px 520px at 82% -12%, color-mix(in srgb, var(--primary) 42%, transparent), transparent 58%),
    radial-gradient(900px 520px at 6% 118%, color-mix(in srgb, var(--blue) 40%, transparent), transparent 55%),
    linear-gradient(150deg, var(--navy) 0%, var(--navy-2) 52%, var(--navy-3) 100%);
}
/* Scroll cue at the bottom of the full-height hero */
.hero .scroll-cue {
  position: absolute; left: 50%; bottom: 22px; transform: translateX(-50%);
  color: rgba(255,255,255,.7); font-size: 1.4rem; animation: bob 1.8s ease-in-out infinite;
  /* Sized to a 44px tap target — the bare glyph was only 11px wide. The box is
     centred via flex rather than padding so the bob keyframes (which bake in
     translateX(-50%)) keep working. */
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
}
/* Lift the scroll cue clear of the fixed sponsor bar (~46px, more on mobile)
   when one is present, so the arrow isn't tucked behind it. */
body.has-sponsor .hero .scroll-cue { bottom: 60px; }
@keyframes bob { 0%,100% { transform: translateX(-50%) translateY(0); } 50% { transform: translateX(-50%) translateY(7px); } }
.hero::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: .5;
  background-image:
    linear-gradient(rgba(255,255,255,.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.05) 1px, transparent 1px);
  background-size: 46px 46px;
  mask-image: radial-gradient(circle at 50% 40%, #000 0%, transparent 72%);
  -webkit-mask-image: radial-gradient(circle at 50% 40%, #000 0%, transparent 72%);
}
.hero::after {
  content: "🏸"; position: absolute; right: -30px; bottom: -50px; font-size: clamp(12rem, 30vw, 24rem);
  opacity: 0.06; transform: rotate(-14deg); pointer-events: none;
  animation: floaty 9s ease-in-out infinite;
}
@keyframes floaty { 0%,100% { transform: rotate(-14deg) translateY(0); } 50% { transform: rotate(-10deg) translateY(-18px); } }
.hero .container { position: relative; z-index: 1; }
.hero .eyebrow {
  display: inline-flex; align-items: center; gap: 8px; background: rgba(255,255,255,0.10); color: #fff;
  font-weight: 700; font-size: .78rem; letter-spacing: .14em; text-transform: uppercase;
  padding: 7px 16px; border-radius: 30px; margin-bottom: 22px; border: 1px solid rgba(255,255,255,0.22);
  backdrop-filter: blur(4px);
}
.hero h1 { font-size: clamp(3rem, 8vw, 5.5rem); margin-bottom: .25em; font-weight: 400; letter-spacing: .02em; line-height: .98; }
.hero h1 .gold {
  background: var(--brand-gradient); -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent; -webkit-text-stroke: 0;
  /* keep a subtle shadow readable on the gradient text */
  text-shadow: 0 2px 18px rgba(6,16,32,0.30);
  padding-bottom: .04em; /* prevent descender clipping on the gradient text */
}
.hero p.lead { font-size: var(--fs-lead); max-width: 660px; margin: 0 auto 2rem; color: rgba(255,255,255,.9); }
.hero-cta { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
/* Bigger, bolder hero CTAs than the site-wide .btn — they're the primary call to
   action. Scoped here so form/admin/pager buttons keep their normal size. */
.hero-cta .btn { font-size: 1.08rem; padding: 15px 30px; min-height: 54px; border-radius: 14px; }
.hero .btn.btn-ghost { color: #fff; border-color: rgba(255,255,255,.35); background: rgba(255,255,255,.06); backdrop-filter: blur(4px); }
.hero .btn.btn-ghost:hover { background: rgba(255,255,255,.16); }

/* Stats inside the hero: the shared .stats-grid markup restyled for the photo.
   The band's dark-on-light treatment is unreadable here, so numbers go solid
   white and the dividers translucent. */
.hero .stats-grid { max-width: 860px; margin: clamp(40px, 6vh, 72px) auto 0; padding: 0; }
.hero .stat-item::after { background: rgba(255,255,255,.22); }
.hero .stat-item .stat-num {
  /* Override the band's gradient-clipped text — a gradient reads as mud on the photo. */
  background: none; -webkit-background-clip: border-box; background-clip: border-box;
  color: #fff; -webkit-text-fill-color: #fff;
  font-size: clamp(1.75rem, 3.2vw, 2.5rem);
  text-shadow: 0 1px 2px rgba(6,16,32,0.45), 0 3px 20px rgba(6,16,32,0.4);
}
:root[data-theme="dark"] .hero .stat-item .stat-num { background: none; color: #fff; -webkit-text-fill-color: #fff; }
.hero .stat-item .stat-lbl {
  color: rgba(255,255,255,.82); font-size: .82rem; letter-spacing: .04em;
  text-shadow: 0 1px 8px rgba(6,16,32,0.5);
}

/* Scroll-reveal */
[data-reveal] { opacity: 0; transform: translateY(22px); transition: opacity .6s var(--ease), transform .6s var(--ease); }
[data-reveal].is-revealed { opacity: 1; transform: none; }

/* ============================ Stats =================================== */
/* Rendered inside the hero on every homepage layout (views/partials/stats.ejs).
   Base rules are colour-neutral; the hero-scoped overrides above handle the
   white-on-photo treatment. */
.stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; text-align: center; }
.stat-item { padding: 8px; position: relative; }
.stat-item:not(:last-child)::after { content: ""; position: absolute; right: 0; top: 20%; height: 60%; width: 1px; background: var(--line); }
.stat-item .stat-num {
  font-size: clamp(2rem, 4.5vw, 2.9rem); font-weight: 900; letter-spacing: -0.03em; line-height: 1;
  background: linear-gradient(135deg, var(--navy-3), var(--navy)); -webkit-background-clip: text; background-clip: text; color: transparent;
}
:root[data-theme="dark"] .stat-item .stat-num { background: linear-gradient(135deg, var(--gold-3), var(--gold)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.stat-item .stat-lbl { color: var(--muted); font-size: .9rem; margin-top: 8px; font-weight: 600; }

/* ============================ Sections ================================= */
.section { padding: clamp(48px, 8vw, 76px) 0; }
.section-tight { padding: clamp(36px, 6vw, 52px) 0; }
.section-alt { background: var(--surface); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.section-title { text-align: center; margin-bottom: 10px; font-size: var(--fs-h2); }
.section-sub { text-align: center; color: var(--muted); max-width: 620px; margin: 0 auto 42px; font-size: 1.05rem; }
/* Pagination */
.pager { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 6px; margin-top: 28px; }
.pager-btn, .pager-num {
  display: inline-flex; align-items: center; justify-content: center; min-width: 44px; height: 44px;
  padding: 0 12px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface);
  color: var(--ink-2); font-weight: 600; font-size: .9rem; text-decoration: none;
}
.pager-btn:hover, .pager-num:hover { background: var(--surface-2); border-color: var(--primary); color: var(--primary); }
.pager-num.is-current { background: var(--primary); border-color: var(--primary); color: #fff; }
.pager-num.is-current:hover { background: var(--primary); color: #fff; }
.pager-btn.is-disabled { opacity: .45; pointer-events: none; }
.pager-gap { padding: 0 4px; color: var(--muted); }

/* Players intro: .section-sub still caps at 620px, so opt out here. The hero
   subtitle no longer needs a nowrap hack — it wraps naturally when narrow. */
.players-intro { max-width: none; }

.page-header { background: linear-gradient(180deg, var(--surface), var(--bg)); border-bottom: 1px solid var(--line); padding: clamp(36px, 6vw, 56px) 0; }
.page-header h1 { font-size: var(--fs-h1); }
.prose { max-width: 780px; }
.prose p { margin: 0 0 1em; }
.prose h2, .prose h3 { margin-top: 1.2em; }

/* ============================ Cards / grid ============================= */
.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px; }
.card {
  position: relative; background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-sm); overflow: hidden; transition: transform .2s var(--ease), box-shadow .2s, border-color .2s;
}
.card::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit; padding: 1px; pointer-events: none; opacity: 0;
  background: linear-gradient(135deg, var(--primary), transparent 42%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude; transition: opacity .2s;
}
.card:hover { transform: translateY(-5px); box-shadow: var(--shadow-lg); }
.card:hover::before { opacity: 1; }
.card-body { padding: 24px; }
.card h3 { font-size: 1.22rem; margin-bottom: .35em; }
.card p { color: var(--muted); margin: 0 0 1.1em; }
.card-icon {
  width: 54px; height: 54px; border-radius: 15px; display: flex; align-items: center; justify-content: center;
  font-size: 1.6rem; background: linear-gradient(140deg, var(--gold-soft), #ffe9b0); margin-bottom: 16px; box-shadow: var(--shadow-xs);
}
:root[data-theme="dark"] .card-icon { background: rgba(245,179,1,.14); }
/* min-height gives a 44px tap target; the negative bottom margin keeps the
   card's visual padding unchanged despite the taller box. */
.card-link { display: inline-flex; align-items: center; gap: 6px; font-weight: 700; font-size: .9rem; color: var(--primary); min-height: 44px; margin-bottom: -10px; }
:root[data-theme="dark"] .card-link { color: var(--red-3); }
.card-link .arrow { transition: transform .18s; }
.card:hover .card-link .arrow { transform: translateX(4px); }

/* Player grid */
.player-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; }
.player-avatar {
  width: 96px; height: 96px; border-radius: 50%; margin: 24px auto 14px;
  background: linear-gradient(140deg, var(--navy-3), var(--navy)); color: var(--gold);
  display: flex; align-items: center; justify-content: center;
  font-size: 2.1rem; font-weight: 800; overflow: hidden;
  box-shadow: inset 0 0 0 3px rgba(245,179,1,.7), var(--shadow-sm);
}
.player-avatar img { width: 100%; height: 100%; object-fit: cover; }
.badge-row { display: flex; gap: 6px; justify-content: center; flex-wrap: wrap; margin-top: 8px; }
.badge {
  display: inline-block; background: var(--surface-2); color: var(--ink-2);
  font-size: .72rem; font-weight: 700; padding: 4px 11px; border-radius: 20px; letter-spacing: .01em;
}
.badge-accent { background: var(--gold-soft); color: var(--accent-ink); }
:root[data-theme="dark"] .badge-accent { background: rgba(245,179,1,.15); color: var(--gold); }
.badge-grade { background: linear-gradient(135deg, var(--navy-3), var(--navy)); color: var(--gold); }

/* ============================ About value cards ======================= */
.about-prose { max-width: 800px; font-size: 1.12rem; color: var(--ink-2); }
.about-prose p { margin: 0 0 1.3em; }
.value-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; max-width: 900px; margin: 44px auto 0; }
/* Surface-card family: .value-card / .dash-card / .member-card all share the same
   surface/border/radius/shadow-xs quartet, differing only in padding & inner layout.
   Grouped here so the shared look lives in one place (base + per-card padding). */
.value-card, .dash-card, .member-card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-xs); transition: transform .16s var(--ease), box-shadow .16s, border-color .16s;
}
.value-card { padding: 26px; }
.value-card:hover { transform: translateY(-4px); box-shadow: var(--shadow); border-color: rgba(31,143,224,.4); }
.value-card h3 { font-family: var(--font-display); font-weight: 400; font-size: 1.5rem; letter-spacing: .03em; color: var(--navy); margin-bottom: 8px; }
:root[data-theme="dark"] .value-card h3 { color: var(--blue); }
.value-card p { color: var(--muted); margin: 0; font-size: 1rem; }

/* ============================ Page hero band (Lovable) ================= */
/* Navy-tinted band derived from the active preset's --navy (not hardcoded),
   so it follows rednavy/ocean/emerald/etc. rather than fighting them. */
.page-hero {
  position: relative; overflow: hidden; padding: clamp(48px, 8vw, 88px) 0 clamp(40px, 7vw, 72px);
  /* --navy is near-black, so a white mix greys out; --blue carries the hue
     and a light --navy pass adds depth at the edges. */
  background:
    radial-gradient(900px 420px at 88% 6%, color-mix(in srgb, var(--blue) 30%, transparent), transparent 64%),
    linear-gradient(120deg,
      color-mix(in srgb, var(--blue) 26%, var(--surface)) 0%,
      color-mix(in srgb, var(--blue) 14%, var(--surface)) 50%,
      color-mix(in srgb, var(--navy) 12%, var(--surface)) 100%);
  border-bottom: 1px solid var(--line);
}
:root[data-theme="dark"] .page-hero {
  background:
    radial-gradient(900px 400px at 90% 10%, color-mix(in srgb, var(--navy) 45%, transparent), transparent 60%),
    linear-gradient(120deg,
      color-mix(in srgb, var(--navy) 70%, var(--bg)) 0%,
      color-mix(in srgb, var(--navy) 50%, var(--bg)) 100%);
}
/* Detail-page back link sits in the eyebrow slot. */
.page-hero-back { display: inline-block; font-size: .9rem; font-weight: 700; color: var(--primary); text-decoration: none; margin-bottom: 6px; }
.page-hero-back:hover { text-decoration: underline; }
.page-hero .meta { color: var(--navy-3); }
:root[data-theme="dark"] .page-hero .meta { color: var(--ink-2); }
/* CMS prose used as the hero subtitle inherits the sub styling. */
.page-hero .prose.page-hero-sub p { margin: 0; }

.page-hero-eyebrow { display: inline-block; color: var(--primary); font-weight: 800; font-size: .8rem; letter-spacing: .22em; text-transform: uppercase; margin-bottom: 14px; }
:root[data-theme="dark"] .page-hero-eyebrow { color: var(--primary-3); }
.page-hero h1 { font-family: var(--font-display); font-weight: 400; font-size: clamp(2.6rem, 7vw, 4.4rem); letter-spacing: .02em; color: var(--navy); line-height: .95; margin-bottom: .3em; }
:root[data-theme="dark"] .page-hero h1 { color: var(--ink); }
/* No max-width: the subtitle spans the full .container width (which already caps
   the page at --maxw and adds the side padding), so short intros sit on one line
   instead of wrapping early against a 620px cap. */
.page-hero-sub { color: var(--navy-3); font-size: clamp(1.05rem, 2.2vw, 1.3rem); max-width: none; margin: 0; }
:root[data-theme="dark"] .page-hero-sub { color: var(--ink-2); }

/* ============================ Members grid ============================ */
.filter-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); padding: 18px; margin-bottom: 26px; }
.members-toolbar { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
.members-toolbar .pt-search { flex: 1 1 240px; min-width: 200px; }
.members-toolbar input[type="search"] {
  width: 100%; padding: 12px 16px; border: 1px solid var(--line-strong); border-radius: 30px;
  font-size: 16px; font-family: inherit; background: var(--bg); color: var(--ink);
}
.members-toolbar input:focus { outline: none; border-color: var(--blue); box-shadow: 0 0 0 4px rgba(31,143,224,.15); }
.chip-row { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 2px; -webkit-overflow-scrolling: touch; scrollbar-width: thin; flex: 2 1 300px; }
.chip {
  white-space: nowrap; padding: 8px 16px; border-radius: 30px; border: 1px solid var(--line-strong);
  color: var(--navy); font-weight: 700; font-size: .78rem; text-transform: uppercase; letter-spacing: .04em; background: var(--surface);
}
:root[data-theme="dark"] .chip { color: var(--ink-2); }
.chip:hover { text-decoration: none; border-color: var(--blue); color: var(--blue-2); }
.chip.active { background: var(--navy); color: #fff; border-color: var(--navy); }
:root[data-theme="dark"] .chip.active { background: var(--blue); border-color: var(--blue); }
.members-count { color: var(--muted); font-size: .9rem; font-weight: 600; white-space: nowrap; }

.member-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
.member-card { display: flex; align-items: center; gap: 14px; padding: 14px 16px; }
.member-card:hover { transform: translateY(-3px); box-shadow: var(--shadow); border-color: rgba(31,143,224,.4); }
.member-avatar {
  width: 52px; height: 52px; border-radius: 50%; flex: 0 0 auto; overflow: hidden;
  background: linear-gradient(140deg, var(--blue), var(--blue-2)); display: flex; align-items: center; justify-content: center;
  box-shadow: inset 0 0 0 2px rgba(255,255,255,.35);
}
/* Bias the crop toward the top of the photo — headshots put the face in the
   upper portion, so centred cropping was cutting foreheads/faces off. */
.member-avatar img { width: 100%; height: 100%; object-fit: cover; object-position: center 25%; }
.member-initials { color: #fff; font-weight: 800; font-size: .95rem; letter-spacing: .02em; }
.member-info { min-width: 0; }
.member-name { font-weight: 800; font-size: 1rem; color: var(--ink); line-height: 1.15; }
.member-city { color: var(--muted); font-size: .76rem; font-weight: 700; letter-spacing: .05em; margin-top: 2px; }
.member-role { color: var(--blue-2); font-size: .72rem; font-weight: 700; margin-top: 3px; }
:root[data-theme="dark"] .member-role { color: var(--blue); }

/* ============================ Players directory ======================= */
.players-toolbar { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 16px; }
.players-toolbar .pt-search { flex: 1 1 260px; min-width: 220px; }
.players-toolbar input[type="search"], .players-toolbar select {
  width: 100%; padding: 11px 14px; border: 1px solid var(--line-strong); border-radius: 12px;
  font-size: 16px; font-family: inherit; background: var(--surface); color: var(--ink);
}
.players-toolbar select { width: auto; min-width: 130px; }
/* Sort labels ("Grade (lowest first)") are longer than the grade/club options. */
.players-toolbar .pt-sort { min-width: 200px; }
.players-toolbar input:focus, .players-toolbar select:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 4px rgba(226,35,26,.14); }
.results-count { color: var(--muted); font-size: .92rem; margin: 0 0 14px; }
.empty-state { text-align: center; color: var(--muted); padding: 40px; background: var(--surface); border: 1px dashed var(--line-strong); border-radius: var(--radius); }

.players-table th a {
  color: var(--muted); font-weight: 700; text-transform: uppercase; font-size: .74rem; letter-spacing: .05em;
  /* Fill the th's padding box so the tap target is the full header cell rather
     than just the ~14px-tall glyphs. Negative margins absorb the th's 13px 16px
     padding, so the header row doesn't grow. */
  display: inline-flex; align-items: center; min-height: 44px;
  margin: -13px -16px; padding: 0 16px;
}
.players-table th a:hover { color: var(--primary); text-decoration: none; }
:root[data-density="compact"] .players-table th a { margin: -9px -12px; padding: 0 12px; }
.player-row { display: inline-flex; align-items: center; gap: 10px; font-weight: 600; color: var(--ink); }
.player-row:hover { text-decoration: none; color: var(--primary); }
.player-dot {
  width: 30px; height: 30px; border-radius: 50%; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  font-size: .82rem; font-weight: 800; color: #fff; background: linear-gradient(135deg, var(--blue), var(--blue-2));
}
/* Grade badges coloured by tier (logo palette) */
.grade-badge { display: inline-block; min-width: 40px; text-align: center; font-weight: 800; font-size: .78rem; padding: 3px 10px; border-radius: 20px; color: #fff; }
.grade-Aplus { background: var(--red); }
.grade-A { background: #f7943d; }
.grade-Bplus { background: var(--gold-2); color: #241a00; }
.grade-B { background: var(--gold); color: #241a00; }
.grade-Cplus { background: #1f8f77; }
.grade-C { background: #2fae6b; }
.grade-D { background: var(--blue); }
.grade-LD { background: #6b7b93; }

/* ============================ Grading ================================= */
.grade-grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 12px; }
.grade-chip {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 22px 10px; text-align: center; box-shadow: var(--shadow-xs); transition: transform .18s var(--ease), border-color .18s, box-shadow .18s;
}
.grade-chip:hover { transform: translateY(-4px); border-color: var(--primary); box-shadow: var(--shadow); }
.grade-chip .g {
  font-size: 1.8rem; font-weight: 900; letter-spacing: -0.02em;
  background: linear-gradient(135deg, var(--navy-3), var(--navy)); -webkit-background-clip: text; background-clip: text; color: transparent;
}
:root[data-theme="dark"] .grade-chip .g { background: linear-gradient(135deg, var(--gold-3), var(--gold)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.grade-chip .gl { font-size: .72rem; color: var(--muted); margin-top: 7px; font-weight: 600; }

/* ============================ Tables ================================== */
/* A table's width:100% collapses to content width inside this overflow-x:auto
   scroll box (percentage widths resolve against content there). Making the wrap a
   flex container and the table `flex:1` with a 0 basis forces it to fill the full
   width even when empty ("No messages."), while overflow-x:auto still lets a
   genuinely wide table scroll on narrow screens. */
.table-wrap { display: flex; overflow-x: auto; border-radius: var(--radius); -webkit-overflow-scrolling: touch; }
.table-wrap > table.data { flex: 1 1 auto; width: 100%; }
table.data { width: 100%; border-collapse: collapse; background: var(--surface); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-sm); }
table.data th, table.data td { text-align: left; padding: 13px 16px; border-bottom: 1px solid var(--line); }
table.data th { background: var(--surface-2); color: var(--muted); font-size: .74rem; text-transform: uppercase; letter-spacing: .05em; font-weight: 700; }
table.data tr:last-child td { border-bottom: 0; }
table.data tbody tr { transition: background .12s; }
table.data tbody tr:hover { background: var(--surface-2); }
/* Content-list preview column: one truncated line on desktop so long bodies
   don't stretch the table. In card mode (mobile) this is overridden to wrap. */
table.data td.preview-cell { color: var(--muted); max-width: 360px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ============================ Tournaments ============================= */
.tournament-item {
  position: relative;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 18px 22px; margin-bottom: 14px; box-shadow: var(--shadow-xs);
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap; transition: border-color .16s, transform .16s, box-shadow .16s;
}
.tournament-item:hover { border-color: rgba(226,35,26,.5); transform: translateY(-2px); box-shadow: var(--shadow); }
/* The whole card is clickable: the title's link is stretched over the card via an
   overlay, so the real <a> (and its keyboard focus) stays on the heading text. */
.tournament-info h3 a::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.tournament-item:focus-within { border-color: rgba(226,35,26,.5); box-shadow: var(--shadow); }
/* Keep the status pill above the overlay so it isn't swallowed by the link. */
.tournament-item .status-pill { position: relative; z-index: 1; }
.tournament-date {
  background: linear-gradient(140deg, var(--navy-3), var(--navy)); color: var(--gold);
  border-radius: 14px; padding: 12px 14px; text-align: center; min-width: 84px; box-shadow: var(--shadow-xs);
}
.tournament-date .day { font-size: 1.7rem; font-weight: 900; line-height: 1; }
.tournament-date .mon { font-size: .74rem; text-transform: uppercase; letter-spacing: .06em; opacity: .92; margin-top: 2px; }
.tournament-info { flex: 1; min-width: 200px; }
/* Result tally: only rendered when a tournament has results posted. */
.result-count {
  display: inline-block; margin-top: 6px; font-size: .74rem; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; color: var(--navy-3);
  background: var(--surface-2); border: 1px solid var(--line); border-radius: 999px; padding: 3px 10px;
}
:root[data-theme="dark"] .result-count { color: var(--ink-2); }
.tournament-info h3 { margin-bottom: 4px; font-size: 1.15rem; }
/* The title anchor is the card's main tap target; pad it to 44px without
   shifting the text (negative margins offset the added padding). */
.tournament-info h3 a { display: inline-flex; align-items: center; min-height: 44px; margin: -11px 0; }
.tournament-info .meta { color: var(--muted); font-size: .9rem; }
.status-pill { font-size: .68rem; font-weight: 800; padding: 5px 12px; border-radius: 30px; text-transform: uppercase; letter-spacing: .05em; }
.status-upcoming { background: var(--gold-soft); color: var(--accent-ink); }
.status-completed { background: var(--surface-2); color: var(--muted); }
:root[data-theme="dark"] .status-upcoming { background: rgba(245,179,1,.15); color: var(--gold); }
/* Live: green, with a pulsing dot so an in-progress event reads as active.
   Derived from the dates (lib/dates.statusOf) — no admin sets this. */
.status-live { background: #e3f7ec; color: #14663a; display: inline-flex; align-items: center; gap: 6px; }
.status-live::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%; background: #1a9d57;
  animation: live-pulse 1.6s ease-in-out infinite;
}
@keyframes live-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .45; transform: scale(.78); } }
@media (prefers-reduced-motion: reduce) { .status-live::before { animation: none; } }
:root[data-theme="dark"] .status-live { background: rgba(20,102,58,.28); color: #7ee0a6; }
/* Cancelled: a manual flag that overrides the derived status (lib/dates.statusOf). */
.status-cancelled { background: #fdecec; color: #b0332a; }
:root[data-theme="dark"] .status-cancelled { background: rgba(176,51,42,.26); color: #f4a19a; }
/* Cancelled cards read as struck-through/dimmed so they're scannable in a list. */
.tournament-item.is-cancelled { opacity: .72; }
.tournament-item.is-cancelled .tournament-info h3 a { text-decoration: line-through; }

.tabs { display: flex; gap: 8px; margin-bottom: 26px; overflow-x: auto; padding-bottom: 4px; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.tabs::-webkit-scrollbar { display: none; }
.tabs a { padding: 10px 20px; border-radius: 30px; color: var(--muted); font-weight: 700; background: var(--surface); border: 1px solid var(--line); white-space: nowrap; transition: all .15s; }
.tabs a.active { background: linear-gradient(135deg, var(--navy-3), var(--navy)); color: #fff; border-color: transparent; }
:root[data-theme="dark"] .tabs a.active { background: linear-gradient(135deg, var(--gold-3), var(--gold)); color: var(--accent-ink); }
.tabs a:hover { text-decoration: none; border-color: var(--line-strong); }

/* ============================ Gallery ================================= */
.gallery-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
.gallery-grid figure { margin: 0; border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow-sm); background: var(--surface); border: 1px solid var(--line); transition: transform .18s, box-shadow .18s; }
.gallery-grid figure:hover { transform: translateY(-3px); box-shadow: var(--shadow); }
.gallery-grid img { aspect-ratio: 4/3; object-fit: cover; width: 100%; transition: transform .3s var(--ease); }
.gallery-grid figure:hover img { transform: scale(1.05); }
/* Year headings on the Roll of Honour. Was an inline `color: var(--green-dark)`
   — an undefined variable, so the accent silently fell back to body text. */
.honour-year {
  margin-top: 32px; margin-bottom: 12px; color: var(--primary);
  display: flex; align-items: center; gap: 12px;
}
.honour-year::after {
  content: ""; flex: 1; height: 2px; border-radius: 2px;
  background: linear-gradient(to right, var(--line-strong), transparent);
}

.gallery-grid .gallery-link { display: block; line-height: 0; }
.gallery-grid figcaption { padding: 9px 13px; font-size: .85rem; color: var(--muted); }
.gallery-title { display: block; color: var(--ink-2); font-weight: 600; }
/* Captions can be long; keep tiles even by clamping to two lines. */
.gallery-caption {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; margin-top: 2px; font-size: .8rem; line-height: 1.4;
}

/* ============================ Edit chip (CMS) ========================= */
.edit-chip {
  display: inline-flex; align-items: center; background: linear-gradient(135deg, var(--gold-3), var(--gold)); color: var(--accent-ink);
  font-size: .72rem; font-weight: 800; padding: 3px 10px; border-radius: 20px; margin-left: 8px; vertical-align: middle;
  box-shadow: var(--shadow-xs);
}
.edit-chip:hover { text-decoration: none; filter: brightness(1.05); }

/* ============================ Footer (light, Lovable-style) ============ */
/* The footer sits at the bottom because .page-wrap (above it) flex-grows to fill
   short pages — no margin-top:auto needed. */
/* The footer is permanently dark in BOTH themes. Rather than hardcode a dozen
   colours, re-declare the theme variables the footer's children key off with
   their dark-mode values — scoped to .site-footer, so nothing outside changes.
   Keeps the light-mode heading (--navy) and link (--blue-2) rules readable by
   pointing them at dark equivalents. */
.site-footer {
  /* Background follows the active colour theme: every preset sets --brand-bg to
     its own dark tone (red/navy #0a1e3d, ocean #0c2340, emerald #0b2e22, …), so
     the footer recolours with the preset instead of staying a fixed navy.
     Foreground vars are re-declared light so text reads on any dark theme. */
  --footer-bg: var(--brand-bg, #0a1e3d);
  --ink: #eef3fb;
  --ink-2: #c7d3e6;
  --muted: rgba(238,243,251,.62);
  --line: rgba(255,255,255,.10);
  --navy: #eef3fb;   /* headings: light in this dark context */
  background: var(--footer-bg); color: var(--ink-2); position: relative; border-top: 1px solid var(--line);
}
.site-footer .container { padding-top: 52px; padding-bottom: 44px; }
.footer-cols { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 40px; }
.footer-brand .brand { color: var(--ink); }
.footer-brand p { color: var(--muted); margin: 18px 0 0; max-width: 360px; font-size: .98rem; }
.footer-col h4 { color: var(--navy); font-family: var(--font-display); font-weight: 400; font-size: 1.1rem; letter-spacing: .04em; text-transform: uppercase; margin-bottom: 16px; }
/* Footer links: a soft off-white that reads on ANY preset's dark background
   (a fixed blue looked wrong on emerald/purple). Scoped under .site-footer
   (0-2-1) so it beats the global `:root[data-theme="dark"] a` rule (0-1-2).
   Hover brightens to full white — theme-neutral, so it works across presets. */
.site-footer .footer-col a { display: block; color: rgba(238,243,251,.78); padding: 7px 0; font-size: 1rem; transition: color .15s, transform .15s; }
.site-footer .footer-col a:hover { color: #fff; transform: translateX(3px); text-decoration: none; }
/* Footer round badge */
.footer-badge { height: 92px; width: 92px; border-radius: 50%; margin-bottom: 6px; box-shadow: var(--shadow-sm); }
/* Sponsor bar */
/* Sponsor bar is fixed to the viewport bottom so it stays visible while
   scrolling. Slimmer than before (smaller text + tighter padding) to stay
   unobtrusive. z-index 40 keeps it under the header (60), mobile drawer (80)
   and its backdrop (50) — opening the menu covers it. The whole bar is the
   click target (it may be an <a>), styled as plain text — no underline, no link
   colour — so it reads as a sponsor line, not a hyperlink. */
.footer-sponsor {
  /* Follows the active theme like the footer, but a touch deeper (mix the theme's
     dark tone with a little black) so the bar still reads as its own strip. */
  --dark-bg: color-mix(in srgb, var(--brand-bg, #0a1e3d) 82%, #000);
  --muted: rgba(238,243,251,.62);
  --ink: #eef3fb;
  --line: rgba(255,255,255,.10);
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
  border-top: 1px solid var(--line); background: var(--dark-bg);
  box-shadow: 0 -2px 12px rgba(6,16,32,.28);
  display: block; text-decoration: none; color: inherit;
}
.footer-sponsor .container { display: flex; justify-content: center; align-items: center; gap: 8px; padding: 8px 20px; text-align: center; }
/* Keep the sponsor credit on a single line — it shrinks to fit rather than
   wrapping. min-width:0 lets the flex item shrink below its content width and
   overflow-ellipsis handles the rare case where even the smallest size can't fit. */
.footer-sponsor span { color: var(--muted); font-size: .8rem; line-height: 1.35; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.footer-sponsor strong { color: var(--ink); }
/* When it's a link: still plain-text styling, just a pointer cursor. No hover
   underline or colour shift, per request. */
.footer-sponsor-link { cursor: pointer; }
.footer-sponsor-link:hover, .footer-sponsor-link:focus { text-decoration: none; }
/* Edit chip overlays the (full-width, clickable) bar at the right edge, so the
   sponsor link stays the click target everywhere else. */
.footer-sponsor-edit { position: fixed; right: 16px; bottom: 8px; z-index: 41; }
.footer-sponsor-edit .edit-chip { flex: 0 0 auto; }
/* Clear the fixed sponsor bar by extending the FOOTER's own dark background down
   behind it — not with body padding, which would leave a strip of light page
   background between the footer's bottom edge and the bar. The bar (~28px) sits
   over this padding; 46px keeps the footer's last row of content clear of it. */
body.has-sponsor .site-footer { padding-bottom: 46px; }

/* ============================ Forms ================================== */
/* Contact: form + aside side-by-side on desktop, stacked on mobile. Was an
   inline style on the container, which a media query cannot override. */
.contact-grid { display: grid; grid-template-columns: 1.4fr 1fr; gap: 32px; align-items: start; }

/* Required-field marker on the contact form labels. */
.contact-form .req { color: var(--primary); font-weight: 700; }
.contact-form label { display: block; margin-bottom: 6px; font-weight: 600; font-size: .82rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }

/* Right column: a bordered "Primary Contact" card, then Community/Video blocks.
   Content is CMS-editable (sanitizer-safe tags only); the template supplies chrome.
   The card is the leading block; the rest read as plain sections beneath it. */
.contact-detail-card { display: flex; flex-direction: column; gap: 4px; }
.contact-detail-card .prose > p:first-child { /* "Primary Contact" eyebrow */
  color: var(--primary); font-weight: 800; font-size: .72rem; text-transform: uppercase;
  letter-spacing: .16em; margin: 0 0 2px;
}
.contact-detail-card .prose h3 { margin: 0 0 18px; font-size: 1.5rem; }
.contact-detail-card .prose p { margin: 0 0 14px; color: var(--ink-2); }
.contact-detail-card .prose p strong { color: var(--ink); }
/* Give the whole aside the boxed look of the mockup's contact card. */
.contact-aside {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm); padding: 28px 30px;
}

/* Honeypot. Deliberately NOT display:none — the better bots skip hidden inputs,
   so this stays "visible" to them while being off-screen for people. Paired with
   aria-hidden + tabindex="-1" in the markup so assistive tech and keyboard users
   never reach it. */
.hp-field {
  position: absolute; left: -9999px; top: auto;
  width: 1px; height: 1px; overflow: hidden;
}

.form-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow); padding: 32px; max-width: 460px; margin: 44px auto; }
/* Wide, left-aligned form (admin edit pages) — overrides the narrow centred default. */
.form-card--flush { max-width: none; margin: var(--space-5) 0; }
.form-wide { max-width: 720px; }
.field { margin-bottom: 18px; }
.field label { display: block; font-weight: 600; margin-bottom: 7px; font-size: .9rem; color: var(--ink); }
.field input, .field select, .field textarea {
  width: 100%; padding: 12px 14px; border: 1px solid var(--line-strong); border-radius: 12px;
  font-size: 16px; font-family: inherit; background: var(--bg); color: var(--ink); transition: border-color .15s, box-shadow .15s;
}
.field textarea { min-height: 130px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 4px rgba(226,35,26,.15); }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.help { color: var(--muted); font-size: .82rem; margin-top: 5px; }

/* Password field with a reveal toggle. The input is wrapped so the button can
   be centred against the input box itself, not the label + input together. */
.field-password .input-wrap { position: relative; display: flex; }
.field-password input { padding-right: 46px; }
.pw-toggle {
  position: absolute; right: 0; top: 0; bottom: 0; width: 44px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: 0 12px 12px 0; padding: 0;
  color: var(--muted); cursor: pointer;
}
.pw-toggle:hover { color: var(--ink); }
.pw-toggle:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.pw-toggle svg { width: 20px; height: 20px; display: block; }
.pw-toggle svg[hidden] { display: none; }
.field-password input::-ms-reveal, .field-password input::-ms-clear { display: none; }

/* "Remember me" row. */
.field-check { display: flex; align-items: center; gap: 9px; margin-bottom: 18px; }
.field-check input[type="checkbox"] {
  width: 17px; height: 17px; flex: none; margin: 0; accent-color: var(--primary); cursor: pointer;
}
.field-check label { margin: 0; font-weight: 500; font-size: .9rem; color: var(--ink); cursor: pointer; }

/* ============================ Admin ================================== */
/* width:100% forces the grid to fill .container on every page — without it the
   grid shrank to content, so pages with a wide table were wider (and shifted)
   than pages with narrow content, misaligning the admin area page-to-page.
   minmax(0,1fr) lets the content track shrink instead of pushing the grid wider. */
.admin-layout { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: 30px; padding: 30px 0; width: 100%; }
.admin-side { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 16px; height: fit-content; box-shadow: var(--shadow-sm); position: sticky; top: calc(var(--header-h) + 16px); }
.admin-side a { display: block; padding: 9px 13px; border-radius: 10px; color: var(--muted); font-weight: 600; font-size: .9rem; transition: background .12s, color .12s; }
.admin-side a:hover, .admin-side a.active { background: var(--surface-2); color: var(--ink); text-decoration: none; }
.admin-side a.active { color: var(--navy); background: var(--gold-soft); }
/* Nav count badges. Default (red) = action needed, e.g. unread messages.
   .nav-count-muted (grey) = a plain total, e.g. player/user counts. */
.nav-count {
  display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px;
  padding: 0 5px; margin-left: 6px; border-radius: 9px; background: var(--primary); color: #fff;
  font-size: .68rem; font-weight: 800; vertical-align: middle;
}
.nav-count-muted { background: var(--line-strong); color: var(--ink-2); }
:root[data-theme="dark"] .nav-count-muted { background: rgba(255,255,255,.14); color: var(--ink-2); }
:root[data-theme="dark"] .admin-side a.active { color: var(--gold); background: rgba(245,179,1,.12); }
.admin-side .side-title { font-size: .68rem; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); margin: 16px 8px 6px; font-weight: 700; }
.stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 28px; }
.stat { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 20px; box-shadow: var(--shadow-xs); transition: transform .16s, border-color .16s, box-shadow .16s; }
.stat:hover { border-color: rgba(226,35,26,.5); text-decoration: none; transform: translateY(-3px); box-shadow: var(--shadow); }
.stat .num { font-size: 2.1rem; font-weight: 900; background: linear-gradient(135deg, var(--navy-3), var(--navy)); -webkit-background-clip: text; background-clip: text; color: transparent; }
:root[data-theme="dark"] .stat .num { background: linear-gradient(135deg, var(--gold-3), var(--gold)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.stat .lbl { color: var(--muted); font-size: .85rem; }
/* Quick actions: buttons flow inline with a small gap on desktop. On mobile they
   become an even two-up grid (see the 860px block) so they line up instead of
   each hugging its own text width. */
.quick-actions { display: flex; flex-wrap: wrap; gap: 10px; }
.toolbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; gap: 12px; flex-wrap: wrap; }
.actions-cell { display: flex; gap: 6px; flex-wrap: wrap; }
.inline-form { display: inline; }

/* Ellipsis row-action menu (keeps wide action buttons out of the table row). */
.row-menu { position: relative; display: inline-block; }
.row-menu-btn {
  display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px;
  border: 1px solid var(--line); background: var(--surface); border-radius: 9px; cursor: pointer;
  color: var(--ink-2); font-size: 1.1rem; line-height: 1; letter-spacing: .06em;
}
.row-menu-btn:hover { background: var(--surface-2); border-color: var(--primary); color: var(--primary); }
.row-menu-list {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 20; min-width: 160px;
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px;
  box-shadow: var(--shadow-lg); padding: 6px; display: none; flex-direction: column; gap: 2px;
}
.row-menu.open .row-menu-list { display: flex; }
.row-menu-list .inline-form { display: block; }
.row-menu-list button {
  width: 100%; text-align: left; padding: 9px 12px; border: 0; background: none; border-radius: 8px;
  font: inherit; font-size: .9rem; font-weight: 600; color: var(--ink-2); cursor: pointer;
}
.row-menu-list button:hover { background: var(--surface-2); color: var(--ink); }
.row-menu-list button.danger:hover { background: var(--red-soft); color: var(--red-2); }

/* ---- Styled confirm modal (replaces window.confirm) --------------------- */
.confirm-overlay {
  position: fixed; inset: 0; z-index: 200; display: flex; align-items: center; justify-content: center;
  padding: 20px; background: rgba(6,16,32,.55); backdrop-filter: blur(2px);
  opacity: 0; transition: opacity .16s var(--ease);
}
.confirm-overlay.is-open { opacity: 1; }
.confirm-box {
  width: 100%; max-width: 380px; background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-lg);
  padding: 22px 22px 18px; transform: translateY(8px) scale(.98); transition: transform .16s var(--ease);
}
.confirm-overlay.is-open .confirm-box { transform: none; }
.confirm-msg { margin: 0 0 18px; font-size: 1rem; line-height: 1.45; color: var(--ink); }
.confirm-actions { display: flex; justify-content: flex-end; gap: 10px; }

/* Messages table: each message is a top row (meta + actions) plus a full-width
   body row beneath it, so long messages don't stretch the table past the margin. */
.messages-table .msg-top td { border-bottom: 0; padding-bottom: 6px; }
.messages-table .msg-body td { padding-top: 0; padding-bottom: 16px; color: var(--ink-2); }
.messages-table .msg-body.is-handled td, .messages-table .msg-top.is-handled td { opacity: .6; }
/* The actions cell hosts the dropdown menu, so dimming it would make the open
   popup translucent (message text behind it bleeds through). Keep the cell — and
   thus the menu — at full opacity; the row still reads as "handled" via the pill. */
.messages-table .msg-top.is-handled td.actions-cell { opacity: 1; }
.messages-table .msg-subject { font-weight: 700; color: var(--ink); margin-right: 6px; }
/* Keep the two rows visually grouped: only the body row draws the divider. */
.messages-table tbody tr.msg-top:hover td, .messages-table tbody tr.msg-body:hover td { background: transparent; }

/* Tighten the fixed-content columns so From/Phone get the room. nth-child:
   1=Received, 2=From, 3=Phone, 4=Status, 5=actions. */
.messages-table th:nth-child(1), .messages-table .msg-top td:nth-child(1) { width: 150px; }
.messages-table th:nth-child(4), .messages-table .msg-top td:nth-child(4) { width: 90px; }
.messages-table th:nth-child(5), .messages-table .msg-top td:nth-child(5) { width: 56px; }

/* Section header rows (New / Handled). */
.messages-table .msg-section td {
  background: var(--surface-2); padding: 8px 16px; font-size: .72rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .08em; color: var(--muted); border-bottom: 1px solid var(--line);
}
.msg-section-count {
  display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px;
  padding: 0 5px; margin-left: 6px; border-radius: 9px; background: var(--line-strong); color: var(--ink-2);
  font-size: .7rem; font-weight: 800;
}
/* Status pill in the row. */
.status-tag { display: inline-block; padding: 3px 10px; border-radius: 20px; font-size: .72rem; font-weight: 700; }
.status-new { background: var(--red-soft); color: var(--red-2); }
.status-handled { background: var(--surface-2); color: var(--muted); }
:root[data-theme="dark"] .status-new { background: rgba(226,35,26,.16); color: var(--red-3); }

#editor { background: var(--surface); min-height: 260px; color: var(--ink); font-size: 1rem; }
.ql-toolbar.ql-snow, .ql-container.ql-snow { border-color: var(--line-strong); }
.ql-container.ql-snow { border-bottom-left-radius: 12px; border-bottom-right-radius: 12px; }
.ql-toolbar.ql-snow { border-top-left-radius: 12px; border-top-right-radius: 12px; background: var(--surface-2); }
:root[data-theme="dark"] .ql-snow .ql-stroke { stroke: var(--ink-2); }
:root[data-theme="dark"] .ql-snow .ql-fill { fill: var(--ink-2); }
:root[data-theme="dark"] .ql-snow .ql-picker { color: var(--ink-2); }
:root[data-theme="dark"] .ql-editor.ql-blank::before { color: var(--muted); }

/* ============================ Admin list components =================== */
.muted-sub { color: var(--muted); font-size: .85rem; margin: 2px 0 0; }
.admin-filters {
  display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 18px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 12px 14px; box-shadow: var(--shadow-xs);
}
.admin-filters .af-search { flex: 1 1 220px; min-width: 180px; }
.admin-filters input, .admin-filters select {
  padding: 9px 12px; border: 1px solid var(--line-strong); border-radius: 10px; font-size: 15px;
  font-family: inherit; background: var(--bg); color: var(--ink);
}
.admin-filters input:focus, .admin-filters select:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
/* Filter submit: label + search icon on desktop. */
.af-submit { gap: 6px; }
.af-submit .af-submit-icon { display: none; }

/* Refined admin table */
.admin-table th a { color: inherit; }
.admin-table th a:hover { color: var(--primary); text-decoration: none; }
/* Keep readable column widths on narrow screens: rather than crushing the
   columns to fit, hold a minimum width and let .table-wrap scroll sideways. */
.admin-table { min-width: 640px; }
.admin-table td, .admin-table th { white-space: nowrap; }
.col-check { width: 34px; text-align: center; }
.col-check input { width: 17px; height: 17px; accent-color: var(--primary); cursor: pointer; }
.col-actions { width: 1%; white-space: nowrap; text-align: right; }
.dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; }
.dot-on { background: #1a9d54; box-shadow: 0 0 0 3px rgba(26,157,84,.15); }
.dot-off { background: #b6c2d1; }

/* Compact row action buttons — Edit quiet, Delete distinct/danger */
.rowbtn {
  display: inline-flex; align-items: center; gap: 5px; font-size: .82rem; font-weight: 600;
  padding: 6px 11px; border-radius: 8px; border: 1px solid var(--line-strong); background: var(--surface);
  color: var(--ink-2); cursor: pointer; transition: all .13s; vertical-align: middle;
}
.rowbtn:hover { background: var(--surface-2); border-color: var(--primary); color: var(--primary); text-decoration: none; }
.rowbtn svg { display: block; }
.rowbtn-danger { color: #c0392b; border-color: transparent; background: transparent; padding: 6px 8px; }
.rowbtn-danger:hover { background: #fdecec; border-color: #f4c3bf; color: #a5271c; }
:root[data-theme="dark"] .rowbtn-danger:hover { background: rgba(192,57,43,.2); border-color: rgba(192,57,43,.5); color: #f4a19a; }

/* Bulk action bar */
.bulk-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  background: var(--primary-soft); border: 1px solid var(--primary); border-radius: var(--radius);
  padding: 10px 16px; margin-bottom: 14px; font-weight: 600; font-size: .9rem; color: var(--primary);
}
:root[data-theme="dark"] .bulk-bar { color: var(--ink); }
.bulk-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* Pagination */
.pagination { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin: 20px 0; flex-wrap: wrap; }
.pg-info { color: var(--muted); font-size: .88rem; }
.pg-btns { display: flex; align-items: center; gap: 8px; }
.pg-btn { padding: 8px 14px; border: 1px solid var(--line-strong); border-radius: 10px; font-weight: 600; font-size: .88rem; color: var(--ink-2); background: var(--surface); }
.pg-btn:hover { border-color: var(--primary); color: var(--primary); text-decoration: none; }
.pg-btn.disabled { opacity: .45; pointer-events: none; }
.pg-cur { font-size: .88rem; color: var(--muted); font-weight: 600; }

/* ============================ Appearance settings ===================== */
.settings-group { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 22px; box-shadow: var(--shadow-xs); margin-bottom: 20px; }
.settings-group h2 { font-size: 1.05rem; margin-bottom: 14px; }
.settings-row { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.preset-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.preset-opt {
  display: flex; flex-direction: column; gap: 8px; align-items: flex-start; cursor: pointer;
  border: 2px solid var(--line); border-radius: var(--radius); padding: 14px; transition: border-color .15s, transform .15s, box-shadow .15s;
}
.preset-opt:hover { transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.preset-opt.selected { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.preset-opt input { position: absolute; opacity: 0; pointer-events: none; }
.preset-swatch { display: inline-flex; border-radius: 8px; overflow: hidden; box-shadow: var(--shadow-xs); }
.preset-swatch span { width: 34px; height: 34px; display: block; }
.preset-name { font-weight: 700; font-size: .9rem; }

/* Layout theme picker with mini mockups */
.layout-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.layout-opt {
  display: flex; flex-direction: column; gap: 6px; cursor: pointer; text-align: left;
  border: 2px solid var(--line); border-radius: var(--radius); padding: 12px; transition: border-color .15s, box-shadow .15s, transform .15s;
}
.layout-opt:hover { transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.layout-opt.selected { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); }
.layout-opt input { position: absolute; opacity: 0; pointer-events: none; }
.layout-name { font-weight: 700; font-size: .95rem; }
.layout-desc { font-size: .8rem; color: var(--muted); line-height: 1.4; }
/* Mini mockup */
.layout-mock { background: var(--bg); border: 1px solid var(--line); border-radius: 8px; padding: 8px; height: 96px; overflow: hidden; }
.layout-mock .lm-content { display: block; }
.layout-mock .lm-bar { display: block; height: 7px; border-radius: 3px; background: var(--primary); width: 60%; margin-bottom: 7px; }
.layout-mock .lm-hero { display: block; height: 30px; border-radius: 5px; background: linear-gradient(120deg, var(--navy-3), var(--navy)); margin-bottom: 7px; }
.layout-mock .lm-cards { display: flex; gap: 5px; }
.layout-mock .lm-cards i { flex: 1; height: 22px; border-radius: 4px; background: var(--surface-2); border: 1px solid var(--line); }
/* Classic mock = default (above). Sidebar mock: left rail + content column */
.layout-mock-sidebar { display: flex; gap: 6px; }
.layout-mock-sidebar .lm-bar { width: 26%; height: auto; margin: 0; align-self: stretch; }
.layout-mock-sidebar .lm-content { flex: 1; }
/* Magazine mock: split hero band */
.layout-mock-magazine .lm-hero { height: 40px; background: linear-gradient(90deg, var(--navy) 55%, var(--primary) 55%); }
.layout-mock-magazine .lm-bar { width: 100%; height: 6px; }
.layout-mock-magazine .lm-cards i:first-child { flex: 1.6; }

.radio-pill { display: inline-flex; align-items: center; gap: 8px; margin: 0 8px 8px 0; cursor: pointer; padding: 9px 16px; border: 1px solid var(--line-strong); border-radius: 30px; font-weight: 600; font-size: .9rem; transition: all .15s; }
.radio-pill:has(input:checked) { background: var(--primary); color: #fff; border-color: var(--primary); }
.radio-pill input { accent-color: var(--primary); }
.radio-pill:has(input:checked) input { display: none; }

/* ============================ Error page ============================== */
.error-page { text-align: center; padding: 96px 20px; }
.error-page .code { font-size: clamp(4rem, 14vw, 6.5rem); font-weight: 900; background: linear-gradient(135deg, var(--gold-3), var(--gold)); -webkit-background-clip: text; background-clip: text; color: transparent; }

/* ============================ Responsive ============================= */
@media (max-width: 1024px) {
  .cards { grid-template-columns: repeat(2, 1fr); }
  .player-grid { grid-template-columns: repeat(3, 1fr); }
  .grade-grid { grid-template-columns: repeat(4, 1fr); }
}

@media (max-width: 860px) {
  /* Turn top-nav into a slide-in drawer */
  .hide-mobile { display: none !important; }
  .nav-toggle { display: flex; }
  /* .main-nav's `margin: 0 auto` is what pushes .header-actions to the right on
     desktop. Here the nav is position:fixed and out of flow, so that auto margin
     is gone — without this the toggle and theme switch bunch up against the logo. */
  .header-actions { margin-left: auto; gap: 6px; }
  /* Trim the header buttons on small screens — 44px each reads bulky next to the
     logo at 375px. The button stays 38px visually, but ::after extends the tap
     area to the 44px minimum, so the touch target is bigger than the paint. */
  .theme-toggle, .nav-toggle { width: 38px; height: 38px; border-radius: 10px; position: relative; }
  .theme-toggle::after, .nav-toggle::after {
    content: ""; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: 44px; height: 44px;
  }
  .theme-toggle { font-size: .95rem; }
  .nav-toggle { gap: 4px; }
  .nav-toggle span { width: 17px; }
  .main-nav {
    position: fixed; top: 0; right: 0; bottom: 0; z-index: 80;
    width: min(84vw, 340px); background: var(--surface); flex-direction: column; align-items: stretch;
    gap: 4px; padding: 18px; margin: 0; box-shadow: var(--shadow-lg);
    transform: translateX(100%); overflow-y: auto;
    /* Depends on .site-header keeping its backdrop-filter on a pseudo-element:
       a backdrop-filter on the header would make it the containing block for
       this fixed panel, collapsing the drawer into the header's box.
       `visibility` (not just transform) keeps the closed, off-screen panel from
       extending the scrollable width — body's overflow-x:hidden cannot clip a
       fixed child — and keeps its links out of the tab order. Delayed on close
       so the slide-out animation still runs. */
    visibility: hidden;
    transition: transform .32s var(--ease), visibility 0s linear .32s;
  }
  body.nav-open .main-nav {
    transform: translateX(0);
    visibility: visible;
    transition: transform .32s var(--ease), visibility 0s;
  }
  .main-nav a { padding: 14px 16px; font-size: .95rem; border-radius: 12px; }
  .main-nav a.active::after { display: none; }
  /* In the drawer, the "More" group just shows its items inline (no dropdown card) */
  .nav-more { flex-direction: column; align-items: stretch; width: 100%; }
  .nav-more-btn { display: none; }
  .nav-more-menu { position: static; display: flex !important; border: 0; box-shadow: none; padding: 0; background: none; min-width: 0; margin-top: 6px; border-top: 1px solid var(--line); padding-top: 6px; }
  .nav-more-menu a { padding: 14px 16px; font-size: .95rem; }
  .drawer-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; padding-bottom: 12px; border-bottom: 1px solid var(--line); }
  .drawer-head .brand { font-size: 1.15rem; }
  .drawer-close { width: 44px; height: 44px; border: 1px solid var(--line); background: var(--surface-2); border-radius: 11px; font-size: 1.05rem; cursor: pointer; color: var(--ink); }
  .drawer-actions { display: flex; flex-direction: column; gap: 10px; margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--line); }
  .drawer-actions .btn { width: 100%; }

  .player-grid { grid-template-columns: repeat(2, 1fr); }
  .gallery-grid { grid-template-columns: repeat(2, 1fr); }
  .member-grid { grid-template-columns: repeat(2, 1fr); }
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
  /* Slimmer stat cards on mobile: centered content, tighter padding, and a
     smaller number so each card is shorter on the narrow 2-up grid instead of
     sitting top-left with a lot of empty space. */
  .stat { padding: 12px 10px; text-align: center; }
  .stat .num { font-size: 1.6rem; }
  .stat .lbl { font-size: .78rem; }
  .stat-grid { gap: 10px; }
  /* Even two-up button grid so the quick actions line up edge-to-edge instead of
     each button sizing to its own label width. */
  .quick-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
  .quick-actions .btn { width: 100%; }

  /* Filters on mobile: two tidy rows.
       row 1:  [ search .......................... ] [🔍]
       row 2:  [ All grades ][ All clubs ][ Any status ]
     The submit button is pulled up beside the search via `order` and shrunk to
     a square icon; the three selects flex to share the second row. */
  .admin-filters { align-items: stretch; }
  .admin-filters .af-search { flex: 1 1 auto; order: 1; min-width: 0; }
  .af-submit { flex: 0 0 auto; order: 2; width: 44px; padding: 9px 0; }
  .af-submit .af-submit-icon { display: block; }
  .af-submit .af-submit-label { display: none; }
  .admin-filters select { flex: 1 1 96px; order: 3; min-width: 90px; }
  /* Reset link, if shown, sits at the end. */
  .admin-filters .btn-ghost { order: 4; }

  .members-toolbar { flex-direction: column; align-items: stretch; }
  /* Flipping to column makes width the CROSS axis, so the desktop `flex` bases
     (`1 1 240px` / `2 1 300px`) stop constraining width — children sized to
     max-content and ~20 nowrap city chips blew the page out to 2600px — while
     those same bases leaked into HEIGHT, stretching the search box to 240px.
     Reset the shorthand and pin width so .chip-row's overflow-x:auto engages. */
  .members-toolbar > * { width: 100%; max-width: 100%; min-width: 0; }
  .members-toolbar .pt-search,
  .members-toolbar .chip-row { flex: 0 0 auto; min-width: 0; }
  .members-toolbar .chip-row { align-items: center; }
  /* ~20 city chips overflow a phone-width row and the touch scrollbar is
     invisible until you drag, so fade the edge that has more chips behind it.
     Same mechanism as .admin-side (see initEdgeFades in app.js). */
  .chip-row {
    --fade-l: 0px; --fade-r: 0px;
    -webkit-mask-image: linear-gradient(to right,
      transparent 0, #000 var(--fade-l),
      #000 calc(100% - var(--fade-r)), transparent 100%);
    mask-image: linear-gradient(to right,
      transparent 0, #000 var(--fade-l),
      #000 calc(100% - var(--fade-r)), transparent 100%);
    scrollbar-width: none;
    scroll-behavior: smooth;
  }
  .chip-row::-webkit-scrollbar { display: none; }
  .chip { scroll-margin-inline: 12px; }
  /* The pill IS the target here, so it grows rather than getting an invisible
     ::after — neighbours are only 8px apart in the scroller and would overlap.
     min-height beats padding maths: the 1px border made 10px padding land at
     43px. inline-flex so min-height applies to the inline anchor. */
  .chip { display: inline-flex; align-items: center; min-height: 44px; padding: 8px 16px; }
  .members-count { text-align: center; }
  .stats-grid { grid-template-columns: repeat(2, 1fr); gap: 24px 8px; }
  .stat-item:nth-child(2)::after { display: none; }
  /* In-hero stats: tighter, since they share the viewport with the h1 and CTAs */
  .hero .stats-grid { margin-top: 32px; gap: 18px 8px; }
  .footer-cols { grid-template-columns: 1fr 1fr; }
  .footer-brand { grid-column: 1 / -1; }
  /* Sponsor bar stays a single line on mobile: smaller type + tighter side
     padding give the credit room to fit, so the bar keeps its one-row height
     and the footer only needs the single-line bottom clearance. */
  .footer-sponsor .container { padding: 8px 10px; gap: 6px; }
  /* Fluid size so the full credit always fits one line without ellipsis: scales
     with viewport width, capped at .78rem so it doesn't grow past the desktop size. */
  .footer-sponsor span { font-size: clamp(.6rem, 2.9vw, .78rem); }
  body.has-sponsor .site-footer { padding-bottom: 46px; }
  /* Slimmer edit chip on mobile so it overlaps less of the sponsor credit —
     tighter padding + smaller type. Scoped to the footer chip only, not the
     inline edit chips used elsewhere. */
  .footer-sponsor-edit { right: 10px; bottom: 6px; }
  .footer-sponsor-edit .edit-chip { padding: 2px 8px; font-size: .66rem; }

  /* Contact: stack the form above the aside instead of two cramped columns. */
  .contact-grid { grid-template-columns: minmax(0, 1fr); gap: 18px; }
  /* Long addresses (info@ibfuk.local) overflowed the narrow aside card. */
  .contact-grid a, .contact-grid .prose { overflow-wrap: anywhere; }

  /* Filter pills: 10px/20px reads bulky at this width. */
  .tabs { gap: 6px; margin-bottom: 18px; }
  .tabs a { padding: 7px 14px; font-size: .88rem; }

  /* Tournament card: date + status pill share the top row, title/meta below.
     .tournament-info's 200px min-width can't fit beside the date at this width,
     so it wraps — order + a full-width basis makes that wrap deliberate and puts
     the pill next to the date instead of stranding it beside the title. */
  .tournament-item { align-items: flex-start; gap: 12px 14px; }
  .tournament-date { order: 1; }
  .tournament-item .status-pill { order: 2; margin-left: auto; align-self: center; }
  .tournament-info { order: 3; flex-basis: 100%; min-width: 0; }

  /* Admin sidebar becomes a horizontal scroller on top.
     minmax(0, 1fr), not 1fr: a grid item's automatic minimum is its max-content
     width, which inflates the track to fit the whole nav and widens the page
     instead of letting .admin-side scroll. */
  .admin-layout { grid-template-columns: minmax(0, 1fr); gap: 18px; }
  .admin-side {
    position: static; display: flex; gap: 4px; overflow-x: auto; padding: 10px;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
    /* The scrollbar is hidden for looks, so fade the trailing edge instead —
       otherwise the strip cuts off with no hint that it scrolls. The mask is
       driven by JS-set --fade-l/--fade-r so it only shows on the side that has
       more content (see initAdminSideFade in app.js). */
    --fade-l: 0px; --fade-r: 0px;
    -webkit-mask-image: linear-gradient(to right,
      transparent 0, #000 var(--fade-l),
      #000 calc(100% - var(--fade-r)), transparent 100%);
    mask-image: linear-gradient(to right,
      transparent 0, #000 var(--fade-l),
      #000 calc(100% - var(--fade-r)), transparent 100%);
    scroll-behavior: smooth;
  }
  .admin-side::-webkit-scrollbar { display: none; }
  .admin-side a { white-space: nowrap; scroll-margin-inline: 12px; }
  .admin-side .side-title, .admin-side a[href="/"] { display: none; }
  /* Tab order on mobile: keep Messages first, then surface Players right after
     it (it's a heavily-used list but sits deep in the desktop sidebar grouping).
     Everything else keeps its DOM order after these two. */
  .admin-side a { order: 3; }
  .admin-side a[href="/admin/messages"] { order: 1; }
  .admin-side a[href="/admin/players"] { order: 2; }
}

/* Messages table -> stacked cards on mobile (its custom two-row layout doesn't fit
   the generic table.data.responsive pattern). Each message becomes a card: meta
   stacked, status + actions on their own line, message text full-width below.
   Uses canonical md (860px) — was 700px; collapsing earlier is safe (no overflow). */
@media (max-width: 860px) {
  .messages-table, .messages-table tbody { display: block; }
  .messages-table thead { display: none; }
  .messages-table .msg-section { display: block; }
  .messages-table .msg-section td { display: block; width: auto; border: 0; border-radius: 8px; margin: 14px 0 6px; }
  .messages-table tr.msg-top,
  .messages-table tr.msg-body { display: block; }
  /* Group each msg-top + following msg-body visually as one card. */
  .messages-table tr.msg-top {
    background: var(--surface); border: 1px solid var(--line); border-bottom: 0;
    border-radius: 12px 12px 0 0; padding: 12px 14px 0;
  }
  .messages-table tr.msg-body {
    background: var(--surface); border: 1px solid var(--line); border-top: 0;
    border-radius: 0 0 12px 12px; padding: 0 14px 14px; margin-bottom: 12px;
  }
  .messages-table tr.msg-top td,
  .messages-table tr.msg-body td {
    display: block; width: auto !important; border: 0; padding: 3px 0;
    white-space: normal !important; max-width: none;
  }
  /* Date + status + actions read as a compact header row inside the card. */
  .messages-table tr.msg-top td:nth-child(1) { color: var(--muted); font-size: .8rem; }
  .messages-table tr.msg-top td:nth-child(4) { display: inline-block; }
  .messages-table tr.msg-top td.actions-cell {
    display: inline-block; float: right; margin-top: -30px;
  }
  .messages-table tr.msg-body td { padding-top: 8px; }
}

@media (max-width: 560px) {
  .container { padding: 0 16px; }
  .cards, .form-row { grid-template-columns: 1fr; }
  /* Magazine feature-cards need the higher-specificity selector to beat the base
     magazine 3-column rule (0,2,0); plain .cards above can't reach them. */
  :root[data-layout="magazine"] .feature-cards { grid-template-columns: 1fr; }
  .member-grid { grid-template-columns: 1fr; }
  /* Two columns left photos ~163px wide — too small to see anything. One column
     gives them the full width, which is the point of a gallery. */
  .gallery-grid { grid-template-columns: 1fr; gap: 16px; }
  .value-cards { grid-template-columns: 1fr; }
  .layout-grid { grid-template-columns: 1fr; }
  .grade-grid { grid-template-columns: repeat(3, 1fr); }
  .hero-cta .btn { width: 100%; }
  .form-card { padding: 24px 20px; margin: 28px auto; border-radius: var(--radius); }

  /* Players toolbar: search spans the full row, then the filter/sort selects
     pack two-per-row to save vertical space, and the buttons take the full row
     again. Each select gets ~half the row minus the flex gap. */
  .players-toolbar > * { min-width: 0; }
  .players-toolbar .pt-search { flex: 1 1 100%; width: 100%; min-width: 0; }
  .players-toolbar select { flex: 1 1 calc(50% - 5px); width: auto; min-width: 0; }
  .players-toolbar .btn { flex: 1 1 100%; width: 100%; }

  /* Mobile hero: the photo is landscape, so on a tall phone screen `cover`
     shows only a sliver. Centre on the racket/shuttle so more of the action is
     visible without over-cropping. The height stays full here (unlike the old
     74svh) because the stats grid now shares this space. */
  .hero {
    min-height: calc(100svh - var(--header-h));
    padding-top: 32px; padding-bottom: 32px;
    background-position: center top, 80% 20%, 62% center, center;
    background-size: auto, auto, cover, auto;
  }
  .hero .stats-grid { margin-top: 26px; gap: 14px 8px; }
  .hero .stat-item { padding: 4px; }
  .hero .stat-item .stat-num { font-size: 1.6rem; }
  .hero .stat-item .stat-lbl { font-size: .74rem; margin-top: 4px; }

  /* Mobile card-view for data tables. Card-mode tables (.responsive) don't need
     the horizontal scroll wrapper, so let those wraps overflow visibly (keeps the
     ⋯ dropdown from being clipped). Plain tables keep .table-wrap's overflow-x
     auto so they scroll sideways on narrow screens. */
  .table-wrap:has(table.data.responsive) { overflow: visible; }
  table.data.responsive thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  table.data.responsive tr { display: block; margin-bottom: 12px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); box-shadow: var(--shadow-xs); overflow: hidden; }
  table.data.responsive td { display: flex; justify-content: space-between; gap: 16px; padding: 11px 15px; border-bottom: 1px solid var(--line); text-align: right; align-items: baseline; overflow-wrap: anywhere; min-width: 0; }
  table.data.responsive td:last-child { border-bottom: 0; }
  /* The label mustn't shrink; the value takes the remaining width and wraps
     instead of overflowing the card (long content previews clipped off-screen). */
  table.data.responsive td::before { content: attr(data-label); flex: 0 0 auto; font-weight: 700; color: var(--muted); font-size: .74rem; text-transform: uppercase; letter-spacing: .04em; text-align: left; }
  table.data.responsive td:empty { display: none; }
  /* In card mode the preview should wrap normally, not use the desktop's
     single-line truncation which pushed text off the right edge. */
  table.data.responsive td.preview-cell { white-space: normal; }

  /* Condensed player cards: two lines. Line 1 is the name (full width); line 2
     pairs the club (left) with the grade badge (right). Reordered with flex
     `order` so the club sits beside the grade even though Grade precedes Club in
     the DOM. The "PLAYER"/"GRADE" labels are dropped as redundant. */
  table.players-table.responsive tr { display: flex; flex-wrap: wrap; align-items: center; margin-bottom: 8px; }
  table.players-table.responsive td { padding: 6px 14px; border-bottom: 0; }
  /* Name: own full-width line. */
  table.players-table.responsive td[data-label="Player"] { order: 1; flex: 1 1 100%; justify-content: flex-start; padding-top: 9px; padding-bottom: 1px; }
  table.players-table.responsive td[data-label="Player"]::before { display: none; }
  table.players-table.responsive td[data-label="Player"] .player-row { font-size: 1.02rem; font-weight: 700; }
  /* Club: left of line 2, grows to push the badge to the right. Muted subline. */
  table.players-table.responsive td[data-label="Club / City"] { order: 2; flex: 1 1 auto; min-width: 0; justify-content: flex-start; padding-top: 1px; padding-bottom: 9px; color: var(--muted); font-size: .88rem; }
  table.players-table.responsive td[data-label="Club / City"]::before { display: none; }
  /* Grade badge: right of line 2, never shrinks, no label. */
  table.players-table.responsive td[data-label="Grade"] { order: 3; flex: 0 0 auto; justify-content: flex-end; padding-top: 1px; padding-bottom: 9px; }
  table.players-table.responsive td[data-label="Grade"]::before { display: none; }
}

/* Sub-phone refinement (narrower than the sm tier) — intentional exception to the
   3-breakpoint scale, only for tightening these two grids on very small screens. */
@media (max-width: 400px) {
  .grade-grid { grid-template-columns: repeat(2, 1fr); }
  .player-grid { grid-template-columns: 1fr 1fr; }
}

/* ============================ Utilities =============================== */
/* Small, composable helpers that replace one-off inline styles. Spacing uses
   the --space-* scale so values stay consistent site-wide. */
.text-muted { color: var(--muted); }
.text-sm { font-size: .9rem; }
.text-center { text-align: center; }
.nowrap { white-space: nowrap; }
.w-full { width: 100%; }

.mt-0 { margin-top: 0; }
.mt-1 { margin-top: var(--space-1); }
.mt-2 { margin-top: var(--space-2); }   /* 8px  */
.mt-3 { margin-top: var(--space-3); }   /* 12px */
.mt-4 { margin-top: var(--space-4); }   /* 16px */
.mt-5 { margin-top: var(--space-5); }   /* 24px */
.mt-6 { margin-top: var(--space-6); }   /* 32px */
.mb-3 { margin-bottom: var(--space-3); }
.mx-auto { margin-left: auto; margin-right: auto; }

/* Horizontal flex row of controls/items — replaces the repeated inline
   display:flex; align-items:center; gap; flex-wrap cluster. */
.inline-row { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }

/* Circular avatar image, three sizes (was inline width/height/border-radius). */
.avatar { width: 52px; height: 52px; border-radius: 50%; object-fit: cover; display: inline-block; vertical-align: middle; }
.avatar-md { width: 48px; height: 48px; }
.avatar-sm { width: 34px; height: 34px; }
