/* ============================================================
   Hegarty's Irish Pub — Drinks Menu
   Deep green + cream, lime accent from the pub's logo.
   Minimal, mobile-first, fullscreen (100dvh), safe-area aware.
   No drink data in here — layout only.
   ============================================================ */

/* --- Self-hosted fonts ------------------------------------------
   Fraunces and Manrope, SIL Open Font License 1.1
   (https://openfontlicense.org) — Fraunces © The Fraunces Project
   Authors; Manrope © Mikhail Sharanda. Variable woff2 subsets,
   previously served by Google Fonts, now shipped from
   assets/fonts/ so the site makes zero third-party requests. */

/* Fraunces — variable (optical size + weight), roman, latin */
@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../assets/fonts/fraunces-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Fraunces — variable roman, latin-ext */
@font-face {
  font-family: "Fraunces";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../assets/fonts/fraunces-var-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Fraunces — variable italic, latin */
@font-face {
  font-family: "Fraunces";
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url("../assets/fonts/fraunces-italic-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Fraunces — variable italic, latin-ext */
@font-face {
  font-family: "Fraunces";
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url("../assets/fonts/fraunces-italic-var-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Manrope — variable weight 400–700, latin */
@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../assets/fonts/manrope-var.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Manrope — variable weight 400–700, latin-ext */
@font-face {
  font-family: "Manrope";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../assets/fonts/manrope-var-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  --bg:           #08160e;   /* deep green-black               */
  --bg-deep:      #050e09;   /* base under the photo layer     */
  --panel:        #0d2317;   /* stage card base                */
  --panel-hi:     #12301f;   /* stage card crown               */
  --line:         rgba(240, 231, 211, 0.09);
  --cream:        #f0e7d3;
  --cream-dim:    rgba(240, 231, 211, 0.62);
  --cream-faint:  rgba(240, 231, 211, 0.32);
  --lime:         #b8d935;   /* signature accent (pub logo)    */
  --lime-bright:  #d3ec60;   /* small text accents             */
  --lime-soft:    rgba(184, 217, 53, 0.38);
  --ink:          #131d07;   /* text on lime                   */
  --amber:        #ffd27d;   /* sold-out accent                */
  --font-display: "Fraunces", Georgia, serif;
  --font-body:    "Manrope", "Helvetica Neue", Arial, sans-serif;
  --sat: env(safe-area-inset-top, 0px);
  --sab: env(safe-area-inset-bottom, 0px);
  /* Stage geometry — shared by .drink-stage and the chevron
     anchors so arrows always sit at the stage's vertical centre */
  --stage-w: min(72vw, 39vh, 300px);
  --info-h:  175px;   /* drink-info block + fraction footprint  */
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html { height: 100%; }

body {
  height: 100%;
  overflow: hidden;
  overscroll-behavior: none;
  background: var(--bg-deep);
  color: var(--cream);
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

img { display: block; }

/* --- Atmosphere 1: photo backdrop, fixed behind everything ----
   The optimized -mobile derivative only — never the 6MB original.
   A deep-green scrim keeps every slide readable; the smoke just
   ghosts through as atmosphere. */
.bg {
  position: fixed;
  inset: 0;
  z-index: 0;
  background:
    linear-gradient(180deg,
      rgba(5, 14, 9, 0.91) 0%,
      rgba(7, 19, 12, 0.83) 36%,
      rgba(5, 13, 8, 0.93) 100%),
    url("../assets/green-background-mobile.jpg") center / cover no-repeat,
    var(--bg-deep);
}

/* --- Atmosphere 2: soft vignette + film grain ------------------ */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 25;
  pointer-events: none;
  background: radial-gradient(115% 85% at 50% 38%, transparent 52%, rgba(0, 0, 0, 0.5) 100%);
}

.grain {
  position: fixed;
  inset: 0;
  z-index: 60;
  pointer-events: none;
  opacity: 0.05;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 240 240' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 240px 240px;
}

/* --- Fixed chrome: text wordmark + specials tag, nothing else --- */
.chrome {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 30;
  padding-top: calc(var(--sat) + 14px);
  /* Reserve the menu-toggle zone (48px button + margin, top-left) so
     the lockup can never slide under it — but reserve symmetrically on
     BOTH sides, so the content box stays centred on the viewport and the
     lockup centres as if the toggle were not there. */
  padding-left: max(64px, calc(env(safe-area-inset-left) + 60px));
  padding-right: max(64px, calc(env(safe-area-inset-right) + 60px));
  padding-bottom: 32px; /* extends the scrim below the wordmark */
  text-align: center;
  pointer-events: none;
  background: linear-gradient(180deg,
    rgba(5, 14, 9, 0.94) 0%,
    rgba(5, 14, 9, 0.62) 55%,
    transparent 100%);
}

/* Wordmark + "Online Specials" tag as one inline-flex row. The
   .chrome parent centres it via text-align inside the space right of
   the toggle reserve; both elements are text, so a plain shared
   baseline aligns the row with no optical nudging needed. */
.chrome-brand {
  display: inline-flex;
  align-items: baseline;
  gap: 12px;
  pointer-events: auto;
}

/* The brand itself: Fraunces caps in the logo's own pale sage
   (#d0db97, sampled from the PNG). Caps read larger than the
   lowercase they replaced, so the clamp sits a step lower and a
   touch of tracking keeps the caps elegant. */
.chrome-wordmark {
  font-family: var(--font-display);
  font-size: clamp(20px, 4.8vh, 26px);
  font-weight: 500;
  line-height: 1;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: #d0db97;
  filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.45));
  white-space: nowrap;
}

/* A small, elegant descriptor — wide-tracked italic caps in the
   site's cream, a quiet counterpoint to the sage wordmark. */
.chrome-subtitle {
  font-family: var(--font-display);
  font-size: clamp(12px, 1.8vh, 15px);
  font-weight: 400;
  font-style: italic;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--cream);
  opacity: 0.9;
  filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.5));
  white-space: nowrap;
}

/* Small phones: stack the lockup masthead-style — wordmark over tag —
   so the never-wrapping row cannot run into the 48px toggle at the
   top-left. The stack is ~20px taller than the row; the scrim's 32px
   bottom padding still clears the tag and the gradient simply fades
   a little deeper. */
@media (max-width: 480px) {
  .chrome-brand {
    flex-direction: column;
    align-items: center;
    gap: 4px;
  }
}

/* --- Menu toggle --------------------------------------------------
   Deliberately independent from the non-interactive chrome.
   No frame, no pill — just three strokes, like a mark made in the
   margin of a printed page. 48px hit area stays intact. */
.menu-toggle {
  position: fixed;
  top: calc(var(--sat) + 8px);
  left: max(12px, env(safe-area-inset-left, 0px));
  z-index: 70;
  width: 48px;
  height: 48px;
  display: grid;
  place-content: center;
  gap: 5px;
  border: 0;
  background: none;
  color: var(--cream);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color 180ms ease;
}

.menu-toggle span {
  display: block;
  width: 20px;
  height: 1.5px;
  background: currentColor;
  transform-origin: center;
  transition: transform 180ms ease, opacity 180ms ease;
}

.menu-toggle.is-open span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
.menu-toggle.is-open span:nth-child(2) { opacity: 0; transform: scaleX(0.2); }
.menu-toggle.is-open span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }

