/* ============================================================================
   HUSTLERS SCENT
   ----------------------------------------------------------------------------
   Design notes, so later edits do not undo the intent:

   - The page is off-white paper. Depth comes from shadow and scale, never from
     gradients or glass. There is exactly one dark band on the site (the
     discovery sets) and it is the only tonal contrast, which is what makes it
     land.
   - Nothing is evenly spaced by default. The fragrance index is deliberately
     offset, headings are oversized against small labels, and the hero bleeds
     past its column. Symmetry everywhere is the fastest way to look generic.
   - Motion only ever animates transform and opacity. Anything that moves does
     so once, on entry or on touch, and never loops.
   ========================================================================= */

:root {
  --paper:      #f6f3ee;
  --paper-warm: #efeae2;
  --card:       #ffffff;
  --ink:        #111110;
  --ink-soft:   #4a4741;
  --ink-faint:  #8a8478;
  --rule:       rgba(17, 17, 16, .10);
  --rule-soft:  rgba(17, 17, 16, .06);

  /* One accent, used sparingly. Warm brass rather than gold. */
  --accent:     #9a7b3f;

  --shell: min(1560px, 92vw);
  --gutter: clamp(20px, 4.5vw, 72px);

  /* A single easing curve across the whole site. Consistency is what makes
     motion feel designed rather than assembled. */
  --ease: cubic-bezier(.22, 1, .36, 1);
  --fast: 220ms;
  --slow: 420ms;

  /* Layered depth: a tight contact shadow plus a wide ambient one. A single
     blurry shadow is the giveaway of a template. */
  --lift-1: 0 1px 2px rgba(17,17,16,.05), 0 8px 24px -12px rgba(17,17,16,.18);
  --lift-2: 0 2px 4px rgba(17,17,16,.06), 0 24px 60px -24px rgba(17,17,16,.28);
  --lift-3: 0 3px 6px rgba(17,17,16,.08), 0 48px 96px -40px rgba(17,17,16,.38);
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

img { max-width: 100%; display: block; }
a { color: inherit; }

::selection { background: var(--ink); color: var(--paper); }

/* Focus that is actually visible, but only for keyboard users. */
:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 3px;
  border-radius: 2px;
}

.container { width: 100%; max-width: var(--shell); margin: 0 auto; padding: 0 var(--gutter); }

/* ---------------------------------------------------------------- type ---- */

h1, h2, h3, .display {
  font-family: 'Manrope', 'Inter', system-ui, sans-serif;
  font-weight: 800;
  /* Tight tracking is what separates display type from body type set large. */
  letter-spacing: -.035em;
  line-height: .98;
  margin: 0;
  text-wrap: balance;
}

.display-xl { font-size: clamp(44px, 9vw, 150px); }
.display-l  { font-size: clamp(34px, 5.6vw, 92px); }
.display-m  { font-size: clamp(26px, 3.4vw, 54px); letter-spacing: -.03em; }

.lead {
  font-size: clamp(16px, 1.3vw, 22px);
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 46ch;
  margin: 0;
  text-wrap: pretty;
}

/* Small uppercase label. Wide tracking, never larger than 12px. */
.label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0;
}

.rule { height: 1px; background: var(--rule); border: 0; margin: 0; }

/* --------------------------------------------------------------- buttons -- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 48px;
  padding: 0 26px;
  border-radius: 999px;
  border: 1px solid var(--ink);
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: transform var(--fast) var(--ease), background var(--fast) var(--ease),
              color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.btn:hover { transform: translateY(-2px); box-shadow: var(--lift-1); }
.btn:active { transform: translateY(0); }

.btn-solid { background: var(--ink); color: var(--paper); }
.btn-solid:hover { background: #26251f; }

.btn-quiet { border-color: var(--rule); color: var(--ink-soft); }
.btn-quiet:hover { border-color: var(--ink); color: var(--ink); }

/* Underline that draws in from the left rather than just appearing. */
.link {
  text-decoration: none;
  font-weight: 600;
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0% 1px;
  background-position: 0 100%;
  background-repeat: no-repeat;
  transition: background-size var(--fast) var(--ease);
  padding-bottom: 2px;
}
.link:hover { background-size: 100% 1px; }

/* ------------------------------------------------------------------ nav --- */

/* The bar rides the wash. Two stacked fills cross-fade rather than one
   background switching, so the chrome changes colour on the same continuous
   move as the page behind it instead of stranding a light bar over black. */
.topbar {
  position: sticky;
  top: 0;
  z-index: 50;
  background: transparent;
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--fast) var(--ease);
  color: color-mix(in srgb, #f2efe8 calc(var(--wash, 0) * 100%), var(--ink));
}
.topbar::before,
.topbar::after { content: ''; position: absolute; inset: 0; pointer-events: none; }
.topbar::before { background: rgba(246, 243, 238, .82); opacity: calc(1 - var(--wash, 0)); }
.topbar::after  { background: rgba(20, 20, 15, .82); opacity: var(--wash, 0); }
.topbar .nav { position: relative; z-index: 1; }
.topbar .brand { color: inherit; }
.topbar .nav-links a { color: color-mix(in srgb, currentColor 72%, transparent); }
.topbar .nav-links a:hover { color: inherit; }

