/* ============================================================
   ROAD TRIP AI — Light Theme
   ============================================================ */

:root {
  --bg:         #faf7f2;
  --bg-2:       #f3ede4;
  --surface:    #ffffff;
  --border:     rgba(0,0,0,0.08);
  --border-2:   rgba(0,0,0,0.15);

  --accent:     #d05c25;
  --accent-2:   #e9a535;
  --accent-bg:  #fff5ed;
  --accent-glow:rgba(208, 92, 37, 0.18);

  --text-1: #1c1714;
  --text-2: #5c5650;
  --text-3: #a8a09a;

  --green:       #15803d;
  --green-bg:    #f0fdf4;

  /* What kind of place this is, wherever it appears — the itinerary, the
     suggestions, anywhere added later. Defined once so the two lists
     cannot drift apart again. */
  --food-bg:      #eff6fd;
  --food-border:  #c3ddf3;
  --food-accent:  #3b82c4;
  --food-text:    #1f4e79;
  /* The Guide's own red, used only for the stars and their chips. */
  --michelin:      #b8253c;
  --michelin-soft: #e3a9b3;
  --michelin-bg:   #fdf1f3;
  --hotel-bg:     #fdf4ec;
  --hotel-border: #f0ddc7;
  --trail-bg:     #f1f8f2;
  --trail-border: #c3e0c9;
  --trail-accent: #15803d;
  --trail-text:   #14532d;
  --green-border:#bbf7d0;
  --red:         #c93030;
  --red-bg:      #fff2f2;
  --red-border:  #fecaca;

  --shadow-sm: 0 1px 4px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04);
  --shadow-md: 0 4px 18px rgba(0,0,0,0.08), 0 2px 6px rgba(0,0,0,0.04);
  --shadow-lg: 0 12px 40px rgba(0,0,0,0.11), 0 4px 12px rgba(0,0,0,0.06);
  --shadow-accent: 0 6px 24px rgba(208,92,37,0.28);

  --r-sm: 10px;
  --r-md: 18px;
  --r-lg: 24px;

  /* Height of the chat panel when pinned to the top-left in the split
     layout; #app reserves exactly this much padding above itself. Clamped
     because a plain 38vh collapses on a short window — the header and the
     input row alone need ~160px, and the messages would be squeezed to
     nothing between them. */
  --chat-dock-h: clamp(230px, 38vh, 430px);

  --ease:   cubic-bezier(0.4, 0, 0.2, 1);
  --spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* ── Reset ────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }

body {
  font-family: 'Inter', sans-serif;
  background: var(--bg);
  color: var(--text-1);
  min-height: 100vh;
  line-height: 1.6;
  overflow-x: hidden;
}

/* Subtle warm background texture */
body::before {
  content: '';
  position: fixed; inset: 0; pointer-events: none; z-index: 0;
  background:
    radial-gradient(ellipse 70% 50% at 10% 0%, rgba(208,92,37,0.06) 0%, transparent 60%),
    radial-gradient(ellipse 60% 40% at 90% 100%, rgba(233,165,53,0.07) 0%, transparent 60%);
}

#app {
  position: relative; z-index: 1;
  max-width: 1300px;
  margin: 0 auto;
  padding: 0 28px 100px;
}

/* Scrollbar */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: var(--bg-2); }
::-webkit-scrollbar-thumb { background: #d6cfc6; border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--accent); }

h1, h2, h3, h4 { font-family: 'Playfair Display', serif; line-height: 1.2; }

.gradient-text {
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
}

/* ── HEADER ───────────────────────────────── */
#header { padding: 40px 0 32px; text-align: center; }
.header-inner { position: relative; display: inline-block; }

.logo { display: flex; align-items: center; justify-content: center; gap: 14px; margin-bottom: 10px; }
.logo-icon {
  font-size: 2.8rem;
  filter: drop-shadow(0 4px 12px rgba(208,92,37,0.35));
  animation: float 4s ease-in-out infinite;
}
@keyframes float { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-7px); } }

.logo-text {
  font-family: 'Playfair Display', serif;
  font-size: 2.4rem; font-weight: 700; letter-spacing: -1px; color: var(--text-1);
}
.logo-accent { color: var(--accent); }
.tagline { color: var(--text-2); font-style: italic; font-size: 1rem; }

.settings-btn {
  position: absolute; top: 4px; right: -56px;
  width: 42px; height: 42px; border-radius: 50%;
  background: var(--surface); border: 1px solid var(--border-2);
  color: var(--text-3); cursor: pointer; font-size: 1.1rem;
  display: flex; align-items: center; justify-content: center;
  box-shadow: var(--shadow-sm); transition: all 0.25s var(--ease);
}
.settings-btn:hover { color: var(--accent); border-color: var(--accent); box-shadow: var(--shadow-md); }

/* ── SETTINGS PANEL ───────────────────────── */
.settings-panel { margin-bottom: 24px; }
.settings-content {
  max-width: 480px; margin: 0 auto; padding: 28px;
  background: var(--surface); border: 1px solid var(--border-2);
  border-radius: var(--r-lg); box-shadow: var(--shadow-md);
}
.settings-content h4 { font-size: 1rem; margin-bottom: 18px; color: var(--text-1); }
.backend-status {
  margin: -8px 0 18px; padding: 10px 14px; border-radius: var(--r-sm);
  background: var(--green-bg); border: 1px solid var(--green-border);
  color: var(--green); font-size: 0.76rem; font-weight: 600; line-height: 1.4;
}
.settings-content label {
  display: block; color: var(--text-2); font-size: 0.78rem;
  text-transform: uppercase; letter-spacing: 0.6px; margin-bottom: 7px; font-weight: 600;
}
.settings-content input[type="password"],
.settings-content select {
  width: 100%; padding: 12px 14px; margin-bottom: 14px;
  border: 1.5px solid var(--border-2); border-radius: var(--r-sm);
  font-size: 0.875rem; font-family: 'Inter', sans-serif;
  background: var(--bg); color: var(--text-1); transition: all 0.25s var(--ease);
}
.settings-content input:focus,
.settings-content select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); }

.btn-secondary {
  padding: 10px 22px; border-radius: var(--r-sm); cursor: pointer;
  background: var(--accent-bg); border: 1.5px solid rgba(208,92,37,0.3);
  color: var(--accent); font-family: 'Inter', sans-serif; font-size: 0.875rem; font-weight: 600;
  transition: all 0.25s var(--ease);
}
.btn-secondary:hover { background: rgba(208,92,37,0.12); }

/* ── INPUT SECTION ────────────────────────── */
/* The form, then the chat, then the generate button — so the traveller
   states their requirements before the thing that acts on them. */
#input-section { margin-bottom: 22px; }
.generate-row { margin-bottom: 52px; }

.input-card {
  padding: 52px 56px; position: relative; overflow: hidden;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
}
.input-card::before {
  content: '';
  position: absolute; top: 0; right: 0; width: 300px; height: 300px;
  background: radial-gradient(circle, rgba(208,92,37,0.06) 0%, transparent 70%);
  pointer-events: none;
}

.input-header { text-align: center; margin-bottom: 44px; }
.input-header h1 { font-size: 2.7rem; margin-bottom: 12px; font-weight: 700; }
.input-subtitle { color: var(--text-2); font-size: 1rem; }

/* Form */
.form-grid { display: grid; grid-template-columns: 1.1fr 1fr; gap: 28px; margin-bottom: 36px; }
@media (max-width: 760px) { .form-grid { grid-template-columns: 1fr; } .input-card { padding: 28px 22px; } }

/* min-width:0 everywhere a grid/flex child holds inputs: date inputs have a
   large intrinsic minimum width, and without this the date row refused to
   shrink below ~360px and pushed the whole page wider than a phone screen —
   which is why the layout looked shifted and never filled the width. */
.form-group { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.date-field { min-width: 0; }
.results-grid > * { min-width: 0; }
.card { min-width: 0; }
.driving-schedule { overflow-x: auto; }
.form-group label {
  color: var(--text-2); font-size: 0.75rem; font-weight: 700;
  letter-spacing: 0.8px; text-transform: uppercase;
  display: flex; align-items: center; gap: 6px;
}
.label-icon { font-size: 0.9rem; }

.form-group input[type="text"] {
  border: 1.5px solid var(--border-2); border-radius: var(--r-md);
  padding: 16px 20px; color: var(--text-1); font-size: 1rem;
  font-family: 'Inter', sans-serif; background: var(--bg);
  transition: all 0.25s var(--ease); width: 100%;
}
.form-group input[type="text"]:focus {
  outline: none; border-color: var(--accent); background: #fff;
  box-shadow: 0 0 0 3px var(--accent-glow);
}
.form-group input::placeholder { color: var(--text-3); }

/* Steers people towards a region rather than a single city — this is a road
   trip planner first, and one city gives it nothing to route between. */
.field-hint {
  color: var(--text-3); font-size: 0.76rem; line-height: 1.5; margin-top: -2px;
}
.input-subtitle strong { color: var(--accent); font-weight: 700; }

.date-row { display: flex; align-items: center; gap: 12px; }
.date-field { flex: 1; display: flex; flex-direction: column; gap: 5px; }
.date-label { font-size: 0.72rem; color: var(--text-3); font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }
.date-field input[type="date"] {
  border: 1.5px solid var(--border-2); border-radius: var(--r-sm);
  padding: 12px 14px; color: var(--text-1); font-size: 0.9rem;
  font-family: 'Inter', sans-serif; background: var(--bg); width: 100%;
  transition: all 0.25s var(--ease);
}
.date-field input[type="date"]:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); }
.date-separator { color: var(--text-3); font-size: 1.3rem; flex-shrink: 0; padding-top: 20px; }

/* On a phone two date pickers plus an arrow will not fit on one line, so
   they stack rather than forcing the page wider than the screen. */
@media (max-width: 560px) {
  /* A 190px square would leave almost no room for the hotel's name
     and description on a phone. */
  .stop-hotel-thumb { width: min(150px, 42%); }
  .stop-hotel-mini { min-height: 130px; }
  .stop-hotel-mini.sk { height: 122px; }
  .date-row { flex-wrap: wrap; gap: 10px; }
  .date-field { flex: 1 1 100%; }
  .date-separator { display: none; }

  /* The gear sits outside the header on wide screens; on a narrow one that
     put it past the right edge of the viewport. */
  .settings-btn { position: static; margin: 10px auto 0; }
  .map-footer { flex-wrap: wrap; }
}

/* Budget */
.budget-section { margin-bottom: 40px; }
.budget-section > label {
  display: block; color: var(--text-2); font-size: 0.75rem; font-weight: 700;
  letter-spacing: 0.8px; text-transform: uppercase; margin-bottom: 14px;
}
.budget-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
@media (max-width: 640px) { .budget-grid { grid-template-columns: repeat(2, 1fr); } }

.budget-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; padding: 20px 12px;
  background: var(--bg); border: 1.5px solid var(--border-2);
  border-radius: var(--r-md); cursor: pointer; transition: all 0.25s var(--ease); text-align: center;
}
.budget-btn:hover { background: var(--accent-bg); border-color: rgba(208,92,37,0.35); transform: translateY(-2px); box-shadow: var(--shadow-sm); }
.budget-btn.active {
  background: var(--accent-bg); border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}
.budget-btn.active .budget-name { color: var(--accent); }
.budget-icon { font-size: 1.6rem; }
.budget-name { color: var(--text-1); font-size: 0.875rem; font-weight: 600; }
.budget-desc { color: var(--text-3); font-size: 0.72rem; }

/* Generate button */
.btn-primary {
  width: 100%; padding: 20px;
  background: linear-gradient(135deg, var(--accent) 0%, var(--accent-2) 100%);
  border: none; border-radius: var(--r-md);
  color: #fff; font-size: 1.1rem; font-weight: 700; font-family: 'Inter', sans-serif;
  cursor: pointer; transition: all 0.3s var(--ease);
  display: flex; align-items: center; justify-content: center; gap: 10px;
  position: relative; overflow: hidden; letter-spacing: 0.3px;
  box-shadow: var(--shadow-accent);
}
.btn-primary::after {
  content: ''; position: absolute; top: 0; left: -100%; width: 60%; height: 100%;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
  transform: skewX(-20deg); transition: left 0.55s ease;
}
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 10px 32px rgba(208,92,37,0.4); }
.btn-primary:hover::after { left: 160%; }
.btn-primary:active { transform: translateY(0); }
.btn-primary:disabled { opacity: 0.55; cursor: not-allowed; transform: none; box-shadow: none; }
.btn-icon { font-size: 1.2rem; }

/* ── LOADING ──────────────────────────────── */
.loading-overlay {
  position: fixed; inset: 0; z-index: 2000;
  background: rgba(250,247,242,0.88); backdrop-filter: blur(10px);
  display: flex; align-items: center; justify-content: center;
}
.loading-content { text-align: center; }
.loading-globe { font-size: 4rem; display: block; margin-bottom: 8px; animation: spin-globe 3s linear infinite; }
@keyframes spin-globe { to { transform: rotateY(360deg); } }
.loading-spinner {
  width: 72px; height: 72px; margin: 0 auto 28px;
  border: 3px solid rgba(208,92,37,0.15);
  border-top-color: var(--accent); border-right-color: var(--accent-2);
  border-radius: 50%; animation: spin 0.85s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loading-text { font-family: 'Playfair Display', serif; font-size: 1.5rem; margin-bottom: 8px; }
.loading-subtext { color: var(--text-2); font-size: 0.88rem; animation: pulse 1.8s ease-in-out infinite; }
@keyframes pulse { 0%,100% { opacity: 0.4; } 50% { opacity: 1; } }

/* Phase 2 inline loading bar */
.phase2-bar {
  display: flex; align-items: center; gap: 10px; justify-content: center;
  padding: 10px 18px; margin-bottom: 16px;
  background: var(--accent-bg); border: 1px solid rgba(208,92,37,0.2);
  border-radius: 100px; font-size: 0.82rem; color: var(--accent); font-weight: 500;
  animation: fadeIn 0.4s var(--ease);
}
.phase2-dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
  animation: phase-pulse 1.2s ease-in-out infinite;
}
@keyframes phase-pulse { 0%,100%{transform:scale(1);opacity:0.6;} 50%{transform:scale(1.4);opacity:1;} }

/* ── RESULTS ──────────────────────────────── */
/* Fades in, but does NOT animate transform: a transformed ancestor becomes
   the containing block for position:fixed descendants and disturbs sticky
   ones, and the map inside is both depending on viewport width. */
#results-section { animation: fadeIn 0.5s var(--ease); }
@keyframes fadeInUp { from { opacity:0; transform:translateY(28px); } to { opacity:1; transform:translateY(0); } }

