/*
   HumberSquash design system
   --------------------------
   Mobile-first. Single source of truth: CSS custom properties below.
   Layout: CSS Grid (12-col content grid + outer page grid).
   Breakpoints:
     - default      : phone (single column, stacked)
     - >=  720px    : tablet (sidebar + main, right rail wraps under main)
     - >= 1080px    : desktop (left rail | main | right rail, three columns)
*/

/* ------------------------------------------------------------------ */
/*  Tokens                                                            */
/* ------------------------------------------------------------------ */

:root {
    /* Brand palette — derived from the legacy site, with stronger contrast. */
    --hs-red:           #C0341E;
    --hs-red-dark:      #8E2515;
    --hs-red-soft:      #FBE9E5;
    --flame:            #FF5A36;   /* vibrant accent for active/hover energy */
    --flame-2:          #FF8A3D;
    --hs-blue:          #0E4D8B;
    --hs-blue-hover:    #073A6E;
    --hs-cream:         #FFF8DC;       /* table row highlight, from legacy #EDEEC0 */
    --hs-cream-border:  #E8DDA8;

    /* Surfaces & text */
    --bg:               #F5F4F1;
    --surface:          #FFFFFF;
    --surface-2:        #F5F5F5;
    --border:           #DCDCDC;
    --border-strong:    #BDBDBD;
    --text:             #222222;
    --text-muted:       #5A5A5A;

    /* Type */
    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
                 "Helvetica Neue", Arial, sans-serif;
    /* Headings, figures and scorelines. The system stack is the fallback rather than a
       placeholder: Archivo is asked for with display=swap in _Layout, so a visitor who never
       receives it reads the site in exactly the face it used before. */
    --font-display: "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
                    "Helvetica Neue", Arial, sans-serif;
    --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    --fs-100:   0.8125rem;
    --fs-200:   0.9375rem;
    --fs-300:   1rem;
    --fs-400:   1.125rem;
    --fs-500:   1.375rem;
    --fs-600:   1.75rem;
    --fs-700:   2.125rem;
    --lh-tight: 1.25;
    --lh-body:  1.55;

    /* Space scale (4px base) */
    --sp-1: 0.25rem;
    --sp-2: 0.5rem;
    --sp-3: 0.75rem;
    --sp-4: 1rem;
    --sp-5: 1.5rem;
    --sp-6: 2rem;
    --sp-7: 3rem;
    --sp-8: 4rem;

    /* Radius / elevation */
    --radius-sm: 4px;
    --radius:    8px;
    --radius-lg: 12px;
    --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.06);
    --shadow:    0 2px 8px rgb(0 0 0 / 0.08);

    /* Result semantics. NOT brand colours: a win is green and a loss is red on every
       site, whatever the league's palette is, so these deliberately do not follow a
       rebrand. Held as tokens all the same so the two places that draw a scoreline
       cannot drift apart. */
    --win:      #2E6F47;
    --loss:     #9A3324;
    --ok:       #2E6F47;
    --ok-ink:   #1F5936;
    --ok-soft:  #EAF3ED;

    /* Layout */
    --content-max: 1320px;
}

/* ------------------------------------------------------------------ */
/*  Reset                                                             */
/* ------------------------------------------------------------------ */

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; tab-size: 4; }
body {
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--fs-300);
    line-height: var(--lh-body);
    color: var(--text);
    background: var(--bg);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

img, svg, video { display: block; max-width: 100%; height: auto; }
button, input, select, textarea { font: inherit; }

a {
    color: var(--hs-blue);
    text-decoration: underline;
    text-underline-offset: 2px;
}
a:hover  { color: var(--hs-blue-hover); }
a:focus-visible { outline: 2px solid var(--hs-blue); outline-offset: 2px; border-radius: 2px; }

/* Hierarchy does the work the borders used to do, so the steps between the levels have
   to be big enough to read as steps. A heading also OPENS the block under it: the space
   above one is part of the separation between sections, which is why it is set here and
   not left to each page to remember. */
h1, h2, h3, h4 {
    line-height: var(--lh-tight); margin: 0 0 var(--sp-4) 0;
    color: var(--hs-red); font-family: var(--font-display); text-wrap: balance;
}
h1 { font-size: var(--fs-700); font-weight: 800; letter-spacing: -0.025em; }
h2 { font-size: var(--fs-600); font-weight: 700; letter-spacing: -0.02em; margin-top: var(--sp-6); }
h3 { font-size: var(--fs-400); font-weight: 700; letter-spacing: -0.01em; margin-top: var(--sp-5); }
h4 { font-size: var(--fs-300); font-weight: 600; color: var(--text); }
:is(h1, h2, h3, h4) + :is(h2, h3, h4) { margin-top: var(--sp-3); }
.card > :is(h1, h2, h3):first-child,
section > :is(h2, h3):first-child { margin-top: 0; }

p  { margin: 0 0 var(--sp-4) 0; }
ul, ol { padding-left: 1.25rem; margin: 0 0 var(--sp-4) 0; }
li + li { margin-top: var(--sp-1); }

/* ...but ONLY in prose. A list that has had its marker removed is not a bulleted list any more,
   it is a layout container - a nav bar, a set of link cards, a breadcrumb - and 4px of top
   margin on every item except the first is wrong in all of them.

   It was wrong in a way that took a while to find, because in a horizontal flex nav the symptom
   does not look like a margin at all: the first item has none, every other item has 4px, and
   under `align-items: stretch` that margin comes out of each neighbour's available height. The
   first pill computed 4px TALLER than its neighbours (28.79px against 24.79px) with identical
   padding, font and line-height, and nothing in the nav's own rules to explain it. The wrap
   point moving with the window width made it come and go.

   Each of these lists already resets `list-style`, `margin` and `padding` on the UL and stops
   there. The items need the same treatment, so it is stated once here rather than repeated.
   A new layout list belongs in this selector. */
.nav__list > li,
.nav__mega-list > li,
.subnav > li,
.linklist > li,
.team-list > li,
.breadcrumb ol > li,
.msgs__list > li,
.newspage .stories > li { margin-top: 0; }
hr { border: 0; border-top: 1px solid var(--border); margin: var(--sp-5) 0; }

.visually-hidden {
    position: absolute !important; width: 1px; height: 1px; padding: 0;
    margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap;
    border: 0;
}

/* Skip link — first focusable element for keyboard users */
.skip-link {
    position: absolute; left: var(--sp-3); top: var(--sp-3);
    transform: translateY(-250%);
    background: var(--hs-red); color: #fff; padding: var(--sp-2) var(--sp-3);
    border-radius: var(--radius-sm); z-index: 1000; font-weight: 600;
    transition: transform .15s ease;
}
.skip-link:focus { transform: translateY(0); }

/* ------------------------------------------------------------------ */
/*  Page layout (CSS Grid)                                            */
/* ------------------------------------------------------------------ */

.page {
    display: grid;
    grid-template-rows: auto 1fr auto;
    min-height: 100vh;
}

.shell {
    width: 100%;
    max-width: var(--content-max);
    margin-inline: auto;
    padding-inline: var(--sp-3);   /* tight gutters on mobile; widened below */
}
/* More breathing room down the sides on tablet/desktop so content isn't squashed */
@media (min-width: 720px)  { .shell { padding-inline: var(--sp-6); } }
@media (min-width: 1080px) { .shell { padding-inline: var(--sp-7); } }

/* Header — "neon energy": one dark gradient bar (logo · nav · account actions)
   that lights up under the cursor, with a value (USP) strip beneath. */
