/* Georgia's numbers are "old-style" (short, some dropping below the line),
   so the times take only their digits and colon from Times New Roman, whose
   numbers stand level at full height; the letters stay Georgia. */
@font-face {
	font-family: "Clock Digits";
	src: local("Times New Roman"), local("TimesNewRomanPSMT");
	unicode-range: U+0030-003A;
}

/* Times New Roman's regular digits are a little lighter than Georgia's
   letters; a hairline outline evens them up. */
.time-digits {
	-webkit-text-stroke: 0.02em currentColor;
}

* {
	padding: 0;
	margin: 0;
	overflow: hidden;
}

html,
body {
	touch-action: none;
}

:root {
	--storm-dark: #091720;
	--storm: #1e3440;
	--storm-light: #536975;
	--ground-dark: #101511;
	--ground: #292d24;
	--ground-light: #46513a;
	--parallax-overscan: 1.2;
	/* Type sizes shared by every panel, the time, the caption and the loading
	   screen. 12px (the small caps) is the smallest text anywhere; the rest
	   scale up from it. Phones differ only in the title and the time. */
	--text-title: 2.2rem;
	--text-clock: 1.6rem;
	--text-label: 1.125rem;
	--text-body: 0.9375rem;
	--text-eyebrow: 0.75rem;
	/* How far the buttons, time and caption sit from the screen's edges;
	   on phones, the same as the caption's gap to its arrows. */
	--edge-gap: 20px;
	/* Between neighbouring round buttons at the top. */
	--button-gap: 6px;
}

@media (max-width: 600px) {
	:root {
		--edge-gap: 12px;
		--text-title: 1.9rem;
		--text-clock: 1.6rem;
	}
}

/* Dark behind everything, so nothing can ever flash white. */
html,
body {
	background: #091720;
}

body {
	height: 100dvh;
	display: flex;
	align-items: center;
	justify-content: center;
	flex-direction: column;
	position: relative;
}

#sky {
	width: 120%;
	height: 120%;
	position: absolute;
	z-index: 1;
	top: 50%;
	left: 50%;
	transform: translateX(-50%) translateY(-50%);
	overflow: hidden;
	contain: layout paint;

	/* The sky's colours: a small canvas, stretched (a smooth gradient looks
	   the same), that drawSky in js/time-and-weather.js repaints as the light changes. A CSS
	   gradient this size had to be repainted in full each second, and that
	   spike in graphics memory made Chrome drop every layer for a frame. */
	.sky-gradient {
		position: absolute;
		inset: 0;
		width: 100%;
		height: 100%;
	}

	/* The stars (createStars in js/sky.js): the still ones painted into one
	   canvas, the twinkling ones tiny elements of their own. Each fades by
	   itself as the sky lightens; fading the whole starfield as a group made
	   the browser redraw it all every frame. The clock updates the light
	   every --clock-tick, so each glides between (a little longer than
	   that, so a late update never leaves a pause). */
	.stars {
		position: absolute;
		inset: 0;
	}

	.stars-canvas,
	.star-twinkle {
		position: absolute;
		opacity: var(--starlight, 1);
		transition: opacity calc(1.2 * var(--clock-tick, 1s)) linear;
	}

	.stars-canvas {
		inset: 0;
		width: 100%;
		height: 100%;
	}

	.star-twinkle {
		width: 0;
		height: 0;
		overflow: visible;

		/* A tiny dot. The painted stars' glow is a blur of the star, so on
		   these (all under 0.7px) it would be too faint to see. */
		& > span {
			position: absolute;
			aspect-ratio: 1;
			translate: -50% -50%;
			border-radius: 50%;
			background: white;
			animation: twinkle 1s alternate-reverse infinite;
		}
	}

	/* The sun rises from behind the hills on the left (#sky is 120% of the
	   screen, so the screen-unit position is offset by its 10% overhang). */
	/* Moved by translate on its own GPU layer, so it never repaints the sky. */
	.sun {
		position: absolute;
		top: 0;
		left: 0;
		width: 14vmin;
		height: 14vmin;
		border-radius: 50%;
		background: radial-gradient(
			circle,
			#fffaf0 0%,
			#fff1d0 28%,
			rgba(255, 214, 150, 0.55) 42%,
			rgba(255, 190, 120, 0.18) 62%,
			transparent 72%
		);
		opacity: var(--sun-opacity, 0);
		translate: calc(var(--sun-left, 20vw) + 10vw - 50%)
			calc(var(--sun-top, 80vh) + 10vh - 50%);
		/* Placed every frame by the running clock (js/timeline.js). */
		will-change: translate, opacity;
	}

	/* The jump is already eased; the stars' long fade only lagged behind. */
	:root[data-jumping] & :is(.stars-canvas, .star-twinkle) {
		transition-duration: 150ms;
	}

	.shooting-star {
		position: absolute;
		top: var(--shooting-start-y);
		left: var(--shooting-start-x);
		width: var(--shooting-tail-width);
		height: var(--shooting-thickness);
		border-radius: 999px;
		/* The head sticks out past the tail; don't let * { overflow: hidden } clip it. */
		overflow: visible;
		background: linear-gradient(
			90deg,
			transparent,
			rgba(218, 235, 247, 0.22) 30%,
			rgba(244, 250, 255, 0.92) 88%,
			white
		);
		filter: drop-shadow(0 0 3px rgba(218, 239, 255, 0.76));
		pointer-events: none;
		opacity: 0; /* only the animation shows it */
		transform-origin: right center;
		animation: shooting-star var(--shooting-duration) ease-out forwards;
		will-change: transform, opacity;

		&::after {
			position: absolute;
			top: 50%;
			right: 0;
			width: var(--shooting-head-size);
			height: var(--shooting-head-size);
			content: "";
			border-radius: 50%;
			background: white;
			box-shadow: 0 0 5px rgba(218, 239, 255, 0.92);
			transform: translate(35%, -50%);
		}

		&.shooting-star--stretch {
			animation-name: shooting-star-stretch;
		}
	}
}

@keyframes twinkle {
	80% {
		opacity: 1;
	}
	100% {
		opacity: 0;
	}
}

@keyframes shooting-star {
	0% {
		opacity: 0;
		transform: rotate(var(--shooting-angle)) translate3d(0, 0, 0)
			scaleX(0.4);
	}
	12% {
		opacity: var(--shooting-opacity);
	}
	72% {
		opacity: var(--shooting-opacity);
	}
	100% {
		opacity: 0;
		transform: rotate(var(--shooting-angle))
			translate3d(var(--shooting-distance), 0, 0) scaleX(1);
	}
}

@keyframes shooting-star-stretch {
	0% {
		opacity: 0;
		transform: rotate(var(--shooting-angle)) translate3d(0, 0, 0)
			scaleX(0.08);
	}
	10% {
		opacity: var(--shooting-opacity);
	}
	68% {
		opacity: var(--shooting-opacity);
		transform: rotate(var(--shooting-angle))
			translate3d(var(--shooting-mid-distance), 0, 0) scaleX(1.65);
	}
	100% {
		opacity: 0;
		transform: rotate(var(--shooting-angle))
			translate3d(var(--shooting-distance), 0, 0) scaleX(2.2);
	}
}

#storm {
	position: absolute;
	inset: 0;
	pointer-events: none;
}

.storm-cloud {
	--cloud-frame: 140dvh;
	--cloud-offset-x: -20vw;
	--cloud-top: 0px;
	position: absolute;
	top: -20dvh;
	left: -15vw;
	width: 130vw;
	height: var(--cloud-frame);
	/* Each layer's own opacity, scaled by the weather (--cloud-amount). */
	opacity: calc(var(--cloud-opacity) * var(--cloud-amount, 1));
	overflow: hidden;
	pointer-events: none;
	contain: layout paint;
}

/* Each texture is only as wide as the frame plus its drift, and only as tall as
   the band its clouds show in (--cloud-top to --cloud-bottom), shifted so the
   clouds land exactly where a full-size layer would put them, with a fraction
   of the GPU memory. Each layer's top and bottom fade is baked into its
   image: a CSS mask here was a second full-size GPU layer per cloud in
   Chrome.

   Each drifts exactly one width of its repeating image (its height times
   the image's width over height), so when the drift starts over the clouds
   are where they began and the loop has no seam; a shorter drift made them
   jump back each time round. --cloud-speed is that whole width's crossing,
   at the speed the layer always had. */
.storm-cloud__texture {
	display: block;
	position: absolute;
	top: var(--cloud-top);
	left: 0;
	width: calc(100% - var(--cloud-drift));
	height: calc(var(--cloud-bottom) - var(--cloud-top));
	background-image: var(--cloud-image);
	background-position: var(--cloud-offset-x)
		calc(var(--cloud-y) - var(--cloud-top));
	background-repeat: repeat-x;
	background-size: auto var(--cloud-image-height);
	/* Each layer's own grade, lightened or darkened by the weather. Once
	   drawn into its canvas the colour is drawn in too (see below). */
	filter: var(--color-grade) brightness(var(--cloud-shade, 1))
		brightness(var(--cloud-light, 1)) sepia(var(--cloud-warmth, 0));
	animation: cloud-drift var(--cloud-speed) linear infinite;
	will-change: transform;
}

.storm-cloud--far {
	--cloud-image: url("./assets/clouds/baked/far.avif");
	--cloud-bottom: calc(var(--cloud-frame) * 0.74);
	--cloud-image-height: 110dvh;
	--cloud-y: 0dvh;
	--cloud-drift: calc(var(--cloud-image-height) * -1536 / 768);
	--cloud-speed: 4760s;
	--color-grade: brightness(62%) contrast(116%) sepia(16%) saturate(80%)
		hue-rotate(154deg);
	z-index: 2;
	--cloud-opacity: 0.28;
}

.storm-cloud--deep {
	--cloud-image: url("./assets/clouds/baked/deep.avif");
	--cloud-bottom: calc(var(--cloud-frame) * 0.66);
	--cloud-image-height: 55dvh;
	--cloud-y: 5dvh;
	--cloud-drift: calc(var(--cloud-image-height) * -1280 / 455);
	--cloud-speed: 2320s;
	--color-grade: brightness(54%) contrast(116%) saturate(48%)
		hue-rotate(158deg);
	z-index: 4;
	--cloud-opacity: 0.56;
}

