/* ==========================================================================
   Vape Reserva — design system
   --------------------------------------------------------------------------
   Premium cyberpunk retail, not a neon template. The rules that keep it on
   the right side of that line:

     - Near-black is the ground. Neon is an ACCENT, never a fill. If a surface
       is glowing, something on it should be the reason.
     - Every animation is transform/opacity only, so it runs on the compositor
       and never triggers layout. Nothing animates width, height, top or left.
     - Atmosphere lives in a single fixed layer behind the page (.bg). It does
       not repaint when you scroll, because it does not move with the page.
     - Type and price are always plain, high-contrast and unglitched. The
       customer must never have to squint at a number.

   Type: system stacks. There are no webfonts — no external requests, no
   layout shift, no font-src exception in the CSP. If you want a licensed
   display face later, self-host it and swap --font-display; nothing else
   needs to change.
   ========================================================================== */

/* ==========================================================================
   1. Tokens
   ========================================================================== */

:root {
  /* Ground */
  --black:      #050608;   /* page ground */
  --char:       #0b0f14;   /* raised surface */
  --char-2:     #10161d;   /* surface, one step up */
  --char-3:     #171f28;   /* hover surface */

  /* Neon — accents only */
  --cyan:       #00e5ff;
  --cyan-dim:   #0891a3;
  --purple:     #8b5cff;
  --purple-dim: #5b3caa;
  --magenta:    #ff2bd6;

  /* Text */
  --text:       #e8edf3;
  --text-2:     #9aa8b8;
  --text-3:     #7c8b9d;
  --white:      #ffffff;

  /* Status — never colour alone; every use is paired with a word or icon */
  --ok:         #34e5a0;
  --warn:       #ffb545;
  --danger:     #ff5a6e;

  /* Lines */
  --line:       rgba(154, 168, 184, 0.14);
  --line-2:     rgba(154, 168, 184, 0.26);
  --line-cyan:  rgba(0, 229, 255, 0.38);
  --line-purp:  rgba(139, 92, 255, 0.38);

  /* Glow — kept as tokens so intensity is tunable in one place */
  --glow-cyan:  0 0 0 1px rgba(0,229,255,.30), 0 0 24px -6px rgba(0,229,255,.55);
  --glow-purp:  0 0 0 1px rgba(139,92,255,.30), 0 0 24px -6px rgba(139,92,255,.55);

  /* Shape — a rounded scale, nested so the sizes read as one system:
     small chips sit inside controls, controls sit inside cards. Change these
     three values and every button, input, card, panel, drawer and notice on
     the site follows; nothing hardcodes a radius except deliberate circles
     (status dots), pills (counters, status chips) and the product placeholder,
     which is drawing a physical object rather than a UI surface. */
  --r-sm: 8px;    /* badges, tags, small chips */
  --r:    12px;   /* buttons, inputs, steppers */
  --r-lg: 16px;   /* cards, panels, dialogs */

  /* Type */
  --font-display: "Inter Tight", "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-body:    "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono:    ui-monospace, "JetBrains Mono", "SF Mono", Menlo, Consolas, monospace;

  /* Motion — one curve, three durations. Consistency reads as expensive. */
  --ease:  cubic-bezier(0.22, 1, 0.36, 1);
  --t-fast: 140ms;
  --t:      260ms;
  --t-slow: 520ms;

  /* Layout */
  --wrap: 1240px;
  --gut:  clamp(1rem, 4vw, 2.5rem);
  --nav-h: 68px;
}

/* ==========================================================================
   2. Reset and base
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

/* The browser's own `[hidden] { display: none }` is a UA rule, so ANY author
   rule that sets display beats it. `.notice { display: flex }` did exactly
   that, leaving an empty red error box permanently visible on the admin login
   form. This makes the attribute mean what it says, everywhere — without it,
   every component that sets `display` has to remember to handle `[hidden]`
   itself, and one of them always forgets. */
[hidden] { display: none !important; }

html {
  scroll-behavior: smooth;
  scrollbar-gutter: stable;
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--black);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

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

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0 0 0.5em;
  color: var(--white);
}

h1 { font-size: clamp(2.4rem, 7vw, 5rem); }
h2 { font-size: clamp(1.7rem, 4vw, 2.8rem); }
h3 { font-size: clamp(1.15rem, 2vw, 1.4rem); }

p { margin: 0 0 1em; }
p:last-child { margin-bottom: 0; }

a { color: var(--cyan); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Focus. Visible, high contrast, never removed — the whole site is
   keyboard-operable and this is how you can tell where you are. */
:focus-visible {
  outline: 2px solid var(--cyan);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}
:focus:not(:focus-visible) { outline: none; }

::selection { background: rgba(0,229,255,.28); color: var(--white); }

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gut);
}

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

/* Skip link — first tab stop on every page. */
.skip {
  position: absolute; left: var(--gut); top: -100px;
  z-index: 200;
  background: var(--cyan); color: var(--black);
  padding: .7rem 1.1rem; font-weight: 700;
  font-family: var(--font-mono); font-size: .8rem;
  letter-spacing: .08em; text-transform: uppercase;
  border-radius: var(--r);
  transition: top var(--t-fast) var(--ease);
}
.skip:focus { top: 12px; text-decoration: none; }

/* ==========================================================================
   3. The living background
   --------------------------------------------------------------------------
   One fixed layer, six children, all pointer-events:none. Fixed means it does
   not participate in scrolling, so scrolling never repaints it. Everything
   here animates transform or opacity only.
   ========================================================================== */

.bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  background: var(--black);
}

.bg > * {
  position: absolute;
  inset: -20%;
  will-change: transform, opacity;
}

/* Slow-moving radial light sources. Two blobs on different periods so the
   pattern never visibly repeats. */
.bg__aura {
  background:
    radial-gradient(42vw 42vw at 22% 18%, rgba(0,229,255,.13), transparent 62%),
    radial-gradient(46vw 46vw at 82% 72%, rgba(139,92,255,.14), transparent 64%),
    radial-gradient(38vw 38vw at 58% 42%, rgba(255,43,214,.05), transparent 60%);
  animation: aura-drift 46s var(--ease) infinite alternate;
}

@keyframes aura-drift {
  0%   { transform: translate3d(-2%, -1%, 0) scale(1.00); }
  50%  { transform: translate3d( 3%,  2%, 0) scale(1.06); }
  100% { transform: translate3d(-1%,  3%, 0) scale(1.02); }
}

/* Faint perspective grid, faded out by a mask so it reads as depth rather
   than as graph paper. */
.bg__grid {
  background-image:
    linear-gradient(to right,  var(--line) 1px, transparent 1px),
    linear-gradient(to bottom, var(--line) 1px, transparent 1px);
  background-size: 64px 64px;
  opacity: .28;
  -webkit-mask-image: radial-gradient(120% 80% at 50% 30%, #000 15%, transparent 78%);
          mask-image: radial-gradient(120% 80% at 50% 30%, #000 15%, transparent 78%);
  animation: grid-creep 34s linear infinite;
}

@keyframes grid-creep {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, 64px, 0); }   /* one cell — seamless loop */
}

/* Horizontal light streaks, like distant traffic. Deliberately sparse: each
   one is visible for about two seconds in every nineteen. */
.bg__streaks::before,
.bg__streaks::after {
  content: "";
  position: absolute;
  left: -30%;
  width: 60%;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(0,229,255,.5), transparent);
  opacity: 0;
  animation: streak 19s var(--ease) infinite;
}
.bg__streaks::before { top: 28%; }
.bg__streaks::after  {
  top: 66%;
  background: linear-gradient(90deg, transparent, rgba(139,92,255,.45), transparent);
  animation-delay: 9.5s;
}

@keyframes streak {
  0%, 88%  { transform: translate3d(0, 0, 0);    opacity: 0; }
  92%      {                                     opacity: .8; }
  100%     { transform: translate3d(210%, 0, 0); opacity: 0; }
}

/* Scanlines. At a 3px period and 2.5% opacity these are felt, not seen. */
.bg__scan {
  background: repeating-linear-gradient(
    to bottom,
    rgba(255,255,255,.025) 0px,
    rgba(255,255,255,.025) 1px,
    transparent 1px,
    transparent 3px
  );
  opacity: .55;
}

/* Grain. A static SVG turbulence tile — no animation, because moving grain is
   both distracting and the most expensive thing here to composite. */
.bg__noise {
  opacity: .16;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.55'/%3E%3C/svg%3E");
}

/* Cursor aura. JS writes --mx/--my; this element only ever translates.
   Large and very soft, so it reads as the room lighting responding to you
   rather than as a spotlight stuck to the pointer. */
.bg__cursor {
  inset: auto;
  top: 0; left: 0;
  width: 52vmax; height: 52vmax;
  margin: -26vmax 0 0 -26vmax;
  background: radial-gradient(circle, rgba(0,229,255,.085), rgba(139,92,255,.05) 38%, transparent 66%);
  opacity: 0;
  transform: translate3d(var(--mx, 50vw), var(--my, 50vh), 0);
  transition: opacity 900ms var(--ease);
}
.bg__cursor.is-live { opacity: 1; }

/* Particles canvas, sized and drawn by assets/js/bg.js. */
.bg__particles { inset: 0; opacity: .5; }

/* ==========================================================================
   4. HUD detail parts
   --------------------------------------------------------------------------
   Supporting texture. Small, monospaced, low contrast — instrumentation at
   the edge of vision, never competing with content.
   ========================================================================== */

.mono {
  font-family: var(--font-mono);
  font-size: .68rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--text-3);
}

.tag {
  display: inline-flex; align-items: center; gap: .5rem;
  font-family: var(--font-mono); font-size: .66rem;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--cyan);
  padding: .34rem .6rem;
  border: 1px solid var(--line-cyan);
  border-radius: var(--r-sm);
  background: rgba(0,229,255,.05);
}
.tag--purple { color: var(--purple); border-color: var(--line-purp); background: rgba(139,92,255,.05); }
.tag--muted  { color: var(--text-3); border-color: var(--line); background: transparent; }

/* Section number, e.g. 02 / SHOP */
.sec-no {
  font-family: var(--font-mono); font-size: .7rem;
  letter-spacing: .2em; color: var(--text-3);
  display: flex; align-items: center; gap: .75rem;
  margin-bottom: 1rem;
}
.sec-no::after {
  content: ""; flex: 1; height: 1px;
  background: linear-gradient(90deg, var(--line-2), transparent);
}

/* Corner brackets, drawn with two pseudo-elements — no extra markup, no
   image request. */
.brackets { position: relative; }
.brackets::before,
.brackets::after {
  content: "";
  position: absolute; width: 22px; height: 22px;
  border-color: var(--line-cyan); border-style: solid;
  pointer-events: none;
  transition: border-color var(--t) var(--ease);
}

