/* 2×2 grid: axis containers stay in their own cells so labels never rotate. */
.board-wrap {
  position: relative; flex-shrink: 0;
  display: grid;
  grid-template-columns: var(--board-label-width, 20px) auto;
  grid-template-rows:    auto var(--board-label-width, 20px);
  gap: 4px;
  padding: 10px;
  box-sizing: border-box;
  --board-ground: var(--bg-1);
  background:
    radial-gradient(ellipse at center, rgba(184, 155, 95, 0.06), transparent 62%),
    var(--board-ground);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-l);
  box-shadow: inset 0 0 40px rgba(0, 0, 0, 0.3);
}
/* Play screen: card closes a fused column, so only bottom corners round. */
.game-screen .board-wrap { border-radius: 0 0 var(--radius-l) var(--radius-l); }
.board-grid {
  display: grid;
  grid-template-columns: repeat(var(--board-size, 16), var(--cell-size));
  grid-template-rows:    repeat(var(--board-size, 16), var(--cell-size));
  border-radius: 4px;
  border: 1px solid var(--bronze-deep);
  background: #2a2419;
  box-shadow:
    0 0 0 4px var(--bg-1),
    0 0 0 5px var(--bronze-deep),
    0 10px 40px rgba(0, 0, 0, 0.6),
    inset 0 0 0 1px rgba(255, 255, 255, 0.05);
  grid-column: 2; grid-row: 1;
  /* Containing block for overlay layers; without it they resolve to .board-wrap and slide left by axis-gutter. */
  position: relative;
}
/* align-self: start prevents stretching that would mis-pivot the rotate transform. */
.board-axis-left   { grid-column: 1; grid-row: 1; display: flex; flex-direction: column; align-self: start; }
.board-axis-bottom { grid-column: 2; grid-row: 2; display: flex; flex-direction: row;   align-self: start; }
/* justify-self does the same for the INLINE axis, and it is load-bearing for the overlays: the
   cells are `repeat(n, --cell-size)` and start-aligned either way, so a stretched box moves no
   cell — but every overlay layer is 100% of that box, and each one then maps the board across a
   width the board does not occupy. Wherever .board-wrap is wider than its tracks (the ≤640px rule
   sizes cells off the viewport) trail and commentary arrows drifted progressively, up to half a
   cell at the far edge, and rotate() pivoted off the board's true centre. */
.board-grid        { align-self: start; justify-self: start; }

/* data-rotation rotates .board-grid; cell contents counter-rotate to stay readable. */
/* The review board (.md-rp-frame > .md-rp-board) rides the SAME rotation as the live board
   and the same rotate control — a selector list, not a second set of rules. Both keep their
   coordinate gutters OUTSIDE the rotating element, so the axes stay upright. */
.board-wrap[data-rotation="90"]  > .board-grid,
.md-rp-frame[data-rotation="90"]  > .md-rp-board { transform: rotate(90deg); }
.board-wrap[data-rotation="180"] > .board-grid,
.md-rp-frame[data-rotation="180"] > .md-rp-board { transform: rotate(180deg); }
.board-wrap[data-rotation="270"] > .board-grid,
.md-rp-frame[data-rotation="270"] > .md-rp-board { transform: rotate(270deg); }

/* --piece-counter-rotate lets transient transforms compose with the rotation instead of overwriting it. */
.board-wrap[data-rotation="90"],  .md-rp-frame[data-rotation="90"]  { --piece-counter-rotate: -90deg; }
.board-wrap[data-rotation="180"], .md-rp-frame[data-rotation="180"] { --piece-counter-rotate: -180deg; }
.board-wrap[data-rotation="270"], .md-rp-frame[data-rotation="270"] { --piece-counter-rotate: -270deg; }

.board-wrap .piece,
.board-wrap .cell-cost,
.board-wrap .cell-battle,
.board-wrap .marquee-layer { transform: rotate(var(--piece-counter-rotate, 0deg)); }

.board-wrap .cell-tooltip { transform: translateX(-50%) rotate(var(--piece-counter-rotate, 0deg)); }