/* Trip header */
.trip-header { text-align: center; margin-bottom: 32px; }
.trip-title { font-size: 2.1rem; font-weight: 700; margin-bottom: 14px; }
.trip-meta { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
.meta-badge {
  padding: 7px 16px; background: var(--accent-bg); border: 1px solid rgba(208,92,37,0.2);
  border-radius: 100px; color: var(--accent); font-size: 0.84rem; font-weight: 500;
}

/* Map */
.map-container {
  margin-bottom: 30px; overflow: hidden; border-radius: var(--r-lg);
  border: 1px solid var(--border-2); box-shadow: var(--shadow-md);
}
#map { height: 480px; width: 100%; display: block; }

.map-footer {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 16px; background: var(--surface);
  border-top: 1px solid var(--border-2); gap: 12px;
}

.map-legend {
  display: flex; gap: 14px;
}
.legend-item { display: flex; align-items: center; gap: 7px; font-size: 0.78rem; color: var(--text-2); }
.legend-dot { width: 12px; height: 12px; border-radius: 50%; flex-shrink: 0; }
.hotel-dot { background: var(--accent); }
.attr-dot { background: #16a34a; }

/* Google Maps button */
.gmaps-btn {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 8px 18px;
  background: linear-gradient(135deg, #1a73e8, #1557b0);
  color: #fff; font-size: 0.82rem; font-weight: 600;
  border-radius: var(--r-sm); text-decoration: none;
  box-shadow: 0 2px 8px rgba(26,115,232,0.35);
  transition: transform 0.18s var(--ease), box-shadow 0.18s var(--ease), opacity 0.18s;
  white-space: nowrap;
}
.gmaps-btn svg { width: 14px; height: 14px; flex-shrink: 0; }
.gmaps-btn:hover { transform: translateY(-2px); box-shadow: 0 5px 16px rgba(26,115,232,0.45); }
.gmaps-btn:active { transform: translateY(0); }


/* Leaflet light popup */
.leaflet-popup-content-wrapper {
  background: #fff !important; border: 1px solid var(--border-2) !important;
  border-radius: var(--r-md) !important; color: var(--text-1) !important;
  box-shadow: var(--shadow-md) !important;
}
.leaflet-popup-tip { background: #fff !important; }
.leaflet-popup-close-button { color: var(--text-3) !important; }
.popup-title { font-family: 'Playfair Display', serif; font-size: 1rem; font-weight: 600; color: var(--accent); margin-bottom: 5px; }
.popup-desc { color: var(--text-2); font-size: 0.8rem; line-height: 1.5; max-width: 200px; }
.popup-nights { color: var(--text-3); font-size: 0.73rem; margin-top: 5px; }

/* Custom Markers */
.custom-marker { background: transparent; border: none; }
.marker-hotel-wrapper { width: 42px; height: 42px; position: relative; }
.marker-hotel-pin {
  width: 38px; height: 38px;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  border-radius: 50% 50% 50% 0; transform: rotate(-45deg);
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 4px 14px rgba(208,92,37,0.45); border: 2px solid rgba(255,255,255,0.5);
}
.marker-hotel-emoji { transform: rotate(45deg); font-size: 1rem; line-height: 1; }
/* Where the driving starts and ends — an airport, usually. Deliberately a
   third colour: orange means "you sleep here", green "you visit here", and
   this is neither. */
.marker-endpoint {
  width: 34px; height: 34px; border-radius: 50%;
  background: #1f2937;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.95rem;
  border: 2.5px solid #fff;
  box-shadow: 0 3px 12px rgba(31,41,55,0.45);
}

/* A place being considered, not yet in the plan — hence its own look,
   distinct from the green "you visit here" and orange "you sleep here". */
.marker-suggested {
  width: 36px; height: 36px; border-radius: 50%;
  background: #7c3aed;
  display: flex; align-items: center; justify-content: center;
  font-size: 1rem; border: 3px solid #fff;
  box-shadow: 0 0 0 6px rgba(124,58,237,0.22), 0 4px 14px rgba(124,58,237,0.5);
  animation: attr-pulse-violet 1.3s ease-in-out infinite;
}
@keyframes attr-pulse-violet {
  0%, 100% { box-shadow: 0 0 0 6px rgba(124,58,237,0.22), 0 4px 14px rgba(124,58,237,0.5); }
  50%      { box-shadow: 0 0 0 12px rgba(124,58,237,0.06), 0 4px 14px rgba(124,58,237,0.5); }
}

/* Matches the hovered card, so it is obvious which pin belongs to it. */
.highlight-card:hover { border-color: #7c3aed; box-shadow: var(--shadow-md); }

.marker-attr-dot {
  width: 26px; height: 26px; border-radius: 50%;
  background: rgba(22,163,74,0.9);
  display: flex; align-items: center; justify-content: center; font-size: 0.7rem;
  box-shadow: 0 2px 8px rgba(22,163,74,0.35); border: 2px solid rgba(255,255,255,0.6);
  transition: transform 0.2s var(--spring), background 0.2s var(--ease), box-shadow 0.2s var(--ease);
}

/* Hover-to-isolate fades pins out instead of removing them from the map —
   removing layers made the map flicker and fight the zoom animation. */
.leaflet-marker-icon { transition: opacity 0.22s var(--ease); }
.leaflet-marker-icon.marker-hidden { opacity: 0; pointer-events: none; }

/* Hovering the hotel card in the itinerary flags its pin on the map. */
.marker-hotel-pin { transition: transform 0.2s var(--spring), box-shadow 0.2s var(--ease); }
.marker-hotel-active .marker-hotel-pin {
  transform: rotate(-45deg) scale(1.35);
  box-shadow: 0 0 0 8px rgba(208,92,37,0.2), 0 6px 18px rgba(208,92,37,0.55);
}

/* Matching pin for an eating stop — same blue as its card in the list. */
.marker-food .marker-attr-dot {
  background: rgba(59,130,196,0.95);
  box-shadow: 0 2px 8px rgba(59,130,196,0.4);
}
.marker-food.marker-attr-active .marker-attr-dot {
  background: #3b82c4;
  box-shadow: 0 0 0 7px rgba(59,130,196,0.25), 0 4px 14px rgba(59,130,196,0.55);
  animation: none;   /* the colour already distinguishes it */
}

/* Hovering an attraction card in the itinerary flags its pin on the map.
   Deliberately stays GREEN: orange is the hotel colour, so recolouring the
   pin made an attraction look like it had turned into a hotel. Size and a
   halo carry the emphasis instead. */
.marker-attr-active .marker-attr-dot {
  transform: scale(1.55);
  background: #16a34a;
  box-shadow: 0 0 0 7px rgba(22,163,74,0.25), 0 4px 14px rgba(22,163,74,0.55);
  animation: attr-pulse 1.3s ease-in-out infinite;
}
@keyframes attr-pulse {
  0%, 100% { box-shadow: 0 0 0 7px rgba(22,163,74,0.25), 0 4px 14px rgba(22,163,74,0.55); }
  50%      { box-shadow: 0 0 0 13px rgba(22,163,74,0.07), 0 4px 14px rgba(22,163,74,0.55); }
}

/* ── PER-SEGMENT KM/TIME LABEL (route midpoint) ───────────── */
.route-distance-label { background: transparent; border: none; }
/* The pill is the hover target for its leg, so it has to accept the
   pointer even though its marker has no icon box of its own. */
.route-distance-pill { cursor: pointer; }
.route-distance-label:hover .route-distance-pill {
  border-color: #16a34a; color: #15803d;
}
.route-distance-pill {
  transform: translate(-50%, -50%);
  white-space: nowrap;
  background: rgba(255,255,255,0.96); border: 1.5px solid var(--border-2);
  border-radius: 100px; padding: 4px 11px;
  font-family: 'Inter', sans-serif; font-size: 0.72rem; font-weight: 700;
  color: var(--text-1); box-shadow: var(--shadow-md);
}

/* ── RESULTS GRID ─────────────────────────── */
.results-grid { display: grid; grid-template-columns: 1.4fr 1fr; gap: 24px; margin-bottom: 28px; }
@media (max-width: 880px) { .results-grid { grid-template-columns: 1fr; } }

.card {
  padding: 30px; display: flex; flex-direction: column; gap: 20px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-md);
}
.card-header {
  border-bottom: 1px solid var(--border-2); padding-bottom: 14px;
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
}
/* Figures that used to sit in a second, duplicate header inside the card. */
.card-header-note { font-size: 0.78rem; font-weight: 600; color: var(--text-3); white-space: nowrap; }
.card-header h3 { font-size: 1.2rem; font-weight: 600; }

/* Description */
.trip-description { color: var(--text-2); font-size: 0.95rem; line-height: 1.85; }
.trip-description p + p { margin-top: 11px; }

.stops-list { display: flex; flex-direction: column; gap: 10px; }
.stop-item {
  display: flex; gap: 13px; align-items: flex-start;
  padding: 13px 15px; background: var(--bg);
  border-radius: var(--r-sm); border: 1px solid var(--border);
  /* No transition at all. These rows contain -webkit-line-clamp text, and
     animating the row repaints that clamped text on every frame — Chrome
     is known to drop it mid-repaint, which is exactly the "text disappears
     while hovering" symptom. Hover feedback is instant instead. */
}
/* Deliberately NO transform on hover. Shifting the row sideways moved it
   out from under the cursor near its edges, which fired mouseleave — the
   row snapped back, fired mouseenter, and oscillated. That loop is what
   made the page flicker, and each cycle also re-triggered the map focus. */
.stop-item:hover { border-color: rgba(208,92,37,0.25); box-shadow: var(--shadow-sm); }
.stop-number {
  min-width: 32px; height: 32px; border-radius: 50%;
  background: var(--accent-bg); border: 1.5px solid rgba(208,92,37,0.3);
  display: flex; align-items: center; justify-content: center;
  font-size: 0.8rem; font-weight: 700; color: var(--accent); flex-shrink: 0;
}
.stop-info { flex: 1; min-width: 0; }
.stop-name { font-weight: 600; color: var(--text-1); font-size: 0.95rem; margin-bottom: 2px; }
.stop-nights-tag {
  display: inline-block; margin-left: 8px; padding: 1px 8px;
  border-radius: 100px; background: var(--accent-bg); color: var(--text-3);
  font-size: 0.68rem; font-weight: 500; vertical-align: middle;
}
.stop-dates {
  font-size: 0.76rem; font-weight: 600; color: var(--accent); margin-bottom: 4px;
}
.stop-desc { color: var(--text-3); font-size: 0.8rem; line-height: 1.5; }

/* Reserved up front (see the layout-shift note on .attr-chip-rating) — the
   weather arrives from the network after the row is already drawn. */
.stop-weather { min-height: 1.5em; margin: 2px 0 8px; }

.wx-note {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  font-size: 0.7rem; font-weight: 600; color: var(--text-3);
  margin-bottom: 5px; letter-spacing: 0.2px;
}
.wx-toggle {
  flex-shrink: 0;
  width: 22px; height: 22px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface); border: 1.5px solid var(--border-2);
  color: var(--text-2); font-size: 0.85rem; font-weight: 700; line-height: 1;
  cursor: pointer;
}
.wx-toggle:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-bg); }
.wx-summary .wx-date { color: var(--text-3); font-weight: 600; }
/* Averages rather than a forecast — shown quieter, so the difference reads
   at a glance and not only in the wording. */
.stop-weather.is-typical .wx-note { font-weight: 500; font-style: italic; }

.wx-grid {
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--bg); overflow: hidden;
}
.wx-row {
  display: grid; grid-template-columns: 58px repeat(4, 1fr);
  align-items: center; gap: 4px;
  padding: 5px 9px; border-bottom: 1px solid var(--border);
}
.wx-row:last-child { border-bottom: none; }
.wx-head {
  background: var(--accent-bg);
  font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.5px;
  color: var(--text-3); font-weight: 700;
}
.wx-date { font-size: 0.72rem; font-weight: 700; color: var(--accent); white-space: nowrap; }
.wx-part { text-align: center; }
.wx-val {
  display: flex; flex-direction: column; align-items: center; line-height: 1.25;
  font-size: 0.76rem; font-weight: 600; color: var(--text-1); white-space: nowrap;
}
.wx-rain { font-size: 0.63rem; font-weight: 500; color: #3b82c4; }

@media (max-width: 520px) {
  .wx-row { grid-template-columns: 46px repeat(4, 1fr); padding: 5px 6px; gap: 2px; }
  .wx-val { font-size: 0.7rem; }
  .wx-date { font-size: 0.66rem; }
}

/* Practical notes on an attraction: how long, and whether a ticket. */
.attr-chip-meta {
  display: flex; flex-wrap: wrap; gap: 4px 10px;
  font-size: 0.72rem; color: var(--text-3); line-height: 1.4;
}

/* ── HOTEL MINI-CARD (inside each itinerary stop) ─────────── */
.stop-hotel-mini {
  /* A panel of its own, tinted the way restaurants and trails are, so where
     you sleep is as easy to pick out of a day as where you eat. Warm rather
     than blue or green — the accent the rest of the app is built on. */
  margin-top: 14px; padding: 14px;
  background: var(--hotel-bg); border: 1px solid var(--hotel-border);
  border-radius: var(--r-sm);
  /* Matches the skeleton's height, so swapping placeholder for real
     content doesn't shove the rest of the itinerary up or down. */
  min-height: 170px;
}
.stop-hotel-mini.sk { height: 160px; }
/* The whole card links through to Booking. */
.stop-hotel-link {
  display: flex; align-items: flex-start; gap: 14px;
  text-decoration: none; color: inherit;
  border-radius: var(--r-sm);
  transition: opacity 0.2s var(--ease);
}
.stop-hotel-link:hover { opacity: 0.82; }
.stop-hotel-link:hover .stop-hotel-name { color: var(--accent); }
.stop-hotel-thumb {
  /* Landscape, not square, and sized to read as at least as prominent as
     an attraction photo beside it — a 190px square looked smaller than the
     ~277x182 attraction images even though its height matched. Still
     capped as a share of the card so a narrow column keeps room for the
     hotel's name and description. */
  width: min(320px, 36%); aspect-ratio: 3 / 2; height: auto;
  border-radius: var(--r-sm); overflow: hidden;
  flex-shrink: 0; background: var(--bg-2);
  display: flex; align-items: center; justify-content: center;
}
.stop-hotel-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.stop-hotel-thumb-icon { font-size: 1.9rem; }
.stop-hotel-info { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 4px; }
.stop-hotel-top { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.stop-hotel-name {
  font-size: 0.9rem; font-weight: 600; color: var(--text-1);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.stop-hotel-stars { font-size: 0.72rem; color: var(--accent-2); letter-spacing: 1px; flex-shrink: 0; }
.stop-hotel-desc {
  font-size: 0.78rem; color: var(--text-2); line-height: 1.55;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.stop-hotel-foot {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 2px;
  line-height: 1.4; min-height: 1.4em;   /* same reservation as .attr-chip-rating */
}
.stop-hotel-rating { font-size: 0.76rem; font-weight: 700; color: #b8860b; line-height: 1.4; }
.stop-hotel-price { font-size: 0.78rem; font-weight: 700; color: var(--accent); }

/* Action buttons */
.action-buttons { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: auto; }
.action-btn {
  padding: 12px 14px; background: var(--bg); border: 1.5px solid var(--border-2);
  border-radius: var(--r-sm); color: var(--text-2); font-size: 0.84rem;
  font-family: 'Inter', sans-serif; cursor: pointer; transition: all 0.22s var(--ease);
  display: flex; align-items: center; justify-content: center; gap: 6px;
}
.action-btn:hover { background: var(--accent-bg); border-color: rgba(208,92,37,0.35); color: var(--accent); transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.action-btn.save-btn {
  grid-column: 1 / -1;
  background: var(--green-bg); border-color: var(--green-border); color: var(--green); font-weight: 600;
}
.action-btn.save-btn:hover { background: #dcfce7; border-color: #86efac; color: var(--green); }

/* ── SCORE CARD ───────────────────────────── */
.score-container { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.score-ring-wrapper { position: relative; width: 150px; height: 150px; }
.score-ring { width: 100%; height: 100%; transform: rotate(-90deg); }
.ring-bg { fill: none; stroke: #f0ebe4; stroke-width: 9; }
.ring-fill {
  fill: none; stroke: url(#scoreGradient); stroke-width: 9; stroke-linecap: round;
  stroke-dasharray: 314.16; stroke-dashoffset: 314.16;
  transition: stroke-dashoffset 1.6s cubic-bezier(0.22, 1, 0.36, 1);
}
.score-number {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
}
#score-value {
  font-family: 'Playfair Display', serif; font-size: 2.8rem; font-weight: 700;
  color: var(--accent); line-height: 1;
}
.score-max { color: var(--text-3); font-size: 0.85rem; }
.score-description { color: var(--text-2); font-size: 0.82rem; text-align: center; font-style: italic; }

/* Pros/Cons */
.pros-cons { display: flex; flex-direction: column; gap: 16px; width: 100%; }
.pros-title, .cons-title { font-size: 0.78rem; font-weight: 700; margin-bottom: 9px; text-transform: uppercase; letter-spacing: 0.6px; font-family: 'Inter', sans-serif; }
.pros-title { color: var(--green); }
.cons-title { color: var(--red); }
.pros-list, .cons-list { list-style: none; display: flex; flex-direction: column; gap: 7px; }
.pros-list li {
  color: var(--text-2); font-size: 0.84rem; padding: 9px 13px;
  background: var(--green-bg); border-radius: var(--r-sm);
  border-left: 2.5px solid var(--green); line-height: 1.5;
  animation: fadeIn 0.4s var(--ease);
}
.cons-list li {
  color: var(--text-2); font-size: 0.84rem; padding: 9px 13px;
  background: var(--red-bg); border-radius: var(--r-sm);
  border-left: 2.5px solid var(--red); line-height: 1.5;
  animation: fadeIn 0.4s var(--ease);
}

/* ── SKELETON LOADING ─────────────────────── */
/* Pulses opacity rather than sliding a gradient.
   `background-position` cannot be animated by the compositor, so every
   skeleton repainted on every frame — and there are dozens of them while a
   trip loads, each one dragging the big fixed panels into the repaint with
   it. That is why the page flickered *during rendering*. Opacity animates
   on the compositor and costs nothing to repaint. */
.sk {
  border-radius: 6px;
  background: #ece5dc;
  animation: sk-pulse 1.5s ease-in-out infinite;
}
@keyframes sk-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }

.sk-ring { width: 140px; height: 140px; border-radius: 50%; margin: 0 auto; }
.sk-line { height: 14px; margin-bottom: 10px; }
.sk-line.w-80 { width: 80%; }
.sk-line.w-60 { width: 60%; }
.sk-line.w-70 { width: 70%; }
.sk-title { height: 20px; width: 75%; margin-bottom: 12px; }
.sk-btn { height: 38px; border-radius: var(--r-sm); }

.hotel-skeleton {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: 24px; box-shadow: var(--shadow-sm);
}

/* Mini inline spinner for phase 2 */
.mini-spinner {
  width: 20px; height: 20px;
  border: 2px solid rgba(208,92,37,0.2);
  border-top-color: var(--accent); border-radius: 50%;
  animation: spin 0.8s linear infinite; display: inline-block; flex-shrink: 0;
}

/* ── HOTELS ───────────────────────────────── */
.hotels-section { margin-bottom: 44px; }
.hotels-header {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 12px; margin-bottom: 22px;
}
.hotels-header .section-title { margin-bottom: 0; }
.hotels-bulk { display: flex; flex-wrap: wrap; gap: 8px; }
.bulk-btn {
  padding: 9px 15px; border-radius: 100px;
  background: var(--surface); border: 1.5px solid var(--border-2);
  color: var(--text-2); font-family: 'Inter', sans-serif;
  font-size: 0.8rem; font-weight: 600; cursor: pointer;
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease), background 0.2s var(--ease);
}
.bulk-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); background: var(--accent-bg); }
.bulk-btn:disabled { opacity: 0.45; cursor: not-allowed; }
.section-title { font-size: 1.5rem; margin-bottom: 22px; }
.hotels-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); gap: 18px; }

.hotel-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: 24px;
  display: flex; flex-direction: column; gap: 11px;
  box-shadow: var(--shadow-sm); transition: all 0.25s var(--ease);
  animation: fadeIn 0.45s var(--ease);
}
.hotel-card:hover { border-color: rgba(208,92,37,0.3); transform: translateY(-4px); box-shadow: var(--shadow-md); }
.hotel-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.hotel-name { font-family: 'Playfair Display', serif; font-size: 1.05rem; font-weight: 600; }
.hotel-stars { color: var(--accent-2); font-size: 0.72rem; letter-spacing: 2px; flex-shrink: 0; }
.hotel-location { color: var(--text-3); font-size: 0.78rem; }

/* Hotel names and descriptions come from a model and routinely contain
   very long unbroken words — Icelandic and German place names especially.
   Without this they refuse to wrap and push straight out of the card. */
.hotel-name, .hotel-description, .hotel-location,
.stop-hotel-name, .stop-hotel-desc,
.attr-chip-name, .attr-chip-desc,
.stop-name, .stop-desc {
  overflow-wrap: anywhere;
}
.hotel-rating { font-size: 0.82rem; font-weight: 700; color: #b8860b; line-height: 1.4; min-height: 1.4em; }
.hotel-description { color: var(--text-2); font-size: 0.84rem; line-height: 1.65; flex: 1; }
.hotel-price { color: var(--accent); font-weight: 700; font-size: 0.9rem; }
.hotel-btn {
  display: block; padding: 10px; text-align: center; text-decoration: none;
  background: var(--accent-bg); border: 1.5px solid rgba(208,92,37,0.25);
  border-radius: var(--r-sm); color: var(--accent); font-size: 0.82rem;
  font-family: 'Inter', sans-serif; font-weight: 600; transition: all 0.22s var(--ease);
}
.hotel-btn:hover { background: rgba(208,92,37,0.12); border-color: var(--accent); }

/* Booking direct, on the hotel's own site. Deliberately quieter than the
   Booking.com button - it is an alternative, not the main path. */
.hotel-btn-site {
  margin-top: 8px;
  background: var(--bg); border-color: var(--border-2); color: var(--text-2);
}
.hotel-btn-site:hover { background: var(--bg-2); border-color: var(--text-3); color: var(--text-1); }
.stop-hotel-site {
  font-size: 0.76rem; font-weight: 600; color: var(--text-2);
  text-decoration: none; border-bottom: 1px dotted var(--text-3);
}
.stop-hotel-site:hover { color: var(--accent); border-bottom-color: var(--accent); }

/* ── HIGHLIGHTS THE PLAN MISSES ───────────── */
.highlights-section { margin-bottom: 44px; }
.highlights-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 14px; }
.hl-tab {
  padding: 9px 16px; border-radius: 999px; cursor: pointer;
  background: var(--surface); border: 1.5px solid var(--border);
  color: var(--text-2); font-family: 'Inter', sans-serif;
  font-size: 0.84rem; font-weight: 600; white-space: nowrap;
  transition: background 0.2s var(--ease), border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.hl-tab:hover { border-color: #7c3aed; color: var(--text-1); }
.hl-tab.active {
  background: rgba(124,58,237,0.1); border-color: #7c3aed; color: #6d28d9;
}
.highlights-intro {
  color: var(--text-2); font-size: 0.86rem; line-height: 1.6;
  margin: -10px 0 20px; max-width: 720px;
}
.highlights-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 16px; margin-bottom: 18px;
}
.highlight-card {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-md); overflow: hidden;
  box-shadow: var(--shadow-sm);
}
.highlight-card.is-trail { background: var(--trail-bg); border-color: var(--trail-border); }
.highlight-card.is-trail:hover { border-color: var(--trail-accent); }
.highlight-card.is-trail .highlight-name { color: var(--trail-text); }
.highlight-card.is-trail .highlight-name:hover { color: var(--trail-accent); }

.highlight-card.is-food { background: var(--food-bg); border-color: var(--food-border); }
.highlight-card.is-food:hover { border-color: var(--food-accent); }
.highlight-card.is-food .highlight-name { color: var(--food-text); }
.highlight-card.is-food .highlight-name:hover { color: var(--food-accent); }
.highlight-card.is-added { border-color: var(--green-border); background: var(--green-bg); }
/* Brief fade as the card leaves, so the list visibly reshuffles rather
   than items teleporting around. */
.highlight-card { transition: opacity 0.25s var(--ease); }
.highlight-card.is-leaving { opacity: 0; }

/* Placeholder cards shown while the suggestions are being fetched. */
.highlight-card.is-skeleton { pointer-events: none; }
.highlight-card.is-skeleton .highlight-body { gap: 10px; }
.highlights-empty {
  grid-column: 1 / -1;
  color: var(--text-3); font-size: 0.88rem; line-height: 1.6;
  padding: 18px 0;
}
/* The image and the name both open the place in Google Maps. Two
   anchors rather than one wrapping the card: the card also holds a
   button, and a button inside a link is a broken control. */
.highlight-link { display: block; text-decoration: none; }
.highlight-alltrails {
  align-self: flex-start; font-size: 0.78rem; font-weight: 600;
  color: var(--trail-accent); text-decoration: none;
  border-bottom: 1px solid rgba(21,128,61,0.3); padding-bottom: 1px;
}
.highlight-alltrails:hover { border-bottom-color: var(--trail-accent); }
.highlight-img {
  width: 100%; height: 200px; overflow: hidden; flex-shrink: 0;
  background: var(--bg-2); display: flex; align-items: center; justify-content: center;
}
.highlight-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.highlight-body { padding: 16px 18px 18px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.highlight-name {
  font-family: 'Playfair Display', serif; font-size: 1.05rem; font-weight: 600;
  color: var(--text-1); overflow-wrap: anywhere;
  text-decoration: none; display: block;
  transition: color 0.2s var(--ease);
}
.highlight-name:hover { color: #6d28d9; text-decoration: underline; }
.highlight-why { font-size: 0.84rem; line-height: 1.6; color: var(--text-2); flex: 1; overflow-wrap: anywhere; }
.highlight-meta { font-size: 0.76rem; font-weight: 600; color: var(--text-3); min-height: 1.4em; }
.highlight-add {
  margin-top: 4px; padding: 11px 14px; border-radius: var(--r-sm);
  background: var(--accent-bg); border: 1.5px solid rgba(208,92,37,0.28);
  color: var(--accent); font-family: 'Inter', sans-serif;
  font-size: 0.84rem; font-weight: 700; cursor: pointer;
  transition: background 0.2s var(--ease), border-color 0.2s var(--ease);
}
.highlight-add:hover:not(:disabled) { background: rgba(208,92,37,0.14); border-color: var(--accent); }
.highlight-add:disabled {
  background: var(--green-bg); border-color: var(--green-border);
  color: var(--green); cursor: default;
}

/* ── SAVED ────────────────────────────────── */
.saved-section { margin-bottom: 44px; }
.saved-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 14px; }
.saved-card {
  padding: 20px 22px; cursor: pointer; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r-md);
  box-shadow: var(--shadow-sm); transition: all 0.22s var(--ease);
}
.saved-card:hover { border-color: rgba(208,92,37,0.3); transform: translateY(-2px); box-shadow: var(--shadow-md); }
.saved-card-title { font-weight: 600; color: var(--text-1); margin-bottom: 4px; }
.saved-card-meta { color: var(--text-3); font-size: 0.78rem; }
.saved-delete { float: right; background: none; border: none; color: var(--text-3); cursor: pointer; font-size: 0.85rem; padding: 2px 4px; transition: color 0.2s; }
.saved-delete:hover { color: var(--red); }

/* ── TOAST ────────────────────────────────── */
.toast {
  position: fixed; bottom: 32px; left: 50%;
  transform: translateX(-50%) translateY(80px);
  padding: 13px 26px; background: #1c1714; border-radius: 100px;
  color: #fff; font-size: 0.875rem; z-index: 9999;
  box-shadow: var(--shadow-lg); transition: transform 0.4s var(--spring); white-space: nowrap;
}
.toast.show { transform: translateX(-50%) translateY(0); }

/* ── ERROR ────────────────────────────────── */
.error-msg {
  background: var(--red-bg); border: 1px solid var(--red-border);
  border-radius: var(--r-sm); padding: 16px; color: var(--red); font-size: 0.9rem; line-height: 1.6;
}
.error-msg a { color: var(--accent); }

/* ── UTILS ────────────────────────────────── */
.hidden { display: none !important; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes slideUp { from { opacity:0; transform:translateY(16px); } to { opacity:1; transform:translateY(0); } }
.slide-up { animation: slideUp 0.4s var(--ease); }

/* ── STOP INNER (row: number + info) ─────── */
.stop-item { flex-direction: column; padding: 14px 15px; }
/* The row keeps `align-items: flex-start` from its base rule, which in a
   column flex container shrinks every child to its own content width.
   These sections are meant to span the card - and a percentage-sized
   thumbnail inside a shrink-to-fit parent collapses to almost nothing. */
.stop-inner, .stop-hotel-mini, .stop-attractions { align-self: stretch; }
.stop-inner { display: flex; gap: 13px; align-items: flex-start; }

/* ── ATTRACTION CHIPS ─────────────────────── */
.stop-attractions {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 14px;
  margin-top: 14px; padding-top: 14px;
  border-top: 1px dashed var(--border-2);
}
.attr-chip {
  display: flex; flex-direction: column;
  text-decoration: none;
  border-radius: var(--r-md); overflow: hidden;
  border: 1px solid var(--border); background: var(--surface);
  box-shadow: var(--shadow-sm);
  /* Same reason as .stop-item — this card's description is clamped text,
     and transitioning the card repaints it every frame. */
}
.attr-chip:hover { box-shadow: var(--shadow-md); border-color: var(--accent); }

/* Somewhere you eat rather than somewhere you look at. A light blue card
   and a 🍽️ make a day's meals findable at a glance among its sights. */
.attr-chip.is-food { background: var(--food-bg); border-color: var(--food-border); }
.attr-chip.is-food:hover { border-color: var(--food-accent); }
.attr-chip.is-food .attr-chip-name { color: var(--food-text); }
.attr-chip-img {
  width: 100%; height: 224px; overflow: hidden; flex-shrink: 0;
  background: var(--bg-2); display: flex; align-items: center; justify-content: center;
}
.attr-chip-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.chip-fallback-icon { font-size: 2.2rem; }
.attr-chip-body {
  padding: 11px 13px 13px;
  display: flex; flex-direction: column; gap: 5px;
}
.attr-chip-name {
  font-size: 0.86rem; font-weight: 600; color: var(--text-1); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* A trail in the itinerary keeps the card it was added from: the same
   serif name, the same length/difficulty line, the same way back to
   AllTrails — and a green card, the way meals get a blue one. */
.attr-chip-trail { background: var(--trail-bg); border-color: var(--trail-border); }
.attr-chip-trail:hover { border-color: var(--trail-accent); }
.attr-chip-trail .attr-chip-name { color: var(--trail-text); }
.attr-chip-link { display: block; text-decoration: none; }
a.attr-chip-name {
  font-family: 'Playfair Display', serif; font-size: 0.98rem; font-weight: 600;
  text-decoration: none; transition: color 0.2s var(--ease);
}
a.attr-chip-name:hover { color: var(--trail-accent); text-decoration: underline; }
.attr-chip-trail-meta {
  font-size: 0.74rem; font-weight: 600; color: var(--text-3); line-height: 1.4;
}
.attr-chip-desc {
  font-size: 0.75rem; color: var(--text-2); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
/* Ratings arrive from Google after the card is already on screen, so their
   line is reserved up front. Growing the card on arrival re-flowed the
   whole itinerary under the cursor — which is what made the list appear to
   flicker while the page was still filling in. */
/* line-height and min-height are pinned to the same value so the empty
   slot is exactly as tall as the filled one — otherwise the reservation
   is short and the card still grows when the rating lands. */
.attr-chip-rating { font-size: 0.75rem; font-weight: 700; color: #b8860b; line-height: 1.4; min-height: 1.4em; }

/* ── TRIP SUMMARY (bottom of the page) ────── */
/* The verdict on the trip, so it sits after everything it judges. Full
   width down here, which is room enough to put pros beside cons instead of
   stacking them as they were in the narrow side column. */
.summary-section { margin-bottom: 44px; }
@media (min-width: 720px) {
  .summary-section .pros-cons { flex-direction: row; align-items: flex-start; gap: 22px; }
  .summary-section .pros-section,
  .summary-section .cons-section { flex: 1; min-width: 0; }
}

/* ── DRIVING SCHEDULE ─────────────────────── */
.driving-schedule {
  border-radius: var(--r-sm);
  border: 1px solid var(--border-2); overflow: hidden;
  background: var(--bg); font-size: 0.82rem;
}
.driving-rows { display: flex; flex-direction: column; }
.driving-row {
  display: grid; grid-template-columns: 74px 1fr auto auto auto;
  align-items: center; gap: 10px; padding: 9px 14px;
  border-bottom: 1px solid var(--border); transition: background 0.18s;
}
.driving-row:last-child { border-bottom: none; }
.driving-row:hover { background: rgba(208,92,37,0.04); }
.driving-day {
  display: flex; flex-direction: column; line-height: 1.25;
  font-weight: 700; color: var(--accent); white-space: nowrap; font-size: 0.78rem;
}
.driving-date { font-size: 0.66rem; font-weight: 500; color: var(--text-3); }
.driving-route { color: var(--text-2); font-size: 0.8rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.driving-km { color: var(--text-1); font-weight: 600; white-space: nowrap; }
.driving-time { color: var(--text-3); white-space: nowrap; min-width: 52px; text-align: right; }
.driving-verified { color: var(--green); font-weight: 500; font-size: 0.72rem; text-transform: none; letter-spacing: 0; }

/* ── LOCAL "UPDATING" STATE (non-blocking chat regeneration) ─── */
/* Used instead of the full-screen loading overlay when the AI chat is
   applying a modification — only the affected section dims out, the rest
   of the page (header, chat, settings) stays fully usable. */
.is-updating {
  position: relative;
  pointer-events: none;
  opacity: 0.4;
  filter: saturate(0.6);
  transition: opacity 0.25s var(--ease), filter 0.25s var(--ease);
}
.is-updating::after {
  content: '';
  position: absolute; inset: 0;
  background: rgba(250,247,242,0.3);
  border-radius: inherit;
  z-index: 5;
}
.updating-spinner {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: 6;
  width: 36px; height: 36px;
  border: 3px solid rgba(208,92,37,0.2);
  border-top-color: var(--accent);
  border-right-color: var(--accent-2);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
  pointer-events: none;
}

/* ── HOTEL CARD (image header) ────────────── */
.hotel-card { padding: 0; overflow: hidden; }
.hotel-img {
  width: 100%; height: 224px; overflow: hidden; flex-shrink: 0;
  background: var(--bg-2); display: flex; align-items: center; justify-content: center;
}
.hotel-img img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform 0.4s ease; }
.hotel-card:hover .hotel-img img { transform: scale(1.06); }
.hotel-img-icon { font-size: 2.5rem; }
.hotel-body {
  padding: 20px; display: flex; flex-direction: column; gap: 10px; flex: 1;
}
.hotel-dates { color: var(--accent); font-size: 0.75rem; font-weight: 600; }

/* ── RICH MAP POPUPS ─────────────────────────── */
.rich-popup .leaflet-popup-content-wrapper {
  border-radius: 14px;
  box-shadow: 0 8px 32px rgba(0,0,0,0.18);
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--border);
}
.rich-popup .leaflet-popup-content {
  margin: 0;
  width: 264px !important;
}
.rich-popup .leaflet-popup-tip-container { display: none; }

/* popup root */
.mp {
  width: 264px;
  font-family: 'Inter', sans-serif;
  color: var(--text-1);
  background: var(--surface);
}

/* two photos side by side */
.mp-photos {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2px;
  height: 154px;
  overflow: hidden;
  background: var(--bg-2);
}
.mp-img {
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-2);
  font-size: 1.4rem;
  color: var(--text-3);
  transition: opacity 0.3s;
}
.mp-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.35s ease;
}
.mp:hover .mp-img img { transform: scale(1.06); }
.mp-img-empty { font-size: 1.6rem; opacity: 0.5; }

/* content below photos */
.mp-title {
  display: block;
  font-size: 0.88rem;
  font-weight: 700;
  color: var(--accent);
  text-decoration: none;
  padding: 11px 13px 4px;
  line-height: 1.35;
  transition: opacity 0.2s;
}
.mp-title:hover { opacity: 0.8; text-decoration: underline; }
.mp-desc {
  font-size: 0.76rem;
  color: var(--text-2);
  line-height: 1.5;
  padding: 0 13px;
  max-height: 52px;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}
.mp-meta {
  font-size: 0.72rem;
  color: var(--text-3);
  padding: 5px 13px 11px;
  font-weight: 500;
}

/* One photo rather than two: it is the same shot the itinerary card shows,
   and a single larger image reads better than two half-width crops. */
.mp-photos.mp-single { grid-template-columns: 1fr; height: 185px; }

.mp-rating { font-size: 0.74rem; font-weight: 700; color: #b8860b; padding: 4px 13px 0; }

/* Leg popup: no photo, just the figures the driving table lists. */
.mp-leg { padding: 13px 15px; }
.mp-leg-day { font-size: 0.72rem; font-weight: 700; color: var(--accent); text-transform: uppercase; letter-spacing: 0.5px; }
.mp-leg-route { font-size: 0.9rem; font-weight: 600; color: var(--text-1); margin: 3px 0 7px; line-height: 1.35; }
.mp-leg-figures { font-size: 0.84rem; font-weight: 700; color: var(--text-1); }

/* The "⏱ ok. 45 min · 🎟 bilet" line, reused verbatim from the cards. */
.mp .attr-chip-meta { padding: 5px 13px 11px; }

/* ── TRIP CHAT ────────────────────────────── */
.trip-chat {
  display: flex;
  flex-direction: column;
  margin-bottom: 22px;
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-md);
  overflow: hidden;
  /* Fades only — no transform. This panel is pinned with position:fixed in
     the split layout, and an entrance animation that moves it would shift
     it away from the viewport edge it is supposed to be attached to (and
     make it a containing block for any fixed descendants). */
  animation: fadeIn 0.5s var(--ease);
}

.chat-header {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
  padding: 18px 24px;
  border-bottom: 1px solid var(--border-2);
  background: linear-gradient(135deg, var(--accent-bg), transparent);
}
.chat-ai-badge {
  flex-shrink: 0;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 13px;
  border-radius: 100px;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #fff;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.3px;
  box-shadow: 0 3px 10px rgba(208,92,37,0.3);
}
.chat-header-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* The title yields before the close button does — a long subtitle must
   never be the reason there is no way out of the chat. */
.chat-title, .chat-subtitle { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Sits at the right end of the chat bar — the one place always on screen
   once a trip exists, on both layouts. */
.share-btn {
  margin-left: auto; flex-shrink: 0;
  display: inline-flex; align-items: center; gap: 7px;
  padding: 10px 20px; border-radius: 100px;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  border: none; color: #fff;
  font-family: 'Inter', sans-serif; font-size: 0.9rem; font-weight: 700;
  cursor: pointer; box-shadow: 0 3px 12px rgba(208,92,37,0.32);
  transition: box-shadow 0.2s var(--ease), opacity 0.2s var(--ease);
}
.share-btn:hover    { box-shadow: 0 6px 18px rgba(208,92,37,0.45); }
.share-btn:disabled { opacity: 0.55; cursor: not-allowed; }

/* ── SHARE DIALOG ─────────────────────────── */
.share-dialog {
  position: fixed; inset: 0; z-index: 3000;
  display: flex; align-items: center; justify-content: center;
  padding: 20px;
  background: rgba(28,23,20,0.45); backdrop-filter: blur(3px);
}
.share-box {
  position: relative;
  width: min(520px, 100%);
  padding: 30px 32px 26px;
  background: var(--surface); border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
}
.share-box h4 { font-size: 1.15rem; margin-bottom: 10px; }
.share-desc { color: var(--text-2); font-size: 0.86rem; line-height: 1.6; margin-bottom: 18px; }
.share-row { display: flex; gap: 10px; align-items: stretch; }
.share-link {
  flex: 1; min-width: 0;
  padding: 12px 14px; font-size: 0.85rem; font-family: 'Inter', sans-serif;
  color: var(--text-1); background: var(--bg);
  border: 1.5px solid var(--border-2); border-radius: var(--r-sm);
}
.share-link:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); }
.share-row .btn-secondary { white-space: nowrap; }
.share-expiry { color: var(--text-3); font-size: 0.76rem; margin-top: 12px; }
.share-close-btn {
  position: absolute; top: 14px; right: 14px;
  width: 30px; height: 30px; border-radius: 50%;
  background: transparent; border: 1.5px solid var(--border-2);
  color: var(--text-3); cursor: pointer; font-size: 0.8rem;
}
.share-close-btn:hover { color: var(--accent); border-color: var(--accent); }

@media (max-width: 560px) {
  .share-btn { padding: 9px 14px; font-size: 0.82rem; }
  .share-row { flex-direction: column; }
}
.chat-title { font-family: 'Playfair Display', serif; font-size: 1.05rem; font-weight: 600; color: var(--text-1); }
.chat-subtitle { font-size: 0.78rem; color: var(--text-2); }

.chat-messages {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 20px 24px;
  max-height: 380px;
  min-height: 90px;
  overflow-y: auto;
  background: var(--bg);
  scroll-behavior: smooth;
}
.chat-messages::-webkit-scrollbar { width: 5px; }
.chat-messages::-webkit-scrollbar-thumb { background: #d6cfc6; border-radius: 3px; }

.chat-msg { display: flex; animation: fadeIn 0.3s var(--ease); }
.chat-msg.chat-user { justify-content: flex-end; }
.chat-msg.chat-assistant { justify-content: flex-start; }
.chat-msg.chat-system { justify-content: center; }

.chat-bubble {
  max-width: 78%;
  padding: 11px 16px;
  font-size: 0.87rem;
  line-height: 1.55;
  border-radius: var(--r-md);
  word-wrap: break-word;
  white-space: pre-wrap;
}
.chat-user .chat-bubble {
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #fff;
  border-bottom-right-radius: 4px;
  box-shadow: 0 3px 10px rgba(208,92,37,0.25);
}
.chat-assistant .chat-bubble {
  background: var(--surface);
  color: var(--text-1);
  border: 1px solid var(--border-2);
  border-bottom-left-radius: 4px;
  box-shadow: var(--shadow-sm);
}

.chat-system-text {
  display: inline-block;
  padding: 6px 14px;
  border-radius: 100px;
  background: var(--accent-bg);
  border: 1px solid rgba(208,92,37,0.2);
  color: var(--accent);
  font-size: 0.75rem;
  font-weight: 500;
  text-align: center;
}

/* Typing indicator */
.chat-typing .chat-bubble { display: flex; align-items: center; gap: 5px; padding: 14px 16px; }
.tdot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--text-3);
  animation: tdot-bounce 1.2s ease-in-out infinite;
}
.tdot:nth-child(2) { animation-delay: 0.15s; }
.tdot:nth-child(3) { animation-delay: 0.3s; }
@keyframes tdot-bounce { 0%,60%,100% { transform: translateY(0); opacity: 0.5; } 30% { transform: translateY(-4px); opacity: 1; } }

.chat-form {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  padding: 16px 20px;
  border-top: 1px solid var(--border-2);
  background: var(--surface);
}
.chat-input {
  flex: 1;
  min-width: 0;
  padding: 12px 18px;
  border: 1.5px solid var(--border-2);
  border-radius: 100px;
  font-size: 0.88rem;
  font-family: 'Inter', sans-serif;
  color: var(--text-1);
  background: var(--bg);
  transition: all 0.25s var(--ease);
}
.chat-input::placeholder { color: var(--text-3); }
.chat-input:focus { outline: none; border-color: var(--accent); background: #fff; box-shadow: 0 0 0 3px var(--accent-glow); }
.chat-input:disabled { opacity: 0.6; cursor: not-allowed; }

.chat-send-btn {
  flex-shrink: 0;
  width: 42px; height: 42px;
  border-radius: 50%;
  border: none;
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  box-shadow: 0 3px 10px rgba(208,92,37,0.3);
  transition: all 0.22s var(--ease);
}
.chat-send-btn svg { width: 17px; height: 17px; }
.chat-send-btn:hover { transform: translateY(-2px) scale(1.04); box-shadow: 0 5px 16px rgba(208,92,37,0.42); }
.chat-send-btn:active { transform: translateY(0) scale(1); }
.chat-send-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; }

@media (max-width: 640px) {
  .chat-bubble { max-width: 86%; }
  .chat-header, .chat-form { padding-left: 16px; padding-right: 16px; }
  .chat-messages { padding-left: 16px; padding-right: 16px; }
}

/* ── ACCESSIBILITY: SKIP LINK + FOCUS STATES ─────────────── */
.skip-link {
  position: absolute; top: -60px; left: 12px; z-index: 10000;
  background: var(--accent); color: #fff; padding: 10px 18px;
  border-radius: var(--r-sm); font-weight: 600; font-size: 0.85rem;
  text-decoration: none; transition: top 0.2s var(--ease);
  box-shadow: var(--shadow-md);
}
.skip-link:focus { top: 12px; }

:focus-visible {
  outline: 2.5px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}
.budget-btn:focus-visible,
.action-btn:focus-visible,
.btn-primary:focus-visible,
.btn-secondary:focus-visible,
.chat-send-btn:focus-visible,
.settings-btn:focus-visible,
.hotel-btn:focus-visible,
.attr-chip:focus-visible {
  outline-offset: 3px;
}

/* ── PROGRESS STEPS (loading overlay) ─────────────────────── */
.progress-steps {
  list-style: none; display: flex; flex-direction: column; gap: 8px;
  margin-top: 18px; text-align: left; max-width: 260px; margin-left: auto; margin-right: auto;
}
.progress-step {
  display: flex; align-items: center; gap: 10px;
  font-size: 0.82rem; color: var(--text-3); transition: color 0.3s var(--ease);
}
.progress-step-num {
  flex-shrink: 0; width: 22px; height: 22px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.7rem; font-weight: 700;
  background: var(--bg-2); color: var(--text-3);
  border: 1.5px solid var(--border-2); transition: all 0.3s var(--ease);
}
.progress-step.active { color: var(--text-1); font-weight: 600; }
.progress-step.active .progress-step-num {
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
  color: #fff; border-color: transparent;
}
.progress-step.done { color: var(--text-2); }
.progress-step.done .progress-step-num {
  background: var(--green-bg); color: var(--green); border-color: var(--green-border);
}
.progress-step.done .progress-step-num::before { content: '✓'; }

/* ── CHAT UNDO BUTTON ──────────────────────────────────────── */
.chat-undo-btn {
  display: inline-flex; align-items: center; gap: 6px;
  margin: 2px auto 0; padding: 7px 15px;
  background: var(--surface); border: 1.5px solid var(--border-2);
  border-radius: 100px; color: var(--text-2); font-size: 0.76rem; font-weight: 600;
  font-family: 'Inter', sans-serif; cursor: pointer; transition: all 0.2s var(--ease);
}
.chat-undo-btn:hover { border-color: rgba(208,92,37,0.4); color: var(--accent); background: var(--accent-bg); }
.chat-msg.chat-undo-row { justify-content: center; }

/* ── SPLIT LAYOUT — map pinned to the right half of the screen ─────────
   Applied via body.map-split, which app.js adds once a trip is on screen.
   Before that the input card keeps the full centred width, since there is
   no map worth reserving half the viewport for. Below 1100px the map goes
   back to being a normal block in the flow — a half-viewport map on a
   laptop-narrow or phone screen leaves neither side usable. */
@media (min-width: 1100px) {
  /* Widths are percentages, NOT vw. `vw` includes the scrollbar while
     `right: 0` is measured against the viewport without it, so a 50vw chat
     on the left and a 50vw map on the right overlapped by exactly the
     scrollbar width. Percentages resolve against the same box as `right`,
     so the two halves meet cleanly. */
  /* The document itself does not scroll here — the left half does.

     This is the root fix for the flickering. With the page scrolling, the
     two big fixed panels (chat and map) had to be re-composited against
     the moving content on every frame, and the map alone is hundreds of
     nodes: tiles, markers, SVG paths. Any repaint anywhere — a hover, an
     image arriving — dragged them along with it.

     Now scrolling happens inside one container. The fixed panels sit
     outside that scroller, so they are simply never involved. */
  body.map-split { overflow: hidden; height: 100vh; }

  body.map-split #app {
    position: fixed;
    top: 0; left: 0;
    width: 50%;
    height: 100vh;
    max-width: none;
    margin: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    /* Top padding clears the chat pinned above; content scrolls behind it. */
    padding: calc(var(--chat-dock-h) + 22px) 34px 80px 28px;
  }

  /* The column itself runs the full half-screen; only the day-by-day card
     is capped. It is the one dense block of prose here, and on a wide
     monitor the half is ~1600px across — lines that long are tiring to
     read and the weather table stretches over nothing. */
  body.map-split #description-card { max-width: 940px; }

  /* The chat is pinned to the top of the left half, mirroring the map on
     the right — it is the one place the conversation happens, so it stays
     reachable no matter how far down the itinerary the reader has gone. */
  body.map-split #trip-chat {
    position: fixed;
    top: 0; left: 0;
    width: 50%;
    height: var(--chat-dock-h);
    z-index: 600;
    margin: 0;
    border-radius: 0;
    border-left: none;
    border-top: none;
    box-shadow: 0 4px 18px rgba(0,0,0,0.07);
  }
  /* Fill the pinned panel instead of the default capped height. */
  body.map-split #chat-messages { flex: 1; max-height: none; min-height: 0; }


  /* NOTE: forcing these two panels onto their own compositing layers
     (`will-change: transform` + `translateZ(0)` + `contain: paint`) was
     tried here as a way to stop the map's redraws repainting the whole
     page. It worked, but it also left the left column blank — the content
     was still there, correctly laid out and fully visible according to the
     DOM, simply never painted. Over-promotion producing stale tiles is a
     known hazard, so it is deliberately not done: the real reduction in
     repaints comes from redrawing far less often (the dwell delay and the
     unchanged-target check in app.js), not from layer tricks. */

  body.map-split .map-container {
    position: fixed;
    top: 0; right: 0;
    width: 50%; height: 100vh;
    margin: 0;
    border: none;
    border-left: 1px solid var(--border-2);
    border-radius: 0;
    box-shadow: none;
    z-index: 400;
    display: flex; flex-direction: column;
  }
  body.map-split #map { flex: 1; height: auto; min-height: 0; }
  body.map-split .map-footer { flex-shrink: 0; }

  /* One full-width column: the legs table, then the day-by-day plan. */
  body.map-split .results-grid { grid-template-columns: 1fr; gap: 20px; align-items: start; }

  /* The legs table gets its own scroller so a long route cannot push the
     itinerary far down the page — both stay reachable together. */
  body.map-split #driving-card {
    max-height: 40vh;
    overflow-y: auto;
    /* Scroll chaining is left ON deliberately. `contain` here trapped the
       wheel: once the legs list hit its end the page stopped moving, so
       scrolling over this card felt broken. It is an inline list, not an
       overlay — reaching its end should carry on down the page. */
  }
  body.map-split #driving-card > .card-header {
    position: sticky; top: -1px;
    background: var(--surface);
    z-index: 2;
  }

  /* An fr track will not shrink below its content's min-content width, and
     the daily-driving table is wide — without this the two cards overflow
     the left half (382px + 200px) instead of splitting it evenly. */
  body.map-split .results-grid > * { min-width: 0; }
  body.map-split .driving-schedule { overflow-x: auto; }

  /* Those columns are half as wide as the card used to be, so let two
     attraction photos still sit side by side inside one. */
  body.map-split .stop-attractions { grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 10px; }
  body.map-split .attr-chip-img { height: 182px; }
  body.map-split .card { padding: 24px; }
}

