/* OrderHero diner menu (layout v3). Coterie look: cream, yellow glows, frosted cream cards, ink pills, light big headings.
   The diner is already seated, so the page is the menu: announcement, shop row (menu button), promo artwork, categories above the
   search, items.
   Solid rules: the page never scrolls sideways (only the promo, category and chip rows scroll, inside their own box),
   every media box has a reserved size, the sticky stack never changes its LAYOUT height while you scroll (the promo
   collapse is a height change inside a reserved box plus a transform), and motion uses one ease and is switched off for
   reduced motion. */
:root{
  --cream:#f6f0dc;--cream2:#fbf8ec;--card:rgba(255,253,246,.78);--card-solid:#fffdf6;
  --ink:#151410;--ink2:#3a372e;--mut:#6f6a5c;--line:#ece5cf;
  --yel:#f4cf3a;--yel2:#fae48a;--yel3:#fdf1b8;--on-yel:#151410;--yel-ink:#7a5c00;
  --anchor:#171612;--on-anchor:#fffdf6;
  --r:20px;--ease:cubic-bezier(.22,1,.36,1);--dur:.28s;
  --shadow:0 1px 2px rgba(80,62,10,.05),0 10px 30px rgba(110,86,10,.07);
  --shadow-s:0 1px 2px rgba(80,62,10,.07),0 5px 12px -3px rgba(110,86,10,.14);
  --bar:#f6f0dc;                    /* opaque: nothing may show through the sticky stack */
  --gut:16px;--sab:env(safe-area-inset-bottom,0px);--cpy:32px;
  --h25:25vh;--h30:30vh;
  --cw:calc(min(100vw,1200px) - 2 * var(--gut)); /* content width */
  --tw:clamp(120px,38vw,170px);                   /* category tile width (4:3) */
  color-scheme:light;
}
@supports (height:1svh){:root{--h25:25svh;--h30:30svh}}
*,*::before,*::after{box-sizing:border-box}
html{font-size:16px;-webkit-text-size-adjust:100%;scroll-behavior:auto;overflow-anchor:none}
html.big{font-size:19px}
body{margin:0;background:var(--cream);color:var(--ink);min-height:100vh;min-height:100svh;overflow-x:hidden;-webkit-tap-highlight-color:transparent;
  font:400 1rem/1.45 system-ui,-apple-system,"Segoe UI",Roboto,"PingFang HK","PingFang TC","Microsoft JhengHei","Noto Sans CJK TC",sans-serif;
  font-feature-settings:'tnum' 1}
