/* app.css — rowOps Race operator theme.
 *
 * Layered on top of Bootstrap 5 via the CDN base. Defines three modes:
 *   .theme-light  — default operator daytime (Bootstrap defaults + brand tint)
 *   .theme-dark   — office / evening (deep slate background)
 *   .theme-sun    — race-day high-contrast (pure white-on-black; jet-yellow accents)
 *
 * Theme class is applied to BOTH <html> and <body> so Bootstrap chrome,
 * form controls, and our custom widgets all flip together. ThemeToggle
 * persists choice to localStorage.
 *
 * Brand CSS vars are emitted by Brand::cssBlock() in layout.php BEFORE
 * this stylesheet so var(--brand-*) references resolve at parse time.
 */

/* -------------------------------------------------------------------------
   Base — applies to every theme
   ------------------------------------------------------------------------- */
:root {
    --rowops-surface: var(--brand-bg, #fafaf8);
    --rowops-surface-2: var(--brand-cream, #f5f1e8);
    --rowops-surface-3: #ffffff;
    --rowops-ink: var(--brand-text, #1f2a33);
    --rowops-ink-muted: rgba(0, 0, 0, 0.6);
    --rowops-ink-faint: rgba(0, 0, 0, 0.35);
    --rowops-border: rgba(0, 0, 0, 0.1);
    --rowops-border-strong: rgba(0, 0, 0, 0.18);
    --rowops-card: #ffffff;
    --rowops-shadow: 0 1px 2px rgba(0, 0, 0, 0.04), 0 0 0 1px rgba(0, 0, 0, 0.04);
    --rowops-shadow-lift: 0 8px 24px rgba(0, 0, 0, 0.06), 0 2px 6px rgba(0, 0, 0, 0.04);
    --rowops-accent: var(--brand-primary, #b07d33);
    --rowops-accent-fg: #ffffff;
    --rowops-accent-tint: rgba(var(--brand-primary-rgb, 176, 125, 51), 0.08);
    --rowops-good: #157347;
    --rowops-warn: #ad6a00;
    --rowops-bad: #b02a37;
    --rowops-link: var(--rowops-accent);

    /* Aliases for names already consumed across templates/JS. Until
       2026-07-30 these were undefined, so 70+ call sites silently
       rendered their var() fallbacks — including retired V1 red
       #AD2F1E. Defining them routes every consumer through the brand
       system (kit: rowOps_home/rowOps_logo/BRAND-SYSTEM.md). */
    --rowops-muted: #6c757d;
    --rowops-text-muted: var(--rowops-ink-muted);
    --rowops-primary: var(--rowops-accent);
    --rowops-success: var(--rowops-good);
    --rowops-ok: var(--rowops-good);
    --rowops-danger: var(--rowops-bad);

    /* UI redesign 2026-05-26 — design tokens for the new shell.
       Spacing rhythm: 4 / 8 / 12 / 16 / 24 / 32 / 48. Use these
       instead of ad-hoc px values so a future tweak lands once. */
    --space-1: 0.25rem;  /*  4 px */
    --space-2: 0.5rem;   /*  8 px */
    --space-3: 0.75rem;  /* 12 px */
    --space-4: 1rem;     /* 16 px */
    --space-6: 1.5rem;   /* 24 px */
    --space-8: 2rem;     /* 32 px */
    --space-12: 3rem;    /* 48 px */
    --radius-sm: 0.3rem;
    --radius-md: 0.5rem;
    --radius-lg: 0.75rem;
    --top-bar-h: 56px;
    --rail-w: 240px;
    --rail-w-compact: 68px;
    --ease-snappy: cubic-bezier(0.2, 0.7, 0.2, 1);
}

html, body { background: var(--rowops-surface); color: var(--rowops-ink); }

/* Widen the layout container — operator-stated 2026-05-19 night:
   "both this and the start screen could use more of the available
   screen space. aiming for 80% width of what is available on any
   given device. aiming for full page on a standard 15inch laptop
   (currently being tested on twin 27 inch desk monitors)".

   Bootstrap's default .container caps at 1320px on xxl — that leaves
   a 27" 2560-wide monitor with ~50% empty gutter on each side.

   Strategy: under 1500 viewport (covers laptops up to ~15") use
   ~95vw so the page is effectively full-bleed; on bigger monitors
   capped at 80vw / 2200px so two-monitor desktops use the screen
   without going edge-to-edge.

   !important needed because Bootstrap defines per-breakpoint .container
   max-widths inside @media queries that would otherwise win.  */
.container { max-width: 95vw !important; }
@media (min-width: 1500px) {
    .container { max-width: min(80vw, 2200px) !important; }
}
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI Variable", "Segoe UI", Roboto, "Helvetica Neue", system-ui, sans-serif;
       font-feature-settings: "kern" 1, "calt" 1; letter-spacing: -0.005em; }
h1, h2, h3 { letter-spacing: -0.025em; font-weight: 650; }
a { color: var(--rowops-link); text-decoration: none; border-bottom: 1px dotted transparent; transition: border-color 0.15s; }
a:hover { color: var(--rowops-accent); border-bottom-color: currentColor; text-decoration: none; }

.tabular { font-variant-numeric: tabular-nums; }
.numeric-large { font-variant-numeric: tabular-nums; font-feature-settings: "tnum"; letter-spacing: -0.02em; }

/* Cards take a soft elevation that holds in dark mode too. */
.card {
    background: var(--rowops-card);
    border: 1px solid var(--rowops-border);
    border-radius: 0.5rem;
    box-shadow: var(--rowops-shadow);
}
.card-header {
    background: var(--rowops-surface-2);
    border-bottom: 1px solid var(--rowops-border);
    font-weight: 600;
    color: var(--rowops-ink);
}

/* Brand-aware primary buttons. */
.btn-primary,
.btn-primary:hover,
.btn-primary:focus,
.btn-primary:active {
    background: var(--rowops-accent);
    border-color: var(--rowops-accent);
    color: var(--rowops-accent-fg);
}
.btn-outline-primary {
    color: var(--rowops-accent);
    border-color: var(--rowops-accent);
}
.btn-outline-primary:hover { background: var(--rowops-accent); color: var(--rowops-accent-fg); }

.badge.bg-brand { background: var(--rowops-accent) !important; color: var(--rowops-accent-fg); }
.badge.bg-dark { background: var(--brand-dark, #1f2a33) !important; }

/* ──────────────────────────────────────────────────────────────────
   Semantic badge family — M5b (2026-05-26 dashboard redesign).
   Replaces the ad-hoc bg-warning / bg-danger / bg-success / bg-info
   sprawl with five colour roles that mean the same thing on every
   surface: error / warning / ok / info / neutral. Pairs with the
   five-colour rule from the design doc (cockpit + dashboard parity).
   Existing Bootstrap classes still work — these are additive, opt-in
   per-callsite. Sun-mode + dark-mode parity is enforced below.
   ────────────────────────────────────────────────────────────────── */
.badge.is-error    { background: var(--rowops-bad);    color: #fff; }
.badge.is-warning  { background: var(--rowops-warn);   color: #fff; }
.badge.is-ok       { background: var(--rowops-good);   color: #fff; }
.badge.is-info     { background: var(--rowops-accent); color: var(--rowops-accent-fg); }
.badge.is-neutral  { background: var(--rowops-surface-2); color: var(--rowops-ink-muted); border: 1px solid var(--rowops-border); }

/* Tiny uppercase label utility — replaces the dozen-or-so inline
   style="font-size:.72rem;letter-spacing:.04em;" attributes scattered
   across the dashboard. Single class, single source of truth. */
.kpi-label {
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    font-weight: 600;
    color: var(--rowops-ink-muted);
}
.kpi-label-row {
    /* Small uppercase label inside a card body — matches `.kpi-label`
       but inherits the surrounding spacing the row context provides. */
    font-size: 0.65rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    font-weight: 600;
    color: var(--rowops-ink-muted);
}

/* Section header — replaces the repeated
   <h2 class="h6 mt-4 mb-2 text-uppercase text-muted" style="letter-spacing:0.06em;">
   pattern that anchors each phase block on the dashboard. Single
   utility class so a typography tweak lands in one place. */
.section-heading {
    font-size: 0.95rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--rowops-ink-muted);
    margin-top: 1.5rem;
    margin-bottom: 0.5rem;
}

/* Control hub glyph — large emoji/icon centred in each surface tile.
   `line-height: 1` keeps the tile body compact; `display: block` lets
   the surrounding text rhythm stay predictable. */
.control-glyph {
    font-size: 2.5rem;
    line-height: 1;
    display: block;
}

/* Form-control "natural-width" helpers — replaces ad-hoc
   style="width: auto" / "max-width: 260px" on filter rows. Operator
   filters across the dashboard now share one footprint. */
.form-natural-width { width: auto !important; }
.form-search-width { max-width: 260px; }

/* Control hub surface tile — invites a tap. Hover lifts the card +
   stronger shadow + brand-red border accent. Race-day operators on a
   tablet should feel "this is the obvious thing to tap". */
.control-tile {
    transition: transform 0.12s ease-out, box-shadow 0.12s ease-out, border-color 0.12s ease-out;
    border-width: 2px;
    cursor: pointer;
}
.control-tile:hover {
    transform: translateY(-3px);
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
    border-color: var(--rowops-accent);
}
.control-tile.is-locked {
    opacity: 0.45;
    cursor: not-allowed;
}
.control-tile.is-locked:hover {
    transform: none;
    box-shadow: var(--rowops-shadow);
    border-color: var(--rowops-border);
}

/* Large variant for the regatta-dashboard race-day cockpit (Starter /
   Finish tile pair). 200px min-height matches the previous inline
   style; the .cockpit-tile-large class also bumps the glyph one step
   so the tap target reads at a tablet's distance. */
.cockpit-tile-large {
    min-height: 200px;
}
.cockpit-tile-large .control-glyph {
    font-size: 3.5rem;
}

/* Inline brand-accent colour helper for inline counters
   (e.g. on-water count on the race-day cockpit). */
.text-accent { color: var(--rowops-accent) !important; }

/* Fraction-denominator helper — used next to a tabular count to
   render the "/ N" denominator at a smaller, muted size. */
.fraction-denominator {
    font-size: 0.7em;
    font-variant-numeric: tabular-nums;
}

/* Gap-spread chips on /draw-decisions pair tables. tight/slim/ok
   are the three margin states the analyser computes per race-pair
   instance; the colour rule follows the brand's five-colour family
   (bad / warn / good) so an operator's eye reads spread severity at
   peripheral-vision distance. Sun-mode parity preserved through the
   existing `html.theme-sun .text-danger` override. */
.gap-spread-tight { color: var(--rowops-bad);  font-weight: 600; }
.gap-spread-slim  { color: var(--rowops-warn); font-weight: 600; }
.gap-spread-ok    { color: var(--rowops-good); font-weight: 600; }

/* Inline badge sized down to ride next to an H1/H2 without
   throwing the baseline off — used on /draw-decisions and similar
   surfaces where a state pill rides inside the page title. */
.badge.badge-h1-inline {
    font-size: 0.55em;
    font-weight: 600;
    vertical-align: middle;
}

/* Card with a brand-accent left border bar — used on action / config
   strips (e.g. /draw-decisions rerun toolbar) to set them apart from
   informational cards without changing background. */
.card.card-accent-bar {
    border-left: 4px solid var(--rowops-accent);
}

/* Pill-shaped button — race-day cross-surface nav links (Starter ↔
   Finish ↔ Settings). Used on control/start.php + control/finish.php
   header strips for the contextual "go to the other tablet's surface"
   shortcuts. Pairs with btn-outline-secondary or btn-sm. */
.btn.btn-pill { border-radius: 999px; }

/* Compact form-help text — tighter line-height + smaller size than
   Bootstrap's default .form-text. Used inline next to inputs where
   the help fits in a tight vertical band. */
.form-help-tight {
    font-size: 0.72rem;
    line-height: 1.2;
}

/* Generic display-none helper — for elements toggled by JS (recall
   form, lane-flag form, cancel button initial state, etc). Single
   class so a future "show/hide animation" lands here, not in every
   inline-style site. */
.is-hidden { display: none !important; }

/* Strong warning text — used for camera-state error banners +
   similar mid-page alerts that need to read at a glance but
   don't warrant a full alert component. Pairs with the brand's
   warning colour token so sun-mode parity holds. */
.text-warn-strong {
    color: var(--rowops-warn);
    font-weight: 600;
}

/* Tablet-friendly board action button — large hit-target buttons on
   the finish board (camera Start / Record clip). Sized for thumb
   taps on a propped iPad; the underlying btn-primary / btn-danger
   colours carry the role-meaning. */
.btn.btn-board-action {
    font-size: 1.05rem;
    padding: 0.55rem 1.1rem;
    border-radius: 0.4rem;
}

/* Force-cancel form on /races sits directly under the draw-job
   banner and needs a slight negative top + reserved bottom spacing
   to look like a continuation of the banner instead of a separate
   block. JS toggles is-hidden, this carries the position. */
.draw-cancel-form-position {
    margin-top: -0.5rem;
    margin-bottom: 1rem;
}

/* ──────────────────────────────────────────────────────────────────
   Sun-mode + night-mode parity for the new M5 utility classes.
   Operator-stated 2026-05-24: red text is hard to read in direct
   sun glare. The existing .text-danger override flips red → jet
   yellow on white border; new utilities that carry semantic red,
   amber, or accent colour need the same treatment so an operator
   on a tablet under the sun reads them at distance.
   ────────────────────────────────────────────────────────────────── */

/* SUN MODE — high-contrast outdoor variant */

/* text-accent (brand-red on white normally) → jet-yellow in sun */
html.theme-sun .text-accent, body.theme-sun .text-accent {
    color: #fde047 !important;
}

/* card-accent-bar — preserve the operator's expected 3px white border
   on every card edge; substitute jet-yellow for the accent bar so it
   reads as an action/config marker without competing with the white
   card border. */
html.theme-sun .card.card-accent-bar,
body.theme-sun .card.card-accent-bar {
    border: 3px solid #fff;
    border-left: 4px solid #fde047;
}

/* Gap-spread chips on /draw-decisions — red/amber/green meanings
   change to white/yellow/yellow in sun (severity inferred from chip
   position + count, not colour, when read at distance in glare). */
html.theme-sun .gap-spread-tight, body.theme-sun .gap-spread-tight {
    color: #fde047;
    font-weight: 800;
    text-transform: uppercase;
}
html.theme-sun .gap-spread-slim, body.theme-sun .gap-spread-slim {
    color: #fde047;
    font-weight: 700;
}
html.theme-sun .gap-spread-ok, body.theme-sun .gap-spread-ok {
    color: #fff;
    font-weight: 700;
}

/* text-warn-strong — amber/brown unreadable in sun, flip to jet-yellow */
html.theme-sun .text-warn-strong, body.theme-sun .text-warn-strong {
    color: #fde047;
    font-weight: 800;
}

/* control-tile hover border — operator preference is no red accents in
   sun (jet-yellow is the action colour). The :hover state still lifts
   the tile via transform; the border just switches accent colour. */
html.theme-sun .control-tile:hover,
body.theme-sun .control-tile:hover {
    border-color: #fde047;
}

/* NIGHT MODE — red text on dark slate is muddy; lift the bad/warn
   tokens for the same utilities so the cockpit reads clearly when an
   operator is in low light (early-morning setup, evening wrap-up). */

html.theme-dark .text-accent, body.theme-dark .text-accent {
    color: #ff7a66 !important;
}
html.theme-dark .gap-spread-tight, body.theme-dark .gap-spread-tight {
    color: #ff7a66;
}
html.theme-dark .gap-spread-slim, body.theme-dark .gap-spread-slim {
    color: #f0c674;
}
html.theme-dark .gap-spread-ok, body.theme-dark .gap-spread-ok {
    color: #6ed28b;
}
html.theme-dark .text-warn-strong, body.theme-dark .text-warn-strong {
    color: #f0c674;
}

/* ──────────────────────────────────────────────────────────────────
   Print sheet utilities — M5b slice 7 (2026-05-26)
   The /print/* templates are A4 sheets distributed to clubs +
   captains. Inline styles accumulated here for tight per-row
   margins + small print-text buttons. Centralise so a future
   typography or spacing tweak on the captain sheets lands in
   one place.
   ────────────────────────────────────────────────────────────────── */

/* Day-block spacing — used inside availability + affected-club
   sheets to separate per-day rows. Existing .day-block class
   gains a default 0.25rem margin-bottom; .day-block-tight is
   for the inner seat-list variant (0.15rem). */
.day-block { margin-bottom: 0.25rem; }
.day-block-tight { margin-bottom: 0.15rem; }

/* Small button text — print sheets reduce button text by ~25%
   so per-club mailer controls don't dominate the A4 layout. */
.btn-print-text { font-size: 0.78rem; }

/* Flush-top heading — print h2/h3 sometimes need the top margin
   stripped to butt against a preceding rule. Pairs with .border-0
   when removing the visual border too. */
.print-heading-flush { margin-top: 0; border: 0; }

/* Captain-mailer controls float to the right of the club row
   header. Bootstrap's .float-end works fine; this aliases it
   semantically for the "no-print + mailer" pattern. */
.mailer-controls-row { margin-left: auto; }

/* Club-totals subhead — small muted parenthetical that rides next
   to the club name h3 ("(8 rowers, 12 races)"). Pairs with the
   .print-heading-flush h3 above it. */
.club-totals-subhead {
    font-weight: 400;
    font-size: 0.85rem;
}

/* Tables get a tighter top-padding + uppercase th. */
.table th {
    font-weight: 600; font-size: 0.78rem; text-transform: uppercase;
    letter-spacing: 0.04em; color: var(--rowops-ink-muted);
}
.table > :not(caption) > * > * { vertical-align: middle; }

/* Alerts with a thicker accent border. */
.alert { border-radius: 0.5rem; border-left-width: 4px; }
.alert-danger  { border-left-color: var(--rowops-bad); }
.alert-warning { border-left-color: var(--rowops-warn); }
.alert-success { border-left-color: var(--rowops-good); }
.alert-info    { border-left-color: var(--rowops-accent); }

code { color: var(--rowops-accent); background: var(--rowops-surface-2); padding: 0.1em 0.35em; border-radius: 3px; }

/* Navbar branding. */
.navbar {
    background: var(--rowops-surface) !important;
    border-bottom: 1px solid var(--rowops-border);
}
.navbar-brand {
    font-weight: 700; letter-spacing: -0.02em; color: var(--rowops-ink) !important;
}
.navbar-brand:hover { color: var(--rowops-accent) !important; }
.navbar .brand-mark {
    display: inline-block; width: 28px; height: 28px; margin-right: 8px;
    background: var(--rowops-accent); border-radius: 4px; vertical-align: -8px;
}

/* Indicator pills (clock-drift + queue-status). */
.clock-drift, .queue-status, .theme-pill {
    font-size: 0.75rem; padding: 2px 8px; border-radius: 10px;
}
.clock-drift.ok    { background: rgba(0, 0, 0, 0.05); color: var(--rowops-ink); }
.clock-drift.warn  { background: var(--brand-warning, #ffc107); color: #000; }
.clock-drift.fail  { background: var(--rowops-bad); color: #fff; }
.queue-status         { background: var(--rowops-surface-2); color: var(--rowops-ink); }
.queue-status.queued  { background: var(--brand-warning, #ffc107); color: #000; }
.queue-status.offline { background: var(--rowops-bad); color: #fff; }

/* Form controls inherit theme. */
.form-control, .form-select {
    background: var(--rowops-card); color: var(--rowops-ink); border-color: var(--rowops-border);
}
.form-control::placeholder { color: var(--rowops-ink-muted); opacity: 0.6; }
.form-control:focus, .form-select:focus {
    background: var(--rowops-card); color: var(--rowops-ink);
    border-color: var(--rowops-accent); box-shadow: 0 0 0 0.2rem rgba(var(--brand-primary-rgb, 176, 125, 51), 0.15);
}

/* Race-day specific widgets. */
.race-card {
    background: var(--rowops-card); border: 1px solid var(--rowops-border);
    border-radius: 0.5rem; padding: 0.75rem 1rem; margin-bottom: 0.5rem;
    transition: transform 0.05s, box-shadow 0.05s;
}
.race-card:hover { box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06); }
.race-status-on-water { border-left: 4px solid var(--brand-warning, #ffc107); }
.race-status-finished { border-left: 4px solid var(--rowops-good); opacity: 0.85; }
.race-status-pending  { border-left: 4px solid var(--rowops-border); }

.lane-grid { display: grid; gap: 0.5rem; }
.lane-cell {
    background: var(--rowops-card); border: 1px solid var(--rowops-border);
    border-radius: 0.4rem; padding: 0.6rem 0.8rem;
    display: grid; grid-template-columns: 2.2rem 1fr auto; gap: 0.75rem; align-items: center;
}
.lane-cell .lane-num {
    font-size: 1.5rem; font-weight: 700; text-align: right;
    font-variant-numeric: tabular-nums; color: var(--rowops-ink-muted);
}
.lane-cell.finished { opacity: 0.6; }
.lane-cell.flagged  { background: rgba(176, 42, 55, 0.08); } /* --rowops-bad tint — flag is a problem state, not a brand accent */

/* Big-button race-day controls — sized for tablets at the start tower. */
.btn-race-action {
    min-width: 6rem; padding: 0.6rem 1.2rem; font-weight: 600;
    font-size: 1.1rem; letter-spacing: 0.02em;
    border-radius: 0.4rem; border: 0;
}
.btn-start { background: var(--brand-warning, #ffc107); color: #000; }
.btn-finish { background: var(--rowops-accent); color: var(--rowops-accent-fg); }
.btn-go { background: var(--rowops-good); color: #fff; }
.btn-flag { background: transparent; color: var(--rowops-ink-muted); border: 1px solid var(--rowops-border); }

/* -------------------------------------------------------------------------
   Dark theme
   ------------------------------------------------------------------------- */
html.theme-dark, body.theme-dark {
    --rowops-surface: #16202A;
    --rowops-surface-2: #1f232a;
    --rowops-ink: #ecedf0;
    --rowops-ink-muted: rgba(236, 237, 240, 0.6);
    --rowops-muted: rgba(236, 237, 240, 0.6);  /* was stuck at light-theme #6c757d via var() fallback */
    --rowops-border: rgba(255, 255, 255, 0.1);
    --rowops-card: #1c2026;
    --rowops-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.04);
    --rowops-link: #ff7a66;
}
html.theme-dark .badge.bg-light, body.theme-dark .badge.bg-light {
    background: var(--rowops-surface-2) !important; color: var(--rowops-ink);
}
html.theme-dark .table-light, body.theme-dark .table-light { --bs-table-bg: var(--rowops-surface-2); }
html.theme-dark .table, body.theme-dark .table { --bs-table-bg: var(--rowops-card); color: var(--rowops-ink); }
html.theme-dark .table-striped > tbody > tr:nth-of-type(odd) > * { --bs-table-accent-bg: rgba(255,255,255,0.03); }
html.theme-dark .navbar, body.theme-dark .navbar { background: #0e1014 !important; }

/* -------------------------------------------------------------------------
   Sun mode (race-day high contrast — outdoor / bright)
   Pure white-on-jet-black; jet-yellow start; accent-red finish.
   Fatter borders + larger crew names so the start tower can read at distance.
   ------------------------------------------------------------------------- */
html.theme-sun, body.theme-sun {
    --rowops-surface: #000000;
    --rowops-surface-2: #000000;            /* was #0a0a0a — eliminate near-black secondary surface that washed out on screen */
    --rowops-ink: #ffffff;
    --rowops-ink-muted: #f0f0f0;            /* was #cfcfcf — operator-stated 2026-05-23 race day: not enough contrast outdoors */
    --rowops-muted: #f0f0f0;                /* track ink-muted in glare — mid-grey #6c757d is illegible outdoors */
    --rowops-border: #ffffff;
    --rowops-card: #000000;
    --rowops-shadow: none;
    --rowops-link: #fde047;                 /* was #facc15 — slightly brighter yellow for direct-sun visibility */
}
/* Body-wide thicker baseline weight so text reads at distance in glare.
   Operator-stated 2026-05-24 race day: "make sure all text is bold and
   strong bold". Bumped 500 → 700 baseline; headings + key controls still
   reach 800-900 via their own rules below. */
html.theme-sun, body.theme-sun { font-weight: 700; }
html.theme-sun p, html.theme-sun li, html.theme-sun span, html.theme-sun div,
body.theme-sun p, body.theme-sun li, body.theme-sun span, body.theme-sun div { font-weight: inherit; }
html.theme-sun strong, html.theme-sun b, html.theme-sun h1, html.theme-sun h2, html.theme-sun h3,
body.theme-sun strong, body.theme-sun b, body.theme-sun h1, body.theme-sun h2, body.theme-sun h3 { font-weight: 800; }
html.theme-sun .small, html.theme-sun small,
body.theme-sun .small, body.theme-sun small { font-weight: 600; }
html.theme-sun *, body.theme-sun * { text-shadow: none; }
html.theme-sun .navbar, body.theme-sun .navbar { background: #000 !important; border-bottom: 3px solid #fff; }
html.theme-sun .card, body.theme-sun .card { border: 3px solid #fff; box-shadow: none; }
html.theme-sun .card-header, body.theme-sun .card-header {
    background: #000; color: #fff; border-bottom: 3px solid #fff; font-weight: 700;
}
html.theme-sun .table, body.theme-sun .table { --bs-table-color: #fff; --bs-table-bg: #000; --bs-table-border-color: #fff; }
html.theme-sun .table th, body.theme-sun .table th { color: #fff; border-color: #fff; font-weight: 700; }
html.theme-sun .table > :not(caption) > * > * { border-color: #fff; }
html.theme-sun .form-control, html.theme-sun .form-select,
body.theme-sun .form-control,  body.theme-sun .form-select {
    background: #000; color: #fff; border: 3px solid #fff; font-weight: 600;
}
html.theme-sun .form-control::placeholder, body.theme-sun .form-control::placeholder { color: #bbb; opacity: 0.85; }
html.theme-sun .btn-primary, body.theme-sun .btn-primary {
    background: #fde047; color: #000; border: 3px solid #fff; font-weight: 800;
}
html.theme-sun .btn-outline-primary, body.theme-sun .btn-outline-primary {
    color: #fde047; border: 3px solid #fde047; font-weight: 700;
}
html.theme-sun .btn-finish, body.theme-sun .btn-finish { background: #fde047; color: #000; font-weight: 800; }
html.theme-sun .btn-start,  body.theme-sun .btn-start  { background: #fde047; color: #000; font-weight: 800; }
html.theme-sun .btn-go,     body.theme-sun .btn-go     { background: #fde047; color: #000; font-weight: 800; }
/* Bootstrap utility buttons we surface in race-day controls. */
html.theme-sun .btn-secondary, body.theme-sun .btn-secondary,
html.theme-sun .btn-outline-secondary, body.theme-sun .btn-outline-secondary {
    background: #000; color: #fff; border: 3px solid #fff; font-weight: 700;
}
html.theme-sun .btn-success, body.theme-sun .btn-success {
    background: #fde047; color: #000; border: 3px solid #fff; font-weight: 800;
}
/* Operator-stated 2026-05-24: red text is hard to read in direct sun
   ("red writing is not great in the sun mode"). Switched both
   .btn-danger and .btn-outline-danger to high-contrast yellow on black
   with a thick white border + 900 weight; the danger semantics are
   carried by an outer ⚠ glyph and the action copy itself. */
html.theme-sun .btn-danger, body.theme-sun .btn-danger {
    background: #fde047; color: #000; border: 3px solid #fff; font-weight: 900;
    text-transform: uppercase; letter-spacing: 0.02em;
}
html.theme-sun .btn-outline-danger, body.theme-sun .btn-outline-danger {
    background: #000; color: #fde047; border: 3px solid #fff; font-weight: 900;
    text-transform: uppercase; letter-spacing: 0.02em;
}
html.theme-sun .btn-outline-danger:hover, body.theme-sun .btn-outline-danger:hover {
    background: #fde047; color: #000;
}
html.theme-sun .text-danger, body.theme-sun .text-danger { color: #fde047 !important; font-weight: 800; }
html.theme-sun .btn-warning, body.theme-sun .btn-warning {
    background: #fde047; color: #000; border: 3px solid #fff; font-weight: 800;
}
html.theme-sun .badge, body.theme-sun .badge { border: 2px solid #fff; font-weight: 700; }
/* Sun-mode parity for the M5 semantic badge tones (2026-06-02 audit).
   Used on head-mode finish-board state pills + the OTW lane flag chip
   + erg-finish DNF/DSQ. Default Bootstrap variant colours (muted
   orange/red on dark) vanish in glare; jet-yellow / pure-red on black
   reads at distance. */
html.theme-sun .badge.is-ok, body.theme-sun .badge.is-ok {
    background: #fde047; color: #000; border-color: #fff; font-weight: 800;
}
html.theme-sun .badge.is-info, body.theme-sun .badge.is-info {
    background: #001a33; color: #fff; border-color: #38f; font-weight: 800;
}
html.theme-sun .badge.is-warning, body.theme-sun .badge.is-warning {
    background: #fde047; color: #000; border-color: #fff; font-weight: 800;
}
html.theme-sun .badge.is-error, body.theme-sun .badge.is-error {
    background: #ff3030; color: #fff; border-color: #fff; font-weight: 800;
}
html.theme-sun .badge.is-neutral, body.theme-sun .badge.is-neutral {
    background: #000; color: #f0f0f0; border-color: #fff; font-weight: 700;
}
html.theme-sun .lane-cell, body.theme-sun .lane-cell {
    background: #000; border: 3px solid #fff; padding: 1rem 1.2rem;
}
html.theme-sun .lane-cell .lane-num, body.theme-sun .lane-cell .lane-num {
    color: #fde047; font-size: 2.2rem; font-weight: 800;
}
html.theme-sun .lane-cell .lane-crew, body.theme-sun .lane-cell .lane-crew {
    color: #fff; font-weight: 700;
}
html.theme-sun .race-card, body.theme-sun .race-card { background: #000; border: 3px solid #fff; }
html.theme-sun a, body.theme-sun a { color: #fde047; text-decoration: underline; font-weight: 700; }
html.theme-sun code, body.theme-sun code { background: #000; color: #fde047; border: 1px solid #fff; padding: 0.05rem 0.3rem; font-weight: 700; }
html.theme-sun .alert, body.theme-sun .alert { border: 3px solid #fff; background: #000; color: #fff; font-weight: 600; }
html.theme-sun .alert-info, body.theme-sun .alert-info { border-color: #38f; }
html.theme-sun .alert-warning, body.theme-sun .alert-warning { border-color: #fde047; }
html.theme-sun .alert-danger, body.theme-sun .alert-danger { border-color: #ff3030; }
html.theme-sun .alert-success, body.theme-sun .alert-success { border-color: #fde047; }
html.theme-sun .text-muted, body.theme-sun .text-muted { color: #f0f0f0 !important; }
html.theme-sun .navbar-brand, body.theme-sun .navbar-brand { color: #fff !important; font-weight: 800; }
html.theme-sun .navbar .brand-mark, body.theme-sun .navbar .brand-mark { background: #fde047; }
/* Bootstrap small-text utility renders at #666 in default themes — too
   low contrast on jet black. Operator-facing meta lines use .small a lot. */
html.theme-sun .small, body.theme-sun .small,
html.theme-sun small, body.theme-sun small { color: #f0f0f0; }
/* Form-check (checkboxes) — defaults invisible on black. */
html.theme-sun .form-check-input, body.theme-sun .form-check-input {
    background-color: #000; border: 2px solid #fff;
}
html.theme-sun .form-check-input:checked, body.theme-sun .form-check-input:checked {
    background-color: #fde047; border-color: #fff;
}

/* -------------------------------------------------------------------------
   Breadcrumbs — `.crumbs` (used at the top of every operator page)
   ------------------------------------------------------------------------- */
.crumbs {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.55rem;
    font-size: 0.85rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    margin-bottom: 1.4rem;
    padding: 0.5rem 0.85rem;
    background: var(--rowops-surface-2);
    border-radius: 0.5rem;
    border-left: 3px solid var(--rowops-accent);
    box-shadow: 0 1px 0 var(--rowops-border);
}
.crumbs a {
    color: var(--rowops-ink-muted);
    border-bottom: none;
    transition: color 0.15s;
}
.crumbs a:hover { color: var(--rowops-accent); }
.crumbs .sep {
    color: var(--rowops-ink-muted);
    opacity: 0.45;
    font-weight: 400;
}
.crumbs .current {
    color: var(--rowops-ink);
    font-weight: 700;
}
html.theme-sun .crumbs, body.theme-sun .crumbs {
    background: #000; border-left-color: var(--brand-warning, #facc15);
    box-shadow: 0 1px 0 #fff;
}
html.theme-sun .crumbs a, body.theme-sun .crumbs a { color: #cfcfcf; }
html.theme-sun .crumbs .current, body.theme-sun .crumbs .current { color: #fff; }

/* Back-compat — existing `nav.small.mb-3` strips that haven't been
   migrated yet pick up the same look so the page chrome is uniform. */
nav.small.mb-3 {
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--rowops-ink-muted);
    padding-bottom: 0.6rem;
    border-bottom: 1px solid var(--rowops-border);
    margin-bottom: 1.25rem !important;
}
nav.small.mb-3 a { color: var(--rowops-ink-muted); border-bottom: none; }
nav.small.mb-3 a:hover { color: var(--rowops-accent); }

/* -------------------------------------------------------------------------
   Theme toggle pill in the navbar
   ------------------------------------------------------------------------- */
.theme-toggle {
    display: inline-flex; gap: 0.15rem; padding: 0.15rem;
    background: var(--rowops-surface-2); border-radius: 999px;
    font-size: 0.8rem; line-height: 1;
}
.theme-toggle button {
    background: transparent; border: 0; padding: 0.25rem 0.6rem; border-radius: 999px;
    color: var(--rowops-ink-muted); cursor: pointer;
}
.theme-toggle button.active {
    background: var(--rowops-card); color: var(--rowops-ink);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
}
html.theme-sun .theme-toggle, body.theme-sun .theme-toggle { background: #111; border: 1px solid #fff; }
html.theme-sun .theme-toggle button.active, body.theme-sun .theme-toggle button.active {
    background: #facc15; color: #000;
}

/* Print: force light theme, hide chrome. */
@media print {
    html, body { background: #fff !important; color: #000 !important; }
    .navbar, footer, .theme-toggle, .no-print { display: none !important; }
    .card, .table, .lane-cell { box-shadow: none !important; border-color: #ccc !important; }
}

/* In-page confirmation overlay (RowOps.confirm + data-confirm). Lives
   in app.css so every theme + page picks up the same shell, and the
   page-level CSP isn't bypassed by an inline <style> the helper would
   otherwise need to emit. */
.rowops-confirm-overlay {
    position: fixed; inset: 0; z-index: 1080;
    display: flex; align-items: center; justify-content: center;
    background: rgba(0, 0, 0, 0.45);
    padding: 1rem;
}
.rowops-confirm-overlay[hidden] { display: none; }
.rowops-confirm-card {
    background: var(--rowops-card);
    color: var(--rowops-ink);
    border-radius: 0.5rem;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
    max-width: 460px; width: 100%;
    padding: 1.25rem 1.5rem;
}
.rowops-confirm-title {
    font-size: 1.05rem; font-weight: 700; margin: 0 0 0.5rem;
    letter-spacing: -0.01em;
}
.rowops-confirm-body { color: var(--rowops-ink-muted); margin-bottom: 1.25rem; font-size: 0.95rem; line-height: 1.4; }
.rowops-confirm-actions { display: flex; justify-content: flex-end; gap: 0.5rem; }
.rowops-confirm-actions button {
    border: 1px solid var(--rowops-border);
    background: transparent; color: var(--rowops-ink);
    padding: 0.4rem 0.9rem; border-radius: 0.375rem;
    font-weight: 600; font-size: 0.9rem; cursor: pointer;
}
.rowops-confirm-actions button.rowops-confirm-ok {
    background: var(--rowops-accent); color: var(--rowops-accent-fg);
    border-color: var(--rowops-accent);
}
.rowops-confirm-actions button.rowops-confirm-ok.is-danger {
    background: #dc2626; border-color: #dc2626; color: #fff;
}
.rowops-confirm-actions button:hover { filter: brightness(0.95); }
html.theme-sun .rowops-confirm-card, body.theme-sun .rowops-confirm-card {
    background: #000; color: #fff; border: 1px solid #fff;
}
html.theme-sun .rowops-confirm-actions button, body.theme-sun .rowops-confirm-actions button {
    color: #fff; border-color: #fff;
}
html.theme-sun .rowops-confirm-actions button.rowops-confirm-ok, body.theme-sun .rowops-confirm-actions button.rowops-confirm-ok {
    background: #facc15; color: #000; border-color: #facc15;
}

/* Lane-numbering convention note. Per RI Rule 2.12.2 ("Lanes always
   count from the Starters left") and WR bye-law to Rule 33 b)(ii):
   Lane 1 is on the Starter's left looking down the course. Surface
   on every operator + public surface where lane numbers appear, so
   visiting crews + spectators at an unfamiliar course aren't guessing. */
.lane-convention-note {
    display: inline-block; padding: 0.25rem 0.65rem;
    background: var(--rowops-surface-2);
    border: 1px solid var(--rowops-border);
    border-left: 3px solid var(--rowops-accent);
    border-radius: 0.3rem; font-size: 0.78rem;
    color: var(--rowops-ink); margin-bottom: 0.75rem;
}
.lane-convention-note .cite {
    color: var(--rowops-ink-muted); margin-left: 0.35rem;
    font-size: 0.72rem;
}
html.theme-sun .lane-convention-note, body.theme-sun .lane-convention-note {
    background: #000; color: #fff; border-color: #fff;
    border-left-color: #facc15;
}

/* ──────────────────────────────────────────────────────────────────
   Dashboard race-day cockpit (M5)
   ────────────────────────────────────────────────────────────────── */
.cockpit-tile {
    transition: transform 0.08s ease-out, box-shadow 0.08s ease-out;
    cursor: pointer;
}
.cockpit-tile:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
html.theme-sun .cockpit-tile, body.theme-sun .cockpit-tile {
    background: #000; color: #fff; border: 2px solid #facc15 !important;
}
html.theme-sun .cockpit-tile .text-body, body.theme-sun .cockpit-tile .text-body { color: #fff !important; }
html.theme-sun .cockpit-tile .text-muted, body.theme-sun .cockpit-tile .text-muted { color: #fde68a !important; }

/* ──────────────────────────────────────────────────────────────────
   Phase chevron — M1 of the 2026-05-26 dashboard redesign
   Renders inline in the regatta strip (templates/partials/phase_chevron.php).
   Salesforce-Path style horizontal track of 8 workflow phases. Past
   nodes are muted-green checks; current node is brand-red pulsing;
   future nodes are dim outlines with a help cursor + title tooltip.
   ────────────────────────────────────────────────────────────────── */
.phase-chevron {
    display: inline-flex;
    align-items: center;
    overflow-x: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;
}
.phase-chevron::-webkit-scrollbar { display: none; }
.phase-chevron-list {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    margin: 0; padding: 0;
    list-style: none;
}
.phase-sep {
    color: var(--rowops-ink-muted);
    font-size: 1rem;
    opacity: 0.55;
    padding: 0 0.05rem;
    -webkit-user-select: none;
    user-select: none;
}
.phase-node { display: inline-flex; }
.phase-link {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.85rem;
    border-radius: 999px;
    font-size: 0.86rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    color: var(--rowops-ink-muted);
    border: 1px solid var(--rowops-border);
    background: var(--rowops-surface);
    border-bottom: 1px solid var(--rowops-border);
    transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.phase-link:hover { color: var(--rowops-ink); background: var(--rowops-surface-2); border-color: var(--rowops-ink-muted); }
.phase-glyph {
    font-size: 0.92rem;
    line-height: 1;
}
.phase-label { line-height: 1.1; }

/* past — completed phases: readable green chip, clearly a clickable step */
.phase-node.is-past .phase-link {
    color: var(--rowops-good);
    border-color: var(--rowops-good);
    background: color-mix(in srgb, var(--rowops-good) 8%, var(--rowops-surface));
}
.phase-node.is-past .phase-link:hover {
    background: color-mix(in srgb, var(--rowops-good) 16%, var(--rowops-surface));
}
.phase-node.is-past .phase-glyph { color: var(--rowops-good); }

/* current — pulsing brand-red */
.phase-node.is-current .phase-link {
    background: var(--rowops-accent);
    color: var(--rowops-accent-fg);
    font-weight: 700;
}
.phase-node.is-current .phase-link:hover {
    background: var(--rowops-accent);
    color: var(--rowops-accent-fg);
    filter: brightness(1.08);
}
.phase-node.is-current .phase-glyph {
    color: var(--rowops-accent-fg);
    animation: phase-pulse 2.2s ease-in-out infinite;
}
@keyframes phase-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.55; }
}

/* future — dim, non-clickable, help cursor on hover (tooltip via title) */
.phase-node.is-future .phase-link.is-disabled {
    color: var(--rowops-ink-muted);
    opacity: 0.55;
    cursor: help;
}
.phase-node.is-future .phase-link.is-disabled:hover {
    background: transparent;
    color: var(--rowops-ink-muted);
}

/* Tablet + mobile — collapse labels, keep glyphs to preserve the
   track at narrow widths. < 768px hides labels; < 540px also hides
   separators so the track compacts to a row of glyphs. */
@media (max-width: 768px) {
    .phase-label { display: none; }
    .phase-link { padding: 0.15rem 0.3rem; gap: 0; }
    .phase-glyph { font-size: 0.85rem; }
}
@media (max-width: 540px) {
    .phase-sep { display: none; }
    .phase-chevron-list { gap: 0.25rem; }
}

/* Dark theme — re-anchor the past-green so it reads against #16202A
   without needing a different colour token. */
html.theme-dark .phase-node.is-past .phase-link,
body.theme-dark .phase-node.is-past .phase-link { color: #2dd4bf; }
html.theme-dark .phase-node.is-past .phase-glyph,
body.theme-dark .phase-node.is-past .phase-glyph { color: #2dd4bf; }

/* Sun mode — high-contrast outdoor variant. Borders + bigger glyphs.
   Yellow for current (matches start/finish buttons), white outline for
   past + future to stay readable in glare. */
html.theme-sun .phase-link, body.theme-sun .phase-link {
    border: 2px solid #fff;
    color: #fff;
    font-weight: 800;
    padding: 0.2rem 0.55rem;
}
html.theme-sun .phase-glyph, body.theme-sun .phase-glyph { font-size: 0.85rem; }
html.theme-sun .phase-node.is-past .phase-link,
body.theme-sun .phase-node.is-past .phase-link {
    color: #fff; border-color: #fff; opacity: 0.65;
}
html.theme-sun .phase-node.is-past .phase-glyph,
body.theme-sun .phase-node.is-past .phase-glyph { color: #fff; }
html.theme-sun .phase-node.is-current .phase-link,
body.theme-sun .phase-node.is-current .phase-link {
    background: #fde047; color: #000; border-color: #fff;
}
html.theme-sun .phase-node.is-current .phase-glyph,
body.theme-sun .phase-node.is-current .phase-glyph { color: #000; }
html.theme-sun .phase-node.is-future .phase-link.is-disabled,
body.theme-sun .phase-node.is-future .phase-link.is-disabled {
    color: #888; border-color: #444; opacity: 1;
}
html.theme-sun .phase-sep, body.theme-sun .phase-sep { color: #fff; opacity: 0.6; }

/* Single-source chevron wrapper — 2026-06-18. The workflow phase
   chevron now renders ONCE from templates/layout.php at the top of the
   content panel (above the hero) on every regatta- AND ladder-scoped
   page, instead of being included ad-hoc in ~6 templates. This wrapper
   gives it consistent top-of-panel spacing regardless of which chevron
   partial (regatta .phase-chevron / erg + beach .dash-chevron / ladder
   .phase-chevron) renders inside it. Day / dark / sun parity comes from
   the chevron partials' own theme rules above — the wrapper is layout
   spacing only, so it needs no per-theme override. */
.content-chevron {
    margin-bottom: var(--space-4);
}
.content-chevron:empty { display: none; }
/* The dash-style chevrons (erg + beach) already carry their own
   bottom margin; collapse the wrapper's so spacing stays even. */
.content-chevron .dash-chevron { margin-bottom: 0; }

/* Print — hide the chevron; it's operational chrome, not document
   content. Matches the existing nav.crumbs.no-print discipline. */
@media print {
    .phase-chevron { display: none !important; }
    .content-chevron { display: none !important; }
}

/* ──────────────────────────────────────────────────────────────────
   Operator home grid (templates/home.php) — 2026-05-26
   Phase-aware regatta cards with operational deep-links. Pairs the
   visual language of the unauth landing (home_public.php) with
   operator-only enrichment: phase pill, KPI strip, multi-link footer.
   This is the first surface every operator (and every SaaS prospect
   on a free trial) sees post-login, so it carries more visual
   weight than a Bootstrap table.
   ────────────────────────────────────────────────────────────────── */

.hp-hero {
    padding: 1.25rem 0 1rem;
}
.hp-hero h1 { font-weight: 700; letter-spacing: -0.02em; }
/* Higher specificity than the legacy centred `.hp-hero` further down the
   sheet so the operator landing reads left-aligned (heading + button on
   one row), while the public spectator landing keeps its centred hero. */
.hp-hero.hp-hero-operator {
    margin-bottom: 1.2rem;
    text-align: left;
}

.hp-section { margin-top: 1.5rem; }
.hp-section-title {
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--rowops-ink-muted); font-weight: 700;
    margin-bottom: 0.7rem; display: flex; align-items: center; gap: 0.5rem;
}
.hp-section-title .pulse {
    display: inline-block; width: 10px; height: 10px; border-radius: 50%;
    background: var(--rowops-bad, #c0392b);
    animation: hp-pulse 1.6s ease-in-out infinite;
}
@keyframes hp-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: 0.5; transform: scale(1.25); }
}

/* Card grid layouts. The unauth landing already defines .hp-grid via
   inline styles; we redefine + extend here so both landings share the
   pattern. Auto-fit + minmax keeps the grid responsive without media
   queries; the operator grid uses a slightly tighter minmax (310px)
   because the cards carry more density. */
.hp-grid {
    display: grid; gap: 0.9rem;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
/* auto-fill (not auto-fit) so a lone regatta card keeps its natural width
   on a wide screen instead of stretching across the whole row. */
.hp-grid-operator {
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
}

/* Live-now rail — only renders when ≥1 regatta is in race_day phase.
   Compact 2-3 column grid sized for at-a-glance scanning from across
   the room (operator on a tablet). */
.hp-live-grid {
    display: grid; gap: 0.9rem;
    grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
}
.hp-live-card {
    border-left: 4px solid var(--rowops-accent);
    transition: transform 0.12s ease-out, box-shadow 0.12s ease-out;
}
.hp-live-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 22px rgba(0, 0, 0, 0.08);
}
.hp-live-headline { margin-bottom: 0.85rem; }
.hp-live-stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.5rem;
    padding: 0.7rem 0.9rem;
    background: var(--rowops-surface-2);
    border-radius: 0.4rem;
    margin-bottom: 0.85rem;
}
.hp-live-cta-row {
    display: flex; flex-wrap: wrap; gap: 0.4rem;
}
.hp-live-cta-row .btn { flex: 1 1 auto; min-width: 100px; }

/* Operator-card layout — head row (title + phase pill) over a meta
   line over a 3-cell KPI strip, with a primary CTA + secondary
   link-row pinned at the bottom. Full card surface is hoverable so
   the operator can hit anywhere then hand off to a specific link. */
.hp-op-card {
    transition: transform 0.12s ease-out, box-shadow 0.16s ease-out, border-color 0.12s ease-out;
    height: 100%;
}
.hp-op-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
    border-color: var(--rowops-accent);
}
.hp-op-card .card-body {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    padding: 1rem 1.1rem;
}
.hp-op-card-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.6rem;
}
.hp-op-card-title {
    font-size: 1.05rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    line-height: 1.2;
    flex: 1 1 auto;
    min-width: 0;
}
.hp-op-card-title a:hover { color: var(--rowops-accent); }
.hp-op-phase-pill {
    flex-shrink: 0;
    font-size: 0.7rem;
    font-weight: 700;
    padding: 0.28rem 0.55rem;
    letter-spacing: 0.02em;
    white-space: nowrap;
}
.hp-op-meta {
    display: flex; flex-wrap: wrap; gap: 0.4rem 0.8rem;
    align-items: center;
}
.hp-op-stats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.5rem;
    padding: 0.6rem 0.8rem;
    background: var(--rowops-surface-2);
    border-radius: 0.4rem;
}
.hp-op-cta { margin-top: 0.3rem; }
.hp-op-links {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem 0.75rem;
    padding-top: 0.4rem;
    border-top: 1px solid var(--rowops-border);
    font-size: 0.82rem;
}
.hp-op-link {
    color: var(--rowops-ink-muted);
    text-decoration: none;
    border-bottom: 1px dotted transparent;
    padding: 0.05rem 0;
}
.hp-op-link:hover {
    color: var(--rowops-accent);
    border-bottom-color: currentColor;
}
.hp-op-link.is-highlight {
    color: var(--rowops-accent);
    font-weight: 600;
}

/* Empty-state — large friendly card for "no regattas yet". Saas-style
   first-run pitch, since brand-new tenants are increasingly common. */
.hp-empty-state .hp-empty-glyph {
    font-size: 3rem;
    line-height: 1;
    opacity: 0.6;
}

/* Theme parity */
html.theme-dark .hp-op-card:hover, body.theme-dark .hp-op-card:hover {
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
}
html.theme-dark .hp-live-card, body.theme-dark .hp-live-card {
    border-left-color: #ff7a66;
}
html.theme-sun .hp-op-card, body.theme-sun .hp-op-card {
    border: 3px solid #fff;
}
html.theme-sun .hp-op-card:hover, body.theme-sun .hp-op-card:hover {
    border-color: #fde047;
    transform: none;
    box-shadow: none;
}
html.theme-sun .hp-op-stats, body.theme-sun .hp-op-stats,
html.theme-sun .hp-live-stats, body.theme-sun .hp-live-stats {
    background: #000;
    border: 2px solid #fff;
}
html.theme-sun .hp-op-link, body.theme-sun .hp-op-link {
    color: #fff;
    font-weight: 700;
}
html.theme-sun .hp-op-link.is-highlight, body.theme-sun .hp-op-link.is-highlight {
    color: #fde047;
}
html.theme-sun .hp-live-card, body.theme-sun .hp-live-card {
    border: 3px solid #fff;
    border-left: 5px solid #fde047;
}

/* Print — hide hover transforms (operators don't print this surface,
   but kill any visual flutter just in case). */
@media print {
    .hp-op-card, .hp-live-card { transform: none !important; box-shadow: none !important; }
    .hp-section-title .pulse { animation: none; }
}

/* =================================================================
   UI redesign 2026-05-26 — clean-slate operator shell
   Persistent left rail + slim top bar + content panel. Best-in-class
   pattern (Linear / Stripe / Vercel / Cloudflare). Old `.navbar` +
   `.regatta-strip` rules above are retained for back-compat with
   any non-operator surface but the operator chrome now goes
   through .top-bar + .left-rail + .content-panel.
   ================================================================= */

/* --- Top bar ----------------------------------------------------- */
.top-bar {
    height: var(--top-bar-h);
    background: var(--rowops-card);
    border-bottom: 1px solid var(--rowops-border);
    position: sticky;
    top: 0;
    z-index: 50;
}
.top-bar-inner {
    height: 100%;
    max-width: none;
    padding: 0 var(--space-6);
    display: flex;
    align-items: center;
    gap: var(--space-3);
}
.top-bar-brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-weight: 700;
    font-size: 0.95rem;
    letter-spacing: -0.01em;
    color: var(--rowops-ink) !important;
    text-decoration: none;
    border-bottom: 0;
}
.top-bar-brand:hover { color: var(--rowops-accent) !important; }
.top-bar-brand .brand-mark {
    width: 24px; height: 24px;
    background: var(--rowops-accent);
    border-radius: 5px;
    flex-shrink: 0;
}
/* Brand / club logo image in the top bar (apex = rowOps mark, tenant =
   uploaded club logo). Height-constrained so any aspect ratio fits. */
.top-bar-brand .brand-logo {
    height: 26px; width: auto; max-width: 180px;
    display: block; flex-shrink: 0; border-radius: 4px;
}
/* Super-admin "acting as a tenant" context banner (impersonation). */
.superadmin-actas-banner {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--space-3);
    background: var(--brand-dark, #1f2a33); color: #f5f1e8;
    padding: 0.4rem 1rem; font-size: 0.88rem;
}
.superadmin-actas-banner a { color: var(--brand-accent, #c4543b); text-decoration: none; font-weight: 600; }
.superadmin-actas-banner a:hover { text-decoration: underline; }
.superadmin-actas-actions { display: flex; align-items: center; gap: var(--space-3); }
.superadmin-actas-stop {
    background: var(--brand-primary, #c4543b); color: #fff; border: 0;
    border-radius: 4px; padding: 0.25rem 0.7rem; font-size: 0.82rem; font-weight: 600; cursor: pointer;
}
.superadmin-actas-stop:hover { filter: brightness(1.1); }
/* Platform-wide announcement banner (super-admin set in /control_hub). Full
   width, square corners, centred — sits above the app shell on every page.
   Colour comes from the bootstrap .alert-info/-warning/-danger class. */
.platform-banner {
    display: flex; align-items: center; justify-content: center;
    gap: var(--space-3); text-align: center;
    border-radius: 0; border-left: 0; border-right: 0;
    padding: 0.5rem 1rem; font-size: 0.92rem;
}
.platform-banner-text { font-weight: 600; }
.platform-banner-close {
    background: none; border: 0; color: inherit; cursor: pointer;
    font-size: 1.25rem; line-height: 1; padding: 0 0.4rem; opacity: 0.7;
}
.platform-banner-close:hover { opacity: 1; }
@media print { .platform-banner { display: none !important; } }
.top-bar-tenant {
    color: var(--rowops-ink-muted);
    font-size: 0.85rem;
    padding-left: var(--space-3);
    border-left: 1px solid var(--rowops-border);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 22ch;
}
.top-bar-spacer { flex: 1 1 auto; }
.top-bar-utils {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}
.top-bar-user {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--rowops-ink-muted);
    font-size: 0.85rem;
    text-decoration: none;
    border-bottom: 0;
    padding: 0 var(--space-2);
    border-radius: var(--radius-sm);
}
.top-bar-user:hover { color: var(--rowops-ink); background: var(--rowops-surface-2); }
.top-bar-user-avatar {
    width: 26px; height: 26px;
    background: var(--rowops-accent);
    color: var(--rowops-accent-fg);
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 0.72rem;
}
.top-bar-user-name {
    max-width: 14ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Spectator top bar variant — same dimensions, neutral content. */
.top-bar-spectator .top-bar-inner { padding: 0 var(--space-4); }

/* --- App shell layout ------------------------------------------- */
.app-shell {
    display: block;
    min-height: calc(100vh - var(--top-bar-h));
}
.app-shell-with-rail {
    display: grid;
    grid-template-columns: var(--rail-w) 1fr;
    min-height: calc(100vh - var(--top-bar-h));
}

/* --- Left rail -------------------------------------------------- */
.left-rail {
    background: var(--rowops-surface);
    border-right: 1px solid var(--rowops-border);
    padding: var(--space-4) 0;
    position: sticky;
    top: var(--top-bar-h);
    align-self: start;
    height: calc(100vh - var(--top-bar-h));
    overflow-y: auto;
    scrollbar-width: thin;
}
.left-rail::-webkit-scrollbar { width: 6px; }
.left-rail::-webkit-scrollbar-thumb {
    background: var(--rowops-border-strong);
    border-radius: 3px;
}
.rail-head {
    padding: 0 var(--space-4) var(--space-3);
    border-bottom: 1px solid var(--rowops-border);
    margin-bottom: var(--space-3);
}
.rail-regatta {
    display: flex;
    flex-direction: column;
    gap: 2px;
    text-decoration: none;
    border-bottom: 0;
}
.rail-regatta:hover { background: transparent; }
.rail-regatta-name {
    color: var(--rowops-ink) !important;
    font-weight: 700;
    font-size: 0.9rem;
    letter-spacing: -0.01em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rail-regatta:hover .rail-regatta-name { color: var(--rowops-accent) !important; }
.rail-phase-tag {
    font-size: 0.65rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--rowops-ink-muted);
    font-weight: 700;
}

.rail-nav { padding: 0 var(--space-2); }

/* Collapsible group wrapper — <details> shell. Each group remembers
   its open/closed state in localStorage via layout.php inline JS.
   The current-phase group is always rendered open server-side and
   bypasses localStorage so the operator's eye lands on the active
   section on every page load. */
.rail-group-wrap {
    margin: var(--space-3) 0 var(--space-1);
}
.rail-group-wrap > summary { list-style: none; }
.rail-group-wrap > summary::-webkit-details-marker { display: none; }
.rail-group-wrap > summary::marker { display: none; }

/* The group heading itself — bigger, bolder, confidently sized
   compared to the old micro-label. Reads as a real section
   heading rather than an apologetic mouse-grey caption. */
.rail-group {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: 0.82rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--rowops-ink);
    font-weight: 800;
    padding: 6px 8px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    -webkit-user-select: none;
    user-select: none;
    transition: background 0.12s var(--ease-snappy), color 0.12s var(--ease-snappy);
}
.rail-group::after {
    content: "›";
    margin-left: auto;
    font-size: 1rem;
    color: var(--rowops-ink-muted);
    transition: transform 0.18s var(--ease-snappy);
    transform: rotate(90deg);
}
.rail-group-wrap:not([open]) > .rail-group::after {
    transform: rotate(0deg);
}
.rail-group:hover {
    background: var(--rowops-surface-2);
}
.rail-group:hover::after { color: var(--rowops-ink); }

/* Inside an open group the child list gets a tiny left inset so the
   visual hierarchy reads as "heading → indented items". */
.rail-group-wrap[open] > .rail-list {
    padding-left: var(--space-2);
    padding-bottom: var(--space-2);
    margin-top: 2px;
}

/* Current-phase group — accent ring on the LEFT edge of the whole
   group (heading + list) so the operator can scan vertically and
   land on "I'm in this phase". Heading text shifts to brand-red. */
.rail-group-wrap.is-current-phase {
    border-left: 3px solid var(--rowops-accent);
    padding-left: var(--space-1);
    margin-left: -2px;
    background: var(--rowops-accent-tint);
    border-radius: var(--radius-sm);
}
.rail-group-wrap.is-current-phase > .rail-group {
    color: var(--rowops-accent);
}
.rail-group-wrap.is-current-phase > .rail-group::after {
    color: var(--rowops-accent);
}
.rail-group-wrap.rail-group-danger > .rail-group {
    color: var(--rowops-bad);
}

.rail-list {
    list-style: none;
    margin: 0;
    padding: 0;
}
.rail-list li { margin: 0; }
.rail-link {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 6px var(--space-3);
    color: var(--rowops-ink-muted);
    text-decoration: none;
    font-size: 0.86rem;
    font-weight: 500;
    border-radius: var(--radius-sm);
    border-bottom: 0;
    position: relative;
    transition: background 0.12s var(--ease-snappy), color 0.12s var(--ease-snappy);
}
.rail-link:hover {
    background: var(--rowops-surface-2);
    color: var(--rowops-ink);
    border-bottom: 0;
}
.rail-link.is-active {
    background: var(--rowops-accent-tint);
    color: var(--rowops-accent);
    font-weight: 600;
}
.rail-link.is-active::before {
    content: "";
    position: absolute;
    left: -2px;
    top: 6px;
    bottom: 6px;
    width: 3px;
    background: var(--rowops-accent);
    border-radius: 2px;
}
.rail-ext {
    margin-left: auto;
    opacity: 0.5;
    font-size: 0.75rem;
}
.rail-link-danger {
    color: var(--rowops-bad);
}
.rail-link-danger:hover {
    background: rgba(176, 42, 55, 0.08);
    color: var(--rowops-bad);
}

/* --- Content panel ---------------------------------------------- */
.content-panel {
    padding: var(--space-6) var(--space-8) var(--space-12);
    max-width: 1400px;
    width: 100%;
    margin: 0 auto;
    background: transparent;
}
.content-panel-tenant {
    /* Tenant-wide pages (no rail) get a centred panel mirroring
       the operator content width. */
    max-width: 1200px;
}

/* The legacy .container max-width override is now bypassed inside
   .content-panel since the shell already constrains the working
   width. Override it back to behave naturally so nested .container
   blocks (legacy pages) don't punch outside the panel. */
.content-panel .container,
.content-panel-tenant .container {
    max-width: 100% !important;
    padding-left: 0;
    padding-right: 0;
}

/* --- App footer ------------------------------------------------- */
.app-footer {
    border-top: 1px solid var(--rowops-border);
    padding: var(--space-4) var(--space-6);
    color: var(--rowops-ink-muted);
    font-size: 0.78rem;
}
.app-footer-inner {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    align-items: baseline;
    max-width: 1400px;
    margin: 0 auto;
}
.app-footer a {
    color: var(--rowops-ink-muted);
    border-bottom: 0;
}
.app-footer a:hover { color: var(--rowops-accent); }

/* --- "Powered by rowOps" link ------------------------------------
   SaaS-stage footer rendered when BRAND_POWERED_BY_URL is set.
   Lives at the very end of <body> so it never disturbs operator
   workflow. Three-theme aware via var(--rowops-ink-muted). */
.rowops-powered-by {
    text-align: center;
    padding: 0.5rem 0;
    font-size: 0.75rem;
    color: var(--rowops-ink-muted);
}
.rowops-powered-by a {
    color: var(--rowops-ink-muted);
    text-decoration: none;
    border-bottom: 0;
}
.rowops-powered-by a:hover { color: var(--rowops-accent); }

/* Responsive collapse — tablet: rail icon-strip on demand,
   mobile: hidden entirely, dashboard cards drive nav. */
@media (max-width: 1100px) {
    .app-shell-with-rail {
        grid-template-columns: 1fr;
    }
    .left-rail {
        position: fixed;
        top: var(--top-bar-h);
        left: 0;
        width: var(--rail-w);
        background: var(--rowops-card);
        box-shadow: var(--rowops-shadow-lift);
        z-index: 40;
        transform: translateX(-100%);
        transition: transform 0.18s var(--ease-snappy);
    }
    body.rail-expanded .left-rail { transform: translateX(0); }
    .content-panel { padding: var(--space-4) var(--space-4) var(--space-8); }
}
@media (max-width: 768px) {
    .top-bar-inner { padding: 0 var(--space-3); }
    .top-bar-tenant { display: none; }
    .top-bar-user-name { display: none; }
    .content-panel { padding: var(--space-3); }
}

/* =================================================================
   Dashboard summary surfaces — templates/regatta_show.php
   ================================================================= */

/* Hero header */
.dash-hero {
    margin-bottom: var(--space-6);
    padding-bottom: var(--space-4);
    border-bottom: 1px solid var(--rowops-border);
}
.dash-hero-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
}
.dash-hero-titles { flex: 1 1 auto; min-width: 0; }
.dash-hero-name {
    font-size: 1.75rem;
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -0.02em;
    margin: 0 0 var(--space-2);
}
.dash-hero-meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1) var(--space-4);
    color: var(--rowops-ink-muted);
    font-size: 0.86rem;
    margin: 0;
}
.dash-hero-cta { flex: 0 0 auto; }
.dash-hero .phase-chevron { margin-top: var(--space-3); }

/* Active draw-job banner */
.banner {
    border: 1px solid var(--rowops-border);
    border-left: 4px solid var(--rowops-warn);
    border-radius: var(--radius-md);
    background: var(--rowops-card);
    margin-bottom: var(--space-4);
    box-shadow: var(--rowops-shadow);
}
.banner-warning { border-left-color: var(--rowops-warn); }
.banner-body {
    padding: var(--space-3) var(--space-4);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
}
.banner-headline { font-size: 1rem; font-weight: 600; margin: var(--space-1) 0; }
.banner-sub { color: var(--rowops-ink-muted); font-size: 0.82rem; }

/* Smart-guide toggle line */
.dash-smart-guide-toggle {
    text-align: right;
    margin-bottom: var(--space-2);
    font-size: 0.78rem;
}

/* KPI strip */
.dash-kpis {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}
.dash-kpi {
    display: block;
    padding: var(--space-3) var(--space-4);
    background: var(--rowops-card);
    border: 1px solid var(--rowops-border);
    border-radius: var(--radius-md);
    text-decoration: none;
    color: inherit;
    transition: border-color 0.12s var(--ease-snappy), transform 0.12s var(--ease-snappy);
    border-bottom: 0;
}
.dash-kpi:hover {
    border-color: var(--rowops-border-strong);
    transform: translateY(-1px);
    color: inherit;
    border-bottom: 0;
}
.dash-kpi-value-row {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    margin-top: var(--space-1);
}
.dash-kpi-value {
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: -0.02em;
}
.dash-kpi-sub {
    color: var(--rowops-ink-muted);
    font-size: 0.78rem;
    margin-top: var(--space-1);
}

/* Per-day issue + race-day-progress strips */
.dash-strip {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    padding: var(--space-2) var(--space-4);
    background: var(--rowops-card);
    border: 1px solid var(--rowops-border);
    border-radius: var(--radius-md);
    margin-bottom: var(--space-4);
    font-size: 0.86rem;
}
.dash-day-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 2px var(--space-2);
    border: 1px solid;
    border-radius: var(--radius-sm);
    font-weight: 600;
    text-decoration: none;
}
.dash-day-chip.is-error   { border-color: var(--rowops-bad);  color: var(--rowops-bad); }
.dash-day-chip.is-warning { border-color: var(--rowops-warn); color: var(--rowops-warn); }
.dash-day-chip.is-ok      { border-color: var(--rowops-good); color: var(--rowops-good); }
.dash-day-chip:hover { background: var(--rowops-surface-2); }

/* Action panel — the operator's working surface */
.dash-action-panel {
    margin: var(--space-6) 0;
}
.dash-section-head {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
    flex-wrap: wrap;
}
.dash-section-head h2 {
    font-size: 1rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    margin: 0;
}
.dash-section-hint {
    color: var(--rowops-ink-muted);
    font-size: 0.82rem;
}

.dash-action-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-3);
}
.dash-action-card {
    background: var(--rowops-card);
    border: 1px solid var(--rowops-border);
    border-radius: var(--radius-md);
    padding: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}
.dash-action-title {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 700;
    color: var(--rowops-ink-muted);
}
.dash-action-help {
    color: var(--rowops-ink-muted);
    font-size: 0.82rem;
    margin: 0;
}
.dash-action-divider {
    border-top: 1px solid var(--rowops-border);
    padding-top: var(--space-2);
    margin-top: var(--space-2);
    color: var(--rowops-ink-muted);
    font-size: 0.78rem;
    font-weight: 600;
}
.dash-action-button-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}
.dash-day-buttons {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}
.dash-day-button-group {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: 2px var(--space-1);
    border-radius: var(--radius-sm);
}
.dash-day-button-group.is-locked {
    background: rgba(220, 200, 80, 0.1);
}
.dash-inline-form {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

/* Race-day cockpit — big primary tiles */
.dash-race-day { background: transparent; }
.dash-cockpit-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-3);
    margin-bottom: var(--space-3);
}
.dash-cockpit-tile {
    background: var(--rowops-card);
    border: 2px solid var(--rowops-accent);
    border-radius: var(--radius-lg);
    padding: var(--space-8) var(--space-4);
    text-align: center;
    text-decoration: none;
    color: inherit;
    transition: transform 0.12s var(--ease-snappy), box-shadow 0.12s var(--ease-snappy);
    border-bottom: 2px solid var(--rowops-accent);
    min-height: 200px;
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.dash-cockpit-tile:hover {
    transform: translateY(-2px);
    box-shadow: var(--rowops-shadow-lift);
    color: inherit;
    border-bottom: 2px solid var(--rowops-accent);
}
.dash-cockpit-glyph {
    font-size: 3rem;
    line-height: 1;
    margin-bottom: var(--space-3);
    color: var(--rowops-accent);
}
.dash-cockpit-title {
    font-size: 1.4rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    margin-bottom: var(--space-1);
}
.dash-cockpit-sub { color: var(--rowops-ink-muted); font-size: 0.86rem; }
.dash-race-day-extras {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
    padding: var(--space-3) var(--space-4);
    background: var(--rowops-card);
    border: 1px solid var(--rowops-border);
    border-radius: var(--radius-md);
}

/* Publish panel */
.dash-publish-grid {
    display: grid;
    grid-template-columns: 2fr 1fr;
    gap: var(--space-4);
}
.dash-publish-list,
.dash-publish-side {
    background: var(--rowops-card);
    border: 1px solid var(--rowops-border);
    border-radius: var(--radius-md);
    padding: var(--space-4);
}
.dash-publish-row {
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--rowops-border);
}
.dash-publish-row:last-of-type { border-bottom: 0; }
.dash-publish-row-head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}
.dash-publish-row-spacer { flex: 1 1 auto; }
.dash-publish-row-help {
    color: var(--rowops-ink-muted);
    font-size: 0.78rem;
    margin-top: var(--space-1);
}
.dash-publish-row-days {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-top: var(--space-2);
    font-size: 0.82rem;
}
.dash-publish-row-days label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    margin: 0;
}
.dash-publish-side-title {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 700;
    color: var(--rowops-ink-muted);
    margin-bottom: var(--space-2);
}
.dash-publish-side .btn { margin-top: var(--space-1); }

/* Quick deep-links grid (tertiary nav) */
.dash-quick-links { margin: var(--space-6) 0; }
.dash-quick-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-3);
}
.dash-quick-card {
    background: var(--rowops-card);
    border: 1px solid var(--rowops-border);
    border-radius: var(--radius-md);
    padding: var(--space-4);
}
.dash-quick-title {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 700;
    color: var(--rowops-ink-muted);
    margin: 0 0 var(--space-2);
}
.dash-quick-card ul {
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: 0.88rem;
}
.dash-quick-card li {
    padding: 3px 0;
    border-bottom: 1px solid var(--rowops-border);
}
.dash-quick-card li:last-child { border-bottom: 0; }
.dash-quick-card a {
    color: var(--rowops-ink);
    text-decoration: none;
    border-bottom: 0;
    display: block;
}
.dash-quick-card a:hover { color: var(--rowops-accent); }

/* Danger zone */
.dash-danger {
    margin-top: var(--space-8);
    padding-top: var(--space-4);
    border-top: 1px solid var(--rowops-border);
}
.dash-danger-summary {
    color: var(--rowops-bad);
    font-weight: 700;
    cursor: pointer;
    font-size: 0.92rem;
}
.dash-danger-body {
    margin-top: var(--space-3);
    padding: var(--space-3) var(--space-4);
    background: rgba(176, 42, 55, 0.04);
    border: 1px solid rgba(176, 42, 55, 0.2);
    border-radius: var(--radius-md);
}

/* Responsive collapses for dashboard surfaces */
@media (max-width: 1100px) {
    .dash-action-grid,
    .dash-quick-grid { grid-template-columns: repeat(2, 1fr); }
    .dash-publish-grid { grid-template-columns: 1fr; }
}
@media (max-width: 768px) {
    .dash-kpis,
    .dash-action-grid,
    .dash-quick-grid,
    .dash-cockpit-grid { grid-template-columns: 1fr; }
    .dash-hero-name { font-size: 1.4rem; }
}

/* --- Sun mode parity for the new shell + dashboard --------------- */
html.theme-sun .top-bar, body.theme-sun .top-bar {
    background: #000;
    border-bottom: 3px solid #fff;
}
html.theme-sun .top-bar-brand, body.theme-sun .top-bar-brand { color: #fff !important; }
html.theme-sun .top-bar-brand .brand-mark, body.theme-sun .top-bar-brand .brand-mark { background: #fde047; }
html.theme-sun .top-bar-tenant, body.theme-sun .top-bar-tenant { color: #fde047; }
html.theme-sun .top-bar-user, body.theme-sun .top-bar-user { color: #fff; }
html.theme-sun .top-bar-user-avatar, body.theme-sun .top-bar-user-avatar {
    background: #fde047; color: #000;
}

html.theme-sun .left-rail, body.theme-sun .left-rail {
    background: #000;
    border-right: 3px solid #fff;
}
html.theme-sun .rail-link, body.theme-sun .rail-link {
    color: #fff;
    font-weight: 700;
}
html.theme-sun .rail-link.is-active, body.theme-sun .rail-link.is-active {
    background: #fde047;
    color: #000;
}
html.theme-sun .rail-link.is-active::before, body.theme-sun .rail-link.is-active::before {
    background: #fff;
}
html.theme-sun .rail-group, body.theme-sun .rail-group { color: #fff; }
html.theme-sun .rail-group.is-current-phase, body.theme-sun .rail-group.is-current-phase {
    color: #fde047;
    border-left-color: #fde047;
}

html.theme-sun .dash-kpi, body.theme-sun .dash-kpi,
html.theme-sun .dash-strip, body.theme-sun .dash-strip,
html.theme-sun .dash-action-card, body.theme-sun .dash-action-card,
html.theme-sun .dash-quick-card, body.theme-sun .dash-quick-card,
html.theme-sun .dash-publish-list, body.theme-sun .dash-publish-list,
html.theme-sun .dash-publish-side, body.theme-sun .dash-publish-side,
html.theme-sun .dash-race-day-extras, body.theme-sun .dash-race-day-extras,
html.theme-sun .banner, body.theme-sun .banner {
    background: #000;
    border: 3px solid #fff;
}
html.theme-sun .dash-cockpit-tile, body.theme-sun .dash-cockpit-tile {
    background: #000;
    border-color: #fde047;
}
html.theme-sun .dash-cockpit-glyph, body.theme-sun .dash-cockpit-glyph { color: #fde047; }
html.theme-sun .dash-cockpit-title, body.theme-sun .dash-cockpit-title { color: #fff; }

/* --- Dark mode parity -------------------------------------------- */
html.theme-dark .top-bar, body.theme-dark .top-bar {
    background: #0e1014;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
html.theme-dark .left-rail, body.theme-dark .left-rail {
    background: #16202A;
    border-right: 1px solid rgba(255, 255, 255, 0.08);
}
html.theme-dark .rail-link.is-active, body.theme-dark .rail-link.is-active {
    background: rgba(255, 122, 102, 0.12);
    color: #ff7a66;
}
html.theme-dark .rail-link.is-active::before, body.theme-dark .rail-link.is-active::before {
    background: #ff7a66;
}
html.theme-dark .dash-cockpit-tile, body.theme-dark .dash-cockpit-tile {
    border-color: #ff7a66;
}
html.theme-dark .dash-cockpit-glyph, body.theme-dark .dash-cockpit-glyph { color: #ff7a66; }

/* ──────────────────────────────────────────────────────────────────
   IC4 (2026-05-27) — Head-mode theme overrides
   Head-mode templates (templates/control/start_head.php +
   finish_head.php + head_finish_audit.php + head_*.php) define their
   selectors inline. CSS custom props (var(--rowops-surface-2), etc.)
   re-theme automatically; the explicit overrides below cover the
   state colours + accent backgrounds that don't follow the base
   custom-prop chain.

   Sun mode swap rule (operator-stated 2026-05-24): red text is
   unreadable in direct sun → jet-yellow #fde047 on solid black for
   accent backgrounds + state highlights; #fff base for body text.

   Night mode (dark) rule: red text → warm-orange #ff7a66 to match
   .dash-cockpit-tile pattern; amber/yellow → #f0c674; green → #6ed28b
   (matches existing .gap-spread-* dark overrides).
   ────────────────────────────────────────────────────────────────── */

/* --- Sun-mode parity for head-mode operator + admin surfaces ----- */

/* Division-tab strip — active tab is the brand-red highlight in light;
   jet-yellow chip on black in sun, with explicit white border so the
   inactive tabs (black bg) still read as distinct from the active. */
html.theme-sun .div-tab, body.theme-sun .div-tab {
    background: #000;
    border-color: #fff;
    color: #fff;
}
html.theme-sun .div-tab.is-active, body.theme-sun .div-tab.is-active {
    background: #fde047;
    color: #000;
    border-color: #fde047;
}

/* Bow-input card — accent border-left becomes jet-yellow; card body
   is black-on-white so the operator's typed bow numeral reads at
   distance. */
html.theme-sun .head-bow-card, body.theme-sun .head-bow-card {
    background: #000;
    border: 3px solid #fff;
    border-left: 4px solid #fde047;
}
html.theme-sun .head-bow-input, body.theme-sun .head-bow-input {
    background: #000;
    color: #fff;
    border-color: #fff;
}
html.theme-sun .head-bow-input:focus, body.theme-sun .head-bow-input:focus {
    outline-color: #fde047;
    border-color: #fde047;
}
html.theme-sun .head-bow-hint, body.theme-sun .head-bow-hint { color: #f0f0f0; }
html.theme-sun .head-bow-hint kbd, body.theme-sun .head-bow-hint kbd {
    background: #000; color: #fff; border-color: #fff;
}

/* Flash banners — red/green become jet-yellow on black for either
   tone, with a left-edge accent to distinguish ok vs err. */
html.theme-sun .head-bow-flash.is-ok, body.theme-sun .head-bow-flash.is-ok {
    background: #000;
    border: 3px solid #fde047;
    color: #fff;
}
html.theme-sun .head-bow-flash.is-err, body.theme-sun .head-bow-flash.is-err {
    background: #000;
    border: 3px solid #fde047;
    color: #fde047;
    font-weight: 800;
}

/* Countdown chip — fixed-mode interval cue on start_head.php. Sun
   keeps the existing jet-yellow .is-due cue but rebases the neutral
   baseline + overdue state to high-contrast B/W with a yellow border. */
html.theme-sun .head-countdown, body.theme-sun .head-countdown {
    background: #000;
    color: #fff;
    border: 2px solid #fff;
}
html.theme-sun .head-countdown.is-due, body.theme-sun .head-countdown.is-due {
    background: #fde047;
    color: #000;
    border-color: #fde047;
}
html.theme-sun .head-countdown.is-overdue, body.theme-sun .head-countdown.is-overdue {
    background: #000;
    color: #fde047;
    border-color: #fde047;
    font-weight: 800;
}

/* Queue table — row tint colours (rgba green/blue/grey) lose contrast
   on the sun-mode black baseline; swap to jet-yellow accent stripes. */
html.theme-sun .head-queue, body.theme-sun .head-queue { color: #fff; }
html.theme-sun .head-queue th, body.theme-sun .head-queue th { color: #fff; border-color: #fff; }
html.theme-sun .head-queue td, body.theme-sun .head-queue td { border-color: #fff; }
html.theme-sun .head-queue tr.is-released, body.theme-sun .head-queue tr.is-released {
    background: rgba(253, 224, 71, 0.18);
}
html.theme-sun .head-queue tr.is-called, body.theme-sun .head-queue tr.is-called {
    background: rgba(253, 224, 71, 0.10);
}
html.theme-sun .head-queue tr.is-finished, body.theme-sun .head-queue tr.is-finished {
    background: rgba(255, 255, 255, 0.10);
    color: #f0f0f0;
}

/* Right-rail card + recent list. */
html.theme-sun .head-rail-card, body.theme-sun .head-rail-card {
    background: #000;
    border: 3px solid #fff;
}
html.theme-sun .head-rail-card h2, body.theme-sun .head-rail-card h2 { color: #fff; }
html.theme-sun .head-recent li, body.theme-sun .head-recent li {
    border-bottom-color: #fff;
}
html.theme-sun .head-recent .r-bow, body.theme-sun .head-recent .r-bow { color: #fff; }
html.theme-sun .head-recent .r-name, body.theme-sun .head-recent .r-name { color: #fff; }
html.theme-sun .head-recent .r-club, body.theme-sun .head-recent .r-club { color: #f0f0f0; }
html.theme-sun .head-recent .r-meta, body.theme-sun .head-recent .r-meta { color: #f0f0f0; }
html.theme-sun .head-recent .r-pos, body.theme-sun .head-recent .r-pos {
    background: #fde047; color: #000;
}

/* Audit-mode action pills — head_finish_audit.php uses per-action
   colour pills (release/call/recall/finish/undo). Sun-mode flattens
   them to jet-yellow-on-black so they read as same-class tags;
   semantic distinction lives in the textual label. */
html.theme-sun .action-pill, body.theme-sun .action-pill {
    background: #000;
    color: #fde047;
    border: 1px solid #fde047;
    font-weight: 800;
}
html.theme-sun .audit-table tr.dup-bright, body.theme-sun .audit-table tr.dup-bright {
    background: rgba(253, 224, 71, 0.18);
}
html.theme-sun .audit-table tr.dup-soft, body.theme-sun .audit-table tr.dup-soft {
    background: rgba(253, 224, 71, 0.08);
}

/* --- Night-mode (dark) parity for head-mode surfaces ------------- */

/* Brand-red accent backgrounds → warm-orange on dark slate (matches
   .dash-cockpit-tile and .gap-spread-tight pattern lines 365-379 and
   1962-1972). */
html.theme-dark .div-tab, body.theme-dark .div-tab {
    background: var(--rowops-surface-2);
    border-color: var(--rowops-border);
    color: var(--rowops-ink);
}
html.theme-dark .div-tab.is-active, body.theme-dark .div-tab.is-active {
    background: rgba(255, 122, 102, 0.18);
    color: #ff7a66;
    border-color: #ff7a66;
}

html.theme-dark .head-bow-card, body.theme-dark .head-bow-card {
    border-left-color: #ff7a66;
}
html.theme-dark .head-bow-flash.is-ok, body.theme-dark .head-bow-flash.is-ok {
    color: #6ed28b;
    border-color: rgba(110, 210, 139, 0.55);
}
html.theme-dark .head-bow-flash.is-err, body.theme-dark .head-bow-flash.is-err {
    color: #ff7a66;
    border-color: rgba(255, 122, 102, 0.55);
}

html.theme-dark .head-countdown.is-overdue, body.theme-dark .head-countdown.is-overdue {
    background: rgba(255, 122, 102, 0.12);
    color: #ff7a66;
    border-color: rgba(255, 122, 102, 0.55);
}
/* .head-countdown.is-due keeps its jet-yellow baseline — it's a cue,
   not a warning, and reads correctly on dark slate. */

/* Action pills carry baked-in state colours that need a dark-mode
   rebase so dark-on-light pills don't read as washed-out. */
html.theme-dark .action-pill.release, body.theme-dark .action-pill.release { color: #6ed28b; }
html.theme-dark .action-pill.call, body.theme-dark .action-pill.call { color: #7fb6ff; }
html.theme-dark .action-pill.recall, body.theme-dark .action-pill.recall { color: #f0c674; }
html.theme-dark .action-pill.finish, body.theme-dark .action-pill.finish { color: #6ed28b; }
html.theme-dark .action-pill.undo, body.theme-dark .action-pill.undo { color: var(--rowops-ink-muted); }

/* Audit dup-row tints (red rgba on dup-bright) need a warm-orange
   rebase so they still pop on dark slate. */
html.theme-dark .audit-table tr.dup-bright, body.theme-dark .audit-table tr.dup-bright {
    background: rgba(255, 122, 102, 0.12);
}
html.theme-dark .audit-table tr.dup-soft, body.theme-dark .audit-table tr.dup-soft {
    background: rgba(255, 122, 102, 0.05);
}

/* ──────────────────────────────────────────────────────────────────
   Per-regatta logo strip (mig 0055).
   ─────────────────────────────────────────────────────────────── */
.regatta-logo-strip {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 0.5rem 1rem;
    margin-bottom: 1rem;
    background: var(--rowops-surface-2);
    border: 1px solid var(--rowops-border);
    border-radius: 0.4rem;
}
.regatta-logo-strip img {
    max-height: 72px;
    max-width: min(100%, 480px);
    height: auto;
    width: auto;
    object-fit: contain;
}
.regatta-logo-preview {
    max-height: 120px;
    max-width: 320px;
    height: auto;
    width: auto;
    object-fit: contain;
    padding: 0.5rem;
    background: var(--rowops-surface-2);
    border: 1px solid var(--rowops-border);
    border-radius: 0.4rem;
}
html.theme-sun .regatta-logo-strip,
body.theme-sun .regatta-logo-strip,
html.theme-sun .regatta-logo-preview,
body.theme-sun .regatta-logo-preview {
    background: #000;
    border-color: #fde047;
}
html.theme-dark .regatta-logo-strip,
body.theme-dark .regatta-logo-strip,
html.theme-dark .regatta-logo-preview,
body.theme-dark .regatta-logo-preview {
    background: var(--rowops-surface-2);
    border-color: var(--rowops-border);
}

/* --- Sponsor strip (spec 03) ------------------------------------- */
.sponsor-strip {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    justify-content: center;
    padding: 0.4rem 1rem;
    margin-bottom: 1rem;
    background: var(--rowops-surface-2);
    border: 1px solid var(--rowops-border);
    border-radius: 0.4rem;
}
.sponsor-strip-label {
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--rowops-muted);
}
.sponsor-strip-logos {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    flex-wrap: wrap;
    justify-content: center;
}
.sponsor-item { display: inline-flex; align-items: center; }
.sponsor-item img {
    max-height: 46px;
    max-width: min(40vw, 200px);
    height: auto;
    width: auto;
    object-fit: contain;
}
.sponsor-tier-title img { max-height: 60px; }
.sponsor-intro { max-width: 48rem; }
.sponsor-admin-thumb { max-height: 38px; max-width: 120px; object-fit: contain; }
html.theme-sun .sponsor-strip,
body.theme-sun .sponsor-strip { background: #000; border-color: #fde047; }
html.theme-dark .sponsor-strip,
body.theme-dark .sponsor-strip { background: var(--rowops-surface-2); border-color: var(--rowops-border); }

/* --- Print suppresses chrome ------------------------------------- */
@media print {
    .top-bar, .left-rail, .app-footer, .rowops-powered-by { display: none !important; }
    .app-shell-with-rail { display: block; }
    .content-panel { padding: 0; max-width: none; }
}

/* ──────────────────────────────────────────────────────────────────
   CSP `style-src 'unsafe-inline'` cleanup (ROADMAP HIGH-3, 2026-05-29)
   ────────────────────────────────────────────────────────────────────
   These utilities replace the last ~238 inline `style="..."` attribute
   sites across templates so the CSP header can drop `'unsafe-inline'`
   from `style-src`. Defence-in-depth — every output already goes
   through `Support\e()`, but if a hostile string slipped past escape,
   a `<style>` injection cannot land.
   Naming follows the existing project pattern: intent-named, kebab-
   case, no visual aliases. Reach for an existing utility (`is-hidden`,
   `text-accent`, `kpi-label`, etc.) before adding a new one here.
   ──────────────────────────────────────────────────────────────── */

/* Width helpers for `<th>` column sizing in operator + print tables. */
.col-w-20  { width: 20px; }
.col-w-30  { width: 30px; }
.col-w-55  { width: 55px; }
.col-w-65  { width: 65px; }
.col-w-40  { width: 40px; }
.col-w-50  { width: 50px; }
.col-w-60  { width: 60px; }
.col-w-70  { width: 70px; }
.col-w-80  { width: 80px; }
.col-w-90  { width: 90px; }
.col-w-100 { width: 100px; }
.col-w-110 { width: 110px; }
.col-w-120 { width: 120px; }
.col-w-130 { width: 130px; }
.col-w-140 { width: 140px; }
.col-w-160 { width: 160px; }
.col-w-165 { width: 165px; }
.col-w-170 { width: 170px; }
.col-w-175 { width: 175px; }
.col-w-200 { width: 200px; }
.col-w-240 { width: 240px; }
.col-w-260 { width: 260px; }
.col-w-pct-25  { width: 25%; }
.col-w-pct-30  { width: 30%; }
.col-w-pct-32  { width: 32%; }
.col-w-pct-50  { width: 50%; }

/* Print-table proportional column widths — % column widths used inside
   the availability-windows + per-club print sheets. */
.col-w-pct-7   { width: 7%; }
.col-w-pct-8   { width: 8%; }
.col-w-pct-9   { width: 9%; }
.col-w-pct-12  { width: 12%; }
.col-w-pct-17  { width: 17%; }
.col-w-pct-22  { width: 22%; }
.col-w-pct-24  { width: 24%; }
.col-w-pct-25-share { width: 25%; }   /* alias kept distinct so a 25% tweak in availability sheets lands in one place */
.col-w-pct-28  { width: 28%; }

/* Em-sized table column widths — used on bow / state / actions columns
   on the head-mode start + finish tables where em scales with the
   surrounding font-size for tablet readability. */
.col-w-em-3   { width: 3.2em; }
.col-w-em-8   { width: 8em; }

/* Rem-sized table column widths — narrow # / position columns. */
.col-w-3rem  { width: 3rem; }
.col-w-6rem  { width: 6rem; }
.col-w-7rem  { width: 7rem; }
.col-w-8rem  { width: 8rem; }
.col-w-11rem { width: 11rem; }

/* Amber card-accent variant — pairs with `.card` to render a 4px
   warning-coloured left border. Mirrors `.card.card-accent-bar` but
   keys off Bootstrap's `--bs-warning` instead of the brand accent. */
.card.card-accent-warning {
    border-left: 4px solid var(--bs-warning);
}

/* Operator-displayed secret block (device pair token, share-link URL,
   etc.) — soft-wrapping monospace card with surface-2 background. */
.token-display {
    white-space: pre-wrap;
    word-break: break-all;
    padding: 0.75rem;
    background: var(--rowops-surface-2);
    border-radius: 0.5rem;
}

/* Dashed-border helper — used to visually signal optional / opt-in
   form sections (e.g. delegate details on the ladder entry form). */
.border-dashed { border-style: dashed; }

/* Centred emoji / glyph hero on status pages — e.g. the 🔒 on the
   timing-locked screen. Pairs with `.text-center` on the parent card. */
.icon-hero { font-size: 3rem; line-height: 1; margin-bottom: 0.75rem; }

/* Mid-size icon — for inline emoji glyphs that need ~2× body text
   (e.g. Logbook upsell card). */
.icon-md { font-size: 2rem; }

/* Spectator-screen muted text — soft-white on the black background.
   Used for boat-class suffixes / metadata on the projector views. */
.spectator-meta { color: rgba(255, 255, 255, 0.55); font-weight: 500; }

/* Form-help text max-width caps — narrow caption text under a wider
   form control should not stretch the full container width. */
.form-help-mw-260 { max-width: 260px; }

/* Em-sized min-widths for inline counter / icon slots inside list rows
   (smart-guide steps, etc.) — keeps numbers / glyphs from collapsing. */
.col-min-em-1-25 { min-width: 1.25em; }
.col-min-em-1-5  { min-width: 1.5em;  }

/* Very-subtle row background — used to faintly differentiate stacked
   list items without imposing a heavy stripe. 1.5% black on light,
   inverse on dark via theme-aware overrides isn't required because
   the alpha is too low for the difference to read. */
.bg-row-subtle { background: rgba(0, 0, 0, 0.015); }

/* Inline chip-style highlight for "locked" day buttons on the draw-
   decisions toolbar — soft amber wash + tight rounded corners. */
.day-locked-chip {
    background: rgba(220, 200, 80, 0.08);
    border-radius: 0.3rem;
    padding: 0 0.2rem;
}

/* Day-locked badge variant for tabbed labels — slightly larger
   padding + rounded corners than the toolbar chip, used on
   /day-assignments tab strip. */
.day-locked-tab {
    background: rgba(220, 200, 80, 0.08);
    border-radius: 0.35rem;
    padding: 0.1rem 0.3rem;
}

/* Microcaps label tag — small uppercase wrap with widened tracking.
   Pair with `.small` and `.text-muted` for the typical render. */
.text-tracked { letter-spacing: 0.05em; }

/* Honeypot field — visually hidden, screen-readable. Bots fill it and
   the server discards the submission. Off-screen rather than display:
   none so accessibility tools and naïve bots still see the field. */
.honeypot {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
}

/* Soft-info wash for opt-in form sections (e.g. Logbook upsell card,
   Concept2 connect prompt) — Bootstrap-info colour at low alpha so the
   section reads as "extra" without competing with the primary fields. */
.bg-info-soft { background: rgba(13, 110, 253, 0.04); }

/* Event-grid relay badge — RELAY · N legs marker on the erg event
   tiles. Bootstrap-info at 15% alpha + the source-of-truth blue. */
.event-badge-relay {
    background: rgba(13, 110, 253, 0.15);
    color: #0d6efd;
}

/* Section-heading microcaps — small uppercase label above grouped
   forms (e.g. "umpire decisions" header on /control/run). */
.text-tag-upper {
    letter-spacing: 0.05em;
    font-size: 0.75rem;
}

/* Sticky composer card on operator broadcast surfaces (WhatsApp
   captains). Top offset keeps it just below the global top bar. */
.sticky-top-tight {
    position: sticky;
    top: 0.5rem;
    z-index: 5;
}

/* Reserved minimum height — keeps a row's vertical space stable when
   the status message inside is empty (avoids layout jank on flash). */
.h-min-1-1em { min-height: 1.1em; }

/* Dark-grey print intro paragraph — between `.text-muted` (too light
   on print) and body text. Used under section headings on print
   sheets to give "small caption" body context. */
.text-print-meta {
    color: #444;
    font-size: 0.88rem;
    margin-bottom: 0.4rem;
}

/* Min-width zero — lets a flex child shrink past its content's intrinsic
   min-width (otherwise long crew names overflow inside flex rows).
   Bootstrap 5.2+ ships this; we ship it ourselves so older Bootstrap
   bundles also have it available. */
.min-w-0 { min-width: 0; }

/* Empty-state padded card body — used when the primed-race slot has
   no pending races to show. Visually balanced large-vertical padding. */
.primed-card-empty {
    text-align: center;
    padding: 3rem 1.5rem;
}

/* Lock-bar fill initial state — full width before the JS countdown
   animation kicks in. Pairs with `.lock-bar-fill`. */
.lock-bar-fill-init { width: 100%; }

/* Camera preview tiles on the race-control-settings page. Two sizes
   — medium (calibration card) + large (live preview). Both share the
   black background + soft-cropped border-radius. Hidden by default;
   toggled visible via .d-none class swap when the preview starts. */
.camera-preview-md,
.camera-preview-lg {
    width: 100%;
    background: #000;
    border-radius: 0.4rem;
}
.camera-preview-md { max-width: 480px; max-height: 270px; }
.camera-preview-lg { max-width: 720px; max-height: 405px; }

/* Finish-line preview wrapper — relative-positioned for the overlay,
   sized to fit the largest preview. */
.fl-preview-wrap {
    display: inline-block;
    position: relative;
    max-width: 100%;
}

/* In-page JS overlay flash background — semi-opaque dark wash on
   inline code in the recall / cancel toast templates. */
.flash-code-wash {
    background: rgba(0, 0, 0, 0.25);
    padding: 0 0.3rem;
    border-radius: 0.2rem;
}

/* Small sub-text inside a flash toast — slightly faded relative to
   the main message but still readable. */
.flash-subtext {
    opacity: 0.85;
    display: block;
    margin-top: 0.2rem;
}

/* Reason text inside the recall flash — slightly larger than caption,
   slightly faded. */
.flash-reason {
    font-size: 0.9rem;
    opacity: 0.95;
}

/* Danger-coloured meta line under starter-dashboard primed cards. */
.meta-line-danger { color: var(--rowops-danger, #c53030); }

/* Form-input width helpers — replace `style="max-width:Xrem"` /
   `style="width:Xrem"` on inline filter / reason / fee inputs.
   The `-` prefix variants apply `max-width` (caps growth without
   pinning narrow on responsive layouts); the bare ones apply
   `width` directly. */
.input-w-3rem  { width: 3rem; }
.input-w-5rem  { width: 5rem; }
.input-w-60   { width: 60px; }
.input-w-80   { width: 80px; }
.input-w-140  { width: 140px; }
.input-mw-5-5rem  { max-width: 5.5rem; }
.input-mw-6rem    { max-width: 6rem; }
.input-mw-8rem    { max-width: 8rem; }
.input-mw-10rem   { max-width: 10rem; }
.input-mw-11rem   { max-width: 11rem; }
.input-mw-12rem   { max-width: 12rem; }
.input-min-130    { min-width: 130px; }
.input-min-140    { min-width: 140px; }
.input-min-220    { min-width: 220px; }
.input-min-240    { min-width: 240px; }
.input-min-280    { min-width: 280px; }
.input-min-260    { max-width: 260px; }

/* Card max-width helpers — used on the standalone-page card layouts
   (regatta_new, regatta_edit, ladder_new, etc). Form cards traditionally
   pinned at 560/680/720/780/800 px. */
.card-mw-560 { max-width: 560px; }
.card-mw-640 { max-width: 640px; }
.card-mw-680 { max-width: 680px; }
.card-mw-720 { max-width: 720px; }
.card-mw-780 { max-width: 780px; }
.card-mw-800 { max-width: 800px; }
.card-mw-960 { max-width: 960px; }

/* Standalone error / forbid pages (rendered inline from controller hot
   paths — no layout chrome). Mirrors the operator card pattern; lives
   in app.css so the inline-style CSP test stays at zero offenders. */
.forbid-page { max-width: 640px; }
.forbid-glyph { font-size: 3rem; line-height: 1; }

/* cm-finish pixel-review tool (2026-06-02) — the canvas overlays the
   snapshot image with a 1:1 coordinate mapping. The image flows
   normally; the canvas sits absolutely on top so click coordinates
   translate cleanly to pixel positions in the underlying frame. */
.cm-review-frame-wrap {
    position: relative;
    display: inline-block;
    max-width: 100%;
}
.cm-review-frame-wrap img {
    display: block;
    max-width: 100%;
    height: auto;
}
.cm-review-frame-wrap canvas {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    cursor: crosshair;
}

/* Input-group max-width for the search / lookup widgets on dashboards. */
.input-group-mw-480 { max-width: 480px; }
.input-group-mw-520 { max-width: 520px; }
.input-group-mw-640 { max-width: 640px; }

/* USB-stick + erg-application + import-log secondary column min-widths. */
.col-min-18rem { min-width: 18rem; }

/* Audit-log word-break + max-width on context cells. */
.cell-wrap-200 { max-width: 200px; word-break: break-word; }
.cell-wrap-420 { max-width: 420px; word-break: break-word; }

/* Generic monospace + break-all (URL display, DNS records, share URLs). */
.font-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.font-mono-sm { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.95rem; }
.break-all { word-break: break-all; }
.nowrap { white-space: nowrap; }
.text-end-right { text-align: right; }
.cursor-pointer { cursor: pointer; }
.cursor-help { cursor: help; }
.cursor-crosshair { cursor: crosshair; }

/* Small-text / micro-label utility. The dashboard uses `.kpi-label`
   for the proper micro-label pattern; `.text-xs` is the tiny-text
   utility used on inline meta + view-source summaries. */
.text-xs { font-size: 0.72rem; }
.text-xs-bold { font-size: 0.72rem; font-weight: 700; }
.text-2xs { font-size: 0.65rem; }
.text-sm { font-size: 0.78rem; }
.text-md-narrow { font-size: 0.82rem; }
.text-rem-09 { font-size: 0.9rem; }
.text-rem-095 { font-size: 0.95rem; }
.text-rem-105 { font-size: 1.05rem; }
.text-bold-500 { font-weight: 500; }
.text-bold-600 { font-weight: 600; }
.text-bold-700 { font-weight: 700; }
.text-italic { font-style: italic; }

/* Status-line min-height — prevents reflow when an inline success / error
   message paints under a card body. */
.status-line-min { min-height: 1.1em; }

/* Min-height helpers for the various copy-status / save-status spots. */
.line-height-narrow { line-height: 1.35; }
.line-height-tight { line-height: 1.2; }
.line-height-roomy { line-height: 1.5; }

/* Inline-form flex helpers — used inside ladder_me / smart_guide / etc
   where a 1fr+auto two-cell layout is needed inside a row. */
.flex-grow { flex: 1 1 auto; min-width: 0; }
.flex-1   { flex: 1; }
.min-w-220 { min-width: 220px; }
.min-w-1-5em { min-width: 1.5em; }
.min-w-1-25em { min-width: 1.25em; }

/* Word-break / nowrap on table cells + share-URL code blocks. */
.code-break-all { word-break: break-all; }
.cell-nowrap { white-space: nowrap; }

/* Margin-left helpers — chip / inline-badge separation. */
.ms-mini { margin-left: 0.3rem; }
.ms-tight { margin-left: 0.4rem; }
.ms-room { margin-left: 0.6rem; }

/* Padding helpers used inside notes panels. */
.p-0-4 { padding: 0.4rem; }
.pl-0 { padding-left: 0; }
.section-intro-tight { margin: 0 0 0.35rem; }
.gap-mt { margin-top: 0.3rem; }
.alert-radius { border-radius: 0.4rem; }
.notes-gap { padding: 0.4rem; }

/* Camera + zoom-slider visibility helpers — these elements start hidden
   and JS toggles them on user action. `.is-revealed` is a no-op pair
   for symmetry. */
.is-hidden-revealable { display: none; }

/* Video preview frames — race-control-settings page two-sized previews. */
.cam-preview-480 {
    width: 100%; max-width: 480px; max-height: 270px;
    background: #000; border-radius: 0.4rem; display: none;
}
.cam-preview-720 {
    width: 100%; max-width: 720px; max-height: 405px;
    background: #000; border-radius: 0.4rem; display: none; cursor: crosshair;
}
.fl-preview-wrap {
    display: inline-block; position: relative; max-width: 100%;
}

/* Centered hero-style primed card on starter dashboard when no race
   is queued — large padding + centered text so the empty-state reads
   from the tablet rest position. */
.primed-card-empty {
    text-align: center;
    padding: 3rem 1.5rem;
}

/* Day-block locked highlight — replaces conditional inline style on
   day_assignments + draw_decisions when per-day draw lock is active. */
.day-locked-highlight {
    background: rgba(220, 200, 80, 0.08);
    border-radius: 0.35rem;
    padding: 0.1rem 0.3rem;
}
.day-locked-highlight-mini {
    background: rgba(220, 200, 80, 0.08);
    border-radius: 0.3rem;
    padding: 0 0.2rem;
}

/* Lock-bar fill — used on starter dashboard's GONE state for the 60 s
   recall-window countdown. Initial width = 100% via the class; JS
   animates via direct `el.style.width` updates which are CSP-safe. */
.lock-bar-fill-init { width: 100%; }

/* Sticky positioning for the WhatsApp composer + similar always-visible
   on-page cards. */
.sticky-top-card {
    position: sticky;
    top: 0.5rem;
    z-index: 5;
}

/* Pre-block formatting for code-token panes (pair-token, share URL). */
.pre-code-block {
    white-space: pre-wrap;
    word-break: break-all;
    padding: 0.75rem;
    background: var(--rowops-surface-2);
    border-radius: 0.5rem;
}

/* Visually-hidden honeypot field — keeps the form input in the DOM
   for attack bots to find while never showing on screen. Mirrors the
   classic Bootstrap `.visually-hidden` but positioned far off-screen
   so even autofill doesn't reveal it. */
.honeypot-field {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
}

/* Border-left accent variants used on warning + brand cards. */
.card-warning-accent { border-left: 4px solid var(--rowops-warn); }
.card-accent-strong { border-left: 4px solid var(--rowops-accent); }

/* Ladder-new card-template picker — `<label class="card">` becomes a
   tappable selection card; cursor: pointer makes the affordance clear. */
.card-template-picker { cursor: pointer; }

/* Dashed-border fieldset (logbook upsell + ladder-enter team panel). */
.fieldset-dashed { border-style: dashed; }
.fieldset-dashed-info { border-style: dashed; background: rgba(13, 110, 253, 0.04); }

/* Erg-mode glyph + flex-stack used on the entry form's logbook card. */
.glyph-2rem { font-size: 2rem; }
.flex-stack-info-grow { flex: 1; min-width: 220px; }
.flex-grow-base { flex: 1; }

/* erg-enter relay/event chip — blue-tinted pill background. */
.event-gender-relay {
    background: rgba(13, 110, 253, 0.15);
    color: #0d6efd;
}

/* erg-enter masters-only options info banner — small top-margin. */
.alert-tight-top { margin-top: 0.3rem; }
.row-align-end { align-items: end; }

/* Substitute page edit-icon — tiny inline link size. */
.icon-link-mini { font-size: 0.78rem; }
.help-narrow-260 {
    font-size: 0.72rem;
    line-height: 1.2;
    max-width: 260px;
}

/* Timing-locked + comm modal centre column. */
.modal-card-centre {
    border-width: 2px;
    max-width: 720px;
    margin: 2rem auto;
}
.glyph-3rem {
    font-size: 3rem;
    line-height: 1;
    margin-bottom: 0.75rem;
}

/* Print-rationale + affected-clubs subdued meta text. */
.print-meta-tight {
    color: #444;
    font-size: 0.88rem;
    margin-bottom: 0.4rem;
}
.print-emdash { color: #888; }
.print-emdash-strong { color: #999; }
.print-emdash-weak { color: #888; font-size: 0.82rem; }
.print-emdash-italic { color: #888; }
.print-section-count {
    color: #888; font-weight: 400; font-size: 0.85rem;
}

/* Spectator screen / spectator dashboard small-text muted variants
   (translucent over a dark backdrop). */
.spectator-meta-faint { color: rgba(255, 255, 255, 0.55); font-weight: 500; }
.spectator-meta-soft { opacity: 0.7; }

/* Spectator big-screen lane bar dynamic width — JS sets the inline
   custom property `--es-fill` on each row; class hooks it. */
.es-lane-bar-dynamic { width: var(--es-fill, 0%); }

/* Smart-guide list-item highlight + tabular index column. */
.sg-row-highlight { background: rgba(0, 0, 0, 0.015); }
.sg-row-tabular { min-width: 1.5em; }
.sg-row-icon { min-width: 1.25em; }

/* Pill margin-left for FYI marker on affected-clubs print. */
.pill-fyi-spacing { margin-left: 0.3rem; }

/* Audit gap-list inside affected-clubs print. */
.gap-list-tight {
    margin: 0.3rem 0 0;
    padding-left: 1.1rem;
    font-size: 0.78rem;
    line-height: 1.35;
}
.gap-list-numeric { font-variant-numeric: tabular-nums; }

/* Race controls notes column empty state. */
.rc-empty-pad { padding: 0.4rem; }
.alert-radius-tight { border-radius: 0.4rem; }
.text-meta-tiny { font-size: 0.75rem; }

/* Lane convention 100% start_list table. */
.lane-table-full { width: 100%; }

/* Recently-finished compact meta. */
.text-rem-07 { font-size: 0.7rem; font-weight: 500; }

/* Camera-tally code label — small monospace badge under finish board. */
.snap-tally-mono { font-family: ui-monospace, Menlo, Consolas, monospace; }
.deploy-mark    { font-size: 0.7rem; }

/* Spectator-screen layout helpers for the small-meta line + label. */
.club-margin-left { margin-left: 0.4rem; }
.label-margin-top { margin-top: 0.4rem; }

/* Letter-spacing helper used by day_assignments section headings. */
.letter-spaced-05 { letter-spacing: 0.05em; }

/* Border accent for the per-row refine-draw warning card. */
.refine-warn-accent { border-left: 4px solid var(--bs-warning, #ffc107); }

/* Public-ladder accent strong border. */
.public-ladder-accent { border-left: 4px solid var(--rowops-accent); }

/* Mobile self-service ladder surface (templates/ladder_me.php). Lifted
   from a 17-line inline <style nonce> block per the 2026-06-02 UI/UX
   audit Wave 4.17 follow-up. Tap-friendly, chrome-light. */
.ladder-me-hero { padding: 1rem 0; }
.ladder-me-hero h1 { font-size: 1.5rem; margin-bottom: 0.25rem; }
.ladder-me-hero p  { font-size: 0.9rem; margin-bottom: 0; }
.ladder-me-rank   { font-size: 1.25rem; min-width: 2.2rem; display: inline-block; text-align: right; }
.ladder-me-name   { font-weight: 600; }
.ladder-me-row    {
    padding: 0.6rem 0.75rem;
    border-bottom: 1px solid var(--rowops-border);
    display: flex;
    align-items: center;
    gap: 0.6rem;
}
.ladder-me-row:last-child { border-bottom: 0; }
.ladder-me-row.withdrawn  { opacity: 0.55; }
.ladder-me-club   { color: var(--rowops-muted); font-size: 0.85rem; }
.ladder-me-tile-link {
    display: block;
    padding: 0.9rem 1rem;
    min-height: 64px;
    background: var(--rowops-surface-2);
    border-radius: 8px;
    text-decoration: none;
    color: inherit;
    border: 1px solid var(--rowops-border);
}
.ladder-me-cta-row {
    display: flex;
    gap: 0.6rem;
    flex-wrap: wrap;
    margin-top: 1rem;
}
.ladder-me-cta-row a { flex: 1 1 8rem; text-align: center; }
.ladder-me-deadline { font-size: 0.8rem; color: var(--rowops-muted); }
.ladder-me-section  { margin-top: 1.25rem; }
.ladder-me-section h2 { font-size: 1rem; margin-bottom: 0.4rem; }

/* Catch-all line-height variants used by spectator scoreboards. */
.line-height-line-narrow { line-height: 1.35; }

/* erg-categories preview pill inline — overrides previous inline
   display:none; JS toggles `.is-hidden`. */
.erg-category-preview-hidden { display: none; }

/* Locked-finals running-order auto-save state cues on
   templates/events.php — subtle green flash on save, persistent red on
   failure. Mirrors the `.session-band-saving` pattern in
   templates/day_assignments.php. Lifted from inline <style nonce> to
   close the 2026-06-02 UI/UX audit Wave 4.17 finding. */
.final-block-input,
.final-block-order-input { width: 4.5rem; padding-left: 0.4rem; padding-right: 0.2rem; }
.final-block-order-input { margin-left: 0.25rem; }
.final-block-input.fb-saving,
.final-block-order-input.fb-saving { opacity: 0.6; }
.final-block-input.fb-saved,
.final-block-order-input.fb-saved {
    background-color: rgba(34, 139, 84, 0.12);
    transition: background-color 0.6s;
}
.final-block-input.is-invalid,
.final-block-order-input.is-invalid {
    border-color: #dc2626;
    background-color: rgba(220, 38, 38, 0.06);
}
/* Per-event masters handicap override dropdown — same auto-save cue
   palette as the final-block inputs above. */
.masters-handicap-input { min-width: 12rem; }
.masters-handicap-input.fb-saving { opacity: 0.6; }
.masters-handicap-input.fb-saved {
    background-color: rgba(34, 139, 84, 0.12);
    transition: background-color 0.6s;
}
.masters-handicap-input.is-invalid {
    border-color: #dc2626;
    background-color: rgba(220, 38, 38, 0.06);
}

/* =========================================================================
   Theme-7 lift 2026-06-02 — inline <style nonce> blocks across 7 templates
   consolidated here. Audit followup to the 2026-05-30 CSP no-inline sweep.
   Each block was bypassing app.css design tokens + theme overrides; sun-
   mode parity was missing on every one. Rules grouped by surface so a
   future tweak lands once.
   ========================================================================= */

/* ------- home_public.php — pre-login public regattas landing -------
   Rebuilt 2026-06-02 as the SaaS-pivot first-impression page (hero +
   feature strip + enriched card grid). Shared section-title / pulse /
   grid classes carry through to templates/home.php (operator-side). */
.hp-hero { padding: 2rem 0 1rem; text-align: center; }
.hp-hero h1 { font-weight: 700; letter-spacing: -0.02em; }
.hp-hero .lead { color: var(--rowops-ink-muted); }
.hp-section { margin-top: 2rem; }
.hp-section-title {
    font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--rowops-ink-muted); font-weight: 700;
    margin-bottom: 0.6rem; display: flex; align-items: center; gap: 0.5rem;
}
.hp-section-title .pulse {
    display: inline-block; width: 10px; height: 10px; border-radius: 50%;
    background: var(--rowops-bad);
    animation: hp-pulse 1.6s ease-in-out infinite;
}
@keyframes hp-pulse {
    0%, 100% { opacity: 1; transform: scale(1); }
    50%      { opacity: 0.5; transform: scale(1.25); }
}
.hp-grid {
    display: grid; gap: 0.9rem;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
}
.hp-card {
    transition: transform 0.12s, box-shadow 0.12s, border-color 0.12s;
    color: var(--rowops-ink);
}
.hp-card:hover { transform: translateY(-2px); box-shadow: var(--rowops-shadow-lift); }
.hp-empty { padding: 2.5rem 1rem; text-align: center; color: var(--rowops-ink-muted); }
.hp-footer { margin-top: 3rem; padding-top: 1.5rem; border-top: 1px solid var(--rowops-border); text-align: center; }
.hp-footer .small { color: var(--rowops-ink-muted); }

/* Hero strapline — centred, generous vertical padding. The slogan
   rides below the brand name as a softer-weight clause; positioning
   subtext sits under it. Two primary CTAs side-by-side, stack on
   phones via flex-wrap. */
.hp-landing-hero {
    padding: 2.8rem 1rem 2rem;
    text-align: center;
    max-width: 56rem;
    margin: 0 auto;
}
.hp-landing-hero-title {
    font-size: clamp(1.9rem, 4.5vw, 2.8rem);
    font-weight: 800;
    letter-spacing: -0.02em;
    margin: 0 0 0.4rem;
    line-height: 1.15;
}
.hp-landing-hero-slogan {
    display: block;
    font-size: clamp(1.05rem, 2.2vw, 1.35rem);
    font-weight: 500;
    color: var(--rowops-ink-muted);
    letter-spacing: -0.01em;
    margin-top: 0.35rem;
}
.hp-landing-hero-subtext {
    font-size: clamp(0.98rem, 1.6vw, 1.12rem);
    color: var(--rowops-ink-muted);
    margin: 0 0 1.5rem;
    line-height: 1.5;
}
.hp-landing-hero-ctas {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    justify-content: center;
}

/* Three-feature strip — equal-weight cards with emoji + headline +
   one-line body. Single column on phones; three columns desktop.
   Tile shape echoes the dashboard's dash-action-tile look. */
.hp-feature-strip {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    margin: 0.5rem auto 2.5rem;
    max-width: 72rem;
}
.hp-feature-card {
    background: var(--rowops-card);
    border: 1px solid var(--rowops-border);
    border-radius: 0.6rem;
    padding: 1.4rem 1.25rem;
    text-align: left;
}
.hp-feature-glyph {
    font-size: 2rem;
    line-height: 1;
    margin-bottom: 0.6rem;
}
/* Brand-coloured line icons (replace the old emoji glyphs) — inherit the
   tenant/platform brand primary so they're on-brand in every theme. */
.hp-feature-glyph svg {
    width: 2.6rem; height: 2.6rem;
    color: var(--brand-primary);
    display: block;
}
html.theme-sun .hp-feature-glyph svg, body.theme-sun .hp-feature-glyph svg,
html.theme-sun .hp-empty-landing-glyph svg, body.theme-sun .hp-empty-landing-glyph svg { color: #fde047; }
.hp-feature-headline {
    font-size: 1.05rem;
    font-weight: 700;
    margin: 0 0 0.4rem;
    letter-spacing: -0.01em;
}
.hp-feature-body {
    font-size: 0.92rem;
    color: var(--rowops-ink-muted);
    margin: 0;
    line-height: 1.5;
}

/* Regatta card grid wrapper + enriched card body. */
.hp-regattas { margin-bottom: 3rem; }
.hp-card-head {
    display: grid;
    grid-template-columns: auto 1fr auto;
    gap: 0.7rem;
    align-items: flex-start;
    margin-bottom: 0.6rem;
}
.hp-card-logo {
    width: 44px; height: 44px;
    object-fit: contain;
    border-radius: 0.3rem;
    background: var(--rowops-surface-2);
    flex-shrink: 0;
}
.hp-card-title-block {
    min-width: 0; /* allow ellipsis */
}
.hp-card-title {
    font-size: 1.05rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    margin: 0 0 0.25rem;
    line-height: 1.2;
    color: var(--rowops-ink);
}
.hp-card-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 0.65rem;
    font-size: 0.82rem;
    color: var(--rowops-ink-muted);
}
.hp-card-meta-item { white-space: nowrap; }
.hp-card-kpis {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
    gap: 0.5rem;
    padding: 0.55rem 0.7rem;
    background: var(--rowops-surface-2);
    border-radius: 0.4rem;
    margin-bottom: 0.75rem;
}
.hp-card-kpi-value {
    font-size: 1.15rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--rowops-ink);
    line-height: 1.1;
}
.hp-card-kpi-accent { color: var(--rowops-accent); }
.hp-card-cta { margin-top: auto; }

/* Mode-coloured chips on regatta cards. Brand red = regatta + division
   (default); green = head; blue = erg; purple = ladder. Sun-mode parity
   below — every chip flips to jet-yellow on black so it reads at
   distance. */
.hp-mode-chip {
    flex-shrink: 0;
    font-size: 0.7rem;
    font-weight: 700;
    padding: 0.28rem 0.55rem;
    letter-spacing: 0.02em;
    white-space: nowrap;
    text-transform: uppercase;
    border-radius: 0.3rem;
}
.hp-mode-regatta  { background: var(--rowops-accent); color: var(--rowops-accent-fg); }
.hp-mode-division { background: var(--rowops-accent); color: var(--rowops-accent-fg); }
.hp-mode-head     { background: #198754; color: #fff; }
.hp-mode-erg      { background: #1d4ed8; color: #fff; }
.hp-mode-ladder   { background: #7c3aed; color: #fff; }

/* Constructive empty-state landing — large friendly card replacing the
   former bland "No published regattas right now" line. */
.hp-empty-landing {
    background: var(--rowops-card);
    border: 1px solid var(--rowops-border);
    border-radius: 0.7rem;
    padding: 3rem 1.5rem;
    text-align: center;
    max-width: 36rem;
    margin: 1rem auto 2rem;
}
.hp-empty-landing-glyph {
    font-size: 3.4rem;
    line-height: 1;
    opacity: 0.65;
    margin-bottom: 0.7rem;
}
.hp-empty-landing-glyph svg {
    width: 3.4rem; height: 3.4rem;
    color: var(--brand-primary);
    display: block;
    margin-inline: auto;
}
.hp-empty-landing-title {
    font-size: 1.2rem;
    font-weight: 700;
    margin: 0 0 0.5rem;
}
.hp-empty-landing-body {
    color: var(--rowops-ink-muted);
    margin: 0 0 0.7rem;
    line-height: 1.5;
}
.hp-empty-landing-cta {
    font-size: 0.95rem;
    color: var(--rowops-ink);
    margin: 0;
}

/* Footer for the public landing — sits above the global powered-by
   strip (rendered separately by layout.php). About / Docs links are
   env-gated so self-hosted single-tenant deploys see nothing extra. */
.hp-landing-footer {
    margin-top: 3rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--rowops-border);
    text-align: center;
    color: var(--rowops-ink-muted);
}
.hp-landing-footer-line { margin: 0 0 0.4rem; font-size: 0.92rem; }
.hp-landing-footer-links {
    margin: 0;
    font-size: 0.88rem;
    display: flex;
    gap: 0.5rem;
    justify-content: center;
    flex-wrap: wrap;
}
.hp-landing-footer-sep { color: var(--rowops-ink-muted); }

/* Sun-mode parity for every new colour-bearing element on the landing. */
html.theme-sun .hp-section-title, body.theme-sun .hp-section-title { color: #fff; font-weight: 800; }
html.theme-sun .hp-section-title .pulse,
body.theme-sun .hp-section-title .pulse { background: #fde047; }
html.theme-sun .hp-card:hover, body.theme-sun .hp-card:hover {
    box-shadow: 0 0 0 3px #fde047;
}
html.theme-sun .hp-feature-card, body.theme-sun .hp-feature-card {
    background: #000; border: 2px solid #fff;
}
html.theme-sun .hp-feature-headline, body.theme-sun .hp-feature-headline { color: #fff; }
html.theme-sun .hp-feature-body, body.theme-sun .hp-feature-body { color: #f0f0f0; }
html.theme-sun .hp-landing-hero-title, body.theme-sun .hp-landing-hero-title { color: #fff; }
html.theme-sun .hp-landing-hero-slogan, body.theme-sun .hp-landing-hero-slogan { color: #fde047; }
html.theme-sun .hp-landing-hero-subtext, body.theme-sun .hp-landing-hero-subtext { color: #f0f0f0; }
html.theme-sun .hp-card-kpis, body.theme-sun .hp-card-kpis {
    background: #000; border: 2px solid #fff;
}
html.theme-sun .hp-card-kpi-value, body.theme-sun .hp-card-kpi-value { color: #fff; }
html.theme-sun .hp-card-kpi-accent, body.theme-sun .hp-card-kpi-accent { color: #fde047; }
html.theme-sun .hp-mode-regatta, body.theme-sun .hp-mode-regatta,
html.theme-sun .hp-mode-division, body.theme-sun .hp-mode-division {
    background: #fde047; color: #000; border: 2px solid #fff; font-weight: 800;
}
html.theme-sun .hp-mode-head, body.theme-sun .hp-mode-head,
html.theme-sun .hp-mode-erg, body.theme-sun .hp-mode-erg,
html.theme-sun .hp-mode-ladder, body.theme-sun .hp-mode-ladder {
    background: #000; color: #fde047; border: 2px solid #fde047; font-weight: 800;
}
html.theme-sun .hp-empty-landing, body.theme-sun .hp-empty-landing {
    background: #000; border: 2px solid #fff;
}
html.theme-sun .hp-empty-landing-title, body.theme-sun .hp-empty-landing-title { color: #fff; }
html.theme-sun .hp-empty-landing-body, body.theme-sun .hp-empty-landing-body { color: #f0f0f0; }
html.theme-sun .hp-empty-landing-cta, body.theme-sun .hp-empty-landing-cta { color: #fff; }
html.theme-sun .hp-landing-footer, body.theme-sun .hp-landing-footer { color: #fff; }

/* Dark-mode adjustments — feature strip + KPI strip get the same
   surface-2 tint as elsewhere; mode chips already work via the explicit
   colours above. */
html.theme-dark .hp-feature-card, body.theme-dark .hp-feature-card {
    background: var(--rowops-card);
}
html.theme-dark .hp-card-kpis, body.theme-dark .hp-card-kpis {
    background: var(--rowops-surface-2);
}

/* Print — hide hover transforms + animations on the landing. */
@media print {
    .hp-card { transform: none !important; box-shadow: none !important; }
    .hp-section-title .pulse { animation: none; }
    .hp-landing-hero-ctas, .hp-landing-footer-links { display: none; }
}

/* ------- race_control_settings.php — lane keymap editor ------- */
.keymap-panel { padding: 0.5rem 0 0; }
.keymap-panel .km-row { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.5rem; }
.keymap-panel .km-lane { display: inline-flex; align-items: center; gap: 0.3rem; }
.keymap-panel .km-num { font-size: 0.78rem; color: var(--rowops-ink-muted); width: 1.5rem; text-align: right; font-weight: 600; }
.keymap-panel input[data-km-lane] {
    width: 2.4rem; text-align: center; text-transform: uppercase;
    font-family: ui-monospace, Menlo, monospace;
    padding: 0.2rem 0.3rem;
    border: 1px solid var(--rowops-border); border-radius: 0.25rem;
    background: var(--rowops-card); color: var(--rowops-ink);
}
.keymap-panel .km-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; }
.keymap-panel .km-hint { flex: 1 1 100%; margin-top: 0.1rem; }
.keymap-panel .km-status { margin-top: 0.4rem; min-height: 1.1em; }
html.theme-sun .keymap-panel input[data-km-lane],
body.theme-sun .keymap-panel input[data-km-lane] {
    background: #000; color: #fff; border: 2px solid #fff; font-weight: 800;
}
html.theme-sun .keymap-panel .km-num, body.theme-sun .keymap-panel .km-num { color: #fff; }

/* ------- audit_race_resets.php — super-admin reset/recall audit ------- */
.resets-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.5rem 0 1rem; }
.resets-chip {
    padding: 0.3rem 0.7rem; border-radius: 999px; font-size: 0.85rem;
    background: var(--rowops-surface-2); color: var(--rowops-ink);
    border: 1px solid var(--rowops-border); text-decoration: none;
}
.resets-chip.active { background: var(--rowops-accent); color: var(--rowops-accent-fg); border-color: var(--rowops-accent); }
.resets-chip strong { font-family: ui-monospace, Menlo, monospace; }
.resets-table .badge-reset    { background: #fde2e2; color: #7f1d1d; padding: 0.15rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }
.resets-table .badge-recall   { background: #fef3c7; color: #92400e; padding: 0.15rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }
.resets-table .badge-restored { background: #d1fae5; color: #065f46; padding: 0.15rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }
.resets-table .badge-pending  { background: #e0e7ff; color: #3730a3; padding: 0.15rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }
.resets-table .reason-cell { max-width: 24rem; font-size: 0.85rem; color: var(--rowops-ink-muted); }
.resets-table .race-cell { min-width: 14rem; }
.resets-table .race-cell .race-no { font-family: ui-monospace, Menlo, monospace; font-weight: 700; color: var(--rowops-accent); }
.resets-table .when-cell { font-family: ui-monospace, Menlo, monospace; font-size: 0.85rem; white-space: nowrap; }
.resets-table th { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--rowops-ink-muted); }
html.theme-dark .resets-table .badge-reset, body.theme-dark .resets-table .badge-reset    { background: #4b1d1d; color: #fecaca; }
html.theme-dark .resets-table .badge-recall, body.theme-dark .resets-table .badge-recall  { background: #4a3415; color: #fde68a; }
html.theme-dark .resets-table .badge-restored, body.theme-dark .resets-table .badge-restored { background: #0d3526; color: #a7f3d0; }
html.theme-dark .resets-table .badge-pending, body.theme-dark .resets-table .badge-pending  { background: #1f2459; color: #c7d2fe; }
html.theme-sun .resets-chip, body.theme-sun .resets-chip {
    background: #000; color: #fff; border: 2px solid #fff; font-weight: 700;
}
html.theme-sun .resets-chip.active, body.theme-sun .resets-chip.active {
    background: #fde047; color: #000; border-color: #fde047;
}
html.theme-sun .resets-table .badge-reset, body.theme-sun .resets-table .badge-reset       { background: #000; color: #fde047; border: 2px solid #fde047; }
html.theme-sun .resets-table .badge-recall, body.theme-sun .resets-table .badge-recall     { background: #000; color: #fde047; border: 2px solid #fde047; }
html.theme-sun .resets-table .badge-restored, body.theme-sun .resets-table .badge-restored { background: #000; color: #fff;    border: 2px solid #fff; }
html.theme-sun .resets-table .badge-pending, body.theme-sun .resets-table .badge-pending   { background: #000; color: #fff;    border: 2px solid #fff; }
html.theme-sun .resets-table .race-cell .race-no, body.theme-sun .resets-table .race-cell .race-no { color: #fde047; }

/* ------- erg_dashboard.php — phase chevron + KPI strip + status chips ------- */
.erg-chevron { display: flex; flex-wrap: wrap; gap: 0.25rem; padding: 0; margin: 0 0 1.25rem; list-style: none; }
.erg-chevron-step {
    padding: 0.4rem 0.85rem; border-radius: 1rem;
    font-size: 0.85rem; font-weight: 600;
    background: var(--rowops-surface-2); color: var(--rowops-ink-muted);
}
.erg-chevron-step a, .erg-chevron-step span { color: inherit; text-decoration: none; }
.erg-chevron-step.erg-state-past { background: rgba(25, 135, 84, 0.15); color: var(--rowops-good); }
.erg-chevron-step.erg-state-current {
    background: var(--rowops-accent); color: var(--rowops-accent-fg);
    box-shadow: 0 0 0 2px rgba(var(--brand-primary-rgb, 176, 125, 51), 0.18);
}
.erg-chevron-step.erg-state-future { opacity: 0.55; }
.erg-kpi-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 0.75rem; margin-bottom: 1.5rem; }
.erg-kpi {
    background: var(--rowops-card); border: 1px solid var(--rowops-border);
    border-radius: 0.6rem; padding: 0.9rem 1rem;
}
.erg-kpi-label { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--rowops-ink-muted); margin-bottom: 0.25rem; }
.erg-kpi-value { font-size: 1.8rem; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.1; }
.erg-kpi-sub { font-size: 0.8rem; color: var(--rowops-ink-muted); margin-top: 0.2rem; }
.erg-panel {
    background: var(--rowops-card); border: 1px solid var(--rowops-border);
    border-radius: 0.6rem; padding: 1rem 1.1rem; margin-bottom: 1.25rem;
}
.erg-panel h2 { font-size: 1.05rem; margin: 0 0 0.6rem; }
.erg-recent-table { width: 100%; border-collapse: collapse; }
.erg-recent-table th, .erg-recent-table td {
    padding: 0.35rem 0.5rem; text-align: left;
    border-bottom: 1px solid var(--rowops-border); font-size: 0.9rem;
}
.erg-recent-table th { font-size: 0.72rem; text-transform: uppercase; color: var(--rowops-ink-muted); font-weight: 600; }
.erg-status-chip { display: inline-block; padding: 0.1rem 0.55rem; border-radius: 0.6rem; font-size: 0.72rem; font-weight: 600; }
.erg-status-pending_payment    { background: #fff3cd; color: #856404; }
.erg-status-paid_pending_review { background: #cfe2ff; color: #084298; }
.erg-status-approved           { background: #d1e7dd; color: #0f5132; }
.erg-status-rejected,
.erg-status-refunded           { background: #f8d7da; color: #842029; }
html.theme-dark .erg-status-pending_payment,
body.theme-dark .erg-status-pending_payment    { background: #4a3a10; color: #ffe7a0; }
html.theme-dark .erg-status-paid_pending_review,
body.theme-dark .erg-status-paid_pending_review { background: #142a4a; color: #cfe2ff; }
html.theme-dark .erg-status-approved,
body.theme-dark .erg-status-approved           { background: #0e3a25; color: #b9eccc; }
html.theme-dark .erg-status-rejected,
html.theme-dark .erg-status-refunded,
body.theme-dark .erg-status-rejected,
body.theme-dark .erg-status-refunded           { background: #421317; color: #f5b1b8; }
html.theme-sun .erg-chevron-step, body.theme-sun .erg-chevron-step {
    background: #000; color: #fff; border: 2px solid #fff; font-weight: 800;
}
html.theme-sun .erg-chevron-step.erg-state-past,
body.theme-sun .erg-chevron-step.erg-state-past   { background: #000; color: #fff; border-color: #fff; }
html.theme-sun .erg-chevron-step.erg-state-current,
body.theme-sun .erg-chevron-step.erg-state-current {
    background: #fde047; color: #000; border-color: #fde047; box-shadow: none;
}
html.theme-sun .erg-status-chip, body.theme-sun .erg-status-chip {
    background: #000; color: #fff; border: 2px solid #fff; font-weight: 800;
}
html.theme-sun .erg-status-approved, body.theme-sun .erg-status-approved {
    background: #000; color: #fde047; border-color: #fde047;
}

/* ------- erg_enter.php — public direct-entry form ------- */
.erg-enter-hero {
    text-align: center; padding: 2.5rem 1rem;
    background: linear-gradient(135deg, var(--rowops-accent) 0%, var(--brand-dark, #1f2a33) 100%);
    color: #fff; border-radius: 1rem; margin-bottom: 1.5rem;
}
.erg-enter-hero h1 { font-size: clamp(1.6rem, 4vw, 2.4rem); margin: 0 0 0.5rem; font-weight: 800; }
.erg-enter-hero .meta { opacity: 0.9; }
.erg-enter-hero .status-band {
    display: inline-block; padding: 0.3rem 1rem; border-radius: 1.2rem;
    background: rgba(40, 200, 100, 0.3);
    font-weight: 600; font-size: 0.85rem; letter-spacing: 0.06em;
    text-transform: uppercase; margin-bottom: 0.8rem;
}
.erg-enter-hero .status-band.is-closed { background: rgba(255, 255, 255, 0.15); }
.erg-form { max-width: 880px; margin: 0 auto; }
.erg-fieldset {
    background: var(--rowops-card); border: 1px solid var(--rowops-border);
    border-radius: 0.6rem; padding: 1.1rem 1.2rem; margin-bottom: 1.1rem;
}
.erg-fieldset legend { font-size: 1.05rem; font-weight: 700; padding: 0; margin: 0 0 0.7rem; float: none; }
.erg-event-grid { display: grid; grid-template-columns: 1fr; gap: 0.6rem; }
.erg-event-card {
    border: 1.5px solid var(--rowops-border); border-radius: 0.5rem;
    padding: 0.8rem 0.9rem; display: grid;
    grid-template-columns: auto 1fr auto auto; gap: 0.9rem; align-items: center;
    transition: border-color 0.12s, background 0.12s; cursor: pointer;
}
.erg-event-card:hover { background: var(--rowops-surface-2); }
.erg-event-card.is-selected { border-color: var(--rowops-accent); background: var(--rowops-accent-tint); }
.erg-event-card.is-full { opacity: 0.5; cursor: not-allowed; }
.erg-event-card input[type="checkbox"] { width: 1.2rem; height: 1.2rem; cursor: pointer; }
.erg-event-card .event-name { font-weight: 600; }
.erg-event-card .event-gender {
    display: inline-block; padding: 0.1rem 0.45rem;
    background: var(--rowops-surface-2); border-radius: 0.4rem;
    font-size: 0.72rem; font-weight: 700; margin-right: 0.35rem;
}
.erg-event-card .event-line { color: var(--rowops-ink-muted); font-size: 0.85rem; }
.erg-event-card .event-cap { font-size: 0.75rem; color: var(--rowops-ink-muted); margin-top: 0.15rem; }
.erg-event-card .event-cap.is-tight { color: var(--rowops-warn); font-weight: 600; }
.erg-event-card .event-cap.is-full  { color: var(--rowops-bad); font-weight: 600; }
.erg-event-card .event-chip {
    padding: 0.25rem 0.7rem; background: var(--rowops-surface-2);
    border-radius: 0.4rem; font-size: 0.85rem; font-weight: 600;
}
.erg-event-card .event-fee { font-weight: 700; color: var(--rowops-accent); font-variant-numeric: tabular-nums; }
.erg-event-card .erg-expected-time {
    padding-left: 1.9rem; padding-top: 0.5rem;
    grid-column: 1 / -1; display: none;
}
.erg-event-card.is-selected .erg-expected-time { display: block; }
.erg-event-card .erg-expected-time input { max-width: 9rem; }
.erg-category-preview {
    display: inline-block; padding: 0.5rem 1rem; border-radius: 0.5rem;
    background: var(--rowops-accent-tint); color: var(--rowops-accent);
    font-weight: 700; margin-top: 0.5rem;
}
.erg-running-total {
    position: sticky; bottom: 0;
    background: var(--rowops-card); border: 1px solid var(--rowops-border);
    border-top: 3px solid var(--rowops-accent);
    border-radius: 0.5rem 0.5rem 0 0; padding: 1rem 1.2rem; margin: 1rem 0 0;
    box-shadow: 0 -6px 20px rgba(0, 0, 0, 0.08);
    display: flex; gap: 1rem; align-items: center; flex-wrap: wrap;
}
.erg-running-total .total-label { font-size: 0.85rem; color: var(--rowops-ink-muted); }
.erg-running-total .total-amount { font-size: 1.5rem; font-weight: 800; font-variant-numeric: tabular-nums; }
.erg-running-total .total-count { color: var(--rowops-ink-muted); font-size: 0.85rem; }
.erg-running-total button { margin-left: auto; }
.erg-club-delegate { display: none; }
.erg-club-delegate.is-active { display: block; }
.erg-closed-state {
    text-align: center; padding: 3rem 1rem;
    background: var(--rowops-card); border-radius: 0.6rem;
}
html.theme-sun .erg-enter-hero, body.theme-sun .erg-enter-hero {
    background: #000; border: 3px solid #fde047; color: #fff;
}
html.theme-sun .erg-enter-hero .status-band,
body.theme-sun .erg-enter-hero .status-band { background: #fde047; color: #000; }
html.theme-sun .erg-enter-hero .status-band.is-closed,
body.theme-sun .erg-enter-hero .status-band.is-closed { background: #000; color: #fff; border: 2px solid #fff; }
html.theme-sun .erg-event-card, body.theme-sun .erg-event-card {
    background: #000; border-color: #fff; color: #fff;
}
html.theme-sun .erg-event-card.is-selected, body.theme-sun .erg-event-card.is-selected {
    background: #000; border-color: #fde047;
}
html.theme-sun .erg-event-card .event-fee, body.theme-sun .erg-event-card .event-fee { color: #fde047; }
html.theme-sun .erg-running-total, body.theme-sun .erg-running-total {
    background: #000; border-color: #fff; border-top-color: #fde047;
}
html.theme-sun .erg-category-preview, body.theme-sun .erg-category-preview {
    background: #000; color: #fde047; border: 2px solid #fde047;
}

/* ------- erg_flights.php — per-event flight block view ------- */
.flight-order-toggle {
    display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center;
    padding: 0.9rem 1.1rem; background: var(--rowops-surface-2);
    border-radius: 0.6rem; margin-bottom: 1.5rem;
}
.flight-order-toggle .order-pill {
    display: inline-block; padding: 0.3rem 0.8rem; border-radius: 1.2rem;
    font-weight: 600; font-size: 0.85rem;
    background: var(--rowops-accent-tint); color: var(--rowops-accent);
}
.event-block {
    background: var(--rowops-card); border: 1px solid var(--rowops-border);
    border-radius: 0.6rem; padding: 1rem 1.2rem; margin-bottom: 1.25rem;
}
.event-block h2 {
    font-size: 1.1rem; margin: 0 0 0.6rem;
    display: flex; gap: 0.6rem; align-items: baseline; flex-wrap: wrap;
}
.event-block h2 .flight-count { color: var(--rowops-ink-muted); font-weight: 400; font-size: 0.85rem; margin-left: auto; }
.flight-card {
    background: var(--rowops-surface-2); border-radius: 0.5rem;
    padding: 0.7rem 0.9rem; margin: 0.6rem 0;
}
.flight-card .flight-head { display: flex; gap: 0.6rem; align-items: baseline; font-weight: 600; margin-bottom: 0.5rem; }
.flight-card .flight-head .heat-no { color: var(--rowops-accent); font-size: 1.05rem; }
.flight-card .flight-head .when    { color: var(--rowops-ink-muted); font-weight: 400; font-size: 0.85rem; margin-left: auto; }
.flight-card .flight-fast          { color: var(--rowops-good); font-weight: 600; font-size: 0.75rem; padding: 0.1rem 0.45rem; background: rgba(25, 135, 84, 0.15); border-radius: 0.4rem; }
.lane-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.lane-table th, .lane-table td { padding: 0.25rem 0.5rem; border-bottom: 1px dotted var(--rowops-border); text-align: left; }
.lane-table td.lane     { width: 4rem; font-weight: 700; font-variant-numeric: tabular-nums; text-align: center; }
.lane-table td.expected { font-variant-numeric: tabular-nums; text-align: right; color: var(--rowops-ink-muted); }
.lane-table td.cat      { font-size: 0.78rem; color: var(--rowops-ink-muted); }
.empty-state { padding: 2rem 1rem; text-align: center; color: var(--rowops-ink-muted); }
html.theme-sun .flight-order-toggle, body.theme-sun .flight-order-toggle {
    background: #000; border: 2px solid #fff;
}
html.theme-sun .flight-order-toggle .order-pill,
body.theme-sun .flight-order-toggle .order-pill {
    background: #fde047; color: #000;
}
html.theme-sun .flight-card, body.theme-sun .flight-card {
    background: #000; border: 2px solid #fff;
}
html.theme-sun .flight-card .flight-head .heat-no,
body.theme-sun .flight-card .flight-head .heat-no { color: #fde047; }
html.theme-sun .flight-card .flight-fast,
body.theme-sun .flight-card .flight-fast {
    background: #000; color: #fde047; border: 2px solid #fde047;
}

/* ------- head_finish_audit.php — admin-only head-mode audit table ------- */
.audit-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.audit-table th, .audit-table td {
    padding: 0.45rem 0.6rem; border-bottom: 1px solid var(--rowops-border);
    vertical-align: top;
}
.audit-table th {
    text-align: left; font-weight: 600; color: var(--rowops-ink-muted);
    font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.02em;
}
.audit-table tr.dup-bright { background: rgba(220, 53, 69, 0.10); }
.audit-table tr.dup-soft   { background: rgba(220, 53, 69, 0.04); }
.audit-table td.ts     { font-variant-numeric: tabular-nums; white-space: nowrap; }
.audit-table td.bow    { font-weight: 700; font-variant-numeric: tabular-nums; text-align: center; min-width: 3em; }
.audit-table td.action { white-space: nowrap; }
.action-pill { display: inline-block; padding: 0.1rem 0.45rem; border-radius: 0.25rem; font-size: 0.78rem; font-weight: 600; }
.action-pill.release { background: rgba(40, 167, 69, 0.18); color: var(--rowops-good); }
.action-pill.call    { background: rgba(56, 132, 220, 0.18); color: #1f5fa3; }
.action-pill.recall  { background: rgba(255, 193, 7, 0.22); color: var(--rowops-warn); }
.action-pill.finish  { background: rgba(40, 167, 69, 0.22); color: var(--rowops-good); }
.action-pill.undo    { background: rgba(120, 120, 120, 0.18); color: var(--rowops-ink-muted); }
.audit-help { color: var(--rowops-ink-muted); font-size: 0.88rem; line-height: 1.5; margin-bottom: 0.8rem; }
html.theme-sun .audit-table tr.dup-bright,
body.theme-sun .audit-table tr.dup-bright { background: #2a0000; }
html.theme-sun .audit-table tr.dup-soft,
body.theme-sun .audit-table tr.dup-soft   { background: #150000; }
html.theme-sun .action-pill, body.theme-sun .action-pill {
    background: #000; color: #fff; border: 2px solid #fff; font-weight: 800;
}
html.theme-sun .action-pill.release, body.theme-sun .action-pill.release { color: #fde047; border-color: #fde047; }
html.theme-sun .action-pill.finish, body.theme-sun .action-pill.finish   { color: #fde047; border-color: #fde047; }
html.theme-sun .action-pill.recall, body.theme-sun .action-pill.recall   { color: #fde047; border-color: #fde047; }
html.theme-sun .action-pill.call, body.theme-sun .action-pill.call       { color: #fff; border-color: #fff; }
html.theme-sun .action-pill.undo, body.theme-sun .action-pill.undo       { color: #fff; border-color: #fff; }

/* =========================================================================
   Print-sheet chrome — 2026-06-02 audit closeout.
   Shared header (regatta logo + name + dates + tenant + sheet subtitle)
   rendered by templates/partials/print_chrome.php on every sheet, plus a
   @page footer (Generated YYYY-MM-DD HH:MM · Page N of M) emitted via
   CSS named strings + counter(page) / counter(pages). Per-sheet @page
   overrides (e.g. landscape mode for draw_rationale) layer on top of
   the @page baseline below.

   Captain-facing sheets (affected_clubs_per_club + availability_windows_per_club)
   use the standalone layout and keep their light-mode body styling; the
   .print-chrome element inherits its surrounding palette so it doesn't
   fight the captain page.
   ========================================================================= */

/* Shared header — always visible on screen as well as in print so the
   operator and the captain see the same identifying chrome on the page
   they're previewing before they hit Print. */
.print-chrome {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.5rem 0 0.85rem;
    margin-bottom: 1rem;
    border-bottom: 1.5px solid #000;
}
.print-chrome-logo {
    flex: 0 0 auto;
    width: 56px;
    height: 56px;
    display: flex;
    align-items: center;
    justify-content: center;
}
.print-chrome-logo img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    display: block;
}
.print-chrome-text {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}
.print-chrome-sponsors {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
    margin: 0.2rem 0 0.4rem;
    padding-top: 0.3rem;
    border-top: 1px solid var(--rowops-border);
}
.print-chrome-sponsors-label {
    font-size: 0.62rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--rowops-muted);
}
.print-chrome-sponsors img { max-height: 30px; max-width: 130px; object-fit: contain; }
.print-chrome-title {
    font-weight: 700;
    font-size: 1.15rem;
    line-height: 1.2;
    letter-spacing: -0.01em;
}
.print-chrome-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 0.9rem;
    font-size: 0.82rem;
    color: #555;
}
.print-chrome-subtitle { font-weight: 600; color: #333; }
.print-chrome-tenant   { color: #555; }
.print-chrome-dates    { font-variant-numeric: tabular-nums; }

/* The .print-chrome-generated span is the seed for the @page footer.
   Off-screen but kept in flow for the named-string content-pickup. */
.print-chrome-generated {
    position: absolute;
    width: 1px; height: 1px; overflow: hidden;
    clip: rect(1px, 1px, 1px, 1px);
    string-set: print-generated content();
}

@media print {
    /* Pin a single typography baseline + page margin across every print
       sheet. Per-sheet @page named overrides (e.g. dr-page-landscape on
       draw_rationale) still win because they're declared later in the
       sheet template — author-order wins for equal specificity. */
    body { font-size: 9.5pt; }
    @page { margin: 14mm; }
    @page {
        @bottom-left  { content: string(print-generated); font-size: 8pt; color: #555; }
        @bottom-right { content: "Page " counter(page) " of " counter(pages); font-size: 8pt; color: #555; }
    }

    /* Hide the seed span on print — the named string already carries
       the text into the @bottom-left margin box. */
    .print-chrome-generated { display: none !important; }

    .print-chrome {
        margin-bottom: 0.8rem;
        padding: 0 0 0.5rem;
        border-bottom: 1pt solid #000;
    }
    .print-chrome-logo { width: 14mm; height: 14mm; }
    .print-chrome-title { font-size: 12pt; }
    .print-chrome-meta { font-size: 8.5pt; color: #333; }
}

/* ── Live GPS map (spec 02) — full-bleed map shell + chrome ───────── */
.track-map-wrap { position: fixed; inset: 0; display: flex; flex-direction: column; background: var(--rowops-surface, #fff); z-index: 1; }
.track-map-bar { flex: 0 0 auto; display: flex; align-items: center; gap: 1rem; padding: 0.5rem 1rem; background: #1a1f2e; color: #fff; font-size: 0.95rem; }
.track-map-bar a { color: #fff; }
.track-map-title { font-weight: 700; }
.track-map-status { margin-left: auto; opacity: 0.85; font-variant-numeric: tabular-nums; }
.track-map-canvas { flex: 1 1 auto; width: 100%; min-height: 0; }
.track-map-alert { flex: 0 0 auto; padding: 0.6rem 1rem; background: #dc3545; color: #fff; font-weight: 600; }
.tracker-pwa-card { max-width: 30rem; margin: 1.5rem auto; }
.tracker-pwa-status { font-variant-numeric: tabular-nums; }
[data-bs-theme="sun"] .track-map-bar { background: #000; }
[data-bs-theme="sun"] .track-map-alert { background: #fde047; color: #000; }

/* ── Beach-sprint knockout bracket (spec 11) ───────────────────────── */
.beach-bracket-grid {
  display: flex;
  gap: 1.25rem;
  overflow-x: auto;
  padding-bottom: 0.5rem;
  align-items: flex-start;
}
.beach-bracket-col { min-width: 12rem; flex: 0 0 auto; }
.beach-bracket-stage {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--rowops-muted, #6c757d);
  margin: 0 0 0.5rem;
}
.beach-bracket-race {
  border: 1px solid var(--rowops-border, #dee2e6);
  border-radius: 0.4rem;
  padding: 0.4rem 0.5rem;
  margin-bottom: 0.9rem;
  background: var(--rowops-surface-2, #fff);
}
.beach-bracket-race.is-done { border-color: var(--rowops-success, #198754); }
.beach-bracket-seat {
  display: flex;
  align-items: baseline;
  gap: 0.35rem;
  padding: 0.2rem 0;
  font-size: 0.9rem;
}
.beach-bracket-seat.is-winner { font-weight: 700; }
.beach-bracket-seat.is-empty { color: var(--rowops-muted, #6c757d); font-style: italic; }
.beach-seed {
  font-size: 0.72rem;
  color: var(--rowops-muted, #6c757d);
  font-variant-numeric: tabular-nums;
}
.beach-crew { flex: 1 1 auto; }
.beach-club { font-size: 0.74rem; color: var(--rowops-muted, #6c757d); }
.beach-bracket-open { display: inline-block; font-size: 0.74rem; margin-top: 0.2rem; }
[data-bs-theme="sun"] .beach-bracket-race { background: #000; border-color: #fde047; }
[data-bs-theme="sun"] .beach-bracket-stage,
[data-bs-theme="sun"] .beach-seed,
[data-bs-theme="sun"] .beach-club { color: #fde047; }

/* ───────────────────────────────────────────────────────────────────
   Auth / standalone card (login · forgot-password · reset-password ·
   themed 403). Lifted out of templates/auth/login.php's inline <style>
   block 2026-06-17 so EVERY auth page gets the same branded card — the
   forgot/reset pages referenced these classes but never carried the CSS,
   so they rendered unstyled with no brand mark ("poor branding").
   ─────────────────────────────────────────────────────────────────── */
body.standalone-shell {
  background:
    radial-gradient(1200px 600px at 10% 0%, rgba(var(--brand-primary-rgb), 0.10), transparent 60%),
    radial-gradient(900px 500px at 90% 100%, rgba(var(--brand-primary-rgb), 0.05), transparent 60%),
    var(--rowops-surface);
  min-height: 100vh;
}
.standalone-main {
  min-height: 100vh;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  padding: 2rem 1rem;
}
.login-card {
  width: 100%; max-width: 460px; border-radius: 1rem;
  background: var(--rowops-card);
  box-shadow: 0 20px 60px -20px rgba(0, 0, 0, 0.25), 0 8px 24px -8px rgba(0, 0, 0, 0.12);
}
/* Real brand logo (rowOps mark on the apex, club logo on a tenant host). */
.login-logo {
  display: block;
  height: 64px; width: auto;
  max-height: 64px; max-width: 220px;
}
/* Fallback only when a deploy explicitly blanks BRAND_LOGO_URL. */
.login-mark {
  display: block;
  width: 60px; height: 60px; border-radius: 14px;
  background: var(--brand-primary);
  position: relative;
  box-shadow: 0 6px 20px -6px var(--brand-primary);
}
.login-mark::after {
  content: '';
  position: absolute; inset: 14px;
  border-radius: 6px; background: rgba(255, 255, 255, 0.95);
}
.login-title { letter-spacing: -0.025em; font-weight: 800; color: var(--rowops-ink); }
.login-subtitle { letter-spacing: 0.06em; }
.login-label { letter-spacing: 0.06em; }
.login-input {
  background: var(--rowops-surface-2);
  border: 1px solid transparent;
  box-shadow: none;
  font-size: 1.05rem;
  padding: 0.85rem 1rem;
  border-radius: 0.5rem;
}
.login-input:focus {
  background: var(--rowops-card);
  border-color: var(--brand-primary);
  box-shadow: 0 0 0 4px rgba(var(--brand-primary-rgb), 0.15);
}
.login-button {
  padding: 0.85rem 1rem;
  border-radius: 0.5rem;
  font-size: 1.05rem;
  letter-spacing: 0.02em;
  background: var(--brand-primary); border: 0;
}
.login-button:hover { background: var(--brand-dark, #1f2a33); }
.login-card .x-small { font-size: 0.7rem; }
@media (max-width: 480px) {
  .login-card { padding: 1.5rem !important; max-width: 420px; }
}

/* ───────────────────────────────────────────────────────────────────
   Brand glyphs — Support\glyph(). The house replacement for decorative
   emoji (2026-06-17). Inline SVG sized to the surrounding text; colour
   inherits via currentColor. Decorative / race-annotation glyphs take the
   active tenant's brand (var(--brand-primary)); status glyphs use a
   semantic class so meaning-carrying icons keep their colour.
   ─────────────────────────────────────────────────────────────────── */
.glyph {
  width: 1em;
  height: 1em;
  display: inline-block;
  vertical-align: -0.125em;       /* sit on the text baseline */
  flex: 0 0 auto;
  color: var(--brand-primary);    /* decorative default = tenant brand */
}
/* In a button/badge/link, follow the text colour instead of the brand so
   the glyph matches its label (e.g. white on a primary button). */
.btn .glyph, .badge .glyph, a .glyph, .nav-link .glyph, summary .glyph { color: inherit; }
/* Semantic status glyphs — meaning over brand. */
.glyph.is-success { color: var(--brand-success, #198754); }
.glyph.is-warn    { color: var(--brand-warning, #f59e0b); }
.glyph.is-danger  { color: var(--brand-danger,  #dc3545); }
.glyph.is-muted   { color: var(--rowops-muted,  #6c757d); }
.glyph.is-brand   { color: var(--brand-primary); }
.glyph.is-record  { color: #dc3545; }            /* live-record red, theme-independent */
/* Sized variants for hero / empty-state contexts. */
.glyph.glyph-lg   { width: 1.5em; height: 1.5em; }
.glyph.glyph-xl   { width: 2.25em; height: 2.25em; }
/* Sun mode (race-day high-contrast): collapse brand + status hues to the
   jet-yellow glance palette like the rest of the sun theme. */
[data-bs-theme="sun"] .glyph,
[data-bs-theme="sun"] .glyph.is-success,
[data-bs-theme="sun"] .glyph.is-warn,
[data-bs-theme="sun"] .glyph.is-brand { color: #fde047; }
[data-bs-theme="sun"] .glyph.is-danger,
[data-bs-theme="sun"] .glyph.is-record { color: #fde047; }

/* Home empty-state — branded mark + sized glyph fallback (2026-06-17). */
.hp-empty-state .hp-empty-glyph .glyph { width: 3rem; height: 3rem; }
.hp-empty-state .hp-empty-logo { height: 64px; width: auto; max-width: 220px; opacity: 0.9; }

/* ─── Erg Registration + Events + public per-event surfaces (2026-06-18) ───
   New erg-mode operator + spectator chrome. Reuses brand surface tokens so
   day / dark / sun parity flows through; sun overrides for colour-bearing
   bits added explicitly below. */

/* Registration fee/cap table — keep the euro fee + cap forms tidy + aligned. */
.erg-fee-table td { vertical-align: middle; }
.erg-fee-input { width: 9rem; flex: 0 0 auto; }
.input-mw-7rem { max-width: 7rem; }

/* Bulk "one price for all events" card on the events page. */
.erg-bulk-fee-card { border-left: 3px solid var(--brand-primary, #b07d33); }

/* Per-event fee + cap inline cell on the events table (erg mode). Kept
   narrow so the two stacked forms don't widen the row. */
.erg-fee-cell { width: 7rem; flex: 0 0 auto; }
.erg-cap-cell { max-width: 5rem; }

/* Registration danger-zone reset card — mirrors .dash-danger but local to
   the erg surfaces so the typed-confirm + reset CTA reads as destructive. */
.erg-danger {
    margin-top: var(--space-8, 2rem);
    padding-top: var(--space-4, 1rem);
    border-top: 1px solid var(--rowops-border, #e5e5e5);
}
.erg-danger-summary {
    color: var(--rowops-bad, #b02a37);
    font-weight: 700; cursor: pointer; font-size: 0.95rem;
}
.erg-danger-body {
    margin-top: var(--space-3, 0.75rem);
    padding: var(--space-3, 0.75rem) var(--space-4, 1rem);
    background: rgba(176, 42, 55, 0.05);
    border: 1px solid rgba(176, 42, 55, 0.22);
    border-radius: var(--radius-md, 0.5rem);
}
.erg-danger-input { width: auto; max-width: 24rem; }

/* Public per-event flights card link affordance (live_erg_registration). */
.erg-event-card-link { text-decoration: none; color: inherit; display: block; transition: border-color 0.12s, box-shadow 0.12s, transform 0.12s; }
.erg-event-card-link:hover { border-color: var(--brand-primary, #b07d33); box-shadow: 0 4px 14px rgba(0,0,0,0.08); transform: translateY(-1px); }
.erg-event-card-link .erg-event-card-cta { display: block; margin-top: 0.5rem; font-size: 0.82rem; font-weight: 600; color: var(--brand-primary, #b07d33); }

/* Leaderboard event-name link (live_erg). */
.erg-leaderboard-event-link { color: inherit; text-decoration: none; }
.erg-leaderboard-event-link:hover { text-decoration: underline; }

/* Sun-mode parity for the erg danger card + bulk-fee accent. */
html.theme-sun .erg-danger-summary, body.theme-sun .erg-danger-summary { color: #fde047; }
html.theme-sun .erg-danger-body, body.theme-sun .erg-danger-body {
    background: #000; border: 3px solid #fde047; color: #fff;
}
html.theme-sun .erg-bulk-fee-card, body.theme-sun .erg-bulk-fee-card { border-left-color: #fde047; }
html.theme-sun .erg-event-card-link .erg-event-card-cta,
body.theme-sun .erg-event-card-link .erg-event-card-cta { color: #fde047; }
html.theme-sun .erg-event-card-link:hover,
body.theme-sun .erg-event-card-link:hover { border-color: #fde047; }

/* Dark-mode parity — the danger tint reads on the deep-slate surface. */
html.theme-dark .erg-danger-body, body.theme-dark .erg-danger-body {
    background: rgba(176, 42, 55, 0.12);
    border-color: rgba(176, 42, 55, 0.4);
}

/* ─── Digital erg check-in desk (erg_check_in.php) ──────────────────────
   Venue registration operator signs entrants in here; the printable
   sign-in sheet stays the offline backup. Row states read at a glance so
   the desk can scan a queue: signed-in rows go green, pending rows keep
   the primary "Sign in" CTA. Day / dark / sun parity below. */
.checkin-counts .card-body { gap: var(--space-2, 0.5rem); }
.checkin-count-pill {
    display: inline-flex; align-items: center; gap: 0.35rem;
    padding: 0.25rem 0.6rem; border-radius: var(--radius-md, 0.5rem);
    font-size: 0.9rem; font-weight: 600;
    background: var(--rowops-surface-2); color: var(--rowops-ink);
    border: 1px solid var(--rowops-border);
}
.checkin-count-pill.is-pending {
    background: rgba(173, 106, 0, 0.1);
    border-color: rgba(173, 106, 0, 0.3);
    color: var(--rowops-warn);
}
.checkin-count-pill.is-done {
    background: rgba(21, 115, 71, 0.1);
    border-color: rgba(21, 115, 71, 0.3);
    color: var(--rowops-good);
}

.checkin-row {
    display: flex; flex-wrap: wrap; align-items: center;
    justify-content: space-between; gap: var(--space-3, 0.75rem);
}
.checkin-row-info {
    display: flex; flex-wrap: wrap; align-items: baseline;
    gap: 0.35rem 0.75rem; min-width: 0;
}
.checkin-row-name { font-weight: 600; }
.checkin-row-club { font-size: 0.9rem; }
.checkin-row-action { display: flex; align-items: center; gap: 0.5rem; }
.checkin-signed-note {
    display: inline-flex; align-items: center; gap: 0.3rem;
    font-size: 0.85rem; font-weight: 600; color: var(--rowops-good);
}
/* Signed-in rows tint green with a left accent so the desk sees state. */
.checkin-row.is-checked-in {
    background: rgba(21, 115, 71, 0.07);
    box-shadow: inset 3px 0 0 var(--rowops-good);
}
.checkin-event-hidden { display: none; }

/* Per-race expected/not-signed-in badge on erg_finish.php. The is-warning
   / is-ok base classes already carry day/dark/sun parity (defined above);
   this only sizes the inline badge to read in the card header. */
.checkin-race-badge { font-size: 0.78rem; font-weight: 700; }

/* Dark-mode parity — the green tint reads on the deep-slate surface. */
html.theme-dark .checkin-row.is-checked-in,
body.theme-dark .checkin-row.is-checked-in {
    background: rgba(21, 115, 71, 0.18);
}
html.theme-dark .checkin-count-pill,
body.theme-dark .checkin-count-pill {
    background: var(--rowops-surface-2); color: var(--rowops-ink);
    border-color: var(--rowops-border);
}
html.theme-dark .checkin-count-pill.is-done,
body.theme-dark .checkin-count-pill.is-done { background: rgba(21, 115, 71, 0.2); }
html.theme-dark .checkin-count-pill.is-pending,
body.theme-dark .checkin-count-pill.is-pending { background: rgba(173, 106, 0, 0.2); }

/* Sun-mode parity — pure white-on-black, jet-yellow accents, signed-in
   rows get a high-contrast green so the desk reads in direct glare. */
html.theme-sun .checkin-count-pill, body.theme-sun .checkin-count-pill {
    background: #000; color: #fff; border: 2px solid #fff; font-weight: 800;
}
html.theme-sun .checkin-count-pill.is-pending,
body.theme-sun .checkin-count-pill.is-pending {
    background: #000; color: #fde047; border-color: #fde047;
}
html.theme-sun .checkin-count-pill.is-done,
body.theme-sun .checkin-count-pill.is-done {
    background: #000; color: #2ecc71; border-color: #2ecc71;
}
html.theme-sun .checkin-row.is-checked-in,
body.theme-sun .checkin-row.is-checked-in {
    background: rgba(46, 204, 113, 0.18); box-shadow: inset 4px 0 0 #2ecc71;
}
html.theme-sun .checkin-signed-note, body.theme-sun .checkin-signed-note {
    color: #2ecc71; font-weight: 800;
}
html.theme-sun .checkin-row-name, body.theme-sun .checkin-row-name { color: #fff; }
html.theme-sun .checkin-race-badge.is-warning,
body.theme-sun .checkin-race-badge.is-warning {
    background: #fde047; color: #000; border: 2px solid #fff; font-weight: 800;
}
