/* =================================================================
   CUBANO CLAN — design system v3 (2026-07-05: dropped the Vice-City
   pink/purple layer — owner didn't like it. One accent family now:
   flame orange/terracotta, warm neutrals, no magenta/violet anywhere.
   Colour roles are themed via CSS variables; the SAME variable names
   are reused across both themes so markup never needs to change.

   This is the canonical copy of the token block below (:root through the
   end of the [data-theme="light"] rule, ~line 180). admin-tokens.css
   duplicates it for the admin panel, which doesn't load this file (2026-08-
   14 — was pulling in this whole ~4200-line stylesheet just for ~180 lines
   of variables). Edit the palette here, then copy the same change there.
   ================================================================= */

/* ---------- BRASS: THE PRODUCT-PAGE ACCENT (2026-07-30) ----------
   The site's accent is flame orange and stays flame orange. Nothing here
   changes the home page, the shop, the nav or the footer.

   The product page is the exception, on the owner's call, because on THAT
   page the single-accent scheme had a real problem: flame was carrying the
   price, the eyebrow, the buy button, the accordion "+", a hover underline
   and the low-stock warning all at once, so nothing was emphasis — a
   scarcity warning looked exactly like a section label. On a page whose only
   job is to sell one garment, the accent has to mean something.

   So inside `.pd-page` (set as the body class by product_detail.html, and
   ONLY there) the accent splits by job:

     brass  — identity and worth: price, eyebrow, the primary button, the
              selected size. Gold is the oldest "this is valuable" signal
              there is, and it is the right colour for a brand called Cubano.
     jade   — trust: in stock, cash on delivery, the delivery promise.
     ember  — scarcity only: "Only 2 left", "−20%".

   The tokens are declared here so both themes can tune the text values, but
   they are APPLIED only in the scoped block further down. Anything outside a
   product page never sees them.

   Text vs fill: the fills are identical in both themes. Only the `-text`
   variants change value, because a hue is not a contrast ratio — gold ink on
   white paper reads dark in the physical world too, and #DCB86F on #F5F5F7 is
   1.9:1. Same hue, different lightness, only where type must be read. */
:root {
  --brass:      #C9A24A;   /* the fill — same in both themes */
  --brass-hi:   #E4C57F;
  --brass-lo:   #A8792C;
  --brass-grad: linear-gradient(118deg, #E4C57F 0%, #C9A24A 46%, #A8792C 100%);
  --jade:       #4FA37A;
  --ember:      #FF8A1E;
  --ember-2:    #D9480F;

  /* shared, non-colour tokens */
  --flame:      linear-gradient(118deg, #FF8A1E 0%, #F2541B 48%, #D9480F 100%);
  --wa:         #25D366;
  --messenger:  #0084FF;
  --radius:     16px;
  --radius-sm:  10px;
  --maxw:       1240px;
  /* Archivo, not Clash Display (2026-07-28). Clash is a characterful grotesque
     and at poster scale its character was the loudest thing on the page. The
     reference the owner is working to — and every house it borrows from — sets
     headlines in a NEUTRAL grotesque at Black weight, where the letterform
     disappears and the word carries. Archivo is the free, self-hostable
     equivalent, and it is variable in BOTH weight and width, which is what
     lets the hero and the footer wordmark compress (`font-stretch`) instead of
     being squeezed with a transform. Clash's files are still in /fonts and
     still declared, so reverting is a one-line change. */
  --font-display: "Archivo", "Helvetica Neue", Helvetica, Arial, sans-serif;
  /* The second voice. Clash Display carries every headline on this site, which
     gave it presence and no dynamics — one volume, everywhere. Sentient is the
     quiet register: editorial serif, lowercase, used for standfirsts and section
     leads so the display type has something to be loud *against*. Never on UI. */
  --font-serif:   "Sentient", Georgia, "Times New Roman", serif;
  --font-body:    "General Sans", "Hind Siliguri", system-ui, sans-serif;
  /* Hind Siliguri sits second in the mono stack purely as a fallback: the
     ticker/eyebrow text is owner-editable in the panel, so if Bangla is ever
     entered there it shapes in a real Bengali face instead of the OS default.
     Latin always renders Space Mono (it has no Bengali coverage to conflict).
     Design chrome itself is English-only — mixed-script elements were
     rejected by the owner 2026-07-06. */
  --font-mono:    "Space Mono", "Hind Siliguri", ui-monospace, "SFMono-Regular", Menlo, monospace;
  --ease:       cubic-bezier(.2, .8, .2, 1);
  accent-color: var(--flame-1); /* native checkboxes/radios/date inputs pick up the brand, per theme */
}

/* ---------- DARK (default): warm charcoal, never pure black ---------- */
:root,
[data-theme="dark"] {
  --ink:       #141318;   /* page */
  --ink-2:     #1b1a21;   /* surface */
  --ink-3:     #24222c;   /* raised surface */
  --cream:     #ECE8E1;   /* primary text */
  --muted:     #9C9690;   /* warm neutral grey (was a cool mauve-grey) */
  --muted-2:   #8C8681;   /* AA 4.5:1+ on both --ink and --ink-2 (was 4.48:1 on --ink-2, footer) */
  --flame-1:   #FF8A1E;
  --flame-2:   #D9480F;
  --mint:      #A6C6A8;   /* Teenage-Engineering sage accent */
  /* Product-page-only roles, readable on charcoal. */
  --brass-text: #DCB86F;
  --jade-text:  #7FC3A0;
  --ember-text: #FF8A1E;
  --border:    rgba(255, 255, 255, .09);
  --border-2:  rgba(255, 255, 255, .17);
  --shadow:    0 24px 60px rgba(0, 0, 0, .5);
  --glow:      0 10px 34px rgba(225, 70, 0, .38);
  --glow-hi:   0 16px 42px rgba(225, 70, 0, .5);
  --flame-ink: #fff;      /* label colour ON a flame fill; light theme flips it */
  /* Bug fix (2026-08-20): was #fff — white on the jade fill is 3.06:1, under
     the 4.5:1 AA floor (axe/Lighthouse caught it). The light-theme override
     two blocks down already carries the correct fix (#1D1D1F, ~5.5:1) with a
     comment claiming "white on jade is ~3.1:1 on paper" — but the fill itself
     is identical in both themes (one-palette rule), so the failure was never
     theme-specific and dark mode — this site's default for a fresh visitor
     with no stored preference — was shipping the unfixed value the whole time. */
  --jade-ink:  #1D1D1F;   /* label colour ON a solid jade fill — same value both themes, the fill doesn't change */
  /* warm-charcoal scrim on the left (keeps headline legible) → sun-glow
     top-right → fade into the page at the bottom. Same recipe re-toned for day. */
  --hero-grad:
    linear-gradient(102deg, rgba(11,10,10,.92) 0%, rgba(18,15,13,.70) 26%, rgba(18,15,13,.34) 46%, rgba(18,15,13,0) 66%),
    radial-gradient(120% 88% at 90% 6%, rgba(255,138,30,.22) 0%, rgba(255,138,30,0) 52%),
    linear-gradient(180deg, rgba(20,19,24,0) 52%, var(--ink) 100%);
  --hero-img-opacity: .52;
  color-scheme: dark;
}

/* ---------- LIGHT: "Sunlit paper" — warm neutral paper, terracotta accent.
   2026-07-05: retired "Vice dawn" (magenta/plum) after owner feedback —
   one accent family now (flame orange → burnt terracotta for AA contrast
   on light paper), warm neutral greys/paper instead of plum/blush. All
   text tokens verified ≥4.5:1 (AA).
   2026-07-05 (later same day): owner asked for "a better white" — the warm
   ivory paper read a little muddy/dated. Moved to a crisp, neutral
   Apple-style light-grey/white system (same family as apple.com's own
   #F5F5F7 background + #1D1D1F near-black text) — reads cleaner and pairs
   with the new glass-material nav/buttons below. ---------- */
[data-theme="light"] {
  --ink:       #F5F5F7;   /* crisp neutral paper — no warm/pink cast */
  --ink-2:     #FFFFFF;   /* card surface — true white */
  --ink-3:     #EDEDF0;   /* raised surface — neutral light grey */
  --cream:     #1D1D1F;   /* primary text — neutral near-black ink */
  --muted:     #57575B;   /* neutral grey, ≥7:1 on paper */
  --muted-2:   #57575B;
  /* ONE PALETTE, BOTH THEMES (2026-07-30, owner directive).
     This block used to darken the accent family for paper: --flame-1 went to
     #CC3A00, --mint to #3F6B4B, and --flame got its own gradient — all in the
     name of AA contrast on light. The owner's verdict on the result was that
     day mode read muted and off-brand, and the instruction is explicit: the
     same colour in both themes, blooming, no per-theme accent hues.
     So the accent tokens are simply INHERITED from :root now — nothing to
     re-declare. The one thing that stays themed is --flame-ink below, which
     isn't a hue at all: it's the label colour that sits ON the flame fill, and
     flipping it (not the fill) is how this project has always kept buttons
     readable without dulling the orange. */
  /* --flame-1 / --flame-2 / --mint / --flame: inherited, deliberately. */
  /* Product-page roles: the FILLS (--brass, --jade, --ember) are inherited
     unchanged — a brass button is the same gold on paper as on charcoal. Only
     the text values drop in value, because gold ink on white reads dark in the
     physical world too and the charcoal value is 1.9:1 here. */
  --brass-text: #8A6218;   /* 5.4:1 on paper — antique brass */
  --jade-text:  #2F7A57;   /* 4.9:1 */
  --ember-text: #C2410C;   /* 5.0:1 — scarcity only; 2026-08-06 pulled off the
                              red end of the ramp with --flame-2, see :root */
  --border:    rgba(0, 0, 0, .10);
  --border-2:  rgba(0, 0, 0, .16);
  --shadow:    0 24px 60px rgba(0, 0, 0, .12);
  --glow:      0 10px 30px rgba(242, 84, 27, .30);
  --glow-hi:   0 16px 40px rgba(242, 84, 27, .40);
  /* THE PRIMARY BUTTON ON PAPER. White text on the flame gradient is 3.37:1 —
     under the AA floor. The fix is NOT to darken the fill (tried; read as mud);
     it's to darken the label. #1D1D1F on the gradient's darkest stop (#F2541B)
     is 5.1:1, and the button reads as a piece of flame on the page. */
  --flame-ink: #1D1D1F;  /* label colour ON a flame fill — see above */
  --jade-ink:  #1D1D1F;  /* same fix, same reasoning, for the jade sale badge — white on jade is ~3.1:1 on paper */
  /* Day mode hero = warm, sunlit: neutral charcoal scrim on the left,
     amber-gold glow top-right, bottom anchors into the paper. The left
     scrim never fades all the way to zero — it tapers to a thin warm veil
     that carries across the whole frame, so the photo reads as "lit at
     golden hour" everywhere instead of washing out to bare paper past ~68%.
     The bottom fade goes photo → dark horizon → paper (not photo → paper
     directly): fading a moody photo straight into light, warm paper reads
     as overexposed fog, but fading it into a dark band first (like a
     sunset horizon) then letting THAT dissolve into the page in one final
     sliver reads as intentional, cinematic vignetting instead. */
  --hero-grad:
    linear-gradient(102deg, rgba(20,16,13,.88) 0%, rgba(30,23,17,.60) 24%, rgba(38,28,18,.30) 42%, rgba(34,24,16,.13) 64%, rgba(30,20,14,.19) 100%),
    radial-gradient(120% 88% at 90% 6%, rgba(255,166,64,.34) 0%, rgba(255,166,64,.14) 38%, rgba(255,166,64,.05) 70%, rgba(255,166,64,0) 88%),
    linear-gradient(180deg, rgba(245,245,247,0) 46%, rgba(26,20,15,.58) 82%, var(--ink) 100%);
  /* Full opacity: fading the photo itself blends it with the light paper
     behind and washes every shadow toward grey fog. The scrim above —
     not image opacity — carries the golden-hour mood while keeping blacks rich. */
  --hero-img-opacity: 1;
  color-scheme: light;
}

*, *::before, *::after { box-sizing: border-box; }
/* scroll-padding-top keeps in-page anchors (#drop, #droplist) clear of the
   sticky header instead of hiding the heading underneath it. */
/* overflow-x:clip (not hidden) kills sideways page scroll without turning <html>
   into a scroll container — body's own overflow-x:hidden doesn't stop the viewport
   from scrolling when content pokes past the root. */
html { scroll-behavior: smooth; scroll-padding-top: 90px; overflow-x: clip; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--cream);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* 2026-07-05: dropped `overflow-x:hidden` — html{overflow-x:clip} above
     already does that job (the comment here even said body's own hidden
     "doesn't stop the viewport from scrolling"), and setting overflow-x
     without overflow-y makes the browser implicitly compute overflow-y:auto,
     which silently turns body into its own scroll container. Since body's
     height isn't otherwise constrained, the *real* scrolling still happens
     on the viewport, not inside body — and position:sticky on the header
     (a body child) was sticking relative to a scroll container that never
     actually receives scroll, so it just scrolled away like a static
     element. Site-wide bug, found while upgrading the navbar. */
  transition: background .5s var(--ease), color .5s var(--ease);
}

img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }

h1, h2, h3, .display {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: .96;
  letter-spacing: -.01em;
  text-transform: uppercase;
  margin: 0;
}

.container { max-width: var(--maxw); margin: 0 auto; padding: 0 24px; }
.section { padding: clamp(52px, 8vw, 104px) 0; position: relative; }
.text-flame {
  background: var(--flame);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.muted { color: var(--muted); }
.eyebrow {
  font-size: .72rem;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--flame-1);
  font-weight: 600;
  font-family: var(--font-body);
}

main, .site-header, .site-footer { position: relative; z-index: 3; }

/* ---------- buttons ----------
   Rebuilt 2026-07-29. The old set worked and had three things wrong with it:
   every button lifted 3px on hover (the default move of every AI-built site in
   2026), none of them had a focus ring, and none had a disabled or busy state,
   so a form mid-submit looked identical to one nobody had touched.

   What replaced the lift: the flame gradient is now oversized and SLIDES on
   hover. The button is molten and the heat moves across it — an effect that
   belongs to this brand specifically, where a translateY belongs to nobody.
   The lift survives at 1px, as weight rather than as the whole gesture. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5em;
  padding: 14px 28px; border-radius: 100px;
  font-family: var(--font-body); font-weight: 600; font-size: .9rem; letter-spacing: .01em;
  border: 1px solid transparent; cursor: pointer;
  transition: transform .25s var(--ease), box-shadow .25s var(--ease),
              background-color .25s, background-position .55s var(--ease),
              color .25s, border-color .25s, opacity .2s;
  white-space: nowrap;
}
.btn:active { transform: translateY(1px) scale(.99); }
/* One ring for every button on the site. `outline` and not a box-shadow ring:
   the shadow version has to fake the gap with the page colour, which is the
   wrong colour the moment a button sits on a card, and it would also wipe out
   .btn-flame's own glow while focused. An outline follows the pill radius,
   needs no background match, and leaves the glow alone. :focus-visible, so a
   mouse click doesn't leave a halo behind. */
.btn:focus-visible { outline: 2px solid var(--flame-1); outline-offset: 3px; }
/* Unavailable and busy are different states and used to look the same as ready.
   `[disabled]` covers sold-out size chips and submitted forms; `.is-busy` is for
   a button waiting on the network, which stays readable but stops inviting. */
.btn[disabled], .btn[aria-disabled="true"] {
  opacity: .45; cursor: not-allowed; transform: none !important;
  box-shadow: none; filter: saturate(.4);
}
.btn.is-busy { pointer-events: none; opacity: .8; }
/* A thin gloss line across the top of every solid button — the same specular
   catch the glass surfaces use, so solid + translucent buttons read as one
   material family instead of two different button systems. */
/* Now a brass fill with dark ink (2026-07-30). The `border-color` matters more
   than it looks: gold sitting on white paper loses its own edge and starts to
   read as a pale blob, so the darkest brass stop is drawn round it as a
   hairline. On charcoal the same line just reads as weight. */
.btn-flame {
  background: var(--flame); background-size: 220% 100%; background-position: 0% 50%;
  border-color: var(--brass-lo);
  color: var(--flame-ink); font-weight: 700;
  box-shadow: var(--glow), inset 0 1px 0 rgba(255, 255, 255, .38);
}
.btn-flame:hover {
  background-position: 100% 50%;
  transform: translateY(-1px);
  box-shadow: var(--glow-hi), inset 0 1px 0 rgba(255, 255, 255, .4);
}
/* The specular sweep: a narrow highlight that crosses the face once per hover.
   ::after and not a background layer, because .btn-flame's background is now
   doing the gradient slide. `overflow: hidden` for it is set with the ripple
   rule further down, which this shares. */
.btn-flame::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg, transparent 38%, rgba(255, 255, 255, .34) 50%, transparent 62%);
  transform: translateX(-120%);
  transition: transform .7s var(--ease);
}
.btn-flame:hover::after { transform: translateX(120%); }
/* Glass outline button — frosted/translucent instead of flat transparent,
   same recipe as the header/icon-buttons. */
.btn-outline {
  border-color: color-mix(in srgb, var(--cream) 16%, transparent); color: var(--cream);
  background: color-mix(in srgb, var(--ink-2) 40%, transparent);
  backdrop-filter: blur(14px) saturate(160%); -webkit-backdrop-filter: blur(14px) saturate(160%);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--cream) 18%, transparent);
}
.btn-outline:hover {
  border-color: color-mix(in srgb, var(--cream) 34%, transparent);
  background: color-mix(in srgb, var(--ink-2) 65%, transparent);
}
.btn-light { background: var(--cream); color: var(--ink); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .3); }
.btn-light:hover { transform: translateY(-1px); box-shadow: 0 10px 26px rgba(0, 0, 0, .22), inset 0 1px 0 rgba(255, 255, 255, .4); }
.btn-wa { background: var(--wa); color: #06310f; }
.btn-wa:hover { transform: translateY(-1px); filter: brightness(1.05); }
.btn-messenger { background: var(--messenger); color: #fff; }
.btn-messenger:hover { transform: translateY(-1px); filter: brightness(1.08); }
.btn-block { width: 100%; }
.btn-lg { padding: 17px 36px; font-size: 1rem; }
/* The sweep and the gradient slide are decoration; the colour change on hover
   is the feedback. Reduced motion keeps the second and drops the first. */
@media (prefers-reduced-motion: reduce) {
  .btn { transition: background-color .25s, color .25s, border-color .25s, box-shadow .25s; }
  .btn:hover { transform: none; }
  .btn-flame::after { display: none; }
  .btn-flame:hover { background-position: 0% 50%; filter: brightness(1.08); }
}

/* ---------- announcement bar ---------- */
/* Inverted chip — dark ink on cream in dark mode, plum on paper in light —
   so it reads in BOTH themes (the old inline var(--ink)+white text vanished
   on light's blush paper). Hidden by default; JS reveals it unless dismissed. */
.announce-bar {
  display: none; position: relative;
  background: var(--cream); color: var(--ink);
  text-align: center; padding: 9px 38px 9px 16px;
  font-size: .9rem; font-weight: 600;
}
.announce-bar a { color: inherit; text-decoration: underline; }
.announce-bar .announce-close {
  all: unset; cursor: pointer; position: absolute; right: 14px; top: 50%;
  transform: translateY(-50%); font-size: 1.2rem; line-height: 1;
}

/* ---------- header ---------- */
/* Glass material (2026-07-05, "iPhone/iPad vibe" pass): a frosted, translucent
   surface — stronger blur+saturate than a plain tint, a hairline border that
   catches a little of the ink colour, and an inset top highlight simulating
   light catching the top edge of glass. Same recipe iOS/macOS use for nav
   bars, sheets and toolbars. Built on --ink-2 (the card-surface token, true
   white in light mode) rather than the page --ink, so the bar reads as a
   distinct frosted layer floating over the page, not just a dimmed page tint. */
.site-header {
  position: sticky; top: 0; z-index: 100;
  background: color-mix(in srgb, var(--ink-2) 62%, transparent);
  backdrop-filter: blur(24px) saturate(190%);
  -webkit-backdrop-filter: blur(24px) saturate(190%);
  border-bottom: 1px solid color-mix(in srgb, var(--cream) 10%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--cream) 16%, transparent);
  transition: background .4s var(--ease), border-color .4s var(--ease),
              box-shadow .4s var(--ease), backdrop-filter .4s var(--ease);
}
/* At the very top of a page that opens on the hero, the bar dissolves so the
   hero reads as one full-bleed image — the page feels like a canvas, not a UI. */
.site-header.over-hero {
  background: transparent;
  border-bottom-color: transparent;
  box-shadow: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
/* Bug fix (2026-08-20): the mobile drawer is opened most often from the very
   top of the home page, exactly where .over-hero makes the bar transparent —
   so the ✕ toggle and the drawer's top edge sat directly on the hero photo
   with no backing surface, reading as "broken/invisible". .menu-open (set by
   the toggle script alongside n.classList.add('open')) forces the same solid
   glass surface .scrolled uses, regardless of scroll position, for as long as
   the drawer is open. */
.site-header.over-hero.menu-open {
  background: color-mix(in srgb, var(--ink-2) 74%, transparent);
  backdrop-filter: blur(24px) saturate(190%);
  -webkit-backdrop-filter: blur(24px) saturate(190%);
  border-bottom-color: color-mix(in srgb, var(--cream) 10%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--cream) 16%, transparent);
}
/* Once you scroll, it solidifies, gains a soft lift and quietly shrinks — alive,
   but the cart + nav stay reachable at all times (never hidden on a store). */
.site-header.scrolled {
  background: color-mix(in srgb, var(--ink-2) 74%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--cream) 20%, transparent), 0 12px 34px rgba(0, 0, 0, .30);
}
/* daylight lift: a pure soot/black shadow reads dirty on light paper — a shadow
   tinted toward neutral black at low opacity sits naturally instead. */
[data-theme="light"] .site-header.scrolled { box-shadow: inset 0 1px 0 rgba(255, 255, 255, .7), 0 12px 34px rgba(0, 0, 0, .10); }
/* 94px, not the 76px this used to be. Two reasons, one of them structural: the
   lockup carries a wisp of smoke above the band, and at 76px there was no
   headroom for it to rise into — it got sliced off at the viewport edge. The
   other is that every house we benchmark against sets its header near 90px;
   the extra air is what stops a storefront header reading as an app toolbar. */
.header-inner { display: flex; align-items: center; justify-content: space-between; height: 94px; gap: 20px; transition: height .4s var(--ease); }
.site-header.scrolled .header-inner { height: 72px; }
.brand { display: flex; align-items: center; gap: 10px; }
.brand .brand-name { font-family: var(--font-display); font-weight: 600; font-size: 1.5rem; letter-spacing: .02em; }
.nav { display: flex; align-items: center; gap: 8px; }
/* Nav type is the utility face, not the body sans: small, wide-tracked mono is
   the register a drop-culture label prints on a care label or a shipping box —
   and it stops the nav from competing with the display type in the hero. */
.nav a {
  font-family: var(--font-mono); font-size: .68rem; font-weight: 700; letter-spacing: .18em;
  text-transform: uppercase; color: var(--muted); position: relative;
  padding: 9px 12px; transition: color .2s var(--ease);
}
/* SIGNATURE — the bracket. The logo is a bracket band clamping the wordmark;
   the nav borrows that clamp instead of a generic sliding underline. Two corner
   rules close in on the link on hover, and stay closed on the current page. */
.nav a::before, .nav a::after {
  content: ""; position: absolute; top: 2px; bottom: 2px; width: 5px;
  border: 1.5px solid var(--flame-1); opacity: 0;
  transition: opacity .22s var(--ease), transform .28s var(--ease);
}
.nav a::before {
  left: 0; border-right: 0; border-radius: 2px 0 0 2px; transform: translateX(6px);
}
.nav a::after {
  right: 0; border-left: 0; border-radius: 0 2px 2px 0; transform: translateX(-6px);
}
.nav a:hover, .nav a.active { color: var(--cream); }
.nav a:hover::before, .nav a:hover::after,
.nav a.active::before, .nav a.active::after {
  opacity: 1; transform: none;
}
/* ═══ COLLECTIONS MEGA MENU ═══════════════════════════════════════════════
   The header's single browse door (see the comment in base.html). Desktop: a
   full-width panel that drops out of the bar and holds one picture card per
   series. Mobile (<881px): the same markup collapses into an in-flow accordion
   inside the drawer — no floating layer on a phone.

   Everything below is scoped to `.megamenu`, and the first job is to undo the
   generic `.nav a` treatment for links INSIDE the panel: they are cards, not
   nav items, so they must not inherit the mono type, the padding or the
   bracket pseudo-elements. */
.megamenu { position: static; }        /* so the panel resolves against .site-header */
.megamenu > summary {
  font-family: var(--font-mono); font-size: .68rem; font-weight: 700; letter-spacing: .18em;
  text-transform: uppercase; color: var(--muted); position: relative;
  padding: 9px 12px; cursor: pointer; list-style: none;
  display: inline-flex; align-items: center; gap: 7px;
  transition: color .2s var(--ease);
}
.megamenu > summary::-webkit-details-marker { display: none; }
/* Same bracket signature as every other nav item. */
.megamenu > summary::before, .megamenu > summary::after {
  content: ""; position: absolute; top: 2px; bottom: 2px; width: 5px;
  border: 1.5px solid var(--flame-1); opacity: 0;
  transition: opacity .22s var(--ease), transform .28s var(--ease);
}
.megamenu > summary::before { left: 0; border-right: 0; border-radius: 2px 0 0 2px; transform: translateX(6px); }
.megamenu > summary::after  { right: 0; border-left: 0; border-radius: 0 2px 2px 0; transform: translateX(-6px); }
.megamenu:hover > summary, .megamenu[open] > summary { color: var(--cream); }
.megamenu:hover > summary::before, .megamenu:hover > summary::after,
.megamenu[open] > summary::before, .megamenu[open] > summary::after { opacity: 1; transform: none; }
.mm-chev { transition: transform .25s var(--ease); }
.megamenu[open] .mm-chev { transform: rotate(180deg); }

/* Panel links are cards — strip the nav-link idiom. */
.mm-panel a { font-family: inherit; letter-spacing: normal; text-transform: none; padding: 0; }
.mm-panel a::before, .mm-panel a::after { content: none; }