/* Hovering a stop or a driving row isolates it on the map — make the row
   itself look interactive so the connection is discoverable. */
.stop-item, .driving-row { cursor: default; }
.stop-item:hover { border-color: rgba(208,92,37,0.3); }
.driving-row:hover { background: var(--accent-bg); }

/* On touch there is no hover, so selection is an explicit tap and has to
   be visible on the row itself — the map may be docked and small, and the
   user needs to know which row they picked. */
.stop-item.is-focused {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-glow);
}
.driving-row.is-focused { background: var(--accent-bg); box-shadow: inset 3px 0 0 var(--accent); }

/* ── FLOATING CHAT (narrow screens) ───────────────────────────
   Below the split-layout breakpoint the chat cannot sit beside the map, and
   left in the normal flow it scrolled off and was easy to miss entirely.
   It becomes a launcher in the bottom-right corner instead — the pattern
   people already know from support chats — opening a floating window that
   closes just as easily. */
.chat-fab { display: none; }
.chat-close-btn { display: none; }

@media (max-width: 1099px) {
  /* Before a trip exists the chat stays in the page, right above the
     generate button — that is where requirements get stated, and hiding it
     behind a launcher would mean most people never find it. It collapses
     into the launcher only once there is a route to discuss (body gains
     .map-split when results appear). */
  /* A pill rather than a circle: the two sparkles say "model", the AI
     beside them says which one of the floating buttons on the screen this
     is. Same height as the old circle, so nothing else moves. */
  body.map-split .chat-fab {
    display: flex; align-items: center; justify-content: center; gap: 6px;
    position: fixed; right: 18px; bottom: calc(18px + var(--kb-inset, 0px));
    height: 58px; padding: 0 20px 0 17px; border-radius: 100px;
    background: linear-gradient(135deg, var(--accent), var(--accent-2));
    border: none; color: #fff; line-height: 1;
    box-shadow: 0 6px 22px rgba(208,92,37,0.45);
    cursor: pointer; z-index: 1200;   /* over the map, under the open sheet */
    transition: transform 0.2s var(--spring), box-shadow 0.2s var(--ease);
  }
  .chat-fab-icon { width: 26px; height: 26px; fill: currentColor; flex-shrink: 0; }
  .chat-fab-label {
    font-family: 'Inter', sans-serif; font-size: 1.02rem; font-weight: 800;
    letter-spacing: 0.5px;
  }
  .chat-fab:hover  { transform: scale(1.06); }
  .chat-fab:active { transform: scale(0.97); }

  /* Unread marker: the assistant may answer while the window is closed. */
  .chat-fab-dot {
    position: absolute; top: 4px; right: 4px;
    width: 13px; height: 13px; border-radius: 50%;
    background: #c93030; border: 2px solid #fff;
  }

  /* Tucked away only after the trip is generated, and only while closed. */
  body.map-split .trip-chat { display: none; }

  /* Open, the chat IS the visual viewport: the exact strip of screen the
     reader can see. Both values come from syncKeyboardInset, so when the
     keyboard takes the bottom half the sheet becomes the top half — no
     lifting, no capping, nothing left to run off the edge. The messages
     scroll inside; the input sits on the sheet's own bottom edge, which
     is the top of the keyboard. */
  body.map-split.chat-open .trip-chat {
    display: flex;
    flex-direction: column;
    position: fixed;
    left: 0; top: 0;
    width: 100%;
    height: var(--vv-h, 100dvh);
    transform: translateY(var(--vv-top, 0px));
    margin: 0;
    border-radius: 0;
    border-left: none; border-right: none;
    /* Above Leaflet, not level with it. Leaflet gives its own controls
       z-index 1000, and at a tie the element painted later wins — which is
       how zoom buttons and attribution ended up sitting on top of the open
       chat. */
    z-index: 2100;
    box-shadow: 0 -8px 40px rgba(0,0,0,0.22);
  }
  body.map-split.chat-open #chat-messages {
    flex: 1 1 auto; max-height: none; min-height: 0; overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }
  body.map-split.chat-open .chat-header,
  body.map-split.chat-open .chat-form { flex-shrink: 0; }
  /* The sheet covers the screen, so the launcher underneath is both
     invisible and a tap target nobody can reach on purpose. */
  body.map-split.chat-open .chat-fab { display: none; }

  body.map-split.chat-open .chat-close-btn {
    display: flex; align-items: center; justify-content: center;
    margin-left: auto; flex-shrink: 0;
    width: 30px; height: 30px; border-radius: 50%;
    background: transparent; border: 1.5px solid var(--border-2);
    color: var(--text-2); font-size: 0.8rem; cursor: pointer;
    transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
  }
  .chat-close-btn:hover { color: var(--accent); border-color: var(--accent); }
}

