/*
 * Single product page (Phase 3E — docs/PRODUCT-PAGE-IMPLEMENTATION.md).
 * Restyles WooCommerce's own native single-product markup (gallery,
 * variation form, stock text) rather than replacing it — see
 * inc/product-page.php's file docblock for why the gallery and stock
 * pipelines are deliberately left untouched. This file only paints over
 * WC's own class names with our design tokens and lays out the page shell.
 */

/* --- Page shell ---
   WooCommerce's own woocommerce-layout.css floats .images/.summary at 48%
   each. Setting the parent to `display: grid` overrides the float
   behaviour entirely (grid items ignore float) without touching or
   dequeuing WooCommerce's stylesheet. */

/* Phase 3K.1: WooCommerce's *other* core stylesheet (woocommerce.css, not
   woocommerce-layout.css) separately sets `.images{margin-bottom:2em}` and
   `.summary{margin-bottom:2em}` — spacing that mattered when these two
   blocks used to stack vertically under the old float layout, so a margin
   was needed to separate whichever block rendered on top from whatever
   came after it. Once the theme's own two-column CSS Grid governs this
   layout, that margin is vestigial (both items sit side-by-side, and the
   grid's own `gap` already provides row spacing) — but it was never
   neutralized, so it silently added an extra 32px on top of the grid gap
   and the tabs section's own margin-top, tripling the intended space
   between the gallery/summary row and the Overview/Specifications
   accordion below it (measured live: 96px instead of the intended 64px =
   48px grid gap + 16px tabs margin-top, both pre-existing values, nothing
   new introduced here). Confirmed via computed-style inspection this
   margin comes from WooCommerce core, not the theme or Phase 3K. */
.woocommerce div.product > .images,
.woocommerce div.product > .summary.entry-summary {
	/* !important: WooCommerce's own real selector here is
	   `.woocommerce div.product div.images` (and the `.summary` equivalent)
	   — a descendant combinator with an extra `div` type selector that
	   this rule's specificity comparison did not out-rank in practice
	   (verified live via the CSSOM: WooCommerce's rule was still winning
	   without `!important`, despite this rule appearing higher-specificity
	   on paper). `!important` is the same documented, narrow exception
	   already used elsewhere in this file for exactly this situation —
	   overriding WooCommerce core CSS that can't be dequeued. */
	margin-bottom: 0 !important;
}

.woocommerce div.product {
	max-width: var(--container-max);
	margin-inline: auto;
	padding-inline: var(--container-pad-mobile);
	padding-block: var(--space-6) var(--space-12);
	display: grid;
	/* minmax(0, 1fr), not a bare 1fr: a bare 1fr track's minimum is "auto",
	   which lets any one grid item's min-content size (WooCommerce's
	   FlexSlider gallery, in practice) force the whole track — and every
	   other item sharing it — wider than the viewport. Classic CSS Grid
	   blowout; confirmed by finding every grid item reporting the exact
	   same oversized computed track width regardless of its own content,
	   which only makes sense if one shared track was inflated. The two-
	   column rule below already used minmax(0, …) for this reason; the
	   single-column base rule had been missed. */
	grid-template-columns: minmax(0, 1fr);
	gap: var(--space-8);
}

@media (min-width: 768px) {
	.woocommerce div.product { padding-inline: var(--container-pad-tablet); }
}

@media (min-width: 1024px) {
	.woocommerce div.product {
		grid-template-columns: minmax(0, 5fr) minmax(0, 4fr);
		gap: var(--space-12);
		padding-inline: var(--container-pad-desktop);
	}
	.woocommerce div.product > .images,
	.woocommerce div.product > .woocommerce-product-gallery {
		grid-column: 1;
		grid-row: 1 / span 2;
	}
	.woocommerce div.product > .summary.entry-summary {
		grid-column: 2;
		/* Spans both rows, same as the gallery, rather than just row 1, so
		   a thin-content product (no description/SKU/stock line) doesn't
		   leave a bare rectangle in row 2 beside the lower half of the
		   image. Phase 3K.1 fix: the row previously also set
		   `justify-content: center`, on the stated assumption that this had
		   "no visible effect when the summary's own content is already as
		   tall as the gallery." That assumption was wrong — measured live
		   on the real "Demo 8GB DDR4 RAM Module" product (a normal-length
		   summary, not the sparse test product): gallery height 653px,
		   summary content only ~350-400px, so centering pushed the product
		   title ~94px below the gallery's top edge. Every real ecommerce
		   product page top-aligns the title with the image — centering it
		   instead is what actually looked broken, on every normal product,
		   not just the sparse one. `justify-content` now defaults to
		   flex-start (top-aligned): a sparse product's extra space now
		   correctly falls at the BOTTOM of the column (the standard,
		   expected pattern), and every normal-content product's title
		   lines up with the gallery top. See
		   docs/SINGLE-PRODUCT-LAYOUT-QA.md. */
		grid-row: 1 / span 2;
	}
}

