/* SVG Framed Template
 * - Keeps the same Template 3 mechanics
 * - Presents the map in a centred square window, with a white "frame" around it
 */


.map-frame {
	position: fixed;
	inset: 0;
	background: #fff;
	display: grid;
	/* Anchor the stage to the top of the frame rather than vertically
	 * centring it (the old `place-items: center`). Centring forced the
	 * empty space *above* and *below* the map to be symmetric, which
	 * meant every pixel we added to the top reservation also ate a
	 * pixel at the bottom — the map couldn't grow into the empty space
	 * beneath it. Anchoring to the top with `align-items: start` lets
	 * the top and bottom reservations be tuned independently: the top
	 * reservation is `pad + yearbar-h/2` (= where the people panel's
	 * top and the map's top both sit), and the bottom reservation is
	 * just `pad` (= where the people panel's bottom and the map's
	 * bottom both sit). The map now fills the full remaining height. */
	align-items: start;
	justify-items: center;
	/* Design tokens hoisted to the frame itself so both the viewport
	 * (which consumes them via `--pm-frame-size`) and the people panel
	 * (which uses them to position itself so its top/bottom edges track
	 * the map viewport — see `.map-frame__people` below) can reference
	 * the same values. `--pm-yearbar-h` used to live on
	 * `.map-frame__viewport` only; hoisted in v0.6.17.
	 *
	 * `--pm-yearbar-h: 120px` isn't the yearbar's literal height (which
	 * is ~82-90px) — it's the *vertical reservation* above the map. The
	 * extra ~30px above the actual yearbar footprint becomes breathing
	 * room between the yearbar's bottom edge and the map's top edge.
	 * The map's top lands at `pad + yearbar-h/2` from the screen top,
	 * matching the people panel's top. Bumping this token shifts both
	 * down by half the increase (they derive from the same expression)
	 * without affecting the bottom anchor, so it's the single knob for
	 * "how high up does the stage sit". */
	--pm-frame-pad: clamp(12px, 2.2vw, 22px);
	--pm-yearbar-h: 120px;
	/* Asymmetric padding: big reservation on top (pad + yearbar-h/2) so
	 * the stage sits below the yearbar with breathing room; small
	 * reservation on the bottom (just pad) so the map can grow into the
	 * available space. Horizontal padding stays symmetric. */
	padding: calc(var(--pm-frame-pad) + var(--pm-yearbar-h) / 2) var(--pm-frame-pad) var(--pm-frame-pad);
	overflow: hidden;
	z-index: 0;
}

.map-frame__viewport {
	/* Account for the controls column so it can "hug" the viewport
	 * without forcing a wrap. Controls column contains just the zoom +/−
	 * stack now; the compass was moved out of this column entirely (it's
	 * `position: fixed` against the viewport — see `.map-compass` below).
	 *
	 * `--pm-yearbar-h` lives on `.map-frame` (see above) so the people
	 * panel can reference the same value. It was bumped from 70px → 90px
	 * in v0.6.17 — the yearbar's actual footprint (track + 30px gap +
	 * readout + margins) is ~82–90px, and the old 70px reservation left
	 * the map viewport sitting too close to the yearbar once the stage
	 * was vertically centred by `.map-frame { place-items: center }`. */
	--pm-controls-col: 54px;
	--pm-stage-gap: clamp(10px, 1.6vw, 16px);
	/* Vertical available space = 100vh minus top padding (pad + yearbar-h/2)
	 * minus bottom padding (pad). Simplifies to:
	 *     100vh - 2*pad - yearbar-h/2
	 * The old calc subtracted the full yearbar-h (twice the pad), which
	 * was correct when `.map-frame` used symmetric padding + centring;
	 * now that only half the yearbar-h lives above the stage and the
	 * rest of the vertical space below is available to the map, we only
	 * subtract half. The map grows by `yearbar-h / 2` as a result. */
	--pm-frame-size: min(
		calc(100vw - 2 * var(--pm-frame-pad) - var(--pm-controls-col) - var(--pm-stage-gap)),
		calc(100vh - 2 * var(--pm-frame-pad) - var(--pm-yearbar-h) / 2)
	);
	width: var(--pm-frame-size);
	height: var(--pm-frame-size);
	position: relative;
	overflow: hidden;
	background: #fff;
	border: 1px solid #000;
	box-shadow: 0 16px 50px rgba(0,0,0,0.12);
}

