* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: #0b0f14;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: #f1f3f5;
}

#map {
  position: absolute;
  inset: 0;
  touch-action: none;
}

#loading {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(-53.94deg, #f4ffcc 8.39%, #ffffff 80.97%);
  z-index: 50;
  pointer-events: none;
  transition: opacity 0.4s ease;
}

#loading.hidden {
  opacity: 0;
  pointer-events: none;
}

.loading-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  width: 258px;
}

.loading-logo {
  width: 52px;
  height: 50px;
}

.loading-logo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.loading-title {
  font-family: 'Area Inktrap Compressed', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 900;
  font-size: 22px;
  line-height: 26px;
  letter-spacing: -0.44px;
  text-align: center;
  text-transform: uppercase;
  background: linear-gradient(100.6deg, #748f6a 1.82%, #55ce29 100.6%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.loading-spinner {
  animation: loading-spin 1s linear infinite;
}

@keyframes loading-spin {
  to {
    transform: rotate(360deg);
  }
}

/* ---------- Landmark labels ----------
   Map-anchored tags (one per landmark, pinned at its own lng/lat via a
   MapLibre Marker in app.js) instead of a screen-corner toast. The outer
   .landmark-label element is Marker's own positioning node — its
   transform is owned by MapLibre (pitchAlignment/rotationAlignment:
   'viewport' in app.js, so it stays a flat, always-horizontal screen-space
   sign, never tilted or rotated to the map's own pitch/bearing) and must
   stay untouched. All visual styling, including the tilt below, lives on
   the .landmark-label-inner child instead.

   .is-hidden is what makes the tag behave like a one-sided "banner on the
   road" despite staying flat on screen — app.js's
   updateLandmarkLabelStates() compares the camera's current heading
   against the tag's own road bearing (lm.bearingDeg) and toggles this
   class, fading the tag out once that angle passes
   LANDMARK_LABEL_VISIBLE_ANGLE_DEG, so it only shows up while you're
   roughly facing it (e.g. hidden again after you've passed it or the
   route's turned away), the way a real roadside sign would — without ever
   warping the label's own on-screen shape to do it.

   The tilt on .landmark-label-inner is a separate, fixed, one-time
   transform (matching the Figma design), NOT recomputed from camera
   pitch/zoom — an earlier version reacted to the chase camera's
   continuously-settling pitch/zoom every frame, which read as the label
   visibly bobbling/rescaling in place. transform-origin stays pinned to
   the tail tip so the fixed tilt still pivots from the point it's
   labeling rather than the tag's center. The tail sits near the LEFT edge
   (its tip's x position must match LANDMARK_LABEL_TAIL_X_PX in app.js,
   which uses it to offset the marker's 'bottom-left' anchor onto that
   exact tip).

   Active/passive: a label is "active" (solid #212121 black, white text) for
   a window around the moment the runner reaches it, and "passive" (black
   at 30% opacity, white text) otherwise — app.js's
   updateLandmarkLabelStates() toggles is-active/is-passive on this
   element. Both the card and its tail read off the same --label-bg custom
   property so they always agree on which state they're in. */

.landmark-label {
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.15s linear;
}

.landmark-label.is-hidden {
  opacity: 0;
}

.landmark-label-inner {
  --label-bg: rgba(33, 33, 33, 0.3);
  position: relative;
  display: inline-block;
  white-space: nowrap;
  background: var(--label-bg);
  color: #fff;
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 600;
  font-size: 11.4px;
  letter-spacing: -0.02em;
  padding: 4px 8px 5.4px;
  border-radius: 5.4px;
  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);
  transform-origin: 16.1px 100%;
  transform: rotate(-5deg) skewX(-6deg) scaleY(0.99);
}

.landmark-label-inner.is-active {
  --label-bg: #212121;
}

.landmark-label-inner.is-passive {
  --label-bg: rgba(33, 33, 33, 0.3);
}

