/* ═══ ez-page.css — the shared sheet for every generated page (D451) ═══════════════════════════
   Until now scripts/build-products.js inlined this whole sheet into all 500 generated pages: the
   same ~8 KB, five hundred times, in dist/. D451 turns /p/<slug>/ into a REAL product page — a
   picture pager, a weight picker, a qty stepper, an add-to-bag button and its own age gate — and
   inlining THAT would have put megabytes of identical bytes on disk and on the wire. So the sheet
   moved here: one content-hashed download for the whole site, and one place where this look lives.

   Everything above the "NEW FURNITURE" banner is carried over VERBATIM from the CSS const that
   used to live at build-products.js:325-425, comments included. It is not re-written, re-ordered
   or "tidied" — those comments are the record of decisions the owner made, and a page that looked
   right yesterday must look byte-identical today. New rules go BELOW the banner.
   ONE DATED EXCEPTION — 2026-10-19, THE CARDS (ANSWERS D685/D686/D688): he retired the boxes
   everywhere a product picture appears, so the rules that DREW a box here (.prod, .prod img,
   .sibcard, .sib-media, and .pdp-media below the banner) are edited in place, each with its date
   and decision, rather than overridden from below with a second, contradicting rule. Everything
   else above the banner is still the verbatim record.

   ⚠️ EVERY var() IN THIS FILE MUST BE DEFINED IN THIS FILE. check-css-vars.js scopes per FILE and
   allows no fallback-less reference to a token it cannot see — and the generated pages no longer
   carry a <style> block of their own, so there is no second sheet to borrow from. Copying a token
   out of index.html without copying its DEFINITION is the one mistake this file cannot survive. */

  :root{--green:#5BE584;--ink:#0A0C0A;--deep:#245C3C;--paper:#FFFFFF;
    --muted:rgba(255,255,255,.64);--line:rgba(255,255,255,.12);
    --panel:rgba(255,255,255,.04);--panel2:rgba(255,255,255,.015)}
  *{box-sizing:border-box}
  html,body{margin:0;padding:0}
  body{background:var(--ink);color:var(--paper);font-family:Syne,Inter,system-ui,-apple-system,sans-serif;
    line-height:1.6}   /* 🚫 D713 (2026-10-21): the deep-green top tint that sat under the film/aurora goes with them — the plain ink ground on every /p/ and /b/ page (the manual checker's catch) */
/* 🌌 THE AURORA, on the static pages too (owner's tap D374: "Every page, site-wide"). The SPA runs
   this as real elements with scroll parallax; here it is two body pseudo-elements, because these
   pages are generated by the hundred and must not grow markup to carry a background. Same reading
   of his four rythm.com frames: a near-black ground the colour never fills, drifting pools, and one
   diagonal ribbon with a GOLD edge sitting ON TOP of the dark as an actual light source. The grain
   is load-bearing — wide smooth gradients band into visible steps on an OLED phone, which is the
   "cheap... pixly" he rejected once already. */
body::before,body::after{content:none;position:fixed;inset:0;z-index:-1;pointer-events:none}   /* 🚫 D713 (owner, 2026-10-21): "Just get rid of the green and gold background" — the aurora below no longer renders (content:none); the ground is the plain ink. The rules stay as the record. */
body::before{
  background:
    radial-gradient(58% 42% at 16% 12%,rgba(63,191,99,.24),transparent 52%),
    radial-gradient(56% 40% at 88% 86%,rgba(91,229,132,.16),transparent 50%),
    linear-gradient(180deg,rgba(10,12,10,.34),rgba(10,12,10,.68) 50%,rgba(10,12,10,.34)),
    radial-gradient(120% 82% at 50% 44%,transparent 22%,rgba(6,8,6,.8) 100%);
  animation:aurPools 64s ease-in-out infinite}
