/**
 * GGP Slideshow — front end and editor.
 *
 * No JavaScript anywhere. Rotation is a pair of CSS animations; the dots are
 * labels for real radio inputs, read back with :checked and a sibling
 * combinator. See the plugin header for why that is a hard requirement on this
 * site rather than a stunt.
 *
 * Namespaces on this site are deliberate and must not be crossed:
 *   .ggp       page content blocks
 *   .gsh-      header
 *   .gsf-      footer
 *   .fd-       fan deck
 *   .gss-      this block
 */

.ggp-slideshow {
	position: relative;
	/*
	 * clip, never hidden. `hidden` turns an element into a scroll container,
	 * and every position:sticky descendant then re-targets it and silently
	 * stops working — which is exactly how the sticky header dies. `clip`
	 * creates no scroll container.
	 */
	overflow: clip;

	--gss-mark-bg-on: #fff;
	--gss-mark-bg-off: rgba( 255, 255, 255, 0.4 );
	--gss-mark-fg-on: #121212;
	--gss-mark-fg-off: #fff;
	--gss-mark-scale-on: 1.25;
	/*
	 * The ring gets its own two colours rather than borrowing the mark's.
	 * Ring mode sets --gss-mark-bg-on to transparent so the resting pip vanishes
	 * under the ring — and the first version had the ring's arc reading that
	 * same variable, so the arc was painted in transparent. Everything measured
	 * correctly and nothing was visible.
	 */
	--gss-ring-fill: #fff;
	--gss-ring-track: rgba( 255, 255, 255, 0.4 );
	--gss-m-ratio: 4 / 5;
	--gss-stack-bg: #121212;
}

/* -------------------------------------------------------------------------
   The stack
   ------------------------------------------------------------------------- */

/*
 * One grid cell, every slide in it. Grid rather than absolute positioning
 * because on phones the "stack text below image" layout needs the box to grow
 * to its content, and a grid row does that for free while absolute children
 * cannot.
 */
.ggp-slideshow .gss-viewport {
	display: grid;
	grid-template-areas: "gss";
	position: relative;
	overflow: clip;
	width: 100%;
}

/*
 * "Adapt to first image" — an empty box holding the first image's own aspect
 * ratio, read server-side from the attachment record. It reserves the exact
 * final height before any image has downloaded, so there is no layout shift,
 * and it costs no extra request because it never renders an image.
 */
.ggp-slideshow .gss-sizer {
	grid-area: gss;
	margin: 0;
	width: 100%;
	aspect-ratio: var( --gss-ratio, 16 / 9 );
	pointer-events: none;
}

.ggp-slideshow.is-height-fixed .gss-viewport {
	min-height: var( --gss-h, 560px );
}

/*
 * "Adapt to first image" means the box takes the photo's shape — but a photo
 * has no opinion about how much room a heading needs. Feed it a 1920x721
 * banner and a phone gets a 147px-tall strip, which is not enough for a
 * heading, a line of copy and a button, so the copy overflows and gets clipped.
 *
 * The floor only applies when slides actually carry words. A photos-only
 * slideshow keeps adapt behaving exactly as it says on the tin.
 */
.ggp-slideshow.is-height-adapt.has-copy .gss-viewport {
	min-height: 380px;
}

.ggp-slideshow .gss-slide {
	grid-area: gss;
	position: relative;
	min-width: 0;
	visibility: hidden;
	z-index: 1;
	/*
	 * Hold the outgoing slide visible for the whole transition, then cut. It is
	 * sitting underneath the incoming one, which is fading in on top, so it is
	 * fully covered by the time it disappears. Fade them both at once instead
	 * and the section dips towards the page background mid-crossfade.
	 */
	transition: visibility 0s linear var( --gss-fade, 600ms );
}

.ggp-slideshow .gss-slide__inner {
	position: absolute;
	inset: 0;
	opacity: 0;
	transition: opacity 0s linear var( --gss-fade, 600ms );
}

.ggp-slideshow .gss-media {
	position: absolute;
	inset: 0;
}

.ggp-slideshow .gss-img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
	max-width: none;
}

.ggp-slideshow .gss-overlay {
	position: absolute;
	inset: 0;
	background: #000;
	opacity: var( --gss-overlay, 0.35 );
	pointer-events: none;
}

.ggp-slideshow.is-text-dark .gss-overlay {
	background: #fff;
}

/* -------------------------------------------------------------------------
   Auto rotation
   ------------------------------------------------------------------------- */

/*
 * Every slide runs the same animations; only the delay differs. Slide i starts
 * one interval after slide i-1:
 *
 *   delay = -( (n - i) * interval + transition )
 *
 * Negative, so the cycle is already mid-flight at load — the extra transition
 * term lands slide 0 at the END of its fade-in rather than the start, which is
 * what stops the page opening on a blank frame that then fades up.
 *
 * animation-name is set inline per instance, because the keyframes depend on
 * how many slides there are.
 */
