/**
 * maps.css — ShadowDark GM · Map Builder (Phase 1)
 *
 * Dark app chrome framing a LIGHT-PARCHMENT canvas plate (KDL 2026-06-27's "single
 * biggest call"). All UI chrome stays in the dark --color-* tokens; only the
 * <canvas> plate is parchment, and that parchment is painted on-canvas by
 * maps.js, not via CSS background. The map-local --map-* ink values live in JS
 * (they're 2D-context fills, not CSS), so this file only styles the FRAME and
 * the surrounding dark controls.
 *
 * Reuses the app's .btn / .btn--primary / .btn--secondary and the
 * segmented-toggle visual language (active = 3px accent left border + Stone
 * Wall fill; inactive = Dungeon Floor fill + muted text), same as the
 * Solo/Group switcher.
 */

.maps {
  display: flex;
  flex-direction: column;
  gap: var(--space-sm, 0.5rem);
  /* v2 (#3) — the live canvas page color, set by applyCanvasColor(); the modal
     icon discs read this so stamped ink stays legible on whichever ground. */
  --map-page-active: #e8dcc0;
}

/* ==========================================================
   v2 (#1) — three-column workspace: slim title strip on top, a LEFT toolbox
   rail, the canvas plate centered, the legend on the right.
   ========================================================== */

/* --- Slim Blackletter title strip across the top --- */
/* #3 / #5 — left-aligned header: "Map Builder" heading with the circular re-roll
   arrow inline beside it, and the editable dungeon title on its own full-width
   line below. */
.maps__titlestrip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0 0.5rem;
  border-bottom: 1px solid var(--color-border);
}

.maps__titlestrip-h {
  margin: 0;
}

/* Ken #5 — 'Back to world map' link, shown only for dungeons created from a
   world-map POI. Styled as a quiet text link (not a heavy button) so it reads as
   navigation, with a visible focus ring for keyboard users. */
