/**
 * insta-blocks — motion styles for core media blocks.
 *
 * Scoped to `.is-style-<name>` so a block without the style, and every other block on the page, are
 * untouched. Every value below is either RECOVERED verbatim from a port's pre-migration theme.json
 * (a length-exact 47,065-character copy) or MEASURED live on the source with agent-browser. Where
 * the two disagreed, the live source won for TIMING and the approved mockup won for GEOMETRY —
 * timing is invisible to the static parity critic, geometry is scored by it.
 *
 * The mockup was wrong twice here, so do not "correct" these values back to it:
 *   easing of the peek reveal   mockup 0.25s          source 0.4s ease
 *   the peek also MOVES         mockup opacity only   source also top 50% -> 0
 */

/* ─────────────────────────────────────────────────────────────────────────────
 * Zoom on hover — core/image, core/post-featured-image
 *
 * MEASURED on the live source (`.oshine-fancy-portfolio-image`), and identical to the recovered
 * theme.json rule on all five properties:
 *   wrapper   overflow: hidden
 *   img       transform: scale(1) -> scale(1.2)
 *   transition: transform 2s cubic-bezier(0.4, 0, 0.2, 1)
 *   will-change: transform
 *
 * The source triggers this from the ROW, so hovering a project title also zooms its image. A style
 * on the image block can only trigger from the image itself; that is the block-scoped equivalent and
 * it needs no second class on an ancestor. `overflow: hidden` on the figure is what keeps the
 * scaled-up image inside its own box — without it the image would grow past its frame.
 * ───────────────────────────────────────────────────────────────────────────── */
.wp-block-image.is-style-modern-portfolio-instawp-zoom,
.wp-block-post-featured-image.is-style-modern-portfolio-instawp-zoom {
	overflow: hidden;
}

.wp-block-image.is-style-modern-portfolio-instawp-zoom img,
.wp-block-post-featured-image.is-style-modern-portfolio-instawp-zoom img {
	transform: scale(1);
	transition: transform 2s cubic-bezier(0.4, 0, 0.2, 1);
	will-change: transform;
}

.wp-block-image.is-style-modern-portfolio-instawp-zoom:hover img,
.wp-block-post-featured-image.is-style-modern-portfolio-instawp-zoom:hover img {
	transform: scale(1.2);
}

/* ─────────────────────────────────────────────────────────────────────────────
 * Row — image preview on hover — core/columns
 *
 * The row's image is hidden at rest and revealed by hovering the row. Registered on core/columns
 * because the ROW is what owns `position: relative` and the `:hover`; the image is reached with a
 * descendant selector, so it carries no class of its own.
 *
 * GEOMETRY from the approved mockup (`golden/pages.css:26-32`) — it is what the parity critic scores:
 *   inset-inline-end 12rem, vertically centred, out of flow, pointer-events none
 * The image's own WIDTH is left to the block's `width` attribute (a clamp resolving to 280px at
 * desktop, 330px at mobile), so nothing is re-specified here that the block already expresses.
 *
 * TIMING from the live source: opacity over 0.4s ease (the mockup says 0.25s and is wrong).
 * The source ALSO slides the preview `top: 50% -> 0` over the same 0.4s. That is deliberately not
 * ported: it is geometry-coupled and would fight the mockup's vertical centring, which is the
 * geometry the critic measures.
 *
 * THE THREE TIERS are the mockup's, and they are why this style authors media queries at all — the
 * effect genuinely differs by width and no block attribute has a per-tier dimension:
 *   < 768        image visible IN FLOW (the block's own layout — nothing needed here)
 *   768 - 1080   image hidden outright
 *   > 1080       image out of flow, revealed on row hover
 * ───────────────────────────────────────────────────────────────────────────── */
@media (min-width: 768px) and (max-width: 1080px) {
	.wp-block-columns.is-style-modern-portfolio-instawp-peek-row > .wp-block-column:first-child > :is(.wp-block-image, .wp-block-post-featured-image) {
		display: none;
	}
}

@media (min-width: 1081px) {
	.wp-block-columns.is-style-modern-portfolio-instawp-peek-row {
		position: relative;
	}

	.wp-block-columns.is-style-modern-portfolio-instawp-peek-row > .wp-block-column:first-child > :is(.wp-block-image, .wp-block-post-featured-image) {
		inset-block-start: 50%;
		inset-inline-end: 12rem;
		margin: 0;
		opacity: 0;
		pointer-events: none;
		position: absolute;
		transition: opacity 0.4s ease;
		translate: 0 -50%;
	}

	.wp-block-columns.is-style-modern-portfolio-instawp-peek-row:hover > .wp-block-column:first-child > :is(.wp-block-image, .wp-block-post-featured-image) {
		opacity: 1;
	}
}

