/* ==========================================================================
   Ресторан Качунка — design system
   Dark wood. Every value in the palette was sampled from the restaurant's own
   materials: the back room's timber and stone, the honey wood of the hero, and
   the near-black board their printed menu is chalked onto.
   Mobile-first without exception. Base rules are the phone.
   ========================================================================== */

:root {
  /* --- surfaces: neutrals tinted toward the wood hue (~65°), never grey ---- */
  --paper:      oklch(17.7% 0.010 67);
  --band:       oklch(21.4% 0.015 67);
  --card:       oklch(23.4% 0.018 63);
  --pitch:      oklch(12.5% 0.008 67);   /* the hero's black, and the footer */

  /* --- ink ---------------------------------------------------------------- */
  --ink:        oklch(94.6% 0.018 78);   /* 16.2:1 on paper */
  --ink-2:      oklch(80.4% 0.025 74);   /* 10.3:1 */
  --ink-3:      oklch(65.8% 0.030 73);   /*  6.0:1 — the quietest text allowed */

  /* --- one accent, the lamplight on wood ---------------------------------- */
  --accent:     oklch(71.9% 0.128 69);   /*  7.4:1 — safe even at body size */
  --accent-dim: oklch(57.2% 0.127 54);

  --hairline:   color-mix(in oklch, var(--ink) 16%, transparent);
  --hairline-2: color-mix(in oklch, var(--ink) 8%, transparent);

  /* --- 4pt spacing scale, semantic ---------------------------------------- */
  --space-2xs: 0.25rem;
  --space-xs:  0.5rem;
  --space-sm:  0.75rem;
  --space-md:  1rem;
  --space-lg:  1.5rem;
  --space-xl:  2rem;
  --space-2xl: 3rem;
  --space-3xl: 4rem;
  --space-4xl: 6rem;
  --gutter:    clamp(1.25rem, 5vw, 4rem);
  --section:   clamp(4rem, 13vw, 9rem);
  --bar-h:     4.25rem;   /* the menu page's sticky pill bar: 44px + padding */

  /* --- type: one high-contrast serif, one workhorse ------------------------ */
  --display: 'Cormorant', Georgia, 'Times New Roman', serif;
  --body:    'Golos Text', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --step--1: 0.8125rem;
  --step-0:  1.0625rem;
  --step-1:  clamp(1.25rem, 1.05rem + 1vw, 1.5rem);
  --step-2:  clamp(1.6rem, 1.3rem + 1.6vw, 2.1rem);
  --step-3:  clamp(2.1rem, 1.6rem + 2.6vw, 3rem);
  --step-4:  clamp(2.9rem, 1.9rem + 5vw, 5rem);
  --step-5:  clamp(3.6rem, 2rem + 8vw, 8rem);

  --ease: cubic-bezier(0.16, 1, 0.3, 1);       /* ease-out-expo, no bounce */
  /* the sheet's own curve: slow in, slow out, fast through the middle. The
     drawn curve in nav.js is stepped with the same shape, so the leading edge
     and the panel stay locked together for all 800ms. */
  --ease-nav: cubic-bezier(0.76, 0, 0.24, 1);
  --measure: 62ch;

  /* --- elevation: two levels, and that is the whole system ----------------
     On a palette this dark a shadow barely registers, so lift is carried by
     surface lightness. These only seat the control on the page. --------- */
  --lift-1: 0 1px 2px oklch(12.5% 0.008 67 / 0.55);
  --lift-2: 0 4px 12px oklch(12.5% 0.008 67 / 0.6);

  color-scheme: dark;
}

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

html {
  -webkit-text-size-adjust: 100%;
  /* No `scroll-behavior: smooth` — Lenis owns easing site-wide now
     (smooth-scroll.js), and a second smoothing pass on top of it fights
     every programmatic jump. */
  /* the nav sheet locks scrolling while it is open; reserving the gutter up
     front means removing the scrollbar can't shift the page under it */
  scrollbar-gutter: stable;
  /* `clip`, not `hidden`, and on html rather than body: `overflow-x: hidden` on
     body turns body into its own scroll container, which breaks the scroll
     position the pinned hero is measured against. */
  overflow-x: clip;
}

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: var(--step-0);
  /* light type on dark reads lighter — leading is bumped to compensate */
  line-height: 1.68;
  font-synthesis-weight: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

img, picture, canvas, svg, iframe { display: block; max-width: 100%; }
/* Without this, the width/height attributes silently override aspect-ratio. */
img { height: auto; }

a { color: inherit; }

:where(button, a, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}

::selection { background: var(--accent); color: var(--pitch); }

/* --- typography ----------------------------------------------------------- */
h1, h2, h3 {
  font-family: var(--display);
  font-weight: 600;          /* Cormorant is light by nature; on dark it needs it */
  line-height: 1.02;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

h1 { font-size: var(--step-5); }
h2 { font-size: var(--step-4); }
h3 { font-size: var(--step-2); letter-spacing: -0.01em; line-height: 1.12; }

p { text-wrap: pretty; }

.lead {
  font-family: var(--display);
  font-size: var(--step-2);
  line-height: 1.32;
  color: var(--ink-2);
  max-width: 22ch;
}

.prose { max-width: var(--measure); color: var(--ink-2); }
.prose + .prose { margin-top: var(--space-md); }

/* The one small-caps device: a tracked label, never a paragraph. */
.kicker {
  font-family: var(--body);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent);
  display: block;
}

.quiet { color: var(--ink-3); }
.tabular { font-variant-numeric: tabular-nums; }

/* Read aloud, never drawn. Carries the text of anything JS takes apart. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --- layout --------------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: 78rem;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section { padding-block: var(--section); }
.section--band { background: var(--band); }
.section--pitch { background: var(--pitch); }

/* A hand-drawn rule, not a 1px line — it echoes the brush circle in the logo
   and the chalked frame on their real menu board. */
.rule {
  width: 100%;
  height: 9px;
  color: var(--hairline);
  margin-block: var(--space-2xl);
}
.rule svg { width: 100%; height: 100%; }

/* ==========================================================================
   Header + sticky call bar
   ========================================================================== */

.head {
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 20;
  padding-block: var(--space-md);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-md);
}

