/* UltraTrack baseline styles: typography, forms, and the window panel
   chrome used to host dashboard features like Log Entry.
   .window/.window-titlebar/.window-body are the real draggable/
   resizable window system (see public/assets/js/window-manager.js) --
   extended in place here, not renamed, per the original scaffolding
   comment's intent. */

/* Single palette surface for theming: every color in this file reads
   from one of these tokens (never a literal hex/rgb), so a theme is
   just a block that redefines them under a selector -- no changes
   needed anywhere else in the stylesheet.

   <html data-theme="dark|light"> selects the active palette below.
   The attribute is set as early as possible by a small inline script
   in each page's <head> (before this stylesheet loads, to avoid a
   flash of the wrong theme) which reads the 'ultratrack-theme'
   localStorage key written by public/assets/js/theme.js. */
:root {
  /* Shared across every theme -- brand accent and semantic colors
     don't change with light/dark, only surface/background/text do. */
  --accent: #4f8cff;
  --on-accent: #fff;
  --error: #ff5d5d;
  --success: #4fdb8f;
  --warning: #e6a23c;
  /* The course map's real-trail line (course-map.js) -- deliberately
     not --accent, which is the same blue OpenTopoMap already uses for
     rivers/streams; a track drawn in that color was rendering
     correctly but was visually indistinguishable from waterways.
     Magenta has no other meaning on this map and reads clearly against
     every basemap (street, aerial, topo). */
  --course-track: #d946ef;
  /* Derived from --error/--panel-bg rather than a fixed hex, so it
     adapts automatically to whichever theme is active without a
     separate light/dark declaration. */
  --alert-bg: color-mix(in srgb, var(--error) 18%, var(--panel-bg));
  --alert-bg-acked: color-mix(in srgb, var(--error) 6%, var(--panel-bg));
  font-family: -apple-system, "Segoe UI", Roboto, sans-serif;
}

[data-theme="dark"] {
  --bg: #14161a;
  --desktop-bg: var(--bg);
  --panel-bg: #1d2025;
  --panel-border: #2c3038;
  --surface-2: #333844;
  --titlebar-bg: #22262d;
  --input-bg: #111316;
  --text: #e8e9ec;
  --text-dim: #9aa0ab;
  --shadow: rgba(0, 0, 0, .5);
}

[data-theme="light"] {
  --bg: #f0f2f5;
  --desktop-bg: #dde1e7;
  --panel-bg: #ffffff;
  --panel-border: #d0d7de;
  --surface-2: #eaeef2;
  --titlebar-bg: #f6f8fa;
  --input-bg: #f6f8fa;
  --text: #1f2328;
  --text-dim: #656d76;
  --shadow: rgba(0, 0, 0, .18);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  height: 100vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  color: var(--text);
  font-size: 15px;
}

h1, h2, h3 { font-weight: 600; margin: 0 0 0.5em; }

/* Unstyled, this renders the browser's default 3D-groove line, which
   clashes with the flat, theme-token-driven look everywhere else in
   this app -- a plain hairline in the panel border color instead. */
hr { border: none; border-top: 1px solid var(--panel-border); margin: 1.5em 0; }

label {
  display: block;
  font-size: 0.85em;
  color: var(--text-dim);
  margin: 0.75em 0 0.25em;
}

input, select, textarea, button {
  font: inherit;
  color: inherit;
}

input[type="text"], input[type="email"], input[type="password"],
input[type="number"], input[type="time"], input[type="datetime-local"], select, textarea {
  width: 100%;
  padding: 0.5em 0.6em;
  background: var(--input-bg);
  border: 1px solid var(--panel-border);
  border-radius: 4px;
  color: var(--text);
}

/* The input's own background/text above doesn't touch the native
   calendar/clock picker popup or its icon -- browsers render those
   using the separate `color-scheme` property, defaulting to a light
   picker regardless of the input's own colors. Without this, the
   icon (and popup) stay light-on-light against a dark theme and
   effectively disappear. */
[data-theme="dark"] input[type="datetime-local"] { color-scheme: dark; }
[data-theme="light"] input[type="datetime-local"] { color-scheme: light; }

/* .button (not just the button tag) so an <a> that needs to act like a
   button -- e.g. Course Track's Download link, which needs a real href
   for the browser's native download handling rather than a JS-driven
   click -- can look like one too. */
button, .button {
  display: inline-block;
  cursor: pointer;
  border: none;
  border-radius: 4px;
  padding: 0.55em 1.1em;
  background: var(--accent);
  color: var(--on-accent);
  font-weight: 600;
  text-decoration: none;
  text-align: center;
}

