/* ==========================================================================
   Qumash - Landing page custom styles & animations
   (Tailwind handles the utility layer; this file holds fonts, tokens,
    fine-tuned type and the animation system.)
   ========================================================================== */

/* Make the `hidden` attribute always win, even on elements that carry a
   Tailwind display utility (flex / inline-flex / grid). Lets us hide any
   element with `hidden` and show it again by simply removing the attribute. */
[hidden] { display: none !important; }

:root {
  --brand-50: #FAF6F1;
  --brand-800: #55351F;
  --brand-950: #332E28;
  --brand-border: #4E483F;
  --ink-muted: #8A8175;
}

/* ---- Typography --------------------------------------------------------- */
html {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  scroll-behavior: smooth;
  /* Removes the ~300ms tap delay some touch browsers add while waiting to see
     if a tap is a double-tap-to-zoom, so a single tap on a product card feels
     instant. */
  touch-action: manipulation;
}

/* No grey flash box on tap (Android/iOS); cards have their own feedback. */
.product-card { -webkit-tap-highlight-color: transparent; }

body {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  color: #000;
  background: #FAF6F1;         /* Brand color/50 - the site's page ground */
  overflow-x: hidden;
}

.font-spectral {
  font-family: "Spectral", Georgia, "Times New Roman", serif;
}

/* Utility container matching Figma content width (1366px) */
.container-x {
  width: 100%;
  max-width: 1420px;
  margin-inline: auto;
  padding-inline: 1.5rem;
}
@media (min-width: 1280px) {
  .container-x { padding-inline: 2rem; }
}

/* Hide scrollbar on horizontal scrollers but keep functionality */
.no-scrollbar::-webkit-scrollbar { display: none; }
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }

/* ---- Offer bar (dismissible) ------------------------------------------- */
#offer-bar {
  max-height: 32px;
  overflow: hidden;
  transition: max-height .45s cubic-bezier(.4,0,.2,1), opacity .3s ease;
}
#offer-bar.offer-hidden,
html.offer-dismissed #offer-bar {
  max-height: 0;
  opacity: 0;
}
#offer-close svg { transition: transform .3s ease; }
#header-spacer { transition: height .45s cubic-bezier(.4,0,.2,1); }
html.offer-dismissed #header-spacer { height: 60px; }

/* ---- Frosted nav -------------------------------------------------------- */
.nav-frost {
  background: rgba(250, 246, 241, 0.72);
  backdrop-filter: blur(19.5px);
  -webkit-backdrop-filter: blur(19.5px);
}

/* Nav link hover underline */
.nav-link {
  position: relative;
}
/* Underline effect copied from houseofyellow.nl: a 2px bar that sweeps IN from
   the left on hover and OUT to the right on mouse-leave, via a left/right anchor
   swap (default anchored right w/ width 0 → hover anchored left w/ width 100%).
   The current page's link keeps a fixed underline and isn't clickable. */
.nav-link::after {
  content: "";
  position: absolute;
  bottom: -4px;
  top: auto;
  left: auto;
  right: 0;                    /* default: anchored right, zero width */
  width: 0;
  height: 2px;                 /* fat 2px */
  background: #332E28;         /* dark brand color (brand-950) */
  transition: left .3s, right .3s, width .3s, background .3s;
}
.nav-link:hover::after,
.nav-link.active::after {
  width: 100%;
  left: 0;                     /* anchor switches to left → in from left, out to the right */
  right: auto;
}
.nav-link.active { pointer-events: none; }   /* current page link (fixed underline) */

/* ---- Collections mega menu (desktop hover) ------------------------------ */
/* Panel drops from the nav. Hidden/inert by default; JS toggles `.mega-open`
   on hover of the trigger or the panel itself (with a small close delay). */
.mega-panel {
  z-index: 40;
  opacity: 0;
  visibility: hidden;
  transform: translateY(10px);
  pointer-events: none;
  transition: opacity .28s ease, transform .32s cubic-bezier(.2,.7,.2,1), visibility .28s;
}
.mega-panel.mega-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
  pointer-events: auto;
}
/* Caret rotates while the menu is open */
#mega-trigger.is-open .mega-caret { transform: rotate(180deg); }

/* Left-column text links: subtle pill highlight + brand color on hover */
.mega-link {
  display: block;
  width: 100%;
  transition: background-color .25s ease, color .25s ease;
}
.mega-link:hover {
  background: rgba(85, 53, 31, 0.06);
  color: #55351F;
}

