/**
 * AdsNxt global motion system -- one place for every entrance/hover/
 * interaction transition site-wide, so it's documented once instead of
 * scattered per page. Loaded last (see inc/enqueue.php) so its rules win
 * the cascade against components.css/base.css for the same selectors
 * without needing !important anywhere in this file.
 *
 * Reduced motion: this file intentionally does NOT duplicate a
 * `prefers-reduced-motion` block -- base.css already has a sitewide
 * `*, *::before, *::after { transition-duration:.001ms !important;
 * animation-duration:.001ms !important; }` rule that already covers every
 * class below. Content still becomes visible/interactive, it just does so
 * without the animated transition.
 *
 * ============================================================
 * REVEAL-ON-SCROLL ("premium content settle" system)
 * ============================================================
 * Usage: add one of these classes in PHP/markup --
 *   .reveal            base: fade in only
 *   .reveal-up         fade in + rise ~24px (default choice for most content)
 *   .reveal-fade       alias of .reveal, kept for readability at call sites
 *   .reveal-scale      fade in + very slight scale up from 0.97
 * The assets/js/adsnxt-interactions.js IntersectionObserver block adds
 * `.is-visible` the first time each element scrolls into view (one-time,
 * observer.unobserve()'d after). Only elements inside a `.js`-tagged <html>
 * (set by the tiny inline bootstrap script in header.php, before first
 * paint) ever start hidden -- with JS disabled, `.js` is never added, so
 * .reveal* elements simply render normally visible. No flash-of-hidden-
 * content risk, no dependency on JS for reachability.
 *
 * Stagger: adsnxt-interactions.js sets `--reveal-i` (child index, capped)
 * as an inline custom property on each revealed item inside a common
 * parent; multiply it below into a transition-delay for the "card 1, card
 * 2, card 3..." stagger effect.
 */

.js .reveal,
.js .reveal-up,
.js .reveal-fade,
.js .reveal-scale {
	transition: opacity 500ms cubic-bezier(0.22, 1, 0.36, 1), transform 500ms cubic-bezier(0.22, 1, 0.36, 1);
	transition-delay: calc(var(--reveal-i, 0) * 70ms);
	will-change: opacity, transform;
}

.js .reveal,
.js .reveal-fade {
	opacity: 0;
}
.js .reveal.is-visible,
.js .reveal-fade.is-visible {
	opacity: 1;
}

.js .reveal-up {
	opacity: 0;
	transform: translateY(26px);
}
.js .reveal-up.is-visible {
	opacity: 1;
	transform: translateY(0);
}

.js .reveal-scale {
	opacity: 0;
	transform: scale(0.97);
}
.js .reveal-scale.is-visible {
	opacity: 1;
	transform: scale(1);
}

/* Once revealed, drop will-change so the browser stops reserving a GPU
   layer for elements that will never animate again. */
.js .reveal.is-visible,
.js .reveal-up.is-visible,
.js .reveal-fade.is-visible,
.js .reveal-scale.is-visible {
	will-change: auto;
}

/* ============================================================
 * PAGE-LOAD ENTRANCE (distinct from scroll-reveal above -- fires once,
 * immediately, on every page load; pure CSS, no JS dependency at all so
 * it still works with JS disabled)
 * ============================================================ */
@keyframes adnxt-page-enter {
	from {
		opacity: 0;
		transform: translateY(14px);
	}
	to {
		opacity: 1;
		transform: none;
	}
}

#adsnxt-main {
	animation: adnxt-page-enter 450ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

/* ============================================================
 * HOMEPAGE HERO ENTRANCE SEQUENCE (adsnxt/hero block only)
 * ============================================================
 * A single choreographed page-load sequence, distinct from the generic
 * #adsnxt-main fade+rise above: trust row -> headline (staggered by
 * *rendered* line, measured live in JS so it's correct at any viewport
 * width, never a guessed line break) -> subhead -> CTA buttons
 * (staggered) -> booking caption. Driven by adsnxt-interactions.js
 * adding .is-visible on a setTimeout timeline -- deliberately not a CSS
 * animation-fill-mode sequence, for the same reason #adsnxt-main's fix
 * above exists: a fill-mode animation stays "current" indefinitely and
 * can trap position:fixed descendants. Gated behind .js so with JS
 * disabled every piece renders normally visible immediately.
 */
