html, body {
    margin: 0;
    font-family: -apple-system, 'Segoe UI', Roboto, Inter, sans-serif;
}

h1:focus {
    outline: none;
}

.app-loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100vh;
    gap: 0.5rem;
    background-color: #FAF7F0;
}

.app-loading-logo {
    font-size: 1.5rem;
    font-weight: 600;
    color: #1E6B45;
}

.app-loading-text {
    color: #5F6B64;
}

#blazor-error-ui {
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}

/* ---- Design tokens not covered by the MudBlazor palette ----
   docs/design.md, "Palette": the three lineup-grid cell tints and the clay
   border that marks the battery (pitcher/catcher) cells. [data-theme] is set
   by Services/ThemeService.cs; it defaults to light when JS hasn't run yet
   (prerendering, or before first paint), matching the light values below. */
:root {
    --cell-infield: #E4EFE8;
    --cell-outfield: #E8EDF5;
    --cell-bench: #F1EDE4;
    --cell-clay: #B8562A;
    /* docs/design.md, "Palette": the season balance bar's outfield segment —
       a mid blue, since the pale --cell-outfield tint disappears at 8 px tall. */
    --balance-outfield: #4A6FA5;
    /* A lock glyph, as a mask rather than the Unicode emoji it replaces: the
       coloured emoji glyph most platforms render for U+1F512 ignores the CSS
       `color` property entirely, which is no good for "warning colour, 14 px"
       (design review, fix 3). Material Design's "lock" icon path. */
    --icon-lock: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zM9 6c0-1.66 1.34-3 3-3s3 1.34 3 3v2H9V6zm9 14H6V10h12v10zm-6-3c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z'/></svg>");
}

:root[data-theme="dark"] {
    --cell-infield: #213A2C;
    --cell-outfield: #1F2A3A;
    --cell-bench: #2A2A24;
    --cell-clay: #E07A48;
    --balance-outfield: #7A9CD1;
}

/* Position-group tints for the lineup grid (docs/design.md, "Screens" > "3. Game screen").
   Infield reads darkest and bench lightest so the three still separate in greyscale. */
.cell-infield {
    background-color: var(--cell-infield);
}

.cell-outfield {
    background-color: var(--cell-outfield);
}

.cell-bench {
    background-color: var(--cell-bench);
}

/* Pitcher and catcher get a clay left border instead of their own fill, so the
   infield tint stays consistent across the group (docs/design.md, "Palette"). */
.cell-battery {
    border-left: 3px solid var(--cell-clay);
}

/* Uniform numbers and every count line up on tabular numerals (docs/design.md, "Type"). */
.tabular-nums {
    font-variant-numeric: tabular-nums;
}

.fairplay-mark {
    display: inline-flex;
    flex-shrink: 0;
}

/* Layout/MainLayout.razor's app bar (design review, fix 5): below 768 px, once
   a team is selected, its name and season replace the "FairPlay" wordmark —
   there is no room for both, and the brief asks for the team to always be
   visible. At 768 px and wider both show, the same threshold every other
   tablet layout in this app uses. */
.fairplay-appbar-brand {
    display: none;
}

.fairplay-appbar-team-desktop {
    display: none;
}

@media (min-width: 768px) {
    .fairplay-appbar-brand {
        display: inline;
    }

    .fairplay-appbar-team-mobile {
        display: none;
    }

    .fairplay-appbar-team-desktop {
        display: flex;
    }
}

.fairplay-nav-disabled {
    opacity: 0.5;
    pointer-events: none;
}

/* Shared/RulesEditor.razor: a plain-language list of rules, one row per
   parameter, each with a compact inline field so the value stays editable in
   place (docs/design.md, wizard step 4). */
.fairplay-rule-row {
    gap: 1rem;
}

.fairplay-rule-field {
    max-width: 110px;
    flex-shrink: 0;
}