/* The brackets have to carry the same radius as the surface they sit on,
   or a square corner draws straight across a rounded one. They also sit at
   inset 0 rather than -1px now: outside a rounded corner there is nothing
   for the overhang to align with, and it gets clipped by the parent's
   overflow anyway. */
.brackets::before {
  top: 0; left: 0;
  border-width: 1px 0 0 1px;
  border-top-left-radius: var(--r-lg);
}
.brackets::after {
  bottom: 0; right: 0;
  border-width: 0 1px 1px 0;
  border-bottom-right-radius: var(--r-lg);
}

/* Status dot. Always accompanied by text — colour alone never carries
   meaning here. */
.dot {
  width: 7px; height: 7px; border-radius: 50%;
  display: inline-block; flex: none;
  background: var(--text-3);
}
.dot--ok    { background: var(--ok);     box-shadow: 0 0 8px rgba(52,229,160,.7); }
.dot--warn  { background: var(--warn);   box-shadow: 0 0 8px rgba(255,181,69,.7); }
.dot--out   { background: var(--danger); }
.dot--live  { animation: pulse 2.4s ease-in-out infinite; }

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: .35; }
}

/* HUD divider */
.rule {
  height: 1px; border: 0; margin: 0;
  background: linear-gradient(90deg, transparent, var(--line-2) 20%, var(--line-2) 80%, transparent);
}

/* A single thin scanning line, used on card images and the hero plate. */
@keyframes sweep {
  0%   { transform: translate3d(0, -100%, 0); opacity: 0; }
  12%  { opacity: 1; }
  88%  { opacity: 1; }
  100% { transform: translate3d(0, 900%, 0);  opacity: 0; }
}

/* ==========================================================================
   5. Buttons
   ========================================================================== */

.btn {
  --btn-accent: var(--cyan);
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  gap: .6rem;
  padding: .95rem 1.6rem;
  font-family: var(--font-mono);
  font-size: .76rem; font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase;
  /* Uppercase + wide tracking makes two-word labels wrap in narrow columns
     ("QUICK / VIEW" on a product card). Keep them on one line. */
  white-space: nowrap;
  color: var(--black);
  background: var(--btn-accent);
  border: 1px solid var(--btn-accent);
  border-radius: var(--r);
  cursor: pointer;
  text-decoration: none;
  overflow: hidden;
  transition: transform var(--t-fast) var(--ease),
              box-shadow var(--t) var(--ease),
              background-color var(--t) var(--ease);
}
.btn:hover {
  text-decoration: none;
  transform: translateY(-1px);
  box-shadow: 0 0 28px -8px var(--btn-accent);
}
.btn:active { transform: translateY(0); }

.btn--ghost {
  color: var(--text);
  background: transparent;
  border-color: var(--line-2);
}
.btn--ghost:hover {
  color: var(--white);
  border-color: var(--btn-accent);
  box-shadow: inset 0 0 24px -12px var(--btn-accent), 0 0 20px -10px var(--btn-accent);
}

.btn--purple { --btn-accent: var(--purple); }
.btn--block  { display: flex; width: 100%; }
.btn--sm     { padding: .62rem 1rem; font-size: .68rem; }

.btn[disabled], .btn[aria-disabled="true"] {
  opacity: .42; cursor: not-allowed; transform: none; box-shadow: none;
}

/* Busy state: a terminal-style progress bar inside the button itself. */
.btn.is-busy { color: transparent; pointer-events: none; }
.btn.is-busy::after {
  content: "";
  position: absolute; inset: auto 0 0 0; height: 2px;
  background: var(--black);
  animation: btn-load 900ms linear infinite;
  transform-origin: left;
}
@keyframes btn-load {
  0%   { transform: scaleX(0); }
  100% { transform: scaleX(1); }
}

/* ==========================================================================
   6. Navigation
   ========================================================================== */

.nav {
  position: sticky; top: 0; z-index: 60;
  border-bottom: 1px solid var(--line);
  background: rgba(5, 6, 8, .72);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
}

.nav__in {
  height: var(--nav-h);
  display: flex; align-items: center; gap: 1.25rem;
}

.logo {
  display: inline-flex; align-items: baseline; gap: .45rem;
  font-family: var(--font-display);
  font-weight: 800; font-size: 1.22rem;
  letter-spacing: .22em; text-transform: uppercase;
  color: var(--white); text-decoration: none;
  margin-right: auto;
  /* A two-word brand set in wide tracking wraps on a narrow header and drags
     the whole nav bar to two rows, stranding the 18+ badge on its own line. */
  white-space: nowrap;
}
.logo:hover { text-decoration: none; }
.logo span {
  font-family: var(--font-mono); font-size: .58rem;
  letter-spacing: .2em; color: var(--cyan);
  border: 1px solid var(--line-cyan); border-radius: var(--r-sm);
  padding: .12rem .34rem;
}

.nav__links { display: flex; align-items: center; gap: .25rem; }

.nav__link {
  position: relative;
  padding: .55rem .8rem;
  font-family: var(--font-mono); font-size: .72rem;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--text-2); text-decoration: none;
  border-radius: var(--r-sm);
  transition: color var(--t-fast) var(--ease);
}
.nav__link:hover, .nav__link:focus-visible { color: var(--white); text-decoration: none; }