body::after{
  background:
    radial-gradient(30% 11% at 50% 42%,
      rgba(124,255,155,.80) 0%,
      rgba(63,191,99,.44) 24%,
      rgba(217,192,138,.36) 38%,
      transparent 66%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='.085'/%3E%3C/svg%3E");
  /* no mix-blend-mode: it forces a full-size offscreen buffer, which is what
     took the renderer out on the SPA (2026-09-04) */
  animation:aurBand 72s ease-in-out infinite}
@keyframes aurPools{0%,100%{transform:translate3d(0,0,0) scale(1)}50%{transform:translate3d(3%,2%,0) scale(1.1)}}
@keyframes aurBand{0%,100%{transform:translate3d(-8%,-4%,0) rotate(-28deg) scale(1.5)}
                   50%   {transform:translate3d(7%,6%,0)  rotate(-14deg) scale(1.9)}}
@media (prefers-reduced-motion:reduce){body::before,body::after{animation:none!important}}

  a{color:var(--green);text-decoration:none}
  img{max-width:100%}
  .wrap{max-width:860px;margin:0 auto;padding:20px}
  header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:6px 0 4px}
  .brand img{height:34px;width:auto;display:block}
  .navcta{font-weight:700;font-size:14px;border:1px solid var(--line);padding:9px 15px;border-radius:999px;color:var(--paper)}
  .eyebrow{font-family:Syne,sans-serif;font-weight:700;font-size:11.5px;letter-spacing:.22em;
    text-transform:uppercase;color:var(--green);margin:26px 0 8px}
  .eyebrow a{color:var(--green)}
  h1{font-family:Syne,sans-serif;font-weight:800;font-size:clamp(30px,6.5vw,48px);line-height:1.05;
    letter-spacing:-.02em;margin:0 0 10px;text-wrap:balance}
  h1 .g{color:var(--green)}
  .brand-mark{display:block;max-height:56px;width:auto;object-fit:contain;margin:0 0 10px;filter:drop-shadow(0 0 12px rgba(91,229,132,.35))}
  /* no drop-shadow any more (2026-10-21): the product page carries NO filter of any kind — ioslayers counts them
     as GPU layers, and the brief's rule is "no `filter` anywhere on the page" (D711) */
  .brand-mark-sm{display:block;max-height:34px;width:auto;object-fit:contain;margin:24px 0 -18px}
  /* display:block above beats the UA's [hidden]{display:none} — without this, onerror's hide is a no-op */
  .brand-mark[hidden],.brand-mark-sm[hidden]{display:none}
  .sub{font-size:clamp(15px,2.3vw,18px);color:var(--muted);max-width:640px;margin:0 0 18px}
  .meta{color:var(--muted);font-size:14px;font-weight:600;margin:0 0 6px}
  .potency{color:var(--green);font-size:14px;font-weight:700;letter-spacing:.04em;margin:0 0 14px}
  .price{font-family:Syne,sans-serif;font-weight:800;font-size:clamp(28px,5vw,36px);color:var(--green);margin:6px 0 16px}
  .price s{color:var(--muted);font-weight:600;font-size:.55em;margin-right:8px;vertical-align:middle}
  .dealnote{font-family:Syne,sans-serif;font-weight:700;font-size:14px;color:var(--green);margin:-8px 0 16px}
  .soldbadge{display:inline-block;font-family:Syne,sans-serif;font-weight:800;font-size:13px;letter-spacing:.08em;
    text-transform:uppercase;color:var(--paper);background:rgba(255,255,255,.06);border:1px solid var(--line);
    border-radius:999px;padding:7px 14px}
  .soldwrap{margin:0 0 12px}
  .hero-img{width:min(340px,80vw);height:auto;border-radius:20px;background:rgba(255,255,255,.04);
    border:1px solid var(--line);display:block;margin:18px 0 6px}
  .cta-row{display:flex;flex-wrap:wrap;gap:12px;margin:0 0 12px}
  .btn{display:inline-block;font-family:Syne,sans-serif;font-weight:800;font-size:16px;padding:15px 28px;border-radius:14px}
  .btn.primary{background:var(--green);color:var(--ink)}
  .btn.ghost{border:1px solid var(--line);color:var(--paper)}
  .trust{display:flex;flex-wrap:wrap;gap:8px 16px;color:var(--muted);font-size:13.5px;font-weight:600;margin:6px 0 28px}
  .trust b{color:var(--green)}
  .rating{color:var(--paper);font-size:14px;font-weight:600;margin:2px 0 16px}
  h2{font-family:Syne,sans-serif;font-weight:700;font-size:clamp(21px,4vw,27px);margin:34px 0 12px}
  .prods{display:grid;gap:0;margin:6px 0}
  /* min-width:0 on the GRID ITEM — same shrink trap build-days documented at 390px */
  /* --pg is set inline only on rows whose product carries a glow; the transparent fallback makes
     the radial layer invisible, so a glow-less row renders EXACTLY as before this layer existed */
  /* 🪶 BOXLESS ROWS — D685 (2026-10-19, "get rid of the boxes completely and just have the product").
     The panel gradient, the border and the radius are gone; what separates two rows is the one
     hairline under each (the grid gap went to 0 so the hairlines are the rhythm). The product's own
     colour stays, as D688 asks ("give product pictures a hue so they don't look awkward just sitting
     there"): the --pg radial is no longer a wash across the row but a soft halo the size of the thumb,
     centred on it (14px padding + half of 56px), never a tile. */
  .prod{min-width:0;display:flex;align-items:center;gap:13px;padding:13px 14px;border-radius:0;
    border:0;border-bottom:1px solid var(--line);background:radial-gradient(circle 46px at 42px 50%,color-mix(in srgb,var(--pg,transparent) 34%,transparent),transparent);color:var(--paper)}
  /* no plate behind the thumb (D685) — and contain, not cover: with nothing framing it, the whole
     cutout shows rather than a crop of it */
  .prod img,.prod-noimg{width:56px;height:56px;flex:0 0 auto;border-radius:0;object-fit:contain;background:transparent}
  .prod-b{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px}
  .prod-b>*{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .prod-brand{font-size:11.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--green);font-weight:700}
  .prod-n{font-family:Syne,sans-serif;font-weight:700;font-size:15.5px;line-height:1.25;
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .prod-m{font-size:12.5px;color:var(--muted)}
  /* one price → the green Syne number, exactly as before. A day deal → <s>standing</s><b>today</b>
     stacked right-aligned, the SAME pair build-days.js:297-299 prints on /deals/<day>/, so the two
     generated trees read as one system. The <s>/<b> rules only bite when the pair is painted. */
  .prod-p{flex:0 0 auto;text-align:right;display:flex;flex-direction:column;line-height:1.2;
    font-family:Syne,sans-serif;font-weight:800;font-size:19px;color:var(--green)}
  .prod-p s{font-family:Syne,Inter,system-ui,-apple-system,sans-serif;font-weight:400;
    font-size:12.5px;color:var(--muted)}
  .prod-p b{font-family:Syne,sans-serif;font-weight:800;font-size:19px;color:var(--green)}
  .note{color:var(--muted);font-size:13.5px;margin:12px 0 0}
  /* 🖼 MORE FROM <BRAND> — the card shape he circled (owner, 2026-09-11: "I liked the older style
     where you could see product pictures as well, don't make full product boxes, have them in the
     style of boxes I circled around"). Ported from index.html's .hf-card rail (index.html:2715-2727)
     rather than invented, so the row on a /p/ page and the FEATURED row he pointed at are the same
     object to the eye. Two deliberate departures:
       · It SCROLLS WITH A FINGER, it does not auto-animate. The featured rail is a marquee because
         it is a shop window; this is a list of six things he might want next, and a row that moves
         under the thumb is the D477 "glitchy tapping" complaint in miniature. It also keeps this off
         the composited-layer budget gpu-test guards.
       · No price line — build-products.js says why (nothing re-prices a sibling at runtime).
     scroll-snap keeps a card from being left half-cut at rest; the negative margin lets the row
     bleed to the page edge so the last card reads as "there is more", while the padding keeps the
     first one aligned with the copy above it. */
  .sibrail{display:flex;gap:10px;margin:10px -18px 0;padding:2px 18px 6px;overflow-x:auto;
    scroll-snap-type:x proximity;-webkit-overflow-scrolling:touch;scrollbar-width:none}
  .sibrail::-webkit-scrollbar{display:none}
  /* 🪶 BOXLESS — D685 (2026-10-19): the card's panel and border are gone, the photo and the two lines
     stay exactly where they were (the same rule the brief gives the .hf-card rail these were ported
     from: "loses background and border — photo + text as today"). The thumb keeps its soft green
     halo (a window, not a plate — the rail's own words) and loses the 3% plate under it. */
  .sibcard{flex:0 0 auto;width:196px;display:flex;align-items:center;gap:11px;text-align:left;
    background:transparent;border:0;border-radius:14px;
    padding:9px 11px 9px 9px;scroll-snap-align:start;transition:transform .18s ease}
  .sibcard:hover{transform:translateY(-3px)}
  /* 2026-10-21 (D699/D711): the green halo behind the thumb is gone — "the hue behind the pictures don't look
     good" — the thumb stands on Spot instead (`.pic`, the two neutral gradients below the Glacier banner) */
  .sib-media{flex:0 0 auto;width:54px;height:54px;border-radius:11px;display:flex;align-items:center;
    justify-content:center;overflow:hidden;background:transparent}
  .sib-media img{width:100%;height:100%;object-fit:contain}
  .sib-info{display:flex;flex-direction:column;min-width:0;gap:2px}
  .sib-name{font-size:13px;font-weight:600;color:var(--white,#fff);white-space:nowrap;overflow:hidden;
    text-overflow:ellipsis;max-width:120px}
  .sib-size{font-size:10px;letter-spacing:.09em;text-transform:uppercase;color:var(--muted-2,rgba(255,255,255,.5))}
  @media(max-width:440px){.sibcard{width:184px}}
  @media (prefers-reduced-motion:reduce){.sibcard{transition:none}.sibcard:hover{transform:none}}
  .links{display:flex;flex-wrap:wrap;gap:9px;margin:10px 0 0}
  .links a{border:1px solid var(--line);padding:10px 16px;border-radius:999px;font-weight:700;font-size:14px;color:var(--paper)}
  .fine{border-top:1px solid var(--line);margin-top:40px;padding-top:18px;color:var(--muted);font-size:12.5px}
  .fine a{color:var(--muted);text-decoration:underline}
  @media (max-width:430px){ .prod img,.prod-noimg{width:48px;height:48px} .prod-p,.prod-p b{font-size:17px}
    /* the halo follows the smaller thumb: 14px + half of 48px */
    .prod{background:radial-gradient(circle 40px at 38px 50%,color-mix(in srgb,var(--pg,transparent) 34%,transparent),transparent)} }

/* ═══ NEW FURNITURE — D451 / D452 ═══════════════════════════════════════════════════════════════
   Above: the sheet as it shipped. Below: what a page needs once it stops being a lander and becomes
   THE product page. Measurements are lifted from index.html's PDP and gate on purpose — the two
   surfaces must not read as two websites. */

/* index.html's vocabulary, copied WITH its definitions (check-css-vars allows no fallback-less
   reference, and these pages carry no <style> block to borrow from). --brand/--brand-2/--pd-glow
   default to the house green; a per-page inline style on .pdp-media overrides them, as the SPA does. */
:root{
  --white:#f3f6f2;--muted-2:#6f786f;--line-strong:rgba(255,255,255,.16);
  --green-bright:#5BE584;--green-glow:rgba(63,191,99,.45);--panel-2:#151915;
  --glass-2:rgba(28,36,29,.44);--glass-line:rgba(255,255,255,.13);--glass-hi:rgba(255,255,255,.22);
  --radius-lg:26px;
  --ease:cubic-bezier(.22,.61,.36,1);--ease-out:cubic-bezier(.16,1,.3,1);
  --ease-soft:cubic-bezier(.32,.72,0,1);--spring:cubic-bezier(.34,1.56,.64,1);
  --brand:#5BE584;--brand-2:#3FBF63;--pd-glow:rgba(91,229,132,.33);
  /* the Glacier page's voice (D697–D712, 2026-10-21): the one mono, the dark ground's ink-on-ink tokens.
     --box is the figures' frame and the callout labels' ground (the v9 sketch's dark values), --accent
     the bands, the + marks and the swap's labels (the brand line went with D718), --muted-3 index.html's own (:root --muted-3, the footer's © line).
     🎨 D717 ("Edition colour"): a limited edition's page re-sets --accent ONCE, inline on .wrap.pdp, to the row's `lim` (Plasma
     #c9a0ff, Glacier #9FE0FF) — the band titles, the + marks and the sequence labels follow it; this green stays
     the value for every other page and for everything outside the column (header, footer, buy bar). */
  --mono:"Fragment Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  --fg:#f3f6f2;--accent:#5BE584;--box:rgba(243,246,242,.05);--muted-3:#5d655d}

/* 🎬 THE FILM. position:fixed, not absolute — these pages have no .bg-fx wrapper; the aurora is two
   body pseudo-elements and the film is the layer below them. NO poster, ever (D446: it is opacity 0
   until it plays, so a poster paints never and cost 2.7s on cold LTE), and it holds at 0 until
   `playing` fires, so a stall shows the aurora and never a frozen frame. `.bg-vid{…opacity:0…}` is
   the literal shape filmfile-test greps for. */
.bg-vid{position:fixed;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;z-index:-2;
  transition:opacity 1.4s var(--ease-soft);pointer-events:none}
.bg-vid.on{opacity:1}
/* 🔄 THE HANDOFF. deals.html display:none's BOTH pseudo-elements when the film takes over — it can,
   it keeps a separate .aur-veil. There is none here, and a headline over an ungraded film is the
   "too dim / goes away" complaint in reverse. So ::before is repainted as the D419 veil and stopped;
   ::after (the gold band + grain) goes. animation:none matters as much as the opacity — a RUNNING
   animation on a full-bleed element stays composited whatever it looks like. */
html.bgvid body::before{animation:none;
  background:linear-gradient(180deg,rgba(10,12,10,.18) 0%,rgba(10,12,10,.42) 26%,rgba(10,12,10,.50) 50%,rgba(10,12,10,.42) 74%,rgba(10,12,10,.18) 100%),
   radial-gradient(120% 82% at 50% 44%,transparent 22%,rgba(6,8,6,.62) 100%)}
html.bgvid body::after{display:none}
@media (prefers-reduced-motion:reduce){ .bg-vid{display:none} }

/* ── 🖼 THE PICTURE BOX, THE AURA AND THE PAGER ARE GONE — 2026-10-21, THE GLACIER PAGE (D697–D712).
   What stood here: the always-square .pdp-media (D442), its blurred ::before aura in the product's own
   colour (D688, filter:blur(32px)), the blurred copy of the photo behind it (.pdp-bg, filter:blur(28px))
   and the dot pager with its stretched dot (D439). His words that retired each of them, in order:
   D697 "For product pics lets go a different route instead of the slides … while youre scrolling more
   pics show" — no pager, the pictures arrive down the page as numbered figures (.figs below);
   D699 "Don't like the motion and the hue behind the pictures don't look good!?" — no coloured aura;
   D703/D711 "Instead of the aura let's find something else because if the product pics just sit there
   without anything, it wouldn't look good" → SPOT, a pool of neutral light and a lit floor, gradients
   only, on every .pic (below). And NO `filter` ANYWHERE on the page — the two blurs here were ~3 MB of
   GPU layer each at 3× (the 2026-10-20 iOS "A problem repeatedly occurred"); ioslayers' count on /p/
   is 0 now. The hero still keeps #ezMedia / #ezPhoto so ringfit/photobox keep their anchors.
   The .mac/.ring history (D435 → D448 → D453 → D685) stays in the git record. */
/* 🏷 THE TWO CHIPS (D435: "different color, and also the placement") — his 🛡 "Actual product photo" and 🔬
   "Macro" flags, per picture (D447), on the hero still. Dark glass, white small caps, opposite corners.
   The [hidden] rule is required: inline-flex beats the UA's [hidden]{display:none}. */
.pic.hero .chip{position:absolute;right:14px;z-index:6;display:inline-flex;align-items:center;gap:6px;
  font:700 10.5px/1 Syne,Inter,system-ui,sans-serif;letter-spacing:.08em;text-transform:uppercase;color:#fff;
  background:rgba(10,13,10,.68);border:1px solid rgba(255,255,255,.22);border-radius:999px;
  padding:6px 10px;box-shadow:0 6px 16px rgba(0,0,0,.35);pointer-events:none}
.pic.hero .chip.real{top:14px}
.pic.hero .chip.macro{bottom:14px}
.pic.hero .chip[hidden]{display:none}

/* ── ⚖️ THE WEIGHT PICKER. On the SPA these are buttons that re-render in place; here each weight is
   a REAL PAGE, so they are anchors — the whole point of D451 ("let's make sure absolutely everything
   has it's own page"). Being anchors, :link and :visited are styled IDENTICALLY: a customer who
   already looked at the 7g must not return to a picker where one pill is a different colour. */
.ezweights{display:flex;gap:8px;flex-wrap:wrap;margin:0 0 20px}
.wpick,.wpick:link,.wpick:visited{display:inline-flex;align-items:center;justify-content:center;
  min-height:44px;min-width:74px;padding:9px 15px;border-radius:13px;
  border:1px solid var(--line-strong);background:rgba(255,255,255,.03);
  font-family:Syne,sans-serif;font-weight:700;font-size:14px;color:var(--white);
  text-decoration:none;transition:border-color .15s,background .15s,color .15s}
/* A rung with no page of its own is a <button> (a hidden weight — see ez-page.js's weightPick).
   It must be pixel-identical to the <a> rungs beside it, so the browser's native button styling is
   reset outright: without appearance:none iOS draws its own rounded chrome over this one and the
   ladder comes out with one pill that does not match the others. */
button.wpick{appearance:none;-webkit-appearance:none;margin:0;cursor:pointer;font:inherit;
  font-family:Syne,sans-serif;font-weight:700;font-size:14px;line-height:normal}
.wpick:hover{border-color:color-mix(in srgb,var(--brand) 55%,transparent)}
.wpick.on,.wpick.on:link,.wpick.on:visited{border-color:var(--brand);
  background:color-mix(in srgb,var(--brand) 14%,transparent);color:var(--green-bright)}

/* ── ⚫ THE SWATCH CIRCLES — D686 (2026-10-19, his tap: "Swatch circles under the photo", "the
   chosen one ringed in green, the price beside it when it differs"). The SAME rungs as the ladder
   above (a .wpick link or button, .on on the one standing — ez-page.js tells them apart by nothing),
   so a color sibling (Puffco, D682) is picked exactly as a hidden weight is; what changes is the
   drawing. Each rung is a 22px circle in its own swatch (`--sw`, set inline by build-products from
   the catalog's #hex), 10px apart, centred under the photo; the chosen one wears a 2px green ring
   standing 3px off its edge (the ::after's inset: 3px of air + the 2px border = -5px). The hairline
   and the inner shadow are the storefront's own .wsw dot treatment — without them Black is invisible
   on ink and White has no edge. The ::before is the tap zone: 22px is a dot, not a target, so each
   circle answers to 32×44 — the full pitch sideways (half the gap each way), never a neighbour's.
   The name lives in aria-label/title and in the caption under the row. */
.ezweights.colors{justify-content:center;gap:10px;margin:0 0 8px}
.ezweights.colors .wpick,.ezweights.colors .wpick:link,.ezweights.colors .wpick:visited{position:relative;
  width:22px;height:22px;min-width:0;min-height:0;padding:0;border-radius:50%;
  border:1px solid rgba(255,255,255,.3);background:var(--sw,var(--muted-2));
  box-shadow:inset 0 0 0 1px rgba(0,0,0,.28);transition:border-color .15s}
.ezweights.colors .wpick::before{content:"";position:absolute;inset:-11px -5px}
.ezweights.colors .wpick:hover{border-color:rgba(255,255,255,.6)}
.ezweights.colors .wpick.on,.ezweights.colors .wpick.on:link,.ezweights.colors .wpick.on:visited{
  border-color:rgba(255,255,255,.3);background:var(--sw,var(--muted-2))}
.ezweights.colors .wpick.on::after{content:"";position:absolute;inset:-5px;border:2px solid var(--green);
  border-radius:50%;pointer-events:none}
/* the caption: `Blue · $180` — the colour's name (never translated, translate="no" in the markup)
   and its price only when it is not this page's price; baked as the page's own colour, repainted on
   every tap by ez-page.js. Syne 700 14px: the chip's own face, so the word reads as the same label
   it was, now under the circles instead of inside a pill. */
.swcap{text-align:center;font-family:Syne,sans-serif;font-weight:700;font-size:14px;color:var(--white);margin:0 0 4px}
.swcap[hidden]{display:none}

/* ── 🏷 THE BADGE ROW — D685 (2026-10-19): "a NEW label is small green text; nothing is drawn over
   the photo." One line of small caps directly above the eyebrow: the day-deal note (#pdeal, the
   clock's element, amber) and the product's badges (New · Popular · Sale — Sale amber like the
   deal, the rest green). 10.5px, .12em tracking: the card's own badge measure.
   THE MARGINS ARE ARITHMETIC, NOT TASTE. The row is printed on every page (#pdeal has to be
   there for the clock) and is usually EMPTY — every child hidden — and an empty block collapses
   through: its 26px top, 0 bottom and the eyebrow's 6px top fold with the brand mark's -18px bottom
   to 26 − 18 = 8px, which is exactly the gap the eyebrow's own 26px top gave yesterday (and 26px
   after a bare pager, also as yesterday). With something showing: 8px under the mark, 6px above
   the eyebrow. The [hidden] rule is required: inline-block beats the UA's [hidden]{display:none},
   the trap .brand-mark documents above — and a hidden span that still painted would also give the
   row a line box and break the collapse. */
/* Syne first (2026-10-21): the site's one body face — typeface-test reads the first family of every text-bearing element */
.pbadges{margin:26px 0 0;font:700 10.5px/1 Syne,Inter,system-ui,sans-serif;letter-spacing:.12em;
  text-transform:uppercase;color:var(--green)}
.pbadges+.eyebrow{margin-top:6px}
.pb{display:inline-block;margin:0 12px 0 0}
.pb:last-child{margin-right:0}
.pb.deal,.pb.sale{color:#ffb347}
.pb.limited{color:#c9a0ff}   /* the fallback violet — a limited row prints its own colour inline, the same as the SPA card (D705) */
.pb[hidden]{display:none}

/* ── ● THE STRAIN DOT — D686 ("Dot + word"): the 7px dot before Indica · Sativa · Hybrid in a /b/
   row's meta line, its colour inline from build-products (one hex per type, the cards' three). */
.sdot{display:inline-block;width:7px;height:7px;border-radius:50%;margin:0 5px 0 0;vertical-align:middle}

/* ── 🛒 THE BUY BAR — block 12 of the Glacier page (2026-10-21, the v3 bar he picked in the sketch: dark, the
   price white, a white pill). Fixed to the bottom, rgba(10,12,10,.96), a hairline on top: the price (Syne 800
   20px white) + `<name> · pay on delivery` (12px --muted-2) left, the qty control compact, the white pill
   `Add to bag` (Syne 800 15px, ink text). 44px is still the floor on every control, and it is not a style
   preference: he ships from a phone and these are the two taps that end in an order. The ids are the
   contract ez-page.js and the overlay keep — #ezBuy (hidden whole when he has taken the product off the
   menu), #pprice, #ezMinus #ezQty #ezPlus, #ezAdd. Under the ☰ sheet and the gate it steps out of the way. */
.ezbar{position:fixed;left:0;right:0;bottom:0;z-index:110;display:flex;align-items:center;gap:8px;
  padding:10px 14px calc(10px + env(safe-area-inset-bottom,0px));background:rgba(10,12,10,.96);color:var(--fg);
  border-top:1px solid rgba(255,255,255,.1)}
/* above 560px the bar's three pieces sit on the page's own 560px column instead of at the screen's two edges —
   the same arithmetic as .wrap.pdp's centring, with the phone's 14px as the floor */
@media (min-width:561px){ .ezbar{padding-left:max(14px,calc(50% - 266px));padding-right:max(14px,calc(50% - 266px))} }
.ezbar[hidden]{display:none}
html.ezmnav-up .ezbar,html.gate-up .ezbar{visibility:hidden}
.ezbar .bar-t{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}
.ezbar .price{font-family:Syne,Inter,sans-serif;font-weight:800;font-size:20px;line-height:1.1;color:var(--fg);margin:0;white-space:nowrap}
.ezbar .price s{color:var(--muted-2);font-weight:600;font-size:.6em;margin-right:6px;vertical-align:middle}
.ezbar .bar-s{display:block;color:var(--muted-2);font-size:12px;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* the qty control is COMPACT on purpose (the brief): at 390 the bar holds the price column, − 1 + and the pill, and
   every pixel the stepper does not take is a letter of the name beside the price. 44px tall stays the floor. */
.qty{display:inline-flex;align-items:center;border:1px solid var(--line-strong);border-radius:999px;overflow:hidden;flex:0 0 auto}
.qbtn{width:32px;min-height:44px;border:0;padding:0;cursor:pointer;font-size:18px;color:var(--white);
  background:rgba(255,255,255,.03);transition:background .15s,color .15s}
.qbtn:hover{background:rgba(255,255,255,.1);color:var(--fg)}
.qn{min-width:22px;text-align:center;font-family:Syne,sans-serif;font-weight:700;font-size:15px;color:var(--white)}
#ezAdd{flex:0 0 auto;height:44px;min-height:44px;border:0;cursor:pointer;font-family:Syne,Inter,sans-serif;font-weight:800;
  font-size:15px;line-height:1;padding:0 16px;border-radius:999px;background:var(--fg);color:#0a0c0a;white-space:nowrap;
  box-shadow:none;transition:transform .2s var(--ease),background .2s}
#ezAdd:hover{background:#fff}
#ezAdd:active{transform:translateY(1px)}
#ezAdd.ok{background:var(--green)}
/* ⚠️ DISABLED MUST READ AS UNAVAILABLE, NOT AS DIM. The button is enabled from first paint on
   purpose (fail-open — if the catalog overlay never answers, a real product stays buyable), so OFF
   is the state a customer meets when we are telling them something. A 45%-opacity slab still
   looks pressable; a grey one that will not move under a thumb does not. */
#ezAdd[disabled]{background:rgba(255,255,255,.06);border:1px solid var(--line);color:var(--muted-2);
  box-shadow:none;cursor:not-allowed}
#ezAdd[disabled]:hover,#ezAdd[disabled]:active{transform:none;box-shadow:none;background:rgba(255,255,255,.06)}

/* ── 🛍 THE HEADER BAG. index.html's #cartCount badge and its hidden state: scale(0), NOT
   display:none — it springs in so a tap reads as "that went in", and a box that never reflows
   cannot shift the header under a thumb mid-tap. .on is added at qty > 0. */
.ezbag{position:relative;display:inline-flex;align-items:center;justify-content:center;
  min-width:44px;min-height:44px;padding:9px 13px;font-size:17px}
.ezbag .count{position:absolute;top:-5px;right:-5px;min-width:18px;height:18px;border-radius:999px;
  background:var(--green-bright);color:#04210f;font-size:11px;font-weight:800;padding:0 4px;
  display:flex;align-items:center;justify-content:center;
  transform:scale(0);transition:transform .25s var(--ease)}
.ezbag .count.on{transform:scale(1)}

/* ══ 🚪 D452 — THE GATE ON THE GENERATED PAGES → assets/ez-gate.css (2026-09-13, sweep high #2).
   The gate's rules lived here and so reached only the pages that link THIS sheet (/p/, /b/); the day
   and city pages and deals.html never had them. They are their own hashed sheet now, linked by every
   public generated page next to this one. Only the veil's deference to the gate stays below. */

/* the guard above covers the aurora; these are the movements this file added */
@media (prefers-reduced-motion:reduce){
  .bg-vid,.ezbag .count,.wpick,#ezAdd{transition:none}
}
@media (max-width:360px){
  .ezbar{gap:6px;padding-left:12px;padding-right:12px}
  .qbtn{width:30px}
  #ezAdd{padding:0 14px;font-size:14px}
}
/* fields do not inherit the body face from the user agent — see index.html */
input,textarea,select{font-family:inherit}

/* ═══ 🧭 THE REAL HEADER, ON EVERY GENERATED PAGE — D500 ═══════════════════════════════════════
   > "Also on product pages, there is no header!? Keep same header style for all product pages"
   > — owner, 2026-09-10, with a screenshot of a /p/ page next to the home page

   These pages had a two-anchor strip — logo · Full menu · 🛍 — while the storefront had cart ·
   search · centred wordmark · 🌐 · ☰ since D402. A product page is where a search result LANDS, so
   for a first-time visitor that strip was the whole site's chrome, and it did not look like the
   site. What follows is the storefront's bar rebuilt with the same geometry and the same rules:
   three columns with the mark dead centre (D402), NOT a box — no bar, no frost, no framed buttons,
   just marks over a fade (D412) — and the ☰ opening a real sheet, which was his tap out of two
   ("A real drawer, same rows as the site") rather than a link that pretends to be one.

   ⚠️ IT IS BUILT ONCE, HERE. Both halves — this sheet and the drawer wiring in ez-page.js — are
   ONE content-hashed download shared by all ~233 pages and their /es/ twins. Putting it in the
   page generator instead would have added ~3 KB of identical markup and CSS to every page, which
   is the exact mistake D451 spent a release undoing.

   Two things differ from the storefront, both because these are separate documents:
   · the SEARCH mark is an ANCHOR to /#/search, not a button — a static page cannot open an overlay
     that lives in index.html. The route was added there in the same change so the two marks do the
     identical thing from either side of the site.
   · there is no menu FILM. The clip is a 1.6 MB decode that D405 will only allow one of at a time,
     and these pages already run the page film; the sheet takes the aurora as its ground instead. */
  :root{--ezhead-h:74px;--ezgold:#D9C08A;--ezline-2:rgba(255,255,255,.16)}
  /* the fixed bar clears the content: .wrap's own padding was 20px all round */
  .wrap{padding-top:calc(var(--ezhead-h) + 8px)}
  .ezhead{position:fixed;top:0;left:0;right:0;z-index:120;height:var(--ezhead-h);
    display:flex;align-items:center;background:transparent;padding:0;
    transition:height .3s cubic-bezier(.22,1,.36,1)}
  /* the fade runs PAST the bar so there is no edge anywhere — a gradient that stops at the bar's
     own bottom is a box with a soft floor, which is still a box (index.html:1001) */
  .ezhead::before{content:"";position:absolute;left:0;right:0;top:0;height:calc(100% + 44px);z-index:-1;pointer-events:none;
    background:linear-gradient(180deg,rgba(6,8,6,.82) 0%,rgba(6,8,6,.55) 48%,rgba(6,8,6,0) 100%);transition:opacity .3s}
  .ezhead.scrolled{height:calc(var(--ezhead-h) - 8px)}
  .ezhead.scrolled::before{background:linear-gradient(180deg,rgba(6,8,6,.94) 0%,rgba(6,8,6,.74) 48%,rgba(6,8,6,0) 100%)}
  .ezh-nav{display:grid;grid-template-columns:1fr auto 1fr;align-items:center;width:100%;max-width:860px;margin:0 auto;padding:0 20px;gap:14px}
  .ezh-left{display:flex;align-items:center;gap:10px;justify-self:start;min-width:0}
  .ezh-right{display:flex;align-items:center;gap:10px;justify-self:end;min-width:0}
  .ezh-brand{display:flex;align-items:center;justify-content:center;justify-self:center;height:50px;transition:height .3s cubic-bezier(.22,1,.36,1)}
  .ezhead.scrolled .ezh-brand{height:42px}
  .ezh-brand img{height:44px;width:auto;display:block;max-width:none}
  .ezhead.scrolled .ezh-brand img{height:38px}
  /* one bare mark, no frame — D412, and D499 for the globe that joined them */
  .ezh-icon{position:relative;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;
    width:42px;height:42px;border:0;border-radius:12px;background:transparent;color:var(--paper);cursor:pointer;
    transition:color .2s,transform .15s;-webkit-tap-highlight-color:transparent}
  .ezh-icon:hover{color:var(--green)}
  .ezh-icon:active{transform:scale(.93)}
  .ezh-icon svg{width:22px;height:22px;pointer-events:none;display:block}
  .ezh-icon .count{position:absolute;top:-4px;right:-4px;min-width:18px;height:18px;border-radius:999px;
    background:var(--green);color:var(--ink);font-family:Syne,Inter,sans-serif;font-weight:800;font-size:11px;line-height:18px;
    text-align:center;padding:0 4px;transform:scale(0);transition:transform .25s cubic-bezier(.34,1.56,.64,1)}
  .ezh-icon .count.on{transform:scale(1)}
  /* ☰ → ✕ in place, the same swap the storefront does */
  /* ⚠️ these four selectors all carry .ezh-icon on purpose. `.ezh-icon svg{display:block}` above is
     (0,1,1), so a bare `.ezh-x{display:none}` at (0,1,0) LOSES to it and both glyphs paint at once —
     which is exactly what the first build did, a ☰ and an ✕ side by side in the corner. */
  .ezh-icon .ezh-bars,.ezh-icon .ezh-x{width:22px;height:22px;display:block}
  .ezh-icon .ezh-x{display:none}
  html.ezmnav-up .ezh-icon .ezh-bars{display:none}
  html.ezmnav-up .ezh-icon .ezh-x{display:block}
  html.ezmnav-up .ezhead{z-index:140}
  html.ezmnav-up .ezhead::before{display:none}   /* the sheet's own veil is the ground */

  /* ── the sheet ──────────────────────────────────────────────────────────────────────────────── */
  .ezmnav{position:fixed;inset:0;z-index:130;visibility:hidden;
    background:linear-gradient(180deg,rgba(6,8,6,.42) 0%,rgba(6,8,6,.26) 40%,rgba(6,8,6,.36) 72%,rgba(6,8,6,.62) 100%);
    -webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);
    display:flex;flex-direction:column;align-items:center;justify-content:flex-start;
    padding:calc(var(--ezhead-h) + 26px) 20px calc(34px + env(safe-area-inset-bottom));overflow-y:auto;overscroll-behavior:contain;
    transform:translateX(100%);transition:transform .42s cubic-bezier(.22,1,.36,1),visibility 0s linear .42s}
  .ezmnav.open{transform:none;visibility:visible;transition:transform .42s cubic-bezier(.22,1,.36,1),visibility 0s}
  .ezmn-panel{position:relative;z-index:2;margin:auto 0;display:flex;flex-direction:column;align-items:center;width:100%;gap:0}
  /* one line each, centred, in the site's face — D407 as amended by D488 (Syne, not Helvetica) */
  .ezmnav a{position:relative;display:block;width:auto;max-width:100%;text-align:center;white-space:nowrap;
    font-family:Syne,Inter,sans-serif;font-weight:700;font-size:clamp(19px,6.3vw,40px);line-height:1.12;
    text-transform:uppercase;padding:8px 22px;color:var(--paper);
    text-shadow:0 1px 2px rgba(0,0,0,.5),0 4px 18px rgba(0,0,0,.4);
    transition:color .2s,transform .2s cubic-bezier(.22,1,.36,1)}
  .ezmnav a:hover,.ezmnav a:active{color:var(--green);transform:scale(1.04)}
  .ezmnav a.ezmn-call{color:var(--green)}
  .ezmnav a.ezmn-gold{color:var(--ezgold)}
  .ezmnav a.ezmn-gold:hover,.ezmnav a.ezmn-gold:active{color:var(--ezgold)}
  .ezmnav a.ezmn-lang{font-family:Syne,Inter,sans-serif;font-weight:700;font-size:13px;letter-spacing:.16em;
    text-transform:uppercase;color:var(--muted);padding:6px 18px;margin-top:6px}
  .ezmnav a.ezmn-lang:hover,.ezmnav a.ezmn-lang:active{color:var(--paper);transform:none}
  /* 🔒 LOCKED LIKE THE GATE — D414. iOS Safari scrolls straight through overflow:hidden on a touch,
     so the page under the sheet kept moving and closing it dropped him somewhere else. The body is
     pinned at top:-scrollY (--ezlock-y, set when the sheet opens) so it does not jump to the top
     under the sheet, and closing puts the scroll back to the pixel. */
  html.ezmnav-up{overscroll-behavior:none}
  html.ezmnav-up body{position:fixed;left:0;right:0;width:100%;overflow:hidden;top:var(--ezlock-y,0px)}
  @media (prefers-reduced-motion:reduce){.ezmnav{transition:none}.ezhead,.ezh-brand{transition:none}}

/* ═══ 🎬 THE LOADING SCREEN — D505 ══════════════════════════════════════════════════════════════
   > "the stuff for the product boxes should be sitting there before the customer gets there, the
   >  correct and not glitchy stuff, so a second or two loading page can make sure all those are
   >  right before customer sees errors" — owner, 2026-09-11

   Built like the storefront's old one (index.html .ez-veil-el, removed by D623 — the cross-fade
   replaced it there; this one stays): the wordmark breathing on flat ink, no spinner, no progress
   bar — a loader that reports progress it does not have is a lie with an animation. It is raised by an inline stamp in the head and dropped by ez-page.js the moment
   the catalog overlay has been applied, so every correction that pass makes — price, sale, photo,
   sold out, the weights row, the "More from" list — happens behind it rather than under his
   customer's eyes.
   NOTE the opacity/visibility pair rather than display: a display swap cannot transition, and the
   whole point is that it LEAVES softly instead of blinking off. */
  .ez-veil-el{position:fixed;inset:0;z-index:900;display:flex;align-items:center;justify-content:center;
    background:var(--ink);opacity:0;visibility:hidden;pointer-events:none;
    transition:opacity .18s ease,visibility 0s linear .18s}
  html.ez-veil .ez-veil-el{opacity:1;visibility:visible;pointer-events:auto;transition:opacity .12s ease,visibility 0s}
  .ez-veil-el img{width:132px;height:auto;opacity:.92;animation:ezVeilPulse 1.6s ease-in-out infinite}
  @keyframes ezVeilPulse{0%,100%{opacity:.45}50%{opacity:.95}}
  @media (prefers-reduced-motion:reduce){.ez-veil-el img{animation:none;opacity:.8}}
  /* the gate outranks it: two covers stacked would put a dark square over his ZIP field */
  html.gate-up .ez-veil-el{display:none}

/* ═══ 🧊 THE PRODUCT PAGE IN THE GLACIER LANGUAGE — D697–D712 (2026-10-21) ═══════════════════════
   docs/product-page-brief-2026-10-21.md, built from the v9 sketch he tapped through. His words, in order:
   D697 "For product pics lets go a different route instead of the slides … while youre scrolling more
   pics show" · D699 "Don't like the motion and the hue behind the pictures" · D700 "I like the Dark look" ·
   D701 "can we have the same videos on our product's page?" · D703/D711 Spot instead of the aura · D706
   films edge to edge · D707 the facts in the page's voice · D708 one footer everywhere · D710 the edition
   wordmarks. One stacked column, max-width 560px, phone first. The one mono voice is Fragment Mono
   (--mono) for the FIG captions, the strip, the bands, the callout labels and the spec labels — 10–11px
   uppercase, +.14–.2em tracking, --muted-2. Everything else is SYNE — the site's ONE body face
   (2026-09-09, typeface-test's FACE_OK): headings 800, body and lead inherited from `body`. The brief's
   "Inter" for the lead, the brand line and the spec values was corrected to Syne by the hub on
   2026-10-21; Fragment Mono is the only face this page adds.
   ⚠️ NO `filter` IN THIS SECTION, EVER (D711 + the 2026-10-20 iOS crash): Spot is two gradients. */
  .wrap.pdp{max-width:560px;padding-left:16px;padding-right:16px;padding-bottom:0}
  .pdp h1{font-size:clamp(30px,9vw,40px);line-height:1.02;letter-spacing:-.01em;margin:0 0 8px}
  /* ✂️ the brand line `PUFFCO · DEVICES` (.pdp .eyebrow) is no longer printed on the product page — owner, 2026-10-21, "delete
     everything I have circled around"; the identity block is the mark, the badge row and the name, so its three rules went too */
  .ident{margin:18px 0 0}
  .ident .pbadges{margin:0 0 8px}
  /* the edition wordmark (D710): a white PNG, 22px high, in the brand mark's place; the brand mark stays
     printed (hidden) so a colour tap onto a sibling without an edition can bring it back */
  .edmark{display:block;height:22px;width:auto;max-width:60%;object-fit:contain;object-position:left center;margin:0 0 12px}
  .edmark[hidden]{display:none}
  .ident .brand-mark-sm{margin:0 0 12px}
  .lead{color:var(--muted);font-size:15.5px;line-height:1.55;margin:0 0 14px}
  .pdp .meta{margin:0 0 6px}
  .pdp .potency{margin:0 0 12px}
  .pdp .sub{font-size:15.5px;line-height:1.55;color:var(--muted);margin:0 0 14px;max-width:none}
  .ident .ezweights{margin:14px 0 4px}
  .pdp .ezweights.colors{margin:10px 0 6px}
  .pdp .swcap{margin:0 0 2px}

/* ── SPOT (D711) — the only thing behind a picture: a pool of neutral light behind the product (::before)
   and a lit floor under it (::after), gradients only, z-index 0 under the picture. Films get neither.
   On every .pic: the hero still, the diagram, the beat picture, the figures' frames, the rail minis. */
  .pic{position:relative;display:flex;align-items:center;justify-content:center;margin:0}
  .pic>img{position:relative;z-index:1;height:auto;max-width:100%}
  .pic::before,.pic::after{content:"";position:absolute;pointer-events:none;z-index:0}
  .pic::before{left:8%;right:8%;top:4%;bottom:4%;border-radius:50%;
    background:radial-gradient(ellipse at 50% 52%,rgba(255,255,255,.11),rgba(255,255,255,.045) 42%,transparent 70%)}
  .pic::after{left:18%;right:18%;bottom:5%;height:9%;border-radius:50%;
    background:radial-gradient(ellipse at center,rgba(255,255,255,.11),rgba(255,255,255,.03) 55%,transparent 72%)}
  .pic.vid::before,.pic.vid::after{display:none}
  /* the hero still: width min(78vw,360px) on Spot — #ezMedia / #ezPhoto keep their names for ringfit/photobox */
  .pic.hero{padding:4vh 0 3vh;min-height:38vh}
  .pic.hero>img{width:min(78vw,360px)}
  .pic.hero .pdp-photo{display:block}
/* ── THE FILMS (D701/D706): `muted autoplay loop playsinline preload=metadata` with a poster, the aspect from
   the row's w/h (inline), EDGE TO EDGE on phones — margin 0 −16px against .wrap.pdp's 16px gutter, square
   corners — and inset with an 18px radius from 560px up. object-fit:cover inside the aspect box. */
  .pic.vid{display:block;margin:0 -16px}
  .pic video{display:block;width:100%;aspect-ratio:1/1;border-radius:0;background:#000;position:relative;z-index:1;object-fit:cover}
  .pic.hero.vid{padding:0;min-height:0}
  .pic.hero video{aspect-ratio:9/16}
  .pic.wide video{aspect-ratio:16/9}
  .pic.light,.pic.strip{margin-top:26px}
  .pic.wide{margin-top:26px}
  @media (min-width:560px){ .pic.vid{margin-left:0;margin-right:0} .pic video{border-radius:18px} }
/* ── THE FIG CAPTION ROW: `FIG 1 · <caption>` left, `1 of N` right */
  .fig{display:flex;justify-content:space-between;align-items:baseline;gap:12px;font:400 10.5px/1.4 var(--mono);
    letter-spacing:.14em;text-transform:uppercase;color:var(--muted-2);margin:8px 0 0}
  /* ✂️ the right-hand `n of N` (.fig-r) is gone — owner, 2026-10-21; a row reads `FIG n · caption`, left */
  .fig .fig-l{min-width:0}
/* ── THE TECH STRIP (block 3): `PUFFCO —⊕— PEAK PRO 3DXL · GLACIER · LIMITED EDITION`, centred, hairlines,
   one line that never wraps (.16em so it fits 390px) */
  .strip{font:400 9.5px/1 var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--muted-2);text-align:center;
    display:flex;justify-content:center;white-space:nowrap;overflow:hidden;padding:10px 0;margin:22px 0 0;border-top:1px solid var(--line);border-bottom:1px solid var(--line)}
  /* measured at 390: "Puffco —⊕— Peak Pro 3DXL · Glacier · Limited edition" is 388px at 9.5px/.16em against a 358px line,
     the Spanish twin 395 — so under 430px the strip tightens to 9px/.12em (≈337 / 350) and still reads as one inscription;
     flex centring means a line that still overflows loses its two ENDS evenly, never its last word alone */
  @media (max-width:430px){ .strip{font-size:9px;letter-spacing:.12em} }
/* ── A BAND: `⊕ DESIGN DETAILS ⊕` — mono 13px, .2em, --accent; the ⊕ are the pseudo-elements */
  .band{display:flex;align-items:center;gap:10px;margin:30px 0 14px}
  .band::before,.band::after{content:"⊕";font:400 11px/1 var(--mono);color:var(--muted-2);opacity:.7}
  .band h2{flex:1;text-align:center;margin:0;font:400 13px/1.2 var(--mono);letter-spacing:.2em;text-transform:uppercase;color:var(--accent)}
  .band[hidden]{display:none}
/* ── DESIGN DETAILS (block 4): a 3:4 box, the picture object-fit:contain, an SVG overlay in a 300×400 viewBox
   (preserveAspectRatio none, so the units ARE the box) — one leader line per callout, a 4×4 square at the
   label end, a ring r=6 + dot r=1.6 at the product end, stroke currentColor — and the labels as absolutely
   positioned boxes (--box ground, mono 9.5px uppercase, max-width 44%, a hairline by box-shadow). The SVG and
   the labels sit above the picture; the labels above the lines, so a line leaves from under its box. */
  .diagram{position:relative;display:block;aspect-ratio:3/4;margin:0 auto;max-width:420px;color:var(--fg)}
  .diagram>img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;z-index:1}
  .diagram svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible;z-index:2}
  .diagram svg line{stroke:currentColor;stroke-width:.9;opacity:.55}
  .diagram svg rect{fill:currentColor;opacity:.8}
  .diagram svg circle.ring{fill:none;stroke:currentColor;stroke-width:1.1;opacity:.9}
  .diagram svg circle.dot{fill:currentColor}
  .lab{position:absolute;z-index:3;background:rgba(243,246,242,.08);color:var(--fg);font:400 9.5px/1.35 var(--mono);
    letter-spacing:.12em;text-transform:uppercase;padding:7px 9px;max-width:44%;text-align:center;box-shadow:0 0 0 1px var(--line)}
/* ── THE FILM BEAT (block 5): the loop, its FIG row, then title (Syne 800 28px) + text (15.5px --muted, the body face);
   without a film, the next picture on Spot beside the next paragraph */
  .beat-t{font:800 28px/1.1 Syne,Inter,sans-serif;margin:18px 0 8px;letter-spacing:-.01em}
  .beat-x{color:var(--muted);font-size:15.5px;line-height:1.55;margin:0 0 6px}
  .beat{margin:26px 0 0}
  .beat .pic.beat-pic{padding:3vh 0}
  .beat .pic.beat-pic>img{width:min(70vw,330px)}
  .beat .sub{margin:14px 0 0}
/* ── THE LIST (block 6): the benefits as native <details> rows — `+ TITLE` in Syne 800, --muted-2, the open row --fg and `−`;
   the panel a 118×157 picture + bold lead + 13.5px. ✂️ The statement above it (one line in giant type) is gone — owner,
   2026-10-21, "delete everything I have circled around" — so the list's first row takes the gap the statement used to give it. */
  details.plus{border-top:1px solid var(--line)}
  details.plus:first-of-type{margin-top:28px}
  details.plus:last-of-type{border-bottom:1px solid var(--line)}
  details.plus summary{list-style:none;cursor:pointer;font:800 clamp(24px,7.6vw,34px)/1 Syne,Inter,sans-serif;text-transform:uppercase;
    letter-spacing:-.01em;padding:12px 0;color:var(--muted-2);display:flex;align-items:baseline;gap:10px}
  details.plus summary::-webkit-details-marker{display:none}
  details.plus summary::before{content:"+";font:400 15px/1 var(--mono);color:var(--accent);flex:0 0 14px}
  details.plus[open] summary{color:var(--fg)}
  details.plus[open] summary::before{content:"−"}
  .panel{display:grid;grid-template-columns:118px 1fr;gap:14px;padding:2px 0 16px;align-items:start}
  .panel img{width:118px;height:157px;object-fit:contain;display:block}
  .panel b{display:block;font:700 13.5px/1.3 Syne,Inter,system-ui,sans-serif;margin:0 0 6px;color:var(--fg)}
  .panel p{margin:0;font-size:13.5px;line-height:1.5;color:var(--muted)}
/* ── 🎚 THE SCROLL SWAP (block 6b — D715, 2026-10-21, the brief's §5): the animation he remembered from Puffco's July 2026 Plasma
   launch page ("The Features" — four close-ups swapping as the page scrolls, docs/glacier-design-notes-2026-10-20.md §8a).
   TWO STATES, ONE MARKUP. As printed — no script, a 404'd runtime, an engine without IntersectionObserver or sticky — `.seq` is a
   plain COLUMN: each step's picture (contained, at most 60vh tall) with its label (the mono, the page's --accent) and its text (the
   body face, --muted) under it, every one visible; the `1 / n` mark is hidden because it would count nothing. assets/ez-page.js
   section J adds `.live`, and only then: the section is the stage's height plus one 70vh step per item (`--seq-n`, inline from
   the generator), the stage sticks just under the condensed header (66px) and fills the screen down to the buy bar, the steps
   stack in ONE grid cell (one picture area, each step's caption under its own picture, so a picture and its words swap together),
   and only `.on` is at opacity 1 — a .5s crossfade, OPACITY ONLY (no transform anywhere on these pictures, no filter on this
   page). Reduce Motion: the swap is instant (no transition) and still follows the scroll.
   ⚠️ STICKY DIES SILENTLY UNDER A CLIPPING ANCESTOR (the .hero leak of the same morning was this family): the stage's ancestors
   are .seq → #pcard → .wrap.pdp → body → html, and none of them may carry an overflow other than visible. The section is NOT `.in`
   for the same reason in another shape — the fade-up's translateY would sit on the whole tall stage. */
  .seq{--seq-n:4;--seq-stage:calc(100vh - var(--ezhead-h) - 56px - env(safe-area-inset-bottom,0px));position:relative;margin:0}
  @supports (height:100svh){ .seq{--seq-stage:calc(100svh - var(--ezhead-h) - 56px - env(safe-area-inset-bottom,0px))} }
  .seq-it{margin:0 0 30px}
  .seq-pic{height:60vh;height:min(60vh,600px)}
  .seq-pic>img{display:block;width:100%;height:100%;object-fit:contain}
  .seq-it figcaption{display:block;text-align:center;max-width:32em;margin:14px auto 0}
  .seq-l{display:block;font:400 11px/1.4 var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--accent);margin:0 0 6px}
  .seq-x{display:block;color:var(--muted);font-size:15px;line-height:1.5}
  .seq-n{display:none;margin:14px 0 0;text-align:center;font:400 10.5px/1 var(--mono);letter-spacing:.2em;color:var(--muted-2)}
  .seq.live{height:calc(var(--seq-stage) + var(--seq-n) * 70vh)}
  .seq.live .seq-stage{position:-webkit-sticky;position:sticky;top:calc(var(--ezhead-h) - 8px);height:var(--seq-stage);
    display:flex;flex-direction:column;justify-content:center}
  .seq.live .seq-items{display:grid}
  .seq.live .seq-it{grid-area:1/1;margin:0;opacity:0;transition:opacity .5s ease;pointer-events:none}
  .seq.live .seq-it.on{opacity:1;pointer-events:auto}
  .seq.live .seq-pic{height:min(60vh,600px,var(--seq-stage) - 150px)}
  .seq.live .seq-n{display:block}
  @media (prefers-reduced-motion:reduce){ .seq.live .seq-it{transition:none} }
/* ── THE FIGURES (block 7): the remaining pictures as a horizontal strip of rounded 3:4 frames (flex 0 0 68%,
   --box ground, a hairline), `FIG n · <caption>` under each; a finger scrolls it, snap on */
  .figs{display:flex;gap:10px;overflow-x:auto;margin:14px -16px 0;padding:2px 16px 6px;scroll-snap-type:x mandatory;
    scrollbar-width:none;-webkit-overflow-scrolling:touch}
  .figs::-webkit-scrollbar{display:none}
  .figs figure{flex:0 0 68%;margin:0;scroll-snap-align:start}
  .figs .frame{position:relative;aspect-ratio:3/4;border-radius:18px;background:var(--box);display:flex;align-items:center;
    justify-content:center;overflow:hidden;box-shadow:0 0 0 1px var(--line)}
  .figs .frame>img{width:82%;height:auto}
  .figs figcaption{font:400 10px/1.4 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--muted-2);margin:8px 2px 0}
  /* 📷 A SCENE (D716, "All six, as figures"): a photograph FILLS its rounded 3:4 frame — object-fit:cover, edge to edge of the
     frame — and stands on no Spot: the generator prints it `.frame.scene`, not `.frame.pic`, so the two gradient pseudo-elements
     never exist on it ("a photograph brings its own light"). Numbered and captioned like any other figure. The beat's picture, when
     it is a scene, takes the same frame at the beat picture's width. */
  .figs .frame.scene>img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
  .beat .beat-pic.scene{position:relative;width:min(70vw,330px);aspect-ratio:3/4;margin:3vh auto;border-radius:18px;overflow:hidden;
    background:var(--box);box-shadow:0 0 0 1px var(--line)}
  .beat .beat-pic.scene>img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
