/* Design system — shared tokens every page/CSS file draws from. Palette
   leans into green/purple/yellow-green (see styleguide.md §2), hand-picked
   against wimbledon.com/scores as a direct reference rather than a
   pixel-identical copy of AELTC's own brand colors. Dark-mode is
   intentionally out of scope — this app is charted outdoors in daylight,
   where high contrast on a light background is what actually matters
   (spec: "high contrast, easy to read in outdoor sunlight"). Full rationale
   for every token below lives in styleguide.md — treat that file as the
   source of truth and keep this block in sync with it. */

/* Fonts — self-hosted (wwwroot/fonts) rather than loaded from Google's CDN
   at runtime, so the app has no network dependency on an external host even
   though it's charted courtside on phones, sometimes on spotty signal. Both
   are variable fonts (single file covers their whole weight range) with
   font-display: swap so text renders immediately in the fallback stack and
   swaps in once the font loads — never a blank screen. */
@font-face {
    font-family: "Space Grotesk";
    font-style: normal;
    font-weight: 500 700;
    font-display: swap;
    src: url("/fonts/space-grotesk-variable.woff2") format("woff2");
}

@font-face {
    font-family: "Manrope";
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url("/fonts/manrope-variable.woff2") format("woff2");
}

/* Serif accent — for the handful of eyebrow-style labels that borrow
   wimbledon.com's look (their own "THE CHAMPIONSHIPS" eyebrow is set in
   Adobe Caslon Pro, a commercial/licensed font we can't self-host; Libre
   Caslon Text is the open, freely-licensed Caslon revival closest to it).
   Not a variable font, so regular and bold ship as two separate static
   files rather than one weight range like the two above. Used sparingly —
   see --font-serif below and styleguide.md §2. */
@font-face {
    font-family: "Libre Caslon Text";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("/fonts/libre-caslon-text-regular.woff2") format("woff2");
}

@font-face {
    font-family: "Libre Caslon Text";
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url("/fonts/libre-caslon-text-bold.woff2") format("woff2");
}