/* Image cards: "View all" button is collapsed by default (Figma default state
   shows the title only) and slides in beneath the title on card hover. */
.mega-viewall {
  max-height: 0;
  margin-top: 0;
  opacity: 0;
  overflow: hidden;
  transform: translateY(6px);
  transition: max-height .4s ease, margin-top .4s ease,
              opacity .35s ease, transform .4s cubic-bezier(.2,.7,.2,1);
}
.mega-img-card:hover .mega-viewall {
  max-height: 40px;
  margin-top: 12px;
  opacity: 1;
  transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
  .mega-panel { transition: opacity .2s ease, visibility .2s; transform: none; }
  .mega-panel.mega-open { transform: none; }
  .mega-viewall,
  .mega-img-card img { transition: none; }
}

/* ---- Buttons ------------------------------------------------------------ */
/* ---- Glass ------------------------------------------------------------
   One frosted treatment for every button that floats over imagery: a
   translucent fill (each button keeps its own colour), a real backdrop blur,
   and a hairline highlight drawn as an inset ring so it costs no layout.
   Applied to .btn-glass, .btn-hero, .arrow-btn, .round-btn and the hero
   slider pills. Buttons on flat surfaces - the header hamburger, the offer
   bar's close, the footer's admin link - are left alone: glass over a solid
   background just reads as a slightly grey box.
   NOTE: this blur is ours, not Figma's. The frames for the hero CTA and the
   card action buttons specify a flat fill with no blur. */
.btn-glass,
.btn-hero,
.arrow-btn,
.arrow-btn-ink,
.round-btn,
.hero-dot {
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.16);
}

.btn-glass {
  background: rgba(0, 0, 0, 0.1);
  transition: transform .3s ease, background .3s ease, box-shadow .3s ease;
}
.btn-glass:hover {
  background: rgba(0, 0, 0, 0.28);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.34);
  transform: translateY(-2px);
}
.btn-glass svg { transition: transform .3s ease; }
.btn-glass:hover svg { transform: translateX(4px); }

/* ---- Cards -------------------------------------------------------------- */
/* All :hover effects are wrapped in `@media (hover: hover)` so they apply only
   to real pointers. On touch screens a hover style makes the browser spend the
   FIRST tap applying hover and only navigate on the second - gating it keeps
   product cards a single tap on mobile. */
.card-hover {
  transition: transform .5s cubic-bezier(.2,.7,.2,1), box-shadow .5s ease;
}
.card-hover .prod-gallery { transition: box-shadow .45s ease; }

@media (hover: hover) {
  .card-hover:hover {
    transform: translateY(-6px);
  }
  /* Product-card hover: elevate the image with a soft brand shadow */
  .card-hover:hover .prod-gallery {
    box-shadow: 0 22px 44px -16px rgba(51, 46, 40, 0.35);
  }
}

/* ---- Buy-now drawer (product page) ------------------------------------- */
/* Slides in from the right over a 25% scrim; click-outside / ✕ / Esc close. */
.buy-overlay {
  opacity: 0;
  visibility: hidden;
  transition: opacity .3s ease, visibility .3s ease;
}
.buy-overlay.open { opacity: 1; visibility: visible; }
.buy-drawer {
  transform: translateX(100%);
  transition: transform .35s cubic-bezier(.4, 0, .2, 1);
}
.buy-drawer.open { transform: translateX(0); }

/* Copy-confirmation toast (product page). Hidden and inert until JS adds
   .is-visible, so it never blocks clicks on the drawer beneath it. */
.buy-toast {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: translate(-50%, 10px);
  transition: opacity .25s ease, transform .25s ease, visibility .25s;
  max-width: min(90vw, 420px);
}
.buy-toast.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}
/* the reused card acts as a slider here → keep its arrows always visible */
/* In the drawer the card IS a slider: its arrows are always visible AND always
   tappable (including on touch, where the card itself doesn't navigate). This
   must re-enable pointer-events, which .prod-arrows disables by default. */
#buy-drawer-card .prod-arrows { opacity: 1; }
#buy-drawer-card .prod-arrows > button { pointer-events: auto; }

/* Product / category image zoom on hover (pointer devices only - see above) */
.img-zoom { overflow: hidden; }
/* Only the photo/clip zooms. Decorative overlays inside the same box carry
   aria-hidden (the blurred glow behind the action buttons), and must stay
   put - scaling them would drag the glow across the card. */
