/* =========================================================================
   mapexplorer.css — the map page's EXPLORER shell.

   Loaded ONLY by an owner who typed `web.map.shell = "explorer"`, and loaded
   LAST, after the site theme, on the studio lab's precedent: this is not a
   re-voicing of the map page, it is a complete ported layout, and it re-homes
   chrome the site theme has its own rules for. The owner's overlay still goes
   after it, because that law has no exceptions.

   ★★★★★ NO RAW COLOUR, ANYWHERE. Every colour in here is a theme.css role
   token, so the shell wears the OWNER'S palette rather than a second one, and
   the same layout reads as this island on this island and as the next island
   on the next. That is also the fleet gate: run_all.py's TOKEN_SHEETS scan and
   test_house_look refuse a hex or an rgb() in any shipped sheet, because a
   colour literal here is one island's palette shipped to every other island.

   ★ GEOMETRY IS NOT A COLOUR, AND IT IS TYPED HERE ON PURPOSE. The radii, the
   blur and the two panel widths are the shell's own proportions - a 16px glass
   card, a 36px round tool, a 280-ish sidebar - and they are what makes this
   layout the layout the owner asked for. Reading them from the theme's radius
   scale would flatten the design on a square-cornered site and balloon it on a
   round one, which is a different page each time rather than one shell. They
   are declared once, at the top, as `--mx-*` custom properties, so an owner
   overlay retunes the whole shell from one block.

   WHAT THIS SHEET DOES NOT DO. It draws no marker, sizes no tile and owns no
   feed. map.css still dresses the Leaflet chrome, the POI labels, the legend
   rows, the coordinate readout, the relief stage and the missing-basemap
   panel; this sheet arranges them. Anything below that touches a map.css class
   does so to re-seat it inside the shell, never to redefine what it is.
   ========================================================================= */

/* -------------------------------------------------------------------------
   0. THE SHELL'S OWN SCALE
   ---------------------------------------------------------------------- */
.map-explorer {
  /* The float inset the topbar, the sidebar and the sheet all share. One
     number: two of them drifting apart is what makes a floating layout look
     assembled rather than designed. */
  --mx-gap: 14px;
  /* A glass card. */
  --mx-r-panel: 16px;
  /* A control inside one. */
  --mx-r-ctl: 8px;
  --mx-r-row: 7px;
  /* A switch row and a result row: the smallest corner in the shell. Typed
     here with its siblings rather than in the rule, so the whole scale is one
     block to retune and `test_house_look` sees a token rather than a
     number somebody picked in a panel. */
  --mx-r-sw: 6px;
  /* A round tool button. Not 50%: the wide `Site` button shares the class and
     a percentage radius on a non-square box draws an ellipse. */
  --mx-r-tool: 999px;
  --mx-blur: 20px;
  /* 42px mark + 2 x 11px padding + 2 x 1px border. Typed rather than measured
     because the sidebar has to start BELOW the bar before either has been
     laid out, and a JS measurement would be one frame of overlap on every
     load. The bar's own height is pinned by the mark, so the two cannot
     drift apart silently. */
  --mx-bar-h: 66px;
  --mx-side-w: 288px;
}

/* THE MAP OWNS THE VIEWPORT — but only once the nav is actually out of the
   flow. js/mapexplorer.js sets `mx-nav-docked` on the body AFTER it has moved
   the real <header class="nav"> into the topbar's Site panel; until then (and
   for ever, if the nav is not there to move) map.css's own height stands and
   the shell sits under the sticky bar, which is a correct page rather than a
   broken one. The live bar's height is still subtracted, because that bar is
   fixed to the bottom of the VIEWPORT and would otherwise print over the
   island - the same arithmetic, and the same reason, as map.css's. */
body.mx-nav-docked .map-shell {
  height: calc(100vh - var(--livebar-h, 0px));
  height: calc(100dvh - var(--livebar-h, 0px));
}

/* -------------------------------------------------------------------------
   1. THE TOPBAR
   ---------------------------------------------------------------------- */
