/*
 * Janda Events — global components.
 *
 * Header / drawer  Figma 51284:4601 (desktop bar), 51288:3387 (mobile bar),
 *                  51298:7838 (open drawer)
 * Footer           Figma 51284:10714 (desktop), 51288:5547 (mobile)
 */

/* ==========================================================================
   Site header
   ========================================================================== */

/*
 * The bar follows the page in both states, fixed rather than sticky, so it
 * never gives or takes flow height as it compacts:
 *
 * - Over a hero it sits exactly where the absolute version used to. The hero
 *   partials already reserve its height with their own padding-top, so taking
 *   it out of flow costs no layout.
 * - On inner pages the solid variant hands that reservation to <main> — see
 *   .site-header--solid below.
 *
 * Everything that moves on scroll is transitioned here, but only once
 * `.is-sticky-ready` is set — see initStickyHeader() in app.js. A page restored
 * mid-scroll gets its compact state on the first frame, and without that gate
 * it would visibly animate down from full height on load.
 */
.site-header {
	--header-top: 0px;

	position: fixed;
	inset: 0 0 auto;
	z-index: var(--z-header);
	height: var(--header-h);
	padding-inline: var(--gutter);
	color: var(--c-white);
}

.site-header.is-sticky-ready {
	transition: height var(--dur) var(--ease), background-color var(--dur) var(--ease),
		box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease);
}

/*
 * The bar does not only resize when it sticks, it repaints: white text becomes
 * dark and the knockout wordmark swaps for the colour mark. Without these the
 * height eases while the contents snap, which reads as a glitch rather than a
 * transition. The two stacked marks cross-fade on `opacity`; .btn brings its
 * own transition, so the CTA needs nothing here.
 */
.site-header.is-sticky-ready .site-logo__img {
	transition: height var(--dur) var(--ease), opacity var(--dur) var(--ease);
}

.site-header.is-sticky-ready .site-nav__list > .menu-item > :is(a, button),
.site-header.is-sticky-ready .site-header__toggle {
	transition: color var(--dur) var(--ease), background-color var(--dur) var(--ease);
}

/*
 * Scrolled state. On a hero page this is what makes the transparent bar
 * readable once the page has moved under it — it takes the same paint as the
 * solid variant (shared selectors above), plus the shadow and the compact
 * height both variants get.
 *
 * Re-pointing --header-h/--header-logo-h at the compact pair rather than
 * setting `height` directly is deliberate: the mega panel's `top` and the
 * drawer's max-height read the same token, so they follow the bar down without
 * a second rule each.
 */
.site-header.is-stuck {
	--header-h: var(--header-h-stuck);
	--header-logo-h: var(--header-logo-h-stuck);

	background-color: var(--c-white);
	box-shadow: 0 2px 16px rgba(0, 0, 0, 0.08);
}

/*
 * Logged-in views: the admin toolbar is itself fixed at the top, so a bar
 * pinned to 0 would sit under it. WP's own breakpoints — 32px tall above
 * 782px, 46px below it, and not fixed at all under 600px, where it scrolls
 * away and the site bar goes back to the top of the viewport.
 *
 * --header-top is what the mega panel adds to --header-h to find the bottom
 * edge of the bar, so it has to be set rather than only applied to `top`.
 */
.admin-bar .site-header {
	--header-top: 32px;

	top: var(--header-top);
}

@media (max-width: 782px) {
	.admin-bar .site-header {
		--header-top: 46px;
	}
}

@media (max-width: 600px) {
	.admin-bar .site-header {
		--header-top: 0px;
	}
}

/* Templates without a full-bleed dark hero get an opaque bar. */
/*
 * Inner pages. This used to be a dark teal bar, chosen when no design showed
 * the solid state; the Terms, Contact and 404 frames all draw it white, so it
 * follows them. The wordmark is a white-on-transparent raster, so it is
 * inverted rather than swapped for a second asset.
 */
.site-header--solid {
	background-color: var(--c-white);
	border-bottom: 1px solid #ececec;
}

/*
 * The solid bar is out of flow like the transparent one, and <main> reserves
 * the height it used to occupy. Leaving it in flow as `sticky` was simpler but
 * wrong: the page loses exactly the height the bar drops the moment it
 * compacts, so every inner page nudged its content up 24px as you started
 * scrolling. The reservation reads --header-h from :root, never the compact
 * override — that one is scoped to the header element — so it never moves.
 *
 * Adjacency is guaranteed by header.php, which opens <main class="site-main">
 * directly after this partial.
 */
.site-header--solid + .site-main {
	padding-top: var(--header-h);
}

/*
 * The dark-on-white paint below is shared with the scrolled state: once a
 * transparent bar has a white fill under it, it needs exactly the inner-page
 * treatment. Keep the two selectors together in every rule — splitting them is
 * how the scrolled bar ends up with white text on white.
 */

/* .site-nav__list > .menu-item > a sets the white a few rules below and matches
   at the same weight, so these carry the extra class to win on specificity. */
.site-header--solid .site-nav__list > .menu-item > :is(a, button),
.site-header--solid .site-header__toggle,
.site-header.is-stuck .site-nav__list > .menu-item > :is(a, button),
.site-header.is-stuck .site-header__toggle {
	color: var(--c-body);
}

.site-header--solid .site-nav__list > .menu-item > :is(a, button):hover,
.site-header.is-stuck .site-nav__list > .menu-item > :is(a, button):hover {
	color: var(--c-primary-500);
}

.site-header--solid .site-logo__img--white,
.site-header.is-stuck .site-logo__img--white {
	opacity: 0;
}

.site-header--solid .site-logo__img--colour,
.site-header.is-stuck .site-logo__img--colour {
	opacity: 1;
}

/* btn--light paints through a background-image, so clearing that is what
   actually reveals the fill — same reason .has-panel-open does it below. */
.site-header--solid .btn.site-header__cta,
.site-header.is-stuck .btn.site-header__cta {
	background-image: none;
	background-color: var(--c-primary-500);
	border-color: var(--c-primary-500);
	color: var(--c-white);
}

.site-header--solid .btn.site-header__cta:hover,
.site-header--solid .btn.site-header__cta:focus-visible,
.site-header.is-stuck .btn.site-header__cta:hover,
.site-header.is-stuck .btn.site-header__cta:focus-visible {
	background-color: var(--c-primary-800);
	border-color: var(--c-primary-800);
	color: var(--c-white);
}

/*
 * Three explicit tracks — menu / logo / menu — so the logo stays optically
 * centred whether or not the side menus are showing. Columns are assigned by
 * hand because `display: none` removes an item from auto-placement.
 */
.site-header__inner {
	position: relative;
	display: grid;
	grid-template-columns: 1fr auto 1fr;
	align-items: center;
	gap: var(--space-16);
	width: 100%;
	height: 100%;
	max-width: var(--container);
	margin-inline: auto;
}

.site-nav--left {
	grid-column: 1;
	justify-self: start;
}

.site-header__brand {
	grid-column: 2;
	justify-self: center;
	min-width: max-content;
}

.site-logo {
	position: relative;
	display: block;
}

.site-logo__img {
	display: block;
	width: auto;
	height: var(--header-logo-h);
}

/*
 * The two marks are stacked so the bar can cross-fade between them without the
 * brand cell changing width mid-transition — the header grid centres the logo
 * on its own track, so a swap that reflowed would drag the wordmark sideways.
 * The white one keeps the flow height; the colour one is lifted out over it and
 * revealed by the white-bar states above.
 */
.site-logo__img--colour {
	position: absolute;
	top: 0;
	left: 0;
	opacity: 0;
}

.site-header__end {
	grid-column: 3;
	justify-self: end;
	display: flex;
	align-items: center;
	gap: var(--space-24);
}

/* --- Desktop menus -------------------------------------------------------- */

.site-nav__list {
	display: flex;
	align-items: center;
	gap: var(--space-24);
	margin: 0;
	padding: 0;
	list-style: none;
}

.site-nav .menu-item {
	position: relative;
}

/*
 * Scoped to the bar's own list rather than to any .menu-item: the panel's cards
 * are menu items too, and their links inherited this nowrap — which is how
 * "Celebrant + Soundtrack + MC" came to run across the card next to it instead
 * of wrapping onto a second line the way the frame draws it.
 */
/*
 * Anchor or button: the five items that own a mega panel render as a button —
 * see janda_menu_disclosure_button() — and the bar must not be able to tell.
 * The reset below is what a button needs to sit in the row like the links do;
 * `font: inherit` matters most, since a button otherwise takes the UA font
 * rather than the body one and reads a size smaller than its neighbours.
 */
.site-nav__list > .menu-item > :is(a, button) {
	display: inline-flex;
	align-items: center;
	gap: var(--space-4);
	margin: 0;
	padding: 0;
	background: none;
	border: 0;
	font: inherit;
	font-size: var(--fs-body-sm);
	font-weight: var(--fw-medium);
	line-height: 16px;
	color: currentcolor;
	text-decoration: none;
	white-space: nowrap;
	cursor: pointer;
}

.site-nav__list > .menu-item > :is(a, button):hover {
	color: var(--c-primary-100);
}

.site-nav .menu-item__chevron {
	width: 12px;
	height: 12px;
	transform: rotate(180deg);
	transition: transform var(--dur-fast) var(--ease);
}

.site-nav .menu-item > :is(a, button)[aria-expanded="true"] .menu-item__chevron {
	transform: rotate(0deg);
}

.site-header__cta {
	border-color: var(--c-primary-50);
	font-size: var(--fs-body-md);
	line-height: 24px;
}

/* --- Desktop dropdown panel ---------------------------------------------- */