@media (hover: hover) {
  /* A quiet lime hint when closed; once open, the X stays cream. */
  .menu-toggle:not(.is-open):hover { color: var(--lime-bright); }
}

.menu-toggle:focus-visible,
.menu-item:focus-visible,
.menu-impressum:focus-visible,
.impressum-close:focus-visible {
  outline: 2px solid var(--lime-bright);
  outline-offset: 3px;
}

/* --- Menu layer and left drawer ---------------------------------- */
.menu-layer {
  position: fixed;
  inset: 0;
  z-index: 65;
  visibility: hidden;
  opacity: 0;
  pointer-events: none;
  transition: opacity 180ms ease, visibility 0s linear 180ms;
}

.menu-layer.is-open {
  visibility: visible;
  opacity: 1;
  pointer-events: auto;
  transition-delay: 0s;
}

.menu-backdrop {
  position: absolute;
  inset: 0;
  width: 100%;
  border: 0;
  background: rgba(5, 14, 9, 0.78);
  cursor: pointer;
}

.menu-drawer {
  position: absolute;
  inset: 0 auto 0 0;
  width: min(88vw, 380px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: calc(var(--sat) + 82px) max(24px, env(safe-area-inset-right, 0px)) calc(var(--sab) + 24px) max(24px, env(safe-area-inset-left, 0px));
  border-right: 1px solid var(--line);
  background: var(--bg-deep);
  box-shadow: 10px 0 30px rgba(0, 0, 0, 0.25);
  scrollbar-color: var(--line) transparent;
  transform: translateX(-100%);
  transition: transform 260ms cubic-bezier(0.22, 0.8, 0.25, 1);
}

.menu-layer.is-open .menu-drawer { transform: translateX(0); }

.menu-drawer-inner {
  min-height: 100%;
  display: flex;
  flex-direction: column;
}

.menu-header {
  padding-bottom: 26px;
  border-bottom: 1px solid var(--line);
}

/* Masthead: the pub name set like a printed menu's title page.
   Plain text so "Hegarty's Online Specials" stays whole for screen
   readers and copy/paste. */
.menu-wordmark {
  font-family: var(--font-display);
  font-size: clamp(18px, 5.4vw, 24px);
  font-weight: 500;
  letter-spacing: 0.01em;
  line-height: 1;
  color: var(--cream);
}

.menu-drawer ul {
  display: grid;
  gap: 14px;
  list-style: none;
}

/* Typographic list — no cards, no boxes, transparent background.
   The page you're on is marked the way a printed menu marks the
   specials: a lime stroke drawn in the margin of the line. */
.menu-item {
  position: relative;
  width: 100%;
  display: block;
  padding: 8px 8px 8px 0;
  border: 0;
  background: none;
  color: var(--cream-dim);
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-size: clamp(24px, 6.4vw, 31px);
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.01em;
  text-align: left;
  cursor: pointer;
  transition: color 180ms ease, padding-left 180ms ease;
}

.menu-item::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 0;
  height: 1px;
  background: var(--lime);
  transform: translateY(-50%);
  transition: width 180ms ease;
}

@media (hover: hover) {
  .menu-item:hover,
  .menu-impressum:hover { color: var(--cream); }
}

/* Active page — declared after :hover so it always wins. */
.menu-item[aria-current="page"] {
  color: var(--lime-bright);
}

.menu-footer {
  margin-top: auto;
  padding-top: 26px;
  border-top: 1px solid var(--line);
}

