/* Map search. Everything here is logical-property only, so the whole layout
   mirrors from <html dir> exactly like the rest of the site.
   Loaded only by /{locale}/map. */

/* Without the map script the page is a heading, an explanation and a list of
   districts — so the full-height, two-pane shell only switches on when the
   script has actually run. The class is set by a one-line script in <head>. */

.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }

.map-page { padding: 0; }
.map-shell { display: flex; flex-direction: column; background: var(--paper); }
.mapjs .map-shell {
  position: sticky; inset-block-start: var(--nav-h); z-index: 1;
  height: calc(100dvh - var(--nav-h)); overflow: hidden;
}

/* ---------- the bar across the top ---------- */
.map-bar {
  flex: none; display: flex; align-items: center; gap: 12px 16px; flex-wrap: wrap;
  padding: 10px var(--gut); border-block-end: 1px solid var(--line); background: var(--surface);
}
.map-bar-main { margin-inline-end: auto; }
.map-bar h1 { font-size: clamp(17px, 2vw, 21px); }
.map-count { font-size: 13px; color: var(--ink-2); font-weight: 500; }
.map-count.busy { opacity: .55; }
/* the listing-mode switch (functions/_app/modes.js) */
.map-modes { margin-block-start: var(--space-2); }
.map-modes .chip { padding: 4px 11px; font-size: 13px; }
/* One row: search, type, button. The look is the app's own controls (.ctl-sm in
   style.css); only the sizes are set here. */
.map-find { display: flex; align-items: center; gap: 8px; flex: 1 1 520px; max-width: 640px; }
.map-find input[type="search"] { flex: 1 1 220px; min-width: 0; width: auto; }
.map-find select { flex: 0 1 170px; width: auto; min-width: 130px; }
.map-find .btn { flex: none; min-height: 42px; }
.map-auto { display: inline-flex; align-items: center; gap: 7px; font-size: 13.5px; color: var(--ink-2); cursor: pointer; white-space: nowrap; }
.map-auto input { width: 18px; height: 18px; }

.map-chips { flex: none; display: flex; gap: 8px; flex-wrap: wrap; padding: 10px var(--gut);
  border-block-end: 1px solid var(--line); background: var(--surface); }
.map-chips .chip { font-size: 13px; padding: 5px 12px; }
.map-chips[hidden] { display: none; }

/* ---------- the two panes ---------- */
.map-body { flex: 1; min-height: 0; display: grid; grid-template-columns: minmax(320px, 400px) 1fr; }
.mapjs .map-body { position: relative; }

.map-list { min-height: 0; display: flex; flex-direction: column; background: var(--surface);
  border-inline-end: 1px solid var(--line); overflow: hidden; }
.mapjs .map-list { overflow-y: auto; overscroll-behavior: contain; }
.map-grip { display: none; }
.map-intro { padding: 14px 16px; border-block-end: 1px solid var(--line); display: grid; gap: 7px; }
.map-intro p { line-height: 1.6; }
.map-results { flex: 1; min-height: 0; }
.mapjs .map-list { display: block; }
.map-results-head { font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-3); padding: 14px 16px 8px; }
[dir="rtl"] .map-results-head { letter-spacing: 0; text-transform: none; font-size: 12.5px; }
.map-foot { padding: 14px 16px 22px; font-size: 14px; }
.map-foot a { color: var(--jade); font-weight: 600; }
.map-foot a:hover { text-decoration: underline; }
.map-note { padding: 10px 16px; font-size: 12.5px; color: var(--ink-3); }
.map-empty { padding: 24px 16px; font-size: 14.5px; color: var(--ink-2); }

