/* ═══════════════════════════════════════════════════════════════
   THE PROJECTION — scroll-driven cinematic hero
   A vertical reel in a dark room. The 9:16 frame is never cropped;
   the margins are the theatre, and copy stands in the theatre.

   Two rendering modes:
     Theatre   (landscape desktop) — column of light, copy in the room
     Full-bleed (portrait / narrow) — film fills width, copy anchored
                                      where the subject is not

   Without JS (or with prefers-reduced-motion) `.cine` degrades to a
   static editorial stack. Same DOM, same copy, no duplication.
   ═══════════════════════════════════════════════════════════════ */

.cine {
  --cine-track: 600svh;
  --plate-w: 480;
  --plate-h: 854;
  position: relative;
  background: var(--espresso-deep);
  color: var(--on-dark);
}

/* ── Header + grain over the dark theatre ──────────────────── */
/* A parchment bar with backdrop-blur floating over a black stage
   reads as a browser chrome bug. Invert it while the film is on
   screen, and drop the multiply grain, which is a no-op over near
   black but still costs a full-viewport composited layer. */
.cine-js body.is-cine .header {
  background: rgba(28,15,7,.72);
  border-bottom-color: rgba(246,235,217,.10);
  color: var(--on-dark);
}
.cine-js body.is-cine .header .nav a,
.cine-js body.is-cine .header .icon-btn { color: var(--on-dark-soft); }
.cine-js body.is-cine .header .nav a:hover,
.cine-js body.is-cine .header .icon-btn:hover { color: var(--on-dark); }
.cine-js body.is-cine .header__logo img { filter: invert(1) brightness(1.9); }
.cine-js body.is-cine .header__cta { background: var(--copper); color: var(--espresso); }
.cine-js body.is-cine .grain { display: none; }
/* The site-wide mobile CTA is fixed at bottom:16px z-index:35 and lands
   directly on top of "Skip film". Drop it while the film owns the screen —
   the cinematic ends with its own Shop Canna Joe button. */
.cine-js body.is-cine .sticky-cta { display: none; }
.header, .header .nav a, .header .icon-btn { transition: background .35s ease, color .35s ease, border-color .35s ease; }

/* ── HANDOFF — carries the visitor out of the theatre into Canna Tini ── */
.handoff {
  background: linear-gradient(180deg, var(--espresso-deep) 0%, var(--espresso) 55%, var(--roast) 100%);
  color: var(--on-dark);
}
.handoff__inner {
  display: grid;
  grid-template-columns: 1.1fr .9fr;
  gap: 48px;
  align-items: center;
  padding-block: 96px;
}
.handoff__lede { margin-left: 0; margin-right: 0; max-width: 46ch; }
/* The big product title (patch 7): the film is Canna Joe's chapter, this is
   Canna Tini's — the name at display scale is what separates the two. */
.handoff__title { margin: 0 0 10px; color: var(--on-dark); }
.handoff__kicker {
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(19px, 2vw, 24px);
  color: var(--gold-light);
  margin: 0 0 16px;
}
.handoff .quote { margin-bottom: 20px; }
/* .btn--ghost-light carries a standalone margin-top for when it sits alone
   under a lede; inside a CTA row it must sit on the same baseline. */
.handoff__cta .btn, .cine__cta .btn { margin-top: 0; }
.handoff__specs { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 26px; }
.handoff__art { display: flex; justify-content: center; }
.handoff__art img {
  height: clamp(320px, 42vw, 520px);
  width: auto;
  filter: drop-shadow(0 30px 40px rgba(0,0,0,.55));
}
@media (max-width: 980px) {
  .handoff__inner { grid-template-columns: 1fr; gap: 32px; padding-block: 72px; }
  .handoff__art { order: -1; }
  .handoff__art img { height: clamp(260px, 54vw, 380px); }
}

/* ── STATIC STACK (no JS / reduced motion) ─────────────────── */
/* This is the authored default. JS upgrades it in place.       */

