/* =====================================================================
   Trova — styles

   COLOUR IS DEFINED IN EXACTLY TWO PLACES, BOTH BELOW.

     1. PALETTE — the raw colours, as 50–900 intensity scales.
        Want a different look? Change these hex values. Nothing else in
        this file contains a hex code.

     2. TOKENS — what each colour DOES (background, text, border,
        primary…). Every rule further down uses a token, never a palette
        value, so re-pointing one token here re-skins that role
        everywhere it appears.

   Intensity guide, used consistently by the tokens:
     50–200   backgrounds, light fills, hover / selected surfaces
     300–500  standard: borders, muted icons, secondary text
     600–800  important actions: primary brand, active states, map pins
     900      strong contrast: body text, deepest accents
   ===================================================================== */

:root {
  /* ============ 1. PALETTE ==========================================
     Four families. Swap the hexes to re-theme the entire app.
     ================================================================== */

  /* Primary — brand orange/terracotta */
  --primary-50:  #FBF2EA;
  --primary-100: #F6E5D6;
  --primary-200: #EECBAC;
  --primary-300: #E5B283;
  --primary-400: #DD9859;
  --primary-500: #D47E30;
  --primary-600: #BD652F;
  --primary-700: #A64B2E;
  --primary-800: #7E3821;
  --primary-900: #562513;

  /* Secondary — warm clay / sand */
  --secondary-50:  #EEF4F0;
  --secondary-100: #DCEAE1;
  --secondary-200: #BAD4C3;
  --secondary-300: #97BFA5;
  --secondary-400: #74A986;
  --secondary-500: #529469;
  --secondary-600: #2F7E4A;
  --secondary-700: #27683D;
  --secondary-800: #1E522F;
  --secondary-900: #153B21;

  /* Accent — contrast blue */
  --accent-50:  #EEF2F4;
  --accent-100: #DCE4EA;
  --accent-200: #BAC9D4;
  --accent-300: #97AEBF;
  --accent-400: #7493A9;
  --accent-500: #527894;
  --accent-600: #2F5D7E;
  --accent-700: #274D68;
  --accent-800: #1E3D52;
  --accent-900: #152C3B;

  /* Neutral — warm taupe */
  --neutral-50:  #FAF7F2;
  --neutral-100: #EEE6DB;
  --neutral-200: #D9C8B2;
  --neutral-300: #C0AC96;
  --neutral-400: #A68F7A;
  --neutral-500: #8A7664;
  --neutral-600: #6E5D4D;
  --neutral-700: #584C42;
  --neutral-800: #413C36;
  --neutral-900: #2B2B2B;

  /* ============ 2. TOKENS ===========================================
     Role  ->  palette rung. Change the right-hand side to re-assign.
     ================================================================== */

  /* Surfaces */
  --color-background:       var(--neutral-50);    /* the page / map ground */
  --color-surface:          #ffffff;              /* cards, panel, popovers */
  --color-surface-sunken:   var(--neutral-100);   /* toggle tracks, count pills */

  /* Text */
  --color-text:             var(--neutral-900);   /* headings, body */
  --color-text-muted:       var(--neutral-600);   /* secondary text, icons */
  --color-text-subtle:      var(--neutral-500);   /* hints, empty states */
  --color-on-primary:       var(--neutral-900);   /* text ON a primary fill */

  /* Borders */
  --color-border:           var(--neutral-200);
  --color-border-strong:    var(--neutral-300);

  /* Primary — the brand colour */
  --color-primary:          var(--primary-500);   /* fills: active chips, hero */
  --color-primary-hover:    var(--primary-600);
  --color-primary-strong:   var(--primary-700);   /* text/icons on light, map pins */
  --color-primary-subtle:   var(--primary-50);    /* hover + selected backgrounds */
  --color-primary-gradient: linear-gradient(135deg, var(--primary-500), var(--primary-500));
  --color-brand-gradient:   linear-gradient(135deg, var(--primary-800), var(--primary-800));

  /* SELECTED state for every pill-shaped control — category chips, the
     Places/Events filter buttons, EN/ES, the route modal segments, the
     favourites tabs. They used to be a mix of --color-primary and
     --color-primary-gradient, so "on" did not look like one thing; routing
     them all through these two means the selected look is changed in one
     place. Deep enough to need light text, hence the paired ink. */
  --color-control-on:       var(--primary-800);
  --color-on-control-on:    var(--color-on-inverse);

  /* Secondary + accent */
  --color-secondary:        var(--secondary-400);
  --color-accent:           var(--accent-600);
  --color-accent-strong:    var(--accent-800);
  --color-accent-subtle:    var(--accent-50);

  /* Map pins, by category group.
     Broad fallback groups (used by any category without a specific group): */
  --color-pin-culture:      var(--primary-400);
  --color-pin-water:        var(--accent-500);
  --color-pin-nature:       var(--secondary-400);
  --color-pin-other:        var(--neutral-300);
  /* Per-category pin hues, for more variety on the map. These are deliberate
     one-off colours outside the four tonal scales, so they are literal hex.
     A category maps here via its `group` in categories.mjs; add a group +
     a --color-pin-<group> var to give any category its own colour. */
  --color-pin-ruins:        var(--primary-800);
  --color-pin-village:      var(--secondary-500);
  --color-pin-monument:     #E5B283;
  --color-pin-castle:       #6E5D4D;
  --color-pin-worship:      #E5B283;
  --color-pin-freshwater:   #97AEBF;   /* lakes + waterfalls */
  --color-pin-aquarium:     #527894;
  --color-pin-viewpoint:    #584C42;
  --color-pin-forest:       #27683D;
  --color-pin-cave:         #153B21;
  --color-pin-event:        var(--primary-700);  /* events layer */
  --color-route:            var(--accent-600);   /* walking-route line + stops */
  --color-on-route:         var(--color-on-inverse);  /* text on a route fill */

  /* Frosted glass — translucent surfaces floating over the map */
  --color-glass:            rgba(255,255,255,.72);  /* top bar, search bar */
  --color-glass-strong:     rgba(255,255,255,.82);  /* side panel, map controls */
  --color-glass-control:    rgba(255,255,255,.60);  /* chips, status pill */
  --color-glass-soft:       rgba(255,255,255,.50);  /* panel footer */
  --color-glass-border:     var(--color-glass-soft);

  /* Marks layered on top of a primary fill */
  --color-on-primary-surface:       rgba(255,255,255,.18);  /* hero buttons */
  --color-on-primary-surface-hover: rgba(255,255,255,.32);
  --color-on-primary-pill:          rgba(255,255,255,.28);  /* vote count pill */
  --color-on-primary-tile:          rgba(255,255,255,.90);  /* hero icon tile */

  /* Map marks */
  --color-cluster:          var(--neutral-50);  /* cluster bubbles */
  --color-on-cluster:       var(--color-on-primary);        /* the count inside */
  --color-on-pin:           #ffffff;                        /* icon inside a pin */
  --color-marker-ring:      var(--color-cluster);                 /* pin + cluster outline */
  /* Cluster "heat": bubbles shade from quiet to hot with the best rank inside,
     so density and QUALITY are both visible at a glance. */
  --color-cluster-quiet:    var(--color-cluster);
  --color-cluster-hot:      var(--primary-700);
  --color-marker-halo:      color-mix(in srgb, var(--color-primary) 55%, transparent);

  /* Inverted surface (toast) */
  --color-surface-inverse:  var(--neutral-900);
  --color-on-inverse:       #ffffff;

  /* Status — functional, deliberately outside the brand palette */
  --color-success:          #2FA84F;
  --color-success-subtle:   rgba(34,160,80,.15);
  --color-success-glow:     color-mix(in srgb, var(--color-success) 45%, transparent);
  --color-danger:           #C0392B;
  --color-danger-subtle:    rgba(209,67,67,.15);
  --color-location:         #2F7BE4;   /* "you are here" marker */
  --color-location-glow:    color-mix(in srgb, var(--color-location) 30%, transparent);

  /* ---- Non-colour design tokens ---- */
  --shadow-color: 58 38 26;   /* warm shadow tint, used with rgb(... / alpha) */
  --shadow-sm:  0 2px 8px    rgb(var(--shadow-color) / .10);
  --shadow-md:  0 10px 30px  rgb(var(--shadow-color) / .16);
  --shadow-lg:  0 24px 60px  rgb(var(--shadow-color) / .22);

  --radius:     20px;
  --radius-sm:  14px;

  --font-display: "Montserrat", system-ui, -apple-system, sans-serif;
  --font-body:    "Montserrat", system-ui, -apple-system, sans-serif;

  --panel-w:    400px;
}

