/* PG Dashboard — layout + widget styles.
 *
 * FIX 2026-08-10: full layout rework to match the reference ArcGIS
 * Dashboard's structure (pulled from that dashboard's own live JSON config —
 * see the plan for this change). Grid areas map 1:1 to top-level container
 * class names (minus the `pg-dashboard-` prefix) for easy maintenance: a
 * full-width slicer bar, a full-width 9-tile KPI strip, a 3-column body
 * (left/center/right), and a full-width bottom table. `.pg-panel*` styles
 * (Country/Religion checkbox lists) retired along with pg-dashboard-panels.js
 * in favor of `.pg-slicer*` (top dropdown bar) and `.pg-breakdown*` (the new
 * generic tabbed breakdown/ranked-list widget). Keeps Nunito Sans and the
 * site's existing red/orange/teal UUPG/EUPG/NUPG convention rather than
 * reverse-engineering ArcGIS's own palette — see the plan's Context section
 * for that call.
 */

/*
 * FIX 2026-08-10: full-bleed — breaks .pg-dashboard out of Astra's boxed
 * content container (max-width + centered) so it spans the actual browser
 * viewport instead of the page's normal content column, matching the
 * reference ArcGIS Dashboard app (which always fills the full viewport,
 * having no theme content wrapper of its own). Applied directly here rather
 * than via Astra's per-page "Full Width" content layout setting, since that
 * only removes the sidebar column — it doesn't remove Astra's own max-width
 * container, so this would still be needed either way.
 *
 * FIX 2026-08-10 (same day, revised): first attempt used
 * `position:relative; left:50%; right:50%; margin-left/right:-50vw` — a
 * conflicting pair (left AND right both set on a position:relative box is
 * invalid; only one wins) and broke the map. Replaced with the
 * `margin: 0 calc(50% - 50vw)` variant, which achieves the same full-bleed
 * effect via margins alone — no `position` property at all, so it can't
 * interfere with any descendant's positioning context (the map overlay
 * card, `.pg-dashboard-map-canvas`, etc. bind to their own nearer positioned
 * ancestors regardless either way, but this removes the risk entirely
 * rather than relying on that reasoning holding up in every browser).
 * pg-dashboard-map.js also now explicitly re-measures the map after boot,
 * covering the case where MapLibre's own initial size read raced the
 * layout settling into its final full-bleed width.
 *
 * FIX 2026-08-10 (later still): the `margin: 0 calc(50% - 50vw)` math above
 * is correct on its own (verified against this page's actual live markup:
 * #content.site-content > .ast-container > #primary.content-area.primary >
 * main#main.site-main > article > .entry-content > .pg-dashboard), but a
 * negative-margin full-bleed trick still renders visually clipped if ANY
 * ancestor between the element and the viewport has `overflow` (or
 * `overflow-x`) set to `hidden`/`clip`/`auto` — very common on Astra's
 * `.ast-container` and similar wrappers, sometimes paired with a
 * border-radius/box-shadow effect elsewhere on the same theme. Neutralized
 * every element in that confirmed chain, scoped via :has() so it only
 * applies to whichever specific instance of each wrapper actually contains
 * this dashboard — other pages/sections using the same Astra classes
 * (e.g. this page's own title banner, which reuses `.ast-container` in a
 * completely separate part of the DOM) are unaffected.
 */
#content:has(.pg-dashboard),
.ast-container:has(.pg-dashboard),
#primary:has(.pg-dashboard),
main#main:has(.pg-dashboard),
.entry-content:has(.pg-dashboard) {
	overflow: visible !important;
	max-width: none !important;
}


.pg-dashboard-dark-toggle {
	padding: 0 14px;
	border: none;
	border-left: 1px solid var(--pg-border);
	background: var(--pg-surface);
	color: var(--pg-text-muted);
	font-size: 12px;
	white-space: nowrap;
	cursor: pointer;
}

.pg-dashboard-dark-toggle:hover {
	background: var(--pg-hover);
	color: var(--pg-text);
}

/*
 * Dark-mode corrections for the few things tokens cannot reach.
 *
 * Astra styles inputs and buttons globally, so the slicer search box and
 * the tab buttons keep a white background in dark mode unless overridden
 * here — with the same boosted specificity the 2026-08-11 orange fix needed.
 */
.pg-dashboard.pg-dark .pg-slicer-search {
	background: var(--pg-surface-2) !important;
	color: var(--pg-text) !important;
	border: 1px solid var(--pg-border) !important;
}

.pg-dashboard.pg-dark .pg-breakdown-tab.pg-breakdown-tab {
	color: var(--pg-text) !important;
}

.pg-dashboard.pg-dark .pg-slicer-popover,
.pg-dashboard.pg-dark .pg-slicer,
.pg-dashboard.pg-dark .pg-stat-tiles,
.pg-dashboard.pg-dark .pg-dashboard-profile-card,
.pg-dashboard.pg-dark [data-pg-breakdown] {
	background: var(--pg-surface);
	color: var(--pg-text);
}

/* Photos and the fallback image sit on white by design; a hard white edge
 * against a dark surface reads as a rendering fault, so soften it. */
.pg-dashboard.pg-dark .pg-table-photo,
.pg-dashboard.pg-dark .pg-profile-card-photo img {
	filter: brightness(0.92);
}

/*
 * Astra styles `a` globally, so dashboard links need an explicit colour in
 * both modes — otherwise dark mode inherits the theme's dark blue on a dark
 * surface, which is what the PGID links looked like.
 */
.pg-dashboard a,
.pg-dashboard .pg-table-cell a,
.pg-dashboard .pg-profile-card-body a {
	color: var(--pg-link);
}

.pg-dashboard a:hover {
	color: var(--pg-link);
	text-decoration: underline;
}

/*
 * Selected row in dark mode, 2026-08-26. `#e7f3fb` is a pale blue wash that
 * works on white and becomes a bright block on a dark surface — the row's
 * own values (population, in muted grey) then all but vanish against it.
 * A tinted dark fill plus the brighter link blue keeps "this row is the
 * active filter" obvious without inverting the row.
 */
/* Wins over :hover — an active filter row keeps its identity while the
 * pointer is over it. The hover rule above excludes .--active for the same
 * reason, belt and braces since both carry !important. */
.pg-dashboard.pg-dark .pg-breakdown-row--active {
	background: #1e3a4f !important;
}

.pg-dashboard.pg-dark .pg-breakdown-row--active td {
	color: var(--pg-text);
}

.pg-dashboard.pg-dark .pg-breakdown-row--active td:first-child {
	color: var(--pg-link);
}

/*
 * Dark-mode hover states, 2026-08-26.
 *
 * Several hovers use pale TINTS rather than the shared palette —
 * `#f7f9fb` on the slicer triggers, `#f0f7fc` on the popover Reset,
 * `#fdf1f0` on Reset All Filters. The token sweep deliberately left those
 * alone (they are not the common greys), which meant they stayed near-white
 * in dark mode: hovering a control turned it into a white block with white
 * text on it. Each maps to the shared hover token here, keeping its accent
 * only where the accent is the point.
 *
 * `!important` matches the base rules, several of which carry it to hold
 * Astra off.
 */
.pg-dashboard.pg-dark .pg-slicer-trigger:hover,
.pg-dashboard.pg-dark .pg-slicer-item:hover,
.pg-dashboard.pg-dark .pg-profile-card-clear:hover,
.pg-dashboard.pg-dark .pg-breakdown-row--clickable:not(.pg-breakdown-row--active):hover {
	background: var(--pg-hover) !important;
	color: var(--pg-text) !important;
}

/* Reset All reads as blue, not red: 2026-08-26 it was a warning red that
 * looked orange on the dark surface. --pg-link carries it in both modes
 * (#007cba light / #7cc4ff dark) so it matches the popover Reset. */
