/*
 * Lightbox — a fullscreen <dialog> carousel for images.
 *
 * Ported from the PHP framework's includes/components/lightbox. Structure is
 * unchanged; every token is remapped to theme.json's presets so a child retunes
 * it with the rest of the system (radius, transition, spacing, colour).
 *
 * Rendered as a dependency by whichever block needs it (today: core's gallery
 * and image blocks, adopted in inc/lightbox.php),
 * never inserted on its own — see inc/lightbox.php for the markup.
 *
 * Column layout: a top bar (zoom, counter, close) over a flex-grow stage that
 * bounds the image. The stage holds a translated track, one slide per image;
 * assets/js/lightbox.js drives it.
 */

/*
 * Adopted core triggers.
 *
 * Core positions `.lightbox-trigger` from JS — `callbacks.setButtonStyles`
 * writes its top and right on load and on resize. That module is deregistered
 * (see inc/lightbox.php), so the button needs real CSS or it collapses to a
 * 12px icon in the figure's top-left corner, which is then the only clickable
 * spot. The image carries the index too, so the picture itself is the hit area
 * and this button is the keyboard affordance.
 */
.wp-lightbox-container [data-lightbox-index] {
	cursor: zoom-in;
}

.wp-lightbox-container .lightbox-trigger {
	position: absolute;
	top: var(--wp--preset--spacing--xs);
	right: var(--wp--preset--spacing--xs);
	display: grid;
	place-items: center;
	inline-size: 2rem;
	block-size: 2rem;
	padding: 0;
	border: 0;
	border-radius: var(--wp--preset--border-radius--s);
	background: rgb(0 0 0 / 0.5);
	opacity: 0;
	transition: opacity var(--wp--custom--transition--fast);
}

/*
 * Hidden until wanted, but never hidden from the keyboard: opacity keeps it
 * focusable, where display:none would take it out of the tab order and leave
 * the gallery openable by pointer only.
 */
.wp-lightbox-container:hover .lightbox-trigger,
.wp-lightbox-container .lightbox-trigger:focus-visible {
	opacity: 1;
}

.wp-lightbox-container .lightbox-trigger:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
	.wp-lightbox-container .lightbox-trigger {
		transition: none;
	}
}

/* Lock background scroll while open. */
html:has(.lightbox[open]) {
	overflow: clip;
}

/*
 * Pinned to color-scheme--dark in the markup so the surface and foreground
 * roles resolve dark against the backdrop, whatever the page's own scheme.
 */
.lightbox {
	width: 100%;
	height: 100%;
	max-width: 100%;
	max-height: 100%;
	margin: auto;
	padding: 0;
	border: none;
	background-color: transparent;

	/*
	 * Dark, slightly-warm outline from the border colour pushed toward black —
	 * gives bare icons and text a legible edge over any image.
	 */
	--lightbox-icon-outline: oklch(from var(--wp--custom--border--color) 0.18 c h / 0.9);

	/*
	 * Base glyph size. Feather glyphs fill their 24x24 box unevenly, so the
	 * chevrons are scaled up below to match the X and magnifier.
	 */
	--lightbox-icon-size: 1.9rem;

	/*
	 * Fast or double clicks otherwise select the image behind (a blue flash);
	 * nothing here is selectable. Also suppresses the iOS grey tap highlight.
	 */
	user-select: none;
	-webkit-user-select: none;
	-webkit-tap-highlight-color: transparent;
}

/*
 * Only lay out the dialog when open — otherwise this flex rule overrides the
 * UA's `display: none` for a closed dialog and the chrome renders inline.
 */
.lightbox[open] {
	display: flex;
	flex-direction: column;
}

/*
 * Focus lands on the dialog itself on open (see the JS) so arrow-key nav works
 * without lighting up a control. It is a container, so no ring.
 */
.lightbox:focus {
	outline: none;
}

.lightbox::backdrop {
	background-color: rgb(0 0 0 / 0.85);
	backdrop-filter: blur(4px);
	-webkit-backdrop-filter: blur(4px);
}

