/* ── Satoshi font (same family as the app) ──────────────── */
@font-face { font-family: "Satoshi"; src: url("assets/fonts/Satoshi-Regular.otf") format("opentype"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Satoshi"; src: url("assets/fonts/Satoshi-Medium.otf") format("opentype"); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: "Satoshi"; src: url("assets/fonts/Satoshi-Bold.otf") format("opentype"); font-weight: 700; font-style: normal; font-display: swap; }
@font-face { font-family: "Satoshi"; src: url("assets/fonts/Satoshi-Black.otf") format("opentype"); font-weight: 900; font-style: normal; font-display: swap; }

/* ── Theme tokens — values map 1:1 to constants/themes.js ─── */
:root {
  --primary: #e91e63;
  --accent: #ff84a1;
  --gold: #ffb82c;
  /* --backdrop is the bare page the bands sit on; --page-bg is the card's own
     interior (the list well). Keeping them a shade apart is what gives the
     window an edge without drawing a border for it. The flat cream is shared
     with /tolet, /wagroups and /brokerdirectory — the two pink ellipses this
     used to be washed with were the one thing that set the scanner apart from
     the rest of the family. */
  --backdrop: #efece0;
  --page-bg: #f8f7ef;
  --surface: #ffffff;
  --surface-2: #fdf4f8;
  --card-edge: rgba(26, 10, 50, 0.07);
  --text: #1a0a32;
  --text-muted: #5f4c7b;
  --line: #ece3ea;
  --shadow: 0 6px 20px rgba(26, 10, 50, 0.10);
  --shadow-sm: 0 2px 8px rgba(26, 10, 50, 0.08);
  --skeleton: #f0ecec;

  /* One hue per BHK size on the card spec line. Room and 5+ are the ends of the
     scale; everything at 5 and above shares the last colour, same as the chips. */
  --bhk-0: #6b7280;
  --bhk-1: #0f9d76;
  --bhk-2: #1d75d6;
  --bhk-3: #7c4dcf;
  --bhk-4: #cc6a06;
  --bhk-5: #d32f3f;

  --list-w: 40%;

  /* One design pixel. Every size in the desktop composition below was measured
     off the 1920-wide comp, so writing them as calc(N * var(--u)) makes the page
     land on the comp at 1920 and scale as one piece either side of it. Clamped so
     the chrome neither balloons on an ultrawide nor squeezes the toolbar type
     down to nothing on a 1366 laptop. */
  --u: clamp(0.85px, 0.0520833vw, 1.08px);
  /* Same idea for the wordmark, which is 0.56 of its desktop size on a phone. */
  --lg: var(--u);

  font-family: "Satoshi", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

/* Dark mode is one purple system, built up from the app's primaryBG #1A0A32
   rather than the app's much lighter secondaryBG: cards at #5F4C7B read as a
   washed-out lilac next to the map, and the point of this theme is that the
   chrome and the basemap look like the same surface. GHOSLA_DARK in app.js is
   tinted to the same family. */
[data-theme="dark"] {
  --primary: #ff84a1;
  --accent: #e91e63;
  --gold: #ffdd9a;
  --backdrop: #0b0219;      /* bare page the bands sit on */
  --page-bg: #14062a;       /* deepest: page behind the panes */
  --surface: #241142;       /* cards, header, popups */
  --surface-2: #31195c;     /* hover, inputs, image wells */
  --card-edge: rgba(255, 255, 255, 0.07);
  --text: #ffffff;
  --text-muted: #c6b4df;
  --line: #3d2668;
  --shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.45);
  --skeleton: #31195c;

  /* Lifted well clear of the #241142 card: the light-mode hues go muddy on it. */
  --bhk-0: #b3a6c9;
  --bhk-1: #4fd3a8;
  --bhk-2: #7cc0ff;
  --bhk-3: #c0a2ff;
  --bhk-4: #ffc46b;
  --bhk-5: #ff8f8f;
}

* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
  color: var(--text);
  background: var(--page-bg);
  display: flex;
  flex-direction: column;
  height: 100dvh;
  overflow: hidden;
  -webkit-text-size-adjust: 100%;
}
button, input, select { font: inherit; color: inherit; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* ── Shell: left hero + main column (same split as /brokerdirectory) ──
   The hero, the wordmark head and the footnote strip are all one uninterrupted
   backdrop: no fills of their own, no rules, no seams between them. Every
   boundary that used to be drawn with a border now belongs to the card that
   floats on top of them, which is the map and the listing pane. */
.shell { flex: 1 1 auto; display: flex; min-height: 0; background: var(--backdrop); }

.hero {
  flex: 0 0 calc(431 * var(--u)); min-width: 0; overflow: hidden;
  display: flex; align-items: stretch; justify-content: center;
  z-index: 20;
}
/* Container for the whole hero stack: every size below is a share of THIS width
   (cqi), which is what keeps the composition's proportions when the column
   narrows — the comp's 431px column at 1920 is the 100cqi reference, so each
   figure is its comp pixel value over 4.31. The dvh term is a floor for short
   windows: the stack is ~1.81x its own width tall, so past that the column has
   to give width back or the ends clip. */
.hero-inner {
  container-type: inline-size;
  width: min(100%, calc((100dvh - 24px) * 0.49));
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  /* Still centred, so it can never run off either end; the bottom padding is what
     sits the whole group above centre — it lifts the stack by half its value.
     A percentage, not cqi: container units on the container element itself fall
     back to the viewport, which here overshot the lift by 4x. */
  text-align: center; padding: 0 0 11.6%;
}

/* "Called an owner but they turned out to be a broker?" — 41/40 in the comp. */
.hero-ask {
  margin: 0; font-size: 9.51cqi; line-height: 0.976;
  font-weight: 700; letter-spacing: -0.05em; color: var(--primary);
}
.hero-got { margin: 7.6cqi 0 0; font-size: 6.50cqi; line-height: 1; font-weight: 400; letter-spacing: -0.03em; color: var(--accent); }
/* --accent and --primary swap places in the dark theme, which would make this the
   louder of the two pinks instead of the quieter one. */
[data-theme="dark"] .hero-got { color: rgba(255, 132, 161, 0.62); }
.hero-cage { display: block; width: 37.12cqi; height: auto; margin: 15.5cqi 0 0; }
.cage-dark, [data-theme="dark"] .cage-light { display: none; }
[data-theme="dark"] .cage-dark { display: block; }
/* Two sizes, not one: "directly by tenants." is set a step larger than the line
   above it in the comp. */
.hero-find { display: block; margin: 14.4cqi 0 0; text-decoration: none; color: inherit; }
.hf-a { display: block; font-size: 9.51cqi; line-height: 1; font-weight: 700; color: var(--text); letter-spacing: -0.05em; }
.hf-b { display: block; font-size: 10.44cqi; line-height: 1; font-weight: 900; color: var(--primary); letter-spacing: -0.05em; }
.hero-rule { width: 79.8%; border: 0; border-top: 1px solid rgba(26, 10, 50, 0.4); margin: 15.6cqi 0 0; }
[data-theme="dark"] .hero-rule { border-top-color: rgba(255, 255, 255, 0.3); }
/* No auto margin: the whole stack — headline, cage, tagline, rule, store buttons
   — is centred in the column as one group. */
.hero-download { display: flex; flex-direction: column; align-items: center; gap: 0; margin: 15cqi 0 0; }
.hd-pre { font-size: 4.64cqi; line-height: 1; font-weight: 500; letter-spacing: -0.04em; color: var(--text); }
.hd-name { font-size: 6.96cqi; line-height: 1; font-weight: 700; letter-spacing: -0.05em; color: var(--text); }

.main-col { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }

/* ── Stage: the floating window ──────────────────────────
   Wordmark, search and map are one object rather than three separate bands. The
   radius lives here but the clipping does not — .layout rounds its own two
   bottom corners instead, so the filter dropdown can still hang over the map
   without being cut off. */
.stage {
  flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
  margin: calc(53 * var(--u)) calc(29 * var(--u)) 0 0;
  border-radius: calc(24 * var(--u));
  background: var(--surface);
  box-shadow: 0 calc(4 * var(--u)) calc(26 * var(--u)) rgba(26, 10, 50, 0.13);
}
[data-theme="dark"] .stage { box-shadow: 0 0 0 1px var(--card-edge), var(--shadow); }

/* Product wordmark, centred above the search: bird over "Ghosla", a hairline,
   then the edition set in three lines. */
.panel-head {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  padding: calc(20 * var(--u)) 20px 0; z-index: 31;
}
.panel-brand {
  display: inline-flex; align-items: center; gap: calc(14 * var(--lg));
  text-decoration: none;
}
.pb-mark { display: flex; flex-direction: column; align-items: center; gap: calc(3 * var(--lg)); }
.pb-bird { display: block; width: calc(50 * var(--lg)); height: auto; }
.pb-name {
  font-size: calc(17 * var(--lg)); line-height: 1; font-weight: 700;
  letter-spacing: -0.06em; color: var(--accent);
}
.pb-rule { flex: 0 0 auto; width: 1px; height: calc(64 * var(--lg)); background: rgba(233, 30, 99, 0.28); }
.pb-edition {
  font-size: calc(22 * var(--lg)); line-height: calc(24 * var(--lg));
  font-weight: 700; letter-spacing: 0.04em; color: var(--primary);
  text-align: left; white-space: nowrap;
}
[data-theme="dark"] .pb-rule { background: rgba(255, 132, 161, 0.32); }
[data-theme="dark"] .pb-name { color: rgba(255, 132, 161, 0.75); }

/* ── Bottom band: deliberately slight — a footnote, not a footer ── */
.botbar {
  flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: calc(22 * var(--u)) calc(30 * var(--u)) calc(23 * var(--u)) 0; min-width: 0;
  font-size: 11.5px; line-height: 1.4; color: var(--text-muted);
}
.bb-note { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bb-links { flex: 0 0 auto; display: flex; align-items: center; gap: 14px; }
.bb-link {
  border: 0; padding: 0; background: none; cursor: pointer;
  font-size: 11.5px; font-weight: 700; color: var(--text-muted);
  text-decoration: underline; text-underline-offset: 2px;
}
.bb-link:hover { color: var(--primary); }

/* ── Banner: the toolbar row, split on the same seam as the panes below ──
   No horizontal padding of its own: the two halves take the same widths as
   .map-pane and .list-pane with the same 6px gutter between them, and each one
   insets its own contents. That is what puts the Filters button exactly on the
   boundary where the listing pane begins, with the count, saved and sort
   controls picking up on the other side of it. */
.banner {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  padding: calc(18 * var(--u)) 0 calc(14 * var(--u));
  z-index: 30;
}
/* padding-left is the search bar's inset from the card edge; it moves the bar,
   not the box, so the Filters button on the far side stays on the seam. */
.banner-map {
  flex: 1 1 0; min-width: 0;
  display: flex; align-items: center;
  gap: calc(31 * var(--u)); padding: 0 calc(12 * var(--u)) 0 calc(20 * var(--u));
  container: bmap / inline-size;
}
/* City picker and search field are one box in the comp: the frame, the radius and
   the height live here, and the two controls fill it flush. min-width:0 all the
   way down — a text input's intrinsic width (~20 chars) is a flex floor
   otherwise, and on a narrow phone it pushes the Filters button off. */
.search-row {
  position: relative; display: flex; align-items: stretch; flex: 1 1 auto; min-width: 0;
  height: calc(55 * var(--u));
  border: 1px solid var(--line); border-radius: calc(14 * var(--u));
  background: var(--surface);
}
.search-row:focus-within { border-color: var(--primary); }
/* Matches the .gutter plus the pane's own left border, so the Filters button ends
   exactly where the listing pane begins. */
.banner-gap { flex: 0 0 9px; }
/* Same three sizing terms as .list-pane, so the two boxes track each other when
   the gutter is dragged. */
.banner-list {
  flex: 0 0 var(--list-w); max-width: 1120px; min-width: 380px;
  display: flex; align-items: center;
  padding: 0 calc(18 * var(--u)) 0 calc(16 * var(--u));
  container: blist / inline-size;
}
.banner-list .list-head-row { flex: 1 1 auto; min-width: 0; gap: calc(14 * var(--u)); }
.banner-list .list-tools { gap: calc(15 * var(--u)); }
/* One height across the whole row, the search bar's. */
.banner-list .saved-btn, .banner-list .sort-select { height: calc(55 * var(--u)); }
.banner-list .list-count { font-size: calc(19 * var(--u)); }
.banner-list .saved-btn { font-size: calc(18 * var(--u)); border-radius: calc(14 * var(--u)); padding: 0 calc(9 * var(--u)); gap: calc(7 * var(--u)); }
.banner-list .saved-btn svg { width: calc(22 * var(--u)); height: calc(22 * var(--u)); }
/* The wide right padding is the comp's: the sort chevron sits hard against the
   card edge with the label left-aligned well clear of it, not next to it. */
.banner-list .sort-select {
  font-size: calc(18 * var(--u)); border-radius: calc(14 * var(--u));
  letter-spacing: -0.02em; width: calc(184 * var(--u)); flex: 0 0 auto;
  padding: 0 calc(40 * var(--u)) 0 calc(16 * var(--u));
  background-position: right calc(14 * var(--u)) center; background-size: calc(22 * var(--u));
}

/* Both halves shed something rather than let anything ellipsise, and both are
   keyed to their own box rather than the viewport, because the gutter that sets
   the split is draggable. The count loses its tail; the Filters button loses its
   label, which the icon and its aria-label both still carry, once the map half
   is narrow enough that keeping it would squeeze the search input to nothing. */
/* Both tails are dropped by fitCount() when shrinking the type has run out of
   room, not by a width threshold — see the note there. */
.lc-no-total .lc-total, .lc-no-view .lc-view { display: none; }
@container bmap (max-width: 360px) {
  .banner-map .fb-label { display: none; }
  .banner-map .filter-btn { padding: 0 12px; }
}
/* City picker — the export covers eight metros, so this is a real control now
   rather than the fixed "Bengaluru" label it used to be. Same component as
   /tolet. */
.city-select { position: relative; flex: 0 0 auto; }
.city-btn {
  height: 100%; display: flex; align-items: center; gap: calc(8 * var(--u));
  padding: 0 calc(10 * var(--u)) 0 calc(13 * var(--u));
  border: 0; border-right: 1px solid var(--line);
  border-radius: calc(13 * var(--u)) 0 0 calc(13 * var(--u)); background: var(--surface-2);
  font: inherit; font-size: calc(19 * var(--u)); font-weight: 700; letter-spacing: -0.02em;
  color: var(--text); cursor: pointer; white-space: nowrap; max-width: calc(230 * var(--u));
}
.city-btn .ci-pin { width: calc(22 * var(--u)); height: calc(22 * var(--u)); }
.city-btn .chev { width: calc(22 * var(--u)); height: calc(22 * var(--u)); }
.city-btn:hover { background: var(--surface); }
.city-btn .ci-pin { color: var(--primary); flex: 0 0 auto; }
.city-btn #cityLabel { overflow: hidden; text-overflow: ellipsis; }
.city-btn .chev { flex: 0 0 auto; opacity: .65; transition: transform .15s ease; }
.city-select.open .city-btn .chev { transform: rotate(180deg); }
.city-menu {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 50;
  display: none; min-width: 200px; max-height: 60vh; overflow-y: auto;
  margin: 0; padding: 4px; list-style: none;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: 12px; box-shadow: var(--shadow);
}
.city-select.open .city-menu { display: block; }
.city-item {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 9px 11px; border-radius: 8px; cursor: pointer;
  font-size: 14px; font-weight: 700; color: var(--text);
}
.city-item:hover { background: var(--surface-2); color: var(--primary); }
.city-item.is-on { color: var(--primary); }
.city-item .ci-count { font-size: 12px; font-weight: 600; color: var(--text-muted); flex: 0 0 auto; }
.city-item.is-on .ci-count { color: var(--primary); }
.search-wrap { position: relative; flex: 1 1 auto; min-width: 0; display: flex; align-items: center; }
.search-ico { position: absolute; left: calc(16 * var(--u)); display: flex; color: var(--text-muted); pointer-events: none; }
.search-ico svg { width: calc(22 * var(--u)); height: calc(22 * var(--u)); }
#placeSearch {
  width: 100%; min-width: 0; height: 100%;
  padding: 0 calc(34 * var(--u)) 0 calc(47 * var(--u));
  border: 0; border-radius: 0 calc(13 * var(--u)) calc(13 * var(--u)) 0;
  background: var(--surface); outline: none; font-size: calc(19 * var(--u));
}
.search-clear {
  position: absolute; right: 8px; width: 22px; height: 22px; border: 0; border-radius: 50%;
  background: var(--surface-2); color: var(--text-muted); cursor: pointer; font-size: 11px; line-height: 1;
}
.search-results {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 40;
  margin: 0; padding: 4px; list-style: none; max-height: 320px; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--line); border-radius: 12px; box-shadow: var(--shadow);
}
.search-results:empty { display: none; }
.search-results li { padding: 9px 11px; border-radius: 8px; cursor: pointer; font-size: 14px; }
.search-results li:hover, .search-results li.is-active { background: var(--surface-2); }
.search-results li .sr-sub { display: block; font-size: 12px; color: var(--text-muted); }

/* ── Filters ────────────────────────────────────────────── */
.filter-wrap { position: relative; }
.filter-btn {
  display: inline-flex; align-items: center; gap: calc(9 * var(--u));
  height: calc(55 * var(--u)); padding: 0 calc(7 * var(--u)) 0 calc(13 * var(--u));
  border: 1px solid var(--line); border-radius: calc(14 * var(--u)); background: var(--surface);
  font-size: calc(19 * var(--u)); font-weight: 700; cursor: pointer;
}
.filter-btn > svg { width: calc(24 * var(--u)); height: calc(24 * var(--u)); }
.filter-btn .filter-badge {
  min-width: calc(25 * var(--u)); height: calc(25 * var(--u));
  border-radius: calc(13 * var(--u)); font-size: calc(14 * var(--u));
}
.filter-btn:hover { border-color: var(--primary); }
.filter-btn[aria-expanded="true"] { border-color: var(--primary); background: var(--surface-2); }
.filter-badge {
  min-width: 19px; height: 19px; padding: 0 5px; border-radius: 10px;
  background: var(--primary); color: #fff; font-size: 11px; font-weight: 900;
  display: inline-flex; align-items: center; justify-content: center;
}
.filter-panel {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 50;
  width: min(400px, calc(100vw - 32px)); max-height: min(72vh, 640px); overflow-y: auto;
  padding: 16px; background: var(--surface); border: 1px solid var(--line);
  border-radius: 16px; box-shadow: var(--shadow); display: none;
}
.filter-panel.is-open { display: block; }
.filter-group { margin-bottom: 16px; }
.filter-group-title { display: block; font-size: 12px; font-weight: 900; letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-muted); margin-bottom: 8px; }
.filter-chips { display: flex; flex-wrap: wrap; gap: 7px; }
.fchip {
  padding: 7px 12px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); font-size: 13px; font-weight: 500; cursor: pointer; white-space: nowrap;
}
.fchip:hover { border-color: var(--primary); }
.fchip.is-on { background: var(--primary); border-color: var(--primary); color: #fff; font-weight: 700; }
.fchip .fc-n { opacity: 0.6; font-size: 11px; margin-left: 4px; }
.filter-foot { display: flex; gap: 10px; align-items: center; position: sticky; bottom: -16px; margin: 0 -16px -16px; padding: 12px 16px; background: var(--surface); border-top: 1px solid var(--line); }
.filter-reset { flex: 0 0 auto; border: 0; background: none; color: var(--text-muted); font-size: 13px; font-weight: 700; text-decoration: underline; cursor: pointer; padding: 8px 0; }
.filter-done { flex: 1 1 auto; height: 40px; border: 0; border-radius: 12px; background: var(--primary); color: #fff; font-size: 14px; font-weight: 800; cursor: pointer; }

/* Dual-thumb rent range: two overlaid range inputs, only their thumbs hit-test. */
.range { position: relative; height: 26px; margin: 6px 2px 2px; }
.range-track { position: absolute; top: 11px; left: 0; right: 0; height: 4px; border-radius: 2px; background: var(--line); }
.range-fill { position: absolute; top: 0; bottom: 0; border-radius: 2px; background: var(--primary); }
.range input[type="range"] {
  position: absolute; top: 0; left: 0; width: 100%; height: 26px; margin: 0;
  -webkit-appearance: none; appearance: none; background: none; pointer-events: none;
}
.range input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; pointer-events: auto; cursor: grab;
  width: 18px; height: 18px; border-radius: 50%; background: var(--surface);
  border: 3px solid var(--primary); box-shadow: var(--shadow-sm);
}
.range input[type="range"]::-moz-range-thumb {
  pointer-events: auto; cursor: grab;
  width: 12px; height: 12px; border-radius: 50%; background: var(--surface);
  border: 3px solid var(--primary); box-shadow: var(--shadow-sm);
}
.range-vals { display: flex; justify-content: space-between; font-size: 13px; font-weight: 700; margin-top: 4px; }
/* Only the deposit uses this so far: it is the one facet the sources publish
   patchily, and narrowing it hides every listing that has none. Saying so beats
   letting the count drop by half unexplained. */
.filter-note { margin: 6px 0 0; font-size: 11.5px; line-height: 1.4; color: var(--text-muted); }
.filter-group[hidden] { display: none; }

/* ── Store badges (in the hero) — /brokerdirectory's format ── */
.store-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 1.86cqi; align-items: center; margin-top: 9.4cqi; }
.store-badge {
  display: inline-flex; align-items: center; gap: 1.86cqi;
  height: 8.58cqi; padding: 0 2.8cqi; border-radius: 1.86cqi;
  background: #000; color: #fff; text-decoration: none; line-height: 1;
}
.store-badge:hover { opacity: 0.85; }
.store-badge svg { width: 4.87cqi; height: 4.87cqi; flex: 0 0 auto; }
.sb-txt { display: flex; flex-direction: column; align-items: flex-start; gap: 0.55cqi; font-size: 3.02cqi; font-weight: 700; }
.sb-txt small { font-size: 1.97cqi; font-weight: 500; letter-spacing: 0.3px; }