/* The cart pill inverts with it. */
.cart-link {
  background: color-mix(in srgb, #f2efe8 calc(var(--wash, 0) * 100%), var(--ink));
  border-color: color-mix(in srgb, #f2efe8 calc(var(--wash, 0) * 100%), var(--ink));
  color: color-mix(in srgb, #14140f calc(var(--wash, 0) * 100%), var(--paper));
}
.badge {
  background: color-mix(in srgb, #14140f calc(var(--wash, 0) * 100%), var(--paper));
  color: color-mix(in srgb, #f2efe8 calc(var(--wash, 0) * 100%), var(--ink));
}
.topbar[data-stuck='true'] { border-bottom-color: var(--rule); }

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  height: 68px;
}

.brand {
  font-family: 'Manrope', sans-serif;
  font-weight: 800;
  font-size: 15px;
  letter-spacing: .14em;
  text-transform: uppercase;
  text-decoration: none;
}

.nav-links { display: flex; gap: 28px; }
.nav-links a {
  font-size: 13px;
  font-weight: 500;
  letter-spacing: .02em;
  text-decoration: none;
  color: var(--ink-soft);
  transition: color var(--fast) var(--ease);
}
.nav-links a:hover { color: var(--ink); }

.nav-right { display: flex; align-items: center; gap: 10px; }

.cart-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 40px;
  padding: 0 18px;
  border-radius: 999px;
  border: 1px solid var(--ink);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
  transition: transform var(--fast) var(--ease);
}
.cart-link:hover { transform: translateY(-2px); }

.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 19px;
  height: 19px;
  padding: 0 5px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
}

/* ----------------------------------------------------------------- hero --- */

.hero {
  position: relative;
  padding: clamp(48px, 9vw, 104px) 0 clamp(56px, 8vw, 96px);
  overflow: hidden;
}

/* Oversized wordmark set into the paper. Very low contrast on purpose: it
   should register as texture, not as text you are meant to read. */
.hero-ghost {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  padding-top: 2vw;
  font-family: 'Manrope', sans-serif;
  font-weight: 800;
  font-size: clamp(96px, 21vw, 300px);
  letter-spacing: -.05em;
  line-height: .78;
  pointer-events: none;
  user-select: none;
  white-space: nowrap;
  overflow: hidden;
}

/* Two words on separate depth planes. The far one moves less than the near
   one, which is what sells the parallax as depth rather than as sliding. */
.hero-ghost span {
  display: block;
  color: rgba(17, 17, 16, .085);
  transform: translate3d(var(--gx, 0px), var(--gy, 0px), 0);
  transition: transform 700ms var(--ease);
  will-change: transform;
}
.hero-ghost span:first-child { margin-left: calc(var(--gutter) * -.5); }
.hero-ghost span:last-child {
  margin-left: 14%;
  color: rgba(17, 17, 16, .13);
  --gx: var(--gx2, 0px);
  --gy: var(--gy2, 0px);
}

.hero-grid {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, .98fr);
  gap: clamp(24px, 4vw, 64px);
  align-items: center;
}

.hero-copy { position: relative; z-index: 2; }
.hero-copy .display-xl { margin: 18px 0 0; max-width: 12ch; }
.hero-sub { margin-top: 22px; max-width: 34ch; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 32px; }

.hero-meta {
  display: flex;
  gap: clamp(20px, 3vw, 40px);
  margin: 40px 0 0;
  padding-top: 22px;
  border-top: 1px solid var(--rule);
  max-width: 34ch;
}
.hero-meta > div { min-width: 0; }
.hero-meta dt { font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint); font-weight: 600; }
.hero-meta dd { margin: 5px 0 0; font-size: 14px; font-weight: 600; }

/* The bottle is allowed to break out of the grid column. Containing it would
   make the hero read as two tidy boxes. */
.hero-stage {
  position: relative;
  perspective: 900px;
  perspective-origin: 50% 45%;
  justify-self: end;
  width: min(92%, 470px);
  margin-right: calc(var(--gutter) * -.2);
  aspect-ratio: 1 / 1;
  display: grid;
  place-items: center;
}

/* Light pooling behind the product, not a gradient panel. */
.hero-stage::before {
  content: '';
  position: absolute;
  inset: 6% 4% 10%;
  border-radius: 50%;
  background: radial-gradient(60% 55% at 50% 42%, #fff 0%, rgba(255,255,255,.7) 42%, rgba(255,255,255,0) 72%);
}

/* Two independent effects compose into one transform: --drift comes from
   scroll, --tilt from the pointer. Composing them in CSS rather than string
   concatenating in JS means neither can clobber the other. Both default to
   identity so the element is correct before any script runs. */
/* ---- the 3D stage ----------------------------------------------------
   The bottle photo is a flat image, so it cannot turn to show another side.
   What it can do is behave like a rigid card under a moving viewpoint: a real
   perspective projection, rotating about Y, catching a moving specular sheen
   across its face. That is the part the eye actually reads as three
   dimensional, and it holds up because the geometry is genuine even though
   the subject is a photograph.  */
.hero-card {
  position: relative;
  width: 76%;
  border-radius: 20px;
  overflow: hidden;
  background: #fff;
  transform-style: preserve-3d;
  box-shadow: var(--lift-3);
  --spin: rotateY(0deg);
  --tilt: rotateX(0deg) rotateY(0deg) translateZ(0px);
  --drift: translate3d(0, 0, 0);
  /* Resting angle. Without it the card settles square-on and the whole
     effect disappears the moment you stop moving. */
  transform: var(--drift) rotateY(-11deg) rotateX(3deg) var(--spin) var(--tilt);
  transition: transform 260ms var(--ease);
  will-change: transform;
  animation: intro-spin 1400ms var(--ease) both;
}

/* The intro. Swings in from a steep angle and settles, once, on load. */
@keyframes intro-spin {
  from { opacity: 0; transform: rotateY(-60deg) rotateX(8deg) translateX(10%) scale(.92); }
  60%  { opacity: 1; }
  to   { opacity: 1; transform: var(--drift) rotateY(-11deg) rotateX(3deg) var(--spin) var(--tilt); }
}

/* Light travelling across the face as it turns. Sits above the photo, below
   nothing else, and is purely decorative. */
.hero-card::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, rgba(255,255,255,0) 38%, rgba(255,255,255,.55) 50%, rgba(255,255,255,0) 62%);
  transform: translateX(var(--sheen, -60%));
  transition: transform 260ms var(--ease);
  pointer-events: none;
  mix-blend-mode: overlay;
}

.hero-bottle {
  position: relative;
  width: 100%;
  height: auto;
  object-fit: contain;
  display: block;
}

/* A pressed-in contact shadow so the bottle sits on a surface rather than
   floating in a void. Elliptical and tight, the way a real one falls. */
.hero-shadow {
  position: absolute;
  left: 50%;
  bottom: 9%;
  width: 46%;
  height: 26px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(17,17,16,.30) 0%, rgba(17,17,16,.10) 55%, rgba(17,17,16,0) 78%);
  filter: blur(5px);
}

/* -------------------------------------------------------------- sections -- */

.section { padding: clamp(56px, 8vw, 108px) 0; }

.section-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 20px 40px;
  /* Aligned to the top of the heading block, not its bottom: with a rating
     line underneath, "end" left the button floating mid-air. */
  align-items: start;
  margin-bottom: clamp(28px, 4vw, 52px);
}
.section-head .label { margin-bottom: 14px; }
.section-head .lead { margin-top: 16px; }
.section-head > .btn { margin-top: 6px; }

/* ------------------------------------------------------- discovery band --- */