:root {
    /* Brand — deep court green, re-picked against wimbledon.com/scores as a
       direct reference (see styleguide.md §2): their hero/header green,
       their near-black green hero background, and the pale mint they use to
       highlight a winner row. Close to their values but hand-adjusted, not
       a pixel-identical copy of AELTC's own brand colors. */
    --color-primary: #046a37;
    --color-primary-dark: #034f29;
    --color-primary-darker: #0c2a18;
    --color-primary-light: #dcf1e4;

    /* Same green family, re-tuned for the dark scoreboard/header cards
       (background: --color-primary-darker) rather than the light surface —
       a plain --color-primary reads as almost-black-on-black there, so
       anything meant to stand out against the dark card needs its own
       brighter/washed variant instead of the light-surface tokens above. */
    --color-primary-bright-on-dark: #4ade80;
    --color-primary-muted-on-dark: #9fd0b0;
    --color-primary-faint-on-dark: #b8c4bb;

    /* Tennis-ball accent — for live indicators, "clinched" badges, active
       states, and anywhere something needs to pop. Used sparingly: this is
       a UI accent, not a background color, since it's too light/saturated
       for body text or large fills to stay readable. Always pair with
       --color-accent-ink for text placed on top of it. Re-picked to match
       the yellow-green wimbledon.com uses over its dark green header,
       rather than the previous neon lime. */
    --color-accent: #c8e56a;
    --color-accent-dark: #69782c;
    --color-accent-ink: #323c17;
    --color-accent-light: #f5f9e5;

    /* Visiting-side color — the "visiting team" color everywhere a match/
       score is shown; kept distinct from home-green/accent-yellow on
       purpose (spec requires the two sides be instantly distinguishable at
       a glance). Purple, matching the third color wimbledon.com actually
       leans on (their "THE CHAMPIONSHIPS" eyebrow text, the logo ring) —
       replaces the old clay red-orange, which didn't come from that
       reference at all. */
    --color-visiting: #56198a;
    --color-visiting-light: #f2e7f8;
    --color-visiting-bright-on-dark: #c77dff;

    /* Neutrals — warmed slightly to sit better next to green than the old
       blue-leaning grays did */
    --color-ink: #1c241d;
    --color-text: #2b332c;
    --color-text-muted: #6b756c;
    --color-text-faint: #9aa39b;
    --color-border: #e1e7e2;
    --color-border-strong: #cdd6ce;
    --color-surface: #ffffff;
    --color-surface-alt: #f7f9f7;
    --color-bg: #f3f6f3;
    --color-disabled: #9aa4b2;

    /* Semantic — success reuses the brand green family; danger/warning stay
       conventional so they're never ambiguous with the palette */
    --color-success: #046a37;
    --color-success-bg: #dcf1e4;
    --color-danger: #b3261e;
    --color-danger-dark: #8f1f18;
    --color-danger-bg: #fdecea;
    --color-warning: #7a5b00;
    --color-warning-bg: #fff3cd;

    /* Issue Penalty specifically — a deep wine red rather than .btn-danger's
       conventional orange-leaning red, which fought visually with the green/
       purple palette next to it (chart.css's Undo/Swap/End Match row). A
       penalty call is a serious in-game action, not a destructive delete, so
       it doesn't need to borrow .btn-danger's alarm color; this leans enough
       toward the visiting purple's hue to read as part of the same family
       instead of clashing with it. */
    --color-penalty: #7a1f3d;
    --color-penalty-dark: #5c1730;
    --color-warning-border: #ffc107;

    /* Typography */
    --font-heading: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-body: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-sans: var(--font-body);
    --font-serif: "Libre Caslon Text", Georgia, "Times New Roman", serif;

    /* Fluid type scale — ~1.25 ratio, interpolated 375px -> 1440px viewport */
    --text-xs: clamp(0.75rem, 0.72rem + 0.10vw, 0.8125rem);
    --text-sm: clamp(0.875rem, 0.84rem + 0.15vw, 0.9375rem);
    --text-base: clamp(1rem, 0.96rem + 0.20vw, 1.0625rem);
    --text-lg: clamp(1.125rem, 1.06rem + 0.30vw, 1.25rem);
    --text-xl: clamp(1.375rem, 1.28rem + 0.45vw, 1.5rem);
    --text-2xl: clamp(1.625rem, 1.48rem + 0.70vw, 1.875rem);
    --text-3xl: clamp(2rem, 1.78rem + 1.05vw, 2.375rem);
    --text-4xl: clamp(2.5rem, 2.16rem + 1.60vw, 3rem);

    /* Spacing scale */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;
    --space-8: 3rem;

    /* Radius */
    --radius-sm: 6px;
    --radius-md: 8px;
    --radius-lg: 13px;
    --radius-pill: 999px;

    /* Shadow */
    --shadow-sm: 0 1px 3px rgba(16, 24, 40, 0.08);
    --shadow-md: 0 4px 12px rgba(16, 24, 40, 0.10);
    --shadow-lg: 0 10px 30px rgba(16, 24, 40, 0.16);

    /* Icons */
    /* The smallest size carries the row-level actions (lineup, scoreboard,
       chart, edit, delete) that get tapped on a phone at the side of a
       court, so it's set a little above the 1rem a caption-sized glyph
       would take. */
    --icon-size-sm: 1.15rem;
    --icon-size-md: 1.5rem;
    --icon-size-lg: 2rem;
    --icon-size-xl: 3rem;
}

/* Every page here toggles sections on and off via the `hidden` attribute/
   property rather than a visibility class, on the assumption that `hidden`
   just works. It doesn't, by default: a plain author rule like
   `.foo { display: flex; }` sits at the same specificity as the browser's
   built-in `[hidden] { display: none }`, and normal author rules always beat
   normal user-agent ones regardless of which was written "later" — so any
   class that sets display unconditionally silently cancels `hidden` on
   itself. This was caught three times independently in one afternoon
   (.point-entry, .game-entry, .set-entry all showing at once regardless of
   which charting level was picked) before it was clear it wasn't three bugs
   but one systemic one — grepping turned up the same defect already latent
   in .add-player-form, .charting-level-change-btn, .team-picker-bar,
   .set-entry-row, and .btn (meaning every hideable button using it, not just
   a specific instance).
   One rule, unconditionally, closes the whole class of bug rather than
   chasing the next class that happens to set display and later gets a
   `hidden` toggle. !important is deliberate and safe here — there's no
   legitimate reason a hidden element should ever actually render, so nothing
   should ever need to out-rank this. */
