/**
 * ParsAccess loading vocabulary — one definition of "the site is working on it".
 *
 * WHY THIS IS A FILE OF ITS OWN
 * Five surfaces had grown five different answers: `.pa-acc2 .is-loading` (account),
 * `.pa-primary-button.is-loading` (storefront buttons), `form.cart.is-adding` (PDP),
 * `.pa-wish-btn.is-busy` (wishlist, with no visual at all) and `.pa-steam-check__btn[disabled]`.
 * None of them covered the cart, the checkout, or the panel, and none of them survived a page
 * navigation. This file is the shared layer; the older per-screen rules are deliberately left
 * where they are, because rewriting a shipped surface to reach a tidier class name is a
 * regression risk with no user-visible payoff.
 *
 * It is enqueued GLOBALLY — including on /panel, which dequeues main.css and carries its own
 * design tokens — so every custom property below falls back to a literal. A missing token must
 * degrade to a visible spinner, never to an invisible one.
 *
 * Everything here is decoration over a state the markup already declares with `aria-busy`.
 * Nothing in this file is required for a page to work.
 *
 * @package parsaccess
 */

/* ============================================================= TOKENS
 * The two anti-flicker numbers live here and ONLY here: assets/js/pa-loading.js
 * reads them off the document element at boot, so the timing cannot drift between
 * what CSS animates and what JS schedules.
 *
 *   --pa-load-delay  a request faster than this shows no loader at all. Below roughly
 *                    150ms a spinner reads as a flash of dirt on the screen, not as feedback.
 *   --pa-load-min    once shown, it stays this long. A spinner that appears and vanishes in
 *                    the same frame is worse than none: it looks like a fault.
 */