/* color: var(--on-accent) from the base button rule above assumes the
   colored --accent background -- surface-2 is much lighter in the
   light theme, so a secondary button needs its own (dark) text color
   too, not just a different background. */
button.secondary, .button.secondary { background: var(--surface-2); color: var(--text); }
button.danger, .button.danger { background: var(--error); }
button:disabled { opacity: 0.5; cursor: not-allowed; }

/* --- Auth page --- */

.auth-page {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
}

.auth-card {
  width: 320px;
  background: var(--panel-bg);
  border: 1px solid var(--panel-border);
  border-radius: 8px;
  padding: 2em;
}

.auth-subtitle { color: var(--text-dim); margin-top: -0.5em; }
.auth-error { color: var(--error); font-size: 0.9em; }
.field-hint { color: var(--text-dim); font-size: 0.85em; margin-top: -0.5em; }

/* --- Top bar --- */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75em 1.25em;
  border-bottom: 1px solid var(--panel-border);
  flex-shrink: 0;
}

.topbar a { color: var(--text-dim); text-decoration: none; }

.topbar-left { display: flex; align-items: center; gap: 0.5em; min-width: 0; }
.topbar-right { display: flex; align-items: center; gap: 0.75em; }

/* Shown by server-clock.js when the browser's clock drifts too far
   from the server's -- see that file for the threshold/reasoning. */
.clock-drift-warning {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1em;
  padding: 0.5em 1.25em;
  background: color-mix(in srgb, var(--warning) 20%, var(--panel-bg));
  color: var(--warning);
  font-size: 0.9em;
  border-bottom: 1px solid var(--panel-border);
}
.clock-drift-warning button {
  background: transparent;
  border: none;
  color: inherit;
  font-size: 1.2em;
  line-height: 1;
  cursor: pointer;
  padding: 0 0.3em;
}

.theme-toggle {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--panel-border);
  background: var(--surface-2);
  color: var(--text);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  padding: 0;
  flex-shrink: 0;
}

.theme-toggle:hover { border-color: var(--accent); }

/* --- Alerts topbar indicator --- */

/* Hidden by default (see dashboard.tpl) -- alerts.js shows it once
   there's at least one active alert, independent of whether the
   Alerts window itself is open/minimized (it keeps polling either
   way, see alerts.js's docblock). */
.alerts-indicator { position: relative; }

.alerts-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 16px;
  height: 16px;
  padding: 0 3px;
  border-radius: 50%;
  background: var(--error);
  color: #fff;
  font-size: 0.65em;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
}

/* Only while at least one alert is unacknowledged -- once everything
   active has been acknowledged, the badge count stays but the
   attention-grabbing flash stops (matches the dimmed, no-longer-
   flagged treatment .alert-row-acked already gives an acked row in
   the Alerts window's own table). */
.alerts-indicator.flashing { animation: alerts-flash 1s ease-in-out infinite; }

@keyframes alerts-flash {
  0%, 100% { background: var(--surface-2); }
  50% { background: var(--error); }
}

/* Generic dropdown positioning, shared by the Saved Layouts panel and
   the event switcher below -- .layouts-menu/.layouts-panel keep their
   own names too (existing id-based JS hooks reference them), just
   also match these shared rules rather than duplicating them. */
.dropdown-menu, .layouts-menu { position: relative; }

.dropdown-panel, .layouts-panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  width: 280px;
  background: var(--panel-bg);
  border: 1px solid var(--panel-border);
  border-radius: 6px;
  box-shadow: 0 16px 48px var(--shadow);
  padding: 0.6em;
  /* Above every window -- floats over the whole desktop, not just the
     topbar. */
  z-index: 9999;
}

/* Layouts' toggle sits in .topbar-right, so its panel opening
   leftward (right: 0, above) stays on screen. The event switcher's
   toggle sits at the left of the topbar instead -- opening leftward
   there runs the panel off the edge of the browser window, so it
   opens rightward here instead. */
.event-switcher .dropdown-panel { right: auto; left: 0; }

.layouts-empty { color: var(--text-dim); font-size: 0.85em; padding: 0.4em 0.2em; }

.layouts-row {
  display: flex;
  align-items: center;
  gap: 0.4em;
  padding: 0.25em 0;
}

.layouts-row button { padding: 0.35em 0.6em; font-size: 0.85em; }