@media (min-width: 881px) {
  .mm-panel {
    position: absolute; top: 100%; left: 0; width: 100%; z-index: 60;
    background: color-mix(in srgb, var(--ink-2) 97%, transparent);
    backdrop-filter: blur(26px) saturate(180%);
    -webkit-backdrop-filter: blur(26px) saturate(180%);
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
    box-shadow: 0 34px 60px -34px rgba(0, 0, 0, .62);
    padding: clamp(22px, 2.6vw, 34px) 0 clamp(24px, 2.8vw, 36px);
  }
  .megamenu[open] .mm-panel { animation: mmDrop .34s var(--ease) both; }
  @keyframes mmDrop { from { opacity: 0; transform: translateY(-10px); } }
  .mm-inner {
    max-width: var(--maxw); margin: 0 auto; padding: 0 24px;
    display: grid; grid-template-columns: minmax(190px, 250px) minmax(0, 1fr);
    gap: clamp(24px, 3vw, 48px); align-items: start;
  }
  .mm-lead { display: flex; flex-direction: column; gap: 12px; }
  .mm-eyebrow {
    font-family: var(--font-mono); font-size: .62rem; font-weight: 700;
    letter-spacing: .26em; text-transform: uppercase; color: var(--flame-1);
  }
  .mm-note { font-size: .88rem; line-height: 1.5; color: var(--muted); max-width: 30ch; }
  .mm-all {
    display: inline-flex; align-items: center; gap: 8px; margin-top: 2px;
    font-family: var(--font-mono) !important; font-size: .66rem !important; font-weight: 700;
    letter-spacing: .16em !important; text-transform: uppercase !important;
    color: var(--cream); align-self: flex-start;
    border-bottom: 1px solid color-mix(in srgb, var(--flame-1) 60%, transparent);
    padding-bottom: 4px !important;
    transition: color .2s var(--ease), gap .2s var(--ease);
  }
  .mm-all:hover { color: var(--flame-1); gap: 12px; }
  /* auto-fit, not fixed tracks: two series and twelve both lay out correctly,
     which is the whole point of moving the list behind one door. The panel
     scrolls rather than growing without limit. */
  .mm-cards {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(172px, 216px));
    justify-content: start;
    gap: 14px; max-height: min(62vh, 460px); overflow-y: auto; padding-right: 2px;
  }
  .mm-card {
    display: grid; gap: 10px; border-radius: 14px;
    animation: mmCardIn .4s var(--ease) both;
    animation-delay: calc(60ms + var(--mi, 0) * 45ms);
  }
  @keyframes mmCardIn { from { opacity: 0; transform: translateY(12px); } }
  .mm-shot {
    position: relative; display: block; aspect-ratio: 4 / 3; overflow: hidden;
    border-radius: 12px; background: var(--ink-3);
  }
  .mm-shot img {
    width: 100%; height: 100%; object-fit: cover; display: block;
    transition: transform .7s var(--ease), filter .3s var(--ease);
    filter: saturate(.92);
  }
  .mm-card:hover .mm-shot img, .mm-card:focus-visible .mm-shot img { transform: scale(1.07); filter: saturate(1.1); }
  /* The flame edge only appears on the card being considered — the rest stay
     quiet, so the panel never looks like a wall of buttons. */
  .mm-shot::after {
    content: ""; position: absolute; inset: 0; border-radius: 12px;
    border: 1px solid transparent; transition: border-color .25s var(--ease);
  }
  .mm-card:hover .mm-shot::after, .mm-card.active .mm-shot::after { border-color: var(--flame-1); }
  .mm-shot--all {
    display: grid; place-items: center; color: var(--muted);
    border: 1px dashed var(--border-2); background: transparent;
    transition: color .25s var(--ease);
  }
  .mm-card--all:hover .mm-shot--all { color: var(--flame-1); }
  .mm-meta { display: flex; flex-direction: column; gap: 3px; }
  .mm-name {
    font-family: var(--font-display); font-size: 1.02rem; font-weight: 600;
    color: var(--cream); line-height: 1.15;
  }
  .mm-card:hover .mm-name, .mm-card.active .mm-name { color: var(--flame-1); }
  /* The "new" flag, at menu scale. It follows the name inline rather than
     taking a row of its own — a badge that reflows the card is a badge that
     makes the menu jump every three weeks. The colour never changes with the
     card's hover state, because it is a fact about the series, not a hover
     affordance. */
  .mm-flag {
    display: inline-block; margin-left: 8px; vertical-align: 2px;
    font-family: var(--font-mono); font-size: .5rem; font-weight: 800;
    letter-spacing: .14em; text-transform: uppercase;
    color: #171216; background: linear-gradient(100deg, var(--flame-1), var(--flame-2));
    padding: 2px 7px; border-radius: 100px;
  }
  .mm-count {
    font-family: var(--font-mono); font-size: .6rem; letter-spacing: .16em;
    text-transform: uppercase; color: var(--muted-2);
  }
}
@media (prefers-reduced-motion: reduce) {
  .megamenu[open] .mm-panel, .mm-card { animation: none; }
}

.header-actions { display: flex; align-items: center; gap: 10px; }

/* Desktop only: brand left, nav optically dead-centre, actions right — the
   three-column lockup fashion houses use. Mobile keeps the flex row (the nav
   leaves the flow entirely as a fixed drawer under 880px). */
@media (min-width: 881px) {
  .header-inner { display: grid; grid-template-columns: 1fr auto 1fr; }
  .nav { justify-self: center; }
  .header-actions { justify-self: end; }
}

/* Header actions are hairline-framed rectangles, not glass pills: the logo's
   bracket is rectilinear, so the controls follow it. The frosted material stays
   on the bar itself — stacking blur on every button turned the right-hand side
   into a row of floating lozenges that read louder than the brand. */
.icon-btn {
  display: inline-grid; place-items: center; width: 40px; height: 40px;
  border: 1px solid color-mix(in srgb, var(--cream) 16%, transparent);
  border-radius: 3px;
  background: transparent;
  color: var(--cream); cursor: pointer;
  transition: border-color .2s var(--ease), background .2s var(--ease), color .2s var(--ease);
}
.icon-btn:hover {
  border-color: color-mix(in srgb, var(--cream) 42%, transparent);
  background: color-mix(in srgb, var(--cream) 7%, transparent);
}
.theme-toggle .moon { display: none; }
[data-theme="light"] .theme-toggle .sun { display: none; }
[data-theme="light"] .theme-toggle .moon { display: block; }

/* The cart is the one action that must always win the eye: it keeps a solid
   frame while its neighbours stay hairline-quiet. */
.cart-btn {
  position: relative; display: inline-flex; align-items: center; gap: 9px;
  height: 40px; padding: 0 16px;
  border: 1px solid color-mix(in srgb, var(--cream) 30%, transparent);
  border-radius: 3px;
  background: transparent;
  font-family: var(--font-mono); font-weight: 700; font-size: .68rem;
  letter-spacing: .16em; text-transform: uppercase;
  transition: border-color .2s var(--ease), background .2s var(--ease), color .2s var(--ease);
}
.cart-btn:hover {
  border-color: var(--flame-1);
  background: color-mix(in srgb, var(--flame-1) 10%, transparent);
}
/* The fly-to-cart clone lands here (base.html) — one quick catch-bounce. */
.cart-btn.is-landed { animation: cartCatch .34s var(--ease); }
@keyframes cartCatch {
  0% { transform: scale(1); }
  35% { transform: scale(1.14); border-color: var(--flame-1); }
  100% { transform: scale(1); }
}
.cart-count {
  background: var(--flame); color: var(--flame-ink); border-radius: 2px; min-width: 19px; height: 19px;
  padding: 0 5px; font-size: .68rem; display: inline-flex; align-items: center; justify-content: center; font-weight: 700;
  font-family: var(--font-mono);
}
.nav-toggle {
  display: none; position: relative; width: 40px; height: 40px;
  background: none; border-radius: 3px;
  border: 1px solid color-mix(in srgb, var(--cream) 16%, transparent);
  color: var(--cream); cursor: pointer;
  transition: border-color .2s var(--ease);
}
.nav-toggle[aria-expanded="true"] { border-color: color-mix(in srgb, var(--cream) 42%, transparent); }
/* Two bars that morph into a ✕ — the open/close state reads instantly. */
.nt-bar {
  position: absolute; left: 50%; top: 50%; width: 20px; height: 2px;
  background: currentColor; border-radius: 2px;
  transform: translate(-50%, calc(-50% - 3.5px));
  transition: transform .32s var(--ease);
}
.nt-bar + .nt-bar { transform: translate(-50%, calc(-50% + 3.5px)); }
.nav-toggle[aria-expanded="true"] .nt-bar { transform: translate(-50%, -50%) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nt-bar + .nt-bar { transform: translate(-50%, -50%) rotate(-45deg); }

/* Drawer-only brand footer — mobile drawer chrome, never on desktop */
.nav-drawer-foot { display: none; }

/* ---------- hero ---------- */
.hero { position: relative; min-height: 92vh; display: flex; align-items: stretch; overflow: hidden; }
.hero-bg { position: absolute; inset: 0; z-index: 0; }
.hero-bg img { width: 100%; height: 100%; object-fit: cover; opacity: var(--hero-img-opacity); transition: opacity .5s; }
.hero::after { content: ""; position: absolute; inset: 0; background: var(--hero-grad); z-index: 1; }

/* text-left / deck-right composition; headline drops to the lower third (repositioned) */
.hero-grid {
  position: relative; z-index: 2; width: 100%;
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: end;
  gap: 32px; padding: 132px 0 72px;
}
.hero h1 { font-size: clamp(3.4rem, 12.5vw, 9.5rem); line-height: .84; font-weight: 700; }
.hero p { max-width: 540px; font-size: 1.12rem; margin: 24px 0 34px; opacity: .94; }
.hero-cta { display: flex; gap: 14px; flex-wrap: wrap; }

/* ---------- hero drop-deck (fanned cards, hover lift + sparkle motion) ---------- */
.hero-deck {
  align-self: center; justify-self: end;
  position: relative; width: clamp(300px, 30vw, 376px); height: clamp(380px, 44vh, 500px);
  perspective: 1400px; flex-shrink: 0;
}
.deck-card {
  --rot: 0deg; --tx: 0px; --ty: 0px; --lift: 0px;
  position: absolute; top: 50%; left: 50%;
  width: clamp(208px, 17vw, 248px); aspect-ratio: 3 / 4.05;
  border-radius: 20px; overflow: hidden; isolation: isolate;
  background: var(--ink-3); border: 1px solid var(--border-2);
  box-shadow: 0 30px 60px rgba(0,0,0,.5), 0 4px 14px rgba(0,0,0,.4);
  transform: translate(-50%, -50%) translate(var(--tx), var(--ty)) rotate(var(--rot)) translateY(var(--lift));
  /* smooth, deliberate ease — the fan opens and the hover lift glide gently */
  transition: transform .55s var(--ease), box-shadow .55s var(--ease), filter .55s var(--ease);
  will-change: transform;
  text-decoration: none; color: var(--cream);
}
/* Resting fan (--tx/--ty/--rot per card) is set by the JS fan in home.html so it
   scales to any number of featured products. Cards default to stacked (0,0,0) and
   ease open on load. The hover lift below is shared; the homepage refines it. */
.deck-card:hover { z-index: 30 !important; filter: brightness(1.05) !important;
  box-shadow: 0 44px 80px rgba(0,0,0,.6), 0 0 0 1px color-mix(in srgb, var(--flame-1) 55%, transparent), var(--glow); }
.deck-media { position: relative; width: 100%; height: 76%; overflow: hidden; }
.deck-media img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .6s var(--ease); }
.deck-card:hover .deck-media img { transform: scale(1.06); }
.deck-ph { width: 100%; height: 100%; display: grid; place-items: center; text-align: center; padding: 14px;
  font-family: var(--font-display); font-size: 1.1rem; background: linear-gradient(160deg, var(--ink-3), var(--ink-2)); color: var(--muted); }
.deck-badge {
  position: absolute; top: 11px; right: 11px; z-index: 2;
  font-family: var(--font-mono); font-size: .62rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  padding: 5px 9px; border-radius: 999px; color: var(--flame-ink); background: var(--flame);
  box-shadow: 0 6px 16px rgba(225,70,0,.4); max-width: 70%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.deck-info { padding: 12px 14px 0; display: flex; flex-direction: column; gap: 3px; }
.deck-name { font-family: var(--font-display); font-weight: 600; font-size: 1.02rem; line-height: 1.12;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* moving diagonal sheen across the lifted card — the one bit of motion we keep */
.deck-shine { position: absolute; inset: 0; z-index: 1; pointer-events: none; opacity: 0; transition: opacity .4s var(--ease);
  background: linear-gradient(105deg, transparent 36%, rgba(255,255,255,.22) 48%, transparent 60%);
  background-size: 280% 100%; }
.deck-card:hover .deck-shine { opacity: 1; animation: deckShine 1.4s var(--ease); }
@keyframes deckShine { from { background-position: 130% 0; } to { background-position: -40% 0; } }

/* ---------- section heading ---------- */
.sec-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 20px; margin-bottom: 38px; flex-wrap: wrap; }
.sec-head h1, .sec-head h2 { font-size: clamp(2.1rem, 5vw, 3.6rem); font-weight: inherit; text-wrap: balance; }
.sec-head .link { font-weight: 600; font-size: .85rem; text-transform: uppercase; letter-spacing: .05em; color: var(--flame-1); }
/* Editorial section number — a quiet "01 /" before the eyebrow gives the page a
   curated, gallery-index cadence instead of a flat stack of product rows. */
.sec-no { font-family: var(--font-mono); color: var(--muted-2); font-weight: 700; letter-spacing: .02em; margin-right: 4px; }

/* ---------- breadcrumb + back link (never leave a page with no way back) ---------- */
.breadcrumb { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: .82rem; margin-bottom: 22px; color: var(--muted); }
.breadcrumb a { color: var(--muted); transition: color .15s; }
.breadcrumb a:hover { color: var(--flame-1); }
.breadcrumb .sep { opacity: .45; }
.breadcrumb .current { color: var(--cream); font-weight: 600; }
.back-link { display: inline-flex; align-items: center; gap: 7px; font-size: .85rem; font-weight: 600; color: var(--muted); transition: color .15s, gap .15s; }
.back-link:hover { color: var(--flame-1); gap: 11px; }
.back-link svg { flex-shrink: 0; }

/* ---------- ethos / manifesto: the "feeling", not the sell ---------- */
.manifesto { text-align: center; padding: clamp(96px, 16vw, 200px) 0; position: relative; }
.manifesto .container { max-width: 1000px; }
.manifesto .eyebrow { display: block; margin-bottom: 26px; }
.manifesto-line {
  font-family: var(--font-display); font-weight: 600;
  font-size: clamp(2rem, 5.4vw, 4.4rem); line-height: 1.05;
  letter-spacing: -.015em; text-transform: none;
  max-width: 17ch; margin: 0 auto;
  text-wrap: balance;
}
.manifesto-sub { max-width: 560px; margin: 30px auto 0; font-size: 1.08rem; line-height: 1.7; }
/* a hairline flame rule under the statement — a signature, not a divider */
.manifesto-rule { width: 64px; height: 3px; margin: 38px auto 0; border-radius: 3px; background: var(--flame); box-shadow: var(--glow); }

/* ---------- product grid + card: THE DROP TICKET -------------------------
   The old card was a filled box with a border and the type packed inside it —
   readable, but it framed the garment the way a spreadsheet frames a row. This
   one is built as two nested enclosures: a shallow tray with a hairline edge,
   and the photograph seated inside it at a tighter, concentric radius. Nothing
   sits ON the card; the photograph sits IN it. That single change is most of
   the difference between "template" and "made".

   The type then moves OUT of the tray onto the page, which is what every house
   we benchmark against does: the image is the object, the name and price are a
   caption under it, not a panel attached to it.                              */
.product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(248px, 1fr)); gap: 22px; }
/* Fixed tracks, not auto-fill: the owner asked for an even grid, and auto-fill
   leaves an orphan row whenever the item count doesn't divide by the column
   count it happens to resolve to. Six live pieces read as a clean 3 x 2. */
.product-grid--even { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 26px; }

.card { display: flex; flex-direction: column; }
/* 2026-07-28: the tray is gone. It was a 5px padded, bordered, tinted panel
   around each photograph, and in a grid of six that border read as exactly what
   the owner called it — grid lines drawn between the products. The photograph
   is the product; a frame around it is the site talking over it.
   What's left is a bare positioning context: no padding, no border, no fill, so
   the image reaches all four edges of its cell. The lift on hover survives (it
   is what makes a card feel like an object), but it now lifts the picture
   itself, with the shadow cast by the image rather than by a panel behind it. */
.card-shell {
  position: relative;                 /* the buy rail positions against the image */
  border-radius: 14px;
  transition: transform .5s var(--ease), box-shadow .5s var(--ease);
}
.card:hover .card-shell {
  transform: translateY(-6px);
  box-shadow: 0 26px 50px -26px rgba(0,0,0,.55), 0 6px 14px -10px rgba(0,0,0,.32);
}
.card-media {
  position: relative; aspect-ratio: 4 / 5; overflow: hidden;
  border-radius: 14px; background: var(--ink-3);
}
.card-shot { position: absolute; inset: 0; display: block; }
/* Resting + hover images stack in the same frame so they can cross-fade. */
.card-media img, .card-media .cm-img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  transition: transform .8s var(--ease), opacity .5s var(--ease);
}
.card-media .cm-hover { opacity: 0; }
.card:hover .card-media .cm-img { transform: scale(1.06); }
.card-media.has-hover:hover .cm-primary { opacity: 0; }
.card-media.has-hover:hover .cm-hover { opacity: 1; }
.card-media .ph { display: flex; align-items: center; justify-content: center; height: 100%; font-family: var(--font-display); color: var(--muted-2); font-size: 1.6rem; letter-spacing: .1em; }

/* Over-image chrome is white-on-scrim in BOTH themes on purpose: it sits on a
   photograph, not on the page, so it must not follow the page's ink.
   Stamp and badge share one top-left row; the heart owns the top-right; the buy
   rail owns the bottom edge. Nothing overlaps anything, at any width. */
.card-tags { position: absolute; top: 10px; left: 10px; z-index: 4; display: flex; gap: 6px; align-items: center; }
.card-stamp {
  padding: 5px 9px; border-radius: 100px;
  font-family: var(--font-mono); font-size: .56rem; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase; color: #fff;
  background: rgba(12,10,10,.46); border: 1px solid rgba(255,255,255,.18);
}
/* THE THREE STATES, RANKED (2026-07-30). Only one badge ever prints per card
   (the template's if/elif chain), so the job is a glance-level hierarchy rather
   than a palette. NEW takes the flame: on a no-restock brand, "just landed" is
   the single most valuable thing a grid can say, and it should be the warmest
   pixel on the card. A discount is a price fact, so it reads as a paper sticker.
   Sold out is the only one allowed to look dead. */
.badge { padding: 5px 10px; border-radius: 100px; font-family: var(--font-mono); font-size: .6rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; white-space: nowrap; }
.badge-new {
  background: var(--flame); color: var(--flame-ink);
  box-shadow: 0 4px 16px rgba(225, 70, 0, .5);
}
/* Jade, not a white sticker — matches the product-detail page's price/discount
   treatment (see --pd-price above): money coming off the price is good news,
   and green is the one color this site already uses to say "safe to buy". A
   solid fill (not a translucent tint) so it stays legible over any photo,
   the same way .badge-new gets a solid fill rather than a soft one. */