body.lock{overflow:hidden}
h1,h2,h3,p,ul,ol,figure{margin:0}
ul{padding:0;list-style:none}
a{color:inherit}
button,input,textarea{font:inherit;color:inherit}
button{background:none;border:0;padding:0;cursor:pointer;touch-action:manipulation}
img{display:block}
:focus-visible{outline:3px solid var(--ink);outline-offset:2px}
[tabindex="-1"]:focus{outline:0}
[hidden]{display:none!important}
html[data-lang=en] .on-z,html[data-lang=zh] .on-e{display:none!important}
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* background glows (Coterie .g1 .g2) */
.glow{position:fixed;z-index:-1;pointer-events:none;border-radius:50%}
.g1{width:70vw;height:70vw;right:-20vw;bottom:-30vw;background:radial-gradient(closest-side,#f6d54a 0%,rgba(246,213,74,.55) 40%,transparent 72%)}
.g2{width:60vw;height:50vw;left:-20vw;top:-18vw;background:radial-gradient(closest-side,rgba(250,228,138,.7),transparent 70%)}

.card{position:relative;background:var(--card);border-radius:var(--r);box-shadow:var(--shadow);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
.app{max-width:1200px;margin:0 auto;padding:0 env(safe-area-inset-right) 0 env(safe-area-inset-left)}

/* =====================================================================================================
   THE STICKY STACK: announcement, shop row, promo artwork, search row, category row.
   Portrait: the whole stack is sticky. Collapsing the promo to half height moves NOTHING in the layout:
   .promos keeps its full height, the promo row shrinks inside it and .tools slides up by the same amount.
   So the list below never shifts when the stack collapses or expands (no scroll compensation needed).
   ===================================================================================================== */
.stack{
  --sw:var(--cw);
  --ph:min(calc(var(--sw) * 9 / 16),var(--h25));   /* promo height (16:9 slide, never more than 25% of the screen) */
  --pw:calc(var(--ph) * 16 / 9);                   /* promo slide width */
  --pcur:var(--ph);
  --cut:calc(var(--ph) / 2);                       /* how far the tools slide up when the promo collapses (half: small art shown) */
  /* fixed design heights, so the stack's LAYOUT height never changes in the home view (the half and compact states are transforms) */
  --annh:32px;--prbh:0px;--shoph:56px;--infoh:0px;--pbox:0px;--srh:64px;
  --hh:calc(var(--annh) + var(--prbh) + var(--shoph) + var(--infoh) + var(--pbox));   /* head: announcement + price-rule banner + shop row + details line + promo box */
  --th:calc(var(--tw) * .75 + 20px + var(--srh));        /* tools: category tiles + search row (reserved) */
  --ty:0px;                                              /* how far the tools are slid up right now */
  position:sticky;top:0;z-index:20;pointer-events:none;
  /* Under a notch / status bar (home-screen app, viewport-fit=cover) the stack starts below it, and an opaque strip covers that area,
     so in the compact state nothing scrolling behind can show above the sticky bar (Lee, 2026-10-14). 0px in a normal browser tab. */
  padding-top:env(safe-area-inset-top,0px);
}
.stack::before{content:"";position:absolute;left:0;right:0;top:0;height:env(safe-area-inset-top,0px);background:var(--bar);z-index:3}
.stack.noann{--annh:0px}
.stack.hasprb{--prbh:28px}
.stack.hasinfo{--infoh:24px}   /* the venue details line: its height is part of the head, so compact slides the tools fully to the top */
.stack.hp{--pbox:calc(var(--ph) + 12px)}
.stack.hp.is-col{--ty:var(--cut)}
.stack.is-cmp,.stack.hp.is-col.is-cmp{--ty:var(--hh)}   /* compact: the tools reach the very top */
.stack.ns{--cut:calc(var(--ph) + 12px)}             /* no post has a small image: the whole promo section folds away */
.stack.multi{--sw:calc(var(--cw) - 20px)}          /* more than one promo: the next slide peeks in */
.stack.is-col{--pcur:calc(var(--ph) / 2)}
html:not(.js) .stack{position:static;pointer-events:auto}
.stack :is(.ann,.shop,.promos-in,.tools-in){pointer-events:auto}

/* head = announcement + shop + promos. It folds away (height 0, fade) in the focus view. */
.head{display:grid;grid-template-rows:1fr;transition:grid-template-rows var(--dur) var(--ease),opacity .2s var(--ease),transform var(--dur) var(--ease),visibility 0s}
/* compact (home, scrolled past the first dish): the head slides up and away, together with the tools reaching the top */
.stack.is-cmp .head{transform:translateY(calc(var(--hh) * -1));visibility:hidden;transition:grid-template-rows var(--dur) var(--ease),opacity .2s var(--ease),transform var(--dur) var(--ease),visibility 0s var(--dur)}
.head-in{min-height:0;min-width:0;overflow:hidden}
html[data-view=home] .head-in{overflow:visible}   /* the settings column floats below the shop row */
html[data-view=focus] .head{grid-template-rows:0fr;opacity:0;visibility:hidden;transition:grid-template-rows var(--dur) var(--ease),opacity .2s var(--ease),transform var(--dur) var(--ease),visibility 0s var(--dur)}

/* announcement: continuous right-to-left marquee (two copies, translateX 0 to -50%) */
.ann{position:relative;height:32px;overflow:hidden;background:var(--yel);color:var(--on-yel);font-size:.85rem;font-weight:500;display:flex;align-items:center}
.ann-track{display:flex;flex:none;width:max-content;will-change:transform}
.ann-copy{flex:none;white-space:nowrap;line-height:32px;padding-inline-end:48px}
.ann:not(.run) .ann-track{margin:0 auto}
.ann:not(.run) .ann-copy{padding:0 var(--gut);max-width:100vw;overflow:hidden;text-overflow:ellipsis}
.ann:not(.run) .ann-copy+.ann-copy{display:none}
.ann.run .ann-track{animation:annx var(--annd,20s) linear infinite}
@keyframes annx{to{transform:translateX(-50%)}}
.ann[role=button]{cursor:pointer}

/* shop row: logo + name + Table pill, then language / A+ / bag */
/* price-rule banner (CONTRACT-PRICERULES.md): one line under the announcement while a rule is in force; long text slides like a tag line */
.prb{height:28px;line-height:28px;padding:0 var(--gut);background:var(--yel3);color:var(--yel-ink);font-size:.8rem;font-weight:700}
.prb[hidden]{display:none}
.prl{display:flex;line-height:1}
.p.pr{font-weight:700;background:var(--yel3);color:var(--yel-ink);box-shadow:none}
.p.pr.up{background:var(--ink);color:var(--cream2)}   /* a mark-up is labelled plainly, never dressed as a deal */
.was{font-weight:400;font-size:.78rem;opacity:.7;margin-right:6px;text-decoration:line-through}
.ip-price .was{font-size:1rem;margin-right:8px}
.ln .prl{margin-top:4px}
.upc .prl{justify-content:center}
.upc .p.pr{font-size:.64rem;padding:2px 6px;white-space:normal;text-align:center;line-height:1.2}
.vinfo{margin:0;height:24px;padding:0 var(--gut) 6px;box-sizing:border-box;overflow:hidden;background:var(--bar);font-size:.82rem;line-height:18px;color:var(--ink2)}
.shop{display:flex;align-items:center;gap:8px;height:56px;padding:0 var(--gut);background:var(--bar)}
.bw{flex:1;min-width:0;display:flex;align-items:center;gap:8px}
.brand{display:flex;align-items:center;gap:8px;min-width:0;font-weight:600;font-size:.92rem;line-height:1.15}
.bn{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-width:0;overflow-wrap:anywhere}
.logo{width:18px;height:18px;border-radius:50%;background:var(--ink);position:relative;flex:none;margin-right:4px}
.logo::after{content:"";position:absolute;right:-3px;top:-3px;width:9px;height:9px;border-radius:50%;background:var(--yel)}
.logo-img{width:24px;height:24px;border-radius:50%;object-fit:cover;flex:none}
.tbp{flex:none;display:inline-flex;align-items:center;height:28px;padding:0 10px;border-radius:99px;background:var(--ink);color:var(--cream2);font-size:.78rem;font-weight:600;white-space:nowrap}
.top-r{display:flex;align-items:center;gap:4px;flex:none}
.pbtn{position:relative;min-width:44px;min-height:44px;border-radius:99px;background:rgba(255,253,246,.8);box-shadow:var(--shadow-s);display:grid;place-items:center;font-weight:600;font-size:.9rem;transition:transform .2s var(--ease)}
.pbtn:active,.pp:active,.ct:active,.item:active,.upbtn:active{transform:scale(.97)}
.cnt{position:absolute;top:2px;right:0;min-width:20px;height:20px;padding:0 5px;border-radius:99px;background:var(--yel);color:var(--on-yel);font-size:.72rem;font-weight:700;display:grid;place-items:center;box-shadow:0 0 0 2px var(--cream)}

/* menu button: a 44px circle with three lines. It opens a column BELOW it (absolutely positioned, so nothing on the shop row moves or is covered) holding the language circles and the larger-text circle. menu.js folds it after 5s without interaction. */
.mset{position:relative;z-index:4;flex:none;width:44px;height:44px}
.mbtn{position:relative;z-index:2;width:44px;height:44px;border-radius:50%;background:rgba(255,253,246,.8);box-shadow:var(--shadow-s);display:grid;place-items:center;transition:background-color .2s var(--ease),color .2s var(--ease),transform .2s var(--ease)}
.mbtn:active{transform:scale(.95)}
.mbtn[aria-expanded=true]{background:var(--ink);color:var(--cream2)}
.mset-p{display:flex;align-items:center;gap:6px}
.lg{display:flex;align-items:center;gap:6px}
.lc{position:relative;flex:none;box-sizing:border-box;width:36px;height:36px;min-width:36px;padding:0;border-radius:50%;display:grid;place-items:center;font-size:.8rem;font-weight:600;line-height:1;text-decoration:none;color:var(--ink2);background:rgba(255,253,246,.8);box-shadow:inset 0 0 0 1px var(--line);transition:background-color .2s var(--ease),color .2s var(--ease),box-shadow .2s var(--ease),transform .2s var(--ease)}
.lc::after{content:"";position:absolute;inset:-3px;border-radius:50%}
.lc:active{transform:scale(.94)}
html[data-lang=en] .lc[data-l=en],html[data-lang=zh] .lc[data-l=zh]{background:var(--ink);color:var(--cream2);box-shadow:none}
.tsz{display:flex;align-items:center;justify-content:center}
.tsz sup{font-size:.62em;line-height:1;position:relative;top:-.3em;margin-left:1px}
.tsz[aria-pressed=true]{background:var(--yel);color:var(--on-yel);box-shadow:none}
/* no JS: the language circles sit inline in the row (the A+ and menu buttons need JS and are hidden by the noscript rule) */
html:not(.js) .mbtn{display:none}
html:not(.js) .mset{width:auto}
/* with JS: folded = invisible; open = the pill grows out of the button towards the left */
/* with JS: folded = invisible; open = a vertical column of circles drops down from the button (8px gap), floating over the promo
   with a soft shadow. Nothing on the shop row moves or is covered. */
.js .mset-p{position:absolute;right:0;top:52px;width:44px;flex-direction:column;padding:4px 0;border-radius:22px;background:var(--card-solid);box-shadow:0 12px 28px -8px rgba(80,62,10,.30),0 2px 6px rgba(80,62,10,.10);visibility:hidden;opacity:0;transform:translateY(-10px) scale(.94);transform-origin:top right;transition:opacity .16s var(--ease),transform var(--dur) var(--ease),visibility 0s var(--dur)}
.js .lg{flex-direction:column}
.js .mset.open .mset-p{visibility:visible;opacity:1;transform:none;transition:opacity .2s var(--ease),transform var(--dur) var(--ease),visibility 0s}
@media (max-width:420px){.logo,.logo-img{display:none}}

/* promo carousel: artwork only. .promos keeps the FULL height; the row inside shrinks to half when collapsed. */
.promos{height:calc(var(--ph) + 12px);pointer-events:none}
.promos-in{background:var(--bar);padding:4px 0 8px;transition:padding var(--dur) var(--ease)}
.prow{display:flex;gap:12px;overflow-x:auto;overflow-y:hidden;height:var(--pcur);padding:0 var(--gut);scroll-snap-type:x mandatory;scroll-padding-inline:var(--gut);scrollbar-width:none;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain;transition:height var(--dur) var(--ease),visibility 0s}
.prow::-webkit-scrollbar{display:none}
/* each post: the LARGE image (16:9) is always there; the SMALL one (32:9) fades in on top of it when the row collapses */
.slide{position:relative;flex:none;width:var(--pw);height:100%;border-radius:20px;overflow:hidden;scroll-snap-align:start;background:var(--yel3);box-shadow:var(--shadow-s);transition:width var(--dur) var(--ease)}
.sl{display:block;position:relative;width:100%;height:100%;border-radius:20px;overflow:hidden}
.slide img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center}
.slide img.sm{opacity:0;transition:opacity .2s linear;pointer-events:none}
.slide img.bad{display:none}
.sl:focus-visible{outline-offset:-4px;outline-color:var(--ink)}
/* collapsed: posts without a small image are hidden; the others show the small one (once it has loaded) */
.stack.is-col .slide.rdy img.sm{opacity:1}
.stack.is-col:not(.ns) .slide:not(.has-sm){display:none}
.stack.is-col.sm1 .slide.has-sm{width:var(--cw)}     /* one small post left: it takes the full width, nothing to peek */
/* collapsed and no post has a small image: the whole section folds away (the tools slide up by the full promo height) */
.stack.ns.is-col .prow{height:0;visibility:hidden;transition:height var(--dur) var(--ease),visibility 0s var(--dur)}
.stack.ns.is-col .promos-in{padding-top:0;padding-bottom:0}

/* search row + categories */
.tools{position:relative;transform:translateY(calc(var(--ty) * -1));transition:transform var(--dur) var(--ease)}
/* the tools box keeps its full height in the home view; .tools-in (the visible bar) is taken out of the flow so it can shrink
   (tiles to chips) without moving the list. The space it leaves is transparent: the list shows through it. */
html[data-view=home] .tools{height:var(--th)}
html:not(.js) .tools{height:auto!important}
.tools-in{position:absolute;left:0;right:0;top:0;isolation:isolate;background:var(--bar);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}
html[data-view=focus] .tools-in,html:not(.js) .tools-in{position:relative}
.tools-in::after{content:"";position:absolute;left:0;right:0;top:100%;height:14px;background:linear-gradient(rgba(80,62,10,.10),rgba(80,62,10,0));opacity:0;pointer-events:none;transition:opacity var(--dur) var(--ease)}
.stack.is-col .tools-in::after,.stack.is-cmp .tools-in::after,html[data-view=focus] .tools-in::after{opacity:1}
html[data-view=focus] .stack .tools{transform:none}
.srow{display:flex;align-items:center;gap:8px;height:var(--srh);padding:8px var(--gut)}
.search{flex:1;min-width:0;display:flex;align-items:center;gap:8px;height:48px;padding:0 16px;border-radius:99px;background:var(--card-solid);box-shadow:inset 0 0 0 1px var(--line)}
.search:focus-within{box-shadow:inset 0 0 0 1.5px var(--ink)}
.search svg{flex:none;color:var(--mut)}
.search input{flex:1;min-width:0;border:0;outline:0;background:none;font-size:1rem;-webkit-appearance:none;appearance:none}
.fbtn{min-height:48px;min-width:48px}
.upbtn{flex:none;width:0;height:44px;margin-right:-8px;border-radius:50%;background:var(--ink);color:var(--cream2);display:grid;place-items:center;overflow:hidden;opacity:0;visibility:hidden;box-shadow:var(--shadow-s);transition:width var(--dur) var(--ease),margin var(--dur) var(--ease),opacity .2s var(--ease),transform .2s var(--ease),visibility 0s var(--dur)}
html[data-view=focus] .upbtn,html[data-cmp] .upbtn{width:44px;margin-right:0;opacity:1;visibility:visible;transition:width var(--dur) var(--ease),margin var(--dur) var(--ease),opacity .2s var(--ease),transform .2s var(--ease),visibility 0s}
.upbtn svg{flex:none}
.res{padding:12px 0 0;font-size:.9rem;color:var(--mut)}
.res:empty{display:none}
.res button{min-height:44px;padding:0 8px;text-decoration:underline;font-size:inherit;color:var(--ink)}

/* category tiles (home): a 4:3 box; the card is the bottom 2/3, the dish media is the top 2/3 so it floats on the card */
.trw,.crw{display:grid;transition:grid-template-rows var(--dur) var(--ease),opacity calc(var(--dur) * .7) var(--ease),visibility 0s}
.trw{grid-template-rows:1fr}
.crw{grid-template-rows:0fr;opacity:0;visibility:hidden;transition:grid-template-rows var(--dur) var(--ease),opacity calc(var(--dur) * .5) var(--ease),visibility 0s var(--dur)}
.trw-in,.crw-in{min-height:0;overflow:hidden}
html[data-view=focus] .trw,html[data-cmp] .trw{grid-template-rows:0fr;opacity:0;visibility:hidden;transition:grid-template-rows var(--dur) var(--ease),opacity calc(var(--dur) * .7) var(--ease),visibility 0s var(--dur)}
html[data-view=focus] .crw,html[data-cmp] .crw{grid-template-rows:1fr;opacity:1;visibility:visible;transition:grid-template-rows var(--dur) var(--ease),opacity calc(var(--dur) * .7) var(--ease) calc(var(--dur) * .15),visibility 0s}
.trow{display:flex;gap:12px;overflow-x:auto;overflow-y:hidden;height:calc(var(--tw) * .75 + 20px);padding:6px var(--gut) 14px;scroll-snap-type:x proximity;scroll-padding-inline:var(--gut);scrollbar-width:none;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain}
.trow::-webkit-scrollbar,.crow::-webkit-scrollbar{display:none}
.trow>li{flex:none}
.ct-media{transform-origin:50% 85%;animation:tsway 7s ease-in-out infinite alternate}
.ct:nth-child(2n) .ct-media,li:nth-child(2n) .ct-media{animation-delay:-3.5s}
@keyframes tsway{from{transform:rotate(-2.5deg)}to{transform:rotate(2.5deg)}}
.ct{position:relative;display:block;width:var(--tw);height:calc(var(--tw) * .75);text-decoration:none;color:inherit;scroll-snap-align:start;border-radius:var(--r);transition:transform .2s var(--ease)}
.ct-card{position:absolute;left:0;right:0;bottom:0;height:66.667%;border-radius:var(--r);background:rgba(255,253,246,.9);box-shadow:var(--shadow-s)}
.ct-media{position:absolute;left:0;right:0;top:0;height:66.667%;display:block}
.ct-media img.tp{position:absolute;inset:0;width:100%;height:100%;object-fit:contain}
.ct-media img.tp{padding:2px 0 0;filter:drop-shadow(0 4px 4px rgba(110,86,10,.2));transition:opacity .35s var(--ease)}
.ct-media img.bad{display:none}
/* The loop is filmed on white. It plays hidden (never display:none: iOS stops decoding) and menu.js draws each frame into the canvas
   through a WebGL colour-to-alpha shader, so the dish floats on the card on every browser (iOS ignores blend modes on <video>). */
.ct-media video{position:absolute;left:0;top:0;width:1px;height:1px;opacity:0;pointer-events:none}
.ct-media canvas{position:absolute;inset:0;width:100%;height:100%;opacity:0;pointer-events:none;transition:opacity .35s var(--ease)}
.ct-media.live canvas{opacity:1}
.ct-media.live img.tp{opacity:0}
.ct-meta{position:absolute;left:10px;right:8px;bottom:6px;height:26px;display:flex;align-items:center;gap:6px}
.ct-nm{flex:1;min-width:0;font-weight:600;font-size:.9rem;line-height:26px}
.ct-n{flex:none;display:grid;place-items:center;min-width:24px;height:24px;padding:0 6px;border-radius:99px;background:var(--yel);color:var(--on-yel);font-size:.76rem;font-weight:700;font-variant-numeric:tabular-nums}
.ct.off .ct-media{opacity:.55}
.ct.off .ct-card{opacity:.75}
.ct.off .ct-nm{color:var(--mut)}

/* one-line text that slides left and right when it does not fit (ping-pong, set up by menu.js) */
.nmq{overflow:hidden;min-width:0;white-space:nowrap}
.nmq-in{display:inline-block;max-width:none;will-change:transform}
/* the tag ticker fades at both ends instead of cutting a chip in half */
.nmq:has(>.nmq-in.tags){-webkit-mask-image:linear-gradient(to right,transparent,#000 8px,#000 calc(100% - 14px),transparent);mask-image:linear-gradient(to right,transparent,#000 8px,#000 calc(100% - 14px),transparent)}
#q::-webkit-search-cancel-button{filter:grayscale(1) brightness(.35);cursor:pointer}
html:not(.js) .nmq-in{display:block;overflow:hidden;text-overflow:ellipsis}

/* category chips (focus view): thin, the poster is only a background */
.crow{display:flex;gap:8px;overflow-x:auto;overflow-y:hidden;height:56px;padding:6px var(--gut) 10px;scroll-snap-type:x proximity;scroll-padding-inline:var(--gut);scrollbar-width:none;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain}
.crow>li{flex:none}
.chip{position:relative;isolation:isolate;overflow:hidden;display:inline-flex;align-items:center;height:40px;max-width:72vw;padding:0 16px;border-radius:99px;background:var(--card-solid);box-shadow:inset 0 0 0 1px var(--line);text-decoration:none;font-weight:600;font-size:.92rem;white-space:nowrap;scroll-snap-align:center;transition:background-color .25s var(--ease),color .25s var(--ease),box-shadow .25s var(--ease),transform .2s var(--ease)}
.chip img{position:absolute;inset:0;z-index:-2;width:100%;height:100%;object-fit:cover;object-position:center}
.chip img.bad{display:none}
.chip::before{content:"";position:absolute;inset:0;z-index:-1;background:rgba(251,248,236,.84);transition:background-color .25s var(--ease)}
.chip-t{overflow:hidden;text-overflow:ellipsis}
.chip[aria-current=true]{color:var(--cream2);box-shadow:0 0 0 2px var(--yel),0 4px 10px -2px rgba(21,20,16,.3)}
.chip[aria-current=true]::before{background:rgba(21,20,16,.82)}
html[data-search] .chip[aria-current=true]{color:inherit;box-shadow:inset 0 0 0 1px var(--line)}
html[data-search] .chip[aria-current=true]::before{background:rgba(251,248,236,.84)}
.chip.spy{box-shadow:0 0 0 2px var(--yel)}                 /* compact: the category in view, lighter than the focus view's chosen chip */
.chip:active{transform:scale(.97)}
.chip:focus-visible,.ct:focus-visible{outline-offset:1px}

/* =====================================================================================================
   LANDSCAPE PHONE: a ~390px tall screen cannot afford a tall sticky stack. Only the search row and the
   category row stick; announcement, shop row and promos scroll away. No promo collapse here.
   ===================================================================================================== */
@media (orientation:landscape) and (max-height:500px){
  .stack{display:contents;--ph:var(--h30);--pw:calc(var(--ph) * 16 / 9);--pcur:var(--ph);--sw:var(--cw);--tw:120px;--srh:56px}
  .tools{position:sticky;top:0;z-index:20;transform:none!important}
  .stack.is-cmp .head{transform:none;visibility:visible}
  .tools-in::after{opacity:1}
  .stack.is-col{--pcur:var(--ph)}
  .promos-in{padding:4px 0 8px}
  .srow{padding:4px var(--gut)}
}

/* ---------- the list ---------- */
main{padding:0 var(--gut)}
.endpad{height:calc(112px + var(--cpy) + var(--sab))}
.cat{padding-top:20px}
.cat.none{display:none}
html[data-view=focus]:not([data-search]) .cat:not(.on){display:none}
html[data-view=focus]:not([data-search]) .chead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
html[data-view=focus] .cat{padding-top:8px}
.chead{font-weight:300;font-size:clamp(26px,3vw,36px);letter-spacing:-.025em;line-height:1.1;margin-bottom:12px}
.srv{margin:-4px 0 12px;font-size:.9rem;font-weight:600;color:var(--yel-ink)}
.items{display:grid;grid-template-columns:minmax(0,1fr);gap:12px}
.item{display:grid;grid-template-columns:80px minmax(0,1fr);gap:12px;align-items:center;padding:12px;min-height:104px;transition:transform .2s var(--ease)}
.hit{position:absolute;inset:0;z-index:1;border-radius:var(--r)}
.iimg{position:relative;width:80px;height:80px}
.plate{position:absolute;inset:4px;border-radius:50%;background:radial-gradient(closest-side,var(--yel2),var(--yel3) 70%,transparent)}
.iimg img{position:relative;width:100%;height:100%;object-fit:contain;filter:drop-shadow(0 5px 5px rgba(110,86,10,.18))}
.iimg img.bad{display:none}
.ibody{display:flex;flex-direction:column;gap:6px;min-width:0}
/* card 1050: one line when the name fits; wraps to a 2nd line only when it does not (A+ / narrow phones); "…" only past 2 lines */
.nm{font-size:1.06rem;font-weight:600;line-height:1.25;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;line-clamp:2;overflow:hidden;overflow-wrap:anywhere}
.nsub{font-size:.88rem;line-height:1.3;color:var(--ink2);margin-top:1px}
.irow{display:flex;align-items:center;gap:8px;min-width:0;height:40px}
.tg{flex:1;min-width:0}
.tags{display:inline-flex;gap:4px;align-items:center;height:26px}
/* price pill with the + circle at its right end; it opens the item page, it never adds directly */
.pp{position:relative;z-index:2;flex:none;display:inline-flex;align-items:center;gap:8px;height:40px;padding:0 4px 0 14px;border-radius:99px;background:var(--yel);color:var(--on-yel);font-weight:700;font-size:.95rem;font-variant-numeric:tabular-nums;white-space:nowrap;text-decoration:none;box-shadow:inset 0 0 0 1px rgba(21,20,16,.12);transition:transform .2s var(--ease)}
.pp::after{content:"";position:absolute;inset:-2px}
.pp .fr{font-weight:400;font-size:.72rem;color:inherit;opacity:.8}
.pl{display:grid;place-items:center;width:32px;height:32px;border-radius:50%;background:var(--ink);color:var(--cream2)}
.pp.na{padding:0 14px}
.pp.un .pl{background:rgba(21,20,16,.4)}            /* category outside its serving hours: the + is still there, but muted */
.pp.so{background:var(--line);color:var(--ink2);box-shadow:none;font-weight:600}
.item.so .nm{color:var(--mut)}
.item.so .iimg{filter:grayscale(1);opacity:.55}
.cat.off .item{opacity:.7}
.p{display:inline-block;flex:none;font-style:normal;font-size:.72rem;line-height:1.2;padding:3px 8px;border-radius:99px;background:rgba(255,253,246,.9);box-shadow:inset 0 0 0 1px var(--line);color:var(--mut)}
.p.d{color:#2f5d3a;background:#eaf2e2;box-shadow:none}
.p.b{background:var(--yel);color:var(--on-yel);font-weight:600;box-shadow:none}
.p.so{background:var(--ink);color:var(--cream2);font-weight:600;box-shadow:none}
.sp{font-size:.8rem;letter-spacing:-.1em;flex:none}
.cl{flex:none;font-size:.72rem;color:var(--mut);margin-left:4px;font-style:normal}
.cl::after{content:"Contains"}
html[data-lang=zh] .cl::after{content:"含有"}
.p::after,.lab::after,.fc[data-diet]::after{content:var(--e)}
html[data-lang=zh] :is(.p,.lab,.fc[data-diet])::after{content:var(--z)}
.t-vegetarian{--e:"Vegetarian";--z:"素食"}.t-vegan{--e:"Vegan";--z:"純素"}.t-gluten_free{--e:"Gluten free";--z:"無麩質"}
.t-dairy_free{--e:"Dairy free";--z:"無奶"}.t-nut_free{--e:"Nut free";--z:"無堅果"}.t-halal{--e:"Halal";--z:"清真"}
.t-gluten{--e:"gluten";--z:"麩質"}.t-crustacean{--e:"crustacean";--z:"甲殼類"}.t-egg{--e:"egg";--z:"蛋"}.t-fish{--e:"fish";--z:"魚"}
.t-milk{--e:"milk";--z:"奶"}.t-peanut{--e:"peanut";--z:"花生"}.t-tree_nut{--e:"tree nuts";--z:"堅果"}.t-sesame{--e:"sesame";--z:"芝麻"}
.t-soy{--e:"soy";--z:"大豆"}.t-lupin{--e:"lupin";--z:"羽扇豆"}.t-mollusc{--e:"mollusc";--z:"軟體類"}
.t-popular{--e:"Popular";--z:"人氣"}.t-chef{--e:"Chef's pick";--z:"主廚推介"}.t-new{--e:"New";--z:"新品"}
.empty{padding:32px 0;color:var(--mut)}

/* ---------- bottom order bar ---------- */
.lbar{position:fixed;left:0;right:0;bottom:calc(var(--cpy) + var(--sab));z-index:30;padding:12px var(--gut) 10px;pointer-events:none}
/* The copyright bar: fixed under everything; the basket bar stacks on top of it. It also fills the home-indicator strip. */
.cpy{position:fixed;left:0;right:0;bottom:0;z-index:29;display:flex;align-items:center;justify-content:center;height:calc(var(--cpy) + var(--sab));padding:0 var(--gut) var(--sab);background:var(--yel);color:var(--on-yel);font-size:.85rem;font-weight:500;text-decoration:none;white-space:nowrap;overflow:hidden}
.cpy.hasrw{justify-content:space-between}
.cpl{display:flex;align-items:center;height:100%;color:inherit;text-decoration:none}
/* My rewards in the bottom bar: a dark pill with a gift, so it clearly reads as a button (Lee 20:58). */
.rwb{display:inline-flex;align-items:center;gap:6px;height:26px;margin-left:-6px;padding:0 12px 0 10px;border-radius:99px;background:var(--ink);color:var(--cream2);border:0;font:inherit;font-size:.8rem;font-weight:700;cursor:pointer;box-shadow:0 2px 6px rgba(30,24,0,.2)}
.rwb svg{width:15px;height:15px;flex:none}
.rwb #stampn{font-weight:500;font-variant-numeric:tabular-nums}
.cpl:focus-visible,.rwb:focus-visible{outline:3px solid var(--ink);outline-offset:-3px}
/* Dish page: the badges (Earns 1 stamp, Popular, Vegan, chilli, Sold out) sit over the bottom of the dish picture, not as more lines beside it. */
.hbadges{position:absolute;left:0;right:0;bottom:2%;z-index:6;display:flex;flex-wrap:wrap;justify-content:center;gap:6px;pointer-events:none}
.hbadges .p,.hbadges .ip-earn,.hbadges .sp{box-shadow:0 2px 8px rgba(60,40,0,.18)}
.hbadges .ip-earn{display:inline-flex;align-items:center;gap:4px;padding:4px 10px;border-radius:99px;background:var(--yel);color:var(--on-yel);font-size:.78rem;font-weight:600}
.hbadges .ip-earn svg{width:14px;height:14px}
.hbadges .sp{padding:2px 8px;border-radius:99px;background:var(--card-solid)}
.ip-alg{display:flex;flex-wrap:wrap;gap:6px;align-items:center;margin-top:10px}
/* The My rewards system card in the promo row: drawn in CSS (no image), the venue's reward line in big type. */
.rwcard{position:relative;overflow:hidden;width:100%;height:100%;display:block;padding:0;border:0;text-align:left;cursor:pointer;color:var(--on-yel);background:radial-gradient(90% 120% at 88% 70%,rgba(255,250,225,.75) 0%,rgba(255,250,225,0) 60%),linear-gradient(135deg,#f9d85c 0%,var(--yel) 45%,#f0b62a 100%)}
/* The text column: everything sits inside 16px+ padding and never reaches the dish's area (the right 40%). */
.rwtx{position:relative;z-index:2;display:flex;flex-direction:column;justify-content:center;gap:clamp(5px,1.6vw,8px);height:100%;box-sizing:border-box;padding:clamp(14px,4.4vw,20px) 0 clamp(14px,4.4vw,20px) clamp(16px,4.8vw,22px);width:62%}
.rwcard:not(.hashero) .rwtx{width:100%;padding-right:20px}
.rwk{display:inline-flex;align-items:center;gap:6px;font-size:.68rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;opacity:.85}
.rwk svg{width:14px;height:14px;flex:none}
.rwt{font-size:clamp(1.02rem,4.7vw,1.42rem);font-weight:750;line-height:1.12;letter-spacing:-.02em;text-wrap:balance;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
/* Stamps: one row of five per line (2 x 5 for ten), evenly spaced. Earned = solid ink with a tick; to come = a soft outline; last = the gift. */
.rwg{--s:clamp(15px,4.9vw,24px);display:grid;grid-template-columns:repeat(5,var(--s));gap:clamp(4px,1.3vw,7px);margin-top:2px}
.rwg.many{grid-template-columns:repeat(10,calc(var(--s) * .8));--s:clamp(13px,4vw,20px)}
.rwg i{display:grid;place-items:center;width:var(--s);height:var(--s);border-radius:50%;box-sizing:border-box;border:1.5px solid rgba(21,20,16,.28);background:rgba(255,253,246,.42);color:transparent;transition:background-color .3s var(--ease),border-color .3s var(--ease)}
.rwg i svg{width:62%;height:62%}
.rwg i.on{background:var(--on-yel);border-color:var(--on-yel);color:var(--yel)}
.rwg i.gift{color:rgba(21,20,16,.55);border-style:dashed}
.rwg i.gift.on{color:var(--yel);border-style:solid}
.rwp2{font-size:.74rem;font-weight:600;opacity:.8;font-variant-numeric:tabular-nums}
/* No browser balancing (older iOS): a measure that breaks the automatic line as "Buy 10 bowls, / get one free". */
@supports not (text-wrap:balance){.rwt{max-width:13.5ch}}
/* ONE hero dish, bleeding off the right and bottom edges with a soft shadow. */
.rwcard img.rwhero{position:absolute;z-index:1;inset:auto -9% -16% auto;right:-9%;bottom:-16%;top:auto;left:auto;width:50%;height:auto;max-width:none;aspect-ratio:1;object-fit:contain;border-radius:0;opacity:1;transform:none;filter:drop-shadow(0 14px 18px rgba(90,55,0,.32));pointer-events:none}
.rwcard:focus{outline:0}
.rwcard:focus-visible{outline:3px solid var(--ink);outline-offset:-4px}
.lychip{display:inline-flex;align-items:center;gap:6px;min-height:40px;margin:2px 0 10px;padding:0 16px;border-radius:99px;background:var(--yel);color:var(--on-yel);font-weight:600;font-size:.9rem}
.lychip svg{width:18px;height:18px;flex:none}
.lbar button{pointer-events:auto;display:flex;width:100%;max-width:560px;margin:0 auto;min-height:56px;align-items:center;justify-content:space-between;gap:12px;padding:0 8px 0 24px;border-radius:99px;background:var(--ink);color:var(--cream2);font-weight:600;font-size:.95rem;box-shadow:0 14px 34px rgba(30,24,0,.3);transition:transform .2s var(--ease)}
.lbar button:active{transform:scale(.98)}
.lbar .tt{display:inline-flex;align-items:center;min-height:40px;padding:0 16px;border-radius:99px;background:var(--yel);color:var(--on-yel);font-variant-numeric:tabular-nums}
.lbar button>span:first-child{min-width:0;line-height:1.25;text-align:left}
.lbar .tt{flex:none}
/* unsent-basket reminder (CONTRACT-BILL.md): the bar turns yellow with an ink outline and says "Not sent yet". Static by default, which is also
   the reduced-motion look; the pulse and shimmer are added only where motion is allowed. .hot = still unsent after ~75 s: a stronger pulse. */
.lbar button.nudge{position:relative;overflow:hidden;background:var(--yel);color:var(--on-yel);outline:3px solid var(--ink);outline-offset:2px}
.lbar button.nudge .tt{background:var(--ink);color:var(--cream2)}
@media (prefers-reduced-motion:no-preference){
  .lbar button.nudge{outline:0;box-shadow:0 14px 34px rgba(30,24,0,.3),0 0 0 3px var(--ink),0 0 0 0 rgba(244,207,58,.75);animation:nudge 2.6s ease-in-out infinite}
  .lbar button.nudge::after{content:"";position:absolute;inset:0;pointer-events:none;background:linear-gradient(105deg,transparent 38%,rgba(255,255,255,.6) 50%,transparent 62%);transform:translateX(-120%);animation:nudgesh 3.4s ease-in-out infinite}
  .lbar button.nudge.hot{animation:nudgeh 1.1s ease-in-out infinite}
  .lbar button.nudge.hot::after{animation-duration:1.7s}
  @keyframes nudge{0%,100%{box-shadow:0 14px 34px rgba(30,24,0,.3),0 0 0 3px var(--ink),0 0 0 0 rgba(244,207,58,.75)}60%{box-shadow:0 14px 34px rgba(30,24,0,.3),0 0 0 3px var(--ink),0 0 0 12px rgba(244,207,58,0)}}
  @keyframes nudgeh{0%,100%{box-shadow:0 14px 34px rgba(30,24,0,.3),0 0 0 3px var(--ink),0 0 0 0 rgba(244,207,58,.9)}50%{box-shadow:0 14px 34px rgba(30,24,0,.3),0 0 0 3px var(--ink),0 0 0 18px rgba(244,207,58,0)}}
  @keyframes nudgesh{0%,30%{transform:translateX(-120%)}70%,100%{transform:translateX(120%)}}
}
.toast{position:fixed;left:50%;bottom:calc(84px + var(--cpy) + var(--sab));z-index:60;max-width:calc(100vw - 32px);padding:10px 18px;border-radius:99px;background:var(--ink);color:var(--cream2);font-weight:600;font-size:.92rem;box-shadow:0 10px 28px rgba(30,24,0,.3);opacity:0;transform:translate(-50%,8px);transition:opacity .25s var(--ease),transform .3s var(--ease);pointer-events:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.toast.on{opacity:1;transform:translate(-50%,0)}

/* ---------- sheets (order, filters) ---------- */
dialog{color:var(--ink)}
dialog::backdrop{background:rgba(21,20,16,.4)}
.sheet{border:0;padding:0;margin:auto 0 0;margin-inline:auto;width:100%;max-width:640px;max-height:92vh;max-height:92dvh;background:var(--cream2);border-radius:24px 24px 0 0;overflow:hidden}
.sheet[open]{display:flex;flex-direction:column}
.sh{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 8px 8px 24px;flex:none}
.sh h2{font-size:1.5rem;font-weight:300;letter-spacing:-.025em;line-height:1.2}
.x{width:44px;height:44px;font-size:1.8rem;line-height:1;flex:none;border-radius:50%;color:var(--ink2)}
.sb{overflow-y:auto;overscroll-behavior:contain;padding:8px 24px 16px;flex:1 1 auto}
.sf{display:flex;gap:8px;flex-wrap:wrap;padding:12px 24px calc(12px + var(--sab));background:var(--cream2);flex:none;box-shadow:0 -1px 0 var(--line)}
.sf>*{flex:1 1 9rem}
/* My order sheet: Send on its own row, Bill / Clear all / Close side by side under it (fits a 320 px phone without stacking four buttons) */
.sf.sf3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr))}
.sf.sf3>:first-child{grid-column:1/-1}
.sf.sf3>.btn:not(:first-child){min-height:48px;padding:0 6px;font-size:.92rem}
/* the Bill page (CONTRACT-BILL.md): one block per order, a status pill, a cancelled order struck through */
.bo{padding:12px 0;border-bottom:1px solid var(--line)}
.bh{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px;margin-bottom:6px}
.bt{color:var(--mut);font-size:.85rem;font-variant-numeric:tabular-nums}
.bs{display:inline-flex;align-items:center;min-height:24px;padding:0 10px;border-radius:99px;font-size:.75rem;font-weight:700;font-style:normal;background:var(--yel3)}
.bs.done{background:var(--ink);color:var(--cream2)}
.bs.void{background:#fbe6e1;color:#7a2a1a}
.bs.ps{background:var(--card-solid);box-shadow:inset 0 0 0 1px var(--line)}
.bw{margin:0 0 6px;color:#7a2a1a;font-size:.85rem;font-weight:600}
.bq{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;padding:4px 0}
.bq>div{min-width:0;overflow-wrap:anywhere}
.bq .bn{font-weight:600;line-height:1.3}
.bq .vn{color:var(--mut);font-size:.85rem;line-height:1.35}
.bq>b{flex:none;font-variant-numeric:tabular-nums}
.bo.void .bq{text-decoration:line-through;color:var(--mut)}
.bpay{margin:12px 0;padding:14px 16px;border-radius:var(--r);box-shadow:inset 0 0 0 1.5px var(--ink)}
.bpay .calls{margin-top:10px}
.bsl{margin-top:8px;text-align:center}
.bp{display:flex;justify-content:space-between;padding:4px 0;font-weight:600}
.btn{min-height:52px;padding:0 24px;border-radius:99px;background:var(--ink);color:var(--cream2);font-weight:600;font-size:1rem;display:inline-flex;align-items:center;justify-content:center;gap:8px;text-align:center;text-decoration:none;transition:transform .2s var(--ease),opacity .2s}
.btn:active{transform:scale(.98)}
.btn.ghost{background:var(--card-solid);color:var(--ink);box-shadow:inset 0 0 0 1.5px var(--ink)}
.btn[disabled]{opacity:.45;cursor:default}
.sec{font-size:.78rem;text-transform:uppercase;letter-spacing:.08em;color:var(--mut);font-weight:600;margin:16px 0 8px}
.sec:first-child{margin-top:0}
.fr-row{display:flex;flex-wrap:wrap;gap:8px}
.fc{min-height:44px;padding:0 16px;border-radius:99px;background:var(--card-solid);box-shadow:inset 0 0 0 1px var(--line);font-size:.9rem;font-weight:500;display:inline-flex;align-items:center}
.fc[aria-pressed=true]{background:var(--ink);color:var(--cream2);box-shadow:none}
.al li label{display:flex;align-items:center;gap:12px;min-height:48px;border-bottom:1px solid var(--line);font-size:1rem}
.al input{width:24px;height:24px;accent-color:var(--ink);flex:none}
.al .lab{text-transform:capitalize}
.fine{margin-top:12px;color:var(--mut);font-size:.82rem}
.msg{margin:12px 0;padding:12px 16px;border-radius:var(--r);background:var(--yel3)}
.msg.err{background:#fbe6e1}
.scan{margin:12px 0;padding:12px 16px;border-radius:var(--r);box-shadow:inset 0 0 0 1.5px var(--ink);font-weight:600}
.calls{display:flex;flex-wrap:wrap;gap:8px;margin-top:16px}
.calls .btn{flex:1 1 7rem;min-height:48px;font-size:.92rem}
.calls .btn.sent{background:var(--line);color:var(--mut);box-shadow:none}
.done h2{font-size:1.6rem}
.done .big{font-size:2.6rem;font-weight:300;letter-spacing:-.03em;margin:4px 0}
.tbl{display:inline-flex;gap:4px;align-items:center;min-height:32px;padding:0 12px;border-radius:99px;background:var(--ink);color:var(--cream2);font-size:.85rem;font-weight:600;margin-bottom:8px}
.fld{display:block;margin-top:16px}
.fld>span{display:block;font-size:.85rem;color:var(--mut);margin-bottom:4px}
.fld input,.ln input,.fld textarea{width:100%;min-height:48px;padding:0 16px;border-radius:14px;border:0;background:var(--card-solid);box-shadow:inset 0 0 0 1px var(--line);font-size:1rem}
.fld input:focus,.ln input:focus,.fld textarea:focus{outline:0;box-shadow:inset 0 0 0 1.5px var(--ink)}
.stp{display:inline-flex;align-items:center;border-radius:99px;background:var(--card-solid);box-shadow:inset 0 0 0 1px var(--line);flex:none}
.stp button{width:44px;height:44px;font-size:1.3rem;font-weight:500;border-radius:50%}
.stp b{min-width:2rem;text-align:center;font-variant-numeric:tabular-nums}
.ln{padding:12px 0;border-bottom:1px solid var(--line)}
.ln .r1{display:flex;justify-content:space-between;gap:12px;align-items:flex-start}
.ln .nm2{font-weight:600;line-height:1.3}
.ln .sub,.ln .vn{color:var(--mut);font-size:.85rem;line-height:1.35}
.ln .up{color:var(--mut);font-size:.82rem;font-weight:400}
.ln .ls{color:var(--mut);font-size:.85rem;font-style:italic}
.ln .r2{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-top:8px}
.ln .r2 input{flex:1;min-width:0}
.ln .rm{min-height:44px;padding:0 8px;color:var(--mut);text-decoration:underline;font-size:.85rem}
.tot{display:flex;justify-content:space-between;font-size:1.2rem;font-weight:600;padding:16px 0 4px}
.surn{font-size:.82rem;color:var(--mut);padding:0 0 4px}

.full{border:0;padding:0;margin:0;width:100%;height:100%;max-width:none;max-height:none;background:#fff;color:#000}
.full[open]{display:flex;flex-direction:column}
.full .sh{border-bottom:3px solid #000;padding:8px 8px 8px 16px}
.full .sh h2{font-size:1.1rem;font-weight:700}
.full .sb{padding:0 16px}
.full .sl{height:auto;border-radius:0;overflow:visible;display:grid;grid-template-columns:auto 1fr;gap:0 16px;align-items:baseline;padding:14px 0;border-bottom:2px solid #000}
.full .q{font-size:2.6rem;font-weight:800;line-height:1;font-variant-numeric:tabular-nums}
.full .n1{font-size:2rem;font-weight:800;line-height:1.15}
.full .n2{font-size:1.6rem;font-weight:600;line-height:1.2}
.full .n3{font-size:1.3rem;font-weight:600;margin-top:4px;overflow-wrap:anywhere}
.full .onote{font-size:1.3rem;font-weight:700;margin:14px 0 0;padding:10px 12px;border:3px solid #000;overflow-wrap:anywhere}
.full .sl>div{min-width:0}
.full .sh .btn{white-space:nowrap;flex:none}
.full .sh h2{text-wrap:balance}
.full .tn{font-size:3rem;font-weight:800;padding:8px 16px;border-bottom:3px solid #000}
.full :focus-visible{outline-color:#000}
.full .btn.ghost{background:#fff;box-shadow:inset 0 0 0 2px #000}

/* =====================================================================================================
   ITEM PAGE v2 (full screen). Portrait: the dish top-left (about 62% wide, max 320px) with the name, price and tags
   beside it; steam rises from the dish and may go off the top of the screen. Below: description, option sets,
   special instructions. A sticky bar holds the qty stepper and the Add button.
   Landscape phone: two columns, the dish and steam stay in the left 40%, the rest scrolls on the right.
   ===================================================================================================== */
.ip{position:fixed;inset:0;z-index:50;background:var(--cream);overflow-y:auto;overscroll-behavior:contain;opacity:0;visibility:hidden;transform:translateY(12px) scale(.99);transition:opacity .3s var(--ease),transform .4s var(--ease),visibility 0s .4s}
.ip.open{opacity:1;visibility:visible;transform:none;transition:opacity .3s var(--ease),transform .4s var(--ease),visibility 0s}
.ip::before{content:"";position:fixed;z-index:-1;width:90vw;height:90vw;max-width:900px;max-height:900px;left:-20vw;top:-10vw;border-radius:50%;background:radial-gradient(closest-side,rgba(246,213,74,.5) 0%,rgba(250,228,138,.28) 45%,transparent 72%);pointer-events:none}
.ip:focus{outline:0}
.ip-x{position:fixed;z-index:3;top:calc(8px + env(safe-area-inset-top,0px));right:12px;width:44px;height:44px;border-radius:50%;background:rgba(255,253,246,.9);box-shadow:var(--shadow-s);display:grid;place-items:center;transition:transform .2s var(--ease)}
.ip-x:active{transform:scale(.95)}
.ip-in{display:grid;grid-template-columns:minmax(0,min(62%,320px)) minmax(0,1fr);grid-template-areas:"dish side" "body body";column-gap:12px;align-items:start;max-width:1100px;margin:0 auto;padding:clamp(64px,14svh,120px) 16px calc(128px + var(--sab)) 12px}
.hbox{grid-area:dish;position:relative;width:100%;aspect-ratio:5/4}
.hglow{position:absolute;inset:-14% -12%;border-radius:50%;background:radial-gradient(closest-side,var(--yel) 0%,var(--yel2) 42%,rgba(250,228,138,.25) 62%,transparent 74%);opacity:.7}
.hground{position:absolute;left:14%;right:14%;bottom:5%;height:8%;border-radius:50%;background:radial-gradient(closest-side,rgba(80,60,10,.32),transparent);filter:blur(3px)}
.himg{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;filter:drop-shadow(0 14px 14px rgba(110,86,10,.16))}
.himg.bad{display:none}
/* steam: a WebGL canvas with real alpha (no blend mode, so iOS draws it right) */
.fx{position:absolute;display:block;pointer-events:none;opacity:0;transition:opacity .5s var(--ease)}
.fx.on{opacity:1}
.side{grid-area:side;min-width:0;display:flex;flex-direction:column;gap:8px;padding-top:4px}
.ip-name{font-weight:300;font-size:clamp(1.35rem,6.4vw,2.1rem);letter-spacing:-.025em;line-height:1.1;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}
.ip-price{font-weight:300;font-size:1.7rem;letter-spacing:-.03em;font-variant-numeric:tabular-nums;min-width:6ch;line-height:1.2}
.ip-tags{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
.ip .p{font-size:.78rem;padding:4px 10px}
.ip-body{grid-area:body;padding-top:20px;min-width:0}
.ip-desc{color:var(--ink2);font-size:1rem;line-height:1.5}
.ip-msg{margin-top:16px;padding:12px 16px;border-radius:var(--r);background:var(--yel3);font-weight:600}
.ogrp{margin-top:24px}
.ogrp[hidden]{display:none}
.ogrp.sub{margin-top:12px;margin-left:14px;padding-left:12px;border-left:3px solid var(--yel)}
.ogrp.sub h3{font-size:.95rem}
.ogrp h3{display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:1rem;font-weight:600;margin-bottom:8px}
.req{display:inline-flex;align-items:center;min-height:22px;padding:0 8px;border-radius:99px;background:var(--yel);color:var(--on-yel);font-size:.7rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em}
.optn{font-size:.8rem;font-weight:400;color:var(--mut)}
.opts{display:flex;flex-wrap:wrap;gap:8px}
.opt{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:44px;min-width:44px;padding:0 14px;border-radius:99px;background:var(--card-solid);box-shadow:inset 0 0 0 1px var(--line);font-size:.95rem;font-weight:500;cursor:pointer;transition:background .2s,color .2s}
.opt input{position:absolute;inset:0;width:100%;height:100%;margin:0;opacity:0;cursor:pointer}
.opt em,.addon em{font-style:normal;font-size:.82rem;color:var(--mut);font-variant-numeric:tabular-nums}
.opt:has(input:checked){background:var(--ink);color:var(--cream2);box-shadow:none}
.opt:has(input:checked) em{color:var(--yel2)}
.opt:has(input:focus-visible),.addon:has(input:focus-visible){outline:3px solid var(--ink);outline-offset:2px}
.opt:has(input:disabled),.addon:has(input:disabled){opacity:.45;cursor:default}
/* add-ons: one row per choice, name + delta + a check circle */
.opts.rows{display:block;border-radius:var(--r);background:var(--card-solid);box-shadow:inset 0 0 0 1px var(--line);overflow:hidden}
.addon{position:relative;display:flex;align-items:center;gap:12px;min-height:52px;padding:0 16px;cursor:pointer;font-size:.98rem;font-weight:500}
.addon+.addon{box-shadow:0 -1px 0 var(--line)}
.addon input{position:absolute;inset:0;width:100%;height:100%;margin:0;opacity:0;cursor:pointer}
.addon .an{flex:1;min-width:0}
.ck{flex:none;display:grid;place-items:center;width:26px;height:26px;border-radius:50%;box-shadow:inset 0 0 0 1.5px var(--mut);color:transparent;transition:background .2s,color .2s,box-shadow .2s}
.addon:has(input:checked) .ck{background:var(--ink);color:var(--cream2);box-shadow:none}
.ins textarea{display:block;width:100%;min-height:76px;padding:12px 16px;border-radius:14px;border:0;background:var(--card-solid);box-shadow:inset 0 0 0 1px var(--line);font-size:1rem;line-height:1.4;resize:vertical}
.ins textarea:focus{outline:0;box-shadow:inset 0 0 0 1.5px var(--ink)}
.ins .cc{display:block;margin-top:4px;text-align:right;font-size:.78rem;color:var(--mut);font-variant-numeric:tabular-nums}
.ip-bar{position:fixed;left:0;right:0;bottom:0;z-index:2;padding:12px var(--gut) calc(12px + var(--sab));background:linear-gradient(to top,var(--cream) 62%,rgba(246,240,220,0))}
.ip-bar-in{display:flex;gap:8px;max-width:1100px;margin:0 auto;align-items:center}
.ip-bar .stp{background:var(--card-solid)}
.ip-bar .btn{flex:1;min-height:56px;justify-content:space-between;padding:0 8px 0 24px;box-shadow:0 14px 34px rgba(30,24,0,.25)}
.ip-bar .btn .tt{display:inline-flex;align-items:center;min-height:40px;padding:0 16px;border-radius:99px;background:var(--yel);color:var(--on-yel);font-variant-numeric:tabular-nums}
.ip-bar .btn[disabled]{justify-content:center;padding:0 24px}
@media (orientation:landscape) and (max-height:500px){
  .ip-in{grid-template-columns:minmax(0,40%) minmax(0,1fr);grid-template-areas:"dish side" "dish body";column-gap:16px;padding:16px 64px calc(104px + var(--sab)) 16px;max-width:none}
  .hbox{position:sticky;top:max(96px,22svh);grid-row:1 / span 2;align-self:start;max-width:min(100%,300px);margin-top:max(96px,22svh)}
  .ip-bar{left:40%;padding-left:24px;-webkit-mask-image:linear-gradient(to right,transparent,#000 28px);mask-image:linear-gradient(to right,transparent,#000 28px)}
  .ip-body{padding-top:12px}
}

/* ---------- motion helpers ---------- */
html.cmx{--dur:.65s;--ease:cubic-bezier(.3,.8,.3,1)}      /* compact in/out: slow and eased enough to see what happened */
html.noanim *,html.noanim *::before{transition:none!important;animation:none!important}
@keyframes rise{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.cat.on.enter{animation:rise .35s var(--ease)}

/* ---------- breakpoints ---------- */
@media (min-width:700px){
  :root{--gut:24px}
  .items{grid-template-columns:repeat(2,minmax(0,1fr))}
  .sheet{margin-block:auto;border-radius:24px;max-height:86dvh}
  .full .n1{font-size:2.6rem}
}
@media (min-width:1100px){
  .items{grid-template-columns:repeat(3,minmax(0,1fr))}
}
@media (min-width:900px) and (min-height:501px){
  .ip-in{grid-template-columns:minmax(0,420px) minmax(0,1fr);grid-template-areas:"dish side" "dish body";column-gap:48px;padding-top:96px}
  .hbox{grid-row:1 / span 2;max-width:420px}
}
@media (prefers-reduced-motion:no-preference){
  .sheet,.full{transition:transform .3s var(--ease),opacity .25s var(--ease),overlay .3s allow-discrete,display .3s allow-discrete}
  .sheet:not([open]),.full:not([open]){transform:translateY(24px);opacity:0}
  @starting-style{.sheet[open],.full[open]{transform:translateY(24px);opacity:0}}
}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{transition-duration:.001ms!important;transition-delay:0s!important;animation:none!important}
  .fx{display:none}
  .nmq-in{display:block;overflow:hidden;text-overflow:ellipsis}
  .nmq-in.tags{display:flex}
  .nmq.ov{-webkit-mask-image:linear-gradient(to right,#000 calc(100% - 18px),transparent);mask-image:linear-gradient(to right,#000 calc(100% - 18px),transparent)}
}

/* =====================================================================================================
   EFFECTS PACK (CONTRACT-FX.md). Video effects are canvases drawn by fx.js (real alpha, no blend modes); the rest is CSS.
   Reduced motion: none of it animates (the global rule below kills animation; fx.js is never even loaded).
   ===================================================================================================== */
.hbox{touch-action:pan-y}
.fx.back{z-index:0}
.fxglow{position:absolute;pointer-events:none;border-radius:50%}
.ip.cold::before{background:radial-gradient(closest-side,rgba(230,240,251,.3) 0%,rgba(230,240,251,.16) 45%,transparent 72%)}
.hbox.cold .hglow{background:radial-gradient(closest-side,rgba(230,240,251,.3) 0%,rgba(230,240,251,.16) 50%,transparent 74%);opacity:1}   /* cold items: a faint cool glow, not the yellow one */
.hfloat,.htilt{position:absolute;inset:0}
.hfloat{animation:hfl 5.2s ease-in-out infinite}
.htilt{will-change:transform}
@keyframes hfl{0%,100%{transform:translateY(0) rotate(-.4deg)}50%{transform:translateY(-8px) rotate(.5deg)}}
.hbox.fl .hground{animation:hgs 5.2s ease-in-out infinite}
@keyframes hgs{0%,100%{transform:scaleX(1);opacity:1}50%{transform:scaleX(.88);opacity:.65}}
/* shine sweep: a soft diagonal glint, masked by the cut-out's own alpha, every ~4s */
.shine{position:absolute;inset:0;pointer-events:none;-webkit-mask-image:var(--m);mask-image:var(--m);-webkit-mask-size:contain;mask-size:contain;-webkit-mask-position:center;mask-position:center;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  background:linear-gradient(105deg,transparent 40%,rgba(255,255,255,.85) 50%,transparent 60%) 160% 0/250% 100% no-repeat;animation:shn 4s ease-in-out infinite}
@keyframes shn{0%{background-position:160% 0}26%{background-position:-60% 0}100%{background-position:-60% 0}}
/* the bag bounces once when the flying dish lands */
.bump{animation:bump .5s cubic-bezier(.3,1.5,.5,1)}
@keyframes bump{0%{transform:scale(1)}35%{transform:scale(1.3)}65%{transform:scale(.94)}100%{transform:scale(1)}}
/* badge shimmer: one soft sweep every 6s on "Chef's pick" and "New" (list and item page) */
.p.t-chef,.p.t-new{position:relative;overflow:hidden}
.p.t-chef::before,.p.t-new::before{content:"";position:absolute;top:0;bottom:0;left:0;width:55%;background:linear-gradient(100deg,transparent,rgba(255,255,255,.7),transparent);transform:translateX(-170%) skewX(-18deg);animation:bsh 6s ease-in-out infinite;pointer-events:none}
@keyframes bsh{0%,76%{transform:translateX(-170%) skewX(-18deg)}100%{transform:translateX(330%) skewX(-18deg)}}
/* festival overlay on the promo banner: lanterns / moon (SVG) and snow (canvas), clipped to the banner and following its collapse */
.promos-in{position:relative}
.pfx{position:absolute;left:var(--gut);right:var(--gut);top:4px;height:var(--pcur);border-radius:20px;overflow:hidden;pointer-events:none;z-index:3;transition:height var(--dur) var(--ease)}
.stack.ns.is-col .pfx{height:0}
.pfx .lant{position:absolute;top:-6px;height:auto;transform-origin:50% 0;animation:sway 3.8s ease-in-out infinite alternate;filter:drop-shadow(0 3px 3px rgba(0,0,0,.28))}
@keyframes sway{from{transform:rotate(-4.5deg)}to{transform:rotate(4.5deg)}}
.pfx .moon{position:absolute;right:7%;top:16%;width:clamp(30px,10vw,44px);aspect-ratio:1;border-radius:50%;background:radial-gradient(circle at 38% 36%,#fffbe3,#f7e48f 70%,#e9cf6a);box-shadow:0 0 0 6px rgba(255,244,190,.18),0 0 26px 8px rgba(255,236,150,.45)}
.pfx .snow{position:absolute;inset:0;display:block}

/* ---------- payments (pay.js): table-mode chooser, tip chips, card form, receipt ---------- */
.pm{display:flex;flex-direction:column;align-items:flex-start;gap:4px;width:100%;min-height:72px;padding:14px 18px;margin:0 0 10px;border-radius:var(--r);background:var(--card-solid);box-shadow:inset 0 0 0 1.5px var(--ink);text-align:left}
.pm b{font-size:1.05rem}
.pm span{color:var(--mut);font-size:.9rem;line-height:1.35}
.pm:active{transform:scale(.99)}
.lockb{font-weight:600}
.bl{display:flex;justify-content:space-between;gap:12px;padding:10px 0;border-bottom:1px solid var(--line)}
.bl .vn{color:var(--mut);font-size:.85rem;line-height:1.35}
.chip2{display:inline-flex;align-items:center;min-height:22px;padding:0 8px;border-radius:99px;background:var(--yel3);font-size:.75rem;font-weight:700}
.tipr{margin-top:16px}
.tipr>span{display:block;font-size:.85rem;color:var(--mut);margin-bottom:6px}
.tipc{display:flex;gap:8px;flex-wrap:wrap}
.tipb{min-height:44px;min-width:64px;padding:0 14px;border-radius:99px;background:var(--card-solid);box-shadow:inset 0 0 0 1px var(--line);font-weight:600}
.tipb[aria-pressed=true]{background:var(--ink);color:var(--cream2);box-shadow:none}
.payel{min-height:120px;margin:16px 0}
.rcp{margin-top:12px;padding:16px;border-radius:var(--r);background:var(--card-solid);box-shadow:inset 0 0 0 1px var(--line);font-variant-numeric:tabular-nums}
.rcp .rv{display:block;font-size:1.05rem}
.rcp .vn{color:var(--mut);font-size:.82rem}
.rl{display:flex;justify-content:space-between;gap:12px;padding:6px 0;font-size:.92rem}
.rl.rt{border-top:1px solid var(--line);margin-top:6px;padding-top:10px;font-size:1.05rem}
.rcp .zh2{color:var(--mut)}
/* ---------- phase 2: "Goes well with", combo dish thumbnails ---------- */
/* Three small cards under the options: cut-out on the soft plate, name, price, and a + circle. Each is a link to that dish. */
/* U: the row is a swipeable strip (was a 3-column grid), see the U block at the end */
.ups .upl{display:flex;gap:8px}
.upc{position:relative;display:flex;flex-direction:column;align-items:center;gap:6px;min-width:0;padding:10px 6px 8px;border-radius:var(--r);background:var(--card-solid);box-shadow:inset 0 0 0 1px var(--line);text-decoration:none;color:inherit;transition:transform .2s var(--ease)}
.upc:active{transform:scale(.97)}
.upi{position:relative;display:block;width:64px;height:64px}
.upi::before{content:"";position:absolute;inset:3px;border-radius:50%;background:radial-gradient(closest-side,var(--yel2),var(--yel3) 70%,transparent)}
.upi img{position:relative;width:100%;height:100%;object-fit:contain;filter:drop-shadow(0 4px 4px rgba(110,86,10,.18))}
.upi img.bad{display:none}
.upn{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;width:100%;min-height:2.5em;text-align:center;font-size:.82rem;font-weight:600;line-height:1.25;overflow-wrap:anywhere}
.upp{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;font-size:.8rem;font-weight:700;font-variant-numeric:tabular-nums}
.upa{position:relative;flex:none;width:22px;height:22px;border-radius:50%;background:var(--ink)}
.upa::before,.upa::after{content:"";position:absolute;left:50%;top:50%;width:10px;height:2px;margin:-1px 0 0 -5px;border-radius:1px;background:var(--cream2)}
.upa::after{transform:rotate(90deg)}
.upc.un .upa{background:rgba(21,20,16,.4)}
@media (max-width:360px){.upi{width:52px;height:52px}.upn{font-size:.76rem}}
/* A combo choice that is a dish from the menu carries its picture inside the pill */
.cimg{flex:none;width:32px;height:32px;margin-left:-8px;border-radius:50%;object-fit:contain;background:radial-gradient(closest-side,var(--yel2),var(--yel3) 80%,transparent)}
span.cimg{display:block}
.opt .cimg{margin-left:-10px}
.addon .cimg{margin-left:0}
.opt.so,.addon.so{opacity:.5}
.cso{flex:none;font-size:.72rem;font-weight:600;color:var(--mut)}

/* ---------- stamp card ("Regulars", CONTRACT-LOYALTY.md) ----------
   Only exists on venues that switched it on. Circles are perfect circles in the venue's own accent (--yel); the grid is 5 across so
   a 9-stamp card is two rows of five (nine stamps + the gift circle) down to a 320px screen. Nothing here can scroll the page sideways. */
.js .mset-p.has-stp{width:auto;min-width:44px;padding:4px}
.smb{display:inline-flex;align-items:center;justify-content:center;gap:5px;flex:none;height:36px;padding:0 11px;border-radius:99px;background:var(--yel);color:var(--on-yel);font-size:.78rem;font-weight:700;white-space:nowrap;font-variant-numeric:tabular-nums;transition:transform .2s var(--ease)}
.smb:active{transform:scale(.95)}
.smb svg{flex:none;width:17px;height:17px}
.ico{display:inline-flex;flex:none}
.ico svg{width:1.1em;height:1.1em}
.ip-earn{display:flex;align-items:center;gap:6px;font-size:.88rem;font-weight:600;color:var(--yel-ink)}
.sline{font-size:1.15rem;font-weight:600;margin:0 0 4px}
.sg{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:8px;margin:14px 0 8px}
.sc{display:grid;place-items:center;aspect-ratio:1;border-radius:50%;background:var(--card-solid);box-shadow:inset 0 0 0 1.5px var(--line);color:var(--mut)}
.sc i{font-style:normal;font-size:.8rem;opacity:.65;font-variant-numeric:tabular-nums}
.sc svg{width:56%;height:56%}
.sc.on{background:var(--yel);box-shadow:none;color:var(--on-yel)}
.sc.gift{box-shadow:inset 0 0 0 2px var(--ink);color:var(--ink)}
.sc.gift.on{background:var(--ink);color:var(--yel)}
@media (prefers-reduced-motion:no-preference){
  .sc.new{animation:stmp .55s var(--ease) both}
  @keyframes stmp{0%{transform:scale(1.5);opacity:0}55%{transform:scale(.9);opacity:1}100%{transform:scale(1)}}
}
.lyact{display:flex;flex-direction:column;gap:8px;margin-top:16px}
.lylink{min-height:44px;text-decoration:underline;color:var(--ink2);font-weight:600}
.terms{display:flex;flex-direction:column;gap:12px;padding:0 0 0 1.1em;list-style:disc}
.terms li{line-height:1.45}
.lyout{margin-top:12px}
.lyin{width:100%;min-height:52px;margin-top:2px;padding:0 16px;border:0;border-radius:14px;background:var(--card-solid);box-shadow:inset 0 0 0 1px var(--line);font:600 1rem ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;letter-spacing:.04em;text-transform:uppercase}
.lyin:focus{outline:0;box-shadow:inset 0 0 0 1.5px var(--ink)}
.codebox{margin:10px 0;padding:16px 12px;border-radius:var(--r);background:var(--yel3);text-align:center;font:700 1.15rem ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;letter-spacing:.06em;overflow-wrap:anywhere;user-select:all;-webkit-user-select:all}
.lyout .btn{width:100%}
/* the bag: yellow banner, "Use my free ..." under qualifying lines, the −$x.xx line once used */
.lyb{display:flex;align-items:center;gap:10px;margin:4px 0 8px;padding:12px 16px;border-radius:var(--r);background:var(--yel);color:var(--on-yel);font-weight:600;line-height:1.3}
.lyb small{display:block;font-weight:500;font-size:.82rem;opacity:.85}
.lyb .ico svg{width:22px;height:22px}
.lyb.on{background:var(--yel3)}
.lyuse{display:inline-flex;align-items:center;justify-content:center;gap:8px;width:100%;min-height:44px;padding:0 16px;border-radius:99px;background:var(--yel3);box-shadow:inset 0 0 0 1.5px var(--yel);font-weight:600;font-size:.88rem;min-width:0}
.lyuse span:last-child{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.lyd{font-weight:600;color:var(--yel-ink)}
.lyd span{min-width:0;overflow-wrap:anywhere}
.lyundo{min-height:44px;padding:0 8px;color:var(--mut);text-decoration:underline;font-size:.85rem;font-weight:400}
.lysent{display:flex;align-items:center;gap:8px;margin:10px 0;padding:10px 14px;border-radius:var(--r);background:var(--yel3);font-weight:600}
/* after the visit (CONTRACT-REVIEW.md): an ordinary card at the bottom of the page (not a dialog, nothing blocks ordering), one step at a time.
   The two review buttons share one class and one size on purpose: neither is pushed. */
.avc{position:fixed;left:50%;transform:translateX(-50%);bottom:calc(84px + var(--cpy) + var(--sab));z-index:40;width:min(560px,calc(100vw - 2 * var(--gut)));max-height:calc(100svh - 120px);overflow:auto;padding:16px;border-radius:var(--r);background:var(--card-solid);color:var(--ink);box-shadow:0 14px 34px rgba(30,24,0,.28),inset 0 0 0 1px var(--line);display:flex;flex-direction:column;gap:12px}
.avc .avt{margin:0;padding-right:36px;font-weight:600;line-height:1.4}
.avx{position:absolute;top:2px;right:2px}
.avb{display:flex;flex-wrap:wrap;gap:10px}
.av1{flex:1 1 11rem;display:flex;flex-direction:column;gap:6px;min-width:0}
.av1 .btn,.avc>.btn{width:100%;min-height:52px;padding:0 16px;font-size:.95rem}
.avr{color:var(--yel-ink);font-weight:600;font-size:.82rem;line-height:1.3}
.avc textarea{width:100%;min-height:96px;padding:12px 16px;border:0;border-radius:14px;background:var(--card-solid);box-shadow:inset 0 0 0 1px var(--line);font:inherit;resize:vertical}
.avc textarea:focus{outline:0;box-shadow:inset 0 0 0 1.5px var(--ink)}
.avd{font-weight:600}

/* a line staff cancelled on its own: struck through on the Bill (not counted in the total) */
.bq.bx{opacity:.55;text-decoration:line-through}

/* Rewards engine (CONTRACT-REWARDS2.md): cash back and points beside the stamp card, and saving rewards with a one-time code. */
.rws{position:relative;z-index:1;font-size:.82rem;font-weight:600;opacity:.8;margin-top:-4px}
.rwbal{position:relative;z-index:1;display:inline-flex;align-items:baseline;gap:6px;font-weight:600;font-size:.95rem}
.rwbal b{font-size:clamp(1.5rem,7.5vw,2rem);font-weight:800;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.lybig{display:flex;align-items:baseline;justify-content:center;gap:8px;margin:14px 0 10px;padding:18px 12px;border-radius:var(--r);background:var(--yel);color:var(--on-yel)}
.lybig b{font-size:2.4rem;font-weight:800;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.lybig span{font-weight:600}
.ptl{display:flex;flex-direction:column;gap:8px;margin:6px 0 0;padding:0;list-style:none}
.ptr{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 12px;border-radius:14px;background:var(--card-solid);box-shadow:inset 0 0 0 1px var(--line)}
.ptn{min-width:0;display:flex;flex-direction:column;gap:2px}
.ptn b{overflow-wrap:anywhere}
.ptn small{color:var(--mut);font-variant-numeric:tabular-nums}
.ptr .lyuse{width:auto;flex:none;min-width:84px}
.lyuse.dim{opacity:.65;box-shadow:inset 0 0 0 1.5px var(--line);background:transparent}
.lyb.col{flex-direction:column;align-items:stretch}
.lyb .lybr{display:flex;align-items:center;gap:10px}
.lyb .grow{flex:1;min-width:0}
.lyb .lyuse{background:var(--on-yel);color:var(--yel);box-shadow:none}
.lyb.on .lyundo{color:var(--yel-ink)}
.lyin.plain{text-transform:none;font-family:inherit;font-weight:500;letter-spacing:0}
.lyform{display:flex;flex-direction:column;gap:10px}
.lyform .btn{width:100%}
.lystep2{display:flex;flex-direction:column;gap:10px}
.codedemo{margin:4px 0;padding:14px 12px;border-radius:var(--r);background:var(--yel3);text-align:center;font-weight:700;overflow-wrap:anywhere}
.lyhr{border:0;border-top:1px solid var(--line);margin:18px 0 12px}
.lypriv{color:var(--mut)}

/* U: ---------- "Goes well with" strip and the nested add sheet (CONTRACT-DEALS.md section U) ---------- */
/* The strip scrolls inside itself (snap, one card per step) and never widens the page: min-width:0 all the way up, max-width:100%. */
.ups{min-width:0;max-width:100%}
.ups .upl{position:relative;min-width:0;max-width:100%;overflow-x:auto;overflow-y:hidden;padding:3px 3px 6px;scroll-snap-type:x mandatory;scroll-padding-inline:3px;overscroll-behavior-x:contain;-webkit-overflow-scrolling:touch;scrollbar-width:thin;touch-action:pan-x pan-y}
.ups .upc{flex:0 0 118px;width:118px;scroll-snap-align:start;cursor:pointer}
.ups .upc:focus-visible{outline:3px solid var(--ink);outline-offset:1px}
@media (max-width:360px){.ups .upc{flex-basis:104px;width:104px}}
@media (prefers-reduced-motion:reduce){.ups .upl{scroll-behavior:auto}}
/* the nested sheet: a bottom sheet over the dish page */
#ups .upm-top{display:flex;align-items:center;gap:14px;margin-bottom:6px}
#ups .upm-top .upi{flex:none}
#ups .upm-id{display:flex;flex-direction:column;align-items:flex-start;gap:6px;min-width:0}
#ups .upm-price{font-size:1.25rem;font-weight:700;font-variant-numeric:tabular-nums}
#ups .upm-price .was{margin-right:6px;font-size:.9rem;font-weight:500;color:var(--mut)}
#ups .ogrp:first-of-type{margin-top:16px}
#ups .sf .btn{flex:1 1 12rem;min-height:56px;justify-content:space-between;padding:0 8px 0 24px}
#ups .sf .btn .tt{display:inline-flex;align-items:center;min-height:40px;padding:0 16px;border-radius:99px;background:var(--yel);color:var(--on-yel);font-variant-numeric:tabular-nums}
#ups .sf .btn[disabled]{justify-content:center;padding:0 24px}
#ups .sf .stp{flex:0 0 auto;background:var(--card-solid)}

/* D: deals (CONTRACT-DEALS): a discount line under the dishes in the bag, and the nudge that opens the category */
.dlr{display:flex;justify-content:space-between;gap:12px;align-items:flex-start;padding:10px 0;border-bottom:1px solid var(--line);color:var(--yel-ink);font-weight:600}
.dlr .dlt{min-width:0;overflow-wrap:anywhere;line-height:1.3}
.dlr .dlk{font-size:.72rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;padding:2px 8px;border-radius:99px;background:var(--yel3)}
.dlr .pr{flex:none}
.dln{display:flex;align-items:center;width:100%;min-height:44px;margin:10px 0 0;padding:8px 16px;border-radius:99px;background:var(--yel3);box-shadow:inset 0 0 0 1.5px var(--yel);font-weight:600;font-size:.9rem;line-height:1.3;text-align:left}