/* The one dark moment on the site. */
.band {
  background: #14140f;
  color: #f2efe8;
  padding: clamp(56px, 8vw, 104px) 0;
}
.band .label { color: rgba(242,239,232,.5); }
.band .lead { color: rgba(242,239,232,.72); }
.band .btn { border-color: rgba(242,239,232,.34); color: #f2efe8; }
.band .btn:hover { border-color: #f2efe8; }
.band .btn-solid { background: #f2efe8; color: #14140f; border-color: #f2efe8; }
.band .btn-solid:hover { background: #fff; }
.band :focus-visible { outline-color: #f2efe8; }

.sets {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(16px, 2.4vw, 32px);
}

.set {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: clamp(22px, 2.6vw, 34px);
  border: 1px solid rgba(242,239,232,.14);
  border-radius: 18px;
  background: rgba(255,255,255,.025);
  text-decoration: none;
  color: inherit;
  overflow: hidden;
  transition: transform var(--slow) var(--ease), border-color var(--slow) var(--ease),
              background var(--slow) var(--ease);
}
.set:hover { transform: translateY(-5px); border-color: rgba(242,239,232,.3); background: rgba(255,255,255,.05); }

/* The box photos are shot on white with no alpha, so against the dark band
   they read as glaring rectangles. Framing them in their own white card makes
   the backdrop part of the design instead of a mistake. */
.set-media {
  position: relative;
  perspective: 1200px;
  aspect-ratio: 4 / 3;
  border-radius: 14px;
  overflow: hidden;
  background: #fff;
  margin-bottom: 24px;
}
.set-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--slow) var(--ease);
}
.set:hover .set-media img { transform: translateY(-6px) scale(1.03); }

.set-top { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; }
.set-name { font-family: 'Manrope', sans-serif; font-weight: 800; font-size: clamp(19px, 2vw, 24px); letter-spacing: -.025em; }
.set-price { font-family: 'Manrope', sans-serif; font-weight: 800; font-size: 17px; white-space: nowrap; }

/* Contents as a real list, because what is in the box is the selling point. */
.set-contents { list-style: none; margin: 20px 0 0; padding: 18px 0 0; border-top: 1px solid rgba(242,239,232,.14); }
.set-contents li {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 5px 0;
  font-size: 14px;
  color: rgba(242,239,232,.8);
}
.set-contents b { font-family: 'Manrope', sans-serif; font-weight: 800; color: #f2efe8; font-size: 13px; letter-spacing: .02em; }

.set-foot { margin-top: 22px; display: flex; align-items: center; gap: 12px; }

/* ------------------------------------------------------ fragrance index --- */

/* Deliberately not a uniform grid: rows alternate which side the image sits
   on, and the two row widths differ. */
.fragrances { display: flex; flex-direction: column; gap: clamp(28px, 5vw, 72px); }

.frag {
  display: grid;
  grid-template-columns: minmax(0, .68fr) minmax(0, 1.32fr);
  gap: clamp(20px, 4vw, 60px);
  align-items: center;
  text-decoration: none;
  color: inherit;
}
.frag:nth-child(odd)  { width: min(100%, 1080px); }
.frag:nth-child(even) { width: min(100%, 1010px); margin-left: auto; grid-template-columns: minmax(0, 1.32fr) minmax(0, .68fr); }
.frag:nth-child(even) .frag-media { order: 2; }
.frag:nth-child(even) .frag-body { order: 1; justify-self: end; }

.frag-media {
  position: relative;
  perspective: 1200px;
  aspect-ratio: 4 / 5;
  border-radius: 20px;
  background: #fff;
  display: grid;
  place-items: center;
  overflow: hidden;
  box-shadow: var(--lift-1);
  transition: box-shadow var(--slow) var(--ease), transform var(--slow) var(--ease);
}
.frag:hover .frag-media { box-shadow: var(--lift-3); transform: translateY(-4px); }

.frag-media img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: 7%;
  transition: transform var(--slow) var(--ease);
}
.frag:hover .frag-media img { transform: scale(1.055) translateY(-6px); }

/* Numeral sunk into the card, behind the product. */
.frag {
  position: relative;
  /* Room for the ghost accord, so it can never sit under the price line. */
  padding-bottom: clamp(26px, 4.2vw, 58px);
}
.frag-media, .frag-body { position: relative; z-index: 1; }

/* The row's empty quadrant carries the scent's key accord as a ghost word,
   echoing the hero wordmark. It is aligned to the text column and clipped to
   the row, so it reads as set type rather than as something that slid off. */
.frag-ghost {
  position: absolute;
  z-index: 0;
  bottom: 0;
  width: 66%;
  font-family: Georgia, 'Times New Roman', serif;
  font-style: italic;
  font-size: clamp(30px, 4.3vw, 66px);
  letter-spacing: -.02em;
  line-height: 1;
  color: rgba(17, 17, 16, .08);
  white-space: nowrap;
  overflow: hidden;
  pointer-events: none;
  user-select: none;
}
/* Odd rows put the copy in the right-hand column, even rows in the left, so
   the word starts exactly where its paragraph does. */
.frag:nth-child(odd) .frag-ghost  { left: 34%; padding-left: clamp(20px, 4vw, 60px); }
.frag:nth-child(even) .frag-ghost { left: 0; text-align: right; padding-right: clamp(20px, 4vw, 60px); }

@media (max-width: 1000px) {
  .frag { padding-bottom: clamp(22px, 6vw, 40px); }
  .frag-ghost, .frag:nth-child(odd) .frag-ghost, .frag:nth-child(even) .frag-ghost {
    left: 0; width: 100%; padding: 0; text-align: left; font-size: clamp(28px, 8vw, 44px);
  }
}
.frag-body { min-width: 0; }
.frag-name { font-family: 'Manrope', sans-serif; font-weight: 800; font-size: clamp(28px, 4.2vw, 46px); letter-spacing: -.035em; line-height: 1; margin: 14px 0 0; }
.frag-note { margin: 14px 0 0; color: var(--ink-soft); font-size: 15px; max-width: 40ch; }
.frag-foot { display: flex; align-items: center; gap: 20px; margin-top: 26px; }
.frag-price { font-family: 'Manrope', sans-serif; font-weight: 800; font-size: 20px; }

/* Accord chips: three words, hairline, no icons. */
.accords { display: flex; flex-wrap: wrap; gap: 7px; margin-top: 18px; }
.accord {
  font-size: 12px;
  letter-spacing: .04em;
  padding: 5px 12px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  color: var(--ink-soft);
}

/* -------------------------------------------------------------- reviews --- */

/* A grid, not CSS columns. With five tall phone photos the column balancer
   left one column empty and orphaned a single image, which is what "the
   reviews are messed up" was. The first photo spans a 2x2 block so the wall
   is still deliberately uneven rather than a tidy row of five. */
.reviews {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: clamp(10px, 1.4vw, 18px);
}
.review {
  position: relative;
  margin: 0;
  aspect-ratio: 3 / 4;
  border-radius: 14px;
  overflow: hidden;
  background: var(--paper-warm);
  box-shadow: var(--lift-1);
  transition: transform var(--slow) var(--ease), box-shadow var(--slow) var(--ease);
}
.review:hover { transform: translateY(-4px); box-shadow: var(--lift-2); }
/* cover + absolute fill, so every tile is the same shape whatever the photo. */
.review img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--slow) var(--ease);
}
.review:hover img { transform: scale(1.04); }

/* ---------------------------------------------------------- product page -- */

.crumbs { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; font-size: 12px; color: var(--ink-faint); padding: 22px 0 0; }
.crumbs a { text-decoration: none; }
.crumbs a:hover { color: var(--ink); }

.pdp {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
  gap: clamp(28px, 5vw, 80px);
  align-items: start;
  padding: clamp(20px, 3vw, 40px) 0 clamp(56px, 8vw, 104px);
}

.pdp-stage {
  position: sticky;
  perspective: 1200px;
  top: 96px;
  aspect-ratio: 1 / 1;
  border-radius: 24px;
  background: #fff;
  display: grid;
  place-items: center;
  overflow: hidden;
  box-shadow: var(--lift-2);
}
/* contain + a height cap so a wide image (the set boxes) and a tall one (the
   bottles) both sit inside the same square stage without distorting. */
.pdp-stage img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: 6%;
  --tilt: rotateX(0deg) rotateY(0deg) translateZ(0px);
  transform: var(--tilt);
  transition: transform 260ms var(--ease);
  will-change: transform;
}
/* The set boxes are landscape, so they need the width rather than the height. */
.pdp-stage[data-shape='wide'] img { width: 100%; height: 100%; }

