/**
 * DFS Motion — Hide Until Hover, for any Elementor widget that carries the control.
 *
 * @decision D411 — a button (or any widget) stays hidden until its column or
 * section is hovered, then arrives with a configurable entrance.
 *
 * Chris, via Brad: "when you hover over one the button in section Full Video
 * fades in and they click to view it." Brad on where the control belongs: "I
 * think putting the hide until hover works best on button itself because we
 * need it to work in both columns and sections".
 *
 * ---------------------------------------------------------------------------
 * NO JAVASCRIPT. AT ALL.
 *
 * The BUILD-PLAN assumed this needed JS: CSS cannot select a descendant from an
 * ancestor's hover when you only control the descendant's selector, and
 * Elementor hands a widget `{{WRAPPER}}` — which expands to
 * `.elementor-1234 .elementor-element-abc`, so prefixing it with
 * `.elementor-column:hover` puts the column OUTSIDE `.elementor-1234` and is
 * structurally wrong.
 *
 * That constraint binds Elementor's `selectors` array. It does NOT bind this
 * file. A static stylesheet writes whatever ancestor selectors it likes, so the
 * control only needs to stamp a class on the widget (prefix_class) and every
 * rule below is plain CSS. No per-frame JS, no init pass, no scan, nothing to
 * miss a widget that arrived late in a popup or a tab.
 *
 * It also removes the failure this week kept producing: a client-visible
 * control that only works once JS has run is what broke Full Video seven times
 * behind a cache (@decision D406). This cannot break that way — there is no
 * code path to skip.
 *
 * ---------------------------------------------------------------------------
 * WHY {{WRAPPER}} AND NOT .elementor-button-wrapper
 *
 * MEASURED, elementor 3.31.3, includes/widgets/traits/button-trait.php:535 —
 * `.elementor-button-wrapper` is rendered ONLY when the `e_optimized_markup`
 * experiment is OFF:
 *
 *     $optimized_markup = ...is_feature_active( 'e_optimized_markup' );
 *     <?php if ( ! $optimized_markup ) : ?>
 *     <div class="elementor-button-wrapper">
 *
 * With that experiment on there is no wrapper div, only the bare <a>. Targeting
 * it would work on one site and silently do nothing on another. The widget
 * element that prefix_class stamps is always rendered, so that is the hook.
 *
 * ---------------------------------------------------------------------------
 * IT FAILS SAFE, IN BOTH DIRECTIONS
 *
 * This control HIDES something, and the thing it hides is usually the link to
 * the video. Every failure mode therefore has to end with the widget VISIBLE:
 *
 *   stylesheet not loaded    no rule matches, widget renders normally
 *   touch device             @media (hover: none) shows it — a hover-only
 *                            button on a phone would be permanently invisible
 *   keyboard user            :focus-within reveals it, so it can be tabbed to
 *   reduced motion           end states kept, transition dropped
 *
 * visibility + pointer-events are carried alongside opacity so a hidden widget
 * is not an invisible click target sitting over the card. That is the CSS
 * equivalent of GSAP's autoAlpha, and the same reasoning.
 */

/* -------------------------------------------------------------- direction --- */
/*
 * The five entrances Brad named: "definitely include Fade In, Fade In Down,
 * Fade In Up, DFade In Left, Fade In Right".
 *
 * Each is a multiplier on the distance, not a hard-coded transform, so the
 * Distance control tunes all five and the resting transform stays one
 * declaration. Plain Fade In is the DEFAULT and needs no class at all — both
 * multipliers stay 0 — which is why it is the state the fallbacks describe.
 *
 * Sign convention: the value is where the widget STARTS. "Fade In Up" begins
 * below its final position (+Y) and travels up to 0.
 *
 * These live here rather than in a control's `selectors` so they survive a post
 * CSS clear, and so the option value in the database stays a readable word
 * instead of a raw CSS declaration.
 */
.elementor-widget.dfs-huh-dir--up    { --dfs-huh-ty:  1; }
.elementor-widget.dfs-huh-dir--down  { --dfs-huh-ty: -1; }
.elementor-widget.dfs-huh-dir--left  { --dfs-huh-tx: -1; }
.elementor-widget.dfs-huh-dir--right { --dfs-huh-tx:  1; }