.pg-dashboard.pg-dark .pg-dashboard-reset-all.pg-dashboard-reset-all {
	background-color: var(--pg-surface) !important;
	color: var(--pg-link) !important;
}

.pg-dashboard.pg-dark .pg-dashboard-reset-all.pg-dashboard-reset-all:hover:not(:disabled) {
	background-color: var(--pg-hover) !important;
	color: var(--pg-link) !important;
}

.pg-dashboard.pg-dark .pg-dashboard-dark-toggle.pg-dashboard-dark-toggle {
	background-color: var(--pg-surface) !important;
	color: var(--pg-text-muted) !important;
}

.pg-dashboard.pg-dark .pg-dashboard-dark-toggle.pg-dashboard-dark-toggle:hover {
	background-color: var(--pg-hover) !important;
	color: var(--pg-text) !important;
}

/* The popover Reset is an outlined button: its pale border and blue text
 * both need lifting, or it reads as a white bar with invisible text. */
.pg-dashboard.pg-dark .pg-slicer-reset {
	background: var(--pg-surface-2) !important;
	border-color: var(--pg-border) !important;
	color: var(--pg-link) !important;
}

.pg-dashboard.pg-dark .pg-slicer-reset:hover {
	background: var(--pg-hover) !important;
	color: var(--pg-link) !important;
}

/* ------------------------------------------------------------------ */
/* Colour tokens + dark mode                                           */
/*                                                                      */
/* ADDED 2026-08-26. Every surface, border and text colour in this file */
/* resolves through these, so the dark variant is one block of          */
/* overrides rather than a parallel stylesheet.                         */
/*                                                                      */
/* Deliberately NOT tokenised, and they must stay literal:              */
/*  - `color: #fff` — every one is white-on-dark (status badge, the map */
/*    control plate, the dimension selector). Those elements are dark in */
/*    BOTH modes; following the surface token would make them invisible. */
/*  - the map controls' rgba(26,28,30,.92) plate and rgba(255,255,255,…) */
/*    hovers, for the same reason.                                      */
/*  - the engagement/GSEC/lostness palettes, which live in              */
/*    PGDashboard.PALETTES and carry meaning, not styling.              */
/*                                                                      */
/* Dark mode is opt-in per the user's call: default light for everyone, */
/* toggled explicitly, remembered per browser. No prefers-color-scheme  */
/* block — a visitor whose OS is dark still gets the light view until   */
/* they choose otherwise.                                              */
/* ------------------------------------------------------------------ */
.pg-dashboard {
	--pg-surface: #ffffff;
	--pg-surface-2: #fafafa;
	--pg-border: #e2e2e2;
	--pg-border-soft: #eeeeee;
	--pg-text: #222222;
	--pg-text-muted: #6b7a89;
	--pg-text-dim: #777777;
	--pg-accent: #007cba;
	--pg-hover: #f4f5f6;
	--pg-pill: #eceef0;
	--pg-table-grid: #ffffff;
	--pg-link: #007cba;
}

.pg-dashboard.pg-dark {
	--pg-surface: #1b1e21;
	--pg-surface-2: #23272b;
	--pg-border: #343a40;
	--pg-border-soft: #2b3035;
	--pg-text: #e8eaed;
	--pg-text-muted: #9aa5b1;
	--pg-text-dim: #8b949e;
	/* Lifted from #007cba: the light-mode blue fails contrast on a dark
	 * surface. */
	--pg-accent: #4aa3df;
	--pg-hover: #2a2f34;
	--pg-pill: #333a41;
	--pg-table-grid: var(--pg-surface);
	/* Brighter than the accent: Astra's link blue and even #4aa3df sit too
	 * close to the dark surface to read comfortably at 13px. */
	--pg-link: #7cc4ff;

	/* Native scrollbars, form controls and the like render in the browser's
	 * dark palette — otherwise the panel and table scrollbars stay bright
	 * white tracks against a dark UI. One declaration does what manually
	 * styling ::-webkit-scrollbar would do less portably. */
	color-scheme: dark;

	background: var(--pg-surface);
	color: var(--pg-text);
}

/* The dashboard is full-bleed, so its own background has to paint the
 * whole band — otherwise the page shows through between panels. */
.pg-dashboard.pg-dark .pg-breakdown-content,
.pg-dashboard.pg-dark .pg-table-viewport {
	background: var(--pg-surface);
}

.pg-dashboard {
	width: 100vw;
	margin: 0 calc(50% - 50vw);
	box-sizing: border-box;
	padding: 0 24px;

	display: grid;
	/*
	 * 28/40/32 (2026-08-26, user's call, arrived at over three passes:
	 * even thirds, then 25/45/30, then this). This REPLACES the fixed
	 * 540px/450px side columns of 2026-08-25, and the reason those were
	 * pixels no longer applies: they were sized to their tab bars'
	 * content, and the left bar only needed 540px because its tabs carried
	 * "(ROP3)"/"(ROP2.5)"/"(ROP2)"/"(ROP1)" suffixes. Those moved to the
	 * column headers the same day, so the widths can scale with the window
	 * again.
	 *
	 * MEASURED, not guessed — the tab bars are the real constraint and
	 * they do not scale with the viewport:
	 *   left  bar needs 310px for 5 tabs  -> 28% gives ~381px at 1440
	 *   right bar needs 439px for 6 tabs  -> 33% gives ~449px at 1440
	 * 25/45/30 wrapped the right bar onto two rows, and so did 28/40/32:
	 * the requirement is the tabs PLUS the bar's own 8px padding and the
	 * 2px gaps between them, which a first measurement of the tabs alone
	 * missed by 8px. Hence 33 rather than 32 — the extra point comes from
	 * the centre, which has slack. If either bar gains a tab or a longer
	 * label, RE-MEASURE including padding rather than nudging these.
	 *
	 * The centre map's dimension selector needs ~602px for one line and
	 * does not get it at any split tried; it wraps to two rows cleanly and
	 * is no longer clipped, which was the actual bug.
	 *
	 * The max-width:782px block below collapses all three to a single
	 * column, so none of this affects mobile.
	 */
	grid-template-columns: 28fr 39fr 33fr;
	/*
	 * Body row is `auto` again (2026-08-25, revised). It was briefly capped
	 * at min(80vh, 900px) so the panes would have a definite height and
	 * therefore scroll — but the left column's real content (mini-map +
	 * legend + profile card + list panel) exceeds that, so everything in it
	 * was squeezed and the mini-map got clipped to a sliver by its pane's
	 * `overflow: hidden`.
	 *
	 * The cap is unnecessary now: `.pg-breakdown-content` carries its own
	 * max-height (ten rows left, twenty right), which is the definite height
	 * overflow-y needs. The column can grow to its content and nothing
	 * inside it has to shrink.
	 */
	grid-template-areas:
		"slicers slicers slicers"
		"stats   stats   stats"
		"left    center  right"
		"table   table   table";
	gap: 16px;
	font-family: 'Nunito Sans', Arial, sans-serif;
}

/*
 * FIX 2026-08-24: every grid item was shrink-wrapping to its content and
 * centering inside its track instead of filling it — the center column
 * rendered 178px wide inside a 744px track (so the main map was a clipped
 * sliver), the slicer bar 674px instead of 1552px, and the bottom table
 * 425px. Cause is Astra's page-builder rule
 * `.entry-content[data-ast-blocks-layout] > :not(.wp-block-group)... > *`,
 * which sets `margin-left:auto; margin-right:auto` on every child of
 * .pg-dashboard. Auto inline margins absorb the free space in a grid track
 * and make `justify-self` a no-op — which is why setting justify-self:stretch
 * (on the item) and justify-items:stretch (on the container) both changed
 * nothing here, and only zeroing the margins worked. `!important` is needed
 * because Astra's selector is more specific than `.pg-dashboard > *`.
 * Left/right columns happened to look correct only because their table
 * content is naturally wide enough to fill a 25% track.
 */
