/* Full photographic scene: no keyer, mask, canvas, or moving cutout edges. */
body{font-size:1.125rem}
#schedule-freshness,.catering-intro>p:last-child,.masthead #mobile-nav #motion-toggle{font-size:1.125rem}
p,.welcome-description,.scene-copy>p:last-child,.section-heading>p,.menu-card p,.smoothie-menu p,.availability,.moment-caption p,.schedule-panel p,.inquiry-note,#inquiry-status,.review-footer p{font-size:1.125rem;opacity:1}
.masthead>nav:not(#mobile-nav),.masthead #mobile-nav,.site-footer nav{font-size:1.125rem}
.masthead{gap:1.4rem}.brand span{font-size:1.125rem}.brand small{font-size:1rem;letter-spacing:.04em}
.eyebrow{font-size:1rem;letter-spacing:.07em}.welcome .eyebrow,.scene-copy>.eyebrow{font-size:1rem}
.welcome-description{color:var(--cream)}.welcome-bottom{flex-wrap:wrap}.welcome-food{margin-top:0}
.welcome{container-type:inline-size}.welcome-copy,.welcome-food,.welcome-bottom{min-width:0}.welcome-food figcaption{flex-wrap:wrap}
.welcome-copy{background:linear-gradient(90deg,#061b13f0,#061b1300);padding:.5rem 0}
.inquiry-panel{background:#790f19;padding:1.5rem}.inquiry-note{color:var(--cream)}
form input,form textarea,#inquiry-message{font-size:1.125rem;background:#5c0a13;color:var(--cream)}
form textarea::placeholder{color:var(--cream);font-size:1rem;opacity:1}
.inquiry-actions,.site-footer nav{flex-wrap:wrap}
.scene-copy{background:#09251bed;padding:1.25rem;left:4.5%;border-left:2px solid var(--gold)}
.scene-bottom{background:#09251bf5;padding:1rem;border-top:1px solid var(--line);left:4.5%;right:4.5%;bottom:0;flex-wrap:wrap}
.scene-cue>span,.film-controls output{color:var(--cream)}
.film-controls{gap:.75rem;flex:1;min-width:15rem}.film-controls input{flex:1;width:auto}.film-controls button{min-width:5rem}
.next-scene{display:block}.people-heading{background:#42171bed;padding:1rem}.trailer-world>div{background:#071e16ed;padding:1rem}
.bowl-stage.studio-film{background:#142b1d;height:calc(100svh - var(--header));min-height:42rem}
.studio-film>.studio-backdrop{display:none}
.studio-film .bowl-cinema-window{inset:0;z-index:0;overflow:hidden}
.studio-film .bowl-cinema-window #bowl-film,.studio-film .bowl-cinema-window #bowl-poster{object-fit:contain;object-position:center;background:#142b1d}
.studio-film .scene-copy{max-width:30%;top:12%;pointer-events:auto}
.studio-film .scene-copy h2{font-size:clamp(2.5rem,4.5vw,5rem);line-height:1.04;margin:1rem 0}
.studio-film .scene-copy>p:last-child{max-width:26ch;line-height:1.5}
.studio-film .scene-bottom{align-items:center}
.studio-film #bowl-status:not(.sr-only){bottom:7.5rem}
.studio-film #bowl-poster{z-index:1}
.motion-on #bowls[data-studio-film]{height:190svh}
@media(max-width:1100px){
.masthead{gap:.75rem}
.visit-button{margin-left:auto}
 
 .studio-film .scene-copy{max-width:34%;left:3%;padding:1rem}.studio-film .scene-copy h2{font-size:2.6rem}
}
@media(max-width:700px){
 :root{--header:88px}.masthead{padding:.375rem 5%}.brand img{width:72px;height:72px;flex-shrink:0}.visit-button{padding:.7rem;font-size:1rem}.nav-toggle{font-size:1rem}
 .welcome{padding-bottom:3rem;grid-template-columns:minmax(0,1fr)}.welcome h1{font-size:clamp(2.7rem,16cqw,6rem)}.welcome-description{max-width:100%;font-size:1.125rem}.welcome-food{margin:1rem 0}.welcome-food img{height:auto;min-height:0;max-height:42svh}.welcome-bottom{position:relative;inset:auto;margin-top:1rem;grid-column:1/-1;font-size:1rem}.welcome-bottom>span{max-width:none}
 .scene-copy{left:4%;right:4%;max-width:none;padding:1rem;top:1rem}.scene-copy h2{font-size:2.7rem}.scene-copy>p:last-child{max-width:none}
 .scene-bottom{left:0;right:0;padding:1rem 6%;gap:.75rem}.scene-cue{width:100%;flex:auto}.scene-cue>span{display:block;max-width:none;font-size:1rem}.scene-cue p{font-size:1.4rem}
 .film-controls{width:100%;min-width:0;flex:0 0 100%}.film-controls button,.film-controls output{font-size:1rem}.film-controls input{width:auto;min-width:4rem}.next-scene{font-size:1rem;flex:0 0 100%}
 .cinema-stage .film-window{inset:8.75rem 0 12rem}
 .studio-film.bowl-stage{display:grid;grid-template-rows:auto minmax(0,1fr) auto;height:calc(100svh - var(--header));min-height:0;overflow:visible}
 .studio-film .scene-copy{position:relative;inset:auto;max-width:none;padding:.875rem 6%;border:0;background:#09251b;z-index:2}
 .studio-film .scene-copy h2{font-size:2.2rem;margin:.5rem 0;line-height:1.05}.studio-film .scene-copy h2 br{display:none}
 .studio-film .scene-copy>p:last-child{max-width:none;font-size:1.125rem;line-height:1.4}
 .studio-film .bowl-cinema-window{position:relative;inset:auto;width:100%;min-height:0;height:100%;aspect-ratio:auto}
 .studio-film .bowl-cinema-window #bowl-film,.studio-film .bowl-cinema-window #bowl-poster{object-fit:contain}
 .studio-film .scene-bottom{position:relative;inset:auto;padding:.75rem 6%;gap:.25rem;background:#09251b}
 .studio-film .scene-cue{display:block}.studio-film .scene-cue p{display:none}.studio-film .next-scene{width:100%;padding:.4rem 0;border:0}
 .studio-film #bowl-status:not(.sr-only){position:relative;inset:auto;grid-row:4}
 .bowl-menu{gap:2rem}.menu-card img{max-width:100%;width:100%;margin:0}.menu-card h3{font-size:1.6rem;overflow-wrap:break-word}
 /* display:block was redundant - a <p> already is one - and its only real
    effect was clobbering original-taste.css's deliberate
    "(max-width:700px) and (max-height:740px) { .moment-caption p{display:none} }".
    This file loads second, so the paragraph came back at short heights, where
    the scene has no room for it: the pair collapsed to 71px while the media
    stayed at 48svh (250px), and the caption landed on top of the photographs.
    Keep the 18px body size, drop the display. */
 .moment-caption p{font-size:1.125rem}.people-controls{font-size:1rem;gap:1rem}.people-controls>span{max-width:60%}.people-controls button{flex-shrink:0}
 .motion-on .people-heading{padding:.75rem;top:1rem}.motion-on .people-scene{padding-top:13rem;padding-bottom:8rem}.people-heading h2{line-height:1.05}
 .inquiry-panel{padding:1.125rem}.form-row{grid-template-columns:1fr}.catering-intro h2{overflow-wrap:break-word}.site-footer nav{font-size:1.125rem}
}
@media(max-width:380px){.bowl-menu{grid-template-columns:1fr}.menu-card img{max-height:20rem}.studio-film .scene-copy h2{font-size:2rem}}
/* When the complete readable scene cannot fit, use native flow + manual film controls. */
@media(max-width:700px) and (max-height:540px){
 .motion-on #bowls[data-studio-film]{height:auto}
 .motion-on .studio-film.bowl-stage,.studio-film.bowl-stage{position:relative;top:auto;height:auto;min-height:0;display:block}
 .studio-film .bowl-cinema-window{height:auto;aspect-ratio:1}
}
@media(prefers-reduced-motion:reduce){.studio-film.bowl-stage{position:relative;top:auto}}

/* ---- Feathered film pass, bowl half - September 14, 2026 -------------------
   The feather itself lives in original-taste.css; this file loads after it, so
   the one rule that defeated the feather has to be corrected here.
   #bowl-film was object-fit:contain over an opaque #142b1d. In a 390x466 window
   a 1080-square film letterboxes to 390x390, so the visible "hard line" was the
   video's own edge against its own background colour - the mask was fading the
   letterbox bar, not the picture. cover makes the film fill its row edge to
   edge (the same treatment the pineapple already had, which is why only that
   half feathered), and a transparent background lets the mask reach real
   pixels. The poster is given the identical treatment so the still and the
   moving frame keep matching bounds, which the bowl QA asserts. */
@media(max-width:700px){
  .studio-film .bowl-cinema-window #bowl-film,
  .studio-film .bowl-cinema-window #bowl-poster{
    object-fit:cover;object-position:center 42%;background:transparent;
  }
}
/* Short viewports drop to normal flow and manual controls; there the window is
   a 1:1 box and the film already fits it, so cover would crop for no reason. */
@media(max-width:700px) and (max-height:540px){
  .studio-film .bowl-cinema-window #bowl-film,
  .studio-film .bowl-cinema-window #bowl-poster{object-fit:contain}
}

/* The panels were the other half of the banding. They are opaque blocks butted
   straight against the film, so even a perfectly feathered film met a hard
   edge - dark band / picture / dark band, which is what reads as "module".
   They become scrims instead: solid under the text, where READABILITY-001
   contrast has to hold, then fading to nothing as they approach the picture.
   Percentages are set so every text node sits inside the solid part. */
@media(max-width:700px){
  .studio-film .scene-copy{
    background:linear-gradient(to bottom,#09251b 0%,#09251b 74%,#09251b00 100%);
    padding-bottom:2rem;
  }
  .studio-film .scene-bottom{
    background:linear-gradient(to top,#09251b 0%,#09251b 78%,#09251b00 100%);
    padding-top:2rem;border-top:0;
  }
}

/* Same treatment for the pineapple half. Here the panels are overlays floating
   on the film rather than grid rows, so they fade directly into the picture.
   The solid portion still covers every text node. */
.cinema-stage .scene-copy{
  background:linear-gradient(to bottom,#09251bed 0%,#09251bed 76%,#09251b00 100%);
  padding-bottom:1.75rem;
}
.cinema-stage .scene-bottom{
  background:linear-gradient(to top,#09251bf5 0%,#09251bf5 78%,#09251b00 100%);
  padding-top:1.75rem;border-top:0;
}

/* ---- Bowl -> pineapple cross-dissolve - September 14, 2026 -----------------
   The two films sat in two sticky sections, so the handoff was a slide: one
   stage left the top while the next arrived from the bottom, which is what
   read as "two modules" even once both films were feathered.
   #sunshine is pulled up by exactly one bowl-stage height plus the dissolve
   distance, so for that distance BOTH stages are pinned in the same place and
   the pineapple fades in over a bowl that has already finished assembling.
   Every number arrives from JS in real pixels (--handoff, --handoff-pull) so
   the layout and the scroll timeline cannot drift apart. Both default to 0px,
   and the document ships class="no-motion", so a JS failure, a no-JS load or
   reduced motion renders exactly as it did before this pass.
   The fade is applied to the section, not the stage: the stage is svh-tall and
   a phone viewport can be taller than svh, which would leave a band of bowl at
   full opacity underneath a dissolving stage. */
/* ---- The hero IS the film's opening frame - September 14, 2026 ------------
   The hero photograph and this film's first frame are the same frame. They used
   to be two elements in two sections, so the hero's bowl scrolled away while an
   identical bowl arrived from below, and for most of a screen-height BOTH were
   on screen, stacked, with the section seam between them. That is the
   repetition - the same picture twice, at once.
   #bowls is pulled up by exactly one hero height and grows by the same amount,
   so its stage is already pinned at scroll 0, behind the hero's words, holding
   the frame the hero used to hold. Height added == margin pulled back, so the
   document is exactly as long as it was: this buys the effect with no extra
   scroll distance to reach anything, which "no compulsory spectacle distance"
   forbids. Every number arrives from JS in real pixels and defaults to 0, and
   the document ships class="no-motion", so no-JS, reduced motion and the short
   viewport render precisely as before. */
.motion-on #bowls[data-studio-film]{
  height:calc(190svh + var(--handoff,0px) + var(--lead,0px));
  margin-top:var(--lead-pull,0px);
  scroll-margin-top:var(--lead-anchor,var(--header));
}
/* Tree order would paint the pulled-up #bowls straight over the hero's words -
   both are positioned, and #bowls comes second. .welcome already isolates, so
   one z-index lifts the whole hero, scrim and copy together, over the film. */
[data-hero-film=on] .welcome{z-index:1}
/* Now that a pinned stage is behind it holding the same frame, the hero's own
   picture IS the second bowl. The backdrop goes with it: it is opaque, and it
   would hide the film entirely. Gated on the published lead, never on
   .motion-on - that is still set at 390x520, where the bowl is NOT sticky and
   there would be nothing behind the hero to take the picture's place. */
[data-hero-film=on] .welcome>.studio-backdrop,
[data-hero-film=on] .welcome-food{display:none}
.motion-on #sunshine{
  height:calc(245svh + var(--handoff,0px) + var(--exit,0px));
  margin-top:var(--handoff-pull,0px);
  scroll-margin-top:var(--handoff-anchor,var(--header));
}
@media(max-width:700px){
  .motion-on #sunshine{height:calc(235svh + var(--handoff,0px) + var(--exit,0px))}
}

/* ---- Bowl -> pineapple: dip through the empty set -------------------------
   The straight cross-fade above assumed the two films can superimpose because
   they are "the same table, shot the same way". That is true of the GROUND and
   false of the SUBJECTS: a wide bowl of chicken and a tall hollowed pineapple
   are different shapes in different places, so at the midpoint the pineapple
   sat visibly INSIDE a transparent bowl. A ghost, not a transition.
   shared-pineapple-set.webp is that same set with nothing on it - the sweep and
   the table, no subject - and its horizon measures 76.0% of frame, the same as
   the pineapple film's to a tenth of a percent. So the seam dips through it:
   the bowl clears to an empty table, the table holds for a beat, and the
   pineapple arrives on it. The two subjects are never on screen together.
   It is a layer OVER the film rather than the film's own opacity, because
   qa-bowl-studio asserts #bowl-film is opacity 1 at every place() point and
   p=1 lands exactly where this begins. Fading the film would fight that test.
   contain matches the film's own contain box to within a pixel on wide screens;
   phones use cover with the film's object-position, because contain in a
   portrait window would letterbox the set against flat colour. */
.studio-film .bowl-cinema-window::after{
  content:"";position:absolute;inset:0;z-index:2;pointer-events:none;
  background-color:#142b1d;
  background-image:url('assets/shared-pineapple-set.webp');
  background-repeat:no-repeat;background-position:center;background-size:contain;
  opacity:var(--set-clear,0)}
@media(max-width:700px){
  .studio-film .bowl-cinema-window::after{background-size:cover;background-position:center 42%}
}
/* Belt and braces, not a registration claim: #bowl-film keeps cover/center 42%
   here, so this does NOT line the set up with the film. It cannot be exercised
   - this is the still/manual viewport, where --handoff is 0 and the dip never
   paints - and it exists only so an inert layer defaults to showing the whole
   set rather than an arbitrary crop of it. If the bowl ever becomes
   scroll-driven at this size, match the film's object-position here first. */
@media(max-width:700px) and (max-height:540px){
  .studio-film .bowl-cinema-window::after{background-size:contain;background-position:center}
}

/* ---- Pineapple -> menu: the light comes up --------------------------------
   The menu used to REPLACE the film: a hard-edged cream panel climbing over a
   fully visible pineapple, which is the same slide change the dissolve above
   removed, one section later. Now the last --exit pixels of the pin fade the
   held frame to the menu's own cream, so the light comes up on a still camera
   and the section that follows opens on the colour already filling the screen
   - there is never an edge to see.
   The curtain covers the whole SECTION, not the svh-tall stage: a phone
   viewport can exceed svh, which would leave a dark band under a cream sheet.
   Its colour is the menu's literal background, so the two cannot drift; if
   that ever moves, this moves with it.
   The stage isolates, so the curtain needs no z-index to cover it: a later
   positioned sibling paints over an isolated one. Leaving it at auto is the
   point - #menu is later still, so the menu's own words land ON the cream
   instead of behind it.
   And #menu is pulled up by exactly one stage height, so the cream wash and
   the menu's arrival are the same moment rather than a blank screen between
   them. That pull is also why no scroll-margin is needed here: #menu's top is
   now the section bottom minus one stage, which is precisely where the stage
   unpins, so the "Menu" link still lands on the heading it always did. */
.film-exit{position:absolute;inset:0;background:#f7edce;opacity:0;pointer-events:none}
.motion-on #menu{margin-top:var(--exit-pull,0px);scroll-margin-top:var(--exit-anchor,var(--header-live,var(--header)))}
/* Short viewports leave the sticky model: the bowl stage is in normal flow
   there and there is nothing to dissolve over. JS sends 0px, but this file's
   own earlier height:auto has to be re-asserted after the rule above. */
@media(max-width:700px) and (max-height:540px){
  .motion-on #bowls[data-studio-film]{height:auto;margin-top:0}
  .motion-on #sunshine{height:235svh;margin-top:0}
  .motion-on #menu{margin-top:0}
  .film-exit{display:none}
}

/* ---- The scrubber goes where the scroll IS the scrubber --------------------
   In scroll mode the page's own movement drives the film, so the range input is
   not merely redundant - it is a full-width, 44px-tall hit target lying exactly
   in the path of the thumb doing the scrolling. And a touch on it does not just
   seek: rangeInput() also scrollTo()s the page to match, so a brush against it
   teleports the reader mid-film. That is the lurch, and it is worth more than
   the control is.
   So the row is lifted out of the flow and made invisible and untouchable. It is
   NOT removed: it stays focusable and in the accessibility tree, and tabbing to
   it brings it back in flow, because for someone driving by keyboard a slider
   beats arrowing through two thousand pixels of track. Returning it to static on
   focus is what keeps the revealed row inside the panel instead of on top of it.
   Where the film is NOT scroll-driven this rule never applies and the controls
   are untouched: reduced motion, and the short viewport where the bowl leaves
   the sticky model - note the bowl can be 'still' while the pineapple on the
   same screen is 'scroll', which is why the mode is read per film from the
   script rather than guessed from a media query.
   On a failed load both films disable their slider and their Play button, so
   the row this hides is already dead; the error message carries the news.
   Specificity is load-bearing here. original-taste.css:62 is
   `.motion-on .film-stage:hover .film-controls{opacity:1}` at 0,4,0, so a plain
   `[data-cinema-mode=scroll] .film-controls` at 0,2,0 loses to it and the bar
   pops back the instant a desktop mouse crosses the film - visible, live, and
   one click from teleporting the page. Matching that shape, and hanging the
   whole thing off :not(:focus-within), both defeats the hover and makes the
   keyboard reveal fall out for free: when the row IS focused this rule simply
   stops applying, so it returns to static flow and line 63 lights it up. */
[data-cinema-mode=scroll] .film-stage .film-controls:not(:focus-within){
  position:absolute;left:0;right:0;bottom:0;width:auto;
  opacity:0;pointer-events:none}

/* ---- The hero is the film's first frame ----------------------------------
   It used to be an owner serving photo cut out of its background and floated on
   a gradient. The cut-out was clean enough; the problem was resolution. That
   source is 1024x1024 and the hero shows it up to 1000px wide, so on any modern
   screen it was running past its own pixels - visibly soft and artifacted with
   2560x1440 studio frames playing directly beneath it. It was the lowest-quality
   image on the page, sitting in the one spot everybody sees.
   So the hero now holds the finished frame of the bowl film itself: same bowl,
   same studio light, same green ground as the section below. You land on the
   dish, and scrolling into the film continues the same shot rather than cutting
   from a pasted cut-out to a movie - which is the "one coherent cinematic
   experience, not unrelated animated panels" the owner asked for, applied to the
   one section that was still a poster.
   Art direction, not a squeeze: phones get the 1080-square crop the film already
   ships, wide screens get the 2560x1440 frame. */
.welcome-food{position:absolute;inset:0;margin:0;max-width:none;z-index:0;
  /* An absolutely positioned GRID child still answers to align-self, and the
     phone rules set it to start - which makes it shrink to its own aspect ratio
     and ignore inset:0 entirely. The hero image came out square and stopped
     halfway down the section. Stretch is what makes inset:0 mean inset:0 here. */
  align-self:stretch;justify-self:stretch}
.welcome-food picture{display:block;width:100%;height:100%}
.welcome-food img{width:100%;height:100%;max-height:none;min-height:0;
  object-fit:cover;object-position:50% 42%;filter:none}
/* The copy sits ON the photograph, so it carries its own ground rather than
   trusting the image to stay dark where the words are. Directional: opaque
   where the headline is, clear over the bowl. */
:root{--hero-veil:linear-gradient(100deg,#061b13ef 0%,#061b13e0 26%,#061b1394 48%,#061b1326 66%,#061b1300 82%)}
.welcome::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:var(--hero-veil)}
/* When the film is the hero, the veil cannot ride the section that scrolls off
   it. .welcome scrolls; the stage behind it does not, so the scrim's bottom
   edge swept up a stationary picture for the whole lead - a measured hard step
   from rgb(14,35,22) to rgb(76,95,46) in one pixel, crossing the bowl. That is
   the same moving seam the feathering pass above exists to remove.
   So the veil moves onto the pinned stage, where it cannot travel, and fades
   out with the hero's words instead: the camera holds, the words cross-dissolve
   over it. z-index 2 puts it above the film and its poster and below the film's
   own captions (4 and 5), and the hero's copy is in .welcome, which the rule
   above lifts over the whole track. Not fixed positioning: .welcome is a
   container-type:inline-size element, which is a containing block for fixed
   descendants, so a fixed veil would still be anchored to the scrolling hero. */
[data-hero-film=on] .welcome::after{display:none}
[data-hero-film=on] .studio-film::before{
  content:"";position:absolute;inset:0;z-index:2;pointer-events:none;
  background:var(--hero-veil);opacity:var(--hero-veil-opacity,1)}
.welcome-copy{position:relative;z-index:2;background:none;padding:0}
.welcome-bottom{z-index:2}
.welcome-food figcaption{position:absolute;right:6%;bottom:86px;z-index:2;margin:0;
  justify-content:flex-end;color:#fff4d3}
@media(max-width:700px){
  /* Portrait crop, and the scrim turns vertical: the headline is at the top of a
     phone hero, not to one side of it. */
  /* A square crop stretched into a portrait hero shows the whole bowl at once,
     which puts the chicken - the thing the headline is promising - directly
     behind the copy, leaving rice and corn as the visible half. So on a phone the
     photograph takes the lower two thirds and is cropped to the chicken, while
     the copy keeps a clean dark ground above it. The top edge is feathered rather
     than cut, so the bowl rises out of the dark instead of starting at a line -
     the same move the rest of this page makes at every seam. */
  .welcome-food{top:34%}
  .welcome-food img{object-position:50% 30%;
    -webkit-mask-image:linear-gradient(180deg,#0000 0,#000 22%);
    mask-image:linear-gradient(180deg,#0000 0,#000 22%)}
  /* The phone hero had no call to action at all: original-taste.css hides the
     hero button below 700px, so the only way forward was the "A closer taste"
     cue in the bottom rule. That was a reasonable trade when the copy shared the
     section with a tall cut-out; the full-bleed photograph gives the room back,
     and the phone is where this site is actually read. */
  .welcome-copy>.solid-link{display:inline-flex;margin-top:1.1rem}
  :root{--hero-veil:linear-gradient(180deg,#061b13e6 0%,#061b13cc 26%,#061b1359 44%,#061b1300 62%,#061b1333 84%,#061b13b3 100%)}
  .welcome-food figcaption{display:none}
}
/* The hero's footer rule sat in flow directly under the copy, which used to be
   over flat dark. With the photograph behind it, that is on top of the chicken.
   Pin it to the foot of the section instead, where the frame is the dark table
   and the scrim is at its strongest. Phones short enough that the hero is
   already cramped keep the in-flow original - there is no room to pin to. */
@media(max-width:700px) and (min-height:541px){
  .welcome-bottom{position:absolute;left:6%;right:6%;bottom:14px;margin-top:0}
  :root{--hero-veil:linear-gradient(180deg,#061b13e6 0%,#061b13cc 26%,#061b1359 44%,#061b1300 56%,#061b1359 74%,#061b13d9 88%,#061b13f2 100%)}
}

/* A hero this short cannot hold a photograph and five lines of copy plus a
   button and a footer rule - at 472px the bowl ends up directly behind the words
   with nowhere to move it to. So below 540px of height the frame stops competing
   and becomes a ground: still the same bowl, still warm, but dark enough that
   every line sits on it at full strength. Degrading the picture is the right
   trade here; degrading the reading never is. */
@media(max-width:700px) and (max-height:540px){
  .welcome-food{top:0}
  .welcome-food img{object-position:50% 38%;-webkit-mask-image:none;mask-image:none}
  :root{--hero-veil:linear-gradient(180deg,#061b13f5 0%,#061b13ed 55%,#061b13f5 100%)}
}

/* The headline reached the bowl. "HUNGRY." is yellow and the cup rim is a bright
   cream highlight, so the tail of the Y and the full stop were landing on it at
   1:1 - invisible, measured against the real composited pixels rather than
   against the section's nominal background.
   The frame is 2560x1440 into a 1440-wide hero, which leaves about four pixels of
   horizontal slack, so object-position cannot move the bowl. Shifting the whole
   image layer right does: the section clips, the vacated strip on the left is
   exactly where the scrim is already at its most opaque, and the type gets its
   own dark ground back without touching the type. */
/* No shift: the hero frame must land exactly where the film's first frame lands,
   because they ARE the same frame. Scrolling from the hero into the film should
   set that picture in motion, not cut to a differently-framed copy of it. The
   headline is protected by the scrim instead - measured, below. */
@media(min-width:701px){
  .welcome-food img{object-fit:contain;object-position:50% 50%}
  :root{--hero-veil:linear-gradient(100deg,#061b13f7 0%,#061b13f2 30%,#061b13d9 44%,#061b1366 60%,#061b1300 78%)}
}

/* Short phones: this file loads after original-taste.css, and its blanket
   (max-width:700px) rules were overriding that file's deliberate short-height
   ones. padding 13rem/8rem left 164px of a 500px stage for content while the
   media still asked for 48svh (250px), so the pictures overflowed the pair and
   the caption printed on top of them. Re-assert the tighter padding here where
   it can win, and let the media yield to the space that is actually left - a
   picture that cannot fit its own box is how text ends up over a photograph. */
@media(max-width:700px) and (max-height:740px){
  .motion-on .people-scene{padding-top:9.375rem;padding-bottom:4.5rem}
  /* max-height:100% cannot resolve here - the figure has no definite height, so
     the percentage is dropped and the cap vanishes entirely. Cap in viewport
     units against the space that is actually left after the padding, heading
     and caption: 45svh clears the pair across this query's whole range - the pair is not a
     fixed fraction of the viewport, because the stage has a 500px floor, so the
     tightest point is mid-range (~41% at 600 tall), not at either end. */
  .motion-on .people-pair img,.motion-on .people-pair video{max-height:40svh}
  .motion-on .people-scene>figure>img,.motion-on .people-scene>figure>video{max-height:40svh}
}

/* ---- Phone: one camera, both halves - September 14, 2026 ------------------
   The two phone films were never the same shot. The bowl's phone master was a
   1:1 crop of its own footage; the pineapple's was the 16:9 desktop master
   squashed down, so .film-window video carried width:190%/left:-45% to crop it
   back to something square-ish. That hack scaled the drink to 741px of
   rendered film inside a 390px window - the smoothie came out 1.75x larger
   than the bowl it is supposed to be standing next to on the same table.

   Both phone masters are now 1440x1440 full-height crops of the SAME desktop
   masters the alignment work locked together, so:

     * the horizon sits on row 827 of 1080 in both (0.766, the desktop figure),
       and in both phone stills and the phone bridge image as well;
     * each crop is centred on its own film's content, so cover takes the same
       bite out of either side (bowl margins 103/112, drink 258/268);
     * the drink renders at 0.754 of the bowl's width - not a chosen number,
       but the ratio the two objects actually have in the footage, which is
       what "same camera" has to mean.

   That only survives to the screen if the two windows are the same box with
   the same fit, so the three rules below are a set: change one and the table
   steps across the dissolve again. qa-film-alignment.mjs --off proves it.

   The bowl's window is a grid row (auto / 1fr / auto) between its two panels;
   the pineapple's is an absolutely positioned window. The insets here are the
   measured output of that grid - 173px above, 101px below, stable across
   320-412px wide - so the pineapple is handed the identical frame. They are
   pinned rather than derived because the two scene-copy blocks hold different
   text at different sizes; qa-film-alignment.mjs asserts the boxes still match
   on phone, so if that text ever changes the gate fails instead of the seam
   quietly reopening. */
@media(max-width:700px){
  /* Re-measured September 18, 2026. The bottom was 101px, taken from the grid
     as it stood in June; the copy and the masthead have both been rewritten
     since, and the grid now ends 118px above the stage floor. The pineapple
     kept the old number, so its window ran 17px taller than the bowl's and the
     table stepped down 13px as the film faded in -- exactly the reopening this
     comment block was written to make loud. The gate caught it; the seam did
     not get to close quietly. Top is still 173: it measures 170 at 360px wide,
     where the copy takes one more line, and 3px is the slack the gate allows. */
  .motion-on .cinema-stage .film-window{inset:173px 0 118px}

  /* 40%, not the old 42%: that was tuned against a phone master whose horizon
     sat at 0.731. At 0.766 there is more scene above the table and less below,
     and 40% is where the subject's own extent (rows 24..1019) centres in the
     visible band on the short windows where cover has to crop vertically. */
  .studio-film .bowl-cinema-window #bowl-film,
  .studio-film .bowl-cinema-window #bowl-poster,
  .cinema-stage .film-window video{
    object-fit:cover;object-position:center 40%;background:transparent;
    width:100%;max-width:100%;left:0;
  }
  .studio-film .bowl-cinema-window::after{background-position:center 40%}

  /* Fail open: when autoplay is refused the film gets its native controls back, and the
     no-JS page ships them from the markup. Show the whole frame there, not a cropped one. */
  .film-window video[controls]{object-fit:contain;width:100%;max-width:100%;left:0}

  /* The bridge is the empty set the dissolve dips through. At 1672x941 it was
     the only 16:9 surface left in a 1:1 sequence: cover cropped 60% of its
     width away in a portrait window, which both moved its horizon off the
     films' row and magnified its grain against theirs. This is the same set
     cropped 941x941 at full height - horizon 0.766, grain untouched because
     nothing is resampled. */
  .studio-film .bowl-cinema-window::after{
    background-image:url('assets/shared-pineapple-set-mobile.webp');
  }
}
/* Short viewports drop to flow and manual controls; the window is a 1:1 box
   there and both square films already fit it, so cover would crop for nothing. */
@media(max-width:700px) and (max-height:540px){
  .cinema-stage .film-window video{object-fit:contain}
}

/* ---- Give the food the room the panels were holding, September 16, 2026 ----
   On a short phone -- an iPhone SE is 375x553 with Safari's chrome showing --
   the three rows came out caption 170px, film 194px, bottom bar 101px inside a
   465px stage. The food, on a page selling food, was the smallest of the three
   and barely a third of the screen. The panels stay opaque, because making them
   translucent is what caused the banding this file fixed in September; they
   just stop taking more than they need. The eyebrow goes because the headline
   directly beneath it already says the same thing in more words, and the body
   copy keeps its 18px -- the film row absorbs every pixel the two give back. */
@media(max-width:700px) and (max-height:740px){
  .studio-film .scene-copy{padding:.5rem 6% .45rem}
  /* Both film scenes, not just this one -- a kicker over one headline and not
     the other is the kind of difference you feel without being able to name. */
  .film-stage .scene-copy>.eyebrow{display:none}
  .studio-film .scene-copy h2{font-size:1.9rem;margin:.2rem 0 .3rem}
  .studio-film .scene-copy>p:last-child{line-height:1.3}
  .studio-film .scene-bottom{padding:.4rem 6% .5rem}
  /* The smoothie lost its umbrella. A 1080-square frame in a 375x226 window
     under object-fit:cover shows only the middle 60% of the picture, and the
     paper umbrella sits in the top tenth -- so the one detail that says "this
     is the Pineapple Party" was the first thing cropped. Squaring the window
     against its own width makes cover show the whole frame, and this stage
     already paints the scrim that lets its words sit over the film. */
  #sunshine .scene-copy{padding:.5rem 6% .45rem}
  #sunshine .scene-bottom{padding:.4rem 6% .5rem}
  /* Re-measured with it: the bowl's grid gives 102/88 on the short phones, not
     88/37, which left the drink in a window 65px taller than the bowl's. The
     umbrella that this rule was written to save is still safe -- the film is
     square and the window portrait, so cover fits it exactly on the vertical
     and crops nothing off the top either way; the shorter window actually
     crops less across (14px against 79px). */
  #sunshine .film-window{inset:102px 0 88px}
}

@media(max-width:1100px){
  /* ---- The jump links are buttons, so let them be buttons, Sept 16 2026 ------
     They were bare inline-ish links: "Something to go with that" was a 343x31
     strip, under the 44px a thumb needs, and both still had user-select:auto --
     so a tap that missed the glyphs selected the words instead of following the
     link. That is the whole of "you have to hit a certain spot".
     They also earn their prominence: taking one is how the film gets watched at
     its own pace, and a reader who never notices them only ever scrubs past. */
  .next-scene,.studio-film .next-scene,.welcome-bottom a[href^="#"]{
    align-items:center;gap:16px;min-height:48px;padding:11px 18px;
    background:#06180fc9;border:1px solid #ffda3866;border-radius:999px;
    color:var(--gold);font-weight:700;text-decoration:none;
    -webkit-user-select:none;user-select:none;
    -webkit-tap-highlight-color:transparent;touch-action:manipulation;
    transition:background .16s ease,color .16s ease,border-color .16s ease;
  }
  .next-scene,.studio-film .next-scene{display:flex;justify-content:space-between;width:100%}
  .welcome-bottom a[href^="#"]{display:inline-flex;width:auto;flex:none}
  .next-scene>span,.welcome-bottom a[href^="#"]>span{margin-left:0;line-height:1}
  /* Pressed, hovered or keyboard-focused, it fills -- the same gold the Find the
     truck button uses, so the two read as the same kind of thing to press. */
  .next-scene:hover,.next-scene:active,.next-scene:focus-visible,
  .welcome-bottom a[href^="#"]:hover,.welcome-bottom a[href^="#"]:active,
  .welcome-bottom a[href^="#"]:focus-visible{
    background:var(--gold);color:var(--deep);border-color:var(--gold)}
  .next-scene:focus-visible,.welcome-bottom a[href^="#"]:focus-visible{
    outline:3px solid var(--cream);outline-offset:2px}
  /* No idle animation on the arrow. A nudge that never stops is a second thing
     moving on a page that already moves, and -- the reason it came out -- it makes
     two screenshots taken a moment apart differ, which is exactly how the dissolve
     gate proves a hidden scene paints nothing. The gate was right. */
}

/* ---- large text: overlays go back into the flow ---- */
/* Two bars on this page are positioned absolutely against the bottom of their
   section, which is fine until the OS text size grows the copy down into them:
   "Family-owned. Generously served." ends up printed across the hero button.
   Nothing here is a type change -- the sizes stay exactly as they are; the bar
   simply stops floating and takes its own row, which is the treatment this
   stylesheet already uses for .welcome-bottom on short screens.
   The play button used to collide with the carousel's "01 / 03" label here at
   this text size, and it was recorded as unfixable without changing the sticky
   stage's own height. The button no longer exists -- the films play themselves
   -- so the collision went with it.
   em in a media query measures against the browser's DEFAULT font size, which
   is the lever Android's font-size setting moves, so this asks whether the
   screen is narrow FOR THIS TEXT SIZE. At the normal size it is 300px and
   fires on no width tested here; at 150% it is 450px and covers every phone.
   Same threshold as the occasion list in pineapple-catering.css. */
@media(max-width:18.75em){
  .welcome-bottom{position:relative;inset:auto;margin-top:1rem;grid-column:1/-1}
  .welcome-bottom>span{max-width:none}
}
