/* ============================================================
   menu.css — public menu (browse + item detail)
   Rebuilt list/layout: one cohesive sticky bar + a single centred,
   print-menu-style column (never ragged). Item-detail / filter-modal /
   item-sheet blocks below are shared with item.php and kept intact.
   Tokens only — no raw hex.
   ============================================================ */

/* No page header/banner — go straight to the day-part tabs so the menu reads
   immediately. (An SR-only <h1> keeps the page accessible + indexable.) */
.section--tight-top { padding-top: clamp(8px, 1.6vw, 14px); }

/* ===== ONE sticky bar: day-part tabs + tools (row 1), category jump (row 2) =====
   Replaces the previous three stacked sticky layers. Sits directly under the
   site nav (72px mobile / 78px desktop). Sections clear it via scroll-margin. */
/* Sticky offsets match the real nav heights (.nav__in: 84px, 92px >=980px) —
   anything smaller tucks the pinned bar under the nav's bottom border. */
.menu-bar { position: sticky; top: 84px; z-index: 30; background: var(--bg); margin: 0 0 8px; }
@media (min-width: 980px) { .menu-bar { top: 92px; } }
.menu-bar__top { display: flex; align-items: center; justify-content: space-between; gap: 12px 16px; flex-wrap: wrap; padding: 8px 0; }

/* day-part tabs — elegant serif, underline-active (overrides the components base) */
.menu-tabs { display: flex; gap: 2px; flex-wrap: nowrap; border: 0; margin: 0; overflow-x: auto; scrollbar-width: none; }
.menu-tabs::-webkit-scrollbar { display: none; }
.menu-tab { font-family: var(--font-display); font-size: clamp(1rem, 2.4vw, 1.14rem); padding: 8px 14px; color: var(--sage); border: 0; border-bottom: 2px solid transparent; background: none; cursor: pointer; white-space: nowrap; transition: color 0.15s; }
.menu-tab:hover { color: var(--ink-2); }
.menu-tab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--accent); }

/* tools — search (icon + input) + filters button */
.menu-tools { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.menu-search { display: inline-flex; align-items: center; gap: 8px; height: 40px; padding: 0 14px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); color: var(--sage); flex: 0 1 240px; min-width: 0; transition: border-color 0.15s; }
.menu-search:focus-within { border-color: var(--accent); color: var(--ink); }
.menu-search svg { flex: none; }
.menu-search input { border: 0; background: none; outline: none; width: 100%; min-width: 0; font-family: inherit; font-size: 0.9rem; color: var(--ink); }
.menu-filterbtn { display: inline-flex; align-items: center; gap: 8px; height: 40px; padding: 0 16px; border: 1.5px solid var(--ink); border-radius: 999px; background: transparent; color: var(--ink); font-family: var(--font-body); font-size: 0.72rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; cursor: pointer; transition: 0.18s; white-space: nowrap; flex: none; }
.menu-filterbtn:hover { background: var(--ink); color: var(--cream); }
.menu-filterbtn__badge { display: inline-flex; align-items: center; justify-content: center; min-width: 19px; height: 19px; padding: 0 5px; border-radius: 999px; background: var(--danger); color: var(--white); font-size: 0.66rem; letter-spacing: 0; }
.menu-filterbtn__badge[hidden] { display: none; }
/* mobile-first: icon-only filter button; reveal the "Filters" label from 561px up */
.menu-filterbtn__txt { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
@media (min-width: 561px) { .menu-filterbtn__txt { position: static; width: auto; height: auto; overflow: visible; clip: auto; } }

/* category jump-nav (row 2 of the sticky bar) — scroll-spy links. 7/21 calm
   pass: the 13 bordered pill-buttons read as heavy UI chrome; quiet text links
   in ONE scrollable row (never wrapping into a two-row cluster) match the
   day-part tabs' language and calm the whole bar. */
.menu-catnav { margin: 0; padding: 0 0 8px; }
.menu-catnav__scroll { display: flex; gap: 20px; overflow-x: auto; scrollbar-width: none; padding: 2px 0; }
.menu-catnav__scroll::-webkit-scrollbar { display: none; }
.menu-catnav a { flex: 0 0 auto; display: inline-flex; align-items: center; min-height: 32px; padding: 4px 0; border: 0; border-bottom: 2px solid transparent; background: none; color: var(--ink-2); font-size: 0.78rem; font-weight: 500; letter-spacing: 0.04em; white-space: nowrap; transition: 0.15s; }
.menu-catnav a:hover { color: var(--ink); }
.menu-catnav a[aria-current="true"] { color: var(--ink); border-bottom-color: var(--accent); }
.menu-catnav a[hidden] { display: none; }

.menu-panel[hidden] { display: none; }

/* quiet print-PDF link at the FOOT of each menu (not wasting space up top) */
.menu-panel__pdf { max-width: 720px; margin: 24px auto 0; text-align: center; }
.menu-pdf { display: inline-flex; align-items: center; gap: 8px; padding: 9px 0; font-size: 0.78rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--sage-ink, var(--olive-ink)); border-bottom: 1.5px solid var(--accent); white-space: nowrap; }
.menu-pdf:hover { color: var(--ink); }

/* ===== visual card grid (mirrors /market, task 567) ===== */
.menu-list { margin: 8px auto 0; }
.menu-section { margin-top: 44px; scroll-margin-top: 160px; }
.menu-section:first-child { margin-top: 24px; }
.menu-section > h3 { font-family: var(--font-display); font-weight: 500; font-size: clamp(1.3rem, 3vw, 1.6rem); margin: 0 0 16px; padding-bottom: 12px; border-bottom: 1px solid var(--line); }

