/* ============================================================================
   BENIS SHOES — SHARED COMPONENTS
   ----------------------------------------------------------------------------
   The canonical UI kit. Every button, field, badge, chip, sheet, accordion,
   empty state and loader in the theme is defined once, here. Page and commerce
   stylesheets compose these; they do not redefine them.

   If a page needs a variant, it gets a modifier in this file — not a new rule
   in that page's stylesheet. That is the discipline the v1-v17 layers lacked,
   where "primary button" was declared independently in six places.
   ========================================================================== */

/* ============================================================================
   BUTTONS
   ========================================================================== */

/* The Benis button.
   ----------------------------------------------------------------------------
   Proportion first. 46px on desktop and 48px on mobile sits between a form
   control and a banner CTA — comfortable to hit, never oversized. Horizontal
   padding is deliberately restrained (24px, not 40px): Persian sets wider than
   Latin at the same point size, so a generous inline pad that looks balanced in
   English turns a two-word Persian label into a slab.

   Radius is 10px. The token scale's 12px is right for cards; on a 46px control
   it starts to read soft, and a pill reads as a chip. 10px is the point where
   the button still belongs to Benis without becoming a bubble. */
/* THE .button SELECTORS BELONG TO PAYMENT PLUGINS, NOT TO US.
   Every redirect gateway prints its own pay button on the order-pay page with
   WooCommerce's `button` / `button alt` / `button cancel` classes, and
   marsi_dequeue_wc_styles() drops woocommerce-general — so those classes have
   no rules at all and the button renders as a raw OS submit control on the one
   page where the shopper hands over money.

   They are listed HERE, on the canonical rules, rather than restyled in
   06-commerce.css. A second definition of "what a Benis button looks like"
   living beside the checkout is how the pay button ends up a different shape
   from every other button in the shop after the next token change.

   Scoped to .order-pay__gateway on purpose: this styles the gateway hand-off,
   not every stray `.button` a plugin might emit anywhere on the site. */
.btn,
.order-pay__gateway .button {
	/* Sizing is driven by two custom properties so modifiers change one value
	   instead of restating the whole box. */
	--btn-height: 2.875rem;       /* 46px */
	--btn-padding: var(--space-6);

	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-2);
	min-block-size: var(--btn-height);
	padding-inline: var(--btn-padding);
	padding-block: 0;
	border: 1px solid transparent;
	border-radius: 10px;
	font-size: var(--text-label);
	font-weight: var(--weight-semibold);
	/* Persian glyphs carry their optical centre slightly above the box centre
	   because of descender depth; a hair of bottom padding re-centres the label
	   without moving the box. */
	padding-block-start: 0.0625rem;
	line-height: 1;
	text-align: center;
	white-space: nowrap;
	cursor: pointer;
	position: relative;
	transition:
		background-color var(--duration-fast) var(--ease),
		border-color var(--duration-fast) var(--ease),
		color var(--duration-fast) var(--ease),
		box-shadow var(--duration-fast) var(--ease),
		transform var(--duration-fast) var(--ease);
}

/* Touch targets get 48px. Mobile has no hover to confirm a hit, so the box
   itself has to be forgiving. */
@media (max-width: 767px) {
	.btn { --btn-height: 3rem; }
}

/* Icons travel toward the reading direction on hover — 4px, fast. This is the
   whole button microinteraction: no scaling, no shadow bloom, no arrow circle
   bolted onto the side. */
.btn svg {
	inline-size: 1.0625rem;
	block-size: 1.0625rem;
	flex-shrink: 0;
	transition: transform var(--duration-fast) var(--ease);
}

@media (hover: hover) {
	.btn:hover svg { transform: translateX(-4px); }
	html[dir="ltr"] .btn:hover svg { transform: translateX(4px); }
}

/* A real press state. Buttons that do not physically acknowledge a tap feel
   cheap on mobile, where there is no hover to confirm the target. */
.btn:active:not(:disabled) { transform: translateY(1px); }

@media (prefers-reduced-motion: reduce) {
	.btn,
	.btn svg { transition: none; }
	.btn:active:not(:disabled) { transform: none; }
}

.btn:disabled,
.btn[aria-disabled="true"] {
	opacity: 0.5;
	cursor: not-allowed;
	pointer-events: none;
}

/* ── Primary — the Benis blue, carrying dark ink.
   This is the brand's own CTA colour, not a neutral stand-in. The contrast
   comes from the ink pairing (9.28:1), which is why the fill can stay at the
   true logo value instead of being darkened into a different blue. ───────── */

/* The inset bottom hairline is what stops a flat cyan fill reading as a default
   component-library button. It behaves like a printed edge rather than a drop
   shadow: it compresses on press instead of blooming outward, which is the
   cheapest way to make a button feel physical. No gradient, no glass, no glow. */
/* `.button.alt` is WooCommerce's "this is the primary action" class, and it is
   what every gateway puts on its pay button — so it maps to the brand CTA. See
   the note on .btn above. */
.btn--primary,
.order-pay__gateway .button.alt,
.order-pay__gateway input[type="submit"].button {
	background-color: var(--action);
	color: var(--action-text);
	box-shadow: inset 0 -2px 0 rgb(1 125 178 / 0.3);
}

.btn--primary:hover,
.order-pay__gateway .button.alt:hover,
.order-pay__gateway input[type="submit"].button:hover {
	background-color: var(--brand-hover);
	box-shadow: inset 0 -2px 0 rgb(1 125 178 / 0.38);
}

.btn--primary:active,
.order-pay__gateway .button.alt:active,
.order-pay__gateway input[type="submit"].button:active {
	background-color: var(--action-active);
	box-shadow: inset 0 -1px 0 rgb(1 125 178 / 0.3);
}

/* ── Glass modifier — for a CTA sitting ON photography ───────────────────────
   .btn--primary above is deliberately flat, and stays that way: on the page's
   own surfaces there is nothing behind a button to refract, so a glass effect
   there is decoration pretending to be material. Over a campaign photograph
   there IS something behind it, and the same material the floating bottom nav
   uses — blur, saturation boost, a lit top edge — makes the button read as a
   pane of glass laid on the image rather than a rectangle stamped onto it.

   HIGH ALPHA ON PURPOSE. The fill runs 0.88–0.92, not the 0.6 that makes a
   dramatic screenshot: the label is dark ink, the photograph behind is
   arbitrary, and every point of transparency spends contrast the text needs. At
   this alpha the fill stays recognisably Benis cyan over a dark scrim and the
   ink keeps roughly 6:1 — well clear of 4.5. The saturation boost is what stops
   the colour washing grey when the photograph behind it is desaturated.

   The brand pairing is untouched: cyan fill, DARK ink. Glass is a material
   here, not a licence to switch to white type. */
