/* ==========================================================================
   Devices online: the masthead counter and the map dialog

   Its own file so it does not collide with work in components.css. Reads
   semantic tokens only; no hex value belongs here.
   ========================================================================== */

@layer components {

.devices-online {
  display: inline-flex;
  align-items: center;
  gap: var(--step-2);
  min-height: 2.25rem;
  padding: var(--step-2) var(--step-3);
  font-family: var(--font-mono);
  font-size: var(--size-2xs);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-text-muted);
  background: var(--color-wash);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-pill);
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

.devices-online:hover {
  color: var(--color-text);
  border-color: var(--color-accent);
}

/* Hidden until the count arrives. A counter that says nothing is worse than no
   counter, and one that says zero while it is still loading is a lie. */
.devices-online[hidden] { display: none; }

.online-dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--glucose-ok);
  box-shadow: 0 0 8px var(--glucose-ok);
  flex: none;
  animation: pulse-dot 2s ease-in-out infinite;
}

@media (prefers-reduced-motion: reduce) {
  .online-dot { animation: none; }
}

/* The count sits on the far side of the wordmark on a phone, where the nav
   toggle needs the corner. */
@media (max-width: 47.999rem) {
  .devices-online { font-size: 0.625rem; padding-inline: var(--step-2); }
  .devices-online .devices-label { display: none; }
}

/* --------------------------------------------------------------------------
   The map dialog
   -------------------------------------------------------------------------- */
/* A dialog centres itself through its default margin: auto, and the reset's
   blanket `* { margin: 0 }` takes that away. Both dialogs on this site need it
   putting back explicitly, or they pin to the top-left corner. */
#map-dialog,
#shot-dialog { margin: auto; }

.map-dialog {
  width: min(56rem, 94vw);
  max-height: 88vh;
  padding: 0;
  background: var(--color-surface);
  color: var(--color-text);
  border: var(--hairline) solid var(--color-border-strong);
  border-radius: var(--radius-card);
  overflow: hidden;
}

.map-dialog::backdrop {
  background: color-mix(in srgb, var(--color-page) 78%, transparent);
  backdrop-filter: blur(4px);
}

.map-head {
  display: flex;
  align-items: flex-start;
  gap: var(--step-4);
  padding: var(--step-5);
  border-bottom: var(--hairline) solid var(--color-border);
}

.map-head h2 { font-size: var(--size-lg); }

.map-sub {
  margin-top: var(--step-1);
  font-size: var(--size-sm);
  color: var(--color-text-muted);
}

.map-close {
  margin-left: auto;
  width: 2.75rem;
  height: 2.75rem;
  display: grid;
  place-items: center;
  color: var(--color-text-muted);
  border-radius: var(--radius-sm);
}

.map-close:hover { color: var(--color-text); background: var(--color-wash); }

.map-canvas {
  height: min(56vh, 26rem);
  width: 100%;
  background: var(--color-surface-alt);
}

.map-foot {
  padding: var(--step-4) var(--step-5);
  border-top: var(--hairline) solid var(--color-border);
  font-size: var(--size-xs);
  color: var(--color-text-faint);
}

/* Leaflet draws its own chrome in a light theme by default, and its stylesheet
   is injected at runtime, so it lands after this file. These have to shout.

   The tile pane is dimmed rather than swapped for a dark tileset: the original
   site did exactly this, and it is what keeps the map reading as part of the
   page instead of a bright rectangle punched through it. */
.map-canvas .leaflet-container { background: var(--color-surface-alt); }
.map-canvas .leaflet-tile-pane { filter: brightness(0.6) saturate(0.85) contrast(1.02); }

.map-canvas .leaflet-popup-content-wrapper,
.map-canvas .leaflet-popup-tip {
  background: var(--color-surface-high) !important;
  color: var(--color-text) !important;
  border: var(--hairline) solid var(--color-border-strong) !important;
  border-radius: var(--radius-sm);
}

.map-canvas .leaflet-popup-content { margin: var(--step-3) var(--step-4) !important; font-family: var(--font-body); }
.map-canvas .leaflet-popup-close-button { color: var(--color-text-faint) !important; }

.map-canvas .leaflet-control-attribution {
  background: color-mix(in srgb, var(--color-page) 70%, transparent) !important;
  color: var(--color-text-faint) !important;
}
.map-canvas .leaflet-control-attribution a { color: var(--color-text-muted) !important; }

.map-canvas .leaflet-bar a {
  background: var(--color-surface-high) !important;
  color: var(--color-text) !important;
  border-bottom-color: var(--color-border) !important;
}
.map-canvas .leaflet-bar a:hover { background: var(--color-surface-hover) !important; }

/* --------------------------------------------------------------------------
   The beacon dots, as on the original site

   A solid dot with a dark rim so it separates from the dimmed map, and a ring
   that expands and fades like a radio ping. Each marker gets its own duration
   and delay from the script, so the pings never fall into step with each other
   and the map reads as live rather than as a blinking pattern. The ring is idle
   for most of its cycle on purpose: occasional pings, not a throb.
   -------------------------------------------------------------------------- */
.map-dot-wrap { background: none; border: 0; }

.map-dot {
  position: relative;
  width: var(--d);
  height: var(--d);
  border-radius: 50%;
  background: var(--color-accent);
  border: 2px solid color-mix(in srgb, var(--color-accent) 30%, var(--color-page));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-page) 55%, transparent),
              0 1px 4px color-mix(in srgb, var(--color-page) 45%, transparent);
}

.map-dot::after {
  content: '';
  position: absolute;
  inset: -2px;
  border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--color-accent) 80%, white);
  opacity: 0;
  pointer-events: none;
  animation: map-beacon var(--dur) ease-out infinite;
  animation-delay: var(--delay);
}

@keyframes map-beacon {
  0%   { transform: scale(1);   opacity: 0.8; }
  30%  { transform: scale(3.4); opacity: 0; }
  100% { transform: scale(3.4); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .map-dot::after { animation: none; }
}

.map-pop-title { font-weight: 600; }
.map-pop-meta { font-size: var(--size-xs); color: var(--color-text-muted); }

} /* @layer components */