/* Uniform card grid: fixed-ratio media (photo or the branded tonal panel) +
   a 2-line-clamped description, so cards line up at every breakpoint.
   min-width:0 guards against grid blowout from long unbroken names. */
.menu-grid { display: grid; gap: 12px; grid-template-columns: repeat(2, 1fr); }
.menu-grid > * { min-width: 0; }
@media (min-width: 560px)  { .menu-grid { gap: 16px; grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 900px)  { .menu-grid { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 1180px) { .menu-grid { grid-template-columns: repeat(5, 1fr); } }

.menu-item { display: flex; flex-direction: column; height: 100%; position: relative; transition: border-color 0.15s, box-shadow 0.15s; }
.menu-item:hover { border-color: var(--taupe); box-shadow: 0 6px 20px rgba(20,21,15,0.06); }
.menu-item .card__media { aspect-ratio: 4 / 3; }
.menu-item .card__body { flex: 1; display: flex; flex-direction: column; gap: 5px; padding: 10px; }
/* Stack title over price on narrow 2-col cards so a long caps title (e.g.
   "GRASS-FED TRI TIP") can't squeeze/clip the price. Side-by-side returns >=980px. */
.menu-item .card__row { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; }
@media (min-width: 980px) { .menu-item .card__row { flex-direction: row; justify-content: space-between; align-items: baseline; gap: 8px; } }
/* stretched link: the whole card opens the item detail sheet */
/* Small caps titles in the narrow 2-col grid: tighten tracking so single words like
   "CHOCOLATE" fit, and break at spaces (break-word) instead of orphaning a letter
   (overflow-wrap:anywhere broke "CHOCOLAT|E"). letter-spacing overrides the global caps rule. */
.menu-item__body { text-decoration: none; font-size: 0.96rem; font-weight: 500; line-height: 1.2; color: var(--ink); overflow-wrap: break-word; letter-spacing: 0.02em; }
/* stretched link on the TITLE only (whole card opens details); action buttons sit
   above it at z-index 2 so Add/Choose are clickable without opening the sheet */
.menu-item .card__title::after { content: ""; position: absolute; inset: 0; z-index: 1; }
.menu-item__body:hover { color: var(--accent); }
.menu-item__body:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--r); }
.menu-card__actions { position: relative; z-index: 2; margin-top: auto; padding-top: 10px; }
.menu-card__actions form { margin: 0; }
.menu-card__actions .btn--sm { min-height: 44px; }
.menu-card__note { font-size: 0.66rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--sage); }
.menu-item .price { font-size: 0.94rem; }
.menu-card__desc { color: var(--ink-2); font-size: 0.88rem; line-height: 1.4; margin: 0; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; min-height: 2.8em; }
.menu-card__flags { display: flex; flex-wrap: wrap; gap: 5px; margin: 1px 0 0; position: relative; z-index: 2; }
.menu-item--soldout { opacity: 0.72; }
.tag--out { color: var(--danger); border-color: var(--danger); }
.tag--age { color: var(--sage); border-color: var(--sage); }

/* Compliance / footnotes. */
.menu-foot { max-width: 720px; margin: 64px auto 0; padding-top: 32px; border-top: 1px solid var(--line); }
.menu-foot p { color: var(--ink-2); font-size: 0.9rem; line-height: 1.6; max-width: 74ch; margin: 0 0 12px; }
.menu-foot .star { color: var(--text-muted); font-style: italic; font-family: var(--font-display); }

.catering-cta { max-width: 720px; margin: 32px auto 0; }
.menu-endcta { max-width: 720px; margin: 32px auto 0; justify-content: center; }

/* ============ ITEM DETAIL (item.php) — shared, unchanged ============ */
.item-wrap { padding-bottom: clamp(40px, 6vw, 80px); }

.crumb { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; font-size: 0.78rem; letter-spacing: 0.04em; color: var(--sage); padding: 18px 0 0; }
.crumb a { color: var(--ink-2); }
.crumb a:hover { color: var(--ink); }
.crumb svg { opacity: 0.6; }

.item { display: grid; grid-template-columns: 1fr; gap: 28px; padding: clamp(20px, 4vw, 40px) 0 0; align-items: start; }
.item__media { display: grid; gap: 14px; }
.item__photo { aspect-ratio: 4/3; overflow: hidden; border: 1px solid var(--line); border-radius: var(--r); background: var(--cream-2); }
/* <picture> is inline by default and won't reliably fill a sized ancestor —
   the classic cause of an image hugging one corner with dead space beside it.
   Make it a real block box the same size as .item__photo, THEN size the img
   to that. */
.item__photo picture { display: block; width: 100%; height: 100%; }
/* The whole photograph must be visible and centered, on any source aspect
   ratio (the 8/5 shoot is largely landscape, the library also holds portrait
   frames, and Square-hosted images have their own dimensions) — contain is
   the only object-fit value that is correct for BOTH: it always shows the
   entire image, letterboxed against .item__photo's own background rather
   than cropping the subject out of frame. object-position's default (center)
   already centers on both axes, stated explicitly here so it can't drift. */