/* Borderless utility row — same quiet micro-label voice as the
   eyebrow, so the footer reads as part of the page, not a widget. */
.menu-impressum {
  width: 100%;
  min-height: 44px;
  display: flex;
  align-items: center;
  padding: 10px 0;
  border: 0;
  background: none;
  color: var(--cream-faint);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 180ms ease;
}

@keyframes menu-item-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

.menu-layer.is-open .menu-item {
  animation: menu-item-in 300ms cubic-bezier(0.22, 0.8, 0.25, 1) both;
  animation-delay: calc(60ms + (var(--item-index) * 30ms));
}

/* --- Impressum dialog ------------------------------------------- */
.impressum-dialog {
  width: min(calc(100% - 32px), 560px);
  max-height: min(84dvh, 720px);
  margin: auto;
  padding: 0;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 18px;
  background: var(--bg-deep);
  color: var(--cream);
  box-shadow: 0 24px 80px rgba(0, 0, 0, 0.5);
}

.impressum-dialog::backdrop { background: rgba(5, 14, 9, 0.84); }

.impressum-dialog-inner {
  max-height: min(84dvh, 720px);
  display: flex;
  flex-direction: column;
}

.impressum-header {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 14px 14px 22px;
  border-bottom: 1px solid var(--line);
  background: linear-gradient(180deg, rgba(18, 48, 31, 0.72), rgba(13, 35, 23, 0.2));
}

.impressum-header h2 {
  font-family: var(--font-display);
  font-size: clamp(30px, 8vw, 40px);
  font-weight: 500;
  line-height: 1;
}

.impressum-close {
  flex: 0 0 44px;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: rgba(5, 14, 9, 0.34);
  color: var(--cream);
  font-family: var(--font-body);
  font-size: 30px;
  font-weight: 300;
  line-height: 1;
  cursor: pointer;
  transition: color 180ms ease, border-color 180ms ease, background 180ms ease;
}

.impressum-close:hover {
  border-color: var(--lime-soft);
  background: rgba(184, 217, 53, 0.08);
  color: var(--lime-bright);
}

.impressum-body {
  min-height: 0;
  overflow-y: auto;
  padding: 22px 22px calc(24px + var(--sab));
  overscroll-behavior: contain;
  scrollbar-color: var(--lime-soft) transparent;
}

.impressum-body section + section {
  margin-top: 24px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}

.impressum-body h3 {
  margin-bottom: 8px;
  font-family: var(--font-display);
  font-size: clamp(20px, 5vw, 25px);
  font-weight: 500;
  line-height: 1.18;
  color: var(--cream);
}

.impressum-body p {
  color: var(--cream-dim);
  font-size: 14px;
  line-height: 1.65;
}

.impressum-body a {
  color: var(--lime-bright);
  text-decoration-color: var(--lime-soft);
  text-underline-offset: 3px;
}

@media (min-width: 600px) and (orientation: portrait) {
  .menu-drawer { width: min(56vw, 440px); }
}

@media (orientation: landscape) and (min-width: 600px) {
  .menu-drawer { width: min(48vw, 420px); }
}

@media (prefers-reduced-motion: reduce) {
  .menu-toggle,
  .menu-toggle span,
  .menu-layer,
  .menu-drawer,
  .menu-item,
  .menu-item::before,
  .menu-impressum,
  .impressum-close,
  .drink { transition: none; }
  .menu-layer.is-open .menu-item { animation: none; }
}

/* Touch polish: no gray tap-flash; no double-tap zoom on controls.
   (Swiper containers already own touch-action via pan-x/pan-y.) */
button,
.menu-item,
.menu-impressum,
.swiper-button-prev,
.swiper-button-next {
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

/* --- Outer vertical pager: fills the viewport at any size ----- */
.swiper--pages {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100vh;  /* ancient fallback */
  height: -webkit-fill-available;  /* iOS 15.0–15.3 — dvh unsupported there */
  height: 100dvh;  /* iOS 15.4+ — tracks the dynamic viewport */
}

/* --- One fullscreen page --------------------------------------- */
.page {
  height: 100%;
  display: flex;
  flex-direction: column;
  padding-top: calc(var(--sat) + 56px);
  padding-bottom: calc(var(--sab) + 56px); /* room for scroll hint */
}

/* Page title: the serif voice of the menu, flanked by hairlines.
   Clearly above the logo chrome in hierarchy, never shouting. */
.page-label {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin-top: 12px;
  padding-bottom: 8px;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: 500;
  font-size: clamp(27px, 7vw, 34px);
  line-height: 1.15;
  letter-spacing: 0.01em;
  color: var(--cream);
  text-shadow: 0 2px 18px rgba(0, 0, 0, 0.45);
}

.page-label::before,
.page-label::after {
  content: "";
  width: 30px;
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--lime-soft));
}

.page-label::after { transform: scaleX(-1); }

/* Long labels (e.g. "Weekly Kitchen Specials") shrink to one line */
.page-label--long {
  font-size: clamp(21px, 5.5vw, 28px);
  white-space: nowrap;
}

/* Optional week line under the page title (e.g. "Sept 7th – 14th"). */
.page-week {
  flex: 0 0 auto;
  margin-top: 6px;
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-align: center;
  color: var(--cream-dim);
}

