/* ==========================================================================
   Gully Bowls — Top Bar + Header
   Built from Figma design context for QjTGvSIwub5sbzyJxVOqWp (node 136:29).
   Reference canvas: 1920px. Fluid clamp() scaling ties every size back to
   that reference so the layout is exact at 1920 and scales proportionally
   down to the explicit breakpoints below.
   ========================================================================== */

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html, body, ul, li { margin: 0; padding: 0; }
html { scroll-behavior: smooth; overflow-x: hidden; }
/* body { overflow-x: hidden; } */
ul { list-style: none; }
a { text-decoration: none; color: inherit; }
p{
  margin: 0;
}
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; padding: 0; }
img { display: block; max-width: 100%; }
svg image { image-rendering: -webkit-optimize-contrast; }

/* ---------- Design tokens ---------- */
:root{
  /* Colors */
  --c-green:        #054E1B;
  --c-pink-light:    #FF98C7;
  --c-pink-medium:   #FE6CAF;
  --c-mint:          #C6DB49;
  --c-sky:           #A2D9F6;
  --c-limestone:     #FBEEC4;
  --c-white:         #FFFFFF;
  --c-black:         #0B0B0B;

  /* Shadows */
  --text-shadow-lg:  8px 8px 0px var(--c-black);
  --text-shadow-sm:  4px 4px 0px var(--c-black);
  --shadow-solid:    8px 8px 0px 0px var(--c-black);
  --shadow-solid-lg: 12px 12px 0px 0px var(--c-black);

  /* Fluid type scale — clamp(mobile-min, vw-at-1920-ratio, figma-px) */
  --fs-hero: clamp(2.75rem, 6.25vw, 7.5rem);   /* 120px */
  --fs-d80:  clamp(2.5rem, 4.1667vw, 5rem);    /* 80px  */
  --fs-d72:  clamp(2.25rem, 3.75vw, 4.5rem);   /* 72px  */
  --fs-60:   clamp(2rem, 3.125vw, 3.75rem);    /* 60px  */
  --fs-48:   clamp(1.75rem, 2.5vw, 3rem);      /* 48px  */
  --fs-30:   clamp(1.375rem, 1.5625vw, 1.875rem); /* 30px */
  --fs-24:   clamp(1.125rem, 1.25vw, 1.5rem);  /* 24px  */
  --fs-18:   clamp(0.9375rem, 0.9375vw, 1.125rem); /* 18px */
  --fs-16:   clamp(0.8125rem, 0.8333vw, 1rem);     /* 16px */
  --fs-14:   clamp(0.8125rem, 0.7292vw, 0.875rem); /* 14px */

  /* Fluid spacing scale — same clamp(mobile-min, vw-at-1920-ratio,
     figma-px) pattern as the type scale above. Several section paddings
     and card/heading gaps were literal px/rem values equal to their
     desktop figure with no reduction until the ≤767 breakpoint — flat
     at the DESKTOP size through the whole tablet/laptop range instead
     of scaling down. These replace those literals so every width in
     between genuinely differs, not just the two breakpoint endpoints. */
  --space-3xl: clamp(4rem, 6.25vw, 7.5rem);        /* 120px section padding-block */
  --space-2xl: clamp(2rem, 3.125vw, 3.75rem);      /* 60px section/card gaps */
  --space-xl:  clamp(1.75rem, 2.5vw, 3rem);        /* 48px card padding/gaps */
  --space-lg:  clamp(1.25rem, 1.5625vw, 1.875rem); /* 30px content gaps */
  --space-md:  clamp(1rem, 1.25vw, 1.5rem);        /* 24px small gaps */

  /* Section side padding, per explicit breakpoints. Beyond the 1920
     reference, grow the padding to hold content at its already-approved
     1920 width (1920 - 2*240 = 1440) instead of letting sections stretch
     edge-to-edge on ultrawide/large-desktop monitors. */
  --pad-inline: max(240px, (100vw - 1440px) / 2);
  --header-h: 125px; /* 35px ticker + 90px nav */

  --ease-soft: cubic-bezier(.16,.84,.44,1);
}

@media (max-width: 1919px){
  :root{ --pad-inline: clamp(80px, 12.5vw, 160px); }
}
@media (max-width: 1023px){
  :root{ --pad-inline: 48px; --header-h: 122px; }
}
@media (max-width: 767px){
  :root{ --pad-inline: 20px; --header-h: 108px; }
}

html{ font-size: 16px; }

body{
  font-family: 'Rubik', system-ui, sans-serif;
  background: var(--c-limestone);
  color: var(--c-black);
  line-height: 1.35;
}

/* ==========================================================================
   HEADER: ticker + sticky nav
   ========================================================================== */
.site-header{
  position: sticky;
  top:0; left:0; right:0;
  z-index: 200;
  width:100%;
}

.ticker-wrap{
  height:35px;
  background: var(--c-mint);
  overflow:hidden;
  display:flex;
  align-items:center;
}
.ticker-track{ display:flex; width:max-content; animation: ticker-scroll 55s linear infinite; gap: 48px; }
.ticker-set{ display:flex; align-items:center; gap:48px; flex-shrink:0; }
.ticker-item{
  font-weight:500; font-size:15px; letter-spacing:-0.03em;
  color: var(--c-green); white-space:nowrap;
}
.ticker-glyph{ width:22px; height:18px; display:flex; }
.ticker-glyph img{ width:100%; height:100%; }
@keyframes ticker-scroll{ from{ transform:translateX(0); } to{ transform:translateX(-50%); } }

.navbar{
  height:90px;
  background: var(--c-green);
  border-bottom: 4px solid var(--c-pink-medium);
  display:flex;
  align-items:center;
  justify-content:space-between;
  padding-inline: var(--pad-inline);
  gap: 24px;
}
.logo{ display:block; flex-shrink:0; }
.logo-svg{ width: clamp(150px, 11.5vw, 220px); height:auto; display:block; }

.nav-links{ display:flex; align-items:center; gap:48px; }
.nav-links a{
  font-weight:500; font-size: var(--fs-24); letter-spacing:-0.03em;
  color: var(--c-white);
  position:relative;
  transition: color .25s var(--ease-soft);
}
.nav-links a::after{
  content:""; position:absolute; left:0; right:0; bottom:-6px; height:2px;
  background: var(--c-mint); transform:scaleX(0); transform-origin:left;
  transition: transform .3s var(--ease-soft);
}
.nav-links a:hover{ color: var(--c-mint); }
.nav-links a:hover::after{ transform:scaleX(1); }