.layouts-star {
  background: transparent;
  color: var(--text-dim);
  padding: 0.2em 0.4em;
  flex-shrink: 0;
}

.layouts-star.active { color: var(--accent); }

.layouts-name {
  background: transparent;
  color: var(--text);
  font-weight: 400;
  text-align: left;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.layouts-name:hover { text-decoration: underline; }

#layouts-save-as { width: 100%; margin-top: 0.5em; font-size: 0.85em; }

/* --- Event switcher --- */

.event-switcher-toggle {
  background: transparent;
  color: inherit;
  font: inherit;
  font-weight: inherit;
  padding: 0.2em 0.4em;
  margin: -0.2em -0.4em;
  border-radius: 4px;
}

.event-switcher-toggle:hover { background: var(--surface-2); }

.event-switcher-row {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  color: var(--text);
  font-weight: 400;
  padding: 0.4em 0.5em;
  border-radius: 4px;
}

.event-switcher-row:hover { background: var(--surface-2); }

.event-switcher-row.current { color: var(--accent); font-weight: 600; }

/* --- Admin panel (Course/Runner Editor, Event Settings/Log) --- */

.admin-shell {
  flex: 1;
  display: flex;
  min-height: 0;
}

.admin-sidebar {
  width: 200px;
  flex-shrink: 0;
  background: var(--panel-bg);
  border-right: 1px solid var(--panel-border);
  padding: 1em 0;
  overflow-y: auto;
}

.admin-nav-link {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  color: var(--text-dim);
  font-weight: 400;
  border-radius: 0;
  padding: 0.7em 1.25em;
}

.admin-nav-link:hover { background: var(--surface-2); color: var(--text); }
.admin-nav-link.active { background: var(--surface-2); color: var(--accent); font-weight: 600; }

.admin-content {
  flex: 1;
  overflow-y: auto;
  padding: 1.5em 2em;
}

.admin-panel { display: none; max-width: 700px; }
.admin-panel.active { display: block; }

/* Log messages run long and the table already fills its container --
   unlike the form-heavy panels above, this one benefits from the full
   content width instead of wrapping at 700px with empty space beside it. */
#admin-panel-event-log { max-width: none; }

/* Course Editor: form/table on the left, map on the right -- same reason
   as the event log above, the fixed 700px cap leaves the map with no
   room. flex-wrap alone (no media query needed) drops the map below the
   form once the columns' combined min-widths don't fit. */
#admin-panel-course-editor { max-width: none; }
.ce-layout { display: flex; gap: 1.5em; flex-wrap: wrap; align-items: flex-start; }
.ce-form-column { flex: 1 1 380px; min-width: 320px; max-width: 700px; }
.ce-map-column { flex: 1 1 420px; min-width: 320px; }
#ce-map {
  height: 520px;
  margin-bottom: 0.75em;
  border: 1px solid var(--panel-border);
  border-radius: 6px;
}

/* Leaflet ships its own hardcoded colors (white popups/controls) --
   overridden here rather than editing the vendored file, so dark mode
   doesn't get a jarring white popup in the middle of the map. */
.leaflet-popup-content-wrapper, .leaflet-popup-tip { background: var(--panel-bg); color: var(--text); }
.leaflet-container { background: var(--panel-bg); }
.leaflet-bar a { background: var(--panel-bg); color: var(--text); border-bottom-color: var(--panel-border); }
.leaflet-bar a:hover { background: var(--surface-2); }
.leaflet-control-layers { background: var(--panel-bg); color: var(--text); }
.leaflet-control-layers-separator { border-color: var(--panel-border); }
/* Station markers (course-map.js) are divIcons, not the default pin --
   Leaflet's .leaflet-div-icon base class adds its own white box/border
   that would otherwise show through as a square behind the circle. */
.leaflet-div-icon { background: transparent; border: none; }
.course-map-marker span { display: block; border-radius: 50%; border-style: solid; box-sizing: border-box; }

#desktop {
  flex: 1;
  position: relative;
  overflow: hidden;
  background: var(--desktop-bg);
}

/**
 * Modal backdrop (currently just backing Edit Entry, via
 * data-window-modal -- see window-manager.js's openModal()/closeModal()).
 * position:fixed so #desktop's overflow:hidden doesn't clip it -- this
 * needs to cover the whole viewport (topbar and taskbar included), not
 * just the desktop area, to actually block interaction with everything
 * else while a modal window is open. z-index set high enough to clear
 * any window's normal focus-order z-index (which only ever climbs into
 * the tens/hundreds in a real session); the modal window itself is
 * pushed above that each time it opens, see openModal().
 */
