html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ──── MudAlert visibility guard ────
   A browser extension running on at least one dev machine writes an inline
   "display:none" onto any element carrying the .mud-alert class once it sits
   inside a <form>. The alert stays in the DOM and reads as present to bUnit and
   to Blazor — it is simply invisible to the user. Evidence that it is the
   extension and not us: the hide arrives in its own MutationObserver batch of 1,
   ~5ms BEFORE Blazor's render batch; patching setAttribute/style.setProperty in
   page context catches nothing (extensions write from an isolated world); a bare
   hand-injected <div class="mud-alert"> is hidden too, while the same div with
   any other class is not; and the same MudAlert placed outside the form element
   is untouched. Confirmed absent in incognito with extensions disabled.

   An author !important declaration outranks a non-important inline style, so this
   one line restores every MudAlert app-wide. It is safe to be this blunt because
   "flex" is exactly what MudBlazor's own .mud-alert rule declares (verified in
   MudBlazor 9.6.0's MudBlazor.min.css) — this re-asserts the framework default
   rather than imposing a new one, and MudBlazor never sets an inline display of
   its own on the element. The one thing it costs: a MudAlert can no longer be
   hidden by writing display:none at it. Nothing here does that — visibility is
   always an @if around the component — so keep it that way.

   Not a substitute for .bpe-intl-warning in BudgetPlayerEditDialog: that warning
   is a compliance message that must never silently vanish, and a hand-rolled
   class the extension does not filter on is defence in depth if the extension
   ever escalates past inline display (it does not today — tested).

   The real fix is on the machine: allowlist the site in the extension. */
.mud-alert {
    display: flex !important;
}

/* ──── International marker ──── */
/* The globe flagging an athlete whose compensation cannot come from cap money. Blue rather
   than the warning orange it shipped as: the NIL budget grid already spends orange and red on
   money going wrong (over Maximum, under Target, negative remaining), so a permanent fact
   about the athlete wearing the same colour reads as one more problem on the row.

   A literal blue and NOT var(--mud-palette-info): this app's PaletteLight deliberately keeps
   the functional colours neutral and sets Info to the grey #6f7c86, so the token would render
   this grey. PaletteDark's Info *is* blue, but MudThemeProvider is never given IsDarkMode
   (see MainLayout) so the dark palette is currently unreachable — revisit if dark mode ships. */
.intl-marker {
    color: #1976d2;
    font-size: 1rem;
}

/* ──── Latest Eval Stat (LatestEvalStat.razor) ──── */
/* Reusable compact "stat bar" card per evaluation type.
 * Two surface variants: --dark (hero banner) and --light (tab content). */
.eval-stats-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.eval-stat {
    flex: 1 1 170px;
    min-width: 160px;
    max-width: 240px;
    padding: 4px 8px 5px;
    border-radius: 6px;
    border: 1px solid;
    display: flex;
    flex-direction: column;
    gap: 2px;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.eval-stat--dark {
    border-color: rgba(255, 255, 255, 0.18);
    background: rgba(255, 255, 255, 0.06);
    color: #fff;
}

.eval-stat--dark:hover {
    background: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.35);
}

.eval-stat--light {
    border-color: rgba(0, 0, 0, 0.12);
    background: rgba(0, 0, 0, 0.02);
    color: var(--mud-palette-text-primary);
}

.eval-stat--light:hover {
    background: rgba(0, 0, 0, 0.04);
    border-color: rgba(0, 0, 0, 0.2);
}

.eval-stat-header {
    display: flex;
    align-items: center;
    gap: 6px;
    line-height: 1;
}

.eval-stat-label {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 0.6rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.eval-stat--dark .eval-stat-label { color: rgba(255, 255, 255, 0.85); }
.eval-stat--light .eval-stat-label { color: var(--mud-palette-text-secondary); }

.eval-stat-dot {
    flex: 0 0 auto;
    width: 7px;
    height: 7px;
    border-radius: 50%;
}

.eval-stat-score-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 6px;
    line-height: 1;
}

.eval-stat-score {
    display: flex;
    align-items: baseline;
    gap: 3px;
    line-height: 1;
    min-width: 0;
}

.eval-stat-score-value {
    font-size: 1.15rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1;
}