.img-zoom img:not([aria-hidden="true"]),
.img-zoom video {
  transition: transform .8s cubic-bezier(.2,.7,.2,1);
  will-change: transform;
}
@media (hover: hover) {
  .card-hover:hover .img-zoom img:not([aria-hidden="true"]),
  .card-hover:hover .img-zoom video,
  .img-zoom:hover img:not([aria-hidden="true"]),
  .img-zoom:hover video { transform: scale(1.06); }
}

/* Circular action buttons on product cards */
.round-btn {
  transition: transform .3s ease, background .3s ease, box-shadow .3s ease;
}
@media (hover: hover) {
  .round-btn:hover {
    transform: scale(1.1);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.34);
  }
}

/* Product carousel arrows reveal on hover.
   `pointer-events: none` while invisible is important: the arrow overlay spans
   the whole image, so without it the hidden arrows would swallow taps near the
   card's left/right edges (changing the slide instead of opening the product).
   On touch screens there is no hover, so they stay hidden and inert and the
   WHOLE card is a single tap - all photos are shown on the details page. */
/* The overlay CONTAINER spans the whole image but must never intercept taps -
   only the two arrow buttons do, and only while they are actually visible
   (invisible-but-tappable arrows would swallow taps meant to open the product). */
.prod-arrows {
  opacity: 0;
  pointer-events: none;
  transition: opacity .35s ease;
}
.prod-arrows > button { pointer-events: none; }

/* Pointer devices: arrows fade in on card hover. */
@media (hover: hover) {
  .group\/card:hover .prod-arrows { opacity: 1; }
  .group\/card:hover .prod-arrows > button { pointer-events: auto; }
}

/* Touch devices: there is no hover, so the arrows stay permanently visible and
   tappable. Tapping an arrow changes the photo; tapping anywhere else on the
   card still opens the product in one tap. */
@media (hover: none) {
  .prod-arrows { opacity: 1; }
  .prod-arrows > button { pointer-events: auto; }
}

/* Product card image gallery (4 images cycled by the arrows).
   Images intentionally carry NO z-index, so the overlay controls (arrows,
   dots, and the wishlist/cart buttons - all later in the DOM) always stack
   above the photo instead of being hidden behind it. */
.prod-gallery .prod-img {
  opacity: 0;
  /* keep the existing hover-zoom on transform, add a cross-fade on opacity */
  transition: opacity .5s ease, transform .8s cubic-bezier(.2,.7,.2,1);
}
.prod-gallery .prod-img.is-active { opacity: 1; }

.prod-dot {
  height: 6px;
  width: 6px;
  border-radius: 9999px;
  background: rgba(255, 255, 255, 0.5);
  transition: width .3s ease, background .3s ease;
}
.prod-dot.active {
  width: 18px;
  background: rgba(255, 255, 255, 0.95);
}

/* ---- Shop the Look - Reels card (Figma default/hover variants) ---------- */
/* The row scrolls horizontally, so it needs a little vertical breathing room
   of its own (the id selector wins over the utility classes on the element). */
#reels-row {
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;  /* the card has no drop shadow to clear any more */
  cursor: grab;
  scroll-behavior: auto;   /* per-frame auto-scroll must be instant, not smooth */
}
#reels-row.cursor-grabbing { cursor: grabbing; }

/* Reel card (Figma "Image" 10459:18700, Default + Hover).
   The Hover variant only deepens the bottom scrim to solid black; the glow
   and bag button fade in on the same 1.022s spring approximation as the
   product card's action column. */
.reel-scrim,
.reel-glow,
.reel-actions {
  opacity: 0;
  transition: opacity 1.022s cubic-bezier(.22, 1.28, .36, 1),
              transform 1.022s cubic-bezier(.22, 1.28, .36, 1);
}
.reel-actions { transform: translateX(26px); }

@media (hover: hover) {
  .reel-card:hover .reel-scrim,
  .reel-card:hover .reel-glow,
  .reel-card:hover .reel-actions { opacity: 1; }
  .reel-card:hover .reel-actions { transform: translateX(0); }
}

/* No hover on touch: show the Default scrim plus a visible, tappable button. */
@media (hover: none) {
  .reel-glow, .reel-actions { opacity: 1; }
  .reel-actions { transform: translateX(0); }
}

