/* ============================================================================
   BAKING WITH MARI — the hero world.
   ----------------------------------------------------------------------------
   This stylesheet is the WORLD, not a theme swap. Every rule here exists to make
   the page look like the same printed object the Seedance clips were drawn in:
   heavy ink linework, halftone dot fields, flat saturated colour, drawn panels.

   Where it comes from — Oliver's own brief PDF (BRIEF_FROM_OLIVER_2026-08-08.pdf):
     · chebahut.com  — the stoner-cartoon poster world: black line, halftone,
                       flat colour, a real photographed food object flying through
                       a hand-drawn world. That IS image_masters/hero_anchor/
                       anchor_A_bleed.png, which every clip was generated from.
     · voodoodoughnut.com — the LAYOUT: simple, centred, straight to the point,
                       ORDER NOW never more than one look away.

   What it refuses: the white minimal scroll-site with a grey route rail and a
   "01 / 10" beat counter. His words, 2026-08-11: "I see every step of the way
   1 of Ten 2 of Ten 3 of 10 it doesn't make sense like this is a slop still."

   TYPE IS GATE-ENFORCED: 11 13 15 17 20 24 30 38 48 60 76, floor 13,
   11px only in a rule that also sets uppercase. clamp() endpoints count.
   ========================================================================== */

/* ---- faces: all SIL OFL 1.1, all on disk, zero third-party requests -------- */
/* WOFF2 FIRST, and it is the single biggest speed win on this site. The three faces shipped
   as raw .ttf: 1,481 KB on EVERY page, on phones, on rural Missouri mobile data — and
   ShantellSans alone was 1.25 MB because it carried 1,728 glyphs and four variable axes
   (weight, informality, bounce, spacing) when the site only ever renders weight 400-700 in
   Latin. Subset to Latin, the unused axes pinned out, converted to woff2: 143 KB total,
   1,338 KB saved per visitor. Measured 2026-08-12.
   The .ttf stays listed second as a fallback and costs nothing — a browser downloads the
   first format it understands, and every browser since 2016 understands woff2. */
@font-face{font-family:'Alfa Slab One';src:url('../assets/fonts/AlfaSlabOne-Regular.woff2') format('woff2'),url('../assets/fonts/AlfaSlabOne-Regular.ttf') format('truetype');font-display:swap}
@font-face{font-family:'Shantell Sans';src:url('../assets/fonts/ShantellSans.woff2') format('woff2'),url('../assets/fonts/ShantellSans.ttf') format('truetype');font-display:swap}
@font-face{font-family:'Jost';src:url('../assets/fonts/Jost.woff2') format('woff2'),url('../assets/fonts/Jost.ttf') format('truetype');font-display:swap}
/* RYE — the western woodtype, and the face Oliver asked for by name. It is the one closest to
   Voodoo Doughnut's own logo, which is his stated reference for this site.
   IT WAS REMOVED IN ERROR. A previous session wrote "Rye. None may appear on the site" into
   DESIGN_V5.md and a later one swept the file out as unused. That sentence was a session's
   opinion recorded as if it were his ruling, and it is the opposite of what he actually said.
   Restored on his direct instruction, 2026-08-13: "I want the rye back."
   Subset to Latin + punctuation: 178.9 KB .ttf -> 38.9 KB .woff2. */
@font-face{font-family:'Rye';src:url('../assets/fonts/Rye-Regular.woff2') format('woff2'),url('../assets/fonts/Rye-Regular.ttf') format('truetype');font-display:swap}

:root{
  /* DESIGN_V5, ratified. The ink is a deep plum, never black — his rule. It is
     also the LINEWORK colour, which is why the page can be drawn without ever
     using #000. */
  --paper:#FFFFFF;
  --ink:#332742;
  --pink:#F2ACB9;
  --rose:#D95C7A;      /* pink darkened until it passes 4.5:1 AS TEXT ON PAPER — that is the
                          measurement this value was chosen against, and it holds. */
  /* THE BUTTON FACE IS A DEEPER ROSE, and the reason is a number that was wrong in this file.
     A comment beside the CTA claimed "white text on #D95C7A measures 4.6:1 — AA at this size."
     It measures 3.64:1. That is AA for LARGE text only (24px+), and these labels ship at 15px in
     the bar, 17px on a normal pill and 20px on a phone — all of which need 4.5:1. So every rose
     button on the site has been failing the contrast gate, quietly, since the day it turned rose.
     This is the SAME hue (345.6°) at the SAME saturation, 7% deeper: white on it measures
     4.54:1 and every button clears AA at every size. It is a separate token so that reverting it
     is one line if Oliver prefers the original — the colour is his call and he has the numbers. */
  --rose-btn:#D23F62;
  --violet:#A87CC9;
  --plum:#6B4A87;
  --gold:#FFB01F;
  --sky:#3FA7D6;
  --wash:#F7EFFB;

  --line:3px;                        /* the pen width. One value, everywhere. */
  --drop:6px;                        /* the ink drop. One value, everywhere. */
  --radius:6px;

  --display:'Rye','Alfa Slab One',Georgia,serif;   /* his face, restored 2026-08-13 */
  --voice:'Shantell Sans','Jost',sans-serif;   /* the funny one */
  --ui:'Jost',system-ui,sans-serif;

  /* halftone: the dot field that makes flat colour read as printed */
  --halftone:radial-gradient(var(--dotcolor,rgba(51,39,66,.16)) 1.4px, transparent 1.5px);
}

*{box-sizing:border-box}
/* `hidden` must actually hide. The UA rule is [hidden]{display:none}, which ANY class rule
   that sets display silently outranks — so the custom-tip field sat open under a selected
   preset, and switching to Pickup would have left the delivery address on screen. Measured
   2026-08-11. One line, and every `hidden` on the page means what it says. */
[hidden]{display:none !important}
html{
  scroll-behavior:smooth;
  /* The bar is fixed, so an anchor jump parks the target underneath it. Measured on
     /shop.php: the delivery toggle landed behind the bar. */
  scroll-padding-top:76px;
}
html,body{margin:0;padding:0;background:var(--paper);color:var(--ink);
  font-family:var(--ui);-webkit-font-smoothing:antialiased;
  /* NEVER LET THE BROWSER FAKE A BOLD. Rye and Alfa Slab One each ship exactly ONE weight, and
     11 elements on the shop asked them for 700 — so Chrome smeared the glyphs sideways to
     invent it, which is why the digits and product names came out double-struck (seen at 390,
     2026-08-13). A previous session fixed this by hand for Alfa Slab and it came straight back
     the moment Rye arrived. This kills the whole class instead: real weights on variable faces
     like Jost still work, only the FAKE ones stop. */
  font-synthesis-weight:none; font-synthesis:none}
img{max-width:100%;display:block}
a{color:inherit}

/* ============================================================================
   1. THE BAR — voodoodoughnut's lesson: ordering is never more than one look away.
   Thin, drawn, always there. It is the only chrome on the whole page.
   ========================================================================== */
