@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('assets/fonts/inter-latin-var.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}

:root {
  color-scheme: light;
  /* WHITE AND GREEN, off the logo. The page is a soft green-white, cards a clean
     white that lifts off it, and the ink a near-black green. Two greens do the
     work the black theme's frost/gold pair used to: LEAF, the bright brand green,
     is the primary (buttons, headings, links, the gate); MOSS, a deep forest
     green, is the darker accent that used to be gold (the top bar, the badge, the
     moment the gate opens). Red survives for errors and low stock ONLY -- an error
     painted in a brand colour is not an error, and that is the one job neither
     green can do. */
  --bg: #f7faf1;
  --surface: #ffffff;
  --surface-2: #eef4e3;
  --border: #d7e3c4;
  --border-light: #bcce9c;
  --text: #12210a;
  --text-muted: #566149;

  --text-faint: #8a9578;
  --red: #cf3a2b;

  /* Moss. The deep forest green that carries the top bar, the cart badge and the
     gate button -- everything the gold used to. Dark enough that cream/white text
     sits on it cleanly, which on a white page is where the contrast has to come
     from now that the ground is no longer black. */
  --moss: #1f5310;
  --moss-hi: #3c8a20;
  --moss-deep: #0e2c06;
  --moss-metal: linear-gradient(178deg, #3c8a20 0%, #2c6d14 22%, #1f5310 55%, #143d0a 78%, #0e2c06 100%);
  --moss-glow: 0 0 22px rgba(31,83,16,.35);

  /* Leaf, the PRIMARY. On white the ramp runs the OTHER way from the black theme:
     mid-green at the top down to deep forest, never a pale top -- a light-topped
     ramp on white is invisible, and this same ramp is clipped to heading TEXT as
     well as poured into buttons, so it has to read as a dark green either way.
     --leaf-hi is the readable green for prices and links ON white; --leaf-ink is
     the cream that sits on a green fill. */
  --leaf: #3f8f24;
  --leaf-hi: #2f7d1e;
  --leaf-deep: #1c4d0c;
  --leaf-ink: #fbfff2;

  --leaf-metal: linear-gradient(177deg, #4a9e28 0%, #3f8f24 34%, #327618 66%, #235510 100%);
  --leaf-glow: 0 0 16px rgba(63,143,36,.22);
  --leaf-emboss: inset 0 1px 0 rgba(255,255,255,.35), inset 0 -1px 0 rgba(0,0,0,.26);

  --radius: 0px;
  --radius-sm: 0px;
  --radius-lg: 0px;
  --shadow: 0 12px 28px rgba(0,0,0,.6);
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --container: 1280px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; }
ul { list-style: none; margin: 0; padding: 0; }
h1, h2, h3, h4 { font-family: var(--font-body); margin: 0; font-weight: 800; letter-spacing: -.01em; }
.container { max-width: var(--container); margin: 0 auto; padding: 0 24px; }
.section { padding: 56px 0; }

.section.section--tight { padding: 16px 0; }
.eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 11px; font-weight: 700; letter-spacing: 1.5px; text-transform: uppercase;
  color: var(--text-muted);
}
.section-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; margin-bottom: 24px; flex-wrap: wrap; border-bottom: 1px solid var(--border); padding-bottom: 16px; }
/* Page headings are METAL-poured rather than metal-coloured. The shop and cart
   headings carry inline font sizing from their templates and sit outside
   .section-head, so they opt in with .leaf-head. */
.section-head h2,
.leaf-head {
  background: var(--leaf-metal); -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 0 16px rgba(140,191,58,.32));
}
.section-head h2 { font-size: 20px; letter-spacing: 1px; text-transform: uppercase; }
.section-head .view-all { font-size: 12px; font-weight: 600; letter-spacing: .5px; text-transform: uppercase; color: var(--text-muted); white-space: nowrap; }
.section-head .view-all:hover { color: var(--text); }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 46px; padding: 12px 26px; border-radius: 0; border: 1px solid var(--leaf);
  font-size: 12.5px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase;
  transition: background .15s ease, color .15s ease, box-shadow .15s ease; white-space: nowrap;
}

.btn-primary {
  background: var(--leaf-metal); color: var(--leaf-ink); border-color: var(--leaf-hi);
  box-shadow: var(--leaf-emboss), 0 3px 16px rgba(140,191,58,.32);
  text-shadow: 0 1px 0 rgba(255,255,255,.35);
}
.btn-primary:hover {
  background: transparent; color: var(--leaf-hi);
  box-shadow: var(--leaf-glow); text-shadow: none;
}
.btn-outline { background: transparent; color: var(--leaf-hi); }
.btn-outline:hover {
  background: var(--leaf-metal); color: var(--leaf-ink);
  box-shadow: var(--leaf-emboss), 0 3px 16px rgba(140,191,58,.32);
}
.btn-sm { min-height: 38px; padding: 8px 16px; font-size: 11px; }
.btn-block { width: 100%; }

.ticker { border-bottom: 1px solid var(--border); background: var(--bg); overflow: hidden; }
.ticker .container { display: flex; align-items: center; justify-content: center; height: 38px; }
.ticker-text {
  font-size: 11px; font-weight: 700; letter-spacing: 1.5px; text-transform: uppercase; color: var(--text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.utility-bar { border-bottom: 1px solid var(--border); }
.utility-bar .container { display: flex; align-items: center; justify-content: flex-end; gap: 20px; height: 38px; }
.utility-link { font-size: 11px; font-weight: 700; letter-spacing: .5px; text-transform: uppercase; color: var(--text-muted); display: flex; align-items: center; gap: 6px; }
.utility-link:hover { color: var(--text); }
.utility-link svg { width: 14px; height: 14px; }

.support-bar {
  background: var(--moss-metal); color: var(--text);
  box-shadow: inset 0 -1px 0 rgba(0,0,0,.28), 0 2px 18px rgba(31,83,16,.28);
  font-size: 11px; letter-spacing: .5px; text-transform: uppercase;
}
.support-bar .container {
  display: flex; align-items: center; justify-content: center;
  padding: 0; flex-wrap: wrap;
}

.support-bar-link {
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
  width: 100%; gap: 2px 8px; min-height: 26px; padding: 3px 14px;
  color: var(--text); text-align: center;
}
.support-bar-label { font-weight: 600; }
.support-bar-hours { opacity: .8; font-weight: 600; }
.support-bar-num { font-weight: 800; letter-spacing: 1px; white-space: nowrap; }
.support-bar-link:hover .support-bar-num { text-decoration: underline; text-underline-offset: 3px; }
/* A phone gets the same bar, only narrower: it is a standing offer, not a headline,
   so it should cost one line of screen and no more. Shrinking the type rather than
   the touch area keeps the whole strip a working tap target. */
@media (max-width: 680px) {
  .support-bar { font-size: 10px; letter-spacing: .3px; }
  .support-bar-link { gap: 1px 6px; min-height: 24px; padding: 2px 12px; }
}
@media (max-width: 380px) {
  .support-bar { font-size: 9.5px; }
  .support-bar-link { gap: 1px 5px; padding: 2px 10px; }
}

.header {
  position: sticky; top: 0; z-index: 50; background: var(--bg); border-bottom: 1px solid var(--border);
}
.header-logo-row { display: flex; align-items: center; justify-content: center; position: relative; padding: 8px 24px; }
.logo { font-size: clamp(24px, 4.5vw, 38px); font-weight: 900; letter-spacing: 2px; color: var(--leaf-hi); text-transform: uppercase; text-shadow: var(--leaf-glow); }

.header-left { position: absolute; left: 24px; top: 50%; transform: translateY(-50%); display: flex; align-items: center; gap: 2px; }
.burger { display: none; background: none; border: none; color: var(--text); width: 40px; height: 40px; align-items: center; justify-content: center; }
.header-icons { position: absolute; right: 24px; top: 50%; transform: translateY(-50%); display: flex; align-items: center; gap: 14px; }
.icon-btn { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; color: var(--text); }
.icon-btn svg { width: 19px; height: 19px; }

.icon-btn--chat svg { width: 17px; height: 17px; }
.icon-btn--chat { color: var(--text-muted); }
.icon-btn--chat:hover { color: var(--text); }
.badge { position: absolute; top: 0; right: 0; background: var(--moss-metal); box-shadow: 0 0 8px rgba(31,83,16,.45); color: var(--text); font-size: 10px; font-weight: 800; width: 15px; height: 15px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }
.mobile-only { display: none; }

.main-nav-row { border-top: 1px solid var(--border); }
.main-nav { display: flex; align-items: center; justify-content: center; gap: 0; flex-wrap: wrap; }
.main-nav > li > a {
  display: block; padding: 16px 18px; font-size: 12.5px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase;
  color: var(--text-muted); transition: color .15s;
}
.main-nav > li > a:hover { color: var(--leaf-hi); }

.main-nav > li.has-sub { position: relative; }
.main-nav > li.has-sub > a::after {
  content: '▾'; font-size: 9px; margin-left: 6px; vertical-align: middle; opacity: .7;
}
.nav-sub {
  position: absolute; top: 100%; left: 0; min-width: 230px; z-index: 60;
  background: var(--surface); border: 1px solid var(--border); border-top: none;
  display: none; box-shadow: var(--shadow);
}
.main-nav > li.has-sub:hover > .nav-sub,
.main-nav > li.has-sub:focus-within > .nav-sub,
.main-nav > li.has-sub.open > .nav-sub { display: block; }
.nav-sub a {
  display: block; padding: 12px 16px; font-size: 11.5px; font-weight: 700;
  letter-spacing: .8px; text-transform: uppercase; color: var(--text-muted);
  border-bottom: 1px solid var(--border-light);
}
.nav-sub li:last-child a { border-bottom: none; }
.nav-sub a:hover { color: var(--text); background: var(--surface-2); }

.drawer-sub { margin: 2px 0 6px 14px; border-left: 2px solid var(--border); }
.drawer-sub a { font-size: 12px; opacity: .9; }

.header-logo-row .logo { display: inline-flex; align-items: center; gap: 11px; }
/* The FULL lockup carries the header now -- wordmark, ribbon and all -- so there is no
   separate text word beside it. It is landscape (~1.77:1), and at this height its own
   "TheRealReal" reads as a word rather than as texture. Sized by HEIGHT so width follows
   the art; even at the tall end it comes out narrower than the old mark+text pair did,
   so the centre column still cannot collide with either icon group. */
.header-logo-img { height: clamp(44px, 5.6vw, 72px); width: auto; display: block; }

.drawer { position: fixed; inset: 0; z-index: 100; display: none; }
.drawer.open { display: block; }
.drawer-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,.72); }
.drawer-panel { position: absolute; top: 0; left: 0; bottom: 0; width: 82%; max-width: 320px; background: var(--surface); border-right: 1px solid var(--border); padding: 24px; overflow-y: auto; }
.drawer-panel .logo { margin-bottom: 24px; font-size: 22px; display: block; }

