/* city-map-card.css - styles for the Map card and its fullscreen overlay.
 *
 * Loaded by templates2/city-map-card.php at the card's markup position.
 * Pairs with css2/shared-tokens.css - every var(--...) below resolves from there.
 *
 * WARNING - MUST LOAD AFTER THE LEAFLET CDN STYLESHEETS (leaflet.css,
 * leaflet-gesture-handling.min.css). TWO rules override Leaflet's own defaults at
 * EQUAL specificity with no !important, so they win on source order alone:
 *     .leaflet-container            background (Leaflet ships #ddd)
 *     .leaflet-control-scale-line   background/border/color/font-size
 * Load this file first instead and the map canvas turns light grey and the scale bar
 * goes white - silently, on every city page.
 * ⚠ KEEP THIS COUNT ACCURATE. It is what tells a future session whether the CDN tags
 * can be relocated. It has moved twice: five -> four when the geocoder's display:none
 * was dropped, and four -> TWO on 2026-08-02 when the geocoder was removed outright
 * (the two .leaflet-control-geocoder-form input rules went with it). Control.Geocoder.css
 * is no longer loaded at all - every selector it carried contained "geocoder", verified
 * against the CDN file, so nothing else lost styling.
 *
 * WARNING - RULE ORDER IS THE ORIGINAL CASCADE ORDER, exactly as it stood inside
 * city-template.php's <style> block: base map -> light overrides -> map controls,
 * fullscreen overlay, scrubber, popups. Do not sort or regroup.
 *
 * WARNING - DO NOT NAMESPACE OR RE-SCOPE THESE SELECTORS. The map DOM moves at
 * runtime: #cityMap is relocated into #mapFsBody and #mapFsScrubber into the
 * overlay on fullscreen open (both restored on close), and #mapFsInfoPanel is
 * appended to <body> at load. That is why some rules are card-scoped
 * (.map-card #mapFsScrubber, .map-card .leaflet-bottom,
 * .map-card .leaflet-control-attribution) while others are bare (#mapFsInfoPanel):
 * they are written to STOP or START matching as the node moves. Wrapping this file
 * under .map-section or .map-card would break fullscreen.
 *
 * The body.map-fs-open rules hiding #site-header and #menu_banner belong here, and
 * are not stray site CSS: both carry z-index:301 against this overlay's z-index:300,
 * so hiding them is what keeps the header and nav bar off the fullscreen map. Same
 * pattern as css2/calltime-planner-card.css.
 *
 * Not here - still supplied by the host page: .card-head / .card-title /
 * .card-head-right (shared card chrome), .card-ctrl-btn (the reorder arrows), and
 * the #sortableCards / [data-side] rules that size and position the card.
 */

/* ══ MAP ══ */
.map-section { padding:0 28px 36px; }
.map-card {
  max-width:700px; margin:0 auto; border-radius:18px; overflow:hidden;
  border:1px solid rgba(255,255,255,.08);
  box-shadow:0 8px 40px rgba(0,0,0,.45);
  position:relative;   /* anchors the floating Meeting Time Planner scrubber on the card */
  /* ⚠ QUERY CONTAINER. Everything INSIDE the card sizes itself against the CARD's width via
     @container, not the window's. That distinction is real, not pedantry: this card is 700px
     across a viewport range from 716px to 1099px, and roughly HALF that whenever the reorder
     controls pair it beside another card — where a viewport media query would confidently
     apply desktop rules to a ~500px card.
     ⚠ The 700/950/1060 size ladder at the foot of this file stays a MEDIA query on purpose:
     it sets this element's own width, so it cannot be driven by that width without circularity.
     ⚠ container-type implies `contain: layout`, which makes this element a containing block
     for absolutely AND fixed positioned descendants. Checked, and nothing depends on escaping
     it: #mapFsScrubber is already anchored to this element's own position:relative, while
     #mapFsInfoPanel and #mapFsOverlay are both hoisted to <body> at init, and #cityMap is
     moved out to #mapFsBody in fullscreen. Re-check this list before adding anything fixed. */
  container-type: inline-size;
}
/* ══ TOOLBAR — FLOATS, not grid and not flex ══
   The chips sit BESIDE the buttons on line 1, then use the FULL width on lines 2+, passing
   underneath the ▲▼ arrows. Neither grid nor flex can do that: a flex or grid item's wrapped
   content stays inside its own box, which is what produced the old indented chip block.
   Floats are the one layout in CSS where following inline content flows around a box and
   then reclaims the full width below it.
   ⚠ THE STRUCTURE IS IDENTICAL IN BOTH CONTEXTS. That is the whole design, and it is why
   almost nothing below needs context scoping:

       container            block + clearfix     .map-toolbar / #mapFsHeader
         ▲▼ / ← Return      float:right          declared FIRST → RIGHTMOST
         Map / 🗺 Map        inline
         #mapControls       display:contents     still ONE relocatable unit
           .map-tb-right    float:right          ⊟ Slim · ⛶ Full screen · Copy link · !
           #mapFsChips      inline               flows around BOTH floats

   Float mechanics this rests on (CSS 2.1 §9.5.1) — ⚠ DO NOT REORDER THE MARKUP:
   - A float only affects line boxes declared AFTER it, so every float must precede the chips.
   - Same-side floats read RIGHT-TO-LEFT in declaration order: first declared is rightmost.
     That is why ▲▼ / ← Return come FIRST in the markup even though they read last.
   - When the two no longer fit side by side, the LATER-declared float drops to the next row
     and keeps its own edge — .map-tb-right lands under the arrows, still hard right. That
     stacking is free: no breakpoint, no container query, no `order` value anywhere.
   ⚠ The ▲▼ arrows stay top-right on all 18 cards, which is the hard constraint. Under floats
   that holds because they are the FIRST float: nothing can displace them. Chips flowing under
   them from line 2 onward is the agreed behaviour, not a regression.
   ⚠ The padding lives HERE, not on the groups inside, so their contents align on a shared
   line (the old .card-head carried 10px top / 8px bottom and the controls 0 / 10px — visibly
   off the moment they met on one row). */
.map-toolbar {
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
}
/* ⚠ CLEARFIX, and it is load-bearing in both contexts. Every child here is either floated or
   inline; floats do not contribute to their container's height, so without this the toolbar
   computes to near-zero height and collapses onto the map.
   ::after rather than overflow:hidden — overflow would clip anything that legitimately
   overhangs the toolbar box, and on the card it would also interact with the card's own
   `contain: layout`. */
.map-toolbar::after,
#mapFsHeader::after { content: ''; display: block; clear: both; }
/* ══ THE TWO FLOATS ══
   ⚠ Written UNSCOPED on purpose, which reverses the 2026-07-30 rule "anything that relocates
   must be styled through its context, never a bare id". That rule existed because the card and
   the fullscreen header wanted DIFFERENT arrangements, so a shared rule would have followed an
   element into a context that did not want it. They no longer differ — the structure above is
   identical in both — so one rule is correct in both, and a context-scoped pair would just be
   two places to keep in sync. Re-scope only if the two contexts genuinely diverge again.
   ⚠ .card-head-right stays map-scoped regardless: it is shared chrome on all 18 cards. */
