/* Image loader ("Moonlight") for image tools. See inc/image-loader.php.
 *
 * assets/js/image-loader.js adds one .ws-img-loader inside AI Engine's reply box while the
 * form runs. While waiting it sits in the box's flow, in the coming image's shape
 * (--ws-il-ratio). When the image has loaded, it moves over the image (.is-over) for the
 * reveal, then the script removes it. data-state is "waiting" or "arriving".
 *
 * Everything is black and white and made of gradients. The speck layer at 5% only stops the
 * dark gradients from banding; it shouldn't read as grain. The label comes from CSS content,
 * so the loader adds no text to the reply (inc/form-loader.php's script reads that text). */

@property --ws-il-a { syntax: "<angle>"; inherits: false; initial-value: 0deg; }

body.ws-dark .mwai-form-output-container .mwai-form-output:has(> .ws-img-loader) { position: relative; }
/* AI Engine's blinking cursor and the half-loaded image wait out of sight under the loader. */
body.ws-dark .mwai-form-output-container .mwai-form-output:has(> .ws-img-loader) > span[style*="border-left"] { display: none; }
body.ws-dark .mwai-form-output-container .mwai-form-output:has(> .ws-img-loader[data-state="waiting"]) img {
	position: absolute;
	opacity: 0;
	pointer-events: none;
}
/* The Download button comes in after the reveal, not during it. */
body.ws-dark .mwai-form-output-container .mwai-form-output:has(> .ws-img-loader) a.ws-img-download.ws-img-download.ws-img-download { opacity: 0; }

body.ws-dark .ws-img-loader {
	--dev: 0; /* 0 → 1 over the wait, set by the script */
	position: relative;
	display: block;
	width: 100%;
	aspect-ratio: var(--ws-il-ratio, 1);
	border-radius: 15px;
	overflow: hidden;
	isolation: isolate;
	contain: paint;
	pointer-events: none;
}
body.ws-dark .ws-img-loader.is-over { position: absolute; z-index: 3; aspect-ratio: auto; }
body.ws-dark .ws-img-loader > * { position: absolute; border-radius: inherit; }