/* ── Layout: the body of the window, under the toolbar ───── */
.layout {
  flex: 1 1 auto; display: flex; min-height: 0; position: relative;
  border-top: 1px solid var(--line);
  border-radius: 0 0 calc(24 * var(--u)) calc(24 * var(--u)); overflow: hidden;
}
.map-pane { position: relative; flex: 1 1 auto; min-width: 0; }
#map { position: absolute; inset: 0; }
.maplibregl-ctrl-bottom-right { z-index: 5; }

.gutter { flex: 0 0 9px; cursor: col-resize; background: var(--line); position: relative; z-index: 6; }
.gutter:hover, .gutter:focus-visible { background: var(--primary); outline: none; }

.list-pane {
  /* Wide enough to hold four columns when dragged out. The floor is what keeps
     two cards per row on a small laptop: at 40% of the space left over from the
     hero column, a 950px window gives the pane ~263px, which is one card wide. */
  flex: 0 0 var(--list-w); max-width: 1120px; min-width: 380px;
  display: flex; flex-direction: column; min-height: 0;
  background: var(--page-bg); border-left: 1px solid var(--line); z-index: 6;
}
/* On desktop the head row has moved up to the toolbar, so all that is left in
   here is the active-filter pills — and they carry their own padding and rule so
   the box collapses to nothing when there are none. */