.eval-stat--dark .eval-stat-score-value { color: #fff; }
.eval-stat--light .eval-stat-score-value { color: var(--mud-palette-text-primary); }

/* nowrap because these cards live in a fixed-height hero banner: the suffix used to be "/ 8.0",
   and anything longer wrapped inside .eval-stat-score-row at the 160px floor, growing the card
   until the grade bar was pushed out of the banner. Ellipsis rather than overflow so a long scale
   is truncated instead of escaping the card. */
.eval-stat-score-suffix {
    font-size: 0.65rem;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

.eval-stat--dark .eval-stat-score-suffix { color: rgba(255, 255, 255, 0.55); }
.eval-stat--light .eval-stat-score-suffix { color: var(--mud-palette-text-secondary); }

.eval-stat-trend {
    display: inline-flex;
    align-items: baseline;
    gap: 1px;
    font-size: 0.7rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    flex: 0 0 auto;
    line-height: 1;
}

.eval-stat-trend-up { color: #4caf50; }
.eval-stat-trend-down { color: #f44336; }

.eval-stat--dark .eval-stat-trend-flat { color: rgba(255, 255, 255, 0.55); }
.eval-stat--light .eval-stat-trend-flat { color: var(--mud-palette-text-secondary); }

.eval-stat-bar {
    position: relative;
    height: 4px;
    border-radius: 999px;
    overflow: hidden;
}

.eval-stat--dark .eval-stat-bar { background: rgba(255, 255, 255, 0.18); }
.eval-stat--light .eval-stat-bar { background: rgba(0, 0, 0, 0.1); }

.eval-stat-bar-fill {
    height: 100%;
    border-radius: 999px;
    transition: width 0.3s ease;
}

.eval-stat-tier {
    font-size: 0.78rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    line-height: 1.1;
}

.eval-stat--dark .eval-stat-tier { color: rgba(255, 255, 255, 0.92); }
.eval-stat--light .eval-stat-tier { color: var(--mud-palette-text-primary); }

.eval-stat-empty {
    font-size: 0.75rem;
    font-style: italic;
}

.eval-stat--dark .eval-stat-empty { color: rgba(255, 255, 255, 0.55); }
.eval-stat--light .eval-stat-empty { color: var(--mud-palette-text-secondary); }

/* Stacked current (CUR) / projected (PROJ) rows inside one card. */
.eval-stat-grade-row {
    display: flex;
    align-items: flex-start;
    gap: 6px;
}

.eval-stat-row-label {
    flex: 0 0 30px;
    font-size: 0.55rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    padding-top: 3px;
    line-height: 1;
}

.eval-stat--dark .eval-stat-row-label { color: rgba(255, 255, 255, 0.5); }
.eval-stat--light .eval-stat-row-label { color: var(--mud-palette-text-secondary); }

.eval-stat-row-body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.eval-stat-divider {
    height: 1px;
    margin: 1px 0;
}

.eval-stat--dark .eval-stat-divider { background: rgba(255, 255, 255, 0.12); }
.eval-stat--light .eval-stat-divider { background: rgba(0, 0, 0, 0.08); }

/* ──── Section header: shared style for in-card / in-dialog section labels ──── */
/* Pair Typo="Typo.overline" Color="Color.Secondary" with Class="section-header" to get */
/* a tight uppercase label with a thin divider rule beneath it. Used by PlayerEdit and */
/* the player measurement edit dialog so both surfaces share one section look. */
.section-header {
    display: block;
    padding-bottom: 4px;
    margin-bottom: 8px;
    border-bottom: 1px solid var(--mud-palette-divider);
    letter-spacing: 0.06em;
    font-size: 0.95rem;
    font-weight: 700;
}

/* ──── Field group: a labelled sub-group of inputs inside a section card ──── */
/* For the case where several inputs answer ONE question and their own labels
   cannot say so alone — a Month + Year that together are a term, where "Month"
   beside "Month" beside "Month" tells the reader nothing about which term each
   belongs to. A caption floating above the row does not scope them either: it
   reads as a heading over everything that follows it, so the fields it covers
   are a guess. A fieldset draws the boundary, and its legend notches into the
   border the same way an outlined input carries its own label — so the group
   name and the field names are the same kind of thing on screen.
   Do NOT wrap a lone input in this; its own label already does the job. */
.field-group {
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 4px;
    padding: 2px 8px 8px;
    margin: 0;
    min-width: 0;   /* a fieldset defaults to min-content, which refuses to shrink in a grid */
}

.field-group > legend {
    padding: 0 4px;
    font-size: 0.75rem;
    line-height: 1;
    letter-spacing: 0.02em;
    color: var(--mud-palette-text-secondary);
}

/* The row inside a field group. Every child gets a floor, so when the group is
   narrower than its fields the row BREAKS instead of shrinking them — a Month
   select squeezed to 60px reads "M…" with a clear button, which is worse than
   the same field on a second line. Pair with Wrap="Wrap.Wrap" on the MudStack;
   without it the children overflow the fieldset instead of wrapping.
   Widths are what the content needs: a month name plus its clear + arrow
   buttons (130), a four-digit year (88), a degree name (160 before it grows). */
.field-group-row > .fg-month { flex: 1 1 130px; min-width: 130px; }
.field-group-row > .fg-year  { flex: 0 0 88px;  min-width: 88px; }
.field-group-row > .fg-grow  { flex: 1 1 160px; min-width: 160px; }

/* ──── Broadcast Banner ──── */
/* Fixed banner under the app bar. Below Md the drawer is a temporary overlay,
   so the banner spans full width. At Md+ the drawer is persistent (clipped),
   so shift the banner past the drawer width when it's open to avoid clipping. */
.broadcast-banner {
    position: fixed;
    top: 64px;
    left: 0;
    right: 0;
    z-index: 1100;
}
@media (min-width: 960px) {
    .broadcast-banner.drawer-open {
        left: var(--mud-drawer-width-left, 240px);
    }
}

/* Read-only emulation ("View as") banner — same fixed placement as the broadcast banner. */
.impersonation-banner {
    position: fixed;
    top: 64px;
    left: 0;
    right: 0;
    z-index: 1101;
}
@media (min-width: 960px) {
    .impersonation-banner.drawer-open {
        left: var(--mud-drawer-width-left, 240px);
    }
}

/* When both banners show, drop the broadcast banner below the emulation banner (48px) so they
   don't overlap. DOM order is impersonation-banner then broadcast-banner (both MudLayout children). */
.impersonation-banner ~ .broadcast-banner {
    top: 112px;
}

/* The banners are position:fixed and reserve no layout space, so push the main content down by each
   banner's height to keep page toolbars from being hidden underneath. Offsets = app bar (64px) +
   banner height(s): emulation ≈ 48px, broadcast ≈ 64px. Keeps .pt-16's 64px baseline otherwise. */
.mud-main-content.has-emulation-banner {
    padding-top: 112px !important;
}
.mud-main-content.has-broadcast-banner {
    padding-top: 128px !important;
}
.mud-main-content.has-emulation-banner.has-broadcast-banner {
    padding-top: 176px !important;
}

/* ──── App Tabs: unified tab look across the app (pages + dialogs) ──── */
/* Apply Class="app-tabs" on every MudTabs instance. Edit this block to restyle all tabs. */
.app-tabs .mud-tab {
    color: #333 !important;
    font-weight: 600;
    font-size: 0.78rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    transition: background 0.18s ease, color 0.18s ease;
    min-height: 44px;
}

.app-tabs .mud-tab .mud-icon-root {
    color: inherit !important;
    font-size: 1.1rem;
}

.app-tabs .mud-tab:hover:not(.mud-tab-active) {
    color: #000 !important;
    background: rgba(0, 0, 0, 0.035);
}

.app-tabs .mud-tab.mud-tab-active {
    color: var(--mud-palette-primary) !important;
    background: #1a1a1a !important;
    font-weight: 700;
}

.app-tabs .mud-tab.mud-tab-active .mud-icon-root {
    color: var(--mud-palette-primary) !important;
}

.app-tabs .mud-tab-slider {
    height: 4px !important;
    background: var(--mud-palette-primary) !important;
}

/* Baseline under the tab strip — anchors the strip visually so it doesn't
   blend into the dialog/page body. The active tab's gold slider sits on
   top of this line. */
.app-tabs .mud-tabs-toolbar,
.app-tabs .mud-tabs-tabbar {
    border-bottom: 1px solid var(--mud-palette-divider);
}

/* Faint outline + tiny rounding on inactive tab labels so they read as
   discrete targets instead of plain text. Active tab keeps its dark fill
   and overrides the border via the existing rule above. */
.app-tabs .mud-tab:not(.mud-tab-active) {
    border: 1px solid var(--mud-palette-divider);
    border-bottom: none;
    border-radius: 4px 4px 0 0;
    margin-right: 2px;
}

/* Modifier: lock the content-panel height to the visible viewport so the tab
   strip never shifts AND long-content tabs (like Notes with many entries)
   scroll inside the panel instead of growing the page. Short tabs (like
   Contacts) pad to the same height; the empty space is the trade-off for
   stable strip position. Reserve covers AppBar (64) + MainContent padding
   (80) + hero banner (~250) + action bar (~30) + tab strip (~48) + buffer. */
.app-tabs-expand .mud-tabs-panels {
    height: calc(100vh - 480px);
    min-height: 320px;
    overflow-y: auto;
}

/* ──── Dialog actions wrap ──── */
/* Apply ActionsClass="dialog-actions-wrap" on dialogs that have too many action
   buttons to fit one row on narrow viewports. Buttons wrap to additional rows;
   `mr-auto` still pushes a left-aligned button in the first row. */
.dialog-actions-wrap {
    flex-wrap: wrap !important;
    gap: 6px;
    row-gap: 6px;
}

/* ──── Grade pills: shared across PlayerEvaluationsTab, EvaluationAssignmentProfileDialog,
       FillEvaluation. Tier-aware variants snap a numeric grade to the nearest
       EvaluationGradeTier and surface the tier name; bg/text color set inline. ──── */
.eval-grade-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 54px;
    height: 28px;
    padding: 0 10px;
    border-radius: 4px;
    color: #fff;
    font-weight: 700;
    font-size: 0.95rem;
    line-height: 1;
}

.eval-grade-pill-sm {
    min-width: 40px;
    height: 22px;
    padding: 0 8px;
    border-radius: 11px;
    font-size: 0.8rem;
}

.eval-grade-pill-tier {
    min-width: 150px;
    max-width: 100%;
    height: auto;
    min-height: 24px;
    padding: 3px 12px;
    border-radius: 12px;
    font-size: 0.62rem;
    line-height: 1.1;
    white-space: nowrap;
    text-align: center;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

.eval-grade-pill-unit {
    font-size: 0.7rem;
    font-weight: 500;
    opacity: 0.85;
    margin-left: 4px;
}

.eval-grade-pill-empty {
    background: rgba(255, 255, 255, 0.08);
    color: var(--mud-palette-text-secondary);
}

/* ──── Score badge: pairs with .eval-grade-pill-tier. Carries the numeric
       average that used to live inside the tier pill. Small rounded chip
       with tabular digits and a thin black outline. ──── */
.eval-score-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 32px;
    height: 22px;
    padding: 0 7px;
    margin-left: 6px;
    border-radius: 999px;
    background: var(--mud-palette-surface);
    border: 1.5px solid #000;
    color: var(--mud-palette-text-primary);
    font-weight: 500;
    font-size: 0.78rem;
    line-height: 1;
    letter-spacing: 0.01em;
    font-variant-numeric: tabular-nums;
    flex-shrink: 0;
}

.eval-score-badge-sm {
    min-width: 26px;
    height: 18px;
    padding: 0 5px;
    font-size: 0.7rem;
    border-width: 1px;
}

/* Dark-surface variant (hero banner) — slightly stronger contrast. */
.eval-score-badge-on-dark {
    background: rgba(255, 255, 255, 0.94);
    color: #111;
}

/* ──── Combined grade pill: single pill split into two sections.
       Left section is a colored leading chip carrying the numeric value
       (background = tier color). Right section carries the tier name over
       a neutral white/light background. One element, one accent color. */
.eval-grade-pill-combined {
    display: inline-flex;
    align-items: stretch;
    height: 22px;
    border-radius: 4px;
    overflow: hidden;
    font-size: 0.68rem;
    font-weight: 700;
    line-height: 1;
    white-space: nowrap;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    flex-shrink: 0;
    background: color-mix(in srgb, var(--pill-color, #888) 14%, transparent);
    color: var(--mud-palette-text-primary);
    border: 1px solid color-mix(in srgb, var(--pill-color, #888) 28%, transparent);
}

.eval-grade-pill-combined-lg {
    height: 28px;
    border-radius: 5px;
    font-size: 0.78rem;
}

/* Left chip: tier-colored, holds the numeric value. */
.eval-grade-pill-combined .eval-grade-pill-combined-value {
    display: inline-flex;
    align-items: center;
    padding: 0 8px;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    letter-spacing: 0;
    text-transform: none;
}

/* Right section: neutral, holds the tier name. */
.eval-grade-pill-combined .eval-grade-pill-combined-label {
    display: inline-flex;
    align-items: center;
    padding: 0 10px;
    min-width: 0;
}

/* Numeric-only variant (no tier): single uniform colored pill. */
.eval-grade-pill-combined.eval-grade-pill-combined-numeric {
    background: transparent;
    border: none;
    color: inherit;
}
.eval-grade-pill-combined.eval-grade-pill-combined-numeric .eval-grade-pill-combined-value {
    padding: 0 12px;
}

/* ──── Labeled grade line (Current / Projected) — shared by the player
       evaluation profile dialog and the mobile evaluations tab. Fixed-width
       label keeps both pills left-aligned to the same x position. ──── */
.eval-grade-line {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.eval-grade-line-label {
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
    flex: none;
    width: 80px;
}

/* ──── Paged dialogs (Prev/Next navigation): keep dialog frame size stable
       across records by fixing the content area height; long content scrolls
       inside instead of resizing the whole dialog. Apply via ContentClass
       on any MudDialog whose body steps through records. ──── */
.eval-paged-dialog-content {
    height: 70vh;
    max-height: 70vh;
    overflow-y: auto;
}

    .dialog-actions-wrap > .mud-button-root {
        margin: 0 !important;
    }

/* ──── Evaluation profile dialog body ──── */
/* Keeps PlayerEvaluationProfileDialog at a consistent height as the user pages
   through evaluations with prev/next chevrons — short ones pad to min-height,
   long ones scroll within the dialog instead of growing it. On mobile the
   dialog is full-screen, so the height constraint is removed there. */
.eval-profile-body {
    min-height: 360px;
    max-height: calc(100vh - 280px);
    overflow-y: auto;
}

@media (max-width: 599.95px) {
    .eval-profile-body {
        min-height: 0;
        max-height: none;
        overflow-y: visible;
    }
}

/* ──── Evaluation help-text rich content ──── */
/* Used by EvaluationQuestionRenderer (fill page) and inside MudTooltip overlays
   for the profile dialog. Tooltip popovers render outside scoped CSS, so these
   rules are global. */
.eval-help-rich {
    font-size: 0.875rem;
    line-height: 1.4;
    text-align: left;
}

.eval-help-rich p {
    margin: 0 0 0.4rem 0;
}

.eval-help-rich p:last-child {
    margin-bottom: 0;
}

/* Quill 2.x emits indented list items as flat <li class="ql-indent-N"> siblings, not nested <ol>/<ul>.
   These rules mirror Quill's snow.css counter scheme so guidance renders nested outside the editor.
   Mirrored in EvaluationQuestionRenderer.razor.css for the in-component scope. */
.eval-help-rich ol,
.eval-help-rich ul {
    padding-inline-start: 0;
    margin: 0.25rem 0 0.5rem 0;
    list-style: none;
    counter-reset: list-0 list-1 list-2 list-3;
}

.eval-help-rich li {
    margin-bottom: 0.15rem;
    position: relative;
    padding-left: 1.5em;
}

.eval-help-rich ol > li {
    counter-increment: list-0;
    counter-reset: list-1 list-2 list-3;
}

.eval-help-rich ol > li::before {
    content: counter(list-0, decimal) ". ";
    position: absolute;
    left: 0;
    width: 1.5em;
}

.eval-help-rich ol > li.ql-indent-1 {
    counter-increment: list-1;
    counter-reset: list-2 list-3;
    padding-left: 3em;
}
.eval-help-rich ol > li.ql-indent-1::before {
    content: counter(list-1, lower-alpha) ". ";
    left: 1.5em;
}

.eval-help-rich ol > li.ql-indent-2 {
    counter-increment: list-2;
    counter-reset: list-3;
    padding-left: 4.5em;
}
.eval-help-rich ol > li.ql-indent-2::before {
    content: counter(list-2, lower-roman) ". ";
    left: 3em;
}

.eval-help-rich ol > li.ql-indent-3 {
    counter-increment: list-3;
    padding-left: 6em;
}
.eval-help-rich ol > li.ql-indent-3::before {
    content: counter(list-3, decimal) ". ";
    left: 4.5em;
}

.eval-help-rich ul > li::before {
    content: "\2022";
    position: absolute;
    left: 0;
    width: 1.5em;
}
.eval-help-rich ul > li.ql-indent-1 {
    padding-left: 3em;
}
.eval-help-rich ul > li.ql-indent-1::before {
    content: "\25E6";
    left: 1.5em;
}
.eval-help-rich ul > li.ql-indent-2 {
    padding-left: 4.5em;
}
.eval-help-rich ul > li.ql-indent-2::before {
    content: "\25AA";
    left: 3em;
}
.eval-help-rich ul > li.ql-indent-3 {
    padding-left: 6em;
}
.eval-help-rich ul > li.ql-indent-3::before {
    content: "\2022";
    left: 4.5em;
}

/* ──── ApexCharts tooltip flicker workaround ──── */
/* The default ApexCharts tooltip uses a CSS opacity transition. Under Blazor
   Server, hover-driven repaints can restart that transition mid-frame, producing
   a strobe/flicker loop. Killing the transitions makes the tooltip appear/hide
   discretely with no visual artifacts. */
.apexcharts-tooltip,
.apexcharts-xaxistooltip,
.apexcharts-yaxistooltip {
    transition: none !important;
}

.apexcharts-tooltip.apexcharts-active {
    opacity: 1 !important;
}

/* ──── Fill Evaluation section panels (Grade vs Questions) ──── */
/* Two side-by-side sections inside the per-player form (grade entry above,
   per-player questions below). Both get an outlined panel + colored left
   accent bar so evaluators can scan between them quickly. Grade uses UCF
   gold; questions use a muted slate to signal "supporting detail". */
.fill-eval-section {
    position: relative;
    border-radius: 8px;
    padding: 14px 18px 14px 22px;
    margin-bottom: 16px;
    overflow: hidden;
    border: 1px solid var(--mud-palette-lines-default);
    background: var(--mud-palette-surface);
}

.fill-eval-section::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    width: 5px;
}

.fill-eval-section-header {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 10px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
}

.fill-eval-section-title {
    font-size: 0.95rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    line-height: 1;
    color: var(--mud-palette-text-primary);
}

.fill-eval-section-icon {
    border-radius: 6px;
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.fill-eval-grade-section {
    background: linear-gradient(to right, rgba(188, 144, 26, 0.10), rgba(0, 0, 0, 0) 60%);
    border-color: rgba(188, 144, 26, 0.35);
}
.fill-eval-grade-section::before {
    background: var(--mud-palette-primary);
}
.fill-eval-grade-section .fill-eval-section-icon {
    background: rgba(188, 144, 26, 0.18);
    color: var(--mud-palette-primary);
}
.fill-eval-grade-section .fill-eval-section-header {
    border-bottom-color: rgba(188, 144, 26, 0.35);
}

.fill-eval-questions-section {
    background: linear-gradient(to right, rgba(100, 116, 139, 0.08), rgba(0, 0, 0, 0) 60%);
    border-color: rgba(100, 116, 139, 0.30);
}
.fill-eval-questions-section::before {
    background: #64748b;
}
.fill-eval-questions-section .fill-eval-section-icon {
    background: rgba(100, 116, 139, 0.15);
    color: #475569;
}
.fill-eval-questions-section .fill-eval-section-header {
    border-bottom-color: rgba(100, 116, 139, 0.25);
}

/* Editable inputs inside section panels stay white so they read as "fill me in"
   against the tinted section background. */
.fill-eval-section .mud-input-outlined,
.fill-eval-section .mud-input-filled,
.fill-eval-section .mud-select .mud-input-outlined {
    background-color: #fff !important;
    border-radius: 4px;
}

/* Read-only evaluator guidance blocks let the parent section's tint show
   through, so they sit naturally inside the Questions panel. */
.eval-help-block {
    background-color: transparent !important;
    border-color: var(--mud-palette-lines-default) !important;
}

/* ──── PlayerResultsDrillDownDialog header & mobile fullscreen ──── */
.results-drilldown-header {
    position: relative;
    background: linear-gradient(135deg, var(--mud-palette-primary-hover) 0%, transparent 65%);
    border-bottom: 3px solid var(--mud-palette-primary);
    border-radius: 4px;
}

/* Variable-content scroll area: pin the body to a fixed height so the dialog
   does not jump as the user navigates between players with different evaluator
   counts. Content shorter than the available space simply leaves whitespace;
   longer content scrolls inside the body. */
.results-drilldown-body {
    height: calc(100vh - 280px);
    overflow-y: auto;
}

/* Per-evaluator card Q&A: each row is a 2-column grid with a full-width separator */
.evaluator-breakdown-qa-row {
    display: grid;
    grid-template-columns: minmax(180px, 1fr) 2fr;
    column-gap: 16px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
}

.evaluator-breakdown-qa-row:last-child {
    border-bottom: none;
}

.evaluator-breakdown-qa-question {
    font-weight: 600;
    background: var(--mud-palette-background-gray);
    padding: 8px 12px;
    align-self: stretch;
}

.evaluator-breakdown-qa-answer {
    min-width: 0;
    padding: 8px 0;
}

/* Stack question above answer on narrow widths (e.g. dialog at smaller desktop sizes) */
@media (max-width: 720px) {
    .evaluator-breakdown-qa-row {
        grid-template-columns: 1fr;
        row-gap: 4px;
    }
}

@media (max-width: 599.95px) {
    /* Fullscreen the dialog edge-to-edge on phones */
    .mud-dialog.results-drilldown-dialog {
        max-width: 100% !important;
        width: 100% !important;
        height: 100% !important;
        max-height: 100% !important;
        margin: 0 !important;
        border-radius: 0 !important;
    }

    /* Drop the absolute-centered pager so it doesn't collide with CLOSE */
    .results-drilldown-actions {
        justify-content: space-between !important;
    }
    .results-drilldown-pager {
        position: static !important;
        transform: none !important;
    }

    /* On the full-screen mobile dialog let the body flow naturally */
    .results-drilldown-body {
        height: auto;
        overflow-y: visible;
    }
}

/* ──── Shared select-dialog results container ──── */
/* Used by Player/Agent/HighSchool/CollegiateTeam/Staff Select dialogs.
   min-height communicates "a results table will appear here" before the
   user searches; max-height keeps the dialog compact on desktop and
   viewport-relative on phones so it adapts to portrait/landscape. */
.app-select-results {
    min-height: 360px;
    max-height: calc(100vh - 320px);
    overflow: auto;
    border: 1px solid var(--mud-palette-divider);
    border-radius: 4px;
}

/* Utility: hide a column/cell on phone-width screens. Apply to <th>/<td>
   pairs for low-priority columns inside select-dialog tables. */
.app-select-hide-sm { /* desktop: visible */ }

/* Tall dialog: grow to ~90% of viewport height on desktop so long forms
   don't require page-level scrolling. DialogContent scrolls internally;
   DialogActions stay pinned at the bottom. Mobile (FullScreen) overrides
   these heights with its own 100% rules, so this is a no-op there. */
.mud-dialog.tall-dialog {
    height: 90dvh;
    max-height: 90dvh;
}

/* Note editor: the pa-2 padding is what keeps fields off the dialog edge (padding,
   not margin, so it never overflows). The stray horizontal scrollbar instead comes
   from the Variant.Outlined inputs (autocomplete / date picker / multiline box), whose
   fieldset border spills ~1px past the content box — the input-free note *profile*
   dialog with the same padding has no scrollbar. Contain that spill on the x-axis only;
   padding is untouched and vertical scrolling for long notes still works. */
.note-edit-content {
    overflow-x: hidden;
}

/* Export-column picker: drag/drop visual cues. Rows highlight on hover so
   it's clear they're grabbable; the drop zone shows a thin accent border
   while dragging so users can see where the item will land. */
.export-picker-row {
    cursor: grab;
    background: var(--mud-palette-surface);
    transition: background-color 0.1s ease;
}

    .export-picker-row:hover {
        background: var(--mud-palette-action-default-hover);
    }

    .export-picker-row:active {
        cursor: grabbing;
    }

.mud-drop-zone.mud-drop-zone-drag-block {
    outline: 2px dashed var(--mud-palette-primary);
    outline-offset: -2px;
}

/* For tall dialogs that keep their action row inside DialogContent (because
   the form uses EditForm + ButtonType.Submit), pin it at the bottom of the
   scroll area so users don't have to scroll to reach Save/Cancel. Apply
   `form-actions-sticky` to the row's MudStack. */
.mud-dialog.tall-dialog .form-actions-sticky {
    position: sticky;
    bottom: 0;
    background: var(--mud-palette-surface);
    padding-top: 8px;
    padding-bottom: 4px;
    margin-top: 12px;
    border-top: 1px solid var(--mud-palette-lines-default);
    z-index: 1;
}

/* Mirror of form-actions-sticky for a running-total or summary row that
   should remain visible while the user scrolls through a long list inside
   a tall dialog (e.g. the % allocation total above the budget group rows). */
.mud-dialog.tall-dialog .dialog-summary-sticky {
    position: sticky;
    top: 0;
    background: var(--mud-palette-surface);
    padding-top: 4px;
    padding-bottom: 6px;
    margin-bottom: 8px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
    z-index: 2;
}

/* ─────────────────────────────────────────────────────────────────────────
   Tabbed dialog: viewport-locked flex chain so a dialog whose body is a
   MudTabs keeps its header + tab bar (and, for EditForm dialogs, the sticky
   action row) fixed while ONLY the active tab's table scrolls. Pair with
   `tall-dialog` on the MudDialog for the definite height:
   `Class="tall-dialog tabbed-dialog"`. Whatever sits above the MudTabs inside
   DialogContent (a header row, a Name field, an info alert) stays put because
   only the MudTabs is allowed to grow. Mirrors the CLAUDE.md "Tabbed list
   pages" pattern. References: SecurityRoleProfileDialog, SecurityRoleEditDialog. */
.mud-dialog.tabbed-dialog .mud-dialog-content {
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
}

/* EditForm dialogs render a <form> as the sole content child; it becomes the
   flex parent that the MudTabs grows inside. */
.mud-dialog.tabbed-dialog .mud-dialog-content > form {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.tabbed-dialog .mud-tabs {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.tabbed-dialog .mud-tabs .mud-tabs-panels {
    flex: 1 1 0;
    min-height: 0;
    overflow: hidden;
}

/* MudBlazor 9.6 sets display:contents on the active panel (specificity 0,4,0);
   including .mud-tabs here makes this rule 0,5,0 so the panel stays a real box
   with a bounded height. Without this the grid area collapses and won't scroll. */
.tabbed-dialog .mud-tabs .mud-tabs-panels > .mud-tab-panel.mud-tab-panel-active {
    height: 100%;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* The MudSimpleTable/MudTable inside the active panel owns the scroll (both
   axes); neutralize its own max-height so the panel drives it. A panel may
   also hold fixed-height siblings (e.g. an "Add User" row) — those stay put
   below the scrolling table because only .mud-table is allowed to grow. */
.tabbed-dialog .mud-tab-panel-active .mud-table {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.tabbed-dialog .mud-tab-panel-active .mud-table-container {
    flex: 1 1 0;
    min-height: 0;
    overflow: auto;
    max-height: none;
}

/* Keep the table's own column header pinned while its rows scroll. */
.tabbed-dialog .mud-tab-panel-active .mud-table-container thead th {
    position: sticky;
    top: 0;
    background: var(--mud-palette-surface);
    z-index: 1;
}

/* For a tabbed-dialog panel whose body is NOT a table (e.g. the "Default Tiers"
   list in EvaluationTypeEditDialog), the active panel's overflow:hidden would
   clip the content with no scrollbar. Wrap that panel's body in .eval-tier-scroll
   so it becomes the growing scroll region instead. */
.tabbed-dialog .mud-tab-panel-active .eval-tier-scroll {
    flex: 1 1 0;
    min-height: 0;
    overflow: auto;
}

/* Writeup-type editor Staff Access grid: shrink the row chrome so more roles
   are visible/editable at once. Trim the cell padding and the dense input's
   top margin so each row is driven only by the compact select's height. */
.eval-access-table td,
.eval-access-table th {
    padding-top: 2px;
    padding-bottom: 2px;
}

.eval-access-table td .mud-input-control,
.eval-access-table td .mud-checkbox {
    margin-top: 0;
    margin-bottom: 0;
}

/* ──── Filter rows ────────────────────────────────────────────────────────
   The toolbar of filters above a list grid. Use `.app-filter-row` on the
   wrapper instead of an inline flex style, and keep wrapping each control in
   `<div style="flex:none; width:NNpx;">` (many Mud inputs stretch to 100% of
   their flex parent otherwise).

   Why this exists: MudBlazor gives every dense input a top margin, and call
   sites cancelled it ad hoc with `Class="mt-0"` on some controls but not
   others — so a MudTextField and the MudSelect beside it sat a few pixels
   apart. This zeroes the margin for every control in the row, so alignment no
   longer depends on remembering `mt-0`. Do NOT add `mt-0` to controls in here. */
.app-filter-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-bottom: 8px;
}

/* The second selector is not redundant. MudBlazor gives a LABELLED outlined input extra headroom
   via `.mud-input-control.mud-input-control-margin-dense.mud-input-outlined-with-label`
   (margin-top:8px) — three classes, which outranks the two-class reset above. So an unlabelled
   TextField got zeroed while a labelled Select beside it kept its 8px and sat visibly lower.
   Repeating MudBlazor's own chain under .app-filter-row makes it four classes, which wins. */
.app-filter-row .mud-input-control,
.app-filter-row .mud-input-control.mud-input-control-margin-dense.mud-input-outlined-with-label,
.app-filter-row .mud-input-control.mud-input-outlined-with-label {
    margin-top: 0;
    margin-bottom: 0;
}

/* Switches and buttons have no .mud-input-control box to zero, so they need
   their own reset to share the row's centre line. */
.app-filter-row .mud-switch,
.app-filter-row .mud-button-root {
    margin-top: 0;
    margin-bottom: 0;
}

/* Writeup-type color field: the raw hex string is meaningless to users, so hide
   the picker's input text and keep only the palette activator + adjacent swatch.
   Clicking the field still opens the picker (where the hex is available).
   !important + the compound selector beat MudBlazor's own .mud-input-slot color. */
.color-picker-compact input.mud-input-slot {
    color: transparent !important;
    caret-color: transparent;
    text-shadow: none;
    user-select: none;
    cursor: pointer;
}

/* User-edit Permissions tab: turn each category into a distinct, scannable
   section. A sticky gold header bar (matching the app's primary accent)
   separates groups far more clearly than the old plain subtitle text, and
   the current category stays visible while scrolling the long list. */
.perm-group {
    margin-bottom: 10px;
}

.perm-group-header {
    position: sticky;
    top: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 10px;
    margin-bottom: 2px;
    background: var(--mud-palette-primary);
    color: var(--mud-palette-primary-text);
    border-radius: 4px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
}

.perm-group-title {
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: 0.8rem;
    line-height: 1.2;
}

/* User Profile dialog: aligned two-column (label / value) grid so labels and
   values line up in tidy columns instead of running together on each row. The
   label column sizes to the widest label; values start at a consistent x. */
.user-profile-grid {
    display: grid;
    grid-template-columns: max-content 1fr;
    column-gap: 16px;
    row-gap: 8px;
    align-items: center;
}

.user-profile-grid .upg-label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-weight: 600;
    font-size: 0.875rem;
    white-space: nowrap;
}

.user-profile-grid .upg-value {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    font-size: 0.875rem;
    min-width: 0;
}

.user-profile-grid .upg-none {
    color: var(--mud-palette-text-secondary);
}

.user-profile-grid .upg-mono {
    font-size: 0.8rem;
    word-break: break-all;
}

/* Zebra-stripe the rows within a group and inset them slightly so they read
   as belonging to the header above. */
.perm-group .perm-row {
    padding-left: 8px;
    padding-right: 8px;
    border-radius: 4px;
}

.perm-group .perm-row:nth-of-type(even) {
    background: var(--mud-palette-background-gray);
}

/* Graphical Role default -> Effective indicator on each permission row.
   Green "on" chip = allowed, muted "off" chip = not allowed; the arrow reads
   "role default flows to the user's effective permission". Replaces the old
   "Role default: … · Effective: …" caption text. */
.perm-status {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 2px;
}

.perm-status-chip {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 1px 8px 1px 5px;
    border-radius: 10px;
    line-height: 1.4;
}

.perm-status-chip.on {
    background: rgba(46, 125, 50, 0.14);
    color: #2E7D32;
}

.perm-status-chip.off {
    background: rgba(211, 47, 47, 0.12);
    color: #C62828;
}

.perm-status-arrow {
    opacity: 0.4;
    font-size: 1rem !important;
}

/* Evaluations tab: three-state scope chips (None / Own / All) for the
   Role Setting -> Effective indicator, plus a small muted context label
   preceding each chip. Colors: None = red, Own = amber, All = green. */
.perm-status-ctx {
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--mud-palette-text-secondary);
}

.perm-status-chip.scope-none {
    background: rgba(211, 47, 47, 0.12);
    color: #C62828;
}

.perm-status-chip.scope-own {
    background: rgba(237, 108, 2, 0.14);
    color: #B25E00;
}

.perm-status-chip.scope-all {
    background: rgba(46, 125, 50, 0.14);
    color: #2E7D32;
}

.eval-vis-list .perm-row:nth-of-type(even) {
    background: var(--mud-palette-background-gray);
    border-radius: 4px;
}

.eval-vis-list .perm-row {
    padding-left: 8px;
    padding-right: 8px;
}

/* Edit pages (PlayerEdit, AgentEdit, EvaluationTemplateEdit): full-viewport
   layout. The host fills the viewport (minus the appbar + MudMainContent's
   pa-4), the title is fixed at top, the form scrolls inside
   .player-edit-scroll, and .player-edit-actions stays pinned at the bottom
   regardless of form length. 100dvh is mobile-aware. */
.player-edit-host,
.agent-edit-host,
.template-edit-host {
    display: flex;
    flex-direction: column;
    height: calc(100dvh - 96px);
    min-height: 0;
}

.player-edit-host > .player-edit-title,
.agent-edit-host > .agent-edit-title,
.template-edit-host > .template-edit-title {
    flex: 0 0 auto;
    margin-bottom: 8px;
}

.player-edit-host .player-edit-form,
.agent-edit-host .agent-edit-form,
.template-edit-host .template-edit-form {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.player-edit-host .player-edit-scroll,
.agent-edit-host .agent-edit-scroll,
.template-edit-host .template-edit-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    scrollbar-gutter: stable;
}

.player-edit-host .player-edit-actions,
.agent-edit-host .agent-edit-actions,
.template-edit-host .template-edit-actions {
    flex: 0 0 auto;
    background: var(--mud-palette-background-gray);
    padding: 8px 12px;
    border-top: 1px solid var(--mud-palette-lines-default);
    box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.08);
}

/* Section-scoped edit (e.g. players.edit.academic): the whole form is a read-only host
   whose inputs are non-interactive, except the one .section-editable island which is
   re-enabled. Wheel scrolling still works because the scroll container (.player-edit-scroll)
   is a pointer-events:auto ancestor. Server-side enforcement in PlayerService.UpdateAsync is
   the real guard — this is UX. Dim non-editable sections so read-only is obvious. */
.section-readonly-host {
    pointer-events: none;
}
.section-readonly-host > .mud-grid > .mud-grid-item:not(.section-editable) {
    opacity: 0.65;
}
.section-readonly-host .section-editable {
    pointer-events: auto;
    opacity: 1;
}

@media (max-width: 599.95px) {
    .agent-tab-panel {
        height: auto;
        overflow-y: visible;
    }

    .app-select-results {
        min-height: 280px;
        max-height: 60vh;
    }

    .app-select-hide-sm {
        display: none !important;
    }

    /* AgentEdit Section 1 (Name & Agency): stack avatar above the form fields
       on phones so the form column gets the full width. */
    .agent-edit-namebar {
        flex-direction: column !important;
        align-items: stretch !important;
    }

    .agent-edit-namebar > .agent-edit-namebar-avatar {
        width: 100% !important;
    }
}

/* ──── Agent Profile Hero ──── */
.agent-hero {
    background: linear-gradient(135deg, #1a1a1a 0%, #2c2c2c 60%, #3a2e0a 100%);
    border: 1px solid rgba(186, 154, 87, 0.35);
    border-radius: 8px;
    padding: 12px 14px;
    color: #fff;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.agent-hero-main {
    display: flex;
    align-items: center;
    gap: 12px;
}

.agent-hero-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 0.65rem;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--mud-palette-primary);
    margin-bottom: 1px;
}

.agent-hero-eyebrow .agent-hero-dot {
    color: rgba(255, 255, 255, 0.4);
    margin: 0 2px;
}

.agent-hero-quickline {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 14px;
    margin-top: 4px;
    font-size: 0.78rem;
    color: rgba(255, 255, 255, 0.85);
}

.agent-hero-link,
.agent-hero-meta {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.agent-hero-link {
    color: rgba(255, 255, 255, 0.92);
    text-decoration: none;
    border-bottom: 1px dotted rgba(255, 255, 255, 0.35);
}

.agent-hero-link:hover {
    color: var(--mud-palette-primary);
    border-bottom-color: var(--mud-palette-primary);
}

/* ──── Shared select wrappers ──── */
/* LookupSelect, LookupMultiSelect, UserSelect, UserMultiSelect all wrap a
   MudAutocomplete. Multi-select variants hijack the floating label to display
   the current selection (see LookupMultiSelect.razor lines 119-138), so a long
   pick like "Agent Communication" or "Player Category (All)" can collide with
   the dropdown chevron on narrower fields. Clip the floating label and the
   input value just shy of the chevron so text ellipsizes cleanly.

   Buffer math: MudBlazor's .mud-input-label-outlined offsets the label
   transform(14px, …) from the container's left edge, so max-width:calc(100%-X)
   leaves a right margin of (X − 14)px. To clear a ~32px chevron with a few
   pixels of breathing room we need X ≈ 50px. */
.app-shared-select .mud-input-label,
.mud-autocomplete .mud-input-label-outlined {
    max-width: calc(100% - 50px) !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
}

.app-shared-select input.mud-input-slot {
    text-overflow: ellipsis;
}

/* ──── Compact data-grid pager footer ──── */
/* MudBlazor ships the pager toolbar at 52px tall on desktop. The dense-UI
   convention used throughout this app prefers tighter bars, so we shorten
   it to 40px. The mobile breakpoint (<960px) ships its own min-height:100px
   variant for wrapped controls — leave that alone. */
@media (min-width: 960px) {
    .mud-table-pagination-toolbar {
        height: 40px;
        min-height: 40px;
    }
}


/* ──── DynamicColumnGrid: size the table to its columns ────
   MudBlazor sets `.mud-table-root { width: 100% }`, and in an auto-layout table `width`/`max-width`
   on a cell are hints rather than rules — so when the columns total less than the container the
   browser stretches every one of them to fill it, and the grid reads as a row of half-empty
   columns. DynamicColumnGrid sizes every column explicitly (see DefaultWidth), so those widths are
   the intended sizes and the table should be exactly as wide as they add up to. Leftover space is
   left as empty area to the right of the table rather than padded into the columns.

   Scoped to .dcg-fit so ordinary MudTable/MudDataGrid instances, whose columns are meant to share
   the full width, are unaffected. */
.dcg-fit .mud-table-root {
    width: auto;
}

/* ──── DynamicColumnGrid: give the leftover width to one column ────
   The alternative to .dcg-fit, applied when a catalog column declares FillTo. Instead of leaving
   the slack beside the table, the table fills its container and exactly one column grows into the
   difference, up to a cap; .dcg-spacer takes whatever is left past that cap. The two classes are
   mutually exclusive — DynamicColumnGrid.GridClass picks one.

   `table-layout: fixed` is what makes this possible: it is the only mode in which a declared column
   width is authoritative. It also decides the two edge cases correctly. When the columns total MORE
   than the container the table overflows and scrolls at its natural width rather than squashing the
   columns (measured: an 887px container held all declared widths and scrolled 589px), so a crowded
   view behaves exactly as it does under .dcg-fit. */
.dcg-grow {
    /* Queried by the fill stops DynamicColumnGrid emits. It sits on the grid root — not on
       .mud-table-container — so it stays off the scroll container and cannot disturb FixedHeader's
       sticky positioning. The grid root's width comes from the page's flex chain and never from its
       own content, so nothing the columns do can feed back into what the stops measure. */
    container-type: inline-size;
    container-name: dcg;
}

.dcg-grow .mud-table-root {
    width: 100%;
    table-layout: fixed;
}

/* Structural, not visual: the spacer exists only to swallow the width left over once the fill
   column reaches its cap. Left alone it reads as a bordered empty column on the end of every row.
   The row's own striping still shows through, so rows stay continuous to the table's edge. */
.dcg-grow .dcg-spacer {
    border: 0;
    padding: 0;
}

/* ──── DynamicColumnGrid: header label alignment ────
   MudBlazor lays the heading out inside `.column-header`, a flex row with
   `justify-content: space-between`, so `text-align` on the cell does not position it. A sortable
   column gets away with it because its label lives in a `.sortable-column-header` span that is
   `width: 100%`; an unsortable one has no such span and its label shrink-wraps at flex-start.
   Without these rules a heading lines up with its data only when the column happens to be
   sortable — e.g. Eligibility (unsortable on Athletes, sortable on Roster) would sit left on one
   page and centred on the other. */
.dcg-h-center .column-header {
    justify-content: center;
}

.dcg-h-right .column-header {
    justify-content: flex-end;
}