.btn--glass {
	color: var(--action-text);
	/* Fallback for browsers with no backdrop-filter — see @supports below. */
	background-color: var(--action);
	background-image: linear-gradient(
		180deg,
		rgb(113 212 254 / 0.92) 0%,
		rgb(81 202 254 / 0.88) 52%,
		rgb(27 185 254 / 0.9) 100%
	);
	border-color: rgb(255 255 255 / 0.5);
	backdrop-filter: blur(14px) saturate(185%);
	-webkit-backdrop-filter: blur(14px) saturate(185%);

	/* Three layers, each doing one job: the specular highlight along the top
	   edge, the printed edge along the bottom that .btn--primary also has, and a
	   cyan-tinted glow that sits the pane off the photograph. */
	box-shadow:
		inset 0 1px 0 rgb(255 255 255 / 0.55),
		inset 0 -2px 0 rgb(1 125 178 / 0.28),
		0 10px 24px -10px rgb(1 125 178 / 0.6);
}

.btn--glass:hover {
	background-image: linear-gradient(
		180deg,
		rgb(113 212 254 / 0.97) 0%,
		rgb(81 202 254 / 0.94) 52%,
		rgb(27 185 254 / 0.96) 100%
	);
	box-shadow:
		inset 0 1px 0 rgb(255 255 255 / 0.65),
		inset 0 -2px 0 rgb(1 125 178 / 0.34),
		0 12px 26px -10px rgb(1 125 178 / 0.68);
}

/* Presses INTO the glass rather than blooming outward, the same motion the flat
   primary has — the highlight dims and the glow collapses. */
.btn--glass:active {
	box-shadow:
		inset 0 1px 0 rgb(255 255 255 / 0.35),
		inset 0 -1px 0 rgb(1 125 178 / 0.3),
		0 4px 12px -8px rgb(1 125 178 / 0.5);
}

/* No blur available means no glass: a flat translucent rectangle over a
   photograph is exactly the unreadable thing the blur was preventing. These
   browsers get the solid primary fill instead. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
	.btn--glass {
		background-image: none;
		background-color: var(--action);
		border-color: transparent;
		box-shadow: inset 0 -2px 0 rgb(1 125 178 / 0.3);
	}
}

/* `.btn--brand` was deleted here. It resolved to exactly the same pairing as
   --primary, so it was a second name for one button — the kind of harmless-
   looking alias that turns into "which one do I use?" six months later. There
   is one Benis CTA. */

/* ── Secondary — a fine dark hairline, filling with brand tint on hover.
   Restrained on purpose: beside a saturated cyan primary, a second heavy
   bordered box reads as a segmented control rather than a subordinate action. */

/* `.button.cancel` is the "go back" link gateways print beside the pay button.
   It is subordinate by definition, so it takes the secondary treatment. */
.btn--secondary,
.order-pay__gateway .button.cancel {
	background-color: transparent;
	color: var(--text);
	border-color: var(--neutral-300);
}

.btn--secondary:hover,
.order-pay__gateway .button.cancel:hover {
	border-color: var(--brand);
	background-color: var(--brand-soft);
	color: var(--text-link);
}

/* ── Solid ink — used where a filled button must not read as the brand CTA
   (e.g. "apply filters" inside a sheet, where blue would compete with
   "add to cart" behind it). */

.btn--ink {
	background-color: var(--neutral-900);
	color: var(--text-inverse);
}

.btn--ink:hover { background-color: var(--neutral-800); }

/* ── Quiet — the second action in a hero pair.
   Outlined is too loud next to a saturated cyan CTA at large sizes: two bordered
   rectangles side by side read as a segmented control rather than a primary and
   a secondary. This carries weight through type and a hairline only. ───────── */

.btn--quiet {
	background-color: transparent;
	color: var(--text);
	border-color: transparent;
	padding-inline: var(--space-3);
}

.btn--quiet::after {
	content: "";
	position: absolute;
	inset-block-end: 0.625rem;
	inset-inline: var(--space-3);
	block-size: 1px;
	background-color: var(--neutral-300);
	transition: background-color var(--duration-fast) var(--ease);
}

.btn--quiet:hover {
	color: var(--text-link);
}

.btn--quiet:hover::after { background-color: var(--brand); }

/* ── Tertiary / text — an inline action with a rule that grows from the
   reading edge on hover. Not every action deserves a filled rectangle; this is
   the tier for "see all", "change address", "size guide". ─────────────────── */

.btn--text {
	--btn-padding: var(--space-2);
	background-color: transparent;
	color: var(--text-link);
	background-image: linear-gradient(var(--brand), var(--brand));
	background-size: 0% 2px;
	background-position: right bottom 0.625rem;
	background-repeat: no-repeat;
	transition: background-size var(--duration-base) var(--ease),
	            color var(--duration-fast) var(--ease);
}

html[dir="ltr"] .btn--text { background-position: left bottom 0.625rem; }

.btn--text:hover {
	color: var(--blue-900);
	background-size: calc(100% - var(--space-4)) 2px;
}

/* ── Destructive ─────────────────────────────────────────────────────────── */

.btn--danger {
	background-color: var(--danger);
	color: var(--text-inverse);
}

.btn--danger:hover { background-color: var(--red-700); }

.btn--danger-quiet {
	background-color: transparent;
	color: var(--danger);
	--btn-padding: var(--space-2);
}

.btn--danger-quiet:hover { background-color: var(--danger-surface); }

/* ── Sizes ───────────────────────────────────────────────────────────────── */

.btn--sm {
	--btn-height: 2.25rem;
	--btn-padding: var(--space-3);
	font-size: var(--text-small);
}

/* 50px, not 52. Past roughly 50 a Persian CTA stops reading as a control and
   starts reading as a banner, and the inline padding drops from space-8 (32px)
   to space-6 (24px) for the same reason — Persian sets wide, so the generous
   pad that balances an English label makes a slab here. */
.btn--lg {
	--btn-height: 3.125rem;
	--btn-padding: var(--space-6);
	font-size: var(--text-body);
}

.btn--block {
	display: flex;
	inline-size: 100%;
}

/* ── Icon button ─────────────────────────────────────────────────────────── */

/* Any display value defeats the UA's [hidden] rule, which is only
   `display: none` at the lowest specificity. Every component that sets display
   therefore has to opt back in, or `hidden` silently stops working — which is
   how the search field's clear button ended up permanently visible on an
   empty field. */
.btn-icon[hidden] { display: none; }

.btn-icon {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: var(--tap-target);
	block-size: var(--tap-target);
	border-radius: var(--radius-md);
	color: var(--text);
	background-color: transparent;
	border: none;
	cursor: pointer;
	transition: background-color var(--duration-fast) var(--ease),
	            color var(--duration-fast) var(--ease);
}