.badge-sale { background: var(--jade); color: var(--jade-ink); }
.badge-soldout {
  background: rgba(10,9,9,.82); color: rgba(255,255,255,.9);
  border: 1px solid rgba(255,255,255,.28);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
.card-media .wish-btn { width: 34px; height: 34px; }
.card-media .wish-heart { width: 17px; height: 17px; }

/* == The buy rail =========================================================
   A size here is a submit button, so picking a size and adding to the cart are
   one tap. base.html catches the POST and answers with the drawer, so the
   visitor never leaves the grid — which is the whole point of putting it here.

   Default (touch) layout: its own strip under the photograph, inside the tray.
   Pointer devices get the floating-over-the-image treatment further down. No
   backdrop-filter either way: these scroll, and blurring a scrolling surface
   repaints it every frame for legibility a plain scrim already provides.      */
.card-add {
  display: flex; align-items: center; gap: 5px;
  margin-top: 5px; padding: 7px 8px; border-radius: 12px;
  background: color-mix(in srgb, var(--ink-3) 88%, transparent);
  border: 1px solid var(--border);
}
.ca-lead {
  font-family: var(--font-mono); font-size: .55rem; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase; color: var(--muted);
  padding-left: 2px; margin-right: auto;
}
.ca-size {
  min-width: 29px; height: 27px; padding: 0 6px; border-radius: 7px;
  border: 1px solid var(--border-2); background: transparent; color: var(--cream);
  font-family: var(--font-mono); font-size: .66rem; font-weight: 700; letter-spacing: .05em;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: background .18s var(--ease), color .18s var(--ease),
              border-color .18s var(--ease), transform .18s var(--ease);
}
.ca-size:hover { background: var(--cream); color: var(--ink); border-color: var(--cream); transform: translateY(-1px); }
.ca-size:active { transform: translateY(0) scale(.94); }
.ca-size:disabled { opacity: .3; cursor: not-allowed; text-decoration: line-through; }
.ca-size:disabled:hover { background: transparent; color: var(--cream); transform: none; }

/* Pointer devices: the rail lifts off the page and floats over the foot of the
   photograph, appearing only on hover so the garment is unobstructed at rest.
   Focus-within is included so a keyboard tab reveals it too. */
@media (hover: hover) and (pointer: fine) {
  .card-add {
    position: absolute; left: 12px; right: 12px; bottom: 12px; z-index: 5;
    margin-top: 0;
    background: linear-gradient(180deg, rgba(14,12,12,.80), rgba(14,12,12,.93));
    border-color: rgba(255,255,255,.14);
    box-shadow: 0 8px 22px -12px rgba(0,0,0,.7);
    opacity: 0; transform: translateY(8px);
    transition: opacity .34s var(--ease), transform .34s var(--ease);
  }
  .card:hover .card-add, .card:focus-within .card-add { opacity: 1; transform: none; }
  .ca-lead { color: rgba(255,255,255,.5); }
  .ca-size { border-color: rgba(255,255,255,.24); color: #fff; }
  .ca-size:hover { background: #fff; color: #16130f; border-color: #fff; }
  .ca-size:disabled:hover { background: transparent; color: #fff; }
}

.card-body { display: flex; flex-direction: column; gap: 5px; padding: 13px 0 0; }
.card-name { font-size: 1rem; font-weight: 600; font-family: var(--font-display); letter-spacing: 0; line-height: 1.22; color: var(--cream); }
.card-price { display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap; }
.card-price .now { font-family: var(--font-mono); font-weight: 700; font-size: .94rem; color: var(--cream); }
.card-price .was { font-family: var(--font-mono); color: var(--muted); text-decoration: line-through; font-size: .76rem; }
/* Concrete savings, not just a struck-through price — "-17%" persuades more
   than a bare crossed-out number (matches the corner badge's percentage).
   Jade, matching the corner badge and the product-detail price: this site's
   one color for "this is good news, not a warning." */
.card-price .pct-off { font-family: var(--font-mono); color: var(--jade-text); font-size: .72rem; font-weight: 700; }
/* A sold-out piece stays on the grid (it's proof the runs sell out) but it has
   to lose the argument at a glance: desaturated further than before and dropped
   a stop in brightness, so a scanning eye sorts the row into "buyable" and
   "gone" without reading a single word. Hover doesn't brighten it back. */
.card.is-out .card-media { filter: saturate(.18) brightness(.82); }
.card.is-out .card-name, .card.is-out .card-price .now { color: var(--muted); }
.stock-tag { display: inline-flex; align-items: center; gap: 6px; margin-top: 2px; color: var(--mint); font-size: .68rem; font-weight: 600; letter-spacing: .02em; }
.stock-tag::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--mint); }
.stock-tag.out { color: var(--muted); }
.stock-tag.out::before { background: var(--muted); }
/* (.stock-tag.low is styled with the low-stock pulse further down.) */

@media (max-width: 1000px) {
  .product-grid--even { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 600px) {
  .product-grid, .product-grid--even { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .card-shell { border-radius: 11px; }
  .card-media { border-radius: 11px; }
  .card-tags { top: 7px; left: 7px; gap: 4px; }
  .card-stamp { letter-spacing: .14em; padding: 4px 7px; }
  .badge { padding: 4px 8px; font-size: .58rem; }
  .card-media .wish-btn { width: 30px; height: 30px; top: 7px; right: 7px; }
  .card-add { gap: 4px; padding: 5px; border-radius: 9px; margin-top: 4px; }
  .ca-lead { display: none; }              /* two-up cards have no room for a label */
  .ca-size { flex: 1; min-width: 0; height: 30px; padding: 0; }
  .card-body { padding: 10px 0 0; }
  .card-name { font-size: .88rem; }
  .card-price .now { font-size: .84rem; }
}

/* ---------- collection band ---------- */
.collection-band { position: relative; border-radius: var(--radius); overflow: hidden; min-height: clamp(320px, 34vw, 420px); display: flex; align-items: flex-end; border: 1px solid var(--border); transition: border-color .4s var(--ease), transform .5s var(--ease); }
/* Brighter than before so even dark, moody product shots keep their texture
   instead of crushing to a flat black box. */
.collection-band img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: .82; transition: transform .9s var(--ease), opacity .5s var(--ease); }
.collection-band:hover { transform: translateY(-4px); border-color: var(--border-2); }
.collection-band:hover img { transform: scale(1.07); opacity: .92; }
/* Lighter at the top (let the image breathe), deep only where the type sits,
   with a whisper of flame so the wash reads as art direction, not a scrim. */
.collection-band::after {
  content: ""; position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(10,8,12,.04) 0%, rgba(10,8,12,.30) 46%, rgba(10,8,12,.88) 100%),
    radial-gradient(120% 80% at 12% 120%, color-mix(in srgb, var(--flame-2) 24%, transparent), transparent 60%);
  transition: opacity .4s var(--ease);
}
.collection-band .cb-body { position: relative; z-index: 2; padding: 30px; color: #fff; }
.collection-band h3 { font-size: clamp(1.9rem, 3vw, 2.4rem); }
.collection-band .cb-body .eyebrow, .collection-band .cb-body .muted { color: rgba(255,255,255,.82); }
.collection-band .cb-go { display: inline-flex; align-items: center; gap: 7px; margin-top: 14px; font-size: .76rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: #fff; opacity: 0; transform: translateY(6px); transition: opacity .4s var(--ease), transform .4s var(--ease); }
.collection-band:hover .cb-go { opacity: 1; transform: none; }

/* ---------- PRODUCT PAGES: flame + purchase scope ----------
   PDP content uses the storefront flame palette. Green is reserved for the
   purchase action and positive trust signals, so it reads as "safe to proceed"
   without recolouring shared header, footer, drawer or mobile navigation. */
.pd-section,
.pd-page .sticky-buy {
  --pd-accent-text: #FF9A3D;
  --pd-purchase: #147B43;
  --pd-purchase-hover: #0E6637;
  --pd-purchase-soft: rgba(20, 123, 67, .18);
  --pd-positive-text: #57C985;
  --pd-cart-flame: linear-gradient(118deg, #FF8A1E 0%, #F66A1B 48%, #F2541B 100%);
  /* THE PRICE (2026-08-05, owner: "not red or something, because they should
     feel good about buying it not get scared").

     The price was set in --pd-accent-text, which resolves to a burnt orange-red
     on paper. On a page whose whole job is to make a stranger comfortable
     handing cash to a courier, the largest number on the screen was rendered in
     the colour this site uses for "sold out" and "only 2 left" — an alarm
     colour doing the work of a reassurance.

     It is jade now, the same green already carrying "Buy it now", "Ready to
     ship" and the three trust ticks. One green means one thing on this page:
     safe to proceed. --jade-text already exists in both themes and is contrast-
     checked on paper, so this is a role assignment, not a new colour. */
  --pd-price: var(--jade-text);
}
[data-theme="light"] .pd-section,
[data-theme="light"] .pd-page .sticky-buy {
  /* Was #C43B00 — an amber that lands on the red side of the line under paper
     light. Pulled back to a clear amber so links, the size-guide control and
     the zoom affordance read as warm, never as a warning. */
  --pd-accent-text: #B0530A;
  --pd-positive-text: #147B43;
}

/* ---------- product detail ----------
   Rebuilt 2026-07-30. The page worked and read like a spec sheet: the product
   name was set at hero scale (clamp up to 3.6rem) next to its own photograph
   and lost the fight, the price was the same weight as body copy, and the
   commerce controls — size, quantity, two competing solid buttons, a wishlist
   pill, a WhatsApp button — sat loose on the page in the order they'd been
   added rather than in the order a buyer needs them.

   The shape now: the photograph holds the left column and STAYS there while
   the words scroll (`position: sticky`), because on a clothing page the image
   is the argument. Everything that takes money lives in one bounded surface
   (.pd-buy) in decision order — size, quantity, buy — with exactly one filled
   button in it. Reassurance sits directly under that, then the detail nobody
   reads until they're already convinced. */
.pd-section {
  padding-top: 34px;
  background:
    radial-gradient(80% 42% at 12% 8%, color-mix(in srgb, var(--flame-1) 6%, transparent), transparent 72%),
    var(--ink);
}
.pd {
  display: grid;
  /* minmax(0,…) and not plain fr: a long product name or a wide table would
     otherwise blow the column past its share and push the page sideways. */
  grid-template-columns: minmax(0, 1.06fr) minmax(0, .94fr);
  gap: clamp(34px, 5.2vw, 78px);
  align-items: start;
}

.pd-gallery { display: flex; flex-direction: column; gap: 12px; position: sticky; top: 96px; }
/* The whole garment has to be on screen when the page opens. A 4/5 frame at
   this column width is ~750px tall, which meant the picture was cut in half by
   the fold. Capping the height to what's left of the viewport after the header
   and breadcrumb — and CONTAINING rather than cropping — shows the entire
   photograph in screen one. */
.pd-main {
  border-radius: 14px; overflow: hidden; aspect-ratio: 4/5;
  max-height: calc(100svh - 210px);
  background: var(--ink-3); border: 1px solid var(--border);
  box-shadow: 0 28px 72px color-mix(in srgb, var(--ink) 55%, transparent);
  outline: none;
}
.pd-main img { width: 100%; height: 100%; object-fit: contain; }
.pd-main:focus-visible {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--flame-1) 48%, transparent),
              0 28px 72px color-mix(in srgb, var(--ink) 55%, transparent);
}

/* The control bar under the photograph: position indicators, the count, and
   the way into the inspector. One row doing three jobs, so the dots aren't a
   decorative strip of nothing (2026-07-30, owner request). */
.pd-bar { display: flex; align-items: center; gap: 14px; min-height: 32px; }
.pd-dots { display: flex; align-items: center; gap: 7px; }
.pd-dot {
  width: 7px; height: 7px; border-radius: 50%; padding: 0; border: 0;
  background: var(--border-2); cursor: pointer;
  transition: transform .2s var(--ease), background .2s var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.pd-dot:hover { background: var(--muted); }
.pd-dot.active { background: var(--flame-1); transform: scale(1.55); }
.pd-count { font-family: var(--font-mono); font-size: .7rem; letter-spacing: .06em; color: var(--muted); }
.pd-zoom {
  margin-left: auto; display: inline-flex; align-items: center; gap: 7px;
  background: none; border: 0; padding: 6px 0; cursor: pointer;
  font-family: var(--font-body); font-size: .7rem; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase; color: var(--muted);
  transition: color .2s;
}
.pd-zoom svg { width: 15px; height: 15px; }
.pd-zoom:hover { color: var(--pd-accent-text); }

.pd-thumbs { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.pd-thumbs button { padding: 0; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; aspect-ratio: 1; background: var(--ink-2); cursor: pointer; transition: border-color .2s, box-shadow .2s; }
.pd-thumbs button.active { border-color: var(--flame-1); box-shadow: 0 0 0 1px var(--flame-1); }
.pd-thumbs img { width: 100%; height: 100%; object-fit: cover; }

/* The wishlist control moves up beside the title, where a secondary action
   belongs — it used to sit between the price and the size picker, i.e. in the
   middle of the buying decision. */
/* The save control lines up with the product NAME, not with the eyebrow above
   it (owner call) — so the heart and the h1 share a centred row. */
.pd-name-row { display: flex; align-items: center; gap: 16px; }
.pd-name-row h1 { min-width: 0; }
.pd-info h1 {
  font-size: clamp(1.85rem, 3.1vw, 2.6rem);
  line-height: 1.05; letter-spacing: -.015em; margin: 9px 0 0;
}
/* The standfirst in the serif — the one place on this page with a different
   voice, which is what stops the column reading as a form. */
.pd-desc { font-family: var(--font-serif); font-size: 1.08rem; line-height: 1.55; color: var(--muted); margin: 15px 0 0; max-width: 46ch; }

.pd-commerce-meta {
  display: flex; align-items: flex-end; justify-content: space-between;
  flex-wrap: wrap; gap: 10px 22px; margin: 25px 0 0;
}
.pd-price { display: flex; align-items: baseline; flex-wrap: wrap; gap: 12px; margin: 0; }
.pd-price .now {
  font-family: var(--font-display); font-size: clamp(1.6rem, 2.2vw, 2rem);
  font-weight: 700; letter-spacing: -.01em; color: var(--pd-price);
  font-variant-numeric: tabular-nums;
}
.pd-price .was { color: var(--muted); text-decoration: line-through; font-size: .95rem; }
/* A bordered chip rather than bare text: a discount is a claim, so it gets a
   badge you can point at. Jade, not ember — money coming OFF the price is good
   news, and it used to be tinted in the same red as a sold-out warning. */
.pd-price .pct-off {
  font-family: var(--font-mono); font-size: .68rem; font-weight: 700;
  color: var(--pd-price); padding: 4px 9px; border-radius: 100px;
  background: color-mix(in srgb, var(--pd-purchase) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--pd-purchase) 40%, transparent);
}
.pd-commerce-meta > .stock-tag { margin: 0 0 5px; }
.pd-commerce-meta > .stock-tag:not(.low):not(.out) { color: var(--pd-positive-text); }
.pd-commerce-meta > .stock-tag:not(.low):not(.out)::before { background: var(--pd-positive-text); }

/* Everything that takes money, in one surface. */
.pd-buy {
  margin-top: 22px; padding: clamp(18px, 2vw, 24px);
  background:
    linear-gradient(145deg, color-mix(in srgb, var(--flame-1) 4%, var(--ink-2)), var(--ink-2));
  border: 1px solid var(--border-2); border-radius: 14px;
  box-shadow: 0 18px 50px color-mix(in srgb, var(--ink) 32%, transparent);
  display: flex; flex-direction: column; gap: 14px; position: relative; overflow: hidden;
}
.pd-buy::before {
  content: ""; position: absolute; inset: 0 0 auto; height: 3px;
  background: var(--flame);
}
.pd-buy-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.pd-buy-head > div:first-child { display: flex; flex-direction: column; gap: 4px; }
.pd-fit-note { color: var(--muted); font-family: var(--font-serif); font-size: .83rem; line-height: 1.35; }
.pd-link {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: inherit; font-size: .8rem; color: var(--pd-accent-text);
  text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px;
  transition: color .2s;
}
.pd-link:hover { color: var(--cream); }

.size-row { display: flex; gap: 9px; flex-wrap: wrap; margin: 0; border-radius: 4px; }
/* Flashed when a submit is blocked for having no size chosen — the toast says
   it, this shows where. */
/* Amber, not the #D9480F end of the flame ramp: this fires when someone taps
   Add to Cart before choosing a size, which is a nudge, not an error. */
.size-row.needs-pick { animation: pickNudge .5s var(--ease) 2; }
@keyframes pickNudge {
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
  50% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--flame-1) 55%, transparent); }
}
.size-opt {
  min-width: 46px; padding: 10px 13px; text-align: center;
  border: 1px solid var(--border-2); border-radius: 8px;
  font-weight: 600; font-size: .92rem; cursor: pointer; background: transparent;
  color: var(--cream);
  transition: border-color .15s var(--ease), background .15s var(--ease),
              color .15s var(--ease), transform .15s var(--ease);
}
.size-opt:hover { border-color: var(--cream); }
.size-opt:active { transform: scale(.96); }
/* Orange keeps selection and cart-building in the brand system. */
.size-opt.active { background: var(--pd-cart-flame); color: #17130A; border-color: var(--flame-2); }
.size-opt.disabled, .size-opt:has(input:disabled) { opacity: .3; cursor: not-allowed; text-decoration: line-through; }
.field-label { font-size: .75rem; letter-spacing: .13em; text-transform: uppercase; color: var(--muted); font-weight: 600; }

.qty { display: inline-flex; align-items: center; border: 1px solid var(--border-2); border-radius: 10px; overflow: hidden; flex: 0 0 auto; }
.qty button { width: 38px; height: 48px; background: transparent; border: 0; color: var(--cream); font-size: 1.15rem; cursor: pointer; transition: transform .14s var(--ease), color .14s var(--ease); }
.qty button:active { transform: scale(.9); }
.qty input { width: 36px; height: 48px; text-align: center; background: transparent; border: 0; color: var(--cream); font-weight: 600; font-size: 1rem; -moz-appearance: textfield; }
.qty input::-webkit-outer-spin-button, .qty input::-webkit-inner-spin-button { -webkit-appearance: none; }

/* Quantity and the primary action share a row, matched in height, so the buy
   button reads as the end of that sentence instead of a separate block. */
.pd-qty-row { display: flex; gap: 12px; align-items: stretch; }
.pd-actions { display: flex; flex: 1; min-width: 0; }
.pd-actions .btn { flex: 1; min-width: 0; }
.pd-buy .btn { border-radius: 10px; }
.pd-section .btn-flame,
.pd-page .sticky-buy .btn-flame {
  background: var(--pd-cart-flame); color: #17130A; border-color: var(--flame-2);
}
.pd-section .btn-purchase {
  position: relative; overflow: hidden;
  background: var(--pd-purchase); color: #fff; border-color: var(--pd-purchase);
  font-weight: 750;
  box-shadow: 0 12px 30px var(--pd-purchase-soft), inset 0 1px 0 rgba(255,255,255,.22);
}
.pd-section .btn-purchase:hover {
  background: var(--pd-purchase-hover); border-color: var(--pd-purchase-hover);
  transform: translateY(-1px);
  box-shadow: 0 16px 34px var(--pd-purchase-soft), inset 0 1px 0 rgba(255,255,255,.18);
}
.pd-section .btn-purchase:focus-visible {
  outline-color: var(--pd-positive-text);
}

/* One column, not two (2026-08-05). The box used to end on a row of two equal
   buttons — "Buy it now" beside a WhatsApp button — which made the last thing a
   buyer read a choice between two unfamiliar routes instead of a route. The
   purchase button now owns its row and WhatsApp drops to a quiet line under the
   box, where a question belongs: it is not a way to buy, it is a way to ask. */
.pd-alt { display: grid; gap: 10px; }
.pd-alt .btn { min-width: 0; }
.pd-ask {
  display: inline-flex; align-items: center; gap: 7px; align-self: center;
  font-size: .82rem; color: var(--muted);
  border-bottom: 1px solid transparent; padding-bottom: 1px;
  transition: color .16s var(--ease), border-color .16s var(--ease);
}
.pd-ask svg { color: var(--wa); flex-shrink: 0; }
.pd-ask:hover { color: var(--cream); border-color: color-mix(in srgb, var(--wa) 55%, transparent); }

/* The three doubts, ticked in the same semantic green as the purchase path. */
.pd-trust { list-style: none; padding: 0; margin: 26px 0 0; display: flex; flex-direction: column; gap: 11px; }
.pd-trust li { position: relative; padding-left: 27px; font-size: .88rem; line-height: 1.5; color: var(--muted); }
.pd-trust li::before {
  content: ""; position: absolute; left: 0; top: .18em; width: 17px; height: 17px; border-radius: 50%;
  background: color-mix(in srgb, var(--pd-purchase) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--pd-purchase) 48%, transparent);
}
.pd-trust li::after {
  content: ""; position: absolute; left: 5px; top: .42em; width: 6px; height: 3px;
  border-left: 1.6px solid var(--pd-positive-text); border-bottom: 1.6px solid var(--pd-positive-text);
  transform: rotate(-45deg);
}
.pd-trust b { color: var(--cream); font-weight: 600; }

.pd-soldout b { font-size: 1.05rem; }
.pd-soldout .muted { font-size: .88rem; margin: 0; }
.pd-restock { display: flex; gap: 10px; flex-wrap: wrap; }
.pd-restock input[type="email"] {
  flex: 1; min-width: 190px; padding: 12px 16px; border: 1px solid var(--border-2);
  border-radius: 100px; background: transparent; color: inherit; font: inherit;
}

.pd-help { margin-top: 20px; font-size: .85rem; color: var(--muted); }
.pd-help a { color: var(--pd-accent-text); text-decoration: underline; text-underline-offset: 3px; }
.pd-service-details { margin-top: 28px; }
.pd-facts { display: grid; gap: 10px; margin: 0; }
.pd-facts > div { display: grid; grid-template-columns: 64px 1fr; gap: 12px; }
.pd-facts dt { color: var(--cream); font-weight: 600; }
.pd-facts dd { margin: 0; }
/* "More from the drop" (2026-08-05). This heading inherited the global section
   scale — up to 57px of display caps — for a row of four cards the buyer has
   already half-decided not to look at. It is a shelf label now: the products
   below it are the content, and the words are there to say what shelf it is.
   The gap above it came down with it; 116px of nothing between the buy box and
   the rail was reading as the end of the page. */
.pd-related { padding: clamp(44px, 6vw, 76px) 0 0; }
.pd-related .sec-head { align-items: baseline; border-bottom: 1px solid var(--border); padding-bottom: 14px; margin-bottom: 26px; }
.pd-related .sec-head h2 { max-width: 22ch; font-size: clamp(1.15rem, 1.8vw, 1.45rem); letter-spacing: .01em; }
.pd-related .sec-link {
  color: var(--pd-accent-text); font-family: var(--font-mono); font-size: .68rem;
  font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  padding: 6px 0; transition: color .2s, transform .2s;
}
.pd-related .sec-link:hover { color: var(--cream); transform: translateX(3px); }
.acc-note { margin-top: 10px; font-size: .82rem; }

.accordion { margin-top: 28px; border-top: 1px solid var(--border); }
.accordion details { border-bottom: 1px solid var(--border); }
.accordion summary { padding: 17px 0; cursor: pointer; font-weight: 600; list-style: none; display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.accordion summary::-webkit-details-marker { display: none; }
/* The marker is chrome, not emphasis — it used to be flame, which put the
   brand's loudest colour on a "+" sign twelve times per page. */
.accordion summary::after { content: "+"; color: var(--muted); font-size: 1.25rem; font-weight: 400; line-height: 1; transition: color .2s; }
.accordion summary:hover::after { color: var(--pd-accent-text); }
.accordion details[open] summary::after { content: "–"; }
.accordion .acc-body { padding: 0 0 18px; color: var(--muted); }

/* ---------- cart / checkout ---------- */
.layout-2 { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: 40px; align-items: start; }
.panel { background: var(--ink-2); border: 1px solid var(--border); border-radius: var(--radius); padding: 28px; }
/* Order-summary sidebar stays in view instead of leaving a tall gap of bare
   page beneath a short 1-2 item cart/checkout form — the common case, not
   the edge case, for this store's typical order size. */
.layout-2 > aside.panel { position: sticky; top: 96px; }
@media (max-width: 880px) { .layout-2 > aside.panel { position: static; } }
.summary-row { display: flex; justify-content: space-between; padding: 9px 0; color: var(--muted); font-size: 1rem; }
.summary-row.total { color: var(--cream); font-weight: 700; font-size: 1.28rem; border-top: 1px solid var(--border); margin-top: 8px; padding-top: 16px; }
/* Delivery-zone note (2026-08-15): sits directly under the Delivery row and
   explains which of the two rates is being quoted. Pulled up tight against
   that row and de-emphasised — it annotates the line above rather than being
   a line of the bill itself, so it must not read as another charge. */
.summary-row.summary-note { margin-top: -9px; padding-top: 0; font-size: .82rem; color: var(--muted-2, var(--muted)); }
/* `display:flex` above outranks the [hidden] attribute's UA `display:none`, so
   the JS setting `.hidden = true` would otherwise do nothing at all. */
.summary-row[hidden] { display: none; }

/* Discount entry, both states (2026-08-05: "make the discount part more
   beautiful"). Unapplied is a labelled, bordered box rather than a bare
   input; applied is a real jade moment naming the savings, not a muted
   aside — same trust-role colour as the WhatsApp-confirm card on success.html.
   Solid border, not dashed (2026-08-05, second pass) — dashed reads as an
   unfinished drop-zone, not an input worth using. */
.disc-box {
  margin-top: 14px; border: 1px solid var(--border-2); border-radius: 14px;
  padding: 16px 18px;
}
.disc-box-label { display: block; font-size: .82rem; font-weight: 700; color: var(--muted); margin-bottom: 10px; }
.disc-box-row { display: flex; gap: 10px; }
.disc-box-row input[type="text"] { flex: 1; min-width: 0; text-transform: uppercase; }
.disc-apply-btn {
  flex-shrink: 0; background: var(--jade); border-color: var(--jade); color: #fff;
  font-weight: 700; padding-inline: 20px;
}
.disc-apply-btn:hover { filter: brightness(1.08); }
.disc-applied {
  margin-top: 14px; border: 1px solid var(--jade); border-radius: 12px;
  background: color-mix(in srgb, var(--jade) 10%, transparent);
  padding: 12px 14px; display: flex; align-items: center; gap: 10px;
}
.disc-applied > svg { color: var(--jade-text); flex-shrink: 0; }
.disc-applied-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.disc-applied-body b { font-size: .88rem; color: var(--cream); }
.disc-applied-body span { font-size: .8rem; color: var(--jade-text); font-weight: 600; }

/* Checkout is a working page, not an editorial section. Keep the decision
   surface close to the header and remove the two global exit shelves (footer
   and mobile tab bar); back-to-cart plus the three help links remain. */
.checkout-section {
  position: relative;
  padding: clamp(22px, 3vw, 40px) 0 clamp(42px, 6vw, 76px);
}
.checkout-page .site-footer { display: none; }
/* Transactional UI never waits on the global decorative reveal observer. */
.checkout-page .reveal,
.contact-page .reveal {
  opacity: 1; transform: none; transition: none;
}
.checkout-head {
  display: grid; gap: 12px;
  margin-bottom: clamp(20px, 2.4vw, 30px);
}
.checkout-headline {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(320px, 420px);
  align-items: end; gap: clamp(24px, 4vw, 64px);
}
/* Sized down from a 3.35rem display headline (2026-08-05, owner: "checkout
   word make it smaller") — this is a page you pass through, not a moment to
   announce. Matches the other utility pages' scale. */
.checkout-head h1 {
  font-size: clamp(1.55rem, 2.6vw, 2.05rem);
  line-height: 1.05; letter-spacing: -.01em;
}
.checkout-mode {
  margin: 8px 0 0;
  color: var(--muted);
  font-size: .94rem; line-height: 1.45;
}
.checkout-pay-note {
  display: grid; grid-template-columns: 20px minmax(0, 1fr); gap: 12px;
  padding: 3px 0 4px 20px;
  border-left: 1px solid var(--border-2);
}
.checkout-pay-note svg { margin-top: 2px; }
.checkout-pay-note strong {
  display: block;
  color: var(--cream); font-size: .9rem; line-height: 1.25;
}
.checkout-pay-note span {
  display: block; margin-top: 3px;
  color: var(--muted); font-size: .84rem; line-height: 1.45;
}
.checkout-layout {
  grid-template-columns: minmax(0, 1.45fr) minmax(320px, .85fr);
  gap: clamp(18px, 2.6vw, 36px);
}
.checkout-details,
.checkout-order { padding: clamp(22px, 2.3vw, 30px); font-weight: 500; }
/* A small brass mark on each panel head (2026-08-05, owner: "better
   coloring", but not more flame/orange) — the fill already used for price
   and identity elsewhere, so it reads as house colour, not a new one. */
.checkout-details h2,
.checkout-order h2 {
  display: flex; align-items: center; gap: 9px;
  margin-bottom: 20px;
  font-size: 1.16rem; line-height: 1;
}
.checkout-details h2::before,
.checkout-order h2::before {
  content: ''; width: 7px; height: 7px; border-radius: 50%;
  background: var(--brass); flex-shrink: 0;
}
.checkout-fields {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}
.checkout-fields .form-field--wide { grid-column: 1 / -1; }
.checkout-details .form-field label {
  margin-bottom: 7px;
  font-size: .86rem; font-weight: 600;
}
.checkout-details textarea { min-height: 92px; resize: vertical; }
.checkout-hint,
.checkout-payment-note {
  display: block; margin-top: 6px;
  color: var(--muted); font-size: .78rem; line-height: 1.45;
}
.checkout-payment-note { text-align: center; margin-top: 12px; }
.checkout-subtotal {
  border-top: 1px solid var(--border);
  margin-top: 8px; padding-top: 12px;
}
.checkout-order .place-order-inline { margin-top: 14px; }
.checkout-disabled { margin-top: 18px; opacity: .6; cursor: not-allowed; }
.checkout-links {
  display: flex; justify-content: center; gap: 18px; flex-wrap: wrap;
  margin-top: 22px;
  color: var(--muted); font-size: .76rem;
}
.checkout-links a {
  text-decoration: underline; text-underline-offset: 3px;
  transition: color .18s var(--ease);
}
.checkout-links a:hover { color: var(--cream); }

/* The only mobile checkout chrome: total + the one action the page exists for. */
.checkout-sticky { display: none; }
@media (max-width: 880px) {
  .checkout-section { padding: 20px 0 28px; }
  .checkout-headline { grid-template-columns: minmax(0, 1fr); gap: 15px; }
  .checkout-head h1 { font-size: clamp(1.5rem, 7vw, 1.85rem); }
  .checkout-mode { margin-top: 6px; font-size: .9rem; }
  .checkout-pay-note {
    padding: 2px 0 2px 14px;
    border-left: 2px solid color-mix(in srgb, var(--wa) 65%, var(--border));
  }
  .checkout-layout { grid-template-columns: minmax(0, 1fr); gap: 14px; }
  .checkout-details,
  .checkout-order { padding: 22px; }
  .checkout-fields { grid-template-columns: minmax(0, 1fr); }
  .checkout-fields .form-field--wide { grid-column: auto; }
  .checkout-page .bottom-nav { display: none; }
  .checkout-page {
    padding-bottom: calc(78px + env(safe-area-inset-bottom, 0px));
  }
  .checkout-sticky {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 97;
    display: flex; align-items: center; gap: 14px;
    padding: 11px 16px calc(11px + env(safe-area-inset-bottom, 0px));
    background: color-mix(in srgb, var(--ink) 94%, transparent);
    -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
    border-top: 1px solid var(--border);
    box-shadow: 0 -12px 30px rgba(0, 0, 0, .16);
  }
  .checkout-sticky .cs-total { display: flex; flex-direction: column; line-height: 1.15; }
  .checkout-sticky .cs-total span {
    color: var(--muted); font-size: .66rem;
    text-transform: uppercase; letter-spacing: .06em;
  }
  .checkout-sticky .cs-total b { font-size: 1.05rem; white-space: nowrap; }
  .checkout-sticky .btn { flex: 1; min-width: 0; padding-inline: 12px; }
  .place-order-inline { display: none; }
  .checkout-links { margin-top: 18px; }
}

.cart-item { display: grid; grid-template-columns: 86px minmax(0, 1fr) auto; gap: 16px; padding: 18px 0; border-bottom: 1px solid var(--border); align-items: center; }
.cart-item .ci-img { border-radius: var(--radius-sm); overflow: hidden; aspect-ratio: 1; background: var(--ink-3); }
.cart-item .ci-img img { width: 100%; height: 100%; object-fit: cover; }
.cart-item .ci-name { font-weight: 600; }
.cart-item .ci-meta { color: var(--muted); font-size: .85rem; }
.cart-item .ci-warn { margin-top: 6px; font-size: .82rem; font-weight: 600; color: var(--flame-1); }
.ci-qty-form { display: flex; gap: 12px; align-items: center; margin-top: 8px; }
.ci-end { display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }
.ci-line { font-weight: 800; font-family: var(--font-mono); }
/* The bin. It replaces the word "Remove": on a line that already carries a
   name, a size, a price and a stepper, one more piece of text was noise, and
   an icon is the faster read. It sits in a proper 40px target because it is
   the one destructive control here — and it is only this easy to reach because
   the undo toast catches the mistake. */
.ci-bin {
  display: grid; place-items: center; width: 40px; height: 40px;
  border: 1px solid var(--border); border-radius: 11px;
  background: transparent; color: var(--muted); cursor: pointer;
  transition: color .18s var(--ease), border-color .18s var(--ease), background .18s var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.ci-bin svg { width: 19px; height: 19px; }
.ci-bin:hover { color: var(--flame-1); border-color: color-mix(in srgb, var(--flame-1) 45%, transparent); background: color-mix(in srgb, var(--flame-1) 8%, transparent); }
/* == Customer account ====================================================
   The design job here is restraint. An account on this store is a convenience,
   not a membership, so the whole area is deliberately quieter than the shop: no
   photography, no accent fills, one column of plain surfaces. Anything more
   would imply the account matters more than it does, and the one thing that
   must stay true is that buying never requires it. */
.auth-wrap { max-width: 440px; margin: 0 auto; }
/* Above the form, never below it: someone who landed here by accident should
   learn they don't need an account before they start reading fields. */
.auth-guest {
  text-align: center; font-size: .88rem; color: var(--muted);
  margin-bottom: 18px; line-height: 1.6;
}
.auth-guest a { color: var(--cream); text-decoration: underline; text-underline-offset: 3px; }
.auth-card {
  background: var(--ink-2); border: 1px solid var(--border);
  border-radius: 18px; padding: clamp(24px, 4vw, 34px);
}
.auth-card h1 { font-size: clamp(1.8rem, 4vw, 2.3rem); margin: 6px 0 8px; }
.auth-sub { color: var(--muted); font-size: .9rem; line-height: 1.6; margin-bottom: 22px; }
.auth-form { display: flex; flex-direction: column; gap: 16px; }
.auth-form .field { display: flex; flex-direction: column; gap: 6px; }
.auth-form .field-label { font-size: .72rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); font-weight: 700; }
.auth-form input:not([type=checkbox]), .auth-form textarea, .auth-form select { width: 100%; }
.field-hint { display: block; font-style: normal; font-size: .76rem; color: var(--muted); line-height: 1.5; }
.field-err { display: block; font-style: normal; font-size: .78rem; color: var(--flame-1); font-weight: 600; }
/* Required-field marker (2026-08-05, owner: star what's required, don't
   caption what isn't). --ember-2 is a genuine red, distinct from the flame/
   brass accents everywhere else on the page, so it reads as the universal
   "required" convention rather than another brand accent. */
.req { color: var(--ember-2); margin-left: 3px; font-weight: 700; }
.auth-error {
  padding: 11px 14px; border-radius: 10px; font-size: .86rem; font-weight: 600;
  color: var(--flame-1); background: color-mix(in srgb, var(--flame-1) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--flame-1) 35%, transparent);
}
.auth-foot {
  display: flex; flex-direction: column; gap: 8px; align-items: center;
  margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--border);
  font-size: .85rem; color: var(--muted); text-align: center;
}
.auth-foot a { color: var(--cream); text-decoration: underline; text-underline-offset: 3px; }

/* THE SPLIT AUTH SHELL IS GONE (2026-07-29). .auth-split, .auth-aside,
   .auth-aside-h, .auth-perks, .auth-aside-fine and their 900px media query went
   with the perks panel the owner cut off both auth pages. The pages are a
   440px column again — .auth-wrap, which never left. */