/* Animated indicator: scales from the centre out. */
.nav__link::after {
  content: "";
  position: absolute; left: .8rem; right: .8rem; bottom: .28rem;
  height: 1px; background: var(--cyan);
  transform: scaleX(0); transform-origin: center;
  transition: transform var(--t) var(--ease);
}
.nav__link:hover::after,
.nav__link:focus-visible::after,
.nav__link[aria-current="page"]::after { transform: scaleX(1); }
.nav__link[aria-current="page"] { color: var(--white); }

/* Cart button */
.cart-btn {
  position: relative;
  display: inline-flex; align-items: center; gap: .55rem;
  padding: .55rem .85rem;
  font-family: var(--font-mono); font-size: .72rem;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--text); background: transparent;
  border: 1px solid var(--line-2); border-radius: var(--r);
  cursor: pointer;
  transition: border-color var(--t) var(--ease), box-shadow var(--t) var(--ease);
}
.cart-btn:hover { border-color: var(--line-cyan); box-shadow: var(--glow-cyan); }

.cart-btn__n {
  min-width: 1.35rem; height: 1.35rem;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0 .3rem;
  font-size: .68rem; font-weight: 700;
  color: var(--black); background: var(--cyan);
  border-radius: 999px;
}
.cart-btn__n:empty { display: none; }

/* Fired by JS when an item lands in the cart. */
.cart-btn.is-hit { animation: cart-hit 420ms var(--ease); }
@keyframes cart-hit {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.14); }
  100% { transform: scale(1); }
}

/* Mobile menu toggle */
.nav__burger {
  display: none;
  width: 40px; height: 40px;
  align-items: center; justify-content: center;
  background: transparent; border: 1px solid var(--line-2);
  border-radius: var(--r-sm); cursor: pointer;
  color: var(--text);
}

/* Narrow headers: pull the tracking in so the brand, cart and menu button all
   fit on one row. Tracking is the first thing to give — it is the most
   expensive part of the wordmark per character and the least missed. */
@media (max-width: 620px) {
  .logo { font-size: 1rem; letter-spacing: .1em; gap: .35rem; }
  .logo span { font-size: .54rem; letter-spacing: .12em; }
  .nav__in { gap: .6rem; }
  .cart-btn { padding: .55rem .65rem; }
}

@media (max-width: 900px) {
  .nav__burger { display: inline-flex; }

  /* Mobile navigation is a plain full-width panel, not a clever overlay.
     Big tap targets beat a fancy transition here. */
  .nav__links {
    position: absolute; top: var(--nav-h); left: 0; right: 0;
    flex-direction: column; align-items: stretch; gap: 0;
    padding: .5rem var(--gut) 1.25rem;
    background: rgba(5,6,8,.97);
    backdrop-filter: blur(16px);
    border-bottom: 1px solid var(--line);
    display: none;
  }
  .nav__links.is-open { display: flex; }
  .nav__link {
    padding: 1rem .25rem;
    font-size: .84rem;
    border-bottom: 1px solid var(--line);
  }
  .nav__link::after { display: none; }
}

/* ==========================================================================
   7. Hero
   ========================================================================== */

.hero {
  position: relative;
  padding: clamp(4rem, 12vh, 8rem) 0 clamp(3rem, 8vh, 6rem);
  overflow: hidden;
}

.hero__grid {
  display: grid;
  grid-template-columns: 1.15fr .85fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}
@media (max-width: 900px) {
  .hero__grid { grid-template-columns: 1fr; }
}

.hero h1 {
  /* Smaller than the global h1 scale. The hero headline names the product
     ("Disposable vapes, delivered after dark") rather than being one short
     word, and at the global 5rem cap that ran to four lines and pushed the
     call to action below the fold. Two lines is the target. */
  font-size: clamp(2.05rem, 5.1vw, 4rem);
  margin: 1rem 0 1.25rem;
  letter-spacing: -0.03em;
  text-wrap: balance;
}
.hero h1 .accent {
  background: linear-gradient(100deg, var(--cyan), var(--purple) 62%, var(--magenta));
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
}

.hero__lede {
  font-size: clamp(1rem, 1.6vw, 1.15rem);
  color: var(--text-2);
  max-width: 46ch;
  margin-bottom: 2rem;
}

/* The hook: the one sentence that tells a first-time visitor what this shop
   is. Brighter than body copy and marked with a cyan rule so the eye lands on
   it straight after the headline — it is doing a job, not decorating. */
.hero__hook {
  font-size: clamp(1.02rem, 1.7vw, 1.22rem);
  line-height: 1.55;
  color: var(--text);
  max-width: 50ch;
  margin-bottom: 2rem;
  padding-left: 1rem;
  border-left: 2px solid var(--cyan);
}

.hero__cta { display: flex; flex-wrap: wrap; gap: .85rem; }

.hero__stats {
  display: flex; flex-wrap: wrap; gap: 2rem;
  margin-top: 2.75rem; padding-top: 1.5rem;
  border-top: 1px solid var(--line);
}
.hero__stat b {
  display: block;
  font-family: var(--font-display);
  font-size: 1.5rem; color: var(--white); letter-spacing: -.01em;
}