.ggp-slideshow.has-autorotate .gss-slide,
.ggp-slideshow.has-autorotate .gss-slide__inner,
.ggp-slideshow.has-autorotate .gss-dot,
.ggp-slideshow.has-autorotate .gss-count,
.ggp-slideshow.has-autorotate .gss-ring,
.ggp-slideshow.has-autorotate .gss-ring__fill {
	animation-duration: var( --gss-cycle, 25s );
	animation-iteration-count: infinite;
	animation-fill-mode: both;
	animation-delay: calc( -1 * ( ( var( --gss-n ) - var( --gss-i ) ) * var( --gss-interval ) + var( --gss-fade ) ) );
}

/* z-index and visibility have to jump at an instant, never glide. */
.ggp-slideshow.has-autorotate .gss-slide,
.ggp-slideshow.has-autorotate .gss-count {
	animation-timing-function: steps( 1, end );
}

.ggp-slideshow.has-autorotate .gss-slide__inner {
	animation-timing-function: cubic-bezier( 0.25, 0.46, 0.45, 0.94 );
}

.ggp-slideshow.has-autorotate .gss-dot,
.ggp-slideshow.has-autorotate .gss-ring__fill {
	animation-timing-function: linear;
}

/* The ring appears and vanishes at an instant; only its arc glides. */
.ggp-slideshow.has-autorotate .gss-ring {
	animation-timing-function: steps( 1, end );
}

/* No dots to click, no rotation running: show the first slide and stop. */
.ggp-slideshow.is-pagination-none:not( .has-autorotate ) .gss-slide:first-of-type {
	visibility: visible;
	z-index: 3;
}

.ggp-slideshow.is-pagination-none:not( .has-autorotate ) .gss-slide:first-of-type > .gss-slide__inner {
	opacity: 1;
}

/* -------------------------------------------------------------------------
   Reduced motion
   ------------------------------------------------------------------------- */

/*
 * Deliberately ahead of the manual-control rules below. These fall back to the
 * first slide, and a visitor who has since clicked a dot must still win — the
 * :checked rules match that here on specificity and beat it on source order.
 */
@media ( prefers-reduced-motion: reduce ) {
	.ggp-slideshow .gss-slide,
	.ggp-slideshow .gss-slide__inner,
	.ggp-slideshow .gss-dot,
	.ggp-slideshow .gss-count,
	.ggp-slideshow .gss-ring,
	.ggp-slideshow .gss-ring__fill {
		animation: none !important;
		transition: none !important;
	}

	.ggp-slideshow.has-autorotate .gss-nav .gss-dot:first-of-type .gss-ring {
		opacity: 1;
	}

	.ggp-slideshow.has-autorotate .gss-nav .gss-dot:first-of-type .gss-ring__fill {
		stroke-dashoffset: 0;
	}

	.ggp-slideshow.has-autorotate .gss-viewport .gss-slide:first-of-type {
		visibility: visible;
		z-index: 3;
	}

	.ggp-slideshow.has-autorotate .gss-viewport .gss-slide:first-of-type > .gss-slide__inner {
		opacity: 1;
	}

	.ggp-slideshow.has-autorotate .gss-nav .gss-dot:first-of-type {
		background-color: var( --gss-mark-bg-on );
		color: var( --gss-mark-fg-on );
	}

	.ggp-slideshow.has-autorotate .gss-nav .gss-counter .gss-count:first-of-type {
		visibility: visible;
	}
}

/* -------------------------------------------------------------------------
   Manual control
   ------------------------------------------------------------------------- */

/*
 * A click on a dot checks a radio, and that is the one piece of state CSS can
 * read. Rotation stops the moment it happens and does not restart: CSS can see
 * a checked radio but has no way to un-check it on a timer. Handing control
 * over permanently is the honest reading of that click.
 *
 * The radios are real inputs, hidden but still focusable, sitting as siblings
 * ahead of everything they drive; the visible dots are their labels. That shape
 * is deliberate. The obvious alternative is :has() on the wrapper, and it does
 * work — but :has() turns the whole subtree into an invalidation scope, and this
 * subtree has fifteen animations changing computed style inside it. Measured on
 * a page carrying three slideshows, the :has() build ran at 47fps against 59fps
 * for this one, for pixel-identical output. The sibling combinator costs
 * nothing, and a hidden radio group is better for keyboard users anyway: tab in,
 * arrow between slides.
 */

.ggp-slideshow > .gss-radio {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	border: 0;
	overflow: hidden;
	white-space: nowrap;
	/* clip-path rather than display:none — a hidden input is still the thing
	   keyboard users tab into and arrow through. display:none would take it out
	   of the tab order entirely. */
	clip-path: inset( 50% );
}