.auth-section { padding-top: clamp(28px, 5vh, 56px); }
/* "Forgot it?" belongs beside the label of the field it is about, not stranded
   in the footer under the button — that is where the doubt actually occurs. */
.field-label--row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.fl-link {
  font-family: var(--font-body); font-size: .76rem; font-weight: 500;
  letter-spacing: 0; text-transform: none; color: var(--muted);
  text-decoration: underline; text-underline-offset: 3px;
}
.fl-link:hover { color: var(--flame-1); }
/* Reveal control, inside the field frame. */
.pw-wrap { position: relative; display: block; }
.pw-wrap input { padding-right: 48px !important; }
.pw-peek {
  position: absolute; top: 50%; right: 6px; transform: translateY(-50%);
  width: 36px; height: 36px; display: grid; place-items: center;
  border: 0; border-radius: 9px; background: none; cursor: pointer;
  color: var(--muted); transition: color .2s var(--ease), background .2s var(--ease);
}
.pw-peek:hover { color: var(--cream); background: color-mix(in srgb, var(--cream) 8%, transparent); }
.pw-peek .pw-off { display: none; }
.pw-peek.is-on .pw-on { display: none; }
.pw-peek.is-on .pw-off { display: block; }

/* == Sign-in: the phone / email segmented control (2026-07-29) =============
   The owner's call, and the pattern fabrilife.com and every serious store in
   this market uses: name the two credentials instead of hiding both behind one
   ambiguous box. Two named doors also means two correct keyboards, two correct
   `autocomplete` tokens, and an error that can point at the field it belongs to.

   The whole control is JS-only ON PURPOSE. Without the script the buttons would
   be furniture that does nothing, so it isn't rendered at all: the form falls
   back to both credential fields visible with their own labels, and the server
   takes whichever one is filled. `.seg-live` is added by the script, and it is
   the single switch between those two worlds. */
.seg { display: none; }
.seg-live .seg {
  position: relative; display: grid; grid-template-columns: 1fr 1fr; gap: 4px;
  padding: 4px; border-radius: 999px;
  background: var(--ink-3); border: 1px solid var(--border);
}
/* The pill is the only flame-filled surface in the account area, which is what
   makes the choice legible at a glance instead of needing to be read. */
.seg-ind {
  position: absolute; top: 4px; bottom: 4px; left: 0; width: 0;
  border-radius: 999px; background: var(--flame);
  box-shadow: var(--glow), inset 0 1px 0 rgba(255, 255, 255, .3);
  pointer-events: none;
  transition: transform .38s var(--ease), width .38s var(--ease);
}
.seg-btn {
  position: relative; z-index: 1;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  padding: 11px 14px; border: 0; border-radius: 999px; background: none; cursor: pointer;
  font-family: var(--font-body); font-size: .78rem; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); transition: color .25s var(--ease);
}
.seg-btn:hover { color: var(--cream); }
.seg-live .seg-btn.is-on { color: var(--flame-ink); }
.seg-btn:focus-visible { outline: 2px solid var(--flame-1); outline-offset: 2px; }
.seg-live .seg-panel { display: none; }
.seg-live .seg-panel.is-on { display: flex; animation: segIn .3s var(--ease) both; }
@keyframes segIn { from { opacity: 0; transform: translateY(-5px); } }
@media (prefers-reduced-motion: reduce) {
  .seg-ind { transition: none; }
  .seg-live .seg-panel.is-on { animation: none; }
}

/* The fixed country code. Not an input — nobody on this store has a number that
   starts with anything else, and leaving "+880" typeable is how one account ends
   up saved four ways. Sits inside the field's border so the two read as one
   control rather than a label that drifted. */
.tel-wrap { position: relative; display: block; }
.tel-cc {
  position: absolute; left: 1px; top: 1px; bottom: 1px; z-index: 1;
  display: flex; align-items: center; padding: 0 13px;
  border-radius: var(--radius-sm) 0 0 var(--radius-sm);
  border-right: 1px solid var(--border-2);
  background: color-mix(in srgb, var(--cream) 6%, transparent);
  font-family: var(--font-mono); font-size: .84rem; color: var(--muted);
  pointer-events: none; user-select: none;
}
.tel-wrap input { padding-left: 72px !important; letter-spacing: .04em; }

/* The card earns one accent: a flame hairline along its top edge, the same
   move the header uses when it sticks. Anything more and the account starts
   looking more important than the shop. */
.auth-card { position: relative; box-shadow: var(--shadow); }
.auth-card::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 2px;
  border-radius: 18px 18px 0 0; background: var(--flame);
}
.check-row { display: flex; align-items: center; gap: 10px; font-size: .9rem; }
.check-row input { width: auto; }

.acct-layout { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: clamp(24px, 4vw, 48px); align-items: start; }
.acct-rail { display: flex; flex-direction: column; }
.acct-rail a {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding: 14px 0; border-bottom: 1px solid var(--border);
  font-family: var(--font-display); font-size: 1.02rem; font-weight: 600;
  color: var(--muted); transition: color .2s var(--ease);
}
.acct-rail a i { font-family: var(--font-mono); font-style: normal; font-size: .62rem; letter-spacing: .12em; opacity: .55; }
.acct-rail a:hover, .acct-rail a.active { color: var(--cream); }
.acct-rail a.active i { color: var(--flame-1); opacity: 1; }
.acct-signout { padding-top: 16px; }
.acct-signout button {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: var(--font-mono); font-size: .68rem; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase; color: var(--muted);
  transition: color .2s var(--ease);
}
.acct-signout button:hover { color: var(--flame-1); }
.acct-intro { margin-bottom: 18px; font-size: .9rem; line-height: 1.6; }
.acct-back { margin-bottom: 16px; font-size: .85rem; }
.acct-back a { color: var(--muted); }
.acct-back a:hover { color: var(--cream); }

.acct-orders { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 12px; }
.acct-order a {
  display: block; padding: 16px 18px; border-radius: 14px;
  background: var(--ink-2); border: 1px solid var(--border);
  transition: border-color .25s var(--ease), transform .25s var(--ease);
}
.acct-order a:hover { border-color: var(--border-2); transform: translateY(-2px); }
.ao-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.ao-ref { font-family: var(--font-mono); font-weight: 700; letter-spacing: .06em; color: var(--cream); }
.ao-meta { display: flex; gap: 7px; flex-wrap: wrap; margin-top: 7px; font-size: .84rem; color: var(--muted); }
.ao-total { font-family: var(--font-mono); font-weight: 700; color: var(--cream); }

/* The account icon in the header. A dot, not a label — the header already has
   one labelled action and it is the cart. */
.acct-btn { position: relative; }
.acct-btn.active { color: var(--flame-1); }

/* Below 880px the bottom tab bar already carries Search and Saved, and the
   drawer carries the account — so on a phone these three are duplicates that
   cost 120px the header does not have. Leaving them in pushed the row to 401px
   inside a 390px viewport and gave every page horizontal overflow. The rule is
   the same one the whole header follows: an entry point exists once per width,
   not once per component. */
@media (max-width: 880px) {
  .header-actions > .wish-link,
  .header-actions > .acct-btn { display: none; }
}

/* Saved: same 40px frame as the other icon actions, with a small count that only
   appears once something is in it. Silence when empty is the rule here. */
.wish-link { position: relative; }
.wish-link.active { color: var(--flame-1); }
.wish-link .wish-count {
  position: absolute; top: 3px; right: 3px; min-width: 15px; height: 15px;
  padding: 0 4px; border-radius: 100px;
  background: var(--flame-1); color: #fff;
  font-family: var(--font-mono); font-size: .55rem; font-weight: 700; line-height: 15px;
  text-align: center;
}
.acct-dot {
  position: absolute; top: 7px; right: 7px; width: 6px; height: 6px;
  border-radius: 50%; background: var(--flame-1);
}
/* The drawer carries the same destination as a full row; on desktop the icon
   already covers it, so the row would just be a duplicate link. */
.nav-acct-row { display: none; }

/* The post-order offer. Framed as a question with an obvious way to ignore it —
   the order is already placed and nothing here is a condition of anything. */
.post-order-account {
  display: flex; align-items: center; justify-content: space-between;
  gap: 22px; flex-wrap: wrap; text-align: left;
  margin-top: 30px; padding: 22px 24px; border-radius: 16px;
  background: var(--ink-2); border: 1px solid var(--border);
}
.post-order-account h2 { font-size: 1.15rem; margin-bottom: 6px; }
.post-order-account p { font-size: .87rem; color: var(--muted); line-height: 1.6; max-width: 46ch; }

@media (max-width: 880px) {
  .acct-layout { grid-template-columns: minmax(0, 1fr); gap: 26px; }
  .nav-acct-row { display: flex; }
  .post-order-account { flex-direction: column; align-items: stretch; text-align: left; }
  .post-order-account .btn { width: 100%; }
}

/* Checkout's order lines are editable in place, so each carries a compact
   stepper and bin under its name rather than sending the shopper back a page. */