/* Shared/RuleSelectField.razor: an enum row's value is a plain-language
   option ("Not allowed by the division"), not a number, so it needs the rest
   of the row rather than the number fields' 110px - flex-grow fills from the
   end of the label to the row's right edge at phone and desktop widths alike
   (#51), and min-width: 0 lets it actually shrink there instead of
   overflowing, since a flex item's default min-width is its content size. */
.fairplay-rule-select {
    flex: 1 1 auto;
    min-width: 0;
}

/* Shared/SeasonBalanceBar.razor: the three-segment infield/outfield/bench bar
   (docs/design.md, "2. Roster" and principle 2, "Fairness is visible"). 8 px
   tall with three clearly distinct fills — the pale --cell-* grid tints wash
   out at this height, so the bar uses its own, more saturated set: the primary
   green at full strength, --balance-outfield (a mid blue), and the muted text
   colour for bench. Infield darkest, blue mid, grey lightest — the greyscale
   order docs/design.md's palette asks every cell tint to keep. */
.fairplay-balance-bar {
    flex: 1 1 80px;
    min-width: 60px;
    height: 8px;
    border-radius: 4px;
    overflow: hidden;
    display: flex;
    border: 1px solid var(--cell-bench);
}

.fairplay-balance-bar > div {
    height: 100%;
}

.fairplay-balance-bar .balance-infield {
    background-color: var(--mud-palette-primary);
}

.fairplay-balance-bar .balance-outfield {
    background-color: var(--balance-outfield);
}

.fairplay-balance-bar .balance-bench {
    background-color: var(--mud-palette-text-secondary);
}

/* Pages/TeamDetail/Roster.razor: a table at 768 px and wider, a card list
   below it (docs/design.md, "2. Roster": "A table on web, a card list on
   mobile", and CLAUDE.md's Tablets section — the same 768 px threshold every
   tablet layout in this app uses). */
.fairplay-roster-table {
    display: none;
}

.fairplay-roster-cards {
    display: flex;
}

@media (min-width: 768px) {
    .fairplay-roster-table {
        display: block;
    }

    /* !important: the cards div also carries MudBlazor's own `d-flex` utility
       class, which is itself `display: flex !important` — without matching
       it here, that beats this media query outright and the cards render
       underneath the table at every width instead of only below 768 px. */
    .fairplay-roster-cards {
        display: none !important;
    }
}

.fairplay-roster-cards .mud-paper {
    cursor: pointer;
}

.fairplay-roster-table .mud-table-row {
    cursor: pointer;
}

/* Pages/TeamDetail/PlayerDetail.razor: one ranked preference row — draggable
   on a pointer device, the up/down/remove buttons always present as the
   fallback (docs/design.md, "2. Roster"). Never shorter than the 44 px
   minimum touch target (docs/design.md, principle 4). */
.fairplay-pref-row {
    min-height: 44px;
    border: 1px solid var(--cell-bench);
    border-radius: 8px;
}

.fairplay-pref-row[draggable="true"] {
    cursor: grab;
}

/* ---- The lineup card (docs/design.md, "3. Game screen") ----
   A plain table, not a MudTable: the sticky first column, the per-cell tints,
   and the 2 px border down the current inning are all things a component would
   have to be fought for, and the mockups render exactly this markup. */
.fairplay-lineup-wrap {
    position: relative;
    overflow-x: auto;
    border-top: 1px solid var(--cell-bench);
    border-bottom: 1px solid var(--cell-bench);
    -webkit-overflow-scrolling: touch;
}

.fairplay-lineup {
    border-collapse: collapse;
    width: max-content;
    font-variant-numeric: tabular-nums;
}

.fairplay-lineup th,
.fairplay-lineup td {
    border: 1px solid var(--cell-bench);
    text-align: center;
    min-width: 52px;
    /* docs/design.md, principle 4: no row is ever shorter than the 44 px touch target. */
    height: 44px;
    padding: 0;
    position: relative;
}

