/**
 * VAPNORY single-product page (Phase P2).
 *
 * Enqueued ONLY on is_product() (see functions.php) - every selector below
 * is additionally scoped under body.single-product so it cannot leak onto
 * any other page even if the conditional enqueue is ever bypassed by a
 * caching layer. Do not remove the body.single-product prefix from any
 * rule added here.
 */

/* 1. Remove the large colored title/breadcrumb banner (vapeoza_get_page_title()).
   CSS-only suppression chosen over a PHP override: the parent function is
   pluggable (function_exists guard), but it is also shared with single BLOG
   POSTS (is_single() covers both post types), so re-implementing ~130 lines
   of parent logic just to add one is_product() early-return was a larger,
   harder-to-maintain surface than hiding the one output block. The PHP
   still executes (negligible cost); nothing from it is visible. */
body.single-product .breadcrumb-noheading {
	display: none !important;
}

/* 2. Purchase area base spacing - compact, not the theme's default airy spacing.
   P3.12H-HF1: the parent theme's own rule (template.css:20841,
   .contents-detail .bwp-single-product) sets margin-bottom:120px - zeroed
   here (single-product-page scope only) so the ~32px top margin already
   set on .woocommerce-tabs.wc-tabs-wrapper (section 13 below) is the
   sole, correctly-sized gap before the tabs, instead of stacking on top
   of this large fixed margin too. */
body.single-product .bwp-single-product {
	margin-top: 24px;
	margin-bottom: 0;
}
/* P3.12H-HF1 - real root cause of the large blank area above the tabs:
   .row (bootstrap.css:497) is display:flex with no align-items override,
   so it defaults to align-items:stretch - the gallery/summary .row here
   was stretching .bwp-single-info (summary column) to match
   .bwp-single-image's (gallery column) full height. Before P3.12E/F/G/J
   trimmed the summary column down (removed SKU/highlights, compacted
   padding, replaced the native select with the compact P3.12G widget,
   removed the old 4-row service panel), the summary was naturally close
   to the gallery's height so this was never visible; now it leaves a
   large invisible gap below the P3.12F gray panel but still inside the
   stretched column, before .bwp-single-product itself even ends. Setting
   align-items:flex-start lets each column size to its own content -
   nothing here has a background painted on the outer .bwp-single-info
   column itself (the P3.12F gray panel is on the inner
   .summary.entry-summary, already sized to its own content either way),
   so this is purely removing empty space, not a visual change to either
   column. Only affects the ONE row wrapping the two columns (>) - not
   the separate nested .row used by the thumbnail-scroll strip inside
   .bwp-single-image. At mobile/tablet widths the columns already stack
   (wrap to one per line), where align-items has no visible effect
   either way, so no separate mobile override is needed. */
body.single-product .bwp-single-product > .row {
	align-items: flex-start;
}
body.single-product .bwp-single-info .summary.entry-summary {
	padding: 28px;
	background: #f7f7f7;
	border-radius: 4px;
	/* P3.12M - flex column so every block below (existing + newly
	   inserted) can be positioned purely via `order`, without touching
	   any PHP hook priority/timing. */
	display: flex;
	flex-direction: column;
}
body.single-product .summary.entry-summary > * {
	margin-bottom: 14px;
}
/* P3.12M - main title: smaller/lighter than the earlier bold mockup, per
   the approved reference. Text itself now comes from
   vapnory_p312m_render_title() (derived brand+type, e.g. "FUMOT
   DISPOSABLE VAPE") instead of the raw product title - see functions.php. */
body.single-product .product_title.entry-title {
	order: 2;
	font-size: 22px;
	line-height: 1.2;
	margin: 0 0 4px;
	font-weight: 600;
	color: #111;
}
body.single-product .vpn-brand-line {
	order: 1;
	font-size: 12px;
	font-weight: 500;
	margin: 0 0 4px;
}
body.single-product .vpn-brand-line a {
	color: #555f6c;
	text-decoration: underline;
}
body.single-product .vpn-flavor-subtitle {
	order: 3;
	font-size: 13px;
	font-weight: 400;
	color: #111;
	margin: 0 0 4px;
}
body.single-product .vpn-flavor-subtitle:empty {
	display: none;
	margin: 0;
}
body.single-product .woocommerce-product-rating {
	order: 4;
	margin-bottom: 10px;
}
/* Short "divider + uppercase label" blocks (DETAILS / ITEM PRICE). Divider
   is intentionally short (not full-width) per the approved reference. */
body.single-product .vpn-details-block {
	order: 5;
	margin: 22px 0 0;
}
body.single-product .vpn-price-block {
	order: 7;
	margin: 22px 0 0;
}
body.single-product .vpn-divider {
	display: block;
	width: 52px;
	height: 1px;
	background: #d9dde1;
	margin-bottom: 10px;
}
body.single-product .vpn-section-label {
	display: block;
	font-size: 12px;
	font-weight: 500;
	letter-spacing: 0.04em;
	color: #4f5968;
	text-transform: uppercase;
}
/* P3.12F - the live price wrapper is a <div class="price"> (inside
   .price-single), not the <p class="price"> this rule originally assumed;
   corrected to a tag-agnostic selector so the sizing actually applies.
   P3.12M: weight reduced from 700 to 500 and the struck-through regular
   price recolored to a flat gray (was opacity-based) per the approved
   reference; sale-price color/size otherwise unchanged. */
body.single-product .discount-price {
	order: 8;
}
body.single-product .bwp-single-info .price {
	font-size: 22px;
	font-weight: 500;
	color: #111;
	margin: 0 0 12px;
}
body.single-product .bwp-single-info .price del {
	font-size: 17px;
	font-weight: 400;
	color: #a8a8a8;
	opacity: 1;
}
/* Same sizing again, once a flavor is selected: the variation-swapped
   price (.single_variation_wrap .price, populated by WooCommerce's own
   variation JS) is governed by a separate, more specific parent-theme
   rule (30px flat for both sale and regular price) - matched here so the
   price doesn't jump size on selection. */
body.single-product .product-type-variable .variations_form.cart .single_variation_wrap .price {
	font-size: 22px;
	font-weight: 500;
	color: #111;
}
body.single-product .product-type-variable .variations_form.cart .single_variation_wrap .price del {
	font-size: 17px;
	font-weight: 400;
	color: #a8a8a8;
	opacity: 1;
}
body.single-product .summary .description {
	order: 6;
	color: #555f6c;
	font-size: 13.5px;
	font-weight: 400;
	line-height: 1.45;
}
body.single-product form.variations_form.cart {
	order: 9;
}

/* 3. Quantity + Add to Cart row - desktop side-by-side, dominant CTA. */
body.single-product form.cart {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 12px;
}
body.single-product form.cart .quantity {
	margin: 0;
}
body.single-product .single_add_to_cart_button {
	border-radius: 4px;
	font-weight: 600;
	letter-spacing: 0.03em;
}
/* P3.12F - dark neutral Add to Cart per the approved visual direction,
   replacing the parent theme's cyan (var(--button-color,#0ad0d7)). The
   parent rule chains 4 classes with 0 elements; adding the body element
   here keeps equal class-specificity but wins on the element tiebreak -
   confirmed correct, no !important needed. Buy Now is intentionally left
   untouched - it already matches the target (white/light bg, thin dark
   border) with no changes required. */
body.single-product .bwp-single-info .single_add_to_cart_button.button {
	background: #171717;
}
body.single-product .bwp-single-info .single_add_to_cart_button.button:hover:not(.disabled) {
	background: #2e2e2e;
	border-color: #2e2e2e;
}

/* P3.12F - Flavor selector visual polish only (native <select>, no JS/
   markup change). The parent theme already supplies the down-arrow via a
   base background-image not touched here; only border/height/colors are
   adjusted. The .selected-term-name span (WooCommerce's own bold
   "Cool Mint"-style echo next to the "Flavor" label) is hidden since the
   select itself already shows the chosen value once picked - this avoids
   the label+value reading as one large combined heading, per the approved
   direction, without touching variation JS/data. */
body.single-product .product-type-variable .variations_form.cart table tr td select {
	height: 48px;
	line-height: 46px;
	border: 1px solid #ddd;
	border-radius: 4px;
	background-color: #fff;
	font-size: 14px;
	padding: 0 40px 0 14px;
}
body.single-product .product-type-variable .variations_form.cart table tr td .selected-term-name {
	display: none;
}
body.single-product .product-type-variable .variations_form.cart table tr td label {
	font-weight: 600;
	color: #1a1a1a;
}

/* 4. Wishlist / Compare / Share row - compact icon row, not oversized buttons. */
body.single-product .woosw-btn,
body.single-product .woosc-btn,
body.single-product .product-share {
	font-size: 13px;
}
body.single-product .woocommerce-after-add-to-cart-button-extra,
body.single-product .product-share {
	margin-top: 10px;
}

/* 5. P3.12J - real free-shipping progress (replaces the old static 4-row
   service panel, removed in functions.php - P3.12I executed as part of
   this phase since it had not yet run). Sits between the Flavor selector
   and Quantity/Add to Cart via the native woocommerce_before_add_to_cart_button
   hook - no template override needed. Compact/restrained per the approved
   direction: no card background of its own (already inside the P3.12F
   gray summary panel), light-gray track, minimal cyan fill. */
body.single-product .vpn-shipping-progress {
	margin: 4px 0 16px;
}
body.single-product .vpn-shipping-progress-msg {
	font-size: 13px;
	color: #333;
	margin-bottom: 6px;
}
body.single-product .vpn-shipping-progress-msg.is-unlocked {
	color: #1a1a1a;
	font-weight: 600;
}
body.single-product .vpn-shipping-progress-track {
	width: 100%;
	height: 6px;
	background: #e5e5e5;
	border-radius: 3px;
	overflow: hidden;
}
body.single-product .vpn-shipping-progress-fill {
	height: 100%;
	background: #0ad0d7;
	border-radius: 3px;
}

