/**
 * Product 3D viewer (the "3D-Ansicht" gallery slide).
 *
 * Companion to inc/product-3d.php + assets/js/product-3d*.js. Loaded only
 * on single-product pages whose product has a STEP download. The slide
 * inherits the white stage that shop.css gives every
 * .woocommerce-product-gallery__image; the visible hairline frame lives
 * on the surrounding .shop-product__media card, never on slides. Rules
 * here cover the parts WC knows nothing about: poster, progress, error
 * and the canvas itself.
 */

/* ============================================================
 * Stage. Fixed square aspect so Flexslider's smoothHeight
 * doesn't jump between the photos and the viewer.
 * ============================================================ */

.emo-3d-slide__stage {
	position: relative;
	/* width, NOT just the ratio. With aspect-ratio plus a max-height and
	 * no explicit width, capping the height also caps the WIDTH: past a
	 * 560px column the stage settled at 560 by 560 inside a 777px wide
	 * slide, so the model sat hard left with 217px of dead space beside
	 * it while the photo slides filled the full width. */
	width: 100%;
	/* Fallback only: inc/product-3d.php inlines the featured photo's real
	 * ratio so every slide measures the same height for smoothHeight. */
	aspect-ratio: 1 / 1;
	/* Collapse guard: if the ratio ever fails to apply (stale stylesheet,
	 * measurement mid-animation), the slide must never shrink to a sliver
	 * and let smoothHeight lock the gallery viewport at nothing. */
	min-height: 240px;
	/* Same cap as the photo slides' images in shop.css: every slide must
	 * measure the same height or smoothHeight jumps at this slide. */
	max-height: 560px;
	background: #ffffff;
}

.emo-3d-slide__stage canvas {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	/* none: the viewer exists only after an explicit poster click, so every
	 * gesture on the canvas, vertical drags included, drives the camera,
	 * never the page. Matches the inline value the viewer JS keeps. */
	touch-action: none;
	cursor: grab;
}

.emo-3d-slide__stage canvas:active {
	cursor: grabbing;
}

.emo-3d-slide__stage canvas:focus-visible {
	outline: 2px solid var(--color-accent);
	outline-offset: -2px;
}

/* ============================================================
 * Poster: the pre-load face of the slide. A <button>, so the
 * 3D view is keyboard-reachable even before Flexslider exists.
 * ============================================================ */

.emo-3d-slide__poster {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--space-3);
	width: 100%;
	padding: var(--space-4);
	border: 0;
	/* The pre-rendered model still, injected as an inline background-image
	 * by inc/product-3d.php (never an <img>: an img inside the slide would
	 * register with PhotoSwipe's item indexing). contain, not cover: the
	 * stage tracks the photos' aspect ratio while the render is square, and
	 * the model must not crop. White fallback when no preview is baked. */
	background: #ffffff center / contain no-repeat;
	font: inherit;
	color: var(--color-text-secondary);
	cursor: pointer;
	/* Opacity in the base list so the press release eases back out, and
	 * the is-loading dim fades instead of stepping. */
	transition: opacity var(--motion-duration-fast) var(--motion-ease-standard);
}

/* The Sketchfab-style load affordance: accent disc, orbit glyph. */
.emo-3d-slide__play {
	display: grid;
	place-items: center;
	width: 64px;
	height: 64px;
	border-radius: 50%;
	background: var(--color-accent);
	color: var(--color-text-inverse);
	box-shadow: 0 4px 16px rgb(0 0 0 / 0.18);
	transition: transform var(--motion-duration-fast) var(--motion-ease-standard);
}

.emo-3d-slide__play svg {
	width: 38px;
	height: 38px;
}

.emo-3d-slide__poster:hover .emo-3d-slide__play {
	transform: scale(1.06);
}

/* Pointer-down feedback under 100ms; release keeps the slower ease. */
.emo-3d-slide__poster:active {
	opacity: 0.7;
	transition-duration: var(--motion-duration-press);
}

