/**
 * The carousel.
 *
 * Built on a native scroll container with scroll snapping rather than a
 * transform-driven track. That decision is what makes the rest of this simple:
 * touch swipe, trackpad, momentum, keyboard scrolling and the scrollbar all
 * work without a line of JavaScript, the carousel is usable before the script
 * loads and if it never loads at all, and slides of wildly different widths —
 * which is the whole point of the layout — need no arithmetic to place.
 *
 * Every slide is the height of the row and as wide as its own aspect ratio
 * makes it. Nothing is cropped.
 */

.airline-gallery {
	--airline-gallery-height: 62vh;
	--airline-gallery-gap: 14px;
	--airline-gallery-dim: 0.55;

	/*
	 * The arrows: the hero's, so the two carousels read as one thing. Light on a
	 * dark translucent disc, because a white disc sits invisibly on a white page
	 * and on the gap between two pictures — which is where the arrows of a
	 * full-bleed carousel land. Every part of them is a custom property so a theme
	 * can restyle them in one rule — `.airline-gallery { --airline-gallery-arrow:
	 * #1a1a1a; --airline-gallery-arrow-bg: #fff; }` — without having to
	 * out-specify anything below.
	 */
	--airline-gallery-arrow: #ffffff;
	/* Darker than the hero's 0.28: the pictures under these are dimmed with a light
	   veil (--airline-gallery-dim), and a faint disc on a veiled picture vanishes. */
	--airline-gallery-arrow-bg: rgba( 0, 0, 0, 0.6 );
	--airline-gallery-arrow-hover-bg: rgba( 0, 0, 0, 0.8 );
	--airline-gallery-arrow-border: rgba( 255, 255, 255, 0.35 );
	--airline-gallery-arrow-size: 3rem;
	--airline-gallery-arrow-icon: 1.4rem;

	/*
	 * The width of the window without its scrollbar. The script measures it and
	 * writes it here; 100vw is the fallback until it does, and the reason it is
	 * only a fallback is that 100vw *includes* the scrollbar, which would give
	 * the page a hairline horizontal scrollbar of its own.
	 */
	--airline-gallery-window: 100vw;

	position: relative;
}

/*
 * A scroll container has to clip — that is what makes it scroll — so the only
 * way to stop the neighbouring pictures being cut off at the edge of the text
 * column is to make the container itself as wide as the window. The negative
 * margin pulls it out of whatever column it was placed in, symmetrically, so
 * the middle picture stays centred on the page.
 */
.airline-gallery.is-bleed .airline-gallery__viewport {
	width: var(--airline-gallery-window);
	max-width: var(--airline-gallery-window);
	margin-inline: calc( 50% - var(--airline-gallery-window) / 2 );
}

/* ------------------------------------------------------------------ track */

.airline-gallery__viewport {
	overflow-x: auto;
	overflow-y: hidden;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;
	overscroll-behavior-x: contain;
	-webkit-overflow-scrolling: touch;

	/* The scrollbar would sit under the pictures and add its own height to the
	   row on the platforms that reserve space for it. The arrows, the counter
	   and the snap points are the affordances here. */
	scrollbar-width: none;
	-ms-overflow-style: none;
}

.airline-gallery__viewport::-webkit-scrollbar {
	display: none;
}

.airline-gallery__viewport:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 3px;
}

.airline-gallery__track {
	display: flex;
	align-items: center;
	gap: var(--airline-gallery-gap);
	height: var(--airline-gallery-height);
	margin: 0;
	padding: 0;
	list-style: none;
}

/*
 * Half the scroll port of empty space at each end, so that the first and last
 * pictures can still be brought into the middle.
 *
 * These are real list items rather than ::before and ::after on the track, and
 * that is the whole point of them. Trailing space in a scroll container is
 * where this kind of layout goes wrong: an overflowing flex container's
 * trailing padding has a chequered history of being left out of the scrollable
 * area, and a pseudo-element is one `ul::after` clearfix in a theme away from
 * being overridden into nothing — at which point the last picture can no longer
 * reach the middle and, under mandatory snapping, the browser refuses to rest
 * there at all. A real element in the markup can be seen in the inspector and
 * cannot be taken away by a stylesheet that knows nothing about this plugin.
 */
.airline-gallery__spacer {
	flex: 0 0 50%;
	height: 100%;
	margin: 0;
	pointer-events: none;
	scroll-snap-align: none;
}

/*
 * In a continuous band the copies do the spacers' job — they are the pictures
 * that sit either side of the ends — so the empty space goes. The class is put
 * on by the script only once the copies are actually in place, so a band with
 * no JavaScript keeps its spacers and behaves like an ordinary carousel rather
 * than starting hard against its left edge.
 */
.airline-gallery.has-band .airline-gallery__spacer {
	display: none;
}

/*
 * A copy is scenery. It is not in the tab order, not announced, and not
 * clickable — clicking one would open a picture the reader is not looking at,
 * and the band jumps back to the real one the moment scrolling settles anyway.
 */
