*{box-sizing:border-box;margin:0;padding:0}
:root{
  /* Aligned to the marketing site's brand system (type3-site-scaffold/brand.html):
     the page itself stays true black (--ink), matching the landing page. --grid
     is the one exception — the map card's own background — done as a deep
     cobalt near-black instead of neutral grey so the map surface reads with a
     cool blue cast against the black page around it. Lifted from #030407 to
     #070B14 so that cast is actually visible under the brighter marker and
     isoline palette, which had been sitting on what was effectively black. */
  --ink:#000000;--paper:#ffffff;--grid:#070B14;--rail:#141414;
  /* The map's own ground, canvas and card alike (see mapGround(), which must
     match). Distinct from --grid, which fills chrome sitting ON the map —
     the search dropdown, the toast — and stays darker so it reads against it.
     The phone lifts this to #151823 in the max-width:900px block; a small
     bright screen needs more than the card does inside a black page. */
  --map-ground:#0A0C14;
  --muted:rgba(255,255,255,0.25);--wire:rgba(255,255,255,0.5);
  /* Same ~225° hue as --grid (the map's cobalt background), lightened way
     up — used as the RGB base for every translucent button/container fill
     that used to be plain white (255,255,255). Each site keeps its own
     alpha; only the hue changes, so e.g. rgba(var(--fill-tint),0.045) reads
     the same "how strong" as the old rgba(255,255,255,0.045) did, just
     blue-cast instead of neutral. */
  --fill-tint:200,215,255;
  --signal:#3AD375;--signal-dk:#27A558;--gold:#FFA514;--alert:#ff4d2e;
  --mono:'Space Grotesk',sans-serif;--sans:'Inter',sans-serif;--display:'Space Grotesk',sans-serif;
}
html,body{height:100%;overflow:hidden}
body{background:var(--ink);color:var(--paper);font-family:var(--sans);font-size:13px;display:flex;flex-direction:column}

.live-dot{width:5px;height:5px;border-radius:50%;background:var(--signal);animation:pulse 1.4s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}

/* ── Landing splash — plain black, briefly, then the full wordmark
   collapses to just the gold "3", then the whole thing fades out to reveal
   the map already sitting underneath. Sequenced in initSplash()
   (assets/map-app.js), not CSS animations, since it's three chained steps
   with different durations.
   Geometry is a 1:1 copy of the rail's own #nav-logo-wrap — same 115x13
   box, same 25px collapsed width, same -90px slide, pinned at that
   element's exact page coordinates (rail 8px offset + its own 7.5px left;
   16px rail padding + half of the 40px logo box, less half the 13px
   height). Same size, same place, so the fade-out is a straight handoff to
   the real logo rather than a resize or a jump-cut. ── */
.splash{position:fixed;inset:0;z-index:9999;background:var(--ink);transition:opacity .5s ease}
.splash.hide{opacity:0;pointer-events:none}
#splash-logo-wrap{position:absolute;left:15.5px;top:29.5px;width:115px;height:13px;overflow:hidden;transition:width .55s cubic-bezier(0.76,0,0.24,1)}
#splash-logo-wrap.collapsed{width:25px}
#splash-logo-wrap svg{display:block;transform:translateX(0);transition:transform .55s cubic-bezier(0.76,0,0.24,1)}
#splash-logo-wrap.collapsed svg{transform:translateX(-90px)}
#splash-logo-wrap svg path[fill="white"]{transition:opacity .4s cubic-bezier(0.76,0,0.24,1)}
#splash-logo-wrap.collapsed svg path[fill="white"]{opacity:0}
#splash-loading{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:flex;font-family:var(--mono);font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--signal);transition:opacity .3s ease;padding:13px 17px}
/* Corner strokes bracketing the load-in element. Absolutely positioned, so
   they sit outside the flex flow and the padding above is what sets their
   distance from the type — change the padding, not these, to loosen the
   frame. currentColor, so they stay locked to the label's signal green.
   The element centres with a transform, so the padding grows its box evenly
   and the text stays on the middle of the screen. */
.sl-corner{position:absolute;width:9px;height:9px;border:1px solid currentColor;opacity:.45;pointer-events:none}
.sl-corner.tl{top:0;left:0;border-right-width:0;border-bottom-width:0;border-top-left-radius:3px}
.sl-corner.tr{top:0;right:0;border-left-width:0;border-bottom-width:0;border-top-right-radius:3px}
.sl-corner.bl{bottom:0;left:0;border-right-width:0;border-top-width:0;border-bottom-left-radius:3px}
.sl-corner.br{bottom:0;right:0;border-left-width:0;border-top-width:0;border-bottom-right-radius:3px}
#splash-loading-dots{display:inline-block;min-width:1.4em;text-align:left}
/* Mirrors the dots’ reserved width on the other side of the label, so the
   type sits on the frame’s centre line instead of 0.7em left of it. Tied to
   the same 1.4em — change both together or the centring drifts. */
.sl-gutter{display:inline-block;min-width:1.4em}

/* ── Main layout: the rail is a "navbar" floating directly on the black
   page background (--ink) — it is NOT part of the map card. The map itself
   is a separate rounded container inset from the window edges, sitting to
   the right of the rail. Panels ("windows": Grid layers on the left, the
   Facility sidecar on the right) flow out flush against that container's
   own edges and share --container-radius on the corners that coincide with
   it, so the rounding reads as one continuous shape ("nesting corners
   match") instead of a small radius floating inside a bigger one. ── */
.main{flex:1;position:relative;overflow:hidden;background:var(--ink)}

/* --rail-map-gap is 0 on purpose: the rail (56px) already centers its 40px
   icons with 8px of padding on each side, so butting the map card flush
   against the rail's own right edge reproduces that same 8px gap on the
   icon's right as it has on its left — visually symmetric. Any positive
   value here would double-count that padding on the right side only. */