.landmark-label-inner::after {
  content: '';
  position: absolute;
  left: 12.1px;
  bottom: -6.7px;
  width: 8px;
  height: 6.7px;
  background: var(--label-bg);
  clip-path: polygon(0 0, 100% 0, 50% 100%);
}

.hidden {
  display: none !important;
}

/* ---------- Route widget font ----------
   Area Trial Inktrap Compressed, self-hosted from the trial family the
   design was built with (see area-trial/ at the repo root — a trial
   license, not for redistribution beyond this project). Only the two
   weights the widget actually uses (Black/XBold, regular + italic) are
   copied into assets/fonts/. A single family name carries all four via
   font-weight/font-style so element rules below just set weight+style. */

@font-face {
  font-family: 'Area Inktrap Compressed';
  src: url('assets/fonts/AreaTrialInkCMP-XBold.woff2') format('woff2'),
       url('assets/fonts/AreaTrialInkCMP-XBold.woff') format('woff');
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Area Inktrap Compressed';
  src: url('assets/fonts/AreaTrialInkCMP-XBoldItalic.woff2') format('woff2'),
       url('assets/fonts/AreaTrialInkCMP-XBoldItalic.woff') format('woff');
  font-weight: 800;
  font-style: italic;
  font-display: swap;
}

@font-face {
  font-family: 'Area Inktrap Compressed';
  src: url('assets/fonts/AreaTrialInkCMP-Black.woff2') format('woff2'),
       url('assets/fonts/AreaTrialInkCMP-Black.woff') format('woff');
  font-weight: 900;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'Area Inktrap Compressed';
  src: url('assets/fonts/AreaTrialInkCMP-BlackItalic.woff2') format('woff2'),
       url('assets/fonts/AreaTrialInkCMP-BlackItalic.woff') format('woff');
  font-weight: 900;
  font-style: italic;
  font-display: swap;
}

/* ---------- Route widget ----------
   Corner HUD card (Figma node 226:253), pinned at a fixed 12px inset from
   the viewport's own top-left corner. Live parts, all driven off
   state.progressKm every frame: the km counter, the landmark name, the
   elevation readout + its graph, and the mini route map in the photo slot
   — see the "Route widget" section in app.js. */