/* The drawn route is clickable — it brings its itinerary back — so say so.
   Scoped to the route layer's own class rather than every SVG path in the
   overlay pane, which would also claim the (non-interactive) map shapes. */
.route-line { cursor: pointer; }

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  /* iOS Safari "text autosizing" inflates the font of blocks it decides are
     too narrow to read — which is why the map attribution kept jumping to a
     larger size on its own, seemingly at random (it re-runs on rotation and on
     layout changes). 100% pins text to its authored size while LEAVING pinch
     zoom working; `none` would disable the latter and is an accessibility
     regression. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  font-family: var(--font-body);
  color: var(--color-text);
  background: var(--color-background);
  -webkit-font-smoothing: antialiased;
  /* The off-screen (hidden) panel sits just past the right edge; without this
     it would add a horizontal scrollbar / make the page wider than the screen. */
  overflow: hidden;
}

/* Full-screen overlay app: fixed positioning keeps the off-screen panel from
   ever expanding the page (no stray horizontal scroll). */
#map {
  position: fixed;
  inset: 0;
  z-index: 1;
}

/* Use emoji-capable fonts everywhere emojis appear */
.brand-mark, .hero-emoji, .marker-pill, .attr-emoji, .choice .emoji,
.cluster-emoji { font-family: "Apple Color Emoji","Segoe UI Emoji","Noto Color Emoji",sans-serif; }

/* ----------------------------- Glass ------------------------------- */
.glass {
  background: var(--color-glass);
  backdrop-filter: blur(18px) saturate(160%);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  border: 1px solid var(--color-glass-border);
}

/* ----------------------------- Top bar ----------------------------- */
.topbar {
  position: fixed;
  top: 16px;
  left: 16px;
  right: 16px;
  /* Above the search bar (also 1000, but later in the DOM) so the filter
     dropdowns, which live in the top bar, paint over it instead of behind. */
  z-index: 1001;
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 10px 16px;
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
}

.brand { display: flex; align-items: center; gap: 10px; flex: none; }
.brand-mark { font-size: 26px; }
.brand-logo {
  height: 32px;
  width: auto;
  max-width: 44px;
  border-radius: 8px;
  object-fit: contain;
  display: block;
  flex: none;
}
.brand h1 {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 24px;
  margin: 0;
  letter-spacing: -0.02em;
  /* Wordmark is lower-case by design. Done in CSS, not by rewriting
     BRAND_NAME, so the page title, the logo's alt text and anything else
     reading the configured name keep their proper capitalisation. */
  text-transform: lowercase;
  background: var(--color-brand-gradient);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.chips {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  flex: 1 1 auto;
  scrollbar-width: none;
  padding: 2px;
}
.chips::-webkit-scrollbar { display: none; }

.chip {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--color-border);
  background: var(--color-glass-control);
  color: var(--color-text-muted);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 13px;
  padding: 7px 12px 7px 10px;
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
  transition: transform .12s ease, box-shadow .12s ease, background .12s ease, color .12s ease;
  user-select: none;
}
.chip svg { display: block; flex: none; }
.chip:hover { transform: translateY(-1px); box-shadow: var(--shadow-sm); background: var(--color-primary-subtle); }
.chip.active {
  /* Longhands, not the `background` shorthand: `background: var(--x)` becomes a
     pending-substitution value that the engine can drop, which clears the old
     gradient but lets the colour fall through to `.chip`. */
  background-color: var(--color-control-on);
  background-image: none;
  color: var(--color-on-control-on);
  border-color: transparent;
  box-shadow: var(--shadow-sm);
}
/* The icon has to be recoloured with the label: it takes currentColor for its
   stroke and some glyphs fill with it, so recolouring only the background
   would leave a dark glyph on the dark fill. */
.chip.active svg { stroke: var(--color-on-control-on); }
.chip.active svg [fill="currentColor"] { fill: var(--color-on-control-on); }


