/**
 * Flip Box block front-end styles.
 *
 * Pure CSS effect engine. Faces are identified structurally:
 * `.wpminty-fb > :first-child` is the front face, `.wpminty-fb > :nth-child(2)`
 * is the back face — never by class name. The flipped state is expressed
 * through the `--wpminty-fb-flipped` flag (0 or 1) so trigger selectors
 * stay tiny; effect sections translate the flag into transforms.
 *
 * Sections are organized per effect so future effects (push/overlay/fade)
 * append without touching existing rules. Media queries mirror the theme
 * breakpoints (mobile 600px / tablet 960px).
 * The shared front-end stylesheet is also loaded on the editor canvas
 * (block.json `style`), so the base container rules exclude the editor
 * wrapper through `:not(.wpminty-fb--editor)`: the grid stacking and the
 * forced min-height tokens must never reshape the static editor preview
 * (edit.js stacks the faces in document flow). The trigger/effect rules
 * below are editor-safe by construction: edit.js never adds the trigger,
 * effect or is-flipped classes to the editor wrapper, and editor.css
 * normalizes the faces defensively on top.
 */

/* ==========================================================================
   Base container + flipped-state flag
   ========================================================================== */

.wpminty-fb:not(.wpminty-fb--editor) {
	--wpminty-fb-flipped: 0;
	position: relative;
	display: grid;
	min-height: var(--wpminty-fb-min-h-desktop, auto);
}

@media (max-width: 960px) {
	.wpminty-fb:not(.wpminty-fb--editor) {
		min-height: var(--wpminty-fb-min-h-tablet, var(--wpminty-fb-min-h-desktop, auto));
	}
}

@media (max-width: 600px) {
	.wpminty-fb:not(.wpminty-fb--editor) {
		min-height: var(--wpminty-fb-min-h-mobile, var(--wpminty-fb-min-h-tablet, var(--wpminty-fb-min-h-desktop, auto)));
	}
}

/* ==========================================================================
   Trigger branches (shared by every effect)
   hover  = :hover + :focus-within (both hover-capable pointers only) + .is-flipped
   tab    = :focus-within (hover-capable pointers only) + .is-flipped
   click  = .is-flipped (toggled by view.js)

   Keyboard focus is an accessibility baseline, not an author choice:
   hover flips on :focus-within as well as :hover, and the legacy tab
   branch (trigger is no longer selectable but saved content keeps it)
   behaves as focus-then-flip. render.php gives the wrapper tabindex="0"
   in hover/tab modes so the flip stays keyboard-reachable even when the
   faces carry no focusable content. Click mode deliberately has no
   focus branch: focusing the box must not flip it, otherwise the
   Enter/Space toggle and the focus would double-trigger.

   The :focus-within branches are gated behind @media (hover: hover): on
   touch devices a tap leaves focus inside the container and a sticky
   :focus-within would pin the flipped flag, making tap-to-flip-back
   impossible. Coarse pointers degrade to tap toggling in view.js instead.
   ========================================================================== */

.wpminty-fb--trigger-hover.is-flipped,
.wpminty-fb--trigger-tab.is-flipped,
.wpminty-fb--trigger-click.is-flipped {
	--wpminty-fb-flipped: 1;
}

@media (hover: hover) {
	.wpminty-fb--trigger-hover:hover,
	.wpminty-fb--trigger-hover:focus-within,
	.wpminty-fb--trigger-tab:focus-within {
		--wpminty-fb-flipped: 1;
	}
}

.wpminty-fb--trigger-click {
	cursor: pointer;
}

/* ==========================================================================
   Effect: flip (3D rotation around the axis chosen by the direction class)
   ========================================================================== */