.list-head { flex: 0 0 auto; }
.list-head-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.list-count { font-size: 14px; font-weight: 800; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-count b { color: var(--primary); }
.list-tools { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; }
.sort-wrap { flex: 0 0 auto; }
.saved-btn {
  display: inline-flex; align-items: center; gap: 5px; height: 34px; padding: 0 11px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface);
  font-size: 13px; font-weight: 800; cursor: pointer;
}
.saved-btn svg { fill: none; stroke: currentColor; stroke-width: 2; }
.saved-btn:hover { border-color: var(--primary); }
.saved-btn.has-any { color: var(--primary); }
.saved-btn.has-any svg { fill: var(--primary); stroke: var(--primary); }
.saved-btn.is-on {
  background: var(--primary); border-color: var(--primary); color: #fff;
}
.saved-btn.is-on svg { fill: #fff; stroke: #fff; }

.btn-fav { display: inline-flex; align-items: center; gap: 6px; }
.btn-fav svg { fill: none; stroke: currentColor; stroke-width: 2; }
.btn-fav.is-on { border-color: var(--primary); color: var(--primary); }
.btn-fav.is-on svg { fill: var(--primary); }
.sort-select {
  height: 34px; padding: 0 26px 0 10px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--surface) url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235f4c7b' stroke-width='2.4' stroke-linecap='round'><path d='M6 9l6 6 6-6'/></svg>") no-repeat right 7px center / 13px;
  font-size: 13px; font-weight: 700; cursor: pointer; -webkit-appearance: none; appearance: none;
}
.active-filters {
  display: flex; flex-wrap: wrap; gap: 6px;
  padding: 9px 16px; border-bottom: 1px solid var(--line); background: var(--surface);
}
.active-filters:empty { display: none; }
.af-pill {
  display: inline-flex; align-items: center; gap: 5px; padding: 4px 8px;
  border-radius: 999px; background: var(--surface-2); border: 1px solid var(--line);
  font-size: 12px; font-weight: 700; cursor: pointer;
}
.af-pill:hover { border-color: var(--primary); }
.af-pill .af-x { color: var(--text-muted); font-size: 11px; }