:root{
  --rail-w:56px; --rail-gap:12px; --rail-map-gap:0px;
  --container-radius:10px; --container-inset:12px; /* 25% less than the original 16px */
  /* Shared gap between the map card's own edge and the floating chrome
     docked in its corners (search bar, LMP legend, zoom controls) — kept as
     one value so all three corners match each other. */
  --chrome-inset:8px;
  --panel-w:340px; --dock-gap:20px;
  /* Left-docked panels (Grid layers/Sign-in) get their own gap to the map
     card, separate from right-docked ones (Facility/Sponsor, still
     --dock-gap). These panels have no visible background of their own, so
     what actually reads is content-edge → map card, and that's matched to
     the gap the rail's own buttons keep from that same content (button's
     right edge to the content's left edge, --rail-content-gap below). The
     content already holds --chrome-inset of that as its own right padding,
     so this structural remainder is just the rest of it. 3× the rail's own
     content gap, which lands 30px of clear space between the content's right
     edge and the map card.
     Both left panels share this. Grid layers used to hold it alone (via a
     .main.layers-open override) on the grounds that Sign-in was "a single
     button" and needed less room — but Sign-in is a full form now, with an
     input and a submit that run to the same right edge as the layers column's
     counts, and at the narrow value it read cramped against the map beside
     the layers panel's breathing room. */
  --rail-content-gap:calc(var(--rail-glyph-x) + var(--rail-glyph) + 20px - var(--rail-icon-x) - var(--rail-icon)); /* 10px */
  --dock-gap-l:calc(var(--rail-content-gap) * 3 - var(--chrome-inset));
  /* Rail icon geometry, so the left panel can line its content up with the
     icon column instead of guessing: the 40px button box is centred in the
     56px rail, and the 20px glyph is centred in that box. Panels align to
     the GLYPH edge (18px), not the button box (8px) — the box is just a
     hover chip, the glyph is the visible left line. */
  --rail-pad:16px; --rail-icon:40px; --rail-glyph:20px;
  /* The wordmark/"3" glyph's own height (see #nav-logo-wrap). The logo has
     no container fill, so this — not its 40px .rail-logo box — is what
     reads as the logo's edge when spacing anything against it. */
  --rail-logo-h:13px;
  --rail-icon-x:calc((var(--rail-w) - var(--rail-icon)) / 2);                      /* 8px  */
  --rail-glyph-x:calc(var(--rail-icon-x) + (var(--rail-icon) - var(--rail-glyph)) / 2); /* 18px */
  /* Search bar's height — it shares the logo's row, and being the taller of
     the two it's the bottom edge that row actually presents. */
  --search-h:32px;
  --rail-row-bottom:calc(var(--rail-pad) + var(--rail-icon) / 2 + var(--search-h) / 2);  /* 52px */
  /* One y for both the Map button's top and the top of any panel that hangs
     off that button. The button is positioned to this rather than falling out
     of the rail's flex flow, so the two can't drift apart. Measured a
     --rail-gap clear of the logo/search row above, so it crowds neither. */
  --rail-map-btn-y:calc(var(--rail-row-bottom) + var(--rail-gap));                       /* 64px */
  --rail-stack-h:calc(var(--rail-map-btn-y) + var(--rail-icon));
  /* Shared by the panels and the map container so they move as one. */
  --dock-dur:.25s; --dock-ease:cubic-bezier(.4,0,.2,1);
}

/* How much gutter each side is currently giving up to an open panel. The
   panels themselves are absolutely positioned in that gutter; these just
   tell .map-area how far to pull its edge in. Toggled by syncDocks(). */
.main{ --dock-left:0px; --dock-right:0px; --topmenu-h:0px; }
/* Same idea, top edge: how far the map card's top flexes down to reveal
   .map-topmenu above it. Toggled by toggleTopMenu(). */
/* Sized so map-area's open-state top (--container-inset + --topmenu-h)
   lands exactly on --rail-map-btn-y — the map/layers rail button's own top
   edge — rather than an arbitrary height that happens to be close. */
.main.menu-open{ --topmenu-h:calc(var(--rail-map-btn-y) - var(--container-inset)); }
/* Left panels start at the rail's icon column (--rail-icon-x), not at the
   rail's outer edge, so this offset is measured from there. */
.main.dock-l{ --dock-left:calc(var(--rail-glyph-x) + var(--panel-w) + var(--dock-gap-l) - var(--rail-w) - var(--rail-map-gap)); }
.main.dock-r{ --dock-right:calc(var(--panel-w) + var(--dock-gap)); }

/* ── Sidebar — the navbar. Sits directly on .main's black background, to
   the left of (not over) the map container. This is where the logo, Map
   button, and future features live, all sharing one vertical centerline. ── */
/* Above the panels, so the account button at the foot of the rail stays on
   top of a docked panel's content rather than being painted over by it.
   pointer-events are handed back to the buttons only — otherwise the rail's
   empty column would swallow clicks on the left panel underneath it. */
.sidebar{
  position:absolute;top:0;left:0;bottom:0;width:var(--rail-w);
  background:transparent;pointer-events:none;
  display:flex;flex-direction:column;align-items:center;
  /* Bottom padding is --container-inset, not --rail-pad: the account button
     at the foot of this column (margin-top:auto) then sits on exactly the
     line the map card's own bottom edge sits on. The top keeps --rail-pad,
     which --rail-map-btn-y is measured from. */
  padding:var(--rail-pad) 0 var(--container-inset);gap:var(--rail-gap);z-index:270;
}
.sidebar > *{pointer-events:auto}
.rail-logo{position:relative;text-decoration:none;width:40px;height:40px}
/* Pinned to --rail-map-btn-y rather than left to the rail's flex flow, so
   the button's top, the rule capping it and the top of any panel hanging
   off it are all literally the same number. */
#rail-map-btn{position:absolute;top:var(--rail-map-btn-y);left:var(--rail-icon-x)}
/* No fixed width here — it animates from the full wordmark down to just the
   "3", same reveal as the main map page's topbar, just scaled down to fit
   the rail. overflow:hidden clips the wordmark as it slides/shrinks; the
   sidebar itself has no overflow clipping, so the full-width reveal is
   allowed to spill out over the map for that first 1.4s. */
/* Left edge pinned at 7.5px so the COLLAPSED (25px-wide) state lands
   centered in the 40px box, matching the rail buttons' centerline below
   it — the expanded (115px) state then only ever overflows to the right,
   never pushed off-screen left by flex centering a wider-than-box child. */
#nav-logo-wrap{position:absolute;left:7.5px;top:50%;transform:translateY(-50%);width:115px;height:var(--rail-logo-h);overflow:hidden;flex-shrink:0;transition:width 0.55s cubic-bezier(0.76,0,0.24,1)}
#nav-logo-wrap.collapsed{width:25px}
#logo-full{display:block;transform:translateX(0);transition:transform 0.55s cubic-bezier(0.76,0,0.24,1)}
#nav-logo-wrap.collapsed #logo-full{transform:translateX(-90px)}
#logo-full path[fill="white"]{transition:opacity 0.4s cubic-bezier(0.76,0,0.24,1)}
#nav-logo-wrap.collapsed #logo-full path[fill="white"]{opacity:0}
.rail-btn{width:40px;height:40px;border-radius:var(--container-radius);background:rgba(var(--fill-tint),0.045);border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--wire);transition:color .15s,background .15s,transform .1s;flex-shrink:0}
.rail-btn:hover{color:var(--paper);background:rgba(var(--fill-tint),0.09)}
.rail-btn:active{transform:scale(0.93)}
.rail-btn.on{color:var(--signal);background:rgba(58,211,117,0.16)}
/* Same green fill as .rail-btn.on (the map button's own "open" state) —
   hovering the sign-in button previews the same treatment it'll get once
   clicked, rather than the neutral blue-tinted hover every other rail
   button uses. Higher specificity than .main.dock-l's own hover override
   below, so it only applies while the sign-in sidecar is closed. */
#rail-signin-btn:hover{color:var(--signal);background:rgba(58,211,117,0.16)}
/* Open state is just .rail-btn.on (toggled in openSigninSidecar/
   closeSigninSidecar) — the same plain green button fill the map button
   gets, no masking rectangle behind it. */