/* Hero plate — the product stage. */
.plate {
  position: relative;
  aspect-ratio: 4 / 5;
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  background:
    radial-gradient(70% 55% at 50% 42%, rgba(0,229,255,.12), transparent 70%),
    linear-gradient(180deg, var(--char-2), var(--char));
  overflow: hidden;
  display: grid; place-items: center;
}
.plate::after {
  content: "";
  position: absolute; left: 0; right: 0; top: 0; height: 12%;
  background: linear-gradient(180deg, rgba(0,229,255,.14), transparent);
  animation: sweep 7s var(--ease) infinite;
}
.plate__hud {
  position: absolute; inset: 0;
  padding: 1rem;
  display: flex; flex-direction: column; justify-content: space-between;
  pointer-events: none;
}
.plate__row { display: flex; justify-content: space-between; gap: 1rem; }

/* ==========================================================================
   8. Sections
   ========================================================================== */

.section { padding: clamp(3.5rem, 9vh, 6.5rem) 0; }

.section__head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 1.5rem; flex-wrap: wrap;
  margin-bottom: 2.5rem;
}
.section__head p { color: var(--text-2); max-width: 52ch; margin: 0; }

/* ==========================================================================
   9. Product cards
   ========================================================================== */

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(258px, 1fr));
  gap: 1.25rem;
}

.card {
  position: relative;
  display: flex; flex-direction: column;
  background: linear-gradient(180deg, var(--char-2), var(--char));
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
  transition: transform var(--t) var(--ease),
              border-color var(--t) var(--ease),
              box-shadow var(--t) var(--ease);
}
.card:hover,
.card:focus-within {
  transform: translateY(-4px);
  border-color: var(--line-cyan);
  box-shadow: 0 18px 40px -24px rgba(0,0,0,.9), 0 0 32px -18px rgba(0,229,255,.75);
}
.card:hover .brackets::before,
.card:hover .brackets::after { border-color: var(--cyan); }

.card__media {
  position: relative;
  aspect-ratio: 1 / 1;
  background:
    radial-gradient(66% 60% at 50% 42%, rgba(139,92,255,.14), transparent 72%),
    var(--char);
  overflow: hidden;
  display: grid; place-items: center;
}
.card__media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--t-slow) var(--ease);
}
.card:hover .card__media img { transform: scale(1.055); }

/* Scanning line, revealed on hover only. */
.card__media::after {
  content: "";
  position: absolute; left: 0; right: 0; top: 0; height: 30%;
  background: linear-gradient(180deg, rgba(0,229,255,.16), transparent);
  opacity: 0;
  transform: translate3d(0, -100%, 0);
  transition: opacity var(--t) var(--ease);
}
.card:hover .card__media::after { opacity: 1; animation: sweep 1.9s var(--ease); }

/* Placeholder art for products with no photo yet.
   --------------------------------------------------------------------------
   Drawn as a recognisable device silhouette — body, mouthpiece, indicator —
   rather than an abstract shape. A vague glowing rectangle reads as a broken
   image and makes the whole shop look unfinished; a deliberate outline reads
   as "photography pending". Restrained on purpose: this is a stand-in, so it
   should never out-shout the cards that do have real photography. */
.ph {
  position: relative;
  width: 46%; aspect-ratio: 1 / 2.35;
  border: 1px solid var(--line-2);
  border-radius: 11px;
  background:
    linear-gradient(100deg, rgba(255,255,255,.045) 0 18%, transparent 42%),
    linear-gradient(165deg, var(--char-3), var(--char) 64%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.06),
    inset 0 -18px 26px -18px rgba(0,229,255,.22);
}

/* Mouthpiece */
.ph::before {
  content: "";
  position: absolute; top: -7px; left: 33%; right: 33%; height: 11px;
  border: 1px solid var(--line-2); border-bottom: 0;
  border-radius: 5px 5px 1px 1px;
  background: linear-gradient(180deg, var(--char-3), var(--char));
}

/* Indicator light */
.ph::after {
  content: "";
  position: absolute; bottom: 13%; left: 50%;
  width: 6px; height: 6px; margin-left: -3px;
  border-radius: 50%;
  background: var(--cyan);
  opacity: .55;
  box-shadow: 0 0 12px rgba(0,229,255,.65);
}

.card__body { padding: 1.05rem 1.1rem 1.2rem; display: flex; flex-direction: column; gap: .5rem; flex: 1; }

.card__brand { font-family: var(--font-mono); font-size: .64rem; letter-spacing: .18em; text-transform: uppercase; color: var(--text-3); }

.card__name {
  font-family: var(--font-display); font-size: 1.02rem; font-weight: 700;
  letter-spacing: -.01em; color: var(--white); margin: 0;
}
.card__name a { color: inherit; }
.card__name a::after { content: ""; position: absolute; inset: 0; }  /* whole-card link */

.card__variant { font-size: .82rem; color: var(--text-2); margin: 0; }

.card__foot {
  margin-top: auto; padding-top: .85rem;
  display: flex; align-items: center; justify-content: space-between; gap: .75rem;
  border-top: 1px solid var(--line);
}

.price {
  font-family: var(--font-display);
  font-size: 1.16rem; font-weight: 700; color: var(--white);
  letter-spacing: -.01em;
}
.price s { font-size: .8rem; font-weight: 400; color: var(--text-3); margin-right: .4rem; }

.stock {
  display: inline-flex; align-items: center; gap: .45rem;
  font-family: var(--font-mono); font-size: .64rem;
  letter-spacing: .12em; text-transform: uppercase; color: var(--text-2);
}

/* Card buttons sit above the whole-card link.
   Grid rather than flex so the primary action takes the free space and
   "Quick view" is only ever as wide as its own text needs. */
.card__actions {
  position: relative; z-index: 2;
  display: grid; grid-template-columns: 1fr auto; gap: .5rem;
}