.list-scroll {
  flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain;
  padding: calc(16 * var(--u)) calc(18 * var(--u)) calc(24 * var(--u));
}
/* Masonry canvas: cards are absolutely positioned by layoutMasonry(), so this
   needs an explicit height (set in JS) to give the scroll container something
   to measure. --cols is written by the same pass. */
.list-grid { position: relative; --cols: 1; }

/* ── Listing cards ──────────────────────────────────────── */
.card {
  position: absolute; top: 0; left: 0;
  width: calc((100% - (var(--cols) - 1) * 12px) / var(--cols));
  display: flex; flex-direction: column;
  padding: 0; border-radius: 14px; overflow: hidden;
  background: var(--surface); border: 1px solid var(--line);
  cursor: pointer; text-align: left; font: inherit; color: inherit;
  transition: border-color .12s, box-shadow .12s;
}
.card:hover { border-color: var(--primary); box-shadow: var(--shadow-sm); }
/* Selected from the map: a plain border change was too quiet to spot after
   clicking a dot, so the active card gets a full ring, a tinted body and a
   deeper shadow. Deliberately no transform — layoutMasonry() owns each card's
   transform, and setting one here would knock it out of position. */
.card.is-active {
  border-color: var(--primary);
  background: var(--surface-2);
  box-shadow: 0 0 0 3px var(--primary), var(--shadow);
}
.card:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