.nav-toggle{ display:none; position:relative; flex-direction:column; gap:5px; width:25px; z-index:60; }
.nav-toggle span{ height:3px; background:var(--c-white); border-radius:2px; transition: transform .3s var(--ease-soft), opacity .3s; }
.nav-toggle[aria-expanded="true"] span:nth-child(1){ transform: translateY(8px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2){ opacity:0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3){ transform: translateY(-8px) rotate(-45deg); }

/* ---------- Nav CTA button: real Figma polygon/blob vector shapes ----------
   Two stacked exported SVGs (shadow + face), not a CSS approximation. */
.btn{
  position:relative;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:10px;
  min-width: 192px;
  height: 50px;
  padding: 0 30px;
  isolation:isolate;
}
.btn::before, .btn::after{
  content:"";
  position:absolute; inset:0;
  background-repeat:no-repeat;
  background-size:contain;
  background-position:center;
  z-index:-1;
  transition: transform 1s var(--ease-soft);
}
.btn::before{
  background-image:url('../assets/svg/btn-shadow.svg');
  transform: translate(4px,6px);
}
.btn::after{
  background-image:url('../assets/svg/btn-face.svg');
}
.btn__label{
  position:relative;
  font-weight:500; font-size: var(--fs-18); letter-spacing:-0.03em;
  color: var(--c-green); text-align:center; white-space:nowrap;
  transition: transform 1s var(--ease-soft);
}
.btn:hover::after, .btn:hover .btn__label, .btn:hover .btn__icon{ transform: translate(3px,4px); }
.btn:active::after, .btn:active .btn__label, .btn:active .btn__icon{ transform: translate(4px,6px); }
.btn__icon{ width:20px; height:20px; position:relative; transition: transform 1s var(--ease-soft); }

/* ==========================================================================
   HERO
   ========================================================================== */
.hero{
  position:relative;
  /* min-height: 700px; */
  min-height: auto;
  display:flex;
  align-items:center;
  overflow:hidden;
   scroll-margin-top: var(--header-h);
}
.hero__bg{
  position:absolute; inset:0;
  background-position:center;
  background-size: contain;
}
.hero__overlay{
  position:absolute; inset:0;
  background: rgba(5,78,27,0.5);
}
.hero__inner{
  position:relative;
  width:100%;
  display:flex;
  flex-direction:column;
  align-items:center;
  justify-content:flex-start;
  gap:34px;
  /* padding: var(--header-h) var(--pad-inline) 7.5rem; */
  padding: 0 var(--pad-inline) 0;
  /* min-height: 700px; */
  text-align:center;
}
.hero__title{
  position:relative;
  z-index:1;
  font-weight:700; font-size: clamp(2rem, 5vw, 78px); line-height:1;
  letter-spacing:.06em; text-transform:uppercase;
  color: var(--c-pink-light);
  -webkit-text-stroke-width: medium;
  -webkit-text-stroke-color: var(--c-pink-medium);
  margin-top: .56em;
  margin-bottom: 0;
}
.hero__title::before{
  content: attr(data-text);
  position:absolute; inset:0;
  transform: translate(6px,5px);
  z-index:-1;
  color: var(--c-black);
  -webkit-text-stroke-width: 3px;
  -webkit-text-stroke-color: var(--c-black);
}
.hero__bowl{ width:min(56.6vw, 1100px); z-index: 1; position: relative;}
.hero__bowl img{ width:100%; height:auto; }
.hero__tagline{
  font-weight:400; font-size: clamp(1rem, 1.0417vw, 1.25rem); line-height:1.35;
  color:var(--c-white); max-width:650px;
}

.star{ position:absolute; width:9.5vw; max-width:190px; min-width:80px; aspect-ratio:1; z-index:-1; }
.star img{ width:100%; height:100%; }
/* .star--1{ left:-2%; top:70%; animation: star-spin 15s linear infinite; } */
.star--1{ left:-5%; top:10%; animation: star-spin 15s linear infinite; }
/* .star--2{ left:90%; top:0%; animation: star-spin 15s linear infinite; } */
.star--2{ left:90%; top:45%; animation: star-spin 15s linear infinite; }
@keyframes star-spin{ from{ transform:rotate(0deg); } to{ transform:rotate(-360deg); } }

/* --wave-h is the pattern's own rendered height, shared by both the
   svg-sizing rule and the top-offset rule below so the two always stay
   proportional to EACH OTHER, not just coincidentally equal at the
   breakpoint extremes. 32px at 1920px+ is the exact original desktop
   value (unchanged) — everything below that scales down continuously
   instead of jumping. */
.wave-strip{
  --wave-h: clamp(16px, 4vw, 32px);
  position:absolute; left:50%; transform:translateX(-50%);
  width:100%;
  overflow:hidden;
  pointer-events:none; z-index:3;
  line-height:0;
}
.wave-strip img{ width:100%; height:auto; }
/* The inline <svg> tooth pattern (dish-spotlight top+bottom edges) is a
   fixed-ratio asset — 2349:32 (~73:1) — repeating the same tooth motif
   ~16.6 times across its own width. Forcing width:100% while height
   stayed pinned at the svg's own height="32" attribute (the previous
   approach: preserveAspectRatio="xMidYMid slice" on the element) kept
   every tooth at a constant ~141px ABSOLUTE size regardless of
   viewport — correct at 1920px (where the desktop reference already
   only shows a cropped ~82% of the full repeat sequence, 1920 of 2349
   units, at native 1:1 scale), but at 375px that same fixed 141px tooth
   is a huge fraction of the whole strip — only ~2-3 fit, reading as a
   completely different, much chunkier pattern than desktop's dense
   repeat, with a visible gap of the hero's own background opening up
   below the strip's now-much-taller relative footprint.
   Overriding BOTH width and height here (beating the svg's own width/
   height attributes, which are lower priority than any CSS) lets the
   svg's rendered size follow --wave-h with width computed from it via
   the browser's normal intrinsic-ratio sizing (from the viewBox, so no
   distortion) — i.e. the WHOLE pattern scales down uniformly together,
   same as it does going from 1920px to any width above ~800px already.
   At 1920px+, --wave-h caps at 32px — the exact original height — so
   the svg's rendered width is exactly 2349px, IDENTICAL to the old
   slice behavior there: the desktop look is pixel-for-pixel unchanged.
   Below that, the whole svg (teeth included) shrinks together, so more
   of the SAME small teeth fit across the same section width — matching
   how the pattern actually reads on desktop, just smaller — rather than
   holding a small number of oversized teeth at a fixed physical size.
   The svg's own rendered width still generally exceeds the section's
   width (2349 vs. any real viewport up to ~2200px), so it's centered
   and cropped by .wave-strip's own overflow:hidden above — the same
   "more pattern than we need, take a centred window of it" principle as
   before, just applied uniformly instead of holding teeth at native
   scale. */
.wave-strip svg{
  display:block;
  width:auto;
  height: var(--wave-h);
  position:relative; left:50%; transform:translateX(-50%);
}
.wave-strip--bottom{ bottom: -2px; }
/* Proportional to --wave-h (not an independently-tuned viewport-width
   formula) so the amount of the pattern poking above the section scales
   exactly with the pattern's own current size at every width, not just
   at the two breakpoint extremes. -0.875 matches the original design's
   own overlap ratio (28px of poke on a 32px-tall desktop pattern). Still
   a plain calc() against a CSS variable, not a percentage of
   .dish-spotlight's own JS-measured height — see the note this replaced
   two rounds ago: that dependency was the actual root cause of a
   previous "wave floats too high, gap opens below it" bug (confirmed:
   the offset visibly overshot during the section's own resize-triggered
   height recalculation). --wave-h has no such dependency; it's a pure
   CSS custom property computed synchronously from viewport width. */
.wave-strip--top{ top: calc(var(--wave-h) * -0.875); }
/* Slide changes update --dish-bg once (js/main.js applyBg), and both
   .dish-spotlight's own background-color AND this strip's fill read the
   same variable — but a CSS transition only animates the property it's
   declared on, on the element it's declared on. .dish-spotlight already
   transitions its own background-color over .8s with this exact easing;
   without an equivalent transition declared here, each <path>'s
   fill="var(--dish-bg)" (wave-strip--bottom's fill is a static colour,
   not slide-dependent — only the top strip needs this) jumped to the
   new colour instantly, finishing well before the section's own
   background caught up. Same duration, same easing, same trigger (the
   variable, changed once) — the two now move together exactly. */
.wave-strip--top path{ transition: fill 1000ms cubic-bezier(.65,0,.35,1); }

/* ==========================================================================
   DISH SPOTLIGHT
   ========================================================================== */
.dish-spotlight{
  position:relative;
  background: var(--dish-bg, var(--c-mint));
  transition: background-color 1000ms cubic-bezier(.65,0,.35,1);
  padding: var(--space-3xl) var(--pad-inline);
  display:flex; flex-direction:column; align-items:center;
  gap: var(--space-lg); text-align:center;
  scroll-margin-top: var(--header-h);
}
/* Sunray backdrop as a real <img>, not a CSS background-image. The PNG's
   own ratio (1920/1077, ~1.78) is fixed and must never distort: on tall
   narrow mobile viewports background-size:cover would zoom in hard on
   just the centre circle to fill the container's height, cropping the
   rays down to almost nothing. Instead this always spans the section's
   full width and lets height follow the asset's native ratio, centred —
   the rays stay correctly proportioned and undistorted at every
   breakpoint, and the PNG's own transparent gaps between rays let the
   section's solid --dish-bg colour show through everywhere the image
   doesn't reach (including above/below it on very tall mobile crops),
   so there's never a hard seam or a flat gap.
   The clip lives on its own wrapper (not on .dish-spotlight itself) so
   the bg image can never bleed into the Hero/next section above/below —
   without clipping the tall/narrow desktop image, and without also
   clipping .wave-strip--top, which intentionally sits partly above the
   section's own top edge and must stay visible. */
.dish-spotlight__bg-clip{
  position:absolute; inset:0;
  overflow:hidden;
  z-index:0;
  pointer-events:none;
}
/* Position AND size are both driven by --dish-bg-center-y / --dish-bg-
   min-h, set in js/main.js's syncBgAlign() (called alongside the
   existing syncTrackHeight, on load + resize). The radial centre of this
   PNG sits at its own exact geometric centre, so centring the whole
   image ON a given point makes that point the visual radial centre —
   `top` is set to the ACTIVE bowl photo's own measured vertical centre
   (not 50%: the bowl sits inside the document flow above the title/
   desc/button, so its centre is normally well above the section's true
   midpoint), and `height` is set to the exact minimum needed so the
   image still reaches both the section's top AND bottom edges from that
   off-centre anchor point — width follows automatically (width:auto
   preserves the native ~1.78 ratio, never distorting), cropped left/
   right by .dish-spotlight__bg-clip's own overflow:hidden exactly as
   before. This single rule replaces what used to be two entirely
   different sizing strategies stitched together at a hard 1199/1200px
   breakpoint (width:100% above it, height:100% below) — that split
   existed only to guarantee full coverage, which this now does at every
   width via one continuously-recalculated formula, while ALSO landing
   the image's centre exactly on the bowl instead of the section's own
   midpoint. Falls back to 50%/100% (the old default) before JS has run
   or if it's unavailable, so the background never disappears. */
.dish-spotlight__bg{
  position:absolute;
  top: var(--dish-bg-center-y, 50%);
  /* left:50%; */
  left: 48%;
  transform: translate(-50%,-50%);
  width:auto; height: var(--dish-bg-min-h, 100%);
  max-width:none;
  user-select:none;
}

/* ---- Slide carousel: viewport + 3 visible positions (prev/active/next) ----
   Matches the actual Figma prototype recording (C:\Users\admin\Videos\
   Captures\Animation.mp4, node 294:799): this is a genuine positional
   carousel, not a fade. At any moment 3 of the 4 real dishes are visible
   simultaneously — active (centred, full size), and the previous/next
   dish partially visible and scaled down at each edge — with the 4th
   dish invisible offscreen, ready to enter. Clicking an arrow animates
   every visible slide's position+scale together: the active slide moves
   to the side it's exiting toward while shrinking, the incoming side
   slide moves to centre while growing, and the section background
   crossfades to the new active dish's colour. 1000ms, cubic-bezier(.65,
   0,.35,1) — a symmetric ease-in-out (its own curve, not the shared
   --ease-soft "What's in your bowl" also uses) so the motion accelerates
   and decelerates gently at both ends instead of --ease-soft's steep
   initial slope, which reads as an abrupt launch even though it's a
   continuous curve. The autoplay interval that triggers it is unrelated
   and stays at 3000ms. See js/main.js
   `initDishCarousel` for the index/offset state machine driving which
   real slide currently sits in which position — no clones, no duplicate
   visible slides; each of the 4 real dishes is a single persistent DOM
   node whose position/scale/opacity animates as the active index
   changes. */
/* Wraps the (still full-bleed, still overflow:hidden) .dish-carousel
   together with the prev/next arrows, so the arrows can be positioned
   relative to the carousel's own rendered height (title+photo, via
   syncTrackHeight in main.js) rather than the whole section. Plain
   position:relative + absolutely-positioned arrows — not a flex row
   like the testimonial slider — because .dish-carousel's own negative
   side margins (the full-bleed peek effect) would otherwise fight a
   flex sibling for space. left:0/right:0 land the arrows at this wrap's
   own edges, which sit at the section's normal padded content width
   (not the carousel's full-bleed edge), matching how far from the
   content the testimonial arrows sit. */
.dish-carousel-wrap{ position:relative; width:100%; }
/* Fluid horizontal padding, scoped here (not on the shared .carousel-
   arrow--float class, which the testimonial slider also uses) so this
   only touches dish-spotlight's own arrows. The button's rendered width
   is never set directly — it stays content-driven (icon + padding, via
   .btn's default inline-flex shrink-to-fit), which used to come from two
   DIFFERENT, disconnected mechanisms depending on viewport: a reduced
   `padding:0 16px` override below 1023px (28px icon + 32px = 60px), vs
   .btn's un-reduced default `padding:0 30px` at 1024px and up (28px icon
   + 60px = 88px) — flat 60px, then a hard jump straight to a flat 88px
   exactly at the breakpoint, never fluid on either side. This
   interpolates the padding itself continuously between those same two
   endpoints instead of jumping between them.
   (An earlier version of this fix set an explicit `width` instead —
   that's wrong: once the target width dips below icon+padding's natural
   content size, the icon is a flex item with the default flex-shrink:1,
   so the flex algorithm SHRINKS it to make the box fit rather than
   overflowing, squeezing the 28px chevron down to a few px and making
   it all but invisible in the ~1024–1500px range. Driving the box via
   padding instead keeps sizing purely content-driven, so the icon is
   never a shrinkable flex item fighting its own container.) */
.dish-carousel-wrap > .carousel-arrow--float{
  position:absolute; top:50%; transform:translateY(-50%);
  /* auto: the shared .carousel-arrow--float class also sets an explicit
     width:36px/44px below 1023px/991px for the testimonial slider — a
     lower-specificity rule, but since this compound selector doesn't
     otherwise declare `width` at all it would still win that one
     property and reintroduce the exact same icon-squeeze bug (a fixed
     width smaller than icon+padding's natural content size, floored up
     by min-width but never allowed to actually grow to fit). auto lets
     the box size to its own content (icon + the fluid padding above) at
     every width, which is what the fluid padding above is relying on. */
  width: auto;
  padding-inline: clamp(12px, 7.63px + 1.165vw, 30px);
  z-index:4;
}
.dish-carousel-wrap > .carousel-arrow--left{ left:0; }
.dish-carousel-wrap > .carousel-arrow--right{ left:auto; right:0; }

.dish-carousel{
  /* position:relative; */
  width: calc(100% + 2 * var(--pad-inline));
  margin-inline: calc(-1 * var(--pad-inline));
  overflow:hidden;
}
/* Section's own background treatment (radial burst) — sits once behind
   the carousel's sliding content, not per-slide, so it never moves or
   duplicates. */
.dish-carousel__bg{ position:absolute; inset:0; z-index:0; pointer-events:none; }
.dish-spotlight__bg-anchor{
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);  width: 100%;
  /* width:min(36.3vw,697px); aspect-ratio:697/540; */
}
/* Explicit height: slides are position:absolute (stacked/overlaid, each
   individually transformed to its own slot) so the viewport can't size
   itself from in-flow content the way a normal flex/block layout would.
   Height is measured and set in JS (see `syncTrackHeight` in
   js/main.js) rather than a fixed CSS formula. */