.ggp-slideshow .gss-radio:checked ~ .gss-viewport .gss-slide,
.ggp-slideshow .gss-radio:checked ~ .gss-viewport .gss-slide .gss-slide__inner,
.ggp-slideshow .gss-radio:checked ~ .gss-nav .gss-dot,
.ggp-slideshow .gss-radio:checked ~ .gss-nav .gss-count,
.ggp-slideshow .gss-radio:checked ~ .gss-nav .gss-ring,
.ggp-slideshow .gss-radio:checked ~ .gss-nav .gss-ring__fill {
	animation: none;
}

/* Clear the board first, so whatever was showing gets out of the way. */
.ggp-slideshow .gss-radio:checked ~ .gss-viewport .gss-slide {
	visibility: hidden;
	z-index: 1;
}

.ggp-slideshow .gss-radio:checked ~ .gss-viewport .gss-slide .gss-slide__inner {
	opacity: 0;
}

.ggp-slideshow .gss-radio:nth-of-type( 1 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 1 ),
.ggp-slideshow .gss-radio:nth-of-type( 2 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 2 ),
.ggp-slideshow .gss-radio:nth-of-type( 3 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 3 ),
.ggp-slideshow .gss-radio:nth-of-type( 4 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 4 ),
.ggp-slideshow .gss-radio:nth-of-type( 5 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 5 ),
.ggp-slideshow .gss-radio:nth-of-type( 6 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 6 ),
.ggp-slideshow .gss-radio:nth-of-type( 7 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 7 ),
.ggp-slideshow .gss-radio:nth-of-type( 8 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 8 ),
.ggp-slideshow .gss-radio:nth-of-type( 9 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 9 ),
.ggp-slideshow .gss-radio:nth-of-type( 10 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 10 ) {
	visibility: visible;
	z-index: 3;
	transition: visibility 0s;
}

.ggp-slideshow .gss-radio:nth-of-type( 1 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 1 ) > .gss-slide__inner,
.ggp-slideshow .gss-radio:nth-of-type( 2 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 2 ) > .gss-slide__inner,
.ggp-slideshow .gss-radio:nth-of-type( 3 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 3 ) > .gss-slide__inner,
.ggp-slideshow .gss-radio:nth-of-type( 4 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 4 ) > .gss-slide__inner,
.ggp-slideshow .gss-radio:nth-of-type( 5 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 5 ) > .gss-slide__inner,
.ggp-slideshow .gss-radio:nth-of-type( 6 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 6 ) > .gss-slide__inner,
.ggp-slideshow .gss-radio:nth-of-type( 7 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 7 ) > .gss-slide__inner,
.ggp-slideshow .gss-radio:nth-of-type( 8 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 8 ) > .gss-slide__inner,
.ggp-slideshow .gss-radio:nth-of-type( 9 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 9 ) > .gss-slide__inner,
.ggp-slideshow .gss-radio:nth-of-type( 10 ):checked ~ .gss-viewport .gss-slide:nth-of-type( 10 ) > .gss-slide__inner {
	opacity: 1;
	transition: opacity var( --gss-fade, 600ms ) cubic-bezier( 0.25, 0.46, 0.45, 0.94 );
}

.ggp-slideshow .gss-radio:nth-of-type( 1 ):checked ~ .gss-nav .gss-dot:nth-of-type( 1 ),
.ggp-slideshow .gss-radio:nth-of-type( 2 ):checked ~ .gss-nav .gss-dot:nth-of-type( 2 ),
.ggp-slideshow .gss-radio:nth-of-type( 3 ):checked ~ .gss-nav .gss-dot:nth-of-type( 3 ),
.ggp-slideshow .gss-radio:nth-of-type( 4 ):checked ~ .gss-nav .gss-dot:nth-of-type( 4 ),
.ggp-slideshow .gss-radio:nth-of-type( 5 ):checked ~ .gss-nav .gss-dot:nth-of-type( 5 ),
.ggp-slideshow .gss-radio:nth-of-type( 6 ):checked ~ .gss-nav .gss-dot:nth-of-type( 6 ),
.ggp-slideshow .gss-radio:nth-of-type( 7 ):checked ~ .gss-nav .gss-dot:nth-of-type( 7 ),
.ggp-slideshow .gss-radio:nth-of-type( 8 ):checked ~ .gss-nav .gss-dot:nth-of-type( 8 ),
.ggp-slideshow .gss-radio:nth-of-type( 9 ):checked ~ .gss-nav .gss-dot:nth-of-type( 9 ),
.ggp-slideshow .gss-radio:nth-of-type( 10 ):checked ~ .gss-nav .gss-dot:nth-of-type( 10 ) {
	background-color: var( --gss-mark-bg-on );
	color: var( --gss-mark-fg-on );
	transform: scale( var( --gss-mark-scale-on, 1.25 ) );
}

