/* ===========================================================
   SHAKE HOUSE · Tap and Rate template v4
   Brand-led: the shop's own hot pink and cyan, heavy caps.
   SWAP POINT 1 — BRAND. Change these and the site follows.
   =========================================================== */
:root{
  /* SWAP POINT 1 — BRAND.
     These are read off the reference build the client approved
     (shakehouse-menu-magic.lovable.app), sampled from its own custom
     properties via canvas, not eyeballed from a screenshot:
       --brand   oklch(58% .23 355)   = #D71481
       --sky     oklch(72% .135 221)  = #00B7E0
       --accent  oklch(88% .16 91)    = #FFD343
       --tangerine oklch(75% .17 62)  = #F8910E
       --ink     oklch(16% .018 345)  = #130A0F
       --background oklch(98.5% .006 48) = #FEF9F7
     This pink measures 4.67:1 on this cream, so it PASSES WCAG AA for
     body-size text. The pink we had, #D82A71, measured 4.41:1 and did
     not. Prices therefore render in the full brand pink now; the
     --pink-dark workaround is only for pink text on white. */
  --pink:#D71481;
  --pink-dark:#841242;   /* logo outline, 5,565 px sampled. 9.37:1 on cream */
  --cyan:#00B7E0;
  --cyan-dark:#0E8CAB;
  --plum:#583862;        /* shopfront facade. Kept as a field colour only:
                            pink on plum is 2.00:1 and must never carry text */
  --sky:#00B7E0;
  --yellow:#FFD343;
  --tangerine:#F8910E;   /* the reference hero's scroll button */
  --ink:#130A0F;
  --ink-2:#241A2A;
  --ink-muted:#C1B3BA;
  --cream:#FEF9F7;
  --paper:#FFFFFF;
  --grey:#66555E;        /* 6.64:1 on cream */
  --line:#DED0D7;

  /* Archivo Black is a single-weight face: its file is weight 400 and it
     is already black. Asking for 700 or 800 makes the browser synthesise
     a faux bold on top of it, which is the same class of bug as the
     font-weight:900 one in the notes. Display runs at 400 throughout.
     Latin payload: Archivo Black 18,604 + Nunito Sans 49,648 = 68,252,
     against 104,580-111,816 for Bricolage + Archivo. */
  --display:"Archivo Black","Archivo",-apple-system,BlinkMacSystemFont,Arial,sans-serif;
  --body:"Nunito Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --wrap:1220px; --wrap-narrow:650px; --r:14px;
  /* easeOutQuad: the most-used curve across the ten food sites measured */
  --ease:cubic-bezier(.25,.46,.45,.94); --dur:.2s; --dur-slow:.3s;
}
*{box-sizing:border-box}
/* No scroll-behavior:smooth. It was global, so every category chip animated
   the full distance down a 6700px page instead of arriving. Measured here:
   anchor jumps are instant now, which is what the sub-100ms response budget
   asks for. https://web.dev/articles/rail */
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--cream);color:var(--ink);
  font-family:var(--body);font-size:17px;line-height:1.6;
  -webkit-font-smoothing:antialiased;padding-bottom:78px;text-wrap:pretty;
  /* The full-bleed picks strip is sized in vw, and vw counts the scrollbar,
     so on desktop it overhangs by the scrollbar width and the page scrolls
     sideways. clip rather than hidden: hidden on body kills position:sticky,
     which the header and the menu chip rail both rely on. */
  overflow-x:clip;
}
img{max-width:100%;display:block}
picture{display:contents}
a{color:inherit}
.wrap{max-width:var(--wrap);margin:0 auto;padding:0 24px}
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
:focus-visible{outline:3px solid var(--cyan);outline-offset:3px}

h1,h2,h3{font-family:var(--display);font-weight:400;line-height:1.1;letter-spacing:0;
  margin:0;text-transform:uppercase;text-wrap:balance}
/* The ladder is read off the reference build at desktop width, measured
   per element rather than taken from a screenshot:
       h1 144px   h2 72px   h3 48px   item name 18px
   so hero:section is 2:1, not the 4:1 an earlier note here claimed. That
   note said "every other heading on the whole site sits around 1.8rem",
   which is simply wrong: its section headings are 4.5rem and its category
   headings 3rem. Cutting h2 to 1.9rem on the strength of it made the page
   timid. Archivo Black has no opsz axis, and the reference sets tracking
   to normal, so these stay at zero. */
h1{font-size:clamp(3rem,1.4rem + 8vw,9rem);line-height:.86;letter-spacing:0}
h2{font-size:clamp(1.9rem,1.1rem + 3.4vw,4.5rem);line-height:1;letter-spacing:0}
h3{font-size:clamp(1.5rem,1rem + 2vw,3rem);line-height:1;letter-spacing:0}
/* Item names are the one heading the reference sets in the body face:
   Nunito Sans 900 at 18px, mixed case. */
h4{font-family:var(--body);font-weight:900;font-size:1.125rem;margin:0;
  letter-spacing:0;text-transform:none}
.eyebrow{
  font-family:var(--body);font-size:.76rem;font-weight:700;letter-spacing:.11em;
  text-transform:uppercase;color:var(--pink);margin:0 0 16px
}
.lede{font-size:clamp(1.08rem,1.5vw,1.3rem);line-height:1.5;color:var(--grey);max-width:46ch}

