/*
 * Motion: light and glass. Every effect is something light or a window does,
 * none of them transforms text, and all of them settle in their end state
 * under reduced motion. Hidden-before-reveal states only apply once the
 * motion script has marked the page (html.pm-motion), so nothing is ever
 * hidden without the script that shows it.
 */

@keyframes pm-fanlight-rise {

	from {
		-webkit-mask-position: 0 0, 100% 0;
		mask-position: 0 0, 100% 0;
	}

	to {
		-webkit-mask-position: 0 0, 0 0;
		mask-position: 0 0, 0 0;
	}
}

@keyframes pm-sash {

	from {
		clip-path: inset(100% 0 0 0);
	}

	to {
		clip-path: inset(0 0 0 0);
	}
}

@keyframes pm-settle {

	from {
		transform: scale(1.14);
	}

	to {
		transform: scale(1);
	}
}

@keyframes pm-glint {

	from {
		background-position: -140% 0;
	}

	to {
		background-position: 240% 0;
	}
}

/* Entrance animations hold their first frame through any delay but not
   their last (fill-mode backwards): each ends where the element's own style
   already is, and an animation that fills forwards keeps its element a GPU
   layer for as long as the page is open, pushing much of the page after it
   into layers too. */

/* The scroll-linked light across buttons and photographs: a band on a
   pseudo-element several times the width of its host, moved with transform
   so the compositor runs it without repainting. */
@keyframes pm-edge {

	from {
		transform: translateX(-91%);
	}

	to {
		transform: translateX(21%);
	}
}

@keyframes pm-door-left {

	0% {
		transform: rotateY(0);
	}

	100% {
		transform: rotateY(-108deg);
	}
}

@keyframes pm-door-right {

	0% {
		transform: rotateY(0);
	}

	100% {
		transform: rotateY(108deg);
	}
}

@keyframes pm-door-spill {

	0% {
		opacity: 0;
	}

	25% {
		opacity: 1;
	}

	100% {
		opacity: 0;
	}
}

@keyframes pm-fade-in {

	from {
		opacity: 0;
	}

	to {
		opacity: 1;
	}
}

@keyframes pm-modal-in {

	from {
		opacity: 0;
		transform: perspective(1400px) rotateX(8deg) translateY(28px) scale(0.97);
	}

	to {
		opacity: 1;
		transform: none;
	}
}

@keyframes pm-step-in-right {

	from {
		opacity: 0;
		transform: translateX(28px);
	}

	to {
		opacity: 1;
		transform: none;
	}
}

@keyframes pm-step-in-left {

	from {
		opacity: 0;
		transform: translateX(-28px);
	}

	to {
		opacity: 1;
		transform: none;
	}
}

@keyframes pm-shake {

	0%,
	100% {
		transform: none;
	}

	20% {
		transform: translateX(-7px);
	}

	40% {
		transform: translateX(6px);
	}

	60% {
		transform: translateX(-4px);
	}

	80% {
		transform: translateX(2px);
	}
}

@keyframes pm-pop {

	0% {
		transform: scale(0);
	}

	70% {
		transform: scale(1.25);
	}

	100% {
		transform: scale(1);
	}
}

@keyframes pm-rise {

	from {
		opacity: 0;
		transform: translateY(8px);
	}

	to {
		opacity: 1;
		transform: none;
	}
}

@keyframes pm-sweep {

	0% {
		transform: translateX(37%);
	}

	55%,
	100% {
		transform: translateX(-98.5%);
	}
}

@keyframes pm-show {

	to {
		opacity: 1;
		transform: none;
		clip-path: none;
	}
}

/* ---------- The fanlight ornament ---------- */

.pm-fanlight {
	display: block;
	aspect-ratio: 313.6 / 154.8;
	background: currentcolor;
	-webkit-mask-image: var(--fanlight-mask);
	mask-image: var(--fanlight-mask);
	-webkit-mask-size: 100% 100%;
	mask-size: 100% 100%;
	-webkit-mask-repeat: no-repeat;
	mask-repeat: no-repeat;
	pointer-events: none;
}