.airline-gallery__slide.is-copy {
	pointer-events: none;
}

.airline-gallery__slide {
	position: relative;
	flex: 0 0 auto;
	height: 100%;

	/*
	 * The one line the whole layout rests on. A 3:2 landscape comes out 1.5
	 * times the row height wide; a 2:3 portrait 0.67 times. Both are exactly as
	 * tall as each other, and neither is cropped.
	 *
	 * min() stops a panorama running off the side of a phone; when it bites,
	 * object-fit keeps the picture whole inside the narrower box.
	 */
	width: min( calc( var(--airline-gallery-height) * var(--ratio, 1) ), 92vw );
	min-width: 3rem;
	margin: 0;

	/*
	 * Deliberately without scroll-snap-stop: always. Forcing every picture to
	 * be stopped at would make a gallery of two hundred impossible to get
	 * through — a fling should carry, and snapping should catch it when it
	 * settles.
	 */
	scroll-snap-align: center;

	opacity: var(--airline-gallery-dim);
	transition: opacity 0.35s ease;
}

.airline-gallery__slide.is-active {
	opacity: 1;
}

/*
 * The picture's own box is always the picture's own shape.
 *
 * The slide is the ratio times the row height wide, so ordinarily the image
 * fills it exactly. When the 92vw cap above bites — a panorama on a phone — the
 * slide is narrower than that, and an image told to be 100% of both would sit
 * letterboxed inside it, with its box taller than the picture. Anything drawn
 * on the box (the site frames its photographs with a 1px outline) then hangs
 * in the air above and below the picture.
 *
 * So the image is sized from its width and its ratio instead, and centred in
 * whatever height is left: its box shrinks with the picture, and object-fit is
 * left in place only as the fallback for a browser without aspect-ratio.
 */
.airline-gallery__figure {
	display: flex;
	align-items: center;
	height: 100%;
	margin: 0;
}

.airline-gallery__link {
	display: block;
	width: 100%;
	max-height: 100%;
	box-shadow: none;
}

.airline-gallery__slide img {
	display: block;
	width: 100%;
	height: auto;
	max-height: 100%;
	aspect-ratio: var(--ratio, 1);
	object-fit: contain;
}

/*
 * When WordPress's own lightbox is in use, each picture is a real core/image
 * block nested inside the slide's figure. Core gives it its own margins and
 * sizing, which have to be undone for it to fill a slide of a fixed height —
 * everything else about it, including the enlarge button and the overlay, is
 * left exactly as core made it.
 */
.airline-gallery__figure > .wp-block-image {
	/* Core already makes the lightbox wrapper a flex column; centring on both
	   axes covers it whichever direction it runs, so the picture sits in the
	   middle of the slide when its box is shorter than the row (see above). */
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 100%;
	margin: 0;
}

.airline-gallery__figure > .wp-block-image > a {
	display: block;
	width: 100%;
	max-height: 100%;
}

.airline-gallery__figure > .wp-block-image > img,
.airline-gallery__figure > .wp-block-image > a > img {
	width: 100%;
	height: auto;
	max-height: 100%;
	aspect-ratio: var(--ratio, 1);
}

/* Core positions the enlarge button from JavaScript against the image box. */
.airline-gallery__figure .lightbox-trigger {
	z-index: 1;
}

/* --------------------------------------------------------------- caption */

.airline-gallery__caption {
	position: absolute;
	inset: auto 0 0 0;
	margin: 0;
	padding: 2.5rem 0.9rem 0.7rem;

	font-size: 0.8125rem;
	line-height: 1.4;
	color: #fff;
	text-shadow: 0 1px 2px rgba( 0, 0, 0, 0.6 );

	/* Sits over the picture rather than under it, so that showing a caption
	   never changes the height of the row and never shifts the layout when the
	   middle picture changes to one that has no caption. */
	background: linear-gradient( to top, rgba( 0, 0, 0, 0.62 ), rgba( 0, 0, 0, 0 ) );

	opacity: 0;
	transition: opacity 0.35s ease;
	pointer-events: none;
}

.airline-gallery__slide.is-active .airline-gallery__caption {
	opacity: 1;
}

/* ----------------------------------------------------------------- arrows */

.airline-gallery__step {
	position: absolute;
	top: calc( var(--airline-gallery-height) / 2 );
	transform: translateY( -50% );
	z-index: 2;

	display: flex;
	align-items: center;
	justify-content: center;
	width: var(--airline-gallery-arrow-size);
	height: var(--airline-gallery-arrow-size);
	padding: 0;

	/* Not `inherit`: a carousel dropped into a dark section inherits a light
	   text colour, which paints a white arrow onto a white circle. */
	color: var(--airline-gallery-arrow);
	background: var(--airline-gallery-arrow-bg);
	border: 1px solid var(--airline-gallery-arrow-border);
	border-radius: 50%;
	box-shadow: 0 2px 8px rgba( 0, 0, 0, 0.25 );
	cursor: pointer;

	transition: opacity 0.2s ease, background-color 0.2s ease, color 0.2s ease;
}