.map-card .card-head-right,
#mapFsClose,
.map-tb-right { float: right; margin-left: 12px; }
/* The map's four buttons, in ONE float box: ⊟ Slim · ⛶ Full screen · Copy link · !
   ⚠ It travels — it lives inside #mapControls. ⛶ Full screen travels with it and is hidden in
   fullscreen by `body.map-fs-open .map-fs-btn`, which is why it may sit in the same box as
   three buttons that stay visible there.
   flex-wrap + justify-content:flex-end so that when the card gets too narrow for four buttons
   they wrap INSIDE this box and the wrapped line stays right-aligned under the one above. */
.map-tb-right {
  display: flex; flex-wrap: wrap; justify-content: flex-end;
  align-items: center; gap: 6px;
}
/* ⚠ #mapControls must stay ONE element — the JS relocates it whole into the fullscreen header
   and back. `display:contents` dissolves it for LAYOUT ONLY: .map-tb-right becomes a real
   float in the container's formatting context and #mapFsChips a real inline box, while the
   element itself stays in the DOM and keeps relocating as a single unit. Without this the
   wrapper would be a block box, .map-tb-right would float inside IT rather than inside the
   toolbar, and the chips could never flow around the arrows.
   ⚠ Splitting #mapControls into two relocatable elements was tried on 2026-07-30 and
   REVERTED. Layout belongs in CSS; do not solve it by moving DOM.
   Unscoped for the same reason as the floats above — both contexts want exactly this. */
#mapControls { display: contents; }
/* The title is a plain inline span (shared .card-title sets type only, no display), so it
   shares its line box with the chips that follow it. The margin is what keeps the first chip
   off the word "Map"; chips carry their own margins for every other gap. */
.map-card .card-title { margin-right: 10px; }
/* ⚠⚠ THE CHIPS ARE `inline`, and that is the single change the whole rebuild turns on.
   A flex container does NOT wrap around a float — it shrinks to sit beside it, which is
   precisely what produced the old indented chip block that this work exists to remove. Only
   inline content flows around a float and then reclaims the full width on the lines below.
   ⚠ The cost, accepted: `display:inline` means NO `gap`. Chip spacing moved to margins on
   .map-chip. Do not put `display:flex` back here to get gap back — it silently reinstates the
   indent, and the symptom looks like a margin problem rather than a display problem.
   ⚠ Inline layout makes whitespace between children significant. It is safe here because
   _renderHeaderChips() builds every chip with createElement + appendChild and never writes
   markup text, so there are no text nodes between chips. Keep it that way — switching that
   function to innerHTML with newlines would add a stray space before every chip. */
#mapFsChips { display: inline; }

/* ══ CONTAINER QUERIES — measured against the CARD, never the window ══
   ⚠ THE 560px QUERY IS GONE, and nothing should replace it. It existed to state a wrap point
   that flex could not infer: .map-tb-actions and .map-tb-pair were two independent boxes with
   two different natural wrap points, so the stacked layout existed only as a narrow band and
   then unwound itself as the card kept shrinking. Floats have no such problem — the later
   float simply drops when it stops fitting and stays dropped — so the threshold is now
   emergent instead of declared. Do not add a breakpoint back to "fix" the toolbar.
   What remains is a genuine sizing tweak, not a layout switch. */
/* Tighten the card's own gutters once the CARD is small — which is not the same as the window
   being small. ~470px is the card width at a 480px viewport (16px gutters each side), the
   breakpoint these values previously used as a media query. */
@container (max-width: 470px) {
  .map-toolbar { padding: 10px 12px; }
}
/* Copy link + ! stay boxed together INSIDE .map-tb-right so they wrap as a unit — you want
   "⊟ Slim ⛶ Full screen" / "Copy link !", never a lone ! stranded on the next line.
   flex-shrink:0 — buttons should wrap, never squash. */
.map-tb-pair { display:flex; align-items:center; gap:6px; flex-shrink:0; }
/* ⚠ nowrap: the two ▲▼ arrows must stay side by side. Nothing else shares this box, so there
   is nothing legitimate to wrap, and a wrapped ▲▼ would break the top-right pinning that all
   18 cards rely on. Map-scoped — the shared rule in city-template.php serves ~14 other cards. */
