:root {
  /* The call button and the search field share one height so the topbar reads as a
     single row of controls rather than a tall block beside a short one. */
  --topbar-control-height: 52px;
  --wine-900: #220612;
  --wine-800: #310419;
  --wine-850: #2a0616;
  --wine-bg: #470024;
  --header-burgundy: #430020;
  --wine-700: #4f0f2b;
  --wine-500: #80184a;
  --wine-300: #b2527f;
  --cream: #f9f1ea;
  --card: #fff9f5;
  --text-900: #201015;
  --text-600: #5d4b52;
  --text-500: #7a646d;
  --border: #ead7ce;
  --shadow: 0 10px 28px rgba(0, 0, 0, 0.18);
  --chip-shadow: 0 8px 24px rgba(32, 10, 19, 0.32);
  --texture-size-desktop: 820px;
  --texture-size-mobile: 620px;
}

* {
  box-sizing: border-box;
}

/* Keyboard-only escape hatch past the sticky topbar, search field, language toggle and
   category rail — Brief 5 Task 7. Off-screen until focused; the page's own
   :focus-visible ring below still applies for the outline. */
.skip-link {
  position: absolute;
  top: -60px;
  left: 8px;
  z-index: 2000;
  padding: 12px 20px;
  background: var(--wine-800);
  color: #fff;
  border-radius: 8px;
  font-weight: 700;
  text-decoration: none;
  transition: top 0.15s ease;
}

.skip-link:focus {
  top: 8px;
}

:focus {
  outline: none;
}

:focus-visible {
  outline: 3px solid rgba(248, 208, 168, 0.9);
  outline-offset: 3px;
  border-radius: 3px;
}

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  min-height: 100vh;
  font-family: "Manrope", "Segoe UI", sans-serif;
  color: var(--text-900);
  /* No background-color fallback: the last gradient layer below is fully opaque with no
     transparent stops, so it alone always covers the box — the shorthand's own reset of
     background-color to transparent has nothing left to matter. */
  background:
    radial-gradient(circle at 8% 10%, rgba(255, 70, 146, 0.1), transparent 24%),
    radial-gradient(circle at 100% 0%, rgba(233, 196, 174, 0.08), transparent 38%),
    radial-gradient(circle at 0% 100%, rgba(164, 66, 112, 0.16), transparent 32%),
    linear-gradient(148deg, #2a0015 0%, #420021 58%, #330019 100%);
  padding-bottom: calc(72px + env(safe-area-inset-bottom));
}

.texture {
  position: fixed;
  inset: 0;
  pointer-events: none;
  opacity: 0.1;
  background-image: url("../assets/background.png");
  /* 504 KB -> 8 KB. The plain url above is what a browser without image-set()
     reads; everything since Safari 17 takes the WebP and never fetches the PNG. */
  background-image: image-set(
    url("../assets/background.webp") type("image/webp"),
    url("../assets/background.png") type("image/png"));
  background-size: var(--texture-size-desktop);
  background-position: center top;
  z-index: -1;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 10;
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  background: linear-gradient(180deg, rgba(67, 0, 32, 0.98), rgba(67, 0, 32, 0.9));
  border-bottom: 1px solid rgba(255, 255, 255, 0.14);
  transition: transform 0.25s ease;
}

/* Task 11.1: reclaim vertical space on a 60+ dish page by sliding the topbar off
   on scroll-down and back on scroll-up (menu/app.js's bindHeaderCollapse). The rail
   is translated up by exactly the topbar's own measured height (--topbar-height,
   already kept live by ResizeObserver for the sticky-offset math elsewhere) rather
   than given its own top:0, so this never duplicates that measurement. transform
   leaves sticky positioning itself untouched — only paint position moves — so
   updateActiveCategoryFromScroll's live getBoundingClientRect() reads stay correct
   with no changes there. */
body.menu-header-collapsed .topbar {
  transform: translateY(-100%);
}

.topbar-inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 12px 16px;
  display: grid;
  /* minmax(0, …), not the implicit auto column: an auto column is at least as wide as its
     widest unbreakable content, so on a 360px phone the search field's intrinsic width
     pushed the PT/EN toggle 28px off the right edge (the page scrolled sideways). */
  grid-template-columns: minmax(0, 1fr);
  gap: 8px;
}

.brand-copy {
  min-width: 0;
}

.brand-wrap {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.back-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 8px;
  color: var(--cream);
  font-size: 1.1rem;
  cursor: pointer;
  transition: background 0.2s;
  padding: 0;
}

.back-btn:hover {
  background: rgba(255, 255, 255, 0.18);
}

.brand-logo {
  width: 74px;
  height: 74px;
  object-fit: contain;
  background: transparent;
  border: 0;
  border-radius: 0;
  padding: 0;
  box-shadow: none;
}

.brand-copy h1 {
  margin: 0;
  color: var(--cream);
  font-family: "Prata", Georgia, serif;
  /* Fluid so the title and the store name stay on one line through the tablet range,
     where the search column takes its 430px maximum and squeezes this one. */
  font-size: clamp(2.1rem, 2.9vw, 2.6rem);
  font-weight: 400;
  text-align: center;
  letter-spacing: 0.02em;
  line-height: 1;
}

/* The store name sits on the title's line, not under it. Stacked, it wedged a filled
   pill between the title and its subtitle and added a third line to the brand block —
   which pushed the whole topbar from 111px to 163px at tablet widths. */
.brand-title-row {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 4px 12px;
}

.order-helper {
  margin: 5px 0 0;
  color: rgba(249, 241, 234, 0.9);
  font-size: 0.76rem;
  line-height: 1.28;
  max-width: 58ch;
}

.phone-cta {
  text-decoration: none;
  color: var(--cream);
  border-radius: 14px;
  border: 1px solid rgba(249, 241, 234, 0.35);
  background: linear-gradient(160deg, rgba(128, 24, 74, 0.88), rgba(79, 15, 43, 0.94));
  box-shadow: var(--chip-shadow);
}

.desktop-phone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: var(--topbar-control-height);
  padding: 0 14px;
  min-width: 190px;
  justify-self: end;
  text-align: center;
}