.item__photo img { display: block; width: 100%; height: 100%; object-fit: contain; object-position: center; }
.item__photo-ph { width: 100%; height: 100%; background: var(--cream-2); }
/* monogram placeholder when an item has no real photo (no broken image / alt text) */
.item__photo--ph { display: flex; align-items: center; justify-content: center; background: radial-gradient(circle at 50% 38%, var(--cream) 0%, var(--cream-2) 100%); }
.item__photo--ph span { font-family: var(--font-display); font-size: clamp(2.2rem, 7vw, 3.6rem); line-height: 1; color: var(--taupe); }

.item__thumbs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.item__thumb { display: block; width: 100%; padding: 0; aspect-ratio: 1; overflow: hidden; border: 1px solid var(--line); border-radius: var(--r); background: var(--cream-2); cursor: pointer; transition: border-color 0.15s, box-shadow 0.15s; }
.item__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.item__thumb:hover { border-color: var(--taupe); }
.item__thumb.is-active { border-color: var(--olive); box-shadow: 0 0 0 1px var(--olive); }
.item__thumb:focus-visible { outline: 2px solid var(--olive); outline-offset: 2px; }

.item__head { display: flex; justify-content: space-between; align-items: baseline; gap: 18px; }
.item__name { font-family: var(--font-display); font-size: clamp(1.9rem, 3.6vw, 2.8rem); font-weight: 500; line-height: 1.06; }
.item__price { font-family: var(--font-display); font-style: italic; color: var(--ink); font-size: clamp(1.4rem, 2.4vw, 1.8rem); white-space: nowrap; }
.item__desc { color: var(--ink-2); margin: 16px 0 18px; max-width: 54ch; }

.mods { margin-top: 8px; }
.modgroup { border: 0; margin: 0; padding: 22px 0; border-top: 1px solid var(--line); }
.modgroup__head { display: flex; justify-content: space-between; align-items: baseline; gap: 14px; margin-bottom: 14px; }
.modgroup__title { font-family: var(--font-display); font-size: 1.25rem; font-weight: 500; padding: 0; }
.modgroup__req { font-size: 0.68rem; letter-spacing: 0.1em; text-transform: uppercase; font-weight: 600; color: var(--danger); white-space: nowrap; }
.modgroup__opt { font-size: 0.68rem; letter-spacing: 0.1em; text-transform: uppercase; font-weight: 600; color: var(--sage); white-space: nowrap; }

.opt { display: flex; align-items: center; gap: 13px; padding: 11px 14px; border: 1px solid var(--line); border-radius: var(--r); background: var(--surface); cursor: pointer; margin-bottom: 9px; transition: border-color 0.15s, background 0.15s; }
.opt:hover { border-color: var(--taupe); }
.opt:has(input:checked) { border-color: var(--olive); background: var(--cream-2); }
.opt:has(input:focus-visible) { outline: 2px solid var(--olive); outline-offset: 2px; }
.opt input { width: 20px; height: 20px; min-height: 0; accent-color: var(--olive); flex: none; cursor: pointer; }
.opt__label { flex: 1; font-weight: 500; font-size: 0.97rem; }

/* THE OPTION'S OWN PHOTOGRAPH, ON THE ROW (client call, 2026-08-17). Renato, working
   through the Build Your Own Board: "I would imagine I prefer to, like, see before I
   click on it... for the ones that do have pictures, I don't wanna have to click into it
   to see what it is." Selecting a choice already swapped the big photo above (8/12), but
   that only answers the question after you have committed to it.

   Only rows that HAVE a photo get the class, so a group where nobody has assigned any
   still renders as the plain list it always did rather than a column of grey squares.
   Rows within one group can differ; the thumbnail is a fixed size so the text baselines
   stay on the same rhythm either way. */
.opt--photo { padding-block: 8px; }
.opt__thumb {
  flex: 0 0 auto;
  width: 46px;
  height: 46px;
  overflow: hidden;
  border-radius: var(--r);
  background: var(--bg-soft);
}
.opt__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (min-width: 560px) {
  .opt__thumb { width: 54px; height: 54px; }
}
.opt__note { display: block; font-weight: 400; font-size: 0.84rem; color: var(--sage); margin-top: 1px; }
.opt__price { font-family: var(--font-display); font-style: italic; color: var(--ink); font-size: 1rem; white-space: nowrap; }

.item__notes { margin-bottom: 0; }

.buy { position: sticky; bottom: 0; background: var(--cream); border-top: 1px solid var(--line); margin-top: 26px; padding: 16px 0; z-index: 10; }
.buy__row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.buy__add { flex: 1; min-width: 200px; }
/* Cart-edit variant of the SAME sticky .buy bar: Save/Cancel replace Add to
   order — the one difference between the two modal variants (item.php). */
.buy__editbtns { display: flex; gap: 10px; flex: 1; min-width: 200px; }
.buy__editbtns .btn { flex: 1; }

.buy .stepper { display: inline-flex; align-items: center; border: 1.5px solid var(--ink); border-radius: var(--r); overflow: hidden; }
.buy .stepper__btn { width: 46px; height: 46px; background: transparent; border: 0; cursor: pointer; color: var(--ink); font-size: 1.3rem; line-height: 1; display: flex; align-items: center; justify-content: center; font-family: var(--font-body); }
.buy .stepper__btn:hover { background: var(--ink); color: var(--cream); }
.buy .stepper__btn:disabled { opacity: 0.35; cursor: not-allowed; }
.buy .stepper__btn:disabled:hover { background: transparent; color: var(--ink); }
.buy .stepper input { width: 44px; height: 46px; text-align: center; border: 0; border-left: 1px solid var(--line); border-right: 1px solid var(--line); font-family: var(--font-display); font-size: 1.1rem; background: var(--surface); color: var(--ink); }