/* ---------- a district row in the list ---------- */
.ma-row, .mu-row {
  display: flex; align-items: center; gap: 12px; padding: 10px 16px;
  border-block-end: 1px solid var(--line); transition: background .15s;
}
.ma-row:hover, .mu-row:hover, .ma-row.is-hot, .mu-row.is-hot { background: var(--surface-2); }
.ma-row.is-hot, .mu-row.is-hot { box-shadow: inset 3px 0 0 var(--jade); }
[dir="rtl"] .ma-row.is-hot, [dir="rtl"] .mu-row.is-hot { box-shadow: inset -3px 0 0 var(--jade); }
.ma-text, .mu-text { display: grid; gap: 2px; min-width: 0; }
.ma-text b, .mu-text > b { font-size: 15px; font-weight: 650; }
.ma-text small, .mu-text small { font-size: 12.5px; color: var(--ink-3); }

/* ---------- the price badge, in the list and on the map ---------- */
.ma-badge {
  flex: none; display: grid; grid-template-columns: auto auto; align-items: baseline;
  justify-content: center; gap: 0 3px; padding: 6px 10px; border-radius: var(--r-sm);
  color: var(--on-ramp); font-weight: 700; line-height: 1.15; min-width: 96px; text-align: center;
  box-shadow: 0 1px 3px rgba(21,24,28,.28);
}
.ma-badge b { font-size: 14px; }
.ma-badge i { font-size: 11px; font-style: normal; opacity: .85; }
.ma-badge small { grid-column: 1 / -1; font-size: 10.5px; font-weight: 600; opacity: .9; }

/* cheapest -> dearest, across what is in view. Five steps, all dark enough for white. */
.lv-0 { background: var(--ramp-0); }
.lv-1 { background: var(--ramp-1); }
.lv-2 { background: var(--ramp-2); }
.lv-3 { background: var(--ramp-3); }
.lv-4 { background: var(--ramp-4); }
.lv-x { background: var(--ramp-x); }
/* Too few units to call it a market rate: say so in the texture as well as the words. */
.ma-badge.thin { background-image: repeating-linear-gradient(45deg, color-mix(in srgb, var(--on-ramp) 22%, transparent) 0 5px, transparent 5px 10px); }

/* ---------- a unit row in the list ---------- */
.mu-row { align-items: stretch; }
.mu-photo { flex: none; width: 78px; height: 62px; object-fit: cover; border-radius: var(--r-xs);
  background: var(--surface-2); }
.mu-text { flex: 1; }
.mu-money { display: flex; align-items: baseline; gap: 6px; }
.mu-money b { font-size: 15px; color: var(--jade); font-weight: 700; }
.mu-money i { font-style: normal; font-size: 11.5px; color: var(--ink-3); }
.mu-code { flex: none; align-self: flex-start; font-size: 11px; color: var(--ink-3);
  font-variant-numeric: tabular-nums; direction: ltr; unicode-bidi: isolate; }

/* ---------- the map ---------- */
.map-canvas { position: relative; min-height: 0; background: var(--surface-2); }
.map-canvas-in { position: absolute; inset: 0; direction: ltr; }
:root:not(.mapjs) .map-canvas-in { position: static; min-height: 240px; display: grid; place-items: center; }
.map-fallback { max-width: 44ch; text-align: center; padding: 24px; display: grid; gap: 8px; }
.mapjs .map-fallback { display: none; }

/* "Search this area", "Draw an area" and "✕ Remove area", centred over the top
   of the map, with the drawing hint on its own line underneath. Above the
   drawing pad, so Cancel and Remove still answer while drawing. */