.site-nav .nav__dropdown {
	position: absolute;
	top: calc(100% + var(--space-16));
	left: 0;
	z-index: var(--z-dropdown);
	display: flex;
	flex-direction: column;
	gap: var(--space-16);
	min-width: 240px;
	margin: 0;
	padding: var(--space-24);
	list-style: none;
	background-color: var(--c-white);
	border-top: 1px solid #e6e6e6;
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-raised);
	opacity: 0;
	visibility: hidden;
	transform: translateY(-4px);
	transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease),
		visibility var(--dur-fast) var(--ease);
}

.site-nav .menu-item:hover > .nav__dropdown,
.site-nav .menu-item:focus-within > .nav__dropdown,
.site-nav .menu-item.is-open > .nav__dropdown {
	opacity: 1;
	visibility: visible;
	transform: translateY(0);
}

.site-nav .nav__dropdown a {
	font-size: var(--fs-body-md);
	font-weight: var(--fw-medium);
	line-height: 16px;
	color: var(--c-black);
	text-decoration: none;
	white-space: nowrap;
}

.site-nav .nav__dropdown a:hover {
	color: var(--c-primary-500);
}

/* Right-hand menus drop from their right edge so they never overflow. */
.site-nav--right .nav__dropdown {
	left: auto;
	right: 0;
}

/* --- Hamburger ------------------------------------------------------------ */

/* Figma 51288:1209 — 24px icon in 6px of padding on a 10% white plate. */
.site-header__toggle {
	position: relative;
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 36px;
	height: 36px;
	padding: 6px;
	border-radius: var(--radius-sm);
	background-color: rgba(255, 255, 255, 0.1);
	color: inherit;
}

/*
 * The design draws a 36px plate, which is under the 44px minimum for a touch
 * target — and this is the only way into the menu on every screen below 1280.
 * The hit area is grown to 44 with a transparent overlay so the painted plate
 * still measures 36 and nothing shifts in the bar.
 */
.site-header__toggle::before {
	content: "";
	position: absolute;
	top: 50%;
	left: 50%;
	width: 44px;
	height: 44px;
	transform: translate(-50%, -50%);
}

.site-header__toggle .icon {
	flex: 0 0 auto;
	width: 24px;
	height: 24px;
}

.site-header__toggle-close {
	display: none;
}

.site-header__toggle[aria-expanded="true"] .site-header__toggle-open {
	display: none;
}

.site-header__toggle[aria-expanded="true"] .site-header__toggle-close {
	display: block;
}

/* ==========================================================================
   Mobile drawer
   ========================================================================== */

.site-drawer {
	position: absolute;
	inset: 100% 0 auto;
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--space-40);
	padding: var(--space-24);
	background-color: var(--c-white);
	border-top: 1px solid #e6e6e6;
	box-shadow: 0 10px 4px rgba(0, 0, 0, 0.25);
	max-height: calc(100dvh - var(--header-h));
	overflow-y: auto;
}

.site-drawer[hidden] {
	display: none;
}

.site-drawer__nav {
	width: 100%;
}

.site-drawer__list {
	display: flex;
	flex-direction: column;
	gap: var(--space-40);
	margin: 0;
	padding: 0;
	list-style: none;
}

.site-drawer__list + .site-drawer__list {
	margin-top: var(--space-40);
}

.site-drawer__list > .menu-item > a {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-16);
	width: 100%;
	font-size: var(--fs-body-md);
	font-weight: var(--fw-medium);
	line-height: 16px;
	color: var(--c-black);
	text-decoration: none;
}

.site-drawer .menu-item__chevron {
	flex: 0 0 auto;
	width: 16px;
	height: 16px;
	transform: rotate(90deg);
	transition: transform var(--dur-fast) var(--ease);
}

.site-drawer .menu-item > a[aria-expanded="true"] .menu-item__chevron {
	transform: rotate(0deg);
}

.site-drawer .nav__dropdown {
	display: flex;
	flex-direction: column;
	gap: var(--space-16);
	margin: var(--space-16) 0 0;
	padding: 0 0 0 var(--space-16);
	list-style: none;
	border-left: 1px solid var(--c-border);
}

.site-drawer .nav__dropdown[hidden] {
	display: none;
}

.site-drawer .nav__dropdown a {
	font-size: var(--fs-body-sm);
	color: var(--c-body);
	text-decoration: none;
}

.btn--accent {
	background-color: var(--c-accent-cta);
	color: var(--c-white);
}

.btn--accent:hover,
.btn--accent:focus-visible {
	background-color: var(--c-primary-800);
	color: var(--c-white);
}

/* ==========================================================================
   Header breakpoints
   
   The header switches at 76em, not the 64em the rest of the layout uses: the
   full bar needs ~1190px (494 left menu + 121 logo + 448 right group + gaps +
   gutters), and below that the centred logo track was being squeezed to zero
   width. Tablets therefore keep the drawer while their content columns go
   two-up, which is the correct trade.
   ========================================================================== */

@media (min-width: 76em) {
	/* Heights live on --header-h / --header-logo-h in tokens.css, which the
	   scrolled state re-points; only the bar's contents change here. */
	.site-header__toggle,
	.site-drawer {
		display: none;
	}
}

@media (max-width: 75.999em) {
	.site-nav,
	.site-header__cta {
		display: none;
	}

	/* Figma 51288:1190 — the mobile bar is logo-left, hamburger-right. */
	.site-header__inner {
		grid-template-columns: auto 1fr;
	}

	.site-header__brand {
		grid-column: 1;
		justify-self: start;
	}

	.site-header__end {
		grid-column: 2;
	}
}

/* ==========================================================================
   Site footer
   ========================================================================== */

.site-footer {
	position: relative;
	color: var(--c-white);
}

/*
 * The angle is the footer's top edge cut out of the full-bleed photo above it.
 * Drawn in normal flow it sat on the page background and left a white wedge, so
 * when a CTA band precedes the footer the angle rides up over it by its own
 * height and the transparent half reveals the photo.
 */
.site-footer__angle {
	--angle-height: var(--footer-angle-h);
	position: relative;
	z-index: 1;
	display: block;
	color: #333;
	line-height: 0;
}

.site-footer--overlap .site-footer__angle {
	margin-top: calc(var(--angle-height) * -1);
}

.site-footer__angle .icon {
	display: block;
	width: 100%;
	height: var(--angle-height);
}

/*
 * --footer-angle-h is fractional (64.29 / 81.5), so the angle's bottom edge and
 * the body's top edge meet mid-pixel. The SVG's last row is antialiased against
 * whatever is behind it — the page, which is white — and that showed as a pale
 * hairline the full width of the footer. This lays 2px of the footer's own
 * ground across the join. It is invisible where it overlaps the angle, whose
 * bottom row is solid across the full width, and it covers the seam wherever
 * the boundary happens to land.
 */
.site-footer__angle::after {
	content: "";
	position: absolute;
	inset: auto 0 -1px;
	height: 2px;
	background-color: #333;
}

/* 32/24 on the mobile frame, 64/48 on the desktop one — --gutter already steps
   at the same breakpoint, so the block padding follows it. */
.site-footer__body {
	background-color: #333;
	padding-block: var(--space-32);
	padding-inline: var(--gutter);
}

@media (min-width: 64em) {
	.site-footer__body {
		padding-block: var(--space-64);
	}
}

.site-footer__inner {
	display: flex;
	flex-direction: column;
	gap: var(--space-40);
	width: 100%;
	max-width: var(--container);
	margin-inline: auto;
}

.site-footer__grid {
	display: flex;
	flex-direction: column;
	gap: var(--space-40);
}

/* The brand is a flex row (logo + inline socials on mobile); without this the
   logo shrinks to nothing once the column itself is allowed to shrink. */
.site-footer__brand .site-logo {
	flex: 0 0 auto;
}

/* 169.22x120 on the mobile frame, 216x153.176 on the desktop one. */
.site-footer .site-logo__img {
	width: 169.22px;
	max-width: 100%;
	height: auto;
}

@media (min-width: 64em) {
	.site-footer .site-logo__img {
		width: 216px;
	}
}

.site-footer__menus {
	display: grid;
	grid-template-columns: repeat(2, minmax(0, 1fr));
	/* Both axes are 24 on the frame — the row gap was 32. */
	gap: var(--space-24);
}

/* The mobile frame (51288:5547) keeps two 178px columns at 428, so only drop to
   a single column once two of them genuinely stop fitting. */
@media (max-width: 22.5em) {
	.site-footer__menus {
		grid-template-columns: 1fr;
	}
}

/* Mobile places the social row alongside the logo (Figma 51288:5547). */
.site-footer__brand {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-24);
}

.footer-menu__title {
	margin: 0 0 var(--space-16);
	font-family: var(--font-body);
	font-size: var(--fs-body-md);
	font-weight: var(--fw-semibold);
	line-height: 22px;
	color: var(--c-white);
	letter-spacing: 0;
}

.footer-menu__list {
	display: flex;
	flex-direction: column;
	gap: 6px;
	margin: 0;
	padding: 0;
	list-style: none;
}

.footer-menu__list a {
	display: inline-block;
	font-size: var(--fs-body-sm);
	line-height: 1.5;
	color: rgba(255, 255, 255, 0.8);
	text-decoration: none;
}

/*
 * Touch sizing. The gap cannot simply shrink to pay for a 44px target: the
 * design's rows sit on a 27px pitch (21px line + 6px gap), so 13px of padding
 * per side was adding 20px to every row and 800px to the mobile footer.
 *
 * Each link is taken to the 24px WCAG 2.5.8 minimum instead, and the gap pays
 * for exactly that — 24 + 3 is the same 27px pitch the frame draws.
 */