.map-card .card-head-right { flex-wrap:nowrap; }
#cityMap { height:360px; width:100%; display:block; }
.leaflet-container { background:#0d0d1a; }
.leaflet-control-attribution { font-size:9px !important; opacity:.45; }
.leaflet-control-scale-line {
  background: rgba(7,7,14,.72); border-color: rgba(255,255,255,.3);
  color: rgba(241,240,249,.8); font-size:11px;
}
[data-theme="light"] .leaflet-control-scale-line {
  background: rgba(255,255,255,.82); border-color: rgba(0,0,0,.3); color: #3a3a5c;
}

/* Light-theme map overrides. These two sat inside the template's shared LIGHT THEME
   block, away from the rules above; kept in their original relative order. */
[data-theme="light"] .map-card { border-color:rgba(0,0,0,.12); box-shadow:0 8px 40px rgba(0,0,0,.12); }
[data-theme="light"] .leaflet-container { background:#f0ede8; }

/* ══ MAP CONTROLS ══ */
.map-sat-btn {
  background: rgba(7,7,14,.75); border: 1px solid rgba(255,255,255,.18);
  border-radius: 8px; color: rgba(241,240,249,.8); font-size: 12px;
  font-family: 'Inter', sans-serif; font-weight: 500;
  padding: 4px 10px; cursor: pointer; letter-spacing: .04em;
  transition: color .15s, border-color .15s, background .15s;
  white-space: nowrap;
}
.map-sat-btn:hover { color: #f1f0f9; border-color: rgba(129,140,248,.5); background: rgba(7,7,14,.92); }
[data-theme="light"] .map-sat-btn {
  background: rgba(255,255,255,.82); border-color: rgba(0,0,0,.18); color: #3a3a5c;
}
[data-theme="light"] .map-sat-btn:hover { background: rgba(255,255,255,.97); border-color: rgba(91,95,232,.4); }
/* Tile-mode picker — three buttons always visible */
.map-tile-picker { display: flex; gap: 3px; }
.map-sat-btn.map-tile-on {
  background: rgba(129,140,248,.28); color: #fff;
  border-color: rgba(129,140,248,.55);
}
[data-theme="light"] .map-sat-btn.map-tile-on {
  background: rgba(91,95,232,.15); color: rgba(60,60,200,.95);
  border-color: rgba(91,95,232,.45);
}

/* ══ VIEW TOGGLE — collapses the on-map furniture on narrow screens ══
   On a phone the map is ~360px tall and carries four stacked controls bottom-left plus My
   location and the scale bottom-right. `View ▾` hides them all; `View ▴` brings them back.

   ⚠ HIDDEN IN PLACE, never relocated — each control keeps its own corner and simply stops
   rendering, so showing them again needs no repositioning and nothing can land wrong.
   ⚠ MEDIA query, not container query: the map is moved out of .map-card in fullscreen, so a
   container query could not reach it there. 699px covers phones and small tablets in both.
   ⚠⚠ 699 IS THE MAP CARD'S ONE NARROW BREAKPOINT — keep all three in step. The other two live
   further down this file: #mapFsScrubber (tightens padding, drops from bottom:22px to 14px) and
   #mapFsHeader (padding 8px 16px). They were 700px and this was briefly 699, which made the
   scrubber shift a step before the controls collapsed — visible in fullscreen as the pill
   dropping on its own. All three are now 699 so narrow mode arrives in a single step.
   ⚠ Change one and change all three, or that stagger comes back.
   ⚠ Desktop is untouched — the button is display:none above the breakpoint and the collapse
   rule does not exist there, so the class the JS sets is completely inert.
   ⚠⚠ THE ATTRIBUTION IS DELIBERATELY EXCLUDED. It sits in the same bottom-right corner as My
   location and the scale bar, but hiding it would drop the OpenStreetMap/Esri credit that the
   tile licences REQUIRE. Never simplify that :not() away. */
.map-view-toggle { display: none; }
@media (max-width: 699px) {
  .map-view-toggle { display: block; }
  .map-view-collapsed .leaflet-bottom.leaflet-left  > .leaflet-control:not(.map-view-toggle),
  .map-view-collapsed .leaflet-bottom.leaflet-right > .leaflet-control:not(.leaflet-control-attribution) {
    display: none;
  }
}

/* ══ CITY SEARCH (top-right of the map) ══
   Replaced the Leaflet Control.Geocoder on 2026-08-02 — see the long note at the control's
   construction in js2/city-map-card.js for why. Styled to match .map-sat-btn above rather than
   Leaflet's plugin look, since it is now one of the map's own controls.
   ⚠ Widths are the one thing that differs by context, so the override is `.map-card`-scoped —
   it stops applying the instant the map is relocated into #mapFsBody, exactly like
   `.map-card .leaflet-bottom`. The base width below is therefore the FULLSCREEN width. */
/* ⚠ The top-right control corner is lifted above Leaflet's default 1000, and the reason is
   concrete: #mapFsScrubber (the Meeting Time Planner strip along the bottom of the map) is
   ALSO z-index:1000, and it sits after #cityMap in the markup — so at equal z-index the
   scrubber wins on DOM order and painted over the lower rows of an open search dropdown.
   Worst on a narrow or short card, where a 310px list reaches all the way down to it.
   ⚠ Raising the CORNER, not just .map-search-dd: `.leaflet-top` is position:absolute with a
   z-index, so it forms a stacking context — bumping the dropdown alone can never lift it past
   a sibling of #cityMap, only past its neighbours inside that corner.
   ⚠ Unscoped on purpose, like the toolbar floats: the card and the fullscreen map want the
   same answer, and this corner only ever exists inside this component's map. */
.leaflet-top.leaflet-right { z-index: 1100; }
.map-search { position: relative; }
.map-search-input {
  width: 220px; box-sizing: border-box;
  background: rgba(7,7,14,.75); border: 1px solid rgba(255,255,255,.18);
  border-radius: 8px; color: rgba(241,240,249,.9);
  font-family: 'Inter', sans-serif; font-size: 12px; font-weight: 500;
  padding: 5px 9px; outline: none;
  transition: border-color .15s, background .15s;
}
.map-search-input::placeholder { color: rgba(241,240,249,.45); }
.map-search-input:focus { border-color: rgba(129,140,248,.55); background: rgba(7,7,14,.92); }
/* ⚠ 150px on the card, at the user's request — the map and its clocks want the width back.
   The old geocoder sat at 220px here. */
.map-card .map-search-input { width: 150px; }
@container (max-width: 470px) {
  .map-card .map-search-input { width: 120px; }
}
/* Right-anchored so it grows leftward over the map instead of off the edge — the input can be
   narrower than the list without truncating city names. */
/* ⚠ max-height must clear the first page of results PLUS the footer, or the dropdown scrolls
   from the very first search and the "8 of 22 ↓" line sits below the fold — which is the one
   thing that line exists to advertise. Now derivable, because both line-heights are pinned:
       row    = 12px × 1.35 line + 7px/7px padding + 1px border   ≈ 31px
       8 rows ≈ 247px  (the last row has no bottom border)
       footer = 14px button (line-height 1) + 6/6 padding + 1px   ≈  27px
       total  ≈ 274px
   310px clears it with ~36px spare. ⚠ The ceiling is the MAP, not taste: on the 700px card the
   map is 360px tall and this list starts ~41px down, so ~319px is all there is. Do not exceed
   that or the dropdown hangs off the bottom of the small card.
   ⚠ History, so this is not "tuned" blindly a fourth time: 260px predated the footer; 290px was
   computed against an UNPINNED line-height and still overflowed, because the flag emoji in each
   row silently inflated it. The fix was pinning the line-heights, not raising the number again.
   ⚠ Raise MAP_SEARCH_FIRST in js2/city-map-card.js and this has to grow with it — but the map
   ceiling above is the hard limit, so past ~9 rows the first page would have to scroll anyway.
   Scrolling AFTER clicking ↓ is correct and expected — 18 rows are meant to scroll. */
/* ⚠ 240px, wider than the 220px input in fullscreen and much wider than the 150px one on the
   card — deliberate. It is right-anchored, so it grows leftward over the map and long city
   names get room instead of being cut off by the ellipsis. Width does not affect the height
   arithmetic above: rows are white-space:nowrap, so they never wrap to a second line.
   Slim scrollbar matches .ov-pick-dd in css2/calltime-planner-card.css so the page's two city
   dropdowns feel the same. scrollbar-width/color covers Firefox; the ::-webkit- rules cover
   Chrome/Edge/Safari. ⚠ Its own class, not borrowed from the planner — this component has to
   render correctly on a page that does not load the planner card. */
.map-search-dd {
  display: none; position: absolute; top: calc(100% + 4px); right: 0; z-index: 1000;
  width: 240px; max-height: 310px; overflow-y: auto;
  background: rgba(12,12,24,.96); border: 1px solid var(--border);
  border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,.5);
  scrollbar-width: thin; scrollbar-color: rgba(255,255,255,.15) transparent;
}
.map-search-dd::-webkit-scrollbar       { width: 4px; }
.map-search-dd::-webkit-scrollbar-track { background: transparent; }
.map-search-dd::-webkit-scrollbar-thumb { background: rgba(255,255,255,.15); border-radius: 4px; }
.map-search-dd.open { display: block; }
/* ⚠ line-height is PINNED, and that is not cosmetic. Each row carries a flag emoji, and emoji
   routinely render taller than their nominal font size — by a few px, differing per platform
   and per installed emoji font. With `line-height: normal` the row height was therefore both
   larger than the text implies and NOT predictable, which is what made the dropdown overflow
   its max-height on every search however the number was tuned. Pinning it makes the row a
   known 31px so the max-height below can actually be derived. `align-items: center` still
   centres a taller glyph, and nothing clips. */
.map-search-row {
  display: flex; align-items: center; gap: 7px; width: 100%;
  background: none; border: 0; border-bottom: 1px solid var(--border);
  padding: 7px 10px; cursor: pointer; text-align: left;
  font-family: 'Inter', sans-serif; font-size: 12px; line-height: 1.35; color: var(--text);
}
.map-search-row:last-child { border-bottom: 0; }
.map-search-row:hover,
.map-search-row.sel { background: var(--surface); }
.map-search-flag { flex-shrink: 0; }
.map-search-city { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.map-search-rgn  { flex-shrink: 0; color: var(--dim); font-size: 11px; white-space: nowrap; }
.map-search-none {
  padding: 9px 10px; color: var(--dim);
  font-family: 'Inter', sans-serif; font-size: 12px;
}
/* "8 of 22 ↓" footer. Deliberately the same shape as .ov-pick-footer / .ov-pick-more in
   css2/calltime-planner-card.css so the page's two city dropdowns behave and read alike —
   just one step down in type size, since this one sits over the map.
   ⚠ Kept as its own class rather than borrowing the planner's: this component must render
   correctly on a page that does not load the planner card. */
.map-search-more {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  padding: 6px 10px; border-top: 1px solid var(--border);
  font-family: 'Inter', sans-serif; font-size: 11px; line-height: 1; color: var(--dim);
}
.map-search-more-btn {
  background: none; border: none; padding: 0;
  color: var(--accent2); font-size: 14px; line-height: 1; cursor: pointer;
  opacity: .75; transition: opacity .15s, transform .15s;
}
.map-search-more-btn:hover { opacity: 1; transform: translateY(2px); }
[data-theme="light"] .map-search-input {
  background: rgba(255,255,255,.86); border-color: rgba(0,0,0,.18); color: #1a1a2e;
}
[data-theme="light"] .map-search-input::placeholder { color: rgba(24,24,39,.45); }
[data-theme="light"] .map-search-input:focus { background: rgba(255,255,255,.97); }
[data-theme="light"] .map-search-dd {
  background: rgba(255,255,255,.99); border-color: rgba(0,0,0,.12);
  scrollbar-color: rgba(0,0,0,.15) transparent;
}
[data-theme="light"] .map-search-dd::-webkit-scrollbar-thumb { background: rgba(0,0,0,.15); }
[data-theme="light"] .map-search-row { color: #1a1a2e; border-bottom-color: rgba(0,0,0,.06); }
[data-theme="light"] .map-search-row:hover,
[data-theme="light"] .map-search-row.sel { background: rgba(0,0,0,.04); }

/* ══ MAP FULLSCREEN ══ */
/* ⚠ font-size 11px, not the 12px this carried until 2026-08-01. ⛶ Full screen now shares one
   float box with ⊟ Slim, Copy link and ← Return, all of which are 11px; at 12px it rendered
   visibly taller than its immediate neighbours. Change all four together or none. */
.map-fs-btn {
  background: none; border: 1px solid var(--border);
  border-radius: 8px; color: var(--dim); font-size: 11px;
  font-family: 'Inter', sans-serif; font-weight: 500;
  padding: 4px 12px; cursor: pointer; letter-spacing: .04em;
  transition: color .15s, border-color .15s;
}
.map-fs-btn:hover { color: var(--text); border-color: rgba(129,140,248,.4); }
/* ⚠ `.map-fs-btn.active` was deleted 2026-08-01 as provably dead, not as tidying. It styled
   the CARD's ⊟ Slim button back when that was a separate JS-built copy carrying .map-fs-btn.
   Since 4a there is one Slim button, it is #mapFsCompact and it does NOT carry this class, so
   the only remaining .map-fs-btn is ⛶ Full screen — which is never given .active by anything.
   Swept the file: .active is toggled in exactly two places, on #mapFsCompact (styled by
   `#mapFsCompact.active`) and on #mapFsInfo (styled by `#mapFsInfo.active`). Both keep their
   own rules. Re-add this only if a .map-fs-btn ever gains a toggled state. */
body.map-fs-open .map-fs-btn { display: none; }

#mapFsOverlay {
  display: none;
  position: fixed; inset: 0; z-index: 300;
  background: var(--bg);
  flex-direction: column;
}
body.map-fs-open #mapFsOverlay { display: flex; }
body.map-fs-open #site-header,
body.map-fs-open #menu_banner { display: none; }
body.map-fs-open { overflow: hidden; }

/* ⚠ A BLOCK, same as .map-toolbar — the two contexts are deliberately the same shape now.
   It was `display:flex` until 2026-08-01; that is gone along with every `order` value.
   ⚠ flex-shrink:0 STAYS. This element stops being a flex CONTAINER but remains a flex ITEM of
   #mapFsOverlay (which is display:flex; flex-direction:column). Drop it and the header gets
   squeezed by the map below it. The clearfix is up with .map-toolbar's. */
#mapFsHeader {
  padding: 10px 28px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0; position: relative; z-index: 1;
}
#mapFsLabel {
  font-family: 'Inter', sans-serif; font-size: 11px; font-weight: 500;
  color: var(--dim); white-space: nowrap;
  letter-spacing: .08em; text-transform: uppercase;
}
/* ⚠ No `margin-left:auto` any more — it is a float:right now (see the toolbar block), and it
   must stay the FIRST child of #mapFsHeader so it floats rightmost.
   ⚠ `flex-shrink:0` went with it: #mapFsHeader is no longer a flex container, so this button
   is not a flex item and the property was inert. The other three buttons keep theirs — they
   sit inside .map-tb-right / .map-tb-pair, which ARE still flex. */
#mapFsClose {
  background: none; border: 1px solid var(--border);
  border-radius: 8px; color: var(--dim); font-size: 11px;
  font-family: 'Inter', sans-serif; font-weight: 500;
  padding: 4px 12px; cursor: pointer; letter-spacing: .04em;
  transition: color .15s, border-color .15s;
}
#mapFsClose:hover { color: var(--text); border-color: rgba(129,140,248,.4); }
#mapFsCompact {
  flex-shrink: 0;
  background: none; border: 1px solid var(--border);
  border-radius: 8px; color: var(--dim); font-size: 11px;
  font-family: 'Inter', sans-serif; font-weight: 500;
  padding: 4px 12px; cursor: pointer; letter-spacing: .04em;
  transition: color .15s, border-color .15s, background .15s;
}
#mapFsCompact:hover { color: var(--text); border-color: rgba(129,140,248,.4); }
#mapFsCopyLink {
  flex-shrink: 0;
  background: none; border: 1px solid var(--border);
  border-radius: 8px; color: var(--dim); font-size: 11px;
  font-family: 'Inter', sans-serif; font-weight: 500;
  padding: 4px 12px; cursor: pointer; letter-spacing: .04em;
  transition: color .15s, border-color .15s;
}
#mapFsCopyLink:hover { color: var(--text); border-color: rgba(129,140,248,.4); }
#mapFsInfo {
  flex-shrink: 0; width: 26px; height: 26px;
  background: none; border: 1px solid var(--border); border-radius: 50%;
  color: var(--dim); font-size: 13px; font-weight: 600;
  font-family: 'Inter', sans-serif; cursor: pointer; line-height: 1;
  transition: color .15s, border-color .15s, background .15s;
}
#mapFsInfo:hover, #mapFsInfo.active {
  color: var(--accent2); border-color: rgba(129,140,248,.5);
  background: rgba(129,140,248,.10);
}
/* Info panel — drops down below the header bar.
   Shown/hidden by the JS writing style.display directly, NOT by a class: the panel
   is appended to <body> at load and positioned with inline top/left, so the JS owns
   its inline style anyway. A dead ".open" variant of this rule used to sit here and
   was removed - nothing anywhere ever added that class. Do not reintroduce one
   without also changing js2/city-map-card.js, which reads style.display to decide
   whether the panel is visible. */