.rotate-board-control {
  position: relative;
  display: flex; justify-content: flex-end;
  margin: 0 0 2px;
}
.btn-rotate-board,
.btn-md-analysis {
  width: 36px; height: 36px;
  padding: 0;
  background: var(--bg-card);
  border: 1px solid color-mix(in srgb, var(--accent-gold) 50%, var(--border-color));
  border-radius: var(--radius-m);
  color: var(--accent-gold);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent-gold) 18%, transparent);
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
/* Rotate is chrome, not an accent: it wears the neutral ink of the captions beside it, on every
   board that carries it — match, training, scenario lab, the setup phases and the review frame.
   Gold stays with the controls that mean something (the general's marker, .btn-md-analysis). */
.btn-rotate-board {
  color: var(--ink-3);
  border-color: var(--border-strong);
  box-shadow: none;
}
.btn-rotate-board:hover {
  color: var(--ink);
  background: var(--bg-hover);
  border-color: var(--bronze);
  transform: translateY(-1px);
}
.btn-md-analysis:hover {
  background: color-mix(in srgb, var(--accent-gold) 12%, var(--bg-hover));
  border-color: var(--accent-gold);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent-gold) 35%, transparent);
  transform: translateY(-1px);
}
/* Focus ring — keyboard affordance, matches layout.css / buttons.css. */
.btn-rotate-board:focus-visible,
.btn-md-analysis:focus-visible,
.rotate-opt:focus-visible {
  outline: 2px solid var(--bronze-2); outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  .btn-rotate-board:hover, .btn-md-analysis:hover { transform: none; }
}
.btn-rotate-board svg,
.btn-md-analysis svg { width: 18px; height: 18px; }

.rotate-popup {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  display: none;
  flex-direction: column;
  min-width: 180px;
  background: var(--bg-panel);
  border: 1px solid var(--border-color);
  border-radius: var(--radius-m);
  padding: 4px;
  z-index: 60;
  box-shadow: var(--shadow-2);
}
.rotate-popup.is-open { display: flex; }
.rotate-opt {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-s);
  color: var(--text-primary);
  font: inherit; font-size: 0.85rem;
  text-align: left;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease;
}
.rotate-opt:hover { background: var(--bg-hover); color: var(--accent-gold); }
.rotate-opt.is-active { background: color-mix(in srgb, var(--accent-gold) 14%, transparent); color: var(--accent-gold); }
.rotate-opt-icon { width: 18px; text-align: center; font-size: 1rem; }
.rotate-opt-label { flex: 1; }

.coord {
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-size: 0.66rem; letter-spacing: 0.06em;
  color: var(--ink-3);
  width: var(--cell-size); height: var(--cell-size);
  user-select: none;
}
.coord-col { height: var(--board-label-width, 20px); }
.coord-row { width: var(--board-label-width, 20px); }