.fairplay-lineup thead th {
    background-color: var(--mud-palette-surface);
    font-size: 13px;
    font-weight: 700;
    color: var(--mud-palette-text-secondary);
}

/* The current inning: a primary header and a 2 px primary border down the whole
   column, drawn as an inset shadow so it doesn't shift the grid by two pixels. */
.fairplay-lineup thead th.inning-current {
    background-color: var(--mud-palette-primary);
    color: var(--mud-palette-primary-text);
}

.fairplay-lineup .cell-current {
    box-shadow: inset 2px 0 0 var(--mud-palette-primary), inset -2px 0 0 var(--mud-palette-primary);
}

.fairplay-lineup .lineup-player,
.fairplay-lineup .lineup-player-head {
    position: sticky;
    left: 0;
    z-index: 2;
    background-color: var(--mud-palette-surface);
    text-align: left;
    padding: 0 8px;
    min-width: 72px;
    box-shadow: 2px 0 4px rgba(0, 0, 0, 0.10);
}

/* At 768 px and wider every inning fits, so nothing scrolls and the first
   column has no reason to float (CLAUDE.md, "Tablets" — the same threshold as
   the roster table). */
@media (min-width: 768px) {
    .fairplay-lineup-wrap {
        overflow-x: visible;
    }

    .fairplay-lineup {
        width: 100%;
    }

    .fairplay-lineup .lineup-player,
    .fairplay-lineup .lineup-player-head {
        position: static;
        box-shadow: none;
        min-width: 140px;
    }
}

.fairplay-lineup .lineup-number {
    font-weight: 700;
    font-size: 16px;
}

/* The name rides along under the number from 768 px up; below that the number
   is the identity and the name lives in the cell sheet (docs/design.md,
   principle 5: "Names are optional; numbers are not"). */
.fairplay-lineup .lineup-name {
    display: none;
    font-size: 13px;
    color: var(--mud-palette-text-secondary);
}

@media (min-width: 768px) {
    .fairplay-lineup .lineup-name {
        display: block;
    }
}

.fairplay-lineup .lineup-cell {
    font-size: 14px;
    font-weight: 700;
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
    /* A long-press has to be a pin, not the browser's own text-selection or
       callout gesture. */
    -webkit-touch-callout: none;
    touch-action: manipulation;
}

/* The tap target fills its cell, so the tint the manager sees and the button
   they hit are the same rectangle. A button, not a div with a click handler,
   because the grid has to be reachable from a keyboard. */
.fairplay-lineup .lineup-cell-button {
    width: 100%;
    height: 100%;
    min-height: 44px;
    border: none;
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: 14px;
    font-weight: 700;
    cursor: pointer;
    padding: 0;
}

.fairplay-lineup .lineup-cell-button:disabled {
    cursor: default;
}

.fairplay-lineup .lineup-cell-button:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: -2px;
}

.fairplay-lineup .cell-empty {
    background-color: transparent;
}

/* Inside the grid the clay battery marker is drawn as a pseudo-element rather
   than the shared .cell-battery border: a 3 px border under border-collapse
   replaces the 1 px gridline and shifts the whole column by two pixels, and the
   current-inning column already owns this cell's box-shadow. Inset from the
   cell's own edge (design review: drawn flush on the outer left edge, inning 1's
   marker read as belonging to the sticky name column instead of the cell) and
   padded top and bottom so it reads as a mark inside the cell at every column,
   not a border shared with whatever sits to its left. */
.fairplay-lineup .cell-battery {
    border-left-width: 1px;
}

.fairplay-lineup .cell-battery::before {
    content: "";
    position: absolute;
    left: 3px;
    top: 6px;
    bottom: 6px;
    width: 3px;
    border-radius: 2px;
    background-color: var(--cell-clay);
}

/* A right-edge fade shows a phone that the grid has more to scroll to, and
   disappears once it's scrolled all the way there (design review, fix 6) —
   wwwroot/js/app.js's watchScrollEnd toggles .at-scroll-end on the wrap. Not
   shown at 768 px and wider, where every inning already fits with no scroll. */