.dish-carousel__track{
  position:relative; z-index:1;
}
.dish-slide{
  position:absolute; inset:0;
  display:flex; flex-direction:column; align-items:center;
  /* Gap between the photo and the title+desc group beneath it. */
  gap: var(--space-md);
  transition: transform 1000ms cubic-bezier(.65,0,.35,1), opacity 1000ms cubic-bezier(.65,0,.35,1);
}
.dish-slide.is-snapping{ transition:none; }
.dish-slide.is-snapping .dish-spotlight__content{ transition:none; }

/* Title + description as one group, directly beneath the photo — matches
   the real fetched Figma structure (node 841:545: photo, then a single
   flex-col group holding title+desc with an 18px gap) instead of the
   title floating separately above the photo with the description a
   static, section-wide element outside the carousel entirely. Fades as
   one unit between active/prev/next (see initDishCarousel's render()) so
   prev/next continue showing the dish photo alone, same as before. */
/* padding-inline here (not on .dish-slide) — .dish-carousel is
   deliberately full-bleed (width:calc(100% + 2*var(--pad-inline)),
   negative margin-inline to match) so the prev/next photos can peek in
   at the section's true edges. .dish-slide inherits that same full
   viewport-edge-to-edge width via inset:0, which is fine for the photo
   (it has its own clamped width, independent of its container) but
   without its own padding this text group could size all the way out to
   the raw viewport width whenever that's narrower than its 44rem
   max-width — i.e. it could reach literally to the screen edge with
   zero breathing room on any viewport under ~750px, which is exactly
   what was happening. Re-applying the section's own --pad-inline here
   keeps the text safely inset at every width without touching the
   carousel's own full-bleed mechanics at all. */
.dish-spotlight__content{
  display:flex; flex-direction:column; align-items:center;
  gap: clamp(10px, 1vw, 18px);
  padding-inline: var(--pad-inline);
  transition: opacity 1000ms cubic-bezier(.65,0,.35,1);
}