@media (max-width: 63.999em) {
	.footer-menu__list {
		gap: 3px;
	}

	/*
	 * Short labels ("DJs") left the inline-block box at 23px wide and failed
	 * WCAG 2.5.8's 24x24 minimum on width alone — the height is already 24 by
	 * the sizing above. min-width, not display:block: block collapses the
	 * inline baseline strut and pulls 4px out of the mobile footer.
	 */
	.footer-menu__list a {
		min-width: 24px;
		padding-block: 1.5px;
	}

	.site-footer__contact-list {
		gap: 1px;
	}

	.site-footer__contact-list a {
		display: inline-block;
		padding-block: 1.5px;
	}

	.site-footer__legal-list a {
		display: inline-block;
		padding-block: 1.5px;
	}
}

.footer-menu__list a:hover {
	color: var(--c-white);
}

/*
 * Right-aligned in both frames, not just the desktop one: the mobile footer
 * (51288:5547) sets the block hard against the gutter — its rule measures 100px
 * ending 24px from the edge, the same rule the 1440 frame draws — so the
 * alignment belongs to the component rather than to the wide breakpoint, and
 * the media query below is left holding only the widths it actually changes.
 */
.site-footer__contact {
	display: flex;
	flex-direction: column;
	gap: var(--space-24);
	align-items: flex-end;
}

.site-footer__contact-title {
	width: 100.49px;
	margin: 0;
	padding-bottom: var(--space-8);
	border-bottom: 1px solid var(--c-white);
	font-size: 12.5px;
	line-height: 18.79px;
	color: var(--c-white);
	text-align: right;
}

.site-footer__contact-list {
	display: flex;
	flex-direction: column;
	align-items: flex-end;
	gap: var(--space-4);
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: var(--fs-body-xs);
	line-height: 21.51px;
	text-align: right;
}

.site-footer__contact-list a {
	color: var(--c-white);
	text-decoration: none;
}

.site-footer__contact-list a:hover {
	color: var(--c-primary-100);
}

.site-footer__social {
	display: flex;
	align-items: center;
	gap: 33px;
	margin: 0;
	padding: 0;
	list-style: none;
}

/* Only one of the two social rows shows at a time. */
.site-footer__social--inline {
	gap: var(--space-16);
}

.site-footer > .site-footer__body .site-footer__inner > .site-footer__social {
	display: none;
}

@media (min-width: 64em) {
	.site-footer__social--inline {
		display: none;
	}

	.site-footer > .site-footer__body .site-footer__inner > .site-footer__social {
		display: flex;
	}
}

.site-footer__social a {
	display: block;
	color: var(--c-white);
}

.site-footer__social .icon {
	width: 40px;
	height: 40px;
}

.site-footer__social a:hover {
	color: var(--c-primary-500);
}

.site-footer__legal-list {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-16);
	margin: 0;
	padding: 0;
	list-style: none;
	font-size: var(--fs-body-xs);
}

.site-footer__legal-list a {
	color: rgba(255, 255, 255, 0.7);
	text-decoration: none;
}

@media (min-width: 64em) {
	.site-footer__grid {
		flex-direction: row;
		align-items: flex-start;
		gap: var(--space-24);
	}

	.site-footer__brand {
		flex: 1 0 0;
		min-width: 0;
	}

	/* Fluid between the breakpoint and the 1440 frame; pinned to the Figma
	   widths once the full 1344 container fits (see the 84em block below). */
	.site-footer__menus {
		flex: 1 1 auto;
		min-width: 0;
		max-width: 640px;
		grid-template-columns: repeat(3, minmax(0, 1fr));
		grid-template-rows: repeat(2, auto);
	}

	.footer-menu--entertainment { grid-column: 1; grid-row: 1; }
	.footer-menu--packages      { grid-column: 2; grid-row: 1; }
	.footer-menu--videography   { grid-column: 3; grid-row: 1; }
	.footer-menu--company       { grid-column: 2; grid-row: 2; }
	.footer-menu--resources     { grid-column: 3; grid-row: 2; }

	/* Alignment is the component's own — see .site-footer__contact above. Only
	   the track sizing changes here. */
	.site-footer__contact {
		flex: 0 1 328px;
		min-width: 220px;
		justify-content: center;
	}
}

/* Once the full container fits, hold the footer tracks at the design's ratio —
   percentages rather than pixels so they follow the container as it grows. */
@media (min-width: 84em) {
	.site-footer__menus {
		flex: 0 0 47.62%; /* 640 / 1344 */
		width: 47.62%;
		max-width: none;
	}

	.site-footer__contact {
		flex: 0 0 24.4%; /* 328 / 1344 */
		width: 24.4%;
	}
}

/* ==========================================================================
   Package card — Figma 51340:4623
   ========================================================================== */

/*
 * 323 of the 1344 design container. Expressed against --container so the card
 * grows with it above 1440 and the rail keeps reaching the right edge; below
 * 1440 the variable is a flat 1344, so this evaluates to exactly 323.
 */
.card-package {
	display: flex;
	width: calc(var(--container) * 0.2403);
	/* 436 of 1344, so the card's height scales with its width and the whole
	   object stays in the design's proportion. */
	min-height: calc(var(--container) * 0.3244);
	padding: 11px;
	background-color: var(--c-white);
	border: 1px solid var(--c-body);
	border-radius: 12px;
	overflow: hidden;
}

.card-package__inner {
	display: flex;
	flex-direction: column;
	gap: var(--space-16);
	width: 100%;
}

.card-package__media {
	aspect-ratio: 301 / 171;
	margin: 0;
	border-radius: 12px;
	overflow: hidden;
}

.card-package__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.card-package__body {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: var(--space-8);
	padding: var(--space-12);
}

.card-package__head {
	display: flex;
	flex-direction: column;
	gap: var(--space-12);
}

.card-package__meta {
	display: flex;
	flex-direction: column;
	gap: var(--space-4);
}

/* Figma 51059:327 — 26 tall including the 1px stroke, so the height is set
   explicitly rather than left to padding + border. */
.card-package__chip {
	align-self: flex-start;
	height: 26px;
	display: inline-flex;
	align-items: center;
	padding: 0 var(--space-8);
	border: 1px solid var(--c-primary-100);
	border-radius: var(--radius-pill);
	background-color: rgba(239, 252, 252, 0.3);
	font-size: var(--fs-body-sm);
	line-height: 22px;
	color: var(--c-body);
}

.card-package__title {
	margin: 0;
	font-family: var(--font-display);
	font-size: var(--fs-display-lg);
	font-weight: var(--fw-regular);
	line-height: 32px;
	letter-spacing: 0;
	color: var(--c-primary-500);
}

.card-package__title a {
	color: inherit;
	text-decoration: none;
}

/* The card's whole face is the click target; the footer link is decorative. */
.card-package__title a::after {
	content: "";
	position: absolute;
	inset: 0;
}

.card-package {
	position: relative;
}

/*
 * Clamped to three lines, as the frame does — its Wedding Celebrants card cuts
 * mid-sentence at "Beyond the legal paperwork". The cards are equal height, so
 * without this the single longest excerpt sets the height of the whole rail:
 * measured 539 against the frame's ~335.
 */
.card-package__text {
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 3;
	line-clamp: 3;
	overflow: hidden;
	margin: 0;
	font-size: var(--fs-body-md);
	line-height: var(--lh-body);
	color: var(--c-body);
}

/* margin-top: auto keeps the link on the card's floor so the bottoms align.
   Figma 51059:388 is a 47-tall block — 12 above and below its 22.86 label —
   which together with the body's own 12 is the 24 of air under the link. */
.card-package__link {
	display: inline-flex;
	align-items: center;
	gap: var(--space-8);
	margin-top: auto;
	padding-block: var(--space-12);
	font-family: var(--font-display);
	font-size: 1.09375rem; /* 17.5 */
	line-height: 22.86px;
	color: var(--c-primary-500);
	text-decoration: none;
	text-transform: capitalize;
}

.card-package__link .icon {
	width: 20px;
	height: 20px;
	transition: transform var(--dur-fast) var(--ease);
}

.card-package:hover .card-package__link .icon {
	transform: translateX(3px);
}

/* ==========================================================================
   Global CTA band — Figma 51284:5073
   ========================================================================== */

.cta-band {
	position: relative;
	display: flex;
	align-items: center;
	min-height: 643px;
	padding-block: var(--space-64);
	padding-inline: var(--gutter);
	overflow: hidden;
	color: var(--c-white);
	isolation: isolate;
}

.cta-band__media {
	position: absolute;
	inset: 0;
	z-index: -1;
}

/*
 * --cta-focus-y arrives inline from the page's own focal point. The band is
 * 1440x643 and the photographs are not: a portrait ceremony shot centred here
 * shows nothing but canopy, while the Wedding Celebrant frame crops the same
 * picture 82% down to land on the couple. 50% — the browser's own default —
 * is what every page that sets nothing keeps.
 */
.cta-band__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: 50% var(--cta-focus-y, 50%);
}

/* Figma applies a flat 20% black wash over the photo. */
.cta-band__media::after {
	content: "";
	position: absolute;
	inset: 0;
	background-color: rgba(0, 0, 0, 0.2);
}

.cta-band__inner {
	width: 100%;
	max-width: var(--container);
	margin-inline: auto;
}

.cta-band__content {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--space-16);
	max-width: 687px;
	color: #f2efea;
}

.cta-band__title {
	margin: 0;
	color: inherit;
}

.cta-band__text {
	margin: 0;
	font-size: var(--fs-body-md);
	line-height: 20px;
}

@media (max-width: 63.999em) {
	.cta-band {
		min-height: 480px;
	}
}