/* The runtime-only furniture is hidden by DEFAULT, not merely under
   .cine-js. Without JS — or under prefers-reduced-motion, where the
   .cine-js class is deliberately never added — an unstyled <canvas>
   and the poster <img> would otherwise render at natural size above
   the editorial stack. */
.cine__room,
.cine__bloom,
.cine__plate,
.cine__poster,
.cine__skip,
.cine__loading,
.cine__hint { display: none; }   /* "Scroll to begin" means nothing in the static stack */
.cine__beats {
  max-width: 1080px;
  margin: 0 auto;
  padding: 72px var(--gutter) 96px;
  display: flex;
  flex-direction: column;
  gap: 72px;
}
.cine__beat { display: grid; gap: 22px; }
.cine__plate-still {
  width: 100%;
  max-width: 380px;
  border-radius: 14px;
  box-shadow: 0 30px 70px -40px rgba(0,0,0,.9);
}
.cine__eyebrow {
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: .22em;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--copper);
  margin: 0;
}
.cine__headline {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(30px, 2.6vw, 44px);
  line-height: 1.08;
  letter-spacing: -.015em;
  margin: 0;
  color: var(--on-dark);
}
.cine__headline em { font-style: italic; color: var(--gold-light); }
.cine__body {
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.6;
  color: var(--on-dark-mute);
  margin: 0;
  max-width: 44ch;
}
.cine__wordmark {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(52px, 9vw, 132px);
  line-height: .92;
  letter-spacing: -.02em;
  margin: 0;
  color: var(--on-dark);
}
/* The lockup replaces the typeset wordmark as the overture's h1. Keeping it
   an <h1> with real alt text means the page still has one meaningful heading. */
.cine__lockup { margin: 0; line-height: 0; }
.cine__lockup img {
  display: block;
  width: min(520px, 74vw);
  height: auto;
  margin: 0 auto;
}
.cine__title {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(38px, 3.4vw, 56px);
  line-height: 1;
  letter-spacing: .01em;
  margin: 0;
  color: var(--on-dark);
}
.cine__cta { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 4px; }
/* Transparent-ingredients line in the finale (patch 5): quieter than the
   dose line but complete — the whole list, no fine print. */
.cine__ingredients { font-size: 12.5px; line-height: 1.55; max-width: 46ch; }
.cine__ingredients strong, .cine__body strong { color: var(--on-dark); font-weight: 700; }
/* Fragment link to the mushroom section — a text line, not a button, so the
   bottom-anchored finale card stays short enough for small phones. */
.cine__mixlink {
  justify-self: start;
  font-family: var(--sans);
  font-weight: 700;
  font-size: 12.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--gold-light);
  text-decoration: underline;
  text-underline-offset: 4px;
}
.cine__mixlink:hover, .cine__mixlink:focus-visible { color: var(--on-dark); }
.cine__hint {
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--on-dark-dim);
  margin: 0;
}
/* The compliance line sits with the potency figure and must be present in
   BOTH the scrub and the static stack — unlike the "scroll to begin" hint. */
.cine__compliance { display: block !important; letter-spacing: .14em; margin-top: 4px; }
.cine__rule {
  height: 1px;
  width: 54px;
  background: var(--copper);
  transform-origin: left center;
  margin: 0 0 14px;
}

/* ═══════════════════════════════════════════════════════════════
   UPGRADED MODE — everything below applies only under .cine-js
   ═══════════════════════════════════════════════════════════════ */

.cine-js .cine { height: var(--cine-track); }
.cine-js .cine__beats {
  position: absolute; inset: 0;
  display: block; max-width: none; padding: 0;
  pointer-events: none;
}
.cine-js .cine__plate-still { display: none; }

/* The stage pins for the whole track. */
.cine-js .cine__stage {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: hidden;
  display: grid;
  place-items: center;
  isolation: isolate;
}

/* ── (1) ROOM — fixed, so a collapsing mobile URL bar can never
       reveal parchment below the stage. Requires that no ancestor
       creates a containing block (see styles.css `rise` exclusion). */
