/* ═══════════════════════════════════════════════════════
   Feature 4: Map-based search view (Leaflet + markercluster)
   Dark-theme overrides, custom price pins, bottom-sheet
   preview, layer-filter overlay. Loaded via index.html.
   ═══════════════════════════════════════════════════════ */

/* ── Feature 4c: three-way view toggle (Swipe / Map / List)
   Segmented pill-group control. Two-way variant on the Likes
   screen, three-way on Discover. Buttons shrink padding on
   mobile so the three-way group fits a 375px viewport without
   horizontal scroll. */
.feed-view-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  padding: 6px 16px 2px;
}
.feed-view-toggle-group {
  display: inline-flex;
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: var(--radius-full);
  padding: 3px;
  gap: 2px;
}
.feed-view-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 32px;
  padding: 0 11px;
  border: none;
  background: transparent;
  border-radius: var(--radius-full);
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 160ms ease, color 160ms ease, transform 140ms ease;
}
.feed-view-btn:hover { color: var(--text); }
.feed-view-btn:active { transform: scale(0.96); }
.feed-view-btn.active {
  background: var(--primary);
  color: #ffffff;
  box-shadow: 0 2px 8px rgba(99, 102, 241, 0.35);
}
.feed-view-btn i { width: 14px; height: 14px; }

/* ── Feature 4e: Zillow-style combined map + drawer ────────────
   The Discover map is a full-height container with the Leaflet
   viewport filling the top and a pull-up drawer docked at the
   bottom. Three snap points (peek 32% / medium 58% / full 92%)
   let the user balance map vs. list focus. Card swipe gestures
   and a draggable grab-handle are wired up in mapView.ts; this
   block defines the visual layout + transitions only.
   ───────────────────────────────────────────────────────────── */

.map-drawer {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: calc(var(--drawer-pct) * 1%);
  background: var(--surface);
  border-top: 1px solid var(--border-strong);
  border-radius: 18px 18px 0 0;
  box-shadow: 0 -12px 32px rgba(0, 0, 0, 0.55);
  display: flex;
  flex-direction: column;
  z-index: 550;
  transition: height 320ms cubic-bezier(0.2, 0.8, 0.2, 1);
  overflow: hidden;
}

/* Grab handle — tappable strip with a short horizontal bar.
   44px tall to meet the touch-target min; the visible bar is
   ~5px so the ergonomics feel right without eating drawer real
   estate. */
.map-drawer-handle {
  flex-shrink: 0;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: grab;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
}
.map-drawer-handle:active { cursor: grabbing; }
.map-drawer-grab {
  width: 52px;
  height: 5px;
  border-radius: 3px;
  background: var(--text-tertiary);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
  transition: background-color 160ms ease, width 160ms ease, box-shadow 160ms ease;
}
.map-drawer-handle:hover .map-drawer-grab {
  background: var(--text-muted);
  width: 60px;
}
.feed-map-view.drawer-dragging .map-drawer-grab {
  background: var(--primary-light);
  width: 60px;
  box-shadow: 0 0 8px rgba(99, 102, 241, 0.4);
}

/* Count header — "47 homes in this area" */
.map-drawer-header {
  flex-shrink: 0;
  padding: 0 16px 8px;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border-bottom: 1px solid var(--border);
}

.map-drawer-scroll {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 8px 12px 24px;
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
  /* Allow vertical scroll; horizontal pointer moves are handled
     by installCardSwipeGestures with axis locking. */
  touch-action: pan-y;
}
.map-drawer-scroll::-webkit-scrollbar { width: 4px; }
.map-drawer-scroll::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: 2px;
}

/* Drawer loading spinner (first mount) */
.map-drawer-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px 0;
}

/* Drawer empty state (no listings in the visible viewport) */
.map-drawer-empty {
  text-align: center;
  padding: 36px 20px;
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: 13px;
}
.map-drawer-empty i { color: var(--text-tertiary); }
.map-drawer-empty-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--text);
  margin-top: 10px;
}
.map-drawer-empty-body {
  margin-top: 4px;
  font-size: 12px;
  color: var(--text-muted);
}

