/* ============================================================================
 * venue-map widget — styles. Cartographic base colors live in the SVG (var()
 * with fallbacks); this file styles the widget chrome (controls, legend, popup,
 * pins, labels) and is theme-aware via the shared --thm-* tokens.
 * ========================================================================== */
.vmap { position: relative; width: 100%; font-family: system-ui, "Segoe UI", sans-serif;
  /* Panning drags the SVG — never let it text-select the road/landmark/water labels. */
  user-select: none; -webkit-user-select: none; }
/* ...but keep popup content copyable (booth names, links). */
.vmap-popup, .vmap-popup * { user-select: text; -webkit-user-select: text; }
.vmap-viewport {
  position: relative; overflow: hidden; border-radius: 14px; background: #a9cbe0;
  border: 1px solid rgba(0,0,0,.12); touch-action: none;
  aspect-ratio: 1500 / 867;
  box-shadow: 0 10px 30px rgba(6,18,26,.35), inset 0 0 0 1px rgba(255,255,255,.15);
}
.vmap-viewport svg { display: block; width: 100%; height: 100%; cursor: grab; --k: 1; }
.vmap-viewport svg.is-grabbing { cursor: grabbing; }

/* Decorative frame overlay — border-only (no board background), sits over the map edges,
   never blocks interaction. Inert until the map root has data-theme; then it pulls the
   theme's rope border (--thm-frame) + iron corners (.thm-corner art, via event-themes.css). */
.vmap-frame { position: absolute; inset: 0; pointer-events: none; z-index: 4; border: 0 solid transparent; border-radius: 14px; }
[data-theme] .vmap-frame { border-width: 15px; border-image: var(--thm-frame) 30 round; }
.vmap-frame .thm-corner.tl { top: -3px; left: -3px; }
.vmap-frame .thm-corner.tr { top: -3px; right: -3px; }
.vmap-frame .thm-corner.br { bottom: -3px; right: -3px; }
.vmap-frame .thm-corner.bl { bottom: -3px; left: -3px; }
/* keep the map corners smaller/proportionate than the schedule-panel corners */
[data-theme] .vmap-frame.thm-corners > .thm-corner { width: 34px; height: 34px; }
/* when framed, tuck the functional overlays clear of the rope border + corner brackets */
[data-theme] .vmap-compass  { top: 50px; left: 20px; }
[data-theme] .vmap-controls { top: 50px; right: 20px; }
[data-theme] .vmap-legend   { left: 54px; bottom: 20px; }

/* labels — sizes scale by --k (set on zoom) so they stay a CONSTANT screen size.
   Layer groups fade with zoom (level-of-detail) via a smooth opacity transition. */
