/* ==========================================================================
   Open Ground — map surfaces: the plate, the pins, and MapLibre's own chrome
   pulled into the palette.
   ========================================================================== */

.mapbox { position: absolute; inset: 0; }
.mapbox canvas { outline: none; }

.mapload {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 18px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-3);
  background: linear-gradient(to top, var(--ground-3), transparent);
  transition: opacity var(--ease), transform var(--ease);
}
.mapload__bar {
  width: 34px; height: 3px;
  border-radius: 3px;
  background: var(--ground-edge);
  overflow: hidden;
  position: relative;
}
.mapload__bar::after {
  content: '';
  position: absolute;
  inset: 0;
  width: 45%;
  border-radius: 3px;
  background: var(--pulse);
  animation: mapload 1.1s ease-in-out infinite;
}
@keyframes mapload {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(240%); }
}
.mapload[hidden] { display: flex; opacity: 0; transform: translateY(8px); pointer-events: none; }

/* -- MapLibre's own furniture ----------------------------------------- */
.maplibregl-ctrl-attrib {
  background: color-mix(in oklab, var(--surface-solid), transparent 12%) !important;
  color: var(--ink-3) !important;
  font-size: 10.5px !important;
  border-radius: var(--r-pill) 0 0 0 !important;
  padding: 2px 9px !important;
}
.maplibregl-ctrl-attrib a { color: var(--ink-2) !important; }
/* compact mode keeps room for its (i) button, or the button covers the credit */
.maplibregl-ctrl-attrib.maplibregl-compact { min-height: 24px; }
.maplibregl-ctrl-attrib.maplibregl-compact-show { padding-right: 30px !important; }
.maplibregl-ctrl-group {
  background: var(--surface-solid) !important;
  border-radius: var(--r-pill) !important;
  box-shadow: var(--lift) !important;
  overflow: hidden;
}
.maplibregl-ctrl-group button + button { border-top-color: var(--line) !important; }
.maplibregl-ctrl-group button { width: 32px !important; height: 32px !important; }
.maplibregl-ctrl-group button span { filter: none; }
:root[data-mode='dark'] .maplibregl-ctrl-group button .maplibregl-ctrl-icon { filter: invert(1) brightness(1.4); }
.maplibregl-popup-content {
  background: var(--surface-solid) !important;
  color: var(--ink) !important;
  border-radius: var(--r) !important;
  box-shadow: var(--lift-2) !important;
  padding: 14px 16px !important;
  font-family: var(--body);
}
.maplibregl-popup-tip {
  border-top-color: var(--surface-solid) !important;
  border-bottom-color: var(--surface-solid) !important;
}
.maplibregl-popup-close-button { color: var(--ink-3) !important; font-size: 18px !important; padding: 2px 8px !important; }

/* ══ Participant pins ═══════════════════════════════════════════════════
   A real pin with the bib number in it, not an arrow: the spectator is
   looking for one number in a field, and the number is the whole job. */
/* The marker element itself carries MapLibre's own positioning transform and
   must never set one of its own; everything that moves lives on .pin__in.
   The whole pin is drawn a tenth smaller than it used to be: a bunch of
   forty reads as a field rather than as a wall of teardrops, and a four
   digit bib still fits. Every measure below is that same 0.9. */
.pin {
  width: 36px;
  height: 45px;
  cursor: pointer;
}
.pin__in {
  position: absolute;
  inset: 0;
  transform-origin: 50% 100%;
  transition: transform var(--spring), filter var(--ease-s);
  will-change: transform;
}
.pin:hover { z-index: 5; }
.pin[data-on='1'] { z-index: 6; }
.pin:hover .pin__in,
.pin[data-on='1'] .pin__in { transform: scale(1.18) translateY(-3px); }

.pin__body {
  position: absolute;
  inset: 0 0 7px 0;
  border-radius: 50% 50% 50% 50% / 46% 46% 54% 54%;
  background: var(--brand);
  color: var(--on-brand);
  display: grid;
  place-items: center;
  box-shadow: 0 6px 16px rgba(var(--shadow), calc(var(--shadow-a) * 1.6));
}
/* the point of the pin, drawn as part of the same body */
.pin__body::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -6px;
  width: 11px; height: 11px;
  background: inherit;
  transform: translateX(-50%) rotate(45deg);
  border-radius: 0 0 3px 0;
  clip-path: polygon(0 100%, 100% 0, 100% 100%);
}
.pin__bib {
  font-family: var(--display);
  font-weight: 700;
  font-size: 13.5px;
  letter-spacing: -0.03em;
  line-height: 1;
  padding-bottom: 2px;
}
.pin__bib[data-len='3'] { font-size: 11.5px; }
.pin__bib[data-len='4'] { font-size: 10px; }