.pdp-title { margin: 12px 0 0; }
.pdp-sub { margin: 14px 0 0; color: var(--ink-faint); font-size: 14px; letter-spacing: .01em; }
.pdp-price { font-family: 'Manrope', sans-serif; font-weight: 800; font-size: clamp(24px, 3vw, 32px); margin: 24px 0 0; }
.pdp-desc { margin: 22px 0 0; color: var(--ink-soft); max-width: 46ch; }
.pdp-actions { display: flex; flex-wrap: wrap; gap: 12px; margin: 30px 0 0; }

.spec { margin: 32px 0 0; padding: 22px 0 0; border-top: 1px solid var(--rule); display: grid; gap: 16px; }
.spec-row { display: grid; grid-template-columns: 128px minmax(0, 1fr); gap: 18px; align-items: baseline; }
.spec-row dt { font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint); font-weight: 600; }
.spec-row dd { margin: 0; font-size: 15px; color: var(--ink-soft); }

/* The pyramid: a vertical rail with the three stages hung off it. Reads as a
   structure rather than three bulleted lists. */
.pyramid { margin: 40px 0 0; padding: 28px 0 0; border-top: 1px solid var(--rule); }
.pyramid-head { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; margin-bottom: 26px; }

.stages { position: relative; padding-left: 26px; }
.stages::before {
  content: '';
  position: absolute;
  left: 4px; top: 6px; bottom: 6px;
  width: 1px;
  background: linear-gradient(180deg, var(--accent) 0%, var(--rule) 100%);
  transform: scaleY(0);
  transform-origin: top;
  transition: transform 900ms var(--ease) 120ms;
}
.pyramid.in .stages::before { transform: scaleY(1); }

.stage {
  position: relative;
  padding: 0 0 26px;
  transition: transform var(--fast) var(--ease);
  cursor: default;
}
.stage:hover { transform: translateX(5px); }
.stage:hover .stage-name { color: var(--accent); }
.stage::after {
  content: '';
  position: absolute;
  left: -26px; top: 7px;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0;
  transform: scale(.4);
  transition: opacity var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.stage:hover::after { opacity: 1; transform: scale(2.1); }
.stage:last-child { padding-bottom: 0; }
.stage::before {
  content: '';
  position: absolute;
  left: -26px; top: 7px;
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--paper);
  border: 1px solid var(--accent);
}
.stage[data-stage='top']::before { background: var(--accent); }
.stage-name { transition: color var(--fast) var(--ease); font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint); font-weight: 600; }
.stage-notes { margin: 8px 0 0; font-family: 'Manrope', sans-serif; font-weight: 700; font-size: clamp(17px, 2vw, 21px); letter-spacing: -.02em; }

/* --------------------------------------------------------- collection ----- */

.grid-products {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 260px), 1fr));
  gap: clamp(16px, 2.4vw, 30px);
}

.pcard { display: flex; flex-direction: column; text-decoration: none; color: inherit; }
.pcard-media {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: 16px;
  background: #fff;
  display: grid;
  place-items: center;
  overflow: hidden;
  box-shadow: var(--lift-1);
  transition: box-shadow var(--slow) var(--ease), transform var(--slow) var(--ease);
}
.pcard:hover .pcard-media { box-shadow: var(--lift-2); transform: translateY(-4px); }
.pcard-media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; padding: 9%; transition: transform var(--slow) var(--ease); }
.pcard:hover .pcard-media img { transform: scale(1.05) translateY(-5px); }
.pcard-body { padding: 16px 2px 0; display: flex; justify-content: space-between; gap: 14px; align-items: baseline; }
.pcard-name { font-family: 'Manrope', sans-serif; font-weight: 800; font-size: 16px; letter-spacing: -.02em; }
.pcard-price { font-family: 'Manrope', sans-serif; font-weight: 800; font-size: 15px; white-space: nowrap; }
.pcard-note { padding: 6px 2px 0; font-size: 13px; color: var(--ink-faint); }

/* --------------------------------------------------------------- cart ----- */