/* Everything after the gallery/summary row (tabs, related products) spans
   the full width, in normal document flow below the grid's two rows. */
.woocommerce div.product > .woocommerce-tabs,
.woocommerce div.product > .bholats-product-tabs,
.woocommerce div.product > section.related {
	grid-column: 1 / -1;
}

/* --- Gallery (native WooCommerce markup, restyled only) --- */

/* WooCommerce's own FlexSlider-based gallery script (frontend/single-product.js
   via jquery.flexslider) sets inline pixel width/height on .flex-viewport
   and its slide track for its carousel mechanism — confirmed via computed
   style inspection to occasionally resolve larger than the actual viewport
   (specific to how the slider measures on first load), pushing the whole
   grid container wider than the page and causing real horizontal overflow
   at every width below 1024px. `!important` here is deliberate: it's the
   only way a stylesheet rule can override an inline style WooCommerce's
   own script sets at runtime, and only `max-width`/`width` are forced —
   the slider's own scroll/transform behaviour is untouched. */
.woocommerce-product-gallery,
.woocommerce-product-gallery .flex-viewport,
.woocommerce-product-gallery ul.slides {
	max-width: 100% !important;
	width: 100% !important;
}

.woocommerce-product-gallery__wrapper {
	border-radius: var(--radius-lg);
	overflow: hidden;
	background: var(--color-surface);
	border: var(--border-default);
}

.woocommerce-product-gallery__image img,
.woocommerce-product-gallery__image--placeholder img {
	width: 100%;
	height: auto;
	aspect-ratio: 1 / 1;
	object-fit: contain;
	background: var(--color-surface);
}

.flex-control-nav {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2);
	margin-top: var(--space-3);
	padding: 0;
	list-style: none;
}

/*
 * Phase 3N.1 — thumbnail clipping defect, root-caused via live DOM/computed-style
 * inspection then confirmed by reading WooCommerce core's own source
 * (wp-content/plugins/woocommerce/assets/css/woocommerce.css):
 *
 *   .woocommerce div.product div.images .flex-control-thumbs li { width: 25%; ... }
 *   .woocommerce div.product div.images img { width: 100%; height: auto; ... }
 *
 * Both rules carry far higher specificity than the plain `.flex-control-nav li` /
 * `.flex-control-nav li img` rules this file used to have (0,4,3 and 0,3,3
 * vs. 0,1,1 and 0,1,2) — WooCommerce won on the <li>'s WIDTH (25% of the
 * gallery, not this file's intended 72px) and on the <img>'s HEIGHT (auto,
 * not this file's intended 100%), while this file's own `height: 72px` on
 * the <li> was left uncontested (WooCommerce never sets a height there).
 * The result: a ~163px-wide <li> (25% of a ~653px gallery) whose fixed
 * height stayed at 72px, containing an <img> at width:100% + height:auto
 * that computed to ~163px tall (its own 1:1 natural aspect ratio, applied
 * via the gallery template's own `onload="this.width=this.naturalWidth;
 * this.height=this.naturalHeight"` attribute sync) — a box and its content
 * disagreeing on height by more than double. `.flex-control-thumbs`'s own
 * `overflow: hidden` (also WooCommerce core) then clipped everything past
 * the 72px box.
 *
 * Fixed two ways, together:
 *  1. This selector chain now matches WooCommerce's own exactly, so it
 *     wins on every property by source order (single-product.css loads
 *     after woocommerce.css) rather than losing a partial specificity
 *     fight on some properties and not others.
 *  2. The <li>/<img> height is never a separate fixed value again —
 *     `aspect-ratio: 1 / 1` on both (matching the main gallery image's own
 *     existing treatment above) means the box and its image content can
 *     never disagree, regardless of which width value ends up applying,
 *     how many thumbnails exist, or the source image's real proportions.
 *     `object-fit: contain` (never `cover`) preserves this project's
 *     standing "never hard-crop a real product photo" rule — a portrait
 *     or landscape source image gets letterboxed inside the square frame,
 *     the same way the main image already handles it, never cropped.
 */