[hidden] {
    display: none !important;
}

/* Icons — monochrome SVGs referenced from wwwroot/icons/sprite.svg via
   <svg class="icon"><use href="/icons/sprite.svg#icon-name"/></svg>.
   fill: currentColor so every icon inherits its surrounding text color —
   never set a fill on an individual icon usage. See styleguide.md §9. */
.icon {
    display: inline-block;
    width: var(--icon-size-md);
    height: var(--icon-size-md);
    fill: currentColor;
    flex-shrink: 0;
}

.icon-sm { width: var(--icon-size-sm); height: var(--icon-size-sm); }
.icon-lg { width: var(--icon-size-lg); height: var(--icon-size-lg); }
.icon-xl { width: var(--icon-size-xl); height: var(--icon-size-xl); }

/* Icon buttons — plain-background small square buttons for an edit/delete
   pair next to a list row. Monochrome via .icon's currentColor. */
.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border: none;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--color-text-muted);
    cursor: pointer;
    transition: background-color 0.15s, color 0.15s;
}

.icon-btn:hover {
    background: var(--color-surface-alt);
    color: var(--color-text);
}

.icon-btn-danger:hover {
    background: var(--color-danger-bg);
    color: var(--color-danger);
}

.icon-btn:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

.icon-btn:disabled:hover {
    background: transparent;
    color: var(--color-text-muted);
}

/* Modal — generic overlay/panel used anywhere the app needs a popup dialog
   (report preview, penalty entry, admin edit forms, delete confirmation).
   Reused as-is; page-specific content goes inside .modal-panel. */
.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 1.5rem 1rem;
    overflow-y: auto;
    z-index: 1000;
}

.modal-panel {
    background: var(--color-surface);
    border-radius: var(--radius-lg);
    padding: 1.25rem;
    width: 100%;
    max-width: 480px;
    box-shadow: var(--shadow-lg);
}

/* Wider variant for forms with a full-sentence field (e.g. the Issue
   Penalty reason textarea), so its text isn't cramped into a narrow column. */
.modal-panel-wide {
    max-width: 640px;
}

/* Marks whichever player/pair is currently serving — used next to a name on
   the charting page's own scoreboard (Pages/Matches/Chart) and on every
   live-scoreboard view (wwwroot/js/set-score-table.js). Shared here since
   both draw from the same server-computed value (Match.CurrentServerSide). */
.serving-dot {
    display: inline-block;
    width: 0.55rem;
    height: 0.55rem;
    border-radius: 50%;
    background: var(--color-accent);
    border: 1px solid var(--color-accent-dark);
    margin-left: 0.4rem;
    vertical-align: middle;
}

/* Set score table: two rows (home/visiting), one column per set (blank past
   however many have been played, so every table is the same width), then a
   wider "current game" column with a distinct background. Used inside a
   .position-row (compact, scores.css) on the dual-match/admin list views,
   standalone on the single-match detail view (.set-score-table-lg,
   scores.css), and on the charting page's own scoreboard (chart.css,
   equally .set-score-table-lg — it's the page's whole focus too). Lives
   here rather than in scores.css since the charting page needs it and
   doesn't load that stylesheet. See wwwroot/js/set-score-table.js. */

.set-score-table {
    width: 100%;
    border-collapse: collapse;
    /* Fixed layout + an explicit width on every score column (below) is
       what makes the 4 score columns line up across different position
       rows' *separate* <table> elements — each table's own content would
       otherwise size its columns independently (e.g. a table with no
       serving dot yet, or shorter names, ending up narrower), so nothing
       would visually line up from row to row. */
    table-layout: fixed;
}