/* ── Windows — the Grid layers panel, Sign-in and Facility/Sponsor sidecars.
   These are NOT cards floating over the map: they're siblings of .map-area
   sitting directly on the page's black background, with no fill, border,
   radius or shadow of their own. Opening one shrinks the map container
   instead (see --dock-left/--dock-right on .main), so the map gets out of
   the way rather than being covered.
   Left gutter (between the rail and the map) holds the layers panel and the
   sign-in sidecar, which are therefore mutually exclusive — see
   openSidebarWindow(). The right gutter holds the facility and sponsor
   sidecars, mutually exclusive with each other for the same reason.
   Because they no longer slide out from behind a card edge, the open/close
   move is a short fade-and-drift rather than a full translate offscreen. ── */
.layers-panel,.facility-sidecar{
  position:absolute;top:var(--container-inset);bottom:var(--container-inset);
  width:var(--panel-w);
  display:flex;flex-direction:column;overflow:hidden;
  /* Sequenced, not simultaneous: the map container flexes first and the
     content eases into the space it left. Opening therefore waits a full
     --dock-dur before starting (see the :not(.collapsed)/.open rules);
     closing has no delay, so the content clears out before the map
     reclaims the width. */
  transition:transform var(--dock-dur) var(--dock-ease),opacity var(--dock-dur) var(--dock-ease);
  transition-delay:0s;
}
/* Left gutter panels hang directly below the rail's Map button, sharing the
   glyph's left line as the PANEL's own edge — but content inside lines up
   with the headline's own left edge instead (see the .sb-section/.sc-pad
   rule below), not this outer edge/the rail icons above it. */
.layers-panel,.facility-sidecar.dock-left{
  left:var(--rail-glyph-x);
  top:var(--rail-map-btn-y);
}
/* Header sits ON the Map button's row — same height as the icon, vertically
   centred against it — so the headline and its back arrow read as that row's
   label. Indented past the glyph so the title sits beside the icon rather
   than on top of it. */
.layers-panel .sidebar-header,.facility-sidecar.dock-left .sidebar-header{
  height:var(--rail-icon);padding:0 0 0 calc(var(--rail-glyph) + 20px);
}
.sc-pad{padding:16px 18px}
/* Everything below the header (stat cards, layer toggles, form fields, …)
   left-aligns with the HEADLINE's own text (same calc(--rail-glyph + 20px)
   indent as .sidebar-header above), not the rail icon column — they used to
   sit flush with the panel's outer edge instead, one line further left than
   the title above them. */
.layers-panel .sb-section,
.facility-sidecar.dock-left .sc-pad{padding-left:calc(var(--rail-glyph) + 20px);padding-right:var(--chrome-inset)}
.layers-panel{z-index:250}
.layers-panel.collapsed{opacity:0;transform:translateX(-24px);pointer-events:none}
.layers-panel:not(.collapsed),.facility-sidecar.open{transition-delay:var(--dock-dur)}
.sidebar-header{display:flex;align-items:center;gap:10px;padding:16px 18px;flex-shrink:0}
.sidebar-title{font-family:var(--display);font-size:17px;font-weight:700;flex:1;min-width:0}
.sidebar-collapse-btn{width:28px;height:28px;border-radius:10px;border:1px solid rgba(255,255,255,0.15);background:transparent;color:var(--muted);cursor:pointer;flex-shrink:0;display:flex;align-items:center;justify-content:center;font-size:14px;transition:background .15s,color .15s}
.sidebar-collapse-btn:hover{color:var(--paper);border-color:rgba(255,255,255,0.35)}
.sidebar-body{flex:1;overflow-y:auto;display:flex;flex-direction:column;min-height:0}
/* Sign-in content sits at the BOTTOM of the panel, near the rail's own
   sign-in button (bottom of the rail — see #rail-signin-btn's own
   margin-top:auto) rather than up near the header like every other panel's
   content. */
#signin-sidecar .sidebar-body{justify-content:flex-end}
/* Lifted clear of the rail's sign-in button rather than sitting level with
   it. At padding-bottom:0 the Notify Me button shared a bottom edge with
   that button and the two read as one wide control split by the rail.
   Raising the content by the button's own height puts Notify Me's bottom
   edge on the button's TOP edge — caddy-corner to it, diagonally rather
   than beside it. The extra --rail-content-gap makes the vertical gap the
   same 10px as the horizontal one already between them, so the diagonal is
   even; both terms are the rail's own variables, so this tracks the rail if
   it ever resizes. */