/* ── Map-drawer listing card ───────────────────────────
   Compact card with photo + details + action buttons, tuned for
   mobile list density (~96px tall). Uses its own class name
   (.map-card) rather than reusing .listing-item so the swipe
   gesture transforms don't cross-pollute the Likes list UI. */
.map-card {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--card-border);
  border-radius: 14px;
  margin-bottom: 10px;
  overflow: hidden;
  cursor: pointer;
  transition: border-color 160ms ease, box-shadow 160ms ease;
  touch-action: pan-y;
  will-change: transform;
}
.map-card:active:not(.swiping) {
  border-color: var(--primary-border);
}
.map-card.swiping {
  z-index: 2;
  cursor: grabbing;
}
.map-card-inner {
  display: flex;
  gap: 12px;
  padding: 10px;
}
.map-card-photo {
  width: 76px;
  height: 76px;
  border-radius: 10px;
  background: var(--surface-alt);
  object-fit: cover;
  flex-shrink: 0;
}
.map-card-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
}
.map-card-price-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.map-card-price {
  font-family: var(--font-body);
  font-size: 17px;
  font-weight: 800;
  color: var(--text);
  letter-spacing: -0.01em;
}
.map-card-trust {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  font-size: 10px;
  font-weight: 900;
  color: #ffffff;
  flex-shrink: 0;
}
.map-card-trust.verified { background: #22c55e; }
.map-card-trust.enhanced { background: #f59e0b; }
.map-card-reduced {
  font-size: 10px;
  font-weight: 800;
  color: var(--success);
  background: var(--success-bg);
  border: 1px solid var(--success-border);
  padding: 2px 7px;
  border-radius: 999px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.map-card-addr {
  font-size: 12px;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.map-card-meta {
  display: flex;
  gap: 10px;
  font-size: 11px;
  color: var(--text-muted);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
}
.map-card-dom {
  color: var(--warning);
  background: var(--warning-bg);
  padding: 1px 6px;
  border-radius: 6px;
  border: 1px solid var(--warning-border);
}

/* Tap-based action buttons — stay usable for users who prefer
   buttons over gestures. Positioned in a compact row below the
   card details. */
.map-card-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 0 10px 10px;
  border-top: 1px dashed var(--border);
  margin-top: 2px;
  padding-top: 8px;
}
.map-card-action {
  width: 36px;
  height: 36px;
  border: 1px solid var(--border-strong);
  background: var(--surface-alt);
  border-radius: 50%;
  color: var(--text);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 150ms ease, border-color 150ms ease, color 150ms ease, transform 150ms ease;
}
.map-card-action:active { transform: scale(0.92); }
.map-card-action.pass:hover {
  background: var(--danger-bg);
  border-color: var(--danger);
  color: var(--danger);
}
.map-card-action.like:hover {
  background: var(--primary-bg);
  border-color: var(--primary);
  color: var(--primary-light);
}

/* Swipe overlays — fade in based on --swipe-progress (0-1) set
   by the card-swipe gesture handler. Red X on left drag, green
   heart on right drag. */
.map-card-overlay {
  position: absolute;
  top: 50%;
  transform: translateY(-50%) scale(0.8);
  width: 56px;
  height: 56px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #ffffff;
  opacity: 0;
  pointer-events: none;
  transition: opacity 80ms linear;
}
.map-card-overlay.pass-overlay {
  left: 16px;
  background: var(--danger);
}
.map-card-overlay.like-overlay {
  right: 16px;
  background: var(--success);
}
.map-card.swipe-left  .map-card-overlay.pass-overlay { opacity: calc(var(--swipe-progress, 0)); }
.map-card.swipe-right .map-card-overlay.like-overlay { opacity: calc(var(--swipe-progress, 0)); }

/* Pin-tap highlight — applied by scrollDrawerToListing() for
   ~2 seconds after a pin tap scrolls the card into view. */
.map-card-highlighted {
  border-color: var(--primary);
  box-shadow: 0 0 0 2px var(--primary-bg), 0 4px 14px rgba(99, 102, 241, 0.35);
  animation: map-card-pulse 2s ease-out;
}
@keyframes map-card-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.6); }
  60%  { box-shadow: 0 0 0 12px rgba(99, 102, 241, 0); }
  100% { box-shadow: 0 4px 14px rgba(99, 102, 241, 0.35); }
}