.storm-cloud--middle {
	--cloud-image: url("./assets/clouds/baked/middle.avif");
	--cloud-bottom: calc(var(--cloud-frame) * 0.7);
	--cloud-image-height: 88dvh;
	--cloud-y: -2dvh;
	--cloud-drift: calc(var(--cloud-image-height) * -1);
	--cloud-speed: 1311s;
	--color-grade: brightness(64%) contrast(116%) sepia(12%) saturate(70%)
		hue-rotate(150deg);
	z-index: 8;
	--cloud-opacity: 0.2;
}

.storm-cloud--midband {
	--cloud-image: url("./assets/clouds/baked/midband.avif");
	--cloud-top: calc(var(--cloud-frame) * 0.31);
	--cloud-bottom: calc(var(--cloud-frame) * 0.49);
	--cloud-image-height: 64dvh;
	--cloud-y: 16dvh;
	--cloud-drift: calc(var(--cloud-image-height) * -1);
	--cloud-speed: 898s;
	--color-grade: brightness(56%) contrast(118%) sepia(10%) saturate(64%)
		hue-rotate(154deg);
	z-index: 12;
	--cloud-opacity: 0.3;
}

.storm-cloud--lower {
	--cloud-image: url("./assets/clouds/baked/lower.avif");
	--cloud-top: calc(var(--cloud-frame) * 0.36);
	--cloud-bottom: calc(var(--cloud-frame) * 0.7);
	--cloud-image-height: 25dvh;
	--cloud-y: 58dvh;
	--cloud-drift: calc(var(--cloud-image-height) * -1280 / 455);
	--cloud-speed: 993s;
	--color-grade: brightness(72%) contrast(108%) saturate(52%)
		hue-rotate(154deg);
	z-index: 24;
	--cloud-opacity: 0.13;
}

.storm-cloud--near {
	--cloud-image: url("./assets/clouds/baked/near.avif");
	--cloud-bottom: calc(var(--cloud-frame) * 0.62);
	--cloud-image-height: 46dvh;
	--cloud-y: 9dvh;
	--cloud-drift: calc(var(--cloud-image-height) * -1280 / 455);
	--cloud-speed: 455s;
	--color-grade: brightness(50%) contrast(120%) saturate(45%)
		hue-rotate(160deg);
	z-index: 35;
	--cloud-opacity: 0.64;
}

/* Drawn into a canvas by wind.js (swayInWind in js/land.js), colour and all
   (once it's up, the background and the live filter go: a filter on these
   drifting, near full-screen layers was redone every frame, the scene's
   biggest cost). */
.storm-cloud__texture.is-swaying {
	background-image: none;
	filter: none;
}

.storm-cloud__texture > canvas {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
}

@keyframes cloud-drift {
	from {
		transform: translate3d(var(--cloud-drift), 0, 0);
	}

	to {
		transform: translate3d(0, 0, 0);
	}
}

.rain-layer,
.lightning-layer,
.storm-tone {
	position: fixed;
	inset: 0;
	pointer-events: none;
}

/* Rain canvases fill the screen (see startRain in js/storm.js). */
.rain-layer {
	width: 100%;
	height: 100%;
}

/* Distant rain: in front of the city, behind the ridge. */
.rain-layer--back {
	z-index: 40;
}

.rain-layer--front {
	z-index: 65;
}

.lightning-layer--back {
	z-index: 1;
}

.lightning-layer--middle {
	z-index: 5;
}

.lightning-layer--front {
	z-index: 20;
}

.lightning-bolt {
	position: absolute;
	top: var(--bolt-top);
	left: var(--bolt-left);
	width: var(--bolt-width-screen);
	height: var(--bolt-height);
	overflow: visible;
	transform: scaleX(var(--bolt-slant));
	transform-origin: top center;
	/* One glow per bolt (each extra glow is another full blur pass). */
	filter: drop-shadow(0 0 9px rgba(150, 190, 235, 0.95));
	animation: lightning-flicker var(--bolt-duration) steps(1, end) forwards;
}

.lightning-bolt--far {
	filter: drop-shadow(0 0 5px rgba(150, 190, 235, 0.8));
}

.lightning-bolt--middle {
	filter: drop-shadow(0 0 7px rgba(150, 190, 235, 0.85));
}

.lightning-bolt path {
	fill: none;
	stroke: rgba(244, 249, 255, 0.98);
	stroke-linecap: round;
	stroke-linejoin: round;
	stroke-width: var(--bolt-width);
	vector-effect: non-scaling-stroke;
}

.lightning-bolt__branch {
	opacity: 0.76;
}

@keyframes lightning-flicker {
	0%,
	8% {
		opacity: 0;
	}
	9%,
	16% {
		opacity: 0.96;
	}
	17%,
	27% {
		opacity: 0.08;
	}
	28%,
	46% {
		opacity: 1;
	}
	47%,
	63% {
		opacity: 0.2;
	}
	64%,
	78% {
		opacity: 0.75;
	}
	100% {
		opacity: 0;
	}
}

/* Twice the original wash; the weather sets its opacity (0.5 = original). */
.storm-tone {
	z-index: 55;
	opacity: var(--gloom, 0.5);
	background:
		radial-gradient(
			circle at 50% 42%,
			transparent 20%,
			rgba(0, 8, 13, 0.4) 92%
		),
		linear-gradient(to bottom, rgba(2, 11, 17, 0.16), rgba(2, 9, 12, 0.4));
	/* A plain overlay, not a multiply blend: with colours this dark the two
	   look the same, and a blend made the browser recombine the whole scene
	   beneath it every frame. */
}

.cloud-lightning-flash {
	position: absolute;
	top: var(--flash-top);
	left: var(--flash-x);
	width: var(--flash-width);
	height: var(--flash-height);
	border-radius: 50%;
	background: radial-gradient(
		ellipse at center,
		rgba(221, 237, 255, var(--flash-strength)),
		rgba(127, 164, 207, 0.16) 28%,
		transparent 68%
	);
	-webkit-mask-image: var(--flash-cloud-mask);
	-webkit-mask-position: center;
	-webkit-mask-repeat: no-repeat;
	-webkit-mask-size: cover;
	mask-image: var(--flash-cloud-mask);
	mask-position: center;
	mask-repeat: no-repeat;
	mask-size: cover;
	/* No blur or blend: on a new element every strike, those made Safari
	   stall for a few hundred milliseconds. */
	transform: translateX(-50%);
	animation: cloud-lightning-flash var(--flash-duration) steps(1, end)
		forwards;
}

.lightning-layer--back .cloud-lightning-flash {
	--flash-cloud-mask: url("./assets/mobile/storm-cloud-map.avif");
}

.lightning-layer--middle .cloud-lightning-flash {
	--flash-cloud-mask: url("./assets/mobile/storm-cloud-alpha.avif");
}

.lightning-layer--front .cloud-lightning-flash {
	--flash-cloud-mask: url("./assets/clouds/storm-cloud-cumulus.avif");
}

@keyframes cloud-lightning-flash {
	0%,
	10% {
		opacity: 0;
	}
	11%,
	19% {
		opacity: 0.48;
	}
	20%,
	31% {
		opacity: 0.08;
	}
	32%,
	47% {
		opacity: 1;
	}
	48%,
	66% {
		opacity: 0.16;
	}
	67%,
	78% {
		opacity: 0.62;
	}
	100% {
		opacity: 0;
	}
}

@media (prefers-reduced-motion: reduce) {
	.storm-cloud__texture {
		animation: none;
	}
}

.is-resizing .storm-cloud__texture,
.is-resizing .shooting-star {
	animation-play-state: paused;
}

.scenery {
	position: absolute;
	left: 50%;
	width: var(--width, 100%);
	height: var(--height);
	bottom: var(--bottom);
	z-index: var(--stack-order);
	transform: translateX(-50%) scale(var(--parallax-overscan));
	transform-origin: center bottom;
	background-color: transparent;
	background-image: var(--texture);
	background-position: center bottom;
	background-repeat: no-repeat;
	background-size: var(--texture-size, auto 100%);
	-webkit-mask-image: var(--mask, none);
	-webkit-mask-position: center bottom;
	-webkit-mask-repeat: no-repeat;
	-webkit-mask-size: var(--mask-size, auto 100%);
	mask-image: var(--mask, none);
	mask-position: center bottom;
	mask-repeat: no-repeat;
	mask-size: var(--mask-size, auto 100%);
	/* Each layer's own grade, then the time of day's light. */
	filter: var(--color-grade) brightness(var(--land-light, 1))
		saturate(var(--land-saturation, 1)) sepia(var(--land-warmth, 0));
	pointer-events: none;
	contain: layout paint style;

	/* Every land image is cropped to the rows that can ever be on screen
	   (plus a margin for the parallax): the rest was never seen but still
	   took graphics memory, and running out made Chrome drop layers
	   (flashing, a vanishing figure). Each layer's height and bottom are set
	   so the crop lands exactly where the full image's rows did. */
}

/* Every land layer stacks above every cloud and lightning layer (2-35), so
   clouds never pass in front of the land. Each far layer's place is shared
   with its drawn copy (wind.js), and the hills' and olives' with the shadow
   on them.
   The far horizon: the mountains of Moab beyond the Dead Sea, peeking over
   the hills, hazy with distance. */
:is(.scenery[data-scenery="far-mountains"], .land-wind--far) {
	--stack-order: 36;
	--bottom: 9dvh;
	--height: 28dvh;
	--texture: url("./assets/far-mountains-v2.avif");
	/* Distance shows as haze: paler, softer and greyer than the hills. */
	--color-grade: brightness(33%) saturate(55%) contrast(78%) sepia(10%);
}

/* The Mount of Olives, between the desert hills and the city, shared with the
   hills' shadow on it and its marker. */
:is(
	.scenery[data-scenery="olives"],
	.land-wind--olives,
	.land-shadow--olives,
	.scene-markers--olives
) {
	--stack-order: 38;
	--bottom: -8dvh;
	--height: 38dvh;
	--texture: url("./assets/olives-v3.avif");
	--color-grade: brightness(35%) saturate(85%) contrast(90%) sepia(6%);
}

