/* =============================================================================
   RefReflect theme — the design-token layer.

   WHY THIS FILE EXISTS
   --------------------
   Every page in this app is built from the same Tabler component vocabulary
   (~640 `.card`, ~520 `.card-header`, Tabler buttons/tables/badges throughout).
   That means the look of all 650+ templates is decided by Tabler's CSS
   variables — so retheming the app is a matter of redefining those variables
   once, here, rather than editing templates.

   THE RULE: colour, radius, shadow, spacing and type belong to a token.
   Do NOT add a hex colour to a template. If you need a shade that isn't here,
   add it here as a token and use the token. The app previously accumulated 249
   distinct hard-coded hex colours across templates (three different design
   systems' palettes mixed together) — that is what this file exists to end.

   LOAD ORDER: Tabler first, then this file. Overrides below rely on that.

   Light mode only, deliberately (Jack, 2026-08-31). The tokens are structured
   so a dark palette can be added later as a `[data-bs-theme="dark"]` block
   without restating any component rule.
   ============================================================================= */

:root {
  /* ---------------------------------------------------------------------
     BRAND RAMP — the existing product blue, extended into a real scale.
     `--rr-blue-600` is Tabler's original #206bc4, kept exactly so the app
     stays recognisable to existing users. The rest of the ramp is derived
     from it so tints/shades finally agree with each other.
     --------------------------------------------------------------------- */
  --rr-blue-50:  #eff5fc;
  --rr-blue-100: #dbe8f8;
  --rr-blue-200: #b9d2f1;
  --rr-blue-300: #8ab4e7;
  --rr-blue-400: #5590d8;
  --rr-blue-500: #2f76cc;
  --rr-blue-600: #206bc4;
  --rr-blue-700: #1a56a0;
  --rr-blue-800: #17477f;
  --rr-blue-900: #133a68;

  /* PANEL HEADER — the full-bleed hero block on the finance and performance
     pages (`.appointment-info`, `.report-header` and friends).

     This is the app's one deliberate departure from the blue ramp above, kept
     at Jack's request (2026-09-01): the violet gradient is the treatment he
     wants on those panels, and a flat fill lost the depth they had. Every one
     of the 26 sites points at this token, so changing the treatment is a
     one-line edit here rather than a sweep. */
  --rr-panel-header-bg: linear-gradient(135deg, #667eea 0%, #764ba2 100%);

  /* ---------------------------------------------------------------------
     NEUTRALS — one cool-grey ramp. Replaces the Bootstrap-4 greys
     (#6c757d, #dee2e6, #e9ecef), the Tailwind greys (#6b7280, #374151,
     #1f2937) and the ad-hoc ones (#e0e0e0, #fefefe) that were all in use
     on different pages at the same time.
     --------------------------------------------------------------------- */
  --rr-grey-25:  #fbfcfd;
  --rr-grey-50:  #f7f8fa;
  --rr-grey-100: #eff1f5;
  --rr-grey-200: #e4e8ee;
  --rr-grey-300: #cfd6e0;
  --rr-grey-400: #9aa5b5;
  --rr-grey-500: #6b7889;
  --rr-grey-600: #52606f;
  --rr-grey-700: #3d4855;
  --rr-grey-800: #2a323c;
  --rr-grey-900: #182433;

  /* ---------------------------------------------------------------------
     SEMANTIC STATUS — muted, modern versions of the traffic-light colours.
     The old #28a745 / #dc3545 / #ffc107 (Bootstrap 4) read as loud and dated
     next to everything else; these keep the same meanings at lower chroma.
     `warning` doubles as the "outstanding / needs attention" colour, matching
     how amber is already used in the dashboards.
     --------------------------------------------------------------------- */
  --rr-success: #1a7f56;
  --rr-success-bg: #e8f6f0;
  --rr-warning: #b5730c;
  --rr-warning-bg: #fdf4e3;
  /* The hairline that pairs with the amber fill. Distinct on purpose: the fill
     and the border are two different jobs, and mapping both onto -bg (which the
     first colour sweep did) leaves a border that is invisible against its own
     box. */
  --rr-warning-border: #f0d9ab;
  --rr-danger:  #c8352f;
  --rr-danger-bg: #fdecea;
  --rr-danger-border: #f5c4c1;
  /* One rung between danger and warning, for the 5-point performance ramp —
     red / orange / amber / teal / green. Without it steps 2 and 3 of that
     scale are the same colour and the scale reads as 4 steps. */
  --rr-orange: #c0561f;
  --rr-info:    var(--rr-blue-600);
  --rr-info-bg: var(--rr-blue-50);

  /* ---------------------------------------------------------------------
     ELEVATION — layered, low-opacity shadows. Tabler's default single
     `0 .5rem 1rem rgba(0,0,0,.15)` is the harsh drop-shadow that dates the
     UI most visibly; real depth comes from a tight contact shadow plus a
     wider ambient one.
     --------------------------------------------------------------------- */
  --rr-shadow-xs: 0 1px 2px rgba(24, 36, 51, 0.05);
  --rr-shadow-sm: 0 1px 2px rgba(24, 36, 51, 0.06), 0 1px 3px rgba(24, 36, 51, 0.04);
  --rr-shadow-md: 0 2px 4px rgba(24, 36, 51, 0.05), 0 4px 12px rgba(24, 36, 51, 0.07);
  --rr-shadow-lg: 0 4px 8px rgba(24, 36, 51, 0.05), 0 12px 28px rgba(24, 36, 51, 0.10);
  --rr-ring: 0 0 0 3px rgba(32, 107, 196, 0.20);

  /* RADIUS — the single loudest "modern vs 2019" signal. Tabler ships 4px. */
  --rr-radius-sm: 6px;
  --rr-radius:    8px;
  --rr-radius-lg: 12px;
  --rr-radius-xl: 16px;

  /* ---------------------------------------------------------------------
     ORGANISATION BRAND COLOURS

     These are Football Australia's own palette, used on the media and analytics
     pages for charts and hero panels — NOT the product UI colours above. They
     were hardcoded across five templates; centralised here so they can be
     changed in one place.

     Note for white-labelling: the app resolves names and logos through
     `AppSettings.organization_name` / `ORGANIZATION_LOGO_URL`, but these colours
     are still fixed. Making them configurable is a separate change; this at
     least puts them in one place to change rather than five.
     --------------------------------------------------------------------- */
  --rr-brand-green: #00843D;
  --rr-brand-green-dark: #006430;
  --rr-brand-gold: #FFD100;
  --rr-brand-navy: #002B5C;

  /* SPACING RHYTHM */
  --rr-space-1: 0.25rem;
  --rr-space-2: 0.5rem;
  --rr-space-3: 0.75rem;
  --rr-space-4: 1rem;
  --rr-space-5: 1.5rem;
  --rr-space-6: 2rem;

  /* =====================================================================
     TABLER VARIABLE MAPPING
     Everything above is wired into Tabler here. This is the part that
     retheme every template at once.
     ===================================================================== */
  --tblr-primary: var(--rr-blue-600);
  --tblr-primary-rgb: 32, 107, 196;
  --tblr-blue: var(--rr-blue-600);
  --tblr-link-color: var(--rr-blue-700);
  --tblr-link-hover-color: var(--rr-blue-800);

  --tblr-body-bg: var(--rr-grey-50);
  --tblr-body-color: var(--rr-grey-900);
  --tblr-bg-surface: #ffffff;
  --tblr-bg-surface-secondary: var(--rr-grey-50);
  --tblr-bg-surface-tertiary: var(--rr-grey-100);
  --tblr-border-color: var(--rr-grey-200);
  --tblr-border-color-translucent: var(--rr-grey-200);
  --tblr-secondary: var(--rr-grey-500);
  --tblr-muted: var(--rr-grey-500);

  --tblr-success: var(--rr-success);
  --tblr-warning: var(--rr-warning);
  --tblr-danger: var(--rr-danger);
  --tblr-info: var(--rr-info);

  --tblr-border-radius: var(--rr-radius);
  --tblr-border-radius-sm: var(--rr-radius-sm);
  --tblr-border-radius-lg: var(--rr-radius-lg);
  --tblr-border-radius-xl: var(--rr-radius-xl);

  --tblr-box-shadow: var(--rr-shadow-md);
  --tblr-box-shadow-sm: var(--rr-shadow-sm);
  --tblr-box-shadow-lg: var(--rr-shadow-lg);

  /* TYPE — 0.875rem base was cramped; 0.9375rem with a looser leading reads
     as considered rather than dense, without reflowing any layout. */
  --tblr-body-font-size: 0.9375rem;
  --tblr-body-line-height: 1.55;
  --tblr-font-sans-serif: 'InterVariable', 'Inter var', 'Inter', -apple-system,
    BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

/* =============================================================================
   BASE
   ============================================================================= */
body {
  font-family: var(--tblr-font-sans-serif);
  font-size: var(--tblr-body-font-size);
  line-height: var(--tblr-body-line-height);
  color: var(--rr-grey-900);
  background-color: var(--rr-grey-50);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Optical sizing + tighter tracking on headings is most of what makes type
   look "designed" rather than default. */
/* Deliberately no `color` here. Headings already inherit --rr-grey-900 from
   `body`, so setting it again buys nothing on an ordinary page — and it breaks
   every heading sitting inside a coloured surface. The panel headers set
   `color: white` on the block; a forced dark heading here won that cascade and
   rendered the title in near-black on the violet gradient while the paragraph
   beside it stayed white. Let headings inherit their surface's colour. */
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6,
.page-title, .card-title {
  font-weight: 600;
  letter-spacing: -0.011em;
}

.page-title {
  font-size: 1.375rem;
  letter-spacing: -0.018em;
}

/* Tabular figures everywhere numbers are compared down a column — money,
   counts, dates in tables. Proportional digits make columns look ragged. */
.table td, .table th,
.text-tabular, .card-title .h1, .h1.mb-3 {
  font-variant-numeric: tabular-nums;
}

a {
  text-decoration: none;
  transition: color 0.12s ease;
}

/* =============================================================================
   FOCUS — one visible, consistent ring. Accessibility and polish at once.
   ============================================================================= */
:focus-visible,
.btn:focus-visible,
.form-control:focus,
.form-select:focus,
.form-check-input:focus {
  outline: none;
  box-shadow: var(--rr-ring);
  border-color: var(--rr-blue-400);
}

/* =============================================================================
   CARDS — hairline border, minimal shadow. The flat-with-a-hairline card is
   the current idiom; a heavy drop shadow on every card is what made the app
   read as an old Bootstrap admin.
   ============================================================================= */
.card {
  border: 1px solid var(--rr-grey-200);
  border-radius: var(--rr-radius-lg);
  box-shadow: var(--rr-shadow-xs);
  background-color: #ffffff;
}

.card-header {
  background-color: transparent;
  border-bottom: 1px solid var(--rr-grey-100);
  padding: var(--rr-space-4) var(--rr-space-5);
  min-height: auto;
}

.card-title {
  font-size: 0.9375rem;
  font-weight: 600;
  margin-bottom: 0;
}

.card-body {
  padding: var(--rr-space-5);
}

.card-footer {
  background-color: var(--rr-grey-25);
  border-top: 1px solid var(--rr-grey-100);
}

/* Interactive cards (dashboard tiles, tool links) get a real hover state
   rather than sitting inert. */
.card-link-hover,
a.card,
.card.card-link {
  transition: box-shadow 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
}
a.card:hover,
.card.card-link:hover,
.card-link-hover:hover {
  box-shadow: var(--rr-shadow-md);
  border-color: var(--rr-grey-300);
  transform: translateY(-1px);
}

/* =============================================================================
   BUTTONS
   ============================================================================= */
.btn {
  border-radius: var(--rr-radius-sm);
  font-weight: 500;
  padding: 0.4375rem 0.875rem;
  transition: background-color 0.12s ease, border-color 0.12s ease,
              box-shadow 0.12s ease, color 0.12s ease;
}

.btn-sm {
  border-radius: var(--rr-radius-sm);
  padding: 0.3125rem 0.625rem;
  font-size: 0.8125rem;
}

.btn-lg { border-radius: var(--rr-radius); padding: 0.625rem 1.25rem; }

.btn-primary {
  background-color: var(--rr-blue-600);
  border-color: var(--rr-blue-600);
  box-shadow: var(--rr-shadow-xs);
}
.btn-primary:hover,
.btn-primary:focus {
  background-color: var(--rr-blue-700);
  border-color: var(--rr-blue-700);
}

.btn-outline-primary { color: var(--rr-blue-700); border-color: var(--rr-blue-300); }
.btn-outline-primary:hover { background-color: var(--rr-blue-600); border-color: var(--rr-blue-600); }

/* The most-used button in the app (239 uses) — it was reading as a disabled
   grey box. Now a proper neutral secondary. */
.btn-outline-secondary {
  color: var(--rr-grey-700);
  border-color: var(--rr-grey-300);
  background-color: #ffffff;
}
.btn-outline-secondary:hover {
  background-color: var(--rr-grey-100);
  border-color: var(--rr-grey-400);
  color: var(--rr-grey-900);
}

.btn-success { background-color: var(--rr-success); border-color: var(--rr-success); }
.btn-danger  { background-color: var(--rr-danger);  border-color: var(--rr-danger); }
.btn-warning { background-color: var(--rr-warning); border-color: var(--rr-warning); color: #fff; }

/* =============================================================================
   FORMS
   ============================================================================= */
.form-control, .form-select {
  border-radius: var(--rr-radius-sm);
  border-color: var(--rr-grey-300);
  padding: 0.4375rem 0.75rem;
  font-size: 0.9375rem;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.form-control::placeholder { color: var(--rr-grey-400); }
.form-label {
  font-weight: 500;
  font-size: 0.8125rem;
  color: var(--rr-grey-700);
  margin-bottom: 0.3125rem;
}
.form-hint, .form-text { color: var(--rr-grey-500); font-size: 0.8125rem; }

/* -----------------------------------------------------------------------------
   PHANTOM FORM CLASSES

   `match_reports/forms.py` styles its widgets with `form-input` (28 uses),
   `form-checkbox` (5) and `form-textarea` (3). None of those are Tabler or
   Bootstrap classes, and none are defined anywhere in the project — so 36 form
   controls, all of them on the match report and incident report forms, render
   completely unstyled: no width, no border, no font size.

   On a phone that is not cosmetic. An unstyled <textarea> falls back to
   `cols`-based sizing and runs past the viewport: this is what put the incident
   report's `incident_description` field off-screen (x29..399 in a 390px window)
   and gave that page 9px of horizontal scroll.

   Aliased here rather than renamed in forms.py: one change fixes all 36, costs
   no Python, and still works if the names come back. Nothing in the templates,
   CSS or JS selects on them, so there is nothing to break.
   ----------------------------------------------------------------------------- */
.form-input,
.form-textarea {
  display: block;
  width: 100%;
  max-width: 100%;
  padding: 0.4375rem 0.75rem;
  font-size: 0.9375rem;
  line-height: var(--tblr-body-line-height);
  color: var(--rr-grey-900);
  background-color: #ffffff;
  border: 1px solid var(--rr-grey-300);
  border-radius: var(--rr-radius-sm);
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.form-input::placeholder,
.form-textarea::placeholder { color: var(--rr-grey-400); }
.form-input:focus,
.form-textarea:focus {
  outline: none;
  border-color: var(--rr-blue-400);
  box-shadow: var(--rr-ring);
}

.form-checkbox {
  width: 1.0625rem;
  height: 1.0625rem;
  margin-right: 0.375rem;
  border: 1px solid var(--rr-grey-300);
  border-radius: 3px;
  vertical-align: middle;
  accent-color: var(--rr-blue-600);
}

/* Structural backstop: NO form control may exceed its container, whatever class
   it carries — or doesn't.

   Every rule above is class-based, so a control with no class at all gets none
   of them and falls back to browser-default sizing. That is a real overflow
   source, not a hypothetical: a bare `<select>` on Bulk Reschedule measured
   485px inside a 390px viewport, and an unclassed `<input>` did the same on
   Merit List Create. `cols` and `size` are character counts, not widths.

   Checkboxes, radios and range sliders are excluded — they are fixed-size
   controls and capping them does nothing useful. */
textarea,
select,
input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="range"]) {
  max-width: 100%;
}

/* -----------------------------------------------------------------------------
   SCROLL AFFORDANCE

   A horizontally scrollable table looks identical to one that simply ends. That
   is the whole reason hidden action columns read as missing rather than as
   "swipe for more" — and it is the remaining cost on Smart Appointments, which
   is ~99% a desktop surface but should still be readable on a phone.

   `responsive-layout.js` adds `.is-scrollable` to any scroller with content off
   its right edge, and removes it once you reach the end. The fade is drawn on
   the container, not the table, so it never moves with the content.
   ----------------------------------------------------------------------------- */
.table-responsive.is-scrollable,
.planner-grid.is-scrollable {
  position: relative;
}
.table-responsive.is-scrollable::after,
.planner-grid.is-scrollable::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 40px;
  pointer-events: none;
  background: linear-gradient(to right, rgba(255, 255, 255, 0), rgba(255, 255, 255, 0.92));
}

/* =============================================================================
   TABLES — lighter rules, a quieter header, a real hover row.
   ============================================================================= */
.table {
  --tblr-table-border-color: var(--rr-grey-100);
  margin-bottom: 0;
}
.table thead th {
  background-color: var(--rr-grey-50);
  border-bottom: 1px solid var(--rr-grey-200);
  color: var(--rr-grey-600);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  padding: 0.6875rem 0.75rem;
  white-space: nowrap;
}
.table tbody td {
  padding: 0.6875rem 0.75rem;
  border-bottom: 1px solid var(--rr-grey-100);
  vertical-align: middle;
}
.table tbody tr:last-child td { border-bottom: 0; }
.table-hover tbody tr { transition: background-color 0.1s ease; }
.table-hover tbody tr:hover { background-color: var(--rr-grey-25); }

/* A table that fills a card should meet its rounded corners cleanly. */
.card > .table-responsive:first-child .table thead th:first-child,
.card > .table:first-child thead th:first-child { border-top-left-radius: var(--rr-radius-lg); }
.card > .table-responsive:first-child .table thead th:last-child,
.card > .table:first-child thead th:last-child { border-top-right-radius: var(--rr-radius-lg); }

/* =============================================================================
   BADGES — soft, legible, pill-shaped.
   ============================================================================= */
.badge {
  border-radius: 999px;
  font-weight: 600;
  font-size: 0.75rem;
  padding: 0.25rem 0.5625rem;
  letter-spacing: 0.005em;
}
.badge.bg-success, .badge.bg-green  { background-color: var(--rr-success-bg) !important; color: var(--rr-success) !important; }
.badge.bg-danger,  .badge.bg-red    { background-color: var(--rr-danger-bg)  !important; color: var(--rr-danger)  !important; }
.badge.bg-warning, .badge.bg-yellow { background-color: var(--rr-warning-bg) !important; color: var(--rr-warning) !important; }
.badge.bg-info,    .badge.bg-azure  { background-color: var(--rr-info-bg)    !important; color: var(--rr-blue-700) !important; }
.badge.bg-primary, .badge.bg-blue   { background-color: var(--rr-blue-50)    !important; color: var(--rr-blue-700) !important; }
.badge.bg-secondary                 { background-color: var(--rr-grey-100)   !important; color: var(--rr-grey-700) !important; }

/* Notification counts stay loud — a soft pill would lose the badge's job. */
.nav-link .badge.bg-red,
.navbar .badge.bg-red {
  background-color: var(--rr-danger) !important;
  color: #ffffff !important;
}

/* =============================================================================
   ALERTS
   ============================================================================= */
.alert {
  border-radius: var(--rr-radius);
  border-width: 1px;
  padding: var(--rr-space-3) var(--rr-space-4);
  box-shadow: none;
}
.alert-success { background-color: var(--rr-success-bg); border-color: #bfe3d3; color: #125c3e; }
.alert-danger  { background-color: var(--rr-danger-bg);  border-color: var(--rr-danger-border); color: #92251f; }
.alert-warning { background-color: var(--rr-warning-bg); border-color: var(--rr-warning-border); color: #85550a; }
.alert-info    { background-color: var(--rr-info-bg);    border-color: var(--rr-blue-200); color: var(--rr-blue-800); }

/* =============================================================================
   NAVBAR + PAGE CHROME
   ============================================================================= */
.navbar {
  background-color: #ffffff;
  border-bottom: 1px solid var(--rr-grey-200);
}
.navbar-sticky .navbar { box-shadow: var(--rr-shadow-xs); }

.navbar .nav-link {
  border-radius: var(--rr-radius-sm);
  font-weight: 500;
  color: var(--rr-grey-600);
  transition: background-color 0.12s ease, color 0.12s ease;
}
.navbar .nav-link:hover { background-color: var(--rr-grey-100); color: var(--rr-grey-900); }
.navbar .nav-link.active,
.navbar .nav-item.active > .nav-link {
  background-color: var(--rr-blue-50);
  color: var(--rr-blue-700);
}

.dropdown-menu {
  border-radius: var(--rr-radius);
  border: 1px solid var(--rr-grey-200);
  box-shadow: var(--rr-shadow-lg);
  padding: var(--rr-space-2);
}
.dropdown-item {
  border-radius: var(--rr-radius-sm);
  padding: 0.4375rem 0.625rem;
  color: var(--rr-grey-700);
}
.dropdown-item:hover, .dropdown-item:focus { background-color: var(--rr-grey-100); color: var(--rr-grey-900); }
.dropdown-item.active { background-color: var(--rr-blue-50); color: var(--rr-blue-700); }
.dropdown-header {
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--rr-grey-400);
  padding: var(--rr-space-2) 0.625rem var(--rr-space-1);
}

.page-header {
  padding-top: var(--rr-space-5);
  padding-bottom: var(--rr-space-3);
}
.page-body { padding-top: var(--rr-space-4); }

.footer {
  border-top: 1px solid var(--rr-grey-200);
  color: var(--rr-grey-500);
  font-size: 0.8125rem;
  margin-top: var(--rr-space-6);
}

/* =============================================================================
   PANEL HEADER

   The full-bleed hero block on finance and performance pages. Its background is
   `--rr-panel-header-bg` (see the token for why it is a violet gradient rather
   than the brand blue) so all 26 sites change together.

   Use this class on new panels rather than restating the gradient inline — that
   is exactly how the app ended up with 175 copies of it to find.
   ============================================================================= */
.rr-panel-header {
  background: var(--rr-panel-header-bg);
  color: #ffffff;
  border-radius: var(--rr-radius-lg) var(--rr-radius-lg) 0 0;
}
.rr-panel-header h1, .rr-panel-header h2, .rr-panel-header h3,
.rr-panel-header h4, .rr-panel-header h5,
.rr-panel-header .card-title { color: #ffffff; }

/* =============================================================================
   SHARED COMPONENT CLASSES
   Used by templates/includes/_empty_state.html, _stat_tile.html and
   _filter_bar.html so pages stop inventing their own. The page header itself
   is not a partial: dashboard_base.html already owns it as a `page_header`
   block, and only two templates override it.
   ============================================================================= */

/* Empty state — a page with no rows should explain itself and offer the
   action that fixes it, not render a blank card. */
.rr-empty {
  text-align: center;
  padding: var(--rr-space-6) var(--rr-space-4);
  color: var(--rr-grey-500);
}
.rr-empty-icon {
  width: 48px; height: 48px;
  margin: 0 auto var(--rr-space-4);
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background-color: var(--rr-grey-100);
  color: var(--rr-grey-400);
}
.rr-empty-title { font-size: 0.9375rem; font-weight: 600; color: var(--rr-grey-800); margin-bottom: var(--rr-space-1); }
.rr-empty-text  { font-size: 0.875rem; max-width: 24rem; margin: 0 auto var(--rr-space-4); }

/* Stat tile — the dashboard number block. */
.rr-stat { display: flex; flex-direction: column; gap: var(--rr-space-1); }
.rr-stat-label {
  font-size: 0.6875rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--rr-grey-500);
}
.rr-stat-value {
  font-size: 1.75rem; font-weight: 650; line-height: 1.1;
  color: var(--rr-grey-900); font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.rr-stat-meta { font-size: 0.8125rem; color: var(--rr-grey-500); }

/* Filter bar — the row of selects above a list page. */
.rr-filter-bar {
  display: flex; flex-wrap: wrap; gap: var(--rr-space-3);
  align-items: flex-end;
  padding: var(--rr-space-4);
  background-color: #ffffff;
  border: 1px solid var(--rr-grey-200);
  border-radius: var(--rr-radius-lg);
  margin-bottom: var(--rr-space-4);
}
.rr-filter-field { display: flex; flex-direction: column; gap: 0.25rem; min-width: 10rem; flex: 1 1 10rem; }
.rr-filter-actions { display: flex; gap: var(--rr-space-2); flex: 0 0 auto; }

/* Section heading between cards. */
.rr-section-title {
  font-size: 0.6875rem; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--rr-grey-500);
  margin: var(--rr-space-5) 0 var(--rr-space-3);
}

/* =============================================================================
   MOBILE
   Phone parity is a first-class requirement, not a fallback. Everything below
   768px is treated as the primary layout for that width, not a squeezed
   desktop.
   ============================================================================= */
@media (max-width: 767.98px) {
  /* Tap targets. Apple/Google both put the floor at 44px; Tabler's default
     .btn-sm is ~28px, and the app is full of them. */
  /* `display` is only changed on things that lay out their own contents.
     An <input>/<select> renders its value through browser-internal boxes, and
     making one a flex container is a known way to break <select> rendering in
     some engines — those get the height and nothing else. */
  .btn, .nav-link, .page-link {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
  .dropdown-item { min-height: 44px; display: flex; align-items: center; }

  /* Pagination wraps instead of running off the screen.
     `.pagination` is a flex row with Bootstrap's default `nowrap`, and
     django_tables2/tabler.html renders EVERY page number — its ELLIPSIS branch
     is dead, because a plain `page_range` never yields one (that needs
     `get_elided_page_range`). At FA's 282 appointments that is 19 links plus
     prev/next, which pushed /appointments/all/ 215px past a 390px viewport.

     The demo fixture has 141 rows, so it renders 10 links, fits in 316px, and
     the whole-app sweep read this page as clean — the bug only exists at
     production row counts. Measured on the real element grown to 19 pages:
     215px of page overflow before this rule, 0 after. */
  .pagination {
    flex-wrap: wrap;
    row-gap: var(--rr-space-1);
    justify-content: center;
  }

  /* ...and the buttons a page styled itself, which never carry `.btn`:
     `.btn-remove` on the merit-list and weighting builders measured 28px,
     `.filter-btn` on the report card 38px. Height only — a native <button>
     already centres its own label, and forcing a display type on one is how
     you break a button the page laid out itself. `.btn-close` keeps its own
     size; it is a fixed glyph with its own hit-area convention. */
  button:not(.btn):not(.btn-close) { min-height: 44px; }
  .form-control, .form-select, .form-input { min-height: 44px; }
  .btn-sm { min-height: 40px; }

  /* Reclaim horizontal space — on a 375px screen the desktop gutters cost
     ~12% of the viewport. */
  .container-fluid, .container-xl { padding-left: var(--rr-space-3); padding-right: var(--rr-space-3); }
  .card-body   { padding: var(--rr-space-4); }
  .card-header { padding: var(--rr-space-3) var(--rr-space-4); }
  .page-header { padding-top: var(--rr-space-4); padding-bottom: var(--rr-space-2); }
  .page-title  { font-size: 1.1875rem; }

  /* Tabler makes `.page-title` a flex container, so a subtitle marked
     `d-block` becomes a flex SIBLING rather than a new line — on the report
     forms that left "Incident Report" squeezed into two words on the left with
     the fixture crammed beside it. Let the title wrap and give the subtitle its
     own full-width row. */
  .page-title { flex-wrap: wrap; }
  .page-title > small,
  .page-title > .d-block {
    flex: 1 0 100%;
    font-size: 0.8125rem;
    font-weight: 400;
    line-height: 1.4;
    margin-top: 0.125rem;
  }

  /* Cards go edge-to-edge and lose their side borders — the standard mobile
     list idiom. Keeps content width instead of spending it on chrome. */
  .card { border-radius: var(--rr-radius); }

  /* A row of buttons in a page header stacks and fills, instead of
     overflowing off the right edge. */
  .page-header .btn-list { width: 100%; display: flex; flex-wrap: wrap; gap: var(--rr-space-2); }
  .page-header .btn-list > .btn { flex: 1 1 auto; justify-content: center; }

  /* `.btn-group` pins its buttons to a single line by design, so a five-button
     filter group measures ~818px and drags the whole page into a horizontal
     scroll on a 390px screen — the single most common "the mobile site is
     broken" symptom, and it was happening on the manager dashboard. Let the
     group wrap; each button then needs its own radius back, since the group's
     square-inner-corners rule assumes one unbroken row. */
  .btn-group:not(.btn-group-vertical) {
    flex-wrap: wrap;
    gap: var(--rr-space-2);
  }
  .btn-group:not(.btn-group-vertical) > .btn,
  .btn-group:not(.btn-group-vertical) > .btn-check + .btn {
    border-radius: var(--rr-radius-sm) !important;
    margin-left: 0 !important;
    flex: 0 1 auto;
  }

  /* Same class of bug, one level up: any flex row of controls that was laid
     out for desktop should wrap rather than push the page sideways. */
  .card-actions { flex-wrap: wrap; }

  /* Tabler's `.card-header` is a flex row with `.card-actions` pushed to the
     right. At 390px there is no room for both, so the actions crowd the title
     and read as floating on top of it — the role tab group on the competition
     statistics page sat over "Appointment Counts by Role". Let the header wrap
     and give the actions their own full-width row underneath. */
  .card-header {
    flex-wrap: wrap;
    /* Enough that a descender ("pp", "y") clears the actions row below. */
    row-gap: var(--rr-space-3);
  }
  .card-header > .card-actions {
    /* Tabler sets `margin-top: -8px` to pull actions up onto the title's line.
       Once the header wraps, that exactly cancels the `row-gap` above and the
       actions sit flush against the title — which is what made the role tabs
       look like they were covering "Appointment Counts by Role". */
    margin-top: 0;
  }

  /* Only a WIDE action group takes a row of its own: a tab strip, or two or
     more controls. A lone button — "Clear" on the appointment-list filter card
     — still belongs on the title's line, and forcing it down left it stranded
     and left-aligned under "Filters". `.nav` is matched as a descendant
     because the tab strips wrap their <ul> in a .nav-tabs-container. */
  .card-header > .card-actions:has(> * + *),
  .card-header > .card-actions:has(.nav) {
    margin-left: 0;
    width: 100%;
  }

  /* ...and the general case. Bootstrap's `.d-flex` defaults to `nowrap`, so any
     desktop-authored row of controls drags the page sideways on a phone — a row
     of four action buttons on Manage Expense Claims measured 260px past the
     viewport. Wrapping is the only sane default at this width; a row that
     genuinely must stay on one line says so with `.flex-nowrap` and is honoured
     (those get a scroller instead, below). */
  .d-flex:not(.flex-nowrap):not(.flex-column):not(.flex-md-column) {
    flex-wrap: wrap;
  }

  /* A sub-navigation IS a row that must stay on one line — wrapping a tab bar
     onto two rows looks broken. Give it a horizontal scroller instead, which is
     what `competition_stats_subnav.html` already does and the other four
     subnavs (performance, appointments, equipment, flights) did not. The merit
     list overflowed 184px purely because of this. */
  .navbar-nav.flex-row.flex-nowrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
  }
  .navbar-nav.flex-row.flex-nowrap > .nav-item { flex: 0 0 auto; }

  /* Modals become sheets. */
  .modal-dialog { margin: 0; align-items: flex-end; min-height: 100%; }
  .modal-content { border-radius: var(--rr-radius-lg) var(--rr-radius-lg) 0 0; }

  /* Forms: 16px minimum stops iOS Safari zooming the viewport on focus —
     the single most common "the mobile site is broken" report. The phantom
     classes above need it just as much; the incident report's description
     textarea was the field that triggered it.

     Matched on the ELEMENT, not just the class, because a sweep found the floor
     missing three different ways: bulk-reschedule uses bare `<select>`/`<input>`
     with no `.form-control` at all; the report card's own `<style>` sets
     `.filter-group input { font-size: 0.9rem }`, which outranks a single-class
     selector and is loaded after this file; and the Smart Appointments note
     fields carry `style="font-size: 0.7rem"` inline. `!important` is what beats
     the last two — this is a floor, not a preference, and a page that wants
     smaller text on a phone is asking for the viewport to jump on focus. */
  .form-control, .form-select, .form-input, .form-textarea,
  textarea,
  select,
  input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]):not([type="range"]):not([type="file"]):not([type="color"]):not([type="submit"]):not([type="button"]):not([type="image"]) {
    font-size: 16px !important;
  }

  /* The menu button is the one control on every page, and it was 32px — under
     the 44px the rest of the app is held to. Tabler sizes it from padding
     alone, so give it a floor and centre the icon inside it. */
  .navbar-toggler {
    min-height: 44px;
    min-width: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }

  /* ---------------------------------------------------------------------
     THE FLEX MIN-WIDTH TRAP

     A flex item defaults to `min-width: auto`, which refuses to shrink below
     its content's min-content width. One long unbroken string — a filename
     like `demo_venue_briefing_ref_only.pdf`, an email, a venue name — therefore
     pushes its whole row past the viewport instead of wrapping, and the page
     gains a horizontal scroll. This is what still overhung Match Documents by
     56px after the table fix: the row was a `.d-flex`, not a table, so nothing
     in the table pass touched it.

     `min-width: 0` only *permits* shrinking; an item that must keep its size
     still says so with `flex-shrink: 0` or a width, and those still win. The
     exclusions below are the things that must never be squeezed narrower than
     their label: buttons, badges, avatars and icons.
     --------------------------------------------------------------------- */
  .d-flex > *:not(.btn):not(.badge):not(.avatar):not(svg):not(img),
  .list-group-item > *:not(.btn):not(.badge):not(.avatar):not(svg):not(img) {
    min-width: 0;
  }

  /* Shrinking is only half of it — the string still has to be allowed to
     break. Scoped to the content containers so it can't affect nav or
     controls.

     `anywhere`, not `break-word`, because only `anywhere` also lowers the
     element's MIN-CONTENT width — which is what lets a flex row carrying a long
     filename shrink instead of pushing the page sideways.

     That same property is exactly wrong inside a table. A table sizes its
     columns from their min-content width, so `anywhere` tells the browser every
     column may shrink to one character: a 10-column appointments table then
     compresses to fit 390px instead of overflowing into its `.table-responsive`
     scroller, and every header renders one letter per line ("D/a/t/e"). It also
     breaks ordinary names mid-word ("Adam Fieldin / g").

     So table cells opt back out. They keep normal wrapping, the table keeps its
     natural width, and the scroller does its job. (This was first patched for
     one card — #officials-activity-content — before it was clear the whole app
     had it.) */
  .list-group-item, .card-body, .card-header, .rr-empty {
    overflow-wrap: anywhere;
  }

  /* Table cells opt back out of the rule above.
     Scoped to `th`/`td` rather than `.table th` because plenty of tables here
     carry their own class instead (`.merit-table`, `.comparison-table`, the
     KMI and parking grids) and shredded just the same. A table cell should
     never break mid-word by default, whatever class its table has. */
  th, td {
    overflow-wrap: normal;
    word-break: normal;
  }

  /* Not wrapping mid-word still leaves the column only as wide as its longest
     word, so "Ashley Beecham (190)" comes out as nine stacked fragments in an
     80px column. `responsive-layout.js` measures what the table would take
     unwrapped and gives it that as a floor; this is the class it toggles for
     one frame to take that measurement. Never leave it on an element. */
  .rr-measure-nowrap th, .rr-measure-nowrap td {
    white-space: nowrap !important;
  }

  /* The flip side: a badge or button excluded from `min-width: 0` above still
     has flex-shrink: 1, so it gets squeezed and CLIPS its own label —
     "DEMO Venue Briefing" rendering as "DEMO Venue Briefi". Now that the text
     beside it can shrink, these can hold their natural width instead; a label
     too long even for that wraps to a second line rather than losing
     characters. */
  .d-flex > .badge,
  .list-group-item .badge,
  .d-flex > .btn,
  .list-group-item .btn {
    flex-shrink: 0;
  }
  .badge {
    white-space: normal;
  }

  /* Match Documents rows: the category badge sits BESIDE the filename, which
     leaves the filename ~230px and forces long ones to break mid-word
     ("demo_travel_itinerar / y_m1.pdf"). Stack the badge above instead — it is
     a tag, not a column — and the filename gets the full width it needs. */
  .doc-row .doc-main {
    flex-direction: column;
    align-items: flex-start !important;
    gap: 0.375rem !important;
    width: 100%;
  }
  .doc-row .doc-main > .badge {
    align-self: flex-start !important;
  }
  .doc-row .doc-meta { width: 100%; }

  /* A row that pairs content with an action (Download, View, Respond) puts the
     action on its own full-width line rather than fighting the text for room. */
  .list-group-item.d-flex.justify-content-between {
    flex-wrap: wrap;
  }
  .list-group-item.d-flex.justify-content-between > *:last-child:has(.btn) {
    width: 100%;
  }
  .list-group-item.d-flex.justify-content-between > *:last-child:has(.btn) .btn {
    width: 100%;
    justify-content: center;
  }

  /* Stat tiles shrink so two fit per row rather than one. */
  .rr-stat-value { font-size: 1.5rem; }

  /* ---------------------------------------------------------------------
     THE MANAGER DASHBOARD'S LENGTH

     The dashboard is 9,541px tall on a phone, and it is worth being precise
     about why: it is NOT a layout problem. Measured at 390px, zero rows sit
     side-by-side — it is already a single column. One card,
     "Officials Last Activity", accounts for 5,565px of it, or 58%, because it
     renders every official across five role tables: 468 rows.

     Those tables are already sorted with the most relevant rows first (longest
     since a game), so the top of each list is the part a manager actually wants.
     Capping each into a scrollable panel keeps every row reachable, keeps the
     column sorting working, and takes roughly 4,000px off the page without
     touching the desktop layout at all.
     --------------------------------------------------------------------- */
  #officials-activity-content .table-responsive {
    max-height: 320px;
    overflow-y: auto;
  }
  /* The role heading must stay put while its own list scrolls under it. */
  #officials-activity-content thead th {
    position: sticky;
    top: 0;
    z-index: 1;
  }

  /* These panels scroll in both directions, so a name takes the width it needs
     rather than wrapping to two lines. (The `overflow-wrap: normal` part is now
     handled for every table above; the `nowrap` is specific to this card.) */
  #officials-activity-content td,
  #officials-activity-content th {
    white-space: nowrap;
  }

  .rr-filter-bar { padding: var(--rr-space-3); gap: var(--rr-space-2); }
  .rr-filter-field { flex: 1 1 100%; }
  .rr-filter-actions { flex: 1 1 100%; }
  .rr-filter-actions .btn { flex: 1 1 auto; justify-content: center; }
}

/* Tablet: two-up stat tiles rather than four cramped ones. */
@media (min-width: 768px) and (max-width: 991.98px) {
  .rr-stat-value { font-size: 1.625rem; }
}

/* =============================================================================
   PRINT — several pages are printed (team sheets, match documents, exports).
   ============================================================================= */
@media print {
  .card { box-shadow: none; border: 1px solid var(--rr-grey-300); }
  .table thead th { background-color: var(--rr-grey-50) !important; -webkit-print-color-adjust: exact; }
}

/* Respect the OS setting — several of the hover transforms above are motion. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  a.card:hover, .card.card-link:hover, .card-link-hover:hover { transform: none; }
}