/* ── MAP ON DEMAND (narrow screens) ───────────────────────────
   Below the split-layout breakpoint the map is a normal block in the flow,
   so scrolling down the itinerary pushes it off screen — and selecting a
   day then appeared to do nothing, because the only feedback was on a map
   the user could no longer see. Rather than keeping the map permanently in
   the way, each day and each leg carries a 🔍 button; pressing it pins the
   map to the top as a strip, focused on that item, until it is closed. */
@media (max-width: 1099px) {
  /* Fixed, not sticky. Sticky only pins an element once the reader has
     scrolled past its place in the page — so pressing 🔍 while sitting
     above the map would have shown nothing at all. Fixed makes the button
     work from anywhere on the page, which is the whole point of it. */
  body.map-docked .map-container {
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: 900;
    margin: 0;
    border-radius: 0;
    box-shadow: var(--shadow-lg);
  }
  body.map-docked #map { height: 34vh; }
  body.map-docked .map-spacer { display: block; height: var(--map-h, 0px); }
  body.map-docked .map-collapse-btn { display: flex; }
  body.map-docked .map-legend { display: none; }   /* no room in the strip */
}

.map-zoom-btn {
  flex-shrink: 0; align-self: flex-start;
  width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--surface); border: 1.5px solid var(--border-2);
  border-radius: 50%; cursor: pointer;
  font-size: 0.82rem; line-height: 1;
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}
.map-zoom-btn:hover { border-color: var(--accent); background: var(--accent-bg); }
.driving-row .map-zoom-btn { width: 26px; height: 26px; font-size: 0.72rem; align-self: center; }