.map-tools {
  position: absolute; inset-block-start: 14px; inset-inline: 0; margin-inline: auto; z-index: 560;
  width: max-content; max-width: calc(100% - 120px);
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 8px;
  pointer-events: none;
}
.map-tools > * { pointer-events: auto; }
.map-tools > .map-drawhint { pointer-events: none; }   /* a drag may start on the hint too */
:root:not(.mapjs) .map-tools { display: none; }
.map-again, .map-draw, .map-clear {
  padding: 9px 18px; border-radius: var(--r-pill); font-weight: 650; font-size: 14px; cursor: pointer;
  box-shadow: var(--sh-3); white-space: nowrap;
}
.map-again { border: 1px solid var(--jade); background: var(--jade); color: var(--on-jade); }
.map-again:hover { background: var(--jade-2); }
.map-draw, .map-clear { border: 1px solid var(--line-2); background: var(--surface); color: var(--ink); }
.map-draw:hover, .map-clear:hover { border-color: var(--jade); color: var(--jade); }
.map-draw[aria-pressed="true"] { border-color: var(--jade); color: var(--jade); background: var(--jade-wash); }
.map-clear { border-color: var(--jade); color: var(--jade); }
.map-again[hidden], .map-clear[hidden] { display: none; }
.map-drawhint {
  flex-basis: 100%; width: max-content; max-width: 100%; margin-inline: auto; padding: 6px 12px;
  border-radius: var(--r-sm); text-align: center;
  background: color-mix(in srgb, var(--surface) 94%, transparent); border: 1px solid var(--line);
  font-size: 13px; color: var(--ink-2); box-shadow: var(--sh-2);
}
.map-drawhint[hidden] { display: none; }
/* While drawing, a clear sheet over the whole map takes every press, so a drag
   draws wherever it starts: on a district, a badge, a dot or the legend. The
   zoom buttons (z-index 1000 inside Leaflet) stay above it. */
.map-drawpad { position: absolute; inset: 0; z-index: 520; cursor: crosshair; touch-action: none;
  -webkit-user-select: none; user-select: none; }
.map-box { stroke: var(--jade); fill: var(--jade); }

/* The line over the results that says they belong to a drawn area, with the way out. */
.map-scope { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin: 10px 12px 0; padding: 8px 10px 8px 12px; border-radius: var(--r-sm);
  background: var(--jade-wash); color: var(--ink); font-size: 13.5px; font-weight: 600; }
.map-scope button { flex: none; background: none; border: 0; padding: 4px 6px; cursor: pointer;
  color: var(--jade); font-weight: 650; font-size: 13.5px; text-decoration: underline; text-underline-offset: 3px; }

/* The colouring legend: a fold (details.fold, public/assets/fold.js) floating over the map.
   Its summary is a chip that names the colouring ("Colours: Our listings"); open, the
   choice, one line saying what it means, and the ramp with its two ends. Closed by default
   on a phone so it does not cover the map; the state is remembered on the device. It
   floats (absolute), so opening or closing it moves nothing else on the page. */
.map-legend {
  position: absolute; inset-block-end: 88px; inset-inline-start: 14px; z-index: 500;
  background: color-mix(in srgb, var(--surface) 94%, transparent); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: 4px 11px; box-shadow: var(--sh-2);
  width: max-content; max-width: 280px; backdrop-filter: blur(6px);
}
:root:not(.mapjs) .map-legend { display: none; }    /* without a script there is no map to colour */
.map-legend > .ml-chip { min-height: 30px; gap: 7px; font-size: 12px; font-weight: 650; color: var(--ink); }
.map-legend[open] > .ml-chip { color: var(--ink-2); }
.map-legend[data-colour="off"] .ml-chip .ml-swatch { background-image: none; background-color: var(--surface-2); }
.map-legend > .ml-body { display: grid; gap: 6px; margin-block: 0 7px; }
.ml-swatch { flex: none; width: 11px; height: 11px; border-radius: 3px; border: 1px solid color-mix(in srgb, var(--ink) 25%, transparent);
  background-image: linear-gradient(to right, var(--ramp-0), var(--ramp-2), var(--ramp-4)); }
[dir="rtl"] .ml-swatch { background-image: linear-gradient(to left, var(--ramp-0), var(--ramp-2), var(--ramp-4)); }

/* "Colour districts by": three segments of one radio group. The radios are the shared
   ones (style.css), laid over each segment and made transparent, so the whole segment is
   the target and keyboard, screen readers and "disabled" behave as a radio group does;
   the segment draws the choice. */