.fairplay-lineup-fade {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 28px;
    pointer-events: none;
    background: linear-gradient(to right, transparent, var(--mud-palette-background) 85%);
    opacity: 1;
    transition: opacity 150ms ease;
}

.fairplay-lineup-wrap.at-scroll-end .fairplay-lineup-fade {
    opacity: 0;
}

@media (min-width: 768px) {
    .fairplay-lineup-fade {
        display: none;
    }
}

/* An inning a player was not there for is hatched and empty — a hole with a
   reason, distinct from a cell nobody has filled in yet. */
.fairplay-lineup .cell-absent {
    background: repeating-linear-gradient(45deg,
        var(--cell-bench), var(--cell-bench) 5px,
        var(--mud-palette-background) 5px, var(--mud-palette-background) 10px);
    cursor: default;
}

/* Design review: a lock glyph on every cell was noisy. The only lock left is
   this one — pinned cells alone, warning colour so it reads as a real signal
   rather than chrome, inset from the corner so it never crowds the position
   letters. Pinning itself is long-press only, on every device (a mouse fires
   pointerdown too), so there is no separate corner control to draw here. */
.fairplay-lineup .cell-pinned::after {
    content: "";
    position: absolute;
    top: 2px;
    right: 2px;
    width: 14px;
    height: 14px;
    background-color: var(--mud-palette-warning);
    -webkit-mask: var(--icon-lock) center / contain no-repeat;
    mask: var(--icon-lock) center / contain no-repeat;
}

/* The changed cell pulses once after a recalculate (docs/design.md: "The
   changed cell pulses once"), so a manager sees which one moved in a grid that
   re-solved around it. */
@keyframes fairplay-cell-pulse {
    0% { box-shadow: inset 0 0 0 3px var(--cell-clay); }
    70% { box-shadow: inset 0 0 0 3px var(--cell-clay); }
    100% { box-shadow: inset 0 0 0 0 transparent; }
}

.fairplay-lineup .cell-pulse {
    animation: fairplay-cell-pulse 900ms ease-out 1;
}

@media (prefers-reduced-motion: reduce) {
    .fairplay-lineup .cell-pulse {
        animation: none;
        box-shadow: inset 0 0 0 3px var(--cell-clay);
    }
}

/* ---- Bottom sheet ----
   docs/design.md's "Mobile differences" allows bottom sheets instead of
   dialogs, and its "Tablets" section asks for dialogs again at 768 px and
   wider. One panel, two media queries: no breakpoint service, nothing that
   needs JS to decide, and it is correct before first paint. */
.fairplay-sheet-scrim {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    z-index: 1400;
}

.fairplay-sheet {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1401;
    max-height: 85vh;
    overflow-y: auto;
    border-radius: 16px 16px 0 0;
}

.fairplay-sheet-handle {
    width: 36px;
    height: 4px;
    border-radius: 2px;
    background-color: var(--cell-bench);
    margin: 0 auto 12px;
}

@media (min-width: 768px) {
    .fairplay-sheet {
        left: 50%;
        right: auto;
        top: 50%;
        bottom: auto;
        transform: translate(-50%, -50%);
        width: min(560px, 92vw);
        border-radius: 8px;
    }

    .fairplay-sheet-handle {
        display: none;
    }
}

/* One row of the cell sheet: the position, then who holds it right now. */
/* MudBlazor's reset gives every button `display:flex; justify-content:center`,
   so text-align alone leaves the row centred — the alignment has to be said in
   flex terms or the position letters float in the middle of the sheet. */
.fairplay-sheet-row {
    min-height: 44px;
    width: 100%;
    border: none;
    border-bottom: 1px solid var(--cell-bench);
    background: transparent;
    color: inherit;
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 10px;
    text-align: left;
    cursor: pointer;
    padding: 6px 8px;
    font-size: 14px;
    font-family: inherit;
}