.buy__total { font-family: var(--font-display); font-style: italic; font-size: 1.05rem; color: var(--ink); margin: 12px 0 0; }
.buy__total b { font-style: normal; font-weight: 600; color: var(--ink); font-size: 1.15rem; }

.form-error { color: var(--danger); font-size: 0.86rem; margin: 10px 0 0; display: none; }
.form-error.is-shown { display: block; }
/* One line per failing group, stacked in group order. */
.form-error__line { margin: 0 0 4px; }
.form-error__line:last-child { margin-bottom: 0; }

@media (min-width: 980px) {
  .item { grid-template-columns: 1fr 1fr; gap: clamp(28px, 5vw, 64px); }
  .item__media { position: sticky; top: 96px; }
}

/* ===== filters/sort modal (shared) ===== */
.menu-fmodal { padding: 0; border: 0; background: transparent; max-width: none; max-height: none; width: 100%; height: 100%; margin: 0; }
.menu-fmodal:not([open]) { display: none; }
.menu-fmodal::backdrop { background: rgba(35,37,31,0.5); backdrop-filter: blur(2px); }
.menu-fmodal__panel { position: fixed; left: 0; right: 0; bottom: 0; width: 100%; max-height: 90vh; overflow: auto; background: var(--cream); border: 1px solid var(--line); border-radius: 16px 16px 0 0; box-shadow: 0 24px 64px rgba(0,0,0,0.28); display: flex; flex-direction: column; }
@media (min-width: 561px) { .menu-fmodal__panel { left: 50%; top: 50%; right: auto; bottom: auto; transform: translate(-50%,-50%); width: min(440px, 92vw); max-height: 88vh; border-radius: 10px; } }
.menu-fmodal__head { display: flex; align-items: center; justify-content: space-between; padding: 18px 20px; border-bottom: 1px solid var(--line); }
.menu-fmodal__head h2 { font-family: var(--font-display); font-weight: 500; font-size: 1.3rem; margin: 0; }
.menu-fmodal__close { width: 44px; height: 44px; border: 1px solid var(--line); border-radius: 999px; background: var(--paper); color: var(--ink); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
.menu-fmodal__close:hover { background: var(--ink); color: var(--cream); }
.menu-fmodal__body { padding: 18px 20px; display: flex; flex-direction: column; gap: 18px; }
.menu-fmodal__body .field { margin: 0; }
.menu-fmodal__label { display: block; font-size: 0.72rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--sage); font-weight: 600; margin-bottom: 8px; }
.menu-fmodal__foot { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px; border-top: 1px solid var(--line); }
.menu-fmodal__clear { min-height: 40px; padding: 0 14px; border: 0; background: none; color: var(--rose-ink); font-size: 0.74rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; cursor: pointer; }
.diet-chips { display: flex; gap: 10px; flex-wrap: wrap; }
.diet-chip { display: inline-flex; align-items: center; min-height: 44px; padding: 8px 18px; border: 1px solid var(--taupe); border-radius: 999px; background: var(--paper); color: var(--ink-2); font-size: 0.8rem; font-weight: 600; cursor: pointer; transition: 0.18s; }
.diet-chip:hover { border-color: var(--sage-ink); }
.diet-chip[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--cream); }
.menu-fmodal__check { display: flex; align-items: center; gap: 10px; font-size: 0.92rem; font-weight: 500; color: var(--ink-2); cursor: pointer; }
.menu-fmodal__check input { width: 18px; height: 18px; min-height: 0; accent-color: var(--accent); cursor: pointer; }

/* multi-select dropdown (category / dietary) */
.msel { position: relative; }
.msel__btn { display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%; min-height: 46px; padding: 11px 14px; border: 1px solid var(--taupe); border-radius: var(--r); background: var(--white); color: var(--ink); font-family: inherit; font-size: 0.98rem; text-align: left; cursor: pointer; }
.msel__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.msel.is-active .msel__btn { border-color: var(--accent); color: var(--ink); font-weight: 500; }
.msel__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msel__chev { flex: none; color: var(--sage); transition: transform 0.15s; }
.msel__btn[aria-expanded="true"] .msel__chev { transform: rotate(180deg); }
.msel__menu { position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: 5; max-height: 244px; overflow-y: auto; padding: 6px; background: var(--white); border: 1px solid var(--line); border-radius: var(--r); box-shadow: 0 12px 30px rgba(20,21,15,0.14); }
.msel__menu[hidden] { display: none; }
.msel__opt { display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: var(--r); font-size: 0.94rem; color: var(--ink); cursor: pointer; }
.msel__opt:hover { background: var(--cream-2); }
.msel__opt input { width: 18px; height: 18px; min-height: 0; accent-color: var(--accent); cursor: pointer; flex: none; }

/* ===== route-backed item sheet (iframe of /menu/{slug}?embed=1) — shared by
   /menu's Add triggers AND /cart's per-line Edit button ===== */
.item-sheet { padding: 0; border: 0; background: transparent; max-width: none; max-height: none; width: 100%; height: 100%; margin: 0; }
.item-sheet:not([open]) { display: none; }
.item-sheet::backdrop { background: rgba(35,37,31,0.5); backdrop-filter: blur(2px); }
.item-sheet__panel { position: fixed; left: 0; right: 0; bottom: 0; height: 92vh; background: var(--cream); border-radius: 16px 16px 0 0; overflow: hidden; display: flex; flex-direction: column; box-shadow: 0 -10px 40px rgba(0,0,0,0.22); }
/* The close control gets its OWN row, ahead of the iframe in the flex column —
   reserved space, not z-order, so it can never land on top of the embedded
   page's photo, name or price, at any scroll position or width. The iframe's
   box starts BELOW this bar, so nothing inside it can ever render under the
   button in the first place (client escalation, 2026-08-10). */
