/**
 * Home page intro loader.
 *
 * A fixed overlay shown once per session on the front page: a dotted gold globe
 * drawn on <canvas> by assets/js/page-loader.js. The screen holds nothing else
 * but the globe, its orbit, the particles and one tiny pulsing gold dot — no
 * text, no counters. The markup is printed by header.php, the enqueue lives in
 * functions.php.
 *
 * Everything tunable — palette, timing, easing, globe size — is a token in the
 * :root block below.
 */

:root {
	/* Palette */
	--dm-loader-bg: #0a0c0c;
	--dm-loader-gold: #c9a96e;
	--dm-loader-gold-light: #f5e6b8;
	--dm-loader-gold-deep: #8a6d3b;

	/* Easing */
	--dm-loader-ease: cubic-bezier(0.22, 1, 0.36, 1);

	/* Timing */
	--dm-loader-globe-ms: 600ms;
	--dm-loader-exit-zoom-ms: 700ms;
	--dm-loader-exit-zoom-scale: 2.4;
	--dm-loader-exit-zoom-ease: cubic-bezier(0.4, 0, 0.6, 1);
	--dm-loader-exit-fade-ms: 300ms;
	--dm-loader-exit-iris-ms: 600ms;
	--dm-loader-exit-iris-delay: 350ms;
	--dm-loader-pulse-ms: 1900ms;
	--dm-loader-pulse-ease: ease-in-out;

	/* Geometry */
	--dm-loader-globe: clamp(260px, 55vmin, 620px);
	--dm-loader-dot-size: 4px;
	--dm-loader-dot-glow: 14px;
	--dm-loader-z: 9999;
}

/* Scroll is locked from the first paint (see the guard in header.php). */
html.dm-intro-active {
	overflow: hidden;
}

/* The front page always scrolls, so reserving the gutter up front means
   suppressing the scrollbar during the intro cannot shift the layout. This
   stylesheet only loads on the front page. */
html {
	scrollbar-gutter: stable;
}

/* Repeat visit in the same session: the loader is never painted at all. */
html.dm-intro-skip .dm-loader {
	display: none;
}

.dm-loader {
	position: fixed;
	inset: 0;
	z-index: var(--dm-loader-z);
	display: grid;
	place-items: center;
	overflow: hidden;
	background: var(--dm-loader-bg);
	/* Exit: the overlay iris-closes on its own centre. */
	clip-path: circle(150% at 50% 50%);
	transition: clip-path var(--dm-loader-exit-iris-ms) var(--dm-loader-ease) var(--dm-loader-exit-iris-delay);
	will-change: clip-path;
}

.dm-loader--exit {
	clip-path: circle(0% at 50% 50%);
	/* The overlay stays mounted for the length of the exit; it must not swallow
	   clicks on the hero that is being revealed underneath it. */
	pointer-events: none;
}

/* Soft vignette. */
.dm-loader__vignette {
	position: absolute;
	inset: 0;
	background: radial-gradient(circle at 50% 48%, rgb(10 12 12 / 0%) 34%, rgb(0 0 0 / 62%) 100%);
}

/* Gold flare drifting behind the globe. */
.dm-loader__flare {
	position: absolute;
	top: 50%;
	left: 50%;
	width: 96vmin;
	height: 96vmin;
	margin: -48vmin 0 0 -48vmin;
	background: radial-gradient(circle, rgb(201 169 110 / 20%) 0%, rgb(201 169 110 / 7%) 42%, rgb(201 169 110 / 0%) 68%);
	filter: blur(22px);
	animation: dm-loader-flare 46s linear infinite;
}

/* Very faint film grain. */
.dm-loader__grain {
	position: absolute;
	inset: -60%;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
	background-size: 180px 180px;
	opacity: 0.05;
	mix-blend-mode: overlay;
}

.dm-loader__inner {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
}

/* Entry: the whole scene fades in and eases up to full scale. */
.dm-loader__stage {
	width: calc(var(--dm-loader-globe) * 1.42);
	aspect-ratio: 1 / 1;
	opacity: 0;
	transform: scale(0.92);
	animation: dm-loader-globe-in var(--dm-loader-globe-ms) var(--dm-loader-ease) forwards;
}