/* ── Map legend, so the pin vocabulary is stated on the surface ───────── */
.mlegend {
  position: absolute;
  left: 12px; bottom: 12px;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  padding: 5px;
  border-radius: var(--r-pill);
  background: color-mix(in oklab, var(--surface-solid), transparent 6%);
  backdrop-filter: blur(8px);
  box-shadow: var(--lift);
  max-width: calc(100% - 24px);
}
.mlegend span {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 11px;
  border-radius: var(--r-pill);
  font-size: 11.5px;
  font-weight: 650;
  color: var(--ink-2);
  white-space: nowrap;
}
.mlegend i {
  width: 11px; height: 11px;
  border-radius: 50% 50% 50% 50% / 46% 46% 54% 54%;
  display: block;
  position: relative;
  flex: none;
}
.mlegend .l-real { background: var(--brand); }
.mlegend .l-stale { background: var(--warn); }
.mlegend .l-off { background: var(--ground-edge); }
@media (max-width: 720px) {
  /* All four entries stay: the legend is the key to a channel the map uses,
     so half a key is worse than a taller one. It clears the attribution. */
  .mlegend {
    left: 10px; right: 10px;
    bottom: 34px;
    max-width: none;
    border-radius: var(--r);
    justify-content: flex-start;
  }
  .mlegend span { padding: 3px 9px; font-size: 11px; gap: 5px; }
}
.pin--fav .pin__body { background: var(--pulse); color: var(--on-pulse); }
.pin--stale .pin__body { background: var(--warn); color: var(--surface-solid); }
.pin--off .pin__body { background: var(--ground-edge); color: var(--ink-2); }

.pin__ring {
  position: absolute;
  inset: -5px -5px 2px -5px;
  border-radius: 50%;
  border: 2px solid var(--pulse);
  opacity: 0;
  animation: pin-ring 2.4s ease-out infinite;
}
/* Only the pin being followed rings. Thirty-six of these at once was
   motion for its own sake, and the device state already has a column. */
.pin[data-on='1'] .pin__ring { opacity: 1; }
@keyframes pin-ring {
  0%   { transform: scale(0.72); opacity: 0.6; }
  70%  { transform: scale(1.5); opacity: 0; }
  100% { transform: scale(1.5); opacity: 0; }
}

.pin__name {
  position: absolute;
  left: 50%;
  top: -9px;
  transform: translate(-50%, -100%) scale(0.9);
  transform-origin: bottom center;
  white-space: nowrap;
  padding: 4px 11px;
  border-radius: var(--r-pill);
  background: var(--surface-solid);
  color: var(--ink);
  font-size: 12.5px;
  font-weight: 650;
  box-shadow: var(--lift);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--ease-xs), transform var(--spring);
}
.pin:hover .pin__name,
.pin[data-on='1'] .pin__name,
.pin__name[data-show='1'] { opacity: 1; transform: translate(-50%, -100%) scale(1); }

/* ══ Checkpoint marks ═══════════════════════════════════════════════════
   Same rule as the pins: the marker element is MapLibre's to transform. */
.cpm { width: 26px; height: 26px; cursor: pointer; }
.cpm__in {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--surface-solid);
  color: var(--second);
  box-shadow: 0 3px 10px rgba(var(--shadow), var(--shadow-a)), inset 0 0 0 2px currentColor;
  font-size: 11px;
  font-weight: 700;
  transition: transform var(--spring);
}
.cpm:hover .cpm__in { transform: scale(1.28); }
.cpm--start .cpm__in { color: var(--pulse-ink); }
.cpm--finish .cpm__in { color: var(--brand); }
:root[data-mode='dark'] .cpm--start .cpm__in { color: var(--pulse); }

/* ══ The stage ═══════════════════════════════════════════════════════════
   Both map pages, one scheme, the one ../user-redesign uses: the map is the
   whole window under the masthead, edge to edge, with no ground of the
   page's own around it, and everything else floats on it. The list is a
   dock of frosted glass on the left that slides off the edge from a handle
   on its side and leaves the map behind it; on a phone the dock is a sheet
   along the bottom instead.

   --dock-off is how much of the left edge the dock is covering, so the
   chrome along the bottom and the top can start where the map is visible
   and move over when the dock slides away. */
body.is-stage { overflow: hidden; }
/* --top-h is the masthead's measured height; chrome.js publishes it. */
body.is-stage .page { height: calc(100dvh - var(--top-h, 62px)); }