.item-sheet__bar { flex: none; position: relative; display: flex; align-items: center; justify-content: center; height: 56px; background: var(--cream); border-bottom: 1px solid var(--line); }
.item-sheet__handle { width: 42px; height: 5px; padding: 0; border: 0; border-radius: 999px; background: var(--taupe); cursor: pointer; }
.item-sheet__close { position: absolute; top: 50%; right: 12px; transform: translateY(-50%); width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--line); border-radius: 999px; background: var(--paper); color: var(--ink); cursor: pointer; }
.item-sheet__close:hover { background: var(--ink); color: var(--cream); }
.item-sheet__frame { flex: 1; width: 100%; border: 0; background: var(--cream); }
@media (min-width: 1024px) {
  .item-sheet__panel { left: 50%; top: 50%; right: auto; bottom: auto; transform: translate(-50%,-50%); width: min(960px, 94vw); height: min(88vh, 900px); border-radius: 12px; }
  .item-sheet__handle { display: none; }
}

/* embed mode: drop the page grain + top padding so the iframe content sits flush */
/* Modal item layout (rendered in the sheet). Mobile bottom-sheet = single column:
   a contained photo on top + scrolling details + sticky add bar. Wider sheet =
   two columns (photo | scrolling details) so the photo is never a stretched band. */
body.is-embed::before { display: none; }
body.is-embed .crumb { display: none; } /* the sheet has its own ✕; no breadcrumb */
body.is-embed .item-wrap { padding: 0; max-width: none; min-height: 100vh; }
body.is-embed .item { display: flex; flex-direction: column; min-height: 100vh; padding: 0; gap: 0; }
body.is-embed .item__media { margin: 0; }
/* width:100% is LOAD-BEARING, not decoration. With aspect-ratio and max-height but no
   stated width, the box is shrink-to-fit: when the 40vh cap binds first, the element
   narrows its OWN width to preserve 4/3 and then sits hard against the left edge, so the
   photo reads as off-centre even though the image is centred within it (owner, 8/11).
   Stating the width makes height the derived value instead — the box fills the sheet, the
   cap still limits its height, and object-fit: contain centres the photo inside it. */
body.is-embed .item__photo {
  border: 0;
  border-radius: 0;
  width: 100%;
  aspect-ratio: 4 / 3;
  height: auto;
  /* 34vh, not 40: the thumbnail strip below costs about 6vh and this document is not
     allowed to scroll. A gallery of one has no strip and no need of the difference, so
     it keeps the original height — see the :only-child rule below. */
  max-height: 34vh;
}
/* One photo, no strip, no reason to shrink the photo. */
body.is-embed .item__media:not([data-gallery]) .item__photo { max-height: 40vh; }
/* Same reasoning as the base .item__photo img rule above: the WHOLE photo
   visible and centered, whatever box shape this mode forces it into (this is
   also where the iframe's own internal breakpoint, not the outer dialog's,
   decides the box — a 960px-wide desktop panel with a 0.85fr photo column and
   a 100vh-tall column produces a tall, narrow box for a landscape source;
   contain letterboxes that instead of cropping the dish out of frame). */
body.is-embed .item__photo img { width: 100%; height: 100%; object-fit: contain; object-position: center; }
/* no-photo: a modest monogram banner, not a full-height empty block */
body.is-embed .item__photo--ph { aspect-ratio: 16 / 6; height: auto; max-height: 20vh; }
/* THE THUMBNAILS COME BACK IN THE SHEET (2026-08-26).
   Renato: "When I click on an item and see the modal I wanna be able to see the multiple
   pictures of it not just on the product detail actual page."

   They were hidden here, and for a real reason rather than an oversight: below 720px this
   document must not scroll — only the details column does (client feedback 7/2) — and the
   3-column grid the full page uses is square tiles a third of the width each, which is
   most of a phone screen.

   So they return in a different shape rather than by deleting the rule. A single
   horizontal strip of small fixed tiles, scrolled sideways when there are more than fit:
   it costs ~60px of height whatever the photo count, where the grid cost a row per three.
   The photo above gives up 6vh to pay for it, which is the whole budget. */
body.is-embed .item__thumbs {
  display: flex;
  gap: 8px;
  grid-template-columns: none;      /* undo the base grid */
  overflow-x: auto;
  overscroll-behavior-x: contain;   /* swiping the strip must not swipe the sheet away */
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 10px clamp(18px, 5vw, 24px) 0;
}
body.is-embed .item__thumbs::-webkit-scrollbar { display: none; }
body.is-embed .item__thumb {
  flex: 0 0 auto;
  width: 54px;
  height: 54px;
  scroll-snap-align: start;
}
body.is-embed .item__detail { display: flex; flex-direction: column; flex: 1; padding: 16px clamp(18px, 5vw, 24px) 0; }
body.is-embed .item__desc { margin: 10px 0 14px; }
/* Thumb-reachable + clear of the iOS home-indicator strip (viewport-fit=cover
   in head.php is what makes env() resolve to a real value here, not 0). */