.co-line { padding: 10px 0; border-bottom: 1px solid var(--border); }
.co-line:last-of-type { border-bottom: 0; }
.co-line.is-busy { opacity: .55; }
.co-line-body { display: grid; grid-template-columns: 54px minmax(0, 1fr); gap: 12px; }
.co-line-thumb {
  width: 54px; aspect-ratio: 4 / 5; overflow: hidden;
  display: grid; place-items: center;
  border-radius: 8px; background: var(--ink-3);
  color: var(--muted); font-family: var(--font-display); font-size: 1.1rem;
}
.co-line-thumb img { width: 100%; height: 100%; object-fit: cover; }
.co-line-main { min-width: 0; }
.co-line-top { display: flex; justify-content: space-between; gap: 12px; align-items: baseline; }
.co-line-name { min-width: 0; font-size: 1.02rem; overflow-wrap: anywhere; }
.co-line-name i { color: var(--muted); font-style: normal; }
.co-line-total { font-family: var(--font-mono); font-weight: 700; font-size: 1.02rem; white-space: nowrap; }
.co-line-ctl { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.qty-sm { display: inline-flex; }
.qty-sm button { width: 32px; height: 32px; font-size: 1rem; }
.qty-sm input { width: 34px; height: 32px; font-size: .86rem; }
.co-bin { width: 32px; height: 32px; border-radius: 9px; }
.co-bin svg { width: 16px; height: 16px; }

.cart-emptied-note { display: none; padding: 26px 0; }
.cart-emptied .cart-emptied-note { display: block; }
.cart-item.is-busy { opacity: .55; }
.cart-item.is-going { opacity: 0; transform: translateX(14px); transition: opacity .2s var(--ease), transform .2s var(--ease); }

/* Undo. A removal you can take back is a removal you're not afraid to make —
   which is the whole reason the bin can be a single unconfirmed tap. */
.cart-undo {
  position: fixed; left: 50%; bottom: calc(24px + env(safe-area-inset-bottom, 0px));
  z-index: 120; display: flex; align-items: center; gap: 16px;
  padding: 13px 15px 13px 19px; border-radius: 14px;
  background: var(--ink-2); border: 1px solid var(--border-2);
  box-shadow: 0 20px 44px -20px rgba(0,0,0,.6);
  font-size: .88rem; color: var(--cream); max-width: min(440px, calc(100vw - 32px));
  transform: translate(-50%, 16px); opacity: 0;
  transition: opacity .26s var(--ease), transform .26s var(--ease);
}
.cart-undo.in { opacity: 1; transform: translate(-50%, 0); }
.cart-undo .cu-text { flex: 1; min-width: 0; }
.cart-undo .cu-btn {
  flex: none; background: none; border: 0; cursor: pointer; padding: 4px 2px;
  font-family: var(--font-mono); font-size: .72rem; font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase; color: var(--flame-1);
}
.cart-undo .cu-btn:hover { text-decoration: underline; }
@media (max-width: 880px) {
  /* Clear the bottom tab bar. */
  .cart-undo { bottom: calc(78px + env(safe-area-inset-bottom, 0px)); }
}
@media (prefers-reduced-motion: reduce) {
  .cart-undo, .cart-item.is-going { transition: none; }
}
.link-danger { background: none; border: 0; color: var(--muted); cursor: pointer; font-size: .8rem; text-decoration: underline; padding: 0; }
.link-danger:hover { color: var(--flame-1); }

.form-grid { display: grid; gap: 16px; }
.form-field label { display: block; margin-bottom: 6px; }
/* `.field` is the account area's wrapper; it shares this control styling so a
   sign-in box can't drift into looking like a different product from checkout. */
.form-field input:not([type=checkbox]):not([type=radio]):not([type=file]),
.form-field textarea,
.form-field select,
.field input:not([type=checkbox]):not([type=radio]):not([type=file]),
.field textarea,
.field select {
  width: 100%; background: var(--ink-3); border: 1px solid var(--border-2); border-radius: var(--radius-sm);
  color: var(--cream); padding: 13px 15px; font-family: inherit; font-size: .98rem; transition: border-color .2s;
}
.form-field select { -webkit-appearance: none; appearance: none; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239d99a6' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 14px center; padding-right: 38px; }
/* Focus used to be a 1px border colour change and nothing else — on a dark
   surface at arm's length that is close to invisible, and it was the only
   signal telling someone which of several boxes their keystrokes were going
   into. The border still changes; a soft ember halo now carries it. */
.form-field input:focus, .form-field textarea:focus, .form-field select:focus,
.field input:focus, .field textarea:focus, .field select:focus {
  border-color: var(--flame-1);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--flame-1) 18%, transparent);
}
/* Suppress the default ring only for mouse/programmatic focus; :focus-visible re-adds it for keyboard. */
.form-field input:focus:not(:focus-visible), .form-field textarea:focus:not(:focus-visible), .form-field select:focus:not(:focus-visible),
.field input:focus:not(:focus-visible), .field textarea:focus:not(:focus-visible), .field select:focus:not(:focus-visible) { outline: none; }
.form-field input[type=file] { width: 100%; color: var(--muted); font-size: .9rem; }
.form-field input[type=file]::file-selector-button {
  background: var(--ink-3); border: 1px solid var(--border-2); border-radius: var(--radius-sm);
  color: var(--cream); padding: 9px 14px; margin-right: 12px; cursor: pointer; font-family: inherit;
}
/* inline checkbox toggles */
.check-row { display: flex; gap: 22px; flex-wrap: wrap; }
.check-row label { display: flex; align-items: center; gap: 9px; cursor: pointer; font-weight: 600; color: var(--cream); }
.check-row input[type=checkbox] { width: 20px; height: 20px; accent-color: var(--flame-1); cursor: pointer; }
.form-field .errorlist { color: #ff6a4d; list-style: none; padding: 0; margin: 6px 0 0; font-size: .82rem; }

/* Contact form + compact direct-channel index, shared by About and Contact. */
.contact-layout {
  display: grid;
  grid-template-columns: minmax(0, 1.38fr) minmax(280px, .62fr);
  gap: clamp(28px, 4vw, 58px);
  align-items: start;
  margin-top: 40px;
}
.contact-form-card { padding: clamp(24px, 3vw, 36px); }
.contact-form-intro { margin-bottom: 24px; }
.contact-form-intro h3 { margin: 0 0 7px; font-size: 1.45rem; }
.contact-form-intro p { max-width: 52ch; margin: 0; color: var(--muted); font-size: .94rem; }
.contact-form-card .form-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.contact-form-card .form-field-wide { grid-column: 1 / -1; }
.contact-field-help { display: block; margin-top: 4px; font-size: .78rem; }
.contact-submit { width: 100%; margin-top: 18px; }

.contact-channels {
  border-top: 1px solid color-mix(in srgb, var(--flame-1) 24%, var(--border));
}
.contact-channels-label {
  margin: 0;
  padding: 13px 0;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: .58rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
}
/* The two messaging apps a Bangladeshi buyer already has open — real buttons,
   each in its own brand colour, not folded into the quiet link list below. */
.contact-quick { display: flex; flex-direction: column; gap: 10px; margin: 12px 0 6px; }
.contact-quick-btn { width: 100%; justify-content: flex-start; gap: 10px; padding: 13px 18px; font-size: .92rem; }
.contact-quick-btn svg { flex-shrink: 0; }
.contact-quick-note { margin: 0 0 10px; color: var(--muted); font-size: .78rem; }
.contact-channel {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto 14px;
  gap: 14px;
  align-items: center;
  padding: 18px 0;
  color: var(--cream);
  border-top: 1px solid var(--border);
  text-decoration: none;
}
.contact-channel > span { display: grid; gap: 3px; min-width: 0; }
.contact-channel b { font-size: .9rem; }
.contact-channel small { color: var(--muted); font-size: .75rem; }
.contact-channel strong {
  max-width: 19ch;
  overflow-wrap: anywhere;
  color: var(--muted);
  font-family: var(--font-mono);
  font-size: .62rem;
  line-height: 1.35;
  text-align: right;
}
.contact-channel i {
  font-style: normal;
  transition: transform .45s cubic-bezier(.2,.8,.2,1);
}
.contact-channel:hover b { color: var(--flame-1); }
.contact-channel:hover i { transform: translate(2px, -2px); }
.contact-channel:focus-visible { outline: 2px solid var(--flame-1); outline-offset: 5px; }
.contact-delivery-note {
  margin: 0;
  padding: 16px 0;
  color: var(--muted);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid color-mix(in srgb, var(--flame-1) 24%, var(--border));
  font-size: .82rem;
  line-height: 1.5;
}
@media (max-width: 860px) {
  .contact-layout { grid-template-columns: 1fr; }
  .contact-form-card .form-grid { grid-template-columns: 1fr; }
  .contact-form-card .form-field-wide { grid-column: auto; }
}
.cod-banner { display: flex; gap: 12px; align-items: center; background: color-mix(in srgb, var(--wa) 12%, transparent); border: 1px solid color-mix(in srgb, var(--wa) 35%, transparent); border-radius: var(--radius-sm); padding: 14px 16px; margin-bottom: 18px; }

/* ---------- messages / notifications ----------
   Now that hearts, cart adds and drawer removals all speak through this shelf
   (see ccToast in base.html), it gets treated as a component rather than a
   Django-messages afterthought: a card on the same material as the nav, with a
   3px status spine on the leading edge doing the colour-coding. The spine is
   what lets the card itself stay neutral — a full-tinted green panel is the
   look every bootstrap site has, and it fights the photography. */
.messages { position: fixed; top: 88px; right: 22px; z-index: 200; display: flex; flex-direction: column; gap: 10px; max-width: 360px; pointer-events: none; }
.toast {
  position: relative; display: flex; align-items: flex-start; gap: 12px;
  padding: 14px 16px 14px 20px; border-radius: var(--radius-sm);
  font-size: .9rem; line-height: 1.45; letter-spacing: .005em;
  box-shadow: var(--shadow); border: 1px solid var(--border-2);
  background: color-mix(in srgb, var(--ink-2) 88%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.3); backdrop-filter: blur(14px) saturate(1.3);
  overflow: hidden; pointer-events: auto; animation: toast-in .34s var(--ease) both;
}
/* The status spine. Default is the brand flame, so a plain notice still reads
   as this store's and not as chrome. */
.toast::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--flame-1);
}
.toast.success::before { background: var(--mint); }
.toast.error::before { background: #FF503C; }
.toast.success { border-color: color-mix(in srgb, var(--mint) 34%, var(--border-2)); }
.toast.error { border-color: rgba(255, 80, 60, .45); }
.toast-msg { flex: 1; color: var(--cream); }
.toast-close {
  flex: none; background: none; border: 0; color: var(--muted); font-size: 1.2rem;
  line-height: 1; cursor: pointer; padding: 0 2px; transition: color .18s ease, transform .18s var(--ease);
}
.toast-close:hover { color: var(--cream); transform: scale(1.12); }
@keyframes toast-in { from { opacity: 0; transform: translateX(16px) scale(.97); } to { opacity: 1; transform: none; } }
/* Inline toasts (track page, panel login) are printed into the flow as form
   errors — they must not float or blur behind anything. */
.toast[style*="static"] { -webkit-backdrop-filter: none; backdrop-filter: none; background: var(--ink-2); }

/* Submit-in-progress button state (double-submit guard) */
.is-submitting { opacity: .7; cursor: progress; position: relative; }
.is-submitting[disabled] { pointer-events: none; }

/* Free-delivery progress nudge (cart) */
.ship-nudge { margin-top: 14px; font-size: .85rem; color: var(--muted); background: color-mix(in srgb, var(--flame-1) 8%, transparent); border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px 12px; }
.ship-nudge b { color: var(--cream); }
.ship-nudge.done { color: var(--mint); background: color-mix(in srgb, var(--mint) 12%, transparent); border-color: color-mix(in srgb, var(--mint) 40%, transparent); }
.ship-bar { display: block; height: 6px; margin-top: 8px; border-radius: 999px; background: var(--border-2); overflow: hidden; }
.ship-bar > span { display: block; height: 100%; background: linear-gradient(90deg, var(--flame-2), var(--flame-1)); transition: width .3s ease; }

/* ---------- Accessibility: visible keyboard focus + skip link ---------- */
:focus-visible { outline: 2px solid var(--flame-1); outline-offset: 2px; border-radius: 4px; }
.skip-link { position: absolute; left: -9999px; top: 0; z-index: 1000; background: var(--ink-2); color: var(--cream); padding: 10px 16px; border: 1px solid var(--border-2); border-radius: 0 0 8px 0; }
.skip-link:focus { left: 0; }
@media (prefers-reduced-motion: reduce) { .toast { animation: none; } }

/* ---------- order tracking: status pill + progress stepper ---------- */
.status-pill {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 7px 15px; border-radius: 999px;
  font-size: .82rem; font-weight: 700; letter-spacing: .03em;
  border: 1px solid currentColor; white-space: nowrap;
  background: color-mix(in srgb, currentColor 10%, transparent);
}
.status-pill::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: currentColor; }
.status-pill.pending   { color: #c79100; }
.status-pill.confirmed { color: #2f80ed; }
.status-pill.shipped   { color: #14b8a6; }
.status-pill.delivered { color: #2faf63; }
.status-pill.cancelled { color: #e0533c; }

.order-steps { display: flex; list-style: none; padding: 0; margin: 6px 0 26px; }
.order-step {
  flex: 1; position: relative; text-align: center;
  font-size: .76rem; font-weight: 600; letter-spacing: .02em; color: var(--muted);
}
.order-step::before {
  content: ""; position: absolute; top: 13px; left: -50%; width: 100%; height: 2px;
  background: var(--border-2); z-index: 0;
}
.order-step:first-child::before { display: none; }
.order-step .os-dot {
  position: relative; z-index: 1; width: 28px; height: 28px; border-radius: 50%;
  margin: 0 auto 9px; display: grid; place-items: center;
  background: var(--ink-3); border: 2px solid var(--border-2); color: #fff;
  transition: background .25s var(--ease), border-color .25s var(--ease), box-shadow .25s var(--ease);
}
.order-step .os-dot svg { width: 15px; height: 15px; opacity: 0; transition: opacity .2s; }
.order-step.done { color: var(--cream); }
.order-step.done::before { background: var(--flame); }
.order-step.done .os-dot { background: var(--flame); border-color: transparent; }
.order-step.done .os-dot svg { opacity: 1; }
.order-step.current .os-dot { box-shadow: 0 0 0 5px color-mix(in srgb, var(--flame-1) 22%, transparent); }

.cancel-note {
  background: color-mix(in srgb, #e0533c 12%, transparent);
  border: 1px solid color-mix(in srgb, #e0533c 38%, transparent);
  border-radius: var(--radius-sm); padding: 16px 18px; margin: 6px 0 24px; color: var(--cream);
}

/* ---------- empty / content ---------- */
.empty { text-align: center; padding: 90px 0; }
.empty h2 { font-size: 2.6rem; margin-bottom: 12px; }
.content-narrow { max-width: 780px; margin: 0 auto; }
.content-narrow h1 { font-size: clamp(2.8rem, 7vw, 5.2rem); margin-bottom: 20px; }
.content-narrow p { color: var(--cream); opacity: .9; margin-bottom: 18px; }

/* ---------- utility-page scale (2026-08-05) ----------
   Every scale above this line was written for the editorial pages — home, the
   manifesto, about — where a 5rem headline IS the content and the air around it
   is doing work. The transactional pages inherited that scale by accident, and
   on them it is the opposite of premium: an 83px "SAVED ITEMS" over an empty
   grid, or a 57px "YOUR CART" over two rows, announces the furniture instead of
   the goods. The owner's diagnosis was exact — big writing, big empty space,
   and the thing you came for pushed below the fold.

   So the working pages get their own scale, opted into with a body class rather
   than applied globally: a cart, a sign-in box and a saved list are chrome
   around a task, and chrome is set at chrome size. Nothing here changes a
   single editorial page.

   Applied by: cart, saved, sign-in, join, collection detail, order confirmed,
   receipt unlock. */
.util-page .section { padding: clamp(26px, 3.4vw, 46px) 0 clamp(46px, 6vw, 76px); }
.util-page .sec-head { margin-bottom: clamp(18px, 2vw, 26px); align-items: baseline; }
.util-page .sec-head h1,
.util-page .sec-head h2 { font-size: clamp(1.45rem, 2.6vw, 1.95rem); letter-spacing: -.005em; }
.util-page .content-narrow h1 { font-size: clamp(1.75rem, 3.4vw, 2.5rem); margin-bottom: 10px; }
/* An empty state is a signpost, not a monument: it should fit on a phone
   screen with the button visible without scrolling. */
.util-page .empty { padding: clamp(38px, 6vw, 58px) 0; }
.util-page .empty h2 { font-size: clamp(1.3rem, 2.4vw, 1.65rem); margin-bottom: 8px; }
.util-page .empty .muted { font-size: .92rem; max-width: 42ch; margin-inline: auto; }
.util-page .breadcrumb { margin-bottom: 14px; }
.util-page .panel { padding: clamp(18px, 2vw, 24px); }
.util-page .summary-row.total { font-size: 1.05rem; padding-top: 13px; }

/* The collection header: a band, not a centred monument. The name, one line of
   tagline, and the count of what is below it — then the grid starts. The old
   centred stack put ~320px of type and air between the breadcrumb and the first
   product on a laptop. */
.coll-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 14px 32px; flex-wrap: wrap;
  padding-bottom: clamp(14px, 1.8vw, 20px); margin-bottom: clamp(22px, 3vw, 34px);
  border-bottom: 1px solid var(--border);
}
.coll-head h1 { font-size: clamp(1.6rem, 3.2vw, 2.35rem); margin: 6px 0 0; letter-spacing: -.01em; }
.coll-head-l { min-width: 0; max-width: 56ch; }
.coll-tagline { font-family: var(--font-serif); color: var(--muted); font-size: .98rem; line-height: 1.5; margin: 9px 0 0; }
/* Now sits below the grid (2026-08-05) — the long-form copy, not competing
   with the tagline for the space right above the first product photo. */
.coll-desc { color: var(--muted); font-size: .85rem; line-height: 1.65; margin: clamp(28px, 4vw, 44px) 0 0; padding-top: clamp(20px, 2.6vw, 28px); border-top: 1px solid var(--border); max-width: 62ch; }
.coll-count {
  font-family: var(--font-mono); font-size: .68rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--muted);
  white-space: nowrap; padding-bottom: 4px;
}

/* Receipt unlock — one field, so the card is sized for one field. */
.unlock-card { max-width: 400px; margin: 0 auto; text-align: center; }
.unlock-card h1 { font-size: clamp(1.45rem, 3vw, 1.85rem); margin: 8px 0 6px; }
.unlock-sub { color: var(--muted); font-size: .9rem; line-height: 1.6; margin: 0 0 20px; }
.unlock-form { display: grid; gap: 14px; text-align: left; }
.unlock-foot { color: var(--muted); font-size: .82rem; margin: 16px 0 0; }
.unlock-foot a { color: var(--flame-1); }
/* ---------- value props (the promise) ---------- */
/* min 200px (was 240) so three cards fit 3-up inside content-narrow — the
   delivery page rendered 2+1 with an orphaned card; auto-fit collapses unused
   tracks so wider containers (home) are unaffected. */
.value-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 22px; margin-top: 34px; }

.value-card {
  position: relative; overflow: hidden; isolation: isolate;
  background:
    linear-gradient(180deg, color-mix(in srgb, var(--ink-2) 100%, transparent) 0%, var(--ink-2) 100%);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 30px 28px 32px;
  transition: transform .4s var(--ease), border-color .4s, box-shadow .4s;
}
/* gradient hairline that ignites along the top edge on hover */
.value-card::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 2px; border-radius: var(--radius) var(--radius) 0 0;
  background: var(--flame); transform: scaleX(0); transform-origin: left; opacity: .9;
  transition: transform .5s var(--ease);
}
/* a faint flame wash that blooms from the icon corner on hover */
.value-card::after {
  content: ""; position: absolute; left: -10%; top: -30%; width: 60%; height: 80%; z-index: -1;
  background: radial-gradient(circle, color-mix(in srgb, var(--flame-1) 22%, transparent) 0%, transparent 70%);
  opacity: 0; transition: opacity .5s var(--ease); pointer-events: none;
}
.value-card:hover {
  transform: translateY(-6px);
  border-color: color-mix(in srgb, var(--flame-1) 32%, var(--border-2));
  box-shadow: var(--shadow);
}
.value-card:hover::before { transform: scaleX(1); }
.value-card:hover::after { opacity: 1; }

/* watermark index number, top-right */
.value-num {
  position: absolute; top: 18px; right: 22px;
  font-family: var(--font-mono); font-size: 1rem; letter-spacing: .1em;
  color: var(--muted); opacity: .45; transition: color .4s, opacity .4s;
}
.value-card:hover .value-num { color: var(--flame-1); opacity: .9; }

.value-ico {
  position: relative; display: inline-grid; place-items: center;
  width: 54px; height: 54px; border-radius: 15px; margin-bottom: 20px;
  color: var(--flame-ink); background: var(--flame);
  box-shadow: 0 10px 26px -8px color-mix(in srgb, var(--flame-2) 70%, transparent),
              inset 0 1px 0 rgba(255,255,255,.25);
  transition: transform .5s var(--ease), box-shadow .5s;
}
.value-card:hover .value-ico {
  transform: translateY(-2px) rotate(-4deg) scale(1.04);
  box-shadow: 0 16px 34px -8px color-mix(in srgb, var(--flame-2) 80%, transparent),
              inset 0 1px 0 rgba(255,255,255,.3);
}
.value-card h3 { font-size: 1.3rem; margin-bottom: 9px; letter-spacing: -.01em; }
.value-card p { font-size: .97rem; line-height: 1.6; max-width: 30ch; }
.value-card b { overflow-wrap: anywhere; }   /* safety: long handles/URLs wrap inside the card */
/* bottom accent rule that draws in on hover */
.value-edge {
  display: block; height: 1px; width: 34px; margin-top: 20px; border-radius: 2px;
  background: var(--flame); opacity: .5;
  transition: width .5s var(--ease), opacity .4s;
}
.value-card:hover .value-edge { width: 70px; opacity: 1; }

/* ---------- footer ---------- */
.site-footer { border-top: 1px solid var(--border); background: var(--ink-2); margin-top: 40px; }
/* Two columns, not three: the WhatsApp card that held the third is gone and its
   one useful thing — the number — moved into the channel rail on the left. The
   left column keeps the larger share because the maker's mark, the line and the
   channels all live in it; the page list only needs its own width. */
.footer-top {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .8fr);
  gap: clamp(32px, 5vw, 72px); padding: clamp(48px, 7vw, 86px) 0 clamp(30px, 4vw, 48px);
  align-items: start;
}
.site-footer a { color: var(--muted); }
.site-footer a:hover { color: var(--cream); }

/* THE CAPTURE BAND IS GONE (2026-07-29). .fx-join, the whole .fxj-* family and
   the .drop-msg response line — about 100 lines — went with the form itself when
   the owner pulled the email ask off every surface. Nothing else referenced them,
   so they were pure weight in a stylesheet every page loads. Git has the band if
   a capture ever earns its way back. */

/* ---- maker's mark ---- */
/* Both supplied lockups are opaque. Rather than crop them, each is blended into
   its own ground: screen erases the black plate on dark, multiply erases the
   white plate on light. The artwork itself is untouched. */
/* Both supplied lockups carry their own generous margin inside the file, so the
   mark optically hangs low against the top of the page list beside it. Pulled up
   by roughly that margin (owner: "take it a little upper") rather than cropping
   the artwork, which stays exactly as supplied. */
.footer-logo { width: clamp(180px, 21vw, 250px); height: auto; display: none; margin-top: -22px; }
/* No blend mode on either lockup: both are keyed transparent PNGs now (see
   base.html). The light lockup used to rely on `mix-blend-mode: multiply`
   against an opaque white-background PNG — the same fragile trick already
   documented and removed from the dark lockup below, for the same reason: a
   blend only erases a plate against an EXACT flat-colour match, and any
   mismatch (compositing layer, a background that isn't pure #fff) leaves the
   plate's true rectangular edge faintly visible. Fixed 2026-08-20 by keying
   the source PNG's white background to real alpha instead. */
html:not([data-theme="light"]) .footer-logo--dark { display: block; }
[data-theme="light"] .footer-logo--light { display: block; }
/* Wider measure than the 34ch it carried in the three-column footer: with the
   WhatsApp card gone the left column is broader, and a 34ch paragraph in it left
   a dead gap between the description and the page list. */
.footer-line { color: var(--muted); max-width: 46ch; margin-top: 10px; font-size: .92rem; line-height: 1.6; }

/* ═══ FOOTER PANELS (2026-07-29, owner: "match with the style of the navbar
   ... proper, standard, premium vibe"). The three boxed regions the owner
   circled — socials, the page list, the WhatsApp card — now share one shell
   built from the header's own tokens: the same hairline color-mix(cream)
   border and rectilinear radius as .icon-btn/.cart-btn (that comment already
   rules out glass pills for this brand), plus the header nav's flame
   corner-bracket, reused verbatim on the page-list rows so the footer
   visibly quotes the header instead of running its own dialect. */
.footer-panel {
  border: 1px solid color-mix(in srgb, var(--cream) 14%, transparent);
  border-radius: 10px;
  background: color-mix(in srgb, var(--cream) 3%, transparent);
}
/* ---- the channel rail ---- */
/* One rail, three real brand glyphs, and a hierarchy inside it: WhatsApp is the
   channel that takes an order, so it is the wide one and it keeps its own green
   and its number; Instagram and Facebook are follow links, so they are square
   and quiet. Every tile is the header's own button shell (hairline cream border,
   10px rectilinear radius) with a fill that wipes UP from the floor on hover —
   the same gesture the header's cart button uses, so the footer keeps quoting
   the header rather than inventing a second dialect. */
.footer-socials { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 26px; }
.fs-link {
  position: relative; isolation: isolate; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center; gap: 11px;
  height: 46px;
  border: 1px solid color-mix(in srgb, var(--cream) 15%, transparent);
  border-radius: 10px;
  background: color-mix(in srgb, var(--cream) 3%, transparent);
  transition: color .28s var(--ease), border-color .28s var(--ease),
              transform .28s var(--ease), box-shadow .28s var(--ease);
}
.fs-link svg { width: 18px; height: 18px; flex: none; }
.fs-link::before {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: var(--fs-fill, var(--flame-1));
  transform: translateY(101%); transition: transform .42s cubic-bezier(.16, 1, .3, 1);
}
.fs-link:hover, .fs-link:focus-visible {
  color: var(--fs-ink, #171216) !important; border-color: transparent;
  transform: translateY(-2px);
  box-shadow: 0 12px 26px -12px var(--fs-fill, var(--flame-1));
}
.fs-link:hover::before, .fs-link:focus-visible::before { transform: none; }
.fs-icon { width: 46px; padding: 0; }
.fs-wa {
  --fs-fill: #25D366; --fs-ink: #08130C;
  padding: 0 18px 0 16px; color: var(--cream) !important;
  font-family: var(--font-mono); font-size: .66rem; font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
}
.fs-wa svg { color: var(--fs-fill); transition: color .28s var(--ease); }
/* No light-theme recolour here any more — one palette in both themes (owner
   directive 2026-07-30). WhatsApp keeps its own #25D366 on paper; the glyph is
   an icon, not body copy, and it sits beside a full-contrast text label. */
.fs-wa:hover svg, .fs-wa:focus-visible svg { color: inherit; }
/* The number rides along inside the label rather than being set as a headline of
   its own — that headline was the third column the owner deleted. */
.fs-wa b { font-weight: 700; letter-spacing: .06em; color: inherit; opacity: .78; }
.fs-wa b::before { content: '· '; opacity: .55; }

/* ---- the pages, set in the second voice ---- */
/* The display face in all-caps with a mono index down the right was the owner's
   own diagnosis: "the numbering looks like ai". Both are gone. The list is now
   set in the serif at reading scale, sentence case — the site's editorial voice,
   already used on the About page and the collection taglines, so the footer is
   quoting the brand rather than shouting the same word twice. */
.footer-nav { display: flex; flex-direction: column; padding: 4px clamp(20px, 2.4vw, 28px); }
.footer-nav a {
  display: block;
  padding: clamp(12px, 1.5vw, 16px) 0;
  border-bottom: 1px solid var(--border);
  font-family: var(--font-serif); font-weight: 400;
  font-size: clamp(1.15rem, 1.6vw, 1.5rem); line-height: 1.2; letter-spacing: 0;
  position: relative;
  transition: color .2s var(--ease), padding-left .28s var(--ease);
}
.footer-nav a:last-child { border-bottom: none; }
/* the header nav's own bracket signature (see .nav a::before/::after), borrowed
   whole so hovering a footer link reads as the same gesture as hovering a
   header link, not a lookalike built separately. */
.footer-nav a::before, .footer-nav a::after {
  content: ""; position: absolute; top: 8px; bottom: 8px; width: 5px;
  border: 1.5px solid var(--flame-1); opacity: 0;
  transition: opacity .22s var(--ease), transform .28s var(--ease);
}
.footer-nav a::before { left: -14px; border-right: 0; border-radius: 2px 0 0 2px; transform: translateX(6px); }
.footer-nav a::after  { right: -14px; border-left: 0; border-radius: 0 2px 2px 0; transform: translateX(-6px); }
.footer-nav a:hover { padding-left: 10px; color: var(--cream); }
.footer-nav a:hover::before, .footer-nav a:hover::after { opacity: 1; transform: none; }

.footer-bottom {
  display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap;
  border-top: 1px solid var(--border); padding: 20px 0;
  color: var(--muted-2); font-size: .8rem;
}
.fb-tag { font-family: var(--font-mono); font-size: .62rem; font-weight: 700;
  letter-spacing: .22em; text-transform: uppercase; color: var(--flame-1); }
@media (max-width: 900px) {
  .footer-top { grid-template-columns: minmax(0, 1fr); }
  .footer-bottom { justify-content: flex-start; }
}

/* ---------- scroll reveal (added by JS) ---------- */
.reveal { opacity: 0; transform: translateY(26px); transition: opacity .7s var(--ease), transform .7s var(--ease); }
.reveal.in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1 !important; transform: none !important; }
  * { scroll-behavior: auto !important; }
}

/* =================================================================
   Tier 1 UX bundle
   ================================================================= */

/* Floating WhatsApp */
.float-wa {
  position: fixed; right: 22px; bottom: 22px; z-index: 90;
  width: 56px; height: 56px; border-radius: 50%;
  background: var(--wa); color: #06310f;
  display: grid; place-items: center;
  box-shadow: 0 10px 30px rgba(37, 211, 102, .45);
  transition: transform .25s var(--ease);
}
.float-wa:hover { transform: translateY(-3px) scale(1.05); }
.float-wa svg { width: 28px; height: 28px; }

/* ═══════════════════════════════════════════════════════════════════════════
   THE COUNTER RAIL — shop header + filters, one line (2026-07-30)

   Replaces .shop-controls / .shop-pills / .size-filter / .results-meta, which
   between them stacked six bands above the grid and pushed the first product
   under the fold. Everything the shopper steers with now shares a single rail:
   identity on the left, collection tabs and the two controls that earn their
   place on the right. Deleted outright: the size chips (a size belongs to one
   garment, and the card's buy rail already sells all of them from the grid) and
   the "Every live piece — no restocks" eyebrow, which is now a four-word note
   riding inside the tally instead of a band of its own.
   ══════════════════════════════════════════════════════════════════════════ */

/* The shop opens on the rail, not on a poster, so it doesn't need the section's
   full top pad — but the bottom keeps it, or the footer crowds the last row. */
.section-tight { padding: clamp(22px, 3vw, 38px) 0 clamp(52px, 8vw, 96px); }

.shop-bar {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: clamp(12px, 2vw, 26px);
  margin-bottom: clamp(22px, 2.8vw, 34px);
  padding: 10px 0;
  position: sticky; top: var(--nav-offset, 84px); z-index: 40;
  background: var(--ink);
}
.sb-id { margin-right: auto; min-width: 0; }
/* Poster scale is for the homepage. Here the H1 is a label on a working page —
   it names the filter you're inside (it becomes "Duality Series" on a tab) and
   then gets out of the grid's way. */
.sb-id h1 {
  font-size: clamp(1.45rem, 2.7vw, 2.05rem); line-height: 1.05;
  font-weight: inherit; margin: 0;
}
.sb-tally {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin: 7px 0 0; color: var(--muted);
  font-family: var(--font-mono); font-size: .72rem; letter-spacing: .1em; text-transform: uppercase;
}
/* Hairline divider before the trailing note/clear — cheaper than a bullet and
   it lines the two fragments up as one meta strip. */
.sb-note, .sb-clear { position: relative; padding-left: 11px; }
.sb-note::before, .sb-clear::before {
  content: ''; position: absolute; left: 0; top: 50%; width: 1px; height: 10px;
  transform: translateY(-50%); background: var(--border-2);
}
.sb-clear { color: var(--flame-1); font-weight: 700; }
.sb-clear:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Collection tabs as a true segmented control: one recessed track, the live
   segment filled with the brand flame. The old version was three outline
   buttons, which read as three unrelated actions rather than one choice. */
.shop-tabs {
  display: flex; align-items: center; gap: 3px; padding: 4px;
  border: 1px solid var(--border); border-radius: 100px;
  background: color-mix(in srgb, var(--ink-2) 72%, transparent);
}
.sb-tab {
  padding: 9px 17px; border-radius: 100px; white-space: nowrap;
  font-family: var(--font-mono); font-size: .72rem; font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase; color: var(--muted);
  transition: color .22s var(--ease), background-color .22s var(--ease);
}
.sb-tab:hover { color: var(--cream); background: color-mix(in srgb, var(--cream) 8%, transparent); }
.sb-tab.is-on {
  color: var(--flame-ink); background: var(--flame); box-shadow: var(--glow);
}
.sb-tab.is-on:hover { color: var(--flame-ink); }

.shop-tools { display: flex; align-items: center; gap: 10px; }

/* Compact search: a quiet pill that grows when you actually want it —
   the old full-width bar ate a whole row and shouted over the products. */
.search-box { position: relative; flex: 0 1 auto; }
.search-box input {
  width: 142px; background: var(--ink-2); border: 1px solid var(--border-2);
  border-radius: 100px; color: var(--cream); padding: 10px 14px 10px 38px; font-family: inherit; font-size: .88rem;
  transition: width .3s var(--ease), border-color .2s ease;
}
.search-box input:focus { border-color: var(--flame-1); width: min(260px, 58vw); }
.search-box input:focus:not(:focus-visible) { outline: none; }
.search-box svg { position: absolute; left: 13px; top: 50%; transform: translateY(-50%); color: var(--muted); width: 15px; height: 15px; }
.select-wrap select {
  background: var(--ink-2); border: 1px solid var(--border-2); border-radius: 100px;
  color: var(--cream); padding: 10px 38px 10px 16px; font-family: inherit; font-size: .86rem; cursor: pointer;
  -webkit-appearance: none; appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239d99a6' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 16px center;
  transition: border-color .2s ease;
}
.select-wrap select:hover { border-color: var(--cream); }

/* Desktop: the rail follows you down the grid, so re-filtering never means
   scrolling back to the top. It's a floating glass panel rather than an
   edge-to-edge band — a full-bleed version needs a 100vw pseudo-element, and
   that pushes document.scrollWidth past the viewport even with overflow clipped,
   which is exactly the class of bug this stylesheet has been bitten by before.
   The negative inline margin cancels the panel's own padding so the H1 still
   lines up with the first card in the grid below it. */
@media (min-width: 901px) {
  .shop-bar {
    margin-inline: -20px; padding: 12px 20px;
    border: 1px solid var(--border); border-radius: var(--radius);
    background: color-mix(in srgb, var(--ink) 84%, transparent);
    -webkit-backdrop-filter: blur(18px) saturate(1.25);
    backdrop-filter: blur(18px) saturate(1.25);
  }
}

@media (max-width: 900px) {
  /* Two rows, not five: identity, then tabs + sort on one swipeable line. The
     inline search is dropped here because the mobile tab bar already carries a
     full-screen search overlay — two search entry points on one viewport is the
     duplication, not a feature. */
  /* Two rows, and the pairing matters. Sort rides up beside the title, which
     leaves the whole second row to the collection track — enough width for
     every series to be on screen. Tabs sharing the row with the sort pill is
     what forced "ARTISTRY" off the edge, and a tab you have to discover by
     swiping is a tab nobody presses. */
  .shop-bar { gap: 10px 12px; padding: 4px 0; }
  .sb-id { margin-right: auto; }
  .shop-bar .search-box { display: none; }
  .shop-tools { order: 1; flex: 0 0 auto; }
  .shop-tabs {
    order: 2; width: 100%; min-width: 0; overflow-x: auto;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
  }
  .shop-tabs::-webkit-scrollbar { display: none; }
  .sb-tab { flex: 1 1 auto; text-align: center; padding: 9px 12px; }
}

@media (max-width: 560px) {
  /* The "limited runs, no restocks" note is what stops the sort pill fitting
     beside the title, and it costs a whole row to keep. It's brand copy the
     homepage already carries — the count is the part that's actually news here. */
  .sb-note { display: none; }
}

@media (max-width: 420px) {
  .sb-tab { padding: 9px 8px; font-size: .655rem; letter-spacing: .09em; }
  .select-wrap select { padding: 9px 32px 9px 13px; font-size: .8rem; background-position: right 12px center; }
}

/* Pagination (was three inline style attributes on the shop template) */
.pagination {
  display: flex; gap: 10px; align-items: center; justify-content: center;
  margin-top: clamp(30px, 4vw, 48px); flex-wrap: wrap;
}
.pagination .muted { font-family: var(--font-mono); font-size: .74rem; letter-spacing: .1em; text-transform: uppercase; }

/* Lightbox (image zoom) */
.pd-main { cursor: zoom-in; }
.lightbox {
  position: fixed; inset: 0; z-index: 300; background: rgba(8, 8, 10, .92);
  display: none; align-items: center; justify-content: center; padding: 30px;
  opacity: 0; transition: opacity .25s var(--ease);
}
.lightbox.open { display: flex; opacity: 1; }
.lightbox-stage {
  margin: 0; max-width: 92vw; max-height: 84vh; overflow: hidden; border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  /* Pan is handled by the pointer handlers, so the browser must not also try to
     scroll/zoom the page from the same drag. */
  touch-action: none;
}
.lightbox img {
  max-width: 92vw; max-height: 84vh; border-radius: 10px; cursor: zoom-in;
  transition: transform .22s var(--ease); will-change: transform;
}
/* Zoomed: the cursor becomes a grab handle and the transition shortens so a
   drag tracks the pointer instead of easing behind it. The transform itself is
   set inline by the zoom controls — see product_detail.html. */
.lightbox.zoomed img { cursor: grab; transition: transform .06s linear; }
.lightbox.zoomed .lightbox-stage:active img { cursor: grabbing; }

/* Zoom toolbar: −, level, +, reset, and the image count. Replaced the old
   "click once for 2.4×" toggle, which gave the shopper no way to choose how
   close they got or to move around once there. */
/* Three separate round buttons — out, reset, in — centred under the picture. */
.lightbox-tools {
  position: absolute; bottom: 22px; left: 50%; transform: translateX(-50%);
  display: flex; align-items: center; gap: 14px; z-index: 2;
}
.lightbox-tools button {
  width: 46px; height: 46px; padding: 0; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: rgba(255, 255, 255, .12); border: 1px solid rgba(255, 255, 255, .2);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  color: #fff; cursor: pointer; transition: background .2s, opacity .2s, transform .12s;
}
.lightbox-tools button svg {
  width: 20px; height: 20px; fill: none; stroke: currentColor;
  stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
.lightbox-tools button:hover { background: rgba(255, 255, 255, .24); }
.lightbox-tools button:active { transform: scale(.94); }
.lightbox-tools button:disabled { opacity: .28; cursor: default; background: rgba(255, 255, 255, .12); }
.lightbox-count:empty { display: none; }
.lightbox-close {
  position: absolute; top: 20px; right: 24px; width: 44px; height: 44px; border-radius: 50%;
  background: rgba(255,255,255,.12); border: 0; color: #fff; font-size: 1.4rem; cursor: pointer; z-index: 2;
}
.lightbox-nav {
  position: absolute; top: 50%; transform: translateY(-50%); width: 50px; height: 50px;
  border-radius: 50%; background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.18);
  color: #fff; font-size: 1.8rem; line-height: 1; cursor: pointer; z-index: 2;
  display: flex; align-items: center; justify-content: center; transition: background .2s;
}
.lightbox-nav:hover { background: rgba(255,255,255,.22); }
.lightbox-nav.prev { left: 24px; }
.lightbox-nav.next { right: 24px; }
.lightbox-nav[hidden] { display: none; }
.lightbox-count {
  position: absolute; bottom: 29px; left: 24px;
  color: rgba(255,255,255,.72); font-size: .75rem; font-family: var(--font-mono); letter-spacing: .08em;
  padding: 7px 10px; border: 1px solid rgba(255,255,255,.16); border-radius: 100px;
  background: rgba(8,8,10,.28); backdrop-filter: blur(10px);
}
/* Zoomed in, the browse arrows get out of the way — but the toolbar stays,
   because that's the only way back out. */
.lightbox.zoomed .lightbox-nav { opacity: 0; pointer-events: none; }
.pd-thumbs button { cursor: pointer; }

/* Size-guide modal */
.modal {
  position: fixed; inset: 0; z-index: 300; background: rgba(8, 8, 10, .7);
  display: none; align-items: center; justify-content: center; padding: 24px;
  opacity: 0; transition: opacity .25s var(--ease);
}
.modal.open { display: flex; opacity: 1; }
.modal-card {
  background: var(--ink-2); border: 1px solid var(--border-2); border-radius: var(--radius);
  max-width: 560px; width: 100%; padding: 30px; max-height: 88vh; overflow-y: auto; box-shadow: var(--shadow);
}
.modal-card h3 { font-size: 1.6rem; margin-bottom: 6px; }
.modal-close { float: right; background: none; border: 0; color: var(--muted); font-size: 1.5rem; cursor: pointer; line-height: 1; }
/* Guest/account confirm — plain-language sizing, both choices read as real
   buttons (the "Sign in" link used to be a bare text line under the CTA,
   which read as an afterthought rather than a second option). */
.guest-confirm-card { max-width: 420px; text-align: center; padding: 34px 30px; }
.guest-confirm-card h3 { font-size: 1.5rem; margin: 6px 0 10px; }
.guest-confirm-card .checkout-hint { font-size: 1rem; margin: 0 0 24px; }
.guest-confirm-card .btn-outline { margin-bottom: 10px; }
/* The uploaded size chart. Wider card than the rest of the modals get: the
   artwork is a landscape board with real measurements on it, and at the 560px
   default the numbers land too small to read on a phone held at arm's length.
   :has() so the widening only happens for the chart; browsers without it fall
   back to the normal width, where the image still fits, just smaller. */
.size-chart-img { display: block; width: 100%; height: auto; border-radius: var(--radius); margin-top: 14px; }
.modal-card:has(.size-chart-img) { max-width: 860px; padding: 22px; }
/* This used to link the chart out to its raw storage URL in a new tab so the
   browser's own pinch-zoom would take over there — walking a shopper off the
   site onto an unbranded Cloudinary address to read numbers the 860px card
   already shows clearly. Just an image now; nothing to tap, nowhere to go. */
@media (max-width: 560px) { .modal-card:has(.size-chart-img) { padding: 12px; } }
/* (the typed .size-table was deleted 2026-08-03 — the uploaded chart is the
   only size guide now, on the product page and on About alike) */
/* Screen-reader-only label — the restock form has one input whose purpose is
   obvious to the eye from its placeholder and to nobody else. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* (the fit finder's input/result styles were deleted 2026-07-30 with the
   calculator itself — the uploaded size chart replaced both) */

/* Sticky mobile add-to-cart */
.sticky-buy {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 95;
  background: color-mix(in srgb, var(--ink) 94%, transparent); backdrop-filter: blur(14px);
  border-top: 1px solid var(--border); padding: 12px 16px; display: none;
  align-items: center; justify-content: space-between; gap: 14px;
  transform: translateY(110%); transition: transform .3s var(--ease);
}
.sticky-buy.show { transform: translateY(0); }
.sticky-buy .sb-info { line-height: 1.2; }
.sticky-buy .sb-name { font-weight: 600; font-size: .95rem; }
.sticky-buy .sb-price { color: var(--muted); font-size: .85rem; }
.sticky-buy .btn { padding: 13px 22px; }

/* ---------- responsive ---------- */
@media (max-width: 880px) {
  .sticky-buy { display: flex; }
  .float-wa { bottom: 84px; }
  .pd { grid-template-columns: 1fr; gap: 26px; }
  /* Stacked, the gallery must NOT stick: it sits above the buy column, so a
     sticky image would pin a full screen of photograph over everything the
     buyer scrolled down to read. */
  .pd-gallery { position: static; }
  .pd-section { padding-top: 22px; }
  .pd-related .sec-head { gap: 14px; }
  /* Bug fix (2026-08-20): bare `1fr` (rule 9, PROJECT.md §3) — the stacked
     mobile column's automatic minimum was the row's own min-content, wide
     enough (cart row image + qty stepper + price + bin) to blow past the
     viewport; a real ~33px page-wide overflow on /cart/ at 360px. */
  .layout-2 { grid-template-columns: minmax(0, 1fr); }
  /* ----- Mobile drawer: an editorial index, not a list of links -----
     Big display type with mono row numbers (same voice as the home sections),
     a soft flame wash in the corner, and a brand footer that ends the sheet
     with the two actions a COD shopper needs: WhatsApp and order tracking. */
  .nav {
    position: fixed; top: var(--nav-offset, 72px); right: 0; left: 0;
    flex-direction: column; align-items: stretch;
    background:
      radial-gradient(130% 100% at 100% 0%, color-mix(in srgb, var(--flame-1) 10%, transparent) 0%, transparent 52%),
      var(--ink-2);
    border-bottom: 1px solid var(--border);
    padding: 6px 22px calc(22px + env(safe-area-inset-bottom));
    gap: 0; transform: translateY(-135%);
    /* visibility flips after the slide-out so Tab can't reach offscreen links */
    visibility: hidden;
    transition: transform .35s var(--ease), visibility 0s .35s; z-index: 99;
    max-height: calc(100dvh - var(--nav-offset, 72px)); overflow-y: auto; overscroll-behavior: contain;
    counter-reset: navrow;
  }
  .nav.open {
    transform: translateY(0); visibility: visible;
    transition: transform .35s var(--ease);
  }
  /* --nav-offset is measured from the header's real bottom edge (JS, base.html),
     not a constant — the announcement bar / "store closed" status bar above the
     header have no fixed height, so a hardcoded top would let the drawer's top
     rows render under the header whenever one of those bars is showing. The
     72px fallback only applies for the instant before JS first runs. */
  .nav-toggle { display: block; }

  /* Rows: 01 SHOP / 02 DUALITY / … — numbered, full-bleed tap targets */
  .nav > a {
    display: flex; align-items: center; gap: 16px; width: 100%;
    padding: 17px 2px; border-bottom: 1px solid var(--border);
    font-family: var(--font-display); font-size: 1.35rem; font-weight: 600;
    letter-spacing: .02em; text-transform: uppercase; color: var(--cream);
  }
  .nav > a::before {
    counter-increment: navrow;
    content: "0" counter(navrow);
    /* reset the desktop bracket corner — in the drawer this pseudo is the row number */
    position: static; width: auto; height: auto; border: 0; opacity: 1; transform: none;
    font-family: var(--font-mono); font-size: .66rem; font-weight: 700;
    letter-spacing: .08em; color: var(--muted); min-width: 24px;
    transition: color .2s;
  }
  .nav > a::after { display: none; }  /* hover underline is a desktop idiom */
  .nav > a.active { color: var(--flame-1); }
  .nav > a.active::before { color: var(--flame-1); }

  /* Collections mega menu, drawer version: an IN-FLOW accordion, not a floating
     panel — it just pushes the rows below it down, exactly like any other
     native <details> in a document. No JS: the browser already knows how to
     open and close this. Row styling is duplicated from `.nav > a` rather than
     shared via a combined selector, because <details>/<summary>'s default
     display model differs enough from <a> that sharing one rule between a
     dropdown and an accordion caused more override-fighting than writing both.

     The picture cards become a two-up thumbnail grid here. A phone drawer can
     carry pictures — it just can't carry a 250px sidebar beside them, so
     `.mm-lead` collapses to its link alone. */
  .nav > details.megamenu { display: block; width: 100%; }
  .megamenu > summary {
    display: flex; align-items: center; gap: 16px; width: 100%;
    padding: 17px 2px; border-bottom: 1px solid var(--border);
    font-family: var(--font-display); font-size: 1.35rem; font-weight: 600;
    letter-spacing: .02em; text-transform: uppercase; color: var(--cream);
    cursor: pointer; list-style: none;
  }
  .megamenu > summary::-webkit-details-marker { display: none; }
  .megamenu > summary::before, .megamenu > summary::after { top: auto; bottom: auto; border: 0; opacity: 1; transform: none; position: static; width: auto; height: auto; }
  .megamenu > summary::before {
    counter-increment: navrow;
    content: "0" counter(navrow);
    font-family: var(--font-mono); font-size: .66rem; font-weight: 700;
    letter-spacing: .08em; color: var(--muted); min-width: 24px;
  }
  .megamenu > summary::after { content: none; }
  .mm-chev { margin-left: auto; width: 18px; height: 18px; color: var(--muted); flex: none; }
  .mm-panel { padding: 14px 0 18px 40px; }
  /* Cards first, then the "browse everything" link — in the drawer the lead
     column is just that one link, and it belongs after what it is an
     alternative to, not above it. */
  .mm-inner { display: flex; flex-direction: column-reverse; }
  .mm-eyebrow, .mm-note { display: none; }   /* the drawer row already says it */
  .mm-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .mm-card { display: grid; gap: 8px; }
  .mm-shot {
    display: block; aspect-ratio: 4 / 3; overflow: hidden;
    border-radius: 10px; background: var(--ink-3);
  }
  .mm-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .mm-shot--all { display: grid; place-items: center; color: var(--muted); border: 1px dashed var(--border-2); }
  .mm-meta { display: flex; flex-direction: column; gap: 2px; }
  .mm-name { font-family: var(--font-display); font-size: .98rem; font-weight: 600; color: var(--cream); }
  .mm-card.active .mm-name { color: var(--flame-1); }
  .mm-flag {
    display: inline-block; margin-left: 7px; vertical-align: 2px;
    font-family: var(--font-mono); font-size: .48rem; font-weight: 800;
    letter-spacing: .12em; text-transform: uppercase;
    color: #171216; background: linear-gradient(100deg, var(--flame-1), var(--flame-2));
    padding: 2px 6px; border-radius: 100px;
  }
  .mm-count { font-family: var(--font-mono); font-size: .56rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted-2); }
  .mm-all {
    display: inline-flex; align-items: center; gap: 8px; margin-top: 14px;
    font-family: var(--font-mono); font-size: .64rem; font-weight: 700;
    letter-spacing: .16em; text-transform: uppercase; color: var(--flame-1);
  }

  /* Drawer choreography: rows rise in one after another (CSS-owned entrance) */
  .nav > * { opacity: 0; transform: translateY(-8px); transition: opacity .28s var(--ease), transform .28s var(--ease); }
  .nav.open > * { opacity: 1; transform: none; }
  .nav.open > *:nth-child(1) { transition-delay: .06s; }
  .nav.open > *:nth-child(2) { transition-delay: .11s; }
  .nav.open > *:nth-child(3) { transition-delay: .16s; }
  .nav.open > *:nth-child(4) { transition-delay: .21s; }
  .nav.open > *:nth-child(5) { transition-delay: .26s; }
  .nav.open > *:nth-child(6) { transition-delay: .31s; }

  /* Drawer brand footer */
  .nav-drawer-foot { display: flex; flex-direction: column; gap: 16px; padding-top: 22px; }
  .ndf-eyebrow {
    font-family: var(--font-body); font-size: .68rem; font-weight: 700;
    letter-spacing: .3em; text-transform: uppercase; color: var(--flame-1);
  }
  .ndf-wa {
    display: inline-flex; align-items: center; justify-content: center; gap: 9px;
    padding: 14px 20px; border-radius: 100px;
    background: var(--wa); color: #06310f;
    font-family: var(--font-body); font-weight: 700; font-size: .92rem;
  }
  .ndf-row { display: flex; align-items: center; gap: 10px; }
  .ndf-row a[aria-label] {
    display: inline-grid; place-items: center; width: 40px; height: 40px;
    border: 1px solid var(--border-2); border-radius: 50%; color: var(--cream);
  }
  .ndf-track {
    margin-left: auto; font-family: var(--font-body); font-size: .8rem; font-weight: 600;
    color: var(--muted); text-decoration: underline; text-underline-offset: 3px;
  }
}
@media (max-width: 880px) and (prefers-reduced-motion: reduce) {
  .nav, .nav > * { transition: none !important; transform: none !important; }
  .nav { display: none; }
  .nav.open { display: flex; }
}
@media (max-width: 520px) {
  .hero { min-height: 82vh; }
  /* Quantity + Add to Cart share a row. On a narrow phone the stepper keeps
     its tap targets and the button gives up its generous padding instead —
     wrapping the two onto separate lines pushed Add to Cart off screen one. */
  .pd-qty-row .btn-lg { padding-left: 18px; padding-right: 18px; font-size: .95rem; }
  /* keep the header on one line at 375px: tighter padding/gaps, smaller wordmark,
     and the cart becomes an icon (the "Cart" label is dropped). */
  .container { padding: 0 18px; }
  .header-inner { gap: 10px; }
  .header-actions { gap: 8px; }
  .brand .brand-name { font-size: 1.28rem; }
  .brand-logo { height: 28px; }
  .site-header.scrolled .brand-logo { height: 25px; }
  .brand-clan { font-size: .92rem; letter-spacing: .18em; padding-left: 9px; }
  .cart-btn { padding: 0 12px; }
  .cart-btn span:not(.cart-count) { display: none; }
}