/* Language toggle (EN / ES) */
.lang-toggle {
  flex: none;
  display: flex;
  gap: 2px;
  background: var(--color-surface-sunken);
  border-radius: 999px;
  padding: 3px;
}
.lang-toggle button {
  border: none;
  background: transparent;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 12px;
  color: var(--color-text-muted);
  padding: 5px 9px;
  border-radius: 999px;
  cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.lang-toggle button.active {
  background: var(--color-control-on);
  color: var(--color-on-control-on);
  box-shadow: var(--shadow-sm);
}

/* Popular / All scope toggle (same segmented style as the language switch) */
.scope-toggle {
  flex: none;
  display: flex;
  gap: 2px;
  background: var(--color-surface-sunken);
  border-radius: 999px;
  padding: 3px;
}
.scope-toggle button {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: none;
  background: transparent;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 12px;
  color: var(--color-text-muted);
  padding: 6px 12px;
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
  transition: background .12s ease, color .12s ease;
}
.scope-toggle button svg { display: block; flex: none; }
.scope-toggle button.active {
  background: var(--color-control-on);
  color: var(--color-on-control-on);
  box-shadow: var(--shadow-sm);
}

/* --------------------------- Map markers --------------------------- */
/* Solid teardrop pin, coloured per category group (--pin), with a white
   Lucide icon. The pill rotates 45° for the tail; the icon counter-rotates. */
.marker-pill {
  display: grid;
  place-items: center;
  width: 34px; height: 34px;
  background: var(--pin, var(--color-primary));
  border-radius: 50% 50% 50% 4px;
  box-shadow: var(--shadow-md);
  border: 2px solid var(--pin, var(--color-marker-ring));
  transform: rotate(45deg);
  transition: transform .12s ease, box-shadow .12s ease;
}
.marker-pill > svg { transform: rotate(-45deg); display: block; }
/* Event pins carry no shadow: they often sit exactly on top of a POI pin,
   and two stacked shadows read as a dark smudge on the map. */
.marker-pill.event-pill { box-shadow: none; }
.marker-pill:hover { transform: rotate(45deg) scale(1.12); z-index: 1000; }
.marker-pill.selected {
  box-shadow: 0 0 0 4px var(--color-marker-halo), var(--shadow-md);
}

/* Cluster bubble restyle */
.marker-cluster-custom {
  background: var(--color-cluster);
  color: var(--color-on-cluster);
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-family: var(--font-body);
  font-weight: 700;
  box-shadow: var(--shadow-md);
  border: 3px solid var(--color-marker-ring);
}
.marker-cluster-custom span { line-height: 1; }
/* --heat (0–1) is set inline by app.js from the best rank in the cluster,
   relative to the best in view; the colours themselves stay here. */
.marker-cluster-custom.heat {
  background: color-mix(in oklab,
    var(--color-cluster-hot) calc(var(--heat, 0) * 100%),
    var(--color-cluster-quiet));
}
/* The count flips to light once the fill is dark enough (t ≈ 0.78, where the
   two contrast curves cross). Any ramp from near-white to dark passes through
   mid-tones where neither black nor white reaches 4.5:1, so the numeral also
   carries a halo in the opposite tone — the usual map-label trick — which
   keeps it crisp at every step of the gradient. */
.marker-cluster-custom.heat.hot { color: var(--color-on-inverse); }
.marker-cluster-custom.heat span { text-shadow: 0 1px 2px rgb(255 255 255 / .6); }
.marker-cluster-custom.heat.hot span { text-shadow: 0 1px 2px rgb(0 0 0 / .5); }

/* ----------------------------- Search ------------------------------ */
.searchbar {
  position: fixed;
  top: 84px;
  left: 16px;
  z-index: 1000;
  width: min(360px, calc(100vw - 32px));
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
}
.search-ico { flex: none; display: grid; place-items: center; color: var(--color-text-muted); }
.search-ico svg { display: block; }
#search-input {
  flex: 1 1 auto;
  border: none;
  background: transparent;
  outline: none;
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 500;
  color: var(--color-text);
  min-width: 0;
}
#search-input::placeholder { color: var(--color-text-muted); opacity: .7; }

.locate-btn {
  flex: none;
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border: none;
  border-radius: 10px;
  background: var(--color-surface-sunken);
  color: var(--color-primary-strong);
  cursor: pointer;
  transition: background .12s ease, transform .12s ease;
}
.locate-btn:hover { background: var(--color-primary-subtle); }
.locate-btn:active { transform: scale(.94); }
.locate-btn.loading svg { animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

/* "You are here" marker */
.user-dot {
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--color-location);
  border: 3px solid var(--color-marker-ring);
  box-shadow: 0 0 0 4px var(--color-location-glow), var(--shadow-sm);
}
/* ?tune: brand mark as the "you are here" pin. The pale disc keeps a
   multi-colour logo readable over any basemap, and the primary-800 contour
   separates it from the map without competing with the logo itself. */
.user-mark {
  /* The soft ring around the marker. The shared --color-location-glow is a
     30% mix of the blue --color-location, which read as a stray blue halo
     against the warm palette; this is the same idea in primary-800.
     Scoped here so the plain .user-dot in the public view is untouched. */
  --mark-glow: color-mix(in srgb, var(--primary-800) 30%, transparent);
  position: relative;            /* anchors the sweep ring below */
  width: 34px; height: 34px;
  border-radius: 50%;
  display: grid; place-items: center;
  background: var(--color-marker-ring);
  border: 2px solid var(--primary-800);
  box-shadow: 0 0 0 4px var(--mark-glow), var(--shadow-sm);
}
.user-mark img { width: 22px; height: 22px; display: block; }

/* Radar sweep: a ring that grows out of the marker and fades, on a loop, so
   "you are here" reads as live rather than as one more pin. Drawn on a
   pseudo-element so it can overflow the marker without changing its size, and
   pointer-events:none so it never eats a tap meant for the map. */
.user-mark::after {
  content: "";
  position: absolute;
  inset: -2px;                   /* line up with the border, not the padding box */
  border-radius: 50%;
  border: 2px solid var(--primary-800);
  pointer-events: none;
  animation: user-sweep 2.6s cubic-bezier(.2, .6, .3, 1) infinite;
}
@keyframes user-sweep {
  0%   { transform: scale(1);   opacity: .5; }
  70%  { opacity: 0; }
  100% { transform: scale(2.8); opacity: 0; }
}
/* An infinite animation is exactly what this setting is for. */
@media (prefers-reduced-motion: reduce) {
  .user-mark::after { animation: none; opacity: 0; }
}
/* The logo failed to load — degrade to a plain dot rather than an empty ring,
   in the same colour so the fallback still reads as the same marker. */
.user-mark.fallback {
  width: 18px; height: 18px;
  background: var(--primary-800);
  border: 2px solid var(--color-marker-ring);
}

.search-results {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  right: 0;
  background: var(--color-surface);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  border: 1px solid var(--color-border);
  overflow: hidden;
  display: none;
  max-height: 320px;
  overflow-y: auto;
}
.search-results.show { display: block; animation: cardIn .2s ease both; }
.sr-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  border: none;
  background: transparent;
  padding: 11px 14px;
  text-align: left;
  cursor: pointer;
  font-family: var(--font-body);
  font-size: 14px;
  color: var(--color-text);
  border-bottom: 1px solid var(--color-border);
  transition: background .1s ease;
}
.sr-item:last-child { border-bottom: none; }
.sr-item:hover, .sr-item.active { background: var(--color-primary-subtle); }
.sr-emoji { flex: none; display: grid; place-items: center; }
.sr-emoji svg { display: block; }
.sr-main { line-height: 1.25; }
.sr-empty { padding: 14px; color: var(--color-text-muted); font-size: 13px; font-style: italic; }