.map-collapse-btn {
  display: none;
  align-items: center; justify-content: center;
  width: 32px; height: 30px; flex-shrink: 0;
  background: var(--bg); border: 1.5px solid var(--border-2);
  border-radius: var(--r-sm); color: var(--text-2);
  font-size: 0.85rem; line-height: 1; cursor: pointer;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.map-collapse-btn:hover { color: var(--accent); border-color: var(--accent); }

/* ── NARROW SCREENS: GIVE THE CONTENT ITS WIDTH BACK ────────
   On a 375px phone the old insets — 28px of page padding plus 30px of
   card padding on each side — spent a third of the screen on empty
   margin. The content is what people came for; the frame around it can be
   a hairline. Last in the file on purpose: these override base rules of
   equal specificity, and earlier in the file they simply lost. */
@media (max-width: 560px) {
  #app { padding: 0 8px 100px; }
  .card { padding: 16px 11px; gap: 16px; }
  .input-card { padding: 26px 14px; }
  .stop-item { padding: 12px 10px; gap: 10px; }
  .chat-header, .chat-form, .chat-messages { padding-left: 12px; padding-right: 12px; }
  .highlights-grid { gap: 12px; }
  .highlight-body { padding: 13px 12px 14px; }
  .attr-chip-body { padding: 10px 11px 12px; }
  .stop-hotel-mini { padding: 12px; }
  .section-title { padding-left: 0; padding-right: 0; }
}

/* ── SHARE BUTTON IN THE SUMMARY ROW (narrow screens) ───────
   placeShareButton() moves the node here, next to the days/hours/km
   badges, so it is reachable without opening the chat. */
@media (max-width: 1099px) {
  .trip-meta .share-btn {
    margin-left: 0;
    padding: 7px 16px;
    font-size: 0.84rem;
  }
  /* Nothing inside the sheet may make it wider than the screen — that is
     what pushed the ✕ out of reach when the keyboard opened. */
  body.map-split.chat-open .trip-chat { max-width: 100vw; overflow: hidden; }
  body.map-split.chat-open .chat-close-btn { margin-left: auto; }
}

/* The day's number and its 🔍 used to flank the content, so every line of
   description, every weather row and every hotel card was squeezed into
   the 223px left between them. On a phone that is 40% of the screen spent
   on two 32px controls. They get their own strip across the top instead,
   and everything below runs the full width of the card. */
@media (max-width: 560px) {

  .wx-row { padding-left: 4px; padding-right: 4px; }
}

/* The 🔍 on cards floats over the top-right corner of the image rather
   than taking a place in the layout — these cards are already tight on a
   phone, and a control that pushed the text aside would cost more room
   than it is worth. */
.attr-chip, .highlight-card, .stop-hotel-mini { position: relative; }
.attr-chip > .map-zoom-btn,
.highlight-card > .map-zoom-btn,
.stop-hotel-mini > .map-zoom-btn {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  width: 30px; height: 30px;
  background: rgba(255,255,255,0.94);
  box-shadow: 0 2px 8px rgba(0,0,0,0.18);
}

/* iOS zooms the whole page in when a text field with a font smaller than
   16px takes focus. That zoom is what "adapts the screen to the input":
   the layout stays 375px wide while the visible area shrinks to whatever
   the zoom shows, so the close and send buttons end up off-screen. It is
   not a layout bug and no amount of min-width fixes it — the field simply
   has to be 16px. The visual size is kept with padding instead. */
@media (max-width: 1099px) {
  .chat-input, .share-link,
  #destination-input, .date-field input[type="date"],
  input[type="date"], input[type="text"], input[type="search"], textarea, select {
    font-size: 16px;
  }
  .chat-input { padding: 10px 16px; }
}

/* The hotel card puts its star rating in the top right, which is exactly
   where the 🔍 was landing. Over the photo on the left there is nothing
   to collide with. */
/* Top right, where every other card carries it. The star rating that used
   to sit there moves to its own line under the hotel name. */
@media (max-width: 1099px) {
  .stop-hotel-top {
    flex-direction: column; align-items: flex-start; gap: 2px;
    padding-right: 42px;      /* the name stops short of the 🔍 */
  }
  .stop-hotel-name { white-space: normal; }
}

/* A 30px circle is a small target for a thumb. The circle stays that size
   — it is deliberately unobtrusive over a photo — while the area that
   responds to a tap grows past it by 6px on every side, taking it from
   30px to 42px, just above the 44px both platforms recommend once the
   finger contact area is counted. */
.map-zoom-btn { position: relative; }
.attr-chip > .map-zoom-btn,
.highlight-card > .map-zoom-btn,
.stop-hotel-mini > .map-zoom-btn { position: absolute; }
.map-zoom-btn::after {
  content: '';
  position: absolute; inset: -6px;
  /* Square, not round: a thumb landing on the corner of the area is still
     aiming at the button. */
}

/* A date field carries an intrinsic width — the widest its dd/mm/yyyy
   spinner can render — and at the 16px the keyboard fix demands, that is
   wider than half a phone screen. width:100% alone cannot shrink a flex
   item below its intrinsic minimum, so it spilled out of the card. */
@media (max-width: 1099px) {
  .date-field, .date-field input[type="date"] {
    min-width: 0;
    max-width: 100%;
    box-sizing: border-box;
  }
  .date-field input[type="date"] { padding-left: 10px; padding-right: 10px; }
}

.map-spacer { display: none; }

/* iOS gives a date input a fixed intrinsic width from its native spinner
   and will not shrink it, so width:100% is ignored and the field hangs out
   of the card. Dropping the native appearance is the only thing that makes
   it size like an ordinary box; the calendar still opens on tap. */
@media (max-width: 1099px) {
  .date-field input[type="date"] {
    -webkit-appearance: none;
    appearance: none;
    width: 100%; min-width: 0; max-width: 100%;
    box-sizing: border-box;
  }
  .date-field input[type="date"]::-webkit-date-and-time-value { text-align: left; }
  .date-field input[type="date"]::-webkit-calendar-picker-indicator { margin-left: 0; }
}

/* The 🔍 only makes sense where a finger cannot hover and the map can be
   off screen. In the split layout the map is always on the right and
   hovering a row already shows the place, so every variant of the button
   goes away there.

   Last in the file, and listing the variants explicitly: this rule used to
   sit ABOVE the base .map-zoom-btn definition, where — at equal
   specificity — the later rule simply won, and the buttons were on screen
   at every width. */
@media (min-width: 1100px) {
  .map-zoom-btn,
  .attr-chip > .map-zoom-btn,
  .highlight-card > .map-zoom-btn,
  .stop-hotel-mini > .map-zoom-btn,
  .driving-row .map-zoom-btn { display: none; }
}

/* ── HAND EDITS: REMOVE AN ATTRACTION, SWAP A HOTEL ─────────
   The ✕ sits opposite the 🔍 so the two are never confused, and it is
   deliberately quiet: it is there when looked for, not competing with the
   photo. Same enlarged tap area as the 🔍. */
.attr-remove {
  position: absolute; top: 8px; left: 8px; z-index: 2;
  width: 30px; height: 30px;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,0.94); color: var(--text-3);
  border: 1.5px solid var(--border-2); border-radius: 50%;
  font-size: 0.78rem; line-height: 1; cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,0.18);
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease), background 0.2s var(--ease);
}
.attr-remove::after { content: ''; position: absolute; inset: -6px; }
.attr-remove:hover { color: #c93030; border-color: #c93030; background: #fff; }

.hotel-alt-btn {
  margin-top: 10px; padding: 9px 14px; width: 100%;
  background: var(--surface); border: 1.5px solid var(--border-2);
  border-radius: var(--r-sm); color: var(--text-2);
  font-family: 'Inter', sans-serif; font-size: 0.8rem; font-weight: 600;
  cursor: pointer;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.hotel-alt-btn:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); }
.hotel-alt-btn:disabled { opacity: 0.6; cursor: default; }