.head__mark {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-family: var(--display);
  font-size: var(--step-1);
  font-weight: 600;
  letter-spacing: 0.01em;
  text-decoration: none;
  color: var(--ink);
}

/* Phone lives in a fixed bar on the phone, and inline in the header above it.
   Everything else the header used to carry now lives in the nav sheet. */
.head__call { display: none; }

/* ==========================================================================
   Navigation — a sheet that slides in from the right behind a drawn curve
   Without JS this is a plain row of links in the header, exactly what the
   header carried before the sheet existed. `body.js`, set by nav.js, is what
   turns it into the sheet — a script that never arrives cannot leave the site
   without navigation, and the burger stays hidden until it can do something.
   ========================================================================== */

.nav {
  position: absolute;
  inset-block-start: 0;
  inset-inline-end: 0;
  z-index: 21;
  padding: var(--space-md) var(--gutter);
  font-size: var(--step--1);
}

.nav__inner,
.nav__top,
.nav__row { display: flex; align-items: center; gap: var(--space-xs); }

.nav__list {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  list-style: none;
  padding: 0;
}

/* the sheet's furniture — it has nothing to say inside a header row */
.nav__label,
.nav__num,
.nav__social,
.nav__call,
.nav__curve { display: none; }

.nav__link,
.nav__lang {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--space-sm);
  letter-spacing: 0.02em;
  text-decoration: none;
  color: var(--ink-2);
  transition: color 0.25s var(--ease);
}
.nav__link:hover,
.nav__lang:hover { color: var(--accent); }
.nav__lang { justify-content: center; min-width: 44px; letter-spacing: 0.14em; text-transform: uppercase; }

/* --- the burger: fixed, so it outlives the header it starts in ------------ */
.burger[hidden] { display: none; }

.burger {
  position: fixed;
  inset-block-start: var(--space-md);
  /* lines up with the wrap's inner edge rather than the viewport's, so on a
     wide screen it sits over the content column, not adrift beside it */
  inset-inline-end: max(var(--gutter), calc(50vw - 39rem + var(--gutter)));
  z-index: 2;
  pointer-events: auto;
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--hairline-2);
  background: color-mix(in oklch, var(--pitch) 55%, transparent);
  box-shadow: var(--lift-1);
  -webkit-backdrop-filter: blur(12px) saturate(140%);
          backdrop-filter: blur(12px) saturate(140%);
  color: var(--ink);
  cursor: pointer;
  transition: background-color 0.5s var(--ease),
              border-color 0.5s var(--ease),
              box-shadow 0.5s var(--ease),
              color 0.25s var(--ease);
}
.burger:hover {
  color: var(--accent);
  border-color: var(--hairline);
  background: color-mix(in oklch, var(--pitch) 72%, transparent);
}
.burger:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.burger__icon { display: block; width: 22px; height: 22px; overflow: visible; }
/* The three bars share one origin — the centre of the 24-unit viewBox — so the
   fold is rotate-then-translate, not the translate-then-rotate a stack of
   absolutely positioned spans would use. */
.burger__bar {
  transform-box: view-box;
  transform-origin: 12px 12px;
  transition: transform 0.5s var(--ease-nav), opacity 0.2s linear;
}

/* open: the plate dissolves into the sheet behind it and the bars become
   lucide `x` — endpoints land on (6,6)-(18,18) and (18,6)-(6,18) */
.nav--open .burger { background: transparent; border-color: transparent; box-shadow: none; }
.nav--open .burger__bar:nth-child(1) { transform: rotate(45deg) translateY(6px); }
.nav--open .burger__bar:nth-child(2) { opacity: 0; transform: scaleX(0.2); }
.nav--open .burger__bar:nth-child(3) { transform: rotate(-45deg) translateY(-6px); }

/* --- with JS: the sheet --------------------------------------------------- */
body.js .nav {
  position: fixed;
  inset: 0;
  z-index: 46;   /* over the header (20) and the call bar (40), under the loader (50) */
  padding: 0;
  font-size: var(--step-0);
  pointer-events: none;   /* only the burger, scrim and sheet take clicks */
}

body.js .nav__scrim {
  position: absolute;
  inset: 0;
  background: oklch(12.5% 0.008 67 / 0.55);
  -webkit-backdrop-filter: blur(2px);
          backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.6s var(--ease);
}
body.js .nav--open .nav__scrim { opacity: 1; pointer-events: auto; }

body.js .nav__sheet {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  z-index: 1;
  width: min(100%, 26rem);
  background: var(--card);
  box-shadow: -30px 0 70px oklch(12.5% 0.008 67 / 0.5);
  /* parked past the edge by the width of the curve, so the curve is off-screen
     too and never flashes at the right edge before the sheet moves */
  transform: translateX(calc(100% + 10rem));
  visibility: hidden;
  pointer-events: auto;
}
body.js .nav--open .nav__sheet { transform: none; visibility: visible; }

/* The sheet is parked the instant `body.js` lands, and parking must not be
   animated — otherwise every page load shows the panel sliding out of a place
   it was never meant to be. nav.js adds `nav--ready` a frame later, and only
   then does the sheet start moving under transition. */
.nav--ready .nav__sheet { transition: transform 0.8s var(--ease-nav), visibility 0s linear 0.8s; }
.nav--ready.nav--open .nav__sheet { transition: transform 0.8s var(--ease-nav), visibility 0s; }

/* The leading edge: an SVG crescent that bulges out ahead of the sheet and
   settles flat against it. Drawn by nav.js — a quadratic whose control point
   travels the width of the curve. preserveAspectRatio="none" is what lets one
   1000-unit-tall viewBox stretch to any viewport without a resize listener. */
body.js .nav__curve {
  display: block;
  position: absolute;
  inset-block: 0;
  inset-inline-end: 100%;
  width: 10rem;
  height: 100%;
  fill: var(--card);
  pointer-events: none;
}

body.js .nav__inner {
  display: flex;
  flex-direction: column;
  align-items: stretch;   /* the row's `center` would shrink-wrap every block */
  justify-content: space-between;
  gap: var(--space-xl);
  height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* clears the burger, which floats above this */
  padding: var(--space-4xl) var(--gutter) var(--space-xl);
}

body.js .nav__top { display: block; }

body.js .nav__label {
  display: block;
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding-block-end: var(--space-sm);
  border-block-end: 1px solid var(--hairline-2);
}