.map-explorer .mx-bar {
  position: absolute;
  top: var(--mx-gap); left: var(--mx-gap); right: var(--mx-gap);
  /* One above the sidebar: they do not overlap, but the Site panel drops out
     of this bar and across the sidebar's top corner. */
  z-index: calc(var(--z-map-panel) + 1);
  /* ★ THE BAR WRAPS RATHER THAN OVERLAPPING ITSELF. Brand, nav dock and
     tools are three flex children with real minimum widths, and an owner
     with fifteen nav links overflows all three past the bar - measured on
     New Age Dinos 2026-09-04, where the location box was printed ON TOP of
     the nav's second row. Wrapping drops the tools to their own line, which
     is legible; overlapping never is. `--mx-bar-h` is then re-measured from
     the real box by mapexplorer.js (`sizeBar`), so the sidebar and the
     missing-basemap panel sit below whatever height the bar actually took. */
  display: flex; align-items: center; flex-wrap: wrap;
  column-gap: 18px; row-gap: 8px;
  padding: 11px 18px;
  border: 1px solid var(--border-2);
  border-radius: var(--mx-r-panel);
  background: var(--scrim-panel);
  backdrop-filter: blur(var(--mx-blur)) saturate(1.15);
  -webkit-backdrop-filter: blur(var(--mx-blur)) saturate(1.15);
  box-shadow: var(--shadow-panel);
}

/* ★ THE OWNER'S NAME HAS A FLOOR, AND THE NAV YIELDS BEFORE IT DOES.
   `min-width: 0` alone let a three-word owner be squeezed to an ellipsis by a
   nav dock with eleven links in it - measured on Paths of Pangea 2026-09-04,
   where the map page's own topbar read "Paths o…". A brand that cannot say
   the owner's name is the one element on this bar with no fallback: the tab
   names, the tools and the layer groups all still work when they shrink.
   `min-width` is a floor, not a width - it is `min()`ed against 100% so the
   narrow-phone rule below (where the brand takes the whole row) is unchanged,
   and it sits BELOW the picture + gap + a fifteen-character name, so a short
   owner name is laid out exactly as it was. */
.map-explorer .mx-brand {
  display: flex; align-items: center; gap: 12px;
  flex: 0 1 auto; min-width: min(100%, 15rem); max-width: 46%;
  color: var(--fg); text-decoration: none;
}
.map-explorer .mx-brand-mark {
  width: 42px; height: 42px; flex: none;
  border-radius: var(--mx-r-ctl);
  /* contain, never cover: an owner's mark is a crest with its own frame and
     a square crop cuts it. Same rule the nav lockup follows. */
  object-fit: contain;
  box-shadow: var(--shadow-panel);
}
.map-explorer .mx-brand-txt { min-width: 0; }
/* THE LOCKUP TAKES THE HOUSE'S INTERIOR STEPS, not a number measured in this
   panel. `--tm-h-item` is the head of an entry and `--tm-h-panel` the head of
   a panel; the hierarchy between name and map is carried by WEIGHT and INK
   rather than by inventing a nineteenth heading size, which is the exact drift
   terminal.css's interior scale exists to end. */