.hotel-alts {
  margin-top: 10px; padding: 12px;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--r-sm);
  display: flex; flex-direction: column; gap: 10px;
}
.hotel-alts-title { font-size: 0.78rem; font-weight: 700; color: var(--text-2); }
.hotel-alt {
  display: flex; align-items: center; gap: 10px;
  padding-bottom: 10px; border-bottom: 1px dashed var(--border-2);
}
.hotel-alt:last-child { padding-bottom: 0; border-bottom: none; }
.hotel-alt-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.hotel-alt-name { font-size: 0.82rem; font-weight: 600; color: var(--text-1); overflow-wrap: anywhere; }
.hotel-alt-stars { color: var(--accent-2); font-size: 0.72rem; letter-spacing: 1px; }
.hotel-alt-desc {
  font-size: 0.75rem; color: var(--text-2); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.hotel-alt-meta { font-size: 0.72rem; color: var(--text-3); font-weight: 600; }
.hotel-alt-pick {
  flex-shrink: 0; padding: 8px 14px; border-radius: var(--r-sm);
  background: var(--accent-bg); border: 1.5px solid rgba(208,92,37,0.28);
  color: var(--accent); font-family: 'Inter', sans-serif;
  font-size: 0.78rem; font-weight: 700; cursor: pointer;
}
.hotel-alt-pick:hover { background: rgba(208,92,37,0.14); border-color: var(--accent); }

/* ── LEAVING AND ARRIVING ───────────────────────────────────
   Only ever applied to a single element that is on its way out or has just
   appeared, and only on opacity/transform — the two properties the
   compositor can animate without re-laying out the itinerary underneath,
   which is what made earlier versions of this page flicker. */
.attr-chip.is-removing {
  opacity: 0;
  transform: scale(0.94);
  transition: opacity 0.24s var(--ease), transform 0.24s var(--ease);
  pointer-events: none;
}

.stop-hotel-mini.is-swapping {
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 0.22s var(--ease), transform 0.22s var(--ease);
  pointer-events: none;
}
.hotel-alts.is-leaving {
  opacity: 0;
  transition: opacity 0.2s var(--ease);
  pointer-events: none;
}

@keyframes hotel-arrive {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.stop-hotel-mini.is-entering { animation: hotel-arrive 0.4s var(--ease) both; }

@media (prefers-reduced-motion: reduce) {
  .attr-chip.is-removing,
  .stop-hotel-mini.is-swapping,
  .hotel-alts.is-leaving { transition: none; }
  .stop-hotel-mini.is-entering { animation: none; }
}

/* "bez ocen w Google" sits on the same line as a real rating but must not
   read like one — it is an absence, not a score. */
.attr-chip-rating, .highlight-meta { font-variant-numeric: tabular-nums; }

/* ── THE HOTEL BLOCK INSIDE AN OVERNIGHT PIN ────────────────
   A tinted panel running the full width of the popup, so the pin reads as
   two facts about the night — the town above, where you sleep below —
   rather than one paragraph that changes subject halfway through. Its side
   padding matches the 13px the rest of the popup uses, so nothing in the
   column is out of line. */
.mp-hotel {
  margin-top: 10px;
  padding: 11px 13px 13px;
  background: var(--bg);
  border-top: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 5px;
}
.mp-hotel-label {
  font-size: 0.62rem; font-weight: 800; letter-spacing: 0.9px;
  text-transform: uppercase; color: var(--text-3);
}
.mp-hotel-name {
  font-family: 'Playfair Display', serif;
  font-size: 0.96rem; font-weight: 600; line-height: 1.25;
  color: var(--text-1); text-decoration: none; overflow-wrap: anywhere;
  transition: color 0.2s var(--ease);
}
.mp-hotel-name:hover { color: var(--accent); }
.mp-hotel-meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 9px; }
.mp-hotel-rating { font-size: 0.72rem; font-weight: 700; color: #b8860b; }
.mp-hotel-stars  { font-size: 0.68rem; letter-spacing: 1px; color: var(--accent-2); }
.mp-hotel-price  { font-size: 0.72rem; font-weight: 700; color: var(--accent); }
.mp-hotel-desc {
  font-size: 0.73rem; color: var(--text-2); line-height: 1.5;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.mp-hotel-book {
  margin-top: 3px; padding: 8px 10px;
  text-align: center; border-radius: var(--r-sm);
  background: var(--accent-bg); border: 1.5px solid rgba(208,92,37,0.28);
  font-size: 0.74rem; font-weight: 700; color: var(--accent);
  text-decoration: none;
  transition: background 0.2s var(--ease), border-color 0.2s var(--ease);
}
.mp-hotel-book:hover { background: rgba(208,92,37,0.14); border-color: var(--accent); }

/* The popup's own padding stops at the panel — the day/nights line above it
   would otherwise leave a gap twice the size of every other. */
.mp-hotel + *, .mp > .mp-meta:last-child { padding-bottom: 11px; }
.mp .mp-meta:has(+ .mp-hotel) { padding-bottom: 0; }

/* Leaflet ships `.leaflet-container a { color: #0078A8 }`, which is one
   notch more specific than a bare class — so every link inside a popup came
   out link-blue no matter what these rules said. Same colours as before,
   stated where they can win. */
.mp a.mp-title      { color: var(--accent); }
.mp a.mp-hotel-name { color: var(--text-1); }
.mp a.mp-hotel-name:hover { color: var(--accent); }
.mp a.mp-hotel-book { color: var(--accent); }

.mp-hotel-pin { font-size: 0.72rem; opacity: 0.75; vertical-align: 1px; }

/* Two controls were still finger-hostile on a phone: the 🔍 in a driving
   row (26 px) and the weather expander (22 px). The circles stay small —
   they sit inside dense rows — while the area that answers a tap grows to
   about 44 px, the size both platforms ask for. */
.driving-row .map-zoom-btn::after { inset: -9px; }
.wx-toggle { position: relative; }
.wx-toggle::after { content: ''; position: absolute; inset: -11px; }

/* Which day a suggestion would land in, said before the button is pressed
   rather than after. Quieter than the rating line above it — it is a
   consequence, not a fact about the place. */
.highlight-where {
  font-size: 0.74rem; color: var(--text-3); line-height: 1.4; min-height: 1.4em;
}

/* ══════════════════════════════════════════════════════════════
   DOCK & RAILS — the phone layout
   ══════════════════════════════════════════════════════════════
   Everything here is inside one media query. Delete the block and the
   previous phone layout returns unchanged; nothing above is modified.

   Three mechanisms: a dock glued to the visual viewport, a re-cut of the
   feed's order, and horizontal rails of the existing cards. */

/* Defaults for the mobile-only shell, stated outside the media query: a
   rule that only exists inside it does not apply on a wide screen, and the
   sheet's tabs were showing above the desktop chat panel. */
.mobile-dock, .sheet-tabs, .sheet-ideas { display: none; }

@media (max-width: 1099px) {

  /* ── 1. THE DOCK ──────────────────────────────────────────
     A leaf element, so its transform cannot contain the fixed map or the
     sticky day headers. Positioned against the VISUAL viewport, whose
     height and offset syncKeyboardInset() publishes — the same mechanism
     that already keeps the chat above the keyboard on iOS. No transition:
     visualViewport fires per frame while the URL bar collapses, and a
     transition would visibly lag the keyboard. */
  /* Gated on the class, not the attribute: an author rule saying
     `display: block` beats the browser's own `[hidden]`, so the bar was on
     screen before there was any trip to change — the element was hidden in
     the markup and visible on the phone. */
  body.has-dock .mobile-dock {
    display: block;
    position: fixed; left: 0; top: 0; width: 100%;
    transform: translateY(calc(var(--vv-top, 0px) + var(--vv-h, 100vh) - 100%));
    z-index: 1400;
    /* 8px under the input, and nothing else. The safe-area inset used to be
       added here and it is simply the wrong quantity for this element: it
       measures the home indicator against the SCREEN, while the bar is
       positioned against the VISUAL VIEWPORT, which already excludes
       whatever browser furniture is at the bottom. The two agree only when
       no such furniture exists — so the moment the address bar collapsed
       on scroll, or the keyboard came up, the inset became up to 34px of
       nothing between the input and the row of keys below it.

       Deliberately not a calc() correcting the inset by the keyboard
       either: that was the previous attempt, and it still trusted a number
       that describes a different coordinate system.

       What IS here is a fixed clearance, and it is small on purpose. A
       phone with rounded corners cuts a chord out of each bottom corner of
       the screen, and the bar's controls reach all the way into them: the
       corners of Sugestie and the send button were being shaved off. 14px
       at the bottom and 14px at the sides pulls every control inside the
       curve, and the row above tightens by the same amount so the bar does
       not get taller for it. */
    padding: 6px 14px 14px;
    background: var(--surface);
    border-top: 1px solid var(--border);
    box-shadow: 0 -6px 22px rgba(28,23,20,.10);
  }
  .dock-row { display: flex; gap: 8px; align-items: stretch; min-width: 0; }
  .dock-form-slot { flex: 1; min-width: 0; display: flex; }
  .dock-form-slot .chat-form {
    flex: 1; min-width: 0; padding: 0; border-top: none; background: transparent;
  }
  .dock-form-slot .chat-input { font-size: 16px; padding: 12px 16px; }
  .dock-form-slot .chat-send-btn { width: 44px; height: 44px; }

  .dock-ideas {
    flex: 0 0 auto; min-width: 116px; min-height: 44px;
    padding: 6px 12px; border: none; border-radius: var(--r-sm);
    background: linear-gradient(135deg, var(--accent), var(--accent-2));
    color: #fff; font-family: 'Inter', sans-serif; cursor: pointer;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  }
  .dock-ideas b { font-size: 0.84rem; font-weight: 800; }
  .dock-ideas span { font-size: 0.68rem; opacity: .93; font-weight: 600; }

  .dock-scope {
    display: inline-flex; align-items: center; gap: 6px; margin-bottom: 7px;
    padding: 5px 11px; border-radius: 999px;
    background: var(--accent-bg); border: 1px solid rgba(208,92,37,.28);
    color: var(--accent); font-size: 0.76rem; font-weight: 700;
  }

  /* The page ends above the dock, and anything scrolled to must clear it. */
  body.has-dock #app { padding-bottom: calc(104px + env(safe-area-inset-bottom, 0px)); }
  body.has-dock .stop-item,
  body.has-dock .driving-row,
  body.has-dock #highlights-section { scroll-margin-bottom: 118px; }

  /* The launcher is gone: the dock is the chat now. */
  body.map-split .chat-fab, .chat-fab { display: none !important; }

  /* ── 2. THE SHEET ─────────────────────────────────────────
     The transcript rises ABOVE the dock rather than containing it, so the
     input never moves when the keyboard opens. */
  body.map-split.chat-open .trip-chat {
    /* The whole screen, edge to edge, and the dock lies ON TOP of it.
       Sizing the sheet to stop at the dock meant its bottom edge depended
       on a measurement — of a bar whose height changes with the safe area,
       the keyboard and the font — and every disagreement between the two
       numbers was a live strip of the page showing between them. A sheet
       that covers everything cannot have that seam. */
    top: 0;
    /* Exactly the visible area. The dock lies ON TOP of its lower edge, so
       there is still no seam — but with the keyboard open the sheet now
       ends where the screen does instead of running on underneath it,
       which is where the dead space at the bottom came from. */
    height: var(--vv-h, 100dvh);
    transform: translateY(var(--vv-top, 0px));
    border-radius: 0;
  }
  /* Only as much room as the dock resting over the last message needs. */
  body.map-split.chat-open #chat-messages,
  body.map-split.chat-open .sheet-ideas:not(.hidden) {
    /* Exactly what the dock covers, nothing over. Not its height: the two
       can disagree while an address bar is collapsing, and the height is
       the larger of them — which is where the dead band under the input
       after scrolling came from. See syncDockHeight. */
    padding-bottom: var(--dock-cover, var(--dock-h, 72px));
  }
  /* The ✕ floats over the content, so the content starts at the top —
     it was being pushed down into its own empty row. */
  body.map-split.chat-open #chat-messages { padding-top: 14px; }
  body.map-split.chat-open .sheet-ideas:not(.hidden) { padding-top: 12px; }
  .sheet-tabs { display: none; }
  body.map-split.chat-open .sheet-tabs {
    display: flex; gap: 8px; padding: 0 14px 10px;
    border-bottom: 1px solid var(--border-2); flex-shrink: 0;
  }
  .sheet-tab {
    min-height: 40px; padding: 8px 16px; border-radius: 999px;
    background: var(--bg); border: 1.5px solid var(--border-2);
    color: var(--text-2); font-family: 'Inter', sans-serif;
    font-size: 0.84rem; font-weight: 700; cursor: pointer;
  }
  .sheet-tab.active { background: var(--accent-bg); border-color: var(--accent); color: var(--accent); }

  .sheet-ideas { display: none; }
  body.map-split.chat-open .sheet-ideas:not(.hidden) {
    display: block; flex: 1; min-height: 0; overflow-y: auto;
    overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
    padding: 14px 12px 20px; background: var(--bg);
  }
  body.map-split.chat-open .sheet-ideas .highlights-section { margin-bottom: 0; }
  body.map-split.chat-open .sheet-ideas .section-title { font-size: 1.05rem; }

  /* ── 3. THE RE-CUT ────────────────────────────────────────
     Eight declarations on children that already exist. The route, the AI's
     voice and the missing places move onto the first screens; the two
     duplicated sections drop to the tail. One-line revert. */
  #results-section { display: flex; flex-direction: column; }
  .trip-header      { order: 1; }
  #route-strip      { order: 2; }
  #map-spacer,
  #map-container    { order: 3; }
  #ideas-rail       { order: 4; }
  .results-grid     { order: 5; }
  #hotels-section   { order: 6; }
  #score-card       { order: 7; }
  #saved-section    { order: 8; }
  /* The map is summoned, not resident: on a phone it opens from the route
     strip or any 🔍 and is otherwise out of the way. */
  body.has-dock #map-container { display: none; }
  /* Same specificity as the rule above plus one class, so summoning the map
     actually wins — the first version lost the cascade and the map stayed
     hidden while the page believed it was open. */
  body.has-dock.map-docked #map-container { display: block; }

  .route-strip {
    order: 2; display: block; width: 100%; margin: 0 0 18px;
    padding: 0; border: 1px solid var(--border); border-radius: var(--r-md);
    background: linear-gradient(150deg, #cfe0d6, #a8c9dd 70%, #93b8d6);
    height: 132px; position: relative; overflow: hidden; cursor: pointer;
    box-shadow: var(--shadow-sm);
  }
  .route-strip-label {
    position: absolute; left: 0; right: 0; bottom: 0; padding: 10px 14px;
    background: linear-gradient(transparent, rgba(28,23,20,.66));
    color: #fff; font-family: 'Inter', sans-serif; font-size: 0.84rem;
    font-weight: 700; text-align: left;
  }

  /* ── 4. BIGGER PHOTOS ─────────────────────────────────────
     +30% on every image in the plan: on a phone the photograph is what
     makes a place recognisable, and the old sizes were set for a column
     that had 90px of chrome around it. */
  .attr-chip-img { height: 291px; }
  .highlight-img { height: 260px; }
  .stop-hotel-thumb { width: min(195px, 46%); }
  .hotel-img { height: 234px; }

  /* ── 5. TAP TARGETS ───────────────────────────────────────
     44px is the floor on both platforms. The circles stay visually small
     where the layout is dense; the area that answers a finger does not. */
  .map-zoom-btn, .attr-remove {
    width: 36px; height: 36px; font-size: 0.92rem;
  }
  .map-zoom-btn::after, .attr-remove::after { inset: -4px; }
  .driving-row .map-zoom-btn { width: 36px; height: 36px; font-size: 0.82rem; }
  .driving-row .map-zoom-btn::after { inset: -4px; }
  .wx-toggle { width: 34px; height: 34px; }
  .wx-toggle::after { inset: -5px; }
  .hl-tab { min-height: 44px; }
  .highlight-add { min-height: 46px; }
  .map-collapse-btn { width: 44px; height: 44px; }

  /* ── 6. RAILS ─────────────────────────────────────────────
     The same .highlight-card, laid out horizontally with snapping. Used
     for the top rail and for each day's spur. */
  .rail {
    display: flex; gap: 12px; overflow-x: auto; scroll-snap-type: x mandatory;
    padding: 2px 2px 10px; margin: 0 -2px;
    overscroll-behavior-x: contain;
  }
  .rail > .highlight-card { flex: 0 0 74%; max-width: 300px; scroll-snap-align: start; }
  .rail-head {
    display: flex; align-items: baseline; gap: 10px; margin: 0 0 10px;
  }
  .rail-head h3 {
    font-family: 'Playfair Display', serif; font-size: 1.02rem; font-weight: 600;
    color: var(--text-1); margin: 0;
  }
  .rail-head .count { font-size: 0.78rem; font-weight: 700; color: var(--accent); }
  .rail-head .dismiss {
    margin-left: auto; width: 36px; height: 36px; border-radius: 50%;
    border: 1px solid var(--border-2); background: var(--surface);
    color: var(--text-3); font-size: 0.8rem; cursor: pointer;
  }
  .rail-all {
    flex: 0 0 132px; scroll-snap-align: start; display: flex; align-items: center;
    justify-content: center; text-align: center; padding: 12px;
    border: 1.5px dashed var(--border-2); border-radius: var(--r-md);
    background: var(--surface); color: var(--text-2);
    font-size: 0.84rem; font-weight: 700; cursor: pointer;
  }

  #ideas-rail { margin: 0 0 26px; }
  .day-spur { margin-top: 16px; padding-top: 14px; border-top: 1px dashed var(--border-2); }

  /* ── 7. THE TAIL ──────────────────────────────────────────
     Hotels and legs already appear inside each day; down here they are a
     reference, not the main text. */
  #hotels-section > .hotels-header .section-title::after,
  #driving-card > .card-header h3::after { content: ''; }
  #hotels-section, #driving-card { margin-bottom: 22px; }
}

/* ══════════════════════════════════════════════════════════════
   DOCK & RAILS — the look, not just the layout
   ══════════════════════════════════════════════════════════════
   Everything the design called for that the structural pass above did not
   cover: the strip that draws the real route, Trippy speaking in the feed,
   compact cards inside rails, and days that open with a numbered header
   instead of a card full of chrome. Still one media query; still nothing
   above this line modified. */

.trippy-rail { display: none; }

@media (max-width: 1099px) {

  /* ── The page as cards on a soft ground ───────────────────── */
  body.has-dock #app { background: var(--bg); }
  body.has-dock .card,
  body.has-dock .hotels-section,
  body.has-dock .trippy-rail,
  body.has-dock #ideas-rail {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    box-shadow: var(--shadow-sm);
    padding: 14px 12px;
  }
  body.has-dock #ideas-rail { padding-bottom: 6px; }
  body.has-dock .results-grid { gap: 16px; }

  /* The trip header is a card too, with the share button on its title row
     rather than lost among the badges. */
  body.has-dock .trip-header {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--r-md); box-shadow: var(--shadow-sm);
    padding: 14px 13px; margin-bottom: 16px; text-align: left;
  }
  body.has-dock .trip-title { font-size: 1.32rem; line-height: 1.2; margin: 0; text-align: left; }
  body.has-dock .trip-meta { justify-content: flex-start; margin-top: 9px; gap: 6px; }
  body.has-dock .meta-badge { padding: 5px 11px; font-size: 0.76rem; }
  body.has-dock .trip-meta .share-btn { margin-left: 0; padding: 7px 15px; font-size: 0.8rem; }

  /* ── The route strip: a drawing of THIS trip ─────────────── */
  .route-strip {
    background: linear-gradient(160deg, #d9e6dc 0%, #c6dbd0 45%, #b3cfdf 100%);
    padding: 0;
  }
  .route-strip-svg { position: absolute; inset: 0; width: 100%; height: 100%; }
  .route-strip-label {
    font-size: 0.8rem; font-weight: 600; letter-spacing: 0.1px;
    padding: 22px 13px 9px;
  }

  /* ── Trippy in the feed ───────────────────────────────────── */
  .trippy-rail {
    display: flex; gap: 10px; align-items: flex-start;
    order: 3; margin: 0 0 16px;
  }
  .trippy-avatar {
    flex: 0 0 auto; width: 28px; height: 28px; border-radius: 50%;
    background: linear-gradient(135deg, var(--accent), var(--accent-2));
    color: #fff; font-size: 0.68rem; font-weight: 800; letter-spacing: 0.4px;
    display: flex; align-items: center; justify-content: center;
  }
  .trippy-body { flex: 1; min-width: 0; }
  .trippy-line {
    margin: 0; font-size: 0.88rem; line-height: 1.5; color: var(--text-1);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  .trippy-chips { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 10px; }
  .trippy-chip {
    min-height: 40px; padding: 8px 14px; border-radius: 999px;
    background: var(--bg); border: 1.5px solid var(--border-2);
    color: var(--text-2); font-family: 'Inter', sans-serif;
    font-size: 0.8rem; font-weight: 600; cursor: pointer;
  }
  .trippy-chip:active { background: var(--accent-bg); border-color: var(--accent); color: var(--accent); }

  /* ── Section labels ───────────────────────────────────────── */
  .rail-head h3 {
    font-family: 'Inter', sans-serif; font-size: 0.7rem; font-weight: 800;
    letter-spacing: 0.09em; text-transform: uppercase; color: var(--text-3);
  }
  .rail-head .count {
    font-size: 0.7rem; font-weight: 800; color: var(--text-3); letter-spacing: 0.09em;
  }
  .rail-head .count::before { content: '· '; }

  /* ── Compact cards inside a rail ──────────────────────────── */
  .rail > .highlight-card { flex: 0 0 152px; }
  .rail .highlight-img { height: 120px; }
  .rail .highlight-body { padding: 10px 10px 11px; gap: 5px; }
  .rail .highlight-name { font-size: 0.88rem; line-height: 1.25; }
  .rail .highlight-why { display: none; }
  .rail .highlight-meta { font-size: 0.72rem; min-height: 0; }
  .rail .highlight-where { font-size: 0.72rem; min-height: 0; }
  .rail .highlight-add { min-height: 40px; padding: 9px 8px; font-size: 0.76rem; }
  .rail .highlight-card > .map-zoom-btn { top: 6px; right: 6px; }
  .rail-all { font-size: 0.8rem; font-weight: 700; line-height: 1.4; }

  /* ── A day opens with a header row ────────────────────────── */
  body.has-dock .stop-item { padding: 0; border: none; background: transparent; }
  body.has-dock .stop-number {
    width: 30px; height: 30px; min-width: 30px;
    font-size: 0.86rem; font-weight: 800;
  }
  body.has-dock .stop-desc { font-size: 0.86rem; }
  body.has-dock .day-spur { margin-top: 14px; }

  /* The tail sections keep their headings but lose the double frame. */
  body.has-dock #hotels-section .section-title,
  body.has-dock .card-header h3 { font-size: 1.02rem; }
}

@media (max-width: 1099px) {
  /* No send icon on an empty field: the first thing anyone does is tap the
     input anyway, and the button was taking 44px from a row that needs the
     width for words. It appears with the text. */
  body.has-dock .dock-form-slot .chat-send-btn { display: none; }
  body.has-dock.dock-typing .dock-form-slot .chat-send-btn { display: flex; }
}

/* ── THE DAY HEADER ─────────────────────────────────────────
   `.stop-inner` is a grid on every screen now: the number, the day's name
   and its dates on the first row, everything the day contains on the
   second. On a wide screen the body stays indented under the header the
   way it always was; on a phone it runs the full width of the card. */
.stop-inner {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 6px 13px;
  align-items: center;
}
.stop-number  { grid-column: 1; grid-row: 1; }
.stop-head    { grid-column: 2; grid-row: 1; min-width: 0;
                display: flex; align-items: baseline; gap: 10px; }
.stop-inner > .map-zoom-btn { grid-column: 3; grid-row: 1; }
.stop-info    { grid-column: 2 / -1; grid-row: 2; min-width: 0; }
.stop-head .stop-name { flex: 1; min-width: 0; margin: 0; }
.stop-head .stop-dates { flex: 0 0 auto; margin: 0; white-space: nowrap; }

@media (max-width: 1099px) {
  /* Full width under the header — a phone has none to give away. */
  body.has-dock .stop-info { grid-column: 1 / -1; }
  body.has-dock .stop-head .stop-name {
    font-family: 'Playfair Display', serif; font-size: 1.08rem; font-weight: 600;
  }
  body.has-dock .stop-head .stop-dates { font-size: 0.78rem; color: var(--text-3); }
  body.has-dock .stop-nights-tag { margin-left: 7px; }

  /* The plan is the page here, not a card inside it: one frame around the
     days, not a frame around a frame. */
  body.has-dock #description-card { padding: 4px 0 0; border: none; box-shadow: none; background: transparent; }
  body.has-dock #description-card > .card-header { display: none; }
  body.has-dock .stop-item {
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--r-md); box-shadow: var(--shadow-sm);
    padding: 13px 12px; margin-bottom: 14px;
  }
  body.has-dock .stops-list { gap: 0; }

  /* ── The sheet must be opaque ─────────────────────────────
     It floats over the plan, the route strip and the map. Anything less
     than a solid ground shows all three through it. */
  body.map-split.chat-open .trip-chat {
    background: var(--surface);
    backdrop-filter: none;
  }
  body.map-split.chat-open .chat-header,
  body.map-split.chat-open .sheet-tabs { background: var(--surface); }
  body.map-split.chat-open #chat-messages { background: var(--bg); }
  /* And nothing underneath may paint over it. */
  body.sheet-open .mobile-dock { z-index: 2200; }
  body.sheet-open #map-container { z-index: 1; }
}

@media (max-width: 1099px) {
  /* No fade for the sheet. .trip-chat carries an entrance animation from
     the days when it was a panel in the page; as a sheet covering the plan
     it spends that half second semi-transparent, and everything underneath
     — the map, the route strip, the cards — shows straight through it.
     It either covers the screen or it is not open. */
  body.map-split.chat-open .trip-chat { animation: none; }
  /* The header gradient fades out to nothing — fine on an opaque page, a
     window onto the plan at the top of a sheet. Here it fades to the
     surface colour instead. */
  body.map-split.chat-open .chat-header {
    background: linear-gradient(135deg, var(--accent-bg), var(--surface));
  }
}

@media (max-width: 1099px) {
  /* The suggestions are not a section of the plan any more — they are
     behind the ✨ Sugestie button, in the sheet, where the whole list and
     its three categories live. A rail in the feed and spurs at the end of
     every day said the same thing three times. */
  /* Off the plan page — but NOT wherever it happens to be. The section
     moves into the sheet, and a blanket hide followed it there and left the
     ✨ Sugestie button opening an empty screen. */
  body.has-dock #ideas-rail,
  body.has-dock #results-section > #highlights-section,
  body.has-dock .day-spur { display: none !important; }
  body.has-dock #sheet-ideas > #highlights-section { display: block !important; }

  /* The rating is a footnote to the plan, not a chapter of it: last, and
     shut until asked for. */
  body.has-dock .summary-section { order: 9; }
  body.has-dock #score-card {
    padding: 0; border: 1px solid var(--border); background: var(--surface);
  }
  body.has-dock #score-card > *:not(.score-toggle) { display: none; }
  body.has-dock #score-card.is-open > *:not(.score-toggle) { display: block; padding: 0 12px 14px; }
  body.has-dock #score-card.is-open > .trip-description { padding-top: 4px; }
  body.has-dock .score-toggle {
    display: flex; align-items: center; gap: 10px; width: 100%;
    min-height: 52px; padding: 14px 12px; cursor: pointer;
    background: none; border: none; text-align: left;
    font-family: 'Playfair Display', serif; font-size: 1.02rem; font-weight: 600;
    color: var(--text-1);
  }
  body.has-dock .score-toggle .caret { margin-left: auto; color: var(--text-3); font-size: 0.9rem; }
}
.score-toggle { display: none; }

@media (max-width: 1099px) {
  /* The sheet carries one control: close. Its title, its badge and its tabs
     all named things the reader had just pressed to get here. */
  /* No header bar at all. It held one control, and its own background was
     one more edge for the page underneath to show at. The ✕ floats over
     the content instead, in the corner where it is expected. */
  body.map-split.chat-open .chat-header { display: none; }

  body.map-split.chat-open .chat-close-btn {
    display: flex; position: absolute; top: 10px; right: 10px; z-index: 20;
    width: 44px; height: 44px; border-radius: 50%;
    background: var(--surface); border: 1.5px solid var(--border-2);
    color: var(--text-2); font-size: 0.95rem;
    box-shadow: 0 2px 10px rgba(28,23,20,.16);
  }
  /* Room for it, so nothing starts underneath the button. */

  /* The dock does not rearrange itself when the sheet opens. Hiding the
     field and stretching the button meant the bar changed shape under the
     thumb that had just pressed it. */

  /* Suggestions first: it is the button people press without having
     anything to say yet. */
  .dock-ideas { order: 1; }
  .dock-form-slot { order: 2; }
}

@media (max-width: 1099px) {
  /* The map lives in the page at thumbnail height, showing the real route,
     and expands to the top strip when tapped. */
  body.has-dock #map-container {
    display: block; order: 2; position: relative;
    /* The same height as when it is opened — 34vh. A thumbnail small enough
       to save space was small enough to be unreadable, and opening it only
       to close it again was the tax on that. Tapping it now pins it to the
       top rather than growing it. */
    height: 34vh; margin: 0 0 18px; padding: 0;
    border-radius: var(--r-md); overflow: hidden; cursor: pointer;
  }
  body.has-dock #map-container #map { height: 34vh; }
  body.has-dock #map-container .map-footer,
  body.has-dock #map-container .map-legend { display: none; }
  body.has-dock.map-docked #map-container {
    position: fixed; top: 0; left: 0; right: 0; height: auto;
    margin: 0; border-radius: 0; z-index: 900;
  }
  body.has-dock.map-docked #map-container #map { height: 34vh; }
  body.has-dock.map-docked #map-container .map-footer { display: flex; }
  body.has-dock #map-spacer { display: none; }
  /* Exactly the height the map gives up when it leaves the flow — dockMap
     measures it into --map-h. A fixed 150px meant the page jumped by the
     difference every time a pin was opened, which read as the whole page
     flashing. */
  body.has-dock.map-docked #map-spacer { display: block; height: var(--map-h, 318px); }

  .map-caption {
    position: absolute; left: 0; right: 0; bottom: 0; z-index: 500;
    padding: 20px 12px 8px;
    background: linear-gradient(transparent, rgba(28,23,20,.62));
    color: #fff; font-size: 0.78rem; font-weight: 700; pointer-events: none;
  }
  body.map-docked .map-caption { display: none; }
}

@media (max-width: 1099px) {
  /* Two controls, two jobs: a button that opens a list of places, and a
     field for talking to the model. Side by side and equally weighted they
     read as one compound widget, so they are separated by a rule and given
     different shapes — the button solid and self-contained, the field an
     outline that invites typing. */
  .dock-row { gap: 0; align-items: center; }
  /* 7px each side of the rule rather than 10. The divider is what separates
     them — the air around it was doing the same job twice, and the width it
     used up is width the two controls now sit inside the screen's curve
     with. */
  .dock-ideas { margin-right: 7px; border-radius: 999px; padding: 10px 15px; }
  .dock-form-slot {
    padding-left: 7px;
    border-left: 1px solid var(--border-2);
  }
  .dock-form-slot .chat-input { background: var(--bg); }
}

@media (max-width: 1099px) {
  /* What is left where an attraction was: its name, and the way back. */
  .attr-undo {
    display: flex; align-items: center; gap: 10px;
    padding: 12px 13px; border: 1px dashed var(--border-2);
    border-radius: var(--r-md); background: var(--bg);
    font-size: 0.84rem; color: var(--text-2);
  }
}
.attr-undo {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 13px; border: 1px dashed var(--border-2);
  border-radius: var(--r-md); background: var(--bg);
  font-size: 0.84rem; color: var(--text-2);
  animation: fadeIn 0.25s var(--ease);
}
.attr-undo span { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.attr-undo button {
  flex: 0 0 auto; min-height: 40px; padding: 9px 14px;
  border-radius: var(--r-sm); cursor: pointer;
  background: var(--accent-bg); border: 1.5px solid rgba(208,92,37,0.28);
  color: var(--accent); font-family: 'Inter', sans-serif;
  font-size: 0.8rem; font-weight: 700;
}
.attr-undo button:hover { background: rgba(208,92,37,0.14); border-color: var(--accent); }

/* ── THE LANDING PAGE ON A PHONE ────────────────────────────
   One promise, one field, one date range, four price levels, one button.
   Everything is sized for a thumb and for reading at arm's length; nothing
   competes with the destination field, which is the only thing that has to
   be filled in for the app to do anything at all. */
@media (max-width: 1099px) {
  .input-card { padding: 26px 16px 24px; }
  .input-header { margin-bottom: 24px; }
  .input-header h1 {
    font-size: clamp(1.7rem, 8vw, 2.15rem); line-height: 1.12;
    text-wrap: balance; margin-bottom: 12px;
  }
  .input-subtitle {
    font-size: 0.95rem; line-height: 1.62; color: var(--text-2);
    max-width: 36ch; margin: 0 auto;
  }

  .form-grid { gap: 22px; }
  .form-group label { font-size: 0.7rem; letter-spacing: 0.09em; }
  #destination-input {
    min-height: 54px; padding: 14px 16px; border-radius: var(--r-md);
  }
  .field-hint { font-size: 0.78rem; line-height: 1.5; }

  .date-row { gap: 10px; }
  .date-field input[type="date"] { min-height: 54px; border-radius: var(--r-md); }
  .date-label { font-size: 0.72rem; }

  .budget-section { margin-bottom: 28px; }
  .budget-grid { gap: 10px; }
  .budget-btn { min-height: 62px; padding: 12px 8px; border-radius: var(--r-md); }
  .budget-btn .budget-label { font-size: 0.86rem; }
  .budget-btn .budget-desc { font-size: 0.7rem; }

  .generate-btn { min-height: 58px; font-size: 1rem; border-radius: var(--r-md); }

  /* Before a trip exists the chat is the second thing on the page, not a
     panel competing with the form. */
  body:not(.map-split) .trip-chat { margin-top: 22px; }
}

/* ── THE LANDING PAGE, STRIPPED ─────────────────────────────
   Before a trip exists the page has one job: take a region and a date
   range. A logo, a tagline, a headline and a name for the chat were four
   pieces of furniture around a form with three fields. The settings gear
   stays — it is the only other control that does anything here. */
@media (max-width: 1099px) {
  body:not(.map-split) #header .logo,
  body:not(.map-split) #header .tagline { display: none; }
  body:not(.map-split) #header { padding-top: 10px; padding-bottom: 0; min-height: 0; }
  body:not(.map-split) .header-inner { justify-content: flex-end; }

  /* The chat introduces itself by being a chat. */
  body:not(.map-split) .chat-header .chat-header-text,
  body:not(.map-split) .chat-header .chat-ai-badge { display: none; }
  body:not(.map-split) .chat-header { padding: 0; border-bottom: none; min-height: 0; }

  body:not(.map-split) .input-header { margin-bottom: 20px; }
  body:not(.map-split) .input-subtitle { max-width: 40ch; }
}

/* ── A BACKDROP UNDER THE SHEET ─────────────────────────────
   Chasing individual gaps was the wrong fix: a map that repaints, a strip
   that peeks at the rounded corners, a keyboard that changes the sheet's
   height mid-animation — each one shows a slice of the page. So the page
   gets covered. One opaque layer between the plan and the sheet, and
   nothing underneath can show through any of them. */
@media (max-width: 1099px) {
  /* On #app, not on body. #app carries `position: relative; z-index: 1`,
     which makes it a stacking context — a backdrop outside it covers
     everything inside it, including the sheet and the dock it was meant to
     sit behind. Inside that context the three layers stack in the order
     they are read: page, backdrop, sheet, dock. */
  body.sheet-open #app::before {
    content: '';
    /* Every edge of the screen, with no arithmetic. The earlier version
       was sized from the visual viewport, which is right for the sheet —
       it must sit above the keyboard — and wrong for a backdrop: any
       moment where the measurement lags the browser (the URL bar
       collapsing, the keyboard animating, a rotation) leaves a live strip
       of the page showing at an edge. inset:0 cannot lag. */
    position: fixed;
    inset: -10px;                 /* past the edges, so no rounding gap */
    background: var(--bg);
    z-index: 1500;
    pointer-events: none;
  }
  body.sheet-open.map-split.chat-open .trip-chat { z-index: 1600; }
  body.sheet-open .mobile-dock { z-index: 1700; }
}