/* ── THE FACTS (block 9, D707): the four promises as a 2×2 mono grid with hairlines, then the spec sheet —
   label above value, hairlines, mono label --muted-2, value 15px --fg in the body face. No green. */
  .facts{display:grid;grid-template-columns:1fr 1fr;border-top:1px solid var(--line);border-left:1px solid var(--line);margin:0 0 22px}
  .facts div{padding:11px 12px;border-right:1px solid var(--line);border-bottom:1px solid var(--line);font:400 10px/1.55 var(--mono);
    letter-spacing:.14em;text-transform:uppercase;color:var(--muted-2)}
  .facts b{display:block;font-weight:400;color:var(--fg)}
  .specs{border-top:1px solid var(--line);margin:0 0 6px}
  .specs div{display:block;padding:12px 0 13px;border-bottom:1px solid var(--line)}
  .specs dt{font:400 10px/1.5 var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--muted-2);margin:0 0 4px}
  .specs dd{margin:0;color:var(--fg);font-size:15px;line-height:1.5}
  .pdp #prating .rating{margin:12px 0 0}
  /* the rail bleeds to the 16px gutter here (it was built for .wrap's 18px) */
  .pdp .sibrail{margin:10px -16px 0;padding:2px 16px 6px}
/* ── THE ONE MOTION (D697, his tap: a soft fade-up as each piece arrives): .in → .on once, via
   IntersectionObserver in ez-page.js, opacity 0→1 and translateY(16px→0), .7s/.9s cubic-bezier(.22,1,.36,1).
   ⚠️ IT HIDES NOTHING UNTIL THE RUNTIME SAYS SO: the hidden state is scoped under html.ezmo, which ez-page.js
   adds AFTER marking everything already in view as .on — so a page with no script (a 404'd asset, scripts off)
   is simply visible, and the first paint never waits. The hero and identity blocks are never .in. */
  html.ezmo .in{opacity:0;transform:translateY(16px);transition:opacity .7s cubic-bezier(.22,1,.36,1),transform .9s cubic-bezier(.22,1,.36,1)}
  html.ezmo .in.on{opacity:1;transform:none}
  @media (prefers-reduced-motion:reduce){ html.ezmo .in{opacity:1;transform:none;transition:none} }