.stage {
  position: relative;
  height: 100%;
  overflow: hidden;
  background: var(--map-ground);
  --dock-w: min(400px, calc(100% - 64px));
  --dock-gap: 12px;
  --dock-off: calc(var(--dock-w) + var(--dock-gap));
}
.stage[data-dock='closed'] { --dock-off: 0px; }
.stage[hidden] { display: none; }

.dock {
  position: absolute;
  z-index: 10;
  top: var(--dock-gap);
  left: var(--dock-gap);
  bottom: var(--dock-gap);
  width: var(--dock-w);
  transition: transform var(--spring);
}
.stage[data-dock='closed'] .dock { transform: translateX(calc(-100% - var(--dock-gap))); }

.dock__panel {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  border-radius: var(--r-lg);
  background: color-mix(in oklab, var(--surface-solid), transparent 12%);
  backdrop-filter: blur(14px) saturate(1.1);
  -webkit-backdrop-filter: blur(14px) saturate(1.1);
  /* a hairline as well as a shadow: on a dark map the shadow alone left the
     panel almost the same value as the ground it sits on */
  box-shadow: var(--lift-2), inset 0 0 0 1px var(--line);
}

/* The handle stays on the dock's own edge, so the gesture reads as sliding
   the list aside, and it is all that is left showing when it has gone. */
.dock__handle {
  position: absolute;
  top: 50%;
  right: -30px;
  width: 30px;
  height: 64px;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  border-radius: 0 12px 12px 0;
  background: color-mix(in oklab, var(--surface-solid), transparent 12%);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: var(--lift), inset 0 0 0 1px var(--line);
  color: var(--ink-2);
  transition: color var(--ease-xs);
}
.dock__handle svg { width: 15px; height: 15px; transition: transform var(--spring); }
.dock__handle:hover { color: var(--pulse-ink); }
.stage[data-dock='closed'] .dock__handle svg { transform: rotate(180deg); }

/* Narrow and tall: the dock becomes a sheet along the bottom and the map
   keeps the top. A phone held sideways is narrow but short, and keeps the
   side dock, because a sheet would leave it no map at all. */
@media (max-width: 900px) and (min-height: 500px) {
  .stage { --dock-off: 0px; }
  .dock {
    top: auto;
    left: 10px; right: 10px; bottom: 10px;
    width: auto;
    height: 58dvh;
  }
  .stage[data-dock='closed'] .dock { transform: translateY(calc(100% + 10px)); }
  .dock__handle {
    top: -34px; right: auto; left: 50%;
    width: 72px; height: 34px;
    transform: translateX(-50%);
    border-radius: 12px 12px 0 0;
  }
  .dock__handle svg { transform: rotate(-90deg); }
  .stage[data-dock='closed'] .dock__handle svg { transform: rotate(90deg); }
}

/* ══ The events map ═════════════════════════════════════════════════════ */
/* The page's own ground, not the map's: zoomed out to the ball, this is the
   space around it, and in the map's colour the land would bleed into it. */
.worldwrap {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: var(--ground);
}
#worldReal { position: absolute; inset: 0; --evpin-s: 1; }

/* An event: a plate with its own course drawn inside it. MapLibre owns the
   transform of the marker itself, so everything that scales or animates
   lives on .evpin__in and .evpin__name. */