/* 6. Category / Brand meta line - compact, subdued. */
body.single-product .product_meta {
	font-size: 13px;
	color: #666;
	border-top: 1px solid #eee;
	padding-top: 10px;
	margin-top: 4px;
}
body.single-product .product_meta > span {
	display: block;
	margin-bottom: 4px;
}
body.single-product .product_meta a {
	color: #1a1a1a;
}

/* 7. Product tabs - horizontal, black active state, subtle divider.
   P3.12H: top spacing moved from ul.tabs to the new outer
   .woocommerce-tabs.wc-tabs-wrapper light-gray layer (section 13 below),
   so it is removed here to avoid doubling the gap. */
body.single-product .woocommerce-tabs ul.tabs {
	display: flex;
	gap: 28px;
	border-bottom: 1px solid #e5e5e5;
	margin: 0;
	padding: 0;
	list-style: none;
}
body.single-product .woocommerce-tabs ul.tabs li {
	margin: 0;
	padding: 0;
}
body.single-product .woocommerce-tabs ul.tabs li a {
	display: inline-block;
	padding: 12px 2px;
	font-size: 13px;
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: #888;
	border-bottom: 2px solid transparent;
}
body.single-product .woocommerce-tabs ul.tabs li.active a,
body.single-product .woocommerce-tabs ul.tabs li a:hover {
	color: #111;
	border-bottom-color: #111;
}
body.single-product .woocommerce-tabs .panel {
	padding: 20px 0 26px;
	font-size: 14.5px;
	color: #333;
	line-height: 1.7;
}
/* P3.12H-HF3 - the browser's own default top margin on whatever element
   happens to be first inside a panel (<h3>Product Overview</h3> for
   Description, <table> for Additional Information, a <div> for Reviews)
   was stacking on top of the panel's own padding-top above, making the
   gap before the heading larger than the padding value alone suggested.
   Reset once, generically, for all three panels - no heading/table/
   content text or tag is changed. */
body.single-product .woocommerce-Tabs-panel > *:first-child {
	margin-top: 0;
}

/* 8. Dynamic breadcrumb (vapnory_p2_bottom_breadcrumb() in functions.php -
   name kept from its original P2 bottom placement to minimize diff).
   P3.12C: relocated to the top of the product content, directly below the
   header - the top-divider border made sense only when it separated the
   breadcrumb from Reviews/tabs above it, so it is dropped here; spacing
   is now a small top gap from the header and a small bottom gap before
   the gallery (.bwp-single-product's own existing 24px top margin already
   covers most of that bottom gap). */
body.single-product .vpn-bottom-breadcrumb {
	margin: 14px 0 0;
	padding: 0 0 6px;
	font-size: 12.5px;
	color: #8a8a8a;
}
/* P3.12M-HF1 - mobile was missing the same horizontal inset the P3.12L
   desktop fix already applies. Reuses the exact same 15px value already
   verified as the site's real mobile content inset - it's
   .bwp-single-product's own dedicated padding-left/right:15px
   (template.css:20841, not gated to any breakpoint) and is the same
   value P3.12H-HF2 already used to align the lower tabs area on mobile -
   so the breadcrumb now lines up with both. Left/right only added (the
   existing top margin/bottom padding above are untouched), so this can't
   stack into anything larger than the intended 15px. */
@media (max-width: 767px) {
	body.single-product .vpn-bottom-breadcrumb {
		padding-left: 15px;
		padding-right: 15px;
		box-sizing: border-box;
	}
}
/* P3.12L - the breadcrumb is a SIBLING of .bwp-single-product (rendered
   on woocommerce_before_single_product, before that wrapper even starts -
   see P3.12C), not a descendant of it, so it never inherited
   .bwp-single-product's own max-width/auto-margins - including the
   narrower 1300px centered container P3.12K-HF2 introduced. Both still
   share the same ambient parent (the page's own content column), so on
   desktop the breadcrumb stayed flush at that column's ~15px padding
   while the now-centered 1300px product block sits well to the right of
   it on wide viewports - exactly the reported "hugs the left edge"
   mismatch. Matching the same 1300px/auto-margin here (desktop only)
   aligns it with that same container; mobile is untouched since
   .bwp-single-product carries no max-width override below 991px, so the
   two already shared the same ~15px inset there. */
@media (min-width: 991px) {
	body.single-product .vpn-bottom-breadcrumb {
		max-width: 1300px;
		margin-left: auto;
		margin-right: auto;
		padding-left: 15px;
		padding-right: 15px;
		box-sizing: border-box;
	}
}
/* P3.12L-HF2 - darker, slightly heavier text per owner follow-up
   (P3.12L-HF1's #777/400-weight read as too light); separator kept
   lighter than the text and at its original 400 weight. */
body.single-product .vpn-bottom-breadcrumb .breadcrumb,
body.single-product .vpn-bottom-breadcrumb .breadcrumb a {
	font-size: 12px;
	font-weight: 600;
	color: #5f6670;
}
body.single-product .vpn-bottom-breadcrumb .breadcrumb .delimiter {
	font-size: 12px;
	font-weight: 400;
	color: #b9bec5;
	margin: 0 6px;
}
body.single-product .vpn-bottom-breadcrumb .breadcrumb a {
	text-decoration: none;
}
body.single-product .vpn-bottom-breadcrumb .breadcrumb a:hover {
	color: #222;
}

/* 9. P2.2 - disable the parent theme's desktop sticky purchase column.
   Root cause, confirmed by reading vapeoza/css/template.css:21069 inside
   @media (min-width: 991px):
     .contents-detail .bwp-single-product.one_column .bwp-single-info>div:not(.sticky-product.sticky){
       position: sticky; top: 5rem;
     }
   The active gallery layout on this site is "one_column" (confirmed via
   the live data-product_layout_thumb="one_column" attribute), so this
   rule matches .bwp-single-info > div (the .summary.entry-summary
   wrapper) and makes the whole purchase column independently sticky
   while the gallery scrolls normally - exactly the reported "separate
   visual layers" behavior. !important is used because matching/beating
   this selector's specificity by chaining classes alone was less robust
   than a single explicit override; scope (body.single-product plus the
   parent's own .one_column class) already guarantees this cannot affect
   any other page or any other gallery layout variant, so !important adds
   certainty without widening the blast radius. */
body.single-product .bwp-single-product.one_column .bwp-single-info > div {
	position: static !important;
	top: auto !important;
}

/* P3.12K - desktop gallery/summary vertical alignment.
   Investigated first: the outer .row wrapping .bwp-single-image and
   .bwp-single-info is display:flex with no align-items override in the
   parent theme (bootstrap.css:497, template.css - confirmed via full-file
   search: no align-items rule scoped to .bwp-single-product/.bwp-single-
   image/.bwp-single-info anywhere), so it defaults to align-items:stretch
   - already corrected to flex-start in P3.12H-HF1, so both columns
   already start at the same top line. No further stretch/centering rule
   was found on any inner wrapper (.row .col-md-10, .container-image-
   single, .scroll-image, .image-additional, or the Swiper library's own
   base CSS, which defaults .swiper-wrapper to align-items:flex-start,
   not center).
   What IS confirmed, mathematically, from the live markup: the main
   image is a native 1600x1600 SQUARE file with no CSS max-width, and the
   parent theme sets it to width:100% of its column
   (template.css:23172, .scroll-image .image-additional img). Since
   .bwp-single-image is a col-lg-7 (up to ~840px wide inside the theme's
   1470px max content width), the image's rendered HEIGHT grows
   unbounded and 1:1 with viewport width, while the right summary column
   (title/price/description/Flavor selector/shipping progress/Quantity+
   Add to Cart, all comfortably compact since P3.12F/G/J) does not grow
   with viewport width at all. With both columns already top-aligned,
   this is exactly what produces "the mismatch becomes more obvious" as
   the browser widens: the gallery column's total height increasingly
   outgrows the summary's, an ever-taller image next to a fixed-height
   summary. Capping the image's display width (not its source file -
   still the same q96 1600px original, same srcset, same subsizes)
   bounds this growth so the two columns stay visually proportionate at
   any width, without touching alignment/positioning directly. */
@media (min-width: 991px) {
	/* P3.12K-HF2 - the 720px image cap alone left the columns still
	   spread across the parent theme's full 1470px max-width
	   (template.css:20841, .contents-detail .bwp-single-product), with
	   Bootstrap's default col-lg-7/col-lg-5 (58.3%/41.7%) and its default
	   ~30px gutter (15px+15px column padding) - on a wide/fullscreen
	   viewport this reads as a huge, loosely-related block rather than
	   one coherent product unit. Narrowing the block itself, adjusting
	   the column split to ~55/45, and widening the gutter to a
	   deliberate, controlled gap addresses the actual container/column
	   widths directly, instead of further patching image sizing alone. */
	body.single-product .bwp-single-product {
		max-width: 1300px;
	}
	body.single-product .bwp-single-image {
		flex: 0 0 55%;
		max-width: 55%;
		padding-right: 25px;
	}
	body.single-product .bwp-single-info {
		flex: 0 0 45%;
		max-width: 45%;
		padding-left: 25px;
	}
	/* (2026-10-04: the 580px image cap that stood here is replaced by the unified picture stage at the end of this file) */
	/* P3.12K-HF1 - P3.12K's width cap alone did not close the gap; two
	   flex containers in the gallery chain were still unaddressed:
	   (1) the INNER .row (thumbnails col-md-2 + main-image col-md-10,
	       inside .bwp-single-image) is a second, separate Bootstrap .row
	       - only the OUTER .bwp-single-product > .row was corrected in
	       P3.12H-HF1; this inner one still defaults to align-items:stretch
	       same as the outer one did.
	   (2) in one_column mode the parent theme explicitly sets
	       flex-direction:column on .bwp-swiper-slider .swiper-wrapper
	       (template.css ~21041) - in a COLUMN-direction flex container the
	       MAIN axis is vertical, so it is justify-content (not
	       align-items) that controls vertical position/distribution of
	       the single slide, and neither the parent theme nor Swiper's own
	       base CSS sets justify-content there, leaving it at its default
	       (flex-start is default per spec, but stretch/centering behavior
	       has been observed live per the owner's follow-up, so both
	       properties are pinned explicitly here rather than relying on
	       the default). Matches the parent theme's own selector chain for
	       this exact element so specificity reliably wins. */
	body.single-product .bwp-single-image .row {
		align-items: flex-start;
	}
}

