html,
body {
  height: 100%;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 14px;
}

body {
  display: flex;
}

#sidebar {
  width: 300px;
  flex: none;
  height: 100%;
  overflow-y: auto;
  background: #f7f7f8;
  border-right: 1px solid #ddd;
  box-sizing: border-box;
  padding: 14px;
}

#map {
  flex: 1;
  height: 100%;
  /* Leaflet's own CSS only sets this once specific handler classes are
     present on the container (added by its JS at init) — setting it here
     directly guarantees the browser hands every touch gesture to JS
     rather than intercepting a double-tap for its own native zoom before
     our double-tap handling (see event.html) ever sees it. */
  touch-action: none;
}

h1 {
  font-size: 16px;
  margin: 0 0 12px;
}

h2 {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #666;
  margin: 20px 0 8px;
}

/* Only the heading immediately after the page header sits flush against
   it — deliberately NOT h2:first-of-type, which matches per-parent: a
   heading wrapped in its own container (e.g. one shown/hidden by JS)
   would count as "first of its type" there too and wrongly lose its top
   margin, regardless of its real position in the page. */
.page-head+h2 {
  margin-top: 0;
}

form.stack {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

input[type=text],
input[type=password],
input[type=file],
input[type=datetime-local] {
  font-size: 13px;
  padding: 6px 8px;
  border: 1px solid #ccc;
  border-radius: 6px;
  box-sizing: border-box;
  width: 100%;
}

button {
  font-size: 13px;
  padding: 6px 10px;
  border: 1px solid #2563eb;
  border-radius: 6px;
  background: #2563eb;
  color: #fff;
  cursor: pointer;
}

button:hover {
  background: #1d4ed8;
}

button.secondary {
  background: #fff;
  color: #444;
  border-color: #ccc;
}

button.secondary:hover {
  background: #eee;
}

button.danger {
  background: #fff;
  color: #b91c1c;
  border-color: #fca5a5;
}

button.danger:hover {
  background: #fee2e2;
}

.or-divider {
  text-align: center;
  color: #999;
  font-size: 11px;
  margin: 2px 0;
}

#runners {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.runner {
  background: #fff;
  border: 1px solid #e2e2e2;
  border-radius: 8px;
  padding: 8px 10px;
  cursor: pointer;
}

.runner:active {
  background: #f3f4f6;
}

.runner-head {
  display: flex;
  align-items: center;
  gap: 6px;
}

.swatch {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  flex: none;
}

.runner-name {
  font-weight: 600;
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.runner-remove {
  background: none;
  border: none;
  color: #999;
  cursor: pointer;
  font-size: 15px;
  padding: 0 2px;
}

.runner-remove:hover {
  color: #b91c1c;
}

.runner-status {
  font-size: 11px;
  padding: 1px 6px;
  border-radius: 10px;
}

/* Lives inside .runner-stats, alongside distance/duration/pace —
   margin-left: auto pushes it to the right end of that same line (or
   its own line's right end, if the row wraps) rather than needing a
   separate row. */
.runner-finish-eta {
  margin-left: auto;
  white-space: nowrap;
}

.runner-status.active {
  background: #dcfce7;
  color: #166534;
}

.runner-status.finished {
  background: #e5e7eb;
  color: #374151;
}

.runner-status.error {
  background: #fee2e2;
  color: #991b1b;
}

.runner-stats {
  display: flex;
  gap: 10px;
  margin-top: 6px;
  font-size: 12px;
  color: #555;
  flex-wrap: wrap;
}

.runner-error {
  margin-top: 4px;
  font-size: 11px;
  color: #b91c1c;
  word-break: break-word;
}

.empty {
  color: #888;
  font-size: 12px;
}

#gpx-current {
  font-size: 12px;
  margin-top: 8px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 6px;
  background: #fff;
  border: 1px solid #e2e2e2;
  border-radius: 8px;
  padding: 6px 8px;
}

#gpx-current .name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.msg {
  font-size: 12px;
  margin-top: 6px;
}

.msg.error {
  color: #b91c1c;
}

.msg.ok {
  color: #1a7f37;
}

.leaflet-div-icon.km-marker {
  background: transparent;
  border: none;
}

.km-marker div {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid #555;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 700;
  color: #333;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
  cursor: pointer;
}

.leaflet-div-icon.route-endpoint-marker {
  background: transparent;
  border: none;
}

.route-endpoint-marker div {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 2px solid #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
  cursor: pointer;
}

/* Bigger than the route's own markers (km, start/finish) — a runner's
   current position is the single most important thing on the map, and
   needs to read clearly even zoomed out to see the whole course. The
   circle's own background color is set inline per runner (their
   assigned color), not here. */
.leaflet-div-icon.runner-marker {
  background: transparent;
  border: none;
}

.runner-marker div {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  border: 2px solid #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  color: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

/* Triggered by tapping a runner's card (focusRunner in event.html). The
   flash class lands on the outer marker element, which Leaflet positions
   via its own inline transform — animating transform there directly
   would fight that and displace the marker, so this scales the inner
   content div instead, which has no transform of its own to conflict with. */
@keyframes runner-marker-flash {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.7); }
}

.runner-marker-flashing div {
  animation: runner-marker-flash 0.6s ease-in-out 2;
}

/* Viewer's own position (event.html's 'locationfound' handler) — a small
   blue dot, with a heading cone layered behind it when the device reports
   one (only while actually moving; absent while stationary). Both are
   centered on the same 40x40 icon box so the cone rotates around the dot
   rather than around some other point. */