.dish-spotlight__title{
  margin:0;
  /* Matches the fetched Figma data for this design (node 841:545): this
     title is meaningfully smaller than the previous design's --fs-d80,
     and reuses the project's existing --fs-30 token exactly rather than
     needing a new one. Colour is a flat green on every slide now — the
     old per-slide data-title-color mechanism is unused here (no slide
     sets that attribute), left in main.js only because it's harmless
     to keep supporting. */
  font-weight:800; font-size: var(--fs-30); letter-spacing:-.03em;
  text-transform:uppercase; color: var(--c-green);
  position:relative; z-index:2;
}

.dish-spotlight__frame-wrap{ position:relative; display:flex; align-items:center; justify-content:center; }
/* The dish photo itself (Slide1-7.png) — a transparent-background cutout.
   The highlight badge (Tag1-7.png, see .dish-tag below) used to be baked
   into a corner of this same canvas; it's since been re-exported as its
   own separate asset per slide and is composited back on top here instead,
   so object-fit:contain is kept (not cover, which would crop toward the
   fixed box and cut into the dish itself on slides with a taller natural
   ratio) even though badge-clipping is no longer the reason for it. */
/* One continuous fluid width from mobile to the approved 1920px desktop
   value (513px = 26.7vw at 1920), replacing what used to be two separate
   formulas stitched together at a hard 1023/1024px breakpoint: min(26.7vw,
   513px) above it, min(60vw,420px) below. Those two curves didn't meet —
   the photo held flat at 420px from ~700px up to 1023px (60vw capping
   out), then FELL to 273px at 1024px (26.7vw at that width), a ~150px
   inverse jump exactly at the breakpoint (narrower viewport rendering a
   BIGGER photo than a slightly wider one). This linear interpolation
   (170px at 375px → 513px at 1920px) removes the seam entirely: every
   width in between now sizes continuously, with no plateau and no crash.
   The mobile end (170px, ~45% of a 375px viewport) was lowered from an
   earlier 225px (~60%) pass — at 225px the photo sat only ~3px from the
   prev/next arrows either side (dish-carousel-wrap's own content width
   at 375px is 335px; centred, a 225px photo leaves just 55px each side,
   and the 52px-wide arrow fills nearly all of that), reading as the bowl
   overlapping/crowding the arrows rather than sitting inside its own
   composition with real breathing room. */
.dish-photo{ width:clamp(170px, 86.75px + 22.2vw, 513px); aspect-ratio:513/480; flex-shrink:0; }
.dish-photo img{ width:100%; height:100%; object-fit:contain; }
/* Same width formula as the shared .dish-photo above (confirmed against
   the fetched node's own ~513px/26.7vw container) — every slide's photo
   renders at one consistent size regardless of active/prev/next slot
   (per initDishCarousel's own SLOTS comment: "no scale here, only
   position/opacity changes between slots"), so this only needs to
   correct the aspect-ratio to the new asset's real 573/540 shape. */
.dish-photo--main{
  width:clamp(170px, 86.75px + 22.2vw, 513px);
  aspect-ratio:573/540;
}
/* Highlight badge (Tag1-7.png — one per slide, same 187x140 art at a
   uniform ratio), reintroduced as its own element after being removed
   from the bowl photos themselves. Sized as a % of .dish-spotlight__frame-
   wrap rather than a separate clamp(): frame-wrap has no width of its own
   (flex, shrink-to-fit around the single .dish-photo child), so it's
   exactly the photo's own box at every breakpoint — a % width here tracks
   .dish-photo's fluid clamp() automatically, keeping the same tag-to-bowl
   proportion from 375px up to 1920px with no separate formula to keep in
   sync. Positioned to sit over the photo's own top-left corner (a small
   negative offset, not fully outside it) — same corner on every slide, so
   the badge is deterministic per dish rather than alternating and needing
   a second per-slide position rule. z-index above the photo (both are
   inside the same stacking context as the title via .dish-slide), below
   nothing else in this section. */
.dish-tag {
  position: absolute;
  top: 0;
  right: 2%;
  width: 27%;
  height: auto;
  z-index: 3;
  pointer-events: none;
  transform-origin: center;
  transform: scale(0.65);
  transition: all 500ms ease;
  transition-delay: 650ms;
  opacity: 0;
}


.dish-slide[data-role="active"] .dish-tag {
  transform: scale(1);
  opacity: 1;
}

@keyframes dishTagPop{
  0%{ transform: scale(0.75); }
  60%{ transform: scale(1.05); }
  100%{ transform: scale(1); }
}
.dish-spotlight__desc{
  margin:0;
  font-weight:400; font-size:var(--fs-18); color:var(--c-green);
  max-width:44rem; position:relative; z-index:2;
}
.carousel-controls{ display:flex; gap:12px; position:relative; z-index:2; }
.carousel-arrow{ min-width:clamp(52px,4.1vw,79px); transition: transform .25s var(--ease-soft); }
.carousel-arrow:disabled{ pointer-events:none; opacity:.6; }
/* .btn's own padding (0 30px) is sized for a text label — an icon-only
   arrow button needs much less, and at the narrower explicit widths this
   class is used at (36px/44px on the testimonial slider) the full 30px
   would force the flex item wider than intended, squeezing its siblings.
   Scoped to .carousel-arrow.btn so it doesn't touch any label-bearing
   button elsewhere. */
.carousel-arrow.btn::after{  background-image: url(../assets/svg/btn-face-arrow.svg); }
.carousel-arrow.btn::before{  background-image: url(../assets/images/btn-face-arrow.png); }

/* CSS-drawn chevron — no image/SVG asset for the arrow icon itself.
   .btn's own ::before/::after (shadow + face polygon backgrounds) are
   untouched and still supply the button's shape; this is a plain child
   element so it can't collide with those pseudo-elements. Drawn as a
   left-pointing chevron (border corner rotated) by default — every
   "next"/right-pointing arrow on the site reuses .carousel-arrow--right
   to flip the whole button 180°, same mechanism as before, rather than
   drawing a second mirrored glyph. */
.carousel-arrow__glyph{
  display:block;
  width: 16px; height: 16px;
  border-top: 4px solid var(--c-white);
  border-right: 4px solid var(--c-white);
  border-radius: 2px;
  transform: rotate(-135deg);
  filter: drop-shadow(1px 1px 0 rgba(0,0,0,.35));
}

.btn--wide::before{ background-image:url('../assets/svg/btn-shadow-wide.svg'); }
.btn--wide::after{ background-image:url('../assets/svg/btn-face-wide.svg'); }

.btn--insta{ min-width:295px; }
.btn--insta::before{ background-image:url('../assets/svg/btn-shadow-insta.svg'); }
.btn--insta::after{ background-image:url('../assets/svg/btn-face-insta.svg'); }
.btn--insta .btn__label{ text-transform:uppercase; }

/* ==========================================================================
   HELLO / BRAND INTRO
   ========================================================================== */