.evpin {
  width: 46px;
  height: 46px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.evpin--live { width: 58px; height: 58px; }
.evpin[hidden] { display: none; }
.evpin[data-on='1'] { z-index: 3; }
.evpin__in {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: var(--surface-solid);
  box-shadow:
    inset 0 0 0 1.8px color-mix(in oklab, var(--ink-3), transparent 45%),
    0 4px 12px rgba(var(--shadow), calc(var(--shadow-a) * 1.3));
  transform: scale(var(--evpin-s));
  transition: transform var(--spring);
}
.evpin--live .evpin__in {
  box-shadow:
    inset 0 0 0 2.6px var(--pulse),
    0 4px 12px rgba(var(--shadow), calc(var(--shadow-a) * 2));
}
.evpin__in svg { display: block; width: 100%; height: 100%; border-radius: 50%; }
.evpin[data-on='1'] .evpin__in { transform: scale(calc(var(--evpin-s) * 1.3)); }
/* a live event keeps a slow ring going out from it */
.evpin--live .evpin__in::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid var(--pulse);
  opacity: 0;
  animation: evpin-ring 2.6s ease-out infinite;
  pointer-events: none;
}
@keyframes evpin-ring {
  from { transform: scale(1); opacity: 0.42; }
  to { transform: scale(1.9); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .evpin--live .evpin__in::before { animation: none; }
}
/* the name only appears for the one being looked at */
.evpin__name {
  position: absolute;
  left: 50%;
  top: calc(50% + 50% * var(--evpin-s) * 1.3 + 8px);
  transform: translateX(-50%);
  padding: 4px 11px;
  border-radius: 12px;
  background: var(--surface-solid);
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.18);
  color: var(--ink);
  font: 650 13px/1.3 "Hanken Grotesk", system-ui, sans-serif;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--ease-xs);
}
.evpin[data-on='1'] .evpin__name { opacity: 1; }
/* several events in one plate: the count, on a plate that reads as a stack */
.evgroup .evpin__in {
  display: grid;
  place-items: center;
  box-shadow:
    inset 0 0 0 1.8px color-mix(in oklab, var(--ink-3), transparent 45%),
    3px -3px 0 -1px var(--surface-solid),
    3px -3px 0 0.5px color-mix(in oklab, var(--ink-3), transparent 55%),
    0 4px 12px rgba(var(--shadow), calc(var(--shadow-a) * 1.3));
}
.evgroup.evpin--live .evpin__in {
  box-shadow:
    inset 0 0 0 2.6px var(--pulse),
    3px -3px 0 -1px var(--surface-solid),
    3px -3px 0 0.5px var(--pulse),
    0 4px 12px rgba(var(--shadow), calc(var(--shadow-a) * 2));
}
.evgroup__n {
  font-family: var(--display);
  font-size: 19px;
  font-weight: 700;
  color: var(--ink);
  line-height: 1;
}
.evgroup.evpin--live .evgroup__n { color: var(--pulse-ink); }
:root[data-mode='dark'] .evgroup.evpin--live .evgroup__n { color: var(--pulse); }

/* A pin standing in for several: the leader keeps their bib and carries the
   count, so the field stays countable where it is too dense to read. */
.pin--group .pin__body::before {
  content: attr(data-more);
  position: absolute;
  right: -10px;
  top: -6px;
  padding: 2px 6px;
  border-radius: var(--r-pill);
  background: var(--ink);
  color: var(--ground);
  font-family: var(--body);
  font-size: var(--t-pip);
  font-weight: 700;
  line-height: 1.3;
  letter-spacing: 0;
  box-shadow: 0 2px 6px rgba(var(--shadow), calc(var(--shadow-a) * 1.4));
}

/* ── The elevation profile ────────────────────────────────────────────────
   Shared: full width under the live map with the whole field on it, and on
   one participant's page with only them. */
/* The hill is drawn in its own course's colour — the one the organiser set
   and the map is drawing — so the chart and the line on the map agree. */
.eplot { position: relative; height: 110px; border-radius: var(--r-sm); background: var(--surface-sunk); overflow: hidden; --c: var(--pulse); }
.eplot svg { width: 100%; height: 100%; display: block; }
.eplot__fill { fill: var(--c); opacity: 0.16; }
.eplot__ridge { fill: none; stroke: var(--c); stroke-width: 1.8; stroke-linejoin: round; }
.eplot__cp { position: absolute; bottom: 0; width: 1.5px; height: 16px; background: var(--second); opacity: 0.75; }
/* Both along the top: the bibs ride along the bottom, and a participant near
   the end of the course used to land on top of the low-point label. */
.eplot__hi, .eplot__lo {
  position: absolute;
  top: 5px;
  font-size: var(--t-pip);
  font-weight: 650;
  color: var(--ink-3);
}
.eplot__hi { right: 8px; }
.eplot__lo { left: 8px; }

/* every participant on this course, at the height they are riding */
.eplot__p {
  position: absolute;
  bottom: 6px;
  transform: translateX(-50%);
  display: grid;
  place-items: center;
  min-width: 20px; height: 17px;
  padding: 0 5px;
  border-radius: var(--r-pill);
  background: var(--brand);
  color: var(--on-brand);
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--t-pip);
  box-shadow: 0 2px 6px rgba(var(--shadow), var(--shadow-a));
  opacity: 0.9;
  transition: transform var(--spring), opacity var(--ease-xs);
}
.eplot__p:hover { transform: translateX(-50%) scale(1.18); opacity: 1; z-index: 3; }
.eplot__p.is-on { background: var(--pulse); color: var(--on-pulse); opacity: 1; z-index: 4; transform: translateX(-50%) scale(1.15); }

.eplot__axis {
  display: flex;
  justify-content: space-between;
  margin-top: 4px;
  font-size: var(--t-pip);
  font-weight: 650;
  color: var(--ink-3);
}