/* --------------------- Route narration card ------------------------ */
.narr-text p {
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--color-text);
  margin: 0 0 10px;
}
.narr-text p:last-child { margin-bottom: 0; }
/* Attribution + the AI disclosure are licence and transparency obligations,
   so they are small but never hidden, clipped or truncated. */
.narr-credit {
  font-size: 10px;
  line-height: 1.4;
  color: var(--color-text-muted);
  margin: 10px 0 0;
}

/* ----------------------- Wikipedia card ---------------------------- */
/* Lead photo. `aspect-ratio` comes from the img's own width/height attributes,
   so the card reserves the right space before the image arrives and the text
   below it never jumps. */
.wiki-figure { margin: 0 0 10px; }
.wiki-photo {
  display: block;
  width: 100%; height: auto;
  border-radius: 10px;
  background: var(--color-surface-alt);
}
/* Attribution is a licence condition, not decoration — small, but never
   hidden, clipped or truncated. */
.wiki-photo-credit {
  font-size: 10px;
  line-height: 1.4;
  color: var(--color-text-muted);
  margin-top: 5px;
}
.wiki-photo-credit a { color: inherit; text-decoration: underline; }
.wiki-extract {
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--color-text);
  margin: 0 0 10px;
  /* clamp long summaries */
  display: -webkit-box;
  -webkit-line-clamp: 5;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.wiki-link {
  display: inline-block;
  font-weight: 700;
  font-size: 13px;
  color: var(--color-primary-strong);
  text-decoration: none;
}
.wiki-link:hover { text-decoration: underline; }
.wiki-credit { font-size: 10px; color: var(--color-text-muted); margin: 8px 0 0; }

/* ------------------------ Weather chip (panel) --------------------- */
.weather-chip {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border-radius: var(--radius-sm);
  background: var(--color-accent-subtle);
  border: 1px solid var(--color-border);
  font-size: 14px;
  animation: cardIn .35s ease both;
}
.weather-chip .wc-emoji { display: grid; place-items: center; flex: none; }
.weather-chip .wc-emoji svg { display: block; }
.weather-chip .wc-text { flex: 1 1 auto; }
.weather-chip .wc-hi { font-size: 12px; font-weight: 700; color: var(--color-text-muted); }
.weather-chip .wc-credit {
  font-size: 9px; font-weight: 700; letter-spacing: .03em;
  color: var(--color-text-muted); text-decoration: none;
  background: var(--color-surface-sunken); padding: 2px 6px; border-radius: 999px;
}
.weather-chip .wc-credit:hover { color: var(--color-accent-strong); }
.weather-chip.loading { opacity: .7; }

/* ----------------------------- Panel ------------------------------- */
.panel {
  position: fixed;
  top: 0; right: 0;
  height: 100%;
  width: var(--panel-w);
  max-width: 92vw;
  z-index: 1100;
  background: var(--color-glass-strong);
  backdrop-filter: blur(22px) saturate(160%);
  -webkit-backdrop-filter: blur(22px) saturate(160%);
  border-left: 1px solid var(--color-glass-border);
  box-shadow: var(--shadow-lg);
  transform: translateX(105%);
  transition: transform .42s cubic-bezier(.22,1,.36,1);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.panel.open { transform: translateX(0); }

.panel-close {
  position: absolute;
  top: 14px; right: 14px;
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border: none;
  border-radius: 50%;
  background: var(--color-on-primary-surface);
  color: var(--hero-ink, var(--color-on-primary));
  cursor: pointer;
  z-index: 2;
  transition: background .12s ease, transform .12s ease;
}
.panel-close svg, .fav-btn svg { display: block; }
.panel-close:hover { background: var(--color-on-primary-surface-hover); transform: rotate(90deg); }

/* Favourite (heart) + "want to go" toggles. They sit at the BOTTOM right of
   the hero: at the top they shared the title's line and a long POI name ran
   straight under them. The title now only has to clear the close button. */
.fav-btn, .want-btn {
  position: absolute;
  top: auto; bottom: 14px; right: 14px;
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border: none;
  border-radius: 50%;
  background: var(--color-on-primary-surface);
  color: var(--hero-ink, var(--color-on-primary));
  line-height: 1;
  cursor: pointer;
  z-index: 2;
  transition: background .12s ease, transform .12s ease, color .12s ease;
}
/* "Quiero ir" sits to the LEFT of the heart. */
.want-btn { right: 52px; }
.fav-btn:hover, .want-btn:hover { background: var(--color-on-primary-surface-hover); transform: scale(1.1); }
.fav-btn.active svg { fill: var(--color-on-primary); }   /* filled heart = saved */
.want-btn.active svg { fill: var(--color-on-primary); }
.fav-btn svg, .want-btn svg { display: block; }

/* Favorites list rows */
.fav-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin-bottom: 8px;
  cursor: pointer;
  font-family: var(--font-body);
  color: var(--color-text);
  transition: background .12s ease, transform .1s ease;
}
.fav-row:hover { background: var(--color-primary-subtle); transform: translateY(-1px); }