/* ---- WhatsApp float ----------------------------------------------------- */
.wa-float {
  animation: wa-bob 2.8s ease-in-out infinite;
  box-shadow: 0 10px 30px rgba(37, 211, 102, 0.4);
}
.wa-float:hover { animation-play-state: paused; transform: scale(1.08); }
/* ---------- Mobile buy bar backdrop -------------------------------------
   The bar used to be flat white with a hard shadow along its top edge, which
   cut the page in two. Instead it fades in: fully transparent at the top,
   reaching the page ground by the time it is behind the buttons, so the
   gallery dissolves under it rather than stopping at a line.

   The gradient is the page ground at varying alpha, not white, so there is no
   colour shift where it becomes solid. Extra top padding (pt-10) gives the fade
   somewhere to happen without the first button sitting in the see-through part.

   Mobile only: from md the bar is an ordinary inline block in the info panel. */
@media (max-width: 767px) {
  .buy-now-bar {
    background: linear-gradient(
      to bottom,
      rgba(250, 246, 241, 0) 0%,
      rgba(250, 246, 241, 0.72) 28%,
      rgba(250, 246, 241, 0.96) 52%,
      rgb(250, 246, 241) 68%
    );
    /* the fade replaces the shadow; both together reads as two edges */
    box-shadow: none;
    /* the transparent strip must not swallow taps meant for the page */
    pointer-events: none;
  }
  .buy-now-bar > * { pointer-events: auto; }
}

/* Clear the mobile sticky Buy-now bar (product page) so it never overlaps the
   WhatsApp float - scoped to pages that have that bar, mobile only.

   104px used to be hard-coded, but that bar holds two stacked 48px buttons plus
   its own padding and is taller than that, so the icon sat on the buttons.
   js/main.js now measures the bar and publishes its real height as
   --buy-bar-h, and the float clears it by 20px whatever it grows to. The
   fallback keeps the old behaviour if the script has not run yet.

   The float is also 12px smaller here (52px rather than 64px): at the bottom of
   a phone screen, stacked above a full-width bar, the desktop size crowds it. */
@media (max-width: 767px) {
  body:has(.buy-now-bar) .wa-float {
    /* 16px of clear air above the first button */
    bottom: calc(var(--buy-bar-h, 104px) + 16px);
  }
  .wa-float {
    height: 52px;
    width: 52px;
  }
  .wa-float svg {
    width: 28px;
    height: 28px;
  }
}
@keyframes wa-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-8px); }
}
.wa-pulse::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 9999px;
  background: #25d366;
  opacity: .55;
  z-index: -1;
  animation: wa-ping 2.2s cubic-bezier(0,0,.2,1) infinite;
}
@keyframes wa-ping {
  0%   { transform: scale(1);   opacity: .55; }
  80%, 100% { transform: scale(1.9); opacity: 0; }
}

/* ---- Scroll reveal animation ------------------------------------------- */
.reveal {
  opacity: 0;
  transform: translateY(38px);
  transition: opacity .8s cubic-bezier(.2,.7,.2,1),
              transform .8s cubic-bezier(.2,.7,.2,1);
  will-change: opacity, transform;
}
.reveal.is-visible {
  opacity: 1;
  transform: none;
}
.reveal-delay-1 { transition-delay: .1s; }
.reveal-delay-2 { transition-delay: .2s; }
.reveal-delay-3 { transition-delay: .3s; }
.reveal-delay-4 { transition-delay: .4s; }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1 !important; transform: none !important; transition: none; }
  .wa-float, .wa-pulse::before { animation: none; }
  html { scroll-behavior: auto; }
}

/* ---- Hero slider dots --------------------------------------------------- */
/* Figma Slider 10454:23947 "Add button" pills. Every pill carries the same
   10% white fill - the active slide is distinguished by width alone (58px
   vs 21px), exactly as the design specifies. */
.hero-dot {
  height: 20px;
  width: 21px;
  border-radius: 14.5px;
  background: rgba(255, 255, 255, 0.1);
  transition: width .4s ease, background .4s ease;
  cursor: pointer;
}
.hero-dot.active {
  width: 58px;
}
/* Pointer affordance only - the design has no hover state for these. */
@media (hover: hover) {
  .hero-dot:hover { background: rgba(255, 255, 255, 0.22); }
}

/* ---- Hero cross-fade slides + Ken Burns pan ---------------------------- */
.hero-slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 1.4s cubic-bezier(0.4, 0, 0.2, 1); /* smooth cross-fade */
}
.hero-slide.active { opacity: 1; }

/* The photo slowly drifts (zoom + pan) while its slide is active. Constant
   linear motion reads smoothest; overscan (min scale 1.06) keeps the edges
   covered during the translate so no gap ever shows. */