.phone-title {
  display: block;
  font-size: 0.96rem;
  font-weight: 800;
  line-height: 1.2;
}

.phone-sub {
  display: block;
  margin-top: 1px;
  font-size: 0.84rem;
  font-weight: 700;
  opacity: 0.96;
}

.phone-open {
  display: block;
  margin-top: 1px;
  font-size: 0.68rem;
  opacity: 0.88;
}

.kicker {
  margin: 0 0 2px;
  font-size: 0.67rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 237, 227, 0.8);
}

.topbar-row2 {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
}

.lang-toggle {
  display: flex;
  flex-shrink: 0;
  border-radius: 8px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.25);
}

.lang-btn {
  background: transparent;
  color: rgba(255, 255, 255, 0.55);
  border: none;
  padding: 0 12px;
  height: 38px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}

.lang-btn.active {
  background: rgba(255, 255, 255, 0.18);
  color: #fff;
}

.lang-btn:hover:not(.active) {
  background: rgba(255, 255, 255, 0.08);
  color: rgba(255, 255, 255, 0.8);
}

.search {
  display: flex;
  align-items: center;
  gap: 10px;
  background: rgba(255, 255, 255, 0.95);
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.7);
  height: var(--topbar-control-height);
  padding: 0 12px;
  min-width: 0;
  flex: 1;
}

.search-icon {
  display: inline-flex;
  align-items: center;
  font-size: 1.1rem;
  opacity: 0.7;
  color: var(--text-600, #5d4b52);
}

.search input {
  flex: 1;
  /* An input's default width is about 20 characters; without this it will not shrink below
     that, and the row it sits in overflows narrow phones. */
  min-width: 0;
  width: 100%;
  border: 0;
  outline: 0;
  /* 16px, and not a rounded-down rem.
     iOS Safari zooms the whole page in when a focused input computes below 16px, and does
     not zoom back out on blur — so tapping search left the reader at a magnification they
     never asked for, on top of everything else that moved. This measured 14.72px. */
  font-size: 16px;
  font-family: inherit;
  background: transparent;
  color: var(--text-900);
}

/* Two clear buttons is worse than one: iOS draws its own inside type="search", and the
   explicit control below has to work on Android, where iOS's does not exist. */
.search input::-webkit-search-cancel-button,
.search input::-webkit-search-decoration {
  -webkit-appearance: none;
  appearance: none;
}

.search-clear {
  flex: none;
  display: inline-grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin-right: -8px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--text-600);
  cursor: pointer;
}
.search-clear svg { width: 18px; height: 18px; }
.search-clear:hover { background: rgba(32, 16, 21, 0.08); color: var(--text-900); }

/* The way back from a search that found nothing. */
.status-action {
  display: block;
  margin: 18px auto 0;
  min-height: 48px;
  padding: 0 22px;
  border-radius: 12px;
  border: 1px solid rgba(249, 241, 234, 0.35);
  background: linear-gradient(160deg, rgba(128, 24, 74, 0.88), rgba(79, 15, 43, 0.94));
  color: var(--cream);
  font: inherit;
  font-size: 0.95rem;
  font-weight: 700;
  cursor: pointer;
}
.status-action:hover { background: var(--wine-500); }

/* search uses focus-within since the input is inside the label */
.search:focus-within {
  outline: 3px solid rgba(248, 208, 168, 0.95);
  outline-offset: 2px;
}

.page {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 16px 22px;
}

.category-rail {
  position: sticky;
  top: calc(var(--topbar-height, 102px) - 1px);
  z-index: 12;
  padding-top: 6px;
  margin-bottom: 28px;   /* room under the chips, before the card or the first heading (was 14px) */
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  background: linear-gradient(180deg, rgba(67, 0, 32, 0.82), rgba(67, 0, 32, 0.62));
  border-radius: 0;
  transition: transform 0.25s ease;
}

body.menu-header-collapsed .category-rail {
  transform: translateY(calc(-1 * var(--topbar-height, 102px)));
}

.category-rail::before,
.category-rail::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 18px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease;
}

.category-rail::before {
  left: 0;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
  background: linear-gradient(90deg, rgba(67, 0, 32, 0.75), rgba(67, 0, 32, 0));
}

.category-rail::after {
  right: 0;
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
  background: linear-gradient(270deg, rgba(67, 0, 32, 0.75), rgba(67, 0, 32, 0));
}

.category-rail.rail-has-overflow-left::before {
  opacity: 1;
}

.category-rail.rail-has-overflow-right::after {
  opacity: 1;
}

/* The arrows that slide the category row (menu/app.js, ensureRailArrows): only with a mouse, and
   only on the side where there is more. A finger swipes, which is why phones never show them. */
.rail-arrow {
  display: none;
}

@media (hover: hover) and (pointer: fine) {
  .rail-arrow {
    position: absolute;
    top: var(--rail-arrow-top, 50%);
    transform: translateY(-50%);
    z-index: 2;
    width: 34px;
    height: 34px;
    padding: 0;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(249, 241, 234, 0.55);
    border-radius: 50%;
    background: var(--wine-900);
    color: var(--cream);
    box-shadow: 0 4px 14px rgba(22, 7, 14, 0.45);
    cursor: pointer;
  }

  .rail-arrow--left { left: 4px; }
  .rail-arrow--right { right: 4px; }

  .category-rail.rail-has-overflow-left .rail-arrow--left,
  .category-rail.rail-has-overflow-right .rail-arrow--right {
    display: inline-flex;
  }

  .rail-arrow:hover { border-color: var(--cream); }
  .rail-arrow:focus-visible { outline: 2px solid var(--cream); outline-offset: 2px; }
}

.chips {
  display: flex;
  /* --chip-gap and --chip-pad are set by menu/app.js (ensureChipPeek) only when a nudge of a
     few pixels is what it takes to leave a button cut at the row's edge. */
  gap: var(--chip-gap, 10px);
  overflow-x: auto;
  padding: 2px 2px 10px;
}

.chips::-webkit-scrollbar {
  display: none;
}

