/* =============================================================================
 *  loading.css — the GLOBAL busy-state keystone (the one source of truth).
 * -----------------------------------------------------------------------------
 *  THE CONVENTION (read this before adding any new action button):
 *
 *    An "action button" that fires a request or a navigation gets ONE attribute
 *    flipped while it works:   element.setAttribute('aria-busy', 'true')
 *    (use the window.RHBusy helper — RHBusy.on(el) / RHBusy.off(el)).
 *
 *    This file then, for free:
 *      • blocks re-clicks (pointer-events:none) → no double add-to-cart / no
 *        duplicate request hitting the server,
 *      • hides the label,
 *      • paints a centred spinner INSIDE the button (label is replaced, the
 *        button keeps its exact width → zero layout shift).
 *
 *    NO DOM is created or destroyed — it is a single attribute flip + a pure-CSS
 *    ::after pseudo. Cost on the client is ~nothing; cost on the server is zero.
 *
 *  WHY THIS FILE EXISTS:
 *    Several component sheets (cart-modal, cart-page, thankyou, checkout-page,
 *    product-single, otp-login) were already written AGAINST this contract — they
 *    carry `[aria-busy="true"]` dim rules and `::after` "re-ink" overrides that
 *    only set the spinner's COLOUR, expecting the base geometry/animation to live
 *    here. This file is that missing base. Load order (priority 25) keeps it
 *    BEFORE every component sheet (50+) so their colour overrides still win.
 *
 *  WHAT THIS FILE DOES *NOT* TOUCH:
 *    `.is-loading` is a separate, deliberate convention for *localised* in-place
 *    states with their own meaning (swatch = "applying this combination",
 *    wishlist heart, coupon field). Those are NOT generic action buttons and keep
 *    their bespoke look — see product-single-ps.css / checkout-page.css.
 * ========================================================================== */

/* One spin animation for the whole theme. New busy UIs should reference this
 * instead of re-declaring their own keyframes. */
@keyframes rh-spin { to { transform: rotate(360deg); } }

/* ---------------------------------------------------------------------------
 *  Action buttons & CTA links.  Scoped to genuinely interactive elements so a
 *  stray aria-busy on a region can never sprout a spinner. The theme only ever
 *  flips aria-busy on buttons/CTAs, so this list is the full surface.
 * ------------------------------------------------------------------------- */
button[aria-busy="true"],
a[aria-busy="true"],
[role="button"][aria-busy="true"],
input[type="submit"][aria-busy="true"],
input[type="button"][aria-busy="true"] {
	position: relative;
	pointer-events: none;          /* the double-submit guard */
	cursor: progress;
	/* Hide the label without collapsing the box → width stays, no layout jump.
	 * !important beats inline/utility text colours on busy state only. */
	color: transparent !important;
}

/* Hide any element children too (icons, nested spans) — ::after is NOT a child,
 * so the spinner stays visible. */
button[aria-busy="true"] > *,
a[aria-busy="true"] > *,
[role="button"][aria-busy="true"] > * {
	visibility: hidden;
}

/* The spinner.
 *   --rh-busy-ink   = the moving arc colour (defaults to brand)
 *   --rh-busy-track = the faint rest-of-ring colour
 * Defaults suit LIGHT/outlined buttons (the common case here — add-to-cart,
 * upsell, "view cart"…). FILLED/dark CTAs flip the two vars to white below. */
button[aria-busy="true"]::after,
a[aria-busy="true"]::after,
[role="button"][aria-busy="true"]::after,
input[type="submit"][aria-busy="true"]::after,
input[type="button"][aria-busy="true"]::after {
	content: "";
	position: absolute;
	inset: 0;
	margin: auto;
	width: 1.15em;
	height: 1.15em;
	box-sizing: border-box;
	border: 2px solid var(--rh-busy-track, rgba(15, 17, 21, 0.18));
	border-top-color: var(--rh-busy-ink, var(--wp--preset--color--primary, #0a66c2));
	border-radius: 50%;
	visibility: visible;           /* survive the > * hide above */
	animation: rh-spin 0.6s linear infinite;
}

/* Filled / brand-dark CTAs — re-ink to a white spinner so it reads on the dark
 * fill. (Light buttons keep the brand default above.) Add a button here only
 * when its resting background is dark/brand. */
.rh-co-btn--primary[aria-busy="true"],
.rh-atc__btn--primary[aria-busy="true"],
.rh-cartpage__checkout[aria-busy="true"],
.rh-co-coupon__apply[aria-busy="true"],
.rh-otp-btn--primary[aria-busy="true"],
.rh-rvform__submit[aria-busy="true"],
.rh-cart-sticky__btn[aria-busy="true"] {
	--rh-busy-track: rgba(255, 255, 255, 0.4);
	--rh-busy-ink: #fff;
}

/* Respect reduced-motion: keep the affordance, calm the spin. */
@media (prefers-reduced-motion: reduce) {
	button[aria-busy="true"]::after,
	a[aria-busy="true"]::after,
	[role="button"][aria-busy="true"]::after,
	input[type="submit"][aria-busy="true"]::after,
	input[type="button"][aria-busy="true"]::after {
		animation-duration: 1.4s;
	}
}