body.ws-dark .ws-il-bg { inset: 0; background: radial-gradient(ellipse at 50% 38%, #141416, #040405 85%); }

/* Soft white light drifting on black, brightening over the wait. */
body.ws-dark .ws-il-light {
	border-radius: 50%;
	mix-blend-mode: screen;
	opacity: calc(0.35 + 0.65 * var(--dev));
	transition: opacity 600ms linear;
	will-change: transform;
}
body.ws-dark .ws-il-l1 { width: 95%; aspect-ratio: 1; top: -34%; left: -28%; background: radial-gradient(closest-side, rgb(255 255 255 / 0.2), rgb(255 255 255 / 0.06) 58%, transparent); animation: ws-il-drift1 19s ease-in-out infinite alternate; }
body.ws-dark .ws-il-l2 { width: 105%; aspect-ratio: 1; bottom: -48%; right: -38%; background: radial-gradient(closest-side, rgb(255 255 255 / 0.16), rgb(255 255 255 / 0.05) 58%, transparent); animation: ws-il-drift2 23s ease-in-out infinite alternate; }
body.ws-dark .ws-il-l3 { width: 58%; aspect-ratio: 1; top: 22%; left: 28%; background: radial-gradient(closest-side, rgb(255 255 255 / 0.26), rgb(255 255 255 / 0.07) 52%, transparent); animation: ws-il-drift3 15s ease-in-out infinite alternate; }
@keyframes ws-il-drift1 { 0% { transform: translate(0, 0) scale(1); } 50% { transform: translate(22%, 14%) scale(1.12); } 100% { transform: translate(8%, 30%) scale(0.94); } }
@keyframes ws-il-drift2 { 0% { transform: translate(0, 0) scale(1.05); } 50% { transform: translate(-18%, -12%) scale(0.92); } 100% { transform: translate(-30%, 4%) scale(1.1); } }
@keyframes ws-il-drift3 { 0% { transform: translate(0, 0) scale(0.9); } 50% { transform: translate(-40%, -24%) scale(1.15); } 100% { transform: translate(-12%, 30%) scale(1); } }

body.ws-dark .ws-il-specks { inset: 0; background: var(--ws-il-specks) 0 0 / 220px 220px; opacity: 0.05; }

/* A white light tracing the frame's edge, with a soft glow. The glow is a thin ring blurred
 * after it's shaped, so it fades both ways instead of stopping at a hard inner edge. */
body.ws-dark .ws-il-edge,
body.ws-dark .ws-il-glow > span {
	inset: 0;
	border-radius: inherit;
	background: conic-gradient(from var(--ws-il-a), transparent 0 56%, rgb(255 255 255 / 0.18) 68%, rgb(255 255 255) 81%, rgb(255 255 255 / 0.18) 90%, transparent 96%);
	-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
	-webkit-mask-composite: xor;
	mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
	animation: ws-il-orbit 3.6s linear infinite;
}
body.ws-dark .ws-il-edge { padding: 1.5px; z-index: 2; }
body.ws-dark .ws-il-glow { inset: 0; filter: blur(10px); opacity: 0.85; }
body.ws-dark .ws-il-glow > span { position: absolute; padding: 4px; }
@keyframes ws-il-orbit { to { --ws-il-a: 360deg; } }

/* Label: the words shimmer, as on the text loader, with a step line under them. */
body.ws-dark .ws-il-label { z-index: 4; top: 22px; left: 24px; right: 24px; border-radius: 0; }
body.ws-dark .ws-il-label::before {
	content: "Creating your image…";
	display: inline-block;
	font: 600 15px/1.3 var(--ws-font-sans);
	color: transparent;
	background: linear-gradient(90deg, var(--ws-gray-400) 0 40%, var(--ws-white) 50%, var(--ws-gray-400) 60% 100%) 0 0 / 250% 100%;
	-webkit-background-clip: text;
	background-clip: text;
	animation: ws-il-shimmer 1.8s linear infinite;
}
body.ws-dark .ws-il-label::after {
	content: attr(data-step);
	display: block;
	margin-top: 4px;
	font: 600 13px/1.4 var(--ws-font-sans);
	color: var(--ws-gray-300);
	transition: opacity 160ms ease-in, transform 260ms var(--ws-ease-out);
}
body.ws-dark .ws-il-label.is-swapping::after { opacity: 0; transform: translateY(-4px); }
@keyframes ws-il-shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }

/* The reveal: the picture settles in from a slight zoom and blur while the loader fades and
 * the edge light runs one fast lap. The clip keeps the zoomed picture inside its own frame. */
body.ws-dark .ws-img-loader[data-state="arriving"] :is(.ws-il-bg, .ws-il-light, .ws-il-specks) { animation: ws-il-fade 900ms ease 200ms forwards; }
body.ws-dark .ws-img-loader[data-state="arriving"] .ws-il-label { animation: ws-il-fade 250ms ease forwards; }
body.ws-dark .ws-img-loader[data-state="arriving"] .ws-il-edge { animation: ws-il-orbit 0.9s linear infinite, ws-il-fade 600ms ease 900ms forwards; }
body.ws-dark .ws-img-loader[data-state="arriving"] .ws-il-glow { animation: ws-il-fade 600ms ease 900ms forwards; }
body.ws-dark .ws-img-loader[data-state="arriving"] .ws-il-glow > span { animation: ws-il-orbit 0.9s linear infinite; }
body.ws-dark .mwai-form-output-container .mwai-form-output:has(> .ws-img-loader[data-state="arriving"]) img {
	animation: ws-il-settle 1500ms var(--ws-ease-out) both;
}
@keyframes ws-il-settle {
	0%   { opacity: 0; filter: blur(30px) brightness(1.2); transform: scale(1.06); clip-path: inset(2.83% round 16px); }
	40%  { opacity: 1; }
	100% { opacity: 1; filter: blur(0) brightness(1); transform: none; clip-path: inset(0 round 15px); }
}
@keyframes ws-il-fade { to { opacity: 0; } }

/* Still while off screen or in a hidden tab. */
body.ws-dark .ws-img-loader.is-paused *,
body.ws-dark .ws-img-loader.is-paused .ws-il-label::before { animation-play-state: paused; }

@media (max-width: 767px) {
	body.ws-dark .ws-il-label { top: 16px; left: 18px; right: 18px; }
}

/* Calmer for visitors who turn motion off: no drift, no orbit, no shimmer, and the picture
 * fades in without the zoom. The label and the steps stay. */
@media (prefers-reduced-motion: reduce) {
	body.ws-dark .ws-il-light,
	body.ws-dark .ws-il-edge,
	body.ws-dark .ws-il-glow > span,
	body.ws-dark .ws-il-label::before { animation: none; }
	body.ws-dark .ws-il-label::before { color: var(--ws-gray-300); background: none; }
	body.ws-dark .ws-il-label::after { transition: none; }
	body.ws-dark .ws-img-loader[data-state="arriving"] > * { animation: ws-il-fade 400ms ease forwards; }
	body.ws-dark .mwai-form-output-container .mwai-form-output:has(> .ws-img-loader[data-state="arriving"]) img { animation: ws-il-appear 400ms ease both; }
}
@keyframes ws-il-appear { from { opacity: 0; } to { opacity: 1; } }