.chips::-webkit-scrollbar-thumb {
  border-radius: 30px;
  background: rgba(255, 255, 255, 0.25);
}

.chip {
  border: 1px solid rgba(255, 255, 255, 0.4);
  background: rgba(255, 255, 255, 0.09);
  color: var(--cream);
  border-radius: 999px;
  padding: 10px var(--chip-pad, 15px);
  font-size: 0.86rem;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.chip:hover {
  transform: translateY(-1px);
}

.chip.active {
  background: var(--cream);
  color: var(--wine-900);
  border-color: var(--cream);
  font-weight: 700;
  box-shadow: 0 10px 24px rgba(22, 7, 14, 0.28);
}

.menu-sections {
  display: grid;
  gap: 20px;
}

.menu-section {
  scroll-margin-top: 220px;
}

.menu-section-title {
  margin: 0 0 10px;
  padding-left: 12px;
  color: var(--cream);
  font-size: 1.2rem;
  font-weight: 800;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.menu-section-description {
  margin: -4px 0 12px;
  padding-left: 12px;
  color: rgba(249, 241, 234, 0.86);
  font-size: 0.88rem;
  line-height: 1.35;
}

/* M and G, spelled out under the Pizzas heading. Same voice as the description above it,
   except the letters: they are the key to every pizza price below ("M 9,90 / G 18,20"), so
   each is a cream badge in the price's own weight and colour, found at a glance rather than
   read out of a sentence. */
.menu-size-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 20px;
  margin: 0 0 14px;
  padding-left: 12px;
  color: rgba(249, 241, 234, 0.86);
  font-size: 0.84rem;
  line-height: 1.4;
}
.menu-size-legend > span:not(.menu-size-note) {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.menu-size-legend b {
  display: inline-grid;
  place-items: center;
  min-width: 2rem;
  height: 2rem;
  padding: 0 0.35rem;
  border-radius: 999px;
  background: var(--cream);
  color: var(--wine-800);
  font-size: 1.2rem;
  font-weight: 800;
  line-height: 1;
}
.menu-size-legend .menu-size-note {
  flex-basis: 100%;
}

.section-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
  justify-items: center;
}

.section-grid.extras-list {
  grid-template-columns: 1fr;
  gap: 10px;
}

.section-grid.extras-list .dish-card {
  max-width: 540px;
}

.dish-card {
  width: 100%;
  max-width: 430px;
  display: flex;
  flex-direction: column;
  background: var(--card);
  border: 1px solid rgba(0, 0, 0, 0.06);
  border-radius: 20px;
  overflow: hidden;
  box-shadow: var(--shadow);
  transition: transform 0.24s ease, box-shadow 0.24s ease;
}

.dish-media-wrap {
  aspect-ratio: 4 / 3;
  display: grid;
  place-items: center;
  position: relative;
  padding: 0;
  /* Not decorative. With overflow visible, an auto-height box still grows to fit its
     content, so aspect-ratio was only a suggestion: a lazy mixed-aspect photo landing
     in a 326px column pushed this row from 245px to 326px the moment it decoded. Every
     card below the fold did that mid-scroll, growing the document ~1141px while a
     category chip's smooth scroll was still in flight — it animated to a coordinate the
     heading had already left, which is why a second click on the same chip landed. */
  overflow: hidden;
}

.dish-media-wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 20px 20px 0 0;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.08));
  pointer-events: none;
}

.dish-media {
  width: 100%;
  height: 100%;
  /* Second guard on the same failure: as a grid item this would otherwise floor at its
     own intrinsic size. Belt and braces — tests/category-scroll.spec.ts holds the line. */
  min-height: 0;
  min-width: 0;
  object-fit: cover;
  object-position: center;
  display: block;
  border-radius: 20px 20px 0 0;
  transform: scale(1);
  transition: transform 0.25s ease;
}

.dish-content {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: 10px;
  padding: 14px 14px 16px;
}

.dish-title {
  margin: 0;
  font-size: 1.28rem;
  font-weight: 800;
  line-height: 1.25;
}

.dish-badge {
  margin: 0;
  width: fit-content;
  border-radius: 999px;
  padding: 4px 8px;
  font-size: 0.72rem;
  font-weight: 800;
  color: #fff;
  background: #b5482f;
}

.dish-compact-line {
  margin: 0;
  display: none;
  align-items: baseline;
  gap: 10px;
}

.dish-compact-name {
  color: var(--text-900);
  font-size: 0.94rem;
  font-weight: 600;
  display: flex;
  align-items: baseline;
  min-width: 0;
  flex: 1;
}

.dish-compact-name::after {
  content: "";
  flex: 1;
  border-bottom: 2px dotted #ccb2bf;
  margin: 0 8px 2px;
}

.dish-compact-price {
  color: var(--wine-800);
  font-size: 0.94rem;
  font-weight: 800;
  white-space: nowrap;
}

.dish-description {
  margin: 0;
  font-size: 0.94rem;
  line-height: 1.42;
  color: var(--text-500);
}

.dish-allergens {
  margin: 2px 0 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  font-size: 0.68rem;
  line-height: 1;
}

.allergen-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.06);
  font-size: 0.95rem;
  cursor: default;
}

/* Draft allergens (data/bake_allergens_draft.py) are keyword guesses, not a verified
   declaration. They stay visible but are deliberately de-emphasised against the verified
   treatment, and carry their own caveat line so the claim never reads as authoritative. */
.dish-allergens--draft .allergen-icon {
  background: transparent;
  border: 1px dashed rgba(0, 0, 0, 0.28);
  opacity: 0.7;
}

.allergen-hedge {
  flex-basis: 100%;
  margin-top: 2px;
  font-size: 0.66rem;
  line-height: 1.3;
  font-style: italic;
  color: var(--text-500);
  opacity: 0.85;
}

/* Chestnut emoji reads more like an almond/nut shape rotated on its side. */
.allergen-icon[data-allergen="frutos_de_casca_rija"] span {
  display: inline-block;
  transform: rotate(135deg);
}

.dish-helper {
  margin: -2px 0 0;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--wine-500);
}