.bar{
  position:fixed; inset:0 0 auto 0; z-index:200;
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  padding:10px max(14px,env(safe-area-inset-left)) 10px max(14px,env(safe-area-inset-right));
  background:var(--paper); border-bottom:var(--line) solid var(--ink);
}
.bar__mark{
  /* gap:0 ON PURPOSE, and this was a live defect he could see: the mark is
     `Baking&nbsp;With&nbsp;<b>Mari</b>`, so flex makes the leading text an anonymous item and
     `<b>` a second one — and an 8px flex gap landed ON TOP of the nbsp already in the markup.
     The result was one word space between "Baking" and "With" and roughly two and a half
     before "Mari". A flex gap is not a word space; the space characters are. */
  display:flex; align-items:center; gap:0; text-decoration:none;
  font-family:var(--display); font-size:20px; line-height:1; letter-spacing:.01em;
  min-height:44px;                      /* it is a link to the home page, so it is a target */
  /* The bar is the only three-item flex row on the page, so something has to give when the
     viewport narrows. It is the NAME, never the button — the button is the page's whole job. */
  min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.bar__mark b{color:var(--rose); font-weight:400}
/* flex:0 0 auto so the RIGHT side of the bar never gives ground. `.bar .order-btn` alone was
   not enough — the button is a child of this wrapper, and it was the WRAPPER that flex was
   squeezing, which squeezed the pill inside it. */
.bar__right{display:flex; align-items:center; gap:12px; flex:0 0 auto}
.bar__note{
  font-family:var(--voice); font-size:15px; color:var(--plum);
}
@media (max-width:760px){ .bar__note{display:none} }

/* the one button the whole page walks toward */
.order-btn{
  /* ONE PADDING SOURCE, THREE SIZES. Padding used to be re-declared in four places, two of
     them in competing `max-width:640px` blocks, so the size a button actually got depended on
     rule order rather than on anything anyone chose. The variants now set the two numbers and
     nothing re-states the shorthand. */
  --pad-y:12px; --pad-x:22px;
  /* THE OPTICAL NUDGE — this is the measured half of "the words don't sit in the buttons well".
     `align-items:center` with `line-height:1` centres the font's EM BOX. This display face
     carries its ink high (tall caps, a descender well that most of these labels never use), so
     a mathematically centred label reads as floating. Measured off the live 390 screenshot by
     comparing the white ink bounding box against the rose face: the label sat 1.5px HIGH.
     0.045em each way at 17px is 0.77px, so the label moves down 1.5px — and because it is in
     em it stays correct at 15px in the bar and 24px on the big button, which a pixel could not. */
  --optical:.045em;
  /* NOT FLEX, and this is the whole two-line fix. As a flex row the label was one item and the
     arrow another; when the label had to wrap, flex shrank the LABEL to fill the row and left
     the arrow pinned at the far right — a pill with its words in the middle and its arrow 130px
     away, which is exactly the "doesn't sit in the button well" he reported. As inline content
     the label and the arrow are one centred block that wraps together. Vertical centring then
     comes from the padding rather than align-items, which is why --pad-y is tuned so the box is
     always at or over the 44px touch floor and `min-height` never has to add space at the
     bottom (that would push the label off centre by the amount it added). */
  display:inline-block; text-align:center; vertical-align:middle;
  padding:calc(var(--pad-y) + var(--optical)) var(--pad-x)
          calc(var(--pad-y) - var(--optical));
  border-radius:999px;
  /* ROSE IS THE DEFAULT, and this is a system rule rather than a preference.
     The reference system this build follows rations its warm colour by JOB: one hue means
     "this moves you forward", another means "this is a tool or a mark", and they never trade
     places. On this site rose is the forward colour — and it was applied by listing the three
     places it happened to be needed (`.bar`, `.bake__go`, `.curtain__foot`), so anything not on
     that list fell through to gold. Measured live: "Order this week's bake", the LAST call to
     action on the home page, was gold, and both "Check out" buttons on the shop — the single
     most important control on the whole site — were gold too, while "← The story", a back link,
     was rose because it sits in the bar. The loudest colour was on the least important control.
     Oliver on the gold, 2026-08-13: "I don't like the yellow, it looks cheap." So the default
     inverts: every .order-btn is the forward colour, and the two that are NOT forward say so
     themselves (--ghost for going back, .join__go for a form that is not an order).
     Gold stays where it was always meant to be: a surface, never a button. */
  background:var(--rose-btn); color:#fff; text-decoration:none;
  /* 1.1, not 1: at line-height:1 a wrapped label's two lines touch. Half-leading is added
     symmetrically, so this does not disturb the optical nudge above. */
  font-family:var(--display); font-size:17px; line-height:1.1; letter-spacing:.02em;
  border:var(--line) solid var(--ink);
  box-shadow:0 var(--drop) 0 var(--ink);
  transition:transform .1s ease, box-shadow .1s ease;
  min-height:44px;                                     /* touch floor */
  /* A pill may never be wider than the column it sits in. "See the whole bake and order →" is
     306px of nowrap label, which at 320px overhung the viewport by 6px. */
  max-width:100%;
}
.order-btn:hover{transform:translateY(-2px); box-shadow:0 calc(var(--drop) + 2px) 0 var(--ink)}
.order-btn:active{transform:translateY(var(--drop)); box-shadow:0 0 0 var(--ink)}
.order-btn:focus-visible{outline:var(--line) solid var(--sky); outline-offset:3px}
.order-btn--big{--pad-y:18px; --pad-x:34px; font-size:24px}
/* GOING BACK IS NOT GOING FORWARD. The specificity has to beat `.bar .order-btn`, because the
   one place a ghost button actually appears is the shop's bar — where it was inheriting the bar's
   rose and reading as the page's primary action while "Check out" sat in utility gold. */
.order-btn--ghost,
.bar .order-btn--ghost{background:var(--paper); color:var(--ink); animation:none}
.order-btn--ghost::before{display:none}
/* THE TWO-LINE CASE, MADE DELIBERATE. A long label breaks evenly instead of leaving one word
   stranded on its own line, and the arrow keeps its distance from the last word. */
.order-btn{text-wrap:balance}
/* 5px, not the old flex gap of 9px: flex DISCARDED the whitespace-only text node between the
   label and the arrow, inline layout renders it. 5px of margin on top of a ~4px word space
   lands back on the 9px the button was designed with. */
.order-btn > span{margin-left:5px}

/* --- THE CTA IS ALIVE. -----------------------------------------------------------------
   Oliver, 2026-08-13: "I want the actual element, the CTA button itself, to pulsate, to glow,
   to do something... I don't like the yellow, it looks cheap." He also rejected the first
   attempt by name: "did I ask you to put a gold ring around the CTA button? No."
   So: no ring, nothing added AROUND it. THE BUTTON ITSELF is what moves.

   Three things happen to the button, all on the compositor (transform, opacity, box-shadow) so
   none of them cost layout on a page whose frame budget we already spent a session recovering:
     1. IT BREATHES — a slow scale, small enough to feel alive rather than twitchy.
     2. IT GLOWS — a soft rose halo swells and fades under the hard ink drop. The drop is the
        brand's linework and never moves; the glow lives behind it, so the button gains light
        without the outline appearing to wobble.
     3. LIGHT CROSSES IT — a highlight sweeps the face every cycle, the way light moves over
        something physical. That is what stops it reading as a flat coloured rectangle.

   COLOUR is no longer set here. Rose is now every order button's default (see .order-btn above),
   so these three lines only decide WHICH buttons MOVE. That list is deliberately short and stays
   exactly as he approved it: the two ways into the shop from the home page, and the one in the
   bar. The checkout button is not on it on purpose — a button that breathes while someone is
   reading their own order total is a distraction at the worst possible moment. */
.bar .order-btn,
.bake__go .order-btn,
.curtain__foot .order-btn{        /* BOTH ways in wear the same face. Two Order buttons on one
                                     screen in two different colours reads as two different
                                     things — measured on the phone shot, 2026-08-13. */
  position:relative; overflow:hidden;
  animation:cta-breathe 3.2s ease-in-out infinite;
}
.bar .order-btn::before,
.bake__go .order-btn::before,
.curtain__foot .order-btn::before{
  content:""; position:absolute; top:0; bottom:0; width:45%;
  background:linear-gradient(100deg,transparent,rgba(255,255,255,.55),transparent);
  pointer-events:none;
  animation:cta-sheen 3.2s ease-in-out infinite;
  /* MEASURED, 2026-08-13, warm page, scripted scroll of the whole live home page:
       everything as it was ....... 9.0% of frames over 20ms, p95 44.5ms, 122 frames over 50ms
       with this one line ......... 6.2%, p95 27.9ms, 14 frames over 50ms
     The sheen translates inside a rounded `overflow:hidden` button that is FIXED in the bar, so
     it is on screen for every frame of every scroll. Without its own layer Chrome cannot
     composite the transform through that rounded clip and repaints the bar instead.
     This is the narrow, justified use of will-change the audit allows — one known-expensive
     animation, not a blanket hint — and it changes nothing about how the button looks. */
  will-change:transform;
}
@keyframes cta-breathe{
  /* The glow is held at nothing for most of the cycle. A button that pulses without pause
     becomes wallpaper within seconds and stops being seen at all — the rest is what makes
     each swell register. */
  0%,60%,100%{transform:scale(1);     box-shadow:0 var(--drop) 0 var(--ink), 0 0 0 rgba(217,92,122,0)}
  78%        {transform:scale(1.045); box-shadow:0 var(--drop) 0 var(--ink), 0 0 26px 6px rgba(217,92,122,.72)}
}

/* THE BAR BUTTON BREATHES BUT DOES NOT GLOW, and this is an engineering decision, not a taste one.
   Oliver asked for the CTA to pulsate and glow, and it does — on the BIG button, at full strength.
   The one in the bar is `position:fixed`, so unlike the hero button it never scrolls away: its
   26px blurred shadow was being repainted on every frame of all 7,000px of the page. Measured warm
   on the live site: 9.0% of frames over 20ms with it, 1.9% without, p95 44.5ms against 11.5ms.
   Scale and the light sweep are compositor work and cost nothing, so they stay. The glow goes
   where it is both most visible and free — the button that leaves the screen after one screen.
   He was asked and told me, fairly, that this is not a question he should be answering. */
.bar .order-btn{
  animation-name:cta-breathe-quiet;
  /* THE BUTTON NEVER SHRINKS. It is a flex item in a row with the wordmark, and with
     `white-space:nowrap` a shrunk pill does not wrap its label — it lets the label hang out
     the side. Measured on the live site at 320px: the pill was 123px, the words ran 8px past
     its right edge. The wordmark now yields instead (ellipsis, above), which is the correct
     trade: the name is recoverable from the page, the way to order is not. */
  flex:0 0 auto;
}
@keyframes cta-breathe-quiet{
  0%,60%,100%{transform:scale(1)}
  78%        {transform:scale(1.045)}
}
@keyframes cta-sheen{
  0%,52%{transform:translateX(-160%) skewX(-18deg)}
  86%,100%{transform:translateX(320%) skewX(-18deg)}
}

/* ============================================================================
   2. THE CURTAIN — the first viewport. Dot lettering (blackdogstory.com, the one
   technique he named by hand), one line of voice, one gold button, Bella peeled
   into the corner like a sticker on a van window.
   ========================================================================== */
/* The canvas is absolutely positioned, not a grid row: a <canvas> carries an intrinsic
   height from its width/height attributes, and inside a `1fr` track (which is
   minmax(auto,1fr)) that intrinsic size wins and stretches the section past the viewport.
   Measured 2026-08-11: the curtain came out 1334px tall in a 900px window and pushed the
   copy, the skip button and Bella entirely below the fold. */
.curtain{
  position:relative; z-index:70; height:100svh; min-height:520px; padding-top:64px;
  display:flex; align-items:flex-end; overflow:hidden;
  background:var(--paper);
}
/* the printed ground: a halftone field in brand pink, not a grey gradient */
.curtain::before{
  content:""; position:absolute; inset:0;
  --dotcolor:rgba(242,172,185,.55);
  background-image:var(--halftone); background-size:9px 9px;
  mask-image:radial-gradient(120% 78% at 50% 30%,#000 0%,transparent 72%);
}
/* A canvas is a REPLACED element, so `width:auto` between left:0 and right:0 resolves to
   its intrinsic 300x150 instead of stretching — the lettering came out thumbnail-sized in
   the top-left corner (seen 2026-08-11). Absolute positioning keeps it out of layout; the
   explicit width/height is what actually sizes it. */
#dots{position:absolute; z-index:2; top:64px; left:0; display:block;
      width:100%; height:calc(100% - 64px)}
.curtain__foot{
  position:relative; z-index:3; text-align:center; width:100%;
  padding:0 20px calc(30px + env(safe-area-inset-bottom));
}
.curtain__foot p{
  margin:0 auto 22px; max-width:36ch;
  font-family:var(--voice); font-size:20px; line-height:1.45; color:var(--plum);
}
.cue{margin-top:24px; font-size:11px; letter-spacing:.2em; text-transform:uppercase; color:var(--plum)}
.cue i{display:inline-block; font-style:normal; animation:bob 1.9s ease-in-out infinite}
@keyframes bob{0%,100%{transform:translateY(0)}50%{transform:translateY(7px)}}

/* Bella, peeled on like a real sticker. She is the mascot the Cheba Hut bird is
   in his brief — same job, his dog. */
.bella-sticker{
  position:absolute; z-index:4; right:2vw; bottom:0; width:min(30vw,320px);
  transform:rotate(-4deg); pointer-events:none;
  filter:drop-shadow(0 8px 0 rgba(51,39,66,.18));
}
@media (max-width:760px){ .bella-sticker{width:44vw; right:-4vw; opacity:.95} }

/* ============================================================================
   3. THE FLIGHT IS GONE, AND SO IS ITS STYLING.
   Eighty-four lines used to live here overriding vendor/scrub-engine.js - the caption
   panel, the eyebrow strip, the tag pills, the killed beat counter. index.php stopped
   loading that engine when the film took over the storytelling, and every selector in
   the block began with a .sw-* class that ONLY that engine ever created, so not one of
   them could match anything on any page. Dead weight in a stylesheet the whole site
   downloads. #world itself survives as the crawlable story block and is styled by the
   [data-sw-seo] rule further down.
   ========================================================================== */


/* ============================================================================
   4. PAGE SECTIONS — everything below the flight. Same pen, same drop, same
   halftone. A section is a drawn panel on paper.
   ========================================================================== */
.sec{position:relative; z-index:70; background:var(--paper);
     padding:clamp(48px,7vw,96px) 20px; border-top:var(--line) solid var(--ink)}
.sec--wash{background:var(--wash)}
.wrap{max-width:1080px; margin:0 auto}
/* The page headline. It had NO rule at all until 2026-08-11, so every <h1> fell back to the
   browser's 32px bold sans while the <h2> under it rendered at 60px Alfa Slab One — the
   biggest words on the ordering page and the find page were the second-most important ones,
   in the wrong typeface. h1 outranks h2 on the same locked scale (…38 48 60 76). */
/* The bar is fixed and 64-70px tall. Any page whose first element is a section must start
   below it — measured at 390px on find.php and 404.html, where the h1 was sliced in half. */
body > .sec:first-of-type,
body > main.sec:first-of-type,
body > main > .sec:first-of-type{padding-top:calc(70px + clamp(28px,5vw,56px))}

.sec h1{
  font-family:var(--display); font-size:clamp(38px,6vw,76px); line-height:1.02;
  margin:0 0 18px; letter-spacing:-.015em;
}
.sec h2{
  font-family:var(--display); font-size:clamp(30px,5vw,60px); line-height:1.04;
  margin:0 0 18px; letter-spacing:-.01em;
}
.sec p{font-family:var(--voice); font-size:20px; line-height:1.5; color:var(--plum); margin:0 0 18px}
@media (max-width:640px){ .sec .lede--tight{font-size:17px; line-height:1.42; margin-bottom:16px} }
.lede{max-width:52ch}

/* --- Meet Mari: she PLAYS. Full-bleed clip as the section's ground, words on top.
   His correction 2026-08-11: "it's still framed." A boxed video that never starts is a
   photograph with extra steps. --- */
.sec--film{
  position:relative; overflow:hidden; border-top:var(--line) solid var(--ink);
  /* THE SIDE PADDING BELONGS OUT HERE, not inside the wrap. `padding:0` killed the 20px that
     `.sec` gives every other section, and `.film__copy` put 20px back INSIDE the 1080px wrap
     instead — so this was the one section on the page whose words started at 192px while all
     ten others started at 172px. Measured, not guessed. The film is `position:absolute;
     inset:0`, so section padding cannot touch it and the clip stays full-bleed. */
  padding:0 20px;
  min-height:min(78svh,720px); display:flex; align-items:flex-end;
}
.film__bg{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  object-position:center 38%;                 /* keeps her face out of the copy */
}
/* the veil is a real surface, not a haze: dark enough at the bottom that cream type on it
   clears 4.5:1 over any frame the clip lands on */
.film__veil{
  position:absolute; inset:0;
  background:linear-gradient(180deg,rgba(51,39,66,0) 22%,rgba(51,39,66,.62) 58%,rgba(51,39,66,.92) 100%);
}
.film__copy{
  position:relative; z-index:2; width:100%;
  padding:0 0 calc(48px + env(safe-area-inset-bottom));
}
.film__copy h2{color:#FFFFFF}
.film__copy .lede{color:#F3EAF8; max-width:46ch}
.mari__sig{font-family:var(--display); font-size:24px; color:var(--pink); margin-top:8px}
@media (prefers-reduced-motion:reduce){ .film__bg{display:none} .sec--film{background:var(--ink)} }

/* ============================================================================
   THE OVEN CATCH — his ruling, twice. The oven is drawn, not photographed: same 2px ink
   linework and hard drop as every other object on the site, so it belongs to the brand rather
   than sitting on top of it. No new art was generated for this.
   ========================================================================== */
.oven{display:block; margin:0 0 32px; text-align:center}
.oven__body{
  position:relative; margin:0 auto; width:min(520px,100%); aspect-ratio:5/3;
  border:var(--line) solid var(--ink); border-radius:16px;
  background:var(--wash); box-shadow:var(--drop) var(--drop) 0 var(--ink);
  overflow:hidden;
}
/* The door swings from its BOTTOM edge, the way an oven actually opens. A door that slid or
   faded would read as a panel; the hinge is what makes it an oven. */
.oven__door{
  position:absolute; inset:0; transform-origin:50% 100%;
  transition:transform .55s cubic-bezier(.22,1,.36,1);
  background:var(--paper); border-bottom:var(--line) solid var(--ink);
  display:grid; place-items:center; z-index:2;
}
.oven.is-open .oven__door{transform:rotateX(-88deg)}
.oven__glass{
  width:74%; height:56%; border-radius:10px; border:var(--line) solid var(--ink);
  background:
    radial-gradient(120% 90% at 50% 120%, rgba(255,176,31,.55), rgba(255,176,31,0) 62%),
    var(--ink);
}
.oven__handle{
  position:absolute; left:12%; right:12%; top:12%; height:12px;
  border:var(--line) solid var(--ink); border-radius:999px; background:var(--pink);
}
/* The mouth is the heat. It only glows once the door is open — a cold oven that glows is a lamp. */
.oven__mouth{
  position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
  gap:clamp(8px,3vw,24px); padding:16px; flex-wrap:wrap;
  background:radial-gradient(120% 100% at 50% 100%, rgba(255,176,31,.34), rgba(255,176,31,0) 70%);
  opacity:0; transition:opacity .4s ease .25s;
}
.oven.is-open .oven__mouth{opacity:1}

/* A treat: the shop's own cutout, lit from below as if the oven is still open under it. */
.floaty{
  appearance:none; border:0; background:none; padding:0; cursor:grab;
  width:clamp(64px,17vw,96px); aspect-ratio:1; touch-action:none;
  filter:drop-shadow(0 6px 14px rgba(255,176,31,.75));
  animation:floaty-rise .7s cubic-bezier(.22,1,.36,1) backwards,
            floaty-bob 3.4s ease-in-out infinite;
  animation-delay:calc(var(--i) * .09s), calc(.7s + var(--i) * .21s);
}
.floaty img{width:100%; height:100%; object-fit:contain; pointer-events:none}
.floaty:focus-visible{outline:var(--line) solid var(--sky); outline-offset:4px; border-radius:8px}
.floaty.is-dragging{cursor:grabbing; z-index:9; animation:none; filter:drop-shadow(0 10px 22px rgba(255,176,31,.9))}
.floaty.is-gone{transform:scale(.2); opacity:0; transition:transform .3s ease, opacity .3s ease}
@keyframes floaty-rise{from{transform:translateY(38px) scale(.7); opacity:0}}
@keyframes floaty-bob{50%{transform:translateY(-7px)}}

.oven__open{margin-top:16px}
.oven__hint{font-family:var(--ui); font-size:15px; color:var(--plum); margin:12px 0 0}

/* The box answers. Armed while a treat is over it, and it thumps when one lands — the feedback
   that tells a thumb the throw worked without a word of copy. */
.box--armed{outline:var(--line) dashed var(--rose-btn); outline-offset:6px}
.box--caught{animation:box-thump .4s ease}
@keyframes box-thump{40%{transform:scale(1.035)}}

@media (prefers-reduced-motion:reduce){
  /* Reduced motion gets the SAME shop, not less of one: the door is simply already open and
     nothing drifts. oven.js disables dragging to match, and every treat is still a real button. */
  .oven__door{transition:none; transform:rotateX(-88deg)}
  .oven__mouth{opacity:1; transition:none}
  .floaty{animation:none; cursor:pointer}
  .box--caught{animation:none}
}

/* --- the hand-off strip: delivery / pickup + address, ABOVE everything (Insomnia) --- */
.hand{
  display:flex; flex-wrap:wrap; gap:16px; align-items:stretch; margin-bottom:32px;
}
.seg{display:inline-flex; border:var(--line) solid var(--ink); border-radius:999px;
     background:var(--paper); overflow:hidden; box-shadow:0 var(--drop) 0 var(--ink)}
.seg__b{
  appearance:none; border:0; cursor:pointer; background:transparent; color:var(--ink);
  font-family:var(--display); font-size:17px; line-height:1; padding:0 22px; min-height:48px;
}
.seg__b.is-on{background:var(--gold)}
.seg__b:focus-visible{outline:var(--line) solid var(--sky); outline-offset:-6px}
.addr{flex:1 1 320px; display:flex; flex-direction:column; gap:8px; min-width:0}
.addr__lab{font-family:var(--ui); font-size:13px; font-weight:600; letter-spacing:.02em}
.addr__in{
  min-height:48px; padding:12px 16px; min-width:0;
  border:var(--line) solid var(--ink); border-radius:var(--radius); background:var(--paper);
  font-family:var(--ui); font-size:17px; color:var(--ink);
}
.addr__in::placeholder{color:#8B7A9B}
.addr__in:focus-visible{outline:var(--line) solid var(--sky); outline-offset:2px}
.addr__lab i{font-style:normal; font-weight:400; color:var(--plum)}
.addrfold{flex:1 1 420px; min-width:0}
.addrfold summary{
  display:inline-flex; align-items:center; min-height:44px; cursor:pointer;
  font-family:var(--ui); font-size:15px; font-weight:600; color:var(--plum);
}
.addrfold summary i{font-style:normal; font-weight:400; margin-left:6px}
.addrfold summary:focus-visible{outline:var(--line) solid var(--sky); outline-offset:2px}
.addrfold[open] summary{margin-bottom:12px}
/* A closed <details> hides its children by hiding the slot — but .addr sets display:flex on
   itself, which wins, so the fields stayed on screen while the fold read as shut. Measured at
   390px, 2026-08-11. Say it explicitly. */
.addrfold:not([open]) > .addr{display:none}
.addrfold[open]{display:flex; flex-wrap:wrap; gap:16px}
.addrfold[open] summary{flex:1 0 100%}
.addr--town{flex:0 1 200px}
select.addr__in{appearance:none; cursor:pointer;
  /* the caret, drawn in ink so the control matches the pen the rest of the page is drawn with */
  background-image:linear-gradient(45deg,transparent 50%,var(--ink) 50%),
                   linear-gradient(135deg,var(--ink) 50%,transparent 50%);
  background-position:calc(100% - 20px) 21px, calc(100% - 14px) 21px;
  background-size:6px 6px, 6px 6px; background-repeat:no-repeat; padding-right:42px}
/* Says where she meets you, or which towns she drives to. It replaces asking for a town we
   would then throw away — pickup happens at a place the shop already knows. */
/* Only a phone needs this folded: at 1440 the two fields sit beside the toggle with room to
   spare, and hiding them there would be a click for nothing. */
@media (min-width:861px){ .addrfold > summary{display:none} .addrfold{display:flex; flex-wrap:wrap; gap:16px} }
.sec .hand__note{flex:1 0 100%; margin:0; font-family:var(--ui); font-size:15px; color:var(--plum)}

/* --- the boast. His brief asked for the Cheba Hut review verbiage verbatim.
   It is printed here as HER OWN claim on a hand-painted sign, not attributed to
   a customer, because no customer has said it yet and this site never invents
   proof (PRODUCT.md, principle 5). Same joke, same words, zero fiction. --- */
.boast{
  --dotcolor:rgba(51,39,66,.14);
  position:relative; text-align:center;
  border:var(--line) solid var(--ink); box-shadow:var(--drop) var(--drop) 0 var(--ink);
  background-color:var(--gold); background-image:var(--halftone); background-size:8px 8px;
  padding:clamp(30px,5vw,54px) 24px;
}
.boast__shot{
  width:min(100%,760px); margin:0 auto; height:auto;
  border:var(--line) solid var(--ink);
}
.boast small{display:block; margin:16px auto 0; max-width:56ch;
  font-family:var(--ui); font-size:15px; color:var(--ink)}

/* --- the club strip: "join Munchie Mob, score some Chiba coin" is his named
   inspiration; hers is the Munchies Club + the Baker's Dozen he already ruled in. */
.club{display:grid; grid-template-columns:repeat(auto-fit,minmax(260px,1fr)); gap:24px; margin-top:32px}
.club__card{
  background:var(--paper); border:var(--line) solid var(--ink);
  box-shadow:var(--drop) var(--drop) 0 var(--ink); padding:24px;
}
.club__card h3{font-family:var(--display); font-size:24px; margin:0 0 10px}
.club__card p{font-size:17px; margin:0}
/* The badge holds a DRAWN MARK, not a number — the two cards it appears on are
   alternatives, and numbering alternatives reads as a sequence the page does not
   have. The icon is stroked in the same 3px pen as the card it sits on, so it
   belongs to the drawing rather than sitting on top of it. */
.club__n{
  display:inline-flex; align-items:center; justify-content:center;
  width:44px; height:44px; margin-bottom:16px;
  border-radius:50%; border:var(--line) solid var(--ink); background:var(--pink);
  color:var(--ink);
}
.club__n svg{width:24px; height:24px; stroke-width:2.2}

/* ============================================================================
   THE LANDING PAGE, REBUILT 2026-08-14 on his instruction: "for a true functioning
   landing home page it's missing several sections... at least eight to twelve
   sections on the home page, and they should be in the appropriate order."

   Order, and why each one sits where it does (Hero-Centric + Conversion, the
   reference pattern for Bakery/Cafe; social proof before the final CTA):
     1 hero · 2 trust strip · 3 THIS WEEK'S BAKE · 4 how it works · 5 why it is
     different · 6 the club · 7 the standing order · 8 how it reaches you ·
     9 what people say · 10 MEET MARI · 11 wholesale · 12 the journal ·
     13 the questions · 14 the last call.
   Meet Mari moved from second to tenth on his direct instruction — a stranger who
   has not seen the food or the price does not yet care who baked it.
   ========================================================================== */

/* --- 2. the trust strip: four checkable facts, no adjectives ---------------- */
/* 32, not 40: the strip is the one band that is deliberately tighter than a full section, and
   40 is the only step in its clamp that was not on the page's rhythm. */
.sec--strip{padding-top:clamp(24px,4vw,32px); padding-bottom:clamp(24px,4vw,32px);
  border-bottom:var(--line) solid var(--ink)}
.strip{display:grid; grid-template-columns:repeat(auto-fit,minmax(210px,1fr));
  gap:16px 24px; margin:0; padding:0; list-style:none}
.strip li{display:flex; flex-direction:column; gap:4px}
.strip b{font-family:var(--display); font-weight:400; font-size:17px; line-height:1.2}
.strip span{font-family:var(--ui); font-size:15px; line-height:1.45; color:var(--plum)}

/* --- 3. this week's bake: the window into the shop -------------------------
   Every card is a LINK, not a form. His ruling 2026-08-11 keeps the shop off this
   page; the food belongs on it. */
.bake{display:grid; gap:16px; margin-top:24px;
  grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
.bake__card{
  position:relative; display:flex; flex-direction:column; align-items:center; gap:4px;
  padding:18px 14px 16px; text-decoration:none; color:var(--ink);
  background:var(--paper); border:var(--line) solid var(--ink);
  box-shadow:var(--drop) var(--drop) 0 var(--ink);
  transition:transform .12s ease, box-shadow .12s ease;
}
.bake__card:hover{transform:translateY(-3px);
  box-shadow:var(--drop) calc(var(--drop) + 3px) 0 var(--ink)}
.bake__card:focus-visible{outline:var(--line) solid var(--sky); outline-offset:3px}
.bake__pic{width:100%; max-width:150px; height:auto; aspect-ratio:1/1; object-fit:contain}
.bake__name{font-family:var(--display); font-size:17px; line-height:1.15; text-align:center}
.bake__protein{font-family:var(--ui); font-size:13px; color:var(--plum); text-align:center}
/* PUSHED TO THE BOTTOM, not stacked under whatever the name happened to wrap to. Product names
   run one to three lines, so without this the prices landed at three different heights across a
   row — which is precisely the "things are off, things are not centered" he keeps catching. The
   card is a flex column; auto margin on the price makes the price line the thing that aligns. */
.bake__price{font-family:var(--display); font-size:20px; color:var(--rose);
  margin-top:auto; padding-top:8px}
/* Only rendered when the stock count is genuinely low — scarcity the checkout can prove. */
.bake__left{position:absolute; top:-11px; right:-9px;
  font-family:var(--ui); font-size:11px; font-weight:700; letter-spacing:.08em;
  text-transform:uppercase; color:var(--ink); background:var(--gold);
  border:2px solid var(--ink); border-radius:999px; padding:4px 9px}
.bake__go{margin-top:32px; text-align:center}

/* --- 4. how it works: three steps ----------------------------------------- */
.steps{display:grid; gap:24px; margin-top:24px;
  grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
.step{background:var(--paper); border:var(--line) solid var(--ink);
  box-shadow:var(--drop) var(--drop) 0 var(--ink); padding:24px}
.step__n{display:inline-flex; align-items:center; justify-content:center;
  width:44px; height:44px; margin-bottom:16px; border-radius:50%;
  border:var(--line) solid var(--ink); background:var(--pink);
  font-family:var(--display); font-size:20px; line-height:1}
.step h3{font-family:var(--display); font-size:24px; margin:0 0 10px}
.step p{font-size:17px; margin:0}

/* --- 13. the questions: native <details>, no JavaScript -------------------- */
.faq{margin-top:24px; border-top:2px solid var(--ink)}
.faq__q{border-bottom:2px solid var(--ink)}
.faq__q summary{
  display:flex; align-items:center; justify-content:space-between; gap:16px;
  min-height:56px; padding:14px 2px; cursor:pointer; list-style:none;
  font-family:var(--display); font-size:20px; line-height:1.25;
}
.faq__q summary::-webkit-details-marker{display:none}
/* The marker is drawn, not a glyph or an emoji — the craft floor bans those as icons.
   The detector reads the two borders below as a card side-stripe, which is its single best
   signal for an AI-built UI and worth keeping loud. It is wrong here: this is a 14x14 pseudo
   element rotated 45 degrees, so the two borders ARE the chevron — there is no card and no
   stripe. The directive anchors to the FIRST line of the comment it sits in, so the reasoning
   lives here and the directive itself is alone on the line below it. */
.faq__q summary::after{
  content:""; flex:0 0 auto; width:14px; height:14px; margin-right:4px;
  /* impeccable-disable-next-line side-tab */
  border-right:3px solid var(--plum); border-bottom:3px solid var(--plum);
  transform:rotate(45deg) translate(-3px,-3px); transition:transform .18s ease;
}
.faq__q[open] summary::after{transform:rotate(225deg) translate(-3px,-3px)}
.faq__q summary:focus-visible{outline:var(--line) solid var(--sky); outline-offset:2px}
.sec .faq__q p{margin:0 0 18px; max-width:62ch; font-size:17px}
@media (prefers-reduced-motion:reduce){ .faq__q summary::after{transition:none} }

/* ============================================================================
   THE SLOGAN — the second voice, restored.
   Oliver, 2026-08-13: *"I still don't see the slogan. You haven't revived that."*

   IT WAS NEVER A FONT FAULT, and that is the part worth remembering. All three
   approved faces were loading and rendering the whole time. What disappeared was
   the JOB: DESIGN_V5 §2 gives Shantell Sans a role as "the second voice" — a
   DISPLAY line under the wordmark — and somewhere along the way it got demoted to
   ordinary body paragraphs. The typeface stayed on the page and the line vanished,
   which is why looking for a broken @font-face found nothing wrong.

   THE WORDS ARE HIS. He picked headline A2 on 2026-08-09 — "Baked. Obviously." —
   and asked for it "maybe even a little louder", which is why this sits at the top
   of the scale rather than politely under it. Both clamp endpoints are on the
   locked scale (24 and 48), so check_type_scale.py stays clean.
   ========================================================================== */
.slogan{
  margin:0 auto 10px; max-width:22ch;
  font-family:var(--voice); font-weight:700;
  font-size:clamp(24px,5vw,48px); line-height:1.05; letter-spacing:-.01em;
  color:var(--ink);
  /* Slight lift off the halftone so it reads as printed ON the paper, not floating. */
  text-shadow:0 2px 0 rgba(255,255,255,.9);
}
.curtain__foot .slogan__sub{
  margin:0 auto 22px; max-width:44ch;
  font-family:var(--ui); font-size:17px; line-height:1.45; color:var(--plum);
}
@media (max-width:640px){
  /* On a phone the film plays behind this, and two long lines of sub-copy over moving
     picture is where legibility goes. The slogan carries it alone; the sub-line is the
     part a small screen can afford to lose. */
  .curtain__foot .slogan__sub{display:none}
}

/* ============================================================================
   PUTTING YOUR HAND UP — the standing order, wholesale, and the follow.
   One form component, three places, because they are the same act: a name, a way
   to reach you, and what you want. ponytail — three near-identical components is
   how a design system starts drifting.
   ========================================================================== */
.join{
  display:flex; flex-wrap:wrap; gap:16px; align-items:flex-end;
  margin-top:24px; max-width:840px;
}
.join .addr{flex:1 1 240px}
/* NOT GOLD. `.order-btn` ships gold, and he said of it plainly: "I don't like the yellow, it
   looks cheap." Three more yellow buttons down the landing page would be that complaint three
   more times. This is DESIGN_V5 §1's own ratified button — ink field, brand-pink text — which
   also settles the hierarchy: rose is ORDER NOW and nothing else on the page competes with it. */
.join__go{flex:0 0 auto; background:var(--ink); color:var(--pink)}
.join__go:focus-visible{outline:var(--line) solid var(--sky); outline-offset:3px}
/* The note under the form does the work a price would do if we had one: it says exactly
   what happens next, so nobody wonders whether they have just been charged. */
.sec .join__note{
  flex:1 0 100%; margin:4px 0 0;
  font-family:var(--ui); font-size:15px; line-height:1.5; color:var(--plum);
}
/* The honeypot. Off-screen rather than display:none, because some bots skip hidden
   fields on purpose. Never focusable, never announced. */
.join__hp{
  position:absolute; left:-9999px; width:1px; height:1px; overflow:hidden;
}

/* What she says back. It is a real surface with the brand's own linework, not a tinted
   strip: a message that matters should look as built as the page it lands on. */
.said{
  display:flex; gap:16px; align-items:flex-start;
  margin:0 0 24px; padding:18px 22px;
  background:var(--gold); border:var(--line) solid var(--ink);
  box-shadow:var(--drop) var(--drop) 0 var(--ink);
}
.sec .said p{margin:0; font-family:var(--ui); font-size:17px; color:var(--ink)}
.said--bad{background:var(--pink)}

/* The wholesale list: the buyers he named by hand — gyms, car lots, offices, senior
   facilities, the casinos and their spas. Printed as a plain run of chips so it reads as
   "yes, that is you" at a glance rather than as a paragraph to be waded through. */
/* 18px below, matching `.sec p` — the chips sit in the paragraph rhythm rather than crowding
   the sentence under them. Seen at 1440, 2026-08-13. */
.who{display:flex; flex-wrap:wrap; gap:12px; margin:0 0 18px; padding:0; list-style:none}
.who li{
  font-family:var(--ui); font-size:15px; font-weight:600; color:var(--ink);
  background:var(--wash); border:2px solid var(--ink); border-radius:999px;
  padding:8px 15px;
}

/* --- the bag: drag a treat in, watch it fill, total always visible ---------- */
.bag{display:grid; grid-template-columns:minmax(0,1fr) minmax(300px,360px); gap:clamp(20px,3vw,40px); align-items:start}
@media (max-width:860px){ .bag{grid-template-columns:1fr} }
.treats{display:grid; grid-template-columns:repeat(auto-fill,minmax(215px,1fr));
  gap:clamp(26px,3vw,44px) 16px; align-items:end}
/* align-items:end so every pile shares one shelf line however tall it has grown — a row of
   piles hanging from their tops reads as broken, a row standing on the same floor reads as
   a counter. */
/* ============================================================================
   THE SHELF — his design, given live on 2026-08-11 with a reference screenshot.

     "A cookie should be its own element not a card. The cookie should be the card, it
      shouldn't have a card that you drag and drop... just the cookie itself, not extra
      surrounding, not square, not anything of that nature... you hit a + and it should
      stack the cookies on top of one another creating 4 cookies or 5 cookies and then you
      drag and drop that."

   So the card is GONE. No border, no ink drop, no plate, no background. The food sits on
   the page and the page holds it — which is also what insomniacookies.com does at checkout,
   the reference he named. Everything that survives is information the buyer needs: what it
   is, what is in it, what it costs, and how many are on the pile.
   ========================================================================== */
.treat{
  display:flex; flex-direction:column; gap:8px; align-items:center; text-align:center;
  background:none; border:0; box-shadow:none; padding:0;
  touch-action:manipulation;
}
.treat.is-dragging{opacity:.45}

/* The pile. A button, because pressing it must do exactly what dropping it does — dragging
   is the delight, but it is unusable with a keyboard and miserable one-handed on a phone,
   and this is a page people reach from a flyer QR code standing outside. */
.stack{
  position:relative; display:block; width:100%; max-width:230px;
  appearance:none; border:0; background:none; padding:0; cursor:grab;
  /* the pile grows UPWARDS: the container reserves the extra height above the base cookie */
  transition:transform .15s ease;
}
.stack:active{cursor:grabbing}
.stack:hover{transform:translateY(-3px)}
.stack:focus-visible{outline:var(--line) solid var(--sky); outline-offset:6px; border-radius:var(--radius)}
.stack::after{                       /* the ground it sits on, so it is not floating in space */
  content:""; position:absolute; left:12%; right:12%; bottom:4px; height:14px; z-index:0;
  background:radial-gradient(ellipse at center, rgba(51,39,66,.22), transparent 70%);
}
.stack__i{
  position:absolute; left:0; right:0; margin:0 auto; width:100%; height:auto;
  aspect-ratio:1/1; object-fit:contain;
  filter:drop-shadow(0 3px 2px rgba(51,39,66,.22));
  transform-origin:50% 90%;
}
.stack__i:first-child{position:relative}   /* the base one holds the box open */

/* The product name is an <h2> for the document outline, NOT for the display scale — the
   page heading is the h1 and these are a flat list beneath it. */
.sec .treat__name{font-family:var(--display); font-size:17px; line-height:1.15;
  margin:0; letter-spacing:0}
.treat__row{display:flex; align-items:center; justify-content:center; gap:16px; flex-wrap:wrap}
.treat__price{font-family:var(--ui); font-weight:600; font-size:17px}

/* âˆ’ 3 + , directly under the pile, which is where he drew it. */
.treat__step{display:inline-flex; align-items:center; gap:8px}
.treat__step button{
  min-width:44px; min-height:44px; border-radius:999px; cursor:pointer;
  border:2px solid var(--ink); background:var(--paper); color:var(--ink);
  font-family:var(--display); font-size:20px; line-height:1;
}
.treat__step button:hover{background:var(--pink)}
.treat__step button:focus-visible{outline:var(--line) solid var(--sky); outline-offset:2px}
.treat__n{font-family:var(--display); font-size:24px; min-width:28px; text-align:center}

.box{
  position:sticky; top:84px;
  border:var(--line) solid var(--ink); box-shadow:var(--drop) var(--drop) 0 var(--ink);
  background:var(--paper);
}
.box__lid{background:var(--pink); border-bottom:var(--line) solid var(--ink);
  padding:12px 18px; font-family:var(--display); font-size:17px}
.box__drop{
  min-height:180px; padding:16px 18px;
  --dotcolor:rgba(51,39,66,.12);
  background-image:var(--halftone); background-size:8px 8px;
}
.box__drop.is-over{background-color:var(--wash); outline:var(--line) dashed var(--rose); outline-offset:-8px}
.box__empty{font-family:var(--voice); font-size:17px; color:var(--plum); margin:0}
.box__line{display:flex; align-items:center; gap:12px; padding:6px 0; border-bottom:2px solid rgba(51,39,66,.14)}
.box__line:last-child{border-bottom:0}
.box__line b{font-family:var(--display); font-weight:400; font-size:17px; flex:1}
.box__qty{display:flex; align-items:center; gap:8px}
/* 44px, not 32. These are the controls someone uses to change their mind about a line they
   already added, one-handed, on a phone — measured at 32x32 by the polish pass, which is under
   the floor this project set for itself and under every platform guideline. */
.box__qty button{
  min-width:44px; min-height:44px; border-radius:50%; cursor:pointer;
  border:2px solid var(--ink); background:var(--paper); font-size:17px; line-height:1;
}
.box__qty button:hover{background:var(--pink)}
.box__qty button:focus-visible{outline:var(--line) solid var(--sky); outline-offset:2px}
.box__line-money{font-family:var(--ui); font-weight:600; font-size:15px}
.box__foot{border-top:var(--line) solid var(--ink); padding:16px 18px}
.box__total{display:flex; justify-content:space-between; align-items:baseline;
  font-family:var(--display); font-size:24px; margin-bottom:16px}
.box__foot .order-btn{width:100%; justify-content:center}
.sec .box__note{margin:12px 0 0; font-family:var(--ui); font-size:13px; color:var(--plum)}

/* --- the Baker's Dozen, counted where the money is ---------------------------
   Thirteen dots. Twelve fill as the box fills and the thirteenth is drawn as the free one,
   so the reward is a picture rather than a sentence you have to read. */
.dozen{border-top:2px dashed rgba(51,39,66,.2); padding-top:14px; margin-bottom:16px}
.dozen__dots{display:flex; gap:4px; flex-wrap:wrap; margin-bottom:8px}
.dozen__d{
  width:14px; height:14px; border-radius:50%; border:2px solid var(--ink);
  background:var(--paper); transition:background-color .15s ease;
}
.dozen__d.is-on{background:var(--pink)}
.dozen__d.is-free{border-style:dashed}
.dozen__d.is-free.is-on{background:var(--gold); border-style:solid}
.sec .dozen__say{margin:0; font-family:var(--ui); font-size:13px; font-weight:600; color:var(--plum)}

/* TOTAL TRANSPARENCY, printed. Every line that makes up the number is shown, so the total
   never moves without a row next to it saying why. His standing rule on this shop. */
.box__break{margin-bottom:12px}
.sec .box__row{display:flex; justify-content:space-between; gap:12px;
  font-family:var(--ui); font-size:15px; color:var(--plum); padding:3px 0}

/* Check out is a real submit button now, not a link that threw the box away. */
button.order-btn{appearance:none; cursor:pointer; font-weight:400}
button.order-btn[disabled]{opacity:.42; cursor:not-allowed; transform:none;
  box-shadow:0 var(--drop) 0 var(--ink)}
button.order-btn.is-busy{cursor:progress}

/* --- the tip. Encouraged, never engineered: nothing is preselected, "No tip" is an equal
   button beside the rest, and there is no guilt copy anywhere near it. --- */
.tip{border-top:2px dashed rgba(51,39,66,.2); padding-top:14px; margin-bottom:16px}
.tip__h{font-family:var(--display); font-size:17px; margin:0 0 10px; font-weight:400}
.tip__row{display:flex; flex-wrap:wrap; gap:8px}
.tipb{
  min-height:44px; padding:0 14px; cursor:pointer; border-radius:999px;
  border:2px solid var(--ink); background:var(--paper); color:var(--ink);
  font-family:var(--ui); font-size:15px; font-weight:600;
}
.tipb.is-on{background:var(--pink)}
.tipb:focus-visible{outline:var(--line) solid var(--sky); outline-offset:2px}
.tip__custom{display:flex; flex-direction:column; gap:8px; margin:10px 0 0}
.sec .tip__note{margin:10px 0 0; font-family:var(--ui); font-size:13px; color:var(--plum)}

/* --- the protein number, printed on the card. His differentiator, never softened: the
   AMOUNT is stated as a fact, the regulated adjective appears nowhere, and the serving it
   belongs to is printed beside it because a number with no serving is noise. --- */
.treat__protein{display:flex; align-items:baseline; justify-content:center; gap:8px;
  flex-wrap:wrap; margin:0}
.treat__protein b{font-family:var(--display); font-weight:400; font-size:17px; color:var(--rose)}
.sec .treat__protein span,.sec .treat__serving,.sec .treat__inbox{font-family:var(--ui); font-size:13px; color:var(--plum)}
.sec .treat__serving{margin:0}
/* what is already in the box, said on the item, so the two never disagree */
.sec .treat__inbox{margin:0; min-height:18px; font-weight:600; color:var(--rose)}
.treat.is-out{opacity:.5}
.treat.is-out .stack{cursor:default}
.treat.is-out .stack__i{filter:saturate(.3)}
.treat__out{font-family:var(--display); font-size:15px; color:var(--plum)}
.sec .treat-error{grid-column:1/-1; font-family:var(--voice); font-size:17px; color:var(--plum)}

/* ============================================================================
   THE PHONE IS THE REAL DEVICE — Oliver, 2026-08-11: "this whole experience needs to be
   mobile optimized, the whole interactive user experience, the game, every aspect of it."
   He is right about the traffic too: these buyers arrive from a Marketplace link or a QR
   code on a flyer, outdoors, one-handed.

   Three things had to change, not just shrink:

   1. DRAG DOES NOT EXIST ON TOUCH. HTML5 drag-and-drop fires no events from a finger, so
      "drag the pile into your box" is a desktop flourish and can never be the way in. TAP
      the pile IS the move — same function, same label, and it was built that way from the
      start rather than bolted on.
   2. A SIDEBAR IS NOT A BASKET ON A PHONE. Below 860px the box stops being a column beside
      the grid and becomes a sheet that comes up from the bottom, over the thumb. Same DOM,
      same JavaScript — only where it sits changes.
   3. THE TOTAL MUST BE VISIBLE WHILE YOU SHOP. The bar at the bottom carries the count and
      the total, and pressing it opens the sheet.
   ========================================================================== */
.minibar{display:none}
@media (max-width:860px){
  .minibar{
    position:fixed; z-index:205; left:0; right:0; bottom:0;
    display:flex; align-items:center; gap:12px;
    padding:10px 14px calc(10px + env(safe-area-inset-bottom));
    background:var(--paper); border-top:var(--line) solid var(--ink);
  }
  .minibar__n{font-family:var(--ui); font-size:13px; font-weight:600; color:var(--plum)}
  .minibar__total{font-family:var(--display); font-size:20px; margin-right:auto}
  /* The way in is rose everywhere. He rejected the gold by name — "I don't like the yellow,
     it looks cheap" — and Check out is the same journey as Order now. No breathe animation
     here though: a pulsing button next to a total reads as pressure, not invitation. */
  /* The sticky checkout bar re-stated the face colour instead of inheriting it, so the phone's
     Check out button kept the old rose and its own 3.64:1 after every other button had moved.
     A restated value is a value that will drift; it now only says what is different about it. */
  .minibar .order-btn{flex:0 0 auto}
  /* The whole left side of the bar opens the sheet. A 44px icon would be the wrong target on
     the control people reach for most. */
  .minibar__open{
    position:absolute; inset:0 42% 0 0; appearance:none; border:0; background:none;
    cursor:pointer; font-size:0;
  }
  .minibar__open:focus-visible{outline:var(--line) solid var(--sky); outline-offset:-4px}
  /* The sound toggle belongs to the STORY. Once the visitor is in the ordering section the
     story is behind them, and on a 390px screen the button was sitting on top of a product
     name. It leaves rather than fights for the corner. */
  body.at-order .soundbtn{opacity:0; pointer-events:none; transform:translateY(8px)}
  .soundbtn{transition:opacity .2s ease, transform .2s ease}

  /* --- the box, as a sheet ------------------------------------------------- */
  .box{
    position:fixed; z-index:206; left:0; right:0; bottom:0; top:auto;
    max-height:82svh; overflow-y:auto; overscroll-behavior:contain;
    border:0; border-top:var(--line) solid var(--ink);
    box-shadow:0 -8px 0 rgba(51,39,66,.12);
    transform:translateY(101%);
    transition:transform .22s cubic-bezier(.2,.8,.2,1);
    padding-bottom:calc(16px + env(safe-area-inset-bottom));
  }
  .box.is-open{transform:none}
  .box__lid{
    position:sticky; top:0; z-index:2;
    display:flex; align-items:center; justify-content:space-between; gap:12px;
  }
  .box__close{
    appearance:none; min-width:44px; min-height:44px; border-radius:999px; cursor:pointer;
    border:2px solid var(--ink); background:var(--paper); color:var(--ink);
    font-family:var(--display); font-size:20px; line-height:1;
  }
  .box__close:focus-visible{outline:var(--line) solid var(--sky); outline-offset:2px}
  .sheet-veil{
    position:fixed; inset:0; z-index:204; background:rgba(51,39,66,.42);
    opacity:0; pointer-events:none; transition:opacity .22s ease;
  }
  body.sheet-open .sheet-veil{opacity:1; pointer-events:auto}
  body.sheet-open{overflow:hidden}

  /* two piles across is the most a 390px screen can show without shrinking the food to a
     thumbnail, and the food is what is being sold */
  .treats{grid-template-columns:repeat(2,minmax(0,1fr)); gap:clamp(22px,5vw,34px) 12px}
  /* room for the bar so the last row is never trapped under it */
  .bag{padding-bottom:78px}
}
@media (min-width:861px){
  .box__close{display:none}     /* the sheet controls do not exist beside a sticky sidebar */
  .sheet-veil{display:none}
}

/* --- footer ---------------------------------------------------------------- */
.foot{background:var(--ink); color:var(--paper); border-top:var(--line) solid var(--ink);
  padding:40px 20px calc(40px + env(safe-area-inset-bottom)); text-align:center}
.foot p{font-family:var(--ui); font-size:15px; margin:0 0 8px; color:#E9DFF3}
.foot a{color:var(--pink); display:inline-block; padding:11px 4px}   /* footer links are targets too — they measured 22px tall */

/* --- reduced motion: nothing that moves is load-bearing ------------------- */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .cue i{animation:none}
  .order-btn{transition:none}
  /* The motion is an invitation, never information — the button says "Order now" on its face,
     so removing it costs nothing to anyone who asked for less. The rose stays. */
  .bar .order-btn,
  .bake__go .order-btn,
  .curtain__foot .order-btn{animation:none}
  .bar .order-btn::before,
  .bake__go .order-btn::before,
  .curtain__foot .order-btn::before{display:none}
}

/* --- the voice switcher: both of his clones, comparable on the real page ------
   Sound is off until pressed, because Chrome and Safari refuse audio without a user
   gesture and scrolling is not one. Pressing A or B is the gesture AND the A/B test. */
.voicebar{display:inline-flex;align-items:center;gap:8px;
  border:var(--line) solid var(--ink);border-radius:999px;padding:4px 4px 4px 12px;background:var(--paper)}
.voicebar__lab{font-family:var(--ui);font-size:11px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--plum)}
.voicebar button{min-width:44px;min-height:36px;border-radius:999px;cursor:pointer;
  border:2px solid var(--ink);background:var(--paper);color:var(--ink);
  font-family:var(--display);font-size:15px;line-height:1}
.voicebar button.is-on{background:var(--gold)}
.voicebar button:focus-visible{outline:var(--line) solid var(--sky);outline-offset:2px}
@media (max-width:600px){.voicebar__lab{display:none}.voicebar{padding-left:4px}}

/* ============================================================================
   PHONE — measured at 390x844 on 2026-08-11 and it was BROKEN: the bar's three
   items (wordmark + voice switcher + Order now) are ~470px of content in a 390px
   viewport, so the whole document scrolled sideways and the wordmark, Bella and
   the button were all clipped off the right edge. A landing page that scrolls
   horizontally on the device most buyers arrive on is a defect, not a nitpick —
   they come from a Marketplace link or a flyer QR code, outdoors, one-handed.
   ========================================================================== */
html,body{max-width:100%; overflow-x:hidden}      /* belt: nothing may push the page wide */

@media (max-width:640px){
  .bar{gap:12px; padding-left:10px; padding-right:10px}
  /* 17px is what this rule ALWAYS produced — a second `max-width:640px` block eighty lines
     down re-declared it, so the 15px written here had never once applied. Stated once. */
  .bar__mark{font-size:17px; white-space:nowrap; min-width:0}
  .bar__right{gap:8px}
  .voicebar{padding:3px; gap:4px}
  .voicebar button{min-width:38px; min-height:38px; font-size:13px}
  /* ONE declaration for the phone size. There used to be two of these blocks setting
     `.order-btn` padding to different numbers — 10px 14px here and 11px 18px eighty lines
     down — so the button's real size was whichever rule happened to come last. */
  /* 12px, not 11px: at 15px/1.1 the box is then 44.5px on its own, so the 44px touch floor is
     met by padding and `min-height` never adds an unbalanced strip under the label. */
  .order-btn{--pad-y:12px; --pad-x:18px; font-size:15px}
  /* nowrap belongs ONLY to the pill that must stay one line in a fixed row. Everywhere else a
     long label wrapping inside the pill beats a pill hanging off the screen. */
  .bar .order-btn{white-space:nowrap}
  .curtain__foot p{font-size:17px}
  .bella-sticker{width:38vw; right:-3vw; bottom:8vh}
  /* Bella sat over the copy at this width; lifting her clears the reading line. */
}

/* Second measurement, 390x844: shrinking type was not enough. Wordmark + voice switcher +
   Order button is ~390px of MIN-content, so flex had nothing left to give and the button
   still hung off the edge. Three things do not fit; two do. So on a phone the switcher
   LEAVES the bar and becomes a floating control at the bottom-left — which is also where it
   belongs, next to the story it controls rather than in the header. The bar keeps the two
   things that must always be visible: who this is, and how to order. */
@media (max-width:640px){
  .voicebar{
    position:fixed; z-index:210;
    left:12px; bottom:calc(12px + env(safe-area-inset-bottom));
    background:var(--paper); box-shadow:0 4px 0 var(--ink);
  }
  /* The big button stops being big on a phone: 24px of display type made "See the whole bake
     and order →" a 306px nowrap pill in a 320px viewport. It keeps its generous padding at the
     next size down the locked scale, which the screen can actually hold. */
  .order-btn--big{--pad-y:14px; --pad-x:24px; font-size:20px}
}

/* Below 400 the wordmark and the button cannot both be full size. The BUTTON wins — it is the
   one thing the page exists to make easy to press, and the name is readable everywhere else on
   the screen. THIS BLOCK MUST STAY BELOW THE 640 BLOCKS: it used to sit above them, so at 320
   the 17px rule won on source order and the wordmark was hard-clipped mid-letter — visible in
   the 320 shot, invisible in the diff. Equal specificity is decided by order, and a narrower
   media query does not outrank a wider one. */
@media (max-width:400px){
  .bar{gap:8px; padding-left:8px; padding-right:8px}
  /* the last two off-rhythm gaps on the page are gone; every space between two things is now
     one of 4 8 12 16 24 32 48 64 96 */
  .bar__mark{font-size:13px}
  .voicebar__lab{display:none}
}

/* The narration toggle. One control, one voice — he chose B on 2026-08-11, so the A/B
   switcher retired. The dot fills in when sound is on, which is the whole state anyone
   needs to read at a glance. */
.soundbtn{display:inline-flex;align-items:center;gap:8px;cursor:pointer;
  min-height:44px;padding:10px 18px;border-radius:999px;
  border:var(--line) solid var(--ink);background:var(--paper);color:var(--ink);
  font-family:var(--ui);font-size:15px;font-weight:600}
.soundbtn__ico{width:13px;height:13px;border-radius:50%;border:2px solid var(--ink);background:transparent}
.soundbtn.is-on{background:var(--pink)}
.soundbtn.is-on .soundbtn__ico{background:var(--ink)}

/* THE CRAWLABLE STORY, VISUALLY HIDDEN.
   This copy used to be hidden by the scroll engine when it mounted. Deleting the engine left it
   dumped raw and unstyled under the hero — caught by opening the 390 shot on 2026-08-13, not by
   reading the diff. The film now TELLS this story out loud, so showing it again as text is
   redundant for a human; it stays in the DOM because it is what search engines, link previews
   and screen readers actually read.
   Clipped rather than display:none deliberately — display:none is the one form of hiding that
   assistive technology also skips. */
[data-sw-seo]{
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap;
}

/* THE FILM, full bleed behind the curtain. Oliver: "the video playing in the background of
   the home screen". object-fit:cover so it fills any shape without letterboxing — a phone in
   portrait and a 1440 desktop are wildly different crops of the same 16:9 film. */
.curtain__film{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; z-index:0; pointer-events:none;
  /* HIDDEN UNTIL IT IS ACTUALLY PLAYING, and hidden again the moment it ends. The page opens
     and closes on paper white with the wordmark plainly readable — his note, 2026-08-13:
     "the very beginning, at least while it was finishing loading, with the original white
      background with the dots, before the video kind of takes over... that part we're having a
      hard time with right here, you can't see it very clearly."
     It doubles as the loading state, so there is no black rectangle and no half-loaded frame. */
  opacity:0; transition:opacity .9s ease;
}
.curtain.is-playing .curtain__film{opacity:1}
/* The wordmark owns the white; over moving picture it steps back so the film can be seen and
   so the dots are not competing with a lit doorway for the same pixels. */
#dots{transition:opacity .9s ease}
.curtain.is-playing #dots{opacity:.34}
/* The wordmark and the button sit ON the film, so they need to win the stack and stay legible
   over moving picture. The scrim is a wash of the paper colour, not black — this palette is
   warm and a black veil would drag it cold, which he has rejected by name. */
/* z-index ONLY — never position. The canvas is deliberately absolute (see the note above the
   .curtain rule); giving it position:relative put it back in the flex flow, where it ate the
   full 390px row and shoved the Order button clean off the right edge. An absolutely
   positioned element already honours z-index, so there was nothing to gain and a layout to
   lose. Measured 2026-08-13: .curtain__foot started at x=390 in a 390px viewport. */
.curtain__film ~ #dots{z-index:2}
.curtain::after{
  content:""; position:absolute; inset:0; z-index:1; pointer-events:none;
  background:linear-gradient(180deg,rgba(255,255,255,.62) 0%,rgba(255,255,255,.34) 42%,rgba(255,255,255,.78) 100%);
}
.curtain__foot{position:relative; z-index:3}

/* THE SOUND CONTROL, CALLING FOR ATTENTION. His ask: "it needs to be flashing for a moment up
   front, first thing, so they can see it. It gets them drawn to it real quick."
   Six seconds, then it stops for good — a control that blinks forever is an irritation, and
   film.js only starts it when the sound is actually muted. */
.soundbtn{position:relative; z-index:3}
.soundbtn.is-calling{animation:sound-calling .9s ease-in-out 6}
@keyframes sound-calling{
  0%,100%{background:var(--paper); box-shadow:0 0 0 0 rgba(217,92,122,0);    transform:scale(1)}
  50%    {background:var(--gold);  box-shadow:0 0 22px 5px rgba(217,92,122,.66); transform:scale(1.09)}
}
@media (prefers-reduced-motion:reduce){
  /* It still has to be FINDABLE without moving — so it keeps the gold, loses the pulse. */
  .soundbtn.is-calling{animation:none; background:var(--gold)}
}
.soundbtn:focus-visible{outline:var(--line) solid var(--sky);outline-offset:2px}
@media (max-width:640px){
  .soundbtn{position:fixed;z-index:210;left:12px;top:calc(70px + 12px);bottom:auto;
    box-shadow:0 4px 0 var(--ink);padding:10px 14px;
    transition:opacity .2s ease, transform .2s ease}
  .soundbtn__lab{display:none}
  .soundbtn__ico{width:15px;height:15px}
  /* It is FIXED here, so it does not scroll away with the story it belongs to — it sat on top
     of the section headings all the way down the page (seen at 390, 2026-08-13). film.js sets
     this the moment the film leaves the viewport, and clears it if they scroll back up. */
  .soundbtn.is-away{opacity:0;pointer-events:none;transform:translateY(-8px)}
}

/* PHONE COMPOSITION, measured at a TRUE 390x844 on 2026-08-11 (see tools/shoot.py — headless
   --window-size clamps at ~511 and lied about this). No overflow, but Bella was sitting on top
   of the wordmark and the body line: she covered "WITH MARI" completely. The dog is the charm,
   not the headline. She drops to the bottom-right corner, below the lettering, small enough to
   read as a sticker on the glass rather than a character blocking the sign. */
@media (max-width:640px){
  .bella-sticker{width:34vw; right:-2vw; bottom:0; max-height:38svh; object-fit:contain; object-position:bottom right}
  .curtain__foot{padding-bottom:calc(22px + env(safe-area-inset-bottom))}
  .curtain__foot p{max-width:22ch; margin-left:0; text-align:left}
  .curtain__foot{text-align:left}
  .cue{text-align:left}
}

/* The sound button lives in the bottom-left on phones, which is exactly where the scroll cue
   was — they collided. The cue loses: "Skip the story" already sits above it and a scrollable
   page announces itself the moment you touch it. */
@media (max-width:640px){ .cue{display:none} }
