/* MENU.TZ v2.2.1 · 2026-09-30 */
@font-face{
  font-family:"Geist";
  src:url("fonts/Geist-latin.woff2") format("woff2");
  font-weight:100 900;
  font-style:normal;
  font-display:swap;
}

/* ===================================================================
   Tokens

   Colour roles arrive from PHP as a generated tonal palette. Everything
   below is shape, spacing and type — three scales, so no value is ever
   picked ad hoc.

   The spacing rule, which the v1 stylesheet kept breaking: a container
   owns its own symmetric padding, and the gap between siblings comes
   from one token. Never a margin on both sides of a gap.
   ================================================================= */
:root{
  --shape-xs:4px;
  --shape-s:8px;
  --shape-m:12px;
  --shape-l:16px;
  --shape-xl:28px;
  --shape-full:100px;

  --sp-1:4px;
  --sp-2:8px;
  --sp-3:12px;
  --sp-4:16px;
  --sp-5:20px;
  --sp-6:24px;

  --gutter:var(--sp-4);
  --stack:var(--sp-4);   /* hero -> switcher -> search -> chips -> content */
  --wrap:1100px;
  --bar-h:60px;

  /* Type (2.1.3). One scale for the whole site, cart included.
     In rem, so a customer who has set a larger default text size in the
     browser gets it; at the normal setting 1rem = 16px and nothing moves.
     The two headline sizes grow smoothly from phone to desktop with a
     floor and a ceiling; the reading sizes stay put and step once at
     720px, so nothing on a small phone ever drops below them.
       title  26px → 34px     cat   20px → 22px     (fluid, clamped)
       item   15px / 16px     body  13px / 14px     meta  11px / 12px   */
  --fs-title:clamp(1.625rem, 1.2rem + 1.8vw, 2.125rem);
  --fs-cat:clamp(1.25rem, 1.15rem + .45vw, 1.375rem);
  --fs-item:.9375rem;
  --fs-body:.8125rem;
  --fs-meta:.6875rem;
}

@media (min-width:720px){
  :root{ --gutter:var(--sp-6); --bar-h:64px; --fs-item:1rem; --fs-body:.875rem; --fs-meta:.75rem; }
}

*,*::before,*::after{ box-sizing:border-box; }
html{ -webkit-text-size-adjust:100%; scroll-behavior:smooth; }

body{
  margin:0;
  background:var(--surface);
  color:var(--on-surface);
  font-family:"Geist",system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  font-size:1rem;
  line-height:1.45;
  -webkit-font-smoothing:antialiased;
}

img{ max-width:100%; display:block; }
button{ font:inherit; color:inherit; }
svg{ width:1em; height:1em; display:block; }

:where(a,button,input,[tabindex]):focus-visible{
  outline:3px solid var(--primary);
  outline-offset:2px;
  border-radius:var(--shape-xs);
}

.skip-link{
  position:absolute; left:-9999px; top:calc(var(--sponsor-h) + 8px); z-index:100;
  background:var(--surface); color:var(--on-surface);
  padding:10px 16px; border-radius:var(--shape-s); text-decoration:none;
}
.skip-link:focus{ left:12px; }

/* The hero clips its own photo with overflow:hidden, which would also
   clip the switcher's open menu — hence a wrapper to position against. */
.masthead{ position:relative; }

/* ===================================================================
   Header — photo carrying logo, name and tagline
   ================================================================= */
.hero{
  position:relative;
  min-height:196px;
  display:flex;
  align-items:flex-end;
  border-radius:0 0 var(--shape-xl) var(--shape-xl);
  overflow:hidden;
  background:var(--primary-container);
}
/* A real element rather than a CSS background, so it can carry a srcset
   and the browser can prioritise it as the largest paint. */
.hero__img{
  position:absolute; inset:0;
  width:100%; height:100%;
  object-fit:cover;
}
.hero::after{
  content:"";
  position:absolute; inset:0;
  background:linear-gradient(to top,
    rgba(0,0,0,calc(var(--hero-overlay) * 1.45)) 0%,
    rgba(0,0,0,calc(var(--hero-overlay) * 0.6)) 45%,
    rgba(0,0,0,calc(var(--hero-overlay) * 0.1)) 100%);
}
.hero--flat{
  background-image:linear-gradient(150deg, var(--primary) 0%, var(--on-primary-container) 100%);
}
.hero__inner{
  position:relative;
  z-index:1;
  width:100%;
  max-width:var(--wrap);
  margin:0 auto;
  padding:var(--sp-6) var(--gutter);
  display:flex;
  align-items:flex-end;
  gap:var(--sp-3);
}
.hero__mark{
  flex:0 0 auto;
  width:70px; height:70px;
  border-radius:var(--shape-full);
  object-fit:cover;
  background:var(--surface);
}
.hero__text{ min-width:0; }
.hero__inner{ padding-top:calc(var(--sp-6) + var(--sp-5)); }
.hero__title{
  margin:0;
  font-size:var(--fs-title);
  font-weight:700;
  letter-spacing:-.02em;
  line-height:1.12;
  color:#fff;
  text-wrap:balance;
}
.hero__tagline{
  margin:var(--sp-1) 0 0;
  max-width:44ch;
  font-size:var(--fs-body);
  color:rgba(255,255,255,.84);
}
@media (min-width:720px){
  .hero{ min-height:280px; }
  .hero__mark{ width:85px; height:85px; }
  .hero__inner{ gap:var(--sp-4); }
}