body.js .nav__list { display: block; }

body.js .nav__link {
  display: flex;
  align-items: baseline;
  gap: var(--space-sm);
  min-height: 0;
  padding-block: clamp(0.7rem, 2.6vh, 1.2rem);
  padding-inline: 0;
  border-block-end: 1px solid var(--hairline-2);
  color: var(--ink);
  transition: border-color 0.4s var(--ease);
}
body.js .nav__link:hover { border-color: color-mix(in oklch, var(--accent) 45%, transparent); }

body.js .nav__num {
  display: block;
  flex: none;
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.1em;
  color: var(--accent);
  opacity: 0.7;
  transition: opacity 0.4s var(--ease);
}
body.js .nav__link:hover .nav__num { opacity: 1; }

/* The word and its letters travel in opposite directions by the same amount,
   so nothing actually moves — but the letters are staggered, so the word
   ripples open and closes back up. Straight from the original component. */
body.js .nav__word {
  display: inline-block;
  font-family: var(--display);
  font-size: clamp(2.1rem, 8.5vw, 2.75rem);
  font-weight: 500;
  line-height: 1.06;
  letter-spacing: -0.01em;
  transition: transform 0.55s var(--ease), color 0.3s var(--ease);
}
body.js .nav__word i {
  display: inline-block;
  font-style: normal;
  transition: transform 0.55s var(--ease);
  transition-delay: calc(var(--i, 0) * 28ms);
}
body.js .nav__link:hover .nav__word,
body.js .nav__link:focus-visible .nav__word { transform: translateX(-0.3em); color: var(--accent); }
body.js .nav__link:hover .nav__word i,
body.js .nav__link:focus-visible .nav__word i { transform: translateX(0.3em); }
body.js .nav__link[aria-current="page"] .nav__word { color: var(--accent); }

body.js .nav__foot {
  display: grid;
  gap: var(--space-md);
  padding-block-start: var(--space-md);
  border-block-start: 1px solid var(--hairline-2);
}
body.js .nav__row { justify-content: space-between; gap: var(--space-sm); }

body.js .nav__call {
  display: inline-flex;
  align-items: center;
  font-family: var(--display);
  font-size: var(--step-2);
  font-weight: 600;
  letter-spacing: 0.01em;
  text-decoration: none;
  color: var(--ink);
  transition: color 0.25s var(--ease);
}
body.js .nav__call:hover { color: var(--accent); }

body.js .nav__social {
  display: flex;
  gap: var(--space-2xs);
  list-style: none;
  padding: 0;
  margin-inline-start: -0.65rem;   /* optical: 44px targets, 20px marks */
}
body.js .nav__social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  color: var(--ink-3);
  transition: color 0.25s var(--ease);
}
body.js .nav__social a:hover { color: var(--accent); }
body.js .nav__social svg { width: 20px; height: 20px; }

body.js .nav__lang {
  border: 1px solid var(--hairline);
  color: var(--ink-2);
  font-size: var(--step--1);
  transition: border-color 0.25s var(--ease), color 0.25s var(--ease);
}
body.js .nav__lang:hover { border-color: var(--accent); }

/* the scroll lock — while the sheet is open the page underneath holds still */
html.nav-lock, html.nav-lock body { overflow: hidden; }

.callbar {
  position: fixed;
  inset-block-end: 0;
  inset-inline: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
  min-height: 56px;
  padding-inline: var(--gutter);
  background: var(--accent);
  color: var(--pitch);
  font-weight: 700;
  letter-spacing: 0.02em;
  text-decoration: none;
  box-shadow: 0 -12px 32px oklch(12.5% 0.008 67 / 0.55);
  transform: translateY(100%);
  transition: transform 0.5s var(--ease);
}
.callbar--in { transform: translateY(0); }
.callbar svg { width: 18px; height: 18px; flex: none; }

/* the bar covers the end of the page, so give the footer room */
body { padding-block-end: 56px; }

/* ==========================================================================
   Hero — pinned canvas scrubbing the frame sequence
   ========================================================================== */

.hero {
  position: relative;
  /* Track length. 100vh of this is the pin itself, the rest is scrub distance.
     Overridden to 100vh for reduced motion and every no-scrub fallback. */
  height: var(--hero-track, 450vh);
  background: var(--pitch);
}

.hero__pin {
  position: sticky;
  inset-block-start: 0;
  height: 100svh;
  overflow: hidden;
}

.hero__poster,
.hero__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* The poster is CONTAINED, not covered, because the canvas it hands over to
   starts contained too — the whole frame visible on black so the wordmark reads.
   Covering here would show a hugely magnified slice of the logo and then jump. */
.hero__poster { object-fit: contain; background: var(--pitch); }

/* The poster is a real <img> and stays in the DOM the whole time: it is the
   LCP element (a bare canvas is invisible to the LCP algorithm) and it is what
   shows through if the frame sequence fails at any point. */
.hero__canvas {
  opacity: 0;
  transition: opacity 0.6s var(--ease);
}
.hero__canvas--in { opacity: 1; }

.hero__veil {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top,
    oklch(12.5% 0.008 67 / 0.86) 0%,
    oklch(12.5% 0.008 67 / 0.45) 28%,
    transparent 62%
  );
  pointer-events: none;
}

.hero__say {
  position: absolute;
  inset-block-end: calc(56px + var(--space-xl));
  inset-inline: 0;
  padding-inline: var(--gutter);
  opacity: 0;
  transform: translateY(14px);
}
.hero__say h1 { margin-block-end: var(--space-sm); }
.hero__say .lead { max-width: 26ch; color: var(--ink); }

/* The one link out of the hero. Underlined rather than bordered so the rule
   hugs the words instead of sitting at the bottom of the 44px target, and
   quiet enough that it never competes with the call bar directly under it. */
.hero__more {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  min-height: 44px;
  margin-block-start: var(--space-xs);
  font-family: var(--body);
  font-size: var(--step-0);
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 6px;
  transition: color 0.25s var(--ease), text-decoration-color 0.25s var(--ease);
}
.hero__more:hover { color: var(--accent); }
.hero__more svg {
  width: 1em;
  height: 1em;
  flex: none;
  transition: translate 0.25s var(--ease);
}
.hero__more:hover svg { translate: 3px 0; }