.woocommerce div.product div.images .flex-control-thumbs li {
	width: 72px;
	aspect-ratio: 1 / 1;
	height: auto;
}

.woocommerce div.product div.images .flex-control-thumbs li img {
	display: block;
	width: 100%;
	height: 100%;
	aspect-ratio: 1 / 1;
	object-fit: contain;
	border-radius: var(--radius-md);
	border: var(--border-width-md) solid transparent;
	background: var(--color-surface);
	cursor: pointer;
	transition: border-color var(--duration-base) var(--ease-standard);
}

.woocommerce div.product div.images .flex-control-thumbs li img.flex-active,
.woocommerce div.product div.images .flex-control-thumbs li img:hover,
.woocommerce div.product div.images .flex-control-thumbs li img:focus-visible {
	border-color: var(--color-brand-blue);
}

/* Sale flash — WooCommerce's own .onsale span, restyled to match the
   product-card sale badge instead of WC's default red circle. */
.woocommerce span.onsale {
	position: absolute;
	z-index: var(--z-raised);
	top: var(--space-3);
	left: var(--space-3);
	min-height: 0;
	min-width: 0;
	padding: var(--space-1) var(--space-2);
	border-radius: var(--radius-sm);
	background: color-mix(in srgb, var(--color-sale) 12%, white);
	color: var(--color-sale);
	font-size: var(--text-xs);
	font-weight: var(--font-weight-semibold);
	line-height: 1.2;
	text-transform: none;
}

/* --- Summary column --- */

/* WooCommerce core's own woocommerce-layout.css sets `div.summary { float:
   right; width: 48%; }`. The grid rule above (line ~45) neutralises the
   float — grid items ignore float entirely — but that same core rule's
   `width: 48%` is a separate declaration and grid items do NOT ignore an
   explicit width; they only stretch to fill their track when the width is
   'auto'. Confirmed via computed-style inspection: with the core rule
   active, .summary rendered at exactly 48% of its own grid track (e.g.
   250.9px of a 522.7px track), not 48% of the page — starving the entire
   summary column (text measure, variation table, quantity/button row) to
   roughly half its intended width on every single-product page and, on
   variable products, squeezing the attribute <select> down to a few dozen
   px so its "Choose an option" text and options were visibly cut off.
   `width: 100%` here has equal-or-higher specificity than the core rule
   (four classes vs. three) and restores the intended full-track width. */
.woocommerce div.product .summary.entry-summary { display: flex; flex-direction: column; gap: var(--space-3); width: 100%; }

.bholats-product-summary__brand {
	font-size: var(--text-sm);
	font-weight: var(--font-weight-semibold);
	/* --color-brand-blue is 4.45:1 on the page's sunken background — just
	   under WCAG AA's 4.5:1 for normal text. --color-brand-deep (12.8:1)
	   keeps the same brand-blue family without the marginal fail. Found via
	   axe-core. */
	color: var(--color-brand-deep);
	text-decoration: none;
	text-transform: uppercase;
	letter-spacing: var(--tracking-wide);
}
.bholats-product-summary__brand:hover { text-decoration: underline; }

.woocommerce div.product .product_title {
	font-family: var(--font-display);
	font-size: var(--text-display-lg);
	font-weight: var(--font-weight-bold);
	letter-spacing: var(--tracking-tight);
	line-height: var(--leading-tight);
	color: var(--color-text-primary);
	margin: 0;
}

/* Phase C: brand + title read as one unit, title dominant. The summary
   column's flex gap (--space-3, 12px) is uniform between every top-level
   child, which is right for title→identity-row and panel→meta but reads
   as too loose between a small uppercase brand label and the big title
   directly under it — the two looked like unrelated lines rather than
   "brand, then the product it makes." Pulling the title up to a --space-1
   (4px) gap only where a brand link actually precedes it (adjacent-sibling
   selector — brand is priority 4, title is WooCommerce core's own priority
   5, always immediately adjacent in source) tightens that one relationship
   without touching the identity-row/purchase-panel spacing below, and
   without affecting products that have no brand link at all. */
