/* ===== SGS Device Visibility Extension ===== */

/*
 * Breakpoints:
 *   Mobile:  0 – 599px
 *   Tablet:  600px – 1023px
 *   Desktop: 1024px+
 *
 * CSS-only hiding preserves SEO (content remains in DOM).
 * display:none also hides from screen readers — acceptable
 * for genuine device-specific content patterns.
 */

/*
 * The .sgs-hide-mobile / .sgs-hide-tablet / .sgs-hide-desktop media queries
 * are GENERATED from the canonical SGS_Breakpoints source (mobile <= 767px,
 * tablet 768–1023px, desktop >= 1024px) and injected as inline CSS by
 * includes/device-visibility.php — one breakpoint source shared with the
 * Spec 37 FR-37-16 responsive engine (FP-1). Do NOT re-add a hardcoded pair here.
 */

/* ===== Button Fill Animation ===== */

@media (prefers-reduced-motion: no-preference) {
	.sgs-button-fill {
		position: relative;
		overflow: hidden;
		isolation: isolate;
	}

	.sgs-button-fill::before {
		content: '';
		position: absolute;
		inset: 0;
		background-color: var(--sgs-button-fill-colour, currentColor);
		opacity: 0.15;
		transform: scaleX(0);
		transform-origin: left center;
		transition: transform var(--wp--custom--duration--medium, 0.3s) var(--wp--custom--easing--ease, ease);
		z-index: -1;
	}

	.sgs-button-fill:hover::before {
		transform: scaleX(1);
	}
}

/* ===== Micro-Interactions & Hover Effects (#115, #122, #125) ===== */