/*
 * Phones crop the same photo far tighter, so the flat 20% wash the 1440 frame
 * specifies leaves the heading and the line under it sitting on the brightest
 * part of the shot. Below 48em the wash becomes a vertical scrim that deepens
 * through the block the copy occupies — darkest at the bottom, where the
 * button is — instead of one even film. The desktop frame is untouched.
 */
@media (max-width: 47.999em) {
	.cta-band__media::after {
		background-color: transparent;
		background-image: linear-gradient(
			to bottom,
			rgba(0, 0, 0, 0.3) 0%,
			rgba(0, 0, 0, 0.55) 40%,
			rgba(0, 0, 0, 0.62) 100%
		);
	}
}

/* ==========================================================================
   Artist card — Figma 51284:4811
   ========================================================================== */

.card-artist {
	display: flex;
	flex-direction: column;
	gap: var(--space-16);
	background-color: var(--c-white);
}

.card-artist[hidden] {
	display: none;
}

/*
 * Portrait on phones, square from the 3-column grid up. The 428 Wedding
 * Artists frame (51288:1687) crops these at 183×235; the 1440 frame
 * (51284:4811) is a 318 square.
 */
.card-artist__media {
	position: relative;
	margin: 0;
	aspect-ratio: 183 / 235;
	overflow: hidden;
}

@media (min-width: 48em) {
	.card-artist__media {
		aspect-ratio: 1;
	}
}

/*
 * Weighted high. These cards are portrait shots squared off, and a centred crop
 * cut the celebrant's head at the top of the card on /celebrants/ — the same
 * failure the gallery and the profile portrait had. A quarter down keeps heads
 * in frame; a landscape card is scaled by height, so its framing is unchanged.
 */
.card-artist__media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
	object-position: 50% 25%;
}

/*
 * A scrim that deepens the lower third of the photo on hover.
 *
 * Deliberately the dark brand navy rather than the highlight teal: the artists'
 * names are burnt into these photos at different heights (Alisha's sits at the
 * top, Jake's at the bottom), and a teal wash would put teal type on a teal
 * ground. Darkening lifts the contrast on those names instead of sinking them.
 *
 * Kept light — the play button below carries its own solid fill, so the scrim
 * is only there for depth.
 */
.card-artist__media::after {
	content: "";
	position: absolute;
	inset: 0;
	background-image: linear-gradient(
		to top,
		rgba(35, 75, 86, 0.38),
		rgba(35, 75, 86, 0.12) 45%,
		transparent 72%
	);
	opacity: 0;
	pointer-events: none;
}

/*
 * The large play affordance that rises out of the bottom-right of the photo on
 * hover. Sits above the scrim, and is clipped by the media's overflow, so the
 * inset is what keeps it clear of the corner.
 */
.card-artist__play {
	position: absolute;
	right: 16px;
	bottom: 16px;
	z-index: 1;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 60px;
	height: 60px;
	border-radius: var(--radius-circle);
	background-color: var(--c-primary-500);
	color: var(--c-white);
	/* Starts low and small so it rises into place rather than just appearing. */
	opacity: 0;
	translate: 0 12px;
	scale: 0.75;
	pointer-events: none;
}

/*
 * Button form — see artist-card.php. The UA chrome comes off, and the control
 * stays untargetable until it is actually visible: an invisible 60px target
 * sitting over the photo would otherwise swallow the clicks meant for the
 * card's own link.
 */
button.card-artist__play {
	padding: 0;
	border: 0;
	font: inherit;
	cursor: pointer;
}

.card-artist__play .icon {
	width: 26px;
	height: 26px;
	/* The glyph's mass sits right of centre; nudge it back into the disc. */
	margin-left: 3px;
}

.card-artist__foot {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--space-24);
}

.card-artist__meta {
	display: flex;
	flex-direction: column;
	gap: var(--space-4);
	min-width: 0;
}

.card-artist__name {
	margin: 0;
	font-family: var(--font-body);
	font-size: var(--fs-body-xl);
	font-weight: var(--fw-regular);
	line-height: var(--lh-body);
	letter-spacing: 0;
	color: var(--c-body);
}

.card-artist__name a {
	color: inherit;
	text-decoration: none;
}

.card-artist__name a::after {
	content: "";
	position: absolute;
	inset: 0;
}

.card-artist {
	position: relative;
}

.card-artist__lines {
	display: flex;
	flex-direction: column;
	gap: 2px;
}

.card-artist__role {
	margin: 0;
	font-size: var(--fs-body-sm);
	line-height: var(--lh-body);
	color: var(--c-body);
}

/*
 * No case transform: the design's casing varies per artist ("SINGER /
 * GUITARIST / DJ" but "Vocalist / DJ"), so it comes from the content and
 * forcing uppercase here overrode it.
 */
.card-artist__genre {
	margin: 0;
	font-size: var(--fs-body-xs);
	line-height: 21.51px;
	color: #909087;
	/* "KEYBOARDIST/PIANIST" has no spaces and overran the narrow two-up card
	   on phones; it stays on one line wherever it fits. */
	overflow-wrap: break-word;
}

/* Figma 51284:4815 — 26px circle, 1px teal outline. */
/* Button form — the flow's foot disc opens the video popup. */
button.card-artist__more {
	padding: 0;
	background: none;
	font: inherit;
	cursor: pointer;
}

.card-artist__more {
	display: inline-flex;
	flex: 0 0 auto;
	align-items: center;
	justify-content: center;
	width: 26px;
	height: 26px;
	border: 1px solid var(--c-primary-500);
	border-radius: var(--radius-circle);
	color: var(--c-primary-500);
	line-height: 1;
	text-decoration: none;
	transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

/* Figma 51284:4815 — the glyph is a play triangle sized inside the 26px ring. */
.card-artist__more .icon {
	width: 100%;
	height: 100%;
}

/* ==========================================================================
   Artist card — hover / focus

   The photo pushes in, a scrim deepens the base of it, the card lifts, and the
   large play disc rises out of the bottom-right corner. The small ring in the
   foot steps aside while that happens — two play affordances at once read as a
   mistake, and the disc is the louder of the two.

   Two things make this feel settled rather than mechanical:

   1. Every property runs off the same pair of curves, not one per property.
   2. Entry and exit are asymmetric — it opens over ~450ms and closes in ~260ms.
      Slow-in, quick-out is most of the difference between "premium" and
      "laggy"; a hover that takes as long to leave as it did to arrive feels
      like it is dragging behind the cursor.

   Only transform, translate, scale and opacity are animated, so the work stays
   on the compositor — the sole exception is the media's box-shadow, which is
   cheap here because the element is small and the blur is static.

   Gated behind hover: hover so it can never stick on a touch device, and
   mirrored on :focus-within so keyboard users get the same affordance — the
   card's whole surface is already one link via .card-artist__name a::after.
   ========================================================================== */

@media (hover: hover) and (pointer: fine) {
	.card-artist {
		/* Decisive, then settle. Used for everything arriving. */
		--card-ease-out: cubic-bezier(0.22, 1, 0.36, 1);
		/* A touch of overshoot, for the disc only. */
		--card-ease-back: cubic-bezier(0.34, 1.4, 0.64, 1);
		/* Symmetrical and quick. Used for everything leaving. */
		--card-ease-in: cubic-bezier(0.4, 0, 0.6, 1);
		--card-exit: 260ms;
		--card-enter: 450ms;

		transition: transform var(--card-exit) var(--card-ease-in);
	}

	.card-artist__media {
		transition: box-shadow var(--card-exit) var(--card-ease-in);
	}

	.card-artist__media img {
		transition: transform 420ms var(--card-ease-in);
	}

	.card-artist__media::after {
		transition: opacity var(--card-exit) var(--card-ease-in);
	}

	.card-artist__play {
		transition: opacity 160ms var(--card-ease-in), translate var(--card-exit) var(--card-ease-in),
			scale var(--card-exit) var(--card-ease-in);
	}

	.card-artist__more {
		transition: opacity 160ms var(--card-ease-in), scale var(--card-exit) var(--card-ease-in);
	}

	.card-artist__name {
		transition: color var(--card-exit) var(--card-ease-in);
	}

	/* --- arriving --------------------------------------------------------
	 * Each rule only swaps the duration and curve, so the entry and exit
	 * states stay in one place instead of being written out twice.
	 * ------------------------------------------------------------------ */

	.card-artist:hover,
	.card-artist:focus-within {
		transform: translateY(-6px);
		transition-duration: var(--card-enter);
		transition-timing-function: var(--card-ease-out);
	}

	.card-artist:hover .card-artist__media,
	.card-artist:focus-within .card-artist__media {
		box-shadow: var(--shadow-raised);
		transition-duration: var(--card-enter);
		transition-timing-function: var(--card-ease-out);
	}

	/*
	 * Matches the push-in on the client's reference capture, which lands around
	 * 1.15. Given the longest duration of the group: the photo should still be
	 * drifting after everything else has settled.
	 */
	.card-artist:hover .card-artist__media img,
	.card-artist:focus-within .card-artist__media img {
		transform: scale(1.15);
		transition-duration: 900ms;
		transition-timing-function: var(--card-ease-out);
	}

	.card-artist:hover .card-artist__media::after,
	.card-artist:focus-within .card-artist__media::after {
		opacity: 1;
		transition-duration: var(--card-enter);
		transition-timing-function: var(--card-ease-out);
	}

	/* Held back a beat so it rises out of a photo that is already moving. */
	.card-artist:hover .card-artist__play,
	.card-artist:focus-within .card-artist__play {
		opacity: 1;
		translate: 0 0;
		scale: 1;
		/* Clickable exactly while it is visible. */
		pointer-events: auto;
		transition-duration: 240ms, 520ms, 520ms;
		transition-timing-function: var(--card-ease-out), var(--card-ease-back), var(--card-ease-back);
		transition-delay: 70ms;
	}

	/* Out of the way — the disc above has taken over the job, so this stops
	   taking clicks as well as stops being visible. Faded but still hit-testable,
	   it was an invisible target sitting over the corner of the foot. */
	.card-artist:hover .card-artist__more,
	.card-artist:focus-within .card-artist__more {
		opacity: 0;
		scale: 0.8;
		pointer-events: none;
		transition-duration: 200ms;
		transition-timing-function: var(--card-ease-out);
	}

	.card-artist:hover .card-artist__name,
	.card-artist:focus-within .card-artist__name {
		color: var(--c-primary-800);
		transition-duration: var(--card-enter);
		transition-timing-function: var(--card-ease-out);
	}

	/*
	 * Colour and the disc still change; only the movement is dropped. The disc
	 * cross-fades at full size instead of rising, and the foot ring still steps
	 * aside so the card never shows both at once.
	 */
	@media (prefers-reduced-motion: reduce) {
		.card-artist,
		.card-artist__media,
		.card-artist__media img {
			transition: none;
		}

		.card-artist__play {
			translate: 0 0;
			scale: 1;
			transition: opacity 160ms linear;
		}

		.card-artist__more {
			transition: opacity 160ms linear;
		}

		.card-artist:hover,
		.card-artist:focus-within,
		.card-artist:hover .card-artist__media img,
		.card-artist:focus-within .card-artist__media img {
			transform: none;
		}

		.card-artist:hover .card-artist__play,
		.card-artist:focus-within .card-artist__play,
		.card-artist:hover .card-artist__more,
		.card-artist:focus-within .card-artist__more {
			translate: 0 0;
			scale: 1;
			transition-delay: 0ms;
		}

		.card-artist:hover .card-artist__media,
		.card-artist:focus-within .card-artist__media {
			box-shadow: none;
		}
	}
}

/* ==========================================================================
   Mega menu panel
   Figma: Celebrants 51294:5523, Entertainment 51298:6647, Wedding Packages
   51298:8074, Company 51298:8167, Resources 51298:8220. Intro column on the
   left, the children on the right, full-bleed white panel under an opaque
   header. Three right-hand sides — see class-janda-nav-walker.php.
   ========================================================================== */

/* Hangs off the bottom of the bar, so it reads --header-h rather than a
   literal — the compact scrolled bar moves it up with no rule of its own. */
.site-nav .nav-panel {
	position: fixed;
	left: 0;
	right: 0;
	top: calc(var(--header-top) + var(--header-h));
	z-index: var(--z-dropdown);
	background-color: var(--c-white);
	border-top: 1px solid #e6e6e6;
	box-shadow: 0 10px 8px rgba(0, 0, 0, 0.25);
	opacity: 0;
	visibility: hidden;
	transform: translateY(-6px);
	/*
	 * Visibility is held until the fade has finished on the way out, and flips
	 * at once on the way in — as a transitioned discrete property it otherwise
	 * changes halfway through, and the panel spends the first 125ms unfocusable.
	 * That is long enough that Enter on a parent item opened the panel and then
	 * failed to move focus into it, leaving keyboard users on the trigger.
	 */
	transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease),
		visibility 0s linear var(--dur), top var(--dur) var(--ease);
}