/* The desert hills, shared with the mountains' shadow cast on them. */
:is(
	.scenery[data-scenery="distant-hills"],
	.land-wind--hills,
	.land-shadow--hills
) {
	--stack-order: 37;
	--bottom: -14.869dvh;
	--height: 40.688dvh;
	--texture: url("./assets/hills-v5.avif");
	--color-grade: brightness(30%) saturate(60%) contrast(84%) sepia(6%);
}

/* On phones and small tablets the hills sit a little lower and the
   mountains beyond them a little higher, so more of the far horizon shows. */
@media (max-width: 900px) {
	:is(.scenery[data-scenery="far-mountains"], .land-wind--far) {
		--bottom: 12dvh;
	}

	:is(
		.scenery[data-scenery="distant-hills"],
		.land-wind--hills,
		.land-shadow--hills
	) {
		--bottom: -16.4dvh;
	}
}

/* The two near layers are cut-out images with their own skylines; the
   ridge's sits about 11dvh above the bottom of the screen and the near
   ground's about 5dvh. The ridge slopes down to the right so Jerusalem shows
   above it. Each place is shared with its swaying copy (wind.js). */
:is(.scenery[data-scenery="golgotha-ridge"], .land-wind--ridge) {
	--stack-order: 41;
	--bottom: -5.984dvh;
	--height: 20.483dvh;
	--texture: url("./assets/golgotha-ridge-v3.avif");
	--color-grade: brightness(32%) saturate(65%) contrast(95%) sepia(8%);
}

.scenery[data-scenery="distant-jerusalem"] {
	background-image: none;
	filter: none;
	isolation: isolate;
}

.scenery[data-scenery="distant-jerusalem"]::before {
	position: absolute;
	inset: 0;
	z-index: 0;
	content: "";
	background-image: var(--texture);
	background-position: center bottom;
	background-repeat: no-repeat;
	background-size: var(--texture-size, auto 100%);
	filter: var(--color-grade) brightness(var(--land-light, 1))
		saturate(var(--land-saturation, 1)) sepia(var(--land-warmth, 0));
	/* Own GPU layer, so a change of light is a GPU filter, not a repaint of
	   the whole city (the other land layers already are their own layers). */
	will-change: transform;
}

/* The city's and near ground's size and place, shared with the shadows cast
   on them. */
:is(
	.scenery[data-scenery="distant-jerusalem"],
	.land-shadow--city,
	.scene-markers--city
) {
	--stack-order: 39;
	/* As wide as the screen, but never less than a little over its height:
	   on narrow screens (phones, a narrowed window) it stops shrinking, its
	   ends cut off, rather than sinking behind the land in front. */
	--city-width: max(100vw, 118dvh);
	--width: var(--city-width);
	/* The bigger it is beyond the screen's width, the lower it sits, so it
	   stays on the ridge rather than floating. */
	--bottom: calc(
		clamp(-14dvh, calc(16dvh - 14vw), 8dvh) - (var(--city-width) - 100vw) *
			0.1
	);
	/* The city's marker (js/teachings.js): three quarters across, or nearer
	   the middle when that would come within 110px (17% on narrow phones,
	   so it stays clear of the cross) of the screen's right edge, allowing
	   for the parallax's enlargement. */
	--city-marker-left: min(
		75%,
		calc(50% + 50vw / var(--parallax-overscan) - min(110px, 17vw))
	);
	/* Exactly the image's height (it's sized by width, anchored at the
	   bottom): a taller box was mostly empty but still took four large
	   GPU layers (the city, its shadow, its lights' box and its marker). */
	--height: calc(var(--city-width) * 708 / 4096);
	--texture: url("./assets/jerusalem-v7.avif");
	--texture-size: 100% auto;
	/* Warm like the hills behind it (it was greyed out before). */
	--color-grade: brightness(47%) saturate(70%) contrast(95%) sepia(6%);
}

:is(.scenery[data-scenery="ground"], .land-shadow--ground, .land-wind--ground) {
	--stack-order: 42;
	--bottom: -6.028dvh;
	--height: 15.19dvh;
	--texture: url("./assets/near-ground-v3.avif");
	--color-grade: brightness(42%) saturate(72%) contrast(98%) sepia(8%);
}

/* Cast shadows of the ridges, drawn by landShadows in js/land.js into a small
   canvas that lies exactly over the land it falls on (same size, place and
   parallax), just above it. Shadows are soft, so a low-resolution canvas
   stretched to fit looks the same. Built from CSS masks and transformed
   layers instead, they made Chrome drop whole frames and Safari stall. The
   swaying ground (wind.js) is placed the same way. */
.land-shadow,
.land-wind,
.scene-markers--city,
.scene-markers--olives {
	position: absolute;
	left: 50%;
	bottom: var(--bottom);
	z-index: var(--stack-order);
	width: var(--width, 100%);
	height: var(--height);
	transform: translateX(-50%) scale(var(--parallax-overscan));
	transform-origin: center bottom;
	pointer-events: none;
}

/* A deep, see-through shade over the land (not a multiply blend, which made
   the browser recombine everything beneath it every frame). */
.land-shadow {
	opacity: var(--shadow-strength, 0);
}

/* The clock changes the light a step at a time; these glide between the
   steps, on the GPU (the drawn layers glide in wind.js, the sky and the
   shadows' angles in the page). A jump in time is already eased. */
:is(
	.storm-cloud,
	.storm-tone,
	.land-shadow,
	.city-lights,
	.city-flicker-plane
) {
	transition: opacity calc(1.2 * var(--clock-tick, 1s)) linear;

	:root[data-jumping] & {
		transition-duration: 150ms;
	}
}

/* Every canvas a worker draws (wind.js). Chrome snaps such a canvas to
   whole pixels whenever it is only moved, so with the parallax each layer
   stepped a pixel at a time, out of step with the others. Any rotation
   stops the snapping; this one is far too small to see. */
:is(
	.land-wind,
	.land-wind--city,
	.christ-wind,
	.storm-cloud__texture > canvas
) {
	rotate: 0.0001deg;
}

/* The city's drawn copy lies under its lights. */
.land-wind--city {
	position: absolute;
	inset: 0;
	z-index: 0;
	width: 100%;
	height: 100%;
}

/* Once the drawn copy is up, the CSS one isn't needed. */
.scenery.is-swaying {
	background-image: none;
	filter: none;

	&::before {
		content: none;
	}
}

/* The light masks and the flicker plane only cover the strip of the city that
   has windows: rows 1408-1728 of the 4096x2048 city image, i.e. a strip
   320/4096 of the width tall, sitting 320/4096 of the width above the bottom.
   Margin percentages use the width, so the strip stays locked to the city at
   any screen size. Keeping the masks this small saves ~230MB of image memory. */
.city-lights,
.city-flicker-plane {
	position: absolute;
	left: 0;
	bottom: 0;
	width: 100%;
	aspect-ratio: 4096 / 320;
	margin-bottom: 7.8125%;
}

/* All the lights together: one warm glow around every window and lantern,
   blended onto the city. The glow is drawn once; as daylight comes the whole
   group fades (its opacity, set by applyTimeOfDay), which doesn't redraw it. */
.city-lights {
	z-index: 1;
	mix-blend-mode: screen;
	filter: drop-shadow(0 0 2px rgba(255, 200, 130, 0.9))
		drop-shadow(0 0 7px rgba(255, 140, 50, 0.65));
	overflow: visible;
}

.city-light {
	position: absolute;
	inset: 0;
	background-color: var(--light-color);
	-webkit-mask-image: var(--light-mask);
	-webkit-mask-repeat: no-repeat;
	-webkit-mask-size: 100% 100%;
	mask-image: var(--light-mask);
	mask-repeat: no-repeat;
	mask-size: 100% 100%;
	opacity: var(--light-opacity);
}

.city-light--home {
	--light-color: #ff9d42;
}

.city-light--architecture {
	--light-mask: url("./assets/city-lights/architecture.webp");
	--light-color: #94735b;
	--light-opacity: 0.35;
}

.city-light--home-1 {
	--light-mask: url("./assets/city-lights/home-1.webp");
	--light-color: #ff9a3d;
	--light-opacity: 0.9;
}

.city-light--home-2 {
	--light-mask: url("./assets/city-lights/home-2.webp");
	--light-color: #ffb35f;
	--light-opacity: 0.95;
}

.city-light--home-3 {
	--light-mask: url("./assets/city-lights/home-3.webp");
	--light-color: #f47f2f;
	--light-opacity: 0.8;
}

.city-light--home-4 {
	--light-mask: url("./assets/city-lights/home-4.webp");
	--light-color: #ffc174;
	--light-opacity: 0.92;
}

.city-light--home-5 {
	--light-mask: url("./assets/city-lights/home-5.webp");
	--light-color: #df6c28;
	--light-opacity: 0.72;
}

.city-light--lantern {
	--light-color: #ffc968;
	z-index: 2;
}

.city-light--lantern-common {
	--light-mask: url("./assets/city-lights/lantern-common.webp");
	--light-color: #ffca69;
	--light-opacity: 1;
}

.city-light--lantern-variant {
	--light-mask: url("./assets/city-lights/lantern-variant.webp");
	--light-color: #ff9f43;
	--light-opacity: 0.95;
}

.city-flicker-plane {
	z-index: 3;
	pointer-events: none;
	opacity: var(--city-glow, 1);
	/* Own GPU layer, so each flicker doesn't repaint the glowing city-light
	   masks underneath. That repaint was the main source of dropped frames. */
	will-change: transform;
}

.city-flicker-light {
	position: absolute;
	top: var(--flicker-y);
	left: var(--flicker-x);
	width: var(--flicker-width);
	height: var(--flicker-height);
	border-radius: 30%;
	background: var(--flicker-color);
	box-shadow:
		0 0 2px rgba(255, 220, 151, 0.95),
		0 0 6px rgba(255, 126, 35, 0.72);
	opacity: 0;
	will-change: opacity;
}

