/* ==========================================================================
   LAYER 3 — COMPONENTS
   Reusable UI that appears on more than one template: buttons, form controls,
   badges, the price lock, product / category / deal cards, the 21+ modal,
   breadcrumbs and the CSS device silhouettes used as image placeholders.
   ========================================================================== */

/* ---- button ----------------------------------------------------------- */
.vp-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 8px;
	padding: 11px 18px;
	border: 1px solid transparent;
	border-radius: 9px;
	font-size: 14px;
	font-weight: 600;
	line-height: 1.2;
	transition: background .15s, border-color .15s, color .15s;
}

.vp-btn--accent { background: var(--vp-accent); color: var(--vp-accent-ink); }
.vp-btn--accent:hover { background: var(--vp-accent-dk); }

/*
 * Ghost is an outline button for a DARK surface — it was written for the hero.
 * Rather than duplicate it for light sections, the two colours are tokens a
 * surrounding block can override, so the same class is legible on either
 * surface: .vp-phero in pages.css sets the light-surface values. Anything
 * painted dark (the hero, the apply band) keeps the defaults.
 */
.vp-btn--ghost { border-color: var(--vp-btn-ghost-line, #39434F); color: var(--vp-btn-ghost-ink, #E5E7EB); }
.vp-btn--ghost:hover { border-color: var(--vp-btn-ghost-line-hover, #5A6673); background: var(--vp-btn-ghost-bg-hover, #1A222B); }

.vp-btn--outline { border-color: var(--vp-line); background: #fff; color: var(--vp-text); }
.vp-btn--outline:hover { border-color: #C9CFD6; background: var(--vp-bg-2); }

.vp-btn--ink { background: var(--vp-ink); color: #fff; }
.vp-btn--ink:hover { background: var(--vp-ink-3); }

.vp-btn--lg { padding: 14px 24px; font-size: 15px; }
.vp-btn--block { width: 100%; }
.vp-btn--sm { padding: 8px 14px; font-size: var(--vp-f-base); }

/* ---- form controls ---------------------------------------------------- */
.vp-field { margin-bottom: 13px; }

.vp-field label,
.vp-label {
	display: block;
	margin-bottom: 5px;
	font-size: var(--vp-f-xs);
	font-weight: 700;
	letter-spacing: .08em;
	text-transform: uppercase;
	color: var(--vp-muted);
}

.vp-input,
.vp-select,
.vp-textarea {
	width: 100%;
	min-height: 40px;
	padding: 9px 12px;
	border: 1px solid var(--vp-line);
	border-radius: 8px;
	background: #fff;
	font-size: var(--vp-f-base);
	color: var(--vp-text);
}

.vp-input:focus,
.vp-select:focus,
.vp-textarea:focus {
	outline: none;
	border-color: var(--vp-ink);
	box-shadow: 0 0 0 3px rgba(12,17,22,.06);
}

.vp-input::placeholder { color: var(--vp-muted-2); }

/* ---- badge / tag ------------------------------------------------------ */
.vp-tag {
	display: inline-block;
	padding: 3px 8px;
	border-radius: 6px;
	font-size: 10px;
	font-weight: 700;
	letter-spacing: .08em;
	text-transform: uppercase;
	background: var(--vp-ink);
	color: #fff;
}

.vp-tag--low { background: var(--vp-warn-bg); color: var(--vp-warn-ink); border: 1px solid var(--vp-warn-line); }
.vp-tag--out { background: #F1F2F4; color: #6B7280; }

/* ---- price lock ------------------------------------------------------- */
/* The single most important B2B component: every price on the site is masked
   until an approved account signs in. Used on cards, PDP and the deals strip. */
.vp-lock {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 7px;
	padding: 8px 10px;
	border: 1px solid var(--vp-warn-line);
	border-radius: 7px;
	background: var(--vp-warn-bg);
	color: var(--vp-warn-ink);
	font-size: var(--vp-f-sm);
	font-weight: 600;
}

.vp-lock svg { flex: 0 0 auto; }
.vp-lock--inline { justify-content: flex-start; align-self: flex-start; }

/* ---- stock indicator -------------------------------------------------- */
.vp-stock {
	display: flex;
	align-items: center;
	gap: 5px;
	font-size: 11.5px;
	font-weight: 600;
	color: var(--vp-ok);
}

.vp-stock::before {
	content: "";
	width: 5px;
	height: 5px;
	border-radius: 50%;
	background: var(--vp-accent);
	flex: 0 0 auto;
}

.vp-stock--low { color: #B45309; }
.vp-stock--low::before { background: #F59E0B; }
.vp-stock--out { color: var(--vp-muted-2); }
.vp-stock--out::before { background: var(--vp-muted-2); }

/* ---- product card ----------------------------------------------------- */
.vp-card {
	display: flex;
	flex-direction: column;
	overflow: hidden;
	border: 1px solid var(--vp-line);
	border-radius: var(--vp-r);
	background: #fff;
	transition: border-color .15s, box-shadow .15s;
}

.vp-card:hover { border-color: #C9CFD6; box-shadow: var(--vp-sh-1); }

.vp-card__shot {
	position: relative;
	/*
	 * The media box matches the artwork's own ratio instead of being a flat
	 * strip.
	 *
	 * The product photos are square — 400×400, with the subject filling about
	 * 340×349 of it — while this frame was 273×152 (1.79:1). `object-fit:
	 * contain` scales the whole canvas, so a square picture was handed 151px of
	 * width inside a 273px box and sat in the middle of 122px of empty grey.
	 * Squaring the frame lets the photo use the width it already had.
	 *
	 * `aspect-ratio` rather than a fixed height, so the box stays square at
	 * every breakpoint: the card narrows on a phone and the media box has to
	 * come down with it.
	 */
	aspect-ratio: 1 / 1;
	display: grid;
	/*
	 * The row track has to be stated, not left to `auto`.
	 *
	 * With an implicit auto track the track's height is derived from its
	 * content, which makes it indefinite — and a percentage height against an
	 * indefinite track is not resolved, it falls back to `auto`. So the image's
	 * `height: 100%` below did nothing and a square 400x400 shot laid itself
	 * out at 273px tall inside a 152px box, where `.vp-card`'s `overflow: hidden`
	 * sliced both sides off it. It never showed until the homepage rows could
	 * feature real WooCommerce products: every catalogue SKU ships with an empty
	 * `image`, so every card had rendered the CSS silhouette instead.
	 *
	 * `minmax(0, 1fr)` pins the track to the box, which is what gives the
	 * percentage below something definite to resolve against.
	 */
	grid-template-rows: minmax(0, 1fr);
	place-items: center;
	border-bottom: 1px solid var(--vp-line-2);
	background: var(--vp-bg-2);
}

/* Real imagery replaces this once product shots exist; the media box keeps a
   fixed ratio so nothing shifts when it loads. */
.vp-card__shot img {
	width: 100%;
	height: 100%;
	object-fit: contain;
	padding: 12px;
}

.vp-card__tag { position: absolute; top: 10px; left: 10px; }

/* Placeholder silhouette shown until a real product webp exists, so the grid
   never looks broken and the card proportions are already correct. */
.vp-card-dev {
	width: 30px;
	border-radius: 15px;
	background: #fff;
	box-shadow: 0 6px 14px rgba(16,24,40,.12);
}

.vp-card-dev::after {
	content: "";
	display: block;
	width: 7px;
	height: 18px;
	margin: 8px auto 0;
	border-radius: 3px;
	background: #2563EB;
}

.vp-grid .vp-card:nth-child(2n) .vp-card-dev::after { background: #8B5CF6; }
.vp-grid .vp-card:nth-child(3n) .vp-card-dev::after { background: #00B378; }
.vp-grid .vp-card:nth-child(4n) .vp-card-dev::after { background: var(--vp-danger); }

.vp-card__body {
	display: flex;
	flex-direction: column;
	gap: 5px;
	flex: 1;
	padding: 13px;
}

.vp-card__brand {
	font-size: 10.5px;
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--vp-muted-2);
}

.vp-card__name { font-size: var(--vp-f-base); font-weight: 600; line-height: 1.35; }
.vp-card__spec { font-size: var(--vp-f-sm); color: var(--vp-muted); }
.vp-card__body .vp-lock { margin-top: auto; }

/*
 * Three lines the card no longer carries: brand, spec and stock.
 *
 * The product name already leads with the brand ("Geek Bar Pulse X DTL 25K
 * Disposable Vape"), so the standalone brand line was the same word twice on a
 * 275px card. The spec and the warehouse count are what the product page is
 * for — in a grid the buyer is comparing models, not counting boxes.
 *
 * Hidden rather than removed from the template: the markup still carries the
 * values for the product page, for structured data and for anyone reading the
 * source, and bringing any of the three back is deleting one selector from
 * this list.
 *
 * `.vp-stock` is scoped to the card on purpose — the single-product page uses
 * the same class for its own stock line and that one still has to show.
 */
.vp-card__brand,
.vp-card__spec,
.vp-card .vp-stock { display: none; }

/* ---- category tile ---------------------------------------------------- */
.vp-cat {
	display: flex;
	flex-direction: column;
	overflow: hidden;
	border: 1px solid var(--vp-line);
	border-radius: var(--vp-r);
	background: #fff;
	transition: border-color .18s, box-shadow .18s, transform .18s;
}

.vp-cat:hover {
	border-color: var(--vp-ink);
	box-shadow: var(--vp-sh-2);
	transform: translateY(-2px);
}

.vp-cat__vis {
	position: relative;
	height: 118px;
	display: grid;
	place-items: center;
	overflow: hidden;
	border-bottom: 1px solid var(--vp-line-2);
}

.vp-cat__vis img {
	position: absolute;
	inset: 0;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

.vp-cat__ring {
	position: absolute;
	width: 154px;
	height: 154px;
	border-radius: 50%;
	border: 1px solid currentColor;
	opacity: .18;
}

.vp-cat__ring--inner { width: 96px; height: 96px; opacity: .12; }

.vp-cat__vis svg { position: relative; transition: transform .22s; }
.vp-cat:hover .vp-cat__vis svg { transform: translateY(-3px) scale(1.07); }

.vp-cat__cnt {
	position: absolute;
	top: 10px;
	left: 10px;
	z-index: 1;
	padding: 2px 7px;
	border: 1px solid var(--vp-line);
	border-radius: 5px;
	background: #fff;
	font-size: 10px;
	font-weight: 700;
	letter-spacing: .07em;
	text-transform: uppercase;
	color: #414A55;
}

.vp-cat__row {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 12px;
	padding: 13px 15px;
}

.vp-cat__row h3 { font-size: 14.5px; font-weight: 600; line-height: 1.32; }
.vp-cat__row small { display: block; margin-top: 3px; font-size: var(--vp-f-sm); color: var(--vp-muted); }

.vp-cat__arrow {
	width: 30px;
	height: 30px;
	flex: 0 0 auto;
	border: 1px solid var(--vp-line);
	border-radius: 8px;
	background: #fff;
	display: grid;
	place-items: center;
	font-size: 14px;
	color: var(--vp-muted);
	transition: background .15s, border-color .15s, color .15s;
}

.vp-cat:hover .vp-cat__arrow { background: var(--vp-ink); border-color: var(--vp-ink); color: #fff; }

/* Per-category tint set. Swap the tint classes for real webp art later and the
   layout stays identical.
   The flavour menu shares the six tints so the two grids cannot drift into
   different palettes; only the surrounding chrome differs. */
.vp-cat--k1 .vp-cat__vis,
.vp-flavor--k1 .vp-flavor__vis { background: linear-gradient(140deg,#EAF1FC,#D5E4F8); color: #2B5AA8; }
.vp-cat--k2 .vp-cat__vis,
.vp-flavor--k2 .vp-flavor__vis { background: linear-gradient(140deg,#F1EDFC,#E0D6F8); color: #5B3FB0; }
.vp-cat--k3 .vp-cat__vis,
.vp-flavor--k3 .vp-flavor__vis { background: linear-gradient(140deg,#E5F6F0,#CDEBE0); color: #0E7A63; }
.vp-cat--k4 .vp-cat__vis,
.vp-flavor--k4 .vp-flavor__vis { background: linear-gradient(140deg,#FDF2E2,#F8E2C4); color: #9A5A14; }
.vp-cat--k5 .vp-cat__vis,
.vp-flavor--k5 .vp-flavor__vis { background: linear-gradient(140deg,#FCEDF0,#F7D6DF); color: #A32A48; }
.vp-cat--k6 .vp-cat__vis,
.vp-flavor--k6 .vp-flavor__vis { background: linear-gradient(140deg,#EDF2F7,#DAE5EF); color: #3A5570; }

/* ---- flavour tile ---------------------------------------------------- */
/* Deliberately plainer than .vp-cat: no ring, no count badge, no arrow, and
   the label sits *under* the tile rather than inside a white row. Two grids of
   identical cards back to back would read as one long list. */
.vp-flavor { display: block; text-align: center; }

.vp-flavor__vis {
	position: relative;
	display: grid;
	place-items: center;
	overflow: hidden;
	aspect-ratio: 4 / 3;
	border: 1px solid var(--vp-line);
	border-radius: var(--vp-r);
	transition: border-color .18s, box-shadow .18s, transform .18s;
}

.vp-flavor__vis img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.vp-flavor__vis svg { position: relative; transition: transform .22s; }

.vp-flavor:hover .vp-flavor__vis {
	border-color: var(--vp-ink);
	box-shadow: var(--vp-sh-2);
	transform: translateY(-2px);
}

.vp-flavor:hover .vp-flavor__vis svg { transform: translateY(-3px) scale(1.08); }

.vp-flavor__name {
	display: block;
	margin-top: 10px;
	font-size: 13.5px;
	font-weight: 700;
	letter-spacing: -.005em;
	color: var(--vp-text);
}

.vp-flavor:hover .vp-flavor__name { color: var(--vp-ink); }

/* ---- deal card ------------------------------------------------------- */
.vp-deal {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: 6px;
	padding: 16px;
	border: 1px solid var(--vp-line);
	border-radius: var(--vp-r);
	background: #fff;
}

.vp-deal:hover { border-color: #C9CFD6; box-shadow: var(--vp-sh-1); }

.vp-deal__off {
	position: absolute;
	top: 14px;
	right: 14px;
	padding: 3px 8px;
	border: 1px solid #FECACA;
	border-radius: 6px;
	background: #FEF2F2;
	color: #B91C1C;
	font-size: var(--vp-f-xs);
	font-weight: 700;
}

.vp-deal__dept {
	font-size: var(--vp-f-xs);
	font-weight: 700;
	letter-spacing: .1em;
	text-transform: uppercase;
	color: var(--vp-muted-2);
}

.vp-deal h3 { padding-right: 54px; font-size: 14px; font-weight: 600; line-height: 1.35; }
.vp-deal__meta { font-size: var(--vp-f-sm); color: var(--vp-muted); }
.vp-deal .vp-lock { margin-top: 8px; }

/* ---- device silhouettes ----------------------------------------------- */
/* Zero-byte placeholder art. Replaced by webp product shots in phase 2. */
.vp-devices { position: relative; display: flex; gap: 18px; align-items: flex-end; }

.vp-dev {
	position: relative;
	width: 42px;
	border-radius: 21px;
	background: var(--vp-bg-2);
	box-shadow: 0 10px 22px rgba(0,0,0,.5);
}

.vp-dev::after {
	content: "";
	position: absolute;
	left: 50%;
	top: 10px;
	width: 11px;
	height: 26px;
	border-radius: 4px;
	opacity: .35;
	transform: translateX(-50%);
}

.vp-dev--1 { height: 140px; }
.vp-dev--1::after { background: #2563EB; }
.vp-dev--2 { height: 168px; background: var(--vp-ink-2); border: 1px solid #2A343F; }
.vp-dev--2::after { background: var(--vp-accent); }
.vp-dev--3 { height: 120px; }
.vp-dev--3::after { background: var(--vp-danger); }
.vp-dev--4 { height: 150px; background: var(--vp-ink-2); border: 1px solid #2A343F; }
.vp-dev--4::after { background: #A855F7; }

/* ---- 21+ gate modal --------------------------------------------------- */
/* Overlay, never an inline bar: an inline gate pushes content down and hurts
   CLS. Body scroll is locked while it is open (see assets/js/gate.js). */
.vp-gate {
	position: fixed;
	inset: 0;
	z-index: 9998;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 18px;
	background: rgba(0,0,0,.78);
}

.vp-gate[hidden] { display: none; }

.vp-gate__card {
	width: 540px;
	max-width: 100%;
	max-height: calc(100vh - 36px);
	overflow-y: auto;
	padding: 22px 56px 24px;
	border: 1px solid var(--vp-line);
	border-radius: 6px;
	background: #fff;
	text-align: center;
	box-shadow: 0 1px 12px rgba(0,0,0,.45);
}

.vp-gate__logo { display: flex; align-items: center; justify-content: center; gap: 9px; min-height: 56px; margin-bottom: 12px; }

/*
 * The card shows the site's own logo — the same upload the masthead shows — and
 * it needs the same height lock for the same reason: the theme tells WordPress
 * 260×60 and WordPress hands back whichever file was uploaded, so a 1200×352
 * PNG with no rule here opens the card to 350px of logo. 60px is the masthead's
 * height and 42px is its phone height (see layout.css), so the mark a visitor is
 * stopped by is the size of the mark they land on.
 */
.vp-gate__logo .custom-logo {
	display: block;
	width: auto;
	height: 60px;
	max-width: 100%;
}

.vp-gate__logo .vp-logo__mark { background: var(--vp-ink); color: var(--vp-accent); }

.vp-gate__wm { font-size: 19px; font-weight: 700; line-height: 1.05; text-align: left; letter-spacing: -.01em; }
.vp-gate__wm small {
	display: block;
	margin-top: 2px;
	font-size: 9.5px;
	font-weight: 600;
	letter-spacing: .2em;
	text-transform: uppercase;
	color: var(--vp-muted-2);
}

.vp-gate__art {
	position: relative;
	height: 150px;
	margin-bottom: 16px;
	border-radius: 8px;
	overflow: hidden;
	background: linear-gradient(120deg,#0C1116,#1C2733 70%);
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 14px;
}

.vp-gate__art::before {
	content: "";
	position: absolute;
	width: 240px;
	height: 240px;
	border-radius: 50%;
	background: rgba(0,214,143,.18);
	filter: blur(46px);
}

/* The panel holds either the drawn bars or the editor's picture (Vapeline →
   Age gate). One modifier carries the difference, so the drawn look is still
   exactly what it was and a site that never opens that screen sees no change. */

/* The green glow is background dressing for the bars; over artwork it reads as
   a smear across the middle of someone's picture. */
.vp-gate__art.has-image::before { display: none; }

.vp-gate__art-img {
	position: absolute;
	inset: 0;
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

/* "Fit the whole image" — for a logo or a picture with type near the edges.
   The panel's own dark gradient shows either side, which is why it stays a
   gradient rather than going white. */
.vp-gate__art--contain .vp-gate__art-img { object-fit: contain; }

/* The panel as one target (Vapeline → Age gate → "Panel link"). The anchor
   covers the panel instead of wrapping it, so the picture, the drawn bars and
   the caption keep the layout they already had — and the caption, which sits
   inside the anchor, is what gives the link its name. The lift is the only clue
   that a picture is now a button; without it the panel is a poster either way. */
.vp-gate__art-link { position: absolute; inset: 0; z-index: 2; display: block; }

.vp-gate__art.has-link .vp-gate__art-img { transition: transform .35s ease; }
.vp-gate__art.has-link:hover .vp-gate__art-img { transform: scale(1.03); }
.vp-gate__art.has-link:hover b { text-decoration: underline; }

.vp-gate__art i { position: relative; display: block; width: 14px; border-radius: 8px; background: rgba(255,255,255,.9); }
.vp-gate__art i:nth-child(1) { height: 74px; opacity: .55; }
.vp-gate__art i:nth-child(2) { height: 92px; }
.vp-gate__art i:nth-child(3) { height: 62px; opacity: .55; }
.vp-gate__art i:nth-child(4) { height: 84px; }

.vp-gate__art b {
	position: absolute;
	left: 14px;
	bottom: 11px;
	font-size: 10px;
	font-weight: 700;
	letter-spacing: .14em;
	text-transform: uppercase;
	color: var(--vp-accent);
}

/* Over a picture the caption needs its own ground: green on an arbitrary photo
   is unreadable about half the time, and "sometimes legible" is not a design. */
.vp-gate__art.has-image b {
	padding: 3px 8px;
	border-radius: 4px;
	background: rgba(6,12,18,.62);
	-webkit-backdrop-filter: blur(2px);
	backdrop-filter: blur(2px);
}

.vp-gate__q { margin: 16px 0 10px; color: #0871B2; font-size: 16px; font-weight: 600; line-height: 1.35; }
.vp-gate__note { margin-bottom: 16px; font-size: var(--vp-f-sm); line-height: 1.5; color: var(--vp-muted); }

.vp-gate__btns { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }

.vp-gate__btns button {
	height: 47px;
	border-radius: 5px;
	color: #fff;
	font-size: 16px;
	font-weight: 700;
}

.vp-gate__yes { background: #4ABD00; }
.vp-gate__yes:hover { background: #429F00; }
.vp-gate__no { background: #0871B2; }
.vp-gate__no:hover { background: #066194; }

.vp-gate__cookie { margin-top: 14px; font-size: var(--vp-f-xs); line-height: 1.5; color: var(--vp-muted); }
.vp-gate__cookie a { text-decoration: underline; }

.vp-gate__social { display: flex; justify-content: center; gap: 7px; margin-top: 18px; }

.vp-gate__social a {
	width: 28px;
	height: 28px;
	border-radius: 50%;
	background: #333;
	color: #fff;
	display: grid;
	place-items: center;
	font-size: 12px;
	font-weight: 700;
}

/* The editor's own links, under the social row (Vapeline → Age gate). Pills
   rather than icons: these have names — "Wholesale application", "Catalogue" —
   and a name needs room the 28px circles cannot give it. */
.vp-gate__links { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; margin-top: 12px; }

.vp-gate__links a {
	padding: 5px 12px;
	border: 1px solid var(--vp-line);
	border-radius: 999px;
	color: var(--vp-text);
	font-size: var(--vp-f-xs);
	font-weight: 600;
	line-height: 1.6;
}

.vp-gate__links a:hover { border-color: var(--vp-accent-dk); color: var(--vp-accent-dk); }

.vp-gate__decline { display: none; padding: 6px 0 2px; }
.vp-gate.is-declined .vp-gate__body { display: none; }
.vp-gate.is-declined .vp-gate__decline { display: block; }
.vp-gate__decline h3 { margin-bottom: 8px; font-size: 17px; font-weight: 700; }
.vp-gate__decline p { margin-bottom: 18px; font-size: var(--vp-f-base); color: var(--vp-muted); }

/* Lock the document behind the modal. Set on <html> rather than <body> so the
   lock survives themes/plugins that wrap or replace the body element. */
html.vp-gate-open,
html.vp-nav-open { overflow: hidden; }

/* ---- breadcrumbs ----------------------------------------------------- */
/* The nav is the outer element and .vp-wrap supplies the gutter, so the same
   markup works inside a full-width band or a narrow page body. */
.vp-crumbs { font-size: 12.5px; color: var(--vp-muted); }
.vp-crumbs .vp-wrap { padding-top: 18px; }
.vp-crumbs ol { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.vp-crumbs a:hover { color: var(--vp-text); text-decoration: underline; }
.vp-crumbs [aria-current="page"] { color: var(--vp-text); font-weight: 500; }
.vp-crumbs li + li::before { content: "/"; margin-right: 6px; color: var(--vp-muted-2); }

/* ---- chips / spec list ---------------------------------------------- */
.vp-chips { display: flex; gap: 7px; flex-wrap: wrap; }

.vp-chip {
	padding: 3px 11px;
	border: 1px solid var(--vp-line);
	border-radius: 20px;
	background: #fff;
	font-size: var(--vp-f-sm);
	color: #414A55;
}

.vp-chip:hover, .vp-chip.is-active { border-color: var(--vp-ink); }

.vp-speclist { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 18px; margin: 18px 0; }

.vp-speclist li {
	display: flex;
	gap: 9px;
	align-items: flex-start;
	font-size: var(--vp-f-base);
	color: #3C4553;
}

.vp-speclist li::before {
	content: "";
	width: 5px;
	height: 5px;
	margin-top: 7px;
	border-radius: 50%;
	background: var(--vp-accent);
	flex: 0 0 auto;
}

/* ---- responsive ----------------------------------------------------- */
@media (max-width: 1020px) {
	.vp-speclist { grid-template-columns: 1fr; }
	.vp-gate__card { padding: 18px 24px 20px; }
	.vp-gate__btns button { font-size: 14px; }
}

@media (max-width: 640px) {
	/*
	 * Product cards on a phone: the media box drops from 1:1 to 4:3 and the body
	 * tightens up.
	 *
	 * A homepage rail of eight products is four rows on a two-column phone, so
	 * every 40px saved on a card is 160px off the page. Nothing is cropped to
	 * get there — the shot is `object-fit: contain`, so a 4:3 frame simply
	 * letterboxes the square artwork and the card stops carrying dead height.
	 */
	.vp-card__shot { aspect-ratio: 4 / 3; }
	.vp-card__shot img { padding: 8px; }
	.vp-card__body { padding: 10px; gap: 4px; }
	.vp-card__name { font-size: 13px; line-height: 1.3; }

	/* "Log in to see price" is 19 characters and the card is 170px wide, so at
	   the desktop size it wrapped to two lines and every card in the grid grew
	   a line taller. It fits on one at 11.5px — and nowrap is the guard, since
	   a wrapped price lock reads as a broken button. */
	.vp-lock { padding: 7px 8px; font-size: 11.5px; gap: 5px; white-space: nowrap; }
	.vp-lock svg { width: 13px; height: 13px; }

	/*
	 * Category tiles: three across, so the column is ~113px rather than 170px
	 * and every size below steps down with it. The 118px visual band does not
	 * need 72px to hold a centred glyph, and the caption has to survive a
	 * three-word name in a 95px text box without pushing the row taller than
	 * the artwork it sits under.
	 */
	.vp-cat__vis { height: 72px; }
	.vp-cat__ring { width: 88px; height: 88px; }
	.vp-cat__ring--inner { width: 56px; height: 56px; }
	.vp-cat__vis svg { width: 24px; height: 24px; }
	.vp-cat__cnt { top: 5px; left: 5px; padding: 1px 4px; font-size: 8.5px; }
	.vp-cat__row { padding: 8px 9px; gap: 6px; }
	.vp-cat__row h3 { font-size: 12px; line-height: 1.25; }
	.vp-cat__row small { margin-top: 2px; font-size: 10.5px; line-height: 1.3; }
	.vp-cat__arrow { flex: 0 0 auto; width: 22px; height: 22px; }
	.vp-cat__arrow svg { width: 12px; height: 12px; }

	.vp-gate__card { padding: 16px 16px 18px; }
	.vp-gate__logo .custom-logo { height: 42px; }
	.vp-gate__art { height: 120px; }
	.vp-gate__q { font-size: 14.5px; }
	.vp-gate__btns { grid-template-columns: 1fr; gap: 9px; }
}