.cell {
  width: var(--cell-size); height: var(--cell-size);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; position: relative; user-select: none;
  /* Hover tint must be a background LAYER, never `filter`: a filter promotes the cell to its
     own compositor layer, which re-rasterises it at a different subpixel snap. */
  --cell-tint: transparent;
  /* Speckle via background-image (paints above background-color) keeps ::before/::after free for terrain blend + target rings. */
  background-image:
    linear-gradient(var(--cell-tint), var(--cell-tint)),
    radial-gradient(rgba(255, 255, 255, 0.024) 1px, transparent 1px),
    radial-gradient(rgba(0, 0, 0, 0.024) 1px, transparent 1px);
  background-size: auto, 4px 4px, 6px 6px;
  background-position: 0 0, 0 0, 2px 2px;
}
/* No transition: a custom property needs @property, above the Safari 16.2 floor. */
.cell:hover { --cell-tint: rgba(255, 255, 255, 0.11); }
/* Hill glyph counter-rotates to stay upright; anchor moves to whichever pre-rotation corner lands at visual bottom-right (90:TR 180:TL 270:BL). */
.cell-terrain-sym {
  position: absolute; bottom: 1px; right: 2px;
  color: rgba(0, 0, 0, 0.22); font-weight: 700;
  font-size: calc(var(--cell-size) * 0.14); line-height: 1;
  pointer-events: none;
  transform: rotate(var(--piece-counter-rotate, 0deg));
}
.board-wrap[data-rotation="90"]  .cell-terrain-sym { bottom: auto; top: 1px; right: 2px; }
.board-wrap[data-rotation="180"] .cell-terrain-sym { bottom: auto; right: auto; top: 1px; left: 2px; }
.board-wrap[data-rotation="270"] .cell-terrain-sym { right: auto; bottom: 1px; left: 2px; }
.cell-plain  { background-color: var(--sq-plain); }
.cell-hill   { background-color: var(--sq-hill); }
.cell-forest { background-color: var(--sq-forest); }
.cell-river  { background-color: var(--sq-river); }
.cell-desert { background-color: var(--sq-desert); }
.cell-trans::before {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background:
    linear-gradient(to bottom, var(--nt, transparent) 0%, var(--nt, transparent) var(--nd-t, 0%), transparent var(--nd-t, 0%)),
    linear-gradient(to top, var(--nb, transparent) 0%, var(--nb, transparent) var(--nd-b, 0%), transparent var(--nd-b, 0%)),
    linear-gradient(to right, var(--nl, transparent) 0%, var(--nl, transparent) var(--nd-l, 0%), transparent var(--nd-l, 0%)),
    linear-gradient(to left, var(--nr, transparent) 0%, var(--nr, transparent) var(--nd-r, 0%), transparent var(--nd-r, 0%));
}
.cell-tooltip {
  opacity: 0; visibility: hidden; position: absolute; bottom: calc(100% + 4px); left: 50%;
  transform: translateX(-50%); background: rgba(30,28,24,0.95);
  color: var(--text-primary); padding: 5px 9px; border-radius: 5px;
  font-size: 0.7rem; white-space: nowrap; z-index: 50;
  pointer-events: none; line-height: 1.4;
  border: 1px solid rgba(255,255,255,0.1);
  /* Hiding starts at once and takes 0.5s. No DELAY here: the open delay belongs to the open state
     alone — on this rule it applied in both directions, so dismissing took as long as summoning.
     visibility flips at the END of the fade, or the tooltip would vanish before it finished. */
  transition: opacity 0.5s ease 0s, visibility 0s linear 0.5s;
}
/* The lift is what lets the tooltip overflow its neighbours. It has to OUTLAST the 0.5s fade-out,
   or the tooltip paints under adjacent cells for the whole of it — which is why the delay is on
   the un-hover side only, and 0s going in. */
.cell { transition: z-index 0s linear 0.5s; }
.cell:hover { z-index: 40 !important; transition: z-index 0s linear 0s; }
.cell:hover .cell-tooltip {
  opacity: 1; visibility: visible;
  transition: opacity 0.2s ease var(--tooltip-delay), visibility 0s linear var(--tooltip-delay);
}
.board-grid { overflow: visible; }
.board-wrap { overflow: visible; }
.tooltip-sep { border: none; border-top: 1px solid rgba(255,255,255,0.2); margin: 3px 0; }
.grid-always .cell { outline: 1px solid rgba(0,0,0,0.08); outline-offset: -1px; }
.grid-on-move.grid-active .cell { outline: 1px solid rgba(0,0,0,0.08); outline-offset: -1px; }
.settings-row {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(0, 1fr));
  gap: 6px; margin-top: 4px; padding: 4px 0;
}
.settings-select {
  background: var(--bg-panel); color: var(--text-primary);
  border: 1px solid var(--border-color); border-radius: 4px;
  padding: 4px 6px; font-size: 0.72rem; cursor: pointer;
  width: 100%; min-height: 26px;
}

/* Valid-move gold disc behind the cost figure; --gold so it follows the theme re-pin. */
.cell-move-dot::after {
  content: ''; position: absolute; inset: 18%;
  border-radius: 50%;
  background: color-mix(in srgb, var(--gold) 35%, transparent);
  pointer-events: none;
}

.cell-selected,
.cell-selected-enemy { z-index: 3; }
.cell-selected { box-shadow: inset 0 0 0 3px var(--gold); }
/* Halo tied to --gold so it reads as lit bronze and follows the theme. */
.cell-selected .piece,
.cell-selected-enemy .piece {
  box-shadow:
    0 0 10px 4px color-mix(in srgb, var(--gold) 70%, transparent),
    0 0 20px 8px color-mix(in srgb, var(--gold) 30%, transparent);
  animation: piece-glow 1.5s ease-in-out infinite alternate;
}

