/* =========================================================================
   PUMP KITCHEN — scroll-driven hero
   A tall scroll track with a sticky, viewport-filling stage. Three canvases
   play WebP frame sequences decoded from the source films:
     centre       DINNER     protein bolognese   (3:4 portrait)
     bottom-left  BREAKFAST  high-protein plate  (1:1)
     bottom-right LUNCH      green curry         (1:1)

   Seamlessness: the two square films were shot on a pure-white cyc, so they
   sit on a white stage with no visible edge. The centre film was shot on a
   pale blue-grey backdrop, so a matching --haze glow is painted behind it and
   the canvas edges are feathered into that glow.
   ========================================================================= */

/* The scroll track. Its length is how much scrolling the whole plating
   animation costs — 97 frames are mapped across it, so this is a pace dial,
   not a layout value. Kept short enough that the hero resolves in about a
   scroll and a half rather than making the visitor grind through it. */
.hero {
  position: relative;
  height: 240vh;
  background: var(--paper);
}

.hero__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  overflow: hidden;
  display: grid;
  place-items: center;
  isolation: isolate;
}

/* the backdrop that the centre film dissolves into */
/* All three films now sit on paper white — the centre by way of its alpha
   channel, the two squares because they were shot on a white cyc. So the
   stage stays white and only carries a whisper of studio light behind the
   hero dish. Anything stronger would expose the square films as rectangles. */
.hero__glow {
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    radial-gradient(46% 44% at 50% 48%, rgba(241, 242, 238, 0.82) 0%, rgba(255, 255, 255, 0) 74%),
    radial-gradient(78% 40% at 50% 108%, rgba(243, 244, 240, 0.7) 0%, rgba(255, 255, 255, 0) 70%);
  opacity: var(--glow-o, 1);
  transition: opacity 0.4s linear;
}

/* ---------- headline ---------- */
.hero__type {
  position: absolute;
  z-index: 3;
  /* Sits high and compact so the plates below can be large without the centre
     plate's bleed halo reaching up into the subline. */
  top: clamp(40px, 5.5vh, 100px);
  left: 0;
  right: 0;
  padding-inline: var(--gutter);
  text-align: center;
  pointer-events: none;
  transform: translate3d(0, var(--type-y, 0px), 0) scale(var(--type-s, 1));
  opacity: var(--type-o, 1);
  will-change: transform, opacity;
}
.hero__eyebrow {
  justify-content: center;
  margin-bottom: clamp(14px, 1.8vh, 24px);
  color: var(--accent-mid);
}
/* Sized against BOTH axes. The vh term is what stops the headline from
   colliding with the centre plate on short laptop viewports. */
.hero__h1 {
  color: var(--ink);
  font-size: clamp(30px, min(8vw, 9vh), 104px);
  text-wrap: balance;
}
.hero__h1 em {
  font-style: normal;
  color: transparent;
  -webkit-text-stroke: 1.4px var(--accent-mid);
}
.hero__sub {
  margin-top: clamp(10px, 1.3vh, 18px);
  font-size: clamp(13px, 1.15vw, 16px);
  font-weight: 350;
  letter-spacing: 0.02em;
  color: var(--t-mid);
  max-width: 44ch;
  margin-inline: auto;
}

/* ---------- plates ---------- */
.hero__plates {
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
}

/* Plates fade in once the frames are decoded, NOT on scroll — landing on the
   page must already show all three meals. Scroll then drives frames+parallax.
   Only opacity transitions; transform is written every rAF by the engine. */