.ml-set { border: 0; padding: 0; margin: 0; min-width: 0; display: grid; gap: 5px; }
.ml-title { padding: 0; font-size: 11.5px; font-weight: 650; color: var(--ink-2); line-height: 1.35; }
.ml-seg { display: flex; gap: 2px; padding: 2px; border: 1px solid var(--line-2); border-radius: var(--r-pill); background: var(--surface); }
.ml-opt { position: relative; flex: 1 1 auto; display: flex; align-items: center; justify-content: center; min-height: 30px;
  padding: 4px 9px; border-radius: var(--r-pill); font-size: 11.5px; font-weight: 650; line-height: 1.2; text-align: center;
  color: var(--ink-2); cursor: pointer; white-space: nowrap; transition: background .15s, color .15s; }
.ml-opt input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.ml-opt:hover { color: var(--ink); }
.ml-opt:has(input:checked) { background: var(--jade-wash); color: var(--jade); box-shadow: inset 0 0 0 1.5px var(--jade); }
.ml-opt:has(input:focus-visible) { outline: 2px solid var(--jade); outline-offset: 1px; }
.ml-opt:has(input:disabled) { color: var(--ink-3); cursor: not-allowed; }
.ml-opt:has(input:disabled) input { cursor: not-allowed; }
.ml-explain { margin: 0; font-size: 11px; line-height: 1.45; color: var(--ink-2); }

/* The ramp, continuous rather than stepped, and running with the reading direction
   so the cheap end is always where the low number is printed. The five stops are the
   .lv-0 … .lv-4 badge colours; RAMP in map.js interpolates the same five. */
.ml-scale { height: 9px; border-radius: 5px; border: 1px solid color-mix(in srgb, var(--ink) 14%, transparent);
  background-image: linear-gradient(to right, var(--ramp-0), var(--ramp-1), var(--ramp-2), var(--ramp-3), var(--ramp-4)); }
[dir="rtl"] .ml-scale { background-image: linear-gradient(to left, var(--ramp-0), var(--ramp-1), var(--ramp-2), var(--ramp-3), var(--ramp-4)); }
.ml-ends { display: flex; justify-content: space-between; gap: 10px; font-size: 11px; color: var(--ink-2); font-weight: 600; }
.ml-note { margin: 0; font-size: 10.5px; line-height: 1.4; color: var(--ink-3); }
.ml-note[hidden] { display: none; }
/* The ends show only while a colouring is on and has something in view. */
.map-legend[data-empty="yes"] .ml-scale, .map-legend[data-empty="yes"] .ml-ends { display: none; }
.map-legend[data-colour="off"] .ml-note#ml-approx { display: none; }

/* ---------- the shaded regions ---------- */
/* Fill stays translucent so the streets under a district still tell you which
   district it is; the stroke carries the same hue, darker. Both come from map.js,
   because the value they encode is data. A dashed edge means the outline was not
   sourced — see .mr-approx and the popup that goes with it. */
path.mr { transition: fill-opacity .16s var(--ease), stroke-width .16s var(--ease); cursor: pointer; }
/* Not a boundary: a shape we drew round the centroid because OpenStreetMap has none.
   The dashes are the standing reminder; the popup spells it out. */
path.mr-approx { stroke-dasharray: 7 5; }
@media (prefers-reduced-motion: reduce) { path.mr { transition: none; } }

/* Leaflet, dressed in the site's tokens. The attribution stays where Leaflet puts
   it and is never hidden — OpenStreetMap's licence asks for it and it is fair. */
.leaflet-container { background: var(--surface-2); font: inherit; }
.leaflet-container a { color: var(--jade); }
.leaflet-control-attribution a { text-decoration: underline; }   /* not by colour alone (WCAG 1.4.1) */
.leaflet-control-attribution { background: color-mix(in srgb, var(--surface) 88%, transparent) !important;
  color: var(--ink-2); font-size: 11px; padding: 2px 7px; }