.btn-icon:hover { background-color: var(--surface-sunken); }

.btn-icon svg {
	inline-size: 1.25rem;
	block-size: 1.25rem;
}

.btn-icon--sm {
	inline-size: 2rem;
	block-size: 2rem;
}

.btn-icon--sm svg {
	inline-size: 1rem;
	block-size: 1rem;
}

/* Matches --btn-height on .btn--lg, so an icon button standing beside a large
   CTA lines up with it instead of floating inside the row. It also takes a
   visible border, because at that size a borderless icon next to a filled
   button reads as a gap rather than as a second control. */
.btn-icon--lg {
	inline-size: 3.125rem;
	block-size: 3.125rem;
	border: 1px solid var(--border-strong);
}

.btn-icon--lg:hover { border-color: var(--neutral-900); }

/* A circular icon button on a surface — used for gallery and rail controls. */
.btn-icon--surface {
	background-color: var(--surface);
	border: 1px solid var(--border);
	border-radius: var(--radius-pill);
	box-shadow: var(--shadow-sm);
}

.btn-icon--surface:hover {
	background-color: var(--surface);
	border-color: var(--border-strong);
}

/* ── Loading ─────────────────────────────────────────────────────────────── */

/* The label is hidden rather than replaced, so the button keeps its width and
   the row does not reflow mid-request. */
.btn.is-loading {
	color: transparent;
	pointer-events: none;
}

.btn.is-loading::after {
	content: "";
	position: absolute;
	inline-size: 1.125rem;
	block-size: 1.125rem;
	/* The button's colour is transparent while loading, so currentColor cannot
	   be used for the ring — it needs an explicit value. */
	border: 2px solid rgb(255 255 255 / 0.4);
	border-block-start-color: transparent;
	border-radius: 50%;
	animation: benis-spin 0.6s linear infinite;
}

.btn--primary.is-loading::after,
.btn--secondary.is-loading::after {
	border-color: rgb(15 27 42 / 0.35);
	border-block-start-color: transparent;
}

@keyframes benis-spin {
	to { transform: rotate(360deg); }
}

/* ============================================================================
   FORM FIELDS
   ========================================================================== */

.field {
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
}

/* Labels are persistent, never placeholder-only. A placeholder that doubles as
   a label disappears the moment the user types, which is the single most
   common cause of checkout errors. */
.field__label {
	font-size: var(--text-label);
	font-weight: var(--weight-medium);
	color: var(--text);
}

.field__label .optional {
	font-weight: var(--weight-regular);
	color: var(--text-muted);
}

.field__hint {
	font-size: var(--text-small);
	color: var(--text-muted);
}

.field__error {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	font-size: var(--text-small);
	color: var(--danger);
}

.field__error svg {
	inline-size: 1rem;
	block-size: 1rem;
}

.input,
.select,
.textarea {
	inline-size: 100%;
	min-block-size: var(--tap-target);
	padding-inline: var(--space-4);
	padding-block: var(--space-3);
	background-color: var(--surface);
	border: 1px solid var(--border-strong);
	border-radius: var(--radius-md);
	color: var(--text);
	font-size: var(--text-body);
	line-height: var(--leading-snug);
	transition: border-color var(--duration-fast) var(--ease),
	            box-shadow var(--duration-fast) var(--ease);
}

.input:hover,
.select:hover,
.textarea:hover {
	border-color: var(--neutral-400);
}

.input:focus-visible,
.select:focus-visible,
.textarea:focus-visible {
	outline: none;
	border-color: var(--border-focus);
	box-shadow: var(--shadow-focus);
}

.input[aria-invalid="true"],
.select[aria-invalid="true"],
.textarea[aria-invalid="true"] {
	border-color: var(--danger);
}

.input[aria-invalid="true"]:focus-visible {
	box-shadow: 0 0 0 3px rgb(200 40 63 / 0.25);
}

.input:disabled,
.select:disabled,
.textarea:disabled {
	background-color: var(--surface-sunken);
	color: var(--text-muted);
}

/* Native select needs its own chevron, positioned on the inline-end edge so it
   follows RTL without a left/right override. */
.select {
	appearance: none;
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23677483' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-size: 1.125rem;
	background-position: left var(--space-4) center;
	padding-inline-end: var(--space-4);
	padding-inline-start: var(--space-10);
}

html[dir="ltr"] .select {
	background-position: right var(--space-4) center;
}

/* ── Checkbox and radio ──────────────────────────────────────────────────── */

.choice {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	min-block-size: var(--tap-target);
	cursor: pointer;
}

.choice input[type="checkbox"],
.choice input[type="radio"] {
	appearance: none;
	inline-size: 1.25rem;
	block-size: 1.25rem;
	flex-shrink: 0;
	border: 1.5px solid var(--border-strong);
	background-color: var(--surface);
	cursor: pointer;
	transition: background-color var(--duration-fast) var(--ease),
	            border-color var(--duration-fast) var(--ease);
}

.choice input[type="checkbox"] { border-radius: var(--radius-xs); }
.choice input[type="radio"]    { border-radius: 50%; }

.choice input:checked {
	background-color: var(--action);
	border-color: var(--action);
	background-repeat: no-repeat;
	background-position: center;
	background-size: 0.75rem;
}

.choice input[type="checkbox"]:checked {
	background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E");
}

.choice input[type="radio"]:checked {
	background-image: radial-gradient(circle, white 0 35%, transparent 36%);
	background-size: 100%;
}

.choice input:focus-visible {
	outline: 2px solid var(--border-focus);
	outline-offset: 2px;
}

.choice__label {
	font-size: var(--text-label);
	line-height: var(--leading-snug);
}

.choice__count {
	margin-inline-start: auto;
	font-size: var(--text-small);
	color: var(--text-muted);
	font-variant-numeric: tabular-nums;
}

/* ── Quantity control ────────────────────────────────────────────────────── */

.qty {
	display: inline-flex;
	align-items: center;
	border: 1px solid var(--border-strong);
	border-radius: var(--radius-md);
	overflow: hidden;
	background-color: var(--surface);
}

.qty__btn {
	inline-size: 2.5rem;
	block-size: 2.5rem;
	display: flex;
	align-items: center;
	justify-content: center;
	color: var(--text);
	transition: background-color var(--duration-fast) var(--ease);
}

.qty__btn:hover:not(:disabled) { background-color: var(--surface-sunken); }
.qty__btn:disabled { color: var(--icon-muted); cursor: not-allowed; }

