/* ── Tray Theory ──────────────────────────────────────────────────────────
   One stylesheet. Mobile first: everything below is the phone layout, and the
   media queries add desktop. Half the traffic will be a manager on a phone
   between meetings, so that is the layout that gets written first, not last.
   ───────────────────────────────────────────────────────────────────────── */

:root{
  /*  DIRECTION D — "same brand, lifted", chosen 16 Aug 2026.
      The brand teal is unchanged. The only colour that moved is the dark
      panel, below, plus a bright highlight for use on it. */
  --teal:#0E7480; --teal-d:#0A4D56; --teal-l:#149AA9; --teal-wash:#E8F3F4;
  /*  The highlight used ON the dark panel. Bright enough to survive a phone
      at half brightness in daylight, which --teal-l is not. */
  --teal-bright:#5FD0DA;

  --ink:#16201F; --ink-2:#495755;
  /*  Was #7C8886, which is 3.67:1 on white — below the 4.5:1 minimum. That
      matters more than it sounds: --ink-3 is the colour of the dish
      description on every menu card, so the text telling somebody what is
      actually in the tray was the least readable text on the page, on a
      phone, in daylight. #606C6A is the same grey-green, darker, and clears
      4.5:1 on all three backgrounds it actually lands on — white cards, the
      cream page, and the cream-2 "build your own" card. Checked, not
      eyeballed; the cream-2 case fails at anything lighter. */
  --ink-3:#606C6A;

  /*  --panel is the dark background. It used to be --ink, i.e. the same
      #16201F used for body text — which is the whole bug. A colour chosen to
      be readable *as text on cream* is 8% lightness with a trace of green in
      it, and a phone screen crushes that trace out, so the hero read as flat
      black. Text colour and panel colour are two different jobs and now have
      two different variables. #123A3E is a real deep teal: dark enough for
      white text at AAA, light enough to still be a colour. */
  --panel:#123A3E; --panel-2:#0D2C2F;
  --on-panel:#F3F0E9; --on-panel-2:rgba(243,240,233,.74);
  --on-panel-3:rgba(243,240,233,.58); --on-panel-line:rgba(243,240,233,.18);

  --cream:#FAF7F1; --cream-2:#F2EDE3; --line:#E2DED4;
  --white:#fff; --amber:#B8862F; --green:#1B7A3D; --red:#A02B25;
  --r:14px; --r-lg:22px;
  --shadow:0 1px 2px rgba(22,32,31,.05), 0 8px 24px -12px rgba(22,32,31,.16);
  --shadow-lg:0 2px 6px rgba(22,32,31,.06), 0 28px 60px -28px rgba(22,32,31,.30);
  --nav-h:64px;
  /*  The closed banner's measured height — 0 whenever the kitchen is open —
      and the total depth of everything pinned to the top of the window.
      Anything that sticks below the header offsets by --stick-h, so pinning
      the banner could not leave a filter strip or a summary panel hiding
      underneath it. paintService writes --svcbar-h. */
  --svcbar-h:0px;
  --stick-h:calc(var(--nav-h) + var(--svcbar-h));
  /*  The smallest thing anybody should have to hit with a thumb. Apple and
      Google both land on 44px; several controls here were 34–36. */
  --tap:44px;
}