.airline-gallery__step:hover {
	background: var(--airline-gallery-arrow-hover-bg, rgba( 0, 0, 0, 0.8 ));
}

.airline-gallery__step:focus-visible {
	outline: 2px solid var(--airline-gallery-arrow);
	outline-offset: 2px;
}

.airline-gallery__step[ disabled ] {
	opacity: 0.3;
	cursor: default;
}

.airline-gallery__step[ hidden ] {
	display: none;
}

/*
 * The chevron is an open path, drawn with a stroke and no fill.
 *
 * Both halves of that have to be said in CSS rather than left to the SVG's own
 * presentation attributes, because a stylesheet beats a presentation attribute
 * and themes very commonly ship `svg { fill: currentColor }` for their icon
 * sets — which turns this path into a filled wedge — or `svg path { stroke:
 * none }`, which erases it entirely. Naming the path explicitly is specific
 * enough to win against either.
 */
.airline-gallery__step svg {
	display: block;
	width: var(--airline-gallery-arrow-icon);
	height: var(--airline-gallery-arrow-icon);
	fill: none;
	stroke: currentColor;
	overflow: visible;
}

.airline-gallery__step svg path {
	fill: none;
	stroke: currentColor;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.airline-gallery__step--prev {
	left: 0.75rem;
}

.airline-gallery__step--next {
	right: 0.75rem;
}

/* Follow the pictures out to the window edges rather than staying at the text
   column, where they would sit on top of the middle picture. */
.airline-gallery.is-bleed .airline-gallery__step--prev {
	left: calc( 50% - var(--airline-gallery-window) / 2 + 0.75rem );
}

.airline-gallery.is-bleed .airline-gallery__step--next {
	right: calc( 50% - var(--airline-gallery-window) / 2 + 0.75rem );
}

/* ---------------------------------------------------------------- counter */

.airline-gallery__counter {
	margin: 0.6rem 0 0;
	font-size: 0.8125rem;
	font-variant-numeric: tabular-nums;
	text-align: center;
	opacity: 0.7;
}

/* --------------------------------------------------------------- lightbox */

.airline-gallery-lightbox {
	width: 100vw;
	max-width: 100vw;
	height: 100vh;
	max-height: 100vh;
	padding: 0;
	border: 0;
	background: #0b0b0b;
	color: #fff;
}

.airline-gallery-lightbox::backdrop {
	background: rgba( 0, 0, 0, 0.92 );
}

.airline-gallery-lightbox__figure {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 100%;
	height: 100%;
	margin: 0;
}

.airline-gallery-lightbox img {
	max-width: 100%;
	max-height: 100vh;
	object-fit: contain;
}

.airline-gallery-lightbox__caption {
	position: absolute;
	inset: auto 0 0 0;
	margin: 0;
	padding: 3rem 1.25rem 1.25rem;
	font-size: 0.875rem;
	text-align: center;
	background: linear-gradient( to top, rgba( 0, 0, 0, 0.7 ), rgba( 0, 0, 0, 0 ) );
}

.airline-gallery-lightbox__button {
	position: absolute;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 3rem;
	height: 3rem;
	padding: 0;
	color: #fff;
	background: rgba( 255, 255, 255, 0.12 );
	border: 0;
	border-radius: 50%;
	cursor: pointer;
}

.airline-gallery-lightbox__button:hover {
	background: rgba( 255, 255, 255, 0.24 );
}

.airline-gallery-lightbox__button svg {
	width: 1.5rem;
	height: 1.5rem;
}

.airline-gallery-lightbox__prev {
	top: 50%;
	left: 1rem;
	transform: translateY( -50% );
}

.airline-gallery-lightbox__next {
	top: 50%;
	right: 1rem;
	transform: translateY( -50% );
}

.airline-gallery-lightbox__close {
	top: 1rem;
	right: 1rem;
}

/* -------------------------------------------------------- reduced motion */

@media ( prefers-reduced-motion: reduce ) {
	.airline-gallery__viewport {
		scroll-behavior: auto;
	}

	.airline-gallery__slide,
	.airline-gallery__caption,
	.airline-gallery__step {
		transition: none;
	}
}

/* Smaller on a narrow screen rather than gone: swiping is the natural gesture
   there, but the arrows are also the only visible sign that there is more to
   the right, so taking them away costs more than the room they occupy. */
@media ( max-width: 600px ) {
	.airline-gallery {
		--airline-gallery-arrow-size: 2.5rem;
		--airline-gallery-arrow-icon: 1.1rem;
	}

	.airline-gallery__step--prev,
	.airline-gallery.is-bleed .airline-gallery__step--prev {
		left: 0.4rem;
	}

	.airline-gallery__step--next,
	.airline-gallery.is-bleed .airline-gallery__step--next {
		right: 0.4rem;
	}
}