/* ===================================================================
   Sticky bar

   At rest it carries the switcher, the search field and the chips. Once
   the header has scrolled past it condenses to a single row: logo, a
   compact switcher, and a search icon.
   ================================================================= */
.bar{
  position:sticky;
  top:0;
  z-index:40;
  background:var(--surface);
  padding:var(--stack) 0;
}

/* Scrolled state. Material signals elevation with tone rather than
   brand colour, so this is surface-container against the page's
   surface — enough to separate the bar without becoming a colour. */
.bar__compact{
  display:none;
  align-items:center;
  gap:var(--sp-3);
  height:var(--bar-h);
  /* Colour runs edge to edge; the contents still line up with the
     content column on a wide screen. */
  padding:0 max(var(--gutter), calc((100% - var(--wrap)) / 2 + var(--gutter)));
  background:var(--surface-container);
  color:var(--on-surface);
}
.bar.is-stuck .bar__compact{ display:flex; }
.bar.is-stuck{ padding-top:0; }
.bar.is-stuck .bar__chips{ padding-top:var(--stack); }
.bar.is-stuck .bar__row{ display:none; }
.bar.is-stuck.is-searching-open .bar__compact{ display:none; }
.bar.is-stuck.is-searching-open .bar__row{ display:block; }

.bar__mark{
  flex:0 0 auto;
  width:44px; height:44px;
  border-radius:var(--shape-full);
  object-fit:cover;
}
.bar__name{
  flex:1;
  min-width:0;
  font-size:var(--fs-cat);
  font-weight:700;
  letter-spacing:-.01em;
  color:inherit;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}

.bar__search-btn{
  flex:0 0 auto;
  display:flex; align-items:center; justify-content:center;
  width:40px; height:40px;
  border:0; border-radius:var(--shape-full);
  background:none; color:var(--on-surface-variant);
  font-size:1.375rem; cursor:pointer;
}


/* ===================================================================
   Menu switcher

   A dropdown in both states rather than a segmented control. The
   failure mode being fixed was people not realising it was a control at
   all, so it is filled with primary rather than set as plain text — the
   same treatment the category chips use, which is what taught the eye
   that filled pills are the tappable things.

   An earlier version put a faint "Menu" eyebrow and a divider rule
   inside the button. Both were dropped once the filled pill proved
   sufficient on its own; their styles have been removed with them.
   ================================================================= */
.drop{ position:relative; display:inline-block; }

.drop__btn{
  display:inline-flex; align-items:center; gap:var(--sp-2);
  border:0;
  border-radius:var(--shape-full);
  padding:11px var(--sp-3) 11px var(--sp-5);
  background:var(--primary);
  color:var(--on-primary);
  font-size:var(--fs-item);
  cursor:pointer;
}
.drop__label{ font-weight:500; }
.drop__chev{ font-size:1.1875rem; transition:transform .18s ease; }
.drop.is-open .drop__chev{ transform:rotate(180deg); }

.drop__menu{
  position:absolute; top:calc(100% + 6px); left:0; z-index:5;
  min-width:180px;
  background:var(--surface-container-high);
  border-radius:var(--shape-m);
  box-shadow:0 4px 18px rgba(0,0,0,.18);
  padding:var(--sp-1);
  display:none;
}
.drop--compact .drop__menu{ left:auto; right:0; }
.drop.is-open .drop__menu{ display:block; }
.drop__item{
  display:flex; align-items:center; gap:var(--sp-2);
  width:100%; text-align:left;
  border:0; border-radius:var(--shape-s);
  padding:11px var(--sp-4);
  background:none; color:var(--on-surface);
  font-size:var(--fs-item); cursor:pointer;
}
.drop__item.is-active{ background:var(--secondary-container); color:var(--on-secondary-container); }
.drop__check{ font-size:1.125rem; flex:0 0 auto; visibility:hidden; }
.drop__item.is-active .drop__check{ visibility:visible; }
.drop__item:hover{ background:var(--surface-container); }

