/*!
 * Duet Map — structural styles.
 *
 * ─────────────────────────────────────────────────────────────────────────────
 * SPECIFICITY CONTRACT — read before editing.
 *
 * Every rule here carries EXACTLY ONE CLASS. No chained classes, no element or
 * id qualifiers, no descendant class pairs, no `!important`. Modifier scoping
 * goes through :where(), which contributes zero.
 *
 * State may add to that and does: `:checked`, `:hover`, `:focus-visible`,
 * `[hidden]`, `::before`. Those are unavoidable, and they are fine, because the
 * invariant that matters is comparative — a block author who prefixes any
 * selector here with their own block class gains exactly one class over the
 * equivalent rule, in the same state. `.b_MyBlock .duet-map__check:checked`
 * beats `.duet-map__check:checked`; `.b_MyBlock .duet-map__legend` beats
 * `.duet-map__legend`. That holds regardless of which file loads first, which
 * is the point: this stylesheet and a block's own stylesheet are loaded by two
 * different mechanisms whose relative order depends on where the integrator
 * calls duet_map_css().
 *
 * Structural pseudo-classes count as class-weight too, so no `:last-child` for
 * cosmetic spacing — use `gap`. The carve-out above is for STATE only.
 *
 * So the rule when adding CSS here is not "stay under a number", it is: never
 * add a second class or a tag/id to a selector. If you need more weight than
 * that, you want a new modifier class or a custom property instead.
 *
 * COROLLARY — SOURCE ORDER MATTERS. Because :where() contributes zero, a
 * modifier like `:where(.duet-map--search-left) .duet-map__search-form` weighs
 * exactly the same as the plain `.duet-map__search-form` it is overriding. The
 * later one wins. So every modifier must sit BELOW the base rule it modifies.
 * Grouping all the modifiers together at the top of the file looks tidier and
 * is silently broken.
 * ─────────────────────────────────────────────────────────────────────────────
 *
 * Theming: override the custom properties below rather than the rules.
 * They fall back to the host theme's own tokens where those exist.
 */

/*
 * Guards the legend column count. `repeat(var(--n), …)` with a bad value (say
 * `3px`) is invalid at computed-value time, which drops the whole declaration
 * and collapses the grid to one column — a failure no fallback rule can rescue,
 * because a second rule at the same specificity just loses on source order.
 * @property makes a bad value fall back to initial-value instead, and adds no
 * specificity of its own.
 */
@property --duet-map-legend-columns {
	syntax: "<integer>";
	inherits: true;
	initial-value: 3;
}