#mapFsInfoPanel {
  display: none; position: fixed; top: 58px; right: 28px;
  width: 420px; max-width: calc(100vw - 56px);
  background: rgba(12,12,26,.97); border: 1px solid rgba(255,255,255,.12);
  border-radius: 14px; padding: 18px 20px 16px;
  box-shadow: 0 8px 40px rgba(0,0,0,.65); z-index: 500;
}
[data-theme="light"] #mapFsInfoPanel {
  background: rgba(255,255,255,.97); border-color: rgba(0,0,0,.12);
  box-shadow: 0 8px 40px rgba(0,0,0,.15);
}
#mapFsInfoClose {
  position: absolute; top: 10px; right: 12px;
  background: none; border: none; color: var(--dim);
  font-size: 18px; line-height: 1; cursor: pointer; padding: 0;
  transition: color .15s;
}
#mapFsInfoClose:hover { color: var(--text); }
.mfi-grid {
  display: grid; grid-template-columns: 22px 1fr; gap: 7px 10px;
  align-items: start;
}
.mfi-icon {
  font-size: 13px; color: var(--accent2); text-align: center;
  padding-top: 1px; line-height: 1.5;
}
.mfi-icon.mfi-dim { opacity: .4; }
.mfi-icon.mfi-pair { font-size: 10px; white-space: nowrap; letter-spacing: 1px; }
.mfi-text {
  font-size: 12px; font-family: 'Inter', sans-serif;
  color: var(--mid); line-height: 1.5;
}
.mfi-text strong { color: var(--text); font-weight: 500; }
.mfi-section {
  grid-column: 1 / -1; display: flex; align-items: center; gap: 8px;
  font-size: 11px; color: var(--mid);
  border-top: 1px solid var(--border); padding-top: 8px; margin-top: 2px;
}
.mfi-chip-example {
  display: inline-flex; align-items: center; gap: 3px; pointer-events: none;
  background: rgba(129,140,248,.15); border: 1px solid rgba(129,140,248,.4);
  border-radius: 20px; padding: 2px 6px 2px 8px;
  font-size: 10px; color: #a5b4fc; white-space: nowrap;
}
.mfi-chip-example .mfi-chip-star { color: #a5b4fc; margin: 0 1px; }
.mfi-chip-example .mfi-chip-x   { color: rgba(241,240,249,.55); }
#mapFsCompact.active {
  color: var(--accent2); border-color: rgba(129,140,248,.5);
  background: rgba(129,140,248,.10);
}

