/* ==========================================================================
   LAYER 2 — LAYOUT
   Structural shells only: utility bar, masthead, primary nav, footer,
   section wrappers and grids. No colours of consequence live here except the
   ones that define the dark chrome.
   ========================================================================== */

/* ---- utility bar ------------------------------------------------------- */
.vp-utilbar {
	background: var(--vp-ink);
	color: #C7CDD4;
	font-size: var(--vp-f-sm);
}

.vp-utilbar .vp-wrap {
	display: flex;
	justify-content: space-between;
	align-items: center;
	gap: 16px;
	min-height: 38px;
	flex-wrap: wrap;
}

.vp-utilbar__left,
.vp-utilbar__right { display: flex; gap: 18px; align-items: center; flex-wrap: wrap; }

.vp-utilbar strong { color: #fff; font-weight: 600; }
.vp-utilbar .vp-sep { width: 1px; height: 12px; background: #3A444F; }
.vp-utilbar a:hover { color: #fff; }

/* ---- masthead ---------------------------------------------------------- */
/* The sticky unit is the main bar plus the category bar, not the whole header.
   The utility strip carries the dispatch cut-off and the phone number — facts a
   buyer reads once — and the three bands measure 192px together: pinned to an
   800px laptop that is a quarter of the screen given to chrome for the rest of
   the visit. z-index 30 is deliberately below the age gate (9998) and the
   drawer (9990) — a floating header must never cover a modal. */
.vp-masthead__sticky { position: sticky; top: 0; z-index: 30; }

/* Main bar. Three columns — brand left, search centred, actions right — with
   equal 1fr side columns, so the search box lands on the container's centre
   line instead of wherever the logo's width happens to leave it.

   The middle column is capped at 480px and floored at 200px. Capped, because a
   1320px-wide field is a form, not a search box. Floored rather than fixed,
   because between 1020px and the 1240px where the container widens the brand
   and the actions need that width back; a fixed column would overflow at
   ~800px. On phones the grid becomes two flex rows — see the 640px block.

   The bar is 104px because the wordmark is 60px: the mark plus ~22px of air on
   each side. At the old 46/96 pairing the lockup floated in the band and the
   whole header read as mostly empty; the eye needs the mark to be the tallest
   thing in the row, not the smallest. */
.vp-header {
	background: var(--vp-ink);
	position: relative;
	z-index: 30;
}

.vp-header .vp-wrap {
	display: grid;
	grid-template-columns: 1fr minmax(200px, 480px) 1fr;
	align-items: center;
	gap: 32px;
	min-height: 104px;
}

/* ---- masthead search --------------------------------------------------- */
/* White on the dark bar on purpose. The search field is the highest-frequency
   control on a wholesale catalogue — a buyer's first move is "geek bar pulse x"
   — and white is the strongest anchor this bar can give it. It also rhymes with
   the white panels that drop out of the category row below. */
.vp-hsearch {
	width: 100%;
	display: flex;
	align-items: center;
	gap: 10px;
	height: 52px;
	padding: 0 5px 0 20px;
	background: #fff;
	border-radius: 999px;
	transition: box-shadow .15s;
}

.vp-hsearch:focus-within { box-shadow: 0 0 0 3px rgba(0, 214, 143, .28); }
.vp-hsearch__ico { flex: 0 0 auto; color: var(--vp-muted); }

.vp-hsearch__input {
	flex: 1 1 auto;
	min-width: 0;
	padding: 0;
	border: 0;
	background: none;
	color: var(--vp-text);
	font-size: 14px;
}

/* The ring belongs on the pill (see :focus-within above), not on the input. */
.vp-hsearch__input:focus { outline: none; }
.vp-hsearch__input::placeholder { color: var(--vp-muted-2); }

/* The UA's own clear button sits against the submit button and crowds it. */
.vp-hsearch__input::-webkit-search-cancel-button { -webkit-appearance: none; }

.vp-hsearch__go {
	flex: 0 0 auto;
	width: 42px;
	height: 42px;
	border-radius: 50%;
	background: var(--vp-accent);
	color: var(--vp-accent-ink);
	display: grid;
	place-items: center;
}

.vp-hsearch__go:hover { background: var(--vp-accent-dk); color: #fff; }

/* ---- header tools ----------------------------------------------------- */
.vp-tools {
	justify-self: end;
	display: flex;
	align-items: center;
	gap: 20px;
}

/* Account, two lines: what the surface is, and what clicking it does. The
   second line is the one a buyer reads, so it carries the weight. */
.vp-tools__account { display: flex; align-items: center; gap: 10px; color: #fff; }
.vp-tools__account .vp-icon { flex: 0 0 auto; color: #B9C2CC; }
.vp-tools__account:hover .vp-icon,
.vp-tools__account:hover .vp-tools__meta strong { color: var(--vp-accent); }

.vp-tools__meta small {
	display: block;
	color: #7C8894;
	font-size: 10.5px;
	letter-spacing: .02em;
}

.vp-tools__meta strong {
	display: block;
	color: #fff;
	font-size: 13px;
	font-weight: 600;
	line-height: 1.25;
	white-space: nowrap;
}

.vp-iconbtn {
	position: relative;
	width: 46px;
	height: 46px;
	border-radius: 12px;
	border: 1px solid #2A343F;
	display: grid;
	place-items: center;
	color: #B9C2CC;
}

.vp-iconbtn:hover { border-color: #3B4653; color: #fff; }

/* The contact bubble is a route to a human, not a utility, so it takes the
   accent on hover while the cart keeps the neutral one. */
.vp-iconbtn--chat:hover { border-color: var(--vp-accent); color: var(--vp-accent); }

.vp-iconbtn__badge {
	position: absolute;
	top: -7px;
	right: -7px;
	min-width: 19px;
	padding: 1px 5px;
	border-radius: 20px;
	background: var(--vp-accent);
	color: var(--vp-accent-ink);
	font-size: 10.5px;
	font-weight: 700;
	text-align: center;
}

.vp-burger { display: none; }

.vp-logo {
	justify-self: start;
	display: flex;
	align-items: center;
	gap: 12px;
	color: #fff;
	font-weight: 700;
	font-size: 21px;
	letter-spacing: -.01em;
}

/*
 * WordPress custom logo (Appearance → Customize → Site Identity).
 *
 * The theme declares the slot at 260×60 with flex-sizing on, which means
 * WordPress hands back the uploaded file at its own resolution — the 2560×710
 * master would open the masthead to 710px with nothing to stop it. One fixed
 * height plus `width: auto` holds the lockup's own ratio inside the bar.
 *
 * 60px against the bar's 104px content box is a 1:1.73 mark-to-band ratio. The
 * artwork is a wide 3.15:1 wordmark with no backing plate, so height is the
 * only lever that gives it presence — at 46px it was the smallest thing in a
 * row it should be leading. 60px also puts the lockup at ~189px wide, which
 * still fits the brand column's 200px floor with room for the gap.
 */
.vp-logo .custom-logo-link { display: block; line-height: 0; }

.vp-logo .custom-logo {
	display: block;
	width: auto;
	height: 60px;
	max-width: 100%;
}

.vp-logo__mark {
	width: 40px;
	height: 40px;
	border-radius: 11px;
	background: var(--vp-accent);
	display: grid;
	place-items: center;
	color: var(--vp-accent-ink);
	font-size: 18px;
	font-weight: 800;
}

.vp-logo__sub {
	display: block;
	margin-top: -3px;
	color: #7C8894;
	font-size: 11.5px;
	font-weight: 500;
	letter-spacing: .14em;
	text-transform: uppercase;
}

/* ---- category bar ----------------------------------------------------- */
/* One shade up from the main bar so the two bands read as separate layers
   without a heavy rule between them. The hairline matters in the sticky state,
   where the bar floats over page content and the boundary is all that says
   "these are chrome, that is the page". */
.vp-navbar {
	background: var(--vp-ink-2);
	border-top: 1px solid #1B232D;
	position: relative;
	z-index: 31;
}

.vp-navbar .vp-wrap {
	display: flex;
	align-items: stretch;
	gap: 4px;
	min-height: 58px;
}

/*
 * `white-space: nowrap` is load-bearing. Without it a flex item is free to
 * shrink below its text width, so the labels break mid-phrase — the bar read
 * "New In Stock" over three lines and "Vape / Kits" over two at every viewport
 * width. A nav label must never wrap, so the rule is explicit here and the
 * breakpoint pair at the foot of this file guarantees the bar only shows when
 * it actually fits.
 *
 * That guarantee is a breakpoint, not a hope — this list is allowed to shrink
 * (`flex: 1 1 auto` plus `min-width: 0`) while its items are not, so an
 * overfull row spills instead of reflowing. And because `.vp-nav > li` is
 * positioned (it anchors the panels) the spill paints *above* the static Apply
 * pill and takes its hit test with it. An overfull row is therefore not a
 * cosmetic problem: at eight categories the left 50px of the Apply button was
 * sending wholesale applicants to /accessories/. The capacity is measured at
 * the foot of this file; re-measure it whenever a category is added or removed.
 */
.vp-nav {
	display: flex;
	align-items: stretch;
	gap: 1px;
	flex: 1 1 auto;
	min-width: 0;
}

.vp-nav > li { position: relative; display: flex; }

/* 10px of side padding, not the 16px this started at. At 16px the eight
   categories no longer fit the 1320px header container and the last label rode
   over the Apply pill; the labels themselves are untouched, so the density only
   gives back air no label was using. Measured — see the capacity note at the
   foot of this file. */
.vp-nav__link {
	display: flex;
	align-items: center;
	gap: 7px;
	margin: 7px 0;
	padding: 0 10px;
	border-radius: 10px;
	color: #C4CBD3;
	font-size: 13.5px;
	font-weight: 600;
	white-space: nowrap;
	transition: background .15s, color .15s;
}

/* The first label sits flush with the brand's left edge above it. The two bars
   share one left margin, which is what stops the header reading as two
   unrelated strips. */
.vp-nav > li:first-child .vp-nav__link { padding-left: 0; }

.vp-nav > li > .vp-nav__link:hover,
.vp-nav > li.is-open > .vp-nav__link { background: rgba(0, 214, 143, .13); color: #fff; }

.vp-nav__dot {
	width: 6px;
	height: 6px;
	border-radius: 50%;
	background: var(--vp-accent);
	box-shadow: 0 0 0 3px rgba(0, 214, 143, .18);
	flex: 0 0 auto;
}

.vp-nav__ico { width: 16px; height: 16px; flex: 0 0 auto; }
.vp-nav > li > .vp-nav__link:hover .vp-nav__ico { color: #fff; }

/*
 * Icon colours for the dark bar — the light twins of the six-tint set the
 * category cards and the panel chips use (components.css). The pastel chips are
 * unreadable on #151C24, and painting all seven icons the brand green would
 * turn the row into one accent instead of seven categories.
 */
.vp-nav__ico--ka { color: #00D68F; }
.vp-nav__ico--k1 { color: #82ABEA; }
.vp-nav__ico--k2 { color: #B59CF0; }
.vp-nav__ico--k3 { color: #4ECBA6; }
.vp-nav__ico--k4 { color: #EDAE5D; }
.vp-nav__ico--k5 { color: #EE829C; }
.vp-nav__ico--k6 { color: #93B2CE; }

.vp-caret {
	width: 8px;
	height: 8px;
	border-right: 1.5px solid currentColor;
	border-bottom: 1.5px solid currentColor;
	transform: rotate(45deg) translateY(-2px);
	opacity: .6;
	transition: transform .18s;
}

.vp-nav > li:hover .vp-caret,
.vp-nav > li.is-open .vp-caret { transform: rotate(225deg) translateY(-1px); opacity: 1; }

/* The Apply CTA closes the row.
 *
 * The two-class selector is load-bearing. This rule lives in layout.css, which
 * loads BEFORE components.css, so a single-class selector loses every property
 * it shares with `.vp-btn` — which is precisely what was happening: the padding
 * and radius written here never rendered, and the bar quietly showed a stock
 * button (200x41, 14px type, 9px radius). Only the properties `.vp-btn` does not
 * declare — position, z-index, margin, align-self — ever took effect. Scoping to
 * `.vp-navbar` puts the specificity above `.vp-btn` and makes these numbers real.
 *
 * Sized at 13px / 7x14 instead of the base 14px / 11x18. The row is a strip of
 * 13.5px labels carrying no fills of their own, so a full-size button was the
 * heaviest thing in the bar by a wide margin. 32px tall is still a comfortable
 * target, and it gives the row back ~33px of its width budget.
 *
 * `white-space: nowrap` keeps the label on one line. Without it the button is a
 * shrinkable flex item and wrapped "Apply for wholesale" onto two lines at
 * narrower widths rather than holding its shape.
 *
 * `position: relative` is a guard, not a layout device. The nav items are
 * themselves positioned, so whichever of the two is positioned wins the hit
 * test; lifting the pill above them means that if the row is ever overfull
 * again — one more category, one longer label — the failure mode is a hidden
 * word rather than a button that silently sends applicants to the wrong page.
 * The breakpoint below is the actual fix; this only keeps the next overflow
 * from being an invisible one. */
.vp-navbar .vp-navbar__cta {
	position: relative;
	z-index: 2;
	margin: 9px 0 9px 14px;
	align-self: center;
	gap: 6px;
	padding: 7px 14px;
	border-radius: 9px;
	font-size: 13px;
	white-space: nowrap;
}

/*
 * Skip link to the pill above, rendered inside the bar itself.
 *
 * The base `.vp-skip-link` hides by parking the link at `top:-100px`, which
 * assumes its containing block is the page. Inside `.vp-navbar`
 * (`position: relative`) that same rule parks it 100px *above the bar* — on
 * screen, lying across the search row. So this variant hides the way WordPress
 * core does it instead: shrink the box to a pixel and clip it, then hand the box
 * back on :focus. Nothing about the hide depends on the container's height.
 *
 * It also inverts the colours. The bar is `--vp-ink-2` and the base skip link
 * paints a `--vp-ink` pill — dark on dark, unreadable in exactly the moment it
 * is meant to be read. White on the dark bar is the one combination that stays
 * loud.
 *
 * Two-class selectors on purpose: `.vp-skip-link` is a single class in the
 * earlier-loaded style.css, and this has to win explicitly rather than by sheet
 * order. `padding` is restated on focus because the 0 below would otherwise beat
 * the base rule and collapse the pill to a 1px sliver.
 */
.vp-navbar .vp-skip-link--bar {
	top: 0;
	width: 1px;
	height: 1px;
	padding: 0;
	overflow: hidden;
	clip-path: inset(50%);
	white-space: nowrap;
	background: #fff;
	color: var(--vp-ink);
}

.vp-navbar .vp-skip-link--bar:focus {
	width: auto;
	height: auto;
	padding: 12px 18px;
	overflow: visible;
	clip-path: none;
}

/* ---- category panel --------------------------------------------------- */
/*
 * CSS-only (:hover / :focus-within) so it works with JavaScript disabled;
 * assets/js/nav.js only adds the tap-to-open fallback for touch, where :hover
 * never fires.
 *
 * Anchored to the left edge of its item. Panels for the last few items would
 * run off the right of the container that way, so nav.php marks those
 * `vp-mega--flip` and they hang from their right edge instead — decided in PHP
 * because only PHP knows how many items there are.
 */
.vp-mega {
	position: absolute;
	top: 100%;
	left: 0;
	z-index: 40;
	display: grid;
	grid-template-columns: 230px 1fr;
	min-width: 600px;
	background: #fff;
	border: 1px solid var(--vp-line);
	border-radius: 14px;
	box-shadow: var(--vp-sh-2);
	overflow: hidden;
	opacity: 0;
	visibility: hidden;
	transform: translateY(8px);
	transition: opacity .16s, transform .16s, visibility .16s;
}

.vp-mega--flip { left: auto; right: 0; }

/* No blurb means no intro column: an item without the copy for one gets a plain
   link list rather than an empty grey panel. */
.vp-mega--plain { grid-template-columns: 1fr; min-width: 260px; }

.vp-nav > li:hover .vp-mega,
.vp-nav > li:focus-within .vp-mega,
.vp-nav > li.is-open .vp-mega {
	opacity: 1;
	visibility: visible;
	transform: translateY(0);
}

.vp-mega__intro {
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding: 20px 18px;
	background: var(--vp-bg-2);
	border-right: 1px solid var(--vp-line-2);
}

.vp-mega__intro strong { font-size: 15px; letter-spacing: -.01em; }
.vp-mega__intro p { font-size: 12.5px; color: var(--vp-muted); line-height: 1.5; }

.vp-mega__badge {
	width: 38px;
	height: 38px;
	margin-bottom: 4px;
	border-radius: 11px;
	display: grid;
	place-items: center;
}

.vp-mega__all {
	margin-top: auto;
	padding-top: 12px;
	display: inline-flex;
	align-items: center;
	gap: 6px;
	font-size: 13px;
	font-weight: 700;
	color: var(--vp-accent-dk);
}

.vp-mega__all:hover { color: var(--vp-accent-ink); }

/* The list caps its own height: it grows with the catalogue, and a panel taller
   than the viewport cannot be scrolled by hovering a nav item. */
.vp-mega__list {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: 2px;
	max-height: 62vh;
	overflow-y: auto;
	overscroll-behavior: contain;
	padding: 10px;
	align-content: start;
}

/* A brand list is a wall of marks, not two columns of icon rows. */
.vp-mega--chips .vp-mega__list { grid-template-columns: repeat(3, minmax(140px, 1fr)); }

.vp-mega__list a {
	display: flex;
	align-items: center;
	gap: 12px;
	padding: 10px 12px;
	border-radius: 10px;
}

.vp-mega__list a:hover { background: var(--vp-bg-2); }
.vp-mega__list strong { display: block; font-size: 13.5px; font-weight: 600; line-height: 1.3; }
.vp-mega__list small { display: block; font-size: 11.5px; color: var(--vp-muted); line-height: 1.35; }

.vp-mega__chip {
	flex: 0 0 auto;
	width: 34px;
	height: 34px;
	border-radius: 10px;
	display: grid;
	place-items: center;
	font-size: 14px;
	font-weight: 700;
}

/*
 * "View all brands" sits at the foot of a truncated list, full width. The rule
 * is a hairline rather than a box so it reads as the end of the list, not as
 * another entry — the mistake that would make a reader click it by accident.
 */
.vp-mega__more { grid-column: 1 / -1; margin-top: 4px; border-top: 1px solid var(--vp-line-2); }
.vp-mega__more a { font-size: 13px; font-weight: 700; color: var(--vp-accent-dk); }
.vp-mega__more a:hover { background: none; color: var(--vp-accent-ink); }

/* Chip colours — the pastel twin of the dark-bar icon set above, and the same
   six tints the category cards use, so the two surfaces cannot drift. */
.vp-chip--ka { background: #E2FBF1; color: #00845A; }
.vp-chip--k1 { background: #EAF1FC; color: #2B5AA8; }
.vp-chip--k2 { background: #F1EDFC; color: #5B3FB0; }
.vp-chip--k3 { background: #E5F6F0; color: #0E7A63; }
.vp-chip--k4 { background: #FDF2E2; color: #9A5A14; }
.vp-chip--k5 { background: #FCEDF0; color: #A32A48; }
.vp-chip--k6 { background: #EDF2F7; color: #3A5570; }

/* ---- mobile drawer ---------------------------------------------------- */
.vp-drawer {
	position: fixed;
	inset: 0 0 0 auto;
	z-index: 9990;
	width: min(360px, 88vw);
	display: flex;
	flex-direction: column;
	overflow-y: auto;
	background: var(--vp-ink);
	color: #D0D6DC;
	box-shadow: -12px 0 40px rgba(0,0,0,.35);
}

.vp-drawer[hidden] { display: none; }

.vp-drawer__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	padding: 16px var(--vp-gutter);
	border-bottom: 1px solid #232C36;
}

.vp-drawer__list { padding: 8px var(--vp-gutter) 0; }

.vp-drawer__list > li > a {
	display: block;
	padding: 13px 0;
	border-bottom: 1px solid #1B2229;
	color: #E5E7EB;
	font-size: 15px;
	font-weight: 600;
}

.vp-drawer__sub { padding: 4px 0 10px 14px; }

.vp-drawer__sub a { display: block; padding: 8px 0; font-size: var(--vp-f-base); color: #98A2B3; }
.vp-drawer__sub a:hover { color: #fff; }

.vp-drawer__foot {
	margin-top: auto;
	padding: 20px var(--vp-gutter) 28px;
	display: flex;
	flex-direction: column;
	gap: 8px;
}

.vp-drawer__phone { margin-top: 6px; color: #fff; font-size: 19px; font-weight: 700; }
.vp-drawer__foot span { font-size: var(--vp-f-sm); color: #7C8894; }

/* The contact route the masthead drops on phones — see the 640px block. */
.vp-drawer__chat {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	margin-top: 4px;
	color: #E5E7EB;
	font-size: var(--vp-f-base);
	font-weight: 600;
}

.vp-drawer__chat .vp-icon { color: var(--vp-accent); flex: 0 0 auto; }
.vp-drawer__chat:hover { color: #fff; }

/* ---- section shells --------------------------------------------------- */
.vp-blk { padding: 56px 0; }

/* For blocks that sit between two full sections and only need breathing room:
   the "keep browsing" chip rows and single-line callouts. */
.vp-blk--tight { padding: 26px 0; }

.vp-blk--alt {
	background: var(--vp-bg-2);
	border-top: 1px solid var(--vp-line);
	border-bottom: 1px solid var(--vp-line);
}

.vp-shead {
	display: flex;
	align-items: flex-end;
	justify-content: space-between;
	gap: 20px;
	margin-bottom: 26px;
	flex-wrap: wrap;
}

.vp-shead h2 { font-size: 23px; font-weight: 700; letter-spacing: -.02em; }
.vp-shead p { margin-top: 4px; font-size: var(--vp-f-base); color: var(--vp-muted); }
.vp-shead .vp-more {
	display: flex;
	align-items: center;
	gap: 6px;
	font-size: var(--vp-f-base);
	font-weight: 600;
	color: var(--vp-accent-dk);
	white-space: nowrap;
}

/* Product row: centred heading with an accent rule. */
.vp-row { padding: 44px 0; }

.vp-row--alt {
	background: var(--vp-bg-2);
	border-top: 1px solid var(--vp-line);
	border-bottom: 1px solid var(--vp-line);
}

.vp-row__head { text-align: center; margin-bottom: 26px; }

.vp-row__head h2 {
	font-size: 22px;
	font-weight: 700;
	letter-spacing: .01em;
	text-transform: uppercase;
}

.vp-row__head h2::after {
	content: "";
	display: block;
	width: 44px;
	height: 3px;
	margin: 12px auto 0;
	border-radius: 2px;
	background: var(--vp-accent);
}

.vp-row__head p { margin-top: 10px; font-size: var(--vp-f-base); color: var(--vp-muted); }

/* Footer action under a product grid — currently only on landing pages. */
.vp-row__foot { display: flex; justify-content: center; margin-top: 26px; }

/* ---- grids ------------------------------------------------------------ */
.vp-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; }
.vp-grid--3 { grid-template-columns: repeat(3, 1fr); gap: 16px; }

/* Explicit four-up, mostly for the twelve-tile flavour menu: 4 x 3 on desktop,
   3 x 4 on tablet, 2 x 6 on phones. Named rather than implied so the row count
   is readable from the markup. */
.vp-grid--4 { grid-template-columns: repeat(4, 1fr); gap: 18px; }

/* ---- product rail ------------------------------------------------------
   A row of products that pages sideways: `cols` per page and `rows` deep,
   turning one whole page per click.

   Pages are ordinary grids laid out in a flex track, which is what makes the
   no-JS case work: the viewport defaults to a native horizontal scroller, so
   every product stays reachable by swipe or shift-scroll. The script only
   swaps that scroller for `overflow: hidden` + a transform on the track, so
   the page turns land exactly on page boundaries instead of wherever a
   momentum scroll stopped. */
.vp-rail { position: relative; --vp-rail-cols: 4; }

.vp-rail--cols-3 { --vp-rail-cols: 3; }
.vp-rail--cols-2 { --vp-rail-cols: 2; }

.vp-rail__viewport {
	overflow-x: auto;
	scrollbar-width: none;
	-webkit-overflow-scrolling: touch;
}

.vp-rail__viewport::-webkit-scrollbar { display: none; }

.vp-rail.is-ready .vp-rail__viewport { overflow: hidden; }

.vp-rail__track { display: flex; }

.vp-rail__page {
	flex: 0 0 100%;
	display: grid;
	grid-template-columns: repeat(var(--vp-rail-cols), 1fr);
	gap: 18px;
	margin: 0;
	padding: 0;
	list-style: none;
}

.vp-rail.is-ready .vp-rail__track {
	transition: transform .42s cubic-bezier(.4, 0, .2, 1);
}

@media (prefers-reduced-motion: reduce) {
	.vp-rail.is-ready .vp-rail__track { transition: none; }
}

/* The arrows sit over the viewport's own edges rather than outside it: an
   offset dialled in from the section would push past .vp-wrap on narrow
   viewports and buy a horizontal scrollbar for the whole page. */
.vp-rail__nav { position: absolute; inset: 0; pointer-events: none; }

.vp-rail__nav button {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	pointer-events: auto;
	width: 38px;
	height: 38px;
	padding: 0;
	border: 1px solid var(--vp-line);
	border-radius: 50%;
	background: #fff;
	color: var(--vp-ink);
	display: grid;
	place-items: center;
	box-shadow: 0 2px 12px rgba(16, 24, 32, .14);
}

.vp-rail__nav button:hover { border-color: var(--vp-ink); }

.vp-rail__nav button[disabled] { opacity: .3; box-shadow: none; }

.vp-rail__prev { left: 0; }
.vp-rail__next { right: 0; }

/* ---- footer ----------------------------------------------------------- */
.vp-footer {
	background: var(--vp-ink);
	color: #8D99A6;
	padding: 52px 0 0;
}

.vp-fcols {
	display: grid;
	grid-template-columns: 1.4fr 1fr 1fr 1fr;
	gap: 36px;
	padding-bottom: 40px;
	border-bottom: 1px solid #232C36;
}

.vp-fcols h2,
.vp-fcols h4 {
	margin-bottom: 14px;
	color: #E5E7EB;
	font-size: var(--vp-f-xs);
	font-weight: 700;
	letter-spacing: .11em;
	text-transform: uppercase;
}

.vp-fcols ul { display: flex; flex-direction: column; gap: 9px; }
.vp-fcols a { font-size: var(--vp-f-base); }
.vp-fcols a:hover { color: #fff; }
.vp-fcols .vp-about p { margin-top: 12px; max-width: 280px; font-size: var(--vp-f-base); line-height: 1.65; }

.vp-fcontact { margin-top: 16px; display: flex; flex-direction: column; gap: 6px; }
.vp-fcontact a { color: #E5E7EB; font-weight: 600; font-size: 14px; }
.vp-fcontact__role { color: #9CA3AF; font-weight: 500; font-size: 12px; margin-right: 4px; }

/* The postal address. <address> is italic by default, and its lines are inline
   spans, so both need stating: normal style, one line each. */
.vp-fcontact__addr {
	font-style: normal;
	display: flex;
	flex-direction: column;
	gap: 2px;
	margin-top: 2px;
	color: #9CA3AF;
	font-size: 13px;
	line-height: 1.5;
}

.vp-socials { display: flex; gap: 9px; margin-top: 16px; }

.vp-socials a {
	width: 32px;
	height: 32px;
	border-radius: 8px;
	border: 1px solid #2A343F;
	display: grid;
	place-items: center;
	font-size: 12px;
	color: #B9C2CC;
}

.vp-socials a:hover { border-color: #4A5763; color: #fff; }

.vp-warnbar { background: #0A0E12; border-top: 1px solid #1B2229; padding: 16px 0; }
.vp-warnbar .vp-wrap { display: flex; gap: 14px; align-items: flex-start; }

.vp-warnbar__b {
	flex: 0 0 auto;
	width: 20px;
	height: 20px;
	border-radius: 5px;
	background: #3A1D1F;
	color: #F0999C;
	display: grid;
	place-items: center;
	font-size: 12px;
	font-weight: 800;
}

.vp-warnbar p { font-size: 11.5px; line-height: 1.6; color: #7C8894; }

.vp-legal { border-top: 1px solid #1B2229; font-size: 11.5px; }

.vp-legal .vp-wrap {
	display: flex;
	justify-content: space-between;
	gap: 16px;
	flex-wrap: wrap;
	padding-top: 16px;
	padding-bottom: 16px;
}

.vp-legal a:hover { color: #fff; }

/* ---- responsive ------------------------------------------------------- */

/*
 * The category bar's capacity — the number these breakpoints are built on.
 * Measured on the live bar: the eight categories come to ~1019px (average
 * ~110px, "Nicotine Pouches" the longest), and the Apply button adds 180px plus
 * 18px of row spacing — ~1217px of content box in total. The row therefore fits
 * from a ~1250px viewport and has real air from ~1270px. For comparison the old
 * single-row masthead needed ~1233px, which is the point of splitting the two
 * rows.
 *
 * Two rules, and they are a pair:
 *
 *   1. Above 1240px the header gets a wider container (1320px). Only the header
 *      bands widen; page content stays at 1200px, so the brand sits slightly
 *      outboard of the content below it. That is the deliberate trade — the
 *      1320px is what lets the search box sit at 440px and still centre.
 *   2. At 1269px and below the category row collapses into the drawer instead
 *      of shrinking. It has nowhere to shrink to: the labels are `nowrap` and
 *      the items are positioned, so an overfull row overlaps the Apply button
 *      rather than reflowing — and takes the button's clicks with it. The drawer
 *      is not a downgrade, it is the same eight items from the same source
 *      (template-parts/header/mobile-nav.php), plus the account actions.
 *
 * These numbers are measured, and both a new category and a resized Apply button
 * move them: the seventh item fit a 1020px viewport, the eighth needed 1300px,
 * and taking the button down to 13px / 7x14 brought it back to 1270px. Re-measure
 * rather than nudging them.
 */
@media (min-width: 1240px) {
	.vp-header .vp-wrap,
	.vp-navbar .vp-wrap,
	.vp-utilbar .vp-wrap { max-width: 1320px; }
}

/* The band between "the row no longer fits" and the phone layout. Kept separate
   from the 1020px block below on purpose: only the category row is out of room
   here, and the product grids, rails and header columns have plenty. Folding
   this into that block would drop 1280px laptops to a two-column catalogue.
 *
 * 1269px is measured, not chosen: the row runs out of content box at ~1250px and
 * has 33px of air at 1280px, so 1269 draws the line where the row still has
 * ~20px of room — enough that sub-pixel font differences between machines cannot
 * push it over. */
@media (min-width: 1021px) and (max-width: 1269px) {
	.vp-navbar { display: none; }
	.vp-burger { display: grid; }
}

@media (max-width: 1020px) {
	/* Three equal columns stop working at this width. A `1fr` column's minimum
	   is its min-content, and an <img> can always shrink — so the brand column
	   collapsed to 28px while the search box held its full 480px, squeezing the
	   lockup into a tall distorted sliver. Auto columns size to the lockup and
	   the tool cluster instead, and the search field — the one control that does
	   not care how wide it is — takes whatever is left over. */
	.vp-header .vp-wrap { grid-template-columns: auto minmax(0, 1fr) auto; }

	/* The categories are in the drawer by now. The account's two lines of type
	   are the widest thing left in the bar, so they go first — the icon still
	   says "account" on its own. */
	.vp-navbar { display: none; }
	.vp-burger { display: grid; }
	.vp-tools { gap: 14px; }
	.vp-tools__meta { display: none; }

	.vp-grid,
	.vp-row .vp-grid { grid-template-columns: repeat(2, 1fr); }

	.vp-grid--3 { grid-template-columns: repeat(2, 1fr); }
	.vp-grid--4 { grid-template-columns: repeat(3, 1fr); gap: 14px; }
	.vp-fcols { grid-template-columns: 1fr 1fr; gap: 28px; }

	/* Two columns, and the arrows drop below the rail: at this width an
	   overlay button covers a third of a card, and -- more to the point --
	   the viewport is `overflow: hidden` once the script runs, so an arrow
	   that is merely hidden would leave the later pages unreachable. */
	.vp-rail,
	.vp-rail--cols-3 { --vp-rail-cols: 2; }
	.vp-rail__page { gap: 14px; }
	.vp-rail__nav {
		position: static;
		display: flex;
		justify-content: center;
		gap: 10px;
		margin-top: 18px;
	}
	.vp-rail__nav button {
		position: static;
		transform: none;
	}
}

@media (max-width: 640px) {
	:root { --vp-gutter: 16px; }

	/*
	 * Two columns everywhere, all the way down to 320px.
	 *
	 * The phone layout used to collapse these grids to a single column, and on a
	 * 12-screen homepage that was most of the length: four category tiles became
	 * four full-width slabs, and the footer's link lists became one column each.
	 * A 170px column is narrow but it is enough for a 2xl product name and a
	 * price button, and halving the count halves the scroll. `minmax(0, 1fr)`
	 * rather than `1fr` because a long unbroken product name would otherwise
	 * push the track wider than the viewport.
	 */
	.vp-grid,
	.vp-grid--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
	.vp-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }

	/* The footer's link lists pair up; the brand blurb keeps the full width
	   because it is a paragraph, not a list. */
	.vp-fcols { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px 18px; }
	.vp-fcols .vp-about { grid-column: 1 / -1; }
	.vp-fcols .vp-about p { max-width: none; }
	.vp-utilbar__left { display: none; }
	.vp-blk { padding: 26px 0; }
	.vp-row { padding: 30px 0; }

	/* Section headings carry a 23px title, a two-line intro and an "All →" link.
	   On a phone the intro is the part that eats the height, so it steps down
	   rather than being dropped — it is the sentence that says what the row is. */
	.vp-shead { margin-bottom: 16px; gap: 12px; }
	.vp-shead h2 { font-size: 19px; }
	.vp-shead p { font-size: 13px; line-height: 1.45; }
	.vp-shead .vp-more { font-size: 13px; }

	/* Flavour tiles are a 130px swatch with one word under it. Four across is a
	   desktop arrangement; three fit a 358px phone without shrinking anything,
	   and that takes six rows of twelve down to four. */
	.vp-grid--4:has(> .vp-flavor) { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }

	/* Rails: tighter gutters and a shallower arrow row. */
	.vp-rail__page { gap: 12px; }
	.vp-rail__nav { margin-top: 12px; }

	/* The footer is 13% of the phone page and almost all of it is vertical
	   rhythm: 52px of top padding, 36px between columns, 9px between links. The
	   link lists are already paired up (see .vp-fcols above); this just closes
	   the gaps that were sized for a desktop. */
	.vp-footer { padding: 32px 0 0; }
	.vp-fcols { gap: 20px 16px; padding-bottom: 26px; }
	.vp-fcols h2,
	.vp-fcols h4 { margin-bottom: 10px; }
	.vp-fcols ul { gap: 6px; }
	.vp-fcols a { font-size: 13px; }
	.vp-fcols .vp-about p { margin-top: 9px; font-size: 13px; line-height: 1.55; }
	.vp-fcontact { margin-top: 12px; gap: 5px; }
	.vp-fcontact a { font-size: 13px; }
	.vp-fcontact__addr { font-size: 12px; }
	.vp-socials { margin-top: 12px; gap: 7px; }

	/*
	 * The main bar becomes two rows on a phone. Measured at 390px: brand 193 +
	 * cart + contact + burger + a search field cannot share one row — the bar
	 * overflowed by 52px, which is the whole reason this is a flow layout here
	 * and a grid above.
	 *
	 * Row one is the brand and the two icons that survive on a phone; row two is
	 * the search field, full width, which is where a thumb expects it. The
	 * account block and the contact bubble move into the drawer, which already
	 * carries the login, the application and the phone number, so nothing
	 * becomes unreachable — see template-parts/header/mobile-nav.php.
	 */
	.vp-header .vp-wrap {
		display: flex;
		flex-wrap: wrap;
		gap: 10px 12px;
		min-height: 0;
		padding-top: 12px;
		padding-bottom: 12px;
	}

	.vp-logo { order: 1; font-size: 17px; gap: 10px; }
	.vp-logo__mark { width: 28px; height: 28px; border-radius: 8px; font-size: 14px; }
	.vp-logo__sub { font-size: 10px; letter-spacing: .1em; }

	/* The uploaded mark steps down with the wordmark it replaces — but not all
	   the way down. Row one is already 46px tall because the cart and burger
	   buttons are, so the lockup can run to 42px without adding a single pixel
	   of bar height; at 30px it just floated in the row. At 3.15:1 that is
	   132px wide, and 132 + 10 + 46 + 10 + 46 = 244px still clears the 284px
	   content box of a 320px phone. */
	.vp-logo .custom-logo { height: 42px; }

	.vp-tools { order: 2; margin-left: auto; gap: 10px; }
	.vp-tools__account,
	.vp-iconbtn--chat { display: none; }

	.vp-hsearch { order: 3; flex: 1 0 100%; height: 44px; }

	/*
	 * The utility bar's three facts wrap onto three lines here, which left the
	 * separator pipes stranded at the end of a line with nothing after them.
	 * Dropping the pipes turns it into a plain stacked list, which is what the
	 * width actually allows.
	 */
	.vp-utilbar .vp-sep { display: none; }
	.vp-utilbar__right { gap: 4px 14px; }
	.vp-utilbar { font-size: 11.5px; }
}

/* ---- scroll-to-top ----------------------------------------------
   Fixed to the bottom-right; revealed by main.js once the visitor has
   scrolled past one viewport. The button stays out of the way on every
   page because it sits inside the viewport, not the document flow. */
.vp-top {
	position: fixed;
	right: 16px;
	bottom: 16px;
	width: 42px;
	height: 42px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	background: #fff;
	color: var(--vp-ink, #1a1a1a);
	border: 1px solid var(--vp-line, #dcdcde);
	border-radius: 999px;
	box-shadow: 0 6px 18px rgba(0, 0, 0, .08);
	font-size: 18px;
	line-height: 1;
	text-decoration: none;
	opacity: 0;
	transform: translateY(8px);
	pointer-events: none;
	transition: opacity .2s ease, transform .2s ease;
	z-index: 50;
}
.vp-top.is-visible {
	opacity: 1;
	transform: none;
	pointer-events: auto;
}
.vp-top:hover,
.vp-top:focus-visible {
	color: var(--vp-accent, #1a8a5a);
	border-color: var(--vp-accent, #1a8a5a);
}

@media (max-width: 640px) {
	.vp-top { right: 10px; bottom: 10px; width: 38px; height: 38px; }
}

/* ---- phone-only corrections (2026-09-17) --------------------------------
   Four things the client marked up on a 390px screenshot. All of them are
   phone-only on purpose: the desktop arrangement was signed off and none of
   this changes it. */

@media (max-width: 640px) {
	/* 1. The utility strip restates what the footer and the access band already
	   carry, and at this width it wrapped to two lines above the logo. The
	   dispatch cut-off it leads with is in the trust bar further down anyway. */
	.vp-utilbar { display: none; }

	/* 2. Section head: title on the left, "All →" on the right of the same
	   line, intro underneath.
	   Both sit inside one wrapper div in the markup, so the wrapper is
	   `display: contents` here — that promotes the h2 and the paragraph to grid
	   items without touching the template. The title column is `minmax(0, 1fr)`
	   so a long heading wraps instead of shoving the link off the row. */
	.vp-shead {
		display: grid;
		grid-template-columns: minmax(0, 1fr) auto;
		align-items: center;
		column-gap: 12px;
		row-gap: 6px;
	}
	.vp-shead > div { display: contents; }
	.vp-shead h2 { grid-area: 1 / 1 / 2 / 2; }
	.vp-shead .vp-more { grid-area: 1 / 2 / 2 / 3; }
	.vp-shead p { grid-area: 2 / 1 / 3 / 3; margin-top: 0; }

	/* 3. Rails on a phone: four products (2 columns x 2 rows) per page rather
	   than four rows of two, with the arrows back over the middle of the rail
	   instead of parked underneath it.
	   `--vp-rail-per-page` is what the script re-chunks by (see main.js), so
	   the two halves of this decision cannot drift apart. Declaring it only
	   here is also what leaves the desktop and tablet rails exactly as they
	   shipped — there the row's own columns x rows decides. */
	.vp-rail { --vp-rail-per-page: 4; }

	.vp-rail__nav {
		position: absolute;
		inset: 0;
		display: block;
		justify-content: normal;
		margin-top: 0;
	}
	.vp-rail__nav button {
		position: absolute;
		top: 50%;
		transform: translateY(-50%);
	}

	/* 4. Category tiles: three across, six in all, so the section is two rows
	   instead of four. The tile itself is sized in components.css (it owns
	   .vp-cat) — that is where the 110px-column values live. */
	.vp-grid--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
}