/* `input.qty__input`, not `.qty__input`.
   02-base.css sets `input[type="number"] { direction: ltr; text-align: end }` so
   that phone numbers, prices and the like read correctly in an RTL document.
   That selector scores (0,1,1) against this one's (0,1,0), so it won a
   specificity tie this rule did not know it was in, and the quantity sat shoved
   against one edge of its box between the − and + buttons. Adding the element
   to the selector levels the score, and being later in the cascade settles it.

   The LTR direction is kept on purpose — it is a numeral in a stepper, and the
   digits should run the way digits run. */
input.qty__input {
	inline-size: 3rem;
	block-size: 2.5rem;
	border: none;
	border-inline: 1px solid var(--border);
	text-align: center;
	font-size: var(--text-label);
	font-weight: var(--weight-medium);
	font-variant-numeric: tabular-nums;
	appearance: textfield;
	-moz-appearance: textfield;
}

.qty__input::-webkit-outer-spin-button,
.qty__input::-webkit-inner-spin-button {
	appearance: none;
	margin: 0;
}

/* ============================================================================
   BADGES, CHIPS AND PRICE
   ========================================================================== */

/* Compact chips, not stamps.
   ----------------------------------------------------------------------------
   Three things were wrong before: the chips were pill-shaped and full-height,
   two of them could stack on one photograph, and "new" was a saturated cyan
   block sitting on top of the product.

   Now: a small squircle (7px — the utility end of the radius scale, so a chip
   never competes with the 14px plate it sits on), one badge maximum enforced in
   benis_product_badges(), and "new" rendered as a frosted white chip carrying
   blue ink. Frosted white is legible over ANY photography — light or dark —
   without painting a saturated rectangle over the shoe, which is the whole
   reason the pill existed.

   Bare letter-spaced type was tested as an alternative and rejected: it is
   unreadable over dark product shots, and the tracking it needs breaks Persian
   letter-joining — see the tracking note in 01-tokens.css. */
.badge {
	display: inline-flex;
	align-items: center;
	gap: var(--space-1);
	padding-inline: 0.5625rem;
	padding-block: 0.1875rem;
	border-radius: 7px;
	font-size: 0.6875rem;
	font-weight: var(--weight-bold);
	line-height: 1.5;
	white-space: nowrap;
}

/* "جدید" — frosted white carrying brand-blue ink. blue-900 on white is 11.1:1. */
.badge--new {
	background-color: rgb(255 255 255 / 0.93);
	color: var(--blue-900);
	backdrop-filter: blur(6px);
}

/* Reductions use berry, not alarm red. A supermarket-red label is the loudest
   "discount template" cue there is; berry still reads as a markdown because it
   sits beside a struck original, and it belongs to the Benis palette. */
.badge--sale { background-color: var(--berry-500); color: var(--neutral-0); }

.badge--accent,
.badge--brand,
.badge--success,
.badge--muted,
.badge--soldout {
	padding-inline: var(--space-2);
	padding-block: 0.1875rem;
	border-radius: var(--radius-xs);
}
.badge--accent  { background-color: var(--berry-100); color: var(--berry-700); }
.badge--brand   { background-color: var(--blue-100); color: var(--blue-900); }
.badge--success { background-color: var(--success-surface); color: var(--green-700); }
.badge--muted   { background-color: var(--surface-sunken); color: var(--text-muted); }
.badge--soldout { background-color: var(--neutral-200); color: var(--neutral-700); }

/* ── Chip — removable applied filters ────────────────────────────────────── */

.chip {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	padding-inline: var(--space-3);
	min-block-size: 2rem;
	border: 1px solid var(--border);
	border-radius: var(--radius-pill);
	background-color: var(--surface-soft);
	font-size: var(--text-small);
	color: var(--text);
}

.chip__remove {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 1.25rem;
	block-size: 1.25rem;
	border-radius: 50%;
	color: var(--text-muted);
	transition: background-color var(--duration-fast) var(--ease),
	            color var(--duration-fast) var(--ease);
}

.chip__remove:hover {
	background-color: var(--neutral-900);
	color: var(--text-inverse);
}

.chip__remove svg { inline-size: 0.75rem; block-size: 0.75rem; }

/* ── Price ───────────────────────────────────────────────────────────────── */

/* Persian prices read right-to-left with the currency word trailing, and the
   digits must not be split across a line break.

   `flex-wrap: nowrap` with the original allowed to shrink: on a 171px card the
   sale price and its struck original wrapped onto two lines, which pushed the
   swatches down and left the grid with cards of three different heights. */
.price {
	display: inline-flex;
	align-items: baseline;
	gap: var(--space-2);
	flex-wrap: nowrap;
	max-inline-size: 100%;
}

.price__current { flex: 0 0 auto; }

.price__original {
	flex: 0 1 auto;
	min-inline-size: 0;
	overflow: hidden;
}

/* Bold, in ink. The price is the strongest thing in the card's text block, and
   it earns that by weight rather than by size or colour — a card whose price is
   also the biggest and the reddest thing on it reads as a clearance bin. */
.price__current {
	font-size: 1.0625rem;
	font-weight: var(--weight-bold);
	color: var(--text);
	white-space: nowrap;
	/* PROPORTIONAL figures, not tabular.
	   Tabular was tried first, on the usual reasoning that prices should align
	   down a column. In Persian it backfires: ۰ is a small circle and ۱ is a
	   thin stroke, so forcing every digit to the width of ۴ left visible gaps
	   around them and made "۲,۴۵۰,۰۰۰" look spaced out and hand-set. Nothing
	   here is a numeric column that needs aligning — each price sits under its
	   own card — so the digits get their natural widths and read as one number. */
	font-variant-numeric: proportional-nums;
	font-feature-settings: "pnum" 1;
}

/* The unit is not the price.
   ----------------------------------------------------------------------------
   تومان now sits a size down, at normal weight, in muted ink, with real space
   before it. It was previously set at the amount's own weight and butted
   straight against the digits, so "تومان۲,۴۵۰,۰۰۰" read as one dense blob with
   no entry point. The eye should land on the number and read the unit as the
   label it is. Pairs with the price-format filter in helpers.php, which moved
   the symbol after the amount. */
.price .woocommerce-Price-currencySymbol {
	font-size: 0.8em;
	font-weight: var(--weight-regular);
	color: var(--text-muted);
}

/* --text-muted, NOT --icon-muted. neutral-400 measures 3.01:1 and the token
   file names it --icon-* precisely so it is not reached for as "a lighter grey
   text" — which is exactly what I did here. A struck original is still text
   carrying information, so it takes the 4.77:1 value. */
.price__original {
	font-size: var(--text-small);
	font-weight: var(--weight-regular);
	color: var(--text-muted);
	text-decoration: line-through;
	text-decoration-thickness: 1px;
	text-decoration-color: var(--neutral-300);
	white-space: nowrap;
	font-variant-numeric: proportional-nums;
}