:root {
	--pa-load-delay: 150ms;
	--pa-load-min: 250ms;
	--pa-load-ink: var(--pa-teal, #1eb6ad);
	--pa-load-track: rgba(120, 140, 180, .24);
}

[data-theme="dark"] {
	--pa-load-track: rgba(210, 230, 255, .18);
}

/* ============================================================= BUSY CONTROL
 * Two classes, because a guard and a picture are not the same thing and must not
 * share a timer:
 *
 *   .pa-is-locked  applied the INSTANT the action starts. It is the duplicate-press
 *                  guard, so it can never be delayed.
 *   .pa-is-busy    applied after --pa-load-delay. It is only the picture.
 *
 * Neither one sets `disabled`. A disabled submit button drops its own name/value from
 * the request body, and WooCommerce's checkout and our cart both read the submitter's
 * name (`apply_coupon`, `update_cart`, `woocommerce_checkout_place_order`) to decide
 * what the POST meant. Locking with pointer-events + an in-JS re-entry flag keeps the
 * button in the form while making it unpressable.
 */
.pa-is-locked {
	pointer-events: none;
	cursor: progress;
}

.pa-is-busy {
	position: relative;
}
/* The hover lift and the brightness filter both read as "still clickable", and they are
   declared in five stylesheets (main, parsaccess-ui, account, panel, cart-checkout) whose
   print order depends on which screen is rendering. There is no specificity this rule can
   carry that wins in every one of those orders, so it takes the two properties by force —
   and only those two. */
.pa-is-busy {
	transform: none !important;
	filter: none !important;
}

/* Leading ring. `::before` rather than an injected element: it appears and disappears
   with the class, so there is no DOM to restore if a script dies mid-flight, and it
   cannot disturb the label the button already carries. */
.pa-is-busy::before {
	content: "";
	flex: none;
	display: inline-block;
	vertical-align: -0.15em;
	inline-size: 1em;
	block-size: 1em;
	max-inline-size: 18px;
	max-block-size: 18px;
	margin-inline-end: .5em;
	border: 2px solid var(--pa-load-track);
	border-block-start-color: currentColor;
	border-radius: 50%;
	animation: pa-load-spin .7s linear infinite;
}

/* An icon-only control (the cart row's bin, a wishlist heart) has no room for a ring
   BESIDE its glyph, so there the ring replaces it. pa-loading.js picks this variant on
   its own when the element renders no text, which is the honest test — a class list is
   not a reliable signal of whether something has a label. */
.pa-is-busy--only::before {
	position: absolute;
	inset-block-start: 50%;
	inset-inline-start: 50%;
	margin: 0;
	/* `translate(-50%, -50%)` after `inset-inline-start: 50%` centres correctly in
	   both directions, because it shifts the ring's own box, not the page. The
	   rotation therefore has to carry that offset in every keyframe. */
	animation-name: pa-load-spin-centred;
}
.pa-is-busy--only > * {
	visibility: hidden;
}

@keyframes pa-load-spin {
	to { transform: rotate(360deg); }
}
@keyframes pa-load-spin-centred {
	from { transform: translate(-50%, -50%) rotate(0); }
	to { transform: translate(-50%, -50%) rotate(360deg); }
}

/* ============================================================= BUSY REGION
 * A section whose contents are being replaced. It never HIDES what is already on
 * screen — a stale total the customer can still read beats an empty box — it only
 * says, quietly, that the number is on its way to being replaced.
 */
[data-pa-region-busy="1"] {
	position: relative;
}
[data-pa-region-busy="1"]::after {
	content: "";
	position: absolute;
	inset-inline: 0;
	inset-block-start: 0;
	block-size: 2px;
	border-radius: 2px;
	background: linear-gradient(90deg, transparent, var(--pa-load-ink), transparent);
	background-size: 40% 100%;
	background-repeat: no-repeat;
	animation: pa-load-sweep 1.15s var(--pa-ease, cubic-bezier(.22, .61, .36, 1)) infinite;
	pointer-events: none;
}
@keyframes pa-load-sweep {
	from { background-position: 120% 0; }
	to { background-position: -40% 0; }
}

/* ============================================================= SKELETON
 * Shape first, value second. The rule this exists to enforce is that a number is
 * never drawn until it is the real number: «۰ تومان» settling into «۴٬۷۲۵٬۳۰۰ تومان»
 * is not a loading state, it is a wrong answer displayed confidently.
 */
.pa-skeleton {
	position: relative;
	overflow: hidden;
	display: block;
	border-radius: 10px;
	min-block-size: 12px;
	background: var(--pa-load-track);
}
.pa-skeleton::after {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient(100deg, transparent 30%, rgba(255, 255, 255, .55) 50%, transparent 70%);
	animation: pa-skeleton-sweep 1.4s linear infinite;
}
[data-theme="dark"] .pa-skeleton::after {
	background: linear-gradient(100deg, transparent 30%, rgba(255, 255, 255, .10) 50%, transparent 70%);
}
@keyframes pa-skeleton-sweep {
	/* Physical translate, mirrored for RTL below, so the sweep always travels with
	   the reading direction instead of against it. */
	from { transform: translateX(-100%); }
	to { transform: translateX(100%); }
}
[dir="rtl"] .pa-skeleton::after {
	animation-name: pa-skeleton-sweep-rtl;
}
@keyframes pa-skeleton-sweep-rtl {
	from { transform: translateX(100%); }
	to { transform: translateX(-100%); }
}

.pa-skeleton--line { block-size: 12px; }
.pa-skeleton--text { block-size: 14px; border-radius: 7px; }
.pa-skeleton--title { block-size: 18px; border-radius: 9px; }
.pa-skeleton--media { block-size: auto; aspect-ratio: 1.34 / 1; border-radius: 11px; }
.pa-skeleton--w70 { inline-size: 70%; }
.pa-skeleton--w45 { inline-size: 45%; }

/* A card-shaped placeholder for a product grid: the same media ratio the real
   .pcard3-media reserves, so the grid does not resize when the real cards land. */
.pa-skeleton-card {
	display: flex;
	flex-direction: column;
	gap: 9px;
}

/* ============================================================= PAGE TRANSITION
 * A 3px rule at the top of the viewport, appended to <body> so it cannot be trapped
 * inside `.pa-page-shell`'s `isolation: isolate` stacking context. `pointer-events:
 * none` is load-bearing: this must never be able to swallow a click on a gateway
 * redirect or a checkout button underneath it.
 *
 * It is indeterminate dressed as determinate — it eases towards 90% and waits there.
 * Nothing on this site can report real navigation progress, so the bar promises only
 * "still working", and it completes to 100% before it fades so the end reads as an end.
 */
.pa-navload {
	position: fixed;
	inset-block-start: 0;
	inset-inline: 0;
	block-size: 3px;
	z-index: 10000;
	pointer-events: none;
	opacity: 0;
	transition: opacity .18s linear;
}
.pa-navload.is-on { opacity: 1; }

.pa-navload__bar {
	display: block;
	block-size: 100%;
	inline-size: 100%;
	/* RTL is the site default, so the bar grows from the right edge; the LTR
	   override below keeps it correct if a page ever renders the other way. */
	transform-origin: 100% 50%;
	transform: scaleX(0);
	background: linear-gradient(90deg, var(--pa-load-ink), var(--pa-violet, #7667e9));
	transition: transform .2s linear;
}
[dir="ltr"] .pa-navload__bar { transform-origin: 0 50%; }
.pa-navload.is-on .pa-navload__bar {
	transform: scaleX(.9);
	transition: transform 7s cubic-bezier(.06, .78, .16, 1);
}
.pa-navload.is-done .pa-navload__bar {
	transform: scaleX(1);
	transition: transform .16s linear;
}

/* ============================================================= LIVE REGION
 * Announcements only. Visually hidden without `display: none`, which would stop a
 * screen reader from reading it at all.
 */
.pa-load-status {
	position: absolute;
	inline-size: 1px;
	block-size: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

/* ============================================================= REDUCED MOTION
 * An indefinite spinner is decoration to somebody who cannot see it move and motion
 * sickness to somebody who can — the same trade already settled for `.pa-idv-spin`.
 * The state stays legible without motion: the ring is still drawn, the skeleton is
 * still a grey block, the progress bar still sits at the top of the page, and
 * `aria-busy` plus the live region carry the meaning either way.
 */
@media (prefers-reduced-motion: reduce) {
	.pa-is-busy::before {
		animation: none;
		border-block-start-color: currentColor;
		opacity: .55;
	}
	.pa-skeleton::after { animation: none; opacity: .35; }
	[data-pa-region-busy="1"]::after { animation: none; background-size: 100% 100%; opacity: .5; }
	.pa-navload__bar,
	.pa-navload.is-on .pa-navload__bar,
	.pa-navload.is-done .pa-navload__bar {
		transition: none;
		transform: scaleX(1);
		opacity: .7;
	}
}
