/* ===================== Design tokens ===================== */
:root {
  /* Fresh & Minimal skin (design direction #6). Token NAMES are kept so the
     existing rules keep resolving; VALUES retuned to a clean, modern
     violet-on-white theme. --serif now holds a sans (Plus Jakarta Sans) — the
     name is a historical hook, not a promise of a serif face. */
  --red: #6c4cf1;        /* primary action — violet */
  --red-dark: #5a3fd6;
  --gold: #6c4cf1;       /* selection / focus accent — violet */
  --blue: #6c4cf1;       /* destination / user pin — violet */
  --blue-dark: #5a3fd6;
  --pink: #ef4444;       /* warning red — debug chip only */
  --ink: #101828;        /* near-black */
  --paper: #ffffff;      /* card surface */
  --bg: #f7f8fa;         /* page background */
  --muted: #667085;
  --line: #eaecf0;
  --yellow-badge: #f4d645;
  --ok: #059669;         /* success / "fits" — Fresh green (accents, borders) */
  --ok-text: #047857;    /* success text — darker for AA contrast on white/tint */
  --ok-bg: #e6f7ee;      /* success badge background */
  --shadow-soft: 0 12px 30px rgba(16, 24, 40, 0.08);
  --shadow-offset: 6px;
  --maxw: 1120px;
  --radius: 16px;
  --serif: "Plus Jakarta Sans", "Plus Jakarta Sans Fallback", system-ui, sans-serif;
  font-synthesis: none;
}

/* Metric-matched fallback faces. Until the web fonts arrive, text is laid out
   in local Arial *stretched to the web font's own metrics* (size-adjust +
   ascent/descent overrides), so the swap to Inter / Plus Jakarta Sans reflows
   almost nothing — this is what keeps CLS out of the "needs improvement" band.
   Override values are the standard next/font-generated set for each family. */
@font-face {
  font-family: "Inter Fallback";
  src: local("Arial");
  size-adjust: 107.40%;
  ascent-override: 90.20%;
  descent-override: 22.48%;
  line-gap-override: 0%;
}
@font-face {
  font-family: "Plus Jakarta Sans Fallback";
  src: local("Arial");
  size-adjust: 103.36%;
  ascent-override: 103.8%;
  descent-override: 22.2%;
  line-gap-override: 0%;
}

/* Which web-font WEIGHTS load is set by the Google Fonts <link> in each page's
   <head> — and that link is duplicated across all four HTML pages (index,
   privacy, terms, accessibility). Keep the loaded weights in sync with the
   weights this stylesheet actually uses: add a weight here, add it to all four
   links; stop using one, trim it from all four (an unused weight is font files
   downloaded for nothing — that's why 500 was dropped). Currently 400/600/700/800. */

* { box-sizing: border-box; }

/* ===================== Debug controls (in the header pill dropdown) ========= */
.debug-control {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 0.74rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.debug-control input[type="datetime-local"] {
  font: inherit;
  font-size: 0.85rem;
  text-transform: none;
  letter-spacing: normal;
  padding: 5px 8px;
  border: 1px solid #fff;
  border-radius: 5px;
}
.debug-move-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.debug-btn {
  font: inherit;
  font-size: 0.8rem;
  font-weight: 700;
  cursor: pointer;
  padding: 5px 10px;
  color: var(--red);
  background: #fff;
  border: 1px solid #fff;
  border-radius: 5px;
}
.debug-btn:hover { background: #ffe5e5; }
.debug-btn:active { transform: translateY(1px); }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: "Inter", "Inter Fallback", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  color: var(--ink);
  background: var(--bg);
  line-height: 1.5;
}

.container {
  width: 100%;
  max-width: var(--maxw);
  margin: 0 auto;
  padding-inline: 24px;
}

a { color: inherit; }

/* ===================== Header ===================== */
.site-header {
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  background: rgba(255, 255, 255, 0.95);
  backdrop-filter: blur(6px);
  z-index: 1000;
}
.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 64px;
  gap: 24px;
}
/* Headers that carry the primary nav become three columns instead, so "Now /
   Plan" sits on the page's centre line rather than in the middle of whatever
   space the logo and the header icons happen to leave over (those two are
   never the same width, so auto margins put the nav off-centre). Legal pages
   have no nav and keep the plain two-item flex row above. */
.header-inner:has(.main-nav) {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
}
.header-inner:has(.main-nav) > :first-child { justify-self: start; }
.header-inner:has(.main-nav) > :last-child { justify-self: end; }
.logo {
  font-family: var(--serif);
  font-weight: 700;
  font-size: 1.4rem;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}
.logo-now { color: var(--red); }
.main-nav {
  display: flex;
  gap: 28px;
}
.nav-link {
  text-decoration: none;
  color: var(--ink);
  font-size: 0.98rem;
  padding-bottom: 2px;
  border-bottom: 2px solid transparent;
}
.nav-link:hover { color: var(--red); }
.nav-link.is-active {
  color: var(--red);
  border-bottom-color: var(--red);
}
.header-icons { display: flex; gap: 8px; }

.icon-btn {
  display: inline-grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border: none;
  border-radius: var(--radius);
  background: transparent;
  color: var(--red);
  cursor: pointer;
}
.icon-btn:hover { background: rgba(108, 76, 241, 0.08); }

/* ===================== Hero ===================== */
.hero { text-align: center; padding: 56px 24px 40px; }
.hero-title {
  font-family: var(--serif);
  font-weight: 600;
  font-size: clamp(2.4rem, 6vw, 3.6rem);
  letter-spacing: -0.01em;
  margin: 0 0 12px;
}
.hero-subtitle {
  color: var(--muted);
  max-width: 560px;
  margin: 0 auto 36px;
  font-size: 1.05rem;
}

/* Constraint cards */
.constraint-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 28px;
  text-align: left;
}
.card {
  position: relative;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  transition: transform 0.14s ease, box-shadow 0.14s ease;
}
.card.is-open,
.card:hover {
  transform: translateY(-3px);
  box-shadow: 0 16px 34px rgba(34, 42, 36, 0.14);
}
/* Lift the open card (and its pop-over) above the map's stacking context. */
.card.is-open { z-index: 1500; }
.card-trigger {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
  padding: 28px 22px 22px;
}

/* Pop-over panels */
.card-panel {
  position: absolute;
  z-index: 1200;
  top: calc(100% + 8px);
  left: 0;
  right: 0;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 18px 40px rgba(34, 42, 36, 0.18);
  padding: 18px 18px 20px;
}
.card-panel[hidden] { display: none; }
.panel-title {
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-size: 0.78rem;
  font-weight: 700;
  margin: 0 0 14px;
  color: var(--muted);
}
.panel-options { display: grid; gap: 4px; }
/* Long option lists (genres, and especially the ~100 subgenres) scroll inside
   the panel rather than growing it past the viewport. */