.dish-helper.link {
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.floating-section {
  position: fixed;
  right: 14px;
  bottom: 164px;
  z-index: 18;
  border-radius: 999px;
  /* Solid rather than 60% opacity: translucent, its cream text became unreadable (and failed
     WCAG contrast) whenever the scroll parked it over a white dish card. */
  background: rgba(34, 6, 18, 0.94);
  border: 1px solid rgba(255, 255, 255, 0.26);
  color: var(--cream);
  font-size: 0.65rem;
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 8px 11px;
}

mark.hl {
  /* A search term is usually a prefix of a longer word, so the highlight lands mid-word:
     "tomat" inside "tomate". Padding alone pushed the remaining letters away and the word
     read as "tomat e" — the negative margin keeps the highlight's breathing room without
     taking up any layout width. */
  padding: 0 2px;
  margin: 0 -2px;
  border-radius: 4px;
  background: #ffe4a3;
  color: #3f1b02;
}

.dish-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: auto;
  padding-top: 2px;
}

.dish-price {
  margin: 0;
  color: var(--wine-800);
  font-size: 1.35rem;
  font-weight: 800;
  letter-spacing: 0.02em;
}

.dish-card--compact {
  border-radius: 16px;
}

.dish-card--compact .dish-media-wrap {
  display: none;
}

.dish-card--compact .dish-content {
  padding: 10px 12px;
  gap: 6px;
}

.dish-card--compact .dish-title {
  display: none;
}

.dish-card--compact .dish-description {
  display: none;
}

.dish-card--compact .dish-footer {
  display: none;
}

.dish-card--compact .dish-helper {
  display: none;
}

.dish-card--compact .dish-compact-line {
  display: flex;
}

.dish-card--extras {
  box-shadow: 0 12px 28px rgba(28, 6, 17, 0.18);
}

.dish-card--extras .dish-price {
  font-size: 1.1rem;
}

.status {
  margin-top: 16px;
  color: var(--cream);
  font-weight: 700;
}

/* While nothing in the menu is signed off, this notice is the ONLY allergen statement on
   the page — no dish carries one — so it is given real weight rather than sitting as fine
   print. It reverts to the quiet treatment automatically once the kitchen confirms a dish
   and per-dish chips resume.
   It sits below the dishes by request; see the note in ALLERGEN_SIGNOFF_GUIDE.md. */
.allergen-info--promoted {
  margin-top: 24px;
  margin-bottom: 8px;
  padding: 18px 22px;
  background: rgba(128, 24, 74, 0.55);
  border: 2px solid rgba(249, 241, 234, 0.45);
  font-size: 0.9rem;
  opacity: 1;
}

.allergen-info--promoted strong {
  display: block;
  margin-bottom: 4px;
  font-size: 1rem;
  letter-spacing: 0.02em;
}

@media (max-width: 767px) {
  .allergen-info--promoted {
    padding: 14px 16px;
    font-size: 0.84rem;
  }
}

/* ---- Searching on a phone, with the keyboard up ----------------------------------------
   The keyboard takes 300-340px of a 667-844px screen. On top of that the page was keeping
   217px of furniture: a header naming the page you are already on, a category rail that
   means nothing during a text search that crosses categories, and a call button pinned
   under the keyboard where no thumb can reach it. That left 150px on a small phone — less
   than one dish card — for the results you are typing to find.

   While the box has focus or a term in it, the page keeps the search field and the results
   and drops the rest. Everything returns on blur, so nothing is lost, only deferred. Gated
   on the phone breakpoint: a desktop keyboard covers nothing and the brand should stay. */