body.is-embed .buy { position: sticky; bottom: 0; margin-top: auto; padding: 12px 0 calc(16px + env(safe-area-inset-bottom)); background: var(--cream); box-shadow: 0 -6px 18px rgba(20,21,15,0.08); }
@media (min-width: 720px) {
  /* Pin the embed document to the sheet: the page itself must never scroll —
     only the details column does. Anything else lets the sheet scroll past
     the content into blank space (client feedback 7/2). */
  body.is-embed { height: 100vh; overflow: hidden; }
  body.is-embed .item-wrap { height: 100vh; min-height: 0; overflow: hidden; }
  body.is-embed .item { display: grid; grid-template-columns: 0.85fr 1.15fr; height: 100vh; min-height: 0; }

  /* THE THUMBNAILS NEED A ROW OF THEIR OWN (2026-08-29).

     Renato: "when I click on it, I don't see the thumbnails for the multiple pictures."
     They were in the markup and they were even reported visible by the DOM -- three of
     them, offsetParent set. They were simply BELOW the sheet: .item__photo was height:100vh
     inside a media column that is also height:100vh, so the photo alone consumed the whole
     column and the strip sat underneath it, past the bottom edge, in a panel that does not
     scroll.

     Two rows instead: the photo takes what is left, the strip takes what it needs.
     minmax(0, 1fr) rather than 1fr because a grid row will otherwise refuse to shrink
     below its content and the photo would push the strip out again. */
  body.is-embed .item__media {
    height: 100vh;
    position: sticky;
    top: 0;
    display: grid;
    grid-template-rows: minmax(0, 1fr) auto;
    gap: 10px;
    padding-bottom: 10px;
  }
  /* max-height:none also LIFTS the 40vh cap the narrow layout puts on a gallery-less
     photo. That cap is right on a phone, where the photo and the options share one
     scrolling column and a tall image buries the choices. In the two-column sheet the
     photo has a column to itself, and capping it there is what left grilled cheese with a
     330px picture in an 824px panel and cream underneath -- half of the "ends vertically
     in the middle" Renato was describing. */
  body.is-embed .item__media:not([data-gallery]) .item__photo,
  body.is-embed .item__photo {
    aspect-ratio: auto;
    height: auto;
    min-height: 0;
    max-height: none;
    /* THE LETTERBOX STOPS READING AS AN EMPTY BOX. object-fit stays `contain`, on purpose:
       the rule above it explains that cropping a photograph of food on a page where
       somebody orders from the picture is not a layout decision to make casually. But a
       3:2 photograph in a tall column leaves bands above and below, and those bands were
       drawn in --cream-2 inside a --line border, on a --cream panel -- so they read as a
       half-empty container rather than as air around a picture. Same ground as the panel
       and no border: the photograph now floats on the sheet, and the column looks
       deliberate at any photo shape. */
    background: var(--cream);
    border: 0;
    border-radius: 0;
  }
  body.is-embed .item__photo--ph { aspect-ratio: auto; height: auto; min-height: 0; max-height: none; }
  body.is-embed .item__thumbs { padding: 0 10px; }

  body.is-embed .item__detail { height: 100vh; overflow-y: auto; overscroll-behavior: contain; padding: clamp(22px, 3vw, 34px) clamp(22px, 3vw, 34px) 0; }

  /* ADD TO ORDER BELONGS AT THE BOTTOM, WHATEVER THE ITEM IS.

     Renato: "if I take an item that doesn't have a modifier, like grilled cheese, the modal
     is really ugly because it ends vertically in the middle. Especially on the right side,
     where you have 'Add to Order' -- that should be all the way at the bottom of the modal."

     .buy already carried margin-top:auto, and it did nothing, because .buy is inside the
     <form> and the form was a plain block. Auto margins push against the FLEX PARENT, and
     the form was not one -- so the bar sat wherever the content ended and everything below
     it was empty cream. Making the form the flex column it was assumed to be is the whole
     fix; the bar has wanted to be at the bottom since it was written. */
  body.is-embed .item__detail > form { display: flex; flex-direction: column; flex: 1; min-height: 0; }
}

/* A build-your-own with no base price says so instead of showing "$0" (7/29:
   trimming the .00 turned "$0.00" into a bare "$0", which reads like a price). */
.menu-item__price-note {
  font-family: var(--font-body);
  font-style: normal;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  white-space: nowrap;
}

/* Search result line. Search spans every day-part (8/5), so results can surface under
   a heading the guest was not looking at — this says how many and where. */
.menu-searchstatus {
  margin: 0;
  padding: 0 0 8px;
  color: var(--text-muted);
  font-size: 0.78rem;
  letter-spacing: 0.02em;
}
.menu-searchstatus[hidden] { display: none; }

/* ===== BEVERAGE LIST (wine, beer) ============================================
   A list with one photograph beside it, replacing the box-per-item grid. Wine
   first (client 8/10: "is there a way to do like just a list and like a click on
   it?"; Renato 8/11: "just a list instead of a box for each and put a picture of
   wines next to the list just make it compact and grouped by grape type"), beer
   on 8/21 (Renato: "so that you don't need to repeat this photo like 8 times";
   April: "Yep. Exactly.").

   Mobile-first: the photograph sits above the list on a phone and moves alongside
   it once there is room. The list is the point, so the photograph never takes more
   than a third of the row. */