.cart-wrap { display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, .8fr); gap: clamp(20px, 3vw, 44px); align-items: start; }
.cart-panel { background: var(--card); border: 1px solid var(--rule-soft); border-radius: 18px; padding: clamp(18px, 2.4vw, 28px); box-shadow: var(--lift-1); }
.cart-line { display: grid; grid-template-columns: 84px minmax(0, 1fr) auto auto; gap: 18px; align-items: center; padding: 18px 0; border-bottom: 1px solid var(--rule-soft); }
.cart-line:last-of-type { border-bottom: 0; }
.cart-thumb { width: 84px; aspect-ratio: 1; border-radius: 10px; background: var(--paper-warm); display: grid; place-items: center; overflow: hidden; }
.cart-thumb img { width: 76%; max-height: 82%; object-fit: contain; }
.cart-name { font-family: 'Manrope', sans-serif; font-weight: 800; font-size: 15px; letter-spacing: -.02em; }
.cart-unit { font-size: 13px; color: var(--ink-faint); margin-top: 3px; }
.qty { width: 62px; height: 40px; text-align: center; border: 1px solid var(--rule); border-radius: 10px; background: var(--paper); font: inherit; font-size: 14px; }
.cart-total { font-family: 'Manrope', sans-serif; font-weight: 800; font-size: 16px; white-space: nowrap; }
.summary-row { display: flex; justify-content: space-between; gap: 16px; padding: 12px 0; font-size: 14px; color: var(--ink-soft); }
.summary-row.total { border-top: 1px solid var(--rule); margin-top: 8px; padding-top: 18px; font-family: 'Manrope', sans-serif; font-weight: 800; font-size: 19px; color: var(--ink); }
.icon-btn { border: 0; background: none; padding: 6px; cursor: pointer; color: var(--ink-faint); border-radius: 8px; transition: color var(--fast) var(--ease); }
.icon-btn:hover { color: var(--ink); }

.empty { text-align: center; padding: clamp(48px, 9vw, 110px) 0; }
.empty .lead { margin: 16px auto 0; }

/* --------------------------------------------------------------- prose ---- */

.prose { max-width: 62ch; }
.prose p { color: var(--ink-soft); }
.prose h2 { font-size: 24px; margin: 32px 0 10px; }

/* -------------------------------------------------------------- footer ---- */