@media (max-width: 767px) {
  body.menu-searching .brand-wrap,
  body.menu-searching .mobile-phone {
    display: none;
  }

  /* The category rail stays while there is room for it. The chips are NOT filtered by the
     query, so during a search they are the list of places to jump back to, and clicking one
     is how somebody escapes a search that hid the section they wanted.
     Measured both ways: on a 390x844 phone with the keyboard up, hiding the rail changed
     the number of results on screen by nothing — 3 either way. It only paid on the smallest
     screens, where 43px is a whole result, so that is the only place it goes. */

  /* The topbar is a single row now. Letting it also slide away on scroll would take the
     search field with it, and the field is the one thing this mode exists to keep. */
  body.menu-searching .topbar {
    transform: none;
  }

  /* Below this the window is a keyboard and a sliver: an iPhone SE leaves 367px, where the
     rail is 43px of a control nobody reaches for mid-search and costs one of the two
     results that fit. Taller phones keep it — there it costs nothing. */
  @media (max-height: 420px) {
    body.menu-searching .category-rail {
      display: none;
    }
  }

  /* iOS does not resize the layout viewport for the keyboard, so the last result would sit
     behind it with no way to scroll further. --kbd-inset is published by app.js from
     visualViewport and is 0 everywhere the viewport already shrank. */
  body.menu-searching .menu-sections {
    padding-bottom: var(--kbd-inset, 0px);
  }

  /* A dish is a row, not a poster — while you are choosing between dishes.
     
     This started as the search layout and is now the default on a phone, because the
     problem it solved was never specific to searching. A poster card is 458px tall and
     280px of that is the photograph, which put 1.34 dishes on screen and made the menu 39
     screens long: anyone comparing three pizzas could not see two of them at once, so every
     comparison became scroll-and-remember. At ~112px it is six dishes a screen and about
     eleven screens end to end.
     
     Nothing is lost — tapping a row opens the full card, photograph and all (.is-open
     below), which is the state worth having once the choice is narrowed to one dish. The
     :not(--compact) exclusion leaves the pizza-extras rows alone; they are already a line. */
  .dish-card:not(.dish-card--compact):not(.is-open) {
    display: grid;
    grid-template-columns: 76px 1fr;
    align-items: center;
    gap: 12px;
    padding: 8px;
    border-radius: 14px;
    max-width: none;
  }

  .dish-card:not(.dish-card--compact):not(.is-open) .dish-media-wrap {
    aspect-ratio: 1 / 1;
    border-radius: 10px;
  }
  .dish-card:not(.dish-card--compact):not(.is-open) .dish-media-wrap::after,
  .dish-card:not(.dish-card--compact):not(.is-open) .dish-media {
    border-radius: 10px;
  }

  .dish-card:not(.dish-card--compact):not(.is-open) .dish-content {
    padding: 0;
    gap: 2px;
    min-width: 0;
  }

  /* "Mais pedido" is a recommendation, and a person who is searching has already decided
     what they want. The helper line goes for the same reason. */
  .dish-card:not(.dish-card--compact):not(.is-open) .dish-badge,
  .dish-card:not(.dish-card--compact):not(.is-open) .dish-helper {
    display: none;
  }

  /* Two lines, not one. The match is highlighted inside this text, and an ingredient list
     puts it anywhere — clamped to a single line, searching "cogumelos" returned a pizza
     whose visible description read "presunto, bacon, espinafres e…" with the word that
     caused the match cut off. A result that cannot show why it is a result is noise. */
  .dish-card:not(.dish-card--compact):not(.is-open) .dish-description {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  .dish-card:not(.dish-card--compact):not(.is-open) .dish-footer {
    margin-top: 2px;
  }

  /* The price is the largest thing on a full card because there it is the decision. In a
     row being scanned it competes with the name, and costs height every result pays for. */
  .dish-card:not(.dish-card--compact):not(.is-open) .dish-price {
    font-size: 1.05rem;
  }

  /* Opened: everything above is switched off by :not(.is-open), so the card falls back to
     its own poster styling with no second definition to keep in step. */
  .dish-card.is-open .dish-media-wrap { aspect-ratio: 4 / 3; }

  /* A row has to look like it does something. The chevron turns when it is open. */
  .dish-card[role="button"] { cursor: pointer; }
  .dish-card[role="button"]::after {
    content: "";
    position: absolute;
    right: 14px;
    top: 50%;
    width: 8px; height: 8px;
    margin-top: -6px;
    border-right: 2px solid var(--text-500);
    border-bottom: 2px solid var(--text-500);
    transform: rotate(45deg);
    transition: transform 0.18s ease;
    pointer-events: none;
  }
  .dish-card[role="button"] { position: relative; }
  .dish-card[role="button"].is-open::after {
    top: 22px;
    transform: rotate(-135deg);
  }
  /* The chevron needs somewhere to sit that is not on top of the price. */
  .dish-card:not(.dish-card--compact):not(.is-open) { padding-right: 30px; }

  /* The section heading stays — in a search that crosses categories, "Pizzas" over a group
     of results is what tells you which part of the menu you are looking at. Its description
     goes: 53px of prose about a category, sitting between you and the results, on a screen
     that has 297px to spend. */
  body.menu-searching .menu-section-description,
  body.menu-searching .menu-size-legend {
    display: none;
  }
  body.menu-searching .menu-section-title {
    margin-bottom: 8px;
  }
}

.allergen-info {
  margin-top: 24px;
  padding: 16px 20px;
  background: rgba(0, 0, 0, 0.28);
  border-radius: 12px;
  font-size: 0.8rem;
  line-height: 1.6;
  color: var(--cream);
  opacity: 0.92;
  text-align: center;
  max-width: 720px;
  margin-left: auto;
  margin-right: auto;
}

.price-disclaimer {
  margin-top: 10px;
  font-size: 0.8rem;
  color: var(--cream);
  opacity: 0.85;
  text-align: center;
}

.mobile-phone {
  position: fixed;
  left: 16px;
  right: 16px;
  /* Down into the home-indicator strip and 2px past the bottom edge, with the same added to the
     padding so the contents stay put: no strip of page under the bar on an iPhone, and no
     hairline at 125% or 150% display scaling. */
  bottom: -2px;
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex-wrap: nowrap;
  gap: 0;
  text-align: center;
  min-height: 48px;
  padding: 6px 12px calc(8px + env(safe-area-inset-bottom, 0px) + 2px);
  border-radius: 16px 16px 0 0;
}

/* The store name qualifies the number at the point the question matters — "which shop
   am I about to ring" — rather than sitting in the header as decoration. Shared by both
   breakpoints; it used to exist only on the mobile bar. */
.phone-cta-location {
  display: block;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  line-height: 1;
  margin-bottom: 3px;
  padding-bottom: 3px;
  color: rgba(249, 241, 234, 0.82);
  border-bottom: 1px solid rgba(255, 255, 255, 0.18);
  text-align: center;
}

.phone-mobile-call-row {
  display: flex;
  align-items: center;
  gap: 0;
}

.phone-mobile-main {
  font-size: 1rem;
  font-weight: 800;
  line-height: 1;
  white-space: nowrap;
}

.phone-mobile-number {
  margin-left: 8px;
  font-size: 0.94rem;
  font-weight: 800;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

.phone-mobile-helper {
  font-size: 0.83rem;
  font-weight: 700;
  opacity: 0.94;
}

.phone-mobile-open {
  font-size: 0.78rem;
  opacity: 0.86;
}

@media (min-width: 768px) {
  .topbar {
    border-bottom: 0;
  }

  .topbar::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 1px;
    background: rgba(67, 0, 32, 0.95);
    pointer-events: none;
  }

  .topbar-inner {
    grid-template-areas: "brand phone search";
    grid-template-columns: 1fr auto minmax(280px, 430px);
    align-items: center;
    gap: 10px;
    padding: 10px 18px;
  }

  .brand-wrap {
    grid-area: brand;
  }

  .desktop-phone {
    grid-area: phone;
  }

  /* .search itself was never a direct child of .topbar-inner — .topbar-row2 (search +
     the PT/EN toggle) is, so this was a no-op; only auto-placement dropping .topbar-row2
     into the one unclaimed cell made it look like it worked. */
  .topbar-row2 {
    grid-area: search;
  }

  .section-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .section-grid.extras-list {
    grid-template-columns: 1fr;
  }

  .page {
    padding: 0 20px 34px;
  }

  .category-rail {
    top: calc(var(--topbar-height, 102px) - 1px);
    margin-top: 0;
  }

  .mobile-phone {
    display: none;
  }

  /* No Ligar bar here, so the cookie banner goes back to the bottom edge. */
  .cookie-banner {
    bottom: 0;
  }

  body {
    padding-bottom: 20px;
  }

  .floating-section {
    bottom: 18px;
  }
}

/* Was max-width: 1080px. Above 1080 the base >=768px rule's single-row "brand phone
   search" layout takes back over, but its 1fr brand column doesn't have enough room
   again until ~1200px — a dead zone (measured: wraps to two lines from 1081px up to
   ~1150px) where "Ementa — <store>" wrapped under the phone pill. Stacking search
   under brand+phone a while longer closes it. */
@media (min-width: 768px) and (max-width: 1199px) {
  .topbar-inner {
    grid-template-areas:
      "brand brand"
      "phone search";
    grid-template-columns: auto minmax(0, 1fr);
    gap: 8px 12px;
  }

  .search {
    max-width: none;
  }
}

@media (min-width: 1100px) {
  .topbar-inner,
  .page {
    max-width: 1460px;
  }

  .section-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  .section-grid.extras-list {
    grid-template-columns: 1fr;
  }

  .dish-title {
    font-size: 1.36rem;
  }
}

@media (hover: hover) and (pointer: fine) {
  .dish-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 16px 40px rgba(0, 0, 0, 0.25);
  }

  .dish-card:hover .dish-media {
    transform: scale(1.04);
  }
}