/* =================================================================
   Manage panel (custom in-site admin)
   ================================================================= */
.mp-wrap { display: grid; grid-template-columns: 232px 1fr; gap: 28px; align-items: start; }
.mp-side { position: sticky; top: 92px; display: flex; flex-direction: column; gap: 4px; }
.mp-side .mp-side-title { font-size: .7rem; letter-spacing: .18em; text-transform: uppercase; color: var(--muted-2); margin: 0 0 8px 12px; }
.mp-side a {
  display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-radius: var(--radius-sm);
  color: var(--muted); font-weight: 600; font-size: .92rem; transition: .15s; border: 1px solid transparent;
}
.mp-side a svg { width: 18px; height: 18px; }
.mp-side a:hover { color: var(--cream); background: color-mix(in srgb, var(--cream) 5%, transparent); }
.mp-side a.active { color: var(--cream); background: var(--ink-2); border-color: var(--border); }
.mp-side a.active svg { color: var(--flame-1); }
.mp-side .mp-side-foot { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--border); }

.mp-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 24px; flex-wrap: wrap; }
.mp-head h1 { font-size: clamp(1.8rem, 4vw, 2.6rem); }
.mp-head .mp-sub { color: var(--muted); font-size: .9rem; margin-top: 4px; }

.mp-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(165px, 1fr)); gap: 16px; margin-bottom: 22px; }
.mp-card { background: var(--ink-2); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; }
.mp-kpi { border-left: 3px solid var(--accent, var(--flame-1)); }
.mp-kpi .mp-ico { width: 38px; height: 38px; border-radius: 11px; display: grid; place-items: center; background: color-mix(in srgb, var(--accent, var(--flame-1)) 16%, transparent); color: var(--accent, var(--flame-1)); margin-bottom: 14px; }
.mp-kpi .mp-ico svg { width: 19px; height: 19px; }
.mp-num { font-family: var(--font-display); font-size: 2rem; line-height: 1; }
.mp-label { font-size: .76rem; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); margin-top: 8px; font-weight: 600; }

.mp-grid-2 { display: grid; grid-template-columns: 1.6fr 1fr; gap: 18px; }
.mp-card h3 { font-size: 1.05rem; margin-bottom: 14px; text-transform: none; font-family: var(--font-display); letter-spacing: 0; }

.mp-table { width: 100%; border-collapse: collapse; font-size: .9rem; }
.mp-table th { text-align: left; font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); font-weight: 600; padding: 10px 12px; border-bottom: 1px solid var(--border); }
.mp-table td { padding: 12px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.mp-table tr:last-child td { border-bottom: 0; }
.mp-table tbody tr { transition: background .15s; }
.mp-table tbody tr:hover { background: color-mix(in srgb, var(--cream) 4%, transparent); }
.mp-table .tprod { display: flex; align-items: center; gap: 12px; }
.mp-table .tprod img { width: 42px; height: 42px; border-radius: 8px; object-fit: cover; }
.mp-link { color: var(--flame-1); font-weight: 600; }

.mp-badge { display: inline-block; padding: 3px 11px; border-radius: 999px; font-size: .72rem; font-weight: 600; border: 1px solid currentColor; white-space: nowrap; }
.mp-badge.pending { color: #c79100; }
.mp-badge.confirmed { color: #2f80ed; }
.mp-badge.shipped { color: #14b8a6; }
.mp-badge.delivered { color: #2faf63; }
.mp-badge.cancelled { color: #e0533c; }

.mp-pills { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px; }
.mp-pills a { padding: 8px 16px; border-radius: 999px; border: 1px solid var(--border-2); font-size: .82rem; font-weight: 600; color: var(--muted); }
.mp-pills a.active { background: var(--cream); color: var(--ink); border-color: var(--cream); }

.mp-toggle { background: none; border: 1px solid var(--border-2); border-radius: 999px; padding: 4px 12px; font-size: .76rem; font-weight: 600; cursor: pointer; color: var(--muted); }
.mp-toggle.on { color: var(--mint); border-color: var(--mint); }

.mp-stock-grid { display: flex; gap: 12px; flex-wrap: wrap; }
.mp-stock-grid label { display: flex; flex-direction: column; gap: 5px; font-size: .78rem; color: var(--muted); font-weight: 600; }
.mp-stock-grid input { width: 76px; background: var(--ink-3); border: 1px solid var(--border-2); border-radius: var(--radius-sm); color: var(--cream); padding: 9px 11px; font-family: inherit; }

.mp-imgs { display: flex; gap: 12px; flex-wrap: wrap; }
.mp-imgs .mp-img { position: relative; width: 84px; }
.mp-imgs .mp-img img { width: 84px; height: 84px; object-fit: cover; border-radius: 9px; border: 1px solid var(--border); }
.mp-imgs .mp-img label { display: flex; align-items: center; gap: 5px; font-size: .72rem; color: var(--muted); margin-top: 6px; cursor: pointer; }

/* login */
.mp-login { max-width: 400px; margin: 6vh auto; }
.mp-login .panel { padding: 34px; }

@media (max-width: 860px) {
  .mp-wrap { grid-template-columns: 1fr; }
  .mp-side { position: static; flex-direction: row; flex-wrap: wrap; }
  .mp-side .mp-side-title { display: none; }
  .mp-grid-2 { grid-template-columns: 1fr; }
  .mp-table-wrap { overflow-x: auto; }
}

/* =================================================================
   SIGNATURE — 3D logo, hero entrance
   ================================================================= */

/* ---------- 3D extruded logo (header + footer) ---------- */
.brand { perspective: 620px; }
.brand .brand-name,
.footer-brand .brand-name {
  position: relative;
  display: inline-block;
  letter-spacing: .04em;
  transform-style: preserve-3d;
  transition: transform .45s var(--ease), filter .45s var(--ease);
  /* crisp at rest — just a hairline depth + soft ambient shadow, so the C reads as a C */
  text-shadow: 1px 1px 0 color-mix(in srgb, var(--flame-2) 45%, transparent), 0 3px 7px rgba(0, 0, 0, .22);
}
/* the flame-gradient half keeps its gradient face; depth comes from a matching drop-shadow */
.brand .brand-name .text-flame,
.footer-brand .brand-name .text-flame {
  text-shadow: none;
  filter: drop-shadow(1px 1px 0 color-mix(in srgb, var(--flame-2) 45%, transparent)) drop-shadow(0 3px 6px rgba(0, 0, 0, .2));
}
/* the "3D" is the hover tilt — full dimension on interaction, zero legibility cost at rest */
.brand:hover .brand-name {
  transform: perspective(500px) rotateX(14deg) rotateY(-8deg) translateY(-1px);
  filter: brightness(1.07) drop-shadow(0 8px 14px rgba(0, 0, 0, .3));
}

@media (prefers-reduced-motion: reduce) {
  .hero-scroll svg { animation: none !important; }
  .deck-spark { display: none !important; }
  .deck-card:hover .deck-shine { animation: none !important; }
  .deck-name::before, .deck-name::after { display: none !important; }
}

/* ---------- hero / deck responsive ---------- */
@media (max-width: 980px) {
  .hero-grid { grid-template-columns: 1fr; align-items: end; padding-top: 118px; }
  .hero-deck { justify-self: center; margin: 30px auto 4px; width: min(380px, 88vw); height: 348px; }
}
@media (max-width: 560px) {
  .hero { min-height: 84vh; }
  .hero-grid { padding: 100px 0 48px; }
  /* touch devices can't hover the fan open; the portrait campaign photo carries the
     mobile hero and the same pieces appear in "The First Drop" section just below. */
  .hero-deck { display: none; }
}

/* ---------- drop-list (email capture) ---------- */
.droplist {
  position: relative; overflow: hidden; isolation: isolate;
  background:
    radial-gradient(120% 140% at 50% -20%, color-mix(in srgb, var(--flame-1) 10%, transparent) 0%, transparent 55%),
    var(--ink-2);
  border: 1px solid var(--border-2); border-radius: calc(var(--radius) + 6px);
  padding: clamp(40px, 7vw, 78px); text-align: center;
  box-shadow: var(--shadow);
}
/* breathing central glow */
.droplist-glow {
  position: absolute; left: 50%; top: -45%; width: 80%; height: 130%; transform: translateX(-50%);
  background: radial-gradient(circle, color-mix(in srgb, var(--flame-1) 30%, transparent) 0%, transparent 62%);
  filter: blur(26px); pointer-events: none; z-index: -2;
  animation: droplist-breathe 7s ease-in-out infinite;
}
/* slow-rotating conic light rays behind the headline */
.droplist-rays {
  position: absolute; left: 50%; top: 50%; width: 150%; aspect-ratio: 1; transform: translate(-50%, -50%);
  background: conic-gradient(from 0deg,
    transparent 0deg, color-mix(in srgb, var(--flame-1) 14%, transparent) 18deg, transparent 36deg,
    transparent 90deg, color-mix(in srgb, var(--flame-2) 12%, transparent) 108deg, transparent 126deg,
    transparent 200deg, color-mix(in srgb, var(--flame-1) 13%, transparent) 218deg, transparent 236deg);
  pointer-events: none; z-index: -3; opacity: .7;
  -webkit-mask-image: radial-gradient(circle, #000 0%, transparent 65%);
          mask-image: radial-gradient(circle, #000 0%, transparent 65%);
  animation: droplist-spin 38s linear infinite;
}
/* faint engineering grid for texture */
.droplist-grid {
  position: absolute; inset: 0; pointer-events: none; z-index: -1; opacity: .5;
  background-image:
    linear-gradient(color-mix(in srgb, var(--cream) 5%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--cream) 5%, transparent) 1px, transparent 1px);
  background-size: 46px 46px;
  -webkit-mask-image: radial-gradient(ellipse 70% 70% at 50% 50%, #000 0%, transparent 75%);
          mask-image: radial-gradient(ellipse 70% 70% at 50% 50%, #000 0%, transparent 75%);
}
.droplist > *:not([class$="-glow"]):not([class$="-rays"]):not([class$="-grid"]) { position: relative; z-index: 1; }
.droplist .eyebrow { display: inline-flex; align-items: center; gap: 9px; }
.droplist-dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--flame-1);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--flame-1) 70%, transparent);
  animation: droplist-pulse 2.4s ease-out infinite;
}
.droplist h2 { font-size: clamp(2.2rem, 6vw, 4rem); line-height: 1.02; letter-spacing: -.02em; margin: 14px 0 0; }
.droplist p { max-width: 540px; margin: 18px auto 0; font-size: 1.06rem; line-height: 1.6; }
.droplist-form {
  display: flex; gap: 12px; flex-wrap: wrap; justify-content: center;
  max-width: 580px; margin: 34px auto 0;
}
.droplist-form input[type=email] {
  flex: 1; min-width: 240px;
  background: var(--ink-3); border: 1px solid var(--border-2); border-radius: 100px;
  color: var(--cream); padding: 16px 24px; font-family: inherit; font-size: 1rem;
  transition: border-color .3s, box-shadow .3s;
}
.droplist-form input[type=email]:focus {
  border-color: var(--flame-1);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--flame-1) 16%, transparent);
}
.droplist-form input[type=email]:focus:not(:focus-visible) { outline: none; }
/* honeypot — visually hidden from humans, present for bots */
.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }
.droplist-fine { display: block; margin-top: 16px; font-size: .8rem; }
@media (max-width: 520px) { .droplist-form .btn { width: 100%; } }

@keyframes droplist-spin { to { transform: translate(-50%, -50%) rotate(360deg); } }
@keyframes droplist-breathe {
  0%, 100% { opacity: .75; transform: translateX(-50%) scale(1); }
  50%      { opacity: 1;   transform: translateX(-50%) scale(1.06); }
}
@keyframes droplist-pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--flame-1) 60%, transparent); }
  70%  { box-shadow: 0 0 0 9px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .droplist-rays, .droplist-glow, .droplist-dot { animation: none !important; }
}

/* =================================================================
   MOBILE PREMIUM — Safe areas, touch UX, 2-col grid, swipe gallery
   All additions below; zero regressions on desktop.
   ================================================================= */

/* 1. iOS text-size-adjust: prevent font inflation on orientation change */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

/* 2. Remove Android/iOS gray tap flash on all interactive elements */
a, button, label, [role="button"],
.size-opt, .size-chip, .card, .card-media,
.collection-band, .accordion summary, .icon-btn, .cart-btn, .nav-toggle {
  -webkit-tap-highlight-color: transparent;
}

/* 3. Fast tap — eliminates 300 ms double-tap-zoom delay on iOS/Android */
button, .btn, label, .size-opt, .size-chip,
.icon-btn, .cart-btn, .nav-toggle, .accordion summary {
  touch-action: manipulation;
}

/* 4. True mobile viewport height (excludes collapsing browser bar) */
@supports (min-height: 1svh) { .hero { min-height: 90svh; } }

/* 5. Safe area insets — iPhone X+ home indicator & notch
      Requires viewport-fit=cover in the meta tag (already set). */
.site-header {
  padding-left: env(safe-area-inset-left, 0px);
  padding-right: env(safe-area-inset-right, 0px);
}
/* Sticky buy bar: lift content above the home indicator */
.sticky-buy {
  padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
}
/* WhatsApp float button: keep above the home indicator */
.float-wa {
  bottom: calc(22px + env(safe-area-inset-bottom, 0px));
}
@media (max-width: 880px) {
  .float-wa { bottom: calc(84px + env(safe-area-inset-bottom, 0px)); }
}
/* Footer: extra breathing room on iPhone */
.footer-bottom {
  padding-bottom: calc(20px + env(safe-area-inset-bottom, 0px));
}

/* 6. Prevent iOS zoom-in on form focus (iOS zooms when font-size < 16px) */
@media (max-width: 880px) {
  .form-field input:not([type=checkbox]):not([type=radio]):not([type=file]),
  .form-field textarea,
  .form-field select,
  .search-box input,
  .droplist-form input[type=email],
  .pd-restock input[type=email],
  .qty input,
  .select-wrap select { font-size: 16px; }
}

/* 7. 2-column product grid on mobile — industry standard (ASOS, Zalando, H&M).
   Bug fix (2026-08-20): was a bare `repeat(2, 1fr)`, which — being wider
   (879px) and later in the file than the 600px block above that correctly
   used `minmax(0,1fr)` — silently won on every phone width (rule 9,
   PROJECT.md §3: bare `1fr` tracks let one wide child, e.g. an oversized
   product photo or a long name, blow the column out and bleed cards into the
   section below). */
@media (max-width: 879px) {
  .product-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .card-name    { font-size: .86rem; }
  .card-price .now { font-size: .86rem; }
  .card-price .was { font-size: .76rem; }
  .card-collection { font-size: .6rem; letter-spacing: .12em; }
  .card-body    { padding: 10px 2px 12px; }
  .badge        { font-size: .63rem; padding: 4px 9px; top: 8px; left: 8px; }
}

/* 8. (was: mobile horizontal-scroll thumb strip — deleted 2026-07-30. Section
      22 hides .pd-thumbs on phones entirely now that the indicator row under
      the image does that job, so this block only ever fought with it.) */

/* 9. Tap feedback on product cards (`:hover` is unreliable on touch) */
@media (hover: none) {
  .card:active .card-media img { transform: scale(1.03); }
  .collection-band:active img  { transform: scale(1.03); }
}

/* 10. Toast notifications: full-width strip on small phones */
@media (max-width: 520px) {
  .messages {
    left: 12px;
    right: 12px;
    max-width: none;
    top: max(80px, calc(80px + env(safe-area-inset-top, 0px)));
  }
}

/* 11. Cart item image: slightly tighter on tiny phones.
   Bug fix (2026-08-20): this was `70px 1fr auto` — a bare `1fr` middle track
   (rule 9 in PROJECT.md: always `minmax(0,1fr)`, the min-content floor on a
   bare `1fr` lets a wide child — here the qty stepper's buttons+input — blow
   the column out). At 360px the row measured 5px wider than the viewport and
   the price + delete button were clipped off the right edge. */
@media (max-width: 480px) {
  .cart-item { grid-template-columns: 70px minmax(0, 1fr) auto; gap: 10px; }
}

/* 12. Mobile nav backdrop overlay */
.nav-backdrop {
  display: none;
  position: fixed;
  inset: 72px 0 0 0;
  background: rgba(0, 0, 0, .54);
  z-index: 97;
  opacity: 0;
  transition: opacity .3s var(--ease);
  pointer-events: none;
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}
.nav-backdrop.open { opacity: 1; pointer-events: auto; }
.site-header.scrolled ~ .nav-backdrop { top: 60px; }   /* follow the shrunken bar */
@media (max-width: 880px) { .nav-backdrop { display: block; } }
/* Freeze the page behind an open drawer/overlay (base.html sets body's position:
   fixed + a negative top offset via ccLockScroll(), which is what actually stops
   scrolling). No overflow:hidden here on purpose — it used to sit on html+body
   too, but a `body` with overflow:hidden AND position:fixed still qualifies as
   the sticky-containing scrollport, so the header stuck relative to body's now
   shifted-off-screen box instead of the viewport. A fixed body already can't
   scroll, so there's nothing left for overflow:hidden to do except break that. */
html.nav-locked { touch-action: none; }

/* 13. (Hamburger open/close morph lives with .nav-toggle in the header section) */

/* 14. Modal scroll isolation (prevents body bounce-through on iOS) */
.modal-card { -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }

/* 15. Collection band: better height on single-column phone */
@media (max-width: 520px) { .collection-band { min-height: 210px; } }

/* 16. Sticky buy bar: ensure CTA button never shrinks below thumb width */
@media (max-width: 880px) { .sticky-buy .btn { min-width: 110px; flex-shrink: 0; } }

/* 17. Lightbox is desktop-only: remove zoom cursor on touch devices */
@media (hover: none) { .pd-main { cursor: default; } }

/* 18. Size guide link: larger touch target (min 44px height) on mobile */
@media (max-width: 880px) {
  [data-sizeguide-open] {
    display: inline-block;
    padding: 10px 0;
    font-size: .9rem;
  }
}

/* 19. Modal card: explicit allow vertical pan so iOS doesn't block inner scroll */
.modal-card { touch-action: pan-y; }

/* 20. Error pages (404 / 403 / 400) */
.error-page {
  display: flex;
  align-items: center;
  min-height: 64vh;
  padding: 80px 0;
}
.error-inner {
  max-width: 560px;
  margin: 0 auto;
  text-align: center;
}
.error-code {
  font-family: var(--font-display);
  font-size: clamp(72px, 18vw, 160px);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -.04em;
  background: var(--flame);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
}
.error-inner h1 {
  font-size: clamp(1.4rem, 4vw, 2rem);
  margin: 14px 0 12px;
  letter-spacing: -.01em;
}
.error-inner p {
  color: var(--muted);
  line-height: 1.65;
  margin-bottom: 30px;
}
.error-actions {
  display: flex;
  gap: 14px;
  justify-content: center;
  flex-wrap: wrap;
}

/* ============================================================
   21. Mobile app shell — bottom tab bar
   The single biggest "native app" lever: a fixed thumb-zone tab
   bar present on every screen. Mobile only (<=880px, matching the
   existing mobile-nav breakpoint). Safe-area aware so it floats
   above the iPhone home indicator. Additive — desktop is untouched.
   ============================================================ */
.bottom-nav { display: none; }