.bholats-product-summary__brand + .product_title {
	margin-top: calc(var(--space-1) - var(--space-3));
}

.bholats-product-summary__identity-row {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--space-3);
}

.woocommerce div.product .woocommerce-product-rating {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	margin: 0;
}

.woocommerce div.product .star-rating { color: var(--color-warning); }

/* Zero-review prompt (Phase C) — same restrained weight as the SKU it sits
   beside (--text-xs, secondary colour), so "Be the first to write a
   review" reads as a quiet piece of identity-row information, not a call-
   to-action competing with Add to Cart. Underline is muted at rest (using
   the border colour, not full text colour) and only strengthens on
   hover/focus, matching how .bholats-product-summary__brand already
   signals "this is a link" without shouting it. */
.bholats-product-summary__review-prompt {
	font-size: var(--text-xs);
	color: var(--color-text-secondary);
	text-decoration: underline;
	text-decoration-color: var(--color-border);
	text-underline-offset: 2px;
}
.bholats-product-summary__review-prompt:hover,
.bholats-product-summary__review-prompt:focus-visible {
	color: var(--color-brand-deep);
	text-decoration-color: currentColor;
}
.bholats-product-summary__review-prompt:focus-visible {
	outline: none;
	box-shadow: var(--shadow-focus);
	border-radius: var(--radius-sm);
}

.bholats-product-summary__highlights { margin-top: var(--space-1); }

/* --- Share control ---
   Rendered via woocommerce_share (WooCommerce's own, previously-empty
   extension point), so it lands after the add-to-cart form and product
   meta in source order — a single small trigger, not a row of social
   icons, per the brief's "don't clutter the page" requirement. */

.bholats-product-share {
	position: relative;
	display: inline-block;
	margin-top: var(--space-4);
}

.bholats-product-share__trigger svg { width: var(--icon-sm); height: var(--icon-sm); }

.bholats-product-share__panel { min-width: 180px; }

.bholats-product-share__panel .bholats-dropdown__item {
	display: flex;
	align-items: center;
	gap: var(--space-2);
}

.bholats-product-share__panel svg { width: var(--icon-sm); height: var(--icon-sm); flex-shrink: 0; }

/* --- Purchase panel (Phase A, redesign audit) ---
   Pure wrapper styling around content that already renders here in the
   exact same order (inc/product-page.php) — price, short excerpt,
   highlight chips, WooCommerce's own native stock/variations/quantity/
   add-to-cart form (untouched), and the existing delivery line. A white
   card against the page's sunken (--color-surface-sunken) background so
   it reads as a genuinely distinct, elevated "buy zone" rather than more
   of the same page flow — restrained radius/shadow, not a dashboard tile. */
.bholats-purchase-panel {
	background: var(--color-surface);
	border: var(--border-default);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-sm);
	padding: var(--space-6);
	margin-top: var(--space-4);
}

/* One internal divider, not several: splits "what is this" (price,
   description, highlight chips) from "is it available and how do I buy
   it" (native stock/variations/quantity/cart/delivery) — the single
   hierarchy break that matters for scanning speed, not a divider between
   every element. border-top + padding-top (not a stacked margin) so it
   doesn't fight the native elements' own existing margin rules. */
.bholats-purchase-panel > .stock {
	margin-top: var(--space-2);
	padding-top: var(--space-5);
	border-top: var(--border-default);
}

/* Price — bholats_price() output (single-product/price.php override) */
.woocommerce div.product .bholats-price { margin: var(--space-2) 0; }

/* Stock text — native wc_get_stock_html() markup, both the initial
   simple-product render and every variation swap use these same classes,
   so a dot works everywhere without touching the generation logic. */
.woocommerce div.product p.stock {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2);
	font-size: var(--text-sm);
	font-weight: var(--font-weight-medium);
	margin: 0;
}
.woocommerce div.product p.stock::before {
	content: "";
	width: 8px;
	height: 8px;
	border-radius: var(--radius-full);
	flex-shrink: 0;
}
.woocommerce div.product p.stock.in-stock { color: var(--color-success); }
.woocommerce div.product p.stock.in-stock::before { background: var(--color-success); }
.woocommerce div.product p.stock.out-of-stock { color: var(--color-text-secondary); }
.woocommerce div.product p.stock.out-of-stock::before { background: var(--color-text-secondary); }
.woocommerce div.product p.stock.available-on-backorder { color: var(--color-warning); }
.woocommerce div.product p.stock.available-on-backorder::before { background: var(--color-warning); }