.plate {
  position: absolute;
  margin: 0;
  opacity: 0;
  transform: translate3d(var(--p-x, 0px), var(--p-y, 0px), 0) scale(var(--p-s, 1));
  transition: opacity 1.1s var(--e-out);
  will-change: transform, opacity;
}
.hero.is-ready .plate { opacity: 1; }
.hero.is-ready .plate--centre { transition-delay: 60ms; }
.hero.is-ready .plate--left { transition-delay: 260ms; }
.hero.is-ready .plate--right { transition-delay: 400ms; }
.plate__canvas {
  display: block;
  width: 100%;
  height: auto;
  /* feather the outer edge so no film ever reads as a rectangle */
  -webkit-mask-image:
    linear-gradient(to right, transparent 0, #000 7%, #000 93%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 5%, #000 95%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-image:
    linear-gradient(to right, transparent 0, #000 7%, #000 93%, transparent 100%),
    linear-gradient(to bottom, transparent 0, #000 5%, #000 95%, transparent 100%);
  mask-composite: intersect;
}

/* The centre film needs a much deeper feather than the two white-cyc films,
   because its backdrop is tinted and would otherwise read as a grey box.
   Generous on the sides (the explosion is centred), gentler top and bottom
   where the basil and the bowl sit close to the frame edge. */
/* The centre film was shot on a lit blue-grey cyc rather than the white one
   the square films use, and its lighting drifts across the sequence, so it
   can neither sit on white nor be keyed cleanly. The engine instead grows its
   canvas by 20% a side and fills that margin with the frame's own stretched
   edge pixels (see `bleed` in hero.js). All this has to do is dissolve that
   margin: the gradient is opaque across the whole picture area and fades only
   through the bled surround, so the backdrop ends in nothing rather than in
   an edge. The plate box stays the picture's 3:4 so the tag sits under the
   food, not under the bleed. */
.plate--centre { aspect-ratio: 3 / 4; }

.plate--centre .plate__canvas {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 128%;              /* 1 + 2 x 0.14 bleed */
  /* the global `canvas { max-width: 100% }` reset would clamp that 152% back
     to the box and cut the bleed off entirely, shrinking the dish by a third */
  max-width: none;
  height: auto;
  transform: translate(-50%, -50%);
  /* opaque out to the edge of the picture (0.5 / 0.76 = 0.66 of the radius),
     then a long three-stop falloff through the bled surround */
  -webkit-mask-image: radial-gradient(50% 50% at 50% 50%,
    #000 0%, #000 67%, rgba(0, 0, 0, 0.5) 83%, rgba(0, 0, 0, 0) 100%);
  -webkit-mask-composite: source-over;
  mask-image: radial-gradient(50% 50% at 50% 50%,
    #000 0%, #000 67%, rgba(0, 0, 0, 0.5) 83%, rgba(0, 0, 0, 0) 100%);
  mask-composite: add;
}

/* centre — the hero film */
.plate--centre {
  --p-w: clamp(200px, min(34vh, 28vw), 404px);
  width: var(--p-w);
  left: 50%;
  top: 61%;
  margin-left: calc(var(--p-w) / -2);
  transform: translate3d(var(--p-x, 0px), calc(-50% + var(--p-y, 0px)), 0) scale(var(--p-s, 1));
  z-index: 3;
}

/* The two corner films. Sized against both axes for the same reason the
   centre is: they are anchored to the bottom, so growing them pushes their
   top edge up towards the headline on short viewports. Kept close in scale to
   the centre so the three read as one composition rather than a hero plus two
   thumbnails. */
.plate--left,
.plate--right {
  /* These are 1:1 and sit low, so they have far more vertical headroom than
     the 3:4 centre — they only have to clear the subline, not carry a halo up
     into it. Sized generously off that headroom: anything tighter leaves the
     stage looking empty on a wide screen. Still kept under the centre's
     canvas width so the hero plate stays dominant. */
  --p-w: clamp(180px, min(44vh, 24vw), 420px);
  width: var(--p-w);
  bottom: clamp(64px, 10vh, 124px);
  z-index: 2;
}
.plate--left { left: clamp(12px, 4.5vw, 92px); }
.plate--right { right: clamp(12px, 4.5vw, 92px); }

/* ---------- meal tag ---------- */
.plate__tag {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  top: calc(100% - 2px);
  text-align: center;
  white-space: nowrap;
}
.plate__label {
  display: block;
  font-family: var(--f-display);
  font-size: clamp(11px, 1.15vw, 16px);
  letter-spacing: 0.19em;
  text-transform: uppercase;
  color: var(--ink);
  line-height: 1;
}
.plate__desc {
  display: block;
  margin-top: 6px;
  font-size: clamp(9.5px, 0.72vw, 11.5px);
  font-weight: 400;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--t-lo);
  line-height: 1;
}
.plate__rule {
  display: block;
  width: 22px;
  height: 1px;
  margin: 9px auto 0;
  background: var(--accent);
  transform: scaleX(var(--rule-s, 0));
  transform-origin: center;
  transition: transform 0.7s var(--e-out);
}
.hero.is-ready .plate__rule { --rule-s: 1; }
.hero.is-ready .plate--centre .plate__rule { transition-delay: 420ms; }
.hero.is-ready .plate--left .plate__rule { transition-delay: 620ms; }
.hero.is-ready .plate--right .plate__rule { transition-delay: 760ms; }

/* the centre plate carries a slightly louder tag */
.plate--centre .plate__label { font-size: clamp(13px, 1.5vw, 21px); }

/* ---------- scroll hint ---------- */
.hero__hint {
  position: absolute;
  z-index: 5;
  bottom: clamp(18px, 3vh, 34px);
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--t-lo);
  opacity: var(--hint-o, 1);
  transition: opacity 0.45s linear;
  pointer-events: none;
}
.hero__hint-line {
  width: 1px;
  height: 34px;
  background: linear-gradient(to bottom, var(--accent), transparent);
  position: relative;
  overflow: hidden;
}
.hero__hint-line::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--accent-mid);
  animation: hint-run 2.1s var(--e-io) infinite;
}
@keyframes hint-run {
  0% { transform: translateY(-100%); }
  60%, 100% { transform: translateY(100%); }
}

/* ---------- corner meta ---------- */
.hero__meta {
  position: absolute;
  z-index: 5;
  bottom: clamp(18px, 3vh, 34px);
  font-size: 10px;
  font-weight: 450;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--t-lo);
  opacity: var(--hint-o, 1);
  transition: opacity 0.45s linear;
}
.hero__meta--l { left: var(--gutter); }
.hero__meta--r { right: var(--gutter); text-align: right; }