.cell-enemy-range {
  background-image: repeating-linear-gradient(45deg, rgba(0,0,0,0.35) 0, rgba(0,0,0,0.35) 2px, transparent 2px, transparent 6px) !important;
  box-shadow: inset 0 0 0 2px rgba(0,0,0,0.45);
}

.cell-cost {
  position: absolute; z-index: 3; font-size: calc(var(--cell-size) * 0.4);
  font-weight: 700; color: var(--text-light); text-shadow: 0 1px 3px rgba(0,0,0,0.7);
  pointer-events: none;
}
/* Sits ON the target's disc, so it carries its OWN chip instead of depending on the army
   underneath — a fixed ink measured 1.88:1 against --p1, and following --pc-ink instead just
   made it read as part of the glyph. --ink/--bg-0 are re-pinned dark inside .board-wrap in
   both themes, so the parchment chip and its near-black digit look the same everywhere. */
/* px floors, not bare ratios: --cell-size goes down to 14. The floors bind below cell 36 only,
   so they are the phone's badge size and nothing else — a 20px tile is the case they set. */
.cell-cost-attack {
  bottom: -1px; right: 0;
  display: flex; align-items: center; justify-content: center; box-sizing: border-box;
  min-width: max(11px, calc(var(--cell-size) * 0.36));
  height: max(11px, calc(var(--cell-size) * 0.36));
  padding: 0 max(1px, calc(var(--cell-size) * 0.05));
  border-radius: max(6px, calc(var(--cell-size) * 0.18));
  font-size: max(9px, calc(var(--cell-size) * 0.3));
  background: var(--ink);
  color: var(--bg-0);
  border: 1px solid color-mix(in srgb, var(--bg-0) 45%, transparent);
  text-shadow: none;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}

/* Screen corner at every rotation: the GRID rotates and seat 1 defaults to 180, so a fixed
   grid corner moves with the seat. Selecting on [data-rotation] covers .board-wrap and the
   review board. Cost bottom-right, battle mark top-right — never the same corner. */
[data-rotation="90"]  .cell-cost-attack { top: -1px;    right: 0; bottom: auto; left: auto;  }
[data-rotation="180"] .cell-cost-attack { top: -1px;    left: 0;  bottom: auto; right: auto; }
[data-rotation="270"] .cell-cost-attack { bottom: -1px; left: 0;  top: auto;    right: auto; }

[data-rotation="90"]  .cell-battle-corner { top: -1px;    left: 0;  bottom: auto; right: auto; }
[data-rotation="180"] .cell-battle-corner { bottom: -1px; left: 0;  top: auto;    right: auto; }
[data-rotation="270"] .cell-battle-corner { bottom: -1px; right: 0; top: auto;    left: auto;  }
.trail-arrows-container { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 3; }
.trail-svg,
.commentary-arrows-svg,
.group-arrows-svg { position: absolute; top: 0; left: 0; pointer-events: none; }

.commentary-arrows-layer { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 8; }

.cell-battle {
  position: absolute; z-index: 4; font-size: calc(var(--cell-size) * 0.55);
  pointer-events: auto; cursor: help; opacity: 1;
  filter: brightness(0);
}
.cell-battle-corner { font-size: calc(var(--cell-size) * 0.3); top: -1px; right: 0; z-index: 5; }
/* "Confirm actions": a tick ON the destination square. The modal it replaced covered the very
   cell being confirmed. Above the piece so it is always clickable; any click off it cancels. */
.confirm-tick {
  position: absolute; inset: 0; z-index: 9;
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%; padding: 0;
  border: 2px solid var(--laurel-2); border-radius: 4px;
  background: color-mix(in srgb, var(--laurel) 55%, transparent);
  color: #fff; font-size: calc(var(--cell-size) * 0.6); line-height: 1;
  cursor: pointer;
  transform: rotate(var(--piece-counter-rotate, 0deg));
}
.confirm-tick:hover { background: color-mix(in srgb, var(--laurel) 78%, transparent); }
.confirm-tick:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }

/* The two units a pending prompt involves, marked identically: ONE danger colour for the pair, so
   the board says "this exchange" rather than asking the reader to decode two hues. Which unit is
   which comes from the pieces' own livery and the cell tooltip, both of which name it outright.
   The two class names are NOT interchangeable even though this rule treats them alike:
   `_responseRole` returns 'anchor' vs 'source' and only the anchor's tooltip carries the costs,
   so collapsing them into one class drops the defender's numbers.
   Inset rings, not pseudo-elements: ::before and ::after are already the terrain blend and the
   target rings. The ring goes fully OFF at the half cycle — fading to 40% was imperceptible at
   this width — at 1.4s, far under the 3Hz photosensitivity floor, and reduced-motion opts out. */
.cell-response-anchor,
.cell-response-source {
  z-index: 2;
  --resp-ring-w: calc(var(--cell-size) * 0.09);
  box-shadow: inset 0 0 0 var(--resp-ring-w) var(--porphyry);
}
@media (prefers-reduced-motion: no-preference) {
  .cell-response-anchor,
  .cell-response-source { animation: cell-response-pulse 1.4s ease-in-out infinite; }
}
@keyframes cell-response-pulse {
  50% { box-shadow: inset 0 0 0 var(--resp-ring-w) transparent; }
}

.cell-attack-ring::after,
.cell-blocked-target::after {
  content: ''; position: absolute;
  width: 85%; height: 85%;
  border-radius: 50%; pointer-events: none; z-index: 1;
}
.cell-attack-ring::after {
  border: calc(var(--cell-size) * 0.13) solid rgba(0, 0, 0, 0.4);
}
.cell-blocked-target::after {
  border: calc(var(--cell-size) * 0.1) dashed rgba(180, 60, 60, 0.55);
}

/* Deploy zones: per-cell --dz-inner ring + per-side --dz-edge-* perimeter; own/enemy share the edge vars, only colour differs. */
.cell-deploy-zone,
.cell-deploy-skirmish,
.cell-deploy-edge-top,
.cell-deploy-edge-bottom,
.cell-deploy-edge-left,
.cell-deploy-edge-right,
.cell-enemy-deploy-edge-top,
.cell-enemy-deploy-edge-bottom,
.cell-enemy-deploy-edge-left,
.cell-enemy-deploy-edge-right {
  --dz-edge-top:    0 0 0 0 transparent;
  --dz-edge-bottom: 0 0 0 0 transparent;
  --dz-edge-left:   0 0 0 0 transparent;
  --dz-edge-right:  0 0 0 0 transparent;
  --dz-inner:       0 0 0 0 transparent;
  box-shadow:
    var(--dz-edge-top),
    var(--dz-edge-bottom),
    var(--dz-edge-left),
    var(--dz-edge-right),
    var(--dz-inner);
}
.cell-deploy-zone,
.cell-enemy-deploy-zone {
  background-image: repeating-linear-gradient(45deg, rgba(0,0,0,0.2) 0, rgba(0,0,0,0.2) 2px, transparent 2px, transparent 6px) !important;
}
/* Suppress terrain-blend pseudo on deploy cells — ::before would hide stripes. */
.cell-deploy-zone.cell-trans::before,
.cell-deploy-skirmish.cell-trans::before,
.cell-enemy-deploy-zone.cell-trans::before {
  display: none;
}
.cell-deploy-skirmish {
  background-image: repeating-linear-gradient(45deg, rgba(255, 184, 50, 0.4) 0, rgba(255, 184, 50, 0.4) 2.5px, transparent 2.5px, transparent 6px) !important;
}
.cell-deploy-zone     { --dz-inner: inset 0 0 0 2px rgba(129, 182, 76, 0.8); }
.cell-deploy-skirmish { --dz-inner: inset 0 0 0 3px rgba(255, 184, 50, 0.95); }
.cell-deploy-edge-top    { --dz-edge-top:    inset 0  3px 0 0 rgba(0, 0, 0, 0.55); }
.cell-deploy-edge-bottom { --dz-edge-bottom: inset 0 -3px 0 0 rgba(0, 0, 0, 0.55); }
.cell-deploy-edge-left   { --dz-edge-left:   inset  3px 0 0 0 rgba(0, 0, 0, 0.55); }
.cell-deploy-edge-right  { --dz-edge-right:  inset -3px 0 0 0 rgba(0, 0, 0, 0.55); }
.cell-enemy-deploy-edge-top    { --dz-edge-top:    inset 0  3px 0 0 rgba(220, 60, 60, 0.75); }
.cell-enemy-deploy-edge-bottom { --dz-edge-bottom: inset 0 -3px 0 0 rgba(220, 60, 60, 0.75); }
.cell-enemy-deploy-edge-left   { --dz-edge-left:   inset  3px 0 0 0 rgba(220, 60, 60, 0.75); }
.cell-enemy-deploy-edge-right  { --dz-edge-right:  inset -3px 0 0 0 rgba(220, 60, 60, 0.75); }