@media (max-width: 880px) {
  /* Reserve space so page content / footer never hide behind the bar.
     58px bar + the device's bottom safe-area inset. */
  body { padding-bottom: calc(58px + env(safe-area-inset-bottom, 0px)); }

  .bottom-nav {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 96;
    display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; align-items: stretch;
    /* Solid, not glass (2026-08-13): at 92% opacity the backdrop-filter blur
       underneath was already barely visible — a fixed, always-on, full-width
       compositing layer running blur(18px) every frame on weaker Android GPUs
       for an effect nearly indistinguishable from no blur at all. Bumped to
       97% opaque so the tiny bit of see-through it did offer isn't missed. */
    background: color-mix(in srgb, var(--ink) 97%, transparent);
    border-top: 1px solid var(--border);
    box-shadow: 0 -10px 30px rgba(0, 0, 0, .28);
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }

  .bn-item {
    position: relative;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    min-height: 58px; padding: 8px 4px 6px;
    color: var(--muted); background: none; border: 0; cursor: pointer; text-decoration: none;
    font-family: var(--font-body); font-size: .62rem; font-weight: 700; letter-spacing: .05em;
    text-transform: uppercase; -webkit-tap-highlight-color: transparent;
    transition: color .18s var(--ease), transform .1s var(--ease);
  }
  .bn-item svg { width: 23px; height: 23px; }
  .bn-item:active { transform: scale(.9); }
  .bn-item.active { color: var(--cream); }
  /* flame accent tab marker */
  .bn-item.active::before {
    content: ""; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
    width: 26px; height: 3px; border-radius: 0 0 4px 4px; background: var(--flame);
  }
  .bn-badge {
    position: absolute; top: 5px; left: calc(50% + 7px);
    min-width: 17px; height: 17px; padding: 0 4px;
    background: var(--flame); color: var(--flame-ink); border-radius: 100px;
    font-size: .58rem; font-weight: 700; line-height: 1;
    display: inline-flex; align-items: center; justify-content: center;
  }

  /* The floating WhatsApp bubble would sit on top of the tab bar — fold it
     away on mobile (WhatsApp still lives in the menu, footer + contact page). */
  .float-wa { display: none; }

  /* Product-page sticky add-to-cart now stacks ABOVE the tab bar. */
  .sticky-buy { bottom: calc(58px + env(safe-area-inset-bottom, 0px)); }

  /* Keep toast strip clear of the tab bar as well as the notch. */
  .messages { bottom: auto; }
}

/* ============================================================
   22. Product gallery — indicators on phones
   The dots + count + zoom bar (.pd-bar, styled with the product
   detail block above) is now present at every viewport. On phones
   the thumbnail strip below it folds away: the dots plus the swipe
   gesture are the whole navigation there. The thumb buttons stay in
   the DOM (display:none) so the swipe, dot and lightbox logic that
   queries them is untouched.
   ============================================================ */
@media (max-width: 880px) {
  .pd-thumbs { display: none; }
  .pd-dot { width: 8px; height: 8px; }
}

/* ============================================================
   23. Slide-up cart drawer
   Bottom-sheet on phones, right-side panel on desktop. Opens on
   add-to-cart AND from every cart affordance on the site — header
   icon, mobile tab bar, checkout's cart link (base.html). Without
   JS all of those are still plain links to /cart/.
   ============================================================ */
/* Two grey bars while the cart is being fetched. The panel opens on the tap,
   not on the response — waiting for a round trip before showing anything would
   make the shortcut feel slower than the navigation it replaces. */
.cd-skel {
  height: 74px; margin-bottom: 14px; border-radius: 10px;
  background: linear-gradient(100deg,
    color-mix(in srgb, var(--cream) 5%, transparent) 30%,
    color-mix(in srgb, var(--cream) 11%, transparent) 50%,
    color-mix(in srgb, var(--cream) 5%, transparent) 70%);
  background-size: 280% 100%;
}
@media (prefers-reduced-motion: no-preference) {
  .cd-skel { animation: cdSkel 1.1s linear infinite; }
}
@keyframes cdSkel { from { background-position: 140% 0; } to { background-position: -40% 0; } }

.cart-drawer-backdrop {
  position: fixed; inset: 0; background: rgba(0, 0, 0, .55); z-index: 198;
  opacity: 0; visibility: hidden; transition: opacity .3s var(--ease), visibility .3s;
  -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
}
.cart-drawer-backdrop.open { opacity: 1; visibility: visible; }

.cart-drawer {
  position: fixed; z-index: 199; display: flex; flex-direction: column;
  background: var(--ink-2); color: var(--cream);
  top: 0; right: 0; height: 100vh; height: 100dvh; width: 410px; max-width: 92vw;
  border-left: 1px solid var(--border);
  box-shadow: -24px 0 60px rgba(0, 0, 0, .4);
  /* visibility gated like .search-overlay/nav drawer — transform alone leaves
     the closed drawer's buttons reachable by Tab (same bug class fixed on the
     nav drawer). visibility flips after the slide-out finishes. */
  transform: translateX(101%); visibility: hidden;
  transition: transform .4s var(--ease), visibility 0s .4s;
}
.cart-drawer.open { transform: none; visibility: visible; transition: transform .4s var(--ease); }
.cart-drawer input[name="csrfmiddlewaretoken"] { display: none; }
/* `hidden` must win over the display:flex rules below (empty state, footer). */
.cart-drawer [hidden] { display: none !important; }

.cd-head { display: flex; align-items: center; justify-content: space-between; padding: 20px 22px 14px; border-bottom: 1px solid var(--border); }
.cd-head h2 { font-family: var(--font-display); font-size: 1.25rem; font-weight: 600; letter-spacing: .02em; }
.cd-count { color: var(--muted); font-size: .9rem; font-weight: 500; }
.cd-close { all: unset; cursor: pointer; font-size: 1.9rem; line-height: 1; color: var(--muted); padding: 0 4px; transition: color .15s; }
.cd-close:hover { color: var(--cream); }

.cd-freeship { padding: 11px 22px; font-size: .82rem; color: var(--flame-1); background: color-mix(in srgb, var(--flame-1) 9%, transparent); border-bottom: 1px solid var(--border); }

.cd-items { flex: 1 1 auto; overflow-y: auto; padding: 4px 22px; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
.cd-item { display: grid; grid-template-columns: 64px 1fr auto; gap: 13px; padding: 15px 0; border-bottom: 1px solid var(--border); transition: opacity .2s; }
.cd-item.cd-busy { opacity: .45; pointer-events: none; }
.cd-thumb, .cd-thumb-ph { width: 64px; height: 80px; border-radius: 10px; object-fit: cover; background: var(--ink); display: block; }
.cd-thumb-link { display: block; }
.cd-meta { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.cd-name { font-weight: 600; font-size: .92rem; color: var(--cream); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cd-sub { color: var(--muted); font-size: .8rem; }
.cd-qty { display: inline-flex; align-items: center; border: 1px solid var(--border-2); border-radius: 100px; width: fit-content; margin-top: 4px; overflow: hidden; }
.cd-qty button { width: 32px; height: 32px; background: none; border: 0; color: var(--cream); font-size: 1.1rem; cursor: pointer; }
.cd-qty span { min-width: 26px; text-align: center; font-weight: 600; font-size: .9rem; }
.cd-end { display: flex; flex-direction: column; align-items: flex-end; justify-content: space-between; }
.cd-line { font-weight: 700; font-size: .92rem; white-space: nowrap; }
.cd-remove {
  all: unset; cursor: pointer; color: var(--muted);
  display: grid; place-items: center; width: 32px; height: 32px; border-radius: 9px;
  transition: color .15s var(--ease), background .15s var(--ease);
}
.cd-remove svg { width: 17px; height: 17px; }
.cd-remove:hover { color: var(--flame-1); background: color-mix(in srgb, var(--flame-1) 10%, transparent); }

.cd-empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; padding: 50px 30px; text-align: center; color: var(--muted); }

.cd-foot { padding: 16px 22px calc(16px + env(safe-area-inset-bottom, 0px)); border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 7px; }
.cd-row { display: flex; justify-content: space-between; font-size: .9rem; }
.cd-row.cd-muted { color: var(--muted); }
.cd-row.cd-total { font-weight: 700; font-size: 1.05rem; margin: 4px 0 10px; padding-top: 11px; border-top: 1px solid var(--border); }
/* Same delivery-zone note as the cart/checkout summaries, drawer-sized. */
.cd-row.cd-note { font-size: .78rem; margin-top: -2px; }
/* Same trap as .summary-row[hidden]: .cd-row's flex beats the [hidden] UA rule. */
.cd-row[hidden] { display: none; }
.cd-viewcart { text-align: center; font-size: .82rem; color: var(--muted); padding: 6px; }
.cd-viewcart:hover { color: var(--cream); }

@media (max-width: 880px) {
  .cart-drawer {
    top: auto; bottom: 0; left: 0; right: 0; width: auto; max-width: none;
    height: auto; max-height: 88vh; max-height: 88dvh;
    border-left: 0; border-top: 1px solid var(--border);
    border-radius: 20px 20px 0 0;
    box-shadow: 0 -20px 60px rgba(0, 0, 0, .45);
    transform: translateY(101%);
  }
  .cart-drawer.open { transform: none; }
}

/* ============================================================
   24. Mobile search overlay
   Full-screen instant search opened from the Search tab. The bar
   submits to the existing shop?q= results page; suggestions are
   fetched as-you-type.
   ============================================================ */
.search-overlay {
  position: fixed; inset: 0; z-index: 205; background: var(--ink);
  display: flex; flex-direction: column;
  opacity: 0; visibility: hidden; transform: translateY(-8px);
  transition: opacity .25s var(--ease), transform .25s var(--ease), visibility .25s;
}
.search-overlay.open { opacity: 1; visibility: visible; transform: none; }
.so-bar {
  display: flex; align-items: center; gap: 10px;
  padding: calc(14px + env(safe-area-inset-top, 0px)) 16px 14px;
  border-bottom: 1px solid var(--border); background: var(--ink-2);
}
.so-icon { width: 20px; height: 20px; color: var(--muted); flex: 0 0 auto; }
.so-bar input {
  flex: 1; min-width: 0; background: none; border: 0; outline: none;
  color: var(--cream); font-size: 1.05rem; font-family: var(--font-body); padding: 8px 0;
}
.so-cancel { all: unset; cursor: pointer; color: var(--flame-1); font-weight: 600; font-size: .9rem; padding: 6px; flex: 0 0 auto; }
.so-body { flex: 1; overflow-y: auto; padding: 16px; -webkit-overflow-scrolling: touch; }
.so-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; font-size: .72rem; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); }
.so-head button { all: unset; cursor: pointer; color: var(--muted); font-size: .7rem; text-transform: uppercase; letter-spacing: .05em; }
.so-head button:hover { color: var(--flame-1); }
.so-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
.so-chip { all: unset; cursor: pointer; padding: 8px 14px; border: 1px solid var(--border-2); border-radius: 100px; font-size: .85rem; color: var(--cream); -webkit-tap-highlight-color: transparent; }
.so-chip:hover, .so-chip:active { border-color: var(--flame-1); }
.so-result { display: flex; align-items: center; gap: 13px; padding: 11px 0; border-bottom: 1px solid var(--border); }
.so-thumb, .so-thumb-ph { width: 48px; height: 60px; border-radius: 8px; overflow: hidden; flex: 0 0 auto; background: var(--ink-2); display: block; }
.so-thumb img { width: 100%; height: 100%; object-fit: cover; }
.so-rmeta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.so-rname { font-weight: 600; color: var(--cream); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.so-rcol { font-size: .78rem; color: var(--muted); }
.so-rprice { font-weight: 700; white-space: nowrap; }
.so-seeall { display: block; text-align: center; padding: 16px; color: var(--flame-1); font-weight: 600; font-size: .9rem; }
.so-hint, .so-empty { color: var(--muted); text-align: center; padding: 30px 16px; font-size: .9rem; }

/* 5 tabs need a touch less label on the smallest phones */
@media (max-width: 374px) {
  .bn-item { font-size: .56rem; letter-spacing: .03em; }
  .bn-item svg { width: 21px; height: 21px; }
}

/* ============================================================
   25. Wishlist — heart toggles, save pill, saved page
   ============================================================ */
/* `hidden` must always beat author display rules (badge, empty states, drawer). */
[hidden] { display: none !important; }

.wish-btn {
  position: absolute; top: 10px; right: 10px; z-index: 6;
  width: 38px; height: 38px; border-radius: 50%; border: 0;
  background: rgba(10, 10, 12, .42); -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  display: grid; place-items: center; cursor: pointer; color: #fff;
  transition: transform .15s var(--ease), background .2s, color .2s;
  -webkit-tap-highlight-color: transparent;
}
.wish-btn:hover { background: rgba(10, 10, 12, .6); }
.wish-btn:active { transform: scale(.85); }
.wish-heart { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 2; transition: fill .15s, stroke .15s; }
.wish-btn.is-saved { color: var(--flame-1); }
.wish-btn.is-saved .wish-heart { fill: var(--flame-1); stroke: var(--flame-1); }

.wish-pill {
  display: inline-flex; align-items: center; gap: 8px; margin: 14px 0 4px;
  padding: 10px 18px; border: 1px solid var(--border-2); border-radius: 100px;
  background: transparent; color: var(--cream); cursor: pointer; font-weight: 600; font-size: .85rem;
  transition: border-color .2s, color .2s; -webkit-tap-highlight-color: transparent;
}
.wish-pill:hover { border-color: var(--cream); }
.wish-pill .wish-heart { width: 17px; height: 17px; }
/* Icon-only variant — the product page's save control, which sits beside the
   title as a secondary action rather than in the middle of the buy flow. */
.wish-icon { width: 46px; height: 46px; padding: 0; margin: 0; justify-content: center; border-radius: 50%; flex: 0 0 auto; }
.wish-icon .wish-heart { width: 19px; height: 19px; }
.wish-pill.is-saved { border-color: var(--flame-1); color: var(--flame-1); }
.wish-pill.is-saved .wish-heart { fill: var(--flame-1); stroke: var(--flame-1); }

/* Saved page */
.wl-card { position: relative; }
.wl-media { display: block; aspect-ratio: 4/5; border-radius: var(--radius); overflow: hidden; background: var(--ink-2); border: 1px solid var(--border); }
.wl-media img { width: 100%; height: 100%; object-fit: cover; }
.wl-media .ph { width: 100%; height: 100%; display: grid; place-items: center; color: var(--muted); padding: 14px; text-align: center; }
.wl-body { display: block; padding: 12px 2px 4px; }
.wl-card .card-name { font-weight: 600; margin-bottom: 4px; }
.wl-empty { text-align: center; max-width: 420px; margin: 30px auto 60px; color: var(--muted); display: flex; flex-direction: column; align-items: center; gap: 14px; }
.wl-empty svg { color: var(--border-2); }
.wl-empty h2 { font-family: var(--font-display); font-size: 1.5rem; color: var(--cream); }

/* ============================================================
   26. A11y polish for the mobile components
   ============================================================ */
/* Keyboard focus is clearly visible on every new interactive element. */
.bn-item:focus-visible,
.wish-btn:focus-visible,
.wish-pill:focus-visible,
.cd-close:focus-visible,
.cd-qty button:focus-visible,
.cd-remove:focus-visible,
.so-cancel:focus-visible,
.so-chip:focus-visible,
.so-result:focus-visible,
.pd-dot:focus-visible {
  outline: 2px solid var(--flame-1);
  outline-offset: 2px;
  border-radius: 8px;
}

/* Honour reduced-motion on the new surfaces: no slides, fades or pop scales. */
@media (prefers-reduced-motion: reduce) {
  .cart-drawer,
  .cart-drawer-backdrop,
  .search-overlay,
  .bn-item,
  .wish-btn,
  .cd-item,
  .pd-dot { transition: none !important; }
  .bn-item:active,
  .wish-btn:active { transform: none !important; }
}

/* ═══════════════════════════════════════════════════════════════════════
   LUX FOUNDATION — cursor, view transitions, selection, ripple.
   Continuous effects live in js/lux.js; everything here degrades cleanly.
   ═══════════════════════════════════════════════════════════════════════ */

/* Text selection carries the brand. */
::selection { background: #F2541B; color: #FFF7F0; }

/* Scrollbar — thin, flame thumb, invisible track. */
html { scrollbar-width: thin; scrollbar-color: rgba(242, 84, 27, .55) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: rgba(242, 84, 27, .5); border-radius: 8px;
  border: 3px solid transparent; background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: rgba(242, 84, 27, .8); border: 3px solid transparent; background-clip: content-box; }

/* 2026-07-05: retired the "Vice dawn" light-mode signature moves (hot-pink
   word sweep, pink dawn-sky body wash, magenta selection/scrollbar) — owner
   didn't like the pink. Both themes now share one accent family: the plain
   --flame gradient (orange) is used wherever .flame-text/.text-flame appear,
   and selection/scrollbar use the shared orange rule above in both themes —
   no light-specific override needed any more. */

/* Cross-page view transitions (MPA) — Chrome+; others simply ignore. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
  ::view-transition-old(root) { animation: luxVtOut .2s ease both; }
  ::view-transition-new(root) { animation: luxVtIn .3s ease both; }
  /* Shop-grid FLIP: cards carry per-product view-transition-names (see
     _product_card.html `vt` flag), so on filter/sort navigations the browser
     morphs each surviving card to its new grid position on the house curve
     instead of crossfading the whole page. */
  ::view-transition-group(*) { animation-duration: .32s; animation-timing-function: var(--ease); }
}
@keyframes luxVtOut { to   { opacity: 0; } }
@keyframes luxVtIn  { from { opacity: 0; transform: translateY(10px); } }


/* ── Cart badge bump (class toggled by lux.js on count change) ─────────── */
.cart-count.bump { animation: luxBump .45s cubic-bezier(.2, 1.6, .4, 1); }
@keyframes luxBump { 0% { transform: scale(1); } 35% { transform: scale(1.45); } 100% { transform: scale(1); } }

/* ── Flame ripple on primary buttons ───────────────────────────────────── */
.btn-flame { position: relative; overflow: hidden; }
.lux-ripple {
  position: absolute; width: 10px; height: 10px; margin: -5px 0 0 -5px;
  border-radius: 50%; background: rgba(255, 247, 240, .55);
  pointer-events: none; animation: luxRipple .6s ease-out forwards;
}
@keyframes luxRipple { to { transform: scale(24); opacity: 0; } }

/* ── Intro curtain (injected by lux.js, home only, once per session) ───── */
.lux-intro {
  position: fixed; inset: 0; z-index: 10000;
  background: #0E0D11;
  display: flex; align-items: center; justify-content: center; gap: .35em;
  font-family: var(--font-display, sans-serif);
  font-size: clamp(2.2rem, 7vw, 4.6rem); font-weight: 700; letter-spacing: -.02em;
  transition: transform .55s cubic-bezier(.7, 0, .2, 1) .75s;
}
.lux-intro .li-word { display: inline-block; overflow: hidden; }
.lux-intro .li-in {
  display: inline-block; color: #F6F2EA; transform: translateY(115%);
  transition: transform .6s cubic-bezier(.19, .8, .22, 1) .08s;
}
.lux-intro .li-in.li-flame { color: #F2541B; transition-delay: .17s; }
.lux-intro.go { transform: translateY(-100%); }
.lux-intro.go .li-in { transform: none; }

/* ── Footer wordmark — the brand's exit note ──────────────────────────────
   The header mark has smoke coming off it. This is where that smoke goes, and
   where the ember finally catches: three ribbons drift up behind the letters,
   and the flame pours into them from the bottom as the block enters view.

   The fill is a real animation of a typed custom property, not a background
   position trick, so it can ease properly and stop exactly where told. Both the
   drift and the fill are opacity/transform/@property only — nothing here causes
   layout — and the whole thing is off under reduced-motion, where the mark
   simply arrives already lit.                                                */
.footer-mark-wrap { position: relative; isolation: isolate; overflow: hidden; }

/* THE GROUND IS GONE AGAIN (2026-07-29, owner: "bring back the 2 days before
   version"). `.fm-ground` and the veil that muted it — the flatlay moved here
   when the First Access band was deleted — are removed, restoring the state this
   block was in before that move: the torch burning on the footer's own colour
   with nothing behind the letters but three drifting wisps. The photographs the
   owner supplied now carry the upper footer instead, and a third one under the
   name was one too many. Git has the ground if it is ever wanted back. */

/* Each wisp is its own small, correctly-proportioned SVG — no
   `preserveAspectRatio="none"` override, so it keeps the ribbon's real 1:3
   aspect instead of being stretched to fill a box roughly 8x wider than tall.
   That stretch was the bug: it squashed a rising ribbon into a flat wide smear
   that read as a grey stain rather than smoke. Three small, correctly-shaped
   ones, faintly visible and gently drifting, read as smoke; one big distorted
   one read as a mistake. */
.fm-wisp {
  position: absolute; bottom: 4%; width: clamp(30px, 4.5vw, 58px); height: auto;
  color: var(--cream); opacity: 0; pointer-events: none;
  transform-origin: 50% 100%;
}
@media (prefers-reduced-motion: no-preference) {
  .fm-wisp { animation: fmDrift 20s ease-in-out var(--fd, 0s) infinite; }
}
@keyframes fmDrift {
  0%   { opacity: 0; transform: translateY(20%) scale(1); }
  20%  { opacity: .22; }
  75%  { opacity: .1; }
  100% { opacity: 0; transform: translateY(-160%) scale(1.35) translateX(-8%); }
}

/* THE TORCH (see the note in base.html). Two stacked layers of the same
   letters: `.fm-cast` is the permanent outline, `.fm-burn` is the flame-filled
   copy masked down to one moving pool of light. Nothing here animates layout —
   it is transform, opacity and mask-position only. */
.footer-mark {
  display: grid;
  position: relative;
  font-family: var(--font-display);
  /* THE MARK IS SET TO THE PAGE, NOT GUESSED (2026-07-29). It used to be a
     hand-tuned clamp — 12.5vw with a floor and a ceiling — which meant the
     wordmark landed at a different, arbitrary margin at every width: nearly
     touching the edges on a phone, floating in the middle of a desktop.

     The number below is measured, not chosen. Rendered in Archivo 900 at
     font-stretch 88% and -.015em, "CUBANO CLAN" is exactly 7.0061x its own
     font-size wide, so a font-size of (available width / 7.0061) sets the mark
     flush to the measure at every viewport. 42px is taken off the viewport
     first: 24px for this element's own padding and ~18px for a desktop
     scrollbar, which 100vw includes and the layout does not get. The 15rem cap
     is the one aesthetic judgement — past that the mark stops reading as a
     signature and starts reading as a wall. */
  font-size: min(calc(14.273vw - 6px), 15rem);
  /* Black, and compressed on the width axis rather than by tracking: a real
     narrow cut keeps the stems at full strength, where negative letter-spacing
     at this size just collides the letterforms. */
  font-weight: 900; font-stretch: 88%; letter-spacing: -.015em; line-height: 1;
  padding: clamp(20px, 4vw, 44px) 12px .12em;
  user-select: none;
  /* The letters rise from below their own baseline, so the box has to clip or
     they would appear over the trust row above. */
  overflow: hidden;
}
.fm-layer {
  grid-area: 1 / 1;
  display: flex; justify-content: center; white-space: pre;
}
.fm-ch {
  display: inline-block;
  transform: translateY(115%) rotate(4deg);
  opacity: 0;
  transition: transform .95s cubic-bezier(.16, 1, .3, 1), opacity .5s linear;
  transition-delay: calc(var(--c, 0) * 42ms);
}
.footer-mark.lit .fm-ch { transform: none; opacity: 1; }
/* Cast: hollow letters, the mark at rest. */
.fm-cast .fm-ch {
  color: transparent;
  -webkit-text-stroke: 1.5px color-mix(in srgb, var(--cream) 34%, transparent);
}
/* Paper needs a heavier hairline than a dark ground does. 34% of near-black on
   #F5F5F7 is a grey the eye reads as unfinished; the same 34% of cream on ink
   reads as engraved. Two different problems, so two different values, and the
   stroke thickens slightly because a light ground eats thin dark lines. */
/* Raised from 58% after seeing the two side by side: on paper the mark was a
   pale grey ghost where the dark build reads as engraved metal. 58% of near-
   black on white lands around #767678 — a value the eye files as "unfinished
   placeholder", not "drawn". 76% at 2.4px is the same weight relationship the
   dark theme's 34%-of-cream has against ink, which is the actual thing being
   matched: not the number, the contrast ratio. */
[data-theme="light"] .fm-cast .fm-ch {
  -webkit-text-stroke: 2.4px color-mix(in srgb, var(--cream) 76%, transparent);
}
/* The pool of light also has to work on paper: the flame gradient is darker in
   this theme (terracotta/rust, chosen for AA on white), so the glow that sells
   it on black would just muddy it here. It is halved and tightened. */
[data-theme="light"] .fm-burn {
  filter: drop-shadow(0 0 14px color-mix(in srgb, var(--flame-1) 30%, transparent));
}
/* Burn: the same letters, filled with the flame gradient, then masked. The mask
   is one soft circle on an oversized canvas, so moving `mask-position` slides
   the pool of light across the wordmark without touching anything else. */
.fm-burn { pointer-events: none; }
.fm-burn .fm-ch {
  background-image: linear-gradient(100deg, var(--flame-1), var(--flame-2));
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.fm-burn {
  /* The pool of light: a soft circle sized to a THIRD of the wordmark's width,
     on a no-repeat mask, so `mask-position` from 0% to 100% walks it from the
     left edge of the first letter to the right edge of the last one. Sizing it
     as a fraction of the element (not in px) keeps the same look from a 390px
     phone to a 2560px display. */
  -webkit-mask-image: radial-gradient(closest-side circle, #000 0 26%, rgba(0,0,0,.55) 52%, transparent 80%);
  mask-image: radial-gradient(closest-side circle, #000 0 26%, rgba(0,0,0,.55) 52%, transparent 80%);
  -webkit-mask-size: 34% 340%; mask-size: 34% 340%;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-position: 50% 50%; mask-position: 50% 50%;
  filter: drop-shadow(0 0 22px color-mix(in srgb, var(--flame-1) 45%, transparent));
  transition: -webkit-mask-position .35s var(--ease), mask-position .35s var(--ease);
}
/* No pointer in the room: the light walks the wordmark by itself. */
.fm-burn { animation: fmTorch 11s ease-in-out infinite alternate; }
@keyframes fmTorch {
  from { -webkit-mask-position: 0% 50%; mask-position: 0% 50%; }
  to   { -webkit-mask-position: 100% 50%; mask-position: 100% 50%; }
}
/* Cursor takes over: JS adds `.fm-live` and writes mask-position per frame, so
   the drift animation has to stand down or the two would fight. */
.footer-mark.fm-live .fm-burn { animation: none; transition-duration: .12s; }
/* Where masks aren't supported the burn layer would render as a solid flame
   wordmark on top of the outline — which is still a perfectly good mark, so
   this degrades rather than breaks. Reduced motion keeps it lit and still. */
@media (prefers-reduced-motion: reduce) {
  .fm-ch { transition: none; transform: none; opacity: 1; }
  .fm-burn { animation: none; }
}

/* ── Product card upgrades: honest scarcity, size clarity ──
   The .badge-new override that used to live here (a dark green pill) is gone —
   it was a later rule quietly beating the badge hierarchy defined up with the
   rest of the card, and green is not what "just landed" sounds like on this
   brand. NEW is the flame; see .badge-new above. */
/* Scarcity is the one job ember kept — see the accent-roles note at the top. */
.stock-tag.low { color: var(--ember-text); }
.stock-tag.low::before { background: var(--ember-2); animation: lowPulse 1.6s ease-in-out infinite; }
@keyframes lowPulse { 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) { .stock-tag.low::before { animation: none; } }
.card-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; min-height: 20px; }
.card-sizes { display: inline-flex; gap: 6px; margin-top: 8px; }
.card-sizes i {
  font-style: normal; font-family: var(--font-mono); font-size: .62rem; font-weight: 700;
  color: var(--muted); letter-spacing: .04em;
}

/* ── Footer: end-of-scroll trust strip ── */
.footer-trust {
  display: flex; justify-content: center; gap: clamp(18px, 5vw, 56px); flex-wrap: wrap;
  padding: 22px 0; margin-top: 8px;
  border-top: 1px solid var(--border);
  color: var(--muted); font-size: .82rem;
}
.footer-trust b { color: var(--flame-1); font-weight: 700; margin-right: 6px; }


/* == Brand logo: the real lockup, theme-swapped. Text wordmark retired from
     the header (still used in footer + intro where it works as typography). == */
.brand-logo { height: 36px; width: auto; display: none; transition: height .4s var(--ease); }
.site-header.scrolled .brand-logo { height: 30px; }
[data-theme="light"] .brand-logo--light { display: block; }
html:not([data-theme="light"]) .brand-logo--dark { display: block; }
/* NOTE: no over-hero variant swap. The header is sticky — it never actually sits
   on the hero photo, so in light mode a forced cream logo lands on the cream page
   background and disappears (the "invisible sliver" bug). Theme decides, always. */
/* CLAN sits in solid ink, never in the flame gradient. The logo band already
   ends in an orange block — a gradient word butted against it put two different
   oranges side by side and made the lockup read as two competing logos. Solid
   ink turns CLAN into the quiet half of the lockup and leaves the band as the
   single warm mark in the header. */
.brand-clan {
  font-family: var(--font-display); font-weight: 600; font-size: 1.06rem;
  letter-spacing: .26em; text-transform: uppercase; line-height: 1;
  color: var(--cream);
  padding-left: 11px; margin-left: 2px;
  border-left: 1px solid color-mix(in srgb, var(--cream) 22%, transparent);
  transform: translateY(1px); /* optical baseline with the bracket band */
  transition: color .25s var(--ease), border-color .25s var(--ease);
}
.brand { gap: 9px; }
.brand:hover .brand-clan { color: var(--flame-1); border-color: color-mix(in srgb, var(--flame-1) 45%, transparent); }
.site-header.scrolled .brand-clan { font-size: .98rem; }

/* Bug fix (2026-08-20): this whole "real lockup" block sits *after* the
   ≤520px mobile block earlier in the file (rule 7 in PROJECT.md — an
   unscoped override appended later silently beats an earlier mobile
   breakpoint at equal specificity). It was cancelling that block's
   `.brand-logo{height:28px}`, `.site-header.scrolled .brand-logo{height:25px}`
   and `.brand-clan{font-size:.92rem;...}`, so on phones the logo rendered at
   the full 36/30px height it needs ~100px of width for while only a sliver of
   flex space was left after "Clan" (rendering at the unscoped 1.06rem, not
   the intended .92rem) and the header actions took their share — the logo
   image was flex-shrunk down to a few px and read as a broken/cropped mark.
   Re-declared here, later in source order, so it actually wins. */
@media (max-width: 520px) {
  .brand-logo { height: 28px; }
  .site-header.scrolled .brand-logo { height: 25px; }
  .brand-clan { font-size: .92rem; letter-spacing: .18em; padding-left: 9px; }
}
/* Below 375px even the reduced lockup doesn't fit both the image and "Clan" —
   the image already spells the brand name, so the word is the one that gives
   way rather than either being crushed illegible. */
@media (max-width: 374px) {
  .brand-clan { display: none; }
  .brand { gap: 0; }
}

/* == The smoke ==============================================================
   The master lockup has a wisp curling off the flame cap, and the band crop we
   use in the header cut it away. It's back, and it's the owner's own curve: both
   ribbons were traced out of the master PNG (threshold → contour walk → bezier
   fit), so this is the logo, not an impression of it.

   Two layers do the work. `.bs-mark` is the wisp itself — it only ever sways, so
   the mark stays the mark. `.bs-ghost` is a second copy that rises off it, swells
   and dissipates on a longer loop; that's what sells "lit", and because it lives
   in its own layer the identity underneath never distorts. The viewBox carries
   150 units of headroom above the paths so the rising copies have somewhere to
   go, and a mask dissolves them into the header instead of clipping them.

   Everything animated is transform/opacity, so it stays on the compositor and
   costs nothing on a phone; the whole thing is off under reduced-motion.        */
.brand-lockup { position: relative; display: inline-flex; align-items: center; }
.brand-smoke {
  position: absolute; left: 79.5%; width: 15%;
  bottom: calc(100% - 3px); height: 28px;
  color: var(--muted);
  pointer-events: none; overflow: visible;
  -webkit-mask-image: linear-gradient(to bottom, transparent 2%, #000 46%);
  mask-image: linear-gradient(to bottom, transparent 2%, #000 46%);
  transition: height .4s var(--ease);
}
.site-header.scrolled .brand-smoke { height: 25px; }
/* Smoke reads as vapour, not as ink: a touch of warmth from the cap it leaves. */
.brand-smoke .bs-mark { opacity: .78; }
.brand-smoke .bs-ghost { opacity: 0; }
.brand:hover .brand-smoke { color: var(--flame-2); }

/* The cap itself keeps a slow ember under it — the reason there's smoke at all. */
.brand-ember {
  position: absolute; right: -3px; top: 50%; width: 26px; height: 26px;
  transform: translateY(-50%); pointer-events: none; border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--flame-1) 55%, transparent) 0%, transparent 68%);
  opacity: .32; filter: blur(3px);
}

@media (prefers-reduced-motion: no-preference) {
  .brand-smoke .bs-mark,
  .brand-smoke .bs-ghost { transform-box: fill-box; transform-origin: 50% 100%; }
  .bs-mark--a { animation: bsSway 11s cubic-bezier(.45, 0, .25, 1) infinite; }
  .bs-mark--b { animation: bsSway 13.5s cubic-bezier(.45, 0, .25, 1) -3.2s infinite; }
  .bs-ghost--a { animation: bsRise 8.5s cubic-bezier(.32, .72, 0, 1) infinite; }
  .bs-ghost--b { animation: bsRise 10.5s cubic-bezier(.32, .72, 0, 1) -4.5s infinite; }
  .brand-ember { animation: bsEmber 4.6s ease-in-out infinite; }
}
/* Sway is deliberately tiny. A logo that visibly wobbles stops being a logo. */
@keyframes bsSway {
  0%, 100% { transform: skewX(-1.1deg) translateY(0) scaleY(1); }
  38%      { transform: skewX(1.5deg) translateY(-2px) scaleY(1.02); }
  71%      { transform: skewX(.2deg) translateY(-1px) scaleY(.995); }
}
/* The ghost never reaches full strength — smoke you can see the edge of reads
   as a shape; smoke you half-catch reads as smoke. */
@keyframes bsRise {
  0%   { opacity: 0; transform: translate(0, 4px) scale(1); }
  22%  { opacity: .42; }
  60%  { opacity: .22; transform: translate(-7px, -78px) scale(1.14) skewX(5deg); }
  100% { opacity: 0; transform: translate(-13px, -160px) scale(1.3) skewX(9deg); }
}
@keyframes bsEmber {
  0%, 100% { opacity: .24; }
  50%      { opacity: .52; }
}


/* ── PDP polish (Phase D, 2026-07-10) ─────────────────────────────────── */
/* Gallery images aren't natively draggable — pointer-drag browses instead
   (see product_detail.html drag block). */
.pd-main img { -webkit-user-drag: none; user-select: none; }
@media (prefers-reduced-motion: no-preference) and (hover: hover) and (pointer: fine) {
  .pd-main img { transition: transform .6s var(--ease); }
  .pd-main:hover img { transform: scale(1.03); }
}
/* Related products: swipeable snap rail on phones (same pattern as the home
   drop rail); desktop keeps the standard grid. Scoped ≤640px per rule #7. */
@media (max-width: 640px) {
  .related-rail {
    grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 70%;
    overflow-x: auto; scroll-snap-type: x mandatory;
    padding: 2px 2px 10px;
    scrollbar-width: none; -webkit-overflow-scrolling: touch;
  }
  .related-rail::-webkit-scrollbar { display: none; }
  .related-rail > .card { scroll-snap-align: start; }
}

/* ── Checkout progress steps (Cart → Details → Confirm) ─────────────────── */
/* Presentation only — cart is step 01, the one-page checkout is 02/03. Mono
   numerals echo the drawer's editorial index. */
.co-steps {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  list-style: none; padding: 0; margin: -6px 0 22px;
  font-family: var(--font-mono); font-size: .74rem; letter-spacing: .05em;
  color: var(--muted-2); text-transform: uppercase;
}
.co-steps li { display: flex; align-items: center; gap: 8px; }
.co-steps li + li::before { content: "→"; color: var(--muted-2); opacity: .55; }
.co-steps li.current { color: var(--flame-1); font-weight: 700; }
.co-steps li.done, .co-steps li.done a { color: var(--cream); text-decoration: none; }
.co-steps li.done a:hover { color: var(--flame-1); }

/* Trust chips at the payment point — quiet, factual, directly above Confirm. */
.pay-trust {
  display: flex; gap: 6px; flex-wrap: wrap; margin-top: 16px;
}
.pay-trust span {
  flex: 1 1 auto; text-align: center; white-space: nowrap;
  font-size: .84rem; font-weight: 600; color: var(--muted);
  border: 1px solid var(--border); border-radius: 999px; padding: 7px 10px;
}
.pay-trust b { color: var(--cream); font-weight: 700; }

/* Who is ordering — a statement above the button, not a decision in front of
   it. Deliberately quiet: no border, no fill, nothing that reads as a control
   the customer has to resolve before they can continue. */
.co-who {
  display: flex; gap: 10px; align-items: flex-start;
  margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--border);
  font-size: .92rem; line-height: 1.6; color: var(--muted);
}
.co-who svg { flex-shrink: 0; margin-top: 3px; color: var(--muted); }
.co-who b { color: var(--cream); font-weight: 600; }
.co-who a {
  color: var(--flame-1); white-space: nowrap;
  border-bottom: 1px solid color-mix(in srgb, var(--flame-1) 40%, transparent);
  transition: border-color .16s var(--ease);
}
.co-who a:hover { border-color: var(--flame-1); }

/* ── About — editorial split, factual story, no poster-sized first screen ── */
.about-page main { overflow: clip; }

/* About & Contact — brass identity accent, not flame (2026-08-05, owner:
   "best coloring, other than orange/reddish"). Same mechanism as .pd-page's
   brass scoping above: the flame custom properties are swapped for brass at
   the page root, so every rule that already reads var(--flame-1)/var(--flame)
   — .eyebrow, .text-flame, .btn-flame, the hairline
   border tints, focus rings — inherits the new hue with no selector changes.
   --flame-ink is fixed dark in both themes here (not swapped per-theme like
   the orange version) because brass is the same light gold on both surfaces. */
.about-page,
.contact-page {
  --flame: var(--brass-grad);
  --flame-1: var(--brass);
  --flame-2: var(--brass-lo);
  --flame-ink: #1D1D1F;
  --glow: 0 10px 30px rgba(168, 121, 44, .3);
  --glow-hi: 0 14px 38px rgba(168, 121, 44, .4);
}

.about-hero {
  padding: clamp(18px, 2.5vw, 36px) 0 clamp(56px, 6vw, 88px);
}
.about-hero-grid {
  display: grid;
  grid-template-columns: minmax(0, .88fr) minmax(340px, .72fr);
  gap: clamp(42px, 7vw, 108px);
  align-items: center;
}
.about-hero-copy { padding-block: clamp(12px, 3vw, 44px); }
.about-hero-copy .eyebrow { margin-bottom: clamp(18px, 2.4vw, 30px); }
.about-hero-copy h1 {
  max-width: 18ch;
  font-size: clamp(2.9rem, 4vw, 3.85rem);
  line-height: .94;
  letter-spacing: -.025em;
  text-wrap: balance;
}
.about-lead {
  max-width: 40ch;
  margin-top: clamp(24px, 3vw, 38px);
  color: var(--muted);
  font-family: var(--font-serif);
  font-size: clamp(1.08rem, 1.45vw, 1.28rem);
  line-height: 1.5;
  text-wrap: pretty;
}
.about-facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin: clamp(30px, 4vw, 52px) 0 0;
  padding: 16px 0;
  border-block: 1px solid color-mix(in srgb, var(--flame-1) 24%, var(--border));
}
.about-facts div { min-width: 0; padding-right: 12px; }
.about-facts div + div { padding-left: 16px; border-left: 1px solid var(--border); }
.about-facts dt,
.about-facts dd,
.about-hero-media figcaption,
.about-fit > p {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: .11em;
}
.about-facts dt { color: var(--muted); font-size: .57rem; }
.about-facts dd { margin-top: 5px; color: var(--cream); font-size: .7rem; font-weight: 700; }

.about-hero-media {
  position: relative;
  margin: 0;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--ink-2);
  box-shadow: 14px 14px 0 color-mix(in srgb, var(--flame-1) 12%, transparent);
}
.about-hero-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.about-hero-media figcaption {
  position: absolute;
  inset: auto 0 0;
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 28px 18px 14px;
  color: #fff;
  background: linear-gradient(to top, rgba(12, 10, 8, .72), transparent);
  font-size: .56rem;
  font-weight: 700;
}

