* { box-sizing: border-box; }
:root { --panel-w: 560px; }
html, body { margin: 0; height: 100%; font-family: "Be Vietnam Pro", system-ui, sans-serif; }
#app { position: relative; height: 100vh; }
#stage { position: relative; height: 100%; }
/* Track A fix: the map is a SIBLING of the panel, not a full-bleed layer
   underneath it. Previously `inset: 0` made #map the full viewport while the
   fixed, opaque #preact-panel-root (560px, z-index:5) painted over its right
   third -- frameBounds() (web/export_preview.js, shared with export.js's crop
   math) was max-fitting into a box a third of which was invisible. Insetting
   #map by --panel-w makes map.getCanvas() itself the visible stage, so
   frameBounds is correct for all 26 layouts with ZERO change to the shared
   crop math. Uses the SAME --panel-w custom property as #preact-panel-root's
   width below, so the two numbers cannot drift apart. */
#map { position: absolute; inset: 0 calc(var(--panel-w) + 24px) 0 0; }
/* #preact-panel-root: the Preact shell (icon rail + one-section panel +
   persistent export bar — see web-src/App.jsx + web-src/app.css, which owns
   everything INSIDE this mount). Docked as its own fixed sidebar on the
   right, over the full-bleed #map. Task 15 removed the legacy #controls
   panel this used to sit on top of — the shell is the only UI now. */
/* UX6: transparent so the frosted panels inside (.summary-rail, #panel-column,
   #export-bar — web-src/app.css) sample the live map behind this mount via
   backdrop-filter, instead of blurring this div's own opaque fill. The three
   panels paint their own (opaque-enough) backgrounds, so nothing here relies
   on this root having a fill of its own. */
#preact-panel-root { position: fixed; top: 0; right: 0; bottom: 0; width: var(--panel-w); overflow: hidden; z-index: 5; background: transparent; border-left: 1px solid #3a3226; }
/* Mobile: the shell (web-src/app.css) reflows its OWN grid into a bottom tab
   bar; here we just make #preact-panel-root cover the full viewport. Track A:
   the panel is no longer a fixed-width sidebar at this breakpoint -- it goes
   full-screen -- so #map must NOT keep reserving --panel-w; put it back to
   full-bleed (inset: 0) or the map would be squeezed into a sliver behind a
   full-screen panel it can no longer see past. */
@media (max-width: 780px) {
  #map { inset: 0; }
  #preact-panel-root { width: 100%; height: 100%; border-left: none; }
}
/* Task 7: WYSIWYG export preview overlay (web/export_preview.js) — a crop
   frame matching the poster's aspect ratio over the live map, plus a
   draggable title chip. UX6 replaces the old border + oversized-box-shadow
   dimming trick below with real dim panels (.xp-dim) and a proper frame
   shadow — see the UX6 rules further down, which override border/box-shadow. */
.xp-frame {
  position: absolute;
  pointer-events: none;
  z-index: 3;
}
.xp-canvas {
  position: absolute;
  pointer-events: none;
  z-index: 5;
}
/* Task 6: the chip becomes a transparent drag handle over the canvas-rendered
   title — the WYSIWYG canvas paints the actual title text; this div keeps the
   hit-area/box so dragging still works, but is invisible. */
.xp-title {
  position: absolute;
  transform: translate(-50%, -50%);
  cursor: move;
  padding: 4px 10px;
  background: transparent;
  color: transparent;
  font-size: 12px;
  border-radius: 3px;
  border: 1px solid transparent;
  z-index: 6;
  touch-action: none;
  user-select: none;
}

/* UX6 — the glass stage. The poster is a framed print on a dark light-table;
   the live map stays full-bleed behind it (panning context is deliberately
   preserved), but everything outside the crop is dimmed so the eye reads the
   frame as an object. Dim PANELS, not a blur: blurring a live MapLibre canvas
   every frame is expensive and would destroy the context we kept. */
.xp-dim {
  position: absolute;
  pointer-events: none;
  z-index: 3;
  background: rgba(6, 14, 22, .62);
  backdrop-filter: saturate(55%);
}

/* the crop frame itself — a hairline plus a real two-layer drop shadow, so it
   reads as a physical print rather than a selection marquee. Hairline warmed
   to the app's gold accent (#d8b869) — no blue anywhere in this app. */
.xp-frame {
  box-shadow:
    0 24px 34px rgba(0, 0, 0, .5),
    0 8px 14px rgba(0, 0, 0, .42),
    0 0 0 1px rgba(216, 184, 105, .28);
  border-radius: 3px;
  z-index: 4;
}
.xp-canvas { z-index: 5; border-radius: 3px; }
.xp-title  { z-index: 6; }