[dir="rtl"] .leaflet-control-attribution { direction: rtl; }   /* the canvas is ltr; «خرائط © مساهمو» is not */
.leaflet-bar a { background: var(--surface); color: var(--ink); border-block-end-color: var(--line); }
.leaflet-bar a:hover { background: var(--surface-2); color: var(--ink); }
.leaflet-popup-content-wrapper { background: var(--surface); color: var(--ink); border-radius: var(--r); box-shadow: var(--sh-3); }
.leaflet-popup-tip { background: var(--surface); }
.leaflet-popup-content { margin: 0; width: 244px !important; }
/* Leaflet gives popup paragraphs 1.3em of margin top and bottom. Both cards here are
   grids with their own gap, so that margin only opens holes between the lines. */
.leaflet-popup-content p { margin: 0; }
.leaflet-popup-close-button { color: var(--ink-3) !important; }
/* Leaflet paints every link inside its container jade, which would leave a jade
   button's label invisible on a jade button. The popups' own buttons keep theirs. */
.leaflet-popup-content a.btn-jade { color: var(--on-jade); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .leaflet-tile-pane { filter: grayscale(.3) brightness(.74) contrast(1.06); }
}
:root[data-theme="dark"] .leaflet-tile-pane { filter: grayscale(.3) brightness(.74) contrast(1.06); }

/* ---------- pins ---------- */
/* The marker element itself is a zero-size anchor at the coordinate; the visible
   pill is an absolutely positioned child, so Leaflet keeps its own transform. */
.pin { width: 0; height: 0; }
.pin-in { position: absolute; white-space: nowrap; cursor: pointer; }
.pin .ma-badge { transform: translate(-50%, -50%); }

/* One unit is a dot, not a label. --dot is the unit's own price on the same ramp
   the regions are shaded by, set by map.js; the pale ring is what keeps a dark
   green dot legible on a motorway and a pale one on sand. */
.u-dot {
  transform: translate(-50%, -50%);
  display: block; width: 13px; height: 13px; border-radius: 50%;
  background: var(--dot, var(--ramp-x)); border: 2px solid color-mix(in srgb, var(--on-ramp) 92%, transparent);
  box-shadow: 0 1px 3px rgba(21,24,28,.45);
  transition: transform .12s var(--ease), box-shadow .12s var(--ease);
}
/* Paid placement is a ring, not a colour: the colour is already saying the price. */
.u-dot.feat { border-color: var(--gold); box-shadow: 0 0 0 1px rgba(21,24,28,.35), 0 1px 3px rgba(21,24,28,.45); }
.leaflet-marker-icon.is-hot { z-index: 900 !important; }
.is-hot .u-dot, .u-dot:hover, .leaflet-marker-icon:focus-visible .u-dot {
  transform: translate(-50%, -50%) scale(1.35);
  box-shadow: 0 0 0 3px var(--jade), 0 2px 6px rgba(21,24,28,.4);
}
/* The Pricemap switched off takes the price out of the dots too, rather than
   leave them on a ramp the legend has stopped describing. */
.pm-off .u-dot { background: var(--ink-2); }

/* Hover or keyboard focus: two lines, no photo. */
.u-tip {
  position: absolute; inset-block-end: 14px; inset-inline-start: 50%; transform: translateX(-50%);
  display: none; width: max-content; max-width: min(56vw, 230px); padding: 7px 10px;
  border-radius: var(--r-sm); background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); box-shadow: var(--sh-3); text-align: start;
  white-space: normal; line-height: 1.35; pointer-events: none;
}
.u-dot:hover .u-tip, .leaflet-marker-icon:focus-visible .u-tip { display: block; }
.u-tip-in { display: grid; gap: 2px; }
.u-tip-n { font-size: 12.5px; font-weight: 700; }
.u-tip-m { display: flex; align-items: baseline; gap: 5px; flex-wrap: wrap; font-size: 11.5px; }
.u-tip-m b { color: var(--jade); font-weight: 700; font-size: 12.5px; }
.u-tip-m i { font-style: normal; color: var(--ink-3); }
.u-tip-t { color: var(--ink-2); font-weight: 600; }
.u-tip-t::before { content: "·"; color: var(--ink-3); margin-inline-end: 5px; }
@media (prefers-reduced-motion: reduce) { .u-dot { transition: none; } }