/* Pinned top right at rest as well as when scrolled, so it never jumps
   across the screen — and the row it used to occupy is reclaimed. */
.drop--hero{
  position:absolute;
  top:var(--sp-3);
  right:max(var(--gutter), calc((100% - var(--wrap)) / 2 + var(--gutter)));
  z-index:2;
}
.drop--hero .drop__btn{
  padding:8px var(--sp-2) 8px var(--sp-4);
  font-size:var(--fs-body);
  font-weight:600;
  box-shadow:0 2px 10px rgba(0,0,0,.3);
}
.drop--hero .drop__chev{ font-size:1.0625rem; }
.drop--hero .drop__menu{ left:auto; right:0; }

.drop--compact .drop__btn{ padding:8px var(--sp-2) 8px var(--sp-4); font-size:.875rem; }
.drop--compact .drop__chev{ font-size:1.0625rem; }

/* ===================================================================
   Search
   ================================================================= */
.bar__row{
  max-width:var(--wrap);
  margin:0 auto;
  padding:0 var(--gutter);
}
.searchfield{
  display:flex; align-items:center; gap:var(--sp-3);
  width:100%;
  height:48px;
  background:var(--surface-container-high);
  border-radius:var(--shape-full);
  padding:0 var(--sp-5);
}
.bar.is-stuck .bar__row{ padding:0; }
.bar.is-stuck .bar__row{
  max-width:none;
  padding:0 max(var(--gutter), calc((100% - var(--wrap)) / 2 + var(--gutter)));
  background:var(--surface-container);
}
.bar.is-stuck .searchfield{ margin:0; height:44px; background:var(--surface); }
.searchfield__icon{ font-size:1.25rem; color:var(--on-surface-variant); flex:0 0 auto; }
.searchfield__input{
  flex:1; min-width:0; height:100%;
  border:0; background:none; outline:none;
  font:inherit; font-size:1rem;
  color:var(--on-surface);
}
.searchfield__input::placeholder{ color:var(--on-surface-variant); }
.searchfield__input::-webkit-search-cancel-button{ display:none; }
.searchfield__clear{
  flex:0 0 auto; display:flex; padding:4px;
  border:0; background:none; color:var(--on-surface-variant);
  font-size:1.125rem; cursor:pointer;
}
.searchfield__clear[hidden]{ display:none; }



/* ===================================================================
   Category chips
   ================================================================= */
.chips{
  overflow-x:auto;
  scrollbar-width:none;
  overscroll-behavior-x:contain;
}
.chips::-webkit-scrollbar{ display:none; }
.bar__chips{
  max-width:var(--wrap);
  margin:0 auto;
  padding:var(--stack) var(--gutter) 0;
}
/* The inset lives on the wrapper, not here. Overflow is clipped at an
   element's padding box, so horizontal padding on the scroller itself
   doesn't hold the chips back — they scroll straight through it. */
.chips__track{ display:flex; gap:var(--sp-2); width:max-content; }
.bar.is-searching .bar__chips,
.bar.is-searching-open .bar__chips{ display:none; }

/* Inactive chips (2.2.1): a near-white fill with a 1px `outline` edge.
   They used to be a surface-container-high fill only, which sat 1.05:1
   against the pinned bar (surface-container) and 1.16:1 at rest — pale on
   pale, nearly invisible on some seeds. A darker fill barely helps (three
   tones darker still only reaches 1.2:1); an edge does: `outline` against
   the pinned row is 3.15:1 on every seed tried, clearing the 3:1 standard
   for a control's boundary. The fill stays so they still read as solid,
   tappable pills (§11). Padding is 1px less to allow for the border, so
   the chip is the same size as before; the active chip gets a border in
   its own colour so selecting one moves nothing. */
.chip{
  flex:0 0 auto;
  display:flex; align-items:center; gap:5px;
  appearance:none;
  border:1px solid var(--outline);
  border-radius:var(--shape-full);
  padding:7px 15px;
  background:var(--surface);
  color:var(--on-surface-variant);
  font-size:var(--fs-body); white-space:nowrap; cursor:pointer;
  transition:background .15s ease, color .15s ease;
}
.chip__check{ display:none; font-size:1rem; }
.chip.is-active{ background:var(--primary); border-color:var(--primary); color:var(--on-primary); padding-left:10px; }
.chip.is-active .chip__check{ display:block; }

/* ===================================================================
   Menu — each category is a card
   ================================================================= */
