/* ==========================================================================
   Peak Performance Peptides — Global Theme
   Consumes tokens.css. No hard-coded colors below this line.

   Contents
     1.  Base & document
     2.  Typography
     3.  Links
     4.  Buttons
     5.  Forms
     6.  Surfaces & cards
     7.  Header — dark band
     8.  Mobile header & off-canvas menu
     9.  Hero / dark section utility
     10. Content sections & layout utilities
     11. Footer — dark band
     12. WooCommerce base restyle
     13. Locked templates (cart / checkout / plugin pages)
     14. Accessibility & focus
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. BASE & DOCUMENT
   -------------------------------------------------------------------------- */

html {
	scroll-behavior: smooth;
	/* Sticky dark header shouldn't cover anchored headings */
	scroll-padding-top: var(--space-2xl);

	/* Every size in this file is in rem, so the root size decides the scale of
	   the entire design. Astra sets it from the customizer, which means it is a
	   property of the host site: 112.5% (18px) on the site this was designed
	   against, 106.25% (17px) on the destination store. Left alone, the whole
	   build would arrive six percent smaller and tighter than what was
	   approved, with nothing obviously wrong to point at.

	   Pinning it here makes the scale travel. The step down on narrow screens
	   matches what the design was reviewed at. */
	font-size: 112.5%;
}

@media (max-width: 921px) {
	html {
		font-size: 102.6%;
	}
}

body.ppp-theme {
	background-color: var(--color-bg);
	color: var(--color-text);
	font-family: var(--font-body);
	font-size: var(--text-base);
	line-height: var(--leading-normal);
	-webkit-font-smoothing: antialiased;
	-moz-osx-font-smoothing: grayscale;
	text-rendering: optimizeLegibility;
}

/* Astra paints the content wrapper white by default; let the page bg show. */
.ppp-theme #page,
.ppp-theme #content,
.ppp-theme .site-content {
	background-color: transparent;
}

::selection {
	background-color: var(--color-accent);
	color: var(--color-on-dark);
}

hr,
.ppp-theme .wp-block-separator {
	border: 0;
	border-top: 1px solid var(--color-border);
	margin-block: var(--space-xl);
	opacity: 1;
}

img,
svg,
video {
	max-width: 100%;
	height: auto;
}

/* --------------------------------------------------------------------------
   2. TYPOGRAPHY
   Sizes and rhythm only — font-family stays inherited from Astra.
   -------------------------------------------------------------------------- */

.ppp-theme h1,
.ppp-theme h2,
.ppp-theme h3,
.ppp-theme h4,
.ppp-theme h5,
.ppp-theme h6 {
	color: var(--color-text);
	font-family: var(--font-heading);
	font-weight: var(--weight-semibold);
	line-height: var(--leading-tight);
	letter-spacing: var(--tracking-tight);
	text-wrap: balance;
	margin-block: 0 var(--space-sm);
}

.ppp-theme h1 { font-size: var(--text-4xl); font-weight: var(--weight-bold); }
.ppp-theme h2 { font-size: var(--text-3xl); }
.ppp-theme h3 { font-size: var(--text-2xl); }
.ppp-theme h4 { font-size: var(--text-xl); line-height: var(--leading-snug); }
.ppp-theme h5 { font-size: var(--text-lg); line-height: var(--leading-snug); }

.ppp-theme h6 {
	font-size: var(--text-sm);
	font-weight: var(--weight-semibold);
	line-height: var(--leading-snug);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	color: var(--color-text-muted);
}

.ppp-theme p {
	margin-block: 0 var(--space-md);
	max-width: var(--measure);
}

.ppp-theme p:last-child {
	margin-bottom: 0;
}

.ppp-theme .entry-content > ul,
.ppp-theme .entry-content > ol {
	margin-block: 0 var(--space-md);
	padding-inline-start: var(--space-md);
	max-width: var(--measure);
}

.ppp-theme li + li {
	margin-top: var(--space-2xs);
}

.ppp-theme strong,
.ppp-theme b {
	font-weight: var(--weight-semibold);
	color: var(--color-text);
}

.ppp-theme small,
.ppp-theme figcaption,
.ppp-theme .wp-block-image figcaption {
	font-size: var(--text-sm);
	color: var(--color-text-muted);
	line-height: var(--leading-snug);
}

.ppp-theme blockquote,
.ppp-theme .wp-block-quote {
	margin-block: var(--space-lg);
	padding: var(--space-xs) 0 var(--space-xs) var(--space-md);
	border-left: 3px solid var(--color-button);
	color: var(--color-text);
	font-size: var(--text-lg);
	line-height: var(--leading-snug);
	font-style: normal;
}

.ppp-theme code,
.ppp-theme kbd {
	background-color: var(--color-bg-secondary);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-xs);
	padding: 0.1em 0.4em;
	font-size: 0.9em;
}

/* Reusable type utilities for custom blocks */
.ppp-eyebrow {
	display: inline-block;
	font-size: var(--text-xs);
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-widest);
	text-transform: uppercase;
	color: var(--color-button);
	margin-bottom: var(--space-xs);
}

.ppp-lead {
	font-size: var(--text-lg);
	line-height: var(--leading-relaxed);
	color: var(--color-text-muted);
	max-width: var(--measure);
}

.ppp-muted { color: var(--color-text-muted); }
.ppp-measure { max-width: var(--measure); }
.ppp-measure-narrow { max-width: var(--measure-narrow); }

/* --------------------------------------------------------------------------
   3. LINKS
   -------------------------------------------------------------------------- */

.ppp-theme a {
	color: var(--color-accent);
	text-decoration-color: var(--color-border-strong);
	text-underline-offset: 0.18em;
	text-decoration-thickness: 1px;
	transition: color var(--transition-base), text-decoration-color var(--transition-base);
}

.ppp-theme a:hover,
.ppp-theme a:focus-visible {
	color: var(--color-button);
	text-decoration-color: currentColor;
}

/* Structural links shouldn't be underlined */
.ppp-theme .site-header a,
.ppp-theme .site-footer a,
.ppp-theme .menu-link,
.ppp-theme .ast-button,
.ppp-theme .wp-block-button__link,
.ppp-theme .woocommerce-loop-product__link,
.ppp-theme .page-numbers,
.ppp-theme .ppp-link-plain {
	text-decoration: none;
}

/* Inline arrow link used across custom sections */
.ppp-link-arrow {
	display: inline-flex;
	align-items: center;
	gap: var(--space-2xs);
	font-weight: var(--weight-semibold);
	color: var(--color-button);
	text-decoration: none;
}

.ppp-link-arrow::after {
	content: "\2192";
	transition: transform var(--transition-base);
}

.ppp-link-arrow:hover::after {
	transform: translateX(3px);
}

/* --------------------------------------------------------------------------
   4. BUTTONS
   Covers Astra buttons, core block buttons, WooCommerce buttons, and submits
   in one place so plugin pages inherit the look without markup changes.
   -------------------------------------------------------------------------- */

.ppp-theme .ast-button,
.ppp-theme .button,
.ppp-theme button,
.ppp-theme input[type="submit"],
.ppp-theme input[type="button"],
.ppp-theme .wp-block-button__link,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) a.button,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) button.button,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) input.button,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) #respond input#submit,
.ppp-theme .wc-block-components-button,
.ppp-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-2xs);
	background-color: var(--color-button);
	background-image: none;
	color: var(--color-button-text);
	border: 1px solid var(--color-button);
	border-radius: var(--radius-sm);
	padding: 0.75rem 1.5rem;
	font-size: var(--text-sm);
	font-weight: var(--weight-semibold);
	line-height: 1.2;
	letter-spacing: 0.01em;
	text-decoration: none;
	text-transform: none;
	cursor: pointer;
	box-shadow: var(--shadow-xs);
	transition: background-color var(--transition-base), border-color var(--transition-base),
		box-shadow var(--transition-base), transform var(--duration-fast) var(--ease-out),
		color var(--transition-base);
}

.ppp-theme .ast-button:hover,
.ppp-theme .button:hover,
.ppp-theme button:hover,
.ppp-theme input[type="submit"]:hover,
.ppp-theme input[type="button"]:hover,
.ppp-theme .wp-block-button__link:hover,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) a.button:hover,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) button.button:hover,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) input.button:hover,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) #respond input#submit:hover,
.ppp-theme .wc-block-components-button:hover,
.ppp-btn:hover {
	background-color: var(--color-button-hover);
	border-color: var(--color-button-hover);
	color: var(--color-button-text);
	box-shadow: var(--shadow-sm);
	transform: translateY(-1px);
}

.ppp-theme .ast-button:active,
.ppp-theme .button:active,
.ppp-theme .wp-block-button__link:active,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) a.button:active,
.ppp-btn:active {
	transform: translateY(0);
	box-shadow: none;
}

/* Woo's "alt" buttons (add to cart, place order) share the primary look */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) a.button.alt,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) button.button.alt,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) input.button.alt,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) #respond input#submit.alt {
	background-color: var(--color-button);
	border-color: var(--color-button);
	color: var(--color-button-text);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) a.button.alt:hover,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) button.button.alt:hover,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) input.button.alt:hover,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) #respond input#submit.alt:hover {
	background-color: var(--color-button-hover);
	border-color: var(--color-button-hover);
}

/* Secondary — outlined on light surfaces */
.ppp-theme .wp-block-button.is-style-outline .wp-block-button__link,
.ppp-theme .ast-outline-button,
.ppp-btn--secondary {
	background-color: transparent;
	color: var(--color-accent);
	border-color: var(--color-border-strong);
	box-shadow: none;
}

.ppp-theme .wp-block-button.is-style-outline .wp-block-button__link:hover,
.ppp-theme .ast-outline-button:hover,
.ppp-btn--secondary:hover {
	background-color: var(--color-accent-soft);
	color: var(--color-accent);
	border-color: var(--color-button);
	box-shadow: none;
}

/* Ghost — for use on the dark bands */
.ppp-btn--ghost {
	background-color: transparent;
	color: var(--color-on-dark);
	border-color: var(--color-on-dark-border);
	box-shadow: none;
}

.ppp-btn--ghost:hover {
	background-color: rgba(255, 255, 255, 0.06);
	border-color: var(--color-on-dark-hover);
	color: var(--color-on-dark);
	box-shadow: none;
}

.ppp-btn--lg {
	padding: 0.95rem 2rem;
	font-size: var(--text-base);
}

.ppp-btn--sm {
	padding: 0.5rem 1rem;
	font-size: var(--text-xs);
}

.ppp-theme button[disabled],
.ppp-theme .button:disabled,
.ppp-theme .button.disabled,
.ppp-theme input[type="submit"]:disabled {
	opacity: 0.5;
	cursor: not-allowed;
	transform: none;
	box-shadow: none;
}

/* --------------------------------------------------------------------------
   5. FORMS
   Restyle only — never restructure markup on plugin or checkout forms.
   -------------------------------------------------------------------------- */

.ppp-theme input[type="text"],
.ppp-theme input[type="email"],
.ppp-theme input[type="url"],
.ppp-theme input[type="password"],
.ppp-theme input[type="search"],
.ppp-theme input[type="number"],
.ppp-theme input[type="tel"],
.ppp-theme input[type="date"],
.ppp-theme select,
.ppp-theme textarea,
.ppp-theme .select2-container .select2-selection--single,
.ppp-theme .wc-block-components-text-input input {
	width: 100%;
	background-color: var(--color-card);
	color: var(--color-text);
	border: 1px solid var(--color-border-strong);
	border-radius: var(--radius-sm);
	padding: 0.7rem 0.9rem;
	font-size: var(--text-base);
	line-height: 1.4;
	box-shadow: none;
	transition: border-color var(--transition-base), box-shadow var(--transition-base);
}

.ppp-theme input::placeholder,
.ppp-theme textarea::placeholder {
	color: var(--color-text-muted);
	opacity: 1;
}

.ppp-theme input:focus,
.ppp-theme select:focus,
.ppp-theme textarea:focus {
	border-color: var(--color-focus);
	box-shadow: 0 0 0 3px var(--color-focus-ring);
	outline: none;
}

.ppp-theme label {
	display: inline-block;
	font-size: var(--text-sm);
	font-weight: var(--weight-medium);
	color: var(--color-text);
	margin-bottom: var(--space-3xs);
}

.ppp-theme textarea {
	min-height: 9rem;
	resize: vertical;
}

/* --------------------------------------------------------------------------
   6. SURFACES & CARDS
   -------------------------------------------------------------------------- */

.ppp-card {
	background-color: var(--color-card);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-lg);
	padding: var(--space-md);
	box-shadow: var(--shadow-sm);
	transition: box-shadow var(--transition-base), transform var(--transition-base),
		border-color var(--transition-base);
}

.ppp-card--interactive:hover,
.ppp-card--interactive:focus-within {
	box-shadow: var(--shadow-lg);
	border-color: var(--color-border-strong);
	transform: translateY(-2px);
}

.ppp-card--flush { padding: 0; overflow: hidden; }
.ppp-card--tint { background-color: var(--color-bg-secondary); box-shadow: none; }

.ppp-surface-alt {
	background-color: var(--color-bg-secondary);
}

/* Small pill label — batch numbers, purity, "research use only" */
.ppp-badge {
	display: inline-flex;
	align-items: center;
	gap: var(--space-3xs);
	background-color: var(--color-accent-soft);
	color: var(--color-accent);
	border: 1px solid var(--color-accent-soft-strong);
	border-radius: var(--radius-pill);
	padding: 0.25rem 0.7rem;
	font-size: var(--text-xs);
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	line-height: 1.4;
}

.ppp-badge--dark {
	background-color: rgba(77, 163, 255, 0.12);
	border-color: var(--color-on-dark-border);
	color: var(--color-glow);
}

/* --------------------------------------------------------------------------
   7. HEADER — DARK BAND
   The logo is glowing blue on black, so every header surface goes dark.
   Selectors cover both the classic header and the Astra header builder;
   Astra's customizer prints inline CSS on `astra-theme-css`, which loads
   before this file, so equal-specificity rules here win.
   -------------------------------------------------------------------------- */

.ppp-theme .site-header,
.ppp-theme .ast-above-header-bar,
.ppp-theme .ast-primary-header-bar,
.ppp-theme .ast-below-header-bar,
.ppp-theme .main-header-bar,
:is(.ppp-theme .ast-header-break-point, .ppp-theme.ast-header-break-point) .main-header-bar,
.ppp-theme #masthead .site-primary-header-wrap,
.ppp-theme #masthead .site-above-header-wrap,
.ppp-theme #masthead .site-below-header-wrap {
	background-color: var(--color-dark);
	background-image: var(--gradient-dark);
	border-bottom-color: var(--color-on-dark-border);
	color: var(--color-on-dark);
}

.ppp-theme .ast-primary-header-bar,
.ppp-theme .main-header-bar {
	border-bottom: 1px solid var(--color-on-dark-border);
}

/* Site title / tagline fallback when no logo image is set */
.ppp-theme .site-title a,
.ppp-theme .site-title a:hover,
.ppp-theme .site-title a:focus,
.ppp-theme .site-description {
	color: var(--color-on-dark);
}

/* Let the helix mark glow against the black band */
.ppp-theme .custom-logo-link img,
.ppp-theme .ast-site-identity img {
	filter: drop-shadow(0 0 14px var(--color-glow-soft));
	transition: filter var(--transition-base);
}

.ppp-theme .custom-logo-link:hover img {
	filter: drop-shadow(0 0 20px var(--color-glow));
}

/* Primary navigation on dark */
.ppp-theme .main-header-menu .menu-link,
.ppp-theme .ast-builder-menu .main-header-menu .menu-link,
.ppp-theme .main-header-menu > .menu-item > .menu-link,
:is(.ppp-theme .ast-header-break-point, .ppp-theme.ast-header-break-point) .main-navigation .menu-link {
	color: var(--color-on-dark-muted);
	font-size: var(--text-sm);
	font-weight: var(--weight-medium);
	letter-spacing: 0.01em;
	transition: color var(--transition-base);
}

.ppp-theme .main-header-menu .menu-item:hover > .menu-link,
.ppp-theme .main-header-menu .current-menu-item > .menu-link,
.ppp-theme .main-header-menu .current-menu-ancestor > .menu-link,
.ppp-theme .ast-builder-menu .main-header-menu .menu-item:hover > .menu-link {
	color: var(--color-on-dark);
}

/* Current page marker — thin glow rule under the active item */
:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu > .menu-item > .menu-link {
	position: relative;
	padding-block: var(--space-2xs);
}

:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu > .menu-item > .menu-link::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 2px;
	border-radius: var(--radius-pill);
	background-color: var(--color-glow);
	opacity: 0;
	transform: scaleX(0.4);
	transition: opacity var(--transition-base), transform var(--transition-base);
}

:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu > .menu-item:hover > .menu-link::after,
:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu > .current-menu-item > .menu-link::after,
:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu > .current-menu-ancestor > .menu-link::after {
	opacity: 1;
	transform: scaleX(1);
}

/* Dropdown submenus stay on the dark surface */
.ppp-theme .main-header-menu .sub-menu,
:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu .astra-megamenu,
.ppp-theme .main-header-menu .children {
	background-color: var(--color-dark-2);
	border: 1px solid var(--color-on-dark-border);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-xl);
	padding-block: var(--space-2xs);
	overflow: hidden;
}

.ppp-theme .main-header-menu .sub-menu .menu-link {
	color: var(--color-on-dark-muted);
	padding-block: var(--space-2xs);
}

.ppp-theme .main-header-menu .sub-menu .menu-item:hover > .menu-link {
	background-color: rgba(255, 255, 255, 0.05);
	color: var(--color-on-dark);
}

/* Header action icons — cart, search, account */
.ppp-theme .ast-header-woo-cart .ast-addon-cart-wrap,
.ppp-theme .ast-header-account-wrap .ast-header-account-link,
.ppp-theme .ast-header-search .astra-search-icon,
.ppp-theme .ast-site-header-cart a,
.ppp-theme .ast-header-social-1-wrap a {
	color: var(--color-on-dark-muted);
	transition: color var(--transition-base);
}

.ppp-theme .ast-header-woo-cart .ast-addon-cart-wrap:hover,
.ppp-theme .ast-header-account-wrap .ast-header-account-link:hover,
.ppp-theme .ast-header-search .astra-search-icon:hover,
.ppp-theme .ast-site-header-cart a:hover,
.ppp-theme .ast-header-social-1-wrap a:hover {
	color: var(--color-on-dark-hover);
}

/* Cart count bubble */
.ppp-theme .ast-site-header-cart .count,
.ppp-theme .ast-addon-cart-wrap .ast-cart-menu-wrap .count {
	background-color: var(--color-button);
	border-color: var(--color-button);
	color: var(--color-button-text);
	border-radius: var(--radius-pill);
}

/* CTA button in the header */
.ppp-theme .ast-header-button-1 .ast-custom-button,
.ppp-theme .site-header .ast-custom-button {
	background-color: var(--color-button);
	color: var(--color-button-text);
	border-radius: var(--radius-sm);
}

.ppp-theme .ast-header-button-1 .ast-custom-button:hover,
.ppp-theme .site-header .ast-custom-button:hover {
	background-color: var(--color-button-hover);
	color: var(--color-button-text);
}

/* Sticky header keeps the dark band and gains a subtle lift */
.ppp-theme .ast-sticky-active .ast-primary-header-bar,
.ppp-theme .ast-sticky-active .main-header-bar,
.ppp-theme #ast-fixed-header .main-header-bar {
	background-color: var(--color-dark);
	background-image: none;
	box-shadow: 0 1px 0 var(--color-on-dark-border), var(--shadow-lg);
	backdrop-filter: saturate(140%) blur(6px);
}

/* --------------------------------------------------------------------------
   8. MOBILE HEADER & OFF-CANVAS MENU
   -------------------------------------------------------------------------- */

.ppp-theme .ast-mobile-header-wrap .ast-primary-header-bar,
.ppp-theme #ast-mobile-header .ast-primary-header-bar {
	background-color: var(--color-dark);
	background-image: var(--gradient-dark);
}

/* Hamburger */
.ppp-theme .ast-mobile-menu-trigger-minimal,
.ppp-theme .ast-mobile-menu-trigger-fill,
.ppp-theme .menu-toggle,
.ppp-theme .ast-button-wrap .menu-toggle {
	background-color: transparent;
	border: 1px solid var(--color-on-dark-border);
	border-radius: var(--radius-sm);
	color: var(--color-on-dark);
	box-shadow: none;
}

.ppp-theme .ast-mobile-menu-trigger-minimal:hover,
.ppp-theme .menu-toggle:hover {
	background-color: rgba(255, 255, 255, 0.06);
	border-color: var(--color-on-dark-hover);
	color: var(--color-on-dark);
	transform: none;
}

.ppp-theme .menu-toggle .ast-mobile-svg,
.ppp-theme .ast-mobile-menu-trigger-minimal .ast-mobile-svg {
	fill: currentColor;
}

/* Off-canvas / dropdown panel */
.ppp-theme .ast-mobile-popup-drawer .ast-mobile-popup-inner,
.ppp-theme .ast-mobile-header-content,
:is(.ppp-theme .ast-header-break-point, .ppp-theme.ast-header-break-point) .ast-mobile-header-wrap .main-header-bar-navigation {
	background-color: var(--color-dark);
	background-image: var(--gradient-dark);
	color: var(--color-on-dark);
}

.ppp-theme .ast-mobile-popup-drawer .menu-item > .menu-link,
:is(.ppp-theme .ast-header-break-point, .ppp-theme.ast-header-break-point) .main-header-menu .menu-link {
	color: var(--color-on-dark-muted);
	border-bottom: 1px solid var(--color-on-dark-border);
}

.ppp-theme .ast-mobile-popup-drawer .menu-item > .menu-link:hover,
:is(.ppp-theme .ast-header-break-point, .ppp-theme.ast-header-break-point) .main-header-menu .menu-link:hover {
	background-color: rgba(255, 255, 255, 0.05);
	color: var(--color-on-dark);
}

.ppp-theme .ast-mobile-popup-drawer .menu-toggle-close,
.ppp-theme .ast-mobile-popup-drawer .ast-mobile-popup-header .menu-toggle-close {
	color: var(--color-on-dark);
	background-color: transparent;
	border: none;
}

/* --------------------------------------------------------------------------
   9. HERO / DARK SECTION UTILITY
   Add `ppp-band--dark` to any section (custom block wrapper, group block)
   to drop it onto the dark ground with correct text colors inside.
   -------------------------------------------------------------------------- */

.ppp-band {
	padding-block: var(--section-y);
}

.ppp-band--tight {
	padding-block: var(--section-y-tight);
}

.ppp-band--alt {
	background-color: var(--color-bg-secondary);
}

.ppp-band--dark {
	position: relative;
	background-color: var(--color-dark);
	background-image: var(--gradient-hero);
	color: var(--color-on-dark);
	isolation: isolate;
}

.ppp-band--dark h1,
.ppp-band--dark h2,
.ppp-band--dark h3,
.ppp-band--dark h4,
.ppp-band--dark h5,
.ppp-band--dark strong {
	color: var(--color-on-dark);
}

.ppp-band--dark p,
.ppp-band--dark li,
.ppp-band--dark .ppp-lead,
.ppp-band--dark .ppp-muted {
	color: var(--color-on-dark-muted);
}

.ppp-band--dark h6,
.ppp-band--dark .ppp-eyebrow {
	color: var(--color-glow);
}

.ppp-band--dark a:not(.ppp-btn):not(.wp-block-button__link) {
	color: var(--color-on-dark);
	text-decoration-color: var(--color-on-dark-border);
}

.ppp-band--dark a:not(.ppp-btn):not(.wp-block-button__link):hover {
	color: var(--color-on-dark-hover);
}

.ppp-band--dark .ppp-card {
	background-color: rgba(255, 255, 255, 0.04);
	border-color: var(--color-on-dark-border);
	box-shadow: none;
	color: var(--color-on-dark-muted);
}

/* Hairline glow rule that separates a dark band from the light page below */
.ppp-band--dark::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	height: 1px;
	background-image: var(--gradient-rule);
	pointer-events: none;
}

.ppp-hero {
	display: flex;
	flex-direction: column;
	justify-content: center;
	min-height: clamp(28rem, 62vh, 40rem);
}

.ppp-hero h1 {
	font-size: var(--text-5xl);
	max-width: 18ch;
}

.ppp-hero .ppp-lead {
	max-width: 52ch;
	margin-top: var(--space-md);
	font-size: var(--text-lg);
}

.ppp-hero-actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-sm);
	margin-top: var(--space-lg);
}

/* --------------------------------------------------------------------------
   10. CONTENT SECTIONS & LAYOUT UTILITIES
   -------------------------------------------------------------------------- */

.ppp-container {
	width: 100%;
	max-width: var(--container-max);
	margin-inline: auto;
	padding-inline: var(--gutter);
}

.ppp-container--wide { max-width: var(--container-wide); }
.ppp-container--narrow { max-width: var(--container-narrow); }