/* The struck original does not repeat the unit. It is obviously the same
   currency as the price beside it, and two تومان on one line is noise. */
.price__original .woocommerce-Price-currencySymbol { display: none; }

/* Berry, not alarm red. The reduction is already communicated by the struck
   original sitting next to it; the colour only has to say "this one is the
   price you pay". --sale stays red for genuinely urgent surfaces (stock
   warnings, error states) where a warm alarm is doing real work. */
.price--sale .price__current { color: var(--berry-600); }

.price--lg .price__current { font-size: var(--text-h3); }
.price--lg .price__original { font-size: var(--text-label); }

/* ============================================================================
   SURFACES
   ========================================================================== */

.card {
	background-color: var(--surface);
	border: 1px solid var(--border);
	border-radius: var(--radius-lg);
	overflow: hidden;
}

.card--flush { border: none; border-radius: 0; }
.card--soft  { background-color: var(--surface-soft); border-color: transparent; }
.card--pad   { padding: var(--space-5); }

.panel {
	background-color: var(--surface);
	border: 1px solid var(--border);
	border-radius: var(--radius-lg);
	padding: var(--space-5);
}

@media (min-width: 768px) {
	.panel { padding: var(--space-6); }
}

/* ── Section header — one pattern for every "title + link" row ───────────── */

.section-head {
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: var(--space-4);
	margin-block-end: var(--space-6);
}

/* Bold. Persian at weight 300 does not read as editorial, it reads as
   under-inked — the script's strokes are finer than a Latin face at the same
   weight, so light display type simply loses the heading. Benis sets these
   heavy, and the contrast against the quiet subtitle carries the hierarchy. */
.section-head__title {
	font-size: var(--text-h2);
	font-weight: var(--weight-bold);
	letter-spacing: var(--tracking-heading);
	line-height: 1.3;
}

/* The eyebrow is BLUE. It is the small repeated brand mark that ties the
   homepage sections, the PLP masthead and the hero together, and it is one of
   the cheapest places to spend the brand colour well. No tracking: Persian. */
.section-head__kicker {
	display: block;
	font-size: var(--text-caption);
	font-weight: var(--weight-semibold);
	color: var(--text-link);
	margin-block-end: var(--space-2);
}

.section-head__sub {
	margin-block-start: var(--space-2);
	font-size: var(--text-label);
	color: var(--text-muted);
	font-weight: var(--weight-regular);
}

/* A real, bounded control rather than a hairline text link. "مشاهده همه" is a
   primary navigation route out of every rail, and as bare 13px type with a
   hover underline it was both hard to hit on touch and easy to miss entirely.
   Outlined keeps it quieter than the section's own CTAs. */
.section-head__link {
	flex-shrink: 0;
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	min-block-size: 2.5rem;
	padding-inline: var(--space-4);
	border: 1px solid var(--border-strong);
	border-radius: var(--radius-md);
	font-size: var(--text-small);
	font-weight: var(--weight-semibold);
	color: var(--text);
	transition: border-color var(--duration-fast) var(--ease),
	            background-color var(--duration-fast) var(--ease),
	            color var(--duration-fast) var(--ease);
}

.section-head__link:hover {
	border-color: var(--brand);
	background-color: var(--brand-soft);
	color: var(--text-link);
}

/* The chevron points toward the reading direction's end. In RTL that is left,
   which a plain rotate(180deg) on an LTR chevron would get wrong on flip. */
.section-head__link svg {
	inline-size: 1rem;
	block-size: 1rem;
	transform: scaleX(-1);
}

html[dir="ltr"] .section-head__link svg { transform: none; }

/* ============================================================================
   OVERLAYS — backdrop, drawer, sheet, modal
   ========================================================================== */

.backdrop {
	position: fixed;
	inset: 0;
	z-index: var(--z-backdrop);
	background-color: rgb(15 27 42 / 0.5);
	opacity: 0;
	visibility: hidden;
	transition: opacity var(--duration-base) var(--ease),
	            visibility var(--duration-base) var(--ease);
}

.backdrop.is-open {
	opacity: 1;
	visibility: visible;
}

/* ── Drawer — slides from the inline-start edge (right, in RTL) ──────────── */

.drawer {
	position: fixed;
	inset-block: 0;
	inset-inline-end: 0;
	z-index: var(--z-drawer);
	inline-size: min(22rem, 88vw);
	max-inline-size: 100%;
	background-color: var(--surface);
	display: flex;
	flex-direction: column;
	/* Off-screen is not the same as gone: a translated drawer still answers Tab,
	   so its links sat in the tab order of every page while invisible.

	   `visibility` is DELAYED rather than eased. A real transition on it leaves
	   the element still computing as hidden on the tick the class lands, and
	   `.focus()` is a no-op on a hidden element — which silently broke the
	   overlay controller's job of moving focus into the panel it just opened.
	   Zero duration with a delay flips it to visible instantly on open, and
	   holds it visible until the slide-out has finished on close. */
	visibility: hidden;
	transition: transform var(--duration-slow) var(--ease-out),
	            visibility 0s linear var(--duration-slow);
	box-shadow: var(--shadow-lg);
}

.drawer.is-open {
	visibility: visible;
	transition: transform var(--duration-slow) var(--ease-out),
	            visibility 0s;
}

/* The direction lives on the ROOT, the state lives on the drawer.
   ----------------------------------------------------------------------------
   This was previously two competing transform rules:

       html[dir="rtl"] .drawer { transform: translateX(-100%); }   (0,2,1)
       .drawer.is-open         { transform: translateX(0); }       (0,2,0)

   The RTL selector is one point more specific, so it won unconditionally and
   `is-open` never applied. On an RTL site — which is every page of this theme —
   the drawer opened with its backdrop, locked the scroll and moved focus into
   itself while sitting entirely off-screen at x: -343. Tapping the menu gave
   you a dimmed page and nothing to read.

   There is no logical equivalent of translateX, so the axis is carried in a
   custom property set on :root instead. Direction and state then live on
   different elements and can never out-specify each other. */
:root { --drawer-hidden: 100%; }
html[dir="rtl"] { --drawer-hidden: -100%; }

.drawer { transform: translateX(var(--drawer-hidden)); }

.drawer.is-open { transform: translateX(0); }

/* Slides from the opposite edge, so it hides in the opposite direction. */
.drawer--start {
	inset-inline-end: auto;
	inset-inline-start: 0;
	transform: translateX(calc(var(--drawer-hidden) * -1));
}

.drawer--start.is-open { transform: translateX(0); }

.drawer--wide { inline-size: min(28rem, 92vw); }

/* ── Sheet — rises from the block-end edge. The mobile filter pattern. ───── */