/* Sunrise: a soft-edged mask slides across, lighting the panes one by one. */
.pm-fanlight--rise {
	-webkit-mask-image: var(--fanlight-mask), linear-gradient(90deg, #000 0 50%, transparent 58%);
	mask-image: var(--fanlight-mask), linear-gradient(90deg, #000 0 50%, transparent 58%);
	-webkit-mask-composite: source-in;
	mask-composite: intersect;
	-webkit-mask-size: 100% 100%, 220% 100%;
	mask-size: 100% 100%, 220% 100%;
	animation: pm-fanlight-rise 2200ms var(--ease-out) var(--rise-delay, 300ms) both;
}

/* Glass catching the light: a slow sheen that crosses, then rests. It is
   a wide band moved with transform, so it never repaints the panel. It
   crosses twice and stops: a looping animation keeps the GPU drawing frames
   for as long as the page is open. */
.pm-sheen {
	position: absolute;
	inset: 0;
	overflow: hidden;
	pointer-events: none;
}

.pm-sheen::before {
	content: "";
	position: absolute;
	top: 0;
	bottom: 0;
	left: 0;
	width: 260%;
	background: linear-gradient(105deg, transparent 40%, rgba(189, 238, 255, 0.07) 48%, rgba(189, 238, 255, 0.13) 50%, rgba(189, 238, 255, 0.07) 52%, transparent 60%);
	transform: translateX(37%);
	animation: pm-sweep 11s var(--ease-inout) 1.2s 2;
}

/* Touch screens go without it: while it runs, much of the page beneath is
   drawn as layers of its own. */
@media (hover: none), (pointer: coarse) {

	.pm-sheen {
		display: none;
	}
}

/* ---------- Scroll-in rise (text blocks) ---------- */

@media (prefers-reduced-motion: no-preference) {

	html.pm-motion .pm-rv {
		transition:
			opacity var(--dur-3) var(--ease-out) var(--rv-delay, 0ms),
			transform var(--dur-3) var(--ease-out) var(--rv-delay, 0ms);
	}

	html.pm-motion .pm-rv.is-waiting {
		opacity: 0;
		transform: translateY(8px);
	}
}

/* ---------- Sash reveal (photographs) ---------- */

.pm-sash {
	position: relative;
	overflow: hidden;
}

.pm-sash > img,
.pm-sash > a > img {
	display: block;
}

@media (prefers-reduced-motion: no-preference) {

	html.pm-motion .pm-sash.is-waiting > img,
	html.pm-motion .pm-sash.is-waiting > a > img {
		clip-path: inset(100% 0 0 0);
	}

	html.pm-motion .pm-sash.is-open > img,
	html.pm-motion .pm-sash.is-open > a > img {
		animation:
			pm-sash var(--dur-sash) var(--ease-sash) var(--rv-delay, 0ms) backwards,
			pm-settle 1600ms var(--ease-out) var(--rv-delay, 0ms) backwards;
	}
}

/* Light passing across the glass as the photo moves through the screen
   (not on touch screens: each is a layer three times the photo's width). */
@supports (animation-timeline: view()) {

	@media (hover: hover) and (pointer: fine) {

		.pm-sash::after {
			content: "";
			position: absolute;
			top: 0;
			bottom: 0;
			left: 0;
			width: 320%;
			pointer-events: none;
			background: linear-gradient(115deg, transparent 40%, rgba(189, 238, 255, 0.1) 46%, rgba(255, 255, 255, 0.22) 50%, rgba(189, 238, 255, 0.1) 54%, transparent 60%);
			animation: pm-edge linear both;
			animation-timeline: view();
		}
	}
}

/* ---------- French doors (photographs) ---------- */

.pm-doors {
	position: relative;
	overflow: hidden;
	perspective: 1600px;
	background: var(--black);
}

.pm-doors > img,
.pm-doors > a > img {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.pm-door,
.pm-door__spill {
	display: none;
}

@media (prefers-reduced-motion: no-preference) {

	html.pm-js .pm-door,
	html.pm-js .pm-door__spill {
		display: grid;
	}

	.pm-door {
		position: absolute;
		top: 0;
		bottom: 0;
		width: 50%;
		z-index: 2;
		box-sizing: border-box;
		padding: clamp(14px, 2.4vw, 34px);
		background: var(--ink-2);
		backface-visibility: hidden;
		pointer-events: none;
	}

	/* A layer of their own only while swinging (see motion.js); a layer per
	   leaf on every photograph costs phones dearly. */
	.pm-doors.is-moving .pm-door {
		will-change: transform;
	}

	/* The light between the leaves fades on every frame of a swing: on a
	   layer of its own it is only recomposited, not repainted with the
	   photograph beneath it. */
	.pm-doors.is-moving .pm-door__spill {
		will-change: opacity;
	}

	.pm-door--left {
		left: 0;
		transform-origin: left center;
		border-right: 1px solid rgba(189, 238, 255, 0.35);
	}

	.pm-door--right {
		right: 0;
		transform-origin: right center;
		border-left: 1px solid rgba(189, 238, 255, 0.35);
	}

	/* A frame of six panes, like a French door. */
	.pm-door__glass {
		display: grid;
		grid-template-columns: 1fr 1fr;
		grid-template-rows: repeat(3, 1fr);
		gap: clamp(6px, 0.8vw, 12px);
		padding: clamp(6px, 0.8vw, 12px);
		border: 1px solid rgba(189, 238, 255, 0.3);
	}

	.pm-door__glass > span {
		display: block;
		background: linear-gradient(135deg, rgba(189, 238, 255, 0.1), rgba(189, 238, 255, 0.02) 60%);
		border: 1px solid rgba(189, 238, 255, 0.22);
	}

	/* Handle on the meeting stile. */
	.pm-door__handle {
		position: absolute;
		top: 50%;
		width: 3px;
		height: clamp(36px, 5vw, 64px);
		transform: translateY(-50%);
		background: var(--sky-200);
	}

	.pm-door--left .pm-door__handle {
		right: clamp(6px, 0.8vw, 12px);
	}

	.pm-door--right .pm-door__handle {
		left: clamp(6px, 0.8vw, 12px);
	}

	/* Light spilling through the gap as the doors part. */
	.pm-door__spill {
		position: absolute;
		inset: 0;
		z-index: 1;
		pointer-events: none;
		background: linear-gradient(90deg, transparent 30%, rgba(189, 238, 255, 0.55) 50%, transparent 70%);
		opacity: 0;
	}

	/* Hero: the doors open once, on load, while the photograph settles. */
	.pm-doors--load .pm-door--left {
		animation: pm-door-left 1500ms var(--ease-door) 350ms both;
	}

	.pm-doors--load .pm-door--right {
		animation: pm-door-right 1500ms var(--ease-door) 350ms both;
	}

	.pm-doors--load .pm-door__spill {
		animation: pm-door-spill 1500ms var(--ease-out) 350ms both;
	}

	.pm-doors--load > img {
		animation: pm-settle 2200ms var(--ease-out) 350ms backwards;
	}

	/* In content: the scroll position drives the swing (--door-e, 0 to 1). */
	.pm-doors--scroll .pm-door--left {
		transform: rotateY(calc(var(--door-e, 0) * -108deg));
	}

	.pm-doors--scroll .pm-door--right {
		transform: rotateY(calc(var(--door-e, 0) * 108deg));
	}

	.pm-doors--scroll .pm-door__spill {
		opacity: var(--door-spill, 0);
	}

	/* If the motion script never starts, the doors still open on their own. */
	.pm-doors--scroll:not(.is-driven) .pm-door--left {
		animation: pm-door-left 1200ms var(--ease-door) 2500ms both;
	}

	.pm-doors--scroll:not(.is-driven) .pm-door--right {
		animation: pm-door-right 1200ms var(--ease-door) 2500ms both;
	}

	/* Fully open doors are gone: nothing left over the photograph. */
	.pm-doors.is-open .pm-door,
	.pm-doors.is-open .pm-door__spill {
		display: none;
	}

	/* Each leaf is a large layer in the doors' 3D space, so doors away from
	   the viewport have none; they return, closed, before the photograph
	   scrolls into view (motion.js sets is-near). */
	.pm-doors--scroll.is-driven:not(.is-near) .pm-door,
	.pm-doors--scroll.is-driven:not(.is-near) .pm-door__spill {
		display: none;
	}

	/*
	 * Where the browser can tie an animation to the scroll, it swings the
	 * doors itself, off the main thread: no script runs per frame (motion.js
	 * swings them frame by frame elsewhere). Same rule as the script: they
	 * start to open with a third of the photograph in view above any bar
	 * along the foot of the screen (forms.css), and are fully open once all
	 * of it is, or three quarters of the screen for a tall one. They wait,
	 * shut, until the photograph has loaded (is-ready).
	 */
	@supports (animation-timeline: view()) {

		.pm-doors--scroll {
			--door-room: calc(100dvh - var(--sticky-bar-h, 0px));
			--door-size: min(100%, calc(var(--door-room) * 0.75));
			view-timeline: --pm-door block;
			view-timeline-inset: 0 var(--sticky-bar-h, 0px);

			/* motion.js eases the swing in and out (quadratic). */
			--door-swing: cubic-bezier(0.45, 0, 0.55, 1);
		}

		.pm-doors--scroll.is-ready .pm-door--left {
			animation: pm-door-left var(--door-swing) both;
			animation-timeline: --pm-door;
			animation-range: entry calc(var(--door-size) * 0.35) entry var(--door-size);
		}

		.pm-doors--scroll.is-ready .pm-door--right {
			animation: pm-door-right var(--door-swing) both;
			animation-timeline: --pm-door;
			animation-range: entry calc(var(--door-size) * 0.35) entry var(--door-size);
		}

		.pm-doors--scroll.is-ready .pm-door__spill {
			animation: pm-door-spill-scroll linear both;
			animation-timeline: --pm-door;
			animation-range: entry calc(var(--door-size) * 0.35) entry var(--door-size);
		}
	}
}

/* The light through the parting doors, brightest halfway (as motion.js
   draws it for the scroll: 0.9 × sin of the eased swing). */
@keyframes pm-door-spill-scroll {

	0%,
	100% {
		opacity: 0;
	}

	10%,
	90% {
		opacity: 0.057;
	}

	20%,
	80% {
		opacity: 0.224;
	}

	30%,
	70% {
		opacity: 0.482;
	}

	40%,
	60% {
		opacity: 0.76;
	}

	50% {
		opacity: 0.9;
	}
}

/* ---------- Counting figures ---------- */

.pm-count {
	font-variant-numeric: tabular-nums;
}

/* Figures are hidden from first paint until they start counting, so they
   never flash their final value first; if the script never runs they show
   after two seconds anyway. */
@media (prefers-reduced-motion: no-preference) {

	html.pm-js .pm-count:not(.is-counted) {
		opacity: 0;
		animation: pm-show 1ms 2000ms forwards;
	}
}

/* ---------- Spotlight ---------- */

.pm-spotlight {
	position: fixed;
	inset: 0;
	z-index: 900;
	pointer-events: none;
	overflow: hidden;
}

.pm-spotlight__shade {
	position: absolute;
	inset: 0;
	background: rgba(0, 0, 0, 0.62);
	opacity: 0;
	transition: opacity 320ms var(--ease-out);
}

.pm-spotlight__rim {
	position: absolute;
	left: 0;
	top: 0;
	width: 0;
	height: 0;
	opacity: 0;
	box-shadow: 0 0 60px 12px rgba(84, 189, 237, 0.22), inset 0 0 0 1px rgba(189, 238, 255, 0.4);
	transition:
		transform 380ms var(--ease-out),
		width 380ms var(--ease-out),
		height 380ms var(--ease-out),
		opacity 320ms var(--ease-out);
}

@media (prefers-reduced-motion: reduce) {

	*,
	*::before,
	*::after {
		animation-duration: 1ms !important;
		animation-iteration-count: 1 !important;
		animation-delay: 0ms !important;
		transition-duration: 1ms !important;
		scroll-behavior: auto !important;
	}

	.pm-sheen,
	.pm-sash::after {
		display: none;
	}

	.pm-spotlight {
		display: none;
	}
}
