/* Faint ridgelines of recent ride elevation profiles, pinned to the bottom
   of the viewport behind the page content. The SVG markup is generated by
   scripts/render-backdrop.mjs. Hovering or focusing a line reveals a chip
   with the ride's stats; clicking opens the ride on Strava. */
.ride-backdrop {
  position: fixed;
  inset: auto 0 0 0;
  height: 38vh;
  /* Not negative: content behind the body's background can never receive
     pointer events, and the lines need to be hover/clickable. */
  z-index: 0;
  pointer-events: none;
  color: var(--primary);
}

/* Keep the page content (and its links) above the backdrop. */
.main-content {
  position: relative;
  z-index: 1;
}

.ride-backdrop svg {
  width: 100%;
  height: 100%;
  display: block;
  /* Soften where the profiles meet empty space. */
  mask-image: linear-gradient(to top, black 70%, transparent);
}

.ride-backdrop a {
  outline: none;
}

.ride-line {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.25;
  stroke-linejoin: round;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  pointer-events: none;
  transition: opacity 200ms;
}

.ride-backdrop a:hover .ride-line,
.ride-backdrop a:focus .ride-line,
.ride-backdrop a.active .ride-line {
  opacity: 0.95;
}

/* Invisible wide stroke along each line; the only place that accepts
   pointer events inside the backdrop. Non-scaling so the target stays
   ~20px however the viewBox is stretched. */
.ride-hit {
  fill: none;
  stroke: transparent;
  stroke-width: 20;
  vector-effect: non-scaling-stroke;
  pointer-events: stroke;
  cursor: pointer;
}

.ride-fill {
  fill: var(--background);
  stroke: none;
}

/* Staggered draw-in on load: each line is revealed left-to-right by an
   animated clip-path (stroke-dash tricks misjudge their length in Chrome
   when the viewBox is stretched with non-scaling strokes, stalling the
   reveal). The 4px outsets keep the clip clear of the stroke and its round
   caps; --i is the ride's index. */
@media (prefers-reduced-motion: no-preference) {
  .ride-line {
    clip-path: inset(-4px 100% -4px -4px);
    animation: ride-draw 1600ms calc(var(--i) * 180ms) ease-out forwards;
  }

  .ride-fill {
    animation: ride-fade 900ms calc(1200ms + var(--i) * 180ms) ease-out backwards;
  }
}

/* Invisible anchor points at each ride's peak, emitted by the render
   script; chips position against them. */
.ride-peak {
  position: absolute;
  width: 0;
  height: 0;
}

/* Stats chips, one per ride, pre-rendered by the render script. Visibility
   is pure CSS (:has rules in the generated <style>); --stack/--sep-display
   flip to a stacked title-over-stats layout below a per-chip breakpoint. */
.ride-chip {
  position: absolute;
  display: flex;
  flex-direction: var(--stack, row);
  align-items: center;
  gap: 0.35em;
  width: max-content;
  max-width: min(420px, 100vw - 24px);
  text-align: center;
  font-size: 0.85em;
  color: var(--primary);
  background-color: var(--background);
  border: 1px solid var(--borders);
  border-radius: 1.2em;
  padding: 0.15em 0.6em 0.05em;
  opacity: 0;
  transition: opacity 200ms;
  pointer-events: none;
}

.ride-chip-sep {
  display: var(--sep-display, inline);
}

.ride-chip-stats {
  white-space: nowrap;
}

/* Chip sits centered above its peak, kept inside the viewport. Anchor
   positioning shifts-to-fit exactly; the fallback clamps using a chip width
   estimated from the label length (--half-w, set inline per chip). */
@supports (anchor-name: --a) {
  .ride-chip {
    bottom: anchor(top);
    margin-bottom: 10px;
    /* Insets set the bounds that shift-to-fit keeps the chip within. */
    inset-inline: 12px;
    justify-self: anchor-center;
  }
}

@supports not (anchor-name: --a) {
  .ride-chip {
    left: clamp(
      calc(12px + var(--half-w)),
      var(--peak-x),
      calc(100% - 12px - var(--half-w))
    );
    top: var(--peak-y);
    transform: translate(-50%, calc(-100% - 10px));
  }
}

@keyframes ride-draw {
  to {
    clip-path: inset(-4px);
  }
}

@keyframes ride-fade {
  from {
    opacity: 0;
  }
}
