/* PULSE · anim.css: the time bar (#timebar) and the animation overlay (js/anim/*).
   Builds on the tokens and components in pulse.css (.timebar, .segmented,
   .icon-button, .badge, .key-*), redefining none of them. */

/* ------------------------------------------------------------------ overlay */

/* Inside the map's canvas container: above the map, below the panel, the
   detail card, the map tools and the time bar. Never takes a click. */
.anim-overlay {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 1;
  pointer-events: none;
}

/* ------------------------------------------------------------------ time bar */

/* One row by default. The narrower layouts further down fold it into a grid
   by setting the --tb-* variables (one bundle per layout), so the rules
   themselves are written once. */
.timebar.anim-timebar {
  display: var(--tb-display, flex);
  grid-template-columns: var(--tb-cols, none);
  grid-template-areas: var(--tb-areas, none);
  align-items: center;
  gap: var(--tb-gap, 10px);
  width: var(--tb-width, max-content);
  min-height: var(--tb-min-h, 48px);
  padding: var(--tb-pad, 6px 10px 6px 6px);
}
.timebar.anim-timebar[data-clock="replay"] { grid-template-areas: var(--tb-areas-replay, var(--tb-areas, none)); }
.anim-timebar .tb-modes { grid-area: modes; }
.anim-timebar .tb-play { grid-area: play; }
.anim-timebar .tb-speed { grid-area: speed; }
.anim-timebar .tb-scrub { grid-area: scrub; }
.anim-timebar .tb-read { grid-area: read; justify-self: var(--tb-read-at, end); }
.anim-timebar .tb-badge { grid-area: badge; justify-self: end; }

.anim-timebar .segmented { box-shadow: none; padding: 2px; border-radius: 9px; }
.anim-timebar .segmented button { min-height: 30px; padding: var(--tb-seg-pad, 4px 10px); font-size: 12.5px; white-space: nowrap; }
.anim-timebar .segmented button:focus-visible { outline-offset: -1px; }