#route-widget {
  position: absolute;
  top: 12px;
  left: 12px;
  z-index: 25;
  width: 276px;
  max-width: calc(100vw - 24px);
  background: linear-gradient(132deg, #f3ffcc 5%, #d3ef73 51%);
  border-radius: 24px 24px 42px 42px;
  overflow: hidden;
  font-family: 'Area Inktrap Compressed', 'Oswald', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

.rw-top {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 18px 16px 0;
}

.rw-header {
  display: flex;
  align-items: center;
  gap: 12px;
}

.rw-title {
  flex: 1 1 0;
  font-weight: 900;
  font-size: 18px;
  line-height: 1.2;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  background: linear-gradient(98deg, #748f6a 2%, #55ce29 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.rw-logo {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
}

.rw-logo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.rw-total {
  display: flex;
  align-items: center;
  gap: 7px;
  color: #2b6815;
  font-weight: 800;
  font-size: 22px;
  letter-spacing: -0.01em;
}

.rw-route {
  position: relative;
  align-self: flex-start;
}

button.rw-total {
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
  font-family: inherit;
  line-height: 1;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

.rw-route-chevron {
  transition: transform 0.18s ease;
}

#rw-route-toggle[aria-expanded='true'] .rw-route-chevron {
  transform: rotate(180deg);
}

.rw-route-menu {
  position: absolute;
  top: calc(100% + 8px);
  left: -6px;
  z-index: 40;
  display: none;
  flex-direction: column;
  min-width: 150px;
  padding: 6px;
  background: #ffffff;
  border-radius: 18px;
  box-shadow: 0 10px 28px rgba(20, 40, 8, 0.22);
}

.rw-route-menu.is-open {
  display: flex;
}

.rw-route-option {
  margin: 0;
  padding: 9px 14px;
  background: none;
  border: 0;
  border-radius: 12px;
  font-family: inherit;
  font-weight: 800;
  font-size: 22px;
  letter-spacing: -0.01em;
  color: #2b6815;
  text-align: left;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}

.rw-route-option:hover {
  background: rgba(43, 104, 21, 0.1);
}

.rw-route-option.is-selected {
  background: rgba(43, 104, 21, 0.16);
}

.rw-panel {
  background: #d4fe43;
  border-radius: 12px 12px 42px 42px;
  padding: 12px;
  margin-top: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.rw-photo {
  position: relative;
  overflow: hidden;
  width: 100%;
  aspect-ratio: 263 / 344;
  border-radius: 12px 12px 0 0;
  background: #f5f3e4;
}

.rw-photo svg {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
}

.rw-mini-map-basemap {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.rw-mini-map-dot {
  position: absolute;
  left: 0;
  top: 0;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #f0824b;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
  transform: translate(-50%, -50%);
  pointer-events: none;
}

.rw-stats {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 0 4px;
}

.rw-km-row {
  display: flex;
  align-items: flex-end;
  gap: 8px;
  color: #313131;
  font-style: italic;
}

#rw-km-count {
  font-weight: 900;
  font-size: 70px;
  line-height: 1;
}

.rw-km-unit {
  font-weight: 800;
  font-size: 30px;
  line-height: 1.3;
}

.rw-landmark {
  position: relative;
  height: 58px;
  overflow: hidden;
  color: #2b6815;
  font-weight: 800;
  font-size: 22px;
  line-height: 27px;
  text-transform: uppercase;
}

.rw-landmark-slot {
  position: absolute;
  inset: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  transform: translateY(0);
  opacity: 1;
  transition: transform 0.45s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.45s ease;
}

.rw-landmark-slot.rw-landmark-enter {
  transform: translateY(-100%);
  opacity: 0;
}

.rw-landmark-slot.rw-landmark-exit {
  transform: translateY(100%);
  opacity: 0;
}

.rw-elevation-row {
  display: flex;
  align-items: baseline;
  color: #2b6815;
}

.rw-elevation-label {
  font-weight: 800;
  font-size: 20px;
}

.rw-elevation-value {
  font-weight: 900;
  font-size: 20px;
}

.rw-elevation-graph {
  position: relative;
  width: 100%;
  height: 82px;
  margin-top: 8px;
}

.rw-elevation-graph svg {
  display: block;
  width: 100%;
  height: 100%;
}

.rw-elevation-dot {
  position: absolute;
  left: 0;
  top: 0;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #2b6815;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
  transform: translate(-50%, -50%);
}

.rw-play {
  align-self: center;
  width: 100%;
  height: 44px;
  margin-top: 4px;
  border: none;
  border-radius: 100px;
  background: #151515;
  color: #fff;
  font-family: inherit;
  font-weight: 800;
  font-style: italic;
  font-size: 20px;
  letter-spacing: 0.02em;
  cursor: pointer;
}

.rw-play:active {
  background: #2a2a2a;
}

.rw-restart {
  align-self: center;
  width: 100%;
  padding: 2px 0 4px;
  border: none;
  background: none;
  color: #242323;
  font-family: inherit;
  font-weight: 800;
  font-style: italic;
  font-size: 13px;
  letter-spacing: 0.02em;
  text-align: center;
  cursor: pointer;
}

.rw-restart:active {
  opacity: 0.6;
}

/* ---------- Route widget — phone layout (Figma node 253:3777) ----------
   The card drops its mini-map photo and elevation graph, the title goes to
   one line, and the km counter sits beside the landmark / elevation text
   instead of above it. The whole widget is re-pinned to the bottom of the
   viewport with a 16px inset (+ safe-area) so it clears the notch/toolbars,
   and the distance dropdown opens upward so it doesn't cover the panel. */
@media (max-width: 560px) {
  #route-widget {
    top: auto;
    left: 16px;
    right: 16px;
    bottom: calc(16px + env(safe-area-inset-bottom, 0px));
    width: auto;
    max-width: none;
    border-radius: 18px 18px 28px 28px;
    overflow: visible;
  }

  .rw-top {
    padding: 12px 12px 0;
    gap: 8px;
  }

  .rw-header {
    gap: 10px;
  }

  .rw-title {
    font-size: 14px;
  }

  .rw-title br {
    display: none;
  }

  .rw-logo {
    width: 26px;
    height: 26px;
  }

  .rw-total {
    font-size: 18px;
  }

  .rw-route-menu {
    top: auto;
    bottom: calc(100% + 8px);
  }

  .rw-route-option {
    font-size: 18px;
    padding: 8px 12px;
  }

  .rw-panel {
    margin-top: 8px;
    padding: 8px;
    border-radius: 8px 8px 28px 28px;
  }

  .rw-photo,
  .rw-elevation-graph {
    display: none;
  }

  .rw-stats {
    display: grid;
    grid-template-columns: auto 1fr;
    column-gap: 28px;
    row-gap: 4px;
    padding: 2px;
  }

  .rw-km-row {
    grid-column: 1;
    grid-row: 1 / span 2;
    align-self: center;
  }

  #rw-km-count {
    font-size: 44px;
  }

  .rw-km-unit {
    font-size: 21px;
  }

  .rw-landmark {
    grid-column: 2;
    grid-row: 1;
    align-self: end;
    height: 18px;
    font-size: 14px;
    line-height: 18px;
  }

  .rw-landmark .rw-landmark-slot {
    -webkit-line-clamp: 1;
  }

  .rw-elevation {
    grid-column: 2;
    grid-row: 2;
    align-self: start;
  }

  .rw-elevation-label,
  .rw-elevation-value {
    font-size: 13px;
  }

  .rw-play {
    grid-column: 1 / -1;
    grid-row: 3;
    height: 38px;
    margin-top: 0;
    font-size: 16px;
  }

  .rw-restart {
    grid-column: 1 / -1;
    grid-row: 4;
    padding: 0;
    font-size: 11px;
  }
}

/* ---------- Keyboard hint bar (Figma node 226:236) ----------
   Only shown while paused/stopped — app.js toggles the shared .hidden
   class on this from setPlaying() (hidden the instant playback starts)
   and once at boot (visible by default, since state.isPlaying starts
   false). Spacebar always toggles play/pause; the arrow-key seek it
   describes only runs while paused — see wireControls() in app.js. */

.kbd-hint {
  position: absolute;
  left: 50%;
  bottom: 20px;
  transform: translateX(-50%);
  z-index: 25;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 8px;
  max-width: calc(100vw - 24px);
  pointer-events: none;
}

.kbd-hint-text {
  font-family: 'Area Inktrap Compressed', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 900;
  font-style: italic;
  font-size: 14px;
  color: #3e3e3e;
  text-shadow: 0 1px 3px rgba(255, 255, 255, 0.8), 0 0 8px rgba(255, 255, 255, 0.55);
  white-space: nowrap;
}

.kbd-key {
  flex-shrink: 0;
}

.kbd-key--arrow {
  width: 19px;
  height: 18px;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.35));
}

.kbd-key--arrow svg {
  display: block;
  width: 100%;
  height: 100%;
}

.kbd-key--space {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 19px;
  padding: 0 9px;
  border-radius: 2px;
  border: 1px solid #000;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.06) 100%), #191a1f;
  box-shadow: inset 0 1px 0.3px rgba(255, 255, 255, 0.15), 0 1px 2px rgba(0, 0, 0, 0.35);
  font-family: 'Area Inktrap Compressed', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 800;
  font-size: 12px;
  color: #fff;
  white-space: nowrap;
}

/* Touch devices have no arrow keys / spacebar, and the bottom-pinned phone
   widget occupies this space anyway — see the phone-layout block above. */
@media (max-width: 560px) {
  .kbd-hint {
    display: none;
  }
}