.modal-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  z-index: 9000;
}
.modal-backdrop.visible { display: block; }

/* --- Window panel chrome --- */

.window {
  position: absolute;
  display: flex;
  flex-direction: column;
  min-width: 340px;
  min-height: 200px;
  background: var(--panel-bg);
  border: 1px solid var(--panel-border);
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 8px 32px var(--shadow);
  z-index: 10;
}

.window.focused {
  box-shadow: 0 16px 48px var(--shadow), 0 0 0 1px var(--accent);
  z-index: 50;
}

.window.minimized { display: none; }

.window-titlebar {
  display: flex;
  align-items: center;
  gap: 0.6em;
  padding: 0.75em 1.1em;
  background: var(--titlebar-bg);
  border-bottom: 1px solid var(--panel-border);
  font-weight: 600;
  cursor: grab;
  user-select: none;
}

.window-titlebar:active { cursor: grabbing; }

.window-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
}

.window-title {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.window-controls { display: flex; gap: 0.4em; }

.window-btn {
  width: 22px;
  height: 22px;
  border-radius: 4px;
  border: 1px solid var(--panel-border);
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font: inherit;
}

.window-btn:hover { background: var(--panel-border); color: var(--text); }
.window-btn-close:hover { background: var(--error); border-color: var(--error); color: var(--on-accent); }

.window-body { flex: 1; overflow: auto; padding: 1.1em; }

.window-resize-handle {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 16px;
  height: 16px;
  cursor: nwse-resize;
  opacity: 0.4;
}

.window-resize-handle:hover { opacity: 1; }

/* --- Taskbar --- */

#taskbar {
  height: 38px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 0.4em;
  padding: 0 0.75em;
  background: var(--panel-bg);
  border-top: 1px solid var(--panel-border);
}

.taskbar-btn {
  display: flex;
  align-items: center;
  gap: 0.4em;
  height: 28px;
  padding: 0 0.7em;
  border-radius: 4px;
  border: 1px solid var(--panel-border);
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  font: inherit;
}

.taskbar-btn.open { background: var(--surface-2); color: var(--text); }
.taskbar-btn.focused { border-color: var(--accent); color: var(--accent); }

.taskbar-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
}

.field-row { display: flex; gap: 1em; }
.field-row > div { flex: 1; }

/* A standalone datetime/number field with no sibling to pair it with
   in a .field-row still shouldn't stretch to the panel's full width --
   their content (a fixed-format date/time or a short number) doesn't
   need it. */
.field-narrow { max-width: 240px; }

.bib-readout { font-size: 0.85em; margin-top: 0.3em; min-height: 1.2em; }
.bib-readout.ok { color: var(--success); }
.bib-readout.bad { color: var(--error); }

.day-choice { display: flex; gap: 1em; align-items: center; }
.day-choice label { display: inline-flex; align-items: center; gap: 0.4em; color: var(--text); margin: 0; }

.checkbox-row { display: flex; align-items: center; gap: 0.5em; margin: 0.75em 0; }
.checkbox-row input { width: auto; }
.checkbox-row label { margin: 0; color: var(--text); }

.entry-actions { display: flex; align-items: center; gap: 0.6em; margin-top: 1em; }

/* Course Editor/Runner Editor: shown while the form has edits that
   don't match the last-loaded (or last-saved) record -- see
   unsaved-changes.js. */
.dirty-indicator { color: var(--warning); font-size: 0.85em; }

.queue-status { margin: 0.75em 0; font-size: 0.9em; color: var(--text-dim); }

/* Alerts/Race Overview/Drop List/Event Log's lone Refresh button used
   to sit in its own .entry-actions row -- Ron: "the buttons take up a
   lot of space... an entire otherwise empty row." Moved into the
   titlebar as a small icon (dashboard windows) or beside the panel's
   own heading (Event Log, no titlebar there), reusing .window-btn's
   existing minimize/close styling rather than a new button treatment.
   Alerts' Acknowledge All is a real action, not just a refresh
   convenience, so it stayed a labeled button -- moved to share a row
   with the status text instead of commanding its own. */
.inline-action-row { display: flex; align-items: center; justify-content: space-between; gap: 0.75em; margin: 0.75em 0; }
.inline-action-row h2 { margin: 0; }
.inline-action-row .queue-status { margin: 0; }