.tb-live { display: inline-flex; align-items: center; gap: 6px; }
.tb-live-dot { position: relative; flex: none; width: 7px; height: 7px; border-radius: 50%; background: var(--pulse); }
.tb-live[aria-pressed="true"] .tb-live-dot { background: #ff6b7d; }
/* A heartbeat while the clock follows real time (off with motion off). */
.anim-timebar[data-clock="live"] .tb-live-dot::after {
  content: "";
  position: absolute;
  inset: -3px;
  border: 1.5px solid currentColor;
  border-radius: 50%;
  opacity: 0;
  animation: tb-beat 1.8s ease-out infinite;
}
.tb-live[aria-pressed="true"] .tb-live-dot::after { color: #ff6b7d; }
.tb-live[aria-pressed="false"] .tb-live-dot::after { color: var(--pulse); }
@keyframes tb-beat { 0% { transform: scale(0.6); opacity: 0.9; } 70%, 100% { transform: scale(1.7); opacity: 0; } }

.tb-play { width: var(--tb-play-size, 34px); height: var(--tb-play-size, 34px); border-radius: 9px; color: var(--navy); }
.tb-play.is-paused { background: var(--wash); }
.tb-play svg { display: block; }

.anim-timebar .tb-speed button { min-height: var(--tb-speed-h, 30px); padding: var(--tb-speed-pad, 4px 8px); font-size: var(--tb-speed-size, 12.5px); font-variant-numeric: tabular-nums; }

/* The scrubber: a day's waveform of scheduled vehicles behind a thin track. */
.tb-scrub {
  position: relative;
  flex: 1 1 260px;
  width: var(--tb-scrub-w, 280px);
  min-width: var(--tb-scrub-min, 150px);
  height: 36px;
  align-self: stretch;
}
.tb-profile {
  position: absolute;
  left: 7px;
  right: 7px;
  top: 3px;
  width: calc(100% - 14px);
  height: 20px;
  pointer-events: none;
}
.tb-profile-area { fill: var(--navy); opacity: 0.13; }
/* 24 px tall: the whole height takes a press, not just the thumb. */
.tb-range {
  position: absolute;
  left: 0;
  right: 0;
  top: 1px;
  width: 100%;
  height: 24px;
  margin: 0;
  background: none;
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
}
.tb-range:focus { outline: none; }
.tb-range:focus-visible { outline: 3px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
.tb-range::-webkit-slider-runnable-track {
  height: 24px;
  border: 0;
  background: linear-gradient(var(--line-strong), var(--line-strong)) 7px 50% / calc(100% - 14px) 2px no-repeat;
}
.tb-range::-moz-range-track {
  height: 2px;
  border: 0;
  background: var(--line-strong);
}
.tb-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 14px;
  height: 14px;
  margin-top: 5px;
  border: 2px solid #fff;
  border-radius: 50%;
  background: var(--navy);
  box-shadow: 0 0 0 1px rgba(12, 12, 72, 0.25), 0 1px 4px rgba(12, 12, 72, 0.3);
}
.tb-range::-moz-range-thumb {
  width: 10px;
  height: 10px;
  border: 2px solid #fff;
  border-radius: 50%;
  background: var(--navy);
  box-shadow: 0 0 0 1px rgba(12, 12, 72, 0.25), 0 1px 4px rgba(12, 12, 72, 0.3);
}
.tb-range:hover::-webkit-slider-thumb { transform: scale(1.12); }
.tb-now {
  position: absolute;
  top: 5px;
  width: 2px;
  height: 16px;
  margin-left: -1px;
  border-radius: 1px;
  background: var(--pulse);
  pointer-events: none;
}
.tb-ticks { position: absolute; left: 7px; right: 7px; bottom: 0; height: 12px; pointer-events: none; }
.tb-tick { position: absolute; top: 0; color: var(--muted); font-size: 10px; font-weight: 500; line-height: 12px; transform: translateX(-50%); white-space: nowrap; }
.tb-tick::before { content: ""; position: absolute; left: 50%; top: -6px; width: 1px; height: 4px; background: var(--line-strong); }

/* The read-out: the clock between the previous- and next-day steps (replay). */
.tb-read { display: inline-flex; flex: none; align-items: center; gap: 0; min-width: 0; }
.tb-clock { min-width: var(--tb-clock-min, 120px); padding: 0 var(--tb-clock-pad, 2px); overflow: hidden; color: var(--navy); font-size: var(--tb-clock-size, 13px); font-weight: 650; text-align: center; white-space: nowrap; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
.tb-day { display: inline-grid; flex: none; place-items: center; width: 24px; height: var(--tb-day-h, 30px); padding: 0 0 2px; border: 0; border-radius: 7px; background: none; color: var(--ink-soft); font-size: 19px; line-height: 1; }
.tb-day:hover:not(:disabled) { background: var(--wash); color: var(--navy); }
.tb-day:disabled { color: var(--line-strong); cursor: default; }
.tb-badge { display: inline-flex; }
.tb-badge .badge { cursor: help; }
.anim-timebar[data-clock="replay"] .tb-badge { display: var(--tb-replay-badge, inline-flex); }

/* Dark maps: the bar stays a white card (house style); nothing to change. */

/* ------------------------------------------------------------------ map key */

.anim-key-note { margin: 0 0 2px; color: var(--muted); font-size: 11px; line-height: 1.35; }
.anim-key-modes { flex-wrap: wrap; gap: 3px 10px; }
.anim-key-mode { display: inline-flex; align-items: center; gap: 4px; }
/* Each swatch sits on a small tile: plain on light maps, the basemap's own
   colour on Dark and Aerial (anim/overlay.js keyTile), as the route key does. */
.anim-key-tile { display: inline-flex; flex: none; align-items: center; justify-content: center; width: 22px; height: 16px; border-radius: 3px; }
.anim-key-tile.is-dark { background: var(--tile); }
.anim-key-tile > .key-swatch { margin: 0; }
.anim-key-tile.is-dark > .key-swatch { box-shadow: 0 0 0 1px rgba(8, 10, 28, 0.9); }
.anim-key-tile.is-dark > .key-swatch.anim-key-particle,
.anim-key-tile.is-dark > .key-swatch.anim-key-ring { box-shadow: none; }
.key-swatch.anim-key-bus { width: 7px; height: 7px; border-radius: 50%; background: var(--swatch); box-shadow: 0 0 0 1px #fff, 0 0 0 1.6px rgba(12, 12, 72, 0.14); }
.key-swatch.anim-key-train { width: 14px; height: 6px; border-radius: 3px; background: var(--swatch); box-shadow: 0 0 0 1px #fff, 0 0 0 1.6px rgba(12, 12, 72, 0.14); }
.key-swatch.anim-key-ferry { width: 7px; height: 7px; background: var(--swatch); transform: rotate(45deg); box-shadow: 0 0 0 1px #fff; }
/* An expanding ring, as drawn round each counter and sensor. */
.key-swatch.anim-key-ring {
  width: 11px;
  height: 11px;
  border: 1.6px solid var(--swatch);
  border-radius: 50%;
  box-shadow: 0 0 0 2.5px color-mix(in srgb, var(--swatch) 20%, transparent);
}
.key-swatch.anim-key-particle {
  position: relative;
  width: 18px;
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--swatch));
  opacity: 0.85;
}
.key-swatch.anim-key-particle::after {
  content: "";
  position: absolute;
  right: -1px;
  top: 50%;
  width: 5px;
  height: 5px;
  margin-top: -2.5px;
  border-radius: 50%;
  background: var(--swatch);
}
.map-key .anim-key-method { margin: 2px 0 4px; padding-top: 4px; }
.map-key .anim-key-method > summary { min-height: 24px; font-size: 11px; gap: 6px; }
.map-key .anim-key-method .method-mark { width: 13px; height: 13px; font-size: 9px; }
.map-key .anim-key-method .method-text { margin-top: 6px; font-size: 11px; line-height: 1.45; }

/* ------------------------------------------------------------------ narrower maps

   Where one row does not fit, the bar folds into two or three rows. How much
   room it has depends on the window, the panel (open: 364 px) and a detail
   card (320–340 px): the card puts the bar between the panel and the card,
   so it never runs under either. Where a card leaves no room at all beside
   an open panel (a window under 1041 px), the bar steps aside until the card
   closes, as on a phone; the panel still says which day and time the map
   shows.

                   no card        card, panel open   card, panel folded
     one row       1181 px +      —                  —
     two rows      1010–1180      1301 +             917 +
     three rows    761–1009       1041–1300          761–916
     hidden        —              761–1040           —

     two rows:    [Live | Replay] [❚❚] [60× 300× 900×]          ‹ Fri 2 Oct · 08:15 ›
                  [──────────────────── 24 h scrubber ─────────────────] [Scheduled]

     three rows:  [Live | Replay] [❚❚]                          [Scheduled]
                                    ‹ Fri 2 Oct · 08:15 ›
                  [60× 300× 900×] [────────── 24 h scrubber ─────────────]   */

@media (min-width: 761px) {
  body[data-place="open"] .timebar.anim-timebar {
    left: calc((var(--panel-w) + 100% - var(--card-w)) / 2);
    max-width: calc(100% - var(--panel-w) - var(--card-w) - 72px);
    --tb-width: min(560px, calc(100vw - var(--panel-w) - var(--card-w) - 72px));
  }
  body[data-place="open"][data-panel="collapsed"] .timebar.anim-timebar {
    left: calc((100% - var(--card-w) - 24px) / 2);
    max-width: calc(100% - var(--card-w) - 72px);
    --tb-width: min(560px, calc(100vw - var(--card-w) - 72px));
  }
}

/* One row, but in a replay only just: the badge (always Scheduled in a
   replay, as the key says) gives way to the speeds and day steps. */
@media (min-width: 1181px) and (max-width: 1263px) {
  body:not([data-place="open"]) .timebar.anim-timebar { --tb-replay-badge: none; }
}

/* Two rows. */
@media (min-width: 1010px) and (max-width: 1180px) {
  body:not([data-place="open"]) .timebar.anim-timebar {
    --tb-width: min(560px, calc(100vw - var(--panel-w) - 96px));
    --tb-display: grid;
    --tb-cols: auto auto auto minmax(0, 1fr) auto;
    --tb-areas: "modes play speed . read" "scrub scrub scrub scrub badge";
    --tb-gap: 2px 8px;
    --tb-pad: 6px 10px 4px 6px;
    --tb-scrub-w: auto;
    --tb-scrub-min: 0;
    --tb-clock-min: 0;
  }
}
@media (min-width: 917px) {
  body[data-place="open"][data-panel="collapsed"] .timebar.anim-timebar {
    --tb-display: grid;
    --tb-cols: auto auto auto minmax(0, 1fr) auto;
    --tb-areas: "modes play speed . read" "scrub scrub scrub scrub badge";
    --tb-gap: 2px 8px;
    --tb-pad: 6px 10px 4px 6px;
    --tb-scrub-w: auto;
    --tb-scrub-min: 0;
    --tb-clock-min: 0;
  }
}
@media (min-width: 1301px) {
  body[data-place="open"]:not([data-panel="collapsed"]) .timebar.anim-timebar {
    --tb-display: grid;
    --tb-cols: auto auto auto minmax(0, 1fr) auto;
    --tb-areas: "modes play speed . read" "scrub scrub scrub scrub badge";
    --tb-gap: 2px 8px;
    --tb-pad: 6px 10px 4px 6px;
    --tb-scrub-w: auto;
    --tb-scrub-min: 0;
    --tb-clock-min: 0;
  }
}

/* Three rows: the clock gets a row of its own, and in a replay the speeds
   share the scrubber's. */
@media (min-width: 761px) and (max-width: 1009px) {
  body:not([data-place="open"]) .timebar.anim-timebar {
    --tb-width: min(560px, calc(100vw - var(--panel-w) - 96px));
    --tb-display: grid;
    --tb-cols: auto auto minmax(0, 1fr);
    --tb-areas: "modes play badge" "read read read" "scrub scrub scrub";
    --tb-areas-replay: "modes play badge" "read read read" "speed scrub scrub";
    --tb-gap: 1px 8px;
    --tb-pad: 5px 8px 4px 6px;
    --tb-read-at: center;
    --tb-day-h: 24px;
    --tb-seg-pad: 4px 8px;
    --tb-speed-h: 28px;
    --tb-speed-pad: 3px 6px;
    --tb-speed-size: 12px;
    --tb-scrub-w: auto;
    --tb-scrub-min: 0;
    --tb-clock-min: 0;
  }
}
@media (min-width: 761px) and (max-width: 916px) {
  body[data-place="open"][data-panel="collapsed"] .timebar.anim-timebar {
    --tb-display: grid;
    --tb-cols: auto auto minmax(0, 1fr);
    --tb-areas: "modes play badge" "read read read" "scrub scrub scrub";
    --tb-areas-replay: "modes play badge" "read read read" "speed scrub scrub";
    --tb-gap: 1px 8px;
    --tb-pad: 5px 8px 4px 6px;
    --tb-read-at: center;
    --tb-day-h: 24px;
    --tb-seg-pad: 4px 8px;
    --tb-speed-h: 28px;
    --tb-speed-pad: 3px 6px;
    --tb-speed-size: 12px;
    --tb-scrub-w: auto;
    --tb-scrub-min: 0;
    --tb-clock-min: 0;
  }
}
@media (min-width: 1041px) and (max-width: 1300px) {
  body[data-place="open"]:not([data-panel="collapsed"]) .timebar.anim-timebar {
    --tb-display: grid;
    --tb-cols: auto auto minmax(0, 1fr);
    --tb-areas: "modes play badge" "read read read" "scrub scrub scrub";
    --tb-areas-replay: "modes play badge" "read read read" "speed scrub scrub";
    --tb-gap: 1px 8px;
    --tb-pad: 5px 8px 4px 6px;
    --tb-read-at: center;
    --tb-day-h: 24px;
    --tb-seg-pad: 4px 8px;
    --tb-speed-h: 28px;
    --tb-speed-pad: 3px 6px;
    --tb-speed-size: 12px;
    --tb-scrub-w: auto;
    --tb-scrub-min: 0;
    --tb-clock-min: 0;
  }
}

/* No room beside an open panel and card. */
@media (min-width: 761px) and (max-width: 1040px) {
  body[data-place="open"]:not([data-panel="collapsed"]) .timebar.anim-timebar { display: none; }
}
/* ------------------------------------------------------------------ phones

     [Live | Replay] [❚❚]    ‹ Fri 2 Oct · 08:15 ›     (the day steps in a replay)
     [──────── 24 h scrubber ────────] [Scheduled]      (live)
     [60× 300× 900×] [──── scrubber ────]               (replay: no badge, as the key says)

   Under 360 px a replay gives the read-out a row of its own. */

@media (max-width: 760px) {
  .timebar.anim-timebar {
    --tb-display: grid;
    --tb-cols: auto auto minmax(0, 1fr) auto;
    --tb-areas: "modes play read read" "scrub scrub scrub badge";
    --tb-areas-replay: "modes play read read" "speed scrub scrub scrub";
    --tb-gap: 2px 8px;
    --tb-width: auto;
    --tb-min-h: 0;
    --tb-pad: 6px 8px 4px;
    --tb-seg-pad: 4px 8px;
    --tb-play-size: 32px;
    --tb-speed-h: 28px;
    --tb-speed-pad: 3px 6px;
    --tb-speed-size: 12px;
    --tb-scrub-w: auto;
    --tb-scrub-min: 0;
    --tb-clock-min: 0;
    --tb-clock-size: 12.5px;
    --tb-clock-pad: 0;
    --tb-replay-badge: none;
  }
}
@media (max-width: 359px) {
  .timebar.anim-timebar[data-clock="replay"] {
    --tb-areas-replay: "modes play play play" "read read read read" "speed scrub scrub scrub";
    --tb-read-at: center;
  }
}

/* ------------------------------------------------------------------ forced colours

   Windows High Contrast and the like replace backgrounds with the system
   colours, which would hide which time-bar button is pressed and the
   scrubber's track (a background gradient). Pressed buttons take the
   highlight; the track, thumb and waveform are drawn in system colours; the
   key's swatches keep their data colours. */

@media (forced-colors: active) {
  .anim-timebar .segmented button[aria-pressed="true"] {
    forced-color-adjust: none;
    background: Highlight;
    color: HighlightText;
    outline: 2px solid Highlight;
    outline-offset: -2px;
  }
  .tb-live-dot, .tb-live[aria-pressed] .tb-live-dot { forced-color-adjust: none; background: CanvasText; }
  .tb-live[aria-pressed="true"] .tb-live-dot { background: HighlightText; }
  .tb-play.is-paused { outline: 2px solid CanvasText; outline-offset: -2px; }
  .tb-range { forced-color-adjust: none; }
  .tb-range::-webkit-slider-runnable-track { background: linear-gradient(CanvasText, CanvasText) 7px 50% / calc(100% - 14px) 2px no-repeat; }
  .tb-range::-moz-range-track { background: CanvasText; }
  .tb-range::-webkit-slider-thumb { border-color: Canvas; background: Highlight; box-shadow: 0 0 0 1px CanvasText; }
  .tb-range::-moz-range-thumb { border-color: Canvas; background: Highlight; box-shadow: 0 0 0 1px CanvasText; }
  .tb-range:focus-visible { outline-color: Highlight; }
  .tb-profile { forced-color-adjust: none; }
  .tb-profile-area { fill: GrayText; opacity: 0.3; }
  .tb-now { forced-color-adjust: none; background: Highlight; }
  .tb-tick::before { forced-color-adjust: none; background: CanvasText; }
  .anim-key-tile, .anim-key-tile > .key-swatch { forced-color-adjust: none; }
}

@media (prefers-reduced-motion: reduce) {
  .anim-timebar .tb-live-dot::after { animation: none; }
}