/* Astra's container inherits the token width so custom blocks line up with
   theme templates (shop, single product, plugin pages). */
.ppp-theme .ast-container,
.ppp-theme .ast-container-fluid {
	max-width: var(--container-max);
}

.ppp-section-head {
	max-width: var(--measure);
	margin-bottom: var(--space-xl);
}

.ppp-section-head--center {
	margin-inline: auto;
	text-align: center;
}

/* Auto-fit grid for feature/product/COA cards */
.ppp-grid {
	display: grid;
	gap: var(--space-md);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}

.ppp-grid--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); }
.ppp-grid--tight { gap: var(--space-sm); }

.ppp-stack > * + * { margin-top: var(--space-sm); }
.ppp-stack--lg > * + * { margin-top: var(--space-md); }

.ppp-center { text-align: center; }
.ppp-center .ppp-lead { margin-inline: auto; }

/* Page title area on interior pages */
.ppp-theme .ast-archive-description,
.ppp-theme .entry-header {
	margin-bottom: var(--space-lg);
}

.ppp-theme .ast-breadcrumbs-inner,
.ppp-theme .woocommerce-breadcrumb {
	font-size: var(--text-sm);
	color: var(--color-text-muted);
	margin-bottom: var(--space-md);
}

.ppp-theme .woocommerce-breadcrumb a {
	color: var(--color-text-muted);
	text-decoration: none;
}

.ppp-theme .woocommerce-breadcrumb a:hover {
	color: var(--color-button);
}

/* --------------------------------------------------------------------------
   11. FOOTER — DARK BAND
   -------------------------------------------------------------------------- */

.ppp-theme .site-footer,
.ppp-theme .site-above-footer-wrap,
.ppp-theme .site-primary-footer-wrap,
.ppp-theme .site-below-footer-wrap,
.ppp-theme .ast-small-footer,
.ppp-theme .footer-adv {
	background-color: var(--color-dark);
	background-image: var(--gradient-dark);
	color: var(--color-on-dark-muted);
	border-top: 1px solid var(--color-on-dark-border);
}

.ppp-theme .site-footer h1,
.ppp-theme .site-footer h2,
.ppp-theme .site-footer h3,
.ppp-theme .site-footer h4,
.ppp-theme .site-footer h5,
.ppp-theme .site-footer .widget-title {
	color: var(--color-on-dark);
	font-size: var(--text-sm);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	margin-bottom: var(--space-sm);
}

.ppp-theme .site-footer a {
	color: var(--color-on-dark-muted);
	text-decoration: none;
}

.ppp-theme .site-footer a:hover {
	color: var(--color-on-dark-hover);
}

.ppp-theme .site-below-footer-wrap,
.ppp-theme .ast-small-footer {
	font-size: var(--text-sm);
}

/* Compliance / research-use-only line */
.ppp-theme .site-footer .ppp-disclaimer {
	max-width: var(--measure);
	font-size: var(--text-xs);
	line-height: var(--leading-normal);
	color: var(--color-on-dark-muted);
	opacity: 0.8;
}

/* --------------------------------------------------------------------------
   12. WOOCOMMERCE BASE RESTYLE
   Shop archive + shared chrome. The custom product page comes later; these
   are safe global defaults that never touch markup.
   -------------------------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product,
.ppp-theme .wc-block-grid__product {
	background-color: var(--color-card);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-lg);
	padding: var(--space-sm);
	box-shadow: var(--shadow-sm);
	transition: box-shadow var(--transition-base), transform var(--transition-base),
		border-color var(--transition-base);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product:hover,
.ppp-theme .wc-block-grid__product:hover {
	box-shadow: var(--shadow-lg);
	border-color: var(--color-border-strong);
	transform: translateY(-2px);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product img {
	border-radius: var(--radius-md);
	background-color: var(--color-bg-secondary);
	margin-bottom: var(--space-sm);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .woocommerce-loop-product__title {
	font-size: var(--text-base);
	font-weight: var(--weight-semibold);
	color: var(--color-text);
	line-height: var(--leading-snug);
	padding: 0;
	margin-bottom: var(--space-3xs);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .price,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product p.price,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product span.price {
	color: var(--color-accent);
	font-size: var(--text-base);
	font-weight: var(--weight-semibold);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .price del,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product p.price del {
	color: var(--color-text-muted);
	font-weight: var(--weight-normal);
	opacity: 0.8;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .price ins {
	text-decoration: none;
	color: var(--color-sale);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) span.onsale,
.ppp-theme .wc-block-grid__product-onsale {
	background-color: var(--color-sale);
	color: var(--color-button-text);
	border-radius: var(--radius-pill);
	font-size: var(--text-xs);
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	padding: 0.2rem 0.7rem;
	min-height: 0;
	min-width: 0;
	line-height: 1.6;
}

/* Notices */
.ppp-theme .woocommerce-message,
.ppp-theme .woocommerce-info,
.ppp-theme .woocommerce-error {
	background-color: var(--color-card);
	border: 1px solid var(--color-border);
	border-top: 3px solid var(--color-button);
	border-radius: var(--radius-md);
	box-shadow: var(--shadow-sm);
	color: var(--color-text);
	padding: var(--space-sm) var(--space-md);
}

.ppp-theme .woocommerce-message { border-top-color: var(--color-success); }
.ppp-theme .woocommerce-info { border-top-color: var(--color-button); }
.ppp-theme .woocommerce-error { border-top-color: var(--color-danger); }

.ppp-theme .woocommerce-message::before,
.ppp-theme .woocommerce-info::before { color: var(--color-button); }
.ppp-theme .woocommerce-error::before { color: var(--color-danger); }

/* Pagination */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) nav.woocommerce-pagination ul,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) nav.woocommerce-pagination ul li {
	border: 0;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) nav.woocommerce-pagination ul li a,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) nav.woocommerce-pagination ul li span {
	background-color: var(--color-card);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-sm);
	color: var(--color-text);
	min-width: 2.5rem;
	padding: 0.4rem 0.6rem;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) nav.woocommerce-pagination ul li span.current,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) nav.woocommerce-pagination ul li a:hover {
	background-color: var(--color-button);
	border-color: var(--color-button);
	color: var(--color-button-text);
}

/* Sorting / result count row */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .woocommerce-result-count,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .woocommerce-ordering select {
	font-size: var(--text-sm);
	color: var(--color-text-muted);
}

/* Tabs on the single product page */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-tabs ul.tabs {
	padding: 0;
	margin-bottom: var(--space-lg);
	border-bottom: 1px solid var(--color-border);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-tabs ul.tabs::before,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-tabs ul.tabs li::before,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-tabs ul.tabs li::after {
	display: none;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-tabs ul.tabs li {
	background-color: transparent;
	border: 0;
	border-radius: 0;
	margin: 0;
	padding: 0;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-tabs ul.tabs li a {
	display: block;
	padding: var(--space-xs) var(--space-sm);
	color: var(--color-text-muted);
	font-weight: var(--weight-semibold);
	font-size: var(--text-sm);
	border-bottom: 2px solid transparent;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-tabs ul.tabs li.active a,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-tabs ul.tabs li a:hover {
	color: var(--color-accent);
	border-bottom-color: var(--color-button);
}

/* Quantity input */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .quantity .qty {
	width: 4.5rem;
	text-align: center;
	border-radius: var(--radius-sm);
	border: 1px solid var(--color-border-strong);
	padding: 0.6rem 0.4rem;
}

/* --------------------------------------------------------------------------
   13. LOCKED TEMPLATES — cart / checkout / plugin pages
   CSS ONLY. Nothing here may hide, move, or replace a field the payment
   processor's plugin renders. Surface + spacing polish only.
   -------------------------------------------------------------------------- */

:is(.ppp-locked-template .woocommerce, .ppp-locked-template.woocommerce) table.shop_table,
:is(.ppp-locked-template .woocommerce-checkout, .ppp-locked-template.woocommerce-checkout) #order_review,
.ppp-locked-template .cart-collaterals .cart_totals {
	background-color: var(--color-card);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-lg);
	border-collapse: separate;
	box-shadow: var(--shadow-sm);
	overflow: hidden;
}

:is(.ppp-locked-template .woocommerce, .ppp-locked-template.woocommerce) table.shop_table th,
:is(.ppp-locked-template .woocommerce, .ppp-locked-template.woocommerce) table.shop_table td {
	border-color: var(--color-border);
	padding: var(--space-sm);
}

:is(.ppp-locked-template .woocommerce, .ppp-locked-template.woocommerce) table.shop_table thead th {
	background-color: var(--color-bg-secondary);
	color: var(--color-text);
	font-size: var(--text-sm);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
}

:is(.ppp-locked-template .woocommerce-checkout, .ppp-locked-template.woocommerce-checkout) h3,
.ppp-locked-template .woocommerce-billing-fields h3 {
	font-size: var(--text-xl);
	margin-bottom: var(--space-sm);
}

.ppp-locked-template #payment,
:is(.ppp-locked-template .woocommerce-checkout, .ppp-locked-template.woocommerce-checkout) #payment {
	background-color: var(--color-bg-secondary);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-lg);
}

.ppp-locked-template #payment div.payment_box {
	background-color: var(--color-card);
	border: 1px solid var(--color-border);
	border-radius: var(--radius-md);
	color: var(--color-text-muted);
	font-size: var(--text-sm);
}

.ppp-locked-template #payment div.payment_box::before {
	border-bottom-color: var(--color-card);
}

.ppp-locked-template #place_order {
	width: 100%;
	padding: 1rem 1.5rem;
	font-size: var(--text-base);
}

/* Contact-form and affiliate plugin pages: inherit the input + button rules
   from sections 4 and 5. Only spacing is nudged here. */
.ppp-theme .wpcf7 .wpcf7-form > p,
.ppp-theme .wpforms-field,
.ppp-theme .gform_wrapper .gfield {
	margin-bottom: var(--space-sm);
}

/* --------------------------------------------------------------------------
   14. ACCESSIBILITY & FOCUS
   -------------------------------------------------------------------------- */

.ppp-theme :focus-visible {
	outline: 2px solid var(--color-focus);
	outline-offset: 2px;
	border-radius: var(--radius-xs);
}

.ppp-band--dark :focus-visible,
.ppp-theme .site-header :focus-visible,
.ppp-theme .site-footer :focus-visible {
	outline-color: var(--color-glow);
}

.ppp-theme .skip-link:focus {
	background-color: var(--color-button);
	color: var(--color-button-text);
	border-radius: var(--radius-sm);
	padding: var(--space-2xs) var(--space-sm);
	box-shadow: var(--shadow-lg);
}

.ppp-visually-hidden {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

@media (prefers-reduced-motion: reduce) {
	html { scroll-behavior: auto; }

	.ppp-theme *,
	.ppp-theme *::before,
	.ppp-theme *::after {
		animation-duration: 0.01ms !important;
		animation-iteration-count: 1 !important;
		transition-duration: 0.01ms !important;
	}

	.ppp-theme .ppp-card--interactive:hover,
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product:hover {
		transform: none;
	}
}

/* ==========================================================================
   HOME PAGE SECTION COMPONENTS
   Built from the same tokens. Every one of these is a plain class applied to
   a core Gutenberg block today; when the blocks plugin lands, the markup
   moves but these rules stay untouched.

     15. Trust bar
     16. Process steps
     17. Feature rows
     18. FAQ accordion
     19. Newsletter
     20. CTA band
     21. Featured products
   ========================================================================== */

/* --------------------------------------------------------------------------
   15. TRUST BAR — the three proof points under the hero CTAs
   -------------------------------------------------------------------------- */

.ppp-trustbar {
	display: grid;
	gap: var(--space-md);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 14rem), 1fr));
	margin-top: var(--space-xl);
	padding-top: var(--space-lg);
	border-top: 1px solid var(--color-on-dark-border);
}

.ppp-trustbar__item {
	display: flex;
	flex-direction: column;
	gap: var(--space-3xs);
}

.ppp-trustbar__label {
	font-size: var(--text-xs);
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-widest);
	text-transform: uppercase;
	color: var(--color-glow);
}

.ppp-trustbar__text {
	font-size: var(--text-sm);
	line-height: var(--leading-snug);
	color: var(--color-on-dark-muted);
	margin: 0;
	max-width: 34ch;
}

/* On a light band the same component flips to accent-on-light */
.ppp-band:not(.ppp-band--dark) .ppp-trustbar {
	border-top-color: var(--color-border);
}

.ppp-band:not(.ppp-band--dark) .ppp-trustbar__label {
	color: var(--color-button);
}

.ppp-band:not(.ppp-band--dark) .ppp-trustbar__text {
	color: var(--color-text-muted);
}

/* --------------------------------------------------------------------------
   16. PROCESS STEPS — numbered three-column sequence
   Numbers come from a CSS counter so the markup carries no hard-coded digits
   and reordering blocks in the editor renumbers automatically.
   -------------------------------------------------------------------------- */

.ppp-steps {
	counter-reset: ppp-step;
	display: grid;
	gap: var(--space-lg);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
}

.ppp-step {
	counter-increment: ppp-step;
	position: relative;
	padding-top: var(--space-lg);
	border-top: 2px solid var(--color-border);
}

.ppp-step::before {
	content: counter(ppp-step, decimal-leading-zero);
	position: absolute;
	top: calc(-1 * var(--space-sm));
	left: 0;
	background-color: var(--color-bg);
	padding-right: var(--space-xs);
	font-size: var(--text-sm);
	font-weight: var(--weight-bold);
	letter-spacing: var(--tracking-wide);
	color: var(--color-button);
	line-height: 1;
}

.ppp-step h3,
.ppp-step h4 {
	font-size: var(--text-lg);
	margin-bottom: var(--space-2xs);
}

.ppp-step p {
	font-size: var(--text-sm);
	color: var(--color-text-muted);
	line-height: var(--leading-normal);
	margin: 0;
}

/* Steps on an alt-tinted band need the number's knockout to match */
.ppp-band--alt .ppp-step::before {
	background-color: var(--color-bg-secondary);
}

.ppp-band--dark .ppp-step {
	border-top-color: var(--color-on-dark-border);
}

.ppp-band--dark .ppp-step::before {
	background-color: var(--color-dark);
	color: var(--color-glow);
}

.ppp-band--dark .ppp-step p {
	color: var(--color-on-dark-muted);
}

/* --------------------------------------------------------------------------
   17. FEATURE ROWS — icon-marked supporting points
   The marker is drawn in CSS, so there are no image assets to manage.
   -------------------------------------------------------------------------- */

.ppp-features {
	display: grid;
	gap: var(--space-lg);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
}

.ppp-feature {
	position: relative;
}

.ppp-feature h3,
.ppp-feature h4 {
	font-size: var(--text-md);
	font-weight: var(--weight-semibold);
	margin-bottom: var(--space-3xs);
}

.ppp-feature p {
	font-size: var(--text-sm);
	color: var(--color-text-muted);
	line-height: var(--leading-normal);
	margin: 0;
}

.ppp-band--dark .ppp-feature p {
	color: var(--color-on-dark-muted);
}

/* --------------------------------------------------------------------------
   18. FAQ ACCORDION
   Built on the native <details>/<summary> pair that core's Details block
   outputs — it opens without JavaScript and is keyboard accessible for free.
   -------------------------------------------------------------------------- */

.ppp-faq {
	max-width: var(--container-narrow);
	margin-inline: auto;
	border-top: 1px solid var(--color-border);
}

.ppp-faq .wp-block-details {
	border-bottom: 1px solid var(--color-border);
	padding: 0;
	background: none;
}

.ppp-faq .wp-block-details summary {
	position: relative;
	display: block;
	list-style: none;
	cursor: pointer;
	padding: var(--space-md) calc(var(--space-lg) + var(--space-sm)) var(--space-md) 0;
	font-size: var(--text-md);
	font-weight: var(--weight-semibold);
	color: var(--color-text);
	line-height: var(--leading-snug);
	transition: color var(--transition-base);
}

.ppp-faq .wp-block-details summary::-webkit-details-marker,
.ppp-faq .wp-block-details summary::marker {
	display: none;
	content: "";
}

.ppp-faq .wp-block-details summary:hover {
	color: var(--color-button);
}

/* Plus sign that rotates into a minus when the item opens */
.ppp-faq .wp-block-details summary::before,
.ppp-faq .wp-block-details summary::after {
	content: "";
	position: absolute;
	right: var(--space-2xs);
	top: 50%;
	background-color: var(--color-button);
	transition: transform var(--transition-base), opacity var(--transition-base);
}

.ppp-faq .wp-block-details summary::before {
	width: 14px;
	height: 2px;
	transform: translateY(-50%);
}

.ppp-faq .wp-block-details summary::after {
	width: 2px;
	height: 14px;
	right: calc(var(--space-2xs) + 6px);
	transform: translateY(-50%);
}

.ppp-faq .wp-block-details[open] summary::after {
	transform: translateY(-50%) rotate(90deg);
	opacity: 0;
}

.ppp-faq .wp-block-details[open] summary {
	color: var(--color-accent);
}

.ppp-faq .wp-block-details > *:not(summary) {
	margin-bottom: var(--space-md);
	max-width: var(--measure);
	color: var(--color-text-muted);
	font-size: var(--text-base);
	line-height: var(--leading-relaxed);
}

/* --------------------------------------------------------------------------
   19. NEWSLETTER
   -------------------------------------------------------------------------- */

.ppp-newsletter {
	display: grid;
	gap: var(--space-lg);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
	align-items: center;
}

.ppp-newsletter__form {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2xs);
}

.ppp-newsletter__form input[type="email"] {
	flex: 1 1 14rem;
	min-width: 0;
}

.ppp-newsletter__note {
	font-size: var(--text-xs);
	color: var(--color-text-muted);
	margin-top: var(--space-2xs);
	max-width: 46ch;
}

.ppp-band--dark .ppp-newsletter__note {
	color: var(--color-on-dark-muted);
}

.ppp-band--dark .ppp-newsletter__form input[type="email"] {
	background-color: rgba(255, 255, 255, 0.06);
	border-color: var(--color-on-dark-border);
	color: var(--color-on-dark);
}

.ppp-band--dark .ppp-newsletter__form input[type="email"]::placeholder {
	color: var(--color-on-dark-muted);
}

.ppp-band--dark .ppp-newsletter__form input[type="email"]:focus {
	border-color: var(--color-glow);
	box-shadow: 0 0 0 3px rgba(77, 163, 255, 0.22);
}

/* --------------------------------------------------------------------------
   20. CTA BAND
   -------------------------------------------------------------------------- */

.ppp-cta {
	text-align: center;
	max-width: var(--container-narrow);
	margin-inline: auto;
}

.ppp-cta h2 {
	font-size: var(--text-3xl);
	margin-bottom: var(--space-sm);
}

.ppp-cta p {
	margin-inline: auto;
	margin-bottom: var(--space-lg);
}

.ppp-cta .wp-block-buttons {
	justify-content: center;
}

/* --------------------------------------------------------------------------
   21. FEATURED PRODUCTS
   The `[products]` shortcode renders standard Woo markup, already styled in
   section 12. These rules only handle the home-page context.
   -------------------------------------------------------------------------- */

/* Astra sets the catalog column count from its own customizer value with
   `.ast-container .woocommerce ul.products:not(.elementor-grid)` (0,4,1).
   The featured row is a fixed 4-up regardless of that setting, so this
   selector is built to outrank it (0,5,1) rather than use !important. */
.ppp-theme .ppp-products .woocommerce ul.products,
.ppp-theme .ppp-products .woocommerce ul.products:not(.elementor-grid) {
	margin: 0;
	display: grid;
	gap: var(--space-md);
	grid-template-columns: repeat(4, minmax(0, 1fr));
	float: none;
}

@media (max-width: 1100px) {
	.ppp-theme .ppp-products .woocommerce ul.products,
	.ppp-theme .ppp-products .woocommerce ul.products:not(.elementor-grid) {
		grid-template-columns: repeat(3, minmax(0, 1fr));
	}
}

