/** Shopify CDN: Minification failed

Line 1194:0 Unexpected "}"

**/
/* ============================================================================
   LUNCH-B  ·  Tasting Menu / Moko Deli
   Figma: JVS6kkMecptvsB7xl4MnH2 · frames 1:308 (formal) 1:383 (casual)

   ONE artboard per theme, 1080 x 8185 design px. Every layer is placed by its
   exact Figma matrix: --x/--y is the layer's UN-ROTATED top-left, --w/--hh its
   placed size, --r the rotation in CSS degrees. transform-origin is 0 0 so the
   numbers map 1:1 onto Figma.

   The two themes are stacked on top of each other (the casual artboard carries
   a negative top margin equal to the full artboard height) and cross-fade in
   place, so switching never moves the reader.

   Rotation is ALWAYS the standalone `rotate` property and motion is ALWAYS the
   standalone `translate`/`scale` property. They compose independently, so an
   entrance or a float can never cancel a layer's designed rotation. Do not
   replace either with a `transform` shorthand.
   ========================================================================= */

/* ---------- 1. Artboard unit ------------------------------------------- */

.lb-page,
.lb-switch-host {
  /* 1 design px. 1080 design px == 100vw on a phone; on anything wider the
     artboard settles at 734px — the mobile composition is the ONLY layout now
     (the desktop one was retired), so it is allowed a comfortable column
     rather than a 540px strip on a 27-inch screen. The switch pins itself
     back to the phone unit below, so the control does not grow with it. */
  --u: min(0.0925926vw, 0.68px);
  --lb-navy: #0d376a;
  --lb-ink: var(--lb-fg, #ffffff);

  --lb-body: "Montserrat", ui-sans-serif, system-ui, sans-serif;
  --lb-script-formal: "Monsieur La Doulaise", cursive;
  /* Madelinette Grande Vino is not a webfont; falls back until it is supplied */
  --lb-script-casual: "Madelinette Grande Vino", "Monsieur La Doulaise", cursive;

  position: relative;
  color: var(--lb-ink);
  font-family: var(--lb-body);
  isolation: isolate;
}

.lb-art {
  position: relative;
  width: calc(1080 * var(--u));
  height: calc(var(--band, 8185) * var(--u));
  margin-inline: auto;
  /* clip at the frame edge like Figma, but never vertically: props are meant
     to bleed past the top and bottom of the artboard. `hidden` on one axis
     forces the other to `auto` and would slice them off. */
  overflow-x: clip;
  overflow-y: visible;
}

/* ---------- 2. Theme stacking + cross-fade ------------------------------ */

.lb-page--casual { margin-top: calc(-1 * var(--band, 8185) * var(--u)); }

.lb-page[data-lb-active="false"] {
  opacity: 0;
  pointer-events: none;
  visibility: hidden;
}
.lb-page[data-lb-active="true"] {
  opacity: 1;
  visibility: visible;
}
.lb-page {
  transition: opacity .55s ease, visibility 0s linear .55s;
}
.lb-page[data-lb-active="true"] {
  transition: opacity .55s ease, visibility 0s;
}
@media (prefers-reduced-motion: reduce) {
  .lb-page { transition: none; }
}

/* Backgrounds ----------------------------------------------------------- */
/* Sampled from the cloth photo's own vertical tone profile. A flat #000 left a
   visible band at the cloth's top edge (its first row is #202022 — a 32-level
   step) and another at the bottom. The cloth is also feathered into this at
   both ends, so there is no seam anywhere. */
.lb-page--formal {
  background: var(--lb-bg-formal, linear-gradient(180deg,
    #202022 0%, #202022 7.31%, #1d1e20 14.66%, #1a1a1c 24.43%, #131313 36.65%,
    #151515 48.87%, #141414 61.09%, #131313 73.3%, #121212 85.85%, #101010 100%));
}
.lb-page--casual {
  background: linear-gradient(180deg,
    var(--lb-bg-top, #76b6eb) 0%,
    var(--lb-bg-bottom, #e0f4fd) 100%);
}

/* ---------- 3. A placed Figma layer ------------------------------------- */

/* The layer is a CLIP BOX carrying the Figma node's rect and rotation; the
   image inside is placed by the matrix taken from the Figma PDF export. One
   model covers scaleMode FILL, scaleMode CROP, and the layers Figma draws
   rotated inside an unrotated box (the olive skewer, the pearl hat-pin) —
   which `object-fit: cover` cannot express at all. */
.lb-l {
  position: absolute;
  left: calc(var(--x) * var(--u));
  top: calc(var(--y) * var(--u));
  width: calc(var(--w) * var(--u));
  height: calc(var(--hh) * var(--u));
  transform-origin: 0 0;
  rotate: calc(var(--r, 0) * 1deg);
  overflow: hidden;
  pointer-events: none;
}
.lb-l > img,
.lb-l > a > img {
  position: absolute;
  display: block;
  left: calc(var(--ix, 0) * var(--u));
  top: calc(var(--iy, 0) * var(--u));
  /* fall back to filling the box: a missing --iw would otherwise make the
     whole declaration invalid and drop the image to its intrinsic size */
  width: calc(var(--iw, var(--w)) * var(--u));
  height: calc(var(--ih, var(--hh)) * var(--u));
  transform-origin: 0 0;
  rotate: calc(var(--irot, 0) * 1deg);
  max-width: none;
}
/* `display: contents` so the image keeps the placement rules above — the
   anchor generates no box of its own. That leaves the IMAGE as the hit
   target, and every .lb-l is pointer-events:none, so without this a product
   link is inert: the tap lands on nothing and never reaches the anchor to
   bubble. Only images that are actually inside a link are re-armed. */
.lb-l > a { display: contents; }
.lb-l > a > img { pointer-events: auto; cursor: pointer; }

/* Feather a layer in (and optionally out again) down its own box. The picnic
   cloth uses it because Figma fades it in over the hero with a gradient; the
   black cloth uses all four stops so its hard top and bottom edges dissolve
   into the page background instead of banding. */
.lb-l--fade > img {
  -webkit-mask-image: linear-gradient(to bottom,
      transparent calc(var(--f0) * 1%), #000 calc(var(--f1) * 1%),
      #000 calc(var(--f2, 100) * 1%), transparent calc(var(--f3, 100) * 1%));
          mask-image: linear-gradient(to bottom,
      transparent calc(var(--f0) * 1%), #000 calc(var(--f1) * 1%),
      #000 calc(var(--f2, 100) * 1%), transparent calc(var(--f3, 100) * 1%));
}
/* an inline vector fills its layer box */
.lb-l--vec > svg,
.lb-l--vec > img { display: block; width: 100%; height: 100%; }

/* The design recolours the same flame emoji per section — orange, blue,
   purple. One source PNG plus a hue rotation is exactly that, and keeps three
   variants from becoming nine files. */
.lb-l[style*="--hue"] > img { filter: hue-rotate(var(--hue, 0deg)); }

.lb-l--tap { pointer-events: auto; cursor: pointer; }
.lb-l--solid { background: var(--fill); }

/* ---------- 4. Type ----------------------------------------------------- */

.lb-t {
  position: absolute;
  left: calc(var(--x) * var(--u));
  top: calc(var(--y) * var(--u));
  /* +1 letter-space track: CSS puts a trailing track after the LAST glyph and
     Figma does not, so every tracked line measures one track wider here than
     in the design. Both italic notes overflowed their Figma box by exactly
     that (929.8 into 928, 835.8 into 834) and wrapped to two lines where the
     design has one. The box grows by a track and shifts back half of one, so
     the centred glyph run lands exactly where it did before. */
  width: calc((var(--w) + var(--ls, 0)) * var(--u));
  margin: 0 0 0 calc(-0.5 * var(--ls, 0) * var(--u));
  /* Every line break on this page is authored (the copy carries real newlines,
     which Liquid turns into <br>), so auto-wrapping can only ever DISAGREE
     with the design — and it did: the formal note sat 0.0px inside its box and
     still broke. nowrap reproduces the designed line structure exactly. */
  white-space: nowrap;
  font-size: calc(var(--fs) * var(--u));
  line-height: var(--lh, 1.1);
  letter-spacing: calc(var(--ls, 0) * var(--u));
  font-weight: var(--fw, 400);
  text-align: center;
  color: var(--fg, currentColor);
  /* No text-indent trick here: it only ever shifts the FIRST line, so on the
     two- and three-line captions it pulls them out of alignment with each
     other. The half-track offset that letter-spacing introduces is absorbed
     into each block's own --x during calibration instead. */
}
.lb-t--left { text-align: left; }
.lb-t--script {
  letter-spacing: 0;
  font-weight: 400;
}
/* A wordmark that ships as artwork rather than as set type: the box IS the
   ink box, so the SVG fills it exactly and lands where the design puts it. */
.lb-t--art { height: calc(var(--hh) * var(--u)); }
.lb-t--art > img { display: block; width: 100%; height: 100%; }

/* The call to action ships as outlined artwork (the spoon and the magnifying
   glass are drawn, not typed), so this <a> keeps the link and the accessible
   text while showing nothing itself. */
.lb-t--ghost { color: transparent; height: calc(var(--hh) * var(--u)); }

.lb-t--cap { text-transform: capitalize; }
.lb-t--upper { text-transform: uppercase; }
.lb-t--italic { font-style: italic; }
.lb-t--small { font-variant-caps: all-small-caps; }

/* Curved captions ship as inline SVG (exact Figma path). The twin below keeps
   them readable to screen readers and to search. */
.lb-curve { position: absolute; left: calc(var(--x) * var(--u)); top: calc(var(--y) * var(--u));
  width: calc(var(--w) * var(--u)); height: calc(var(--hh) * var(--u));
  transform-origin: 0 0; rotate: calc(var(--r, 0) * 1deg); pointer-events: none; }
.lb-curve svg { width: 100%; height: 100%; display: block; overflow: visible; }
.lb-sr {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* ---------- 5. Scroll-driven rotation ----------------------------------- */

/* A wrapper that pivots about a point in design px. Several wrappers sharing a
   group id and a pivot rotate as one rigid body, which is how a plate and the
   bag sitting on it stay locked together.

   It is ZERO-SIZED on purpose. Its children are absolutely positioned, so they
   still lay out from the artboard origin — but the wrapper itself no longer
   claims a 1080 x 8185 box. That box was doing real damage twice over: it sat
   on top of everything painted before it and swallowed every tap meant for a
   layer underneath, and with `will-change` on it Safari was asked to keep a
   dozen full-page composited layers alive at once, which on a phone exhausts
   the tile budget and the rotation simply stops updating.

   Do not give these wrappers a size, and do not put `will-change` back. */
.lb-spin {
  position: absolute;
  left: 0; top: 0; width: 0; height: 0;
  pointer-events: none;
  transform-origin: calc(var(--px) * var(--u)) calc(var(--py) * var(--u));
  rotate: var(--lb-rot, 0deg);
}

/* The badge ring needs TWO rotations with different origins: the designed
   angle positions the circle around the pearl and pivots about the box corner
   (.lb-curve, origin 0 0), while the scroll spin must turn about the circle's
   own centre. Put them on the same element and either the base angle is lost
   or the ring swings away from the pearl as you scroll, so the spin lives on
   the inner SVG. */
.lb-curve > svg.lb-ring {
  overflow: visible;   /* the divider tick reaches outside the text circle */
  transform-origin: 50% 50%;
  rotate: calc(var(--lb-ring-deg, 0) * 1deg);
}

/* The product turns continuously on its own centre, INSIDE the plate's
   scroll-driven group. Two rotations about two different points cannot share
   one element — `rotate` is a single property — so this is its own wrapper,
   zero-sized for the same reason as .lb-spin. */
/* 1 everywhere; lunch-b.js overrides it to .5 on <html class="lb-webkit"> */
:root { --lb-turnf: 1; }
.lb-webkit { --lb-turnf: .5; }

.lb-turn {
  position: absolute;
  left: 0; top: 0; width: 0; height: 0;
  pointer-events: none;
  transform-origin: calc(var(--px) * var(--u)) calc(var(--py) * var(--u));
  /* LONGHAND, deliberately. This was the one animation on the page written as
     a shorthand carrying a var() — `animation: lb-turn var(--turn) linear
     infinite` — and the one animation reported dead on iPhone. Safari has a
     long history of failing to substitute a custom property inside the
     `animation` shorthand, which invalidates the whole declaration and leaves
     the element simply not animating. Longhand substitutes reliably. */
  animation-name: lb-turn;
  /* --lb-turnf is the ENGINE factor, set to .5 on .lb-webkit by lunch-b.js.
     WebKit runs this turn visibly slower than Blink at the same declared
     duration — reported side by side on iPhone vs Android and Windows — so
     iOS and Safari get half the seconds, i.e. twice the speed. Everything
     else keeps the theme's own turn_secs untouched. */
  animation-duration: calc(var(--turn, 14.5s) * var(--lb-turnf, 1));
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
/* `transform` rather than the standalone `rotate` for the same reason: this
   wrapper exists only to turn, so it gives up nothing, and transform is the
   one form no engine has ever disagreed about. */
@keyframes lb-turn { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

/* Colourways of one product, stacked in the same box. Only `is-on` shows, and
   the script advances it on each completed turn, so the colour changes at the
   moment the product comes back round to where it started. */
.lb-l--cyc > img,
.lb-l--cyc > a > img { opacity: 0; transition: opacity var(--fade, 600ms) ease; }
.lb-l--cyc > img.is-on,
.lb-l--cyc > a > img.is-on { opacity: 1; }

/* ---------- 6. Ambient motion ------------------------------------------- */

/* transform, NOT the standalone `translate` property, and literal px, NOT
   var(--u) amplitudes: iPhone Safari shipped these floats frozen — the same
   engine that already forced .lb-turn and the skewer spin onto `transform`.
   The amplitudes are the design's -22/-14/-46 design px at each breakpoint's
   --u cap; being ambient drift, the sub-pixel error below the cap is
   invisible, and a fixed number is the one form WebKit cannot refuse.
   The switch off `translate` also ends a silent clash: the reveal transition
   animates `translate` on the same layers, and whichever loop was running
   overrode the reveal's slide for as long as it lived. */
@keyframes lb-float {
  0%, 100% { transform: translate(0, 0); }
  50%      { transform: translate(0, -11px); }
}
@keyframes lb-float-soft {
  0%, 100% { transform: translate(0, 0); }
  50%      { transform: translate(3px, -7px); }
}
/* The two hero props the reader actually looks at — the olive skewer and the
   sandwich — drift twice as far as the scatter of small props around them. */
@keyframes lb-float-big {
  0%, 100% { transform: translate(0, 0); }
  50%      { transform: translate(0, -23px); }
}
@media (min-width: 20000px) {
  @keyframes lb-float {
    0%, 100% { transform: translate(0, 0); }
    50%      { transform: translate(0, -22px); }
  }
  @keyframes lb-float-soft {
    0%, 100% { transform: translate(0, 0); }
    50%      { transform: translate(6px, -14px); }
  }
  @keyframes lb-float-big {
    0%, 100% { transform: translate(0, 0); }
    50%      { transform: translate(0, -46px); }
  }
}

.lb-float      { animation: lb-float 5.2s ease-in-out infinite; }
.lb-float--alt { animation: lb-float-soft 6.4s ease-in-out infinite; }
.lb-float--d1  { animation-delay: -1.3s; }
.lb-float--d2  { animation-delay: -2.6s; }
.lb-float--d3  { animation-delay: -3.9s; }

/* ---------- 7. Scroll reveal -------------------------------------------- */

.lb-anim {
  opacity: 0;
  transition: opacity .8s ease, translate .8s ease, scale .8s ease;
}
.lb-anim.is-in { opacity: 1; translate: none; scale: none; }
.lb-anim--up   { translate: 0 calc(70 * var(--u)); }
.lb-anim--zoom { scale: .94; }

/* ---------- 8. Hero prop: the olive skewer ------------------------------ */

/* The hero prop turns on the spot as the page scrolls, about its own centre.
   The scroll turn and the tap turn sit on separate wrappers, and the prop's
   own designed angle stays on the image itself — three rotations that must
   not overwrite each other, since `rotate` is a single property. */
/* zero-sized for the same reason as .lb-spin — a full-artboard wrapper here was
   the thing sitting on top of the jalapeno and eating its tap */
/* Pure screen-space hover, outside the orbit so the rotation cannot swing it */
.lb-hover {
  position: absolute;
  left: 0; top: 0; width: 0; height: 0;
  pointer-events: none;
  animation: lb-float-big 5.9s ease-in-out infinite;
  animation-delay: -1.8s;
}

.lb-orbit,
.lb-tapspin {
  position: absolute;
  left: 0; top: 0; width: 0; height: 0;
  pointer-events: none;
  transform-origin: calc(var(--px) * var(--u)) calc(var(--py) * var(--u));
}
/* The jalapeno drifts like the sandwich does. The float has to live on .lb-orbit
   because that element's `rotate` is set as a PROPERTY (by the scroll handler),
   not by an animation, so an animation on `translate` here collides with
   nothing. .lb-tapspin below it is already spoken for by the tap turn. */
.lb-orbit {
  rotate: var(--lb-orbit, 0deg);

}
/* Ten full turns, spun up and left to run down like a real flicked skewer.
   The curve is easeOutQuad, which is the actual physics: something slowing
   under constant friction loses speed linearly, so the angle it has covered is
   quadratic in time. That puts peak speed at exactly twice the average and the
   halfway point at 29% of the dwell — measured, not guessed. Sharper curves
   were tried and rejected: cubic-bezier(.25,.5,.2,1) peaks at 5.5 turns/s and
   the skewer strobes into an unreadable smear for the first second.
   `transform: rotate()` rather than the standalone `rotate` property, for the
   same reason .lb-turn uses it — Safari is unreliable animating the individual
   transform properties, and this wrapper sets no other transform. */
.lb-tapspin.is-spinning { animation: lb-skewer-spin 5.4s cubic-bezier(.5,1,.89,1) 1; }
@keyframes lb-skewer-spin { from { transform: rotate(0deg); } to { transform: rotate(3600deg); } }

/* ---------- 9. Hero prop: the sandwich ---------------------------------- */

/* Starts deconstructed (offsets are the designer's own exploded pose from
   Group 4201) and stacks bottom-up on arrival in casual, or on a tap. */
.lb-slice {
  translate: calc(var(--ex) * var(--u) * var(--exf, 1))
             calc(var(--ey) * var(--u) * var(--exf, 1));
  opacity: 0;
  /* Half speed (2026-08-19 request): .72s/.38s/110ms doubled. The stagger has
     to double with the durations or the slices bunch up and the build stops
     reading bottom-up. */
  transition: translate 1.44s cubic-bezier(.22,1,.36,1),
              opacity .76s ease;
  transition-delay: calc(var(--i) * 220ms);
}
.lb-stack.is-stacked .lb-slice { translate: none; opacity: 1; }
/* while resetting, the slices JUMP back to the exploded pose — without this,
   removing is-stacked merely starts a slow transition outward and re-adding it
   turns straight back: a tap then reads as a jiggle, not a rebuild */
.lb-stack.is-resetting .lb-slice { transition: none !important; }

/* Once assembled it drifts, the same gentle idle the props have. The float
   lives on the wrapper and the stacking on the slices, so the two `translate`
   animations are on different elements and cannot cancel each other. */
.lb-stack { position: absolute; left: 0; top: 0; width: 0; height: 0;
            pointer-events: none; }
.lb-stack.is-stacked { animation: lb-float-big 5.6s ease-in-out infinite; }
.lb-stack__hit {
  position: absolute;
  left: calc(var(--x) * var(--u));
  top: calc(var(--y) * var(--u));
  width: calc(var(--w) * var(--u));
  height: calc(var(--hh) * var(--u));
  padding: 0; border: 0; background: none;
  pointer-events: auto;
  cursor: pointer;
  z-index: 7;
  -webkit-tap-highlight-color: transparent;
}
/* The button is sized to the artwork exactly, which on a phone is a ~76x64px
   target — reachable, but unforgiving of a thumb landing slightly off. This
   grows the hit region well past the sandwich without changing any layout. */
.lb-stack__hit::after {
  content: "";
  position: absolute;
  inset: calc(-70 * var(--u));
}
.lb-stack__hit:focus-visible { outline: 2px solid currentColor; outline-offset: 4px; }

/* Reduced motion DAMPS this page; it does not switch it off. Cancelling the
   stack outright meant a tap on the sandwich did nothing whatsoever, which
   reads as a broken page rather than a considerate one. What goes is the
   motion the reader never asked for — the idle drift. What stays, shortened
   and shallower, is the motion they triggered themselves. */
@media (prefers-reduced-motion: reduce) {
  .lb-page { --exf: .3; }
  .lb-slice {
    transition: translate .8s ease, opacity .56s ease;
    transition-delay: calc(var(--i) * 120ms);
  }
  /* the assembled sandwich keeps its full drift — see section 14b */
}

@media (prefers-reduced-motion: reduce) {
  /* The floats deliberately have NO rule here — see section 14b. `animation:
     none` once froze the olive skewer and the sandwich on an iPhone with
     Reduce Motion on, and damping them then made the hover read as broken
     next to Android. Nothing on this page may switch a loop off. */
  /* keeps the engine factor: reduced motion damps whatever the base is,
     so WebKit lands at 1.5x the theme duration, not 3x */
  .lb-turn { animation-duration: calc(var(--turn, 14.5s) * var(--lb-turnf, 1) * 3); }
  .lb-anim { opacity: 1; translate: none; scale: none; transition: none; }
  /* The tap turn survives — the reader asked for it — but at three turns, not
     ten. Shortening the DWELL here (what this rule used to do) would have made
     it spin FASTER for the same ten turns, which is precisely backwards: the
     discomfort is in the speed and the sweep, not the seconds. Fewer turns over
     a proportionate time is the calm version. */
  .lb-tapspin.is-spinning { animation-name: lb-skewer-spin--calm; animation-duration: 2.6s; }
  @keyframes lb-skewer-spin--calm {
    from { transform: rotate(0deg); } to { transform: rotate(1080deg); }
  }
}


/* ---------- 10. The envelope & letter — the Claude Design component -------- */

/* Everything below the shim is the component from the Claude Design project
   (lunch-b-envelope.html), imported VERBATIM — geometry, artwork stand-ins,
   letter faces, knife, timings and all. Do not restyle it here; edit the
   design project and re-import.

   Only this placement shim is ours. The component was built on its own
   1080-unit artboard with its envelope 620u (casual) / 560u (formal) wide.
   --k rescales that artboard so the component's envelope has exactly the
   footprint the old photographed envelope had, and the wrapper pins the
   stage at the artboard position that puts it there. --uu restates the page
   unit because a child cannot override --u in terms of itself. */
.lb-page { --uu: min(0.0925926vw, 0.68px); }   /* must track --u above */
@media (min-width: 20000px) { .lb-page { --uu: min(0.0520833vw, 1px); } }

.lb-cdstage {
  position: absolute;
  left: calc(var(--x) * var(--uu));
  top: calc(var(--y) * var(--uu));
  --u: calc(var(--uu) * var(--k));
  /* The stage spans its full 1080-unit artboard, which overlaps the CTA above
     the envelope. Taps land only on the component's own pieces; everything
     else falls through to the page. */
  pointer-events: none;
}
@media (min-width: 20000px) {
  .lb-cdstage { left: calc(var(--dx) * var(--uu)); top: calc(var(--dy) * var(--uu)); }
}
.lb-cdstage .lb-stage { pointer-events: none; }
.lb-cdstage .lb-env,
.lb-cdstage .lb-label,
.lb-cdstage .lb-knife-arm { pointer-events: auto; cursor: pointer; }
/* Aug-30 client request: the badge props hold still — no idle bob on the
   olive skewer or the sandwich stack. Only these two; every other prop keeps
   its gentle float. */
.lb-hover:has(.lb-skewer) { animation: none !important; }
.lb-stack { animation: none !important; }

/* Aug-30 PDFs put TAP AND OPEN BELOW the envelope (the component draws it
   above). Offsets are in artboard design px via --uu, not the stage's scaled
   --u, so they stay exact regardless of cd_k. */
.lb-page--formal .lb-cdstage .lb-label { translate: 0 calc(974 * var(--uu)); }
.lb-page--casual .lb-cdstage .lb-label { translate: 0 calc(867 * var(--uu)); }

/* ---------- the component, verbatim ---------- */
/* Claude Design "Lunch-B Envelope (new)" — imported 2026-08-18. Supersedes the
   previous component: the formal envelope is now the same 620x489 as the
   casual one (was 560x452), the paper carries grain/mottle/fibre texture, and
   the close-on-second-tap is native to the component, so our old 10x/10y
   extensions are gone with it. The demo harness (its own --u, page background
   and captions) is stripped — this page supplies those. */
  /* ── Stage: one relatively-positioned artboard, everything in units of --u ── */
  .lb-stage {
    position: relative;
    width: calc(1080 * var(--u));
    height: calc(1100 * var(--u));
    cursor: pointer;
    touch-action: pan-y;
    -webkit-tap-highlight-color: transparent;
    user-select: none; -webkit-user-select: none;
  }
  .lb-stage * { box-sizing: border-box; }

  /* Theme geometry (measured off the artwork) */
  .lb-page--casual .lb-stage { --ew: calc(620 * var(--u)); --eh: calc(489 * var(--u)); --fd: calc(279 * var(--u)); /* 57% */
                               --sw: calc(114 * var(--u)); --sh: calc(112 * var(--u));
                               --lw: calc(510 * var(--u)); --lh: calc(400 * var(--u));
                               --kdx: calc(-350 * var(--u)); --kdy: calc(70 * var(--u)); }
  .lb-page--formal .lb-stage { --ew: calc(620 * var(--u)); --eh: calc(489 * var(--u)); --fd: calc(357 * var(--u)); /* ~73% */
                               --sw: calc(111 * var(--u)); --sh: calc(108 * var(--u));
                               --lw: calc(510 * var(--u)); --lh: calc(400 * var(--u));
                               --kdx: calc(-340 * var(--u)); --kdy: calc(140 * var(--u)); }

  /* ── Label ── */
  .lb-label {
    position: absolute; left: 0; right: 0; top: calc(474 * var(--u));
    text-align: center; font-size: calc(44 * var(--u)); letter-spacing: 0.42em;
    text-indent: 0.42em; font-weight: 500; z-index: 8;
    transition: opacity 0.45s ease;
  }
  .lb-page--casual .lb-label { color: #0c3669; }
  .lb-page--formal .lb-label { color: #ffffff; }
  .lb-stage.is-cut .lb-label { opacity: 0; }

  /* ── Envelope group ── */
  .lb-env {
    position: absolute; left: calc(50% - var(--ew) / 2); top: calc(560 * var(--u));
    width: var(--ew); height: var(--eh);
    perspective: calc(1600 * var(--u));
  }

  /* paper grain — inline SVG noise: fine tooth + coarse pulp mottle, tiled */
  .lb-stage { --grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 0.45 0 0 0 0 0.42 0 0 0 0 0.36 0 0 0 0.55 0'/></filter><rect width='140' height='140' filter='url(%23n)'/></svg>");
               --mottle: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='260' height='260'><filter id='m'><feTurbulence type='fractalNoise' baseFrequency='0.045' numOctaves='3' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 0.5 0 0 0 0 0.46 0 0 0 0 0.38 0 0 0 0.4 0'/></filter><rect width='260' height='260' filter='url(%23m)'/></svg>");
               --fiber: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='f'><feTurbulence type='turbulence' baseFrequency='0.012 0.35' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='matrix' values='0 0 0 0 0.55 0 0 0 0 0.5 0 0 0 0 0.42 0 0 0 0.25 0'/></filter><rect width='200' height='200' filter='url(%23f)'/></svg>"); }
  .lb-env__body::after, .lb-env__flap::after, .lb-env__letter::after {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    background: var(--mottle), var(--grain);
    background-size: calc(260 * var(--u) * 2.2), calc(140 * var(--u) * 2.6);
    opacity: 0.22; mix-blend-mode: multiply; border-radius: inherit;
  }
  .lb-env__flap::after { opacity: 0.26; }
  .lb-env__letter::after {
    background: var(--fiber), var(--mottle), var(--grain);
    background-size: calc(200 * var(--u) * 2.4), calc(260 * var(--u) * 2.2), calc(140 * var(--u) * 2.6);
    opacity: 0.2;
  }

  /* body — photo slot */
  .lb-env__body {
    position: absolute; inset: 0; z-index: 3;
    box-shadow: 0 calc(20 * var(--u)) calc(46 * var(--u)) rgba(0,0,0,0.28);
  }
  .lb-page--casual .lb-env__body {
    background:
      linear-gradient(180deg, rgba(0,0,0,0.18), rgba(0,0,0,0.06) calc(50 * var(--u)), rgba(0,0,0,0) calc(110 * var(--u))),
      repeating-linear-gradient(90deg, #9c2f35 0, #9c2f35 calc(19.5 * var(--u)), #c8a08e calc(19.5 * var(--u)), #c8a08e calc(39 * var(--u)));
    border-radius: calc(6 * var(--u));
  }
  .lb-page--formal .lb-env__body {
    background:
      linear-gradient(180deg, rgba(0,0,0,0.12), rgba(0,0,0,0.04) calc(60 * var(--u)), rgba(0,0,0,0) calc(120 * var(--u))),
      linear-gradient(180deg, #eee3cc, #e4d7ba);
    border-radius: calc(6 * var(--u));
  }

  /* pocket mouth — the "open envelope" photo slot; cross-fades in as the flap lifts */
  .lb-env__open {
    position: absolute; left: 0; right: 0; top: 0; height: calc(30 * var(--u)); z-index: 4;
    background: linear-gradient(180deg, rgba(0,0,0,0.42), rgba(0,0,0,0) 92%);
    border-radius: calc(6 * var(--u)) calc(6 * var(--u)) 0 0;
    opacity: 0; transition: opacity 0.5s ease 0.4s;
  }
  .lb-stage.is-cut .lb-env__open { opacity: 1; }

  /* flap — hinges on the envelope's top edge; photo slot */
  .lb-env__flap {
    position: absolute; left: 0; top: 0; width: 100%; height: var(--fd); z-index: 5;
    transform-origin: 50% 0;
    transform: rotateX(0deg);
    transition: transform 0.75s cubic-bezier(0.52, 0.04, 0.22, 1) 0.35s;
    clip-path: polygon(0 0, 100% 0, 52% 97%, 48% 97%);
    filter: drop-shadow(0 calc(14 * var(--u)) calc(18 * var(--u)) rgba(0,0,0,0.42));
  }
  .lb-page--casual .lb-env__flap {
    background:
      linear-gradient(180deg, rgba(255,255,255,0.22), rgba(255,255,255,0.06) 34%, rgba(0,0,0,0.05) 68%, rgba(0,0,0,0.28)),
      linear-gradient(180deg, #2f5fae, #1e4587 60%, #163567);
    box-shadow: inset 0 calc(-4 * var(--u)) calc(8 * var(--u)) rgba(8,20,45,0.5);
  }
  .lb-page--formal .lb-env__flap {
    background:
      radial-gradient(ellipse 46% 34% at 50% 38%, rgba(0,0,0,0.2), rgba(0,0,0,0) 70%),
      radial-gradient(ellipse 70% 55% at 50% 40%, rgba(255,255,255,0.08), rgba(0,0,0,0) 65%),
      linear-gradient(180deg, #4a3b28, #3a2d1e 55%, #2a1f13);
    box-shadow: inset 0 calc(-4 * var(--u)) calc(8 * var(--u)) rgba(0,0,0,0.55);
    /* scalloped edge stand-in */
    clip-path: polygon(0 0, 100% 0, 92% 22%, 96% 34%, 84% 46%, 88% 58%, 72% 68%, 74% 80%, 58% 88%, 52% 97%, 48% 97%, 42% 88%, 26% 80%, 28% 68%, 12% 58%, 16% 46%, 4% 34%, 8% 22%);
  }
  .lb-stage.is-cut .lb-env__flap { transform: rotateX(180deg); }
  .lb-stage.is-lifting .lb-env__flap { z-index: 1; }

  /* wax seal, intact — photo slot */
  .lb-env__seal {
    position: absolute; z-index: 6;
    left: calc(50% - var(--sw) / 2); top: calc(var(--fd) - var(--sh) / 2 + 17 * var(--u));
    width: var(--sw); height: var(--sh); border-radius: 50%;
    transition: opacity 0.2s ease;
  }
  .lb-page--casual .lb-env__seal {
    /* longhand, and the url comes from lb-theme.liquid — see the note there */
    background-image: var(--lb-cd-seal);
    background-position: center;
    background-size: contain;
    background-repeat: no-repeat;
    filter: drop-shadow(0 calc(5 * var(--u)) calc(9 * var(--u)) rgba(0,0,0,0.28));
  }
  /* The export ships the FORMAL seal as a flat radial-gradient disc — its own
     comments call these "photo slots", and the casual one was the only slot
     that got filled. Next to a real embossed wax seal on the casual envelope
     the disc read as unfinished, so it takes the design's own artwork too
     (lb-wax-seal-formal, the ornate purple crest). Box ratio 111:108 matches
     the source 902:877, so `contain` lands it exactly. */
  .lb-page--formal .lb-env__seal {
    background-image: var(--lb-cd-seal-formal);
    background-position: center;
    background-size: contain;
    background-repeat: no-repeat;
    filter: drop-shadow(0 calc(5 * var(--u)) calc(10 * var(--u)) rgba(0,0,0,0.38));
  }
  .lb-stage.is-cut .lb-env__seal { opacity: 0; }

  /* severed wax — one slot; halves drawn as pseudos so the stand-in visibly parts */
  .lb-env__cut {
    position: absolute; z-index: 6;
    left: calc(50% - var(--sw) / 2); top: calc(var(--fd) - var(--sh) / 2 + 17 * var(--u));
    width: var(--sw); height: var(--sh);
    opacity: 0; pointer-events: none;
  }
  .lb-stage.is-cut .lb-env__cut { animation: lb-cutwax 1.5s ease both; }
  @keyframes lb-cutwax { 0% { opacity: 0; } 10% { opacity: 1; } 50% { opacity: 1; } 100% { opacity: 0; } }
  .lb-env__cut::before, .lb-env__cut::after {
    content: ""; position: absolute; top: 0; width: 50%; height: 100%;
    transition: transform 0.45s cubic-bezier(0.2, 0.7, 0.3, 1);
  }
  .lb-page--casual .lb-env__cut::before, .lb-page--casual .lb-env__cut::after {
    background-image: var(--lb-cd-seal); background-repeat: no-repeat; background-size: 200% 100%;
    filter: drop-shadow(0 calc(4 * var(--u)) calc(8 * var(--u)) rgba(0,0,0,0.25)); }
  .lb-page--casual .lb-env__cut::before { left: 0; background-position: 0 0; }
  .lb-page--casual .lb-env__cut::after  { right: 0; background-position: 100% 0; }
  .lb-page--formal .lb-env__cut::before, .lb-page--formal .lb-env__cut::after {
    background-image: var(--lb-cd-seal-formal); background-repeat: no-repeat; background-size: 200% 100%;
    filter: drop-shadow(0 calc(4 * var(--u)) calc(8 * var(--u)) rgba(0,0,0,0.28)); }
  .lb-page--formal .lb-env__cut::before { left: 0; background-position: 0 0; }
  .lb-page--formal .lb-env__cut::after  { right: 0; background-position: 100% 0; }
  .lb-stage.is-cut .lb-env__cut::before { transform: translate(calc(-26 * var(--u)), calc(6 * var(--u))) rotate(-9deg); }
  .lb-stage.is-cut .lb-env__cut::after  { transform: translate(calc(26 * var(--u)), calc(-4 * var(--u))) rotate(7deg); }

  /* ── The letter (live DOM, not a photo slot) ── */
  .lb-env__letter {
    position: absolute; z-index: 2;
    left: calc(50% - var(--lw) / 2); top: calc(45 * var(--u));
    width: var(--lw); height: var(--lh);
    transform-origin: 50% 100%;
    transform: translateY(0) rotate(0deg);
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    text-align: center; padding: calc(34 * var(--u)) calc(40 * var(--u));
    box-shadow: 0 calc(14 * var(--u)) calc(34 * var(--u)) rgba(0,0,0,0.25),
                inset 0 0 calc(30 * var(--u)) rgba(165,140,100,0.16),
                inset 0 calc(1 * var(--u)) 0 rgba(255,255,255,0.7);
    border-radius: calc(4 * var(--u));
  }
  .lb-stage.is-lifting .lb-env__letter { animation: lb-lift 1.3s cubic-bezier(0.4, 0, 0.3, 1) both; }
  .lb-stage.is-open .lb-env__letter { transform: translateY(-103%) rotate(0deg); } /* self-heal floor */
  @keyframes lb-lift {
    0%   { transform: translateY(0) rotate(0deg); animation-timing-function: cubic-bezier(0.6, 0.02, 0.5, 0.9); }
    16%  { transform: translateY(-7%) rotate(0.4deg); animation-timing-function: cubic-bezier(0.35, 0, 0.2, 1); } /* resistance */
    68%  { transform: translateY(-112%) rotate(-2.4deg); animation-timing-function: cubic-bezier(0.35, 0, 0.35, 1); } /* overshoot */
    84%  { transform: translateY(-98%) rotate(0.9deg); animation-timing-function: cubic-bezier(0.35, 0, 0.35, 1); }
    100% { transform: translateY(-103%) rotate(0deg); }
  }
  /* casual letter — mokobara receipt */
  .lb-page--casual .lb-env__letter {
    background:
      linear-gradient(180deg, rgba(255,255,255,0.35), rgba(255,255,255,0) 12%),
      linear-gradient(180deg, #f6efe1, #f0e6d2);
    align-items: stretch; justify-content: flex-start;
    padding: calc(30 * var(--u)) calc(38 * var(--u)) calc(26 * var(--u));
  }
  .lb-letter__brand { font-size: calc(30 * var(--u)); font-weight: 700; color: #33383f; letter-spacing: 0.01em; margin-bottom: calc(16 * var(--u)); }
  .lb-letter__dots { width: 100%; border: 0; border-top: calc(3 * var(--u)) dotted rgba(70,70,70,0.4); margin: 0 0 calc(16 * var(--u)); }
  .lb-letter__meta { display: flex; justify-content: space-between; font-size: calc(22 * var(--u)); font-weight: 700; color: #33383f; margin-bottom: calc(16 * var(--u)); }
  .lb-letter__items { display: flex; flex-direction: column; gap: calc(13 * var(--u)); margin-bottom: calc(18 * var(--u)); }
  .lb-letter__item { display: flex; align-items: baseline; gap: calc(8 * var(--u)); font-size: calc(21 * var(--u)); color: #767b82; text-align: left; }
  .lb-letter__item i { flex: 1; border-bottom: calc(2 * var(--u)) dotted rgba(118,123,130,0.6); min-width: calc(20 * var(--u)); }
  .lb-letter__scrawl {
    position: absolute; left: calc(-14 * var(--u)); right: calc(-14 * var(--u)); top: calc(120 * var(--u));
    transform: rotate(-9deg);
    font-family: "Snell Roundhand", "Apple Chancery", "Segoe Script", "Brush Script MT", cursive;
    font-size: calc(56 * var(--u)); line-height: 1.02; color: #1e3d9e; font-weight: 700;
    text-align: center; pointer-events: none;
    text-shadow: 0 calc(1 * var(--u)) 0 rgba(30,61,158,0.25);
  }
  /* formal letter — white card */
  .lb-page--formal .lb-env__letter {
    background:
      linear-gradient(180deg, rgba(255,255,255,0.5), rgba(255,255,255,0) 12%),
      linear-gradient(180deg, #ffffff, #f5f3ec);
    justify-content: center; padding: calc(36 * var(--u)) calc(30 * var(--u));
  }
  .lb-letter__script {
    font-family: "Snell Roundhand", "Apple Chancery", "Segoe Script", "Brush Script MT", cursive;
    font-size: calc(48 * var(--u)); line-height: 1.18; color: #4c4f52; margin: 0 0 calc(24 * var(--u)); text-wrap: pretty;
  }
  .lb-letter__sub { font-size: calc(24 * var(--u)); line-height: 1.45; color: #55595e; margin: 0; }

  /* ── Knife: .lb-knife is the swappable slot; the arm does the travel ── */
  .lb-knife-arm {
    position: absolute; z-index: 7;
    left: calc(800 * var(--u)); top: calc(620 * var(--u));
    width: calc(150 * var(--u)); height: calc(280 * var(--u));
  }
  .lb-stage.is-cutting .lb-knife-arm { animation: lb-cutpass 0.9s cubic-bezier(0.5, 0.05, 0.3, 1) forwards; }
  .lb-stage.is-cut .lb-knife-arm { opacity: 0; transition: opacity 0.3s ease 0.15s; }
  @keyframes lb-cutpass {
    0%   { transform: translate(0, 0) rotate(0deg); }
    45%  { transform: translate(calc(var(--kdx) * 0.28), calc(var(--kdy) * 0.85)) rotate(-12deg); }
    100% { transform: translate(var(--kdx), var(--kdy)) rotate(-6deg); }
  }
  .lb-knife {
    position: absolute; inset: 0;
    transform: rotate(38deg);
    animation: lb-bob 1.53s ease-in-out infinite;
    background-image: var(--lb-cd-knife);
    background-position: center;
    background-size: contain;
    background-repeat: no-repeat;
    filter: drop-shadow(0 calc(8 * var(--u)) calc(14 * var(--u)) rgba(0,0,0,0.3));
  }
  @keyframes lb-bob {
    0%, 100% { transform: rotate(38deg) translateY(0); }
    50%      { transform: rotate(38deg) translateY(calc(-40 * var(--u))); }
  }
  .lb-stage.is-cutting .lb-knife { animation: none; transform: rotate(38deg); }

  /* ── Closing (reverse) ── */
  .lb-stage.is-closing .lb-env__letter { animation: lb-drop 0.85s cubic-bezier(0.55, 0, 0.45, 1) both; }
  @keyframes lb-drop {
    0%   { transform: translateY(-103%) rotate(0deg); }
    18%  { transform: translateY(-108%) rotate(-1.2deg); } /* picked up first */
    100% { transform: translateY(0) rotate(0deg); }
  }
  .lb-stage.is-closing .lb-env__seal { transition-delay: 1.6s; transition-duration: 0.4s; }

  /* ── Reduced motion: damped, never cancelled ── */
  @media (prefers-reduced-motion: reduce) {
    .lb-env__flap { transition-duration: 0.35s; transition-delay: 0.15s; }
    .lb-stage.is-lifting .lb-env__letter { animation: lb-lift-rm 0.5s ease-out both; }
    @keyframes lb-lift-rm {
      0%   { transform: translateY(-30%) rotate(0deg); opacity: 0.6; }
      100% { transform: translateY(-103%) rotate(0deg); opacity: 1; }
    }
    .lb-stage.is-cutting .lb-knife-arm { animation-duration: 0.4s; }
    .lb-env__open { transition-duration: 0.25s; transition-delay: 0.15s; }
    .lb-stage.is-closing .lb-env__letter { animation: lb-drop-rm 0.4s ease-out both; }
    @keyframes lb-drop-rm { 0% { transform: translateY(-103%); } 100% { transform: translateY(0); } }
    .lb-knife { animation: none; }
  }

/* ---------- 11. The theme switch ---------------------------------------- */

.lb-switch-host {
  /* the whole control is drawn in --u units, so scaling the unit scales the
     track, knob, type and the knob's slide together (+20%) */
  --u: calc(min(0.0925926vw, 0.5px) * 1.2);
  position: fixed;
  /* never above the artboard: --lb-clamp tracks the first artboard's top
     edge from lunch-b.js, so at the top of the page the pill sits INSIDE the
     page instead of riding over the storefront header */
  top: max(calc(var(--lb-switch-top, 96) * 1px), var(--lb-clamp, -999px));
  left: 50%;
  translate: -50% 0;
  z-index: 60;
  pointer-events: none;
}
.lb-switch-host.is-pinned { position: fixed; }

.lb-switch {
  position: relative;
  display: inline-flex;
  align-items: center;
  width: calc(343 * var(--u));
  height: calc(85 * var(--u));
  padding: 0;
  border: 0;
  border-radius: calc(48 * var(--u));
  /* A SOLID tone per theme rather than the flat white it shipped as: cream
     against the formal cloth, pale sky against the casual gingham. Both are
     theme settings, and both stay light enough for the black label.
     background-COLOR, not the shorthand: the shading below is background-image
     and the shorthand would wipe it every time the theme rule fires. */
  background-color: var(--lb-track, #efe6d7);
  /* Shading, in translucent black and white only, so the reader's own track
     colour still shows through: light down the top third, shade up from the
     bottom. That is what turns a flat capsule into a cylinder. */
  background-image:
    linear-gradient(180deg, rgba(255,255,255,.60), rgba(255,255,255,0) 44%),
    linear-gradient(0deg,   rgba(0,0,0,.15),       rgba(0,0,0,0)       48%);
  transition: background-color .45s ease,
              transform .13s ease,
              box-shadow .13s ease;
  /* the shine below is a full-bleed sweep, so it has to be clipped to the
     pill; isolation keeps it off the page's own stacking context */
  overflow: hidden;
  isolation: isolate;
  font-family: var(--lb-body);
  font-size: calc(25 * var(--u));
  line-height: 1.1;
  color: #000000;
  cursor: pointer;
  pointer-events: auto;
  /* Depth is four shadows, not one. A single soft blur reads as a sticker;
     a real object standing off a surface has a tight contact shadow directly
     under it and progressively softer, wider ambient ones behind that. The
     two insets are the edges — a hairline of light along the top rim, shade
     gathering under the bottom rim — which is the cue that actually sells
     "raised" at this size. */
  box-shadow:
    inset 0 calc(1.5 * var(--u)) 0 rgba(255,255,255,.85),
    inset 0 calc(-2.5 * var(--u)) calc(4 * var(--u)) rgba(0,0,0,.18),
    0 calc(2 * var(--u))  calc(3 * var(--u))  rgba(0,0,0,.16),
    0 calc(8 * var(--u))  calc(13 * var(--u)) rgba(0,0,0,.16),
    0 calc(18 * var(--u)) calc(30 * var(--u)) rgba(0,0,0,.18),
    0 calc(34 * var(--u)) calc(58 * var(--u)) rgba(0,0,0,.14);
  -webkit-tap-highlight-color: transparent;
}
/* Pressing it puts it back down on the page: the travel and the shadow
   collapse together, which is the only way a press reads as weight rather
   than as a colour change. */
.lb-switch:active {
  transform: translateY(calc(3 * var(--u))) scale(.988);
  box-shadow:
    inset 0 calc(1.5 * var(--u)) 0 rgba(255,255,255,.55),
    inset 0 calc(-2 * var(--u)) calc(4 * var(--u)) rgba(0,0,0,.22),
    0 calc(1 * var(--u)) calc(2 * var(--u))  rgba(0,0,0,.18),
    0 calc(4 * var(--u)) calc(8 * var(--u))  rgba(0,0,0,.16),
    0 calc(9 * var(--u)) calc(16 * var(--u)) rgba(0,0,0,.12);
}

/* The shine. One specular band travelling across the pill, then a long hold
   before the next pass — a continuous sweep reads as a loading shimmer. It is
   drawn OVER the labels on purpose (that is what a gloss does) which is why
   the peak alpha stays well under 1. `transform`, not the standalone
   `translate`, for the same reason as every other loop here. */
.lb-switch::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(100deg,
              rgba(255,255,255,0)   40%,
              rgba(255,255,255,.26) 46%,
              rgba(255,255,255,.60) 50%,
              rgba(255,255,255,.26) 54%,
              rgba(255,255,255,0)   60%);
  transform: translateX(-150%);
  animation-name: lb-shine;
  animation-duration: 4.8s;
  animation-timing-function: cubic-bezier(.35,0,.25,1);
  animation-iteration-count: infinite;
}
@keyframes lb-shine {
  0%   { transform: translateX(-150%); }
  42%  { transform: translateX(150%); }
  100% { transform: translateX(150%); }
}

.lb-switch__knob {
  position: absolute;
  top: calc(8 * var(--u));
  left: calc(10 * var(--u));
  width: calc(70 * var(--u));
  height: calc(70 * var(--u));
  border-radius: 50%;
  background-color: var(--lb-knob, #6c7919);
  /* a highlight up and left, the terminator down and right — the two together
     turn the disc into a bead sitting in the track */
  background-image:
    radial-gradient(circle at 34% 26%, rgba(255,255,255,.55), rgba(255,255,255,0) 48%),
    radial-gradient(circle at 66% 84%, rgba(0,0,0,.32),       rgba(0,0,0,0)       56%);
  box-shadow:
    inset 0 calc(2 * var(--u)) calc(4 * var(--u)) rgba(255,255,255,.30),
    inset 0 calc(-4 * var(--u)) calc(7 * var(--u)) rgba(0,0,0,.38),
    0 calc(3 * var(--u)) calc(7 * var(--u)) rgba(0,0,0,.34);
  transition: translate .45s cubic-bezier(.5,0,.2,1), background-color .45s ease;
}
.lb-switch__label {
  position: absolute;
  top: 50%;
  translate: 0 -50%;
  left: calc(97 * var(--u));
  white-space: nowrap;
  /* letterpress: one hairline of light directly under each glyph, so the type
     reads as stamped into the pill rather than laid on top of it */
  text-shadow: 0 calc(1.5 * var(--u)) 0 rgba(255,255,255,.7);
  transition: opacity .28s ease;
}
.lb-switch__label--casual { left: calc(97 * var(--u)); }
.lb-switch__label--formal { left: calc(28 * var(--u)); }

/* The "slide to unlock" shimmer: a highlight travelling left to right THROUGH
   the glyphs, not over them — the gradient is clipped to the text, so the
   letters themselves light up as it passes. Same 4.8s clock and the same
   sweep window as the pill gloss above, so the two read as one light crossing
   the control rather than two unrelated loops.

   Behind @supports because the whole effect rests on background-clip: text,
   and its failure mode is the worst kind — `color: transparent` with no clip
   leaves the label INVISIBLE. Engines that cannot clip keep plain black type.

   Direction: with a 300%-wide image, background-position 100% parks the bright
   band off the LEFT edge and 0% parks it off the RIGHT, so the sweep runs
   100% -> 0%. The peak stays mid-grey rather than white: black type on a light
   track has nowhere to go but lighter, and a white peak would blank the word
   out mid-sweep instead of glinting. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .lb-switch__label {
    background-image: linear-gradient(100deg,
                      var(--lb-label-ink, #000) 44%,
                      var(--lb-label-shine, #b2ada3) 50%,
                      var(--lb-label-ink, #000) 56%);
    background-size: 300% 100%;
    background-position: 100% 0;
    background-repeat: no-repeat;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
    animation-name: lb-label-shine;
    animation-duration: 4.8s;
    animation-timing-function: cubic-bezier(.35,0,.25,1);
    animation-iteration-count: infinite;
  }
  @keyframes lb-label-shine {
    0%   { background-position: 100% 0; }
    42%  { background-position: 0% 0; }
    100% { background-position: 0% 0; }
  }
}

/* casual active: knob slides right, label swaps */
[data-lb-theme="casual"] .lb-switch { background-color: var(--lb-track-casual, #d9ecfb); }
[data-lb-theme="casual"] .lb-switch__knob { translate: calc(254 * var(--u)) 0; background-color: var(--lb-knob-casual, #0d376a); }
[data-lb-theme="formal"] .lb-switch__label--formal,
[data-lb-theme="casual"] .lb-switch__label--casual { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .lb-switch__knob { transition: none; }
  /* damped, not cancelled — the page rule. A frozen sweep would just look
     like a stray gradient baked into the pill, and a frozen LABEL sweep would
     leave one word permanently half-lit. */
  .lb-switch::after { animation-duration: 14s; opacity: .5; }
  .lb-switch__label { animation-duration: 14s; }
}

/* ---------- 12. Host theme neutraliser ---------------------------------- */

/* The storefront theme paints every section wrapper opaque, which would cover
   the artboard entirely. Both the :has() rule and the .lb-host class (added by
   the script) exist so this survives without :has() support. */
.shopify-section:has(> .lb-page),
.shopify-section:has(> .lb-switch-host),
.shopify-section.lb-host {
  background: transparent !important;
  margin: 0 !important;
  padding: 0 !important;
  overflow: visible !important;
}

/* ---------- 13. Desktop ------------------------------------------------- */

/* A second, wider composition rather than a scaled-up phone. The artboard
   becomes 1920 design px and every element carries a second set of
   coordinates (--dx/--dy/--dw/--dh, and --dix.. for the artwork inside it).
   Each plated cluster keeps its internal geometry exactly — only where it
   sits and how big it is changes — so nothing is re-art-directed.

   --u is redefined so the artboard fills the viewport up to 1920px and then
   stops, which keeps the design px -> CSS px relationship predictable at any
   width. */
@media (min-width: 20000px) {
  .lb-page { --u: min(0.0520833vw, 1px); }
  .lb-switch-host { --u: calc(min(0.0520833vw, 1px) * 1.2); }

  .lb-art {
    width: calc(var(--daw, 1920) * var(--u));
    height: calc(var(--dband, 6700) * var(--u));
  }
  .lb-page--casual { margin-top: calc(-1 * var(--dband, 6700) * var(--u)); }

  .lb-l,
  .lb-curve {
    left: calc(var(--dx) * var(--u));
    top: calc(var(--dy) * var(--u));
    width: calc(var(--dw) * var(--u));
    height: calc(var(--dh) * var(--u));
  }
  .lb-l > img,
  .lb-l > a > img {
    left: calc(var(--dix, 0) * var(--u));
    top: calc(var(--diy, 0) * var(--u));
    width: calc(var(--diw, var(--dw)) * var(--u));
    height: calc(var(--dih, var(--dh)) * var(--u));
  }
  .lb-l--fade > img {
    -webkit-mask-image: linear-gradient(to bottom,
        transparent calc(var(--df0) * 1%), #000 calc(var(--df1) * 1%),
        #000 calc(var(--df2, 100) * 1%), transparent calc(var(--df3, 100) * 1%));
            mask-image: linear-gradient(to bottom,
        transparent calc(var(--df0) * 1%), #000 calc(var(--df1) * 1%),
        #000 calc(var(--df2, 100) * 1%), transparent calc(var(--df3, 100) * 1%));
  }
  .lb-slice { translate: calc(var(--dex) * var(--u) * var(--exf, 1))
                         calc(var(--dey) * var(--u) * var(--exf, 1)); }

  .lb-t {
    left: calc(var(--dtx, var(--x)) * var(--u));
    top: calc(var(--dty, var(--y)) * var(--u));
    width: calc(var(--dtw, var(--w)) * var(--u));
  }
  .lb-t--art { height: calc(var(--dth, var(--hh)) * var(--u)); }
  .lb-spin,
  .lb-orbit,
  .lb-tapspin,
  .lb-turn {
    transform-origin: calc(var(--dpx, var(--px)) * var(--u))
                      calc(var(--dpy, var(--py)) * var(--u));
  }

  .lb-switch-host { top: max(calc(var(--lb-switch-top-desktop, 110) * 1px), var(--lb-clamp, -999px)); }
  /* the switch is sized in design px, which just became half as many per CSS
     px — hold it at its mobile size so it stays a control, not a banner */
  .lb-switch { width: 172px; height: 43px; font-size: 12.5px; border-radius: 24px; }
  .lb-switch__knob { width: 35px; height: 35px; top: 4px; left: 5px; }
  [data-lb-theme="casual"] .lb-switch__knob { translate: 127px 0; }
  .lb-switch__label--casual { left: 49px; }
  .lb-switch__label--formal { left: 14px; }
}

/* ---------- 12. Ambient animation sleeper ------------------------------- */

/* The decorative loops — plate turns, prop floats, the hero hover — ran on
   every layer of both artboards at all times: visibility:hidden does NOT stop
   a CSS animation, so the hidden theme's plates were spinning too, ~40
   animations ticking across 20,000 design px of page. A desktop emulator
   composites that for free; a phone GPU pays for every one of those layers
   every frame, which is exactly the scroll jank that was reported. The
   observer in lunch-b.js marks offscreen loops with .lb-asleep; the attribute
   rules below silence the hidden theme wholesale. !important because the
   `animation:` shorthands these pause against reset play-state at equal or
   higher specificity. Paused, not none — a woken loop resumes mid-pose
   instead of snapping back to its start. */
.lb-asleep { animation-play-state: paused !important; }

.lb-page[data-lb-active="false"] .lb-turn,
.lb-page[data-lb-active="false"] .lb-float,
.lb-page[data-lb-active="false"] .lb-float--alt,
.lb-page[data-lb-active="false"] .lb-hover,
.lb-page[data-lb-active="false"] .lb-stack.is-stacked,
.lb-page[data-lb-active="false"] .lb-knife {
  animation-play-state: paused !important;
}

/* iOS Safari (2026-08-24): PAUSED IS NOT ASLEEP ON WEBKIT. A paused CSS
   animation keeps its element composited, so every plate, product and fx
   shadow on BOTH artboards held a 3x-DPR GPU texture whether it was moving
   or not. On an iPhone that blew the tab's compositing budget: WebKit
   evicted the textures of whole spin groups (plates and bags rendered as
   NOTHING while the napkin beside them — never composited — kept painting),
   and eventually jetsammed the page outright (the white flash + reload in
   the user's video). Blink survives this; WebKit must drop the animation
   entirely — animation-name: none decomposites the subtree, frees the
   texture, and lets the layer paint back into normal page tiles.
   The cost is that a woken loop restarts at keyframe 0 instead of resuming
   mid-pose. Wakes happen at the viewport edge (the sleeper's WebKit band is
   viewport+10%), so the snap is off-screen; and a whole group flips in one
   style flush, so its members restart on the same clock and stay rigid. */
.lb-webkit .lb-asleep,
.lb-webkit .lb-page[data-lb-active="false"] .lb-turn,
.lb-webkit .lb-page[data-lb-active="false"] .lb-float,
.lb-webkit .lb-page[data-lb-active="false"] .lb-float--alt,
.lb-webkit .lb-page[data-lb-active="false"] .lb-hover,
.lb-webkit .lb-page[data-lb-active="false"] .lb-stack.is-stacked,
.lb-webkit .lb-page[data-lb-active="false"] .lb-knife {
  animation-name: none !important;
}

/* ---------- 13. The badge pearl ----------------------------------------- */

/* The bead at the centre of the badge ring is a PEARL in the design, not a
   flat white disc. Colours sampled off the fresh PDF exports' own pearl at
   seven radii (identical in both themes): near-white centre, warm ivory
   fall-off, darker warm rim. The highlight sits just above centre, like a
   sphere lit from above. */
.lb-l--pearl {
  background: radial-gradient(circle at 46% 40%,
      #fbfaf9 0%, #eeebe5 30%, #e3ded4 50%, #d8d1c3 70%,
      #cbc2b0 85%, #b5ab92 96%, #a89d84 100%);
  border-radius: 50%;
}

/* ---------- 14. The switch on a wide screen ----------------------------- */

/* With the desktop composition retired the artboard is a 734px column, so
   there is a wide empty gutter either side. Park the control in the LEFT
   gutter rather than over the page: centred in that gutter and centred in the
   viewport, it can never collide with the storefront header (logo left,
   nav centre) or with the artboard, and it stays reachable at any scroll
   depth. Percentages on a fixed element resolve against the viewport, so this
   needs no vw units and is unaffected by the scrollbar.
   1200px is the width at which the gutter is finally wide enough to hold the
   pill with clear air on both sides; below it the control stays centred at
   the top, exactly as on a phone. */
@media (min-width: 1200px) {
  .lb-switch-host {
    top: 50%;
    left: calc((100% - 734.4px) / 4);
    translate: -50% -50%;
  }
}

/* ---------- 14b. Drift under Reduce Motion ------------------------------ */

/* iPhones ship Reduce Motion far more often than desktops, and the original
   rules answered it with `animation: none`, so the olive skewer and the
   sandwich sat dead still there. The first fix damped them to a third of the
   travel at half the speed — which read as "the hover is broken on iPhone"
   next to Android and Windows, reported from a real device.

   So the idle DRIFT now runs at full speed and full travel everywhere: it is
   a slow vertical hover of a few px, the least vestibular-triggering motion
   on the page, and matching it across platforms is what the design intends.
   Reduce Motion still does real work elsewhere — the plate rotation is
   slowed to a third and the tap spin drops from ten turns to three — which
   is where the discomfort actually lives. */
}
