/* map.css — map-view (map-view-v1 D-M4/D-M5/D-M10/D-M13/D-M14 +
   map-view-v2 Phase A part 1: D-M2-1, D-M2-2, D-M2-6, D-M2-7, D-M2-8, D-M2-9
   + map-view-v2 Phase A part 2: D-M2-3, D-M2-4, D-M2-5).
   Only .map-* / #map-panel / .plan-map-row-scoped rules, using var(--*) tokens
   per circle.css header convention. app/static/style.css is NOT modified
   (v2 constraint, D-M14).

   TYPE SCALE (D-M2-9) — the map has no font-size outside this table, and no
   px font sizes anywhere in this file:
     .92rem/800  top-bar count (#map-status)      .78rem/700  chip + control pill
     .78–.95rem  pin / cluster count              1.05rem/800 sheet title
     .82rem/700  sheet subtitle                   .95rem/800  card title / list heading
     .92rem/700  suburb row                       .72rem/600  precision note + attribution
   Every size is rem, so the user's text-size setting scales the map layer with
   the rest of the app; the only fixed-px values are hit areas and marker
   geometry, which must not scale. */

/* ---- D-M2-1: the canvas owns a stacking context ------------------------ */
/* MAP-1 is not a z-index tuning problem, it is a MISSING stacking context.
   While #map-canvas computes z-index:auto, Leaflet's internal pane ladder
   (tiles 200, markers 600, controls 800) is published into the root stacking
   context and beats anything the app can reasonably number — so pins,
   clusters, the zoom control and the attribution all painted OVER the open
   suburb sheet and won the tap. isolation:isolate + an explicit z-index:0
   fixes the CLASS of defect, not the instance: future Leaflet panes, plugins
   and popups are all contained. The overlay numbers then read as one ladder
   in one file. */
#map-panel {
  position: relative;           /* the sheet's containing block */
}

.map-canvas-wrap {
  position: relative;           /* the tile banner floats inside this */
}

#map-canvas {
  position: relative;
  z-index: 0;
  isolation: isolate;           /* belt: contains the 200–800 pane range */
  height: 62vh;
  min-height: 320px;
  /* The map stops being the only square, borderless, shadowless rectangle in
     a rounded-card app (MAP-14), and gains a defined edge for the floating
     chrome in D-M2-3 (H7b) to sit inside. */
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--cream);     /* what shows through when tiles fail — D-M2-7 */
}

@media (min-width: 640px) {
  #map-canvas {
    height: min(70vh, 620px);
  }
}

/* One ladder, map-owned, above the isolated canvas. */
#map-sheet-scrim { z-index: 60; }
#map-sheet { z-index: 61; }
.map-chrome { z-index: 40; }

/* ---- D-M2-6 / MAP-17: visually-hidden precision note -------------------- */
/* D-M12 requires role="application" + aria-label AND a visually-hidden
   restatement of D-M2's precision caveat. The app has no sr-only utility, so
   the map layer ships its own, scoped. A screen-reader user entering the map
   region is told the pins are approximate. */
.map-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---- D-M2-5: a pin is a pin ------------------------------------------- */
/* MAP-8: two teal circles that look the same and do opposite things is the
   exact failure "things that look the same must behave the same" names. A pin
   opens the suburb sheet; a cluster zooms. So the pin becomes a 34px ANCHORED
   marker whose tail tip sits on the centroid — it reads as *pointing at a
   place* rather than floating near one — and the cluster becomes a tailless
   halo whose diameter ramps with count (below).

   Geometry: L.divIcon iconSize [44, 52], iconAnchor [22, 46]. The 34px circle
   is inset left 5 / top 6 inside that box, so the tail tip lands exactly on
   local (22, 46) = the projected centroid, and the 44px transparent hit area
   (D-M5) survives the new icon size. */
.map-pin-icon {
  position: relative;
  background: transparent;
  border: none;
}

