/**
 * Pinned Photo Strip
 *
 * The measurements behind every default are in the header comment of
 * photo-strip.php. The one that shapes this file: the active photo is not
 * scaled, it is raised by exactly its own caption's height. So the
 * caption is always laid out beneath the frame and it's the INACTIVE
 * items that get pushed DOWN by their own caption height — which makes
 * the varying raise (77px for a one-line caption, 96px for two lines)
 * fall out of the layout instead of needing to be measured per item.
 */

.cl-pst {
	position: relative;
}

/* Gives the pin something to travel through. Its height is the panel
   plus the scroll range, so the panel stays stuck for exactly as long as
   the strip needs to finish travelling. */
.cl-pst__spacer {
	height: calc(var(--cl-pst-panel-h, 91vh) + var(--cl-pst-scroll-range, 3200px));
}

/**
 * Pinned by script.js with position: fixed, NOT position: sticky.
 *
 * Sticky was the first attempt and it failed outright on this site: a
 * sticky element only sticks within its nearest scrolling ancestor, and
 * any ancestor with overflow other than visible becomes that ancestor.
 * Salient puts overflow: hidden on wrappers around page content, so the
 * panel simply scrolled away with the row it was in. A fixed element
 * ignores ancestor overflow entirely, which is why Story Flip Hero and
 * Video Hero Overlay both pin this way in this theme.
 */
.cl-pst__spacer {
	position: relative;
}

.cl-pst__panel {
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	height: var(--cl-pst-panel-h, 91vh);
	margin: 0 var(--cl-pst-panel-margin, 20px);
	border-radius: var(--cl-pst-panel-radius, 24px);
	background-color: var(--cl-pst-panel-bg, #101010);
	overflow: hidden;
}

/**
 * Promoted to its own compositor layer while pinned, on touch devices only.
 *
 * iOS repaints a position: fixed element on the main thread while the page
 * scrolls on the compositor, so a JS-driven pin visibly lags the finger.
 * will-change is used rather than a translateZ(0) hint because script.js
 * writes the transform property on these elements itself, and a CSS
 * transform here would either be overwritten or fight it.
 *
 * Scoped to pointer: coarse — a needless layer costs memory and can soften
 * text rendering on a desktop.
 */
@media (pointer: coarse) {
	.cl-pst--pinned .cl-pst__panel { will-change: transform; }
}

.cl-pst--pinned .cl-pst__panel {
	position: fixed;
	top: var(--cl-pst-pin-offset, 20px);
}

/**
 * "Hide site header while pinned".
 *
 * A body class rather than anything scoped to this element, because the
 * header sits outside the element entirely — there is no selector from
 * inside the section that can reach it.
 *
 * The transition lives on a separate `hideable` class added at setup, so
 * it is already attached before the first toggle; putting it on the hidden
 * state alone would make the first hide snap instantly and only animate
 * from the second onwards.
 *
 * !important because Salient animates its own header transform on scroll,
 * and without it the theme's inline transform wins and the header simply
 * stays put.
 */
body.cl-pst-header-hideable #header-outer,
body.cl-pst-header-hideable #header-secondary-outer,
body.cl-pst-header-hideable .header-outer {
	transition: transform 0.4s ease;
}

body.cl-pst-header-hidden #header-outer,
body.cl-pst-header-hidden #header-secondary-outer,
body.cl-pst-header-hidden .header-outer {
	transform: translateY(-100%) !important;
}

/**
 * The secondary bar sits ABOVE the main header, so translating both by
 * -100% of their own height leaves the main header covering the gap the
 * secondary one left rather than clearing the screen. Shifting it by the
 * combined height is not expressible in CSS without knowing both, so the
 * main header is moved by its own height plus the secondary bar's, which
 * the script measures and sets.
 */
body.cl-pst-header-hidden #header-outer {
	transform: translateY(calc(-100% - var(--cl-pst-header-extra, 0px))) !important;
}