.christ {
	image-rendering: auto;
	pointer-events: none;
	/* The crossbar as wide and high on screen as the old figure's. */
	height: 91.22%;
	aspect-ratio: 1186 / 2337; /* the artwork's, for the canvas too */
	position: absolute;
	z-index: 45;
	bottom: -2.47%;
	left: 50%;
	/* The cross in the artwork is turned about 0.5deg anticlockwise (its
	   crossbar's right end higher, its upright drifting right toward the
	   foot), so it's turned back level. Its upright is 3.6% of the width right
	   of the crossbar's middle, so it's shifted halfway between the two. */
	transform: translateX(-51.77%) rotate(0.5deg);
	transform-origin: center center;
	filter: drop-shadow(0 0 1px rgba(24, 14, 10, 0.42))
		brightness(var(--figure-light, 1)) saturate(var(--figure-saturation, 1));
}

/* The figure's markers lie over it, turning and moving with it. */
.christ-markers {
	z-index: 70;
	filter: none;
}

/* The moving figure applies the light itself (wind.js). */
.christ-wind {
	filter: none;
}

.christ.is-swaying {
	visibility: hidden;
}

/* Full-screen panels over the blurred scene: the loading screen and the
   phone motion prompt. */
.overlay {
	position: fixed;
	inset: 0;
	display: grid;
	place-items: center;
	box-sizing: border-box;
	padding: max(24px, env(safe-area-inset-top)) 24px
		max(24px, env(safe-area-inset-bottom));
	border: 0;
	background:
		radial-gradient(
			circle at 50% 42%,
			rgba(231, 180, 119, 0.11),
			transparent 46%
		),
		rgba(5, 15, 22, 0.3);
	-webkit-backdrop-filter: blur(16px) saturate(70%);
	backdrop-filter: blur(16px) saturate(70%);
	color: white;
	font: inherit;
	z-index: 100;
}

.overlay[hidden] {
	display: none;
}

.overlay-card {
	display: flex;
	align-items: center;
	flex-direction: column;
	box-sizing: border-box;
	width: min(78vw, 300px);
	max-width: calc(100vw - 48px);
	padding: 27px 20px 24px;
	border: 1px solid rgba(255, 255, 255, 0.18);
	border-radius: 20px;
	background: linear-gradient(
		145deg,
		rgba(35, 54, 64, 0.82),
		rgba(12, 28, 37, 0.88)
	);
	box-shadow:
		0 24px 70px rgba(0, 0, 0, 0.42),
		inset 0 1px rgba(255, 255, 255, 0.08);
	text-align: center;
	transition: transform 120ms ease-out;
}

.overlay-actions {
	display: flex;
	flex-direction: column;
	gap: 10px;
	width: 100%;
	margin-top: 22px;
}

.overlay-button {
	padding: 12px 16px;
	border: 1px solid rgba(255, 255, 255, 0.16);
	border-radius: 12px;
	background: rgba(255, 255, 255, 0.04);
	color: rgba(235, 240, 239, 0.86);
	font:
		600 0.86rem/1.2 system-ui,
		sans-serif;
	letter-spacing: 0.02em;
	cursor: pointer;
	touch-action: manipulation;
	transition:
		background 150ms ease,
		transform 120ms ease-out;
}

.overlay-button:active {
	transform: scale(0.98);
}

.overlay-button--primary {
	border-color: transparent;
	background: #e7b477;
	color: #13222b;
}

.overlay-button:focus-visible,
.settings-button:focus-visible,
.sound-toggle:focus-visible,
.settings-switch-input:focus-visible + .settings-switch {
	outline: 2px solid #e7b477;
	outline-offset: 2px;
}

.overlay-eyebrow {
	margin-bottom: 9px;
	color: #e7b477;
	font:
		700 var(--text-eyebrow)/1.2 system-ui,
		sans-serif;
	letter-spacing: 0.16em;
	text-transform: uppercase;
}

.overlay-title {
	font:
		500 clamp(1.65rem, 6.8vw, 2rem) / 1.15 Georgia,
		"Times New Roman",
		serif;
	letter-spacing: 0.015em;
	text-shadow: 0 2px 18px rgba(0, 0, 0, 0.55);
}

.overlay-copy {
	max-width: 225px;
	margin-top: 10px;
	color: rgba(235, 240, 239, 0.72);
	font:
		500 0.78rem/1.5 system-ui,
		sans-serif;
	letter-spacing: 0.045em;
}

/* Loading screen: a softly lit cross whose outline is traced by a moving
   glint, over a bar that fills as the scene loads (--progress, 0 to 1). */
#loading {
	--progress: 0;
	z-index: 110;
	cursor: progress;
	transition:
		opacity 900ms ease,
		visibility 0s linear 900ms;
}

#loading.is-done {
	opacity: 0;
	visibility: hidden;
}

/* The card matches the caption box (narrower): the same background,
   corners, padding (the caption's plus its text's), fonts and sizes, and the
   same small even gaps. */
/* Spaced in groups: the spinner, the name and title together, the
   progress line, then the verse with its reference. */
#loading .overlay-card {
	gap: 0;
	width: min(440px, calc(100vw - 32px));
	max-width: none;
	padding: 20px 28px 22px;
	border: 0;
	border-radius: 20px;
	background: rgba(8, 16, 22, 0.62);
	box-shadow: none;
}

#loading .overlay-eyebrow {
	margin: 0 0 6px;
	font:
		700 var(--text-eyebrow)/1.2 system-ui,
		sans-serif;
}

#loading .overlay-title {
	font:
		500 var(--text-label)/1.25 Georgia,
		"Times New Roman",
		serif;
	text-shadow: none;
}

.loading-emblem {
	position: relative;
	display: grid;
	place-items: center;
	width: 56px;
	height: 56px;
	margin-bottom: 16px;
}

/* Spinner: a thin golden arc circling the cross. */
.loading-emblem::before {
	position: absolute;
	inset: 0;
	content: "";
	border: 1.5px solid rgba(231, 180, 119, 0.16);
	border-top-color: #e7b477;
	border-radius: 50%;
	animation: loading-spin 1.1s linear infinite;
}

@keyframes loading-spin {
	to {
		transform: rotate(1turn);
	}
}

.loading-cross {
	width: 23px;
	height: 32px;
	overflow: visible;
}

.loading-cross__base {
	fill: rgba(231, 180, 119, 0.32);
	stroke: rgba(231, 180, 119, 0.28);
	stroke-width: 1.2;
}

.loading-cross__glint {
	fill: none;
	stroke: #f3cf9d;
	stroke-width: 1.6;
	stroke-linecap: round;
	stroke-linejoin: round;
	stroke-dasharray: 18 82;
	filter: drop-shadow(0 0 4px rgba(243, 207, 157, 0.9));
	animation: loading-trace 2.8s linear infinite;
}

@keyframes loading-trace {
	to {
		stroke-dashoffset: -100;
	}
}

.loading-progress {
	width: 140px;
	height: 2px;
	margin: 16px 0 14px;
	overflow: hidden;
	border-radius: 2px;
	background: rgba(255, 255, 255, 0.12);
}

.loading-progress::after {
	display: block;
	height: 100%;
	content: "";
	background: #e7b477;
	transform: scaleX(var(--progress));
	transform-origin: left;
	transition: transform 400ms ease-out;
}

.loading-verse {
	text-wrap: balance;
	color: rgba(235, 240, 239, 0.85);
	font:
		italic 500 var(--text-body)/1.5 system-ui,
		sans-serif;
}

.loading-verse cite {
	display: block;
	margin-top: 8px;
	color: #e7b477;
	font:
		700 var(--text-eyebrow)/1.2 system-ui,
		sans-serif;
	font-style: normal;
	letter-spacing: 0.14em;
	text-transform: uppercase;
}

/* Phones and computers: the caption's sizes there. */
@media (max-width: 600px) {
	#loading .overlay-card {
		width: min(300px, calc(100vw - 24px));
		padding: 28px 14px;
		border-radius: 16px;
	}

	.loading-progress {
		width: 120px;
	}
}

@media (min-width: 601px) {
	#loading .overlay-card {
		width: min(440px, calc(100vw - 32px));
		padding: 32px 24px;
		border-radius: 10px;
	}

	/* A little under the sizes of "Scene Settings" and the settings' labels. */
	#loading .overlay-title {
		font-size: calc(var(--text-title) * 0.9);
	}

	.loading-verse {
		font-size: 1rem;
	}

	.loading-progress {
		width: 120px;
	}
}

@media (prefers-reduced-motion: reduce) {
	.loading-cross__glint,
	.loading-emblem::before {
		animation-duration: 4s;
	}
}

[data-z_index] {
	will-change: translate;
}

/* Settings: a gear in the top-right corner opens a panel of live controls. */
.settings-button {
	position: fixed;
	top: max(var(--edge-gap), env(safe-area-inset-top));
	right: max(var(--edge-gap), env(safe-area-inset-right));
	z-index: 90;
	display: grid;
	place-items: center;
	width: 76px;
	height: 76px;
	border-radius: 50%;
	color: #e7b477;
	cursor: pointer;
	touch-action: manipulation;
}

.settings-button svg {
	width: 40px;
	height: 40px;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.5;
	stroke-linecap: round;
	stroke-linejoin: round;
}

/* While the panel is open the gear becomes an x that closes it. */
.settings-x,
.settings-button[aria-expanded="true"] .settings-gear {
	display: none;
}

.settings-button[aria-expanded="true"] .settings-x {
	display: block;
}

/* A panel that drops from the gear, over the scene but leaving the time,
   the gear and the caption visible (js/settings-panel.js keeps it above the caption). */
.settings {
	position: fixed;
	inset: calc(max(var(--edge-gap), env(safe-area-inset-top)) + 88px)
		max(var(--edge-gap), env(safe-area-inset-right)) auto auto;
	z-index: 85;
	width: min(580px, calc(100vw - 40px));
	max-height: calc(100dvh - 140px);
	margin: 0;
	padding: 0;
	border: 0;
	border-radius: 24px;
	background: none;
	color: white;
	overflow: hidden;
}

.settings[open] {
	display: flex;
	flex-direction: column;
}

.settings * {
	overflow: visible;
}

/* The card fits the panel's height and everything in it scrolls. */
.settings .settings-card {
	min-height: 0;
	overflow: hidden;
}