.set-score-name {
    text-align: left;
    font-size: 0.88rem;
    font-weight: 700;
    color: var(--color-ink);
    padding: 0.1rem 0.5rem 0.1rem 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.set-score-cell {
    text-align: center;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    font-size: 0.92rem;
    color: var(--color-ink);
    padding: 0.1rem 0.4rem;
    width: 2rem;
}

.set-score-cell-empty {
    color: var(--color-text-faint);
    font-weight: 500;
}

/* A completed set's winning side reads bolder than the losing side; an
   in-progress set (Complete: false on the DTO) doesn't get either — nobody's
   "won" it yet, so both sides stay the plain .set-score-cell weight
   regardless of who's currently ahead. */
.set-score-cell-set-winner {
    font-weight: 800;
}

.set-score-cell-set-loser {
    font-weight: 500;
    color: var(--color-text-muted);
}

/* The tiebreak loser's point count (e.g. the "4" in "7-6(4)"), shown next
   to the losing side's game count instead of in parentheses — a table cell
   doesn't have room for "(4)" without crowding the column. */
.set-score-tiebreak {
    font-size: 0.62em;
    font-weight: 700;
    vertical-align: super;
    margin-left: 1px;
}

.set-score-cell-current {
    background: var(--color-accent-light);
    color: var(--color-accent-dark);
    border-radius: var(--radius-sm);
    font-weight: 800;
    /* Wider than a set column — "Ad"/"40" and higher tiebreak point counts
       need more room than a single set's game count ever does. */
    width: 2.6rem;
}

/* Fluid rather than the fixed 1.15rem this used to be. The wider score
   columns below (needed for a double-digit tiebreak superscript) leave this
   column less room on a narrow phone, so it shrinks a little there instead
   of truncating names more aggressively than before. Stays full-size from
   roughly tablet width up. */
.set-score-table-lg .set-score-name {
    /* Saturates at the old fixed 1.15rem by ~700px viewport, same as before
       there. Only phone widths below that actually shrink. */
    font-size: clamp(0.95rem, 0.72rem + 0.98vw, 1.15rem);
    max-width: none;
}

.set-score-table-lg .set-score-cell {
    /* Fluid with a lower floor than --text-2xl (which this used to use
       directly). Shrinking a little on a narrow phone both keeps a
       double-digit tiebreak superscript comfortably clear of the column
       edge and gives the name column above back some of the room the wider
       columns below take from it. Still a large, clearly legible score
       digit at every width (1.35rem is well above body text size).
       Saturates at the old max (1.875rem) by ~900px viewport, so nothing
       narrower than a phone actually renders smaller than before. */
    font-size: clamp(1.35rem, 0.975rem + 1.6vw, 1.875rem);
    /* Horizontal padding trimmed from 0.6rem to claw back interior room for
       the wider case below, rather than only growing the column outward.
       The name column (no fixed width; see .set-score-table's comment) eats
       whatever this table gains, and on a 375px phone it's already down to
       ~40px. Vertical padding untouched, since that's row height/spacing,
       not what overflowed. */
    padding: 0.4rem 0.35rem;
    /* Was 3.2rem, too narrow once a match tiebreak's raw point total (each
       side's own th/tv, not the usual single-digit "(4)" of a 6-6 tiebreak)
       runs to two digits, e.g. a 12-10 breaker deciding the 3rd set. The
       superscript was overflowing the column in that case. */
    width: 3.75rem;
}

.set-score-table-lg .set-score-cell-current {
    width: 4.4rem;
}

.modal-panel h2 {
    font-size: 1.2rem;
    font-weight: 700;
    margin-bottom: 1rem;
}

/* .modal-actions is what admin-common.js builds its modals with;
   .form-actions is the hand-written equivalent in the charting, trainer,
   review and dashboard markup. They're the same thing — a row of buttons
   closing a modal form — and the second had no rule at all, so those modals
   were rendering an unstyled block of buttons while the admin ones lined up
   correctly. Same treatment for both rather than a second definition to keep
   in sync. */
.modal-actions,
.form-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.6rem;
    margin-top: 1.25rem;
}

.confirm-delete-message {
    color: var(--color-text);
    line-height: 1.5;
}

.confirm-delete-detail {
    color: var(--color-text-muted);
    font-size: 0.88rem;
    margin-top: 0.4rem;
}

/* confirmTypedDelete's "type X to confirm" field. */
.confirm-typed-delete-label {
    display: block;
    margin-top: 1rem;
    font-weight: 600;
    font-size: 0.88rem;
}

.confirm-typed-delete-label input {
    display: block;
    margin-top: 0.4rem;
    width: 100%;
}

/* General Styles */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

/* Flex column rather than a fixed calc() on main (see the old comment
   there): that approach guessed at the combined height of the header and
   footer and reserved exactly that much, so any page where the real header
   (nav wrapping to two lines, the impersonation banner) or footer differed
   from the guess left a gap of leftover background below the footer on a
   short page, or forced a scrollbar on a tall one. flex: 1 0 auto on main
   below has it absorb exactly whatever's left, so the footer always lands
   at the bottom of the viewport with nothing to tune by hand. */