.map-explorer .mx-brand h1 {
  margin: 0;
  font: 700 var(--tm-h-item)/1.15 var(--font-ui); color: var(--fg);
  letter-spacing: .01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.map-explorer .mx-brand-sub {
  margin: 3px 0 0;
  font: 500 var(--tm-h-panel)/1.2 var(--font-ui); color: var(--fg-mute);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.map-explorer .mx-brand:hover h1 { color: var(--accent-hi); }

.map-explorer .mx-tools {
  display: flex; align-items: center; justify-content: flex-end;
  gap: 9px; flex: 1 1 auto; min-width: 0;
}

/* ---- the search box ---- */
/* ★ THE SEARCH BOX HAS A FLOOR TOO, FOR THE SAME REASON THE BRAND DOES.
   The 110px track floor only holds while the BOX is allowed its content
   width; `min-width: 0` on a flex child inside a `min-width: 0` tools row
   let an owner with fifteen nav links crush the whole control - measured on
   New Age Dinos 2026-09-04, where the topbar read "LOCATION W a -1 T" and
   the one control a player types into was about twelve pixels wide. The nav
   dock already wraps to a second row; it is the thing that should yield. */
.map-explorer .mx-search {
  display: grid; grid-template-columns: auto minmax(110px, 300px);
  align-items: center; gap: 9px;
  min-width: min(100%, 17rem); padding: 6px 11px;
  border: 1px solid var(--border); border-radius: var(--mx-r-ctl);
  background: var(--surface-2);
  transition: border-color .15s ease;
}
.map-explorer .mx-search:focus-within { border-color: var(--accent); }
.map-explorer .mx-search-lab {
  font: 600 var(--tm-h-micro)/1 var(--font-ui); letter-spacing: .11em;
  text-transform: uppercase; color: var(--fg-mute);
}
.map-explorer .mx-search input {
  min-width: 0; width: 100%; height: 24px; margin: 0; padding: 0;
  border: 0; background: transparent; color: var(--fg);
  font: 500 13.4px/1 var(--font-ui);
  -webkit-appearance: none; appearance: none;
}
.map-explorer .mx-search input:focus { outline: none; }
.map-explorer .mx-search input::placeholder { color: var(--fg-mute); }
/* The browser's own clear affordance is drawn in its chrome colour, which is
   the one colour on this bar nobody chose. The shell has its own bin. */
.map-explorer .mx-search input::-webkit-search-cancel-button { display: none; }

/* ---- the round tools ---- */
.map-explorer .mx-ico {
  display: inline-grid; place-items: center;
  width: 36px; height: 36px; flex: 0 0 36px; padding: 0;
  color: var(--fg-dim);
  border: 1px solid var(--border); border-radius: var(--mx-r-tool);
  background: var(--surface-2); cursor: pointer;
  transition: color .15s ease, background .15s ease, border-color .15s ease;
}
.map-explorer .mx-ico:hover {
  color: var(--fg); background: var(--surface-3); border-color: var(--border-2);
}
.map-explorer .mx-ico:focus-visible { outline: none; border-color: var(--accent); }
.map-explorer .mx-ico svg {
  width: 18px; height: 18px;
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}
/* A tool that is currently OFF says so in ink, not only in a tooltip. */
.map-explorer .mx-ico[aria-pressed="false"] { color: var(--fg-mute); }
.map-explorer .mx-ico[aria-pressed="false"]:hover { color: var(--fg-dim); }
/* SAY IT OUT LOUD: `display: inline-grid` out-specifies the browser's own
   [hidden] rule, so without this the bin ships VISIBLE on a page that has
   nothing to clear - the same lesson map.css records for .ai-kinds. */
.map-explorer .mx-ico[hidden] { display: none; }
.map-explorer .mx-ico-wide {
  width: auto; flex: 0 0 auto; padding: 0 12px; gap: 6px;
  grid-auto-flow: column; border-radius: var(--mx-r-ctl);
}
.map-explorer .mx-ico-txt {
  font: 600 var(--tm-h-micro)/1 var(--font-ui); letter-spacing: .09em;
  text-transform: uppercase;
}
.map-explorer .mx-ico[aria-expanded="true"] {
  color: var(--fg); border-color: var(--accent);
}

/* -------------------------------------------------------------------------
   1b. THE NAV DOCK — the site's tab strip, kept visible in the bar
   ---------------------------------------------------------------------- */
/* js/mapexplorer.js moves the REAL `#nav-links` in here on a viewport wider
   than 1180px, and back into the Site panel below that (see the fragment's
   note: the owner read a vanished tab bar as breakage, 2026-09-01). The
   strip sits with the lockup on the left, the way a map application docks
   its site tabs; the tools keep the right. */
.map-explorer .mx-navdock {
  display: flex; align-items: center;
  flex: 0 1 auto; min-width: 0;
}
/* `display: flex` out-specifies the browser's own [hidden] rule — the same
   lesson .mx-ico[hidden] above records. Without this the empty dock ships
   as a visible gap on a shell whose nav never arrived. */
.map-explorer .mx-navdock[hidden] { display: none; }

/* ★ ID SELECTORS, exactly as the Site panel's rules below and for the same
   two reasons: the site theme addresses this element with selectors these
   classes cannot out-rank, and `.nav-links` is a house primitive no page
   sheet may restyle in general — but THE ONE ELEMENT moved into THIS dock
   is this dock's to seat. The active page is a filled accent chip because
   that is the shell's own active-tab language (`.mx-tab.is-active`), and
   `--on-primary` is the ink theme.py already proved readable on it. */
#mx-navdock #nav-links {
  display: flex; align-items: center; gap: 2px;
  position: static; transform: none; opacity: 1; visibility: visible;
  width: auto; max-height: none; margin: 0; padding: 4px;
  border: 1px solid var(--border); border-radius: var(--mx-r-ctl);
  background: var(--surface-2); box-shadow: none;
}
#mx-navdock #nav-links a {
  display: inline-block; padding: 8px 11px;
  border-radius: var(--mx-r-row);
  background: none; box-shadow: none; text-decoration: none;
  font: 600 var(--tm-h-micro)/1 var(--font-ui); letter-spacing: .09em;
  text-transform: uppercase; color: var(--fg-mute); white-space: nowrap;
  transition: color .15s ease, background .15s ease;
}
#mx-navdock #nav-links a:hover,
#mx-navdock #nav-links a:focus-visible {
  color: var(--fg); background: var(--surface-hover); outline: none;
}
#mx-navdock #nav-links a[aria-current="page"] {
  color: var(--on-primary); background: var(--accent);
}
/* A group separator is a vertical hairline in a horizontal strip — the same
   turn the Site panel's rule below makes in the other direction. */