/* Year bar (horizontal range slider across the top of the frame) */
.map-frame__yearbar {
	position: fixed;
	top: clamp(12px, 2.2vw, 22px);
	left: clamp(60px, 8vw, 100px);
	right: clamp(60px, 8vw, 100px);
	z-index: 20;
	pointer-events: auto;
}
.yearbar {
	display: grid;
	gap: 30px;
}
.yearbar__track {
	position: relative;
	height: 28px;
	user-select: none;
	touch-action: none;
}
.yearbar__track::before {
	content: '';
	position: absolute;
	top: 50%;
	left: 0;
	right: 0;
	height: 2px;
	background: #ccc;
	transform: translateY(-50%);
}
.yearbar__fill {
	position: absolute;
	top: 50%;
	height: 4px;
	background: #000;
	transform: translateY(-50%);
	pointer-events: none;
	left: 0%;
	width: 100%;
}
.yearbar__handle {
	position: absolute;
	top: 50%;
	width: 16px;
	height: 16px;
	border-radius: 50%;
	border: 2px solid #000;
	background: #fff;
	transform: translate(-50%, -50%);
	cursor: grab;
	z-index: 3;
	padding: 0;
	transition: background 0.1s;
}
.yearbar__handle:hover,
.yearbar__handle:active { background: #000; cursor: grabbing; }
.yearbar__handle-label {
	position: absolute;
	top: 100%;
	left: 50%;
	transform: translateX(-50%);
	margin-top: 4px;
	font-size: 0.7rem;
	font-weight: 700;
	color: #000;
	white-space: nowrap;
	pointer-events: none;
}
.yearbar__tick {
	position: absolute;
	top: 50%;
	left: var(--tick-pos, 0%);
	width: 1px;
	height: 8px;
	background: #bbb;
	transform: translate(-50%, -50%);
	pointer-events: none;
}
.yearbar__tick--major {
	height: 14px;
	background: #888;
}
.yearbar__label {
	position: absolute;
	top: 14px;
	left: 50%;
	transform: translateX(-50%);
	font-size: 0.65rem;
	font-weight: 700;
	color: #555;
	white-space: nowrap;
}
.yearbar__readout {
	display: flex;
	justify-content: flex-end;
	align-items: center;
	gap: 10px;
}
.yearbar__range-text {
	font-size: 0.8rem;
	font-weight: 700;
	color: #000;
	letter-spacing: 0.02em;
}
.yearbar__clear {
	font-size: 0.7rem;
	font-weight: 700;
	padding: 3px 10px;
	border: 1px solid #000;
	border-radius: 0;
	background: #fff;
	color: #000;
	cursor: pointer;
}
.yearbar__clear:hover { background: #f2f2f2; }

/* Legacy filter box (kept for non-framed templates) */
.map-frame__filters {
	position: fixed;
	top: clamp(12px, 2.2vw, 22px);
	left: clamp(12px, 2.2vw, 22px);
	z-index: 20;
	pointer-events: auto;
}

/* Stage: viewport + controls sit together, centred */
.map-frame__stage {
	position: relative;
	display: grid;
	grid-template-columns: auto auto;
	align-items: end;
	gap: var(--pm-stage-gap);
}

.map-frame__controls {
	position: relative;
	z-index: 20;
	pointer-events: auto;
}

/* Override the full-screen SVG template behaviour when used in framed mode */
.map-template-svg.map-template-svg-framed {
	position: absolute;
	inset: 0;
	overflow: hidden;
	touch-action: none;
}

/* Framed: hide the Home pill since the drawer menu contains a Home link.
 *
 * The `.pm-svg-framed .map-menu-btn` button override that used to live
 * here was removed in v0.6.15: the white/square burger look became the
 * site-wide default (see `css/generic.css`), so scoping it to the
 * framed template was no longer needed. */
.pm-svg-framed .map-home { display: none; }

/* Make framed zoom buttons vertical and compact */
.map-frame__controls .map-controls {
	position: static;
	display: grid;
	grid-auto-flow: row;
	gap: 5px;
	/* Breathing room between the map frame's right border and the zoom
	 * buttons. Originally 5px; bumped in v0.6.9 because at 5px the zoom
	 * buttons' left border sat visually right up against the frame's
	 * right border. */
	margin-left: 8px;
}
.map-frame__controls .map-controls button {
	width: 44px;
	height: 44px;
	border-radius: 0;
	border: 1px solid #000;
	background: #fff;
	color: #000;
}
.map-frame__controls .map-controls button:hover { background: #f2f2f2; }

/* Compass widget: always-visible with 4 cardinal-direction buttons.
 * Pinned to the bottom-right corner of the viewport (outside
 * `.map-frame`, so the pop-out panel isn't clipped by the frame's
 * `overflow: hidden`). The `clamp(...)` values here mirror
 * `.map-frame`'s `--pm-frame-pad` so the compass sits inside the
 * same safe area as the rest of the framed UI without needing to
 * inherit the variable across the frame boundary.
 *
 * Used to be 8 directions — diagonals (NE/SE/SW/NW) were dropped in
 * v0.6.9; the `region_direction` taxonomy terms still exist so no
 * region data is lost. */
.map-compass {
	--compass-size: 130px;
	position: fixed;
	right: clamp(12px, 2.2vw, 22px);
	bottom: clamp(12px, 2.2vw, 22px);
	width: var(--compass-size);
	height: var(--compass-size);
	/* Sits in the same stacking band as the other in-map widgets
	 * (yearbar, people panel at z-index: 20) so that when the main
	 * navigation drawer opens (z-index: 30 in `css/generic.css`) its
	 * backdrop + panel cleanly obscure the compass along with the rest
	 * of the map chrome. Used to be 30, which tied with the drawer and
	 * fell back to DOM order — and because the compass is appended
	 * later than `<header>`, it won the tie and stayed visible through
	 * the open drawer. */
	z-index: 20;
	pointer-events: auto;
}
.map-compass__rose {
	position: absolute;
	inset: 15px;
	display: block;
}
.map-compass__rose svg {
	width: 100%;
	height: 100%;
	display: block;
}

/* Directional buttons placed around the rose */
.map-compass__dir {
	position: absolute;
	width: 24px;
	height: 24px;
	border-radius: 50%;
	border: 1px solid #000;
	background: #fff;
	color: #000;
	font-size: 0.55rem;
	font-weight: 700;
	line-height: 1;
	padding: 0;
	cursor: pointer;
	display: grid;
	place-items: center;
	transition: background 0.15s;
	z-index: 2;
}
.map-compass__dir:hover { background: #e8e8e8; }
.map-compass__dir.is-active { background: #000; color: #fff; }

/* Position each direction — percentages of the compass container.
 * Only the four cardinals are shown in the UI; see note above
 * `.map-compass`. */
.map-compass__dir[data-dir="N"] { top: 0;    left: 50%; transform: translate(-50%, 0); }
.map-compass__dir[data-dir="E"] { top: 50%;  right: 0;  transform: translateY(-50%); }
.map-compass__dir[data-dir="S"] { bottom: 0; left: 50%; transform: translate(-50%, 0); }
.map-compass__dir[data-dir="W"] { top: 50%;  left: 0;   transform: translateY(-50%); }

/* Pop-out panel. Opens *upward* from the compass since the compass
 * is pinned to the bottom of the viewport — a downward-opening panel
 * would fall off the bottom of the screen. */
.map-compass__popout {
	position: absolute;
	left: 50%;
	bottom: calc(100% + 6px);
	transform: translateX(-50%);
	min-width: 160px;
	max-width: 220px;
	background: #fff;
	border: 1px solid #000;
	padding: 6px 0;
	opacity: 0;
	pointer-events: none;
	transition: opacity 0.2s ease;
	z-index: 10;
	box-sizing: border-box;
}
.map-compass__popout.is-open {
	opacity: 1;
	pointer-events: auto;
}
.map-compass__region {
	display: flex;
	justify-content: space-between;
	align-items: baseline;
	gap: 8px;
	padding: 6px 12px;
	text-decoration: none;
	color: #000;
	font-size: 0.8rem;
	transition: background 0.1s;
}
.map-compass__region:hover { background: #f2f2f2; }
.map-compass__region-name { font-weight: 700; }
.map-compass__region-distance { font-size: 0.7em; opacity: 0.7; white-space: nowrap; }
.map-compass__empty {
	padding: 8px 12px;
	margin: 0;
	font-size: 0.8rem;
	color: #888;
}

/* ---------- Map Key (icon legend) — v0.6.13 ---------------------------
 *
 * Two very different presentations share a single DOM render (mounted
 * inside `.map-frame__controls`, above the zoom buttons):
 *
 *   DESKTOP (default, >900px): Always visible. Icons stacked vertically
 *     with labels to the right, sitting directly above the zoom-in/
 *     zoom-out buttons at the right edge of the frame. No header, no
 *     title, no toggle, no close button — the key is reference material
 *     and on a big screen there's always room to leave it on display.
 *
 *   MOBILE (≤900px): Falls back to a bottom-left slide drawer pinned
 *     next to the People toggle. Starts CLOSED on phones (the mobile-init
 *     strip in `js/map-template-3.js` removes the PHP-rendered `.is-open`
 *     class on narrow viewports). The drawer pulls in from the left edge,
 *     has a header strip with an × close, and collapses back to a compact
 *     "Key" reopener. Screen real estate is too precious on phones to
 *     keep an always-on panel.
 *
 * The open/close state machine (`.is-open` class, JS handlers for ×
 * and the reopener) only matters in the mobile layout; on desktop the
 * widget is presented as a plain list and the toggle/close buttons are
 * hidden outright. The state classes are harmless when they're set but
 * their styling effects are overridden below — JS doesn't need to know
 * which layout is active.
 * ------------------------------------------------------------------- */

/* ----- Desktop default (>900px): static inline list ----- */

.map-frame__controls {
	/* Stack the Key above the zoom buttons in the right-hand column. The
	 * `.map-frame__stage` grid already has `align-items: end` so the
	 * whole stack anchors to the bottom of the frame; the Key therefore
	 * sits directly above the zoom buttons. `align-items: flex-start`
	 * keeps both children left-aligned within the column so the Key
	 * icons' left edge shares a clean guide line with the zoom buttons'
	 * left edge (both are 8px in from the frame — see `margin-left: 8px`
	 * on both `.map-key` below and `.map-frame__controls .map-controls`). */
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 12px;
}

.map-key {
	/* Matches the zoom-buttons column width (44px) plus the label text
	 * flowing to the right. Min-content lets the label determine the
	 * overall width naturally. */
	position: static;
	display: block;
	/* Cancel any margin/padding from inherited `.map-frame__controls .map-controls`
	 * sibling rules that might apply. */
	margin: 0;
	/* Same left-edge offset as the zoom buttons so the two widgets share
	 * a clean left guide line on the right of the frame. See
	 * `.map-frame__controls .map-controls { margin-left: 8px }` above. */
	margin-left: 8px;
}

/* Hide all the mobile-only chrome on desktop. `.map-key__toggle`,
 * `.map-key__header` (title + ×) are mobile-only; show the raw
 * `.map-key__list` only. */
.map-key__toggle,
.map-key__header {
	display: none;
}

/* Popout is the container of the list on desktop too, but stripped of
 * its popout chrome (no absolute positioning, no border/background, no
 * opacity animation, no min-width). Let the list take its natural size. */
.map-key__popout {
	position: static;
	background: transparent;
	border: 0;
	padding: 0;
	opacity: 1;
	pointer-events: auto;
	min-width: 0;
	box-sizing: border-box;
}

.map-key__list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 6px;
}
.map-key__item {
	display: flex;
	align-items: center;
	gap: 10px;
	font-size: 0.8rem;
	color: #000;
	/* No row padding/background on desktop. Hover & active states are shown
	 * by inverting the icon tile itself (below) rather than the row, so the
	 * tile's left edge lines up cleanly with the zoom buttons' left edge
	 * instead of sitting inset behind row padding. */
	padding: 0;
	cursor: pointer;
	user-select: none;
}
.map-key__item + .map-key__item { border-top: 0; }
.map-key__icon {
	flex: 0 0 auto;
	width: 28px;
	height: 28px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	/* Matches the zoom-buttons' visual treatment so the two widgets read
	 * as members of the same family: white fill, 1px black border,
	 * square corners. */
	background: #fff;
	border: 1px solid #000;
	box-sizing: border-box;
	/* Smoothly cross to the inverted (black) state on hover / active. */
	transition: background 140ms ease, color 140ms ease;
}
.map-key__icon img {
	display: block;
	/* Pad the icon slightly inside its 28px tile so it breathes against
	 * the border (actual SVG renders at 22px inside a 28px container). */
	width: 22px;
	height: 22px;
	object-fit: contain;
	transition: filter 140ms ease;
}

/* Active / hidden = invert the tile to black with a white glyph. Driven by a
 * JS-toggled class, so it's reliable on every device. Category glyphs are
 * <img> (flipped with filter:invert); the eye tile uses an inline SVG drawn
 * in currentColor (flipped by swapping the colour). */
.map-key__item.is-active .map-key__icon,
.map-key__item--hide.is-hidden .map-key__icon {
	background: #000;
	color: #fff;
}
.map-key__item.is-active .map-key__icon img,
.map-key__item--hide.is-hidden .map-key__icon img {
	filter: invert(1);
}
/* Hover invert ONLY on devices with a true hover pointer. On touch, a tap
 * leaves the tile in a sticky :hover state, which (a) overrides the active
 * tile and (b) triggers iOS's two-tap behaviour — the first tap on a now-
 * hovered tile is swallowed to "show hover" instead of firing a click, so an
 * active category couldn't be tapped again to clear it. Gating hover behind
 * `hover: hover` removes hover rules on touch entirely, so every tap is a
 * clean click and the JS toggle works. */
@media (hover: hover) {
	.map-key__item:hover .map-key__icon {
		background: #000;
		color: #fff;
	}
	.map-key__item:hover .map-key__icon img {
		filter: invert(1);
	}
}
.map-key__label {
	font-weight: 600;
	/* Labels can stay one line on desktop — wrap only if the user has
	 * set a huge font size. */
	white-space: nowrap;
}

/* ----- Location pins with featured images — slightly larger than icon pins ----- */
/* img.map-pin = featured image; span.map-pin = icon fallback. Only the former gets the size bump. */
.map-template-svg-framed img.map-pin {
	width: 38px;
}

/* ----- Key category filter — pin highlight / dim states ----- */

/* Applied by JS when the user clicks a key item. Uses :not(.is-filtered-out)
 * so year-filtered (invisible) pins are never affected by the key filter. */
.map-template-svg-framed.has-key-filter .map-pin:not(.is-filtered-out).is-key-dimmed {
	opacity: 0.1;
	transition: opacity 200ms ease, filter 200ms ease;
}
.map-template-svg-framed.has-key-filter .map-pin:not(.is-filtered-out).is-key-highlighted {
	filter: drop-shadow(0 0 6px #007cba) drop-shadow(0 0 2px rgba(0,124,186,0.9));
	transition: opacity 200ms ease, filter 200ms ease;
}

/* ----- Mobile / narrow-viewport override (≤900px) ----- */

@media (max-width: 900px) {
	/* NOTE: positioning rules for `.map-frame__controls` and `.map-key`
	 * used to live here (zoom-buttons column collapse, Key pinned to the
	 * top-right below the burger). They were replaced in the second mobile
	 * block below (search for "MOBILE LAYOUT — ≤ 900px") which repositions
	 * `.map-frame__controls` as a fixed top-right overlay and `.map-key`
	 * as a bottom-left slide-drawer. The CHROME rules (toggle, popout
	 * shell, header, ×, list density) below still apply in both layouts —
	 * the second block only redoes positioning. */
	/* Reopener (the "Key" button that appears after the popout is closed).
	 * Hidden while the popout is open so we never show both chrome pieces
	 * at once. */
	.map-key__toggle {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		min-width: 48px;
		height: 32px;
		padding: 0 12px;
		border: 1px solid #000;
		background: #fff;
		color: #000;
		font-size: 0.8rem;
		font-weight: 700;
		letter-spacing: 0.04em;
		text-transform: uppercase;
		line-height: 1;
		cursor: pointer;
		transition: background 0.15s, color 0.15s;
	}
	.map-key__toggle:hover { background: #e8e8e8; }
	.map-key.is-open .map-key__toggle { display: none; }

	/* Popout gets its chrome back: absolute position, border, fade-in. */
	.map-key__popout {
		position: absolute;
		right: 0;
		top: 0;
		min-width: 200px;
		background: #fff;
		border: 1px solid #000;
		padding: 0;
		opacity: 0;
		pointer-events: none;
		transition: opacity 0.2s ease;
		z-index: 10;
	}
	.map-key__popout.is-open {
		opacity: 1;
		pointer-events: auto;
	}

	/* Header strip: "KEY" title on the left, × close on the right. */
	.map-key__header {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 8px;
		padding: 6px 8px 6px 12px;
		border-bottom: 1px solid #000;
		background: #000;
		color: #fff;
	}
	.map-key__title {
		font-size: 0.8rem;
		font-weight: 700;
		letter-spacing: 0.04em;
		text-transform: uppercase;
		line-height: 1;
	}
	.map-key__close {
		display: inline-grid;
		place-items: center;
		width: 24px;
		height: 24px;
		padding: 0;
		border: 1px solid rgba(255, 255, 255, 0.35);
		background: transparent;
		color: #fff;
		font-size: 1.1rem;
		line-height: 1;
		cursor: pointer;
		transition: background 0.15s, color 0.15s, border-color 0.15s;
	}
	.map-key__close:hover,
	.map-key__close:focus-visible {
		background: #fff;
		color: #000;
		border-color: #fff;
		outline: none;
	}
	.map-key__close span {
		/* Nudge the × glyph up slightly so it looks optically centred. */
		display: block;
		transform: translateY(-1px);
	}

	/* List styling on mobile: row-padded items with hairline separators
	 * (matches the visual density of the compass popout). Overrides the
	 * desktop defaults above. */
	.map-key__list {
		padding: 6px 0;
		gap: 0;
	}
	.map-key__item {
		padding: 6px 12px;
		border-radius: 0; /* full-width rows on mobile — no rounded corners */
	}
	.map-key__item.is-active {
		/* Active state is shown by inverting the icon tile (below) — same as
		 * desktop. No row outline (that blue ring read like an iOS default
		 * focus border). */
		outline: 0;
	}
	.map-key__item + .map-key__item { border-top: 1px solid #eee; }

	/* Resting tiles are borderless on mobile — the popout already has its own
	 * border, so a per-icon border would feel busy. */
	.map-key__icon {
		width: 24px;
		height: 24px;
		background: transparent;
		border: 0;
	}
	/* The desktop tile-invert (black square + white glyph) carries through to
	 * mobile for :hover, .is-active and the hidden eye tile — that's the
	 * "what did I press" indicator. We deliberately do NOT cancel :hover here:
	 * iOS keeps the tapped element in :hover after the tap, and an equal-
	 * specificity hover-cancel placed later in the file would beat the active
	 * rule and wipe the selected tile back to white. Leaving hover to invert
	 * means the active state always reads. */
	.map-key__icon img {
		width: 100%;
		height: 100%;
	}
}

/* Make framed filters narrower so they don't obscure the window */
.map-frame__filters .map-filters {
	position: static;
	display: grid;
	grid-auto-flow: row;
	gap: 8px;
	padding: 10px;
	border-radius: 0;
	background: #fff;
	border: 1px solid #000;
	color: #000;
	width: 220px;
	box-sizing: border-box;
}
.map-frame__filters-title { font-weight: 900; font-size: 0.9rem; letter-spacing: 0.01em; }
.map-frame__filters .map-filter { display: grid; gap: 6px; align-items: start; }
.map-frame__filters .map-filter__label { font-size: 0.85rem; }
.map-frame__filters .map-filter__select {
	width: 100%;
	box-sizing: border-box;
	padding: 8px 10px;
	border-radius: 0;
	border: 1px solid #000;
	background: #fff;
	color: #000;
	overflow: hidden;
	text-overflow: ellipsis;
}
.map-frame__filters .map-filter__select option { color: #111; }
.map-frame__filters .map-filter__clear {
	padding: 8px 10px;
	border-radius: 0;
	border: 1px solid #000;
	background: #fff;
	color: #000;
	font-weight: 900;
	justify-self: start;
}
.map-frame__filters .map-filter__clear:hover { background: #f2f2f2; }

/* ---------- People panel: framed overrides (black/white aesthetic) ----
 *
 * Positioning: `.map-frame` now uses asymmetric padding with
 * `align-items: start` (was symmetric padding + `place-items: center`).
 * This lets the top and bottom reservations be tuned independently:
 *   - Top reservation  = pad + yearbar-h/2  (where the stage starts)
 *   - Bottom reservation = pad              (where the stage ends)
 *
 * So the map's top edge sits at `pad + yearbar-h/2` from the screen
 * top and its bottom edge sits at `pad` from the screen bottom. We
 * mirror both offsets here so the people panel's top and bottom edges
 * track the map frame's top and bottom edges. Using the same
 * `--pm-frame-pad` and `--pm-yearbar-h` tokens (hoisted to `.map-frame`)
 * means this stays in sync automatically if either token is retuned.
 *
 * On rare viewports where the map is width-constrained instead of
 * height-constrained (very tall portrait displays), the map will be
 * smaller than the full available height and sit at the top of the
 * stage; the people panel will extend below the map in that case.
 * Acceptable — the primary target is desktop/laptop. */
.map-frame__people {
	position: fixed;
	left: var(--pm-frame-pad);
	top: calc(var(--pm-frame-pad) + var(--pm-yearbar-h) / 2);
	bottom: var(--pm-frame-pad);
	z-index: 20;
	pointer-events: auto;
	display: grid;
	align-content: start;
}
.map-frame__people .map-people-panel {
	position: static;
	width: 220px;
	box-sizing: border-box;
	top: auto;
	left: auto;
	bottom: auto;
	max-height: 100%;
	border-radius: 0;
	background: #fff;
	border: 1px solid #000;
	color: #000;
	backdrop-filter: none;
	-webkit-backdrop-filter: none;
	overflow: hidden;
}
.map-frame__people .map-people-panel__list {
	/* Cap the scrollable list height to fit inside the panel's available
	 * space. Wrapper height = 100vh - top - bottom
	 *                       = 100vh - (pad + yearbar-h/2) - pad
	 *                       = 100vh - 2*pad - yearbar-h/2
	 * Subtract ~130px for search box + clear button + panel padding +
	 * grid gaps so the list scrolls internally rather than pushing the
	 * panel past its wrapper. Tracks the same design tokens as
	 * `.map-frame` so any future retuning of pad / yearbar-h cascades. */
	max-height: calc(100vh - 2 * var(--pm-frame-pad) - var(--pm-yearbar-h) / 2 - 130px);
	overflow-y: auto;
}
.map-frame__people .map-people-panel__search {
	border-radius: 0;
	border: 1px solid #000;
	background: #fff;
	color: #000;
}
.map-frame__people .map-people-panel__search::placeholder { color: #888; }
.map-frame__people .map-people-panel__btn { color: #000; }
.map-frame__people .map-people-panel__btn:hover { background: #f2f2f2; }
.map-frame__people .map-people-panel__item.is-active .map-people-panel__btn { background: #e4e4e4; }
.map-frame__people .map-people-panel__thumb {
	border: 1px solid rgba(0,0,0,0.15);
	background: #eee;
}
.map-frame__people .map-people-panel__clear {
	border-radius: 0;
	border: 1px solid #000;
	background: #fff;
	color: #000;
	font-weight: 900;
}
.map-frame__people .map-people-panel__clear:hover { background: #f2f2f2; }

/* ---- "Hide all people" toggle ----
 * Primary action at the top of the People panel. Mirrors the framed
 * template chrome (square, 1px black border, white fill). When active
 * (people hidden) it inverts to black-on-white so the "off" state reads
 * at a glance, and the eye SVG animates to its closed form. */
.map-frame__people .map-people-panel__hide-toggle {
	display: flex;
	align-items: center;
	gap: 8px;
	width: 100%;
	box-sizing: border-box;
	margin: 0 0 8px;
	padding: 8px 10px;
	border-radius: 0;
	border: 1px solid #000;
	background: #fff;
	color: #000;
	font: inherit;
	font-size: 0.82rem;
	font-weight: 700;
	letter-spacing: 0.02em;
	text-align: left;
	cursor: pointer;
}
.map-frame__people .map-people-panel__hide-toggle:hover { background: #f2f2f2; }
.map-frame__people .map-people-panel__hide-toggle.is-hidden {
	background: #000;
	color: #fff;
}
.map-frame__people .map-people-panel__hide-toggle.is-hidden:hover { background: #1a1a1a; }
.map-people-panel__hide-label { line-height: 1; }

/* Animated eye icon. Strokes use currentColor so the eye inverts with the
 * button. The open and closed forms cross-fade; `transform-box: fill-box`
 * makes `transform-origin: center` resolve to each shape's own centre. */
.pm-eye {
	width: 22px;
	height: 22px;
	flex: 0 0 auto;
	overflow: visible;
}
.pm-eye path,
.pm-eye circle {
	fill: none;
	stroke: currentColor;
	stroke-width: 1.6;
	stroke-linecap: round;
	stroke-linejoin: round;
}
.pm-eye__pupil { fill: currentColor; stroke: none; }

.pm-eye__open,
.pm-eye__closed,
.pm-eye__iris,
.pm-eye__pupil {
	transform-box: fill-box;
	transform-origin: center;
	transition: opacity 0.28s ease, transform 0.28s ease;
}

/* Default state = eye OPEN */
.pm-eye__open   { opacity: 1; transform: scaleY(1); }
.pm-eye__closed { opacity: 0; transform: scaleY(0.4); }

/* Active (people hidden) = eye CLOSED: lid squeezes shut, iris shrinks out,
 * the closed curve + lashes fade in. */
.is-hidden .pm-eye__open   { opacity: 0; transform: scaleY(0.08); }
.is-hidden .pm-eye__iris,
.is-hidden .pm-eye__pupil  { opacity: 0; transform: scale(0.15); }
.is-hidden .pm-eye__closed { opacity: 1; transform: scaleY(1); }

@media (prefers-reduced-motion: reduce) {
	.pm-eye__open,
	.pm-eye__closed,
	.pm-eye__iris,
	.pm-eye__pupil { transition: none; }
}

.pm-svg-framed .map-people-panel__toggle {
	border-radius: 0;
	border: 1px solid #000;
	background: #fff;
	color: #000;
	backdrop-filter: none;
	-webkit-backdrop-filter: none;
}
.pm-svg-framed .map-people-panel__toggle:hover { background: #f2f2f2; }

/* People panel header — hidden on desktop (panel is always-open there),
 * shown on mobile where it matches the Key panel header. */
.map-frame__people .map-people-panel__header { display: none; }

/* ---------- Top bar — hidden on desktop, shown on mobile ---------- */
.map-frame__topbar { display: none; }
.map-frame__site-title {
	font-size: 0.85rem;
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: #000;
	text-decoration: none;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

/* ================================================================
 * MOBILE LAYOUT — ≤ 900px
 * ================================================================
 *
 * All mobile overrides live here. Desktop rules above are untouched.
 *
 * Structure:
 *   [Top bar: site title | burger]   48px fixed
 *   [Year slider — full width]       ~46px fixed
 *   [Map viewport — full width]      fills remaining space
 *   [People] [Key]        [Compass]  bottom controls
 *   Zoom buttons overlay map top-right corner
 * ================================================================ */

@media (max-width: 900px) {

	/* Collapse the global site header visually. We can't use
	 * display:none because the burger (.map-menu-btn) and drawer
	 * (#site-drawer) are position:fixed — they're DOM children of
	 * the header but fixed children escape a parent's display:none.
	 * Wait — actually they DON'T escape display:none (spec). So we
	 * use height:0 + overflow:hidden instead: the fixed children then
	 * sit outside the collapsed box and remain fully visible. */
	.pm-svg-framed .site-header {
		height: 0;
		overflow: hidden;
		padding: 0;
		margin: 0;
	}

	/* ---- Top bar: site title left, burger slots into top-right ---- */
	.map-frame__topbar {
		display: flex;
		align-items: center;
		position: fixed;
		top: 0; left: 0; right: 0;
		height: 64px; /* was 48px → 56px → 64px — breathing room around title/burger */
		background: #fff;
		border-bottom: 1px solid #000;
		padding: 0 60px 0 12px; /* 60px right clears the 40px burger + 12px margin */
		box-sizing: border-box;
		z-index: 24;
	}

	/* ---- Yearbar: full-width, no side margins, below top bar ---- */
	.map-frame__yearbar {
		top: 64px; /* tracks topbar height */
		left: 0;
		right: 0;
		background: #fff;
		border-bottom: 1px solid #eee;
		padding: 20px 12px 16px; /* was 10px 12px 8px → 14px/12px → 20px/16px */
		box-sizing: border-box;
	}

	/* Hide the "Currently Viewing" readout row — tick labels on the
	 * track tell the story and this frees ~30px of vertical space */
	.yearbar__readout { display: none; }

	/* No gap needed once the readout row is gone */
	.yearbar { gap: 0; }

	/* Inset the track so handles at 0% / 100% have room to breathe
	 * and year-edge labels (1946, 1968) aren't clipped */
	.yearbar__track { margin: 0 16px; }

	/* Larger touch targets for dragging */
	.yearbar__handle { width: 26px; height: 26px; }

	/* Slightly larger tick labels */
	.yearbar__label { font-size: 0.72rem; }

	/* ---- Map frame: top reservation = topbar (64) + yearbar (~64) + gap ≈ 142px
	 * --pm-yearbar-h used by --pm-frame-size height constraint ---- */
	.map-frame {
		--pm-yearbar-h: 180px;
		padding-top: 142px; /* was 100px → 118px → 142px */
	}

	/* When any panel is open, raise the frame above root-level fixed elements:
	 * burger (.map-menu-btn z-index:31) and compass (.map-compass z-index:20).
	 * JS adds/removes this class via showBackdrop/hideBackdrop. */
	.map-frame.has-panel-open,
	/* Modal open: same lift so compass/burger sit behind the modal. */
	.map-frame.has-modal-open {
		z-index: 100;
	}

	/* ---- Viewport: explicit square rather than relying on the 1fr grid
	 * arithmetic. On mobile the controls are all position:fixed so they're
	 * out of the flow; we just need the viewport itself to be the right size.
	 * 24px = 12px left pad + 12px right pad (matches --pm-frame-pad). ---- */
	.map-frame__viewport {
		--pm-controls-col: 0px;
		--pm-stage-gap: 0px;
		width: calc(100vw - 24px);
		height: calc(100vw - 24px); /* keep it square */
	}

	/* ---- Stage: single column, sized to the viewport (auto not 1fr so the
	 * stage doesn't balloon to full-frame width and stretch the viewport) ---- */
	.map-frame__stage {
		grid-template-columns: auto;
		gap: 0;
	}

	/* ---- Zoom buttons: overlay the top-right corner of the map ---- */
	/* No explicit z-index here — controls sits at z-index:auto within .map-frame,
	 * which paints below the backdrop (z-index:45) so the overlay correctly dims
	 * the zoom buttons when a panel opens. The Key popout is moved to be a direct
	 * child of .map-frame by JS on mobile (see map-template-3.js) so it no longer
	 * needs .map-frame__controls to be elevated. */
	.map-frame__controls {
		position: fixed;
		right: 12px;
		top: calc(142px + 10px); /* tracks padding-top — zoom buttons sit 10px inside the map top edge */
		bottom: auto;
		display: block;
	}
	.map-frame__controls .map-controls { margin-left: 0; }

	/* ---- Compass: scaled down so it doesn't dominate the below-map space ---- */
	.map-compass { --compass-size: 72px; }

	/* ---- People panel wrapper ----
	 *
	 * z-index 46 sits this wrapper above `.map-panel-backdrop` (45) so taps
	 * inside the open panel reach the list instead of being intercepted by
	 * the backdrop and closing the panel. The desktop default (z-index 20,
	 * defined above) doesn't apply at ≤900px so we redeclare here.
	 *
	 * `pointer-events: none` on the wrapper itself lets taps fall through
	 * to the map canvas in the area the (transparent) wrapper occupies
	 * when the panel is closed and translated off-screen. The inner panel
	 * keeps its own `pointer-events: auto` from `map-template-3.css`, so
	 * the visible panel still captures its own interactions. */
	.map-frame__people {
		top: 142px; /* tracks padding-top */
		bottom: 60px;
		overflow: hidden; /* clips the panel while it's translated off-screen */
		z-index: 46;
		pointer-events: none;
	}

	/* ---- People panel slide: extend the ≤600px rule to cover ≤900px
	 * (the framed template activates at 900px, not 600px) ---- */
	.map-frame__people .map-people-panel {
		transform: translateX(calc(-100% - 24px));
		transition: transform 260ms cubic-bezier(0.22, 1, 0.36, 1);
	}
	.map-frame__people .map-people-panel.is-open {
		transform: translateX(0);
	}

	/* ---- People panel header: mirrors .map-key__header so the two drawers match ---- */
	.map-frame__people .map-people-panel__header {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 8px;
		padding: 6px 8px 6px 12px;
		border-bottom: 1px solid #000;
		background: #000;
		color: #fff;
		flex-shrink: 0;
	}
	.map-frame__people .map-people-panel__title {
		font-size: 0.8rem;
		font-weight: 700;
		letter-spacing: 0.04em;
		text-transform: uppercase;
		line-height: 1;
	}
	.map-frame__people .map-people-panel__close {
		display: inline-grid;
		place-items: center;
		width: 24px;
		height: 24px;
		padding: 0;
		border: 1px solid rgba(255, 255, 255, 0.35);
		background: transparent;
		color: #fff;
		font-size: 1.1rem;
		line-height: 1;
		cursor: pointer;
		transition: background 0.15s, color 0.15s, border-color 0.15s;
	}
	.map-frame__people .map-people-panel__close:hover,
	.map-frame__people .map-people-panel__close:focus-visible {
		background: #fff;
		color: #000;
		border-color: #fff;
		outline: none;
	}
	.map-frame__people .map-people-panel__close span {
		display: block;
		transform: translateY(-1px);
	}

	/* ---- People toggle: show at ≤900px (not just ≤600px), bottom-left ---- */
	.pm-svg-framed .map-people-panel__toggle {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		height: 40px;
		padding: 0 14px;
		position: fixed;
		bottom: 12px;
		left: 12px;
		top: auto;
		right: auto;
		font-size: 0.8rem;
		font-weight: 700;
		letter-spacing: 0.04em;
		text-transform: uppercase;
		line-height: 1;
	}
	/* Toggle stays put when the panel opens — it sits in the 60px strip below
	 * the panel wrapper (bottom: 60px), so there is no overlap to clear.
	 * Also overrides the base ≤600px rule in map-template-3.css. */
	.pm-svg-framed .map-people-panel__toggle.is-open {
		left: 12px;
	}

	/* ---- Key: bottom-left, right of People toggle ----
	 *
	 * `position: fixed` and `pointer-events: auto` used to come from the
	 * first ≤900px block above. They were consolidated into this rule when
	 * the dead top-right positioning was removed from that block. */
	.map-key {
		position: fixed;
		top: auto;
		bottom: 12px;
		right: auto;
		left: calc(12px + 90px + 8px);
		margin: 0;
		z-index: 26;
		pointer-events: auto;
	}
	/* Key toggle: match People button height, and stay visible when open
	 * (overrides the first ≤900px block's display:none rule — same approach
	 * as the People toggle which stays put when its panel is open). */
	.map-key__toggle {
		height: 40px;
	}
	.map-key.is-open .map-key__toggle {
		display: inline-flex;
	}

	/* Key popout: slides in from the left, same direction and easing as the
	 * People panel. Position, size, and border now match .map-frame__people /
	 * .map-people-panel so the two drawers feel like a matched pair:
	 *   left: 12px  — same left edge as the People wrapper
	 *   top: 142px  — same top as the People wrapper
	 *   width: 220px — same width as the People panel
	 *   border: 1px solid #000 — all four sides, same as the People panel
	 * Height is content-driven (no bottom: 0 pin) but capped so it never
	 * extends into the bottom controls strip. */
	.map-key__popout {
		position: fixed;
		bottom: 60px;            /* bottom-aligned with .map-frame__people */
		top: auto;               /* height grows upward from that anchor */
		left: 12px;              /* matches .map-frame__people left (--pm-frame-pad) */
		right: auto;
		width: 220px;            /* matches .map-people-panel width */
		max-height: calc(100vh - 142px - 60px); /* can't grow above the yearbar */
		min-width: unset;
		overflow-y: auto;
		-webkit-overflow-scrolling: touch;
		background: #fff;
		border: 1px solid #000;  /* all four sides — matches .map-people-panel */
		opacity: 1;              /* kill the opacity-fade from block 1 */
		pointer-events: none;
		transform: translateX(calc(-100% - 14px)); /* slides fully off-screen left */
		transition: transform 260ms cubic-bezier(0.22, 1, 0.36, 1);
		z-index: 46;             /* matches .map-frame__people wrapper */
		box-sizing: border-box;
	}
	.map-key__popout.is-open {
		transform: translateX(0);
		pointer-events: auto;
		opacity: 1;
	}
}

/* Backdrop: dims the map when the People or Key panel is open. Created by
 * `js/map-template-3.js` and appended to `.map-frame`. The styles live
 * OUTSIDE the ≤900px @media block on purpose:
 *
 * - The element is created at script-load time (unconditionally, post-v0.6.??)
 *   so an orientation change from landscape → portrait still has a working
 *   backdrop without re-running the init logic.
 * - The default state is invisible + non-interactive (opacity:0, pointer-events:none),
 *   so on desktop where `.is-visible` is never added the backdrop has no
 *   visual or interaction effect — it's just an inert div hanging off
 *   `.map-frame`. */
.map-panel-backdrop {
	position: fixed;
	inset: 0;
	background: rgba(0, 0, 0, 0.45);
	opacity: 0;
	z-index: 45;
	pointer-events: none;
	transition: opacity 0.25s ease;
}
.map-panel-backdrop.is-visible {
	opacity: 1;
	pointer-events: auto;
}