/* The focus ring belongs on the dot, not on the input nobody can see. */
.ggp-slideshow .gss-radio:nth-of-type( 1 ):focus-visible ~ .gss-nav .gss-dot:nth-of-type( 1 ),
.ggp-slideshow .gss-radio:nth-of-type( 2 ):focus-visible ~ .gss-nav .gss-dot:nth-of-type( 2 ),
.ggp-slideshow .gss-radio:nth-of-type( 3 ):focus-visible ~ .gss-nav .gss-dot:nth-of-type( 3 ),
.ggp-slideshow .gss-radio:nth-of-type( 4 ):focus-visible ~ .gss-nav .gss-dot:nth-of-type( 4 ),
.ggp-slideshow .gss-radio:nth-of-type( 5 ):focus-visible ~ .gss-nav .gss-dot:nth-of-type( 5 ),
.ggp-slideshow .gss-radio:nth-of-type( 6 ):focus-visible ~ .gss-nav .gss-dot:nth-of-type( 6 ),
.ggp-slideshow .gss-radio:nth-of-type( 7 ):focus-visible ~ .gss-nav .gss-dot:nth-of-type( 7 ),
.ggp-slideshow .gss-radio:nth-of-type( 8 ):focus-visible ~ .gss-nav .gss-dot:nth-of-type( 8 ),
.ggp-slideshow .gss-radio:nth-of-type( 9 ):focus-visible ~ .gss-nav .gss-dot:nth-of-type( 9 ),
.ggp-slideshow .gss-radio:nth-of-type( 10 ):focus-visible ~ .gss-nav .gss-dot:nth-of-type( 10 ) {
	outline: 2px solid var( --gss-mark-bg-on );
	outline-offset: 3px;
}

.ggp-slideshow .gss-radio:nth-of-type( 1 ):checked ~ .gss-nav .gss-counter .gss-count:nth-of-type( 1 ),
.ggp-slideshow .gss-radio:nth-of-type( 2 ):checked ~ .gss-nav .gss-counter .gss-count:nth-of-type( 2 ),
.ggp-slideshow .gss-radio:nth-of-type( 3 ):checked ~ .gss-nav .gss-counter .gss-count:nth-of-type( 3 ),
.ggp-slideshow .gss-radio:nth-of-type( 4 ):checked ~ .gss-nav .gss-counter .gss-count:nth-of-type( 4 ),
.ggp-slideshow .gss-radio:nth-of-type( 5 ):checked ~ .gss-nav .gss-counter .gss-count:nth-of-type( 5 ),
.ggp-slideshow .gss-radio:nth-of-type( 6 ):checked ~ .gss-nav .gss-counter .gss-count:nth-of-type( 6 ),
.ggp-slideshow .gss-radio:nth-of-type( 7 ):checked ~ .gss-nav .gss-counter .gss-count:nth-of-type( 7 ),
.ggp-slideshow .gss-radio:nth-of-type( 8 ):checked ~ .gss-nav .gss-counter .gss-count:nth-of-type( 8 ),
.ggp-slideshow .gss-radio:nth-of-type( 9 ):checked ~ .gss-nav .gss-counter .gss-count:nth-of-type( 9 ),
.ggp-slideshow .gss-radio:nth-of-type( 10 ):checked ~ .gss-nav .gss-counter .gss-count:nth-of-type( 10 ) {
	visibility: visible;
}

/*
 * A visitor who has clicked has stopped the clock, so the ring shows complete
 * rather than frozen part-drawn — a half-finished countdown that never moves
 * reads as broken.
 */
.ggp-slideshow .gss-radio:nth-of-type( 1 ):checked ~ .gss-nav .gss-dot:nth-of-type( 1 ) .gss-ring,
.ggp-slideshow .gss-radio:nth-of-type( 2 ):checked ~ .gss-nav .gss-dot:nth-of-type( 2 ) .gss-ring,
.ggp-slideshow .gss-radio:nth-of-type( 3 ):checked ~ .gss-nav .gss-dot:nth-of-type( 3 ) .gss-ring,
.ggp-slideshow .gss-radio:nth-of-type( 4 ):checked ~ .gss-nav .gss-dot:nth-of-type( 4 ) .gss-ring,
.ggp-slideshow .gss-radio:nth-of-type( 5 ):checked ~ .gss-nav .gss-dot:nth-of-type( 5 ) .gss-ring,
.ggp-slideshow .gss-radio:nth-of-type( 6 ):checked ~ .gss-nav .gss-dot:nth-of-type( 6 ) .gss-ring,
.ggp-slideshow .gss-radio:nth-of-type( 7 ):checked ~ .gss-nav .gss-dot:nth-of-type( 7 ) .gss-ring,
.ggp-slideshow .gss-radio:nth-of-type( 8 ):checked ~ .gss-nav .gss-dot:nth-of-type( 8 ) .gss-ring,
.ggp-slideshow .gss-radio:nth-of-type( 9 ):checked ~ .gss-nav .gss-dot:nth-of-type( 9 ) .gss-ring,
.ggp-slideshow .gss-radio:nth-of-type( 10 ):checked ~ .gss-nav .gss-dot:nth-of-type( 10 ) .gss-ring {
	opacity: 1;
}