/* ── Time scrubber — floats over the bottom of the fullscreen map. Drag to
   freeze the day/night terminator + every open clock at a chosen moment;
   ● Live snaps back to real time. Shown only in fullscreen. ── */
#mapFsScrubber {
  display: none;
  position: absolute; left: 50%; bottom: 22px; transform: translateX(-50%);
  z-index: 1000;
  grid-template-columns: auto minmax(40px, 1fr) auto;   /* ● Live | slider (flexes/shrinks) | readout — the slider yields so the readout never runs off-screen */
  align-items: center; column-gap: 12px; row-gap: 3px;
  width: min(580px, calc(100vw - 32px));   /* DEFINITE width so the 1fr slider column resolves predictably (centred, ≤580px, shrinks on narrow) */
  padding: 8px 12px 8px 14px;
  background: rgba(12,12,26,.86); border: 1px solid rgba(255,255,255,.12);
  border-radius: 999px; box-shadow: 0 6px 28px rgba(0,0,0,.5);
  /* No backdrop-filter here on purpose. The fill above is rgba(12,12,26,.86) - 86%
     opaque - so only 14% of the backdrop showed through and a blur(6px) behind it
     was not visible. Deleted 2026-08-15 in the card-surface pass; it cost a
     compositing layer and a backdrop re-sample per frame for nothing. */
}
body.map-fs-open #mapFsScrubber { display: grid; }
/* On the card, the same scrubber floats over the bottom of the card map (card-relative width, not 100vw) */
.map-card #mapFsScrubber { display: grid; width: min(580px, calc(100% - 24px)); bottom: 14px; grid-template-rows: auto minmax(16px, auto) auto; }   /* snug to bottom at all widths; row 2 reserves slider-thumb room so the readout can span the column without the thumb overflowing */
/* Card version is vertically COMPACT (~half the FS pill height) — minimal vertical padding,
   tight row-gaps, slimmer ● Live button + marks/label. Scoped to .map-card so FS stays full. */
.map-card #mapFsScrubber { padding-top: 2px; padding-bottom: 2px; row-gap: 1px; }
/* ● Live keeps its full Full-Screen size, just placed in the left column and vertically CENTRED
   (not stretched) — so it matches FS and doesn't grow the thin card slider row. */
.map-card #mapFsScrubber .map-scrub-live { grid-row: 1 / 4; align-self: center; }
.map-card #mapFsScrubber .map-scrub-marks { font-size: 8px; }
.map-card #mapFsScrubber .map-scrub-caption { font-size: 9px; }
/* Readout spans the column + centres (like ● Live) so a two-line readout fits in the pill's
   existing height instead of growing it → consistent pill height whether the readout is 1 line or 2. */
.map-card #mapFsScrubber .map-scrub-readout { grid-row: 1 / 4; align-self: center; }
/* Lift the map's bottom controls clear of the floating scrubber bar on narrow/tablet
   widths, where the centred bar would otherwise overlap the corner controls
   (left tile/view stack, right "My location" + attribution). Wide desktops have
   clearance, so the bar floats free there. */
@media (max-width: 1100px) {
  body.map-fs-open .leaflet-bottom { bottom: 106px; }   /* clears the 3-row pill (marks + slider + label), ~94px tall at desktop-scrubber widths */
  /* …but keep the OSM/CARTO credit pinned to the real bottom-right corner — it is short
     enough to tuck just under the bar's bottom edge, so it stays visible without lifting. */
  body.map-fs-open .leaflet-control-attribution { position: fixed; right: 0; bottom: 0; margin: 0; }
}
/* Same idea on the CARD: lift the card map's bottom controls clear of the floating scrubber.
   Matches only while the map is in the card (in FS the controls move into the overlay). Always
   on — the card (≤700px) is always narrower than the pill's reach. */
.map-card .leaflet-bottom { bottom: 53px; }
/* Keep the OSM/CARTO credit at the CARD's bottom-right corner. position:fixed (the FS trick)
   would pin to the whole window here, so instead offset it back down by the same 106px the
   container was lifted → it lands at the card corner. Keep -106px in sync with the lift above. */
.map-card .leaflet-control-attribution { position: absolute; right: 0; bottom: -53px; margin: 0; white-space: nowrap; }
[data-theme="light"] #mapFsScrubber {
  background: rgba(255,255,255,.92); border-color: rgba(0,0,0,.12);
  box-shadow: 0 6px 28px rgba(0,0,0,.18);
}
.map-scrub-live {
  flex-shrink: 0; background: none; border: 1px solid var(--border);
  border-radius: 999px; color: var(--dim);
  font-family: 'Inter', sans-serif; font-size: 11px; font-weight: 600;
  letter-spacing: .04em; padding: 5px 13px; cursor: pointer; white-space: nowrap;
  transition: color .15s, border-color .15s, background .15s;
}
.map-scrub-live:hover { color: var(--text); border-color: rgba(129,140,248,.4); }
/* The dot is DRAWN, not the ● glyph in the markup. As a text character it shared a
   baseline with "Live", and its ink sits low in its own em box - at 9px against the
   button's 11px it read visibly low next to the cap-height L, in the card and in
   fullscreen alike. vertical-align:middle alone does not fix that (it centres the box,
   not the ink), so the glyph is hidden with font-size:0 and replaced by a circle.
   ⚠ background:currentColor is deliberate - it keeps BOTH existing color: rules driving
   the dot, including the amber #mapFsScrubber.frozen override below. Don't swap it for a
   literal colour. Also makes the alignment font-independent (the button falls back to
   plain sans-serif until Inter loads). Width/height are the only tunable numbers. */