/* 10. P2.2 - let vertical page scrolling win over the product gallery's
   Swiper instance on touch devices.
   Root cause, confirmed by reading the live page's actually-enqueued
   Swiper stylesheet (elementor/assets/lib/swiper/v8/css/swiper.min.css -
   NOT the unused copy under the parent theme's own assets/vendors/swiper/
   folder, which is never loaded on this site) plus the gallery's init
   code in vapeoza/assets/js/functions.js (~line 905-1044, initMainSwiper/
   initThumbsSwiper): Swiper v8 normally self-applies
   touch-action:pan-y via its own conditional "swiper-pointer-events"
   class, but nothing in the gallery markup/config here pins that
   explicitly, so it is not guaranteed. Setting touch-action directly and
   explicitly on the two swiper containers the gallery actually uses
   (.bwp-swiper-slider = main image, .bwp-swiper-thumbs = thumbnails) is
   the deterministic, spec-guaranteed fix requested: per the CSS Touch
   Events spec, an element restricted to touch-action:pan-y cannot have
   its vertical pan cancelled by any JS touchmove handler (including
   Swiper's own), while horizontal swipe-to-change-image is left
   available to Swiper's JS and keeps working. Scoped to
   body.single-product .bwp-single-product only - no other Swiper
   instance sitewide (Best Sellers, homepage sliders, etc.) is touched. */
body.single-product .bwp-single-product .bwp-swiper-slider,
body.single-product .bwp-single-product .bwp-swiper-thumbs {
	touch-action: pan-y pinch-zoom;
}

/* 11. Mobile - comfortable tap targets, no horizontal overflow, compact stack. */
@media (max-width: 767px) {
	/* PHASE P2.25: restored - P2.24 real-device result was a major
	   improvement with all of Group 1 disabled, narrowing this to a
	   smaller cosmetic subset confirmed low-risk (font-size/spacing
	   only, no flex/width/containment). Restored: */
	body.single-product .product_title.entry-title {
		font-size: 19px;
		margin-top: 14px;
	}
	/* P3.12F - fixes the same p.price/.price tag mismatch noted above for
	   the mobile-only size (the live element is a <div>, this selector
	   never matched); del kept proportionally smaller. */
	body.single-product .bwp-single-info .price,
	body.single-product .product-type-variable .variations_form.cart .single_variation_wrap .price {
		font-size: 20px;
	}
	body.single-product .bwp-single-info .price del,
	body.single-product .product-type-variable .variations_form.cart .single_variation_wrap .price del {
		font-size: 17px;
	}
	body.single-product .vpn-service-panel {
		margin-top: 14px;
	}
	/* P3.12F - light-gray summary panel: mobile padding target (18-20px)
	   is smaller than desktop (28px, set unscoped above). */
	body.single-product .bwp-single-info .summary.entry-summary {
		padding: 20px 18px;
	}

	/* PHASE P2.27: purchase-row layout restored following the P2.26
	   real-device result (mobile main-gallery swipe disable resolved
	   the gesture-competition symptom). Rebuilt from the P2.24/P2.25
	   disabled block with ONE deliberate omission: the previous
	   `overflow-x: hidden` on .bwp-single-product is NOT restored -
	   P2.23/P2.24 identified this file as the root cause and disabling
	   this exact overflow-establishing rule on a common product-page
	   ancestor was part of what produced the major improvement, so it
	   is treated as the prime remaining suspect and left out unless
	   independently proven necessary. Only `margin-top` is kept from
	   that rule. `form.cart { flex-wrap: wrap }` is not re-added here
	   either - the base (non-mobile) rule earlier in this file already
	   sets it unconditionally, so re-stating it on mobile was always
	   redundant. */
	body.single-product .bwp-single-product {
		margin-top: 12px;
	}
	body.single-product form.cart .quantity-button {
		display: flex;
		align-items: stretch;
		gap: 8px;
		flex: 1 1 100%;
		min-width: 0;
	}
	body.single-product form.cart .quantity-button .quantity {
		flex: 0 0 auto;
	}
	body.single-product form.cart .quantity-button .single_add_to_cart_button {
		flex: 1 1 auto;
		min-width: 0;
	}
	body.single-product form.cart .quantity input {
		min-width: 46px;
	}
	body.single-product .quantity .plus,
	body.single-product .quantity .minus {
		min-width: 38px;
		min-height: 38px;
	}
	body.single-product .single_add_to_cart_button {
		min-height: 44px;
	}
	body.single-product form.cart .quick-buy {
		flex: 1 1 100%;
		min-width: 0;
		min-height: 44px;
	}
	body.single-product form.cart .woosw-wishlist,
	body.single-product form.cart .woosc-compare,
	body.single-product form.cart .share-content {
		flex: 1 1 30%;
		min-width: 0;
	}
	body.single-product form.cart .woosw-btn,
	body.single-product form.cart .woosc-btn {
		width: 100%;
		min-height: 40px;
		font-size: 12px;
		padding-left: 6px;
		padding-right: 6px;
		white-space: normal;
		line-height: 1.25;
	}

	/* Products Highlights - keep single-column stack and all 6 items,
	   just tighten the vertical rhythm. Desktop values (defined in the
	   parent theme, untouched here) are 47px/48px outer padding, 120px
	   bottom margin, 15px grid row-gap, 48px per-box line-height - all
	   confirmed by reading vapeoza/css/template.css before writing this,
	   so these mobile-only numbers are a real reduction, not a guess. */
	body.single-product .vapeoza-highlights-fullwidth {
		padding: 22px 0 24px;
		margin-bottom: 36px;
	}
	body.single-product .vapeoza-highlights-fullwidth .highlights-intro {
		margin-bottom: 10px;
	}
	body.single-product .vapeoza-highlights-fullwidth .highlights-intro h2 {
		font-size: 18px;
		line-height: 1.3;
		margin-bottom: 4px;
	}
	body.single-product .vapeoza-highlights-fullwidth .highlights-intro p {
		font-size: 13px;
		line-height: 1.4;
	}
	body.single-product .vapeoza-highlights-fullwidth .highlights-grid {
		grid-row-gap: 8px;
	}
	body.single-product .vapeoza-highlights-fullwidth .highlight-box {
		line-height: 1.3;
		padding: 10px 12px;
		font-size: 13.5px;
	}
	body.single-product .woocommerce-tabs ul.tabs {
		gap: 18px;
		overflow-x: auto;
		flex-wrap: nowrap;
		-webkit-overflow-scrolling: touch;
	}
	body.single-product .woocommerce-tabs ul.tabs li a {
		white-space: nowrap;
	}
}

/* 12. P3.12G - Enhanced Flavor / Flavor Combination selector. Presentation
   only (see js/enhanced-flavor-selector.js); the native table.variations
   is hidden ONLY via the .vpn-fsel-native-hidden class the script adds
   after a successful build, so if the script never runs (or errors) the
   native select stays fully visible per the P3.12F/summary panel style. */
body.single-product .content-variations.vpn-fsel-native-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	overflow: hidden;
	clip: rect(0 0 0 0);
	white-space: nowrap;
}
/* P3.12G-HF1 - .vpn-fsel is a direct child of form.cart, which this same
   stylesheet already makes a flex container (display:flex; flex-wrap:wrap;
   for the Quantity + Add to Cart row further down the same form). Without
   an explicit flex-basis, a flex item defaults to shrink-wrapping its
   content's natural width - this, not the trigger's own width:100% (which
   was already correct relative to .vpn-fsel), was the real cause of the
   trigger visibly changing width with each flavor name's text length.
   flex: 0 0 100% pins it to the full row width regardless of content. */
/* P3.12G-HF2 - constrain the desktop width to a fixed, comfortable size
   instead of the full summary column; left-aligned (no auto margins), not
   centered. The open .vpn-fsel-panel uses left:0/right:0 relative to this
   same element (position:relative), so it automatically matches this
   width exactly with no separate rule needed. Mobile reverts to 100% in
   the existing P3.12G mobile block below - the HF1 fix (flex-basis
   pinned instead of shrink-wrapping to text) is unchanged/still in
   effect at both widths, just with a smaller fixed target on desktop. */
body.single-product .vpn-fsel {
	position: relative;
	width: 100%;
	max-width: 420px;
	flex-basis: 420px;
	box-sizing: border-box;
	margin: 0 0 15px;
}
/* P3.12M - FLAVOR / FLAVOR COMBINATION label styled to match the new
   DETAILS/ITEM PRICE section labels (presentation only - the text itself
   is still the real native attribute label, just uppercased visually). */