.js .adnxt-hero__trust,
.js .adnxt-hero__subhead,
.js .adnxt-hero__booking {
	opacity: 0;
	transition: opacity 450ms cubic-bezier(0.22, 1, 0.36, 1), transform 450ms cubic-bezier(0.22, 1, 0.36, 1);
}
.js .adnxt-hero__trust {
	transform: translateY(14px);
}
.js .adnxt-hero__subhead {
	transform: translateY(16px);
}
.js .adnxt-hero__booking {
	transform: none;
	transition-duration: 400ms;
}

/* Explicit combined transition (not just opacity/transform) so this
   higher-specificity rule doesn't blank out .adnxt-btn's own hover
   transition (background-color/color/box-shadow) -- both need to be
   listed together since the `transition` shorthand fully replaces
   whichever rule loses the cascade, it doesn't merge across rules. */
.js .adnxt-hero__ctas > * {
	opacity: 0;
	transform: translateY(12px);
	transition: opacity 350ms cubic-bezier(0.22, 1, 0.36, 1), transform 350ms cubic-bezier(0.22, 1, 0.36, 1), background-color 220ms ease, color 220ms ease, box-shadow 220ms ease;
}

.js .adnxt-hero__word {
	display: inline-block;
	opacity: 0;
	transform: translateY(30px);
	transition: opacity 550ms cubic-bezier(0.22, 1, 0.36, 1), transform 550ms cubic-bezier(0.22, 1, 0.36, 1);
	transition-delay: calc(var(--line-i, 0) * 100ms);
}

.js .adnxt-hero__trust.is-visible,
.js .adnxt-hero__subhead.is-visible,
.js .adnxt-hero__booking.is-visible,
.js .adnxt-hero__ctas > *.is-visible,
.js .adnxt-hero__word.is-visible {
	opacity: 1;
	transform: translateY(0);
}

/* ============================================================
 * CARD HOVER POLISH
 * ============================================================
 * .adsnxt-archive__card is the shared Service-hub / Portfolio-hub /
 * Related-services card markup (template-parts/services/card.php,
 * template-parts/portfolio/card.php, patterns/*-grid.php) -- one rule
 * covers every service and portfolio card site-wide.
 */
.adsnxt-archive__card {
	transition: transform 300ms ease, box-shadow 300ms ease, border-color 300ms ease;
}
.adsnxt-archive__card:hover {
	transform: translateY(-6px);
	box-shadow: var(--shadow-soft);
}

/* adnxt-service-card: the homepage/service-hub tile grid (Section/Why
   AdsNxt pillar cards use the same class via adsnxt_service_card()). */
.adnxt-service-card {
	transition: transform 300ms ease, box-shadow 300ms ease;
}
.adnxt-service-card:hover {
	transform: translateY(-6px);
	box-shadow: var(--shadow-soft);
}

/* Generic content card (adsnxt_card_open()) -- covers case-study panels,
   pricing cards, and any other .adnxt-card instance not already carrying
   its own more specific hover rule (e.g. .adnxt-card--testimonial). */
.adnxt-card {
	transition: box-shadow 300ms ease;
}

/* ============================================================
 * BUTTON HOVER POLISH -- subtle scale only, no colour change
 * ============================================================
 * Every button-shaped CTA site-wide (.adnxt-btn -- all 4 variants, via
 * adsnxt_button() -- plus the two native form buttons that duplicate
 * .is-primary's look outside that helper: the CF7 contact-form submit
 * button and the 404 page's search submit) gets the exact same
 * interaction: a small scale on hover, a slightly smaller scale on
 * press. Colours, background, border-radius, dimensions, typography are
 * all untouched -- this rule only ever sets `transform`.
 *
 * :active is declared after :hover with the same specificity so a real
 * click (hover+active at once) correctly shows the press-down scale
 * instead of the hover scale winning by source order.
 *
 * <input type=submit> note: an earlier version of this system used a
 * ::before sweep layer, which silently never rendered on the two native
 * <input> buttons above (::before doesn't generate on <input> in
 * Chrome/most browsers) -- moot now since this version uses only
 * `transform` directly on the element itself, which works identically
 * on <a>, <button>, and <input>.
 */
.adnxt-btn,
.adnxt-contact-form input.wpcf7-submit,
.adsnxt-404 .search-form .search-submit,
.adnxt-icon-btn {
	transition: transform 250ms ease;
}
.adnxt-btn:hover,
.adnxt-btn:focus-visible,
.adnxt-contact-form input.wpcf7-submit:hover,
.adnxt-contact-form input.wpcf7-submit:focus-visible,
.adsnxt-404 .search-form .search-submit:hover,
.adsnxt-404 .search-form .search-submit:focus-visible,
.adnxt-icon-btn:hover,
.adnxt-icon-btn:focus-visible {
	transform: scale(1.02);
}
.adnxt-btn:active,
.adnxt-contact-form input.wpcf7-submit:active,
.adsnxt-404 .search-form .search-submit:active,
.adnxt-icon-btn:active {
	transform: scale(0.97);
	transition-duration: 100ms;
}