.site-nav .menu-item:hover > .nav-panel,
.site-nav .menu-item:focus-within > .nav-panel,
.site-nav .menu-item.is-open > .nav-panel {
	opacity: 1;
	visibility: visible;
	transform: translateY(0);
	transition-delay: 0s;
}

/*
 * Escape dismisses the panel while focus stays on the trigger, which on its own
 * would re-satisfy :focus-within and reopen it immediately. This wins on
 * specificity so the panel stays shut until focus leaves the item or a pointer
 * returns to it — see initDropdowns() in app.js.
 */
.site-nav .menu-item.is-dismissed:focus-within > .nav-panel {
	opacity: 0;
	visibility: hidden;
}

/*
 * The frames are a 1440 panel padded 48 all round: a 400 column, a 40 gap, then
 * the children. The left inset repeats the container's own centring maths so
 * the heading lines up with the page under it (100%, not 100vw, so a scrollbar
 * cannot push it out) — and the right side is left open, because the Packages
 * and Entertainment rails are drawn running off the edge of the frame. The
 * feature panels close it again below.
 */
.nav-panel__inner {
	display: grid;
	grid-template-columns: minmax(0, 400px) minmax(0, 1fr);
	gap: var(--space-40);
	width: 100%;
	padding-block: var(--space-48);
	padding-inline: max(var(--gutter), calc((100% - var(--container)) / 2)) 0;
}

.nav-panel--feature .nav-panel__inner {
	padding-inline-end: max(var(--gutter), calc((100% - var(--container)) / 2));
}

.nav-panel__intro {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--space-24);
}

.nav-panel__heading {
	margin: 0;
	font-family: var(--font-display);
	font-size: var(--fs-display-2xl);
	font-weight: var(--fw-regular);
	line-height: var(--lh-display);
	letter-spacing: var(--ls-display);
	color: var(--c-primary-500);
}

.nav-panel__text {
	margin: 0;
	font-size: var(--fs-body-lg);
	line-height: var(--lh-body);
	color: var(--c-body);
}

.nav-panel__more {
	font-family: var(--font-display);
	font-size: 1.09375rem; /* 17.5, matching the card links */
	color: var(--c-primary-500);
}

.nav-panel__more .icon {
	width: 20px;
	height: 20px;
}

/* The cards and, under them, the rail's buttons. */
.nav-panel__deck {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--space-40);
	min-width: 0;
}

.nav-panel__cards {
	gap: var(--space-24);
	width: 100%;
	margin: 0;
	padding: 0;
	scroll-padding-inline: 0;
	list-style: none;
}

.nav-panel__nav {
	display: flex;
	gap: var(--space-8);
}

.nav-panel__nav .rail-btn--prev .icon {
	rotate: 180deg;
}

.nav-panel__card {
	flex: 0 0 280px;
}

.nav-panel__card-link {
	display: flex;
	flex-direction: column;
	gap: var(--space-24);
	color: var(--c-black);
	text-decoration: none;
}

.nav-panel__card-media {
	display: block;
	height: 320px;
	overflow: hidden;
}

.nav-panel__card-media img {
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.nav-panel__card-media--monochrome img {
	filter: grayscale(1);
}

.nav-panel__card-title {
	font-family: var(--font-display);
	font-size: 1.625rem; /* 26 — Figma Heading/lg, the size the frames set here */
	line-height: var(--lh-display);
	letter-spacing: var(--ls-display);
	color: var(--c-black);
	transition: color var(--dur-fast) var(--ease);
}

.nav-panel__card-link:hover .nav-panel__card-title,
.nav-panel__card-link:focus-visible .nav-panel__card-title {
	color: var(--c-primary-500);
	text-decoration: underline;
	text-underline-offset: 0.15em;
}

/* The Entertainment panel's artist cards: the shared component, cropped to the
   frame's 280×320 rather than the square the Featured Talent rail uses. */
.nav-panel__card--talent {
	flex: 0 0 280px;
}

.nav-panel__card--talent .card-artist__media {
	aspect-ratio: 280 / 320;
}

/* ==========================================================================
   Mega menu — feature tiles (Company 51298:8167, Resources 51298:8220)
   Two 448×340 tiles with the picture behind the copy. At rest the title sits
   over the foot of the photo; hovering brings up the panel copy and the cue,
   and deepens the gradient to carry it.
   ========================================================================== */

.nav-panel__tiles {
	display: flex;
	gap: var(--space-8);
	margin: 0;
	padding: 0;
	list-style: none;
}

.nav-panel__tile {
	flex: 1 1 0;
	min-width: 0;
}

.nav-panel__tile-link {
	position: relative;
	display: flex;
	align-items: flex-end;
	height: 340px;
	padding: var(--space-24);
	overflow: hidden;
	color: var(--c-white);
	text-decoration: none;
}

.nav-panel__tile-media,
.nav-panel__tile-media img {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* Rest gradient — the frame runs it from the 57% mark. */
.nav-panel__tile-link::after {
	content: "";
	position: absolute;
	inset: 0;
	background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0) 57%, rgba(0, 0, 0, 1));
}

/*
 * Hover gradient. A second layer rather than a redeclared one: gradient stops
 * do not animate, so swapping the value on hover would snap.
 */
.nav-panel__tile-media::after {
	content: "";
	position: absolute;
	inset: 0;
	background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 1));
	opacity: 0;
	transition: opacity var(--dur) var(--ease);
}

.nav-panel__tile-link:hover .nav-panel__tile-media::after,
.nav-panel__tile-link:focus-visible .nav-panel__tile-media::after {
	opacity: 1;
}

/*
 * z-index, not just position: the rest gradient is the link's own ::after and
 * so paints after — that is, on top of — this block. Without the lift the
 * title, the copy and the cue all sit under it and come out at 44% of their
 * colour, which on a bright photo is the difference between reading and not.
 */
.nav-panel__tile-body {
	position: relative;
	z-index: 1;
	display: flex;
	flex-direction: column;
	width: 100%;
}

.nav-panel__tile-title {
	font-family: var(--font-display);
	font-size: 1.625rem; /* 26 — Figma Heading/lg */
	line-height: var(--lh-display);
	letter-spacing: var(--ls-display);
}

/*
 * Collapsed to a zero row at rest and opened to its content height on hover, so
 * the copy lifts the title instead of being clipped behind it. The 16px above
 * the copy belongs to this block rather than to the body's gap, which would
 * otherwise hold the title 16px off the padding while nothing is showing.
 */