main{ min-height:50vh; }
.panel{
  display:none;
  padding:0 0 var(--sp-6);
  /* Swapping panels changes the document height, and browsers try to
     keep the previous content visually still — which can undo a
     scrollTo issued in the same frame. */
  overflow-anchor:none;
}
.panel.is-active{ display:block; }

.cat{
  max-width:var(--wrap);
  margin:0 auto var(--sp-5);
  padding:0 var(--gutter);
  scroll-margin-top:calc(var(--bar-h) + var(--sponsor-h) + 60px);
}
.cat.is-hidden{ display:none; }
@media (min-width:720px){
  .cat{ margin-bottom:var(--sp-5); }
}

.cat__head{
  margin:0;
  background:var(--surface-container-low);
  border-radius:var(--shape-xl) var(--shape-xl) 0 0;
}
.cat:not(.is-open) .cat__head{ border-radius:var(--shape-xl); }
.cat__toggle{
  width:100%;
  display:flex; align-items:center; justify-content:space-between; gap:var(--sp-3);
  padding:var(--sp-5) var(--sp-5) var(--sp-4);
  background:none; border:0; text-align:left; cursor:pointer;
}
/* Hairline between the title and its items. Sits on the toggle so it
   disappears with the title when the category is collapsed. */
.cat.is-open .cat__toggle{ border-bottom:1px solid var(--outline-variant); }
.cat:not(.is-open) .cat__toggle{ padding:var(--sp-5); }
.cat__name{
  font-size:var(--fs-cat);
  font-weight:700;
  letter-spacing:-.01em;
  color:var(--on-surface);
}
.cat__chev{ flex:0 0 auto; font-size:1.25rem; color:var(--on-surface-variant); transition:transform .2s ease; }
.cat:not(.is-open) .cat__chev{ transform:rotate(-90deg); }

.items{
  list-style:none;
  margin:0;
  padding:0 var(--sp-5);
  background:var(--surface-container-low);
  border-radius:0 0 var(--shape-xl) var(--shape-xl);
}
.cat:not(.is-open) .items{ display:none; }

.item{
  position:relative;
  display:flex; align-items:flex-start; gap:var(--sp-4);
  padding:var(--sp-4) 0;
}
/* Deliberately a step lighter than the rule under the category heading,
   so the section break stays the stronger of the two. */
.item + .item{ border-top:1px solid var(--outline-faint); }
.item.is-hidden{ display:none; }

.item__open{
  position:absolute; inset:0; z-index:1;
  border:0; background:none; padding:0; cursor:pointer;
}
.item__main{ flex:1 1 auto; min-width:0; }
.item__head{ display:flex; align-items:center; gap:var(--sp-2); flex-wrap:wrap; }
.item__name{ font-size:var(--fs-item); font-weight:500; color:var(--on-surface); }
.item__desc{
  position:relative;
  margin:var(--sp-1) 0 0;
  font-size:var(--fs-body);
  line-height:1.42;
  color:var(--on-surface-variant);
  display:-webkit-box;
  -webkit-line-clamp:2;
  -webkit-box-orient:vertical;
  overflow:hidden;
}
/* "… more" (2.1.6), only on a description menu.js found was cut. Drawn
   over the end of the second line on the card's own colour, so it covers
   the browser's "…": a faded "…" first, then "more" in the brand colour —
   the pattern people know from Instagram and Facebook for "tap for the
   rest". Sized in em so it follows the text size. The row's tap target
   sits above it, so tapping anywhere still opens the dish. */
.item__desc.is-clamped::before,
.item__desc.is-clamped::after{
  position:absolute; bottom:0;
  line-height:inherit;
}
.item__desc.is-clamped::after{
  content:"more";
  right:0;
  width:2.6em;           /* "more" in Geist 500 is 2.4em; a hair of room */
  text-align:right;
  background:var(--surface-container-low);
  color:var(--primary);
  font-weight:500;
}
.item__desc.is-clamped::before{
  content:"\2026";
  right:2.6em;
  padding:0 .15em 0 .9em;
  background:linear-gradient(to right, transparent, var(--surface-container-low) .75em);
}
.item__info{
  margin:7px 0 0;
  font-size:var(--fs-meta);
  line-height:1.4;
  color:var(--outline);
}

.badge{
  display:inline-flex; align-items:center;
  border-radius:var(--shape-full);
  padding:2px 9px;
  background:var(--badge);
  color:#fff;
  font-size:.625rem; font-weight:500; line-height:1.6;
  white-space:nowrap;
}