.wpminty-fb--effect-flip {
	/*
	 * Width-adaptive perspective: view.js measures the wrapper and writes
	 * --wpminty-fb-perspective = FLIP_PERSPECTIVE_RATIO * width so the
	 * peak projection overflow stays inside the clip budget below (and any
	 * author-supplied perspective wins untouched). The static fallback
	 * covers no-JS rendering.
	 */
	perspective: var(--wpminty-fb-perspective, 1200px);
	/*
	 * Projection overflow handling (clip + compression, zero layout
	 * footprint).
	 *
	 * Root cause (confirmed by 2deg-sweep headless measurement): mid-flip
	 * the perspective projection paints up to ~0.13-0.17 * w^2 / p pixels
	 * beyond the wrapper (w = face width, p = perspective), peaking at
	 * 62-86deg; rotateY directions overflow top/bottom, rotateX directions
	 * overflow left/right. Per CSS Transforms that painted overflow feeds
	 * the ancestors' scrollable overflow area, so a card near the document
	 * edge grew the scroll area mid-flip and the scrollbar flashed.
	 *
	 * A padding/negative-margin buffer was tried and rejected: percentage
	 * padding resolves against the containing block WIDTH, producing huge
	 * vertical whitespace on tall layouts, and negative margins are not
	 * reliable inside WordPress layout containers (core forces
	 * margin-left/right: auto !important on constrained children).
	 *
	 * Final scheme, two cooperating parts:
	 * A. Clip the projection overflow at the wrapper. overflow:clip (not
	 *    hidden) never creates a scroll container, so the painted overflow
	 *    can no longer reach the document scroll area. overflow-clip-margin
	 *    pushes the clip edge outward by a fixed pixel budget so the
	 *    projection stays fully visible in supporting engines. Engine
	 *    differences (headless-verified): Chromium does not clip the
	 *    perspective-projected faces at all under overflow:clip (the
	 *    projection paints fully visible while the scrollable overflow
	 *    area stays untouched — verified with a 12000px projection
	 *    overshoot at a forced 600px perspective), so the margin is a
	 *    forward-compat guarantee there; Firefox honors the property with
	 *    a <length> only, never a percentage, and clips past the margin;
	 *    Safari ignores the property entirely and degrades to clipping
	 *    exactly at the border box — which part B keeps visually
	 *    negligible. The overflow:hidden line is the legacy-engine
	 *    fallback (it hard-clips the projection at the border box, but
	 *    the scrollbar is still suppressed).
	 * B. view.js keeps p >= FLIP_PERSPECTIVE_RATIO * w, which bounds the
	 *    peak overflow to ~2% of the card width: browsers with
	 *    overflow-clip-margin render the full projection inside the budget
	 *    (zero clipping) and Safari's hard clip removes at most a hair-thin
	 *    sliver exactly at the edge-on angles, which paint almost nothing.
	 * The ratio (view.js FLIP_PERSPECTIVE_RATIO = 8) and the margin below
	 * were calibrated together by a 2deg-sweep headless scan across
	 * 400/800/1200px cards in all four directions: at ratio 8 the peak
	 * overflow is <= 1.4% of the card width and <= 11px absolute, so both
	 * budgets hold. Any change to one side must re-validate the other.
	 *
	 * The classic "overflow flattens 3D" trap does not apply here: it only
	 * concerns transform-style: preserve-3d chains, while this block is a
	 * single-level context (wrapper perspective -> face transforms) and
	 * perspective itself is unaffected by overflow. The wrapper keeps its
	 * natural layout box — no padding, no negative margins — so WordPress
	 * layout containers (constrained/flow/group/columns) see an ordinary
	 * block and hit-testing needs no pointer-events compensation.
	 */
	overflow: hidden;
	overflow: clip;
	/* 32px = calibrated worst-case overflow (~11px at ratio 8) + headroom
	   for taller faces and subpixel rounding. */
	overflow-clip-margin: 32px;
}

/*
 * Rotation axis tokens per direction. The sign drives which way the card
 * rotates; the unused axis defaults to 0deg through the var() fallbacks.
 */
.wpminty-fb--effect-flip.wpminty-fb--dir-right {
	--wpminty-fb-rot-y: 180deg;
}

.wpminty-fb--effect-flip.wpminty-fb--dir-left {
	--wpminty-fb-rot-y: -180deg;
}

.wpminty-fb--effect-flip.wpminty-fb--dir-up {
	--wpminty-fb-rot-x: -180deg;
}