@media (max-width: 767px) {
  .texture {
    background-size: var(--texture-size-mobile);
    background-position: center -30px;
  }

  .topbar {
    background: linear-gradient(180deg, rgba(67, 0, 32, 0.98), rgba(67, 0, 32, 0.9));
    border-bottom: 0;
  }

  .topbar::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 1px;
    background: rgba(67, 0, 32, 0.95);
    pointer-events: none;
  }

  .topbar-inner {
    padding: 8px 10px 10px;
    gap: 5px;
  }

  .brand-wrap {
    gap: 10px;
    align-items: center;
  }

  .brand-logo {
    width: 44px;
    height: 44px;
    border-radius: 0;
    padding: 0;
  }

  .brand-copy h1 {
    /* 1.4rem, not 1.5: the h1 now carries the store's short name too (Brief 5 Task 5),
       and at 1.5rem "Ementa — Porto Salvo" (the longest short_name) wrapped to two lines
       at 390px, a width common enough to matter. This keeps every store on one line at
       390px+ and degrades to two lines only on narrower phones now largely out of use —
       harmless when it happens (checked: .topbar stays well under the height budget
       menu-location-i18n.spec.ts holds it to). */
    font-size: 1.4rem;
    line-height: 1.1;
    text-align: left;
  }

  .kicker {
    margin-bottom: 3px;
    font-size: 0.72rem;
    letter-spacing: 0.12em;
  }

  .order-helper {
    display: none;
  }

  .search {
    display: flex;
    padding: 8px 10px;
    border-radius: 10px;
  }

  .page {
    padding: 0 0 16px;
  }

  .menu-sections {
    gap: 14px;
    margin-top: 0;
  }

  .menu-section-title {
    margin-bottom: 6px;
    font-size: 0.95rem;
  }

  .menu-section-description {
    margin: -2px 0 8px;
    font-size: 0.82rem;
  }

  .desktop-phone {
    display: none;
  }

  .category-rail {
    top: calc(var(--topbar-height, 124px) - 1px);
    width: 100vw;
    /* 24px to whatever comes first under the row - the chef's card or the first heading - so
       the menu does not open pressed against the chips (it was 4px). */
    margin: 0 0 24px;
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
    margin-top: 0;
    padding: 4px 8px 4px;
    border-radius: 0;
    background: linear-gradient(180deg, rgba(67, 0, 32, 0.84), rgba(67, 0, 32, 0.72));
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    border-top: 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.13);
  }

  .category-rail::before,
  .category-rail::after {
    opacity: 0;
    width: 22px;
  }

  .chips {
    gap: var(--chip-gap, 10px);
    padding: 0 2px 2px;
    scroll-snap-type: x proximity;
  }

  .chip {
    padding: 8px var(--chip-pad, 14px);
    font-size: 0.9rem;
    border-color: rgba(255, 255, 255, 0.44);
    background: rgba(255, 255, 255, 0.1);
    line-height: 1;
    scroll-snap-align: start;
  }

  .chip.active {
    box-shadow: 0 10px 20px rgba(22, 7, 14, 0.2);
  }

  .dish-content {
    padding: 12px;
    gap: 8px;
  }

  .dish-card {
    max-width: 100%;
    margin: 0 auto;
    border-radius: 16px;
  }

  .dish-media-wrap {
    padding: 0;
  }

  .dish-title {
    font-size: 1.03rem;
  }

  .dish-description {
    font-size: 0.9rem;
  }

  .menu-section {
    scroll-margin-top: calc(var(--topbar-height, 124px) + 46px);
  }

  .menu-section:first-child {
    margin-top: 0;
    padding-top: 0;
  }

  .menu-section:first-child .menu-section-title {
    margin-top: 0;
  }

  .menu-section:first-child .menu-section-description {
    margin-top: -1px;
  }

  .floating-section {
    display: none;
  }

  .mobile-phone {
    left: 10px;
    right: 10px;
    bottom: -2px;
    min-height: 48px;
    /* The home-indicator strip counted once, as padding. It used to be counted twice: the bar
       floated above the strip and was padded by it as well. */
    padding: 8px 12px calc(8px + env(safe-area-inset-bottom, 0px) + 2px);
    gap: 0;
    border-radius: 14px 14px 0 0;
  }

  .phone-mobile-main {
    font-size: 0.96rem;
  }

  .phone-mobile-number {
    margin-left: 6px;
    font-size: 0.88rem;
  }

  .phone-mobile-helper {
    display: none;
  }

  .phone-mobile-open {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .dish-card,
  .dish-media,
  .chip,
  .topbar,
  .category-rail {
    transition: none;
  }
}

/* ============================================================
   Page Loader
   ============================================================ */
.page-loader {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: #310419;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: opacity 1.3s ease;
}