/* ═══ 🃏 THE BRAND TILE — "MORE FROM <BRAND>" (D719, 2026-10-21) ════════════════════════════════════════════════════════
   > "the more from puffco, you can't see the products, have the same sliding brand style from home page instead on this part,
   >  on all products pages" — his tap "The brand's tile (Recommended)"
   index.html's "Shop by brand" tile (.brand-tile, ~L1722–1776, and its touch walk ~L2398–2452), PORTED at the home's 390px
   measurements: the tinted window with the brand's glow on top, the logo plate (64px) and the product count, the label ·
   categories caption, the 130px art row, "Enter <brand> →". build-products.js brandTileOf() prints it; assets/ez-page.js
   section K raises its two flags. Three things are not the home's, on purpose:
     · NO FILTER anywhere (D711 — this page renders zero filter layers, ioslayers-test): the home's drop-shadows under each photo
       and around the logo are not ported; the glows are gradients, as the home's own are since 2026-10-20.
     · TWO STATES. As printed (no script, no IntersectionObserver) the art row is THREE products standing still,
       side by side, each a link — the home's desktop fan without its overlap (its wings tuck ±38px behind the centre, sized for
       its 74px drawings, and a narrow photo like a Puffco device disappears behind its neighbour: a still state that must SHOW
       its products cannot hide two of three). `.orbit` (the runtime) turns it into the home's TOUCH walk — every product stacked in
       one cell, walking off-right → right wing → centre → left wing → off-left, one step every PA_T = 1.55s, phased by the inline
       animation-delay build-products prints — and `.live` (the observer: on screen or within 30% of it, like the home's
       liveWatch) is the only state in which it moves. Off screen it holds still.
     · THE PRODUCTS ARE LINKS and the tile is a link: each .pa is an <a> to its page, above a stretched "Enter <brand> →" that
       covers the rest of the tile. The walk carries `visibility` with its opacity, so a product parked off-stage (opacity 0)
       can never catch a tap meant for the one in front of it. */
  .brand-tile{--bc:#5BE584;--bc-2:#3FBF63;display:block;position:relative;isolation:isolate;overflow:hidden;margin:0;
    border-radius:var(--radius-lg);color:var(--paper);
    background:linear-gradient(165deg,color-mix(in srgb,var(--bc) 20%,transparent),rgba(10,13,10,.52));
    border:1px solid color-mix(in srgb,var(--bc) 34%,var(--line))}
  .brand-tile::before{content:"";position:absolute;inset:0;z-index:0;opacity:.30;pointer-events:none;
    background:radial-gradient(120% 90% at 50% 0%,var(--bc-2),transparent 60%)}
  .brand-tile .bt-defs{position:absolute;width:0;height:0;overflow:hidden}
  .bt-top{position:relative;z-index:2;padding:24px 24px 0;display:flex;align-items:flex-start;justify-content:space-between;pointer-events:none}
  .brand-tile .brand-logo-plate{display:inline-flex;flex-direction:column;gap:3px;align-items:center;justify-content:center;min-width:116px;
    border-radius:12px;background:radial-gradient(60% 66% at 50% 50%,color-mix(in srgb,var(--lg,#5BE584) 26%,transparent),transparent 72%)}
  .brand-tile .bt-logo{height:64px;padding:8px 10px;max-width:170px}
  .brand-tile .bt-logo img{display:block;max-height:48px;max-width:150px;object-fit:contain}
  .brand-tile .blp-fallback{display:none;font-family:Syne,sans-serif;font-weight:800;font-size:17px;color:#f3f6f2;letter-spacing:.01em;
    white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}
  .bt-count{font-size:10.5px;line-height:1.6;color:var(--muted);border:1px solid var(--line-strong);border-radius:999px;padding:4px 10px;white-space:nowrap}
  .bt-tag{position:relative;z-index:2;padding:7px 24px 0;color:var(--muted-2);font-size:9.5px;font-weight:600;letter-spacing:.16em;
    text-transform:uppercase;line-height:1.6;pointer-events:none}
  .bt-art{position:relative;z-index:2;height:130px;margin-top:8px;display:flex;align-items:flex-end;justify-content:center;gap:10px;pointer-events:none}
  .bt-art::before{content:"";position:absolute;left:50%;top:50%;width:180px;height:180px;transform:translate(-50%,-50%);border-radius:50%;opacity:.55;
    pointer-events:none;background:radial-gradient(circle,color-mix(in srgb,var(--bc) 32%,transparent) 0%,color-mix(in srgb,var(--bc) 18%,transparent) 34%,transparent 68%)}
  /* as printed — three products, side by side, the wings tilted like the home's fan, nothing hidden behind anything */
  .bt-art .pa{position:relative;display:block;z-index:1;pointer-events:auto;-webkit-tap-highlight-color:transparent}
  .bt-art .pa.a{transform:rotate(-6deg) scale(.92);transform-origin:50% 100%;z-index:1}
  .bt-art .pa.b{transform:scale(1.04);transform-origin:50% 100%;z-index:3}
  .bt-art .pa.c{transform:rotate(6deg) scale(.92);transform-origin:50% 100%;z-index:1}
  .bt-art .pa:nth-child(n+4){display:none}
  .bt-art .pa .pa-photo{display:block;height:108px;width:auto;max-width:96px;object-fit:contain}
  .bt-art .pa svg{display:block;width:74px;height:100px}
  /* the walk (.orbit, from the runtime) — the home's touch showcase: one cell, a halo in the brand's colour riding each product */
  .brand-tile.orbit .bt-art{display:grid;place-items:center}
  .brand-tile.orbit .bt-art .pa{grid-area:1/1;opacity:0;visibility:hidden;transform:none;transform-origin:50% 50%;z-index:1}
  .brand-tile.orbit .bt-art .pa:nth-child(n+4){display:block}
  .brand-tile.orbit .bt-art .pa::before{content:"";position:absolute;left:50%;top:50%;width:260px;height:260px;transform:translate(-50%,-50%);
    border-radius:50%;z-index:-1;pointer-events:none;
    background:radial-gradient(circle,color-mix(in srgb,var(--bc) 90%,transparent) 0%,color-mix(in srgb,var(--bc) 62%,transparent) 18%,color-mix(in srgb,var(--bc) 30%,transparent) 44%,transparent 68%)}
  .brand-tile.orbit .bt-art .pa .pa-photo{height:112px;max-width:132px}
  .brand-tile.orbit .bt-art .pa svg{width:80px;height:108px}
  .brand-tile.orbit.live .bt-art[data-n] .pa{animation:ezOrb8 12.4s cubic-bezier(.4,.05,.15,1) infinite}
  .brand-tile.orbit.live .bt-art[data-n="4"] .pa{animation-name:ezOrb4;animation-duration:6.2s}
  .brand-tile.orbit.live .bt-art[data-n="5"] .pa{animation-name:ezOrb5;animation-duration:7.75s}
  .brand-tile.orbit.live .bt-art[data-n="6"] .pa{animation-name:ezOrb6;animation-duration:9.3s}
  .brand-tile.orbit.live .bt-art[data-n="7"] .pa{animation-name:ezOrb7;animation-duration:10.85s}
  /* "Enter <brand> →" — the link to the brand's page, stretched over the whole tile UNDER the products (z 1 < the art row's 2) */
  .brand-tile .bt-cta-row{position:static;padding:0 24px 22px}
  .brand-tile .link-arrow{display:inline-flex;align-items:center;gap:8px;font-weight:600;font-size:14px;padding:7px 0;text-decoration:none}
  .brand-tile .bt-go::after{content:"";position:absolute;inset:0;z-index:1}
  .brand-tile .bt-go svg{flex:none}
  /* Reduce Motion: the walk keeps going, like the home's tile (index.html's `reduce-disabled` block — D019, "force animations even
     under reduced-motion, EVERYWHERE"); the never-matching query is kept so the rule reads the same on both surfaces */
  @media (prefers-reduced-motion:reduce-disabled){
    .brand-tile.orbit .bt-art .pa{animation:none!important}
    .brand-tile.orbit .bt-art .pa:first-child{opacity:1;visibility:visible}
  }
/* the walks, per N (index.html's paOrb4–8, stations and timing verbatim — step S = 100/N % of the cycle, glide .74 S, hold the rest;
   N × PA_T seconds a cycle), plus the visibility that keeps a parked product out of the way of a tap */
@keyframes ezOrb4{0%{opacity:0;visibility:hidden;transform:translateX(150px) scale(.5);z-index:1}18.5%{opacity:.4;visibility:visible;transform:translateX(96px) scale(.6);z-index:2}25%{opacity:.4;visibility:visible;transform:translateX(96px) scale(.6);z-index:2}43.5%{opacity:1;visibility:visible;transform:translateX(0) scale(1);z-index:3}50%{opacity:1;visibility:visible;transform:translateX(0) scale(1);z-index:3}68.5%{opacity:.4;visibility:visible;transform:translateX(-96px) scale(.6);z-index:2}75%{opacity:.4;visibility:visible;transform:translateX(-96px) scale(.6);z-index:2}93.5%,100%{opacity:0;visibility:hidden;transform:translateX(-150px) scale(.5);z-index:1}}
@keyframes ezOrb5{0%{opacity:0;visibility:hidden;transform:translateX(150px) scale(.5);z-index:1}14.8%{opacity:.4;visibility:visible;transform:translateX(96px) scale(.6);z-index:2}20%{opacity:.4;visibility:visible;transform:translateX(96px) scale(.6);z-index:2}34.8%{opacity:1;visibility:visible;transform:translateX(0) scale(1);z-index:3}40%{opacity:1;visibility:visible;transform:translateX(0) scale(1);z-index:3}54.8%{opacity:.4;visibility:visible;transform:translateX(-96px) scale(.6);z-index:2}60%{opacity:.4;visibility:visible;transform:translateX(-96px) scale(.6);z-index:2}74.8%,100%{opacity:0;visibility:hidden;transform:translateX(-150px) scale(.5);z-index:1}}
@keyframes ezOrb6{0%{opacity:0;visibility:hidden;transform:translateX(150px) scale(.5);z-index:1}12.33%{opacity:.4;visibility:visible;transform:translateX(96px) scale(.6);z-index:2}16.67%{opacity:.4;visibility:visible;transform:translateX(96px) scale(.6);z-index:2}29%{opacity:1;visibility:visible;transform:translateX(0) scale(1);z-index:3}33.33%{opacity:1;visibility:visible;transform:translateX(0) scale(1);z-index:3}45.67%{opacity:.4;visibility:visible;transform:translateX(-96px) scale(.6);z-index:2}50%{opacity:.4;visibility:visible;transform:translateX(-96px) scale(.6);z-index:2}62.33%,100%{opacity:0;visibility:hidden;transform:translateX(-150px) scale(.5);z-index:1}}
@keyframes ezOrb7{0%{opacity:0;visibility:hidden;transform:translateX(150px) scale(.5);z-index:1}10.57%{opacity:.4;visibility:visible;transform:translateX(96px) scale(.6);z-index:2}14.29%{opacity:.4;visibility:visible;transform:translateX(96px) scale(.6);z-index:2}24.86%{opacity:1;visibility:visible;transform:translateX(0) scale(1);z-index:3}28.57%{opacity:1;visibility:visible;transform:translateX(0) scale(1);z-index:3}39.14%{opacity:.4;visibility:visible;transform:translateX(-96px) scale(.6);z-index:2}42.86%{opacity:.4;visibility:visible;transform:translateX(-96px) scale(.6);z-index:2}53.43%,100%{opacity:0;visibility:hidden;transform:translateX(-150px) scale(.5);z-index:1}}
@keyframes ezOrb8{0%{opacity:0;visibility:hidden;transform:translateX(150px) scale(.5);z-index:1}9.25%{opacity:.4;visibility:visible;transform:translateX(96px) scale(.6);z-index:2}12.5%{opacity:.4;visibility:visible;transform:translateX(96px) scale(.6);z-index:2}21.75%{opacity:1;visibility:visible;transform:translateX(0) scale(1);z-index:3}25%{opacity:1;visibility:visible;transform:translateX(0) scale(1);z-index:3}34.25%{opacity:.4;visibility:visible;transform:translateX(-96px) scale(.6);z-index:2}37.5%{opacity:.4;visibility:visible;transform:translateX(-96px) scale(.6);z-index:2}46.75%,100%{opacity:0;visibility:hidden;transform:translateX(-150px) scale(.5);z-index:1}}

/* ═══ 🦶 THE FOOTER — D708: "the footer always stays the same everywhere" ═══════════════════════════════
   index.html's footer (index.html:2271–2311, the 2026-09-06 build) ported rule for rule onto this sheet's
   tokens, on every /p/ and /b/ page (build-products.js siteFooter). The pulse on the Open-now dot is the
   home's own and stays (reduced motion stops it). .hasbar = a product page: its bottom clears the buy bar.
   The face is the page's — Syne, inherited from body — not the SPA's Inter: one body face on the generated
   pages (typeface-test), the markup verbatim. */
  .ezfoot{position:relative;margin-top:40px;min-height:100vh;min-height:100svh;overflow:hidden;display:flex;flex-direction:column;
    justify-content:center;padding:clamp(40px,8vh,88px) 0 calc(30px + env(safe-area-inset-bottom));background:transparent;color:var(--paper)}
  .ezfoot.hasbar{padding-bottom:calc(104px + env(safe-area-inset-bottom))}
  .ezfoot .ft-fx{position:absolute;inset:0;z-index:0;pointer-events:none;
    background:linear-gradient(180deg,transparent 0%,rgba(10,13,10,.30) 22%,rgba(10,13,10,.56) 60%,rgba(10,13,10,.76) 100%)}
  .ezfoot .container{position:relative;z-index:1;width:100%;max-width:1240px;margin:0 auto;padding:0 28px;display:flex;flex-direction:column;align-items:center;text-align:center}
  .ezfoot .warn{width:100%;max-width:720px;text-align:left;background:rgba(10,13,10,.42);border:1px solid rgba(255,255,255,.10);border-radius:14px;padding:14px 18px;margin:0 0 40px;
    color:var(--muted);font-size:11px;line-height:1.6;display:flex;gap:12px}
  .ezfoot .warn .wbadge{flex-shrink:0;font-weight:800;font-family:Syne,Inter,sans-serif;color:#ffcf6b;border:1px solid rgba(255,207,107,.3);border-radius:8px;padding:2px 8px;height:fit-content;font-size:11px;letter-spacing:.08em;white-space:nowrap}
  .ezfoot .foot-brand{display:flex;flex-direction:column;align-items:center}
  .ezfoot .foot-brand p{color:var(--muted);font-size:11.5px;line-height:1.6;letter-spacing:.01em;max-width:400px;margin:0 auto 16px}
  .ezfoot .foot-pills{display:flex;gap:10px;justify-content:center;flex-wrap:wrap}
  .ezfoot .pill{display:inline-flex;align-items:center;gap:7px;font-size:11px;color:var(--muted);border:1px solid var(--line);border-radius:999px;padding:7px 12px}
  .ezfoot .pill .dot{width:7px;height:7px;border-radius:50%;background:var(--green-bright);box-shadow:0 0 0 0 var(--green-glow);animation:ezPulse 2.2s infinite}
  @keyframes ezPulse{0%{box-shadow:0 0 0 0 var(--green-glow)}70%{box-shadow:0 0 0 8px rgba(63,191,99,0)}100%{box-shadow:0 0 0 0 rgba(63,191,99,0)}}
  .ezfoot .foot-join{margin:40px 0 0;display:flex;flex-direction:column;align-items:center;gap:18px}
  .ezfoot .foot-join h3{margin:0;font-family:Syne,'Helvetica Neue',Inter,sans-serif;font-weight:800;font-size:clamp(19px,5vw,26px);line-height:1.05;letter-spacing:.06em;text-transform:uppercase;color:var(--white)}
  .ezfoot .fj-btn{display:inline-flex;align-items:center;gap:14px;background:#fff;color:#0a0c0a;border-radius:999px;padding:17px 30px;
    font-family:Syne,Inter,sans-serif;font-weight:700;font-size:12.5px;line-height:1;letter-spacing:.1em;text-transform:uppercase;text-decoration:none;cursor:pointer;
    box-shadow:0 20px 48px -18px rgba(0,0,0,.85);transition:transform .2s var(--ease-out),box-shadow .2s}
  .ezfoot .fj-btn:hover{transform:translateY(-2px);box-shadow:0 26px 56px -18px rgba(0,0,0,.9)}
  .ezfoot .fj-btn:active{transform:scale(.98)}
  .ezfoot .fj-btn svg{width:24px;height:14px;flex:none}
  .ezfoot .foot-social{margin:36px 0 0;display:flex;flex-direction:column;align-items:center}
  .ezfoot .fs-row{display:flex;gap:14px}
  .ezfoot .fs-btn{width:54px;height:54px;border-radius:50%;background:rgba(10,13,10,.72);border:1px solid rgba(255,255,255,.16);display:inline-flex;align-items:center;justify-content:center;color:var(--white);
    transition:transform .2s var(--ease-out),border-color .2s,background .2s}
  .ezfoot .fs-btn:hover{transform:translateY(-2px);border-color:var(--green-bright);background:rgba(10,13,10,.9)}
  .ezfoot .fs-btn svg{width:22px;height:22px}
  .ezfoot .foot-links{margin:42px 0 0;display:flex;flex-wrap:wrap;justify-content:center;gap:4px 24px}
  .ezfoot .foot-links a{font-family:Syne,Inter,sans-serif;font-weight:600;font-size:11.5px;letter-spacing:.16em;text-transform:uppercase;color:rgba(255,255,255,.72);padding:8px 2px;cursor:pointer;transition:color .2s}
  .ezfoot .foot-links a:hover{color:var(--green-bright)}
  .ezfoot .foot-links.sub{margin-top:2px}
  .ezfoot .foot-links.sub a{font-size:10px;color:var(--muted-2);letter-spacing:.12em}
  .ezfoot .foot-hours{margin:22px 0 0;color:var(--muted-2);font-size:10.5px;letter-spacing:.06em}
  .ezfoot .foot-hours span{white-space:nowrap}
  .ezfoot .foot-bottom{margin:28px 0 0;padding:22px 0 0;border-top:1px solid rgba(255,255,255,.12);display:flex;flex-direction:column;align-items:center;gap:12px;width:100%;max-width:720px}
  .ezfoot .foot-bottom p{color:var(--muted-3);font-size:10px;margin:0;line-height:1.6;letter-spacing:.02em}
  .ezfoot .foot-lic{font-size:10px;color:var(--muted-3);line-height:1.65;letter-spacing:.025em}
  /* the language row: the SPA's toggle button, here a link to the twin page, in the button's own dress */
  .ezfoot .foot-lang{display:inline-block;background:transparent;border:1px solid var(--line-strong);border-radius:999px;color:var(--muted);
    font:600 12.5px Syne,Inter,sans-serif;padding:8px 14px;text-decoration:none}
  .ezfoot .foot-lang:hover{color:var(--paper)}
  @media (max-width:640px){
    .ezfoot .foot-brand p{font-size:13.5px;margin:0 auto 12px}
    .ezfoot .foot-join{margin-top:32px}
    .ezfoot .foot-social{margin-top:30px}
    .ezfoot .foot-links{margin-top:34px;gap:2px 18px}
    .ezfoot .foot-bottom{margin-top:22px;padding-top:18px;gap:10px}
    .ezfoot .warn{padding:11px 13px;font-size:11px;gap:9px;margin:0 0 28px}
  }
  @media (prefers-reduced-motion:reduce){ .ezfoot .pill .dot{animation:none} .ezfoot .fj-btn,.ezfoot .fs-btn{transition:none} }