/* ── Map container ──────────────────────────────────── */
.feed-map-view {
  flex: 1;
  position: relative;
  min-height: 0;
  /* --drawer-pct drives both the map-container height and the
     drawer height so they always sum to 100%. setDrawerState() in
     mapView.ts writes this variable plus a .drawer-* class. During
     a drag the variable is updated per frame; on release the class
     change re-engages the snap-point transition. */
  --drawer-pct: 58;
}
.feed-map-view.drawer-peek   { --drawer-pct: 32; }
.feed-map-view.drawer-medium { --drawer-pct: 58; }
.feed-map-view.drawer-full   { --drawer-pct: 92; }
.feed-map-view.drawer-dragging #feed-map-container,
.feed-map-view.drawer-dragging .map-drawer {
  transition: none;
}
/* Both the Discover map and the Likes map use .feed-map-view as
   their wrapper. The Discover map has --drawer-pct > 0 (drawer takes
   that share); the Likes map sets --drawer-pct: 0 so the map fills
   100%. Both containers need identical positioning — use the shared
   .map-leaflet-container class to avoid Vite HMR issues with
   comma-separated ID selectors. */
.map-leaflet-container {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: calc(100% - var(--drawer-pct) * 1%);
  background: var(--surface);
  transition: height 320ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* Leaflet's default attribution box — dark theme override */
.leaflet-container {
  background: #0b0b15;
  font-family: var(--font-body);
}
.leaflet-control-attribution {
  background: rgba(15, 15, 26, 0.85) !important;
  color: var(--text-muted) !important;
  font-size: 10px !important;
  padding: 2px 6px !important;
}
.leaflet-control-attribution a { color: var(--primary-light) !important; }
.leaflet-control-zoom a {
  background: var(--surface) !important;
  color: var(--text) !important;
  border-color: var(--border-strong) !important;
}
.leaflet-control-zoom a:hover {
  background: var(--surface-alt) !important;
  color: var(--primary-light) !important;
}

/* ── Feature 4b: Trust-informed price pill marker ──────
   Zillow/Redfin use pill-shaped price markers; SwipeHome adds a
   trust layer on top of the same pattern. Each pill encodes four
   independent pieces of information at a glance:
     1. Price (abbreviated $350K / $1.2M / $12M — the label itself)
     2. Listing status (pill background color)
     3. Photo-Truth verification (tiny dot, top-left)
     4. Days on market (text badge, bottom-right, only if stale)
   The downward triangle tail pinpoints the exact geographic anchor
   so the pill can be visually offset upward while still reading as
   "this is the pin location."
   ──────────────────────────────────────────────────── */
.price-pin {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 74px;
  height: 30px;
  padding: 0 10px;
  border-radius: 14px;
  background: var(--primary);
  color: #ffffff;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: -0.01em;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.45);
  border: 2px solid #ffffff;
  white-space: nowrap;
  cursor: pointer;
  transition:
    transform 160ms cubic-bezier(0.2, 0.8, 0.2, 1),
    box-shadow 160ms ease,
    background-color 200ms ease;
}

/* Downward-pointing triangle tail centered under the pill — an
   8x6 white-bordered wedge so the pill reads as a map pin. Uses
   a 2-layer trick: an outer (border) triangle and an inner (fill)
   triangle stacked so the stroke matches the pill's white border. */
.price-pin::before,
.price-pin::after {
  content: '';
  position: absolute;
  left: 50%;
  width: 0;
  height: 0;
  pointer-events: none;
}
.price-pin::before {
  /* Outer white border wedge */
  bottom: -7px;
  transform: translateX(-50%);
  border-left: 7px solid transparent;
  border-right: 7px solid transparent;
  border-top: 7px solid #ffffff;
}
.price-pin::after {
  /* Inner fill — inherits background via currentColor trick */
  bottom: -4px;
  transform: translateX(-50%);
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 5px solid var(--primary);
}

/* ── Status color variants ──────────────────────────── */
.price-pin.status-active::after    { border-top-color: var(--primary); }
.price-pin.status-reduced          { background: var(--success); }
.price-pin.status-reduced::after   { border-top-color: var(--success); }
.price-pin.status-pending          { background: #94a3b8; }
.price-pin.status-pending::after   { border-top-color: #94a3b8; }
.price-pin.status-sold             { background: #475569; opacity: 0.85; }
.price-pin.status-sold::after      { border-top-color: #475569; }
.price-pin.status-liked            { background: var(--success); }
.price-pin.status-liked::after     { border-top-color: var(--success); }

/* Hover — desktop only (no :hover on touch) — and selected
   (persistent after tap) states. Selected wins visually: bright
   white 2px border + primary glow. */
@media (hover: hover) {
  .price-pin:hover {
    transform: scale(1.1);
    box-shadow: 0 4px 14px rgba(99, 102, 241, 0.55);
    z-index: 900;
  }
}
.price-pin.selected {
  transform: scale(1.15);
  border-color: #ffffff;
  box-shadow: 0 0 0 2px var(--primary), 0 6px 18px rgba(99, 102, 241, 0.7);
  z-index: 1000;
}

/* Price label — its own span so trust dot + DoM badge can be
   positioned absolutely without shifting the price text. */
.price-pin-label {
  position: relative;
  z-index: 1;
}

/* ── Trust dot (Photo-Truth verification) ───────────── */
.price-pin-trust {
  position: absolute;
  top: -3px;
  left: -3px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  border: 1.5px solid #ffffff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
  z-index: 2;
}
.price-pin-trust.verified { background: #22c55e; }  /* fresh green */
.price-pin-trust.enhanced { background: #f59e0b; }  /* amber caution */

/* ── Days-on-market indicator ──────────────────────── */
.price-pin-dom {
  position: absolute;
  bottom: -6px;
  right: -4px;
  background: rgba(15, 15, 26, 0.92);
  color: var(--text-muted);
  font-size: 9px;
  font-weight: 700;
  padding: 1px 5px;
  border-radius: 6px;
  border: 1px solid var(--border-strong);
  letter-spacing: 0.02em;
  z-index: 2;
  pointer-events: none;
}

/* Leaflet wraps DivIcons in its own marker element — zero out
   its padding and background so our pill sits flush with no
   framing artifact. */
.leaflet-div-icon.price-pin-wrap {
  background: transparent;
  border: none;
}

/* ── Feature 4b: Custom cluster bubbles ────────────────
   Size tiers (sm 36 / md 44 / lg 52) + heat tint based on the
   cluster's average price relative to the context's median.
   Below median → green tint, above → indigo, within ±15% → neutral.
   Override leaflet.markercluster's default classes so nothing
   leaks through. */
.map-cluster {
  background: transparent !important;
  border: none !important;
}
.map-cluster .map-cluster-inner {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #ffffff;
  font-family: var(--font-body);
  font-weight: 800;
  font-size: 13px;
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.5);
  background: rgba(99, 102, 241, 0.85);
  border: 2px solid #ffffff;
  transition: transform 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.map-cluster:hover .map-cluster-inner {
  transform: scale(1.06);
}
.map-cluster.cluster-sm .map-cluster-inner { font-size: 12px; }
.map-cluster.cluster-md .map-cluster-inner { font-size: 13px; }
.map-cluster.cluster-lg .map-cluster-inner { font-size: 14px; }

/* Heat tint — subtle ±15% bands around the median. The default
   (no heat class) stays neutral indigo. */
.map-cluster.heat-below .map-cluster-inner {
  background: rgba(0, 212, 138, 0.82);
}
.map-cluster.heat-above .map-cluster-inner {
  background: rgba(129, 140, 248, 0.92);
}

/* Neutralize markercluster plugin default classes so the legacy
   .marker-cluster-small / -medium / -large rules don't double up
   on our custom .map-cluster. */
.marker-cluster-small,
.marker-cluster-medium,
.marker-cluster-large {
  background: transparent !important;
}
.marker-cluster-small div,
.marker-cluster-medium div,
.marker-cluster-large div {
  background: transparent !important;
  border: none !important;
}

/* Cluster hover tooltip — "$320K – $1.4M · 23 homes" */
.map-cluster-tooltip.leaflet-tooltip {
  background: rgba(15, 15, 26, 0.95);
  color: #ffffff;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 6px 10px;
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
}
.map-cluster-tooltip.leaflet-tooltip::before {
  border-top-color: rgba(15, 15, 26, 0.95);
}

/* ── Layer-filter overlay (Discover | Liked | Both) ─── */
.map-layer-toggle {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 500;
  display: inline-flex;
  background: rgba(15, 15, 26, 0.92);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-full);
  padding: 3px;
  gap: 2px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.map-layer-btn {
  height: 28px;
  padding: 0 12px;
  border: none;
  background: transparent;
  border-radius: var(--radius-full);
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background-color 160ms ease, color 160ms ease;
}
.map-layer-btn:hover { color: var(--text); }
.map-layer-btn.active {
  background: var(--primary);
  color: #ffffff;
}

/* ── Bottom-sheet preview card ──────────────────────── */
.map-preview-sheet {
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 12px;
  z-index: 600;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  padding: 12px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
  transform: translateY(16px);
  opacity: 0;
  transition: transform 220ms ease, opacity 220ms ease;
  pointer-events: none;
}
.map-preview-sheet.visible {
  transform: translateY(0);
  opacity: 1;
  pointer-events: auto;
}
.map-preview-sheet-inner {
  display: flex;
  gap: 12px;
  align-items: stretch;
}
.map-preview-photo {
  width: 96px;
  height: 96px;
  flex-shrink: 0;
  border-radius: var(--radius-md);
  background: var(--surface-alt);
  background-size: cover;
  background-position: center;
}
.map-preview-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 6px;
}
.map-preview-price {
  font-family: var(--font-body);
  font-size: 18px;
  font-weight: 800;
  color: var(--text);
}
.map-preview-address {
  font-size: 12px;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.map-preview-meta {
  display: flex;
  gap: 10px;
  font-size: 11px;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 600;
}
.map-preview-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 30px;
  padding: 0 14px;
  border: none;
  border-radius: var(--radius-full);
  background: var(--primary);
  color: #ffffff;
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  align-self: flex-start;
  margin-top: 4px;
  transition: background-color 160ms ease;
}
.map-preview-cta:hover { background: var(--primary-dark); }
.map-preview-close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 26px;
  height: 26px;
  border: none;
  border-radius: 50%;
  background: var(--surface-alt);
  color: var(--text-secondary);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.map-preview-close:hover { color: var(--text); }

/* Swipe action row inside the bottom-sheet — only rendered for the
   Discover map. Mirrors the main swipe-actions row visually but at
   a compact size so the sheet doesn't get too tall. */
.map-preview-swipe-actions {
  display: flex;
  justify-content: center;
  gap: 18px;
  padding: 10px 0 4px;
  margin-top: 10px;
  border-top: 1px solid var(--border);
}
.map-swipe-btn {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: var(--surface-alt);
  color: var(--text);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 150ms ease, border-color 150ms ease, transform 150ms ease, color 150ms ease;
}
.map-swipe-btn:hover { transform: scale(1.05); }
.map-swipe-btn:active { transform: scale(0.94); }
.map-swipe-nope:hover {
  background: var(--danger-bg);
  border-color: var(--danger);
  color: var(--danger);
}
.map-swipe-like:hover {
  background: var(--primary-bg);
  border-color: var(--primary);
  color: var(--primary-light);
}

/* ── Feature 4d: Pan-driven search UI ──────────────────
   Thin indeterminate progress bar docked at the top of the map
   container while a bbox query is in flight. YouTube/Vercel
   style sweep — 1200ms left-to-right loop, no full-screen
   spinner so the user stays in the map context. */
.map-pan-progress {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 2px;
  pointer-events: none;
  z-index: 700;
  opacity: 0;
  transition: opacity 180ms ease;
  overflow: hidden;
}
.map-pan-progress.active { opacity: 1; }
.map-pan-progress::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 40%;
  height: 100%;
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--primary) 35%,
    var(--primary-light) 50%,
    var(--primary) 65%,
    transparent 100%
  );
  animation: map-pan-sweep 1200ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
@keyframes map-pan-sweep {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(350%); }
}

/* Map-scoped toast — floats at the bottom of the map container,
   auto-dismisses after 4s. Kept separate from the global toast
   system so it doesn't cover the filter bar at the top of the
   screen. */
.map-pan-toast {
  position: absolute;
  left: 50%;
  bottom: 20px;
  transform: translate(-50%, 12px);
  background: rgba(15, 15, 26, 0.95);
  color: var(--text);
  padding: 10px 16px;
  border-radius: var(--radius-full);
  border: 1px solid var(--border-strong);
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.55);
  opacity: 0;
  transition: transform 220ms ease, opacity 220ms ease;
  pointer-events: none;
  z-index: 650;
  max-width: calc(100% - 32px);
  text-align: center;
}
.map-pan-toast.visible {
  transform: translate(-50%, 0);
  opacity: 1;
}

/* ── Feature 4f: Draw boundary button + states ─────── */
.map-draw-btn {
  position: absolute;
  top: 12px;
  right: 12px;
  z-index: 600;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 34px;
  padding: 0 14px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-full);
  background: rgba(15, 15, 26, 0.92);
  color: var(--text-secondary);
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4);
  transition: background-color 160ms ease, color 160ms ease, border-color 160ms ease;
}
.map-draw-btn:hover {
  color: var(--text);
  border-color: var(--primary-border);
}
.map-draw-btn.drawing {
  background: var(--primary-bg);
  border-color: var(--primary);
  color: var(--primary-light);
}
.map-draw-btn.active {
  background: rgba(99, 102, 241, 0.18);
  border-color: var(--primary);
  color: var(--primary-light);
}
.map-draw-btn i { width: 14px; height: 14px; }