.map-scrub-live .scrub-dot {
  color: #4ade80; margin-right: 5px; font-size: 0;
  display: inline-block; width: 6px; height: 6px;
  border-radius: 50%; background: currentColor;
  vertical-align: middle;
  /* Optical lift, user's call. vertical-align:middle centres against the parent's
     x-height, but "Live" is cap-height text, so a geometrically centred dot still reads
     low. position:relative + top shifts it visually with NO layout effect (no line-box
     growth, no reflow of the grid cell) - preferred over transform, which would create a
     stacking context. Tune this number, not vertical-align. */
  position: relative; top: -1px;
}
/* Frozen: the Live button lights up as the way back to real time */
#mapFsScrubber.frozen .map-scrub-live {
  color: var(--accent2); border-color: rgba(129,140,248,.5);
  background: rgba(129,140,248,.12);
}
#mapFsScrubber.frozen .map-scrub-live .scrub-dot { color: #fbbf24; }
.map-scrub-range {
  -webkit-appearance: none; appearance: none;
  width: 100%; max-width: 100%; height: 4px; border-radius: 999px;   /* fills the flexible (1fr) grid cell, so it shrinks before the readout does */
  background: var(--border); outline: none; cursor: pointer; margin: 0;
}
.map-scrub-range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--accent2); border: 2px solid #fff;
  box-shadow: 0 1px 4px rgba(0,0,0,.45); cursor: pointer;
}
.map-scrub-range::-moz-range-thumb {
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--accent2); border: 2px solid #fff; cursor: pointer;
}
/* ⚠ .rd-date is `display:block`, so the readout is ALWAYS two lines:
       Helsinki · Mon 14:30
       Aug 4
   That costs the card pill nothing — `.map-card #mapFsScrubber .map-scrub-readout` above
   already spans grid rows 1/4 and centres, written precisely so a two-line readout fits the
   existing pill height instead of growing it.
   Smaller and dimmer than the line above it because it is secondary: at rest you mostly want
   the time, and the date matters when you scrub past midnight. Shown ALWAYS rather than only
   while frozen — a date that appears and disappears would jump the pill height, and on a city
   page far from the visitor the map city's local date is genuinely different information. */
/* ⚠ .rd-date is a small OUTLINED CHIP, not plain text, and that is a discoverability fix rather
   than decoration. As dim 10px text it read as a label — nothing said the date could be changed,
   and the only hints were a hover colour and a title tooltip, neither of which anyone discovers.
   It now borrows the exact visual language of `● Live` sitting inches away in the same pill:
   1px border, fully rounded, padding, same hover treatment. That button is instantly readable as
   a button, so matching it is the most reliable signal available — and the convention is already
   established on this very control.
   ⚠ `display:flex` + `width:fit-content`: flex is block-level so the chip still takes its own
   second line (as the old `display:block` did), while fit-content shrink-wraps it so the border
   hugs the text instead of spanning the whole readout. The flex also spaces the ▾ caret.
   ⚠ The padding is not only cosmetic — at 10px the old tap target was poor on mobile. This
   fixes the target size and the affordance in one change. */
/* The date is a BUTTON, styled as plain text plus a ▾ caret. The caret is the always-visible
   "you can change this" signal; the hover colour confirms it once you are on it.

   ⚠⚠ AN OUTLINED PILL WAS TRIED (2026-08-02) AND REJECTED — do not bring it back. Bordering it
   like the ● Live button seemed right in theory, but a border BOX cannot be made to sit
   comfortably under a plain text line: it reads as misaligned with the city/time line above no
   matter how the horizontal offset is tuned (2px and 3px were both tried by eye and both still
   looked indented), and the border+padding cost vertical space in a pill that has none to give.
   The caret alone carries the affordance at a fraction of the visual weight.
   ⚠ Consequently there is NO padding, NO margin and NO border here — the date must sit on the
   readout's left edge exactly like the line above it. That flush alignment is the whole reason
   the pill was dropped, so nothing may be added back that breaks it.
   ⚠ `display:flex` + `width:fit-content` stay: flex is block-level so the date takes its own
   second line and spaces the caret, while fit-content keeps the hover target tight to the text
   instead of spanning the whole readout. */
.rd-date {
  display: flex; width: fit-content; align-items: center; gap: 4px;
  margin: 0; padding: 0;
  font-size: 10px; font-weight: 500; font-family: 'Inter', sans-serif;
  color: var(--dim); letter-spacing: .03em; line-height: 1.25;
  background: none; border: 0;
  cursor: pointer; transition: color .15s;
}
.rd-date:hover { color: var(--accent2); }
.rd-date:hover .rd-date-caret { opacity: 1; }
/* The ▾ lives in the markup, never written from JS (the JS targets .rd-date-txt).
   ⚠ It matches the date's own 10px rather than sitting smaller: since the outlined pill was
   dropped, this caret is the ONLY always-visible signal that the date can be changed, so it has
   to carry that on its own. Still dimmed to .7 (brightening to 1 on hover) so it reads as a hint
   beside the date rather than competing with it.
   ⚠⚠ 12px IS THE CEILING, and it is a height limit rather than a taste one. `line-height:1`
   makes the caret's box exactly its font-size, while the date's box is 10px x 1.25 = 12.5px.
   At 12px the caret still fits inside the line the date already occupies, so it costs NOTHING.
   One pixel more and the caret starts driving the line box, the readout grows, and the ~41px
   scrubber pill grows with it — the exact regression that took three rounds to undo when this
   was an outlined chip. If more presence is ever wanted, raise the OPACITY, not the size. */
.rd-date-caret { font-size: 12px; line-height: 1; opacity: .7; }
/* ⚠ The 1px optical lift that used to be here went with the pill. It existed to centre the ink
   inside a border box; with no border there is nothing to centre against, and lifting the text
   would only push it out of line with the city/time line above. Don't re-add it unless a box
   comes back — which, per the note on .rd-date, it should not. */
/* ⚠⚠ opacity:0 over a REAL box — never display:none or visibility:hidden. showPicker() throws
   InvalidStateError on an input that is "not being rendered", and the picker anchors itself to
   this element's position, so it needs to sit where the date text is. pointer-events:none keeps
   it from swallowing the button's own clicks; a programmatic .click() still works through it. */
