/**
 * Interactive map — structure only.
 *
 * Every colour, font and radius below is a custom property with a deliberately
 * plain fallback. The site's own stylesheet is expected to set the tokens; this
 * file's job is layout, positioning and states, so the module drops into any
 * project without dragging another project's palette along with it.
 *
 * Tokens, all optional:
 *
 *   --veld-map-gap             space between the map and its filter list
 *   --veld-map-width          map column width, set per map from the admin
 *   --veld-map-sidebar        filter column minimum, before it wraps
 *   --veld-map-radius          corner radius on the map frame
 *   --veld-map-ratio           viewport aspect ratio (set from the image by PHP)
 *   --veld-map-ratio-mobile    the same below the stacking breakpoint, set per
 *                              map from the admin; absent unless one is given
 *   --veld-map-ink             text colour
 *   --veld-map-accent          pin and marker colour
 *   --veld-map-accent-ink      text on the accent colour
 *   --veld-map-surface         control and callout background
 *   --veld-map-pin-size        pin diameter
 *   --veld-map-breakpoint      width below which the layout stacks
 *   --veld-map-head-gap        space under the eyebrow/heading row
 *   --veld-map-eyebrow-gap     space between the eyebrow and the heading
 *   --veld-map-actions-gap     space above the call to action
 *   --veld-map-category-icon-size  the icon in a filter row
 *
 * These four are set per map from the admin, not by the site, and are listed
 * here only so a stylesheet can read them:
 *
 *   --veld-map-pad-top / -right / -bottom / -left   section padding
 */

/*
 * Everything below sits in a cascade layer on purpose.
 *
 * Unlayered CSS always beats layered CSS, whatever the specificity and whatever
 * the source order. So a site's own stylesheet can restyle any of this by
 * writing the same selector once — no !important, no doubled classes, and no
 * depending on which stylesheet the page happens to print first. That is what
 * makes "the plugin ships no brand colours, the site skins it" actually true
 * rather than aspirational.
 *
 * If a site puts its skin inside its own @layer, that layer needs to be
 * declared after this one to win.
 */