/* ── Tier 1: Locate me button ──────────────────────────
   Circular floating button on the left side of the map,
   below Leaflet's zoom controls. Pans the viewport to the
   user's GPS location when tapped. Spins during fetch. */
.map-locate-btn {
  position: absolute;
  top: 86px;         /* below Leaflet's zoom control stack */
  left: 10px;
  z-index: 600;
  width: 34px;
  height: 34px;
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  background: rgba(15, 15, 26, 0.92);
  color: var(--text);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4);
  transition: background-color 160ms ease, color 160ms ease, transform 160ms ease;
}
.map-locate-btn:hover {
  color: var(--primary-light);
  border-color: var(--primary-border);
}
.map-locate-btn:active { transform: scale(0.94); }
.map-locate-btn.locating i {
  animation: locate-spin 1s linear infinite;
  color: var(--primary-light);
}
@keyframes locate-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}

/* ── Tier 1: Likes drawer controls (search + sort) ─────
   Compact row beneath the "N homes saved" count inside the
   Likes drawer header. Discover's drawer header stays
   count-only — its filtering lives in the global filter
   drawer accessible from the feed screen toolbar. */
.likes-drawer-controls {
  display: flex;
  gap: 8px;
  margin-top: 10px;
  align-items: center;
}
.likes-drawer-search {
  flex: 1;
  position: relative;
  display: flex;
  align-items: center;
}
.likes-drawer-search i {
  position: absolute;
  left: 10px;
  color: var(--text-muted);
  pointer-events: none;
}
.likes-drawer-search input {
  width: 100%;
  height: 34px;
  padding: 0 12px 0 30px;
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 13px;
  outline: none;
  transition: border-color 160ms ease;
}
.likes-drawer-search input:focus {
  border-color: var(--primary);
}
.likes-drawer-search input::placeholder {
  color: var(--text-placeholder);
  text-transform: none;
  letter-spacing: 0;
}
.likes-drawer-sort {
  flex-shrink: 0;
  height: 34px;
  padding: 0 10px;
  background: var(--input-bg);
  border: 1px solid var(--input-border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  text-transform: none;
  letter-spacing: 0;
}

/* ── Tier 3: Compare mode toggle + checkboxes + toolbar ── */
.likes-compare-toggle {
  background: transparent;
  border: 1px solid var(--border-strong);
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 4px 10px;
  border-radius: 999px;
  cursor: pointer;
  margin-left: auto;
  transition: color 160ms ease, border-color 160ms ease, background-color 160ms ease;
}
.likes-compare-toggle:hover {
  color: var(--text);
  border-color: var(--primary-border);
}

/* Compare checkbox overlay (replaces the unsave heart on Likes
   cards while compare mode is active). */
.map-card-compare-check {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 26px;
  height: 26px;
  border: 2px solid var(--border-strong);
  border-radius: 50%;
  background: rgba(15, 15, 26, 0.6);
  color: #ffffff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 160ms ease, border-color 160ms ease, transform 160ms ease;
}
.map-card-compare-check.selected {
  background: var(--primary);
  border-color: #ffffff;
}
.map-card-compare-check:active { transform: scale(0.92); }

/* Floating compare toolbar — sticky bottom bar that appears when
   the user has 2+ selections. Sits above the tab bar. */
.compare-toolbar {
  position: fixed;
  left: 16px;
  right: 16px;
  bottom: 76px;
  z-index: 900;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px 10px 16px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-full);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.55);
  animation: compare-toolbar-in 220ms ease-out;
}
@keyframes compare-toolbar-in {
  from { transform: translateY(12px); opacity: 0; }
  to   { transform: translateY(0); opacity: 1; }
}
.compare-toolbar-count {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
}
.compare-toolbar-go {
  height: 36px;
  padding: 0 18px;
  background: var(--primary);
  color: #ffffff;
  border: none;
  border-radius: var(--radius-full);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  transition: background-color 160ms ease, opacity 160ms ease;
}
.compare-toolbar-go:disabled {
  background: var(--surface-alt);
  color: var(--text-muted);
  cursor: not-allowed;
}
.compare-toolbar-go:hover:not(:disabled) {
  background: var(--primary-dark);
}