.leaflet-div-icon.viewer-marker {
  background: transparent;
  border: none;
}

.viewer-dot {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 14px;
  height: 14px;
  margin: -7px 0 0 -7px;
  border-radius: 50%;
  background: #4285f4;
  border: 2px solid #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

/* A 60deg wedge pointing at 0deg (up = true north) by default, then
   rotated by the actual heading via an inline transform (see viewerIcon
   in event.html) — conic-gradient rather than a rotated triangle/image so
   the wedge's angle and softness are just two numbers to tune here. */
.viewer-heading {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 40px;
  height: 40px;
  margin: -20px 0 0 -20px;
  border-radius: 50%;
  background: conic-gradient(from -30deg, rgba(66, 133, 244, 0.35) 0deg 60deg, transparent 60deg 360deg);
}

/* Shown once poll() has failed RECONNECT_FAILURE_THRESHOLD times in a row
   (event.html) — the map/sidebar otherwise give no sign the data on
   screen has gone stale. Fixed and centered so it's visible regardless
   of scroll position or which panel (sidebar vs. map) is in view; z-index
   comfortably above Leaflet's own panes (which top out around 650). */
.reconnect-banner {
  position: fixed;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10000;
  background: #b91c1c;
  color: #fff;
  font-size: 12px;
  font-weight: 600;
  padding: 6px 14px;
  border-radius: 0 0 8px 8px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}

.eta-popup {
  font-size: 12px;
}

.eta-group+.eta-group {
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid #eee;
}

.eta-title {
  font-weight: 700;
  margin-bottom: 6px;
}

.eta-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 2px 0;
  font-size: 12px;
}

.eta-row .swatch {
  width: 8px;
  height: 8px;
}

.eta-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.eta-detail {
  color: #555;
  white-space: nowrap;
}

.eta-directions {
  display: block;
  margin-top: 10px;
  padding-top: 8px;
  border-top: 1px solid #eee;
  font-size: 12px;
  color: #2563eb;
  text-decoration: none;
}

.eta-directions:hover {
  text-decoration: underline;
}

/* ── Full-width pages (event list, admin) ─────────────────────────────── */

.page {
  max-width: 720px;
  margin: 0 auto;
  padding: 24px 16px;
  box-sizing: border-box;
  width: 100%;
}

.page-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}

.breadcrumb {
  font-size: 12px;
  color: #666;
  text-decoration: none;
}

.breadcrumb:hover {
  text-decoration: underline;
}

.event-list {
  list-style: none;
  margin: 0 0 24px;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.event-card {
  background: #fff;
  border: 1px solid #e2e2e2;
  border-radius: 8px;
  padding: 12px 14px;
  display: flex;
  align-items: center;
  gap: 10px;
}

.event-card-main {
  flex: 1;
  min-width: 0;
}

.event-card-name {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.event-card-name a {
  color: inherit;
  text-decoration: none;
}

.event-card-name a:hover {
  text-decoration: underline;
}

.event-card-meta {
  font-size: 12px;
  color: #888;
  margin-top: 2px;
}

.event-card-actions {
  display: flex;
  gap: 6px;
  flex: none;
}

/* The expanded runners/route panel for one event — rendered as a second
   .event-card immediately after the event's own card (see managePanelHtml
   in admin.html), so it reads as an attached drawer rather than a
   separate, unrelated card. */
.manage-panel {
  margin-top: -8px;
  background: #f7f7f8;
  border-top: none;
  border-top-left-radius: 0;
  border-top-right-radius: 0;
  /* .event-card centers a single short child vertically by default —
     wrong here, where manage-panel-body is tall and needs to fill the
     card's full width instead. */
  align-items: stretch;
}

.manage-panel-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1;
  min-width: 0;
}

.manage-panel-body h2:first-child {
  margin-top: 0;
}

/* Reuses .runner/.runner-head/etc. from the event map page's sidebar list,
   but these rows aren't clickable (no map to focus there), so the pointer
   cursor and tap-highlight that make sense on that page would be
   misleading here. */
.runner-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.runner-list .runner {
  cursor: default;
}

.runner-list .runner:active {
  background: #fff;
}

.event-edit-form {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
}

.event-edit-row {
  display: flex;
  gap: 6px;
}

.event-edit-row input {
  flex: 1;
}

.event-edit-row > label {
  flex: 1;
}

.inline-form {
  display: flex;
  gap: 8px;
  align-items: flex-start;
}

.inline-form input {
  flex: 1;
}

.window-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  background: #f7f7f8;
  border: 1px solid #e2e2e2;
  border-radius: 8px;
}

.window-section-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #666;
}

.window-fields {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.window-label {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 12px;
  color: #555;
}

@media (max-width: 700px) {
  body {
    flex-direction: column;
  }

  #sidebar {
    width: 100%;
    height: auto;
    /* Sized by its own content (mainly the capped runner list below)
       rather than a fixed split, so the map can claim whatever's left —
       but capped so the map still gets at least half the screen even
       with every admin section open. */
    max-height: 50vh;
    box-sizing: border-box;
    border-right: none;
    border-bottom: 1px solid #ddd;
  }

  /* ~2.5 runner cards tall, the half-cut-off row hinting there's more
     to scroll to. Approximate — a card's real height varies with how
     many stat/error lines it has — but close enough for the hint to
     read clearly regardless. */
  #runners {
    max-height: 190px;
    overflow-y: auto;
  }

  #map {
    width: 100%;
    flex: 1;
  }

  button,
  .runner-remove {
    min-height: 32px;
  }

  .event-card {
    flex-wrap: wrap;
  }
}