/* ---------- loader ---------- */
.hero__loader {
  position: absolute;
  inset: 0;
  z-index: 20;
  display: grid;
  place-items: center;
  background: var(--paper);
  transition: opacity 0.8s var(--e-out), visibility 0.8s;
}
.hero__loader.is-done { opacity: 0; visibility: hidden; }
.loader__inner { display: flex; flex-direction: column; align-items: center; gap: 20px; }
.loader__mark { height: 26px; width: auto; opacity: 0.9; }
.loader__bar {
  width: clamp(140px, 20vw, 220px);
  height: 1.5px;
  background: var(--rule);
  overflow: hidden;
  border-radius: 2px;
}
.loader__fill {
  height: 100%;
  width: 100%;
  background: var(--accent-mid);
  transform: scaleX(var(--load, 0));
  transform-origin: left;
  transition: transform 0.3s linear;
}
.loader__pct {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--t-lo);
}

/* =========================================================================
   Small viewports — keep the triangle composition but tighten it.
   ========================================================================= */
/* These overrides must keep a height term. Sizing the plate on width alone
   lets it grow into the headline on any short viewport in the band — a
   half-width window on a 1536px laptop, a Fold, a phone in landscape — and
   the plate is 3:4, so every pixel of width costs 1.33 of height. */
@media (max-width: 860px) {
  .hero { height: 215vh; }
  .plate--centre { --p-w: clamp(168px, min(44vw, 26vh), 290px); top: 58%; }
  .plate--left,
  .plate--right { --p-w: clamp(104px, min(27vw, 17vh), 168px); bottom: clamp(70px, 12vh, 116px); }
  .hero__meta { display: none; }
}

@media (max-width: 560px) {
  .hero { height: 195vh; }
  .hero__type { top: clamp(72px, 9vh, 110px); }
  .plate--centre { --p-w: min(50vw, 26vh); top: 60%; }
  .plate--left,
  .plate--right { --p-w: min(30vw, 17vh); bottom: clamp(66px, 11vh, 100px); }
  .plate--left { left: 10px; }
  .plate--right { right: 10px; }
  .plate__desc { display: none; }
}

/* The corner meta is anchored to the same baseline as the meal tags, so on a
   short viewport the LUNCH tag runs into it. Height, not just width, decides. */