/* Favourites tabs (Places / Routes) */
.fav-tabs { display: flex; gap: 6px; margin-bottom: 12px; }
.fav-tabs button {
  flex: 1 1 0; border: 1px solid var(--color-border); background: var(--color-surface);
  color: var(--color-text-muted); font-family: var(--font-body); font-weight: 700; font-size: 13px;
  padding: 8px 10px; border-radius: 999px; cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.fav-tabs button.active { background: var(--color-control-on); color: var(--color-on-control-on); border-color: transparent; }

/* Saved-route row: a big recall button + a small delete button */
.favroute-row { padding: 0; border: none; background: none; gap: 0; }
.favroute-row .favroute-main {
  flex: 1 1 auto; display: flex; align-items: center; gap: 10px; min-width: 0;
  text-align: left; border: 1px solid var(--color-border); background: var(--color-surface);
  border-radius: var(--radius-sm) 0 0 var(--radius-sm); border-right: none;
  padding: 12px 14px; cursor: pointer; font-family: var(--font-body); color: var(--color-text);
}
.favroute-row .favroute-main:hover { background: var(--color-primary-subtle); }
.favroute-row .favroute-del {
  flex: none; border: 1px solid var(--color-border); background: var(--color-surface);
  color: var(--color-text-muted); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  padding: 0 12px; cursor: pointer;
}
.favroute-row .favroute-del:hover { background: var(--color-danger); color: #fff; border-color: transparent; }

/* Route panel action bar (Save + Clear) */
.route-actions { display: flex; gap: 8px; margin-bottom: 6px; }
.route-actions .choice { flex: 1 1 0; justify-content: center; }
.route-save { color: var(--color-primary-strong); }

.fav-emoji { flex: none; display: grid; place-items: center; }
.fav-emoji svg { display: block; }
.fav-name { font-weight: 600; font-size: 14px; flex: 1 1 auto; }
.fav-cat { font-size: 12px; color: var(--color-text-muted); flex: none; }

/* Active state for the searchbar favorites button */
#fav-list-btn.active { color: var(--color-primary); }
#fav-list-btn.active svg { fill: var(--color-primary); }

.panel-hero {
  position: relative;
  padding: 30px 26px 26px;
  /* Tinted per POI category by applyHeroTint(); falls back to the brand
     fill for the favourites list. */
  background: var(--hero-bg, var(--color-primary-gradient));
  color: var(--hero-ink, var(--color-on-primary));
  display: flex;
  align-items: center;
  gap: 16px;
  flex: none;
}
.hero-emoji {
  width: 72px; height: 72px;
  display: grid; place-items: center;
  background: var(--color-on-primary-tile);
  border-radius: 22px;
  box-shadow: var(--shadow-sm);
  flex: none;
}
.hero-emoji svg { display: block; }
/* Close + favourite buttons sit over the hero, so they take the same ink
   the tint chose (--hero-ink is set on .panel and inherits down). */
/* Clears the close button and, on its lower lines, the two hero buttons below
   it — the bookmark sits further left than the heart, so the reservation is
   sized to the outer one. Without it a long POI name runs underneath them. */
.hero-text { min-width: 0; padding-right: 62px; }
.hero-text h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 24px;
  line-height: 1.15;
  margin: 0 0 4px;
  letter-spacing: -0.01em;
  word-wrap: break-word;
}
/* Localised name under the title (e.g. the English name of a Korean or
   Arabic place). Smaller and lighter than the title, but clearly readable. */
.hero-alt {
  margin: 0 0 4px;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.2;
  opacity: .82;
  word-wrap: break-word;
}
.hero-alt[hidden] { display: none; }
.hero-sub {
  margin: 0;
  font-size: 13px;
  font-weight: 500;
  opacity: .9;
  text-transform: capitalize;
}

.panel-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 18px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* ------------------------- Attribute card -------------------------- */
.attr-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 16px;
  box-shadow: var(--shadow-sm);
  animation: cardIn .35s ease both;
}
@keyframes cardIn {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

.attr-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
}
.attr-emoji { display: grid; place-items: center; flex: none; }
.attr-emoji svg { display: block; }
.attr-emoji .emoji-fallback { font-size: 20px; }
.attr-label {
  font-weight: 700;
  font-size: 15px;
  flex: 1 1 auto;
}
.attr-current {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  font-weight: 700;
  color: var(--color-accent-strong);
  background: var(--color-accent-subtle);
  padding: 4px 10px;
  border-radius: 999px;
}
.attr-current svg { display: block; flex: none; }
.listed-hint svg { vertical-align: -3px; }
.attr-current.empty { color: var(--color-text-subtle); background: var(--color-surface-sunken); }
.attr-fresh { margin: 8px 2px 0; font-size: 11px; color: var(--color-text-subtle); font-variant-numeric: tabular-nums; }

/* one-click choice buttons */
/* Equal-width vote buttons on a grid: every choice in a card gets the same
   footprint and they wrap in tidy rows, so cards with 2, 3 or 4 options all
   read as one consistent control instead of ragged pills. */
.choices {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
  gap: 8px;
}
.choice {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 38px;
  text-align: center;
  border: 1.5px solid var(--color-border);
  background: var(--color-surface);
  border-radius: 999px;
  padding: 7px 12px;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: transform .1s ease, border-color .12s ease, background .12s ease, color .12s ease;
  position: relative;
}
.choice .emoji { font-size: 15px; }
.choice > svg { display: block; flex: none; }
.choice .count {
  margin-left: auto;
  font-size: 11px;
  font-weight: 700;
  background: var(--color-surface-sunken);
  border-radius: 999px;
  padding: 1px 6px;
  min-width: 18px;
  text-align: center;
}
.choice:hover { transform: translateY(-1px); border-color: var(--color-primary); background: var(--color-primary-subtle); color: var(--color-text); }
.choice:active { transform: scale(.96); }
.choice.mine {
  background: var(--color-primary-gradient);
  color: var(--color-on-primary);
  border-color: transparent;
  box-shadow: var(--shadow-sm);
}
.choice.mine .count { background: var(--color-on-primary-pill); color: var(--color-on-primary); }

/* burst animation on vote */
.choice.burst::after {
  content: "";
  position: absolute;
  inset: -3px;
  border-radius: 999px;
  border: 2px solid var(--color-primary);
  animation: burst .5s ease-out forwards;
}
@keyframes burst {
  from { opacity: .7; transform: scale(1); }
  to   { opacity: 0; transform: scale(1.5); }
}

/* opening-hours mini table */
.hours-table {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 3px 12px;
  font-size: 13px;
  margin-bottom: 12px;
}
.hours-table .day { color: var(--color-text-muted); font-weight: 600; }
.hours-table .day.today {
  color: var(--color-primary-strong);
}
.hours-table .val { font-weight: 600; text-align: right; }
.hours-table .val.closed { color: var(--color-text-muted); font-weight: 500; }
.open-now {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; font-weight: 700;
  padding: 4px 10px; border-radius: 999px;
  margin-bottom: 10px;
}
.open-now.yes { background: var(--color-success-subtle); color: var(--color-success); }
.open-now.no  { background: var(--color-danger-subtle); color: var(--color-danger); }
.open-now.unknown { background: var(--color-surface-sunken); color: var(--color-text-muted); }

.no-data {
  font-size: 13px;
  color: var(--color-text-muted);
  font-style: italic;
  margin: 0 0 10px;
}

.panel-foot {
  flex: none;
  padding: 12px 18px;
  font-size: 11px;
  color: var(--color-text-muted);
  border-top: 1px solid var(--color-border);
  background: var(--color-glass-soft);
}
.panel-foot a { color: var(--color-accent-strong); }

/* skeletons */
.skeleton {
  background: linear-gradient(90deg, var(--color-surface-sunken) 25%, var(--color-border) 37%, var(--color-surface-sunken) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.3s ease infinite;
  border-radius: 8px;
}
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }
.skeleton.line { height: 12px; margin: 6px 0; }
.skeleton.pill { height: 32px; width: 90px; border-radius: 999px; display:inline-block; margin-right: 8px; }