@layer veld-map {

	.veld-map {
		--veld-map-gap: 3rem;
		--veld-map-sidebar: 15rem;
		--veld-map-radius: 0px;
		--veld-map-ink: currentColor;
		--veld-map-accent: #c0392b;
		--veld-map-accent-ink: #fff;
		--veld-map-surface: #fff;
		--veld-map-surface-ink: #1d1d1d;
		--veld-map-pin-size: 1.5rem;

		/* The section, not the columns. A heading above the map and a call to
		   action below it are both full-width rows that want their own spacing,
		   so the two-column part is .veld-map__body and this is ordinary block
		   flow around it. */
		display: block;
		color: var(--veld-map-ink);

		/* Four numbers set per map from the admin. A side the editor left at 0
		   is not emitted at all, so each fallback here is what a bare install
		   gets — and a site that has its own section spacing restates these
		   four with its own value in the fallback slot, which is how an untouched
		   map keeps the spacing it already had.

		   Padding rather than margin: the section paints a ground, and a margin
		   would leave that ground short of the space it is meant to fill. */
		padding:
			var(--veld-map-pad-top, 0px)
			var(--veld-map-pad-right, 0px)
			var(--veld-map-pad-bottom, 0px)
			var(--veld-map-pad-left, 0px);
	}

	.veld-map__body {
		/* Grid, not flex. With flex the two columns wrap as soon as their *base*
		   sizes plus the gap exceed the line — 971 + 38 + 229 in a 1048px container
		   wraps before any shrinking gets a chance. Grid squeezes instead.

		   The map column prefers its configured width but may shrink to nothing;
		   the filter column is guaranteed --veld-map-sidebar and takes the slack.
		   So on a container wide enough for the comp the map gets its full width,
		   and on a narrower one the map yields rather than the sidebar collapsing. */
		display: grid;
		grid-template-columns:
			minmax(0, var(--veld-map-width, 60%))
			minmax(min(var(--veld-map-sidebar), 100%), 1fr);

		align-items: start;
		gap: var(--veld-map-gap);
	}

	/* One column once there is no room for two. */
	@media ( max-width: 900px ) {
		.veld-map__body {
			grid-template-columns: minmax(0, 1fr);
		}
	}

	/* ── The filter column, pinned beside the map ────────────────────────────
	   Beside the map, the two are grid siblings, so the taller of them sizes the
	   row — and the accordion changes height every time a category opens. Left
	   in flow, opening one pushes the row taller and carries the call to action
	   down the page with it, which is the one thing the comp fixes: the button
	   sits under the map's bottom edge whichever category is open.

	   So above the stacking breakpoint the list is taken out of flow. Top, left
	   and right are pinned to the column; the **bottom is `auto`**, so the box is
	   whatever height its own content is and simply draws on past the map's
	   bottom edge when a long category is open. Nothing clips and nothing
	   scrolls — the list is a handful of rows a visitor is choosing between, not
	   a document, and a scrollbar inside a section reads as a defect and hides
	   its own last row.

	   That is the trade this makes: the button holds its place, and a category
	   longer than the artwork overlaps the space beneath it rather than being
	   contained. Give the map a taller `aspect_ratio` if a category ever grows
	   far enough for that to matter.

	   min-width, not a reset inside the max-width block above: pinning is what
	   the two-column layout wants and the one-column layout has no map to pin
	   against, so each states its own case. */
	@media ( min-width: 901px ) {
		.veld-map__filters {
			position: relative;
		}

		.veld-map__filters-inner {
			position: absolute;
			inset: 0 0 auto;
		}
	}

	/* ── Head and actions ───────────────────────────────────────────────────────
	   Structure only, as everywhere else: the module has no opinion about the
	   size of a heading or the shape of a button, so these carry no type, no
	   colour and no radius. The margins are the one exception, because they are
	   what stops the three rows colliding before a site has styled anything. */

	.veld-map__head {
		margin: 0 0 var(--veld-map-head-gap, 2rem);
	}

	.veld-map__eyebrow,
	.veld-map__heading {
		margin: 0;
	}

	.veld-map__eyebrow + .veld-map__heading {
		margin-top: var(--veld-map-eyebrow-gap, 0.5rem);
	}

	.veld-map__actions {
		margin: var(--veld-map-actions-gap, 2rem) 0 0;
	}

	.veld-map__button {
		display: inline-flex;
		align-items: center;
	}

	/* ── Stage ──────────────────────────────────────────────────────────────── */

	.veld-map__stage {
		position: relative;
		min-width: 0;
	}

	/* The height comes from the ratio, never from the contents: Leaflet is handed
	   an empty div, and the fallback image that would otherwise give this box a
	   height is removed the moment the map initialises. */
	.veld-map__viewport {
		position: relative;
		overflow: hidden;
		border-radius: var(--veld-map-radius);
		aspect-ratio: var(--veld-map-ratio, 16 / 9);
		background: var(--veld-map-surface);
	}

	.veld-map__canvas,
	.veld-map__fallback {
		position: absolute;
		inset: 0;
	}

	/* Leaflet's own panes run from z-index 200 (tiles) to 800 (controls), all
	   inside this same stacking context — neither .veld-map__stage nor
	   .veld-map__canvas creates one, since both are positioned with z-index auto.
	   So anything of ours that must sit above the map has to clear 800, or Leaflet
	   silently paints over it. */
	.veld-map__fallback {
		/* Covers the empty Leaflet container until JavaScript removes it. */
		z-index: 900;
	}

	.veld-map__image {
		display: block;
		width: 100%;
		height: 100%;
		object-fit: contain;
	}

	/* ── The narrow-screen plate ─────────────────────────────────────────────
	   A second piece of artwork, drawn over the map below the stacking
	   breakpoint and hidden above it. See Renderer::render_mobile_plate() for
	   why it covers the map rather than replacing it — in short, so Leaflet
	   still has a box to build in and a rotation needs no re-render.

	   It is emitted only when a map has one, so a map without one has no rule
	   here to do anything. */
	.veld-map__mobile {
		display: none;
	}

	@media ( max-width: 900px ) {
		/* The frame the plate is drawn at. `--veld-map-ratio-mobile` is set per
		   map from the admin and is simply absent when nobody has set one, so
		   the fallback chain leaves the narrow frame the shape it already
		   was. */
		.veld-map__viewport {
			aspect-ratio: var(
				--veld-map-ratio-mobile,
				var(--veld-map-ratio, 16 / 9)
			);
		}

		.veld-map__mobile {
			position: absolute;
			inset: 0;

			/* Over the map, and over the pre-JavaScript fallback image at 900
			   as well — both are the wide artwork, and neither should ever be
			   glimpsed here. Still under .veld-map__controls at 1000, which is
			   why those are hidden below. */
			z-index: 950;
			display: block;
			background: var(--veld-map-surface);
		}

		/* Cover, where the wide artwork is contained. The plate is authored at
		   the frame's own shape, so the two agree and neither crops; where they
		   disagree — an editor's crop is a little off, or the frame numbers
		   were left for the plate to decide — filling the plate reads as a
		   deliberate bleed where letterboxing reads as a mistake. */
		.veld-map__mobile .veld-map__image {
			object-fit: cover;
		}

		/* Nothing to zoom: the plate is a picture. Scoped to the plate being
		   present, so a map without one keeps its buttons on a phone. */
		.veld-map:has(.veld-map__mobile) .veld-map__controls {
			display: none;
		}
	}

	/* ── Leaflet ────────────────────────────────────────────────────────────────
	   Leaflet ships opinions about type and colour that belong to a map widget,
	   not to a page section. These hand them back to the site. */

	.veld-map .leaflet-container {
		width: 100%;
		height: 100%;
		font: inherit;
		background: var(--veld-map-surface);
		outline-offset: 2px;
	}

	.veld-map .leaflet-container a {
		color: inherit;
	}

	/* Keeps the artwork crisp when zoomed past its natural size, rather than
	   letting the browser smooth an illustrated map into mush. */
	.veld-map .leaflet-image-layer {
		image-rendering: -webkit-optimize-contrast;
	}

	.veld-map--draggable .leaflet-grab {
		cursor: grab;
	}

	.veld-map--draggable .leaflet-dragging .leaflet-grab {
		cursor: grabbing;
	}

	/* While one-finger panning is still off on touch, say so with the cursor
	   rather than looking broken. */
	.veld-map--tap-to-pan .leaflet-grab {
		cursor: pointer;
	}

	/* ── Markers ────────────────────────────────────────────────────────────────
	   Leaflet positions the marker's top-left at the coordinate. The button
	   centres itself on that point, which is what keeps a pin in place at every
	   zoom level. */

	.veld-map__marker {
		background: none;
		border: 0;
	}

	.veld-map__pin-button {
		position: absolute;
		left: 0;
		top: 0;
		display: grid;
		place-items: center;
		width: var(--veld-map-pin-size);
		height: var(--veld-map-pin-size);
		padding: 0;
		border: 0;
		border-radius: 50%;
		background: var(--veld-pin-colour, var(--veld-map-accent));
		color: var(--veld-map-accent-ink);
		font: inherit;
		font-size: 0.6875rem;
		font-weight: 700;
		line-height: 1;
		cursor: pointer;

		/* scale() sits to the right of translate() so it resolves first, about the
		   button's own centre — which leaves the translate offset unchanged and the
		   pin anchored whatever the scale is. */
		transform: translate(-50%, -50%) scale(var(--veld-pin-scale, 1));
		transform-origin: center;
		transition: box-shadow 150ms ease, background-color 150ms ease;
	}

	/* Markers are a constant pixel size in Leaflet, which is usually what you
	   want. This is the opt-in to letting them grow with the artwork. */
	.veld-map--scaling-pins {
		--veld-pin-scale: var(--veld-map-scale, 1);
	}

	.veld-map__pin-button:focus-visible {
		outline: 2px solid var(--veld-map-accent);
		outline-offset: 3px;
	}

	.veld-map__pin-icon {
		display: block;
		width: 60%;
		height: auto;
	}

	/* An inlined SVG has no intrinsic size once PHP has stripped its width and
	   height — the viewBox is what it scales by — so the wrapper is what holds
	   the 60%, and the artwork fills it. `height: auto` on the svg resolves
	   through the viewBox's own ratio, so a wide glyph and a tall one both fit
	   without either being distorted. */
	.veld-map__pin-icon--svg {
		display: grid;
		place-items: center;
	}

	.veld-map__pin-icon--svg svg {
		display: block;
		width: 100%;
		height: auto;
		max-height: 100%;
	}

	.veld-map__pin-button:hover,
	.veld-map__marker.is-highlighted .veld-map__pin-button {
		box-shadow: 0 0 0 0.25rem color-mix(in srgb, var(--veld-pin-colour, var(--veld-map-accent)) 35%, transparent);
	}

	/* ── Pin labels ─────────────────────────────────────────────────────────────
	   Two separate things, deliberately:

	   .veld-map__pin-name    always present, always hidden — the button's
	                          accessible name. Never made visible.
	   .veld-map__pin-callout the optional bubble a sighted visitor sees, shown on
	                          hover, focus or selection. Absent from the markup
	                          entirely when the map is set to show no label. */

	.veld-map__pin-name {
		position: absolute;
		width: 1px;
		height: 1px;
		margin: -1px;
		padding: 0;
		overflow: hidden;
		clip-path: inset(50%);
		white-space: nowrap;
		border: 0;
	}

	.veld-map__pin-callout {
		position: absolute;
		left: 50%;
		bottom: calc(100% + 0.5rem);
		width: max-content;
		max-width: 12rem;
		padding: 0.35rem 0.6rem;
		transform: translateX(-50%);
		border-radius: 0.25rem;
		background: var(--veld-map-surface);

		/* --veld-map-surface-ink, NOT --veld-map-ink. The latter defaults to
		   currentColor so the sidebar inherits the site's text colour — but this
		   bubble sits *inside* the pin button, which sets its own colour to
		   --veld-map-accent-ink (white, so the number reads on the pin). Inheriting
		   from there gives white text on a white bubble. */
		color: var(--veld-map-surface-ink);
		font-size: 0.75rem;
		font-weight: 600;
		line-height: 1.3;
		text-align: center;
		box-shadow: 0 2px 10px rgb(0 0 0 / 18%);
		pointer-events: none;

		/* Hidden by default, revealed by the states below. visibility rather than
		   display so the bubble can transition, and so its box is measurable. */
		opacity: 0;
		visibility: hidden;
		transition: opacity 120ms ease, visibility 120ms ease;
	}

	.veld-map__pin-button:hover .veld-map__pin-callout,
	.veld-map__pin-button:focus-visible .veld-map__pin-callout,
	.veld-map__marker.is-highlighted .veld-map__pin-callout {
		opacity: 1;
		visibility: visible;
	}

	/* A hovered or selected pin has to sit above its neighbours, or the callout
	   slides underneath the next marker along. */
	.veld-map__marker:hover,
	.veld-map__marker:focus-within,
	.veld-map__marker.is-highlighted {
		z-index: 1000 !important; /* Leaflet writes marker z-index inline */
	}

	/* ── Controls ───────────────────────────────────────────────────────────── */

	.veld-map__controls {
		position: absolute;
		right: 0.75rem;
		bottom: 0.75rem;
		z-index: 1000; /* above every Leaflet pane — see the note above */
		display: flex;
		flex-direction: column;
		gap: 0.25rem;
	}

	.veld-map__control {
		display: grid;
		place-items: center;
		width: 2rem;
		height: 2rem;
		padding: 0;
		border: 0;
		border-radius: 0.25rem;
		background: var(--veld-map-surface);
		/* Same reasoning as the callout: painted on the surface, so it takes the
		   surface's ink rather than whatever the section happens to inherit. On a
		   dark section with light text, currentColor here is white on white. */
		color: var(--veld-map-surface-ink);
		cursor: pointer;
		box-shadow: 0 1px 4px rgb(0 0 0 / 20%);
	}

	.veld-map__control:hover {
		background: color-mix(in srgb, var(--veld-map-surface) 88%, var(--veld-map-surface-ink));
	}

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

	.veld-map__control-glyph {
		display: block;
		width: 1rem;
		height: 1rem;
	}

	.veld-map__control-glyph svg {
		display: block;
		width: 100%;
		height: 100%;
	}

	/* ── Filters ────────────────────────────────────────────────────────────── */

	.veld-map__filters {
		min-width: 0;
	}

	.veld-map__filters-title {
		margin: 0 0 1.25rem;
	}

	.veld-map__categories,
	.veld-map__pois {
		margin: 0;
		padding: 0;
		list-style: none;
	}

	.veld-map__category + .veld-map__category {
		margin-top: 0.75rem;
	}

	.veld-map__category-toggle {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 0.5rem;
		width: 100%;
		padding: 0.25rem 0;
		border: 0;
		background: none;
		color: inherit;
		font: inherit;
		text-align: left;
		cursor: pointer;
	}

	.veld-map__category-toggle:focus-visible {
		outline: 2px solid var(--veld-map-accent);
		outline-offset: 2px;
	}

	/* The category's icon, which is also the face of every pin it reveals. It
	   sits before the label and pushes nothing around: `flex: none` keeps the
	   label's own flex growth doing the work, so adding an icon to one category
	   does not shift the chevron on the others. */
	.veld-map__category-icon {
		flex: none;
		display: block;
		width: var(--veld-map-category-icon-size, 1rem);
		height: var(--veld-map-category-icon-size, 1rem);
	}

	.veld-map__category-icon--svg {
		display: grid;
		place-items: center;
	}

	.veld-map__category-icon svg,
	.veld-map__category-icon--svg svg {
		display: block;
		width: 100%;
		height: auto;
		max-height: 100%;
	}

	/* The label takes the slack so the chevron stays at the far edge whether or
	   not there is an icon in front of it. */
	.veld-map__category-label {
		flex: 1 1 auto;
		min-width: 0;
	}

	.veld-map__chevron {
		flex: none;
		width: 1rem;
		height: 1rem;
		transition: transform 180ms ease;
	}

	.veld-map__chevron svg {
		display: block;
		width: 100%;
		height: 100%;
	}

	.veld-map__category-toggle[aria-expanded='true'] .veld-map__chevron {
		transform: rotate(180deg);
	}

	.veld-map__pois {
		padding-top: 0.5rem;
	}

	.veld-map__poi-button {
		display: flex;
		align-items: baseline;
		gap: 0.5rem;
		width: 100%;
		padding: 0.25rem 0;
		border: 0;
		background: none;
		color: inherit;
		font: inherit;
		text-align: left;
		cursor: pointer;
	}

	.veld-map__poi-button:focus-visible {
		outline: 2px solid var(--veld-map-accent);
		outline-offset: 2px;
	}

	/* The number beside a place in the list is the same disc as its pin on the
	   map, drawn from the same three values: --veld-map-pin-size, the category's
	   own --veld-pin-colour (set on the row by PHP, on the marker by the script)
	   and --veld-map-accent-ink. That is the whole point of the number — it is
	   how a reader gets from a name in the list to a marker on the artwork — so
	   the two are one thing here rather than two things that resemble each
	   other and drift.

	   A site restyling one should restyle both; `.veld-map__pin-button` carries
	   everything specific to sitting on the map, and nothing that defines the
	   disc. */
	.veld-map__poi-number {
		display: grid;
		place-items: center;
		flex: none;
		width: var(--veld-map-pin-size);
		height: var(--veld-map-pin-size);
		border-radius: 50%;
		background: var(--veld-pin-colour, var(--veld-map-accent));
		color: var(--veld-map-accent-ink);
		font-size: 0.6875rem;
		font-weight: 700;
		line-height: 1;
	}

	.veld-map__poi-note {
		margin-left: auto;
		opacity: 0.7;
		font-size: 0.8125em;
	}

	.veld-map__poi.is-highlighted .veld-map__poi-label {
		text-decoration: underline;
	}

	/* ── Motion ─────────────────────────────────────────────────────────────── */

	@media ( prefers-reduced-motion: reduce ) {
		.veld-map__chevron,
		.veld-map__pin-button,
		.veld-map__pin-callout {
			transition: none;
		}
	}
}