/* ---------------------------------------------------------------- hidden ---- */
/*
 * The resting state. Applied to the widget itself, and scoped to a host that is
 * actually present, so a button set to "column" inside a section it was never
 * placed in simply never hides.
 */
.elementor-column .elementor-widget.dfs-huh--col,
.elementor-section .elementor-widget.dfs-huh--sec,
.e-con .elementor-widget.dfs-huh--sec {
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	transform: translate3d(
		calc( var( --dfs-huh-tx, 0 ) * var( --dfs-huh-dist, 16px ) ),
		calc( var( --dfs-huh-ty, 0 ) * var( --dfs-huh-dist, 16px ) ),
		0
	);
	/*
	 * FADE OUT lives here, on the resting rule.
	 *
	 * A transition declared on the base state is the one that runs when the
	 * element returns TO that state — i.e. when the pointer leaves. The reveal
	 * rule below carries its own, so in and out are independently timed.
	 *
	 * Brad: "for the button hide until hover when its on make the fade out
	 * duration controlable too" / "seperate from the fade in".
	 *
	 * No delay on the way out: a button that lingers after the pointer has gone
	 * reads as lag, not as design. Delay is a fade-IN control only.
	 */
	transition:
		opacity    var( --dfs-huh-out, 0.45s ) var( --dfs-huh-ease, cubic-bezier( 0.226, 0.861, 0.408, 1.033 ) ),
		transform  var( --dfs-huh-out, 0.45s ) var( --dfs-huh-ease, cubic-bezier( 0.226, 0.861, 0.408, 1.033 ) ),
		visibility var( --dfs-huh-out, 0.45s ) linear;
}

/* --------------------------------------------------------------- revealed --- */
.elementor-column:hover        .elementor-widget.dfs-huh--col,
.elementor-column:focus-within .elementor-widget.dfs-huh--col,
.elementor-section:hover        .elementor-widget.dfs-huh--sec,
.elementor-section:focus-within .elementor-widget.dfs-huh--sec,
.e-con:hover                    .elementor-widget.dfs-huh--sec,
.e-con:focus-within             .elementor-widget.dfs-huh--sec {
	opacity: 1;
	visibility: visible;
	pointer-events: auto;
	transform: translate3d( 0, 0, 0 );
	/* FADE IN — its own duration and the delay, independent of the fade out. */
	transition:
		opacity    var( --dfs-huh-dur, 0.45s ) var( --dfs-huh-ease, cubic-bezier( 0.226, 0.861, 0.408, 1.033 ) ) var( --dfs-huh-delay, 0s ),
		transform  var( --dfs-huh-dur, 0.45s ) var( --dfs-huh-ease, cubic-bezier( 0.226, 0.861, 0.408, 1.033 ) ) var( --dfs-huh-delay, 0s ),
		visibility var( --dfs-huh-dur, 0.45s ) linear var( --dfs-huh-delay, 0s );
}

/*
 * A widget that is itself focused must be visible even if the browser has not
 * propagated :focus-within to the host yet — belt and braces for keyboard.
 */
.elementor-widget.dfs-huh--col:focus-within,
.elementor-widget.dfs-huh--sec:focus-within {
	opacity: 1;
	visibility: visible;
	pointer-events: auto;
	transform: translate3d( 0, 0, 0 );
}

/* ------------------------------------------------------------------ touch --- */
/*
 * The degradation rule: smaller, softer, later — never broken. A device with no
 * real hover can never reveal this, and the widget is typically the only route
 * to the video, so on touch it is simply always visible.
 */
@media ( hover: none ) {
	.elementor-column .elementor-widget.dfs-huh--col,
	.elementor-section .elementor-widget.dfs-huh--sec,
	.e-con .elementor-widget.dfs-huh--sec {
		opacity: 1;
		visibility: visible;
		pointer-events: auto;
		transform: none;
		transition: none;
	}
}