.site-foot { margin-top: auto; border-top: 1px solid var(--rule); padding: clamp(36px, 5vw, 64px) 0 32px; }
.foot-grid { display: flex; flex-wrap: wrap; gap: 28px; justify-content: space-between; align-items: flex-start; }
.foot-links { display: flex; flex-wrap: wrap; gap: 22px; }
.foot-links a { font-size: 13px; color: var(--ink-soft); text-decoration: none; }
.foot-links a:hover { color: var(--ink); }
.ig {
  display: inline-grid;
  place-items: center;
  width: 42px; height: 42px;
  border-radius: 50%;
  border: 1px solid var(--rule);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.ig:hover { background: var(--ink); color: var(--paper); transform: translateY(-2px); }
.foot-fine { margin-top: 34px; font-size: 12px; color: var(--ink-faint); }

/* --------------------------------------------------------------- toast ---- */

#toast-container { position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%); z-index: 90; display: flex; flex-direction: column; gap: 8px; align-items: center; pointer-events: none; }
.toast {
  background: var(--ink);
  color: var(--paper);
  padding: 12px 20px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  box-shadow: var(--lift-2);
  animation: toast-in 320ms var(--ease);
}
.toast.error { background: #7a2620; }
@keyframes toast-in { from { opacity: 0; transform: translateY(10px) scale(.97); } to { opacity: 1; transform: none; } }

/* -------------------------------------------------------------- motion ---- */

/* Reveal on entry. --i staggers siblings; the values are small because a long
   stagger is the thing that makes a page feel auto-generated. */
[data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--slow) var(--ease), transform var(--slow) var(--ease);
  transition-delay: calc(var(--i, 0) * 65ms);
}
[data-reveal].in { opacity: 1; transform: none; }

/* Images settle rather than pop: a touch of scale released on entry. */
[data-reveal='media'] { transform: translateY(22px) scale(.985); }
[data-reveal='media'].in { transform: none; }

/* If scripting never runs, nothing may stay hidden. */
.no-js [data-reveal] { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .hero-card { animation: none; }
  .hero-card::after { display: none; }
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  [data-reveal] { opacity: 1 !important; transform: none !important; }
  .hero-bottle { transform: none !important; }
}

/* ----------------------------------------------------------- responsive --- */

@media (max-width: 1000px) {
  .hero-grid { grid-template-columns: 1fr; gap: 8px; }
  .hero-copy { order: 2; }
  .hero-copy .display-xl { max-width: none; }
  .hero-stage { order: 1; justify-self: center; width: min(80%, 420px); margin: 0 0 28px; }
  .hero-meta { max-width: none; }
  .pdp { grid-template-columns: 1fr; }
  .pdp-stage { position: static; }
  .frag, .frag:nth-child(even) { grid-template-columns: 1fr; width: 100%; margin-left: 0; }
  .frag:nth-child(even) .frag-media { order: 0; }
  .reviews { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .cart-wrap { grid-template-columns: 1fr; }
}

@media (max-width: 720px) {
  .nav-links { display: none; }
  .section-head { grid-template-columns: 1fr; align-items: start; }
  .sets { grid-template-columns: 1fr; }
  .reviews { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .review-img { aspect-ratio: 1 / 1; }
  .review-body { padding: 12px 13px 15px; }
  .review-body p { font-size: 12.5px; }
  .cart-line { grid-template-columns: 64px minmax(0, 1fr) auto; row-gap: 10px; }
  .cart-thumb { width: 64px; }
  .cart-total { grid-column: 2 / -1; }
  .hero-ghost { font-size: 34vw; }
  .spec-row { grid-template-columns: 100px minmax(0, 1fr); gap: 12px; }
}

/* -------------------------------------------------------------- rating ---- */

.rating { display: flex; align-items: center; gap: 12px; margin-top: 18px; font-size: 13px; color: var(--ink-soft); }
.stars { display: inline-flex; gap: 3px; }
.stars svg { width: 15px; height: 15px; fill: var(--accent); }

/* The wordmark parts to let the hero in. */
@keyframes ghost-in-l {
  from { opacity: 0; transform: translate3d(-7vw, 0, 0); }
  to   { opacity: 1; transform: translate3d(var(--gx, 0px), var(--gy, 0px), 0); }
}
@keyframes ghost-in-r {
  from { opacity: 0; transform: translate3d(7vw, 0, 0); }
  to   { opacity: 1; transform: translate3d(var(--gx, 0px), var(--gy, 0px), 0); }
}
.hero-ghost span:first-child { animation: ghost-in-l 1300ms var(--ease) backwards; }
.hero-ghost span:last-child  { animation: ghost-in-r 1300ms var(--ease) 140ms backwards; }

@media (prefers-reduced-motion: reduce) {
  .hero-ghost span { animation: none !important; }
}

/* ---- reviews: photo above, stars and the customer's words below --------- */
.review { display: flex; flex-direction: column; aspect-ratio: auto; }
.review-img { position: relative; aspect-ratio: 3 / 4; overflow: hidden; background: var(--paper-warm); }
.review:first-child .review-img { aspect-ratio: 3 / 4; }
.review-img img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform var(--slow) var(--ease); }
.review:hover .review-img img { transform: scale(1.04); }
.review-body { background: var(--card); padding: 14px 16px 18px; flex: 1; }
.review-body p { margin: 8px 0 0; font-size: 13px; line-height: 1.5; color: var(--ink-soft); }

/* ==========================================================================
   THE REEL
   --------------------------------------------------------------------------
   Every section of the page is an act: a tall track that supplies scroll
   distance, wrapping a sticky stage pinned at viewport height. The page
   scrolls the whole time; the stage does not move, so what you see is content
   transforming in place rather than sliding past. That is the video feeling.

   One number drives each act: --p, 0 to 1 across its track, written by the
   layout script onto the act element. Everything below is derived from it.

   Rules held throughout:
     - transform and opacity only, so nothing triggers layout or paint
     - a stage is never taller than the viewport, or sticky releases
     - every act degrades to a plain stacked section under reduced motion
   ========================================================================== */

@property --p {
  syntax: '<number>';
  inherits: true;
  initial-value: 0;
}

/* The fixed backdrop the dark act fades in and out of. Sits behind all
   content and above the paper, so the page changes colour by washing rather
   than by one section ending and another starting. */
.page-wash {
  position: fixed;
  inset: 0;
  z-index: 0;
  background: #14140f;
  opacity: 0;
  pointer-events: none;
}
main { position: relative; z-index: 1; }

.act { position: relative; --p: 0; }

/* ---- the seam ------------------------------------------------------------
   Acts sit back to back, so without this the handover between them is a hard
   vertical scroll: one stage slides off, the next slides on. --edge softens
   both ends of every act into a shared envelope — content recedes in Z and
   dims as its act finishes, and arrives the same way as the next begins — so
   the join reads as a camera move rather than a cut.

   It never reaches zero. A full fade at the seam would flash the bare page
   between acts, which is worse than the cut it replaces. */
.act {
  --edge: clamp(0.24, min(calc(var(--p) / 0.07), calc((1 - var(--p)) / 0.07)), 1);
  --recede: calc((1 - var(--edge)) * -180px);
}

/* The envelope exists to blend an act with its neighbours. The first act has
   nothing before it and the last has nothing after, so they get only the half
   they need — otherwise the page opened at 24% opacity. */
.act-hero  { --edge: clamp(0.24, calc((1 - var(--p)) / 0.07), 1); }
.act-close { --edge: clamp(0.24, calc(var(--p) / 0.07), 1); }

.act-stage {
  position: sticky;
  top: 0;
  height: 100vh;
  max-height: 100vh;
  overflow: hidden;
  display: grid;
  place-items: center;
  perspective: 1200px;
  perspective-origin: 50% 45%;
}

.act-inner {
  position: relative;
  transform: translate3d(0, 0, var(--recede));
  width: 100%;
  max-width: var(--shell);
  padding: 0 var(--gutter);
  margin: 0 auto;
}

/* Track heights are the runtime of each act. */
.act-hero    .act-track { height: 300vh; }
.act-sets    .act-track { height: 240vh; }
.act-range   .act-track { height: 420vh; }
.act-reviews .act-track { height: 240vh; }
.act-close   .act-track { height: 160vh; }

/* A beat: one card in the stack, opaque only for its stretch of the timeline.
   --a is its own ramp; clamping keeps it fully on while it holds. */
.beat {
  position: absolute;
  inset: 0;
  display: grid;
  align-content: center;
  padding: 0 var(--gutter);
  max-width: var(--shell);
  margin: 0 auto;
  opacity: clamp(0, var(--a, 1), 1);
  pointer-events: none;
}
.beat[data-live='true'] { pointer-events: auto; }
.beat { opacity: calc(clamp(0, var(--a, 1), 1) * var(--edge)); }
.beat-inner { width: min(52%, 760px); }
.beat-inner .lead { max-width: 38ch; }
.beat h1 { font-size: clamp(36px, min(5.6vw, 12vh), 112px); }
.beat h2 { font-size: clamp(30px, min(4.4vw, 9.5vh), 86px); }
.beat .lead { margin-top: 18px; }
.beat .hero-actions { margin-top: 28px; }

/* The progress hairline, shared by every act. Reads as a rule, works as a
   scrubber. */
.act-rule {
  position: absolute;
  left: 0; bottom: 0;
  height: 2px; width: 100%;
  background: var(--accent);
  transform: scaleX(var(--p));
  transform-origin: 0 50%;
  opacity: calc(.45 * var(--edge));
}

/* ---- ACT ONE: the opening ------------------------------------------------ */

.cine-mark {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  transform-style: preserve-3d;
  pointer-events: none;
  user-select: none;
  transform: translate3d(0, calc(var(--p) * -12vh), calc(var(--p) * -760px));
}
.cine-mark span {
  display: block;
  font-family: 'Manrope', sans-serif;
  font-weight: 800;
  font-size: clamp(58px, 13vw, 300px);
  line-height: .84;
  letter-spacing: -.05em;
  text-align: center;
  color: rgba(17, 17, 16, .055);
  transform-style: preserve-3d;
}
.cine-mark span:last-child { color: rgba(17, 17, 16, .09); }

/* Each letter is its own plane, arriving from its own distance, so the mark
   assembles rather than fades. */
.cine-mark i {
  display: inline-block;
  font-style: normal;
  transform-origin: 50% 60%;
  animation: letter-in 1500ms cubic-bezier(.19, 1, .22, 1) backwards;
  animation-delay: calc(var(--i) * 42ms);
}
@keyframes letter-in {
  from { opacity: 0; transform: translate3d(0, 26%, -900px) rotateX(58deg) rotateZ(-9deg); }
  to   { opacity: 1; transform: none; }
}

/* Handover points: 0.26 and 0.62. Each ramp hits 0 exactly where the next
   hits 1, so the stage is never empty. */
.beat-1 { --a: calc((0.26 - var(--p)) / 0.08); transform: translate3d(0, calc(var(--p) * -22vh), 0); }
.beat-2 { --a: min(calc((var(--p) - 0.18) / 0.08), calc((0.62 - var(--p)) / 0.08)); transform: translate3d(0, calc((0.44 - var(--p)) * 26vh), 0); }
.beat-3 { --a: calc((var(--p) - 0.54) / 0.08); transform: translate3d(0, calc((0.78 - var(--p)) * 22vh), 0); }

.cine-product {
  position: absolute;
  right: max(var(--gutter), 4vw);
  top: 50%;
  width: min(28vw, 440px);
  border-radius: 20px;
  overflow: hidden;
  background: #fff;
  box-shadow: var(--lift-3);
  transform:
    translate3d(calc(var(--p) * -22vw), -50%, calc(var(--p) * 150px))
    rotateY(calc(var(--p) * -26deg))
    scale(calc(1 + var(--p) * 0.06));
  opacity: clamp(0, calc((0.88 - var(--p)) / 0.12), 1);
}
.cine-product img { display: block; width: 100%; height: auto; }

.cine-cue {
  position: absolute;
  left: 50%; bottom: 26px;
  transform: translateX(-50%);
  font-size: 11px; letter-spacing: .16em; text-transform: uppercase;
  color: var(--ink-faint);
  opacity: clamp(0, calc((0.06 - var(--p)) / 0.05), 1);
  animation: cue-in 700ms var(--ease) 2200ms backwards;
}
@keyframes cue-in { from { opacity: 0; transform: translateX(-50%) translateY(8px); } }

/* ---- ACT TWO: the sets --------------------------------------------------- */

.act-sets .act-stage { color: #f2efe8; }
.act-sets .label { color: rgba(242,239,232,.5); }
.act-sets .lead { color: rgba(242,239,232,.72); }
.act-sets .btn { border-color: rgba(242,239,232,.34); color: #f2efe8; }
.act-sets .btn-solid { background: #f2efe8; color: #14140f; border-color: #f2efe8; }
.act-sets :focus-visible { outline-color: #f2efe8; }

.sets-copy {
  opacity: var(--edge);
  position: absolute;
  left: var(--gutter);
  top: 50%;
  width: min(40%, 580px);
  transform: translate3d(0, calc(-50% + (0.5 - var(--p)) * 8vh), 0);
}

/* The two boxes hand over to each other on the right of the stage. */
.set-card {
  position: absolute;
  right: max(var(--gutter), 4vw);
  top: 50%;
  width: min(38vw, 560px);
  text-decoration: none;
  color: inherit;
  pointer-events: none;
}
.set-card[data-live='true'] { pointer-events: auto; }
.set-card .set-media {
  border-radius: 16px;
  overflow: hidden;
  background: #fff;
  aspect-ratio: 4 / 3;
  position: relative;
  box-shadow: 0 40px 90px -30px rgba(0,0,0,.7);
}
.set-card .set-media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

.set-a {
  --a: calc((0.52 - var(--p)) / 0.12);
  transform: translate3d(calc(var(--p) * -6vw), -50%, calc(var(--p) * 120px)) rotateY(calc(var(--p) * -14deg));
}
.set-b {
  --a: calc((var(--p) - 0.40) / 0.12);
  transform: translate3d(calc((1 - var(--p)) * 6vw), -50%, calc((1 - var(--p)) * 120px)) rotateY(calc((1 - var(--p)) * 14deg));
}
.set-card { opacity: calc(clamp(0, var(--a), 1) * var(--edge)); }

/* ---- ACT THREE: the range ------------------------------------------------
   Four fragrances, each holding a quarter of the timeline. The card and its
   copy cross-dissolve in place, so the bottles appear to replace one another
   without the page moving. */

.frag-beat {
  position: absolute;
  inset: 0;
  display: grid;
  align-content: center;
  padding: 0 var(--gutter);
  max-width: var(--shell);
  margin: 0 auto;
  opacity: clamp(0, var(--a, 1), 1);
  pointer-events: none;
}
.frag-beat[data-live='true'] { pointer-events: auto; }
.frag-beat { opacity: calc(clamp(0, var(--a, 1), 1) * var(--edge)); }
.frag-beat .frag-copy { width: min(46%, 680px); }
.frag-beat .frag-name { font-size: clamp(32px, min(4.8vw, 10vh), 92px); margin: 12px 0 0; }
.frag-beat .frag-note { margin-top: 16px; max-width: 38ch; font-size: clamp(15px, 1.2vw, 19px); }

/* Windows of 0.25 each, with a short dissolve at each seam. */
/* Four bottles, handovers at 0.28 / 0.53 / 0.78. Out exactly where the next
   comes in, so the stage is never empty.

   --c is each beat's centre on the timeline. Every beat is offset vertically
   by its distance from that centre, so during a crossfade the outgoing copy
   is already travelling up and out while the incoming rises from below. Two
   texts at half opacity in the same place read as a printing error; the same
   two passing each other read as a cut in a film. */
.frag-beat { transform: translate3d(0, calc((var(--c) - var(--p)) * 52vh), 0); }
.fb-1 { --c: 0.14; --a: calc((0.28 - var(--p)) / 0.08); }
.fb-2 { --c: 0.39; --a: min(calc((var(--p) - 0.20) / 0.08), calc((0.53 - var(--p)) / 0.08)); }
.fb-3 { --c: 0.64; --a: min(calc((var(--p) - 0.45) / 0.08), calc((0.78 - var(--p)) / 0.08)); }
.fb-4 { --c: 0.89; --a: calc((var(--p) - 0.70) / 0.08); }

/* The bottle stays put and only dissolves, so it reads as one object being
   swapped rather than four sliding past. */
.frag-shot { transform: translate3d(0, calc(-50% - (var(--c) - var(--p)) * 52vh), 0); }

.frag-shot {
  position: absolute;
  right: max(var(--gutter), 4vw);
  top: 50%;
  width: min(26vw, 400px);
  aspect-ratio: 4 / 5;
  border-radius: 18px;
  overflow: hidden;
  background: #fff;
  box-shadow: var(--lift-3);
  transform: translateY(-50%);
}
.frag-shot img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; padding: 7%; }

/* The running index, fixed on the left of the stage. */
.range-index {
  position: absolute;
  left: var(--gutter);
  bottom: 8vh;
  display: flex;
  gap: 10px;
  align-items: center;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.range-index i {
  display: block;
  width: 26px; height: 2px;
  background: var(--rule);
  font-style: normal;
}
.range-index i { position: relative; overflow: hidden; }
.range-index i::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--ink);
  opacity: clamp(0, var(--a, 0), 1);
}
.range-index i:nth-of-type(1) { --a: calc((0.28 - var(--p)) / 0.08); }
.range-index i:nth-of-type(2) { --a: min(calc((var(--p) - 0.20) / 0.08), calc((0.53 - var(--p)) / 0.08)); }
.range-index i:nth-of-type(3) { --a: min(calc((var(--p) - 0.45) / 0.08), calc((0.78 - var(--p)) / 0.08)); }
.range-index i:nth-of-type(4) { --a: calc((var(--p) - 0.70) / 0.08); }

/* ---- ACT FOUR: the reviews -----------------------------------------------
   The wall converges: the five cards start scattered in depth and settle into
   their grid as the act plays. */

.wall {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: clamp(10px, 1.4vw, 18px);
  align-items: start;
}
.wall .review {
  transform:
    translate3d(calc(var(--o, 0) * (1 - var(--p)) * 14vw), calc((1 - var(--p)) * 12vh), calc((1 - var(--p)) * -420px))
    rotateY(calc(var(--o, 0) * (1 - var(--p)) * 12deg));
  opacity: calc(clamp(0, calc(var(--p) / 0.28), 1) * var(--edge));
}
.wall .review:nth-child(1) { --o: -2; }
.wall .review:nth-child(2) { --o: -1; }
.wall .review:nth-child(3) { --o: 0; }
.wall .review:nth-child(4) { --o: 1; }
.wall .review:nth-child(5) { --o: 2; }

.reviews-head {
  margin-bottom: clamp(20px, 3vw, 40px);
  transform: translate3d(0, calc((1 - var(--p)) * 4vh), 0);
  opacity: calc(clamp(0, calc(var(--p) / 0.18), 1) * var(--edge));
}

/* ---- ACT FIVE: the close ------------------------------------------------- */

.close-inner {
  text-align: center;
  transform: scale(calc(0.94 + var(--p) * 0.06));
  opacity: calc(clamp(0, calc(var(--p) / 0.25), 1) * var(--edge));
}
.close-inner .lead { margin: 18px auto 0; }
.close-inner .hero-actions { justify-content: center; margin-top: 30px; }

/* ---- responsive ---------------------------------------------------------- */

@media (max-width: 900px) {
  .act-hero .act-track    { height: 280vh; }
  .act-sets .act-track    { height: 220vh; }
  .act-range .act-track   { height: 380vh; }
  .act-reviews .act-track { height: 220vh; }

  .beat { align-content: end; padding-bottom: 12vh; }
  .beat-inner { width: 100%; }
  .beat-inner .lead { max-width: none; }

  .cine-product {
    right: auto; left: 50%; top: 18%;
    width: min(56vw, 280px);
    transform: translate3d(-50%, calc(var(--p) * -6vh), calc(var(--p) * 90px)) rotateY(calc(var(--p) * -16deg));
  }

  .sets-copy { position: absolute; left: var(--gutter); right: var(--gutter); top: auto; bottom: 10vh; width: auto; transform: none; }
  .set-card { right: auto; left: 50%; top: 16%; width: min(72vw, 340px); }
  .set-a { transform: translate3d(-50%, 0, calc(var(--p) * 80px)) rotateY(calc(var(--p) * -10deg)); }
  .set-b { transform: translate3d(-50%, 0, calc((1 - var(--p)) * 80px)) rotateY(calc((1 - var(--p)) * 10deg)); }

  .frag-beat { align-content: end; padding-bottom: 10vh; }
  .frag-beat .frag-copy { width: 100%; }
  .frag-beat .frag-note { max-width: none; }
  .frag-shot { right: auto; left: 50%; top: 14%; width: min(52vw, 250px); transform: translateX(-50%); }
  .range-index { bottom: 3vh; }

  .wall { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .wall .review:nth-child(n+5) { display: none; }
  .wall .review-body p { font-size: 12px; }
}

/* ---- reduced motion: no reel, just a page ------------------------------- */

@media (prefers-reduced-motion: reduce) {
  .act-track { height: auto !important; }
  .act-stage { position: static; height: auto; max-height: none; overflow: visible; padding: 10vh 0; }
  .cine-mark, .act-rule, .cine-cue, .range-index { display: none; }
  .cine-mark i { animation: none; }

  .beat, .frag-beat, .frag-shot {
    position: static; opacity: 1; transform: none; padding: 0 var(--gutter);
  }
  .beat-2, .beat-3, .fb-2, .fb-3, .fb-4 { display: none; }

  .cine-product, .set-card, .frag-shot {
    position: static; transform: none; opacity: 1; margin: 28px auto 0;
  }
  .set-b { display: none; }
  .sets-copy { position: static; width: auto; transform: none; }
  .wall .review, .reviews-head, .close-inner { transform: none; opacity: 1; }
}

/* ==========================================================================
   THE CATALOG
   --------------------------------------------------------------------------
   Once the reel has run, the page stops performing. No pinning, no scroll
   driving, no beats: a plain grid that scrolls the way a shop should, with
   every product reachable and buyable in one place. The acts are the pitch;
   this is the counter.
   ========================================================================== */

.catalog { padding: clamp(56px, 8vw, 104px) 0 clamp(64px, 9vw, 120px); }
.catalog .section-head { margin-bottom: clamp(24px, 3vw, 40px); }
.catalog-group + .catalog-group { margin-top: clamp(40px, 5vw, 68px); }
.catalog-group > .label { margin-bottom: 18px; }
/* Explicit counts: auto-fill left the fourth fragrance orphaned on its own
   row at common desktop widths. */
.catalog-group .grid-products { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.catalog-group:last-child .grid-products { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 620px; }
@media (max-width: 1000px) { .catalog-group .grid-products { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) {
  .catalog-group .grid-products,
  .catalog-group:last-child .grid-products { grid-template-columns: 1fr; }
}

.pcard-actions { display: flex; gap: 8px; padding-top: 12px; }
.pcard-actions .btn { min-height: 40px; padding: 0 16px; font-size: 13px; }

@media (prefers-reduced-motion: reduce) {
  .act { --edge: 1; --recede: 0px; }
  /* No scroll driving, so the dark act paints its own background again. */
  .page-wash { display: none; }
  .act-sets .act-stage { background: #14140f; }
}
