/* OSMEA — the scroll-driven bottle section.
   Layers, bottom to top: the canvas the bottle is drawn into, the blue wash
   the ingredient overlay brings up, then the copy beats. Only one beat is
   legible at a time; assets/bottle.js fades them against scroll progress.

   The section is as tall as it has beats, and the stage inside it is sticky,
   so the bottle holds still on screen while the page scrolls past it. Take
   the pin away (no WebGL, or reduced motion, or a phone) and the same markup
   reads as an ordinary stack -- see .bx--static at the end. */

.bx{
  position:relative;
  /* one screen to hold the stage, plus one per move between beats */
  height:calc(var(--bx-beats,7) * 100svh);
  background:var(--paper);
}

.bx__pin{
  position:sticky;top:0;
  height:100svh;
  overflow:hidden;
}

.bx__canvas{
  position:absolute;inset:0;
  width:100%;height:100%;
  display:block;
}

/* Nothing is drawn until both textures are decoded, so fade the stage in
   rather than letting a bare canvas flash. */
.bx__pin{opacity:0;transition:opacity .5s ease}
.bx--ready .bx__pin,.bx--static .bx__pin{opacity:1}

/* ---------- the copy beats ---------- */
.bx__beats{
  position:absolute;inset:0;
  display:grid;
  grid-template-columns:repeat(12,minmax(0,1fr));
  column-gap:var(--colgap);
  padding-inline:var(--gut);
  align-content:center;
  pointer-events:none;
}

/* every beat occupies the same row, so they stack rather than queue */
.bx__beat{
  grid-row:1;
  opacity:0;
  will-change:opacity,transform;
  transition:color .4s ease;
}
/* Column placement is set by where the bottle is at that beat, measured, not
   chosen: the bottle spans 478..1005 on the left-hand beats and 89..973 on
   the last one, so the copy has to clear those edges. Keeping the storyboard's
   own clearance (about 100px) is what these three variants encode. */
.bx__beat--left{grid-column:1/4}
.bx__beat--right{grid-column:9/13}
.bx__beat--far{grid-column:10/13}

.bx__beat h2{
  font-family:Carentro,Georgia,serif;font-weight:400;
  text-transform:uppercase;
  font-size:var(--t-title);line-height:1.02;letter-spacing:.01em;
  color:var(--blue);
  margin:0 0 var(--s1);
  text-wrap:balance;
}
.bx__beat p{
  font-size:var(--t-body);line-height:1.5;
  color:var(--ink);
  margin:0;max-width:30ch;text-wrap:pretty;
}

/* The drawer's trigger is part of the beat that offers it, so it fades and
   becomes clickable with that beat and needs no schedule of its own. */
.bx__beat .pd__open{margin-top:var(--s2)}

/* The scroll cue at the open. Same deal: it lives in the first beat, so it
   is gone by the time the bottle has turned. Decorative -- the section's
   own transcript is what carries the page to a reader who is not scrolling
   it by hand. */
/* The cue is fixed to the foot of the window. The wrapper repeats
   .bx__beats' grid exactly so the cue lands in the first beat's column
   without anyone having to work out where column 9 starts. Fixed rather than
   absolute because the pin's bottom is below the fold until it sticks; it
   sits outside .bx__pin so that pin's overflow:hidden cannot clip it. */
.bx__cuewrap{
  position:fixed;left:0;right:0;bottom:var(--s2);z-index:3;
  display:grid;grid-template-columns:repeat(12,minmax(0,1fr));
  column-gap:var(--colgap);padding-inline:var(--gut);
  opacity:0;pointer-events:none;
}
.bx__cue{
  grid-column:9/13;margin:0;
  display:flex;flex-direction:column;gap:.45em;
  font-size:var(--t-small);color:var(--blue);
}
.bx__cue-arr{font-size:1.15em;line-height:1}

/* ---------- the four ingredients ---------- */
.bx__ings{
  list-style:none;margin:var(--s2) 0 0;padding:0;
  display:grid;gap:.55em;
}
.bx__ings button{
  appearance:none;background:none;border:0;padding:0;
  font:inherit;font-size:var(--t-body);
  color:var(--blue);
  display:inline-flex;align-items:baseline;gap:.5em;
  cursor:pointer;text-align:left;
}
.bx__ings button::after{content:"\2192";transition:transform .25s ease}
.bx__ings button:hover::after,
.bx__ings button:focus-visible::after{transform:translateX(.25em)}
.bx__ings button[aria-expanded="true"]{font-weight:600}

/* ---------- the overlay (storyboard 10) ---------- */
.bx__wash{
  position:absolute;inset:0;
  background:var(--blue);
  opacity:0;transition:opacity .45s ease;
  pointer-events:none;
}
.bx--overlay .bx__wash{opacity:.94;pointer-events:auto}