.wpminty-fb--effect-flip.wpminty-fb--dir-down {
	--wpminty-fb-rot-x: 180deg;
}

/*
 * Both faces share one grid cell so the container height equals the
 * taller face; the back face is pre-rotated 180deg so it stays hidden
 * behind its backface until the flag flips.
 *
 * Hidden-face a11y: the face that is currently on the hidden side of the
 * rotation carries visibility:hidden, which removes it from the Tab
 * order and the accessibility tree. Visibility is a discrete property
 * with a zero duration and a delay of half the flip duration, so it
 * switches exactly at the 90deg handoff — never mid-side, never early.
 */
.wpminty-fb--effect-flip > :first-child,
.wpminty-fb--effect-flip > :nth-child(2) {
	grid-area: 1 / 1;
	backface-visibility: hidden;
	-webkit-backface-visibility: hidden;
	transition:
		transform var(--wpminty-fb-duration, 0.6s) var(--wpminty-fb-easing, ease),
		visibility 0s linear calc(var(--wpminty-fb-duration, 0.6s) / 2);
	will-change: transform;
}

.wpminty-fb--effect-flip > :first-child {
	transform: rotateY(calc(var(--wpminty-fb-rot-y, 0deg) * var(--wpminty-fb-flipped)))
		rotateX(calc(var(--wpminty-fb-rot-x, 0deg) * var(--wpminty-fb-flipped)));
}

.wpminty-fb--effect-flip > :nth-child(2) {
	transform: rotateY(calc(var(--wpminty-fb-rot-y, 0deg) * (1 - var(--wpminty-fb-flipped))))
		rotateX(calc(var(--wpminty-fb-rot-x, 0deg) * (1 - var(--wpminty-fb-flipped))));
}

/*
 * Visibility state machine for the hidden face (mirrors the flipped flag
 * 1:1). At rest the back face is pre-rotated 180deg and hidden from the
 * Tab order and the accessibility tree; once any trigger branch raises
 * the flag, the roles swap — the front face becomes the hidden one.
 * The selector lists duplicate the trigger branches above so hover/tab
 * instances (which flip without the .is-flipped class) stay in sync.
 */
.wpminty-fb--effect-flip > :nth-child(2) {
	visibility: hidden;
}

.wpminty-fb--effect-flip.wpminty-fb--trigger-hover.is-flipped > :first-child,
.wpminty-fb--effect-flip.wpminty-fb--trigger-tab.is-flipped > :first-child,
.wpminty-fb--effect-flip.wpminty-fb--trigger-click.is-flipped > :first-child {
	visibility: hidden;
}

.wpminty-fb--effect-flip.wpminty-fb--trigger-hover.is-flipped > :nth-child(2),
.wpminty-fb--effect-flip.wpminty-fb--trigger-tab.is-flipped > :nth-child(2),
.wpminty-fb--effect-flip.wpminty-fb--trigger-click.is-flipped > :nth-child(2) {
	visibility: visible;
}

@media (hover: hover) {
	.wpminty-fb--effect-flip.wpminty-fb--trigger-hover:hover > :first-child,
	.wpminty-fb--effect-flip.wpminty-fb--trigger-hover:focus-within > :first-child,
	.wpminty-fb--effect-flip.wpminty-fb--trigger-tab:focus-within > :first-child {
		visibility: hidden;
	}

	.wpminty-fb--effect-flip.wpminty-fb--trigger-hover:hover > :nth-child(2),
	.wpminty-fb--effect-flip.wpminty-fb--trigger-hover:focus-within > :nth-child(2),
	.wpminty-fb--effect-flip.wpminty-fb--trigger-tab:focus-within > :nth-child(2) {
		visibility: visible;
	}
}

/* ==========================================================================
   Motion preferences
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
	.wpminty-fb--effect-flip > :first-child,
	.wpminty-fb--effect-flip > :nth-child(2) {
		/* No transform animation: drop the visibility delay too so the
		   hidden-face switch happens instantly together with the snap. */
		transition: visibility 0s;
	}
}
