/* OSTADEEZ — shop layer. Loaded only on views that render product markup.
   Component sizes come from the approved wireframes, not from taste. */

/* ---------------------------------------------------------------- page head */

.ostz-phead {
	padding-block: var(--ostz-space-8) var(--ostz-space-2);
}

/* ⚠ BREADCRUMB moved to modules/ostadeez-m-crumb.css on 2026-09-03, and this sheet is why it had
   to: it loads on WooCommerce views and the front page, while `ostz_breadcrumb_entry()` prints a
   trail on every single ARTICLE and every project update — templates that never load it. The
   `--entry` variant had been re-declared in main.css to compensate, so the trail LOOKED right on a
   post while `.ostz-crumb-link` had no rule at all there: no tap-target padding, no hover.
   Nothing about the breadcrumb may be declared here again. */

/* The gallery's zoom control measured 17×23 — under the tap floor, and it is the only way into the
   full-screen viewer. Sized here rather than in the lightbox module because it lives on the PRODUCT
   page, not inside the viewer. */
.ostz .woocommerce-product-gallery__trigger {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 36px;
	height: 36px;
	border-radius: var(--ostz-radius-sm, 10px);
	background: color-mix(in srgb, var(--ostz-bg-raised, #15171e) 82%, transparent);
	text-decoration: none;
}

.ostz .woocommerce-product-gallery__trigger:hover {
	background: var(--ostz-bg-raised-2, #1b1e27);
}

/* ---------------------------------------------------------------- toolbar */

.ostz-toolbar {
	position: sticky;
	top: var(--ostz-header-real, var(--ostz-header-h));
	z-index: 20;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--ostz-space-3);
	margin-block: var(--ostz-space-6) var(--ostz-space-4);
	padding-block: var(--ostz-space-2);
	background: color-mix(in srgb, var(--ostz-bg, #0b0c10) 85%, transparent);
	backdrop-filter: blur(8px);
}

/*
 * ⚠ CHIP moved to modules/ostadeez-m-chip.css on 2026-09-03, and THIS SHEET is why it had to.
 * It loads on WooCommerce views and the front page only — but `search.php` prints chips too, and a
 * plain `?s=` search is not a WooCommerce view, so the category row on the search results page
 * rendered as bare links with no pill, no border and no tap target. Same shape as the dead
 * `.ostz-btn-ghost`: a shell component parked in a per-section sheet.
 * Nothing about a chip may be declared here again.
 */

.ostz-toolbar-meta {
	display: flex;
	align-items: center;
	gap: var(--ostz-space-3);
}

/* ---------------------------------------------------------------- archive grid */

.ostz .woocommerce ul.products,
.ostz ul.products {
	display: grid;
	grid-template-columns: repeat(4, 1fr);
	gap: 18px;
	margin: var(--ostz-space-4) 0 0;
	padding: 0;
	list-style: none;
}

@media (max-width: 900px) {
	.ostz .woocommerce ul.products,
	.ostz ul.products {
		grid-template-columns: repeat(2, 1fr);
	}
}

@media (max-width: 520px) {
	.ostz .woocommerce ul.products,
	.ostz ul.products {
		grid-template-columns: 1fr;
	}
}

.ostz ul.products::before,
.ostz ul.products::after {
	content: none;
}

.ostz ul.products li.product {
	width: auto !important;
	margin: 0 !important;
	float: none !important;
	clear: none !important;
}

/* ---------------------------------------------------------------- product card */

.ostz-card {
	position: relative;
	display: flex;
	flex-direction: column;
	height: 100%;
	overflow: hidden;
	border: 1px solid var(--ostz-line, #262a35);
	border-radius: var(--ostz-radius);
	background: var(--ostz-bg-raised, #15171e);
	transition: border-color .18s, transform .18s, box-shadow .18s;
}

.ostz-card:hover {
	border-color: var(--ostz-line-strong, #333846);
	transform: translateY(-4px);
	box-shadow: 0 14px 34px -18px color-mix(in srgb, var(--ostz-accent, #00e0d5) 40%, transparent);
}

/* 1:1, matching the wireframe — not the 4:5 the first pass guessed. */
/*
 * ⚠ The ratio is enforced on the IMAGE, not only on this box — that distinction is the whole bug.
 *
 * `aspect-ratio` on a block container is only a suggestion: it yields a definite height until the
 * CONTENT wants more, and then the box grows. The image inside is the content, so a 600×750 upload
 * rendered 200×250 and stretched this box to 251px. The card ratio the wireframe specifies simply
 * did not exist on any archive or on the front page, and nothing errored.
 *
 * `overflow: hidden` closes the same door for anything else dropped in here later.
 */
.ostz-card-media {
	display: block;
	aspect-ratio: 1;
	overflow: hidden;
	border-block-end: 1px solid var(--ostz-line, #262a35);
	background: var(--ostz-bg-sunken, #111318);
}

/* BADGE moved to modules/ostadeez-m-badge.css on 2026-09-02 — see modules/_ARCHITECTURE.md.
   Nothing about the badge may be declared here again: one component, one file. */

/* The image carries the ratio itself, so it is square whatever the shop owner uploads, and it no
   longer depends on the parent resolving a height first. */
.ostz-card-img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 1;
	object-fit: cover;
}

.ostz-card-img-empty {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 1;
}

/*
 * ⚠ `flex: 1` + `margin-block-start: auto` on the action is what bottom-aligns the CTAs across a
 * row. Without it a two-line title pushed its own button 20px below its neighbours' — measured on
 * the archive, three buttons at y=1089 and one at 1109 — and a row of buttons that do not line up
 * reads as a rendering fault rather than as a grid.
 */
.ostz-card-body {
	display: flex;
	flex: 1;
	flex-direction: column;
	gap: 9px;
	padding: 13px 14px;
}

.ostz-card-body > .button,
.ostz-card-body > .ostz-card-actions,
.ostz-card-body > .dm-eng-archive-actions:not(:empty) {
	margin-block-start: auto;
}

/* The waitlist row rides directly under the button rather than being pushed to the bottom itself,
   or the two actions separate and stop reading as one group. */
.ostz-card-body > .button ~ .dm-eng-archive-actions {
	margin-block-start: 0;
}

/*
 * ⚠ The engagement plugin prints this wrapper on EVERY card, empty on the ones with no waitlist.
 * An empty flex child still takes the row gap, so it added a stray 9px under the button on most of
 * the grid. Removing it is also what lets the rule above bottom-align the right element.
 */
.ostz-card-body > .dm-eng-archive-actions:empty {
	display: none;
}

/* The title is the card's only real link — the media above it is `aria-hidden` with `tabindex="-1"`
   — and it measured 23px tall. The padding lifts it over the 24px floor without moving the text. */
.ostz-card-link {
	display: inline-block;
	padding-block: 1px;
}

.ostz-card-title {
	margin: 0;
	font-size: var(--ostz-text-base);
	font-weight: var(--ostz-weight-bold);
	line-height: var(--ostz-leading-snug);
}

.ostz-card-link {
	color: var(--ostz-text, #f2f4f8);
	text-decoration: none;
}

.ostz-card-link:hover {
	color: var(--ostz-accent, #00e0d5);
}

/* The card foot puts price and action on one row, as in the wireframe. */
.ostz .ostz-card .price,
.ostz .ostz-card .ostz-card-actions {
	margin: 0;
}

/* The card's buttons are the button COMPONENT at its compact size — see
   modules/ostadeez-m-button.css. `ostz_loop_button_class()` puts `ostz-btn ostz-btn--compact` on
   them in PHP, and marks the purchasable one `--primary` so «افزودن به سبد خرید» finally outranks
   «اطلاعات بیشتر» instead of being indistinguishable from it. */

/* ---------------------------------------------------------------- price */

.ostz .price {
	color: var(--ostz-text, #f2f4f8);
	font-size: var(--ostz-text-base);
	font-weight: var(--ostz-weight-bold);
	unicode-bidi: isolate;
}

.ostz .price del {
	color: var(--ostz-text-faint, #828a9c);
	font-weight: var(--ostz-weight-normal);
}

/* ⚠ WooCommerce wraps the SALE price in `<ins>`, whose browser default is an underline. With
   WooCommerce's own stylesheet dequeued nothing ever reset it, so MEASURED 2026-09-05 every
   discounted price on the site was underlined — which on a page full of links reads as «this
   price is clickable». The strike on `<del>` is meaning and stays; the underline on `<ins>` is
   a browser default nobody chose. */
.ostz .price ins {
	background: none;
	text-decoration: none;
}

.ostz-price-soon {
	display: inline-block;
	color: var(--ostz-accent, #00e0d5);
	font-size: var(--ostz-text-base);
	font-weight: var(--ostz-weight-black);
}

/* ---------------------------------------------------------------- buttons
 * Moved to modules/ostadeez-m-button.css on 2026-09-02, and this is the block that made the move
 * necessary: `.ostz .button` (0-2-0) was written here as the site's loud primary and could never
 * beat the baseline's `.ostz a.button` (0-2-1). It painted on ZERO elements.
 *
 * ⚠ The first move left `.ostz-btn-ghost` and `.ostz-btn-primary` behind on the claim that they
 * «appeared in no template at all». They did — the 404, the front page and two update surfaces —
 * and the real defect was subtler than a missing rule: they were declared in THIS sheet, which
 * loads only on WooCommerce views and the front page, so the front page looked right and the 404
 * did not. A variant of a shell component may never live in a per-section sheet. All of them are
 * in modules/ostadeez-m-button.css now, single-dash spellings kept there as aliases.
 */

/* ---------------------------------------------------------------- single product */

/*
 * ⚠ `position: relative` is LOAD-BEARING, not decoration. WooCommerce prints `span.onsale` inside
 * this wrapper with `position: absolute`, and with nothing positioned between it and <body> the
 * containing block became the VIEWPORT: MEASURED 2026-09-05, the «تخفیف!» badge rendered at
 * x=10 y=10 — the top-left corner of the page, sitting on the announcement bar — on every
 * on-sale product page, desktop and phone. It looks correct on the archive because `li.product`
 * IS positioned there, which is exactly why looking at the shop page never revealed it.
 */
.ostz-pdp {
	position: relative;
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 34px;
	padding-block-end: var(--ostz-space-6);
	align-items: start;
}

/* Woo's gallery and summary are floated by default; the grid owns placement here.
   ⚠ `.ostz-gallery` is OUR class — `ostz_gallery_classes()` adds it through
   `woocommerce_single_product_image_gallery_classes`. It was added and then never used by any
   rule, so the theme was still selecting the platform's name for a surface it had already claimed.
   `min-inline-size: 0` is what stops a wide image forcing the grid column past its share. */
.ostz-gallery,
.ostz-pdp .summary {
	min-inline-size: 0;
	width: auto !important;
	float: none !important;
	margin: 0 !important;
}

.ostz-pdp .woocommerce-product-gallery__wrapper {
	border: 1px solid var(--ostz-line-strong, #333846);
	border-radius: var(--ostz-radius);
	overflow: hidden;
	background: var(--ostz-bg-raised, #15171e);
}

.ostz-pdp .woocommerce-product-gallery__image img {
	display: block;
	width: 100%;
	aspect-ratio: 1;
	object-fit: cover;
}

.ostz-pdp .flex-control-thumbs {
	display: flex;
	gap: 10px;
	margin: 12px 0 0;
	padding: 0;
	list-style: none;
}

.ostz-pdp .flex-control-thumbs li {
	width: 66px;
	height: 66px;
}

.ostz-pdp .flex-control-thumbs img {
	width: 100%;
	height: 100%;
	border: 1px solid var(--ostz-line-strong, #333846);
	border-radius: var(--ostz-radius-sm);
	object-fit: cover;
	cursor: pointer;
	opacity: .7;
	transition: .15s;
}

.ostz-pdp .flex-control-thumbs img.flex-active,
.ostz-pdp .flex-control-thumbs img:hover {
	border-color: var(--ostz-accent, #00e0d5);
	opacity: 1;
}

/* ⚠ COMPOUND, not descendant. `ostz` and `single-product` are both classes on <body>, i.e. the
   SAME element — `.ostz .single-product` asks for one inside the other and silently never
   matches. This whole block was dead until measured in a browser. */
.ostz.single-product div.product .product_title {
	margin: 12px 0 8px;
	font-size: var(--ostz-title-lg);
	font-weight: var(--ostz-weight-black);
	line-height: var(--ostz-leading-heading);
	letter-spacing: -.5px;
}

/* ---------------------------------------------------------------- supply tag */

/*
 * ⚠ The second accent cannot be INK. MEASURED 2026-09-05: this pill read 4.40:1 in dark, on the
 * three sentences that carry the whole drop model — «دراپِ محدود — ۳۸ از ۳۵۰ باقی مانده»،
 * «هنوز عرضه نشده»، «تمام شد». #c026ff as text measures 4.23 on a raised ground and 3.93 on the
 * second raised level before this pill's own wash is even counted, so no amount of tuning the
 * wash rescues it. Same resolution as the sale flash: the magenta is the RING and the dot, the
 * words take the ordinary ink. The colour still carries the identity; it just stops carrying
 * the legibility.
 */
.ostz-tag-supply {
	display: inline-flex;
	align-items: center;
	gap: 7px;
	margin: 0;
	padding: 4px 12px;
	border: 1px solid var(--ostz-accent-2, #c026ff);
	border-radius: 20px;
	background: color-mix(in srgb, var(--ostz-accent-2, #c026ff) 8%, transparent);
	color: var(--ostz-text, #f2f4f8);
	font-size: var(--ostz-text-xs);
}

/* The dot keeps the brand colour — it is a shape, not text, so contrast is not a legibility
   question for it. */
.ostz-tag-dot {
	width: 7px;
	height: 7px;
	border-radius: 50%;
	background: var(--ostz-accent-2, #c026ff);
}

.ostz.single-product div.product .price {
	font-size: var(--ostz-text-xl);
	font-weight: var(--ostz-weight-bold);
	color: var(--ostz-text-dim, #aab0be);
}

.ostz-prep-note {
	margin: var(--ostz-space-3) 0 0;
	padding: var(--ostz-space-3) var(--ostz-space-4);
	border-inline-start: 2px solid var(--ostz-accent, #00e0d5);
	border-radius: var(--ostz-radius-sm);
	background: var(--ostz-bg-raised, #15171e);
	color: var(--ostz-text-dim, #aab0be);
	font-size: var(--ostz-text-sm);
}

/* ---------------------------------------------------------------- swatches & sizes */

/* The native select stays put until the enhancement layer actually builds the buttons —
   with JS off it is the control, so it must never be hidden by CSS alone. */
.ostz-has-swatches select {
	display: none;
}

/* The refusal the buy button used to keep to itself. It is an alert, so it announces itself, and
   it sits above the form rather than below the fold. */
.ostz-buy-notice {
	margin: 0 0 var(--ostz-space-3);
	padding: 10px 14px;
	border: 1px solid var(--ostz-accent, #00e0d5);
	border-radius: var(--ostz-radius-sm, 10px);
	background: color-mix(in srgb, var(--ostz-accent, #00e0d5) 12%, var(--ostz-bg-raised, #15171e));
	color: var(--ostz-text, #f2f4f8);
	font-size: var(--ostz-text-sm);
	font-weight: var(--ostz-weight-bold);
}

/* The field the message is about, briefly. Not colour alone — the ring is a shape change too. */
.ostz-needs-choice {
	outline: 2px solid var(--ostz-accent, #00e0d5);
	outline-offset: 6px;
	border-radius: var(--ostz-radius-sm, 10px);
}

.ostz-swatches {
	display: flex;
	flex-wrap: wrap;
	gap: 12px;
	align-items: center;
}

.ostz-swatch {
	width: 34px;
	height: 34px;
	padding: 0;
	border: 2px solid transparent;
	border-radius: 50%;
	background: var(--ostz-bg-raised-2, #1b1e27);
	box-shadow: inset 0 0 0 1px var(--ostz-line-strong, #333846);
	cursor: pointer;
	transition: .15s;
}

/* The colour the JS resolved for this term. The inset ring stays so a white swatch still has an
   edge against a light panel and a near-black one still reads against the dark. */
.ostz-swatch.has-colour {
	background: var(--ostz-swatch);
}

/*
 * The honest fallback: a colour we have no value for shows its NAME rather than an anonymous dot.
 * Wider than the round swatches because a word needs room — and two named options can never look
 * identical, which is the whole failure this replaces.
 */
.ostz-swatch.is-named {
	width: auto;
	min-width: 34px;
	height: 34px;
	padding-inline: 12px;
	border-radius: 999px;
	color: var(--ostz-text, #f2f4f8);
	font-family: inherit;
	font-size: var(--ostz-text-xs);
	font-weight: var(--ostz-weight-bold);
}

.ostz-swatch.is-on {
	border-color: var(--ostz-accent, #00e0d5);
}

.ostz-sizes {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

.ostz-size {
	min-width: 48px;
	height: 42px;
	padding-inline: var(--ostz-space-3);
	border: 1px solid var(--ostz-line-strong, #333846);
	border-radius: var(--ostz-radius-sm);
	background: var(--ostz-bg-raised, #15171e);
	color: var(--ostz-text-dim, #aab0be);
	font-family: inherit;
	font-size: var(--ostz-text-base);
	font-weight: var(--ostz-weight-bold);
	cursor: pointer;
	transition: .15s;
}

.ostz-size:hover {
	color: var(--ostz-text, #f2f4f8);
	border-color: var(--ostz-text-faint, #828a9c);
}

.ostz-size.is-on {
	border-color: transparent;
	background: var(--ostz-accent, #00e0d5);
	color: var(--ostz-accent-ink, #08090c);
}

/* ---------------------------------------------------------------- quantity stepper */

/* Woo wraps the input in its own .quantity div; the stepper is built inside that, so the
   outer wrapper must not add a second box or its own spacing. */
.ostz .quantity {
	display: contents;
}

.ostz-qty {
	display: flex;
	align-items: center;
	overflow: hidden;
	border: 1px solid var(--ostz-line-strong, #333846);
	border-radius: 12px;
}

.ostz-qty-btn {
	width: 44px;
	height: 48px;
	border: 0;
	background: var(--ostz-bg-raised, #15171e);
	color: var(--ostz-text, #f2f4f8);
	font-family: inherit;
	font-size: var(--ostz-text-xl);
	cursor: pointer;
}

.ostz-qty-btn:hover {
	background: var(--ostz-bg-raised-2, #1b1e27);
}

.ostz .ostz-qty input.qty {
	width: 52px;
	height: 48px;
	border: 0;
	border-radius: 0;
	background: transparent;
	font-weight: var(--ostz-weight-bold);
	text-align: center;
	-moz-appearance: textfield;
}

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

/* The single most important action on a product page gets the loud treatment, as the wireframe
   has it. The gradient is otherwise reserved for «place order». */
/*
 * ⚠ `flex: 1` alone is not enough on a phone. `flex-basis` defaults to `auto`, so the button is
 * only as wide as its own text before it grows — and the quantity stepper beside it, which has a
 * fixed width, took the larger share: MEASURED at 375px, the buy button was 143px and the stepper
 * 188px. The single most important control on the page was the smaller of the two.
 * A zero basis plus a minimum makes the button take the row and the stepper take what it needs.
 */
.ostz form.cart .single_add_to_cart_button {
	flex: 1 1 0;
	min-width: 55%;
	min-height: 48px;
	background: var(--ostz-accent, #00e0d5);
	color: var(--ostz-accent-ink, #08090c);
}

.ostz form.cart .quantity,
.ostz .woocommerce-variation-add-to-cart .quantity {
	flex: 0 1 auto;
}

/* ---------------------------------------------------------------- assurance */

.ostz-assure {
	margin: var(--ostz-space-4) 0 0;
	padding: 14px 16px;
	border: 1px solid var(--ostz-line, #262a35);
	border-radius: 12px;
	background: var(--ostz-bg-raised, #15171e);
	list-style: none;
	font-size: var(--ostz-text-sm);
}

.ostz-assure-item {
	display: flex;
	gap: 9px;
	align-items: center;
	padding-block: 3px;
	color: var(--ostz-text-dim, #aab0be);
}

.ostz-assure-check {
	color: var(--ostz-accent, #00e0d5);
	font-weight: var(--ostz-weight-black);
}

/* ---------------------------------------------------------------- origin strip */

.ostz-origin {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 14px;
	margin-top: 14px;
	padding: 14px 16px;
	border: 1px solid var(--ostz-line-strong, #333846);
	border-radius: 12px;
	background: var(--ostz-bg-raised, #15171e);
}

.ostz-origin-body {
	display: flex;
	flex-direction: column;
	flex: 1;
	min-width: 160px;
}

.ostz-origin-kicker {
	color: var(--ostz-accent-2, #c026ff);
	font-size: var(--ostz-text-xs);
	font-weight: var(--ostz-weight-bold);
	letter-spacing: 1.5px;
}

.ostz-origin-title {
	margin-top: 2px;
	font-size: var(--ostz-text-base);
	font-weight: var(--ostz-weight-bold);
}

.ostz-origin-go {
	padding: 6px 14px;
	border: 1px solid var(--ostz-line-strong, #333846);
	border-radius: 16px;
	color: var(--ostz-accent, #00e0d5);
	font-size: var(--ostz-text-sm);
	text-decoration: none;
	white-space: nowrap;
}

.ostz-origin-go:hover {
	border-color: var(--ostz-accent, #00e0d5);
}

/* ---------------------------------------------------------------- sections */

.ostz.single-product .related,
.ostz.single-product .upsells,
.ostz .woocommerce-tabs {
	padding-block: 30px;
	border-block-start: 1px solid var(--ostz-line, #262a35);
}

.ostz.single-product .related > h2,
.ostz .woocommerce-tabs h2 {
	margin-block-end: 12px;
	font-size: var(--ostz-text-2xl);
	font-weight: var(--ostz-weight-black);
}

/*
 * ⚠ Related products are a SHORT list — WooCommerce prints whatever it found, often two or three.
 * The archive's fixed four-column grid then left one or two empty columns and the section read as
 * a failed load.
 *
 * ⚠ AND `auto-fill` DOES NOT FIX THAT — it creates the empty tracks anyway and only declines to
 * place anything in them. MEASURED 2026-09-05 with one up-sell: the computed value was five
 * 211px tracks, so a single card sat beside ~900px of nothing and the section still read as
 * broken. `auto-fit` COLLAPSES the empty tracks, which is the behaviour this comment always
 * described. The two keywords differ only when the list is shorter than the row, and neither
 * section had ever been seen in that state.
 */
 * ⚠ AND THE TRACK IS CAPPED, not `1fr`. `auto-fit` collapses the empty tracks and then hands ALL
 * the freed space to what is left, so with one up-sell the single track measured the full 1128px
 * and the card was a banner — the same «this section looks broken» in a new shape. A `minmax`
 * with a real maximum keeps a card the size of a card whether there are one or four, and
 * `justify-content: start` puts them where the reader starts rather than centring a lonely one.
 */
.ostz.single-product .related ul.products,
.ostz.single-product .upsells ul.products {
	grid-template-columns: repeat(auto-fit, minmax(200px, 268px));
	justify-content: start;
}

/*
 * Product tabs. WooCommerce ships them as a plain <ul>, and with its stylesheet dequeued they
 * rendered as a bulleted list of underlined links. This is the whole widget, ours.
 */
.ostz .woocommerce-tabs ul.tabs {
	display: flex;
	flex-wrap: wrap;
	gap: 6px;
	margin: 0 0 var(--ostz-space-6);
	padding: 0;
	border-block-end: 1px solid var(--ostz-line, #262a35);
	list-style: none;
}

.ostz .woocommerce-tabs ul.tabs li {
	margin: 0;
	padding: 0;
	list-style: none;
}

.ostz .woocommerce-tabs ul.tabs li a {
	display: block;
	padding: 10px 16px;
	border-block-end: 2px solid transparent;
	color: var(--ostz-text-dim, #aab0be);
	font-size: var(--ostz-text-base);
	font-weight: var(--ostz-weight-bold);
	text-decoration: none;
}

.ostz .woocommerce-tabs ul.tabs li.active a {
	border-block-end-color: var(--ostz-accent, #00e0d5);
	color: var(--ostz-text, #f2f4f8);
}

/*
 * The product meta line reads «شناسه محصول: نامعلوم» on every fixture and on any product whose
 * SKU is not filled — a field that mostly renders the word «unknown» is noise on a buyer's page.
 * The category is already in the breadcrumb above.
 */
.ostz.single-product .product_meta {
	display: none;
}

/* ---------------------------------------------------------------- notices, results, sorting */

.ostz .woocommerce-result-count,
.ostz .woocommerce-ordering {
	display: inline-block;
	color: var(--ostz-text-dim, #aab0be);
	font-size: var(--ostz-text-sm);
}

.ostz .woocommerce-ordering {
	margin-inline-start: var(--ostz-space-4);
}

/*
 * ⚠ THE GENERIC FIELD moved to modules/ostadeez-m-form.css on 2026-09-03. It was declared HERE
 * (46px min-height, 11px/13px padding, the strong line colour) and AGAIN in the WooCommerce bridge
 * (44px, 12px/14px, the plain line colour) — the same control, two sizes, and load order decided
 * which one a page got. Worse, this sheet loads only on WooCommerce views and the front page, so
 * the contact page's fields were reached by neither.
 * Nothing about a form field may be declared here again.
 */

.ostz .woocommerce-ordering select {
	width: auto;
	min-height: 40px;
	padding-block: 6px;
}

.ostz input::placeholder,
.ostz textarea::placeholder {
	color: var(--ostz-text-faint, #828a9c);
}

.ostz input:focus,
.ostz select:focus,
.ostz textarea:focus {
	outline: none;
	border-color: var(--ostz-accent, #00e0d5);
}

/* Two-up field rows and the table scroll guard moved to modules/ostadeez-b-woo.css on 2026-09-03,
   with the rest of the form and table surfaces they belong to. Both were ALSO declared in
   ostadeez-woo-baseline.css, which is the duplication this pass exists to end. */

/* ---------------------------------------------------------------- variations */

.ostz table.variations {
	width: 100%;
	margin: var(--ostz-space-6) 0 var(--ostz-space-4);
	border-collapse: collapse;
}

.ostz table.variations th,
.ostz table.variations td {
	padding: var(--ostz-space-2) 0;
	text-align: start;
	vertical-align: middle;
}

.ostz table.variations th {
	width: 7rem;
	color: var(--ostz-text-dim, #aab0be);
	font-size: var(--ostz-text-sm);
	font-weight: var(--ostz-weight-medium);
}

.ostz .reset_variations {
	display: inline-block;
	margin-block-start: var(--ostz-space-2);
	color: var(--ostz-text-faint, #828a9c);
	font-size: var(--ostz-text-xs);
}

.ostz-size-guide-trigger {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	margin-block: 0 var(--ostz-space-2);
	padding: 0;
	border: 0;
	background: transparent;
	color: var(--ostz-accent, #00e0d5);
	font-family: inherit;
	font-size: var(--ostz-text-sm);
	font-weight: var(--ostz-weight-medium);
	text-decoration: underline;
	text-underline-offset: 4px;
	cursor: pointer;
}

.ostz .quantity input[type='number'] {
	width: 5rem;
	min-height: 44px;
	padding: var(--ostz-space-2);
	border: 1px solid var(--ostz-line, #262a35);
	border-radius: var(--ostz-radius);
	background: var(--ostz-bg-raised, #15171e);
	color: var(--ostz-text, #eceef2);
	font-family: inherit;
	text-align: center;
}

/* Simple products: qty + button on one row. Variable products keep the form as a block
   (the variation table lives inside it) and the row is the add-to-cart wrapper instead. */
.ostz form.cart,
.ostz .woocommerce-variation-add-to-cart {
	display: flex;
	flex-wrap: wrap;
	align-items: stretch;
	gap: 12px;
	margin-top: 20px;
}

.ostz form.variations_form.cart {
	display: block;
}

.ostz .variations .label label {
	display: block;
	margin-block-end: 9px;
	color: var(--ostz-text-faint, #828a9c);
	font-size: var(--ostz-text-sm);
	font-weight: var(--ostz-weight-bold);
}

/* ---------------------------------------------------------------- sticky buy bar */

.ostz-buybar {
	position: fixed;
	inset-inline: 0;
	inset-block-end: 0;
	z-index: 100;
	display: none;
	align-items: center;
	justify-content: space-between;
	gap: var(--ostz-space-4);
	padding: var(--ostz-space-3) var(--ostz-space-4);
	border-block-start: 1px solid var(--ostz-line-strong, #333846);
	background: color-mix(in srgb, var(--ostz-bg, #0b0b0f) 94%, transparent);
	backdrop-filter: blur(10px);
}

.ostz-buybar-info {
	display: flex;
	flex-direction: column;
	min-width: 0;
}

.ostz-buybar-name {
	font-size: var(--ostz-text-xs);
	color: var(--ostz-text-dim, #aab0be);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.ostz-buybar-price {
	font-size: var(--ostz-text-md);
	font-weight: var(--ostz-weight-bold);
	unicode-bidi: isolate;
}

.ostz-buybar-cta {
	flex: 0 0 auto;
}

/* ---------------------------------------------------------------- step indicator */

.ostz-steps {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: center;
	gap: var(--ostz-space-2);
	margin: 0 0 var(--ostz-space-6);
	padding: var(--ostz-space-6) 0 var(--ostz-space-2);
	list-style: none;
	counter-reset: none;
}

.ostz-step {
	display: flex;
	align-items: center;
	gap: 9px;
	padding-inline-end: var(--ostz-space-4);
	color: var(--ostz-text-faint, #828a9c);
	font-size: var(--ostz-text-sm);
}

.ostz-step + .ostz-step::before {
	content: '';
	width: 34px;
	height: 1.5px;
	margin-inline-end: var(--ostz-space-4);
	background: var(--ostz-line-strong, #333846);
}

/* ⚠ Emitted by `ostz_checkout_steps()` since the step indicator was built and declared NOWHERE —
   the word beside each number had no size, weight or colour of its own and inherited whatever the
   row happened to give it. A class written into markup with no rule anywhere is the same defect
   as a rule with no markup; both were found by the same sweep. */
.ostz-step-label {
	font-size: var(--ostz-text-xs);
	font-weight: var(--ostz-weight-medium);
	color: var(--ostz-text-dim, #aab0be);
}

.ostz-step-on .ostz-step-label {
	color: var(--ostz-text, #f2f4f8);
	font-weight: var(--ostz-weight-bold);
}

.ostz-step-num {
	display: grid;
	place-items: center;
	width: 26px;
	height: 26px;
	border: 1.5px solid var(--ostz-line-strong, #333846);
	border-radius: 50%;
	background: var(--ostz-bg-raised, #15171e);
	font-size: var(--ostz-text-sm);
	unicode-bidi: isolate;
}

.ostz-step-on {
	color: var(--ostz-text, #f2f4f8);
}

.ostz-step-on .ostz-step-num {
	border-color: transparent;
	background: var(--ostz-accent, #00e0d5);
	color: var(--ostz-accent-ink, #08090c);
	font-weight: var(--ostz-weight-bold);
}

.ostz-step-done .ostz-step-num {
	border-color: var(--ostz-accent, #00e0d5);
	color: var(--ostz-accent, #00e0d5);
}

.ostz-trustline {
	margin: 0 0 var(--ostz-space-6);
	color: var(--ostz-text-faint, #828a9c);
	font-size: var(--ostz-text-xs);
	text-align: center;
}

/* ---------------------------------------------------------------- checkout columns */

/*
 * ⚠ MEASURED: this grid has THREE children, not two. WooCommerce puts an <h3> («سفارش شما»)
 * between the customer details and the order review. Auto-placement therefore sent the heading
 * into column 2 on its own and pushed the order summary down into column 1 — so the "two-column
 * checkout" rendered as one narrow column with an orphan heading floating beside it, from the
 * day it was written. Placement is explicit now; auto-placement cannot be trusted on markup
 * whose child list belongs to somebody else.
 */
.ostz .woocommerce-checkout {
	display: grid;
	grid-template-columns: 1.5fr 1fr;
	gap: 20px 20px;
	align-items: start;
	padding-block-end: var(--ostz-space-12);
}

.ostz .woocommerce-checkout > #customer_details {
	grid-column: 1;
	grid-row: 1 / span 2;
}

.ostz .woocommerce-checkout > #order_review_heading {
	grid-column: 2;
	grid-row: 1;
	align-self: end;
	margin: 0;
}

.ostz .woocommerce-checkout > #order_review {
	grid-column: 2;
	grid-row: 2;
	position: sticky;
	top: calc(var(--ostz-header-real, var(--ostz-header-h, 64px)) + var(--ostz-space-4));
}

@media (max-width: 820px) {
	.ostz .woocommerce-checkout {
		grid-template-columns: 1fr;
	}

	.ostz .woocommerce-checkout > #customer_details,
	.ostz .woocommerce-checkout > #order_review_heading,
	.ostz .woocommerce-checkout > #order_review {
		grid-column: 1;
		grid-row: auto;
	}

	.ostz .woocommerce-checkout > #order_review {
		position: static;
	}
}

/*
 * The billing column was itself split in two at 310px each, which is not a usable form width.
 * Shipping is behind a toggle and the notes field is a single textarea, so one column with the
 * fields laid out inside it is both wider and simpler.
 */
.ostz #customer_details.col2-set {
	display: block;
}

.ostz #customer_details .col-1,
.ostz #customer_details .col-2 {
	width: auto;
	float: none;
}

.ostz #customer_details,
.ostz .woocommerce-checkout-payment {
	padding: 18px 20px;
	border: 1px solid var(--ostz-line, #262a35);
	border-radius: var(--ostz-radius-lg);
	background: var(--ostz-bg-raised, #15171e);
}

.ostz #customer_details .col-1,
.ostz #customer_details .col-2 {
	width: auto;
	float: none;
}

.ostz #order_review_heading {
	grid-column: 2;
	margin: 0;
}

/* The summary follows the shopper down a long form. */
.ostz #order_review {
	position: sticky;
	top: calc(var(--ostz-header-real, var(--ostz-header-h)) + var(--ostz-space-4));
	overflow: hidden;
	border: 1px solid var(--ostz-line-strong, #333846);
	border-radius: var(--ostz-radius-lg);
	background:
		radial-gradient(90% 60% at 90% 0, color-mix(in srgb, var(--ostz-accent, #00e0d5) 8%, transparent), transparent 60%),
		var(--ostz-bg-raised, #15171e);
}

@media (max-width: 820px) {
	.ostz #order_review_heading,
	.ostz #order_review {
		grid-column: 1;
		position: static;
	}
}

/* The site's biggest CTA moved to modules/ostadeez-m-button.css as `--hero` on 2026-09-03, and is
   mapped from `#place_order` / `.checkout-button` in modules/ostadeez-b-woo.css. It was the fifth
   place a button was defined outside the button component, and its own comment still promised a
   gradient the palette stopped emitting a day earlier. */

/* ---------------------------------------------------------------- empty cart & next steps */

/* The empty-cart lead-in row moved to modules/ostadeez-m-empty.css on 2026-09-03, with the rest of
   the empty state it belongs to. One component, one file. */

.ostz-nextsteps {
	margin: var(--ostz-space-6) 0;
	padding: 0;
	list-style: none;
	counter-reset: ostz-next;
}

.ostz-nextstep {
	display: flex;
	gap: var(--ostz-space-3);
	align-items: flex-start;
	padding: var(--ostz-space-3) 0;
	border-block-end: 1px solid var(--ostz-line, #262a35);
	color: var(--ostz-text-dim, #aab0be);
	font-size: var(--ostz-text-sm);
	counter-increment: ostz-next;
}

.ostz-nextstep::before {
	content: counter(ostz-next);
	display: grid;
	place-items: center;
	flex: 0 0 auto;
	width: 24px;
	height: 24px;
	border: 1px solid var(--ostz-line-strong, #333846);
	border-radius: 50%;
	color: var(--ostz-accent, #00e0d5);
	font-size: var(--ostz-text-xs);
	font-weight: var(--ostz-weight-bold);
}

.ostz-nextstep:last-child {
	border-block-end: 0;
}

/* ---------------------------------------------------------------- cart, checkout & my account
 *
 * ⚠ EVERY PLATFORM SURFACE THAT USED TO BE DECLARED HERE MOVED TO modules/ostadeez-b-woo.css on
 * 2026-09-03, and this block is the clearest example of why the move was needed: the money table,
 * the form row, its label, the required mark, the three notices and the whole account panel were
 * ALL declared here AND AGAIN in ostadeez-woo-baseline.css, with different values — two paddings
 * and two radii for a notice, `text-sm/medium` here against `text-xs/bold` there for a table
 * heading, a form row built from scratch in both. Nothing was wrong with either copy; the problem
 * was that «what does a notice look like on this site» had two answers and load order picked one.
 *
 * The theme's OWN account classes (`.ostz-account-*`) stay here — they are ours, not the
 * platform's, and this is the sheet that owns them.
 */

/* The dashboard greeting, replacing WooCommerce's boilerplate paragraph — see
   `woocommerce/myaccount/dashboard.php` for why the template is overridden at all. */
.ostz-account-welcome {
	margin-block-end: var(--ostz-space-6);
}

.ostz-account-hello {
	margin: 0 0 var(--ostz-space-2);
	font-size: var(--ostz-text-xl);
	font-weight: var(--ostz-weight-black);
}

.ostz-account-lead {
	margin: 0 0 var(--ostz-space-5);
	color: var(--ostz-text-dim, #aab0be);
	font-size: var(--ostz-text-sm);
}

.ostz-account-actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--ostz-space-3);
	margin: 0 0 var(--ostz-space-5);
}

.ostz-account-signout {
	margin: 0;
	color: var(--ostz-text-dim, #aab0be);
	font-size: var(--ostz-text-sm);
}

/* Every inline link the account screens own clears the 24px floor. They are all short words in a
   sentence — «خروج از حساب»، «سفارش ۲۹۷» — and an inline anchor takes its height from the line
   box, so each one measured 20–23px until it was told otherwise. */
.ostz-account-signout a,
.ostz-account-order-link {
	display: inline-flex;
	align-items: center;
	min-block-size: 24px;
}

.ostz-account-subtitle {
	margin: 0 0 var(--ostz-space-2);
	font-size: var(--ostz-text-md);
	font-weight: var(--ostz-weight-bold);
}

.ostz-account-order-meta {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--ostz-space-3);
	margin: 0 0 var(--ostz-space-6);
	font-size: var(--ostz-text-sm);
}

.ostz-account-order-link {
	color: var(--ostz-accent, #00e0d5);
	font-weight: var(--ostz-weight-bold);
	text-decoration: none;
}

.ostz-muted {
	color: var(--ostz-text-faint, #828a9c);
}

/* ---------------------------------------------------------------- fulfilment tracker */

.ostz-track {
	display: flex;
	align-items: flex-start;
	margin: 0 0 var(--ostz-space-4);
	padding: 0;
	list-style: none;
	counter-reset: none;
}

.ostz-track-step {
	position: relative;
	flex: 1;
	text-align: center;
	font-size: var(--ostz-text-xs);
	color: var(--ostz-text-dim, #aab0be);
}

/* The connector sits BEHIND the dots and starts at the second step. */
.ostz-track-step::before {
	content: '';
	position: absolute;
	top: 12px;
	inset-inline-end: 50%;
	z-index: 1;
	width: 100%;
	height: 2px;
	background: var(--ostz-line-strong, #333846);
}

.ostz-track-step:first-child::before {
	display: none;
}

.ostz-track-dot {
	position: relative;
	z-index: 2;
	display: block;
	width: 26px;
	height: 26px;
	margin: 0 auto var(--ostz-space-2);
	border: 2px solid var(--ostz-line-strong, #333846);
	border-radius: 50%;
	background: var(--ostz-bg-raised-2, #1b1e27);
}

.ostz-track-step.is-done::before,
.ostz-track-step.is-now::before {
	background: var(--ostz-accent, #00e0d5);
}

.ostz-track-step.is-done .ostz-track-dot {
	border-color: var(--ostz-accent, #00e0d5);
	background: var(--ostz-accent, #00e0d5);
}

.ostz-track-step.is-now .ostz-track-dot {
	border-color: var(--ostz-accent, #00e0d5);
	box-shadow: 0 0 0 4px color-mix(in srgb, var(--ostz-accent, #00e0d5) 14%, transparent);
}

.ostz-track-step.is-now .ostz-track-label {
	color: var(--ostz-text, #f2f4f8);
	font-weight: var(--ostz-weight-bold);
}

/* Off-track states get a plain honest line, never a progress bar. */
.ostz-track-flat {
	margin: 0 0 var(--ostz-space-4);
	padding: var(--ostz-space-3) var(--ostz-space-4);
	border-inline-start: 2px solid var(--ostz-line-strong, #333846);
	border-radius: var(--ostz-radius-sm);
	background: var(--ostz-bg-raised-2, #1b1e27);
	font-size: var(--ostz-text-sm);
}

.ostz-track-cancelled,
.ostz-track-failed {
	border-inline-start-color: var(--ostz-danger, #e2564a);
	color: var(--ostz-danger, #e2564a);
}

.ostz-track-on-hold,
.ostz-track-pending {
	border-inline-start-color: var(--ostz-amber, #ffb020);
	color: var(--ostz-amber, #ffb020);
}

.ostz-track-code {
	display: flex;
	flex-wrap: wrap;
	gap: var(--ostz-space-2);
	align-items: baseline;
	margin: 0;
	font-size: var(--ostz-text-sm);
}

.ostz-track-code-label {
	color: var(--ostz-text-dim, #aab0be);
}

.ostz-track-code-value {
	font-weight: var(--ostz-weight-bold);
	letter-spacing: .04em;
	unicode-bidi: isolate;
}

/* ---------------------------------------------------------------- order received */

.ostz .woocommerce-order .woocommerce-thankyou-order-received {
	margin: 0 0 var(--ostz-space-6);
	font-size: var(--ostz-text-xl);
	font-weight: var(--ostz-weight-bold);
}

.ostz .woocommerce-order-overview {
	display: flex;
	flex-wrap: wrap;
	gap: var(--ostz-space-6);
	margin: 0 0 var(--ostz-space-8);
	padding: var(--ostz-space-6);
	border: 1px solid var(--ostz-line, #262a35);
	border-radius: var(--ostz-radius-lg);
	background: var(--ostz-bg-raised, #15171e);
	list-style: none;
}

.ostz .woocommerce-order-overview li {
	display: flex;
	flex-direction: column;
	gap: var(--ostz-space-1);
	color: var(--ostz-text-dim, #aab0be);
	font-size: var(--ostz-text-xs);
}

.ostz .woocommerce-order-overview strong {
	color: var(--ostz-text, #eceef2);
	font-size: var(--ostz-text-md);
	unicode-bidi: isolate;
}

/* ---------------------------------------------------------------- narrow screens */

@media (max-width: 820px) {
	.ostz-pdp {
		grid-template-columns: 1fr;
		gap: var(--ostz-space-6);
	}
}

@media (max-width: 800px) {

	.ostz-buybar {
		display: flex;
		/*
		 * Raised clear of the footer nav bar, which is fixed in the same corner. On a product page
		 * the buy action outranks navigation, and two fixed bars at the same offset is how one of
		 * them becomes untappable. Moved here from ostadeez-footerbar.css on 2026-09-02 — the bar
		 * is this stylesheet's component and its offset belongs beside the rest of it.
		 */
		inset-block-end: calc(56px + env(safe-area-inset-bottom, 0px));
	}

	/* Room for the fixed bar so it never covers the end of the page. */
	.ostz.single-product .ostz-main {
		padding-block-end: calc(var(--ostz-space-16) + 72px);
	}
}

/* ================================================================ phone density
 * Merged in from ostadeez-density.css on 2026-09-02, which is now deleted. See the matching note
 * at the end of ostadeez-main.css for why a separate density sheet was the problem rather than the
 * solution. Numbers unchanged; they were measured, not guessed.
 */

@media (max-width: 820px) {

	/*
	 * ⚠ THE 19px HORIZONTAL OVERFLOW, found by hiding each subtree in turn until it disappeared:
	 * the «فیلترها» button on this row. The row is a flex line that never wrapped, so the button
	 * plus the result count plus WooCommerce's ordering select were 19px wider than the viewport
	 * and pushed the whole PAGE sideways.
	 *
	 * Two earlier attributions (the filter rail, then the chip row) were both wrong; this one was
	 * isolated by elimination rather than reasoning, which is why it is the one that holds.
	 */
	.ostz-toolbar-meta {
		display: flex;
		flex-wrap: wrap;
		align-items: center;
		gap: 8px;
		min-width: 0;
	}

	.ostz-toolbar-meta > * {
		min-width: 0;
	}

	/* The select is the only elastic item here, so it is the one allowed to give. */
	.ostz-toolbar-meta .woocommerce-ordering {
		flex: 1 1 140px;
		margin: 0;
		min-width: 0;
	}

	.ostz-toolbar-meta .woocommerce-ordering select {
		width: 100%;
		max-width: 100%;
	}

	.ostz-toolbar-meta .woocommerce-result-count {
		flex: 0 1 auto;
		margin: 0;
		font-size: var(--ostz-text-xs);
	}

	.ostz-phead {
		margin-block-end: 10px;
	}

	/* The breadcrumb is one line and small; it is a wayfinding aid, not a headline. */
	.ostz-toolbar {
		gap: 8px;
		padding-block: 8px;
	}

	/* The chip's phone behaviour travelled with the component to
	   modules/ostadeez-m-chip.css on 2026-09-03 — a breakpoint is part of a component, not a
	   separate concern, which is the same lesson that closed `ostadeez-density.css`. */
}