/* ----------------------------- Toast ------------------------------- */
.toast {
  position: fixed;
  left: 50%;
  bottom: 28px;
  transform: translate(-50%, 20px);
  z-index: 1200;
  background: var(--color-surface-inverse);
  color: var(--color-on-inverse);
  font-weight: 600;
  font-size: 13px;
  padding: 11px 18px;
  border-radius: 999px;
  box-shadow: var(--shadow-md);
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s ease, transform .25s ease;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* --------------------------- Responsive ---------------------------- */
@media (max-width: 600px) {
  .topbar { flex-wrap: wrap; gap: 10px; top: 10px; left: 10px; right: 10px; padding: 10px 12px; }
  .brand h1 { font-size: 20px; }
  .chips { order: 3; width: 100%; }

  /* Search docked just under the top bar (exact top set by layoutSearchbar in
     app.js) so its suggestions dropdown opens into view. */
  .searchbar { top: 120px; bottom: auto; left: 10px; right: 10px; width: auto; }

  /* Pinch-to-zoom is enough on touch — reclaim the corner. */
  .leaflet-control-zoom { display: none; }

  /* Panel becomes a bottom sheet: map stays visible above it. */
  .panel {
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    max-width: 100%;
    height: 78vh;
    border-left: none;
    border-radius: 24px 24px 0 0;
    transform: translateY(105%);
  }
  .panel.open { transform: translateY(0); }
  /* Grab-handle hint at the top of the sheet. */
  .panel::before {
    content: "";
    position: absolute;
    top: 9px; left: 50%;
    transform: translateX(-50%);
    width: 42px; height: 5px;
    border-radius: 999px;
    background: var(--color-glass);
    z-index: 3;
  }
  .panel-hero { padding-top: 26px; }
}

/* Leaflet attribution: keep it readable on the sand basemap */
.leaflet-control-attribution {
  font-family: var(--font-body);
  font-size: 11px !important;
  background: var(--color-glass-strong) !important;
}

/* Zoom control (bottom-left) restyled to match the glass UI */
.leaflet-control-zoom {
  border: none !important;
  border-radius: 14px !important;
  overflow: hidden;
  box-shadow: var(--shadow-md) !important;
}
.leaflet-control-zoom a {
  width: 38px !important;
  height: 38px !important;
  line-height: 38px !important;
  font-size: 20px !important;
  color: var(--color-text) !important;
  background: var(--color-glass-strong) !important;
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--color-border) !important;
}
.leaflet-control-zoom a:hover { background: var(--color-surface) !important; color: var(--color-primary-strong) !important; }

/* Base-layer (map / satellite) toggle — a Leaflet control, bottom-left */
.layer-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: none;
  border-radius: 12px;
  padding: 8px 12px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 13px;
  color: var(--color-text);
  background: var(--color-glass-strong);
  box-shadow: var(--shadow-md);
  cursor: pointer;
  transition: transform .12s ease, background .12s ease;
}
.layer-btn:hover { background: var(--color-surface); transform: translateY(-1px); }

/* Events toggle — same control as the base-layer button, tinted with the
   event pin colour when the layer is on. */
.events-btn.active { background: var(--color-pin-event); color: var(--color-on-pin); }
.events-btn:disabled { opacity: .5; cursor: not-allowed; }
.events-btn.active:hover { background: var(--color-pin-event); }

/* Event panel body */
.event-when {
  margin: 0 0 6px;
  font-size: 15px;
  font-weight: 700;
  color: var(--color-text);
}
.event-desc {
  margin: 8px 0 10px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--color-text);
}
.layer-btn:active { transform: scale(.97); }

/* ===================== View toggle + list view ===================== */
/* Map ⇄ list toggle, a glass icon button in the top bar. */
.icon-btn {
  flex: none;
  width: 36px; height: 36px;
  display: grid; place-items: center;
  border: 1px solid var(--color-border);
  border-radius: 999px;
  background: var(--color-glass-control);
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background .12s ease, color .12s ease, transform .1s ease;
}
.icon-btn svg { display: block; }
.icon-btn:hover { background: var(--color-primary-subtle); }
.icon-btn.active { background: var(--color-control-on); color: var(--color-on-control-on); border-color: transparent; }

/* Event-type chips reuse .chip but flip to the event colour when active. */
.event-chip.active { background: var(--color-pin-event); color: var(--color-on-pin); border-color: transparent; }
.event-chip.active svg { stroke: var(--color-on-pin); }

/* Full overlay covering the map; top is set from the top bar's height in JS
   so it always clears the (wrapping) bar. */
.list-view {
  position: fixed;
  left: 0; right: 0; bottom: 0; top: 96px;
  z-index: 900;
  background: var(--color-background);
  display: none;
  flex-direction: column;
}
.list-view.open { display: flex; }

.list-head {
  flex: none;
  padding: 10px 14px;
  border-bottom: 1px solid var(--color-border);
  background: var(--color-surface);
}
.list-head .chips { padding: 0; }

.list-body {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: 8px 12px 24px;
  max-width: 760px;
  width: 100%;
  margin: 0 auto;
}
.list-empty { text-align: center; padding: 40px 16px; }