.cl-pst__heading {
	position: absolute;
	top: var(--cl-pst-heading-top, 10%);
	left: 0;
	right: 0;
	margin: 0;
	color: var(--cl-pst-heading-color, #fff);
	font-family: var(--cl-pst-heading-font-family, inherit);
	font-size: var(--cl-pst-heading-size, 11vw);
	font-weight: var(--cl-pst-heading-weight, inherit);
	line-height: var(--cl-pst-heading-leading, 1);
	letter-spacing: var(--cl-pst-heading-tracking, normal);
	text-align: center;
	/* Deliberately behind the photos — the active card overlapping the
	   heading is a feature of the reference, not an accident. */
	z-index: 1;
}

/* The heading's own type settings must beat the theme's heading rules.
   Salient's phone sizes use `.col h2` (0,1,1), which outranks the plain
   class above, so the element's mobile Heading size was ignored below
   690px (Jordan, 22 Sep 2026). Type only; layout stays on the rule above
   so the centring and reduced-motion overrides still apply. */
.cl-pst h2.cl-pst__heading {
	color: var(--cl-pst-heading-color, #fff);
	font-family: var(--cl-pst-heading-font-family, inherit);
	font-size: var(--cl-pst-heading-size, 11vw);
	font-weight: var(--cl-pst-heading-weight, inherit);
	line-height: var(--cl-pst-heading-leading, 1);
	letter-spacing: var(--cl-pst-heading-tracking, normal);
}

.cl-pst__viewport {
	position: absolute;
	left: 0;
	right: 0;
	/* The baseline every inactive photo's bottom edge sits on. A
	   percentage resolves against the panel height, keeping the proportion
	   the reference has at any panel size. */
	bottom: var(--cl-pst-track-bottom, 5.4%);
	z-index: 2;
}

.cl-pst__track {
	display: flex;
	align-items: flex-end;
	gap: var(--cl-pst-card-gap, 1.4%);
	/* Where the first photo starts and the last one comes to rest.
	   scrollWidth includes padding, so the travel distance accounts for
	   both sides on its own. */
	padding: 0 var(--cl-pst-track-inset, 5.7%);
	/* Positioned by script.js from scroll progress. */
	will-change: transform;
}

.cl-pst__item {
	display: flex;
	flex: 0 0 auto;
	flex-direction: column;
	/* Pushed down by its own caption height while inactive; script.js
	   sets --cl-pst-drop per item from the caption's measured height. */
	transform: translateY(var(--cl-pst-drop, 0px));
	transition: transform var(--cl-pst-duration, 250ms) ease-out;
}

.cl-pst__item.is-active {
	transform: none;
}

/**
 * Height comes from --cl-pst-card-px, which script.js resolves from the
 * configured value. A percentage cannot be used directly here: a
 * percentage height resolves against the parent's height, and every
 * ancestor up to the panel (item, track, viewport) is auto-height, so the
 * browser treats it as auto and the image renders at its natural size —
 * which is exactly what happened, photos filling the whole panel.
 */
/**
 * EVERY card is the same size: the height from --cl-pst-card-px and the
 * width from one shared aspect ratio. The uploaded photo has no bearing
 * on it — the reference proves that, reporting 726.4 x 481.5 for two
 * differently shaped source photos, an aspect of 1.51 either way.
 *
 * An earlier version sized each card from its own photo's natural aspect,
 * which gave a ragged strip of mismatched cards. Before that the width
 * wasn't set at all, and the browser's automatic width for an image is
 * its MAX-CONTENT width — the photo's full pixel width — which made
 * cards about 30% too wide.
 */
.cl-pst__frame {
	position: relative;
	height: var(--cl-pst-card-px, 48vh);
	width: calc(var(--cl-pst-card-px, 48vh) * var(--cl-pst-card-aspect, 1.51));
	border-radius: var(--cl-pst-card-radius, 10px);
	overflow: hidden;
	filter: brightness(var(--cl-pst-dim, 0.4));
	transition: filter var(--cl-pst-duration, 250ms) ease-out;
}

.cl-pst__item.is-active .cl-pst__frame {
	filter: none;
}

/**
 * The photo fills the frame exactly, and script.js sizes the FRAME from
 * the photo's natural aspect so that filling it means no crop.
 *
 * width: auto here was the earlier approach and it interacted badly with
 * the frame's automatic width: the frame took its max-content width (the
 * photo's full natural pixel width) while the photo took its aspect
 * width, so a 1024px-wide source sat 787px wide in a 1024px frame with
 * dead space beside it. Filling means an aspect mismatch crops rather
 * than leaving a gap, which fails visibly rather than silently.
 */
.cl-pst__frame img,
.cl-pst__frame video {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/**
 * The caption block's height IS the active card's lift, so its own
 * proportions decide how far the active photo rises over the heading.
 * Measured off the reference in a 634px-tall panel: 16px above the text
 * (2.5%), 21.5px line spacing (3.4%), 38px below it (6.0%) — 76px in
 * total for one line, which is exactly the raise measured on the card.
 *
 * All three were fixed pixels before (12px padding, 16px text, nothing
 * below), giving a 33px lift on a 1000px panel instead of 120px. That is
 * why the active card sat below the heading instead of overlapping it.
 * script.js resolves the percentages against the panel height, since
 * percentage padding resolves against WIDTH and percentage font-size
 * against the parent's font-size — neither of which is what's wanted.
 */
.cl-pst__caption {
	display: flex;
	flex-direction: column;
	gap: var(--cl-pst-desc-gap, 10px);
	padding-top: var(--cl-pst-caption-pad-top-px, 16px);
	padding-bottom: var(--cl-pst-caption-pad-bottom-px, 38px);
	color: var(--cl-pst-caption-color, #fff);
	font-family: var(--cl-pst-caption-font-family, inherit);
	font-size: var(--cl-pst-caption-px, 19px);
	font-weight: var(--cl-pst-caption-weight, inherit);
	line-height: var(--cl-pst-caption-leading, 1.15);
	opacity: 0;
	transition: opacity var(--cl-pst-duration, 250ms) ease-out;
}

/**
 * The title and year keep their original side-by-side arrangement — that
 * moved off .cl-pst__caption when the caption became a column to make
 * room for the description beneath it.
 */
.cl-pst__caption-row {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 16px;
	width: 100%;
}

/**
 * Body copy under the caption. Sized as a ratio of the caption rather
 * than a fixed px value, so it stays in proportion when the caption size
 * setting changes; line-height is looser than the caption's 1.15 because
 * this is a paragraph to read rather than a label to scan.
 *
 * Its height feeds the card's rise the same way the caption's does —
 * script.js measures the whole caption block, so nothing extra is needed
 * to keep the animation correct.
 */
.cl-pst__desc {
	margin: 0;
	padding: 0;
	font-family: var(--cl-pst-desc-font-family, inherit);
	font-size: var(--cl-pst-desc-size, 0.78em);
	font-weight: var(--cl-pst-desc-weight, inherit);
	line-height: var(--cl-pst-desc-leading, 1.5);
	color: var(--cl-pst-desc-color, rgba(255, 255, 255, 0.72));
}

.cl-pst__item.is-active .cl-pst__caption {
	opacity: 1;
}

.cl-pst__caption-text {
	flex: 1 1 auto;
}

.cl-pst__year {
	flex: 0 0 auto;
	padding: 3px 10px;
	border-radius: 999px;
	background-color: var(--cl-pst-year-bg, #2e2e2e);
	color: var(--cl-pst-year-text, #fff);
	font-size: var(--cl-pst-tag-size, 0.78em);
	font-weight: var(--cl-pst-tag-weight, inherit);
	line-height: 1.5;
}

/**
 * While a handoff is in flight the landing slot's own photo is held back,
 * so the flying element is the only copy on screen — otherwise the same
 * image would be visible twice for the length of the flight, once mid-air
 * and once already sitting in its slot.
 */
.cl-pst__item--slot.is-awaiting-handoff .cl-pst__frame {
	opacity: 0;
}

/**
 * Anything handed to this slot by a Curved Reveal Hero above fills the
 * frame exactly.
 *
 * This rule lives HERE, in the receiving element's stylesheet, and is
 * scoped to the frame the card now sits in. The hero has an equivalent
 * rule of its own, and relying on that alone did not work: the card is
 * reparented into this element, so it stops inheriting the hero's custom
 * properties, and every width the hero's CSS gives it is a calc() built
 * from those. With the properties undefined the calcs collapse and the
 * card sat as a static, centred, square-ish box inside a correctly sized
 * frame — measurably so: a 531px square centred in a 797px frame.
 *
 * The attribute selector matches whatever the hero calls its card without
 * this element having to know the class name.
 */
.cl-pst__frame > .cl-pst__docked,
.cl-pst__frame > .cl-crh__media,
.cl-pst__frame > [data-cl-handoff-card] {
	position: absolute !important;
	inset: 0 !important;
	width: 100% !important;
	height: 100% !important;
	min-width: 0;
	max-width: none;
	margin: 0 !important;
	border-radius: 0;
	overflow: hidden;
}

.cl-pst__frame > .cl-pst__docked > *,
.cl-pst__frame > .cl-crh__media > * {
	width: 100% !important;
	height: 100% !important;
	object-fit: cover;
}

/**
 * Docked: the flown card is now the slot's content. The slot's own photo
 * stays in the layout with visibility rather than display so it can't
 * reflow anything on the way in or out.
 */
.cl-pst__item--slot.is-docked .cl-pst__frame > img {
	visibility: hidden;
}

/**
 * "Behind the heading" is NOT done with z-index here.
 *
 * The first attempt raised this heading's z-index above the body-level
 * flying card, on the reasoning that the panel is still position:
 * absolute during the flight and so isn't a stacking context. That
 * reasoning only holds if nothing ABOVE the panel is one either — and on
 * this site the WPBakery row and Salient wrappers are, so the heading's
 * z-index was capped inside them and the card stayed in front no matter
 * what number went on it.
 *
 * The hero instead draws a copy of this heading over the top of the card
 * (see .cl-crh-heading-echo), which needs no cooperation from any
 * ancestor. Nothing to do here.
 */

/**
 * Small screens.
 *
 * The card sizes, gaps, insets and caption block are all panel-relative
 * already and follow the panel height down on their own, so nothing needs
 * scaling here. Two structural things do need attention:
 *
 * 1. The panel height default is 70vh, and on iOS Safari vh means the
 *    URL-bar-HIDDEN viewport — so a 70vh panel is taller than 70% of what
 *    you can actually see, and the JS pin, which measures against
 *    innerHeight, disagrees with it. svh is the at-rest height and makes
 *    the two agree.
 *
 * 2. The strip is scroll-driven horizontally, so a horizontal swipe over it
 *    should not also drag the page sideways or trigger a back-gesture.
 *    touch-action: pan-y keeps vertical scrolling — which is what actually
 *    drives the strip — and refuses the horizontal component.
 */
@media (max-width: 900px) {
	.cl-pst__spacer {
		height: calc(var(--cl-pst-panel-h, 91vh) + var(--cl-pst-scroll-range, 3200px));
	}

	.cl-pst__panel { touch-action: pan-y; }

	/**
	 * Centre on phones.
	 *
	 * The cards sit a fixed distance off the panel's FLOOR, which is a
	 * desktop proportion: on a 91vh panel the card block fills most of the
	 * height and the remainder reads as margin. On a 70svh phone panel the
	 * same block leaves nearly 200px in one lump between the heading and
	 * the cards. Centring puts that space above and below instead, where
	 * it reads as breathing room rather than a hole.
	 *
	 * The panel's own resting position is centred by script.js — it needs
	 * the measured panel height, which CSS here does not have.
	 */
	.cl-pst--centre-m .cl-pst__panel {
		display: flex;
		flex-direction: column;
		justify-content: center;
		/* The px form, resolved by script.js. A percentage PADDING
		   resolves against the panel's WIDTH, where the same value as a
		   top offset resolved against its height — so the raw setting
		   would mean something different here. */
		gap: var(--cl-pst-heading-top-px, 56px);
		padding: var(--cl-pst-heading-top-px, 56px) 0;
	}

	/**
	 * Heading and cards centre TOGETHER as one block, rather than the
	 * heading being pinned to the top and the cards centred separately.
	 *
	 * Centring only the cards left the panel with equal space above and
	 * below the CARDS — which, with a heading sitting in the top part,
	 * meant far more empty panel underneath than above. Taking both out
	 * of absolute positioning and centring the pair makes the space above
	 * the heading and the space below the caption the same by
	 * construction, whatever the panel height or the caption length.
	 *
	 * The padding is the same value the heading used as its top offset,
	 * so it stays the minimum breathing room at either end.
	 */
	.cl-pst--centre-m .cl-pst__heading {
		position: static;
		flex: 0 0 auto;
	}

	.cl-pst--centre-m .cl-pst__viewport {
		position: static;
		flex: 0 0 auto;
	}

	/* The caption sets the active card's rise, so on a narrow screen a long
	   caption can lift a card most of the way up the panel. Two lines is
	   plenty, and the ellipsis makes the truncation deliberate. */
	.cl-pst__caption-text {
		display: -webkit-box;
		-webkit-line-clamp: 2;
		-webkit-box-orient: vertical;
		overflow: hidden;
	}

	/* Descriptions are NOT clamped: Jordan wants the full text shown on
	   every screen (22 Sep 2026), accepting that a long one lifts the
	   active card further. */
}

@media (prefers-reduced-motion: reduce) {
	/**
	 * No pin, no travel: the strip becomes a plain horizontally
	 * scrollable row with every photo at full brightness and every
	 * caption shown, which is the same information without the motion.
	 */
	.cl-pst__spacer {
		height: auto;
	}

	.cl-pst__panel {
		position: static;
		height: auto;
		padding: 6vh 0;
		margin: 0;
	}

	.cl-pst__heading {
		position: static;
		margin-bottom: 4vh;
	}

	.cl-pst__viewport {
		position: static;
		overflow-x: auto;
	}

	.cl-pst__track {
		transform: none !important;
		padding: 0 var(--cl-pst-panel-margin, 20px);
	}

	.cl-pst__item {
		transform: none !important;
	}

	.cl-pst__frame {
		height: 40vh;
		filter: none;
	}

	.cl-pst__caption {
		opacity: 1;
	}

	.cl-pst__item--slot.is-awaiting-handoff .cl-pst__frame {
		opacity: 1;
	}
}