.hello{
  background: var(--c-limestone);
  display:flex; gap:0;
  margin-inline: auto;
  padding: var(--space-3xl) var(--pad-inline) 0 var(--pad-inline);
}
.hello__card{
  background: var(--c-mint);
  border: 8px solid var(--c-green);
  border-radius: 60px;
  box-shadow: var(--shadow-solid);
  padding: 0 var(--space-2xl) var(--space-2xl) var(--space-2xl);
  display:flex; flex-direction:column; align-items:flex-start; gap: var(--space-xl);
  text-align:center;
  width: min(37.5vw, 720px);
  flex-shrink:0;
  margin-right:-48px;
  position:relative;
  background-image: url(../assets/images/hello-bg.png);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
/* HELLO! + lead read as one tightly-grouped headline unit in Figma (a
   small gap between them), distinct from the larger gap that separates
   that unit from the body paragraphs below — not the uniform spacing a
   single flex gap on .hello__card would produce. */
.hello__heading{ display:flex; flex-direction:column; align-items: center; gap:20px; }
/* Was the "Hello!" text (font-weight:900, --c-mint, thick --c-green
   stroke) — replaced by the fork_picture.png asset, which already bakes
   in equivalent brand styling (green fork + cream/mint badge) as actual
   artwork. Sized by width, matching the old heading's own clamp() curve
   (3rem-4.5rem) so it occupies roughly the same visual weight at the top
   of .hello__heading across breakpoints; height follows the asset's own
   ratio via auto rather than being forced. */
.hello__hi{
  width: clamp(13.5rem, 8vw, 13.5rem);
  height: auto;
  display: block;
}
.hello__lead{
  font-weight:800; font-size:var(--fs-30); line-height:1.15; letter-spacing:-.03em;
  color: var(--c-green);
}
.hello__copy{ display:flex; flex-direction:column; gap:18px; font-size:var(--fs-24); letter-spacing:-.03em; color: var(--c-black); }

.hello__collage{ position:relative; flex:1; min-height:31rem; }
.collage-photo{
  position:absolute; border-radius:60px;
  width:24.7vw; max-width:380px; aspect-ratio:421/497;
}
/* Inner wrapper is what the cursor-momentum kick actually transforms —
   keeping it separate from the outer element means the kick's
   translate/rotate composes on top of (rather than overwrites) this
   photo's own static Figma position/rotation. */
.collage-photo__inner{ width:100%; height:100%; will-change:transform; }
.collage-photo__inner img{ width:100%; height:100%; object-fit:cover; }
.collage-photo--1{ left:36.5%; top:0; }
.collage-photo--2{ left:24%; top:20.4vw; max-width:350px; }
/* --3/--4 are transparent-background sticker badges (hexagon PNGs), not
   opaque photos — box-shadow paints behind the element's rectangular box
   regardless of the image's own alpha shape, so it would bleed out as a
   stray black rectangle past the hexagon outline. filter:drop-shadow
   follows the actual rendered alpha silhouette instead. */
.collage-photo--3{ left:56%; top:17vw; width:20.6vw; max-width:300px; aspect-ratio:1/1; border-radius:0; transform:rotate(15deg); box-shadow:none; filter:drop-shadow(8px 8px 0px var(--c-black)); }
.collage-photo--4{ left:0; top:13.7vw; width:16.8vw; max-width:300px; aspect-ratio:1/1; border-radius:0; border:none; box-shadow:none; filter:drop-shadow(8px 8px 0px var(--c-black)); }
/* Was "20,000 Bowls / Daily" and "40 Flavours" text (font-weight:900,
   --c-sky, text-shadow, thick stroke) — replaced by the crunch-sticker.png
   / all_flavour_sticker.png assets, which already bake in equivalent
   badge styling as actual artwork. left/top/transform (position,
   centring, rotation) on --1/--2 below are untouched — those are pure
   coordinates. .collage-tag is now the OUTER element (mirroring
   .collage-photo) so it can keep owning that static position/centring/
   rotation while .collage-tag__inner (mirroring .collage-photo__inner)
   is the actual cursor-momentum-kick target — see initMomentumHover's own
   comment for why the kick needs a separate inner target instead of
   overwriting this element's own transform directly. pointer-events is
   no longer none: the kick is triggered by this element's own mouseenter,
   same as .collage-photo, so it needs to actually receive pointer events. */
.collage-tag{
  position:absolute; z-index:3;
  width: clamp(15.5rem, 8.5vw, 15.5rem);
}
.collage-tag__inner{ display:block; width:100%; height:auto; will-change:transform; }
.collage-tag__inner img{ width:100%; height:auto; display:block; }
.collage-tag--1{ left:34%; top:9vw; transform:translate(-50%,-50%) rotate(0deg); }
.collage-tag--2{ left:78%; top:36vw; transform:translate(-50%,-50%) rotate(-7.11deg); width: clamp(10.5rem, 8.5vw, 10.5rem); }

/* ---------- Section heading (diamond divider + title) ---------- */
.section-heading{
  display:flex;
  flex-direction:column;
  align-items:center;
  gap:18px;
  text-align:center;
  margin-inline:auto;
  max-width: 62.5rem; /* 1000px */
}
.section-heading__divider{ height: 30px; width: auto; }
.section-heading h2{
  font-weight:600;
  font-size: var(--fs-60);
  letter-spacing: -0.03em;
  line-height:1;
  color: var(--c-green);
  margin: 0;
}

/* ==========================================================================
   WHY GULLY BOWLS
   ========================================================================== */
.why-gully{
  background: var(--c-limestone);
  padding: var(--space-3xl) var(--pad-inline);
  display:flex; flex-direction:column; align-items:center; gap: var(--space-2xl);
  scroll-margin-top: var(--header-h);
}
.why-gully__grid{ display:flex; gap: var(--space-2xl); width:100%; text-align:center; }
.why-card{ flex:1; display:flex; flex-direction:column; align-items:center; gap: var(--space-md); padding: var(--space-md) 12px; border-radius:30px; min-width:0; }
.why-card__title{
  font-weight:900; font-size:var(--fs-48); line-height:.7;
  text-transform:uppercase; text-shadow: var(--text-shadow-sm);
  text-wrap: balance;
  margin: 0;
}
.why-card__title--mint{ color: var(--c-mint); -webkit-text-stroke-width: medium; -webkit-text-stroke-color: var(--c-green); }
.why-card__title--pink{ color: var(--c-pink-light); -webkit-text-stroke-width: medium; -webkit-text-stroke-color: #E53F8B; }
.why-card__title--sky{ color: var(--c-sky); -webkit-text-stroke-width: medium; -webkit-text-stroke-color: #2B8DC0; }
.why-card p{ font-size:var(--fs-18); letter-spacing:-.03em; }

/* ==========================================================================
   ROTATED MARQUEE BANNERS
   ========================================================================== */
/* overflow-x:clip / overflow-y:visible — NOT overflow:hidden or
   contain:paint (both removed here) — is what actually lets this
   element clip one axis while leaving the other genuinely open.
   overflow:hidden can't do that alone: per spec, once EITHER overflow-x
   or overflow-y computes to anything other than `visible`, the other
   axis — even written as `visible` explicitly — gets force-computed to
   `auto` instead, so a plain "overflow-x:hidden; overflow-y:visible;"
   silently becomes "hidden; auto;", clipping/scroll-boxing Y right along
   with X. That's what the previous overflow:hidden (plus contain:paint,
   added later to guard a Safari/iOS compositing quirk where a
   translate3d()'d + will-change:transform child can otherwise escape a
   plain overflow:hidden ancestor — contain:paint has the exact same
   "clips every axis, no per-axis control" limitation) was actually
   doing: clipping vertical kicks along with horizontal ones, with no way
   to keep the second while dropping the first.
   The modern `clip` keyword doesn't have that coercion rule — pairing
   overflow-x:clip with overflow-y:visible is explicitly valid per the
   CSS Overflow spec and is the intended mechanism for exactly this "clip
   one axis, leave the other free" case. (Confirmed this actually matters
   here, not just in theory: relying on the site's separate root-level
   `html{ overflow-x:hidden }` instead — i.e. removing this element's own
   clipping entirely — measurably left the page horizontally scrollable,
   since `hidden` on a NON-scrolling-box ancestor doesn't reliably stop a
   descendant's overflow from reaching the real viewport scroll
   mechanism the way clipping directly at the source does.) Each .mband
   still bleeds 120px past every side (width:calc(100% + 240px) below)
   so its own edges never reach this clip boundary even at the momentum
   kick's full clamped range — overflow-x:clip only ever needs to catch
   that same already-off-screen bleed margin. Vertically, nothing clips
   at all now, so an up/down kick can freely paint above/below this
   section's own box instead of being cut off at its top/bottom edge. */
.marquee-banners{
  position:relative; padding-block: 24px; background:var(--c-limestone);
  overflow-x: clip;
  overflow-y: visible;
}
/* The whole strip is what js/main.js `initMomentumHover` moves — no
   separate inner wrapper. No auto-scroll, no CSS animation: this element
   only ever gets a transform via JS in response to the cursor, and sits
   at its plain CSS rotate (its default Figma tilt) otherwise. */
.mband{
  overflow:hidden;
  margin-block: -12px;
  /* Bleed the box itself 120px past each side, well beyond the momentum
     kick's own clamped peak (initMomentumHover's MAX_TRANSLATE = 70px):
     the whole element is what gets translated on cursor move (see the
     big comment above), so if its own box were only exactly viewport-
     width, kicking it left/right would slide its trailing edge inward
     and expose the section's plain background in the gap left behind.
     Widening it here (via margin-inline, not transform — that property
     is owned entirely by the JS render loop every frame) means even at
     the full 70px kick the box's real edges never enter the viewport.
     Plain width/margin, not transform, so this never fights the JS's
     own `style.transform` writes. */
  margin-inline: -120px;
  width: calc(100% + 240px);
  position:relative; z-index:1;
  display:flex; justify-content:center; align-items:center;
  padding-block: 24px;
  will-change:transform;
  border-top: 4px solid var(--c-green);
  border-bottom: 4px solid var(--c-green);
  /* Still cover/no-repeat, same as the original single-copy asset (it has
     its own baked-in border on all four sides — top, bottom, left AND
     right — so repeat-x would print a visible double-border seam at
     every tile join; this is a card graphic, not a tileable pattern).
     cover always scales to fully fill whatever box it's in, in both
     dimensions, with zero gap — so the only thing that mattered for the
     "edge exposed" bug was the box itself extending far enough that its
     own edges (which is where cover's image content ends) never enter
     the viewport. The margin-inline/width bleed above already covers
     that at every breakpoint (verified: box edges stay >100px outside
     the viewport even at the full 70px kick), so cover doesn't need to
     change at all here. */
  background-repeat: no-repeat;
  background-position:center;
  background-size: cover;
}
/* Real strip artwork (background colour, scallop pattern, top/bottom
   border) — all baked into the Figma-exported asset. No CSS-drawn
   background-color/border here; the image supplies it. */
.mband--mint{ background-image: url('../assets/images/Green Banner 2.png'); }
.mband--pink{ background-image: url('../assets/images/Pink Banner 1.png'); }
.mband--rot-neg{ transform: rotate(-1deg); }
.mband--rot-green{ transform: rotate(1deg);}
.mband--rot-pos{ transform: rotate(1deg); }
.mband__text{
  font-weight:800; font-size:var(--fs-d80); letter-spacing:-.05em; text-transform:uppercase;
  color: var(--c-green); white-space:nowrap;
}

/* ==========================================================================
   FIND US ON — platform ordering links
   ========================================================================== */
.find-us-on{
  background: var(--c-limestone);
  padding: var(--space-2xl) var(--pad-inline) 0 var(--pad-inline);
  display:flex; flex-direction:column; align-items:center; gap: var(--space-lg);
  text-align:center;
  scroll-margin-top: var(--header-h);
}
/* Compact heading, not the big statement-headline scale used by Why Gully
   Bowls/What's in your bowl — scoped override, shared .section-heading
   (divider + h2 structure) untouched for those other sections. */
.find-us-on .section-heading{ gap:10px; }
.find-us-on__buttons{
  display:flex;
  align-items:center;
  justify-content:center;
  gap: clamp(14px, 2.2vw, 32px);
  flex-wrap: nowrap;
  max-width: 100%;
}
/* Real button artwork (background, shape, drop-shadow, wordmark) is baked
   into each PNG — no CSS-drawn buttons. Sized by width with height:auto so
   every breakpoint keeps the asset's own proportions; the three source
   images share one aspect ratio (403x183), so a single clamp keeps all
   three uniform the way the reference shows them. */
.find-us-on__btn{
  display:block;
  flex: 0 1 clamp(96px, 15.5vw, 300px);
  min-width: 0;
  transition: transform .2s var(--ease-soft);
}
.find-us-on__btn:hover, .find-us-on__btn:focus-visible{ transform: translateY(-3px); }
.find-us-on__btn img{
  width:100%;
  height:auto;
  display:block;
}

/* ==========================================================================
   WHAT'S IN YOUR BOWL — Figma node 144:66
   ========================================================================== */
.whats-in-bowl{
  background: var(--c-limestone);
  padding: var(--space-3xl) var(--pad-inline) 0;
  display:flex; flex-direction:column; align-items:center; gap: var(--space-xl); text-align:center;
  scroll-margin-top: var(--header-h);
}
/* Figma's heading group for this section is 960px wide, not the 1000px
   used by Why Gully Bowls' shared .section-heading — scoped override so
   the shared class (and that other, already-approved section) is untouched. */
.whats-in-bowl .section-heading{ max-width: 60rem; }
.whats-in-bowl__headline{
  font-weight:900; font-size:var(--fs-d72); line-height:.95; text-transform:uppercase;
  color: var(--c-mint); text-shadow: var(--text-shadow-sm);
  text-wrap: balance;
  margin:0;
    -webkit-text-stroke-width: medium;
  -webkit-text-stroke-color: var(--c-green);
}
.whats-in-bowl__desc{ font-size:var(--fs-18); letter-spacing:-.03em; max-width:36.25rem; }

/* ---------- Flavour-state text carousel (headline + description) ----------
   One sliding track holding all 4 states; js/main.js `initBowlFlavors`
   sets translateX(-N*100%) to bring state N into view. The transition
   lives here so JS only ever touches `transform`. */
.bowl-flavor{ width:100%; max-width:70%; overflow:hidden; }
.bowl-flavor__track{
  display:flex;
  transition: transform 650ms var(--ease-soft);
}
.bowl-flavor__slide{
  flex:none;
  width:100%;
  display:flex; flex-direction:column; align-items:center; gap:18px;
}

/* overflow:hidden matches Figma's own structure (the rotating image sits
   inside an "overflow-hidden" wrapper there too) — without it, the square
   image's rotated bounding box briefly exceeds this box mid-turn (its
   diagonal is longer than its side), which is otherwise invisible since
   the image itself is what's spinning, but would register as unwanted
   horizontal scroll room on narrow viewports. */
.protein-bowl{ width:min(56.8vw,1090px); aspect-ratio:1; margin-bottom: -42%; padding-top: 18px; overflow: hidden;}
/* Rotation is driven by js/main.js `initBowlFlavors` (one JS interval sets
   both this transform AND advances the text carousel above, so the two
   can never drift apart) — this rule only supplies the eased turn itself.
   No CSS @keyframes here: see that function's comment for the reference
   video's measured timing (4 stops, ~2s per state). Duration matches
   .bowl-flavor__track's own transition exactly (650ms) so the rotation
   and the content slide always start and finish as one movement. */
.protein-bowl img{
  width:100%; height:auto;
  transform-origin:50% 50%;
  will-change:transform;
  transition: transform 650ms var(--ease-soft);
}

/* ==========================================================================
   DECORATIVE SCALLOP DIVIDER
   ========================================================================== */
/* Real Figma-exported asset (assets/images/Pattern.png) — not a CSS-drawn
   pattern. The PNG is the full 1920px-reference-width strip already, so it
   just needs to render at its own aspect ratio across the section width. */
.scallop-divider{ width:100%; height:auto; display:block; margin-bottom: -6px; z-index: 1;position: relative; }

/* ==========================================================================
   TESTIMONIAL
   ========================================================================== */
.testimonial{
  position:relative;
  background: var(--c-green);
  padding: var(--space-3xl) var(--pad-inline);
  display:flex; align-items:center; justify-content:center; gap: var(--space-lg);
  overflow:hidden;
}
.testimonial__texture{ position:absolute; inset:0; opacity:.05; mix-blend-mode:overlay; pointer-events:none; transform: scale(1.1); right: -20%; }
.testimonial__texture img{ width:100%; height:100%; object-fit:cover; }

/* Only this wrap clips/slides — the arrows stay fixed outside the track so
   they never move. Takes over .testimonial__content's old flex:1 role at
   the outer arrow/content/photo row; the inner row (content + gap + photo)
   is recreated per-slide, unchanged from the single-slide layout. */
.testimonial__track-wrap{ flex:1; min-width:0; overflow:hidden; position:relative; z-index:2; }
.testimonial__track{ display:flex; transition: transform 700ms var(--ease-soft); }
.testimonial__slide{
  flex:none; width:100%;
  display:flex; align-items:center; justify-content:center; gap: var(--space-lg);
}

/* No flex:1 here — that was making content greedily fill all leftover row
   space (pushing the photo off to one side) instead of letting the whole
   content+photo pair size to its own content and center as a compact unit
   via .testimonial__slide's justify-content:center, per Figma. */
/* flex-shrink:1 (was flex:none, i.e. shrink:0) — content+photo's combined
   natural width doesn't actually fit the track-wrap across most of the
   1024-1650px range (arrows+gaps eat into it faster than the two fixed
   sizes give back), so with shrink disabled both were silently
   overflowing their slide and getting cropped at the track-wrap clip
   edge — the quote's first letters and the photo's edge were being cut
   off, not just visually tight. Kept flex-grow:0 (unlike the flex:1
   this comment used to warn against) so once there IS room, this still
   settles at its own natural/max size instead of stretching to fill the
   row — min-width keeps it from ever shrinking to an unreadably narrow
   column. */
.testimonial__content{ flex:0 1 auto; min-width:260px; display:flex; flex-direction:column; align-items:center; gap: var(--space-lg); max-width:34rem; }
.testimonial__stars{ display:flex; gap:8px; }
.testimonial__stars img{ height:30px; width:30px; display:block; }
.testimonial__quote{ font-weight:600; font-size:var(--fs-30); line-height:1.15; color:var(--c-white); text-align:center; }
.testimonial__name{ font-weight:500; font-size:var(--fs-24); color:var(--c-mint); text-align:center; }
.testimonial__role{ font-weight:400; font-size:var(--fs-16); color:var(--c-mint); margin-top:-18px; text-align:center; }

/* The pink octagon border is baked into the source photo itself (a
   pre-clipped PNG with transparency, exported straight from Figma) — no
   CSS clip-path/border recreates it, per "don't approximate artwork with
   CSS if the asset already exists". The box just sizes it. */
/* flex-shrink was 0 — same overflow bug as .testimonial__content above,
   this was the other half of what was overflowing the track-wrap. */
.testimonial__photo{ width:min(32.8vw,630px); min-width:180px; flex-shrink:1; }
.testimonial__photo img{ width:100%; height:auto; display:block; }

.testimonial__bottom-photo{ position:absolute; left:0; bottom:0; width:100%; height:51%; z-index:0; pointer-events:none; }
.testimonial__bottom-photo img{ width:100%; height:100%; object-fit:cover; }

.carousel-arrow--float{ flex-shrink:0; position:relative; z-index:2; }

/* ==========================================================================
   300+ STRONG COMMUNITY — continuous auto-scrolling image marquee
   ========================================================================== */
.community{
  background: var(--c-limestone);
  padding-block: var(--space-3xl);
  display:flex; flex-direction:column; align-items:center; gap: var(--space-2xl);
  overflow:hidden;
  scroll-margin-top: var(--header-h);
}
/* Same technique as the header ticker (.ticker-track/.ticker-set): the
   card set is duplicated back-to-back in one flex track, which animates
   exactly one set-width (-50%) to the left, linear, infinite. Since both
   sets are pixel-identical, the loop point is invisible — no JS, no
   duplicate listener, nothing but this one CSS animation. */
.community__grid{ width:100%; overflow:hidden; }
.community__track{ display:flex; width:max-content; animation: community-scroll 32s linear infinite; }
.community__set{ display:flex; align-items:flex-end; gap:24px; padding-right:24px; flex-shrink:0; }
@keyframes community-scroll{ from{ transform:translateX(0); } to{ transform:translateX(-50%); } }

.community-card{
  width:min(23.4vw,450px); aspect-ratio:474/587;
  flex-shrink:0;
}
.community-card img{ width:100%; height:100%; object-fit:cover; box-sizing:border-box; }

.privacy{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xl);
}
.privacy-policy p{
  margin-bottom: 1rem;
}
.privacy-policy ul{
  list-style: disc;
  padding-left: 1.5rem;
}
.privacy-policy a{
  color: var(--c-green);
  font-weight: 600;
}

/* ==========================================================================
   FOOTER — Figma node 652:541
   ========================================================================== */
.site-footer{
  position:relative;
  /* background: var(--c-green); */
  border-radius: 60px 60px 0 0;
  padding: var(--space-3xl) var(--pad-inline) 0;
  overflow:hidden;
  color: var(--c-white);
}
.site-footer__bg{ background-size:cover; background-position:center; z-index:0; }
/* Fluid instead of a flat 180px: that gap alone eats 360px out of the
   row before content gets any space, and the 1024-1439px laptop range
   has no separate breakpoint of its own — a flat value left .footer-nav
   squeezed so tight its two-word links ("OUR MENU") wrapped mid-label.
   9.375vw == 180px exactly at the 1920 reference, so desktop is unchanged. */
.site-footer__top{ position:relative; z-index:2; display:flex; gap:clamp(48px, 9.375vw, 180px); padding-bottom:60px; }

.footer-brand{ flex:1; display:flex; flex-direction:column; gap: var(--space-lg); }
.footer-logo{ width:184px; height:auto; }
@media screen and (min-width: 1024px){
  .footer-logo{width: 60%;}
}
.footer-brand__lorem{ font-size:var(--fs-16); letter-spacing:-.03em; max-width:32rem; }
.footer-brand__social{ display:flex; align-items:center; gap:20px; }
.footer-brand__social img{ height:28px; width:auto; display:block; }
.footer-brand__social a{ transition: transform .22s var(--ease-soft); }
.footer-brand__social a:hover{ transform: translateY(-3px); }

.footer-nav{ flex:1; display:flex; flex-direction:column; gap: var(--space-md); }
.footer-nav a{ font-weight:500; font-size:var(--fs-18); letter-spacing:-.03em; text-transform:uppercase; transition:color .25s; }
.footer-nav a:hover{ color: var(--c-mint); }
.footer-nav__rule{ display:block; height:1px; background:var(--c-mint); margin-top:20px; }

.footer-contact{ width:240px; display:flex; flex-direction:column; gap:20px; }
.footer-contact h4{ font-weight:500; font-size:var(--fs-18); letter-spacing:-.03em; text-transform:uppercase; margin-bottom:8px; margin-top: 0;}
.footer-contact p{ font-weight:400; font-size:var(--fs-14); letter-spacing:-.03em; line-height:1.35; }

.footer-rule{ position:relative; z-index:2; border:0; border-top:1px solid var(--c-mint); margin:0; }
.site-footer__bottom{ position:relative; z-index:2; display:flex; align-items:center; justify-content:space-between; 
  /* padding-top:30px;  */
  padding:30px 0; 
  font-size:var(--fs-18); letter-spacing:-.03em; gap:24px; flex-wrap:wrap; }
.footer-legal{ display:flex; gap:30px; text-transform:uppercase; font-weight:500; }
.footer-legal a:hover{ color: var(--c-mint); }

/* Real Figma-exported asset (assets/images/Footer Image.png) — transparent
   PNG at its own 1920x760 aspect ratio, not recreated with CSS. */
.site-footer__photo{ position:relative; z-index:1; width:100%; margin-top:60px; }
.site-footer__photo img{ width:100%; height:auto; display:block; }

/* ==========================================================================
   RESPONSIVE — 1440 / 1024 / 768 / mobile
   ========================================================================== */

/* ---- 1440–1919: fluid, already handled by clamp() + --pad-inline ---- */

/* ---- below 1440: tagline wraps to 2 lines at the exact-Figma-ratio star
   position (tuned for the 1920 reference), which starts landing on top
   of it — pull the stars into the side gutters beside the bowl photo. ---- */
@media (max-width:1439px){
  /* Fixed px, not a % of hero's own (fixed 700px) height: the bowl photo
     itself is what grows across this tier (min(56.6vw,1100px) — nearly
     doubles from 768 to 1439), so its vertical middle drifts ~90px over
     the range. A %-based top stayed still while that drifted past it, so
     the stars ended up sitting off the bowl entirely at either end of
     the range instead of flanking it. These values center on the drift. */
  .star{ width:7vw; min-width:0; }
  /* .star--1{ top:390px; left:1%; } */
  /* .star--2{ top:330px; left:auto; right:1%; } */

  /* Row-based hello (card + collage side by side) doesn't hold up here:
     .hello__card's width:min(37.5vw,720px) shrinks to ~330-540px across
     this tier, forcing its paragraph text into a tall wrapped column
     that badly outgrows the collage's fixed min-height beside it — big
     dead space under the collage, cramped-looking photos up top. Same
     stacked treatment as the narrower tiers, just extended up to the
     existing 1439 boundary already used for this range's other fixes. */
  .hello{ flex-direction:column; }
  .hello__card{ width:100%; max-width:60%; margin:0 auto; border-radius:48px; }
  .hello__collage{
    position: unset;
    min-height: auto;
  }
  .hello{position:relative; overflow: hidden;}
  /* .hello__collage{ width:100%; min-height:28rem; } */
  .collage-photo--4{ left: 10%;}

  /* .hello__card{z-index: 0;} */
  .collage-photo{width: 13vw;}
  .collage-photo--1{left: 73%; top: 7%;}
  .collage-photo--3 {left: 73%; top: 60%;}
  .collage-tag--2 {left: 80%; top: 85%;}
  .collage-photo--2 { left: 15%; top: 70%;}
  .collage-photo--4 { top: 10%; }
  .collage-tag--1 { left: 25%; top: 15%;}


  /* The 3-across card row is narrower here than at the 1920 reference, so
     "Freshly Built / Fully Loaded" etc. wrap to 3 lines — Figma's exact
     48px/0.7 line-height (safe at true desktop) collides badly once that
     happens. Figma has no tablet variant to match here, so trade a little
     size for legibility instead of exact pixel-parity. */
  .why-card__title{ font-size:clamp(1.5rem, 2.2vw, 2.75rem); line-height:.95; }
}

/* ---- 768–1023 ---- */
@media (max-width:1023px){
  .navbar{ height:88px; }
  .nav-links{ position:fixed; inset:88px 0 0 0; top:var(--header-h); background:var(--c-green);
    flex-direction:column; justify-content:flex-start; padding-top:60px; gap:36px;
    transform:translateX(100%); transition:transform .35s var(--ease-soft); height:calc(100vh - var(--header-h)); }
  .nav-links.is-open{ transform:translateX(0); }
  .nav-toggle{ display:flex; }
  .nav-cta{ display:none; }
  .nav-links.is-open ~ .nav-cta{
    display:inline-flex;
    position:fixed;
    left:50%; bottom:60px;
    transform:translateX(-50%);
    z-index:10;
  }

  .hero__title{ letter-spacing:.02em; }
  .hero__inner{ 
    /* padding-top: calc(var(--header-h) + 2rem);  */
    padding-bottom: 0; }

  .why-gully__grid{ flex-direction:column; gap:30px; }
  .why-card{ padding:12px 8px; }

  .mband__text{ font-size: clamp(1.75rem, 5vw, 3rem); }

  .testimonial__track-wrap{ width:100%; }
  .testimonial__slide{ flex-direction:column; text-align:center; gap:var(--space-md); }
  .testimonial__content{ align-items:center; text-align:center; max-width:100%; }
  .testimonial__quote{ text-align:center; }
  .testimonial__photo{ width:min(70vw,420px); order:-1; }
  /* Was display:none — kept visible and usable instead: shrink so the
     stacked content column still has room, staying as flanking rails
     either side of the slide rather than being removed. */
  .carousel-arrow--float{ width:36px; padding: 0 16px; }

  .community-card{ width:min(40vw,300px); }

  .site-footer__top{ flex-direction:column; gap:48px; padding-bottom: 30px; }
  .footer-contact{ width:100%; }
  .site-footer__photo{
    margin-top: 30px;
  }
  .bowl-flavor{ max-width:80%; }
}

@media (max-width:991px){
  .hello__card{ max-width: 70%;}
  .hello__copy{font-size: 16px;}
  .collage-photo{width: 15vw;}
  .collage-photo--1{left: 80%; top: 7%;}
  .collage-photo--3 {left: 82%; top: 60%;}
  /* .hello__card is capped at max-width:70% here and its own bottom edge
     lands exactly at the section's own bottom edge (text can run close
     behind it) — there's no safe gap below the card to park a ~100px-wide
     badge without either clipping .hello's own overflow:hidden or
     overlapping the paragraph text, which the plain-text version never
     risked at this size. Moved into the empty margin to the right of the
     card instead (card right edge sits ~81-83% across this whole range),
     clear of .collage-photo--3 (left:82%, top:60%) below it. */
  .collage-tag--2 {left: 92%; top: 85%; width: 64px;}
  .collage-photo--2 { left: 0%; top: 67%;}
  .collage-photo--4 { top: 10%; left: 0%; }
  .collage-tag--1 { left: 25%; top: 15%;}
}

/* ---- below 768: mobile ---- */
@media (max-width:767px){
  .hello__hi{
    width: clamp(8.5rem, 8vw, 8.5rem);
  }
  .ticker-set,
  .ticker-track{
    gap: 24px;
  }
  .ticker-glyph{
    width: 15px;
    height: 13px;
  }
  :root{ --header-h: 96px; }
  .ticker-wrap{ height:32px; }
  .ticker-item{ font-size:12px; }
  .navbar{ height:62px; padding-inline:20px; }
  .logo-svg{ width:130px; }

  /* .hero{ min-height: min(120vw, 540px); } */
  .hero__inner{ 
    /* min-height: min(120vw, 540px);  */
    gap:28px; 
    /* padding: calc(var(--header-h) + 1.5rem) 20px 4rem;  */
    padding: calc(var(--header-h) + 1.5rem) 20px 0; 
  }
  .hero__title{ font-size:clamp(2.25rem,11vw,3rem); letter-spacing:.02em; white-space:normal; -webkit-text-stroke-width: 1px; }
  .hero__tagline{ max-width:100%; font-size: clamp(0.9rem, 1.0417vw, 1.25rem); }
  .hero__bowl{ width:min(85vw, 1100px);}

  .star{ width:12vw; min-width:0; }
  .whats-in-bowl__headline{
    font-size: var(--fs-60);
    -webkit-text-stroke-width: 1px;
  }
  .hello__card{ padding:0 24px 32px 24px; border-radius:36px; gap: 20px; }
  .hello__copy{
    font-size: 14px;
  }
  .hello__lead{
    font-size: var(--fs-24);
  }


  .collage-photo { width: 18vw;}
  .collage-tag--1{
    width: 100px;
  }
  .testimonial__quote{ font-size: var(--fs-24); }

  .why-card__title{ font-size:2rem; -webkit-text-stroke-width: 1px; }

   .mband{ padding-block:14px; padding-inline:12px; }
  .mband__text{ font-size:clamp(0.85rem, 4.6vw, 1.5rem); }

  .protein-bowl{ width:92vw; margin-bottom: -55%; }

  /* Arrows no longer fit as flanking rails beside the stacked content at
     this width — reflowed onto their own row below the slide (still
     visible/usable) via flex-wrap + order, instead of being hidden. */
  .testimonial{ flex-wrap:wrap; row-gap:24px; }
  .testimonial__track-wrap{ order:1; flex-basis:100%; }
  .carousel-arrow--float{ order:2; width:44px; }

  .community__set{ gap:16px; padding-right:16px; }
  .community-card{ width:42vw; border-radius:32px; }
  .community-card img{ border-radius:32px; border-width:5px; }

  .site-footer{ border-radius:36px 36px 0 0; }
  .site-footer__top{ gap:36px; }
  .site-footer__bottom{ flex-direction:column; align-items:flex-start; gap:12px; }
  .footer-legal{ flex-wrap:wrap; gap:16px; }
  .footer-nav{
    gap: 15px;
  }
  .footer-nav__rule{
    margin-top: 15px;
  }
  .footer-brand__social img{height: 20px;}
  .bowl-flavor{ max-width:100%; }
}

@media (max-width:575px){
  svg.btn__label{
    width: 20px;
  }
  .carousel-arrow{ min-width:clamp(40px,4.1vw,79px); }
  .dish-spotlight__title{
      font-size: var(--fs-24);
  }
  .why-gully__grid{ gap:10px; }
  .collage-tag--1{
    width: 145px;
  }
  .hello__card{max-width: 100%;}
  .collage-photo--4, .collage-photo--3{position: relative;width: 40vw;}
  .collage-tag--1 {left: 20%; top: 9%;}
  .collage-photo--2{display: none;}
  /* .hello__card goes full-width at this breakpoint (no right margin to
     float the badge in, unlike the 576-991px range above) — parked near
     the card's bottom-right corner instead, sized down so it fits the
     gap there without reaching into the paragraph text above it or the
     decorative photo/sticker cluster below (see the ≤480px override just
     below this block for the still-tighter mobile case). */
  .collage-tag--2{ left: 88%; top: 73%; width: 100px; }
  .collage-photo--3 { transform: translate(0); left: 50%;}
  .collage-photo--4 { left: 0%; margin-top: -42%;}
  .hello{padding-top: 15%;}
  .collage-photo--1{left: auto; right: 0%; width: 25vw; top: 2%;}
  .why-gully,
  .testimonial{
    padding: var(--space-2xl) var(--pad-inline);
  }
  .why-gully,
  .community{
    gap: var(--space-xl);
  }
  .whats-in-bowl,
  .site-footer{
    padding: var(--space-2xl) var(--pad-inline) 0;
  }
  .community{ padding-block: var(--space-2xl);}
  .mband{
    border-top: 2px solid var(--c-green);
    border-bottom: 2px solid var(--c-green);
  }
}
@media (max-width:480px){
  .collage-tag--2 { top: 79%; width: 80px; }
}

@media (prefers-reduced-motion: reduce){
  .ticker-track, .star{ animation:none !important; }
  .dish-slide, .dish-spotlight__title{ transition:none !important; }
  .protein-bowl img, .bowl-flavor__track{ transition:none !important; }
  .testimonial__track{ transition:none !important; }
  .community__track{ animation:none !important; }
}