.site-header {
    position: relative;
    color: #fff;
    background: radial-gradient(140% 220% at 0% 0%, var(--header-from, #3c130c) 0%, var(--header-mid, #1c1310) 55%, var(--header-to, #130d0c) 100%);
    border-bottom: 3px solid var(--hs-red);
}
.site-header::before {                      /* cursor-follow glow */
    content: "";
    position: absolute; inset: 0;
    pointer-events: none;
    background: radial-gradient(340px 150px at var(--mx, 12%) 0%, color-mix(in srgb, var(--flame, #FF5A36) 30%, transparent), transparent 70%);
    transition: background .2s ease;
}

.header-bar {
    position: relative; z-index: 5;   /* above the USP bar so mega dropdowns overlay it */
    display: flex; align-items: center; gap: var(--sp-4);
    padding-block: var(--sp-3);
}

/* Logo chip. White by default, because the squash mark was drawn for paper and needs a light
   ground to read on the dark bar — but a logo that already carries its own background wants no
   chip at all, so the backdrop is a theme setting and "transparent" drops the padding and the
   shadow with it (both emitted together, so they cannot disagree). */
.brand {
    display: inline-flex; align-items: center; flex: none; text-decoration: none;
    background: var(--logo-backdrop, #fff);
    border-radius: 10px;
    padding: var(--logo-chip, 6px 12px);
    box-shadow: var(--logo-shadow, var(--shadow-sm));
}
.brand__logo { display: block; height: 40px; width: auto; }
@media (min-width: 720px) { .brand__logo { height: 58px; } }
/* A site with no logo yet shows its name in the chip instead of the other league's mark. */
.brand__name { display: block; font-weight: 700; font-size: 1.05rem; line-height: 1.2; color: var(--text); }
@media (min-width: 720px) { .brand__name { font-size: 1.3rem; } }

/* Account / sign-out actions (e-commerce style icon buttons) */
.header-actions { display: flex; align-items: center; gap: 4px; flex: none; padding-left: var(--sp-2); }
.uitem-form { margin: 0; }
.uitem {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 8px 13px; border-radius: 11px; border: 1px solid transparent;
    font: inherit; font-size: var(--fs-300); font-weight: 600; cursor: pointer;
    color: rgba(255,255,255,.82); background: transparent; text-decoration: none;
    transition: background-color .2s, color .2s, transform .18s ease, box-shadow .2s;
}
.uitem svg { width: 19px; height: 19px; flex: none; }

/* The unread count on the inbox icon.

   LIGHT on the dark header, not brand red. Red on red was the version that shipped, and it
   failed contrast badly; white against the brand is also the wrong way round here, because the
   header is already dark and a dark badge disappears into it. A near-white disc with the brand
   colour inside it reads at a glance and clears 4.5:1, which small bold text needs. */
.uitem--badged { position: relative; }
.uitem__badge {
    /* ON the icon, at its top right, the way a badge is read everywhere else. Sitting it
       beside the envelope made it a second item in the row rather than a mark on the first. */
    position: absolute; top: 2px; right: 6px;
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 1.05rem; height: 1.05rem; padding: 0 0.28rem;
    border-radius: 999px;
    background: #fff; color: var(--hs-red-dark);
    font-size: 0.68rem; font-weight: 700; line-height: 1;
    font-variant-numeric: tabular-nums;
    /* Separates the disc from the icon it overlaps. The header is a gradient and the badge sits
       toward its right-hand end, so the ring takes --header-to rather than inventing a token
       that does not exist -- which is what --header-bg was. */
    box-shadow: 0 0 0 2px var(--header-to, #130d0c);
}

/* hidden HAS to win here. [hidden] and a class have the same specificity, so the class wins on
   source order alone -- which is why an EMPTY white disc sat in the header when there was
   nothing unread: the element was correctly hidden and this rule displayed it anyway. */
.uitem__badge[hidden] { display: none; }
.uitem:hover { background: rgba(255,255,255,.09); color: #fff; transform: translateY(-1px); }
/* Glows derive from the theme, never from a literal.
   These four box-shadows were written as rgba(255,90,54,...) and rgba(255,138,61,...) - which
   are exactly --flame and --flame-2 spelled out - so the pill took the site's palette while the
   halo around it stayed squash orange on every other site. A second league could set its whole
   palette and still be ringed in somebody else's brand. color-mix keeps the alpha without
   restating the colour. */
.uitem--primary {
    color: #fff; background: linear-gradient(90deg, var(--hs-red), var(--flame));
    box-shadow: 0 8px 22px -10px color-mix(in srgb, var(--flame, #FF5A36) 85%, transparent);
}
.uitem--primary:hover { color: #fff; border-color: #fff; transform: translateY(-1px); }

/* Account menu — the one trigger that replaced "My profile", "Admin" and "Sign out".

   Built on <details>/<summary> so it opens, closes and takes keyboard focus with no script.
   The marker is removed on both spellings because Safari still wants ::-webkit-details-marker
   and would otherwise render a disclosure triangle inside the pill. */
.acct { position: relative; }
.acct > summary::-webkit-details-marker { display: none; }
.acct > summary { list-style: none; }
.acct__trigger { user-select: none; }
.acct__trigger::-moz-list-bullet { list-style-type: none; }
.acct[open] > .acct__trigger { background: rgba(255,255,255,.12); color: #fff; }
.acct__chev { transition: transform .18s ease; }
.acct[open] .acct__chev { transform: rotate(180deg); }

/* The disc. A fixed circle rather than a padded span, so a "W" and an "I" leave the trigger
   exactly the same width and the bar does not shift as people sign in and out. */
.acct__avatar {
    display: inline-flex; align-items: center; justify-content: center;
    width: 26px; height: 26px; flex: none; border-radius: 50%;
    background: linear-gradient(135deg, var(--hs-red), var(--flame));
    color: #fff; font-size: var(--fs-200); font-weight: 800; line-height: 1;
}

/* One long name must not stretch the header across the page. */
.acct__name { max-width: 11ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.acct__menu {
    position: absolute; right: 0; top: calc(100% + 8px); z-index: 60;
    min-width: 232px; padding: 6px; border-radius: 14px;
    background: #fff; color: #1c1c1c;
    border: 1px solid rgba(0,0,0,.08);
    box-shadow: 0 18px 44px -18px rgba(0,0,0,.55);
}

/* Who you are, stated once at the top — the thing three unlabelled verbs never told anybody.
   The name can be an address, so it wraps rather than overflowing the card. */
.acct__who {
    margin: 0; padding: 10px 12px 12px;
    font-size: var(--fs-200); color: #5b5b5b; line-height: 1.45;
    border-bottom: 1px solid rgba(0,0,0,.08);
    overflow-wrap: anywhere;
}
.acct__who strong { color: #1c1c1c; font-weight: 700; }

.acct__link {
    display: flex; align-items: center; gap: 10px; width: 100%;
    padding: 10px 12px; margin-top: 2px; border: 0; border-radius: 10px;
    font: inherit; font-size: var(--fs-300); font-weight: 600; text-align: left;
    color: #1c1c1c; background: transparent; text-decoration: none; cursor: pointer;
}
.acct__link svg { width: 18px; height: 18px; flex: none; color: #6a6a6a; }
/* colour AND background. Setting only the background let the global `a:hover` rule win, so a
   menu item turned link-blue on hover -- on a dark panel, which is where it showed worst. A
   menu item is not a link in a paragraph; it should darken, not change colour. */
.acct__link:hover,
.acct__link:focus-visible { background: rgba(0,0,0,.055); color: #1c1c1c; }
.acct__link:focus-visible { outline: 2px solid var(--flame, #FF5A36); outline-offset: -2px; }

/* Leaving is separated from the rest and coloured, because a sign-out sitting flush against
   "My profile" in identical type is the mis-click that ends somebody's session. */
.acct__link--out { margin-top: 6px; border-top: 1px solid rgba(0,0,0,.08); border-radius: 0 0 10px 10px; color: var(--hs-red, #c62828); }
.acct__link--out svg { color: currentColor; }
.acct__link--out:hover,
.acct__link--out:focus-visible {
    background: color-mix(in srgb, var(--hs-red, #c62828) 9%, transparent);
    color: var(--hs-red, #c62828);
}

@media (prefers-color-scheme: dark) {
    .acct__menu { background: #1f1f22; color: #f2f2f2; border-color: rgba(255,255,255,.1); }
    .acct__who { color: #a9a9ad; border-bottom-color: rgba(255,255,255,.1); }
    .acct__who strong { color: #f2f2f2; }
    .acct__link { color: #f2f2f2; }
    .acct__link svg { color: #a9a9ad; }
    .acct__link:hover,
    .acct__link:focus-visible { background: rgba(255,255,255,.08); color: #f2f2f2; }
    .acct__link--out:hover,
    .acct__link--out:focus-visible { color: var(--hs-red, #E2555B); }
    .acct__link--out { border-top-color: rgba(255,255,255,.1); }
}


/* ---- the clubs directory ------------------------------------------------
   Shares .clubpage's tokens, hero, band and figures on purpose: a visitor who lands here and
   then opens a club should feel they went FURTHER INTO one thing, not across to another. What
   it does not share is the per-club hero - nine of those stacked is nine mastheads and no
   directory - so each club is a card and the page keeps one. */
.clubdir .hero { padding-block: var(--sp-7) var(--sp-6); }
.clubdir__lede {
    margin: var(--sp-4) 0 0; max-width: 46ch;
    color: var(--cp-on-brand-dim); font-size: var(--fs-300);
}
.clubdir__notice {
    margin: var(--sp-6) 0 0; padding: var(--sp-3) var(--sp-4);
    border-left: 3px solid var(--flame);
    background: var(--surface-2); font-size: var(--fs-200); color: var(--text-muted);
}
.clubdir__empty { margin: var(--sp-7) 0; color: var(--text-muted); }

/* auto-fit rather than a fixed count: nine clubs and two read the same way, and a league that
   grows does not need this number changed. */
/* auto-FILL for the same reason as the courts grid: a league with one club should not get one
   card stretched across the page. */
.clubdir__grid {
    display: grid; gap: var(--sp-5);
    grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
    margin-block: var(--sp-6) var(--sp-7);
}

.cd {
    display: flex; flex-direction: column;
    padding: var(--sp-5); border: 1px solid var(--border); background: var(--surface);
    /* The flame edge is the only colour on the card. It ties the grid to the band above it
       without nine cards each shouting a different accent. */
    border-top: 3px solid var(--flame);
}
.cd__head { display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap; }
.cd__name { margin: 0; font-size: 1.25rem; font-weight: 700; letter-spacing: -0.02em; }
.cd__name a { color: var(--text); text-decoration: none; }
.cd__name a:hover { color: var(--hs-red); text-decoration: underline; }
.cd__since {
    margin: 0; font-family: var(--cp-mono); font-size: 0.625rem;
    letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted);
}

/* The one figure carried across from the club page. A club with nothing recorded shows no
   figure at all rather than a zero: "0 courts" reads as a fact about the club when it is a
   fact about our database. */
.cd__courts { display: flex; align-items: baseline; gap: var(--sp-2); margin: var(--sp-4) 0 0; }
.cd__courts b {
    font-size: 1.75rem; font-weight: 800; line-height: 1;
    letter-spacing: -0.03em; color: var(--flame); font-variant-numeric: tabular-nums;
}
.cd__courts span {
    font-family: var(--cp-mono); font-size: 0.625rem;
    letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted);
}
.cd__courts--shared b { color: var(--border-strong); font-size: 1.25rem; }

.cd__where {
    display: flex; gap: var(--sp-2); align-items: flex-start;
    margin: var(--sp-3) 0 0; font-size: var(--fs-200); color: var(--text-muted);
}
.cd__where svg { width: 13px; height: 13px; flex: none; margin-top: 0.2em; }
.cd__map { margin-left: var(--sp-2); white-space: nowrap; }

.cd__divs { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin: var(--sp-4) 0 0; }
.cd__div {
    font-family: var(--cp-mono); font-size: 0.625rem; letter-spacing: 0.1em;
    text-transform: uppercase; padding: 3px 8px;
    border: 1px solid var(--border-strong); color: var(--text-muted);
}

.cd__teams { list-style: none; margin: var(--sp-4) 0 0; padding: 0; }
.cd__teams li {
    margin: 0; padding-block: var(--sp-2);
    border-top: 1px solid var(--border);
    display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2) var(--sp-3);
}
/* Subtle by design: the team name keeps the body colour and wears a faint underline rather
   than link blue. A card already has the club name and "Club page" as links; four more obvious
   ones per card would read as a list of links with some words around them.

   text-decoration-color, not "no underline": an underline that only appears on hover is
   invisible to anyone not using a mouse, and this one is always present -- just quiet. */
.cd__team {
    font-weight: 600; font-size: var(--fs-300);
    color: var(--text); text-decoration: underline;
    text-decoration-color: var(--border-strong);
    text-underline-offset: 3px; text-decoration-thickness: 1px;
}
.cd__team:hover,
.cd__team:focus-visible { color: var(--hs-red); text-decoration-color: currentColor; }
.cd__cap { font-size: var(--fs-200); color: var(--text-muted); }
/* The contact runs onto its own line so a long address cannot squeeze the team name. */
.cd__contact { flex-basis: 100%; font-size: var(--fs-100); color: var(--text-muted); }
.cd__contact a + span { margin-left: var(--sp-2); }

.cd__noteams {
    margin: var(--sp-4) 0 0; font-size: var(--fs-200); color: var(--text-muted); font-style: italic;
}
/* margin-top:auto pins it to the bottom whatever the card above it holds, so a row of cards
   has its links on one line instead of stepped. */
.cd__more { margin: auto 0 0; padding-top: var(--sp-4); font-size: var(--fs-200); font-weight: 600; }

/* A table row that needs somebody's attention -- a team with no captain, a fixture with no
   result. Used by five admin pages and defined by NONE of them until now, so every one of them
   was asking for an emphasis that rendered as nothing at all.

   A left edge rather than a background wash: it survives the striped-row rule underneath it,
   reads in both themes, and does not fight the semantic colour of any tag inside the row. */
tr.row--attention > td:first-child { box-shadow: inset 3px 0 0 0 var(--flame); }
tr.row--attention > td { background: color-mix(in srgb, var(--flame) 5%, transparent); }

/* Latest news on the front page -- CARDS.
   ------------------------------------------------------------------
   It was a list with a grid on it: three headlines stacked in a column, which reads as a table
   of contents. The same three side by side read as "here is what is happening", which is the
   job the block is doing on a front page.

   The whole card is one anchor. One target the width of the card beats a small headline plus a
   separate "read more": easier to hit on a phone, and a screen reader announces one thing
   rather than two that lead to the same place. */

.home-news__head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-5);
}
.home-news__head h2 { margin: 0; }
.home-news__all { font-size: var(--fs-200); font-weight: 600; white-space: nowrap; }

/* auto-FILL again: a league that has written ONE article should get one card, not one banner. */
.home-news {
    display: grid; gap: var(--sp-4);
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    margin: var(--sp-4) 0 var(--sp-6);
}

.hn {
    display: flex; flex-direction: column; gap: var(--sp-2);
    padding: var(--sp-4);
    border: 1px solid var(--border);
    /* The flame edge ties these to the club cards on /clubs without a second accent colour. */
    border-top: 3px solid var(--flame);
    background: var(--surface);
    color: var(--text); text-decoration: none;
    transition: border-color .15s ease, transform .15s ease, box-shadow .15s ease;
}
.hn:hover, .hn:focus-visible {
    border-color: var(--border-strong);
    transform: translateY(-2px);
    box-shadow: 0 10px 24px -16px rgb(0 0 0 / 0.4);
    text-decoration: none;
}
@media (prefers-reduced-motion: reduce) { .hn { transition: none; } .hn:hover { transform: none; } }

.hn__when {
    font-family: var(--font-mono); font-size: 0.6875rem;
    letter-spacing: 0.12em; text-transform: uppercase; color: var(--flame);
}
.hn__title {
    font-size: 1.0625rem; font-weight: 700; line-height: 1.25;
    letter-spacing: -0.01em; text-wrap: balance;
}
.hn:hover .hn__title, .hn:focus-visible .hn__title { color: var(--hs-red); }
/* The summary takes the room left over, so a short one and a long one still leave the
   "Read" line on the same baseline across a row of cards. */
.hn__summary { flex: 1 1 auto; font-size: var(--fs-200); color: var(--text-muted); }
.hn__go {
    margin-top: auto; padding-top: var(--sp-2);
    font-size: var(--fs-200); font-weight: 600; color: var(--hs-red);
}

/* USP / value strip */
.usp-bar { position: relative; z-index: 1; background: rgba(0,0,0,.28); border-top: 1px solid rgba(255,255,255,.08); }
.usp { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px 56px;
       padding-block: 11px; font-size: .82rem; font-weight: 600; color: rgba(255,255,255,.9); }
.usp .u { display: inline-flex; align-items: center; gap: 10px; }
.usp svg { width: 18px; height: 18px; flex: none; color: var(--flame-2); }

/* Primary navigation — sits in the dark header bar, pushed to the right.
   Mobile: collapses behind a toggle into a full-width dropdown. Desktop:
   inline pills with a flame-gradient active state and mega dropdowns. */
.nav { margin-left: auto; }
.nav__list { list-style: none; margin: 0; padding: 0; }
.nav__link {
    display: block; text-decoration: none; font-weight: 600;
    color: rgba(255,255,255,.78); padding: 10px 14px; border-radius: 9px;
    transition: color .2s ease, background-color .2s ease, box-shadow .25s ease;
}
.nav__link:hover { color: #fff; background: rgba(255,255,255,.08); }
.nav__link[aria-current="page"] {
    color: #fff; background: linear-gradient(90deg, var(--hs-red), var(--flame));
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--flame-2, #FF8A3D) 45%, transparent),
                0 8px 24px -8px color-mix(in srgb, var(--flame, #FF5A36) 70%, transparent);
}

.nav__mega-list { list-style: none; margin: 0; padding: 0; }
.nav__mega-list a {
    display: block; text-decoration: none; color: rgba(255,255,255,.82);
    padding: 9px 14px; border-radius: 8px; font-size: var(--fs-200);
}
.nav__mega-list a:hover { background: rgba(255,255,255,.09); color: #fff; }
.nav__mega-list a[aria-current="page"] { color: #fff; font-weight: 600; }

.nav__toggle {
    display: inline-flex; align-items: center; gap: var(--sp-2); margin-left: auto;
    padding: var(--sp-2) var(--sp-3);
    background: transparent; color: #fff; border: 1px solid rgba(255,255,255,.45);
    border-radius: var(--radius-sm); font-weight: 600; cursor: pointer;
}
.nav__toggle-icon, .nav__toggle-icon::before, .nav__toggle-icon::after {
    content: ""; display: block; width: 18px; height: 2px; background: #fff;
}
.nav__toggle-icon { position: relative; }
.nav__toggle-icon::before { position: absolute; top: -6px; left: 0; }
.nav__toggle-icon::after  { position: absolute; top:  6px; left: 0; }

/* Mobile: dropdown menu hidden until toggled */
.nav__list { display: none; }
/* The collapsed nav runs up to and including 900px, not 719px.
   ------------------------------------------------------------------
   720 is a phone/desktop split and this is a tablet: at 768-834 (iPad mini, iPad, iPad Pro
   11" in portrait) the inline nav had to carry six top-level items, a logo and the account
   menu, and wrapped onto a second row — which then pushed the whole page down and looked
   like a fault rather than a layout.

   900 clears every tablet in portrait up to the 11" Pro at 834. The 12.9" Pro (1024) and
   every laptop keep the inline nav. If the menu grows past six items this is the number to
   raise, and 1024 is the next sensible stop — it catches the large tablet too. */
@media (max-width: 900px) {
    .nav__list {
        position: absolute; left: 0; right: 0; top: 100%; z-index: 60;
        background: var(--header-mid, #1c1310); border-top: 1px solid rgba(255,255,255,.12);
        padding: 8px 16px 14px; box-shadow: 0 20px 40px -16px rgba(0,0,0,.7);
    }
    .nav--open .nav__list { display: block; }
    .nav__item { display: block; }
    .nav__link { padding: 12px 12px; }
    .nav__mega-list a { padding-left: var(--sp-5); }
    .header-actions .uitem span { display: none; }   /* icon-only to save room */
    /* ...but the avatar IS the icon, so it survives the rule above. Without this the account
       trigger on a phone is a bare chevron with nothing to say what it opens. */
    .header-actions .acct__trigger .acct__avatar { display: inline-flex; }
    .acct__menu { min-width: 208px; }
    .header-actions .uitem { padding: 8px; }

    /* Mobile nav = accordion: sections collapse; only level 1 shows by default. */
    .nav__item--has-menu > .nav__link {
        display: flex; align-items: center; justify-content: space-between;
    }
    .nav__item--has-menu > .nav__link::after {
        content: ""; width: .5em; height: .5em; margin-left: var(--sp-3); opacity: .85;
        border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
        transform: rotate(45deg); transition: transform .2s ease;
    }
    .nav__item--has-menu.is-open > .nav__link::after { transform: rotate(-135deg); }
    .nav__item--has-menu > .nav__mega { display: none; }
    .nav__item--has-menu.is-open > .nav__mega { display: block; }

    /* USP strip: show 2 at a time (JS rotates them on a timer). */
    .usp { justify-content: center; gap: 8px 22px; font-size: .78rem; }
}
@media (min-width: 901px) {
    .nav__toggle { display: none; }
    /* The uneven pill heights were NOT caused here - the cause was the prose rule
       `li + li { margin-top }` reaching into the nav, and it is fixed at source near the top of
       this file. This comment used to blame `align-items: stretch`, which was wrong, and is
       corrected rather than deleted because the wrong explanation is the instructive part: a
       4px margin on every item except the first showed up as the FIRST item computing 4px
       taller, which looks like anything but a margin.

       These three rules stand on their own merits. `center` rather than `stretch` so an item's
       height never depends on its neighbours; `min-height: 44px` because that is the minimum
       comfortable touch target and a nav item should clear it whatever the font settings do. */
    .nav__list { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }
    .nav__item { position: relative; display: flex; align-items: center; }
    .nav__link { display: flex; align-items: center; min-height: 44px; }
    .nav__mega {
        position: absolute; top: 100%; left: 0; min-width: 230px; z-index: 50;
        background: var(--header-mid, #1c1310); border: 1px solid rgba(255,255,255,.1);
        border-radius: 0 0 12px 12px; box-shadow: 0 20px 44px -18px rgba(0,0,0,.7);
        padding: 8px;
        opacity: 0; visibility: hidden; transform: translateY(-6px);
        transition: opacity .15s ease, transform .15s ease, visibility .15s;
    }
    .nav__item--has-menu:hover > .nav__mega,
    .nav__item--has-menu:focus-within > .nav__mega {
        opacity: 1; visibility: visible; transform: none;
    }
    .nav__mega-list a { white-space: nowrap; }
}

/* Main grid: ONE column, at every width.
   ------------------------------------------------------------------
   It used to be `220px 1fr` from 720px up, and that narrow first track held exactly one
   thing: the home page's "Quick links" rail, an <aside class="left-rail">. When the quick
   links were removed at the owner's request the rail went with them and the track did not,
   so the grid was left with an empty 220px column and an opt-in list of children allowed to
   span it (.article, .breadcrumb, .right-rail).

   Anything NOT on that list fell into the 220px track. The home page's announcement block is
   an <aside class="card">, so the padel league — which has written one — had its whole
   "How the league works" section rendered in a 220px column with the rest of the page blank
   beside it. Squash never saw it, because squash has no announcement row.

   Collapsing it to one column removes the trap rather than adding a fourth class to the
   allow-list. Nothing uses .left-rail or .right-rail any more — grep the web project. */
.main-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--sp-3);
    padding-block: var(--sp-4);
    align-items: start;     /* cards size to their content, don't stretch full width */
    align-content: start;   /* pack rows at the top — don't centre/space them in the tall main area */
}
/* Let grid items shrink below content width so wide tables scroll INSIDE their
   panel (via .table-wrap) instead of stretching the page sideways. */
.main-grid > * { min-width: 0; }

@media (min-width: 720px) {
    .main-grid { padding-block: var(--sp-5); }
}

/* Content section — `.card` in the markup, and NOT a card any more.
   -----------------------------------------------------------------
   `.card` used to draw a white, bordered, rounded, shadowed box, and 160 places in
   this application ask for one. The result was a site with no hierarchy at all: the
   league table, a two-line filter form, an empty state and the footer's newsletter
   signup were the same object at the same elevation, stacked, so nothing on any page
   looked more important than anything else and the eye had nowhere to go.

   So the class stays — renaming it would mean editing 86 files to change nothing a
   reader can see — and the treatment goes. A section is now separated by SPACE and
   one hairline rule, and told apart by its heading. The mobile rules above already
   did exactly this from 2019 onwards, which is the strongest evidence it works: the
   phone view was the readable one.

   The exceptions are deliberate and each says so where it is written: the admin's
   dense back-office panels (bottom of this file), tiles that are genuinely objects in
   a grid (.player-card), and anything that must be noticed rather than read (.alert).

   The separator is scoped to the places cards actually STACK. Applied to `.card +
   .card` flat it would also draw a rule above every tile but the first in a four-up
   grid, where "the previous sibling" is the tile to the LEFT and a top border is
   meaningless. */
.card {
    background: transparent;
    border: 0;
    border-radius: 0;
    padding: 0;
    box-shadow: none;
}
.article > .card + .card,
.main-grid > .card + .card {
    margin-top: var(--sp-6);
    padding-top: var(--sp-6);
    border-top: 1px solid var(--border);
}
@media (max-width: 719px) {
    .article > .card + .card,
    .main-grid > .card + .card { margin-top: var(--sp-5); padding-top: var(--sp-5); }
}
.card--flush { padding: 0; }

/* The section label above a block that has no <h2> of its own — a division name over a
   standings table, "Downloads" over a list. Small caps rather than a heading size,
   because it labels a block rather than opening one. */
.card__title {
    margin: 0 0 var(--sp-3);
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.08em;
}
.card--flush .card__title { margin: 0 0 var(--sp-2); padding: 0; border: 0; }

/* Tiles in a grid ARE objects, and keep an edge: four of them side by side with no
   border is one run-on paragraph in four columns. */
.players-grid > .card {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: var(--sp-4);
    background: var(--surface);
}

/* Something the reader must NOTICE rather than read keeps its box on purpose. These
   were only ever defined under `.admin`, so a public page emitting `.alert` markup —
   the archived-club warning on a club page, for one — rendered it as a bare
   paragraph, which is the one case where a plain paragraph is the wrong answer. */
.alert {
    margin: 0 0 var(--sp-4);
    padding: var(--sp-3) var(--sp-4);
    border: 1px solid var(--border);
    border-left: 4px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    font-size: var(--fs-200);
}
.alert:last-child { margin-bottom: 0; }
.alert--ok    { border-left-color: var(--ok); background: var(--ok-soft); color: var(--ok-ink); }
.alert--error { border-left-color: var(--hs-red); background: var(--hs-red-soft); color: var(--hs-red-dark); }
.alert:empty  { display: none; }

/* A tag is a fact about the thing beside it — an accreditation, a price, a status. */
.tag {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-1);
    padding: 2px var(--sp-2);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    font-size: var(--fs-100);
    line-height: 1.4;
    color: var(--text-muted);
    white-space: nowrap;
}
.tag--ok    { border-color: var(--ok); color: var(--ok-ink); background: var(--ok-soft); }
.tag--muted { border-color: var(--border); }
.tag--warn  { border-color: var(--hs-red); color: var(--hs-red-dark); background: var(--hs-red-soft); }

/* Sidebar nav (left rail) — clean continuous list, no chunky blocks. */
.subnav {
    list-style: none;
    margin: 0;
    padding: var(--sp-1) 0;
    display: block;
}
.subnav li + li > a { border-top: 1px solid var(--border); }
.subnav a {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
    padding: var(--sp-3) var(--sp-5);
    color: var(--text);
    text-decoration: none;
    font-weight: 500;
    box-shadow: inset 0 0 0 0 var(--hs-red);   /* animatable left bar */
    transition: background-color .15s ease, color .15s ease, box-shadow .15s ease;
}
.subnav a::after {
    content: "›";
    font-size: 1.1em;
    line-height: 1;
    color: currentColor;
    opacity: 0;
    transform: translateX(-4px);
    transition: opacity .15s ease, transform .15s ease;
}
.subnav a:hover {
    background: var(--surface-2);
    color: var(--hs-red);
}
.subnav a:hover::after { opacity: 0.6; transform: translateX(0); }
.subnav a[aria-current="page"] {
    background: var(--hs-red-soft);
    color: var(--hs-red);
    font-weight: 600;
    box-shadow: inset 3px 0 0 0 var(--hs-red);
}
.subnav a[aria-current="page"]::after { opacity: 1; transform: translateX(0); }

/* Right rail link list — quiet, underline-on-hover */
.linklist { list-style: none; margin: 0; padding: 0; }
/* NOT on the row variant. .linklist > li is reset near the top of this file for exactly this
   reason, and then this rule put the margin back -- which is fine in the column the rule was
   written for, and wrong in the footer's horizontal legal links, where a top margin on every
   item except the first sits Privacy 2px above Accessibility and Terms. .linklist--row is a
   flex row with a gap; it needs no margin at all. */
.linklist:not(.linklist--row) li + li { margin-top: 2px; }
.linklist a {
    display: inline-block;
    color: var(--hs-blue);
    text-decoration: none;
    border-bottom: 1px solid transparent;
    padding-block: 2px;
    transition: border-color .15s ease, color .15s ease;
}
.linklist a:hover {
    color: var(--hs-blue-hover);
    border-bottom-color: currentColor;
}

/* Division jump links (player rankings) */
/* A row of jump links — the member area's own tabs, and the division picker on the
   rankings. They read as buttons because that is what they are for.

   The bug worth naming: [aria-current="page"] had no rule at all, so the page you were
   already on looked exactly like the four you were not. A tab strip whose selected tab is
   invisible is worse than no tab strip, because it actively misleads. */
.div-jump { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-4); }
.div-jump a {
    display: inline-block;
    padding: 0.4rem 0.9rem;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    background: var(--surface);
    text-decoration: none;
    color: var(--text);
    font-size: var(--fs-200);
    font-weight: 500;
    line-height: 1.4;
    transition: background-color .15s ease, border-color .15s ease, color .15s ease;
}
.div-jump a:hover {
    background: var(--hs-red-soft); border-color: var(--hs-red); color: var(--hs-red-dark);
}
.div-jump a:focus-visible { outline: 2px solid var(--hs-blue); outline-offset: 2px; }

/* Where you are. Filled rather than merely tinted, so it survives being glanced at, and
   marked up with aria-current already — this only gives that fact a colour. */
.div-jump a[aria-current="page"] {
    background: var(--hs-red); border-color: var(--hs-red); color: #fff; font-weight: 600;
}
.div-jump a[aria-current="page"]:hover {
    background: var(--hs-red-dark); border-color: var(--hs-red-dark); color: #fff;
}

/* Breadcrumb — inline so every crumb sits on one baseline */
.breadcrumb { font-size: var(--fs-200); line-height: 1.5; }
.breadcrumb ol { list-style: none; margin: 0; padding: 0; }
.breadcrumb li { display: inline; }
.breadcrumb li + li::before { content: "›"; margin: 0 0.45rem; color: var(--text-muted); }
.breadcrumb a { color: var(--hs-blue); text-decoration: none; }
.breadcrumb a:hover { text-decoration: underline; }
.breadcrumb [aria-current="page"] { color: var(--text-muted); }

/* Article body */
.article > h1:first-child,
.article > h2:first-child { margin-top: 0; }
.article blockquote {
    margin: var(--sp-4) 0;
    padding: var(--sp-3) var(--sp-4);
    border-left: 4px solid var(--hs-red);
    background: var(--hs-red-soft);
    border-radius: 0 var(--radius) var(--radius) 0;
    font-style: italic;
}
.article blockquote p { margin: 0; }

/* Tables — keep the legacy cream feel, but cleaner */
.table-wrap { overflow-x: auto; }     /* mobile-safe horizontal scroll */
table.results,
table.standings {
    width: 100%; border-collapse: collapse; font-size: var(--fs-200);
}
table.results th, table.standings th,
table.results td, table.standings td {
    padding: var(--sp-2) var(--sp-3); text-align: left;
    border-bottom: 1px solid var(--cream-border, var(--border));
}
table.results thead th,
table.standings thead th {
    background: var(--hs-red);
    color: #fff;
    font-weight: 600;
    text-align: left;
    border-bottom: 0;
}
table.results tbody tr:nth-child(odd),
table.standings tbody tr:nth-child(odd) {
    background: var(--hs-cream);
}
/* Row hover has its own colour, and its own TEXT colour with it. It used to be a hardcoded
   #FFF1A8 here and --hs-red-soft in the admin tables — borrowing that worked until a site set
   the soft brand colour to something dark, at which point a hovered row turned dark under link
   text still coloured for a pale ground and stopped being readable. */
table.results tbody tr:hover,
table.standings tbody tr:hover {
    background: var(--row-hover, #FFF1A8);
    color: var(--row-hover-text, inherit);
}
table.results tbody tr:hover a,
table.standings tbody tr:hover a { color: var(--row-hover-text, inherit); }
table.results th.score, table.results td.score,
table.standings th.num, table.standings td.num {
    text-align: right; font-variant-numeric: tabular-nums;
}

/* Form pips (recent results) and Next-fixture badge */
.form-line { display: inline-flex; gap: 3px; }
.form-pip {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.3rem; height: 1.3rem; border-radius: 4px;
    font-size: 0.72rem; font-weight: 700; color: #fff;
}
.form-pip--w { background: #2e7d32; }
.form-pip--d { background: #9e9e9e; }
.form-pip--l { background: var(--hs-red); }
.next-cell { white-space: nowrap; }
.next-badge { display: inline-flex; align-items: center; gap: .4rem; font-size: var(--fs-200); }
.next-ha {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.3rem; height: 1.3rem; border-radius: 50%;
    background: var(--surface-2); border: 1px solid var(--border);
    font-size: 0.7rem; font-weight: 700; color: var(--text-muted);
}

/* Section heading — sits above grids of cards, doesn't need its own card */
.section-heading {
    margin: var(--sp-6) 0 var(--sp-4);
    padding-bottom: var(--sp-2);
    border-bottom: 2px solid var(--hs-red);
    color: var(--text);
    font-size: var(--fs-500);
}

.muted { color: var(--text-muted); }
.muted.small,
.small.muted,
.small { font-size: var(--fs-200); }
.lead { font-size: var(--fs-400); color: var(--text-muted); }

/* Champion cards — division winners hero. Mobile 1-col → tablet 2-col → desktop 4-col. */
.champions-grid {
    display: grid;
    gap: var(--sp-4);
    grid-template-columns: 1fr;
    margin-bottom: var(--sp-5);
}
@media (min-width: 540px) { .champions-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1080px) { .champions-grid { grid-template-columns: repeat(4, 1fr); } }

.champion {
    position: relative;
    background: linear-gradient(180deg, #fff 0%, var(--hs-red-soft) 100%);
    border: 1px solid var(--hs-cream-border);
    border-top: 4px solid var(--hs-red);
    border-radius: var(--radius);
    padding: var(--sp-5) var(--sp-4) var(--sp-4);
    text-align: center;
    box-shadow: var(--shadow-sm);
}
.champion__division {
    display: inline-block;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--hs-red);
    margin-bottom: var(--sp-2);
}
.champion__trophy {
    display: block;
    font-size: 2rem;
    line-height: 1;
    margin-bottom: var(--sp-2);
    filter: saturate(1.1);
}
.champion__name {
    margin: 0 0 var(--sp-2);
    color: var(--text);
    font-size: var(--fs-500);
    line-height: 1.2;
}
.champion__line {
    margin: 0 0 var(--sp-3);
    color: var(--text-muted);
    font-size: var(--fs-200);
}
.champion__line strong { color: var(--text); }
.champion__line .dot { color: var(--border-strong); margin: 0 0.25em; }
.champion__points {
    margin: 0;
    font-size: var(--fs-700);
    font-weight: 800;
    color: var(--hs-red);
    line-height: 1;
}
.champion__points span {
    font-size: var(--fs-300);
    font-weight: 500;
    color: var(--text-muted);
    margin-left: 0.25em;
}
.champion .badge {
    position: absolute;
    top: var(--sp-3); right: var(--sp-3);
    background: var(--hs-red); color: #fff;
    padding: 2px 8px; border-radius: 999px;
    font-size: 0.7rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.05em;
}
.champion--undefeated {
    border-top-color: #B8860B;       /* gold */
}

/* Player of the season cards — same grid, lighter look */
.players-grid {
    display: grid;
    gap: var(--sp-4);
    grid-template-columns: 1fr;
    margin-bottom: var(--sp-5);
}
@media (min-width: 540px)  { .players-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1080px) { .players-grid { grid-template-columns: repeat(4, 1fr); } }

.player-card {
    padding: var(--sp-4);
    text-align: left;
}
.player-card__division {
    display: inline-block;
    font-size: 0.7rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.1em;
    color: var(--hs-red);
    margin-bottom: var(--sp-1);
}
.player-card__name {
    margin: 0 0 2px;
    color: var(--text);
    font-size: var(--fs-400);
}
.player-card__team {
    margin: 0 0 var(--sp-3);
    color: var(--text-muted);
    font-size: var(--fs-200);
}
.player-card__stats {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--sp-2);
    margin: 0;
    padding-top: var(--sp-3);
    border-top: 1px solid var(--border);
    font-size: var(--fs-100);
}
.player-card__stats > div { text-align: center; }
.player-card__stats dt {
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: 0.65rem;
    margin-bottom: 2px;
}
.player-card__stats dd {
    margin: 0;
    color: var(--text);
    font-weight: 700;
    font-size: var(--fs-300);
    font-variant-numeric: tabular-nums;
}

/* Standings: highlight the leader row */
table.standings tbody tr.winner {
    background: var(--hs-red-soft);
    font-weight: 600;
}
table.standings tbody tr.winner td:first-child::after {
    content: " ★";
    color: var(--hs-red);
}
table.standings tbody tr.winner:hover {
    background: var(--hs-red-soft, #FFD9CF);
}

/* Forms */
.form { display: grid; gap: var(--sp-4); }
.form label { font-weight: 600; color: var(--text); display: block; margin-bottom: var(--sp-1); }
.form input[type="text"],
.form input[type="email"],
.form input[type="number"],
.form input[type="password"],
.form select,
.form textarea {
    width: 100%; padding: var(--sp-2) var(--sp-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: #fff;
}
.form input:focus,
.form select:focus,
.form textarea:focus {
    outline: 2px solid var(--hs-blue); outline-offset: 1px; border-color: var(--hs-blue);
}

.btn {
    display: inline-block; padding: var(--sp-2) var(--sp-5);
    background: var(--hs-red); color: #fff;
    border: 0; border-radius: var(--radius-sm);
    font-weight: 600; cursor: pointer;
    text-decoration: none;
}
.btn:hover { background: var(--hs-red-dark); color: #fff; }
.btn--ghost { background: transparent; color: var(--hs-red); border: 1px solid var(--hs-red); }
.btn--ghost:hover { background: var(--hs-red-soft); color: var(--hs-red-dark); }

/* Footer */
/* Footer — dark, to cohere with the header; flame accents on socials + signup. */
.site-footer {
    background: linear-gradient(180deg, var(--footer-from, #1c1310) 0%, var(--footer-to, #130d0c) 100%);
    color: var(--footer-text, #C9C2BD);
    border-top: 3px solid var(--hs-red);
    margin-top: var(--sp-6);
}
.site-footer a { color: var(--footer-link, #E7E1DC); text-decoration: none; }
.site-footer a:hover { color: #fff; }
.site-footer h4 {
    color: #fff; font-size: .8rem; letter-spacing: .12em; text-transform: uppercase;
    margin: 0 0 var(--sp-3);
}

.footer-grid {
    display: grid; gap: var(--sp-5) var(--sp-6);
    grid-template-columns: 1fr;
    padding-block: var(--sp-6);
}
@media (min-width: 600px) { .footer-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 980px) { .footer-grid { grid-template-columns: 1.5fr 1fr 1fr 1.6fr; } }

.footer-brand p { margin: 0 0 var(--sp-3); color: var(--footer-muted, #A89F98); font-size: var(--fs-200); max-width: 32ch; }
.brand--footer {
    /* Same chip as the header's, from the same setting. It was a second hardcoded #fff, so
       setting the logo backdrop to transparent cleared the white box at the top of the page
       and left an identical one at the bottom. */
    background: var(--logo-backdrop, #fff); box-shadow: var(--logo-shadow, none);
    border-radius: 12px; padding: 10px 16px; display: inline-flex; margin-bottom: var(--sp-4);
}
.brand--footer .brand__logo { height: 76px; width: auto; display: block; max-width: 100%; }

.footer-col .linklist a { color: var(--footer-text, #C9C2BD); }
.footer-col .linklist a:hover { color: #fff; }
.linklist--row { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); }

/* social icons */
.socials { display: flex; gap: 10px; }
.social {
    display: inline-flex; align-items: center; justify-content: center;
    width: 38px; height: 38px; border-radius: 50%;
    border: 1px solid rgba(255,255,255,.22); color: var(--footer-link, #E7E1DC);
    transition: background .2s, border-color .2s, color .2s, transform .18s ease, box-shadow .2s;
}
.social svg { width: 18px; height: 18px; }
.social:hover {
    color: #fff; transform: translateY(-2px); border-color: transparent;
    background: linear-gradient(135deg, var(--hs-red), var(--flame));
    box-shadow: 0 8px 20px -8px color-mix(in srgb, var(--flame, #FF5A36) 70%, transparent);
}

/* newsletter signup */
.newsletter-form { display: grid; gap: var(--sp-2); max-width: 22rem; }
.newsletter-form input {
    width: 100%; padding: 10px 12px; font: inherit;
    border: 1px solid rgba(255,255,255,.18); border-radius: 8px;
    background: rgba(255,255,255,.06); color: #fff;
}
.newsletter-form input::placeholder { color: var(--footer-muted, #9a918b); }
.newsletter-form input:focus { outline: 2px solid var(--flame); outline-offset: 1px; border-color: transparent; }
.btn--flame {
    width: 100%; border: 0; color: #fff;
    background: linear-gradient(90deg, var(--hs-red), var(--flame));
    box-shadow: 0 8px 20px -10px color-mix(in srgb, var(--flame, #FF5A36) 80%, transparent);
}
.btn--flame:hover { filter: brightness(1.06); transform: translateY(-1px); }

/* bottom bar */
.footer-bottom { border-top: 1px solid rgba(255,255,255,.1); }
.footer-bottom__inner {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: var(--sp-2) var(--sp-4); padding-block: var(--sp-4);
    font-size: var(--fs-100); color: var(--footer-muted, #8C837D);
}
.footer-bottom__inner p { margin: 0; }
.footer-bottom a { color: var(--footer-muted, #8C837D); }
.footer-bottom a:hover { color: #fff; }

/* Print */
@media print {
    .site-header, .site-footer { display: none; }
    .main-grid { grid-template-columns: 1fr; }
    .card { border: 0; box-shadow: none; padding: 0; }
}

/* Forms — contact form etc. */
.form { display: grid; gap: var(--sp-4); max-width: 38rem; margin-top: var(--sp-4); }
.form__row { display: grid; gap: var(--sp-1); }
.form__row label { font-weight: 600; font-size: var(--fs-200); }
/* Checkboxes and radios are excluded deliberately. This rule stretched them to the full width
   of the grid cell and gave them a text field's padding, which rendered the box adrift in the
   middle of the row with its label shoved to the far edge and wrapping. A tick box is not a
   field you type into: it keeps its intrinsic size and sits immediately left of its words. */
.form input:not([type="checkbox"]):not([type="radio"]),
.form textarea {
    width: 100%;
    padding: var(--sp-2) var(--sp-3);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: #fff;
}
.form select {
    width: 100%;
    padding: var(--sp-2) var(--sp-3);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: #fff;
    font: inherit;
}
.form input:focus,
.form select:focus,
.form textarea:focus { outline: 2px solid var(--hs-blue); outline-offset: 1px; border-color: var(--hs-blue); }
.form textarea { resize: vertical; }

/* Result entry form */
.result-form select,
.result-form input[type="date"] {
    width: 100%;
    max-width: 28rem;
    padding: var(--sp-2) var(--sp-3);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: #fff;
    font: inherit;
}
.result-form select:focus,
.result-form input:focus { outline: 2px solid var(--hs-blue); outline-offset: 1px; border-color: var(--hs-blue); }
.rubber__players { display: grid; gap: var(--sp-3); margin-bottom: var(--sp-4); }
@media (min-width: 560px) { .rubber__players { grid-template-columns: 1fr 1fr; } }
.games-input { display: flex; flex-wrap: wrap; gap: var(--sp-4); }
.game-input { display: flex; flex-direction: column; gap: 4px; }
.game-input__no { font-size: var(--fs-100); color: var(--text-muted); font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }
.game-input__scores { display: flex; align-items: center; gap: 10px; }
/* Who retired, and the game in progress when they did — under the finished games, set apart
   because it is the one part of the card a captain fills in only now and then. */
.rubber__retirement { display: grid; gap: var(--sp-3); margin-top: var(--sp-4); padding-top: var(--sp-3); border-top: 1px dashed var(--border); }

/* The row scored by different rules from the ones above it. A padel rubber's last row is a
   championship tie-break to 10 played to two clear, not a third set, so its heading is longer
   than "Set 2" and carries the rule underneath: a captain about to type 6-4 has to be able to
   read that BEFORE typing it, not learn it from a refusal afterwards. Darkened rather than
   coloured, because nothing here is an error. None of it fires for squash — SquashParFormat
   returns one constant label, no detail and no decider. */
.game-input--decider .game-input__no { color: var(--text); }
.game-input__rule {
    display: block;
    font-weight: 400;
    text-transform: none;
    letter-spacing: normal;
    color: var(--text-muted);
}

/* Entering a scorecard is 8 to 40 taps on a phone at a club, so these are sized as touch
   targets rather than as text inputs: 3.25rem wide and one padding unit tall was hard to hit.
   4.75rem and a 2.75rem minimum height clears the usual 44px guidance with room to spare, and
   the larger type makes a mistyped score visible without leaning in. */
.game-input__scores input {
    width: 4.75rem; min-height: 2.75rem; text-align: center;
    padding: var(--sp-2) var(--sp-3);
    border: 1px solid var(--border); border-radius: var(--radius-sm);
    font: inherit; font-size: 1.15rem; font-variant-numeric: tabular-nums;
}
.game-input__scores input:focus { outline: 2px solid var(--hs-blue); outline-offset: 1px; border-color: var(--hs-blue); }

/* An out-of-range score is shown WHILE typing. min/max on a number input only bite on submit,
   so the field looked as though it accepted anything and only complained after the whole card
   had been filled in. :invalid also catches a non-numeric entry. Not applied until the field
   has been touched, or every empty box would be red on arrival. */
.game-input__scores input:user-invalid {
    border-color: var(--hs-red, #a8202f);
    background: color-mix(in srgb, var(--hs-red, #a8202f) 7%, transparent);
}

/* Hide the spin buttons: on a scorecard they sit exactly where a thumb lands and nudge a
   score by one, and every browser draws them differently. Keyboard arrows still work. */
.game-input__scores input::-webkit-outer-spin-button,
.game-input__scores input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.game-input__scores input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.game-input__sep { color: var(--text-muted); font-weight: 600; }
.field-error { color: var(--hs-red); font-size: var(--fs-100); }
.form__errors:not(:empty) {
    color: var(--hs-red);
    background: var(--hs-red-soft);
    border: 1px solid var(--hs-red);
    border-radius: var(--radius);
    padding: var(--sp-3);
}
.form__errors ul { margin: 0; padding-left: 1.2em; }
/* ASP.NET validation summary renders even when valid — hide it until there are errors */
.validation-summary-valid { display: none; }

.btn {
    display: inline-block;
    background: var(--hs-red);
    color: #fff;
    border: 0;
    border-radius: var(--radius);
    padding: var(--sp-3) var(--sp-5);
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
    transition: background-color .15s ease;
}
.btn:hover { background: var(--hs-red-hover, #a83232); }
.btn--ghost {
    background: transparent;
    color: var(--hs-red);
    border: 1px solid var(--hs-red);
}
.btn--ghost:hover { background: var(--hs-red-soft); }

/* Honeypot — visually and from-AT hidden, but still in the DOM for bots */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* Expandable team-result match rows */
.match {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    margin-bottom: var(--sp-3);
    background: var(--surface);
}
.match[open] { box-shadow: var(--shadow-sm); }
.match__summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--sp-2) var(--sp-4);
    padding: var(--sp-3) var(--sp-4);
}
.match__summary::-webkit-details-marker { display: none; }
.match__summary::before {
    content: "▸";
    color: var(--hs-red);
    transition: transform .15s ease;
}
.match[open] .match__summary::before { content: "▾"; }
.match__summary:hover { background: var(--surface-2); }
.match__date { min-width: 6.5rem; color: var(--text-muted); font-size: var(--fs-200); }
.match__teams { flex: 1 1 auto; }
.match__score { margin: 0 var(--sp-1); color: var(--hs-red); }
.match__meta { font-size: var(--fs-100); }
.match__detail { border-top: 1px solid var(--border); padding: var(--sp-2) var(--sp-3) var(--sp-3); }
table.rubbers { width: 100%; border-collapse: collapse; font-size: var(--fs-200); }
table.rubbers th, table.rubbers td { padding: var(--sp-2) var(--sp-3); border-bottom: 1px solid var(--border); text-align: left; }
table.rubbers .score { text-align: right; white-space: nowrap; }
table.rubbers .num { text-align: center; width: 2rem; color: var(--text-muted); }
.winner-cell { font-weight: 600; }
.win-tag {
    display: inline-block;
    background: var(--hs-red);
    color: #fff;
    font-size: 0.7rem;
    font-weight: 700;
    border-radius: var(--radius-sm);
    padding: 0 6px;
    margin-left: 4px;
}
.games { color: var(--text-muted); font-weight: 400; margin-left: var(--sp-2); }
/* "ret." beside the side that retired from a rubber. Outlined and muted rather than filled,
   because it qualifies a result rather than announcing one: the W still marks the winner. */
.ret-tag {
    display: inline-block;
    border: 1px solid var(--border);
    color: var(--text-muted);
    font-size: 0.7rem;
    font-weight: 700;
    border-radius: var(--radius-sm);
    padding: 0 5px;
    margin-left: 4px;
}

/* Highlight the filtered team in the fixtures table */
table.results td.is-team { font-weight: 700; color: var(--hs-red); }

/* News list */
.news-item { padding-block: var(--sp-4); border-top: 1px solid var(--border); }
.news-item:first-of-type { border-top: 0; }
.news-item__date { color: var(--text-muted); font-size: var(--fs-100); margin: 0 0 var(--sp-1); }
/* Bodies are sanitised HTML from the editor, so no pre-line: block elements
   provide their own spacing and pre-line would double it. */
.news-item__body > :first-child { margin-top: 0; }
.news-item__body > :last-child { margin-bottom: 0; }
.news-item__body h2 { font-size: var(--fs-400); margin: var(--sp-4) 0 var(--sp-1); }
.news-item__body h3 { font-size: var(--fs-300); margin: var(--sp-3) 0 var(--sp-1); }
.news-item__body h4 { font-size: var(--fs-200); margin: var(--sp-3) 0 var(--sp-1); }
.news-item__body ul,
.news-item__body ol { padding-left: 1.4rem; margin: var(--sp-2) 0; }
.news-item__body li { margin: .25rem 0; }
.news-item__body blockquote {
    margin: var(--sp-2) 0;
    padding: var(--sp-1) var(--sp-3);
    border-left: 3px solid var(--border);
    color: var(--text-muted);
}
.news-item__body img { max-width: 100%; height: auto; display: block; margin: var(--sp-3) 0; }
.news-item__body hr { border: 0; border-top: 1px solid var(--border); margin: var(--sp-4) 0; }
.news-item__body table { border-collapse: collapse; width: 100%; margin: var(--sp-3) 0; }
.news-item__body th,
.news-item__body td { border: 1px solid var(--border); padding: .4rem .6rem; text-align: left; }

/* Clubs & Teams — single panel, one section per club */
.club { padding-top: var(--sp-4); margin-top: var(--sp-4); border-top: 1px solid var(--border); }
.club h2 { margin: 0 0 var(--sp-2); }
.team-list { list-style: none; margin: var(--sp-2) 0 0; padding: 0; }
.team-list li { padding: var(--sp-2) 0; }
.team-list li + li { border-top: 1px solid var(--border); }

/* Visually-hidden (screen-reader only) */
.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Search field */
.search { position: relative; max-width: 26rem; }
.search__input {
    width: 100%;
    padding: var(--sp-3) var(--sp-4) var(--sp-3) 2.75rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface);
    font-size: var(--fs-300);
    color: var(--text);
    box-shadow: var(--shadow-sm);
    transition: border-color .15s ease, box-shadow .15s ease;
    appearance: none;
}
.search__input::placeholder { color: var(--text-muted); }
.search__input:hover { border-color: var(--border-strong); }
.search__input:focus {
    outline: none;
    border-color: var(--hs-blue);
    box-shadow: 0 0 0 3px rgb(14 77 139 / 0.15);
}
.search::before {
    content: "";
    position: absolute;
    left: 0.95rem; top: 50%;
    width: 1.05rem; height: 1.05rem;
    transform: translateY(-50%);
    background-color: var(--text-muted);
    pointer-events: none;
    -webkit-mask: var(--search-icon) center / contain no-repeat;
    mask: var(--search-icon) center / contain no-repeat;
    --search-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E");
}

/* External-link & document (PDF/Word/Excel) indicators.
   Automatic: any absolute http(s) link gets an "external" arrow; any link to a
   document gets a document icon. Icons are masked to currentColor so they
   match the link colour (white in the nav, blue in body text).
   The `i` flag makes the extension match case-insensitive, so an uppercase
   .PDF or .XLSX gets an icon too. */
a[href^="http://"]::after,
a[href^="https://"]::after,
a[href$=".pdf" i]::after,
a[href$=".doc" i]::after,
a[href$=".docx" i]::after,
a[href$=".xls" i]::after,
a[href$=".xlsx" i]::after {
    content: "";
    display: inline-block;
    width: 0.85em;
    height: 0.85em;
    margin-left: 0.3em;
    vertical-align: -0.05em;
    background-color: currentColor;
    -webkit-mask: var(--link-icon) center / contain no-repeat;
    mask: var(--link-icon) center / contain no-repeat;
}
a[href^="http://"]::after,
a[href^="https://"]::after {
    --link-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6'/%3E%3Cpolyline points='15 3 21 3 21 9'/%3E%3Cline x1='10' y1='14' x2='21' y2='3'/%3E%3C/svg%3E");
}
a[href$=".pdf" i]::after,
a[href$=".doc" i]::after,
a[href$=".docx" i]::after,
a[href$=".xls" i]::after,
a[href$=".xlsx" i]::after {
    --link-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z'/%3E%3Cpolyline points='14 2 14 8 20 8'/%3E%3Cline x1='16' y1='13' x2='8' y2='13'/%3E%3Cline x1='16' y1='17' x2='8' y2='17'/%3E%3Cline x1='10' y1='9' x2='8' y2='9'/%3E%3C/svg%3E");
}
/* Don't draw an icon on image links, the brand logo, or social icon buttons.
   (a.social beats the a[href^="https://"] external-link rule on specificity.) */
a:has(> img)::after,
.brand::after,
a.social::after { content: none !important; }

/* Print */
@media print {
    .site-header, .site-footer { display: none; }
    .main-grid { grid-template-columns: 1fr; }
    .card { border: 0; box-shadow: none; padding: 0; }
}

/* Reduced-motion respect */
@media (prefers-reduced-motion: reduce) {
    * { transition: none !important; animation: none !important; }
}

/* ==================================================================== */
/*  Club profile — /clubs/{handle}                                      */
/*                                                                      */
/*  A court specification sheet. The club's own figures lead, and court  */
/*  geometry is the identity device: the plan of a squash court is the   */
/*  one drawing that says "squash club" without a word of copy, and it   */
/*  is drawn rather than photographed so it costs no image request and   */
/*  is legible at 40px.                                                  */
/*                                                                      */
/*  Here rather than inline in the page, which is where it used to live. */
/*  That decision was right at sixty lines and wrong at three hundred:   */
/*  inline, the whole sheet is re-sent with every club page a visitor    */
/*  opens and is never cached, and a league with a dozen clubs is a page */
/*  a member walks through club by club.                                 */
/*                                                                      */
/*  Everything is scoped under `.clubpage` — `.court`, `.day`, `.rung`   */
/*  and `.panel` are words the rest of the site could plausibly want —   */
/*  and every colour comes from the theme variables, so a rebranded site */
/*  takes the whole page with it rather than leaving one page red.       */
/* ==================================================================== */

.clubpage {
    /* Matches .shell's padding at each breakpoint, because the full-width bands below
       cancel it with a negative margin and would tear away from the page edge if the
       two ever disagreed. */
    --cp-gutter: var(--sp-3);
    --cp-mono: var(--font-mono);
    /* Text ON the brand colour. White rather than a tint of the brand: the brand is
       whatever a league configured, and only white is guaranteed to carry contrast
       against every primary somebody might pick. */
    --cp-on-brand: #fff;
    --cp-on-brand-dim: rgb(255 255 255 / 0.74);
}
@media (min-width: 720px)  { .clubpage { --cp-gutter: var(--sp-6); } }
@media (min-width: 1080px) { .clubpage { --cp-gutter: var(--sp-7); } }

.clubpage .band  { margin-inline: calc(-1 * var(--cp-gutter)); }
.clubpage .inner { padding-inline: var(--cp-gutter); }
/* The jump bar is sticky, so an anchored section has to stop under it rather than
   behind it. */
.clubpage [id] { scroll-margin-top: 4rem; }

.clubpage .hero h1,
.clubpage .figs b,
.clubpage .court__n,
.clubpage .court__t,
.clubpage .slot__l,
.clubpage .rung__n,
.clubpage .next__who,
.clubpage .match__s b,
.clubpage .person__n,
.clubpage .rate { font-family: var(--font-display); }

.clubpage .eyebrow {
    margin: 0;
    font-family: var(--cp-mono);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--text-muted);
}

/* ---- hero ---------------------------------------------------------- */
.clubpage .hero {
    position: relative;
    overflow: hidden;
    isolation: isolate;
    background: var(--hs-red);
    color: var(--cp-on-brand);
}
.clubpage .hero__weave { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; opacity: 0.5; }
/* Fades the drawing out from the left so the club's name always sits on flat colour.
   color-mix, not a literal rgba(), because the colour underneath is the league's. */
.clubpage .hero::after {
    content: "";
    position: absolute; inset: 0; z-index: 1; pointer-events: none;
    background: linear-gradient(105deg,
        var(--hs-red) 8%,
        color-mix(in srgb, var(--hs-red) 72%, transparent) 48%,
        color-mix(in srgb, var(--hs-red) 28%, transparent) 100%);
}
.clubpage .hero__in { position: relative; z-index: 2; padding-block: var(--sp-8) var(--sp-7); }
.clubpage .hero .eyebrow { color: var(--cp-on-brand-dim); }
.clubpage .hero h1 {
    margin: var(--sp-3) 0 0;
    color: var(--cp-on-brand);
    font-size: clamp(2.75rem, 9vw, 5.5rem);
    font-weight: 800;
    letter-spacing: -0.04em;
    line-height: 1.02;
}
.clubpage .hero__where {
    margin: var(--sp-5) 0 0;
    display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-5);
    font-family: var(--cp-mono); font-size: 0.8125rem;
    color: var(--cp-on-brand-dim);
}
.clubpage .hero__where span { display: inline-flex; align-items: flex-start; gap: var(--sp-2); }
.clubpage .hero__where svg  { width: 13px; height: 13px; flex: none; margin-top: 0.3em; }
.clubpage .hero__where a,
.clubpage .hero__where a:hover { color: var(--cp-on-brand); text-underline-offset: 3px; }

/* ---- the figures strip --------------------------------------------- */
/* The four or five numbers a visitor wants before they read a word. A figure with
   nothing behind it is ABSENT, never a dash: five tiles reading "—" report the
   league's data-entry backlog, which is not what anybody came to the page for. */
.clubpage .figs { position: relative; z-index: 2; background: var(--hs-red-dark); color: var(--cp-on-brand); }
.clubpage .figs__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); }
.clubpage .figs__grid > div { padding-block: var(--sp-5); border-right: 1px solid rgb(255 255 255 / 0.14); }
.clubpage .figs__grid > div:last-child { border-right: 0; }
.clubpage .figs__grid > div + div { padding-left: var(--sp-5); }
.clubpage .figs b {
    display: block; font-weight: 800; font-size: 2rem;
    letter-spacing: -0.025em; line-height: 1; font-variant-numeric: tabular-nums;
}
.clubpage .figs span {
    display: block; margin-top: var(--sp-2);
    font-family: var(--cp-mono); font-size: 0.625rem;
    letter-spacing: 0.16em; text-transform: uppercase;
    color: var(--cp-on-brand-dim);
}

/* ---- jump bar ------------------------------------------------------- */
.clubpage .jump {
    position: sticky; top: 0; z-index: 20;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}
.clubpage .jump ul {
    /* padding-BLOCK, not the padding shorthand. The shorthand zeroed the inline padding that
       .clubpage .inner sets, and beat it on specificity (two classes plus an element against
       two classes), so the jump bar sat hard against the page edge while every section below
       it lined up on the gutter. */
    margin: 0; padding-block: 0; list-style: none;
    display: flex; gap: var(--sp-5);
    overflow-x: auto; scrollbar-width: none;
}
.clubpage .jump ul::-webkit-scrollbar { display: none; }
.clubpage .jump li + li { margin-top: 0; }
.clubpage .jump a {
    display: block; padding-block: var(--sp-4);
    text-decoration: none; white-space: nowrap;
    font-family: var(--cp-mono); font-size: 0.75rem;
    letter-spacing: 0.1em; text-transform: uppercase;
    color: var(--text-muted);
    border-bottom: 2px solid transparent;
}
.clubpage .jump a:hover { color: var(--text); border-bottom-color: var(--border-strong); }

/* ---- sections ------------------------------------------------------- */
.clubpage .sec { padding-block: var(--sp-7); border-bottom: 1px solid var(--border); }
.clubpage .sec:last-of-type { border-bottom: 0; }
.clubpage .sec__head {
    display: flex; align-items: baseline; gap: var(--sp-4);
    flex-wrap: wrap; margin-bottom: var(--sp-6);
}
.clubpage .sec__head h2 {
    margin: 0; color: var(--text);
    font-size: 1.5rem; font-weight: 700; letter-spacing: -0.025em;
}
.clubpage .sec__head .eyebrow { margin-left: auto; }

/* The club's own words, set as a lede rather than under an "About" heading — a
   heading over one paragraph is a heading too many, and the paragraph is the point. */
.clubpage .intro { padding-block: var(--sp-6); border-bottom: 1px solid var(--border); }
.clubpage .intro p {
    max-width: 44rem; margin: 0 0 var(--sp-3);
    font-size: var(--fs-400); line-height: 1.55; color: var(--text-muted);
}
.clubpage .intro p:last-child { margin-bottom: 0; }

.clubpage .cp-back { margin: 0; padding-block: var(--sp-3); font-size: var(--fs-200); }

/* ---- facilities ----------------------------------------------------- */
/* auto-FILL, not auto-fit. auto-fit collapses its empty tracks, so a club with ONE court type
   got a single track the full width of the page and a court diagram marooned in the middle of
   it. auto-fill keeps the tracks, so one card stays card-sized and the rest of the row is
   simply empty -- which is the truth about the club. The max-width is belt and braces for very
   wide screens, where even a full row of tracks gets too generous. */
.clubpage .courts {
    display: grid; gap: var(--sp-5);
    grid-template-columns: repeat(auto-fill, minmax(10.5rem, 1fr));
}
.clubpage .court { max-width: 16rem; }
.clubpage .court  { display: flex; flex-direction: column; gap: var(--sp-3); }
.clubpage .court__draw {
    display: grid; place-items: center;
    padding: var(--sp-4);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    color: var(--border-strong);
}
.clubpage .court__draw svg { display: block; width: 100%; max-width: 7rem; height: auto; }
/* A sport played on somebody else's lines is drawn in somebody else's lines. */
.clubpage .court--borrowed .court__draw       { background: transparent; border-style: dashed; }
.clubpage .court--borrowed .court__draw svg * { stroke-dasharray: 5 4; }
.clubpage .court__row  { display: flex; align-items: baseline; gap: var(--sp-2); }
.clubpage .court__n {
    font-size: 2.25rem; font-weight: 800; line-height: 1;
    letter-spacing: -0.03em; font-variant-numeric: tabular-nums;
    color: var(--flame);
}
.clubpage .court--borrowed .court__n { color: var(--border-strong); font-size: 1.5rem; }
.clubpage .court__unit {
    font-family: var(--cp-mono); font-size: 0.6875rem;
    letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted);
}
.clubpage .court__t { margin: 0; color: var(--text); font-size: 1rem; font-weight: 700; letter-spacing: -0.01em; }
.clubpage .court__note { margin: 0; font-size: var(--fs-100); color: var(--text-muted); }
.clubpage .key {
    margin: var(--sp-6) 0 0; padding-top: var(--sp-4);
    border-top: 1px solid var(--border);
    display: flex; flex-wrap: wrap; gap: var(--sp-3) var(--sp-6);
    font-size: var(--fs-200); color: var(--text-muted);
}
.clubpage .key span { display: inline-flex; align-items: center; gap: var(--sp-2); }
/* The key's swatches. Miniature court PLANS, not squares: with a border-radius and a filled
   background at 14x20 they were indistinguishable from checkboxes, and the key read as two
   unticked options on a page that asks for nothing. The centre line is what makes it a court
   at a glance, and no radius is what stops it being a control. */
.clubpage .sw {
    position: relative;
    width: 15px; height: 21px; flex: none; border-radius: 0;
    border: 1.5px solid var(--border-strong); background: var(--surface-2);
}
.clubpage .sw::after {
    content: ""; position: absolute; left: 0; right: 0; top: 62%;
    border-top: 1.5px solid var(--border-strong);
}
.clubpage .sw--b,
.clubpage .sw--b::after { border-style: dashed; border-color: var(--border); }
.clubpage .sw--b { background: transparent; }

/* ---- club nights: a weekly timetable, not a list --------------------- */
/* The week is a grid because that is what a week IS. A list of five sessions makes a
   reader assemble "so nothing on Friday, then" for themselves; a column headed FRI
   with "courts bookable as normal" in it has already answered them. */
.clubpage .week {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
    gap: 1px; background: var(--border);
    border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden;
}
.clubpage .day {
    background: var(--surface); padding: var(--sp-4);
    display: flex; flex-direction: column; gap: var(--sp-3); min-height: 8.5rem;
}
.clubpage .day--off { background: var(--surface-2); }
.clubpage .day__n {
    margin: 0; font-family: var(--cp-mono); font-size: 0.625rem;
    letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-muted);
}
.clubpage .slot { display: flex; flex-direction: column; gap: 1px; }
.clubpage .slot__t { font-family: var(--cp-mono); font-size: 0.75rem; color: var(--flame); font-variant-numeric: tabular-nums; }
.clubpage .slot__l { font-size: 0.9375rem; font-weight: 700; letter-spacing: -0.01em; }
.clubpage .slot__note { font-size: 0.75rem; line-height: 1.4; color: var(--text-muted); }
.clubpage .day__off { margin: 0; font-size: var(--fs-200); color: var(--text-muted); }

/* ---- teams ----------------------------------------------------------- */
.clubpage .ladder { display: flex; flex-direction: column; }
.clubpage .rung {
    display: grid; grid-template-columns: 3rem 1fr auto auto;
    align-items: center; gap: var(--sp-4);
    padding-block: var(--sp-4); border-bottom: 1px solid var(--border);
}
.clubpage .rung:first-child { border-top: 1px solid var(--border); }
.clubpage .rung__d {
    font-family: var(--cp-mono); font-size: 0.75rem;
    color: var(--cp-on-brand); background: var(--hs-red);
    border-radius: 2px; text-align: center; padding-block: var(--sp-1);
}
.clubpage .rung__n { font-size: 1.125rem; font-weight: 700; letter-spacing: -0.015em; }
.clubpage .rung__n a { color: var(--text); text-decoration: none; }
.clubpage .rung__n a:hover { text-decoration: underline; text-underline-offset: 3px; }
.clubpage .rung__pos,
.clubpage .rung__s {
    font-family: var(--cp-mono); font-size: var(--fs-100);
    color: var(--text-muted); font-variant-numeric: tabular-nums;
}
.clubpage .rung__s { min-width: 6rem; text-align: right; }
.clubpage .rung__u { letter-spacing: 0.08em; text-transform: uppercase; font-size: 0.625rem; }
@media (max-width: 30rem) {
    .clubpage .rung   { grid-template-columns: 2.5rem 1fr auto; gap: var(--sp-3); }
    .clubpage .rung__s { display: none; }
}

/* ---- fixtures & results ---------------------------------------------- */
.clubpage .two { display: grid; grid-template-columns: repeat(auto-fit, minmax(18rem, 1fr)); gap: var(--sp-6); }
.clubpage .panel h3 {
    margin: 0 0 var(--sp-4);
    font-family: var(--cp-mono); font-size: 0.6875rem; font-weight: 600;
    letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-muted);
}
.clubpage .panel h3 + .next { margin-bottom: var(--sp-6); }
.clubpage .match {
    display: grid; grid-template-columns: 1fr auto; gap: var(--sp-3);
    align-items: center; padding-block: var(--sp-3);
    border-bottom: 1px solid var(--border);
}
.clubpage .match:first-of-type { border-top: 1px solid var(--border); }
.clubpage .match__t { font-weight: 500; }
.clubpage .match__t em {
    display: block; margin-top: 2px; font-style: normal;
    font-family: var(--cp-mono); font-size: 0.6875rem;
    letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted);
}
.clubpage .match__s {
    font-family: var(--cp-mono); font-size: 0.9375rem;
    font-variant-numeric: tabular-nums; text-align: right;
}
.clubpage .match__s b { font-size: 1.0625rem; letter-spacing: -0.01em; }
.clubpage .w { color: var(--win); }
.clubpage .l { color: var(--loss); }
.clubpage .d { color: var(--text-muted); }
/* The next fixture is the one thing on this page somebody acts on, so it is the one
   thing still in a box. */
.clubpage .next {
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: var(--radius-sm); padding: var(--sp-4) var(--sp-5);
}
.clubpage .next p { margin: 0; }
.clubpage .next > * + * { margin-top: var(--sp-2); }
.clubpage .next__when {
    font-family: var(--cp-mono); font-size: 0.75rem;
    letter-spacing: 0.08em; text-transform: uppercase; color: var(--flame);
}
.clubpage .next__who  { font-size: 1.375rem; font-weight: 700; letter-spacing: -0.02em; }
.clubpage .next__who .v { color: var(--text-muted); font-weight: 500; }
.clubpage .next__where { font-family: var(--cp-mono); font-size: var(--fs-100); color: var(--text-muted); }

/* ---- location --------------------------------------------------------- */
.clubpage .loc { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: var(--sp-6); align-items: start; }
@media (max-width: 760px) { .clubpage .loc { grid-template-columns: 1fr; } }
.clubpage .map {
    position: relative; padding-top: 56%;
    border: 1px solid var(--border); border-radius: var(--radius-sm);
    background: var(--surface-2); overflow: hidden;
}
.clubpage .map iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.clubpage .dl { margin: 0; display: flex; flex-direction: column; gap: var(--sp-4); }
.clubpage .dl div { display: flex; flex-direction: column; gap: 2px; }
.clubpage .dl dt {
    font-family: var(--cp-mono); font-size: 0.625rem;
    letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-muted);
}
.clubpage .dl dd { margin: 0; font-size: var(--fs-200); }
.clubpage .dl a  { color: var(--hs-red); text-underline-offset: 3px; }

/* ---- coaching & services ---------------------------------------------- */
/* auto-FILL: a club with one coach should get one entry, not one stretched across the page.
   (.week above keeps auto-fit deliberately -- it always has seven days to place.) */
.clubpage .people { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: var(--sp-5) var(--sp-6); }
.clubpage .person { display: flex; gap: var(--sp-3); align-items: flex-start; }
.clubpage .mono-badge {
    width: 2.75rem; height: 2.75rem; flex: none; border-radius: 50%;
    background: var(--hs-red); color: var(--cp-on-brand);
    display: grid; place-items: center;
    font-size: 0.9375rem; font-weight: 700; letter-spacing: 0.02em;
}
/* A service is not a person. Square mark and a tool, so a restringer never reads as
   somebody you could book a lesson with. */
.clubpage .svc-badge {
    width: 2.75rem; height: 2.75rem; flex: none; border-radius: 6px;
    border: 1.5px solid var(--border-strong); background: var(--surface-2);
    color: var(--text-muted); display: grid; place-items: center;
}
.clubpage .svc-badge svg { width: 1.35rem; height: 1.35rem; }
.clubpage .person__n { margin: 0; font-size: 1rem; font-weight: 700; letter-spacing: -0.01em; }
.clubpage .person__r {
    margin: 2px 0 0; font-family: var(--cp-mono); font-size: 0.6875rem;
    letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted);
}
.clubpage .person__b { margin: var(--sp-2) 0 0; font-size: var(--fs-200); line-height: 1.5; color: var(--text-muted); }
.clubpage .person__meta { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-3); margin: var(--sp-2) 0 0; align-items: center; }
.clubpage .accred {
    display: inline-flex; align-items: center; gap: var(--sp-1);
    font-family: var(--cp-mono); font-size: 0.625rem;
    letter-spacing: 0.08em; text-transform: uppercase;
    border: 1px solid var(--border-strong); border-radius: 2px;
    padding: 2px var(--sp-2); color: var(--text-muted);
}
.clubpage .rate { font-size: 0.9375rem; font-weight: 700; letter-spacing: -0.01em; }
.clubpage .rate span {
    font-family: var(--cp-mono); font-weight: 400; font-size: 0.6875rem;
    letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-muted);
}

/* ---- news -------------------------------------------------------------- */
.clubpage .news { display: flex; flex-direction: column; gap: var(--sp-5); }
.clubpage .post {
    display: grid; grid-template-columns: 5.5rem 1fr; gap: var(--sp-5);
    padding-bottom: var(--sp-5); border-bottom: 1px solid var(--border);
}
.clubpage .post:last-child { border-bottom: 0; padding-bottom: 0; }
.clubpage .post__d { margin: 0; padding-top: 4px; font-family: var(--cp-mono); font-size: 0.75rem; line-height: 1.5; color: var(--text-muted); }
.clubpage .post h3 { margin: 0; color: var(--text); font-size: 1.125rem; font-weight: 700; letter-spacing: -0.015em; }
.clubpage .post__body { margin-top: var(--sp-2); font-size: var(--fs-200); color: var(--text-muted); }
.clubpage .post__body > :last-child { margin-bottom: 0; }
@media (max-width: 560px) { .clubpage .post { grid-template-columns: 1fr; gap: var(--sp-2); } }

/* ---- honours ------------------------------------------------------------ */
.clubpage .honours { display: flex; flex-wrap: wrap; gap: var(--sp-3); list-style: none; margin: 0; padding: 0; }
.clubpage .honours li { margin: 0; }
.clubpage .honour {
    display: flex; align-items: baseline; gap: var(--sp-3);
    border: 1px solid var(--border); border-radius: var(--radius-sm);
    padding: var(--sp-3) var(--sp-4); background: var(--surface);
}
.clubpage .honour b { font-family: var(--cp-mono); font-size: var(--fs-100); color: var(--flame); font-variant-numeric: tabular-nums; }
.clubpage .honour span { font-size: var(--fs-200); }
.clubpage .honour em { font-style: normal; color: var(--text-muted); }

/* ==================================================================== */
/*  ===== Admin =====                                                   */
/*  Dense, professional back-office presentation layer.                 */
/*  Scoped under `.admin` (the page <article>). Its navigation is the   */
/*  header bar's own .nav rules. Reuses the site's existing tokens —    */
/*  no second palette. Presentation only; no markup logic depends on    */
/*  these rules. Public pages are untouched (nothing here is global).   */
/* ==================================================================== */

/* ---- Page shell -------------------------------------------------- */
.admin { color: var(--text); }
.admin > h1 { margin-bottom: var(--sp-2); }
.admin > h1 + .muted { margin-top: 0; }
.admin h2 {
    font-size: var(--fs-400);
    color: var(--text);
    margin: 0 0 var(--sp-3);
    padding-bottom: var(--sp-2);
    border-bottom: 2px solid var(--hs-red);
}
/* The admin KEEPS its panels, on purpose, and is the reason `.card` is still a class
   rather than a deletion.

   The public site dropped the boxes because its pages are read: a fixture list, a club's
   page, a news item. An admin screen is not read, it is operated — a form here, a queue
   there, a table of rows with a toolbar over it — and the panel is what says where one
   control group ends and the next begins when there is no prose to do it. Flattening
   these too would leave a season's settings, a danger zone and a list of divisions as one
   undifferentiated column of fields.

   These rules restate every property the public `.card` now clears, because they have the
   same specificity as the rules up there and win only by coming later in the file. Do not
   move this block above them. */
.admin.card,
.admin .card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: var(--sp-4) var(--sp-5);
}
.admin .card + .card,
.admin.card + .card { margin-top: var(--sp-4); padding-top: var(--sp-4); }
@media (max-width: 719px) {
    .admin.card,
    .admin .card { padding: var(--sp-4); }
}
.admin .card > h1:first-child,
.admin .card > h2:first-child { margin-top: 0; }
/* The admin's own headings are the site's smaller step, and they carry their own rule —
   so the public heading rhythm, which is set for prose, must not add to it. */
.admin :is(h1, h2, h3) { margin-top: 0; letter-spacing: normal; }
.admin h3 { margin-top: var(--sp-4); }

/* Card / section header: title on the left, optional actions on the right */
.admin .card__head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--sp-3); flex-wrap: wrap;
    margin-bottom: var(--sp-3);
}
.admin .card__head .card__title,
.admin .card__head h2 { margin: 0; padding: 0; border: 0; }
.admin .card__head p { margin: 0; }

/* ---- Buttons: compact, auto-width, never full-bleed -------------- */
.admin .btn {
    display: inline-flex; align-items: center; justify-content: center; gap: .35rem;
    width: auto;
    padding: .42rem .85rem;
    border-radius: var(--radius-sm);
    font-size: var(--fs-200);
    font-weight: 600;
    line-height: 1.25;
    white-space: nowrap;
    vertical-align: middle;
}
.admin .btn:focus-visible { outline: 2px solid var(--hs-blue); outline-offset: 2px; }

/* Primary = filled brand red (inherits global .btn colours) */
.admin .btn--primary { background: var(--hs-red); color: #fff; }

/* Secondary / ghost recoloured to a calm neutral so the red primary leads */
.admin .btn--ghost {
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border-strong);
}
.admin .btn--ghost:hover {
    background: var(--surface-2);
    border-color: var(--text-muted);
    color: var(--text);
}

/* Danger = ghost red, only warms up on hover */
.admin .btn--danger {
    background: var(--surface);
    color: var(--hs-red);
    border: 1px solid var(--border-strong);
}
.admin .btn--danger:hover {
    background: var(--hs-red-soft);
    border-color: var(--hs-red);
    color: var(--hs-red-dark);
}

/* Size + icon variants */
.admin .btn--sm { padding: .3rem .6rem; font-size: var(--fs-100); }
.admin .btn--icon {
    padding: 0; width: 1.85rem; height: 1.85rem; font-size: 1rem; line-height: 1;
}

/* ---- Toolbars / inline action clusters --------------------------- */
.admin .row-actions,
.admin .form__actions {
    display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
}
.admin .row-actions { gap: .35rem; }
.admin .row-actions form { display: inline-flex; gap: .35rem; margin: 0; }

/* Per-user actions read as one cluster that wraps, not a single line that overflows. A user row
   can carry six of them - impersonate, approver, sign out everywhere, new claim code, delete -
   and on a laptop they pushed the table sideways. Wrapping keeps every action reachable without
   a horizontal scroll, and aligning to the start stops a wrapped last button sitting alone on
   the right where it reads as a different control. */
.admin td .row-actions { justify-content: flex-start; row-gap: .35rem; }
.admin td form[method="post"] { flex-wrap: wrap; }
/* A form's action row is a right-aligned toolbar, not a stretched button */
.admin .form__actions { justify-content: flex-end; margin-top: var(--sp-3); }

/* ---- Forms: tighter rhythm, consistent field height -------------- */
.admin .form { gap: var(--sp-3); max-width: 46rem; }
.admin .form__row { gap: 4px; }
.admin .form label,
.admin .field > label {
    font-size: var(--fs-100);
    font-weight: 700;
    letter-spacing: .02em;
    color: var(--text-muted);
    margin: 0;
}
/* checkbox / radio rows read as a normal inline label, not a heading */
.admin .form label:has(input[type="checkbox"]),
.admin .form label:has(input[type="radio"]),
.admin .check {
    display: inline-flex; align-items: center; gap: .45rem;
    font-size: var(--fs-200); font-weight: 600; color: var(--text);
    text-transform: none; letter-spacing: 0;
}

/* Visual field styling (no width here — width is set per-context below so
   fixed-width inputs like the score boxes keep their own sizing). */
.admin input:not([type]),
.admin input[type="text"],
.admin input[type="email"],
.admin input[type="number"],
.admin input[type="password"],
.admin input[type="url"],
.admin input[type="date"],
.admin select,
.admin textarea {
    padding: .4rem .6rem;
    font-size: var(--fs-200);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: #fff;
    color: var(--text);
}
.admin textarea { resize: vertical; line-height: var(--lh-body); }
/* Full-width inside stacked field contexts only */
.admin .form input[type="text"],
.admin .form input[type="email"],
.admin .form input[type="number"],
.admin .form input[type="password"],
.admin .form input[type="url"],
.admin .form input[type="date"],
.admin .form select,
.admin .form textarea,
.admin .field > input,
.admin .field > select,
.admin .field > textarea,
.admin .editor-row__fields input[type="text"],
.admin .editor-row__fields textarea { width: 100%; }
.admin input:focus,
.admin select:focus,
.admin textarea:focus {
    outline: 2px solid var(--hs-blue); outline-offset: 1px; border-color: var(--hs-blue);
}

/* Field building blocks */
.admin .field { display: grid; gap: 3px; min-width: 0; }
.admin .field--inline { grid-auto-flow: column; align-items: center; gap: .45rem; }
.admin .field--inline > label { white-space: nowrap; }
/* width utilities need to out-specify the input[type=…] rule above */
.admin input.input--xs,
.admin select.input--xs { width: 4.5rem; }
.admin input.input--sm,
.admin select.input--sm { width: 8rem; }

/* A select whose options are NAMES, not numbers.
   This one was on .input--xs - 4.5rem, the width meant for a two-digit score box - so a list of
   "Team (Club)" options rendered as "- ca" and the placeholder overflowed the control entirely.
   It sizes to its content instead, with a floor wide enough to read the longest club name and a
   ceiling so one long entry cannot push the row's buttons off the table. */
.admin select.input--pick {
    width: auto;
    min-width: 13rem;
    max-width: 22rem;
}

/* Two (or more) related fields on one row */
.admin .field-row {
    display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3);
}
@media (max-width: 560px) { .admin .field-row { grid-template-columns: 1fr; } }

/* ---- List editors as compact rows (coaches, club news, USPs) ----- */
.admin .editor-list { display: grid; gap: var(--sp-2); margin: 0 0 var(--sp-4); }
.admin .editor-row {
    display: grid; gap: var(--sp-2);
    padding: var(--sp-3);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface-2);
}
.admin .editor-row__fields { display: grid; gap: var(--sp-2); margin: 0; }
.admin .editor-row__foot {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--sp-3); flex-wrap: wrap;
}
.admin .editor-row__foot .row-actions { margin-left: auto; }

/* Compact single-line row for icon+text pairs (Appearance USPs) */
.admin .inline-row {
    display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap;
}
.admin .inline-row select { width: auto; flex: 0 0 auto; }
.admin .inline-row input[type="text"],
.admin .inline-row input { flex: 1 1 16rem; }

/* ---- Data tables: calm, dense, light headers --------------------- */
.admin table.standings,
.admin table.results { font-size: var(--fs-200); }
.admin table.standings thead th,
.admin table.results thead th {
    background: var(--surface-2);
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: .04em;
    font-size: var(--fs-100);
    font-weight: 700;
    border-bottom: 1px solid var(--border);
}
.admin table.standings th,
.admin table.results th,
.admin table.standings td,
.admin table.results td {
    padding: .45rem .6rem;
    vertical-align: middle;
    border-bottom: 1px solid var(--border);
}
/* replace the legacy cream zebra with a quiet neutral one */
.admin table.standings tbody tr:nth-child(odd),
.admin table.results tbody tr:nth-child(odd) { background: transparent; }
.admin table.standings tbody tr:nth-child(even),
.admin table.results tbody tr:nth-child(even) { background: var(--surface-2); }
/* The admin tables keep their own former literal as the var() fallback, so a site that has not
   set a hover colour renders exactly as it did. */
.admin table.standings tbody tr:hover,
.admin table.results tbody tr:hover {
    background: var(--row-hover, #FBE9E5);
    color: var(--row-hover-text, inherit);
}
.admin table.standings tbody tr:hover a,
.admin table.results tbody tr:hover a { color: var(--row-hover-text, inherit); }
.admin table td form { margin: 0; }
.admin table td input[type="date"] { min-width: 9rem; }

/* status tags used in queues */
.admin .tag {
    display: inline-block; padding: 1px 8px; border-radius: 999px;
    font-size: var(--fs-100); font-weight: 700; letter-spacing: .02em;
}
.admin .tag--ok { background: #E5F3E6; color: #1f7a24; }
.admin .tag--muted { background: var(--surface-2); color: var(--text-muted); }
.admin .tag--warn { background: var(--hs-red-soft); color: var(--hs-red-dark); }
/* A stop, not a warning: the one state where the person is being refused entry right now. Solid
   rather than soft so it separates at a glance from the expired-invitation tag beside it, which
   is the other red thing in the same column. */
.admin .tag--stop { background: var(--hs-red); color: #fff; }

/* ================================================================== */
/*  /admin/users — the account list                                   */
/* ================================================================== */
/* The row this replaces put eight to eleven controls in one cell at one visual weight: a
   captain picker, a club-rep picker, and loose buttons for approver, sign out everywhere, new
   claim code, impersonate, resend and Delete — destructive sitting beside routine. At a few
   hundred accounts that is not a list, it is a wall.

   The row now reads WHO / WHAT STATE / WHAT THEY ARE / WHAT YOU CAN DO. The first three are
   read-only; every action but the one routine chase-up lives behind a per-row <details>. */

/* ---- the filter bar ---------------------------------------------- */
.admin .userfilter {
    display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-3);
    margin: 0 0 var(--sp-3); padding: var(--sp-3);
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-left: 3px solid var(--flame, #FF5A36);
    border-radius: var(--radius-sm);
}
.admin .userfilter__field { display: grid; gap: 3px; min-width: 0; }
.admin .userfilter__field > label {
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: var(--fs-100); font-weight: 700; letter-spacing: .08em;
    text-transform: uppercase; color: var(--text-muted);
}
/* input[type="search"] is not in the admin's shared field rule, so it is styled here rather
   than by widening that list — this is the only place the admin uses one for a filter. */
.admin .userfilter input[type="search"] {
    padding: .4rem .6rem; font-size: var(--fs-200);
    border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
    background: #fff; color: var(--text);
}
.admin .userfilter input[type="search"],
.admin .userfilter select { min-width: 13rem; }
.admin .userfilter__go { display: flex; align-items: center; gap: .4rem; }

/* ---- the row ----------------------------------------------------- */
.admin .userlist td { vertical-align: top; }
.admin .userlist__who { min-width: 12rem; }
.admin .userlist__name { font-weight: 700; color: var(--text); }
/* An address can be long and has no spaces to break at, so it is allowed to break anywhere
   rather than widening the column it shares with the name. */
.admin .userlist__email {
    font-size: var(--fs-100); color: var(--text-muted); overflow-wrap: anywhere;
}
.admin .userlist__flag {
    margin-top: .3rem; display: flex; flex-wrap: wrap; gap: .3rem; align-items: baseline;
}
.admin .userlist__note { margin-top: .25rem; font-size: var(--fs-100); color: var(--text-muted); }
.admin .userlist__tags { display: flex; flex-wrap: wrap; gap: .25rem; }
/* The badge is one unit and must never wrap mid-phrase; the sentence under it may. */
.admin .userlist__state { white-space: nowrap; }
.admin .userlist__state .userlist__note { white-space: normal; max-width: 15rem; }
.admin .userlist__do { min-width: 11rem; }

/* ---- the per-row menu -------------------------------------------- */
/* A <details>, as the account menu in _Layout uses: it opens on click and on Enter, closes on
   Escape, and is reachable by keyboard with NO script at all. Nothing on this page adds any. */
.admin .rowmenu { display: inline-block; }
.admin .rowmenu > summary { list-style: none; cursor: pointer; }
.admin .rowmenu > summary::-webkit-details-marker { display: none; }
.admin .rowmenu > summary::-moz-list-bullet { list-style-type: none; }
.admin .rowmenu > summary:focus-visible { outline: 2px solid var(--hs-blue); outline-offset: 2px; }
.admin .rowmenu__trigger {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: .3rem .6rem; white-space: nowrap; user-select: none;
    font-size: var(--fs-100); font-weight: 600;
    background: var(--surface); color: var(--text);
    border: 1px solid var(--border-strong); border-radius: var(--radius-sm);
}
.admin .rowmenu__trigger:hover { background: var(--surface-2); border-color: var(--text-muted); }
.admin .rowmenu[open] .rowmenu__trigger { background: var(--surface-2); }
.admin .rowmenu__chev { width: 14px; height: 14px; transition: transform .18s ease; }
.admin .rowmenu[open] .rowmenu__chev { transform: rotate(180deg); }

/* IN FLOW, not floating. A panel positioned absolutely inside .table-wrap is clipped by that
   element's horizontal scroll, and unclipping it is a z-index and stacking-context argument
   this page does not need to have. An open row simply gets taller. */
.admin .rowmenu__panel {
    margin-top: .4rem; min-width: 15rem; max-width: 22rem;
    padding: var(--sp-2);
    background: var(--surface);
    border: 1px solid var(--border); border-radius: var(--radius-sm);
    box-shadow: 0 10px 24px -18px rgba(0, 0, 0, .55);
}
.admin .rowmenu__who {
    margin: 0 0 .25rem; padding: 0 .35rem .3rem;
    font-size: var(--fs-100); font-weight: 700; color: var(--text);
    border-bottom: 1px solid var(--border); overflow-wrap: anywhere;
}
.admin .rowmenu__group {
    margin: .55rem 0 .2rem; padding: 0 .35rem;
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: var(--fs-100); font-weight: 700; letter-spacing: .08em;
    text-transform: uppercase; color: var(--text-muted);
}
.admin .rowmenu__group--danger { color: var(--hs-red-dark); }
/* Out-specifies nothing: these sit after `.admin .row-actions form`, which the menu is nested
   inside, and carry the same weight — so they must stay BELOW it in this file. */
.admin .rowmenu__panel form { display: block; margin: 0; }
.admin .rowmenu__item {
    display: block; width: 100%; margin: 0; padding: .35rem;
    text-align: left; cursor: pointer;
    font-size: var(--fs-200); font-weight: 600; line-height: 1.3;
    background: transparent; color: var(--text);
    border: 0; border-radius: var(--radius-sm);
}
.admin .rowmenu__item:hover { background: var(--surface-2); }
.admin .rowmenu__item:focus-visible { outline: 2px solid var(--hs-blue); outline-offset: -2px; }
.admin .rowmenu__item--danger { color: var(--hs-red); }
.admin .rowmenu__item--danger:hover { background: var(--hs-red-soft); color: var(--hs-red-dark); }
.admin .rowmenu__pick { display: flex; align-items: center; gap: .35rem; padding: .2rem .35rem; }
.admin .rowmenu__pick select { flex: 1 1 auto; width: auto; min-width: 0; max-width: none; }
@media (max-width: 560px) {
    .admin .rowmenu__panel { min-width: 12rem; max-width: none; }
}

/* alerts (some pages already emit .alert markup) */
.admin .alert {
    margin: 0 0 var(--sp-4); padding: var(--sp-3) var(--sp-4);
    border-radius: var(--radius-sm); font-size: var(--fs-200);
    border: 1px solid transparent;
}
.admin .alert--ok { background: #E5F3E6; color: #1f7a24; border-color: #bfe1c1; }
.admin .alert--error { background: var(--hs-red-soft); color: var(--hs-red-dark); border-color: var(--hs-red); }
.admin .alert--error:empty { display: none; }

/* Contrast warning under a colour swatch (Appearance). The colour is a literal,
   not a brand token: this text has to stay readable precisely when the palette
   being edited is the problem. Empty until the page script fills it in. */
.admin .contrast-warn {
    margin: 2px 0 0;
    font-size: var(--fs-100);
    font-weight: 600;
    color: #9C2415;
}
.admin .contrast-warn:empty { display: none; }

/* The admin's own navigation is no longer a left rail: inside /admin the header bar
   renders the admin sections through the same .nav / .nav__mega rules as the public
   nav (see _Layout + Services/AdminNav.cs), so there is nothing to style here.

   .left-rail is gone entirely now. It was the home page's "Quick links" aside, removed at
   the owner's request; the rules that styled it outlived it, as did the 220px grid track it
   sat in — which is what squeezed the padel home page's announcement into a narrow column. */


/* A tick box sits at the start of its row, at its own size, with the words beside it. The
   label is the flex container (see the :has rule above), so this only has to stop the input
   growing and keep it from being dragged down by a wrapped second line of text. */
.form input[type="checkbox"],
.form input[type="radio"] {
    flex: none;
    width: auto;
    margin: 0;
    align-self: start;
    accent-color: var(--hs-red);
}
.admin .form label:has(input[type="checkbox"]),
.admin .form label:has(input[type="radio"]) {
    justify-content: flex-start;
    align-items: start;
    cursor: pointer;
}


/* ===========================================================================
   News: the list, and the two bits of chrome the article page adds.

   Added when /news stopped printing every article's body on one page and became a
   list you click into (/news/{handle}). The article BODY still uses the
   .news-item__body rules further up this file, untouched: the same markup renders
   on the article page as used to render on the list, so those rules were left
   exactly where they were rather than moved and renamed for the sake of it.
   =========================================================================== */

/* ---- news: the list, and one article ------------------------------------
   Both sit inside .clubpage, so they inherit its tokens, brand band, .inner gutter and court
   weave. Three pages that looked like three different websites -- the club page, the clubs
   directory and news -- now look like one, which is the whole reason they share the class. */

.newspage__empty { margin: var(--sp-7) 0; color: var(--text-muted); }

/* The lead story. A league's news is not a feed: one thing has usually just happened and the
   rest is history, so the newest gets the room and the others get a row each. */
.newspage .lead { padding-block: var(--sp-6) var(--sp-5); border-bottom: 1px solid var(--border); }
.newspage .lead__when {
    margin: 0 0 var(--sp-2);
    font-family: var(--cp-mono); font-size: 0.6875rem;
    letter-spacing: 0.14em; text-transform: uppercase; color: var(--flame);
}
.newspage .lead__title {
    margin: 0; font-size: clamp(1.5rem, 3.4vw, 2.1rem);
    font-weight: 800; line-height: 1.12; letter-spacing: -0.025em; text-wrap: balance;
}
.newspage .lead__title a { color: var(--text); text-decoration: none; }
.newspage .lead__title a:hover,
.newspage .lead__title a:focus-visible { color: var(--hs-red); text-decoration: underline; }
/* 60ch on the lead, not the card's full width: a summary set across 900px is harder to skim
   than the headline it belongs to. */
.newspage .lead__summary { margin: var(--sp-4) 0 0; max-width: 60ch; color: var(--text-muted); }
.newspage .lead__go { margin: var(--sp-4) 0 0; font-weight: 600; font-size: var(--fs-200); }

/* Earlier stories. A date rail on the left at reading width, stacked on a phone, so the eye
   runs down the dates or down the headlines without reading both. */
.newspage__more {
    margin: var(--sp-6) 0 var(--sp-2);
    font-family: var(--cp-mono); font-size: 0.6875rem;
    letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted);
}
.newspage .stories { list-style: none; margin: 0 0 var(--sp-7); padding: 0; }
.newspage .story {
    display: grid; grid-template-columns: 1fr; gap: var(--sp-1);
    padding-block: var(--sp-4); border-top: 1px solid var(--border);
}
@media (min-width: 640px) {
    .newspage .story { grid-template-columns: 9.5rem 1fr; gap: var(--sp-5); align-items: baseline; }
}
.newspage .story__when {
    margin: 0; font-family: var(--cp-mono); font-size: 0.6875rem;
    letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted);
    white-space: nowrap;
}
.newspage .story__title { margin: 0; font-size: 1.0625rem; font-weight: 700; letter-spacing: -0.01em; }
/* Undecorated until hovered, so a list of forty is not a wall of underlines -- but it keeps
   the link colour on hover, so it never stops LOOKING like a link, which is the half of that
   trade people get wrong. */
.newspage .story__title a { color: var(--text); text-decoration: none; }
.newspage .story__title a:hover,
.newspage .story__title a:focus-visible { color: var(--hs-red); text-decoration: underline; }
.newspage .story__summary {
    margin: var(--sp-2) 0 0; max-width: 68ch;
    font-size: var(--fs-200); color: var(--text-muted);
}

/* The article. One measure, because a reader is here to read. */
/* The body reuses .news-item__body for its typography; this only sets the MEASURE and the
   room around it. A reader is here to read, and 68ch is where prose stops being a wall. */
.newspage__body { max-width: 68ch; padding-block: var(--sp-6); }
.newspage__body img { max-width: 100%; height: auto; }

.newspage__back {
    margin: 0 0 var(--sp-7); padding-top: var(--sp-4);
    border-top: 1px solid var(--border); font-weight: 600; font-size: var(--fs-200);
}

/* The editor's address box. "/news/" sits against the input as a prefix rather than
   being typed: an administrator can see what they are actually editing, and cannot
   store a handle with a slash in it by accident. */
.admin .handle-field { display: flex; align-items: center; gap: .35rem; flex-wrap: wrap; }
.admin .handle-field__prefix { color: var(--text-muted); font-size: var(--fs-100); }
.admin .handle-field input { flex: 1 1 16rem; min-width: 0; }


/* ============================================================================
   MESSAGES  (/messages)

   The page shipped as a "thin shell" - real markup, no design - and looked it:
   flat text on the page background, because .card is transparent site-wide and
   the page leaned on it for panelling that .card has never provided.

   It borrows the club directory's language rather than inventing a third one:
   the same brand band, the same court geometry, the same flame-edged panel on
   a white surface. A member who has just come from /clubs should recognise it.
   ========================================================================= */

.msgs__lede { margin: var(--sp-3) 0 0; max-width: 46ch; color: var(--cp-on-brand-dim); }

.msgs__grid {
    display: grid; gap: var(--sp-5); align-items: start;
    grid-template-columns: minmax(16rem, 22rem) 1fr;
    margin-block: var(--sp-6) var(--sp-7);
}
/* One column before the two would start squeezing the thread. The rail goes
   first so a phone user searches, picks, and then reads - which is the order
   the task actually happens in. */
@media (max-width: 899px) { .msgs__grid { grid-template-columns: 1fr; } }

.msgs__rail,
.msgs__panel {
    background: var(--surface); border: 1px solid var(--border);
    border-top: 3px solid var(--flame);
}
.msgs__rail  { padding: var(--sp-5); }
.msgs__panel { padding: var(--sp-5); min-height: 22rem; display: flex; flex-direction: column; }

.msgs__find label {
    display: block; margin-bottom: var(--sp-2);
    font-family: var(--cp-mono); font-size: 0.625rem; letter-spacing: 0.14em;
    text-transform: uppercase; color: var(--text-muted);
}
.msgs__find input { width: 100%; }
.msgs__hint { margin: var(--sp-2) 0 0; font-size: var(--fs-200); color: var(--text-muted); }

.msgs__h {
    margin: var(--sp-6) 0 var(--sp-2); font-family: var(--cp-mono); font-size: 0.625rem;
    letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted); font-weight: 600;
}

.msgs__list { list-style: none; padding: 0; margin: 0; }
.msgs__list li + li { border-top: 1px solid var(--border); }
.msgs__list li.muted { padding: var(--sp-3) 0; font-size: var(--fs-200); }

/* Rows are buttons so they take focus and fire on Enter or Space; the styling
   keeps them reading as rows rather than as a stack of controls. */
.msg-item {
    display: block; width: 100%; padding: var(--sp-3) var(--sp-2); text-align: left;
    background: none; border: 0; font: inherit; color: inherit; cursor: pointer;
    border-left: 3px solid transparent;
}
.msg-item:hover    { background: var(--surface-2); }
.msg-item.is-active { background: var(--surface-2); border-left-color: var(--flame); }
.msg-item:focus-visible { outline: 2px solid var(--hs-blue); outline-offset: -2px; }
.msg-item .who {
    display: flex; gap: var(--sp-2);
    align-items: baseline; font-weight: 600;
}
/* The name takes the room; the time and the badge sit at the end. The name is what a long
   list is scanned by, so it is the part allowed to truncate rather than the part allowed to
   push everything else out of the row. */
.msg-item .who > :first-child { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-when {
    flex: none; font-weight: 400; font-size: 0.72rem; color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}
.msg-item .small {
    display: block; margin-top: 0.15rem; font-size: var(--fs-200);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.msg-badge {
    background: var(--hs-red); color: #fff; border-radius: 999px;
    font-size: 0.7rem; font-weight: 700; padding: 0 0.45rem; line-height: 1.5;
}

/* The resting state: what somebody sees before they have picked anybody, which
   on a new account is the only state they will see. It says what to do next
   rather than describing the page. */
.msgs__empty {
    margin: auto; padding: var(--sp-6) 0; text-align: center; max-width: 24rem;
}
.msgs__empty svg { width: 2.5rem; height: 2.5rem; color: var(--border-strong); }
.msgs__empty p { margin: var(--sp-2) 0 0; }

.msgs__convo { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.msgs__title {
    margin: 0 0 var(--sp-3); padding-bottom: var(--sp-3);
    border-bottom: 1px solid var(--border);
    font-size: 1.05rem; font-weight: 700; letter-spacing: -0.01em;
}

/* Scrollable, and focusable with it: a keyboard user has to be able to scroll
   back through a long thread. */
.thread {
    display: flex; flex-direction: column; gap: var(--sp-3);
    flex: 1; min-height: 8rem; max-height: 52vh; overflow-y: auto;
    padding: var(--sp-2) var(--sp-1);
}
.thread:focus-visible { outline: 2px solid var(--hs-blue); outline-offset: 2px; }

.bubble {
    max-width: 78%; padding: var(--sp-3) var(--sp-4);
    border-radius: var(--radius-lg); font-size: var(--fs-200); line-height: 1.5;
}
.bubble.mine   { align-self: flex-end; background: var(--hs-red); color: #fff; border-bottom-right-radius: var(--radius-sm); }
.bubble.theirs { align-self: flex-start; background: var(--surface-2); border: 1px solid var(--border); border-bottom-left-radius: var(--radius-sm); }
.bubble .meta  { display: block; font-size: 0.7rem; opacity: 0.75; margin-top: 0.3rem; }

.msgs__compose {
    display: flex; gap: var(--sp-3); align-items: flex-end;
    margin-top: var(--sp-4); padding-top: var(--sp-4);
    border-top: 1px solid var(--border);
}
.msgs__compose textarea { flex: 1; min-height: 3rem; resize: vertical; }
.msgs__compose .btn { flex: none; }
@media (max-width: 479px) {
    .msgs__compose { flex-direction: column; align-items: stretch; }
}

.msgs__noscript {
    margin: var(--sp-6) 0 0; padding: var(--sp-4);
    border: 1px solid var(--hs-red); border-radius: var(--radius);
    background: var(--hs-red-soft); color: var(--hs-red-dark);
}


/* The page-content editor's identity band. It exists because the editor and the page chooser
   above it could disagree, and the only thing saying which page you were about to overwrite was
   a heading that looked like a section title. */
.pageedit__who {
    display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--sp-3);
    padding-block: var(--sp-3); border-bottom: 2px solid var(--flame);
}
.pageedit__label {
    font-family: var(--font-mono); font-size: 0.625rem; letter-spacing: 0.14em;
    text-transform: uppercase; color: var(--text-muted); font-weight: 600;
}


/* ---- challenge ladders on a club page ----------------------------------- */
.clubpage .ladders {
    list-style: none; margin: var(--sp-5) 0 0; padding: 0;
    display: grid; gap: var(--sp-4);
    grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
}
.clubpage .ladders > li { margin-top: 0; }
.clubpage .ladder {
    padding: var(--sp-4) var(--sp-5);
    border: 1px solid var(--border); background: var(--surface);
    border-left: 3px solid var(--flame);
}
.clubpage .ladder__name {
    font-weight: 700; font-size: 1.05rem; color: var(--text); text-decoration: none;
}
.clubpage .ladder__name:hover { color: var(--hs-red); text-decoration: underline; }
.clubpage .ladder__meta {
    margin: var(--sp-2) 0 0; display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-4);
    font-family: var(--cp-mono); font-size: 0.625rem; letter-spacing: 0.12em;
    text-transform: uppercase; color: var(--text-muted);
}


/* ---- the roll of honour: years the site never ran ----------------------- */
.roll { margin-top: var(--sp-7); padding-top: var(--sp-6); border-top: 1px solid var(--border); }
.roll__year {
    display: grid; gap: var(--sp-2) var(--sp-5); align-items: baseline;
    padding-block: var(--sp-4); border-top: 1px solid var(--border);
}
@media (min-width: 640px) { .roll__year { grid-template-columns: 7rem 1fr; } }
.roll__label {
    margin: 0; font-family: var(--font-mono); font-size: var(--fs-300);
    letter-spacing: 0.08em; color: var(--hs-red); font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.roll__list { list-style: none; margin: 0; padding: 0; }
.roll__list > li {
    margin-top: 0; padding-block: 0.15rem;
    display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-1) var(--sp-3);
}
.roll__who  { font-weight: 600; }
.roll__what { color: var(--text-muted); font-size: var(--fs-200); }


/* ---- members' benefits -------------------------------------------------- */
.benefits__lede  { margin: var(--sp-3) 0 0; max-width: 46ch; color: var(--cp-on-brand-dim); }
.benefits__intro { margin-block: var(--sp-6) 0; max-width: 62ch; }
.benefits__empty { margin-block: var(--sp-7); color: var(--text-muted); }
.benefits__grid {
    list-style: none; padding: 0; margin-block: var(--sp-6) var(--sp-7);
    display: grid; gap: var(--sp-5);
    grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
}
.benefits__grid > li { margin-top: 0; }
.bn {
    display: flex; flex-direction: column; gap: var(--sp-2);
    padding: var(--sp-5); background: var(--surface);
    border: 1px solid var(--border); border-top: 3px solid var(--flame);
}
.bn__partner { margin: 0; font-size: 1.15rem; font-weight: 700; letter-spacing: -0.01em; }
.bn__offer   { margin: 0; font-size: var(--fs-400); }

/* The code is what somebody opened the page for, so it is the loudest thing on the card. */
.bn__codewrap {
    margin: var(--sp-2) 0 0; display: flex; flex-wrap: wrap;
    align-items: center; gap: var(--sp-2) var(--sp-3);
}
.bn__codelabel {
    font-family: var(--cp-mono, var(--font-mono)); font-size: 0.625rem;
    letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted);
}
.bn__code {
    font-family: var(--font-mono); font-size: 1.05rem; font-weight: 700;
    letter-spacing: 0.06em; color: var(--hs-red-dark);
    background: var(--surface-2); border: 1px dashed var(--border-strong);
    border-radius: var(--radius-sm); padding: 0.3rem 0.6rem;
    user-select: all;
}
.bn__nocode { margin: var(--sp-2) 0 0; font-size: var(--fs-200); color: var(--text-muted); }
.bn__terms  { margin: var(--sp-2) 0 0; font-size: var(--fs-200); color: var(--text-muted); }
.bn__foot {
    margin: auto 0 0; padding-top: var(--sp-3);
    display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-2) var(--sp-4);
    font-size: var(--fs-200);
}
.bn__until { color: var(--text-muted); }


/* A club a player ALSO turns out for, on /admin/players. A chip with its own remove, because
   the alternative -- a second select listing what is already set -- reads as "change this" when
   the action is "take this one away". */
.alsoclub {
    display: inline-flex; align-items: center; gap: 0.3rem;
    padding: 0.15rem 0.2rem 0.15rem 0.5rem; margin: 0 0.25rem 0.25rem 0;
    border: 1px solid var(--border-strong); border-radius: 999px;
    background: var(--surface-2); font-size: var(--fs-200); white-space: nowrap;
}
.alsoclub form { display: inline; margin: 0; }
.alsoclub__x {
    border: 0; background: none; cursor: pointer; padding: 0 0.25rem;
    color: var(--text-muted); font-size: 1rem; line-height: 1;
}
.alsoclub__x:hover { color: var(--hs-red); }
.alsoclub__x:focus-visible { outline: 2px solid var(--hs-blue); outline-offset: 1px; }


/* ============================================================================
   THE SIGNED-IN MEMBER'S OWN CORNER on the front page.

   Quieter than the news cards below it on purpose. This block is about what is
   happening to ONE person, and a member coming back weekly needs to find it
   rather than be sold it -- so it is a light band rather than a set of
   flame-topped cards competing with the league's own headlines.
   ========================================================================= */
.mine {
    margin-block: var(--sp-5) var(--sp-6);
    padding: var(--sp-5); background: var(--surface-2);
    border: 1px solid var(--border); border-left: 3px solid var(--flame);
}
.mine__hd {
    margin: 0 0 var(--sp-4); font-size: 1.05rem; font-weight: 700; letter-spacing: -0.01em;
}
.mine__grid {
    display: grid; gap: var(--sp-4);
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}
.mine__card {
    display: flex; flex-direction: column; gap: 0.15rem;
    padding: var(--sp-4); background: var(--surface); border: 1px solid var(--border);
}
.mine__card--next  { border-left: 3px solid var(--hs-blue); }
.mine__card--alert { border-left: 3px solid var(--hs-red); }
.mine__label {
    margin: 0; font-family: var(--font-mono); font-size: 0.625rem;
    letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted);
}
.mine__big { margin: 0.15rem 0 0; font-size: var(--fs-400); line-height: 1.35; }
.mine__sub { margin: 0.15rem 0 0; font-size: var(--fs-200); color: var(--text-muted); }
.mine__more { margin: auto 0 0; padding-top: var(--sp-3); font-size: var(--fs-200); }
.mine__more a { text-decoration: none; }
.mine__more a:hover { text-decoration: underline; }

/* =========================================================================
   Sponsors - Pages/Shared/_Sponsors.cshtml
   A quiet "supported by" band at the foot of a page, never above its content, and
   deliberately NOT a card: no flame edge, no shadow, nothing competing with the league's
   own tables. Tokens only, so it follows each site's palette. The logo box is fixed by the
   img's width/height and object-fit keeps any logo's own shape inside it, so a late-loading
   image never moves the page.
   ========================================================================= */
.sponsors {
    margin-block: var(--sp-6) var(--sp-4);
    padding-top: var(--sp-4);
    border-top: 1px solid var(--border);
    text-align: center;
}
.sponsors__hd {
    margin: 0 0 var(--sp-3);
    font-family: var(--font-mono); font-size: 0.6875rem;
    letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted);
}
.sponsors__list {
    display: flex; flex-wrap: wrap; justify-content: center; align-items: center;
    gap: var(--sp-4) var(--sp-6);
    margin: 0; padding: 0; list-style: none;
}
.sponsors__list img,
.sponsor-line img {
    display: block; max-width: 100%; height: auto;
    object-fit: contain;
}
.sponsors__list img { width: 160px; aspect-ratio: 160 / 64; }
.sponsors__list a,
.sponsor-line a { display: inline-block; text-decoration: none; }
.sponsors__list a:focus-visible,
.sponsor-line a:focus-visible { outline: 2px solid var(--hs-blue); outline-offset: 3px; }

.sponsor-line {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
    margin: var(--sp-4) 0 0; font-size: var(--fs-100); color: var(--text-muted);
}
.sponsor-line img { width: 96px; aspect-ratio: 96 / 32; }

/* ---- analytics consent banner ------------------------------------------------------------
   Rendered only on a site with an analytics id configured (see _AnalyticsBanner). Sticky at the
   foot of the page and in the normal flow, so it never covers content: scrolled to the end, it
   sits below the footer rather than over it. Not a dialog - nothing is blocked while it is
   unanswered. Both buttons share one class, so neither choice is the more prominent. */
.consent {
    position: sticky; bottom: 0; z-index: 900;
    padding: var(--sp-4);
    background: var(--surface); color: var(--text);
    border-top: 3px solid var(--hs-red);
    box-shadow: 0 -4px 16px rgba(0, 0, 0, .15);
}
.consent[hidden] { display: none; }
.consent__inner {
    max-width: 72rem; margin: 0 auto;
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: var(--sp-3) var(--sp-5);
}
.consent__title { margin: 0 0 var(--sp-1); font-size: 1rem; }
.consent__text { margin: 0; max-width: 46rem; font-size: var(--fs-200); }
.consent__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.consent__btn { min-width: 12rem; }
.consent__btn:focus-visible { outline: 3px solid var(--hs-blue); outline-offset: 2px; }

/* The share bar (Pages/Shared/_ShareBar.cshtml): "Share on WhatsApp", and - revealed by
   /js/share.js where the browser can do them - "Share…" and "Copy link". Sized as the match
   page's print button was, so the two rows of buttons on a scorecard match. */
.share-bar { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: center; margin-top: var(--sp-3); }
.share-bar__btn { display: inline-flex; align-items: center; gap: .4rem; padding: .35rem .7rem; font-size: var(--fs-200); }
/* An author display rule beats the browser's [hidden], so restate it. */
.share-bar__btn[hidden] { display: none; }
.share-bar__btn svg { width: 1rem; height: 1rem; flex: none; }
/* The WhatsApp button carries its own icon; the automatic external-link arrow would be a second one. */
.share-bar__btn::after { content: none; }
.share-bar__btn:focus-visible { outline: 3px solid var(--hs-blue); outline-offset: 2px; }
.share-bar__status:empty { display: none; }
@media print { .share-bar { display: none; } }


/* ---- SEO & sharing on the page and news editors (Pages/Admin/_SeoFields) ---- */
.seo-fields {
    margin: var(--sp-5) 0; padding: var(--sp-4); border: 1px solid var(--border);
    border-radius: var(--radius); background: var(--surface-2);
}
.seo-fields legend {
    padding: 0 var(--sp-2); font-family: var(--font-display); font-weight: 700; font-size: var(--fs-400);
}
.seo-count { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.seo-count--over { color: var(--loss); font-weight: 600; }
.seo-fields__thumb { display: block; width: 240px; max-width: 100%; height: auto; aspect-ratio: 1200 / 630; object-fit: cover; border: 1px solid var(--border); border-radius: var(--radius-sm); }
.seo-preview { display: grid; gap: var(--sp-4); grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); margin-top: var(--sp-4); }
.seo-preview p { margin: 0; overflow-wrap: anywhere; }
.seo-preview__label {
    margin-bottom: var(--sp-2) !important; font-family: var(--font-mono); font-size: 0.625rem;
    letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted); font-weight: 600;
}
.seo-preview__google, .seo-preview__card { padding: var(--sp-3); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
.seo-preview__url { font-size: var(--fs-100); color: var(--text-muted); }
.seo-preview__title { font-size: var(--fs-400); color: #1a0dab; line-height: var(--lh-tight); margin-block: .15rem !important; }
.seo-preview__description { font-size: var(--fs-100); color: var(--text-muted); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.seo-preview__card { padding: 0; overflow: hidden; }
.seo-preview__card .seo-preview__label { padding: var(--sp-3) var(--sp-3) 0; }
.seo-preview__image { display: block; width: 100%; height: auto; aspect-ratio: 1200 / 630; object-fit: cover; background: var(--surface-2); }
.seo-preview__text { padding: var(--sp-3); border-top: 1px solid var(--border); }
.seo-preview__host { font-size: var(--fs-100); color: var(--text-muted); text-transform: uppercase; }
.seo-preview__share-title { font-weight: 700; }
.seo-preview__share-description { font-size: var(--fs-100); color: var(--text-muted); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