/*
 * @decision D426 — ON TOUCH, REVEAL WHEN IT SCROLLS INTO VIEW. Opt-in.
 *
 * Brad, 2026-08-29: "buttons have hide until hover added by DFS Motion — add
 * hide until enter viewport mobile toggle for that."
 *
 * THIS IS NOT A BUG FIX, AND THE DISTINCTION MATTERS. The block above already
 * shows the widget on every touch device, so nothing is invisible today. This
 * adds a SECOND touch behaviour for authors who want the entrance to still
 * happen on a phone, just triggered by scroll instead of by a pointer that
 * does not exist. Default is unchanged — an author who never opens this
 * control sees exactly what shipped.
 *
 * WHY THE `html.dfs-huh-js` GATE IS NOT OPTIONAL.
 * The hidden state below is the only thing standing between a visitor and the
 * button. If the script fails to parse, is blocked, or the browser has no
 * IntersectionObserver, a rule that hid the widget unconditionally would leave
 * it permanently invisible on that phone — strictly worse than the behaviour it
 * replaced. So the script adds `dfs-huh-js` to <html> ONLY once it has
 * confirmed it can actually observe, and these rules require that class. No
 * script, no hiding, and the page falls back to the proven always-visible
 * state above.
 *
 * It is registered in the HEAD rather than the footer for the same reason: the
 * flag has to land before first paint, or the widget would paint visible and
 * then snap to hidden.
 *
 * SPECIFICITY: the force-visible rule above is (0,3,0). The hidden rule here is
 * (0,5,0) — an extra class plus `:not( .is-inview )` — and the revealed rule
 * matches it at (0,5,0) but comes later, so presence of `.is-inview` decides.
 * No `!important` anywhere.
 */
html.dfs-huh-js .elementor-column .elementor-widget.dfs-huh--col.dfs-huh-mob--view:not( .is-inview ),
html.dfs-huh-js .elementor-section .elementor-widget.dfs-huh--sec.dfs-huh-mob--view:not( .is-inview ),
html.dfs-huh-js .e-con .elementor-widget.dfs-huh--sec.dfs-huh-mob--view:not( .is-inview ) {
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	transform: translate3d(
		calc( var( --dfs-huh-tx, 0 ) * var( --dfs-huh-dist, 16px ) ),
		calc( var( --dfs-huh-ty, 0 ) * var( --dfs-huh-dist, 16px ) ),
		0
	);
	transition: none;
}

@media ( hover: none ) {
	html.dfs-huh-js .elementor-column .elementor-widget.dfs-huh--col.dfs-huh-mob--view.is-inview,
	html.dfs-huh-js .elementor-section .elementor-widget.dfs-huh--sec.dfs-huh-mob--view.is-inview,
	html.dfs-huh-js .e-con .elementor-widget.dfs-huh--sec.dfs-huh-mob--view.is-inview {
		opacity: 1;
		visibility: visible;
		pointer-events: auto;
		transform: none;
		/* The fade-IN duration, the same control the hover path uses. The
		   fade-out control is deliberately not read here: nothing scrolls
		   back out and re-hides, so an out-duration would never run. */
		transition:
			opacity    var( --dfs-huh-dur, 0.45s ) var( --dfs-huh-ease, cubic-bezier( 0.226, 0.861, 0.408, 1.033 ) ),
			transform  var( --dfs-huh-dur, 0.45s ) var( --dfs-huh-ease, cubic-bezier( 0.226, 0.861, 0.408, 1.033 ) ),
			visibility 0s linear;
	}
}


/* Reduced motion: keep the end states, drop the travel — same stance as the
   block further up this file. The widget still appears; it just does not fly. */
@media ( hover: none ) and ( prefers-reduced-motion: reduce ) {
	html.dfs-huh-js .elementor-widget.dfs-huh-mob--view:not( .is-inview ) {
		transform: none;
	}
	html.dfs-huh-js .elementor-widget.dfs-huh-mob--view.is-inview {
		transition: opacity 0.01ms linear, visibility 0s linear;
	}
}

/* --------------------------------------------------------- reduced motion --- */
/* WCAG 2.3.3 — keep the end states, drop the travel. */
@media ( prefers-reduced-motion: reduce ) {
	.elementor-column .elementor-widget.dfs-huh--col,
	.elementor-section .elementor-widget.dfs-huh--sec,
	.e-con .elementor-widget.dfs-huh--sec {
		transform: none;
		transition: opacity 0.01s linear, visibility 0.01s linear;
	}
}