/* on the wash, the beat has to invert -- black on this blue measures 1.55:1 */
.bx--overlay .bx__beat--left h2,
.bx--overlay .bx__beat--left p,
.bx--overlay .bx__ings button{color:var(--paper)}

.bx__figure{
  position:absolute;left:50%;top:50%;
  transform:translate(-50%,-50%);
  width:min(38vw,520px);
  margin:0;
  z-index:3;
}
.bx__figure img{
  display:block;width:100%;height:auto;
  aspect-ratio:1/1;object-fit:cover;
  border-radius:2px;
}
.bx__figure figcaption{
  margin-top:.85em;
  font-size:var(--t-small);line-height:1.5;
  color:var(--paper);
  max-width:34ch;
}

/* ---------- what the label says, for anything that cannot see it ----------
   The wrap's copy lives in a texture, so it is invisible to a screen reader
   and to a crawler. The markup carries it too; this only hides it visually. */
.bx__transcript{position:absolute;width:1px;height:1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap}

/* ---------- static fallback ----------
   No WebGL, or reduced motion. The pin and the stacking both come off and the
   beats become a normal sequence of sections with one still of the bottle. */
.bx--static{height:auto}
.bx--static .bx__pin{position:static;height:auto;overflow:visible}
.bx--static .bx__canvas,.bx--static .bx__wash{display:none}
/* the cue is an affordance for the turntable; the static stack scrolls like
   any other page and a fixed cue over it would simply follow you down */
.bx--static .bx__cuewrap{display:none}
.bx--static .bx__beats{
  position:static;display:block;padding-block:var(--s3);
}
.bx--static .bx__beat{
  opacity:1!important;transform:none!important;
  max-width:52ch;padding-block:var(--s2);
}
.bx--static .bx__beat--left,.bx--static .bx__beat--right{grid-column:auto}
.bx--static .bx__still{display:block}
.bx__still{display:none;width:min(320px,62vw);height:auto;margin:0 auto var(--s2)}
.bx--static .bx__figure{
  position:static;transform:none;width:min(420px,100%);margin-top:var(--s2);
}
.bx--static .bx__figure figcaption{color:var(--ink)}

/* ---------- phones ----------
   The turntable runs fine on a phone. What does not is sharing one screen
   between the bottle and the copy: at the zoomed beats the label is carrying
   its own printed type, and any wash over it either leaves the copy sitting
   on glass or eats the label's last line.

   So the stage splits in two. The bottle gets the top band and keeps the
   whole of it; the copy gets the foot of the stage on solid paper and never
   crosses into the glass. bottle.js sizes its keyframes to the band -- see
   PORTRAIT -- and because the canvas is measured, not assumed, changing
   --bx-stage here is all it takes to retune the split.

   Keep this last: these rules share specificity with the ones above and have
   to win. */