body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    font-family: var(--font-body);
    font-size: var(--text-base);
    background-color: var(--color-bg);
    color: var(--color-text);
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
}

/* Form controls don't inherit font-family from the page by default in the
   browser's UA stylesheet — without this they'd silently fall back to
   Arial regardless of the --font-body token above. */
button, input, select, textarea {
    font-family: inherit;
}

h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-heading);
    font-weight: 700;
    color: var(--color-ink);
    line-height: 1.25;
}

h1 { font-size: var(--text-3xl); margin-top: var(--space-4); }
h2 { font-size: var(--text-2xl); }
h3 { font-size: var(--text-xl); }
h4 { font-size: var(--text-lg); }

a {
    color: var(--color-primary);
}

/* Horizontal-only padding, deliberately not the shorthand: <main
   class="container"> is both `main` and `.container` on the same element,
   and a shorthand `padding` here would silently win the whole property
   (top/bottom included) over main's own vertical padding below regardless
   of which was more specific for the sides that actually matter. Same
   cascade trap as the [hidden]/display one documented in styleguide.md §10,
   just with padding instead of display. Longhand keeps this rule out of
   main's top/bottom padding entirely. */
.container {
    /* Explicit rather than left to default block sizing: <main
       class="container"> is a direct flex child of body's sticky-footer
       column (see body, above), and a flex item with auto cross-axis
       margins — which margin: 0 auto below computes to here — is exempt
       from the default stretch-to-fill behavior per the flexbox spec.
       Without this, main sized itself to its own max-content width
       instead of the available column width, which happened to be
       exactly narrow enough to collapse the dashboard's auto-fit tile
       grid down to a single column even on a full-width desktop screen.
       width: 100% opts back into filling the available width (still
       capped by max-width and centred by the auto margins below). */
    width: 100%;
    max-width: 1200px;
    margin: 0 auto;
    padding-left: 20px;
    padding-right: 20px;
}

/* Navigation */
.navbar {
    background-color: var(--color-primary-darker);
    color: white;
    padding: 0.9rem 0;
    box-shadow: var(--shadow-md);
}

.navbar-brand {
    font-size: 1.4rem;
    font-weight: 800;
    letter-spacing: -0.01em;
    color: white;
    text-decoration: none;
}

.navbar-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.75rem;
}

/* Hidden above the narrow breakpoint below, where .navbar-links renders
   inline instead. */
.navbar-toggle {
    display: none;
    background: none;
    border: none;
    padding: 0.3rem;
    border-radius: var(--radius-sm);
    color: white;
    cursor: pointer;
}

.navbar-toggle:hover {
    background: rgba(255, 255, 255, 0.1);
}

.navbar-toggle .icon {
    width: 1.5rem;
    height: 1.5rem;
}

.navbar-links {
    display: flex;
    align-items: center;
    gap: 1.25rem;
    flex-wrap: wrap;
}

.navbar-link {
    color: #a9c9b3;
    text-decoration: none;
    font-size: 0.9rem;
    font-weight: 600;
    transition: color 0.15s;
}

.navbar-link:hover {
    color: white;
}

.navbar-user {
    color: white;
    font-size: 0.9rem;
    font-weight: 700;
}

.navbar-logout-btn {
    background: none;
    border: 1px solid #2f4a38;
    color: #a9c9b3;
    border-radius: var(--radius-sm);
    padding: 0.35rem 0.8rem;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s;
}

.navbar-logout-btn:hover {
    color: white;
    border-color: white;
}

/* Impersonation banner: persistent across every page while an admin is
   viewing as a Coach/TeamMember (_Layout.cshtml, UserEndpoints.Impersonate).
   Full-strength accent, since this is exactly what that token is for:
   something that needs to stay noticeable the whole time this state is
   active, not just on first load. */
.impersonation-banner {
    background: var(--color-accent);
    color: var(--color-accent-ink);
}

/* Also carries .container for its max-width/horizontal padding. Longhand
   here so this doesn't re-fight that shared rule for the sides it doesn't
   care about (see styleguide.md §10's [hidden]/padding writeup). */