/* --- Categorized week label ---------------------------------------
   On categorized pages the week line shows the CURRENT category name
   instead of the page's week text (the original week copy labels the
   trailing uncategorized bucket). Structurally identical to a plain
   week line: the current name is an IN-FLOW span, so the line box keeps
   its natural height — no layout shift ever — while the incoming name
   is an absolutely stacked, aria-hidden twin revealed during swipes.
   js/main.js slides and fades both in sync with swipe progress
   (transform/opacity only). */
.page--categorized .page-week { position: relative; }

.cat-week {
  display: inline-block;
  will-change: transform, opacity;
}

/* The resting state hides the incoming slot; JS drives it from there. */
.cat-week--nxt {
  position: absolute;
  inset: 0;
  opacity: 0;
}

/* Settle ease — applied only when snapping to a category (release,
   chevrons, keyboard). Never present during a drag, so the names
   track the finger with zero lag. */
.page-week--categorized.is-settling .cat-week {
  transition: transform 200ms cubic-bezier(0.22, 0.8, 0.25, 1),
              opacity 200ms ease;
}

/* --- Horizontal drink carousel --------------------------------- */
.swiper--drinks {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
}

.drink {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-inline: 20px;
  cursor: pointer; /* the whole card opens the item card */
  transition: opacity 150ms ease;
}

/* Subtle press affordance on the whole-card tap target */
.drink:active { opacity: 0.85; }

/* Inset ring: the slide is viewport-wide, so an outset ring would
   clip against the carousel's overflow:hidden edges. */
.drink:focus-visible {
  outline: 2px solid var(--lime-bright);
  outline-offset: -4px;
}

/* The stage: one consistent treatment for every drink image.
   Rectangle panel + candlelight glow + thin border. It bottom-aligns
   in its flex area so the panel sits close above the text. */
.drink-figure {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 40px;
}

.drink-stage {
  position: relative;
  width: var(--stage-w);
  max-width: 100%;
  aspect-ratio: 3 / 4;
  max-height: 100%;
  overflow: hidden;
  /* WebKit fix: Safari skips overflow clipping for children that get
     their own compositing layer (object-fit: cover). A mask forces
     GPU-accelerated rounded-corner clipping; Blink ignores it. */
  -webkit-mask-image: -webkit-radial-gradient(white, black);
  border: 1px solid var(--line);
  border-radius: 22px;
  background: linear-gradient(180deg, var(--panel-hi) 0%, var(--panel) 100%);
}

/* All images fill the stage edge-to-edge — no padding, no distortion. */
.drink-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: drop-shadow(0 20px 26px rgba(0, 0, 0, 0.5));
}

/* Full-bleed images sit flush inside the stage's overflow clip, so their
   drop-shadow can never be seen — skip the filter to save a compositing
   layer per slide (pre-15.4 iOS decodes every slide eagerly). */
.drink-img:not(.drink-img--padded) { filter: none; }

/* Pages flagged layout: "edge" and full-bleed drinks fill the stage
   edge-to-edge; every other page gets uniform breathing room. */
.drink-img--padded { padding: 4%; }

/* Imageless specials render a text placeholder card inside the stage. */
.drink-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 12%;
  text-align: center;
}

