/**
 * ncp runtime — motion & interaction layer (STYLES)
 * Contract: docs/design-system.md → "Motion & interaction layer".
 *
 * Rules baked in here:
 * - Class-driven only (never data-* on saved blocks → keeps block validation clean).
 * - This file is the SINGLE SOURCE OF TRUTH for motion values (durations/easing/distance);
 *   built on theme.json presets where a preset exists. Do not copy these into docs.
 * - Every component degrades gracefully: no JS / no browser support / reduced motion all
 *   leave content fully visible and usable.
 * - No @layer: unlayered so single-class selectors keep normal specificity vs core output.
 */

/* ============================================================
 * Tokens
 * ============================================================ */
:root {
	--ncp-motion-duration: 600ms;
	--ncp-motion-duration-fast: 300ms;
	--ncp-motion-easing: cubic-bezier(0.2, 0.7, 0.2, 1);
	--ncp-motion-distance: var(--wp--preset--spacing--50, 2rem);
	--ncp-marquee-duration: 20s;
	--ncp-carousel-gap: var(--wp--preset--spacing--40, 1.5rem);

	/* Focus ring. A graphical indicator, so it takes `brand` (the client's hex, contract-
	   guaranteed at 3:1) rather than `accent`, which is the ink reserved for text. */
	--ncp-focus-color: var(--wp--preset--color--brand, currentColor);
}

/* Dark surfaces reassign the ring instead of restating it — one property, no class explosion. */
.is-style-section-inverted,
.is-style-card-highlight,
.has-contrast-background-color {
	--ncp-focus-color: var(--wp--preset--color--accent-inverted, currentColor);
}

/* Central reduced-motion zeroing. Per-component static overrides live in each section. */
@media (prefers-reduced-motion: reduce) {
	:root {
		--ncp-motion-duration: 0s;
		--ncp-motion-duration-fast: 0s;
	}
}

/* ============================================================
 * Entrance animation — JS IntersectionObserver reveal (one-shot)
 *  ncp-animate (+ --fade / --fade-up / --zoom-in / --slide-left / --slide-right)
 *  ncp-animate--stagger  → direct children reveal in sequence (cap: 12)
 *
 * Why not scroll-driven CSS (animation-timeline: view())? It only runs in Chromium, it
 * scrubs with the scrollbar (reverses when you scroll back, easy to miss on a fast fling),
 * and Firefox/older Safari show nothing at all. This reveal is time-based and fires ONCE
 * when the element enters — universal support, consistent, clearly visible.
 *
 * The hidden state is armed ONLY under html.ncp-anim-ready (added by ncp.js) AND when
 * motion is allowed. No JS, or reduced motion → the class is never added → content is
 * simply visible (never a stuck opacity:0). JS toggles .is-visible on enter; the CSS
 * transition performs the reveal.
 * ============================================================ */
@media (prefers-reduced-motion: no-preference) {
	.ncp-anim-ready .ncp-animate,
	.ncp-anim-ready .ncp-animate--stagger > * {
		opacity: 0;
		transform: translateY(var(--ncp-motion-distance));
		transition:
			opacity var(--ncp-motion-duration) var(--ncp-motion-easing),
			transform var(--ncp-motion-duration) var(--ncp-motion-easing);
	}
	/* Direction / style variants override the default fade-up travel. */
	.ncp-anim-ready .ncp-animate--fade        { transform: none; }
	.ncp-anim-ready .ncp-animate--fade-up     { transform: translateY(var(--ncp-motion-distance)); }
	.ncp-anim-ready .ncp-animate--zoom-in     { transform: scale(0.92); }
	.ncp-anim-ready .ncp-animate--slide-left  { transform: translateX(var(--ncp-motion-distance)); }
	.ncp-anim-ready .ncp-animate--slide-right { transform: translateX(calc(-1 * var(--ncp-motion-distance))); }

	/* Revealed state (JS adds .is-visible when the element scrolls into view). */
	.ncp-anim-ready .ncp-animate.is-visible,
	.ncp-anim-ready .ncp-animate--stagger > .is-visible {
		opacity: 1;
		transform: none;
	}

	/* Stagger: time-based delays now work (this is a transition, not a scroll timeline).
	   Per-child ladder capped at 12. */
	.ncp-anim-ready .ncp-animate--stagger > *:nth-child(2)    { transition-delay: 70ms; }
	.ncp-anim-ready .ncp-animate--stagger > *:nth-child(3)    { transition-delay: 140ms; }
	.ncp-anim-ready .ncp-animate--stagger > *:nth-child(4)    { transition-delay: 210ms; }
	.ncp-anim-ready .ncp-animate--stagger > *:nth-child(5)    { transition-delay: 280ms; }
	.ncp-anim-ready .ncp-animate--stagger > *:nth-child(6)    { transition-delay: 350ms; }
	.ncp-anim-ready .ncp-animate--stagger > *:nth-child(7)    { transition-delay: 420ms; }
	.ncp-anim-ready .ncp-animate--stagger > *:nth-child(8)    { transition-delay: 490ms; }
	.ncp-anim-ready .ncp-animate--stagger > *:nth-child(9)    { transition-delay: 560ms; }
	.ncp-anim-ready .ncp-animate--stagger > *:nth-child(10)   { transition-delay: 630ms; }
	.ncp-anim-ready .ncp-animate--stagger > *:nth-child(11)   { transition-delay: 700ms; }
	.ncp-anim-ready .ncp-animate--stagger > *:nth-child(n+12) { transition-delay: 770ms; }
}