.card-photo {
  position: relative; display: block; width: 100%;
  aspect-ratio: var(--ar, 1.33);
  background: var(--skeleton); overflow: hidden;
}
.card-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.card-photo img.is-broken { display: none; }
/* Overlaid on the photo so the body is pure information, no filler rows. */
.card-src {
  position: absolute; left: 7px; top: 7px;
  background: rgba(255, 255, 255, 0.93); border-color: transparent; color: #1a0a32;
  backdrop-filter: blur(3px);
}
.tag-managed {
  position: absolute; left: 7px; top: 32px;
  padding: 2px 7px; border-radius: 6px; font-size: 10.5px; font-weight: 800;
  background: rgba(255, 184, 44, 0.95); color: #4a3000;
}
/* Wishlist heart, top-right of the photo (source badge owns top-left). */
.card-fav {
  position: absolute; right: 6px; top: 6px; z-index: 2;
  width: 30px; height: 30px; padding: 0; display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%; cursor: pointer;
  background: rgba(255, 255, 255, 0.9); backdrop-filter: blur(3px);
  transition: transform .12s, background .12s;
}
.card-fav svg { fill: none; stroke: #4a3a5e; stroke-width: 2; }
.card-fav:hover { transform: scale(1.08); background: #fff; }
.card-fav.is-on { background: var(--primary); }
.card-fav.is-on svg { fill: #fff; stroke: #fff; }
.card-fav:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

.card-nphotos {
  position: absolute; right: 7px; bottom: 7px;
  display: inline-flex; align-items: center; gap: 3px;
  padding: 2px 7px; border-radius: 6px;
  background: rgba(0, 0, 0, 0.6); color: #fff; font-size: 10.5px; font-weight: 700;
}
.card-nphotos::before { content: "◳"; font-size: 10px; }

.card-main { min-width: 0; display: flex; flex-direction: column; gap: 2px; padding: calc(11 * var(--u)) calc(13 * var(--u)) calc(13 * var(--u)); }
.card-rent { font-size: calc(22 * var(--u)); font-weight: 900; letter-spacing: -0.3px; line-height: 1.15; }
.card-rent span { font-size: calc(14 * var(--u)); font-weight: 500; color: var(--text-muted); }
.card-specs { font-size: calc(15 * var(--u)); font-weight: 700; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card-specs .bhk { font-weight: 900; color: var(--bhk-0); }
.card-specs .bhk-1 { color: var(--bhk-1); }
.card-specs .bhk-2 { color: var(--bhk-2); }
.card-specs .bhk-3 { color: var(--bhk-3); }
.card-specs .bhk-4 { color: var(--bhk-4); }
.card-specs .bhk-5 { color: var(--bhk-5); }
.card-loc {
  font-size: calc(14 * var(--u)); color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.card-date {
  font-size: calc(13 * var(--u)); color: var(--text-muted); opacity: 0.8; margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.src-badge {
  padding: 2px 7px; border-radius: 6px; font-size: 10.5px; font-weight: 800;
  letter-spacing: 0.02em; background: var(--surface-2); border: 1px solid var(--line); color: var(--text-muted);
  white-space: nowrap;
}

.list-empty { padding: 40px 20px; text-align: center; color: var(--text-muted); }
.list-empty b { display: block; font-size: 16px; color: var(--text); margin-bottom: 6px; }
.list-more { padding: 14px 8px 4px; text-align: center; font-size: 12.5px; color: var(--text-muted); }
.list-skel { height: 210px; border-radius: 14px; margin-bottom: 12px; background: var(--skeleton); animation: pulse 1.3s ease-in-out infinite; }
@keyframes pulse { 0%, 100% { opacity: 1 } 50% { opacity: .55 } }

/* Floats over the map, top-left. Shadowed and on the page surface colour so it
   reads as a control sitting on the map rather than part of the basemap.
   Both icons are drawn (SVG, not ☀/☾ glyphs — Satoshi has neither, so they fell
   back to whatever the system had and mostly rendered as nothing) and both stay
   ABOVE the knob: the one the knob is parked on goes white and reads as the
   current mode, the other stays muted as the mode you can switch to. */
.theme-switch {
  position: absolute; top: calc(20 * var(--u)); left: calc(22 * var(--u)); z-index: 10;
  width: calc(90 * var(--u)); height: calc(42 * var(--u)); border-radius: 999px;
  border: 1px solid var(--line); background: var(--surface); cursor: pointer; padding: 0;
  box-shadow: var(--shadow);
}
.theme-switch:hover { border-color: var(--primary); }
.ts-ico {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  display: flex; line-height: 0; color: var(--text-muted); transition: color .18s ease;
}
.ts-ico svg { width: calc(20 * var(--u)); height: calc(20 * var(--u)); display: block; }
.ts-sun { left: calc(11 * var(--u)); } .ts-moon { right: calc(12 * var(--u)); }
.theme-switch[aria-checked="false"] .ts-sun,
.theme-switch[aria-checked="true"] .ts-moon { color: #fff; }
.theme-switch-knob {
  position: absolute; top: calc(3 * var(--u)); left: calc(3 * var(--u)); z-index: 1;
  width: calc(36 * var(--u)); height: calc(36 * var(--u)); border-radius: 50%;
  background: var(--primary); transition: transform .18s ease;
}
/* Dark's --primary is the pale pink, which white-on-white-ish loses the icon
   against; the knob keeps the saturated pink in both themes. */
[data-theme="dark"] .theme-switch-knob { background: var(--accent); }
.theme-switch[aria-checked="true"] .theme-switch-knob { transform: translateX(calc(48 * var(--u))); }

/* ── Map overlays ───────────────────────────────────────── */

.map-loading {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 10;
  display: flex; align-items: center; gap: 9px; padding: 11px 18px; border-radius: 999px;
  background: var(--surface); box-shadow: var(--shadow); font-size: 13.5px; font-weight: 700;
}
.map-loading[hidden] { display: none; }
.spinner { width: 15px; height: 15px; border: 2px solid var(--line); border-top-color: var(--primary); border-radius: 50%; animation: spin .7s linear infinite; }
@keyframes spin { to { transform: rotate(360deg) } }

/* Hover preview popup on a dot */
.maplibregl-popup-content { padding: 0; border-radius: 12px; overflow: hidden; box-shadow: var(--shadow); background: var(--surface); }
.maplibregl-popup-tip { border-top-color: var(--surface) !important; border-bottom-color: var(--surface) !important; }
.pop { width: 210px; cursor: pointer; }
.pop img { width: 100%; height: 118px; object-fit: cover; display: block; background: var(--skeleton); }
.pop-body { padding: 8px 10px 10px; }
.pop-rent { font-size: 15px; font-weight: 900; }
.pop-specs { font-size: 12px; font-weight: 700; margin-top: 1px; }
.pop-loc { font-size: 11.5px; color: var(--text-muted); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── Detail modal ───────────────────────────────────────── */
.detail-modal { position: fixed; inset: 0; z-index: 100; display: flex; align-items: center; justify-content: center; padding: 24px; }
.detail-modal.is-closed { display: none; }
.detail-backdrop { position: absolute; inset: 0; background: rgba(10, 4, 20, 0.62); backdrop-filter: blur(2px); }
.detail-card {
  position: relative; z-index: 1; width: min(1020px, 100%);
  /* A definite height is what lets the gallery column flex: without it the stage
     grows to the photo's intrinsic size and squeezes the thumbnail strip away. */
  height: min(680px, 100%);
  display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(320px, 1fr);
  background: var(--surface); border-radius: 18px; overflow: hidden; box-shadow: var(--shadow);
}
.detail-close {
  position: absolute; top: 12px; right: 12px; z-index: 3; width: 32px; height: 32px;
  border: 0; border-radius: 50%; background: rgba(255, 255, 255, 0.92); color: #1a0a32;
  font-size: 14px; cursor: pointer; box-shadow: var(--shadow-sm);
}

.detail-gallery { display: flex; flex-direction: column; background: #0d0715; min-width: 0; min-height: 0; }
.dg-stage { position: relative; flex: 1 1 0; min-height: 0; overflow: hidden; display: flex; align-items: center; justify-content: center; }
.dg-img { max-width: 100%; max-height: 100%; width: 100%; height: 100%; object-fit: contain; display: block; }
.dg-nav {
  position: absolute; top: 50%; transform: translateY(-50%); width: 38px; height: 38px;
  border: 0; border-radius: 50%; background: rgba(255, 255, 255, 0.9); color: #1a0a32;
  font-size: 22px; line-height: 1; cursor: pointer; box-shadow: var(--shadow-sm);
}
.dg-prev { left: 10px; } .dg-next { right: 10px; }
.dg-nav[hidden] { display: none; }
.dg-counter {
  position: absolute; right: 12px; bottom: 12px; padding: 3px 9px; border-radius: 999px;
  background: rgba(0, 0, 0, 0.6); color: #fff; font-size: 11.5px; font-weight: 700;
}
.dg-strip { flex: 0 0 auto; display: flex; gap: 6px; padding: 8px; overflow-x: auto; background: rgba(0, 0, 0, 0.25); }
.dg-strip img {
  width: 64px; height: 48px; object-fit: cover; border-radius: 6px; cursor: pointer;
  opacity: 0.55; border: 2px solid transparent; flex: 0 0 auto; background: #241a33;
}
.dg-strip img.is-on { opacity: 1; border-color: var(--primary); }

.detail-body { padding: 22px 22px 24px; overflow-y: auto; min-width: 0; }
/* padding-right keeps the source badge clear of the floating close button. */
.detail-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding-right: 40px; }
.detail-price { margin: 0; font-size: 27px; font-weight: 900; letter-spacing: -0.6px; }
.detail-psf { margin: 2px 0 0; font-size: 12.5px; color: var(--text-muted); }
.detail-title { margin: 14px 0 4px; font-size: 16px; font-weight: 700; line-height: 1.35; }
.detail-loc { margin: 0; font-size: 13.5px; color: var(--text-muted); }
.spec-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1px; margin: 18px 0 0; background: var(--line); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
.spec { background: var(--surface); padding: 10px 12px; }
.spec-k { display: block; font-size: 11px; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase; color: var(--text-muted); }
.spec-v { display: block; font-size: 14.5px; font-weight: 800; margin-top: 2px; }
.detail-notes { margin-top: 14px; display: flex; flex-direction: column; gap: 7px; }
.note { display: flex; gap: 7px; font-size: 12.5px; line-height: 1.45; color: var(--text-muted); padding: 8px 10px; border-radius: 9px; background: var(--surface-2); }
.detail-actions { display: flex; gap: 10px; margin-top: 18px; }
.btn-primary {
  flex: 1 1 auto; height: 44px; display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 12px; background: var(--primary); color: #fff;
  font-size: 14px; font-weight: 800; text-decoration: none; cursor: pointer;
}
.btn-primary.is-disabled { background: var(--surface-2); color: var(--text-muted); pointer-events: none; }
.btn-ghost {
  flex: 0 0 auto; height: 44px; padding: 0 16px; border: 1px solid var(--line); border-radius: 12px;
  background: var(--surface); font-size: 14px; font-weight: 800; cursor: pointer;
}
.btn-ghost:hover { border-color: var(--primary); }
.detail-plug { margin: 16px 0 0; font-size: 12px; line-height: 1.5; color: var(--text-muted); }
.detail-plug a { color: var(--primary); font-weight: 700; }

/* ── Disclaimer ─────────────────────────────────────────── */
.disc-modal { position: fixed; inset: 0; z-index: 110; display: flex; align-items: center; justify-content: center; padding: 20px; }
.disc-modal.is-closed { display: none; }
.disc-backdrop { position: absolute; inset: 0; background: rgba(10, 4, 20, 0.6); }
.disc-card {
  position: relative; z-index: 1; width: min(460px, 100%); padding: 26px;
  background: var(--surface); border-radius: 18px; box-shadow: var(--shadow); text-align: center;
}
.disc-icon { font-size: 34px; }
.disc-title { margin: 8px 0 12px; font-size: 20px; font-weight: 900; }
.disc-body { font-size: 13.5px; line-height: 1.55; color: var(--text-muted); text-align: left; }
.disc-body p { margin: 0 0 10px; }
.disc-body .disc-lead { font-weight: 700; color: var(--text); }
.disc-body a { color: var(--primary); font-weight: 700; }
.disc-btn { margin-top: 8px; width: 100%; height: 44px; border: 0; border-radius: 12px; background: var(--primary); color: #fff; font-size: 15px; font-weight: 800; cursor: pointer; }

/* Desktop only: on a phone the card is already most of the screen and the values
   above are the ones the layout is built around. Here it has room, so the whole
   card steps up about a third and the map behind it goes properly out of focus —
   it is the first thing on screen and should read as one, not as a note taped
   over a working map. */
@media (min-width: 901px) {
  .disc-backdrop {
    background: rgba(10, 4, 20, 0.55);
    backdrop-filter: blur(14px) saturate(0.9);
    -webkit-backdrop-filter: blur(14px) saturate(0.9);
  }
  .disc-card { width: min(620px, 100%); padding: 36px 38px 34px; border-radius: 24px; }
  .disc-icon { font-size: 46px; }
  .disc-title { margin: 12px 0 18px; font-size: 27px; }
  .disc-body { font-size: 17px; line-height: 1.6; }
  .disc-body p { margin: 0 0 14px; }
  .disc-btn { margin-top: 14px; height: 58px; border-radius: 16px; font-size: 19px; }
}

/* ── Phone pitch strip ──────────────────────────────────────
   Desktop has the hero column for this; here it is a band at the foot of the
   screen. Laid out as a flex row rather than fixed, so the list well simply ends
   above it — the shadow is all that is left of it overlapping. */
.mcta { display: none; }
@media (max-width: 900px) {
  .mcta {
    /* Above the sheet: its foot overhangs the map area and would paint over this. */
    position: relative; z-index: 40;
    /* space-between, not a margin-left:auto on the icons: pushing them to the edge
       parked them 60px past the second rule on a 430pt phone, which read as a
       stray pair rather than the third item in a row of three. The slack is spread
       across all four gaps instead, so the bar stays evenly set at any width and
       the icons keep the comp's inset from the right edge. */
    flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between;
    gap: 12px; overflow: hidden;
    padding: 16px 20px calc(16px + env(safe-area-inset-bottom, 0px)) 16px;
    background: var(--surface); box-shadow: 0 -4px 14px rgba(26, 10, 50, 0.10);
    font-size: 10.5px; line-height: 1.15; font-weight: 700; letter-spacing: -0.055em;
    color: var(--text); white-space: nowrap;
  }
  [data-theme="dark"] .mcta { box-shadow: 0 -4px 14px rgba(0, 0, 0, 0.45); }
  .mcta-line { flex: 0 0 auto; }
  .mcta-line b { font-weight: 900; color: var(--primary); }
  /* Explicit height, not stretch: an auto cross-axis margin cancels align-self,
     which left this empty span at zero height and invisible. */
  .mcta-div { flex: 0 0 1px; height: 19px; background: rgba(26, 10, 50, 0.5); }
  [data-theme="dark"] .mcta-div { background: rgba(255, 255, 255, 0.35); }
  /* Pushed to the right edge — the two lines leave a wide gap there — and given a
     hit area well past the artwork via ::after, which costs no layout. */
  .mcta-apps {
    position: relative; flex: 0 0 auto;
    display: flex; align-items: center; gap: 7px; line-height: 0;
  }
  .mcta-apps::after { content: ""; position: absolute; inset: -14px -10px; }
  .mcta-apps:active { opacity: 0.7; }
  .mcta-icon { width: 23px; height: 23px; display: block; }
}
/* Anything narrower than the comp's 375: the two lines have to hold their
   two-line shape, so the type and the gaps give way before the layout does. */
@media (max-width: 374px) {
  .mcta { gap: 9px; padding-left: 12px; padding-right: 12px; font-size: 9.5px; }
  .mcta-icon { width: 20px; height: 20px; }
}

/* ── Responsive ─────────────────────────────────────────── */
@media (max-width: 900px) {
  /* One column, top to bottom: wordmark, search, a fixed slab of map, then the
     listings scrolling under it. No floating window: at this width the chrome IS
     the page, so the band behind the wordmark runs straight into the map and the
     list well starts where the map ends. The band is the cream backdrop, not the
     window's white — the same band /tolet, /wagroups and /brokerdirectory stack
     their phone chrome on.
     `display: contents` dissolves .main-col and .stage so the wordmark, search
     and map become direct children of .shell and can be ordered freely. */
  :root { --u: 1px; --lg: 0.56px; }
  .shell { flex-direction: column; background: var(--backdrop); }
  .main-col, .stage { display: contents; }
  .panel-head { order: 0; flex: 0 0 auto; justify-content: center; padding: 18px 16px 0; }
  .banner      { order: 2; flex: 0 0 auto; }
  .layout      { order: 3; flex: 1 1 auto; min-height: 0; }

  /* The hero column has nowhere to go here; .mcta at the foot of the screen
     carries its two lines and its store links instead. */
  .hero { display: none; }

  /* The footnote would only crowd the list; the same caveat is in the disclaimer
     that opens on load. */
  .botbar { display: none; }

  /* No panes to line up with — the map is full width. Search and Filters already
     share .banner-map, so the row needs nothing but its padding back; the count
     and sort move into the list head, which leaves the right half empty.
     The containment has to go with the sizing: an inline-size container takes its
     width from outside, and here .banner-map is the whole row. */
  /* z-index, not just padding: .mcta is a flex item of <body> at z-index 40, and
     .banner is the stacking context every dropdown here lives in — the filter
     sheet's own z-index:50 is scoped inside it. Below 40 the sheet's foot paints
     under the pitch strip and, worse, the strip wins the hit test, so "Show
     results" cannot be tapped at all. */
  .banner { padding: 22px 12px 7px; z-index: 50; }
  .banner-map { padding: 0; gap: 12px; container-type: normal; }
  .banner-gap, .banner-list { display: none; }
  .search-row { flex: 1 1 auto; max-width: none; height: 40px; border-radius: 12px; }
  .city-btn { font-size: 13.5px; letter-spacing: -0.03em; padding: 0 10px 0 12px; gap: 4px; border-radius: 11px 0 0 11px; max-width: 150px; }
  .city-btn .ci-pin { width: 14px; height: 14px; }
  .city-btn .chev { width: 16px; height: 16px; }
  .search-ico { left: 6px; }
  .search-ico svg { width: 19px; height: 19px; }
  #placeSearch { font-size: 13.5px; padding: 0 28px 0 32px; border-radius: 0 11px 11px 0; }
  .filter-btn { height: 40px; padding: 0 11px 0 10px; gap: 7px; border-radius: 12px; font-size: 13.5px; }
  .filter-btn > svg { width: 20px; height: 20px; }
  .filter-btn .filter-badge { min-width: 18px; height: 18px; border-radius: 9px; font-size: 11px; }

  /* Map fills the whole area and the listings ride over it as a sheet. It loads
     parked at the comp's split — 45.6dvh of the 72dvh sheet showing, which puts
     its top edge exactly where the comp draws it — and drags to two other stops:
     all the way up for the most list, or down to a peek that hands the screen
     back to the map. Rounded on top only: the white band above is the same
     surface, so the seam needs softening only where it starts. */
  .layout { display: block; border-top: 0; border-radius: 0; overflow: visible; }
  .map-pane { position: absolute; inset: 0; border-radius: 16px 16px 0 0; overflow: hidden; }
  .gutter { display: none; }
  /* --sheet-y IS the stop: how far down from the top of the map area the sheet
     sits. 31dvh is the comp's split, and it is also the scroller's extra bottom
     padding, because the sheet is as tall as the whole map area and slides down —
     so exactly --sheet-y of its foot hangs off the bottom of the screen, and
     without that padding the last card could never be scrolled into view. The
     drag writes --sheet-y inline, which keeps the two in step for free. */
  .list-pane {
    position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: 20;
    max-width: none; min-width: 0;
    --sheet-y: 31dvh;
    border-left: 0; border-top: 1px solid var(--line);
    border-radius: 18px 18px 0 0; box-shadow: 0 -6px 24px rgba(26, 10, 50, 0.16);
    transform: translateY(var(--sheet-y));
    transition: transform .22s ease;
  }
  .list-pane.is-open { --sheet-y: 5dvh; }
  .list-pane.is-peek { --sheet-y: 62dvh; }
  .list-pane.is-dragging { transition: none; }
  /* 6 + 4 + 2 keeps the count row on the comp's baseline, which is why .list-head
     gives up its own top padding to the handle. */
  /* --line is all but invisible on the cream list well, and this pill is the only
     thing telling anyone the sheet moves. */
  .sheet-handle { display: block; width: 40px; height: 4px; border-radius: 2px; background: rgba(26, 10, 50, 0.18); margin: 6px auto 2px; }
  [data-theme="dark"] .sheet-handle { background: rgba(255, 255, 255, 0.28); }
  /* The count / saved / sort row is back in here on a phone, sitting on the list
     well rather than in the toolbar. */
  /* Doubles as the sheet's grab area, so the browser must not read a vertical drag
     on it as a scroll gesture. */
  .list-head { background: var(--page-bg); padding: 0 12px 0; touch-action: none; }
  .list-head .list-tools { touch-action: auto; }
  .list-head .list-count { font-size: 14px; }
  .list-head .list-tools { gap: 6px; }
  .list-head .saved-btn, .list-head .sort-select { height: 33px; border-radius: 10px; }
  .list-head .saved-btn { padding: 0 9px; gap: 5px; font-size: 13px; }
  .list-head .saved-btn svg { width: 15px; height: 15px; }
  .list-head .sort-select {
    width: 131px; flex: 0 0 auto; font-size: 12.5px; letter-spacing: -0.02em;
    padding: 0 30px 0 11px; background-position: right 11px center; background-size: 15px;
  }
  .active-filters { padding: 8px 0 0; border-bottom: 0; background: transparent; }
  .list-scroll { padding: 7px 12px calc(16px + var(--sheet-y)); }
  /* The comp keeps the phone map clean, and both of these would sit behind the
     sheet anyway: the zoom stepper and the locate button go, pinch does both.
     Attribution has to stay visible, so it moves to the corner the sheet never
     reaches — collapsed behind its own (i). */
  .maplibregl-ctrl-bottom-right { display: none; }
  .maplibregl-ctrl-bottom-left { top: 12px; right: 12px; bottom: auto; left: auto; }
  .theme-switch { top: 14px; left: 12px; width: 50px; height: 26px; }
  .ts-ico svg { width: 13px; height: 13px; }
  .ts-sun { left: 6px; } .ts-moon { right: 7px; }
  .theme-switch-knob { top: 2px; left: 2px; width: 22px; height: 22px; }
  .theme-switch[aria-checked="true"] .theme-switch-knob { transform: translateX(24px); }

  .filter-panel { position: fixed; top: auto; bottom: 0; left: 0; right: 0; width: 100%; max-height: 82dvh; border-radius: 18px 18px 0 0; }
  /* Flush with the bottom edge here, so the foot carries the home indicator's
     inset itself — the sticky offset is unchanged, the bar just grows. */
  .filter-foot { padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px)); }

  .detail-modal { padding: 0; }
  .detail-card { grid-template-columns: 1fr; grid-template-rows: auto minmax(0, 1fr); border-radius: 0; height: 100dvh; max-height: none; width: 100%; }
  .dg-stage { flex: 0 0 auto; height: 44dvh; }
  .dg-strip { display: none; }
  /* On mobile the close button floats over the gallery, not the detail column. */
  .detail-head { padding-right: 0; }
  .detail-body { padding: 16px 16px 28px; }
  .detail-price { font-size: 23px; }
}

/* Narrow columns, wherever they come from — a phone, a dragged-in pane, or a
   desktop at 125% zoom. Keyed off the measured column width in layoutMasonry()
   rather than a viewport breakpoint, since the pane is resizable. */
.list-grid.is-tight .card-main { padding: 7px 9px 9px; }
.list-grid.is-tight .card-rent { font-size: 16px; }
.list-grid.is-tight .card-specs { font-size: 11.5px; }
.list-grid.is-tight .card-loc { font-size: 11.5px; }
.list-grid.is-tight .card-date { font-size: 11px; }
.list-grid.is-tight .card-src, .list-grid.is-tight .tag-managed { font-size: 9.5px; }