/* More than eight units on one coordinate: the ring keeps its middle for the count. */
.u-count {
  transform: translate(-50%, -50%); pointer-events: none;
  padding: 2px 8px; border-radius: var(--r-pill); background: color-mix(in srgb, var(--ink) 74%, transparent);
  color: var(--paper); font-size: 11px; font-weight: 700; line-height: 1.3;
  box-shadow: 0 1px 3px rgba(21,24,28,.35);
}
.is-hot .ma-badge, .pin-in.ma-badge:hover { outline: 3px solid var(--ink); outline-offset: 1px; }
.leaflet-marker-icon:focus-visible .ma-badge { outline: 3px solid var(--jade); outline-offset: 2px; }

/* ---------- the popup card ---------- */
.mp { display: grid; }
.mp img { width: 100%; height: 132px; object-fit: cover; border-start-start-radius: var(--r); border-start-end-radius: var(--r); background: var(--surface-2); }
.mp-b { padding: 12px 14px 14px; display: grid; gap: 6px; }
.mp-t { font-size: 15px; font-weight: 700; line-height: 1.3; }
.mp-s, .mp-x { font-size: 12.5px; color: var(--ink-3); line-height: 1.45; }
.mp-m { display: grid; gap: 1px; padding: 8px 10px; border-radius: var(--r-sm); background: var(--jade-wash); }
.mp-m .k { font-size: 11.5px; color: var(--ink-2); }
.mp-m .v { font-size: 17px; font-weight: 700; color: var(--jade); }
.mp-r { font-size: 12px; color: var(--ink-2); }
.mp-n { font-size: 11px; color: var(--ink-3); line-height: 1.4; }
.mp .btn { width: 100%; margin-block-start: 2px; }

/* ---------- the district popup ---------- */
.mpa { display: grid; gap: 7px; padding: 13px 14px 14px; }
.mpa-t { font-size: 15px; font-weight: 700; line-height: 1.3; }
.mpa-m { display: grid; gap: 1px; padding: 8px 10px; border-radius: var(--r-sm); background: var(--jade-wash); }
.mpa-m .k { font-size: 11.5px; color: var(--ink-2); }
.mpa-m .v { font-size: 17px; font-weight: 700; color: var(--jade); }
/* A median of three asks is not a market rate. It gets the hatch the badge gets and
   the sentence underneath, so nobody reads it as one. */
.mpa-m.thin { background: var(--surface-2);
  background-image: repeating-linear-gradient(45deg, color-mix(in srgb, var(--clay) 10%, transparent) 0 6px, transparent 6px 12px); }
.mpa-m.thin .v { color: var(--ink); }
.mpa-s, .mpa-x { font-size: 12px; color: var(--ink-3); line-height: 1.45; }
.mpa-warn { font-size: 12px; line-height: 1.45; color: var(--ink-2); font-weight: 600; }
.mpa-n { font-size: 10.5px; color: var(--ink-3); line-height: 1.4; }
.mpa-n.warn { color: var(--ink-2); }
.mpa .btn { width: 100%; margin-block-start: 2px; }
.mpa-zoom { width: 100%; padding: 7px 12px; font: inherit; font-size: 12.5px; font-weight: 650;
  color: var(--ink-2); background: transparent; border: 1.5px solid var(--line-2);
  border-radius: var(--r-pill); cursor: pointer; }
.mpa-zoom:hover { border-color: var(--ink-3); color: var(--ink); }