.list-row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  text-align: left;
  border: none;
  border-bottom: 1px solid var(--color-border);
  background: transparent;
  padding: 12px 8px;
  cursor: pointer;
  font-family: var(--font-body);
  color: var(--color-text);
  transition: background .1s ease;
}
.list-row:hover { background: var(--color-primary-subtle); }
.list-ico { flex: none; display: grid; place-items: center; width: 30px; }
.list-ico svg { display: block; }
.list-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.list-name {
  font-weight: 600; font-size: 14.5px; line-height: 1.25;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.list-sub {
  font-size: 12.5px; color: var(--color-text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.list-dist {
  flex: none; font-size: 12px; font-weight: 700;
  color: var(--color-text-subtle);
  font-variant-numeric: tabular-nums;
}

/* ============= Fixed map-control stack (bottom-left) ============== */
/* Order top→bottom: list/map toggle, events, satellite. Floats above the
   list overlay (z-index) so the first two stay usable while browsing the
   list; the satellite toggle is hidden there because it is map-only. */
.map-controls {
  position: fixed;
  left: 16px;
  bottom: 108px;            /* clears the Leaflet zoom control on desktop */
  z-index: 1000;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}
@media (max-width: 600px) {
  .map-controls { bottom: 16px; }   /* zoom control is hidden on mobile */
}
/* In list view: hide the city search (a map action). The whole control
   stack (list/map, events, satellite) stays put so buttons never shift
   between views — the satellite toggle simply takes effect on the map. */
body.list-open .searchbar { display: none; }

/* ================= Filter dropdowns (Places / Events) ============== */
.filter-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 auto;
  min-width: 0;
  overflow: visible;
}
.filter-group { position: relative; }

.filter-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--color-border);
  background: var(--color-glass-control);
  color: var(--color-text-muted);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 13px;
  padding: 8px 11px;
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.filter-btn svg { display: block; flex: none; }
.filter-btn:hover { background: var(--color-primary-subtle); }
.filter-btn[aria-expanded="true"] { border-color: var(--color-primary); background: var(--color-primary-subtle); }
.filter-btn.has-filters { background: var(--color-control-on); color: var(--color-on-control-on); border-color: transparent; }

.filter-count {
  min-width: 18px; height: 18px; padding: 0 5px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px;
  background: var(--color-primary); color: var(--color-on-primary);
  font-size: 11px; font-weight: 800; font-variant-numeric: tabular-nums;
}
.filter-btn.has-filters .filter-count { background: var(--color-on-primary); color: var(--color-primary); }

.filter-pop {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 50;
  width: min(340px, 86vw);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  padding: 12px;
}
/* Chips inside a popover: an equal-width grid (like the POI feedback
   buttons) so every chip is the same size — cleaner than ragged wrapping. */
.filter-pop .chips {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 8px;
  max-height: 56vh;
  overflow-y: auto;
  padding: 2px;
}
.filter-pop .chip {
  display: flex;
  width: 100%;
  min-height: 38px;
  justify-content: flex-start;
}
/* Long category / event labels truncate rather than stretch a chip. */
.filter-pop .chip span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Popular/All scope toggle hidden for now — the map defaults to the adaptive
   "popular" view. Restore by removing this rule (markup + logic still exist). */
#scope-toggle { display: none; }

/* Satellite toggle hidden: the free aerial imagery is too low-resolution to be
   worth the button. Restore by removing this rule (layer + logic still exist). */
#layer-btn { display: none; }

/* ===== Popularity signals card (dev-only, ?tune) ===== */
.signals-card {
  border: 1px dashed var(--color-border); border-radius: 12px;
  padding: 10px 12px; margin-bottom: 6px; background: var(--color-surface-sunken);
}
.signals-card .signals-rank { font-weight: 800; font-size: 13px; color: var(--color-primary); }
.signals-card .signals-row {
  display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
  font-size: 12.5px; font-weight: 600; color: var(--color-text); margin-top: 3px;
}
.signals-card .sig-sep { color: var(--color-text-subtle); }
.signals-card .signals-attr { font-size: 10.5px; color: var(--color-text-subtle); margin-top: 5px; }

/* ===== Popularity tuner (dev-only, ?tune) ===== */
#rank-tuner {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: 18px; z-index: 1500; width: min(320px, calc(100vw - 32px));
  background: var(--color-surface); color: var(--color-text);
  border: 1px solid var(--color-border); border-radius: 14px;
  box-shadow: var(--shadow-lg); padding: 12px 14px;
  font-family: var(--font-body);
}
#rank-tuner .rt-modes { display: flex; gap: 4px; margin-bottom: 10px; }
#rank-tuner .rt-modes button {
  flex: 1 1 0; border: 1px solid var(--color-border); background: var(--color-surface);
  color: var(--color-text-muted); font-family: var(--font-body); font-weight: 700; font-size: 11px;
  padding: 6px 4px; border-radius: 999px; cursor: pointer; white-space: nowrap;
}
#rank-tuner .rt-modes button.active { background: var(--color-control-on); color: var(--color-on-control-on); border-color: transparent; }
#rank-tuner .rt-floorrow {
  display: flex; align-items: center; gap: 8px; margin-top: 8px;
  font-size: 11px; font-weight: 700; color: var(--color-text-muted);
}
#rank-tuner .rt-floorrow input[type="range"] { flex: 1 1 auto; accent-color: var(--color-accent); }
#rank-tuner .rt-floorrow #rt-floorval { min-width: 26px; text-align: right; font-variant-numeric: tabular-nums; color: var(--color-text); }
#rank-tuner .rt-row { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
#rank-tuner .rt-title { font-size: 13px; font-weight: 600; }
#rank-tuner .rt-title strong { color: var(--color-primary); font-variant-numeric: tabular-nums; }
#rank-tuner .rt-count { font-size: 12px; font-weight: 700; color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
#rank-tuner input[type="range"] { width: 100%; margin: 10px 0 4px; accent-color: var(--color-primary); }
#rank-tuner .rt-scale { display: flex; justify-content: space-between; font-size: 10px; color: var(--color-text-subtle); }

/* ======================= Route planner ============================ */
.route-btn svg { color: var(--color-route); }

/* Questionnaire modal — centred card on desktop, bottom sheet on mobile */
.route-modal {
  position: fixed; inset: 0; z-index: 1200;
  background: rgb(var(--shadow-color) / .38);
  display: grid; place-items: center;
  padding: 16px;
}
.route-modal[hidden] { display: none; }
.route-card {
  position: relative;
  width: min(420px, 100%);
  max-height: 88vh; overflow-y: auto;
  background: var(--color-surface);
  backdrop-filter: none; -webkit-backdrop-filter: none;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-lg);
  padding: 24px 20px 20px;
  display: flex; flex-direction: column; gap: 18px;
}
/* A flex column whose content exceeds max-height SHRINKS its items by default,
   so a long form quietly squashed everything — the 34px close button was being
   rendered 22px tall. The card is a scroll container; let it scroll instead. */
.route-card > * { flex: none; }
.route-card h2 {
  font-family: var(--font-display); font-weight: 700; font-size: 20px;
  margin: 0; padding-right: 34px; letter-spacing: -.01em;
}
.route-close {
  position: absolute; top: 12px; right: 12px;
  background: var(--color-surface-sunken); color: var(--color-text-muted);
}
.route-close:hover { background: var(--color-border); }
.route-q { display: flex; flex-direction: column; gap: 8px; }
.route-q label {
  font-size: 12px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--color-text-muted);
}
/* "Start at an exact place" typeahead. The box is the positioning context for
   the shared .search-results dropdown, which is absolutely positioned. */
.route-place-box { position: relative; }
.route-place-box input {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 14px;
}
.route-place-box input:focus {
  outline: none;
  border-color: var(--color-primary-strong);
}
/* The dropdown must sit above the segments that follow it in the modal. */
.route-place-box .search-results { z-index: 5; }

.seg { display: flex; gap: 6px; flex-wrap: wrap; }
.seg-btn {
  flex: 1 1 0; min-width: 64px;
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text-muted);
  font-family: var(--font-body); font-weight: 700; font-size: 13px;
  padding: 9px 10px; border-radius: 999px; cursor: pointer;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.seg-btn:hover { background: var(--color-primary-subtle); }
/* The route modal's own controls take the ROUTE colour rather than the generic
   selected fill: blue means "route" everywhere else in the app (the line, the
   stop numbers), so the planner's chips should agree with it. */
