/*
 * Motion and interaction feedback.
 *
 * Design rule for this file: motion is a response, never a performance.
 * Every animation is either (a) triggered by something the reader just did,
 * or (b) a one-shot entrance that plays once and is never repeated. Nothing
 * loops, nothing auto-plays, nothing competes with the text for attention.
 *
 * All of it is gated behind [data-mmb-motion="on"], which the blocking head
 * script sets only when the reader has not asked for reduced motion. Without
 * JavaScript, or with reduced motion, every rule below is inert and the page
 * renders in its final state.
 */

:root {
	--mmb-reveal-distance: 0.75rem;
	--mmb-reveal-stagger: 55ms;
	--mmb-lift: -2px;
	--mmb-press: 1px;
}

/* --------------------------------------------------------------------------
 * 1. Entrance reveals
 *
 * Content starts marginally low and transparent, then settles once it enters
 * the viewport. The travel is deliberately small: at this distance the eye
 * reads it as the page "settling", not as an element flying in.
 * -------------------------------------------------------------------------- */

[data-mmb-motion='on'] .mmb-reveal {
	opacity: 0;
	transform: translate3d(0, var(--mmb-reveal-distance), 0);
	will-change: opacity, transform;
}

[data-mmb-motion='on'] .mmb-reveal.is-revealed {
	opacity: 1;
	transform: none;
	transition:
		opacity var(--mmb-duration-slow, 420ms) var(--mmb-ease, ease),
		transform var(--mmb-duration-slow, 420ms) var(--mmb-ease, ease);
	transition-delay: calc(var(--mmb-reveal-index, 0) * var(--mmb-reveal-stagger));
	will-change: auto;
}

/*
 * Once an element has finished arriving it must never animate again, so the
 * observer unobserves and this class drops the transition entirely. Without
 * it, a later layout change would re-run the entrance.
 */
[data-mmb-motion='on'] .mmb-reveal.is-settled {
	transition: none;
}

/* --------------------------------------------------------------------------
 * 2. Surface feedback
 *
 * Cards and interactive surfaces acknowledge the pointer with a small lift
 * and a slightly warmer border. The shadow does most of the work; the
 * translate is only two pixels so a grid never appears to wobble.
 * -------------------------------------------------------------------------- */

[data-mmb-motion='on'] .mmb-card,
[data-mmb-motion='on'] .mmb-post-card,
[data-mmb-motion='on'] .wp-block-post {
	transition:
		transform var(--mmb-duration, 200ms) var(--mmb-ease, ease),
		border-color var(--mmb-duration, 200ms) var(--mmb-ease, ease),
		box-shadow var(--mmb-duration, 200ms) var(--mmb-ease, ease);
}

[data-mmb-motion='on'] .mmb-card:hover,
[data-mmb-motion='on'] .mmb-post-card:hover,
[data-mmb-motion='on'] .wp-block-post:hover {
	transform: translate3d(0, var(--mmb-lift), 0);
}

[data-mmb-motion='on'] .mmb-card:focus-within,
[data-mmb-motion='on'] .mmb-post-card:focus-within,
[data-mmb-motion='on'] .wp-block-post:focus-within {
	transform: translate3d(0, var(--mmb-lift), 0);
}

/* --------------------------------------------------------------------------
 * 3. Control feedback
 *
 * A control that moves under the finger feels connected to the click. One
 * pixel is enough; anything more reads as a bug on a trackpad.
 * -------------------------------------------------------------------------- */

[data-mmb-motion='on'] :is(button, .wp-block-button__link, .mmb-reader-actions a, .mmb-topic-filters a):active {
	transform: translateY(var(--mmb-press));
}

[data-mmb-motion='on'] .mmb-icon {
	transition: transform var(--mmb-duration-fast, 120ms) var(--mmb-ease-emphasis, ease);
}

/* Directional controls nudge the way they point. */
[data-mmb-motion='on'] :is(a, button):hover > .mmb-icon--arrow-right,
[data-mmb-motion='on'] :is(a, button):hover > .mmb-icon--chevron-right {
	transform: translateX(2px);
}

[data-mmb-motion='on'] :is(a, button):hover > .mmb-icon--arrow-left,
[data-mmb-motion='on'] :is(a, button):hover > .mmb-icon--chevron-left {
	transform: translateX(-2px);
}

[data-mmb-motion='on'] :is(a, button):hover > .mmb-icon--arrow-up {
	transform: translateY(-2px);
}

[data-mmb-motion='on'] :is(a, button):hover > .mmb-icon--refresh {
	transform: rotate(-35deg);
}

[data-mmb-motion='on'] .mmb-theme-toggle[aria-expanded='true'] .mmb-theme-toggle__caret {
	transform: rotate(180deg);
}

/* The copy control confirms with a single spring, then stays still. */
[data-mmb-motion='on'] .mmb-code-copy.is-copied .mmb-icon {
	animation: mmb-confirm var(--mmb-duration, 200ms) var(--mmb-ease-emphasis, ease);
}

@keyframes mmb-confirm {
	0% {
		transform: scale(0.7);
		opacity: 0;
	}

	100% {
		transform: scale(1);
		opacity: 1;
	}
}

