/**
 * GA Enhancements — the mechanism stylesheet.
 *
 * Everything here is structural: what makes an overlay an overlay, what makes a
 * hover colour apply, how an icon sits beside a label. Nothing here is a design
 * decision — colours, spacing and radii all come through custom properties the
 * PHP writes, or are inherited, so the theme stays in charge of how things look.
 *
 * Loaded on the front end *and* in the editor canvas (see modules/assets.php),
 * because an absolutely positioned overlay with no stylesheet behind it renders
 * as a stray link across the page in either place.
 *
 * ## Not here on purpose
 *
 * The Ollie parent theme, which stays installed, already ships:
 *
 *   body:not(.wp-admin) header:has(> .is-position-sticky) { position: sticky; … }
 *   .ollie-row-stack, .ollie-swap-order, .ollie-sticky-top, .ollie-hide-on-mobile
 *
 * Those are the theme's and are left alone. The one exception is the
 * `.ollie-unstick-mobile` pair, duplicated below so this plugin keeps working if
 * the site is ever reparented off Ollie — identical declarations, so there is
 * nothing for the cascade to resolve.
 */

/* -----------------------------------------------------------------------------
 * Whole-card link — modules/block-link.php
 * -------------------------------------------------------------------------- */

/**
 * `isolation` contains the overlay's z-index inside the card, so a stacking
 * context further up — a sticky header, a modal — can never be painted over by
 * a card that happens to come later in the document.
 */
.is-linked {
	position: relative;
	isolation: isolate;
}

/**
 * `border-radius: inherit` so the focus ring follows a rounded card rather than
 * cutting its corners off. The z-index is on the overlay itself and not only on
 * its siblings, because a Cover with a background image sets its own paint
 * order and the overlay has to beat it.
 */
.is-linked > .ga-card-link {
	position: absolute;
	inset: 0;
	z-index: 1;
	border-radius: inherit;
	text-decoration: none;
}

.is-linked > .ga-card-link:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: 2px;
}

/**
 * Everything a visitor can interact with has to sit above the overlay, or the
 * card swallows the one link that goes somewhere else. Links that merely repeat
 * the overlay's own destination are exempt: promoting a linked featured image
 * above the Cover's own gradient makes the gradient look like it vanished.
 *
 * The two `:not()`s qualify the whole list, not just the `a` branch — a
 * carousel script that stamps `tabindex="0"` onto the links inside its active
 * slides would otherwise re-catch, through the `[tabindex]` branch, the very
 * anchors they excuse.
 *
 * `:where()` keeps this at zero specificity, so the theme can override any of
 * it without reaching for `!important`.
 */
.is-linked
	:where(
		:is(a, button, input, select, textarea, summary, label, [tabindex]:not([tabindex="-1"])):not(.ga-card-link):not(
				.ga-card-link-echo
			)
	) {
	position: relative;
	z-index: 2;
}

/* -----------------------------------------------------------------------------
 * Sticky position — modules/sticky.php
 * -------------------------------------------------------------------------- */

/**
 * A `<header>` whose sticky child asked for it. The class and both custom
 * properties are set by assets/js/frontend/sticky.js, which is the only place
 * that can know a sticky Group is a direct child of a header.
 *
 * The z-index fallback is 100, which is what Ollie hard-coded and what the
 * theme's own header rule uses — high enough to clear page content, low enough
 * to stay under a modal.
 */
header.ga-sticky-header {
	position: sticky;
	top: var(--ga-header-sticky-top, 0px);
	z-index: var(--ga-header-sticky-z, 100);
}

/**
 * Set once a hidden scroll-up header has finished sliding away, so clicks pass
 * through the full-width wrapper it leaves behind instead of hitting nothing.
 */
header.ga-sticky-header-behind,
.site-header.ga-sticky-header-behind {
	z-index: -1;
}

/**
 * The scroll-up header. The transform is the whole mechanism; the module's
 * front-end script only toggles the two state classes.
 *
 * `--sticky-full-offset` rather than 100%: an element pinned 160px down the
 * viewport has to travel its own height *plus* that offset to clear the top,
 * or it stops with a strip of itself still showing.
 */