/* ── Top bar ────────────────────────────────────────────────── */

.lightbox__bar {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	gap: var(--wp--preset--spacing--s);
	min-block-size: 2.75rem;
	padding: calc(var(--wp--preset--spacing--xxs) / 2) var(--wp--preset--spacing--s);
	z-index: 2;
}

/*
 * Slide counter — the position indicator for swipe-only nav. flex:1 fills the
 * gap between the two equal-width buttons, so it lands dead centre.
 */
.lightbox__counter {
	flex: 1;
	display: flex;
	align-items: center;
	justify-content: center;
	margin: 0;
	color: var(--wp--preset--color--foreground);
	font-size: var(--wp--preset--font-size--large);
	font-weight: var(--wp--custom--font-weight--bold);
	font-variant-numeric: tabular-nums;
	letter-spacing: 0.02em;
	text-shadow: 0 1px 3px var(--lightbox-icon-outline);
	pointer-events: none;
}

.lightbox__counter[hidden] {
	display: none;
}

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

.lightbox__stage {
	position: relative;
	flex: 1 1 auto;
	min-height: 0;
	display: flex;
	overflow: clip; /* clips the off-screen carousel slides */
}

/*
 * The track is a row of full-width slides; the JS translates it on X.
 * touch-action:none because every gesture is hand-driven — there is no native
 * scroll here to cooperate with.
 */
.lightbox__track {
	display: flex;
	flex: 1;
	min-height: 0;
	width: 100%;
	touch-action: none;
	will-change: transform;
}

/* Each slide is exactly the stage width and centres its image. */
.lightbox__slide {
	flex: 0 0 100%;
	min-width: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	cursor: default;
}

/*
 * Bounded by the stage height, so it never reaches up under the bar — and by
 * the file's own width. With `sizes="100vw"` the browser reports every image as
 * viewport-wide whatever the file really is, so without that second bound a
 * 400px image would be stretched to fill the stage.
 */
.lightbox__img {
	max-width: min(90vw, var(--lightbox-full-width, 100vw));
	max-height: 100%;
	object-fit: contain;
	border-radius: var(--wp--preset--border-radius--m);

	/*
	 * Zoom transform, driven by the JS: --lightbox-zoom scales and
	 * --lightbox-pan-x/y translate. The defaults are the resting state.
	 */
	transform: translate(var(--lightbox-pan-x, 0px), var(--lightbox-pan-y, 0px)) scale(var(--lightbox-zoom, 1));
	transition: transform var(--wp--custom--transition--fast);
	touch-action: none;
	-webkit-user-drag: none;
	cursor: zoom-in;
}

/* Zoomed → open hand (it can be dragged to pan); closed hand while dragging. */
.lightbox__img--zoomed {
	cursor: grab;
}

/*
 * While actively dragging or pinching, kill the transition so the image tracks
 * the finger 1:1 instead of lagging behind it.
 */
.lightbox__img--grabbing {
	transition: none;
	cursor: grabbing;
}

.lightbox__caption {
	margin-top: var(--wp--preset--spacing--s);
	color: var(--wp--custom--foreground--muted);
	font-size: var(--wp--preset--font-size--small);
	text-align: center;
}

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

/*
 * Bare icons, no circle or box: near-white glyphs with a dark outline so they
 * read over any image without a panel behind them. Zoom sits left, close right,
 * and the counter centres between them.
 */
.lightbox__close,
.lightbox__zoom,
.lightbox__nav {
	display: flex;
	align-items: center;
	justify-content: center;
	inline-size: 2.75rem; /* 44px: the smallest comfortable touch target */
	block-size: 2.75rem;
	padding: 0;
	border: none;
	background: transparent;
	color: oklch(from var(--wp--preset--color--raised) 0.97 c h);
	cursor: pointer;
	transition: transform var(--wp--custom--transition--fast);
}