#mx-navdock #nav-links .sep {
  width: 1px; height: 16px; margin: 0 4px;
}

/* -------------------------------------------------------------------------
   2. THE SITE PANEL — where the real nav is docked
   ---------------------------------------------------------------------- */
.map-explorer .mx-sitemenu {
  position: absolute; top: calc(100% + 9px); right: 0;
  width: min(320px, calc(100vw - 2 * var(--mx-gap)));
  max-height: min(70vh, 520px); overflow: auto; scrollbar-width: thin;
  padding: 13px;
  border: 1px solid var(--border-2); border-radius: var(--mx-r-panel);
  /* ★ OPAQUE, WHERE THE BAR AND THE SIDEBAR ARE GLASS - and the difference is
     not a taste. This panel holds LINKS, and a place name drawn on the island
     underneath a link is two lines of text in the same place: the first shot
     of it had "PORT (OCEAN-PORT)" reading straight through the menu, over the
     nav. The register's own rule for a floating overlay says the same thing -
     an overlay that sits over page content keeps an opaque ground and the
     panel lift.

     ★★★ AND `--surface` IS NOT AN OPAQUE GROUND, which is the trap this
     comment exists to name. It reads as one in theme.css, but a SITE THEME may
     re-point it: on this owner it resolves to
     `color-mix(in srgb, #08080d 66%, transparent)`, so a panel painted with it
     is 66% opaque and the island reads straight through. Measured in the
     browser, not guessed - `getComputedStyle` on the live menu is the only
     thing that could have told us. `--bg-deep` is the PAGE's own ground (it is
     what map.css paints behind the tiles) and no theme re-points it, so the
     ground goes on `background-color` and the surface's tone rides over it as
     one flat layer: a theme that tints `--surface` still tints this panel, and
     a theme that thins it can no longer make it see-through. */
  background-color: var(--bg-deep);
  background-image: linear-gradient(0deg, var(--surface), var(--surface));
  backdrop-filter: blur(var(--mx-blur)) saturate(1.15);
  -webkit-backdrop-filter: blur(var(--mx-blur)) saturate(1.15);
  box-shadow: var(--shadow-panel);
}
.map-explorer .mx-sitemenu[hidden] { display: none; }

/* ★ ID SELECTORS, AND THAT IS DELIBERATE. The site theme addresses the nav as
   `html[data-site-theme="x"] .nav ...`, which out-specifies any class pair
   this sheet could write even though this sheet lands after it. An id wins
   that comparison outright, and it is the honest tool here: these rules exist
   to re-seat ONE element that has been moved into ONE container, not to
   restyle navs in general. */
#mx-sitemenu .nav {
  position: static; z-index: auto; min-height: 0;
  background: none; border: 0; box-shadow: none;
  backdrop-filter: none; -webkit-backdrop-filter: none;
}
#mx-sitemenu .wrap {
  width: auto; max-width: none; margin: 0; padding: 0;
}
#mx-sitemenu .nav-in {
  display: flex; flex-direction: column; align-items: stretch;
  gap: 10px; min-height: 0; padding: 0;
}
/* The topbar already carries the lockup, and the burger opens a drawer that
   this panel IS. Two copies of either is two controls that disagree. */
#mx-sitemenu .brand,
#mx-sitemenu .burger { display: none; }
/* ★ `#nav-links`, NOT `.nav-links`. The class is a HOUSE PRIMITIVE - it is on
   terminal.css's shared list and a page sheet that re-describes one is drift
   by definition (test_house_look::NoPageSheetRestylesASharedPrimitive, and it
   is right to refuse it). What this rule is actually addressing is not "nav
   links everywhere", it is THE ONE ELEMENT that has been moved into THIS
   panel, and its id says exactly that. */
#mx-sitemenu #nav-links {
  display: flex; flex-direction: column; align-items: stretch; gap: 2px;
  position: static; transform: none; opacity: 1; visibility: visible;
  width: auto; max-height: none; margin: 0; padding: 0;
  background: none; border: 0; box-shadow: none;
}
#mx-sitemenu #nav-links a {
  display: block; padding: 9px 10px; border-radius: var(--mx-r-row);
}
/* A hairline group separator is a VERTICAL rule in a horizontal bar; in a
   column it has to turn, or it draws a 1px sliver nobody can read as a
   divider. */
