/*
 * Prototype: the nebula scaffold's paint, draft v16.
 *
 * `scaffold-nebula.js` builds one `.lvi-nebula` overlay above each image
 * whose file has not arrived, and copies the image's `--lvi-glow` and
 * `--lvi-glow2` channels onto it. Two systems orbit the same way, each an
 * atmosphere and a hot core breathing in anti-phase, shattered by a noise
 * mask. The ground is opaque, so nothing beneath shows until release.
 *
 * Release is the draft's own: the ground leaves over 2 seconds and the
 * lights and grain over 4, while the loaded image stands beneath them.
 *
 * The grain is the script's generated noise, seeded per cover and carried
 * as a data URI, so nothing is fetched for it.
 */

.lvi-nebula-holder {
	position: relative;
	display: block;
	/* The blurred image's soft edge bleeds past its box; the holder clips
	   it to the frame, and the wait state scales the image slightly so the
	   soft edge falls outside. The script removes this class when the
	   release has run, so the hover glow's shadow is not clipped later. */
	overflow: hidden;
}

.lvi-nebula {
	position: absolute;
	inset: 0;
	overflow: hidden;
	pointer-events: none;
	z-index: 2;
	/* One shared shatter mask; the draft seeded one per card, which a later
	   pass can restore by generating the URI per image. */
	--lvi-nebula-mask: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%22256%22 height=%22256%22%3E%3Cfilter id=%22m%22 x=%220%22 y=%220%22 width=%22100%25%22 height=%22100%25%22%3E%3CfeTurbulence type=%22fractalNoise%22 baseFrequency=%220.00390625%22 numOctaves=%223%22 stitchTiles=%22stitch%22 seed=%2264%22/%3E%3CfeColorMatrix type=%22matrix%22 values=%220 0 0 0 1 0 0 0 0 1 0 0 0 0 1 3.5 0 0 0 -1%22/%3E%3C/filter%3E%3Crect width=%22256%22 height=%22256%22 filter=%22url(%23m)%22/%3E%3C/svg%3E');
}

.lvi-nebula__ground {
	position: absolute;
	inset: 0;
	background: #131313;
	transition: opacity 2s ease;
}

/* The grain stands over the lights, as the draft stacked it. Its image is
   the script's generated noise, seeded per cover. */
.lvi-nebula__grain {
	position: absolute;
	inset: 0;
	background-repeat: repeat;
	background-size: 128px 128px;
	mix-blend-mode: overlay;
	opacity: 0.75;
	z-index: 4;
	/* The grain leaves on the image's own two seconds; the lights keep
	   their four. */
	transition: opacity 2s ease;
}

.lvi-nebula__rig {
	position: absolute;
	inset: 0;
	z-index: 3;
	transition: opacity 4s ease;
	-webkit-mask-image: var(--lvi-nebula-mask);
	mask-image: var(--lvi-nebula-mask);
	-webkit-mask-size: 256px 256px;
	mask-size: 256px 256px;
	animation: lvi-nebula-drift 90s linear infinite;
}

.lvi-nebula__spinner {
	position: absolute;
	inset: 0;
	animation: lvi-nebula-spin 9.1s linear infinite;
	animation-delay: var(--lvi-spin-delay, 0s);
}

.lvi-nebula__spinner--alt {
	animation-name: lvi-nebula-spin-alt;
	opacity: 0.7;
}

.lvi-nebula__blob {
	position: absolute;
	top: 15%;
	left: 50%;
	width: 540px;
	height: 360px;
	margin: -180px 0 0 -270px;
	border-radius: 50%;
	background: radial-gradient(
		ellipse,
		rgba(var(--lvi-glow, 230, 230, 230), 0.52) 0%,
		rgba(var(--lvi-glow, 230, 230, 230), 0.16) 40%,
		rgba(var(--lvi-glow, 230, 230, 230), 0) 75%
	);
	filter: blur(14px);
	mix-blend-mode: screen;
	animation: lvi-nebula-blob 4s ease-in-out infinite alternate;
}

.lvi-nebula__core {
	position: absolute;
	top: 15%;
	left: 50%;
	width: 80px;
	height: 80px;
	margin: -40px 0 0 -40px;
	border-radius: 50%;
	background: radial-gradient(
		circle,
		rgba(255, 255, 255, 0.65) 0%,
		rgba(var(--lvi-glow, 230, 230, 230), 0.65) 30%,
		rgba(var(--lvi-glow, 230, 230, 230), 0) 70%
	);
	filter: blur(8px);
	mix-blend-mode: screen;
	animation: lvi-nebula-core 4s ease-in-out infinite alternate;
}