.panel-options--scroll { max-height: 46vh; overflow-y: auto; overscroll-behavior: contain; }
.panel-option {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 8px;
  border-radius: var(--radius);
  cursor: pointer;
  font-size: 0.95rem;
  font-weight: 600;
}
.panel-option:hover { background: var(--line); }
.panel-option input { flex: 0 0 auto; accent-color: var(--red); width: 16px; height: 16px; }
.panel-field-label {
  display: block;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--muted);
  margin: 12px 0 6px;
}
.panel-select {
  width: 100%;
  font: inherit;
  font-weight: 600;
  padding: 10px 12px;
  border: 2px solid var(--ink);
  border-radius: var(--radius);
  background: var(--paper);
  cursor: pointer;
}
.panel-select:disabled { opacity: 0.5; cursor: not-allowed; }
.panel-input {
  width: 100%;
  font: inherit;
  font-weight: 600;
  padding: 10px 12px;
  border: 2px solid var(--ink);
  border-radius: var(--radius);
  background: var(--paper);
}
.panel-input:disabled { opacity: 0.5; }
/* "Find on map" — resolves a typed place and closes the picker. */
.panel-find { display: inline-flex; align-items: center; gap: 6px; margin: 10px 0 2px; font: inherit; font-weight: 700; font-size: 0.85rem; color: #fff; background: var(--blue); border: none; border-radius: 12px; padding: 10px 16px; cursor: pointer; }
.panel-find:hover { filter: brightness(0.96); }
.panel-find:disabled { opacity: 0.45; cursor: not-allowed; }
.panel-find:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }

/* Candidate places under "Find on map" — rows in the show-pick mould, plus a
   dashed "keep as a note" fallback row (no coordinates, label only). */
.place-results { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.place-msg { font-size: 0.85rem; color: var(--muted); margin: 0; }
.place-hit { display: flex; align-items: center; gap: 11px; text-align: left; width: 100%; padding: 10px 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--paper); cursor: pointer; font: inherit; color: inherit; }
.place-hit:hover { border-color: #cfc9bb; }
.place-hit:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.place-hit--note { border-style: dashed; color: var(--muted); }
.ph-ico { font-size: 1.05rem; }
.ph-body { display: flex; flex-direction: column; gap: 2px; }
.ph-title { font-weight: 700; font-size: 0.9rem; }
.ph-area { font-size: 0.78rem; color: var(--muted); }
.panel-range {
  width: 100%;
  accent-color: var(--red);
  cursor: pointer;
  margin: 2px 0 0;
}
.range-value {
  margin: 6px 0 2px;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--ink);
}
/* Speed hint beside a travel mode, e.g. "Walking (3.3 km/h)". */
.opt-speed { font-weight: 600; font-size: 0.8rem; color: var(--muted); }
/* Genre emoji prefix in the genre dropdown options. */
.opt-ico { display: inline-block; width: 1.4em; text-align: center; }
/* Segmented control — used for the ticket-price choice. */
.segmented { display: flex; gap: 4px; margin: 4px 0 4px; padding: 3px; background: var(--bg); border: 1px solid var(--line); border-radius: 999px; }
/* Six price options don't fit one pill row, so this variant wraps them onto two
   rows of three. The pill's fully-round ends stop making sense once it is two
   rows tall, hence the softer radius. */
.segmented--wrap { flex-wrap: wrap; border-radius: 16px; }
.segmented--wrap .seg-btn { flex: 1 1 28%; padding: 7px 4px; }
.seg-btn { flex: 1 1 0; font: inherit; font-weight: 700; font-size: 0.85rem; color: var(--muted); background: transparent; border: none; padding: 8px 6px; border-radius: 999px; cursor: pointer; transition: color 0.12s ease, background 0.12s ease; }
.seg-btn.is-active { color: #fff; background: var(--red); }
/* The grey per-choice show count, matching the .opt-count on the option lists. */
.seg-count { display: block; font-size: 0.72rem; font-weight: 700; color: #9aa39a; font-variant-numeric: tabular-nums; line-height: 1.2; }
.seg-btn.is-active .seg-count { color: rgba(255, 255, 255, 0.82); }
.seg-count:empty { display: none; }
.seg-btn:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
/* A panel heading that carries its "everything!" clear link on the right. */
.panel-title--row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
/* "everything!" — a link, not a button: clearing the filter is a quiet escape
   hatch beside the question, not a call to action competing with the options. */
.panel-everything {
  font: inherit; font-weight: 700; font-size: 0.78rem; text-transform: none; letter-spacing: 0;
  color: var(--blue); background: transparent; border: none; padding: 0; cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1.5px;
}
.panel-everything:hover { color: var(--red); }
.panel-everything:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; border-radius: 4px; }
/* The grey per-option show count, pushed to the right of the option row. */
.opt-count { margin-left: auto; flex: 0 0 auto; font-size: 0.78rem; font-weight: 700; color: #9aa39a; font-variant-numeric: tabular-nums; }
.panel-option.is-empty { opacity: 0.55; }
.panel-note {
  margin: 14px 0 0;
  font-size: 0.82rem;
  color: var(--muted);
  font-style: italic;
}
.card-caret {
  display: inline-block;
  margin-top: 12px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--muted);
}

.card-label {
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-size: 0.78rem;
  font-weight: 600;
  margin: 0 0 6px;
}
.card--genre .card-label { color: var(--red); }
.card--travel .card-label { color: var(--gold); }
.card--constraint .card-label { color: var(--blue); }

.card-value {
  font-weight: 800;
  text-transform: uppercase;
  font-size: 1.15rem;
  margin: 0;
  line-height: 1.2;
}

/* ===================== Map ===================== */
.map-section { margin: 16px auto 0; }
#leaflet-map {
  width: 100%;
  height: 460px;
  background: #e9eee7;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  overflow: hidden;
  /* Positioned + z-index:0 caps Leaflet's internal panes/controls (which go up
     to z-index 1000) inside this stacking context, so the card pop-overs above
     the map can render over it. */
  position: relative;
  z-index: 0;
}

/* Journey arrows drawn on the map */
.leaflet-overlay-pane path.route-line {
  stroke-dasharray: 10 8;
  animation: routeDash 0.8s linear infinite;
}
@keyframes routeDash { to { stroke-dashoffset: -18; } }
.route-arrow-wrap { background: transparent; border: none; }
.route-arrow {
  display: block;
  width: 0;
  height: 0;
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-bottom: 12px solid var(--ink);
}
/* The check marker's root is a zero-size anchor sitting exactly on the pin; the
   visible pill is an inner span so its centring transform survives Leaflet's own
   inline position transform on the root. */
.route-check-marker { background: transparent; border: none; }
.route-label {
  position: absolute; left: 0; top: 0;
  background: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 999px;
  padding: 2px 9px;
  font-size: 0.72rem;
  font-weight: 700;
  white-space: nowrap;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
  /* Centre horizontally on the pin and sit directly above it. */
  transform: translate(-50%, -175%);
}
.route-check {
  border-color: var(--ok);
  color: var(--ok-text);
}
.route-check .tick { color: var(--ok-text); font-weight: 800; }

/* ===================== Journey strip ===================== */
/* Narrow timeline that mirrors the plan: origin → optional stop → commitment. */
/* Row holding the journey summary box and, on its right, the Google Maps
   route link. The strip flexes to fill; the link keeps its content width. */
.journey-row {
  display: flex;
  align-items: stretch;
  gap: 14px;
  margin-top: 18px;
}
.journey-row > .journey { flex: 1 1 auto; min-width: 0; margin: 0; }
.journey-row:has(> #journeyStrip:empty) { display: none; }

.journey {
  display: flex;
  align-items: stretch;
  margin: 18px auto 0;
  padding: 10px 16px;
  min-height: 66px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--paper);
  box-shadow: var(--shadow-soft);
  overflow-x: auto;
}
.journey:empty { display: none; }