.ggp-slideshow .gss-radio:nth-of-type( 1 ):checked ~ .gss-nav .gss-dot:nth-of-type( 1 ) .gss-ring__fill,
.ggp-slideshow .gss-radio:nth-of-type( 2 ):checked ~ .gss-nav .gss-dot:nth-of-type( 2 ) .gss-ring__fill,
.ggp-slideshow .gss-radio:nth-of-type( 3 ):checked ~ .gss-nav .gss-dot:nth-of-type( 3 ) .gss-ring__fill,
.ggp-slideshow .gss-radio:nth-of-type( 4 ):checked ~ .gss-nav .gss-dot:nth-of-type( 4 ) .gss-ring__fill,
.ggp-slideshow .gss-radio:nth-of-type( 5 ):checked ~ .gss-nav .gss-dot:nth-of-type( 5 ) .gss-ring__fill,
.ggp-slideshow .gss-radio:nth-of-type( 6 ):checked ~ .gss-nav .gss-dot:nth-of-type( 6 ) .gss-ring__fill,
.ggp-slideshow .gss-radio:nth-of-type( 7 ):checked ~ .gss-nav .gss-dot:nth-of-type( 7 ) .gss-ring__fill,
.ggp-slideshow .gss-radio:nth-of-type( 8 ):checked ~ .gss-nav .gss-dot:nth-of-type( 8 ) .gss-ring__fill,
.ggp-slideshow .gss-radio:nth-of-type( 9 ):checked ~ .gss-nav .gss-dot:nth-of-type( 9 ) .gss-ring__fill,
.ggp-slideshow .gss-radio:nth-of-type( 10 ):checked ~ .gss-nav .gss-dot:nth-of-type( 10 ) .gss-ring__fill {
	stroke-dashoffset: 0;
}

/* -------------------------------------------------------------------------
   Slide copy
   ------------------------------------------------------------------------- */

.ggp-slideshow .gss-content {
	position: absolute;
	inset: 0;
	display: flex;
	padding: clamp( 24px, 5vw, 64px );
	z-index: 2;
}

/*
 * Reserve room on the side the nav is actually on. The old rule always padded
 * the bottom, which was right until the nav could move — put it at the top and
 * the heading ran straight into the dots.
 */
.ggp-slideshow.is-navv-bottom:not( .is-pagination-none ) .gss-content {
	padding-bottom: clamp( 58px, 7vw, 92px );
}

.ggp-slideshow.is-navv-top:not( .is-pagination-none ) .gss-content {
	padding-top: clamp( 52px, 6vw, 84px );
}

/*
 * "safe" is the whole point here. Plain `center` splits any overflow between
 * top and bottom, so copy that is taller than its box loses the top of the
 * heading — the first thing anyone reads. `safe` falls back to start alignment
 * the moment the item overflows, so the heading always survives and it is the
 * tail that gets cut instead.
 */
.ggp-slideshow.is-valign-top .gss-content { align-items: flex-start; }
.ggp-slideshow.is-valign-middle .gss-content { align-items: safe center; }
.ggp-slideshow.is-valign-bottom .gss-content { align-items: safe flex-end; }

.ggp-slideshow.is-align-left .gss-content { justify-content: flex-start; }
.ggp-slideshow.is-align-center .gss-content { justify-content: center; }
.ggp-slideshow.is-align-right .gss-content { justify-content: flex-end; }

.ggp-slideshow .gss-content__inner {
	max-width: 40rem;
}

.ggp-slideshow.is-align-left .gss-content__inner { text-align: left; }
.ggp-slideshow.is-align-center .gss-content__inner { text-align: center; }
.ggp-slideshow.is-align-right .gss-content__inner { text-align: right; }

.ggp-slideshow .gss-heading {
	margin: 0 0 0.45em;
	font-size: clamp( 1.9rem, 4.2vw, 3.35rem );
	line-height: 1.06;
	font-weight: 600;
	letter-spacing: -0.01em;
	text-wrap: balance;
	/* Photographs are busy. A soft shadow keeps the type legible over the
	   handful of frames where the overlay alone is not enough. */
	text-shadow: 0 1px 24px rgba( 0, 0, 0, 0.28 );
}