.hero-slide img {
  transform: scale(1.06);
  transition: transform 1.4s cubic-bezier(0.4, 0, 0.2, 1); /* eases the reset as a slide fades out */
  will-change: transform;
  backface-visibility: hidden;
}
.hero-slide.active img { animation: kenburns-a 8s linear forwards; }
.hero-slide:nth-child(even).active img { animation-name: kenburns-b; }

@keyframes kenburns-a {
  from { transform: scale(1.06) translate(0, 0); }
  to   { transform: scale(1.16) translate(-1.8%, -1.2%); }
}
@keyframes kenburns-b {
  from { transform: scale(1.16) translate(1.8%, 1.2%); }
  to   { transform: scale(1.06) translate(0, 0); }
}

@media (prefers-reduced-motion: reduce) {
  .hero-slide img,
  .hero-slide.active img { animation: none; transform: scale(1.06); }
}

/* ---- Section title underline accent ------------------------------------ */
.section-title { position: relative; }

/* ---- Mobile menu -------------------------------------------------------- */
.mobile-menu {
  transition: transform .4s cubic-bezier(.2,.7,.2,1), opacity .3s ease;
  transform: translateX(100%);
  opacity: 0;
  pointer-events: none;
}
.mobile-menu.open {
  transform: translateX(0);
  opacity: 1;
  pointer-events: auto;
}
.menu-backdrop {
  transition: opacity .35s ease;
  opacity: 0;
  pointer-events: none;
}
.menu-backdrop.open {
  opacity: 1;
  pointer-events: auto;
}

/* Fade-in for the whole page on load */
.page-fade { animation: pageFade .8s ease both; }
@keyframes pageFade {
  from { opacity: 0; }
  to   { opacity: 1; }
}


/* ---- Hero CTA (Figma Button 10454:23952) ------------------------------
   Exactly what the frame specifies: a 10% black fill at radius 32, no
   border and no blur. Sizing/padding live on the element. Kept separate
   from .btn-glass (bordered, blurred) so the offer cards are untouched.
   The hover lift is ours - Figma defines the Default state only. */
.btn-hero {
  background: rgba(0, 0, 0, 0.1);
  transition: transform .3s ease, background .3s ease;
}
.btn-hero:hover {
  background: rgba(0, 0, 0, 0.28);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.34);
  transform: translateY(-2px);
}
.btn-hero svg { transition: transform .3s ease; }
.btn-hero:hover svg { transform: translateX(4px); }

/* ---- Arrow buttons (Figma "Right arrow" 10459:18968) -------------------
   ONE treatment for every arrow button on the site - Shop the Look, the
   promo banner and the product-card gallery. Size stays on the element, so
   each keeps the dimensions its own frame specifies (58px for the section
   and banner arrows, 48px on the cards); only the fill, blur and hover are
   shared. The blur is ours: a 10% white fill alone disappears over pale
   photos. */
.arrow-btn {
  background: rgba(255, 255, 255, 0.1);
  transition: background .3s ease, transform .3s ease;
}
@media (hover: hover) {
  .arrow-btn:hover {
    background: rgba(255, 255, 255, 0.22);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.34);
  }
}

/* Reveal motion for the action column and its glow. Figma's transition is a
   spring, duration 1.022s, bounce 0.25 - approximated by a cubic-bezier that
   overshoots slightly. The column also carries the +26px x offset from the
   design's motion spec, so it settles inward as it fades in. */
.prod-glow,
.prod-actions {
  opacity: 0;
  transition: opacity 1.022s cubic-bezier(.22, 1.28, .36, 1),
              transform 1.022s cubic-bezier(.22, 1.28, .36, 1);
}
.prod-actions { transform: translateX(26px); }

@media (hover: hover) {
  .group\/card:hover .prod-glow,
  .group\/card:hover .prod-actions { opacity: 1; }
  .group\/card:hover .prod-actions { transform: translateX(0); }
}

/* Touch devices have no hover, so the buttons stay visible and tappable. */
@media (hover: none) {
  .prod-glow, .prod-actions { opacity: 1; }
  .prod-actions { transform: translateX(0); }
}

/* ---- Arrow buttons on a LIGHT ground (Figma 10479:21400) ---------------
   Same 58px pill as .arrow-btn, but a 10% BLACK fill: these sit on the cream
   In Their Words section rather than over a photo. The glyph stays white, as
   the design specifies. */
.arrow-btn-ink {
  background: rgba(0, 0, 0, 0.1);
  transition: background .3s ease, transform .3s ease;
}
@media (hover: hover) {
  .arrow-btn-ink:hover { background: rgba(0, 0, 0, 0.2); }
}