@media (max-width: 860px) {
	.ppp-theme .ppp-products .woocommerce ul.products,
	.ppp-theme .ppp-products .woocommerce ul.products:not(.elementor-grid) {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

/* Normalize product image height. Source photos vary in proportion, which is
   what makes the row look ragged and oversized. `contain` rather than `cover`
   so nothing gets cropped off a vial label. */
.ppp-theme .ppp-products .woocommerce ul.products li.product img {
	width: 100%;
	aspect-ratio: 1 / 1;
	object-fit: contain;
	background-color: var(--color-bg-secondary);
	padding: var(--space-sm);
}

.ppp-products .woocommerce ul.products::before,
.ppp-products .woocommerce ul.products::after {
	display: none;
}

.ppp-products .woocommerce ul.products li.product {
	width: auto;
	margin: 0;
	float: none;
	display: flex;
	flex-direction: column;
}

.ppp-products .woocommerce ul.products li.product .button {
	margin-top: auto;
	align-self: flex-start;
}

/* Compliance line that has to sit near product listings */
.ppp-research-note {
	font-size: var(--text-xs);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	color: var(--color-text-muted);
	margin-top: var(--space-lg);
	text-align: center;
}

.ppp-band--dark .ppp-research-note {
	color: var(--color-on-dark-muted);
}

/* --------------------------------------------------------------------------
   22. GRID CHILD MARGIN RESET
   Core's flow layout applies `margin-block-start` to every child after the
   first. Correct for stacked blocks, wrong inside a grid — it drops columns
   2+ below column 1. The grid's own `gap` owns spacing here.

   The `.wp-block-group` in each selector lifts specificity above core's
   `.wp-block-group.is-layout-flow > *`; the bare selectors below cover the
   same components once they become custom blocks.
   -------------------------------------------------------------------------- */

.ppp-theme .ppp-grid.wp-block-group > *,
.ppp-theme .ppp-steps.wp-block-group > *,
.ppp-theme .ppp-features.wp-block-group > *,
.ppp-theme .ppp-trustbar.wp-block-group > *,
.ppp-theme .ppp-newsletter.wp-block-group > *,
.ppp-theme .ppp-faq.wp-block-group > *,
.ppp-theme .ppp-split.wp-block-group > *,
.ppp-theme .ppp-split.wp-block-group > .ppp-container > *,
.ppp-grid > *,
.ppp-steps > *,
.ppp-features > *,
.ppp-trustbar > *,
.ppp-split > *,
.ppp-split > .ppp-container > *,
.ppp-newsletter > * {
	margin-block-start: 0;
	margin-block-end: 0;
}

/* Grid items align to the top of their row rather than inheriting a
   stretched baseline from the tallest sibling. */
.ppp-grid,
.ppp-steps,
.ppp-features,
.ppp-trustbar {
	align-items: start;
}

/* Cards stay equal-height — they read as a set, so stretch is correct there. */
.ppp-grid:has(.ppp-card) {
	align-items: stretch;
}

/* --------------------------------------------------------------------------
   23. ASTRA LAYOUT INTEGRATION
   Astra wraps page content in its own spacing assumptions, which double up
   against the band system. These rules hand spacing control back to the
   tokens. Scoped to pages built from bands so ordinary posts and plugin
   pages keep Astra's defaults.
   -------------------------------------------------------------------------- */

/* Astra pads `.entry-content > .wp-block-group` with its own block padding
   vars. Our bands already own their vertical rhythm and delegate horizontal
   gutter to .ppp-container, so zero it out. Specificity clears Astra's
   `.entry-content > .wp-block-group` (0,2,0). */
.ppp-theme .entry-content > .ppp-band,
.ppp-theme .entry-content > .wp-block-group.ppp-band {
	padding-inline: 0;
	padding-block: var(--section-y);
}

.ppp-theme .entry-content > .ppp-band.ppp-band--tight {
	padding-block: var(--section-y-tight);
}

/* The hero sits flush against the dark header so the two read as one field. */
.ppp-theme .entry-content > .ppp-band.ppp-hero {
	padding-block: clamp(var(--space-2xl), 9vw, var(--space-4xl));
}

/* Astra's page-title header duplicates the hero's own h1. Hiding it with
   display:none also removes it from the accessibility tree, so the hero h1
   becomes the page's single heading rather than competing with a second. */
.ppp-theme.home .entry-header,
.ppp-theme .ppp-page-no-title .entry-header {
	display: none;
}

/* Remove the content-area padding that would otherwise show as a light gap
   between the header band and the hero. */
.ppp-theme.home .site-content > .ast-container,
.ppp-theme.home #primary,
.ppp-theme.home .site-main {
	padding-block: 0;
	margin-block: 0;
}

.ppp-theme.home .entry-content {
	margin-block: 0;
}

/* Bands manage their own edges; kill the inherited article gutter so the
   dark bleeds edge to edge. */
.ppp-theme.home .ast-article-single,
.ppp-theme.home .ast-plain-container .ast-article-single {
	padding: 0;
	border: 0;
}

/* Core adds top margin to the first band because it is not the first child
   of .entry-content in every layout. Bands never need it. */
.ppp-theme .entry-content > .ppp-band {
	margin-block: 0;
}

/* --------------------------------------------------------------------------
   24. CENTERING HELPERS
   The global `p { max-width: var(--measure) }` keeps line length readable but
   pins paragraphs to the left edge of their container. Centering a section
   therefore needs auto inline margins on the children, not just text-align.
   -------------------------------------------------------------------------- */

.ppp-section-head--center > *,
.ppp-section-head--center p,
.ppp-section-head--center .ppp-lead {
	margin-inline: auto;
}

/* Optional centered variant of the feature row. The diamond marker moves
   above the heading — a marker anchored to the left reads as broken once the
   text beside it is centered. */
.ppp-features--center .ppp-feature {
	padding-left: 0;
	text-align: center;
}

.ppp-features--center .ppp-feature p,
.ppp-features--center .ppp-feature h3,
.ppp-features--center .ppp-feature h4 {
	margin-inline: auto;
}

/* --------------------------------------------------------------------------
   25. CORE ALIGNMENT CLASSES
   The block toolbar's align-text buttons write `has-text-align-*`. On their
   own they only shift text inside the element's box — and since paragraphs
   carry a max-width for line length, that box stays pinned left and the
   result looks unchanged. Auto margins make the toolbar button behave the
   way the editor preview implies it will.
   -------------------------------------------------------------------------- */

.ppp-theme .has-text-align-center {
	margin-inline: auto;
}

.ppp-theme .has-text-align-right {
	margin-inline-start: auto;
	margin-inline-end: 0;
}

/* A centered heading inside an otherwise left-aligned section head should
   still center its own block. */
.ppp-theme .ppp-section-head .has-text-align-center {
	margin-inline: auto;
}

/* --------------------------------------------------------------------------
   26. HERO — COVER VARIANT
   Core's Cover block supplies the background image and a flat dim overlay.
   We keep the image and replace the dim with the brand gradient so the
   headline holds contrast at the top while the band still resolves to
   --color-dark where the next section meets it.
   -------------------------------------------------------------------------- */

/* Astra pads .entry-content > .wp-block-cover the same way it pads groups. */
.ppp-theme .entry-content > .ppp-hero-cover {
	padding-inline: 0;
	padding-block: 0;
	margin-block: 0;
}

.ppp-theme .ppp-hero-cover {
	min-height: clamp(30rem, 76vh, 46rem);
	align-items: center;
}

.ppp-theme .ppp-hero-cover .wp-block-cover__image-background {
	object-fit: cover;
}

/* Override the flat dim. Darker top and bottom keeps text legible over a busy
   photo; the radial tint echoes the glow in the helix mark. */
.ppp-theme .ppp-hero-cover .wp-block-cover__background {
	background-color: transparent;
	background-image:
		radial-gradient(115% 80% at 50% 6%, rgba(36, 89, 184, 0.40) 0%, rgba(11, 18, 32, 0) 58%),
		linear-gradient(
			180deg,
			rgba(11, 18, 32, 0.88) 0%,
			rgba(11, 18, 32, 0.62) 42%,
			rgba(16, 27, 46, 0.97) 100%
		);
	opacity: 1;
}

.ppp-theme .ppp-hero-cover .wp-block-cover__inner-container {
	width: 100%;
	max-width: var(--container-max);
	margin-inline: auto;
	padding-inline: var(--gutter);
}

.ppp-theme .ppp-hero-cover h1 {
	font-size: var(--text-5xl);
	max-width: 20ch;
	margin-inline: auto;
}

.ppp-theme .ppp-hero-cover .ppp-eyebrow {
	color: var(--color-glow);
}

/* --------------------------------------------------------------------------
   27. HERO BULLETS
   A core List block. Three across on desktop, stacking as space runs out.
   Each marker sits inline with its text so the row still reads correctly
   when the hero is centered.
   -------------------------------------------------------------------------- */

.ppp-theme .ppp-hero-bullets {
	display: grid;
	gap: var(--space-sm) var(--space-md);
	grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
	margin-block: var(--space-lg);
	padding: 0;
	max-width: none;
	list-style: none;
}

.ppp-theme .ppp-hero-bullets li {
	display: flex;
	align-items: flex-start;
	justify-content: center;
	gap: var(--space-2xs);
	margin: 0;
	text-align: left;
	font-size: var(--text-lg);
	font-weight: var(--weight-semibold);
	line-height: var(--leading-snug);
	color: var(--color-on-dark);
}

.ppp-theme .ppp-hero-bullets li::marker {
	content: "";
}

.ppp-theme .ppp-hero-bullets li::before {
	content: "";
	flex: 0 0 auto;
	width: 1.05rem;
	height: 1.05rem;
	margin-top: 0.32em;
	border-radius: var(--radius-xs);
	border: 1px solid var(--color-glow);
	background-color: rgba(77, 163, 255, 0.20);
	box-shadow: 0 0 14px var(--color-glow-soft);
	transform: rotate(45deg);
}

/* Left-aligned hero variant: drop the centering without touching markup. */
.ppp-theme .ppp-hero-cover.ppp-hero--left .ppp-hero-bullets li {
	justify-content: flex-start;
}

.ppp-theme .ppp-hero-cover.ppp-hero--left h1 {
	margin-inline: 0;
}

/* --------------------------------------------------------------------------
   28. PRODUCT CARDS
   Built against Astra Pro's loop markup, emitted by both the shop archive and
   the [products] shortcode:

     li.product
       .astra-shop-thumbnail-wrap     image + hover action
       .astra-shop-summary-wrap       category, title, price, add-to-cart

   Stack: category, name, price, then a full-width action button pinned to the
   bottom edge. The button is pushed down with `margin-top: auto` so those
   bottom rows align across the catalog no matter how long each title runs.

   Woo marks context two ways — wrapper div for shortcodes, body class for
   archives — so :is() covers both branches at one specificity.
   -------------------------------------------------------------------------- */

/* Woo still ships its float-era margins on li.product. As grid items those
   margins survive and offset individual cards within the row, which reads as
   one card sitting higher than its neighbours. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products {
	align-items: stretch;
	align-content: start;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product {
	position: relative;
	display: flex;
	flex-direction: column;
	align-self: stretch;
	height: 100%;
	overflow: hidden;
	margin: 0;
	padding: 0;
	border: 1px solid var(--color-border-navy);
	border-radius: var(--radius-lg);
	background-color: var(--color-card);
	box-shadow: var(--shadow-sm);
	transition: box-shadow var(--transition-base), transform var(--transition-base),
		border-color var(--transition-base);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product:hover {
	border-color: var(--color-border-navy-strong);
	box-shadow: var(--shadow-lg);
	transform: translateY(-3px);
}

/* ---- image panel -------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-thumbnail-wrap {
	position: relative;
	margin: 0;
	padding: var(--space-sm);
	background-color: var(--color-card);
	border-bottom: 1px solid var(--color-border-navy);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-thumbnail-wrap img {
	display: block;
	width: 100%;
	margin: 0;
	padding: 0;
	border: 0;
	border-radius: var(--radius-sm);
	background: none;
	box-shadow: none;
	aspect-ratio: 1 / 1;
	object-fit: contain;
}

/* ---- content area ------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap {
	flex: 1;
	display: flex;
	flex-direction: column;
	align-items: stretch;
	gap: var(--space-3xs);
	padding: var(--space-sm) var(--space-sm) var(--space-sm);
	text-align: left;
}

/* Category eyebrow — needs "Category" enabled in Astra's Shop Product
   Structure, and products actually assigned to a category. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .ast-woo-product-category {
	font-size: var(--text-xs);
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	color: var(--color-button);
	line-height: 1.4;
}

/* Collapse the slot entirely when empty so it costs no vertical space. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .ast-woo-product-category:empty {
	display: none;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .ast-loop-product__link {
	text-decoration: none;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .woocommerce-loop-product__title {
	margin: 0;
	padding: 0;
	font-size: var(--text-base);
	font-weight: var(--weight-semibold);
	line-height: var(--leading-snug);
	letter-spacing: 0;
	color: var(--color-text);
	transition: color var(--transition-base);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product:hover .woocommerce-loop-product__title {
	color: var(--color-accent);
}

/* Title row: name on the left, meta value beside it. The meta is a product
   attribute when one exists, otherwise a trimmed short description; it is
   spliced in after the title by ppp_card_meta_buffer_end().

   Baseline alignment keeps the small meta text sitting on the same line as
   the name rather than centred against its cap height. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .ast-loop-product__link {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--space-3xs) var(--space-2xs);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .ppp-card-meta {
	margin: 0;
	font-size: var(--text-xs);
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	line-height: var(--leading-snug);
	color: var(--color-text-muted);
	white-space: nowrap;
}

.ppp-band--dark ul.products li.product .ppp-card-meta {
	color: var(--color-on-dark-muted);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .price {
	margin: 0;
	font-size: var(--text-md);
	font-weight: var(--weight-semibold);
	letter-spacing: 0;
	color: var(--color-text);
	line-height: 1.35;
}

/* Struck-through original price sits inline before the sale price rather than
   stacking into its own line. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .price del {
	display: inline;
	margin-right: var(--space-3xs);
	font-size: var(--text-sm);
	font-weight: var(--weight-normal);
	color: var(--color-text-muted);
	opacity: 1;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .price ins {
	display: inline;
	text-decoration: none;
	font-weight: var(--weight-semibold);
	color: var(--color-text);
}

/* Full-width action bar pinned to the card's bottom edge. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap .button,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap .added_to_cart {
	display: flex;
	width: 100%;
	margin-top: auto;
	padding: 0.6rem 0.75rem;
	background-color: var(--color-accent);
	border-color: var(--color-accent);
	border-radius: var(--radius-sm);
	font-size: var(--text-xs);
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	text-align: center;
	white-space: normal;
	box-shadow: none;
}

/* Gap above the button only, so name and price stay tight together. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap .button {
	margin-top: auto;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap > .button:not(:first-child) {
	margin-top: var(--space-sm);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap .button:hover {
	background-color: var(--color-text);
	border-color: var(--color-text);
	transform: none;
	box-shadow: none;
}

/* Sale flag rides the image panel's top-left corner. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product span.onsale {
	position: absolute;
	top: var(--space-2xs);
	left: var(--space-2xs);
	right: auto;
	margin: 0;
	padding: 0.15rem 0.6rem;
	font-size: var(--text-xs);
	z-index: var(--z-raised);
}

/* Dark-band variant, for a featured row on a dark section later. */
.ppp-band--dark ul.products li.product {
	background-color: rgba(255, 255, 255, 0.04);
	border-color: var(--color-on-dark-border);
	box-shadow: none;
}

.ppp-band--dark ul.products li.product .astra-shop-thumbnail-wrap {
	background-color: rgba(255, 255, 255, 0.05);
	border-bottom-color: var(--color-on-dark-border);
}

.ppp-band--dark ul.products li.product .woocommerce-loop-product__title,
.ppp-band--dark ul.products li.product .price {
	color: var(--color-on-dark);
}

.ppp-band--dark ul.products li.product .ast-woo-product-category {
	color: var(--color-glow);
}

/* --------------------------------------------------------------------------
   29. SINGLE PRODUCT PAGE
   Woo's default single-product layout is two floated halves with loose,
   unstructured copy. This rebuilds it as a grid: a framed image column with a
   reference card beneath, and a summary column that leads with the spec data
   a researcher actually compares.

   Markup targeted (Astra + Woo):
     div.product
       .woocommerce-product-gallery      image
       .ppp-product-info-card            appended beneath the gallery
       .summary.entry-summary            category, title, price, specs,
                                         blurb, stock, add-to-cart form
   -------------------------------------------------------------------------- */

@media (min-width: 921px) {
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product {
		display: grid;
		grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
		column-gap: var(--space-2xl);
		align-items: start;
	}

	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-product-gallery {
		grid-column: 1;
		width: 100%;
		float: none;
		margin: 0;
	}

	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .ppp-product-info-card {
		grid-column: 1;
	}

	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary.entry-summary {
		grid-column: 2;
		grid-row: 1 / span 2;
		width: 100%;
		float: none;
		margin: 0;
	}

	/* Tabs, related products and anything else span the full width below.
	   The dossier belongs in this list: without it the grid auto-places it in
	   column one, under the gallery, which is why it sat left instead of
	   running under both columns. */
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product > .woocommerce-tabs,
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product > .related,
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product > .upsells {
		grid-column: 1 / -1;
		margin-top: var(--space-2xl);
	}

	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product > .ppp-dossier {
		grid-column: 1 / -1;
		grid-row: auto;
	}
}

/* ---- image column ------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-product-gallery {
	background-color: var(--color-bg-secondary);
	border: 1px solid var(--color-border-navy);
	border-radius: var(--radius-lg);
	padding: var(--space-lg);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-product-gallery img {
	border-radius: var(--radius-md);
	mix-blend-mode: multiply;
}

/* Reference card under the image. */
.ppp-product-info-card {
	margin-top: var(--space-md);
	padding: var(--space-md);
	background-color: var(--color-card);
	border: 1px solid var(--color-border-navy);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-sm);
}

.ppp-product-info-row + .ppp-product-info-row {
	margin-top: var(--space-md);
	padding-top: var(--space-md);
	border-top: 1px solid var(--color-border);
}

.ppp-product-info-row h3 {
	margin: 0 0 var(--space-3xs);
	font-size: var(--text-base);
	font-weight: var(--weight-semibold);
	color: var(--color-text);
}

.ppp-product-info-row p {
	margin: 0;
	font-size: var(--text-sm);
	line-height: var(--leading-normal);
	color: var(--color-text-muted);
}

/* ---- summary column ----------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .single-product-category {
	display: block;
	margin-bottom: var(--space-2xs);
	font-size: var(--text-xs);
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-widest);
	text-transform: uppercase;
	color: var(--color-button);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .single-product-category:empty {
	display: none;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .product_title {
	margin: 0 0 var(--space-2xs);
	font-size: var(--text-3xl);
	font-weight: var(--weight-bold);
	letter-spacing: var(--tracking-tight);
	color: var(--color-text);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product p.price,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product span.price {
	margin: 0 0 var(--space-md);
	font-size: var(--text-2xl);
	font-weight: var(--weight-bold);
	letter-spacing: var(--tracking-tight);
	color: var(--color-text);
}

/* ---- batch + spec pills ------------------------------------------------- */

.ppp-product-specs {
	margin-bottom: var(--space-md);
	padding-bottom: var(--space-md);
	border-bottom: 1px solid var(--color-border);
}

.ppp-product-batch {
	margin: 0 0 var(--space-2xs);
	font-size: var(--text-xs);
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	color: var(--color-text-muted);
}

.ppp-product-batch span {
	color: var(--color-text-muted);
	margin-right: var(--space-3xs);
}

.ppp-spec-pills {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2xs);
	margin: 0;
	padding: 0;
	list-style: none;
	max-width: none;
}

.ppp-spec-pill {
	display: inline-flex;
	align-items: baseline;
	gap: var(--space-3xs);
	margin: 0;
	padding: 0.3rem 0.75rem;
	border: 1px solid var(--color-accent-soft-strong);
	border-radius: var(--radius-pill);
	background-color: var(--color-accent-soft);
	font-size: var(--text-xs);
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	line-height: 1.5;
	color: var(--color-accent);
}

.ppp-spec-pill__label {
	opacity: 0.7;
}

.ppp-spec-pill__label::after {
	content: ":";
}

/* ---- description card --------------------------------------------------- */

.ppp-product-blurb {
	margin-bottom: var(--space-md);
	padding: var(--space-md);
	background-color: var(--color-bg-secondary);
	border: 1px solid var(--color-border);
	border-left: 3px solid var(--color-button);
	border-radius: var(--radius-md);
}

.ppp-product-blurb p {
	margin: 0;
	max-width: none;
	font-size: var(--text-base);
	line-height: var(--leading-normal);
	color: var(--color-text);
}

.ppp-product-blurb p + p {
	margin-top: var(--space-2xs);
}

/* ---- stock pill --------------------------------------------------------- */

.ppp-stock-pill {
	display: inline-flex;
	align-items: center;
	gap: var(--space-3xs);
	margin: 0 0 var(--space-md);
	padding: 0.3rem 0.85rem;
	border-radius: var(--radius-pill);
	font-size: var(--text-xs);
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	line-height: 1.5;
}

.ppp-stock-pill::before {
	content: "";
	width: 0.5rem;
	height: 0.5rem;
	border-radius: 50%;
	background-color: currentColor;
}

.ppp-stock-pill--in {
	background-color: rgba(27, 127, 90, 0.10);
	border: 1px solid rgba(27, 127, 90, 0.28);
	color: var(--color-success);
}

.ppp-stock-pill--out {
	background-color: rgba(179, 38, 30, 0.08);
	border: 1px solid rgba(179, 38, 30, 0.25);
	color: var(--color-danger);
}

/* Woo's own stock line is redundant once the pill is present. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .stock {
	display: none;
}

/* ---- add to cart -------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product form.cart {
	display: flex;
	flex-wrap: wrap;
	align-items: stretch;
	gap: var(--space-2xs);
	margin: 0 0 var(--space-md);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product form.cart .quantity {
	margin: 0;
	flex: 0 0 auto;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product form.cart .quantity .qty {
	height: 100%;
	min-height: 3rem;
	width: 5rem;
	border-radius: var(--radius-sm);
	border: 1px solid var(--color-border-navy);
	background-color: var(--color-card);
	font-size: var(--text-base);
	font-weight: var(--weight-semibold);
	text-align: center;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .single_add_to_cart_button {
	flex: 1 1 12rem;
	min-height: 3rem;
	padding: 0.85rem 1.75rem;
	background-color: var(--color-accent);
	border-color: var(--color-accent);
	border-radius: var(--radius-sm);
	font-size: var(--text-sm);
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .single_add_to_cart_button:hover {
	background-color: var(--color-text);
	border-color: var(--color-text);
}

/* ---- meta + tabs -------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .product_meta {
	padding-top: var(--space-md);
	border-top: 1px solid var(--color-border);
	font-size: var(--text-sm);
	color: var(--color-text-muted);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .product_meta:empty {
	display: none;
	border: 0;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) table.shop_attributes {
	width: 100%;
	border: 1px solid var(--color-border);
	border-radius: var(--radius-md);
	border-collapse: separate;
	border-spacing: 0;
	overflow: hidden;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) table.shop_attributes th,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) table.shop_attributes td {
	padding: var(--space-xs) var(--space-sm);
	border-bottom: 1px solid var(--color-border);
	font-size: var(--text-sm);
	text-align: left;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) table.shop_attributes th {
	width: 30%;
	background-color: var(--color-bg-secondary);
	font-weight: var(--weight-semibold);
	color: var(--color-text);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) table.shop_attributes td p {
	margin: 0;
}

/* --------------------------------------------------------------------------
   30. SINGLE PRODUCT — SIZING CORRECTIONS
   Overrides section 29 where it was wrong: the gallery never filled its grid
   column because Woo's gallery script writes inline widths onto the wrapper,
   and the summary type was set a full step too large.
   -------------------------------------------------------------------------- */

/* Gallery fills its column. Woo's flexslider sets inline widths on the wrapper
   and slides, so each level is pinned explicitly rather than relying on the
   parent alone. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-product-gallery,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product div.images {
	width: 100%;
	max-width: none;
	float: none;
	margin: 0;
	padding: var(--space-md);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-product-gallery__wrapper,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-product-gallery__image {
	width: 100%;
	max-width: none;
	margin: 0;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-product-gallery__image img,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-product-gallery img {
	width: 100%;
	height: auto;
	max-width: none;
	object-fit: contain;
}

/* ---- type scale --------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .product_title {
	font-size: var(--text-2xl);
	margin-bottom: var(--space-xs);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product p.price,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product span.price {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	gap: var(--space-2xs);
	margin-bottom: var(--space-md);
	font-size: var(--text-xl);
}

/* Original price sits beside the sale price rather than stacking above it at
   the same weight, which read as two competing prices. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product p.price del,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product span.price del {
	order: 2;
	font-size: var(--text-base);
	font-weight: var(--weight-normal);
	color: var(--color-text-muted);
	opacity: 1;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product p.price ins,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product span.price ins {
	order: 1;
	text-decoration: none;
	font-weight: var(--weight-bold);
	color: var(--color-text);
}

/* Tighter blocks now that specs and description sit directly under the name. */
.ppp-product-specs {
	margin-bottom: var(--space-sm);
	padding-bottom: var(--space-sm);
}

.ppp-product-blurb {
	margin-bottom: var(--space-md);
	padding: var(--space-sm) var(--space-md);
}

.ppp-product-blurb p {
	font-size: var(--text-sm);
}

.ppp-stock-pill {
	margin-bottom: var(--space-sm);
}