.sheet {
	position: fixed;
	inset-inline: 0;
	inset-block-end: 0;
	z-index: var(--z-drawer);
	background-color: var(--surface);
	border-start-start-radius: var(--radius-xl);
	border-start-end-radius: var(--radius-xl);
	display: flex;
	flex-direction: column;
	/* Bounded, not full-screen: the page stays visible behind it, which is what
	   keeps a filter sheet feeling like a control rather than a new page. */
	max-block-size: min(85svh, 44rem);
	transform: translateY(100%);
	visibility: hidden;   /* same reason, and same delay trick, as the drawer */
	transition: transform var(--duration-slow) var(--ease-out),
	            visibility 0s linear var(--duration-slow);
	box-shadow: var(--shadow-lg);
}

.sheet.is-open {
	transform: translateY(0);
	visibility: visible;
	transition: transform var(--duration-slow) var(--ease-out),
	            visibility 0s;
}

.sheet__grip {
	inline-size: 2.25rem;
	block-size: 0.25rem;
	border-radius: var(--radius-pill);
	background-color: var(--border-strong);
	margin: var(--space-3) auto var(--space-1);
	flex-shrink: 0;
}

/* ── Shared overlay chrome ───────────────────────────────────────────────── */

.overlay__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-3);
	padding: var(--space-4) var(--space-5);
	border-block-end: 1px solid var(--border);
	flex-shrink: 0;
}

.overlay__title {
	font-size: var(--text-body-lg);
	font-weight: var(--weight-semibold);
}

.overlay__body {
	flex: 1 1 auto;
	overflow-y: auto;
	overscroll-behavior: contain;
	padding: var(--space-5);
	-webkit-overflow-scrolling: touch;
}

.overlay__foot {
	flex-shrink: 0;
	padding: var(--space-4) var(--space-5);
	border-block-start: 1px solid var(--border);
	background-color: var(--surface);
	/* Clears the iOS home indicator when the sheet is flush to the bottom. */
	padding-block-end: max(var(--space-4), env(safe-area-inset-bottom));
}

/* ── Modal — centred dialog, for the size guide ──────────────────────────── */

.modal {
	position: fixed;
	inset: 0;
	z-index: var(--z-modal);
	display: grid;
	place-items: center;
	padding: var(--gutter);
	pointer-events: none;
}

/* A CLOSED modal must be inert, not merely transparent.
   ----------------------------------------------------------------------------
   This panel used to carry `pointer-events: auto` unconditionally and hide
   itself with `opacity: 0` alone. The quick-view shell is printed on every
   catalogue page, so that left an invisible, fully interactive block parked in
   the dead centre of the viewport at z-index 500 — permanently.

   The symptoms did not look like a modal bug at all:
     - product cards under the centre of the screen would not hover,
     - clicks there hit nothing,
     - and the wheel stopped scrolling the page, because `.overlay__body`
       inside it sets `overscroll-behavior: contain` and so swallowed the
       scroll instead of chaining it to the document.
   It got dramatically worse after the first quick view: the panel keeps its
   fetched markup, so the dead zone grew from a ~115px stub to the full 46rem
   dialog and stayed that size. Reloading "fixed" it only by emptying it again.

   `visibility: hidden` is what makes it inert. It is DELAYED rather than eased,
   for the same reason the drawer delays it: an eased transition still computes
   as hidden on the tick the class lands, and the overlay controller's `.focus()`
   into the dialog would be silently dropped. Zero duration on open, a full
   duration's delay on close so the fade still plays out. */
.modal__panel {
	inline-size: min(40rem, 100%);
	max-block-size: min(85svh, 46rem);
	background-color: var(--surface);
	border-radius: var(--radius-xl);
	box-shadow: var(--shadow-lg);
	display: flex;
	flex-direction: column;
	opacity: 0;
	visibility: hidden;
	transform: translateY(0.5rem) scale(0.98);
	transition: opacity var(--duration-base) var(--ease),
	            transform var(--duration-base) var(--ease-out),
	            visibility 0s linear var(--duration-base);
	pointer-events: none;
}

.modal.is-open .modal__panel {
	opacity: 1;
	visibility: visible;
	transition: opacity var(--duration-base) var(--ease),
	            transform var(--duration-base) var(--ease-out),
	            visibility 0s;
	transform: none;
	pointer-events: auto;
}

/* On small screens the size guide becomes a sheet rather than a shrunken
   dialog, because a 40rem table does not survive being squeezed to 390px. */
@media (max-width: 599px) {
	.modal { padding: 0; align-items: flex-end; }

	.modal__panel {
		inline-size: 100%;
		max-block-size: 88svh;
		border-end-start-radius: 0;
		border-end-end-radius: 0;
		transform: translateY(100%);
	}

	.modal.is-open .modal__panel { transform: translateY(0); }
}

/* ============================================================================
   ACCORDION
   ========================================================================== */

.accordion {
	border-block-start: 1px solid var(--border);
}

.accordion__item {
	border-block-end: 1px solid var(--border);
}

.accordion__trigger {
	inline-size: 100%;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-3);
	padding-block: var(--space-4);
	min-block-size: var(--tap-target);
	font-size: var(--text-body);
	font-weight: var(--weight-medium);
	text-align: start;
	color: var(--text);
}

.accordion__icon {
	flex-shrink: 0;
	inline-size: 1.25rem;
	block-size: 1.25rem;
	color: var(--text-muted);
	transition: transform var(--duration-base) var(--ease);
}

.accordion__trigger[aria-expanded="true"] .accordion__icon {
	transform: rotate(180deg);
}

.accordion__panel {
	padding-block-end: var(--space-5);
	color: var(--text-muted);
	font-size: var(--text-label);
	line-height: var(--leading-normal);
}

.accordion__panel[hidden] { display: none; }

/* ============================================================================
   TABS
   ========================================================================== */

.tabs__list {
	display: flex;
	gap: var(--space-1);
	border-block-end: 1px solid var(--border);
	overflow-x: auto;
	scrollbar-width: none;
}

.tabs__list::-webkit-scrollbar { display: none; }

.tabs__tab {
	padding-inline: var(--space-4);
	padding-block: var(--space-3);
	min-block-size: var(--tap-target);
	font-size: var(--text-label);
	font-weight: var(--weight-medium);
	color: var(--text-muted);
	white-space: nowrap;
	border-block-end: 2px solid transparent;
	margin-block-end: -1px;
	transition: color var(--duration-fast) var(--ease),
	            border-color var(--duration-fast) var(--ease);
}

.tabs__tab:hover { color: var(--text); }

.tabs__tab[aria-selected="true"] {
	color: var(--text);
	border-block-end-color: var(--neutral-900);
}

.tabs__panel { padding-block-start: var(--space-5); }
.tabs__panel[hidden] { display: none; }