.drawer-logo-img { width: 100%; max-width: 152px; height: auto; display: block; margin-bottom: 22px; }
.drawer-panel li a { display: block; padding: 14px 0; font-weight: 700; text-transform: uppercase; font-size: 13px; letter-spacing: .5px; border-bottom: 1px solid var(--border); }

.grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; background: var(--border); border: 1px solid var(--border); }

.card { background: var(--surface-2); display: flex; flex-direction: column; }
.card-media { position: relative; aspect-ratio: 1/1; overflow: hidden; }
.card-media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

.card-media--clickable { cursor: zoom-in; }

.card-media--album { display: grid; grid-template-columns: 2fr 1fr; gap: 2px; }
.card-media--album .album-side { display: grid; grid-auto-rows: 1fr; gap: 2px; }

.album-cell { position: relative; overflow: hidden; }

.album-cell--soon { background: var(--surface-2); cursor: default; }
.album-cell--soon img { object-fit: contain; }

.card-media--soon { background: var(--surface-2); }
.card-media--soon img { object-fit: contain; }
.gallery-main--soon .gallery-frame { background: var(--surface-2); }
.gallery-main--soon .gallery-frame img { object-fit: contain; cursor: default; }

.card-tags { position: absolute; top: 10px; left: 10px; display: flex; flex-direction: column;
             gap: 6px; align-items: flex-start; max-width: calc(100% - 20px); }
.tag { font-size: 10px; font-weight: 800; letter-spacing: .5px; text-transform: uppercase; padding: 4px 8px; }
.tag-out { background: #000; color: #fff; border: 1px solid rgba(255,255,255,.55); }

.tag-low {
  background: #000; color: var(--red); border: 1px solid var(--red);
  animation: tag-low-pulse 1.8s ease-in-out infinite;
}
@keyframes tag-low-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(224,192,50,.5); }
  50% { box-shadow: 0 0 0 4px rgba(224,192,50,0); }
}
@media (prefers-reduced-motion: reduce) {
  .tag-low { animation: none; }
}

.tag-note {
  background: #000; color: #fff; border: 1px solid #fff;

  white-space: normal; line-height: 1.25;
}
@media (max-width: 520px) {

  .tag-note { font-size: 9px; padding: 3px 6px; }
}
.card-quick { position: absolute; top: 10px; right: 10px; display: flex; flex-direction: column; gap: 6px; opacity: 0; transition: opacity .15s; }
.card:hover .card-quick { opacity: 1; }
.qbtn { width: 30px; height: 30px; background: rgba(255,255,255,.92); border: 1px solid var(--leaf); display: flex; align-items: center; justify-content: center; color: var(--leaf-hi); box-shadow: 0 1px 4px rgba(18,33,10,.12); }

.qbtn:hover { background: var(--leaf); color: var(--leaf-ink); border-color: var(--leaf-hi); }
.qbtn svg { width: 13px; height: 13px; }
.card-body { padding: 14px 16px 20px; display: flex; flex-direction: column; gap: 4px; flex: 1; }
.card-cat { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .5px; color: var(--text-faint); }
.card-title { font-size: 12.5px; font-weight: 700; letter-spacing: .2px; text-transform: uppercase; color: var(--text); }
.card-title a:hover { color: var(--text-muted); }
/* The price is bare text when nothing is discounted and only wraps itself in
   .now when there is a .was to sit beside — so the leaf metal goes on the container,
   or every full-price item on the site stays off-brand cream. */
.card-price { font-size: 13px; font-weight: 700; margin-top: 2px; color: var(--leaf-hi); text-shadow: 0 0 14px rgba(140,191,58,.28); }
.card-price .was { color: var(--text-faint); text-decoration: line-through; font-weight: 500; font-size: 12px; margin-right: 6px; text-shadow: none; }
.card-price .now { color: var(--leaf-hi); }
.card-add { margin-top: auto; padding-top: 12px; }