/* --------------------------------------------------------------------------
   31. SINGLE PRODUCT — GALLERY WIDTH, DISCOUNTS, DENSITY
   Astra pins the gallery with

     .woocommerce .ast-woocommerce-container div.product div.images  (0,4,2)

   which tied with section 30's rule. On a tie the later sheet wins, and
   WordPress.com's CSS concatenator does not reliably order our bundle after
   Astra's inline block — so the gallery kept rendering at 50% of its grid
   column. Including .ast-woocommerce-container here scores 0,5,2 and settles
   it outright rather than relying on source order.
   -------------------------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .ast-woocommerce-container div.product div.images,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .ast-woocommerce-container div.product .woocommerce-product-gallery,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) #content .ast-woocommerce-container div.product div.images {
	width: 100%;
	max-width: none;
	float: none;
	margin: 0;
	padding: var(--space-sm);
}

/* ---- density ------------------------------------------------------------ */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary.entry-summary > * {
	margin-bottom: var(--space-sm);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary.entry-summary > *:last-child {
	margin-bottom: 0;
}

/* Anything Astra leaves behind with no content should not hold space. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary.entry-summary > :empty,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-product-details__short-description:empty {
	display: none;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product p.price,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product span.price {
	margin-bottom: var(--space-sm);
}

.ppp-product-specs {
	margin-bottom: var(--space-sm);
	padding-bottom: 0;
	border-bottom: 0;
}

/* ---- quantity discounts ------------------------------------------------- */

.ppp-discount-card {
	padding: var(--space-sm) var(--space-md);
	background-color: var(--color-card);
	border: 1px solid var(--color-border-navy);
	border-radius: var(--radius-lg);
}

.ppp-discount-card__title {
	margin: 0 0 var(--space-3xs);
	font-size: var(--text-base);
	font-weight: var(--weight-semibold);
	color: var(--color-text);
}

.ppp-discount-card__note {
	margin: 0 0 var(--space-xs);
	font-size: var(--text-sm);
	color: var(--color-text-muted);
}

.ppp-discount-tiers {
	display: flex;
	flex-direction: column;
	gap: var(--space-3xs);
	margin: 0;
	padding: 0;
	list-style: none;
	max-width: none;
}

.ppp-discount-tier {
	display: flex;
	align-items: center;
	gap: var(--space-2xs);
	margin: 0;
	padding: var(--space-2xs) var(--space-xs);
	border-radius: var(--radius-sm);
	background-color: var(--color-bg-secondary);
	font-size: var(--text-sm);
}

.ppp-discount-tier__label {
	font-weight: var(--weight-semibold);
	color: var(--color-text);
}

/* Pushes the saving to the right edge regardless of whether a flag is set. */
.ppp-discount-tier__value {
	margin-left: auto;
	font-weight: var(--weight-semibold);
	color: var(--color-accent);
	white-space: nowrap;
}

.ppp-discount-flag {
	padding: 0.1rem 0.5rem;
	border: 1px solid var(--color-accent-soft-strong);
	border-radius: var(--radius-pill);
	background-color: var(--color-card);
	font-size: var(--text-xs);
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	color: var(--color-button);
	white-space: nowrap;
}

/* --------------------------------------------------------------------------
   32. CARD PILLS
   Same pill vocabulary as the product page, scaled down for the grid.
   -------------------------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .ppp-card-pills {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-3xs);
	margin: var(--space-3xs) 0 0;
	padding: 0;
	list-style: none;
	max-width: none;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .ppp-card-pills .ppp-spec-pill {
	padding: 0.15rem 0.5rem;
	font-size: 0.6875rem;
	letter-spacing: 0.02em;
	line-height: 1.5;
}

/* Label is dropped on cards — at this size the value alone reads faster, and
   three "STRENGTH: 10MG" pills would wrap past the card edge. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .ppp-card-pills .ppp-spec-pill__label {
	display: none;
}

.ppp-band--dark ul.products li.product .ppp-card-pills .ppp-spec-pill {
	background-color: rgba(77, 163, 255, 0.12);
	border-color: var(--color-on-dark-border);
	color: var(--color-glow);
}

/* --------------------------------------------------------------------------
   33. PRODUCT SUMMARY WIDTH + DISCOUNT TIER OVERFLOW
   The 50/50 split left the summary too narrow once the discount panel landed
   in it — the heading wrapped and, on the flagged tier, "Save 10%" overflowed
   outside its own background. Flex items default to min-width:auto, so the
   label and flag refused to shrink and pushed the value past the row's edge.
   -------------------------------------------------------------------------- */

@media (min-width: 921px) {
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product {
		/* Image reads fine a little narrower; the summary carries far more. */
		grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
		column-gap: var(--space-xl);
	}
}

.ppp-discount-tier {
	flex-wrap: wrap;
	row-gap: var(--space-3xs);
}

/* min-width:0 lets the label shrink instead of forcing the row wider than
   its container. */
.ppp-discount-tier__label {
	flex: 1 1 auto;
	min-width: 0;
}

.ppp-discount-flag {
	flex: 0 0 auto;
}

.ppp-discount-tier__value {
	flex: 0 0 auto;
	margin-left: auto;
}

.ppp-discount-card__title {
	text-wrap: balance;
}

/* --------------------------------------------------------------------------
   34. CARD PILL LABELS
   Section 32 hid the attribute name on cards to save width. With the labels
   restored, the pills need to be able to wrap rather than run past the card.
   -------------------------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .ppp-card-pills .ppp-spec-pill__label {
	display: inline;
	opacity: 0.65;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .ppp-card-pills .ppp-spec-pill {
	max-width: 100%;
	white-space: normal;
}

/* --------------------------------------------------------------------------
   35. DISCOUNT CARD — SIZING CORRECTIONS
   `.ppp-discount-card__title` (0,1,0) lost to the global `.ppp-theme h3`
   (0,1,1), so the heading rendered at --text-2xl and wrapped to two lines,
   which is what made the panel look cramped rather than the column width.
   Element-qualified selectors here clear the global rule outright.

   The single product page also gets the wide container: its summary carries
   specs, description, discounts, stock and the cart, and 1200px was tight.
   -------------------------------------------------------------------------- */

.ppp-theme .ppp-discount-card h3.ppp-discount-card__title {
	margin: 0 0 var(--space-3xs);
	font-size: var(--text-base);
	font-weight: var(--weight-semibold);
	line-height: var(--leading-snug);
	letter-spacing: 0;
	color: var(--color-text);
	text-wrap: nowrap;
}

.ppp-theme .ppp-discount-card p.ppp-discount-card__note {
	margin: 0 0 var(--space-sm);
	max-width: none;
	font-size: var(--text-sm);
	line-height: var(--leading-snug);
	color: var(--color-text-muted);
}

/* Wider page container on single products only. */
.ppp-theme.single-product .ast-container,
.ppp-theme.single-product .ast-woocommerce-container {
	max-width: var(--container-wide);
}

@media (min-width: 921px) {
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product {
		grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
	}
}

/* Tier rows: tighter type and a smaller flag so a flagged row stays on one
   line at the widths the panel actually gets. */
.ppp-theme .ppp-discount-tier {
	padding: var(--space-2xs) var(--space-sm);
	font-size: var(--text-sm);
}

.ppp-theme .ppp-discount-flag {
	padding: 0.1rem 0.45rem;
	font-size: 0.6875rem;
	letter-spacing: 0.03em;
}

.ppp-theme .ppp-discount-tier__label,
.ppp-theme .ppp-discount-tier__value {
	white-space: nowrap;
}

/* --------------------------------------------------------------------------
   36. PILL UNIFORMITY
   Pills were rendering at inconsistent heights and sitting out of line with
   each other. Two causes, both inherited rather than declared:

     · the global `li + li { margin-top }` from section 2 applies to every pill
       after the first, pushing it down inside the flex row
     · `align-items: baseline` sizes the box around the tallest baseline box,
       so a value with different metrics (digits and % versus plain letters)
       produced a taller pill than its neighbours

   Centre alignment plus an explicit line-height and min-height makes every
   pill resolve to the same box regardless of its contents.
   -------------------------------------------------------------------------- */

.ppp-theme .ppp-spec-pills,
.ppp-theme .ppp-card-pills {
	align-items: center;
}

.ppp-theme .ppp-spec-pills > li,
.ppp-theme .ppp-card-pills > li,
.ppp-theme .ppp-spec-pills > li + li,
.ppp-theme .ppp-card-pills > li + li {
	margin: 0;
}

.ppp-theme .ppp-spec-pill {
	align-items: center;
	min-height: 1.75rem;
	font-size: var(--text-xs);
	line-height: 1.4;
}

/* Label and value must share metrics or the pill grows around the larger of
   the two. Only opacity separates them. */
.ppp-theme .ppp-spec-pill__label,
.ppp-theme .ppp-spec-pill__value {
	font-size: inherit;
	line-height: inherit;
	letter-spacing: inherit;
}

.ppp-theme .ppp-spec-pill__label {
	opacity: 0.65;
	font-weight: var(--weight-semibold);
}

.ppp-theme .ppp-spec-pill__value {
	font-weight: var(--weight-semibold);
}

/* Card pills keep their smaller scale, still uniform among themselves. */
.ppp-theme ul.products li.product .ppp-card-pills .ppp-spec-pill {
	min-height: 1.4rem;
	font-size: 0.6875rem;
	line-height: 1.4;
}

/* --------------------------------------------------------------------------
   37. SINGLE PRODUCT — MOBILE ORDER
   The info card is hooked before the summary so the desktop grid can place it
   under the image. Below the grid breakpoint that DOM order surfaces
   literally, dropping shipping and compliance text between the photo and the
   product name — ahead of the price and the buy button.

   Flex ordering moves it after the cart on narrow screens without changing
   where it renders in the markup.
   -------------------------------------------------------------------------- */

@media (max-width: 920px) {
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product {
		display: flex;
		flex-direction: column;
	}

	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-product-gallery,
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product div.images {
		order: 1;
	}

	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary.entry-summary {
		order: 2;
	}

	/* Documentation follows the buy button: a batch number matters once the
	   thing is worth buying, not before the price. Without an order of its own
	   it defaults to 0 and jumps ahead of the photograph. */
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product > .ppp-dossier {
		order: 3;
	}

	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .ppp-product-info-card {
		order: 4;
		margin-top: var(--space-lg);
	}

	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product > .woocommerce-tabs {
		order: 5;
	}

	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product > .related,
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product > .upsells {
		order: 6;
	}
}

/* --------------------------------------------------------------------------
   38. COA MODAL
   Native <dialog>. The browser supplies the backdrop, ESC-to-close and focus
   trapping; these rules only handle size, chrome and the viewer area.
   -------------------------------------------------------------------------- */

.ppp-coa-button {
	width: 100%;
	justify-content: center;
}

.ppp-coa-dialog {
	width: min(56rem, calc(100vw - 2 * var(--space-md)));
	max-width: none;
	/* Fixed height so the PDF viewer has something to fill; without it an
	   iframe collapses to its default 150px. */
	height: min(85vh, 60rem);
	max-height: none;
	padding: 0;
	border: 1px solid var(--color-border-navy);
	border-radius: var(--radius-lg);
	background-color: var(--color-card);
	color: var(--color-text);
	box-shadow: var(--shadow-xl);
	overflow: hidden;
}

.ppp-coa-dialog::backdrop {
	background-color: rgba(11, 18, 32, 0.62);
	backdrop-filter: blur(2px);
}

/* Grid rather than flex so the body absorbs all remaining height and the
   header and footer keep their intrinsic size. */
.ppp-coa-dialog[open] {
	display: grid;
	grid-template-rows: auto minmax(0, 1fr) auto;
}

.ppp-coa-dialog__head {
	display: flex;
	align-items: center;
	gap: var(--space-sm);
	padding: var(--space-sm) var(--space-md);
	border-bottom: 1px solid var(--color-border);
	background-color: var(--color-bg-secondary);
}

.ppp-theme .ppp-coa-dialog h2.ppp-coa-dialog__title {
	flex: 1 1 auto;
	min-width: 0;
	margin: 0;
	font-size: var(--text-base);
	font-weight: var(--weight-semibold);
	line-height: var(--leading-snug);
	letter-spacing: 0;
	color: var(--color-text);
}

.ppp-theme .ppp-coa-dialog__close {
	flex: 0 0 auto;
	width: 2rem;
	height: 2rem;
	padding: 0;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	background-color: transparent;
	border: 1px solid var(--color-border);
	border-radius: var(--radius-sm);
	color: var(--color-text-muted);
	font-size: 1.25rem;
	line-height: 1;
	box-shadow: none;
}

.ppp-theme .ppp-coa-dialog__close:hover {
	background-color: var(--color-bg-secondary);
	border-color: var(--color-border-strong);
	color: var(--color-text);
	transform: none;
}

.ppp-coa-dialog__body {
	min-height: 0;
	overflow: auto;
	background-color: var(--color-bg-secondary);
}

.ppp-coa-dialog__frame {
	display: block;
	width: 100%;
	height: 100%;
	border: 0;
}

.ppp-coa-dialog__image {
	display: block;
	width: 100%;
	height: auto;
	margin: 0 auto;
}

.ppp-coa-dialog__foot {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--space-sm);
	padding: var(--space-xs) var(--space-md);
	border-top: 1px solid var(--color-border);
	background-color: var(--color-card);
}

/* Phones: full-bleed sheet, and the iframe gets a hard height because iOS
   Safari sizes PDF iframes to their content rather than their container. */
@media (max-width: 600px) {
	.ppp-coa-dialog {
		width: 100vw;
		height: 100vh;
		max-height: 100vh;
		border-radius: 0;
		border: 0;
	}

	.ppp-coa-dialog__frame {
		min-height: 60vh;
	}
}

/* --------------------------------------------------------------------------
   39. COA BUTTON + MODAL FOOTER ACTIONS
   Verification sits in the dialog footer beside Close, not on the product
   page — it is a check on the certificate, so it belongs next to it.
   -------------------------------------------------------------------------- */

.ppp-coa-actions {
	display: flex;
	margin-bottom: var(--space-sm);
}

.ppp-theme .ppp-coa-actions > .ppp-btn {
	flex: 1 1 auto;
	justify-content: center;
	text-align: center;
}

.ppp-theme .ppp-coa-button {
	width: auto;
}

/* Footer: link on the left, actions grouped right. */
.ppp-coa-dialog__actions {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-2xs);
}

/* External-link mark, so the new-tab behaviour is visible and not only
   announced to screen readers. */
.ppp-theme .ppp-verify-button::after {
	content: "↗";
	margin-left: var(--space-3xs);
	font-size: 0.9em;
	line-height: 1;
}

/* Narrow phones: the footer stacks, so let each part span the sheet. */
@media (max-width: 480px) {
	.ppp-coa-dialog__foot {
		justify-content: stretch;
	}

	.ppp-coa-dialog__actions {
		width: 100%;
	}

	.ppp-theme .ppp-coa-dialog__actions > * {
		flex: 1 1 auto;
		justify-content: center;
	}
}

/* --------------------------------------------------------------------------
   40. ANCHOR-BASED BUTTONS
   `.ppp-btn` (0,1,0) loses to the global `.ppp-theme a` (0,2,0), so any button
   rendered as a link kept the navy link colour — dark blue text on the blue
   button fill. Element-qualified selectors restore the intended colour for
   every variant.
   -------------------------------------------------------------------------- */

.ppp-theme a.ppp-btn,
.ppp-theme a.ppp-btn:visited {
	color: var(--color-button-text);
}

.ppp-theme a.ppp-btn:hover,
.ppp-theme a.ppp-btn:focus-visible {
	color: var(--color-button-text);
}

/* Outlined variant stays accent-on-transparent by design. */
.ppp-theme a.ppp-btn--secondary,
.ppp-theme a.ppp-btn--secondary:visited,
.ppp-theme a.ppp-btn--secondary:hover {
	color: var(--color-accent);
}

/* Ghost variant only ever sits on a dark band. */
.ppp-theme a.ppp-btn--ghost,
.ppp-theme a.ppp-btn--ghost:visited,
.ppp-theme a.ppp-btn--ghost:hover {
	color: var(--color-on-dark);
}

/* --------------------------------------------------------------------------
   41. DESCRIPTION PANELS
   The short description card and the long description tab share one treatment:
   a white ground with a navy wash falling away toward the bottom, so a long
   passage never loses contrast where the tint would otherwise sit behind the
   last lines.
   -------------------------------------------------------------------------- */

.ppp-product-blurb,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-Tabs-panel--description {
	position: relative;
	padding: var(--space-md);
	background-color: var(--color-card);
	background-image: var(--gradient-navy-fade);
	border: 1px solid var(--color-border-navy);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-xs);
}

/* Accent edge, drawn rather than bordered so it can be inset from the
   panel's rounded corners instead of squaring them off. */
.ppp-product-blurb::before,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-Tabs-panel--description::before {
	content: "";
	position: absolute;
	top: var(--space-sm);
	bottom: var(--space-sm);
	left: 0;
	width: 3px;
	border-radius: 0 var(--radius-pill) var(--radius-pill) 0;
	background-image: var(--gradient-accent);
}

.ppp-product-blurb {
	border-left: 1px solid var(--color-border-navy);
	padding-left: calc(var(--space-md) + var(--space-3xs));
}

.ppp-product-blurb p {
	max-width: none;
	font-size: var(--text-sm);
	line-height: var(--leading-normal);
	color: var(--color-text);
}

.ppp-product-blurb p + p {
	margin-top: var(--space-2xs);
}

/* Long description tab: roomier, since it holds real body copy. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-Tabs-panel--description {
	padding: var(--space-lg);
	padding-left: calc(var(--space-lg) + var(--space-3xs));
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-Tabs-panel--description > :first-child {
	margin-top: 0;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .woocommerce-Tabs-panel--description > :last-child {
	margin-bottom: 0;
}

/* Same panel on a dark band, should a description ever sit on one. */
.ppp-band--dark .ppp-product-blurb {
	background-color: rgba(255, 255, 255, 0.04);
	background-image: none;
	border-color: var(--color-on-dark-border);
	box-shadow: none;
}

.ppp-band--dark .ppp-product-blurb p {
	color: var(--color-on-dark-muted);
}

/* --------------------------------------------------------------------------
   42. MOBILE CONTENT INSET
   On phones the gallery panel butted straight against the header band with no
   breathing room. Applied to shop and product templates only — the home page
   hero is deliberately flush so the header and hero read as one dark field.
   -------------------------------------------------------------------------- */

@media (max-width: 920px) {
	.ppp-theme.single-product .site-content,
	.ppp-theme.woocommerce-shop .site-content,
	.ppp-theme.post-type-archive-product .site-content,
	.ppp-theme.tax-product_cat .site-content {
		padding-top: var(--space-md);
	}
}

/* --------------------------------------------------------------------------
   43. HERO BACKGROUND IMAGE
   Lets the hero take a background image straight from the editor's Background
   control, with no markup change.

   The editor writes `background-image: url(...)` inline, which outranks any
   stylesheet — so the band's own gradient would simply be replaced, leaving
   white text on a bare photo. Instead the base is a flat colour and the
   gradient moves to an overlay layer above the image and below the content.
   That reads correctly whether or not an image is ever set.
   -------------------------------------------------------------------------- */

.ppp-theme .ppp-hero.ppp-band--dark,
.ppp-theme .ppp-hero {
	position: relative;
	background-color: var(--color-dark);
	background-image: none;
	background-size: cover;
	background-position: center;
	background-repeat: no-repeat;
}

.ppp-theme .ppp-hero::before {
	content: "";
	position: absolute;
	inset: 0;
	z-index: 0;
	background-image: var(--gradient-hero-overlay);
	pointer-events: none;
}

/* Content above the overlay. */
.ppp-theme .ppp-hero > * {
	position: relative;
	z-index: 1;
}

/* Keep the band's hairline rule on top of the overlay. */
.ppp-theme .ppp-hero.ppp-band--dark::after {
	z-index: 2;
}

/* The Cover-based hero variant uses the same overlay, applied to the element
   Cover already provides for it. */
.ppp-theme .ppp-hero-cover .wp-block-cover__background {
	background-image: var(--gradient-hero-overlay);
}

/* --------------------------------------------------------------------------
   44. HERO — FULL BLEED + TIGHTER VERTICAL
   Two corrections:

   · If the hero group ever loses its alignfull class it would sit inside the
     content container and stop spanning the viewport. The :not() guard
     full-bleeds it without doubling up when alignfull is present, which would
     otherwise overflow by the scrollbar's width.

   · A background image set on the INNER container only spans the container's
     max-width, leaving the photo as a band in the middle of a wider dark
     field. Stretching that inner element covers the viewport while its
     padding keeps the text on the same measure as everywhere else.
   -------------------------------------------------------------------------- */

/* Only bleeds a hero carrying NEITHER alignment class. `alignwide` already
   gets its own width and auto margins from Astra; layering a viewport-width
   rule on top of that shifts the band sideways instead of centring it, which
   shows as a gap down one edge and overflow on the other. Full width is a
   one-click setting in the editor and is the correct fix. */
.ppp-theme .ppp-hero:not(.alignfull):not(.alignwide) {
	width: 100vw;
	max-width: 100vw;
	margin-inline: calc(50% - 50vw);
}

/* Rescue for an image applied to the inner container rather than the band. */
.ppp-theme .ppp-hero > .ppp-container[style*="background-image"] {
	width: 100vw;
	max-width: 100vw;
	margin-inline: calc(50% - 50vw);
	padding-inline: max(var(--gutter), calc(50vw - var(--container-max) / 2));
	background-size: cover;
	background-position: center;
}

/* Vertical rhythm. A `cover` background crops whatever does not fit the band's
   aspect ratio — on a band far wider than it is tall, that crop lands top and
   bottom and clips the product. Taller band, closer aspect ratio, less crop.
   Padding raises height too, but min-height is the direct control and leaves
   the text block's own spacing alone. */
.ppp-theme .entry-content > .ppp-band.ppp-hero,
.ppp-theme .ppp-hero {
	padding-block: clamp(var(--space-xl), 5vw, var(--space-3xl));
}

.ppp-theme .ppp-hero {
	min-height: clamp(30rem, 78vh, 46rem);
}

/* --------------------------------------------------------------------------
   45. TRUST BAND
   The three proof points as their own section below the hero, rather than
   floating over the photo. Cards on a light band, with an accent rule so they
   read as a set.
   -------------------------------------------------------------------------- */

.ppp-trust-card {
	position: relative;
	overflow: hidden;
	padding: var(--space-md);
	background-color: var(--color-card);
	background-image: var(--gradient-navy-fade);
	border: 1px solid var(--color-border-navy);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-sm);
}

.ppp-trust-card::before {
	content: "";
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	height: 3px;
	background-image: var(--gradient-accent);
}

.ppp-theme .ppp-trust-card .ppp-eyebrow {
	margin-bottom: var(--space-2xs);
}

.ppp-theme .ppp-trust-card p {
	margin: 0;
	max-width: none;
	font-size: var(--text-sm);
	line-height: var(--leading-normal);
	color: var(--color-text-muted);
}

.ppp-band--dark .ppp-trust-card {
	background-color: rgba(255, 255, 255, 0.04);
	background-image: none;
	border-color: var(--color-on-dark-border);
	box-shadow: none;
}

.ppp-band--dark .ppp-trust-card p {
	color: var(--color-on-dark-muted);
}

/* --------------------------------------------------------------------------
   46. STAGGERED HERO HEADLINE
   HTML collapses runs of whitespace, so leading spaces typed in the editor to
   stagger a headline are thrown away on render — the editor shows the indent
   and the front end does not.

   `pre-wrap` preserves those spaces while still allowing the line to wrap if
   it runs long. `text-wrap: wrap` cancels the balancing inherited from the
   global heading rule, which would otherwise redistribute the very lines the
   author is positioning by hand.
   -------------------------------------------------------------------------- */

.ppp-theme .ppp-hero h1,
.ppp-theme .ppp-hero-cover h1 {
	white-space: pre-wrap;
	text-wrap: wrap;
}

/* Phones: indents plus a large type size overflow a narrow screen, so let the
   headline set naturally and drop the stagger. */
@media (max-width: 600px) {
	.ppp-theme .ppp-hero h1,
	.ppp-theme .ppp-hero-cover h1 {
		white-space: normal;
	}
}

/* --------------------------------------------------------------------------
   47. HERO — HEADLINE TO BUTTON GAP
   Section 9 set these for a hero that also carried an eyebrow, a lead
   paragraph and a trust bar. With the hero reduced to a headline and buttons
   the same spacing reads as a hole between them.

   Note: trailing <br> elements inside the headline are not removable from CSS.
   With pre-wrap active each one renders as a real empty line, so they have to
   come out of the content itself.
   -------------------------------------------------------------------------- */

.ppp-theme .ppp-hero-actions {
	margin-top: var(--space-md);
}

.ppp-theme .ppp-hero h1,
.ppp-theme .ppp-hero-cover h1 {
	margin-bottom: var(--space-xs);
}

/* The lead only exists on heroes that still have one; harmless otherwise. */
.ppp-theme .ppp-hero .ppp-lead {
	margin-top: var(--space-2xs);
}

/* --------------------------------------------------------------------------
   48. TRUST CARDS — MARKER AND SCALE
   Picks up the diamond marker used by the feature rows further down the page,
   so the two sections read as one visual language rather than two unrelated
   card treatments.
   -------------------------------------------------------------------------- */

.ppp-trust-card {
	display: flex;
	flex-direction: column;
	gap: var(--space-2xs);
	padding: var(--space-lg) var(--space-md) var(--space-md);
}

.ppp-trust-card::after {
	content: "";
	width: 1.15rem;
	height: 1.15rem;
	order: -1;
	margin-bottom: var(--space-2xs);
	border-radius: var(--radius-xs);
	border: 1px solid var(--color-button);
	background-color: var(--color-accent-soft-strong);
	transform: rotate(45deg);
}

/* Label carries the card, so it steps up from the eyebrow size used inline. */
.ppp-theme .ppp-trust-card .ppp-eyebrow {
	margin-bottom: 0;
	font-size: var(--text-sm);
	letter-spacing: var(--tracking-wide);
	color: var(--color-accent);
}

.ppp-theme .ppp-trust-card p {
	font-size: var(--text-sm);
	line-height: var(--leading-normal);
}

.ppp-band--dark .ppp-trust-card::after {
	border-color: var(--color-glow);
	background-color: rgba(77, 163, 255, 0.18);
}

.ppp-band--dark .ppp-trust-card .ppp-eyebrow {
	color: var(--color-glow);
}

/* --------------------------------------------------------------------------
   49. HERO ON MOBILE
   The image is 2.11:1. Shown whole at a phone's width it is roughly 185px
   tall, while the hero band is around 660px — so `cover` scales the image up
   until it fills that height and crops away most of its width. That is why it
   reads as enormously zoomed on a phone.

   A wide image cannot be both fully visible and sit behind a three-line
   headline on a portrait screen; there is not enough height for the text. So
   on mobile the image is shown whole across the top and the headline and
   buttons sit beneath it, still inside the same band.

   Top padding reserves exactly the image's own height via --hero-image-ratio,
   so the text starts where the image ends with no overlap and no guesswork.
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
	.ppp-theme .ppp-hero {
		min-height: 0;
		background-size: contain;
		background-position: center top;
		background-repeat: no-repeat;
		padding-top: calc(100vw / var(--hero-image-ratio) + var(--space-md));
		padding-bottom: var(--space-2xl);
	}

	/* The editor writes font-size inline on the headline, which no selector can
	   outrank — and a desktop size set in px does not scale down. This is the
	   one place !important is the correct tool rather than a shortcut. */
	.ppp-theme .ppp-hero h1 {
		font-size: var(--text-3xl) !important;
		max-width: none;
	}

	.ppp-theme .ppp-hero-actions {
		width: 100%;
	}

	.ppp-theme .ppp-hero-actions .wp-block-button {
		flex: 1 1 auto;
	}

	.ppp-theme .ppp-hero-actions .wp-block-button__link {
		width: 100%;
		justify-content: center;
	}
}

/* --------------------------------------------------------------------------
   50. HERO ON MOBILE — BUTTON SCALE AND TEXT CONTRAST
   Supersedes the full-width buttons from section 49. Stretching them edge to
   edge on a phone reads as two banners rather than two actions.

   The background size is set inline from the editor, so the image covers the
   band rather than sitting above the text as section 49 assumed. With the
   headline over a photo it needs its own contrast rather than relying on the
   overlay alone — a shadow keeps it legible over the pale caps and label text
   in the image without darkening the whole picture.
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
	.ppp-theme .ppp-hero-actions {
		width: auto;
		gap: var(--space-2xs);
	}

	.ppp-theme .ppp-hero-actions .wp-block-button {
		flex: 0 1 auto;
	}

	.ppp-theme .ppp-hero-actions .wp-block-button__link {
		width: auto;
		padding: 0.7rem 1.35rem;
		font-size: var(--text-sm);
	}

	/* Stronger overlay behind the text half of the band. */
	.ppp-theme .ppp-hero::before {
		background-image:
			linear-gradient(
				180deg,
				rgba(11, 18, 32, 0.72) 0%,
				rgba(11, 18, 32, 0.55) 45%,
				rgba(16, 27, 46, 0.94) 100%
			);
	}

	.ppp-theme .ppp-hero h1 {
		text-shadow: 0 2px 16px rgba(11, 18, 32, 0.75), 0 1px 3px rgba(11, 18, 32, 0.6);
	}

	.ppp-theme .ppp-hero .ppp-eyebrow {
		text-shadow: 0 1px 8px rgba(11, 18, 32, 0.8);
	}
}