/* --------------------------------------------------------------------------
 * 4. Link affordance
 *
 * Prose links draw their underline from the leading edge on hover instead of
 * simply changing colour. It is the one flourish in the reading column, and
 * it only ever fires under the pointer.
 * -------------------------------------------------------------------------- */

[data-mmb-motion='on'] .mmb-article :where(p, li) > a[href]:not(.mmb-heading-anchor) {
	background-image: linear-gradient(currentcolor, currentcolor);
	background-repeat: no-repeat;
	background-position: 0 100%;
	background-size: 0 1px;
	text-decoration: none;
	transition: background-size var(--mmb-duration, 200ms) var(--mmb-ease, ease);
}

[data-mmb-motion='on'] .mmb-article :where(p, li) > a[href]:not(.mmb-heading-anchor):hover,
[data-mmb-motion='on'] .mmb-article :where(p, li) > a[href]:not(.mmb-heading-anchor):focus-visible {
	background-size: 100% 1px;
}

/*
 * Keyboard users get a persistent underline regardless, because the sweep
 * alone is not a sufficient focus indicator.
 */
.mmb-article :where(p, li) > a[href]:not(.mmb-heading-anchor):focus-visible {
	text-decoration: underline;
}

/*
 * Section anchors are positioned in site.css; this only widens the reveal to
 * headings outside the article shell.
 */
:where(h2, h3, h4, h5, h6):hover > .mmb-heading-anchor {
	opacity: 1;
}

/* --------------------------------------------------------------------------
 * 5. Header
 *
 * The header condenses after the first scroll: less padding, a real edge, and
 * a translucent backdrop. It is a state change rather than a continuous
 * effect, so it costs nothing while reading.
 * -------------------------------------------------------------------------- */

.mmb-site-header {
	transition:
		padding-block var(--mmb-duration, 200ms) var(--mmb-ease, ease),
		box-shadow var(--mmb-duration, 200ms) var(--mmb-ease, ease),
		background-color var(--mmb-duration, 200ms) var(--mmb-ease, ease),
		border-color var(--mmb-duration, 200ms) var(--mmb-ease, ease);
}

.mmb-site-header.is-scrolled {
	box-shadow: 0 1px 0 0 var(--wp--preset--color--border), 0 8px 24px -20px rgb(0 0 0 / 45%);
}

@supports (backdrop-filter: blur(1px)) {
	.mmb-site-header.is-scrolled {
		background-color: color-mix(in srgb, var(--wp--preset--color--surface) 82%, transparent);
		backdrop-filter: saturate(1.4) blur(10px);
	}
}

/* --------------------------------------------------------------------------
 * 6. Navigation continuity
 *
 * Cross-document view transitions let a page change read as a continuation
 * instead of a blink. Browsers without support simply navigate as usual.
 * -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
	@view-transition {
		navigation: auto;
	}

	::view-transition-old(root) {
		animation: mmb-fade-out 90ms var(--mmb-ease, ease) both;
	}

	::view-transition-new(root) {
		animation: mmb-fade-in 180ms var(--mmb-ease, ease) both;
	}

	/* The masthead is shared chrome, so it should not cross-fade with the page. */
	.mmb-site-header {
		view-transition-name: mmb-header;
	}
}

@keyframes mmb-fade-out {
	to {
		opacity: 0;
	}
}

@keyframes mmb-fade-in {
	from {
		opacity: 0;
	}
}

/* --------------------------------------------------------------------------
 * 7. Deferred navigation indicator
 *
 * Shown only when a navigation is slow enough to notice — the script waits
 * before adding the class — so fast pages never flash a loading bar. There is
 * no blocking preloader: content is never hidden behind an animation.
 * -------------------------------------------------------------------------- */

.mmb-route-progress {
	position: fixed;
	z-index: 2147483000;
	inset-block-start: 0;
	inset-inline: 0;
	height: 2px;
	background: var(--wp--preset--color--accent);
	opacity: 0;
	transform: scaleX(0);
	transform-origin: 0 50%;
	pointer-events: none;
}

[data-mmb-motion='on'] .mmb-route-progress.is-active {
	opacity: 1;
	animation: mmb-route 2.4s var(--mmb-ease, ease) forwards;
}

@keyframes mmb-route {
	0% {
		transform: scaleX(0);
	}

	60% {
		transform: scaleX(0.72);
	}

	100% {
		transform: scaleX(0.94);
	}
}

/* --------------------------------------------------------------------------
 * 8. Opt-outs
 * -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
	.mmb-reveal,
	.mmb-reveal.is-revealed {
		opacity: 1 !important;
		transform: none !important;
		transition: none !important;
	}

	.mmb-heading-anchor {
		transition: none;
	}

	.mmb-route-progress {
		display: none;
	}
}

@media print {
	.mmb-reveal,
	.mmb-reveal.is-revealed {
		opacity: 1 !important;
		transform: none !important;
	}

	.mmb-route-progress,
	.mmb-heading-anchor {
		display: none !important;
	}
}

@media (forced-colors: active) {
	.mmb-route-progress {
		background: Highlight;
	}

	.mmb-heading-anchor {
		opacity: 1;
	}
}