/* Nodes (origin / stop / destination) */
.journey-node {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  flex: 0 0 auto;
  max-width: 220px;
  padding: 2px 6px;
}
.journey-flag {
  font-weight: 800;
  font-size: 0.9rem;
  line-height: 1.2;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 210px;
}
.journey-flag--dull { color: var(--muted); }
.journey-flag--stop { color: var(--ok-text); }
.journey-flag--dest { color: var(--blue); }
.journey-sub {
  font-size: 0.74rem;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 210px;
}
.journey-slack {
  margin-top: 3px;
  align-self: flex-start;
  font-size: 0.66rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  padding: 1px 7px;
  border-radius: 999px;
  white-space: nowrap;
}
.journey-slack--ok { background: var(--ok-bg); color: var(--ok-text); border: 1px solid var(--ok); }
.journey-slack--late { background: #fde7e7; color: var(--red-dark); border: 1px solid var(--red); }

/* Connector segments */
.journey-seg {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  flex: 1 1 80px;
  min-width: 78px;
  padding: 0 8px;
}
.journey-seg--long { flex-grow: 3; }
.journey-line-label {
  font-size: 0.72rem;
  font-weight: 700;
  white-space: nowrap;
}
.journey-line {
  position: relative;
  width: 100%;
  height: 0;
  border-top: 2px dashed var(--ink);
}
.journey-line-walker {
  position: absolute;
  top: -11px;
  left: 0;
  font-size: 0.9rem;
  animation: journeyWalk 2.6s linear infinite;
}
@keyframes journeyWalk {
  from { left: 0; }
  to { left: calc(100% - 14px); }
}

/* Idle middle: walker bobbing in place + trailing dots */
.journey-seg--idle { flex: 1 1 auto; gap: 6px; }
.journey-idle-row { display: inline-flex; align-items: center; gap: 8px; }
.journey-walker {
  font-size: 1.35rem;
  animation: journeyBob 0.55s ease-in-out infinite alternate;
}
@keyframes journeyBob {
  from { transform: translateY(0); }
  to { transform: translateY(-3px); }
}
.journey-dots { display: inline-flex; gap: 4px; }
.journey-dots i {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--muted);
  animation: journeyDot 1.2s ease-in-out infinite;
}
.journey-dots i:nth-child(2) { animation-delay: 0.2s; }
.journey-dots i:nth-child(3) { animation-delay: 0.4s; }
@keyframes journeyDot {
  0%, 100% { opacity: 0.2; }
  50% { opacity: 1; }
}
.journey-hint { font-size: 0.72rem; color: var(--muted); }

/* "Buy now" CTA on a selected paid show */
.journey-buy {
  margin-top: 4px;
  align-self: flex-start;
  display: inline-block;
  max-width: 210px;
  font-size: 0.68rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 3px 10px;
  border-radius: 999px;
  color: #fff;
  background: var(--red);
  border: 1px solid var(--red-dark);
  box-shadow: 0 1px 0 var(--red-dark);
  animation: journeyPulse 1.6s ease-in-out infinite;
}
.journey-buy:hover { background: var(--red-dark); }
@keyframes journeyPulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.04); }
}
.journey-soldout {
  margin-top: 4px;
  align-self: flex-start;
  font-size: 0.66rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--muted);
}

/* Google Maps route link — its own bordered box matching the journey strip's
   neobrutalist style, sitting to the strip's right. */