.settings .settings-scroll {
	display: flex;
	flex: 1 1 auto;
	flex-direction: column;
	gap: inherit;
	min-height: 0;
	/* Reaches into the card's right padding (the text stays put), so the
	   scrollbar runs down that empty strip instead of over the text. */
	margin-right: -18px;
	padding-right: 18px;
	overflow-y: auto;
	overscroll-behavior: contain;
	touch-action: pan-y;
}

.settings-card {
	display: flex;
	flex-direction: column;
	gap: 38px;
	padding: 46px 44px 42px;
	border: 1px solid rgba(255, 255, 255, 0.18);
	border-radius: 24px;
	background: linear-gradient(
		145deg,
		rgba(35, 54, 64, 0.9),
		rgba(12, 28, 37, 0.94)
	);
	box-shadow:
		0 24px 70px rgba(0, 0, 0, 0.42),
		inset 0 1px rgba(255, 255, 255, 0.08);
}

.settings-header {
	position: relative;
}

.settings-title {
	margin-top: 6px;
	font:
		500 var(--text-title)/1.15 Georgia,
		"Times New Roman",
		serif;
}

.settings-group {
	display: flex;
	flex-direction: column;
	gap: 28px;
	padding: 34px 0 0;
	border: 0;
	border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.settings-group legend,
.settings-group-title {
	float: left;
	margin-bottom: 4px;
	color: #e7b477;
	font:
		700 var(--text-eyebrow)/1.2 system-ui,
		sans-serif;
	letter-spacing: 0.16em;
	text-transform: uppercase;
}

.settings-row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 20px;
	cursor: pointer;
}

.settings-row--stacked {
	flex-direction: column;
	align-items: stretch;
	gap: 16px;
}

.settings-label {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	gap: 8px 16px;
	font:
		600 var(--text-label)/1.3 system-ui,
		sans-serif;
}

.settings-label small,
.settings-label output {
	color: rgba(235, 240, 239, 0.6);
	font:
		500 var(--text-body)/1.45 system-ui,
		sans-serif;
}

.settings-label small {
	flex-basis: 100%;
}

.settings input[type="range"] {
	width: 100%;
	height: 32px;
	margin: 0;
	accent-color: #e7b477;
	touch-action: none;
}

/* "Clear ... Storm" labels under a slider's ends. */
.settings-scale {
	display: flex;
	justify-content: space-between;
	margin-top: -8px;
	color: rgba(235, 240, 239, 0.45);
	font:
		600 var(--text-eyebrow)/1.2 system-ui,
		sans-serif;
	letter-spacing: 0.04em;
}

.settings input[type="range"]:disabled {
	opacity: 0.35;
}

/* Switch: a visually hidden checkbox drives the pill next to it. */
.settings-switch-input {
	position: absolute;
	opacity: 0;
	pointer-events: none;
}

.settings-switch {
	position: relative;
	flex: none;
	width: 60px;
	height: 36px;
	border-radius: 18px;
	background: rgba(255, 255, 255, 0.16);
	transition: background 200ms ease;
}

.settings-switch::before {
	position: absolute;
	top: 3px;
	left: 3px;
	width: 30px;
	height: 30px;
	content: "";
	border-radius: 50%;
	background: white;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
	transition: translate 200ms ease;
}

.settings-switch-input:checked + .settings-switch {
	background: #e7b477;
}

/* Auto / High / Medium / Low: one segmented row of radio buttons. */
.settings-choice {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 4px;
	padding: 4px;
	border-radius: 14px;
	background: rgba(255, 255, 255, 0.08);
}

.settings-choice input {
	position: absolute;
	opacity: 0;
	pointer-events: none;
}

.settings-choice span {
	display: block;
	padding: 10px 0;
	border-radius: 10px;
	color: rgba(235, 240, 239, 0.75);
	font:
		600 var(--text-body)/1.2 system-ui,
		sans-serif;
	text-align: center;
	cursor: pointer;
	transition:
		background-color 150ms ease,
		color 150ms ease;
}

.settings-choice span:hover {
	background: rgba(255, 255, 255, 0.08);
}

.settings-choice input:checked + span {
	background: #e7b477;
	color: #13212a;
}

.settings-choice input:focus-visible + span {
	outline: 2px solid #e7b477;
	outline-offset: 2px;
}

.settings-switch-input:checked + .settings-switch::before {
	translate: 24px 0;
}

/* Phones: the panel fills the screen. */
@media (max-width: 600px) {
	.settings {
		inset: calc(max(var(--edge-gap), env(safe-area-inset-top)) + 48px) 12px
			auto;
		width: auto;
		border-radius: 20px;
	}

	.settings-card {
		gap: 28px;
		padding: 28px 22px 26px;
		border-radius: 20px;
	}

	.settings-group {
		gap: 22px;
		padding-top: 24px;
	}

	/* The same size as the caption's arrows. */
	.settings-button {
		width: 38px;
		height: 38px;
	}

	.settings-button svg {
		width: 22px;
		height: 22px;
	}
}

/* Sound on or muted: the gear's size, just left of it, as close as the
   speed button is to pause/play. */
.sound-toggle {
	position: fixed;
	top: max(var(--edge-gap), env(safe-area-inset-top));
	right: calc(
		max(var(--edge-gap), env(safe-area-inset-right)) + 38px +
			var(--button-gap)
	);
	z-index: 90;
	display: grid;
	place-items: center;
	width: 38px;
	height: 38px;
	padding: 0;
	border-radius: 50%;
	color: #e7b477;
	cursor: pointer;
	touch-action: manipulation;
}