/* Whatever else is said about it, hidden means hidden. */
.mobile-dock[hidden] { display: none !important; }

/* ── DESKTOP: THE VERDICT COMES LAST ────────────────────────
   The rating and its pros and cons are an opinion about the plan, so they
   belong after everything they are an opinion about — including the
   suggestions, which are still part of choosing what the trip contains. */
@media (min-width: 1100px) {
  #results-section { display: flex; flex-direction: column; }
  .trip-header       { order: 1; }
  #map-spacer,
  #map-container     { order: 2; }
  .results-grid      { order: 3; }
  /* Suggestions before the hotel list: what to add to the trip is still a
     decision about the trip, while the hotel summary and the rating are
     both recaps of one already made. */
  #highlights-section { order: 4; }
  #hotels-section    { order: 5; }
  .summary-section   { order: 6; }
  #saved-section     { order: 7; }
}

/* ── THE RESTART BAND ───────────────────────────────────────
   A strip above the plan holding one control. The page opens below it, so
   it costs nothing until someone pulls the plan down looking for a way
   out — the gesture people already make when they want to start again. */
.restart-band {
  order: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; padding: 26px 16px 30px; text-align: center;
}
.restart-btn {
  min-height: 50px; padding: 13px 26px; border-radius: 999px; cursor: pointer;
  background: var(--surface); border: 1.5px solid var(--border-2);
  color: var(--text-1); font-family: 'Inter', sans-serif;
  font-size: 0.92rem; font-weight: 700;
  box-shadow: var(--shadow-sm);
  transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}
.restart-btn:hover { border-color: var(--accent); color: var(--accent); }
.restart-note { font-size: 0.8rem; color: var(--text-3); }

@media (max-width: 1099px) {
  /* With a trip on screen the form is a different screen, not the top of
     this one. Leaving it above the plan meant "scroll up" landed in a
     half-filled form; the restart band is the whole of what is up there
     now, and pressing it brings the form back. */
  body.has-dock #input-section { display: none; }
  /* Tall enough to be a deliberate pull, not so tall that the plan feels
     buried under empty space. */
  body.has-dock .restart-band { min-height: 30vh; }
}

@media (max-width: 1099px) {
  /* Room for an open pin card — see fitMapToPopup(). */
  body.has-dock.map-popup-open #map-container,
  body.has-dock.map-popup-open #map-container #map { height: var(--map-open-h, 34vh); }
  /* No transition: the map is an overlay now, and animating its height
     only gives the page something to repaint against. */
}

@media (max-width: 1099px) {
  /* The bar under the map holds two controls and was as tall as a section
     header. 40% shorter: the map keeps the pixels instead. */
  body.has-dock .map-footer { padding: 5px 10px; gap: 8px; min-height: 0; }
  body.has-dock .gmaps-btn { padding: 5px 12px; font-size: 0.76rem; }
  body.has-dock .gmaps-btn svg { width: 13px; height: 13px; }
  body.has-dock .map-collapse-btn { width: 34px; height: 30px; }
}

@media (max-width: 1099px) {
  /* The bar under the map is always there now — it holds the one link
     worth having next to a route — and it is small. */
  body.has-dock #map-container .map-footer {
    display: flex; justify-content: flex-end; padding: 5px 8px;
    background: var(--surface); border-top: 1px solid var(--border-2);
  }
  body.has-dock #map-container .map-legend { display: none; }
  body.has-dock .gmaps-btn { padding: 6px 12px; font-size: 0.74rem; }
  body.has-dock .map-caption { display: none; }
  /* Room for the bar, so the map itself keeps its full height. */
  body.has-dock #map-container { height: auto; }
}

@media (max-width: 1099px) {
  /* Whenever the map is taller than its resting height there is a way to
     put it back — floating on the map itself, not in the bar, so it is
     within reach of the thumb that just opened the pin. */
  body.has-dock.map-popup-open .map-collapse-btn,
  body.has-dock.map-docked .map-collapse-btn {
    /* Positioned against the map container, not the footer it lives in —
       the footer is a flex row that pushed it past the right edge. */
    display: flex; position: fixed; top: 10px; right: 12px; z-index: 950;
    width: 40px; height: 40px; border-radius: 50%;
    background: var(--surface); border: 1.5px solid var(--border-2);
    box-shadow: 0 2px 10px rgba(28,23,20,.18);
  }
}