.bevlist { display: grid; grid-template-columns: 1fr; gap: clamp(14px, 2.4vw, 26px); }
.bevlist__art { margin: 0; }
.bevlist__art img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  display: block;
  background: var(--bg-soft);
}
@media (min-width: 46em) {
  .bevlist { grid-template-columns: minmax(0, 1fr) 2fr; align-items: start; }
  .bevlist__art img { position: sticky; top: 84px; }
  /* Tall crop beside a long list reads better than a letterbox stranded at the
     top — but only for a photograph shot tall. The beer is six cans across a 3:2
     frame, and cropping that to 3/4 loses the can at each end. */
  .bevlist__art--tall img { aspect-ratio: 3 / 4; }
}
.bevlist__note {
  margin: 0 0 clamp(8px, 1.2vw, 12px);
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.bevlist__grape {
  margin: clamp(14px, 2vw, 22px) 0 clamp(5px, 0.8vw, 8px);
  font-family: var(--font-display);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-size: clamp(0.76rem, 1.3vw, 0.88rem);
  color: var(--ink);
}
.bevlist__grape:first-of-type { margin-top: 0; }
.bevlist__ul { list-style: none; margin: 0; padding: 0; }
/* NO RULE BETWEEN THE ROWS (client, 2026-08-28). April: "This looks almost like an Excel
   sheet, like with the lines and everything... It just looks like a lot of information."
   Stacey: "the problem for us is it looks like a spreadsheet kind of" -- and, asked what
   to take away, "The box."

   Three ruled things per row is what read as cells: a border under every row, a dotted
   leader from the name to the price, and an OUTLINED Add button. All three are gone as
   lines; only the leader is gone as an element. What separates the rows now is space, so
   this list is set on rhythm rather than on a grid.

   WHAT DID NOT CHANGE IS THE BUTTON ITSELF, because they asked for it twice in the same
   breath. Stacey: "I do like the usability of just pushing that button, like it's
   foolproof, right? Which is what we need to do." April: "we like the function of it
   obviously." See .bev__addbtn below for how it stops drawing a line and stays a button. */
.bevlist__ul > li { border-bottom: 0; }
/* The row: name, a rule that eats the slack, price. Baseline-aligned so the rule
   sits on the type rather than through it. */
.bev {
  display: flex;
  align-items: baseline;
  /* The gap IS the separator now that the dotted leader is gone: name on the left, price
     hard right, air between. 5px of padding was legible only because a hairline closed
     each row; without one it reads as a wall of text, so the rows are given room. */
  gap: 14px;
  justify-content: space-between;
  padding: 9px 0;
  color: var(--ink);
  text-decoration: none;
}
/* THE ROW STOPPED BEING A LINK (2026-08-27). It is a <span> now — no detail page, no
   modal — so the hover underline that promised a destination goes with it. */
/* A GRID, NOT A FLEX ROW, and this is a bug fix rather than a preference.
   As a flex row every child of the <li> became a column — including .bev__desc, the
   beer's style-and-brewery line. So the description sat to the RIGHT of the Add button
   and pushed it left by however long the name and price happened to be, which put every
   Add at a different x. Renato: "the misaligned add buttons for beer is terrible."

   Two columns and two rows: name/leader/price on the left, Add pinned to the right in a
   column that lines up down the whole list, and the description spanning both underneath
   where it belongs. */
.bev-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  column-gap: 14px;
}
.bev-row > .bev { grid-column: 1; min-width: 0; }

.bev-row > .bev__desc { grid-column: 1 / -1; }
/* Add sits on the row, not under it: this list is scanned down the left and acted on
   from the right, and a control that reflows below the price doubles the row height on
   the narrow column this list lives in. */
/* One control on the right, fixed width, so the Add buttons line up down the whole list.
   The inline size select is gone — see the note in menu.php: it ate the width the wine
   NAMES needed and wrapped them onto two lines. Choosing a size is the sheet's job. */
/* SOLID AND SMALL, NOT OUTLINED AND BIG. Two things had to be true at once.

   Stacey pointed at this control when she said "I bet you have to have a square around it
   like that" and then "the problem for us is it looks like a spreadsheet". The 1.5px
   border on .btn IS the square, and forty outlined squares stacked down a page draw a
   column of cells.

   But she also said, in the same breath, "I do like the usability of just pushing that
   button, like it's foolproof, right? Which is what we need to do" -- and April, "we like
   the function of it obviously". So the button stays a button.

   A FILL instead of an OUTLINE removes the line without removing the affordance: a filled
   shape reads as pressable at least as strongly as a bordered one and contributes nothing
   to the grid. Rendered at full .btn--sm size that swung too far the other way -- seven
   solid blocks down the right edge is heavier than the outlines were -- so it is also
   smaller, which is Renato's own instinct on the call: "I can try to play around with this
   add button, make it smaller."

   THE HIT TARGET IS NOT THE VISUAL BOX. Shrinking a control that gets tapped with a thumb
   is how you trade one complaint for a worse one, so ::after grows the target back past
   44x44 without drawing anything. The row is position:relative-free, so the pseudo-element
   is anchored to the button itself. */
.bev-row .bev__addbtn {
  position: relative;
  white-space: nowrap;
  justify-self: end;
  grid-column: 2;
  grid-row: 1;
  padding: 6px 13px;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  background: var(--ink);
  border-color: var(--ink);
  color: var(--cream);
}
.bev-row .bev__addbtn::after {
  content: "";
  position: absolute;
  /* -9px vertical on a ~27px box clears 44px; horizontal is already past it. */
  inset: -9px -6px;
}
.bev-row .bev__addbtn:hover,
.bev-row .bev__addbtn:focus-visible { background: var(--ink-deep); border-color: var(--ink-deep); }
.bev__name { flex: 0 1 auto; font-size: clamp(0.86rem, 1.5vw, 0.95rem); }
/* The dotted leader between name and price is the second of April's "lines and
   everything". It kept its element -- it is aria-hidden and removing it from the markup
   would touch a template three sections share -- and lost its border, so it is now the
   flexible gap that pushes the price to the right edge. */