.drink-placeholder-soon {
  margin: 0;
  font-family: var(--font-body);
  font-size: clamp(11px, 2.8vw, 14px);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--lime-soft, var(--lime, #b8d935));
}

.drink-placeholder-name {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(20px, 5vw, 30px);
  color: var(--cream);
}

.drink-placeholder-desc {
  margin: 0;
  font-family: var(--font-body);
  font-size: clamp(13px, 3.4vw, 16px);
  color: var(--cream-dim);
  max-width: 34ch;
}

/* Dietary labels (menuData vegan/vegetarian flags). */
.drink-tags {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin: 6px 0 0;
}

.drink-tag {
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px solid var(--lime);
  color: var(--lime-bright);
}

.drink-tag--vegan {
  background: var(--lime);
  color: var(--ink);
}

/* <picture> wrapper keeps the stage sizing identical to a plain <img> */
.drink-stage picture {
  display: block;
  width: 100%;
  height: 100%;
}

/* --- Text block: badge · name · price · description ----------- */
.drink-info {
  position: relative;
  flex: 0 0 auto;
  width: 100%;
  min-height: 148px; /* keeps the stage steady across slides */
  padding-top: 12px;
  text-align: center;
}

/* "New!" pill hangs on the seam between stage and text */
.drink-badge {
  position: absolute;
  top: -11px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  white-space: nowrap;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-indent: 0.2em;
  text-transform: uppercase;
  color: var(--ink);
  background: linear-gradient(180deg, var(--lime-bright), var(--lime));
  padding: 5px 11px;
  border-radius: 999px;
  box-shadow: 0 6px 18px rgba(184, 217, 53, 0.32);
}

/* "Sold out" — same pill, but an amber outline on transparent so it
   reads as "paused", not "fresh". The photo greys out and the text
   block dims; the slide keeps its place in the carousel. */
.drink-badge--soldout {
  border: 1px solid var(--amber);
  color: var(--amber);
  background: transparent;
  box-shadow: none;
}

.drink--soldout .drink-img {
  filter: grayscale(1);
  opacity: 0.55;
}

.drink--soldout .drink-info { opacity: 0.8; }

.drink-name {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: 560;
  font-size: clamp(24px, 6.6vw, 32px);
  line-height: 1.14;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

.drink-price {
  margin-top: 9px;
  font-size: 18px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-indent: 0.22em;
  color: var(--lime-bright);
}

.drink-desc {
  margin: 7px auto 0;
  max-width: 36ch;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--cream-dim);
}

/* --- Tiled pages: 2×2 grids of drink tiles per slide ------------
   Pages flagged tiled: true swap one-drink-per-slide for grids of
   four. Same pill family, same sold-out grayscale — just smaller.
   Photos are transparent cutouts, so they contain-fit on the stage
   panel instead of covering it. */
.drinks-grid {
  height: 100%;
  width: 100%;
  max-width: 960px;
  margin-inline: auto;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-template-rows: repeat(2, minmax(0, 1fr));
  /* Any overflow-grid row sizes like the template rows — equal cells. */
  grid-auto-rows: minmax(0, 1fr);
  /* Real gutter between tiles — breathing space, not a chasm. */
  gap: clamp(10px, 2.5vw, 16px);
  /* Outer padding sizes the tiles: side padding clears the chevrons
     and pulls the grid in from the slide edges; bottom padding keeps
     tile text clear of the fraction counter. */
  padding: clamp(8px, 2vh, 18px) clamp(22px, 6vw, 48px) clamp(24px, 4vh, 34px);
}

.drink-tile {
  height: 100%; /* stretch to fill the cell — equal-height neighbours */
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
}

/* Photo box — the cutout sits contain-fit on the stage panel. The panel
   height is a pure function of VIEWPORT WIDTH: 38vw inside a px-clamped
   range. Tile columns are equal 1fr at any viewport (grid capped at
   960px), so a width-derived height is pixel-equal across sister tiles
   and rows regardless of tag/description count. The height expression
   contains ONLY absolute px and vw units — no percentage, no calc(100%…),
   nothing that resolves against the tile or its content: the content-
   driven rail makes each tile's height its own, so any tile-relative
   term re-imports text dependence (the bug this replaces).
   Fit arithmetic — worst-case rail (two wrapped pill rows + 2-line title
   + 2-line description + price ≈ 135px, + 5px stage↔rail gap = 140px;
   ≈128px at 380px where the title font clamps to 13px) against the cell:
   380×844 cell ≈ 300 → 144 + 128 = 272 ✓; 600×960 cell ≈ 350 → 168 (cap)
   + 140 = 308 ✓; 1440×900 cell ≈ 320 → 168 + 140 = 308 ✓ (tightest fit,
   ~12px slack). Ultra-short landscape (~105px cells) cannot fit both by
   definition of a width-only height — there the rail yields (bounded by
   the 2-line clamps, pill wrap and the tile's overflow:hidden), handled
   by the fixed 24px override in the short-landscape media query at the
   foot of this file. flex stays 0 0 auto: fill/shrink would re-import
   the per-tile variance this height exists to remove. */
.tile-stage {
  position: relative;
  flex: 0 0 auto;
  height: clamp(64px, 38vw, 168px);
  min-height: 0;
  width: 100%;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: linear-gradient(180deg, var(--panel-hi) 0%, var(--panel) 100%);
}

.tile-photo-btn {
  display: block;
  width: 100%;
  height: 100%;
  padding: 7%;
  border: 0;
  background: none;
  cursor: zoom-in;
}

.tile-photo-btn picture,
.tile-stage picture {
  display: block;
  width: 100%;
  height: 100%;
}

/* Tile photos render ~10% larger than the button box — a uniform zoom at
   every viewport. The scale lives on the picture wrapper, NOT .tile-img:
   the img's own hover/active transforms (1.03 / 0.98) SET transform and
   would replace a base scale, but on a separate element they compose
   (hover ≈ 1.13, press ≈ 1.08 — order preserved). Contain still governs
   inside the scaled box, and the bleed (5% of the picture box per side
   ≈ 4.3% of the stage) stays inside the button's 7% padding, so even
   edge-to-edge cutouts never reach the frame border — nothing is clipped
   mid-item. Placeholders have no picture element and stay centered; the
   button's hit area still covers the whole stage; cards, lightbox and
   the sold-out grayscale (a filter on the img) are untouched. */
.tile-photo-btn picture {
  transform: scale(1.1);
}

/* Contain, not cover: cutout photos keep their silhouette. The
   .drink-img class stays so the sold-out grayscale rule applies.
   min-height: 0 lets the photo shrink inside a short cell without
   overflowing the contain-fit box. */
.tile-img {
  min-height: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  transition: transform 220ms cubic-bezier(0.22, 0.8, 0.25, 1);
}

.tile-photo-btn:active .tile-img { transform: scale(0.98); }

/* Subtle desktop affordance — a breath of lift, no new chrome. */
@media (hover: hover) {
  .tile-photo-btn:hover .tile-img { transform: scale(1.03); }
  .drink-tile:hover .tile-stage { border-color: rgba(240, 231, 211, 0.22); }
}

/* Text block — content-driven rows in a FIXED visual order: tags →
   title → description → price, packed tight under the photo. Rows are
   implicit auto tracks placed via grid-row (DOM order is unchanged —
   buildTile() still appends name first, which is also the screen-reader
   order), so an absent row — buildTile() omits the tag node entirely and
   skips price/description — collapses to nothing and the rows below it
   move up. No reserved empty tracks. Row rhythm lives in the items'
   margins, not grid row-gap: a margin dies with its element, whereas a
   row-gap would leave a ghost gap behind a collapsed row.
   Tradeoff (accepted): with no fixed tracks, a tile's rail height varies
   with its content, so on mixed grids the photo panels — which still
   flex-grow to fill their cells — can differ slightly between sister
   tiles (typically the height of a missing tag row). Every tile's text
   still hugs its own photo; bottoms are content-driven by design. */
.tile-info {
  flex: 0 0 auto;
  width: 100%;
  min-width: 0;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-auto-rows: auto; /* content-driven — no reserved tracks */
  text-align: center;
}

.tile-name {
  grid-row: 2;
  min-width: 0;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: 560;
  font-size: clamp(13px, 3.2vw, 18px);
  line-height: 1.15;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

/* Badges ride inline in the tag row on tiles — the card's
   seam-hanging position would collide with the grid. */
.drink-tile .drink-badge {
  position: static;
  transform: none;
  text-indent: 0;
  margin: 0;
}

/* Hollow New! pill (tiled view only): transparent fill, the lime
   identity moves to the edge — same recipe as the sold-out pill and
   the diet tags (outline + accent text, no glow). :not() keeps the
   sold-out variant's amber treatment intact; cards and the lightbox
   keep the solid seam-hanging badge. */
.drink-tile .drink-badge:not(.drink-badge--soldout) {
  background: transparent;
  border: 1px solid var(--lime);
  color: var(--lime-bright);
  box-shadow: none;
}

.tile-tags {
  grid-row: 1; /* directly under the photo */
  min-width: 0;
  overflow: hidden;
  /* A whisker of air before the title; dies with the row when absent. */
  margin: 0 0 2px;
  line-height: 1;
  flex-wrap: wrap;
  align-items: flex-start;
  align-content: flex-start;
}

.tile-price {
  grid-row: 4; /* last — tight under the description */
  min-width: 0;
  width: 100%;
  overflow: hidden;
  margin-top: 2px;
  white-space: nowrap;
  text-overflow: ellipsis;
  font-size: 11.5px;
  letter-spacing: 0.16em;
  text-indent: 0.16em;
}

.tile-desc {
  grid-row: 3;
  min-width: 0;
  margin: 2px auto 0;
  /* Centre the 30ch box itself: a stretched grid item capped by
     max-width falls back to start alignment, which parked the box
     (and its centering axis) left of the title's on wide tiles. */
  max-width: 30ch;
  font-size: 11.5px;
  line-height: 1.45;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Placeholder tiles reuse the card content, scaled to the grid. */
.tile-placeholder { padding: 10%; gap: 6px; }
.tile-placeholder .drink-placeholder-soon { font-size: 9px; letter-spacing: 0.1em; }
.tile-placeholder .drink-placeholder-name { font-size: clamp(13px, 3vw, 17px); }
.tile-placeholder .drink-placeholder-desc {
  font-size: 10.5px;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* On tiled pages the card stage geometry doesn't apply — centre the
   chevrons on the slide instead. */
.swiper--drinks--tiled .swiper-button-prev,
.swiper--drinks--tiled .swiper-button-next {
  top: 50%;
  bottom: auto;
  transform: translateY(-50%);
}

/* Single-grid tiled pages: one grid, nothing to page. */
.swiper--drinks--single .swiper-button-prev,
.swiper--drinks--single .swiper-button-next,
.swiper--drinks--single .swiper-pagination { display: none; }

.tile-photo-btn:focus-visible,
.lightbox-close:focus-visible {
  outline: 2px solid var(--lime-bright);
  outline-offset: 3px;
}

/* --- Prev/next chevrons -----------------------------------------
   Featherweight line arrows tucked at the screen edges, centred
   on the stage. Loop mode wraps around, so they never disable.
   44px hit area, 10px glyph — supplement swipe, don't replace. */
.swiper--drinks .swiper-button-prev,
.swiper--drinks .swiper-button-next {
  top: auto;
  bottom: calc(var(--info-h) + var(--stage-w) * 2 / 3);
  left: max(4px, env(safe-area-inset-left, 0px));
  right: auto;
  width: 44px;
  height: 44px;
  margin-top: 0;
  transform: translateY(50%);
  background: none;
  color: var(--cream);
  opacity: 0.55;
  transition: opacity 0.3s ease;
}

.swiper--drinks .swiper-button-next {
  left: auto;
  right: max(4px, env(safe-area-inset-right, 0px));
}

.swiper--drinks .swiper-button-prev::after,
.swiper--drinks .swiper-button-next::after {
  content: "";
  width: 10px;
  height: 10px;
  border-left: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  filter: drop-shadow(0 1px 5px rgba(0, 0, 0, 0.6));
}

.swiper--drinks .swiper-button-prev::after { transform: rotate(45deg); }
.swiper--drinks .swiper-button-next::after { transform: rotate(-135deg); }

@media (hover: hover) {
  .swiper--drinks .swiper-button-prev:hover,
  .swiper--drinks .swiper-button-next:hover { opacity: 0.95; }
}

.swiper--drinks .swiper-button-prev:active,
.swiper--drinks .swiper-button-next:active { opacity: 0.95; }

/* Only reachable if loop is ever switched off */
.swiper--drinks .swiper-button-disabled { opacity: 0.15; }

/* --- Position: minimal "2 / 4" fraction ------------------------ */
.swiper--drinks .swiper-pagination {
  position: static;
  margin-top: 14px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.24em;
  text-align: center;
  color: var(--cream-dim);
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.55);
}

.swiper--drinks .swiper-pagination-current {
  color: var(--lime-bright);
  font-size: 13px;
}

.swiper--drinks .swiper-pagination-total { color: var(--cream-dim); }

/* --- Fullscreen item-card lightbox --------------------------------
   A modal <dialog> generated by js/main.js: dark backdrop, an item
   card (photo stage on top, text block below) centred in a gutter.
   Zoom/pan are pointer-event transforms scoped to the photo stage —
   the text block scrolls with native touch/wheel. Transform
   transitions animate only the double-tap toggle; direct
   manipulation stays immediate. */
.lightbox {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  border: 0;
  overflow: hidden;
  padding: clamp(12px, 3vw, 32px); /* backdrop gutter around the card */
  background: rgba(5, 14, 9, 0.96);
  color: var(--cream);
  user-select: none;
  -webkit-user-select: none;
  opacity: 1;
  transition: opacity 200ms ease, overlay 200ms ease, display 200ms ease;
  transition-behavior: allow-discrete;
}

.lightbox[open] {
  display: flex;
  align-items: center;
  justify-content: center;
}

@starting-style {
  .lightbox[open] { opacity: 0; }
}

.lightbox::backdrop { background: rgba(5, 14, 9, 0.8); }

/* The card: same panel treatment as .drink-stage. Fills the viewport
   (minus gutter) on phones; centred at a reading width on desktop. */
.lightbox-card {
  flex: 0 1 auto;
  width: 100%;
  max-width: 680px;
  height: 100%;
  min-height: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid var(--line);
  border-radius: 22px;
  background: linear-gradient(180deg, var(--panel-hi) 0%, var(--panel) 100%);
  box-shadow: 0 30px 80px rgba(0, 0, 0, 0.5);
}

/* Photo stage — the only zoom surface (touch-action: none here and
   nowhere else, so the text block keeps native scrolling). */
.lightbox-stage {
  position: relative;
  flex: 1 1 auto;
  min-height: clamp(120px, 28dvh, 260px);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  touch-action: none; /* pointer events own every gesture here */
  overscroll-behavior: none;
  cursor: zoom-in;
}

.lightbox-stage.is-zoomed { cursor: grab; }
.lightbox-stage.is-grabbing { cursor: grabbing; }

.lightbox-img {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  transform-origin: center center;
  will-change: transform;
  transition: transform 220ms cubic-bezier(0.22, 0.8, 0.25, 1);
  -webkit-user-drag: none;
  filter: drop-shadow(0 24px 44px rgba(0, 0, 0, 0.55));
}

/* While a finger/mouse is actively moving the photo, transforms must
   apply immediately — the transition is only for the double-tap zoom. */
.lightbox-img.is-direct { transition: none; }

/* Sold-out cards grey the photo exactly like tiles and cards do. */
.lightbox.drink--soldout .lightbox-img {
  filter: grayscale(1);
  opacity: 0.55;
}

/* Text-only item cards (placeholder items have no photo): the stage
   is removed entirely and the text block takes the whole card,
   centred. */
.lightbox-stage[hidden] { display: none; }

.lightbox-card.is-text-only { justify-content: center; }

.lightbox-card.is-text-only .lightbox-info {
  max-height: 100%;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* Text block — its own scroll when the description runs long (capped
   so the stage keeps its share); the × lives on the dialog, never
   inside this scroll, so it stays reachable. */
.lightbox-info {
  flex: 0 1 auto;
  min-height: 0;
  max-height: 60%;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 18px 22px 22px;
  text-align: center;
  scrollbar-color: var(--lime-soft) transparent;
  user-select: text;
  -webkit-user-select: text;
}

.lightbox-name {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-weight: 560;
  font-size: clamp(22px, 5vw, 32px);
  line-height: 1.14;
  letter-spacing: -0.01em;
  text-wrap: balance;
}

/* Pills ride inline here (as on tiles) — the badge's default
   seam-hanging position would collide with the panel top. */
.lightbox-tags {
  justify-content: center;
  margin-top: 10px;
}

.lightbox-tags .drink-badge {
  position: static;
  transform: none;
  text-indent: 0;
  margin: 0;
}

/* .drink-tags sets display:flex, which would beat the UA's [hidden] */
.lightbox-tags[hidden] { display: none; }

.lightbox-desc {
  margin-top: 10px;
  max-width: 44ch;
  margin-inline: auto;
  font-size: 14px;
  line-height: 1.6;
}

@media (min-width: 600px) {
  .lightbox-info { padding: 24px 32px 28px; }
}

.lightbox-close {
  position: absolute;
  top: calc(var(--sat) + 14px);
  right: max(14px, env(safe-area-inset-right, 0px));
  z-index: 2;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: rgba(5, 14, 9, 0.72);
  color: var(--cream);
  font-family: var(--font-body);
  font-size: 28px;
  font-weight: 300;
  line-height: 1;
  cursor: pointer;
  transition: color 180ms ease, border-color 180ms ease, background 180ms ease;
}

@media (hover: hover) {
  .lightbox-close:hover {
    border-color: var(--lime-soft);
    background: rgba(184, 217, 53, 0.08);
    color: var(--lime-bright);
  }
}

@media (prefers-reduced-motion: reduce) {
  .lightbox,
  .lightbox-img,
  .tile-img { transition: none; }
}

/* --- Scroll hint (hidden on the last page via body class) ----- */
.scroll-hint {
  position: fixed;
  left: 50%;
  bottom: calc(var(--sab) + 16px);
  transform: translateX(-50%);
  z-index: 30;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border: 1px solid var(--lime-soft);
  border-radius: 50%;
  background: rgba(5, 14, 9, 0.55);
  color: var(--lime-bright);
  pointer-events: none;
}

.scroll-hint svg {
  width: 14px;
  height: 14px;
  animation: hint-bob 1.9s ease-in-out infinite;
}

@keyframes hint-bob {
  0%, 100% { transform: translateY(-2px); }
  50%      { transform: translateY(2.5px); }
}

body.is-last-page .scroll-hint {
  opacity: 0;
  transform: translateX(-50%) translateY(10px);
}

/* --- Page-load nudge: brief upward shift to hint at scrolling ---- */
.swiper--pages {
  animation: nudge-from-below 0.7s ease-in-out 0.8s 1 both;
}
@keyframes nudge-from-below {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-20px); }
}
@media (prefers-reduced-motion: reduce) {
  .swiper--pages { animation: none; }
}

/* --- Noscript fallback ----------------------------------------- */
.noscript-note {
  position: fixed;
  left: 50%;
  bottom: 20px;
  transform: translateX(-50%);
  z-index: 70;
  padding: 10px 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel);
  font-size: 13px;
}

/* --- Reduced motion: carousels only move on input, no bobbing, no rail ease --- */
@media (prefers-reduced-motion: reduce) {
  .scroll-hint svg { animation: none; }
  .page-week--categorized.is-settling .cat-week { transition: none; }
}

/* --- Large portrait (iPad etc.): same column, scaled up -------- */
@media (min-width: 600px) and (orientation: portrait) {
  :root {
    --stage-w: min(62vw, 46vh, 430px);
    --info-h:  217px;
  }
  .drink-info  { min-height: 190px; }
  .drink-name  { font-size: clamp(30px, 4.4vw, 42px); }
  /* Scoped to cards: .drink-price/.drink-desc are shared with the
     tiles' text rail, and these bumps used to leak in at ≥600px,
     clipping the tiles' 2-line clamp inside its fixed grid track. */
  .drink .drink-price { font-size: 19px; }
  .drink .drink-desc  { font-size: 15px; max-width: 40ch; }
  /* Keep the lightbox price at its current size — it previously
     inherited this bump through the unscoped rule. */
  .lightbox .lightbox-price { font-size: 19px; }
}

/* --- Landscape / wide: stage left, text right ------------------
   Deliberate side-by-side composition; the stage is sized by
   viewport height so nothing ever crops on short screens. */
@media (orientation: landscape) and (min-width: 600px) {
  :root {
    --stage-w: min(45vh, 38vw, 480px);
    --info-h:  27px;   /* fraction footprint only — text sits beside */
  }
  .page {
    padding-top: calc(var(--sat) + 56px);
    padding-bottom: calc(var(--sab) + 40px);
  }
  .drink {
    flex-direction: row;
    justify-content: center;
    gap: clamp(22px, 4vw, 64px);
    /* Never let slide content tuck under the notch/Dynamic Island. */
    padding-inline: max(clamp(20px, 5vw, 72px), env(safe-area-inset-left))
                    max(clamp(20px, 5vw, 72px), env(safe-area-inset-right));
  }
  .drink-figure {
    flex: 0 0 auto;
    height: 100%;
    width: auto;
    align-items: center;
  }
  .drink-info {
    flex: 0 1 420px;
    width: auto;
    min-height: 0;
    padding-top: 0;
    text-align: left;
  }
  /* Scoped to cards — tiles reuse .drink-badge/.drink-desc, and the
     unscoped rules re-spaced and left-aligned the tile text rail the
     moment the viewport flipped to landscape. */
  .drink .drink-badge {
    position: static;
    display: inline-block;
    transform: none;
    margin-bottom: 14px;
  }
  .drink-name { font-size: clamp(24px, 3.2vw, 44px); }
  .drink .drink-desc { margin-inline: 0; }
  /* Stage is vertically centred here — arrows centre on the slide */
  .swiper--drinks .swiper-button-prev,
  .swiper--drinks .swiper-button-next {
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
  }
}

/* --- Short landscape (phones): tighten everything -------------- */
@media (orientation: landscape) and (min-width: 600px) and (max-height: 480px) {
  :root { --stage-w: min(40vh, 38vw, 480px); }
  .page {
    padding-top: calc(var(--sat) + 48px);
    padding-bottom: calc(var(--sab) + 32px);
  }
  .page-label {
    font-size: 19px;
    gap: 10px;
    padding-bottom: 2px;
  }
  .page-label::before,
  .page-label::after { width: 16px; }
  .drink-info  { max-width: 46vw; }
  /* Scoped to cards — see the landscape block above. */
  .drink .drink-badge { margin-bottom: 10px; }
  .drink-name  { font-size: clamp(19px, 3vw, 30px); }
  .drink .drink-desc  { font-size: 12.5px; }
  .swiper--drinks .swiper-pagination { margin-top: 8px; }
}

/* Fixed stage height in short landscape (own query — the block above is
   min-width: 600px, but small phones landscape at 568×320 need it too).
   A width-derived height cannot see the ~105px cells here, so the clamp
   is overridden with a FIXED 24px panel: still equal across every tile
   (the requirement), and it preserves the text capacity flex-fill used
   to give (its stages were ≈22px slivers) instead of annihilating the
   rail under a desktop-sized panel. */
@media (orientation: landscape) and (max-height: 480px) {
  .tile-stage { height: 24px; }
}