.hero__scroll {
  position: absolute;
  inset-block-end: calc(56px + var(--space-sm));
  inset-inline-start: var(--gutter);
  font-size: var(--step--1);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-3);
  transition: opacity 0.4s var(--ease);
}

/* The idle nudge — the corner cue above is ambient, and ambient is exactly
   what a first-time visitor's eye skips. Three seconds of not scrolling on a
   screen whose only instruction is "scroll" is somebody who does not know
   that, so after three seconds they are told in a whole sentence, centred,
   with an arrow that moves. It leaves the instant the scroll it asked for
   arrives, and while it is up the corner cue steps aside — they sit on the
   same line and say the same thing. */
.hero__nudge {
  position: absolute;
  inset-block-end: calc(56px + var(--space-sm));
  inset-inline: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-xs);
  padding-inline: var(--gutter);
  opacity: 0;
  translate: 0 12px;
  pointer-events: none;
  transition: opacity 0.55s var(--ease), translate 0.55s var(--ease);
}
.hero__nudge b {
  padding: 0.5rem 1rem;
  font-family: var(--body);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--ink);
  text-align: center;
  /* Opaque, not a blur: this floats over a canvas that repaints on every
     scroll tick, and backdrop-filter over moving pixels is the one thing
     that reliably drops a phone below 60fps. */
  background: oklch(12.5% 0.008 67 / 0.82);
  border: 1px solid var(--hairline);
  border-radius: 999px;
}
.hero__nudge svg {
  width: 20px;
  height: 20px;
  color: var(--accent);
}

.hero--nudging .hero__nudge { opacity: 1; translate: 0 0; }
.hero--nudging .hero__nudge svg { animation: nudge-bob 1.6s var(--ease) infinite; }
/* the sheet is a full-screen surface — nothing underneath it should be asking
   for attention while it is open */
.nav-lock .hero__nudge { opacity: 0; }

@keyframes nudge-bob {
  0%, 100% { translate: 0 0; opacity: 0.75; }
  50%      { translate: 0 5px; opacity: 1; }
}

/* Without JS, or with reduced motion, the hero is one still frame at 100vh
   and the headline is simply present. */
.hero--still { height: 100svh; }
.hero--still .hero__say { opacity: 1; transform: none; }
.hero--still .hero__scroll,
.hero--still .hero__nudge { display: none; }

/* ==========================================================================
   The slogan — "Како Дома", typed onto the empty board
   ========================================================================== */

/* Hidden by default and shown only under body.js, then hidden again wherever
   the sequence doesn't run: with no animation the hero is the logo poster, and
   there is no empty board to write the line on. */
.hero__slogan { display: none; }
body.js .hero__slogan { display: block; }
/* `body.js` in the selector is not decoration: without it this rule loses to the
   one above on specificity and the slogan lands on top of the logo poster. */
body.js .hero--still .hero__slogan { display: none; }

.hero__slogan {
  position: absolute;
  inset-block-start: 50%;
  inset-inline: 0;
  translate: 0 -50%;
  padding-inline: var(--gutter);
  text-align: center;
  font-family: var(--display);
  font-weight: 600;
  font-size: clamp(2.75rem, 1.5rem + 6.2vw, 6.5rem);
  line-height: 1.05;
  letter-spacing: -0.012em;
  color: var(--ink);
  text-shadow: 0 0.02em 0.14em oklch(12.5% 0.008 67 / 0.45);
}

/* Warm off-white on mid-brown wood is only about 4:1. A pool of shade behind
   the line — not a flat scrim over the whole frame — takes it past 6.5:1
   without dulling the board it is sitting on. Kept very large and very soft on
   purpose: any tighter and it reads as a stain on the wood rather than light
   falling off. */
.hero__slogan::before {
  content: "";
  position: absolute;
  inset: -340% -30%;
  z-index: -1;
  background: radial-gradient(
    50% 50% at 50% 50%,
    oklch(12.5% 0.008 67 / 0.5) 0%,
    oklch(12.5% 0.008 67 / 0.3) 38%,
    oklch(12.5% 0.008 67 / 0.1) 62%,
    transparent 80%
  );
  opacity: 0;
  transition: opacity 1.4s var(--ease);
  pointer-events: none;
}
.hero__slogan--lit::before { opacity: 1; }

/* The caret: ONE element moved on animated custom properties, rather than a
   pseudo-element per letter being switched on and off. It glides, which is the
   difference between this and a `steps()` wipe. `--caret-o` defaults to 0, so a
   page that never types never shows a cursor. */
.ty { position: relative; display: inline-block; }

.ty::after {
  content: "";
  position: absolute;
  inset-block-start: var(--caret-y, 0px);
  inset-inline-start: var(--caret-x, 0px);
  /* Cormorant's own metrics: cap height 0.625em, and with line-height 1.05 the
     cap top lands 0.218em below the top of the letter's box. The caret spans
     exactly that, so it sits on the baseline instead of hanging under it. */
  width: max(1px, 0.035em);
  height: 0.625em;
  margin-block-start: 0.218em;
  background: var(--accent);
  opacity: var(--caret-o, 0);
  pointer-events: none;
}

/* Words are inline-block so a per-letter split can never break a word across
   two lines; the space keeps its width because inline-block would collapse it. */
.ty__word { display: inline-block; }
.ty__char { display: inline-block; }
.ty__space { white-space: pre; }
.ty--live .ty__char { will-change: opacity, filter, transform; }

/* ==========================================================================
   The loading screen — video one, on its own layer above the page
   ========================================================================== */

/* The page does not scroll while it runs, so there is always a way out: the
   skip button, Esc, or any scroll intent at all. */
.loading { overflow: hidden; touch-action: none; }
.loading .head,
.loading .hero__scroll,
.loading .hero__nudge { opacity: 0; pointer-events: none; }
.head { transition: opacity 0.9s var(--ease); }

/* `fixed`, above everything including the header, and NOT inside the hero: this
   is a screen over the site, not a layer inside a section. Opaque from the
   instant it's unhidden — the <video>'s own `poster` attribute paints the logo
   frame immediately, natively, before a single byte of the clip has decoded,
   so there is no custom sync code needed to avoid a flash of black. */
.loader {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: var(--pitch);
  pointer-events: none;
}
.loader[hidden] { display: none; }