.impersonation-banner-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.5rem;
    padding-top: 0.5rem;
    padding-bottom: 0.5rem;
    font-size: 0.88rem;
    font-weight: 600;
}

.impersonation-banner-inner .btn-secondary {
    min-height: 32px;
    padding: 0.3rem 0.9rem;
    font-size: 0.82rem;
}

/* Main Layout */
main {
    flex: 1 0 auto;
    padding: 2rem 0;
}

/* Reachable from every page on purpose: a coach, a parent or a school's
   counsel should be able to find what the site says about student information
   without hunting for it. */
.footer-link {
    margin-left: 0.75rem;
    color: inherit;
    text-decoration: underline;
}

.footer-link:hover {
    color: #fff;
}

footer {
    background-color: var(--color-primary-darker);
    color: #7fa38c;
    text-align: center;
    padding: 1.5rem 0;
    margin-top: 3rem;
    font-size: 0.85rem;
}

/* Buttons */
.btn {
    display: inline-block;
    padding: 0.7rem 1.4rem;
    border: none;
    border-radius: var(--radius-sm);
    font-size: 1rem;
    font-weight: 600;
    /* A <button> has no inherited line-height of its own in the UA
       stylesheet (it computes to "normal", ~1.15 of its font size) while an
       <a> inherits the page's 1.6 — same padding and font-size, but a
       shorter box, which is what made a .btn-sm link and a .btn-sm button
       sitting side by side (admin-schools.js's Google Maps row) come out a
       few pixels different in height. Pinning it here makes every .btn the
       same height regardless of which element renders it. */
    line-height: 1;
    cursor: pointer;
    text-decoration: none;
    text-align: center;
    transition: background-color 0.15s, box-shadow 0.15s, transform 0.05s;
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
}

.btn:active {
    transform: translateY(1px);
}

.btn:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* Flat neutral gray regardless of variant (primary/secondary/danger/success)
   — a disabled button should read as obviously inert at a glance, not just
   a slightly-dimmer version of its normal color. Wins over the variant
   background rules below on specificity alone (pseudo-class + class beats
   a single class), no !important needed. */
.btn:disabled {
    background-color: var(--color-border-strong);
    color: var(--color-text-faint);
    cursor: not-allowed;
    box-shadow: none;
}

.btn:disabled:active {
    transform: none;
}

.btn-primary {
    background-color: var(--color-primary);
    color: white;
}

.btn-primary:hover {
    background-color: var(--color-primary-dark);
}

.btn-secondary {
    background-color: #667085;
    color: white;
}

.btn-secondary:hover {
    background-color: #545c6e;
}

.btn-danger {
    background-color: var(--color-danger);
    color: white;
}

.btn-danger:hover {
    background-color: var(--color-danger-dark);
}

/* Forms */
.form-group {
    margin-bottom: 1.5rem;
}

label {
    display: block;
    margin-bottom: 0.5rem;
    font-weight: 600;
    color: var(--color-text);
    font-size: 0.92rem;
}

input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="date"],
input[type="datetime-local"],
select,
textarea {
    width: 100%;
    padding: 0.7rem 0.85rem;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    font-size: 1rem;
    font-family: inherit;
    color: var(--color-text);
    background: var(--color-surface);
    transition: border-color 0.15s, box-shadow 0.15s;
}

input:focus,
select:focus,
textarea:focus {
    outline: none;
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px var(--color-primary-light);
}

