/*
Theme Name: Way More BD — ISDB Custom
Theme URI: https://waymorebd.com/
Author: Khondoker Moin Hossain
Author URI: https://waymorebd.com/
Description: 100% custom-coded WooCommerce theme for Way More BD ("Your Trusted Kitchen Companion"). Raw PHP + Tailwind CSS + Alpine.js. No page builders.
Version: 1.0.4
Requires at least: 6.0
Tested up to: 6.8
Requires PHP: 7.4
WC requires at least: 8.0
WC tested up to: 10.6
Text Domain: isdb-custom
Domain Path: /languages
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
*/

/*
 * NOTE: Real styling is handled by Tailwind (see functions.php).
 * This file only carries the WordPress theme header above — it is what
 * registers the theme with WP. Keep the header block intact.
 *
 * A few utility fallbacks below cover things Tailwind can't (Alpine x-cloak,
 * and hiding WC's default cart-empty flash). Everything else = Tailwind classes.
 */

[x-cloak] { display: none !important; }

/* Hide the theme until Alpine + Tailwind have painted, prevents FOUC */
.wmb-no-fouc { visibility: hidden; }
.wmb-ready .wmb-no-fouc { visibility: visible; }

/*
 * Floating cart pill (.isdb-floating-cart): fixed to the right-centre of the
 * viewport. On the Cart & Checkout pages it overlaps the cart-item quantity
 * selector and "Remove" link (worst on mobile ~360–414px). It's redundant on
 * those pages — the user is already looking at their cart — so hide it there.
 */
body.woocommerce-cart .isdb-floating-cart,
body.woocommerce-checkout .isdb-floating-cart {
	display: none !important;
}

/*
 * Accessibility: this theme strips ALL default WooCommerce CSS
 * (functions.php: woocommerce_enqueue_styles -> __return_empty_array), which
 * also removed WC's `.screen-reader-text` visually-hidden rule. Without it,
 * WooCommerce's own labels (e.g. the "Quantity of {product}" <label> emitted by
 * quantity-input.php) render as VISIBLE text. On the cart the `.quantity`
 * wrapper is display:contents, so that label was flowing into the qty stepper
 * and clipping the number input. Restore the standard WP screen-reader-text.
 */
.screen-reader-text {
	border: 0;
	clip: rect(1px, 1px, 1px, 1px);
	clip-path: inset(50%);
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute !important;
	width: 1px;
	word-wrap: normal !important;
}

/*
 * Cart quantity stepper — guarantee the number input is visible and centred
 * regardless of WC core markup changes or a purged utility. Scoped to the cart
 * form so it can't affect other quantity inputs.
 */
.woocommerce-cart-form .wmb-qty .quantity { display: contents; }
.woocommerce-cart-form .wmb-qty input.qty {
	width: 2.75rem;
	height: 2.25rem;
	padding: 0;
	border: 0;
	background: transparent;
	text-align: center;
	font-weight: 700;
	font-size: 0.875rem;
	color: #222831; /* brand-title, explicit — no CSS-var dependency */
	-moz-appearance: textfield;
	appearance: textfield;
	opacity: 1;
	visibility: visible;
}
.woocommerce-cart-form .wmb-qty input.qty::-webkit-outer-spin-button,
.woocommerce-cart-form .wmb-qty input.qty::-webkit-inner-spin-button {
	-webkit-appearance: none;
	margin: 0;
}

/*
 * Cross-sell "You may also like" carousel (cart page).
 * Tailwind is pre-compiled and node_modules isn't installed here, so the
 * horizontal scroll-snap track lives in real CSS (loaded after app.css) rather
 * than as new arbitrary-variant utilities that would need a rebuild. Replaces
 * the old 2/3-column grid that left a lone card dangling on its own row.
 */
.wmb-carousel ul.products {
	display: flex;
	gap: 1rem;
	list-style: none;
	margin: 0;
	padding: 0.25rem 0 0.75rem;   /* breathing room for card hover-shadow + scrollbar */
	overflow-x: auto;
	overflow-y: hidden;
	scroll-snap-type: x mandatory;
	-webkit-overflow-scrolling: touch;
	scrollbar-width: thin;
	scrollbar-color: #cbd5e1 transparent;
}
.wmb-carousel ul.products > li.product {
	flex: 0 0 auto;
	width: 46%;                   /* mobile: ~2 cards in view, 3rd peeking */
	margin: 0;
	scroll-snap-align: start;
}
@media (min-width: 480px) {
	.wmb-carousel ul.products > li.product { width: 33%; }
}
@media (min-width: 640px) {
	.wmb-carousel ul.products > li.product { width: 15rem; }
}
.wmb-carousel ul.products::-webkit-scrollbar {
	height: 6px;
}
.wmb-carousel ul.products::-webkit-scrollbar-track {
	background: transparent;
}
.wmb-carousel ul.products::-webkit-scrollbar-thumb {
	background: #cbd5e1;          /* slate-300 */
	border-radius: 9999px;
}