/* --------------------------------------------------------------------------
   51. HERO SCRIM
   Replaces the uniform overlay with a directional one. Dimming the whole
   image to make text legible costs the photography exactly where it earns its
   place; a scrim spends that contrast only where the text actually sits.

   Desktop runs left-to-right, since the text occupies the left column and the
   vials sit right. Phones run top-down, because the headline spans nearly the
   full width there and a horizontal scrim would darken everything.
   -------------------------------------------------------------------------- */

.ppp-theme .ppp-hero::before {
	background-image: var(--gradient-hero-scrim);
}

@media (max-width: 768px) {
	.ppp-theme .ppp-hero::before {
		background-image: var(--gradient-hero-scrim-mobile);
	}

	/* Lighter shadow now the scrim is doing the work — enough to hold the
	   letterforms against the pale caps without looking embossed. */
	.ppp-theme .ppp-hero h1 {
		text-shadow: 0 2px 12px rgba(11, 18, 32, 0.55);
	}
}

/* --------------------------------------------------------------------------
   52. HERO ON MOBILE — CENTRED TEXT, PAIRED BUTTONS
   Supersedes the stacked, left-aligned arrangement from sections 49 and 50.

   The stagger is disabled across the whole mobile range rather than only
   below 600px: hand-set indents and centred text pull in opposite directions,
   and between 600 and 768px the two were both active.
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
	.ppp-theme .ppp-hero .ppp-container {
		text-align: center;
	}

	.ppp-theme .ppp-hero h1 {
		white-space: normal;
		max-width: none;
		margin-inline: auto;
	}

	.ppp-theme .ppp-hero .ppp-lead {
		margin-inline: auto;
	}

	/* Side by side, sharing the row equally. min-width:0 lets a long label
	   shrink its button rather than forcing the pair to wrap. */
	.ppp-theme .ppp-hero-actions {
		flex-wrap: nowrap;
		justify-content: center;
		gap: var(--space-2xs);
	}

	.ppp-theme .ppp-hero-actions .wp-block-button {
		flex: 1 1 0;
		min-width: 0;
	}

	.ppp-theme .ppp-hero-actions .wp-block-button__link {
		width: 100%;
		padding: 0.7rem 0.85rem;
		font-size: var(--text-sm);
		text-align: center;
		white-space: normal;
	}
}

/* Very narrow phones cannot hold two labels of this length on one line, so
   the pair stacks again rather than wrapping mid-word. */
@media (max-width: 360px) {
	.ppp-theme .ppp-hero-actions {
		flex-wrap: wrap;
	}

	.ppp-theme .ppp-hero-actions .wp-block-button {
		flex: 1 1 100%;
	}
}

/* --------------------------------------------------------------------------
   53. HERO — CAPS HEADLINE, PILL BUTTONS, HIGHER TEXT
   Both hero buttons carry `is-style-outline` from the editor plus Astra's
   global-colour classes. Shape and background clear those on specificity
   alone, at 0,5,0.

   Text colour does not. WordPress emits its palette utilities with
   !important:

       .has-ast-global-color-2-color {
           color: var(--wp--preset--color--ast-global-color-2) !important;
       }

   No specificity beats that, so both buttons rendered dark navy text — one on
   a blue fill, one on the scrim — regardless of what this section asked for.
   The colour declarations below therefore carry !important. It is the same
   situation as section 49: the only way to outrank a value the editor forces.
   Clearing the buttons' Text colour in the editor would also work, but that is
   a per-block value that would not travel to any other page.
   -------------------------------------------------------------------------- */

.ppp-theme .ppp-hero h1,
.ppp-theme .ppp-hero-cover h1 {
	text-transform: uppercase;
}

/* Shared button shape. */
.ppp-theme .ppp-hero-actions .wp-block-button .wp-block-button__link {
	border-radius: var(--radius-pill);
	padding: 0.85rem 1.75rem;
	border-width: 1px;
	border-style: solid;
	font-weight: var(--weight-semibold);
	letter-spacing: 0.01em;
	box-shadow: none;
}

/* First: solid. */
.ppp-theme .ppp-hero-actions .wp-block-button:first-child .wp-block-button__link {
	background-color: var(--color-button);
	border-color: var(--color-button);
	color: var(--color-button-text) !important;
}

.ppp-theme .ppp-hero-actions .wp-block-button:first-child .wp-block-button__link:hover {
	background-color: var(--color-button-hover);
	border-color: var(--color-button-hover);
	color: var(--color-button-text) !important;
}

/* Second: outlined, sitting on the dark hero. */
.ppp-theme .ppp-hero-actions .wp-block-button:nth-child(2) .wp-block-button__link,
.ppp-theme .ppp-hero-actions .ppp-btn--ghost .wp-block-button__link {
	background-color: transparent;
	border-color: rgba(226, 234, 246, 0.55);
	color: var(--color-on-dark) !important;
}

.ppp-theme .ppp-hero-actions .wp-block-button:nth-child(2) .wp-block-button__link:hover,
.ppp-theme .ppp-hero-actions .ppp-btn--ghost .wp-block-button__link:hover {
	background-color: rgba(255, 255, 255, 0.10);
	border-color: var(--color-on-dark);
	color: var(--color-on-dark) !important;
}

/* Empty paragraph blocks left in the hero hold full line height and push the
   content down. Collapse them rather than leaving dead space. */
.ppp-theme .ppp-hero p:empty {
	display: none;
}

@media (max-width: 768px) {
	/* Text sits higher: the reserved image height from section 49 is no longer
	   needed now the image is set to cover from the editor. */
	.ppp-theme .ppp-hero {
		padding-top: var(--space-2xl);
		justify-content: flex-start;
	}

	.ppp-theme .ppp-hero-actions .wp-block-button .wp-block-button__link {
		padding: 0.75rem 1rem;
	}
}

/* --------------------------------------------------------------------------
   54. HERO — DISPLAY TYPE
   Measured off verticalaminos.com, the reference for this hero. Its headline
   is not a better typeface — it loads no webfont at all and resolves to plain
   Arial. Its authority comes entirely from four things:

     weight 700+ (real, not synthesized)  ·  leading 0.90–0.95
     tracking -0.035em                    ·  one line in the accent colour

   Section 2 asked for 700 while Astra only ever loaded weight 600, so the
   browser drew a synthetic bold — a stretched 600 — at 89px. That, not the
   font, is why the headline read soft. functions.php now loads 600/700/800.

   Sizes here are deliberately close to the inline `font-size` the editor
   wrote on the headline (89px), so removing that inline value shifts nothing
   visually and simply hands the size back to the stylesheet.
   -------------------------------------------------------------------------- */

.ppp-theme .ppp-hero h1,
.ppp-theme .ppp-hero-cover h1 {
	font-family: var(--font-heading);
	font-weight: var(--weight-extrabold);
	font-size: clamp(2.75rem, 6vw, 5.5rem);   /* 44 → 88 */
	line-height: var(--leading-display);
	letter-spacing: var(--tracking-display);
	max-width: none;
}

/* The accent line. The reference colours its middle line cyan; the equivalent
   here is the glow already taken from the helix mark, so the hero picks up the
   logo rather than introducing a second brand colour. Wrap one line of the
   headline in <span class="ppp-accent"> to use it. */
.ppp-theme .ppp-hero h1 .ppp-accent,
.ppp-theme .ppp-hero-cover h1 .ppp-accent {
	color: var(--color-glow);
}

/* Eyebrow: the reference tracks it to 0.28em, twice the theme's widest step.
   At 12px caps that spacing is what makes it read as a label rather than as a
   small line of copy. */
.ppp-theme .ppp-hero .ppp-eyebrow,
.ppp-theme .ppp-hero-cover .ppp-eyebrow {
	letter-spacing: var(--tracking-eyebrow);
	font-weight: var(--weight-bold);
	color: var(--color-glow);
}

/* Mobile keeps the same proportions. Section 49 pins the size with !important
   to beat the editor's inline value, so only the metrics are restated here. */
@media (max-width: 768px) {
	.ppp-theme .ppp-hero h1,
	.ppp-theme .ppp-hero-cover h1 {
		line-height: 0.95;
		letter-spacing: var(--tracking-display);
	}
}

/* --------------------------------------------------------------------------
   55. SPLIT SECTION — pillar + proof grid
   Combines the three layouts sent as reference for the "Built by researchers"
   section:

     Mile High Compounds  →  asymmetric split, bold all-caps headline
     BioEdge              →  saturated pillar panel beside a card grid,
                             sitting on a light band rather than a coloured one
     Why Choose Us        →  a marker above each card title, titles in caps

   Deliberately NOT taken: Mile High's full-bleed saturated band. The next
   section on the page is already .ppp-band--dark, and two heavy bands meeting
   flatten each other. Keeping the band light and putting the colour in the
   pillar gets the same contrast and is what BioEdge does.

   Applies by adding `ppp-split` to the existing .ppp-container. The markup
   underneath is unchanged: .ppp-section-head becomes the pillar, .ppp-features
   becomes the grid.
   -------------------------------------------------------------------------- */

/* Accepts the class on either wrapper. List View labels both the band and the
   container "Group" with nothing to tell them apart, so requiring the inner one
   is a trap. Every other rule in this section is a descendant selector and
   already worked from either; only the grid declaration needed the second
   selector. */
.ppp-container.ppp-split,
.ppp-split > .ppp-container {
	display: grid;
	grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
	/* Stretch, not start: the pillar and the card grid are two halves of one
	   block, so their top AND bottom edges have to line up. `start` let the
	   pillar shrink to its own text and the two ran to different depths.
	   Core's flow margin on the second child is cleared in section 22. */
	align-items: stretch;
	gap: var(--space-xl);
	max-width: var(--container-wide);
}

/* ---- Left pillar ---- */

.ppp-split .ppp-section-head {
	max-width: none;
	margin-bottom: 0;
	position: relative;
	overflow: hidden;
	/* Now that the panel stretches to the grid's height it is taller than its
	   own text. Centring the content keeps the spare room split evenly top and
	   bottom instead of pooling underneath the last line. */
	display: flex;
	flex-direction: column;
	justify-content: center;
	border-radius: var(--radius-lg);
	padding: var(--space-xl) var(--space-lg);
	color: var(--color-on-dark);
	/* Fine dot mesh over the brand gradient — the texture BioEdge uses to stop
	   a large flat panel reading as dead space. */
	background-image:
		radial-gradient(circle at 1px 1px, rgba(255, 255, 255, 0.10) 1px, transparent 0),
		var(--gradient-accent);
	background-size: 22px 22px, auto;
	box-shadow: var(--shadow-lg);
}

/* The section head carries an empty paragraph as an eyebrow slot. Left alone
   it holds a full line of height inside the panel. */
.ppp-split .ppp-section-head p:empty {
	display: none;
}

.ppp-split .ppp-section-head h2 {
	font-family: var(--font-heading);
	font-weight: var(--weight-extrabold);
	font-size: var(--text-3xl);
	text-transform: uppercase;
	line-height: var(--leading-display);
	letter-spacing: var(--tracking-display);
	color: var(--color-on-dark);
	max-width: 14ch;
	margin-bottom: var(--space-sm);
}

.ppp-split .ppp-section-head .ppp-lead {
	color: var(--color-on-dark-muted);
	font-size: var(--text-md);
	line-height: var(--leading-normal);
	max-width: none;
	margin-bottom: 0;
}

.ppp-split .ppp-section-head .ppp-eyebrow {
	color: var(--color-on-dark);
	letter-spacing: var(--tracking-eyebrow);
	opacity: 0.75;
}

/* Optional proof list for the pillar, per BioEdge's ticked list. Add
   `ppp-proof-list` to a core List block inside the section head.
   Content must be claims that are actually true — see the launch checklist. */
.ppp-split .ppp-proof-list {
	list-style: none;
	margin: var(--space-md) 0 0;
	padding: 0;
	max-width: none;
}

.ppp-split .ppp-proof-list li {
	position: relative;
	padding-left: calc(var(--space-md) + var(--space-3xs));
	margin-top: var(--space-2xs);
	font-size: var(--text-sm);
	font-weight: var(--weight-semibold);
	color: var(--color-on-dark);
}

.ppp-split .ppp-proof-list li::before {
	content: "";
	position: absolute;
	left: 0;
	top: 0.34em;
	width: 0.95rem;
	height: 0.95rem;
	border-radius: var(--radius-xs);
	border: 1px solid rgba(255, 255, 255, 0.7);
	background-color: rgba(255, 255, 255, 0.16);
	transform: rotate(45deg);
}

/* ---- Right proof grid ---- */

.ppp-split .ppp-features {
	/* One column, full width. With two proof points beside a tall pillar, two
	   half-width cards leave the column looking thin — stacked, each card gets
	   the full measure and the pair balances the pillar's height.

	   This also retires the old "odd last card spans both columns" rule, which
	   assumed three cards: with one card emptied but its Group left in place,
	   the last card was still child three, so it spanned while the first
	   stayed half-width — a narrow box above a wide one. */
	grid-template-columns: minmax(0, 1fr);
	gap: var(--space-md);
	align-items: stretch;
}

.ppp-split .ppp-feature {
	padding: var(--space-lg) var(--space-md);
	background-color: var(--color-card);
	border: 1px solid var(--color-border-navy);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-sm);
	transition: transform var(--transition-base), box-shadow var(--transition-base),
		border-color var(--transition-base);
}

.ppp-split .ppp-feature:hover {
	transform: translateY(-2px);
	border-color: var(--color-border-navy-strong);
	box-shadow: var(--shadow-md);
}

.ppp-split .ppp-feature h3 {
	font-family: var(--font-heading);
	font-size: var(--text-md);
	font-weight: var(--weight-bold);
	text-transform: uppercase;
	letter-spacing: var(--tracking-wide);
	line-height: var(--leading-snug);
	color: var(--color-accent);
	margin-bottom: var(--space-2xs);
}

/* ---- Stacking ---- */

@media (max-width: 900px) {
	.ppp-container.ppp-split {
		grid-template-columns: minmax(0, 1fr);
		gap: var(--space-lg);
	}

	.ppp-split .ppp-section-head h2 {
		max-width: none;
	}
}

@media (max-width: 600px) {
	.ppp-split .ppp-section-head {
		padding: var(--space-lg) var(--space-md);
	}
}

/* --------------------------------------------------------------------------
   56. TRANSPARENCY — VERIFICATION LEDGER
   The section claims open documentation, so it is built as a document of
   record rather than as three cards that describe one. The whole grid is a
   single sheet; each card is a ruled row on it.

     · the sheet carries the security crosshatch, a brand-gradient header rule
       and one large seal cropped by its corner — one certificate, not three
     · each row is numbered from a CSS counter, set in mono, so the numerals
       need no content and stay correct if rows are added or reordered
     · an icon column sits ready between the number and the title

   ICONS: the column is already reserved. Drop an Image block in as the FIRST
   block inside a row and it takes that column automatically, and the CSS
   placeholder diamond hides itself. Nothing here needs changing when the real
   icons arrive.

   SELF-APPLYING — see the note in section 22 about List View. Every rule
   matches either an explicit `ppp-transparency` class or, via :has(), a
   .ppp-band holding a .ppp-card with a .ppp-badge. That pairing is unique to
   this section: .ppp-badge occurs three times site-wide, all of them here, and
   not at all on the shop or product templates.
   -------------------------------------------------------------------------- */

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-section-head h2 {
	font-family: var(--font-heading);
	font-weight: var(--weight-extrabold);
	text-transform: uppercase;
	line-height: var(--leading-display);
	letter-spacing: var(--tracking-display);
	max-width: 18ch;
	margin-inline: auto;
}

/* ---- The sheet ---- */

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-grid {
	display: grid;
	grid-template-columns: minmax(0, 1fr);
	gap: 0;
	position: relative;
	overflow: hidden;
	background-color: var(--color-card);
	/* Security printing. Two opposed hatches at very low alpha read as texture
	   rather than pattern — visible on the sheet, invisible in a thumbnail. */
	background-image:
		repeating-linear-gradient(45deg, rgba(36, 89, 184, 0.045) 0 1px, transparent 1px 8px),
		repeating-linear-gradient(-45deg, rgba(36, 89, 184, 0.045) 0 1px, transparent 1px 8px);
	border: 1px solid var(--color-border-navy);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-md);
}

/* Document header rule. Inside the sheet's overflow, so it picks up the top
   corner radius for free. */
:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-grid::before {
	content: "";
	position: absolute;
	inset: 0 0 auto 0;
	height: 4px;
	background-image: var(--gradient-accent);
	z-index: var(--z-raised);
}

/* Embossed seal, pushed past the corner so the sheet's own overflow crops it
   to an arc. A whole ring reads as a target; a cropped one reads as a seal
   running off the edge of the paper. */
:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-grid::after {
	content: "";
	position: absolute;
	right: -3rem;
	bottom: -3rem;
	width: 12rem;
	height: 12rem;
	border-radius: 50%;
	border: 1.75rem solid var(--color-accent-soft);
	pointer-events: none;
}

/* ---- A row of the record ---- */

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card {
	display: grid;
	grid-template-columns: minmax(10rem, 17rem) minmax(0, 1fr);
	align-items: start;
	gap: var(--space-sm) var(--space-md);
	position: relative;
	z-index: var(--z-base);
	padding: var(--space-md) var(--space-lg);
	background: none;
	border: 0;
	border-radius: 0;
	box-shadow: none;
	transition: background-color var(--transition-base);
}

/* Core's flow layout puts margin-block-start on every child after the first.
   The row is a grid now, so that margin would drop the body copy out of line
   with its title. Same failure section 22 documents, one level deeper. */
:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card > * {
	margin-block: 0;
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card + .ppp-card {
	border-top: 1px solid var(--color-border-navy);
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card:hover {
	background-color: var(--color-accent-soft);
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card .wp-block-image {
	grid-column: 1;
	width: 1.75rem;
	margin: 0;
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card .wp-block-image img {
	width: 100%;
	height: auto;
	display: block;
}

/* The badge was carrying the job of a heading as a small pill. In the ledger
   it becomes the record's title outright. */
:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card .ppp-badge {
	grid-column: 1;
	display: block;
	background: none;
	border: 0;
	border-radius: 0;
	padding: 0;
	font-family: var(--font-heading);
	font-size: var(--text-md);
	font-weight: var(--weight-bold);
	line-height: var(--leading-snug);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	color: var(--color-accent);
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card p:not(.ppp-badge) {
	grid-column: 2;
	font-size: var(--text-sm);
	color: var(--color-text-muted);
	line-height: var(--leading-normal);
	max-width: none;
}

/* ---- Closing action ---- */

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-hero-actions {
	justify-content: center;
	margin-top: var(--space-xl);
}

/* ---- Narrow screens ---- */

@media (max-width: 900px) {
	:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card {
		grid-template-columns: minmax(0, 1fr);
		padding: var(--space-md);
		gap: var(--space-2xs) var(--space-sm);
	}

	/* Title over body, each spanning the row, so long copy is not squeezed
	   into a column. */
	:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card .ppp-badge {
		grid-row: 1;
		grid-column: 1 / -1;
	}

	:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card p:not(.ppp-badge) {
		grid-row: 2;
		grid-column: 1 / -1;
	}
}

@media (max-width: 600px) {
	:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-section-head h2 {
		max-width: none;
	}

	/* The seal crowds a narrow sheet; shrink it rather than dropping it. */
	:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-grid::after {
		width: 7.5rem;
		height: 7.5rem;
		border-width: 1.1rem;
		right: -2.25rem;
		bottom: -2.25rem;
	}
}

/* --------------------------------------------------------------------------
   56b. VERIFICATION LEDGER — NAVY SHEET
   Recolours the ledger only. Structure, spacing and the icon column are
   untouched, so this whole block can be deleted to return to the white sheet.

   The sheet goes navy while the BAND stays light — deliberately. Making the
   band dark would put it against the process section directly above, which is
   already .ppp-band--dark, and the two would merge. A dark panel framed by a
   light band reads as an object on a page instead.

   It also earns the colour: the product is black vials with a glowing blue
   helix, so a navy sheet with glow accents echoes the thing being documented.
   -------------------------------------------------------------------------- */

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-grid {
	background-color: var(--color-dark);
	/* Crosshatch inverts to white alpha — navy-on-navy would vanish. The lit
	   navy underneath it is --gradient-navy-panel, defined with the rest of
	   the gradients in tokens.css. */
	background-image:
		repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.045) 0 1px, transparent 1px 8px),
		repeating-linear-gradient(-45deg, rgba(255, 255, 255, 0.045) 0 1px, transparent 1px 8px),
		var(--gradient-navy-panel);
	border-color: var(--color-on-dark-border);
	box-shadow: var(--shadow-lg);
}

/* The brand gradient is navy-on-navy here. The glow rule reads instead. */
:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-grid::before {
	background-image: var(--gradient-rule);
	height: 2px;
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-grid::after {
	border-color: rgba(77, 163, 255, 0.10);
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card + .ppp-card {
	border-top-color: var(--color-on-dark-border);
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card:hover {
	background-color: rgba(255, 255, 255, 0.05);
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card::before {
	color: var(--color-glow);
	opacity: 0.8;
}

/* Matches the marker treatment .ppp-band--dark already gives feature rows. */
:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card::after {
	border-color: var(--color-glow);
	background-color: rgba(77, 163, 255, 0.20);
	box-shadow: 0 0 14px var(--color-glow-soft);
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card .ppp-badge {
	color: var(--color-on-dark);
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-card p:not(.ppp-badge) {
	color: var(--color-on-dark-muted);
}

/* --------------------------------------------------------------------------
   57. HERO ON MOBILE — REBUILT
   Supersedes the mobile blocks in sections 49, 50, 52 and 53. Those accreted
   one fix at a time against a dark hero and a dark photo; the art is now a
   white-ground product shot, and the assumptions underneath them no longer
   hold. Placed last, so equal-specificity rules win on source order.

   WHY `cover` CANNOT WORK HERE
   The art is 2:1 and the band is portrait, so `cover` scales the image until
   its HEIGHT fills the band — roughly 1400px wide inside a 390px window. Only
   a narrow vertical slice is ever visible, and the horizontal position picks
   which one. There is no good choice: the left half of the art is empty white,
   the right half is vials. At 54% the headline lands on the vials, which is
   what made the text unreadable.

   `contain` anchored to the bottom shows the product whole, across the full
   width, and leaves the upper band free for type.

   WHY THE BAND GOES LIGHT
   The art has a white ground. On a light band the photo has no visible edge —
   the vials simply sit on the page. On the dark band it reads as a pasted-in
   white rectangle. Going light also gives the headline far better contrast
   than white type over pale glassware ever could, and it matches the direction
   the desktop hero already took.

   The !important flags are not shortcuts. WordPress prints its palette
   utilities with !important — `.has-ast-global-color-3-background-color` on
   the band and `.has-ast-global-color-5-color` on the headline — and the
   editor writes background-size and position inline. Nothing outranks either.
   -------------------------------------------------------------------------- */

@media (max-width: 768px) {
	.ppp-theme .ppp-hero {
		display: flex;
		flex-direction: column;
		justify-content: center;
		/* No min-height. Reserving a screenful and then centring in it is what
		   produced a band of empty white above the headline — the content is
		   what should set the height here. */
		/* The editor writes padding-top and padding-bottom INLINE, so the values
		   set here were being ignored and the band collapsed to its content —
		   which is why the vials filled it edge to edge with the headline
		   sitting straight on them. !important is the only way past an inline
		   declaration. */
		min-height: 72svh;
		padding-top: var(--space-2xl) !important;
		padding-bottom: var(--space-2xl) !important;
		background-color: var(--color-dark) !important;
		/* Two layers: the product over the brand gradient. The desktop art is
		   47% product and 53% empty white, composed for a text-left /
		   product-right split that a phone has no room for — this is the same
		   photograph cropped to its subject and cut out of its white ground.
		   The alpha is the point: without it the vials arrive inside a white
		   rectangle and there is no way to put a gradient behind them. */
		background-image: url("../img/hero-mobile.webp"), var(--gradient-hero) !important;
		/* Slightly wider than the band so the product fills it, the way the
		   reference does. The outer two vials lose a little to the edges; all
		   three still read. `cover` would crop far harder — the art is 1.21:1
		   and the band is about 0.65:1. */
		background-size: 126% auto, cover !important;
		background-position: center 56%, center center !important;
		background-repeat: no-repeat, no-repeat !important;
	}

	/* Scrim over the photograph. The reference lays a flat 80% black over its
	   vials for exactly this reason: the product becomes a texture behind the
	   type instead of competing with it. Weighted rather than flat, so the
	   band still resolves darker at the bottom edge where the next section
	   meets it.

	   Tuned by eye across four passes: 0.88–0.95 erased the vials, 0.74–0.90
	   still read as too dark. These are ~20 points lighter again. Legibility
	   is no longer carried by the scrim alone — the headline takes its own
	   shadow below, which is what lets the photograph come up this far.
	   Lower is more product, higher is more type. */
	.ppp-theme .ppp-hero::before {
		display: block;
		background-image: linear-gradient(
			180deg,
			rgba(11, 18, 32, 0.55) 0%,
			rgba(11, 18, 32, 0.62) 40%,
			rgba(11, 18, 32, 0.56) 68%,
			rgba(11, 18, 32, 0.80) 100%
		);
	}

	/* ---- Type on a light ground ---- */

	/* White type, matching desktop. The gradient behind it is dark enough that
	   the shadows the old dark-photo hero needed are no longer doing anything. */
	/* The shadow does the work the scrim used to. Spending contrast only where
	   the type actually sits costs the photograph far less than dimming the
	   whole frame to the same end. */
	.ppp-theme .ppp-hero h1 {
		color: var(--color-on-dark) !important;
		font-size: clamp(2.4rem, 12vw, 3.5rem) !important;
		text-shadow: 0 2px 20px rgba(11, 18, 32, 0.85), 0 1px 4px rgba(11, 18, 32, 0.65);
	}

	/* The .ppp-accent span is added by assets/js/hero-lines.js, which wraps each
	   line of the headline. CSS cannot address text between <br> elements, and
	   nesting spans by hand in the editor is a step that does not survive. The
	   line it colours is set there, so a copy rewrite does not need new markup.

	   The script also sizes nothing — an earlier pass tapered the three lines
	   to funnel them, which did not read well and was removed. */
	.ppp-theme .ppp-hero h1 .ppp-accent {
		color: var(--color-glow) !important;
	}

	.ppp-theme .ppp-hero .ppp-eyebrow {
		color: var(--color-glow);
		text-shadow: none;
	}

	.ppp-theme .ppp-hero .ppp-lead {
		color: var(--color-on-dark-muted);
		text-shadow: none;
	}

	/* ---- Actions ---- */

	/* Section 52 sat these side by side; two labels this long cannot share a
	   phone's width, so one wrapped to two lines. Stretching them edge to edge
	   instead turned them into banners. Stacked and centred at a fixed width
	   they read as a pair of buttons, which is what they are.

	   Borders are written as a shorthand rather than border-color alone. The
	   editor's `is-style-outline` supplies its own border declaration, and
	   setting only the colour left the width and style to whatever won that
	   fight — which is how the second button ended up with no visible edge. */
	.ppp-theme .ppp-hero-actions {
		flex-direction: row;
		flex-wrap: wrap;
		align-items: center;
		justify-content: center;
		width: 100%;
		gap: var(--space-2xs);
		margin-top: var(--space-md);
	}

	/* `0 1 auto` let these shrink below their own content while white-space:
	   nowrap held the label at full width — so the text ran outside the box.
	   No shrink, and the row wraps instead if it ever cannot fit. */
	.ppp-theme .ppp-hero-actions .wp-block-button {
		flex: 0 0 auto;
		width: auto;
		min-width: 0;
	}

	/* Rounded rectangles, not pills. Section 53 sets --radius-pill for the
	   desktop hero; at this size a pill reads as a lozenge.

	   Sizing note: Astra sets html { font-size: 112.5% }, so 1rem is 18px on
	   this site, not 16px. The previous padding and type here were chosen as
	   if 1rem were 16px, which is how the pair ended up 12.5% wider than the
	   row could hold. inline-flex centres the label in both axes rather than
	   relying on text-align, which cannot centre what has overflowed. */
	.ppp-theme .ppp-hero-actions .wp-block-button__link {
		display: inline-flex;
		align-items: center;
		justify-content: center;
		width: auto;
		padding: 0.75rem 0.8rem;
		border-radius: var(--radius-md);
		/* Fluid rather than fixed. At 393px the row has ~332px of usable width
		   once the block's own inline padding and the container gutter are
		   taken out, and the two labels at a flat --text-sm came to about that
		   — close enough that they tipped over and wrapped. Scaling with the
		   viewport keeps them on one row down to ~340px instead of depending
		   on one size happening to fit. */
		font-size: clamp(0.72rem, 3.3vw, var(--text-sm));
		font-weight: var(--weight-semibold);
		line-height: 1.2;
		text-align: center;
		white-space: nowrap;
	}

	.ppp-theme .ppp-hero-actions .wp-block-button:first-child .wp-block-button__link {
		border: 1px solid var(--color-button) !important;
		background-color: var(--color-button) !important;
		color: var(--color-button-text) !important;
	}

	/* Filled light, not outlined. An outline on a dimmed photo is exactly what
	   disappeared before; a solid panel cannot. This is what the reference
	   does with its second action. */
	.ppp-theme .ppp-hero-actions .wp-block-button:nth-child(2) .wp-block-button__link,
	.ppp-theme .ppp-hero-actions .ppp-btn--ghost .wp-block-button__link {
		border: 1px solid var(--color-on-dark) !important;
		background-color: var(--color-on-dark) !important;
		color: var(--color-accent) !important;
	}

	.ppp-theme .ppp-hero-actions .wp-block-button:nth-child(2) .wp-block-button__link:hover,
	.ppp-theme .ppp-hero-actions .ppp-btn--ghost .wp-block-button__link:hover {
		border-color: var(--color-card) !important;
		background-color: var(--color-card) !important;
		color: var(--color-accent) !important;
	}
}

/* Section 52 stacked the pair again below 360px. They stack at every mobile
   width now, so that exception has nothing left to do — it is neutralised
   here rather than edited out of 52, which keeps that section readable as the
   record of what was tried. */
@media (max-width: 360px) {
	.ppp-theme .ppp-hero-actions .wp-block-button {
		flex: 0 0 auto;
	}
}

/* Very narrow phones cannot hold both hero labels on one row at full size.
   Kept outside the 768px block rather than nested inside it — nested media
   queries are valid CSS, but this file is concatenated by WordPress.com before
   it is served and there is no reason to hand a rewriter that shape. */
@media (max-width: 360px) {
	.ppp-theme .ppp-hero-actions .wp-block-button__link {
		padding: 0.75rem 0.8rem;
		font-size: var(--text-xs);
	}
}

/* --------------------------------------------------------------------------
   58. COA INDEX
   Rows emitted by [ppp_coa_index], inside the certificate sheet from section
   56 — the crosshatch, header rule and seal all come from there.

   SPECIFICITY: section 56 addresses the sheet through
   `:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))`, and :has()
   takes the specificity of its most specific argument, so that prefix alone
   scores 0,3,0 and its row rules land at 0,4,0 and 0,5,0. Plain
   `.ppp-coa-index .ppp-card` cannot reach that and silently loses — which it
   did, leaving 56's four-column row and its generated number and diamond in
   place. Repeating the prefix here is verbose but it is the honest fix: these
   rules are overriding that section and have to say so.

   The generated number and diamond that this section used to suppress are gone
   from section 56 entirely now, so that override has been removed as dead code.
   -------------------------------------------------------------------------- */

/* Name over its pills on the left, actions held right and centred against
   both — so the pills read as belonging to the name above them rather than
   floating in a column of their own. */
:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-coa-index .ppp-card {
	grid-template-columns: minmax(0, 1fr) auto;
	align-items: center;
	gap: var(--space-2xs) var(--space-md);
	padding: var(--space-md) var(--space-lg);
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-coa-index .ppp-card .ppp-badge {
	grid-column: 1;
	grid-row: 1;
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-coa-index .ppp-card .ppp-coa-index__pills,
:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-coa-index .ppp-card p.ppp-coa-index__meta {
	grid-column: 1;
	grid-row: 2;
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-coa-index .ppp-card .ppp-coa-index__actions {
	grid-column: 2;
	grid-row: 1 / span 2;
	align-self: center;
	display: flex;
	align-items: center;
	gap: var(--space-2xs);
}

/* ---- Attribute pills ---- */

/* Two classes, not one: the page carries
   `.entry-content ul { padding-left: 20px }` at 0,1,1, which outranks a bare
   .ppp-coa-index__pills and was indenting the pills 20px past the product name
   above them. */
.ppp-coa-index .ppp-coa-index__pills {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-3xs);
	margin: 0;
	padding: 0;
	list-style: none;
	max-width: none;
}

/* Section 2 puts margin between adjacent list items, which would stagger a
   wrapped row of pills. */
.ppp-coa-index__pills li + li {
	margin-top: 0;
}

.ppp-coa-index__pills .ppp-spec-pill {
	background-color: var(--color-on-dark);
	border-color: var(--color-on-dark);
	color: var(--color-accent);
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-coa-index .ppp-card p.ppp-coa-index__meta {
	margin: 0;
	font-size: var(--text-sm);
	color: var(--color-on-dark-muted);
	max-width: none;
}

/* ---- Actions ----
   These were link-coloured text: brand blue on a navy sheet, legible in theory
   and invisible in practice. Buttons now — the report solid, the verification
   outlined. */

.ppp-coa-index .ppp-coa-index__link {
	display: inline-flex;
	align-items: center;
	padding: 0.5rem 0.95rem;
	border: 1px solid var(--color-on-dark);
	border-radius: var(--radius-md);
	background-color: var(--color-on-dark);
	color: var(--color-accent);
	font-size: var(--text-xs);
	font-weight: var(--weight-bold);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	text-decoration: none;
	white-space: nowrap;
	transition: background-color var(--transition-base), border-color var(--transition-base),
		color var(--transition-base);
}

.ppp-coa-index .ppp-coa-index__link:hover,
.ppp-coa-index .ppp-coa-index__link:focus-visible {
	background-color: var(--color-card);
	border-color: var(--color-card);
	color: var(--color-accent);
}

.ppp-coa-index .ppp-coa-index__link--muted {
	background-color: transparent;
	border-color: rgba(226, 234, 246, 0.45);
	color: var(--color-on-dark);
}

.ppp-coa-index .ppp-coa-index__link--muted:hover,
.ppp-coa-index .ppp-coa-index__link--muted:focus-visible {
	background-color: rgba(255, 255, 255, 0.10);
	border-color: var(--color-on-dark);
	color: var(--color-on-dark);
}

/* Honest empty state. The page is linked from the hero and every product, so
   it has to say something true when no certificate has been added yet. */
.ppp-coa-index__empty {
	margin: 0;
	padding: var(--space-lg);
	border: 1px dashed var(--color-border-navy);
	border-radius: var(--radius-lg);
	color: var(--color-text-muted);
	font-size: var(--text-sm);
	text-align: center;
	max-width: none;
}

@media (max-width: 700px) {
	/* Narrower side padding buys back ~16px of row width, which is most of
	   what the pills need to pair up. */
	:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-coa-index .ppp-card {
		grid-template-columns: minmax(0, 1fr);
		padding: var(--space-md) var(--space-sm);
	}

	:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-coa-index .ppp-card .ppp-coa-index__actions {
		grid-column: 1;
		grid-row: 3;
		align-self: start;
		padding-top: var(--space-2xs);
	}

	/* A real two-column grid rather than a wrapping row. Wrapping put
	   "Endotoxin: Pass" and "Batch: 123-456" on lines of their own because
	   together they were a few pixels over the width — a grid pairs them by
	   construction and cannot be tipped over by one long value. Pills share
	   the width equally, so the block reads as a table of four rather than a
	   ragged stack. */
	.ppp-coa-index .ppp-coa-index__pills {
		display: grid;
		grid-template-columns: repeat(2, minmax(0, 1fr));
		gap: var(--space-3xs);
		padding: 0;
		margin: 0;
	}

	.ppp-coa-index__pills .ppp-spec-pill {
		justify-content: center;
		padding: 0.28rem 0.5rem;
		font-size: 0.7rem;
		letter-spacing: 0.02em;
	}
}

/* --------------------------------------------------------------------------
   59. TRANSPARENCY — SHEET ABSORBS ITS HEADING AND ACTION
   The section was a centred heading, a thin band of three rows, and a button
   floating underneath — three separate objects, and the sheet was the shortest
   of them. Pulling the heading and the button inside makes it one document
   instead, which is what the certificate treatment was reaching for.

   No markup change. The heading, grid and button are already siblings inside
   .ppp-container, so the sheet simply moves up one level: the container takes
   the ground, the border and the seal, and .ppp-grid keeps only its rows.

   SCOPE: `:has(.ppp-section-head):has(.ppp-hero-actions)` is what separates
   this from the other bands that share the certificate treatment — the COA
   page's index and its "what each report shows" have a heading but no action,
   so they keep the sheet on .ppp-grid and are untouched. That pairing is also
   the structural condition for the layout, not an arbitrary hook: a sheet can
   only absorb a header and a footer if it has one of each.
   -------------------------------------------------------------------------- */

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))
	.ppp-container:has(.ppp-section-head):has(.ppp-hero-actions) {
	/* The container's own gutter becomes the sheet's inset from the viewport,
	   so it never runs to the screen edge on a phone. */
	max-width: min(var(--container-max), 100% - 2 * var(--gutter));
	padding-inline: 0;
	position: relative;
	overflow: hidden;
	background-color: var(--color-dark);
	background-image:
		repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.045) 0 1px, transparent 1px 8px),
		repeating-linear-gradient(-45deg, rgba(255, 255, 255, 0.045) 0 1px, transparent 1px 8px),
		var(--gradient-navy-panel);
	border: 1px solid var(--color-on-dark-border);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-lg);
}

/* Header rule and seal move up with the ground. */
:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))
	.ppp-container:has(.ppp-section-head):has(.ppp-hero-actions)::before {
	content: "";
	position: absolute;
	inset: 0 0 auto 0;
	height: 2px;
	background-image: var(--gradient-rule);
	z-index: var(--z-raised);
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))
	.ppp-container:has(.ppp-section-head):has(.ppp-hero-actions)::after {
	content: "";
	position: absolute;
	right: -3rem;
	bottom: -3rem;
	width: 14rem;
	height: 14rem;
	border-radius: 50%;
	border: 1.75rem solid rgba(77, 163, 255, 0.10);
	pointer-events: none;
}

/* The grid is now just rows on that ground. */
:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))
	.ppp-container:has(.ppp-section-head):has(.ppp-hero-actions) .ppp-grid {
	background: none;
	border: 0;
	border-radius: 0;
	box-shadow: none;
	overflow: visible;
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))
	.ppp-container:has(.ppp-section-head):has(.ppp-hero-actions) .ppp-grid::before,
:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))
	.ppp-container:has(.ppp-section-head):has(.ppp-hero-actions) .ppp-grid::after {
	content: none;
}

/* ---- The sheet's header ----
   Left-aligned rather than centred. Centred type reads as a section title
   announcing what follows; a document's header sits at its top left. */
:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))
	.ppp-container:has(.ppp-hero-actions) .ppp-section-head {
	position: relative;
	z-index: var(--z-base);
	max-width: none;
	margin: 0;
	padding: var(--space-xl) var(--space-lg) var(--space-lg);
	text-align: left;
	border-bottom: 1px solid var(--color-on-dark-border);
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))
	.ppp-container:has(.ppp-hero-actions) .ppp-section-head h2 {
	color: var(--color-on-dark);
	margin-inline: 0;
	max-width: 22ch;
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))
	.ppp-container:has(.ppp-hero-actions) .ppp-section-head .ppp-eyebrow {
	color: var(--color-glow);
	letter-spacing: var(--tracking-eyebrow);
}

/* ---- The sheet's footer ---- */
:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))
	.ppp-container:has(.ppp-hero-actions) .ppp-hero-actions {
	position: relative;
	z-index: var(--z-base);
	margin: 0;
	padding: var(--space-lg);
	border-top: 1px solid var(--color-on-dark-border);
	justify-content: flex-end;
}

/* Section 53 fills the first hero button with --color-button, which is blue on
   a navy ground. Filled light reads here, matching the COA index actions. */
:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))
	.ppp-container:has(.ppp-hero-actions) .ppp-hero-actions .wp-block-button:first-child .wp-block-button__link {
	background-color: var(--color-on-dark) !important;
	border-color: var(--color-on-dark) !important;
	color: var(--color-accent) !important;
	border-radius: var(--radius-md);
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))
	.ppp-container:has(.ppp-hero-actions) .ppp-hero-actions .wp-block-button:first-child .wp-block-button__link:hover {
	background-color: var(--color-card) !important;
	border-color: var(--color-card) !important;
	color: var(--color-accent) !important;
}

@media (max-width: 700px) {
	:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))
		.ppp-container:has(.ppp-hero-actions) .ppp-section-head {
		padding: var(--space-lg) var(--space-md) var(--space-md);
	}

	:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))
		.ppp-container:has(.ppp-hero-actions) .ppp-hero-actions {
		padding: var(--space-md);
		justify-content: stretch;
	}

	:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge))
		.ppp-container:has(.ppp-hero-actions) .ppp-hero-actions .wp-block-button {
		width: 100%;
	}
}

/* --------------------------------------------------------------------------
   60. EMPTY FEATURE CARDS
   Deleting a feature's heading and text in the editor leaves the Group block
   behind, and a .ppp-feature with nothing in it still renders its padding,
   border and shadow — an empty card in the middle of the row.

   Emptying a block is a much easier thing to do by accident than removing it,
   so the card hides itself rather than relying on the container also being
   deleted. `:not(:has(*))` matches a group with no element children; a card
   holding anything at all is untouched.
   -------------------------------------------------------------------------- */

.ppp-feature:not(:has(*)),
.ppp-split .ppp-feature:not(:has(*)) {
	display: none;
}

/* --------------------------------------------------------------------------
   61. FAQ — SPLIT WITH A PANELLED ACCORDION
   The FAQ was the one section left with no object in it: every other band now
   carries a navy pillar, product cards, numbered steps or a certificate sheet,
   while this was bare text on a tint. That is what made it read as out of
   place rather than merely plain.

   Two moves. The accordion gets a white panel, so it becomes a thing sitting
   on the band instead of loose rows. And the heading moves beside it rather
   than above, which fills the width and leaves room for a contact route the
   page otherwise only has in the nav.

   Deliberately NOT a second navy pillar. The researchers section already owns
   that device at the top of the page; repeating it here would read as a
   template rather than a rhythm. The weight here comes from the panel on the
   right, with the left column staying open.

   No markup change: `:has(.ppp-faq)` finds the container, and its two children
   are already the heading and the accordion.
   -------------------------------------------------------------------------- */

.ppp-container:has(.ppp-faq) {
	display: grid;
	grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
	align-items: start;
	gap: var(--space-xl);
}

/* Core's flow layout margins the second child; the grid's gap owns spacing. */
.ppp-container:has(.ppp-faq) > * {
	margin-block: 0;
}

/* ---- Left column ---- */

.ppp-container:has(.ppp-faq) .ppp-section-head {
	max-width: none;
	margin: 0;
	text-align: left;
	/* Holds the heading alongside the questions while a long accordion
	   scrolls past it. */
	position: sticky;
	top: var(--space-2xl);
}

.ppp-container:has(.ppp-faq) .ppp-section-head h2 {
	font-family: var(--font-heading);
	font-weight: var(--weight-extrabold);
	font-size: var(--text-3xl);
	text-transform: uppercase;
	line-height: var(--leading-display);
	letter-spacing: var(--tracking-display);
	max-width: 12ch;
	margin-inline: 0;
}

.ppp-container:has(.ppp-faq) .ppp-section-head .ppp-lead {
	margin-top: var(--space-sm);
	font-size: var(--text-md);
	max-width: 32ch;
}

/* Contact action, when one is added to the heading group. */
.ppp-container:has(.ppp-faq) .ppp-section-head .wp-block-buttons {
	margin-top: var(--space-md);
}

.ppp-container:has(.ppp-faq) .ppp-section-head .wp-block-button__link {
	border-radius: var(--radius-md);
	border: 1px solid var(--color-border-navy-strong);
	background-color: transparent;
	color: var(--color-accent);
	font-size: var(--text-sm);
	font-weight: var(--weight-semibold);
	padding: 0.7rem 1.25rem;
}

.ppp-container:has(.ppp-faq) .ppp-section-head .wp-block-button__link:hover {
	background-color: var(--color-accent-soft);
	border-color: var(--color-accent);
	color: var(--color-accent);
}

/* ---- Right column: the accordion as a panel ---- */

.ppp-container:has(.ppp-faq) .ppp-faq {
	max-width: none;
	margin: 0;
	padding-inline: var(--space-lg);
	background-color: var(--color-card);
	border: 1px solid var(--color-border-navy);
	border-top: 1px solid var(--color-border-navy);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-md);
	overflow: hidden;
}

/* Inside a panel the last hairline would sit on the panel's own edge. */
.ppp-container:has(.ppp-faq) .ppp-faq .wp-block-details:last-child {
	border-bottom: 0;
}

.ppp-container:has(.ppp-faq) .ppp-faq .wp-block-details[open] {
	background-color: var(--color-accent-soft);
	margin-inline: calc(var(--space-lg) * -1);
	padding-inline: var(--space-lg);
}

/* ---- Stacking ---- */

@media (max-width: 900px) {
	.ppp-container:has(.ppp-faq) {
		grid-template-columns: minmax(0, 1fr);
		gap: var(--space-lg);
	}

	.ppp-container:has(.ppp-faq) .ppp-section-head {
		position: static;
	}

	.ppp-container:has(.ppp-faq) .ppp-section-head h2 {
		max-width: none;
	}
}

@media (max-width: 600px) {
	.ppp-container:has(.ppp-faq) .ppp-faq {
		padding-inline: var(--space-md);
	}

	.ppp-container:has(.ppp-faq) .ppp-faq .wp-block-details[open] {
		margin-inline: calc(var(--space-md) * -1);
		padding-inline: var(--space-md);
	}
}

/* --------------------------------------------------------------------------
   62. CONTACT FORM
   Jetpack's form, so restyle only — the markup belongs to the plugin and is
   never rebuilt, same rule as cart and checkout.

   Section 5 already gives the fields their border, radius and focus ring, so
   what was missing was the container: the page was a heading and a bare form
   sitting on the page background with nothing holding it.

   The form takes a white panel with the same brand rule across its top edge
   that the certificate sheet uses. That is deliberate continuity rather than
   decoration — a contact form and a certificate are both documents the reader
   is asked to trust, and the light panel says so without repeating the navy.

   Found by :has() rather than a page ID, so it travels to any page carrying a
   Jetpack form and survives the page being recreated on another install.
   -------------------------------------------------------------------------- */

.ppp-theme .entry-content:has(.jetpack-contact-form-container) {
	max-width: var(--container-narrow);
	margin-inline: auto;
}

/* The intro copy above the form reads as a standfirst, not body text. */
.ppp-theme .entry-content:has(.jetpack-contact-form-container) > p {
	font-size: var(--text-md);
	color: var(--color-text-muted);
	max-width: var(--measure);
}

.ppp-theme .jetpack-contact-form-container {
	position: relative;
	overflow: hidden;
	margin-top: var(--space-lg);
	padding: var(--space-xl) var(--space-lg) var(--space-lg);
	background-color: var(--color-card);
	border: 1px solid var(--color-border-navy);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-md);
}

.ppp-theme .jetpack-contact-form-container::before {
	content: "";
	position: absolute;
	inset: 0 0 auto 0;
	height: 4px;
	background-image: var(--gradient-accent);
}

/* ---- Fields ---- */

.ppp-theme .wp-block-jetpack-contact-form {
	gap: var(--space-md);
}

.ppp-theme .grunion-field-label {
	display: block;
	font-size: var(--text-xs);
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	color: var(--color-text-muted);
	margin-bottom: var(--space-3xs);
}

.ppp-theme .grunion-label-required {
	color: var(--color-danger);
	margin-left: 0.15em;
}

.ppp-theme .jetpack-contact-form-container textarea {
	min-height: 9rem;
	resize: vertical;
}

/* ---- Submit ---- */

.ppp-theme .jetpack-contact-form-container .wp-block-button__link {
	background-color: var(--color-button);
	border: 1px solid var(--color-button);
	border-radius: var(--radius-md);
	color: var(--color-button-text);
	font-weight: var(--weight-semibold);
	padding: 0.85rem 1.9rem;
	box-shadow: none;
}

.ppp-theme .jetpack-contact-form-container .wp-block-button__link:hover {
	background-color: var(--color-button-hover);
	border-color: var(--color-button-hover);
	color: var(--color-button-text);
}

/* ---- Validation and success ---- */

.ppp-theme .contact-form__error,
.ppp-theme .contact-form__error-message {
	color: var(--color-danger);
	font-size: var(--text-sm);
}

.ppp-theme .contact-form__input-error input,
.ppp-theme .contact-form__input-error textarea {
	border-color: var(--color-danger);
}

.ppp-theme .contact-form-submission.submission-success {
	padding: var(--space-lg);
	background-color: var(--color-accent-soft);
	border: 1px solid var(--color-border-navy);
	border-radius: var(--radius-lg);
}