.pg-dashboard > * {
	margin-left: 0 !important;
	margin-right: 0 !important;
	min-width: 0;
}

/*
 * Sticky slicer bar (2026-08-26, user request): the dashboard's global
 * filters stay reachable while scrolling the long tables below.
 *
 * Two things make this work, both already in place for other reasons:
 * the ancestor chain's `overflow` was neutralised for the full-bleed
 * layout (any `overflow: hidden` ancestor silently disables sticky), and
 * the bar has an opaque token background so content cannot show through it.
 *
 * z-index sits above the tables' own sticky headers (2-3) and the KPI
 * strip, so a pinned table header scrolls UNDER this bar rather than
 * through it.
 */
.pg-dashboard-slicers {
	grid-area: slicers;
	position: sticky;
	top: 0;
	z-index: 20;
	background: var(--pg-surface);
}
.pg-dashboard-stats   { grid-area: stats; }
.pg-dashboard-left    { grid-area: left; }
.pg-dashboard-center  { grid-area: center; }
.pg-dashboard-right   { grid-area: right; }
.pg-dashboard-table   { grid-area: table; }

.pg-dashboard-left,
.pg-dashboard-right {
	display: flex;
	flex-direction: column;
	gap: 16px;
}

.pg-dashboard-center {
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.pg-dashboard-map {
	position: relative;
	min-height: 500px;
	flex: 1 1 auto;
}

/*
 * FIX 2026-08-24: the map rendered nothing at all — no basemap, no points,
 * not even its own zoom/layer controls — while throwing no console error.
 * MapLibre initializes onto this same div, so it carries BOTH
 * `.pg-dashboard-map-canvas` and `.maplibregl-map`. Those selectors have
 * identical specificity (0,1,0), and maplibre-gl.css is enqueued after
 * pg-dashboard.css, so `.maplibregl-map { position: relative; overflow:
 * hidden }` won: `inset: 0` stopped applying, the div's height collapsed to
 * 0 (MapLibre's own canvas is absolutely positioned, so it contributes no
 * height), and overflow:hidden then clipped the entire map away. Scoping
 * with the parent lifts this to (0,2,0) so it wins regardless of load order.
 * Do NOT flatten this back to a single class selector.
 */
.pg-dashboard-map .pg-dashboard-map-canvas {
	position: absolute;
	inset: 0;
}

.pg-dashboard-map--unavailable {
	display: flex;
	align-items: center;
	justify-content: center;
	background: #f4f4f4;
	color: var(--pg-text-dim);
	font-size: 14px;
	border-radius: 6px;
	min-height: 500px;
}

.pg-dashboard-caption {
	margin: 0;
	font-size: 12px;
	color: var(--pg-text-dim);
	text-align: center;
}

@media (max-width: 782px) {
	.pg-dashboard {
		padding: 0 12px;
		grid-template-columns: 1fr;
		/*
		 * Stacking order 2026-08-26: the two table groupings come first,
		 * then the centre map, then the lower table. On a narrow screen the
		 * desktop left-centre-right reading order would drop the tall map
		 * between the two groupings, pushing the right-hand one far below
		 * the fold; keeping the groupings adjacent means the map is scrolled
		 * past once rather than through.
		 */
		grid-template-areas:
			"slicers"
			"stats"
			"left"
			"right"
			"center"
			"table";
	}

	/*
	 * FIX 2026-08-24: these four mobile overrides never applied. They sit
	 * ABOVE the base `.pg-dashboard-slicers { grid-template-columns:
	 * repeat(4, 1fr) auto }` rule further down this file, and at equal
	 * specificity the later rule wins no matter what a media query says.
	 * So the slicer bar kept its 4-across desktop grid at every width: at
	 * 390x844 it forced the row to 471px against a 390px viewport and the
	 * whole page scrolled sideways (body.scrollWidth 484 vs innerWidth 390).
	 * Scoped with `.pg-dashboard` to win on specificity instead of moving
	 * the block, since relocating it below would place it after the
	 * max-width:640px block and change which rules win at <=640px.
	 */
	.pg-dashboard .pg-dashboard-slicers {
		grid-template-columns: repeat(2, 1fr);
	}

	.pg-slicer:nth-child(2n) {
		border-right: none;
	}

	.pg-dashboard .pg-slicer {
		border-bottom: 1px solid var(--pg-border);
	}

	.pg-dashboard .pg-dashboard-reset-all,
	.pg-dashboard .pg-dashboard-dark-toggle {
		grid-column: 1 / -1;
		border-left: none;
		border-top: 1px solid var(--pg-border);
	}
}

/* -------------------------------------------------------------------- */
/* Top slicer bar (Indigenous / Affinity Group / Country / Religion)      */
/*                                                                          */
/* Matches the reference ArcGIS Dashboard's own selector styling (checked  */
/* against real screenshots of its open dropdowns, 2026-08-10): a full-    */
/* width row of 4 equal boxes with divider borders (not separate pill      */
/* buttons), each showing the dimension label stacked over its current     */
/* value, opening a single-select radio-list popover with an optional      */
/* search box and a Reset button.                                          */
/*                                                                          */
/* FIX 2026-08-10: added a 5th, differently-sized element — a global       */
/* "Reset All Filters" button (`auto` width, not `1fr` like the 4          */
/* dropdowns) so the user can clear every active filter across the whole   */
/* dashboard in one click, not just one dimension at a time.               */
/* -------------------------------------------------------------------- */

.pg-dashboard-slicers {
	display: grid;
	/* Two trailing auto columns: Reset All Filters, then the dark toggle. */
	grid-template-columns: repeat(4, 1fr) auto auto;
	border: 1px solid var(--pg-border);
	border-radius: 6px;
	background: var(--pg-surface);
	overflow: visible;
}

.pg-slicer {
	position: relative;
	border-right: 1px solid var(--pg-border);
}

.pg-dashboard-reset-all {
	padding: 10px 18px;
	border: none;
	border-left: 1px solid var(--pg-border);
	border-radius: 0 6px 6px 0;
	background: var(--pg-surface);
	color: var(--pg-link);
	font-size: 13px;
	font-weight: 700;
	cursor: pointer;
	white-space: nowrap;
	appearance: none;
	-webkit-appearance: none;
}

/*
 * SPECIFICITY ARMOUR 2026-08-26 — same problem, same shape as the
 * `.pg-breakdown-tab.pg-breakdown-tab` armour further down, and it must not
 * be simplified back to a single class. Astra styles `button` globally with
 * an orange fill on hover, using a selector more specific than one class, so
 * a plain `.pg-dashboard-reset-all:hover { background: ... }` LOST even with
 * `!important` — the button went solid orange with white text on hover in
 * both modes. Doubling the class matches exactly the same element at higher
 * specificity. `background-image` and `box-shadow` are listed explicitly:
 * Astra paints a gradient and a shadow, and clearing only `background`
 * leaves both behind.
 */
.pg-dashboard-reset-all.pg-dashboard-reset-all:hover:not(:disabled),
.pg-dashboard-dark-toggle.pg-dashboard-dark-toggle:hover {
	background-color: var(--pg-hover) !important;
	background-image: none !important;
	box-shadow: none !important;
	border-color: var(--pg-border) !important;
}

.pg-dashboard-reset-all.pg-dashboard-reset-all:hover:not(:disabled) {
	color: var(--pg-link) !important;
}

.pg-dashboard-dark-toggle.pg-dashboard-dark-toggle:hover {
	color: var(--pg-text) !important;
}

/* Resting state needs the same armour — Astra's fill is not hover-only. */
.pg-dashboard-reset-all.pg-dashboard-reset-all,
.pg-dashboard-dark-toggle.pg-dashboard-dark-toggle {
	background-color: var(--pg-surface) !important;
	background-image: none !important;
	box-shadow: none !important;
	border-radius: 0 !important;
}

.pg-dashboard-reset-all.pg-dashboard-reset-all {
	color: var(--pg-link) !important;
}

.pg-dashboard-reset-all.pg-dashboard-reset-all:disabled {
	color: #ccc !important;
}

.pg-dashboard-dark-toggle.pg-dashboard-dark-toggle {
	color: var(--pg-text-muted) !important;
}

.pg-dashboard-reset-all:disabled {
	color: #ccc;
	cursor: default;
}

.pg-slicer-trigger {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: 2px;
	width: 100%;
	padding: 10px 16px;
	border: none;
	border-radius: 0;
	background: none;
	cursor: pointer;
	text-align: left;
	/* Buttons carry a native rounded-corner appearance in some browsers
	 * (Safari especially) that only becomes visible once :hover fills in a
	 * background color — border-radius:0 alone doesn't override it in every
	 * engine, appearance:none is the actual fix. */
	appearance: none;
	-webkit-appearance: none;
}

.pg-slicer-trigger:hover {
	background: #f7f9fb;
}

.pg-slicer-trigger-label {
	font-size: 13px;
	font-weight: 700;
	color: var(--pg-text);
}

.pg-slicer-trigger-value {
	font-size: 12px;
	color: var(--pg-text-muted);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	max-width: 100%;
}

.pg-slicer-popover {
	position: absolute;
	top: calc(100% + 2px);
	left: 0;
	z-index: 20;
	width: 260px;
	padding: 10px;
	background: var(--pg-surface);
	border: 1px solid #ccc;
	border-radius: 4px;
	box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15);
}