/* object-fit: contain keeps the whole frame — logo included — inside the box;
   the JS grows it toward cover with a CSS transform as the board forms, which
   is a GPU-composited scale rather than a canvas redraw and costs nothing per
   frame. transform-origin stays centred so the grow reads as the frame filling
   outward, matching how the scroll canvas is cover-fit once it takes over. */
.loader__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  transform-origin: 50% 50%;
  background: var(--pitch);
}

.loader__bar {
  position: absolute;
  inset-block-end: 0;
  inset-inline: 0;
  height: 2px;
  background: var(--hairline-2);
  /* held back: the logo gets the opening beat to itself */
  opacity: 0;
  animation: loader-in 0.7s var(--ease) 0.8s forwards;
}

.loader__fill {
  display: block;
  height: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: 0 50%;
}

.loader__skip {
  position: absolute;
  inset-block-end: calc(2px + var(--space-md));
  inset-inline-end: var(--gutter);
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--space-md);
  pointer-events: auto;
  font-family: var(--body);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
  background: none;
  border: 0;
  cursor: pointer;
  opacity: 0;
  animation: loader-in 0.7s var(--ease) 1.3s forwards;
  transition: color 0.25s var(--ease);
}
.loader__skip:hover { color: var(--ink); }

@keyframes loader-in { to { opacity: 1; } }

/* ==========================================================================
   Buttons
   ========================================================================== */

/* Three levels, and every action on the site is assigned to one of them:
   .btn--solid  primary    — the one action a section is asking for
   .btn         secondary  — a real, tappable control, clearly subordinate
   .btn--quiet  tertiary   — available, but not competing for the eye  */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-xs);
  min-height: 48px;
  padding-inline: var(--space-lg);
  font-family: var(--body);
  font-size: var(--step-0);
  font-weight: 500;
  letter-spacing: 0.01em;
  text-decoration: none;
  /* A 16% hairline is the dark-theme version of a grey-200 button: it reads
     as disabled. The edge has to be visible, and the face has to sit above
     the page, or this stops looking like something you can press. */
  border: 1px solid color-mix(in oklch, var(--ink) 32%, transparent);
  border-radius: 999px;
  background: var(--card);
  color: var(--ink);
  box-shadow: var(--lift-1);
  transition: border-color 0.25s var(--ease), color 0.25s var(--ease),
              background-color 0.25s var(--ease),
              box-shadow 0.25s var(--ease), transform 0.12s var(--ease);
}
.btn:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: color-mix(in oklch, var(--card) 92%, var(--ink));
  box-shadow: var(--lift-2);
}
.btn:active {
  transform: translateY(1px);
  box-shadow: var(--lift-1);
}

.btn--solid {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--pitch);
  font-weight: 600;
  box-shadow: var(--lift-2);
}
.btn--solid:hover {
  background: oklch(75.8% 0.119 71);
  border-color: oklch(75.8% 0.119 71);
  color: var(--pitch);
  box-shadow: var(--lift-2);
}

.btn--quiet {
  background: transparent;
  border-color: transparent;
  color: var(--ink-2);
  font-weight: 500;
  box-shadow: none;
  padding-inline: var(--space-sm);
}
.btn--quiet:hover {
  background: transparent;
  border-color: transparent;
  color: var(--accent);
  box-shadow: none;
}
.btn--quiet:active { transform: translateY(1px); box-shadow: none; }

.btns { display: flex; flex-wrap: wrap; gap: var(--space-sm); align-items: center; }

/* The phone number itself is the largest interactive thing on the page. */
.phone {
  display: inline-block;
  font-family: var(--display);
  font-size: var(--step-4);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.01em;
  color: var(--ink);
  text-decoration: none;
  padding-block-end: var(--space-2xs);
  border-block-end: 2px solid var(--accent);
  transition: color 0.25s var(--ease);
}
.phone:hover { color: var(--accent); }

/* ==========================================================================
   Sections
   ========================================================================== */

.stack > * + * { margin-block-start: var(--space-lg); }
.stack--tight > * + * { margin-block-start: var(--space-sm); }

/* Two rooms / events — text and image, alternating on desktop */
.duo { display: grid; gap: var(--space-xl); }
.duo__media img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }
.duo__media picture.px { aspect-ratio: 3 / 2; }

.figure figcaption {
  margin-block-start: var(--space-sm);
  font-size: var(--step--1);
  color: var(--ink-3);
}

/* --- the board: echoes their real chalked menu ---------------------------- */
.board {
  background: var(--pitch);
  border: 1px solid var(--hairline-2);
  padding: var(--space-lg) var(--space-md) var(--space-xl);
}

.dish {
  display: grid;
  grid-template-columns: 4.5rem 1fr auto;
  align-items: baseline;
  gap: var(--space-sm);
  padding-block: var(--space-md);
  border-block-end: 1px solid var(--hairline-2);
}
.dish:last-child { border-block-end: 0; }

.dish__img {
  grid-row: span 2;
  align-self: center;
  width: 4.5rem;
  height: 4.5rem;
  object-fit: cover;
  border-radius: 50%;
}
/* The circle moves off the photograph and onto its frame once the parallax is
   running: the photograph is taller than the round hole it is seen through and
   drifts behind it. Same box, so nothing in the row shifts. */
.dish picture.px {
  width: 4.5rem;
  height: 4.5rem;
  border-radius: 50%;
}
.dish__name {
  font-family: var(--display);
  font-size: var(--step-1);
  font-weight: 600;
  line-height: 1.15;
}
.dish__note {
  grid-column: 2 / -1;
  font-size: var(--step--1);
  color: var(--ink-3);
  line-height: 1.45;
}
/* the hand-written price tags pinned to the real board */
.dish__price {
  justify-self: end;
  align-self: center;
  grid-row: span 2;
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--ink);
  background: color-mix(in oklch, var(--ink) 10%, transparent);
  padding: var(--space-2xs) var(--space-xs);
  white-space: nowrap;
}