.badge {
  position: absolute; top: .7rem; left: .7rem; z-index: 2;
  font-family: var(--font-mono); font-size: .6rem;
  letter-spacing: .16em; text-transform: uppercase;
  color: var(--black); background: var(--cyan);
  padding: .26rem .5rem; border-radius: var(--r-sm);
}
.badge--out { background: var(--danger); color: var(--white); }

.card.is-out .card__media { filter: grayscale(1) brightness(.62); }

/* ==========================================================================
   10. Scroll reveal
   --------------------------------------------------------------------------
   Applied by IntersectionObserver in assets/js/motion.js. The hidden start
   state is scoped to html.js, which JS sets on itself — so with JavaScript
   disabled every element is simply visible.
   ========================================================================== */

.js .reveal {
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  transition: opacity var(--t-slow) var(--ease), transform var(--t-slow) var(--ease);
  transition-delay: var(--d, 0ms);
}
.js .reveal.is-in { opacity: 1; transform: none; }

/* ==========================================================================
   11. Forms
   ========================================================================== */

.field { display: flex; flex-direction: column; gap: .4rem; margin-bottom: 1rem; }

.field label {
  font-family: var(--font-mono); font-size: .66rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--text-2);
}
.field .req { color: var(--cyan); }

.field input, .field select, .field textarea {
  width: 100%;
  padding: .8rem .9rem;
  font-family: var(--font-body); font-size: .95rem;
  color: var(--text);
  background: var(--char);
  border: 1px solid var(--line-2);
  border-radius: var(--r);
  transition: border-color var(--t) var(--ease), box-shadow var(--t) var(--ease);
}
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none;
  border-color: var(--cyan);
  box-shadow: 0 0 0 1px rgba(0,229,255,.35), 0 0 22px -10px rgba(0,229,255,.9);
}
.field textarea { resize: vertical; min-height: 96px; }

.field .hint  { font-size: .78rem; color: var(--text-3); }
.field .error { font-size: .78rem; color: var(--danger); }
.field input[aria-invalid="true"] { border-color: var(--danger); }

.row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 1rem; }
@media (max-width: 640px) { .row2 { grid-template-columns: 1fr; } }

.check {
  display: flex; align-items: flex-start; gap: .7rem;
  padding: 1rem;
  border: 1px solid var(--line-2); border-radius: var(--r);
  background: var(--char);
}
.check input { margin-top: .2rem; width: 18px; height: 18px; accent-color: var(--cyan); flex: none; }
.check span { font-size: .88rem; color: var(--text-2); }

/* Quantity stepper */
.qty { display: inline-flex; align-items: stretch; border: 1px solid var(--line-2); border-radius: var(--r); overflow: hidden; }
.qty button {
  width: 38px;
  background: var(--char); color: var(--text);
  border: 0; cursor: pointer; font-size: 1rem;
  transition: background-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.qty button:hover { background: var(--char-3); color: var(--cyan); }
.qty input {
  width: 52px; text-align: center;
  background: var(--char); color: var(--white);
  border: 0; border-inline: 1px solid var(--line);
  font-family: var(--font-mono); font-size: .9rem;
  -moz-appearance: textfield;
}
.qty input::-webkit-outer-spin-button,
.qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* ==========================================================================
   12. Panels, notices, states
   ========================================================================== */

.panel {
  background: linear-gradient(180deg, var(--char-2), var(--char));
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: clamp(1.25rem, 3vw, 1.9rem);
}

.notice {
  display: flex; gap: .85rem;
  padding: 1rem 1.1rem;
  border: 1px solid var(--line-2);
  border-left-width: 2px;
  border-radius: var(--r-sm);
  background: var(--char);
  font-size: .88rem; color: var(--text-2);
}
.notice strong { color: var(--text); }
.notice--warn   { border-left-color: var(--warn); }
.notice--danger { border-left-color: var(--danger); }
.notice--info   { border-left-color: var(--cyan); }
.notice--ok     { border-left-color: var(--ok); }

/* Empty / error state block */
.state { text-align: center; padding: clamp(2.5rem, 8vh, 5rem) 1rem; }
.state__code {
  font-family: var(--font-mono); font-size: .72rem;
  letter-spacing: .24em; text-transform: uppercase; color: var(--cyan);
  margin-bottom: .9rem;
}
.state p { color: var(--text-2); max-width: 46ch; margin-inline: auto; }
.state__cta { margin-top: 1.75rem; display: flex; gap: .75rem; justify-content: center; flex-wrap: wrap; }

/* Loading — terminal-style skeleton rather than a spinner. */
.skeleton {
  position: relative; overflow: hidden;
  background: var(--char-2);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.skeleton::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(0,229,255,.08), transparent);
  transform: translate3d(-100%, 0, 0);
  animation: shimmer 1.4s linear infinite;
}
@keyframes shimmer { to { transform: translate3d(100%, 0, 0); } }

.loading-line {
  font-family: var(--font-mono); font-size: .72rem;
  letter-spacing: .16em; text-transform: uppercase; color: var(--text-3);
}
.loading-line::after {
  content: "_";
  animation: caret 1s steps(2) infinite;
}
@keyframes caret { 50% { opacity: 0; } }

/* ==========================================================================
   13. Cart drawer
   ========================================================================== */