/* Close stays pinned right even when the counter and zoom are hidden. */
.lightbox__close {
	margin-inline-start: auto;
}

/*
 * The SVGs carry no width or height, so size them here. Stacked dark
 * drop-shadows act as an outline hugging the glyph — box-shadow cannot, since
 * it tracks the box rather than the alpha shape.
 */
.lightbox__close :where(svg),
.lightbox__zoom :where(svg),
.lightbox__nav :where(svg) {
	inline-size: var(--lightbox-icon-size);
	block-size: var(--lightbox-icon-size);
	filter:
		drop-shadow(0 0 1px var(--lightbox-icon-outline))
		drop-shadow(0 1px 2px var(--lightbox-icon-outline));
}

/*
 * A chevron fills far less of its box than the magnifier, so the arrows render
 * larger to match the others' visible size.
 */
.lightbox__nav :where(svg) {
	inline-size: calc(var(--lightbox-icon-size) * 1.4);
	block-size: calc(var(--lightbox-icon-size) * 1.4);
}

.lightbox__close:focus-visible,
.lightbox__zoom:focus-visible,
.lightbox__nav:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 2px;
	border-radius: var(--wp--preset--border-radius--s);
}

/*
 * Prev/next arrows — desktop only, since touch navigates by swipe. Centred over
 * the stage sides, above the track.
 */
.lightbox__nav {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	z-index: 1;
}

.lightbox__nav--prev {
	left: var(--wp--preset--spacing--s);
}

.lightbox__nav--next {
	right: var(--wp--preset--spacing--s);
}

/* No prev on the first slide, no next on the last (the JS sets `hidden` when
   the lightbox doesn't loop). Restated because display: flex above beats the
   UA's [hidden] rule. */
.lightbox__nav[hidden] {
	display: none;
}

@media (hover: none) and (pointer: coarse) {
	.lightbox__nav {
		display: none;
	}
}

/*
 * The zoom button swaps glyph by state — magnifier-plus normally, minus once
 * zoomed (the JS toggles .is-zoomed). Class-only selectors, so cascade order
 * decides rather than a type selector.
 */
.lightbox__zoom-in {
	display: flex;
}

.lightbox__zoom-out {
	display: none;
}

.lightbox.is-zoomed .lightbox__zoom-in {
	display: none;
}

.lightbox.is-zoomed .lightbox__zoom-out {
	display: flex;
}

/*
 * Image already shown at or near its actual size — there is nothing to zoom
 * into, so the control goes (the JS toggles .is-unzoomable).
 *
 * Hidden rather than removed: `display: none` collapses the button's 3rem slot,
 * and since the counter between the two buttons is `flex: 1`, it then re-centres
 * across the reclaimed space. The counter visibly jumped sideways on every slide
 * whose image was too small to zoom. `visibility` keeps the slot, and takes the
 * button out of the tab order just as `display: none` did.
 */
.lightbox.is-unzoomable .lightbox__zoom {
	visibility: hidden;
}

.lightbox.is-unzoomable .lightbox__img {
	cursor: default;
}

/* A quarter-turn on the X — a small bit of delight on hover and focus. */
.lightbox__close:hover,
.lightbox__close:focus-visible {
	transform: rotate(90deg);
}

@media (prefers-reduced-motion: reduce) {
	.lightbox__img,
	.lightbox__close,
	.lightbox__zoom {
		transition: none;
	}

	.lightbox__close:hover,
	.lightbox__close:focus-visible {
		transform: none;
	}
}

/*
 * Phone and small screens — the image goes full-bleed width, and the stage's
 * flex still keeps it under the bar. A slide gap gives breathing space while
 * swiping between differently-sized images; the JS reads it for the translate
 * step, so it must stay a real column-gap.
 */
@media (width <= 48rem) {
	.lightbox__img {
		max-width: 100vw;
		border-radius: 0;
	}

	.lightbox__track {
		column-gap: var(--wp--preset--spacing--s);
	}
}