.maps__worldback {
  display: inline-flex;
  align-items: center;
  margin-top: 0.15rem;
  padding: 0.1rem 0;
  background: none;
  border: 0;
  font: inherit;
  font-size: 0.85rem;
  color: var(--color-accent, #6495ED);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.maps__worldback:hover {
  color: var(--color-accent-hover, #7aa8f0);
}

.maps__worldback:focus-visible {
  outline: 2px solid var(--color-accent, #6495ED);
  outline-offset: 2px;
  border-radius: 2px;
}

.maps__worldback[hidden] {
  display: none;
}

/* --- BUILDING floor switcher -------------------------------------------------
   Chrome ABOVE the editor, shown only when the loaded map is part of a multi-
   floor building (tavern/shop interior). A compact segmented tablist: Cellar /
   Ground Floor / Upper Floor…, low->high, the current floor highlighted with the
   accent. Real <button role="tab">s -> keyboard-navigable; the editor core is
   untouched. Reuses the established surface/border/accent tokens. */
.maps__floors {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem var(--space-md, 1rem);
  padding: 0.4rem 0 0.5rem;
  border-bottom: 1px solid var(--color-border);
}

.maps__floors[hidden] {
  display: none;
}

.maps__floors-label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-text, #e8e2d4);
}

.maps__floors-tabs {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}

/* A floor tab — quiet by default, accent-bordered on hover/focus, filled when it
   is the current floor. NOT a .btn (kept out of the .btn white-button audit; this
   follows the legend-control idiom of a dedicated class). */
.maps__floor-tab {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.25rem 0.6rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 3px;
  font: inherit;
  font-size: 0.8rem;
  color: var(--color-text-muted, #b8b09c);
  cursor: pointer;
  transition: border-color 120ms ease, color 120ms ease, background-color 120ms ease;
}

.maps__floor-tab:hover {
  border-color: var(--color-accent);
  color: var(--color-text, #e8e2d4);
}

.maps__floor-tab:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Current floor — accent fill so "you are here" reads at a glance, independent of
   focus/hover. aria-selected="true" carries the same meaning for AT. */
.maps__floor-tab--current,
.maps__floor-tab[aria-selected="true"] {
  background: var(--color-accent);
  border-color: var(--color-accent);
  color: var(--color-bg, #1a1712);
  font-weight: 600;
}

/* A floor whose map was deleted independently — shown disabled, never navigable. */
.maps__floor-tab--missing,
.maps__floor-tab:disabled {
  opacity: 0.55;
  cursor: not-allowed;
  text-decoration: line-through;
}

.maps__floor-missing {
  font-style: italic;
  text-decoration: none;
}

/* ADD / REMOVE LEVEL controls (Finding #8). The level-controls row sits at the end
   of the floor switcher; "+ Add Level/Floor" is a quiet accent-outlined affordance
   (not a .btn — same audit-exempt idiom as the floor tabs). The per-floor remove is
   a small × placed right after each removable tab, reading as one control group. */
.maps__level-controls {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  margin-left: auto;   /* push the Add control to the trailing edge of the row */
}

.maps__level-add {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.25rem 0.6rem;
  background: transparent;
  border: 1px dashed var(--color-border);
  border-radius: 3px;
  font: inherit;
  font-size: 0.8rem;
  color: var(--color-text-muted, #b8b09c);
  cursor: pointer;
  transition: border-color 120ms ease, color 120ms ease;
}

.maps__level-add:hover {
  border-color: var(--color-accent);
  border-style: solid;
  color: var(--color-text, #e8e2d4);
}

.maps__level-add:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.maps__level-add:disabled {
  opacity: 0.55;
  cursor: progress;
}

/* The per-floor remove × — small, quiet, danger-tinted on hover/focus. A real
   button so it is keyboard-focusable; sits in the tablist next to its tab but is
   NOT itself a tab (no role), so it is skipped by the tablist arrow-key nav. */
.maps__level-remove {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.3rem;
  height: 1.3rem;
  margin-left: -0.15rem;   /* tuck it against the preceding tab */
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 3px;
  font: inherit;
  font-size: 0.95rem;
  line-height: 1;
  color: var(--color-text-muted, #b8b09c);
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease, background-color 120ms ease;
}

.maps__level-remove:hover {
  color: var(--color-danger, #c14b4b);
  border-color: var(--color-danger, #c14b4b);
}

.maps__level-remove:focus-visible {
  outline: 2px solid var(--color-danger, #c14b4b);
  outline-offset: 1px;
}

/* --- The 3-column grid: rail | plate | legend --- */
.maps__workspace {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: stretch;
  gap: var(--space-md, 1rem);
}

/* --- LEFT TOOLBOX RAIL ------------------------------------------------- */
.maps__rail {
  position: sticky;
  top: 0.5rem;
  align-self: start;
  width: 224px;
  max-height: calc(100vh - 8rem);
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: none;
  transition: width 200ms ease;
}

.maps__rail.is-collapsed {
  width: 64px;
}

/* Collapse chevron — top of the rail, right-aligned. */
.maps-rail__top {
  display: flex;
  justify-content: flex-end;
  padding: 0.4rem;
  border-bottom: 1px solid var(--color-border);
}

.maps__rail.is-collapsed .maps-rail__top {
  justify-content: center;
}

.maps-rail__collapse {
  appearance: none;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
  width: 2rem;
  height: 2rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: color var(--transition-fast, 150ms), border-color var(--transition-fast, 150ms);
}

.maps-rail__collapse:hover,
.maps-rail__collapse:focus-visible {
  color: var(--color-text);
  border-color: var(--color-accent);
  outline: none;
}

/* Scrolling body of the rail; the File group is pushed to the bottom. */
.maps-rail__scroll {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.5rem;
  overflow-y: auto;
  flex: 1;
}

.maps__rail.is-collapsed .maps-rail__scroll {
  padding: 0.5rem 0.4rem;
}

/* --- Rail groups --- */
.maps-rail__group {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 0.4rem 0 0.55rem;
  border-bottom: 1px solid rgba(46, 40, 32, 0.5);
}

.maps-rail__group--foot {
  margin-top: auto;
  border-bottom: none;
  border-top: 1px solid var(--color-border);
  padding-top: 0.6rem;
}

.maps-rail__grouphead {
  margin: 0;
  font-family: var(--font-heading-ui);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  color: var(--color-text-muted);
  cursor: pointer;
  user-select: none;
  display: flex;
  align-items: center;
  gap: 0.3rem;
}

.maps-rail__grouphead::after {
  content: "";
  margin-left: auto;
  width: 0.5rem;
  height: 0.5rem;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  transition: transform 200ms ease;
  opacity: 0.6;
}

.maps-rail__group.is-folded .maps-rail__grouphead::after {
  transform: rotate(-45deg);
}

.maps-rail__group.is-folded .maps-rail__groupbody {
  display: none;
}

/* Collapsed rail: hide group headers + text labels (icon-only). */
.maps__rail.is-collapsed .maps-rail__grouphead,
.maps__rail.is-collapsed .maps-rail__label,
.maps__rail.is-collapsed .maps-rail__dice,
.maps__rail.is-collapsed .maps-rail__optlbl,
.maps__rail.is-collapsed .maps-rail__check span,
.maps__rail.is-collapsed .maps__env-label {
  display: none;
}

.maps-rail__groupbody {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

/* A vertical list of icon+label buttons (Mode / Draw / File). */
.maps-rail__list {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}

/* Icon button: line icon + text label when expanded, icon-only when collapsed. */
.maps-rail__btn {
  appearance: none;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  padding: 0.5rem 0.6rem;
  background: var(--color-bg);
  color: var(--color-text-muted);
  border: 1px solid transparent;
  border-left: 3px solid transparent;
  font-family: var(--font-heading-ui);
  font-size: 0.78rem;
  text-align: left;
  cursor: pointer;
  transition:
    background-color var(--transition-fast, 150ms),
    color var(--transition-fast, 150ms),
    border-color var(--transition-fast, 150ms);
}

.maps-rail__btn:hover {
  background: var(--color-surface-hover);
  color: var(--color-text);
}

.maps-rail__btn.is-active {
  background: var(--color-surface);
  color: var(--color-text);
  border-left-color: var(--color-accent);
}

.maps-rail__btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

.maps-rail__btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

/* Icon wrapper on the Foundry button — keeps the placeholder SVG (and a future
   fft*.png <img>) centered and from shrinking, matching the bare-SVG siblings. */
.maps-rail__icon {
  display: inline-flex;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
}

/* The FoundryVTT mark is a 512×512 raster (fvtt-d20.png); pin it to the same
   20px footprint as the vector rail icons so the button row height is unchanged.
   object-fit: contain keeps the d20+anvil mark crisp and undistorted. */
.maps-rail__icon-img {
  display: block;
  width: 20px;
  height: 20px;
  object-fit: contain;
}

/* Foundry export picker — two stacked choice cards inside the shared info modal. */
.maps-foundry-modal {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.maps-foundry-modal__choice {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  width: 100%;
  padding: 0.85rem 1rem;
  background: var(--color-bg);
  color: var(--color-text);
  border: 1px solid var(--color-border, var(--color-surface-hover));
  border-left: 3px solid var(--color-accent);
  text-align: left;
  cursor: pointer;
  transition:
    background-color var(--transition-fast, 150ms),
    border-color var(--transition-fast, 150ms);
}

.maps-foundry-modal__choice:hover {
  background: var(--color-surface-hover);
}

.maps-foundry-modal__choice:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.maps-foundry-modal__choice-title {
  font-family: var(--font-heading-ui);
  font-size: 0.95rem;
  color: var(--color-text);
}

.maps-foundry-modal__choice-desc {
  font-size: 0.8rem;
  color: var(--color-text-muted);
  line-height: 1.4;
}

.maps-rail__filebtn--danger:hover {
  color: var(--color-danger);
  border-left-color: var(--color-danger);
}

.maps-ic {
  flex: none;
  display: block;
}

/* Collapsed: center the icon, drop labels, tooltips do the talking. */
.maps__rail.is-collapsed .maps-rail__btn {
  justify-content: center;
  padding: 0.5rem 0;
}

/* Rail-wide buttons (Roll, Stock, Undo) span the group width. */
.maps-rail__wide {
  width: 100%;
  justify-content: center;
  /* The base .btn is white-space:nowrap, but the 224px expanded rail is narrower
     than a long action label ("Re-Stock Unmodified Rooms"), so nowrap text spilled
     past the rail edge and got clipped (KDL 2026-06-27). Let these full-width
     rail buttons wrap to two lines and break long words, and never let the button's
     min-content force the rail/group wider than its column. The collapsed rule
     below still hides the label entirely (icon-only), so wrapping is moot there. */
  white-space: normal;
  overflow-wrap: anywhere;
  min-width: 0;
  text-align: center;
}

.maps__rail.is-collapsed .maps-rail__wide {
  font-size: 0;          /* hide label text; keep the button as a compact block */
  padding: 0.5rem 0;
}

/* Make [hidden] actually hide rail buttons. The .btn / .maps-rail__btn classes
   set display:inline-flex / flex, and at equal specificity the author rule beats
   the UA [hidden]{display:none}, so toggling the `hidden` attribute had no visual
   effect. These selectors are an attribute + class compound (higher specificity)
   so the hide wins. Governs the Stock group (.maps-rail__wide: data-map-stock,
   data-map-restock, data-map-restock-all) and the .maps-rail__btn file actions
   that show/hide via `hidden` (e.g. data-map-save-copy). JS toggling is unchanged. */
.maps-rail__wide[hidden],
.maps-rail__btn[hidden] {
  display: none !important;
}

.maps-rail__actions {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.maps-rail__dice {
  margin-left: 0;
}

/* Size toggle inside the rail — full width, segments share the row. */
.maps-rail__sizes {
  display: inline-flex;
  border: 1px solid var(--color-border);
  background: var(--color-bg);
}

.maps-rail__sizes .map-seg {
  flex: 1;
  text-align: center;
  padding: 0.45rem 0.3rem;
}

.maps__rail.is-collapsed .maps-rail__sizes {
  flex-direction: column;
}

/* Two-segment toggle (Canvas color, Wall style) in Options. */
.maps__seg2 {
  display: inline-flex;
  border: 1px solid var(--color-border);
  background: var(--color-bg);
}

.maps__seg2 .map-seg {
  flex: 1;
  text-align: center;
  padding: 0.4rem 0.3rem;
  font-size: 0.66rem;
}

.maps-rail__optlbl {
  font-family: var(--font-heading-ui);
  font-size: 0.64rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  color: var(--color-text-muted);
}

.maps-rail__check {
  font-size: 0.78rem;
}

.maps__rail.is-collapsed .maps-rail__check {
  justify-content: center;
}

.maps__rail.is-collapsed .maps-rail__seg,
.maps__rail.is-collapsed .maps-rail__optlbl,
.maps__rail.is-collapsed .maps__env {
  /* keep controls reachable when collapsed but compact; labels already hidden */
}

/* Unsaved-changes cue: the Save button's disk icon turns a clear "unsaved"
   blue while there are unsaved edits, and returns to the neutral muted color
   when clean/saved. Driven by the is-dirty class toggled in JS. */
.maps-rail__btn.is-dirty .maps-ic {
  color: var(--color-info, #4a90d9);
}
.maps-rail__btn.is-dirty:hover .maps-ic {
  color: var(--color-info, #4a90d9);
}

/* --- Segmented toggle frame (room shape). Size/canvas/wall toggles get their
   own rail frames above (.maps-rail__sizes, .maps__seg2). --- */
.maps__shape {
  display: inline-flex;
  align-items: center;
  gap: 0;
  border: 1px solid var(--color-border);
  background: var(--color-bg);
}

/* #2 (Map Builder UI) — contextual sub-options live INDENTED beneath their tool
   button and show ONLY while that tool is active. The sync functions add
   .is-disabled to the inner control wrap when its tool is not active, so a
   disabled sub-block is hidden entirely (not just dimmed). The .maps-rail__sub
   wrapper supplies the indent + a little breathing room above/below. */
.maps-rail__sub {
  padding: 0.25rem 0 0.4rem 1.4rem;
}
/* Fix: the whole sub-block hides when its tool is not the active tool. Visibility
   is driven in JS (syncToolSubVisibility) off the active tool, NOT off the inner
   control's .is-disabled — that left the wrapper itself rendering display:block. */
.maps-rail__sub.is-hidden {
  display: none;
}
.maps-rail__sub > .is-disabled {
  display: none;
}
/* Collapsed rail: drop the indent so the sub-control (when its tool is active)
   stays centered under the icon-only button. */
.maps__rail.is-collapsed .maps-rail__sub {
  padding-left: 0;
}

.maps__shape.is-disabled .map-seg {
  cursor: not-allowed;
}
.maps__terrainpick.is-disabled .maps__env-select {
  cursor: not-allowed;
}

.map-seg {
  appearance: none;
  background: var(--color-bg);
  color: var(--color-text-muted);
  border: none;
  border-left: 3px solid transparent;
  padding: 0.5rem 1rem;
  font-family: var(--font-heading-ui);
  font-size: 0.8rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  line-height: 1;
  cursor: pointer;
  transition:
    background-color var(--transition-fast, 150ms),
    color var(--transition-fast, 150ms),
    border-color var(--transition-fast, 150ms);
}

.map-seg + .map-seg {
  border-left: 1px solid var(--color-border);
}

.map-seg:hover {
  color: var(--color-text);
  background: var(--color-surface-hover);
}

.map-seg.is-active {
  background: var(--color-surface);
  color: var(--color-text);
  border-left: 3px solid var(--color-accent);
}

.map-seg:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

/* Dice-count read-out, now in the rail's Generate group. */
.maps__dice-count {
  font-family: var(--font-body);
  font-style: italic;
  color: var(--color-text-muted);
  font-size: 0.82rem;
}

/* --- Status / loading line ("Casting the bones...") — centered under title --- */
.maps__status {
  min-height: 1.25rem;
  margin: 0;
  text-align: center;
  font-family: var(--font-body);
  font-style: italic;
  color: var(--color-text-muted);
}

/* --- The parchment plate: dark Stone Wall frame around the canvas. Centered in
   the middle column, taking the available width/height. --- */
/* Ken 2026-07-07 — the title no longer has a reserved DOM band above the plate
   (it's a canvas-anchored box again, drawn on the canvas itself — see maps.js);
   .maps__platecol is kept as the single-child column wrapper for the plate. */
.maps__platecol {
  display: flex;
  flex-direction: column;
  min-width: 0;
  gap: 0.4rem;
}

.maps__plate {
  position: relative;       /* anchors the on-plate zoom controls */
  display: block;           /* the canvas (width:100%) fills this column */
  min-width: 0;
}

.maps__canvas {
  display: block;
  /* v2 (#1) — fill the (now much wider) middle column. width:100% + height:auto
     keeps the backing-store aspect ratio (no distortion — the canvas only scales
     its backing store when ONE dimension is auto). max-height caps very tall
     portrait composites so they don't run past the viewport; for those the
     height binds and the width auto-reduces to keep aspect. The big empty
     parchment border is handled by the auto-fit view (#4), not by shrinking the
     element. */
  width: 100%;
  height: auto;            /* keeps the backing-store aspect — box never distorts,
                             so client->backing hit-testing stays exact (no
                             object-fit letterboxing to offset against). */
  max-width: 100%;
  margin: 0 auto;          /* center within the wider plate */
  border: 1px solid var(--color-border);
  /* inner mat reads as a thick stone frame seating the parchment */
  box-shadow: inset 0 0 0 6px var(--color-surface);
  border-radius: 0;
}

/* Refinement 1 — zoom/pan cursors. Pannable when zoomed in (grab), grabbing
   while a pan drag is live, hand override while space is held in manual mode. */
.maps__canvas--pannable { cursor: grab; }
.maps__canvas--panning,
.maps__canvas--hand { cursor: grabbing; }

/* --- Refinement 1 — on-plate zoom controls (+/Fit/-) --- */
.maps__zoom {
  position: absolute;
  top: 0.6rem;
  right: 0.6rem;
  z-index: 5;
  display: inline-flex;
  gap: 0;
  border: 1px solid var(--color-border);
  background: var(--color-bg);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}

.maps__zoom-btn {
  appearance: none;
  background: var(--color-bg);
  color: var(--color-text);
  border: none;
  border-left: 1px solid var(--color-border);
  min-width: 2.1rem;
  height: 2.1rem;
  padding: 0 0.5rem;
  font-family: var(--font-heading-ui);
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  transition:
    background-color var(--transition-fast, 150ms),
    color var(--transition-fast, 150ms);
}

.maps__zoom-btn:first-child { border-left: none; }

.maps__zoom-btn:hover {
  background: var(--color-surface-hover);
  color: var(--color-text);
}

.maps__zoom-btn:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

.maps__zoom-btn[disabled] {
  opacity: 0.4;
  cursor: not-allowed;
}

.maps__zoom-btn.is-fit {
  color: var(--color-accent);
}

/* --- Checkbox toggles (Stone tiles, Corridor traps) in the rail --- */
.maps__walltoggle {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--font-body);
  font-size: 0.85rem;
  color: var(--color-text);
  cursor: pointer;
  user-select: none;
}

.maps__walltoggle input {
  accent-color: var(--color-accent);
  cursor: pointer;
}

/* Manual tool list is a vertical icon list in the rail (.maps-rail__list). */

/* Editable canvas affordance: crosshair while drawing, a softer cursor for
   erase so the active tool is felt as well as labelled. */
.maps__canvas--editable {
  cursor: crosshair;
  touch-action: none;   /* let pointer drags drive drawing, not page scroll */
}

.maps__canvas--editable.maps__canvas--erase {
  cursor: cell;
}

/* #8 — Pointer tool: pan/inspect. A hand (grab) cursor at rest signals the canvas
   is draggable; --panning (set while a pan drag is live) flips it to grabbing. This
   wins over the editable crosshair via the compound selector. A no-drag click still
   inspects (hover tip) and a double-click still opens the room editor. */
.maps__canvas--editable.maps__canvas--pan {
  cursor: grab;
}
.maps__canvas--editable.maps__canvas--pan.maps__canvas--panning {
  cursor: grabbing;
}

/* --- Manual-mode placeholder (kept for the empty-state note, if ever shown) --- */
.maps__manual-note {
  width: 100%;
}

/* --- Saved-maps browser (modal) — matches the Encounter saved-list look,
   floated over the workspace via the shared .action-modal scrim/card. --- */
.maps-saved-modal__card {
  width: min(560px, 92vw);
  max-height: 80vh;
  display: flex;
  flex-direction: column;
}

.maps-saved {
  overflow-y: auto;
}

/* The maps list loading state now uses the SHARED animated-text indicator
   (`.roster-loading`, styled in roster_filter.css) via
   window.ShadowDark.loadingIndicatorHTML — the maps-local copy + keyframes were
   promoted to that one shared helper in the batch-9 reconciliation, so no maps
   loading CSS lives here anymore. `.maps-saved__empty` keeps the shared base
   below. */
.maps-saved__empty {
  font-style: italic;
  margin: 0;
}

.maps-saved__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-sm, 0.5rem);
}

.maps-saved-row {
  /* position:relative anchors the Load button's stretched ::after overlay so a
     click anywhere on the row triggers Load (matches the .bestiary-card pattern). */
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-sm, 0.5rem);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-accent);
  background-color: var(--color-surface);
  padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
  cursor: pointer;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}

.maps-saved-row:hover,
.maps-saved-row:focus-within {
  background-color: var(--color-surface-hover);
  border-color: var(--color-accent);
}

/* Stretch the Load button's hit area over the whole row ("stretched link").
   Lives on the Load button only; secondary buttons sit above it (z-index). */
.maps-saved-row__load::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}

.maps-saved-row__main {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;
}

.maps-saved-row__nameline {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.maps-saved-row__name {
  font-family: var(--font-body);
  font-weight: 600;
  color: var(--color-text);
}

.maps-saved-row__current {
  font-family: var(--font-heading-ui);
  font-size: 0.62rem;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  text-transform: uppercase;
  color: var(--color-accent);
  border: 1px solid var(--color-accent);
  padding: 0.05rem 0.35rem;
}

.maps-saved-row__counts {
  font-family: var(--font-heading-ui);
  font-size: 0.72rem;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  text-transform: uppercase;
  color: var(--color-accent);
}

/* Richer meta line (environment · linked encounters · linked characters).
   Quiet body-font line under the room count, mirroring the Encounters saved
   row's terrain/counts summary. The dot separators are baked into the text. */
.maps-saved-row__meta {
  font-family: var(--font-body);
  font-size: 0.8rem;
  color: var(--color-text-muted);
}

.maps-saved-row__date {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--color-text-muted);
  opacity: 0.8;
}

.maps-saved-row__actions {
  display: flex;
  gap: var(--space-xs, 0.35rem);
  flex: none;
}

.maps-saved-row__btn {
  padding: 0.25rem 0.7rem;
  font-size: 0.72rem;
}

/* Secondary actions sit above the Load button's stretched overlay so a click on
   Delete does not also trigger row-load. The Load button itself is the overlay
   source, so it needs no raised z-index. Color + hover now come from the shared
   .btn--danger class applied in markup; this is layout-only. */
.maps-saved-row__btn--delete {
  position: relative;
  z-index: 2;
}

/* Saved-map card (H-4, 2026-06-27) — the dungeon gallery + the modal browser now
   render the shared .char-card family as a .char-card-grid. The card keeps the
   legacy .maps-saved-row class as a JS hook for the delegated Load/Delete
   handlers, so .map-card must neutralize the row's flex/border/padding and let
   the shared .char-card chrome (border-left accent, hover, medallion) own the
   look. The name keeps .maps-saved-row__name (the delete handler reads its text),
   so re-assert the blackletter heading over that span's body-font rule. */
.map-card.maps-saved-row,
.map-card.world-saved-row {
  display: block;
  align-items: initial;
  justify-content: initial;
  flex-wrap: initial;
  gap: 0;
  padding: 0;
  border-radius: 0;
  /* border / border-left / background / hover come from .char-card. */
}

.map-card .char-card__name.maps-saved-row__name,
.map-card .char-card__name.world-saved-row__name {
  font-family: var(--font-heading);
  font-weight: normal;
  font-size: var(--text-h4);
  color: var(--color-heading);
  display: inline-flex;
  align-items: baseline;
  gap: 0.4rem;
}

.map-card__current {
  font-family: var(--font-heading-ui);
  font-size: 0.6rem;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  text-transform: uppercase;
  color: var(--color-accent);
  border: 1px solid var(--color-accent);
  padding: 0.03rem 0.3rem;
  flex: none;
}

/* The card meta carries the environment · rooms · links line; let it wrap and
   drop the shared meta uppercase tracking (it is a quiet body-style summary). */
.map-card .char-card__meta.map-card__meta {
  letter-spacing: normal;
  text-transform: none;
  font-family: var(--font-body);
  color: var(--color-text-muted);
}

/* #5 (2026-06-28) — environment icon as the card media inside the shared 5rem
   .char-card__portrait medallion. The env art is an illustrative "light etching on
   black" tile, so object-fit:contain shows the WHOLE icon (cover would crop it into
   the circle). A hair of padding keeps the etching off the circular border. If the
   webp 404s, img-fallback.js replaces this <img> with the glyph fallback span (which
   the shared .char-card__portrait-fallback rules then size). */
.map-card .char-card__portrait .map-card__env-icon {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  padding: 0.35rem;
}

/* ============================================================
   #4 — Maps HOME (saved-maps landing). Left-aligned header with the heading, the
   circular refresh arrow, and a New Map button; the saved-map gallery below reuses
   the .maps-saved__list / .maps-saved-row styles above.
   ============================================================ */
.maps-home {
  gap: var(--space-md, 1rem);
}

.maps-home__head {
  /* Match the baseline page header pattern (.encounter-module__header): a flex row
     with the .module-view__title on the LEFT and the .btn--primary action on the
     RIGHT, no title-strip border/padding. This makes the Maps selection page
     look-and-feel like Forge/Encounters/Bestiary. Overrides the .maps__titlestrip
     defaults inherited from the shared class (center align, padding, bottom rule). */
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  padding: 0;
  border-bottom: none;
  margin-bottom: var(--space-sm);
}

.maps-home__refresh {
  /* Same affordance as the title re-roll arrow. */
  appearance: none;
  background: transparent;
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
  font-size: 1.1rem;
  line-height: 1;
  width: 2rem;
  height: 2rem;
  flex: none;
  cursor: pointer;
  transition:
    color var(--transition-fast, 150ms),
    border-color var(--transition-fast, 150ms);
}

.maps-home__refresh:hover,
.maps-home__refresh:focus-visible {
  color: var(--color-text);
  border-color: var(--color-accent);
  outline: none;
}

.maps-home__new {
  /* space-between on the header places this at the right edge; margin-left:auto is
     a belt-and-braces fallback if the title ever wraps. align-self:center matches
     the baseline header's primary action (.enc-landing__create). */
  margin-left: auto;
  align-self: center;
  flex: none;
}

.maps-home__body {
  /* Full module width so the saved-list's right edge aligns with the header's
     "Create New Map" button (matches the Encounters landing list, which has no
     width cap). The former 720px reading-width cap left the button overhanging
     the table by ~172px; removed so both span the full ~892px module. */
  width: 100%;
}

/* --- Save-map name modal — .action-modal family; only sizes the text field. --- */
.maps-name-modal__field {
  margin: 0;
}
.maps-name-modal__input {
  width: 100%;
}

/* ==========================================================
   PHASE 4 — Dungeon stocking UI: stock bar, title, legend, room editor.
   All chrome stays in the dark --color-* palette; only the canvas plate is
   parchment (the legend is a dark on-theme panel, not parchment, per KDL 2026-06-27's
   "dark app frames the parchment" call — it reads as DM-screen annotation).
   ========================================================== */

/* --- Environment picker — in the rail's Stock group: label above the select. --- */
.maps__env {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.maps__env-label {
  font-family: var(--font-heading-ui);
  font-size: 0.64rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  color: var(--color-text-muted);
}

/* #5 (2026-06-28) — env dropdown + a small icon preview of the selected
   environment beside it (a native <select> can't render images in options). The
   preview slot is fixed-size; the select takes the remaining width. An empty/hidden
   preview img collapses gracefully (the slot has no min-width of its own). */
.maps__env-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.maps__env-preview {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.maps__env-preview-img {
  width: 1.9rem;
  height: 1.9rem;
  object-fit: contain;
  display: block;
  border: 1px solid var(--color-border);
  background-color: var(--color-bg);
  padding: 0.15rem;
}

.maps__env-select {
  width: 100%;
  min-width: 0;
  padding: 0.4rem 0.6rem;
}

/* --- Editable dungeon title ---
   Ken 2026-07-07 — the title moved from a static DOM header band back onto the
   canvas (a draggable/resizable/rotatable/croppable box, like the compass — see
   titleGeomLive/paintTitle in maps.js), reversing the 2026-06-24 "band" decision.
   The only DOM piece left is the floating rename input, created fresh at the
   box's on-screen position each time (beginTitleEditAt) — there is no more
   persistent heading/band/re-roll-button markup to style. */
.maps__title-input {
  font-family: var(--font-heading);
  color: var(--color-heading);
  font-size: 1.3rem;
  background: var(--color-surface);
  border: 1px solid var(--color-accent);
  padding: 0.2rem 0.4rem;
  text-align: left;
}

/* --- Numbered legend panel — the right column of the workspace grid (dark
   on-theme annotation panel beside the map). --- */
.maps__legend {
  width: 20rem;
  max-width: 24rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.2);
  align-self: stretch;
  max-height: calc(100vh - 9rem);
  display: flex;
  flex-direction: column;
}

.maps__legend-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.6rem 0.85rem;
  border-bottom: 1px solid var(--color-border);
}

.maps__legend-title {
  margin: 0;
  font-family: var(--font-heading-ui);
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  color: var(--color-heading);
}

.maps__legend-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-family: var(--font-body);
  font-size: 0.8rem;
  color: var(--color-text-muted);
  cursor: pointer;
}

.maps__legend-list {
  list-style: none;
  margin: 0;
  padding: 0.4rem 0;
  overflow-y: auto;
}

.maps__legend-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: start;
  gap: 0.5rem;
  padding: 0.4rem 0.85rem;
  border-bottom: 1px solid rgba(46, 40, 32, 0.5);
}

.maps__legend-row:last-child {
  border-bottom: none;
}

.maps__legend-row:hover {
  background: var(--color-surface-hover);
}

/* The number is itself the "locate" button — circular badge echoing the on-map
   parchment disc, but in dark chrome so it belongs to the panel. */
.maps__legend-locate {
  appearance: none;
  background: var(--color-bg);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: 50%;
  width: 1.6rem;
  height: 1.6rem;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  font-weight: 700;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  transition:
    border-color var(--transition-fast, 150ms),
    color var(--transition-fast, 150ms);
}

.maps__legend-locate:hover,
.maps__legend-locate:focus-visible {
  border-color: var(--color-accent);
  color: var(--color-accent);
  outline: none;
}

.maps__legend-num {
  line-height: 1;
}

.maps__legend-text {
  font-family: var(--font-body);
  font-size: 0.88rem;
  line-height: 1.35;
  color: var(--color-text);
  align-self: center;
  /* The 1fr middle track of the legend grid: let it shrink (min-width:0) and
     break long room names / labels so they wrap here instead of widening the
     track and pushing the trailing locate column out. */
  min-width: 0;
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* Empty rooms read quieter so contents stand out. */
.maps__legend-row[data-kind="empty"] .maps__legend-text {
  color: var(--color-text-muted);
  font-style: italic;
}

.maps__legend-edited {
  color: var(--color-accent);
  font-size: 0.8rem;
  margin-left: 0.2rem;
}

/* #7 / batch-10 #2 — note mark in a legend row: a small page glyph echoing the on-canvas
   note badge. Ink for a regular note, accent for a GM-only note (matches the badge). It is
   a button (toggles the note-body reveal), so reset the button chrome to read as the inline
   glyph it replaced. */
.maps__legend-note {
  appearance: none;
  background: none;
  border: none;
  padding: 0;
  font-size: 0.8rem;
  line-height: 1;
  margin-left: 0.2rem;
  color: inherit;
  opacity: 0.75;
  cursor: pointer;
  border-radius: var(--radius-sm, 4px);
}

.maps__legend-note:hover {
  opacity: 1;
}

.maps__legend-note:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  opacity: 1;
}

.maps__legend-note--gm {
  color: var(--color-accent);
  opacity: 1;
}

/* batch-10 #2 — expanded note body, revealed inline below the row. Spans the full row
   width (the row is a 3-col grid; this is a 4th item) and reads as a quiet, indented
   block of the GM's own text. white-space:pre-wrap preserves the note's line breaks. */
.maps__legend-note-body {
  grid-column: 1 / -1;
  margin: 0.25rem 0 0.1rem 2.1rem;
  padding: 0.35rem 0.5rem;
  border-left: 2px solid var(--color-border);
  background: var(--color-bg);
  border-radius: var(--radius-sm, 4px);
  font-family: var(--font-body);
  font-size: 0.82rem;
  line-height: 1.4;
  color: var(--color-text-muted);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* #8 — the legend label is inline-renamable: it reads as editable on hover/focus
   (a quiet underline + text cursor) without shouting at rest. */
.maps__legend-text[data-legend-rename] {
  cursor: text;
  border-radius: var(--radius-sm, 4px);
}
.maps__legend-text[data-legend-rename]:hover {
  text-decoration: underline;
  text-decoration-style: dotted;
  text-underline-offset: 2px;
}
.maps__legend-text[data-legend-rename]:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}
/* #8 — the inline rename input replaces the label span in place; match the label's
   type scale so the row doesn't jump, and fill the available width. */
.maps__legend-rename-input {
  align-self: center;
  width: 100%;
  min-width: 0;
  font-family: var(--font-body);
  font-size: 0.88rem;
  line-height: 1.35;
  padding: 0.15rem 0.35rem;
}

/* WAVE B (#5) — pinned Entrance / Exit rows at the TOP of the legend. They sit
   above the numbered rooms with a heavier bottom rule so the "how you get in/out"
   reads as a header band distinct from the room list. */
.maps__legend-row--stair {
  background: rgba(100, 149, 237, 0.06);
}
.maps__legend-row--stair[data-stair="exit"] {
  background: rgba(28, 42, 58, 0.10);
}
.maps__legend-row--stair + .maps__legend-row:not(.maps__legend-row--stair) {
  border-top: 1px solid var(--color-border);
}
.maps__legend-row--stair .maps__legend-text strong {
  font-family: var(--font-heading-ui);
  font-weight: 600;
  letter-spacing: var(--ls-cinzel-caps, 0.03em);
}
/* The stair locate badge carries the up/down arrow; accent it so it pops. */
.maps__legend-locate--stair {
  font-size: 0.95rem;
  font-weight: 700;
}
.maps__legend-row--stair[data-stair="entrance"] .maps__legend-locate--stair {
  border-color: var(--color-accent);
  color: var(--color-accent);
}

/* WAVE B (#5) — Stairs Up/Down sub-toggle in the Draw group. Reuses the
   .maps__shape segmented frame (same dim-when-inactive behavior as room shape),
   but allows its longer labels to wrap to the rail width. */
.maps__stairtarget {
  flex-wrap: wrap;
}
.maps__stairtarget .map-seg {
  padding-left: 0.7rem;
  padding-right: 0.7rem;
}

/* #2 (Map Builder UI) — the 3-way Door sub-toggle (Regular | Secret | Bars) can
   exceed the rail width, so let its segments wrap and share the row evenly. */
.maps__doormode {
  flex-wrap: wrap;
}
.maps__doormode .map-seg {
  flex: 1;
  text-align: center;
  padding-left: 0.55rem;
  padding-right: 0.55rem;
}

/* #2b — Corridors subsection heading inside the legend list. */
.maps__legend-subhead {
  list-style: none;
  padding: 0.5rem 0.85rem 0.25rem;
  font-family: var(--font-heading-ui);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  color: var(--color-text-muted);
  border-top: 1px solid var(--color-border);
  margin-top: 0.25rem;
}

/* Corridor-trap locate badge: a square (not circular) badge so it reads as a
   passage hazard distinct from the round room badges. */
.maps__legend-locate--corridor {
  border-radius: 0;
  font-size: 0.66rem;
}

/* #4 — Terrain key rows: a color swatch (matching the on-map body color) + the
   terrain name. The swatch sits in the first (locate) grid column. */
.maps__legend-swatch {
  width: 1.4rem;
  height: 1.4rem;
  border: 1px solid var(--color-border);
  flex: none;
  align-self: center;
}
.maps__legend-row--terrain .maps__legend-text {
  align-self: center;
}

.maps__legend-edit {
  padding: 0.2rem 0.55rem;
  font-size: 0.7rem;
  flex: none;
  align-self: center;
}

/* --- Per-room edit: CENTERED modal (Refinement 5). Reuses the .action-modal
   scrim/card family (same as showConfirmModal / save-name modal) so it sits
   centered over the workspace with a backdrop. --- */
.maps-roomedit-modal__card {
  width: min(34rem, 94vw);
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  /* min-height:0 lets the inner scroll region (.action-modal__body) actually
     shrink below its content size inside this capped flex column; without it a
     flex item defaults to min-height:auto and refuses to shrink, so the body
     never scrolls and the foot (Linked items + Create Encounter) gets pushed
     past 88vh and off-screen. */
  min-height: 0;
}

/* The Kind/Icon/Legend block is the flexible scroll region: it takes the slack
   between the fixed head and the pinned actions+foot, and scrolls when tall. The
   head, actions, and foot do NOT shrink, so the Create/Edit Encounter button and
   the Linked-items controls stay reachable at the bottom on short viewports. */
.maps-roomedit-modal__card .action-modal__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

.maps-roomedit__head,
.maps-roomedit-modal__card .maps-roomedit__actions,
.maps-roomedit__foot {
  flex: 0 0 auto;
}

/* Belt-and-braces for very short viewports (~700px): if head + actions + foot
   together still exceed the card cap, the foot itself becomes scrollable so its
   Linked-items list and the Create/Edit Encounter button remain reachable rather
   than being clipped below the fold. */
.maps-roomedit__foot {
  max-height: 40vh;
  overflow-y: auto;
}

.maps-roomedit__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.maps-roomedit__head .action-modal__title {
  margin: 0;
}

.maps-roomedit__num {
  font-family: var(--font-heading-ui);
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  color: var(--color-heading);
}

.maps-roomedit__close {
  appearance: none;
  background: transparent;
  border: none;
  color: var(--color-text-muted);
  font-size: 1.3rem;
  line-height: 1;
  cursor: pointer;
  padding: 0 0.25rem;
}

.maps-roomedit__close:hover,
.maps-roomedit__close:focus-visible {
  /* Accent on hover to match the central .modal-close--icon standard. */
  color: var(--color-accent);
  outline: none;
}

.maps-roomedit__row {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.maps-roomedit__lbl {
  font-family: var(--font-heading-ui);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  color: var(--color-text-muted);
}

.maps-roomedit__row + .maps-roomedit__row {
  margin-top: 0.85rem;
}

.maps-roomedit__text {
  resize: vertical;
  font-family: var(--font-body);
  min-height: 3.5rem;
}

.maps-roomedit__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

/* #9 (2026-06-27) — optional room-description roll (building rooms only). Roll shows a
   preview the GM can SET (writes feature.text) or DISCARD. The preview reads as a
   quiet quoted blurb so it's clearly a SUGGESTION, not yet applied. */
.maps-roomedit__desc {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.maps-roomedit__desc-roll {
  align-self: flex-start;
}
.maps-roomedit__desc-preview {
  border-left: 2px solid var(--color-accent);
  padding: 0.4rem 0 0.4rem 0.65rem;
}
.maps-roomedit__desc-text {
  margin: 0 0 0.6rem;
  font-family: var(--font-body);
  font-style: italic;
  line-height: 1.5;
  color: var(--color-text);
}
.maps-roomedit__desc-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.maps-roomedit__desc-roll[disabled],
.maps-roomedit__desc-reroll[disabled] {
  opacity: 0.5;
  cursor: default;
}

.maps-roomedit__actions .btn,
.maps-roomedit__actions .btn--modal-confirm {
  flex: 1;
  padding: 0.45rem 0.6rem;
  font-size: 0.78rem;
}

.maps-roomedit__clear {
  border-color: var(--color-danger);
  color: var(--color-danger);
}

.maps-roomedit__clear:hover,
.maps-roomedit__clear:focus-visible {
  background: var(--color-danger);
  color: var(--color-text);
}

/* --- #2 — Export-choice modal. A centered .action-modal scrim/card offering two
   outputs: GM map (full) and Player map (clean layout + title only). Two large
   stacked choice buttons; the second is the quieter "clean" variant. --- */
.maps-export-modal__card {
  width: min(30rem, 94vw);
}

.maps-export-modal__intro {
  margin: 0 0 0.85rem;
  font-family: var(--font-body);
  color: var(--color-text-muted);
}

.maps-export-modal__choices {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.maps-export-choice {
  appearance: none;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  text-align: left;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-accent);
  padding: 0.7rem 0.85rem;
  cursor: pointer;
  transition:
    border-color var(--transition-fast, 150ms),
    background-color var(--transition-fast, 150ms);
}

.maps-export-choice:hover,
.maps-export-choice:focus-visible {
  border-color: var(--color-accent);
  background: var(--color-surface-hover);
  outline: none;
}

.maps-export-choice:focus-visible {
  box-shadow: 0 0 0 2px var(--color-accent);
}

.maps-export-choice__name {
  font-family: var(--font-heading-ui);
  font-size: 0.9rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  color: var(--color-heading);
}

.maps-export-choice__desc {
  font-family: var(--font-body);
  font-size: 0.85rem;
  line-height: 1.35;
  color: var(--color-text-muted);
}

/* --- #5 — Room hover tooltip. A small dark on-theme card floated near the
   cursor (position set inline by JS). Read-only now: holds the room's number +
   description text ONLY (no action buttons). pointer-events:none so a double-click
   to edit always reaches the canvas; editing moved to a double-click on the room. --- */
.maps-roomtip {
  position: fixed;
  z-index: 60;
  max-width: 18rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-accent);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.45);
  padding: 0.5rem 0.6rem 0.55rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  /* #5 — read-only tip (no buttons); never intercept the cursor so a double-click
     to edit always reaches the canvas underneath. */
  pointer-events: none;
}

.maps-roomtip[hidden] {
  display: none;
}

/* Empty / unstocked rooms read quieter, matching the legend convention. */
.maps-roomtip[data-kind="empty"] {
  border-left-color: var(--color-border);
}

.maps-roomtip__title {
  font-family: var(--font-heading-ui);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  color: var(--color-heading);
}

.maps-roomtip__body {
  font-family: var(--font-body);
  font-size: 0.84rem;
  line-height: 1.35;
  color: var(--color-text);
}

.maps-roomtip[data-kind="empty"] .maps-roomtip__body {
  color: var(--color-text-muted);
  font-style: italic;
}

.maps-roomtip__actions {
  display: flex;
  gap: 0.35rem;
  margin-top: 0.15rem;
}

.maps-roomtip__btn {
  appearance: none;
  flex: 1;
  background: var(--color-bg);
  color: var(--color-text-muted);
  border: 1px solid var(--color-border);
  padding: 0.3rem 0.5rem;
  font-family: var(--font-heading-ui);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  cursor: pointer;
  transition:
    color var(--transition-fast, 150ms),
    border-color var(--transition-fast, 150ms),
    background-color var(--transition-fast, 150ms);
}

.maps-roomtip__btn:hover,
.maps-roomtip__btn:focus-visible {
  color: var(--color-text);
  border-color: var(--color-accent);
  background: var(--color-surface-hover);
  outline: none;
}

/* --- Refinement 6 — icon + label kind picker (radiogroup of tiles) --- */
.maps-kindgrid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(5.2rem, 1fr));
  gap: 0.5rem;
  margin-top: 0.25rem;
}

.maps-kind {
  appearance: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  padding: 0.5rem 0.4rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
  cursor: pointer;
  transition:
    border-color var(--transition-fast, 150ms),
    color var(--transition-fast, 150ms),
    background-color var(--transition-fast, 150ms);
}

.maps-kind:hover {
  border-color: var(--color-accent);
  color: var(--color-text);
}

.maps-kind:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

.maps-kind.is-active {
  border-color: var(--color-accent);
  border-width: 2px;
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: inset 0 0 0 1px var(--color-accent);
}

/* The icon thumb sits on a small parchment disc so the ink stamps (drawn for a
   cream map page) stay legible against the dark modal chrome. */
.maps-kind__icon {
  width: 2.4rem;
  height: 2.4rem;
  object-fit: contain;
  background: var(--map-page-active, #e8dcc0);  /* live page color (#3) so stamps stay legible */
  border: 1px solid var(--color-border);
  padding: 0.15rem;
}

.maps-kind__icon--empty {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 1.1rem;
  color: #241a12;
}

.maps-kind__label {
  font-family: var(--font-heading-ui);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  text-align: center;
  line-height: 1.1;
}

/* --- #7 — Icon section in the room editor (current icon + Browse button) --- */
.maps-roomedit__iconpick {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}

.maps-roomedit__iconthumb {
  width: 2.6rem;
  height: 2.6rem;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--map-page-active, #e8dcc0);  /* live page color (#3) */
  border: 1px solid var(--color-border);
  padding: 0.15rem;
}

.maps-roomedit__iconthumb img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

.maps-roomedit__iconthumb--empty {
  font-family: var(--font-mono);
  font-size: 1.1rem;
  color: #241a12;
}

.maps-roomedit__iconname {
  font-family: var(--font-body);
  font-size: 0.82rem;
  color: var(--color-text-muted);
}

/* --- #7 — Symbol browser modal (categorized + searchable) --- */
.maps-symbols-modal__card {
  width: min(48rem, 96vw);
  max-height: 88vh;
  display: flex;
  flex-direction: column;
}

.maps-symbols__search {
  padding: 0 0 0.6rem;
}

.maps-symbols__searchinput {
  width: 100%;
}

.maps-symbols__body {
  overflow-y: auto;
  flex: 1;
}

.maps-symbols__group + .maps-symbols__group {
  margin-top: 1rem;
}

.maps-symbols__grouphead {
  margin: 0 0 0.5rem;
  font-family: var(--font-heading-ui);
  font-size: 0.74rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  color: var(--color-heading);
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
}

.maps-symbols__count {
  font-size: 0.66rem;
  color: var(--color-text-muted);
}

.maps-symbols__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(5rem, 1fr));
  gap: 0.5rem;
}

.maps-symbol {
  appearance: none;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3rem;
  padding: 0.4rem 0.3rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
  cursor: pointer;
  transition:
    border-color var(--transition-fast, 150ms),
    color var(--transition-fast, 150ms);
}

.maps-symbol:hover {
  border-color: var(--color-accent);
  color: var(--color-text);
}

.maps-symbol:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

.maps-symbol.is-active {
  border-color: var(--color-accent);
  border-width: 2px;
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: inset 0 0 0 1px var(--color-accent);
}

.maps-symbol__img {
  width: 2.6rem;
  height: 2.6rem;
  object-fit: contain;
  background: var(--map-page-active, #e8dcc0);  /* live page color (#3) */
  border: 1px solid var(--color-border);
  padding: 0.15rem;
}

.maps-symbol__name {
  font-family: var(--font-body);
  font-size: 0.66rem;
  line-height: 1.15;
  text-align: center;
  word-break: break-word;
}

/* --- Mid widths (<=1100px): JS auto-collapses the rail to icon-only so the
   canvas owns the middle. The legend narrows but stays beside the plate. --- */
@media (max-width: 1100px) {
  .maps__legend {
    width: 16rem;
  }
}

/* --- Mobile (<768px): the rail becomes a TOP DRAWER, the workspace stacks to a
   single column so the canvas owns the small screen, and the legend drops below
   the plate full-width. --- */
/* ==========================================================================
   MEI #4/#5 — Character tokens: Party rail group, Add Party modal, token menu,
   and the room-editor Create Encounter button. Tokens themselves are canvas-drawn
   (no DOM); these rules style the surrounding UI chrome in the dark --color-*
   tokens, matching the existing rail/modal family.
   ========================================================================== */

/* Party group count read-out under the Add Party button. */
.maps-rail__partycount {
  margin: 0.35rem 0 0;
  font-size: 0.75rem;
  color: var(--color-text-muted);
  font-family: var(--font-heading-ui);
  letter-spacing: var(--ls-cinzel-caps, 0.03em);
}

/* Add Party modal — saved-character list. Reuses .action-modal scrim/card. */
.maps-party__body {
  max-height: 60vh;
  overflow-y: auto;
}
.maps-party__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.maps-party-row {
  /* #7 — thumbnail / text / add-button. The text column stacks name + meta. */
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0.25rem 0.75rem;
  padding: 0.5rem 0.6rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm, 4px);
}
.maps-party-row__text {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-width: 0;   /* allow long names to ellipsis rather than push the button off */
}
.maps-party-row__name {
  font-weight: 600;
  color: var(--color-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.maps-party-row__meta {
  font-size: 0.8rem;
  color: var(--color-text-muted);
  text-transform: capitalize;
}
.maps-party-row__add {
  align-self: center;
  white-space: nowrap;
}

/* #7 — character profile thumbnail. The disc is always rendered as the base; an
   <img> (uploaded or class-mapped portrait) layers over it and hides itself on
   error so the disc shows through. Sized to one comfortable touch target. */
.maps-party-row__thumb {
  position: relative;
  width: 2.4rem;
  height: 2.4rem;
  flex-shrink: 0;
  border-radius: 50%;
  overflow: hidden;
  border: 1px solid var(--color-border);
}
.maps-party-row__disc {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--party-disc-color, var(--color-surface));
  color: #f3ead6;
  font-family: var(--font-heading-ui, serif);
  font-weight: 700;
  font-size: 1rem;
  line-height: 1;
}
.maps-party-row__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Token quick-menu popover (who + remove + upload portrait). Floats over the
   canvas near the click; pointer-driven, dismissed on outside click / Escape. */
.maps-tokenmenu {
  position: fixed;
  z-index: 1200;
  min-width: 12rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md, 6px);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
  padding: 0.6rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.maps-tokenmenu__head {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  border-bottom: 1px solid var(--color-border);
  padding-bottom: 0.4rem;
}
.maps-tokenmenu__name {
  font-weight: 600;
  color: var(--color-text);
}
.maps-tokenmenu__meta {
  font-size: 0.78rem;
  color: var(--color-text-muted);
  text-transform: capitalize;
}
.maps-tokenmenu__actions {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.maps-tokenmenu__remove {
  color: #d98b8b;
  border-color: rgba(155, 32, 32, 0.6);
}

/* Ken 2026-07-07 — selection popover for a dragged/resized title/compass/scale
   box (see bindDecorPointer/openDecorMenu in maps.js). Same floating-card look
   as .maps-tokenmenu so map chrome popovers read as one family. */
.maps-decormenu {
  position: fixed;
  z-index: 1200;
  min-width: 11rem;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md, 6px);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
  padding: 0.6rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.maps-decormenu__actions {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.maps-decormenu__btn {
  white-space: nowrap;
}
.maps-decormenu__crop {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  border-top: 1px solid var(--color-border);
  padding-top: 0.45rem;
}
.maps-decormenu__crop-row {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
.maps-decormenu__crop-label {
  flex: 1 1 auto;
  font-size: 0.78rem;
  color: var(--color-text-muted);
}
.maps-decormenu__crop-row .maps-decormenu__btn {
  width: 1.8rem;
  height: 1.8rem;
  padding: 0;
  line-height: 1;
}

/* Floating rename input for the canvas-anchored title box (Ken 2026-07-07) —
   the title no longer has a DOM heading to swap in place, so this is created
   fresh at the box's on-screen position each time (see beginTitleEditAt). */
.maps__title-input--floating {
  position: fixed;
  z-index: 1200;
  width: 16rem;
  max-width: calc(100vw - 1rem);
}

/* Room-editor Create Encounter button — a full-width footer affordance beneath
   the Apply/Re-roll/Clear actions, visually distinct (accent) as the cross-module
   jump. */
.maps-roomedit__foot {
  margin-top: 0.6rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--color-border);
}
.maps-roomedit__encounter {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  width: 100%;
  padding: 0.55rem 0.75rem;
  background: transparent;
  border: 1px solid var(--color-accent);
  border-radius: var(--radius-sm, 4px);
  color: var(--color-accent);
  font-family: var(--font-heading-ui);
  font-size: 0.85rem;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  cursor: pointer;
  transition: background-color var(--transition-fast, 150ms), color var(--transition-fast, 150ms);
}
.maps-roomedit__encounter:hover,
.maps-roomedit__encounter:focus-visible {
  background: var(--color-accent);
  color: var(--color-bg);
  outline: none;
}
.maps-roomedit__encounter .maps-ic {
  width: 18px;
  height: 18px;
}

/* MEI #2 — the room->encounter link, shown in the editor footer when the room has
   a saved encounter. A quiet label + the encounter's name, then a secondary
   button to jump straight to it. Sits above the Create/Edit Encounter action. */
.maps-roomedit__linked {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.5rem;
  margin-bottom: 0.55rem;
  font-family: var(--font-body);
  font-size: 0.82rem;
  color: var(--color-text);
}
.maps-roomedit__linked-lbl {
  font-family: var(--font-heading-ui);
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  color: var(--color-text-muted);
}
.maps-roomedit__linked-name {
  flex: 1 1 8rem;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--color-accent);
}
.maps-roomedit__open-enc {
  flex: 0 0 auto;
  padding: 0.3rem 0.6rem;
  font-size: 0.78rem;
}

/* --- Room-editor "Room notes" section (#8, 2026-06-27): a list of free-text notes
   for this room, each with an optional "GM only" badge + Edit/Delete, plus an add
   form (textarea + GM-use-only checkbox + Add). Owner-only by route. --- */
.maps-roomedit__notes {
  margin-bottom: 0.6rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--color-border);
}
.maps-roomedit__notes-head {
  margin-bottom: 0.45rem;
}
.maps-roomedit__notes-list {
  list-style: none;
  margin: 0 0 0.5rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.maps-roomedit__note {
  padding: 0.4rem 0.5rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm, 4px);
}
.maps-roomedit__note-top {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
}
.maps-roomedit__note-text {
  font-family: var(--font-body);
  font-size: 0.84rem;
  color: var(--color-text);
  white-space: pre-wrap;
  word-break: break-word;
}
/* "GM only" badge — an accent pill that flags a note as hidden from shared viewers. */
.maps-roomedit__note-badge {
  flex: 0 0 auto;
  font-family: var(--font-heading-ui);
  font-size: 0.6rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 0.1rem 0.35rem;
  border-radius: var(--radius-sm, 4px);
  background: var(--color-accent);
  color: var(--color-bg, #1b1b1b);
}
.maps-roomedit__note-acts {
  display: flex;
  gap: 0.35rem;
  margin-top: 0.35rem;
}
.maps-roomedit__note-act {
  padding: 0.2rem 0.5rem;
  font-size: 0.74rem;
}
.maps-roomedit__note-add {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.maps-roomedit__note-input {
  width: 100%;
  resize: vertical;
}
.maps-roomedit__note-ctrls {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex-wrap: wrap;
}
.maps-roomedit__note-gm {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-family: var(--font-body);
  font-size: 0.8rem;
  color: var(--color-text);
}
.maps-roomedit__note-addbtn {
  margin-left: auto;
  padding: 0.3rem 0.6rem;
  font-size: 0.78rem;
}
.maps-roomedit__notes-empty,
.maps-roomedit__notes-hint {
  font-family: var(--font-body);
  font-size: 0.8rem;
  color: var(--color-text-muted);
  margin: 0 0 0.4rem;
}

/* --- Room-editor "Linked items" section: a header (label + Link items button)
   and a wrapping list of chips, each with a remove control. Shown only on
   treasure / trapped-treasure / monster rooms (toggled by JS via [hidden]). --- */
.maps-roomedit__items {
  margin-bottom: 0.6rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid var(--color-border);
}
.maps-roomedit__items[hidden] {
  display: none;
}
.maps-roomedit__items-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.45rem;
}
.maps-roomedit__items-add {
  flex: 0 0 auto;
  padding: 0.3rem 0.6rem;
  font-size: 0.78rem;
}
.maps-roomedit__chips {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}
.maps-roomedit__chips-empty {
  list-style: none;
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--color-text-muted);
}
.maps-roomedit__chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  max-width: 100%;
  padding: 0.2rem 0.2rem 0.2rem 0.55rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm, 4px);
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--color-text);
}
.maps-roomedit__chip-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 12rem;
}
.maps-roomedit__chip-type,
.maps-roomedit__chip-magic {
  font-family: var(--font-heading-ui);
  font-size: 0.6rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  color: var(--color-text-muted);
}
.maps-roomedit__chip-magic {
  color: var(--color-accent);
}
.maps-roomedit__chip-remove {
  appearance: none;
  background: transparent;
  border: none;
  color: var(--color-text-muted);
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
  padding: 0 0.25rem;
}
.maps-roomedit__chip-remove:hover,
.maps-roomedit__chip-remove:focus-visible {
  color: var(--color-danger);
  outline: none;
}

/* --- Item picker modal: filterable, multi-select item-selection list. Reuses the
   .action-modal scrim/card; the filter bar mirrors a compact Treasure filter set
   (search + Type + Source + Magical toggle); rows show icon + name + tags. --- */
.maps-itempick-modal__card {
  width: min(40rem, 96vw);
  max-height: 88vh;
  display: flex;
  flex-direction: column;
}
.maps-itempick__filters {
  padding: 0 0 0.6rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.maps-itempick__search {
  width: 100%;
}
.maps-itempick__selects {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.6rem;
}
.maps-itempick__field {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  flex: 1 1 9rem;
  min-width: 8rem;
}
.maps-itempick__field-lbl {
  font-family: var(--font-heading-ui);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  color: var(--color-text-muted);
}
.maps-itempick__toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-family: var(--font-body);
  font-size: 0.8rem;
  color: var(--color-text);
  cursor: pointer;
  flex: 0 0 auto;
  padding-bottom: 0.35rem;
}
.maps-itempick__body {
  overflow-y: auto;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.maps-itempick__row {
  appearance: none;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  width: 100%;
  text-align: left;
  padding: 0.4rem 0.55rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  color: var(--color-text);
  cursor: pointer;
  transition:
    border-color var(--transition-fast, 150ms),
    background-color var(--transition-fast, 150ms);
}
.maps-itempick__row:hover {
  border-color: var(--color-accent);
}
.maps-itempick__row:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}
.maps-itempick__row.is-active {
  border-color: var(--color-accent);
  background: var(--color-surface);
  box-shadow: inset 0 0 0 1px var(--color-accent);
}
.maps-itempick__row.is-linked {
  opacity: 0.55;
  cursor: default;
}
.maps-itempick__media {
  flex: 0 0 auto;
  display: inline-flex;
}
.maps-itempick__img {
  width: 2rem;
  height: 2rem;
  object-fit: contain;
  background: var(--map-page-active, #e8dcc0);
  border: 1px solid var(--color-border);
  padding: 0.1rem;
}
.maps-itempick__img--empty {
  background: var(--color-surface);
}
.maps-itempick__info {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.maps-itempick__name {
  font-family: var(--font-body);
  font-size: 0.85rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.maps-itempick__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
}
.maps-itempick__tag {
  font-family: var(--font-heading-ui);
  font-size: 0.6rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  color: var(--color-text-muted);
}
.maps-itempick__tag--magic {
  color: var(--color-accent);
}
.maps-itempick__src {
  font-family: var(--font-body);
  font-size: 0.7rem;
  color: var(--color-text-muted);
}
.maps-itempick__check {
  flex: 0 0 auto;
  min-width: 1.4rem;
  text-align: right;
  font-family: var(--font-heading-ui);
  font-size: 0.7rem;
  color: var(--color-accent);
}
.maps-itempick__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--color-border);
}
.maps-itempick__selcount {
  flex: 1 1 auto;
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--color-text-muted);
}
.maps-itempick__actions .btn,
.maps-itempick__actions .btn--modal-confirm {
  flex: 0 0 auto;
  padding: 0.45rem 0.8rem;
  font-size: 0.78rem;
}

/* MEI #2 — the "Linked encounter" note in the read-only hover tooltip. A small
   accent-tinted caps line under the room description so the link reads on hover. */
.maps-roomtip__linked {
  font-family: var(--font-heading-ui);
  font-size: 0.66rem;
  text-transform: uppercase;
  letter-spacing: var(--ls-cinzel-caps, 0.04em);
  color: var(--color-accent);
}
.maps-roomtip__linked[hidden] {
  display: none;
}
.maps-roomtip__linked::before {
  content: "\2756"; /* small lozenge — echoes the on-canvas accent dot */
  margin-right: 0.35rem;
}

@media (max-width: 768px) {
  .maps__workspace {
    grid-template-columns: 1fr;
  }
  .maps__rail {
    position: static;
    width: 100% !important;          /* override the collapsed inline width */
    max-height: none;
    flex-direction: column;
    border-left: 1px solid var(--color-border);
  }
  /* On mobile the rail reads as a horizontal-wrapping toolbox drawer rather than
     a tall column; let the groups flow across. */
  .maps-rail__scroll {
    flex-direction: row;
    flex-wrap: wrap;
    gap: 0.6rem;
  }
  .maps-rail__group {
    border-bottom: none;
    border-right: 1px solid rgba(46, 40, 32, 0.5);
    padding: 0 0.6rem;
    min-width: 8rem;
  }
  .maps-rail__group--foot {
    margin-top: 0;
    border-top: none;
    width: 100%;
  }
  .maps-rail__group--foot .maps-rail__list {
    flex-direction: row;
    flex-wrap: wrap;
  }
  .maps-rail__filebtn {
    flex: 1;
    min-width: 6rem;
  }
  .maps__legend {
    width: 100%;
    max-width: none;
    max-height: 50vh;
  }
  .maps-saved-row {
    flex-direction: column;
    align-items: flex-start;
  }
}

/* ============================================================
   Admin "View as User" — read-only map viewer (Phase 2.6+)
   body.maps-readonly hides the editing rail / status so the admin sees the
   rendered map (plate + canvas + legend) only. Pan/zoom remain available
   (canvas-driven, local-only). Applies to both dungeon + world maps (world
   reuses .maps__rail / .maps__workspace). The dungeon title's re-roll control
   is on-canvas now (Ken 2026-07-07 — see maps.js's decor selection popover),
   so there is no more standalone .maps__title-reroll element to hide here.
   ============================================================ */
body.maps-readonly .maps__rail,
body.maps-readonly .maps__status,
body.maps-readonly [data-map-worldback] {
  display: none !important;
}

/* With the rail hidden (display:none drops it from the grid), keep the OWNER
   editor's arrangement minus the rail: the map plate plus the legend to its
   RIGHT. Two columns — plate (flexible) | legend (intrinsic) — so the canvas
   shrinks to share the row with the legend exactly as the owner sees it, rather
   than going full-bleed with the legend pushed away. (Under 768px maps.css
   already stacks .maps__workspace to a single column, so this only governs the
   wide layout.) */
body.maps-readonly .maps__workspace {
  grid-template-columns: minmax(0, 1fr) auto;
}