.vmap .l-floats, .vmap .l-streets { transition: opacity .3s ease; }
.vmap .lbl-street { fill: #3f4b56; font-size: calc(12px * var(--k,1)); font-weight: 600; letter-spacing: .3px; paint-order: stroke; stroke: #fff; stroke-width: calc(2.6px * var(--k,1)); }
.vmap .float-badge { fill: var(--map-float,#7c5a34); stroke: #fff; r: calc(8px * var(--k,1)); stroke-width: calc(1.4px * var(--k,1)); }
.vmap .float-num { fill: #fff; font-size: calc(11px * var(--k,1)); font-weight: 700; text-anchor: middle; dominant-baseline: central; }
.vmap .lm-dot { fill: #b7791f; stroke: #fff; r: calc(4.5px * var(--k,1)); stroke-width: calc(1.4px * var(--k,1)); }
.vmap .lbl-lm { fill: #6b4a12; font-size: calc(12px * var(--k,1)); font-weight: 700; text-anchor: middle; paint-order: stroke; stroke: #fff; stroke-width: calc(2.6px * var(--k,1)); }
.vmap .lbl-water { fill: #2c6b8f; font-size: calc(14px * var(--k,1)); font-style: italic; font-weight: 600; text-anchor: middle; paint-order: stroke; stroke: #dcecf5; stroke-width: calc(2.6px * var(--k,1)); }
.vmap .credit { fill: #5a7484; font-size: calc(10px * var(--k,1)); }

/* pins — soft shadow + hover lift */
.vmap-pin { cursor: pointer; filter: drop-shadow(0 1.5px 2px rgba(0,0,0,.4)); transition: filter .12s ease; }
.vmap-pin:hover { filter: drop-shadow(0 4px 6px rgba(0,0,0,.55)) brightness(1.06); }
/* living pins (tiles/tourism): a soft category-colored glow so pins pop off the
   base map — subtle per pin, and it reads strongest in dusk mode. --pin = the
   category color, set on each pin group by the engine. */
.vmap.is-tiles .vmap-pin { filter: drop-shadow(0 1px 2px rgba(0,0,0,.45)) drop-shadow(0 0 4px var(--pin, transparent)); }
.vmap.is-tiles .vmap-pin:hover { filter: drop-shadow(0 4px 7px rgba(0,0,0,.55)) drop-shadow(0 0 8px var(--pin, transparent)) brightness(1.05); }
/* breathing pulse on the pin whose card is open — inert otherwise (opacity 0) */
.vmap-pin .pin-pulse { fill: none; stroke: var(--pin, var(--ce-teal-light)); stroke-width: 2; opacity: 0;
  transform-box: fill-box; transform-origin: center; pointer-events: none; }
.vmap-pin.is-selected .pin-pulse { animation: vmap-pin-pulse 1.7s ease-out infinite; }
.vmap-pin.is-selected { filter: drop-shadow(0 2px 3px rgba(0,0,0,.5)) drop-shadow(0 0 9px var(--pin, var(--ce-teal-light))); }
@keyframes vmap-pin-pulse {
  0%   { opacity: .55; transform: scale(1); }
  70%  { opacity: 0;   transform: scale(2.6); }
  100% { opacity: 0;   transform: scale(2.6); }
}
.vmap-pin .pin-body { stroke: #fff; stroke-width: 2.5; }
.vmap-pin .pin-letter { fill: #fff; font-size: 12px; font-weight: 800; text-anchor: middle; dominant-baseline: central; pointer-events: none; }
.vmap-pin .pin-icon path { fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; pointer-events: none; }
/* open (available) spot: hollow head, colored outline */
.vmap-pin.is-open .pin-body { stroke-width: 2.5; stroke-dasharray: 4 2.5; }

/* clusters + spiderfy spokes */
.vmap-cluster { cursor: pointer; filter: drop-shadow(0 2px 3px rgba(0,0,0,.35)); }
/* bloom on hover — halo swells + brightens (scale a CHILD, not the group, whose
   transform positions the cluster in tiles mode) */
.vmap-cluster .cl-halo { opacity: .26; transform-box: fill-box; transform-origin: center;
  transition: opacity .15s ease, transform .18s ease; }
.vmap-cluster .cl-body { stroke: #fff; stroke-width: 2.5; transform-box: fill-box; transform-origin: center;
  transition: transform .18s ease; }
.vmap-cluster:hover .cl-halo { opacity: .52; transform: scale(1.14); }
.vmap-cluster:hover .cl-body { transform: scale(1.08); }
.vmap-cluster .cl-count { fill: #fff; font-size: 14px; font-weight: 800; text-anchor: middle; dominant-baseline: central; pointer-events: none; }
.vmap-spoke { stroke: #6b5638; stroke-width: 1.5; opacity: .55; vector-effect: non-scaling-stroke; }

/* routes (polylines) — constant-width like a real map route line */
.vmap-route-casing { fill: none; stroke: #fff; stroke-width: 8; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; opacity: .85; }
.vmap-route { fill: none; stroke-width: 5; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
.vmap-route-hit { fill: none; stroke: transparent; stroke-width: 18; cursor: pointer; vector-effect: non-scaling-stroke; }
/* ---- RouteLayer: ONE shared look for trail/tour/scenic-drive routes ----
   Used by the public attraction page, the admin editor and the regional map so a
   route renders identically everywhere. (.vmap-stop-dot kept as a back-compat alias.) */
.rl-stop-ico, .rl-wp-ico, .vmap-stop-ico { background: none; border: 0; }
.rl-stop-dot, .vmap-stop-dot {
  display: flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%;
  color: #fff; font: 700 13px/1 var(--ce-font-body);
  border: 2px solid #fff; box-shadow: 0 1px 4px color-mix(in srgb, var(--ce-navy) 45%, transparent);
}
.rl-wp-dot {
  display: block; width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid #fff; box-shadow: 0 1px 3px rgba(10,22,40,.4); opacity: .9;
}
.rl-line, .vmap-troute { cursor: pointer; }
/* ---- PhotoLayer: PhotoWalk visitor-photo pins (js/map/photo-layer.js) ----
   The pin is the photo itself (circular thumb); popup is a small photo card.
   Data: baked data/photowalk/*.json from the isolated photowalk service. */
.pw-pin-ico { background: none; border: 0; }
.pw-pin {
  position: relative;   /* anchors .pw-pin-count */
  display: block; width: 38px; height: 38px; border-radius: 50%;
  background-size: cover; background-position: center; background-color: var(--ce-navy);
  border: 3px solid #fff; box-shadow: 0 2px 8px color-mix(in srgb, var(--ce-navy) 50%, transparent);
  transition: transform .12s ease;
}
.pw-pin:hover { transform: scale(1.12); }
/* how many photos hide in this ONE drop. Positioned relative to .pw-pin, so both the
   mystery and the unlocked thumb wear it identically. */
.pw-pin-count {
  position: absolute; top: -5px; right: -5px; min-width: 19px; height: 19px; padding: 0 4px;
  border-radius: 999px; background: var(--ce-coral); color: #fff; border: 2px solid #fff;
  font: 700 11px/15px var(--ce-font-body); font-style: normal; text-align: center;
  box-shadow: 0 1px 4px color-mix(in srgb, var(--ce-navy) 50%, transparent);
}
/* face-down mystery pin (PhotoWalk game board only — the unlock flips it to the thumb pin) */
.pw-pin--mystery {
  display: grid; place-items: center;
  background-image: radial-gradient(120% 120% at 30% 25%, var(--ce-navy-pin-2) 0%, var(--ce-navy) 70%);
  border-style: dashed; border-color: color-mix(in srgb, var(--ce-peach-2) 85%, transparent);
  color: var(--ce-peach-2); font: 800 17px/1 var(--ce-font-body);
  animation: pw-mystery-breathe 2.6s ease-in-out infinite;
}
/* the "?" sits in its own element so the count badge isn't centred by place-items too */
.pw-pin--mystery b { grid-area: 1 / 1; font: inherit; }
.pw-pin--pending {
  display: grid; place-items: center; width: 34px; height: 34px;
  background-image: radial-gradient(120% 120% at 30% 25%, var(--ce-slate-pin) 0%, var(--ce-navy-pin) 70%);
  border-style: dashed; border-color: rgba(255,255,255,.6); opacity: .92;
}
.pw-pin--pending svg {
  width: 17px; height: 17px; fill: none; stroke: var(--ce-peach-3); stroke-width: 2;
  stroke-linecap: round;
}
@keyframes pw-mystery-breathe {
  0%, 100% { box-shadow: 0 2px 8px color-mix(in srgb, var(--ce-navy) 50%, transparent); }
  50% { box-shadow: 0 2px 8px color-mix(in srgb, var(--ce-navy) 50%, transparent), 0 0 14px color-mix(in srgb, var(--ce-peach-2) 55%, transparent); }
}
/* HEAT — the pin reacts to being approached, so the walk has a tension curve instead of
   40 m of nothing followed by a binary unlock. walk.js sets the class from _distM. */
.pw-pin--warm { animation-duration: 1.5s; border-color: var(--ce-peach-2); }
.pw-pin--hot {
  animation-duration: .72s; border-style: solid; border-color: #fff;
  background-image: radial-gradient(120% 120% at 30% 25%, var(--ce-ocean-pin) 0%, var(--ce-navy-pin-3) 70%);
}
/* the hot pin also throws a ring, so it reads at a glance across a busy board */
.pw-pin--hot::before {
  content: ""; position: absolute; inset: -4px; border-radius: 50%;
  border: 2px solid color-mix(in srgb, var(--ce-peach-2) 90%, transparent); animation: pw-pin-ring 1.1s ease-out infinite;
}
@keyframes pw-pin-ring {
  0% { transform: scale(1); opacity: .85; }
  100% { transform: scale(2.1); opacity: 0; }
}
/* YOURS — a photo you dropped yourself. It is never a face-down mystery to the person who
   took it: you know what it is, and seeing it on the board is the confirmation that your
   drop actually made it through moderation. Teal ring + a camera dot. */
.pw-pin--mine { border-color: var(--ce-teal, var(--ce-teal-light)); box-shadow: 0 0 0 2px color-mix(in srgb, var(--ce-teal-light) 45%, transparent), 0 2px 8px color-mix(in srgb, var(--ce-navy) 50%, transparent); }
.pw-pin--mine::after {
  content: ""; position: absolute; right: -3px; bottom: -3px; width: 14px; height: 14px;
  border-radius: 50%; background: var(--ce-teal, var(--ce-teal-light)); border: 2px solid #fff;
  background-image: radial-gradient(circle at 50% 50%, #fff 0 2.2px, transparent 2.4px);
}
/* NEW SINCE YOU WERE HERE — a place you know that has gained photos. The whole point of a
   forever-growing board: the pin you already flipped can call you back. Coral badge, and a
   soft pulse so it reads as "something changed" rather than "you missed one". */
.pw-pin--new { border-color: #fff; }
.pw-pin--new .pw-pin-count {
  background: var(--ce-coral-hot, var(--ce-coral)); color: #fff;
  animation: pw-new-pulse 1.8s ease-in-out infinite;
}
@keyframes pw-new-pulse {
  0%, 100% { transform: scale(1); box-shadow: 0 1px 4px color-mix(in srgb, var(--ce-navy) 50%, transparent); }
  50% { transform: scale(1.14); box-shadow: 0 1px 4px color-mix(in srgb, var(--ce-navy) 50%, transparent), 0 0 12px color-mix(in srgb, var(--ce-coral) 85%, transparent); }
}
/* TIER — a drop holding several people's photos is worth crossing the road for, and
   nothing on the board said so. Gold face + gold count badge. */
.pw-pin--rich {
  border-color: #ffd27d; color: #ffd27d;
  background-image: radial-gradient(120% 120% at 30% 25%, #4a3a1c 0%, #1a1305 72%);
}
.pw-pin--rich .pw-pin-count { background: #ffd27d; color: #33270c; }
.pw-pin--rich.pw-pin--hot::before { border-color: rgba(255,210,125,.95); }
/* SPOTTED — passed on wheels between areas. Westport is a driving town, so the drive
   scouts instead of costing you the spots you drove past: a dashed ring marks "seen from
   the road, come back on foot". Deliberately quieter than the warm/hot approach states —
   it is a to-do, not a proximity cue. */
.pw-pin--spotted { border-style: dashed; border-color: rgba(255,217,168,.85); }
.pw-pin--spotted::after {
  content: ''; position: absolute; inset: -6px; border-radius: 50%;
  border: 1px dashed rgba(255,217,168,.4); pointer-events: none;
}
/* DISPLAY CLUSTER — several drops merged because they collide on SCREEN at this zoom.
   Purely a view artifact (walk.js::viewClusters): the drop is still the game unit and a
   cluster always splits as you zoom in. Two looks, and the difference is the whole point:
     --live  something inside is still unfound → mystery colours, dashed, breathing, and a
             coral badge counting the UNFOUND. A new spot can never be swallowed by a pile
             of photos you have already seen.
     --seen  everything inside is found → quiet, solid, no badge. It gets out of the way. */
.pw-cluster {
  position: relative; display: grid; place-items: center; border-radius: 50%;
  background-size: cover; background-position: center;
  box-shadow: 0 2px 10px color-mix(in srgb, var(--ce-navy) 55%, transparent); transition: transform .12s ease;
}
.pw-cluster:hover { transform: scale(1.08); }
.pw-cluster b {
  grid-area: 1 / 1; font: 800 15px/1 var(--ce-font-body);
  text-shadow: 0 1px 3px color-mix(in srgb, var(--ce-navy) 90%, transparent);
}
.pw-cluster--live {
  background-image: radial-gradient(120% 120% at 30% 25%, var(--ce-navy-pin-2) 0%, var(--ce-navy) 72%);
  border: 3px dashed color-mix(in srgb, var(--ce-peach-2) 90%, transparent); color: var(--ce-peach-3);
  animation: pw-mystery-breathe 2.6s ease-in-out infinite;
}
.pw-cluster--seen {
  border: 3px solid rgba(255,255,255,.72); color: #fff; opacity: .82;
  background-color: var(--ce-navy-pin);
}
/* the found cluster wears a photo face, so the count sits on a scrim rather than on top
   of whatever happened to be in that thumbnail */
.pw-cluster--seen::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--ce-navy) 78%, transparent) 0%, color-mix(in srgb, var(--ce-navy) 42%, transparent) 70%);
}
@media (prefers-reduced-motion: reduce) { .pw-cluster--live { animation: none; } }
/* one-shot unlock pop, applied via PhotoLayer.pinIcon(p,'pw-pin--pop') at flip time */
.pw-pin--pop { animation: pw-pin-pop .8s cubic-bezier(.2, 1.4, .4, 1); }
@keyframes pw-pin-pop {
  0% { transform: scale(.3); box-shadow: 0 0 0 0 color-mix(in srgb, var(--ce-teal-light) 80%, transparent); }
  55% { transform: scale(1.35); }
  100% { transform: scale(1); box-shadow: 0 0 0 22px color-mix(in srgb, var(--ce-teal-light) 0%, transparent); }
}
/* FLIP — setIcon swaps the DOM outright, so there is no two-faced card to rotate. Spinning
   the incoming thumb in from edge-on reads as the face-down pin turning over, which is the
   thing the whole game is named after; it deserves more than a scale pop. */
.pw-pin--flip { animation: pw-pin-flip .85s cubic-bezier(.2, 1.1, .35, 1); backface-visibility: hidden; }
@keyframes pw-pin-flip {
  0% { transform: rotateY(-100deg) scale(.55); box-shadow: 0 0 0 0 color-mix(in srgb, var(--ce-teal-light) 85%, transparent); }
  55% { transform: rotateY(0deg) scale(1.3); }
  100% { transform: rotateY(0deg) scale(1); box-shadow: 0 0 0 26px color-mix(in srgb, var(--ce-teal-light) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
  .pw-pin--mystery, .pw-pin--pop, .pw-pin--flip { animation: none; }
  .pw-pin--hot::before { animation: none; opacity: .7; }
  .pw-pin--new .pw-pin-count { animation: none; }
}
.pw-popup .leaflet-popup-content { margin: 0; width: auto !important; }
.pw-popup .leaflet-popup-content-wrapper { padding: 0; overflow: hidden; border-radius: 12px; }
.pw-card img { display: block; width: 240px; height: 170px; object-fit: cover; }
/* card(p,{noThumb}) — the meta only. The full-screen reveal already IS the photo, so the
   card's little copy of it was duplication, and its click-through was what threw players
   out into the phone's system browser instead of paging the gallery. */
.pw-card--bare .pw-card-meta { padding: 10px 14px 11px; }
.pw-card-meta { padding: 8px 12px 10px; display: grid; gap: 2px; }
.pw-card-meta b { font-size: 13.5px; color: var(--ce-deep); }
.pw-card-cap { font-size: 12.5px; color: #41586f; }
.pw-card-when { font-size: 11.5px; color: #7a8ea1; }
/* areas (polygons) — translucent zones */
.vmap-area { fill-opacity: .16; stroke-width: 2; stroke-opacity: .7; vector-effect: non-scaling-stroke; cursor: pointer; transition: fill-opacity .15s ease; }
.vmap-area:hover { fill-opacity: .3; }

/* north compass */
.vmap-compass {
  position: absolute; top: 12px; left: 12px; width: 34px; height: 34px; border-radius: 50%;
  background: rgba(255,255,255,.92); border: 1px solid rgba(0,0,0,.12); box-shadow: 0 2px 6px rgba(0,0,0,.22);
  display: grid; place-items: center; pointer-events: none;
}
.vmap-compass span { font-size: 12px; font-weight: 800; color: #c0392b; line-height: 1; }
.vmap-compass::before { content: ""; position: absolute; top: 3px; left: 50%; transform: translateX(-50%);
  border-left: 4px solid transparent; border-right: 4px solid transparent; border-bottom: 7px solid #c0392b; }

/* zoom / print controls — grouped, glassy, shadowed */
.vmap-controls { position: absolute; top: 12px; right: 12px; display: flex; flex-direction: column; gap: 10px; }
.vmap-ctl-group {
  display: flex; flex-direction: column; border-radius: 10px; overflow: hidden;
  background: rgba(255,255,255,.94); box-shadow: 0 2px 8px rgba(0,0,0,.25); border: 1px solid rgba(0,0,0,.08);
}
.vmap-controls button {
  width: 36px; height: 36px; border: 0; border-bottom: 1px solid rgba(0,0,0,.08);
  background: transparent; color: #253; font-size: 18px; line-height: 1; cursor: pointer;
  display: grid; place-items: center; transition: background .12s ease;
}
.vmap-ctl-group button:last-child { border-bottom: 0; }
.vmap-controls button:hover { background: rgba(0,0,0,.06); }
.vmap-controls button:active { background: rgba(0,0,0,.12); }
.vmap-controls button[data-act="print"] { font-size: 16px; }

/* legend — ONE unified marker that also filters. Collapsed = a compact pill; open = a single card
   whose bottom bar IS that same button, with tabs (Markers/Zones/Routes) so a visitor filters to just
   what they want ("show me vendors / parking") instead of wading through the whole key. Opens upward. */
.vmap-legend {
  position: absolute; left: 12px; bottom: 12px; z-index: 5;
  display: flex; flex-direction: column; align-items: stretch;
  max-width: min(82%, 260px); font-size: 12px; color: #223;
}
/* the card chrome lives on the CONTAINER when open, so header + tabs + list read as one marker */
.vmap-legend.is-open {
  background: rgba(255,255,255,.98); border: 1px solid rgba(0,0,0,.1); border-radius: 11px;
  box-shadow: 0 10px 26px rgba(0,0,0,.24); overflow: hidden; width: max-content; max-width: 100%;
}
.vmap-legend-hd {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer; width: 100%;
  background: rgba(255,255,255,.95); border: 1px solid rgba(0,0,0,.1); border-radius: 9px;
  padding: 7px 11px; font: 600 12px/1 inherit; color: #223; box-shadow: 0 2px 8px rgba(0,0,0,.2);
}
.vmap-legend-hd:hover { background: #fff; }
/* open: header becomes the card's bottom bar (own chrome removed; container carries it) */
.vmap-legend.is-open .vmap-legend-hd {
  background: transparent; border: 0; border-top: 1px solid rgba(0,0,0,.08); border-radius: 0; box-shadow: none;
}
.vmap-lg-ico { color: #5a6b7a; flex: none; }
.vmap-legend-ttl { flex: 1 1 auto; text-align: left; }
.vmap-legend-badge { background: #c0392b; color: #fff; border-radius: 10px; padding: 1px 6px; font-size: 10.5px; font-weight: 700; }
.vmap-legend-badge[hidden] { display: none; }
.vmap-lg-chev { color: var(--ce-steel); flex: none; transition: transform .18s ease; }
.vmap-legend.is-open .vmap-lg-chev { transform: rotate(180deg); }
.vmap-legend-body { order: -1; display: none; }   /* order:-1 → above the bar so the card opens upward */
.vmap-legend.is-open .vmap-legend-body { display: block; }
/* tabs — little segmented switcher across the top of the card */
.vmap-legend-tabs { display: flex; align-items: center; gap: 2px; padding: 5px 6px 0; border-bottom: 1px solid rgba(0,0,0,.07); }
.vmap-lg-tab { position: relative; background: transparent; border: 0; cursor: pointer; padding: 6px 9px 7px;
  font: 600 11.5px/1 inherit; color: #7a8894; border-radius: 7px 7px 0 0; white-space: nowrap; }
.vmap-lg-tab:hover { background: rgba(0,0,0,.04); color: #334; }
.vmap-lg-tab.is-on { color: #c0392b; }
.vmap-lg-tab.is-on::after { content: ""; position: absolute; left: 8px; right: 8px; bottom: -1px; height: 2px; background: #c0392b; border-radius: 2px 2px 0 0; }
.vmap-lg-tab.has-filter:not(.is-on)::before { content: ""; position: absolute; top: 4px; right: 3px; width: 5px; height: 5px; border-radius: 50%; background: #c0392b; }
.vmap-lg-reset { margin-left: auto; cursor: pointer; background: transparent; border: 0; padding: 5px 4px 6px; font: 600 11px/1 inherit; color: #3f6cae; }
.vmap-lg-reset:hover { text-decoration: underline; }
.vmap-lg-reset[hidden] { display: none; }
.vmap-legend-list { padding: 5px; min-width: 172px; max-height: min(42vh, 300px); overflow-y: auto; overscroll-behavior: contain; }
.vmap-lg-row { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; cursor: pointer;
  background: transparent; border: 0; border-radius: 7px; padding: 5px 7px; font: 500 12px/1.15 inherit; color: #223; }
.vmap-lg-row:hover { background: rgba(0,0,0,.05); }
.vmap-lg-name { flex: 1 1 auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vmap-lg-count { flex: none; font-size: 10.5px; color: var(--ce-steel); font-variant-numeric: tabular-nums; }
.vmap-lg-row.is-off { color: #aab4bd; }
.vmap-lg-row.is-off .sw { opacity: .25; }
.vmap-lg-row.is-off .vmap-lg-name { text-decoration: line-through; }
.vmap-legend .sw { width: 12px; height: 12px; border-radius: 50%; border: 1.5px solid #fff; box-shadow: 0 0 0 1px rgba(0,0,0,.15); flex: none; }
.vmap-legend .sw-line { width: 16px; height: 4px; border-radius: 2px; border: 0; }
.vmap-legend .sw-area { width: 13px; height: 13px; border-radius: 3px; border: 0; opacity: .5; }

/* popup card → rich storefront card (see map.widget placePopup) */
.vmap-popup {
  position: absolute; z-index: 5; min-width: 176px; max-width: 244px; transform: translate(-50%, -100%);
  background: #fff; color: #1c2833; border-radius: 13px; box-shadow: 0 14px 34px rgba(4,10,20,.34);
  border: 1px solid rgba(0,0,0,.1); padding: 0; font-size: 13px; pointer-events: auto;
  animation: vmap-pop .16s cubic-bezier(.2,.7,.3,1);
}
.vmap-popup.has-media { min-width: 214px; max-width: 282px; }
.vmap-popup.is-featured { border-color: var(--ce-peach, var(--ce-peach)); box-shadow: 0 14px 34px rgba(4,10,20,.34), 0 0 0 2px var(--ce-peach, var(--ce-peach)); }
.vmap-popup.below { transform: translate(-50%, 0); }   /* flipped: opens below the pin */
@keyframes vmap-pop { from { opacity: 0; } to { opacity: 1; } }
.vmap-popup[hidden] { display: none; }
.vmap-popup .pop-body { padding: 11px 13px 12px; }
.vmap-popup .pop-accent { position: absolute; top: 0; left: 0; right: 0; height: 4px; border-radius: 13px 13px 0 0; z-index: 1; }
/* photo header — flush to the card top, rounded to match; badge + featured ribbon overlay it */
.vmap-popup .pop-media { position: relative; margin: 0; border-radius: 13px 13px 0 0; overflow: hidden; }
.vmap-popup .pop-media .pop-img { width: 100%; height: 118px; object-fit: cover; display: block; margin: 0; border-radius: 0; }
.vmap-popup .pop-media-shade { position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(10,22,40,0) 52%, rgba(10,22,40,.5)); }
.vmap-popup .pop-badge { position: absolute; top: 9px; right: 9px; z-index: 2;
  font: 700 10px/1 var(--ce-font-body); padding: 5px 9px; border-radius: 999px; }
.vmap-popup .pop-badge--open { background: #7fe0c0; color: #073a26; }
.vmap-popup .pop-badge--closed { background: #e7c2c2; color: #5a1f1f; }
.vmap-popup .pop-badge--featured { background: var(--ce-peach, var(--ce-peach)); color: #5a3a10; }
.vmap-popup .pop-ribbon { position: absolute; top: 9px; left: 9px; z-index: 2;
  font: 700 9.5px/1 var(--ce-font-body); text-transform: uppercase; letter-spacing: .1em;
  padding: 5px 9px; border-radius: 999px; background: var(--ce-peach, var(--ce-peach)); color: #5a3a10;
  box-shadow: 0 0 16px var(--ce-glow-peach, color-mix(in srgb, var(--ce-peach) 50%, transparent)); }
.vmap-popup .pop-type { font-size: 10.5px; text-transform: uppercase; letter-spacing: .12em; font-weight: 700; margin-top: 1px; }
.vmap-popup .pop-title { font-weight: 700; font-size: 15.5px; margin: 2px 0 5px; line-height: 1.15; font-family: var(--ce-font-display, Georgia, serif); }
.vmap-popup .pop-blurb { color: #4b5a68; line-height: 1.4; margin: 0 0 9px; font-size: 12.5px; }
.vmap-popup .pop-rows { display: flex; flex-direction: column; gap: 3px; margin: 0 0 10px; }
.vmap-popup .pop-row { display: flex; gap: 8px; font-size: 12px; line-height: 1.35; }
.vmap-popup .pop-k { flex: none; min-width: 48px; color: var(--ce-steel); font-weight: 700; }
.vmap-popup .pop-v { color: #2b3947; }
/* action row — pill buttons; the primary (Directions) is filled with the pin color */
.vmap-popup .pop-links { display: flex; gap: 7px; flex-wrap: wrap; }
.vmap-popup .pop-link { display: inline-flex; align-items: center; justify-content: center; flex: 1 1 auto;
  font-weight: 700; font-size: 12px; text-decoration: none; padding: 8px 11px; border-radius: 9px;
  color: var(--pop-accent, var(--ce-deep)); background: color-mix(in srgb, var(--ce-deep) 9%, transparent); white-space: nowrap;
  transition: background .13s ease, transform .13s ease; }
.vmap-popup .pop-link:hover { background: color-mix(in srgb, var(--ce-deep) 16%, transparent); transform: translateY(-1px); text-decoration: none; }
.vmap-popup .pop-link--primary { color: #fff; background: var(--pop-accent, var(--ce-deep)); flex: 1 1 100%;
  box-shadow: 0 4px 14px color-mix(in srgb, var(--pop-accent, var(--ce-deep)) 45%, transparent); }
.vmap-popup .pop-link--primary:hover { filter: brightness(1.06); background: var(--pop-accent, var(--ce-deep)); }
.vmap-popup .pop-close { position: absolute; top: 6px; right: 8px; z-index: 3; width: 24px; height: 24px; border: 0;
  background: rgba(255,255,255,.82); border-radius: 50%; font-size: 15px; cursor: pointer; color: #33414e;
  display: grid; place-items: center; box-shadow: 0 1px 4px rgba(0,0,0,.25); }
.vmap-popup.has-media .pop-close { background: rgba(10,22,40,.5); color: #fff; }
/* save / "Add to My Day" heart — mirrors the close button, top-left */
.vmap-popup .pop-save { position: absolute; top: 6px; left: 8px; z-index: 3; width: 26px; height: 26px; border: 0;
  background: rgba(255,255,255,.82); border-radius: 50%; cursor: pointer; padding: 0; display: grid; place-items: center;
  box-shadow: 0 1px 4px rgba(0,0,0,.25); transition: transform .12s ease; }
.vmap-popup.has-media .pop-save { background: rgba(10,22,40,.5); }
.vmap-popup .pop-save svg { width: 15px; height: 15px; fill: none; stroke: #33414e; stroke-width: 2; }
.vmap-popup.has-media .pop-save svg { stroke: #fff; }
.vmap-popup .pop-save.is-saved svg { fill: var(--ce-coral); stroke: var(--ce-coral); }
.vmap-popup .pop-save:hover { transform: translateY(-1px); }
.vmap-popup::after { content: ""; position: absolute; left: calc(50% + var(--arrow-dx, 0px)); bottom: -7px;
  transform: translateX(-50%); border: 7px solid transparent; border-top-color: #fff; }
/* flipped: arrow on top, pointing up at the pin */
.vmap-popup.below::after { bottom: auto; top: -7px; border-top-color: transparent; border-bottom-color: #fff; }

/* ---- tiles base mode (Feature B: regional tourism map on Leaflet/OSM) --------------------------
   The page sizes the host; the viewport fills it (no SVG aspect-ratio). The engine's pin/cluster
   layer renders in an overlay SVG above the Leaflet panes; only .vmap-mark takes pointer events so
   the base map still drags/zooms everywhere else. */
.vmap.is-tiles { height: 100%; }
.vmap.is-tiles .vmap-viewport { aspect-ratio: auto; height: 100%; min-height: 320px; }
.vmap-tilebase { position: absolute; inset: 0; z-index: 1; }
.vmap-tilesvg { position: absolute; inset: 0; z-index: 2; width: 100%; height: 100%;
  pointer-events: none; overflow: hidden; transition: opacity .12s ease; }
.vmap-tilesvg .vmap-mark { pointer-events: auto; }
/* touch devices: marks are pointer-INERT — pan/pinch starting on a pin must drag the
   map (the engine resolves taps by hit-testing map clicks instead). */
.vmap-tilesvg.is-touch .vmap-mark { pointer-events: none; }
.vmap-tilesvg.is-zooming { opacity: 0; }          /* marks fade during the tile zoom animation, re-cluster at zoomend */
.vmap.is-tiles .vmap-controls { z-index: 3; }
.vmap.is-tiles .leaflet-control-attribution { font-size: 10px; }
/* glassy glow on the map controls so they belong to the coastal chrome */
.vmap.is-tiles .vmap-ctl-group { box-shadow: 0 2px 10px rgba(4,10,20,.3), 0 0 0 1px color-mix(in srgb, var(--ce-teal-light) 14%, transparent); }

/* --- dusk mode: flip the OSM base into the chamber's night palette ---------
   One GPU filter on the tile pane (cheap); smooth cross-flip. The pin/label
   overlay is untouched, so pins actually pop harder at dusk. */
.vmap-tilebase .leaflet-tile { transition: filter .5s ease; }
.vmap.is-dusk .vmap-tilebase .leaflet-tile {
  filter: grayscale(.35) sepia(.55) hue-rotate(178deg) saturate(1.5) brightness(.74) contrast(1.03);
}
/* a faint navy vignette at dusk for depth (pointer-inert, above tiles/below pins) */
.vmap.is-dusk .vmap-tilebase::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(120% 90% at 50% 40%, transparent 55%, rgba(10,22,40,.34));
}
@media (prefers-reduced-motion: reduce) { .vmap-tilebase .leaflet-tile { transition: none; } }

/* --- Member of the Day: a golden halo + persistent glow on the featured pin --- */
.vmap-pin .pin-featured-halo { fill: none; stroke: #ffcf6b; stroke-width: 2.5; opacity: 0;
  transform-box: fill-box; transform-origin: center; }
.vmap-pin.is-featured .pin-featured-halo { animation: vmap-featured 2.3s ease-out infinite; }
.vmap-pin.is-featured { filter: drop-shadow(0 2px 3px rgba(0,0,0,.5)) drop-shadow(0 0 9px #ffcf6b); }
@keyframes vmap-featured { 0% { opacity: .7; transform: scale(.85); } 70% { opacity: 0; transform: scale(2.5); } 100% { opacity: 0; transform: scale(2.5); } }
@media (prefers-reduced-motion: reduce) { .vmap-pin.is-featured .pin-featured-halo { animation: none; } }

/* --- Open now cue (tourism map): open pins get a green ring, closed pins dim --- */
.vmap-pin .pin-ring-open { fill: none; stroke: #2ecc71; stroke-width: 2.4; opacity: .95; }
.vmap-pin.is-closed { opacity: .5; transition: opacity .12s ease; }
.vmap-pin.is-closed:hover { opacity: .82; }

/* inline open/closed pill inside the card body (no-photo cards) */
.vmap-popup .pop-badge--inline { position: static; display: inline-block; margin-left: 8px; vertical-align: middle;
  font: 700 9.5px/1 var(--ce-font-body); padding: 3px 7px; border-radius: 999px;
  text-transform: none; letter-spacing: 0; }
/* featured "Member of the Day" ribbon inline in a no-photo card */
.vmap-popup .pop-ribbon--inline { position: static; display: inline-block; margin: 0 0 6px;
  box-shadow: none; }

/* --- "you are here" beacon (Near me) — pulsing dot in the Leaflet marker pane --- */
.tmap-beacon { background: transparent; border: 0; }
.tmap-beacon-dot { display: block; width: 16px; height: 16px; border-radius: 50%; position: relative;
  background: #2f7fff; border: 2.5px solid #fff; box-shadow: 0 0 0 2px rgba(47,127,255,.4), 0 2px 6px rgba(0,0,0,.4); }
.tmap-beacon-dot::after { content: ""; position: absolute; inset: -6px; border-radius: 50%;
  border: 2px solid rgba(47,127,255,.55); animation: tmap-beacon 1.8s ease-out infinite; }
@keyframes tmap-beacon { 0% { transform: scale(.55); opacity: .85; } 100% { transform: scale(1.9); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .tmap-beacon-dot::after { animation: none; } }


@media print {
  .vmap-controls, .vmap-legend, .vmap-popup, .vmap-compass { display: none !important; }
  .vmap-rotor { transform: none !important; }   /* print always north-up */
}

/* ---- rotation + mobile usability ------------------------------------------------ */
/* The compass doubles as the rotate control: DRAG it to spin the map to your facing,
   TAP it to snap back to true north. It always points to true north. */
.vmap-compass { pointer-events: auto; cursor: grab; touch-action: none; transition: transform .08s linear; will-change: transform; }
.vmap-compass:active { cursor: grabbing; }
/* transparent, finger-friendly tap target around each pin (helps on phones) */
.vmap-pin .pin-hit { fill: transparent; pointer-events: all; }
/* gestures (pan / pinch / twist) must not scroll or zoom the PAGE on mobile */
.vmap-viewport svg { touch-action: none; }
/* touch screens: bigger controls + roomier popup/legend */
@media (pointer: coarse) {
  .vmap-compass { width: 46px; height: 46px; top: 10px; left: 10px; }
  .vmap-controls { gap: 12px; }
  .vmap-controls button { width: 46px; height: 46px; font-size: 21px; }
  .vmap-popup { min-width: 210px; max-width: min(86vw, 320px); font-size: 14px; }
  .vmap-popup .pop-body { padding: 13px 15px 15px; }
  .vmap-legend { max-width: 76%; }
  .vmap-legend-hd { padding: 9px 13px; font-size: 13px; }
  .vmap-legend-list { max-height: 50vh; min-width: 200px; }
  .vmap-lg-tab { padding: 8px 11px 9px; font-size: 12.5px; }
  .vmap-lg-row { padding: 8px 8px; font-size: 13px; }
  .vmap-lg-reset { padding: 7px 6px; font-size: 12px; }

  /* tiles map declutter on touch: pinch zooms; hide the +/− buttons, keep reset */
  .vmap.is-tiles .vmap-controls .vmap-ctl-group:first-child { display: none; }
  /* finger-sized action buttons in the card; no double-tap-zoom on any control */
  .vmap-popup .pop-link { min-height: 44px; font-size: 13px; }
  .vmap-popup .pop-link, .vmap-popup .pop-close, .vmap-popup .pop-save,
  .vmap-controls button { touch-action: manipulation; }
}
@keyframes vmap-sheet { from { transform: translateY(100%); } to { transform: translateY(0); } }

/* ---- docked card (tiles map on touch devices) ----------------------------------------
   The card docks INSIDE the map box (absolute in .vmap, bottom edge) — it pops up OVER
   the map you're looking at, so it is on screen the instant you tap a pin. No
   position:fixed, no body-mounting: nothing for a mobile browser to get wrong.
   A scrim dims the map behind it; tap the scrim (or swipe the handle down) to close. */
.vmap-scrim { position: absolute; inset: 0; z-index: 7; background: rgba(8, 16, 30, .38);
  opacity: 0; pointer-events: none; transition: opacity .18s ease; }
.vmap.sheet-open .vmap-scrim { opacity: 1; pointer-events: auto; }
.vmap-popup.is-sheet {
  position: absolute; left: 0; right: 0; bottom: 0; top: auto;
  transform: none !important; width: 100%; min-width: 0; max-width: none;
  z-index: 8;                                   /* above the scrim, inside the map's own context */
  border-radius: 16px 16px 0 0; border: 0;
  box-shadow: 0 -12px 40px rgba(4, 10, 20, .5);
  max-height: 64%;                              /* of the MAP box — the top strip stays visible */
  overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  animation: vmap-sheet .24s cubic-bezier(.2, .72, .28, 1);
}
.vmap-popup.is-sheet::after { display: none; }               /* a docked card has no pin-arrow */
.vmap-popup.is-sheet .pop-media { border-radius: 0; }
.vmap-popup.is-sheet .pop-media .pop-img { height: 118px; }  /* compact — details first */
.vmap-popup.is-sheet .pop-body { padding: 4px 16px 14px; }
.vmap-popup.is-sheet .pop-title { font-size: 18px; }
.vmap-popup.is-sheet .pop-blurb { display: none; }           /* the card is a launchpad; prose lives on Details */
.vmap-popup.is-sheet .pop-close { top: 8px; right: 10px; width: 32px; height: 32px; font-size: 18px; }
.vmap-popup.is-sheet .pop-save { top: 8px; left: 10px; width: 32px; height: 32px; }
.vmap-popup.is-sheet .pop-save svg { width: 18px; height: 18px; }
/* swipe-down grab handle — sticky so it stays reachable while the sheet scrolls */
.vmap-popup .pop-grab { display: none; }
.vmap-popup.is-sheet .pop-grab { display: flex; justify-content: center; align-items: center;
  position: sticky; top: 0; z-index: 4; padding: 10px 0 6px; cursor: grab; touch-action: none;
  background: linear-gradient(180deg, rgba(255,255,255,.95), rgba(255,255,255,0)); }
.vmap-popup.is-sheet.has-media .pop-grab { position: absolute; top: 0; left: 0; right: 0;
  background: linear-gradient(180deg, rgba(10,22,40,.45), transparent); border-radius: 20px 20px 0 0; }
.vmap-popup .pop-grab span { width: 44px; height: 5px; border-radius: 3px; background: color-mix(in srgb, var(--ce-deep) 28%, transparent); }
.vmap-popup.is-sheet.has-media .pop-grab span { background: rgba(255,255,255,.75); }
@media (prefers-reduced-motion: reduce) { .vmap-popup.is-sheet { animation: none; } }

/* long content must WRAP, never push the card (or the page) sideways */
.vmap-popup .pop-row { flex-wrap: wrap; }
.vmap-popup .pop-v { min-width: 0; overflow-wrap: anywhere; }
.vmap-popup .pop-title, .vmap-popup .pop-blurb { overflow-wrap: break-word; }