/* Exit: the globe dives at the viewer and brightens, so the iris that follows
   closes into the globe itself. Kept on a child so it cannot fight the entry
   animation's fill state. */
.dm-loader__scene {
	width: 100%;
	height: 100%;
	transform: scale(1);
	filter: brightness(1);
	transition:
		transform var(--dm-loader-exit-zoom-ms) var(--dm-loader-exit-zoom-ease),
		filter var(--dm-loader-exit-zoom-ms) var(--dm-loader-exit-zoom-ease);
}

.dm-loader--exit .dm-loader__scene {
	transform: scale(var(--dm-loader-exit-zoom-scale));
	filter: brightness(1.85) saturate(1.08);
}

.dm-loader__globe {
	display: block !important; /* style.css hides every canvas; this one is ours. */
	width: 100%;
	height: 100%;
}

/* Single indicator: a tiny gold dot under the sphere, pulsing until the page
   is ready. It is taken out of flow so the globe stays dead centre — the stage
   is taller than the sphere because it also holds the halo, so the sphere's
   bottom edge sits 0.21 of the globe's diameter above the stage box. */
.dm-loader__dot {
	position: absolute;
	top: calc(100% - var(--dm-loader-globe) * 0.21 + clamp(18px, 3vmin, 34px));
	left: 50%;
	width: var(--dm-loader-dot-size);
	height: var(--dm-loader-dot-size);
	margin-left: calc(var(--dm-loader-dot-size) / -2);
	border-radius: 50%;
	background: var(--dm-loader-gold);
	box-shadow: 0 0 var(--dm-loader-dot-glow) rgb(201 169 110 / 55%);
	animation: dm-loader-pulse var(--dm-loader-pulse-ms) var(--dm-loader-pulse-ease) infinite;
	transition:
		opacity var(--dm-loader-exit-fade-ms) var(--dm-loader-ease),
		transform var(--dm-loader-exit-fade-ms) var(--dm-loader-ease);
}

/* The pulse owns opacity and transform, so it has to stop before the dot can
   fade out with the rest of the overlay. */
.dm-loader--exit .dm-loader__dot {
	animation: none;
	opacity: 0;
	transform: scale(0.7);
}

/* Visually hidden label; the loader itself announces as a status region. */
.dm-loader__label {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

@keyframes dm-loader-globe-in {
	from {
		opacity: 0;
		transform: scale(0.92);
	}

	to {
		opacity: 1;
		transform: scale(1);
	}
}

@keyframes dm-loader-pulse {
	0%,
	100% {
		opacity: 0.3;
		transform: scale(0.8);
	}

	50% {
		opacity: 1;
		transform: scale(1.15);
	}
}

@keyframes dm-loader-flare {
	from {
		transform: rotate(0deg) translateX(6vmin) rotate(0deg);
	}

	to {
		transform: rotate(360deg) translateX(6vmin) rotate(-360deg);
	}
}

/* Reduced motion: static globe, no spin, no pulse, plain fade out. */
@media (prefers-reduced-motion: reduce) {
	.dm-loader__stage,
	.dm-loader__flare,
	.dm-loader__grain,
	.dm-loader__dot {
		animation: none;
	}

	.dm-loader__stage {
		opacity: 1;
		transform: none;
	}

	.dm-loader__dot {
		opacity: 1;
	}

	.dm-loader {
		transition: opacity 400ms ease;
	}

	.dm-loader--exit {
		clip-path: none;
		opacity: 0;
	}

	/* Fade only: no scale on the way out, and no zoom on the scene. */
	.dm-loader--exit .dm-loader__dot {
		transform: none;
	}

	.dm-loader--exit .dm-loader__scene {
		transform: none;
		filter: none;
	}
}

/* Landscape phones are too short for the halo plus the dot; tighten both. */
@media (max-height: 480px) {
	:root {
		--dm-loader-globe: clamp(150px, 40vh, 260px);
	}
}