/* ============================================================================
   EMPTY STATE
   ========================================================================== */

.empty {
	display: flex;
	flex-direction: column;
	align-items: center;
	text-align: center;
	gap: var(--space-4);
	/* Fluid, not a flat 64px. On a phone that fixed padding pushed the icon
	   half a screen below the heading it belonged to, so an empty panel read
	   as a page that had failed to load rather than as one with nothing in it
	   yet. The generous version is still there once there is room for it. */
	padding-block: clamp(var(--space-8), 6vw, var(--space-16));
	padding-inline: var(--gutter);
	max-inline-size: 30rem;
	margin-inline: auto;
}

.empty__icon {
	inline-size: 3rem;
	block-size: 3rem;
	color: var(--icon-muted);
}

.empty__title {
	font-size: var(--text-h3);
	font-weight: var(--weight-semibold);
}

.empty__text {
	color: var(--text-muted);
	font-size: var(--text-label);
}

/* An empty state that offers only one way out is a dead end when that one way
   is not what the person wanted. Two routes wrap rather than shrink, because a
   squeezed Persian CTA is worse than a second row. */
.empty__actions {
	display: flex;
	flex-wrap: wrap;
	justify-content: center;
	gap: var(--space-3);
	margin-block-start: var(--space-2);
}

/* ============================================================================
   LOADING
   ========================================================================== */

.skeleton {
	background-color: var(--surface-sunken);
	border-radius: var(--radius-sm);
	position: relative;
	overflow: hidden;
}

.skeleton::after {
	content: "";
	position: absolute;
	inset: 0;
	background: linear-gradient(
		90deg,
		transparent,
		rgb(255 255 255 / 0.6),
		transparent
	);
	transform: translateX(-100%);
	animation: benis-shimmer 1.4s infinite;
}

@keyframes benis-shimmer {
	100% { transform: translateX(100%); }
}

@media (prefers-reduced-motion: reduce) {
	.skeleton::after { animation: none; }
}

.spinner {
	inline-size: 1.5rem;
	block-size: 1.5rem;
	border: 2px solid var(--border-strong);
	border-block-start-color: var(--action);
	border-radius: 50%;
	animation: benis-spin 0.6s linear infinite;
}

/* A region waiting on AJAX dims but stays in place, so the grid does not
   collapse and bounce the user's scroll position. */
.is-busy {
	opacity: 0.45;
	pointer-events: none;
	transition: opacity var(--duration-fast) var(--ease);
}

/* ============================================================================
   TOAST
   ========================================================================== */

/* ABOVE THE FURNITURE, not under it.
   ----------------------------------------------------------------------------
   This sat 20px off the bottom edge, which on a phone is underneath the
   floating navigation pill — so "به علاقه‌مندی‌ها اضافه شد" appeared behind the
   nav, clipped and unreadable, which is exactly where a confirmation must not
   be. On a product page it was worse: the sticky purchase bar is parked there
   too, and the toast landed under both.

   The offset is built from the same token the nav's own position uses, so the
   two cannot drift apart, and `body.has-buy-bar` adds the purchase bar's height
   on the pages that have one. */
/* CENTRED WITHOUT A TRANSFORM — and that is the bug fix, not a style choice.
   ----------------------------------------------------------------------------
   This was `inset-inline-start: 50%` plus `transform: translateX(-50%)`, the
   standard LTR centring trick. It does not survive RTL, because the two halves
   resolve against different axes: `inset-inline-start` is LOGICAL and becomes
   `right: 50%` in a Persian document, while `translateX` is PHYSICAL and keeps
   moving the box LEFT. So instead of cancelling out, the two compounded — the
   toast was placed with its right edge at the centre and then shifted a further
   half-width to the left, landing almost entirely off the screen with a few
   pixels of itself showing at the edge. Exactly what the screenshot caught.

   `inset-inline: 0` with `margin-inline: auto` over a definite width centres in
   both directions with no transform involved, so there is nothing left to
   disagree about. */
.toaster {
	position: fixed;
	inset-block-end: calc(var(--mobile-nav-clearance) + var(--space-3));
	inset-inline: 0;
	margin-inline: auto;
	z-index: var(--z-toast);
	display: flex;
	flex-direction: column;
	gap: var(--space-2);
	inline-size: min(24rem, calc(100vw - var(--space-8)));
	pointer-events: none;
}

body.has-buy-bar .toaster {
	inset-block-end: calc(var(--mobile-nav-clearance) + 5.5rem);
}

/* From 1024px the floating nav and the purchase bar are both gone, so the toast
   returns to the corner it belongs in. */
@media (min-width: 1024px) {
	.toaster,
	body.has-buy-bar .toaster {
		inset-block-end: var(--space-5);
		/* start auto / end gutter — pinned to the inline-end corner, which is
		   the LEFT one on an RTL page and the right one on an LTR page. */
		inset-inline: auto var(--space-6);
		margin-inline: 0;
	}
}

.toast {
	display: flex;
	align-items: center;
	gap: var(--space-3);
	padding: var(--space-3) var(--space-4);
	border-radius: var(--radius-md);
	background-color: var(--neutral-900);
	color: var(--text-inverse);
	font-size: var(--text-label);
	box-shadow: var(--shadow-lg);
	pointer-events: auto;
	animation: benis-toast-in var(--duration-base) var(--ease-out);
}

.toast--success { background-color: var(--green-700); }
.toast--error   { background-color: var(--red-700); }

.toast svg { inline-size: 1.125rem; block-size: 1.125rem; }

.toast__close { margin-inline-start: auto; color: inherit; opacity: 0.7; }
.toast__close:hover { opacity: 1; }

@keyframes benis-toast-in {
	from { opacity: 0; transform: translateY(0.5rem); }
}

/* ============================================================================
   BREADCRUMBS — see 05-navigation.css
   ----------------------------------------------------------------------------
   A `.breadcrumb` / `.breadcrumb__sep` component used to be defined here, with
   a chevron separator mirrored for RTL. Nothing has emitted that markup for a
   long time: every template goes through marsi_breadcrumb(), which prints
   `.breadcrumb-wrap` + `.breadcrumb-list` and a "/" separator. Forty lines of
   rules for elements that never existed on a page — including the block
   padding, which is why tightening it here changed nothing on screen.
   ========================================================================== */

/* ============================================================================
   PAGINATION
   ========================================================================== */