.wp-block-group[data-sticky-on-scroll-up="true"] {
	transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.wp-block-group[data-sticky-on-scroll-up="true"].is-sticky-hidden {
	transform: translateY(calc(-100% - var(--sticky-full-offset, 0px)));
}

.wp-block-group[data-sticky-on-scroll-up="true"].is-sticky-active {
	transform: translateY(0);
}

/**
 * A header must not slide away while a modal it contains is open — the menu
 * would scroll out from under the person using it. `.has-modal-open` is core's
 * class on `<body>` while a Navigation overlay is open, and Tour Operator's
 * modal script sets it too.
 */
.wp-block-group[data-sticky-on-scroll-up="true"]:has(.has-modal-open),
.has-modal-open .wp-block-group[data-sticky-on-scroll-up="true"] {
	transform: none;
	transition: none;
}

/**
 * The header still has to get out of the way for someone who has asked for less
 * motion — it is a layout behaviour, not an embellishment — so the transform
 * stays and only the animation between the two states goes.
 */
@media (prefers-reduced-motion: reduce) {
	.wp-block-group[data-sticky-on-scroll-up="true"] {
		transition: none;
	}
}

/**
 * Duplicated from ollie/style.css:206-212 so this plugin does not depend on the
 * parent theme. Same breakpoint, same declaration.
 *
 * Last in this section on purpose: `header:has(> .ollie-unstick-mobile)` and
 * `header.ga-sticky-header` have equal specificity, so source order decides, and
 * the mobile opt-out has to be the one that wins.
 */
@media (max-width: 781px) {
	.ollie-unstick-mobile {
		position: static;
	}

	header:has(> .ollie-unstick-mobile) {
		position: static;
	}
}

/* -----------------------------------------------------------------------------
 * Hover colours — modules/hover-colors.php
 * -------------------------------------------------------------------------- */

/**
 * One transition rule for all three properties rather than Ollie's sixteen
 * combinations of them. Transitioning a property that never changes costs
 * nothing, and the shorthand cannot get out of step with itself.
 */
.has-hover__color:not(.wp-block-button),
.has-hover__background-color:not(.wp-block-button),
.has-hover__border-color:not(.wp-block-button) {
	transition-property: color, background-color, border-color;
	transition-duration: var(--hover-transition-duration, 200ms);
	transition-timing-function: var(--hover-transition-timing, ease);
}

/**
 * `!important` is load-bearing, not decoration: core writes the block's own
 * colour as an inline `style="color:…"` on this same element, and an inline
 * declaration beats any selector. Without it the hover colour would never apply
 * to a block that also has a base colour — which is every one of them.
 */
.has-hover__color:not(.wp-block-button):hover {
	color: var(--hover-color) !important;
}

.has-hover__background-color:not(.wp-block-button):hover {
	background-color: var(--hover-background-color) !important;
}

.has-hover__border-color:not(.wp-block-button):hover {
	border-color: var(--hover-border-color) !important;
}

/**
 * Button is the exception: the attributes sit on the `.wp-block-button`
 * wrapper, but the thing that carries the colour is the `.wp-element-button`
 * inside it. Hovering the wrapper is what a visitor means by hovering the
 * button, so the trigger stays on the wrapper and the effect moves inwards.
 */
.wp-block-button.has-hover__color .wp-element-button,
.wp-block-button.has-hover__background-color .wp-element-button,
.wp-block-button.has-hover__border-color .wp-element-button {
	transition-property: color, background-color, border-color;
	transition-duration: var(--hover-transition-duration, 200ms);
	transition-timing-function: var(--hover-transition-timing, ease);
}

.wp-block-button.has-hover__color:hover .wp-element-button {
	color: var(--hover-color) !important;
}

.wp-block-button.has-hover__background-color:hover .wp-element-button {
	background-color: var(--hover-background-color) !important;
}

.wp-block-button.has-hover__border-color:hover .wp-element-button {
	border-color: var(--hover-border-color) !important;
}

/* -----------------------------------------------------------------------------
 * Button icons — modules/button-icons.php
 * -------------------------------------------------------------------------- */

/**
 * The label and the icon become flex items so the icon can be ordered and
 * spaced.
 *
 * `--ga-icon-gap` and `--ga-icon-size` are set on the `.wp-block-button`
 * wrapper and inherit down. They are custom properties rather than direct
 * declarations because the editor has to be able to set the same two values,
 * and what it is styling there is a pseudo-element standing in for the icon
 * (modules/button-icons.php, editor_preview_css) — which JavaScript cannot
 * reach, but which inherits a custom property just fine. The PHP writes neither
 * property unless an editor chose something other than these defaults.
 */
.wp-block-button[class*="has-icon__"] .wp-block-button__link {
	display: flex;
	align-items: center;
	gap: var(--ga-icon-gap, 0.5em);
}

/**
 * Rich text formatting inside the label arrives as inline elements; zeroing
 * their line-height stops a `<strong>` from making the row taller than the
 * icon.
 */
.wp-block-button[class*="has-icon__"] .wp-block-button__link span {
	line-height: 0;
}

.wp-block-button[class*="has-icon__"] .wp-block-button__link svg {
	flex-shrink: 0;
	color: currentColor;
	fill: currentColor;
}

/**
 * Doubled class for specificity: core's button styles reach the same element,
 * and a single class loses to them.
 */
.wp-block-button[class*="has-icon__"][class*="has-icon__"] .wp-block-button__link svg {
	width: var(--ga-icon-size, 1.2em);
	height: var(--ga-icon-size, 1.2em);
}

/**
 * A flex row does not inherit the justification core puts on the buttons
 * container, so it has to be restated for each alignment.
 */
.wp-block-buttons.is-content-justification-left
	.wp-block-button[class*="has-icon__"]
	.wp-block-button__link {
	justify-content: flex-start;
}

.wp-block-buttons.is-content-justification-center
	.wp-block-button[class*="has-icon__"]
	.wp-block-button__link {
	justify-content: center;
}

.wp-block-buttons.is-content-justification-right
	.wp-block-button[class*="has-icon__"]
	.wp-block-button__link {
	justify-content: flex-end;
}

.wp-block-buttons.is-content-justification-space-between
	.wp-block-button[class*="has-icon__"]
	.wp-block-button__link {
	justify-content: space-between;
}

/**
 * `iconPositionLeft` moves the icon rather than reversing the row, so the
 * label's own text alignment is untouched.
 */
.wp-block-button.has-icon-position__left .wp-block-button__link svg {
	order: -1;
}

/* -----------------------------------------------------------------------------
 * Mobile column order — modules/column-order.php
 * -------------------------------------------------------------------------- */

/**
 * 781px is core's own columns-stack breakpoint, so the ordering takes effect on
 * exactly the widths where the columns have become a single stack and an order
 * is meaningful.
 *
 * The second rule is the half that makes the first one behave. `order` defaults
 * to 0, so a column numbered 1 would otherwise sit *behind* every sibling that
 * was left alone. Pushing the unnumbered ones to the back instead means
 * numbering one column of three reads as "this one first, the rest after",
 * which is how the templates were authored.
 */
@media (max-width: 781px) {
	.wp-block-column[style*="--order-mobile"] {
		order: var(--order-mobile, 0);
	}

	.wp-block-columns > .wp-block-column:not([style*="--order-mobile"]) {
		order: 999;
	}
}