.pg-slicer-search {
	width: 100%;
	box-sizing: border-box;
	margin-bottom: 8px;
	padding: 6px 8px;
	border: 1px solid #ccc;
	border-radius: 4px;
	font-size: 13px;
}

.pg-slicer-list {
	list-style: none;
	margin: 0 0 10px;
	padding: 0;
	max-height: 220px;
	overflow-y: auto;
}

.pg-slicer-item {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 6px 4px;
	font-size: 13px;
	cursor: pointer;
}

.pg-slicer-item:hover .pg-slicer-item-label {
	text-decoration: underline;
}

.pg-slicer-item input[type="radio"] {
	accent-color: var(--pg-accent);
	margin: 0;
	pointer-events: none;
}

.pg-slicer-item-label {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	color: var(--pg-text);
}

.pg-slicer-item--selected .pg-slicer-item-label {
	color: var(--pg-accent);
	font-weight: 700;
}

.pg-slicer-item:first-child .pg-slicer-item-label {
	color: var(--pg-text);
	font-weight: 700;
}

.pg-slicer-reset {
	display: block;
	width: 100%;
	padding: 7px;
	background: var(--pg-surface);
	border: 1px solid #a7cbe6;
	border-radius: 4px;
	color: #4f9bd6;
	font-size: 13px;
	cursor: pointer;
}

.pg-slicer-reset:hover {
	background: #f0f7fc;
}

/* -------------------------------------------------------------------- */
/* KPI tile strip                                                         */
/*                                                                          */
/* Matches the reference ArcGIS Dashboard's own indicator row (checked     */
/* against a screenshot, 2026-08-10): one bordered, connected row of      */
/* equal-width tiles with thin dividers between them — not separate        */
/* rounded cards with gaps and a colored accent border. The 4 ROP tiles    */
/* (People Across Country/Ethne/Cluster/Affinity) have blue-tinted labels  */
/* in the reference; the rest don't.                                       */
/*                                                                          */
/* FIX 2026-08-10: back to 9 tiles. "Total People Groups"/"Total           */
/* Population" (fixed, never-filtered sitewide reference counts) are        */
/* stacked inside the "People Groups"/"Population" tiles alongside their    */
/* "Selected" counterparts, rather than each being its own adjacent tile.   */
/* -------------------------------------------------------------------- */

.pg-stat-tiles {
	display: flex;
	flex-wrap: wrap;
	border: 1px solid var(--pg-border);
	border-radius: 6px;
	background: var(--pg-surface);
	margin-bottom: 16px;
	overflow: hidden;
}

/*
 * CHANGE 2026-08-25: tiles size to their own content and wrap, rather than
 * dividing the row into equal fractions. Each tile now reads
 * "<selected> of <total>", and those strings differ enormously in width —
 * "17 of 17" against "8,288,841,619 of 8,288,841,619" — so equal columns
 * would either clip the widest or waste most of the narrowest. `flex-wrap`
 * also means a row too wide for its container drops to a second line
 * instead of being swallowed by the strip's `overflow: hidden`, which is
 * what produced the clipped "5,06 Languag" bug on mobile. That is why the
 * max-width:1100px `repeat(3, ...)` override is gone: wrapping handles
 * narrow viewports on its own, with no breakpoint to keep in sync.
 */
.pg-stat-tile {
	flex: 1 0 auto;
	padding: 12px 14px;
	border-right: 1px solid var(--pg-border);
	text-align: center;
}

.pg-stat-tile:last-child {
	border-right: none;
}

/* "<selected> of <total>" on one baseline. */
.pg-stat-tile-figure {
	display: flex;
	align-items: baseline;
	justify-content: center;
	gap: 5px;
	white-space: nowrap;
}

.pg-stat-tile-selected {
	font-size: 20px;
	font-weight: 700;
	line-height: 1.2;
	color: var(--pg-text);
}

.pg-stat-tile-of {
	font-size: 11px;
	font-weight: 400;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	color: var(--pg-text-muted);
}

.pg-stat-tile-total {
	font-size: 16px;
	font-weight: 400;
	line-height: 1.2;
	color: var(--pg-text-muted);
}

.pg-stat-tile-label {
	font-size: 11px;
	color: var(--pg-text-muted);
	margin-top: 2px;
}

/*
 * Wrapped rows need their own separator: with flex-wrap there is no way to
 * target "last tile in a visual row", so every tile carries a bottom border
 * and the strip drops its own, letting the final row's tile borders form the
 * strip's bottom edge. Single row and wrapped rows then look identical, with
 * no doubled line where the two would otherwise meet.
 *
 * The former `@media (max-width: 1100px)` block that re-gridded this strip to
 * `repeat(3, ...)` — and the `.pg-stat-tile-pair*` rules for the old stacked
 * Selected/Total markup — were removed on 2026-08-25 along with the markup
 * they styled. Wrapping covers narrow viewports without a breakpoint.
 */
.pg-stat-tiles {
	border-bottom: none;
}

.pg-stat-tile {
	border-bottom: 1px solid var(--pg-border);
}

/* -------------------------------------------------------------------- */
/* Breakdown / ranked-list tab widget (left + right column panels)        */
/* -------------------------------------------------------------------- */

.pg-dashboard-breakdown {
	display: flex;
	flex-direction: column;
	border: 1px solid var(--pg-border);
	border-radius: 6px;
	overflow: hidden;
	background: var(--pg-surface);
}

.pg-dashboard-left > .pg-dashboard-breakdown:last-child,
.pg-dashboard-right > .pg-dashboard-breakdown:last-child {
	flex: 1 1 auto;
	min-height: 320px;
}

.pg-breakdown-tabbar {
	display: flex;
	flex-wrap: wrap;
	gap: 2px;
	padding: 6px 4px;
	/* RESTYLE 2026-08-25: no divider rule, no grey bar. The reference design
	 * carries the tab strip on whitespace alone; a bottom border and a filled
	 * background were both doing work the active pill now does by itself. */
	background: transparent;
}

/*
 * FIX 2026-08-25: `display: flex` above outranks the browser's built-in
 * `[hidden] { display: none }`, so setting `.hidden = true` in JS (done for
 * the map-only tab group, whose selection moved to the centre map's radio)
 * had no visible effect — the element reported hidden while still rendering.
 * Caught by looking at a screenshot; a property check said hidden and was
 * wrong. Any element given `display` in CSS needs this companion rule for
 * the hidden attribute to mean anything.
 */