.nav-panel__tile-reveal {
	display: grid;
	grid-template-rows: 0fr;
	opacity: 0;
	transition: grid-template-rows var(--dur) var(--ease), opacity var(--dur) var(--ease);
}

.nav-panel__tile-link:hover .nav-panel__tile-reveal,
.nav-panel__tile-link:focus-visible .nav-panel__tile-reveal {
	grid-template-rows: 1fr;
	opacity: 1;
}

.nav-panel__tile-reveal-inner {
	display: flex;
	flex-direction: column;
	gap: var(--space-16);
	min-height: 0;
	padding-top: var(--space-16);
	overflow: hidden;
}

.nav-panel__tile-text {
	font-size: var(--fs-body-md);
	line-height: var(--lh-body);
}

.nav-panel__tile-more {
	font-family: var(--font-display);
	font-size: 1.09375rem; /* 17.5, matching the intro's link */
	font-weight: var(--fw-regular);
	color: var(--c-primary-500);
}

.nav-panel__tile-more .icon {
	width: 20px;
	height: 20px;
}

/* An open panel turns the transparent header opaque so the white panel reads
   as one surface with it. */
.site-header.has-panel-open {
	background-color: var(--c-white);
	color: var(--c-primary-900);
	border-bottom: 1px solid #e6e6e6;
}

.site-header.has-panel-open .site-nav__list > .menu-item > :is(a, button) {
	color: var(--c-primary-900);
}

.site-header.has-panel-open .site-nav__list > .menu-item.is-open > a,
.site-header.has-panel-open .site-nav__list > .menu-item:hover > a {
	color: var(--c-primary-500);
	text-decoration: underline;
	text-underline-offset: 0.3em;
}

.site-header.has-panel-open .site-header__cta {
	background-image: none;
	background-color: var(--c-primary-500);
	color: var(--c-white);
}

/* The logo is a white knockout, so it needs the colour mark on a white bar. */
.site-header.has-panel-open .site-logo__img--white {
	opacity: 0;
}

.site-header.has-panel-open .site-logo__img--colour {
	opacity: 1;
}

/* A card whose target has no featured image yet keeps its footprint instead of
   collapsing, so the panel does not jump when the client adds the photo. */
.nav-panel__card-link:not(:has(.nav-panel__card-media))::before {
	content: "";
	display: block;
	width: 100%;
	height: 320px;
	background-color: var(--c-primary-50);
	border: 1px solid var(--c-border);
}

/* ==========================================================================
   Drawer mega panel

   The drawer opens the same panel the bar does — see mobile-nav.php. The bar
   splits it into a 400px intro column beside the cards; at 428 that split has
   nowhere to go, so it stacks: intro, then the rail with its buttons under it,
   as the drawer frame draws it. Everything else — the three right-hand sides,
   the rail behaviour, the card markup — is inherited, so a change to the panel
   lands in both places.
   ========================================================================== */

/*
 * Drilled state: the open section fills the drawer. Every other row goes, the
 * open row's own label goes (the panel heading names it), and so does the CTA —
 * which is what the frames show. The back row above it is the way out; see
 * mobile-nav.php for why it exists when the design does not draw one.
 */
.site-drawer.is-drilled .site-drawer__list > .menu-item:not(.is-active),
.site-drawer.is-drilled .site-drawer__list > .menu-item.is-active > a,
.site-drawer.is-drilled .site-drawer__cta {
	display: none;
}

/* A list whose items are all hidden still holds the 40px it is given between
   lists, which would sit as a gap above the panel. */
.site-drawer.is-drilled .site-drawer__list:not(:has(.is-active)) {
	display: none;
}

.site-drawer__back {
	display: flex;
	align-items: center;
	gap: var(--space-8);
	margin: 0;
	padding: 0;
	background: none;
	border: 0;
	font: inherit;
	font-size: var(--fs-body-sm);
	font-weight: var(--fw-medium);
	line-height: 16px;
	color: var(--c-body);
	cursor: pointer;
}

.site-drawer__back[hidden] {
	display: none;
}

.site-drawer__back:hover {
	color: var(--c-primary-500);
}

/* The rows point their chevron right; the way back points it the other way. */
.site-drawer__back-icon {
	width: 16px;
	height: 16px;
	transform: rotate(-90deg);
}

/* Drilled, the panel is the drawer's first content and needs no offset from a
   row that is no longer above it. */
.site-drawer.is-drilled .nav-panel {
	margin-top: 0;
}

.site-drawer .nav-panel {
	width: 100%;
	margin-top: var(--space-24);
}

.site-drawer .nav-panel[hidden] {
	display: none;
}

/* Stacked, and flush to the row above rather than carrying the bar's 48px. */
.site-drawer .nav-panel__inner {
	display: flex;
	flex-direction: column;
	gap: var(--space-24);
	padding: 0;
}

.site-drawer .nav-panel__intro {
	gap: var(--space-16);
}

/* display-2xl is a 64px headline on the bar; the drawer sets the panel title at
   the heading scale so "Wedding Entertainment" still fits the 380px column. */
.site-drawer .nav-panel__heading {
	font-size: 1.875rem; /* 30 */
}

.site-drawer .nav-panel__text {
	font-size: var(--fs-body-sm);
}

.site-drawer .nav-panel__deck {
	gap: var(--space-24);
	width: 100%;
}

/*
 * The rail bleeds off the right edge, so the card after the last visible one is
 * cut by the screen rather than stopping short of it — which is what tells the
 * reader the row scrolls, and what the frame draws.
 *
 * The pair below is load-bearing, and neither half works alone. .nav-panel__cards
 * carries `width: 100%`, which resolves against the drawer's padded content box
 * and pins the rail there, so the negative margins moved it left without ever
 * widening it and it ended 48px short of the screen — hence `width: auto`. But
 * .nav-panel__deck is a column flex container set to `align-items: flex-start`,
 * and an auto-width item there is sized to max-content, not to the container:
 * the rail grew to the full width of its cards, stopped scrolling (there was no
 * overflow left to scroll), and pushed the whole panel sideways instead, so the
 * heading and intro slid off-screen with it. `align-self: stretch` opts this one
 * item back into filling the line, and because stretch sizes the margin box the
 * negative margins still widen the border box to the full screen. Padding is
 * left-only for the same reason the homepage rail's is — the gutter belongs on
 * the leading edge, nothing holds the trailing one back.
 */
.site-drawer .nav-panel__cards {
	align-self: stretch;
	width: auto;
	margin-inline: calc(var(--space-24) * -1);
	padding-inline: var(--space-24) 0;
	scroll-padding-inline: var(--space-24);
}

.site-drawer .nav-panel__card {
	flex: 0 0 200px;
}

.site-drawer .nav-panel__card-link {
	gap: var(--space-16);
}

/* The two feature tiles sit side by side in a 1440 panel; at 428 they stack. */
.site-drawer .nav-panel__tiles {
	flex-direction: column;
	gap: var(--space-16);
}

.site-drawer .nav-panel__tile-link {
	height: 220px;
}


/* ==========================================================================
   Drawer-open header state
   The design shows the bar turning white behind the open drawer, with the dark
   logo and a grey close plate.
   ========================================================================== */

.site-header.has-drawer-open {
	background-color: var(--c-white);
	color: var(--c-primary-900);
}

.site-header.has-drawer-open .site-logo__img--white {
	opacity: 0;
}

.site-header.has-drawer-open .site-logo__img--colour {
	opacity: 1;
}

.site-header.has-drawer-open .site-header__toggle {
	background-color: #ececec;
	color: var(--c-black);
}

/* ==========================================================================
   Rail navigation button — used by every card rail
   ========================================================================== */

.rail-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 3rem;
	height: 3rem;
	border-radius: var(--radius-circle);
	background-color: #e5e5e5;
	color: var(--c-black);
	transition: background-color var(--dur-fast) var(--ease),
		color var(--dur-fast) var(--ease),
		opacity var(--dur-fast) var(--ease);
}

.rail-btn--primary {
	background-color: var(--c-primary-500);
	color: var(--c-white);
}

.rail-btn:hover:not([disabled]) {
	background-color: var(--c-primary-800);
	color: var(--c-white);
}

.rail-btn[disabled] {
	opacity: 0.4;
	cursor: not-allowed;
}

/* ==========================================================================
   Video popup

   Shared by every [data-video-modal] trigger. <dialog> handles the backdrop,
   the focus trap and Esc; this is only the look of it.
   ========================================================================== */

.video-modal {
	width: min(1100px, 92vw);
	max-width: none;
	max-height: none;
	padding: 0;
	border: 0;
	background: transparent;
	overflow: visible;
}

.video-modal::backdrop {
	background-color: rgba(0, 0, 0, 0.82);
}

.video-modal__frame {
	position: relative;
	aspect-ratio: 16 / 9;
	overflow: hidden;
	border-radius: var(--radius-lg);
	background-color: #000;
}

.video-modal__frame iframe {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	border: 0;
}

.video-modal__close {
	position: absolute;
	top: -3rem;
	right: 0;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.5rem;
	height: 2.5rem;
	border-radius: var(--radius-circle);
	background-color: rgba(255, 255, 255, 0.14);
	color: var(--c-white);
	font-size: 1.75rem;
	line-height: 1;
	cursor: pointer;
	transition: background-color var(--dur-fast) var(--ease);
}

.video-modal__close:hover {
	background-color: rgba(255, 255, 255, 0.28);
}

/* The dialog scrolls the page behind it without this. */
.has-video-modal,
.has-video-modal body {
	overflow: hidden;
}

@media (max-width: 47.999em) {
	.video-modal__close {
		top: -2.75rem;
		width: 2.25rem;
		height: 2.25rem;
	}
}