/* ─────────────────────────────────────────────────────────────────────────────
 * Row hover, the other two halves — core/columns (same style as the preview above)
 *
 * MEASURED on the live source with real pointer input, `.oshine-fancy-hover-image-style`:
 *   ::after   1px #000 at bottom:-1px, scaleX(0) -> scaleX(1) from the LEFT,
 *             transition: transform 0.8s cubic-bezier(0.4, 0, 0.2, 1)
 *   columns   .oshine-left padding-left 0 -> 24px, .oshine-right padding-right 0 -> 24px,
 *             transition: padding 0.45s cubic-bezier(0.4, 0, 0.2, 1)
 *
 * WHY A PSEUDO-ELEMENT IS RIGHT HERE, where it was wrong for the separator. The separator is a real
 * `core/separator` block, so inventing a `::before` for it would have replaced a block that exists.
 * This accent line has no block: the row's resting hairline is a BORDER attribute on the columns
 * block, and a border cannot be animated independently of its box — you cannot wipe a border in from
 * the left. No block expresses `::after` by definition, so this is the only available form.
 *
 * Scoped to the same >1080 tier as the preview: all three are one hover gesture, and a hover-only
 * affordance below that width would be unreachable on touch. That tier already establishes
 * `position: relative` on the row, which is what the absolute `::after` resolves against.
 * ───────────────────────────────────────────────────────────────────────────── */
@media (min-width: 1081px) {
	.wp-block-columns.is-style-modern-portfolio-instawp-peek-row::after {
		background-color: var(--wp--preset--color--contrast, currentColor);
		block-size: 1px;
		content: "";
		inline-size: 100%;
		inset-block-end: -1px;
		inset-inline-start: 0;
		pointer-events: none;
		position: absolute;
		transform: scaleX(0);
		transform-origin: left center;
		transition: transform 0.8s cubic-bezier(0.4, 0, 0.2, 1);
		will-change: transform;
	}

	.wp-block-columns.is-style-modern-portfolio-instawp-peek-row:hover::after {
		transform: scaleX(1);
	}

	.wp-block-columns.is-style-modern-portfolio-instawp-peek-row > .wp-block-column:first-child,
	.wp-block-columns.is-style-modern-portfolio-instawp-peek-row > .wp-block-column:last-child {
		transition: padding 0.45s cubic-bezier(0.4, 0, 0.2, 1);
		will-change: padding;
	}

	.wp-block-columns.is-style-modern-portfolio-instawp-peek-row:hover > .wp-block-column:first-child {
		padding-inline-start: 24px;
	}

	.wp-block-columns.is-style-modern-portfolio-instawp-peek-row:hover > .wp-block-column:last-child {
		padding-inline-end: 24px;
	}
}

/* ─────────────────────────────────────────────────────────────────────────────
 * Wipe in on scroll — core/separator
 *
 * MEASURED on the live source (`.oshine-fancy-portfolio-item::before`): a 1px black rule that wipes
 * in from the left, `transform: scaleX(0) -> scaleX(1)` over 1s cubic-bezier(0.4, 0, 0.2, 1), fired
 * when 20% of its row has entered the viewport. In the port that rule is already a real
 * `core/separator` block, so the block itself is animated — no pseudo-element is invented to stand
 * in for something a block already expresses.
 *
 * SCROLL-DRIVEN, NOT JS. `docs/BLOCK-DEV-LEARNINGS.md` §9 records that the IntersectionObserver
 * version of this was built and then REVERTED — it lived in a `type="module"` view script and
 * browsers cached the module so aggressively that updates served stale code. `animation-timeline`
 * is the house pattern (see `src/blocks/portfolio/style.scss`).
 *
 * ⚠ ONE HONEST DEVIATION: a view() timeline SCRUBS the animation against scroll position, where the
 * source PLAYS it for 1s once triggered. There is no CSS-only way to play-once-on-enter, so the
 * duration below is `auto` (governed by the range) and the source's 1s has no equivalent. The easing
 * and the direction are the source's.
 *
 * The `@supports` test MUST include `(animation-range: entry)` — it filters out partial support.
 * Unsupported browsers simply show the rule already drawn, which is the correct fallback for a
 * decorative reveal.
 * ───────────────────────────────────────────────────────────────────────────── */