/* ---------- photo slots ---------- */
.ph{position:relative;background:#F3E6EE center/cover no-repeat;border-radius:var(--r);overflow:hidden}
.ph:not(.has-img){
  background:repeating-linear-gradient(45deg,#F7E9F1,#F7E9F1 12px,#F2DEE9 12px,#F2DEE9 24px);
  display:grid;place-items:center
}
.ph:not(.has-img)::after{
  content:attr(data-label);font-size:.66rem;font-weight:800;letter-spacing:.12em;
  text-transform:uppercase;color:var(--pink);text-align:center;padding:14px;max-width:26ch;line-height:1.7
}

/* ---------- buttons ---------- */
/* ---------- buttons ----------
   Four things here are deliberate and easy to undo by accident.

   1. Radius 12px, not a pill. The pill is what a button looks like when
      nobody decided, and it reads as theme rather than brand. Pills stay
      on the chips and the amenity tags, where the element really is a
      small token in a row of tokens.
   2. The button presses down into its own bottom edge instead of lifting
      off the page. The face travels 3px, the edge collapses to nothing,
      and the face lands where the edge was. A lift is the default move
      in every framework; a press is what a physical button does.
   3. The 2px border is an inset shadow, so it costs no layout and never
      shifts the label by a pixel when a variant drops it. Its width is
      taken out of the horizontal padding, which is why 30px here reads
      as the same inset as the old 32px against a real border.
   4. Vertical padding is asymmetric, 18 over 16. A cap-and-lowercase
      label sits optically high in its box, so an even split looks
      bottom-heavy at this weight.

   Variants set four custom properties and nothing else. --fill is the
   face, --ring the inset border, --edge the bottom edge, --face the text.
   Tiers differ by how much of the shape is filled in, never by hue.
   ------------------------------------------------------------------ */
.btn{
  --fill:var(--ink-2); --ring:var(--ink-2); --edge:var(--ink); --face:#fff;
  display:inline-flex;align-items:center;justify-content:center;gap:9px;
  font-family:var(--body);font-size:.97rem;font-weight:800;letter-spacing:.01em;
  padding:18px 30px 16px;border-radius:12px;border:0;line-height:1;
  background:var(--fill);color:var(--face);text-decoration:none;cursor:pointer;min-height:54px;
  box-shadow:inset 0 -3px 0 var(--edge),inset 0 0 0 2px var(--ring);
  transition:box-shadow .1s ease,transform .1s ease,background .14s ease,color .14s ease
}
.btn:active{transform:translateY(2px);box-shadow:inset 0 0 0 2px var(--ring)}

.btn:hover        {--fill:var(--pink);--ring:var(--pink);--edge:var(--pink-dark)}
.btn.pink         {--fill:var(--pink);--ring:var(--pink);--edge:var(--pink-dark)}
.btn.pink:hover   {--fill:var(--ink-2);--ring:var(--ink-2);--edge:var(--ink)}
.btn.cyan         {--fill:var(--cyan);--ring:var(--cyan);--edge:var(--cyan-dark);--face:var(--ink)}
.btn.cyan:hover   {--fill:var(--ink-2);--ring:var(--ink-2);--edge:var(--ink);--face:#fff}
.btn.white        {--fill:#fff;--ring:#fff;--edge:#CDC0C9;--face:var(--ink)}
.btn.white:hover  {--fill:var(--cyan);--ring:var(--cyan);--edge:var(--cyan-dark);--face:var(--ink)}
/* Hollow tiers take no lip: a transparent face would show the page through
   it. They still share the filled tiers' box, so a row stays aligned. */
.btn.outline      {--fill:transparent;--ring:var(--ink);--edge:transparent;--face:var(--ink)}
.btn.outline:hover{--fill:var(--ink-2);--ring:var(--ink-2);--edge:var(--ink);--face:#fff}
.btn.outline-light      {--fill:transparent;--ring:#fff;--edge:transparent;--face:#fff}
.btn.outline-light:hover{--fill:#fff;--ring:#fff;--edge:#CDC0C9;--face:var(--ink)}
.btn.sm{padding:13px 20px 11px;font-size:.87rem;min-height:46px;border-radius:10px}

/* ---------- header ---------- */
/* Opaque, and deliberately not blurred. backdrop-filter:blur(12px) sat here
   and made the browser re-read and re-blur the strip behind a sticky element
   on every scroll frame, which is the most expensive thing that was on this
   page. A solid background costs nothing and looks the same over cream.
   Re-adding it also brings back the containing-block trap documented in
   CLAUDE.md, where the mobile panel sized itself to the 78px bar. */
header{
  position:sticky;top:0;z-index:60;overflow:visible;
  background:var(--cream);
  border-bottom:1px solid var(--line)
}
header .wrap{display:flex;align-items:center;justify-content:space-between;height:78px;gap:16px;position:relative}
.mark{display:block;text-decoration:none}
/* ---------- the logo ----------
   It used to be scaled to 1.92 so it overhung the bar onto the hero, then
   retract on scroll. That read as odd rather than deliberate, and it carried
   a drop-shadow filter on an element that was simultaneously being
   transform-scaled, which forces a repaint instead of a composite.
   The wordmark is simply large and sits in the bar, shrinking a little on
   scroll so the header still answers the page moving. Direct values in both
   rules: driving the scale through a custom property does not re-resolve. */
.mark{display:block;text-decoration:none;position:relative;z-index:2}
.mark img{
  height:clamp(54px,5.1vw,68px);width:auto;display:block;transform-origin:left center;
  transform:scale(1);
  transition:transform .34s var(--step)
}
html.stuck .mark img{transform:scale(.86)}
@media(max-width:620px){
  .mark img{height:clamp(46px,11vw,56px)}
  html.stuck .mark img{transform:scale(.9)}
}
nav{display:none;gap:26px;align-items:center}
nav a{
  font-size:.9rem;font-weight:700;letter-spacing:0;text-decoration:none;color:var(--ink);
  position:relative;padding:6px 1px
}
nav a::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:3px;border-radius:2px;
  background:var(--pink);transform:scaleX(0);transform-origin:right center;
  transition:transform .26s var(--ease)
}
nav a:hover::after,nav a[aria-current="page"]::after{transform:scaleX(1);transform-origin:left center}
nav a:hover,nav a[aria-current="page"]{color:var(--pink)}
@media(min-width:1000px){nav{display:flex}}

/* ---------- hero ---------- */
/* ---------- the hero ----------
   The hero carries a photograph of the actual shop or the actual product,
   full bleed, under a dark gradient. This replaced a flat pink field with a
   small photo box beside the headline, and the reason is blunt: a dessert
   parlour's website should look like dessert, and the food is the strongest
   asset here. The client read the flat version as generic and was right.

   Pink is not gone, it moved: the header, the buttons, the bands and the
   section fields still carry the brand. The hero borrows its contrast from
   the photo instead.

   Per page, .hero.shot sets --jpg and --set inline. Two declarations rather
   than one so a browser that cannot parse image-set() keeps the JPEG, the
   same pattern slotBackgrounds() uses in js/site.js. */
.hero{position:relative;background:var(--pink);color:#fff;overflow:hidden}
.hero.shot{
  background-color:#140a10;
  background-image:var(--ov),var(--jpg);
  background-size:cover;
  background-position:var(--pos,center);
  background-repeat:no-repeat
}
@supports (background-image:image-set(url("a.webp") type("image/webp"))){
  .hero.shot{background-image:var(--ov),var(--set)}
}
:root{
  --ov:linear-gradient(180deg,rgba(12,5,9,.34) 0%,rgba(12,5,9,.58) 46%,rgba(12,5,9,.82) 100%)
}
/* the dot field is noise over a photograph */
.hero.shot::before{display:none}

/* photo heroes centre their content; the headline is the thing people see
   first and centring it stops the type fighting the image's composition */
.hero.shot .hero-grid{text-align:center;max-width:1240px;margin:0 auto}
.hero.shot h1{
  font-size:clamp(2.75rem,1.3rem + 8vw,9rem);
  margin-bottom:16px;
  text-shadow:0 3px 26px rgba(0,0,0,.5)
}
.hero.shot .lede{
  margin-left:auto;margin-right:auto;max-width:54ch;
  font-size:clamp(1.02rem,1.1vw,1.18rem);
  text-shadow:0 1px 10px rgba(0,0,0,.5)
}
.hero.shot .cta-row{justify-content:center;margin-top:22px}
.hero.shot .direct-line{margin-left:auto;margin-right:auto;text-align:center;max-width:none}
.hero.shot .hero-rating{justify-content:center}
.hero.shot .wrap{padding-top:clamp(26px,3.4vw,46px);padding-bottom:clamp(26px,3.4vw,46px)}
.hero.shot .crumbs{text-align:left}
.hero::before{
  content:"";position:absolute;inset:0;opacity:.42;pointer-events:none;
  background-image:radial-gradient(rgba(255,255,255,.55) 1.5px,transparent 1.6px);
  background-size:24px 24px
}
.hero .wrap{position:relative;z-index:1}
/* Tightened deliberately. Above-fold content takes ~57% of all viewing time,
   the 100px above the fold is looked at about twice as much as the 100px
   below, and over 65% of above-fold attention lands in the top half of the
   first screen. The display scale is NOT cut to achieve that: see CLAUDE.md
   on the five rejected rounds. The padding is.
   https://www.nngroup.com/articles/scrolling-and-attention/ */
.hero .wrap{position:relative;z-index:2;padding-top:clamp(34px,4.4vw,62px);padding-bottom:clamp(34px,4.4vw,58px)}
.hero .eyebrow{color:#fff;opacity:.9}
.hero h1{
  font-size:clamp(3rem,1.4rem + 8vw,9rem);line-height:.86;letter-spacing:0;
  margin-bottom:20px;
  /* Milk Bar sets its loudest type as a hard offset shadow with no blur. On
     a saturated field that is what stops huge white caps going soft. */
  text-shadow:6px 6px 0 rgba(22,16,26,.22)
}
.hero h1 .cy{display:block;color:var(--cyan)}
.hero .lede{color:#fff;max-width:46ch;font-weight:500}
/* The direct-order line sits under the buttons, not in a banner. Nobody in
   the sector runs a banner for this and banner blindness is the one certainty
   in the evidence; Chaiiwala ships the same shape, a single benefit line
   immediately above its order CTA. Framed as what the shop charges rather
   than what it keeps: the margin framing was the client's first rejection,
   and a claim about yourself cannot be falsified by a platform's fees. */
.direct-line{
  margin:14px 0 0;font-family:var(--body);font-size:.95rem;font-weight:600;
  color:rgba(255,255,255,.88);max-width:42ch
}
.cta-row{display:flex;flex-wrap:wrap;gap:12px;margin-top:26px}
.hero .cta-row .btn{min-width:13.5rem}
@media(max-width:560px){.hero .cta-row{display:grid;grid-template-columns:1fr;gap:10px}.hero .cta-row .btn{min-width:0;width:100%}}
.hero-grid{display:block}
.hero-foot{display:grid;gap:clamp(26px,4vw,40px);grid-template-columns:1fr}
@media(min-width:950px){.hero-foot{grid-template-columns:1.02fr .98fr;align-items:center;gap:clamp(30px,4vw,56px)}}
.hero-shot{aspect-ratio:16/10;border-radius:var(--r)}
.hero-rating{display:flex;align-items:center;gap:9px;margin:22px 0 0;font-size:.9rem;font-weight:600;color:#fff}
.hero-rating .stars{color:var(--yellow);letter-spacing:1px}
@media(max-width:560px){
  /* .hero.shot h1 is two classes deep, so it outranks a bare .hero h1 and
     was keeping the desktop clamp on a phone: "MILKSHAKE." ran off the right
     edge at 390px. Both selectors have to be named. The ceiling is set by the
     longest single word in any hero headline, not by the viewport. */
  .hero h1,.hero.shot h1{font-size:clamp(2.1rem,1rem + 6.2vw,3rem)}
}
@media(max-width:949px){.hero-shot{aspect-ratio:16/10;margin-top:26px}.hero .wrap{padding-bottom:clamp(38px,6vw,70px)}}


/* ---------- delivery strip ---------- */
/* Plum, not near-black: it is the colour of the actual shopfront. */
.deliver{background:var(--plum);color:#fff;padding:28px 0}
.deliver .wrap{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:14px 22px;text-align:center}
.deliver p{margin:0;font-weight:600;font-size:.96rem}
.deliver .direct{color:var(--cyan);font-weight:800}

/* ---------- sections ---------- */
section{padding:clamp(46px,5.6vw,84px) 0}
section.paper{background:var(--paper)}
section.ink{background:var(--plum);color:#fff}
section.ink .lede{color:rgba(255,255,255,.72)}
section.ink .eyebrow{color:var(--cyan)}
/* ---------- the section opener ----------
   Every section used to run the same shape: big caps heading, then a grey
   one-line lede under it, left aligned at 58ch with the right half of the
   page empty, four times down the hub. The eyebrow labels were removed as
   the generated-page tell, but this is the other half of the same tell, and
   the client spotted it.

   Above 900px the heading and the lede sit side by side on one baseline with
   a rule under them, which fills the dead right-hand column, gives each
   section a horizontal edge instead of a stack, and breaks the vertical
   repetition without changing a word of the copy. Stacked below that, where
   there is no second column to use. */
.sec-head{margin-bottom:clamp(28px,3.4vw,42px)}
.sec-head .lede{margin-top:14px;max-width:52ch}
/* The two-column opener existed to fill the dead space a 4rem heading left
   beside it. At 1.9rem there is no dead space, so the heading and its line
   simply stack, with a rule under them the way the reference does it. */
.sec-head{padding-bottom:16px;border-bottom:1px solid var(--line)}
.sec-head h2{margin:0}
.sec-head .lede{margin-top:10px;font-size:1rem;max-width:62ch}
section.ink .sec-head,.reelsec .sec-head{border-bottom-color:rgba(255,255,255,.18)}
.num{
  display:grid;place-items:center;width:46px;height:46px;border-radius:50%;flex:0 0 auto;
  background:var(--ink);color:#fff;font-family:var(--display);font-weight:400;font-size:.88rem
}
.sec-bar{display:flex;align-items:center;gap:15px;padding-bottom:0;margin-bottom:22px}
.sec-bar .txt{display:grid;grid-template-columns:auto 1fr;align-items:center;gap:16px;min-width:0;flex:1}
.sec-bar .txt::after{content:"";height:2px;background:var(--line);border-radius:2px}
.sec-bar h3{font-size:clamp(1.6rem,3.6vw,2.4rem)}
.sec-bar .note{font-size:.84rem;color:var(--grey);margin:4px 0 0;text-transform:none;font-weight:600}

/* ---------- facts / ratings ---------- */
.facts{display:flex;flex-wrap:wrap;gap:12px 28px;align-items:center;padding:18px 0;font-size:.9rem;color:var(--grey)}
.facts strong{color:var(--ink)}
.facts .dot{color:var(--pink)}
.rating{display:flex;align-items:center;gap:7px;font-weight:700;color:var(--ink)}
.stars{color:var(--yellow);letter-spacing:1px}
.rating small{font-weight:500;color:var(--grey)}

/* ---------- branch cards ---------- */
.loc-grid{display:grid;gap:22px;grid-template-columns:1fr}
@media(min-width:720px){.loc-grid{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1040px){.loc-grid{grid-template-columns:repeat(3,1fr)}}
.loc{
  background:var(--paper);border:1px solid var(--line);border-radius:var(--r);
  display:flex;flex-direction:column;overflow:hidden;
  transition:transform .18s ease,box-shadow .18s ease
}
.loc:hover{transform:translateY(-3px);border-color:var(--ink)}
.loc .ph{aspect-ratio:16/10;border-radius:0;border-bottom:1px solid var(--line)}
.loc-body{padding:24px;display:flex;flex-direction:column;flex:1}
.loc .area{font-size:.66rem;font-weight:800;letter-spacing:.13em;text-transform:uppercase;color:var(--pink);margin-bottom:9px}
.loc h3{margin-bottom:10px}
.loc .meta{font-size:.9rem;color:var(--grey);margin:0 0 6px}
.loc .meta a{font-weight:700;color:var(--ink);text-decoration:none;border-bottom:2px solid var(--cyan)}
.loc .hrs{font-size:.86rem;color:var(--grey);margin:10px 0 18px}
.loc .acts{display:flex;flex-wrap:wrap;gap:9px;margin-top:auto}

/* ---------- signatures ---------- */
/* The picks run full bleed, edge to edge, with the caption sitting on the
   photo. Everything else on this page is a card inside the 1220px container,
   and a page where nothing ever leaves the container reads as a template. The
   heading above stays in the container; only the strip breaks out. */
.sig-grid{
  display:grid;gap:2px;grid-template-columns:repeat(2,1fr);
  width:100vw;margin-left:calc(50% - 50vw);margin-top:6px
}
@media(min-width:1000px){.sig-grid{grid-template-columns:repeat(4,1fr)}}
.sig{position:relative;overflow:hidden;background:var(--ink)}
.sig:hover .sig-body{padding-bottom:26px}
.sig .ph{aspect-ratio:4/5;border-radius:0}
.sig-body{
  transition:padding var(--dur) var(--ease);
  position:absolute;inset:auto 0 0 0;padding:72px 18px 20px;color:#fff;
  background:linear-gradient(to top,rgba(8,4,10,.96) 0%,rgba(8,4,10,.88) 34%,rgba(8,4,10,.55) 68%,rgba(8,4,10,0) 100%)
}
.sig .film{font-size:.6rem;font-weight:800;letter-spacing:.13em;text-transform:uppercase;color:var(--cyan);margin-bottom:7px}
.sig h4{font-size:1.14rem;margin-bottom:5px;text-transform:uppercase;color:#fff;letter-spacing:.005em}
.sig .price{font-family:var(--display);font-weight:400;color:#fff;font-size:1.2rem}
.sig p{display:none}

/* ---------- menu ---------- */
.menu-tools{display:grid;gap:14px;grid-template-columns:1fr;margin-bottom:4px}
@media(min-width:800px){.menu-tools{grid-template-columns:minmax(0,1fr) 340px;align-items:center}}
.menu-tools .lede{margin:0;font-size:.95rem}
.search{position:relative}
.search input{
  width:100%;font-family:var(--body);font-size:1rem;padding:14px 16px 14px 42px;
  border:0;border-radius:12px;box-shadow:inset 0 0 0 2px var(--ink);
  background:var(--paper);color:var(--ink);min-height:52px
}
.search input::placeholder{color:var(--grey)}
.search svg{position:absolute;left:16px;top:50%;transform:translateY(-50%);width:18px;height:18px;opacity:.55}
.chip-rail{
  position:sticky;top:78px;z-index:30;background:var(--paper);
  margin:0 -24px 30px;padding:12px 24px;border-bottom:1px solid var(--line)
}
.chip-rail::before,.chip-rail::after{
  content:"";position:absolute;top:0;bottom:0;width:30px;pointer-events:none;z-index:2
}
.chip-rail::before{left:0;background:linear-gradient(to right,var(--paper),transparent)}
.chip-rail::after{right:0;background:linear-gradient(to left,var(--paper),transparent)}
.chips{display:flex;gap:8px;overflow-x:auto;padding:0;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.chips::-webkit-scrollbar{display:none}
.chip{
  flex:0 0 auto;font-family:var(--body);font-size:.84rem;font-weight:700;letter-spacing:.01em;
  padding:11px 18px;border-radius:999px;border:1px solid var(--line);background:var(--paper);color:var(--ink);
  cursor:pointer;white-space:nowrap;min-height:44px;
  text-decoration:none;display:inline-flex;align-items:center
}
.chip:hover{background:var(--cyan)}
.chip[aria-selected="true"]{background:var(--pink);border-color:var(--pink);color:#fff}

/* Each category is a <details>. 112px scroll offset is the value Chaiiwala
   declares on its menu anchors, and it clears this site's 78px header. */
.mcat{border-bottom:1px solid var(--line);scroll-margin-top:112px}
.mcat:first-of-type{border-top:1px solid var(--line)}
.mcat>summary{
  display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:18px;
  padding:19px 0;cursor:pointer;list-style:none
}
.mcat>summary::-webkit-details-marker{display:none}
.mcat>summary:hover h3{color:var(--pink)}
.mcat .num{width:38px;height:38px;font-size:.76rem}
.mcat-t{min-width:0}
.mcat-t .note{
  display:block;font-family:var(--body);font-size:.83rem;font-weight:600;
  color:var(--grey);margin-top:5px;text-transform:none;letter-spacing:0
}
.mcat .count{
  display:flex;align-items:center;gap:14px;font-family:var(--body);
  font-size:.76rem;font-weight:700;color:var(--grey);font-variant-numeric:tabular-nums
}
.mcat .count::after{
  content:"+";font-family:var(--display);font-weight:400;color:var(--pink);
  font-size:1.4rem;line-height:1
}
.mcat[open] .count::after{content:"\2013"}
.mgrid{display:grid;gap:18px 56px;grid-template-columns:1fr;padding:2px 0 34px}
@media(min-width:760px){.mgrid{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1150px){.mgrid{grid-template-columns:repeat(3,1fr);gap:18px 48px}}

.mitem{display:grid;grid-template-columns:1fr auto;align-items:baseline;gap:6px 18px;padding:0}
.mitem .t{min-width:0;font-family:var(--body);font-weight:900;font-size:1.125rem;
  letter-spacing:0;line-height:1.25}
.mitem .d{
  grid-column:1/-1;display:none;font-family:var(--body);font-weight:400;
  font-size:14px;line-height:18px;letter-spacing:0;color:var(--grey);margin:0;
  text-transform:none
}
.mitem .p{
  font-family:var(--display);font-weight:400;color:var(--pink);white-space:nowrap;
  font-size:1.25rem;letter-spacing:0;min-width:4.2rem;text-align:right;
  font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1
}
.mitem .p.tbc{color:var(--grey);font-family:var(--body);font-size:.74rem;font-weight:700;letter-spacing:.04em}
/* when a whole section is one price, the header says so and the column goes away */
.mcat.flat .mitem{grid-template-columns:1fr}
.mcat.flat .mitem .p{display:none}
@media(min-width:768px){.mitem .d{display:block}}
.mitem.hide{display:none}
.mcat.hide{display:none}
.noresult{padding:28px 0;color:var(--grey);font-weight:600}
.allergy{background:var(--cream);border-left:4px solid var(--pink);padding:15px 18px;border-radius:0 10px 10px 0;font-size:.89rem;color:var(--grey);margin-top:26px}

/* ---------- location detail ---------- */
.detail{display:grid;gap:34px;grid-template-columns:1fr}
@media(min-width:920px){.detail{grid-template-columns:1.05fr .95fr;gap:54px}}
dl.spec{margin:0;border-top:3px solid var(--ink)}
dl.spec>div{display:grid;grid-template-columns:118px 1fr;gap:16px;padding:16px 0;border-bottom:1px solid var(--line)}
dl.spec dt{font-size:.66rem;font-weight:800;letter-spacing:.13em;text-transform:uppercase;color:var(--pink);margin:0}
dl.spec dd{margin:0;font-size:.97rem}
dl.spec dd a{text-decoration:none;border-bottom:2px solid var(--cyan);font-weight:600}
.amen{display:flex;flex-wrap:wrap;gap:8px;margin:0;padding:0;list-style:none}
.amen li{font-size:.78rem;font-weight:800;letter-spacing:.04em;text-transform:uppercase;color:var(--grey);background:var(--paper);border:1px solid var(--line);border-radius:999px;padding:8px 15px}

/* ---------- faq ---------- */
.faq{border-top:3px solid var(--ink);max-width:74ch}
.faq details{border-bottom:1px solid var(--line)}
.faq details summary{cursor:pointer;padding:19px 0;font-weight:700;font-size:1.02rem;list-style:none;display:flex;justify-content:space-between;align-items:center;gap:16px;min-height:50px}
.faq details summary::-webkit-details-marker{display:none}
.faq details summary::after{content:"+";font-family:var(--display);font-weight:400;color:var(--pink);font-size:1.5rem;line-height:1}
.faq details[open] summary::after{content:"\2013"}
.faq details .ans{padding:0 0 19px;color:var(--grey);font-size:.96rem;max-width:62ch}

/* ---------- loyalty ---------- */
.loyalty{background:var(--cyan);color:var(--ink)}
.loyalty .eyebrow{color:var(--pink)}
.loyalty .lede{color:rgba(22,16,26,.78)}
.loyal-grid{display:grid;gap:36px;align-items:center;grid-template-columns:1fr}
@media(min-width:880px){.loyal-grid{grid-template-columns:1.05fr .95fr}}
.loyal-grid .ph{aspect-ratio:4/3;border-radius:var(--r)}

/* ---------- footer ----------
   Hierarchy by opacity on one colour, not by adding colours: links 100%,
   labels 75%, legal 60%. The branch name is the largest thing in the
   footer, because it is the thing people scroll down here to find.
   ------------------------------------------------------------------ */
footer{background:var(--plum);color:#E4D9E8;padding:64px 0 48px;font-size:16px}
@media(min-width:760px){footer{padding:80px 0 56px}}
footer a{text-decoration:none;color:inherit}
footer a:hover{color:#fff}

/* The reference build puts the wordmark in the footer rather than a large
   text line, which is both simpler and does more work: the tagline is
   already drawn into the logo. */
.foot-mark{display:inline-block;margin:0 0 clamp(26px,3.4vw,40px)}
.foot-mark img{height:clamp(52px,5vw,66px);width:auto;display:block}

.foot-grid{display:grid;gap:40px;grid-template-columns:1fr}
@media(min-width:620px){.foot-grid{grid-template-columns:repeat(2,1fr);gap:40px}}
@media(min-width:1040px){.foot-grid{grid-template-columns:repeat(3,1fr) .8fr;gap:56px}}

.foot-label{
  font-family:var(--body);font-size:13px;font-weight:400;letter-spacing:.13em;
  text-transform:uppercase;color:#D7CBDC;opacity:.75;margin:0 0 18px
}
.foot-branch h3{
  font-family:var(--display);font-weight:400;font-size:1.22rem;letter-spacing:-.03em;
  text-transform:none;color:#fff;margin:0 0 10px
}
.foot-branch p{margin:0 0 4px;line-height:1.55;opacity:.85}
.foot-branch .tel{display:inline-block;margin-top:6px;font-weight:700;color:#fff;border-bottom:2px solid rgba(255,255,255,.25)}
.foot-branch .tel:hover{border-color:var(--cyan)}

/* live status, computed from the hours we already hold */
.status{display:flex;align-items:center;gap:8px;margin:14px 0 16px;font-size:14px;font-weight:700}
.status::before{content:"";width:8px;height:8px;border-radius:50%;background:currentColor;flex:0 0 auto}
.status.open{color:#49D38A}
.status.shut{color:#D7CBDC;opacity:.6}

.foot-branch .btn{padding:12px 18px 10px;font-size:.88rem;min-height:42px;border-radius:10px}
.foot-links{display:flex;flex-direction:column;gap:12px}
.foot-social{display:flex;gap:14px;margin-bottom:18px}
.foot-social a{
  width:40px;height:40px;border-radius:50%;border:1px solid rgba(255,255,255,.22);
  display:grid;place-items:center;transition:border-color .15s ease,background .15s ease
}
.foot-social a:hover{border-color:var(--cyan);background:rgba(21,194,223,.12)}
.foot-social svg{width:19px;height:19px;stroke:currentColor;fill:none;stroke-width:1.6}

.legal{
  margin-top:48px;padding-top:24px;border-top:1px solid rgba(255,255,255,.15);
  font-size:12px;letter-spacing:.13em;text-transform:uppercase;opacity:.6;
  display:flex;flex-wrap:wrap;gap:10px 22px;justify-content:space-between;align-items:center
}
.legal .dot{opacity:.5;margin:0 4px}

.foot-logo{height:54px;width:auto;opacity:.95;margin-bottom:26px}

/* ---------- mobile order bar ---------- */
.order-bar{
  position:fixed;bottom:0;left:0;right:0;z-index:70;background:var(--ink);
  border-top:2px solid var(--pink);padding:11px 14px calc(11px + env(safe-area-inset-bottom));display:flex;gap:10px
}
.order-bar .btn{flex:1;padding:14px 10px 12px;font-size:.84rem;min-height:48px;border-radius:10px}
.order-bar .btn.call{flex:0 0 104px;--fill:transparent;--ring:rgba(255,255,255,.5);--edge:transparent;--face:#fff}
@media(min-width:1000px){.order-bar{display:none}body{padding-bottom:0}}

/* ---------- reveal: never hides content ---------- */
.rv{transform:translateY(14px);transition:transform .8s cubic-bezier(.26,.54,.32,1)}
.rv.in{transform:none}
@media(prefers-reduced-motion:reduce){.rv{transform:none;transition:none}}

@media print{
  .order-bar,.search{display:none}
  header{position:static}
  *{-webkit-print-color-adjust:exact;print-color-adjust:exact}
  .rv{transform:none!important}
  .mcat.hide,.mitem.hide{display:block!important}
  section{padding:30px 0;break-inside:avoid}
  .hero .wrap{padding:40px 0}
  h1{font-size:3.4rem} h2{font-size:2rem}
  .loc-grid{grid-template-columns:repeat(3,1fr)}
  .sig-grid{grid-template-columns:repeat(4,1fr)}
  .faq details .ans{display:block!important}
  .mcat{display:block!important}
  .mcat .mgrid{display:grid!important}
}

/* ---------- shake board (hub page) ----------
   Set as a listings board rather than a card grid: two columns of rules,
   names in display caps, ingredients small underneath. The hub's other
   sections are all pale bands holding a card grid, so this one is dark and
   typographic on purpose, to break a rhythm that had run three sections
   deep. See the copy notes in CLAUDE.md for why that mattered.
   ------------------------------------------------------------------ */
.board{
  display:grid;grid-template-columns:1fr;
  border-top:1px solid var(--line)
}
@media(min-width:800px){.board{grid-template-columns:1fr 1fr;column-gap:clamp(36px,5vw,72px)}}
/* Name left, price right on the same baseline, description under both,
   which is the shape the reference build uses for all fourteen of its
   categories. */
.brow{
  padding:13px 0 14px;border-bottom:1px solid var(--line);
  display:grid;grid-template-columns:1fr auto;align-items:baseline;
  column-gap:18px
}
.brow h3{
  font-size:clamp(1rem,1.5vw,1.16rem);letter-spacing:.005em;
  color:var(--ink);margin:0
}
.bprice{
  font-family:var(--display);font-weight:400;font-size:1.25rem;
  /* --pink, at full brand saturation, like the reference. Recomputed:
     the brand pink #D71481 on this cream #FEF9F7 measures 4.67:1, which
     passes WCAG AA for body-size text. The earlier note here was about
     the pink we used to run, #D82A71 at 4.41:1, which did not pass. */
  color:var(--pink);white-space:nowrap;letter-spacing:0
}
.brow p{grid-column:1/-1}
.brow p{
  margin:0;font-family:var(--body);font-weight:400;font-size:.83rem;line-height:1.45;
  color:var(--grey)
}
.board-more{
  margin:clamp(30px,4vw,44px) 0 0;max-width:62ch;
  font-size:.96rem;color:var(--grey)
}
.board-more a{
  color:var(--pink-dark);font-weight:700;text-decoration:none;
  border-bottom:2px solid var(--pink-dark);white-space:nowrap
}
.board-more a:hover{color:var(--ink);border-color:var(--ink)}

/* ================= motion and live state =================
   Everything here either carries the brand idea or answers a question a
   customer actually has. Nothing is here to fill space.
   ========================================================= */

/* The headline's shake name cycles. The site's whole positioning is that the
   shakes are named after films, so the headline demonstrates it rather than
   citing one example. Outgoing name lifts and fades, incoming settles in. */
/* The hero shake name. No transition: it no longer changes after load. */
.shakename{display:inline-block}

/* Live open/closed. A shop trading to 1am gets asked this more than anything
   else, so it is answered on sight rather than in an hours table. */
/* Centred, because the client asked for it and because this is the one line
   that answers the most-asked question for a shop trading to 1am. Otter's
   analysis of 5m+ UK delivery orders puts midnight-6am at only 4-5% of
   volume but the highest average order value of any slot, so the people
   reading this line are the best orders on the site. */
.livestat{
  display:flex;align-items:center;justify-content:center;gap:11px;
  margin:clamp(26px,3vw,38px) auto 0;width:fit-content;max-width:100%;
  padding:11px 20px;border-radius:999px;
  background:rgba(22,16,26,.22);
  font-family:var(--body);font-size:1rem;font-weight:800;
  letter-spacing:.01em;color:#fff;text-align:center
}
.livestat::before{
  content:"";width:9px;height:9px;border-radius:50%;background:#49D38A;flex:none;
  box-shadow:0 0 0 0 rgba(73,211,138,.65);animation:pulse 2.6s var(--ease) infinite
}
.livestat.off::before{background:rgba(255,255,255,.4);animation:none}
@keyframes pulse{
  70%{box-shadow:0 0 0 10px rgba(73,211,138,0)}
  100%{box-shadow:0 0 0 0 rgba(73,211,138,0)}
}

/* Pick one for me. 108 items is a decision problem; this is the control that
   solves it, and it is the thing staff get asked to do every night anyway. */
.pickbar{
  display:flex;flex-wrap:wrap;align-items:center;gap:16px 24px;
  margin:0 0 clamp(26px,3.5vw,38px)
}
.pickbar .btn{--fill:transparent;--ring:rgba(22,16,26,.4);--edge:transparent;--face:var(--ink)}
.pickbar .btn:hover{--fill:var(--cyan);--ring:var(--cyan);--edge:var(--cyan-dark);--face:var(--ink)}
.pickout{min-height:2.8em}
.pickout strong{
  display:block;font-family:var(--display);font-weight:400;text-transform:uppercase;
  font-size:clamp(1.05rem,1.6vw,1.3rem);letter-spacing:0;color:var(--pink)
}
.pickout span{
  display:block;font-family:var(--body);font-size:.84rem;line-height:1.45;
  color:var(--grey);margin-top:4px;max-width:46ch
}
.pickout.landed strong{animation:land .42s var(--ease)}
@keyframes land{from{transform:scale(.95);opacity:.45}to{transform:none;opacity:1}}

/* A board row is a thing you can point at, so it behaves like one. */
.brow{transition:color var(--dur) var(--ease)}
.brow:hover h3{color:var(--pink-dark)}

/* Scroll-driven reveal, no JS and no observer. None of the ten food sites
   measured uses animation-timeline at all. Browsers without it simply show
   the rows, which is the correct fallback. */
@media (prefers-reduced-motion:no-preference){
  @supports (animation-timeline:view()){
    .brow{animation:browin linear both;animation-timeline:view();animation-range:entry 0% entry 50%}
    @keyframes browin{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}
  }
}

/* Cross-document transitions. One rule, and moving between the hub and a
   branch stops feeling like a page load. */
@view-transition{navigation:auto}
.mark img{view-transition-name:mark}

@media (prefers-reduced-motion:reduce){
  .livestat::before{animation:none}
  @view-transition{navigation:none}
}

/* ---------- angled bands ----------
   Two skewed strips where the hero meets the page. A flat horizontal marquee
   was removed earlier for being generic; set on an angle, overlapping, in the
   two brand colours, it stops being filler and becomes the thing that says
   this is a dessert shop and not a law firm.
   ---------------------------------- */
/* The two skewed marquee bands are gone, for the second and final time.
   They carried "Shakes / Waffles / Cookie dough / Sundaes / Cheesecakes /
   Falooda" and "Open till late / Halal / Three branches / Order direct":
   category nouns and generic claims, which is exactly the padding the Style
   rules in CLAUDE.md say to avoid, scrolling on a timer. The client called it
   AI slop twice. It is not coming back.

   What sits in that slot now is .reelsec, the film-named shakes, which was
   previously buried at the eighth section on the page, below the whole menu.
   It is real content drawn from MENU, it cannot name an item the shop does
   not sell, no competitor can run it, and the visitor drives it. */

/* The third brand colour was sitting unused. The category numbers are where
   it earns its place: it marks them without competing with the pink prices. */
/* The number circles cycle the brand palette instead of all being yellow,
   which is what the reference build does across its fourteen categories.
   Costs nothing and it is the cheapest bit of fun on the page. */
.num{background:var(--yellow);color:var(--ink)}
.mcat:nth-child(3n+1) .num{background:var(--pink);color:#fff}
.mcat:nth-child(3n+2) .num{background:var(--cyan);color:var(--ink)}
.mcat:nth-child(3n)   .num{background:var(--yellow);color:var(--ink)}

/* ---------- sprinkles ----------
   Not a generic confetti pattern: the Carlisle Road shopfront is a purple
   facade with coloured sprinkles painted across it, so this is the shop's
   own signage device rather than decoration borrowed from nowhere. Drawn as
   rotated capsules in the brand palette, sized in em so it scales with the
   band, and aria-hidden because it says nothing. */
.sprinkles{
  height:clamp(54px,6vw,84px);
  background-color:var(--plum);
  background-image:url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22260%22%20height%3D%2286%22%20viewBox%3D%220%200%20260%2086%22%3E%3Crect%20x%3D%22109.0%22%20y%3D%2243.8%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23D71481%22%20transform%3D%22rotate%2863.6%20119.5%2047.5%29%22%2F%3E%3Crect%20x%3D%2248.1%22%20y%3D%2259.4%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23D71481%22%20transform%3D%22rotate%28-3.6%2058.6%2063.1%29%22%2F%3E%3Crect%20x%3D%2225.8%22%20y%3D%2227.4%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23F0F0F0%22%20transform%3D%22rotate%28-61.4%2036.3%2031.1%29%22%2F%3E%3Crect%20x%3D%22142.1%22%20y%3D%2233.4%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23D71481%22%20transform%3D%22rotate%28-7.1%20152.6%2037.1%29%22%2F%3E%3Crect%20x%3D%22148.6%22%20y%3D%2261.2%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23F0F0F0%22%20transform%3D%22rotate%28-65.5%20159.1%2064.9%29%22%2F%3E%3Crect%20x%3D%22235.5%22%20y%3D%2271.7%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23FFFFFF%22%20transform%3D%22rotate%2851.0%20246.0%2075.4%29%22%2F%3E%3Crect%20x%3D%2277.1%22%20y%3D%2222.7%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%2300B7E0%22%20transform%3D%22rotate%28-31.6%2087.6%2026.4%29%22%2F%3E%3Crect%20x%3D%22219.1%22%20y%3D%2211.3%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23FFD343%22%20transform%3D%22rotate%28-18.7%20229.6%2015.0%29%22%2F%3E%3Crect%20x%3D%22168.0%22%20y%3D%228.7%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23D71481%22%20transform%3D%22rotate%28-5.3%20178.5%2012.4%29%22%2F%3E%3Crect%20x%3D%22190.4%22%20y%3D%2227.5%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23FFD343%22%20transform%3D%22rotate%2857.7%20200.9%2031.2%29%22%2F%3E%3Crect%20x%3D%22106.4%22%20y%3D%228.6%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23FFFFFF%22%20transform%3D%22rotate%2816.5%20116.9%2012.3%29%22%2F%3E%3Crect%20x%3D%2280.8%22%20y%3D%2254.9%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23D71481%22%20transform%3D%22rotate%283.3%2091.3%2058.6%29%22%2F%3E%3Crect%20x%3D%2214.3%22%20y%3D%2267.9%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23D71481%22%20transform%3D%22rotate%2835.6%2024.8%2071.6%29%22%2F%3E%3Crect%20x%3D%22188.0%22%20y%3D%2266.6%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23D71481%22%20transform%3D%22rotate%28-22.2%20198.5%2070.3%29%22%2F%3E%3Crect%20x%3D%2251.0%22%20y%3D%2218.9%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23D71481%22%20transform%3D%22rotate%2860.9%2061.5%2022.6%29%22%2F%3E%3Crect%20x%3D%22223.7%22%20y%3D%2240.0%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23D71481%22%20transform%3D%22rotate%2874.5%20234.2%2043.7%29%22%2F%3E%3Crect%20x%3D%22168.6%22%20y%3D%2244.4%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23FFFFFF%22%20transform%3D%22rotate%2862.8%20179.1%2048.1%29%22%2F%3E%3Crect%20x%3D%22119.6%22%20y%3D%2267.8%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23D71481%22%20transform%3D%22rotate%2871.6%20130.1%2071.5%29%22%2F%3E%3Crect%20x%3D%224.8%22%20y%3D%2243.0%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%2300B7E0%22%20transform%3D%22rotate%2852.3%2015.3%2046.7%29%22%2F%3E%3Crect%20x%3D%224.2%22%20y%3D%228.1%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%23FFFFFF%22%20transform%3D%22rotate%28-25.8%2014.7%2011.8%29%22%2F%3E%3Crect%20x%3D%22132.7%22%20y%3D%228.6%22%20width%3D%2221%22%20height%3D%227.4%22%20rx%3D%223.7%22%20fill%3D%22%2300B7E0%22%20transform%3D%22rotate%28-53.1%20143.2%2012.3%29%22%2F%3E%3C%2Fsvg%3E");
  background-repeat:repeat-x;
  background-position:center;
  background-size:auto 100%
}
.mcat[open]>summary{border-bottom:3px solid var(--ink)}
.mcat.hide{display:none}
#menu-cats.empty::after{
  content:"Nothing matches that. Try oreo, waffle or mango.";
  display:block;padding:34px 0;color:var(--grey);font-size:.95rem
}


/* ---------- card stagger ----------
   Tony's Chocolonely drives a whole cascade off one --animation-order per
   item with steps(1,end), so items pop in rather than fade in. Costs five
   lines and one inline custom property per card.
   ---------------------------------- */
@media (prefers-reduced-motion:no-preference){
  .loc.rv, .sig.rv{animation:popin .42s both cubic-bezier(.36,1,.46,1)}
  .loc.rv{animation-delay:calc(var(--animation-order,0) * .09s)}
  .sig.rv{animation-delay:calc(var(--animation-order,0) * .07s)}
  @keyframes popin{from{opacity:0;transform:translateY(18px) scale(.97)}to{opacity:1;transform:none}}
}

/* ================= the film reel =================
   A horizontal snap-scrolling strip. This is the one thing on the site that
   moves sideways, and sideways is the axis the page was missing.

   The whole progress rail is CSS. The track declares `scroll-timeline` on its
   inline axis, `.reel` hoists that name with `timeline-scope`, and the bar
   underneath consumes it. A sibling animating off another element's scroll
   position is the thing people load a scroll library to do. Nothing here
   needs JS, and with JS off the strip is still a scrollable row.

   Tiles are type, not photographs, on purpose: the photos are phone-quality
   owner uploads and are the weakest asset here, while the shake names are the
   strongest. ================================================== */
.reel{position:relative;timeline-scope:--reel;margin:0 0 clamp(30px,4vw,44px)}
.reel-track{
  display:flex;gap:14px;overflow-x:auto;overscroll-behavior-x:contain;
  scroll-snap-type:x proximity;scrollbar-width:none;-webkit-overflow-scrolling:touch;
  padding:2px 0 20px;scroll-timeline:--reel inline;
  /* the leading and trailing cards dissolve rather than clip */
  mask-image:linear-gradient(90deg,#0000 0,#000 2.5%,#000 97.5%,#0000 100%)
}
.reel-track::-webkit-scrollbar{display:none}
.reel-track:focus-visible{outline:3px solid var(--cyan);outline-offset:4px;border-radius:var(--r)}
.reel-card{
  flex:0 0 clamp(228px,25vw,310px);scroll-snap-align:start;border-radius:var(--r);
  padding:20px 19px 18px;min-height:208px;display:flex;flex-direction:column;gap:10px;
  background:var(--ink-2);color:#fff
}
/* tiers by fill, cycling the two brand colours against the dark default */
.reel-card:nth-child(3n+1){background:var(--pink)}
.reel-card:nth-child(3n+2){background:var(--cyan);color:var(--ink)}
.reel-card h3{font-size:clamp(1.3rem,2vw,1.7rem);line-height:1;letter-spacing:.004em;color:inherit}
.reel-card p{margin:0;font-family:var(--body);font-size:.85rem;line-height:1.45;opacity:.82;flex:1}
.reel-p{font-family:var(--display);font-weight:400;font-size:1.25rem;letter-spacing:.004em}

.reel-bar{height:3px;background:rgba(255,255,255,.16);border-radius:3px;overflow:hidden}
.reel-bar i{display:block;height:100%;background:var(--yellow);transform-origin:0 50%;transform:scaleX(.18)}
@supports (animation-timeline:scroll()){
  @keyframes reel-progress{from{transform:scaleX(.14)}to{transform:scaleX(1)}}
  .reel-bar i{animation:reel-progress linear both;animation-timeline:--reel}
}
/* The reel cards used to scale from .95 and fade from .3 opacity as they
   entered the scroller, on a view(inline) timeline. It made every card that
   was not fully scrolled in render smaller and a few pixels lower than its
   neighbours, so a row of equal cards looked misaligned. Measured: cards 0-3
   at top 1055 height 268, card 4 at top 1060 height 259.

   The client read that as broken boxes, which is the correct reading. A row
   of product cards has to be a clean row. The progress rail under the track
   already says the row scrolls; the cards do not need to perform it. */

/* The logo used to wobble while the page scrolled. Removed with the rest of
   the logo treatment: it was motion on the one element present at every
   scroll position, and the client read the whole thing as odd. html.scrolling
   is still set by initScrollState() and is free to carry something later. */

/* ================= header, second pass =================
   The motion signature here is stepped, not smooth. Tony's Chocolonely puts
   `steps(3,end)` on its header transitions and nothing on that site glides;
   it reads as hand-made, and smooth easing is what every framework ships.
   Scoped to the header on purpose, so it reads as the header's own character
   while the page's reveals stay on the .8s easeOutQuad documented above.
   ======================================================= */
:root{--step:steps(3,end)}

/* The announcement strip is gone. It rotated three messages on a 5s timer,
   which is the pattern NN/g's auto-forwarding study shows people do not read:
   a participant hunting a discount that rotated every 5s concluded the site
   had none, because it was on screen only ~20% of the time. It was also a
   thin full-width bar at the top of the page, which is the exact shape
   banner blindness attaches to. Its three messages now live where they are
   read: open/closed in .livestat, the price point under the hero buttons,
   and the Google ratings in .facts directly below the hero.
   https://www.nngroup.com/articles/auto-forwarding/
   https://www.nngroup.com/articles/banner-blindness-old-and-new-findings/ */

/* ---------- nav: a plate, not an underline ----------
   The live OPEN/CLOSED pills that sat on each branch link are gone. Three
   links each reading "Open" is the same fact three times, and the live status
   line under the hero already carries it in one place, which is where the
   client wanted it. Removing them also drops a 60s repaint that rewrote nav
   markup for the life of the page. */

/* ---------- the hamburger ----------
   Keyed entirely off aria-expanded, the attribute the button needs for
   screen readers anyway, so the only JS is setAttribute. Chin Chin's values. */
.burger{
  display:grid;place-items:center;width:46px;height:46px;flex:0 0 auto;
  background:none;border:0;cursor:pointer;padding:0;margin-left:auto
}
.burger-box{position:relative;width:30px;height:22px;display:block}
.burger-in,.burger-in::before,.burger-in::after{
  position:absolute;width:30px;height:3px;border-radius:2px;background:var(--ink);
  transition:transform .12s ease,top .12s ease,bottom .12s ease,opacity .12s ease
}
.burger-in{top:50%;margin-top:-1.5px}
.burger-in::before,.burger-in::after{content:"";left:0}
.burger-in::before{top:-9px}
.burger-in::after{bottom:-9px}
.burger[aria-expanded="true"] .burger-in{transform:rotate(45deg);transition-delay:.12s}
.burger[aria-expanded="true"] .burger-in::before{top:0;opacity:0;transition-delay:0s}
.burger[aria-expanded="true"] .burger-in::after{bottom:0;transform:rotate(-90deg);transition-delay:.12s}
@media(min-width:1000px){.burger{display:none}}

/* ---------- mobile: the nav becomes a full-screen panel ----------
   Below 1000px there was no navigation at all, so the three branch pages were
   unreachable on a phone. Same <nav>, different presentation. */
@media(max-width:999px){
  nav{
    display:flex;position:fixed;inset:0;z-index:70;background:var(--cream);
    flex-direction:column;justify-content:center;align-items:flex-start;
    gap:2px;padding:134px 26px 30px;counter-reset:nv;
    transform:translateX(-100%);visibility:hidden;
    transition:transform .34s var(--step),visibility 0s linear .34s
  }
  html.navopen nav{transform:none;visibility:visible;transition-delay:0s,0s}
  /* No backdrop-filter rule needed here any more: the header no longer has
     one. If one is ever re-added, it makes the header a containing block for
     position:fixed children and this panel will size itself to the 78px bar
     instead of the viewport. That trap is in CLAUDE.md. */
  /* the bar rides above the panel so the logo stays put and the X is
     reachable; both are cream, so it reads as one surface */
  html.navopen header{z-index:80;background:transparent;border-bottom-color:transparent}
  /* the panel carries its own order button, so the sticky bar would only
     clip it */
  html.navopen .order-bar{display:none}
  /* nav sits at z-index 70 inside the header's own stacking context, so the
     logo and the close button have to clear it to stay reachable */
  html.navopen .mark,html.navopen .burger{position:relative;z-index:90}
  html.navopen .mark img{transform:scale(1)}
  html.navopen,html.navopen body{overflow:hidden}
  nav a{
    font-family:var(--display);font-size:clamp(1.5rem,7vw,2.1rem);font-weight:400;
    text-transform:uppercase;letter-spacing:.004em;line-height:1.1;
    padding:11px 0;display:flex;align-items:center;gap:13px;counter-increment:nv;width:100%
  }
  nav a::before{display:none}
  nav a::after{
    display:block;content:counter(nv,decimal-leading-zero);position:static;
    order:-1;width:auto;height:auto;background:none;transform:none;border-radius:0;
    font-family:var(--body);font-size:.68rem;font-weight:800;letter-spacing:.1em;
    color:var(--cyan);clip-path:none
  }
  nav a[aria-current="page"]{color:var(--pink)}
  nav a[aria-current="page"]::after{color:var(--pink)}
  .nav-cta{margin-top:auto;align-self:stretch;justify-content:center}
  .nav-cta::after{display:none}
}
@media(min-width:1000px){.nav-cta{display:none}}


/* ---------- the film reel, promoted ----------
   Full bleed: the track runs to both edges of the viewport while the first
   card still lines up with the page gutter, so the row reads as continuing
   past the screen rather than sitting in a box. The heading and the progress
   rail stay inside .wrap so they align with everything else. */
.reelsec{background:var(--ink);color:#fff;padding:clamp(44px,6vw,76px) 0 clamp(34px,4.5vw,56px);overflow:hidden}
.reelsec .sec-head{margin-bottom:clamp(22px,3vw,34px)}
.reelsec .lede{color:rgba(255,255,255,.66)}
.reelsec .reel{margin:0}
.reelsec .reel-track{
  padding-left:max(20px,calc((100vw - var(--wrap)) / 2));
  padding-right:max(20px,calc((100vw - var(--wrap)) / 2));
  mask-image:none
}
.reelsec .reel-card{
  flex-basis:clamp(236px,26vw,324px);
  min-height:clamp(214px,24vw,268px)
}
.reelsec .reel-bar{margin-top:18px}

/* Only Carlisle Road has a working join link. The other two branches show
   this instead of a dead button, which is also what the expired join page
   itself tells people to do. Replace with a real link per branch when
   Tap and Rate issues one. */
.loyal-note{margin:20px 0 0;font-family:var(--body);font-weight:700;font-size:.95rem;color:var(--ink)}