/* --- sticky category pill bar: an infinite rotating marquee ----------------
   The track holds the same set of pills twice back to back; animating it
   exactly -50% loops seamlessly since both halves are identical. Runs
   continuously and is never paused by hover, focus, or touch — owner's call:
   the bar keeps moving no matter what, always. A tap still lands correctly
   mid-slide (the link is wherever it visually is at the moment of the tap;
   CSS transforms don't block pointer events), so nothing is lost by not
   pausing. The duplicate set is aria-hidden with tabindex="-1" links, so
   assistive tech and Tab order only ever see the one real copy of each
   category. */
.menu-pills {
  position: sticky;
  inset-block-start: 0;
  z-index: 15;
  overflow: hidden;
  padding-block: var(--space-sm);
  background: var(--pitch);
  border-block-end: 1px solid var(--hairline-2);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
          mask-image: linear-gradient(to right, transparent, #000 6%, #000 94%, transparent);
}
.menu-pills__track {
  display: flex;
  width: max-content;
  gap: var(--space-xs);
  animation: menu-pills-scroll 38s linear infinite;
}

.menu-pills__set { display: flex; flex: none; gap: var(--space-xs); padding-inline: var(--space-xs); }

@keyframes menu-pills-scroll {
  to { transform: translateX(-50%); }
}

.menu-pills a {
  flex: none;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-inline: var(--space-md);
  font-size: var(--step--1);
  letter-spacing: 0.03em;
  white-space: nowrap;
  text-decoration: none;
  color: var(--ink-2);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  transition: border-color 0.25s var(--ease), color 0.25s var(--ease);
}
.menu-pills a:hover { border-color: var(--accent); color: var(--accent); }

/* ==========================================================================
   The menu page
   Their menu is one chalked board, not ten framed panels, so this is a single
   continuous sheet with the category names ruled across it. Sixty-eight rows
   is a document to be scanned, not a page to be toured: the vertical rhythm
   here is deliberately much tighter than anywhere else on the site.
   ========================================================================== */

/* No hero on this page, so the masthead clears the absolutely-positioned site
   header by hand. Kept short — the food should start on the first screen. */
.menu-intro { padding-block: clamp(6rem, 22vw, 8.5rem) var(--space-xl); }
.menu-intro h1 { font-size: var(--step-4); }

/* --- the sheet ------------------------------------------------------------ */
.sheet {
  /* a menu row is a name and a price; stretched to the full 78rem wrap they
     end up a hand's width apart and stop reading as one line */
  max-width: 54rem;
  margin-inline: auto;
  background: var(--pitch);
  border: 1px solid var(--hairline-2);
  padding: var(--space-lg) var(--space-md) var(--space-2xl);
}

.menu-cat + .menu-cat { margin-block-start: var(--space-3xl); }
/* headings clear the sticky pill bar on an anchor jump */
.menu-cat { scroll-margin-top: calc(var(--bar-h) + var(--space-lg)); }

.menu-cat__title {
  display: flex;
  align-items: baseline;
  gap: var(--space-md);
  font-size: var(--step-2);
  letter-spacing: 0;
  margin-block-end: var(--space-2xs);
}
/* the ruled line a category is written above on the real board */
.menu-cat__title::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--hairline);
  translate: 0 -0.32em;
}

.items { list-style: none; padding: 0; }

.item {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  column-gap: var(--space-lg);
  padding-block: var(--space-md);
  border-block-end: 1px solid var(--hairline-2);
}
.item:last-child { border-block-end: 0; padding-block-end: 0; }

.item__name {
  font-family: var(--display);
  font-size: var(--step-1);
  font-weight: 600;
  line-height: 1.15;
}

/* A handful of items are the ones people actually order most — call them out
   with a small tag, not a photo. */
.item__badge {
  display: inline-block;
  margin-inline-start: var(--space-xs);
  padding: 0.15em 0.6em;
  font-family: var(--body);
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--accent);
  border: 1px solid var(--accent-dim);
  border-radius: 999px;
  vertical-align: middle;
  translate: 0 -0.15em;
}

/* The price is the second thing anyone reads, so it is set at reading size and
   right-aligned into a column of its own. The alignment does the work the
   dotted leaders were faking, and unlike a leader it survives a dish name that
   wraps onto two lines. */
.item__price {
  justify-self: end;
  white-space: nowrap;
  font-size: var(--step-0);
  font-weight: 500;
  color: var(--ink);
}
/* said 68 times, so it is said quietly */
.item__cur { font-size: var(--step--1); font-weight: 400; color: var(--ink-3); }

.item__note {
  grid-column: 1 / -1;
  margin-block-start: var(--space-2xs);
  max-width: 56ch;
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--ink-3);
}

/* A handful of rows carry a photo — same round-thumbnail treatment as the
   home page board, not a tour of all sixty-eight. */
.item--photo {
  grid-template-columns: 3.25rem 1fr auto;
  column-gap: var(--space-sm);
}
.item__img {
  grid-row: span 2;
  align-self: center;
  width: 3.25rem;
  height: 3.25rem;
  object-fit: cover;
  border-radius: 50%;
}
.item--photo picture.px {
  width: 3.25rem;
  height: 3.25rem;
  border-radius: 50%;
}
.item--photo .item__note { grid-column: 2 / -1; }

/* --- reviews -------------------------------------------------------------- */
.quotes { display: grid; gap: var(--space-xl); }

.quote blockquote {
  font-family: var(--display);
  font-style: italic;
  font-size: var(--step-2);
  line-height: 1.34;
  color: var(--ink);
  max-width: 30ch;
}
.quote figcaption {
  margin-block-start: var(--space-sm);
  font-size: var(--step--1);
  color: var(--ink-3);
  letter-spacing: 0.02em;
}

.rating {
  display: inline-flex;
  align-items: baseline;
  min-height: 44px;
  gap: var(--space-xs);
  font-size: var(--step--1);
  letter-spacing: 0.06em;
  color: var(--ink-2);
  text-decoration: none;
  border-block-end: 1px solid var(--hairline);
  padding-block-end: 2px;
  transition: color 0.25s var(--ease), border-color 0.25s var(--ease);
}
.rating:hover { color: var(--accent); border-color: var(--accent); }
.rating b {
  font-family: var(--display);
  font-size: var(--step-1);
  font-weight: 600;
  color: var(--accent);
  letter-spacing: 0;
}

/* --- „Како беше искуството?" — the Google review call ----------------------
   Google has no public API for *writing* a review and forbids collecting one
   through a third-party form, so the honest maximum is a deep link into
   Google's own dialog with this restaurant already selected. The card says so
   in as many words rather than pretending the form lives on this page. */