/* --- Variations table --- */

.woocommerce div.product table.variations {
	width: 100%;
	margin: var(--space-2) 0;
	border-collapse: collapse;
}
/* Phase 3K.2 bugfix: this table had no responsive rule at any width, so its
 * default auto table layout always gave th.label (white-space: nowrap)
 * first claim on the row's width and td.value's select (width:100%;
 * max-width:320px below) whatever was left over — confirmed live at 390px:
 * "Storage Capacity" alone left only ~192px for the select, visibly
 * truncating a real-length attribute value ("1TB NVMe PCIe Gen4 SSD
 * (Read-Intensive)") with no wrap or ellipsis, since a native <select>'s
 * closed-state text cannot wrap (browser-level behaviour — not something
 * CSS can fix directly; only giving the select its rightful share of the
 * row's width can). Stacking label-above-value below 768px (this file's
 * existing mobile breakpoint) gives the select the full row width
 * regardless of label length; the side-by-side table layout returns
 * unchanged at tablet width and up.
 */
.woocommerce div.product table.variations,
.woocommerce div.product table.variations tbody,
.woocommerce div.product table.variations tr {
	display: block;
	width: 100%;
}
.woocommerce div.product table.variations tr + tr { margin-top: var(--space-3); }
.woocommerce div.product table.variations th.label,
.woocommerce div.product table.variations td.value {
	display: block;
	width: 100%;
}
.woocommerce div.product table.variations th.label {
	text-align: left;
	font-size: var(--text-sm);
	font-weight: var(--font-weight-semibold);
	color: var(--color-text-primary);
	padding-bottom: var(--space-1);
}
@media (min-width: 768px) {
	.woocommerce div.product table.variations,
	.woocommerce div.product table.variations tbody { display: table; }
	.woocommerce div.product table.variations tbody { display: table-row-group; }
	.woocommerce div.product table.variations tr {
		display: table-row;
		margin-top: 0;
	}
	.woocommerce div.product table.variations tr + tr td,
	.woocommerce div.product table.variations tr + tr th { padding-top: var(--space-3); }
	.woocommerce div.product table.variations th.label,
	.woocommerce div.product table.variations td.value {
		display: table-cell;
		width: auto;
	}
	.woocommerce div.product table.variations th.label {
		padding-right: var(--space-4);
		padding-bottom: 0;
		white-space: nowrap;
	}
}
.woocommerce div.product table.variations td.value select {
	width: 100%;
	max-width: 320px;
	padding: var(--space-2) var(--space-3);
	border: var(--border-default);
	border-radius: var(--radius-md);
	background: var(--color-surface);
	font-family: var(--font-body);
	font-size: var(--text-sm);
	color: var(--color-text-primary);
	min-height: 44px;
}
.woocommerce div.product table.variations td.value select:focus-visible {
	outline: none;
	box-shadow: var(--shadow-focus);
	border-color: var(--color-brand-blue);
}
.woocommerce div.product a.reset_variations {
	display: inline-block;
	margin-top: var(--space-2);
	font-size: var(--text-xs);
	color: var(--color-text-secondary);
}

.woocommerce div.product .woocommerce-variation-description p { color: var(--color-text-secondary); font-size: var(--text-sm); }
.woocommerce div.product .woocommerce-variation-price { margin: var(--space-2) 0; }
.woocommerce div.product .woocommerce-variation-availability { margin-bottom: var(--space-2); }

/* A non-sale variation's price_html is WooCommerce's own untouched
   <span class="price">…</span> (the woocommerce_available_variation filter
   in inc/product-page.php only restyles the on-sale case) — pinning the
   colour here (a WC core stylesheet sets .price/.amount to its own
   --wc-highlight olive/gold tone, confirmed via computed-style inspection,
   not a colour this project ever chose) keeps every price on the page
   using the same design-system text colour regardless of which template
   generated the markup. */