/* ============================================================
 * Parallax — CSS only. Framework path targets core/cover's inner media layer.
 *  ncp-parallax on a core/cover → the image/video layer drifts on scroll.
 * Cheap path (no class): core/cover native "Fixed background".
 * Degrades flat when unsupported / reduced motion.
 * ============================================================ */
@keyframes ncp-parallax-drift {
	from { transform: translateY(-8%); }
	to   { transform: translateY(8%); }
}
@supports (animation-timeline: view()) {
	@media (prefers-reduced-motion: no-preference) {
		.ncp-parallax .wp-block-cover__image-background,
		.ncp-parallax .wp-block-cover__video-background {
			/* Oversize the layer so the drift never reveals an edge inside the clipped box. */
			height: 128%;
			top: -14%;
			animation: ncp-parallax-drift both;
			animation-timeline: view();
			animation-range: cover;
		}
	}
}

/* ============================================================
 * Marquee — CSS owns the motion; JS adds one aria-hidden clone for a seamless loop.
 *  ncp-marquee (+ --reverse / --slow). Pauses on hover; static under reduced motion.
 * No-JS fallback: children wrap into a static row (all visible).
 * ============================================================ */
.ncp-marquee {
	display: flex;
	flex-wrap: wrap; /* no-JS fallback: everything wraps and stays visible */
	align-items: center;
	gap: var(--ncp-carousel-gap);
	overflow: hidden;
}
.ncp-marquee__track {
	display: flex;
	flex-wrap: nowrap;
	width: max-content;
	animation: ncp-marquee-scroll var(--ncp-marquee-duration) linear infinite;
}
.ncp-marquee__group {
	display: flex;
	flex: 0 0 auto;
	align-items: center;
	gap: var(--ncp-carousel-gap);
	/* trailing gap so the seam between the two copies matches the item gap */
	padding-inline-end: var(--ncp-carousel-gap);
}
.ncp-marquee--reverse .ncp-marquee__track { animation-direction: reverse; }
.ncp-marquee--slow    .ncp-marquee__track { --ncp-marquee-duration: 40s; }
.ncp-marquee:hover    .ncp-marquee__track { animation-play-state: paused; }
@keyframes ncp-marquee-scroll {
	to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
	.ncp-marquee__track { animation: none; }
}

/* ============================================================
 * Carousel — CSS scroll-snap track (works with zero JS) + optional JS controls.
 *  ncp-carousel on a core/group (recommended) or core/columns.
 *  ncp-carousel__slide on each direct child.
 *  Modifiers wired by JS: --arrows / --dots / --autoplay. --peek is CSS-only.
 * No-JS fallback: a horizontally scrollable, snapping strip.
 * ============================================================ */
.ncp-carousel {
	--ncp-carousel-slide: min(100%, 22rem);
	display: flex;
	flex-direction: row;
	flex-wrap: nowrap;
	gap: var(--ncp-carousel-gap);
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	scroll-behavior: smooth;
	scrollbar-width: thin;
	-webkit-overflow-scrolling: touch;
}
.ncp-carousel--peek { --ncp-carousel-slide: 85%; }
.ncp-carousel > .ncp-carousel__slide {
	flex: 0 0 var(--ncp-carousel-slide);
	scroll-snap-align: start;
}