@media (max-width: 600px) {
	.ppp-theme .jetpack-contact-form-container {
		padding: var(--space-lg) var(--space-md) var(--space-md);
	}
}

/* --------------------------------------------------------------------------
   63. CERTIFICATE STRIP
   Replaces the lone "View COA" button in the product summary. The batch was a
   pill among pills and the certificate a button among buttons, which put the
   page's whole argument at the same weight as its shipping note.

   The batch number sets in mono, like the record numbers on the COA index —
   it is data, not prose, and reads as something to check against a vial.

   Section 29 already excludes the batch from the pill row on this template, so
   it is stated once, here.
   -------------------------------------------------------------------------- */

.ppp-certificate {
	display: flex;
	flex-direction: column;
	gap: var(--space-2xs);
	margin-block: var(--space-md);
	padding: var(--space-md);
	background-color: var(--color-accent-soft);
	border: 1px solid var(--color-border-navy);
	border-radius: var(--radius-lg);
}

.ppp-certificate__batch {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: var(--space-2xs);
	margin: 0;
	max-width: none;
}

.ppp-certificate__label {
	font-size: var(--text-xs);
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	color: var(--color-text-muted);
}

.ppp-certificate__value {
	font-family: var(--font-mono);
	font-size: var(--text-md);
	font-weight: var(--weight-bold);
	font-variant-numeric: tabular-nums;
	color: var(--color-accent);
	word-break: break-word;
}

.ppp-certificate__note {
	margin: 0;
	font-size: var(--text-sm);
	color: var(--color-text-muted);
	line-height: var(--leading-snug);
	max-width: none;
}

.ppp-certificate__actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-2xs);
	margin-top: var(--space-2xs);
}

.ppp-certificate__action {
	font-size: var(--text-sm);
	padding: 0.6rem 1.1rem;
	border-radius: var(--radius-md);
}

@media (max-width: 480px) {
	.ppp-certificate__actions {
		flex-direction: column;
		align-items: stretch;
	}

	.ppp-certificate__action {
		text-align: center;
	}
}

/* --------------------------------------------------------------------------
   64. PRODUCT DOSSIER
   The specifications and the batch's certificate, full width beneath the image
   and the cart. Both were in the narrow summary column, where a row of pills
   wrapped into fragments and the certificate read as one more button among
   buttons — "pieced together" was the right description.

   Same navy sheet as the COA page, so documentation looks the same wherever a
   customer meets it on this site.
   -------------------------------------------------------------------------- */

.ppp-dossier {
	position: relative;
	overflow: hidden;
	margin-block: var(--space-lg);
	padding: var(--space-md) var(--space-lg);
	background-color: var(--color-dark);
	background-image:
		repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.045) 0 1px, transparent 1px 8px),
		repeating-linear-gradient(-45deg, rgba(255, 255, 255, 0.045) 0 1px, transparent 1px 8px),
		var(--gradient-navy-panel);
	border: 1px solid var(--color-on-dark-border);
	border-radius: var(--radius-lg);
	box-shadow: var(--shadow-lg);
}

.ppp-dossier::before {
	content: "";
	position: absolute;
	inset: 0 0 auto 0;
	height: 2px;
	background-image: var(--gradient-rule);
}

/* Seal, cropped by the corner — the same mark the COA sheet carries. */
.ppp-dossier::after {
	content: "";
	position: absolute;
	right: -3rem;
	bottom: -3rem;
	width: 9rem;
	height: 9rem;
	border-radius: 50%;
	border: 1.1rem solid rgba(77, 163, 255, 0.10);
	pointer-events: none;
}

.ppp-dossier__head {
	position: relative;
	z-index: var(--z-base);
	margin-bottom: var(--space-sm);
	padding-bottom: var(--space-xs);
	border-bottom: 1px solid var(--color-on-dark-border);
}

.ppp-dossier__head .ppp-eyebrow {
	color: var(--color-glow);
	letter-spacing: var(--tracking-eyebrow);
	margin-bottom: var(--space-3xs);
}

.ppp-dossier__title {
	font-family: var(--font-heading);
	font-size: var(--text-lg);
	font-weight: var(--weight-extrabold);
	text-transform: uppercase;
	letter-spacing: var(--tracking-display);
	line-height: var(--leading-display);
	color: var(--color-on-dark);
	margin: 0;
}

.ppp-dossier__body {
	position: relative;
	z-index: var(--z-base);
	display: flex;
	flex-direction: column;
	gap: var(--space-sm);
}

/* Pills invert for the dark ground, matching the COA index rows. */
.ppp-dossier .ppp-product-specs {
	margin: 0;
}

.ppp-dossier .ppp-spec-pills {
	display: flex;
	flex-wrap: wrap;
	gap: var(--space-3xs);
	margin: 0;
	padding: 0;
	list-style: none;
	max-width: none;
}

.ppp-dossier .ppp-spec-pills li + li {
	margin-top: 0;
}

.ppp-dossier .ppp-spec-pill {
	background-color: var(--color-on-dark);
	border-color: var(--color-on-dark);
	color: var(--color-accent);
}

/* The certificate loses its own frame in here — the dossier is the frame. */
.ppp-dossier .ppp-certificate {
	margin: 0;
	padding: 0;
	background: none;
	border: 0;
	border-radius: 0;
}

.ppp-dossier .ppp-certificate__label {
	color: var(--color-on-dark-muted);
}

.ppp-dossier .ppp-certificate__value {
	color: var(--color-glow);
	font-size: var(--text-lg);
}

.ppp-dossier .ppp-certificate__note {
	color: var(--color-on-dark-muted);
}

.ppp-dossier .ppp-certificate__action {
	background-color: var(--color-on-dark);
	border: 1px solid var(--color-on-dark);
	color: var(--color-accent);
}

.ppp-dossier .ppp-certificate__action:hover {
	background-color: var(--color-card);
	border-color: var(--color-card);
	color: var(--color-accent);
}

.ppp-dossier .ppp-certificate__action.ppp-btn--secondary {
	background-color: transparent;
	border-color: rgba(226, 234, 246, 0.45);
	color: var(--color-on-dark);
}

.ppp-dossier .ppp-certificate__action.ppp-btn--secondary:hover {
	background-color: rgba(255, 255, 255, 0.10);
	border-color: var(--color-on-dark);
	color: var(--color-on-dark);
}

@media (min-width: 48rem) {
	/* Specs left, certificate right — the width is there, so use it. */
	.ppp-dossier__body {
		flex-direction: row;
		align-items: flex-start;
		gap: var(--space-lg);
	}

	.ppp-dossier .ppp-product-specs {
		flex: 1 1 55%;
	}

	.ppp-dossier .ppp-certificate {
		flex: 1 1 45%;
		padding-left: var(--space-lg);
		border-left: 1px solid var(--color-on-dark-border);
	}
}

/* --------------------------------------------------------------------------
   65. QUANTITY PRICING
   The tier a quantity lands in is marked as the number changes, and the price
   for that quantity is stated outright. Display only — the charged price is
   set in ppp_apply_quantity_pricing(), and both read the same tiers.
   -------------------------------------------------------------------------- */

.ppp-discount-tier {
	transition: background-color var(--transition-base), border-color var(--transition-base);
}

.ppp-discount-tier.is-active {
	background-color: var(--color-accent-soft-strong);
	border-color: var(--color-border-navy-strong);
}

.ppp-discount-card__readout {
	margin: var(--space-sm) 0 0;
	padding-top: var(--space-sm);
	border-top: 1px dashed var(--color-border-navy);
	font-size: var(--text-md);
	font-weight: var(--weight-semibold);
	color: var(--color-text);
	max-width: none;
}

.ppp-discount-card__readout.is-discounted {
	color: var(--color-success);
}

/* --------------------------------------------------------------------------
   66. TIERED HEADLINE PRICE
   When a quantity reaches a tier, the summary's price restates itself at the
   tier rate with the original struck through. Written by
   assets/js/quantity-price.js; the charged price is still set server-side, and
   without the script the price simply stays as WooCommerce rendered it.
   -------------------------------------------------------------------------- */

.ppp-price--tiered {
	display: flex;
	align-items: baseline;
	flex-wrap: wrap;
	gap: var(--space-2xs);
}

.ppp-price--tiered del {
	opacity: 0.55;
	font-size: 0.7em;
	text-decoration-thickness: 1px;
}

.ppp-price--tiered ins {
	display: inline-flex;
	align-items: baseline;
	gap: 0.25em;
	background: none;
	text-decoration: none;
	color: var(--color-success);
}

.ppp-price__unit {
	font-size: 0.5em;
	font-weight: var(--weight-semibold);
	letter-spacing: var(--tracking-wide);
	text-transform: uppercase;
	color: var(--color-text-muted);
}

/* --------------------------------------------------------------------------
   NOTE ON ASTRA'S BODY CLASSES
   `ast-desktop`, `ast-mobile` and `ast-header-break-point` all sit on <body>,
   which is the same element that carries `ppp-theme`. So `.ppp-theme
   .ast-desktop` — with a space — asks for an .ast-desktop INSIDE a .ppp-theme
   and matches nothing at all. Fourteen rules in this file were written that
   way and had never once applied.
   Always pair them as `:is(.ppp-theme .x, .ppp-theme.x)`, the same shape this
   file already uses for WooCommerce's two contexts.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   67. HEADER NAV — OUTLINED ITEMS
   The menu was plain text on the dark band. Each item takes an outline now, so
   the nav reads as a row of controls rather than a line of words.

   This also fixes the alignment. Astra prints a dropdown-arrow SVG inside
   every link whether or not the item has a submenu, and the items are a mix of
   custom links and page links — enough difference for one to sit off the line.
   An explicit inline-flex box with identical padding on every item removes the
   variable entirely, rather than nudging the one that looked wrong.

   Supersedes the underline marker above: the box carries the current-page
   state now, so two markers would be saying the same thing twice.
   -------------------------------------------------------------------------- */

:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu > .menu-item > .menu-link {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-3xs);
	padding: 0.5rem 0.95rem;
	border: 1px solid var(--color-on-dark-border);
	border-radius: var(--radius-md);
	line-height: 1.2;
	white-space: nowrap;
	transition: color var(--transition-base), border-color var(--transition-base),
		background-color var(--transition-base);
}

/* The underline is redundant now the item has a frame. */
:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu > .menu-item > .menu-link::after {
	content: none;
}

:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu > .menu-item:hover > .menu-link {
	color: var(--color-on-dark);
	border-color: rgba(226, 234, 246, 0.55);
	background-color: rgba(255, 255, 255, 0.06);
}

/* Current page: filled rather than merely outlined, so it is distinguishable
   from a hover on a neighbour. */
:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu > .current-menu-item > .menu-link,
:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu > .current-menu-ancestor > .menu-link {
	color: var(--color-on-dark);
	border-color: var(--color-glow);
	background-color: rgba(77, 163, 255, 0.14);
}

/* Astra renders the dropdown chevron on every item, submenu or not. Inside a
   box an arrow that opens nothing reads as a broken control. */
:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu > .menu-item:not(.menu-item-has-children) > .menu-link .ast-icon {
	display: none;
}

/* Breathing room between the boxes, which the text row did not need. */
:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu > .menu-item + .menu-item {
	margin-left: var(--space-2xs);
}

/* Off-canvas on phones stays a list: boxing a full-width stack would make four
   large slabs of nothing. */
@media (max-width: 921px) {
	.ppp-theme .main-navigation .menu-link {
		border: 0;
		background: none;
	}
}

/* --------------------------------------------------------------------------
   68. HEADER NAV — ALIGNMENT AND THE CART
   Three corrections to section 67.

   ALIGNMENT. The items are structurally identical — same spans, same classes
   — so nothing in the markup explains one sitting off the line. Rather than
   hunt Astra's own spacing, the row is made to align its boxes explicitly:
   each item is a centred flex box and the row centres them against each other.
   That fixes the symptom whatever the cause, and cannot drift when an item is
   added.

   STICKING. Section 67 highlighted on `:focus-within`. Click a nav link, come
   back, and focus is still on it — so the item stayed lit as though it were
   the current page. Hover alone now, with the global focus ring in section 14
   still serving keyboard users.

   THE CART. Its outline is not ours. Astra renders it as
   `ast-menu-cart-with-border ast-menu-cart-outline`, both set in the
   customizer. It is turned off here rather than there because a customizer
   setting does not travel with the theme, and this needs to be off wherever
   the theme lands.
   -------------------------------------------------------------------------- */

:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu {
	align-items: center;
}

:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu > .menu-item {
	display: flex;
	align-items: center;
	margin-block: 0;
}

/* Astra's own cart frame. */
.ppp-theme .ast-site-header-cart.ast-menu-cart-with-border,
.ppp-theme .ast-site-header-cart.ast-menu-cart-outline,
.ppp-theme .ast-site-header-cart.ast-menu-cart-fill {
	border: 0;
	border-radius: 0;
	background: none;
	padding: 0;
}

.ppp-theme .ast-site-header-cart .ast-cart-menu-wrap,
.ppp-theme .ast-addon-cart-wrap {
	border: 0;
	background: none;
}

/* --------------------------------------------------------------------------
   69. FEATURED PRODUCTS — TWO UP ON PHONES
   The row used to collapse to one card below 520px. Two cards side by side
   show four products in the space one column showed two, which is the point of
   a featured row — it is there to be scanned, not scrolled.

   Two cards on a 360px screen is about 150px each, so the card has to give
   ground to earn it: tighter gap and padding, smaller title, and the price on
   its own line rather than fighting the button for width.
   -------------------------------------------------------------------------- */

@media (max-width: 520px) {
	.ppp-theme .ppp-products .woocommerce ul.products,
	.ppp-theme .ppp-products .woocommerce ul.products:not(.elementor-grid) {
		gap: var(--space-2xs);
	}

	.ppp-theme .ppp-products .woocommerce ul.products li.product {
		padding: var(--space-2xs);
	}

	.ppp-theme .ppp-products .woocommerce ul.products li.product .woocommerce-loop-product__title {
		font-size: var(--text-sm);
		line-height: var(--leading-snug);
	}

	.ppp-theme .ppp-products .woocommerce ul.products li.product .price {
		font-size: var(--text-sm);
	}

	/* Three pills will not fit across 150px. One is enough to differentiate a
	   card at this size; the rest are on the product page. */
	.ppp-theme .ppp-products .woocommerce ul.products li.product .ppp-card-pills .ppp-spec-pill:nth-child(n + 2) {
		display: none;
	}

	.ppp-theme .ppp-products .woocommerce ul.products li.product .button,
	.ppp-theme .ppp-products .woocommerce ul.products li.product .ast-on-card-button {
		font-size: var(--text-xs);
		padding: 0.55rem 0.6rem;
	}
}

/* --------------------------------------------------------------------------
   70. NAV DROPDOWN PARENTS

   The client's menu has an Affiliate item with two children, which this build
   had never seen — every menu it was designed against was flat.

   For a parent item Astra emits a <button class="ast-menu-toggle"> as a SIBLING
   of the link, inside the same <li>. The global button rule in section 2 styles
   every <button> on the site, so that toggle inherited the full blue pill and
   rendered as a solid box stacked underneath the menu item. It was doing
   exactly what it was told; nothing had ever told it otherwise.

   Astra's own toggle is a control, not a button in the design sense. Everything
   below strips our chrome back off it and returns display to Astra, which knows
   when it should and should not be visible.
   -------------------------------------------------------------------------- */

.ppp-theme .main-header-menu .ast-menu-toggle,
.ppp-theme .ast-builder-menu .ast-menu-toggle,
.ppp-theme .main-navigation .ast-menu-toggle,
.ppp-theme .ast-mobile-popup-drawer .ast-menu-toggle,
.ppp-theme .main-header-menu .ast-menu-toggle:hover,
.ppp-theme .ast-builder-menu .ast-menu-toggle:hover,
.ppp-theme .main-navigation .ast-menu-toggle:hover,
.ppp-theme .ast-mobile-popup-drawer .ast-menu-toggle:hover {
	background: none;
	background-color: transparent;
	border: 0;
	border-radius: 0;
	box-shadow: none;
	padding: 0 var(--space-3xs);
	min-height: 0;
	min-width: 0;
	width: auto;
	color: inherit;
	font-size: inherit;
	font-weight: inherit;
	letter-spacing: normal;
	text-transform: none;
	transform: none;
}

/* The arrow inside the link itself is a <span role="button">, so it escapes the
   rule above. It still picks up the inherited type treatment and needs to sit
   on the text baseline rather than pushing the row taller. */
.ppp-theme .main-header-menu .dropdown-menu-toggle,
.ppp-theme .ast-builder-menu .dropdown-menu-toggle {
	display: inline-flex;
	align-items: center;
	padding: 0;
	margin-left: var(--space-3xs);
	line-height: 1;
	color: inherit;
}

.ppp-theme .main-header-menu .dropdown-menu-toggle .ast-arrow-svg,
.ppp-theme .main-header-menu .ast-menu-toggle .ast-arrow-svg {
	width: 0.6em;
	height: auto;
	fill: currentColor;
}

/* On desktop the toggle button is redundant — the dropdown opens on hover and
   the arrow inside the link already signals it. Astra hides it; this states it
   outright so a future global button rule cannot bring it back.

   Both classes sit on <body>, so they have to be paired — see the note on
   Astra's body classes earlier in this file. */
:is(.ppp-theme .ast-desktop, .ppp-theme.ast-desktop) .main-header-menu > .menu-item > .ast-menu-toggle {
	display: none;
}

/* A parent item must not grow taller than its flat siblings. The outline in
   section 67 draws around the link, so the <li> has to stay a plain flex row
   or the button lands on a second line underneath — which is precisely how
   this surfaced. */
.ppp-theme .main-header-menu > .menu-item.menu-item-has-children {
	display: flex;
	align-items: center;
}

/* --------------------------------------------------------------------------
   71. WOOCOMMERCE CONTAINER GUTTER

   The client's shop and product pages run Astra's "Page Builder" content
   layout, which this build had never been tested against. Astra strips the
   container for it:

       .ast-page-builder-template .site-content > .ast-container {
           max-width: 100%;
           padding: 0;
       }

   Section 29 already sets a max-width for single products and wins that half
   on source order — the two selectors tie at 0,3,0 and ours loads later. But
   nothing ever restored the padding, so the layout kept Astra's `padding: 0`
   and the content sat flush against the container edge, and against the screen
   edge on a phone.

   The client hit this themselves: their Additional CSS carried a "Shop Page
   Width Constraint" block forcing max-width and 24px of side padding back on.
   That CSS is tied to their old theme and stops applying the moment this one
   activates, so the constraint has to live here instead.

   Scored deliberately above Astra's 0,3,0 rather than left to source order.
   Which container layout a site uses is a customizer setting, so it differs
   per install — this must not depend on winning a tie.
   -------------------------------------------------------------------------- */

/* Cart and checkout are excluded on purpose. This rule sets the width of the
   box the PRISM payment iframe sits inside, and their own Additional CSS —
   three attempts at forcing that box full-width on mobile — is evidence of how
   little room there is to get it wrong. Shop and product pages needed the
   gutter; the purchase flow keeps whatever Astra and their CSS already give
   it. */
.ppp-theme.woocommerce-page:not(.woocommerce-cart):not(.woocommerce-checkout) .site-content > .ast-container,
.ppp-theme.woocommerce:not(.woocommerce-cart):not(.woocommerce-checkout) .site-content > .ast-container,
.ppp-theme:not(.woocommerce-cart):not(.woocommerce-checkout) .woocommerce .site-content > .ast-container {
	max-width: var(--container-wide);
	padding-left: var(--gutter);
	padding-right: var(--gutter);
	margin-left: auto;
	margin-right: auto;
}

/* The inner WooCommerce wrapper must not add a second gutter on top of the
   one above, or product pages end up inset twice as far as every other page. */
.ppp-theme.woocommerce-page:not(.woocommerce-cart):not(.woocommerce-checkout) .ast-woocommerce-container,
.ppp-theme.woocommerce:not(.woocommerce-cart):not(.woocommerce-checkout) .ast-woocommerce-container {
	padding-left: 0;
	padding-right: 0;
	max-width: none;
}

/* --------------------------------------------------------------------------
   72. HIDE THE DISCOUNT PLUGIN'S OWN TABLE

   The store runs Discount Rules for WooCommerce, which prints its own bulk
   pricing table on every product page. Its tiers are the same ones the panel in
   section 65 shows, so the page carried the offer twice in two different
   designs.

   The plugin keeps doing the maths — it owns what a customer is charged, and
   `ppp_external_quantity_pricing()` stands our cart pricing down so the two
   cannot both apply. Only its presentation is suppressed.

   Hidden rather than switched off in the plugin's settings, for the usual
   reason: a settings change lives in the database and does not travel, so the
   duplicate would reappear the moment this theme reached another install.

   Kept in the DOM rather than removed so a mismatch stays findable — if the
   panel above ever disagrees with what the cart charges, this table is the
   plugin's own answer and can be revealed with one line.
   -------------------------------------------------------------------------- */

.ppp-theme .awdr-bulk-customizable-table,
.ppp-theme table.awdr-bulk-customizable-table,
.ppp-theme .wdr_bulk_table_msg {
	display: none !important;
}

/* --------------------------------------------------------------------------
   73. SUMMARY COLUMN ORDER

   The summary was relying on DOM order, which means whatever a plugin injects
   lands wherever WooCommerce's hook priorities happen to put it. On the client's
   store that put WPLoyalty's "Purchase & earn 31 points!" between the price and
   the quantity panel — the most prominent slot on the page, for the least
   important line on it.

   Ordering explicitly fixes that, but the important part is the default below.
   Section 29's mobile layout taught this the hard way three times: a flex child
   with no `order` gets 0, and 0 beats every positive value, so an unrecognised
   element does not land at the end — it leaps to the very front. Setting every
   child to 50 first means anything this file has never heard of settles at the
   bottom, which is the safe place for it. Named elements then claim their
   position from there.

   This store runs 37 plugins. More things will inject into this column.
   -------------------------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary.entry-summary {
	display: flex;
	flex-direction: column;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary.entry-summary > * {
	order: 50;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary .woocommerce-breadcrumb { order: 1; }
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary .product_title          { order: 2; }
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary > .price                { order: 3; }
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary .ppp-product-blurb      { order: 4; }
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary .ppp-stock-pill         { order: 5; }
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary .ppp-discount-card      { order: 6; }
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary form.cart               { order: 7; }

/* Loyalty points sit under the buy button. It is a reward for a decision
   already made, so it has no business competing with the price. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary .wlr-product-message {
	order: 8;
	margin-top: var(--space-2xs);
	font-size: var(--text-sm);
	color: var(--color-text-muted);
}

/* Astra prints "Availability:" as a label with the value in a sibling this
   layout does not use, so it arrives as a dangling word. The stock pill says
   the same thing and says it better. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary .ast-stock-detail {
	display: none;
}

/* --------------------------------------------------------------------------
   74. PRICE AND STOCK ON ONE ROW

   The pill is declared inline-flex, but a flex column stretches its children
   across the cross axis, so it was being blown out to the full width of the
   summary and reading as a banner rather than a label. It also cost a whole
   row of vertical space to say one word.

   Switching the column to a two-track grid lets the price and the pill share a
   row. Everything else still spans both tracks, so the change is invisible to
   every other element in here. The `order` values from section 73 carry over —
   grid honours them for auto-placement — and the pill is given the price's own
   order so the two land side by side rather than with the blurb between them.

   `justify-self: start` is what actually restores the pill to its content
   width: grid stretches items too unless told otherwise, which is the same
   trap in a different shape.
   -------------------------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary.entry-summary {
	display: grid;
	grid-template-columns: auto minmax(0, 1fr);
	align-items: start;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary.entry-summary > * {
	grid-column: 1 / -1;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary > .price {
	grid-column: 1;
	align-self: center;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) div.product .summary .ppp-stock-pill {
	grid-column: 2;
	order: 3;
	justify-self: start;
	align-self: center;
	margin: 0 0 0 var(--space-sm);
}

/* --------------------------------------------------------------------------
   75. LOYALTY POINTS OFF THE SHOP CARDS

   WPLoyalty prints "Purchase & earn 31 points!" into every card in the loop as
   well as onto the single product page. On a card it is noise: it repeats down
   the whole grid, it is the least useful line on a tile that has to carry a
   name, three specs, a price and a button, and it pushes the button below the
   fold on a phone.

   It stays on the single product page, where section 73 places it under the
   add-to-cart button — a reward for a decision already made, shown at the point
   the decision is being made.

   `!important` is required, not lazy: the plugin writes `display: block` as an
   inline style on the element, and nothing in a stylesheet outranks that.
   -------------------------------------------------------------------------- */

.ppp-theme ul.products li.product .wlr-product-message,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .wlr-product-message {
	display: none !important;
}