/* Responsive */
@media (max-width: 768px) {
    .container {
        padding-left: 15px;
        padding-right: 15px;
    }

    .navbar-brand {
        font-size: 1.2rem;
    }

    .card {
        margin-bottom: 1rem;
    }

    /* Below this width .navbar-links used to just wrap onto a second (and
       for a logged-in admin, third) line, since .navbar-inner and
       .navbar-links both allow wrapping — cramped, and it pushed the rest
       of the page down by however many rows the links needed on that
       particular device. Collapsed into a toggle button + flyout instead
       (wired up in site.js): the bar stays one line at any width, and the
       links themselves keep their normal desktop markup and behavior,
       just reflowed into a column panel dropped from the bar. */
    .navbar-toggle {
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* Anchors the flyout panel below to this row rather than to the whole
       page, and stops the toggle button and brand from wrapping now that
       the links themselves are the thing collapsing away. */
    .navbar-inner {
        position: relative;
        flex-wrap: nowrap;
    }

    /* Closed by default; .navbar-links-open (toggled in site.js) switches
       this back to flex. A dropdown panel rather than the links' own inline
       row, so each one gets the full width of the bar to tap rather than
       competing for space on a line. */
    .navbar-links {
        display: none;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        flex-direction: column;
        align-items: stretch;
        gap: 0;
        background: var(--color-primary-darker);
        box-shadow: var(--shadow-md);
        padding: 0.5rem 0;
        /* Below .modal-overlay's 1000, above ordinary page content — this
           only ever needs to sit on top of the page it's opened over. */
        z-index: 900;
    }

    .navbar-links.navbar-links-open {
        display: flex;
    }

    .navbar-links .navbar-link,
    .navbar-links .navbar-user {
        width: 100%;
        padding: 0.75rem 20px;
    }

    .navbar-links .navbar-logout-btn {
        width: 100%;
        margin: 0.25rem 20px;
        text-align: center;
    }
}

/* Inline form feedback — used on any page with an auto-saving or ad-hoc
   form (dashboard, charting's penalty form, etc.) */
.form-status {
    font-size: 0.88rem;
    font-weight: 600;
}

.form-status-error { color: var(--color-danger); }
.form-status-success { color: var(--color-success); }


/* Reaches screen readers but takes no space on screen. For a field whose
   visible context already names it (a second number box beside a labelled
   first, a search box next to its button) but which a screen reader would
   otherwise announce unnamed. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ---- Back links ---- */

/* The small muted link that sits above a page title and returns the visitor
   to wherever they came from (see renderBackLink in site.js). Every page's
   is this one: the admin and coach pages used to wrap theirs in a bordered
   .admin-subnav bar, which never held anything but the back link itself and
   drew each one a shade fainter than the public pages' equivalent. */
.back-link {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    margin-bottom: 1rem;
    color: var(--color-text-muted);
    font-weight: 600;
    font-size: 0.9rem;
    text-decoration: none;
}

.back-link:hover { color: var(--color-primary); }

/* ---- Pill filters ---- */

/* A radio group drawn as pills, for the handful of pages that segment a list
   by one exclusive choice (admin's Teams, the scoreboard, standings). Lives
   here rather than in any one page's stylesheet: it started out copied
   between admin.css and scores.css, and standings would have made three. */
.pill-filter {
    display: flex;
    gap: 0.4rem;
}

.pill-filter label {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.4rem 0.9rem;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-pill);
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--color-text-muted);
    background: var(--color-surface);
    cursor: pointer;
}

.pill-filter input {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
}

.pill-filter input:checked + span {
    color: inherit;
}

.pill-filter label:has(input:checked) {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: white;
}

.pill-filter input:focus-visible {
    outline: none;
}

.pill-filter label:has(input:focus-visible) {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

/* ---- Status pills ---- */

/* Not started / Live / Suspended / Final, on a position or on a whole dual
   match. Here rather than in scores.css because five pages across three
   stylesheets show them and only two of those load that file. The vocabulary
   itself is POSITION_STATUS_TAGS in site.js. */
.status-tag {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0.1rem 0.4rem;
    border-radius: var(--radius-sm);
    font-size: 0.62rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    vertical-align: middle;
}

.status-tag-suspended {
    background: var(--color-warning-bg);
    color: var(--color-warning);
}

.status-tag-not-started {
    background: var(--color-surface-alt);
    color: var(--color-text-muted);
}

.status-tag-live {
    background: var(--color-accent);
    color: var(--color-accent-ink);
}

.status-tag-final {
    background: var(--color-success-bg);
    color: var(--color-success);
}

/* ---- Dual match score cells (see renderDualMatchScore in site.js) ---- */

/* The score itself, wherever a dual match's result is shown next to a
   status pill: the public scores list, the coach schedule, admin's Dual
   Matches. Tabular figures so the numbers line up down a column. */
.score-numbers {
    font-weight: 800;
    font-variant-numeric: tabular-nums;
    color: var(--color-ink);
}

/* .status-tag carries a leading margin for the usual case, where it trails
   a label. In these cells it's a flex item with its own gap instead. */
.schedule-score .status-tag {
    margin-left: 0;
}