/* .queue-status/.inline-action-row/table.queue-table's own margin-top
   above all assume something precedes them -- true in most windows,
   but not in one that opens straight on any of them (Race Overview/
   Drop List, once their status line folded into the window title --
   see race-overview.js/drop-list.js -- open straight on their table
   now, with nothing above it at all). Stacking that margin on
   .window-body's own padding gave an oversized gap at the very top of
   the window. */
.window-body > .queue-status:first-child,
.window-body > .inline-action-row:first-child,
.window-body > table.queue-table:first-child { margin-top: 0; }

table.queue-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 0.75em;
  font-size: 0.9em;
}

/* A roster/list table bounded to a fixed, independently-scrollable
   height instead of growing forever and pushing whatever's below it
   further down the page -- first built for Runner Editor's roster
   (Ron 2026-08-27: "clunky to edit runners because the fields are at
   the bottom of the window and scrolling is required"), reused as-is
   for Course Editor's station list ("Same improvement as was made to
   the runner editor"). Sticky header needs its own opaque background
   (--bg, matching this page's actual background -- .admin-panel sits
   directly on it, not --panel-bg) or scrolled rows would show through
   underneath it. */
.table-scroll { max-height: 320px; overflow-y: auto; margin-top: 0.75em; border: 1px solid var(--panel-border); border-radius: 4px; }
.table-scroll table.queue-table { margin-top: 0; }
.table-scroll table.queue-table th {
  position: sticky;
  top: 0;
  background: var(--bg);
}

table.queue-table th, table.queue-table td {
  text-align: left;
  padding: 0.4em 0.5em;
  border-bottom: 1px solid var(--panel-border);
}

table.queue-table tr.has-error td { color: var(--error); }
table.queue-table tr.has-warning td { color: var(--warning); }
table.queue-table .row-actions button { padding: 0.25em 0.6em; font-size: 0.85em; }

table.queue-table tbody tr.clickable-row { cursor: pointer; }
table.queue-table tbody tr.clickable-row:hover { background: var(--surface-2); }

table.queue-table th[data-sort] {
  cursor: pointer;
  user-select: none;
  position: relative;
  padding-right: 1.3em;
}
table.queue-table th[data-sort]:hover { color: var(--text); }
table.queue-table th[data-sort].sorted-asc::after,
table.queue-table th[data-sort].sorted-desc::after {
  position: absolute;
  right: 0.4em;
  font-size: 0.85em;
  color: var(--accent);
}
table.queue-table th[data-sort].sorted-asc::after { content: '\25B2'; }
table.queue-table th[data-sort].sorted-desc::after { content: '\25BC'; }

/* --- Stats row (Station Log) --- */

.stats-row {
  display: flex;
  gap: 0.75em;
  margin: 1em 0;
}

.stat-box {
  flex: 1;
  padding: 0.6em 0.9em;
  background: var(--surface-2);
  border: 1px solid var(--panel-border);
  border-radius: 6px;
}

.stat-val { font-size: 1.6em; font-weight: 700; color: var(--accent); line-height: 1.1; }
.stat-lbl { font-size: 0.8em; color: var(--text-dim); margin-top: 0.15em; }

/* --- Dual-list runner selector (Bulk Log Entry) --- */

.dual-list { display: flex; gap: 1em; margin-top: 1em; }
.dual-list-panel {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-width: 0;
  border: 1px solid var(--panel-border);
  border-radius: 6px;
  overflow: hidden;
}

.dual-list-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5em 0.7em;
  background: var(--surface-2);
  font-size: 0.85em;
  font-weight: 600;
}

.dual-list-header button.secondary { padding: 0.25em 0.6em; font-size: 0.85em; }

.dual-list-panel > input[type="text"] {
  border-radius: 0;
  border-width: 0 0 1px;
}

.dual-list-items {
  flex: 1;
  overflow-y: auto;
  min-height: 160px;
  max-height: 220px;
}

.dual-list-item {
  padding: 0.4em 0.7em;
  font-size: 0.9em;
  cursor: pointer;
  border-bottom: 1px solid var(--panel-border);
}

.dual-list-item:hover { background: var(--surface-2); }

/* --- Topbar clock --- */

/* Replaces the old standalone Race Clock window -- always visible in
   the topbar now, time and elapsed side by side per Ron's note. */
.topbar-clock {
  display: flex;
  align-items: baseline;
  gap: 0.6em;
  font-family: 'Courier New', ui-monospace, monospace;
}