.pg-breakdown-tabbar[hidden] {
	display: none;
}

/*
 * RESTYLE 2026-08-25 to the minimal reference the user supplied: plain text
 * tabs with no borders or underline, the ACTIVE one carrying a soft grey
 * rounded pill. Hover gets a lighter version of the same pill, so the
 * affordance and the selected state are the same shape rather than two
 * different visual languages (an underline for one, a fill for the other).
 *
 * KEPT FROM 2026-08-11 — do not simplify away: Astra's own <button> styling
 * bleeds orange through at multiple points including the RESTING state, not
 * just hover/focus/active. Every property here stays at boosted specificity
 * (`.pg-breakdown-tab.pg-breakdown-tab` — same match, higher specificity
 * than one class) with `!important`, because a plain single-class
 * `!important` rule repeatedly lost to the theme's own more-specific
 * `!important` rule. The blue underline is gone, but the specificity armour
 * it needed is still required for the background and colour.
 */
.pg-breakdown-tab.pg-breakdown-tab {
	/* 8px horizontal, not 12px — buys ~48px across a six-tab bar, enough for
	 * the Region…Language group to sit on one line at the current column
	 * width instead of wrapping. */
	padding: 6px 8px;
	border: none !important;
	border-radius: 16px !important;
	background-color: transparent !important;
	background-image: none !important;
	box-shadow: none !important;
	font-size: 12px;
	font-weight: 400;
	cursor: pointer;
	color: var(--pg-text) !important;
	white-space: nowrap;
}

.pg-breakdown-tab.pg-breakdown-tab:hover,
.pg-breakdown-tab.pg-breakdown-tab:focus {
	background-color: var(--pg-hover) !important;
	background-image: none !important;
	color: var(--pg-text) !important;
}

/*
 * Same boosted specificity as the base rule: without it the active pill
 * would lose to that rule's `!important` transparent background the moment
 * the pointer left, and the selected tab would stop looking selected.
 */
.pg-breakdown-tab.pg-breakdown-tab--active {
	background-color: var(--pg-pill) !important;
	color: var(--pg-text) !important;
	font-weight: 600;
}

/*
 * CHANGE 2026-08-25: the content area grows to fill its panel rather than
 * stopping at a fixed 380px.
 *
 * The left and right columns are grid siblings, so both stretch to the
 * taller one's height. With a fixed cap the right column's table ended
 * partway down and left a large empty area inside the bordered panel —
 * visible whitespace that looked like a rendering fault. Flexing the
 * content area means that space becomes more visible rows instead, and the
 * table still scrolls internally once it runs out.
 *
 * min-height: 0 is required: a flex item's default min-height is auto,
 * which refuses to shrink below its content and would push the panel taller
 * than its column instead of scrolling inside it.
 */
/* `flex: 0 0 auto` — these panels size to their content and must never be
 * squeezed by a sibling; the mini-map being clipped is exactly what happens
 * when they can shrink. */
[data-pg-breakdown] {
	display: flex;
	flex-direction: column;
	flex: 0 0 auto;
	min-height: 0;
}

/*
 * Row counts are expressed as a height because the tables are virtualised
 * by scrolling, not by slicing the row array — the fetch keeps topping up
 * (see ensureFilled) until the pane overflows, so what the user sees is
 * governed by how tall the pane is. A panel row is ~30px; the header adds
 * ~32px. Measured on the live panels rather than assumed: a row is ~35px,
 * so ten rows is 32 + 353 and twenty is 32 + 706.
 */
.pg-breakdown-content {
	overflow-y: auto;
	flex: 0 1 auto;
	min-height: 0;
}

/* Left column: ten rows. Measured at ~39px per row here — the list tab's
 * rows sit noticeably taller than the category tables' (43px vs 34px),
 * plus a ~32px header. */
.pg-dashboard-left .pg-breakdown-content {
	max-height: 466px;
}

/*
 * While a profile card is open the list panel gives up the height the card
 * takes, instead of the column growing by that much (2026-08-25, user
 * request). The card runs ~190px once populated, and without this every
 * click on a map point pushed the whole column — and the lower table with
 * it — that far down the page.
 *
 * The panel keeps ~6 rows and stays scrollable, so nothing becomes
 * unreachable; the class is toggled from showProfileCard/clearProfileCard.
 */
.pg-dashboard-left.pg-has-profile .pg-breakdown-content {
	max-height: 276px;
}

/* Right column: twenty rows at ~33.6px each, plus the header. */
.pg-dashboard-right .pg-breakdown-content {
	max-height: 704px;
}

/*
 * FIX 2026-08-10: mini-maps (Engagement Progress/GSEC/Lostness tabs) +
 * dedicated profile-card panel, per the user's request — colored points +
 * a clickable legend like the site's existing standalone SPI/GSEC/LPI
 * Mapbox maps, but scaled down and with click-to-profile-card instead of a
 * popup. `.pg-breakdown-map-canvas` needs an explicit height for MapLibre
 * to have any geometry to render into — a 0-height container is a common,
 * easy-to-miss way for a map to silently fail to appear.
 */
.pg-breakdown-map-pane {
	display: flex;
	flex-direction: column;
	overflow: hidden;
}

/*
 * The `hidden` attribute (set via JS as a plain DOM property, see
 * updatePaneVisibility() in pg-dashboard-breakdown.js) only hides an
 * element via the UA stylesheet's `[hidden] { display: none; }` rule —
 * which has the SAME specificity as the class selector above, and author
 * rules win ties over UA rules. Without this override, toggling `.hidden`
 * would have no visible effect at all.
 */
.pg-breakdown-map-pane[hidden] {
	display: none;
}

.pg-breakdown-map-canvas {
	position: relative;
	height: 220px;
	flex-shrink: 0;
}

/*
 * FIX 2026-08-11: max-height raised from 160px to fit GSEC/Lostness's 7
 * legend items without an internal scrollbar (Engagement Progress only has
 * 3, so it just leaves some empty space below — a deliberate tradeoff for
 * consistent height across all 3 mini-map tabs, since they share this same
 * persistent DOM node). overflow-y:auto stays as a safety net in case a
 * dimension ever gets more categories than fit even at this height.
 */
/*
 * FIX 2026-08-25: the legend collapsed to a ~40px scrolling strip. The map
 * canvas is `flex-shrink: 0`, so once the column gained a definite height
 * (grid-template-rows, added the same day) the legend was the only flex
 * item left able to shrink — and flex items shrink below their content
 * happily. `flex-shrink: 0` here too: this legend is the map's key AND its
 * filter control, so it should never be the thing that gives way.
 */
.pg-breakdown-legend {
	flex-shrink: 0;
	overflow-y: auto;
	max-height: 240px;
	padding: 8px 10px;
	border-top: 1px solid var(--pg-border);
}

.pg-breakdown-legend-hint {
	margin: 0 0 6px;
	font-size: 11px;
	font-style: italic;
	color: var(--pg-text-dim);
}

.pg-breakdown-legend-item {
	display: flex;
	align-items: center;
	gap: 8px;
	padding: 4px 2px;
	font-size: 12px;
	cursor: pointer;
}

.pg-breakdown-legend-item:hover {
	background: var(--pg-surface-2);
}

.pg-breakdown-legend-item--active {
	font-weight: 700;
}

.pg-breakdown-legend-swatch {
	width: 12px;
	height: 12px;
	border-radius: 50%;
	flex-shrink: 0;
}