/* The second system reads the secondary glow, half a cycle out of phase. */
.lvi-nebula__spinner--alt .lvi-nebula__blob,
.lvi-nebula__spinner--alt .lvi-nebula__core {
	--lvi-glow: var(--lvi-glow2, 230, 230, 230);
	animation-delay: -4s;
}

@keyframes lvi-nebula-spin {
	0% { transform: rotate(0deg); }
	100% { transform: rotate(360deg); }
}

/* The alt starts opposite and orbits the same way, tighter and inside. */
@keyframes lvi-nebula-spin-alt {
	0% { transform: scale(0.85) rotate(180deg); }
	100% { transform: scale(0.85) rotate(540deg); }
}

@keyframes lvi-nebula-blob {
	0% { transform: scale(0.8) rotate(-10deg); opacity: 0.1; }
	100% { transform: scale(1.15) rotate(10deg); opacity: 1; }
}

@keyframes lvi-nebula-core {
	0% { transform: scale(1.2); opacity: 1; }
	100% { transform: scale(0.5); opacity: 0.15; }
}

@keyframes lvi-nebula-drift {
	0% { -webkit-mask-position: 0 0; mask-position: 0 0; }
	100% { -webkit-mask-position: 256px 256px; mask-position: 256px 256px; }
}

.lvi-nebula__crop {
	position: absolute;
	width: 12px;
	height: 12px;
	z-index: 5;
	transition: opacity 0.5s ease;
}
.lvi-nebula__crop::before,
.lvi-nebula__crop::after {
	content: "";
	position: absolute;
	background: rgba(230, 230, 230, 0.15);
}
.lvi-nebula__crop--tl { left: 9px; top: 9px; }
.lvi-nebula__crop--tr { right: 9px; top: 9px; }
.lvi-nebula__crop--bl { left: 9px; bottom: 9px; }
.lvi-nebula__crop--br { right: 9px; bottom: 9px; }
/* The horizontal strokes belong to the right corners alone; the left edge
   carries the index text instead, as the draft drew it. */
.lvi-nebula__crop--tr::before,
.lvi-nebula__crop--br::before { right: 0; width: 12px; height: 1px; }
.lvi-nebula__crop--tr::before { top: 0; }
.lvi-nebula__crop--br::before { bottom: 0; }
.lvi-nebula__crop--tl::after,
.lvi-nebula__crop--tr::after { top: 0; height: 12px; width: 1px; }
.lvi-nebula__crop--bl::after,
.lvi-nebula__crop--br::after { bottom: 0; height: 12px; width: 1px; }
.lvi-nebula__crop--tl::after,
.lvi-nebula__crop--bl::after { left: 0; }
.lvi-nebula__crop--tr::after,
.lvi-nebula__crop--br::after { right: 0; }

.lvi-nebula__index {
	position: absolute;
	top: 9px;
	left: 16px;
	font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
	font-size: 11px;
	line-height: 14px;
	letter-spacing: 0.05em;
	color: rgb(255 255 255 / 20%);
	z-index: 6;
	transition: opacity 0.5s ease;
}

/* A small frame carries no corners, and its index sits on the vertical
   centre rather than hanging from the top edge. */
.lvi-nebula--small .lvi-nebula__index {
	top: 50%;
	transform: translateY(-50%);
}

.lvi-nebula--done .lvi-nebula__ground,
.lvi-nebula--done .lvi-nebula__grain,
.lvi-nebula--done .lvi-nebula__rig,
.lvi-nebula--done .lvi-nebula__crop,
.lvi-nebula--done .lvi-nebula__index {
	opacity: 0;
}

/* The image beneath the scaffold arrives blurred and out of view, and
   clears as it fades in, on the ground's own two seconds. The holder is in
   the selector for weight: the works grid states a filter and a transition
   of its own on `.work__image img`, and an equal selector loses to it. */
.lvi-nebula-holder img.lvi-nebula-wait {
	opacity: 0;
	filter: blur(14px);
	transform: scale(1.04);
	transition: opacity 2s ease, filter 2s ease, transform 2s ease;
}

.lvi-nebula-holder img.lvi-nebula-wait.lvi-nebula-in {
	opacity: 1;
	filter: blur(0);
	transform: none;
}

/* The wait state is entered with this beside it, and the script removes it
   after one reflow: without the snap, the class lands after first paint
   and the browser animates into the blur instead of starting there. */
img.lvi-nebula-snap {
	transition: none !important;
}


@media (prefers-reduced-motion: reduce) {
	.lvi-nebula__rig,
	.lvi-nebula__spinner,
	.lvi-nebula__blob,
	.lvi-nebula__core {
		animation: none;
	}
}