#mx-sitemenu #nav-links .sep {
  width: auto; height: 1px; margin: 6px 2px;
}
#mx-sitemenu .nav-util {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin: 0; padding-top: 11px; border-top: 1px solid var(--border);
}
#mx-sitemenu .nav-auth { flex: 1 1 100%; }
#mx-sitemenu .nav-auth-btn { justify-content: center; width: 100%; }

/* -------------------------------------------------------------------------
   3. THE SIDEBAR
   ---------------------------------------------------------------------- */
.map-explorer .mx-side {
  position: absolute;
  top: calc(var(--mx-gap) + var(--mx-bar-h) + 10px);
  left: var(--mx-gap);
  bottom: var(--mx-gap);
  width: var(--mx-side-w);
  max-height: none;
  /* Two rows, and the second one is what scrolls. `minmax(0, 1fr)` rather
     than `1fr`: a grid track's default minimum is its content, so without it
     a long layer list grows the sidebar past the viewport instead of
     scrolling inside it. */
  display: grid; grid-template-rows: auto minmax(0, 1fr);
  border: 1px solid var(--border-2); border-radius: var(--mx-r-panel);
  background: var(--scrim-panel);
  backdrop-filter: blur(12px) saturate(1.2);
  -webkit-backdrop-filter: blur(12px) saturate(1.2);
  box-shadow: var(--shadow-panel);
  overflow: hidden;
  transition: transform .3s cubic-bezier(.16, 1, .3, 1), opacity .22s ease;
}
/* `data-open` is map.js's attribute - the hamburger flips it and this sheet
   decides what the two answers look like. Closed slides OFF the edge rather
   than collapsing, so the island under it is never re-laid-out mid-animation
   and the panel comes back exactly where it left. */
.map-explorer .mx-side[data-open="false"] {
  transform: translateX(calc(-100% - var(--mx-gap) - 10px));
  opacity: 0; pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .map-explorer .mx-side { transition: none; }
}

.map-explorer .mx-tabs {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px;
  padding: 11px; border-bottom: 1px solid var(--border);
}
.map-explorer .mx-tab {
  min-height: 34px; padding: 0 4px;
  color: var(--fg-mute);
  border: 1px solid var(--border); border-radius: var(--mx-r-row);
  background: var(--surface-2);
  font: 600 12.2px/1 var(--font-ui); cursor: pointer;
  /* ★ FOUND BY A SCREENSHOT, NOT BY READING. The site theme upper-cases
     button text, and "LOCATION" in caps does not fit a third of a 288px
     sidebar - the live tab shipped reading "LOCATI...". These three are the
     shell's own words and they are set in the shell's own case, which is also
     the case the design this is a port of uses. */
  text-transform: none; letter-spacing: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: color .15s ease, background .15s ease, border-color .15s ease;
}
.map-explorer .mx-tab:hover {
  color: var(--fg); background: var(--surface-3); border-color: var(--border-2);
}
.map-explorer .mx-tab:focus-visible { outline: none; border-color: var(--accent); }
/* The live tab is a filled chip in the site's accent with the accent's own
   ink on it - `--on-primary` is the colour theme.py already proved readable
   against it, so this cannot become the one unreadable control on the page. */
.map-explorer .mx-tab.is-active {
  color: var(--on-primary); background: var(--accent); border-color: var(--accent);
}

.map-explorer .mx-panes { min-height: 0; }
.map-explorer .mx-pane {
  display: none; min-height: 0; height: 100%;
  overflow-y: auto; scrollbar-width: thin;
  padding: 14px 15px 18px;
}
.map-explorer .mx-pane.is-active { display: block; }
/* LAST, so it wins over `.is-active`: the two are managed by the same script
   and a pane that carries both is a pane mid-switch, which must not flash. */
.map-explorer .mx-pane[hidden] { display: none; }

.map-explorer .mx-head { margin: 0 0 13px; }
.map-explorer .mx-head h2 {
  margin: 0; font: 700 var(--tm-h-item)/1.2 var(--font-ui); color: var(--fg);
}
.map-explorer .mx-note {
  margin: 5px 0 0; font: 500 12.2px/1.5 var(--font-ui);
}

/* ---- the four overlay groups ---- */
.map-explorer .mx-gtitle {
  margin: 17px 0 7px;
  font: 600 var(--tm-h-micro)/1.2 var(--font-ui);
  letter-spacing: .1em; text-transform: uppercase; color: var(--fg-mute);
}
.map-explorer .mx-gtitle:first-of-type { margin-top: 2px; }
/* A heading whose list is empty is a promise the page cannot keep, so the
   script hides it - and a block-level heading would out-specify [hidden]
   through the rule above if this were not here. */