/* ---------- phones: the map fills the page, the list is a sheet ---------- */
@media (max-width: 900px) {
  .map-bar { padding: 8px var(--gut); gap: 8px 12px; }
  /* The count changes with every search ("… inside your area" is long in Arabic).
     It stays on one line and gives way, rather than pushing the switch and the
     list link onto a row of their own: that reflow shrank the map under a box
     someone had just drawn and nudged it off the place they drew it. The full
     sentence is at the head of the list. */
  .map-bar-main { margin-inline-end: auto; flex: 1 1 0; min-width: 0; }
  .map-count { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .map-find { order: 3; width: 100%; }
  .map-find { max-width: none; flex-basis: 100%; }
  .map-auto { order: 2; }
  .map-browse { order: 2; }
  .map-body { grid-template-columns: 1fr; }
  .map-list { border-inline-end: 0; }

  /* The sheet exists only once the script has taken over (.mapjs, set in <head>). It is
     drawn as the closed sheet from the first paint, before map.js has set data-sheet:
     laid out in the page's flow until then, it moved down 30px when the script ran and
     was most of the page's layout shift (update/PERFORMANCE.md). */
  .mapjs .map-body { display: block; }
  .mapjs .map-canvas { position: absolute; inset: 0; }
  .mapjs .map-list {
    position: absolute; inset-inline: 0; inset-block-end: 0; z-index: 600;
    height: 86%; border-start-start-radius: var(--r-lg); border-start-end-radius: var(--r-lg);
    box-shadow: 0 -8px 30px rgba(21,24,28,.22); border-block-start: 1px solid var(--line);
    transition: transform .26s var(--ease); will-change: transform; touch-action: pan-y;
  }
  .mapjs .map-list[data-sheet="full"] { transform: translateY(0); }
  .mapjs .map-list[data-sheet="half"] { transform: translateY(45%); }
  .mapjs .map-list[data-sheet="peek"], .mapjs .map-list:not([data-sheet]) { transform: translateY(calc(100% - 54px)); overflow: hidden; }
  .mapjs .map-grip {
    display: flex; align-items: center; justify-content: center; gap: 10px; position: sticky;
    inset-block-start: 0; z-index: 2; width: 100%; height: 54px; border: 0; cursor: grab;
    background: var(--surface); border-start-start-radius: var(--r-lg); border-start-end-radius: var(--r-lg);
    font-size: 14px; font-weight: 650; color: var(--ink);
  }
  .mapjs .map-grip:active { cursor: grabbing; }
  .map-grip-bar { position: absolute; inset-block-start: 8px; inset-inline-start: 50%;
    transform: translateX(-50%); width: 40px; height: 4px; border-radius: 2px; background: var(--line-2); }
  /* Clear of the two floating site buttons, which the rule at the end of this block
     parks at 70px from the bottom: at 68px the legend's cheap end sat underneath one
     of them. Above them it is out of everything's way. */
  .map-legend { inset-block-end: 136px; max-width: min(calc(100vw - 28px), 280px); padding: 2px 9px; }
  .ml-title { font-size: 10.5px; }
  .ml-opt { font-size: 11px; padding: 4px 7px; }
  .ml-explain { font-size: 10.5px; }
  .ml-ends { font-size: 10px; gap: 6px; }
  .ml-note { font-size: 10px; }
  .map-tools { inset-block-start: 10px; max-width: calc(100% - 110px); gap: 6px; }
  .map-again, .map-draw, .map-clear { font-size: 13px; padding: 8px 14px; }
  .map-drawhint { font-size: 12px; }
  .mp img { height: 110px; }

  /* the two floating site buttons must not sit on top of the sheet's handle */
  body:has(.map-page) .wa, body:has(.map-page) .as-bubble { inset-block-end: 70px; }
}
@media (min-width: 901px) {
  /* keep the floating buttons clear of Leaflet's attribution strip */
  body:has(.map-page) .wa, body:has(.map-page) .as-bubble { inset-block-end: 34px; }
}
@media (max-width: 560px) {
  .map-bar h1 { font-size: 16px; }
  .map-count { font-size: 12px; }
  .map-find select { min-width: 0; flex-basis: 120px; }
}