.map-pin {
  position: absolute;
  left: 5px;
  top: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--teal-deep, #1a5c66);
  border: 2px solid var(--cream, #fbf7ef);
  color: #fff;
  font-weight: 800;
  font-size: .78rem;
  letter-spacing: .01em;      /* small text: slight POSITIVE tracking */
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(23, 69, 76, .35);
  /* The marker grows from its own tip, not its middle — a pin that scaled
     about its centre would visibly walk off the centroid it points at. */
  transform-origin: 50% 118%;
  /* Press feedback only. The select grow is a spring in map.js. */
  transition: transform 100ms ease-out;
}

/* The tail — 7px wide, centred, tip at the anchor. */
.map-pin::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: -6px;
  transform: translateX(-50%);
  border-left: 6px solid transparent;
  border-right: 6px solid transparent;
  border-top: 8px solid var(--cream, #fbf7ef);
}

.map-pin-count {
  pointer-events: none;
}

/* ---- D-M2-5: a cluster is a cluster ------------------------------------ */
/* Never a tailed pin: a soft halo whose DIAMETER AND HALO RAMP WITH COUNT, so
   "how much is over there" is answerable pre-attentively — a family scanning a
   metro map asks that question, and a two-digit number does not answer it. The
   ramp is the skill's "take a small input and make a big output" applied to
   reading rather than to motion.

     band  activities   Ø     halo   count size
       1      <= 9      36px   5px   .78rem
       2     10-29      40px   7px   .82rem
       3     30-99      46px   9px   .88rem
       4     >= 100     52px  11px   .95rem */
.map-cluster-icon {
  background: transparent;
  border: none;
}

.map-cluster {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--teal, #23808c);
  border: 2px solid var(--cream, #fbf7ef);
  color: #fff;
  font-weight: 800;
  font-size: .78rem;
  letter-spacing: .01em;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 0 0 5px rgba(31, 115, 126, .18), 0 2px 6px rgba(23, 69, 76, .3);
  transform-origin: 50% 50%;
  transition: transform 100ms ease-out;
}

.map-cluster[data-band="2"] {
  width: 40px;
  height: 40px;
  font-size: .82rem;
  box-shadow: 0 0 0 7px rgba(31, 115, 126, .18), 0 2px 6px rgba(23, 69, 76, .3);
}

.map-cluster[data-band="3"] {
  width: 46px;
  height: 46px;
  font-size: .88rem;
  box-shadow: 0 0 0 9px rgba(31, 115, 126, .18), 0 2px 6px rgba(23, 69, 76, .3);
}

.map-cluster[data-band="4"] {
  width: 52px;
  height: 52px;
  font-size: .95rem;
  box-shadow: 0 0 0 11px rgba(31, 115, 126, .18), 0 2px 6px rgba(23, 69, 76, .3);
}

.map-cluster-count {
  pointer-events: none;
}

/* ---- D-M2-5: both answer the finger ------------------------------------ */
/* MAP-9's missing press state is the cheapest possible win: feedback on
   pointer-DOWN, committed on up, with the existing 44px hit area giving the
   ~10px of hysteresis the skill asks for. 100ms, two rules, and the map stops
   feeling dead.

   It is a CLASS, not `:active`: Leaflet calls preventDefault() on mousedown
   inside the map container (that is how it owns the pan gesture), and Chromium
   suppresses :active for a defaultPrevented press — witnessed as
   `transform: none` 95ms into a real mouse-down on a pin. A delegated
   pointerdown listener in map.js sets .is-pressed instead, which is also the
   more honest reading of "respond on pointer-down, not on release". */
.map-pin.is-pressed,
.map-cluster.is-pressed {
  transform: scale(.92);
}

/* Selected (set by openSheet, cleared by closeSheet). The scale is sprung in
   map.js (zeta 0.8 / response 0.35) via --pin-scale; this rule owns the ring
   and the base value so a reduced-motion user gets the state instantly. */
.map-pin.is-selected,
.map-cluster.is-selected {
  transform: scale(var(--pin-scale, 1.18));
  box-shadow: 0 0 0 3px var(--cream, #fbf7ef), 0 0 0 6px var(--teal, #23808c), 0 2px 6px rgba(23, 69, 76, .35);
}

.map-pin.is-selected.is-pressed,
.map-cluster.is-selected.is-pressed {
  transform: scale(calc(var(--pin-scale, 1.18) * .92));
}

/* ---- D-M2-3: Leaflet's own zoom control is not created ------------------ */
/* MAP-13: Leaflet's default control is a white square with a 1px #ccc border
   and a black serif-ish +/-, parked TOP-LEFT — the furthest corner from a
   right-handed thumb on a 390px phone, and directly over the densest
   inner-suburb pins. map.js passes { zoomControl: false } and ships the
   .map-chrome-cluster pills below instead; this rule is the belt in case a
   plugin adds one back. */
.leaflet-control-zoom {
  display: none;
}

/* ---- Attribution restyle ---------------------------------------------- */
.leaflet-control-attribution {
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .015em;
  color: var(--muted, #8a8a8a);
  background: rgba(251, 247, 239, .9);
}

/* ---- Status / loading --------------------------------------------------- */
#map-status {
  margin-top: .5rem;
  font-size: .92rem;
  font-weight: 800;
  line-height: 1.2;
  letter-spacing: .01em;
  color: var(--teal-deep, #1a5c66);
}

#map-loading {
  padding: .5rem;
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--muted, #8a8a8a);
}

/* ---- D-M2-7: tile-failure banner + a canvas that degrades to paper ------ */
/* The banner is NOT an empty state — pins keep rendering, the suburb list keeps
   working, the attribution stays (v1 D-M13 upheld) and the panel's existing
   "Back to plan" escape is one tap away at the top-left. */
.map-chrome {
  background: rgba(251, 247, 239, .78);          /* --cream at 78% */
  backdrop-filter: blur(20px) saturate(180%);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  border: 1px solid rgba(255, 255, 255, .45);    /* bright top edge = light on the material */
  /* Heavier than a card's --shadow: it floats over busy tiles. */
  box-shadow: 0 2px 10px rgba(23, 69, 76, .18);
  color: var(--teal-deep, #1a5c66);
  font-weight: 700;                              /* vibrancy over a changing background */
  letter-spacing: .01em;
}

.map-tile-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-height: 44px;
  padding: 6px 8px 6px 14px;
  border-radius: var(--radius);
  font-size: .78rem;
}

.map-tile-banner-text {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.map-chrome-pill {
  flex: none;
  min-height: 32px;
  padding: 6px 16px;
  font: inherit;
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .01em;
  color: var(--cream, #fbf7ef);
  background: var(--teal-deep, #1a5c66);
  border: none;
  border-radius: 999px;
  cursor: pointer;
}

.map-chrome-pill:active {
  transform: scale(.94);
}

.map-chrome-pill:focus-visible {
  outline: 2px solid var(--teal, #23808c);
  outline-offset: 2px;
}

/* The failed canvas reads as a deliberate schematic of labelled dots — which
   is exactly what it still honestly is — rather than a grey rectangle that
   failed. */
#map-canvas.is-tileless {
  background:
    repeating-linear-gradient(0deg, rgba(23, 69, 76, .04) 0 1px, transparent 1px 24px),
    repeating-linear-gradient(90deg, rgba(23, 69, 76, .04) 0 1px, transparent 1px 24px),
    var(--cream, #fbf7ef);
}

/* ---- D-M2-6: the By-suburb list gets house language --------------------- */
/* MAP-5: D-M12 calls this list "the guarantee" and "the better surface on a
   small phone", and it shipped as 21px Arial UA buttons with disc bullets.
   48px rows (above the 44px floor, because these are dense list rows a thumb
   scans), a card container so the list belongs to the same family as
   everything else in the app, and a --gold inset rule for picks that matches
   the pin treatment without inventing a colour. */
.map-list-heading {
  margin: 18px 0 8px;
  font-size: .95rem;
  font-weight: 800;
  line-height: 1.25;
  letter-spacing: -.005em;
  color: var(--teal-deep, #1a5c66);
}

.map-suburb-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
  box-shadow: var(--shadow);
  overflow: hidden;
}

.map-suburb-list li + li {
  border-top: 1px solid var(--line);
}

.map-suburb-row {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  min-height: 48px;
  padding: 10px 14px;
  font: inherit;
  font-size: .92rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--ink);
  background: transparent;
  border: none;
  cursor: pointer;
  text-align: left;
}

.map-suburb-row-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.map-suburb-row-pick {
  flex: none;
  font-size: .78rem;
  font-weight: 700;
  color: var(--muted);
}

.map-suburb-row:active {
  background: var(--teal-soft);
}

.map-suburb-row:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: -2px;
}

.map-suburb-row.is-pick {
  box-shadow: inset 3px 0 0 var(--gold);
}

/* D-M2 said this; nothing implemented it — the caveat rendered at full body
   weight in full ink and was the loudest sentence on the screen. */
.map-note {
  font-size: .78rem;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: .015em;
  color: var(--muted);
}

.map-filter-summary {
  margin: 10px 0 6px;
}

.map-filter-summary .chip {
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .01em;
}

/* ---- D-M2-8: entry and exit are affordances, not underlined words ------- */
/* MAP-14 measured the three most important controls on the surface at 56x44,
   72x44 and 56x44 underlined text. "An option, never the default" (v1 D-M1) is
   a WEIGHT ruling — outline weight, never --orange — not an instruction to
   make the control hard to see. */
.plan-map-row {
  margin: 10px 0 14px;
}

.plan-map-row button {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  min-height: 48px;
  padding: 12px 18px;
  font: inherit;
  font-size: .92rem;
  font-weight: 700;
  letter-spacing: 0;
  color: var(--teal-deep);
  background: var(--card);
  border: 1.5px solid var(--line);
  border-radius: var(--radius);
  text-decoration: none;
  cursor: pointer;
}

.plan-map-row button:active {
  transform: scale(.98);
}

.plan-map-row button:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 2px;
}

/* ---- Suburb sheet (D-M6) ----------------------------------------------- */
/* Geometry, detents and the gesture are D-M2-4, below; this is type only. */
.map-sheet .daterange-sheet-title {
  font-size: 1.05rem;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -.01em;
}

.map-sheet .daterange-hint {
  font-size: .82rem;
  font-weight: 700;
  line-height: 1.35;
  letter-spacing: 0;
}

.map-sheet .card {
  margin-bottom: 12px;
}

.map-card .card-photo {
  height: 96px;
}

@media (min-width: 640px) {
  .map-card .card-photo {
    height: 120px;
  }
}

/* ---- Your-pick chip (D-M1a) --------------------------------------------- */
/* Fixed legal-disclosure accent: var(--gold), NOT var(--orange-soft). */
.map-chip-pick {
  background: var(--gold, #d4a84b);
  color: #3a2a06;
  font-weight: 700;
}

/* ---- D-M2-5: gold ring for pick-marked pins / clusters (D-M1a) --------- */
/* Unchanged meaning; the ring moves OUTSIDE the cream ring so it survives the
   selected ring and a pin that is both a pick AND selected shows both. */
.map-pin.is-pick,
.map-cluster.is-pick {
  box-shadow: 0 0 0 2px var(--cream, #fbf7ef), 0 0 0 5px var(--gold, #d4a84b), 0 2px 6px rgba(23, 69, 76, .35);
}

.map-pin.is-pick.is-selected,
.map-cluster.is-pick.is-selected {
  box-shadow: 0 0 0 2px var(--cream, #fbf7ef), 0 0 0 5px var(--gold, #d4a84b), 0 0 0 8px var(--teal, #23808c), 0 2px 6px rgba(23, 69, 76, .35);
}


/* ====================================================================== */
/* D-M2-3 — chrome floats OVER the map: one translucent top bar, one       */
/*          thumb-reach control cluster                                    */
/* ====================================================================== */
/* MAP-7 measured it: 200px of a 844px phone spent above the map on an
   underlined link and the word "Map", and every fact about the map 100-300px
   BELOW the fold under a two-line disclaimer set in full-weight body ink. The
   skill's answer is explicit — "build nav/toolbars/sheets as translucent
   layers with content scrolling underneath, not opaque bars that consume a
   fixed strip" — and it buys back ~340px of vertical budget in one move.
   Bigger surface = stronger blur and deeper shadow than a chip: the bar gets
   card radius and the buttons get pills, so material weight encodes hierarchy
   rather than decoration. */

.map-canvas-wrap {
  /* The floating layers position against this, never inside #map-canvas —
     Leaflet owns everything in there. */
  isolation: isolate;
}

/* .map-chrome's default shape is a pill; the bar overrides it below. */
.map-chrome {
  border-radius: 999px;
}

/* One floating column at the top of the canvas: the Back pill, then the top
   bar, then (only when it fires) the tile banner. A column rather than three
   absolutely-placed layers, because the bar's height depends on the user's
   text size and on how many chips there are — three hard-coded `top` values
   would collide the moment either changed (witnessed: the banner landed on
   top of the Back pill when both were pinned to top: 8px).
   The column itself is transparent to the pointer; its children are not, so
   the map stays pannable in the gaps between them. */
.map-chrome-top {
  position: absolute;
  top: 8px;
  left: 8px;
  right: 8px;
  z-index: 41;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  pointer-events: none;
}

.map-chrome-top > * {
  pointer-events: auto;
}

/* Back to plan — the first item in the column, and still the FIRST focusable
   element in the panel (v1 D-M12 upheld). */
.map-chrome-back {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 16px;
  font: inherit;
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .01em;
  cursor: pointer;
}

/* The top bar. Line 1 is the count in the app's voice; line 2 the chip
   scroller with Edit filters as its trailing pill. */
.map-chrome-bar {
  min-width: 0;
  border-radius: var(--radius);   /* a big surface: card radius, not a pill */
  padding: 8px 10px;
}

.map-chrome-bar #map-status {
  margin: 0 0 6px 4px;
}

/* The chip SCROLLER is the map's answer to filter overflow without giving the
   map a filter surface (v1 D-M7 stands): chips never wrap, never push the map
   down, and the affordance to change them is the last item in the same row,
   where the user's eye already is. Chips stay inert <span>s with no role, no
   tabindex and no handler (D-M12) — Edit filters is the only focusable
   control in the row. */
.map-chip-scroller {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: nowrap;
  max-width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
}

.map-chip-scroller::-webkit-scrollbar {
  display: none;
}

.map-chip-scroller .map-filter-summary {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: nowrap;
  margin: 0;
}

.map-chip-scroller .chip {
  flex: none;
  white-space: nowrap;
  scroll-snap-align: start;
}

.map-chrome-edit {
  flex: none;
  white-space: nowrap;
  scroll-snap-align: end;
}

/* Control cluster — bottom-right of the canvas, above the safe-area inset, in
   the app's language (999px pills, Nunito, teal) rather than Leaflet's white
   squares in the corner furthest from a right-handed thumb (MAP-13). */
.map-chrome-cluster {
  position: absolute;
  right: 12px;
  bottom: calc(34px + env(safe-area-inset-bottom, 0px));
  z-index: 41;
  display: flex;
  flex-direction: column;
  gap: 8px;
  pointer-events: none;   /* the gap between the pills stays map, not chrome */
}

.map-chrome-cluster > * {
  pointer-events: auto;
}

.map-chrome-zoom {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  font: inherit;
  font-size: .95rem;
  font-weight: 800;
  line-height: 1;
  cursor: pointer;
}

.map-chrome-back:active,
.map-chrome-zoom:active {
  transform: scale(.94);
}

.map-chrome-back:focus-visible,
.map-chrome-zoom:focus-visible,
.map-chrome-edit:focus-visible {
  outline: 2px solid var(--teal, #23808c);
  outline-offset: 2px;
}

/* The precision note moves INTO the canvas, bottom-left, on the attribution's
   line at .72rem — permanent, non-dismissible, never behind an i (v1 D-M2
   upheld), but no longer the loudest sentence on the screen. */
.map-credit {
  position: absolute;
  left: 10px;
  bottom: 4px;
  z-index: 41;
  margin: 0;
  max-width: calc(100% - 118px);   /* clears the attribution on the same line */
  white-space: nowrap;
  font-size: .72rem;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: .015em;
  color: var(--teal-deep, #1a5c66);
  text-shadow: 0 1px 2px var(--cream, #fbf7ef), 0 0 6px var(--cream, #fbf7ef);
  pointer-events: none;
}


/* ====================================================================== */
/* D-M2-4 — the suburb sheet becomes a REAL sheet                          */
/* ====================================================================== */
/* MAP-3: the sheet cloned .daterange-popover wholesale — an ANCHORED popover
   that relies on `.daterange { position: relative }` as its containing block.
   #map-sheet has no such wrapper, so its offsetParent was BODY and
   `top: calc(100% + 8px)` resolved against the initial containing block:
   witnessed at 1200x900 as a 348x2867px column glued to the left gutter, with
   the page auto-scrolled ~493px away from the map. And `max-height: 70vh`
   existed ONLY inside @media (max-width: 640px), so above 640px the sheet had
   no cap at all.

   It now owns its geometry at EVERY width — the app's 520px column means a
   bottom sheet is right on desktop too.

   The sheet is always its FULL height and is translated down so exactly `x` px
   of it is on screen: --sheet-y = fullHeight - x. Only `transform` and
   `opacity` ever animate (compositor), never `height` (layout). */
.map-sheet {
  position: fixed;
  left: 50%;
  bottom: 0;
  width: min(520px, 100vw);
  height: var(--sheet-full, 92vh);
  max-height: 92vh;
  transform: translate(-50%, 0) translateY(var(--sheet-y, 100%));
  border-radius: var(--radius) var(--radius) 0 0;
  background: var(--card);
  box-shadow: 0 -2px 24px rgba(23, 69, 76, .22);
  /* MAP-16: NO top inset. A bottom-anchored sheet never meets the top safe
     area; the inherited `calc(12px + env(safe-area-inset-top))` added ~47px of
     dead space above the title inside an already-capped sheet. */
  padding: 0 16px calc(16px + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  touch-action: none;           /* the sheet owns the vertical gesture */
}

/* Rubber-banding past `full` moves the sheet a few px above its natural top.
   This paints the sheet's own colour into the gap that opens at the bottom, so
   the boundary can resist visibly without the element ever being taller than
   `full` (which must stay <= 92vh). No overflow:hidden on .map-sheet for the
   same reason. */
.map-sheet::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  height: 80px;
  background: var(--card);
}

/* will-change is set ONLY while a gesture or a spring is live. */
.map-sheet.is-live {
  will-change: transform;
}

/* A 44px drag area holding a 36x5 grabber, 10px above the title. */
.map-sheet-grab-area {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 44px;
  cursor: grab;
}

.map-sheet-grab-area:active {
  cursor: grabbing;
}

.map-sheet-grab {
  width: 36px;
  height: 5px;
  border-radius: 999px;
  background: var(--line);
}

.map-sheet .daterange-sheet-header,
.map-sheet #map-sheet-sub,
.map-sheet #map-sheet-use-suburb {
  flex: none;
}

.map-sheet-body {
  flex: 1 1 auto;
  overflow-y: auto;
  /* MAP-18: a flick that overscrolls the card list must not chain into the
     page underneath and scroll the map panel away. */
  overscroll-behavior: contain;
  min-height: 0;
  touch-action: pan-y;
}

/* The scrim's opacity is bound CONTINUOUSLY to sheet height by map.js, so at
   peek and half it is invisible AND inert and the map stays live under the
   user's thumb — the sheet is a parallel panel there, not a modal. It dims
   only at `full`, where the task genuinely is modal ("dim to focus, separate
   to keep flow"). */
.map-sheet-scrim {
  position: fixed;
  inset: 0;
  background: rgb(23, 69, 76);
  opacity: 0;
  pointer-events: none;
}

/* The page behind gets a scroll lock ONLY at the full detent. */
body.map-sheet-locked {
  position: fixed;
  left: 0;
  right: 0;
  width: 100%;
  overflow: hidden;
}

/* ---- Accessibility: materials degrade, motion is optional --------------- */
@media (prefers-reduced-transparency: reduce) {
  .map-chrome {
    background: var(--cream, #fbf7ef);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-color: var(--line);
  }
}

@media (prefers-contrast: more) {
  .map-chrome {
    background: var(--card);
    border: 1.5px solid var(--teal-deep);
  }
}

@media (prefers-reduced-motion: reduce) {
  .map-chrome-pill:active,
  .map-chrome-back:active,
  .map-chrome-zoom:active,
  .plan-map-row button:active {
    transform: none;
  }

  /* D-M2-5: press becomes instantaneous (the global transition kill) and the
     select scale is applied without a spring. The pin still SHOWS the state —
     that is comprehension; only the unattended motion goes. */
  .map-pin,
  .map-cluster {
    transition: none;
  }

  /* D-M2-4: the GESTURE stays — 1:1 tracking is comprehension, not decoration
     — and only the unattended motion is replaced. Tap-open, tap-dismiss and
     the post-release snap become a 180ms opacity cross-fade with no spring, no
     overshoot and no rubber-band; map.js sets --sheet-y directly.

     This rule is the declarative record of that intent. The fade itself is run
     from map.js through the Web Animations API, because style.css:303-305
     kills EVERY transition and CSS animation under this media query with
     `!important` and style.css is out of bounds for the map layer (v1 D-M14).
     An opacity cross-fade is the reduced-motion guidance's own prescribed
     replacement for a spring, so the blunt global kill must not delete it. */
  .map-sheet.is-fading {
    transition: opacity 180ms ease;
  }
}