/* TARGETS THE MARKUP WOOCOMMERCE ACTUALLY PRINTS.
   ----------------------------------------------------------------------------
   Every rule here used to hang off `.pagination`, and WooCommerce has never
   rendered that class: woocommerce_pagination() outputs

       <nav class="woocommerce-pagination"><ul class="page-numbers">
         <li><a class="page-numbers">…</a></li>
         <li><span class="page-numbers current">…</span></li>

   so not one selector matched and the shop's pagination was a bare browser
   list — bullets, blue underlined links, no spacing. The same mistake as the
   breadcrumb component: styles written against markup that was imagined rather
   than looked at.

   `.pagination` is kept in the selector list because the theme's own AJAX
   pagination container uses it, so both spellings land on one design. */
.pagination,
.woocommerce-pagination ul.page-numbers {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: var(--space-2);
	padding-block: var(--space-10);
	/* The <ul> is a real list and arrives with a marker and inherited padding. */
	margin: 0;
	padding-inline: 0;
	list-style: none;
}

.woocommerce-pagination ul.page-numbers li {
	display: flex;
	margin: 0;
	list-style: none;
}

.pagination a,
.pagination span,
.woocommerce-pagination a.page-numbers,
.woocommerce-pagination span.page-numbers {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-inline-size: var(--tap-target);
	min-block-size: var(--tap-target);
	padding-inline: var(--space-2);
	border: 1px solid var(--border);
	border-radius: var(--radius-md);
	background-color: var(--surface);
	color: var(--text);
	font-size: var(--text-label);
	font-weight: var(--weight-medium);
	font-variant-numeric: tabular-nums;
	text-decoration: none;
	transition: border-color var(--duration-fast) var(--ease),
	            background-color var(--duration-fast) var(--ease),
	            color var(--duration-fast) var(--ease);
}

.pagination a:hover,
.woocommerce-pagination a.page-numbers:hover {
	border-color: var(--brand);
	color: var(--text-link);
}

/* The current page takes the BRAND fill with dark ink — the same pairing as
   the primary button and the active tab in the mobile bar, rather than the
   near-black chip it used to describe. */
.pagination .current,
.woocommerce-pagination span.page-numbers.current {
	background-color: var(--brand);
	border-color: var(--brand);
	color: var(--text-on-brand);
}

.pagination .dots,
.woocommerce-pagination .page-numbers.dots {
	border-color: transparent;
	background-color: transparent;
	color: var(--text-muted);
	min-inline-size: auto;
	padding-inline: var(--space-1);
}

/* prev / next read as controls, not as numbers. */
.woocommerce-pagination a.prev,
.woocommerce-pagination a.next {
	padding-inline: var(--space-4);
}

/* ============================================================================
   NOTICES — WooCommerce messages, restyled at the source
   ========================================================================== */

.notice {
	display: flex;
	align-items: flex-start;
	gap: var(--space-3);
	padding: var(--space-4);
	border-radius: var(--radius-md);
	border: 1px solid var(--border);
	background-color: var(--surface-soft);
	font-size: var(--text-label);
	line-height: var(--leading-snug);
}

.notice svg { inline-size: 1.25rem; block-size: 1.25rem; flex-shrink: 0; }

.notice--success {
	background-color: var(--success-surface);
	border-color: var(--green-500);
	color: var(--green-700);
}

.notice--error {
	background-color: var(--danger-surface);
	border-color: var(--danger);
	color: var(--red-700);
}

.notice--warning {
	background-color: var(--warning-surface);
	border-color: var(--warning);
	color: var(--amber-700);
}

.notice__action { margin-inline-start: auto; flex-shrink: 0; }

/* ============================================================================
   STEPS — the checkout progress indicator
   ----------------------------------------------------------------------------
   Shared by the cart, the checkout and the confirmation page, because it is the
   same three-step journey seen from three positions. Each page sets one step to
   aria-current and marks the ones behind it as done.

   The connecting rule is drawn by the step itself rather than by a separate
   element, so the markup stays an ordered list and a screen reader hears
   "1 of 3", not a run of empty decorative nodes.
   ========================================================================== */

.steps {
	display: flex;
	align-items: center;
	gap: var(--space-2);
	flex-wrap: wrap;
}

.steps__item {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	font-size: var(--text-small);
	color: var(--text-muted);
}

/* The rule sits BEFORE each step except the first, and is the only thing here
   that is purely decorative. */
.steps__item + .steps__item::before {
	content: "";
	inline-size: clamp(1rem, 4vw, 2.5rem);
	block-size: 1px;
	background-color: var(--border-strong);
	margin-inline-end: var(--space-2);
}

.steps__num {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	inline-size: 1.5rem;
	block-size: 1.5rem;
	border-radius: 50%;
	border: 1px solid var(--border-strong);
	font-size: var(--text-caption);
	font-weight: var(--weight-semibold);
	flex-shrink: 0;
}

/* The current step carries the brand fill with dark ink — the same pairing the
   primary CTA uses, so "where you are" and "what to press" read as one system. */
.steps__item[aria-current="step"] {
	color: var(--text);
	font-weight: var(--weight-medium);
}

.steps__item[aria-current="step"] .steps__num {
	background-color: var(--brand);
	border-color: var(--brand);
	color: var(--text-on-brand);
}

/* A completed step is a tick, not a number: the number has stopped being
   information once the step is behind you. */
.steps__item.is-done .steps__num {
	background-color: var(--surface-sunken);
	border-color: transparent;
	color: var(--green-700);
}

.steps__item.is-done .steps__num svg { inline-size: 0.875rem; block-size: 0.875rem; }

/* On a phone the labels alone fill the row, so the rules go and the type
   shrinks rather than wrapping the tracker onto two lines. */
@media (max-width: 479px) {
	.steps__item { font-size: var(--text-caption); }
	.steps__item + .steps__item::before { inline-size: 0.75rem; }
}

/* ============================================================================
   PAGE HEAD — the title block every non-home template opens with
   ----------------------------------------------------------------------------
   One pattern for the cart, the checkout, the account, the wishlist and every
   content page, so a shopper moving between them never meets a different
   heading treatment. Sits on the page canvas rather than in a card: it is a
   label for the page, not an object on it.
   ========================================================================== */

/* Same reasoning as .plp__head: the wide layout earns the larger interval, a
   390px column does not. */
.page-head {
	padding-block: var(--space-5) var(--space-4);
}

@media (min-width: 768px) {
	.page-head { padding-block: var(--space-8) var(--space-6); }
}

.page-head__eyebrow {
	display: block;
	font-size: var(--text-small);
	font-weight: var(--weight-semibold);
	color: var(--blue-800);
	margin-block-end: var(--space-2);
}

.page-head__title {
	font-size: var(--text-h1);
	line-height: var(--leading-tight);
	letter-spacing: var(--tracking-heading);
}

.page-head__sub {
	margin-block-start: var(--space-3);
	color: var(--text-muted);
	max-inline-size: var(--container-text);
	line-height: var(--leading-normal);
}

.page-head__steps { margin-block-start: var(--space-5); }