.prices{
  list-style:none;
  flex:0 0 auto;
  display:flex; flex-direction:column; align-items:flex-end;
  gap:var(--sp-1);
  margin:0; padding:0;
}
.price{
  display:inline-flex; align-items:stretch;
  border-radius:var(--shape-full);
  overflow:hidden;
  font-size:.75rem; line-height:1.6;
}
.price__label{
  padding:4px 9px;
  background:var(--primary);
  color:var(--on-primary);
  max-width:74px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.price__value{
  padding:4px 11px;
  background:var(--primary-container);
  color:var(--on-primary-container);
  white-space:nowrap;
  font-variant-numeric:tabular-nums;
}

mark{ background:var(--secondary-container); color:var(--on-secondary-container); border-radius:3px; }

/* Two columns of items inside the full-width card */
@media (min-width:900px){
  .items{ display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); column-gap:var(--sp-6); }
  .items > .item:nth-child(-n+2){ border-top:0; }
  .price__label{ max-width:96px; }
}
/* Filtering changes which item is first and :nth-child still counts the
   hidden ones, so the rules come off while a search is running. Results
   are short lists where the dividers earn little anyway. */
body.is-searching .items > .item{ border-top-color:transparent; }

.empty{
  max-width:var(--wrap);
  margin:0 auto;
  padding:var(--sp-6) var(--gutter);
  color:var(--on-surface-variant);
  font-size:.875rem;
}

/* ===================================================================
   No search results

   Centred horizontally but kept near the top rather than mid-screen:
   on a phone the keyboard covers the lower half while someone is
   searching. Colours are palette roles only, so the message follows
   the brand seed, or the sponsor's seed when one is running.
   ================================================================= */
.noresults{
  display:flex;
  flex-direction:column;
  align-items:center;
  max-width:var(--wrap);
  margin:0 auto;
  padding:var(--sp-6) var(--gutter) calc(var(--sp-6) * 2);
  text-align:center;
}
/* display:flex beats the browser's own [hidden] rule, the same trap
   as the search field's clear button. */
.noresults[hidden]{ display:none; }
.noresults__icon{
  display:flex;
  align-items:center;
  justify-content:center;
  width:72px; height:72px;
  border-radius:var(--shape-full);
  background:var(--primary-container);
  color:var(--on-primary-container);
  font-size:2.25rem;
}
.noresults__title{
  margin:var(--sp-5) 0 0;
  font-size:var(--fs-cat);
  font-weight:700;
  letter-spacing:-.01em;
  color:var(--on-surface);
}
.noresults__text{
  margin:var(--sp-2) 0 0;
  max-width:34ch;
  font-size:var(--fs-body);
  line-height:1.5;
  color:var(--on-surface-variant);
}
.noresults__term{ color:var(--on-surface); font-weight:500; }
/* Tonal rather than filled: primary already means the active chip, the
   current menu and the price labels. */
.noresults__btn{
  display:inline-flex;
  align-items:center;
  gap:var(--sp-2);
  margin-top:var(--sp-5);
  border:0;
  border-radius:var(--shape-full);
  padding:10px var(--sp-5) 10px var(--sp-4);
  background:var(--primary-container);
  color:var(--on-primary-container);
  font-size:.875rem;
  font-weight:500;
  cursor:pointer;
}
.noresults__btn svg{ font-size:1.125rem; }


/* ===================================================================
   Detail sheet
   ================================================================= */
.sheet{ position:fixed; inset:0; z-index:60; display:flex; align-items:flex-end; justify-content:center; }
.sheet[hidden]{ display:none; }
.sheet__scrim{
  position:absolute; inset:0;
  background:rgba(0,0,0,.42);
  opacity:0; transition:opacity .22s ease;
}
.sheet.is-open .sheet__scrim{ opacity:1; }
.sheet__card{
  position:relative;
  width:100%;
  max-height:86vh;
  overflow-y:auto;
  background:var(--surface-container-low);
  border-radius:var(--shape-xl) var(--shape-xl) 0 0;
  padding:var(--sp-3) var(--sp-5) calc(var(--sp-6) + env(safe-area-inset-bottom));
  transform:translateY(100%);
  transition:transform .26s cubic-bezier(.2,.8,.25,1);
}
.sheet.is-open .sheet__card{ transform:translateY(0); }
.sheet__grip{
  display:block; width:32px; height:4px;
  margin:0 auto var(--sp-4);
  border:0; padding:0;
  border-radius:var(--shape-full);
  background:var(--outline-variant);
  cursor:pointer;
}
.sheet__close{
  position:absolute; top:14px; right:12px;
  display:none; padding:6px;
  border:0; background:none;
  color:var(--on-surface-variant); font-size:1.25rem; cursor:pointer;
}
.sheet__title{
  display:flex; align-items:center; gap:var(--sp-2); flex-wrap:wrap;
  margin:0;
  font-size:var(--fs-cat); font-weight:500; letter-spacing:-.01em;
  color:var(--on-surface);
}
.sheet__desc{
  margin:var(--sp-2) 0 0;
  font-size:var(--fs-item); line-height:1.55;
  color:var(--on-surface-variant);
  max-width:62ch;
}
.sheet__body .item__info{ margin-top:var(--sp-2); font-size:var(--fs-body); }
.sheet__body .prices{ align-items:flex-start; gap:var(--sp-2); margin-top:var(--sp-4); }
.sheet__body .price{ font-size:var(--fs-body); }
.sheet__body .price__label{ max-width:none; padding:6px 12px; }
.sheet__body .price__value{ padding:6px 14px; }