@supports ((animation-timeline: view()) and (animation-range: entry)) {
	@keyframes ib-wipe-in {
		from {
			transform: scaleX(0);
		}

		to {
			transform: scaleX(1);
		}
	}

	.wp-block-separator.is-style-modern-portfolio-instawp-wipe {
		transform-origin: left center;
		will-change: transform;
		/* animation-timeline MUST come after the shorthand, which resets it. */
		animation: ib-wipe-in auto cubic-bezier(0.4, 0, 0.2, 1) both;
		animation-timeline: view();
		animation-range: entry 0% entry 55%;
	}
}

/* ─────────────────────────────────────────────────────────────────────────────
 * Reveal on scroll — core/group
 *
 * MEASURED on the live source (`.oshine-fancy-portfolio-module-style-1 .oshine-fancy-portfolio-item`):
 *   rest      opacity: 0; transform: translateY(24px); will-change: opacity, transform
 *   revealed  opacity: 1; transform: translateY(0)
 *   timing    opacity 1s ease  +  transform 1s cubic-bezier(0.4, 0, 0.2, 1)
 *   trigger   when 20% of the row has entered the viewport (its own JS: itemTop + height*0.2)
 *
 * ⚠ TWO HONEST DEVIATIONS, both forced by the mechanism:
 *   1. The source gives opacity and transform DIFFERENT easings. One animation carries one easing,
 *      so both use the transform's cubic-bezier — it is the dominant motion and the one an eye
 *      tracks. Splitting into two animations to preserve `ease` on opacity is possible but buys a
 *      difference nobody can see.
 *   2. A view() timeline SCRUBS against scroll position where the source PLAYS for 1s once its
 *      trigger fires. There is no CSS-only play-once-on-enter, and the JS route is closed —
 *      `docs/BLOCK-DEV-LEARNINGS.md` §9 records the IntersectionObserver version being built and
 *      REVERTED over ES-module caching. The range below starts at `entry 20%` to honour the
 *      measured trigger point and finishes at 65%, so the row is fully revealed well before it
 *      reaches the middle of the viewport.
 *
 * `both` fill is what holds the row hidden BEFORE its range. That is safe here only because the
 * timeline is known active on this subtree (verified: the sibling separator's ViewTimeline reports
 * playState "running"). If a future port nests these rows inside a non-scrolling `overflow` ancestor
 * the timeline goes inactive and the fill would leave whole rows invisible — check that first.
 * ───────────────────────────────────────────────────────────────────────────── */
@supports ((animation-timeline: view()) and (animation-range: entry)) {
	@keyframes ib-reveal-row {
		from {
			opacity: 0;
			transform: translateY(24px);
		}

		to {
			opacity: 1;
			transform: translateY(0);
		}
	}

	.wp-block-group.is-style-modern-portfolio-instawp-reveal-row {
		will-change: opacity, transform;
		/* animation-timeline MUST come after the shorthand, which resets it. */
		animation: ib-reveal-row auto cubic-bezier(0.4, 0, 0.2, 1) both;
		animation-timeline: view();
		animation-range: entry 20% entry 65%;
	}
}

/* ─────────────────────────────────────────────────────────────────────────────
 * Reduced motion — every effect above resolves to its END state, never its hidden one.
 *
 * The recovered theme.json carried the same pair for the zoom. The peek keeps its reveal (the image
 * would otherwise be unreachable) but stops fading, and the separator is simply drawn.
 * ───────────────────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
	.wp-block-image.is-style-modern-portfolio-instawp-zoom img,
	.wp-block-post-featured-image.is-style-modern-portfolio-instawp-zoom img {
		transition: none;
	}

	.wp-block-image.is-style-modern-portfolio-instawp-zoom:hover img,
	.wp-block-post-featured-image.is-style-modern-portfolio-instawp-zoom:hover img {
		transform: scale(1);
	}

	.wp-block-columns.is-style-modern-portfolio-instawp-peek-row > .wp-block-column:first-child > :is(.wp-block-image, .wp-block-post-featured-image) {
		transition: none;
	}

	.wp-block-columns.is-style-modern-portfolio-instawp-peek-row::after,
	.wp-block-columns.is-style-modern-portfolio-instawp-peek-row > .wp-block-column:first-child,
	.wp-block-columns.is-style-modern-portfolio-instawp-peek-row > .wp-block-column:last-child {
		transition: none;
	}

	.wp-block-columns.is-style-modern-portfolio-instawp-peek-row:hover::after {
		transform: scaleX(1);
	}

	.wp-block-separator.is-style-modern-portfolio-instawp-wipe {
		animation: none;
		transform: scaleX(1);
	}

	.wp-block-group.is-style-modern-portfolio-instawp-reveal-row {
		animation: none;
		opacity: 1;
		transform: none;
	}
}