.maps-route-btn {
  flex: 0 0 auto;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  max-width: 150px;
  padding: 10px 16px;
  text-align: center;
  text-decoration: none;
  font-weight: 800;
  font-size: 0.8rem;
  line-height: 1.2;
  color: #fff;
  background: var(--blue);
  border: none;
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  transition: transform 0.06s ease, box-shadow 0.06s ease;
}
.maps-route-btn:hover { background: var(--blue-dark, #1746a2); }
.maps-route-btn:active {
  transform: translate(3px, 3px);
  box-shadow: 0 0 0 var(--ink);
}
.maps-route-btn[hidden] { display: none; }
.maps-route-btn__pin { font-size: 1.2rem; }

@media (max-width: 560px) {
  .journey-row { flex-direction: column; }
  .maps-route-btn {
    max-width: none;
    flex-direction: row;
    align-self: stretch;
  }
}

@media (prefers-reduced-motion: reduce) {
  .journey-walker,
  .journey-line-walker,
  .journey-dots i,
  .journey-buy { animation: none; }
}

/* ===================== Shows list ===================== */
.shows-section { padding: 36px 24px 56px; }
.shows-title {
  font-family: var(--serif);
  font-weight: 600;
  font-size: 1.5rem;
  margin: 0 0 22px;
}
.shows-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 20px;
}
.show-item {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px;
  cursor: pointer;
  transition: border-color 0.12s ease, transform 0.12s ease;
  background: var(--paper);
}
.show-item:hover { border-color: var(--ink); transform: translateY(-2px); }
.show-item--tight { opacity: 0.55; }
.show-item--leg { border-color: var(--ok); border-width: 2px; }
.show-item--selected { border-color: var(--red); border-width: 2px; box-shadow: var(--shadow-soft); }
.show-genre {
  display: inline-block;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-weight: 700;
  color: var(--red);
  margin-bottom: 8px;
}
.show-name { font-weight: 800; font-size: 1.05rem; margin: 0 0 6px; }
.show-meta { color: var(--muted); font-size: 0.9rem; margin: 0 0 2px; }
.show-time { font-weight: 700; }

/* Map popup */
.popup-genre {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-weight: 700;
  color: var(--red);
}
.popup-title { font-weight: 800; font-size: 1rem; margin: 2px 0 4px; }
.popup-meta { color: var(--muted); font-size: 0.85rem; margin: 0; }

/* "You are here" marker */
.user-marker { background: transparent; border: none; }
.user-dot {
  display: block;
  width: 16px;
  height: 16px;
  margin: 2px;
  border-radius: 50%;
  background: var(--blue);
  border: 3px solid #fff;
  box-shadow: 0 0 0 0 rgba(108, 76, 241, 0.5);
  animation: userPulse 2s infinite;
}
@keyframes userPulse {
  0%   { box-shadow: 0 0 0 0 rgba(108, 76, 241, 0.45); }
  70%  { box-shadow: 0 0 0 14px rgba(108, 76, 241, 0); }
  100% { box-shadow: 0 0 0 0 rgba(108, 76, 241, 0); }
}

/* ===================== Footer ===================== */
.site-footer {
  background: var(--ink);
  color: #d9d9d9;
  padding: 48px 0;
  margin-top: 24px;
}
.footer-inner {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr auto;
  gap: 32px;
  align-items: start;
}
.logo--footer { color: var(--red); margin: 0 0 12px; }
.footer-tagline { max-width: 320px; margin: 0 0 16px; color: #cfcfcf; }
.footer-copy { color: #8c8c8c; font-size: 0.85rem; margin: 0; }
/* The site-version popup on the copyright line (shared/version-popup.js builds
   the element; this is its skin). No help cursor — the popup is the
   affordance, and it must be reachable by keyboard and touch, not by hover
   alone. */
.footer-version { position: relative; }
.footer-version:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
.version-pop {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 0;
  z-index: 20;
  white-space: nowrap;
  padding: 6px 10px;
  border-radius: 8px;
  background: #1f2430;
  color: #fff;
  font-size: 0.78rem;
  font-weight: 600;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.28);
}
.version-pop::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 14px;
  border: 6px solid transparent;
  border-top-color: #1f2430;
}
.footer-heading {
  color: var(--red);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-size: 0.82rem;
  margin: 4px 0 14px;
}
.footer-link {
  display: block;
  text-decoration: underline;
  color: #d9d9d9;
  margin-bottom: 10px;
  font-size: 0.92rem;
}
.footer-link:hover { color: #fff; }
.footer-actions { display: flex; gap: 10px; }
.icon-btn--dark {
  background: #2b2b2b;
  color: #d9d9d9;
}
.icon-btn--dark:hover { background: #3a3a3a; color: #fff; }

/* ===================== Responsive ===================== */
@media (max-width: 860px) {
  .main-nav { display: none; }
  /* No nav to centre once it's hidden — back to logo-left, icons-right. */
  .header-inner:has(.main-nav) { display: flex; }
  .constraint-cards { grid-template-columns: 1fr; gap: 24px; }
  .footer-inner { grid-template-columns: 1fr 1fr; }
  .footer-brand { grid-column: 1 / -1; }
  .footer-actions { grid-column: 1 / -1; }
}

/* ============================================================
   Redesign v2 — one calm centered column on every screen size.
   Standard time picker, vertical plan, compact capped list,
   collapsible debug, and a supporting (not dominant) map.
   ============================================================ */
.app { width: 100%; }
.wrap { width: 100%; max-width: 680px; margin: 0 auto; padding: 16px 20px 60px; }

/* --- One neutral debug pill (version) that opens a dropdown of testing tools --- */
.debug-menu { position: relative; display: inline-flex; }
.debug-menu[hidden] { display: none; }
.debug-pill {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer; white-space: nowrap;
  font: inherit; font-size: 0.66rem; font-weight: 700; letter-spacing: 0.02em;
  color: var(--muted); background: var(--bg); border: 1px solid var(--line);
  border-radius: 999px; padding: 5px 12px; font-variant-numeric: tabular-nums;
}
.debug-pill::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: #c2c8d2; }
.debug-pill:hover { border-color: #d4d8e0; color: var(--ink); }
.debug-pill[aria-expanded="true"] { border-color: var(--red); color: var(--ink); }
.debug-pill:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
/* The dropdown card, anchored under the pill. */
.debug-pop {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 1600;
  display: flex; flex-direction: column; gap: 14px; min-width: 232px;
  background: var(--paper); color: var(--ink); border: 1px solid var(--line);
  border-radius: 12px; padding: 14px 16px; box-shadow: var(--shadow-soft);
  font-size: 0.82rem;
}
.debug-pop[hidden] { display: none; }
.debug-pop__now { margin: 0; font-weight: 700; font-size: 0.78rem; }
.debug-control { display: flex; flex-direction: column; gap: 6px; font-size: 0.64rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; color: var(--muted); }
.debug-control input[type="datetime-local"] { font: inherit; font-size: 0.82rem; text-transform: none; letter-spacing: normal; padding: 6px 8px; border: 1px solid var(--line); border-radius: 7px; color: var(--ink); background: #fff; }
.debug-move-grid { display: flex; flex-wrap: wrap; gap: 6px; }
.debug-btn { font: inherit; font-size: 0.78rem; font-weight: 700; cursor: pointer; padding: 5px 9px; color: var(--pink); background: #fff; border: 1px solid var(--line); border-radius: 7px; }
.debug-btn:hover { background: #ffecea; }

/* ===================== First-run explainer ===================== */
/* Shown once, above everything, until dismissed (initIntro in js/app.js).
   The stage animates one 9-second loop split into three equal phases that
   match the three captions beneath it — commitment, reach, pick — so the
   picture and the words are never describing different steps. Every phase
   boundary below is a percentage of that shared --loop, which is why the
   captions can drive off the same keyframes with a 3s / 6s delay. */
.intro {
  --loop: 9s;
  position: relative;
  margin: 0 0 22px;
  padding: 20px 22px 18px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
}
.intro[hidden] { display: none; }
.intro-kicker {
  margin: 0; font-size: 0.74rem; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--red);
}
.intro-title {
  margin: 5px 0 14px; max-width: 30ch;
  font-family: var(--serif); font-size: 1.3rem; font-weight: 800;
  line-height: 1.25; color: var(--ink);
}
.intro-x {
  position: absolute; top: 12px; right: 12px;
  width: 30px; height: 30px; display: grid; place-items: center;
  font: inherit; font-size: 1.25rem; line-height: 1; cursor: pointer;
  color: var(--muted); background: #fff;
  border: 1px solid var(--line); border-radius: 9px;
}
.intro-x:hover { color: var(--ink); background: #f3f4f8; }

/* --- The animated stage --- */
.intro-stage {
  margin: 0 0 14px; padding: 6px;
  background: linear-gradient(180deg, #f7f6ff, #fcfcfe);
  border: 1px solid var(--line); border-radius: 12px;
}
.intro-svg { display: block; width: 100%; height: auto; }
.i-streets { fill: none; stroke: #e6e4f6; stroke-width: 2; }

/* Phase 2's reach ring. Scaled rather than animated on `r`, which is a CSS
   property only in newer engines — a transform works everywhere. */
.i-ring {
  fill: rgba(108, 76, 241, 0.09);
  stroke: var(--red); stroke-width: 2; stroke-dasharray: 5 5;
  transform-box: fill-box; transform-origin: center;
  opacity: 0;
  animation: iRing var(--loop) linear infinite;
}
@keyframes iRing {
  0%, 33% { transform: scale(0.12); opacity: 0; }
  38%     { opacity: 1; }
  52%     { transform: scale(1); opacity: 0.95; }
  94%     { transform: scale(1); opacity: 0.95; }
  100%    { transform: scale(1); opacity: 0; }
}

/* Phase 1: you are here. The halo beats on its own short cycle — it is a
   "live" signal, not part of the three-phase story. */
.i-you {
  fill: var(--red); stroke: #fff; stroke-width: 2.5;
  opacity: 0; animation: iFade var(--loop) linear infinite;
}
@keyframes iFade { 0% { opacity: 0; } 4%, 94% { opacity: 1; } 100% { opacity: 0; } }
.i-pulse {
  fill: rgba(108, 76, 241, 0.22);
  transform-box: fill-box; transform-origin: center;
  animation: iPulse 2.2s ease-out infinite;
}
@keyframes iPulse {
  0%        { transform: scale(0.5); opacity: 0.75; }
  70%, 100% { transform: scale(1.9); opacity: 0; }
}

/* Phase 2: the shows the ring swept up, popping in a beat apart. */
.i-pin {
  transform-box: fill-box; transform-origin: center;
  opacity: 0; animation: iPin var(--loop) linear infinite;
}
.i-pin circle { fill: #fff; stroke: var(--red); stroke-width: 2; }
.i-pin text { font-size: 11px; text-anchor: middle; }
.i-pin.is-pick circle { fill: #f1eefe; stroke-width: 3; }
.i-pin--2 { animation-delay: 0.3s; }
.i-pin--3 { animation-delay: 0.6s; }
@keyframes iPin {
  0%, 40%   { opacity: 0; transform: scale(0.3); }
  48%       { opacity: 1; transform: scale(1.14); }
  52%, 94%  { opacity: 1; transform: scale(1); }
  100%      { opacity: 0; transform: scale(1); }
}

/* Phase 3: the route drawing you → the show → where you have to be. */
.i-route {
  fill: none; stroke: var(--red); stroke-width: 2.5;
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 220; stroke-dashoffset: 220;
  animation: iRoute var(--loop) linear infinite;
}
@keyframes iRoute {
  0%, 66%  { stroke-dashoffset: 220; opacity: 1; }
  84%, 94% { stroke-dashoffset: 0; opacity: 1; }
  100%     { stroke-dashoffset: 0; opacity: 0; }
}

/* The next commitment (phase 1) and the verdict it earns (phase 3). */
.i-dest { opacity: 0; animation: iDest var(--loop) linear infinite; }
.i-dest rect { fill: #fff; stroke: var(--ink); stroke-width: 1.5; }
.i-dest text { font-size: 12px; font-weight: 700; text-anchor: middle; fill: var(--ink); }
@keyframes iDest { 0%, 12% { opacity: 0; } 20%, 94% { opacity: 1; } 100% { opacity: 0; } }

.i-verdict { opacity: 0; animation: iVerdict var(--loop) linear infinite; }
.i-verdict rect { fill: var(--ok-bg); }
.i-verdict text { font-size: 12px; font-weight: 700; text-anchor: middle; fill: var(--ok-text); }
@keyframes iVerdict { 0%, 84% { opacity: 0; } 90%, 96% { opacity: 1; } 100% { opacity: 0; } }

/* --- The three captions, lit in step with the stage --- */
.intro-steps {
  list-style: none; margin: 0 0 15px; padding: 0;
  display: grid; gap: 8px; counter-reset: istep;
}
.intro-step {
  position: relative; padding: 9px 12px 9px 42px;
  font-size: 0.9rem; line-height: 1.4; color: var(--muted);
  background: #fbfbfe; border: 1px solid transparent; border-radius: 11px;
  counter-increment: istep;
  animation: iStep var(--loop) linear infinite;
}
.intro-step b { color: var(--ink); font-weight: 700; }
.intro-step::before {
  content: counter(istep);
  position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
  width: 21px; height: 21px; display: grid; place-items: center;
  font-size: 0.72rem; font-weight: 800; color: #fff;
  background: #c7c2e8; border-radius: 50%;
  animation: iStepNum var(--loop) linear infinite;
}
.intro-step:nth-child(2), .intro-step:nth-child(2)::before { animation-delay: 3s; }
.intro-step:nth-child(3), .intro-step:nth-child(3)::before { animation-delay: 6s; }
@keyframes iStep {
  0%, 2%    { background: #fbfbfe; border-color: transparent; }
  5%, 30%   { background: #f3f1fe; border-color: #ddd7fb; }
  34%, 100% { background: #fbfbfe; border-color: transparent; }
}
@keyframes iStepNum {
  0%, 2%    { background: #c7c2e8; }
  5%, 30%   { background: var(--red); }
  34%, 100% { background: #c7c2e8; }
}

.intro-got {
  font: inherit; font-family: var(--serif); font-weight: 700; font-size: 0.92rem;
  color: #fff; background: var(--red); border: none; border-radius: 11px;
  padding: 10px 18px; cursor: pointer;
  box-shadow: 0 8px 18px rgba(108, 76, 241, 0.24);
}
.intro-got:hover { background: var(--red-dark); }
.intro-got:focus-visible, .intro-x:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }

/* Reduced motion gets the loop's *last* frame held still — every element
   visible, the route drawn — which is exactly the summary the animation
   spends nine seconds building up to. */
@media (prefers-reduced-motion: reduce) {
  .intro-stage *, .intro-step, .intro-step::before { animation: none; }
  .i-you, .i-dest, .i-pin, .i-verdict { opacity: 1; }
  .i-ring { opacity: 0.95; transform: scale(1); }
  .i-route { stroke-dashoffset: 0; }
  .i-pulse { opacity: 0; }
  .intro-step { background: #f3f1fe; border-color: #ddd7fb; }
  .intro-step::before { background: var(--red); }
}

/* --- Primary CTA (opens the next-commitment picker) --- */
.cta-card { border: none; box-shadow: none; background: transparent; }
.cta-card:hover { transform: none; box-shadow: none; }
.cta-card.is-open { z-index: 1500; }

/* Initial state: the faded placeholder itinerary and the "Tap to set
   constraints" dropzone are stacked in one grid cell, so the box floats
   centred over the skeleton — echoing the Plan page's upload interface. */
.cta-intake { display: grid; }
.cta-intake > * { grid-area: 1 / 1; }

/* The greyed-out preview of the plan the picker will build. Non-interactive;
   the dropzone sits over it and takes the clicks. */
.cta-skeleton {
  padding: 16px 18px 18px;
  border: 1px solid var(--line); border-radius: 16px;
  background: var(--paper);
  opacity: 0.5;
  pointer-events: none;
  user-select: none;
}
.cta-skeleton .plan-node { padding-left: 30px; }
/* Skeleton bars stand in for the real show name / venue / time chip. */
.skel-bar {
  display: block; border-radius: 6px;
  background: linear-gradient(90deg, #e7e9ef 0%, #eef0f5 50%, #e7e9ef 100%);
}
.skel-chip { width: 78px; height: 16px; border-radius: 999px; }
.skel-title { width: 62%; height: 15px; margin: 3px 0; }
.skel-sub { width: 44%; height: 11px; }

/* The overlay: a dashed, tappable card matching the Plan page's .dropzone,
   with a violet action pill reading "Tap to set constraints". */
.cta-trigger {
  align-self: center; justify-self: center;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 12px; text-align: center; cursor: pointer;
  width: min(100%, 420px); padding: 30px 24px;
  color: inherit;
  background: rgba(243, 241, 254, 0.94);
  border: 2px dashed var(--red); border-radius: 16px;
  box-shadow: var(--shadow-soft);
  backdrop-filter: blur(1px);
  transition: border-color 0.15s ease, background 0.15s ease, transform 0.12s ease;
}
.cta-trigger:hover { border-color: var(--red-dark); background: rgba(237, 233, 254, 0.97); transform: translateY(-1px); }
.cta-trigger:focus-visible { outline: 3px solid var(--gold); outline-offset: 3px; }
.cta-dz-icon {
  width: 46px; height: 46px; display: grid; place-items: center;
  color: var(--red); background: rgba(108, 76, 241, 0.1); border-radius: 14px;
}
.cta-dz-btn {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--serif); font-weight: 700; font-size: 1rem;
  color: #fff; background: var(--red);
  border-radius: 12px; padding: 12px 22px;
  box-shadow: 0 8px 20px rgba(108, 76, 241, 0.28);
}
.cta-trigger:hover .cta-dz-btn { background: var(--red-dark); }
/* Once a commitment is set the intake is redundant — the plan carries it and
   its destination node reopens the editor. Hide it, keep the panel usable. */
.has-plan .cta-intake { display: none; }
.has-plan .cta-card { margin: 0; }

/* --- Constraint picker: a standard <input type=time> + a show list --- */
/* Scroll-snap time wheel (real native momentum scrolling, like an iOS picker). */
.wheel-time {
  position: relative; display: flex; align-items: stretch; justify-content: center;
  gap: 4px; height: 200px; margin: 4px 0 2px;
  border: 1px solid var(--line); border-radius: 14px; background: var(--paper);
}
.wheel {
  position: relative; z-index: 1; height: 200px; width: 88px; overflow-y: scroll;
  scroll-snap-type: y mandatory; text-align: center; scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 32%, #000 68%, transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 32%, #000 68%, transparent);
}
.wheel::-webkit-scrollbar { display: none; }
.wheel:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; border-radius: 10px; }
.wheel-item {
  height: 40px; line-height: 40px; scroll-snap-align: center;
  font-family: var(--serif); font-weight: 600; font-size: 1.55rem; color: #b3bab2;
  transition: color 0.12s ease;
}
.wheel-item.sel { color: var(--ink); }
.wheel-pad { height: 80px; }
.wheel-sep { align-self: center; font-family: var(--serif); font-weight: 700; font-size: 1.55rem; color: var(--ink); }
/* The highlighted centre band. */
.wheel-band {
  position: absolute; left: 8px; right: 8px; top: 80px; height: 40px; z-index: 0;
  border-radius: 10px; background: rgba(108, 76, 241, 0.10);
  border: 1px solid rgba(108, 76, 241, 0.28); pointer-events: none;
}
.timepick-count { text-align: center; margin: 10px 0 10px; font-size: 0.82rem; color: var(--muted); }
.timepick-count .countnum { font-family: var(--serif); font-weight: 700; font-size: 1.4rem; color: var(--red-dark); display: inline-block; vertical-align: -2px; }
.timepick-count .countnum.bump { animation: countbump 0.4s ease; }
@keyframes countbump { 0% { transform: scale(1); } 42% { transform: scale(1.16); color: var(--red); } 100% { transform: scale(1); } }
.timepick-suggest { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 8px; }
.timepick-suggest button { font: inherit; font-size: 0.8rem; font-weight: 600; padding: 5px 10px; border-radius: 999px; border: 1px solid var(--line); background: #fff; color: var(--red-dark); cursor: pointer; }
.timepick-suggest button:hover { border-color: var(--red); }

.show-picklist { display: flex; flex-direction: column; gap: 8px; max-height: 300px; overflow-y: auto; margin: 2px 0 8px; }
.show-pick { display: flex; align-items: center; gap: 11px; text-align: left; width: 100%; padding: 10px 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--paper); cursor: pointer; font: inherit; color: inherit; }
.show-pick:hover { border-color: #cfc9bb; }
.show-pick.is-sel { border-color: var(--gold); background: rgba(108,76,241,0.07); }
.show-pick:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.sp-radio { width: 18px; height: 18px; border-radius: 50%; border: 2px solid #9aa39c; flex: 0 0 auto; }
.show-pick.is-sel .sp-radio { border-color: var(--gold); background: radial-gradient(circle at 50% 50%, var(--gold) 0 5px, transparent 6px); }
.sp-body { min-width: 0; }
.sp-title { display: block; font-family: var(--serif); font-weight: 600; font-size: 1rem; line-height: 1.15; }
.sp-venue { display: block; font-size: 0.78rem; color: var(--muted); }
.sp-genre { display: block; font-size: 0.66rem; letter-spacing: 0.04em; text-transform: uppercase; color: #9aa39c; margin-top: 2px; }
.show-pick.anim { animation: pickin 0.32s ease both; }
@keyframes pickin { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.picklist-empty { font-size: 0.85rem; color: var(--muted); margin: 4px 0; }

/* Subgenre tags — finer, display-only descriptors shown under the headline
 * genre on show rows, the picker and the plan. Wrap; never a filter. */
.show-subs { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.show-sub {
  font-size: 0.66rem;
  font-weight: 600;
  line-height: 1;
  padding: 3px 7px;
  border-radius: 999px;
  background: rgba(108, 76, 241, 0.09);
  color: var(--red);
  white-space: nowrap;
}

/* --- Filters as compact chips --- */
/* Four chips (genre · subgenre · $ · travel) don't fit one 375px line, so the
   row wraps rather than squeezing every label to an ellipsis. */
.filters-row { display: flex; align-items: center; gap: 6px; margin: 10px 0 6px; flex-wrap: wrap; }
.filters-label { flex: 0 0 auto; font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: #8c958c; }
.filter-chip { position: relative; min-width: 0; border: none; box-shadow: none; background: transparent; border-radius: 999px; }
.filter-chip:hover { transform: none; box-shadow: none; }
/* Filter chips stay flat when open too — without this override the
   .card.is-open lift (translateY(-3px)) only applies once the pointer
   leaves, so the chip visibly jumps up as the mouse moves away. */
.filter-chip.is-open { z-index: 1500; transform: none; box-shadow: none; }
.filter-chip .card-trigger {
  display: inline-flex; align-items: center; gap: 5px; width: auto; min-width: 0;
  background: #fff; border: 1px solid var(--line); border-radius: 999px;
  padding: 7px 11px; cursor: pointer; font: inherit; color: var(--ink);
}
.filter-chip .card-trigger:hover { border-color: #cfc9bb; }
.filter-chip .card-trigger:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
.filter-chip .fc-ico { flex: 0 0 auto; font-size: 0.9rem; }
.filter-chip .card-value { font-weight: 700; font-size: 0.82rem; text-transform: none; letter-spacing: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.filter-chip .fc-caret { flex: 0 0 auto; color: var(--muted); font-size: 0.68rem; }
.filter-chip .card-panel { left: 0; right: auto; min-width: 240px; }
/* Only the first chip on a row can safely drop its panel to the right; every
   later one anchors its panel's right edge to its own, so nothing spills off a
   narrow (375px) viewport. */
.card--travel .card-panel,
.card--subgenre .card-panel,
.card--price .card-panel { left: auto; right: 0; }
/* The two long option lists earn a slightly wider panel — their headings carry
   the "everything!" link alongside the question. */
.card--genre .card-panel, .card--subgenre .card-panel { min-width: 288px; }
.panel-everything { white-space: nowrap; }
/* The $ chip: a narrow button carrying the mark alone until a price is chosen,
   when it also spells out the current selection. */
.filter-chip--mini .card-trigger { padding: 7px 9px; gap: 4px; }
.fc-money { flex: 0 0 auto; font-weight: 800; font-size: 0.9rem; color: var(--muted); line-height: 1; }
.card--price.is-set .fc-money { color: var(--red); }
.filter-chip .card-value--price[hidden] { display: none; }
/* A tiny dropdown — only the price choices, so it needn't be panel-wide. Wide
   enough for three cap buttons per row (see .segmented--wrap). */
.card-panel--mini { min-width: 232px; padding: 14px 14px 15px; }
.card-panel--mini .panel-title { margin-bottom: 4px; }
.card-panel--mini .segmented { margin: 0; }
/* Sentence-case gloss under an ALL-CAPS panel title — carries the qualifier
   ("up to") that the compact buttons drop, so it is said once, not six times. */
.panel-hint { margin: 0 0 8px; font-size: 0.76rem; color: var(--muted); }

/* --- The plan: a compact vertical itinerary (works on narrow screens) --- */
.plan { margin: 16px 0 0; }
.plan:empty { display: none; }
.plan-head { font-family: var(--serif); font-weight: 600; font-size: 1.15rem; margin: 4px 0 12px; }
.plan-node { position: relative; padding: 0 0 2px 30px; }
/* A leading glyph in place of the old coloured dot: 📍 for "you", the show's
   genre emoji for the stop/destination. */
.plan-ico { position: absolute; left: 0; top: 0; width: 24px; text-align: center; font-size: 16px; line-height: 1.3; }
/* Small, explicit edit/remove controls shared by the plan's destination node and
   the focus card — a violet "Change ▾" link and a muted "×". */
.chg-link { border: none; background: transparent; font: inherit; font-size: 0.72rem; font-weight: 700; color: var(--blue); cursor: pointer; padding: 3px 6px; border-radius: 8px; white-space: nowrap; }
.chg-link:hover { background: rgba(108, 76, 241, 0.08); }
.chg-x { border: none; background: transparent; color: var(--muted); font-size: 1.25rem; line-height: 1; cursor: pointer; width: 26px; height: 26px; border-radius: 8px; }
.chg-x:hover { color: var(--ink); background: rgba(0, 0, 0, 0.05); }
.chg-link:focus-visible, .chg-x:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
/* Node controls sit top-right, on the hour line; reserve room so long titles
   don't run under them. */
.plan-node.has-node-controls { padding-right: 96px; }
.node-controls { position: absolute; top: -3px; right: 0; display: flex; align-items: center; gap: 2px; }
.plan-leg { margin: 3px 0 3px 6px; padding: 7px 0 7px 19px; border-left: 2px dashed #cfc9bb; font-size: 0.74rem; color: var(--muted); }
/* Time line: the slack chip sits to the left of the event time. */
.plan-timerow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.plan-time { font-size: 0.74rem; font-weight: 700; color: var(--red-dark); }
.plan-title { font-family: var(--serif); font-weight: 600; font-size: 1.06rem; line-height: 1.18; margin: 1px 0; }
.plan-sub { font-size: 0.78rem; color: var(--muted); }
/* A show named in the plan links to its own page on edfringe.com. The selected
   show links from its title (the title IS the show, so it's where you'd click);
   the commitment's title is a venue, so its show is named underneath instead. */
.plan-title-link { color: inherit; text-decoration: none; }
.plan-title-link:hover { text-decoration: underline; text-decoration-color: var(--blue); }
.plan-title-out { font-size: 0.8rem; font-weight: 700; color: var(--blue); }
.plan-dest-show { display: inline-block; margin-top: 4px; font-size: 0.78rem; font-weight: 700; color: var(--blue); text-decoration: none; }
.plan-dest-show:hover { text-decoration: underline; }
.plan-title-link:focus-visible, .plan-dest-show:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; border-radius: 6px; }
.plan-slack { display: inline-block; font-size: 0.68rem; font-weight: 700; padding: 3px 9px; border-radius: 999px; }
.plan-slack.ok { color: var(--ok-text); background: var(--ok-bg); }
.plan-slack.late { color: var(--red-dark); background: #fde7e7; }
/* The selected show can't be slipped in before the commitment — a solid, firmer
   red so it's clearly the thing that doesn't work (not the commitment). */
.plan-slack.wontfit { color: #fff; background: #e5484d; }
.plan-buy-inline { display: inline-block; margin-top: 8px; font-size: 0.72rem; font-weight: 700; text-decoration: none; padding: 5px 11px; border-radius: 999px; color: #fff; background: var(--red); }
.plan-buy-inline:hover { background: var(--red-dark); }
.plan-soldout { display: inline-block; margin-top: 8px; font-size: 0.7rem; font-weight: 700; color: var(--muted); }
/* Plan header row: "Your plan" on the left, a mild Maps link on the right. */
.plan-head-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 4px 0 12px; }
.plan-head-row .plan-head { margin: 0; }
.plan-maps-mild { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 5px; font-size: 0.8rem; font-weight: 700; text-decoration: none; color: var(--blue); white-space: nowrap; }
.plan-maps-mild .gmaps-logo { flex: 0 0 auto; }
.plan-maps-mild:hover { text-decoration: underline; }
.plan-maps-mild:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; border-radius: 6px; }

/* Spare-time CTA: turns the plan's slack into a prompt to browse shows. */
/* Spare-time prompt — rendered in the plan's middle slot (see spareCtaNode).
   Visually identical to the old standalone box; positioned relative so the
   dismiss × can sit in its corner. */
.plan-spare { position: relative; margin: 6px 0 8px; }
.spare-line { margin: 0; padding: 13px 16px; border: 1px solid var(--line); border-left: 4px solid var(--blue); border-radius: 12px; background: var(--paper); font-size: 0.98rem; line-height: 1.4; color: var(--ink); }
.plan-spare .spare-line { padding-right: 36px; }
.spare-line b { color: var(--blue); }
.spare-count { display: inline-block; margin-left: 4px; font-size: 0.82rem; font-weight: 700; color: var(--red-dark); white-space: nowrap; }
.spare-jump { cursor: pointer; text-decoration: underline; }
.spare-jump:hover { color: var(--red); }
.spare-jump:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; border-radius: 6px; }
/* Dismiss × shared by the corner-dismissable prompts (spare time, booking). */
.plan-spare-x, .plan-book-x { position: absolute; top: 6px; right: 8px; width: 24px; height: 24px; display: grid; place-items: center; border: none; background: transparent; color: var(--muted); font-size: 1.2rem; line-height: 1; cursor: pointer; border-radius: 6px; }
.plan-spare-x:hover, .plan-book-x:hover { color: var(--ink); background: rgba(0, 0, 0, 0.05); }
.plan-spare-x:focus-visible, .plan-book-x:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }

/* "Need to book it?" — the bold booking question on a committed bookable
   place. Dismissing it (the ×) falls back to the quiet .plan-buy-inline pill. */
.plan-book-cta { position: relative; margin-top: 10px; padding: 12px 14px; border: 1px solid var(--line); border-left: 4px solid var(--red); border-radius: 12px; background: var(--paper); }
.plan-book-cta .book-q { margin: 0 0 9px; padding-right: 30px; font-size: 1rem; font-weight: 800; color: var(--ink); }
.plan-book-btn { display: block; text-align: center; font-size: 0.95rem; font-weight: 800; text-decoration: none; padding: 12px 16px; border-radius: 12px; color: #fff; background: var(--red); }
.plan-book-btn:hover { background: var(--red-dark); }
.plan-book-btn:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }

/* Hairline between the top actionable card (plan, or the CTA before one
   exists) and the browse section (shows title, filters, map, list). */
.section-divider { border: 0; height: 1px; background: var(--line); margin: 28px 0 4px; }

/* --- View selector: Map | Closest | Soonest, one control under the filters.
   It carries both the view and (for the two list positions) the order, so the
   list itself has no sort control of its own. --- */
.view-area { margin-top: 10px; }
.view-panes { min-width: 0; }
/* The selector above owns the gap to the panes, so they add none of their own. */
.view-panes > .map-section,
.view-panes > .shows-section { margin-top: 0; }
/* Mirrors the filters row above it: same small caps label, same left edge. */
.view-row { display: flex; align-items: center; gap: 6px; margin: 8px 0 0; }
/* Same pill idiom as the in-panel .segmented controls; the three positions
   share the row so any of them is one tap away. */
.view-switch {
  flex: 1 1 auto; display: flex; gap: 4px;
  padding: 3px; border: 1px solid var(--line); border-radius: 999px; background: var(--bg);
}
.view-btn {
  flex: 1 1 0;
  display: flex; align-items: center; justify-content: center; gap: 6px;
  padding: 8px 6px;
  font: inherit; font-size: 0.85rem; font-weight: 700;
  color: var(--muted); background: transparent; border: none; border-radius: 999px;
  cursor: pointer; transition: color 0.12s ease, background 0.12s ease;
}
.view-btn.is-active { color: #fff; background: var(--red); }
.view-btn:not(.is-active):hover { color: var(--ink); background: rgba(0, 0, 0, 0.04); }
.view-btn:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
.view-btn-label { line-height: 1; }

/* --- Map: supporting, calm, modest --- */
.map-section { margin: 20px 0 0; }
#leaflet-map { height: 260px; border: 1px solid var(--line); border-radius: 14px; box-shadow: none; }
/* Desaturate the tiles a touch so the map reads as informative, not the main thing. */
.leaflet-tile-pane { filter: saturate(0.82) brightness(1.03); }
/* Genre-emoji map pins — the emoji sits inside a thin, semi-transparent white ring
   (override Leaflet's default square div-icon box). The ring is nearly transparent
   so pins stay light on a busy map; a soft shadow keeps the emoji legible. */
.genre-pin { background: none !important; border: none !important; }
.gpin { display: flex; align-items: center; justify-content: center; text-align: center; line-height: 1; border-radius: 50%; background: rgba(255, 255, 255, 0.12); border: 1px solid rgba(255, 255, 255, 0.7); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25); filter: drop-shadow(0 0.5px 1px rgba(0, 0, 0, 0.3)); }
/* The focused show and the committed destination are highlighted in the page's
   violet — the same 1px ring as every other pin, just recoloured, plus a faint
   violet tint. No size change, so the map's density is unaffected. */
.gpin--selected, .gpin--leg { background: rgba(108, 76, 241, 0.18); border: 1px solid var(--gold); }
/* Marker-cluster count bubble — violet disc in the page palette (replaces the
   plugin's default coloured circles, whose skin CSS we don't load). */
.genre-cluster { background: none; }
.gcluster { display: flex; align-items: center; justify-content: center; border-radius: 50%; background: var(--gold); color: #fff; font-weight: 700; font-size: 0.78rem; border: 2px solid #fff; box-shadow: 0 1px 4px rgba(16, 24, 40, 0.35); }
/* Fade the count bubbles while a show is focused, to match the dimmed pins. */
.gcluster--dim { opacity: 0.3; }
/* Leaflet attribution links — styled in site purple */
.leaflet-control-attribution { font-size: 0.65rem; }
.leaflet-control-attribution a { color: var(--blue); text-decoration: underline; }
.leaflet-control-attribution a:hover { color: var(--blue-dark); }

/* --- Reachable list: compact rows, capped --- */
.shows-section { padding: 8px 0 0; }
/* The count heading now sits above the filters + map, framing the whole area. */
.shows-title { font-size: 1.3rem; margin: 18px 0 8px; }
.shows-grid { display: flex; flex-direction: column; gap: 8px; grid-template-columns: none; }
.show-item {
  position: relative;
  display: flex; align-items: center; gap: 12px; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: 12px; background: var(--paper);
  cursor: pointer; transition: border-color 0.12s ease;
}
.show-item:hover { border-color: #cfc9bb; transform: none; }
.show-item--tight { opacity: 0.6; }
.show-item--soldout { opacity: 0.6; }
.show-item--leg { border-color: var(--ok); border-width: 2px; }
.show-item--selected { border-color: var(--red); border-width: 2px; box-shadow: none; }

/* Ink-stamp overlay on cards that are sold out / no longer reachable — a bold
 * boxed word, roughened at the edges (SVG #stamp-ink filter) and eaten away in
 * patches (a turbulence mask) so it reads like a real rubber stamp. The card
 * stays clickable — the stamp ignores pointer events. */
.show-stamp {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%) rotate(-7deg);
  z-index: 3; pointer-events: none;
  /* Heavy slab display face — the chunky letterform is what reads as a stamp
   * (loaded via Google Fonts in index.html). Alfa Slab One is already bold. */
  font-family: "Alfa Slab One", Rockwell, "Roboto Slab", var(--serif), serif;
  font-weight: 400; font-size: 1.1rem; line-height: 1;
  letter-spacing: 0.01em; text-transform: uppercase; white-space: nowrap;
  padding: 6px 15px 3px;
  border: 3px solid currentColor; border-radius: 4px;
  background: transparent;
  opacity: 0.85;
  /* Straight-ish edges (small displacement) — the worn look comes from the mask,
   * not from warping the box. */
  filter: url(#stamp-ink);
  /* Dry-ink erosion: a FINE, fairly uniform fractal-noise speckle eats little
   * flecks out of the letters and border everywhere (not big blotches). */
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='220'%20height='120'%3E%3Cfilter%20id='g'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='0.34'%20numOctaves='3'%20seed='7'%20stitchTiles='stitch'/%3E%3CfeColorMatrix%20type='saturate'%20values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR%20type='gamma'%20amplitude='1.9'%20exponent='0.55'%20offset='-0.28'/%3E%3CfeFuncG%20type='gamma'%20amplitude='1.9'%20exponent='0.55'%20offset='-0.28'/%3E%3CfeFuncB%20type='gamma'%20amplitude='1.9'%20exponent='0.55'%20offset='-0.28'/%3E%3CfeFuncA%20type='linear'%20slope='0'%20intercept='1'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect%20width='100%25'%20height='100%25'%20filter='url(%23g)'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='220'%20height='120'%3E%3Cfilter%20id='g'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='0.34'%20numOctaves='3'%20seed='7'%20stitchTiles='stitch'/%3E%3CfeColorMatrix%20type='saturate'%20values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR%20type='gamma'%20amplitude='1.9'%20exponent='0.55'%20offset='-0.28'/%3E%3CfeFuncG%20type='gamma'%20amplitude='1.9'%20exponent='0.55'%20offset='-0.28'/%3E%3CfeFuncB%20type='gamma'%20amplitude='1.9'%20exponent='0.55'%20offset='-0.28'/%3E%3CfeFuncA%20type='linear'%20slope='0'%20intercept='1'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect%20width='100%25'%20height='100%25'%20filter='url(%23g)'/%3E%3C/svg%3E");
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
  -webkit-mask-position: center; mask-position: center;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
}
.show-stamp--soldout { color: #e2726b; }
.show-stamp--late { color: #d09a5b; }
.show-item--selected .show-stamp { opacity: 0.5; } /* recede when the row is the active pick */
.si-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.show-genre { align-self: flex-start; font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 700; color: var(--red); margin: 0 0 2px; }
.show-name { font-family: var(--serif); font-weight: 600; font-size: 1rem; margin: 0 0 2px; line-height: 1.15; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.show-meta { color: var(--muted); font-size: 0.78rem; margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The row's out-link to edfringe.com. Quiet, so it doesn't compete with the
   row's own job (picking the show into the plan) — and clicking it only ever
   opens the show's page, never changes the plan. */
.si-link { align-self: flex-start; margin-top: 3px; font-size: 0.72rem; font-weight: 700; color: var(--blue); text-decoration: none; }
.si-link:hover { text-decoration: underline; }
.si-link:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; border-radius: 6px; }
.si-side { flex: 0 0 auto; text-align: right; }
.si-time { font-family: var(--serif); font-weight: 700; font-size: 1rem; color: var(--ink); }
.si-walk { font-size: 0.72rem; color: var(--muted); }
.si-fits { font-size: 0.66rem; font-weight: 700; color: var(--ok-text); }
.shows-group-head { font-family: var(--serif); font-weight: 600; font-size: 0.95rem; color: var(--red-dark); margin: 14px 0 2px; padding-top: 8px; border-top: 1px solid var(--line); }
.shows-group-head:first-child { border-top: none; padding-top: 0; margin-top: 2px; }
.show-more { display: block; width: 100%; margin: 14px 0 0; padding: 12px; font: inherit; font-weight: 700; font-size: 0.9rem; color: var(--red-dark); background: #fff; border: 1px solid var(--line); border-radius: 12px; cursor: pointer; }
.show-more:hover { border-color: var(--red); }
.show-more[hidden] { display: none; }

/* --- Focus visibility --- */
.nav-link:focus-visible, .icon-btn:focus-visible, .footer-link:focus-visible,
.show-item:focus-visible, .show-more:focus-visible {
  outline: 3px solid var(--gold); outline-offset: 2px; border-radius: 6px;
}

@media (prefers-reduced-motion: reduce) {
  .timepick-count .countnum.bump, .show-pick.anim { animation: none; }
}

/* Desktop keeps the same centered single column as mobile — just a wider gutter. */
@media (min-width: 720px) {
  .wrap { padding-left: 28px; padding-right: 28px; }
  #leaflet-map { height: 300px; }
}