*{margin:0;padding:0;box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  font-family:'Inter',system-ui,-apple-system,sans-serif;
  background:var(--cream); color:var(--ink);
  line-height:1.6; -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
img{max-width:100%;display:block}
button,input,select,textarea{font:inherit;color:inherit}
button{cursor:pointer;background:none;border:0}
a{color:inherit;text-decoration:none}
h1,h2,h3,h4{font-family:'Plus Jakarta Sans','Inter',sans-serif;font-weight:800;letter-spacing:-.02em;line-height:1.15}
h1{font-size:clamp(2rem,7vw,3.6rem)}
h2{font-size:clamp(1.5rem,4.4vw,2.4rem)}
h3{font-size:1.12rem;font-weight:700;letter-spacing:-.01em}
.wrap{max-width:1180px;margin:0 auto;padding:0 20px}
.eyebrow{font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.18em;color:var(--teal)}
/*  The brand teal is chosen to sit on cream. On the dark panel it is 2.25:1 —
    effectively invisible, which is what "A FLENBO FOODWORKS BRAND" was doing
    at the top of the home page. Same on the solid-teal strip. */
.hero .eyebrow,.stripdark .eyebrow{color:var(--teal-bright)}
.stripteal .eyebrow{color:rgba(255,255,255,.82)}
.muted{color:var(--ink-2)}
.small{font-size:.84rem}
.tiny{font-size:.75rem}
.center{text-align:center}
.hide{display:none !important}
.nowrap{white-space:nowrap}

/* ── buttons ─────────────────────────────────────────────────────────── */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  border-radius:999px;padding:13px 24px;font-size:.86rem;font-weight:600;
  transition:.2s ease;border:1.5px solid transparent;white-space:nowrap;
}
.btn-primary{background:var(--teal);color:#fff}
.btn-primary:hover{background:var(--teal-d)}
.btn-dark{background:var(--panel);color:var(--on-panel)}
.btn-dark:hover{background:var(--teal)}
.btn-ghost{border-color:var(--line);background:var(--white);color:var(--ink)}
.btn-ghost:hover{border-color:var(--teal);color:var(--teal)}
.btn-light{background:rgba(255,255,255,.14);color:#fff;border-color:rgba(255,255,255,.34)}
.btn-light:hover{background:#fff;color:var(--ink)}
/*  Even a "small" button is still something a thumb has to hit. The padding
    stays small so it reads as secondary; min-height does the accessibility
    work without making it look like a primary action. */
.btn-sm{padding:9px 16px;font-size:.79rem;min-height:38px}
@media(max-width:699px){.btn-sm{min-height:var(--tap);padding:10px 18px}}
.btn-block{width:100%}
.btn:disabled{opacity:.45;cursor:not-allowed}
.btn-link{color:var(--teal);font-weight:600;font-size:.86rem;text-decoration:underline;text-underline-offset:3px}

/* ── header ──────────────────────────────────────────────────────────── */
header.nav{
  /*  Not top:0 — the closed banner is pinned above it and sets --svcbar-h to
      its own measured height, which is 0 whenever the kitchen is open. */
  position:sticky;top:var(--svcbar-h);z-index:60;height:var(--nav-h);
  background:rgba(250,247,241,.92);backdrop-filter:blur(14px);
  border-bottom:1px solid var(--line);
}
.nav .wrap{display:flex;align-items:center;gap:14px;height:100%}
.nav .brand{display:flex;align-items:center;gap:10px;flex:0 0 auto}
.nav .brand img{height:38px;width:auto}
.nav .links{display:none}
.nav .spacer{flex:1}
.nav .icons{display:flex;align-items:center;gap:6px}
.iconbtn{
  position:relative;width:var(--tap);height:var(--tap);border-radius:50%;
  display:grid;place-items:center;transition:.2s;
}
.iconbtn:hover{background:var(--cream-2)}
.iconbtn svg{width:21px;height:21px;stroke:var(--ink);fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.cartcount{
  position:absolute;top:2px;right:1px;min-width:19px;height:19px;padding:0 5px;
  border-radius:999px;background:var(--teal);color:#fff;
  font-size:.66rem;font-weight:700;display:grid;place-items:center;line-height:1;
}
.avatar{width:32px;height:32px;border-radius:50%;object-fit:cover;background:var(--teal-wash)}
.burger{display:grid}

/* ── account dropdown ────────────────────────────────────────────────
   Sign out has to be reachable from every page, not only from inside the
   account page. Somebody on a shared laptop who wants out should not have to
   navigate somewhere first to find the door. */
.usermenu{position:relative}
.umenu{
  position:absolute;top:calc(100% + 10px);right:0;z-index:70;
  min-width:232px;max-width:calc(100vw - 32px);
  background:var(--cream);border:1px solid var(--line);
  border-radius:16px;box-shadow:var(--shadow);overflow:hidden;
  transform-origin:top right;animation:umenuIn .14s ease-out;
}
@keyframes umenuIn{from{opacity:0;transform:scale(.96) translateY(-4px)}to{opacity:1;transform:none}}
.umenu .uhead{
  display:flex;align-items:center;gap:11px;padding:14px 16px;
  border-bottom:1px solid var(--line);background:var(--cream-2);
}
.umenu .uhead .who{min-width:0}
.umenu .uhead b{display:block;font-size:.9rem;line-height:1.3}
/* min-width:0 above plus these three: a long Google display name or a work
   email must ellipsize, not stretch the panel off the side of the screen. */
.umenu .uhead span{
  display:block;font-size:.76rem;color:var(--ink-3);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.umenu a,.umenu button{
  display:flex;align-items:center;gap:10px;width:100%;
  padding:12px 16px;font-size:.9rem;font-weight:500;color:var(--ink);
  text-align:left;border-bottom:1px solid var(--line);transition:.15s;
}
.umenu a:hover,.umenu button:hover{background:var(--cream-2);color:var(--teal)}
.umenu .usep{border-bottom:0}
.umenu .uout{color:#a3341f;font-weight:600}
.umenu .uout:hover{background:#fdeeea;color:#8c2b1a}
.umenu .uadmin{color:var(--teal);font-weight:600}
@media(min-width:900px){
  :root{--nav-h:74px}
  .nav .links{display:flex;align-items:center;gap:26px;margin-left:26px}
  /*  A LINE UNDER THE LINK, ON HOVER AND ON THE PAGE YOU ARE ON.

      Colour was doing this alone, and colour alone gives no feedback until
      the pointer is already on the word — Bites & Barrel draws a rule that
      wipes in under whatever the cursor is over, and the client asked for the
      same: "the line appears just on hovering even before clicking in bites
      and barrel. same does not happen with tray theory."

      Their geometry: 2px, full width of the link, scaled from zero over .18s,
      so it grows out of the centre rather than appearing. In teal here, and
      it also stays put on the current page — the same job the tab bar's line
      does on a phone, so the two ends of the site agree about what "you are
      here" looks like. */
  .nav .links a{font-size:.87rem;font-weight:500;color:var(--ink-2);transition:color .18s ease;
    position:relative;padding:6px 0}
  .nav .links a:hover,.nav .links a.on{color:var(--teal)}
  .nav .links a::after{
    content:"";position:absolute;left:0;right:0;bottom:0;height:2px;
    background:var(--teal);border-radius:2px;transform:scaleX(0);
    transition:transform .18s ease;
  }
  .nav .links a:hover::after,.nav .links a:focus-visible::after,
  .nav .links a.on::after{transform:scaleX(1)}
  @media(prefers-reduced-motion:reduce){.nav .links a::after{transition:none}}
  .burger{display:none}
}
.drawer{
  position:fixed;inset:var(--stick-h) 0 auto 0;z-index:59;background:var(--cream);
  border-bottom:1px solid var(--line);box-shadow:var(--shadow);
  transform:translateY(-140%);transition:transform .3s cubic-bezier(.22,1,.36,1);
}
.drawer.open{transform:none}
.drawer a{display:block;padding:15px 20px;font-weight:600;border-bottom:1px solid var(--line)}
.drawer a:last-child{border:0}

/* ── generic sections ────────────────────────────────────────────────── */
/*  56px between every section is a lot of scrolling on a 6-inch screen when
    there are nine of them. */
section{padding:40px 0}
@media(min-width:700px){section{padding:60px 0}}
@media(min-width:900px){section{padding:84px 0}}
.sechead{max-width:56ch;margin-bottom:30px}
@media(max-width:699px){.sechead{margin-bottom:20px}}
.sechead p{color:var(--ink-2);margin-top:12px}
.grid{display:grid;gap:14px}
@media(min-width:700px){.grid{gap:16px}}
@media(min-width:700px){.g2{grid-template-columns:repeat(2,1fr)}}
@media(min-width:700px){.g3{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1000px){.g3{grid-template-columns:repeat(3,1fr)}}
@media(min-width:640px){.g4{grid-template-columns:repeat(2,1fr)}}
@media(min-width:1000px){.g4{grid-template-columns:repeat(4,1fr)}}

/* ── image placeholders ──────────────────────────────────────────────────
   Deliberately visible. A grey box that says what belongs in it is far more
   useful than a stock photo nobody remembers to replace.

   But a dashed border reads as "broken image" to a customer, not as "photo
   coming". A soft tint with a faint diagonal weave reads as an intentional
   surface — still obviously not a photograph, but not an error either. That
   matters right now, because the site is live with 43 empty slots. */
.imgslot{
  position:relative;border-radius:var(--r);
  background:
    repeating-linear-gradient(135deg,
      rgba(14,116,128,.045) 0 2px, transparent 2px 9px),
    var(--cream-2);
  border:1px solid var(--line);
  display:grid;place-items:center;text-align:center;padding:18px;overflow:hidden;
}
.imgslot::before{
  content:attr(data-label);
  font-size:.66rem;font-weight:600;letter-spacing:.09em;text-transform:uppercase;
  color:#A79C87;max-width:24ch;line-height:1.55;
}
@media(max-width:699px){
  /* Shorter on a phone: an empty box should not push the actual dish name
     and price below the fold. A 4:3 slot at full phone width is ~293px tall,
     which is most of the screen spent on a box with no photograph in it. */
  .imgslot{padding:12px}
  .imgslot::before{font-size:.6rem;max-width:20ch}
  .traycard .imgslot.r43{aspect-ratio:16/10}
  /*  …but only while the slot is EMPTY.

      21 August. Client: "the three images on home page appear bigger than the
      menu card items. i want the menu card items to also look similar."
      Measured before changing anything: on a 600px viewport the home format
      card painted its tray at 557x418 and a menu card painted the same kind of
      photograph at 466x348 — thirty per cent less area. Identical on desktop,
      which is why it reads as a phone-only complaint.

      The cause is this very rule. A 16/10 box was right when these slots held
      a placeholder — a 4:3 box at full phone width is most of a screen spent on
      an empty rectangle. But the photographs are 4:3, so `object-fit:contain`
      letterboxes them inside the shorter box and paints them narrower than the
      box they sit in.

      So the short box stays for a card with no photograph yet, and a card that
      HAS one gets the ratio its picture actually is. Nothing is cropped, and
      the two card types match. */
  .traycard .imgslot.r43:has(img){aspect-ratio:4/3}
  /*  A 1:1 or 4:3 slot at full phone width is 350–390px tall — a whole
      screenful of EMPTY box on the corporate and sustainability sections.
      Capping the height rather than changing the ratio means the eventual
      photographs are not cropped differently on phone and desktop; the box
      just stops growing. */
  .imgslot{max-height:300px}

  /*  …but only while it is empty.

      That cap was written when every slot was a placeholder. Now that the
      photographs exist it does real damage, because a box with a fixed
      aspect-ratio and a capped height shrinks in BOTH directions: the slot
      stopped at 400×300 while the card carried on to the full column width,
      and every card between about 430px and 700px wide — a large phone in
      landscape, a small tablet, a desktop window dragged narrow — showed the
      tray photograph on the left with 80 to 200px of bare white card beside
      it. Reported from a real screenshot, 17 Aug 2026.

      Once there IS a photograph the cap has nothing left to protect against:
      a picture of the food is the reason the section exists, and it should
      fill the card. Written as an override rather than :not(:has(img)) so
      that a browser without :has() simply keeps the old capped behaviour
      instead of dropping the rule and losing the cap on empty slots too. */
  .imgslot:has(img){max-height:none}
}
.imgslot.dark{
  background:
    repeating-linear-gradient(135deg, rgba(255,255,255,.05) 0 2px, transparent 2px 9px),
    rgba(255,255,255,.05);
  border-color:var(--on-panel-line);
}
.imgslot.dark::before{color:rgba(255,255,255,.5)}

/*  Once there is a real photograph in the slot, the frame is just a line drawn
    round a picture. The border and the woven fill exist to say "a photograph
    belongs here"; the moment one does, they should get out of the way — on the
    dark hero especially, where the faint rule was the only thing making the
    photo read as a box pasted onto the page.

    :has() rather than a class toggled in script, because the <img> deletes
    itself when the file is missing (onerror), so this follows it automatically.
    Both selectors are needed: `.imgslot.dark` is two classes and outranks
    `.imgslot:has(img)`, whose specificity counts as one class plus an element.
    Browsers without :has() keep the border, which is the old behaviour. */
.imgslot:has(img),.imgslot.dark:has(img){border-color:transparent}

/*  A slot may never be wider than what contains it.

    Belt and braces behind the min-height rule below, and the cheapest possible
    insurance: every one of these boxes sits inside something with a known
    width, and there is no arrangement in which growing past it is the right
    answer. The hero clips overflow, so when it happened the photograph simply
    ran off the right of the screen with nothing to say why. */
.imgslot{max-width:100%}
.imgslot.r43{aspect-ratio:4/3}
.imgslot.r11{aspect-ratio:1/1}
.imgslot.r169{aspect-ratio:16/9}
.imgslot.r32{aspect-ratio:3/2}

/* ── hero ────────────────────────────────────────────────────────────── */
.hero{background:var(--panel);color:var(--on-panel);padding:44px 0 52px;position:relative;overflow:hidden}
.hero::after{
  content:"";position:absolute;inset:auto -20% -40% -20%;height:70%;
  background:radial-gradient(ellipse at 50% 100%,rgba(95,208,218,.20),transparent 65%);
  pointer-events:none;
}
.hero .wrap{position:relative;z-index:1}
.hero h1{color:#fff;margin:14px 0 0}
.hero h1 em{font-style:normal;color:var(--teal-bright)}
.hero p.lede{color:var(--on-panel-2);margin-top:18px;max-width:48ch;font-size:1.03rem}

/*  The scale line — "5,500 meals a day" under both hero headlines.

    A left rule and two type sizes, not a pill and not a badge. A buyer reads
    a boxed-off number as a marketing claim and a stated one as a fact, and
    this one is a fact worth being believed. The rule is the bright teal so it
    picks up the eyebrow above it and ties the block together.

    clamp() on the number because it sits under a headline that is itself
    fluid: at 360px a fixed 1.6rem pushed "5,500 meals a day" onto two lines
    with "a day" alone on the second, which reads as a typo. */
.hero .proofline{
  margin:22px 0 0;padding:2px 0 2px 16px;border-left:3px solid var(--teal-bright);
  max-width:44ch;
}
.hero .proofline b{
  display:block;font-family:'Plus Jakarta Sans',sans-serif;font-weight:800;color:#fff;
  font-size:clamp(1.25rem,3.2vw,1.6rem);letter-spacing:-.02em;line-height:1.15;
}
.hero .proofline span{
  display:block;margin-top:5px;font-size:.86rem;line-height:1.45;color:var(--on-panel-2);
}
/*  On the photo hero the line sits over a photograph rather than the flat
    panel, so it needs a touch more contrast than --on-panel-2 gives. */
.photohero.hasphoto .proofline span{color:rgba(255,255,255,.88)}
.photohero .proofline{margin-top:18px}
/*  The pre-JavaScript hero silhouette. Same background as the real hero, so
    the swap is invisible apart from text appearing. */
.hero.boot{min-height:58vh}
.hero.boot .bl{
  display:block;border-radius:8px;
  /*  Visible enough to read as "loading", not as "empty". At 9% it looked like
      a blank dark band, which is the thing the skeleton exists to avoid. */
  background:linear-gradient(90deg,
    rgba(243,240,233,.14) 0%, rgba(243,240,233,.30) 50%, rgba(243,240,233,.14) 100%);
  background-size:220% 100%;
  animation:bootshine 1.5s ease-in-out infinite;
}
.hero.boot .bootbtns{display:flex;gap:12px;margin-top:30px}
@keyframes bootshine{0%{background-position:120% 0}100%{background-position:-120% 0}}
/*  Somebody who has asked not to see motion gets a plain block. */
@media(prefers-reduced-motion:reduce){.hero.boot .bl{animation:none}}

.hero .actions{display:flex;gap:12px;margin-top:28px;flex-wrap:wrap}
/*  The brand teal against the panel is 2.25:1 — below the 3:1 WCAG asks for
    the *boundary* of a control, so the main call to action was dissolving
    into its own background. A hairline ring defines the edge without
    repainting the button a colour that is not the brand's. */
.hero .btn-primary{border-color:rgba(255,255,255,.30)}
.hero .btn-primary:hover{background:var(--teal-l);border-color:rgba(255,255,255,.5)}
.hero .heroimg{margin-top:36px}
.hero .heroimg .imgslot{min-height:230px}

/*  …but only while the slot is empty. Reported from a phone, 19 August 2026:
    "the image is cut on extreme right. its not aligned."

    A min-height and an aspect-ratio on the same box are a contradiction the
    moment the box is narrow, and the browser resolves it by growing the WIDTH.
    On a 390px phone the column is 350px, 16:10 of that is 219px tall, the
    230px minimum wins — and the width is then re-derived from the definite
    height as 230 x 1.6 = 368px. Eighteen pixels wider than its parent, clipped
    by .hero{overflow:hidden}, and the right-hand compartment of the tray
    disappears off the edge of the screen. Below 408px of viewport it is
    arithmetic, not chance: every phone narrower than that showed it.

    The same contradiction was quietly costing the desktop too. min-height:400
    at 900px and up forced the slot to 533px against a grid column of 382–514,
    so the photograph pushed the headline column narrower on every window below
    about 1300px — no clipping, so nobody reported it, but the two-column
    balance was never the one that was designed.

    Both minimums exist for one reason: an empty placeholder should not
    collapse to a sliver. A photograph makes that impossible, so once there is
    one the minimum has nothing left to defend and the ratio can simply win.
    Written the same way as the max-height rule above — as an override rather
    than :not(:has(img)) — so a browser without :has() keeps today's behaviour
    rather than losing the floor on empty slots as well. */
.hero .heroimg .imgslot:has(img){min-height:0}
/*  The three facts and the four certifications: a full-width teal band,
    butted straight up against the bottom of the dark hero.

    It was a rounded teal card floating inside the hero for a while, which read
    as a widget dropped onto the panel rather than part of the page. Two bands
    edge to edge — deep panel, then brand teal — say the same thing without the
    card. No rounding, no margin, no gap: the join is the point.

    Inside, four cells of equal width divided by hairlines. Before, it was a
    flex row of three statistics that stopped two thirds of the way across and
    left a third of the band empty, then a rule, then the certifications on a
    second line: two half-filled rows. One row of four fills the width, gives
    the marks a column of their own rather than a footnote, and reads as a
    single set of facts — which is what it is. */
.statband{background:var(--teal);padding:0}
.statgrid{display:grid;gap:1px;background:rgba(255,255,255,.20)}
/*  Two columns between 640 and 1000, and the certifications cell spans them
    both. Without the span it would sit in half the band with an empty cell
    beside it — and an empty cell is not blank, it is the grid's own hairline
    background showing through as a pale teal rectangle.

    The second rule is the one that keeps this true as facts are added. The
    facts before the certifications cell are laid two to a row, so an ODD
    number of them leaves exactly that pale rectangle in the last row. It
    appeared the moment "Pre Order" made three of them on 21 August. Rather
    than hand-span the third cell — which would break again at the fourth — the
    LAST fact stretches across the row whenever it would otherwise be left on
    its own. Add a fifth fact and it stops stretching by itself. */
@media(min-width:640px){
  .statgrid{grid-template-columns:repeat(2,1fr)}
  .certcell{grid-column:1 / -1}
  .statcell:nth-last-child(2):nth-child(odd){grid-column:1 / -1}
}
@media(min-width:1000px){
  /*  Four cells now, not three — "Pre Order" joined them on 21 August. The
      certifications cell still takes the extra width because it carries four
      marks and a button, and the three one-line facts do not need it. */
  .statgrid{grid-template-columns:1fr 1fr 1fr 1.9fr}
  .certcell{grid-column:auto}
  .statcell:nth-last-child(2):nth-child(odd){grid-column:auto}
}
/*  Centred, at the client's request. It also suits three cells of unequal
    width better than left alignment did: ragged left edges at three different
    x-positions are more noticeable than a centred set. */
.statcell{background:var(--teal);padding:22px 24px 24px;text-align:center}

.statcell b{
  display:block;font-family:'Plus Jakarta Sans',sans-serif;font-size:1.5rem;font-weight:800;
  color:#fff;letter-spacing:-.025em;line-height:1.1;
}
.statcell > span{display:block;margin-top:6px;font-size:.8rem;color:rgba(255,255,255,.72)}

/*  The certifications cell. Set exactly like the two beside it: the same
    headline, the same small print underneath. It was a small-caps label over a
    two-column grid of icons, which made it the odd one out in a row of three
    and read as a badge rail rather than a fact about the food.

    Wider than the others because the sentence is longer, and that is the only
    difference between them. */
.certcell b{font-size:1.5rem}
.certcell > span i{font-style:normal;opacity:.5;margin:0 7px}
/*  A supplied logo sits inline in that line of small print, sized to it and
    capped so a wide wordmark cannot push the marks onto another row. */
.certcell .certlogo{
  height:19px;width:auto;max-width:56px;object-fit:contain;
  vertical-align:-5px;margin:0 1px;
}
/*  A pill, like "See the menu" in the hero above it, rather than the
    underlined text link it was. Same shape and the same weight; white on the
    teal instead of teal on the panel, because a teal button on a teal band is
    a rectangle of nothing. Compact, because it is a footnote to the band and
    not the thing the band is for. */
.btn-cert{
  margin-top:14px;background:#fff;color:var(--teal-d);
  padding:9px 20px;font-size:.82rem;
}
.btn-cert:hover{background:var(--cream);color:var(--teal-d)}

/*  A copy column beside a picture column, both full height.

    The pattern the dark sections wanted and did not have. A heading spanning
    half the page, followed by a two-up grid centred against itself, leaves a
    void in one corner and floats the small print in the middle of another.
    Everything readable stacks in one column; the photograph fills the other. */
.splitrow{display:grid;gap:26px}
@media(min-width:900px){
  .splitrow{grid-template-columns:1fr 1fr;gap:56px;align-items:center}
}
.splitcopy .lede{color:var(--on-panel-2);margin-top:14px;max-width:52ch}
.splitcopy .specs{margin-top:24px}
.splitimg .imgslot{height:100%}

/*  On a phone the home hero is copy → photograph → buttons.

    It used to be copy → buttons → three statistics → photograph, because the
    buttons and the stats lived inside the text block and the picture came
    after all of it. The tray ended up most of a screen below the fold on the
    one page where showing the product early matters most.

    The markup is separate siblings now, so the stacked order is simply source
    order. `.heroactions` carries the top margin the buttons used to. */
.hero .heroactions .actions{margin-top:30px}

/*  And a shallower crop for the photograph below the two-column breakpoint.

    Moving it above the buttons costs the buttons their place on the first
    screen: at 4:3 and full phone width the tray is 263px tall on a 390px phone
    and 450px on a 640px one, which put "See the menu" at or below the fold.
    16:10 takes 44px back on a phone and 75px on a small tablet.

    Safe to crop rather than letterbox because the hero slot is `cover`, and
    the hero photograph is a centred tray with generous margin above and below
    — the 10% trimmed off each edge is backdrop. Checked, not assumed. */
@media(max-width:899px){
  .hero .heroimg .imgslot.r43{aspect-ratio:16/10}
}

@media(min-width:900px){
  .hero{padding:76px 0 88px}
  /*  And on a desktop it is unchanged: copy above actions in the left column,
      the photograph spanning both rows on the right and vertically centred.
      Explicit placement rather than auto-flow, because three children in a
      two-column grid would otherwise wrap the actions under the photograph.

      Other heroes — corporate, food safety — still have two children and no
      .herocopy, so they auto-place into column 1 and land exactly as before. */
  .hero .wrap{display:grid;grid-template-columns:1.05fr .95fr;gap:0 56px;align-items:center}
  .hero.photohero .wrap{display:block}
  .hero .herocopy{grid-column:1;grid-row:1;align-self:end}
  .hero .heroactions{grid-column:1;grid-row:2;align-self:start}
  .hero .heroimg{grid-column:2;grid-row:1 / span 2;align-self:center;margin:0}
  .hero .heroimg .imgslot{min-height:400px}
}

/* ── cards ───────────────────────────────────────────────────────────── */
.card{background:var(--white);border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;transition:.22s}
.card:hover{box-shadow:var(--shadow-lg);transform:translateY(-2px)}
.card .body{padding:18px}
@media(max-width:699px){.card .body{padding:15px 15px 16px}}
.card .imgslot{border:0;border-radius:0;border-bottom:1px solid var(--line)}

.pill{
  display:inline-flex;align-items:center;gap:5px;border-radius:999px;
  padding:4px 10px;font-size:.68rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
}
.pill.veg{background:#E9F5EC;color:var(--green)}
.pill.egg{background:#FBF1DD;color:var(--amber)}
.pill.nonveg,.pill\.non-veg{background:#FBE9E8;color:var(--red)}
.pill.tray{background:var(--teal-wash);color:var(--teal-d)}
.pill.ghost{background:var(--cream-2);color:var(--ink-2)}
.dietdot{width:13px;height:13px;border:1.6px solid currentColor;border-radius:2px;display:inline-grid;place-items:center;flex:0 0 auto}
.dietdot::after{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}

/* ── menu page ───────────────────────────────────────────────────────── */
.filters{
  display:flex;gap:8px;overflow-x:auto;padding:4px 0 14px;
  scrollbar-width:none;-webkit-overflow-scrolling:touch;
}
.filters::-webkit-scrollbar{display:none}
.chip{
  border:1.5px solid var(--line);background:var(--white);border-radius:999px;
  padding:0 16px;min-height:var(--tap);display:inline-flex;align-items:center;
  font-size:.84rem;font-weight:600;color:var(--ink-2);
  white-space:nowrap;transition:.18s;
}
.chip.on{background:var(--panel);border-color:var(--panel);color:var(--on-panel)}
/*  A sticky bar with no edge looks like the cards are sliding under nothing.
    The fade plus a hairline gives it somewhere to end. */
.stickybar{
  position:sticky;top:var(--stick-h);z-index:40;padding-top:6px;
  background:var(--cream);
  box-shadow:0 1px 0 var(--line), 0 10px 12px -10px rgba(22,32,31,.14);
}
.stickybar .filters{padding:0 0 6px;gap:7px;overflow:visible}

/* ── the two pickers ──────────────────────────────────────────────────────
   Two rows of chips, both pinned, cost 138px: thirty per cent of an iPhone
   and forty per cent of a small Android, permanently, for a choice made once.
   Fifty pixels now, and no "TRAY" / "DIET" labels above them — "3CP · Indian"
   could not be a diet and "Everything" could not be a tray.
   ───────────────────────────────────────────────────────────────────────── */
.pk{
  flex:1;min-width:0;display:flex;align-items:center;gap:8px;
  padding:7px 12px;border-radius:9px;border:1.5px solid var(--line);background:var(--white);
  color:var(--ink);font-weight:700;font-size:.85rem;white-space:nowrap;overflow:hidden;
  transition:.15s;
}
/*  Not an even split. "8CP · Executive" is fifteen characters and "Everything"
    is ten, so an even split truncates the tray at 360px — where the words
    matter most, because there is no room for anything else to explain them. */
.pk[data-pick="format"]{flex:1.45}
.pk:hover{border-color:var(--teal)}
.pk:focus-visible{outline:2px solid var(--teal);outline-offset:2px}
.pklbl{overflow:hidden;text-overflow:ellipsis}
.pkch{width:13px;height:13px;flex:none;margin-left:auto;fill:none;stroke:var(--ink-3);
      stroke-width:2.6;stroke-linecap:round;stroke-linejoin:round;opacity:.7}
.pktray{width:14px;height:14px;flex:none;fill:none;stroke:var(--teal-d);stroke-width:1.9;stroke-linejoin:round}
/*  Below 380px "8CP · Executive" and the glyph cannot both fit, and the words
    are the information — the glyph is only saying "this is the tray", which
    the words already say. So the glyph goes and nothing is truncated. */
@media(max-width:379px){.pktray{display:none}.pk{gap:6px;padding:7px 10px}}
/*  And on the last of the 320px phones, a size down as well. "8CP · Executive"
    and "Everything" both fit in 292px of usable width at .79rem and not at
    .85rem, and a truncated filter is worse than a small one. */
@media(max-width:349px){.pk{font-size:.79rem;padding:7px 9px;gap:5px}.pkch{width:12px;height:12px}}
/*  Present only when a diet is actually chosen. See dietMark(). */
.pkdot{width:9px;height:9px;border-radius:2px;flex:none;display:block}

.pksheet{max-width:420px}
.pksheet .pkh{font-size:1.05rem;margin-bottom:14px;padding-right:40px}
.pklist{display:grid;gap:6px}
.pkopt{
  display:flex;align-items:center;gap:11px;width:100%;text-align:left;
  padding:12px 13px;border-radius:11px;border:1.5px solid transparent;background:var(--cream-2);
  font-size:.92rem;font-weight:600;color:var(--ink);transition:.15s;
}
.pkopt:hover{background:var(--white);border-color:var(--line)}
.pkopt.on{background:var(--white);border-color:var(--teal)}
.pkopt .radio{width:19px;height:19px;border:2px solid #CFC9BC;border-radius:50%;flex:none;
              display:grid;place-items:center;transition:.15s}
.pkopt.on .radio{border-color:var(--teal)}
.pkopt.on .radio::after{content:"";width:9px;height:9px;border-radius:50%;background:var(--teal)}
.pkname{flex:1;min-width:0}
/*  How many cards the choice leaves, so nobody taps into an empty menu to
    find out there is nothing there. */
.pkn{font-size:.76rem;font-weight:700;color:var(--ink-3);background:var(--cream-2);
     border-radius:6px;padding:2px 8px;flex:none}
.pkopt.on .pkn{background:var(--teal-wash);color:var(--teal-d)}

.traycard .body{display:flex;flex-direction:column;gap:10px}
.traycard .top{display:flex;justify-content:space-between;gap:10px;align-items:flex-start}
.traycard h3{flex:1}
.traycard .price{font-family:'Plus Jakarta Sans',sans-serif;font-weight:800;font-size:1.2rem;white-space:nowrap;min-width:0}
/*  Two lines, then an ellipsis. Three of these descriptions stacked at full
    length turns the menu into a wall of near-identical text, and the part
    that differs — the dish name and the price — is already above it. */
.traycard .contents{
  font-size:.82rem;color:var(--ink-3);line-height:1.55;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
}
.traycard .foot{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:2px}

/* ── what is in the tray ─────────────────────────────────────────────────
   One line per compartment: what it holds on the left, whether it can be
   changed on the right. Three columns rather than a sentence, because the
   value of this block is that you can run your eye down it — a customer
   deciding between two cards is comparing compartment against compartment.

   Every column that could hold a long dish name gets minmax(0,1fr) and an
   ellipsis. A grid track defaults to min-width:auto, which is the width of
   its longest word, so one long name would otherwise push the swap count off
   the card rather than truncating. */
.traylist{list-style:none;display:grid;gap:1px}
.traylist li{
  display:grid;grid-template-columns:70px minmax(0,1fr) auto;gap:9px;align-items:center;
  padding:3px 0;line-height:1.4;
}
.traylist .lbl{font-size:.68rem;color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.traylist .dish{font-size:.82rem;color:var(--ink);min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.traylist .sw{font-size:.66rem;color:var(--ink-3);white-space:nowrap}
/*  The dish the card is named after. It is the reason the customer stopped
    here, so it reads first and reads heavier. */
.traylist li.own .dish{font-weight:700}
.traylist li.own .sw{color:var(--teal);font-weight:600}
/*  A square, not a circle — it is the Indian veg/non-veg mark, and the mark is
    square. Small enough to sit inside a line of text without disturbing it. */
.dd{display:inline-block;width:7px;height:7px;border-radius:2px;margin-right:7px;vertical-align:middle}

/* ── quantity stepper ────────────────────────────────────────────────── */
.qty{display:inline-flex;align-items:center;border:1.5px solid var(--line);border-radius:999px;background:var(--white);overflow:hidden}
.qty button{width:var(--tap);height:var(--tap);display:grid;place-items:center;font-size:1.25rem;font-weight:700;color:var(--teal);line-height:1}
.qty button:hover{background:var(--teal-wash)}
.qty input{width:52px;height:var(--tap);text-align:center;border:0;font-weight:700;font-size:.95rem;background:none;-moz-appearance:textfield}
.qty input::-webkit-outer-spin-button,.qty input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}

/* ── build your own ──────────────────────────────────────────────────── */
.formatpick{display:grid;gap:12px;margin-bottom:26px}
@media(min-width:760px){.formatpick{grid-template-columns:repeat(4,1fr)}}
.fmt{
  border:1.5px solid var(--line);background:var(--white);border-radius:var(--r);
  padding:16px;text-align:left;transition:.18s;
}
.fmt:hover{border-color:var(--teal)}
.fmt.on{border-color:var(--teal);background:var(--teal-wash);box-shadow:inset 0 0 0 1px var(--teal)}
.fmt b{display:block;font-family:'Plus Jakarta Sans',sans-serif;font-size:1.05rem;font-weight:800}
.fmt span{font-size:.78rem;color:var(--ink-2)}

.slot{border:1px solid var(--line);background:var(--white);border-radius:var(--r);padding:14px;margin-bottom:10px}
.slot .lab{display:flex;justify-content:space-between;align-items:baseline;gap:10px;margin-bottom:9px}
.slot .lab b{font-size:.9rem}
.slot .lab span{font-size:.72rem;color:var(--ink-3)}
select.sel{
  width:100%;padding:12px 38px 12px 13px;border:1.5px solid var(--line);border-radius:10px;
  background:var(--cream) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2316201F' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") no-repeat right 12px center/16px;
  appearance:none;font-size:.9rem;font-weight:500;
}
select.sel:focus{outline:0;border-color:var(--teal)}
.buildsum{
  position:sticky;bottom:0;background:var(--white);border:1px solid var(--line);
  border-radius:var(--r-lg);padding:16px;box-shadow:var(--shadow-lg);margin-top:16px;
}
@media(min-width:960px){
  .buildgrid{display:grid;grid-template-columns:1fr 340px;gap:24px;align-items:start}
  .buildsum{position:sticky;top:calc(var(--stick-h) + 20px);margin:0}
}
/*  Label left, amount hard right — the way every ordering app does it, because
    that is the column the eye runs down to add the bill up. The amount is a
    fixed second column that never wraps and never shrinks; the label takes
    whatever is left and wraps into it. */
.sumrow{display:flex;justify-content:space-between;align-items:baseline;gap:12px;font-size:.86rem;padding:5px 0}
.sumrow>:first-child{min-width:0;flex:1 1 auto}
.sumrow>:last-child{flex:0 0 auto;margin-left:auto;text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}
.sumrow.total{border-top:1px solid var(--line);margin-top:8px;padding-top:12px;font-weight:800;font-size:1.05rem}

/*  A value that is PROSE, not a figure.

    The nowrap above is deliberate and must stay: a price broken across two
    lines is unreadable, and a date that wraps mid-word looks like a bug. But an
    address is neither, and forcing it onto one line is what pushed the order
    card wider than the phone it was being read on.

    .wrap lets it fold. .stack puts the label on its own line and gives the
    value the full width — which is the honest answer for an address on a
    390px screen, where label-left/value-right leaves about twelve characters
    of room whatever you do about wrapping. */
.sumrow.wrap>:last-child,
.sumrow.stack>:last-child{white-space:normal;flex:1 1 auto;min-width:0;overflow-wrap:anywhere}
.sumrow.stack{flex-direction:column;align-items:stretch;gap:2px}
.sumrow.stack>:first-child{flex:0 0 auto;color:var(--ink-3);font-size:.78rem}
.sumrow.stack>:last-child{margin-left:0;text-align:left}

/* ── forms ───────────────────────────────────────────────────────────── */
.field{margin-bottom:14px}
.field label{display:block;font-size:.78rem;font-weight:600;color:var(--ink-2);margin-bottom:6px}
.field input,.field textarea,.field select{
  width:100%;padding:12px 13px;border:1.5px solid var(--line);border-radius:10px;
  background:var(--white);font-size:.92rem;
}
.field input:focus,.field textarea:focus,.field select:focus{outline:0;border-color:var(--teal)}
.field textarea{min-height:86px;resize:vertical}
.field .hint{font-size:.74rem;color:var(--ink-3);margin-top:5px}
.row2{display:grid;gap:0 14px}
@media(min-width:560px){.row2{grid-template-columns:1fr 1fr}}

/* Block, not flex: a <b> inside a flex notice becomes its own flex item and
   the sentence breaks into columns. */
.notice{border-radius:12px;padding:13px 15px;font-size:.86rem;display:block}
.notice.warn{background:#FDF3E2;color:#7A5A16;border:1px solid #F0DFBC}
.notice.bad{background:#FBE9E8;color:#7C231F;border:1px solid #F2CFCC}
.notice.good{background:#E9F5EC;color:#15602F;border:1px solid #C9E6D2}
.notice.info{background:var(--teal-wash);color:var(--teal-d);border:1px solid #C6E2E5}

/* ── cart ────────────────────────────────────────────────────────────── */
.cartline{display:grid;grid-template-columns:1fr auto;gap:12px;padding:16px 0;border-bottom:1px solid var(--line)}
.cartline h4{font-size:.96rem;font-weight:700}
.cartline .meta{font-size:.78rem;color:var(--ink-3);margin-top:4px;line-height:1.5}
.cartline .right{text-align:right;display:flex;flex-direction:column;align-items:flex-end;gap:8px}
.cartline .lt{font-weight:800;font-family:'Plus Jakarta Sans',sans-serif}
/*  min-width:0 on the tracks is load-bearing, not tidiness.

    A grid track defaults to min-width:auto, meaning "never shrink below my
    content". The date picker inside the left track is a flex row of 28 chips
    with overflow-x:auto — but auto-minimum ignored the overflow and sized the
    track to all 28 chips laid end to end. The grid then grew to roughly twice
    the viewport, and the 360px right-hand column — the one holding the total
    and the Pay button — was pushed off the right edge of the screen.

    The symptom read as two unrelated faults: "the page is not aligned" and
    "there is nothing to do after I pick an address". Both were this. */
@media(min-width:960px){
  .cartgrid{display:grid;grid-template-columns:minmax(0,1fr) 360px;gap:28px;align-items:start}
}
.cartgrid > *{min-width:0}
.panel{background:var(--white);border:1px solid var(--line);border-radius:var(--r-lg);padding:20px}
/*  The sign-out panel. Red-bordered and on its own, because "where do I sign
    out" should never be a question — see the note in the profile view. */
.panel.signout{display:flex;gap:16px;align-items:center;justify-content:space-between;flex-wrap:wrap;
  border-color:#EBCFC9;background:#FDF7F5}
.btn-danger{background:#A3341F;color:#fff}
.btn-danger:hover{background:#8C2B1A}
@media(min-width:960px){.panel.sticky{position:sticky;top:calc(var(--stick-h) + 20px)}}

/* ── account ─────────────────────────────────────────────────────────── */
.tabs{display:flex;gap:4px;border-bottom:1px solid var(--line);margin-bottom:22px;overflow-x:auto;scrollbar-width:none}
.tabs::-webkit-scrollbar{display:none}
.tab{padding:11px 15px;font-size:.87rem;font-weight:600;color:var(--ink-3);border-bottom:2.5px solid transparent;white-space:nowrap}
.tab.on{color:var(--teal);border-color:var(--teal)}
.orderrow{border:1px solid var(--line);border-radius:var(--r);padding:16px;margin-bottom:12px;background:var(--white)}
.orderrow .h{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;align-items:baseline}
.status{font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.07em;padding:3px 9px;border-radius:999px}
.status.confirmed{background:#E9F5EC;color:var(--green)}
.status.awaiting_payment{background:#FDF3E2;color:var(--amber)}
.status.cancelled{background:#FBE9E8;color:var(--red)}

/* ── strips ──────────────────────────────────────────────────────────── */
.stripdark{background:var(--panel);color:var(--on-panel)}
.stripdark h2{color:#fff}
.stripdark p{color:var(--on-panel-2)}
.stripteal{background:var(--teal);color:#fff}
.stripteal h2,.stripteal h3{color:#fff}
.stripteal p{color:rgba(255,255,255,.82)}
.stepcard{background:var(--white);border:1px solid var(--line);border-radius:var(--r-lg);padding:22px;height:100%}
.stepcard .n{
  width:34px;height:34px;border-radius:50%;background:var(--teal-wash);color:var(--teal-d);
  display:grid;place-items:center;font-weight:800;font-size:.9rem;margin-bottom:14px;
  font-family:'Plus Jakarta Sans',sans-serif;
}
.stepcard h3{margin-bottom:7px}
.stepcard p{font-size:.88rem;color:var(--ink-2)}
.badgerow{display:flex;flex-wrap:wrap;gap:8px;margin-top:20px}
.badge{border:1px solid var(--line);background:var(--white);border-radius:999px;padding:7px 14px;font-size:.75rem;font-weight:600;color:var(--ink-2)}
.stripdark .badge{background:rgba(255,255,255,.06);border-color:var(--on-panel-line);color:var(--on-panel-2)}

/* ── food safety ─────────────────────────────────────────────────────────
   Corporate buyers treat hygiene as a gate, not a feature: it is checked
   before price and it is checked by somebody who is not the person ordering
   lunch. So the marks get a band of their own under the hero — see .statband —
   and their own page to link an auditor at.
   ───────────────────────────────────────────────────────────────────────── */
/*  The certificate grid.

    Flenbo's shape, kept deliberately: two by two, cells divided by hairlines
    rather than floated as separate cards, each opening with its own
    line-drawn mark above a large setting of the standard's name. A row of
    four small cards said "here are four badges"; this says "here are four
    things, read them" — which is what somebody vetting a supplier is doing.

    The hairlines are the grid's own gap showing through: one background
    behind, cream cells on top, 1px of space between them. Cheaper and more
    exact than eight border declarations that have to cancel each other out at
    the edges.

    Colour and type are Tray Theory's — teal where Flenbo is gold, and the
    display face rather than a serif. Everything else is the same. */
.certgrid{
  display:grid;gap:1px;background:var(--line);
  border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;
}
@media(min-width:700px){.certgrid{grid-template-columns:repeat(2,1fr)}}
.cert{background:var(--cream);padding:30px 28px 34px}
@media(max-width:699px){.cert{padding:26px 22px 28px}}
/*  Flenbo sets the mark at roughly 1.4× the icon, with a lot of air between
    them. Matched by eye against their page rather than guessed. */
.certicon{
  width:36px;height:36px;display:block;margin-bottom:30px;
  stroke:var(--teal);fill:none;stroke-width:1.45;stroke-linecap:round;stroke-linejoin:round;
}
.cert h3{
  font-size:clamp(1.85rem,4.4vw,2.4rem);line-height:1.08;letter-spacing:-.025em;
  color:var(--ink);margin:0;
}
.cert .sub{
  margin-top:12px;font-size:.72rem;font-weight:700;text-transform:uppercase;
  letter-spacing:.16em;color:var(--teal);
}
.cert p:not(.sub){margin-top:16px;font-size:.95rem;line-height:1.65;color:var(--ink-2)}

/*  The rules, as a spec sheet.

    They were eleven prose bullets, then a grid of hairline boxes. The boxes
    were better than the bullets and still not right: every label started at a
    different x-position, so six short facts looked like six things to read
    rather than one short table. And a grid of an odd number of cells in a
    two-column layout shows its own hairline background through the empty
    position — a grey rectangle where a seventh rule should be.

    Rows fix both. The labels align on one axis and the eye runs down them; a
    row cannot leave a hole. Which is what a list of terms wants to be anyway. */
.rules{max-width:840px;border-top:1px solid var(--line)}
.rule{
  display:grid;gap:5px;padding:17px 0;border-bottom:1px solid var(--line);
}
@media(min-width:700px){
  .rule{grid-template-columns:190px minmax(0,1fr);gap:0 36px;align-items:baseline;padding:19px 0}
}
.rule dt{
  font-size:.7rem;font-weight:700;text-transform:uppercase;letter-spacing:.14em;
  color:var(--teal);
}
.rule dd{margin:0;font-size:1rem;line-height:1.6;color:var(--ink)}

/*  A photographic page header.

    The dark panel with the radial glow is right for the home hero, where the
    picture is a tray in its own frame beside the words. For food safety the
    picture IS the argument — a real kitchen, real hairnets, real gloves — so
    it goes full bleed with the headline over it, the way flenbo.com does it.

    Falls back to the ordinary dark hero if no photograph has been supplied,
    which is what it does today. The scrim is a left-weighted gradient rather
    than a flat wash so the headline stays legible without dulling the whole
    image; on a phone it deepens, because the text runs across more of it. */
/*  Taller on a wide screen. The banner was capped at 440px, which is a
    reasonable page header on a 1400px laptop and a sliver across a 2600px
    monitor — the cap stopped it growing long before the viewport did, so the
    wider the screen the thinner it looked. Scaling to 32vw with a 660px
    ceiling keeps it ~450px on a laptop and gives it real presence on a large
    display, without ever eating a whole screen.

    The padding grows with it, or the headline sits marooned at the bottom of a
    much taller picture. */
.photohero{padding:clamp(56px,6vw,96px) 0 clamp(64px,7vw,104px)}
/*  Opt out of the home hero's two-column grid, which is declared on
    `.hero .wrap` at 900px and up and was putting the eyebrow in one column
    and the headline in the other, half a screen apart. */
.photohero .wrap{display:block}
/*  26ch, not 18. At the larger size an 18ch measure broke "planned like a
    delivery." across two lines, so a two-line headline set as three. */
.photohero h1{max-width:26ch;font-size:clamp(1.8rem,4.6vw,3.4rem)}
.photohero.hasphoto{
  background-size:cover;background-position:center;background-repeat:no-repeat;
  min-height:clamp(340px,32vw,660px);display:flex;align-items:flex-end;
}
.photohero.hasphoto .wrap{width:100%}
.photohero.hasphoto::before{
  content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,rgba(9,32,34,.90) 0%,rgba(9,32,34,.72) 42%,rgba(9,32,34,.28) 100%);
}
/*  The home hero's glow would sit on top of the photograph as a teal haze. */
.photohero.hasphoto::after{display:none}
@media(max-width:699px){
  .photohero.hasphoto::before{
    background:linear-gradient(180deg,rgba(9,32,34,.62) 0%,rgba(9,32,34,.88) 100%);
  }
}

/*  The one paragraph that carries the page. Wide type, generous line height,
    and a teal rule down the left — the same device flenbo.com uses to mark
    the block as the statement rather than the body copy. */
.leadblock{
  max-width:62ch;border-left:3px solid var(--teal);padding:4px 0 4px 22px;
}
.leadblock p{font-size:1.06rem;line-height:1.75;color:var(--ink-2)}
.leadblock p + p{margin-top:15px}
@media(max-width:699px){.leadblock{padding-left:16px}.leadblock p{font-size:1rem}}

/*  The safe-hold figure. One number, said once, big — it is the fact a buyer
    planning a 1 pm delivery actually needs and it should not be a bullet. */
.holdcard{
  border:1px solid var(--on-panel-line);border-radius:var(--r-lg);
  background:rgba(95,208,218,.08);padding:26px 24px;text-align:center;
}
.holdcard b{
  display:block;font-family:'Plus Jakarta Sans',sans-serif;font-weight:800;
  font-size:clamp(2.4rem,7vw,3.4rem);line-height:1;color:var(--teal-bright);
  letter-spacing:-.03em;
}
.holdcard span{display:block;margin-top:12px;font-size:.9rem;color:var(--on-panel-2);
  max-width:34ch;margin-left:auto;margin-right:auto}

/*  Packaging facts. Denser than .badge because there are more of them and
    each is a specification rather than a label. */
.specs{display:grid;gap:10px;margin-top:18px}
@media(min-width:560px){.specs{grid-template-columns:repeat(2,1fr)}}
.specs div{display:flex;gap:9px;align-items:flex-start;font-size:.88rem;color:var(--ink-2)}
.specs svg{width:16px;height:16px;flex:none;margin-top:2px;stroke:var(--teal);fill:none;
  stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}
.stripdark .specs div{color:var(--on-panel-2)}
.stripdark .specs svg{stroke:var(--teal-bright)}

/* ── footer ──────────────────────────────────────────────────────────── */
footer{background:var(--cream);border-top:1px solid var(--line);padding:52px 0 30px;color:var(--ink-2)}
footer a:hover{color:var(--teal)}
.fmain{display:grid;gap:32px}
@media(min-width:900px){.fmain{grid-template-columns:1.9fr 1fr 1fr 1.2fr;gap:44px}}
.fbrand img{height:44px;margin-bottom:16px}
.fbrand p{font-size:.88rem;line-height:1.7;max-width:38ch}
.fsocials{display:flex;flex-wrap:wrap;align-items:center;gap:8px 20px;margin-top:16px}
.fsocial{display:inline-flex;align-items:center;gap:9px;font-size:.72rem;text-transform:uppercase;letter-spacing:.16em;color:var(--teal);font-weight:600}
/*  The glyph is drawn rather than fetched — one <img> for a 24px mark is a
    request the page does not need, and a stroked SVG inherits the link colour
    so it darkens on hover with the text instead of sitting there unchanged. */
.fsocial svg{
  width:19px;height:19px;flex:none;fill:none;stroke:currentColor;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;
}
.fsocial:hover{color:var(--teal-d)}

/*  Same mark inside a run of text on the contact page. Sized in em so it
    tracks the surrounding type rather than needing its own breakpoint. */
.iglink{display:inline-flex;align-items:center;gap:7px;vertical-align:-.22em}
.iglink svg{
  width:1.15em;height:1.15em;flex:none;fill:none;stroke:currentColor;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;
}
.fcol h4{font-family:'Inter',sans-serif;font-size:.66rem;text-transform:uppercase;letter-spacing:.2em;color:var(--teal);font-weight:700}
.fcol ul{list-style:none;margin-top:16px}
.fcol li{margin-bottom:11px;font-size:.88rem}
.fwa{display:inline-flex;align-items:center;gap:8px;margin-top:18px;border:1.5px solid var(--line);border-radius:999px;padding:10px 17px;font-size:.7rem;text-transform:uppercase;letter-spacing:.12em;font-weight:600;color:var(--ink)}
.fwa:hover{background:var(--panel);border-color:var(--panel);color:var(--on-panel)}
.fbar{border-top:1px solid var(--line);margin-top:34px;padding-top:22px;font-size:.76rem;color:var(--ink-3);display:flex;justify-content:space-between;flex-wrap:wrap;gap:12px}
.flinks{display:flex;gap:20px;flex-wrap:wrap}
.freg{margin-top:12px;font-size:.7rem;color:var(--ink-3);line-height:1.7}

/* ── modal / sheet ───────────────────────────────────────────────────── */
.scrim{position:fixed;inset:0;background:rgba(22,32,31,.55);z-index:80;display:grid;place-items:end center;padding:0}
@media(min-width:700px){.scrim{place-items:center}}
.sheet{
  background:var(--white);width:100%;max-width:460px;border-radius:22px 22px 0 0;
  padding:24px 20px calc(24px + env(safe-area-inset-bottom));max-height:92vh;overflow:auto;
  animation:up .28s cubic-bezier(.22,1,.36,1);
}
@media(min-width:700px){.sheet{border-radius:var(--r-lg);animation:pop .2s ease}}
@keyframes up{from{transform:translateY(100%)}}
@keyframes pop{from{transform:scale(.96);opacity:0}}
.sheet h3{margin-bottom:6px}
.sheet .x{position:absolute;top:14px;right:16px}

.toast{
  position:fixed;left:50%;bottom:24px;transform:translateX(-50%);z-index:90;
  background:var(--panel-2);color:var(--on-panel);padding:13px 20px;border-radius:999px;
  font-size:.86rem;font-weight:500;box-shadow:var(--shadow-lg);max-width:90vw;text-align:center;
  animation:pop .2s ease;
}
.toast.bad{background:var(--red);color:#fff}

.spinner{width:18px;height:18px;border:2.2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:spin .7s linear infinite}
.spinner.dark{border-color:rgba(22,32,31,.2);border-top-color:var(--ink)}
@keyframes spin{to{transform:rotate(360deg)}}
.skeleton{background:linear-gradient(90deg,var(--cream-2) 25%,#EDE7DA 37%,var(--cream-2) 63%);background-size:400% 100%;animation:sk 1.3s ease infinite;border-radius:var(--r)}
@keyframes sk{0%{background-position:100% 50%}100%{background-position:0 50%}}

/* the sticky "go to cart" bar on phones */
.cartbar{
  position:fixed;left:0;right:0;bottom:0;z-index:55;background:var(--panel);color:#fff;
  padding:12px 16px calc(12px + env(safe-area-inset-bottom));
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  box-shadow:0 -8px 28px -14px rgba(0,0,0,.5);
}
.cartbar .n{font-size:.82rem;color:rgba(255,255,255,.72)}
.cartbar b{font-family:'Plus Jakarta Sans',sans-serif;font-size:1.02rem}
body.hasbar{padding-bottom:78px}
@media(min-width:900px){.cartbar{display:none}body.hasbar{padding-bottom:0}}

/* ── floating WhatsApp ───────────────────────────────────────────────────
   Sits above the sticky cart bar (z-index 55) and below the modal scrim (80),
   so it is always reachable but never floats on top of a dialogue the visitor
   is trying to read.

   On a phone it is a plain 56px circle — anything wider eats the corner of the
   page. From 900px up, where there is room, it grows a "Chat with us" label on
   hover: a bare green circle is recognisable to people who already know the
   convention and invisible to everyone else.

   56px, not the 44px minimum. This is a corner target reached with a thumb
   stretched across the screen, which is the least accurate tap anyone makes. */
.wafloat{
  position:fixed;right:16px;z-index:56;
  bottom:calc(16px + env(safe-area-inset-bottom));
  display:inline-flex;align-items:center;gap:0;
  height:56px;padding:0 16px;border-radius:999px;
  background:var(--panel);color:#fff;
  box-shadow:0 6px 16px -4px rgba(11,26,25,.34), 0 18px 40px -18px rgba(11,26,25,.7);
  transition:background .18s ease, transform .18s ease, gap .22s cubic-bezier(.22,1,.36,1);
}
.wafloat svg{width:26px;height:26px;flex:none;fill:currentColor}
.wafloat:hover{background:#128C7E;color:#fff;transform:translateY(-2px)}
.wafloat:active{transform:translateY(0)}
.wafloat:focus-visible{outline:3px solid var(--teal-bright);outline-offset:3px}

/*  The label is collapsed to zero width rather than display:none so it can
    animate open, and stays out of the accessibility tree's way either side. */
.walabel{
  max-width:0;overflow:hidden;white-space:nowrap;
  font-size:.82rem;font-weight:600;letter-spacing:.01em;
  transition:max-width .22s cubic-bezier(.22,1,.36,1);
}
@media(min-width:900px){
  .wafloat{right:22px;bottom:22px}
  .wafloat:hover,.wafloat:focus-visible{gap:10px}
  .wafloat:hover .walabel,.wafloat:focus-visible .walabel{max-width:150px}
}

/*  When the cart bar is up, clear it — otherwise the button covers the total
    and the "View cart" tap target on exactly the screen where both matter. */
body.hasbar .wafloat{bottom:calc(90px + env(safe-area-inset-bottom))}
@media(min-width:900px){body.hasbar .wafloat{bottom:22px}}

@media(prefers-reduced-motion:reduce){
  .wafloat,.walabel{transition:none}
  .wafloat:hover{transform:none}
}

/* A photo dropped into /assets/trays/<id>.jpg covers the placeholder
   automatically. Until then the dashed box says what belongs there. */
.imgslot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;z-index:1;background:var(--cream-2)}
/* Tray shots are shown whole, never cropped. A top-view tray that loses a
   compartment to a crop is a photograph of the wrong product. */
.imgslot.contain img{object-fit:contain;padding:6px;background:var(--cream-2)}

/* ── customiser sheet ────────────────────────────────────────────────── */
.cust{max-width:920px;width:100%;background:var(--white);border-radius:22px 22px 0 0;max-height:94vh;overflow:auto;position:relative;animation:up .28s cubic-bezier(.22,1,.36,1)}
/*  The columns carry the height cap, not just the panel.

    `max-height:88vh; overflow:hidden` on the grid alone does not make anything
    scroll. The single implicit row is sized to its tallest child — an 8CP tray
    is about 2400px of options — and the max-height then CLIPS that row. Both
    columns inherit the full 2400px as their own height, so the
    `.right{overflow:auto}` below never engages: its content already fits its
    box. On every desktop this showed the first two compartments and left the
    remaining six unreachable by any means — no scrollbar, no wheel, nothing.
    Which looks exactly like what the customer reported: a tray whose contents
    you cannot find out.

    Capping each column instead gives it a real scrollport. The panel still
    sizes to its content, so a 3CP tray does not open as a tall empty box. */
@media(min-width:820px){
  /*  THE PRICE BAR IS A ROW OF THE GRID, NOT SOMETHING FLOATING OVER IT.

      It used to be position:absolute along the bottom of the right half, with
      the choices reserving 110px of padding to clear it. 110px is a guess, and
      the guess was wrong the moment the bar grew: with the kitchen shut it
      carried a notice strip as well as the price, the stepper and the button,
      and the last two compartments of an 8CP tray sat underneath it with no
      way to scroll to them. Client, 8 September: "only 6 of the 8 items are
      visible and not able to scroll down."

      As a grid row it takes exactly the height it needs and the choices get
      exactly what is left, whatever either of them contains. There is no
      number here to be wrong. */
  .cust{
    border-radius:var(--r-lg);animation:pop .2s ease;
    display:grid;grid-template-columns:1fr 1.05fr;grid-template-rows:minmax(0,1fr) auto;
    max-height:88vh;overflow:hidden;
  }
  /*  Grid items refuse to shrink below their content without min-height:0. */
  .cust > .left{grid-column:1;grid-row:1 / span 2;min-height:0}
  .cust > .right{grid-column:2;grid-row:1;min-height:0}
  .cust > .custbar{grid-column:2;grid-row:2;min-height:0}
}
.cust .x{position:absolute;top:12px;right:12px;z-index:5;width:36px;height:36px;border-radius:50%;background:rgba(255,255,255,.92);display:grid;place-items:center;box-shadow:var(--shadow)}
.cust .x svg{width:18px;height:18px;stroke:var(--ink);stroke-width:2;fill:none;stroke-linecap:round}
.cust .left{padding:20px}
@media(min-width:820px){.cust .left{border-right:1px solid var(--line);overflow:auto}}
.cust .right{padding:20px;background:var(--cream)}
@media(min-width:820px){.cust .right{overflow:auto;padding-bottom:20px}}
.cust h3{font-size:1.3rem;margin-bottom:6px}
/* ── "In this tray", pinned above the choices ────────────────────────────
   The customiser lists one compartment at a time, and on a five- or
   eight-compartment tray the last of them is a long way down. A customer part
   way through swapping the rice could not answer "so what is in this tray?"
   without scrolling to the bottom and back — which is where this summary used
   to live.

   Negative margins pull it out of the panel's padding so it spans the full
   width when it sticks; without them it would stick with a 20px stripe of
   scrolling content showing either side of it. */
/* ── the tray, as one list ────────────────────────────────────────────────
   Until 8 September this sheet described the same five compartments twice:
   a folding summary at the top, then every compartment's options again as
   stacked blocks below it. Nineteen hundred pixels for a five-compartment
   tray, and "change" merely scrolled you from the first list into the second
   — which is why the options landed nowhere near the row that sent you.

   One list now. The row is the summary; the drawer under it is the choice;
   they are the same element, so they cannot be scrolled apart.
   ───────────────────────────────────────────────────────────────────────── */
.trayhd{display:flex;align-items:center;gap:9px;padding-right:44px;margin-bottom:11px}
.trayhd b{font-family:'Plus Jakarta Sans',sans-serif;font-size:.95rem;font-weight:800}
.trayhd .cnt{font-size:.72rem;color:var(--ink-3)}
.traylist{display:grid;gap:7px}

/*  .trayrow, not .sumrow. It was .sumrow until 21 August and that was a real
    bug, not a tidy-up: the checkout summary uses .sumrow too, this block sits
    LATER in the file, and a three-column grid therefore won the cascade over
    the money rows. The result on a phone was "Chilly Paneer · 3CP Chinese"
    squeezed into a 76px column and the amount parked in the middle of the row
    instead of pinned to the right edge. Two different things must not share a
    class name. */
.trayrow{
  display:grid;grid-template-columns:76px minmax(0,1fr) auto;gap:10px;align-items:center;
  width:100%;text-align:left;padding:9px 11px;min-height:44px;
  background:var(--white);border:1.5px solid var(--line);border-radius:11px;transition:.15s;
}
.trayrow:hover{border-color:var(--teal)}
.trayrow:focus-visible{outline:2px solid var(--teal);outline-offset:2px}
/*  Nothing to choose, so nothing to press: the hero dish and any compartment
    with a single option state themselves and stop there. */
.trayrow.shut{cursor:default;background:var(--cream-2);border-style:dashed}
.trayrow.shut:hover{border-color:var(--line)}
.trayrow .lbl{font-size:.68rem;color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.trayrow .dish{font-size:.85rem;font-weight:600;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.trayrow .dish small{font-weight:400;font-size:.72rem;color:var(--ink-3);margin-left:7px}
.trayrow .sw{font-size:.68rem;font-weight:700;color:var(--teal);white-space:nowrap;
             display:flex;align-items:center;gap:3px}
.trayrow .sw.mine{color:var(--teal-d)}
.trayrow .sw.fixed{color:var(--ink-3);font-weight:500}
.trayrow .chev{
  width:14px;height:14px;flex:none;stroke:currentColor;fill:none;
  stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round;transition:transform .2s ease;
}

/*  Open, the row and its drawer are one card: the row loses its bottom edge
    and the drawer grows out of it, so it is unmistakable WHICH compartment
    these options belong to — the whole complaint that started this. */
.trayitem.open .trayrow{
  border-color:var(--teal);border-bottom-color:transparent;
  border-bottom-left-radius:0;border-bottom-right-radius:0;
}
.trayitem.open .trayrow .chev{transform:rotate(180deg)}
.traydrawer{
  background:var(--white);border:1.5px solid var(--teal);border-top:0;
  border-radius:0 0 11px 11px;padding:2px 8px 8px;
  animation:drawerIn .16s ease-out;
}
@keyframes drawerIn{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
@media(prefers-reduced-motion:reduce){.traydrawer{animation:none}}
.traydrawer .note{font-size:.72rem;color:var(--ink-3);padding:7px 5px 8px;line-height:1.45}

.opt{display:flex;align-items:center;gap:11px;padding:11px 12px;border-radius:11px;cursor:pointer;transition:.15s;border:1.5px solid transparent}
.opt:hover{background:var(--white)}
.opt.on{background:var(--white);border-color:var(--teal)}
.opt .radio{width:20px;height:20px;border:2px solid #CFC9BC;border-radius:50%;flex:0 0 auto;display:grid;place-items:center;transition:.15s}
.opt.on .radio{border-color:var(--teal)}
.opt.on .radio::after{content:"";width:10px;height:10px;border-radius:50%;background:var(--teal)}
.opt .nm{flex:1;font-size:.9rem;font-weight:500;line-height:1.35}
.opt .nm small{display:block;font-size:.72rem;color:var(--ink-3);font-weight:400}
.opt .dl{font-size:.82rem;font-weight:700;color:var(--ink-2);white-space:nowrap}
.opt .dl.zero{color:var(--ink-3);font-weight:500}
.custbar{
  position:sticky;bottom:0;background:var(--white);border-top:1px solid var(--line);
  padding:14px 20px calc(14px + env(safe-area-inset-bottom));
  display:flex;align-items:center;gap:12px;justify-content:space-between;flex-wrap:wrap;
}
@media(min-width:820px){.custbar{position:static}}
.custbar .p{font-family:'Plus Jakarta Sans',sans-serif;font-size:1.3rem;font-weight:800}
.custbar .p small{display:block;font-family:'Inter',sans-serif;font-size:.68rem;font-weight:500;color:var(--ink-3);letter-spacing:.02em}

/* On a phone the panel opens on the CHOICES, not on a photo and a paragraph —
   that is what the customer came to do. The picture shrinks, the blurb goes. */
@media(max-width:819px){
  .cust .left{padding:16px 20px 4px}
  /*  Every tray photograph is 4:3. This frame was 16/7, so object-fit:contain
      fitted the picture to the height of a short wide box and left cream down
      both sides — the tray rendered about 201px wide on a 390px phone, just
      over half the screen. Matching the frame to the photograph gives all of
      that width back with nothing cropped; the padding goes for the same
      reason. It costs 110px of height, which is what the 16/7 was buying, and
      the photograph is why somebody opened the sheet. */
  .cust .left .imgslot{aspect-ratio:4/3;padding:0}
  .cust .left .blurb{display:none}
  .cust .left h3{font-size:1.15rem;margin-top:12px}
  .cust .right{padding-top:8px}
}

/* Menu cards stay vertical on a phone — photo on top, description under it.
   The food is the thing being sold, so it gets the width. */
@media(max-width:639px){
  #menuGrid{gap:14px}
  /*  16/10, not 4/3. This ID selector outranks the .traycard rule further up,
      so both have to say the same thing — an easy trap: editing only the
      lower-specificity one changes nothing and looks like a caching problem. */
  #menuGrid .traycard .imgslot{aspect-ratio:16/10}
  /*  Same pairing as the rule above, and it has to be repeated here for the
      same reason the comment above gives: this ID selector outranks it. */
  #menuGrid .traycard .imgslot:has(img){aspect-ratio:4/3}
  #menuGrid .traycard .body{padding:14px}
  #menuGrid .traycard h3{font-size:1.02rem}
  #menuGrid .traycard .price{font-size:1.12rem}
  #menuGrid .traycard .contents{font-size:.8rem}
}
/* (The two-line clamp for .traycard .contents lives with the rest of the
   tray-card rules further up, so there is only one place to change it.) */

/* A grid track defaults to min-width:auto, which is the width of its longest
   unbreakable child — so a long contents line pushed the price off the right
   edge of a 390px phone. */
#menuGrid .traycard .body{min-width:0}
.traycard .top{min-width:0}
.traycard h3{min-width:0;overflow-wrap:anywhere}
.traycard .foot{flex-wrap:wrap;row-gap:8px}
@media(max-width:639px){
  .cartline{grid-template-columns:1fr auto;gap:10px}
  .cartline h4{font-size:.9rem;overflow-wrap:anywhere}
}

/* ── delivery date ───────────────────────────────────────────────────────
   Was a scrolling row of 28 date chips. Replaced by a native date input:
   one control, a real calendar on desktop, the OS date wheel on a phone, and
   no horizontal overflow to contain. The .cartgrid min-width:0 rules above
   stay regardless — they are correct for any wide child. */

/* ── address map ─────────────────────────────────────────────────────── */
.mapbox{width:100%;height:230px;border-radius:var(--r);border:1px solid var(--line);background:var(--cream-2)}
@media(min-width:700px){.mapbox{height:280px}}
.maphint{font-size:.74rem;color:var(--ink-3);margin-top:7px;text-align:center}
.mapshell{position:relative}
.locbtn{
  position:absolute;left:50%;bottom:12px;transform:translateX(-50%);z-index:3;
  display:inline-flex;align-items:center;gap:7px;white-space:nowrap;
  background:var(--white);box-shadow:0 2px 10px rgba(22,32,31,.18);
}
.locbtn svg{width:15px;height:15px;stroke:currentColor;fill:none;stroke-width:1.8;stroke-linecap:round}
.locbtn:disabled{opacity:.6}
.georesult{
  display:block;width:100%;text-align:left;padding:11px 12px;border:1px solid var(--line);
  background:var(--white);border-radius:10px;margin-bottom:6px;font-size:.86rem;line-height:1.4;transition:.15s;
}
.georesult:hover{border-color:var(--teal);background:var(--teal-wash)}

/* ── address type-ahead ──────────────────────────────────────────────── */
.typeahead{position:relative}
.suggest{
  position:absolute;top:calc(100% + 6px);left:0;right:0;z-index:12;
  background:var(--white);border:1px solid var(--line);border-radius:12px;
  box-shadow:var(--shadow);overflow:hidden;max-height:288px;overflow-y:auto;
}
.suggest .sug{
  display:flex;align-items:flex-start;gap:10px;width:100%;text-align:left;
  padding:11px 13px;border-bottom:1px solid var(--line);transition:background .12s;
}
.suggest .sug:last-child{border-bottom:0}
.suggest .sug:hover,.suggest .sug.on{background:var(--teal-wash)}
.suggest .sug svg{
  width:16px;height:16px;flex:0 0 16px;margin-top:2px;
  stroke:var(--teal);fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;
}
.suggest .sug span{min-width:0}
.suggest .sug b{display:block;font-size:.88rem;font-weight:600;line-height:1.35}
/*  The secondary line is the sector and city. It is what distinguishes two
    buildings with the same name, so it must stay on one line and ellipsize
    rather than wrap into a paragraph. */
.suggest .sug em{
  display:block;font-style:normal;font-size:.76rem;color:var(--ink-3);margin-top:2px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.hr{border-top:1px solid var(--line);margin:18px 0 14px}
.sheet h4{font-family:'Plus Jakarta Sans',sans-serif;font-weight:800}
.opt .nm small + small{margin-top:2px}

/* ── the lane, on the customiser ──────────────────────────────────────────
   The strip is the warning and the line under the price is the teaching, and
   they are deliberately different weights: one is read once, the other is
   glanced at continuously while the number changes.

   The strip sits ABOVE .custbar rather than inside it so that appearing does
   not reflow the row the thumb is already on — the Add button keeps its
   position relative to the bottom of the sheet either way. */
/*  .custbar becomes a column: the strip on top, the price-and-button row
    below. It has to be INSIDE the bar rather than above it, because the bar is
    position:sticky and a sibling is not — the strip scrolled out of sight on
    any sheet tall enough to scroll, which is every sheet on a phone. */
.custbar{flex-direction:column;align-items:stretch;gap:0;padding:0}
.custrow{
  display:flex;align-items:center;gap:12px;justify-content:space-between;flex-wrap:wrap;
  padding:14px 20px calc(14px + env(safe-area-inset-bottom));
}
.lanestrip{
  display:block;padding:12px 20px;font-size:.82rem;line-height:1.5;
  background:#FAF0DE;border-bottom:1px solid #E6CF9E;color:#6B4410;
}
.lanestrip b{font-weight:700}
.lanestrip .laneundo{
  display:block;margin-top:7px;color:#6B4410;font-weight:700;font-size:.8rem;
}
.custbar .p .when{
  display:block;font-family:'Inter',sans-serif;font-size:.72rem;font-weight:700;
  margin-top:5px;letter-spacing:.01em;
}
.custbar .p .when.today{color:var(--teal)}
.custbar .p .when.plan{color:#8A5410}
/*  The button carries the count and the total, so the last screen before the
    cart is not a mystery. Line-height is pulled in because two lines in a pill
    otherwise make the pill taller than the stepper beside it. */
/*  .btn is inline-flex, so the label and the sub-line are two flex items.
    Stacking the column is what puts one under the other; display:block on the
    <small> does nothing inside a flex container, which is how they ended up on
    one line. */
#cAdd{flex-direction:column;gap:0;line-height:1.2;padding:9px 22px}
#cAdd small{
  font-weight:500;font-size:.66rem;opacity:.85;margin-top:1px;letter-spacing:.01em;
}
#cAdd.lanecall{background:var(--ink-2);border-color:var(--ink-2)}

/* ── the kitchen is shut ──────────────────────────────────────────────────
   PINNED, above the pinned header, as one stack.

   It used to sit in normal flow and scroll away with the rest of the page
   furniture — the argument being that the availability line on every card
   carries the message anyway, and that a bar nobody can dismiss is louder
   rather than more honest.

   That was wrong in the one situation that matters. Somebody who scrolls a
   long menu is exactly the person about to try to order, and by then the bar
   was three screens above them. Petpooja keeps its banner there the whole
   time, and it is right to.

   The header's own `top` is driven from --svcbar-h rather than a guessed
   number, because this bar is one line on a laptop and two on a narrow phone,
   and a hardcoded offset would leave a red seam under the header on exactly
   the devices most customers use. See paintService. */
.svcbar{
  position:sticky;top:0;z-index:70;
  background:var(--red);color:#fff;padding:9px 16px;font-size:.78rem;line-height:1.4;
  display:flex;gap:8px;align-items:flex-start;justify-content:center;text-align:left;
}
.svcbar svg{flex:none;width:15px;height:15px;margin-top:1px}
.svcbar b{font-weight:700}

.svcmodal{
  position:fixed;inset:0;z-index:95;background:rgba(22,32,31,.55);
  display:grid;place-items:center;padding:22px;
}
.svcmodal .box{
  background:var(--white);border-radius:16px;padding:26px 24px;text-align:center;
  max-width:340px;width:100%;position:relative;box-shadow:0 20px 46px -20px rgba(0,0,0,.5);
}
.svcmodal .x{
  position:absolute;top:10px;right:12px;border:0;background:none;color:var(--ink-3);
  font-size:1rem;line-height:1;cursor:pointer;padding:4px;
}
/*  A covered dish, drawn in the site's own stroke rather than borrowed from
    the phone's emoji font — an emoji is a different picture on every device,
    and this one has to read as "the food is put away" on all of them. */
.svcicon{
  width:52px;height:52px;margin:0 auto;border-radius:50%;
  background:var(--teal-wash);color:var(--teal-d);display:grid;place-items:center;
}
.svcicon svg{width:26px;height:26px}
.svcmodal h4{font-family:'Plus Jakarta Sans',sans-serif;font-size:1.1rem;font-weight:800;margin:12px 0 0;letter-spacing:-.02em}
/*  Only ever the owner's own message, when she has closed the shop by hand.
    The timetable needs no sentence — the block below says it all. */
.svcmodal p{font-size:.86rem;color:var(--ink-2);margin:8px 0 0;line-height:1.55}
.svcmodal p b{color:var(--ink);font-weight:700}
/*  The date as an object, not as prose: something to glance at rather than
    read, which is the whole reason this notice got shorter. */
.svcwhen{
  background:var(--cream-2);border-radius:12px;padding:11px 14px;margin-top:14px;
}
.svcwhen span{
  display:block;font-size:.63rem;text-transform:uppercase;letter-spacing:.14em;
  color:var(--ink-3);font-weight:700;
}
.svcwhen b{display:block;font-size:1rem;font-weight:700;color:var(--panel);margin-top:3px}
.svcmodal .svcgo{margin-top:14px}

/*  Replaces "Customise +" on the card while the kitchen is shut. Same slot,
    same size, so the grid does not move — it just stops offering something it
    cannot do and answers the question instead. */
.nextav{font-size:.7rem;color:var(--ink-3);text-align:right;line-height:1.35}
.nextav b{display:block;color:var(--red);font-weight:700}

.custbar .p .when.shut{color:#8A2E24}
#cAdd:disabled{background:var(--line);border-color:var(--line);color:var(--ink-3);cursor:not-allowed}

/* ══ THE PHONE SHELL ══════════════════════════════════════════════════════
   Approved 10 September: an opening screen, a five-tab bar along the bottom,
   and a header carrying nothing but the logo. All three are PHONE ONLY — the
   desktop site keeps its header, its links and its hero, because a tab bar on
   a 1400px screen is a phone app pretending to be a website.

   900px is the breakpoint the sticky cart bar already uses. Reusing it means
   there is one number deciding what "a phone" is, rather than two that can
   drift apart.
   ───────────────────────────────────────────────────────────────────────── */
:root{
  /*  The bar's own height, without the safe area. Everything that has to sit
      clear of it adds env(safe-area-inset-bottom) itself, because that inset
      is 0 on most devices and 34px on a notched iPhone and no constant can be
      right for both. */
  --tabbar-h:60px;
}

/* ── the opening screen ──────────────────────────────────────────────────
   C3, chosen 9 September: the brand teal as the ground rather than the dark
   panel, which is the version that looks most like the logo. Two amendments
   from the client on 10 September: the claim is banded in the golden yellow
   from C1, and Enter is white.

   Every colour is set here as a value, not inherited from the page, because
   this screen is the one place the brand shows on its own — no cream, no
   cards, nothing else on screen to carry it. */
/*  hidden must actually hide it. The browser's own [hidden] rule is
    display:none at the lowest possible specificity, so a class setting
    display:flex beats it — and the opening screen was drawn over every page
    on the site, on every navigation, whatever the script had decided. */
.splash[hidden]{display:none}
.splash{
  position:fixed;inset:0;z-index:100;
  background:var(--teal);color:#fff;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  padding:26px 24px calc(26px + env(safe-area-inset-bottom));
  text-align:center;overflow:hidden;
}
/*  Two solid discs, one lighter and one darker than the ground. Solid, not
    translucent: a colour at low alpha over its own family turns to mud, and
    mud reads as a printing fault rather than a decision. */
.splash::before,.splash::after{content:"";position:absolute;border-radius:50%}
.splash::before{width:250px;height:250px;background:#0C6470;top:-122px;left:-104px}
.splash::after{width:210px;height:210px;background:#0A5560;bottom:-96px;right:-86px}
.splash .in{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;width:100%;max-width:420px}
.splash .lg{width:112px;height:auto;margin-bottom:20px}
.splash .kick{
  font-size:.66rem;font-weight:700;letter-spacing:.22em;text-transform:uppercase;
  /*  The gold, not white. Client, 10 September. It also ties the byline to
      the band below it, so the two golden things on the screen read as one
      decision rather than two. */
  color:#E8B84B;margin-bottom:13px;
}
.splash h1{font-family:'Plus Jakarta Sans',sans-serif;font-weight:800;letter-spacing:-.035em;line-height:1.05;margin:0}
.splash h1 span{display:block;font-size:1.62rem;color:#fff}
/*  The second line is the claim, so it takes the band and the tilt; the first
    is only the run-up. The golden yellow is C1's, kept at the client's ask —
    a cream band on teal was the one part of C3 that read as absence of colour
    rather than a choice. */
.splash h1 em{
  display:inline-block;font-style:normal;font-size:1.98rem;
  background:#E8B84B;color:#0D2C2F;padding:3px 17px 6px;border-radius:6px;
  margin-top:9px;max-width:100%;transform:rotate(-2.2deg);
}
/*  Named .hair, not .rule: the food-safety page already owns .rule as a
    definition-list row with a hairline under it, and the splash was quietly
    inheriting that border — a stray line across the opening screen. */
.splash .hair{display:flex;align-items:center;gap:10px;width:58%;margin:20px 0}
.splash .hair i{flex:1;height:1px;background:rgba(255,255,255,.3)}
.splash .hair b{width:5px;height:5px;border-radius:50%;background:#E8B84B;flex:none}
.splash .sub{margin:0 0 26px;max-width:32ch;font-size:.84rem;line-height:1.6;color:rgba(255,255,255,.84)}
/*  The same three figures the front page carries — 30 min, 3·5·8, 100% — so
    the opening screen and the page behind it say one thing rather than two. */
.splash .figs{
  display:grid;grid-template-columns:repeat(3,1fr);width:100%;
  background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.22);
  border-radius:14px;overflow:hidden;
}
.splash .figs div{padding:13px 8px;text-align:center}
.splash .figs div+div{border-left:1px solid rgba(255,255,255,.18)}
.splash .figs b{display:block;font-family:'Plus Jakarta Sans',sans-serif;font-weight:800;font-size:1.02rem}
.splash .figs span{display:block;font-size:.62rem;line-height:1.4;color:rgba(255,255,255,.76);margin-top:3px}
.splash .enter{
  margin-top:26px;min-width:172px;min-height:var(--tap);
  display:inline-flex;align-items:center;justify-content:center;
  background:#fff;color:var(--teal-d);border-radius:999px;
  padding:13px 34px;font-weight:700;font-size:.95rem;
  box-shadow:0 10px 26px -12px rgba(0,0,0,.5);
}
.splash .enter:active{transform:translateY(1px)}
/*  While it is up, nothing behind it may scroll — a phone that scrolls the
    page under a full-screen overlay feels broken even when nothing is. */
html.splashing,html.splashing body{overflow:hidden;height:100%}
/*  Nothing behind it, at any width. It is a full-screen overlay at z-index
    100, so the header, the tab bar, the WhatsApp button and the footer are all
    underneath it already — but they are hidden outright rather than merely
    covered, because a translucent notch, a rounded corner or a scrollbar is
    enough to let one of them show through the edge of the screen. */
html.splashing .nav,html.splashing .tabbar,html.splashing .wafloat,
html.splashing footer,html.splashing #svcbar{display:none}
/*  On a desktop the type has room to be the size the screen deserves. */
@media(min-width:900px){
  .splash .in{max-width:560px}
  .splash .lg{width:148px}
  .splash h1 span{font-size:2.1rem}
  .splash h1 em{font-size:2.6rem}
  .splash .sub{font-size:.95rem;max-width:40ch}
  .splash .figs b{font-size:1.2rem}
  .splash .figs span{font-size:.7rem}
}

/* ── the tab bar ─────────────────────────────────────────────────────────
   Trays · Build · [Menu] · Cart · Account, with the middle tab raised into a
   disc that breaks the top edge. The centre of the bar is the easiest thing
   on a phone to hit with a thumb, and the menu is what people came for. */
.tabbar{
  position:fixed;left:0;right:0;bottom:0;z-index:57;
  display:grid;grid-template-columns:repeat(5,1fr);
  background:rgba(250,247,241,.97);backdrop-filter:blur(14px);
  border-top:1px solid var(--line);
  padding-bottom:env(safe-area-inset-bottom);
  /*  The raised disc has to be allowed out of the box it is drawn in. */
  overflow:visible;
}
.tabbar .tb{
  /*  .tb, not .tab: the account page has owned .tab since it was written —
      11px of padding and a 2.5px bottom border — and every tab down here was
      quietly inheriting both, which drew a teal underline under whichever
      one was active. */
  height:var(--tabbar-h);position:relative;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
  color:var(--ink-3);font-size:.63rem;font-weight:600;letter-spacing:.01em;
}
.tabbar .tb svg{width:21px;height:21px;fill:none;stroke:currentColor;stroke-width:1.75;stroke-linecap:round;stroke-linejoin:round}
/*  The account mark's slot. It holds an outline most of the time and a
    photograph once somebody signs in, so it has to be a fixed box either
    way or the tab jumps a pixel the moment they do. */
.tabbar .tb .tbmark{width:21px;height:21px;display:grid;place-items:center}
.tabbar .tb .tbmark .avatar{width:21px;height:21px;border-radius:50%;object-fit:cover}
/*  THE MARK ABOVE THE CURRENT TAB.

    Colour alone was doing this job, and colour alone is not enough on a bar of
    five small grey icons — the difference between teal and grey at 21px, on a
    phone held at arm's length, is not something you notice while walking.
    Bites & Barrel draws a short bar across the top edge of the live tab and
    the client asked for the same: "pink line appears on top of that tab and it
    changes with every click".

    Same geometry as theirs — 4px, inset to the middle 56%, rounded at the
    bottom only, so it reads as the tab reaching up into the page rather than
    as a border on the bar. */
.tabbar .tb.on{color:var(--teal)}
.tabbar .tb.on::before{
  content:"";position:absolute;top:0;left:22%;right:22%;height:4px;
  border-radius:0 0 4px 4px;background:var(--teal);
}
/*  Not on the raised disc. A teal circle breaking the top edge of the bar is
    already the most conspicuous thing on it; a bar above it would be drawn
    across the disc's own shadow. */
.tabbar .tb.mid.on::before{display:none}
.tabbar .tb.on svg{stroke-width:2}
.tabbar .tb.mid .ring{color:#fff}
.tabbar .tb.mid .ring{
  width:46px;height:46px;border-radius:50%;background:var(--teal);color:#fff;
  display:grid;place-items:center;margin-top:-22px;
  box-shadow:0 8px 20px -8px rgba(14,116,128,.7);border:3px solid var(--cream);
}
.tabbar .tb.mid .ring svg{width:22px;height:22px;stroke-width:2.1}
.tabbar .tb.mid span:last-child{margin-top:-1px}
/*  The cart count, in the same teal pill the header uses, so the two are
    obviously the same number and not two different counts. */
.tabbar .tbadge{
  position:absolute;top:6px;left:calc(50% + 4px);min-width:17px;height:17px;padding:0 4px;
  border-radius:999px;background:var(--teal);color:#fff;
  font-size:.6rem;font-weight:700;font-style:normal;display:grid;place-items:center;line-height:1;
}
@media(min-width:900px){.tabbar{display:none}}

/*  Room for it, and for everything that used to sit on the bottom edge alone.
    The cart bar keeps its job — it says what is in the cart and takes you
    there — but it now stacks ON the tab bar instead of over it. */
@media(max-width:899px){
  body{padding-bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom))}
  .cartbar{bottom:calc(var(--tabbar-h) + env(safe-area-inset-bottom));padding-bottom:12px}
  body.hasbar{padding-bottom:calc(var(--tabbar-h) + 78px + env(safe-area-inset-bottom))}
  .wafloat{bottom:calc(var(--tabbar-h) + 14px + env(safe-area-inset-bottom))}
  body.hasbar .wafloat{bottom:calc(var(--tabbar-h) + 88px + env(safe-area-inset-bottom))}
  /*  Cart and account are two of the five tabs now. Keeping them in the
      header as well is the same door drawn twice, and it was the client's
      first note on the design: "dont need cart and profile icon on top
      right". The burger stays — How it works, Food safety and Contact have
      no tab, and the footer is a long way down. */
  .nav .icons #cartBtn,.nav .icons #userMenuWrap{display:none}
  /*  AND THE BURGER WITH THEM.

      It was kept on the reasoning that How it works, Food safety and Contact
      have no tab. They do not need one: all three are in the footer, which is
      the bottom of every page, and Bites & Barrel settles the question — below
      900px its top bar is the logo and nothing else (`.topnav, .topicons
      { display:none }`), with the five tabs carrying the app and the footer
      carrying the rest.

      Client, 10 September: "why do we need this on top right in mobile view?"
      We do not. A phone header with one control that opens a list of three
      pages, under a five-tab bar that already answers "where can I go", is a
      second navigation for a site that has one.

      Hidden, not deleted: app.js binds #burger and #drawer by id on load. */
  .nav .icons .burger{display:none}
}

/* ── Trays ───────────────────────────────────────────────────────────────
   B1, chosen 10 September. The home page's own card with the compartment pill
   dropped — the photograph already shows three wells or eight — and the
   "Browse 3CP →" link promoted to a full-width teal button, which is the
   biggest tap target a card of this width can carry.

   The copy, the prices and the photographs are the home page's, read from the
   same catalogue. Two screens describing one tray in two sentences is how one
   of them quietly goes out of date. */
/*  .trayformats, NOT .traylist.

    .traylist was already taken TWICE — the contents list on a menu card
    (`Main / Rice-roti / Dessert`) and the customiser's compartment list,
    `#cSlots`. Both are single-column lists; this was a three-across card grid
    declared later in the same file with no more specificity, so it won.

    The damage was not subtle and I did not spot it: on any screen over 700px
    the compartments inside the customiser were laid out LEFT TO RIGHT and
    overflowed their own panel — 572px of content in a 431px box, with a
    horizontal scrollbar under it — and the three contents rows on every menu
    card went side by side too. Client, 10 September: "instead of left to right
    as in desktop, it should be top to down as is in the mobile."

    Third collision of the day, after .rule and .tab. The lesson is the same
    each time: this stylesheet is one long file with no scoping, so a new class
    has to be checked against it before it is used, not after. */
.trayformats{display:grid;gap:14px}
@media(min-width:700px){.trayformats{grid-template-columns:repeat(2,1fr);gap:16px}}
@media(min-width:1000px){.trayformats{grid-template-columns:repeat(3,1fr)}}
.trayformats .card{display:flex;flex-direction:column}
.trayformats .card .body{flex:1;display:flex;flex-direction:column}
.btn-browse{width:100%;margin-top:auto;padding-top:13px}
.btn-browse .btn{width:100%;justify-content:center}

/*  The hero's single call to action. With "How it works" gone there is one
    button, and one button left-aligned under a paragraph reads as the start of
    a row that never arrived. Centred on a phone, where the copy is centred
    against nothing anyway; left on a desktop, where it sits under a
    left-aligned column and lines up with the sentence above it. */
.hero .actions.one{justify-content:center}
@media(min-width:900px){.hero .actions.one{justify-content:flex-start}}

/*  The folded half of the address form — city, pincode, landmark. All three
    are written by the reverse geocode the moment a pin lands, so they are a
    correction, not a question. */
.morefields{border:1px solid var(--line);border-radius:12px;background:var(--cream);margin-bottom:14px}
.morefields>summary{
  list-style:none;cursor:pointer;padding:12px 14px;
  font-size:.86rem;font-weight:600;color:var(--ink-2);
  display:flex;align-items:center;justify-content:space-between;min-height:var(--tap);
}
.morefields>summary::-webkit-details-marker{display:none}
.morefields>summary::after{content:"Edit";color:var(--teal);font-weight:600}
.morefields[open]>summary::after{content:"Done"}
.morefields[open]>summary{border-bottom:1px solid var(--line)}
.morefields>*:not(summary){padding:0 14px}
.morefields[open]>.row2{padding-top:14px}
.morefields[open]>.field:last-child{padding-bottom:14px}