.topbar-clock-time { font-size: 1.1em; font-weight: 700; color: var(--accent); letter-spacing: 0.03em; }
.topbar-clock-elapsed { font-size: 0.85em; color: var(--text-dim); }

/* --- Alerts --- */

/* Unacknowledged: full high-visibility red. Acknowledged: dimmed but
   still present -- stays listed (not hidden) until the sweep resolves
   it, see alerts.js's docblock. */
table.queue-table tr.alert-row td { background: var(--alert-bg); color: var(--error); font-weight: 600; }
table.queue-table tr.alert-row-acked td { background: var(--alert-bg-acked); color: var(--text-dim); font-weight: 400; }

/* --- Per-window minimum and maximum sizes ---

   The generic .window minimum above (340x200) is a lazy floor, not a
   real one -- most of these windows hold multi-column data tables that
   go unreadable well before 340px, especially once WindowManager.
   autoArrange() (public/assets/js/window-manager.js) has to fit many
   windows into a tight grid. Each min-width/min-height below is sized
   for that window's actual content (table column count, or a plain
   form's field rows), not just picked to look right at one screen
   size. Discussed with Ron 2026-08-26 after autoArrange() exposed the
   generic floor as too small; see docs/Rons_Notes.md.

   Maximums added in the same pass, same reasoning in reverse: most of
   these are narrow-column tables or small forms with nothing useful to
   do with a huge window -- extra space beyond a generous "comfortably
   see plenty of rows" size is just padding. Roughly 1.3-1.5x each
   window's own default size. Enforced by onResizeMove()/reclampAll()
   in window-manager.js, same as the minimums. (Race Clock used to be
   the one exception here -- no maximum, since a big room-visible clock
   display was a legitimate deliberate use, not wasted space -- but it
   moved into the topbar 2026-08-27 and isn't a window anymore.) */

/* 8-column queue table (Station/Bib/Runner/Day/Time/Dropped/Notes/
   action) plus two-column field rows above it. min-height raised
   420->550 -- 420 let the queue table itself shrink out of view
   entirely, leaving only the entry fields visible. Ron 2026-08-27. */
#log-entry { min-width: 480px; min-height: 550px; max-width: 1000px; max-height: 900px; }

/* Three-column field row, plus the dual-list Active/Selected runner
   panels side by side -- those need real width each to stay readable,
   not just the table columns. */
#bulk-log-entry { min-width: 560px; min-height: 420px; max-width: 1000px; max-height: 800px; }

/* 7-column sortable table (Pos/Bib/Runner/Station/Day/Time/Status). */
#race-overview { min-width: 460px; min-height: 260px; max-width: 700px; max-height: 900px; }

/* 6-column table (Bib/Runner/Expected At/Min. Overdue/Reminders/
   action) plus two buttons in the entry-actions row. */
#alerts { min-width: 440px; min-height: 260px; max-width: 700px; max-height: 800px; }

/* Without table-layout: fixed, the browser's auto column-sizing spreads
   any width beyond what the content needs unevenly across columns --
   at this window's default/min width that meant "Expected At"/"Min.
   Overdue" wrapping their headers and some runner names wrapping too
   (inflating every row's height); at a wider window it meant big,
   uneven gaps between short values instead. The colgroup in
   dashboard.tpl's #al-table gives each column a fixed share instead,
   scaling proportionally either way. Scoped to this one table, not
   table.queue-table generally -- every other window using that class
   relies on the default auto-sizing behavior and hasn't shown this
   problem. */
#al-table { table-layout: fixed; }

/* 6-column sortable table (Bib/Runner/Station/Day/Time/Notes). */
#drop-list { min-width: 460px; min-height: 240px; max-width: 900px; max-height: 700px; }

/* The widest table in the dashboard: 7 columns (Bib/Runner/Day/Time/
   Dropped/Source/Notes), plus a station selector and a stats row
   above it. */
#station-log { min-width: 520px; min-height: 340px; max-width: 900px; max-height: 900px; }

/* 7-column table (Station/Day/Time/Split/Dropped/Source/Notes) plus
   the runner-number lookup field above it. */
#runner-history { min-width: 520px; min-height: 320px; max-width: 900px; max-height: 900px; }

/* The smallest content in the dashboard -- a plain form, no table --
   but still needs room for the day-choice row and three buttons
   without wrapping badly. Sizes to its actual content on open rather
   than trusting these as a fixed value; see window-manager.js's
   openModal handling. */
#edit-entry { min-width: 360px; min-height: 340px; max-width: 600px; max-height: 600px; }