.map-select-layout {
  display: flex;
  flex-direction: column;
  gap: 20px;
  width: 100%;
}
.map-select-prompt {
  text-align: center;
  color: var(--text-primary);
  font-size: 1rem;
  line-height: 1.4;
  margin: 0;
}
.map-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 16px;
  width: 100%;
  max-width: 1039px;
  margin: 0 auto;
}
@media (max-width: 900px) {
  .map-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
}
@media (max-width: 640px) {
  .map-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
}
.map-card {
  position: relative;
  background: var(--bg-1);
  border: 2px solid var(--border);
  border-radius: var(--radius-l);
  padding: 14px;
  cursor: pointer;
  transition: border-color 0.2s, transform 0.15s, box-shadow 0.2s;
  text-align: center;
  box-sizing: border-box;
}
.map-card:hover { border-color: var(--bronze); transform: translateY(-2px); }
.map-card.map-card-selected {
  border-color: var(--bronze-2);
  background: color-mix(in srgb, var(--bronze) 8%, var(--bg-2));
  box-shadow: 0 0 0 1px var(--bronze-2), 0 0 18px color-mix(in srgb, var(--bronze) 25%, transparent);
}
.map-card h3 { color: var(--text-primary); font-size: 0.95rem; margin-bottom: 4px; }
.map-card p { font-size: 0.78rem; color: var(--text-muted); margin-bottom: 10px; }

.map-card-featured {
  border-color: color-mix(in srgb, var(--accent-gold) 40%, transparent);
  background: var(--bg-card);
}
.map-card-featured:hover { border-color: var(--accent-gold); }
.map-card-featured.map-card-selected {
  border-color: var(--accent-gold);
  box-shadow: 0 0 0 1px var(--accent-gold),
              0 0 18px color-mix(in srgb, var(--accent-gold) 25%, transparent);
}
.map-card-badge {
  position: absolute;
  top: 6px; left: 6px;
  width: 24px; height: 24px;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent-gold);
  border-radius: 50%;
  font-size: 0.9rem;
  line-height: 1;
  pointer-events: none;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
}
.map-card-random-controls {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 8px;
}
.map-card-random-controls .btn-sm { width: 100%; padding: 4px 8px; font-size: 0.74rem; }

.map-select-footer {
  display: flex;
  gap: 12px;
  align-items: center;
  justify-content: center;
  margin-top: 4px;
}
.map-select-footer > .btn { min-width: 9em; }

.mini-grid { display: grid; grid-template-columns: repeat(var(--board-size, 16), 1fr); width: 100%; aspect-ratio: 1; border-radius: 3px; overflow: hidden; }
/* box-shadow halos fill 1px pixel-rounding gaps between cells. */
.mini-cell {
  aspect-ratio: 1;
  box-shadow: 0 0 0 1px currentColor;
}

@media (max-width: 640px) {
  /* Keep overflow:visible — board is sized to fit; overflow-x:auto would promote overflow-y to auto and show BOTH scrollbars. */
  .board-wrap { overflow: visible; max-width: 100%; padding: 6px; }
}

.terrain-legend { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 8px; font-size: 0.68rem; color: var(--text-muted); margin-top: 6px; }
.tl-item { display: flex; align-items: center; gap: 4px; }