/* Same subtle texture as body::before so loader matches site background */
.page-loader::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: url('../assets/background.png');
  /* 504 KB -> 8 KB. The plain url above is what a browser without image-set()
     reads; everything since Safari 17 takes the WebP and never fetches the PNG. */
  background-image: image-set(
    url("../assets/background.webp") type("image/webp"),
    url("../assets/background.png") type("image/png"));
  background-size: cover;
  background-repeat: repeat;
  background-position: center;
  opacity: 0.07;
  z-index: 0;
}

.page-loader.loader-hidden {
  opacity: 0;
  pointer-events: none;
}

.loader-overlay {
  display: none;
}

.loader-sun {
  position: absolute;
  top: 66%;
  left: 50%;
  z-index: 2;
  width: 200px;
  height: 200px;
  opacity: 0;
  animation: loader-sun-in 0.8s ease forwards, loader-sun-spin 2.5s linear infinite;
  transition: opacity 0.4s ease;
}

@keyframes loader-sun-in {
  to { opacity: 0.85; }
}

@keyframes loader-sun-spin {
  from { transform: translate(-50%, -50%) rotate(0deg); }
  to   { transform: translate(-50%, -50%) rotate(360deg); }
}

.loader-flag {
  position: absolute;
  bottom: 24px;
  right: 24px;
  width: 27px;
  height: auto;
  z-index: 2;
  opacity: 0;
  animation: loader-sun-in 0.8s ease 0.4s forwards;
}

@media (max-width: 768px) {
  .loader-sun { top: 62%; width: 130px; height: 130px; }
  .loader-flag { width: 28px; bottom: 16px; right: 16px; }
}

@media (prefers-reduced-motion: reduce) {
  .loader-sun { animation: loader-sun-in 0.8s ease forwards; }
  .page-loader { transition: opacity 0.3s ease; }
}

/* ============================================================
   Shared modals (data/modal.js) — used by index.html and menu/menu.html
   Framework-free on purpose: the menu page carries no Bootstrap, and search traffic
   lands there first.
   ============================================================ */
.dmodal {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}

.dmodal[hidden] { display: none; }

body.dmodal-open {
  position: fixed;
  width: 100%;
  overflow: hidden;
}

.dmodal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.62);
}

.dmodal-panel {
  position: relative;
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  width: 100%;
  max-width: 420px;
  padding: 52px 24px 22px;
  border-radius: var(--radius-lg, 20px);
  background: var(--wine-800, #310419);
  border: 1px solid rgba(249, 241, 234, 0.18);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
  color: #fff;
  text-align: center;
}

.dmodal-close {
  position: absolute;
  top: 10px;
  right: 12px;
  width: 34px;
  height: 34px;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.1);
  color: #fff;
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
}

.dmodal-close:hover { background: rgba(255, 255, 255, 0.2); }

.dmodal-title {
  /* Sits below .dmodal-close, which is absolutely positioned in the panel's top-right
     corner. "Ligar para encomendar" is long enough to run under it otherwise. */
  margin: 0 0 4px;
  line-height: 1.2;
  font-family: 'Florence', 'Prata', Georgia, serif;
  font-size: 1.5rem;
  font-weight: 400;
}

/* Which store you are about to ring — a deep-linked visitor may not know. */
.dmodal-store {
  margin: 0 0 14px;
  font-size: 0.95rem;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.9);
}

/* Reservation-specific copy. The only thing that should distinguish that modal from the
   call modal — the chrome is otherwise identical. */
.dmodal-note {
  margin: 0 0 16px;
  font-size: 0.9rem;
  line-height: 1.45;
  color: rgba(255, 255, 255, 0.8);
}

/* Outline icons inherit the surrounding colour and sit on the text baseline. Sized in em
   so they track whatever they sit beside. */
.dmodal-icon {
  width: 1em;
  height: 1em;
  flex: 0 0 auto;
  vertical-align: -0.12em;
}

.dmodal-title .dmodal-icon { width: 0.85em; height: 0.85em; }
.dmodal-copy .dmodal-icon { width: 20px; height: 20px; }

.dmodal-rows { display: flex; flex-direction: column; gap: 10px; }

.dmodal-row { display: flex; align-items: stretch; gap: 8px; }

.dmodal-call {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 14px 16px;
  border-radius: var(--radius-sm, 8px);
  background: var(--secondary-color, rgb(128, 24, 74));
  color: #fff;
  font-size: 1.05rem;
  font-weight: 700;
  text-decoration: none;
}