/* ==========================================================================
   Experience / service card

   One component, two rails: "Add To The Experience" on Wedding Packages and
   the services in the homepage's Why Us band (Figma "Frame 1984078286") draw
   the same card. It sits here rather than in either page's sheet because
   components.css is the only one loaded on both.
   ========================================================================== */

/*
 * The stroke is solid #3e3e3e in the frame, not the 16% tint of it that
 * --c-border carries — sampled off Frame 1984078309, where every edge of every
 * card in the rail reads exactly rgb(62,62,62) against white. The tint rendered
 * as #e0e0e0 and read as no border at all at a distance.
 */
.card-experience {
	display: flex;
	flex-direction: column;
	height: 100%;
	padding: 11px;
	border: 1px solid var(--c-ground-dark);
	border-radius: var(--radius-lg);
	background-color: var(--c-white);
}

.card-experience__media {
	margin: 0 0 var(--space-16);
}

/*
 * Weighted to the top. The card is a 301/175 letterbox over photos that are
 * mostly wider-than-tall portraits of people, and a centred cover crop takes
 * the top of their heads off — the Wedding Celebrants card was cutting the
 * groom's forehead. 20% gives back most of that headroom without pushing the
 * subject low in the frame.
 */
.card-experience__media img {
	width: 100%;
	height: auto;
	aspect-ratio: 301 / 175;
	object-fit: cover;
	object-position: 50% 20%;
	border-radius: var(--radius-lg);
}

.card-experience__body {
	display: flex;
	flex-direction: column;
	flex: 1 1 auto;
	gap: var(--space-12);
	padding: 0 var(--space-8) var(--space-8);
}

.card-experience__title {
	margin: 0;
	color: var(--c-primary-500);
	font-size: var(--fs-display-lg);
	line-height: var(--lh-display);
	letter-spacing: 0;
}

.card-experience__title a {
	color: inherit;
	text-decoration: none;
}

.card-experience__text {
	margin: 0;
}

/* Sits on the card's bottom edge however tall the copy above it runs. */
.card-experience__link {
	display: inline-flex;
	align-items: center;
	gap: var(--space-8);
	margin-top: auto;
	padding-top: var(--space-16);
	color: var(--c-primary-500);
	font-family: var(--font-display);
	font-size: var(--fs-body-lg);
	line-height: 1.3;
	text-decoration: none;
}

.card-experience__link .icon {
	width: 20px;
	height: 20px;
	transition: transform var(--dur-fast) var(--ease);
}

.card-experience:hover .card-experience__link {
	color: var(--c-primary-800);
}

.card-experience:hover .card-experience__link .icon {
	transform: translateX(2px);
}

/* ==========================================================================
   Photo lightbox

   Shared by every gallery — the Gallery grid, the Resources rail and the
   artist/celebrant marquee. Not a frame in the design (the design has no
   opened photo), so it is built from the theme's own parts: the video popup's
   backdrop and close button, and the rails' circular step buttons.
   ========================================================================== */

/* The thumbnail is now inside a link; nothing about the grid should change. */
.lightbox-link {
	display: block;
	width: 100%;
	height: 100%;
	cursor: zoom-in;
}

.lightbox-link img {
	display: block;
	transition: opacity var(--dur) var(--ease);
}

.lightbox-link:hover img {
	opacity: 0.88;
}

/*
 * The frame hugs the photo rather than being a fixed box it sits inside: the
 * gallery holds anything from a 578px original to a 2048px one, and a letterbox
 * plate around the small ones reads as a broken viewer. fit-content takes the
 * width from the file; the three caps are the design container, the viewport,
 * and — via the ratio the script hands over — the width the photo may have
 * before it would be taller than the viewport. Without that third one a tall
 * portrait shrinks to fit the height while the frame stays as wide as the file,
 * stranding the arrows out in the dark.
 */
.lightbox {
	width: fit-content;
	/*
	 * The height cap is what sizes a portrait: its width can only ever be
	 * ratio x its height, so 80vh left a 1136x2196 photo 397px wide on a 960
	 * tall screen with 190px of unused viewport under it. Reserving a fixed
	 * 7rem for the caption and counter instead of a flat 20% gives that back —
	 * the same photo opens 439x848, and a landscape one reaches the 1200 cap.
	 */
	max-width: min(1200px, 88vw, calc((100vh - 7rem) * var(--lightbox-ratio, 1.5)));
	max-height: none;
	padding: 0;
	border: 0;
	background: transparent;
	overflow: visible;
}

.lightbox::backdrop {
	background-color: rgba(0, 0, 0, 0.82);
}

.lightbox__figure {
	display: flex;
	flex-direction: column;
	gap: var(--space-12);
	margin: 0;
}

/*
 * aspect-ratio comes from the file's own dimensions, set by the script before
 * the photo has loaded, so the frame opens at the right shape instead of
 * snapping to it a moment later.
 */
.lightbox__image {
	display: block;
	width: 100%;
	height: auto;
	/*
	 * The frame's shape comes from the file's declared dimensions, so anything
	 * that makes those disagree with the bytes actually loaded — a stale copy of
	 * a replaced file still in cache, a proxy serving an older version — used to
	 * stretch the photo to fit, because the initial value of object-fit is fill.
	 * contain letterboxes it at its true proportions instead: a mismatch then
	 * shows as a little space around the picture rather than distorted faces.
	 */
	object-fit: contain;
	border-radius: var(--radius-lg);
	background-color: rgba(255, 255, 255, 0.06);
}

.lightbox__caption {
	color: var(--c-white);
	font-size: var(--fs-body-sm);
	line-height: var(--lh-body);
	text-align: center;
}

.lightbox__count {
	margin: var(--space-8) 0 0;
	color: rgba(255, 255, 255, 0.72);
	font-size: var(--fs-body-sm);
	text-align: center;
}

.lightbox__close,
.lightbox__step {
	position: absolute;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.75rem;
	height: 2.75rem;
	border-radius: var(--radius-circle);
	background-color: rgba(255, 255, 255, 0.14);
	color: var(--c-white);
	cursor: pointer;
	transition: background-color var(--dur-fast) var(--ease);
}

.lightbox__close:hover,
.lightbox__step:hover {
	background-color: rgba(255, 255, 255, 0.3);
}

.lightbox__close .icon,
.lightbox__step .icon {
	width: 20px;
	height: 20px;
}

.lightbox__close {
	top: -3.5rem;
	right: 0;
}

/* Outside the photo on a desktop, where there is room beside it. */
.lightbox__step {
	top: 50%;
	transform: translateY(-50%);
}

.lightbox__step--prev {
	left: -3.75rem;
}

/* One arrow file, turned around — the same trick the rails use. */
.lightbox__step--prev .icon {
	transform: rotate(180deg);
}

.lightbox__step--next {
	right: -3.75rem;
}

/* The page scrolls behind an open dialog without this. */
.has-lightbox,
.has-lightbox body {
	overflow: hidden;
}

/*
 * Below ~1100 there is no margin left beside the photo, so the steps come
 * inside it and the close button drops onto the photo's top-right.
 */
@media (max-width: 68.75em) {
	.lightbox__step--prev {
		left: var(--space-8);
	}

	.lightbox__step--next {
		right: var(--space-8);
	}

	.lightbox__close {
		top: var(--space-8);
		right: var(--space-8);
	}

	/* All three now sit on the photo rather than on the backdrop, so they need
	   a ground of their own to stay visible over a bright one. */
	.lightbox__close,
	.lightbox__step {
		background-color: rgba(0, 0, 0, 0.55);
	}

	.lightbox__close:hover,
	.lightbox__step:hover {
		background-color: rgba(0, 0, 0, 0.75);
	}
}

@media (max-width: 47.999em) {
	.lightbox {
		max-width: min(94vw, calc(72vh * var(--lightbox-ratio, 1.5)));
	}

	.lightbox__close,
	.lightbox__step {
		width: 2.25rem;
		height: 2.25rem;
	}
}

/* Coarse pointers have no hover, so the fade would only ever read as a flash. */
@media (hover: none) {
	.lightbox-link:hover img {
		opacity: 1;
	}
}

/* ==========================================================================
   Editor content

   Everything the client writes in the editor — article bodies, the default page
   template, any future WYSIWYG field. Scoped to .entry-content rather than to a
   template's own wrapper so a page that renders the_content() is never left with
   unstyled headings and lists; the measure and the band around it stay with the
   template.
   ========================================================================== */

.entry-content {
	color: var(--c-body);
	font-size: var(--fs-body-md);
	line-height: var(--lh-body);
}

.entry-content > :first-child {
	margin-top: 0;
}

.entry-content > :last-child {
	margin-bottom: 0;
}

.entry-content p {
	margin: 0;
}

.entry-content p + p {
	margin-top: var(--space-24);
}

.entry-content h2 {
	margin: var(--space-64) 0 var(--space-24);
	color: var(--c-black);
	font-size: var(--fs-display-2xl);
	line-height: var(--lh-display);
	letter-spacing: var(--ls-display);
}

.entry-content h3 {
	margin: var(--space-48) 0 var(--space-16);
	color: var(--c-black);
	font-size: 2rem;
	line-height: var(--lh-tight);
	letter-spacing: var(--ls-display);
}

.entry-content h4 {
	margin: var(--space-32) 0 var(--space-12);
	color: var(--c-black);
	font-size: var(--fs-display-lg);
	line-height: var(--lh-tight);
}

.entry-content :is(h5, h6) {
	margin: var(--space-32) 0 var(--space-12);
	color: var(--c-black);
	line-height: var(--lh-tight);
}