body.single-product .vpn-fsel-field-label {
	font-size: 12px;
	font-weight: 500;
	letter-spacing: 0.04em;
	color: #4f5968;
	text-transform: uppercase;
	margin-bottom: 6px;
}
body.single-product .vpn-fsel-trigger {
	display: flex;
	align-items: center;
	gap: 10px;
	width: 100%;
	max-width: 100%;
	box-sizing: border-box;
	height: 48px;
	padding: 0 14px;
	background: #fff;
	border: 1px solid #ddd;
	border-radius: 4px;
	cursor: pointer;
	text-align: left;
	font-family: inherit;
}
body.single-product .vpn-fsel-trigger:hover {
	border-color: #bbb;
}
body.single-product .vpn-fsel-trigger:focus-visible {
	outline: 2px solid #171717;
	outline-offset: 1px;
}
body.single-product .vpn-fsel-trigger-thumb {
	flex: 0 0 32px;
	flex-shrink: 0;
	width: 32px;
	height: 32px;
	border-radius: 3px;
	overflow: hidden;
	background: #f2f2f2;
}
body.single-product .vpn-fsel-trigger-thumb img {
	width: 100%;
	height: 100%;
	object-fit: contain; /* 2026-09-30: whole device shown (tall JNR images); square FUMOT images look the same */
	display: block;
}
body.single-product .vpn-fsel-trigger-label {
	flex: 1 1 auto;
	min-width: 0;
	font-size: 14.5px;
	font-weight: 600;
	color: #1a1a1a;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
body.single-product .vpn-fsel-trigger-label.is-placeholder {
	font-weight: 400;
	color: #888;
}
body.single-product .vpn-fsel-arrow {
	flex: 0 0 auto;
	flex-shrink: 0;
	display: flex;
	color: #666;
	transition: transform 0.15s ease;
}
body.single-product .vpn-fsel.is-open .vpn-fsel-arrow {
	transform: rotate(180deg);
}
body.single-product .vpn-fsel-panel {
	display: none;
	position: absolute;
	left: 0;
	right: 0;
	top: calc(100% + 4px);
	z-index: 20;
	background: #fff;
	border: 1px solid #ddd;
	border-radius: 4px;
	box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
	max-height: 420px;
	overflow-y: auto;
	-webkit-overflow-scrolling: touch;
}
body.single-product .vpn-fsel.is-open .vpn-fsel-panel {
	display: block;
}
body.single-product .vpn-fsel-row {
	display: flex;
	align-items: center;
	gap: 10px;
	padding: 8px 12px;
	min-height: 56px;
	cursor: pointer;
	border-bottom: 1px solid #f0f0f0;
}
body.single-product .vpn-fsel-row:last-child {
	border-bottom: none;
}
body.single-product .vpn-fsel-row:hover {
	background: #f5f7f8;
}
/* P3.12G-HF2 - restrained cyan-gray highlight so the current selection
   reads clearly against the plain-white rows; a subtle left border (using
   the site's existing cyan accent) reinforces it without a heavy shadow
   or a bright blue fill. Wins over the plain :hover background via the
   .is-selected class regardless of hover state. */
body.single-product .vpn-fsel-row.is-selected,
body.single-product .vpn-fsel-row.is-selected:hover {
	background: #eaf6f8;
	border-left: 2px solid #0ad0d7;
	padding-left: 10px;
}
body.single-product .vpn-fsel-row.is-disabled {
	cursor: not-allowed;
	opacity: 0.5;
}
body.single-product .vpn-fsel-row.is-disabled:hover {
	background: transparent;
}
body.single-product .vpn-fsel-row-thumb {
	flex: 0 0 40px;
	width: 40px;
	height: 40px;
	border-radius: 3px;
	overflow: hidden;
	background: #f2f2f2;
}
body.single-product .vpn-fsel-row-thumb img {
	width: 100%;
	height: 100%;
	object-fit: contain; /* 2026-09-30: whole device shown (tall JNR images); square FUMOT images look the same */
	display: block;
}
body.single-product .vpn-fsel-row-main {
	flex: 1 1 auto;
	min-width: 0;
	display: flex;
	flex-direction: column;
	gap: 2px;
}
body.single-product .vpn-fsel-row-name {
	font-size: 14.5px;
	font-weight: 600;
	color: #1a1a1a;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}
body.single-product .vpn-fsel-row-meta {
	display: flex;
	align-items: center;
	gap: 10px;
}
body.single-product .vpn-fsel-row-price {
	font-size: 13px;
	color: #555;
}
body.single-product .vpn-fsel-row-price .woocommerce-Price-amount {
	font-size: inherit;
	color: inherit;
}
body.single-product .vpn-fsel-row-stock {
	font-size: 12.5px;
}
body.single-product .vpn-fsel-row-stock.is-in-stock {
	color: #2e8b52;
}
body.single-product .vpn-fsel-row-stock.is-out-of-stock {
	color: #999;
}

@media (max-width: 767px) {
	/* P3.12G-HF2 - revert to full-width inside the gray summary panel on
	   mobile; the desktop 420px cap above would otherwise leave unused
	   space or clip against the narrower column. */
	body.single-product .vpn-fsel {
		max-width: none;
		flex-basis: 100%;
	}
	body.single-product .vpn-fsel-panel {
		max-height: 340px;
	}
	body.single-product .vpn-fsel-row {
		min-height: 64px;
		padding: 10px 12px;
	}
	body.single-product .vpn-fsel-trigger {
		height: 50px;
	}
}

/* 13. P3.12H - PDP lower content layer (Description / Additional
   Information / Reviews only - does not touch the upper gallery/summary
   area, P3.12F, or P3.12G at all).

   Verified live wrappers (vapeoza/woocommerce/content-single-product.php
   + template.css around line 13690):
     .woocommerce-tabs.wc-tabs-wrapper     - outermost tabs/accordion block
       .content-woocommerce-tabs           - parent theme's own max-width/
                                              padding container (untouched)
         .content-ul-tab > ul.tabs          - desktop tab-label row; the
                                              parent theme already sets
                                              this to display:none at
                                              <=991px, which is the exact
                                              point it switches to the
                                              per-section mobile accordion
                                              header (.tab-title) instead -
                                              not duplicated here
         .content-tab                       - wraps all three
                                              .container-tab panels
                                              (Description/Additional
                                              Information/Reviews); each
                                              .container-tab already gets
                                              its own divider/spacing from
                                              the parent theme at <=991px

   Effect: light-gray outer layer -> white header bar (desktop) -> white
   content box (both breakpoints) -> existing black/dark-gray typography,
   with only the divider colors nudged to the approved subtle neutral
   range. No JS/accordion behavior touched - purely background/border/
   padding on already-existing elements. */
body.single-product .woocommerce-tabs.wc-tabs-wrapper {
	background: #f7f7f7;
	border-radius: 4px;
	padding: 16px;
	margin-top: 32px;
}
/* P3.12H-HF3 - the parent theme's own .content-ul-tab margin-bottom:50px
   (template.css:13737) was the real source of the oversized gap between
   the tab bar and the content panel below it - not previously overridden
   in P3.12H. Reduced to the approved 14-18px visual-separation target. */
body.single-product .woocommerce-tabs .content-ul-tab {
	background: #fff;
	border: 1px solid #ebebeb;
	border-radius: 4px;
	padding: 0 24px;
	margin-bottom: 16px;
}
body.single-product .woocommerce-tabs .content-tab {
	background: #fff;
	border: 1px solid #ebebeb;
	border-radius: 4px;
	padding: 4px 24px 6px;
}
body.single-product .woocommerce-tabs .container-tab {
	border-bottom-color: #ebebeb !important;
}
body.single-product .woocommerce-tabs .tab-title {
	font-size: 14px;
	font-weight: 600;
	color: #1a1a1a;
}
body.single-product .woocommerce-Tabs-panel table.woocommerce-product-attributes th,
body.single-product .woocommerce-Tabs-panel table.woocommerce-product-attributes td {
	border-color: #ebebeb;
}

@media (max-width: 767px) {
	/* P3.12H-HF2 - the gray background is painted on
	   .woocommerce-tabs.wc-tabs-wrapper itself (the outermost element),
	   whose own box has no left/right inset of its own on mobile - only
	   its much deeper descendant .content-woocommerce-tabs carries the
	   parent theme's 15px padding (template.css:13732), which only
	   affects that inner element's own content, not where the gray
	   background's box edges sit. .bwp-single-product (the upper
	   gallery/summary wrapper) avoids this because it has its OWN
	   dedicated padding-left/right:15px (template.css:20841) on its own
	   outer box. Reusing that exact same 15px value here, as a margin on
	   the gray wrapper itself, gives the lower section a matching visual
	   inset instead of running edge-to-edge - desktop is untouched since
	   this is inside the existing 767px-only P3.12H mobile block. */
	body.single-product .woocommerce-tabs.wc-tabs-wrapper {
		padding: 10px;
		margin-top: 24px;
		margin-left: 15px;
		margin-right: 15px;
	}
	body.single-product .woocommerce-tabs .content-tab {
		padding: 2px 18px 4px;
	}
	body.single-product .woocommerce-tabs .panel {
		padding: 16px 0 18px;
	}
	/* P3.12H-HF3 - mobile accordion header-to-content gap: the parent
	   theme's .tab-title.active margin-bottom:15px (template.css:13717,
	   <=991px) sits just outside the approved 10-14px mobile target;
	   nudged down slightly rather than left as-is. */
	body.single-product .woocommerce-tabs .tab-title.active {
		margin-bottom: 12px;
	}
}

/* ==========================================================================
   P3.12N - Conditional single-image thumbnail cleanup
   Verified live DOM (Fumot 12K, one image; Fumot 50K 2-in-1, 24 images):
     .bwp-single-image .images figure.woocommerce-product-gallery .row.left
       > .col-md-2 > .container-thumbnail-single > .content-thumbnail-scroll
         > .image-thumbnail.bwp-swiper-thumbs.swiper > .swiper-wrapper
           > .swiper-slide.img-thumbnail  (one per gallery image, static -
   confirmed in the parent theme's functions.js that variation selection only
   swaps image src / re-slides existing swiper instances via the native
   wc_variation_form "show_variation"/"reset_image" events; it never adds or
   removes .swiper-slide elements, so this count is a reliable, page-load-
   static signal of the effective gallery image count, unaffected by which
   flavor/variation is later selected)
       > .col-md-10 > .container-image-single (main image/swiper, untouched)

   :has() lets one CSS-only rule detect "exactly one gallery image" directly
   from that structure with no JS/PHP: when the thumb swiper's wrapper has
   only one .swiper-slide.img-thumbnail child, the thumbnail column is
   hidden and the main image column reclaims its space. Any product that
   later has 2+ gallery images automatically keeps/regains thumbnails - no
   code change needed. This is scoped to .bwp-single-image only and does
   not touch the gallery, lightbox, zoom, srcset, or variation logic.
   Desktop (>=991px) already hides this same column unconditionally via the
   parent theme's own .one_column CSS (template.css ~21032) using its
   vertical-scroll main-image stack instead of thumbnails - this rule does
   not need to touch desktop and simply agrees with that existing behavior
   whenever it also applies. The real, previously-unhandled gap this closes
   is mobile/tablet (<991px), where no such hiding rule existed at all. */
.bwp-single-image .row.left:has(.image-thumbnail.bwp-swiper-thumbs .swiper-wrapper > .swiper-slide.img-thumbnail:only-child) .col-md-2 {
	display: none;
}
.bwp-single-image .row.left:has(.image-thumbnail.bwp-swiper-thumbs .swiper-wrapper > .swiper-slide.img-thumbnail:only-child) .col-md-10 {
	flex: 0 0 100%;
	max-width: 100%;
}

/* ==========================================================================
   P3.13.6 - Mobile main-image size trial (reversible CSS-only test)

   Owner-approved trial, scoped to <=575px only, targeting the same main
   gallery <img> the >=991px rule above already overrides
   (.bwp-single-image .scroll-image .image-additional img -
   template.css:23172's un-scoped width:100% is what both rules replace).
   Measured across 5 real DashVapes single-product pages at ~477px width:
   longest visible image edge ~207-236px (median ~230px), vs VAPNORY's
   current ~378x378px main image at the same width - substantially
   oversized by comparison.

   Only the <img> itself and its .container-image-single wrapper are
   touched:
   - .image-additional (the swiper root) and .swiper-wrapper keep their
     own display/position rules completely untouched, so slide/zoom/
     lightbox mechanics (Elementor's data-elementor-open-lightbox on the
     <a>, and the hover-zoom data-zoom_* attributes on the outer
     .bwp-single-product, both independent of this image's rendered
     size) are not touched.
   - .container-image-single is a plain wrapper div, not a swiper
     element, so making it a flex container to vertically center the
     (now smaller) image within a ~300px-tall stage does not affect
     Swiper's own layout.
   - variation image-swap only ever changes the <img> src/data
     attributes (confirmed in P3.12N above), never this CSS, so
     switching flavors keeps the same capped size/centering.
   ========================================================================== */
@media (max-width: 575px) {
	/* P3.13.6-HF1: !important added defensively. Root-cause diagnosis
	   found no CSS rule that outranks this selector on specificity (the
	   only other width rule for this exact chain,
	   .single-product .container-image-single .scroll-image
	   .image-additional img{width:100%} at template.css:23173, has lower
	   specificity: 4 classes+1 element vs this rule's 4 classes+2
	   elements) and no inline style is ever set on this <img> by any
	   theme JS (functions.js only ever reads its width/height for zoom
	   math and variation-swap transitions, never writes to it, and only
	   ever writes .attr('src') to it for the flavour swap). The actual
	   fix for the "no visible change" report was the enqueue version
	   bump above (1.7.2 unchanged since every prior P3.12/P3.13 edit to
	   this file - the browser had no reason to ever re-fetch it). !important
	   is added here purely as defensive hardening in case a mobile
	   browser's own UA stylesheet or an as-yet-unobserved plugin rule
	   is also in play; it does not change the diagnosis above.
	   HOTFIX (mobile gallery CLS): added aspect-ratio:1/1 so the browser
	   reserves the image's proportional space immediately from the
	   computed width, instead of waiting for image bytes to arrive
	   before it knows the height - this is what a plain width:auto/
	   height:auto pair cannot do on its own. */
	/* (2026-10-04: the 245px picture cap that stood here is replaced by the unified picture stage at the end of this file) */
	/* HOTFIX (mobile gallery CLS): min-height -> height (a true fixed
	   stage, not just a floor) + overflow:hidden. External real-device
	   reproduction measured the product-info panel moving twice after
	   this stage's first paint (~150ms, then ~700ms) even with
	   min-height:300px already in place, because min-height only
	   guarantees a FLOOR - it does not stop the stage growing taller if
	   anything inside it (e.g. the ~100ms-delayed show_variation handler
	   in functions.js) ends up taller than 300px. See HOTFIX 2 just
	   below for why this rule also needed !important. */
	/* HOTFIX 2 (mobile gallery CLS): height now !important - the owner's
	   external re-measurement after HOTFIX 1 found the same shift still
	   happening, and explicitly asked "confirm whether the fixed 300px
	   rule actually wins in computed style - file presence is not
	   evidence." No !important here was a real gap: without it, this
	   rule could lose to any later or more specific competing rule. The
	   HOTFIX-1 .vpn-gallery-settling timer/class approach (and its JS
	   file) has been removed entirely per instruction, rather than
	   layered under another fix. */
	/* (2026-10-04: the fixed 300px stage that stood here is replaced by the unified picture stage at the end of this file - a square with an aspect ratio, which reserves its
	   space just as early, so the page does not move when the picture arrives or is swapped) */

	/* HOTFIX 2 (mobile gallery CLS): the owner's re-measurement noted the
	   breadcrumb - well above the gallery - also moves by a similar
	   amount, which points at browser scroll anchoring (Chrome/Safari
	   automatically re-scroll the page to keep on-screen content stable
	   whenever off-screen-above content changes size) rather than a pure
	   in-place layout shift. Disabled on both the gallery itself and
	   <html> (this stylesheet only loads on single-product pages, so the
	   <html> rule cannot reach Shop/other pages) - scoped to this
	   media query only, so desktop/tablet are unaffected either way, per
	   instruction to disable it "only on the product gallery". */
	.bwp-single-image,
	html {
		overflow-anchor: none;
	}
}

/* ==========================================================================
   P3.13.7 - Single-image gallery cleanup (CSS-only, all breakpoints)

   Owner-approved: show only one current main image on FUMOT single-
   product pages - no thumbnail strip, no secondary large images stacked
   below it in "one_column" mode. No media/attachment/variation-image-
   mapping data is touched; this only hides existing DOM nodes.

   Confirmed safe for variation switching before writing any CSS:
   - WooCommerce's own (unmodified) wc-add-to-cart-variation.js always
     writes the swapped src/srcset/data-src to the FIRST
     .woocommerce-product-gallery__image element on found_variation/
     show_variation - never to a different slide.
   - The parent theme's own handler for the same events
     (functions.js:1069, "$('.variations_form').on('wc_variation_form
     show_variation reset_image', ...)") additionally calls
     mainSwiper.slideTo(0, 500) and thumbsSwiper.slideTo(0, 500) every
     time - i.e. the theme itself always snaps back to slide 0 as "the"
     current image on every variation change, confirming slide 0 is the
     sole swap target by design, not an incidental first item.
   - Verified live on the Fumot 50K page: of 48 total .swiper-slide
     elements across both swiper instances, exactly ONE carries the
     .woocommerce-product-gallery__image class (always the first slide
     of the main .image-additional swiper) - the other 47 (thumbnail
     rail slides plus extra stacked "flavor" reference images from the
     original product gallery, e.g. .../fumot-50k-002-flavor.webp) are
     never targeted by the swap logic at all.
   - No prev/next navigation arrows were found for the main
     .image-additional swiper in one_column mode (it stacks slides in a
     column, not a slide-by-slide carousel) - there is nothing to hide
     for item 4 of the approved scope.
   Because of this, hiding every .swiper-slide that lacks
   .woocommerce-product-gallery__image is safe: the one slide WooCommerce
   swaps content into is never touched by this rule, so switching
   flavours/combinations keeps updating the one visible image exactly as
   before. No JS change was needed.
   ========================================================================== */
body.single-product .bwp-single-image .row.left .col-md-2 {
	display: none !important;
}
body.single-product .bwp-single-image .row.left .col-md-10 {
	flex: 0 0 100% !important;
	max-width: 100% !important;
}
/* The static gallery template (child-theme override of
   content-image/one_column.php) no longer renders any extra
   .swiper-slide gallery-image elements at all - it only ever outputs the
   one current .woocommerce-product-gallery__image - so the rule that used
   to hide the others here has nothing left to target and was removed. */


/* ==========================================================================
   P3.14 v2 - Narrower purchase-panel cleanup, scoped: body.postid-74533 ONLY.
   FLAVOR label/selector, Buy Now, Share, Wishlist are all UNAFFECTED - no
   rule below touches them.
   ========================================================================== */

/* Hide: duplicate flavour subtitle, the "Details" paragraph (site's
   configured age-verification copy occupying this display slot - product
   summary display only, the real age-verification system is untouched),
   "Item Price" label (price value itself stays), and WooCommerce's own
   per-variation description sentence. */
body.single-product.postid-74533 .vpn-flavor-subtitle,
body.single-product.postid-74533 .summary .description,
body.single-product.postid-74533 .vpn-price-block,
body.single-product.postid-74533 .woocommerce-variation-description,
body.single-product.postid-74590 .vpn-flavor-subtitle,
body.single-product.postid-74590 .summary .description,
body.single-product.postid-74590 .vpn-price-block,
body.single-product.postid-74590 .woocommerce-variation-description,
body.single-product.postid-75398 .vpn-flavor-subtitle,
body.single-product.postid-75414 .vpn-flavor-subtitle,
body.single-product.postid-75398 .summary .description,
body.single-product.postid-75414 .summary .description,
body.single-product.postid-75398 .vpn-price-block,
body.single-product.postid-75414 .vpn-price-block,
body.single-product.postid-75398 .woocommerce-variation-description,
body.single-product.postid-75414 .woocommerce-variation-description {
	display: none !important;
}

/* Dynamic title: "FUMOT 12000 - {FLAVOUR}", same order:2 slot as before. */
body.single-product.postid-74533 .vpn-pp-title,
body.single-product.postid-74590 .vpn-pp-title,
body.single-product.postid-75398 .vpn-pp-title,
body.single-product.postid-75414 .vpn-pp-title {
	margin: 0 0 6px;
	padding: 0;
	font-size: 24px;
	line-height: 1.25;
	font-weight: 700;
	letter-spacing: 0.01em;
	text-transform: uppercase;
	color: #14284b;
	overflow-wrap: anywhere;
}
/* DOM order is sep THEN flavour (" - " precedes the flavour span), so the
   :has() form (sep hidden when the flavour that FOLLOWS it is empty) is the
   only one of the two that can ever match - kept as the single rule. */
body.single-product.postid-74533 .vpn-pp-title-sep:has(+ .vpn-pp-title-flavour:empty),
body.single-product.postid-74590 .vpn-pp-title-sep:has(+ .vpn-pp-title-flavour:empty),
body.single-product.postid-75398 .vpn-pp-title-sep:has(+ .vpn-pp-title-flavour:empty),
body.single-product.postid-75414 .vpn-pp-title-sep:has(+ .vpn-pp-title-flavour:empty) {
	display: none;
}

/* Price: restrained top margin now the "Item Price" label above it is gone. */
body.single-product.postid-74533 .discount-price,
body.single-product.postid-74590 .discount-price,
body.single-product.postid-75398 .discount-price,
body.single-product.postid-75414 .discount-price {
	margin-top: 2px;
}

@media (max-width: 575px) {
	body.single-product.postid-74533 .vpn-pp-title,
body.single-product.postid-74590 .vpn-pp-title,
body.single-product.postid-75398 .vpn-pp-title,
body.single-product.postid-75414 .vpn-pp-title {
		font-size: 20px;
	}
}

/* ==========================================================================
   P3.14 follow-up 3 - REPLACES follow-up 2's ::before/order technique, which
   was mis-scoped (see PREVIEW_NOTES_v2.md for the exact DOM proof) and had
   no effect on the real bug. Scoped: body.postid-74533 ONLY.

   ROOT CAUSE (found by parsing the LIVE DOM with an explicit tag-depth
   tracker, not assumption): .woosw-wishlist and .share-content are NOT
   children of .woocommerce-variation-add-to-cart - they are its SIBLINGS.
   All four - the empty WC alert div (.woocommerce-variation.single_variation),
   .woocommerce-variation-add-to-cart (which internally contains Quantity +
   Add to Cart as its own nested flex row), .woosw-wishlist, and
   .share-content - are direct children of .single_variation_wrap, and THAT
   element is the real flex container:

     template.css:22479-22482
     .single-product .product-type-variable .variations_form.cart
       .single_variation_wrap { display:flex; vertical-align:top;
       flex-wrap:wrap; }

   Follow-up 2's ::before + order rules were attached to
   .woocommerce-variation-add-to-cart, one level too deep - they never
   participated in .single_variation_wrap's flex layout at all, so nothing
   stopped Wishlist/Share from competing for space on the same line as
   Quantity+Add to Cart (desktop: all three crowd together; mobile: the
   crowding also squeezes .woocommerce-variation-add-to-cart below its own
   min-content width, which is what forces ITS internal Quantity/Add to Cart
   flex row to wrap internally too).

   FIX: make .single_variation_wrap itself a 2-column CSS Grid (explicit,
   deterministic placement - no order/pseudo-element reliance). Each of the
   3 visible children is placed by its own class into an exact cell; nothing
   depends on source order or line-wrap heuristics.
   ========================================================================== */
body.single-product.postid-74533 .single_variation_wrap,
body.single-product.postid-74590 .single_variation_wrap,
body.single-product.postid-75398 .single_variation_wrap,
body.single-product.postid-75414 .single_variation_wrap {
	display: grid !important;
	grid-template-columns: 1fr 1fr;
	column-gap: 10px;
	row-gap: 14px;
	align-items: center;
	width: 100%;
}
/* The empty WooCommerce alert div (populated only with ARIA text on error,
   normally empty) - drop it from the grid instead of giving it a cell. */
body.single-product.postid-74533 .single_variation_wrap > .woocommerce-variation.single_variation:empty,
body.single-product.postid-74590 .single_variation_wrap > .woocommerce-variation.single_variation:empty,
body.single-product.postid-75398 .single_variation_wrap > .woocommerce-variation.single_variation:empty,
body.single-product.postid-75414 .single_variation_wrap > .woocommerce-variation.single_variation:empty {
	display: none;
}
/* Row 1, full width: Quantity + Add to Cart. This element's OWN internal
   layout (display:flex, Quantity auto-width, Add to Cart flex:1) is
   UNCHANGED and untouched - it already produces exactly "compact quantity,
   Add to Cart fills the rest" on its own; it only needed a guaranteed
   full-width row with nothing else competing for space, which grid-column
   1/-1 (span both columns) now guarantees deterministically. */
body.single-product.postid-74533 .single_variation_wrap > .woocommerce-variation-add-to-cart,
body.single-product.postid-74590 .single_variation_wrap > .woocommerce-variation-add-to-cart,
body.single-product.postid-75398 .single_variation_wrap > .woocommerce-variation-add-to-cart,
body.single-product.postid-75414 .single_variation_wrap > .woocommerce-variation-add-to-cart {
	grid-column: 1 / -1;
	grid-row: 1;
	margin: 0 !important;
	flex-wrap: nowrap !important; /* Add to Cart must never wrap below Quantity once it has its own full row */
}
body.single-product.postid-74533 .single_variation_wrap > .woocommerce-variation-add-to-cart .quantity,
body.single-product.postid-74590 .single_variation_wrap > .woocommerce-variation-add-to-cart .quantity,
body.single-product.postid-75398 .single_variation_wrap > .woocommerce-variation-add-to-cart .quantity,
body.single-product.postid-75414 .single_variation_wrap > .woocommerce-variation-add-to-cart .quantity {
	flex: 0 0 auto;
	margin: 0 10px 0 0 !important;
}
body.single-product.postid-74533 .single_variation_wrap > .woocommerce-variation-add-to-cart .single_add_to_cart_button,
body.single-product.postid-74590 .single_variation_wrap > .woocommerce-variation-add-to-cart .single_add_to_cart_button,
body.single-product.postid-75398 .single_variation_wrap > .woocommerce-variation-add-to-cart .single_add_to_cart_button,
body.single-product.postid-75414 .single_variation_wrap > .woocommerce-variation-add-to-cart .single_add_to_cart_button {
	flex: 1 1 auto;
	min-width: 0;
}
/* P3.16 - Row 2, full width: delivery estimate module (new). Presentational
   only, no shipping-zone/calculation logic. Pushes Wishlist/Share to row 3. */
body.single-product.postid-74533 .single_variation_wrap > .vpn-delivery-estimate,
body.single-product.postid-74590 .single_variation_wrap > .vpn-delivery-estimate,
body.single-product.postid-75398 .single_variation_wrap > .vpn-delivery-estimate,
body.single-product.postid-75414 .single_variation_wrap > .vpn-delivery-estimate {
	grid-column: 1 / -1;
	grid-row: 2;
	display: flex;
	align-items: center;
	gap: 10px;
	margin: 0 !important;
	padding: 0;
	background: transparent;
	border: 0;
}
body.single-product.postid-74533 .vpn-delivery-icon,
body.single-product.postid-74590 .vpn-delivery-icon,
body.single-product.postid-75398 .vpn-delivery-icon,
body.single-product.postid-75414 .vpn-delivery-icon {
	flex: 0 0 auto;
	display: inline-flex;
	line-height: 0;
	color: #4a6478;
}
body.single-product.postid-74533 .vpn-delivery-icon svg,
body.single-product.postid-74590 .vpn-delivery-icon svg,
body.single-product.postid-75398 .vpn-delivery-icon svg,
body.single-product.postid-75414 .vpn-delivery-icon svg {
	display: block;
}
body.single-product.postid-74533 .vpn-delivery-copy,
body.single-product.postid-74590 .vpn-delivery-copy,
body.single-product.postid-75398 .vpn-delivery-copy,
body.single-product.postid-75414 .vpn-delivery-copy {
	flex: 1 1 auto;
	min-width: 0;
	font-size: 12.5px;
	line-height: 1.45;
	color: #4a4a4a;
}
body.single-product.postid-74533 .vpn-delivery-destination strong,
body.single-product.postid-74533 .vpn-delivery-date strong,
body.single-product.postid-74590 .vpn-delivery-destination strong,
body.single-product.postid-74590 .vpn-delivery-date strong,
body.single-product.postid-75398 .vpn-delivery-destination strong,
body.single-product.postid-75414 .vpn-delivery-destination strong,
body.single-product.postid-75398 .vpn-delivery-date strong,
body.single-product.postid-75414 .vpn-delivery-date strong {
	font-weight: 600;
}
/* Row 3: Wishlist left, Share right (was row 2 before P3.16 added the delivery row). */
body.single-product.postid-74533 .single_variation_wrap > .woosw-wishlist,
body.single-product.postid-74590 .single_variation_wrap > .woosw-wishlist,
body.single-product.postid-75398 .single_variation_wrap > .woosw-wishlist,
body.single-product.postid-75414 .single_variation_wrap > .woosw-wishlist {
	grid-column: 1;
	grid-row: 3;
	justify-self: start;
	margin: 0 !important;
}
body.single-product.postid-74533 .single_variation_wrap > .share-content,
body.single-product.postid-74590 .single_variation_wrap > .share-content,
body.single-product.postid-75398 .single_variation_wrap > .share-content,
body.single-product.postid-75414 .single_variation_wrap > .share-content {
	grid-column: 2;
	grid-row: 3;
	justify-self: end;
	margin: 0 !important;
}

/* P3.17 - unify the image->purchase-panel background transition, product
   74533 ONLY, across the full mobile/tablet range (<=767px, extended from
   the initial <=575px preview per owner follow-up: the same fragmentation
   is present 576-767px too). Root cause: the gray panel background
   (#f7f7f7) is painted on the INNER .summary.entry-summary box only
   (see the base rule above, shared by every product - left untouched);
   the OUTER .bwp-single-info column carries the parent theme's own
   padding-top:10px at <=767px (template.css:20904-20906) with no
   background of its own, producing a white strip above the box and white
   gutters beside it. Moving the fill one level up removes both without
   any negative margins and without touching the shared global rule -
   every other product, including 50K, is unaffected; desktop (>767px)
   is untouched. */
@media (max-width: 767px) {
	body.single-product.postid-74533 .bwp-single-info,
body.single-product.postid-74590 .bwp-single-info,
body.single-product.postid-75398 .bwp-single-info,
body.single-product.postid-75414 .bwp-single-info {
		background-color: #f7f7f7;
		padding-top: 8px;
	}
	body.single-product.postid-74533 .bwp-single-info .summary.entry-summary,
body.single-product.postid-74590 .bwp-single-info .summary.entry-summary,
body.single-product.postid-75398 .bwp-single-info .summary.entry-summary,
body.single-product.postid-75414 .bwp-single-info .summary.entry-summary {
		background: transparent;
		padding-top: 16px;
	}

	/* P3.19 - REPLACES P3.18's gradient (owner follow-up: a single unified
	   gray section is wanted instead of a gray->blue blend). Product
	   74533 ONLY, same <=767px range. .vpn-fx margin-top:0 kept (removes
	   the white gap between the two sections - same reason as P3.18).
	   .vpn-fx's own background AND its box-shadow (the plugin's own
	   full-bleed technique - fumot-product-experience.css:18-21, paints
	   solid colour beyond .vpn-fx's own box on a narrower parent
	   container, needed so the sides match too, not just the centre) are
	   both overridden to the same #f7f7f7 as the purchase panel, so the
	   whole area from the purchase panel through the Cool Mint section
	   reads as one continuous light-gray field. clip-path (layout, not
	   colour) is left untouched. Not edited in the plugin file - CSS
	   override from the child theme only. */
	body.single-product.postid-74533 .vpn-fx,
body.single-product.postid-74590 .vpn-fx,
body.single-product.postid-75398 .vpn-fx,
body.single-product.postid-75414 .vpn-fx {
		margin-top: 0;
		background: #f7f7f7;
		box-shadow: 0 0 0 100vmax #f7f7f7;
	}
}

/* P3.20 - purchase-panel typography refinement, product 74533 ONLY,
   mobile/tablet (<=767px). Every base rule touched here is normally
   shared (see below); none is edited in place - each is overridden by
   this more specific 74533+mobile-scoped rule instead, so desktop and
   every other product keep their current values. Only the property that
   actually differs from the live value is set (weight-only or size-only
   changes are not paired with a redundant redeclaration of the other).
   FLAVOR label (.vpn-fsel-field-label) already matches the approved
   12px/500 target - intentionally not touched. */
@media (max-width: 767px) {
	/* FUMOT brand line (base rule: body.single-product .vpn-brand-line,
	   shared by every product using this panel structure): 500 -> 400 */
	body.single-product.postid-74533 .vpn-brand-line,
body.single-product.postid-74590 .vpn-brand-line,
body.single-product.postid-75398 .vpn-brand-line,
body.single-product.postid-75414 .vpn-brand-line {
		font-weight: 400;
	}
	/* Dynamic title "FUMOT 12K - {Flavour Name}": 20px/700 -> 17px/500.
	   text-transform:none added here (P3.21 follow-up, caught during this
	   deploy's own validation, not requested separately): the base desktop
	   rule above (line ~1232) sets text-transform:uppercase, which forces
	   the flavour span to render visually uppercase regardless of its
	   actual HTML case - without this, the new natural-Title-Case text
	   ("Cool Mint") would still visually display as "COOL MINT" on
	   mobile, silently defeating the point of this change. Scoped to this
	   existing mobile-only rule, so desktop's own uppercase display is
	   untouched, per instruction not to change desktop. */
	body.single-product.postid-74533 .vpn-pp-title,
body.single-product.postid-74590 .vpn-pp-title,
body.single-product.postid-75398 .vpn-pp-title,
body.single-product.postid-75414 .vpn-pp-title {
		font-size: 17px;
		font-weight: 500;
		text-transform: none;
	}
	/* Current/sale price (base rules shared by every product): 22px -> 18px.
	   Both the initial-load rule and the variation-swapped duplicate are
	   updated together, matching this file's own existing intent that the
	   two stay in sync so price size doesn't jump on flavour selection. */
	body.single-product.postid-74533 .bwp-single-info .price,
	body.single-product.postid-74533 .product-type-variable .variations_form.cart .single_variation_wrap .price,
body.single-product.postid-74590 .bwp-single-info .price,
body.single-product.postid-74590 .product-type-variable .variations_form.cart .single_variation_wrap .price,
body.single-product.postid-75398 .bwp-single-info .price,
body.single-product.postid-75414 .bwp-single-info .price,
body.single-product.postid-75398 .product-type-variable .variations_form.cart .single_variation_wrap .price,
body.single-product.postid-75414 .product-type-variable .variations_form.cart .single_variation_wrap .price {
		font-size: 18px;
	}
	/* Regular/original (struck-through) price: 17px -> 16px */
	body.single-product.postid-74533 .bwp-single-info .price del,
	body.single-product.postid-74533 .product-type-variable .variations_form.cart .single_variation_wrap .price del,
body.single-product.postid-74590 .bwp-single-info .price del,
body.single-product.postid-74590 .product-type-variable .variations_form.cart .single_variation_wrap .price del,
body.single-product.postid-75398 .bwp-single-info .price del,
body.single-product.postid-75414 .bwp-single-info .price del,
body.single-product.postid-75398 .product-type-variable .variations_form.cart .single_variation_wrap .price del,
body.single-product.postid-75414 .product-type-variable .variations_form.cart .single_variation_wrap .price del {
		font-size: 16px;
	}
	/* Sale badge: base .onsale rule is in the PARENT theme
	   (template.css:14698) and is site-wide, every product/page - not
	   edited; this scoped override only affects this product's own badge
	   on mobile. 700 -> 600 */
	body.single-product.postid-74533 .discount-price .onsale,
body.single-product.postid-74590 .discount-price .onsale,
body.single-product.postid-75398 .discount-price .onsale,
body.single-product.postid-75414 .discount-price .onsale {
		font-weight: 600;
	}
}

/* P3.22 - Cool Mint illustration reduced to 50% of its current rendered
   size, product 74533, Cool Mint flavour panel ONLY, mobile/tablet
   (<=767px). Real container responsible: .vpn-fx-art (PLUGIN's own CSS,
   NOT edited here per house rule against plugin-file modification -
   overridden from the child theme instead), fumot-product-experience.css:
   57-62 (width: min(72vw, 300px)) and its own min-width:500px override at
   line 450-452 (width: min(72vw, 360px) - the active value from 500px up
   to the plugin's next breakpoint at 1024px, i.e. for the whole 500-767px
   slice of our mobile/tablet range). Both breakpoints are mirrored here,
   each halved, so the reduction is an exact 50% at every width from
   390-767px, not an approximation:
     <500px:  min(72vw,300px) -> min(36vw,150px)
     500-767: min(72vw,360px) -> min(36vw,180px)
   .vpn-fx-art has no fixed height (height is auto, driven entirely by its
   own width via the image's intrinsic aspect ratio - confirmed by reading
   the plugin CSS, no separate height rule anywhere on .vpn-fx-art or its
   .vpn-fx-hero parent), so halving its width alone already makes the
   wrapper collapse to the smaller image's height with no extra rule
   needed - no leftover empty space. Scoped with the
   [data-flavour="Cool Mint"] attribute selector so only that one flavour
   panel is targeted (every other flavour's own .vpn-fx-art, e.g. Black
   Ice, is a completely separate element under its own data-flavour panel
   and is untouched) - this also gives the override higher specificity
   than the plugin's bare .vpn-fx-art rule, so it wins regardless of
   stylesheet load order. max-width:100% and the radial-gradient mist
   background (both on the same element) are left exactly as the plugin
   defines them - only width changes, so the mist scales down coherently
   with the image instead of being a separate, unrelated change. */
@media (max-width: 499px) {
	body.single-product.postid-74533 .vpn-fx-panel[data-flavour="Cool Mint"] .vpn-fx-art {
		width: min(36vw, 150px);
	}
}
@media (min-width: 500px) and (max-width: 767px) {
	body.single-product.postid-74533 .vpn-fx-panel[data-flavour="Cool Mint"] .vpn-fx-art {
		width: min(36vw, 180px);
	}
}

/* P3.23/P3.24/P3.25/P3.28 - "Explore the Cooling Spectrum": Cool Mint
   (01), Grape Mint (02), Black Ice (03) and Strawberry Watermelon (04)
   cards - remove the separate .vpn-fx-cat-name title for each, since
   their spectrum images already have the flavour name baked into the
   artwork itself - keeping both duplicated the label. CSS-only, no
   plugin-file edit: the card <li> carries no per-flavour class/attribute
   of its own to hook into, so each is targeted via its own link's href
   (rawurlencode() of the flavour name, confirmed on the live page as
   ?attribute_flavor=Cool%20Mint / ?attribute_flavor=Black%20Ice /
   ?attribute_flavor=Grape%20Mint / ?attribute_flavor=Strawberry%20
   Watermelon) using :has() - already an established technique in this
   file (see the Cool Mint thumbnail-count rule above). Cool Mint's card
   also carries is-current/aria-current (it's the product's default
   flavour) - unrelated attributes, no effect on this href-ends-with
   match. The underlying flavour NAME/data value, href, and switching
   logic are completely untouched for all four - only these visible text
   nodes are suppressed. display:none (not visibility:hidden) so the
   vertical gap closes naturally - .vpn-fx-cat-desc, the next flex-column
   child, simply moves up to fill it; no negative margins needed. Cuban
   Menthol Mojito (still numbered 04, currently hidden by stock - see
   fumot-product-experience.php) is untouched - its artwork does not
   contain the flavour name, so its title would still need to show if it
   ever became visible again. */
body.single-product.postid-74533 .vpn-fx-cat:has(> .vpn-fx-cat-link[href$="attribute_flavor=Cool%20Mint"]) .vpn-fx-cat-name,
body.single-product.postid-74533 .vpn-fx-cat:has(> .vpn-fx-cat-link[href$="attribute_flavor=Black%20Ice"]) .vpn-fx-cat-name,
body.single-product.postid-74533 .vpn-fx-cat:has(> .vpn-fx-cat-link[href$="attribute_flavor=Grape%20Mint"]) .vpn-fx-cat-name,
body.single-product.postid-74533 .vpn-fx-cat:has(> .vpn-fx-cat-link[href$="attribute_flavor=Strawberry%20Watermelon"]) .vpn-fx-cat-name {
	display: none;
}

/* P3.26/P3.27 - "Explore the Cooling Spectrum", product 74533 ONLY.
   P3.26 hid the whole catalogue UI (numbers/descriptors/arrows) alongside
   the divider lines; P3.27 (this revision, owner follow-up: the
   image-only result read as too empty) RESTORES the numbers, descriptors
   and arrows - only the divider/border system and the current-card
   accent bar stay removed, plus the external flavour-name headings stay
   hidden (P3.23-P3.25 above - unchanged, artwork already carries the
   name). Real elements identified by reading the plugin's own CSS
   (fumot-product-experience.css:303-390), none edited directly - every
   rule below is a higher-specificity child-theme override (this selector
   prefix always wins over the plugin's bare selectors, no !important
   needed):
     .vpn-fx-cat:nth-child(odd) { border-right }   <- vertical divider
     .vpn-fx-cat:nth-child(-n+2) { border-bottom }  <- horizontal divider
     .vpn-fx-cat.is-current::before                <- left accent bar
       (only ever drawn on Cool Mint's cell, the default flavour)
   .vpn-fx-cat-num/.vpn-fx-cat-desc/.vpn-fx-cat-arrow are NOT touched here
   (P3.26's display:none for them removed) - they render with their
   existing alignment (num top-left, arrow bottom-left, desc centered -
   all from the plugin's own .vpn-fx-cat-link flex-column layout,
   untouched, so alignment is already consistent card-to-card with no
   extra CSS needed). .vpn-fx-cat-name stays display:none for Cool
   Mint/Grape Mint/Black Ice (P3.23-P3.25) - not duplicated here.
   .vpn-fx-cat-link (the <a>) itself, its href, and .vpn-fx-cat-img (same
   width/aspect-ratio/object-fit:contain as before) are completely
   untouched - each card is still the same clickable link wrapping the
   same image, so flavour switching/navigation is unaffected.
   Divider borders are replaced with an explicit gap on the grid
   container itself (clean whitespace instead of lines), per the
   requested ~16-20px horizontal / ~20-28px vertical target - column-gap
   18px, row-gap 24px (middle of each range). */
body.single-product.postid-74533 .vpn-fx-spectrum {
	column-gap: 18px;
	row-gap: 24px;
}
body.single-product.postid-74533 .vpn-fx-cat:nth-child(odd),
body.single-product.postid-74533 .vpn-fx-cat:nth-child(-n+2) {
	border-right: 0;
	border-bottom: 0;
}
body.single-product.postid-74533 .vpn-fx-cat.is-current::before {
	display: none;
}

/* ==========================================================================
   FUMOT mobile gallery - enlarge the product body (owner request 2026-09-29)

   The FUMOT transparent images are square, with the product occupying only
   ~33-67% of the width and ~22-87% of the height (measured on all 50). The
   P3.13.6 mobile cap (245px canvas) therefore showed the product only
   ~160px tall. For FUMOT 12K (74533) and 50K (74590) only, draw the same
   image larger and let the existing fixed-height, overflow-hidden stage crop
   the empty canvas margins. The stage keeps a fixed height (no layout
   shift); every variation image uses the same rule (only src changes).
   Other products are untouched. <=575px only.
   ========================================================================== */
@media (max-width: 575px) {
	/* "html" prefix: the FUMOT plugin's fumot-gallery-portrait.css uses the
	   same selector with !important (200x300 box, aspect-ratio 2/3, written
	   for the older portrait photos) and loads later, so it beat the plain
	   selector; the extra element raises this rule's specificity. */
	/* (2026-10-04: the enlarged FUMOT picture + fixed 330px stage that stood here is replaced by normalised pictures and the unified picture stage at the end of this file) */
}

/* ==========================================================================
   FUMOT flavour illustration - same phone/tablet size for EVERY flavour
   (owner request 2026-09-29: "size per the Cool Mint page standard").

   P3.22 above halved the hero illustration for the Cool Mint panel only
   (<=767px); desktop stays at the plugin default for all flavours. The other
   flavour panels (12K and 50K, incl. Black Ice) were still at the plugin's
   full phone size (about twice as wide). Mirror the exact Cool Mint values
   for all panels of products 74533 / 74590. Each panel's art is additionally
   normalised to Cool Mint's visible artwork size by the plugin (per-image
   scale in data/hero-scale.php). Width only: the mist gradient and max-width
   stay as the plugin defines them.
   ========================================================================== */
@media (max-width: 499px) {
	body.single-product.postid-74533 .vpn-fx-panel .vpn-fx-art,
	body.single-product.postid-74590 .vpn-fx-panel .vpn-fx-art,
body.single-product.postid-75398 .vpn-fx-panel .vpn-fx-art,
body.single-product.postid-75414 .vpn-fx-panel .vpn-fx-art {
		width: min(36vw, 150px);
	}
}
@media (min-width: 500px) and (max-width: 767px) {
	body.single-product.postid-74533 .vpn-fx-panel .vpn-fx-art,
	body.single-product.postid-74590 .vpn-fx-panel .vpn-fx-art,
body.single-product.postid-75398 .vpn-fx-panel .vpn-fx-art,
body.single-product.postid-75414 .vpn-fx-panel .vpn-fx-art {
		width: min(36vw, 180px);
	}
}

/* ==========================================================================
   Sticky Add to cart bar (phones / small tablets; 12K, 50K and JNR pages) - see js/sticky-add-to-cart.js.
   The quantity + Add to cart row is pinned to the bottom of the screen while its normal place is (partly) off screen.
   ========================================================================== */
@media (max-width: 767px) {
	body.single-product .single_variation_wrap > .vpn-atc-ph {
		grid-column: 1 / -1;
		grid-row: 1;
	}
	body.single-product .single_variation_wrap > .vpn-atc-ph[hidden] {
		display: none;
	}
	body.single-product .single_variation_wrap > .woocommerce-variation-add-to-cart.vpn-atc-fixed {
		position: fixed !important;
		left: 0;
		right: 0;
		bottom: 0;
		top: auto;
		z-index: 9000;
		box-sizing: border-box;
		margin: 0 !important;
		padding: 10px 16px calc(10px + env(safe-area-inset-bottom, 0px));
		background: #fff;
		box-shadow: 0 -8px 22px rgba(0, 0, 0, 0.14);
	}
	/* The cart drawer locks the background with not-scroll. Its header
	   stacking context can sit below this fixed bar, covering drawer buttons.
	   Keep the bar's layout/display intact: the sticky script measures display
	   and observes class changes, so display:none would trigger resticking. */
	body.single-product.not-scroll .single_variation_wrap > .woocommerce-variation-add-to-cart.vpn-atc-fixed {
		visibility: hidden;
		pointer-events: none;
	}
	body.vpn-atc-on {
		padding-bottom: var(--vpn-atc-h, 76px);
	}
	body.vpn-atc-on .back-top {
		bottom: calc(var(--vpn-atc-h, 76px) + 16px);
	}
}

/* ==========================================================================
   UNIFIED PRODUCT PICTURE STAGE (2026-10-04, owner request: the same visual size for the device on every product page and for every flavour)
   The pictures are normalised files (media library entries "... display image (stage v1)", made from the original pictures, which stay untouched): a SQUARE canvas in
   which the visible device is exactly 85% of the height, centred, with 7.5% free space above and below - the same for FUMOT 12K, FUMOT 50K, JNR Falcon-X and
   JNR Lila Kiss, whatever the transparent margins of the original files were. The stage is a square the width of the picture column (580px at most on
   desktop, 420px at most on phones); the picture is CONTAINED in it (never stretched, never cropped). The stage has its height from the aspect ratio, so the page
   reserves the space at once and does not move when a flavour is chosen. This replaces the earlier picture caps (580px / 245px / 330px / 200x300 per product).
   ========================================================================== */
body.single-product .bwp-single-image .container-image-single {
	width: 100%;
	max-width: 580px;
	margin-left: auto;
	margin-right: auto;
	aspect-ratio: 1 / 1;
	height: auto;
	display: block;
}
body.single-product .bwp-single-image .container-image-single .scroll-image,
body.single-product .bwp-single-image .container-image-single .image-additional,
body.single-product .bwp-single-image .container-image-single .woocommerce-product-gallery__image {
	height: 100%;
}
body.single-product .bwp-single-image .container-image-single .woocommerce-product-gallery__image > a {
	display: block;
	height: 100%;
}
body.single-product .bwp-single-image .scroll-image .image-additional img {
	display: block;
	width: 100%;
	height: 100%;
	max-width: none;
	max-height: none;
	aspect-ratio: auto;
	object-fit: contain;
	object-position: center;
	margin: 0 auto;
}
@media (max-width: 575px) {
	body.single-product .bwp-single-image .container-image-single {
		max-width: 420px;
	}
}