/* ---- In Their Words cross-fade ----------------------------------------
   The quote and its attribution fade out, swap while invisible, and fade
   back in with a small upward drift, so reviews change smoothly instead of
   snapping. Timings are mirrored in js/main.js (FADE = 320ms). */
.words-swap {
  transition: opacity .32s ease, transform .32s ease;
}
.words-swap.is-swapping {
  opacity: 0;
  transform: translateY(-8px);
}
@media (prefers-reduced-motion: reduce) {
  .words-swap { transition: none; }
  .words-swap.is-swapping { opacity: 1; transform: none; }
}

/* ---- Mobile: side drawers become bottom sheets -------------------------
   All three panels (the nav menu, Buy now and the Size chart) slide in from
   the right on a desktop. On a phone that is the wrong gesture and the wrong
   shape: a sheet rising from the bottom sits under the thumb and gets the
   full width for its content.

   This is CSS only - the JS still just toggles `.open`, so the close
   button, the backdrop tap and Esc all keep working untouched. The rules
   override the Tailwind utilities on the elements (top-0 / right-0 / h-full
   / max-w-*) by setting the same properties from a later stylesheet.

   Breakpoint matches the nav menu's own `md:hidden`, so a panel is a sheet
   exactly when the mobile menu exists. */
@media (max-width: 767.98px) {
  .mobile-menu,
  .buy-drawer {
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    max-width: none;
    height: auto;
    max-height: 88vh;                /* always leaves the backdrop tappable */
    border-radius: 20px 20px 0 0;
    transform: translateY(100%);     /* rises instead of sliding sideways */
  }
  .mobile-menu.open,
  .buy-drawer.open { transform: translateY(0); }

  /* The nav menu has no scroll container of its own, so cap it here. */
  .mobile-menu { overflow-y: auto; }

  /* Grab handle - the affordance that says "this pulls down". Drawn on the
     panel itself so no markup changes are needed. */
  .mobile-menu::before,
  .buy-drawer::before {
    content: "";
    position: absolute;
    top: 8px;
    left: 50%;
    width: 40px;
    height: 4px;
    margin-left: -20px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.16);
    z-index: 20;
    pointer-events: none;
  }
  /* Push each panel's own header clear of the handle. */
  .mobile-menu { padding-top: 26px; }
  /* Size chart: its header is the first thing in the sheet. */
  .buy-drawer > div:first-child > div:first-child { padding-top: 26px; }
  /* Buy now: the product preview above is hidden on mobile (the sheet is just
     the buying choices), so the actions block is what sits under the handle. */
  #buy-sheet-actions { padding-top: 26px; }
}

/* ---- Buy sheet order cards (Figma "Content" 10276:16981) ---------------
   Shadow/sm from the file: a 1px-offset, 2px-blur black at 6%. Kept in CSS
   rather than a utility so the hover lift has something to transition. */
.order-card {
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
  transition: box-shadow .25s ease, border-color .25s ease;
}
@media (hover: hover) {
  a.order-card:hover {
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.10);
    border-color: #E6D3C0;
  }
}

/* ---- Buy sheet primary CTA (Figma "Content" 10296:16672) ---------------
   The inset highlight along the top edge is what stops a flat black pill
   looking like a dead rectangle - it is in the frame, not decoration. */
.buy-cta-primary {
  box-shadow: inset 0 5.8px 8.7px 0 rgba(255, 243, 222, 0.25);
  transition: opacity .25s ease;
}
.buy-cta-primary:hover { opacity: .9; }

/* ---- Fullscreen media viewer (product page) -----------------------------
   Photos and clips as full-screen slides. Swipe on a phone, the arrows or
   keyboard anywhere; paging is CSS scroll-snap, so the browser supplies the
   momentum and the snap. Opens from the 2x2 gallery on any width - see
   js/main.js, the click handler under buildGallery(). */
.pd-viewer-track {
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;   /* no back-navigation swipe on the edges */
}
.pd-viewer-track::-webkit-scrollbar { display: none; }

/* The viewer reserves a band at the top and the bottom, and the media is sized
   to what is left. That is what keeps the close button and the counter OFF the
   photograph: with a full-bleed image there is otherwise no outside for them to
   sit in, and any inset at all puts them on top of the picture.

   Sizing the slide rather than nudging the chrome means it holds for every
   aspect ratio, portrait or landscape, photo or video. */