.bev__rule { flex: 1 1 auto; min-width: 12px; border-bottom: 0; }
.bev__price { flex: none; font-size: clamp(0.82rem, 1.4vw, 0.9rem); color: var(--text-muted); font-variant-numeric: tabular-nums; }
.bev--out .bev__name { text-decoration: line-through; color: var(--text-muted); }
.bev__desc {
  margin: 2px 0 8px;
  /* No right padding any more: it used to keep the text clear of an Add button that sat
     on the same line. The button is in its own column now. */
  font-size: 0.78rem;
  line-height: 1.45;
  color: var(--text-muted);
}

/* The link out to the full cellar (April, 8/28). Sits under the 21+ note and above the
   first grape heading. Quiet — it is a signpost, not a call to action competing with the
   Add buttons in the list below it. */
.bevlist__more { margin: 0 0 14px; font-size: 0.86rem; }
.bevlist__more a { color: var(--ink-2); border-bottom: 1px solid var(--line); }
.bevlist__more a:hover, .bevlist__more a:focus-visible { border-bottom-color: var(--ink); }

/* The pickup / 21+ line on an alcohol item (2026-08-28). Quiet and factual — it is a
   condition of sale, not a warning, and it sits under the description rather than beside
   the Add button where it would read as a hesitation. */
.item__legal {
  margin: 0 0 18px;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--text-muted);
}

/* ── THE GRAPE FILTER (April, 2026-08-28) ─────────────────────────────────────────────
   "If I was looking for a sparkling, I could just click in sparkling and then maybe
   that's where they would populate... 138 wines, that's a lot to scroll through."

   Chips, not a <select>. A select hides its own options, which is the wrong shape for a
   control whose whole job is to show that the cellar HAS a sparkling section — April's
   sentence is about discovering the breakdown, not about operating a filter she already
   knows the contents of.

   Deliberately quiet: outlined, ink on the page's own ground, the active one filled. This
   sits directly above the Add buttons and must not compete with them, which is the same
   reason .bevlist__more is a plain link. */
.bevfilter {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 clamp(14px, 2vw, 20px);
}
.bevfilter__chip {
  font-family: var(--font-body);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  /* 8px/14px on a 0.72rem line clears a 34px chip; the row is scanned and tapped, not
     hunted for, and 44px chips at this count would be a wall. The whole row is one
     control and the gap is generous enough that a thumb does not hit two. */
  padding: 8px 14px;
  /* --text-muted, not --line. A chip is a CONTROL and its boundary has to be findable:
     --line is #e7e4dd on a #faf9f6 page, which measured 1.15:1 and read as a smudge.
     This is 4.76:1 -- past the 3:1 WCAG asks of a control boundary, and still quiet
     enough that a row of seven does not compete with the Add buttons below it. */
  border: 1px solid var(--text-muted);
  border-radius: 999px;
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
  text-decoration: none;
  transition: background 0.18s, color 0.18s, border-color 0.18s;
}
.bevfilter__chip:hover { border-color: var(--ink); color: var(--ink); }
.bevfilter__chip.is-active {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--cream);
}
/* Hidden by the filter. A class rather than an inline style so the state is inspectable
   and so `display:none` cannot be left behind on a re-render. */
.bevlist__grape[hidden], .bevlist__ul[hidden] { display: none; }

/* The count on each chip (2026-08-29). It is what makes choosing a grape possible instead
   of defaulting to everything: "Cabernet 12" says what you are about to open and a bare
   word does not. Quiet — a number, not a badge. */
.bevfilter__n {
  font-variant-numeric: tabular-nums;
  opacity: 0.62;
  margin-left: 2px;
  font-size: 0.92em;
}
/* "All" sits at the END of the row and is never the default. First-and-selected is a
   default; last-and-unselected is an escape hatch, and that difference is the whole of
   what was asked for. */
.bevfilter__chip--all { margin-left: auto; }
@media (max-width: 760px) { .bevfilter__chip--all { margin-left: 0; } }

/* NO SECOND COLUMN, AND THAT IS A DECISION RATHER THAN AN OMISSION (2026-08-29).

   Splitting a long grape into two columns halves the section's height, which is what was
   asked for -- and it was built, rendered and measured: 33 bottles went from 1943px to
   644px. It is reverted anyway, because at 1440px the photograph takes the left third and
   each column lands near 300px, which wraps a wine name onto two lines.

   That is the precise thing the client complained about three weeks earlier: "the wrapping
   on wine names looks bad too" (2026-08-28), which is why the inline size select was taken
   out to give the names their width back. Buying vertical space by re-breaking the names
   trades one complaint for the one that came first.

   The length is solved by not showing 128 rows at once -- see the grape default in
   menu.js. Filtered to a grape the longest group is 33 and the median is 12. */

/* Menu not being served right now (April, 2026-09-04: day menu to 4pm, evening
   4pm to close). The dishes stay readable; only ordering stops. Tonal rather than
   an error colour — nothing has gone wrong, the kitchen is simply on a different
   menu. */
.menu-panel__closed {
  margin: 0 0 1.25rem;
  padding: .75rem 1rem;
  border-radius: var(--radius-sm, 4px);
  background: var(--c-sand, #f0ece3);
  color: var(--c-ink, #23211d);
  font-size: .95rem;
  line-height: 1.45;
}