.ggp-slideshow .gss-text {
	margin: 0 0 1.5em;
	font-size: clamp( 1rem, 1.35vw, 1.16rem );
	line-height: 1.55;
	max-width: 46ch;
	text-shadow: 0 1px 18px rgba( 0, 0, 0, 0.24 );
}

.ggp-slideshow.is-align-center .gss-text { margin-inline: auto; }
.ggp-slideshow.is-align-right .gss-text { margin-inline: auto 0; }

.ggp-slideshow .gss-text:last-child,
.ggp-slideshow .gss-heading:last-child {
	margin-bottom: 0;
}

.ggp-slideshow.is-text-light .gss-content { color: #fff; }
.ggp-slideshow.is-text-dark .gss-content { color: #121212; }

.ggp-slideshow.is-text-dark .gss-heading,
.ggp-slideshow.is-text-dark .gss-text {
	text-shadow: 0 1px 20px rgba( 255, 255, 255, 0.42 );
}

.ggp-slideshow .gss-btn {
	display: inline-block;
	padding: 13px 32px;
	border-radius: 4px;
	font-size: 0.95rem;
	font-weight: 600;
	letter-spacing: 0.02em;
	text-decoration: none;
	transition: transform 0.18s ease, background-color 0.18s ease, color 0.18s ease;
}

.ggp-slideshow .gss-btn:hover,
.ggp-slideshow .gss-btn:focus-visible {
	transform: translateY( -2px );
}

.ggp-slideshow.is-text-light.is-btn-solid .gss-btn {
	background: #fff;
	color: #121212;
}

.ggp-slideshow.is-text-light.is-btn-outline .gss-btn {
	background: transparent;
	color: #fff;
	box-shadow: inset 0 0 0 2px #fff;
}

.ggp-slideshow.is-text-light.is-btn-outline .gss-btn:hover {
	background: #fff;
	color: #121212;
}

.ggp-slideshow.is-text-dark.is-btn-solid .gss-btn {
	background: #121212;
	color: #fff;
}

.ggp-slideshow.is-text-dark.is-btn-outline .gss-btn {
	background: transparent;
	color: #121212;
	box-shadow: inset 0 0 0 2px #121212;
}

.ggp-slideshow.is-text-dark.is-btn-outline .gss-btn:hover {
	background: #121212;
	color: #fff;
}

/* -------------------------------------------------------------------------
   Pagination
   ------------------------------------------------------------------------- */

/*
 * The nav is a full-size overlay rather than a strip pinned to the bottom, so
 * one flex alignment pair can put it in any of the nine positions. It has to be
 * pointer-events: none or it would sit on top of the slide's own button and
 * swallow every click; the dots opt back in.
 */
.ggp-slideshow .gss-nav {
	position: absolute;
	inset: 0;
	z-index: 20;
	display: flex;
	gap: calc( 10px * var( --gss-ns ) );
	align-items: flex-end;
	justify-content: center;
	padding: clamp( 14px, 2.2vw, 26px ) clamp( 16px, 3vw, 26px );
	pointer-events: none;
}

.ggp-slideshow .gss-dot {
	pointer-events: auto;
}

/*
 * --gss-ns is the scale the sizes below actually read, and it is defined here
 * rather than inline for one specific reason: an inline custom property beats
 * every stylesheet rule, media query included. render.php publishes both the
 * desktop and phone values as separate properties and this indirection picks
 * the right one at the right width.
 */
.ggp-slideshow {
	--gss-ns: var( --gss-nav-scale, 1 );
}

.ggp-slideshow.is-navv-top .gss-nav { align-items: flex-start; }
.ggp-slideshow.is-navv-middle .gss-nav { align-items: center; }
.ggp-slideshow.is-navv-bottom .gss-nav { align-items: flex-end; }

.ggp-slideshow.is-navh-left .gss-nav { justify-content: flex-start; }
.ggp-slideshow.is-navh-center .gss-nav { justify-content: center; }
.ggp-slideshow.is-navh-right .gss-nav { justify-content: flex-end; }

.ggp-slideshow.is-pagination-none .gss-nav {
	display: none;
}

.ggp-slideshow .gss-dot {
	display: block;
	margin: 0;
	cursor: pointer;
	background-color: var( --gss-mark-bg-off );
	color: var( --gss-mark-fg-off );
	font: inherit;
	transition: background-color 0.2s ease, transform 0.2s ease, color 0.2s ease;
}

/* Dots */
.ggp-slideshow.is-pagination-dots .gss-dot {
	width: calc( 10px * var( --gss-ns ) );
	height: calc( 10px * var( --gss-ns ) );
	border-radius: 50%;
	box-shadow: 0 0 0 1px rgba( 0, 0, 0, 0.14 );
}

/* Numbers */
.ggp-slideshow.is-pagination-numbers .gss-dot {
	--gss-mark-scale-on: 1;
	width: calc( 30px * var( --gss-ns ) );
	height: calc( 30px * var( --gss-ns ) );
	border-radius: 50%;
	display: grid;
	place-items: center;
	font-size: calc( 0.8rem * var( --gss-ns ) );
	font-weight: 600;
	line-height: 1;
}

.ggp-slideshow.is-pagination-numbers .gss-dot::after {
	content: attr( data-n );
}

/*
 * Progress ring — a countdown you can watch.
 *
 * The trick is that nothing new drives the resting dot. Ring mode just
 * redefines what "current" means for the existing gss-mark animation: on
 * becomes transparent-and-small instead of white-and-large, so the dot gets out
 * of the way exactly when the ring arrives, using the animation that was
 * already running. The SVG on top handles the sweep.
 */
.ggp-slideshow.is-pagination-ring .gss-dot {
	position: relative;
	/*
	 * border-box, explicitly. The label's default is content-box, so an 18px
	 * width plus 6px padding gave a 30px element whose painted core was the full
	 * 18px — five fat dots instead of five small pips inside a ring's footprint.
	 * Here 18px is the ring's outer size and the padding shrinks the pip.
	 */
	box-sizing: border-box;
	width: calc( 18px * var( --gss-ns ) );
	height: calc( 18px * var( --gss-ns ) );
	padding: calc( 5.5px * var( --gss-ns ) );
	border-radius: 50%;
	background-clip: content-box;
	--gss-mark-bg-on: transparent;
	--gss-mark-scale-on: 1;
}

.ggp-slideshow .gss-ring {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	opacity: 0;
	/* Start the sweep at twelve o'clock rather than three. */
	transform: rotate( -90deg );
	overflow: visible;
	pointer-events: none;
	/* The same 1px dark edge the plain dots carry, for the same reason: white
	   pagination over a pale photo otherwise disappears. */
	filter: drop-shadow( 0 0 1px rgba( 0, 0, 0, 0.35 ) );
}

.ggp-slideshow .gss-ring circle {
	fill: none;
	stroke-width: 2;
	stroke-linecap: round;
}

/* The track is deliberately fainter than the arc — the whole point is that you
   can tell at a glance how much is drawn and how much is left. */
.ggp-slideshow .gss-ring__track {
	stroke: var( --gss-ring-track );
	opacity: 0.55;
}

.ggp-slideshow .gss-ring__fill {
	stroke: var( --gss-ring-fill );
	stroke-dashoffset: 47.1239;
}

/* Counter — slim dashes plus a live "3 / 5" readout. */
.ggp-slideshow.is-pagination-counter .gss-dot {
	--gss-mark-scale-on: 1;
	width: calc( 22px * var( --gss-ns ) );
	height: calc( 3px * var( --gss-ns ) );
	border-radius: 2px;
}

.ggp-slideshow .gss-counter {
	display: grid;
	grid-template-areas: "count";
	margin-left: 8px;
}

.ggp-slideshow .gss-count {
	grid-area: count;
	visibility: hidden;
	font-variant-numeric: tabular-nums;
}

.ggp-slideshow .gss-counter,
.ggp-slideshow .gss-total {
	color: var( --gss-mark-fg-off );
	font-size: calc( 0.85rem * var( --gss-ns ) );
	font-weight: 600;
	letter-spacing: 0.04em;
}

.ggp-slideshow .gss-total {
	font-weight: 400;
	opacity: 0.75;
	margin-left: 2px;
}

.ggp-slideshow.is-text-dark {
	--gss-ring-fill: #121212;
	--gss-ring-track: rgba( 18, 18, 18, 0.35 );
}

.ggp-slideshow.is-text-dark .gss-dot {
	--gss-mark-bg-on: #121212;
	--gss-mark-bg-off: rgba( 18, 18, 18, 0.35 );
	--gss-mark-fg-on: #fff;
	--gss-mark-fg-off: #121212;
}

.ggp-slideshow.is-text-dark .gss-counter,
.ggp-slideshow.is-text-dark .gss-total {
	color: #121212;
}

/* -------------------------------------------------------------------------
   Phones and tablets
   ------------------------------------------------------------------------- */

/* Breakpoints match the Shopify theme this site is migrating from, so the
   layout changes at the same widths visitors are already used to. */

@media ( max-width: 749px ) {
	.ggp-slideshow.gss-hide-mobile {
		display: none;
	}

	.ggp-slideshow.is-height-fixed .gss-viewport {
		/*
		 * svh, not vh: on mobile Safari vh is the tallest the viewport ever
		 * gets, so a "full height" hero is cropped by the toolbar until the
		 * user scrolls.
		 *
		 * The 380px in there is a floor under the 78svh cap, not a new minimum.
		 * On a short viewport — a phone in landscape, a small window — 78svh
		 * can land near 200px, and that cap was squeezing the box below the
		 * height its own copy needs. A deliberately small custom height still
		 * wins, because the outer min() keeps --gss-h as the ceiling.
		 */
		min-height: min( var( --gss-h, 560px ), max( 380px, 78svh ) );
	}

	.ggp-slideshow.is-height-adapt.has-copy .gss-viewport {
		min-height: 340px;
	}

	/*
	 * Type on phones. The old sizes were desktop values with a vw term bolted
	 * on: the heading still hit its 2.3rem ceiling on a 390px screen, and the
	 * button never moved at all. Every value here is viewport-relative with a
	 * floor and a ceiling, so the whole block scales together.
	 */
	.ggp-slideshow .gss-heading {
		font-size: clamp( 1.45rem, 6.2vw, 2rem );
		line-height: 1.14;
		margin-bottom: 0.4em;
	}

	.ggp-slideshow .gss-text {
		font-size: clamp( 0.92rem, 3.6vw, 1.05rem );
		line-height: 1.45;
		margin-bottom: 1.15em;
	}

	.ggp-slideshow .gss-btn {
		padding: clamp( 10px, 3vw, 13px ) clamp( 18px, 5.5vw, 30px );
		font-size: clamp( 0.85rem, 3.5vw, 0.95rem );
	}

	.ggp-slideshow {
		--gss-ns: var( --gss-nav-scale-m, 0.8 );
	}

	.ggp-slideshow .gss-nav {
		padding: 14px 16px;
	}

	.ggp-slideshow.is-navmv-top .gss-nav { align-items: flex-start; }
	.ggp-slideshow.is-navmv-middle .gss-nav { align-items: center; }
	.ggp-slideshow.is-navmv-bottom .gss-nav { align-items: flex-end; }

	.ggp-slideshow.is-navmh-left .gss-nav { justify-content: flex-start; }
	.ggp-slideshow.is-navmh-center .gss-nav { justify-content: center; }
	.ggp-slideshow.is-navmh-right .gss-nav { justify-content: flex-end; }

	.ggp-slideshow .gss-content {
		padding: 20px 18px;
	}

	.ggp-slideshow.is-navv-bottom:not( .is-pagination-none ) .gss-content,
	.ggp-slideshow.is-navmv-bottom:not( .is-pagination-none ) .gss-content {
		padding: 20px 18px 50px;
	}

	.ggp-slideshow.is-navv-top:not( .is-pagination-none ) .gss-content,
	.ggp-slideshow.is-navmv-top:not( .is-pagination-none ) .gss-content {
		padding: 48px 18px 20px;
	}

	/* Stack text below image */
	.ggp-slideshow.stacks-on-mobile .gss-sizer {
		display: none;
	}

	.ggp-slideshow.stacks-on-mobile.is-height-fixed .gss-viewport {
		min-height: 0;
	}

	.ggp-slideshow.stacks-on-mobile .gss-slide__inner {
		position: static;
	}

	.ggp-slideshow.stacks-on-mobile .gss-media {
		position: relative;
		inset: auto;
		aspect-ratio: var( --gss-m-ratio, 4 / 5 );
	}

	.ggp-slideshow.stacks-on-mobile .gss-overlay {
		display: none;
	}

	.ggp-slideshow.stacks-on-mobile .gss-content {
		position: static;
		background: var( --gss-stack-bg, #121212 );
		padding: 26px 20px 56px;
		color: #fff;
	}

	/* Stacked layout grows to fit its copy, so no floor is wanted here. */
	.ggp-slideshow.stacks-on-mobile.is-height-adapt.has-copy .gss-viewport {
		min-height: 0;
	}

	.ggp-slideshow.stacks-on-mobile .gss-heading,
	.ggp-slideshow.stacks-on-mobile .gss-text {
		text-shadow: none;
	}

	.ggp-slideshow.stacks-on-mobile.is-btn-solid .gss-btn {
		background: #fff;
		color: #121212;
	}

	.ggp-slideshow.stacks-on-mobile.is-btn-outline .gss-btn {
		color: #fff;
		box-shadow: inset 0 0 0 2px #fff;
	}

	.ggp-slideshow.stacks-on-mobile .gss-dot {
		--gss-mark-bg-on: #fff;
		--gss-mark-bg-off: rgba( 255, 255, 255, 0.4 );
	}
}

@media ( min-width: 750px ) and ( max-width: 989px ) {
	.ggp-slideshow.gss-hide-tablet {
		display: none;
	}
}