#signin-sidecar .sc-pad{padding-bottom:calc(var(--rail-icon) + var(--rail-content-gap))}
/* Scrollbar — black thumb in a dark grey track, matching in both engines */
.sidebar-body{scrollbar-color:#000000 #1a1a1a;scrollbar-width:thin}
.sidebar-body::-webkit-scrollbar{width:8px}
.sidebar-body::-webkit-scrollbar-track{background:#1a1a1a}
.sidebar-body::-webkit-scrollbar-thumb{background:#000000;border-radius:4px}
.sidebar-body::-webkit-scrollbar-thumb:hover{background:#000000}

.sb-section{padding:12px 16px}
.sb-label{font-family:var(--mono);font-size:9px;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);margin-bottom:8px}

/* Layer pills */
.layer-list{display:flex;flex-direction:column;gap:6px}
/* No outline on these — a layer being on is signalled by its facility count
   lighting up in that layer's own colour (and the glyph/label coming out of
   dim), which doubles as a legend. Rows with no count (the geography
   toggles) rely on the glyph + label alone. --layer-color is set per pill in
   buildSidebarLayers(). */
/* Right padding zeroed (left kept, for the icon's own inset) — .lpill-count's
   margin-left:auto then lands flush with the section's own content edge,
   matching .group-head/.mini-btn's right edge instead of sitting 8px short
   of it. */
.lpill{display:flex;align-items:center;gap:8px;padding:7px 0 7px 8px;border:none;border-radius:8px;cursor:pointer;user-select:none;transition:background .15s,color .15s;font-size:11.5px;color:var(--muted)}
.lpill:hover{background:rgba(var(--fill-tint),0.05)}
.lpill.on{color:var(--paper)}
.lpill .ldot,.lpill .lsq,.lpill .lhex,.lpill .ldia{opacity:.3;transition:opacity .15s}
.lpill.on .ldot,.lpill.on .lsq,.lpill.on .lhex,.lpill.on .ldia{opacity:1}
.lpill-count{margin-left:auto;font-family:var(--mono);font-size:10px;color:rgba(255,255,255,0.18);transition:color .15s}
.lpill.on .lpill-count{color:var(--layer-color,var(--signal));font-weight:500}
.ldot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
.lsq{width:8px;height:8px;border-radius:0;flex-shrink:0}
.lhex{width:9px;height:9px;flex-shrink:0;clip-path:polygon(50% 0%,93% 25%,93% 75%,50% 100%,7% 75%,7% 25%)}
.ldia{width:8px;height:8px;flex-shrink:0;transform:rotate(45deg)}
.group-head{display:flex;justify-content:space-between;align-items:center;margin:10px 0 6px;font-family:var(--mono);font-size:9px;letter-spacing:.1em;text-transform:uppercase;color:var(--wire)}
.group-head:first-child{margin-top:0}
.mini-btn{font-family:var(--mono);font-size:9px;letter-spacing:.06em;padding:2px 8px;border-radius:0;border:0.5px solid rgba(255,255,255,0.15);background:transparent;color:var(--muted);cursor:pointer;transition:color .15s,border-color .15s}
.mini-btn:hover{color:var(--paper);border-color:rgba(255,255,255,0.35)}

/* Search results — rendered into #search-results (see .map-search-results
   below for where that div actually sits); item styling shared regardless
   of container. */
.sr-item{padding:7px 8px;font-family:var(--mono);font-size:10.5px;cursor:pointer;border-radius:0;color:var(--wire);transition:background .1s;line-height:1.5}
.sr-item:hover{background:rgba(var(--fill-tint),0.06);color:var(--paper)}
.sr-item b{color:var(--paper);font-weight:500}
.sr-sub{color:var(--muted);font-size:9px}

/* Zoom controls — same 10px radius and translucent white fill as the rail's
   own .rail-btn, so every control on screen reads as one family. These sit
   over the canvas rather than the page background, so they take a dark
   translucent fill plus a backdrop blur instead of the rail's sheer white
   tint — over a busy stretch of map a light fill washes out and the glyphs
   stop reading. */
.zoom-controls{position:absolute;bottom:var(--chrome-inset);right:var(--chrome-inset);display:flex;flex-direction:column;gap:4px;z-index:200;pointer-events:auto}
.zoom-btn{width:34px;height:34px;background:rgba(0,0,0,0.55);border:none;border-radius:5px;color:var(--paper);font-size:18px;display:flex;align-items:center;justify-content:center;cursor:pointer;font-family:var(--mono);transition:background .15s,opacity .15s,transform .1s;user-select:none;pointer-events:auto;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}
.zoom-btn:hover{background:rgba(0,0,0,0.75)}
.zoom-btn:active{transform:scale(0.93)}
.zoom-reset{font-size:10px;letter-spacing:.05em}

/* ── Facility search — fixed near the rail logo's row, a direct child of
   .main so it's positioned in .main's own coordinate frame, not the map
   card's. Deliberately a STATIC left (--rail-w + --chrome-inset) rather
   than anything derived from --dock-left — that's the whole point: it stays
   exactly here whether the map card's edge is at its collapsed position or
   shoved right by an open panel. top:20px centers its 32px height on the
   logo's own 36px center, which puts it --chrome-inset below the map card's
   own top edge too — the same --chrome-inset used on its left, so the two
   paddings match instead of one quietly being --container-inset. z-index
   265 (below the rail's own 270, above every panel — layers-panel 250,
   facility-sidecar 260) so it renders on top of whatever's under it rather
   than being buried by an open panel. Same translucent black + blur
   treatment as .zoom-btn/.lmp-legend (which now share --chrome-inset too),
   so it reads as one family of floating map chrome. There is only ever this
   one input (id="facility-search"). ── */
/* left:64px = --rail-w(56px) + --chrome-inset(8px) — its landing/default
   position, matching the same corner inset every other piece of floating
   map chrome uses. When a left-docked panel is open (.main.dock-l), it
   shifts instead so the search ICON (14px inset inside the pill) lines up
   with that panel's own headline/body text (--rail-glyph-x + --rail-glyph
   + 20px — see .sidebar-header's own padding-left and the .sb-section/
   .sc-pad rule below it), rather than staying at its landing spot and
   sitting a few px off to the side of that content. */
/* Starts hidden/offset and animates in on its own (see initSplash's
   reveal class add) — the splash overlay covers it either way until then,
   but this is what makes it the one thing that visibly *animates* into
   place as the splash clears, rather than just being flatly uncovered
   along with the rest of the map chrome underneath. */
.map-search-teaser{position:absolute;top:20px;left:calc(var(--rail-w) + var(--chrome-inset));z-index:265;width:210px;height:var(--search-h);display:flex;align-items:center;background:rgba(0,0,0,0.55);border-radius:5px;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);opacity:0;transform:translateY(-6px);transition:opacity .5s ease,transform .5s ease,left var(--dock-dur) var(--dock-ease)}
.map-search-teaser.reveal{opacity:1;transform:none}
.main.dock-l .map-search-teaser{left:calc(var(--rail-glyph-x) + var(--rail-glyph) + 6px)}
.map-search-teaser input{width:100%;height:100%;padding:0 14px 0 34px;background:transparent;border:none;color:var(--paper);font-family:var(--mono);font-size:11px;outline:none}
.map-search-teaser input::placeholder{color:var(--muted)}
.map-search-teaser .search-icon{position:absolute;left:14px;top:50%;transform:translateY(-50%);width:12px;height:12px;opacity:.5}
/* Results float directly under the pill rather than pushing its height —
   same floating-chrome treatment, solid enough to stay legible over the map
   (or an open panel). Tracks the pill's own left, landing vs. dock-l. */
/* Opaque, not glass: the layers panel carries no ground of its own — its
   rows sit straight on the app's black — so a translucent dropdown let the
   layer names and counts read through the results. --grid is the map's own
   card surface, which is what the comment above always meant by "solid". */
.map-search-results{position:absolute;top:60px;left:calc(var(--rail-w) + var(--chrome-inset));z-index:264;width:260px;max-height:280px;overflow-y:auto;background:var(--grid);border:0.5px solid rgba(255,255,255,0.15);border-radius:5px;padding:4px;transition:left var(--dock-dur) var(--dock-ease)}
.main.dock-l .map-search-results{left:calc(var(--rail-glyph-x) + var(--rail-glyph) + 6px)}
.map-search-results:empty{display:none;padding:0}

/* Close button for the right-docked sidecars (Facility, Sponsor) — top:20px,
   same row as the search bar. The left-docked pair (Grid layers, Sign-in)
   deliberately has none: the rail button that opens each also toggles it
   shut, so a second control would be redundant there.
   Sized to the ✕ glyph itself (width:auto) rather than a fixed 32px box: a
   centred glyph in a 32px box lands ~11px inside the box's own right edge,
   so box-aligning it left the ✕ visibly short of the content it's supposed
   to line up with. Left padding alone gives back the hit area without
   moving that right edge, and positioning by `right` anchors that edge
   whatever the glyph measures — --container-inset (the panel's own right
   edge) + 18px, .sc-pad's right padding, = its content edge.
   Hidden unless one of the two is open — via opacity/pointer-events rather
   than display, so it can fade with the same timing as the panel's own
   content: 0s delay closing (fades out immediately, matching
   .facility-sidecar's base rule), var(--dock-dur) delay opening (waits for
   the map to finish flexing first, same as .facility-sidecar.open below),
   so it loads in WITH the content instead of snapping on early. */
/* Phone-only: shown from the max-width:900px block. */
.panel-close-m{display:none}
.sidecar-close-btn{position:absolute;top:20px;right:calc(var(--container-inset) + 18px);z-index:265;height:var(--search-h);padding:0 0 0 12px;display:flex;align-items:center;background:none;border:none;color:var(--wire);cursor:pointer;font-size:13px;opacity:0;pointer-events:none;transition:color .15s,opacity var(--dock-dur) var(--dock-ease);transition-delay:0s}
.sidecar-close-btn:hover{color:var(--paper)}
.main.sidecar-open .sidecar-close-btn{opacity:1;pointer-events:auto;transition-delay:var(--dock-dur)}

/* ── LMP legend — top-right key for the price-gradient wash. Same radius /
   fill / blur treatment as .zoom-btn above. Sits inside the map container
   at a lower z-index than the side panels, so it's naturally covered when
   the Facility sidecar occupies that corner. ── */
/* align-items:flex-end so both columns land their LAST row on one line:
   the gradient's endpoints-and-caption row on the left, the average's region
   label on the right. The value and the bar still line up above it, because
   each column is two rows of the same heights. */
.lmp-legend{position:absolute;top:var(--chrome-inset);right:var(--chrome-inset);z-index:210;background:rgba(0,0,0,0.55);border:none;border-radius:5px;padding:9px 12px;display:flex;align-items:flex-end;gap:12px;font-family:var(--mono);pointer-events:none;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);transition:opacity .15s}
.lmp-legend-col{display:flex;flex-direction:column;gap:6px}
.lmp-legend-label{font-size:8px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted);white-space:nowrap;padding:0 8px;align-self:center}
.lmp-legend-bar{width:160px;height:6px;border-radius:3px;background:linear-gradient(90deg,#40D0EB 0%,#26F096 30%,#FFB220 60%,#FF7E34 82%,#FF5C40 100%)}
.lmp-legend-scale{display:flex;justify-content:space-between;align-items:baseline;font-size:9px;color:var(--wire)}
.lmp-legend-divider{width:1px;align-self:stretch;background:rgba(255,255,255,0.1)}
/* Value first, region callout under it. The value's 14px line is taller than
   the 6px bar it sits level with, so it gets a small negative top offset to
   put its cap height — not its line box — on the bar's line. */
.lmp-legend-avg{display:flex;flex-direction:column;gap:4px;align-items:flex-end}
.lmp-legend-avg-label{font-size:8px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);white-space:nowrap}
.lmp-legend-avg-val{font-size:14px;font-weight:600;color:var(--paper);line-height:1;margin-top:-2px}

/* ── Map canvas area — the rounded card. Inset from the window edges and
   offset past the rail; overflow:hidden clips the canvas and every docked
   panel to this same rounded rect. ── */
.map-area{
  position:absolute;top:calc(var(--container-inset) + var(--topmenu-h));bottom:var(--container-inset);
  left:calc(var(--rail-w) + var(--rail-map-gap) + var(--dock-left));
  right:calc(var(--container-inset) + var(--dock-right));
  border-radius:var(--container-radius);overflow:hidden;cursor:grab;background:var(--map-ground);
  /* Matches the panels' own .25s so the card and the panel move together.
     A ResizeObserver re-fits the canvas as this animates — see initEvents().
     `top` is in here too now, for the top-menu flex (see .main.menu-open). */
  transition:top var(--dock-dur) var(--dock-ease),left var(--dock-dur) var(--dock-ease),right var(--dock-dur) var(--dock-ease);
}
.map-area.dragging{cursor:grabbing}
#map-canvas{display:block;width:100%;height:100%}

/* ── Top menu — mirrors the landing page's primary nav (Manifest, Library +
   dropdown). Occupies exactly the strip .map-area's own top edge flexes
   down to reveal (--topmenu-h, see .main.menu-open above); height:0 +
   overflow:hidden when closed rather than display:none, so that flex reads
   as one continuous card-height change instead of the menu just popping in
   once the card's already moved. No background/card of its own — it's just
   a positioning strip; Manifest/Library float directly on the page like
   the search bar does. Spans the FULL page width (left/right:0 against
   .main, not the map card's own narrower span) so justify-content:center
   centers the nav items on the page rather than on the map card — which
   also means they land well clear of the fixed-position search bar over on
   the left without needing to reason about its width at all. ── */
.map-topmenu{
  position:absolute;top:var(--container-inset);left:0;right:0;
  height:0;overflow:hidden;
  display:flex;align-items:center;justify-content:center;gap:28px;
  opacity:0;pointer-events:none;
  transition:height var(--dock-dur) var(--dock-ease),opacity var(--dock-dur) var(--dock-ease);
}
.main.menu-open .map-topmenu{height:var(--topmenu-h);opacity:1;pointer-events:auto}
.map-topmenu a,.map-topmenu-dropdown-trigger{color:var(--wire);text-decoration:none;background:none;border:none;cursor:pointer;font-family:var(--mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;white-space:nowrap;transition:color .15s;padding:0}
.map-topmenu a:hover,.map-topmenu-dropdown-trigger:hover{color:var(--paper)}
.map-topmenu-dropdown{position:relative}
.map-topmenu-dropdown-trigger{display:flex;align-items:center;gap:5px}
.map-topmenu-dropdown-trigger::after{content:'▾';font-size:8px}
/* Same design as index.html's own .nav-dropdown-menu (landing page primary
   nav) — near-opaque black, 1px rule border, heavy blur, sharp corners, no
   shadow. Positioned dynamically (left/top set inline by
   toggleTopMenuDropdown, centered under the trigger like the landing
   page's own `left:50%;transform:translateX(-50%)` does) rather than
   CSS-anchored under the trigger — see the HTML comment above
   #map-topmenu-library-menu for why this lives outside .map-topmenu
   instead of nested under it. */
.map-topmenu-dropdown-menu{display:none;position:absolute;transform:translateX(-50%);padding:8px 0;min-width:200px;background:rgba(0,0,0,0.96);border:1px solid rgba(255,255,255,0.1);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);z-index:267}
.map-topmenu-dropdown-menu.open{display:block}
.map-topmenu-dropdown-menu a{display:block;padding:10px 20px;font-family:var(--mono);font-size:10px;font-weight:500;letter-spacing:.18em;text-transform:uppercase;color:var(--muted);text-decoration:none;white-space:nowrap;transition:color .2s,background .2s}
.map-topmenu-dropdown-menu a:hover{color:var(--paper);background:rgba(255,255,255,0.06)}

/* ── Menu handle — the dash/chevron pull-tab, centered horizontally on the
   page. A sibling of .map-area (not a child), so it isn't clipped by that
   card's overflow:hidden. top:20px matches the shared top line the search
   bar (top:20px) and the LMP legend (chrome-inset 8px below the map card's
   own top, which is container-inset 12px — 12+8=20 too) both sit on when
   the menu is closed, rather than poking up above that line. + --topmenu-h
   keeps it tracking the card's edge as the menu opens, same as
   .map-topmenu itself. Dash by default; hover morphs it into an up-chevron
   (a hint of what clicking does), which flips to point down once the menu
   is actually open. ── */
.map-menu-handle{
  position:absolute;top:calc(20px + var(--topmenu-h));left:50%;transform:translateX(-50%);
  width:44px;height:18px;display:flex;align-items:center;justify-content:center;
  background:none;border:none;padding:0;cursor:pointer;z-index:266;
  transition:top var(--dock-dur) var(--dock-ease);
}
.map-menu-handle .dash{width:22px;height:2px;border-radius:1px;background:var(--wire);transition:opacity .15s,transform .15s}
.map-menu-handle .chevron{position:absolute;width:12px;height:12px;color:var(--wire);opacity:0;transform:translateY(2px);transition:opacity .15s,transform .15s}
.map-menu-handle:hover .dash{opacity:0;transform:scale(0.6)}
.map-menu-handle:hover .chevron{opacity:1;transform:translateY(0)}
.main.menu-open .map-menu-handle .chevron{opacity:1;transform:translateY(0) rotate(180deg)}
.main.menu-open .map-menu-handle .dash{opacity:0}
.main.menu-open .map-menu-handle:hover .chevron{transform:rotate(180deg) translateY(-2px)}

/* ── Shared price cell styles (ERCOT live bar) ── */
.pb-cell{padding:10px 14px;border-right:0.5px solid rgba(255,255,255,0.06);display:flex;flex-direction:column;justify-content:center;gap:3px}
.pb-cell:last-child{border-right:none}
.ip-item{display:flex;gap:6px;align-items:baseline}
.ip-name{color:var(--muted);font-size:9px;letter-spacing:.1em}
.ip-val{font-size:12px;font-weight:500;color:#e8e0cc;transition:color .3s}
.ip-val.up{color:#FFA514}.ip-val.down{color:#3AD375}

/* Tooltip */
.maptip{position:absolute;pointer-events:none;background:rgba(0,0,0,0.9);border:0.5px solid rgba(255,255,255,0.15);border-radius:0;padding:8px 12px;font-size:11px;z-index:500;display:none;max-width:200px;line-height:1.5;font-family:var(--mono)}
.lmp-cursor-tip{position:absolute;pointer-events:none;transform:translate(-50%,-100%);background:rgba(0,0,0,0.85);border:0.5px solid rgba(255,255,255,0.15);border-radius:5px;padding:5px 9px;z-index:500;display:none;white-space:nowrap;font-family:var(--mono)}
.lmp-tip-price{font-size:13px;font-weight:600}
.lmp-tip-unit{font-size:9px;color:var(--muted);margin-left:3px}


/* ── Facility sidecar — nests in the map container's top-right corner
   (see .layers-panel above for the shared --panel-inset/--panel-radius
   convention). Doubles as the visual "page" for facility.html (see
   assets/map-app.js openFacilitySidecar/bootFacilityFromPath): on
   texas.html it opens over the map when you click a marker; on
   facility.html it's open on load, pre-populated from the URL's slug.
   Same header/body pattern as .layers-panel. The Sign-in sidecar reuses
   this same shell with .dock-left added, to nest in the top-left corner
   instead (see openSigninSidecar in map-app.js) — it's mutually
   exclusive with .layers-panel there rather than with this. ── */
.facility-sidecar{
  right:var(--container-inset);
  opacity:0;transform:translateX(24px);pointer-events:none;z-index:260;
}
.facility-sidecar.open{opacity:1;transform:none;pointer-events:auto}
/* `left` deliberately omitted — it's set above alongside .layers-panel, and
   redeclaring it here (equal specificity, later in the file) would silently
   win and knock the panel out of line with the rail icons. */
.facility-sidecar.dock-left{right:auto;transform:translateX(-24px)}
/* Equal specificity to .open above (two classes each) would let source
   order silently decide the winner — pin it explicitly instead. */
.facility-sidecar.dock-left.open{transform:none}
/* Facility sidecar has no sidebar-header/title bar — it starts straight into
   its own content, with just the status badge (+ a close button standing in
   for the header's) on a row above the eyebrow and name. Sign-in/Sponsor
   still use the shared header, so this is scoped to facility-sidecar only. */
/* gap is 0.4em, not the 7px (0.7em) it was. Both marker shapes taper to a
   point on their right — the diamond to a single vertex, the hexagon to its
   flat-top corner — so the ink stops well short of the 10px box the flex gap
   measures from, and the optical space ran a good 2px wider than the nominal
   one. At 0.7em that read as a detached bullet with a hole after it. 0.4em
   lands the optical gap on the label's own 0.1em tracking rhythm. In em so it
   tracks font-size; the icon is drawn to the same 10px (facilityCategoryIcon). */
.sc-eyebrow{font-family:var(--mono);font-size:10px;font-weight:400;color:var(--muted);letter-spacing:.1em;text-transform:uppercase;min-height:1.2em;display:flex;align-items:center;gap:0.4em}
/* The facility panel's eyebrow ran straight into the title below it. Its
   own padding opens that gap, rather than a margin on the title, so the
   label owns the space it sits in.
   Only padding-bottom: with the status tag removed the eyebrow is the first
   thing in .sc-pad, so the space above it is already the panel's own 16px
   inset — a padding-top here would stack on that and land 28 above against
   12 below. Scoped to this panel; the Sign-in and Sponsor eyebrows head
   their panels with no headline crowding them from underneath. */
#facility-sidecar .sc-eyebrow{padding-bottom:12px}
/* Header glyph. Echoes something the map already draws, so the panel reads
   as the thing you clicked rather than a detached record: .sc-cat-icon
   repeats the marker itself — same diamond/hex split by group, same
   CATEGORY_META colour as the dot on the map. A decorative restatement of
   the label beside it, hence aria-hidden at the point of render (see
   facilityCategoryIcon).
   There was a second glyph here, .sc-badge-dot, inside a status tag above
   the eyebrow. The tag is gone; status still reads in the stats grid, in
   the same statusColor. */
.sc-cat-icon{flex-shrink:0;display:block}
.sc-title-row{display:flex;align-items:flex-start;gap:8px;min-width:0;margin-top:4px}
.sc-title{font-family:var(--display);font-size:19px;font-weight:700;letter-spacing:.01em;line-height:1.3;min-width:0;overflow-wrap:break-word;word-break:break-word}
.sc-sub{font-size:11px;color:var(--muted);font-family:var(--mono);margin-top:6px}
.sc-stats{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin:16px 0}
.mstat{background:rgba(var(--fill-tint),0.04);border-radius:8px;padding:10px 12px;text-align:center}
.sc-notes{font-size:12px;color:var(--muted);line-height:1.7;padding:12px 14px;background:rgba(var(--fill-tint),0.03);border:0.5px solid rgba(255,255,255,0.07);border-radius:8px}
.mstat-val{font-size:16px;font-weight:500;margin-bottom:2px}
.mstat-lbl{font-family:var(--mono);font-size:9px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
.sc-section-label{font-family:var(--mono);font-size:9px;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);margin-bottom:10px;display:flex;align-items:center;gap:8px}
.src-link{display:flex;align-items:center;justify-content:space-between;gap:8px;font-size:11px;color:var(--wire);text-decoration:none;padding:9px 12px;background:rgba(var(--fill-tint),0.02);border:0.5px solid rgba(255,255,255,0.08);border-radius:8px;line-height:1.4;transition:border-color .15s,color .15s;margin-top:16px}
.sc-crumbs{display:flex;gap:6px;font-family:var(--mono);font-size:10px;letter-spacing:.06em;color:var(--muted);margin-bottom:14px;padding-right:calc(var(--search-h) + 10px)}
.sc-crumbs a{color:var(--muted);text-decoration:none}
.sc-crumbs a:hover{color:var(--paper)}
.sc-summary{color:var(--paper);opacity:.86;font-size:13px}
.sc-section-label{margin-top:22px}
.sc-prose p{font-size:12.5px;color:var(--muted);line-height:1.75;margin:0 0 12px}
.sc-faq{margin-bottom:14px}
.sc-faq-q{font-family:var(--display);font-size:13px;font-weight:600;color:var(--paper);line-height:1.4;margin:0 0 5px}
.sc-faq p{font-size:12px;color:var(--muted);line-height:1.7;margin:0}
.sc-links{display:flex;flex-direction:column;gap:8px;padding-bottom:6px}
.rel-link{flex-wrap:wrap}
.rel-link small{display:block;width:100%;font-family:var(--mono);font-size:9px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);opacity:.8;order:3}
.rel-link::after{order:2}
.src-link.internal::after{content:'→'}
.src-link:hover{border-color:rgba(255,255,255,0.22);color:var(--paper)}
.src-link::after{content:'↗';font-size:10px;opacity:.5;flex-shrink:0}

/* ── Sign-in sidecar content — a "coming soon" notice + email waitlist
   (see #signin-sidecar, handleSigninEmailSubmit in map-app.js). Same
   right-docked shell as the facility sidecar; these rules just style what's
   inside it, using the map's own tokens (--signal/--mono/--sans/--display)
   instead of the old standalone login.html page's separate palette. ── */
.auth-sub{font-size:12px;color:var(--muted);line-height:1.6;margin-top:8px}
.auth-seg{display:flex;border:0.5px solid rgba(255,255,255,0.1);border-radius:8px;overflow:hidden;margin:16px 0}
.auth-seg button{flex:1;padding:8px 0;border:none;background:transparent;color:var(--muted);font-family:var(--mono);font-size:11px;letter-spacing:.04em;cursor:pointer;transition:background .15s,color .15s}
.auth-seg button.on{background:var(--signal);color:var(--ink);font-weight:500}
.auth-seg button:not(:last-child){border-right:0.5px solid rgba(255,255,255,0.1)}
.auth-field{margin-bottom:14px}
.auth-field label{display:block;font-family:var(--mono);font-size:9px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin-bottom:6px}
.auth-field input{width:100%;padding:9px 11px;background:rgba(var(--fill-tint),0.04);border:0.5px solid rgba(255,255,255,0.14);color:var(--paper);font-family:var(--sans);font-size:13px;border-radius:6px;outline:none;transition:border-color .2s}
.auth-field input:focus{border-color:rgba(58,211,117,0.5)}
.auth-field input::placeholder{color:var(--muted)}
.auth-submit-btn{width:100%;margin-top:2px;padding:11px 0;border-radius:6px;border:0.5px solid rgba(58,211,117,0.4);background:rgba(58,211,117,0.14);color:var(--signal);font-family:var(--mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;cursor:pointer;transition:background .15s,border-color .15s}
.auth-submit-btn:hover{background:rgba(58,211,117,0.24);border-color:rgba(58,211,117,0.7)}
.auth-note{margin-top:18px;padding:12px 14px;background:rgba(58,211,117,0.05);border:0.5px solid rgba(58,211,117,0.18);border-radius:8px;font-size:11px;color:rgba(58,211,117,0.9);line-height:1.6}
.auth-note b{color:var(--signal)}
.auth-mockup-note{margin-top:14px;text-align:center;font-family:var(--mono);font-size:9.5px;letter-spacing:.04em;color:var(--muted);line-height:1.7}

/* Sponsor-a-state "pay with" placeholder + email opt-in (see #sponsor-sidecar).
   Real payment methods (Bitcoin wallet, Strike, Kraken, Coinbase) aren't wired
   up yet — see the note above openSponsorSidecar() — so this is just a
   coming-soon card instead of a selectable (but non-functional) method list. */
.tip-comingsoon{padding:14px;background:rgba(255,165,20,0.06);border:0.5px solid rgba(255,165,20,0.22);border-radius:8px}
.tip-comingsoon-badge{display:inline-block;font-family:var(--mono);font-size:9px;letter-spacing:.1em;text-transform:uppercase;color:#FFA514;border:0.5px solid rgba(255,165,20,0.4);border-radius:5px;padding:2px 8px}

.tip-toggle{display:flex;align-items:center;gap:10px;cursor:pointer;user-select:none}
.tip-toggle input{position:absolute;opacity:0;width:0;height:0}
.tip-toggle-track{position:relative;width:32px;height:18px;border-radius:9px;background:rgba(var(--fill-tint),0.1);flex-shrink:0;transition:background .18s}
.tip-toggle-thumb{position:absolute;top:3px;left:3px;width:12px;height:12px;border-radius:50%;background:var(--wire);transition:transform .18s,background .18s}
.tip-toggle input:checked + .tip-toggle-track{background:rgba(58,211,117,0.35)}
.tip-toggle input:checked + .tip-toggle-track .tip-toggle-thumb{transform:translateX(14px);background:var(--signal)}
.tip-toggle input:focus-visible + .tip-toggle-track{outline:2px solid rgba(58,211,117,0.6);outline-offset:2px}
.tip-toggle-label{font-size:11.5px;color:var(--wire);line-height:1.4}

/* Toast — brief confirmation strip, bottom-center, above the sidecar */
.toast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%) translateY(20px);background:var(--grid);border:0.5px solid rgba(58,211,117,0.35);border-radius:8px;padding:12px 18px;font-family:var(--mono);font-size:11px;color:var(--signal);opacity:0;pointer-events:none;transition:opacity .25s,transform .25s;white-space:nowrap;z-index:400}
.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}

/* Panels now steal width from the map rather than covering it, so a
   near-full-width panel would leave no map at all — narrow the gutter
   instead of widening the panel. */
@media(max-width:900px){
  :root{ --panel-w:260px; --dock-gap:12px; }
  /* The phone panels' single content line: past the rail's 40px button
     column, plus a gap, so nothing sits under the logo or the Map button.
     --m-inset is the screen margin the chrome keeps on the other side. */
  /* On :root, not .main — html and body are .main's ANCESTORS, so a value
     set there never reaches them. It happened to look right while both
     grounds were the same colour; the moment desktop went darker, body
     started painting the desktop ground behind the phone's safe areas. */
  :root{ --map-ground:#151823; }
  .main{
    --m-inset:12px;
    --m-content-x:calc(var(--rail-icon-x) + var(--rail-icon) + 12px);
    /* Top of anything sharing the logo's row: the logo glyph is centred in
       the rail's 40px box, so its line is that box's centre, not its top. */
    --m-row-y:calc(var(--rail-pad) + (var(--rail-icon) - var(--search-h)) / 2);
  }

  /* ── The map runs full-bleed. The rail's buttons float over the canvas
     instead of sitting in a gutter beside it, so the strip of page that
     used to show down the left is gone. Black opens there only when the
     left sidecar does, and only as wide as that panel. ── */
  /* One ground for the phone's MAP: html/body show through the safe areas
     Safari's chrome sits over, and .main shows nowhere (the map covers it)
     but is kept in step. The panels are deliberately not in this set — they
     stay black, a darker surface laid over the map. */
  html, body, .main{ background:var(--map-ground); }

  .map-area{
    top:0; bottom:0; left:0; right:0;
    border-radius:0;
  }
  /* Panels cover the map rather than pushing it aside — at this width there
     is nothing left to push it into. So the dock offsets stay at zero and
     the map simply sits behind. */
  .main.dock-l, .main.dock-r{ --dock-left:0px; --dock-right:0px; }

  .layers-panel, .facility-sidecar, .facility-sidecar.dock-left{
    left:0; right:0; top:0; bottom:0;
    width:100%; max-width:none;
    border-radius:0;
    /* These carry no ground of their own on desktop — they sit on the page's
       black beside the map card. Full-screen they need one, or the map reads
       straight through the panel. Black, deliberately: the panels stay a
       darker surface than the map they cover, which is also what the lifted
       .mstat / .sc-notes fills below are tuned against. */
    background:var(--ink);
  }
  /* Clear of the rail's own stack: the logo and the Map button float above
     the panel (z-index 270), so the panel's header starts below them. */
  /* The headline rides the Map button's own row rather than starting below
     the whole rail stack, and indents past it. */
  .layers-panel .sidebar-header{
    margin-top:var(--rail-map-btn-y);
    height:var(--rail-icon);
    padding-left:var(--m-content-x);
  }
  .layers-panel .sb-section{ padding-left:var(--m-content-x); padding-right:var(--m-inset); }

  /* Close control for the left-docked panels, on the logo and search row. */
  .panel-close-m{
    display:flex; align-items:center; justify-content:center;
    position:absolute; z-index:268;
    top:var(--m-row-y);
    right:var(--m-inset);
    width:var(--search-h); height:var(--search-h);
    background:rgba(0,0,0,0.55);
    backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
    border:none; border-radius:5px;
    color:var(--wire); font-size:13px; cursor:pointer;
  }
  .panel-close-m:hover{ color:var(--paper); }

  /* Membership reads as a centred card rather than a column pinned to the
     foot of the rail — there is no rail beside it at this width. */
  #signin-sidecar .sidebar-body{ justify-content:center; align-items:center; }
  #signin-sidecar .sc-pad{
    padding:0 var(--m-content-x) 0 var(--m-content-x);
    max-width:360px; text-align:center;
  }
  #signin-sidecar .auth-field label{ text-align:left; }

  /* Single-finger drag pans the canvas (see the touch handlers in
     initEvents), so the browser must not claim the gesture first. */
  #map-canvas{ touch-action:none; }

  /* No top menu on a phone, and no handle to open one. --topmenu-h is
     pinned so .map-area's top cannot be flexed by a stale .menu-open. */
  .map-topmenu, .map-menu-handle{ display:none; }
  .main.menu-open{ --topmenu-h:0px; }

  /* Search lives in the left sidecar here, not over the map — there is no
     room for it beside the key at this width. syncMobileSearch() moves the
     node rather than cloning it, so there is still one input and one id. */
  /* Up on the rail's logo line, not below the panel header — its left edge
     on the same rule as the layer rows underneath it (their own
     --rail-glyph + 20px indent), so the logo, the search and the whole layer
     list read off one grid. Narrower than the panel: it is a field, not a
     banner. */
  .layers-panel .map-search-teaser,
  .main.dock-l .layers-panel .map-search-teaser{
    position:absolute;
    top:var(--m-row-y);
    left:var(--m-content-x);
    right:calc(var(--m-inset) + var(--search-h) + 10px);
    width:auto;
    height:var(--search-h);
    /* flex:0 0 auto is the whole point: .sidebar-body is a flex column, so
       as a laid-out child both of these shrank against the layer list
       below them — the field came out 14px tall and the results 9px, with
       a 46px row inside. */
    opacity:1; transform:none;
    margin:0;
    background:rgba(var(--fill-tint),0.05);
    border:0.5px solid rgba(255,255,255,0.14);
    border-radius:6px;
  }
  .layers-panel .map-search-results,
  .main.dock-l .layers-panel .map-search-results{
    position:absolute;
    top:calc(var(--m-row-y) + var(--search-h) + 8px);
    left:var(--m-content-x);
    right:var(--m-inset);
    width:auto; max-height:210px;
    margin:0;
    z-index:266;
  }

  /* The key stacks: the price and its region label on top, the gradient
     and its range underneath. Column-reverse because the gradient column
     comes first in the DOM — the markup order is the desktop reading
     order and does not need to change for this. */
  .lmp-legend{
    flex-direction:column-reverse;
    align-items:stretch;
    gap:9px;
    /* Same insets the rail's buttons keep: 8px from the edge, 16px down. */
    top:var(--rail-pad); right:var(--rail-icon-x);
  }
  .zoom-controls{ right:var(--rail-icon-x); bottom:var(--container-inset); }
  .lmp-legend-divider{ display:none; }
  /* Price on its own line, hard right; its region label under it, and the
     gradient and range under that (column-reverse puts this block on top). */
  .lmp-legend-avg{
    flex-direction:column; align-items:flex-end;
    gap:3px;
  }
  .lmp-legend-col{ gap:7px; }
  .lmp-legend-bar{ width:100%; }

  /* A right-docked panel takes the screen at this width, so the rail gets
     out of the way rather than floating over the map behind it. Closing
     the panel brings it back. */
  /* Map and account buttons read as floating map chrome here, like the zoom
     controls and the LMP key — they sit directly on the canvas now rather
     than in a rail gutter, so the rail's own translucent tint made them the
     only controls on screen in a different material. */
  .rail-btn{
    background:rgba(0,0,0,0.55);
    backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);
  }
  .rail-btn:hover{ background:rgba(0,0,0,0.75); }

  /* The eyebrow is the first thing in the facility panel, so the close
     button drops onto its line rather than floating above it. Centres are
     matched: the eyebrow's box is --rail-logo-h tall against the button's
     --search-h, the same pairing the search and logo use on the row above. */
  .main.sidecar-open .sidecar-close-btn{
    top:var(--m-row-y);
    right:var(--m-inset);
  }
  .facility-sidecar .sc-pad{
    padding-top:calc(var(--rail-pad) + (var(--rail-icon) - var(--rail-logo-h)) / 2);
  }
  #facility-sidecar .sc-eyebrow{ padding-right:calc(var(--search-h) + 10px); }

  /* The panel's surfaces go up roughly 2.5x. These tints were set against
     the page's black BESIDE the map card, where the card's own cobalt gives
     the eye a reference; full-screen on a phone they sit on pure --ink with
     nothing to read against, and 0.04 of a light tint over black lands about
     #0b0c0f — a container you cannot see is not a container. */
  .mstat{ background:rgba(var(--fill-tint),0.10); }
  .sc-notes{ background:rgba(var(--fill-tint),0.07); border-color:rgba(255,255,255,0.12); }
  .src-link{ background:rgba(var(--fill-tint),0.07); border-color:rgba(255,255,255,0.12); }

  .sidebar{ transition:transform var(--dock-dur) var(--dock-ease); }
  .main.dock-r .sidebar{ transform:translateX(-100%); pointer-events:none; }
}