.drawer-scrim {
  position: fixed; inset: 0; z-index: 90;
  background: rgba(2,3,5,.72);
  backdrop-filter: blur(3px);
  opacity: 0; visibility: hidden;
  transition: opacity var(--t) var(--ease), visibility var(--t);
}
.drawer-scrim.is-open { opacity: 1; visibility: visible; }

.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 91;
  width: min(430px, 100vw);
  display: flex; flex-direction: column;
  background: var(--black);
  border-left: 1px solid var(--line-2);
  /* Only the inner edge is rounded — the other side is flush against the
     viewport, where a curve would just show a sliver of page behind it. */
  border-radius: var(--r-lg) 0 0 var(--r-lg);
  transform: translate3d(100%, 0, 0);
  transition: transform var(--t) var(--ease);
  visibility: hidden;
}
.drawer.is-open { transform: none; visibility: visible; }

.drawer__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 1.15rem 1.25rem;
  border-bottom: 1px solid var(--line);
}
.drawer__body { flex: 1; overflow-y: auto; padding: 1.25rem; }
.drawer__foot { border-top: 1px solid var(--line); padding: 1.25rem; background: var(--char); }

.x-btn {
  width: 36px; height: 36px;
  display: grid; place-items: center;
  background: transparent; color: var(--text-2);
  border: 1px solid var(--line-2); border-radius: var(--r-sm);
  cursor: pointer; font-size: 1.1rem; line-height: 1;
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.x-btn:hover { color: var(--white); border-color: var(--line-cyan); }

.line {
  display: grid;
  grid-template-columns: 62px 1fr auto;
  gap: .9rem; align-items: start;
  padding: .95rem 0;
  border-bottom: 1px solid var(--line);
}
.line:first-child { padding-top: 0; }
.line__thumb {
  width: 62px; aspect-ratio: 1; border-radius: var(--r-sm);
  border: 1px solid var(--line);
  background: radial-gradient(60% 60% at 50% 45%, rgba(0,229,255,.14), var(--char));
  display: grid; place-items: center; overflow: hidden;
}
.line__thumb img { width: 100%; height: 100%; object-fit: cover; }
.line__name { font-weight: 600; color: var(--white); font-size: .92rem; }
.line__meta { font-size: .78rem; color: var(--text-3); }
.line__ctl  { display: flex; align-items: center; gap: .6rem; margin-top: .5rem; }

.link-btn {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: var(--font-mono); font-size: .64rem;
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-3); text-decoration: underline;
}
.link-btn:hover { color: var(--danger); }

.totals { display: flex; flex-direction: column; gap: .5rem; margin-bottom: 1.1rem; }
.totals div { display: flex; justify-content: space-between; font-size: .9rem; color: var(--text-2); }
.totals .grand {
  padding-top: .7rem; margin-top: .2rem;
  border-top: 1px solid var(--line);
  font-family: var(--font-display); font-size: 1.2rem;
  font-weight: 700; color: var(--white);
}

/* The chip that flies to the cart when something is added. Position and
   distance are set inline by JS; the animation itself lives here. */
.fly {
  position: fixed; z-index: 95;
  width: 46px; height: 46px; border-radius: var(--r-sm);
  border: 1px solid var(--cyan);
  background: radial-gradient(circle, rgba(0,229,255,.35), rgba(5,6,8,.9));
  pointer-events: none;
  animation: fly 520ms var(--ease) forwards;
}
@keyframes fly {
  0%   { transform: translate3d(0,0,0) scale(1);   opacity: 1; }
  70%  {                                           opacity: .9; }
  100% { transform: translate3d(var(--dx), var(--dy), 0) scale(.24); opacity: 0; }
}

/* ==========================================================================
   14. Product detail
   ========================================================================== */

.pdp { display: grid; grid-template-columns: 1.05fr .95fr; gap: clamp(1.75rem, 5vw, 3.5rem); align-items: start; }
@media (max-width: 900px) { .pdp { grid-template-columns: 1fr; } }

.gallery__main {
  position: relative; aspect-ratio: 1 / 1;
  border: 1px solid var(--line-2); border-radius: var(--r-lg);
  background: radial-gradient(64% 58% at 50% 42%, rgba(0,229,255,.13), transparent 70%), var(--char);
  overflow: hidden;
  display: grid; place-items: center;
}
.gallery__main img { width: 100%; height: 100%; object-fit: cover; }

/* Crossfade between images: frames are stacked in one grid cell and only
   opacity animates. */
.gallery__main > * { grid-area: 1 / 1; transition: opacity var(--t) var(--ease); }
.gallery__main .is-hidden { opacity: 0; }

.gallery__thumbs { display: flex; gap: .6rem; margin-top: .75rem; flex-wrap: wrap; }
.thumb {
  width: 68px; aspect-ratio: 1;
  border: 1px solid var(--line-2); border-radius: var(--r-sm);
  background: var(--char); cursor: pointer; overflow: hidden; padding: 0;
  transition: border-color var(--t) var(--ease);
}
.thumb[aria-current="true"] { border-color: var(--cyan); box-shadow: var(--glow-cyan); }

.spec {
  display: grid; grid-template-columns: auto 1fr;
  gap: .45rem 1.25rem;
  font-size: .88rem;
}
.spec dt { font-family: var(--font-mono); font-size: .66rem; letter-spacing: .12em; text-transform: uppercase; color: var(--text-3); }
.spec dd { margin: 0; color: var(--text); }