.woocommerce div.product .woocommerce-variation-price .price,
.woocommerce div.product .woocommerce-variation-price .woocommerce-Price-amount {
	color: var(--color-text-primary);
	font-size: var(--text-price-lg);
	font-weight: var(--font-weight-bold);
}

/* --- Quantity + Add to Cart row --- */

.woocommerce div.product form.cart,
.woocommerce div.product .woocommerce-variation-add-to-cart {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: var(--space-3);
	margin-top: var(--space-2);
}

.bholats-qty {
	display: inline-flex;
	align-items: stretch;
}

/* Delivery timeframe (inc/product-page.php) — plain text only, no icon/
   badge/border per the placement audit: --text-sm + --color-text-secondary
   matches how this page already treats secondary-but-readable copy (e.g.
   the short description above), so it reads as clearly present but
   subordinate to price and Add to Cart, not another promotional element. */
.bholats-product-summary__delivery {
	margin: var(--space-3) 0 0;
	font-size: var(--text-sm);
	color: var(--color-text-secondary);
}

/* Shipping cost / free-delivery threshold (Phase B) — same size/colour as
   the delivery line directly above so the two read as one small "delivery
   &amp; shipping" cluster, not two separate announcements. Tight
   --space-1 gap from the delivery line (not another --space-3) keeps them
   visually grouped; each line is its own block so the phrase never wraps
   mid-sentence on narrow widths (confirmed live: a single "·"-joined line
   wrapped awkwardly at 390px before this). No icon, no badge, no bold —
   secondary to Add to Cart, same restrained treatment as the line above. */
.bholats-product-summary__shipping {
	margin: var(--space-1) 0 0;
	font-size: var(--text-sm);
	color: var(--color-text-secondary);
}
.bholats-product-summary__shipping span { display: block; }

.woocommerce div.product .single_add_to_cart_button {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	flex: 1 1 auto;
	min-width: 180px;
	min-height: 44px;
	padding: var(--space-3) var(--space-6);
	border-radius: var(--radius-md);
	border: none;
	background: var(--color-brand-blue);
	color: var(--color-text-on-brand);
	font-family: var(--font-body);
	font-size: var(--text-sm);
	font-weight: var(--font-weight-semibold);
	cursor: pointer;
	transition: transform var(--duration-fast) var(--ease-out),
		background-color var(--duration-base) var(--ease-standard);
}
.woocommerce div.product .single_add_to_cart_button:hover { background: var(--color-brand-bright); transform: translateY(-1px); }
.woocommerce div.product .single_add_to_cart_button:active { background: var(--color-brand-deep); transform: translateY(0); }
.woocommerce div.product .single_add_to_cart_button:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.woocommerce div.product .single_add_to_cart_button.disabled,
.woocommerce div.product .single_add_to_cart_button:disabled {
	background: var(--color-surface-muted);
	/* --color-text-muted is 3.06:1 on --color-surface-muted — fails WCAG AA.
	   --color-text-secondary (5.97:1) is the same fix commerce.css already
	   applies to the out-of-stock label. Found via axe-core. */
	color: var(--color-text-secondary);
	cursor: not-allowed;
	transform: none;
	/* WooCommerce's own bundled CSS applies opacity:.5 to disabled buttons,
	   which dims the text colour above back down to a failing ~2.1:1 by the
	   time it's composited against the page background — found via
	   axe-core reporting a rendered colour this file never set. Cancelling
	   the opacity keeps the deliberately-chosen, already-passing colours. */
	opacity: 1;
}
.woocommerce div.product .single_add_to_cart_button.loading { opacity: 0.7; }

/* --- SKU / meta --- */
.woocommerce div.product .product_meta {
	margin-top: var(--space-4);
	padding-top: var(--space-4);
	border-top: var(--border-default);
	font-size: var(--text-xs);
	color: var(--color-text-secondary);
	display: flex;
	flex-direction: column;
	gap: var(--space-1);
}
.woocommerce div.product .product_meta a { color: var(--color-text-secondary); }

/* --- Notices (real WooCommerce notices, styled via feedback.css's
   .woocommerce-message/.woocommerce-error mapping — just spacing here) --- */
.woocommerce div.product .woocommerce-notices-wrapper:empty { display: none; }

/* --- Tabs (rendered as an accordion — see
   woocommerce/single-product/tabs/tabs.php) --- */

.bholats-product-tabs {
	max-width: 100%;
	margin-top: var(--space-4);
	border-top: var(--border-default);
}