/* Compare screen — horizontal scrollable columns */
.compare-screen {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.compare-screen .screen-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
}
.compare-back-btn {
  background: var(--surface-alt);
  border: 1px solid var(--border);
  border-radius: 50%;
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text);
  cursor: pointer;
}
.compare-loading {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex: 1;
  color: var(--text-muted);
  font-size: 13px;
}
.compare-scroll {
  flex: 1;
  overflow-x: auto;
  overflow-y: auto;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}
/* ── Compare matrix — v2 (sections + winner highlighting) ──
   Sticky photo/price/address headers across the top, then a
   vertical stack of sections (Price / Home / Condition / Market).
   Each section has rows; each row shows the same metric across
   every column. Winner cells get a star + tinted background so
   buyers can scan the matrix at a glance. */
.compare-matrix {
  padding: 0 16px 32px;
  min-width: min-content;
}

/* Sticky header row — photo + price + address per listing */
.cmp-headers {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--bg);
  display: grid;
  gap: 10px;
  padding: 14px 0;
  border-bottom: 1px solid var(--border);
}
.cmp-col-header {
  cursor: pointer;
  transition: opacity 140ms ease;
}
.cmp-col-header:hover { opacity: 0.9; }
.cmp-photo {
  width: 100%;
  height: 100px;
  border-radius: 10px;
  object-fit: cover;
  background: var(--surface-alt);
  margin-bottom: 8px;
  display: block;
}
.cmp-price {
  font-family: var(--font-body);
  font-size: 16px;
  font-weight: 800;
  color: var(--text);
  letter-spacing: -0.01em;
  margin-bottom: 2px;
}
.cmp-addr {
  font-size: 11px;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Section header — "Price" / "Home" / "Condition" / "Market" */
.cmp-section {
  margin-top: 18px;
}
.cmp-section-label {
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 800;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 6px 2px 10px;
  border-bottom: 1px solid var(--border-accent);
  margin-bottom: 4px;
}

/* Row = label on the left, grid of cells across */
.cmp-row {
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.cmp-row:last-child { border-bottom: none; }
.cmp-row-label {
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 6px;
}
.cmp-row-cells {
  display: grid;
  gap: 10px;
}
.cmp-cell {
  position: relative;
  padding: 8px 10px;
  background: var(--surface-alt);
  border: 1px solid transparent;
  border-radius: 10px;
  min-height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
.cmp-cell-val {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
  line-height: 1.25;
}

/* Winner highlight — subtle tint + gold star top-right */
.cmp-cell-winner {
  background: rgba(34, 197, 94, 0.08);
  border-color: rgba(34, 197, 94, 0.35);
}
.cmp-winner-star {
  position: absolute;
  top: 2px;
  right: 4px;
  font-size: 10px;
  color: #f59e0b;
  line-height: 1;
}

/* Photo-Truth / HomeCheck score pill */
.cmp-trust {
  font-size: 11px;
  font-weight: 800;
  padding: 3px 8px;
  border-radius: 6px;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.cmp-trust.verified {
  color: var(--success);
  background: var(--success-bg);
  border: 1px solid var(--success-border);
}
.cmp-trust.enhanced {
  color: var(--warning);
  background: var(--warning-bg);
  border: 1px solid var(--warning-border);
}
.cmp-trust.none {
  color: var(--text-muted);
}

/* Boolean Yes/No flags — turnkey / needs_rehab */
.cmp-flag {
  font-size: 11px;
  font-weight: 800;
  padding: 2px 7px;
  border-radius: 5px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.cmp-flag.yes {
  color: var(--success);
  background: var(--success-bg);
  border: 1px solid var(--success-border);
}
.cmp-flag.no {
  color: var(--text-muted);
  background: var(--surface-alt);
}
.cmp-flag.warn {
  color: var(--warning);
  background: var(--warning-bg);
  border: 1px solid var(--warning-border);
}

/* Status pill */
.cmp-status {
  font-size: 10px;
  font-weight: 800;
  padding: 2px 8px;
  border-radius: 4px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.cmp-status.active {
  color: var(--success);
  background: var(--success-bg);
  border: 1px solid var(--success-border);
}
.cmp-status.pending {
  color: var(--warning);
  background: var(--warning-bg);
  border: 1px solid var(--warning-border);
}
.cmp-status.sold {
  color: var(--text-muted);
  background: var(--surface-alt);
}

.cmp-sub {
  display: block;
  font-size: 10px;
  font-weight: 500;
  color: var(--text-muted);
  margin-top: 2px;
}

/* Feature 11: Open House tag on map drawer cards —
   inline with price row, same pill visual language as the
   price-reduced tag so buyers visually group "time-sensitive
   info about this listing" in one spot. */
.map-card-openhouse {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 7px;
  border-radius: 999px;
  background: var(--warning);
  color: #ffffff;
  font-family: var(--font-body);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.02em;
  white-space: nowrap;
  flex-shrink: 0;
}
.map-card-openhouse i { flex-shrink: 0; }

/* ── Tier 1: Unsave heart on Likes drawer cards ────────
   Replaces the Discover Pass/Like action row. Single
   filled-heart button that removes the listing from saved
   when tapped. Positioned absolute in the card's top-right
   so it doesn't push card layout around. */
.map-card-unsave {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: 50%;
  background: rgba(99, 102, 241, 0.18);
  color: var(--primary-light);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background-color 160ms ease, color 160ms ease, transform 160ms ease;
}
.map-card-unsave i {
  fill: currentColor;  /* filled heart — matches the "saved" state */
}
.map-card-unsave:hover {
  background: var(--danger-bg);
  color: var(--danger);
}
.map-card-unsave:active { transform: scale(0.92); }

/* ── Map empty state ────────────────────────────────── */
.map-empty {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: 13px;
  padding: 24px;
  text-align: center;
  pointer-events: none;
}
.map-empty i { color: var(--text-tertiary); }