@media (max-height: 880px) {
  .hero__meta { display: none; }
}

/* Wide but short (common laptops: 1366x768, 1280x720, and down). These sit
   above the 860px width breakpoint, so they take the desktop rule — but at
   these heights the headline block, the pinned plate, its meal tag and the
   scroll hint cannot all fit at desktop scale. The plate's tag would run into
   the hint at the bottom while its bleed halo runs into the subline at the
   top. So here the headline lifts, the plate shrinks and rides higher, and
   the corners stay large. Covers up to 880px tall so the tight bottom
   clearance the desktop rule's 64% push produces only ever happens under this
   rule, which rides the plate higher and sidesteps it. */
@media (min-width: 861px) and (max-height: 880px) {
  .hero__type { top: clamp(40px, 5vh, 84px); }
  .plate--centre { --p-w: clamp(168px, min(27vh, 26vw), 330px); top: 57%; }
  .plate--left,
  .plate--right { --p-w: clamp(150px, min(42vh, 22vw), 380px); }
}

/* A short landscape viewport — a phone rotated, or a very short desktop window
   — cannot hold a pinned 3:4 plate plus a headline plus two corner plates in
   this little height without the plate crowding the headline. Below 620px tall
   stop pinning and stack instead, the same composition the reduced-motion and
   no-JS paths use. (Sits after the short-viewport rule above, so it wins for
   anything that matches both.) */
@media (orientation: landscape) and (max-height: 620px) {
  .hero { height: auto; }
  .hero__stage {
    position: relative;
    height: auto;
    padding-block: clamp(96px, 16vh, 150px) clamp(56px, 10vh, 90px);
    display: block;
  }
  .hero__type { position: relative; top: 0; transform: none; opacity: 1; }
  .hero__plates {
    position: relative;
    inset: auto;
    margin-top: clamp(28px, 6vh, 56px);
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: center;
    gap: clamp(30px, 7vw, 76px);
    padding-inline: var(--gutter);
    padding-bottom: 56px;
  }
  .plate {
    position: relative;
    inset: auto;
    left: auto;
    right: auto;
    top: auto;
    bottom: auto;
    margin: 0;
    transform: none;
  }
  .plate--centre { order: 2; --p-w: clamp(150px, 22vw, 250px); }
  .plate--left { order: 1; }
  .plate--right { order: 3; }
  .plate--left,
  .plate--right { --p-w: clamp(96px, 14vw, 160px); }
  .hero__hint { display: none; }
}

/* =========================================================================
   Static fallback — reduced motion, or no JS.
   The stage stops pinning and the three films become a still composition.
   ========================================================================= */
.hero.is-static { height: auto; }
/* in the static state there is no sequence to wait for, so the loader must not
   sit over the composition — this is what the no-JS visitor sees */
.hero.is-static .hero__loader { display: none; }
.hero.is-static .hero__stage {
  position: relative;
  height: auto;
  min-height: 0;
  padding-block: clamp(120px, 18vh, 200px) clamp(80px, 12vh, 140px);
  display: block;
}
.hero.is-static .hero__type { position: relative; top: 0; transform: none; opacity: 1; }
.hero.is-static .hero__plates {
  position: relative;
  inset: auto;
  margin-top: clamp(36px, 6vh, 72px);
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: center;
  gap: clamp(28px, 6vw, 76px);
  padding-inline: var(--gutter);
  padding-bottom: 64px;
}
.hero.is-static .plate {
  position: relative;
  inset: auto;
  left: auto;
  right: auto;
  top: auto;
  bottom: auto;
  margin: 0;
  opacity: 1;
  transform: none;
}
.hero.is-static .plate--centre { order: 2; --p-w: clamp(200px, 30vw, 330px); }
.hero.is-static .plate--left { order: 1; }
.hero.is-static .plate--right { order: 3; }
.hero.is-static .plate--left,
.hero.is-static .plate--right { --p-w: clamp(130px, 19vw, 215px); }
.hero.is-static .hero__hint,
.hero.is-static .hero__meta { display: none; }

@media (prefers-reduced-motion: reduce) {
  .hero__hint-line::after { animation: none; }
}