@media (max-width: 1099px) {
  /* Square corners while it is a sheet: a rounded corner is a hole with
     the page behind it. */
  body.map-split.chat-open .trip-chat { border-radius: 0; }
}

@media (max-width: 1099px) {
  /* Above the plan there is one control and nothing else — no logo, no
     tagline, no gear. Pulling up is a single, unambiguous gesture. */
  body.has-dock #header { display: none; }
  body.has-dock .restart-band { padding-top: 34px; }
}

/* ── NO MASTHEAD ────────────────────────────────────────────
   The logo and the tagline said what the page below already demonstrates,
   on every screen and at every step. The settings gear stays — it is a
   control, not decoration — and now it is the whole header. */
#header .logo, #header .tagline { display: none; }
#header { padding: 14px 0 0; min-height: 0; }
#header .header-inner { justify-content: flex-end; align-items: center; }

@media (max-width: 1099px) {
  /* The form is a screen you leave, and its button leaves with it. It sits
     outside #input-section in the markup, which is why hiding that section
     left the button behind under the finished plan. */
  body.has-dock .generate-row,
  body.has-dock .trip-chat:not(.chat-open) { display: none; }
  body.has-dock.chat-open .trip-chat { display: flex; }

  /* The band above the plan is a door, not a room: enough height to be
     found by pulling down, no more. */
  body.has-dock .restart-band { min-height: 0; padding: 16px 16px 20px; }
}

@media (max-width: 1099px) {
  /* Summoned while the suggestions are open, the map lies over them —
     above the sheet and its backdrop, below the dock, which stays the way
     out of both. */
  /* !important because this competes with the resting docked-map rule at
     the same specificity, and which one wins would otherwise depend on
     where in this file each block happens to sit. */
  body.sheet-open.map-docked #map-container { z-index: 1650 !important; }
  body.sheet-open.map-docked #map-container .map-footer { display: flex; }
}

@media (max-width: 1099px) {
  /* The map is the reference for the ✕ whether it is pinned to the top or
     sitting in the page. */
  body.has-dock #map-container { position: relative; }
  body.has-dock.map-popup-open .map-collapse-btn { position: absolute; }
  body.sheet-open.map-docked .map-collapse-btn { z-index: 1660; }
}

/* ── TAP TARGETS, SWEPT ─────────────────────────────────────
   Everything a finger is meant to hit, measured on a 375px screen and
   brought to 44px. Text links inside cards keep their type size but gain
   the padding that makes them hittable. */
@media (max-width: 1099px) {
  .trip-meta .share-btn { min-height: 44px; padding: 11px 18px; }
  body.has-dock .gmaps-btn { min-height: 40px; padding: 10px 14px; }
  .hotel-alt-btn, .bulk-btn { min-height: 44px; }
  .map-zoom-btn, .attr-remove { width: 40px; height: 40px; }
  .map-zoom-btn::after, .attr-remove::after { inset: -2px; }
  .stop-hotel-site, .hotel-btn, .mp-hotel-book, .highlight-alltrails {
    display: inline-flex; align-items: center; min-height: 40px;
  }
}

@media (max-width: 1099px) {
  /* The 🔍 in a driving row was the last control under 44px — an earlier
     narrow-screen rule had it at 34. Stated last so it wins. */
  .driving-row .map-zoom-btn { width: 40px; height: 40px; font-size: 0.86rem; }
  .driving-row .map-zoom-btn::after { inset: -2px; }
}

@media (max-width: 1099px) {
  /* The card-corner variants are positioned absolutely and were still
     sized by the 30px rule that put them there. Stated last, with the
     enlarged hit area, so every 🔍 and ✕ on the page is 44px to a finger. */
  .attr-chip > .map-zoom-btn,
  .highlight-card > .map-zoom-btn,
  .stop-hotel-mini > .map-zoom-btn,
  .attr-chip > .attr-remove { width: 38px; height: 38px; }
  .attr-chip > .map-zoom-btn::after,
  .highlight-card > .map-zoom-btn::after,
  .stop-hotel-mini > .map-zoom-btn::after,
  .attr-chip > .attr-remove::after { inset: -3px; }
}

@media (max-width: 1099px) {
  /* While a card is open the pinned map takes the height fitMapToPopup
     asked for; the page underneath is held by the spacer and does not
     move. */
  body.has-dock.map-docked.map-popup-open #map-container #map { height: var(--map-open-h, 34vh); }
  /* Pins fade rather than blink when the map is re-framed. */
  body.has-dock .leaflet-marker-icon { transition: opacity 0.18s var(--ease); }
}

/* ── WHILE A SHEET IS OPEN, THE PAGE IS NOT PAINTED ─────────
   Every previous attempt covered the page: an opaque panel, then a
   backdrop, then a backdrop past the edges. Each one still depended on
   something — a stacking context, a measured height, a transform on an
   ancestor, the order two equal-specificity rules happen to sit in — and
   any of those failing shows a strip of the plan behind the chat.

   This does not cover the page; it stops the page being drawn at all.
   `visibility: hidden` keeps the layout (so the scroll position survives)
   and paints nothing, so there is nothing left to see through, under or
   between anything. The map is the one exception: it can be summoned from
   inside the sheet and has to remain visible when it is. */
@media (max-width: 1099px) {
  body.sheet-open #header,
  body.sheet-open #input-section,
  body.sheet-open .generate-row,
  body.sheet-open #results-section,
  body.sheet-open #saved-section { visibility: hidden; }

  body.sheet-open.map-docked #results-section,
  body.sheet-open.map-docked #map-container,
  body.sheet-open.map-docked #map-container * { visibility: visible; }
  body.sheet-open.map-docked #results-section > *:not(#map-container) { visibility: hidden; }
}

/* ── ON DESKTOP TOO: NOTHING ABOVE THE RESTART ──────────────
   With a plan on screen the form has done its job. Leaving it, its button
   and the header above the plan meant scrolling up from a finished trip
   landed in a half-filled form — the same thing that was fixed on the
   phone. The whole of what is up there is one control; the form comes back
   when it is pressed. */
@media (min-width: 1100px) {
  body.map-split #header,
  body.map-split #input-section,
  body.map-split .generate-row { display: none; }
  body.map-split .restart-band { padding: 20px 16px 26px; }
}

/* ── DESKTOP: THE PLAN'S OWN CONTROLS, TOGETHER ────────────
   Share and Start over both act on the whole trip, so they sit with the
   line that describes the whole trip — days, hours, kilometres — instead
   of one hiding in the chat panel and the other in a band of its own. */
@media (min-width: 1100px) {
  /* The chat is the conversation, nothing else. Its header carried a name,
     a tagline and a share button above every message — a title bar for a
     panel that is already unmistakable. */
  .chat-header { display: none; }

  /* What stays, once there is a plan to talk about: the badge alone,
     floating in the corner. It is a label for the panel, not a bar across
     it — so it sits over the conversation rather than pushing it down, and
     lets clicks through to whatever is underneath. */
  body.map-split .chat-header {
    display: block; position: absolute; top: 12px; left: 14px; z-index: 5;
    padding: 0; min-height: 0; border-bottom: none; background: none;
    pointer-events: none;
  }
  body.map-split .chat-header-text { display: none; }
  body.map-split .chat-ai-badge { display: inline-flex; }
  /* Room for it, so the first message does not start underneath. */
  body.map-split #chat-messages { padding-top: 48px; }

  .trip-meta { align-items: center; gap: 10px; }
  .trip-meta .share-btn { margin-left: 4px; }
  .trip-meta .restart-btn {
    min-height: 0; padding: 8px 16px; font-size: 0.82rem;
  }

  /* Empty now that its button stands in the summary. */
  .restart-band { display: none; }
}

.chat-ai-badge-icon {
  width: 14px; height: 14px; fill: currentColor; flex-shrink: 0;
}

/* ── ON A PHONE THE LEG PILL IS JUST THE CAR ────────────────
   The map is a 34vh strip; a pill reading '75 km · 1h 20m' is wider than
   the leg it labels and lies across the road it is describing. The car
   alone marks the drive, and tapping it gives the numbers in full — which
   is where they were being read from anyway. */
@media (max-width: 1099px) {
  .route-distance-text { display: none; }
  /* A circle, sized rather than padded. With the numbers gone the pill was
     a block in a zero-width marker: its box collapsed to the padding, the
     car overflowed out of the side of it, and what was left read as an
     ellipse standing on its end. Fixed dimensions and a centring flexbox
     give the same shape every stop marker has, with the car in the middle
     of it. */
  .route-distance-pill {
    position: relative;
    display: flex; align-items: center; justify-content: center;
    /* The same 26px circle every attraction pin is, so a map of stops,
       sights and drives reads as one set of marks rather than one of them
       shouting. */
    width: 26px; height: 26px; padding: 0;
    border-radius: 50%; font-size: 0.7rem; line-height: 1;
  }
  /* Without its numbers the pill is 18px across — a target the size of the
     car inside it. The reach around it is invisible and 44px square. */
  .route-distance-pill::after {
    content: ""; position: absolute; top: 50%; left: 50%;
    width: 44px; height: 44px; transform: translate(-50%, -50%);
  }
}

/* ── SUGGESTIONS READ AS A LAYER, NOT A PAGE ────────────────
   The sheet covered the screen edge to edge in the same colour as the page
   behind it, so opening it looked like navigating somewhere — and closing
   it looked like navigating back. Nothing said the plan was still there,
   underneath, waiting.

   The layer it stands on is already there: an opaque backdrop that keeps
   the plan from showing through. Darkening THAT, rather than adding
   anything, is what makes the sheet legible as a sheet — a card lifted off
   a dimmed ground, held clear of three edges, with a handle at the top.
   No new layer means no new seam for the page to show at. */
@media (max-width: 1099px) {
  body.sheet-open.sheet-ideas-open #app::before {
    /* Opaque twice over: a flat wash composited onto the page colour, not
       a transparency that would let the plan glimmer through. */
    background: linear-gradient(rgba(32,25,20,0.52), rgba(32,25,20,0.52)), var(--bg);
  }

  body.sheet-open.sheet-ideas-open .trip-chat {
    top: 32px; left: 10px; right: 10px; width: auto;
    height: calc(var(--vv-h, 100dvh) - 32px);
    border: 1px solid var(--border-2); border-bottom: none;
    border-radius: 20px 20px 0 0;
    box-shadow: 0 -4px 30px rgba(20,15,12,0.4);
    overflow: hidden;
  }

  /* The grab bar. It does not drag — the dock button and the ✕ both close
     the sheet — but it is the mark every phone uses for 'this slides over
     what you were reading', and it costs four pixels. */
  body.sheet-open.sheet-ideas-open .trip-chat::before {
    content: ""; position: absolute; top: 10px; left: 50%;
    width: 38px; height: 4px; margin-left: -19px;
    border-radius: 99px; background: var(--border-2); z-index: 3;
  }

  /* :not(.hidden) is here for weight, not for filtering. The rule that sets
     this element's padding higher up carries it, so without it that
     shorthand wins on specificity and puts the padding back to 20px —
     which left 'Pokaż kolejne 10' stranded underneath the dock with no
     way to scroll it clear. */
  body.sheet-open.sheet-ideas-open .sheet-ideas:not(.hidden) {
    padding-top: 26px;
    /* The dock lies over the sheet's lower edge, so the last card needs
       its height to clear it — plus a little, because what ends up down
       here is 'Pokaż kolejne 10', and a button resting flush against the
       bar above it reads as part of the bar. */
    padding-bottom: calc(var(--dock-cover, var(--dock-h, 72px)) + 12px);
  }
}

/* ── SMALLER CARDS ON A SMALL MAP ───────────────────────────
   The map is a strip on a phone, and a pin's card was taking most of it:
   the map had to grow to fit the card, so the bigger the card the less of
   the route stayed visible around whatever had just been tapped. */
@media (max-width: 1099px) {
  /* Scaled, not cropped. Taking the height down on its own left the frame
     the same width and half as tall, so object-fit: cover did what it is
     supposed to do and sliced the top and bottom off every photo — the
     picture was not smaller, it was less of a picture. The card comes in
     with it, at the same ratio, so what is inside the frame is exactly what
     was there before, drawn smaller.

       two-up:  131x154 -> 87x102     single: 264x185 -> 176x123
     */
  .mp { width: 176px; }
  /* And the bubble around it. Leaflet is told the content is 264px wide by
     a rule further up — with !important, so its own measurement never gets
     a say — and narrowing only the card left the difference as bare white
     down the right-hand side. Leaflet reads this width when it lays the
     popup out, so the bubble is both the right size and still centred on
     its pin. */
  /* The bubble takes its width from the card inside it, whatever that card
     decided to be. Naming each width here meant one rule per kind of card,
     each of them selected with :has() — and on a browser without :has()
     every one of them was dropped while the plain 176px rule above stayed,
     clamping the wide card back into a narrow column. auto needs no
     support: the popup is absolutely positioned, so it shrink-wraps, and
     Leaflet measures the result when it places it. */
  .rich-popup .leaflet-popup-content { width: auto !important; }

  /* 30% wider for the cards that carry a paragraph — 176 -> 229 — and the
     photo grows with it at the same ratio, so the wide card is the narrow
     one scaled up rather than a different crop.
       two-up: 113x133   single: 229x160 */
  .mp.mp-wide { width: 229px; }
  .mp-wide .mp-photos { height: 133px; }
  .mp-wide .mp-photos.mp-single { height: 160px; }

  /* Except the card with a hotel under it, where the picture is the least
     useful thing on screen and scaling it with the width was adding more
     height than the width had just saved. Still larger than the same photo
     on a narrow card — 229x130 against 176x123 — just not the full scale. */
  .mp-stay .mp-photos.mp-single { height: 130px; }
  .mp-photos { height: 102px; }
  .mp-photos.mp-single { height: 123px; }

  /* The leg card carries four short lines and no picture, so most of what
     it occupied was margin. Narrower, tighter, and the type down a notch —
     but only a notch: shrinking 0.72rem by the same proportion as the box
     would leave it unreadable. */
  .mp-leg { width: 158px; padding: 8px 10px; }
  .mp-leg-day { font-size: 0.64rem; letter-spacing: 0.3px; }
  .mp-leg-route { font-size: 0.78rem; margin: 2px 0 4px; line-height: 1.3; }
  .mp-leg-figures { font-size: 0.74rem; }
}

/* ── THE MICHELIN FILTER ────────────────────────────────────
   Three chips, any combination of them, on the restaurants tab only. The
   red belongs to the Guide, so it stays on the stars themselves at every
   state — pressing a chip tints the ground beneath them rather than
   inverting the whole pill, which had three solid red slabs sitting above
   the intro text and shouting louder than the results they filter. */
.michelin-filter {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
  margin: 14px 0 0;
}
/* The intro below carries a -10px pull-up, from when the tabs sat directly
   on top of it. With this row in between, that pull dragged the first line
   of text up UNDER the chips — an 8px overlap, and the chips' invisible
   touch reach extended further into it still. Cancelled while the row is
   showing; the other two tabs keep the tighter spacing they were tuned
   for. Adjacent sibling rather than :has(), so it needs no support. */
.michelin-filter:not(.hidden) + .highlights-intro { margin-top: 12px; }
.michelin-label {
  margin-right: 2px;
  font-size: 0.68rem; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--text-3);
}
.michelin-chip {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 32px; min-width: 44px; padding: 5px 12px; cursor: pointer;
  border-radius: 999px; border: 1px solid var(--border-2);
  background: var(--surface); color: var(--michelin);
  font-family: 'Inter', sans-serif; font-size: 0.8rem; line-height: 1;
  letter-spacing: 1.5px;
  /* Colour only, and not the background: whether a chip is on is the one
     thing this control has to say, and it should not be arriving over a
     fifth of a second. */
  transition: border-color .18s var(--ease), box-shadow .18s var(--ease);
}
.michelin-chip:hover { border-color: var(--michelin-soft); }
.michelin-chip.active {
  background: var(--michelin-bg);
  border-color: var(--michelin-soft);
  box-shadow: inset 0 0 0 1px var(--michelin-soft);
}
.michelin-clear {
  min-height: 32px; padding: 5px 10px; margin-left: 2px; cursor: pointer;
  border-radius: 999px; background: none; border: none; color: var(--text-3);
  font-family: 'Inter', sans-serif; font-size: 0.76rem;
}
.michelin-clear:hover { color: var(--text-1); background: var(--bg-2); }

/* On the card: the stars, and the link that settles whether they are real.
   Set as one quiet line — the claim is the model's, not Google's, and it
   should not out-shout the verified rating sitting under it. */
.michelin-badge {
  display: flex; align-items: center; flex-wrap: wrap; gap: 7px;
  margin: 4px 0 2px;
}
.michelin-stars {
  color: var(--michelin); font-size: 0.8rem; letter-spacing: 1.5px; line-height: 1;
}
.michelin-badge a {
  font-size: 0.68rem; color: var(--text-3); text-decoration: none;
  border-bottom: 1px dotted var(--border-2); padding-bottom: 1px;
}
.michelin-badge a:hover { color: var(--michelin); border-bottom-color: var(--michelin-soft); }

@media (max-width: 1099px) {
  /* A finger, not a cursor — reach without bulk: the pill keeps its size
     and grows an invisible 44px target around itself. */
  .michelin-chip, .michelin-clear { position: relative; }
  .michelin-chip::after, .michelin-clear::after {
    content: ""; position: absolute; inset: -6px;
  }
  .michelin-badge a { padding: 4px 0; }
}}