.grev {
  display: grid;
  justify-items: start;
  gap: var(--space-lg);
  margin-block-start: var(--space-2xl);
}
.grev__title {
  font-size: var(--step-3);
  line-height: 1.04;
  max-width: 15ch;
}

.grev__card {
  display: grid;
  justify-items: start;
  gap: var(--space-md);
  width: min(100%, 30rem);
  padding: clamp(1.25rem, 5vw, 1.75rem);
  border: 1px solid color-mix(in oklch, var(--accent) 38%, transparent);
  border-radius: 1.25rem;
  background:
    radial-gradient(135% 150% at 0% 0%,
      color-mix(in oklch, var(--accent) 10%, transparent), transparent 60%),
    var(--card);
  box-shadow: var(--lift-2);
  color: var(--ink);
  text-decoration: none;
  transition: border-color 0.3s var(--ease), box-shadow 0.3s var(--ease),
              transform 0.3s var(--ease);
}
.grev__card:hover { border-color: var(--accent); transform: translateY(-2px); }
.grev__card:active { transform: translateY(0); box-shadow: var(--lift-1); }

.grev__stars { display: flex; gap: 0.35rem; }
.grev__star {
  width: clamp(2.1rem, 7.5vw, 2.7rem);
  height: auto;
  fill: var(--accent);
  stroke: var(--accent);
  stroke-width: 1.1;
  stroke-linejoin: round;
}
/* The empty→full flick is the whole invitation, so the stars strike one by
   one as the card scrolls into view. No JS: reveal--in is already on the
   wrapper, and without JS the stars are simply drawn full from the start. */
body.js .grev__star {
  fill: transparent;
  transform: scale(0.86);
  transition: fill 0.34s var(--ease), transform 0.34s var(--ease);
}
/* the stagger lives here, not in a style attribute — CSP is style-src 'self' */
body.js .grev__star:nth-child(2) { transition-delay: 90ms; }
body.js .grev__star:nth-child(3) { transition-delay: 180ms; }
body.js .grev__star:nth-child(4) { transition-delay: 270ms; }
body.js .grev__star:nth-child(5) { transition-delay: 360ms; }
body.js .grev.reveal--in .grev__star { fill: var(--accent); transform: none; }
body.js .grev.reveal--in .grev__card:hover .grev__star { transform: scale(1.07); }

.grev__cta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: 48px;
  padding-inline: var(--space-lg);
  border-radius: 999px;
  background: var(--accent);
  color: var(--pitch);
  font-size: var(--step-0);
  font-weight: 600;
  letter-spacing: 0.01em;
  box-shadow: var(--lift-1);
  transition: background-color 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
.grev__card:hover .grev__cta {
  background: oklch(75.8% 0.119 71);
  box-shadow: var(--lift-2);
}
/* Google's mark is specified for light backgrounds — give it its own disc
   rather than dropping it straight onto the gold. */
.grev__g {
  flex: none;
  width: 1.05em;
  height: 1.05em;
  padding: 0.28em;
  border-radius: 50%;
  background: #fff;
}
.grev__hint {
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--ink-3);
  letter-spacing: 0.02em;
}

/* --- hours + map ---------------------------------------------------------- */
.hours { border-block-start: 1px solid var(--hairline-2); }
.hours__row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-md);
  padding-block: var(--space-sm);
  border-block-end: 1px solid var(--hairline-2);
  font-size: var(--step-0);
}
.hours__row dt { color: var(--ink-2); }
.hours__row dd { color: var(--ink); font-variant-numeric: tabular-nums; }
.hours__row--now dt, .hours__row--now dd { color: var(--accent); }

.map {
  width: 100%;
  aspect-ratio: 4 / 3;
  border: 1px solid var(--hairline-2);
  filter: grayscale(0.45) contrast(1.05) brightness(0.86);
}

.addr { font-style: normal; }

/* --- footer --------------------------------------------------------------- */
.foot {
  background: var(--pitch);
  padding-block: var(--space-3xl) var(--space-2xl);
  font-size: var(--step--1);
  color: var(--ink-3);
}
/* Links inside the address block and the legal line stay inline text — WCAG
   2.5.8 exempts targets inline in a sentence, and padding them out would break
   the address into a ragged column. Every standalone control is >= 44px. */
.foot a { color: var(--ink-2); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
.foot a:hover { color: var(--accent); }
.foot__links a { text-decoration: none; }
.foot__grid { display: grid; gap: var(--space-xl); }
.foot__title {
  font-family: var(--body);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-block-end: var(--space-sm);
}
.foot__links { list-style: none; padding: 0; display: grid; gap: var(--space-2xs); }
.foot__links a { display: inline-block; min-height: 44px; line-height: 44px; }
.foot__legal {
  margin-block-start: var(--space-2xl);
  padding-block-start: var(--space-md);
  border-block-start: 1px solid var(--hairline-2);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm) var(--space-lg);
  justify-content: space-between;
}

/* ==========================================================================
   Parallax — every photograph drifts against its own frame

   Both classes are added by parallax.js and appear nowhere in the markup, so
   this whole section is inert until that script has decided the visitor wants
   motion. Without it the photographs stay exactly as they are: in flow, at
   their natural crop, clipped by nothing.

   The frame is the <picture> that was already wrapping the image. It clips;
   the image inside it is taller than it is and is pushed up by half the
   difference, so it sits centred with the same slack above and below. That
   slack is the whole effect — parallax.js reads it back off the layout and
   never travels further than it, so no edge of the frame is ever exposed.
   Make an image taller here and it moves more. That is the only dial.
   ========================================================================== */

.px { position: relative; overflow: clip; }

.px__img {
  position: absolute;
  inset-block-start: -11%;
  inset-inline: 0;
  width: 100%;
  height: 122%;
  max-width: none;
  object-fit: cover;
}

/* The round thumbnails are small enough that 11% is a couple of pixels and
   reads as nothing. They get a deeper crop and travel further for it. */
.dish .px__img,
.item--photo .px__img {
  inset-block-start: -15%;
  height: 130%;
}

/* ==========================================================================
   Motion — scoped to body.js so a script failure can never hide content
   ========================================================================== */

