/* ═══ 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.

   ⚠️ 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;background-image:radial-gradient(ellipse 90% 55% at 18% -5%,rgba(36,92,60,.5),transparent 60%)}
/* 🌌 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:"";position:fixed;inset:0;z-index:-1;pointer-events:none}
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))}
  .brand-mark-sm{display:block;max-height:34px;width:auto;object-fit:contain;margin:24px 0 -18px;filter:drop-shadow(0 0 10px rgba(91,229,132,.3))}
  /* 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:9px;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 */
  .prod{min-width:0;display:flex;align-items:center;gap:13px;padding:11px 14px;border-radius:15px;
    border:1px solid var(--line);background:radial-gradient(90% 100% at 8% 50%,color-mix(in srgb,var(--pg,transparent) 10%,transparent),transparent 55%),linear-gradient(160deg,var(--panel),var(--panel2));color:var(--paper)}
  .prod img,.prod-noimg{width:56px;height:56px;flex:0 0 auto;border-radius:11px;object-fit:cover;background:rgba(255,255,255,.05)}
  .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}
  .sibcard{flex:0 0 auto;width:196px;display:flex;align-items:center;gap:11px;text-align:left;
    background:var(--panel-2,rgba(255,255,255,.04));border:1px solid var(--line);border-radius:14px;
    padding:9px 11px 9px 9px;scroll-snap-align:start;transition:transform .18s ease,border-color .18s}
  .sibcard:hover{transform:translateY(-3px);border-color:var(--green)}
  .sib-media{flex:0 0 auto;width:54px;height:54px;border-radius:11px;display:flex;align-items:center;
    justify-content:center;overflow:hidden;background:radial-gradient(110% 90% at 50% 30%,rgba(91,229,132,.22),transparent 70%),rgba(255,255,255,.03)}
  .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} }

/* ═══ 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 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. ALWAYS a square (D442: "lets make sure they are all in the same sized
   squares"). Two modes and no third: a PHOTOGRAPH takes the whole square (D433), and his 🔬 macro
   flag — the only road to a crop, reviewed by eye, never guessed from a filename (D434/D435) — is
   the circle. */
.pdp-media{position:relative;width:100%;aspect-ratio:1/1;height:auto;margin:8px 0 14px;overflow:hidden;
  display:flex;align-items:center;justify-content:center;
  border:1px solid var(--line);border-radius:var(--radius-lg);
  background:radial-gradient(120% 80% at 50% 15%,color-mix(in srgb,var(--brand) 16%,transparent),transparent 60%),
    linear-gradient(170deg,var(--panel-2),var(--ink))}
/* 🟩 DEFAULT = THE WHOLE SQUARE (D433: "Can we use that entire square box for product pictures?").
   object-fit:CONTAIN over a blurred copy of the same photo — a square photo fills edge to edge, a
   portrait one shows WHOLE with its own colours filling the rest. NOTHING is cropped unless he asks:
   these are designed packages whose brand mark and strain name live in the bands a circle deletes
   first. The backdrop bleeds past the box (1.08) and the photo takes the box's own radius, so no
   straight seam shows inside the curve ("theres a squared line behind it!"). */
.pdp-photo{position:absolute;left:0;top:0;width:100%;height:100%;object-fit:contain;z-index:4;
  border-radius:var(--radius-lg);display:block}
.pdp-bg{position:absolute;left:0;top:0;width:100%;height:100%;object-fit:cover;z-index:1;
  pointer-events:none;transform:scale(1.08);filter:blur(28px) saturate(1.2);opacity:.9;display:none}
.pdp-bg.on{display:block}
.pdp-media .ring{position:absolute;left:50%;top:50%;border:1px solid var(--line);border-radius:50%;
  display:none;transform:translate(-50%,-50%)}
/* 🔬 THE MACRO CIRCLE IS GONE — D453 (owner, 2026-09-09, shown the live box): "Show the whole
   picture, no circle." D435 had circled his 🔬 shots and D448 tried to make that circle sit right in
   the square; the answer was that the circle WAS the problem — cover-fit inside a round frame is a
   crop, and it was cutting the top and bottom off the jar. A macro now takes the identical square
   treatment as every other photograph (object-fit:contain, whole, in a fixed 1:1 box, its own
   blurred copy behind it), so there is no .mac rule left to write. The 🔬 flag still earns the MACRO
   chip below — that label is his, and it is information, not a shape. */