@media (prefers-reduced-motion: no-preference) {
	/* Underline slide-in on links (#115) */
	/* Targets navigation links and generic text links within content */
	/* A list link carrying any sgs- class is excluded, the same way
	 * .wp-block-button__link is: an SGS block's link (nav menu items, table of
	 * contents, business info, icon list, filter terms, account cards) owns its
	 * hover treatment through the block's own controls and rules, and this rule's
	 * specificity (0,3,2 for .entry-content li a:not(...)) would beat them inside
	 * page or post content: it strips a menu item's hover text-decoration and the
	 * TOC's active underline (WCAG 1.4.1) and stacks a second ::after underline.
	 * .sgs-text-link opts in through its own selector above. */
	.wp-block-navigation-item__content,
	.sgs-text-link,
	.entry-content p a:not([class*="sgs-"]),
	.entry-content li a:not(.wp-block-button__link):not([class*="sgs-"]) {
		position: relative;
		text-decoration: none;
	}

	.wp-block-navigation-item__content::after,
	.sgs-text-link::after,
	.entry-content p a:not([class*="sgs-"])::after,
	.entry-content li a:not(.wp-block-button__link):not([class*="sgs-"])::after {
		content: '';
		position: absolute;
		width: 100%;
		height: 1px;
		bottom: 0;
		left: 0;
		background-color: currentColor;
		transform: scaleX(0);
		transform-origin: bottom right;
		transition: transform var(--wp--custom--duration--medium, 0.3s) var(--wp--custom--easing--ease, ease);
	}

	.wp-block-navigation-item__content:hover::after,
	.sgs-text-link:hover::after,
	.entry-content p a:not([class*="sgs-"]):hover::after,
	.entry-content li a:not(.wp-block-button__link):not([class*="sgs-"]):hover::after {
		transform: scaleX(1);
		transform-origin: bottom left;
	}

	/* Button fill animation (#122) */
	.wp-block-button__link,
	.sgs-cta-button {
		position: relative;
		overflow: hidden;
		z-index: 1;
		transition: color var(--wp--custom--duration--medium, 0.3s) var(--wp--custom--easing--ease, ease);
	}

	.wp-block-button__link::before,
	.sgs-cta-button::before {
		content: '';
		position: absolute;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		background-color: var(--wp--preset--color--foreground, #1E1E1E);
		z-index: -1;
		transform: scaleX(0);
		transform-origin: left;
		transition: transform var(--wp--custom--duration--medium, 0.3s) var(--wp--custom--easing--ease, ease);
	}

	.wp-block-button__link:hover::before,
	.sgs-cta-button:hover::before {
		transform: scaleX(1);
	}

	.wp-block-button__link:hover,
	.sgs-cta-button:hover {
		color: var(--wp--preset--color--background, #ffffff) !important;
	}

	/* Icon slide (#125) */
	/* Targeting SVG icons inside buttons */
	.wp-block-button__link svg,
	.sgs-cta-button svg,
	.wp-block-button__link i,
	.sgs-cta-button i,
	.wp-block-button__link .sgs-icon,
	.sgs-cta-button .sgs-icon {
		transition: transform var(--wp--custom--duration--medium, 0.3s) var(--wp--custom--easing--ease, ease);
	}

	.wp-block-button__link:hover svg,
	.sgs-cta-button:hover svg,
	.wp-block-button__link:hover i,
	.sgs-cta-button:hover i,
	.wp-block-button__link:hover .sgs-icon,
	.sgs-cta-button:hover .sgs-icon {
		transform: translateX(4px);
	}
}

/*
 * ===== Block Link Extension — stretched-link overlay pattern =====
 * includes/hover-effects/block-link.php injects an EMPTY `sgs-block-link-overlay` <a>
 * as the block root's LAST CHILD (a sibling of the content), never a
 * wrapper — this is what makes nested <a>/<button> elements already inside
 * the block valid HTML. `.sgs-has-block-link` on the block ROOT (added
 * server-side alongside the overlay) establishes the positioning context;
 * the overlay stretches to fill it and sits BELOW real interactive
 * descendants, which are raised above it via z-index so they stay clickable.
 */
.sgs-has-block-link {
	position: relative;
}

.sgs-block-link-overlay {
	position: absolute;
	inset: 0;
	z-index: 1;
	/* Empty element — no visible affordance or text; its accessible name
	   comes from the required aria-label injected server-side. */
	display: block;
}

/*
 * The LENDER — the block's own real link, which the extension found in the
 * rendered output and gave the stretched surface to
 * (`includes/hover-effects/link-overlay.php::insert_block_link_overlay`).
 *
 * It is the element a keyboard user lands on. The focus system (theme
 * `focus.css`) draws its indicator, so `sgs-block-link-source` is a marker
 * class with no rule of its own.
 */

/*
 * Real interactive descendants stay clickable ABOVE the overlay.
 *
 * Two rules, not one, because the two declarations need opposite strengths.
 * `z-index` keeps full specificity: every real control wants to be above the
 * overlay, always. `position: relative` is emitted at ZERO specificity
 * (`:where()` contributes nothing, and the `:not()` sits INSIDE it so it
 * contributes nothing either — only the `a` branch needs that exclusion, the
 * overlay is never a <button>), because it exists solely to give a
 * STATICALLY positioned control a stacking position. A control that already
 * positions itself must keep its own value: sgs/product-card's wishlist
 * heart is `position: absolute; top: 10px; right: 10px` at (0,1,0), which a
 * combined rule at (0,2,1) overrode — dropping the heart out of the image's
 * top-right corner into normal flow. Its own `z-index: 2` matches what this
 * rule asks for anyway.
 */
.sgs-has-block-link :is(a, button):not(.sgs-block-link-overlay) {
	z-index: 2;
}

:where(.sgs-has-block-link) :where(a:not(.sgs-block-link-overlay), button) {
	position: relative;
}

/*
 * A direct child must not create a STACKING CONTEXT, or the z-index above
 * cannot reach past it.
 *
 * sgs/container gives every child `position: relative; z-index: 1`
 * (`container/style.css`), and every sgs/product-card render branch returns
 * through SGS_Container_Wrapper, so `.product-card__media` is such a child.
 * That scoped the wishlist heart's `z-index: 2` INSIDE the photo box, while
 * the overlay — a LATER sibling at the same z-index 1 — won on source order
 * and took the clicks. The same trap hid the colour swatches, which sit in
 * `.product-card-body`, another child at z-index 1.
 *
 * `z-index: auto` removes the context but keeps each child's `position`, which
 * the heart still needs as its containing block. The overlay is excluded
 * because it needs its own z-index. container/style.css asks for an explicit
 * z-index on one of the two siblings rather than re-specifying that rule; this
 * is that, applied on the overlay's side so no other container child changes.
 */
.sgs-has-block-link > *:not(.sgs-block-link-overlay) {
	z-index: auto;
}

/* ===== Image Controls Extension ===== */

/*
 * Applied when a block declares supports.sgs.imageControls: true and has
 * one or more image-control attributes set. PHP (image-controls.php) injects
 * the class sgs-has-image-controls and the corresponding CSS custom properties
 * (--sgs-object-position, --sgs-object-fit, --sgs-max-width,
 * --sgs-height-desktop, etc.).
 *
 * The inner-image selectors cover the most common SGS image markup shapes:
 *   - Direct <img> child (decorative-image, info-box media slot)
 *   - .wp-block-image > img (core image block inside an SGS block)
 *   - figure > img (team-member, testimonial avatar)
 *   - .sgs-responsive-logo__picture > img (sgs/responsive-logo's <a><picture><img>
 *     shape — added alongside sgsColourTreatment, the first image-control
 *     attribute a logo instance actually needs; existing selectors above never
 *     matched this block's markup, so its own colour-treatment escaped every
 *     prior image-control rule silently until this addition)
 */

/*
 * object-position / max-width / object-fit were previously split into two
 * rule blocks, with object-fit gated on `[style*="--sgs-object-fit"]`. That
 * gate — and the equivalent one that used to sit on the desktop-height rule
 * below — can NEVER match: the PHP (image-controls.php) emits these as CSS
 * custom properties inside a scoped `<style>` rule keyed to a
 * `.sgs-imgctl-{uid}` class, NEVER as an inline `style=""` attribute (Spec 32
 * no-inline contract, FR-32-11). Both rules were dead on the frontend for
 * every opted-in block. Un-gated to match what PHP actually emits, using the
 * same unconditional var()-fallback pattern already used below for the
 * tablet/mobile height rules (which read the inherited custom property
 * directly rather than gating on a style attribute) — and merged into one
 * rule since the selector list is now identical for all three properties.
 */
.sgs-has-image-controls > img,
.sgs-has-image-controls .wp-block-image > img,
.sgs-has-image-controls figure > img,
.sgs-has-image-controls .sgs-responsive-logo__picture > img,
.sgs-has-image-controls > video,
.sgs-has-image-controls .wp-block-image > video,
.sgs-has-image-controls figure > video {
	object-position: var(--sgs-object-position, center center);
	object-fit: var(--sgs-object-fit, cover);
	max-width: var(--sgs-max-width, 100%);
	filter: var(--sgs-image-filter, none);
}

/* Desktop height (also the base for tablet/mobile fallbacks). */
.sgs-has-image-controls > img,
.sgs-has-image-controls .wp-block-image > img,
.sgs-has-image-controls figure > img {
	height: var(--sgs-height-desktop, auto);
}

/* Tablet height — falls back to desktop value when not set. */
@media (min-width: 768px) and (max-width: 1023px) {
	.sgs-has-image-controls > img,
	.sgs-has-image-controls .wp-block-image > img,
	.sgs-has-image-controls figure > img {
		height: var(--sgs-height-tablet, var(--sgs-height-desktop, auto));
	}
}

/* Mobile height — falls back to the tablet value, then desktop, when not set (a narrower device inherits the next wider one). */
@media (max-width: 767px) {
	.sgs-has-image-controls > img,
	.sgs-has-image-controls .wp-block-image > img,
	.sgs-has-image-controls figure > img {
		height: var(--sgs-height-mobile, var(--sgs-height-tablet, var(--sgs-height-desktop, auto)));
	}
}

/* ===== Shared BackgroundPanel editor-canvas preview (2026-08-26) =====
 *
 * Every block that mounts the shared `BackgroundPanel` control
 * (`src/blocks/container/components/ContainerWrapperControls.js` —
 * sgs/container, sgs/multi-button, sgs/physics-canvas, sgs/site-footer,
 * sgs/site-header, sgs/trust-bar; sgs/hero has its own equivalent copy,
 * out of scope here) writes background image/video and overlay attributes that `SGS_Container_Wrapper::render()` paints on the
 * FRONTEND. Until this pass only sgs/container built a JS+CSS mirror so the
 * EDITOR CANVAS showed the same thing — CHECK A finding group
 * "BackgroundPanel canvas-preview gap"
 * (reports/2026-08-26-check-a-triage-group-b.md, root-cause group 1).
 *
 * `src/utils/background-preview.js`'s `backgroundPreview()` is the ONE JS
 * mirror every adopting block now calls; it writes the `--sgs-ed-bg-*` /
 * `--sgs-ed-overlay-*` custom properties consumed below and applies the
 * `.sgs-ed-has-bg-media` / `.sgs-ed-has-overlay` marker classes these rules
 * are gated on, so a block instance with no background image/overlay set
 * never grows an empty pseudo-element.
 *
 * This stylesheet is the SHARED editor extension stylesheet — loaded into
 * the editor canvas IFRAME via `enqueue_block_assets` (iframe-aware; see
 * `includes/class-sgs-blocks.php::enqueue_editor_extension_styles()`), which
 * is why these rules live here rather than being duplicated into six
 * per-block `editor.css` files. It is ALSO loaded on the frontend under the
 * `sgs-extensions` handle — harmless there, since only `edit.js` ever
 * applies these marker classes, never `render.php`.
 */
.sgs-ed-has-bg-media {
	position: relative;
}

.sgs-ed-has-bg-media::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	background-image: var( --sgs-ed-bg-image );
	background-size: var( --sgs-ed-bg-size, cover );
	background-position: var( --sgs-ed-bg-position, center center );
	background-repeat: var( --sgs-ed-bg-repeat, no-repeat );
	background-attachment: var( --sgs-ed-bg-attachment, scroll );
}

.sgs-ed-has-overlay {
	position: relative;
}

.sgs-ed-has-overlay::after {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	pointer-events: none;
	background-image: var( --sgs-ed-overlay-image, none );
	background-color: var( --sgs-ed-overlay-colour, transparent );
	opacity: var( --sgs-ed-overlay-opacity, 1 );
	mix-blend-mode: var( --sgs-ed-overlay-blend, normal );
}

/* ===== Product field fallback link =====
 *
 * `Product_Bindings::empty_value()` (includes/class-product-bindings.php)
 * returns `<a class="sgs-field-fallback">` when a product has no value for a
 * bound field and the binding sets `fallback` + `fallback_link` ("Ask us").
 * It is a button, not prose: the anchor fills its whole cell so the cell is the
 * tap target, and it is painted from the SAME per-client tokens as sgs/button's
 * primary preset (`--wp--custom--button-presets--primary--*`, theme-snapshot
 * `buttonPresets.primary`), with the same theme-token fallbacks, so a client
 * restyles it in the Site Editor's button preset and nowhere else. A real value
 * is never wrapped in it and stays plain text.
 *
 * Hover BRIGHTENS the resting fill and border by mixing them toward white,
 * whatever the client's preset hover colours are (a preset's own hover colour
 * may darken, which this link never does). It deliberately does not take the
 * preset's hover lift: a lift would slide the target off a pointer resting on
 * its lower edge and flicker the hover state.
 *
 * `a.` raises specificity above the theme's `:root :where(a)` link rules
 * (underline, link colour, link focus colour). Hover gets the touch-safe guard
 * (`:where(:root:not(.sgs-touch-input))`); `:focus-visible` stays outside
 * it because it is keyboard-reachable.
 */
a.sgs-field-fallback,
a.sgs-field-fallback:visited {
	--sgs-ff-bg: var(--wp--custom--button-presets--primary--background, var(--wp--preset--color--primary));
	--sgs-ff-border: var(--wp--custom--button-presets--primary--border, var(--wp--preset--color--primary));
	--sgs-ff-color: var(--wp--custom--button-presets--primary--text, var(--wp--preset--color--text-inverse));
	display: flex;
	align-items: center;
	justify-content: center;
	box-sizing: border-box;
	width: 100%;
	min-height: 44px;
	padding: 0 var(--wp--preset--spacing--20, 0.5rem);
	border: var(--wp--custom--button-presets--primary--border-width, 2px) solid var(--sgs-ff-border);
	border-radius: var(--wp--custom--button-presets--primary--border-radius, 10px);
	background-color: var(--sgs-ff-bg);
	color: var(--sgs-ff-color);
	font-family: var(--wp--preset--font-family--body, inherit);
	font-size: var(--wp--custom--button-presets--primary--font-size, 15px);
	font-weight: var(--wp--custom--button-presets--primary--font-weight, 600);
	letter-spacing: var(--wp--custom--button-presets--primary--letter-spacing, normal);
	text-transform: var(--wp--custom--button-presets--primary--text-transform, none);
	letter-spacing: normal;
	line-height: 1.2;
	text-align: center;
	text-decoration: none;
	text-transform: none;
	white-space: nowrap;
	cursor: pointer;
	transition: background-color var(--wp--custom--button-presets--default--hover-transition, 0.18s) var(--wp--custom--easing--ease, ease), border-color var(--wp--custom--button-presets--default--hover-transition, 0.18s) var(--wp--custom--easing--ease, ease), color var(--wp--custom--button-presets--default--hover-transition, 0.18s) var(--wp--custom--easing--ease, ease);
}

a.sgs-field-fallback:focus {
	color: var(--sgs-ff-color);
	text-decoration: none;
}

:where(:root:not(.sgs-touch-input)) a.sgs-field-fallback:hover {
	background-color: color-mix(in srgb, var(--sgs-ff-bg) 78%, white);
	border-color: color-mix(in srgb, var(--sgs-ff-border) 78%, white);
	color: var(--sgs-ff-color);
	text-decoration: none;
}

@media (prefers-reduced-motion: reduce) {
	a.sgs-field-fallback {
		transition: none;
	}
}