.bholats-product-tabs .bholats-accordion__panel-inner { max-width: var(--reading-width); }
.bholats-product-tabs .bholats-product-specs,
.bholats-product-tabs .bholats-policy-panel { max-width: none; }

/* --- Specifications --- */

.bholats-product-specs {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--space-6);
}
@media (min-width: 768px) {
	.bholats-product-specs { grid-template-columns: repeat(2, 1fr); gap: var(--space-8); }
}
.bholats-product-specs__group-title {
	font-size: var(--text-h5);
	font-weight: var(--font-weight-semibold);
	color: var(--color-text-primary);
	margin: 0 0 var(--space-2);
}

/* --- Warranty / delivery / returns --- */

.bholats-policy-panel {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--space-4);
}
@media (min-width: 768px) {
	.bholats-policy-panel { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
}
.bholats-policy-panel__title { margin: 0 0 var(--space-2); font-size: var(--text-h5); }
.bholats-policy-panel__body { margin: 0 0 var(--space-2); font-size: var(--text-sm); color: var(--color-text-secondary); }
.bholats-policy-panel__link { font-size: var(--text-sm); font-weight: var(--font-weight-semibold); color: var(--color-brand-blue); }

/* --- What's in the box --- */
.bholats-product-whats-in-box { max-width: var(--reading-width); line-height: var(--leading-relaxed); }

/* --- Reviews ---
   WooCommerce hooks the core `comments_template` filter
   (WC_Template_Loader::comments_template_loader()) to force-load its own
   single-product-reviews.php for any product, regardless of the theme's
   comments.php — confirmed by testing (a themed comments.php empty-state
   never appeared in the rendered output; WC's own #reviews/#comments/
   .commentlist/#respond markup did). These are WooCommerce's real class
   names, not this theme's invention; comments.php remains only for
   WordPress's own generic Page/Post comment scenarios, unrelated to
   product reviews (docs/PRODUCT-PAGE-QA.md §2). */
#reviews.woocommerce-Reviews { max-width: var(--reading-width); }
.woocommerce-Reviews-title { font-family: var(--font-display); font-size: var(--text-h3); margin: 0 0 var(--space-4); }
.woocommerce-noreviews,
.woocommerce-verification-required { color: var(--color-text-secondary); font-size: var(--text-sm); margin-bottom: var(--space-4); }
#reviews .commentlist { list-style: none; margin: 0 0 var(--space-6); padding: 0; }
#reviews .commentlist li { padding: var(--space-4) 0; border-bottom: var(--border-default); }
#reviews .comment-form p { margin: 0 0 var(--space-3); }
#reviews .comment-form label { display: block; font-size: var(--text-sm); font-weight: var(--font-weight-medium); margin-bottom: var(--space-1); }
#reviews .comment-form input[type="text"],
#reviews .comment-form input[type="email"],
#reviews .comment-form select,
#reviews .comment-form textarea {
	width: 100%;
	padding: var(--space-2) var(--space-3);
	border: var(--border-default);
	border-radius: var(--radius-md);
	font-family: var(--font-body);
	font-size: var(--text-sm);
}
#reviews .comment-form .form-submit input {
	display: inline-flex;
	align-items: center;
	min-height: 44px;
	padding: var(--space-3) var(--space-6);
	border: none;
	border-radius: var(--radius-md);
	background: var(--color-brand-blue);
	color: var(--color-text-on-brand);
	font-weight: var(--font-weight-semibold);
	cursor: pointer;
}
#reviews .comment-form .form-submit input:hover { background: var(--color-brand-bright); }
#reviews #reply-title { font-size: var(--text-h4); display: block; margin-bottom: var(--space-3); }

/* --- Related products --- */
.bholats-product-section { margin-top: var(--space-12); }
.bholats-product-section__title {
	font-family: var(--font-display);
	font-size: var(--text-h2);
	margin: 0 0 var(--space-4);
}