/*
 * Cart line item — responsive layout.
 * Desktop (>=640px): a single row  [thumb] [name grows] [qty] [price] [remove]
 * so there is no sparse empty band. Mobile: wraps to two rows —
 * [name | price] then [qty | remove]. Layout lives here (not as Tailwind
 * arbitrary variants) so it works without a Tailwind rebuild.
 */
.wmb-line {
	display: flex;
	gap: 1rem;
	padding: 1rem;
}
.wmb-line__body {
	min-width: 0;
	flex: 1;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	column-gap: 0.75rem;
	row-gap: 0.85rem;
}
.wmb-line__info {
	order: 1;
	flex: 1 1 55%;
	min-width: 0;
}
.wmb-line__price {
	order: 2;
	margin-left: auto;
	display: flex;
	flex-direction: column;
	align-items: flex-end;
	gap: 0.25rem;
	text-align: right;
}
.wmb-line__qty {
	order: 3;
}
.wmb-line__remove {
	order: 4;
	margin-left: auto;
}
@media (min-width: 640px) {
	.wmb-line__body {
		flex-wrap: nowrap;
		column-gap: 1.5rem;
	}
	.wmb-line__info  { flex: 1 1 auto; }
	.wmb-line__qty   { order: 2; }
	.wmb-line__price { order: 3; margin-left: 0; width: 8.5rem; }
	.wmb-line__remove{ order: 4; margin-left: 0; }
}

/*
 * Add-to-cart toast — replaces WooCommerce's full-page "added to cart" notice.
 * Fired from the isdb-qty inline JS on the `added_to_cart` event.
 */
.wmb-toast-wrap {
	position: fixed;
	z-index: 9999;              /* above the sticky header */
	top: 1rem;                  /* pinned to the TOP */
	right: 0.75rem;             /* anchored to the RIGHT edge (mobile) */
	left: auto;
	bottom: auto;
	transform: none;
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	width: min(88vw, 360px);
	pointer-events: none;
}
@media (min-width: 640px) {
	.wmb-toast-wrap { right: 1.25rem; top: 1.25rem; }  /* top-right on desktop */
}
.wmb-toast {
	pointer-events: auto;
	display: flex;
	align-items: center;
	gap: 0.75rem;
	background: #ffffff;
	color: #222831;
	border: 1px solid #f1f5f9;
	border-left: 4px solid #f4600a;
	border-radius: 0.9rem;
	padding: 0.7rem 0.85rem;
	box-shadow: 0 14px 34px -10px rgba(2, 6, 23, 0.28);
	/* Enter: start off-screen to the RIGHT, fully transparent. */
	transform: translateX(115%);
	opacity: 0;
	filter: blur(0);
	will-change: transform, opacity, filter;
	/* Smooth spring-like slide in from the right. */
	transition: transform 0.55s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.5s ease-out;
}
/* Slide in smoothly from the right. */
.wmb-toast.is-in { transform: translateX(0); opacity: 1; }
/* Exit: dissolve like mist — fade + blur + a gentle upward drift, NO slide back.
   Overrides .is-in (declared after it), so the toast melts away in place. */
.wmb-toast.is-out {
	transform: translateY(-8px) scale(1.04);
	opacity: 0;
	filter: blur(7px);
	transition: opacity 0.6s ease, filter 0.6s ease, transform 0.6s ease;
}
/*
 * Error variant — same shape, red accent, no "View cart" CTA. Used when an AJAX
 * add is rejected (out of stock, min/max quantity, a validation plugin), so the
 * optimistic success toast is replaced by the real reason rather than silently
 * disappearing.
 */