.cine-js .cine__room {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 0;
  background: var(--espresso-deep);
  pointer-events: none;
}
/* A permanent full-viewport fixed layer costs a composited layer for the
   whole home route; only paint the room while the stage is actually up. */
.cine-js .cine--onscreen .cine__room { display: block; }
.cine-js .cine__skip,
.cine-js .cine__loading,
.cine-js .cine__hint { display: block; }
.cine-js .cine__room::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 70% 60% at 50% 45%, transparent 0%, var(--espresso-deep) 76%);
}

/* ── (2) BLOOM — the film lighting its own room.
       Drawn at 64px wide; the browser's upscale IS the blur.
       No CSS filter in the per-frame path. */
.cine-js .cine__bloom {
  position: absolute;
  inset: -14%;
  width: 128%;
  height: 128%;
  object-fit: cover;
  z-index: 1;
  opacity: 0;
  pointer-events: none;
  display: none;
  /* Dissolve the spill outward so it never resolves a hard rectangle
     against the room, and stays out of the corners. */
  -webkit-mask-image: radial-gradient(ellipse 78% 70% at 50% 46%, #000 18%, transparent 82%);
          mask-image: radial-gradient(ellipse 78% 70% at 50% 46%, #000 18%, transparent 82%);
}

/* ── (3) COLUMN ────────────────────────────────────────────── */
.cine-js .cine__frame {
  position: relative;
  z-index: 2;
  display: grid;
  place-items: center;
}
.cine-js .cine__plate,
.cine-js .cine__poster {
  display: block;
  aspect-ratio: var(--plate-w) / var(--plate-h);
  width: 100vw;
  height: auto;
  max-height: 100svh;
  object-fit: contain;
}
.cine-js .cine__poster {
  position: absolute;
  inset: 0;
  margin: auto;
  z-index: 1;
  opacity: 1;
  transition: opacity .28s linear;
}
.cine-js .cine__plate { position: relative; z-index: 0; }
.cine-js .cine--running .cine__poster { opacity: 0; }
.cine-js .cine--settled .cine__poster { opacity: 1; }

/* Copy cards float over the stage. */
.cine-js .cine__beat {
  position: absolute;
  z-index: 3;
  display: grid;
  gap: 14px;
  opacity: 0;
  pointer-events: none;
  will-change: opacity;
}
.cine-js .cine__beat[data-visible] { pointer-events: auto; }
.cine-js .cine__beat--silent { display: none; }

/* Legibility floor: a soft pool of the room's own colour. */
.cine-js .cine__beat::before {
  content: '';
  position: absolute;
  inset: -40px -44px;
  z-index: -1;
  background: radial-gradient(140% 100% at 50% 50%,
    rgba(28,15,7,.80) 0%, rgba(28,15,7,.56) 55%, transparent 100%);
  pointer-events: none;
}

.cine-js .cine__skip {
  position: absolute;
  right: max(20px, var(--gutter));
  bottom: 22px;
  z-index: 5;
  appearance: none;
  background: none;
  border: none;
  padding: 8px 2px;
  font-family: var(--sans);
  font-size: 11px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--on-dark-dim);
  border-bottom: 1px solid transparent;
  transition: color .2s, border-color .2s, opacity .3s;
}
.cine-js .cine__skip:hover,
.cine-js .cine__skip:focus-visible { color: var(--on-dark); border-bottom-color: var(--copper); }

/* Loading whisper — never a spinner. */
.cine-js .cine__loading {
  position: absolute;
  left: 50%; bottom: 26px;
  transform: translateX(-50%);
  z-index: 5;
  width: 120px; height: 1px;
  background: rgba(246,235,217,.16);
  overflow: hidden;
  opacity: 0;
  transition: opacity .4s;
}
.cine-js .cine__loading[data-on] { opacity: 1; }
.cine-js .cine__loading i {
  display: block; height: 100%; width: 0%;
  background: var(--copper);
  transition: width .3s ease-out;
}

/* ── THEATRE MODE — landscape desktop ──────────────────────── */
/* Keyed on ASPECT, because aspect is the entire problem: this   */
/* routes a 1366x768 laptop into theatre and an iPad portrait    */
/* or half-width window into full-bleed.                        */
@media (min-aspect-ratio: 5/4) and (min-width: 1024px) {
  .cine-js .cine__bloom { display: block; }

  .cine-js .cine__plate,
  .cine-js .cine__poster {
    width: auto;
    height: min(100svh, 1000px);
    max-height: none;
    /* Edge feather — static, so the mask is compositor-cached.
       4% clears the tightest subject margin (measured x=23 of 480). */
    -webkit-mask-image: linear-gradient(to right, transparent 0%, #000 4%, #000 96%, transparent 100%);
            mask-image: linear-gradient(to right, transparent 0%, #000 4%, #000 96%, transparent 100%);
  }
  .cine-js .cine__frame { box-shadow: 0 0 140px 24px rgba(179,120,59,.16); }

  .cine-js .cine__beat {
    top: 50%;
    /* --base-y positions the card; --lift is the enter/exit rise the JS
       writes. Composing them in one transform means the rise actually
       applies (it previously wrote --lift that no rule consumed). */
    transform: translateY(calc(var(--base-y, -46%) + var(--lift, 0px)));
    width: min(360px, 26vw);
    max-width: 360px;
  }
  /* The title and the beat beneath it are on screen together and must
     stack as title + subtitle rather than land on the same coordinates. */
  .cine-js .cine__beat[data-stack="title"] { --base-y: -104%; }
  .cine-js .cine__beat[data-stack="below"] { --base-y: 6%; }
  /* Process beats stand in the LEFT room; the product beats in the
     RIGHT. The side flips exactly on the f030 hard cut. */
  .cine-js .cine__beat[data-side="left"]  { left: clamp(80px, 5vw, 120px); }
  .cine-js .cine__beat[data-side="right"] { right: clamp(80px, 5vw, 120px); text-align: left; }

  .cine-js .cine__beat--overture {
    left: 50%; right: auto;
    width: min(1000px, 78vw); max-width: none;
    transform: translate(-50%, calc(-50% + var(--lift, 0px)));
    text-align: center;
    justify-items: center;
  }
  .cine-js .cine__beat--overture::before { inset: -60px -80px; }

  /* Takeover card (patch 4): enters from ABOVE — the lift is inverted and
     amplified so it reads as a slide-in, not the usual rise — and rests
     high in the frame. */
  .cine-js .cine__beat--takeover {
    --base-y: -96%;
    transform: translateY(calc(var(--base-y) - var(--lift, 0px) * 3.2));
  }
  .cine-js .cine__beat--takeover .cine__headline { font-size: clamp(34px, 3vw, 50px); }
}

/* Ultra-wide: keep the composition tucked rather than drifting. */
@media (min-width: 1800px) {
  .cine-js .cine__stage > .cine__beats { max-width: 1560px; left: 50%; transform: translateX(-50%); }
}

/* ── FULL-BLEED MODE — portrait / narrow / short ───────────── */
@media not all and (min-aspect-ratio: 5/4) and (min-width: 1024px) {
  .cine { --cine-track: 420svh; }

  .cine-js .cine__beat {
    left: 20px; right: 20px;
    width: auto; max-width: none;
    justify-items: start;
  }
  /* Copy is anchored where the subject is not, per beat. */
  .cine-js .cine__beat { transform: translateY(var(--lift, 0px)); }
  .cine-js .cine__beat[data-anchor="top"]    { top: max(84px, 12svh); }
  .cine-js .cine__beat[data-anchor="bottom"] { bottom: max(96px, 13svh); }
  /* Same title/subtitle stacking problem, solved with a real offset. */
  .cine-js .cine__beat[data-stack="below"][data-anchor="top"] { top: max(232px, 30svh); }
  .cine-js .cine__beat--overture {
    top: 50%; bottom: auto;
    transform: translateY(calc(-50% + var(--lift, 0px)));
    text-align: center; justify-items: center;
  }
  /* Full-bleed has no dark room behind the copy, so the scrim has to do
     all the work rather than merely adding depth. */
  /* A generous vertical bleed so the scrim's own edge never resolves as a
     rectangle against the film — it has to read as shadow, not as a box. */
  .cine-js .cine__beat::before {
    inset: -74px -24px;
    background: radial-gradient(120% 92% at 50% 50%,
      rgba(28,15,7,.94) 0%, rgba(28,15,7,.86) 40%, rgba(28,15,7,.52) 68%,
      rgba(28,15,7,.18) 86%, transparent 100%);
  }
  .cine-js .cine__headline { font-size: clamp(26px, 7vw, 38px); }
  .cine-js .cine__wordmark { font-size: clamp(44px, 13vw, 74px); }
  .cine-js .cine__title    { font-size: clamp(34px, 10vw, 52px); }
  .cine-js .cine__body     { font-size: 14px; max-width: 34ch; }
  /* Placed after the 34ch cine__body rule so its max-width actually wins —
     the card's own 20px side margins are the real limit. */
  .cine-js .cine__ingredients { font-size: 11.5px; max-width: none; }

  /* The finale card carries the most content of any beat; tighten its
     rhythm and controls so the whole card clears the 77px sticky header
     on short phones (verified at 553svh, the iPhone SE/8 floor). */
  .cine-js .cine__beat { gap: 10px; }
  .cine-js .cine__cta .btn { padding: 13px 16px; font-size: 11px; letter-spacing: .11em; }

  /* Takeover card (patch 4): slides down from the top edge and sits just
     under the 77px sticky header — higher than the standard 84/12svh
     anchor but never behind the bar. */
  .cine-js .cine__beat--takeover[data-anchor="top"] { top: max(84px, 10svh); }
  .cine-js .cine__beat--takeover { transform: translateY(calc(var(--lift, 0px) * -3.2)); }
  .cine-js .cine__beat--takeover .cine__headline { font-size: clamp(30px, 8.4vw, 44px); }
}

/* Very short viewports get the static stack — a 100svh stage with
   copy over it is unusable below this. */
@media (max-height: 480px) {
  .cine-js .cine { height: auto; }
  .cine-js .cine__stage { position: static; height: auto; display: block; }
  .cine-js .cine__room, .cine-js .cine__bloom, .cine-js .cine__plate,
  .cine-js .cine__poster, .cine-js .cine__skip, .cine-js .cine__loading { display: none; }
  .cine-js .cine__beats { position: static; padding: 64px var(--gutter); }
  /* !important because the scrubber writes inline opacity/transform on
     every beat; without it these fallbacks render a stack of invisible
     elements. This state is reachable by rotating a phone to landscape. */
  .cine-js .cine__beat {
    position: static; opacity: 1 !important; transform: none !important; visibility: visible !important;
    width: auto; max-width: 620px; margin: 0 auto 56px; pointer-events: auto;
  }
  .cine-js .cine__beat::before { display: none; }
  .cine-js .cine__beat--silent { display: none; }
  .cine-js .cine__plate-still { display: block; }
}

/* ── Reduced motion — hard opt out of the whole mechanism ──── */
@media (prefers-reduced-motion: reduce) {
  .cine, .cine-js .cine { height: auto; }
  .cine-js .cine__stage { position: static; height: auto; display: block; }
  .cine-js .cine__room, .cine-js .cine__bloom, .cine-js .cine__plate,
  .cine-js .cine__poster, .cine-js .cine__skip, .cine-js .cine__loading { display: none; }
  .cine-js .cine__beats {
    position: static; display: flex; flex-direction: column;
    gap: 72px; max-width: 1080px; margin: 0 auto; padding: 72px var(--gutter) 96px;
  }
  .cine-js .cine__beat {
    position: static; opacity: 1 !important; transform: none !important; visibility: visible !important;
    width: auto; max-width: none; pointer-events: auto;
  }
  .cine-js .cine__beat::before { display: none; }
  .cine-js .cine__plate-still { display: block; }
  .cine-js .cine__beat--silent { display: none; }
}