/* --- Sparse-related-set fix (same pattern/reasoning as the homepage's
   product rails and the Shop archive grid — components/homepage.css and
   components/archive.css): wc_get_related_products() commonly returns only
   1-2 items (confirmed on both demo products used for this review, whose
   only real sibling is each other), and .bholats-product-grid's desktop
   repeat(4-5, 1fr) columns (components/product-card.css) stretched that
   single card to occupy one column while the other 3-4 sat empty — a
   half-empty grid row directly under a "Related products" heading reads as
   broken, not restrained. Overridden only at the 1024px/1440px breakpoints
   where the multi-column layout starts, so mobile/tablet (2/3 columns,
   where 1-2 items don't leave an awkward gap) and every other page's
   .bholats-product-grid (Shop archive, homepage rails — different files,
   already carrying their own copy of this same fix) are unaffected. */
@media (min-width: 1024px) {
	.bholats-product-section .bholats-product-grid {
		grid-template-columns: repeat(auto-fit, minmax(220px, 260px));
		justify-content: flex-start;
	}
}

/* --- Sticky mobile purchase bar (Step 21) ---
   Hidden by default and at ≥1024px; revealed only once the real purchase
   area has scrolled out of view (assets/js/product-purchase-bar.js). */

.bholats-mobile-purchase-bar {
	position: fixed;
	inset-block-end: 0;
	inset-inline: 0;
	z-index: var(--z-sticky);
	display: none;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-4);
	padding: var(--space-3) var(--container-pad-mobile);
	padding-block-end: calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
	background: var(--color-surface);
	border-top: var(--border-default);
	box-shadow: var(--shadow-lg);
	transform: translateY(100%);
	transition: transform var(--duration-base) var(--ease-standard);
}

.bholats-mobile-purchase-bar--visible { display: flex; transform: translateY(0); }

.bholats-mobile-purchase-bar__price { min-width: 0; flex: 1 1 auto; }
.bholats-mobile-purchase-bar__price .bholats-price { margin: 0; }
.bholats-mobile-purchase-bar__action { flex: 0 0 auto; }

@media (min-width: 1024px) {
	.bholats-mobile-purchase-bar { display: none !important; }
}

@media (prefers-reduced-motion: reduce) {
	.bholats-mobile-purchase-bar { transition: none; }
}

/* Bottom clearance for the fixed bar — bug fix. The bar is position:fixed
   and consumes no document-flow height, so the page's true scroll-bottom
   left the footer's legal-links/copyright row sitting directly under it:
   Privacy Policy and the copyright line were unreachable, with no amount
   of scrolling able to clear them (confirmed live: at max scroll,
   `scrollY + innerHeight === scrollHeight` yet the footer's bottom row
   still measured inside the bar's own rect).
   Reserved on `body` itself, deliberately NOT on .bholats-main (the
   <main> landmark that comes BEFORE the footer in document order) and
   NOT inside footer.css. The two are not interchangeable: padding on an
   element before the footer pushes the footer down by the same amount it
   grows the document, so the footer's resting position at max-scroll is
   completely unchanged — confirmed by trying that first, measuring live,
   and finding the footer's viewport-relative position identical before
   and after. Padding on `body` sits after all of body's own children in
   the box model, so it doesn't move the footer at all — it only extends
   how far past the footer's real bottom edge the document can scroll,
   which is the actual fix. `body.single-product` means no other template
   gains any padding; `:has(...--visible)` — the exact same visibility
   class whatsapp-button.css keys its own elevated offset off — means the
   reserved space appears and disappears in lockstep with the bar itself
   (an out-of-stock product, which never renders the bar element in the
   DOM at all, is correctly unaffected). The <1024px guard matches the
   bar's own and WhatsApp's own breakpoint, so a --visible class left
   over from a resize can never add this padding once the bar is
   force-hidden at desktop.
   69px is the bar's own real rendered height — measured live (Puppeteer,
   real product page, 430/390/360, identical at all three since its
   content doesn't reflow by width) with env(safe-area-inset-bottom) at 0.
   The env() call here is added on top of that measurement, separately —
   not a duplicate — so on a real device with a non-zero inset (where the
   bar itself grows taller by that same amount, via its own
   padding-block-end rule above) the reserved space grows by exactly the
   same amount and stays in sync. var(--space-3) is the small intentional
   gap above the bar the fix calls for, matching the gap already used
   between the delivery/shipping lines elsewhere in this file. */
@media (max-width: 1023px) {
	body.single-product:has(.bholats-mobile-purchase-bar--visible) {
		padding-bottom: calc(69px + var(--space-3) + env(safe-area-inset-bottom, 0px));
	}
}