body.js .reveal { opacity: 0; transform: translateY(18px); }
body.js .reveal--in {
  opacity: 1;
  transform: none;
  transition: opacity 0.8s var(--ease), transform 0.8s var(--ease);
}

/* meni.js: category headings and item rows, tweened directly rather than
   through a CSS transition — see body.js .reveal above for the plain-CSS
   version this page used before. */
body.gsap.menu-page .menu-cat__title,
body.gsap.menu-page .item { opacity: 0; transform: translateY(14px); }

/* ==========================================================================
   Desktop — added, never subtracted
   ========================================================================== */

@media (min-width: 48rem) {
  .duo {
    grid-template-columns: 1fr 1fr;
    align-items: center;
    gap: var(--space-3xl);
  }
  .duo--flip .duo__media { order: -1; }
  .duo__media img { aspect-ratio: 4 / 5; }
  .duo__media picture.px { aspect-ratio: 4 / 5; }
  .quotes { grid-template-columns: 1fr 1fr; gap: var(--space-3xl); }
  .dish { grid-template-columns: 6rem 1fr auto; }
  .dish__img { width: 6rem; height: 6rem; }
  .dish picture.px { width: 6rem; height: 6rem; }
  .board { padding: var(--space-2xl) var(--space-xl); }
  .foot__grid { grid-template-columns: 1.4fr 1fr 1fr; }
}

@media (min-width: 56rem) {
  /* the sticky bar is a phone affordance; on desktop the number lives up top */
  .callbar { display: none; }
  body { padding-block-end: 0; }

  /* …except on the menu page, where "up top" is 10,000px behind you and the
     header has long since scrolled away. Here it stays as a floating control
     rather than a full-width band — it is one action, not a toolbar. */
  body.menu-page .callbar {
    display: inline-flex;
    inset-inline: auto var(--gutter);
    inset-block-end: var(--space-lg);
    min-height: 52px;
    padding-inline: var(--space-lg);
    border-radius: 999px;
    box-shadow: var(--lift-2);
  }
  .hero__say { inset-block-end: var(--space-3xl); }
  /* moved out of the headline's way once there is horizontal room for it */
  .hero__scroll {
    inset-block-end: var(--space-lg);
    inset-inline-start: auto;
    inset-inline-end: var(--gutter);
  }
  /* the call bar is gone at this width, so the nudge drops with it */
  .hero__nudge { inset-block-end: var(--space-lg); }
  .head { padding-block: var(--space-lg); }
  .burger { inset-block-start: var(--space-lg); }
  .head__call {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding-inline: var(--space-md);
    font-weight: 500;
    letter-spacing: 0.02em;
    text-decoration: none;
    border: 1px solid var(--hairline);
    transition: border-color 0.25s var(--ease), color 0.25s var(--ease);
  }
  .head__call:hover { border-color: var(--accent); color: var(--accent); }
  /* the burger is fixed at the same edge — the number stops short of it */
  body.js .head__call { margin-inline-end: 3.75rem; }
  .map { aspect-ratio: 16 / 9; }
  .split {
    display: grid;
    grid-template-columns: 1fr 1.15fr;
    gap: var(--space-3xl);
    align-items: start;
  }
}

@media (min-width: 72rem) {
  /* deliberate asymmetry: the statement sits off the left edge, not centred */
  .offset { padding-inline-start: 8vw; }
}

/* ==========================================================================
   Reduced motion — everything animated stops, including the hero scrub
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .hero { height: 100svh; }
  .hero__say { opacity: 1; transform: none; }
  .hero__scroll,
  .hero__nudge { display: none; }
  .callbar { transform: none; }
  body.js .reveal { opacity: 1; transform: none; }
  /* the stars are the review card's only animation — hand them over full */
  body.js .grev__star {
    fill: var(--accent);
    transform: none;
    transition-delay: 0s !important;
  }
  /* meni.js never adds body.gsap when this preference is on, so these never
     apply — kept as a second belt in case the preference changes mid-session. */
  body.gsap.menu-page .menu-cat__title,
  body.gsap.menu-page .item { opacity: 1; transform: none; }

  /* The rule above fast-forwards every animation to its end state, which for
     the pill marquee is translateX(-50%) — parking the duplicate set on screen.
     That copy is aria-hidden with tabindex="-1", so a reduced-motion visitor
     would be left with a bar no screen reader announces and no Tab reaches.
     Stop the marquee outright instead, drop the duplicate, and hand back an
     ordinary scrollable bar of the real links. */
  .menu-pills__track { animation: none !important; transform: none; }
  .menu-pills__set[aria-hidden] { display: none; }
  .menu-pills { overflow-x: auto; scrollbar-width: none; }

  /* the sheet appears and disappears outright: the delays that sequence the
     slide would otherwise hold it visible for 800ms after it is gone, and the
     letters would still ripple one by one. nav.js skips the curve and the
     letter split on its own. */
  body.js .nav__sheet,
  body.js .nav__word i { transition-delay: 0s !important; }
}

/* ==========================================================================
   Print — someone will print the hours and the address
   ========================================================================== */

@media print {
  .hero, .callbar, .map, .nav, .burger { display: none; }
  body { background: #fff; color: #000; padding: 0; }
  .section { padding-block: 1rem; }

  /* the menu page prints as a clean text-only sheet: no sticky bar, no
     photos, and every colour that isn't already inherited black gets
     forced there too — the dark-page tokens read wrong on white paper */
  body.menu-page .menu-pills { display: none; }
  body.menu-page .menu-intro { padding-block: 1rem; }
  body.menu-page .kicker,
  body.menu-page h1, body.menu-page h2, body.menu-page h3,
  body.menu-page .item__name, body.menu-page .item__price,
  body.menu-page .item__cur, body.menu-page .item__note,
  body.menu-page .prose { color: #000; }
  body.menu-page .sheet { background: #fff; border-color: #000; padding: 0; max-width: none; }
  body.menu-page .item { border-block-end-color: #ccc; }
  body.menu-page .menu-cat__title::after { background: #000; }
  /* a category should not be orphaned from its first few rows */
  body.menu-page .menu-cat { break-inside: auto; }
  body.menu-page .menu-cat__title { break-after: avoid; }
  body.menu-page .item { break-inside: avoid; }
}