.pg-breakdown-legend-label {
	flex: 1 1 auto;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

.pg-breakdown-legend-count {
	flex-shrink: 0;
	color: var(--pg-text-dim);
	font-size: 11px;
}

/* -------------------------------------------------------------------- */
/* Dedicated profile-card panel (mini-map click target)                   */
/* -------------------------------------------------------------------- */

.pg-dashboard-profile-card {
	position: relative;
	display: flex;
	gap: 10px;
	padding: 12px;
	border: 1px solid var(--pg-border);
	border-radius: 6px;
	background: var(--pg-surface);
	font-size: 12.5px;
	line-height: 1.4;
}

/*
 * FIX 2026-08-11: clear button, per the user's request — lets the user
 * deselect the current people group and go back to the placeholder
 * instructions, rather than the card staying populated indefinitely.
 * Absolutely positioned in the corner rather than taking up flex-row
 * space, since it needs to sit above whichever content (placeholder text
 * OR the photo+body pair) happens to be rendered inside the card.
 */
/*
 * FIX 2026-08-11: the browser's default <button> padding was never reset
 * here, so it was adding to the fixed width/height instead of being
 * contained within them — rendered as a stretched oval instead of a
 * circle (confirmed via screenshot), same root cause as the slicer
 * trigger's earlier hover-radius bug (native button chrome bleeding
 * through). padding:0 + box-sizing:border-box fixes the shape;
 * flex-centering fixes the "×" glyph's position within it, which default
 * button line-height/vertical-align was leaving slightly off-center.
 */
.pg-profile-card-clear {
	position: absolute;
	top: 4px;
	right: 4px;
	width: 20px;
	height: 20px;
	padding: 0;
	margin: 0;
	box-sizing: border-box;
	display: flex;
	align-items: center;
	justify-content: center;
	border: none;
	border-radius: 50%;
	background: var(--pg-border-soft);
	color: var(--pg-text-dim);
	font-size: 14px;
	line-height: 1;
	cursor: pointer;
	appearance: none;
	-webkit-appearance: none;
}

.pg-profile-card-clear:hover {
	background: var(--pg-border);
	color: var(--pg-text);
}

.pg-profile-card-placeholder {
	margin: 0;
	color: var(--pg-text-dim);
	font-style: italic;
}

.pg-profile-card-photo {
	width: 80px;
	flex-shrink: 0;
	text-align: center;
}

.pg-profile-card-photo img {
	width: 100%;
	height: auto;
	display: block;
	border-radius: 4px;
}

.pg-profile-card-credit {
	font-size: 10px;
	color: var(--pg-text-dim);
	margin-top: 2px;
}

.pg-profile-card-body {
	flex: 1;
	min-width: 0;
}

.pg-profile-card-body p {
	margin: 0;
}

.pg-profile-card-meta {
	margin-top: 6px;
	font-size: 11px;
	color: var(--pg-text-dim);
}

/*
 * FIX 2026-08-10: table-layout:fixed + fixed column widths + ellipsis on
 * the name cell — without this, long names (e.g. "African Traditional
 * Religion", "Papua New Guinea") pushed the table wider than its column
 * (25% of the page as of the later 2026-08-10 width bump), forcing a
 * horizontal scrollbar the user explicitly doesn't want. Percentage-based
 * column widths here (46/27/27) are already relative to whatever the
 * actual left/right column width ends up being, so this table needed no
 * further change when that column went from a fixed 330px to 25%.
 * Column widths deliberately don't sum with any border/padding slack; the
 * name column gets the majority share since it holds the most variable
 * content, the two number columns just need to fit formatted totals like
 * "8,288,450,519" without wrapping.
 */
/*
 * Sticky header + row caps (2026-08-25, per the user): left panels show ten
 * rows, right panels twenty, each scrolling with the header pinned.
 *
 * border-collapse must be `separate` for sticky cells — with `collapse` the
 * shared borders belong to the table rather than the cells, so they scroll
 * away and the pinned header loses its rule. Same reason as the lower table.
 */
.pg-breakdown-table thead th {
	position: sticky;
	top: 0;
	z-index: 1;
	/* #fafafa to match the lower table's header (2026-08-25). An opaque
	 * background is also load-bearing for a sticky header — without it the
	 * rows scroll visibly through the heading. */
	background: var(--pg-surface-2);
	border-bottom: 1px solid var(--pg-border);
}

.pg-breakdown-table {
	width: 100%;
	table-layout: fixed;
	border-collapse: separate;
	border-spacing: 0;
	font-size: 12.5px;
}

.pg-breakdown-th {
	position: sticky;
	top: 0;
	text-align: left;
	padding: 6px 8px;
	background: var(--pg-surface);
	border-bottom: 2px solid var(--pg-border-soft);
	font-size: 11px;
	text-transform: uppercase;
	letter-spacing: 0.02em;
	color: var(--pg-text-dim);
	cursor: pointer;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.pg-breakdown-th:hover {
	color: var(--pg-accent);
}

/*
 * LEFT GROUP column ratio (2026-08-25, set by the user): 180 / 130 / 110,
 * expressed proportionally so the columns keep that balance at any width
 * rather than being pinned to those exact pixels. The three sum to 420,
 * which is also the cap on the table itself — so at full width the columns
 * land on their nominal sizes, and below it they scale together.
 *
 * Scoped to .pg-dashboard-left: the right group keeps its own split, where
 * the count column was widened by 10px for its header.
 *
 * The PGID list tab has only two columns, so the third rule simply does not
 * match there and the browser distributes the remainder.
 */
/*
 * REVISED 2026-08-25: the 420px cap left the table short of its 500px
 * column. The numeric columns hold the sizes from the user's 180/130/110
 * ratio as fixed widths, and the name column takes everything else — so the
 * table fills whatever width the column has, and the ratio still holds at
 * 420px.
 */
.pg-dashboard-left .pg-breakdown-table {
	width: 100%;
}

/*
 * The name column takes whatever the other two leave, rather than a fixed
 * share (2026-08-25). With three columns that still resolves to the 180px
 * of the user's ratio; with the two-column PGID list it absorbs the ~110px
 * that a fixed 42.857% was leaving empty, so long names truncate far less.
 */
.pg-dashboard-left .pg-breakdown-th:first-child,
.pg-dashboard-left .pg-breakdown-table td:first-child {
	width: auto;
}

.pg-dashboard-left .pg-breakdown-th:nth-child(2),
.pg-dashboard-left .pg-breakdown-table td:nth-child(2) {
	width: 130px;
}

.pg-dashboard-left .pg-breakdown-th:nth-child(3),
.pg-dashboard-left .pg-breakdown-table td:nth-child(3) {
	width: 110px;
}

/* The name column gives up the 10px that the "PEOPLE GROUPS" header needs
 * to sit on one line without truncating (2026-08-25). */
.pg-breakdown-th:first-child,
.pg-breakdown-table td:first-child {
	width: calc(46% - 10px);
}

.pg-breakdown-th:nth-child(2),
.pg-breakdown-table td:nth-child(2) {
	width: calc(27% + 10px);
}

.pg-breakdown-th:nth-child(3),
.pg-breakdown-table td:nth-child(3) {
	width: 27%;
}

/*
 * FIX 2026-08-25: Astra's `table, td, th { border: 1px solid
 * var(--ast-border-color) }` paints edges blue (rgb(43,95,148)). These
 * tables only ever set `border-bottom`, so everything else kept the theme's
 * blue and drew a blue grid through the panels.
 *
 * REVISED: the first pass covered only td/th, which left the <table>
 * element, thead and tr still blue. Every table element is now neutralised
 * in one rule, and the separators that should be visible are re-asserted
 * after it — so this must stay ABOVE those rules in the file.
 */
.pg-breakdown-table,
.pg-breakdown-table thead,
.pg-breakdown-table tbody,
.pg-breakdown-table tr,
.pg-breakdown-table th,
.pg-breakdown-table td {
	/* Token, not literal white (2026-08-26): in dark mode a white grid
	 * blazes through the panels. It resolves to white in light mode, which
	 * is what the "make the blue borders white" request asked for, and to
	 * the panel surface in dark. */
	border-color: var(--pg-table-grid);
}

.pg-breakdown-table td {
	padding: 6px 8px;
	border-bottom: 1px solid var(--pg-border-soft);
}

.pg-breakdown-table td:first-child {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* The count and population headers align right with their values
 * (2026-08-25) — a right-aligned number under a left-aligned heading reads
 * as two different columns. */
.pg-breakdown-th:nth-child(2),
.pg-breakdown-th:nth-child(3) {
	text-align: right;
}

.pg-breakdown-num {
	text-align: right;
}

.pg-breakdown-row--clickable {
	cursor: pointer;
}

/*
 * The active row is excluded from hover in BOTH modes (2026-08-26). Hover
 * and "this row is the current filter" are two different messages, and the
 * hover fill is the later/equal-specificity rule, so without the exclusion
 * pointing at the selected row silently repaints it as an ordinary one --
 * the selection appears to vanish exactly while you are looking at it. The
 * dark override carries the matching `:not()`.
 */
.pg-breakdown-row--clickable:not(.pg-breakdown-row--active):hover {
	background: var(--pg-surface-2);
}

.pg-breakdown-row--active {
	background: #e7f3fb;
}

.pg-breakdown-row--active td:first-child {
	color: var(--pg-accent);
	font-weight: 700;
}

.pg-breakdown-empty {
	padding: 16px;
	text-align: center;
	color: var(--pg-text-dim);
}

.pg-breakdown-pagination {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 10px;
	padding: 6px;
	font-size: 12px;
	border-top: 1px solid var(--pg-border-soft);
}

.pg-breakdown-pagination button {
	padding: 3px 8px;
	border: 1px solid #ccc;
	border-radius: 4px;
	background: var(--pg-surface);
	cursor: pointer;
}

.pg-breakdown-pagination button:disabled {
	opacity: 0.4;
	cursor: default;
}

/* -------------------------------------------------------------------- */
/* People-group table                                                     */
/* -------------------------------------------------------------------- */

.pg-table-export-bar {
	display: flex;
	justify-content: flex-end;
	gap: 16px;
	margin-bottom: 8px;
	font-size: 12px;
}

.pg-table-export-link {
	color: var(--pg-accent);
	text-decoration: none;
}

.pg-table-export-link:hover {
	text-decoration: underline;
}

.pg-table-export-link--full {
	color: var(--pg-text-dim);
}

/*
 * CHANGE 2026-08-25: the table grew from 4 columns to every dimension the
 * dashboard exposes, so it no longer fits the viewport. It scrolls inside
 * its own container rather than widening the page — the dashboard is
 * full-bleed, so a table pushing past 100vw would give the whole page a
 * horizontal scrollbar. `width: auto` with `min-width: 100%` lets it fill
 * the space when it can and exceed it when it must; `width: 100%` alone
 * would crush twenty columns into the viewport instead of scrolling.
 */
.pg-dashboard-table {
	overflow: visible;
}

/*
 * CHANGE 2026-08-25: the table scrolls inside a fixed-height viewport
 * instead of paginating, at the user's request (~10 rows, then load more on
 * scroll).
 *
 * This also fixes how unreachable the horizontal scroll was. Previously the
 * whole 25-row table scrolled sideways, which put its horizontal scrollbar
 * at the very bottom of a tall block — usually below the fold, so the extra
 * columns were effectively undiscoverable. A short viewport keeps that
 * scrollbar on screen next to the rows it controls.
 *
 * 580px is about ten 58px rows plus the header. Height is capped rather
 * than fixed so a filter returning three rows does not leave a large void.
 */
.pg-table-viewport {
	max-height: 580px;
	overflow: auto;
	border: 1px solid var(--pg-border);
	border-radius: 6px;
}

/* border-collapse: separate is required for sticky cells — with `collapse`
 * the shared borders belong to the table, not the cell, so they scroll away
 * and leave sticky headers looking detached. Borders move onto the cells
 * themselves below. */
.pg-table {
	width: auto;
	min-width: 100%;
	border-collapse: separate;
	border-spacing: 0;
	font-size: 13px;
}

.pg-table thead th {
	position: sticky;
	top: 0;
	z-index: 2;
	background: var(--pg-surface-2);
	border-bottom: 1px solid var(--pg-border);
}

/*
 * The first two columns stay put while the rest scroll sideways, so a row
 * remains identifiable at any horizontal offset — scrolling to "Ethne
 * (ROP2.5)" is useless if you can no longer see which people group the
 * value belongs to. Widths are fixed because a sticky column's `left`
 * offset has to be a known number.
 */
.pg-table th:nth-child(1),
.pg-table td:nth-child(1) {
	position: sticky;
	left: 0;
	z-index: 1;
	width: 60px;
	min-width: 60px;
	background: var(--pg-surface);
}

.pg-table th:nth-child(2),
.pg-table td:nth-child(2) {
	position: sticky;
	left: 60px;
	z-index: 1;
	width: 104px;
	min-width: 104px;
	background: var(--pg-surface);
	border-right: 1px solid var(--pg-border);
}

/* Corner cells outrank both sticky axes. */
.pg-table thead th:nth-child(1),
.pg-table thead th:nth-child(2) {
	z-index: 3;
	background: var(--pg-surface-2);
}

.pg-breakdown-status {
	padding: 6px 4px;
	font-size: 11px;
	color: var(--pg-text-muted);
	text-align: center;
}

.pg-table-status {
	padding: 8px 4px;
	font-size: 12px;
	color: var(--pg-text-muted);
	text-align: center;
}

.pg-table th,
.pg-table .pg-table-cell {
	white-space: nowrap;
}

.pg-table th {
	text-align: left;
	padding: 8px 10px;
	border-bottom: 2px solid var(--pg-border-soft);
	font-size: 12px;
	text-transform: uppercase;
	letter-spacing: 0.02em;
	color: var(--pg-text-dim);
}

.pg-table-sort-th {
	cursor: pointer;
	white-space: nowrap;
}

.pg-table-sort-th:hover {
	color: var(--pg-accent);
}

/* Same Astra blue-border override as the panel tables above, in the lower
 * table's own grey — and covering the table/thead/tr elements, not just the
 * cells. */
.pg-table,
.pg-table thead,
.pg-table tbody,
.pg-table tr,
.pg-table th,
.pg-table .pg-table-cell {
	border-color: var(--pg-border);
}

.pg-table .pg-table-cell {
	padding: 8px 10px;
	border-bottom: 1px solid var(--pg-border-soft);
	vertical-align: middle;
}

/*
 * FIX 2026-08-25: photos rendered 0px wide (40px tall) after the table
 * changed from `width: 100%` to `width: auto` earlier the same day.
 *
 * Astra's `img { max-width: 100% }` resolves against the containing cell.
 * While the table had a definite width the cell did too, so 100% was a real
 * number. With an auto-width table the photo cell sizes to its content and
 * the image sizes to the cell — a circular dependency the browser settles
 * at zero, leaving a loaded image (naturalWidth > 0, no request failure)
 * painted at no width. It looked like missing data and wasn't.
 *
 * `max-width: none` breaks the cycle: the 40px below becomes the definite
 * size, and the cell sizes to it. min-width keeps the column from being
 * squeezed by its neighbours.
 */
.pg-table-photo {
	width: 40px;
	min-width: 40px;
	max-width: none;
	height: 40px;
	object-fit: cover;
	border-radius: 4px;
	display: block;
}

/*
 * The Status column was removed entirely on 2026-08-25 — its pill said
 * "Active" on nearly every row, and the colour it carried actually encoded
 * EngagementProgress, which now shows as a dot beside that column's own
 * value instead. This pill rule stays because the profile card and map
 * popups still use it.
 */
.pg-table-status-badge {
	display: inline-block;
	padding: 2px 8px;
	border-radius: 10px;
	color: #fff;
	font-size: 11px;
	white-space: nowrap;
}

/*
 * Legend colour swatch shown before a dimension's value (Engagement
 * Progress / GSEC / Lostness), 2026-08-25. Replaced a separate Status
 * column of bare dots: the colour belongs next to the value it describes,
 * and this way the table reads the same as the map legends beside it.
 */
.pg-table-dot {
	display: inline-block;
	width: 10px;
	height: 10px;
	min-width: 10px;
	border-radius: 50%;
	margin-right: 6px;
	vertical-align: baseline;
}

.pg-table-empty {
	padding: 20px;
	text-align: center;
	color: var(--pg-text-dim);
}

.pg-table-pagination {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 16px;
	margin-top: 12px;
	font-size: 13px;
}

.pg-table-pagination button {
	padding: 6px 12px;
	border: 1px solid #ccc;
	border-radius: 4px;
	background: var(--pg-surface);
	cursor: pointer;
}

.pg-table-pagination button:disabled {
	opacity: 0.4;
	cursor: default;
}

/* -------------------------------------------------------------------- */
/* Map controls                                                           */
/* -------------------------------------------------------------------- */

/*
 * CHANGE 2026-08-25: `.pg-dashboard-layer-toggle` (Points/Polygons) became
 * `.pg-dashboard-type-toggle` (GSEC/Frontier/Pioneer, plus a fourth later).
 * Same chrome as the control it replaces — white maplibregl-ctrl-group box,
 * 12px, radio per option — but laid out in a ROW rather than a column, per
 * the user's reference to how that selector was designed. `flex-wrap` keeps
 * it usable as types are added or on a narrow map; `white-space: nowrap` on
 * the label stops a two-word type breaking across lines mid-option.
 */
/*
 * CONTRAST 2026-08-25: white text on a dark ground, at the user's request.
 * This control floats directly over map tiles whose brightness varies from
 * near-white ice to dark ocean, so dark-on-light was legible in some places
 * and not others. Light text on its own dark plate is readable wherever it
 * lands, and reverses the earlier white box that competed with the basemap.
 *
 * The plate is near-opaque rather than a light tint: transparency here would
 * put map detail directly behind small text and undo the point. `!important`
 * on the background overrides MapLibre's own `.maplibregl-ctrl-group`
 * white, which this element also carries.
 */
.pg-dashboard-type-toggle {
	display: flex;
	flex-direction: row;
	flex-wrap: wrap;
	/*
	 * FIX 2026-08-26: the selector was being clipped by the map's edge
	 * ("Global Status of Evangelical Christian…"). It needs ~590px, more
	 * than the centre column gives it, so it must be capped for flex-wrap
	 * to engage at all — an unconstrained absolutely-positioned control
	 * overflows and gets cut off by the map's `overflow: hidden` rather
	 * than wrapping.
	 *
	 * REVISED the same day, when the columns went to even thirds: the cap
	 * was `calc(100vw - 1040px)`, derived from the old fixed 540+450 side
	 * columns, and silently became wrong the moment those widths changed.
	 * Measuring against the control's own containing block instead means it
	 * tracks the map at any column width, with no arithmetic to keep in
	 * sync — the class of hardcoded duplicate that keeps biting this
	 * codebase.
	 */
	max-width: calc(100% - 20px);
	box-sizing: border-box;
	align-items: center;
	gap: 4px 14px;
	padding: 7px 12px;
	font-size: 12px;
	font-weight: 600;
	color: #fff;
	background: rgba(26, 28, 30, 0.92) !important;
	border-radius: 4px;
	box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}

.pg-dashboard-type-toggle label {
	display: flex;
	align-items: center;
	gap: 5px;
	cursor: pointer;
	/*
	 * Was `nowrap` (2026-08-26): with the centre column narrowed, "Global
	 * Status of Evangelical Christianity" could not fit on one line, and a
	 * nowrap label simply overflowed the control and was cut off by the
	 * map's overflow: hidden. Allowing it to wrap keeps the full name the
	 * user asked for instead of truncating it.
	 */
	white-space: normal;
	color: #fff;
}

/* Default radios render as dark-on-dark here; accent-color keeps the checked
 * dot visible without replacing the native control. */
.pg-dashboard-type-toggle input[type="radio"] {
	accent-color: #fff;
	margin: 0;
}

/*
 * MAP CONTROLS 2026-08-25: zoom, compass, fullscreen, reset and lasso all
 * wear the same dark semi-transparent plate as the dimension selector, so
 * the map's furniture reads as one set rather than white browser chrome
 * beside a dark custom control.
 *
 * MapLibre draws its own icons as dark SVG *background images* on the
 * button, not as fill-able markup — so they cannot simply be recoloured.
 * `filter: invert(1)` flips those dark glyphs to white. It is scoped to
 * `.maplibregl-ctrl-icon` spans, which is exactly the set MapLibre paints
 * that way; our own buttons draw inline SVG with currentColor and are
 * deliberately NOT inverted (they would come out black).
 */
.pg-dashboard-map .maplibregl-ctrl-group {
	background: rgba(26, 28, 30, 0.92) !important;
	border-radius: 6px;
	box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
	overflow: hidden;
}

.pg-dashboard-map .maplibregl-ctrl-group button {
	width: 30px;
	height: 30px;
	background: transparent !important;
	border: none !important;
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	transition: background-color 120ms ease;
}

.pg-dashboard-map .maplibregl-ctrl-group button + button {
	border-top: 1px solid rgba(255, 255, 255, 0.12) !important;
}

.pg-dashboard-map .maplibregl-ctrl-group button:hover {
	background: rgba(255, 255, 255, 0.14) !important;
}

.pg-dashboard-map .maplibregl-ctrl-group button:active {
	background: rgba(255, 255, 255, 0.22) !important;
}

/* MapLibre's own background-image glyphs only. */
.pg-dashboard-map .maplibregl-ctrl-group button > .maplibregl-ctrl-icon {
	filter: invert(1);
}

/*
 * The compass is two-tone — a dark north arrow over a LIGHT south arrow —
 * so a plain invert flips that south arrow to near-black and it disappears
 * into the dark plate. `brightness(0)` crushes the whole glyph to black
 * first, then `invert(1)` lifts all of it to white, so both arrows read.
 * The pair still shows heading by their opposing directions rather than by
 * colour.
 */
.pg-dashboard-map .maplibregl-ctrl-compass > .maplibregl-ctrl-icon {
	filter: brightness(0) invert(1);
}

/* Our inline-SVG buttons size their icon and inherit the white. */
.pg-dashboard-map .pg-dashboard-home-btn svg,
.pg-dashboard-map .pg-dashboard-lasso-btn svg {
	width: 17px;
	height: 17px;
	display: block;
}

/* Active lasso reads as engaged, matching the selector's active pill. */
.pg-dashboard-map .pg-dashboard-lasso-btn.active {
	background: rgba(255, 255, 255, 0.28) !important;
}

.pg-dashboard-lasso-btn {
	font-size: 16px;
	line-height: 1;
}

.pg-dashboard-lasso-btn.active {
	background: var(--pg-accent);
	color: #fff;
}

/* Mobile card layout — same pattern as snippets/new/pg-search-results.php:
 * hide the header row, each <tr> becomes a bordered card, cells stack with
 * their column name as a label. */
@media (max-width: 640px) {
	.pg-table thead {
		display: none;
	}
	.pg-table, .pg-table tbody, .pg-table tr, .pg-table .pg-table-cell {
		display: block;
		width: 100%;
	}
	.pg-table tr {
		border: 1px solid var(--pg-border);
		border-radius: 6px;
		margin-bottom: 10px;
		padding: 8px;
	}
	.pg-table .pg-table-cell {
		border-bottom: none;
		padding: 4px 0;
	}
	.pg-table .pg-table-cell[data-label]:not([data-label=""])::before {
		content: attr(data-label) ": ";
		font-weight: 700;
		color: var(--pg-text-dim);
	}
	.pg-table-photo {
		float: left;
		margin-right: 10px;
	}
}