.duet-map {
	--duet-map-height: 600px;
	--duet-map-columns: 2fr 3fr;
	--duet-map-legend-columns: 3;
	--duet-map-legend-gap: 24px;
	--duet-map-legend-row-gap: 14px;
	--duet-map-gap: 32px;
	--duet-map-intro-bg: transparent;
	--duet-map-intro-padding: 0;
	--duet-map-intro-gap: 20px;
	--duet-map-radius: var(--border-radius-sm, 0);
	--duet-map-accent: var(--color-primary-b, #662046);
	--duet-map-text: var(--color-primary-a, #000);
	--duet-map-muted: #757575;
	--duet-map-border: #c4c4c4;
	--duet-map-surface: #fff;
	--duet-map-canvas-bg: #e5e3df;
	--duet-map-legend-bg: transparent;
	--duet-map-font: var(--font-primary, inherit);
	--duet-map-icon-size: 42px;
	--duet-map-control-padding: 18px 20px;
	--duet-map-scroll-margin: 0px;
	--duet-map-transition: var(--transition-speed, 200ms) var(--transition-easing, ease-in-out);

	display: block;
	box-sizing: border-box;
	font-family: var(--duet-map-font);
	color: var(--duet-map-text);
}

.duet-map * {
	box-sizing: border-box;
}

/* ── Two-column body ──────────────────────────────────────────────────────── */

.duet-map__body {
	display: grid;
	grid-template-columns: var(--duet-map-columns);
	gap: var(--duet-map-gap);
	align-items: stretch;
}

/*
 * The column stretches; its CONTENTS are centred. Centring the grid item itself
 * would make --duet-map-intro-bg paint only as tall as the text, leaving a
 * tinted slab floating in the middle of a 600px column.
 */
.duet-map__intro {
	display: flex;
	flex-direction: column;
	justify-content: center;
	align-items: flex-start;
	gap: var(--duet-map-intro-gap);
	min-width: 0;
	padding: var(--duet-map-intro-padding);
	background-color: var(--duet-map-intro-bg);
	border-radius: var(--duet-map-radius);
}

/*
 * min-height, not height: a stretched grid item with an explicit height will
 * not grow, so a taller intro (long translated heading) would leave the map
 * top-aligned with dead space beside it. min-height still gives Google Maps a
 * resolved height at init, which it needs.
 *
 * Consequence worth knowing: a percentage --duet-map-height now resolves
 * against an auto-height row and yields zero. Use absolute or viewport units.
 */
.duet-map__canvas {
	min-width: 0;
	min-height: var(--duet-map-height);
	background-color: var(--duet-map-canvas-bg);
	border: 1px solid var(--duet-map-border);
	border-radius: var(--duet-map-radius);
}

:where(.duet-map--layout-stacked) .duet-map__body,
:where(.duet-map--no-intro) .duet-map__body {
	grid-template-columns: 1fr;
}

:where(.duet-map--search-above) .duet-map__search {
	margin-bottom: var(--duet-map-gap);
}

:where(.duet-map--search-left) .duet-map__search {
	width: 100%;
}

/* ── Intro column ─────────────────────────────────────────────────────────── */

.duet-map__title {
	margin: 0;
	font-size: 2em;
	line-height: 1.2;
}

.duet-map__legend-link {
	color: var(--duet-map-accent);
	font-weight: 700;
	text-decoration: underline;
}

.duet-map__legend-link:focus-visible {
	outline: 2px solid var(--duet-map-accent);
	outline-offset: 2px;
}

/* ── Search ───────────────────────────────────────────────────────────────── */

.duet-map__search-form {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 16px;
}

.duet-map__search-input {
	flex: 1 1 240px;
	max-width: 385px;
	padding: var(--duet-map-control-padding);
	border: 2px solid var(--duet-map-border);
	background-color: var(--duet-map-surface);
	color: var(--duet-map-muted);
	font-family: inherit;
	font-size: 1em;
	line-height: 1.35;
}

.duet-map__search-button {
	flex: 0 0 auto;
	margin: 0 !important;
	padding: var(--duet-map-control-padding);
	border: 0;
	border-radius: var(--duet-map-radius);
	background-color: var(--duet-map-accent);
	color: #fff;
	font-family: inherit;
	font-size: 1em;
	line-height: 1.35;
	cursor: pointer;
	transition: opacity var(--duet-map-transition);
}

.duet-map__search-button:hover,
.duet-map__search-button:focus {
	opacity: 0.85;
}

.duet-map__search-error {
	flex: 1 1 100%;
	margin: 0;
	color: #b3261e;
	font-size: 0.9em;
}

.duet-map__search-error[hidden] {
	display: none;
}

/*
 * Left-column variant. These MUST stay below the base rules above — they are
 * the same specificity (0,1,0), so source order is what decides. Putting them
 * higher up silently reverts `align-items` and `flex`, which in a column
 * container means a horizontally centred button and an input stretched to the
 * full height of the column.
 *
 * The field and button are an explicit vertical stack rather than a wrapped
 * row, for two reasons: whether the button lands beside or below the field
 * would otherwise depend on the column width and the translated label, and a
 * button alone on a second flex line is trivially re-centred by any stray
 * `justify-content` or `auto` margin from another stylesheet.
 */
:where(.duet-map--search-left) .duet-map__search-form {
	flex-direction: column;
	align-items: flex-start;
	justify-content: flex-start;
}

:where(.duet-map--search-left) .duet-map__search-input {
	flex: 0 0 auto;
	width: 100%;
}

:where(.duet-map--search-left) .duet-map__search-button {
	align-self: flex-start;
}

:where(.duet-map--search-left) .duet-map__search-error {
	flex: 0 0 auto;
	width: 100%;
}

/* ── Legend ───────────────────────────────────────────────────────────────── */

.duet-map__legend {
	margin-top: var(--duet-map-gap);
	scroll-margin-top: var(--duet-map-scroll-margin);
	background-color: var(--duet-map-legend-bg);
}

/* Only ever focused programmatically, by the View Legend anchor. */
.duet-map__legend:focus {
	outline: none;
}

.duet-map__legend-head {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 24px;
	margin-bottom: 24px;
}

.duet-map__legend-title {
	margin: 0;
	font-size: 1.8em;
	line-height: 1.3;
}

.duet-map__legend-all {
	display: inline-flex;
	align-items: center;
	gap: 12px;
	cursor: pointer;
	font-weight: 700;
}

.duet-map__legend-group {
	margin: 0 0 12px;
	font-size: 1.1em;
	line-height: 1.3;
}

/*
 * Multi-column, not grid, and the reason is fill order.
 *
 * Grid fills row-major — across the first row, then down. The design fills
 * column-major: down column one, then down column two. With six services and
 * three columns that is the difference between
 *
 *   grid      For · With · Visiting        multicol   For      · Visiting · Paperwork
 *             Phoning · Paperwork · Tech              With     · Phoning  · Tech
 *
 * Multi-column flows content down each column in turn, which is exactly the
 * wanted order, and `column-fill: balance` (the default) evens the columns out.
 * It also keeps working when the count changes at a breakpoint or a service is
 * added — neither of which is true of reordering the services by hand.
 */
.duet-map__legend-list {
	column-count: var(--duet-map-legend-columns);
	column-gap: var(--duet-map-legend-gap);
	margin: 0 0 20px;
	padding: 0;
	list-style: none;
}

/*
 * Multicol has no row-gap for in-flow content, so the vertical rhythm is a
 * margin — and break-inside stops a row being split across two columns.
 */
.duet-map__legend-row {
	break-inside: avoid;
	-webkit-column-break-inside: avoid;
	margin: 0 0 var(--duet-map-legend-row-gap);
	padding: 0;
}

.duet-map__legend-label {
	display: flex;
	align-items: center;
	gap: 16px;
	cursor: pointer;
}

.duet-map__legend-icon {
	flex: 0 0 auto;
	width: var(--duet-map-icon-size);
	height: var(--duet-map-icon-size);
	object-fit: contain;
}

.duet-map__legend-text {
	flex: 1 1 auto;
	min-width: 0;
}

/* Checkbox: the host theme resets form controls aggressively, so draw our own. */
.duet-map__check {
	flex: 0 0 auto;
	appearance: none;
	-webkit-appearance: none;
	width: 29px;
	height: 29px;
	margin: 0;
	display: grid;
	place-content: center;
	border: 2px solid var(--duet-map-border);
	border-radius: 2px;
	background-color: var(--duet-map-surface);
	cursor: pointer;
}

.duet-map__check::before {
	content: "";
	width: 15px;
	height: 15px;
	transform: scale(0);
	transition: transform 120ms ease-in-out;
	box-shadow: inset 1em 1em var(--duet-map-accent);
}

.duet-map__check:checked::before {
	transform: scale(1);
}

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

/* ── Info window ──────────────────────────────────────────────────────────── */

.duet-map__iw {
	font-family: var(--duet-map-font);
	line-height: 1.45;
	max-width: 280px;
}

.duet-map__iw-name {
	margin: 0 0 4px;
	font-weight: 600;
	font-size: 1.05em;
}

.duet-map__iw-list {
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: 0.95em;
}

.duet-map__iw-link {
	display: inline-block;
	margin-top: 8px;
	color: var(--duet-map-accent);
	font-weight: 700;
}

/* ── Fallback / empty states ──────────────────────────────────────────────── */

.duet-map__notice {
	display: flex;
	align-items: center;
	justify-content: center;
	min-height: 240px;
	padding: 32px;
	border: 1px dashed var(--duet-map-border);
	border-radius: var(--duet-map-radius);
	background-color: var(--duet-map-canvas-bg);
	text-align: center;
	color: var(--duet-map-muted);
}

.duet-map__notice-detail {
	margin-top: 8px;
	font-size: 0.9em;
}

/* ── Responsive ───────────────────────────────────────────────────────────── */

/*
 * Viewport media queries, not container queries. A container query would be
 * more correct for a map inside a narrow block, but it fails OPEN — a browser
 * without support ignores the rule and the layout simply never stacks. These
 * maps are full-width in practice, so correctness here is not worth that.
 *
 * Note the breakpoints re-declare the column count on the LIST, not on the root.
 * Overriding --duet-map-legend-columns on .duet-map therefore sets the desktop
 * count and keeps the responsive step-down; overriding it on
 * .duet-map__legend-list pins it at every width.
 */
@media (max-width: 991px) {
	.duet-map__body {
		grid-template-columns: 1fr;
	}

	.duet-map__intro {
		justify-content: flex-start;
	}

	.duet-map__legend-list {
		--duet-map-legend-columns: 2;
	}
}

@media (max-width: 600px) {
	.duet-map__legend-list {
		--duet-map-legend-columns: 1;
	}

	.duet-map__search-input {
		max-width: none;
	}
}

@media (prefers-reduced-motion: reduce) {
	.duet-map__check::before,
	.duet-map__search-button {
		transition: none;
	}
}