.about-story {
  padding: clamp(72px, 9vw, 132px) 0;
  background: var(--ink-2);
  border-block: 1px solid var(--border);
}
/* 2026-08-06: these were two-column grids whose narrow first column held the
   numbered chapter header (01 / Origin). The owner cut those, and a two-column
   grid with one child would drop the copy into the 150px column — so the blocks
   are single-column now. */
.about-origin,
.about-proof {
  display: block;
}
.about-origin {
  padding-bottom: clamp(80px, 10vw, 148px);
}
.about-origin-copy,
.about-proof-copy { max-width: 820px; }
/* Wider than the old 14ch/9ch: the headings are the owner's own sentences now,
   not two-word chapter titles. */
.about-story h2 {
  max-width: 20ch;
  font-size: clamp(2.45rem, 4.8vw, 4rem);
  line-height: .95;
  letter-spacing: -.02em;
  text-wrap: balance;
}
.about-standfirst {
  max-width: 54ch;
  margin-top: clamp(22px, 3vw, 34px);
  color: var(--cream);
  font-family: var(--font-serif);
  font-size: clamp(1.16rem, 1.7vw, 1.5rem);
  line-height: 1.48;
  text-wrap: pretty;
}
.about-standfirst + p {
  max-width: 62ch;
  margin-top: 20px;
  color: var(--muted);
  font-size: .98rem;
  line-height: 1.68;
}

.about-craft {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(300px, .65fr);
  gap: clamp(42px, 6vw, 92px);
  align-items: center;
  padding: clamp(30px, 4vw, 58px) 0 clamp(88px, 11vw, 158px);
}
.about-craft-media {
  margin: 0;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--ink);
}
.about-craft-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.about-craft-copy h2 {
  max-width: 15ch;
  font-size: clamp(2.3rem, 4vw, 3.55rem);
}
.about-fit { margin-top: clamp(28px, 4vw, 42px); }
.about-fit > p {
  margin-bottom: 10px;
  color: var(--muted);
  font-size: .58rem;
  font-weight: 700;
}
/* The chart artwork, on the About page's product chapter. A hairline frame in
   the flame edge so a dark JPEG on a dark section still reads as a placed
   object rather than a hole; on light it keeps the same edge, which is the
   one-palette rule. */
.about-chart {
  display: block;
  margin-top: 4px;
  border: 1px solid color-mix(in srgb, var(--flame-1) 26%, var(--border));
  border-radius: var(--radius);
  overflow: hidden;
}
.about-chart img { display: block; width: 100%; height: auto; }

.about-proof {
  padding-top: clamp(58px, 7vw, 96px);
  border-top: 1px solid color-mix(in srgb, var(--flame-1) 22%, var(--border));
}
.about-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 22px;
  margin-top: 30px;
}
.about-social-link {
  color: var(--cream);
  font-size: .84rem;
  font-weight: 700;
  text-decoration: none;
  text-underline-offset: 5px;
}
.about-social-link span {
  display: inline-block;
  margin-left: 4px;
  transition: transform .45s cubic-bezier(.2,.8,.2,1);
}
.about-social-link:hover { text-decoration: underline; }
.about-social-link:hover span { transform: translate(2px, -2px); }
.about-social-link:focus-visible { outline: 2px solid var(--flame-1); outline-offset: 5px; }

.about-contact {
  padding-block: clamp(70px, 8vw, 112px);
}
.about-contact .contact-layout { margin-top: 0; }
/* A form must never masquerade as empty space while a decorative observer waits. */
.about-page .about-contact .reveal {
  opacity: 1;
  transform: none;
  transition: none;
}
.about-contact-head { margin-bottom: clamp(32px, 5vw, 58px); }
.about-contact-head h2 {
  max-width: 16ch;
  font-size: clamp(2.35rem, 4vw, 3.65rem);
  line-height: .98;
}
.about-contact-head .muted {
  max-width: 52ch;
  margin-top: 14px;
  font-family: var(--font-serif);
  font-size: 1.06rem;
  line-height: 1.5;
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .about-origin-copy,
    .about-craft-copy,
    .about-proof-copy {
      animation: aboutReveal both;
      animation-timeline: view();
      animation-range: entry 5% cover 28%;
    }
  }
}
@keyframes aboutReveal {
  from { opacity: .25; transform: translateY(28px); }
  to { opacity: 1; transform: none; }
}

@media (max-width: 860px) {
  .about-hero { padding: 0 0 64px; }
  .about-hero-grid { grid-template-columns: minmax(0, 1fr); gap: 36px; }
  .about-hero-media {
    order: -1;
    width: calc(100% + 48px);
    margin-left: -24px;
    aspect-ratio: 3 / 2;
    box-shadow: none;
  }
  .about-hero-copy { padding: 0; }
  .about-hero-copy .eyebrow { margin-bottom: 16px; }
  .about-hero-copy h1 {
    max-width: none;
    font-size: clamp(2.15rem, 9.4vw, 2.65rem);
    line-height: .98;
  }
  .about-lead { margin-top: 20px; font-size: 1.02rem; }
  .about-facts { margin-top: 28px; padding-block: 13px; }
  .about-facts div { padding-right: 7px; }
  .about-facts div + div { padding-left: 8px; }
  .about-facts dt { font-size: .5rem; letter-spacing: .06em; }
  .about-facts dd { font-size: .61rem; line-height: 1.25; }

  .about-story { padding: 68px 0 76px; }
  .about-origin,
  .about-proof,
  .about-craft { grid-template-columns: minmax(0, 1fr); gap: 28px; }
  .about-origin { padding-bottom: 78px; }
  .about-story h2 { max-width: none; font-size: clamp(2.25rem, 10vw, 2.8rem); }
  .about-standfirst { margin-top: 20px; font-size: 1.12rem; }
  .about-standfirst + p { margin-top: 16px; }

  .about-craft { padding: 0 0 82px; }
  .about-craft-media {
    width: calc(100% + 48px);
    margin-left: -24px;
    aspect-ratio: 4 / 3;
  }
  .about-craft-copy h2 { max-width: none; }

  .about-proof { padding-top: 54px; }
  .about-actions { align-items: flex-start; flex-direction: column; gap: 18px; }
  .about-contact { padding-block: 64px 78px; }
  .about-contact-head h2 { font-size: clamp(2.2rem, 9.6vw, 2.75rem); }
}

/* =================================================================
   Journal (blog) — the store's slowest surface, so it is set as
   reading matter: one measure, real leading, and no card chrome.
   ================================================================= */
.blog-masthead { max-width: 46ch; margin-bottom: clamp(38px, 6vw, 72px); }
.blog-masthead h1 { font-size: clamp(2.6rem, 7vw, 5.4rem); line-height: .92; text-transform: uppercase; letter-spacing: -.03em; }
.blog-masthead p { color: var(--muted); margin-top: 16px; font-size: 1.02rem; }

.blog-date {
  font-family: var(--font-mono); font-size: .62rem; font-weight: 700;
  letter-spacing: .22em; text-transform: uppercase; color: var(--muted-2);
}

.blog-list { display: grid; }
.blog-row { border-top: 1px solid var(--border); }
.blog-row:last-child { border-bottom: 1px solid var(--border); }
.blog-row-link {
  display: grid; grid-template-columns: minmax(0, 260px) minmax(0, 1fr);
  gap: clamp(20px, 4vw, 48px); align-items: center;
  padding: clamp(22px, 3.4vw, 40px) 0; color: inherit;
}
/* the lead entry gets a bigger plate and bigger type; the rest stay a list */
.blog-row--lead .blog-row-link { grid-template-columns: minmax(0, 420px) minmax(0, 1fr); }
.blog-row--lead h2 { font-size: clamp(1.7rem, 3.4vw, 2.7rem); }
/* a post with no cover gets the full measure instead of a narrow orphan column */
.blog-row-body:only-child { grid-column: 1 / -1; }
.blog-row-media { overflow: hidden; border-radius: 3px; aspect-ratio: 4 / 3; background: var(--ink-3); }
.blog-row-media img { width: 100%; height: 100%; object-fit: cover; transition: transform 1s var(--ease); }
.blog-row-link:hover .blog-row-media img { transform: scale(1.04); }
.blog-row-body h2 {
  font-size: clamp(1.35rem, 2.4vw, 2rem); line-height: 1.08; margin: 12px 0 0;
  text-transform: uppercase; letter-spacing: -.015em; transition: color .2s var(--ease);
}
.blog-row-link:hover h2 { color: var(--flame-1); }
.blog-row-body p { color: var(--muted); margin-top: 12px; max-width: 62ch; }
.blog-more {
  display: inline-flex; align-items: center; gap: 8px; margin-top: 18px;
  font-family: var(--font-mono); font-size: .64rem; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase; color: var(--flame-1);
}
.blog-more svg { transition: transform .25s var(--ease); }
.blog-row-link:hover .blog-more svg { transform: translateX(4px); }

/* the rule spans the page; only the words are held to a measure */
.blog-empty { border-top: 1px solid var(--border); padding-top: clamp(30px, 5vw, 56px); }
.blog-empty > * { max-width: 46ch; }
.blog-empty-line { font-family: var(--font-display); font-weight: 600; text-transform: uppercase;
  font-size: clamp(1.6rem, 3.4vw, 2.4rem); letter-spacing: -.02em; }
.blog-empty p + p { color: var(--muted); margin: 14px 0 26px; }

/* ---- single post ---- */
.blog-post .container { max-width: 780px; }
.blog-post-head { margin-bottom: clamp(26px, 4vw, 44px); }
.blog-back {
  display: flex; width: fit-content; align-items: center; gap: 8px; margin-bottom: 26px;
  font-family: var(--font-mono); font-size: .64rem; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase; color: var(--muted);
}
.blog-back:hover { color: var(--flame-1); }
.blog-post-head .blog-date { display: block; }
.blog-post-head h1 {
  font-size: clamp(2.1rem, 5.2vw, 3.6rem); line-height: 1.04; margin: 14px 0 0;
  text-transform: uppercase; letter-spacing: -.03em; text-wrap: balance;
}
.blog-standfirst { color: var(--muted); font-size: 1.12rem; line-height: 1.6; margin-top: 18px; }
.blog-cover { margin: 0 0 clamp(28px, 4vw, 46px); border-radius: 3px; overflow: hidden; }
.blog-cover img { width: 100%; height: auto; display: block; }
/* one measure, generous leading — this is the only place on the site meant to be read straight through */
.blog-body { font-size: 1.08rem; line-height: 1.78; color: var(--cream); }
.blog-body p { margin: 0 0 1.25em; }
.blog-body p:first-child::first-letter {
  float: left; font-family: var(--font-display); font-weight: 700;
  font-size: 3.4em; line-height: .82; padding: .06em .12em 0 0; color: var(--flame-1);
}

.blog-more-rail { margin-top: clamp(44px, 6vw, 76px); border-top: 1px solid var(--border); padding-top: 22px; }
.blog-rail-label { font-family: var(--font-mono); font-size: .62rem; font-weight: 700;
  letter-spacing: .22em; text-transform: uppercase; color: var(--muted-2); }
.blog-rail-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px;
  background: var(--border); margin-top: 18px; }
.blog-rail-card { display: block; padding: 22px; background: var(--ink); color: inherit;
  transition: background .2s var(--ease); }
.blog-rail-card:hover { background: var(--ink-3); }
.blog-rail-card time { font-family: var(--font-mono); font-size: .6rem; font-weight: 700;
  letter-spacing: .2em; text-transform: uppercase; color: var(--muted-2); }
.blog-rail-title { display: block; margin-top: 10px; font-family: var(--font-display);
  font-weight: 600; font-size: 1.1rem; text-transform: uppercase; letter-spacing: -.01em; }

@media (max-width: 760px) {
  .blog-row-link, .blog-row--lead .blog-row-link { grid-template-columns: minmax(0, 1fr); }
  .blog-rail-grid { grid-template-columns: minmax(0, 1fr); }
  .blog-body p:first-child::first-letter { font-size: 2.8em; }
}

/* ═══ POLICY PAGES (/returns/, /privacy/) ═════════════════════════════════
   2026-08-06. Both pages used to be `.content-narrow` prose: an h1 at
   clamp(2.8rem, 7vw, 5.2rem) — hero scale, on a page nobody reads for pleasure
   — over stacked `.panel` cards of bulleted sentences. Too loud at the top,
   too undifferentiated below.

   Replaced by the invoice's terms block at page scale, which is what the owner
   asked for: a brass numeral, one plain line beside it, a hairline between
   rows. Two columns on desktop so six terms are one glance rather than one
   scroll; one column from 820px down. Nothing here is bespoke to either page —
   the same markup carries both, so they cannot drift apart. */
.policy-page .policy-wrap { padding: clamp(30px, 4vw, 56px) 0 clamp(56px, 7vw, 96px); }
.policy-head { max-width: 62ch; }
.policy-head h1 { font-size: clamp(2rem, 4vw, 3.1rem); line-height: 1.02; margin: 10px 0 0;
  letter-spacing: -.02em; text-transform: uppercase; }
.policy-head > p { margin: 16px 0 0; max-width: 56ch; font-family: var(--font-serif);
  font-size: clamp(1rem, 1.5vw, 1.12rem); line-height: 1.6; color: var(--muted); }

.policy-terms { list-style: none; margin: clamp(30px, 4vw, 46px) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(28px, 4vw, 64px); border-top: 1px solid var(--border); }
.policy-terms li { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start;
  gap: 16px; padding: 20px 2px; border-bottom: 1px solid var(--border); }
/* Tabular figures so 01–06 sit on one optical column down the page. */
.policy-terms .pt-n { font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: .78rem; font-weight: 700; letter-spacing: .08em; color: var(--brass-text);
  padding-top: .28em; }
.policy-terms p { margin: 0; font-size: clamp(.94rem, 1.2vw, 1.02rem); line-height: 1.55;
  color: var(--cream); }

.policy-foot { margin: clamp(26px, 3vw, 38px) 0 0; font-size: .9rem; color: var(--muted); }
.policy-foot a { color: var(--flame-1); }

@media (max-width: 820px) {
  .policy-terms { grid-template-columns: minmax(0, 1fr); }
}
