/**
 * Native single product - the Shofy structure, this brand's tokens.
 *
 * ⚠ NEVER WRITE A COMMENT DELIMITER INSIDE A COMMENT IN THIS FILE.
 *   CSS comments do not nest; an inner close ends the block early and the parser then discards
 *   whole rules. Verify with: python tools/css-comment-check.py <this file>
 *
 * Taken from Shofy's own compiled values: product title 32px/500 line-height 1, price 24px/500,
 * the stock pill as accent text on a 6% accent tint at 4px 12px, the vertical 78px thumbnail rail
 * beside the stage, the outline add-to-cart at 9px 30px with a 1px #E0E2E3 border, the solid
 * accent buy-now at 10px 30px, and the payment row under the cart area.
 *
 * 🔴 IT RESTYLES WOOCOMMERCE'S OWN CART FORM RATHER THAN REPLACING IT.
 *   The selectors below reach into `.variations`, `.quantity`, `.single_add_to_cart_button` and
 *   `.woocommerce-variation-*` because that markup is WooCommerce's and stays WooCommerce's - it
 *   carries the variation matching, the nonce and the stock rules. Styling it is the whole job;
 *   rebuilding it would put the payment path in our hands for no visual gain.
 *
 * No colour or typeface is declared here. Everything comes from the global `--afw-*` tokens, with
 * a fallback on each `var()` so the page still renders if the token layer is switched off.
 */

.afw-p {
	padding-bottom: 90px;
}

.afw-p .afw-crumb {
	padding-top: 26px;
	margin-bottom: 26px;
}

/*  ---- the two columns --------------------------------------------------  */
.afw-p-top {
	display: grid;
	grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
	gap: 56px;
	align-items: start;
	margin-bottom: 64px;
}

/**
 * ⚠️ THE RAIL IS NOT IN THE GRID UNLESS THERE IS A RAIL.
 *   `grid-template-columns` is declared on the gallery only when the rail exists, via
 *   `:has()`. 686 of 1,086 products have a single image, so an always-present 78px gutter would
 *   leave most of the catalogue looking like a failed image load.
 */
.afw-p-gallery {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: 18px;
	min-width: 0;
}

.afw-p-gallery:has(.afw-p-rail) {
	grid-template-columns: 78px minmax(0, 1fr);
}

.afw-p-rail {
	display: flex;
	flex-direction: column;
	gap: 11px;
}

/**
 * ═════════════════════════════════════════════════════════════════════════════════════════════
 * THE RAIL GOES HORIZONTAL AT FOUR IMAGES - because past three it is taller than the stage
 * ═════════════════════════════════════════════════════════════════════════════════════════════
 *
 * 🔴 THE REPORTED DEAD SPACE WAS ARITHMETIC I NEVER DID. The rail is an uncapped vertical column
 *   at 78px a thumb plus an 11px gap, and the stage beside it is a SQUARE - so its height is just
 *   its own width. Past a certain count the rail simply runs off the bottom of the image and the
 *   difference is empty page. Measured across the catalogue:
 *
 *       images   products   rail height     verdict
 *            1        582           0px     no rail at all
 *            2        100         167px     fits
 *            3        235         256px     fits
 *            4        122         345px     marginal - only ~19px of slack at 1025px wide
 *            5         27         434px     OVERFLOWS
 *            6          8         523px     OVERFLOWS
 *          8-10         3     701-879px     OVERFLOWS
 *         13-14         5   1146-1235px     OVERFLOWS - 735px of white space on the worst one
 *
 *   The product in the screenshot is one of the 9-image ones. 165 of 1,086 product pages were
 *   affected, and every product I opened while building had three images or fewer.
 *
 * ⚠️ THE THRESHOLD IS FOUR, NOT FIVE, AND THE 19px OF SLACK IS WHY. At 1025px - the first width
 *   where the two-column layout applies - the stage computes to about 364px against a 4-thumb
 *   rail of 345px. Nineteen pixels is not a margin, it is a coincidence waiting to break on a
 *   padding change. Three thumbs (256px) clears every width with room to spare.
 *
 * ⚠️ ONE ROW THAT SCROLLS, NOT SEVERAL THAT WRAP. Wrapping 14 thumbs under the stage would add
 *   ~267px and make the GALLERY the tall column instead - the same bug mirrored, with the dead
 *   space moved under the info panel. A single scrolling row makes the gallery exactly
 *   `stage + gap + 78px` tall whatever the image count, which is the only version of this that
 *   cannot come back.
 *
 * ⚠️ And the scroll is `overflow-x` ON THE RAIL, not on the page. A row of 14 thumbs is 1,245px
 *   wide; left to itself it would widen the layout and put a horizontal scrollbar on the whole
 *   document.
 */
.afw-p-gallery:has(.afw-p-rail button:nth-child(4)) {
	grid-template-columns: minmax(0, 1fr);
}

.afw-p-gallery:has(.afw-p-rail button:nth-child(4)) .afw-p-stage {
	order: 1;
}