.map-explorer .mx-gtitle[hidden] { display: none; }
.map-explorer .mx-switches { display: grid; gap: 2px; }

/* THE SWITCH ROW, in two flavours that must read as one control: the rows
   js/map.js and js/mapbounty.js BUILD (`.legend-row`, moved in here by
   js/mapexplorer.js) and the rows this shell authors itself (`.mx-switch`).
   One rule for both, because a reader who can tell them apart has found a
   seam that should not exist. */
.map-explorer .mx-switches .legend-row,
.map-explorer .mx-legend .legend-row,
.map-explorer .mx-switch {
  display: flex; align-items: center; gap: 10px;
  min-height: 30px; padding: 4px 7px;
  border-radius: var(--mx-r-sw); cursor: pointer;
  font: 500 13.1px/1.35 var(--font-ui); color: var(--fg-dim);
  transition: background .14s ease, color .14s ease;
}
.map-explorer .mx-switches .legend-row:hover,
.map-explorer .mx-legend .legend-row:hover,
.map-explorer .mx-switch:hover {
  background: var(--surface-hover); color: var(--fg);
}
.map-explorer .mx-switches input[type="checkbox"],
.map-explorer .mx-legend input[type="checkbox"] {
  width: 14px; height: 14px; flex: none; margin: 0;
  accent-color: var(--accent); cursor: pointer;
}
/* 12px rather than map.css's 9px: in a two-column sidebar the dot is the only
   thing carrying WHICH layer a row is, and at 9px three of the seventeen
   category colours stop being tellable apart on a laptop panel.

   ★ SIZE AND COLOUR ARE TWO RULES, AND THAT IS THE WHOLE POINT. A single rule
   that set both took the WANTED LAYER's colour away: map.css dresses
   `.legend-swatch-bounty` in the warn family (a bounty is a threat marker, not
   a brand mark), this sheet loads after it, and a blanket
   `background: var(--c)` on every swatch repainted that dot in the fallback
   grey - the row said "Bounties" in the colour of nothing. Found in a
   screenshot. So the SIZE applies to every swatch, and the CATEGORY COLOUR
   applies only to a swatch that has no dress of its own. */
.map-explorer .mx-switches .legend-swatch,
.map-explorer .mx-legend .legend-swatch,
.map-explorer .mx-swatch {
  width: 12px; height: 12px; flex: none;
  border-radius: var(--r-round);
}
/* `legend-swatch-` with the hyphen: the plain class is exactly
   `legend-swatch`, and every specialised one (bounty, shelter, and whatever a
   later layer adds) carries a second class that starts with it. A new layer
   that dresses its own dot therefore keeps that dress by construction, which
   is the property this selector exists to buy. */
.map-explorer .mx-switches .legend-swatch:not([class*="legend-swatch-"]),
.map-explorer .mx-legend .legend-swatch:not([class*="legend-swatch-"]),
.map-explorer .mx-swatch {
  background: var(--c, var(--fg-mute));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--c, var(--fg-mute)) 20%, transparent);
}
/* THE ZOOM ROW. A label and a track, on the same grid as a switch so the
   View group reads as one list rather than a list with a gadget in it. */
.map-explorer .mx-range {
  display: flex; align-items: center; gap: 10px;
  min-height: 30px; padding: 4px 7px;
  font: 500 13.1px/1.35 var(--font-ui); color: var(--fg-dim);
}
.map-explorer .mx-range .mx-swname { flex: 0 0 46px; }
.map-explorer .mx-range input[type="range"] {
  flex: 1 1 auto; min-width: 0; height: 20px; margin: 0;
  accent-color: var(--accent); background: transparent; cursor: pointer;
}

/* THE READER'S OWN SWITCH FOR PLACE NAMES, and it is a SECOND class on
   purpose. map.js owns `poi-labels-off` and recomputes it on every zoom frame
   from the island's rendered width - "is there room for names". This one is
   "do you want names". Two questions, two classes; one class would mean the
   next zoom frame silently undoing what the reader just chose. */
.leaflet-container.mx-labels-off .poi-label { display: none; }

/* The search mark. Leaflet writes `stroke` and `fill` onto the path as
   presentation attributes from its own defaults; a stylesheet outranks a
   presentation attribute, which is how this stays a token rather than
   becoming the one hard-coded colour on the page. */
.map-explorer .mx-mark {
  stroke: var(--accent-hi);
  fill: var(--accent-hi);
}