@media (min-width:720px){
  .sheet{ align-items:center; padding:var(--sp-6); }
  .sheet__card{
    max-width:560px;
    border-radius:var(--shape-xl);
    padding:var(--sp-6);
    transform:translateY(12px); opacity:0;
    transition:transform .2s ease, opacity .2s ease;
  }
  .sheet.is-open .sheet__card{ transform:none; opacity:1; }
  .sheet__grip{ display:none; }
  .sheet__close{ display:block; }
}

/* ===================================================================
   Connect
   ================================================================= */
.fab{
  position:fixed;
  right:var(--sp-4);
  bottom:calc(var(--sp-4) + env(safe-area-inset-bottom));
  z-index:55;
  display:flex; flex-direction:column; align-items:flex-end;
  /* The panel keeps its height even while hidden, so this box is a tall
     invisible column up the right-hand side. Without this it swallows
     taps meant for whatever is behind it — category headers especially,
     since their chevron sits on the same edge. */
  pointer-events:none;
}
.fab__btn,
.fab__row,
.fab__scrim{ pointer-events:auto; }
body.sheet-open .fab{ display:none; }
.fab__scrim{
  position:fixed; inset:0;
  background:rgba(255,255,255,.88);
  opacity:0; visibility:hidden;
  transition:opacity .2s ease, visibility .2s;
}
.fab.is-open .fab__scrim{ opacity:1; visibility:visible; }
.fab__panel{
  position:relative;
  display:flex; flex-direction:column; align-items:flex-end;
  gap:var(--sp-3);
  margin-bottom:var(--sp-3);
  visibility:hidden;
}
.fab.is-open .fab__panel{ visibility:visible; }
.fab__row{
  display:flex; align-items:center; gap:var(--sp-3);
  text-decoration:none;
  color:var(--on-surface-variant);
  font-size:var(--fs-body);
  opacity:0; transform:translateY(10px);
  transition:opacity .16s ease, transform .16s ease;
}
.fab.is-open .fab__row{ opacity:1; transform:none; }
.fab.is-open .fab__row:nth-last-child(1){ transition-delay:.02s; }
.fab.is-open .fab__row:nth-last-child(2){ transition-delay:.05s; }
.fab.is-open .fab__row:nth-last-child(3){ transition-delay:.08s; }
.fab.is-open .fab__row:nth-last-child(4){ transition-delay:.11s; }
.fab.is-open .fab__row:nth-last-child(5){ transition-delay:.14s; }
.fab.is-open .fab__row:nth-last-child(6){ transition-delay:.17s; }
.fab__icon{
  flex:0 0 auto;
  width:44px; height:44px;
  border-radius:var(--shape-full);
  display:flex; align-items:center; justify-content:center;
  color:#fff; font-size:1.3125rem;
  box-shadow:0 2px 8px rgba(0,0,0,.16);
}
.fab__text{ white-space:nowrap; }
.fab__btn{
  /* The scrim is position:fixed, so an unpositioned sibling paints
     underneath it — which made this button look faded out. */
  position:relative;
  display:inline-flex; align-items:center; gap:var(--sp-2);
  padding:var(--sp-4) var(--sp-5);
  border:0;
  border-radius:var(--shape-l);
  background:var(--primary);
  color:var(--on-primary);
  font-size:var(--fs-item); font-weight:500; cursor:pointer;
  box-shadow:0 3px 12px rgba(0,0,0,.22);
}
.fab__glyph{ font-size:1.3125rem; }
.fab__glyph--close,.fab__label--close{ display:none; }
.fab.is-open .fab__glyph--open,.fab.is-open .fab__label--open{ display:none; }
.fab.is-open .fab__glyph--close{ display:block; }
.fab.is-open .fab__label--close{ display:inline; }

@media (min-width:720px){
  .fab{ right:var(--sp-6); bottom:var(--sp-6); }
}

/* ===================================================================
   Footer
   ================================================================= */