.afw-p-gallery:has(.afw-p-rail button:nth-child(4)) .afw-p-rail {
	order: 2;
	/* a grid item that scrolls already gets an automatic minimum size of zero by spec - stating
	   it anyway, because "by spec" is how the last three layout bugs in this project started */
	min-width: 0;
	flex-direction: row;
	gap: 10px;
	overflow-x: auto;
	overscroll-behavior-x: contain;
	scroll-snap-type: x proximity;
	scrollbar-width: thin;
	scrollbar-color: var(--afw-control, #8c7ba6) transparent;
	padding-bottom: 8px;
}

.afw-p-gallery:has(.afw-p-rail button:nth-child(4)) .afw-p-rail button {
	flex: 0 0 auto;
	scroll-snap-align: start;
}

/* a thin track, so the strip does not read as a browser chrome element */
.afw-p-rail::-webkit-scrollbar { height: 6px; }
.afw-p-rail::-webkit-scrollbar-track { background: var(--afw-band, #f5f2f8); border-radius: 3px; }
.afw-p-rail::-webkit-scrollbar-thumb {
	background: var(--afw-control, #8c7ba6);
	border-radius: 3px;
}

.afw-p-rail button {
	display: block;
	width: 78px;
	height: 78px;
	padding: 0;
	cursor: pointer;
	border: 1px solid var(--afw-line, #e4deec);
	border-radius: var(--afw-radius, 2px);
	background: var(--afw-card, #fff) center / cover no-repeat;
	transition: border-color var(--afw-ease, 150ms ease-out), box-shadow var(--afw-ease, 150ms ease-out);
}

.afw-p-rail button:hover { border-color: var(--afw-mauve, #7a62a8); }

.afw-p-rail button.is-on {
	border-color: var(--afw-mauve, #7a62a8);
	box-shadow: 0 0 0 2px rgba(122, 98, 168, 0.28);
}

.afw-p-stage {
	background: var(--afw-card, #fff);
	border-radius: var(--afw-radius, 2px);
	overflow: hidden;
	min-width: 0;
}

.afw-p-stage img {
	display: block;
	width: 100%;
	height: auto;
	aspect-ratio: 1;
	object-fit: contain;
}

/*  ---- the info column --------------------------------------------------  */
.afw-p-info { min-width: 0; }

.afw-p-cat {
	margin: 0 0 8px;
	font-size: 11.5px;
	font-weight: 500;
	letter-spacing: var(--afw-tr-label, 0.08em);
	text-transform: uppercase;
	color: var(--afw-meta, #6e6b7b);
}

.afw-p-title {
	font-family: var(--afw-serif, Georgia, serif);
	font-size: clamp(27px, 3.4vw, 34px);
	font-weight: 500;
	line-height: 1.1;
	letter-spacing: var(--afw-tr-display, -0.02em);
	color: var(--afw-ink, #1c1a22);
	margin: 0 0 14px;
	text-transform: none;
	text-wrap: balance;
}

.afw-p-meta-row {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 12px;
	margin-bottom: 16px;
}

.afw-p-stock {
	display: inline-block;
	font-size: 13.5px;
	line-height: 1;
	padding: 6px 12px;
	border-radius: var(--afw-radius, 2px);
	color: var(--afw-sage, #3f6b4a);
	background: var(--afw-sage-soft, #e8f0e9);
}

.afw-p-stock.is-out {
	color: var(--afw-clay, #7a3b33);
	background: var(--afw-clay-soft, #f7eae7);
}

.afw-p-rating {
	font-size: 13.5px;
	color: var(--afw-meta, #6e6b7b);
	font-variant-numeric: tabular-nums;
}

.afw-p-short {
	font-size: 16px;
	line-height: 1.72;
	color: var(--afw-ink-2, #4c4a57);
	margin-bottom: 20px;
	max-width: 60ch;
}

.afw-p-short p { margin: 0 0 9px; }
.afw-p-short p:last-child { margin-bottom: 0; }

.afw-p-price {
	margin: 0 0 24px;
	font-size: 24px;
	font-weight: 500;
	letter-spacing: -0.02em;
	color: var(--afw-ink, #1c1a22);
	font-variant-numeric: tabular-nums;
}

.afw-p-price del { font-size: 18px; font-weight: 400; color: var(--afw-meta, #6e6b7b); margin-right: 8px; }
.afw-p-price ins { text-decoration: none; background: transparent; color: inherit; }

/*  ---- the size readout - the answer to 463 keywords, above the button ---  */
/**
 * ═════════════════════════════════════════════════════════════════════════════════════════════
 * THE SIZE READOUT - a badge and a ruler, not a card
 * ═════════════════════════════════════════════════════════════════════════════════════════════
 *
 * 🔴 THE EMPTY BOX WAS A CONTAINER BUILT FOR CONTENT THAT NO LONGER EXISTS. It carried a white
 *   card, 16px/18px of padding and a ruled list - a sensible shape when each row read
 *   "M  fits plush 20-29 cm" and there could be several. The fit range was removed from this block
 *   on 2026-09-14 at the owner's request, which left **one short line inside a padded card**:
 *   ~200px of white around eight characters, with a divider drawn under a list of one.
 *   Measured across the catalogue: of the **408 products that render this block at all, 406 carry
 *   exactly ONE size** - one has four and one has five, and that is the whole distribution. So the
 *   sparse case was not an edge case, it was 99.5% of them.
 *
 *   ⚠️ The lesson is not "the box was too big". It is that **removing content without revisiting
 *   its container leaves the container behind**, and a box sized for three rows reads as broken
 *   when it holds one. The same sweep that dropped the range should have dropped this.
 *
 * 🔴 AND IT IS DELIBERATELY NOT INTERACTIVE, WHICH IS A DEPARTURE FROM THE BRIEF.
 *   The brief asked for a "sleek, interactive Size Pill", and offered "or refined inline badge" -
 *   this takes the badge. **The real size selector is WooCommerce's own variation form directly
 *   below**, and two controls for one attribute is how a customer ends up with a cart that does
 *   not match what they clicked. So the badge carries no hover lift, no pointer cursor and no
 *   pressed state: it reads as information, because that is all it is. The pills that ARE
 *   controls - the ones `product.js` builds over the `<select>` - look and behave differently on
 *   purpose.
 */
.afw-p-sizes {
	margin: 0 0 20px;
}

.afw-p-sizes .afw-p-sub {
	display: inline-flex;
	align-items: center;
	gap: 7px;
	font-family: var(--afw-sans, system-ui, sans-serif);
	font-size: 11.5px;
	font-weight: 600;
	letter-spacing: var(--afw-tr-label, 0.08em);
	text-transform: uppercase;
	color: var(--afw-meta, #6e6b7b);
	margin: 0 0 9px;
}

/* the glyph carries the accent, the words stay quiet - one mauve mark, not a mauve heading */
.afw-p-sizes .afw-p-sub .afw-i { color: var(--afw-mauve, #7a62a8); }

.afw-p-sizes ul {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
}

.afw-p-sizes li {
	display: inline-flex;
	align-items: baseline;
	gap: 6px;
	padding: 7px 14px;
	font-size: 14px;
	color: var(--afw-ink, #1c1a22);
	background: var(--afw-wash, #f1ebf8);
	/**
	 * ⚠️ A TINTED EDGE, NOT `--afw-line`. The badge now sits on the cream page ground rather than
	 *   inside a white card, and the neutral hairline was invisible against it. The border is the
	 *   accent at 28% - enough to read as a defined shape without becoming a second mauve element
	 *   competing with the colour swatches underneath.
	 */
	border: 1px solid rgba(122, 98, 168, 0.28);
	border-radius: 999px;
}

.afw-p-sizes li b {
	font-weight: 500;
	/* so 28 cm and 31 cm line up when a garment has more than one */
	font-variant-numeric: tabular-nums;
}

.afw-p-sizes li span {
	color: var(--afw-mauve-strong, #5f4a8a);
	font-size: 12.5px;
}

.afw-p-guide {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	margin-top: 10px;
	font-size: 13px;
	color: var(--afw-mauve, #7a62a8);
	text-decoration: none;
}

.afw-p-guide:hover { text-decoration: underline; }
.afw-p-guide .afw-i { color: var(--afw-mauve, #7a62a8); }

/*  ---- WooCommerce's cart form, restyled --------------------------------  */
.afw-p-cart { margin-bottom: 20px; }

.afw-p-cart .variations {
	width: 100%;
	border-collapse: collapse;
	margin: 0 0 18px;
}

.afw-p-cart .variations tr { display: block; margin-bottom: 14px; }

.afw-p-cart .variations th,
.afw-p-cart .variations td {
	display: block;
	padding: 0;
	border: 0;
	text-align: left;
}

.afw-p-cart .variations .label label {
	font-family: var(--afw-sans, system-ui, sans-serif);
	font-size: 11.5px;
	font-weight: 600;
	letter-spacing: var(--afw-tr-label, 0.08em);
	text-transform: uppercase;
	color: var(--afw-meta, #6e6b7b);
	margin-bottom: 7px;
	display: block;
}

/**
 * ⚠️ The select keeps its own appearance chrome removed and a REAL border at 3:1 - WCAG 1.4.11
 *   applies to the boundary of any control the customer must find, and this one chooses the size.
 */
.afw-p-cart .variations select,
.afw-p-cart select {
	appearance: none;
	-webkit-appearance: none;
	width: 100%;
	max-width: 340px;
	font-family: var(--afw-sans, system-ui, sans-serif);
	font-size: 15px;
	color: var(--afw-ink, #1c1a22);
	background-color: var(--afw-card, #fff);
	background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23221D19' stroke-width='1.4' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 14px center;
	border: 1px solid var(--afw-control, #8c7ba6);
	border-radius: var(--afw-radius, 2px);
	height: 46px;
	padding: 0 40px 0 14px;
	cursor: pointer;
}

.afw-p-cart .reset_variations {
	display: inline-block;
	margin-top: 8px;
	font-size: 13px;
	color: var(--afw-meta, #6e6b7b);
}

.afw-p-cart .woocommerce-variation-price {
	margin: 0 0 14px;
	font-size: 20px;
	font-weight: 500;
	color: var(--afw-ink, #1c1a22);
	font-variant-numeric: tabular-nums;
}

.afw-p-cart .woocommerce-variation-availability p { margin: 0 0 12px; font-size: 14px; }

.afw-p-cart form.cart {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 12px;
	margin: 0;
}

.afw-p-cart form.variations_form { display: block; }
.afw-p-cart .woocommerce-variation-add-to-cart { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; }

.afw-p-cart .quantity {
	display: inline-flex;
	align-items: center;
	border: 1px solid var(--afw-control, #8c7ba6);
	border-radius: var(--afw-radius, 2px);
	background: var(--afw-card, #fff);
	overflow: hidden;
}

.afw-p-cart .quantity input[type="number"],
.afw-p-cart .quantity .qty {
	width: 72px;
	height: 50px;
	border: 0;
	background: transparent;
	text-align: center;
	font-family: var(--afw-sans, system-ui, sans-serif);
	font-size: 15px;
	color: var(--afw-ink, #1c1a22);
	font-variant-numeric: tabular-nums;
	box-shadow: none;
	border-radius: 0;
	padding: 0;
}

.afw-p-cart .quantity input:focus { outline: none; box-shadow: none; }

.afw-p-cart .stock.out-of-stock {
	margin: 0 0 12px;
	font-size: 14px;
	color: var(--afw-clay, #7a3b33);
}

/**
 * ═════════════════════════════════════════════════════════════════════════════════════════════
 * SHOFY VARIATION SWATCHES - and the one rule that makes them safe
 * ═════════════════════════════════════════════════════════════════════════════════════════════
 *
 * From `product-details-swatches.html`, Shofy presents variations as swatches rather than a
 * dropdown: round colour buttons with a tooltip, and labelled pills for anything textual.
 * Measured: `.tp-color-variation-btn` is a 32px round button with a tooltip above it.
 *
 * 🔴 THE SWATCHES DO NOT REPLACE WOOCOMMERCE'S SELECT. THEY DRIVE IT.
 *   Each swatch sets the value of the real `<select name="attribute_...">` and dispatches a
 *   `change` event, and WooCommerce's own variation script does everything after that - matching
 *   the variation, updating the price and availability, swapping the image and enabling the
 *   button. The select stays in the DOM, so the form still submits exactly what it always did.
 *
 *   ⚠️ The alternative - swatches that post their own attribute values - would be a second
 *   implementation of the logic that decides what the customer is buying. That is the same trap
 *   as the gallery: two answers to "which variation is this" is how someone is charged for one
 *   colourway and shipped another. The select is the single source of truth and stays visible to
 *   screen readers.
 */
.afw-p-swatches {
	margin-bottom: 4px;
}

.afw-p-swatch-row {
	display: flex;
	flex-wrap: wrap;
	gap: 9px;
	margin-bottom: 4px;
}

/* the colour swatch: Shofy's 32px round button with a tooltip above */
.afw-p-sw {
	position: relative;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 32px;
	height: 32px;
	padding: 0;
	border: 1px solid rgba(28, 26, 34, 0.14);
	border-radius: 50%;
	background: var(--afw-card, #fff) center / cover no-repeat;
	cursor: pointer;
	transition: box-shadow var(--afw-ease, 150ms ease-out), border-color var(--afw-ease, 150ms ease-out);
}

/**
 * ⚠️ A colour-filled swatch needs its own inner hairline. "White" and "Cream" are two of the
 *   three most common colourway words (23 and 15 of 106 values) and a white circle on a white
 *   card is invisible without one - the same problem the archive's cream family had.
 */
.afw-p-sw.is-fill {
	box-shadow: inset 0 0 0 1px rgba(28, 26, 34, 0.16);
}

.afw-p-sw:hover,
.afw-p-sw[aria-pressed="true"] {
	border-color: var(--afw-mauve, #7a62a8);
	box-shadow: 0 0 0 2px rgba(122, 98, 168, 0.3);
}

/* a text swatch is a pill - a size is a word and a circle cannot carry one */
.afw-p-sw.is-text {
	width: auto;
	border-radius: var(--afw-radius, 2px);
	padding: 0 14px;
	font-family: var(--afw-sans, system-ui, sans-serif);
	font-size: 14px;
	line-height: 1;
	color: var(--afw-ink-2, #4c4a57);
	/* ⚠️ was `--afw-border`, a token that does not exist - it rendered only because of the
	   fallback. The real one is `--afw-line`. A nonexistent token is invisible until the
	   fallback and the intended value diverge. */
	border-color: var(--afw-line, #e4deec);
	white-space: nowrap;
	font-variant-numeric: tabular-nums;
}

.afw-p-sw.is-text[aria-pressed="true"] {
	background: var(--afw-mauve-strong, #5f4a8a);
	border-color: var(--afw-mauve-strong, #5f4a8a);
	color: #fff;
	box-shadow: none;
}

/* WooCommerce removes options as choices narrow - a swatch whose value is gone says so */
.afw-p-sw.is-unavailable {
	opacity: 0.4;
	cursor: not-allowed;
	position: relative;
}

.afw-p-sw.is-unavailable::before {
	position: absolute;
	content: "";
	left: 10%;
	right: 10%;
	top: 50%;
	height: 1px;
	background: var(--afw-meta, #6e6b7b);
	transform: rotate(-20deg);
}

.afw-p-sw .tip {
	position: absolute;
	bottom: calc(100% + 8px);
	left: 50%;
	transform: translateX(-50%);
	white-space: nowrap;
	font-size: 12px;
	line-height: 1;
	padding: 6px 9px;
	background: var(--afw-ink, #1c1a22);
	color: #fff;
	border-radius: var(--afw-radius, 2px);
	visibility: hidden;
	opacity: 0;
	transition: all var(--afw-ease, 150ms ease-out);
	pointer-events: none;
	z-index: 2;
}

.afw-p-sw:hover .tip,
.afw-p-sw:focus-visible .tip { visibility: visible; opacity: 1; }

/**
 * 2026-09-30: the circle shows the COLOUR the value names; the variation's photograph is a
 * preview card above it on hover or focus (owner: the photo inside a 32px circle read as a
 * white bubble). A value with a photograph but no colour word shows the photo zoomed in on the
 * garment instead of the whole white frame.
 */
.afw-p-sw.is-fill,
.afw-p-sw.is-photo { width: 36px; min-width: 36px; height: 36px; }
.afw-p-sw.is-photo { background-size: 280%; background-position: 50% 62%; box-shadow: inset 0 0 0 1px rgba(28, 26, 34, 0.16); }
.afw-p-sw .pic {
	position: absolute;
	bottom: calc(100% + 34px);
	left: 50%;
	width: 104px;
	height: 104px;
	transform: translate(-50%, 6px) scale(0.94);
	border-radius: 16px;
	border: 3px solid #fff;
	background: #fff center / cover no-repeat;
	box-shadow: 0 2px 6px rgba(28, 26, 34, 0.06), 0 18px 40px rgba(95, 74, 138, 0.22);
	visibility: hidden;
	opacity: 0;
	pointer-events: none;
	z-index: 4;
	transition: opacity 180ms ease-out, transform 260ms cubic-bezier(0.22, 1, 0.36, 1), visibility 180ms;
}
.afw-p-sw:hover .pic,
.afw-p-sw:focus-visible .pic { visibility: visible; opacity: 1; transform: translate(-50%, 0) scale(1); }
.afw-p-sw .pic ~ .tip,
.afw-p-sw .tip:has(~ .pic) { bottom: calc(100% + 8px); }
@media (hover: none) {
	.afw-p-sw .pic { display: none; }
	/* 2026-10-04: the same for the name tip. On a touch screen a hover that reveals something makes iOS
	   spend the first tap on it ("tap twice" to choose a colour). Here the tip stays as it is at rest. */
	.afw-p-sw:hover .tip { visibility: hidden; opacity: 0; }
}

/**
 * ⚠️ The real select is hidden VISUALLY ONLY - never with `display: none`.
 *   A `display: none` select is removed from the accessibility tree and, in some browsers, from
 *   form submission edge cases. This keeps it focusable and submitted, so a keyboard or
 *   screen-reader user still has the native control and the swatches are pure enhancement.
 */
.afw-p-cart.has-swatches .variations select {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: -1px;
	padding: 0;
	overflow: hidden;
	clip: rect(0 0 0 0);
	clip-path: inset(50%);
	white-space: nowrap;
	border: 0;
}

.afw-p-cart.has-swatches .variations select:focus-visible {
	position: static;
	width: 100%;
	height: auto;
	margin: 0;
	clip: auto;
	clip-path: none;
	padding: 0 40px 0 14px;
	min-height: 46px;
	border: 1px solid var(--afw-mauve, #7a62a8);
}

/*  ---- the payment trust row, from Shofy --------------------------------  */
.afw-p-pay {
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: 14px;
	padding: 15px 0 18px;
	border-top: 1px solid var(--afw-hair, #eeeaf3);
	border-bottom: 1px solid var(--afw-hair, #eeeaf3);
	margin-bottom: 20px;
}

.afw-p-pay p {
	margin: 0;
	font-size: 13px;
	line-height: 1.45;
	color: var(--afw-meta, #6e6b7b);
}

.afw-p-pay img {
	display: block;
	max-width: 248px;
	height: auto;
}

/*  ---- the fact list ----------------------------------------------------  */
.afw-p-facts {
	display: grid;
	grid-template-columns: max-content minmax(0, 1fr);
	gap: 7px 16px;
	margin: 0;
	font-size: 14px;
}

.afw-p-facts dt {
	font-size: 11.5px;
	font-weight: 600;
	letter-spacing: var(--afw-tr-label, 0.08em);
	text-transform: uppercase;
	color: var(--afw-meta, #6e6b7b);
	padding-top: 2px;
}

.afw-p-facts dd { margin: 0; color: var(--afw-ink-2, #4c4a57); }

/*  ---- tabs -------------------------------------------------------------  */
.afw-p-tabs { margin-bottom: 60px; }

/**
 * 🔴 THE TABS, REBUILT TO SHOFY'S OWN SPEC - AND THE OLD VERSION'S THREE FAULTS NAMED.
 *
 *   Measured from Shofy's `main.css`:
 *       .tp-product-details-tab-nav .nav-tabs          padding-bottom: 12px;
 *                                                      border-bottom: 1px solid #E0E2E3
 *       ... .nav-link                                  font-size: 20px; color: #A0A2A4
 *       ... .nav-link:not(:first-child)                margin-left: 40px
 *       ... .nav-link.active                           color: near-black
 *       ... .nav-link::after                           right: 0; width: 0; height: 2px; bottom: -1px
 *       ... .nav-link.active::after                    left: 0; right: auto; width: 100%
 *       .tp-tab .nav-tabs .nav-link                    padding: 0; margin: 0; border: 0
 *
 *   WHAT WAS WRONG WITH MINE, all three visible in the owner's screenshot:
 *     1. **Uppercase with 0.08em tracking at 15px.** That is a label treatment, not a tab, and it
 *        is what made three short words read as crowded. Shofy sets neither - plain sentence case.
 *     2. **The bottom padding was on the BUTTON, not the nav.** So the 2px indicator, anchored to
 *        the button at `bottom: -1px`, landed on the container's own 1px border and the two lines
 *        merged into one thick rule that looked like it was cutting the text. Shofy puts the
 *        padding on the nav, which separates the indicator (under the word) from the rule (under
 *        the row) - two lines with a real gap, which is the whole point of the design.
 *     3. **30px of gap between items.** Shofy uses 40px, and at sentence case that space is what
 *        stops the three words running together.
 *
 *   Kept from Shofy and worth keeping: the indicator grows **from the right on hover and from the
 *   left when active**, which is why `::after` is anchored `right: 0` by default. It is a small
 *   thing that makes the row feel responsive without any movement of the text itself.
 *
 *   ⚠️ `line-height` is set explicitly. The theme's reset gives every `button` `line-height:
 *   inherit`, so without this the tab inherits the body's 1.7 and the text box grows tall enough
 *   for the indicator to sit visibly away from the word it belongs to.
 */
.afw-p-tabnav {
	display: flex;
	align-items: flex-end;
	gap: 0 40px;
	flex-wrap: wrap;
	padding-bottom: 12px;
	border-bottom: 1px solid var(--afw-line, #e4deec);
	margin-bottom: 30px;
}

.afw-p-tabnav button {
	position: relative;
	border: 0;
	background: transparent;
	margin: 0;
	padding: 0 0 6px;
	font-family: var(--afw-sans, system-ui, sans-serif);
	font-size: 17px;
	font-weight: 400;
	line-height: 1.25;
	letter-spacing: 0;
	text-transform: none;
	color: var(--afw-meta, #6e6b7b);
	cursor: pointer;
	transition: color var(--afw-ease, 150ms ease-out);
}

.afw-p-tabnav button::after {
	position: absolute;
	content: "";
	left: auto;
	right: 0;
	bottom: 0;
	width: 0;
	height: 2px;
	background: var(--afw-mauve, #7a62a8);
	transition: width 250ms ease-in-out;
}

.afw-p-tabnav button:hover { color: var(--afw-ink, #1c1a22); }

.afw-p-tabnav button:hover::after {
	width: 100%;
	left: 0;
	right: auto;
}

.afw-p-tabnav button.is-on {
	color: var(--afw-ink, #1c1a22);
	font-weight: 500;
}

.afw-p-tabnav button.is-on::after {
	width: 100%;
	left: 0;
	right: auto;
}

.afw-p-pane {
	font-size: 16px;
	line-height: 1.75;
	color: var(--afw-ink-2, #4c4a57);
	max-width: 74ch;
}

.afw-p-pane h2,
.afw-p-pane h3,
.afw-p-pane h4 {
	font-family: var(--afw-serif, Georgia, serif);
	font-weight: 500;
	color: var(--afw-ink, #1c1a22);
	line-height: 1.22;
	margin: 28px 0 10px;
	text-transform: none;
}

.afw-p-pane h2 { font-size: 24px; }
.afw-p-pane h3 { font-size: 20px; }
.afw-p-pane h4 { font-size: 17px; }
.afw-p-pane > :first-child { margin-top: 0; }
.afw-p-pane p { margin: 0 0 14px; }
.afw-p-pane ul, .afw-p-pane ol { margin: 0 0 14px; padding-left: 20px; }
.afw-p-pane li { margin-bottom: 6px; }

.afw-p-tablewrap { overflow-x: auto; background: var(--afw-card, #fff); border-radius: var(--afw-radius, 2px); max-width: 760px; }

.afw-p-attrs { width: 100%; border-collapse: collapse; font-size: 14.5px; margin: 0; }

.afw-p-attrs th,
.afw-p-attrs td {
	text-align: left;
	padding: 12px 16px;
	border-bottom: 1px solid var(--afw-hair, #eeeaf3);
	vertical-align: top;
}

.afw-p-attrs th {
	width: 210px;
	white-space: nowrap;
	font-weight: 500;
	color: var(--afw-ink, #1c1a22);
	background: var(--afw-band, #f5f2f8);
}

.afw-p-attrs tr:last-child th,
.afw-p-attrs tr:last-child td { border-bottom: 0; }

/*  the review pane is WooCommerce's own markup, lightly tidied  */
.afw-p-pane #reviews .commentlist { list-style: none; margin: 0 0 24px; padding: 0; }
.afw-p-pane #reviews .comment_container { display: flex; gap: 14px; }
.afw-p-pane #reviews img.avatar { width: 44px; height: 44px; border-radius: 50%; }
.afw-p-pane #reviews .comment-text { flex: 1 1 auto; }
.afw-p-pane .woocommerce-noreviews {
	background: var(--afw-card, #fff);
	padding: 18px 20px;
	border-radius: var(--afw-radius, 2px);
	margin: 0 0 20px;
}
.afw-p-pane .comment-form-rating label,
.afw-p-pane .comment-form label {
	display: block;
	font-size: 13px;
	font-weight: 500;
	color: var(--afw-ink, #1c1a22);
	margin-bottom: 5px;
}
.afw-p-pane .comment-form input[type="text"],
.afw-p-pane .comment-form input[type="email"],
.afw-p-pane .comment-form textarea {
	width: 100%;
	max-width: 520px;
	font-family: var(--afw-sans, system-ui, sans-serif);
	font-size: 15px;
	color: var(--afw-ink, #1c1a22);
	background: var(--afw-card, #fff);
	border: 1px solid var(--afw-control, #8c7ba6);
	border-radius: var(--afw-radius, 2px);
	padding: 11px 13px;
}
.afw-p-pane .comment-form .submit {
	min-height: 44px;
	padding: 12px 26px;
	font-size: 15px;
	font-weight: 500;
	color: #fff;
	background: var(--afw-mauve-strong, #5f4a8a);
	border: 1px solid var(--afw-mauve-strong, #5f4a8a);
	border-radius: var(--afw-radius, 2px);
	cursor: pointer;
}

/*  ---- related ----------------------------------------------------------  */
.afw-p-related h2 {
	font-family: var(--afw-serif, Georgia, serif);
	font-size: 26px;
	font-weight: 500;
	color: var(--afw-ink, #1c1a22);
	margin: 0 0 28px;
	text-transform: none;
}

/*  ---- responsive -------------------------------------------------------  */
@media (max-width: 1024px) {
	.afw-p-top { grid-template-columns: minmax(0, 1fr); gap: 34px; margin-bottom: 46px; }
}

@media (max-width: 560px) {
	/**
	 * ⚠️ THE SAME STRIP HERE, NOT A WRAPPING GRID. This used to be `flex-wrap: wrap`, which on a
	 *   phone stacked 14 thumbs into four rows under the image and pushed the price and the
	 *   Add to cart button most of a screen further down. One scrolling row behaves the same way
	 *   at every width, which is also one fewer layout to reason about.
	 */
	.afw-p-gallery:has(.afw-p-rail) { grid-template-columns: minmax(0, 1fr); }
	.afw-p-gallery:has(.afw-p-rail) .afw-p-stage { order: 1; }
	.afw-p-gallery:has(.afw-p-rail) .afw-p-rail {
		order: 2;
		min-width: 0;
		flex-direction: row;
		gap: 10px;
		overflow-x: auto;
		overscroll-behavior-x: contain;
		scroll-snap-type: x proximity;
		scrollbar-width: thin;
		padding-bottom: 8px;
	}
	.afw-p-gallery:has(.afw-p-rail) .afw-p-rail button { flex: 0 0 auto; scroll-snap-align: start; }
	.afw-p-rail button { width: 62px; height: 62px; }
	.afw-p-cart .single_add_to_cart_button { flex: 1 1 100%; }
	.afw-p-facts { grid-template-columns: minmax(0, 1fr); gap: 2px; }
	.afw-p-facts dd { margin-bottom: 10px; }
	.afw-p-tabnav { gap: 0 24px; padding-bottom: 10px; }
	.afw-p-tabnav button { font-size: 15px; }
}

@media (prefers-reduced-motion: reduce) {
	.afw-p *, .afw-p *::before, .afw-p *::after {
		transition-duration: 0.01ms !important;
		animation-duration: 0.01ms !important;
	}
}

/* ═══════════════════════════════════════════════════════════════════════════════
   THE RELATED ROW - four across, then two. Nothing in between.
   ═══════════════════════════════════════════════════════════════════════════════
 *
 * 🔴 THE MISALIGNMENT WAS AN INHERITED BREAKPOINT. `.afw-grid` comes from `native.css`, which is
 *   written for the shop archive and steps 4 -> 3 at 1280px -> 2 at 1024px. The archive wants
 *   that; a related row of exactly four does not - on a 1280px laptop it rendered **three across
 *   with a gap where the fourth should be**, because the template always asks for four items.
 *
 * ⚠️ So the row is pinned here rather than by changing `.afw-grid`, which would re-flow the whole
 *   catalogue to fix four cards on a product page.
 */
.afw-p-related .afw-grid {
	grid-template-columns: repeat(4, minmax(0, 1fr));
	gap: 34px 22px;
	align-items: start;
}

/**
 * ⚠️ 2x2 FROM 1024px DOWN, and `1fr` is never used - a single column at any width would make the
 *   brief's "2 items per row on mobile" false on a 700px tablet. Two columns hold from 1024 all
 *   the way down, which is what was asked for.
 */
@media (max-width: 1024px) {
	.afw-p-related .afw-grid {
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: 28px 16px;
	}
}

/**
 * ⚠️ AND THE CARDS ARE MADE THE SAME SHAPE, because "misaligned" was also about their insides: a
 *   two-line product name pushed its price a line below its neighbours'. The card becomes a
 *   column with the name allowed to grow and the meta pinned under it, so the four bottom edges
 *   line up whatever the names do.
 */
.afw-p-related .afw-grid .afw-c {
	display: flex;
	flex-direction: column;
	height: 100%;
}

.afw-p-related .afw-grid .afw-c-name {
	flex: 1 0 auto;
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   THE BACK-IN-STOCK FORM — 316 products can show it and it arrived in bootstrap blue
   ═══════════════════════════════════════════════════════════════════════════════════════════

   🔴 IT WAS THE LOUDEST THING ON THE PAGE. `cwginstock` ships its own bootstrap panel: a
     saturated blue heading bar, a pale blue pill button and a hard 1px border, sitting directly
     under a mauve-and-cream boutique layout. On an out-of-stock product it read as an advert
     someone had pasted in.

   ⚠️ THE PLUGIN IS NOT TOUCHED AND ITS MARKUP IS NOT REWRITTEN. This is presentation only, so
     the subscribe path, its nonce and its ajax keep working exactly as the plugin intends.
     Restyling is reversible; forking the template is not.

   ⚠️ SELECTORS ARE SCOPED UNDER `.afw-p-cart`, so the form keeps its own look anywhere else it
     appears (the shop loop's quick view, for instance) and only the product page changes.
*/
.afw-p-cart .cwginstock-subscribe-form .panel,
.afw-p-cart .cwginstock-panel-primary {
	background: var(--afw-card, #fff);
	border: 1px solid rgba(28, 26, 34, 0.10);
	border-radius: 14px;
	box-shadow: none;
	overflow: hidden;
}

.afw-p-cart .cwginstock-panel-heading {
	background: rgba(122, 98, 168, 0.07);
	border-bottom: 1px solid rgba(28, 26, 34, 0.08);
	padding: 14px 18px;
}

.afw-p-cart .cwginstock-panel-heading h4 {
	margin: 0;
	font-size: 15px;
	font-weight: 600;
	letter-spacing: 0.01em;
	color: var(--afw-ink, #1c1a22);
	text-align: left !important;   /* the plugin sets text-align inline on the element */
}

.afw-p-cart .cwginstock-panel-body { padding: 16px 18px 18px; }

.afw-p-cart .cwginstock-subscribe-form input[type="email"] {
	width: 100%;
	height: 46px;
	padding: 0 15px;
	border: 1px solid rgba(28, 26, 34, 0.16);
	border-radius: 999px;
	background: #fff;
	font-size: 14px;
	text-align: left !important;   /* inline `text-align:center` on the input */
}

.afw-p-cart .cwginstock-subscribe-form input[type="email"]:focus {
	outline: none;
	border-color: rgba(122, 98, 168, 0.55);
	box-shadow: 0 0 0 3px rgba(122, 98, 168, 0.14);
}

/* the plugin's button is a bootstrap `.btn` and comes in several variants — all of them mauve */
.afw-p-cart .cwginstock-subscribe-form .btn,
.afw-p-cart .cwginstock-subscribe-form button,
.afw-p-cart .cwginstock-subscribe-form input[type="submit"] {
	width: 100%;
	height: 48px;
	margin-top: 10px;
	border: 0;
	border-radius: 999px;
	background: #7a62a8;
	color: #fff;
	font-size: 14px;
	font-weight: 600;
	letter-spacing: 0.01em;
	box-shadow: none;
	transition: background 150ms ease-out;
}

.afw-p-cart .cwginstock-subscribe-form .btn:hover,
.afw-p-cart .cwginstock-subscribe-form button:hover,
.afw-p-cart .cwginstock-subscribe-form input[type="submit"]:hover {
	background: #5f4a8a;
}

/* bootstrap's grid inside a 1-column card adds 15px gutters that push the field off-centre */
.afw-p-cart .cwginstock-subscribe-form .row,
.afw-p-cart .cwginstock-subscribe-form [class*="col-md-"] {
	margin: 0;
	padding: 0;
	width: 100%;
	max-width: 100%;
	float: none;
}

/* ═══ THE BACK-IN-STOCK HEADING — the plugin's bar won the first round ═══════════════════════
 *
 * 🔴 MEASURED AFTER DEPLOYING: the button and the input took the new styling, the heading bar
 *   stayed bootstrap blue. `cwginstock` ships `.cwginstock-panel-primary > .panel-heading` — a
 *   child combinator with two classes — and its stylesheet is enqueued after this file, so my
 *   single-class rule lost on both specificity and order.
 *
 * ⚠️ `!important` IS THE HONEST TOOL HERE, not a bigger selector chain. The competing rule is a
 *   plugin default I do not control and cannot reorder; matching its shape would leave the next
 *   plugin update able to win again for the same reason.
 */
.afw-p-cart .cwginstock-panel-primary > .panel-heading,
.afw-p-cart .cwginstock-subscribe-form .panel-heading,
.afw-p-cart .cwginstock-panel-heading {
	background: rgba(122, 98, 168, 0.07) !important;
	background-image: none !important;
	border-color: rgba(28, 26, 34, 0.08) !important;
	color: var(--afw-ink, #1c1a22) !important;
}

.afw-p-cart .cwginstock-panel-primary {
	border-color: rgba(28, 26, 34, 0.10) !important;
}

.afw-p-cart .cwginstock-panel-primary > .panel-heading h4,
.afw-p-cart .cwginstock-panel-heading h4 {
	color: var(--afw-ink, #1c1a22) !important;
	font-size: 14px !important;
	font-weight: 600 !important;
	letter-spacing: 0.02em !important;
	text-transform: none !important;
}

/* the plugin also colours the panel body and its own success/error notices */
.afw-p-cart .cwginstock-panel-body { background: #fff !important; }


/* ═══════════════════════════════════════════════════════════════════════════════════════════
   THE BUY BOX — quantity, variations and the call to action
   ═══════════════════════════════════════════════════════════════════════════════════════════

   🔴 WOOCOMMERCE'S FORM IS NOT REBUILT, ONLY DRESSED. Everything below is presentation on
     `woocommerce_template_single_add_to_cart()` output: `table.variations`, `.quantity input.qty`,
     `.single_add_to_cart_button`, `.woocommerce-variation-add-to-cart`, `.reset_variations`. The
     variation matching, stock rules, min/max quantity, nonce and price updates are the payment
     path and stay exactly as WooCommerce ships them.

   ⚠️ SCOPED UNDER `.afw-p-cart` throughout, so the same form keeps its own look in the mini-cart,
     the quick view and the cart page - none of which are being redesigned here.
*/
.afw-p-cart {
	margin-top: 22px;
	padding: 22px;
	border: 1px solid rgba(28, 26, 34, 0.09);
	border-radius: 18px;
	background: rgba(255, 255, 255, 0.55);
}

/**
 * ⚠️ IT MUST NOT BECOME A CARD WHEN IT HOLDS NOTHING. An out-of-stock simple product renders an
 *   empty `.afw-p-cart`, and a padded bordered box around nothing is exactly the kind of artefact
 *   these audits keep finding.
 */
.afw-p-cart:empty { display: none; }

/* ── the variation rows ───────────────────────────────────────────────────────────────────
 *
 * WooCommerce renders these as a <table>. Rather than fight it, the table becomes a block layout
 * so each label sits above its control at full width - a 2-column table with a 90px label column
 * is what made "Size" and its dropdown look pasted together.
 */
.afw-p-cart table.variations {
	width: 100%;
	margin: 0 0 18px;
	border: 0;
	border-collapse: collapse;
	background: none;
}

.afw-p-cart table.variations tbody,
.afw-p-cart table.variations tr,
.afw-p-cart table.variations th,
.afw-p-cart table.variations td {
	display: block;
	width: 100%;
	padding: 0;
	border: 0;
	background: none;
	text-align: left;
}

.afw-p-cart table.variations tr + tr { margin-top: 14px; }

.afw-p-cart table.variations th.label label {
	display: block;
	margin: 0 0 7px;
	font-family: var(--afw-sans, "Albert Sans", system-ui, sans-serif);
	font-size: 11px;
	font-weight: 600;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: rgba(28, 26, 34, 0.5);
}

/**
 * ⚠️ `appearance: none` PLUS A DRAWN CHEVRON. The native select arrow cannot be styled and it was
 *   the one control giving the buy box a stock-form look. The chevron is an inline SVG background,
 *   so there is no extra element and no script.
 */
.afw-p-cart table.variations td.value select,
.afw-p-cart .variations select {
	appearance: none;
	-webkit-appearance: none;
	width: 100%;
	height: 52px;
	padding: 0 44px 0 16px;
	border: 1px solid rgba(28, 26, 34, 0.16);
	border-radius: 12px;
	background-color: #fff;
	background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23221d19' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 16px center;
	font-family: inherit;
	font-size: 14.5px;
	color: var(--afw-ink, #1c1a22);
	cursor: pointer;
	transition: border-color 150ms ease-out, box-shadow 150ms ease-out;
}

.afw-p-cart .variations select:hover { border-color: rgba(122, 98, 168, 0.5); }

.afw-p-cart .variations select:focus-visible {
	outline: none;
	border-color: #7a62a8;
	box-shadow: 0 0 0 3px rgba(122, 98, 168, 0.15);
}

/* "Clear" is a text control, not a button competing with Add to cart */
.afw-p-cart .reset_variations {
	display: inline-block;
	margin-top: 8px;
	font-size: 12.5px;
	color: #6a5596;
	text-decoration: none;
	border-bottom: 1px solid rgba(122, 98, 168, 0.3);
}

.afw-p-cart .reset_variations:hover { border-bottom-color: #7a62a8; }

.afw-p-cart .woocommerce-variation-price { margin: 0 0 14px; }

.afw-p-cart .woocommerce-variation-price .price {
	font-family: var(--afw-serif, "Fraunces", Georgia, serif);
	font-size: 22px;
	color: var(--afw-ink, #1c1a22);
}

.afw-p-cart .woocommerce-variation-availability p { margin: 0 0 12px; font-size: 13px; }

/* ── quantity and the primary action share one line ───────────────────────────────────── */
.afw-p-cart form.cart,
.afw-p-cart .woocommerce-variation-add-to-cart {
	display: flex;
	flex-wrap: wrap;
	align-items: stretch;
	gap: 12px;
	margin: 0;
}

.afw-p-cart .quantity {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	height: 56px;
	padding: 0 6px;
	border: 1px solid rgba(28, 26, 34, 0.16);
	border-radius: 999px;
	background: #fff;
}

/**
 * ⚠️ THE SPINNERS ARE HIDDEN, NOT THE INPUT TYPE. The field stays `type=number`, so a phone still
 *   opens a numeric keypad and WooCommerce's own min/max/step validation still applies.
 */
.afw-p-cart .quantity input.qty,
.afw-p-cart .quantity input[type="number"] {
	appearance: textfield;
	-moz-appearance: textfield;
	width: 56px;
	height: 46px;
	border: 0;
	background: none;
	box-shadow: none;
	text-align: center;
	font-family: inherit;
	font-size: 15px;
	font-variant-numeric: tabular-nums;
	color: var(--afw-ink, #1c1a22);
}

.afw-p-cart .quantity input.qty::-webkit-outer-spin-button,
.afw-p-cart .quantity input.qty::-webkit-inner-spin-button {
	-webkit-appearance: none;
	margin: 0;
}

.afw-p-cart .quantity input.qty:focus-visible { outline: none; }

/* where the theme ships plus/minus controls, they are made to match the pill */
.afw-p-cart .quantity button,
.afw-p-cart .quantity .qty-btn {
	width: 38px;
	height: 38px;
	border: 0;
	border-radius: 50%;
	background: rgba(122, 98, 168, 0.08);
	color: #3f3160;
	font-size: 16px;
	line-height: 1;
	cursor: pointer;
	transition: background-color 150ms ease-out;
}

.afw-p-cart .quantity button:hover { background: rgba(122, 98, 168, 0.18); }

/* the express-checkout row sits below, on its own line, never competing for the same space */
.afw-p-cart .wc-stripe-product-checkout-container,
.afw-p-cart .wcpay-payment-request-wrapper {
	flex: 1 1 100%;
	margin-top: 4px;
}

@media (max-width: 560px) {
	.afw-p-cart { padding: 16px; }
	.afw-p-cart .single_add_to_cart_button { flex: 1 1 100%; }
}


/* ═══════════════════════════════════════════════════════════════════════════════════════════
   1 · THE STOCK ALERT — a conversion block, not a plugin panel
   ═══════════════════════════════════════════════════════════════════════════════════════════

   ⚠️ THE WORDS WERE CHANGED WHERE THEY LIVE - `cwginstocksettings` - so the plugin renders
     "Get notified when it is back", "Your email address" and "Notify me" itself. Nothing here
     rewrites text on the client.

   🔴 AND NO FALSE SCARCITY. No countdown, no invented "3 people watching", no stock counter. This
     shop sells discontinued Jellycat: the scarcity is real, it is stated plainly in the category
     copy, and manufacturing a second fake one is what makes a boutique read like a marketplace.
*/
.afw-p-cart.is-oos {
	padding: 24px;
	border: 1px solid rgba(122, 98, 168, 0.22);
	border-radius: 18px;
	background: linear-gradient(180deg, rgba(122, 98, 168, 0.055) 0%, rgba(255, 255, 255, 0.6) 58%);
}

.afw-stock-lead {
	margin: 0 0 16px;
	max-width: 46ch;
	font-size: 14px;
	line-height: 1.6;
	color: var(--afw-ink-2, #4c4a57);
}

/* the plugin's panel chrome is removed entirely inside our block - one card, not two */
.afw-p-cart.is-oos .cwginstock-subscribe-form .panel,
.afw-p-cart.is-oos .cwginstock-panel-primary {
	border: 0 !important;
	border-radius: 0 !important;
	background: none !important;
	box-shadow: none !important;
	overflow: visible !important;
}

.afw-p-cart.is-oos .cwginstock-panel-heading,
.afw-p-cart.is-oos .cwginstock-panel-primary > .panel-heading {
	padding: 0 0 12px !important;
	border: 0 !important;
	background: none !important;
}

.afw-p-cart.is-oos .cwginstock-panel-heading h4 {
	font-family: var(--afw-serif, "Fraunces", Georgia, serif) !important;
	font-size: 19px !important;
	font-weight: 500 !important;
	letter-spacing: 0 !important;
	color: var(--afw-ink, #1c1a22) !important;
}

.afw-p-cart.is-oos .cwginstock-panel-body { padding: 0 !important; background: none !important; }

.afw-p-cart.is-oos .cwginstock-subscribe-form input[type="email"] {
	height: 52px;
	border-radius: 999px;
	border-color: rgba(28, 26, 34, 0.18);
	font-size: 14.5px;
}

/**
 * ⚠️ HIGH CONTRAST MEANS CONTRAST, NOT A NEW COLOUR. The CTA is the same mauve as Add to cart at
 *   full strength on a tinted ground, which is the strongest this page gets without introducing a
 *   second accent that would then have to be explained everywhere else.
 */
.afw-p-cart.is-oos .cwginstock-subscribe-form .btn,
.afw-p-cart.is-oos .cwginstock-subscribe-form button,
.afw-p-cart.is-oos .cwginstock-subscribe-form input[type="submit"] {
	height: 52px;
	margin-top: 12px;
	background: #7a62a8;
	font-size: 14.5px;
	letter-spacing: 0.02em;
	box-shadow: 0 6px 18px rgba(122, 98, 168, 0.22);
}

.afw-p-cart.is-oos .cwginstock-subscribe-form .btn:hover {
	background: #5f4a8a;
	box-shadow: 0 8px 22px rgba(122, 98, 168, 0.28);
}

.afw-stock-note {
	display: flex;
	align-items: center;
	gap: 7px;
	margin: 14px 0 0;
	font-size: 12.5px;
	line-height: 1.45;
	color: rgba(28, 26, 34, 0.55);
}

.afw-stock-note svg { flex: 0 0 13px; opacity: 0.7; }

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   2 · THE LONG DESCRIPTION — a reading block, not a column of the page
   ═══════════════════════════════════════════════════════════════════════════════════════════

   🔴 THE MEASURE WAS THE FAULT. `.afw-p-pane` inherited the full content width, so the product
     story ran to roughly 120 characters a line. Anything past about 75 stops being read and starts
     being scanned, which is exactly the "scattered, raw" impression in the audit - the line height
     was only ever the second problem.
*/
.afw-p-pane {
	max-width: 72ch;
	font-size: 16px;
	line-height: 1.72;
	color: var(--afw-ink-2, #4c4a57);
}

.afw-p-pane > :first-child { margin-top: 0; }
.afw-p-pane > :last-child { margin-bottom: 0; }

.afw-p-pane p { margin: 0 0 16px; }
.afw-p-pane p + p { margin-top: 0; }

/**
 * ⚠️ SPACE ABOVE A HEADING, NOT BELOW IT. A heading belongs to the text under it; the gap goes
 *   above so the eye groups the section correctly. 38px above, 10px below.
 */
.afw-p-pane h2,
.afw-p-pane h3 {
	font-family: var(--afw-serif, "Fraunces", Georgia, serif);
	font-weight: 500;
	color: var(--afw-ink, #1c1a22);
	line-height: 1.25;
	margin: 38px 0 10px;
	text-wrap: balance;
}

.afw-p-pane h2 { font-size: 21px; }
.afw-p-pane h3 { font-size: 17.5px; }
.afw-p-pane h2:first-child, .afw-p-pane h3:first-child { margin-top: 0; }

.afw-p-pane ul,
.afw-p-pane ol { margin: 0 0 16px; padding-left: 20px; }
.afw-p-pane li { margin-bottom: 6px; }

.afw-p-pane strong { color: var(--afw-ink, #1c1a22); font-weight: 600; }

@media (max-width: 700px) {
	.afw-p-pane { font-size: 15.5px; line-height: 1.68; }
	.afw-p-pane h2 { font-size: 19px; margin-top: 30px; }
	.afw-p-pane h3 { font-size: 16.5px; margin-top: 26px; }
}

/* ═══════════════════════════════════════════════════════════════════════════════════════════
   3 · SECTION TRANSITIONS — one ground, and the seams removed
   ═══════════════════════════════════════════════════════════════════════════════════════════

   ⚠️ THE JUMP IS BETWEEN THREE GROUNDS: the page at #fbfafc, the related row inheriting it, and
     the newsletter band plus footer at #f5f2f8. Two creams eight values apart meet on a hard edge
     and read as a join. Rather than repaint everything one colour - which flattens the page - the
     related row hands over with a short gradient into the footer ground, so the change happens
     across 90px instead of across one pixel.
*/
.afw-p-related {
	margin-top: 64px;
	padding-bottom: 76px;
	background: linear-gradient(180deg, rgba(245, 242, 248, 0) 0%, rgba(245, 242, 248, 0.55) 62%,
	                                    rgba(245, 242, 248, 1) 100%);
}

.afw-p-related > * { position: relative; }


/* ═══════════════════════════════════════════════════════════════════════════════════════════
   THE BUY BOX, REBUILT — no wrapper, a real stepper, one clear action
   ═══════════════════════════════════════════════════════════════════════════════════════════

   🔴 THIS SUPERSEDES THE BLOCK ABOVE. The earlier version put the cart in a white bordered card;
     the owner asked for that wrapper stripped, so the rules that drew it are overridden here
     rather than deleted, because the out-of-stock card still uses the same class and must keep
     its own treatment.

   🔴 AND THE REAL OBSTACLE WAS NOT CSS AT ALL. The Customizer's Additional CSS held four rules
     with `!important` - `form.cart .quantity { margin: 10px !important }` among them - which no
     stylesheet can beat from outside. They were removed at source on 2026-09-18, together with an
     off-brand `#7E57C2` that was painting prices, badges and the Buy it now button a shade
     brighter than the header.
*/
.afw-p-cart {
	margin-top: 26px;
	padding: 0;
	border: 0;
	border-radius: 0;
	background: none;
}

/* the out-of-stock block keeps its card: it is a different job, and it converts */
.afw-p-cart.is-oos {
	margin-top: 22px;
	padding: 24px;
	border: 1px solid rgba(122, 98, 168, 0.22);
	border-radius: 18px;
	background: linear-gradient(180deg, rgba(122, 98, 168, 0.055) 0%, rgba(255, 255, 255, 0.6) 58%);
}

/* ── the row: stepper, then the action, then everything else on its own line ───────────── */
.afw-p-cart form.cart,
.afw-p-cart .woocommerce-variation-add-to-cart {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 12px;
	margin: 0;
}

/* ── the quantity stepper ─────────────────────────────────────────────────────────────── */
.afw-p-cart .quantity {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	gap: 2px;
	height: 58px;
	padding: 0 6px;
	border: 1px solid rgba(28, 26, 34, 0.14);
	border-radius: 999px;
	background: #fff;
	transition: border-color 150ms ease-out;
}

.afw-p-cart .quantity:focus-within { border-color: rgba(122, 98, 168, 0.55); }

.afw-qty-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: transparent;
	color: var(--afw-ink, #1c1a22);
	cursor: pointer;
	transition: background-color 140ms ease-out, opacity 140ms ease-out;
}

.afw-qty-btn:hover { background: rgba(122, 98, 168, 0.1); }

.afw-qty-btn:focus-visible {
	outline: 2px solid rgba(122, 98, 168, 0.6);
	outline-offset: -2px;
}

/**
 * ⚠️ A DISABLED STEPPER BUTTON STAYS VISIBLE. Hiding it makes the pill change width the moment a
 *   limit is reached, which moves the Add to cart button under the reader's cursor.
 */
.afw-qty-btn:disabled { opacity: 0.28; cursor: default; background: transparent; }

.afw-p-cart .quantity input.qty,
.afw-p-cart .quantity input[type="number"] {
	appearance: textfield;
	-moz-appearance: textfield;
	width: 44px;
	height: 40px;
	padding: 0;
	border: 0;
	background: none;
	text-align: center;
	font-family: inherit;
	font-size: 15px;
	font-weight: 600;
	font-variant-numeric: tabular-nums;
	color: var(--afw-ink, #1c1a22);
}

.afw-p-cart .quantity input.qty::-webkit-outer-spin-button,
.afw-p-cart .quantity input.qty::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.afw-p-cart .quantity input.qty:focus-visible { outline: none; }

/* the label WooCommerce prints for screen readers must not take layout space */
.afw-p-cart .quantity label.screen-reader-text { position: absolute; left: -9999px; }

/**
 * ── Buy it now: the SECOND action, and it must not compete ────────────────────────────────
 *
 * ⚠️ TWO FILLED PILLS IN THE SAME COLOUR IS NOT A CHOICE, IT IS A COIN TOSS. Buy it now keeps
 *   full width on its own line - it skips the cart, so it is the bigger commitment - but it is
 *   outlined rather than filled, so Add to cart stays the default.
 */
.afw-p-cart .em-buy-now-button {
	flex: 0 0 100%;
	width: 100%;
	height: 56px;
	margin: 2px 0 0;
	border: 1.5px solid #7a62a8;
	border-radius: 999px;
	background: transparent;
	color: #3f3160;
	font-family: var(--afw-sans, "Albert Sans", system-ui, sans-serif);
	font-size: 14px;
	font-weight: 600;
	letter-spacing: 0.03em;
	cursor: pointer;
	transition: background-color 150ms ease-out, color 150ms ease-out;
}

.afw-p-cart .em-buy-now-button:hover { background: rgba(122, 98, 168, 0.08); color: #3f3060; }

.afw-p-cart .em-buy-now-button:focus-visible {
	outline: 2px solid #7a62a8;
	outline-offset: 3px;
}

/* the express-checkout row sits below both, separated, never inline with them */
.afw-p-cart .wc-stripe-product-checkout-container,
.afw-p-cart .wcpay-payment-request-wrapper,
.afw-p-cart .ppcp-messages {
	flex: 0 0 100%;
	width: 100%;
	margin-top: 6px;
}

.afw-p-cart .wc-stripe-clear { flex: 0 0 100%; }

/* ── variation pills: the size choice, made a control rather than a menu ──────────────── */
.afw-p-swatch-row {
	display: flex;
	flex-wrap: wrap;
	gap: 8px;
	margin: 0 0 6px;
}

.afw-p-sw {
	min-height: 44px;
	padding: 0 18px;
	border: 1.5px solid rgba(28, 26, 34, 0.16);
	border-radius: 999px;
	background: #fff;
	font-family: inherit;
	font-size: 14px;
	font-variant-numeric: tabular-nums;
	color: var(--afw-ink, #1c1a22);
	cursor: pointer;
	transition: border-color 150ms ease-out, background-color 150ms ease-out,
	            color 150ms ease-out, box-shadow 150ms ease-out;
}

.afw-p-sw:hover { border-color: rgba(122, 98, 168, 0.55); }

/**
 * ⚠️ THE CHOSEN PILL CHANGES WEIGHT AS WELL AS COLOUR. A selected state carried by colour alone
 *   disappears for a colour-blind reader, and this control decides what is bought.
 */
.afw-p-sw[aria-pressed="true"] {
	border-color: #7a62a8;
	background: #7a62a8;
	color: #fff;
	font-weight: 600;
	box-shadow: 0 4px 14px rgba(122, 98, 168, 0.22);
}

.afw-p-sw:focus-visible { outline: 2px solid #7a62a8; outline-offset: 2px; }

/**
 * 2026-09-30: the pill rule above is for TEXT choices (a size). A colour swatch is a circle and
 * carries the colour itself, so the chosen one is marked with a ring, never repainted mauve
 * (that would hide the very colour it stands for). Written after the pill rule on purpose.
 */
.afw-p-cart .afw-p-sw.is-fill,
.afw-p-cart .afw-p-sw.is-photo {
	flex: 0 0 38px;
	width: 38px;
	min-width: 38px;
	height: 38px;
	min-height: 38px;
	padding: 0;
	border-radius: 50%;
	border: 2px solid #fff;
	box-shadow: 0 0 0 1px rgba(28, 26, 34, 0.16), 0 3px 8px rgba(28, 26, 34, 0.08);
	font-weight: 400;
}
.afw-p-cart .afw-p-sw.is-fill:hover,
.afw-p-cart .afw-p-sw.is-photo:hover { border-color: #fff; box-shadow: 0 0 0 2px rgba(122, 98, 168, 0.55), 0 6px 14px rgba(95, 74, 138, 0.18); transform: translateY(-1px); }
.afw-p-cart .afw-p-sw.is-fill[aria-pressed="true"],
.afw-p-cart .afw-p-sw.is-photo[aria-pressed="true"] { border-color: #fff; box-shadow: 0 0 0 2.5px #7a62a8, 0 6px 14px rgba(122, 98, 168, 0.28); font-weight: 400; }
.afw-p-cart .afw-p-sw.is-fill,
.afw-p-cart .afw-p-sw.is-photo { transition: box-shadow 150ms ease-out, transform 200ms cubic-bezier(0.22, 1, 0.36, 1); }
.afw-p-swatch-row:has(.afw-p-sw.is-fill),
.afw-p-swatch-row:has(.afw-p-sw.is-photo) { gap: 12px; padding: 4px 0 2px; }
/* the preview card rises above the pane, so the pane must not clip it */
.afw-p-info .afw-p-cart.has-swatches { overflow: visible; }

@media (max-width: 560px) {
	.afw-p-cart .single_add_to_cart_button { flex: 1 1 100%; }
	.afw-p-cart .quantity { width: 100%; justify-content: space-between; }
	.afw-p-cart .quantity input.qty { flex: 1 1 auto; }
}


/* ═══ THE QUANTITY CONTROL THE THEME ALREADY SHIPS ══════════════════════════════════════
 *
 * 🔴 Ecomus renders `<span class="ecomus-qty-button decrease">` and `.increase` inside
 *   `.quantity`. My script did not recognise spans and injected a second pair, so the live page
 *   showed FOUR targets: "- (-) 1 (+) +". The script now stands down; these rules give the
 *   theme's own controls the same treatment mine had, so the design is identical and there is
 *   only one control.
 *
 * ⚠️ THEY ARE SPANS, so `cursor`, `user-select` and a focus ring have to be stated - a span gets
 *   none of that for free the way a button does.
 */
.afw-p-cart .quantity .ecomus-qty-button,
.afw-p-cart .quantity .qty-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	border: 0;
	border-radius: 50%;
	background: transparent;
	color: var(--afw-ink, #1c1a22);
	cursor: pointer;
	user-select: none;
	transition: background-color 140ms ease-out;
}

.afw-p-cart .quantity .ecomus-qty-button:hover,
.afw-p-cart .quantity .qty-btn:hover { background: rgba(122, 98, 168, 0.1); }

.afw-p-cart .quantity .ecomus-qty-button:focus-visible {
	outline: 2px solid rgba(122, 98, 168, 0.6);
	outline-offset: -2px;
}

.afw-p-cart .quantity .ecomus-qty-button svg { display: block; }

/* the theme's own icon sizes differ between minus and plus - held to one box so they align */
.afw-p-cart .quantity .ecomus-svg-icon--minus svg,
.afw-p-cart .quantity .ecomus-svg-icon--plus svg { width: 12px; height: 12px; }

/* ═══════════════════════════════════════════════════════════════════════════════════════════════
 * THE BUY BOX AND THE TABS, 2026-09-18
 * ═══════════════════════════════════════════════════════════════════════════════════════════════
 *
 * 🔴 THIS FILE NOW HAS EXACTLY ONE OWNER FOR THE ADD-TO-CART BUTTON. It had four. Three earlier
 *   redesign passes each appended a fresh `.afw-p-cart .single_add_to_cart_button` block and left
 *   the previous one in place: a 50px square-cornered button, a 56px pill on `#7a62a8`, a 58px
 *   pill with a glow, and then this one. Every pass "worked" because the newest block was last in
 *   the file, so nobody noticed the file was growing a museum. The three dead blocks are deleted.
 *
 * 🔴 AND THE FIRST VERSION OF THIS BLOCK DID NOTHING AT ALL - MEASURED, NOT SUSPECTED. The button
 *   carries `class="single_add_to_cart_button button alt"`, and `design-tokens.css` styles
 *   `body.afw-tokens .button.alt`, which is specificity (0,3,1). My rules were (0,2,0), so the
 *   gradient, the 58px height and the sheen were all parsed, all matched, and all lost. The
 *   screenshot showed a pale flat button and I could have called it a theme bug for an hour.
 *   Every CTA rule below is therefore stated TWICE:
 *       .afw-p-info .afw-p-cart .single_add_to_cart_button          (0,3,0)  fallback
 *       .afw-p-info .afw-p-cart .single_add_to_cart_button.button    (0,4,0)  the one that wins
 *   The pair means the button is styled whether or not WooCommerce keeps emitting `.button`, and
 *   it beats the token rule without one `!important`. Raising a number until something works is
 *   not the same as knowing which selector you are competing with.
 *
 * ⚠️ NOTHING HERE TOUCHES THE FORM BEHAVIOUR. `form.cart`, `.variations`, `.quantity`,
 *   `.single_add_to_cart_button` and the Buy-it-now button belong to WooCommerce and the theme.
 *   This styles them. No script is dequeued, no markup is moved, no name is changed, so variation
 *   matching, stock rules, min/max quantity and the nonce all still work exactly as before.
 *
 * ⚠️ THE GLASS SURFACE IS A DELIBERATE REVERSAL OF THE 2026-09-16 "STRIP THE WHITE WRAPPER" PASS.
 *   What was stripped was an opaque white card that fenced the controls off from the page. What is
 *   added is a translucent tinted pane the page ground still shows through. To go back to bare
 *   controls, delete this block rather than patch it.
 *
 * ⚠️ backdrop-filter IS PROGRESSIVE ENHANCEMENT, NOT THE DESIGN. The pane carries a real
 *   background colour first; the blur is added only inside `@supports`. A browser without it gets
 *   a solid tinted pane rather than an invisible one, which is the usual glassmorphism bug.
 *
 * ⚠️ AND EVERY MOTION STANDS DOWN UNDER prefers-reduced-motion, at the end of this block.
 */

/*  ---- hierarchy: the eye should land title, then price, then button ------------------------  */

/**
 * ⚠️ THE SHORT DESCRIPTION IS NOW ONE PARAGRAPH, so its measure matters more than it did as three
 *   short lines. A single flowing paragraph at full column width is a wall; 58 characters is the
 *   line length running prose is actually read at.
 */
.afw-p-info .afw-p-short {
	max-width: 58ch;
	font-size: 16.5px;
	line-height: 1.76;
	color: var(--afw-ink-2, #4c4a57);
	margin-bottom: 22px;
}

/* the price is the second thing read, so it gets the weight the title does not spend */
.afw-p-info .afw-p-price {
	display: flex;
	align-items: baseline;
	gap: 10px;
	font-family: var(--afw-serif, Georgia, serif);
	font-size: 30px;
	font-weight: 500;
	letter-spacing: -0.025em;
	margin: 0 0 6px;
}

/*  ---- the purchase pane --------------------------------------------------------------------  */

.afw-p-info .afw-p-cart {
	position: relative;
	isolation: isolate;
	margin: 22px 0;
	padding: 22px 22px 20px;
	border: 1px solid var(--afw-line, #e4deec);
	border-radius: 14px;
	background: linear-gradient(
		168deg,
		rgba(255, 255, 255, 0.82) 0%,
		rgba(243, 240, 249, 0.72) 58%,
		rgba(235, 229, 246, 0.66) 100%
	);
	box-shadow: var(--afw-lift, 0 1px 2px rgba(28, 26, 34, 0.04), 0 8px 24px rgba(28, 26, 34, 0.05));
	overflow: hidden;
}

@supports (backdrop-filter: blur(1px)) {
	.afw-p-info .afw-p-cart {
		background: linear-gradient(
			168deg,
			rgba(255, 255, 255, 0.62) 0%,
			rgba(243, 240, 249, 0.46) 58%,
			rgba(235, 229, 246, 0.4) 100%
		);
		backdrop-filter: blur(14px) saturate(1.12);
	}
}

/**
 * THE REFLET. One hairline of light along the top edge and a soft bloom in the upper left, which
 * is where a light source above the page would actually catch a pane of glass. It is
 * `pointer-events: none` and sits behind the controls on z-index, so it can never intercept a
 * click on the button underneath it.
 */
.afw-p-info .afw-p-cart::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: -1;
	pointer-events: none;
	border-radius: inherit;
	background:
		linear-gradient(180deg, rgba(255, 255, 255, 0.9) 0, rgba(255, 255, 255, 0) 1px),
		radial-gradient(120% 70% at 12% -10%, rgba(255, 255, 255, 0.75) 0%, rgba(255, 255, 255, 0) 62%);
}

/* the out-of-stock pane keeps the glass but shifts to the clay side, so the state reads before
   the copy does */
.afw-p-info .afw-p-cart.is-oos {
	border-color: rgba(122, 59, 51, 0.16);
	background: linear-gradient(168deg, rgba(255, 255, 255, 0.84) 0%, rgba(247, 234, 231, 0.7) 100%);
}

/*  ---- the 81px of nothing between the swatches and the quantity row -----------------------  */

/**
 * 🔴 MEASURED AT 81px, AND NONE OF IT WAS MINE OR DELIBERATE. Two invisible boxes were holding
 *   it open:
 *     - WooCommerce prints `.reset_variations` ("Clear") on page load and hides it with an inline
 *       `visibility: hidden`, which reserves its full height plus its 8px margin. It becomes
 *       visible only after a variation is chosen.
 *     - `table.variations` then added its own 18px bottom margin under that.
 *   `visibility: hidden` is the trap: `display: none` would have collapsed and this would never
 *   have happened, but WooCommerce needs the element in flow to reveal it without a reflow.
 *
 * ⚠️ SO "Clear" IS TAKEN OUT OF FLOW RATHER THAN HIDDEN HARDER. It is positioned at the top right
 *   of the variations block, level with the "Size" label, which is where a reset belongs anyway.
 *   When WooCommerce reveals it, it appears there instead of pushing the button down.
 */
.afw-p-cart.has-swatches .variations { position: relative; margin-bottom: 8px; }

.afw-p-cart.has-swatches .reset_variations {
	position: absolute;
	top: 0;
	right: 0;
	margin: 0;
	font-size: 12.5px;
	line-height: 1.6;
}

.afw-p-cart .single_variation_wrap { margin-top: 0; }

/* the quantity + CTA row: one gap value, so the vertical rhythm is stated once */
.afw-p-cart .woocommerce-variation-add-to-cart,
.afw-p-cart form.cart { gap: 12px; }

/*  ---- the primary action -------------------------------------------------------------------  */

.afw-p-info .afw-p-cart .single_add_to_cart_button,
.afw-p-info .afw-p-cart .single_add_to_cart_button.button {
	position: relative;
	overflow: hidden;
	flex: 1 1 220px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-height: 58px;
	height: auto;
	padding: 17px 34px;
	font-family: var(--afw-sans, system-ui, sans-serif);
	font-size: 16px;
	font-weight: 500;
	line-height: 1.2;
	letter-spacing: 0.012em;
	text-transform: none;
	cursor: pointer;
	color: #fff;
	background: linear-gradient(176deg, #6b5498 0%, var(--afw-mauve-strong, #5f4a8a) 52%, #513e77 100%);
	border: 1px solid rgba(63, 49, 96, 0.9);
	border-radius: 10px;
	box-shadow:
		inset 0 1px 0 rgba(255, 255, 255, 0.22),
		0 1px 2px rgba(28, 26, 34, 0.08),
		0 8px 22px rgba(95, 74, 138, 0.22);
	transition:
		transform 180ms cubic-bezier(0.2, 0.7, 0.3, 1),
		box-shadow 180ms cubic-bezier(0.2, 0.7, 0.3, 1),
		background-color var(--afw-ease, 150ms ease-out);
}

/**
 * The specular sweep. A wide, low-opacity band parked off the left edge that crosses the button
 * once on hover. It animates `transform` only, so it never triggers layout, and it is the reason
 * the button reads as a lit surface rather than a flat fill.
 */
.afw-p-info .afw-p-cart .single_add_to_cart_button::after,
.afw-p-info .afw-p-cart .single_add_to_cart_button.button::after {
	content: "";
	position: absolute;
	top: 0;
	bottom: 0;
	left: -60%;
	width: 45%;
	pointer-events: none;
	background: linear-gradient(
		100deg,
		rgba(255, 255, 255, 0) 0%,
		rgba(255, 255, 255, 0.26) 50%,
		rgba(255, 255, 255, 0) 100%
	);
	transform: translateX(0);
	transition: transform 620ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.afw-p-info .afw-p-cart .single_add_to_cart_button:hover,
.afw-p-info .afw-p-cart .single_add_to_cart_button.button:hover {
	background: linear-gradient(176deg, #755ea5 0%, #644f90 52%, #56427e 100%);
	border-color: rgba(63, 49, 96, 0.95);
	color: #fff;
	transform: translateY(-1px);
	box-shadow:
		inset 0 1px 0 rgba(255, 255, 255, 0.26),
		0 2px 4px rgba(28, 26, 34, 0.09),
		var(--afw-glow, 0 10px 30px rgba(122, 98, 168, 0.16));
}

.afw-p-info .afw-p-cart .single_add_to_cart_button:hover::after,
.afw-p-info .afw-p-cart .single_add_to_cart_button.button:hover::after { transform: translateX(360%); }

/* the press has to be felt, and it has to be quick: 90ms, not the hover 180 */
.afw-p-info .afw-p-cart .single_add_to_cart_button:active,
.afw-p-info .afw-p-cart .single_add_to_cart_button.button:active {
	transform: translateY(1px) scale(0.994);
	transition-duration: 90ms;
	box-shadow: inset 0 2px 6px rgba(28, 26, 34, 0.18);
}

.afw-p-info .afw-p-cart .single_add_to_cart_button:focus-visible,
.afw-p-info .afw-p-cart .single_add_to_cart_button.button:focus-visible {
	outline: 2px solid var(--afw-mauve-strong, #5f4a8a);
	outline-offset: 3px;
}

/**
 * 🔴 THE PENDING STATE WAS UNREADABLE, AND ON A VARIABLE PRODUCT IT IS THE FIRST THING EVERY
 *   VISITOR SEES. The theme sets `button.disabled { opacity: 0.3 }`, so until a size is chosen the
 *   primary call to action was white text at 30% on mauve at 30%: present, unreadable, and giving
 *   no clue that the fix is to pick a size. Eight dragons are variable products now, so this is
 *   the default first impression on all of them.
 *
 * ⚠️ OPACITY IS RESET TO 1 AND THE STATE IS DRAWN HONESTLY INSTEAD: a dashed mauve outline on
 *   white, ink-2 text, `cursor: not-allowed`. It reads as "not yet", which is true, rather than as
 *   a broken button. The dash is the signal; fading is not.
 */
.afw-p-info .afw-p-cart .single_add_to_cart_button.disabled,
.afw-p-info .afw-p-cart .single_add_to_cart_button:disabled,
.afw-p-info .afw-p-cart .single_add_to_cart_button.button.disabled,
.afw-p-info .afw-p-cart .single_add_to_cart_button.button:disabled {
	opacity: 1;
	color: var(--afw-ink-2, #4c4a57);
	background: rgba(255, 255, 255, 0.78);
	border: 1px dashed rgba(95, 74, 138, 0.5);
	box-shadow: none;
	transform: none;
	cursor: not-allowed;
}

.afw-p-info .afw-p-cart .single_add_to_cart_button.disabled::after,
.afw-p-info .afw-p-cart .single_add_to_cart_button:disabled::after,
.afw-p-info .afw-p-cart .single_add_to_cart_button.button.disabled::after,
.afw-p-info .afw-p-cart .single_add_to_cart_button.button:disabled::after { display: none; }

/*  ---- Buy it now: the express button, made the quiet second option -------------------------  */

.afw-p-info .afw-p-cart .em-buy-now-button,
.afw-p-info .afw-p-cart .buy-now-button {
	position: relative;
	flex: 1 1 100%;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	min-height: 52px;
	margin: 0;
	padding: 14px 30px;
	font-family: var(--afw-sans, system-ui, sans-serif);
	font-size: 15.5px;
	font-weight: 500;
	letter-spacing: 0.012em;
	text-transform: none;
	color: var(--afw-mauve-strong, #5f4a8a);
	background: rgba(255, 255, 255, 0.7);
	border: 1px solid rgba(95, 74, 138, 0.38);
	border-radius: 10px;
	box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7);
	transition:
		background-color var(--afw-ease, 150ms ease-out),
		border-color var(--afw-ease, 150ms ease-out),
		transform 180ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.afw-p-info .afw-p-cart .em-buy-now-button:hover,
.afw-p-info .afw-p-cart .buy-now-button:hover {
	background: #fff;
	border-color: var(--afw-mauve-strong, #5f4a8a);
	color: var(--afw-mauve-strong, #5f4a8a);
	transform: translateY(-1px);
}

.afw-p-info .afw-p-cart .em-buy-now-button:active,
.afw-p-info .afw-p-cart .buy-now-button:active { transform: translateY(1px); transition-duration: 90ms; }

.afw-p-info .afw-p-cart .em-buy-now-button:focus-visible,
.afw-p-info .afw-p-cart .buy-now-button:focus-visible {
	outline: 2px solid var(--afw-mauve-strong, #5f4a8a);
	outline-offset: 3px;
}

/*  ---- quantity: the same glass, one size smaller -------------------------------------------  */

.afw-p-info .afw-p-cart .quantity {
	height: 58px;
	border-radius: 10px;
	border: 1px solid rgba(95, 74, 138, 0.3);
	background: rgba(255, 255, 255, 0.78);
	box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8);
	transition: border-color var(--afw-ease, 150ms ease-out), box-shadow var(--afw-ease, 150ms ease-out);
}

.afw-p-info .afw-p-cart .quantity:hover { border-color: rgba(95, 74, 138, 0.55); }

.afw-p-info .afw-p-cart .quantity:focus-within {
	border-color: var(--afw-mauve-strong, #5f4a8a);
	box-shadow: 0 0 0 3px rgba(122, 98, 168, 0.16);
}

.afw-p-info .afw-p-cart .quantity input[type="number"],
.afw-p-info .afw-p-cart .quantity .qty { height: 56px; font-size: 15.5px; }

/* the theme stepper spans, which product.js now stands down for */
.afw-p-info .afw-p-cart .quantity .ecomus-qty-button {
	transition: color var(--afw-ease, 150ms ease-out), background-color var(--afw-ease, 150ms ease-out);
}

.afw-p-info .afw-p-cart .quantity .ecomus-qty-button:hover {
	color: var(--afw-mauve-strong, #5f4a8a);
	background: rgba(122, 98, 168, 0.08);
}

/* the variation dropdown, on the pages that show it rather than swatches: it sits on the glass, so
   it needs a white ground of its own - a transparent select over a blurred pane is unreadable */
.afw-p-info .afw-p-cart .variations select,
.afw-p-info .afw-p-cart select {
	border-radius: 10px;
	background-color: rgba(255, 255, 255, 0.86);
	border-color: rgba(95, 74, 138, 0.3);
	transition: border-color var(--afw-ease, 150ms ease-out), box-shadow var(--afw-ease, 150ms ease-out);
}

.afw-p-info .afw-p-cart .variations select:hover { border-color: rgba(95, 74, 138, 0.55); }

.afw-p-info .afw-p-cart .variations select:focus-visible {
	border-color: var(--afw-mauve-strong, #5f4a8a);
	box-shadow: 0 0 0 3px rgba(122, 98, 168, 0.16);
}

/*  ---- the trust line under the pane --------------------------------------------------------  */

.afw-p-info .afw-p-cart + .afw-p-pay {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 10px 14px;
	margin: 0 0 22px;
	padding: 0;
	border: 0;
	background: none;
}

/* ═══ THE TABS ══════════════════════════════════════════════════════════════════════════════════
 *
 * ⚠️ THE GLYPH IS SIZED IN CSS, NOT BY ITS ATTRIBUTE. Learned on the header search icon, which
 *   computed to 0x17 because `width="17"` is an attribute and a theme rule sizes svg inside a
 *   button. Anything that must hold a size states it here.
 */

.afw-p-tabs .afw-p-tabnav {
	gap: 0 6px;
	padding-bottom: 0;
	border-bottom: 1px solid var(--afw-line, #e4deec);
}

.afw-p-tabs .afw-p-tabnav button {
	display: inline-flex;
	align-items: center;
	gap: 9px;
	padding: 15px 20px 16px;
	font-size: 15.5px;
	font-weight: 500;
	letter-spacing: 0.005em;
	color: var(--afw-meta, #6e6b7b);
	background: none;
	border: 0;
	border-radius: 10px 10px 0 0;
	transition: color var(--afw-ease, 150ms ease-out), background-color var(--afw-ease, 150ms ease-out);
}

.afw-p-tabs .afw-p-tabnav button svg {
	flex: 0 0 auto;
	display: block;
	width: 15px;
	height: 15px;
	stroke: currentColor;
	opacity: 0.72;
	transition: opacity var(--afw-ease, 150ms ease-out), transform 200ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.afw-p-tabs .afw-p-tabnav button:hover { background: rgba(122, 98, 168, 0.06); }
.afw-p-tabs .afw-p-tabnav button:hover svg { opacity: 1; transform: translateY(-1px); }

.afw-p-tabs .afw-p-tabnav button.is-on { color: var(--afw-ink, #1c1a22); }
.afw-p-tabs .afw-p-tabnav button.is-on svg { opacity: 1; }

.afw-p-tabs .afw-p-tabnav button:focus-visible {
	outline: 2px solid var(--afw-mauve-strong, #5f4a8a);
	outline-offset: -2px;
}

/* the underline sits on the container hairline rather than under the text */
.afw-p-tabs .afw-p-tabnav button::after {
	left: 20px;
	right: 20px;
	bottom: -1px;
	height: 2px;
}

/* the review count as a chip, so "Reviews (12)" stops competing with the label */
.afw-p-tabs .afw-p-tabnav .afw-p-tabct {
	font-style: normal;
	font-size: 11.5px;
	font-weight: 600;
	line-height: 1;
	padding: 4px 7px;
	border-radius: 999px;
	color: var(--afw-mauve-strong, #5f4a8a);
	background: rgba(122, 98, 168, 0.12);
	font-variant-numeric: tabular-nums;
}

/*  ---- the panes ----------------------------------------------------------------------------  */

.afw-p-tabs .afw-p-pane {
	max-width: 72ch;
	padding: 34px 0 8px;
	font-size: 16.5px;
	line-height: 1.78;
	color: var(--afw-ink-2, #4c4a57);
}

.afw-p-tabs .afw-p-pane h2,
.afw-p-tabs .afw-p-pane h3,
.afw-p-tabs .afw-p-pane h4 {
	font-family: var(--afw-serif, Georgia, serif);
	color: var(--afw-ink, #1c1a22);
	letter-spacing: -0.015em;
	text-wrap: balance;
}

.afw-p-tabs .afw-p-pane h2 { font-size: 25px; line-height: 1.22; margin: 34px 0 12px; }
.afw-p-tabs .afw-p-pane h3 { font-size: 20px; line-height: 1.3; margin: 28px 0 10px; }
.afw-p-tabs .afw-p-pane h4 { font-size: 17px; margin: 22px 0 8px; }
.afw-p-tabs .afw-p-pane > :first-child { margin-top: 0; }
.afw-p-tabs .afw-p-pane p { margin: 0 0 17px; }
.afw-p-tabs .afw-p-pane li { margin-bottom: 8px; }

/**
 * ⚠️ A LIFTED OPENING LINE, NOT A DROP CAP. A drop cap needs a reliable first character, and these
 *   descriptions open with anything from a product name to a heading. This lifts the first
 *   paragraph colour and size instead, which works whatever the sentence begins with.
 */
.afw-p-tabs .afw-p-pane[data-pane="desc"] > p:first-of-type {
	font-size: 17.5px;
	line-height: 1.7;
	color: var(--afw-ink, #1c1a22);
}

/*  ---- the spec list that replaced the table ------------------------------------------------  */

.afw-p-tabs .afw-p-spec {
	max-width: 620px;
	margin: 0;
	padding: 0;
	border-top: 1px solid var(--afw-line, #e4deec);
}

.afw-p-tabs .afw-p-specrow {
	display: grid;
	grid-template-columns: minmax(120px, 34%) 1fr;
	gap: 10px 20px;
	align-items: baseline;
	padding: 15px 2px;
	border-bottom: 1px solid var(--afw-line, #e4deec);
	transition: background-color var(--afw-ease, 150ms ease-out);
}

.afw-p-tabs .afw-p-specrow:hover { background: rgba(122, 98, 168, 0.04); }

.afw-p-tabs .afw-p-spec dt {
	min-width: 0;
	font-size: 11.5px;
	font-weight: 600;
	letter-spacing: var(--afw-tr-label, 0.08em);
	text-transform: uppercase;
	color: var(--afw-meta, #6e6b7b);
}

.afw-p-tabs .afw-p-spec dd {
	min-width: 0;
	margin: 0;
	display: flex;
	flex-wrap: wrap;
	gap: 6px 8px;
}

.afw-p-tabs .afw-p-specval {
	display: inline-flex;
	align-items: center;
	padding: 5px 11px;
	font-size: 14px;
	line-height: 1.35;
	color: var(--afw-ink, #1c1a22);
	background: var(--afw-mauve-tint, #f3f0f9);
	border: 1px solid var(--afw-line, #e4deec);
	border-radius: 999px;
	overflow-wrap: anywhere;
}

/*  ---- phone --------------------------------------------------------------------------------  */

@media (max-width: 767px) {
	.afw-p-info .afw-p-cart { padding: 18px 16px 16px; border-radius: 12px; }
	.afw-p-info .afw-p-price { font-size: 26px; }

	.afw-p-info .afw-p-cart .single_add_to_cart_button,
	.afw-p-info .afw-p-cart .single_add_to_cart_button.button {
		flex: 1 1 100%;
		min-height: 54px;
		font-size: 15.5px;
	}

	.afw-p-info .afw-p-cart .quantity { height: 54px; }

	/* ⚠️ AND IT KEEPS ITS OWN WIDTH. At 390 the quantity control stretched the full column, which
	   pushed the minus and plus to opposite edges with the number marooned in the middle. A
	   stepper is a small control; it should read as one object, not as a bar.
	   ⚠️ THE WIDTH IS LET, NOT SET: a fixed 132px clipped the theme's "+" button clean off the
	   right edge, because its two 40px spans plus the field do not fit in it. The field is sized
	   instead and the control takes whatever that adds up to. */
	.afw-p-info .afw-p-cart .quantity { flex: 0 0 auto; width: auto; }
	.afw-p-info .afw-p-cart .quantity input[type="number"],
	.afw-p-info .afw-p-cart .quantity .qty { width: 52px; }
	.afw-p-info .afw-p-cart .quantity input[type="number"],
	.afw-p-info .afw-p-cart .quantity .qty { height: 52px; width: 64px; }

	/* the tab rail scrolls rather than wrapping into two rows of half-labels */
	.afw-p-tabs .afw-p-tabnav {
		gap: 0 2px;
		flex-wrap: nowrap;
		overflow-x: auto;
		scrollbar-width: none;
		-webkit-overflow-scrolling: touch;
	}
	.afw-p-tabs .afw-p-tabnav::-webkit-scrollbar { display: none; }
	.afw-p-tabs .afw-p-tabnav button { flex: 0 0 auto; padding: 13px 13px 14px; font-size: 14.5px; }
	.afw-p-tabs .afw-p-tabnav button::after { left: 13px; right: 13px; }

	.afw-p-tabs .afw-p-pane { padding-top: 26px; font-size: 16px; }
	.afw-p-tabs .afw-p-pane h2 { font-size: 22px; }
	.afw-p-tabs .afw-p-pane h3 { font-size: 18.5px; }

	/* one column: a 120px label track beside a chip list is unreadable at 390 */
	.afw-p-tabs .afw-p-specrow { grid-template-columns: 1fr; gap: 7px; padding: 14px 2px; }
}

/*  ---- and all of it stands down when motion is not wanted ----------------------------------  */

@media (prefers-reduced-motion: reduce) {
	.afw-p-info .afw-p-cart .single_add_to_cart_button,
	.afw-p-info .afw-p-cart .single_add_to_cart_button.button,
	.afw-p-info .afw-p-cart .single_add_to_cart_button::after,
	.afw-p-info .afw-p-cart .single_add_to_cart_button.button::after,
	.afw-p-info .afw-p-cart .em-buy-now-button,
	.afw-p-info .afw-p-cart .buy-now-button,
	.afw-p-info .afw-p-cart .quantity,
	.afw-p-tabs .afw-p-tabnav button,
	.afw-p-tabs .afw-p-tabnav button svg,
	.afw-p-tabs .afw-p-specrow {
		transition: none;
	}

	.afw-p-info .afw-p-cart .single_add_to_cart_button:hover,
	.afw-p-info .afw-p-cart .single_add_to_cart_button.button:hover,
	.afw-p-info .afw-p-cart .single_add_to_cart_button:active,
	.afw-p-info .afw-p-cart .single_add_to_cart_button.button:active,
	.afw-p-info .afw-p-cart .em-buy-now-button:hover,
	.afw-p-info .afw-p-cart .buy-now-button:hover,
	.afw-p-info .afw-p-cart .em-buy-now-button:active,
	.afw-p-info .afw-p-cart .buy-now-button:active { transform: none; }

	.afw-p-info .afw-p-cart .single_add_to_cart_button:hover::after,
	.afw-p-info .afw-p-cart .single_add_to_cart_button.button:hover::after { transform: translateX(0); }
	.afw-p-tabs .afw-p-tabnav button:hover svg { transform: none; }
}

/*  ---- the last two gaps, both measured to the pixel --------------------------------------  */

/**
 * 🔴 42px BETWEEN "Add to cart" AND "Buy it now", AND IT WAS THE FLEX ROW GAP PAID THREE TIMES.
 *   Measured children of `.woocommerce-variation-add-to-cart`: `.wc-stripe-clear` (512x0,
 *   flex 0 0 100%) and `.wc-stripe-product-checkout-container` (512x0, flex 0 0 100%,
 *   margin-top 6px). Both are zero-height here but both are full-width flex items, so each
 *   consumed a whole flex line plus the 12px row gap: 12 + 12 + 6 + 12 = 42.
 *
 * 🔴 MY FIRST FIX WAS WRONG AND THE PAGE SOURCE SAID SO. I collapsed them with `:empty`,
 *   which changed nothing, and the reason it changed nothing is the important part: that container
 *   is NOT empty. It holds `<ul class="wc_stripe_product_payment_methods">` with real `<li>`
 *   entries. It measures zero here because Apple Pay and Google Pay do not initialise in a
 *   headless Chrome - on an iPhone those list items are a working express-checkout button. Had
 *   `:empty` matched, I would have deleted a payment path for the visitors most likely to use it,
 *   and every screenshot I take would still have looked correct.
 *
 * ⚠️ SO NOTHING IS HIDDEN. The row gap is set to 0 and the 12px is moved onto the button that
 *   actually needs it. A zero-height item now costs zero, and the moment Stripe renders a real
 *   button the container takes its own height and the spacing still holds.
 */
.afw-p-info .afw-p-cart .woocommerce-variation-add-to-cart,
.afw-p-info .afw-p-cart form.cart {
	row-gap: 0;
	column-gap: 12px;
}

.afw-p-info .afw-p-cart .em-buy-now-button,
.afw-p-info .afw-p-cart .buy-now-button { margin-top: 12px; }

.afw-p-info .afw-p-cart .wc-stripe-product-checkout-container { margin-top: 0; }

/**
 * ⚠️ 34px BETWEEN THE SWATCHES AND THE QUANTITY ROW, from `.variations tr { margin-bottom: 14px }`
 *   - a sensible value when every attribute was a labelled dropdown stacked on the next, and too
 *   much under a single row of swatches. It is reduced only inside `.has-swatches`, so a product
 *   that still renders real dropdowns keeps the original rhythm. Measured after: 24px.
 */
.afw-p-cart.has-swatches .variations tr { margin-bottom: 2px; }
.afw-p-cart.has-swatches .variations { margin-bottom: 6px; }

/* â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
 * GALLERY, FOOTER BRIDGE AND THE NEW BUY-BOX PARTS â€” 2026-09-18
 * â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
 */

/*  ---- the gallery frame --------------------------------------------------------------------  */

/**
 * âš ï¸ THE IMAGE GROWS, THE FRAME DOES NOT. The stage keeps its box and clips; only the photograph
 *   scales, and `transform-origin` is moved by the pointer so the part being looked at is the part
 *   that grows. Transform only, so there is no layout work on a pointermove.
 */
.afw-p-gallery .afw-p-stage {
	position: relative;
	overflow: hidden;
	border-radius: 16px;
	border: 1px solid var(--afw-line, #e4deec);
	background: linear-gradient(162deg, #ffffff 0%, #fdfcfe 58%, var(--afw-mauve-tint, #f3f0f9) 100%);
	box-shadow: var(--afw-lift, 0 1px 2px rgba(28, 26, 34, 0.04), 0 8px 24px rgba(28, 26, 34, 0.05));
	transition: box-shadow 260ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

/* the same single light source as the buy box, so the two panes agree about where the light is */
.afw-p-gallery .afw-p-stage::after {
	content: "";
	position: absolute;
	inset: 0;
	pointer-events: none;
	border-radius: inherit;
	background:
		linear-gradient(180deg, rgba(255, 255, 255, 0.85) 0, rgba(255, 255, 255, 0) 1px),
		radial-gradient(120% 60% at 10% -8%, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0) 60%);
}

.afw-p-gallery .afw-p-stage img {
	transition: transform 620ms cubic-bezier(0.2, 0.7, 0.3, 1);
	will-change: transform;
}

.afw-p-gallery .afw-p-stage.afw-p-lens { cursor: zoom-in; }
.afw-p-gallery .afw-p-stage.afw-p-lens:hover { box-shadow: var(--afw-lift-2, 0 2px 6px rgba(28, 26, 34, 0.05), 0 18px 44px rgba(28, 26, 34, 0.07)); }
.afw-p-gallery .afw-p-stage.afw-p-lens:hover img { transform: scale(1.5); }

/* the thumbnails: a ring rather than a border swap, so nothing shifts by a pixel when selected */
.afw-p-gallery .afw-p-rail button {
	border-radius: 12px;
	border: 1px solid var(--afw-line, #e4deec);
	box-shadow: 0 1px 2px rgba(28, 26, 34, 0.04);
	transition:
		transform 200ms cubic-bezier(0.2, 0.7, 0.3, 1),
		box-shadow 200ms cubic-bezier(0.2, 0.7, 0.3, 1),
		border-color var(--afw-ease, 150ms ease-out);
}

.afw-p-gallery .afw-p-rail button:hover {
	transform: translateY(-2px);
	box-shadow: 0 6px 16px rgba(28, 26, 34, 0.09);
}

.afw-p-gallery .afw-p-rail button.is-on {
	border-color: var(--afw-mauve-strong, #5f4a8a);
	box-shadow: 0 0 0 3px rgba(122, 98, 168, 0.18);
}

.afw-p-gallery .afw-p-rail button:focus-visible {
	outline: 2px solid var(--afw-mauve-strong, #5f4a8a);
	outline-offset: 3px;
}

/*  ---- the line total -----------------------------------------------------------------------  */

.afw-p-cart .afw-p-linetotal {
	margin: 12px 0 0;
	font-size: 14px;
	line-height: 1.5;
	color: var(--afw-ink-2, #4c4a57);
	font-variant-numeric: tabular-nums;
}

.afw-p-cart .afw-p-linetotal::before {
	content: "";
	display: inline-block;
	width: 6px;
	height: 6px;
	margin-right: 8px;
	border-radius: 50%;
	background: var(--afw-mauve, #7a62a8);
	vertical-align: middle;
}

/*  ---- the Buy-now glyph --------------------------------------------------------------------  */

.afw-p-cart .em-buy-now-button .afw-p-bn-i,
.afw-p-cart .buy-now-button .afw-p-bn-i {
	display: block;
	width: 16px;
	height: 16px;
	stroke: currentColor;
	transition: transform 220ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.afw-p-cart .em-buy-now-button:hover .afw-p-bn-i,
.afw-p-cart .buy-now-button:hover .afw-p-bn-i { transform: translateX(3px); }

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

/**
 * âš ï¸ IT IS `position: fixed` AND OUT OF THE FLOW, so it cannot change the height of anything or
 *   push the footer. It starts translated off the bottom edge and is only ever revealed by a
 *   class, so a browser without IntersectionObserver simply never sees it.
 */
.afw-p-stickybar {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 60;
	transform: translateY(102%);
	transition: transform 320ms cubic-bezier(0.2, 0.7, 0.3, 1);
	background: rgba(255, 255, 255, 0.86);
	border-top: 1px solid var(--afw-line, #e4deec);
	box-shadow: 0 -8px 28px rgba(28, 26, 34, 0.09);
}

@supports (backdrop-filter: blur(1px)) {
	.afw-p-stickybar {
		background: rgba(255, 255, 255, 0.72);
		backdrop-filter: blur(16px) saturate(1.1);
	}
}

.afw-p-stickybar.is-on { transform: translateY(0); }

.afw-p-sb-in {
	display: flex;
	align-items: center;
	gap: 16px;
	max-width: 1240px;
	margin: 0 auto;
	padding: 12px 24px;
}

.afw-p-sb-name {
	flex: 1 1 auto;
	min-width: 0;
	font-family: var(--afw-serif, Georgia, serif);
	font-size: 16px;
	color: var(--afw-ink, #1c1a22);
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.afw-p-sb-price {
	flex: 0 0 auto;
	font-size: 16px;
	font-weight: 500;
	color: var(--afw-ink, #1c1a22);
	font-variant-numeric: tabular-nums;
}

.afw-p-sb-cta {
	flex: 0 0 auto;
	min-height: 44px;
	padding: 11px 26px;
	font-family: var(--afw-sans, system-ui, sans-serif);
	font-size: 15px;
	font-weight: 500;
	color: #fff;
	background: linear-gradient(176deg, #6b5498 0%, var(--afw-mauve-strong, #5f4a8a) 52%, #513e77 100%);
	border: 1px solid rgba(63, 49, 96, 0.9);
	border-radius: 10px;
	cursor: pointer;
	box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.22), 0 6px 16px rgba(95, 74, 138, 0.2);
	transition: transform 180ms cubic-bezier(0.2, 0.7, 0.3, 1), box-shadow 180ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.afw-p-sb-cta:hover { transform: translateY(-1px); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.26), var(--afw-glow, 0 10px 30px rgba(122, 98, 168, 0.16)); }
.afw-p-sb-cta:active { transform: translateY(1px); transition-duration: 90ms; }
.afw-p-sb-cta:focus-visible { outline: 2px solid var(--afw-mauve-strong, #5f4a8a); outline-offset: 3px; }

@media (max-width: 767px) {
	.afw-p-sb-in { gap: 10px; padding: 10px 16px; }
	.afw-p-sb-name { font-size: 14.5px; }
	.afw-p-sb-price { font-size: 15px; }
	.afw-p-sb-cta { padding: 11px 18px; font-size: 14.5px; }
}

@media print { .afw-p-stickybar { display: none; } }

/*  ---- the bridge into the footer -----------------------------------------------------------  */

/**
 * ðŸ”´ THE "HARSH WHITE GAP" WAS 90px OF `main` PADDING, AND I HAD ALREADY CALLED THIS TRANSITION
 *   CORRECT. Measured: `.afw-p-related` ends at y2810 with its gradient resolving to #f5f2f8, the
 *   footer starts at y2900 with #f5f2f8 - the two colours I compared last round, which matched
 *   perfectly. Between them sat `main.afw-native-main { padding-bottom: 90px }` painting
 *   rgb(251,250,252). Comparing the two ends of a join says nothing about the join.
 *
 * âš ï¸ THE PADDING IS MOVED, NOT DELETED. It is removed from `main` only where a related row is
 *   actually present, using `:has()`, and the same space is given to that row instead - so the
 *   tinted gradient now occupies it and runs into the footer with no seam. A product page without
 *   a related row keeps the old spacing rather than having its content land on the footer edge.
 */
main.afw-native-main.afw-p:has(.afw-p-related) { padding-bottom: 0; }

.afw-p-related { padding-bottom: 96px; }

/* and the gradient finishes on the footer's exact ground, so the two surfaces meet as one */
.afw-p-related {
	background: linear-gradient(
		180deg,
		rgba(245, 242, 248, 0) 0%,
		rgba(245, 242, 248, 0.55) 46%,
		rgba(245, 242, 248, 1) 82%,
		rgba(245, 242, 248, 1) 100%
	);
}

/*  ---- reduced motion -----------------------------------------------------------------------  */

@media (prefers-reduced-motion: reduce) {
	.afw-p-gallery .afw-p-stage,
	.afw-p-gallery .afw-p-stage img,
	.afw-p-gallery .afw-p-rail button,
	.afw-p-stickybar,
	.afw-p-sb-cta,
	.afw-p-cart .em-buy-now-button .afw-p-bn-i {
		transition: none;
	}

	.afw-p-gallery .afw-p-stage.afw-p-lens:hover img { transform: none; }
	.afw-p-gallery .afw-p-rail button:hover { transform: none; }
	.afw-p-sb-cta:hover,
	.afw-p-sb-cta:active { transform: none; }
	.afw-p-cart .em-buy-now-button:hover .afw-p-bn-i { transform: none; }
}

/**
 * âš ï¸ THE CHAT BUTTON STEPS ASIDE FOR THE BUY BAR, RATHER THAN THE BUY BAR MAKING ROOM FOR IT.
 *   Measured in the render: our own contact trigger (`.afw-cd-trigger`, fixed at bottom 22px,
 *   z-index 2147483000) sat directly on top of the sticky "Add to cart" button - the one control
 *   on the bar that has a job. Padding the bar to clear it would have squeezed the button on a
 *   phone, where the space is worth most, so the smaller element moves instead.
 *
 * âš ï¸ IT IS SCOPED TO THE BAR BEING VISIBLE, so the chat button sits where it always has on every
 *   other page and on this one until the buy box scrolls away. Where `:has()` is unsupported the
 *   two simply overlap as before, which is the old behaviour rather than a new fault.
 */
body:has(.afw-p-stickybar.is-on) .afw-cd-trigger {
	bottom: 92px;
	transition: bottom 320ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

@media (max-width: 767px) {
	body:has(.afw-p-stickybar.is-on) .afw-cd-trigger { bottom: 84px; }
}

@media (prefers-reduced-motion: reduce) {
	body:has(.afw-p-stickybar.is-on) .afw-cd-trigger { transition: none; }
}

/* and the bar keeps a small gutter so the button never runs under the lifted trigger on a phone */
@media (max-width: 480px) {
	.afw-p-sb-name { display: none; }
	.afw-p-sb-in { justify-content: space-between; }
	.afw-p-sb-cta { flex: 1 1 auto; }
}

/* â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
 * THE CONVERSION LAYER AND THE MOTION LAYER â€” 2026-09-18
 * â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•â•
 *
 * ðŸ”´ WHY THE PAGE LOOKED DEAD IN A SCREENSHOT, AND WHY THAT WAS A FAIR COMPLAINT. Everything I
 *   built last round only existed on hover or on scroll: a sheen that crosses the button when the
 *   pointer enters it, a zoom that follows a cursor, a bar that arrives after the buy box leaves.
 *   A still image shows none of it, and neither does a customer who has not moved the mouse yet.
 *   The resting state was doing no work. This block gives the page something to say before it is
 *   touched: an entrance, a living CTA, a pulse on real scarcity.
 *
 * âš ï¸ THE RESTING STATE IS STILL THE READABLE ONE. Every entrance animation ENDS at the visible
 *   layout and runs on load, not on scroll - nothing is parked at opacity 0 waiting for an
 *   observer that may never fire. If the animation never runs, the page looks exactly as it does
 *   when it finishes.
 */

/*  ---- the trust row -------------------------------------------------------------------------  */

.afw-p-info .afw-p-trust {
	list-style: none;
	margin: 0 0 20px;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 9px;
}

.afw-p-info .afw-p-trust li {
	display: flex;
	align-items: flex-start;
	gap: 10px;
	margin: 0;
	font-size: 14px;
	line-height: 1.5;
	color: var(--afw-ink-2, #4c4a57);
}

.afw-p-info .afw-p-trust li svg {
	flex: 0 0 auto;
	width: 15px;
	height: 15px;
	margin-top: 2px;
	stroke: var(--afw-mauve-strong, #5f4a8a);
	opacity: 0.9;
}

.afw-p-info .afw-p-trust a {
	color: inherit;
	text-decoration: none;
	border-bottom: 1px solid rgba(122, 98, 168, 0.3);
	transition: border-color var(--afw-ease, 150ms ease-out), color var(--afw-ease, 150ms ease-out);
}

.afw-p-info .afw-p-trust a:hover {
	color: var(--afw-mauve-strong, #5f4a8a);
	border-bottom-color: var(--afw-mauve-strong, #5f4a8a);
}

.afw-p-info .afw-p-trust .afw-p-eta {
	display: block;
	font-style: normal;
	font-size: 13.5px;
	color: var(--afw-meta, #6e6b7b);
	margin-top: 2px;
}

/**
 * âš ï¸ THE SCARCITY LINE IS THE ONLY RED THING ON THE PAGE, and it only ever renders when the
 *   product really counts its stock and really holds three or fewer. Urgency that is always on is
 *   not urgency, it is decoration, and customers have learned to read it as a lie.
 */
.afw-p-info .afw-p-trust .afw-p-trust-low {
	align-items: center;
	font-weight: 500;
	color: var(--afw-clay, #7a3b33);
}

.afw-p-info .afw-p-trust .afw-p-pulse {
	position: relative;
	flex: 0 0 auto;
	width: 8px;
	height: 8px;
	margin: 0 3px;
	border-radius: 50%;
	background: var(--afw-clay, #7a3b33);
}

.afw-p-info .afw-p-trust .afw-p-pulse::after {
	content: "";
	position: absolute;
	inset: -4px;
	border-radius: 50%;
	border: 1px solid var(--afw-clay, #7a3b33);
	opacity: 0;
	animation: afw-p-ping 2.4s cubic-bezier(0.2, 0.7, 0.3, 1) infinite;
}

@keyframes afw-p-ping {
	0%   { transform: scale(0.6); opacity: 0.55; }
	70%  { transform: scale(1.5); opacity: 0; }
	100% { transform: scale(1.5); opacity: 0; }
}

/*  ---- the CTA, alive at rest ----------------------------------------------------------------  */

/**
 * A single slow pass of light every eight seconds, on the resting button. It is the same sweep the
 * hover uses, at a third of the opacity, so the control reads as a lit surface in a screenshot and
 * for a customer who has not moved the pointer yet. Hovering still fires the brighter one.
 */
.afw-p-info .afw-p-cart .single_add_to_cart_button:not(.disabled):not(:disabled)::before {
	content: "";
	position: absolute;
	top: 0;
	bottom: 0;
	left: -40%;
	width: 34%;
	pointer-events: none;
	background: linear-gradient(
		100deg,
		rgba(255, 255, 255, 0) 0%,
		rgba(255, 255, 255, 0.16) 50%,
		rgba(255, 255, 255, 0) 100%
	);
	animation: afw-p-idle-sheen 8s ease-in-out 1.2s infinite;
}

@keyframes afw-p-idle-sheen {
	0%   { transform: translateX(0); }
	14%  { transform: translateX(420%); }
	100% { transform: translateX(420%); }
}

/*  ---- the entrance ---------------------------------------------------------------------------  */

/**
 * âš ï¸ ONE SEQUENCE, ON LOAD, AND IT ENDS WHERE THE PAGE LIVES. `backwards` fill only applies the
 *   start frame during the delay, so nothing is left invisible if the animation is interrupted.
 */
.afw-p-info .afw-p-cat,
.afw-p-info .afw-p-title,
.afw-p-info .afw-p-short,
.afw-p-info .afw-p-price,
.afw-p-info .afw-p-cart,
.afw-p-info .afw-p-trust {
	animation: afw-p-rise 520ms cubic-bezier(0.2, 0.7, 0.3, 1) backwards;
}

.afw-p-info .afw-p-title { animation-delay: 40ms; }
.afw-p-info .afw-p-short { animation-delay: 90ms; }
.afw-p-info .afw-p-price { animation-delay: 140ms; }
.afw-p-info .afw-p-cart  { animation-delay: 190ms; }
.afw-p-info .afw-p-trust { animation-delay: 250ms; }

@keyframes afw-p-rise {
	from { opacity: 0; transform: translateY(10px); }
	to   { opacity: 1; transform: none; }
}

.afw-p-gallery { animation: afw-p-fade 620ms cubic-bezier(0.2, 0.7, 0.3, 1) backwards; }

@keyframes afw-p-fade {
	from { opacity: 0; transform: scale(0.99); }
	to   { opacity: 1; transform: none; }
}

/*  ---- a title with more presence -------------------------------------------------------------  */

.afw-p-info .afw-p-title {
	font-size: clamp(30px, 3.9vw, 40px);
	line-height: 1.04;
	letter-spacing: -0.026em;
	margin-bottom: 12px;
}

/*  ---- the tab underline, drawn rather than switched -----------------------------------------  */

.afw-p-tabs .afw-p-tabnav button::after {
	transform: scaleX(0);
	transform-origin: left center;
	transition: transform 260ms cubic-bezier(0.2, 0.7, 0.3, 1);
}

.afw-p-tabs .afw-p-tabnav button.is-on::after,
.afw-p-tabs .afw-p-tabnav button:hover::after { transform: scaleX(1); }

/*  ---- phone ---------------------------------------------------------------------------------  */

@media (max-width: 767px) {
	.afw-p-info .afw-p-trust { gap: 8px; margin-bottom: 18px; }
	.afw-p-info .afw-p-trust li { font-size: 13.5px; }
	.afw-p-info .afw-p-title { font-size: clamp(26px, 7vw, 32px); }
}

/*  ---- and every one of them stands down ------------------------------------------------------  */

@media (prefers-reduced-motion: reduce) {
	.afw-p-info .afw-p-cat,
	.afw-p-info .afw-p-title,
	.afw-p-info .afw-p-short,
	.afw-p-info .afw-p-price,
	.afw-p-info .afw-p-cart,
	.afw-p-info .afw-p-trust,
	.afw-p-gallery,
	.afw-p-info .afw-p-trust .afw-p-pulse::after,
	.afw-p-info .afw-p-cart .single_add_to_cart_button:not(.disabled):not(:disabled)::before {
		animation: none;
	}

	.afw-p-tabs .afw-p-tabnav button::after { transition: none; transform: scaleX(1); }
	.afw-p-tabs .afw-p-tabnav button:not(.is-on)::after { transform: scaleX(0); }
}