.wmb-toast.is-error { border-left-color: #e11d48; }
.wmb-toast.is-error .wmb-toast__icon { background: linear-gradient(180deg, #f43f5e 0%, #e11d48 100%); }
.wmb-toast.is-error .wmb-toast__title { white-space: normal; }

.wmb-toast__icon {
	flex: none;
	width: 2.1rem;
	height: 2.1rem;
	border-radius: 0.6rem;
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 1.05rem;
	font-weight: 700;
	color: #ffffff;
	background: linear-gradient(180deg, #ff7d1a 0%, #f4600a 100%);
}
.wmb-toast__body { min-width: 0; flex: 1; }
.wmb-toast__title {
	display: block;
	font-size: 0.8rem;
	font-weight: 700;
	line-height: 1.2;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
.wmb-toast__sub {
	display: block;
	margin-top: 0.1rem;
	font-size: 0.72rem;
	color: #64748b;
}
/*
 * "View cart" CTA. This is a <button>, not a link — it dispatches
 * `isdb-open-cart` to open the slide-over drawer so the shopper never leaves the
 * page they were browsing. The resets below make a button render exactly as the
 * old anchor did.
 */
.wmb-toast__cta {
	flex: none;
	appearance: none;
	background: none;
	border: 0;
	padding: 0;
	margin: 0;
	font-family: inherit;
	font-size: 0.72rem;
	font-weight: 700;
	line-height: inherit;
	color: #f4600a;
	text-decoration: none;
	white-space: nowrap;
	cursor: pointer;
}
.wmb-toast__cta:hover { text-decoration: underline; }
.wmb-toast__cta:focus-visible { outline: 2px solid #f4600a; outline-offset: 2px; border-radius: 0.25rem; }

/* ==================================================================
 * WISHLIST HEART
 * Outline by default, solid brand-orange once saved. The fill swap is done with
 * `fill: currentColor` rather than swapping the SVG, so there is one icon in the
 * markup and no flash while toggling.
 * ================================================================== */
.isdb-wishlist-btn.is-in {
	color: #f48721;
	--tw-ring-color: rgb(244 135 33 / 0.45);
}
.isdb-wishlist-btn.is-in svg { fill: currentColor; }
.isdb-wishlist-btn svg { transition: fill 0.15s ease, transform 0.15s ease; }
.isdb-wishlist-btn:active svg { transform: scale(0.88); }
@media (prefers-reduced-motion: reduce) {
	.isdb-wishlist-btn svg { transition: none; }
	.isdb-wishlist-btn:active svg { transform: none; }
}

/* ==================================================================
 * FLOATING CART PILL — right rail, vertically centred (ALL viewports)
 *
 * This is the intended, client-approved position: flush to the right edge of the
 * viewport at 50% height, with only the LEFT corners rounded so it reads as a
 * tab growing out of the screen edge.
 *
 * HISTORY / DO NOT "FIX" AGAIN: an earlier pass moved the pill to the
 * bottom-right corner below 1440px (and a centred bottom bar below 768px) to
 * dodge an overlap with in-page Add-to-Cart rows. That was rejected — it landed
 * the pill on top of the #isdb-top scroll-to-top button (footer.php:
 * `fixed bottom-6 right-6 z-40`) and its `body { padding-bottom: 5.5rem }` space
 * reservation left a dead strip under the footer. The pill stays right-centre;
 * the Cart/Checkout pages, where the overlap actually mattered, are already
 * excluded by the display:none rule near the top of this file.
 * ================================================================== */
.isdb-floating-cart {
	position: fixed;
	right: 0;
	top: 50%;
	bottom: auto;
	transform: translateY(-50%);
	z-index: 40;
}

/* ==================================================================
 * SINGLE-PRODUCT QUANTITY STEPPER
 *
 * ROOT CAUSE OF THE "INVISIBLE QUANTITY" BUG: the theme's spinner-removal rule
 * was scoped to `.woocommerce-cart-form .wmb-qty input.qty` — the CART page
 * only. The single-product stepper never received it, so Chrome/Safari painted
 * native spinner arrows inside the field. Those arrows claim ~16-18px; in a
 * 40px-wide input with zero padding that leaves too little room and the digit
 * is clipped out of view. It surfaced only at narrow widths because that is
 * where the sticky panel — and therefore the field — is tightest.
 * ================================================================== */
.wmb-cart input[name="quantity"] {
	-moz-appearance: textfield;
	appearance: textfield;
	min-width: 2.75rem;   /* fits two digits + breathing room at 320px */
	opacity: 1;
	visibility: visible;
}
.wmb-cart input[name="quantity"]::-webkit-outer-spin-button,
.wmb-cart input[name="quantity"]::-webkit-inner-spin-button {
	-webkit-appearance: none;
	margin: 0;
}

/* ==================================================================
 * VARIABLE / GROUPED PRODUCTS — same cart layout as simple products
 *
 * Simple products use the theme's own stacked form (single-product.php): a
 * full-width quantity bar above a full-width Add to Cart, both 2.75rem tall.
 * Variable and grouped products render WooCommerce's own form instead
 * (woocommerce_template_single_add_to_cart), and its base styling in
 * assets/css/tailwind.css lays the quantity and the button SIDE BY SIDE — a
 * narrow field beside a button with a 12rem minimum, which then wrapped into two
 * mismatched rows on a phone. Same request, so: stack them and match the sizes.
 *
 * Deliberately here and not in tailwind.css: style.css is enqueued with app.css
 * as a dependency, so it loads afterwards and wins on source order at equal
 * specificity — which means no Tailwind rebuild is needed to deploy this.
 * ================================================================== */
.wmb-wc-cart .variations_button,
.wmb-wc-cart .woocommerce-variation-add-to-cart {
	flex-direction: column;
	flex-wrap: nowrap;
}
.wmb-wc-cart .quantity {
	display: flex;
	width: 100%;
	height: 2.75rem;
}
.wmb-wc-cart .quantity input.qty {
	width: 100%;
	height: 100%;
}
.wmb-wc-cart .single_add_to_cart_button {
	width: 100%;
	min-width: 0;      /* the 12rem floor is exactly what forced the wrap */
	height: 2.75rem;
	padding: 0 1.5rem;
	line-height: 1;
}

/* ==================================================================
 * DESCRIPTION SPEC TABLE — 4 columns desktop, 2 columns mobile
 *
 * Built by isdb_format_description_specs() from a "Label: Value" bullet list.
 * Follows the same responsive philosophy as .isdb-attr-table below: stop trying
 * to hold the desktop column count and re-flow instead, rather than shrinking
 * text to squeeze 4 columns onto a phone.
 *
 * The collapse uses `display:grid` on the <tr> rather than the block-stacking
 * .isdb-attr-table uses, because these rows carry FOUR cells, not two. Blocking
 * them would give one cell per line (label / value / label / value) — a single
 * column. A 2-track grid re-flows the same four cells into two tidy
 * label|value rows, which is the required "one pair per row" behaviour.
 * ================================================================== */
.isdb-spec-table { table-layout: fixed; }
.isdb-spec-table .isdb-spec-label { width: 22%; }
.isdb-spec-table .isdb-spec-value { width: 28%; }

@media (max-width: 767px) {
	.isdb-spec-table,
	.isdb-spec-table tbody { display: block; width: 100%; }

	.isdb-spec-table tr {
		display: grid;
		grid-template-columns: 40% 1fr;   /* label | value, one pair per line */
	}

	/*
	 * 🚨 SPECIFICITY, NOT DECORATION — this is what broke the mobile "Key Features"
	 * table. The reset used to be written `.isdb-spec-table td { width: auto }`,
	 * which is (0,1,1) — one class + one type. The desktop widths above are set on
	 * `.isdb-spec-table .isdb-spec-label` / `.isdb-spec-value`, which is (0,2,0) —
	 * TWO classes. A media query adds no specificity, so 22%/28% kept winning
	 * inside this block. Once the row became a grid, those percentages resolved
	 * against the grid TRACK instead of the table, so both cells collapsed to their
	 * min-content width — labels broke as "Recommended / Uses", values as
	 * "SK- / 06019" and "25 × / 20 × / 27 cm" — and the rest of each row was left
	 * as dead white space. The reset must therefore name the same classes.
	 */
	.isdb-spec-table .isdb-spec-label,
	.isdb-spec-table .isdb-spec-value {
		display: block;
		width: auto;
		min-width: 0;   /* a long value shrinks inside its track instead of overflowing */
	}
	/* The odd-count filler has nothing to show once rows re-flow. */
	.isdb-spec-table .isdb-spec-filler { display: none; }
}

/* ==================================================================
 * PRODUCT ATTRIBUTES TABLE — Amazon-style split grid
 * Fixed label column on desktop; rows stack label-over-value on mobile so a
 * long value (care instructions, dimensions) is never crushed into a sliver.
 * ================================================================== */
.isdb-attr-table { table-layout: fixed; }
.isdb-attr-table .isdb-attr-label { width: 38%; }
.isdb-attr-table tr:last-child th,
.isdb-attr-table tr:last-child td { border-bottom: 0; }

@media (max-width: 639px) {
	.isdb-attr-table,
	.isdb-attr-table tbody,
	.isdb-attr-row {
		display: block;
		width: 100%;
	}
	.isdb-attr-row { border-bottom: 1px solid #e5e7eb; }
	.isdb-attr-table tr:last-child { border-bottom: 0; }

	/*
	 * Same specificity trap as .isdb-spec-table above: `width: 38%` is declared with
	 * two classes, so a `th, td` reset (0,1,1) loses to it and the label stayed a
	 * 38%-wide sliver instead of a full-width line. Both cells are therefore reset
	 * by class name here — display and width together, so the stack can't half-apply.
	 */
	.isdb-attr-table .isdb-attr-label,
	.isdb-attr-table .isdb-attr-value {
		display: block;
		width: 100%;
		min-width: 0;
		border-bottom: 0;
	}
	.isdb-attr-table .isdb-attr-label {
		padding-bottom: 0.25rem;
		font-size: 0.75rem;
		text-transform: uppercase;
		letter-spacing: 0.04em;
	}
	.isdb-attr-table .isdb-attr-value {
		padding-top: 0.25rem;
	}
}

/* ==================================================================
 * "SAVE X%" BADGE — anchored to the main photo, never the thumbnail rail
 *
 * The badge is a child of .isdb-gallery, which wraps BOTH the stage and the
 * rail. Below lg the rail sits under the stage, so the badge's natural top-left
 * is already correct. From lg up the rail is a column on the left (the gallery
 * is row-reverse), so the badge must clear it:
 *
 *     rail width (4.5rem) + gallery gap (0.75rem) + inset (0.75rem)
 *
 * Those first two are values this stylesheet itself sets on
 * .flex-control-thumbs and .woocommerce-product-gallery, so the sum cannot
 * drift unless both are edited together. `.has-rail` is written by PHP from the
 * real image count, so single-image products never get the offset.
 * ================================================================== */
.isdb-save-badge {
	position: absolute;
	top: 0.75rem;
	left: 0.75rem;
	z-index: 30;
}
@media (min-width: 1024px) {
	.isdb-gallery.has-rail .isdb-save-badge {
		left: calc(4.5rem + 0.75rem + 0.75rem);
	}
}

/* ==================================================================
 * PRICE ROW — WooCommerce's <del>/<ins> normalised
 *
 * get_price_html() emits core's own markup, which this theme does not
 * otherwise style (all WooCommerce CSS is stripped). Left alone, the
 * strikethrough old price renders at the same 1.5rem weight as the new one, so
 * the row reads as two competing prices instead of one price with an anchor.
 *
 * Scoped to .isdb-price-row and written as descendant selectors on purpose:
 * variable products replace the price node's innerHTML on every variation
 * change, so anything applied via utility classes on the inner elements would
 * be wiped. Descendant CSS keeps applying to whatever markup lands there.
 *
 * `order` (not a DOM/PHP swap): get_price_html() is WooCommerce core and
 * always emits <del> (original) before <ins> (current) — reordering that in
 * PHP risks breaking other places that depend on the same filter. Flipping
 * the visual order in CSS instead leaves the DOM order intact, so screen
 * readers still announce "original price, then current price" (the correct
 * semantic order) while sighted shoppers see the current price first. The
 * flex container is .isdb-price itself (rule further down); its `gap`
 * spaces the items regardless of visual order, so the old del-only
 * margin-right is dropped rather than left stranded on the wrong side.
 * ================================================================== */
.isdb-price-row del {
	text-decoration: line-through;
	color: #94a3b8;          /* slate-400 */
	font-size: 0.875rem;
	font-weight: 500;
	order: 2;
}
.isdb-price-row del .woocommerce-Price-amount,
.isdb-price-row del bdi { font-size: inherit; font-weight: inherit; }

.isdb-price-row ins {
	text-decoration: none;
	background: transparent;  /* some browsers give <ins> a highlight */
	font-size: 1.5rem;
	font-weight: 800;
	color: #0f172a;           /* slate-900 */
	order: 1;
}
.isdb-price-row ins .woocommerce-Price-amount,
.isdb-price-row ins bdi { font-size: inherit; font-weight: inherit; }

/*
 * Tablet step. The `md:text-xl` utility on the price wrapper cannot reach a
 * sale price, because WooCommerce wraps it in <ins> and the rule above pins
 * that to 1.5rem. Without this the price would stay desktop-sized at tablet
 * width on discounted products only — a difference that is easy to miss when
 * testing on a full-price item.
 */
@media (min-width: 768px) and (max-width: 1023px) {
	.isdb-price-row ins { font-size: 1.25rem; }   /* matches md:text-xl */
}

/*
 * Keep the two prices together when space runs out. The price node is one flex
 * item, so <del> and <ins> can only wrap inside it — the savings pill is the
 * separate item and is what drops to its own line first. That is the required
 * 320px behaviour: price and old price stay paired, pill wraps alone.
 */
.isdb-price-row .isdb-price { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem; }

/* ==================================================================
 * SINGLE PRODUCT GALLERY — WooCommerce native (zoom + slider + lightbox)
 *
 * The page now uses woocommerce_show_product_images() instead of a hand-rolled
 * gallery, because native hover-zoom only attaches to WooCommerce's own
 * .woocommerce-product-gallery markup. The layout CSS that normally ships in
 * woocommerce-general.css is stripped by this theme
 * (woocommerce_enqueue_styles -> __return_empty_array), so it is supplied here.
 * PhotoSwipe's own CSS is enqueued separately by WooCommerce and survives, so
 * the lightbox needs nothing from us.
 * ================================================================== */

/* Thumbs left, stage right. flexslider appends .flex-control-thumbs AFTER the
   viewport, so row-reverse is what puts the thumbnail rail on the left. */
.woocommerce-product-gallery {
	display: flex;
	flex-direction: row-reverse;
	align-items: flex-start;
	gap: 0.75rem;
	position: relative;
	/* Safety net: WooCommerce ships this element at opacity:0 and relies on its
	   JS to reveal it. On a cached page with delayed JS that would leave the
	   product image permanently invisible. Force it visible after 2s — by then
	   the script has normally already done it, so the two never fight. */
	animation: isdb-gallery-reveal 0s linear 2s forwards;
}
@keyframes isdb-gallery-reveal { to { opacity: 1; } }

.woocommerce-product-gallery .flex-viewport,
.woocommerce-product-gallery > .woocommerce-product-gallery__wrapper {
	flex: 1 1 auto;
	min-width: 0;
	border-radius: 0.75rem;
	overflow: hidden;
	background: #fff;
	border: 1px solid #e2e8f0;
}

/* Before flexslider initialises, every slide is in normal flow — show only the
   first so a delayed script never stacks the whole gallery down the page. */
.woocommerce-product-gallery__wrapper:not(.flexslider) > *:not(:first-child) { display: none; }

.woocommerce-product-gallery__image img { display: block; width: 100%; height: auto; }

/* Vertical thumbnail rail. */
.woocommerce-product-gallery .flex-control-thumbs {
	flex: 0 0 4.5rem;
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	margin: 0;
	padding: 0;
	list-style: none;
}
.woocommerce-product-gallery .flex-control-thumbs li { margin: 0; width: 100%; }
.woocommerce-product-gallery .flex-control-thumbs img {
	display: block;
	width: 100%;
	height: auto;
	cursor: pointer;
	border: 1px solid #e2e8f0;
	border-radius: 0.5rem;
	background: #fff;
	opacity: 0.65;
	transition: opacity 0.15s ease, border-color 0.15s ease;
}
.woocommerce-product-gallery .flex-control-thumbs img:hover,
.woocommerce-product-gallery .flex-control-thumbs img.flex-active {
	opacity: 1;
	border-color: #f48721;
}

/* The jQuery zoom plugin injects img.zoomImg absolutely inside the trigger. */
.woocommerce-product-gallery__image { position: relative; }
.woocommerce-product-gallery__image .zoomImg {
	background-color: #fff;
	position: absolute;
	top: 0;
	left: 0;
	opacity: 0;
	border: none !important;
	max-width: none !important;
	max-height: none !important;
}

/* Lightbox trigger WooCommerce injects into the gallery. */
.woocommerce-product-gallery__trigger {
	position: absolute;
	top: 0.6rem;
	right: 0.6rem;
	z-index: 20;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	border-radius: 9999px;
	background: #fff;
	box-shadow: 0 2px 8px rgba(2, 6, 23, 0.14);
	font-size: 0;
	text-decoration: none;
}
.woocommerce-product-gallery__trigger::after {
	content: "";
	width: 15px;
	height: 15px;
	border: 2px solid #222831;
	border-radius: 50%;
}
.woocommerce-product-gallery__trigger::before {
	content: "";
	position: absolute;
	right: 0.55rem;
	bottom: 0.6rem;
	width: 2px;
	height: 8px;
	background: #222831;
	transform: rotate(-45deg);
	transform-origin: bottom;
}

/* Mobile: rail moves under the stage and scrolls horizontally. */
@media (max-width: 1023px) {
	.woocommerce-product-gallery { flex-direction: column; }
	.woocommerce-product-gallery .flex-control-thumbs {
		flex: 0 0 auto;
		flex-direction: row;
		overflow-x: auto;
		scrollbar-width: none;
	}
	.woocommerce-product-gallery .flex-control-thumbs::-webkit-scrollbar { display: none; }
	.woocommerce-product-gallery .flex-control-thumbs li { width: 4.5rem; flex: 0 0 auto; }
}

/* ==================================================================
 * REVIEW FORM — star rating selector (no JavaScript)
 *
 * The fieldset is `flex-row-reverse`, so the 5-star radio is FIRST in source
 * order and last visually. That inversion is what makes the pure-CSS hover /
 * checked run possible: `input:checked ~ label` matches every label that comes
 * AFTER the checked input in the source, which — because of the reversal — is
 * exactly the stars at or below the chosen rating.
 *
 * Radios are visually hidden but never display:none, so they stay focusable and
 * keyboard-navigable (arrow keys move through the group) and screen readers can
 * still announce each option.
 * ================================================================== */
.isdb-stars__input {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
}
/* Checked star + every star below it. */
.isdb-stars__input:checked ~ .isdb-stars__star {
	color: #facc15; /* yellow-400 */
}
/* Hover preview — same run, driven by the label being hovered. */
.isdb-stars:hover .isdb-stars__star {
	color: #e2e8f0; /* reset the whole row, then light up the hovered run below */
}
.isdb-stars .isdb-stars__star:hover,
.isdb-stars .isdb-stars__star:hover ~ .isdb-stars__star {
	color: #fbbf24; /* amber-400 — slightly warmer so preview reads as distinct from committed */
}
/* Keyboard focus must be visible even though the input itself is clipped. */
.isdb-stars__input:focus-visible + .isdb-stars__star {
	outline: 2px solid #f48721;
	outline-offset: 2px;
	border-radius: 0.25rem;
}

/*
 * Carousel track — hide the scrollbar in every engine.
 * isdb_carousel_start() sets `scrollbar-width:none` inline, which only covers
 * Firefox; Chrome/Safari/Edge still painted a bar under every rail. Benefits the
 * testimonial slider and every existing product carousel equally. Scrolling
 * itself is untouched — touch, trackpad, arrows and dots all still work.
 */
.isdb-car-track {
	scrollbar-width: none;        /* Firefox */
	-ms-overflow-style: none;     /* legacy Edge */
}
.isdb-car-track::-webkit-scrollbar { /* Chrome / Safari / Edge */
	display: none;
	width: 0;
	height: 0;
}

/* ==================================================================
 * SHOP FILTER PANEL — one element, two behaviours
 *
 * Below lg it is an off-canvas drawer; at lg and up it is a static sidebar
 * column that is ALWAYS visible. Both states are driven by this one rule set
 * rather than Tailwind utilities, deliberately:
 *
 *   - x-show can't be used, because it would force display:none on desktop
 *     whenever filterOpen is false (i.e. always).
 *   - A :class transform toggle would need !important gymnastics to beat the
 *     lg: variant deterministically, and its pre-Alpine state would flash the
 *     open drawer on mobile before Alpine boots.
 *
 * With the closed state as the CSS default, the panel is correctly hidden on
 * mobile and correctly visible on desktop even before Alpine initialises — no
 * flash, no dependence on JS for the desktop layout at all.
 *
 * The visibility transition is delayed by the slide duration on close so the
 * slide-out animation finishes before the panel becomes untabbable, and is
 * instant on open. visibility:hidden also keeps the off-screen panel out of
 * the tab order.
 * ================================================================== */
.isdb-filter-panel {
	transform: translateX(-100%);
	visibility: hidden;
	transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), visibility 0s linear 0.3s;
}
.isdb-filter-panel.is-open {
	transform: translateX(0);
	visibility: visible;
	transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), visibility 0s;
}
@media (min-width: 1024px) {
	/* Static column: no transform, always visible, never animated. */
	.isdb-filter-panel {
		transform: none;
		visibility: visible;
		transition: none;
	}
}

/* ==================================================================
 * SHOP TOOLBAR — "Filter & Sort" vs sort dropdown, mobile
 *
 * The sort trigger carries a fixed Tailwind width (w-[13.5rem] = 216px).
 * Below ~480px that alone eats most of the row, and since neither sibling
 * has an explicit basis, flexbox's default flex-shrink:1 squeezes the
 * "Filter & Sort" button until its label wraps to two lines.
 *
 * Fix: below 768px both controls take an equal share of the row (the
 * standard `flex: 1 1 0%` equal-column trick — divides space AFTER the
 * gap, so it can't overflow the way a 50%+50% width split would) and the
 * button label is pinned to one line.
 * ================================================================== */
@media (max-width: 768px) {
	.isdb-shop-toolbar {
		gap: 0.5rem;
	}
	.isdb-shop-toolbar .isdb-filter-toggle,
	.isdb-shop-toolbar .isdb-sort-wrap {
		flex: 1 1 0%;
		width: auto;
		margin-left: 0;
	}
	.isdb-shop-toolbar .isdb-filter-toggle {
		justify-content: center;
		white-space: nowrap;
	}
	.isdb-shop-toolbar .isdb-sort-trigger {
		width: 100%;
	}
}

/* ==================================================================
 * CHECKOUT COUPON ROW — forced inline
 *
 * The markup in woocommerce/checkout/form-checkout.php already carries
 * `flex flex-row items-center gap-3`, but those are COMPILED Tailwind
 * utilities living in app.css. If app.css is stale on a deploy, or an
 * optimiser serves a cached/combined stylesheet, the row silently falls back
 * to block layout and the Apply button drops beneath the input — the exact
 * reported symptom.
 *
 * These rules live in style.css, which is enqueued raw and versioned by
 * filemtime (see functions.php), so they ship immediately, bust the browser
 * cache on change, and cannot be tree-shaken by Tailwind's purge.
 * ================================================================== */
/*
 * MOVED — the coupon row's layout rules now live in a scoped <style id="isdb-coupon-css">
 * block inside woocommerce/checkout/form-checkout.php, next to the markup they
 * style. They were relocated for two reasons:
 *
 *   1. WooCommerce's checkout.js calls $('form.checkout_coupon').hide(), which
 *      sets an INLINE display:none on the form. Beating an inline style needs
 *      !important, and keeping the forcing rules beside the markup makes that
 *      requirement obvious to the next person who touches it.
 *   2. Shipping them with the template removes any dependency on style.css /
 *      app.css being fresh in a given deploy.
 *
 * Everything there is scoped to #isdb-coupon-form and cannot affect any other
 * input on the site. Nothing to add here.
 */

/* ==================================================================
 * ORDER RECEIVED — celebration
 * Lives here rather than in tailwind.css so it works the moment the theme is
 * deployed, without waiting for an `npm run build`. Pure CSS: no confetti
 * library, no canvas, nothing that costs the shopper a request.
 * ================================================================== */

/* Halo breathing out from behind the tick — the "it worked" pulse. */
.wmb-celebrate__halo { animation: wmb-halo 2.6s ease-out infinite; }
@keyframes wmb-halo {
	0%   { transform: scale(0.8); opacity: 0.5; }
	70%  { transform: scale(1.45); opacity: 0; }
	100% { transform: scale(1.45); opacity: 0; }
}

/* The tick draws itself once — reads as "completing", not just "done". */
.wmb-celebrate__tick {
	stroke-dasharray: 32;
	stroke-dashoffset: 32;
	animation: wmb-draw 0.55s cubic-bezier(0.65, 0, 0.35, 1) 0.25s forwards;
}
@keyframes wmb-draw { to { stroke-dashoffset: 0; } }

/* Disc pops in under the tick. */
.wmb-celebrate__disc { animation: wmb-pop 0.5s cubic-bezier(0.18, 0.89, 0.32, 1.28) both; }
@keyframes wmb-pop { 0% { transform: scale(0); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }

/* Confetti: each chip carries its own --dx / --rot / delay from inline style. */
.wmb-confetti { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.wmb-confetti i {
	position: absolute;
	top: -6%;
	width: 7px;
	height: 11px;
	border-radius: 2px;
	opacity: 0;
	animation: wmb-fall 2.8s cubic-bezier(0.3, 0.6, 0.5, 1) forwards;
}
@keyframes wmb-fall {
	0%   { opacity: 0; transform: translate3d(0, -12px, 0) rotate(0deg); }
	12%  { opacity: 1; }
	100% { opacity: 0; transform: translate3d(var(--dx, 0px), 340px, 0) rotate(var(--rot, 540deg)); }
}

/* Motion is decoration here — the page reads identically without it. */
@media (prefers-reduced-motion: reduce) {
	.wmb-celebrate__halo,
	.wmb-celebrate__disc { animation: none !important; }
	.wmb-celebrate__tick { stroke-dashoffset: 0; animation: none !important; }
	.wmb-confetti { display: none; }
}

/* ==================================================================
 * POLICY ACCORDIONS
 * Native <details>/<summary>, not an Alpine widget: the content stays
 * readable and expandable with zero JavaScript, which matters on
 * optimiser-processed cached pages where scripts can be delayed.
 * ================================================================== */
.wmb-acc > summary { list-style: none; cursor: pointer; }
.wmb-acc > summary::-webkit-details-marker { display: none; }
.wmb-acc > summary::marker { content: ''; }
.wmb-acc__chev { transition: transform 0.2s ease; }
.wmb-acc[open] .wmb-acc__chev { transform: rotate(180deg); }
.wmb-acc[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }

/*
 * Social share — "Copy link" icon swap. JS toggles .is-copied on the button;
 * these two rules do the rest (Tailwind can't express a JS-driven class variant
 * without an arbitrary-variant plugin).
 */
.isdb-share-copy .isdb-share-ico-check { display: none; }
.isdb-share-copy.is-copied .isdb-share-ico-link { display: none; }
.isdb-share-copy.is-copied .isdb-share-ico-check { display: block; }
.isdb-share-copy.is-copied {
	border-color: #f48721;
	background: #f48721;
	color: #fff;
}

/*
 * Floating cart pill — solid-orange top + white price block (per spec).
 * Colours come from the Tailwind classes in the markup (bg-brand-primary /
 * bg-white / text-brand-primary); here we add a soft shadow toward the screen so
 * the pill lifts off the page edge, plus the corner radius.
 *
 * RADIUS LIVES HERE, NOT IN A TAILWIND CLASS: the requested 2px is nearly square
 * and only applies to the LEFT corners (the right edge is flush against the
 * viewport). `rounded-l-sm` is not present in the compiled assets/css/app.css, so
 * using it in the markup would silently do nothing until someone re-ran the
 * Tailwind build. Keeping it in this hand-written file also means the value is
 * correct for fragment markup already cached in a visitor's sessionStorage,
 * which may still carry the old rounded-l-2xl class.
 */
.isdb-floating-cart > button {
	/*
	 * The !important is load-bearing: the markup still carries `shadow-lg ring-1
	 * ring-black/5`, and Tailwind composes all three into one `box-shadow`, so
	 * without it the ring would win. Those utilities are therefore inert — do not
	 * drop this !important expecting the markup's shadow to take over, and note
	 * that fragment markup cached in a visitor's sessionStorage can still carry
	 * them even if they are removed from functions.php later.
	 */
	box-shadow: -6px 8px 20px -8px rgba(2, 6, 23, 0.30) !important;
	border-radius: 2px 0 0 2px;
}