/* 🏷 THE TWO CHIPS (D435: "different color, and also the placement"). Dark glass, white small caps,
   off the brand green — which fought every scene. index.html stacks them in a .pdp-tags column; the
   DOM contract here has no wrapper, and a stack without one leaves a hole when only one chip shows,
   so they take opposite corners. The [hidden] rule is required: inline-flex beats the UA's
   [hidden]{display:none}, the same trap .brand-mark documents above. */
.pdp-media .chip{position:absolute;right:14px;z-index:6;display:inline-flex;align-items:center;gap:6px;
  font:700 10.5px/1 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}
.pdp-media .chip.real{top:14px}
.pdp-media .chip.macro{bottom:14px}
.pdp-media .chip[hidden]{display:none}

/* ── 🎞 THE PAGER (D439: "These boxes dont look good… pictures don't have to be show, but it hints
   that this is a sliding picture section") — so no thumbnails. THE STRETCHED DOT IS THE HINT: 24px
   against its neighbours' 7px, and that stretch is the entire "this slides" signal he asked for.
   Bare arrows, NO circle (D442: "remove circle around flashes") — the 38px tap target stays. */
.pdp-pager{display:flex;align-items:center;justify-content:center;gap:12px;margin:0 0 16px}
.pdp-pager[hidden]{display:none}
.pg-arrow{width:38px;height:38px;flex:none;border:0;background:transparent;padding:0;cursor:pointer;
  color:var(--white);font:400 26px/1 Inter,system-ui,sans-serif;opacity:.85;
  display:inline-flex;align-items:center;justify-content:center;transition:color .2s,opacity .2s,transform .2s}
.pg-arrow:hover{color:var(--brand);opacity:1}
.pg-arrow:active{transform:scale(.94)}
.pg-dots{display:flex;align-items:center;gap:7px;padding:9px 13px;border-radius:999px;
  background:var(--glass-2);border:1px solid var(--glass-line);box-shadow:inset 0 1px 0 rgba(255,255,255,.08)}
.pg-dot{position:relative;width:7px;height:7px;padding:0;border:0;border-radius:999px;cursor:pointer;
  background:rgba(255,255,255,.3);transition:width .28s var(--ease-out),background .28s,box-shadow .28s}
/* a 31px-tall tap zone on a 7px dot — only ±3px sideways (half the gap), so a neighbour's zone
   never covers this dot's own centre */
.pg-dot::before{content:"";position:absolute;inset:-12px -3px}
.pg-dot.macro{background:transparent;box-shadow:inset 0 0 0 1.5px rgba(255,255,255,.45)}
.pg-dot.on{width:24px;background:var(--brand);box-shadow:0 0 12px color-mix(in srgb,var(--brand) 60%,transparent)}
.pg-count{font:700 11px/1 Inter,system-ui,sans-serif;letter-spacing:.08em;color:var(--muted);
  min-width:34px;text-align:center;white-space:nowrap}
.pg-count b{color:var(--white)}

/* ── ⚖️ 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 BUY ROW. 44px is 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. */
.ezbuy{display:flex;gap:12px;align-items:stretch;margin:0 0 6px}
.qty{display:inline-flex;align-items:center;border:1px solid var(--line-strong);border-radius:14px;overflow:hidden}
.qbtn{width:46px;min-height:44px;border:0;padding:0;cursor:pointer;font-size:20px;color:var(--white);
  background:rgba(255,255,255,.03);transition:background .15s,color .15s}
.qbtn:hover{background:rgba(63,191,99,.16);color:var(--green-bright)}
.qn{min-width:42px;text-align:center;font-family:Syne,sans-serif;font-weight:700;color:var(--white)}
#ezAdd{flex:1;min-height:48px;border:0;cursor:pointer;font-family:Syne,sans-serif;font-weight:800;
  font-size:16px;padding:15px 22px;border-radius:14px;background:var(--green);color:var(--ink);
  box-shadow:0 10px 30px -8px var(--green-glow);transition:transform .2s var(--ease),box-shadow .2s,filter .2s}
#ezAdd:hover{filter:brightness(1.05);box-shadow:0 16px 40px -8px var(--green-glow)}
#ezAdd:active{transform:translateY(1px)}
/* ⚠️ 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 green 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;filter:none}
#ezAdd[disabled]:hover,#ezAdd[disabled]:active{transform:none;filter:none;box-shadow:none}

/* ── 🛍 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,.pg-dot,.wpick,#ezAdd{transition:none}
}
@media (max-width:430px){
  .ezbuy{gap:9px}
  .qbtn{width:42px}
}
/* 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

   Same construction as the storefront's (index.html .ez-veil-el): 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}