/* --------------------------------------------------------------------------
   76. TIGHTER SHOP CARDS

   The cards were reading loose rather than premium, and the space was coming
   from three places at once.

   The image. Their photography is a small vial centred in a large canvas, so
   the file already contains generous whitespace. Rendering it at natural height
   then padding it as well stacked one margin on another. A fixed square box
   with `contain` gives every card the same picture height and stops the tallest
   image dictating the row.

   The gap above the button. `margin-top: auto` pins buttons to a common
   baseline, which is right — a ragged row of buttons looks worse than a gap.
   But it means every card inherits the height of the tallest in its row, so any
   variation in the content above shows up as dead space below. The fix is to
   reduce that variation, not to unpin the button: a fixed image box and a
   capped pill row do most of it.

   The pill row. A third pill wraps to a second line on one card and not the
   next, which is the single largest source of height variance. Two pills fit
   across every card width the grid produces, so the row stays one line and the
   cards stay level.
   -------------------------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-thumbnail-wrap {
	padding: var(--space-2xs);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-thumbnail-wrap img {
	aspect-ratio: 1 / 1;
	object-fit: contain;
	height: auto;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap {
	gap: var(--space-3xs);
	padding: var(--space-xs) var(--space-sm) var(--space-sm);
}

/* All three pills stay. They wrap to a second row on narrower cards, which
   costs some height evenness — that is a deliberate trade: the specs are the
   reason a researcher stops on a card at all. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .ppp-card-pills {
	margin-top: 0;
	flex-wrap: wrap;
}

/* The price is the line the eye should land on after the name, so it gets air
   above it and none below — the button supplies the rest. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .price {
	margin: var(--space-2xs) 0 0;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .button {
	margin-top: var(--space-sm);
}

/* Only once the row is tall enough for a pinned baseline to be worth having. */
@media (min-width: 48rem) {
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .button {
		margin-top: auto;
	}
}

/* --------------------------------------------------------------------------
   77. CARD PRICE

   Section 29 reverses the sale price on the single product page so the amount
   payable leads and the original follows it, smaller and muted. That rule is
   scoped to `div.product`, so the shop card never received it and showed
   WooCommerce's raw order instead: struck-through original first, price second.

   Reading "$34.95 $31.45" left to right, the first number is the one that is
   not true. The same product read the opposite way round on its own page.

   The card now matches: price first, original after it as a footnote.
   -------------------------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .price {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	justify-content: flex-start;
	gap: var(--space-2xs);
	margin: var(--space-2xs) 0 0;
	font-size: var(--text-base);
	line-height: 1.3;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .price ins {
	order: 1;
	text-decoration: none;
	font-weight: var(--weight-bold);
	color: var(--color-text);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .price del {
	order: 2;
	font-size: var(--text-sm);
	font-weight: var(--weight-normal);
	color: var(--color-text-muted);
	opacity: 1;
}

/* --------------------------------------------------------------------------
   78. COMPACT CARD

   Matching the reference card, which is doing three things this one was not.

   1. The price and the button share the bottom row. That is the whole reason
      it reads compact: two elements that each claimed a full-width line now
      claim one between them, and the dead space above the button disappears
      because there is no longer a full-width button to push down.

   2. Pills are outlined, not filled. A filled grey pill is a background; an
      outlined one is a label. Three of them stacked read as blocks of tone
      before they read as data.

   3. The rhythm is tight and even. Every gap in the summary is the same small
      value, so the block reads as one unit rather than four stacked ones.

   The button is no longer pinned to the bottom of the tallest card in the row.
   Pinning bought a level baseline at the cost of a gap on every card shorter
   than its neighbour, and with the image box fixed in section 76 the remaining
   variation is one pill row. Content-height cards are what the reference does.
   -------------------------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap {
	display: grid;
	grid-template-columns: 1fr auto;
	align-content: start;
	gap: var(--space-3xs) var(--space-2xs);
	padding: var(--space-sm);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap > * {
	grid-column: 1 / -1;
}

/* ---- the bottom row ----------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .price {
	grid-column: 1;
	align-self: center;
	justify-self: start;
	margin: var(--space-2xs) 0 0;
	font-size: var(--text-lg);
	font-weight: var(--weight-bold);
	color: var(--color-text);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap .button,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .button {
	grid-column: 2;
	align-self: center;
	justify-self: end;
	width: auto;
	margin: var(--space-2xs) 0 0;
	padding: var(--space-2xs) var(--space-sm);
	font-size: var(--text-xs);
	letter-spacing: var(--tracking-wide);
	white-space: nowrap;
}

/* ---- outlined pills ----------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .ppp-card-pills .ppp-spec-pill {
	background: none;
	background-color: transparent;
	border: 1px solid currentColor;
	border-radius: var(--radius-pill);
	padding: 0.2rem 0.6rem;
	font-size: var(--text-xs);
	color: var(--color-accent);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .ppp-card-pills .ppp-spec-pill strong,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .ppp-card-pills .ppp-spec-pill b {
	color: var(--color-accent);
	font-weight: var(--weight-semibold);
}

/* ---- title ------------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .woocommerce-loop-product__title {
	margin: 0;
	font-size: var(--text-lg);
	font-weight: var(--weight-bold);
	line-height: 1.2;
}

/* --------------------------------------------------------------------------
   79. SHOP ARCHIVE GRID GAP

   The featured grid on the home page was given an explicit gap in section 20.
   The shop archive never was — it inherited whatever WooCommerce's own float
   layout left behind, which is a horizontal margin and no vertical one at all,
   so rows sat flush and each card's bottom edge met the next card's top.

   Astra's own product grid CSS varies with its column setting, which is a
   customizer value and therefore differs per install. Stating the gap here
   rather than relying on that is the same rule as everywhere else in this
   file: if the design depends on it, it belongs in the theme.
   -------------------------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products:not(.elementor-grid) {
	gap: var(--space-md);
	row-gap: var(--space-md);
}

/* --------------------------------------------------------------------------
   80. NARROW CARDS STACK

   Section 78 puts the price and the button on one row, which is what makes the
   card read compact — at desktop widths. Two up on a phone each card is about
   180px wide, and the button column is sized by "ADD TO CART" with nowrap, so
   it takes what it needs and the title column gets whatever is left. Below a
   certain width that is not enough for either, and the grid overflows its own
   card: titles clipped mid-word, the button cut off at the edge.

   Below 600px the summary drops to a single column and the two stack. The card
   gets taller, which is the correct trade — a taller card is readable, a
   clipped one is broken.

   `min-width: 0` matters as much as the column change. Grid items default to
   `min-width: auto`, which refuses to shrink below their content, and that
   refusal is what let the overflow happen rather than the text wrapping.
   -------------------------------------------------------------------------- */

@media (max-width: 600px) {
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap {
		grid-template-columns: minmax(0, 1fr);
		padding: var(--space-2xs);
	}

	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap > * {
		grid-column: 1;
		min-width: 0;
	}

	/* grid-column has to be restated here. Section 78 pins the button to
	   column 2 with a more specific selector than the `> *` rule above, so
	   without this the button stays in the second column of a grid that no
	   longer has one and lands on top of the price. */
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .price {
		grid-column: 1;
		justify-self: start;
		margin-top: var(--space-2xs);
		font-size: var(--text-base);
	}

	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap .button,
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .button {
		grid-column: 1;
		justify-self: stretch;
		width: 100%;
		margin-top: var(--space-2xs);
		padding: var(--space-2xs) var(--space-3xs);
		text-align: center;
	}

	/* No overflow-wrap here, deliberately. `anywhere` reduces an element's
	   intrinsic minimum width to a single character, so a grid track sized
	   from its content collapses to one letter and the whole card wraps
	   vertically. `min-width: 0` above is what actually permits wrapping;
	   normal word breaks are all these names need. */
	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .woocommerce-loop-product__title {
		font-size: var(--text-base);
	}

	:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .ppp-card-pills .ppp-spec-pill {
		padding: 0.15rem 0.45rem;
		font-size: 0.7rem;
		white-space: nowrap;
	}
}


/* --------------------------------------------------------------------------
   81. CHECKOUT LAYOUT INHERITED FROM THE CLIENT

   These rules are not ours. They are lifted verbatim from the store's
   Customizer -> Additional CSS, which is stored per theme and therefore stops
   applying the moment this theme is activated.

   About a third of that file forced the checkout into one full-width column on
   phones. Their checkout is an Elementor page wrapping a WooCommerce
   shortcode, and the PRISM gateway renders in an iframe inside `.payment_box`;
   without these the iframe is clipped on a narrow screen and a customer cannot
   pay. It took them four attempts at slightly different breakpoints to get it
   working, which is why all four are still here.

   Carried in the theme rather than pasted back into Additional CSS after
   activation. Pasting leaves a window — however short — where the theme is
   live and the checkout is broken, and it is one more thing to remember during
   a migration. In the theme it applies the instant the theme does.

   DO NOT TIDY THIS. The duplication is theirs, it is known to work on their
   store, and mid-migration is the wrong moment to discover which copy was
   load-bearing. Section 71 deliberately excludes cart and checkout so nothing
   here is competing with our own rules.
   -------------------------------------------------------------------------- */

/* Make WooCommerce checkout full width on mobile */

@media only screen and (max-width: 768px) {
    body.woocommerce-checkout .site-main,

    body.woocommerce-checkout .entry-content,

    body.woocommerce-checkout .woocommerce,

    body.woocommerce-checkout form.checkout,

    body.woocommerce-checkout #order_review,

    body.woocommerce-checkout #payment {
        width: 100% !important;
        max-width: 100% !important;
        min-width: 0 !important;
        box-sizing: border-box !important;
    }

    /* Stack billing and order-review columns */

    body.woocommerce-checkout #customer_details,

    body.woocommerce-checkout #order_review_heading,

    body.woocommerce-checkout #order_review {
        float: none !important;
        width: 100% !important;
        margin-left: 0 !important;
        margin-right: 0 !important;
    }

    /* Widen the payment method box */

    body.woocommerce-checkout .woocommerce-checkout-payment,

    body.woocommerce-checkout .payment_box,

    body.woocommerce-checkout .payment_box iframe {
        width: 100% !important;
        max-width: 100% !important;
        box-sizing: border-box !important;
    }

    /* Reduce excessive side padding */

    body.woocommerce-checkout .payment_box {
        padding-left: 15px !important;
        padding-right: 15px !important;
    }
}
@media only screen and (max-width: 768px) {
    body.woocommerce-checkout .container,

    body.woocommerce-checkout .content-area,

    body.woocommerce-checkout .site-content,

    body.woocommerce-checkout .wp-site-blocks {
        width: 100% !important;
        max-width: 100% !important;
        padding-left: 12px !important;
        padding-right: 12px !important;
        margin-left: auto !important;
        margin-right: auto !important;
        box-sizing: border-box !important;
    }
}
/* Force WooCommerce checkout into one full-width column on mobile */

@media only screen and (max-width: 768px) {
    body.woocommerce-checkout .elementor-widget-shortcode,

    body.woocommerce-checkout .elementor-shortcode,

    body.woocommerce-checkout .woocommerce,

    body.woocommerce-checkout form.checkout {
        width: 100% !important;
        max-width: 100% !important;
        min-width: 0 !important;
        display: block !important;
    }

    body.woocommerce-checkout .col2-set,

    body.woocommerce-checkout .col2-set .col-1,

    body.woocommerce-checkout .col2-set .col-2,

    body.woocommerce-checkout #customer_details,

    body.woocommerce-checkout #order_review_heading,

    body.woocommerce-checkout #order_review {
        float: none !important;
        clear: both !important;
        width: 100% !important;
        max-width: 100% !important;
        min-width: 0 !important;
        margin-left: 0 !important;
        margin-right: 0 !important;
        display: block !important;
    }

    body.woocommerce-checkout #payment,

    body.woocommerce-checkout .woocommerce-checkout-payment,

    body.woocommerce-checkout .payment_box,

    body.woocommerce-checkout .payment_box iframe {
        width: 100% !important;
        max-width: 100% !important;
        min-width: 0 !important;
        box-sizing: border-box !important;
    }

    body.woocommerce-checkout .payment_box {
        padding-left: 12px !important;
        padding-right: 12px !important;
    }
}
/* Force Astra/WooCommerce checkout to one column on mobile */

@media only screen and (max-width: 768px) {
    body.woocommerce-checkout form.checkout.woocommerce-checkout {
        display: block !important;
        grid-template-columns: 1fr !important;
        width: 100% !important;
        max-width: 100% !important;
    }

    body.woocommerce-checkout #customer_details,

    body.woocommerce-checkout #order_review_heading,

    body.woocommerce-checkout #order_review {
        display: block !important;
        float: none !important;
        clear: both !important;
        width: 100% !important;
        max-width: 100% !important;
        min-width: 0 !important;
        margin: 0 !important;
        grid-column: 1 / -1 !important;
    }

    body.woocommerce-checkout #payment,

    body.woocommerce-checkout #payment ul.payment_methods,

    body.woocommerce-checkout #payment li.wc_payment_method,

    body.woocommerce-checkout #payment .payment_box,

    body.woocommerce-checkout

        .payment_method_prism_simple_checkout {
        width: 100% !important;
        max-width: 100% !important;
        min-width: 0 !important;
        box-sizing: border-box !important;
    }

    body.woocommerce-checkout #payment .payment_box {
        margin-left: 0 !important;
        margin-right: 0 !important;
        padding: 15px !important;
    }
}
/* Mobile WooCommerce checkout full-width fix */

@media screen and (max-width: 921px) {
    body.woocommerce-checkout .elementor-container,

    body.woocommerce-checkout .elementor-widget-shortcode,

    body.woocommerce-checkout .elementor-shortcode,

    body.woocommerce-checkout .woocommerce,

    body.woocommerce-checkout form.checkout.woocommerce-checkout {
        width: 100% !important;
        max-width: 100% !important;
        min-width: 0 !important;
        display: block !important;
        box-sizing: border-box !important;
    }

    body.woocommerce-checkout #customer_details,

    body.woocommerce-checkout #order_review_heading,

    body.woocommerce-checkout #order_review {
        float: none !important;
        clear: both !important;
        display: block !important;
        width: 100% !important;
        max-width: 100% !important;
        min-width: 0 !important;
        margin-left: 0 !important;
        margin-right: 0 !important;
        box-sizing: border-box !important;
    }

    body.woocommerce-checkout #payment,

    body.woocommerce-checkout #payment ul.payment_methods,

    body.woocommerce-checkout #payment li.wc_payment_method,

    body.woocommerce-checkout #payment .payment_box,

    body.woocommerce-checkout .payment_method_prism_simple_checkout,

    body.woocommerce-checkout .payment_method_prism_simple_checkout iframe {
        float: none !important;
        display: block !important;
        width: 100% !important;
        max-width: 100% !important;
        min-width: 0 !important;
        box-sizing: border-box !important;
    }

    body.woocommerce-checkout #payment .payment_box {
        margin-left: 0 !important;
        margin-right: 0 !important;
        padding: 15px !important;
    }
}

/* --------------------------------------------------------------------------
   82. FEATURED BAND SPACING AND OVERFLOW

   Two faults in the featured products band, both visible as the cards running
   into the section beneath them.

   The pattern was writing `padding-top:0;padding-bottom:0` onto the band as an
   inline style, which no stylesheet rule can outrank. `.ppp-band` sets its
   vertical rhythm in `--section-y` and was being silently ignored. The pattern
   no longer writes it, but any page already built from the old pattern still
   carries it in its saved content, so it is forced back here too.

   The cards were also overflowing their track. Section 78 gives the summary a
   `1fr auto` grid whose second column is sized by "ADD TO CART" with nowrap;
   at four across inside `.ppp-container` there is less room per card than on
   the shop archive, and a grid item's default `min-width: auto` refuses to
   shrink below its content. The same fault as the phone cards, in a different
   place.
   -------------------------------------------------------------------------- */

.ppp-theme .ppp-band.ppp-products {
	padding-top: var(--section-y) !important;
	padding-bottom: var(--section-y) !important;
}

.ppp-theme .ppp-products .woocommerce ul.products li.product,
.ppp-theme .ppp-products .woocommerce ul.products li.product .astra-shop-summary-wrap > * {
	min-width: 0;
}

.ppp-theme .ppp-products .woocommerce ul.products li.product .astra-shop-summary-wrap .button {
	white-space: normal;
	overflow-wrap: break-word;
}

/* --------------------------------------------------------------------------
   83. PRICE AND BUTTON ON ONE ROW, AT ANY PRICE

   `$450.00 $650.00` is nearly twice the width of `$31.45`, and the price and
   the button share a row. Once the pair no longer fits, the price ran into the
   button rather than giving way.

   Two reasons it could not give way. A grid item defaults to `min-width: auto`
   and will not shrink below its content, so the price track held its full
   width and pushed into the gap. And the gap itself was `--space-2xs`, which
   is fine between a short price and a button but is not a margin of safety.

   With `min-width: 0` the price track can shrink, and because `.price` is a
   wrapping flex row the struck-through original drops to a second line before
   anything collides. Section 82 fixed this for the featured band only; the
   shop archive needed it too.
   -------------------------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap {
	column-gap: var(--space-sm);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap > * {
	min-width: 0;
}

/* The pair is made narrower rather than forced onto two lines. Stacking the
   struck price unconditionally made every sale card taller and read worse than
   the collision it was meant to prevent — most sale prices fit beside the
   button once the original is set at caption size and the button stops
   reserving space it does not need.

   `flex-wrap: wrap` is kept so the original still drops below on a genuinely
   long pair, but only then. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .price {
	flex-wrap: wrap;
	column-gap: var(--space-3xs);
	row-gap: 0;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .price del {
	font-size: var(--text-xs);
	line-height: 1.2;
}

/* The button was sized for "ADD TO CART" plus generous padding. On a card
   sharing its row with a sale price, that padding is the difference between
   fitting and colliding. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .astra-shop-summary-wrap .button,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) ul.products li.product .button {
	padding: var(--space-2xs) var(--space-xs);
	letter-spacing: 0.04em;
}

/* --------------------------------------------------------------------------
   84. FEATURED CARDS STACK THE BUTTON

   The shared price-and-button row works on the shop archive, where the grid
   runs the full container width. It does not work here. The featured band puts
   four cards inside `.ppp-container`, so each is meaningfully narrower, and a
   sale price — two figures, and this catalogue is almost entirely on sale — has
   to sit beside a button in a space that was never going to hold both.

   Everything tried so far treated the symptom: shrink the original, tighten
   the gap, cut the button's padding. Each bought a few pixels and the next
   longer price took them back. Four hundred and fifty dollars beside six
   hundred and fifty beside ADD TO CART does not fit at this width, and no
   amount of narrowing changes that.

   So in this band only, the button takes its own line. The card grows by one
   row and stops fighting. The shop archive keeps the compact treatment,
   because there it genuinely fits.
   -------------------------------------------------------------------------- */

.ppp-theme .ppp-products .woocommerce ul.products li.product .astra-shop-summary-wrap {
	grid-template-columns: minmax(0, 1fr);
}

.ppp-theme .ppp-products .woocommerce ul.products li.product .astra-shop-summary-wrap > * {
	grid-column: 1;
}

.ppp-theme .ppp-products .woocommerce ul.products li.product .price {
	grid-column: 1;
	justify-self: start;
	margin: var(--space-2xs) 0 0;
	font-size: var(--text-base);
}

.ppp-theme .ppp-products .woocommerce ul.products li.product .price del {
	font-size: var(--text-sm);
}

.ppp-theme .ppp-products .woocommerce ul.products li.product .astra-shop-summary-wrap .button,
.ppp-theme .ppp-products .woocommerce ul.products li.product .button {
	grid-column: 1;
	justify-self: stretch;
	width: 100%;
	margin-top: var(--space-2xs);
	text-align: center;
	white-space: nowrap;
}

/* --------------------------------------------------------------------------
   85. COA INDEX ROWS AS SEPARATE CARDS

   The index was one continuous panel with a hairline between rows. Each row is
   a self-contained record — a product, its batch, its certificate — so it reads
   better as its own card than as a line in a table.

   The selector prefix is repeated rather than shortened. `:is()` takes the
   specificity of its most specific argument, which is what let section 56 beat
   an earlier attempt at these rows; matching the prefix keeps this at the same
   weight as the rules it is adjusting rather than one class short of them.
   -------------------------------------------------------------------------- */

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-coa-index {
	display: flex;
	flex-direction: column;
	gap: var(--space-sm);
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-coa-index .ppp-card {
	background-color: rgba(255, 255, 255, 0.05);
	border: 1px solid var(--color-on-dark-border);
	border-radius: var(--radius-md);
	transition: background-color var(--transition-base), border-color var(--transition-base);
}

/* The hairline that used to separate rows is now the gap doing that job. */
:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-coa-index .ppp-card + .ppp-card {
	border-top: 1px solid var(--color-on-dark-border);
}

:is(.ppp-transparency, .ppp-band:has(.ppp-card .ppp-badge)) .ppp-coa-index .ppp-card:hover {
	background-color: rgba(255, 255, 255, 0.09);
	border-color: var(--color-glow-soft, var(--color-on-dark-border));
}

/* --------------------------------------------------------------------------
   86. CROSS-SELL AND UPSELL CARDS

   "You may be interested in…" on the cart, and the upsells on a product page,
   render as `ul.products li.product` — so every rule written for the shop
   archive lands on them, in a column roughly half the width and in a context
   nobody was comparing specifications in.

   The spec pills are the visible casualty: three of them, each sized for a
   full-width card, wrapping into a stack inside a narrow cart column.

   They are dropped here rather than shrunk. A cross-sell is a nudge, not a
   comparison — the purity and endotoxin figures matter on the shop page and on
   the product page, and someone already at the cart has passed both. The card
   keeps its image, name, price and button.

   This also honours the rule the rest of the cart follows: the purchase flow
   gets as little of our layout as possible.
   -------------------------------------------------------------------------- */

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .cross-sells ul.products li.product .ppp-card-pills,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .up-sells ul.products li.product .ppp-card-pills,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .cross-sells ul.products li.product .ppp-card-meta,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .up-sells ul.products li.product .ppp-card-meta {
	display: none;
}

/* One column, so the price and button stack instead of fighting for a row
   that is not there at this width. */
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .cross-sells ul.products li.product .astra-shop-summary-wrap,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .up-sells ul.products li.product .astra-shop-summary-wrap {
	grid-template-columns: minmax(0, 1fr);
	gap: var(--space-3xs);
	padding: var(--space-2xs);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .cross-sells ul.products li.product .astra-shop-summary-wrap > *,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .up-sells ul.products li.product .astra-shop-summary-wrap > * {
	grid-column: 1;
	min-width: 0;
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .cross-sells ul.products li.product .woocommerce-loop-product__title,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .up-sells ul.products li.product .woocommerce-loop-product__title {
	font-size: var(--text-base);
}

:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .cross-sells ul.products li.product .button,
:is(.ppp-theme .woocommerce, .ppp-theme.woocommerce) .up-sells ul.products li.product .button {
	justify-self: stretch;
	width: 100%;
	margin-top: var(--space-2xs);
	text-align: center;
}

/* --------------------------------------------------------------------------
   87. BANDS RUN EDGE TO EDGE WHATEVER ASTRA'S TEMPLATE SAYS

   `.ppp-band` relies on the block editor's `alignfull`, which only stretches
   when Astra's content layout is set to Full Width / Stretched. That is a
   per-page customizer value, so a page built from one of our patterns renders
   correctly or boxed depending on a setting nobody can see from the pattern.
   The contact page arrived boxed with white margins either side of the dark
   band while the home page, with the same markup, did not.

   Rather than chase the setting, the container gives way whenever it actually
   contains one of our bands. `:has()` keeps this off every other page — Astra's
   own templates, plugin pages and the shop are untouched, because none of them
   contain a `.ppp-band`.

   Width is still governed by `.ppp-container` inside each band, so nothing runs
   wider than it should; only the wrapper stops constraining it.
   -------------------------------------------------------------------------- */

.ppp-theme .site-content > .ast-container:has(.ppp-band),
.ppp-theme .ast-container:has(> .site-main .ppp-band),
.ppp-theme #primary:has(.ppp-band),
.ppp-theme .site-main:has(.ppp-band),
.ppp-theme .entry-content:has(.ppp-band) {
	max-width: 100%;
	padding-left: 0;
	padding-right: 0;
	margin-left: 0;
	margin-right: 0;
	width: 100%;
}

/* The article wrapper Astra puts around page content carries its own padding
   and, on this install, a centre alignment class. Both have to go or the band
   sits inside a boxed card. */
.ppp-theme .ast-article-single:has(.ppp-band),
.ppp-theme .ast-article-post:has(.ppp-band) {
	padding: 0;
	border: 0;
	background: none;
	text-align: left;
}

/* Text alignment is set by the components themselves, not inherited from
   whatever Astra's alignment setting happens to be. */
.ppp-theme .ppp-band .ppp-container {
	text-align: left;
}

.ppp-theme .ppp-band .ppp-section-head--center,
.ppp-theme .ppp-band .ppp-research-note {
	text-align: center;
}