/* Compatibility: core/columns forces column stacking + flex-basis:100% !important on
   mobile. Override just those layout props so a columns-based carousel still scrolls. */
.wp-block-columns.ncp-carousel {
	display: flex !important;
	flex-direction: row !important;
	flex-wrap: nowrap !important;
}
.ncp-carousel > .wp-block-column.ncp-carousel__slide {
	flex: 0 0 var(--ncp-carousel-slide) !important;
}

/* Controls (injected by JS; present only after enhancement) */
.ncp-carousel-wrap { position: relative; }
.ncp-carousel__arrow {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	z-index: 2;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 2.75rem; /* 44px tap target */
	block-size: 2.75rem;
	padding: 0;
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: var(--wp--custom--radius--full, 9999px);
	background: var(--wp--preset--color--base);
	color: var(--wp--preset--color--contrast);
	box-shadow: var(--wp--preset--shadow--sm);
	cursor: pointer;
}
.ncp-carousel__arrow--prev { left: var(--wp--preset--spacing--20, 0.5rem); }
.ncp-carousel__arrow--next { right: var(--wp--preset--spacing--20, 0.5rem); }
.ncp-carousel__arrow:disabled { opacity: 0.35; cursor: default; }
.ncp-carousel__arrow:focus-visible {
	outline: 2px solid var(--ncp-focus-color);
	outline-offset: 2px;
}
.ncp-carousel__dots {
	display: flex;
	justify-content: center;
	gap: var(--wp--preset--spacing--20, 0.5rem);
	margin-block-start: var(--wp--preset--spacing--30, 1rem);
}
.ncp-carousel__dot {
	inline-size: 0.65rem;
	block-size: 0.65rem;
	padding: 0;
	border: 0;
	border-radius: var(--wp--custom--radius--full, 9999px);
	background: var(--wp--preset--color--hairline);
	cursor: pointer;
}
.ncp-carousel__dot[aria-current="true"] { background: var(--wp--preset--color--brand, currentColor); }
.ncp-carousel__dot:focus-visible {
	outline: 2px solid var(--ncp-focus-color);
	outline-offset: 2px;
}

/* ============================================================
 * Tabs & Switch — one engine, two skins (JS state).
 *  ncp-tabs (+ --switch segmented skin)
 *  ncp-tabs__nav > ncp-tabs__option ×N   (real paragraph/heading blocks)
 *  ncp-tabs__panel ×N                     (option n ↔ panel n by document order)
 * Base CSS NEVER hides panels → no-JS fallback shows all panels stacked. JS adds [hidden].
 * ============================================================ */
.ncp-tabs__nav {
	display: flex;
	flex-wrap: wrap;
	gap: var(--wp--preset--spacing--30, 1rem);
	margin-block-end: var(--wp--preset--spacing--40, 1.5rem);
	border-block-end: 1px solid var(--wp--preset--color--hairline);
}
.ncp-tabs__option {
	margin: 0;
	padding-block: var(--wp--preset--spacing--20, 0.5rem);
	margin-block-end: -1px;
	color: var(--wp--preset--color--muted);
	border-block-end: 2px solid transparent;
	cursor: pointer;
}
.ncp-tabs__option[aria-selected="true"] {
	color: var(--wp--preset--color--contrast);
	border-block-end-color: var(--wp--preset--color--brand, currentColor);
}
.ncp-tabs__option:focus-visible {
	outline: 2px solid var(--ncp-focus-color);
	outline-offset: 2px;
}
.ncp-tabs__panel[hidden] { display: none; }

/* Switch skin — a centered segmented control */
.ncp-tabs--switch .ncp-tabs__nav {
	display: flex;
	width: fit-content;
	margin-inline: auto;
	gap: 0.25rem;
	padding: 0.25rem;
	border: 1px solid var(--wp--preset--color--hairline);
	border-radius: var(--wp--custom--radius--full, 9999px);
}
.ncp-tabs--switch .ncp-tabs__option {
	margin: 0;
	padding: 0.5rem 1.25rem;
	border: 0;
	border-radius: var(--wp--custom--radius--full, 9999px);
	color: var(--wp--preset--color--muted);
}
.ncp-tabs--switch .ncp-tabs__option[aria-selected="true"] {
	background: var(--wp--preset--color--contrast);
	color: var(--wp--preset--color--base);
	border-block-end-color: transparent;
}