.p1 { background: #eef4e3; }
.p2 { background: #e9f0dd; }
.p3 { background: #f0f5e7; }
.p4 { background: #e7efdb; }
.p5 { background: #eff5e6; }
.p6 { background: #e6eeda; }

.category-grid {
  /* ONE COLUMN. The art is a set of wide banner plates, all 3.1034:1, and a plate that
     wide dropped into the old 1.872:1 paired slot would have been cropped to about half
     its width -- taking the artwork off one end and the type off the other. The 1-2-2-2-1
     rhythm existed to make three-ish shapes tile neatly; these are all one shape. */
  display: grid; grid-template-columns: 1fr;

  row-gap: clamp(6px, 1.1vh, 14px);

  --tile-cap: 360px;
  --pack-w: 96%;

  /* Everything on the home page that is NOT this grid, measured (not guessed) in Chrome
     at six viewports. The px part is the fixed furniture -- support bar, header, and the
     44px Join Signal button -- and it differs by breakpoint, so it stays in the two media
     blocks below. This is only the part that scales: the section's own padding and the
     cta band's, both twice over for top and bottom. The numbers here mirror
     .page-home .section--tight and .page-home .cta-band--links exactly; change one and
     you must change the other, or the grid is sized against a budget that no longer
     exists and the scroll comes back.

     vh, not dvh, deliberately, even though the totals below subtract from 100dvh: those
     two rules are written in vh, so the padding really is sized off the LARGE viewport.
     Subtracting a vh amount from a dvh total over-subtracts while a mobile address bar
     is showing, which leaves the grid a few pixels small. That is the safe direction --
     the other way round is a page that scrolls the moment the bar appears. */
  --home-pad: calc(2 * clamp(10px, 2.4vh, 30px) + 2 * clamp(8px, 1.5vh, 18px));

  /* The fixed part of that furniture: support bar + sticky header + the 44px Join Signal
     button. Measured across seventeen widths from 360 to 1920, and it is NOT a step
     function -- .logo is font-size: clamp(24px, 4.5vw, 38px), so the header grows
     CONTINUOUSLY with width: 53px up to about 489px wide, then about 0.09px per px until
     it caps at 61px around 578px, and above the 680px breakpoint the support bar gains
     2px and the header keeps climbing to its 81px cap by 1440px.

     So this is deliberately the WORST CASE for each block rather than a fitted curve:

       <= 680px   support 24 + header 61 (its cap) + button 44 = 129, + 4px slack = 133
       >= 681px   support 26 + header 81 (its cap) + button 44 = 151, + 4px slack = 155

     Over-subtracting is free here and under-subtracting is a scroll. A narrow phone
     really only spends 121px, so the grid comes out about 12px shorter than it strictly
     needs to be -- and body.page-home is a flex column that hands the leftover to the
     ::after spacer, so those 12px show up as breathing room, not as a gap in the wrong
     place. Fit a curve to the header instead and every future logo tweak silently
     un-fits the page. */
  /* 162px, not the 151px the measurements below add up to: the +4px slack became +11px
     when the plates got deeper (3.10:1 against the old ~3.9:1). Over-subtracting is free
     -- body.page-home is a flex column and hands the leftover to the ::after spacer, so
     it shows up as breathing room -- and under-subtracting is a 4px scroll, which is what
     161px still left at 1366x900. */
  --home-furniture: 162px;

  /* Declared once, here, and NOT repeated in the two media blocks below: a var() inside a
     custom property resolves against the cascaded values on the same element, and both
     --home-furniture and --budget land on .category-grid, so the mobile block's 133px
     override is what this sees there. Two copies of the same formula is how one of them
     ends up stale. */
  --budget: calc(100dvh - var(--home-furniture) - var(--home-pad));
  max-width: min(var(--tile-cap), 94vw);
  margin-left: auto; margin-right: auto;

  align-items: center;
  justify-items: center;
}

.category-card { width: var(--pack-w); }

/* No border and no background of its own. Each plate is cropped to its OWN frame now,
   so that frame IS the tile edge -- the card used to add a second border outside it, with
   27px of the art's black margin showing in between, and you saw both. */
.category-card {
  display: block; background: none; border-radius: 11px; overflow: hidden;
  border: 0;
  transition: box-shadow .18s ease;
}

/* Flat tiles: the art already carries the depth, so the card behind it does not.
   Pointing at one lights its edge rather than lifting it off the page. */
.category-card:hover,
.category-card:focus-visible {
  box-shadow: 0 0 22px rgba(31,83,16,.30);
}

/* ONE aspect for all eight. The new artwork arrives as a single sheet of eight plates cut
   on the same grid, so they really are one shape -- measured 3.079 to 3.135 across the
   cut files, a spread of 0.06, normalised to 3.1034 on export. The old per-plate
   overrides existed because the previous art was eight different depths; keeping them
   here would pin seven plates to a ratio none of them has. */
.category-media { display: block; aspect-ratio: 3.1034; }

.category-media img { display: block; width: 100%; height: 100%; object-fit: cover; }
.category-blob { width: 100%; height: 100%; border-radius: 50%; filter: blur(1px); opacity: .9; }

/* Fallback swatches for a category with no photograph. Currently unreferenced -- every
   tile on the homepage has real art -- but kept because that is what they are FOR, and a
   missing tile should still land on-theme. They used to be a rainbow, one hue per
   category; with three colours to play with the identity has to come from VALUE instead,
   leaf stepping down from bright to dark, and moss reserved for the fire sale. */
.cat-firesale { background: radial-gradient(circle at 40% 25%, #4aa028, #1f5310 45%, #0e2c06); }
.cat-flower { background: radial-gradient(circle at 35% 30%, #e6f7c0, #6e9a22); }
.cat-wax { background: radial-gradient(circle at 35% 30%, #c2e878, #46700f); }
.cat-snowballs { background: radial-gradient(circle at 35% 30%, #ffffff, #78aa28); }
.cat-moonrocks { background: radial-gradient(circle at 35% 30%, #8cbf3a, #2a5400); }
.cat-vapes { background: radial-gradient(circle at 35% 30%, #86b432, #33530f); }
.cat-prerolls { background: radial-gradient(circle at 35% 30%, #bce065, #416d14); }
.cat-edibles { background: radial-gradient(circle at 35% 30%, #9ccc45, #2c4a0c); }
.cat-mushies { background: radial-gradient(circle at 35% 30%, #7fae2c, #26400a); }
.cat-merch { background: radial-gradient(circle at 35% 30%, #6d9825, #1f3608); }

@media (max-width: 680px) {
  /* The long hot dog, but sized so it is never a scroll. One column of eight plates, and
     the only free variable is how WIDE they are -- each plate's height follows from its
     own aspect ratio, so the whole stack is:

         height = width * SUM(1/ratio) + 7 * gap

     SUM(1/ratio) over the eight plates is 2.57780 -- they share one ratio now, so it is
     simply 8 / 3.1034. Solve it for width against the space left over and the stack lands
     on the budget instead of past it. These plates are DEEPER than the old ones (3.10 vs
     ~3.9), so the sum grew from 2.10622 and the solved width comes out smaller; that
     constant and .category-media's aspect-ratio must be changed together or the phone
     gets a scroll back.

     One width for all eight, not one per plate: ragged widths down a column read as a
     mistake, and the height still falls out of each plate's own ratio, so nothing is
     cropped and nothing is letterboxed. Narrowing the card is the whole mechanism --
     object-fit: cover would take the lettering off these plates, and contain would
     letterbox them inside a box we just went to the trouble of sizing.

     --home-furniture is 133px here rather than the 155px above: below 681px the support
     bar narrows and the header's logo clamp has not yet grown. See its comment. */
  .category-grid {
    --home-furniture: 133px;
    --gap: clamp(3px, 0.7vh, 8px);
    --pack-w: 100%;
    row-gap: var(--gap); column-gap: clamp(2px, 0.5vh, 6px);
    /* Floor, so the page scrolls again on purpose rather than rendering slivers: a min()
       with nothing under it can resolve to a few pixels, which "fits" perfectly and shows
       nothing at all. 160px and not more: at 200px the floor was winning at a 617px-tall
       viewport -- a real 360x640 Android -- and putting back a 3 to 11px scroll on a
       phone where the solved width, 193px, was perfectly readable. Measured at 217px on
       an iPhone SE and the lettering is crisp; the floor is for viewports shorter than
       any phone, not for phones. */
    max-width: max(160px, min(var(--tile-cap), 94vw,
                 calc((var(--budget) - 7 * var(--gap)) / 2.57780)));
  }
  .category-card { border-radius: 12px; }
  .section.section--tight { padding: 10px 0; }
}

/* No hover on a touch screen, so the edge lights on press instead. */
@media (hover: none) and (pointer: coarse) {
  .category-card:active { border-color: var(--moss-hi); }
}

/* Desktop: two across, the way LAK and BMF lay out, four rows of pairs.

   Not BMF's 1-2-2-2-1 rhythm, and the reason is the art rather than taste. BMF's six
   middle tiles are 1.87:1, so spanning its first and last across both columns costs
   little height. All eight plates here are 3.10:1 banners with no squarer hero among
   them, and a full-width 3.10:1 plate eats a third of the budget on its own; spanning
   one shrinks the paired tiles to slivers. Four even rows it is.

   Same trick as mobile, solved for four rows instead of eight in a column. Every plate
   now shares ONE ratio, so a row is exactly as tall as either of its pair and the sum is
   just four of them:

       each row  1 / 3.1034 = 0.322226
       4 rows              = 1.288904

       height = width * 1.288904 + 3 * row gap,   so   width = (budget - 3 gaps) / 1.288904

   and 2 / 1.288904 = 1.55171 turns that width into the grid's, both columns plus the gap.

   Because the ratios are now identical there is no stagger between the two plates in a
   row and nothing is cropped by object-fit: cover -- the box is the art's own shape. That
   was NOT true of the previous art, which ran 3.37:1 to 4.02:1 and had to keep a ratio
   per plate to stop cover() trimming a frame edge off the shallow ones.

   The budget itself is --home-furniture + --home-pad off 100dvh; both are declared on the
   base rule above with the measurements that produced them. */
@media (min-width: 681px) {
  .category-grid {
    --cgap: clamp(4px, 0.6vw, 14px);
    --rgap: clamp(4px, 1.1vh, 14px);
    grid-template-columns: repeat(2, 1fr);
    column-gap: var(--cgap); row-gap: var(--rgap);
    max-width: max(300px, min(1100px, 94vw,
                 calc((var(--budget) - 3 * var(--rgap)) * 1.55171 + var(--cgap))));
  }
  /* Side by side the first and last plate are not rails any more, so they fill their
     column like the rest. align-items: center on the base rule centres the shorter plate
     of a pair in its row. */
  .category-grid .category-card { width: 100%; justify-self: stretch; }
}

/* The old (max-height: 620px) and (min-width: 680px) block lived here. It went two
   across for short windows; the rule above now does that at every height, and its
   hardcoded max-width: min(1100px, 94vw) would have overridden the height-solved one
   on exactly the short windows that need it most. Its min-width was 680px against the
   mobile block's max-width 680px, so both applied at exactly 680px wide. */

@media (max-height: 760px) {
  .section.section--tight { padding: 10px 0; }
}

.cta-band { border: 0; }
.cta-band .container { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding: 40px 24px; flex-wrap: wrap; }
.cta-band h3 { font-size: 18px; letter-spacing: 1px; text-transform: uppercase; margin-bottom: 4px; }
.cta-band p { color: var(--text-muted); font-size: 13.5px; }
.cta-band .actions { display: flex; gap: 10px; flex-wrap: wrap; }

.cta-band.cta-band--links .container { justify-content: center; align-items: center; padding: 10px 24px; }
/* The two links are the band. Slimmer than a page button, still 44px to the thumb. */
.cta-band.cta-band--links .btn { min-height: 44px; padding: 10px 22px; font-size: 12px; }
.cta-band.cta-band--links .actions { justify-content: center; }

body.page-home { min-height: 100vh; display: flex; flex-direction: column; }
@supports (height: 100dvh) {
  body.page-home { min-height: 100dvh; }
}

.page-home .section.section--tight {
  flex: 1 1 auto; min-height: 0;
  display: flex; align-items: center;
  padding: clamp(10px, 2.4vh, 30px) 0;
}
.page-home .section.section--tight > .container { width: 100%; }

.page-home .cta-band.cta-band--links .container {
  padding: clamp(8px, 1.5vh, 18px) 24px;
}

.page-home::after {
  content: ''; display: block; flex: 1 1 0; max-height: clamp(0px, 5vh, 56px);
}

.footer { border-top: 1px solid var(--border); padding-top: 44px; font-size: 14px; }
.footer-grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1.2fr; gap: 32px; padding-bottom: 32px; border-bottom: 1px solid var(--border); }
.footer h4 { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; color: var(--text); margin-bottom: 16px; font-weight: 800; }
.footer-grid p { color: var(--text-muted); font-size: 13px; line-height: 1.7; max-width: 320px; }
.footer-grid li { margin-bottom: 9px; }
.footer-grid a { color: var(--text-muted); font-size: 13px; }
.footer-grid a:hover { color: var(--text); }
.footer-contact { display: flex; flex-direction: column; gap: 10px; }
.footer-contact a { display: inline-flex; align-items: center; gap: 8px; color: var(--text-muted); font-weight: 600; font-size: 13px; text-transform: uppercase; letter-spacing: .3px; }
.footer-contact a:hover { color: var(--text); }
.footer-contact .dot { width: 5px; height: 5px; background: var(--leaf); box-shadow: 0 0 8px rgba(140,191,58,.7); flex-shrink: 0; }
.pay-row { font-size: 11px; letter-spacing: .5px; text-transform: uppercase; color: var(--text-faint); margin-top: 14px; }
.footer-legal { padding: 22px 0 26px; color: var(--text-faint); font-size: 11px; line-height: 1.8; }
.footer-legal p { margin: 0 0 10px; }
.footer-bottom { display: flex; justify-content: space-between; align-items: center; padding: 18px 0; border-top: 1px solid var(--border); color: var(--text-faint); font-size: 11.5px; text-transform: uppercase; letter-spacing: .3px; flex-wrap: wrap; gap: 10px; }
.footer-bottom .links { display: flex; gap: 18px; }
.footer-bottom .links a:hover { color: var(--text); }

.breadcrumb { padding: 16px 0; font-size: 11.5px; text-transform: uppercase; letter-spacing: .3px; color: var(--text-faint); border-bottom: 1px solid var(--border); }
.breadcrumb .container { display: flex; flex-wrap: wrap; row-gap: 4px; }
.breadcrumb a:hover { color: var(--text); }
.breadcrumb .sep { margin: 0 8px; }
.breadcrumb .current { color: var(--text-muted); overflow-wrap: anywhere; }

@media (max-width: 520px) {
  .breadcrumb { font-size: 10.5px; letter-spacing: .2px; }
  .breadcrumb .sep { margin: 0 5px; }
}

.shop-layout { display: grid; grid-template-columns: 230px 1fr; gap: 32px; align-items: start; }
.filter-box { border: 1px solid var(--border); padding: 18px; margin-bottom: 14px; }
.filter-box h4 { font-size: 11px; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 14px; color: var(--text-muted); }
.filter-row { display: flex; align-items: center; justify-content: space-between; padding: 7px 0; font-size: 13px; color: var(--text-muted); min-height: 32px; }
.filter-row:hover { color: var(--text); }
.filter-row input { accent-color: var(--leaf); width: 15px; height: 15px; }
.filter-row .count { font-size: 11px; color: var(--text-faint); }
.range-slider { width: 100%; accent-color: var(--leaf); margin: 10px 0; }
.shop-toolbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 22px; flex-wrap: wrap; gap: 12px; border-bottom: 1px solid var(--border); padding-bottom: 18px; }
.shop-toolbar .count-txt { color: var(--text-muted); font-size: 12.5px; text-transform: uppercase; letter-spacing: .3px; }

.shop-tools { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; justify-content: flex-end; }
.search-box { position: relative; display: flex; align-items: center; }
.search-input {
  -webkit-appearance: none; appearance: none;
  background: transparent; border: 1px solid var(--border-light); color: var(--text);
  padding: 10px 34px 10px 14px; border-radius: 0; font-size: 12px; font-weight: 700;
  min-height: 40px; width: 220px; letter-spacing: .5px; text-transform: uppercase;
}
.search-input::placeholder { color: var(--text-muted); font-weight: 700; letter-spacing: .5px; }
.search-input:focus { outline: none; border-color: var(--leaf); }

.search-input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.search-clear {
  position: absolute; right: 2px; top: 50%; transform: translateY(-50%);
  width: 30px; min-height: 34px; display: flex; align-items: center; justify-content: center;
  background: none; border: 0; color: var(--text-muted); font-size: 20px; line-height: 1; cursor: pointer;
}
.search-clear:hover { color: var(--text); }

.search-clear[hidden] { display: none; }
.select {
  background: transparent; border: 1px solid var(--border-light); color: var(--text);
  padding: 10px 14px; border-radius: 0; font-size: 12px; font-weight: 700; min-height: 40px;
  text-transform: uppercase; letter-spacing: .5px;
}
.pagination { display: flex; gap: 0; justify-content: center; margin-top: 36px; border: 1px solid var(--border); width: fit-content; margin-left: auto; margin-right: auto; }
.pagination a, .pagination span { min-width: 38px; height: 38px; display: flex; align-items: center; justify-content: center; border-right: 1px solid var(--border); font-size: 12px; font-weight: 700; padding: 0 6px; }
.pagination a:last-child, .pagination span:last-child { border-right: none; }
.pagination .active { background: var(--leaf-metal); color: var(--leaf-ink); box-shadow: var(--leaf-emboss); }
.pagination a:hover { color: var(--leaf-hi); background: var(--surface-2); }

.product-layout { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; }
.gallery-main { aspect-ratio: 1/1; margin-bottom: 10px; border: 1px solid var(--border); }

.gallery-main--photo { aspect-ratio: auto; border: none; }
.gallery-frame { position: relative; aspect-ratio: 1/1; border: 1px solid var(--border); overflow: hidden; }
.gallery-frame img { width: 100%; height: 100%; object-fit: cover; display: block; cursor: zoom-in; }
.gallery-thumbs { display: flex; gap: 8px; margin-top: 8px; }
.gallery-thumb { width: 64px; height: 64px; flex-shrink: 0; padding: 0; border: 1px solid var(--border); background: none; opacity: .6; }
.gallery-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gallery-thumb:hover { opacity: .85; }
.gallery-thumb.active { opacity: 1; border-color: var(--leaf); }

.gallery-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 40px; height: 40px; border-radius: 50%;
  background: rgba(8,12,6,.85); border: 1px solid var(--leaf);
  display: flex; align-items: center; justify-content: center;
  font-size: 22px; line-height: 1; color: var(--leaf-hi); cursor: pointer;
}
.gallery-nav:hover { background: var(--leaf); color: var(--leaf-ink); }
.gallery-nav--prev { left: 10px; }
.gallery-nav--next { right: 10px; }

.lightbox { position: fixed; inset: 0; z-index: 1000; display: flex; align-items: center; justify-content: center; }
.lightbox[hidden] { display: none; }
.lightbox:not([hidden]) { animation: lightbox-fade .18s ease; }
@keyframes lightbox-fade { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .lightbox:not([hidden]) { animation: none; } }
body.lightbox-open { overflow: hidden; }
.lightbox-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,.9); }
.lightbox-img { position: relative; z-index: 1; max-width: 90vw; max-height: 88vh; object-fit: contain; }
.lightbox-close {
  position: absolute; top: 16px; right: 16px; z-index: 2;
  width: 44px; height: 44px; background: transparent; border: none;
  color: #fff; font-size: 30px; line-height: 1; cursor: pointer;
}
.lightbox-nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 52px; height: 52px; border-radius: 50%;
  background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.35);
  color: #fff; font-size: 28px; line-height: 1;
  display: flex; align-items: center; justify-content: center; cursor: pointer;
}
.lightbox-nav:hover { background: rgba(255,255,255,.24); }
.lightbox-nav--prev { left: 12px; }
.lightbox-nav--next { right: 12px; }
@media (max-width: 520px) {
  .lightbox-nav { width: 44px; height: 44px; font-size: 24px; }
  .lightbox-nav--prev { left: 6px; }
  .lightbox-nav--next { right: 6px; }
}
.product-info .card-cat { font-size: 11px; }
.product-title { font-size: 20px; font-weight: 800; margin: 8px 0 12px; letter-spacing: .3px; text-transform: uppercase; }
.product-price { font-size: 18px; font-weight: 700; color: var(--leaf-hi); margin-bottom: 14px; text-shadow: var(--leaf-glow); }

.stock-meter { margin-bottom: 20px; max-width: 280px; }
.stock-meter-track { height: 6px; background: var(--surface-2); border: 1px solid var(--border); overflow: hidden; }
.stock-meter-fill { height: 100%; width: 0%; background: var(--leaf-metal); box-shadow: 0 0 10px rgba(140,191,58,.5); transition: width .5s ease; }
.stock-meter-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .3px; color: var(--text-muted); margin-top: 7px; }
.stock-meter--low .stock-meter-fill { background: var(--red); animation: stock-fill-pulse 1.8s ease-in-out infinite; }
.stock-meter--low .stock-meter-label { color: var(--red); }
.stock-meter--out .stock-meter-fill { background: var(--text-faint); }
.stock-meter--out .stock-meter-label { color: var(--text-faint); }
@keyframes stock-fill-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .5; }
}
@media (prefers-reduced-motion: reduce) {
  .stock-meter--low .stock-meter-fill { animation: none; }
}

.qty-add { display: flex; gap: 10px; align-items: center; margin-bottom: 22px; }
.qty-box { display: flex; align-items: center; border: 1px solid var(--border-light); }
.qty-box button { width: 40px; height: 46px; background: transparent; border: none; color: var(--text); font-size: 16px; }
.qty-box button:hover { color: var(--text-muted); }
.qty-box input { width: 40px; text-align: center; background: transparent; border: none; color: var(--text); font-size: 14px; }
.product-meta-row { display: flex; gap: 20px; font-size: 12px; text-transform: uppercase; letter-spacing: .3px; color: var(--text-muted); border-top: 1px solid var(--border); padding-top: 16px; flex-wrap: wrap; }

.cart-layout { display: grid; grid-template-columns: 1fr 360px; gap: 40px; align-items: start; }
.cart-row { display: grid; grid-template-columns: 80px 1fr auto auto auto; gap: 16px; align-items: center; padding: 18px 0; border-bottom: 1px solid var(--border); }

.cart-block {
  font-size: 12px; font-weight: 700; letter-spacing: .3px; text-align: center; margin-top: 10px;
  color: var(--red); text-transform: uppercase;
}
.cart-note { font-size: 11px; font-weight: 600; letter-spacing: .3px; margin-top: 4px; color: var(--text-muted); }
.cart-note--gone { color: var(--red); }
.cart-row--gone .cart-thumb, .cart-row--gone .card-title { opacity: .55; }
.cart-thumb { width: 72px; height: 72px; position: relative; overflow: hidden; }
.cart-thumb img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.cart-remove { display: inline-flex; padding: 8px; color: var(--text-faint); }
.cart-remove:hover { color: var(--text); }
.summary-box { border: 1px solid var(--border); padding: 24px; }
.summary-box h3 { font-size: 14px; letter-spacing: 1px; text-transform: uppercase; margin-bottom: 18px; font-weight: 800; }
.summary-row { display: flex; justify-content: space-between; padding: 10px 0; font-size: 13px; text-transform: uppercase; letter-spacing: .3px; color: var(--text-muted); border-bottom: 1px solid var(--border); }
.summary-row.total { color: var(--text); font-weight: 800; font-size: 15px; border-bottom: none; padding-top: 14px; }
/* Why the shipping line says what it says -- "Add 12 more, or any item sold by the pound,
   and shipping is free." A fee with no reason next to it reads as a mistake, and this one
   is worth acting on: it is the difference between $25 and nothing. Empty most of the
   time, and :empty keeps it from reserving space when there is nothing to say. */
.summary-note { margin-top: 10px; font-size: 12px; line-height: 1.5; color: var(--text-muted); }
.summary-note:empty { display: none; }
.coupon-row { display: flex; gap: 8px; margin: 16px 0; }
.coupon-row input { flex: 1; background: transparent; border: 1px solid var(--border-light); padding: 11px 14px; color: var(--text); font-size: 12.5px; min-height: 40px; text-transform: uppercase; letter-spacing: .3px; }
.empty-cart { text-align: center; padding: 72px 24px; color: var(--text-muted); }

.order-note-label {
  display: block; margin-top: 16px; margin-bottom: 6px;
  font-size: 10px; font-weight: 700; letter-spacing: .5px; text-transform: uppercase;
  color: var(--text-muted);
}
.order-note {
  width: 100%; background: transparent; border: 1px solid var(--border-light);
  padding: 10px 12px; color: var(--text); font-family: inherit; font-size: 13px;
  resize: vertical;
}
.order-note:focus { outline: none; border-color: var(--leaf); }

.send-panel { margin-top: 18px; border-top: 1px solid var(--border); padding-top: 16px; }
.send-panel[hidden] { display: none; }
.send-panel h4 {
  font-size: 11px; letter-spacing: 1px; text-transform: uppercase;
  margin-bottom: 8px; color: var(--text);
}
.send-hint { font-size: 12px; line-height: 1.6; color: var(--text-muted); margin: 0 0 12px; }
.send-actions { display: flex; flex-direction: column; gap: 8px; }


@media (max-width: 520px) {
}


.btn-plain {
  display: block; width: 100%; min-height: 44px; margin-top: 10px;
  background: none; border: 0; padding: 12px 8px;
  font: inherit; font-size: 12px; font-weight: 700; letter-spacing: .3px;
  text-transform: uppercase; color: var(--text-muted); text-decoration: underline;
  cursor: pointer;
}
.btn-plain:hover { color: var(--text); }

.invoice-modal { position: fixed; inset: 0; z-index: 1100; display: flex; align-items: center; justify-content: center; padding: 16px; }
.invoice-modal[hidden] { display: none; }
.invoice-modal:not([hidden]) { animation: lightbox-fade .18s ease; }
@media (prefers-reduced-motion: reduce) { .invoice-modal:not([hidden]) { animation: none; } }
body.invoice-open { overflow: hidden; }
.invoice-modal-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,.7); }
.invoice-modal-box {
  position: relative; z-index: 1; width: 100%; max-width: 460px;
  max-height: 88vh; display: flex; flex-direction: column;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--border-light);
  border-radius: 14px; padding: 18px 16px 16px;
  box-shadow: 0 18px 50px rgba(0,0,0,.7);
}
@supports (height: 1dvh) { .invoice-modal-box { max-height: 88dvh; } }
.invoice-modal-box h4 { font-size: 14px; letter-spacing: .5px; text-transform: uppercase; margin: 0 0 6px; }

.invoice-modal-text {
  flex: 0 1 auto; min-height: 0; margin-bottom: 12px;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; line-height: 1.5;
  white-space: pre-wrap; overflow-wrap: anywhere;
  overflow-x: hidden; overflow-y: auto; resize: none;
}

@media (max-width: 420px) {
  .invoice-modal-box { padding: 16px 12px 12px; }
  .invoice-modal-text { font-size: 11px; }
}
.invoice-modal-close {
  position: absolute; top: 6px; right: 8px; width: 40px; height: 40px;
  background: none; border: 0; font-size: 26px; line-height: 1;
  color: var(--text-muted); cursor: pointer;
}
.invoice-modal-close:hover { color: var(--text); }

.send-actions .btn { width: 100%; }

#checkout-btn[disabled] { opacity: .45; cursor: not-allowed; }

.filter-toggle-btn { display: none; }
@media (max-width: 1080px) {
  .grid { grid-template-columns: repeat(3, 1fr); }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .product-layout { grid-template-columns: 1fr; }
  .cart-layout { grid-template-columns: 1fr; }
}
@media (max-width: 860px) {
  .main-nav-row { display: none; }
  .burger { display: flex; }
  .mobile-only { display: inline-flex !important; }

  .header-logo-row { padding: 4px 14px; justify-content: space-between; gap: 6px; flex-wrap: wrap; }
  .header-left,
  .header-icons { position: static; transform: none; }
  .header-icons { gap: 6px; }
  /* The 44px tap targets either side already set the row's height, so the mark can
     come down to meet them and the padding does the rest. */
  .header-logo-img { height: clamp(44px, 9vw, 52px); }
  .icon-btn--chat { width: 30px; }
  .utility-bar .container { justify-content: center; gap: 16px; }
  .grid { grid-template-columns: repeat(2, 1fr); }
  .shop-layout { grid-template-columns: 1fr; }
  .cta-band .container { flex-direction: column; align-items: flex-start; }
  /* ...but not the links band. That rule is for the copy-plus-buttons band, which reads
     better left-aligned once it stacks; the links band is a single centred button and
     inherits flex-start from it otherwise. Only 681-860px was affected -- the <=680px
     block already re-centres -- which is exactly the width nobody opens by hand. */
  .cta-band.cta-band--links .container { flex-direction: row; align-items: center; justify-content: center; }
  .filter-toggle-btn { display: inline-flex; }

  .shop-tools { width: 100%; justify-content: flex-start; }
  .search-box { flex: 1 1 100%; order: -1; }
  .search-input { width: 100%; }

  .search-input,
  .select { font-size: 16px; }
  .filters-col { display: none; }
  .filters-col.open { display: block; }
  .section { padding: 40px 0; }
  .product-title { font-size: 17px; }
}
@media (max-width: 520px) {
  .footer-grid { grid-template-columns: 1fr; }
  .container { padding: 0 16px; }
  .cart-row { grid-template-columns: 60px 1fr; row-gap: 10px; }
  .cart-row > *:nth-child(3) { grid-column: 2; }
  .cart-row > *:nth-child(4) { grid-column: 2; }
  .cart-row > *:nth-child(5) { grid-column: 2; justify-self: start; }
  .qty-add { flex-wrap: wrap; }
  .qty-add .btn { width: 100%; order: 3; }
}

@media (max-width: 680px) {
  .cta-band.cta-band--links .container { padding: 6px 16px; align-items: center; gap: 8px; }
  .cta-band.cta-band--links .btn { padding: 10px 18px; font-size: 11.5px; }
}
@media (max-height: 760px) {
  .cta-band.cta-band--links .container { padding: 8px 24px; }
}

@media (hover: none) and (pointer: coarse) {
  .burger { width: 44px; height: 44px; }
  /* Square crest: the home link is no wider than it is tall, so it gets padded out
     to a 44px target rather than growing the emblem past the row height. */
  .header-logo-row .logo { min-height: 44px; padding-inline: 5px; }

  .icon-btn:not(.icon-btn--chat) { width: 44px; height: 44px; }
  .icon-btn--chat { height: 44px; }
  .cart-remove { padding: 14px; }
  .btn-sm { min-height: 44px; }

  .pagination a, .pagination span { min-width: 44px; min-height: 44px; }
  .filter-row { min-height: 44px; }
  .filter-row input { width: 20px; height: 20px; }

  .order-note,
  .coupon-row input,
  .qty-box input,
  .search-input,
  .select { font-size: 16px; }
}

.gate-wrap { min-height: 82vh; display: flex; align-items: center; justify-content: center; padding: 24px; }
.lock-card {
  max-width: 460px; width: 100%; text-align: center;
  display: flex; flex-direction: column; align-items: center; gap: 32px;
  position: relative; z-index: 2;
}
/* The hero is STEADY. It used to breathe -- an animated filter: drop-shadow plus a 1.5%
   scale pulse -- and on a 700px alpha illustration that is two expensive things per frame:
   the shadow is re-rasterised from the alpha channel, and the bitmap is resampled at a new
   sub-pixel size, so the detail crawls. Against the smoke rising off it, that reads as the
   picture flickering rather than as anything glowing.

   The glow stays; it is simply painted once and left alone. Nothing here animates now, so
   the only moving thing on the splash is the smoke, which is where the movement belongs. */
.gate-logo {
  width: clamp(230px, 46vw, 400px); height: auto; display: block;
  filter: drop-shadow(0 10px 26px rgba(18,33,10,.18));
}
.lock-form { display: flex; flex-direction: column; align-items: center; gap: 26px; width: 100%; }
.gate-group { display: flex; flex-direction: column; align-items: center; gap: 10px; width: 100%; }
.lock-label {
  font-size: 11px; font-weight: 700; letter-spacing: 2px; text-transform: uppercase;
  color: var(--text-muted); margin: 0;
}
.lock-input {
  width: 100%; max-width: 260px; text-align: center;
  background: var(--surface); border: 1px solid var(--border); color: var(--text);
  padding: 13px 16px; font-size: 15px; letter-spacing: 3px; font-family: inherit;
}
.lock-input::placeholder { color: var(--text-faint); letter-spacing: 1px; }
.lock-input:focus { outline: none; border-color: var(--leaf); }
.lock-input.is-shaking { animation: lock-shake .32s ease; }
.lock-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 46px; min-width: 200px; padding: 12px 26px;
  border: 1px solid var(--leaf); background: transparent; color: var(--leaf-hi);
  font-size: 12.5px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase;
  cursor: pointer; transition: background .15s ease, color .15s ease;
}
.lock-btn:hover { background: var(--moss-metal); color: var(--text); box-shadow: var(--leaf-emboss), 0 4px 20px rgba(31,83,16,.35); }
.lock-btn[disabled] { opacity: .6; cursor: default; }

.lock-btn.is-nudged { animation: lock-nudge .5s ease; }
@keyframes lock-nudge {
  0%, 100% { background: transparent; color: var(--leaf-hi); transform: none; }
  25%      { background: var(--moss); color: var(--text); transform: scale(1.04); }
  60%      { background: var(--moss); color: var(--text); transform: none; }
}
.lock-error {
  margin: 0; font-size: 12px; letter-spacing: .3px; text-transform: uppercase;
  color: var(--red); font-weight: 600;
}
.lock-error[hidden] { display: none; }
.lock-note {
  font-size: 11px; color: var(--text-faint); letter-spacing: .3px; max-width: 340px;
  line-height: 1.6; margin: 0;
}

@media (max-height: 760px) {
  .gate-wrap { min-height: 100vh; padding: 16px; }
  .lock-card { gap: 18px; }
  .lock-form { gap: 16px; }
  .gate-group { gap: 8px; }
  .gate-logo { width: clamp(170px, 32vw, 250px); }
}
@media (max-height: 560px) {
  .lock-card { gap: 12px; }
  .lock-form { gap: 12px; }
  .gate-logo { width: clamp(120px, 24vw, 170px); }
  .lock-note { font-size: 10px; line-height: 1.45; max-width: 420px; }
}
@keyframes lock-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-8px); }
  75% { transform: translateX(8px); }
}

/* The gate IS a wall — a plain dark screen hiding the shop until the password is
   right. This overlay is that same wall rendered on the page behind it: two halves
   sharing one jagged seam, covering it exactly (same --bg), so showing it changes
   nothing the visitor can see. Verifying age is what cracks it open. Hidden by
   default and only shown by the inline script in home.part.php when the gate just
   redirected here — a plain later visit to the homepage never sees it. */
/* The gate is a wall; verifying age SHATTERS it onto the shop.
   Cracks race out from the impact point first, then the wall itself breaks into
   chunks that fly away — inner ones first, so the hole opens at the middle and grows,
   the way a real wall gives. Every shard is a full-viewport box clipped to one
   polygon; PHP emits the geometry, so the whole thing is declarative CSS that starts
   on its own the instant it renders, with no JS in the path to mistime or skip. */
/* The shop, pulled in behind the wall before it breaks. A snapshot with its scripts
   inert -- it is on screen for well under a second, mostly behind flying chunks, and
   the real page replaces it the moment the break finishes. Mirrors body.page-home's
   own column layout so the shop lands where it will land for real. */
.lk-reveal {
  position: fixed; inset: 0; z-index: 1; overflow: hidden;
  display: flex; flex-direction: column;
  background: var(--bg);
  /* Hidden until the smoke has actually covered the screen. visibility as well as
     opacity, so nothing in here can be tabbed to or read out while it is invisible. */
  opacity: 0; visibility: hidden;
}
/* Cross-faded in rather than switched on: the paint is a splatter now, not a lid, so
   the shop appears in the gaps between the splatters and a hard cut would pop there. */
.lk-reveal { transition: opacity 170ms ease; }
.lk-reveal.is-shown { opacity: 1; visibility: visible; }
/* ---- Ooze --------------------------------------------------------------------
   ONE fixed canvas, and it does nothing until the password lands -- there is no idle
   animation on the gate any more. On success it SPLATS: a green mass hits the screen
   and covers it, the shop is dropped in behind at the thickest moment, and then the
   mass OOZES DOWNWARD off the bottom edge, uncovering the real page from the top.
   Its own compositor layer so the full-viewport repaint does not drag the layer
   beneath it. It only ever exists during the burst, so it starts life on top. */
.lk-ooze {
  position: fixed; inset: 0; z-index: 600; pointer-events: none;
  width: 100%; height: 100%;
  transform: translateZ(0);
  will-change: transform;
}

.promo-pop { position: fixed; inset: 0; z-index: 200; display: flex; align-items: center; justify-content: center; padding: 20px; }
.promo-pop[hidden] { display: none; }
.promo-pop-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,.82); }
.promo-pop-box {
  position: relative; width: 100%; max-width: 620px; text-align: center;
  background: linear-gradient(158deg, #3c8a20 0%, #2c6d14 26%, #1f5310 55%, #143d0a 72%, #327618 100%);
  color: var(--text); border: 4px solid var(--leaf);
  padding: 46px 34px 40px; box-shadow: 0 24px 60px rgba(0,0,0,.7);
  animation: promo-pop-in .32s cubic-bezier(.2,.9,.3,1.2);
  max-height: calc(100vh - 40px); overflow-y: auto;
}
.promo-pop-kicker { font-size: clamp(28px, 7vw, 54px); font-weight: 900; line-height: .98; letter-spacing: -1px; text-transform: uppercase; margin: 0 0 14px; }
.promo-pop-line { font-size: clamp(15px, 2.6vw, 21px); font-weight: 800; letter-spacing: .5px; text-transform: uppercase; line-height: 1.35; margin: 0 0 8px; }
.promo-pop-deals { display: flex; flex-direction: column; gap: 6px; margin: 14px 0 4px; }
.promo-pop-deal { display: flex; align-items: baseline; justify-content: center; gap: 10px; flex-wrap: wrap; }
.promo-pop-off { font-size: clamp(30px, 7.4vw, 56px); font-weight: 900; line-height: 1.05; letter-spacing: -2px; }
.promo-pop-per { font-size: clamp(12px, 2.3vw, 17px); font-weight: 800; letter-spacing: 1px; text-transform: uppercase; opacity: .95; }
.promo-pop-terms { font-size: clamp(11px, 2vw, 13.5px); font-weight: 700; letter-spacing: .6px; text-transform: uppercase; line-height: 1.35; opacity: .9; margin: 10px 0 0; }
.promo-pop-cta {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  margin-top: 22px; min-height: 54px; padding: 14px 34px;
  background: var(--bg); color: var(--leaf-hi); border: 1px solid var(--leaf);
  font-size: 17px; font-weight: 900; letter-spacing: 1.5px; text-transform: uppercase;
}
.promo-pop-cta:hover { background: var(--leaf-hi); color: var(--leaf-ink); }
.promo-pop-num { display: block; margin-top: 14px; font-size: 13px; font-weight: 700; letter-spacing: 1px; color: var(--text); opacity: .92; }
.promo-pop-close {
  position: absolute; top: 6px; right: 10px; width: 44px; height: 44px;
  background: none; border: none; color: var(--text); font-size: 30px; line-height: 1; cursor: pointer;
}
.promo-pop-close:hover { opacity: .7; }
.promo-pop-dismiss {
  display: block; margin: 16px auto 0; background: none; border: none; color: var(--text);
  font-size: 11px; letter-spacing: .8px; text-transform: uppercase; opacity: .8;
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer;
}
@keyframes promo-pop-in {
  from { transform: scale(.88) translateY(14px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .category-card { transition: border-color .15s ease; }

  .lock-input.is-shaking { animation: none; }

  .lock-btn.is-nudged { animation: none; background: var(--moss); color: var(--text); }
  .promo-pop-box { animation: none; }
  /* The smoke canvas is never created under reduced motion -- the gate navigates
     straight through -- so there is nothing here to switch off. */
}

/* ======================== LIQUID METAL =========================
   Third pass. The first two built the metal out of a many-banded
   gradient, and the bands were the problem: a gradient can only make
   STRAIGHT lines, so the surface read as a striped ramp sliding
   sideways. Straight stripes are what cheap metal looks like.

   Real pattern-welded steel -- damascus -- has folded, flowing grain, and
   no gradient of any number of stops can produce it. So the grain is now
   a texture: assets/metal-grain.webp, 256px, 10.8KB, neutral grey so it
   retints with whatever body ramp it is blended over, generated by
   domain-warping a periodic field and then contouring it. Every term in
   that field is a sinusoid at an integer frequency over the tile, so it
   wraps exactly and the flow never shows a seam.

   Three layers, and only two of them move:

     BODY   the metal itself, a GENTLE gradient held STILL. Nothing about
            the body slides any more. That single change is most of what
            makes this read as a solid metal object catching light rather
            than as a video playing inside a button.
     GRAIN  the damascus tile, blended soft-light so it MODULATES the
            body instead of painting over it -- mid grey in the texture is
            a no-op, which is why it is histogram-centred when generated.
            It drifts one whole tile per cycle, diagonally, so the grain
            flows along its own lines. Movement ALONG the grain is far
            quieter than bands crossing it.
            The tile is mostly narrow specular glints with only a whisper
            of broad figure, and that ratio is the whole trick: wood grain
            and damascus grain have nearly the same geometry, and what
            separates them to the eye is that metal throws thin bright
            lines while timber has broad soft bands. Rendered bold, the
            figure read as maple veneer on every button. See
            tools/make-metal-grain.py --river / --ridge.
     GLOSS  one soft specular band, sized in percent so a large surface
            gets a large highlight, crossing once per cycle.

   The separate speck layers are gone. The grain carries fine detail of
   its own, and confetti on top of flowing steel is the opposite of
   subtle. Restoring them is one line if they are missed.
   ============================================================== */
:root {
  /* THE SPEED KNOB -- every metal surface is tied to it. */
  --leaf-cycle: 22s;
  --leaf-halo-cycle: 11s;
  /* Tile scale. The animation below moves the grain by exactly this much,
     so the two have to change together or the loop gains a seam. */
  --leaf-grain: 110px;

  /* The BODY is now light-from-above rather than a bright band sliding
     across, and its range is deliberately narrow. Three versions of this
     were rendered against the real 26px support bar and compared:
       a wide horizontal ramp put an arbitrary bright blob mid-bar;
       a flat fill with no ramp at all read as matte paint, not metal;
       gentle top-lighting read as a strip catching light.
     2deg off vertical, because a perfectly axis-aligned ramp looks
     printed. The grain carries the character; this gives the form.

     BRIGHTNESS, second pass. A ramp that stays pale the whole way down reads
     as painted card rather than metal, so the shadow end drops into a deep
     forest green while the top edge stays cream -- which is the pairing the
     logo's own wordmark uses.
  */
  --leaf-body: linear-gradient(178deg,
    #53aa2e 0%, #46972a 22%, #3a8420 52%, #2c6d16 76%, #21550f 100%);
  --leaf-grain-img: url('assets/metal-grain.webp?v=590e2ab6a8');
  --leaf-gloss: linear-gradient(102deg,
    rgba(255,255,255,0) 40%, rgba(255,255,255,.10) 46%,
    rgba(255,255,255,.34) 50%, rgba(255,255,255,.10) 54%,
    rgba(255,255,255,0) 60%);

  --moss-body: linear-gradient(178deg,
    #45992a 0%, #327618 26%, #235510 55%, #163f0a 78%, #0e2c06 100%);
  --moss-layers: var(--leaf-gloss), var(--leaf-grain-img), var(--moss-body);

  --leaf-layers: var(--leaf-gloss), var(--leaf-grain-img), var(--leaf-body);
  --leaf-layer-size: 240% 100%, var(--leaf-grain) var(--leaf-grain), 100% 100%;
  --leaf-layer-repeat: no-repeat, repeat, no-repeat;
  --leaf-layer-blend: screen, soft-light, normal;
}

/* The grain steps exactly one tile in each axis, so the loop is seamless;
   the gloss sweeps the element; the body never moves. */
@keyframes leaf-live {
  from { background-position: -70% 0, 0 0, 0 0; }
  to   { background-position: 180% 0, calc(var(--leaf-grain) * -1) var(--leaf-grain), 0 0; }
}

/* LEAF fills -- all of these previously said `background: var(--leaf-metal)`. */
.btn-primary,
.btn-outline:hover,
.qbtn:hover,
.gallery-nav:hover,
.pagination .active,
.stock-meter-fill {
  background-image: var(--leaf-layers);
  background-size: var(--leaf-layer-size);
  background-repeat: var(--leaf-layer-repeat);
  background-blend-mode: var(--leaf-layer-blend);
  animation: leaf-live var(--leaf-cycle) linear infinite;
}

/* MOSS fills — the same live metal, deep-green body. The support bar is the shop's
   banner and the gate button is the moment the doors open: the darkest, most solid
   green on the site, so both read as weight against the white page. */
.support-bar,
.badge,
.lock-btn:hover,
.lock-btn.is-nudged {
  background-image: var(--moss-layers);
  background-size: var(--leaf-layer-size);
  background-repeat: var(--leaf-layer-repeat);
  background-blend-mode: var(--leaf-layer-blend);
  animation: leaf-live var(--leaf-cycle) linear infinite;
  color: var(--text);
}

/* LEAF text. Same material clipped to the glyphs. A text-shadow cannot be
   used on transparent text -- it draws the shadow of a shape nobody can
   see -- so the outer glow is a drop-shadow filter, which follows the
   visible pixels. */
.section-head h2,
.leaf-head,
.product-price,
.card-price,
.card-price .now {
  background-image: var(--leaf-layers);
  background-size: var(--leaf-layer-size);
  background-repeat: var(--leaf-layer-repeat);
  background-blend-mode: var(--leaf-layer-blend);
  -webkit-background-clip: text; background-clip: text;
  color: transparent; -webkit-text-fill-color: transparent;
  text-shadow: none;
  filter: drop-shadow(0 0 12px rgba(140,191,58,.34));
}
/* Headings and the single product price animate. Card prices do not: that
   one repeats dozens of times per screen, and sixty small elements
   repainting every frame costs real battery for something illegible at
   13px. They keep the material, still. */
.section-head h2,
.leaf-head,
.product-price {
  animation: leaf-live var(--leaf-cycle) linear infinite;
}

/* LEAF borders: a 1px ring of the same material, as a masked pseudo-
   element, because a border cannot hold a gradient. The grain is left out
   -- at one pixel there is nothing of it to see.

   Geometry: the border stays 1px and only turns transparent, so nothing
   reflows. The ring sits at inset:-1px, the BORDER box; an absolutely
   positioned child is offset from the PADDING box, so inset:0 would draw
   it a pixel inside the border and leave a transparent hairline outside.

   GUARDED, and the guard is not decoration. Without mask compositing the
   two mask layers UNION instead of subtracting, so the ring would paint
   as a SOLID METAL RECTANGLE over the whole control -- covering the label
   on every outline button, the checkout send buttons included. This site
   is opened inside Instagram and TikTok webviews on purpose, which is
   exactly where an old engine turns up. No mask support, no ring, and the
   original solid border is left exactly as it was. */
@supports ((mask-composite: exclude) or (-webkit-mask-composite: xor)) {
  .btn,
  .qbtn,
  .gallery-nav,
  .lock-btn {
    position: relative;
    border-color: transparent;
  }
  .btn::after,
  .qbtn::after,
  .gallery-nav::after,
  .lock-btn::after {
    content: ''; position: absolute; inset: -1px; padding: 1px;
    border-radius: inherit; pointer-events: none;
    background-image: var(--leaf-gloss), var(--leaf-body);
    background-size: 240% 100%, 100% 100%;
    background-repeat: no-repeat, no-repeat;
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
            mask-composite: exclude;
    animation: leaf-edge var(--leaf-cycle) linear infinite;
  }
  .gallery-nav::after { border-radius: 50%; }
}
@keyframes leaf-edge {
  from { background-position: -70% 0, 0 0; }
  to   { background-position: 180% 0, 0 0; }
}

/* Thin leaf trim on the category tiles.
   NOT the masked ring above, and it cannot be: .category-card sets
   overflow:hidden, which clips a pseudo-element at inset:-1px straight off
   the card. This rides the card's OWN background instead -- a solid fill
   clipped to the padding box, the metal clipped to the border box, so the
   metal paints in the 1px border channel and nowhere else.
   background-origin/background-clip ignore overflow, and the corner radius
   comes along for free. */
.category-card {
  border-color: transparent;
  background-image: linear-gradient(#141a10, #141a10), var(--leaf-gloss), var(--leaf-body);
  background-size: auto, 240% 100%, 100% 100%;
  background-repeat: no-repeat, no-repeat, no-repeat;
  background-origin: border-box;
  background-clip: padding-box, border-box, border-box;
  animation: leaf-trim var(--leaf-cycle) linear infinite;
}
@keyframes leaf-trim {
  from { background-position: 0 0, -70% 0, 0 0; }
  to   { background-position: 0 0, 180% 0, 0 0; }
}

/* The logo is a PNG, not text, so there are no glyphs to clip a gradient
   to. It gets a slow breathing halo instead, on its own clock so the
   header does not pulse in lockstep with the bar above it. */
@keyframes leaf-halo {
  0%, 100% { filter: drop-shadow(0 0 6px rgba(140,191,58,.28)); }
  50%      { filter: drop-shadow(0 0 15px rgba(242,241,224,.52)); }
}
.header-logo-img,
.drawer-logo-img {
  animation: leaf-halo var(--leaf-halo-cycle) ease-in-out infinite;
}

/* ---- Breathing glow, the same idea as the logo halo ----
   A metal surface on jet black wants to look LIT, not printed, and the
   cheapest honest way to say that is an outer glow that breathes. This is
   filter: drop-shadow rather than box-shadow deliberately: every one of
   these elements already carries a box-shadow (emboss insets, lift), and
   animating that property would have to restate all of it and would drop
   whatever it forgot. A filter composes on top and touches nothing.

   All of it runs on --leaf-halo-cycle, the LOGO's clock, so the header
   mark and the metal around it breathe together instead of drifting in and
   out of phase.

   WHY IT IS NOT ON EVERY METAL THING. Counted live: the shop grid renders
   25 .btn-primary and 24 .card-price on one screen. A blurred shadow
   repainting 25 times a frame is a real cost on a phone, for a glow
   nobody looks at on a card button. So the breathing glow goes on the
   handful of one-off surfaces, and the grid buttons get a stronger STATIC
   glow instead. .btn-sm is the tell -- it is only ever on the grid
   cards. */
@keyframes leaf-breathe {
  0%, 100% { filter: drop-shadow(0 0 5px rgba(150,205,90,.26)); }
  50%      { filter: drop-shadow(0 0 17px rgba(242,241,224,.54)); }
}
/* The tiles sit in a grid with gaps as small as 3px, so a full-strength
   glow on each would pool into one warm haze instead of reading as eight
   lit objects. Theirs is deliberately about a third as strong. */
@keyframes leaf-breathe-soft {
  0%, 100% { filter: drop-shadow(0 0 4px rgba(150,205,90,.16)); }
  50%      { filter: drop-shadow(0 0 11px rgba(242,241,224,.32)); }
}
.support-bar,
.badge,
.pagination .active,
.btn-primary:not(.btn-sm) {
  animation: leaf-live var(--leaf-cycle) linear infinite,
             leaf-breathe var(--leaf-halo-cycle) ease-in-out infinite;
}
.category-card {
  animation: leaf-trim var(--leaf-cycle) linear infinite,
             leaf-breathe-soft var(--leaf-halo-cycle) ease-in-out infinite;
}
/* The 25 grid buttons: same warmth, no per-frame cost. */
.btn-primary.btn-sm {
  box-shadow: var(--leaf-emboss), 0 0 18px rgba(150,205,90,.34), 0 3px 14px rgba(0,0,0,.5);
}

/* Reduced motion: the material, none of the movement. Still metal, just
   standing still. */
@media (prefers-reduced-motion: reduce) {
  .btn-primary, .btn-primary:not(.btn-sm), .btn-outline:hover, .qbtn:hover, .gallery-nav:hover,
  .lock-btn:hover, .lock-btn.is-nudged, .support-bar, .badge,
  .pagination .active, .stock-meter-fill,
  .section-head h2, .leaf-head, .product-price,
  .btn::after, .qbtn::after, .gallery-nav::after, .lock-btn::after,
  .category-card,
  .header-logo-img, .drawer-logo-img {
    animation: none;
  }
}