.pd-viewer-track > div {
  box-sizing: border-box;
  /* Top: 10px inset + 40px close button + 30px gap = 80px.
     Bottom: 16px inset + 26px counter + 23px gap = 65px. */
  padding: 80px 0 65px;
}

/* Top band. */
.pd-viewer-close {
  top: 10px;
  right: 12px;
  z-index: 20;
}

/* Bottom band, centred under the picture. */
.pd-viewer-count {
  bottom: 16px;
  z-index: 20;
}

/* The arrows stay against the screen edges on a phone or tablet, vertically
   centred: that is where a thumb reaches, and it does not move between
   slides. Dark enough to read over a bright image. */
.pd-viewer-prev,
.pd-viewer-next {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 44px;
  width: 44px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.42);
  border: 1px solid rgba(255, 255, 255, 0.22);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: #fff;
  transition: background .2s ease;
}
.pd-viewer-prev { left: 12px; }
.pd-viewer-next { right: 12px; }
.pd-viewer-prev:active,
.pd-viewer-next:active { background: rgba(0, 0, 0, 0.6); }

/* From desktop up, a portrait product photo sits in a wide viewport with a
   deep letterbox band either side - screen-edge positioning (fine where the
   photo fills the width, on a phone) would strand the close button and
   arrows far from the picture. js/main.js measures the CURRENT slide's own
   media and publishes its edges as --pv-l/--pv-r; these read that instead,
   landing 50px out from the image on both sides. The fallback (12px) covers
   the one frame before the first measurement. */
@media (min-width: 1024px) {
  /* Arrows only: 30px outside the image's own left/right edge. --pv-l/--pv-r
     is the gap from the SCREEN edge to the image edge, so getting OUTSIDE the
     image means backing off further still - by the arrow's own 44px plus the
     30px gap. max() floors it for a photo with almost no letterbox, where the
     arithmetic would otherwise go negative.

     The close button stays a plain corner control - top-right of the SCREEN,
     same as it already was on a phone - rather than tracking the image, so it
     does not go wandering out toward a wide letterbox on a narrow photo. Its
     20px gap from the image is vertical, handled by the slide's own padding
     above, which holds on every width. */
  .pd-viewer-prev { left:  max(12px, calc(var(--pv-l, 12px) - 44px - 30px)); }
  .pd-viewer-next { right: max(12px, calc(var(--pv-r, 12px) - 44px - 30px)); }
}

/* Lock the page behind the viewer, or a swipe drifts the page underneath. */
body.pd-viewer-open { overflow: hidden; }

/* Tiles say they open something, on a pointer device that can actually
   hover - a phone has no cursor to rest before it taps. */
@media (hover: hover) {
  #pd-gallery [data-media-index] { transition: opacity .25s ease; }
}

/* ---- Mobile: section titles centre ------------------------------------
   On a phone the shelves read as a single stacked column, so a left-aligned
   heading sits off-axis from the centred cards beneath it. "Shop the look."
   and "In Their Words" are already centred by their own design, so this only
   moves the two product-shelf titles and keeps all four consistent.
   The h2 carries flex-1, so it spans the row and centres within it. */
@media (max-width: 767.98px) {
  .section-title { text-align: center; }
}

/* ---- Viewer photo zoom -------------------------------------------------
   The transform is driven from JS; these are the bits that must be right
   before the first gesture lands. `touch-action: pinch-zoom` tells the
   browser we intend to handle pinches here, while still letting a plain
   horizontal drag reach the snap-scroller underneath so paging stays native. */