.sound-toggle svg {
	width: 20px;
	height: 20px;
	fill: none;
	stroke: currentColor;
	stroke-width: 1.5;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.sound-toggle[aria-pressed="true"] .sound-off,
.sound-toggle:not([aria-pressed="true"]) .sound-on {
	display: none;
}

/* The hint that the scene has sound: a small bubble just under the speaker,
   its point aimed at it, fading in and out (js/sound.js). */
.sound-hint {
	position: fixed;
	top: calc(max(var(--edge-gap), env(safe-area-inset-top)) + 38px + 12px);
	right: max(var(--edge-gap), env(safe-area-inset-right));
	z-index: 90;
	max-width: 280px;
	padding: 9px 13px;
	overflow: visible; /* the page clips by default; this lets the point show */
	border: 1px solid rgba(231, 180, 119, 0.45);
	border-radius: 10px;
	/* Solid, the same as its point, so the two join without a seam. */
	background: rgb(12, 28, 37);
	color: white;
	font:
		500 var(--text-body)/1.35 Georgia,
		"Times New Roman",
		serif;
	text-align: left;
	opacity: 0;
	translate: 0 -6px;
	pointer-events: none;
	transition:
		opacity 600ms ease,
		translate 600ms ease;
}

.sound-hint.is-shown {
	opacity: 1;
	translate: 0 0;
}

/* The point, under the middle of the speaker button. */
.sound-hint::before {
	content: "";
	position: absolute;
	top: -6px;
	right: calc(38px + var(--button-gap) + 19px - 6px);
	width: 10px;
	height: 10px;
	border-top: 1px solid rgba(231, 180, 119, 0.45);
	border-left: 1px solid rgba(231, 180, 119, 0.45);
	background: rgb(12, 28, 37);
	rotate: 45deg;
}

/* The same bubble under the gear, pointing at it, with a link to GitHub. */
.github-hint {
	&.is-shown {
		pointer-events: auto;
	}

	&::before {
		right: calc(19px - 6px);
	}

	a {
		color: #e7b477;
		text-underline-offset: 3px;
	}
}

/* Top left: a pause/play button the same size as the settings gear, then
   the time in plain white text. */
.clock-bar {
	/* The button sits as far from the corner as the gear does from its
	   corner, and the time as far again from the button. */
	--edge: var(--edge-gap);
	position: fixed;
	top: max(var(--edge), env(safe-area-inset-top));
	left: max(var(--edge), env(safe-area-inset-left));
	z-index: 80;
	display: flex;
	align-items: center;
	gap: var(--edge);
	font-size: var(--text-clock);
}

/* Nothing here is clipped (the page clips everything by default), so the
   time's soft shadow shows in full. */
.clock-bar,
.clock-bar * {
	overflow: visible;
}

.clock-time {
	color: white;
	font:
		500 1em/1 "Clock Digits",
		Georgia,
		"Times New Roman",
		serif;
	letter-spacing: 0.01em;
	white-space: nowrap;
	text-shadow:
		0 1px 2px rgba(0, 0, 0, 0.5),
		0 2px 16px rgba(0, 0, 0, 0.45);
}

:is(.clock-play, .clock-speed) {
	display: grid;
	place-items: center;
	width: 76px;
	height: 76px;
	padding: 0;
	border-radius: 50%;
	color: white;
	cursor: pointer;
	touch-action: manipulation;
}

:is(.clock-play, .clock-speed):focus-visible {
	outline: 2px solid #e7b477;
	outline-offset: 2px;
}

.clock-play svg {
	width: 36px;
	height: 36px;
	fill: currentColor;
	stroke: currentColor;
	stroke-width: 3;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.clock-play__pause {
	fill: none;
}

/* Close beside pause/play: half the gap the time keeps from them. */
.clock-speed {
	/* --button-gap from pause/play (the bar's own gap is 12px). */
	margin-left: calc(var(--button-gap) - 12px);
	font:
		600 26px/1 system-ui,
		sans-serif;
	font-variant-numeric: tabular-nums;
	letter-spacing: -0.02em;
}

/* Paused, it shows play. */
.clock-play__play,
.clock-play[data-paused] .clock-play__pause {
	display: none;
}

.clock-play[data-paused] .clock-play__play {
	display: block;
}

/* At 3 PM, pause/play (and the caption's next arrow) become restart. */
.clock-play__restart,
.caption-step:not([data-restart]) .caption-step__restart,
.caption-step[data-restart] svg:not(.caption-step__restart),
.clock-play[data-restart] :is(.clock-play__pause, .clock-play__play) {
	display: none;
}

.clock-play[data-restart] .clock-play__restart {
	display: block;
	fill: none;
	stroke-width: 2.2;
}

/* The caption's restart the same size as the clock's (its arrows are
   larger). */
.caption-step .caption-step__restart {
	width: 18px;
	height: 18px;
	stroke-width: 2.2;
}

/* The caption, bottom centre: what is happening, like a film subtitle, with
   arrows to step between events; clicking it opens the list of them all
   upward. It folds to one line a few seconds after each event and opens
   again on hover or a tap (timelineClock in js/timeline.js). */
.caption {
	position: fixed;
	bottom: max(28px, env(safe-area-inset-bottom));
	left: 50%;
	z-index: 80;
	display: grid;
	grid-template-columns: auto 1fr auto;
	align-items: end; /* the arrows stay put as the box grows upward */
	gap: 14px;
	width: min(760px, calc(100vw - 32px));
	box-sizing: border-box;
	color: white;
	translate: -50% 0;
	overflow: visible;
}

/* The title and text are one button (the dark box; the arrows sit outside
   it) that opens the list. */
.caption-content {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 6px;
	min-width: 0;
	padding: 18px 24px 20px;
	border: 1px solid transparent;
	border-radius: 20px;
	background: rgba(8, 16, 22, 0.62);
	color: inherit;
	text-align: center;
	text-wrap: balance;
	cursor: pointer;
	touch-action: manipulation;
	transition:
		background-color 150ms ease,
		border-color 150ms ease;
}

.caption-content:hover,
.caption-content[aria-expanded="true"] {
	border-color: rgba(255, 255, 255, 0.14);
	background: rgba(24, 33, 39, 0.66);
}

.caption-title,
.caption-text {
	transition: opacity 400ms ease;
}

.caption.is-changing :is(.caption-title, .caption-text) {
	opacity: 0;
}

.caption-title {
	font:
		500 var(--text-label)/1.25 Georgia,
		"Times New Roman",
		serif;
}

.caption-time {
	opacity: 0.6;
	font-family: "Clock Digits", Georgia, "Times New Roman", serif;
}

@keyframes caption-appear {
	from {
		opacity: 0;
	}
}

/* Folded (a few seconds after each event, or a click elsewhere): one line,
   cut off with an ellipsis only when the title doesn't fit. */
.caption.is-folded .caption-content {
	flex-direction: row;
	justify-content: center;
	gap: 8px;
}

.caption.is-folded .caption-title {
	min-width: 0;
	white-space: nowrap;
	text-overflow: ellipsis;
}

.caption.is-folded .caption-text {
	display: none;
}

.caption:not(.is-folded) .caption-text {
	animation: caption-appear 300ms ease;
}

/* Lines filled as far as they go (only a lone last word is avoided), so the
   box stays as short as it can. */
.caption-text {
	color: rgba(235, 240, 239, 0.85);
	font:
		500 var(--text-body)/1.4 system-ui,
		sans-serif;
	text-wrap: pretty;
}

/* Round buttons of their own, beside the caption box. */
.caption-step {
	display: grid;
	place-items: center;
	border-radius: 50%;
	color: #e7b477;
	cursor: pointer;
	touch-action: manipulation;
}

.caption-step {
	width: 52px;
	height: 52px;
}

.caption-step svg {
	width: 30px;
	height: 30px;
	fill: none;
	stroke: currentColor;
	stroke-width: 2;
	stroke-linecap: round;
	stroke-linejoin: round;
}

.caption-step:disabled {
	opacity: 0.3;
	cursor: default;
}

.caption-step:focus-visible,
.caption-content:focus-visible,
.timeline-events button:focus-visible {
	outline: 2px solid #e7b477;
	outline-offset: 2px;
}

/* The list of the day's events opens upward from the caption, as wide as
   it. js/timeline.js sets its height to the space above. */
.timeline {
	position: absolute;
	/* As far above the caption as the controls are from the screen's edge. */
	bottom: calc(100% + var(--edge-gap));
	left: 0;
	width: 100%;
	box-sizing: border-box;
	--pad-top: 22px;
	--pad-right: 16px;
	padding: var(--pad-top) var(--pad-right) 16px;
	border: 1px solid rgba(255, 255, 255, 0.18);
	border-radius: 24px;
	background: linear-gradient(
		145deg,
		rgba(35, 54, 64, 0.96),
		rgba(12, 28, 37, 0.98)
	);
	box-shadow: 0 24px 70px rgba(0, 0, 0, 0.42);
	overflow-y: auto;
	overscroll-behavior: contain;
	touch-action: pan-y;
}

.timeline[hidden] {
	display: none;
}

/* The list's close button (styled like the cards'), staying in the top
   corner as the list scrolls. */
.timeline * {
	overflow: visible;
}

.timeline-note {
	max-width: 71%; /* ends a little short of three quarters across, well short of the events below */
	padding: 0 18px 14px;
	color: rgba(235, 240, 239, 0.55);
	font:
		500 var(--text-body)/1.45 system-ui,
		sans-serif;
	text-align: justify;
	hyphens: auto;
}

.timeline-events {
	display: flex;
	flex-direction: column;
	gap: 6px;
	list-style: none;
}

.timeline-events button {
	display: flex;
	flex-direction: column;
	gap: 8px;
	width: 100%;
	padding: 20px 18px;
	border: 0;
	border-radius: 16px;
	background: none;
	color: white;
	text-align: left;
	cursor: pointer;
	touch-action: pan-y;
}

.timeline-events button:hover {
	background: rgba(255, 255, 255, 0.06);
}

/* The latest event, at or before the clock's time. */
.timeline-events button[aria-current] {
	background: rgba(231, 180, 119, 0.13);
	box-shadow: inset 3px 0 #e7b477;
}

.timeline-event-time {
	color: #e7b477;
	font:
		700 var(--text-eyebrow)/1.2 system-ui,
		sans-serif;
	letter-spacing: 0.14em;
	text-transform: uppercase;
}

.timeline-event-title {
	font:
		600 var(--text-label)/1.3 system-ui,
		sans-serif;
}

.timeline-event-text {
	color: rgba(235, 240, 239, 0.75);
	font:
		500 var(--text-body)/1.5 system-ui,
		sans-serif;
	text-align: justify;
	hyphens: auto;
}

.timeline-event-references {
	color: rgba(231, 180, 119, 0.75);
	font:
		italic 500 var(--text-body)/1.45 Georgia,
		"Times New Roman",
		serif;
}

@media (max-width: 600px) {
	:is(.clock-play, .clock-speed) {
		width: 38px;
		height: 38px;
	}

	/* The time centred at the top, between the buttons on either side. */
	.clock-time {
		position: fixed;
		top: calc(max(var(--edge-gap), env(safe-area-inset-top)) + 19px);
		left: 50%;
		translate: -50% -50%;
	}

	.clock-speed {
		font-size: 13px;
	}

	.clock-play svg {
		width: 18px;
		height: 18px;
	}

	/* The time a little closer to the button than the button is to the edge. */
	.clock-bar {
		gap: 12px;
	}

	/* As far from the bottom and sides as the top buttons are from theirs. */
	.caption {
		bottom: max(var(--edge-gap), env(safe-area-inset-bottom));
		gap: 12px; /* like the time from the pause button */
		width: calc(100vw - 2 * var(--edge-gap));
	}

	.caption-content {
		padding: 12px 12px 14px;
		border-radius: 16px;
	}

	/* Centred on the folded box: its padding (12px + 14px), border and one
	   line of title. */
	.caption-step {
		width: 38px;
		height: 38px;
		margin-bottom: calc(
			(12px + 14px + 2px + var(--text-label) * 1.25 - 38px) / 2
		);
	}

	.caption-step svg {
		width: 22px;
		height: 22px;
	}

	.timeline {
		--pad-top: 16px;
		--pad-right: 8px;
		padding: var(--pad-top) var(--pad-right) 8px;
		border-radius: 20px;
	}

	.timeline-note {
		padding: 0 12px 10px;
	}

	.timeline-events button {
		padding: 16px 12px;
	}
}

/* Live render details: small, quiet monospace rows. */
.settings-details {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 12px 18px;
	color: rgba(235, 240, 239, 0.6);
	font:
		500 0.8rem/1.4 ui-monospace,
		SFMono-Regular,
		Menlo,
		monospace;
}

.settings-details dt {
	color: rgba(235, 240, 239, 0.42);
}

.settings-details dd {
	margin: 0;
	text-align: right;
	overflow-wrap: anywhere;
}

/* The maker's mark, centred at the very bottom of the panel with room above
   it, styled as in the footer of hemm.ing (linking to the portfolio). */
.settings-maker {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 14px;
	/* 40px from the line to the mark, and from the link to the panel's
	   bottom edge (the panel adds 21px of its own below). */
	padding: 37px 0 19px;
	border-top: 1px solid rgba(255, 255, 255, 0.08); /* like the groups' */
}

/* The panel's own padding below is larger on phones. */
@media (max-width: 600px) {
	.settings-maker {
		padding-bottom: 13px;
	}
}

.maker-mark {
	display: inline-block;
	text-decoration: none;
	opacity: 0.4;
	transition: opacity 0.2s;

	&:hover,
	&:focus-visible {
		opacity: 0.8;
	}
}

/* The symbols sit in its top padding, inside its box: placed above the box,
   Safari clipped them away whenever the mark wasn't fading. */
.maker-mark__text {
	position: relative;
	display: block;
	padding-top: 14px;
	color: #f6f6f6;
	font:
		300 18px/1 "Times New Roman",
		serif;
	letter-spacing: 0.1px;
	text-align: center;
	opacity: 0.6;
	transform: scaleX(1.15) scaleY(0.9);
}

.maker-mark__symbols {
	position: absolute;
	top: 0;
	left: 50%;
	display: flex;
	gap: 4px;
	align-items: center;
	justify-content: center;
	font:
		200 9px/9px Arial,
		Helvetica,
		sans-serif;
	opacity: 0.5;
	transform: translateX(-50%) scaleY(1.1);
}

.maker-mark__an {
	display: inline-block;
	font:
		italic 400 14px "Allura",
		cursive;
	transform: scaleX(1) scaleY(1.5) translateY(-1px) rotate(-2.25deg);
}

/* The project's code and README, at the bottom of the panel. */
.settings-link {
	color: #e7b477;
	font:
		600 var(--text-body)/1.4 system-ui,
		sans-serif;
	text-decoration: none;

	&:hover,
	&:focus-visible {
		text-decoration: underline;
		text-underline-offset: 3px;
	}
}

/* Computers: every interface size above at half. They were tuned by eye in
   a browser zoomed to 50%, so at normal (100%) zoom this matches what was
   approved. Phones keep the sizes above (tuned at real size); borders stay
   1px. */
@media (min-width: 601px) {
	.overlay {
		padding: max(12px, env(safe-area-inset-top)) 12px
			max(12px, env(safe-area-inset-bottom));
		-webkit-backdrop-filter: blur(8px) saturate(70%);
		backdrop-filter: blur(8px) saturate(70%);
	}

	.overlay-card {
		width: min(78vw, 150px);
		max-width: calc(100vw - 24px);
		padding: 13.5px 10px 12px;
		border-radius: 10px;
		box-shadow:
			0 12px 35px rgba(0, 0, 0, 0.42),
			inset 0 0.5px rgba(255, 255, 255, 0.08);
	}

	.overlay-actions {
		gap: 5px;
		margin-top: 11px;
	}

	.overlay-button {
		padding: 6px 8px;
		border-radius: 6px;
	}

	.overlay-eyebrow {
		margin-bottom: 4.5px;
	}

	.overlay-title {
		text-shadow: 0 1px 9px rgba(0, 0, 0, 0.55);
	}

	.overlay-copy {
		max-width: 112.5px;
		margin-top: 5px;
	}

	/* The same distances from the screen's edges as on phones. */
	.settings-button {
		width: 38px;
		height: 38px;
	}

	.settings-button svg {
		width: 20px;
		height: 20px;
	}

	.settings {
		inset: calc(max(var(--edge-gap), env(safe-area-inset-top)) + 48px)
			max(var(--edge-gap), env(safe-area-inset-right)) auto auto;
		width: min(440px, calc(100vw - 40px));
		max-height: calc(100dvh - 88px);
		border-radius: 12px;
	}

	.settings-card {
		gap: 19px;
		padding: 23px 22px 21px;
		border-radius: 12px;
		box-shadow:
			0 12px 35px rgba(0, 0, 0, 0.42),
			inset 0 0.5px rgba(255, 255, 255, 0.08);
	}

	.settings-title {
		margin-top: 3px;
	}

	.settings-group {
		gap: 14px;
		padding: 17px 0 0;
	}

	.settings-group legend,
	.settings-group-title {
		margin-bottom: 2px;
	}

	.settings-row {
		gap: 10px;
	}

	.settings-row--stacked {
		gap: 8px;
	}

	.settings-label {
		gap: 4px 8px;
	}

	.settings input[type="range"] {
		height: 16px;
	}

	.settings-scale {
		margin-top: -4px;
	}

	.settings-switch {
		width: 30px;
		height: 18px;
		border-radius: 9px;
	}

	.settings-switch::before {
		top: 1.5px;
		left: 1.5px;
		width: 15px;
		height: 15px;
		box-shadow: 0 0.5px 1.5px rgba(0, 0, 0, 0.35);
	}

	.settings-switch-input:checked + .settings-switch::before {
		translate: 12px 0;
	}

	.settings-choice {
		gap: 2px;
		padding: 2px;
		border-radius: 7px;
	}

	.settings-choice span {
		padding: 5px 0;
		border-radius: 5px;
	}

	.clock-bar {
		gap: 12px;
	}

	:is(.clock-play, .clock-speed) {
		width: 38px;
		height: 38px;
	}

	.clock-speed {
		font-size: 13px;
	}

	.clock-play svg {
		width: 18px;
		height: 18px;
	}

	.clock-time {
		text-shadow:
			0 0.5px 1px rgba(0, 0, 0, 0.5),
			0 1px 8px rgba(0, 0, 0, 0.45);
	}

	.caption {
		bottom: max(12px, env(safe-area-inset-bottom));
		gap: 12px; /* like the time from the pause button */
		width: min(700px, calc(100vw - 32px));
	}

	.caption-content {
		gap: 3px;
		padding: 10px 14px 11px;
		border-radius: 10px;
	}

	/* The same size as the pause and settings buttons. */
	.caption-step {
		width: 38px;
		height: 38px;
		/* Centred on the folded box (10px + 11px padding, border, a line). */
		margin-bottom: calc(
			(10px + 11px + 2px + var(--text-label) * 1.25 - 38px) / 2
		);
	}

	.caption-step svg {
		width: 20px;
		height: 20px;
	}

	.timeline {
		--pad-top: 11px;
		--pad-right: 8px;
		padding: var(--pad-top) var(--pad-right) 8px;
		border-radius: 12px;
		box-shadow: 0 12px 35px rgba(0, 0, 0, 0.42);
	}

	.timeline-note {
		padding: 0 9px 7px;
	}

	.timeline-events {
		gap: 3px;
	}

	.timeline-events button {
		gap: 4px;
		padding: 10px 9px;
		border-radius: 8px;
	}

	.timeline-events button[aria-current] {
		box-shadow: inset 1.5px 0 #e7b477;
	}

	.settings-details {
		gap: 6px 9px;
	}
}

/* Phones: a narrower, shorter cloud frame, with the images baked for it
   (and smaller where the computer ones are bigger). */
@media (max-width: 900px) {
	.storm-cloud {
		--cloud-frame: 124dvh;
		--cloud-offset-x: -15vw;
		top: -12dvh;
		left: -10vw;
		width: 120vw;
	}

	.storm-cloud--far {
		--cloud-image: url("./assets/mobile/clouds/far.avif");
	}

	.storm-cloud--deep {
		--cloud-image: url("./assets/mobile/clouds/deep.avif");
	}

	.storm-cloud--middle {
		--cloud-image: url("./assets/mobile/clouds/middle.avif");
	}

	.storm-cloud--midband {
		--cloud-image: url("./assets/mobile/clouds/midband.avif");
	}

	.storm-cloud--lower {
		--cloud-image: url("./assets/mobile/clouds/lower.avif");
	}

	.storm-cloud--near {
		--cloud-image: url("./assets/mobile/clouds/near.avif");
	}
}

/* Image size. data-assets="small" (phones) or the low quality level swap in
   the half-size images, keeping phones below their GPU and decoded-image
   memory limits. Set in the <head> script before anything loads. */
:root:is([data-assets="small"], [data-quality="low"]) {
	.lightning-layer--back .cloud-lightning-flash {
		--flash-cloud-mask: url("./assets/mobile/storm-cloud-map.avif");
	}

	.lightning-layer--middle .cloud-lightning-flash {
		--flash-cloud-mask: url("./assets/mobile/storm-cloud-alpha.avif");
	}

	.lightning-layer--front .cloud-lightning-flash {
		--flash-cloud-mask: url("./assets/mobile/storm-cloud-cumulus.avif");
	}

	.scenery[data-scenery="far-mountains"] {
		--texture: url("./assets/mobile/far-mountains-v2.avif");
	}

	.scenery[data-scenery="distant-hills"] {
		--texture: url("./assets/mobile/hills-v5.avif");
	}

	.scenery[data-scenery="olives"] {
		--texture: url("./assets/mobile/olives-v3.avif");
	}

	.scenery[data-scenery="distant-jerusalem"] {
		--texture: url("./assets/mobile/jerusalem-v7.avif");
	}

	.scenery[data-scenery="golgotha-ridge"] {
		--texture: url("./assets/mobile/golgotha-ridge-v3.avif");
	}

	.scenery[data-scenery="ground"] {
		--texture: url("./assets/mobile/near-ground-v3.avif");
	}

	.city-light--architecture {
		--light-mask: url("./assets/mobile/city-lights/architecture.webp");
	}

	.city-light--home-1 {
		--light-mask: url("./assets/mobile/city-lights/home-1.webp");
	}

	.city-light--home-2 {
		--light-mask: url("./assets/mobile/city-lights/home-2.webp");
	}

	.city-light--home-3 {
		--light-mask: url("./assets/mobile/city-lights/home-3.webp");
	}

	.city-light--home-4 {
		--light-mask: url("./assets/mobile/city-lights/home-4.webp");
	}

	.city-light--home-5 {
		--light-mask: url("./assets/mobile/city-lights/home-5.webp");
	}

	.city-light--lantern-common {
		--light-mask: url("./assets/mobile/city-lights/lantern-common.webp");
	}

	.city-light--lantern-variant {
		--light-mask: url("./assets/mobile/city-lights/lantern-variant.webp");
	}
}

/* While the connection is being tested, keep the image layers hidden so the
   browser doesn't start downloading images of the wrong size. */
:root[data-probing] :is(#sky, #storm, .scenery) {
	display: none;
}

/* Quality levels (data-quality on <html>): high is the full scene (where
   every device starts), medium is lighter, low drops the most expensive
   layers and glows. Lower levels come only from a slow connection or frame
   rate, little memory, data saver, or a choice in the settings. */
:root:not([data-quality="high"]) {
	.storm-cloud--midband,
	.storm-cloud--lower {
		display: none;
	}

	.overlay {
		-webkit-backdrop-filter: none;
		backdrop-filter: none;
		background:
			radial-gradient(
				circle at 50% 42%,
				rgba(231, 180, 119, 0.11),
				transparent 46%
			),
			rgba(5, 15, 22, 0.82);
	}

	.city-flicker-light {
		will-change: auto;
	}
}

:root[data-quality="low"] {
	.storm-cloud--deep,
	.storm-cloud--middle,
	.city-flicker-plane,
	.land-shadow {
		display: none;
	}

	.lightning-bolt {
		filter: none;
	}
}

/* Markers: a small gold ring over the scene's layers (above the rain and
   storm wash, below the caption and panels). Faint until the mouse comes
   near (teachingMarkers in js/markers.js); hovering one makes it pulse, lights up
   the area around it and shows its title.
   The button is bigger than the ring so it's easy to tap. */
.scene-markers {
	z-index: 70;
	pointer-events: none;
}

/* Just a point where the sky's marker sits (a full-screen box here was a
   full-screen GPU layer for one small ring). */
.scene-markers--sky {
	position: fixed;
	top: 14dvh;
	left: 80vw;
	width: 0;
	height: 0;
}

/* The glow and title reach past each marker (everything clips by default). */
:is(.scene-markers, .christ-markers),
:is(.scene-markers, .christ-markers) * {
	overflow: visible;
}

.marker {
	position: absolute;
	width: 36px;
	height: 36px;
	margin: -18px 0 0 -18px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: none;
	/* Faint, brightening as the mouse comes near (--near, from js/markers.js). */
	opacity: calc(0.3 + 0.7 * var(--near, 0));
	cursor: pointer;
	pointer-events: auto;
	touch-action: manipulation;
	transition: opacity 150ms ease;
}

.marker:hover,
.marker:focus-visible,
.marker[aria-expanded="true"] {
	opacity: 1;
}

/* Touch screens can't come near first: they light up for a few seconds when
   the scene first shows and after each tap, then dim (js/markers.js). */
@media (hover: none) {
	.marker {
		transition: opacity 800ms ease;
	}

	:root[data-markers-lit] .marker {
		opacity: 1;
		transition-duration: 200ms;
	}
}

.marker::before,
.marker::after {
	position: absolute;
	inset: 9px;
	content: "";
	border-radius: 50%;
}

.marker::before {
	border: 2px solid #e7b477;
	background: radial-gradient(
		circle,
		#f3d3a4 0 3px,
		rgba(12, 28, 37, 0.6) 3.5px
	);
	box-shadow:
		0 0 0 1px rgba(0, 0, 0, 0.35),
		0 0 10px rgba(231, 180, 119, 0.45);
	transition:
		scale 200ms ease,
		box-shadow 300ms ease;
}

/* The pulse, while hovered or open. */
.marker::after {
	border: 1.5px solid rgba(231, 180, 119, 0.8);
	opacity: 0;
}

.marker:is(:hover, :focus-visible, [aria-expanded="true"])::after {
	animation: marker-pulse 2.6s ease-out infinite;
}

@keyframes marker-pulse {
	from {
		opacity: 0.9;
		transform: scale(1);
	}
	to {
		opacity: 0;
		transform: scale(2.2);
	}
}

/* Hovered or open: a soft golden light over the part it's about. */
.marker:hover::before,
.marker:focus-visible::before,
.marker[aria-expanded="true"]::before {
	scale: 1.2;
	box-shadow:
		0 0 0 1px rgba(0, 0, 0, 0.35),
		0 0 36px 24px rgba(231, 180, 119, 0.3);
}

.marker:focus-visible {
	outline: 2px solid #e7b477;
	outline-offset: 2px;
}

/* The hovered marker (and its title) above the others, and its layer above
   the other layers'. */
.marker:is(:hover, :focus-visible) {
	z-index: 1;
}

:is(.scene-markers, .christ-markers):has(.marker:is(:hover, :focus-visible)) {
	z-index: 71;
}

/* Its title, above it while hovered. */
.marker-label {
	position: absolute;
	bottom: calc(100% + 4px);
	left: 50%;
	padding: 5px 10px;
	border: 1px solid rgba(231, 180, 119, 0.45);
	border-radius: 999px;
	background: rgba(8, 16, 22, 0.88);
	color: white;
	font:
		600 var(--text-eyebrow)/1.2 system-ui,
		sans-serif;
	letter-spacing: 0.02em;
	white-space: nowrap;
	opacity: 0;
	translate: calc(-50% + var(--nudge, 0px)) 4px; /* --nudge: js/markers.js */
	pointer-events: none;
	transition:
		opacity 150ms ease,
		translate 150ms ease;
}

.marker:is(:hover, :focus-visible):not([aria-expanded="true"]) .marker-label {
	opacity: 1;
	translate: calc(-50% + var(--nudge, 0px)) 0;
}

@media (prefers-reduced-motion: reduce) {
	.marker::after {
		animation: none !important;
	}
}

/* The card a marker opens: placed beside it by teachingMarkers in js/markers.js. */
.teaching {
	box-sizing: border-box; /* its max-height includes the border */
	inset: auto;
	width: min(400px, calc(100vw - 40px));
	max-height: calc(100dvh - 40px);
	margin: 0;
	padding: 0;
	overflow: visible; /* for its pointer */
	border: 1px solid rgba(255, 255, 255, 0.12);
	border-radius: 10px;
	background: #0a1218; /* solid, so its pointer blends into it */
	box-shadow: 0 12px 35px rgba(0, 0, 0, 0.45);
	color: white;
}

.teaching:popover-open {
	display: flex;
	flex-direction: column;
}

/* Its contents scroll inside it. */
.teaching-body {
	min-height: 0;
	--pad-top: 18px;
	--pad-right: 20px;
	padding: var(--pad-top) var(--pad-right) 20px;
	overflow-y: auto;
	overscroll-behavior: contain;
	border-radius: inherit;
}

/* A pointer from the card's side to its marker: a square in the card's
   colour, turned 45deg and centred on the card's edge. Its inner half covers
   that stretch of the card's border and only its two outer sides have one,
   so a single outline runs round the card and the point. js/markers.js sets the
   side and --pointer-y. */
.teaching[data-pointer]::before {
	position: absolute;
	top: calc(var(--pointer-y) - 11px);
	width: 22px;
	height: 22px;
	content: "";
	box-sizing: border-box;
	background: #0a1218;
	rotate: 45deg;
}

.teaching[data-pointer="left"]::before {
	left: -12px;
	border-bottom: 1px solid rgba(255, 255, 255, 0.12);
	border-left: 1px solid rgba(255, 255, 255, 0.12);
}

.teaching[data-pointer="right"]::before {
	right: -12px;
	border-top: 1px solid rgba(255, 255, 255, 0.12);
	border-right: 1px solid rgba(255, 255, 255, 0.12);
}

.teaching--wide {
	width: min(560px, calc(100vw - 40px));
}

.teaching-close {
	float: right;
	display: grid;
	place-items: center;
	width: 36px;
	height: 36px;
	/* Centred on a card's title line (15px down), and as far in from the
	   right, in the cards and the list alike, staying there as they scroll.
	   Sticky is measured inside the box's padding, so that comes off. */
	--close-top: 15px;
	--close-right: 15px;
	position: sticky;
	top: calc(var(--close-top) - var(--pad-top));
	z-index: 1;
	/* Text beside it keeps the same gap as the button keeps from the edge. */
	margin: calc(var(--close-top) - var(--pad-top))
		calc(var(--close-right) - var(--pad-right)) 0 var(--close-right);
	padding: 0;
	border-radius: 50%;
	color: #e7b477;
	cursor: pointer;
}

.teaching-close svg {
	width: 20px;
	height: 20px;
	fill: none;
	stroke: currentColor;
	stroke-width: 2;
	stroke-linecap: round;
}

/* A close-up (the sign), at the top of the card. */
.teaching-picture {
	clear: right; /* below the close button */
	/* Padding, not margin: clearing the button swallows a top margin. */
	margin: 0 0 16px;
	padding-top: 14px;
	text-align: center;
}

.teaching-picture img {
	display: block;
	width: 100%;
	height: auto;
	border-radius: 6px;
}

.teaching-picture figcaption {
	margin-top: 10px;
	color: rgba(235, 240, 239, 0.7);
	font:
		italic 500 var(--text-body)/1.4 system-ui,
		sans-serif;
}

.teaching-title {
	/* Its line is centred on the close button; the room under it sets the
	   text low enough that the button looks centred between the edge and
	   the text. */
	margin: 2px 0 16px;
	font:
		500 calc(var(--text-title) * 0.72) / 1.2 Georgia,
		"Times New Roman",
		serif;
}

.teaching-text {
	margin: 0 0 16px;
	color: rgba(235, 240, 239, 0.85);
	font:
		500 var(--text-body)/1.5 system-ui,
		sans-serif;
	text-align: justify;
	hyphens: auto;
}

/* Each scripture like the loading screen's verse: the words, then the
   reference in gold. */
.teaching-scriptures {
	display: grid;
	gap: 14px;
	padding-top: 14px;
	border-top: 1px solid rgba(255, 255, 255, 0.1);
}

.teaching-scripture {
	margin: 0;
	color: rgba(235, 240, 239, 0.9);
	font:
		italic 500 var(--text-body)/1.5 system-ui,
		sans-serif;
}

.teaching-scripture cite {
	display: block;
	margin-top: 6px;
	color: #e7b477;
	font:
		700 var(--text-eyebrow)/1.2 system-ui,
		sans-serif;
	font-style: normal;
	letter-spacing: 0.14em;
	text-transform: uppercase;
}

/* Phones: across the screen, under the time. */
@media (max-width: 600px) {
	.teaching {
		top: calc(max(var(--edge-gap), env(safe-area-inset-top)) + 54px);
		left: 20px;
		width: auto;
		right: 20px;
		max-height: calc(
			100dvh - max(var(--edge-gap), env(safe-area-inset-top)) - 74px
		);
		border-radius: 16px;
	}

	/* Across the screen, not beside the marker. */
	.teaching[data-pointer]::before {
		display: none;
	}
}

/* Every round button looks alike: the same dark glass, edge and hover (a
   little darker, with a gold edge). Icons are gold, except pause/play's. */
:is(
	.settings-button,
	.sound-toggle,
	.clock-play,
	.clock-speed,
	.caption-step,
	.teaching-close
) {
	border: 1px solid rgba(255, 255, 255, 0.16);
	background: rgba(12, 28, 37, 0.55);
	-webkit-backdrop-filter: blur(10px);
	backdrop-filter: blur(10px);
	transition:
		background-color 150ms ease,
		border-color 150ms ease;
}

:is(
		.settings-button,
		.sound-toggle,
		.clock-play,
		.clock-speed,
		.caption-step,
		.teaching-close
	):hover:not(:disabled) {
	border-color: rgba(231, 180, 119, 0.45);
	background: rgba(12, 28, 37, 0.72);
}

@media (min-width: 601px) {
	:is(
		.settings-button,
		.sound-toggle,
		.clock-play,
		.clock-speed,
		.caption-step,
		.teaching-close
	) {
		-webkit-backdrop-filter: blur(5px);
		backdrop-filter: blur(5px);
	}
}

/* Switched off when fully faded (applyTimeOfDay in js/time-and-weather.js). */
:root[data-no-stars] #sky .stars,
:root[data-no-city-lights] :is(.city-lights, .city-flicker-plane) {
	display: none;
}