.fairplay-sheet-row:disabled {
    cursor: default;
    opacity: 0.55;
}

/* The tint is the shared .cell-infield/.cell-outfield/.cell-bench classes the
   grid itself uses (LineupGrid.PositionTintClass) — the same rule in one place,
   so the sheet's letters sit on the same tints the grid's cells do (design
   review, fix 7: "the sheet and grid read as one thing"). */
.fairplay-sheet-row .sheet-position {
    font-weight: 700;
    min-width: 36px;
    text-align: center;
    padding: 3px 6px;
    border-radius: 4px;
    display: inline-block;
    font-variant-numeric: tabular-nums;
    position: relative;
}

.fairplay-sheet-row .sheet-position.cell-battery {
    border-left: 3px solid var(--cell-clay);
}

.fairplay-sheet-row.sheet-row-current {
    background-color: var(--cell-infield);
    border-radius: 8px;
}

/* One game on the games list. */
.fairplay-game-card {
    cursor: pointer;
}

/* ---- Reports (docs/design.md, "4. Reports") ----
   The team table at 768 px and wider, a card list below it — the same
   threshold and the same reasoning as the roster's table/cards split. */
.fairplay-report-table {
    display: none;
}

.fairplay-report-cards {
    display: flex;
}

@media (min-width: 768px) {
    .fairplay-report-table {
        display: block;
        overflow-x: auto;
    }

    /* !important: the cards div also carries MudBlazor's own `d-flex` utility
       class, which is itself `display: flex !important` — without matching
       it here, that beats this media query outright and the cards render
       underneath the table at every width instead of only below 768 px. */
    .fairplay-report-cards {
        display: none !important;
    }
}

.fairplay-report-card {
    cursor: pointer;
}

table.report {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
}

table.report th {
    text-align: left;
    font-size: 12px;
    color: var(--mud-palette-text-secondary);
    border-bottom: 2px solid var(--cell-bench);
    padding: 8px 10px;
    white-space: nowrap;
}

table.report td {
    padding: 8px 10px;
    border-bottom: 1px solid var(--cell-bench);
    white-space: nowrap;
}

table.report tbody tr {
    cursor: pointer;
}

table.report tbody tr:hover {
    background-color: var(--cell-bench);
}

.report .tnum {
    font-variant-numeric: tabular-nums;
}

/* Every column sorts (docs/design.md: "Sortable by any column") through a
   plain button in the header, backed by Services/ReportsDisplay.Sort — a pure
   function with its own tests, not MudTable's built-in sort. */
.fairplay-report-sort {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    border: none;
    background: transparent;
    color: inherit;
    font: inherit;
    font-weight: 700;
    font-size: 12px;
    cursor: pointer;
    padding: 0;
}

.fairplay-report-sort svg {
    width: 14px;
    height: 14px;
}

.fairplay-report-stat {
    min-width: 64px;
}

/* The per-player report's heat row: darker is more (docs/design.md, "4. Reports"),
   rgba(--mud-palette-primary-rgb, intensity) computed by Services/ReportsDisplay.HeatIntensity. */
.fairplay-heat-row {
    display: flex;
    gap: 6px;
}

.fairplay-heat-cell {
    flex: 1;
    text-align: center;
    padding: 10px 0;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 700;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.fairplay-report-game {
    border-bottom: 1px solid var(--cell-bench);
    padding: 10px 0;
}

/* One inning's position letter on a player's game log, tinted the same way the
   grid cell would be (Services/ReportsDisplay.PositionStringTokens) — a dash
   for bench or an unrecorded inning gets no tint at all. */
.fairplay-position-token {
    display: inline-block;
    min-width: 28px;
    text-align: center;
    padding: 2px 4px;
    border-radius: 4px;
    font-size: 13px;
    font-weight: 700;
}

.fairplay-position-token.cell-battery {
    border-left: 3px solid var(--cell-clay);
}