.pv-zoom {
  touch-action: pinch-zoom;
  transform-origin: center center;
  will-change: transform;
  cursor: zoom-in;
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* ---- Touch: finger-sized targets ---------------------------------------
   Keyed on `pointer: coarse` rather than a width, because it is the input
   device that decides how big a target must be. 44px is the WCAG 2.5.5 /
   Apple HIG minimum.

   The small controls keep their painted size and gain an invisible hit area
   through ::after - the pixel you see stays put, the pixel you can hit grows.
   That matters here because several of these sizes come straight from Figma
   and changing them would break the design. */
@media (pointer: coarse) {
  /* Hero slider pills: 20px tall by design, unhittable in practice. */
  .hero-dot, .words-dot { position: relative; }
  .hero-dot::after, .words-dot::after {
    content: "";
    position: absolute;
    inset: -14px -6px;          /* 48px tall, without moving the pill */
  }

  /* Product-card gallery arrows and the card action buttons. */
  .prod-nav, .arrow-btn, .arrow-btn-ink, .round-btn { position: relative; }
  .arrow-btn::after, .arrow-btn-ink::after {
    content: "";
    position: absolute;
    inset: -6px;                /* 44px+ around the 44/58px pills */
  }

  /* Form controls: 16px stops iOS zooming the page on focus, and 44px is a
     comfortable strike area. */
  input:not([type="checkbox"]):not([type="radio"]), select, textarea {
    min-height: 44px;
    font-size: 16px;
  }

  /* Anything tappable in the buy sheet gets room around it. */
  #buy-sheet-actions .order-card { min-height: 84px; }
  #buy-copy { min-height: 44px; }

  /* Two targets side by side need a gap a thumb cannot fall between. */
  #hero-dots { gap: 10px; }
}

/* The info panel used to be a sticky column with its own scrollbar, which meant
   the page had two scrolling regions side by side and the wheel did different
   things depending on which one the pointer was over. It is now an ordinary
   block: one page, one scroll. Access to the buy actions after they scroll away
   is handled by #buy-float below instead. */

/* ---------- Floating buy button (#buy-float) ----------------------------
   Appears only once the real buy bar has scrolled off the top, so it never
   competes with the buttons themselves. Desktop and tablet only: on a phone the
   .buy-now-bar is already pinned to the bottom of the screen.

   It sits beside the WhatsApp float, on its left. That button is 64px square on
   a 24px inset, so 130px of right inset leaves a 42px gap between the two,
   and a 32px bottom inset puts this 48px pill on the same centre line as the
   64px circle rather than on the same baseline.

   It travels down from well above its resting place, so the arrival reads as
   the buy bar following you down the page. The distance is deliberately long
   enough to be seen; the ease-out lands it softly instead of stopping dead. */
#buy-float {
  position: fixed;
  right: 130px;
  bottom: 32px;
  z-index: 45;
  /* A fixed element is shrink-to-fit, so the label was free to wrap and
     square the pill off. Size it to the full width of its own text and
     keep that text on one line. */
  width: max-content;
  max-width: calc(100vw - 160px);
  white-space: nowrap;
  opacity: 0;
  visibility: hidden;
  /* Scaled down a touch as well as lifted, so it settles rather than just
     stops - a pure translate reads mechanical at this distance. */
  transform: translateY(-140px) scale(.96);
  /* Promote once, up front, so the first frame of the drop is not the one the
     browser spends creating a layer. */
  will-change: transform, opacity;
  /* Leaving: shorter and plainer. Only the arrival is worth watching. */
  transition: opacity .2s ease-in,
              transform .32s cubic-bezier(.4, 0, .7, .3),
              visibility .32s;
}
#buy-float.is-on {
  opacity: 1;
  visibility: visible;
  transform: none;
  /* Arriving: a long ease-out that covers most of the distance early and
     eases off at the end, with the fade finishing well before the movement so
     the button is solid on the way down instead of resolving on impact. */
  transition: opacity .28s ease-out,
              transform .78s cubic-bezier(.22, 1, .36, 1),
              visibility 0s;
}
@media (max-width: 767px) {
  #buy-float { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  #buy-float { transition: none; transform: none; }
}

/* Lock the page behind the modal, same pattern as the media viewer. */
body.fg-open { overflow: hidden; }

/* ---------- Fit Guide modal ----------------------------------------------
   Lead-capture: a phone number in exchange for a sizing PDF. Kept out of the
   Tailwind template strings for two things arbitrary classes do awkwardly -
   a spinner keyframe, and a reduced-motion override that has to beat an
   inline transform set by JS.

   Markup and behaviour: js/main.js, fitGuideModal(). Trigger: any element
   carrying [data-fit-guide-trigger] - product.html has one beside the Size
   Chart button, and the same attribute works from any other page. */
.fg-overlay {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(33, 28, 23, 0.44);
  opacity: 0;
  transition: opacity .22s ease;
}
.fg-overlay.is-open { opacity: 1; }

.fg-panel {
  opacity: 0;
  transform: translateY(10px) scale(.98);
  transition: opacity .22s ease, transform .22s ease;
}
.fg-overlay.is-open .fg-panel {
  opacity: 1;
  transform: none;
}

.fg-spinner {
  display: inline-block;
  width: 16px;
  height: 16px;
  border-radius: 999px;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-top-color: #FBFAF6;
  animation: fg-spin .7s linear infinite;
}
@keyframes fg-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .fg-overlay, .fg-panel { transition: none !important; transform: none !important; }
  .fg-spinner { animation-duration: 1.4s; }
}