.footer{
  background:var(--footer-bg);
  color:#fff;
  padding:var(--sp-6) var(--gutter);
}
/* Clearance for the floating button belongs to the content, not the
   footer — putting it here kept the footer's padding lopsided. */
.has-fab main{ padding-bottom:calc(72px + env(safe-area-inset-bottom)); }
.footer{ display:flex; justify-content:center; }
.footer__mark{
  display:block;
  color:#fff;
  text-decoration:none;
  font-size:var(--fs-body);
}
.footer__mark svg{ width:auto; height:24px; }

@media (prefers-reduced-motion:reduce){
  html{ scroll-behavior:auto; }
  *,*::before,*::after{ animation-duration:.001ms !important; transition-duration:.001ms !important; }
}

/* ===================================================================
   Print

   Owners print menus, and "save as PDF" runs through the same rules.
   Everything interactive goes, every category opens, colour drops out.
   ================================================================= */
@media print{
  .bar,.fab,.sheet,.skip-link,.cat__chev,.item__open,.bar__chips,.drop{ display:none !important; }
  html,body{ background:#fff !important; color:#000 !important; font-size:10pt; }

  .hero{ min-height:auto; border-radius:0; break-after:avoid; }
  .hero::after{ display:none; }
  .hero__img{ display:none; }
  .hero__inner{ padding:0 0 12pt; }
  .hero__title{ color:#000 !important; font-size:20pt; }
  .hero__tagline{ color:#333 !important; }
  .hero__mark{ filter:grayscale(1); }

  main{ column-count:2; column-gap:20pt; }
  .panel{ display:block !important; padding:0; }
  .cat{ padding:0; margin:0 0 10pt; break-inside:avoid; }
  .cat__head,.items{ background:none !important; border-radius:0; padding:0; }
  .cat .items{ display:block !important; }
  .cat__toggle{ padding:0 0 4pt; border-bottom:0.5pt solid #000 !important; }
  .cat__name{ font-size:13pt; }

  .item,.item.is-hidden{ display:flex !important; padding:0 0 6pt; break-inside:avoid; }
  .item__desc{ -webkit-line-clamp:unset; overflow:visible; color:#333 !important; }
  .item__desc::before,.item__desc::after{ display:none !important; }
  .item__info{ color:#555 !important; }
  .badge{ background:none !important; color:#000 !important; border:0.5pt solid #000; }
  .price{ border:0.5pt solid #666; }
  .price__label,.price__value{ background:none !important; color:#000 !important; padding:0 3pt; }

  .footer{ background:none !important; color:#000 !important; padding:8pt 0 0; }
  a{ text-decoration:none; color:#000 !important; }
}


/* ===================================================================
   Sponsorship

   A sponsored menu is paid for by a third party and carries their mark
   at the top of every page view. Everything below is additive: without
   .has-sponsor on the body, --sponsor-h is zero and none of it applies,
   so the standard build is unaffected by its presence.
   ================================================================= */
:root{
  /* Zero by default. Every rule that has to clear the band reads this,
     so there is one number to change and nothing to keep in step. */
  --sponsor-h:0px;
  --sponsor-bg:#004B93;
}
body.has-sponsor{ --sponsor-h:52px; }
@media (min-width:720px){ body.has-sponsor{ --sponsor-h:60px; } }

/* The band pins at the top and the bar pins directly beneath it. Both
   are sticky rather than fixed, so neither is taken out of the flow. */
.sponsor{
  position:sticky;
  top:0;
  z-index:50;                 /* above the bar (40), below the Connect panel (55) */
  height:var(--sponsor-h);
  display:flex;
  align-items:center;
  background:var(--sponsor-bg);
}
body.has-sponsor .bar{ top:var(--sponsor-h); }

/* Left aligned, and on the same column as everything else on the page —
   the mark lines up with the business name in the bar below it rather
   than sitting on its own inset. */
.sponsor__link{
  display:flex;
  align-items:center;
  height:100%;
  padding:calc((var(--bar-h) - 44px) / 2) max(var(--gutter), calc((100% - var(--wrap)) / 2 + var(--gutter)));
  text-decoration:none;
}
/* The mark is square and carries its own white disc, so it needs no
   plate behind it and no clear-space padding beyond the band's own. */
.sponsor__logo{
  height:36px;
  width:auto;
  display:block;
}
@media (min-width:720px){ .sponsor__logo{ height:44px; } }

/* ---- Adverts between categories ---------------------------------- */
/* Same gutter, same max width and the same bottom margin as a category
   card, so an advert sits on the page's rhythm instead of on top of it. */
.promo{
  max-width:var(--wrap);
  margin:0 auto var(--sp-5);
  padding:0 var(--gutter);
}
/* A slot ships hidden and is only revealed once the ad server has actually
   put something inside it. The !important is not decoration: the desktop
   rule below sets display:flex, and a display declaration beats the user
   agent's [hidden] rule — the same way display:flex once kept the search
   field's clear button visible when it was meant to be gone. */
.promo[hidden]{ display:none !important; }

.promo__label{
  display:block;
  margin:0 0 5px;
  font-size:var(--fs-meta);
  line-height:1.4;
  text-align:center;
  /* on-surface-variant rather than outline: this is a disclosure, and it
     is the smallest text on the page. outline measures 3.3:1. */
  color:var(--on-surface-variant);
}

/* The markup inside comes from the ad server, so nothing in here can be
   addressed by class. Everything is a descendant selector, and the image
   is capped at 100% because an advertiser who uploads oversize artwork
   would otherwise push the page sideways. */
.promo__box{
  border-radius:var(--shape-xl);
  overflow:hidden;
  line-height:0;
}
.promo__box ins{ display:block; text-decoration:none; }
.promo__box a{ display:block; line-height:0; }
.promo__box img{
  display:block;
  width:100%;
  max-width:100%;
  height:auto;
  margin:0 auto;
}

/* The desktop creative is a fixed 780x90 leaderboard, not a fluid image.
   Left to fill the content column it is upscaled to about 1050px wide and
   goes soft, so from 720px up the slot is pinned to the creative's own
   width and centred. The radius drops with it — 28px on a 90px-tall strip
   reads as a pill rather than a banner. */
@media (min-width:720px){
  .promo{ display:flex; flex-direction:column; align-items:center; }
  .promo__label{ width:780px; max-width:100%; }
  .promo__box{ width:780px; max-width:100%; border-radius:var(--shape-m); }
  .promo__box img{ width:auto; }
}

/* A filtered list is someone looking for one dish. Leaving adverts
   between the results makes the filter look broken. */
body.is-searching .promo{ display:none; }

@media print{
  .promo{ display:none !important; }
  /* The band survives into print and PDF, flattened. The mark carries
     its own colours and prints as-is on white. */
  .sponsor{
    position:static;
    height:auto;
    background:none !important;
    padding:0 0 8pt;
    border-bottom:0.5pt solid #000;
  }
  .sponsor__link{ padding:0; }
  .sponsor__logo{ height:20pt; }
}

/* ===================================================================
   Brand bar

   On an unsponsored site the compact row of the scrolled bar — logo,
   name, switcher, search icon — takes the restaurant's brand colour,
   continuing the browser strip above it (theme-color). The
   chips row below stays neutral, on surface-container, so primary still
   means "active" there: colouring the chips too is what forced the
   active chip to invert, and why the first full-colour bar was reverted.

   Everything here is scoped to body.has-brand-bar, which PHP sets only
   when no sponsor band is showing. A sponsored site renders exactly as
   before. --brand-bar and --on-brand-bar come from PHP: the colour is
   brand.seed, the text colour is brand.bar_text.
   ================================================================= */
body.has-brand-bar .bar__compact{
  background:var(--brand-bar);
  color:var(--on-brand-bar);
}
body.has-brand-bar .bar__search-btn{ color:var(--on-brand-bar); }

/* A filled primary pill would sink into the bar, so the switcher inverts:
   the pill takes the text colour and its label the bar colour. Contrast
   is symmetric, so it reads exactly as well as the business name does. */
body.has-brand-bar .drop--compact .drop__btn{
  background:var(--on-brand-bar);
  color:var(--brand-bar);
}

/* The page-wide focus ring is primary, which can vanish on the bar. Only
   the two controls sitting on it change; the open dropdown list is on a
   neutral surface and keeps the normal ring. */
body.has-brand-bar .drop--compact .drop__btn:focus-visible,
body.has-brand-bar .bar__search-btn:focus-visible{
  outline-color:var(--on-brand-bar);
}

/* Compact search open. The field replaces the compact row, so the row
   keeps the brand colour — otherwise the colour block would vanish every
   time someone searched. Padded to the compact row's height so the field
   sits centred in it rather than touching the strip above. */
body.has-brand-bar .bar.is-stuck .bar__row{
  background:var(--brand-bar);
  padding-top:calc((var(--bar-h) - 44px) / 2);
  padding-bottom:calc((var(--bar-h) - 44px) / 2);
}

/* Once pinned, the area below the coloured row — chips and the bar's own
   bottom padding — sits on surface-container, so it reads as part of the
   bar rather than floating on the page. At rest the bar stays surface. */
body.has-brand-bar .bar.is-stuck{ background:var(--surface-container); }

/*html:root{ --primary: #f40009; }*/ /*coke primary colour override */