@media(max-width:760px){
  :root{--bx-stage:62svh}

  /* the inset is breathing room over the cap, and the canvas measures itself,
     so the keyframes follow it without being told */
  .bx__canvas{
    inset:calc(var(--s2) * 1.5) 0 auto 0;
    height:calc(var(--bx-stage) - var(--s2) * 1.5);
  }
  .bx__beats{
    inset:var(--bx-stage) 0 0 0;
    /* the copy sits at the foot of the screen: align-content puts the row
       there, align-items drops each beat to the bottom of it rather than
       stretching, so a short beat opens up the gap under the bottle instead
       of hanging off it. The row is still as tall as the longest beat. */
    align-content:end;align-items:end;
    background:var(--paper);
    padding-block:0 var(--s2);
  }
  .bx__beat,.bx__beat--left,.bx__beat--right,.bx__beat--far{grid-column:1/13}
  /* No cue on a phone. The stage is split here -- bottle band on top, copy
     at the foot -- so there is no free bottom edge to hang it on: pinned to
     the stage it lands on the copy, raised to the band's edge it lands on the
     glass. A thumb scrolls without being asked to. */
  .bx__cuewrap{display:none}
  .bx__beat p{max-width:none}

  /* The overlay splits the same way: the picture and its note take the
     bottle's band, the ingredient list stays in the copy band. Paper type
     needs the wash behind it, so the band stops painting itself. */
  .bx--overlay .bx__beats{background:none}

  /* ---------- the immersive opening (.bx--immersive) ---------- */
  /* OPT-IN, because the split above is what /dev/product-new ships and it
     is signed off. Add the class and the phone stops being two stacked
     boxes -- a 62svh bottle band over an opaque 38svh copy band, which is
     what made the opening read as a product shot with a caption rather
     than as the full-bleed moment the desktop gets.

     Here the canvas is the WHOLE screen, the copy floats over its foot,
     and a paper scrim carries the type. The bottle is composed into the
     top of the frame by bottle.js rather than by this file -- the canvas
     measures itself and the keyframes follow it, so the framing has to
     move with them or the label would end up behind the copy. */
  /* THE BLOCK BLEEDS PAST THE FOOT OF THE PIN (J, 2026-10-28).

     There is a strip of paper under the blue block on an iPhone and not in
     any render here, which is the family the pinned nav's gap belongs to:
     100svh is the viewport with the toolbars OUT, so with them collapsed
     the pin is shorter than the screen and the section shows under it.

     The first answer here was to paint the SECTION blue and let the pin
     carry the paper. That fixed the strip and bought two bars J did not
     want -- blue under the bottle at the open, where no block has arrived
     yet, and blue over the cap wherever iOS puts the sticky box a few
     points low. The section is the wrong surface: it is visible whenever
     the pin falls short, and the block is only on screen for four of the
     seven beats.

     So the BLOCK bleeds instead, through a pseudo-element that runs on
     down past the pin's foot. It carries the beat's own opacity, so it is
     there exactly when the block is and nowhere else, and the pin stops
     clipping it -- which costs nothing, because the canvas is inset:0 and
     has never had anything to clip. */
  .bx--immersive .bx__pin{overflow:visible}
  .bx--immersive .bx__beat::after{
    content:"";position:absolute;left:0;right:0;top:100%;
    height:18svh;background:var(--blue);
  }

  /* the snap ladder bottle.js builds -- one rung per keyframe, down the
     section. Rungs carry no size and no paint; only their start edge is
     read. The declaration sits on the document because the document is
     the scroller, and proximity means the rest of the page, which has no
     snap targets at all, scrolls exactly as it did. */
  html.bx-snap{scroll-snap-type:y proximity}
  .bx__snaps{position:absolute;inset:0;pointer-events:none}
  .bx__snaps i{position:absolute;left:0;width:1px;height:1px;scroll-snap-align:start}

  .bx--immersive .bx__canvas{inset:0;height:100%}
  .bx--immersive .bx__beats{
    inset:0;background:none;
    align-content:end;align-items:end;
    /* no padding of its own: each beat is a full-bleed block now and sets
       its own inset, so the grid is only here to stack them */
    padding-block:0;padding-inline:0;
  }
  /* NO SCRIM. There was one here and J was right about it: a fade is what
     you reach for when type is sitting on a picture, and the type should
     not have been on the picture. bottle.js composes the bottle into the
     band ABOVE this keyframe's copy, so the copy has clean paper under it
     and nothing needs veiling. The canvas is still the whole screen -- that
     is what keeps this one field rather than the two stacked boxes the
     split layout made -- it is only the bottle that is framed clear. */
  /* THE COPY SITS ON ITS OWN BLUE BLOCK (J, 2026-10-28).
     "a distinct blue container to house the content, it will allow a cut
     off bottle feel more distinct" -- and that is exactly what it buys:
     with a hard blue edge across the foot of the screen, glass running
     under it reads as a crop rather than as a bottle that did not fit, so
     the reading frames are free to be as large as their panel wants. See
     the z and v in IKEYS.
     Full bleed to all three edges, and down past the safe area, so the
     block ends where the screen does and there is no strip of paper under
     it -- J marked that gap twice.
     Each beat carries its own, sized to its own copy, so they cross-fade
     with the beat rather than needing a block of their own to schedule. */
  .bx--immersive .bx__beat{
    position:relative;z-index:1;
    grid-column:1/13;
    background:var(--blue);
    padding:var(--s2) var(--gut);
    padding-bottom:calc(var(--s2) + env(safe-area-inset-bottom,0px));
  }
  .bx--immersive .bx__beat h2,
  .bx--immersive .bx__beat p{color:var(--paper)}
  /* the pill is paper-on-blue here, not blue-on-paper */
  .bx--immersive .bx__beat .pd__open{
    background:none;border-color:var(--paper);color:var(--paper);
  }

  /* on the wash every beat inverts, not just the one the list sits in: the
     band is full width here, so a neighbour mid-fade is on the blue too */
  .bx--overlay .bx__beat h2,
  .bx--overlay .bx__beat p{color:var(--paper)}
  .bx--overlay .bx__figure{
    left:var(--gut);right:var(--gut);top:var(--s2);
    transform:none;width:auto;
    display:grid;grid-template-columns:minmax(0,9rem) minmax(0,1fr);
    gap:1rem;align-items:start;
  }
  .bx--overlay .bx__figure figcaption{margin-top:0;max-width:none}
}