/* Accordion for shipping / compliance blocks. */
.acc { border-top: 1px solid var(--line); }
.acc summary {
  cursor: pointer; padding: .95rem 0; list-style: none;
  font-family: var(--font-mono); font-size: .72rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--text);
  display: flex; justify-content: space-between; align-items: center;
}
.acc summary::-webkit-details-marker { display: none; }
.acc summary::after { content: "+"; color: var(--cyan); font-size: 1rem; }
.acc[open] summary::after { content: "\2212"; }
.acc__body { padding-bottom: 1.1rem; color: var(--text-2); font-size: .9rem; }

/* ==========================================================================
   15. Checkout
   ========================================================================== */

.checkout { display: grid; grid-template-columns: 1.25fr .75fr; gap: clamp(1.5rem, 4vw, 3rem); align-items: start; }
@media (max-width: 900px) { .checkout { grid-template-columns: 1fr; } }

.summary { position: sticky; top: calc(var(--nav-h) + 1.25rem); }

.steps { display: flex; gap: .5rem; flex-wrap: wrap; margin-bottom: 2rem; }
.step {
  display: inline-flex; align-items: center; gap: .5rem;
  font-family: var(--font-mono); font-size: .64rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--text-3);
  padding: .4rem .7rem;
  border: 1px solid var(--line); border-radius: var(--r-sm);
}
.step[data-state="done"]   { color: var(--ok);   border-color: rgba(52,229,160,.4); }
.step[data-state="active"] { color: var(--cyan); border-color: var(--line-cyan); box-shadow: var(--glow-cyan); }

/* ==========================================================================
   16. Age gate
   ========================================================================== */

.age {
  position: fixed; inset: 0; z-index: 120;
  display: grid; place-items: center;
  padding: var(--gut);
  background: rgba(3,4,6,.96);
  backdrop-filter: blur(10px);
}
.age[hidden] { display: none; }
.age__card {
  width: min(480px, 100%);
  background: linear-gradient(180deg, var(--char-2), var(--char));
  border: 1px solid var(--line-cyan);
  border-radius: var(--r-lg);
  padding: clamp(1.5rem, 5vw, 2.25rem);
  box-shadow: 0 0 60px -30px rgba(0,229,255,.8);
}
.age__card h2 { font-size: 1.5rem; }

/* ==========================================================================
   17. Footer
   ========================================================================== */

.footer {
  margin-top: clamp(3rem, 8vh, 6rem);
  border-top: 1px solid var(--line);
  background: rgba(5,6,8,.6);
  padding: clamp(2.5rem, 6vh, 4rem) 0 2rem;
}
.footer__grid {
  display: grid;
  grid-template-columns: 1.5fr repeat(3, 1fr);
  gap: 2rem;
  margin-bottom: 2.5rem;
}
@media (max-width: 800px) { .footer__grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 480px) { .footer__grid { grid-template-columns: 1fr; } }

.footer h4 {
  font-family: var(--font-mono); font-size: .66rem;
  letter-spacing: .18em; text-transform: uppercase;
  color: var(--text-3); margin-bottom: .9rem;
}
.footer ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .55rem; }
.footer a { color: var(--text-2); font-size: .9rem; }
.footer a:hover { color: var(--cyan); }

.footer__legal {
  padding-top: 1.75rem;
  border-top: 1px solid var(--line);
  display: flex; justify-content: space-between; gap: 1.5rem; flex-wrap: wrap;
  font-size: .78rem; color: var(--text-3);
}

/* The nicotine warning. Deliberately the highest-contrast block on the page:
   compliance copy is the one thing that must never be atmospheric. */
.warning-bar {
  border: 1px solid var(--warn);
  border-radius: var(--r-sm);
  background: rgba(255,181,69,.07);
  color: var(--text);
  padding: .9rem 1.1rem;
  font-size: .84rem;
  display: flex; gap: .8rem; align-items: flex-start;
}
.warning-bar strong {
  font-family: var(--font-mono); font-size: .7rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--warn);
  flex: none;
}
@media (max-width: 560px) { .warning-bar { flex-direction: column; gap: .4rem; } }

/* ==========================================================================
   18. Toasts
   ========================================================================== */

.toasts {
  position: fixed; left: 50%; bottom: 1.5rem; z-index: 130;
  transform: translateX(-50%);
  display: flex; flex-direction: column; gap: .5rem;
  width: min(420px, calc(100vw - 2rem));
  pointer-events: none;
}
.toast {
  display: flex; align-items: center; gap: .7rem;
  padding: .85rem 1rem;
  background: var(--char-2);
  border: 1px solid var(--line-cyan);
  border-radius: var(--r-sm);
  box-shadow: 0 12px 40px -20px rgba(0,0,0,1);
  font-size: .88rem;
  animation: toast-in var(--t) var(--ease);
}
.toast--err { border-color: var(--danger); }
@keyframes toast-in {
  from { opacity: 0; transform: translate3d(0, 12px, 0); }
  to   { opacity: 1; transform: none; }
}

/* ==========================================================================
   19. Reduced motion
   --------------------------------------------------------------------------
   The atmosphere layer is a nice-to-have. Someone who asks for reduced motion
   gets the same site, still, with every state change instant. Colour,
   contrast and layout are untouched — nothing is lost but the movement.
   ========================================================================== */

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

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

  .bg__grid, .bg__aura, .bg__streaks, .bg__scan { animation: none; }
  .bg__streaks, .bg__particles, .bg__cursor { display: none; }

  .js .reveal { opacity: 1; transform: none; }
  .card:hover { transform: none; }
}