.dmodal-call:hover { background: rgb(148, 34, 94); color: #fff; }

/* tel: does nothing on a desktop, so the number has to be copyable there. */
.dmodal-copy {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 48px;
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: var(--radius-sm, 8px);
  background: transparent;
  color: #fff;
  cursor: pointer;
}

.dmodal-copy:hover { background: rgba(255, 255, 255, 0.12); }
.dmodal-copy.is-copied { background: rgba(255, 255, 255, 0.25); }

@media (hover: none) {
  /* Touch devices dial directly; a copy button would just crowd the row. The call button
     is flex:1 so it takes the freed width rather than leaving a gap where the button was. */
  .dmodal-copy { display: none; }
}

.dmodal-hours {
  margin: 14px 0 0;
  font-size: 0.85rem;
  color: rgba(255, 255, 255, 0.75);
}

.dmodal .loc-modal-list { display: flex; flex-direction: column; gap: 10px; }

/* ---- Cookie banner: the same banner as the rest of the site (driven by data/consent.js) ---- */
.cookie-banner {
  position: fixed;
  left: 0;
  right: 0;
  /* Above the Ligar bar rather than over it: someone who came to call should not have to deal
     with the banner first. The desktop layout, which has no bar, resets this to 0. */
  bottom: calc(env(safe-area-inset-bottom) + 64px);
  z-index: 2000;
  background: rgba(20, 4, 12, 0.97);
  border-top: 1px solid rgba(128, 24, 74, 0.5);
  color: var(--cream);
  padding: 14px 20px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.cookie-banner[hidden] { display: none; }
.cookie-banner-main { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.cookie-banner-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.cookie-text { flex: 1; font-size: 0.78rem; opacity: 0.9; margin: 0; line-height: 1.5; }
/* The banner's wording: the full text from a tablet up, one line on a phone (the same promise
   and both policy links), so on a phone the banner leaves the page it sits on visible. */
.cookie-text-short { display: none; }
@media (max-width: 767px) {
  .cookie-text-long { display: none; }
  .cookie-text-short { display: inline; }
}
.cookie-link { color: rgba(249, 241, 234, 0.8); text-decoration: underline; }
.cookie-link:hover { color: #fff; }
.btn-cookie-accept,
.btn-cookie-reject,
.btn-cookie-manage {
  border-radius: 10px;
  min-height: 44px;
  padding: 8px 22px;
  font: inherit;
  font-size: 0.8rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  cursor: pointer;
  white-space: nowrap;
  flex-shrink: 0;
}
.btn-cookie-accept { background-color: var(--wine-500); color: #fff; border: none; }
.btn-cookie-accept:hover { background-color: var(--wine-700); }
.btn-cookie-reject,
.btn-cookie-manage { background: none; color: rgba(249, 241, 234, 0.9); border: 1px solid rgba(249, 241, 234, 0.35); }
.btn-cookie-reject:hover,
.btn-cookie-manage:hover { border-color: #fff; }
.cookie-manage-panel[hidden] { display: none; }
.cookie-manage-panel { display: flex; flex-direction: column; gap: 10px; border-top: 1px solid rgba(255, 255, 255, 0.12); padding-top: 12px; }
.cookie-category label { font-size: 0.82rem; font-weight: 600; display: flex; align-items: center; gap: 8px; cursor: pointer; }
.cookie-category p { font-size: 0.75rem; opacity: 0.75; margin: 2px 0 0 24px; }

.menu-cookie-link { text-align: center; margin: 28px 0 8px; font-size: 0.8rem; }
.menu-cookie-link a { color: rgba(249, 241, 234, 0.75); }

/* ---- Call dialog, shop closed: when it reopens, and what to do instead (data/modal.js) ---- */
.dmodal-closed[hidden],
.dmodal-rows-label[hidden],
.dmodal-open-status[hidden] { display: none; }
.dmodal-closed { display: flex; flex-direction: column; gap: 10px; margin: 0 0 16px; }
.dmodal-closed-note {
  margin: 0;
  padding: 12px 14px;
  border-radius: 8px;
  background: rgba(242, 164, 164, 0.14);
  border: 1px solid rgba(242, 164, 164, 0.45);
  color: #fbe6e6;
  font-size: 0.95rem;
  font-weight: 700;
  line-height: 1.4;
}
.dmodal-alts { display: flex; flex-direction: column; gap: 8px; }
.dmodal-alt {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 48px;
  padding: 12px 16px;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.45);
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
  font-weight: 700;
  text-decoration: none;
}
.dmodal-alt:hover { background: rgba(255, 255, 255, 0.16); color: #fff; }
.dmodal-rows-label {
  margin: 0 0 8px;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.72);
}

/* "Sugestão do chef" — the way from a store's menu to its suggestions page, while it has any
   (menu/app.js placeSuggestionBanner). Cream on the wine ground, like the dish cards. */
/* Aligned with the dish cards' left edge, not centred as a floating pill; the text column
   may shrink (min-width: 0) so it never runs into the "Ver ›" on the right. */
.sug-banner {
  display: grid;
  grid-template-columns: 72px minmax(0, 1fr) auto;
  align-items: center;
  gap: 14px;
  width: 100%;
  max-width: 640px;
  margin: 4px 0 32px;              /* 32px down to the first section */
  padding: 12px 12px 12px 8px;
  border-radius: 16px;
  background: var(--card);
  color: var(--text-900);
  text-decoration: none;
  box-shadow: var(--shadow);
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.sug-banner:hover { transform: translateY(-1px); box-shadow: 0 10px 26px rgba(20, 0, 10, 0.32); }
.sug-banner:active { transform: translateY(0); }
.sug-banner:focus-visible { outline: 3px solid var(--cream); outline-offset: 3px; }
.sug-banner-art { width: 72px; height: 72px; display: grid; place-items: center; }
.sug-banner-art img { width: 72px; height: 72px; object-fit: contain; }
.sug-banner-text { min-width: 0; display: grid; gap: 2px; }
.sug-banner-eyebrow { font-size: 0.69rem; font-weight: 800; letter-spacing: 0.12em; text-transform: uppercase; color: var(--wine-500); }
.sug-banner strong { font-size: 1.05rem; line-height: 1.2; color: var(--wine-800); }
.sug-banner-picks {
  font-size: 0.86rem; line-height: 1.35; color: #4a3a40;   /* 9.6:1 on the card */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
}
.sug-banner-go { display: inline-flex; align-items: center; justify-content: center; gap: 2px; min-width: 44px; min-height: 44px;
  padding: 0 6px; color: var(--wine-500); font-weight: 800; font-size: 0.86rem; }
.sug-banner-go i { font-style: normal; font-size: 1.6rem; line-height: 1; }
@media (prefers-reduced-motion: reduce) { .sug-banner, .sug-banner:hover { transition: none; transform: none; } }

/* A 44px tap height for the PT/EN switch on a phone (it was 38px). */
@media (max-width: 767px) {
  .lang-btn { height: 44px; min-width: 44px; }
}

/* The card's slot, above the dishes: takes no room when empty. */
.sug-slot:empty, .sug-slot[hidden] { display: none; }

/* Cookie banner on a phone: Rejeitar and Aceitar side by side, the same size - refusing is as
   easy as accepting - and Preferências full width under them. Three buttons wrapping freely
   left "Aceitar tudo" alone on a row, off to one side. */
@media (max-width: 767px) {
  .cookie-banner-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; flex: 1 1 100%; width: 100%; }
  .cookie-banner-actions .btn-cookie-reject { order: 1; }
  .cookie-banner-actions .btn-cookie-accept { order: 2; }
  .cookie-banner-actions .btn-cookie-manage { order: 3; grid-column: 1 / -1; }
  .cookie-banner-actions button { width: 100%; min-height: 44px; padding-inline: 8px; text-align: center; justify-content: center; }
}