.entry-content :is(ul, ol) {
	display: flex;
	flex-direction: column;
	gap: var(--space-8);
	margin: var(--space-16) 0 0;
	padding-left: 1.5em;
	list-style: disc outside;
}

.entry-content ol {
	list-style: decimal outside;
}

/* Lists only carry a top margin, so whatever follows one pays for its own
   space — without this a list runs straight into the next paragraph. */
.entry-content :is(ul, ol) + :is(p, ul, ol) {
	margin-top: var(--space-24);
}

/* The disc is drawn from the font, so the body face at 600 gives it the weight
   the frame has — same as the lists on the landing pages. */
.entry-content li::marker {
	font-family: var(--font-body);
	font-weight: 600;
}

.entry-content a {
	color: inherit;
	text-decoration: underline;
	text-underline-offset: 0.2em;
}

.entry-content a:hover {
	color: var(--c-link-hover);
}

.entry-content blockquote {
	margin: var(--space-32) 0;
	padding-left: var(--space-24);
	border-left: 2px solid var(--c-primary-500);
	font-size: var(--fs-body-lg);
}

.entry-content :is(img, figure) {
	margin: var(--space-32) 0;
}

.entry-content img {
	border-radius: var(--radius-lg);
}

.entry-content figcaption {
	margin-top: var(--space-8);
	color: var(--c-body);
	font-size: var(--fs-body-sm);
}

/* The editor's own alignment classes, which otherwise do nothing. */
.entry-content .aligncenter {
	margin-inline: auto;
}

.entry-content .alignwide,
.entry-content .alignfull {
	width: 100%;
}

.entry-content table {
	margin: var(--space-32) 0;
	font-size: var(--fs-body-sm);
}

.entry-content :is(th, td) {
	padding: var(--space-12) var(--space-16);
	border: 1px solid var(--c-border);
	text-align: left;
}

.entry-content th {
	font-weight: var(--fw-semibold);
}

.entry-content hr {
	margin: var(--space-48) 0;
	border: 0;
	border-top: 1px solid var(--c-border);
}

/* ==========================================================================
   Default page shell

   page.php and single.php — the fallbacks behind every route that has no
   designed template of its own (the footer's Videography pages, a CPT single
   with no template). They carry no design frame, so they stay deliberately
   plain: a centred title, the featured image, and the editor content on the
   article measure.
   ========================================================================== */

.page-shell__header {
	padding-block: var(--section-y) var(--space-40);
	background-color: var(--c-white);
	text-align: center;
}

.page-shell__header-inner {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-16);
	width: 100%;
	max-width: calc(760px + (var(--gutter) * 2));
	margin-inline: auto;
	padding-inline: var(--gutter);
}

.page-shell__title {
	margin: 0;
}

.page-shell__intro {
	margin: 0;
	color: var(--c-body);
	font-size: var(--fs-body-lg);
}

/* Carries the air below itself, so a page that stops at the image still clears
   the CTA band by a full section. */
.page-shell__media {
	width: 100%;
	max-width: calc(984px + (var(--gutter) * 2));
	margin: 0 auto var(--section-y);
	padding-inline: var(--gutter);
}

.page-shell__media img {
	width: 100%;
	aspect-ratio: 16 / 9;
	object-fit: cover;
	border-radius: var(--radius-lg);
}

/* The header's lower padding (or the image's margin) already sets the space
   above, so the body only pays for the space below it. */
.page-shell__body {
	padding-block: 0 var(--section-y);
	background-color: var(--c-white);
}

/* The same 984 measure the article body uses, so editor content sits on one
   line length across the site. */
.page-shell__inner {
	width: 100%;
	max-width: calc(984px + (var(--gutter) * 2));
	margin-inline: auto;
	padding-inline: var(--gutter);
}

/* Nothing but a heading — the CTA band follows immediately, so the header
   carries the section's whole rhythm on its own. */
.page-shell--bare .page-shell__header {
	padding-block: var(--section-y);
}

/* page.php runs under the shared .we-hero band rather than its own header, so
   the body pays for its top space too — nothing above it carries that any more.
   single.php and the archives keep their header, and their rule above. */
.page-shell--after-hero .page-shell__body {
	padding-block: var(--section-y);
}

.page-shell__meta {
	margin: 0;
}

/* Listings (search results, undesigned archives) need the full container, not
   the reading measure. */
.page-shell__inner--wide {
	max-width: calc(var(--container) + (var(--gutter) * 2));
}

/* The same steps the blog index uses, so a listing is a listing site-wide. */
.page-shell__grid {
	display: grid;
	gap: var(--space-32) var(--space-24);
	width: 100%;
	margin: 0;
	padding: 0;
	list-style: none;
}

.page-shell__grid > li {
	display: flex;
}

@media (min-width: 48em) {
	.page-shell__grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

@media (min-width: 64em) {
	.page-shell__grid {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}
}

.no-results {
	text-align: center;
}

.page-links {
	display: flex;
	gap: var(--space-12);
	margin-top: var(--space-32);
}

/* ==========================================================================
   Pagination — WordPress's own the_posts_pagination() markup

   The designed listings page themselves with the rail buttons; this is the
   numbered pager behind the fallback templates.
   ========================================================================== */

.pagination {
	margin-top: var(--space-48);
}

.pagination .nav-links {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--space-8);
}

.pagination .page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 2.75rem;
	min-height: 2.75rem;
	padding-inline: var(--space-12);
	border: 1px solid var(--c-border);
	border-radius: var(--radius-pill);
	color: var(--c-text);
	font-size: var(--fs-body-sm);
	text-decoration: none;
	transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}

.pagination a.page-numbers:hover {
	background-color: var(--c-primary-50);
	color: var(--c-primary-800);
}

.pagination .page-numbers.current {
	background-color: var(--c-primary-500);
	border-color: var(--c-primary-500);
	color: var(--c-white);
}

.pagination .page-numbers.dots {
	border-color: transparent;
}

/* ==========================================================================
   Auto-running rails (marquee)
   ==========================================================================

   Shared by the artist gallery and a package's "Every Moment Matters". Lives
   here rather than in either page's sheet because those load conditionally —
   artist.css is not on a package page, so the keyframes would be missing
   exactly where the second rail needs them.

   app.js adds .is-marquee once it has cloned the photos, so the plain scroller
   each rail ships as is what survives with no JavaScript. The track carries two
   identical runs and is translated by exactly one run's width, which is why the
   loop has no seam: the moment the animation restarts, run two sits precisely
   where run one was. --marquee-shift and --marquee-duration are measured and
   set per rail; --marquee-gap is whatever gap that rail's design calls for,
   since the rail's own gap has to go to zero once the track owns the spacing.

   Scrolling by hand stands down while it runs — overflow stays hidden so a
   stray gesture cannot desynchronise the loop from the transform. */

/* Written against the attribute as well as the class so it outweighs the rail's
   own single-class rule: components.css loads first, so a bare .is-marquee ties
   on specificity with .pkg-moments__rail and loses on order — leaving the rail
   scrollable and double-gapped underneath a running marquee. */
[data-marquee].is-marquee {
	overflow-x: hidden;
	gap: 0;
}

.marquee__track {
	display: flex;
	/*
	 * The track becomes the flex parent the items used to have, so it has to
	 * carry the rail's cross-axis alignment or every rail silently goes
	 * top-aligned the moment the marquee wraps it — which is wrong wherever
	 * short and tall photos alternate and the frames centre them against each
	 * other. `inherit` rather than a value: each rail already declares its own
	 * per-breakpoint alignment, and this follows it without restating any of it.
	 */
	align-items: inherit;
	gap: var(--marquee-gap, var(--space-24));
	margin: 0;
	padding: 0;
	list-style: none;
	will-change: transform;
	animation: janda-marquee var(--marquee-duration, 60s) linear infinite;
}

/* Hover, and keyboard focus inside it, hold the rail still so a photo can
   actually be looked at. */
.is-marquee:hover .marquee__track,
.is-marquee:focus-within .marquee__track {
	animation-play-state: paused;
}

/* --- rails you can grab -----------------------------------------------------
 * Opted into per rail with data-marquee-drag, and shared for the same reason
 * the rest of this block is: the artist gallery and the enquiry intro both want
 * it, and their sheets load on different pages.
 *
 * A running animation owns the transform outright, so a rail that answers the
 * pointer cannot also run the keyframes — app.js writes the offset itself frame
 * by frame instead, and the animation is switched off here on the same
 * attribute the script opts in on. The loop is unchanged: still two runs, still
 * wrapped at one run's width, so pulling either way never reaches an end.
 * -------------------------------------------------------------------------- */

[data-marquee-drag] {
	cursor: grab;
}

/* touch-action keeps the vertical gesture with the page: the box is
   overflow-hidden while the marquee runs, so a sideways pull has nothing to
   scroll and would otherwise be swallowed. */
[data-marquee-drag].is-marquee {
	touch-action: pan-y;
}

[data-marquee-drag].is-marquee .marquee__track {
	animation: none;
}

[data-marquee-drag].is-dragging {
	cursor: grabbing;
	/* Kills text/image selection and the photo's lightbox firing mid-drag. */
	user-select: none;
}

[data-marquee-drag].is-dragging a {
	pointer-events: none;
}

@keyframes janda-marquee {
	from {
		transform: translate3d(0, 0, 0);
	}

	to {
		transform: translate3d(calc(var(--marquee-shift, 0px) * -1), 0, 0);
	}
}

/* Reduced motion gets no animation at all — and the rail goes back to being a
   scroller, so the photos past the fold are still reachable. */
@media (prefers-reduced-motion: reduce) {
	[data-marquee].is-marquee {
		overflow-x: auto;
	}

	.marquee__track {
		animation: none;
	}
}