/* The shell's OWN switches have no category colour - they are view controls,
   not layers - so they wear the quiet ink rather than borrowing a layer's. */
.map-explorer .mx-swatch-plain {
  background: var(--fg-mute);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--fg-mute) 20%, transparent);
}
.map-explorer .mx-switches .legend-name,
.map-explorer .mx-legend .legend-name,
.map-explorer .mx-swname {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.map-explorer .mx-switches .legend-count,
.map-explorer .mx-legend .legend-count {
  flex: none;
  font: 500 10.6px/1 var(--font-mono); color: var(--fg-mute);
  font-variant-numeric: tabular-nums;
}
/* The relief control, moved into the View group. In the other shell it is a
   section of its own with a heading and a note; here it is one more row, so
   the heading its own script writes is dropped by js/mapexplorer.js and the
   note becomes the group's footnote. */
.map-explorer .mx-switches .tilt-wrap { margin: 4px 7px 2px; }
.map-explorer .mx-switches .panel-note { margin: 2px 7px 0; }

/* ---- the layer scripts' leftovers ----
   `#legend-list` stays in the document and stays visible: the switch ROWS are
   lifted out of it, and what remains is the sentence map.js writes when the
   POI file is missing. A shell that hid this box would turn a stated failure
   back into a silent empty panel. */
.map-explorer .mx-legend { display: grid; gap: 2px; margin: 10px 0 0; }
.map-explorer .mx-legend:empty { display: none; }

/* -------------------------------------------------------------------------
   4. THE LOCATION TAB
   ---------------------------------------------------------------------- */
.map-explorer .mx-find {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 9px;
  margin: 0;
}
.map-explorer .mx-find input {
  min-width: 0; min-height: 36px; padding: 8px 10px;
  color: var(--fg);
  border: 1px solid var(--border); border-radius: var(--mx-r-row);
  background: var(--surface-2);
  font: 500 13.2px/1.2 var(--font-ui);
  -webkit-appearance: none; appearance: none;
}
.map-explorer .mx-find input:focus-visible { outline: none; border-color: var(--accent); }
.map-explorer .mx-find input::placeholder { color: var(--fg-mute); }
.map-explorer .mx-find input::-webkit-search-cancel-button { display: none; }
.map-explorer .mx-go {
  min-height: 36px; padding: 0 15px;
  color: var(--on-primary);
  border: 1px solid var(--accent); border-radius: var(--mx-r-row);
  background: var(--accent);
  font: 600 12.4px/1 var(--font-ui); cursor: pointer;
}
.map-explorer .mx-go:hover { background: var(--accent-hi); border-color: var(--accent-hi); }
.map-explorer .mx-go:focus-visible { outline: none; border-color: var(--fg); }

.map-explorer .mx-results { display: grid; gap: 2px; margin: 9px 0 0; }
.map-explorer .mx-hit {
  display: flex; align-items: baseline; gap: 9px; width: 100%;
  padding: 7px 8px; text-align: left;
  border: 1px solid transparent; border-radius: var(--mx-r-sw);
  background: transparent; color: var(--fg-dim); cursor: pointer;
  font: 500 13px/1.3 var(--font-ui);
}
.map-explorer .mx-hit:hover { background: var(--surface-hover); color: var(--fg); }
.map-explorer .mx-hit:focus-visible { outline: none; border-color: var(--accent); }
.map-explorer .mx-hit-nm {
  flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.map-explorer .mx-hit-kind {
  flex: none;
  font: 500 var(--tm-h-micro)/1 var(--font-ui); letter-spacing: .08em;
  text-transform: uppercase; color: var(--fg-mute);
}

/* The uppercase rule that separates a block inside a tab. Also worn by
   `#ai-label`, which map.js unhides when the ambient feed answers. */
.map-explorer .mx-lab {
  margin: 19px 0 8px; padding-top: 15px;
  border-top: 1px solid var(--border);
  font: 600 var(--tm-h-micro)/1 var(--font-ui);
  letter-spacing: .12em; text-transform: uppercase; color: var(--fg-mute);
}
.map-explorer .mx-lab[hidden] { display: none; }
.map-explorer .coord { border-radius: var(--mx-r-row); }

/* -------------------------------------------------------------------------
   5. THE LIVE TAB — everything in it is built by another script
   ---------------------------------------------------------------------- */
.map-explorer #map-share { margin: 10px 0 0; }
.map-explorer .map-follow { border-radius: var(--mx-r-row); }
.map-explorer .storm-card { margin: 12px 0 0; }

/* -------------------------------------------------------------------------
   6. WHAT THE SHELL DOES TO map.css's OWN OVERLAYS
   ---------------------------------------------------------------------- */
/* The honest missing-basemap panel is centred in the WHOLE viewport, which in
   this layout puts it half under the topbar and half under the sidebar. The
   message is the one thing on the page that has to be readable when the map
   is not there, so it is centred in the space the shell actually leaves. */
.map-explorer .map-missing {
  padding-top: calc(var(--mx-gap) + var(--mx-bar-h) + 34px);
  padding-left: calc(var(--mx-gap) + var(--mx-side-w) + 26px);
}
/* The sidebar reaches the bottom-left corner, so the credit and the zoom
   control keep the right-hand side to themselves - which is where map.css
   already puts them. Nothing to move; the note is here so the next reader
   does not go looking for the rule that must be missing. */

/* -------------------------------------------------------------------------
   7. PHONE — the sidebar becomes a bottom sheet
   ---------------------------------------------------------------------- */
@media (max-width: 820px) {
  /* The whole scale retunes from one block: a phone gets a tighter inset,
     a shorter bar and a slightly tighter card corner, and every rule that
     already reads these tokens follows without being restated. */
  .map-explorer { --mx-gap: 10px; --mx-bar-h: 58px; --mx-r-panel: 14px; }

  .map-explorer .mx-bar { flex-wrap: wrap; gap: 10px; padding: 9px 12px; }
  .map-explorer .mx-brand { max-width: none; flex: 1 1 auto; }
  .map-explorer .mx-brand-mark { width: 34px; height: 34px; }
  .map-explorer .mx-tools {
    flex: 1 1 100%; flex-wrap: wrap; justify-content: flex-start;
  }
  /* Full width and FIRST on the row: on a phone the search box is the reason
     most people open a map, and a 110px box wedged between five round
     buttons is a control nobody types into. */
  .map-explorer .mx-search {
    order: -1; flex: 1 1 100%;
    grid-template-columns: auto minmax(0, 1fr);
  }
  .map-explorer .mx-sitemenu {
    width: auto; left: 0; right: 0;
    max-height: min(64vh, 460px);
  }

  .map-explorer .mx-side {
    top: auto; left: 0; right: 0; bottom: 0; width: auto;
    /* Shorter than the reference's, because this topbar wraps to three rows
       on a 390px phone and a 62% sheet left a 130px sliver of island between
       the two. */
    height: min(58dvh, 470px);
    border-radius: var(--mx-r-panel) var(--mx-r-panel) 0 0;
    border-bottom: 0;
    transform: translateY(0);
  }
  /* Redefined inside the query, and it has to be: the desktop rule slides the
     panel out to the LEFT, which on a bottom sheet is a panel that leaves
     sideways and comes back sideways. */
  .map-explorer .mx-side[data-open="false"] {
    transform: translateY(calc(100% + 8px));
    opacity: 0; pointer-events: none;
  }
  .map-explorer .mx-tabs { padding: 10px; }

  /* The sheet covers the bottom-right corner, and the two overlays that live
     there would be underneath it rather than beside it. They step aside while
     the sheet is up and come straight back - one rule, on the shell, rather
     than a class the script has to remember to take off again. */
  .map-explorer .mx-shell:has(.mx-side[data-open="true"]) .leaflet-control-zoom,
  .map-explorer .mx-shell:has(.mx-side[data-open="true"]) .map-credit {
    opacity: 0; pointer-events: none;
  }
  /* THE CREDIT WAS UNDER THE ZOOM BUTTONS. map.css floats both at the
     bottom right, which is room enough on a desktop and is not on a phone
     where the credit wraps to two lines - the shot showed the minus key
     printed over the second one. The credit takes the left corner here; the
     zoom keys keep the right, which is the thumb's side. */
  .map-explorer .map-credit {
    left: var(--mx-gap); right: auto; text-align: left;
    max-width: calc(100% - 66px);
  }
  .map-explorer .map-missing {
    padding-left: 24px; padding-right: 24px;
    padding-top: calc(var(--mx-gap) + var(--mx-bar-h) + 60px);
  }
}

/* A finger needs a bigger target than a mouse does - the same trade map.css
   already makes for the AI rows. */
@media (pointer: coarse) {
  .map-explorer .mx-switches .legend-row,
  .map-explorer .mx-legend .legend-row,
  .map-explorer .mx-switch { min-height: 38px; padding: 8px 7px; }
  .map-explorer .mx-hit { padding: 10px 8px; }
}