/* ============================================================
 * LINK POLISH -- footer/nav text links and inline content links
 * ============================================================ */
.adnxt-mega-menu__links a,
.adnxt-footer a:not(.adnxt-icon-btn) {
	transition: opacity 220ms ease, color 220ms ease;
}
.adnxt-mega-menu__links a:hover,
.adnxt-footer a:not(.adnxt-icon-btn):hover {
	opacity: 0.7;
}

/* ============================================================
 * IMAGE HOVER SCALE (opt-in utility -- add adnxt-img-zoom to a wrapper
 * with overflow:hidden around an <img>; the masonry/portfolio overlay
 * already has its own reveal treatment in utilities.css/site.css and is
 * left untouched here)
 * ============================================================ */
.adnxt-img-zoom {
	overflow: hidden;
	display: block;
}
.adnxt-img-zoom img {
	transition: transform 500ms cubic-bezier(0.22, 1, 0.36, 1);
}
.adnxt-img-zoom:hover img {
	transform: scale(1.05);
}

/* ============================================================
 * MEGA MENU -- smoother open/close than the previous hard display:none/
 * grid toggle. Same trigger selectors as base.css (:hover / :focus-within
 * / .is-mega-open, set by adsnxt-interactions.js's forgiving-hover timer)
 * so no JS or HTML changed -- these rules simply load after base.css's
 * and win the cascade for the same selectors, replacing display:none with
 * a visibility+opacity+transform fade that keeps the panel out of the tab
 * order (visibility:hidden) and out of the way (pointer-events:none)
 * until it's actually open.
 * ============================================================ */
.adnxt-mega-menu {
	display: grid;
	opacity: 0;
	visibility: hidden;
	pointer-events: none;
	transform: translateY(-8px);
	transition: opacity 220ms ease, transform 220ms ease, visibility 0s linear 220ms;
}
.adnxt-header__nav-item.has-mega-menu:hover .adnxt-mega-menu,
.adnxt-header__nav-item.has-mega-menu:focus-within .adnxt-mega-menu,
.adnxt-header__nav-item.has-mega-menu.is-mega-open .adnxt-mega-menu {
	opacity: 1;
	visibility: visible;
	pointer-events: auto;
	transform: translateY(0);
	transition: opacity 220ms ease, transform 220ms ease, visibility 0s linear 0s;
}

/* ============================================================
 * MOBILE MENU -- same idea as the mega menu (fade + slight rise), but
 * driven by a second class (.is-menu-visible) that adsnxt-interactions.js
 * adds one tick after .is-open, rather than a fill-mode CSS animation.
 * Deliberately NOT using animation-fill-mode:both/forwards here after
 * finding live (see the #adsnxt-main page-load animation fix in
 * adsnxt-interactions.js) that such an animation stays "current"
 * indefinitely per spec, not just for its visible duration -- a plain
 * transition between two real class states doesn't have that problem and
 * needs no cleanup. base.css's display:none/flex toggle on .is-open is
 * unchanged, so the menu is exactly as functional as before with JS off
 * or in browsers that don't run the class-toggle timing right -- it would
 * just appear instantly instead of fading.
 * ============================================================ */
.adnxt-mobile-menu.is-open {
	opacity: 0;
	transform: translateY(-8px);
	transition: opacity 220ms ease, transform 220ms ease;
}
.adnxt-mobile-menu.is-open.is-menu-visible {
	opacity: 1;
	transform: translateY(0);
}

.adnxt-mobile-menu__services-toggle svg {
	transition: transform 220ms ease;
}

/* ============================================================
 * FAQ POLISH -- native <details>/<summary> already reaches its content
 * with zero JS, and base.css already smooths the chevron rotation
 * (.adnxt-faq-item summary svg, transition:transform 0.2s ease). This
 * just adds a subtle hover cue on the question row. Deliberately not
 * animating the answer's reveal itself (animating <details> height
 * reliably needs JS and risks the exact accessibility/keyboard
 * regressions the brief said not to cause), so open/close stays instant
 * and 100% native.
 * ============================================================ */
.adnxt-faq-item summary {
	transition: opacity 220ms ease;
	cursor: pointer;
}
.adnxt-faq-item summary:hover {
	opacity: 0.7;
}