.rd-date-input {
  position: absolute; left: 0; bottom: 0; width: 100%; height: 1.25em;
  opacity: 0; pointer-events: none;
  border: 0; padding: 0; margin: 0; background: none;
}
.map-scrub-readout {
  position: relative;   /* anchors .rd-date-input — see its note above */
  flex-shrink: 0; min-width: 150px; padding-right: 4px;
  font-family: 'Inter', sans-serif; font-size: 12px; font-weight: 500;
  color: var(--text); letter-spacing: .02em; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* Grid placement — row 1: −12h·live·+12h marks over the slider · row 2: ● Live | slider | readout
   · row 3: "Meeting Time Planner". Marks + caption span only the slider column (2). */
.map-scrub-marks   { grid-column: 2; grid-row: 1; }
.map-scrub-live    { grid-column: 1; grid-row: 2; }
.map-scrub-range   { grid-column: 2; grid-row: 2; }
.map-scrub-readout { grid-column: 3; grid-row: 2; }
.map-scrub-caption {
  grid-column: 2; grid-row: 3; justify-self: center; text-align: center;
  font-family: 'Inter', sans-serif; font-size: 10px; font-weight: 600;
  letter-spacing: .05em; color: var(--mid); white-space: nowrap; line-height: 1;
  pointer-events: none;
}
/* −12h · live · +12h marks sitting on top of the slider (span the slider's width) */
.map-scrub-marks {
  display: flex; justify-content: space-between; align-items: center; width: 100%;
  font-family: 'Inter', sans-serif; font-size: 9px; font-weight: 500;
  letter-spacing: .03em; color: var(--dim); line-height: 1; pointer-events: none;
}
.map-scrub-marks span { white-space: nowrap; }
@media (max-width: 699px) {
  #mapFsScrubber { column-gap: 8px; padding: 6px 10px 6px 10px; bottom: 14px; }
  .map-scrub-readout { font-size: 11px; }   /* slider now flexes (1fr), so the readout keeps its width and stays on-screen */
  .map-scrub-caption { font-size: 9px; letter-spacing: .04em; }
  .map-scrub-marks { font-size: 8px; }
}
/* Narrow widths — stack the readout (city over day·time) so it stays on-screen. On the card the readout
   spans its column (above), so stacking does NOT grow the pill. Bumped 420→460px to stay comfortable longer. */
@media (max-width: 460px) {
  #mapScrubReadout { min-width: 0; text-align: left; line-height: 1.22; }   /* city + day/time share the same LEFT edge (ragged right is fine) */
  #mapScrubReadout .rd-sep { display: none; }
  #mapScrubReadout .rd-city, #mapScrubReadout .rd-when { display: block; }
  #mapScrubReadout .rd-city { max-width: 42vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* ⚠ Here the readout is THREE lines (city / day+time / date chip), not two — .rd-city and
     .rd-when stack at this width as well. The chip is pulled in tighter so the extra line costs
     as little as possible against the ~41px card pill: smaller type, and vertical padding down
     to 0 since the border already carries the affordance.
     ⚠ If the pill visibly grows at this width, these are the numbers to reach for FIRST — the
     font-size, then the padding — not the pill's own padding, and not the border, which is the
     whole point of the chip. */
  #mapScrubReadout .rd-date { font-size: 9px; line-height: 1.15; }
  /* ⚠ Date text is 9px x 1.15 = 10.35px here, so 10px is the ceiling — and it is a tight one,
     only 0.35px of slack. If the pill grows at this width, this is the first number to drop
     (9px is comfortably safe); do not touch the pill's own padding. */
  #mapScrubReadout .rd-date-caret { font-size: 10px; }
}

/* Open-clock chips in the fullscreen map header.
   Every open popup gets a chip. Star = deliberately saved to localStorage.
   ⚠ THIS BLOCK USED TO REDECLARE #mapFsChips as a flex box and re-order the whole fullscreen
   header. All of it is gone as of 2026-08-01. Both contexts are now the same float layout,
   declared once up in the TOOLBAR block — there is no fullscreen-specific arrangement left to
   express, and #mapControls / #mapFsChips are styled there, unscoped, on purpose.
   ⚠ Do not reintroduce a `#mapFsChips { display:flex }` here. It would sit LATER in the file
   than the `display:inline` rule at equal specificity, win on source order, and silently
   restore the indented chip block in fullscreen only. That asymmetry is exactly the class of
   bug this rebuild removed. */
/* Narrow screens — just tighter padding. The old `flex-wrap:wrap` + `order:10` +
   `flex-basis:100%` rules that forced the chip list onto its own row are unnecessary now:
   floats drop and inline content reflows on their own, at whatever width actually runs out
   of room, with no threshold to keep in sync. */
@media (max-width: 699px) {
  #mapFsHeader { padding: 8px 16px; }
}
/* ⚠ margin replaces the `gap:6px` that #mapFsChips lost when it became `display:inline` —
   an inline box has no gap. The bottom margin is what separates chip ROWS once they wrap.
   vertical-align:middle centres each chip against the "Map" title sharing its line box;
   without it the chips hang off the text baseline and sit visibly low. */
.map-chip {
  display: inline-flex; align-items: center; gap: 3px;
  margin: 0 6px 5px 0; vertical-align: middle;
  background: rgba(255,255,255,.06); border: 1px solid var(--border);
  border-radius: 20px; padding: 5px 8px 5px 12px;
  font-size: 11px; font-family: 'Inter', sans-serif; font-weight: 500;
  color: var(--mid); white-space: nowrap;
  transition: background .15s, border-color .15s, color .15s;
}
.map-chip:hover { background: rgba(255,255,255,.10); color: var(--text); }
/* Saved chip gets an accent border so it stands out from unsaved ones */
.map-chip.map-chip-saved {
  border-color: rgba(129,140,248,.40); color: var(--accent2);
}
.map-chip-label { cursor: pointer; margin-right: 4px; transition: color .15s; }
.map-chip-label:hover { color: var(--accent); }
.map-chip-save {
  background: none; border: none; padding: 8px 4px; margin: -4px 0;
  font-size: 13px; line-height: 1; cursor: pointer;
  color: rgba(165,180,252,.30);
  transition: color .15s, transform .15s;
}
.map-chip-save:hover             { color: var(--accent2); transform: scale(1.2); }
.map-chip-saved .map-chip-save   { color: var(--accent2); }
.map-chip-x {
  background: none; border: none; padding: 8px 4px; margin: -4px 0;
  color: rgba(241,240,249,.55); font-size: 13px; line-height: 1;
  cursor: pointer;
  transition: color .15s, transform .15s;
}
.map-chip-x:hover { color: var(--text); transform: scale(1.2); }
[data-theme="light"] .map-chip-x { color: rgba(24,24,39,.35); }
[data-theme="light"] .map-chip-x:hover { color: rgba(24,24,39,.65); }
.map-chip-open {
  background: none; border: none; padding: 8px 4px; margin: -4px 0;
  color: rgba(165,180,252,.45); font-size: 10px; line-height: 1;
  cursor: pointer;
  transition: color .15s, transform .15s;
}
.map-chip-open:hover { color: var(--accent2); transform: scale(1.2); }
/* Dim chip: city is saved but popup is currently closed.
   No × button — label click flies there and reopens the clock. */
.map-chip.map-chip-dim {
  background: transparent;
  border-style: dashed; border-color: rgba(129,140,248,.22);
  color: var(--dim);
}
.map-chip.map-chip-dim .map-chip-label { color: var(--dim); }
.map-chip.map-chip-dim .map-chip-save  { color: rgba(129,140,248,.45); }
.map-chip.map-chip-dim:hover { border-color: rgba(129,140,248,.45); color: var(--mid); }

#mapFsBody { flex: 1; position: relative; }
#mapFsBody #cityMap {
  position: absolute !important;
  inset: 0; height: 100% !important; width: 100% !important;
}