.seg-btn.active { background: var(--color-route); color: var(--color-on-route); border-color: transparent; }
.seg-btn.active svg { stroke: var(--color-on-route); }
.seg-btn.active svg [fill="currentColor"] { fill: var(--color-on-route); }
.seg-btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
.seg-btn svg { flex: none; }

/* Time-window: whole-day toggle beside the label */
.route-q-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.route-q .wholeday {
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  font-size: 12px; font-weight: 700; letter-spacing: 0; text-transform: none;
  color: var(--color-text-muted);
}
.route-q .wholeday input { accent-color: var(--color-route); width: 15px; height: 15px; cursor: pointer; }
#tr-wrap { display: flex; flex-direction: column; gap: 8px; }
#tr-wrap.disabled { opacity: .4; pointer-events: none; }

/* Dual-thumb range: two overlaid inputs over a rail + coloured fill */
.time-range { position: relative; height: 26px; }
.tr-rail, .tr-fill {
  position: absolute; top: 50%; transform: translateY(-50%);
  height: 5px; border-radius: 999px;
}
.tr-rail { left: 0; right: 0; background: var(--color-border); }
.tr-fill { background: var(--color-route); }
.time-range input[type="range"] {
  position: absolute; top: 0; left: 0; width: 100%; height: 26px; margin: 0;
  -webkit-appearance: none; appearance: none;
  background: none; pointer-events: none;
}
.time-range input[type="range"]::-webkit-slider-runnable-track { background: none; border: none; }
.time-range input[type="range"]::-moz-range-track { background: none; border: none; }
.time-range input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; pointer-events: auto;
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--color-surface); border: 3px solid var(--color-route);
  box-shadow: var(--shadow-sm); cursor: grab; margin-top: -6.5px;
}
.time-range input[type="range"]::-moz-range-thumb {
  pointer-events: auto; width: 18px; height: 18px; border-radius: 50%;
  background: var(--color-surface); border: 3px solid var(--color-route);
  box-shadow: var(--shadow-sm); cursor: grab;
}
.tr-readout {
  display: flex; justify-content: space-between;
  font-size: 13px; font-weight: 700; color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}
.route-generate {
  margin-top: 4px;
  border: none; border-radius: 999px;
  background: var(--color-route); color: var(--color-on-inverse);
  font-family: var(--font-body); font-weight: 700; font-size: 15px;
  padding: 13px; cursor: pointer;
  transition: filter .12s ease, transform .1s ease;
}
.route-generate:hover { filter: brightness(1.06); }
.route-generate:active { transform: scale(.98); }

/* Map: numbered stop badges, start marker, dashed line */
.route-stop {
  width: 28px; height: 28px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--color-route); color: #fff;
  font-family: var(--font-body); font-weight: 800; font-size: 13px;
  border: 2px solid #fff; box-shadow: var(--shadow-md);
}
.route-start {
  width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--color-text); border: 2px solid #fff;
  box-shadow: var(--shadow-md);
}
.route-start svg { display: block; }

/* Itinerary rows in the panel */
.route-clear {
  width: 100%; justify-content: center; margin-bottom: 6px;
  color: var(--color-text-muted);
}
.route-row {
  display: flex; align-items: center; gap: 12px; width: 100%;
  text-align: left; border: none; border-bottom: 1px solid var(--color-border);
  background: transparent; padding: 12px 4px; cursor: pointer;
  font-family: var(--font-body); color: var(--color-text);
  transition: background .1s ease;
}
.route-row:hover { background: var(--color-primary-subtle); }
.route-num {
  flex: none; width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--color-route); color: #fff; font-weight: 800; font-size: 13px;
}
.route-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.route-name { font-weight: 600; font-size: 14.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.route-meta { font-size: 12px; color: var(--color-text-muted); display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* Category icon instead of its name — the same idea as the favourites rows.
   It reads faster and buys back the width the label was using. */
.route-ico { flex: none; display: grid; place-items: center; }
.route-ico svg { display: block; }
/* Visit time: hourglass + minutes, so it needs no label. */
.route-visit { display: inline-flex; align-items: center; gap: 3px; font-variant-numeric: tabular-nums; }
.route-visit svg { flex: none; }
.route-open { font-weight: 700; }
.route-open.yes { color: var(--color-success); }
.route-open.no { color: var(--color-danger); }

/* Native date picker for "Pick a date" */
.date-input {
  margin-top: 8px; width: 100%;
  border: 1px solid var(--color-border); border-radius: 10px;
  background: var(--color-surface); color: var(--color-text);
  font-family: var(--font-body); font-size: 14px; font-weight: 600;
  padding: 9px 12px; cursor: pointer;
}
.date-input[hidden] { display: none; }

/* Weather outlook chip inside the popup */
.route-weather {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 12px; border-radius: 12px;
  background: var(--color-accent-subtle); border: 1px solid var(--color-border);
  font-size: 13px; color: var(--color-text);
}
.route-weather[hidden] { display: none; }
.route-weather.muted { color: var(--color-text-muted); }
.route-weather .wc-emoji { display: inline-flex; }
.route-weather .wc-text { font-weight: 600; }
.route-weather .wx-advice {
  flex: 1 1 100%; font-size: 12.5px; font-weight: 700; color: var(--color-accent-strong);
}

/* Event stops: distinct tint + inline "when" tag in the itinerary */
.route-stop.ev, .route-num.ev { background: var(--color-pin-event, #B3261E); }
.route-ev {
  display: inline-flex; align-items: center; gap: 3px;
  font-weight: 700; color: var(--color-pin-event, #B3261E);
  font-variant-numeric: tabular-nums;
}
.route-ev svg { flex: none; }

@media (max-width: 600px) {
  .route-modal { place-items: end center; padding: 0; }
  .route-card {
    width: 100%;
    /* 92vh on iOS measures the LARGE viewport — the one with the URL bar
       hidden — so the sheet could end up taller than what is actually on
       screen. Anchored to the bottom, its top (and the close button with it)
       then sat above the visible area, with no backdrop left to tap either.
       dvh tracks the browser chrome; the vh line is the fallback for engines
       that don't know dvh. */
    max-height: 92vh;
    max-height: 92dvh;
    border-radius: var(--radius) var(--radius) 0 0;
  }
  /* The close button is absolutely positioned inside a scrolling card, so it
     scrolled away with the content — you could not reach it by sliding either.
     Sticky keeps it pinned to the top of the sheet at any scroll position.
     The negative margin cancels the row it would otherwise add to the flex
     column, so the title stays exactly where it was. */
  .route-close {
    position: sticky;
    top: 0;
    align-self: flex-end;
    margin-bottom: calc(-34px - 18px);   /* button height + .route-card gap */
  }
}