/* Legibility pill: the label sits on top of the preview render.
 * The slide lives inside WooCommerce's gallery markup, outside the
 * theme's styled content tree, and the poster is a <button>, so nothing
 * upstream hands it a font: without an explicit family it rendered in
 * the UA serif default. */
.emo-3d-slide__poster,
.emo-3d-slide__cta {
	font-family: var(--font-sans);
}

.emo-3d-slide__cta {
	padding: var(--space-1) var(--space-3);
	border-radius: var(--radius-control);
	background: var(--color-bg-translucent-strong);
	font-size: var(--font-size-14);
	font-weight: 600;
	letter-spacing: var(--letter-spacing-base);
	color: var(--color-text-primary);
}

.emo-3d-slide.is-loading .emo-3d-slide__poster {
	opacity: 0.5;
}

.emo-3d-slide.is-ready .emo-3d-slide__poster {
	display: none;
}

/* ============================================================
 * Loading status: text line + thin progress bar, sitting over
 * the poster's lower edge while the WASM/model downloads run.
 * ============================================================ */

.emo-3d-slide__status {
	position: absolute;
	inset: auto 0 var(--space-5) 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--space-2);
	padding: 0 var(--space-5);
	text-align: center;
	font-size: 0.85rem;
	color: var(--color-text-muted);
	pointer-events: none;
}

.emo-3d-slide__bar {
	position: relative;
	display: block;
	width: min(240px, 60%);
	height: 3px;
	border-radius: 999px;
	background: var(--color-border-hairline);
	overflow: hidden;
}

.emo-3d-slide__bar-fill {
	position: absolute;
	inset: 0 auto 0 0;
	width: 100%;
	border-radius: inherit;
	background: var(--color-accent);
	/* scaleX, not width: progress must not relayout every tick. The JS
	 * writes transform: scaleX(pct / 100) inline. */
	transform: scaleX(0);
	transform-origin: 0 50%;
	transition: transform var(--motion-duration-fast) var(--motion-ease-standard);
}

/* Parse phase has no byte count; slide a fixed chunk back and forth.
 * The animation owns the transform while it runs, overriding the base
 * scaleX(0); the JS clears its inline transform on entering this state. */
.emo-3d-slide__bar.is-indeterminate .emo-3d-slide__bar-fill {
	width: 30%;
	animation: emo-3d-indeterminate 1.2s var(--motion-ease-standard) infinite;
}

@keyframes emo-3d-indeterminate {
	0%   { transform: translateX(-100%); }
	100% { transform: translateX(333%); }
}

@media (prefers-reduced-motion: reduce) {
	.emo-3d-slide__bar.is-indeterminate .emo-3d-slide__bar-fill {
		animation: none;
		width: 100%;
		opacity: 0.4;
		/* Without the animation the base scaleX(0) would hide the bar. */
		transform: none;
	}

	/* The play-disc zoom is decorative; the opacity press stays as the
	 * comprehension feedback spec section 7 keeps. */
	.emo-3d-slide__play {
		transition: none;
	}

	.emo-3d-slide__poster:hover .emo-3d-slide__play {
		transform: none;
	}
}

/* ============================================================
 * Error face (network failure, unsupported browser).
 * ============================================================ */

.emo-3d-slide__error {
	position: absolute;
	inset: 0;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	gap: var(--space-3);
	padding: var(--space-5);
	/* The stage white, literal for the same reason as .emo-3d-slide__stage:
	 * the error face replaces the render in place, so it must not change
	 * the slide's ground. Pinned to the photo matte, never to
	 * --color-bg-base. */
	background: #ffffff;
	text-align: center;
}

.emo-3d-slide__error p {
	margin: 0;
	color: var(--color-text-secondary);
}

/* The gallery-level .emo-3d-active class (toggled by product-3d.js while
 * the 3D slide is current) used to hide WC's zoom/lightbox magnifier
 * trigger; the trigger no longer exists since the theme dropped
 * wc-product-gallery-zoom support (inc/shop.php). The class stays
 * available as a styling hook. */