/* Leaflet popup — dark theme to match site */
.leaflet-popup-content-wrapper {
  background: rgba(12,12,26,.93) !important;
  border: 1px solid rgba(255,255,255,.12) !important;
  border-radius: 12px !important;
  box-shadow: 0 4px 24px rgba(0,0,0,.55) !important;
  color: var(--text) !important;
}
.leaflet-popup-tip { background: rgba(12,12,26,.93) !important; }
.leaflet-popup-content { margin: 10px 14px !important; }
[data-theme="light"] .leaflet-popup-content-wrapper {
  background: rgba(255,255,255,.97) !important;
  border-color: rgba(0,0,0,.10) !important;
}
[data-theme="light"] .leaflet-popup-tip { background: rgba(255,255,255,.97) !important; }
/* Popup close button — themed × */
.leaflet-popup-close-button {
  top: 5px !important; right: 8px !important;
  color: rgba(241,240,249,.35) !important;
  font-size: 18px !important; font-weight: 300 !important; line-height: 1 !important;
  width: 20px !important; height: 20px !important;
  display: flex !important; align-items: center !important; justify-content: center !important;
  background: none !important; padding: 0 !important;
  transition: color .15s !important;
}
.leaflet-popup-close-button:hover { color: rgba(241,240,249,.85) !important; background: none !important; }
[data-theme="light"] .leaflet-popup-close-button { color: rgba(30,30,60,.35) !important; }
[data-theme="light"] .leaflet-popup-close-button:hover { color: rgba(30,30,60,.75) !important; }

/* ⚠ THE GEOCODER BLOCK THAT SAT HERE IS GONE (2026-08-02) — ~40 lines restyling
   `.leaflet-control-geocoder`, its form input, and its `-alternatives` list, in both themes,
   almost all with !important to beat the plugin's own CDN stylesheet.
   All of it died with the plugin. The map's search is now `.map-search` (see the CITY SEARCH
   block up in MAP CONTROLS), which is our own markup and needs no !important to style.
   ⚠ Do not resurrect these rules to "fix" the search box — they target class names nothing
   on the page emits any more. */

/* ══ SHARED CONTROL ROW ══
   #mapControls holds the button float (.map-tb-right) + the city chips. It is ONE instance
   that lives on the card and is BORROWED by the fullscreen header — js2/city-map-card.js
   relocates it on open and restores it on close, exactly as it already does for
   #mapFsScrubber.
   ⚠ Do not "simplify" by duplicating the controls into two sets — the whole point of one
   relocating instance is that _renderHeaderChips() (which re-reads #mapFsChips by id on
   every call) and the info panel (which positions itself from its button's bounding rect)
   need no sync code and cannot drift.

   ⚠⚠ THERE IS DELIBERATELY NO #mapControls RULE HERE, and adding one back is a trap.
   Until 2026-08-01 this section carried a `#mapControls { display:flex; flex-wrap:wrap;
   align-items:center; gap:8px }` base, described as a harmless fallback. It was harmless only
   because the real rules were `.map-tb-main #mapControls` and `#mapFsHeader #mapControls`,
   both (1,1,0) or higher, which outranked it. The float rebuild replaced those with a single
   UNSCOPED `#mapControls { display:contents }` near the top of the file — same specificity
   (1,0,0) as that base, and EARLIER in source order, so the flex base would have won and
   silently reinstated a wrapper box around the float. Everything this element needs is
   declared once, up in the TOOLBAR block. Nothing belongs here.
   Same reasoning retires the old card/fullscreen split notes that used to follow: there is no
   card-specific or fullscreen-specific arrangement left to state. */
/* When no city is open or saved the list must not leave an empty gap; the button float still
   shows. ⚠ `:empty` adds (0,1,0) on top of the id, so it beats the plain `#mapFsChips`
   rule without needing !important. */
#mapFsChips:empty { display: none; }

/* ══ CARD SIZE LADDER ══
   The map card grows with the viewport, matching .ov-card in
   css2/calltime-planner-card.css step for step (700 / 950 / 1060 at the same 1100px and
   1400px breakpoints), so the page's two big cards stay the same width on any screen.

   ⚠ HEIGHT MUST SCALE WITH WIDTH — keep this ~0.515 ratio if you change either number.
   flyToWorld() in js2/city-map-card.js takes its zoom from WIDTH alone:
   `Math.log2(map.getSize().x / 256)`, the zoom at which 360° of longitude exactly fills
   the card. So width decides how large the globe is drawn and height only CROPS latitude
   off the top and bottom. Widening WITHOUT heightening therefore shows LESS of the world,
   not more: at 1060x360 the world view would drop from 70°N–64°S to roughly 57°N–47°S,
   losing Scandinavia, most of Canada, southern Chile and New Zealand. At the paired values
   below the world view is identical at all three sizes — just bigger. If more globe is ever
   wanted, the ratio to aim at is ~0.65 x width, which reaches about ±75°.

   ⚠ This block sits at the END of the file deliberately. Both rules tie on specificity with
   the base `.map-card` (0,1,0) and `#cityMap` (1,0,0) declarations near the top, so they win
   only by source order — move this block above them and it silently stops working. Media
   queries add no specificity. Fullscreen is unaffected either way: `#mapFsBody #cityMap` is
   (2,0,0) and uses `height:100% !important`. */
/* Narrow screens — reclaim the outer gutter for the card's contents.
   `.map-section` was the only card section with NO mobile override: 28px each side at every
   width, i.e. 56px of a 360px screen spent on padding. That was survivable when the card
   header held two buttons; with the control row added it is not. 16px matches what
   `.world-section` and `.conv-section` already drop to in city-template.php, and the 480px
   breakpoint is the one `.conv-section` uses. */
/* 755px is derived, not arbitrary: `.map-card` is max-width:700px inside `.map-section`'s
   28px gutters, so the card stops reaching full width at 700 + 56 = 756px. From there down,
   every gutter pixel is taken straight out of the map. Dropping to 8px lets the card hold
   its full 700px until a 716px viewport (700 + 16) and shrink more slowly below that.
   ⚠ 8px is deliberately tighter than the 16px that `.overlap-section` and `.conv-section`
   use — this card's content IS a map, which wants the width, and unlike `.ov-card` the map
   card has no second layer of inner card padding to give back. */
@media (max-width: 755px) {
  .map-section { padding: 0 8px 28px; }
}
/* The toolbar/gutter tightening that used to live here as `@media (max-width:480px)` moved UP
   into the file's one remaining `@container (max-width:470px)` block — it is about how much
   room the CARD has, not the window. (There were two container queries until 2026-08-01; the
   560px one went with the grid, since floats need no declared wrap point.) Only the
   `.map-section` gutter above is still viewport-driven, because that padding is page-level. */

@media (min-width: 1100px) {
  .map-card { max-width: 950px; }
  #cityMap  { height: 490px; }
}
@media (min-width: 1400px) {
  .map-card { max-width: 1060px; }
  #cityMap  { height: 545px; }
}
