/* Her Table — "Linnen".
   A linen tablecloth, an olive branch and a lemon: the palette is taken from the
   cloth the dishes are photographed on. Linen cream is the ground, olive-leaf
   green does the work (buttons, links, figures, the CTA band), and lemon gold is
   reserved for the handwritten voice — eyebrows, category tags, the signature on
   the story photo. Nothing else gets a colour.

   Three typefaces, each with one job: Cormorant Garamond sets every heading and
   price figure, Karla carries body and UI, Caveat writes by hand. Photos are
   always a 24px-radius card or a full circle. Bands alternate linen and the
   slightly deeper #F3EBDD, never more than two before the olive CTA.

   Motion is transform/opacity only, under 300ms for UI, strong ease-out. */

:root {
  /* ---- ground ---- */
  --paper: #FAF5EC;          /* linen */
  --paper-2: #F3EBDD;        /* alternating band */
  --paper-raised: #FFFFFF;   /* cards */

  /* ---- ink ---- */
  --ink: #2E2822;            /* headings */
  --ink-soft: #5A5245;       /* body */
  /* The design's muted tone is #8C8B6C. That lands at 3.1:1 on linen, so it is
     kept for large or decorative text (--muted) while small print uses a
     darkened version that clears 4.5:1. */
  --ink-faint: #6E6C50;
  /* Decorative only: 3.2:1 on linen, 3.5:1 on white. It reads as a soft olive
     grey and it is tempting for small print, which is exactly how it ended up
     on seven bits of 12-13px text that all failed AA. Text uses --ink-faint;
     this one is for shapes (the guidance bullet) that carry no words. */
  --muted: #8C8B6C;

  /* ---- rules ---- */
  --line: #E7DCC8;           /* solid borders */
  --line-strong: #D8CBB2;    /* dashed dividers, inputs */
  --leader: #C9BBA0;         /* dotted price leaders */
  --rule-soft: #F0E7D6;      /* cart line rules */

  /* ---- olive (primary) ---- */
  --olive: #6F7442;
  --olive-deep: #4F5430;
  --olive-tint: #ECE8D2;     /* hover wash on light */
  --olive-mid: #5D6338;      /* hover on the olive band */

  /* ---- lemon gold (secondary, handwriting) ---- */
  --gold: #A98A2F;
  /* Gold for SMALL text (eyebrows): --gold sits at 2.8-3.0:1 on the linen and
     band grounds, fine for the large Caveat voice but under AA's 4.5:1 for
     12px labels. This darker cut clears 4.5:1 on both grounds. */
  --gold-ink: #80661D;
  --gold-hero: #E5C560;      /* gold on dark */
  --hero-text: #EDE5D2;
  /* Body copy on the olive bands. The old #E9E6C9 measured 3.9:1 on --olive,
     under AA for 17px text. This is the warmest cream that clears it (4.6:1);
     going further toward white buys contrast by throwing away the warmth. */
  --cta-text: #FDF8E6;

  /* ---- footer ---- */
  --foot-bg: #31341D;
  --foot-text: #D6D6B8;
  /* Secondary footer type (column headings, the sign-off, the copyright). A
     lighter cut of the footer olive rather than a grey, so it stays part of the
     palette: 4.9:1 on --foot-bg where --muted managed 3.7:1. */
  --foot-muted: #A2A286;
  --foot-line: #4A4E2E;

  /* ---- state ---- */
  --terracotta: #9C4B2A;
  --terracotta-tint: #F6E7DF;
  --gold-tint: #F5EDD8;      /* halal badge ground; gold-ink on it is 4.7:1 */
  /* Deep olive scrim for cream text sitting over the hero video. The footer
     olive, not black: at 78% it holds 6.2:1 against the brightest frame while
     still reading as part of the palette. */
  --scrim: rgba(49, 52, 29, 0.78);
  --scrim-strong: rgba(49, 52, 29, 0.88);
  --green: #4F7A46;

  /* one radius scale */
  --r-field: 10px;
  --r-sum: 14px;
  --r: 20px;
  --r-lg: 24px;
  --r-pill: 999px;

  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-reveal: cubic-bezier(0.16, 1, 0.3, 1);

  --shadow-card: 0 16px 40px rgba(80, 50, 20, 0.08);
  --shadow-photo: 0 22px 55px rgba(80, 50, 20, 0.18);
  --shadow-thumb: 0 4px 12px rgba(80, 50, 20, 0.16);
  --shadow-soft: 0 8px 20px rgba(80, 50, 20, 0.12);
  --shadow-lift: 0 20px 50px rgba(80, 50, 20, 0.18);

  --serif: "Cormorant Garamond", "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, ui-serif, serif;
  --sans: "Karla", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --script: "Caveat", "Segoe Script", "Bradley Hand", cursive;

  --maxw: 1180px;
  --maxw-kaart: 960px;
  --maxw-narrow: 760px;
  --gutter: clamp(1.25rem, 4vw, 2rem);
  --nav-h: 68px;
}

* { box-sizing: border-box; }
::selection { background: var(--olive-tint); color: var(--olive-deep); }
::-moz-selection { background: var(--olive-tint); color: var(--olive-deep); }
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + 1.5rem);
  scrollbar-gutter: stable;
}
body {
  margin: 0;
  font-family: var(--sans);
  font-size: clamp(1rem, 0.97rem + 0.15vw, 1.06rem);
  line-height: 1.7;
  color: var(--ink);
  background: var(--paper);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  -webkit-tap-highlight-color: transparent;
}
img { max-width: 100%; display: block; }
button { font-family: inherit; cursor: pointer; -webkit-tap-highlight-color: transparent; }
a { color: var(--olive); -webkit-tap-highlight-color: transparent; }

/* Skip link. The landmarks already satisfy 2.4.1 for screen readers, but a
   sighted keyboard user still had to tab the whole nav on every page. Off-canvas
   until focused rather than display:none, which would take it out of the tab
   order entirely and defeat the point. */
.skip-link {
  position: absolute; left: 0.75rem; top: -4rem; z-index: 1200;
  background: var(--olive-deep); color: var(--paper);   /* 7.3:1 */
  padding: 0.7rem 1.15rem; border-radius: 0 0 var(--r-field) var(--r-field);
  font-family: var(--sans); font-size: 0.94rem; font-weight: 700; text-decoration: none;
  box-shadow: var(--shadow-soft);
  transition: top 180ms var(--ease-out);
}
.skip-link:focus-visible { top: 0; }
@media (prefers-reduced-motion: reduce) { .skip-link { transition: none; } }
/* The skip target is a landmark, not a control: focus it without parking a tab
   stop on it or drawing a ring around the whole page. */
main[tabindex="-1"] { outline: none; }
a:hover { color: var(--olive-deep); }

:focus { outline: none; }
:focus-visible { outline: 2px solid var(--olive); outline-offset: 2px; border-radius: 4px; }
h1, h2, h3, h4 { margin: 0; font-weight: 600; line-height: 1.15; }
/* A heading that is one long word ("Datenschutzerklärung", "Keukenwoordenboek")
   hyphenates in its own language instead of running out of a narrow phone. */
h1, h2, h3, h4 { overflow-wrap: break-word; hyphens: auto; }

/* ---------- shared layout ---------- */
.wrap { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
.section { padding-block: clamp(3.5rem, 7vw, 6rem); }
/* The alternating deeper band, hairlined top and bottom so two bands never merge. */
.band { background: var(--paper-2); border-block: 1px solid var(--line); }

.eyebrow {
  font-family: var(--sans);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-ink);
  margin: 0 0 0.9rem;
}
.display {
  font-family: var(--serif);
  font-weight: 600;
  font-size: clamp(2rem, 1.35rem + 2.6vw, 2.75rem);
  line-height: 1.15;
  color: var(--ink);
  text-wrap: pretty;
}
.display em { font-style: normal; color: var(--olive); }
.lede { font-size: clamp(1rem, 0.97rem + 0.3vw, 1.06rem); line-height: 1.7; color: var(--ink-soft); max-width: 54ch; }
/* The handwritten voice. One per section, never two. */
.script { font-family: var(--script); font-weight: 600; color: var(--gold); line-height: 1.1; }

/* ---------- buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem;
  border: 1.5px solid transparent; border-radius: var(--r-pill);
  padding: 0.82rem 1.85rem;
  font-size: 0.94rem; font-weight: 700; letter-spacing: 0.01em;
  text-decoration: none; white-space: nowrap;
  transition: transform 160ms var(--ease-out), background-color 200ms var(--ease-out),
              color 200ms var(--ease-out), border-color 200ms var(--ease-out);
}
.btn-primary { background: var(--olive); color: var(--paper); }
.btn-primary:hover { background: var(--olive-deep); color: var(--paper); }
.btn-ghost { background: transparent; color: var(--olive); border-color: var(--olive); }
.btn-ghost:hover { background: var(--olive-tint); color: var(--olive-deep); border-color: var(--olive); }
.btn:active { transform: scale(0.97); }
.btn-lg { padding: 0.92rem 2rem; font-size: 0.98rem; }
/* A button never wider than its column. On a phone a long label ("Über die
   Speisekarte bestellen") wraps inside the pill instead of pushing the page 3px
   sideways; on wider screens it still fits on one line. */
.btn { max-width: 100%; text-align: center; }
@media (max-width: 480px) { .btn { white-space: normal; } }

/* ---------- nav ---------- */
/* Sticky linen bar, the same on every page and over the hero video: the design
   drops the old transparent-over-video treatment, so the bar never changes
   colour and the links are always ink on cream. */
/* Fixed, not sticky: the home hero runs full-bleed under the bar, so the bar
   must not occupy flow at the top of the page. Pages that carry .nav--solid get
   that height back as padding (see "main" below). */
/* No backdrop-filter here, and do not put one back. The bar has exactly two
   reachable states and neither can show a blur: transparent at the top of the
   home page, and opaque --paper the moment it scrolls, turns solid, or opens.
   (The rgba(...0.93) below never paints: the two rules further down are
   mutually exhaustive and both outrank it.) So the blur was invisible in every
   state, while still forcing the compositor to read back the strip of page
   under a fixed element on every frame. Scrolling the home page, what is under
   that strip is the hero video's own layer, and sampling a live video layer
   through a fixed blur is what smeared the footage into the bar and shifted its
   colour as the frames changed. */
.nav {
  position: fixed; inset: 0 0 auto 0; z-index: 50;
  height: var(--nav-h); display: flex; align-items: center;
  background: rgba(250, 245, 236, 0.93);
  border-bottom: 1px solid var(--line);
  transition: background-color 260ms var(--ease-out), border-color 260ms var(--ease-out);
}
/* Everything except the home page shows the bar solid from the first pixel, so
   it needs the height the fixed bar no longer takes up in flow. */
body:has(.nav--solid) main { padding-top: var(--nav-h); }
.nav .wrap { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.nav-brand {
  font-family: var(--serif); font-weight: 600; font-size: 1.5rem;
  color: var(--ink); text-decoration: none; white-space: nowrap; line-height: 1;
  transition: color 200ms var(--ease-out);
}
.nav-brand:hover { color: var(--olive); }
.nav-links { display: flex; align-items: center; gap: clamp(0.9rem, 2vw, 1.75rem); }
/* Direct children only, here as well as in the mobile block: the language
   dropdown lives inside .nav-links, so a descendant selector outranks every
   .lang-opt rule and dresses the options as bar links (0.875rem/500, 0.04em
   tracking) instead of menu options. */
.nav-links > a {
  font-size: 0.875rem; font-weight: 500; letter-spacing: 0.04em;
  text-decoration: none; color: var(--ink);
  transition: color 200ms var(--ease-out);
}
.nav-links > a:hover { color: var(--olive); }
.nav-links .nav-cta { display: none; }
@media (min-width: 720px) {
  .nav-links .nav-cta {
    display: inline-flex; align-items: center; gap: 0.5rem;
    padding: 0.62rem 1.5rem; border-radius: var(--r-pill);
    background: var(--olive); color: var(--paper); font-weight: 700;
    transition: background-color 200ms var(--ease-out);
  }
  .nav-links .nav-cta:hover { background: var(--olive-deep); color: var(--paper); }
}
/* Item count on the Bestellen pill: cream on olive, hidden while the cart is empty. */
.nav-cart-count {
  background: var(--paper); color: var(--olive);
  border-radius: var(--r-pill); padding: 1px 8px;
  font-size: 0.75rem; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.5;
}
.nav-cart-count[hidden] { display: none; }

.lang-switch { position: relative; display: flex; align-items: center; }
.lang-btn {
  display: inline-flex; align-items: center; gap: 0.35rem;
  background: none; border: none; cursor: pointer; padding: 0.4rem 0.4rem;
  color: var(--ink); font: inherit; font-size: 0.875rem;
  transition: color 200ms var(--ease-out);
}
.lang-btn:hover { color: var(--olive); }
.lang-menu {
  position: absolute; top: calc(100% + 0.5rem); inset-inline-end: 0; z-index: 60;
  margin: 0; padding: 0.3rem; list-style: none; min-width: 9rem;
  background: var(--paper-raised); border: 1px solid var(--line); border-radius: 0.85rem;
  box-shadow: var(--shadow-card);
}
.lang-menu[hidden] { display: none; }
.lang-opt {
  display: block; width: 100%; text-align: start; background: none; border: none;
  cursor: pointer; padding: 0.5rem 0.7rem; border-radius: 0.55rem;
  font: inherit; font-size: 0.92rem; color: var(--ink);
  /* the options are <a> now, not <button>: kill the link underline */
  text-decoration: none;
}
.lang-opt:hover { background: var(--paper-2); }
.lang-opt.is-active { color: var(--olive); font-weight: 700; }

/* ---------- mobile nav ---------- */
.nav-mobile { display: none; align-items: center; gap: 0.55rem; }
.nav-cta-m {
  display: inline-flex; align-items: center; gap: 0.45rem;
  padding: 0.5rem 1.05rem; border-radius: var(--r-pill);
  font-size: 0.82rem; font-weight: 700; text-decoration: none; white-space: nowrap;
  background: var(--olive); color: var(--paper); border: 1.5px solid var(--olive);
  transition: background-color 200ms var(--ease-out);
}
.nav-cta-m:hover { background: var(--olive-deep); color: var(--paper); }
.nav-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; padding: 0; border: none; background: none; border-radius: 10px;
}
.nav-toggle-bars { position: relative; width: 22px; height: 16px; }
.nav-toggle-bars span {
  position: absolute; left: 0; width: 100%; height: 2px; border-radius: 2px; background: var(--ink);
  transition: transform 220ms var(--ease-out), opacity 150ms var(--ease-out);
}
.nav-toggle-bars span:nth-child(1) { top: 0; }
.nav-toggle-bars span:nth-child(2) { top: 7px; }
.nav-toggle-bars span:nth-child(3) { top: 14px; }
.nav-toggle.is-open .nav-toggle-bars span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle.is-open .nav-toggle-bars span:nth-child(2) { opacity: 0; }
.nav-toggle.is-open .nav-toggle-bars span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
/* Solid linen once scrolled, on a content page, or while the drop panel is open
   (so panel and bar read as one surface). */
.nav.nav-open, .nav.scrolled, .nav.nav--solid { background: var(--paper); }

/* Untouched at the top of the home page: no bar at all, just the wordmark and
   links floating over the video. Ink on linen would vanish against the footage,
   so brand, links and burger go cream and carry a soft shadow for legibility;
   the moment the bar turns solid they inherit their ink colours again. */
.nav:not(.scrolled):not(.nav--solid):not(.nav-open) {
  background: transparent; border-bottom-color: transparent;
}
/* Direct children only. The language dropdown lives inside .nav-links, so a
   descendant selector here reached into the open panel and painted its options
   cream, with a shadow, on a white surface: the menu opened full of invisible
   text. The bar's own links are all direct children; nothing in a panel is. */
.nav:not(.scrolled):not(.nav--solid):not(.nav-open) .nav-brand,
.nav:not(.scrolled):not(.nav--solid):not(.nav-open) .nav-links > a,
.nav:not(.scrolled):not(.nav--solid):not(.nav-open) .lang-btn {
  color: var(--paper); text-shadow: 0 1px 12px rgba(30, 26, 16, 0.55);
}
.nav:not(.scrolled):not(.nav--solid):not(.nav-open) .nav-toggle-bars span { background: var(--paper); }

/* Hover and keyboard focus over the video. The colour rules above are five
   classes deep, so the ordinary .nav-links > a:hover (two) never got a look in
   and hovering the bar over the hero did nothing at all.

   Gold, not the usual olive: olive is tuned for linen and sinks into the
   footage. Gold on dark is what the hero headline beside it already uses.
   The Bestellen pill is excluded, it answers hover with its own background. */
.nav:not(.scrolled):not(.nav--solid):not(.nav-open) .nav-brand:hover,
.nav:not(.scrolled):not(.nav--solid):not(.nav-open) .nav-links > a:not(.nav-cta):hover,
.nav:not(.scrolled):not(.nav--solid):not(.nav-open) .nav-links > a:not(.nav-cta):focus-visible,
.nav:not(.scrolled):not(.nav--solid):not(.nav-open) .lang-btn:hover,
.nav:not(.scrolled):not(.nav--solid):not(.nav-open) .lang-btn:focus-visible {
  color: var(--gold-hero);
}

@media (max-width: 719px) {
  .nav-mobile { display: flex; }
  .nav-links {
    display: flex;
    position: absolute; top: 100%; inset-inline: 0;
    flex-direction: column; align-items: stretch; gap: 0;
    background: var(--paper); border-top: 1px solid var(--line);
    box-shadow: var(--shadow-card);
    padding: 0.2rem var(--gutter) 1rem;
    opacity: 0; visibility: hidden; pointer-events: none;
    transition: opacity 260ms var(--ease-out), visibility 0s linear 260ms;
  }
  .nav-links.open {
    opacity: 1; visibility: visible; pointer-events: auto;
    transition: opacity 260ms var(--ease-out), visibility 0s;
  }
  /* Direct children only. The language options are <a> too, and a descendant
     selector here outranked every .lang-opt rule below it: the options came out
     at 1rem/700 with 0.9rem 0 padding, i.e. three 58px capsules with no
     horizontal padding and their labels jammed against the rounded border. */
  .nav-links > a { font-size: 1rem; padding: 0.9rem 0; border-bottom: 1px solid var(--line); }
  .nav-links .nav-cta { display: none; }
  #lang-btn { display: none; }

  /* The globe trigger is gone at this width, so the languages are always open.
     Three loose outlined pills were a second vocabulary in a panel whose every
     other row is a plain word on a hairline: they read as bolted on. This is
     the segmented control the order form already uses for pickup/deliver, so
     the panel gains no new pattern, and one object replaces three. */
  .lang-switch { display: block; padding-top: 1rem; }
  /* Five languages do not fit one row on a phone ("Nederlands" alone needs
     70px), so they wrap: three and two at 360px, one row where there is room. */
  #lang-menu {
    position: static; min-width: 0; margin: 0;
    display: grid; grid-template-columns: repeat(auto-fit, minmax(5.75rem, 1fr)); gap: 0.3rem;
    padding: 0.28rem; background: var(--paper-2);
    border: none; border-radius: var(--r-field); box-shadow: none;
  }
  .lang-opt {
    display: flex; align-items: center; justify-content: center; min-height: 42px;
    width: auto; padding: 0.5rem 0.35rem;
    border: none; border-radius: calc(var(--r-field) - 3px);
    font-size: 0.8125rem; font-weight: 700; line-height: 1.2; text-align: center;
    color: var(--ink-soft);
    transition: background-color 200ms var(--ease-out), color 200ms var(--ease-out);
  }
  .lang-opt:hover { background: none; color: var(--olive-deep); }
  .lang-opt.is-active {
    background: var(--paper-raised); color: var(--olive-deep);
    box-shadow: 0 1px 3px rgba(80, 50, 20, 0.14);
  }
}

/* ---------- hero ---------- */
.hero {
  position: relative; isolation: isolate; overflow: hidden;
  min-height: 100svh; display: flex; align-items: flex-end;
  background: var(--olive-deep); color: var(--paper);
}
.hero-media { position: absolute; inset: 0; z-index: 0; }
.hero-media > picture, .hero-media > video { position: absolute; inset: 0; }
.hero-media video:not([src]) { display: none; }
.hero-media img, .hero-media video {
  width: 100%; height: 100%; object-fit: cover; object-position: center 50%;
  transform: translateZ(0); backface-visibility: hidden;
}
/* No wash over the footage: the olive tint is already baked into the video and
   poster pixels, and a full-cover gradient greyed the picture out (worst at the
   bottom, where it read as a dark band creeping up as you scrolled). The copy
   earns its contrast from its own shadow instead, so the video stays the video. */
.hero > .wrap { position: relative; z-index: 2; max-width: calc(920px + 2 * var(--gutter)); }
.hero-sub, .hero-mark, .hero-tagline { text-shadow: 0 2px 22px rgba(30, 26, 16, 0.62); }
/* Sits low and to the left, which is where the footage is shaded. Centred, the
   copy landed on the sunlit tablecloth and the tagline washed out against it;
   the fix is to move the words, not to dim the video behind them. */
.hero-inner { padding-top: calc(var(--nav-h) + 4rem); padding-bottom: clamp(3rem, 7vw, 5.5rem); max-width: 46rem; }
.hero-sub {
  font-family: var(--script); font-weight: 600;
  font-size: clamp(1.8rem, 1.3rem + 1.8vw, 2.125rem);
  color: var(--gold-hero); margin: 0; line-height: 1;
}
.hero-mark {
  font-family: var(--serif); font-weight: 600;
  font-size: clamp(2.1rem, 1.2rem + 3.6vw, 3.25rem);
  line-height: 1.14; color: var(--paper); margin: 0.85rem 0 1.35rem;
  text-wrap: pretty;
}
.hero-mark em { font-style: normal; color: var(--gold-hero); }
.hero-tagline {
  font-size: clamp(1rem, 0.95rem + 0.4vw, 1.125rem); line-height: 1.65;
  color: var(--hero-text); margin: 0 0 2.25rem; max-width: 46ch;
}
.hero-cta { display: flex; flex-wrap: wrap; gap: 0.875rem; }
/* Over the video both buttons are cream: one solid, one outline. */
.hero .btn-primary { background: var(--paper); color: var(--olive-deep); }
.hero .btn-primary:hover { background: var(--olive-tint); color: var(--olive-deep); }
/* The secondary button sat fully transparent, so cream text and a cream border
   landed on the sunlit tablecloth and all but vanished. It carries the deep
   olive scrim now, which holds 6.2:1 against the brightest frame of the footage
   while staying visibly secondary to the solid primary. */
.hero .btn-ghost {
  background: var(--scrim); color: var(--paper); border-color: rgba(250, 245, 236, 0.92);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
}
.hero .btn-ghost:hover { background: var(--scrim-strong); color: var(--paper); border-color: var(--paper); }
@media (max-width: 460px) {
  .hero-cta .btn { padding: 0.75rem 1.2rem; font-size: 0.87rem; }
}
.hero-scrollhint { display: none; }

/* ---------- story ("Onze sofra") ---------- */
.story .wrap {
  display: grid; gap: clamp(2rem, 5vw, 5rem); align-items: center;
  grid-template-columns: 1fr;
}
@media (min-width: 860px) {
  .story .wrap { grid-template-columns: 0.9fr 1.1fr; }
  .story-media { order: 1; }
  .story-copy { order: 2; }
}
/* The photo is tipped off-square and signed, like a print left on the cloth. */
.story-media { position: relative; }
.story-media picture { display: block; }
.story-media img {
  width: 100%; aspect-ratio: 1 / 1.05; object-fit: cover;
  border-radius: var(--r-lg); transform: rotate(-2deg); box-shadow: var(--shadow-photo);
}
.story-badge {
  position: absolute; bottom: -14px; right: -6px;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-pill);
  padding: 0.4rem 1.35rem;
  font-family: var(--script); font-weight: 600; font-size: 1.4rem; line-height: 1.2; color: var(--gold-ink);
  transform: rotate(3deg); box-shadow: var(--shadow-soft);
}
.story-copy p { color: var(--ink-soft); margin: 0 0 1.1rem; line-height: 1.75; }
.story-copy p:last-child { margin-bottom: 0; }
.story-copy p a { color: var(--olive); text-decoration: underline; text-underline-offset: 2px; }
.story-copy p a:hover { color: var(--olive-deep); }
.story-copy .display { margin: 0.85rem 0 1.35rem; }

/* ---------- catering ---------- */
/* Its own band. Without one it sat on the same linen as the kaart below and the
   two ran together as a single empty stretch. Deep olive rather than the contact
   band's mid olive, so the page's two dark CTA bands stay distinguishable and
   the footer still reads as the darkest note. */
/* Copy on the left, one looping clip of catering-size bowls on the right. It
   used to be a centred column at the narrow width; the clip is what turns it
   into a split, and left-aligned copy is what makes the split read as a
   composition rather than as centred text with a picture stuck beside it. */
.catering {
  background: var(--olive-deep);
  color: var(--paper);
  /* The mount behind the photo is inset negatively AND rotated, so its corners
     swing wider than the photo does. On a phone that was enough to push the
     document 4px wider than the viewport and give the whole page a horizontal
     scrollbar. clip rather than hidden: hidden on one axis forces the other to
     auto and would trap the sticky nav's scrolling. */
  overflow-x: clip;
}
.catering .wrap { max-width: calc(var(--maxw) + 2 * var(--gutter)); }
.catering-wrap {
  display: grid;
  /* Both tracks are capped rather than elastic, and the pair is centred. The
     copy here is one short paragraph and two buttons, so a 1fr track left a
     wide strip of bare olive down the right-hand side: the column filled, the
     words did not. Sizing to the content and centring what is left balances
     the band without stretching the measure. */
  grid-template-columns: minmax(0, 24rem) minmax(0, 40rem);
  justify-content: center;
  /* The same gutter the story band uses. This section is the same shape as
     that one, a photo beside a column of copy, so it should breathe the same
     way; a tighter gutter made the two read as different kinds of thing. */
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
}
/* Photo first on the row, copy second. The DOM order is the other way round so
   that a phone, which stacks this to one column, still leads with the heading
   people scan for rather than with a picture. */
.catering-media { grid-column: 1; grid-row: 1; }
.catering-copy { grid-column: 2; grid-row: 1; }
.catering .script { font-size: clamp(1.6rem, 1.2rem + 1.4vw, 1.875rem); display: block; color: var(--gold-hero); }
.catering .display { margin: 0.6rem 0 1.1rem; color: var(--paper); }
/* The default em olive is tuned for linen and goes muddy here; gold on dark,
   the same pairing the hero headline uses. */
.catering .display em { color: var(--gold-hero); }
.catering p { color: var(--cta-text); margin: 0 0 1.9rem; max-width: 46ch; line-height: 1.75; }
.catering-links { display: flex; gap: 0.875rem; flex-wrap: wrap; }

/* The same treatment the story photo gets, and nothing more: rounded, tipped a
   couple of degrees, a handwritten note pinned to the corner. It carried a
   mounting panel behind it for a while, and before that a dashed outline; both
   were decoration around a photograph that did not need any. */
.catering-media { position: relative; min-width: 0; }

.catering-frame {
  position: relative;
  display: block;
  aspect-ratio: 4 / 5;
  border-radius: var(--r-lg);
  overflow: hidden;
  transform: rotate(-2deg);
  /* Not the linen placeholder: on this band a cream box would flash pale
     before the photo paints. */
  background: var(--olive-mid);
  /* The warm shadow the photos use on linen is invisible against olive this
     deep, so it is the same shape of shadow in a tone that shows. */
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.32);
}
.catering-frame img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* The same note the story photo carries, in the same hand. Cream pill so it
   holds against the olive, tipped the opposite way to the photo. */
.catering-badge {
  position: absolute;
  bottom: -0.9rem;
  right: -0.55rem;
  background: var(--paper);
  border-radius: var(--r-pill);
  padding: 0.4rem 1.35rem;
  font-family: var(--script);
  font-weight: 600;
  font-size: 1.4rem;
  line-height: 1.2;
  color: var(--gold-ink);
  transform: rotate(3deg);
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.28);
}

@media (max-width: 900px) {
  /* One column, photo under the copy: the heading is what people scan for, so
     it keeps the top of the band. */
  .catering-wrap { grid-template-columns: 1fr; justify-content: stretch; gap: 2.5rem; }
  .catering-media { grid-column: 1; grid-row: 2; max-width: 22rem; }
  .catering-copy { grid-column: 1; grid-row: 1; }
  .catering-frame { border-radius: var(--r); }
}

@media (prefers-reduced-motion: reduce) {
  /* The tilt is a static transform, not motion, so it stays. Nothing here
     animates. */
}
/* Same pairing the contact band uses: one solid cream, one cream outline. */
.catering .btn-primary { background: var(--paper); color: var(--olive-deep); }
.catering .btn-primary:hover { background: var(--olive-tint); color: var(--olive-deep); }
.catering .btn-ghost { color: var(--paper); border-color: var(--cta-text); }
.catering .btn-ghost:hover { background: var(--olive-mid); color: var(--paper); border-color: var(--cta-text); }

/* ---------- de kaart ---------- */
.menu .wrap { max-width: calc(var(--maxw-kaart) + 2 * var(--gutter)); }
.menu-head { text-align: center; max-width: 36rem; margin: 0 auto clamp(1.5rem, 3vw, 2rem); }
.menu-head .eyebrow { margin-bottom: 0.85rem; }
.menu-head .script { display: none; }   /* the kaart title is set in Cormorant, not by hand */
.menu-head .display { font-size: clamp(2.1rem, 1.4rem + 2.8vw, 2.75rem); margin-bottom: 1rem; }
.menu-head .lede { margin-inline: auto; }
.menu-sprig { display: block; width: clamp(120px, 20vw, 160px); margin: 0.5rem auto 0.9rem; color: var(--olive); opacity: 0.5; }
.menu-sprig svg { display: block; width: 100%; height: auto; }

/* Category chip nav: sticks under the bar so the 25-dish kaart has an index.
   Plain anchors (works without JS); scrollspy in main.js sets .is-active. */
.cat-chips {
  position: sticky; top: var(--nav-h); z-index: 5;
  display: flex; gap: 0.5rem;
  overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch;
  padding: 0.6rem 0; margin: 0 0 clamp(1.5rem, 3vw, 2.25rem);
  background: var(--paper);
}
.cat-chips::-webkit-scrollbar { display: none; }
.cat-chips a {
  flex: none;
  padding: 0.45rem 0.95rem; border-radius: var(--r-pill);
  border: 1px solid var(--line-strong); background: var(--paper-raised);
  color: var(--ink); font-size: 0.875rem; font-weight: 500; text-decoration: none;
  transition: border-color 200ms var(--ease-out), background-color 200ms var(--ease-out), color 200ms var(--ease-out);
}
.cat-chips a:hover { border-color: var(--olive); color: var(--olive); }
.cat-chips a.is-active { background: var(--olive); border-color: var(--olive); color: var(--paper); }

.menu-cats { display: grid; gap: clamp(2.75rem, 5vw, 4.25rem); }
/* land anchor jumps below the sticky bar AND the sticky chip row */
.menu-cat { scroll-margin-top: calc(var(--nav-h) + 4.25rem); }
.cat-head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.2rem 1rem;
  padding-bottom: 0.625rem; border-bottom: 2px solid var(--ink);
}
.cat-title { font-family: var(--serif); font-size: clamp(1.6rem, 1.25rem + 1.5vw, 1.94rem); font-weight: 600; color: var(--ink); }
.cat-tag { font-family: var(--script); font-weight: 600; font-size: 1.375rem; color: var(--gold-ink); line-height: 1; }
.cat-blurb { color: var(--ink-soft); font-size: 0.94rem; line-height: 1.6; margin: 0.75rem 0 0; max-width: 60ch; }
/* A note in the owner's own hand, under the category blurb. */
.cat-note { font-family: var(--script); font-weight: 600; font-size: 1.3rem; color: var(--gold-ink); margin: 0.35rem 0 0; line-height: 1.3; }
/* A footnote to the whole category, printed under the last row because it is
   true of every row above it. Plain sans and quiet, not the script hand
   .cat-note uses: that voice is the owner speaking, this is a fact about the
   recipes. --ink-faint rather than --muted, which fails AA at this size. */
.cat-foot { color: var(--ink-faint); font-size: 0.9rem; line-height: 1.55; margin: 1.15rem 0 0; max-width: 60ch; }

.menu-item {
  display: grid; grid-template-columns: 84px minmax(0, 1fr) 280px;
  grid-template-areas: "thumb head tiers";
  gap: 1.6rem; align-items: start;
  padding: 1.5rem 0; border-bottom: 1px dashed var(--line-strong);
}
.menu-item:first-of-type { padding-top: 1.75rem; }
/* Every dish carries a thumbnail now (a photo, or a drawn tile until the new
   photography lands), so .has-thumb is the ordinary case rather than a variant. */
.mi-thumb { grid-area: thumb; display: block; }
.mi-thumb img {
  width: 84px; height: 84px; object-fit: cover; border-radius: 50%;
  border: 3px solid var(--paper-raised); box-shadow: var(--shadow-thumb);
}
.mi-head { grid-area: head; min-width: 0; }
.mi-name { font-family: var(--serif); font-size: 1.5rem; font-weight: 600; color: var(--ink); margin: 0 0 0.375rem; }
.mi-desc { color: var(--ink-soft); font-size: 0.94rem; line-height: 1.6; margin: 0; }
/* "Lees meer over onze turşu." sits on its own line rather than trailing the
   description, so the link is findable at a glance. */
.mi-more { display: block; margin-top: 0; padding-block: 0.25rem; font-size: 0.875rem; color: var(--olive); text-decoration: underline; text-underline-offset: 2px; }
.mi-more:hover { color: var(--olive-deep); }
/* Facts about what can be ordered: a minimum batch, or the one dish with meat. */
.mi-notes { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0.35rem 0 0; }
.mi-note { font-size: 0.8125rem; font-style: italic; color: var(--ink-faint); }

/* The kaart's rating: one star, the figure, the count, linking to the reactions
   on the dish's own page. It sits in the notes line with the diet icons and is
   meant to read as one of them, not as a badge: a dish other people rated is
   worth a second look, and that is the whole claim. No border, no colour of its
   own beyond the star, and it disappears entirely on a dish nobody has
   written about. */
.mi-rv {
  display: inline-flex; align-items: center; gap: 0.22rem;
  font-size: 0.8125rem; color: var(--ink-soft); text-decoration: none;
  font-variant-numeric: tabular-nums;
}
.mi-rv-star { fill: var(--olive); flex: none; }   /* the same star colour as the review rows, not a second one */
.mi-rv-fig { font-weight: 600; }
.mi-rv-count { color: var(--ink-faint); }
/* Underline only the numbers on hover, so the row does not light up as a whole
   and the star stays a mark rather than part of a link decoration. */
.mi-rv:hover .mi-rv-fig, .mi-rv:focus-visible .mi-rv-fig { text-decoration: underline; }
.mi-rv:focus-visible { outline: 2px solid var(--olive); outline-offset: 2px; border-radius: 0.25rem; }
/* What the dish is, on the kaart row: a small icon plus its word. Every dish
   carries at least one, so these sit on the row rather than shouting from it. */
.mi-attrs { display: inline-flex; flex-wrap: wrap; gap: 0.55rem; }
.mi-attr {
  display: inline-flex; align-items: center; gap: 0.28rem;
  font-size: 0.76rem; font-weight: 700; letter-spacing: 0.02em;
  font-style: normal; color: var(--olive);
}
/* Three meanings, three colours. Olive for what grows, terracotta for meat, and
   gold for the halal certification, which is neither: green next to a leaf read
   as "plant-based", which is the opposite of what the mark says. */
.mi-attr-chicken, .mi-attr-beef { color: var(--terracotta); }
.mi-attr-halal { color: var(--gold-ink); }
/* Fish shares the meat colour: the distinction a vegetarian is scanning for is
   "does this contain an animal", and that is the same answer. */
.mi-attr-fish { color: var(--terracotta); }
/* The asterisk marks a badge that only applies depending on the order. */
.mi-attr-star, .diet-badge-star { font-weight: 700; margin-left: -.18em; }
.diet-choice { margin: .4rem 0 0; font-size: .8rem; color: var(--ink-faint); }
.mi-note.mi-choice { font-style: normal; font-size: 0.72rem; color: var(--ink-faint); }
/* "Extra carrot, cucumber or pepper?" under the five jars that take them. An
   aside in the same voice as the minimum-order note: it announces, it does not
   sell, because the choosing happens on the cart line. */
.mi-note.mi-extras { flex-basis: 100%; }
/* "Allergenen: gluten, melk": its own line under every dish, upright rather
   than italic because it is a fact someone may be scanning for, not an aside. */
.mi-note.mi-allergens { flex-basis: 100%; font-style: normal; font-size: 0.76rem; }
.diet-allergens { margin: .6rem 0 0; font-size: .9rem; color: var(--ink); }
.diet-traces { margin: .2rem 0 0; font-size: .8rem; color: var(--ink-faint); }
.mi-note.mi-meat { color: var(--terracotta); font-style: normal; font-size: 0.76rem; font-weight: 700; letter-spacing: 0.02em; }
/* Halal sits next to the meat note and answers the question that note raises,
   so it reads as reassurance rather than another warning: olive, not terracotta. */
.mi-note.mi-halal { color: var(--olive); font-style: normal; font-size: 0.76rem; font-weight: 700; letter-spacing: 0.02em; }
/* A dish that closes on a date. It wears the terracotta the meat note wears
   because both answer "read this before you scroll past", and it is a pill
   rather than another italic aside so it survives a row that already carries
   two badges. The date is in the words; the colour only makes them findable. */
.mi-note.mi-seasonal {
  font-style: normal; font-size: 0.76rem; font-weight: 700; letter-spacing: 0.02em;
  color: var(--terracotta); background: var(--terracotta-tint);
  border-radius: var(--r-pill); padding: 0.14rem 0.55rem;
}

/* A dish that is paused: still on the kaart so a regular sees it is coming
   back, but visibly out of reach. The photo goes grey and pale, the words step
   down to the faint ink, and where the prices would be sits one quiet pill
   instead of a +. The text keeps --ink-faint rather than an opacity fade, so it
   stays AA-legible while reading as switched off. */
.menu-item.is-unavailable .mi-thumb img { filter: grayscale(1); opacity: 0.5; box-shadow: none; }
.menu-item.is-unavailable .mi-name { color: var(--ink-faint); }
.menu-item.is-unavailable .mi-desc { color: var(--ink-faint); }
.mi-unavailable {
  align-self: flex-start; margin: 0;
  font-size: 0.76rem; font-weight: 700; letter-spacing: 0.02em;
  color: var(--ink-soft); border: 1px dashed var(--line-strong);
  border-radius: var(--r-pill); padding: 0.2rem 0.65rem;
}

/* Price rows: size, dotted leader, price, and the round + that adds it. */
.mi-tiers { grid-area: tiers; display: flex; flex-direction: column; gap: 0.44rem; padding-top: 0.25rem; }
.tier { display: flex; align-items: center; gap: 0.5rem; font-size: 0.94rem; }
.tier-info { display: flex; align-items: baseline; gap: 0.5rem; flex: 1; min-width: 0; }
.tier-label { display: flex; align-items: baseline; gap: 0.5rem; flex: 1; min-width: 0; color: var(--ink-soft); white-space: nowrap; }
.tier-label::after { content: ""; flex: 1 1 auto; min-width: 1rem; border-bottom: 1px dotted var(--leader); transform: translateY(-0.25rem); }
.tier-price { font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.tier.active .tier-price { color: var(--olive-deep); }
.tier-ctl { flex: none; display: flex; align-items: center; justify-content: flex-end; min-width: 28px; min-height: 28px; }
/* The most-tapped control on a phone. It stays 28px because the kaart row is
   drawn around that circle, so the tap area is grown instead of the button:
   a transparent ::before pads it out to the 44px comfortable target without
   moving a pixel of the layout. */
.tier-add {
  width: 28px; height: 28px; border-radius: 50%;
  border: 1.5px solid var(--olive); background: transparent; color: var(--olive);
  font-size: 1rem; line-height: 1; padding: 0;
  display: grid; place-items: center;
  position: relative;
  transition: background-color 180ms var(--ease-out), color 180ms var(--ease-out), transform 140ms var(--ease-out);
}
.tier-add::before {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 44px; height: 44px; transform: translate(-50%, -50%);
}
.sum-remove::before {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 44px; height: 44px; transform: translate(-50%, -50%);
}
/* Invisible hit-area extension: 44px+ wide touch target without growing the
   visual circle. Vertical reach stays modest so dense tier rows don't overlap. */
.tier-add::after { content: ""; position: absolute; inset: -4px -10px; border-radius: var(--r-pill); }
.tier-add:hover { background: var(--olive); color: var(--paper); }
.tier-add:active { transform: scale(0.92); }
/* Same height as the + it replaces, so a row never jumps when a dish is added. */
.mini-stepper {
  display: inline-flex; align-items: center; gap: 0.125rem;
  height: 28px; padding: 0 0.125rem;
  border: 1.5px solid var(--olive); border-radius: var(--r-pill); background: var(--olive-tint);
  animation: ctlIn 180ms var(--ease-out);
}
.mini-stepper button {
  width: 24px; height: 24px; border: none; background: transparent; color: var(--olive-deep);
  font-size: 0.95rem; line-height: 1; padding: 0; border-radius: 50%;
  display: grid; place-items: center;
  transition: transform 140ms var(--ease-out), background-color 160ms var(--ease-out);
}
.mini-stepper button:hover { background: rgba(111, 116, 66, 0.16); }
.mini-stepper button:active { transform: scale(0.85); }
.mini-stepper .n {
  min-width: 1.05rem; text-align: center; font-weight: 700; font-size: 0.8125rem;
  font-variant-numeric: tabular-nums; color: var(--olive-deep);
}
@keyframes ctlIn { from { opacity: 0.4; transform: scale(0.9); } to { opacity: 1; transform: scale(1); } }

/* Below the three-column width the price rows drop under the description, and
   the tap targets grow to a comfortable size. */
@media (max-width: 860px) {
  .menu-item { grid-template-columns: 84px minmax(0, 1fr); grid-template-areas: "thumb head" "tiers tiers"; gap: 1rem 1.1rem; }
  .mi-tiers { padding-top: 0.35rem; gap: 0.15rem; }
  .tier { padding: 0.28rem 0; }
  .tier-ctl { min-width: 36px; min-height: 36px; }
  .tier-add { width: 36px; height: 36px; font-size: 1.15rem; }
  .mini-stepper { height: 36px; }
  .mini-stepper button { width: 30px; height: 30px; font-size: 1.05rem; }
}
@media (max-width: 480px) {
  .menu-item { grid-template-columns: 64px minmax(0, 1fr); }
  .mi-thumb img { width: 64px; height: 64px; }
  .mi-name { font-size: 1.3rem; }
}

/* ---------- photo zoom (kaart thumbnails) ----------
   The round thumbnail is a button (role set in main.js:wireMenuPhotos); it
   opens a card in the same dress as the order confirmation: linen, a hairline
   border, the large radius and the lifted shadow. Photo above the words on a
   phone, beside them from 720px. The photo, the cross and the dimmed page
   around the card close it; the words do not, so "Lees meer" still works. */
#menu-cats .mi-thumb { cursor: zoom-in; border-radius: 50%; }
#menu-cats .mi-thumb img { transition: transform 0.2s var(--ease-out); }
#menu-cats .mi-thumb:hover img { transform: scale(1.06); }
#menu-cats .mi-thumb:focus-visible { outline: 2px solid var(--olive); outline-offset: 3px; }
.photo-zoom {
  padding: 0; border: 0; background: transparent; overflow: visible;
  width: calc(100% - 2 * var(--gutter)); max-width: 28rem; max-height: none;
}
.photo-zoom::backdrop { background: rgba(38, 32, 20, 0.55); }
.photo-zoom[open] .pz-card { animation: pz-in 220ms var(--ease-out); }
.photo-zoom[open]::backdrop { animation: pz-fade 320ms var(--ease-out); }
/* A fade only: the card used to rise 14px and grow from 98%, which read as
   the photo shifting into place. It appears where it will stay. */
@keyframes pz-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes pz-fade { from { opacity: 0; } to { opacity: 1; } }
.pz-card {
  position: relative; overflow: hidden;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-lift);
}
.pz-photo { position: relative; overflow: hidden; cursor: zoom-out; background: var(--paper-2); }
/* The blurred thumbnail a slow connection sees first (set by main.js as
   --pz-thumb); the original fades in over it rather than snapping. */
.pz-photo::before {
  content: ""; position: absolute; inset: -16px;
  background: var(--pz-thumb, none) center / cover no-repeat;
  filter: blur(14px); opacity: 0; transition: opacity 200ms var(--ease-out);
}
.pz-photo.is-loading::before { opacity: 1; }
.pz-photo img {
  position: relative; display: block; width: 100%; height: auto; aspect-ratio: 1; max-height: 52vh; object-fit: cover;
  transition: opacity 280ms var(--ease-out);
}
.pz-photo.is-loading img { opacity: 0; }
.pz-body { padding: 1.25rem 1.5rem 1.5rem; }
.pz-cat { margin: 0 0 0.35rem; }
.pz-cat:empty { display: none; }
.pz-name { margin: 0 0 0.5rem; font-family: var(--serif); font-size: clamp(1.5rem, 1.2rem + 1.2vw, 1.9rem); font-weight: 600; line-height: 1.15; color: var(--ink); }
.pz-desc { margin: 0; color: var(--ink-soft); font-size: 0.95rem; line-height: 1.7; }
.pz-desc:empty { display: none; }
.pz-attrs .mi-attrs { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.9rem; }
.pz-more { margin-top: 0.9rem; padding-top: 0.75rem; border-top: 1px dashed var(--line-strong); }
.pz-more .mi-more { display: inline-block; }
.pz-more:empty, .pz-attrs:empty { display: none; }
.pz-close {
  position: absolute; top: 0.75rem; right: 0.75rem; z-index: 1;
  display: grid; place-items: center; padding: 0;
  width: 44px; height: 44px; border-radius: 50%; border: 1px solid var(--line);
  background: var(--paper-raised); color: var(--ink); cursor: pointer;
  box-shadow: var(--shadow-thumb);
}
.pz-close:hover { color: var(--olive-deep); }
.pz-close:focus-visible { outline: 2px solid var(--olive); outline-offset: 2px; }
@media (min-width: 720px) {
  .photo-zoom { max-width: 52rem; }
  .pz-card { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); }
  .pz-photo img { height: 100%; max-height: min(72vh, 30rem); }
  .pz-body { align-self: center; padding: clamp(2rem, 4vw, 2.75rem); padding-right: clamp(2.5rem, 5vw, 3.5rem); }
}
@media (prefers-reduced-motion: reduce) {
  #menu-cats .mi-thumb img { transition: none; }
  #menu-cats .mi-thumb:hover img { transform: none; }
  .photo-zoom[open] .pz-card, .photo-zoom[open]::backdrop { animation: none; }
  .pz-photo::before, .pz-photo img { transition: none; }
}

/* ---------- floating cart shortcut ---------- */
.cart-fab {
  position: fixed; right: clamp(1rem, 3vw, 2rem); bottom: clamp(1rem, 3vw, 2rem); z-index: 60;
  display: inline-flex; align-items: center; gap: 0.55rem;
  padding: 0.75rem 0.85rem 0.75rem 1.05rem; border: none; border-radius: var(--r-pill);
  background: var(--olive); color: var(--paper); box-shadow: var(--shadow-lift);
  font-family: var(--sans); font-weight: 700; font-size: 0.92rem;
  transition: transform 220ms var(--ease-out), opacity 220ms var(--ease-out), background-color 200ms var(--ease-out);
}
.cart-fab[hidden] { display: none; }
.cart-fab.at-order, .cart-fab.at-hero { opacity: 0; transform: translateY(10px); pointer-events: none; }
.cart-fab:hover { background: var(--olive-deep); }
.cart-fab:active { transform: scale(0.96); }
.cart-fab svg { display: block; }
.cart-fab-count {
  min-width: 1.5rem; height: 1.5rem; padding: 0 0.4rem; border-radius: var(--r-pill);
  background: var(--paper); color: var(--olive);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 0.8rem; font-weight: 700; font-variant-numeric: tabular-nums;
}
.cart-fab.pop { animation: fabPop 340ms var(--ease-out); }
@keyframes fabPop { 0% { transform: scale(1); } 32% { transform: scale(1.13); } 100% { transform: scale(1); } }

/* ---------- install invitation (js/pwa.js) ---------- */
/* A linen card, not a banner: it sits in a corner, it is dismissible, and it
   is drawn once per visitor per 90 days. Bottom LEFT deliberately, because the
   cart shortcut owns the bottom right; under 560px there is no room for both
   side by side, so it stacks above that button instead of over it. */
.pwa-invite {
  position: fixed; z-index: 70;
  left: clamp(1rem, 3vw, 2rem);
  bottom: max(clamp(1rem, 3vw, 2rem), env(safe-area-inset-bottom));
  width: min(23rem, calc(100vw - 2rem));
  display: flex; gap: 0.85rem; align-items: flex-start;
  padding: 1rem 1.1rem;
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--paper-raised); box-shadow: var(--shadow-lift);
  opacity: 0; transform: translateY(0.75rem);
  transition: opacity 240ms var(--ease-out), transform 240ms var(--ease-out);
}
.pwa-invite.is-in { opacity: 1; transform: none; }
@media (max-width: 560px) {
  /* Clear the .cart-fab row (its own bottom offset, plus its height), but only
     while that button is on screen: js/main.js hides it whenever the cart is
     empty, and reserving the space anyway leaves the card floating over a gap. */
  body:has(.cart-fab:not([hidden])) .pwa-invite {
    bottom: calc(max(clamp(1rem, 3vw, 2rem), env(safe-area-inset-bottom)) + 3.9rem);
  }
}
.pwa-invite-mark img { display: block; width: 40px; height: 40px; border-radius: 11px; }
.pwa-invite-body { min-width: 0; }
.pwa-invite-title {
  margin: 0 0 0.2rem; font-family: var(--serif); font-size: 1.12rem;
  font-weight: 600; line-height: 1.25; color: var(--ink);
}
.pwa-invite-text { margin: 0; font-size: 0.86rem; line-height: 1.55; color: var(--ink-soft); }
.pwa-invite-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.85rem; }
/* Smaller than a page button, still over the 24px AA target on every side. */
.pwa-invite-actions .btn { padding: 0.5rem 1.1rem; font-size: 0.85rem; }
@media (prefers-reduced-motion: reduce) {
  .pwa-invite { transition: none; opacity: 1; transform: none; }
}

/* ---------- offline page ---------- */
.offline-actions { display: flex; flex-wrap: wrap; gap: 0.75rem; }

/* ---------- bestellen ---------- */
.order .wrap { display: grid; gap: clamp(2rem, 4vw, 4rem); grid-template-columns: 1fr; }
@media (min-width: 900px) { .order .wrap { grid-template-columns: 1fr 1fr; align-items: start; } }
.order-intro .display { margin: 0.85rem 0 1.25rem; }
.order-intro > p { color: var(--ink-soft); line-height: 1.75; margin: 0 0 1.75rem; max-width: 46ch; }
.order-note {
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--r);
  padding: 1.6rem 1.85rem; font-size: 0.94rem; line-height: 1.75; color: var(--ink-soft);
}
.order-note b, .order-note strong { color: var(--ink); font-weight: 700; }
.order-note p { margin: 0; }

.order-card {
  background: var(--paper-raised); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-card); overflow: hidden;
}
.order-card > * { padding-inline: clamp(1.35rem, 3vw, 2.125rem); }
.oc-title {
  font-family: var(--serif); font-size: 1.625rem; font-weight: 600; color: var(--ink);
  padding-top: 2.125rem; padding-bottom: 1rem;
}

/* summary box */
.summary { border: 1.5px dashed var(--line-strong); border-radius: var(--r-sum); padding: 1.125rem 1.25rem; margin-inline: clamp(1.35rem, 3vw, 2.125rem); }
.order-card > .summary { padding-inline: 1.25rem; }
.summary-empty { color: var(--ink-soft); font-size: 0.875rem; margin: 0 0 0.75rem; }
.sum-line {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto auto;
  align-items: center; column-gap: 0.75rem;
  padding: 0.56rem 0; border-bottom: 1px solid var(--rule-soft); font-size: 0.875rem;
}
.sum-main { min-width: 0; }
.sum-name { font-weight: 700; color: var(--ink); }
.sum-meta { font-size: 0.75rem; color: var(--ink-faint); }
/* The dressing chips under a turşu line. Toggle buttons, one per extra, in
   the olive the rest of the cart's controls wear: outlined when off, filled
   when on, so a pressed chip reads as "on the jar" without a checkbox. The
   price rides on every chip because that is the moment the euro is spent.
   Two lines of chips on a phone are fine; a chip that wraps mid-word is not,
   so each chip keeps its words together and the row wraps between them. */
/* The pot rows under a turşu group: "Pot 1   wortel, extra pittig   € 22,00 >".
   One tappable row per pot, words instead of chips, so the cart reads as a
   list and the choosing happens in the sheet. The chevron is the only hint
   that the row opens something; the row is a button and the whole width is
   the target. */
.sum-pots { grid-column: 1 / -1; display: grid; gap: 0.1rem; margin-top: 0.1rem; }
/* [open button: label, worn, price] [x]: the same last two columns as the
   group line above (price, then the x), so every amount in the block sits in
   one column and the totals under it read as their sum. */
.sum-pot-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; column-gap: 0.75rem; }
/* The phone layout pins .sum-remove to a named area of the group line; in a
   pot row that name is unknown and would conjure empty columns. */
.sum-pot-row .sum-remove { grid-area: auto; }
.sum-pot-open {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: baseline; column-gap: 0.55rem;
  min-height: 2rem; padding: 0.25rem 0 0.25rem 0.35rem; margin-left: -0.35rem;
  border: none; border-radius: var(--r-field); background: transparent; color: var(--ink-soft);
  font: inherit; font-size: 0.8125rem; text-align: left; cursor: pointer;
  transition: background-color 160ms var(--ease-out);
}
.sum-pot-open:hover { background: var(--olive-tint); }
.sum-pot-open:focus-visible { outline: 2px solid var(--olive); outline-offset: -2px; }
/* A fixed column for the number, so "Wortel" on pot 1 and "geen extra's" on
   pot 2 start at the same x, and the number's left edge is the dish name's. */
/* Tabular figures: a 1 as wide as a 2, so "Pot 1" and "Pot 2" end on the
   same x and the numbers stack instead of jittering. */
.sum-pot-label { min-width: 2.75rem; font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }
.sum-pot-worn { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink-faint); font-style: italic; }
.sum-pot-worn.on { color: var(--olive-deep); font-style: normal; font-weight: 600; }
/* The same width as the group line's price, so the two right-align. */
.sum-pot-price { width: 4.125rem; text-align: right; font-variant-numeric: tabular-nums; color: var(--ink-soft); }

/* The sheet itself. Same scrim and fade as the confirmation, but the card
   sits at the bottom of a phone screen, where a thumb is, and in the middle
   of a wide one. */
/* .confirm.sheet, not .sheet: the .confirm rule further down the file sets
   place-items and padding, and would win on order alone. */
.confirm.sheet { align-items: end; padding: 0; }
.sheet-card {
  position: relative; z-index: 1; width: 100%; max-width: 28rem; margin: 0 auto;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-lg) var(--r-lg) 0 0;
  box-shadow: var(--shadow-lift); padding: 1.25rem 1.25rem calc(1.25rem + env(safe-area-inset-bottom));
  opacity: 0; transform: translateY(24px);
  transition: opacity 320ms var(--ease-out), transform 320ms var(--ease-out);
}
.sheet.show .sheet-card { opacity: 1; transform: none; }
@media (min-width: 720px) {
  .confirm.sheet { align-items: center; padding: var(--gutter); }
  .sheet-card { border-radius: var(--r-lg); }
}
.sheet-title { margin: 0; font-family: var(--serif); font-size: 1.25rem; font-weight: 600; color: var(--ink); }
.sheet-sub { margin: 0.25rem 0 0.9rem; font-size: 0.8125rem; color: var(--ink-faint); }
.sheet-opts { display: grid; gap: 0.5rem; margin-bottom: 1rem; }
/* One row per extra: drawing, name, price, and a check that fills when it is
   on the pot. Big enough for a thumb, and the whole row is the button. */
.sheet-opt {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto; align-items: center; column-gap: 0.75rem;
  width: 100%; min-height: 3rem; padding: 0.55rem 0.85rem;
  border: 1.5px solid var(--line-strong); border-radius: var(--r-field);
  background: var(--paper-raised); color: var(--ink);
  font: inherit; font-size: 0.94rem; font-weight: 600; text-align: left; cursor: pointer;
  transition: border-color 160ms var(--ease-out), background-color 160ms var(--ease-out);
}
.sheet-opt .attr-icon { width: 22px; height: 22px; color: var(--olive-deep); }
.sheet-opt-price { font-weight: 500; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
.sheet-opt-check {
  width: 1.35rem; height: 1.35rem; border-radius: 50%;
  border: 1.5px solid var(--line-strong); display: grid; place-items: center;
  transition: background-color 160ms var(--ease-out), border-color 160ms var(--ease-out);
}
.sheet-opt[aria-pressed="true"] { border-color: var(--olive); background: var(--olive-tint); }
.sheet-opt[aria-pressed="true"] .sheet-opt-check { background: var(--olive); border-color: var(--olive); color: var(--paper); }
.sheet-opt[aria-pressed="true"] .sheet-opt-check::before { content: "✓"; font-size: 0.8rem; font-weight: 700; }
.sheet-opt:focus-visible { outline: 2px solid var(--olive); outline-offset: 2px; }
.sheet-card .btn { width: 100%; }
@media (prefers-reduced-motion: reduce) { .sheet-card, .sheet-opt, .sheet-opt-check, .sum-pot-open { transition: none; } }
.sum-stepper { display: inline-flex; align-items: center; gap: 0.5rem; }
.sum-stepper button {
  width: 24px; height: 24px; border-radius: 50%; border: 1px solid var(--line-strong);
  background: var(--paper); color: var(--ink); font-size: 0.875rem; line-height: 1; padding: 0;
  display: grid; place-items: center;
  transition: border-color 160ms var(--ease-out), color 160ms var(--ease-out), transform 140ms var(--ease-out);
}
.sum-stepper button:hover { border-color: var(--olive); color: var(--olive); }
.sum-stepper button:active { transform: scale(0.9); }
.sum-stepper .q { min-width: 1rem; text-align: center; font-weight: 700; font-variant-numeric: tabular-nums; }

/* On a phone the four columns leave the name perhaps 150px, so a two-word dish
   wraps and collides with the stepper sitting centred beside it. Give the name
   its own full-width row and put the controls underneath: the dish is what the
   reader is checking, and it should never be obscured by the buttons. */
@media (max-width: 560px) {
  .sum-line {
    grid-template-columns: auto 1fr auto;
    grid-template-areas:
      "main main main"
      "stepper price remove";
    row-gap: 0.5rem; column-gap: 0.75rem;
  }
  .sum-main { grid-area: main; }
  .sum-stepper { grid-area: stepper; }
  /* The price fills its column and sits at its right edge, beside the x; the
     desktop's fixed 4-rem width left it floating just right of the stepper. */
  .sum-price { grid-area: price; width: auto; justify-self: end; text-align: right; }
  .sum-pot-price { width: auto; }
  .sum-remove { grid-area: remove; }
  /* The tier belongs with the name, not orphaned onto its own line. */
  .sum-meta { display: inline; margin-left: 0.4rem; }
  .sum-name { display: inline; }
  /* On a phone the bestellen card IS the page: the desktop's card-in-a-column
     stacked four side paddings (gutter, card, dashed box, box padding) and
     left the order lines 238 of 380 pixels, with the free-jar picker down to
     207. So the card goes edge to edge and loses its side borders, the dashed
     box keeps only its top and bottom rules, and every inner padding is one
     rem. Same colours, same type; only the frame is gone. */
  .order .wrap { padding-inline: 0; }
  /* The intro text keeps one rem of side room; the note box goes edge to
     edge like the card under it, with the same rules top and bottom. */
  .order-intro > * { padding-inline: 1rem; }
  .order-note {
    padding: 1.2rem 1rem; border-radius: 0; border-left: 0; border-right: 0;
  }
  .order-card { border-radius: 0; border-left: 0; border-right: 0; box-shadow: none; }
  .order-card > * { padding-inline: 1rem; }
  .oc-title { padding-top: 1.5rem; }
  .order-card > .summary {
    margin-inline: 0; padding-inline: 1rem;
    border-left: 0; border-right: 0; border-radius: 0;
  }
  .sum-upsell { padding: 0.75rem 0.85rem; }
  /* The jar's name may wrap; the price and the photo never give way. */
  .sum-pick-name { white-space: normal; }
  .sum-pick-price { flex: 0 0 auto; }
}
.sum-price { width: 4.125rem; text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink); }
.sum-remove {
  border: none; background: transparent; color: var(--ink-faint); font-size: 1.05rem; line-height: 1;
  width: 1.75rem; height: 1.75rem; padding: 0; display: grid; place-items: center; border-radius: 50%;
  position: relative;
  transition: color 180ms var(--ease-out);
}
.sum-remove:hover { color: var(--terracotta); }
.sum-totals { border-top: 1px solid var(--rule-soft); margin-top: 0.75rem; padding-top: 0.75rem; }
.sum-row { display: flex; justify-content: space-between; align-items: baseline; font-size: 0.875rem; color: var(--ink-soft); padding: 0.1rem 0; }
.sum-row[hidden] { display: none; }
.sum-row .v { font-variant-numeric: tabular-nums; }
/* Parcel count next to Verzendkosten: without it a bundled two-jar order shows
   one shipping fee for two jars and reads as a pricing mistake. */
.sum-ship-label .sum-meta { margin-left: 0.4rem; }
.sum-row.grand { font-size: 0.94rem; font-weight: 700; color: var(--ink); margin-top: 0.35rem; }
.sum-row.grand .v { color: var(--ink); }

/* Free-jar upsell under the totals. The box is already paid for, so a second
   jar of that size ships for nothing; it sits with the money rather than in the
   guidance line, where it read as one more sentence to skip. */
.sum-upsell {
  display: flex; align-items: center; gap: 0.875rem; flex-wrap: wrap;
  margin-top: 0.9rem; padding: 0.8rem 0.9rem;
  background: var(--olive-tint); border: 1px solid var(--line-strong);
  border-radius: var(--r-sum);
}
.sum-upsell[hidden] { display: none; }
.sum-upsell-text { flex: 1 1 12rem; min-width: 0; }
.sum-upsell-head { margin: 0; font-weight: 700; font-size: 0.875rem; color: var(--olive-deep); }
.sum-upsell-sub { margin: 0.15rem 0 0; font-size: 0.8125rem; color: var(--ink-soft); }
.sum-upsell-btn {
  flex: 0 0 auto; min-height: 2.25rem; padding: 0.45rem 1rem;
  border: 1px solid var(--olive); border-radius: 999px;
  background: var(--olive); color: var(--cta-text);
  font-size: 0.8125rem; font-weight: 700; cursor: pointer;
  transition: background 160ms var(--ease-out), border-color 160ms var(--ease-out);
}
.sum-upsell-btn:hover { background: var(--olive-deep); border-color: var(--olive-deep); }

/* The jars that fit the free slot, opened by the button above. It takes its own
   row inside the flex box rather than squeezing beside the text, because five
   rows of photo-name-price need the full width to stay scannable. The circular
   thumbnails are the kaart's, so a jar recognised on the price list is
   recognised here. */
.sum-upsell-pick { flex: 1 0 100%; margin-top: 0.15rem; }
.sum-upsell-pick[hidden] { display: none; }
.sum-pick-title { margin: 0 0 0.5rem; font-size: 0.8125rem; font-weight: 700; color: var(--olive-deep); }

/* The size switch above the jars, shown when the open box takes either a 3-kilo
   or a 5-kilo pot. Same segmented control as Afhalen/Bezorgen, sized down: it
   is a choice inside a panel and must not outweigh the list it filters. It sits
   on its own row at the panel's own width rather than stretching, because two
   words do not need the full column. */
.sum-pick-sizes {
  display: inline-flex; gap: 0.25rem; margin: 0 0 0.55rem;
  background: var(--paper-2); padding: 0.25rem; border-radius: var(--r-field);
  /* The panel it sits on is already tinted, so the track needs an edge of its
     own: without one the unpressed half reads as a word rather than a choice. */
  border: 1px solid var(--line);
}
.sum-pick-size {
  border: none; background: transparent; border-radius: calc(var(--r-field) - 3px);
  min-height: 2.25rem; padding: 0.4rem 0.85rem;
  font: inherit; font-size: 0.8125rem; font-weight: 700; color: var(--ink-soft);
  cursor: pointer; white-space: nowrap;
  transition: background-color 200ms var(--ease-out), color 200ms var(--ease-out);
}
.sum-pick-size.on { background: var(--paper-raised); color: var(--olive-deep); box-shadow: var(--shadow-card); }
.sum-pick-size:focus-visible { outline: 2px solid var(--olive); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .sum-pick-size { transition: none; } }

.sum-pick-list { display: grid; gap: 0.4rem; }
.sum-pick-opt {
  display: flex; align-items: center; gap: 0.65rem; width: 100%;
  min-height: 2.75rem; padding: 0.4rem 0.6rem;
  font: inherit; color: var(--ink); text-align: left; cursor: pointer;
  background: var(--paper-raised); border: 1px solid var(--line);
  border-radius: var(--r-field);
  transition: border-color 160ms var(--ease-out), background 160ms var(--ease-out);
}
.sum-pick-opt:hover { border-color: var(--olive); background: #fff; }
.sum-pick-opt:focus-visible { outline: 2px solid var(--olive); outline-offset: 2px; }
.sum-pick-thumb, .sum-pick-thumb img {
  display: block; flex: 0 0 auto; width: 34px; height: 34px;
  border-radius: 50%; object-fit: cover;
}
.sum-pick-name { flex: 1 1 auto; min-width: 0; font-size: 0.875rem; font-weight: 600; }
.sum-pick-price { flex: 0 0 auto; font-size: 0.875rem; font-weight: 700; font-variant-numeric: tabular-nums; }
@media (prefers-reduced-motion: reduce) { .sum-pick-opt { transition: none; } }

/* fields */
.field { padding-top: 1.15rem; }
.field:last-child { padding-bottom: 1.4rem; }
.field > label, .field-label {
  display: block; font-size: 0.8125rem; font-weight: 700; color: var(--ink-soft); margin-bottom: 0.375rem;
}
.field-label .opt, label .opt { color: var(--ink-faint); font-weight: 500; }
input, textarea, select {
  width: 100%; font-family: inherit; font-size: 0.94rem; color: var(--ink);
  background: var(--paper); border: 1px solid var(--line-strong); border-radius: var(--r-field);
  padding: 0.69rem 0.75rem;
  transition: border-color 180ms var(--ease-out), box-shadow 180ms var(--ease-out);
}
textarea { resize: vertical; min-height: 4.5rem; }
input:focus, textarea:focus, select:focus { outline: none; border-color: var(--olive); box-shadow: 0 0 0 3px var(--olive-tint); }
input.invalid, textarea.invalid { border-color: var(--terracotta); box-shadow: 0 0 0 3px var(--terracotta-tint); }
input.soft { background: var(--olive-tint); color: var(--ink-soft); }
.error-text { color: var(--terracotta); font-size: 0.8rem; margin: 0.35rem 0 0; }

/* "Bedoelde je …@gmail.com?" under the e-mail field. Deliberately not styled as
   an error: nothing is wrong yet, the address is only suspicious, and all that
   is asked for is a glance. The corrected address is itself the button, so what
   you read is what you tap. */
.mail-fix[hidden] { display: none; }   /* a class selector beats the browser's own [hidden] rule, so say it here */
.mail-fix { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.25rem; margin: 0.35rem 0 0; font-size: 0.8rem; color: var(--ink-soft); }
.mail-fix .link-btn {
  border: 0; background: none; padding: 0.15rem 0; margin: 0;
  font: inherit; font-weight: 600; color: var(--terracotta);
  text-decoration: underline; text-underline-offset: 0.15em;
  cursor: pointer; min-height: 24px;   /* WCAG 2.2 target size, AA */
}
.mail-fix .link-btn:hover, .mail-fix .link-btn:focus-visible { opacity: 0.75; }
/* Temporary: the Microsoft junk-folder warning under the e-mail field. */
.mail-warn[hidden] { display: none; }
.mail-warn { margin: 0.5rem 0 0; padding: 0.6rem 0.75rem; border: 1px solid var(--terracotta); border-radius: 8px; background: var(--terracotta-tint); color: var(--terracotta); font-size: 0.8rem; line-height: 1.45; }
.date-note {
  display: grid; grid-template-columns: auto 1fr; gap: 0.55rem; align-items: start;
  margin: 0.5rem 0 0; padding: 0.6rem 0.75rem; background: var(--olive-tint);
  border-radius: var(--r-field); font-size: 0.83rem; color: var(--olive-deep); line-height: 1.45;
}
.date-note[hidden] { display: none; }
.date-note b { font-weight: 700; }
.date-note-i {
  width: 1.15rem; height: 1.15rem; margin-top: 0.05rem; border-radius: 50%;
  background: var(--olive); color: var(--paper); font-family: var(--serif);
  font-size: 0.75rem; font-weight: 700; font-style: italic; display: grid; place-items: center;
}
.ig-icon { color: var(--ink); flex: none; }
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* segmented fulfilment toggle */
.segment { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 0.3rem; background: var(--paper-2); padding: 0.28rem; border-radius: var(--r-field); }
/* Four countries ("Deutschland", "Frankreich") do not fit one row on a phone,
   so the country choice wraps two by two there and stays one row where it fits. */
#seg-country { grid-auto-flow: row; grid-template-columns: repeat(auto-fit, minmax(6.5rem, 1fr)); }
.seg {
  border: none; background: transparent; border-radius: calc(var(--r-field) - 3px);
  padding: 0.6rem 0.5rem; font-size: 0.875rem; font-weight: 700; color: var(--ink-soft);
  transition: background-color 200ms var(--ease-out), color 200ms var(--ease-out);
}
.seg small { display: block; font-weight: 400; font-size: 0.72rem; color: var(--ink-faint); }
.seg[aria-pressed="true"] { background: var(--paper-raised); color: var(--olive-deep); box-shadow: var(--shadow-card); }
.seg[aria-pressed="true"] small { color: var(--olive); }

/* Betaling uses the same segment as Afhalen/Bezorgen and Land: title only,
   like the Land toggle. It starts with neither half pressed because the choice
   is required; an unanswered submit rings the control in the error colour, so
   the problem is marked on the control itself and not only in the line below. */
.pay-field-err .segment { box-shadow: inset 0 0 0 1.5px var(--terracotta); }

/* delivery guidance line */
.guidance {
  margin-top: 0.9rem; font-size: 0.875rem; padding: 0.7rem 0.85rem; border-radius: var(--r-field);
  background: var(--paper); border: 1px solid var(--line); color: var(--ink-soft);
  display: flex; gap: 0.5rem; align-items: flex-start;
}
.guidance::before { content: ""; width: 0.55rem; height: 0.55rem; border-radius: 50%; margin-top: 0.45rem; flex: none; background: var(--muted); }
.guidance.ok { background: var(--olive-tint); border-color: var(--olive); color: var(--olive-deep); }
.guidance.ok::before { background: var(--olive); }
.guidance.warn { background: var(--terracotta-tint); border-color: var(--terracotta); color: var(--terracotta); }
.guidance.warn::before { background: var(--terracotta); }
.guidance.info { background: #F6EFD9; border-color: var(--gold); color: var(--olive-deep); }
.guidance.info::before { background: var(--gold); }

/* address grid */
.addr-grid { display: grid; gap: 0.7rem; }
.addr-row { display: grid; gap: 0.7rem; }
.addr-row.pchn { grid-template-columns: 1.1fr 1fr; }
.addr-row.stpl { grid-template-columns: 1.6fr 1fr; }
.sub { display: flex; flex-direction: column; gap: 0.3rem; min-width: 0; }
.sub label { font-size: 0.75rem; font-weight: 700; color: var(--ink-soft); }

/* Street suggestions (German postcodes): a panel that hangs off the street
   field in the same paper, line and radius as the field itself. */
.addr-combo { position: relative; }
.addr-suggest {
  position: absolute; top: calc(100% + 4px); left: 0; z-index: 20;
  width: max(100%, 15rem); max-width: calc(100vw - 2rem); max-height: 15.5rem; overflow-y: auto;
  margin: 0; padding: 0.3rem; list-style: none;
  background: var(--paper-raised); border: 1px solid var(--line-strong); border-radius: var(--r-field);
  box-shadow: var(--shadow-soft); overscroll-behavior: contain;
}
/* Windows otherwise draws its grey bar with arrow buttons inside the panel.
   Chrome drops the ::-webkit- rules as soon as scrollbar-color is set, and only
   they can remove the arrows, so the standard properties are for Firefox alone. */
.addr-suggest::-webkit-scrollbar { width: 10px; }
.addr-suggest::-webkit-scrollbar-button { display: none; height: 0; }
.addr-suggest::-webkit-scrollbar-track { background: transparent; }
.addr-suggest::-webkit-scrollbar-thumb { background: var(--leader); border-radius: 10px; border: 3px solid var(--paper-raised); }
@supports (-moz-appearance: none) {
  .addr-suggest { scrollbar-width: thin; scrollbar-color: var(--leader) transparent; }
}
.addr-suggest[hidden] { display: none; }
.addr-suggest li {
  padding: 0.6rem 0.65rem; border-radius: 7px; cursor: pointer;
  font-size: 0.94rem; color: var(--ink); line-height: 1.25;
}
.addr-suggest li:hover { background: var(--paper-2); }
.addr-suggest li[aria-selected="true"] { background: var(--olive-tint); color: var(--olive-deep); }
.addr-suggest li.is-current { display: flex; justify-content: space-between; gap: 0.5rem; font-weight: 700; color: var(--olive-deep); }
.addr-suggest li.is-current::after { content: "\2713"; color: var(--olive); }
/* With streets to choose from the field reads as a dropdown: a chevron that
   turns over while the list is open. Typing still filters it. */
.addr-combo.has-options input {
  padding-right: 2.3rem; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%236F7442' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 0.85rem center; background-size: 12px 8px;
}
.addr-combo.has-options.is-open input {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 6.5l5-5 5 5' fill='none' stroke='%236F7442' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.addr-status { font-size: 0.8rem; margin: 0.5rem 0 0; min-height: 1rem; color: var(--ink-faint); }
.addr-status.searching { color: var(--ink-soft); }
.addr-status.ok { color: var(--green); }
.addr-status.ok::before { content: "\2713  "; }
.addr-status.error, .addr-status.warn { color: var(--terracotta); }

.submit-row { padding-bottom: 1.75rem; padding-top: 1.4rem; display: flex; flex-direction: column; gap: 1rem; }
.submit-row .btn-primary { width: 100%; padding-block: 0.94rem; font-size: 1rem; }
.form-alt { font-size: 0.875rem; color: var(--ink-soft); text-align: center; margin: 0; }
.form-alt a { color: var(--olive); font-weight: 700; text-decoration: underline; text-underline-offset: 2px; }
.form-msg { font-size: 0.92rem; padding: 0.85rem 1rem; border-radius: var(--r-field); display: none; }
.form-msg.show { display: block; }
.form-msg.ok { background: var(--olive-tint); color: var(--olive-deep); }
.form-msg.err { background: var(--terracotta-tint); color: var(--terracotta); }

/* ---------- bezorging & afhalen ---------- */
.deliver { text-align: left; }
.deliver .eyebrow, .deliver .display { text-align: center; }
.deliver .display { max-width: 36rem; margin: 0 auto; }
.deliver-grid { display: grid; gap: clamp(1.4rem, 3vw, 1.875rem); margin-top: clamp(2.25rem, 4vw, 3.75rem); grid-template-columns: 1fr; }
@media (min-width: 640px) { .deliver-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1000px) { .deliver-grid { grid-template-columns: repeat(4, 1fr); } }
.deliver-card { border-top: 2px solid var(--olive); padding-top: 1.125rem; }
.deliver-card .dc-k { font-family: var(--serif); font-size: 2.875rem; font-weight: 600; color: var(--olive); line-height: 1; }
.deliver-card h3 { font-size: 1rem; font-weight: 700; color: var(--ink); margin: 0.75rem 0 0.5rem; }
.deliver-card p { font-size: 0.875rem; color: var(--ink-soft); line-height: 1.65; margin: 0; }

/* ---------- contact (olive band) ---------- */
.contact { background: var(--olive); text-align: center; }
.contact .wrap { max-width: calc(var(--maxw-narrow) + 2 * var(--gutter)); }
.contact .script { font-size: clamp(1.5rem, 1.2rem + 1.2vw, 1.75rem); display: block; color: var(--cta-text); }
.contact .display { color: var(--paper); font-size: clamp(2.1rem, 1.4rem + 3vw, 3rem); margin: 0.625rem 0 1.125rem; }
.contact .lede { color: var(--cta-text); font-size: 1.06rem; max-width: 48ch; margin: 0 auto 2.125rem; }
.contact-links { display: flex; flex-wrap: wrap; gap: 0.875rem; justify-content: center; }
.contact .btn-primary { background: var(--paper); color: var(--olive-deep); }
.contact .btn-primary:hover { background: var(--olive-tint); color: var(--olive-deep); }
.contact .btn-ghost { color: var(--paper); border-color: var(--cta-text); }
.contact .btn-ghost:hover { background: var(--olive-mid); color: var(--paper); border-color: var(--cta-text); }
.contact .ig-icon, .contact .tt-icon, .contact .mail-icon { color: currentColor; }

/* ---------- reveal on scroll ---------- */
.reveal { opacity: 0; transform: translateY(16px); transition: opacity 620ms var(--ease-reveal), transform 620ms var(--ease-reveal); }
.reveal.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { transition: opacity 300ms linear; transform: none; }
  .btn:active, .tier-add:active, .mini-stepper button:active, .sum-stepper button:active, .cart-fab:active { transform: none; }
  .cart-fab.pop, .mini-stepper { animation: none; }
  * { scroll-behavior: auto !important; }
}
@media (hover: none) {
  .btn-primary:hover { background: var(--olive); }
  .tier-add:hover { background: transparent; color: var(--olive); }
}

/* ---------- order confirmation overlay ---------- */
.confirm { position: fixed; inset: 0; z-index: 200; display: grid; place-items: center; padding: var(--gutter); }
.confirm[hidden] { display: none; }
.confirm-scrim { position: absolute; inset: 0; background: rgba(38, 32, 20, 0.55); opacity: 0; transition: opacity 320ms var(--ease-out); }
.confirm.show .confirm-scrim { opacity: 1; }
.confirm-card {
  position: relative; z-index: 1; width: 100%; max-width: 25rem; text-align: center;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--shadow-lift);
  padding: clamp(2rem, 5vw, 2.75rem);
  opacity: 0; transform: translateY(14px) scale(0.98);
  transition: opacity 360ms var(--ease-out), transform 360ms var(--ease-out);
}
.confirm.show .confirm-card { opacity: 1; transform: none; }
.confirm-mark { width: 3.5rem; height: 3.5rem; margin: 0 auto 1rem; border-radius: var(--r-pill); background: var(--olive); color: var(--paper); display: grid; place-items: center; }
.confirm.show .confirm-mark { animation: confirmPop 440ms var(--ease-out); }
@keyframes confirmPop { 0% { transform: scale(0.55); opacity: 0; } 55% { transform: scale(1.08); } 100% { transform: scale(1); opacity: 1; } }
.confirm-script { font-size: clamp(1.7rem, 1.3rem + 1.4vw, 2rem); display: block; }
.confirm-card .display { font-size: clamp(1.5rem, 1.2rem + 1.2vw, 1.9rem); margin: 0.25rem 0 0.75rem; }
.confirm-body { color: var(--ink-soft); font-size: 0.95rem; margin: 0 0 1.75rem; line-height: 1.7; }
.confirm-card .btn { width: 100%; }
@media (prefers-reduced-motion: reduce) {
  .confirm-scrim, .confirm-card { transition: opacity 200ms linear; transform: none; }
  .confirm.show .confirm-card { transform: none; }
  .confirm.show .confirm-mark { animation: none; }
}

/* ============================================================
   Content pages (product, FAQ, keuken, privacy, terms, how)
   ============================================================ */
/* The bar is sticky rather than fixed, so these pages need no top offset for it. */
.page-hero {
  padding-top: clamp(2.5rem, 5vw, 4rem);
  padding-bottom: clamp(1.5rem, 3vw, 2.25rem);
  background: var(--paper);
}
.page-hero .wrap { max-width: calc(var(--maxw-narrow) + 2 * var(--gutter)); }
.page-hero .eyebrow { margin-bottom: 0.6rem; }
.page-hero h1 {
  font-family: var(--serif); font-weight: 600; line-height: 1.14;
  font-size: clamp(2.1rem, 1.5rem + 2.4vw, 3.25rem); color: var(--ink); margin: 0;
  text-wrap: pretty;
}
.page-hero h1 em { font-style: normal; color: var(--olive); }
.page-hero .lede { max-width: 60ch; margin-top: 1rem; }
/* Handwritten opener above a page title (the 404's "Oeps"). */
.page-script { display: block; font-size: 1.75rem; margin-bottom: 0.2rem; }
.page-hero-head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 0.6rem 1rem; }
.diet-badges { display: flex; flex-wrap: wrap; gap: 0.4rem; list-style: none; margin: 0.5rem 0 0; padding: 0; }
.diet-badge {
  display: inline-flex; align-items: center; gap: 0.35rem;
  padding: 0.32rem 0.72rem; border-radius: var(--r-pill);
  background: var(--olive-tint); color: var(--olive-deep); border: 1px solid var(--line);
  font-size: 0.8rem; font-weight: 700; line-height: 1; white-space: nowrap;
}
.diet-leaf { width: 0.95em; height: 0.95em; flex: none; color: var(--olive); }
/* Attribute icons share one sizing rule wherever they appear. The halal roundel
   is a wordmark rather than a pictogram, so it needs a little more room than the
   others to stay legible at all. */
.attr-icon { width: 1.05em; height: 1.05em; flex: none; }
.attr-icon-halal { width: 1.25em; height: 1.25em; }
/* Meat reads as a caution, halal as reassurance, plants as neither. */
.diet-badge-chicken, .diet-badge-beef, .diet-badge-fish { background: var(--paper-raised); color: var(--terracotta); }
.diet-badge-halal { background: var(--gold-tint); color: var(--gold-ink); border-color: #E6D8B4; }
.page-updated { margin-top: 0.85rem; font-size: 0.85rem; color: var(--ink-faint); }

.page-body { padding-block: clamp(2rem, 4vw, 3.25rem); }
.page-body .wrap { max-width: calc(var(--maxw-narrow) + 2 * var(--gutter)); }

/* Prose */
.prose { color: var(--ink-soft); }
.prose > * + * { margin-top: 1.05rem; }
.prose h2 {
  font-family: var(--serif); font-weight: 600; color: var(--ink);
  font-size: clamp(1.5rem, 1.2rem + 1vw, 1.94rem); line-height: 1.2; margin-top: 2.6rem;
}
.prose h2:first-child { margin-top: 0; }
.prose h2 em { font-style: normal; color: var(--olive); }
.prose h3 { font-family: var(--sans); font-weight: 700; color: var(--ink); font-size: 1.05rem; margin-top: 1.7rem; }
.prose p { line-height: 1.78; max-width: 68ch; }
.prose a { color: var(--olive); text-decoration: underline; text-underline-offset: 2px; }
.prose a:hover { color: var(--olive-deep); }

/* Olive on --paper-2 is 4.2:1, under AA. Bare prose links inside a band take
   the deep cut (6.7:1); anything with a class styles itself. */
.band p a:not([class]), .band li a:not([class]) { color: var(--olive-deep); }
.prose strong { color: var(--ink); font-weight: 700; }
.prose ul, .prose ol { padding-left: 1.3rem; max-width: 68ch; }
.prose li { line-height: 1.75; margin-top: 0.4rem; }
.prose li::marker { color: var(--olive); }
.prose hr { border: 0; border-top: 1px solid var(--line); margin: 2.5rem 0; }
/* A pull-quote in the owner's hand, for the personal asides on /keuken. */
.prose .hand { font-family: var(--script); font-weight: 600; font-size: 1.5rem; line-height: 1.4; color: var(--gold-ink); max-width: 40ch; }

/* Closing CTA under an article: serif heading, one line, one button. */
.page-cta { margin-top: 2.6rem; }
.page-cta h2 {
  font-family: var(--serif); font-weight: 600; color: var(--ink);
  font-size: clamp(1.5rem, 1.2rem + 1vw, 1.94rem); line-height: 1.2; margin: 0 0 0.5rem;
}
.page-cta p { color: var(--ink-soft); line-height: 1.78; max-width: 68ch; margin: 0 0 1.2rem; }
/* The buttons here sit as bare siblings with no flex parent, so on a narrow
   screen they wrapped onto separate lines and touched. Every other button group
   on the site (.hero-cta, .catering-links, .contact-links) is a flex row with a
   gap; this one was the exception. Laying the buttons out on their own row keeps
   the h2 and p flow above untouched. */
.page-cta .btn { display: inline-flex; align-items: center; justify-content: center; }
.page-cta .btn + .btn { margin-left: 0.875rem; }
@media (max-width: 520px) {
  /* Stacked and full width: two pill buttons side by side do not fit, and a
     half-width second button reads as less important than it is. */
  .page-cta .btn { display: flex; width: 100%; }
  .page-cta .btn + .btn { margin-left: 0; margin-top: 0.75rem; }
}

.note-card {
  background: var(--paper-2); border: 1px solid var(--line); border-radius: var(--r);
  padding: 1.35rem 1.6rem; color: var(--ink-soft); line-height: 1.72; max-width: 68ch;
}
/* The order-section "Goed om te weten" card: one policy per line, scannable. */
.order-note ul { margin: 0.55rem 0 0; padding-left: 1.15rem; }
.order-note li { margin-top: 0.4rem; line-height: 1.6; }
.order-note li::marker { color: var(--olive); }
.note-card strong { color: var(--ink); }

/* Numbered "how it's made" steps */
.steps { display: grid; gap: 1.2rem; margin-top: 1.7rem; }
.step { display: grid; grid-template-columns: auto 1fr; gap: 1rem; align-items: start; }
.step-n {
  width: 2.4rem; height: 2.4rem; flex: none; border-radius: var(--r-pill);
  background: var(--olive); color: var(--paper); display: grid; place-items: center;
  font-family: var(--serif); font-size: 1.15rem; font-weight: 600;
}
/* Step titles are h2 for heading order (h1 -> h2, no skip) but keep the small
   sans look of a step label, overriding the serif .prose h2 that also matches. */
.step h2 { font-family: var(--sans); font-weight: 700; color: var(--ink); font-size: 1.05rem; margin: 0.2rem 0; }
.step p { margin: 0; }

/* ---------- FAQ accordion ---------- */
.faq-cat {
  font-family: var(--serif); font-weight: 600;
  font-size: clamp(1.35rem, 1.15rem + 0.8vw, 1.7rem); color: var(--ink);
  margin: clamp(2rem, 4vw, 2.75rem) 0 0.75rem;
}
.faq-list > .faq-cat:first-child { margin-top: 0; }
/* Each question is its own card rather than a row in a ruled list. */
.faq-item {
  background: var(--paper-raised); border: 1px solid var(--line); border-radius: var(--r);
  margin-top: 0.75rem; overflow: hidden;
  transition: border-color 200ms var(--ease-out);
}
.faq-item.open { border-color: var(--line-strong); }
.faq-q {
  width: 100%; text-align: left; background: none; border: 0;
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  padding: 1.15rem 1.35rem; font-family: var(--sans); font-weight: 700;
  font-size: 1rem; color: var(--ink); line-height: 1.45;
}
.faq-q:hover { color: var(--olive); }
.faq-icon { flex: none; width: 1.1rem; height: 1.1rem; color: var(--olive); transition: transform 300ms var(--ease-out); }
.faq-item.open .faq-icon { transform: rotate(180deg); }
/* Collapse via grid-template-rows (animatable without layout thrash); the
   inner's overflow:hidden zeroes its automatic minimum so 0fr really is 0.
   Bottom breathing room lives on the last child, not as padding, because a
   border-box padding would hold the collapsed row open by its own height. */
.faq-a { display: grid; grid-template-rows: 0fr; transition: grid-template-rows 340ms var(--ease-out); }
.faq-item.open .faq-a { grid-template-rows: 1fr; }
.faq-a-inner { overflow: hidden; min-height: 0; padding: 0 1.35rem; color: var(--ink-soft); font-size: 0.94rem; line-height: 1.75; max-width: 68ch; }
.faq-a-inner > :last-child { margin-bottom: 1.3rem; }
.faq-a-inner p { margin: 0; }
.faq-a-inner p + p { margin-top: 0.7rem; }
@media (prefers-reduced-motion: reduce) { .faq-a, .faq-icon { transition: none; } }

/* ============================================================
   Product landing pages
   ============================================================ */
/* The dish photo gets the same tipped, signed card as the story photo. */
.prod-photo { margin: 0 auto clamp(1.8rem, 4vw, 2.5rem); max-width: 420px; }
.prod-photo img { width: 100%; height: auto; aspect-ratio: 1 / 1; object-fit: cover; border-radius: var(--r-lg); transform: rotate(-2deg); box-shadow: var(--shadow-photo); }
/* Sarma: draggable before/after of yaprak vs lahana. The frame carries the
   shadow and the rounding instead of the photos, and stays square and untilted:
   the tipped polaroid the other dish pages wear would open corner gaps under
   overflow:hidden, and the drag maths reads straight across the frame. Both
   photos are stacked at full size, so .prod-photo's img rule is undone here
   rather than fought with. */
.prod-compare { max-width: 440px; }
.compare { position: relative; width: 100%; aspect-ratio: 1 / 1; border-radius: var(--r-lg); overflow: hidden; touch-action: none; cursor: ew-resize; --pos: 50%; box-shadow: var(--shadow-photo); }
.compare:focus-visible { outline: 2px solid var(--olive); outline-offset: 3px; }
.compare picture { position: absolute; inset: 0; display: block; }
.compare img { display: block; width: 100%; height: 100%; aspect-ratio: auto; object-fit: cover; border-radius: 0; transform: none; box-shadow: none; }
.cmp-base { position: absolute; inset: 0; }
.cmp-top { position: absolute; inset: 0; clip-path: inset(0 0 0 var(--pos)); }
.cmp-handle { position: absolute; top: 0; bottom: 0; left: var(--pos); width: 2px; margin-left: -1px; background: var(--paper); box-shadow: 0 0 0 1px rgba(79, 84, 48, 0.35); pointer-events: none; }
.cmp-grip { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 40px; height: 40px; border-radius: 50%; background: var(--paper); color: var(--olive); display: grid; place-items: center; box-shadow: var(--shadow-soft); }
.cmp-grip svg { display: block; width: 22px; height: 22px; }
/* Opacity is driven from js/pages.js, which fades a chip out as the divider
   reaches it. The transition only smooths the keyboard's 4% steps; during a
   drag the class is off, because easing there lags the chip behind the finger. */
.cmp-label { position: absolute; bottom: 10px; font-size: 0.72rem; font-weight: 700; color: var(--paper); background: rgba(46, 40, 34, 0.6); padding: 0.2rem 0.55rem; border-radius: var(--r-pill); pointer-events: none; }
.compare:not(.is-dragging) .cmp-label { transition: opacity 150ms ease; }
.cmp-label-l { left: 10px; }
.cmp-label-r { right: 10px; }
/* The chip still has to vanish under reduced motion, it just stops easing:
   what it says would otherwise be wrong, which is not a motion preference. */
@media (prefers-reduced-motion: reduce) { .cmp-label { transition: none; } }

/* Turşu: a gallery of the five jars, one at a time. Unlike the sarma wipe this
   one keeps the tipped card the other dish pages wear, because nothing here is
   measured across the frame: the slides simply cross-fade in place, and a
   rotated box with overflow:hidden rounds correctly in its own coordinates.
   .prod-photo's img rule is undone inside, as it is for the wipe: the photos
   are stacked at full size and the frame carries the tilt, the rounding and the
   shadow instead of each photo carrying its own. */
.prod-gallery { max-width: 440px; }
.gallery {
  position: relative; width: 100%; aspect-ratio: 1 / 1;
  border-radius: var(--r-lg); overflow: hidden;
  transform: rotate(-2deg); box-shadow: var(--shadow-photo);
}
.gal-slide { position: absolute; inset: 0; opacity: 0; transition: opacity 700ms ease; }
.gal-slide.is-on { opacity: 1; }
.gal-slide picture { position: absolute; inset: 0; display: block; }
.gal-slide img { display: block; width: 100%; height: 100%; aspect-ratio: auto; object-fit: cover; border-radius: 0; transform: none; box-shadow: none; }
/* Named like the wipe's chips, and for the same reason: a photograph of pickled
   vegetables does not tell you which jar it is. It rides inside its own slide,
   so it fades with the photo it names and can never end up on another one. */
.gal-label {
  position: absolute; left: 10px; bottom: 10px;
  font-size: 0.72rem; font-weight: 700; color: var(--paper);
  background: rgba(46, 40, 34, 0.6); padding: 0.2rem 0.55rem;
  border-radius: var(--r-pill); pointer-events: none;
}

/* The strip under the photo: the same circular thumbnails the kaart uses, so a
   reader who recognises a jar from the price list recognises it here. */
.gal-thumbs { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.55rem; margin-top: 1.15rem; }
.gal-thumb {
  width: 48px; height: 48px; padding: 0; border-radius: 50%; cursor: pointer;
  background: none; border: 2px solid transparent; box-shadow: var(--shadow-thumb);
  transition: border-color 160ms ease, transform 160ms ease;
}
.gal-thumb picture, .gal-thumb img {
  display: block; width: 100%; height: 100%; border-radius: 50%;
  aspect-ratio: auto; object-fit: cover; transform: none; box-shadow: none;
}
.gal-thumb:hover { transform: translateY(-2px); }
.gal-thumb.is-on { border-color: var(--olive); }
.gal-thumb:focus-visible { outline: 2px solid var(--olive); outline-offset: 3px; }
/* 48px of ring is the tap target, comfortably over the 24px WCAG 2.2 minimum,
   and the flex gap keeps neighbouring targets from touching. */

/* Reduced motion stops the rotation dead (js/pages.js never starts the timer)
   and drops the cross-fade, so the strip becomes an ordinary set of buttons
   that swap the photo instantly. */
@media (prefers-reduced-motion: reduce) {
  .gal-slide { transition: none; }
  .gal-thumb { transition: none; }
  .gal-thumb:hover { transform: none; }
}

/* Price rows + add-to-cart on a product page, in the kaart's row style. */
.product-order {
  margin: 2.5rem 0; padding: 1.6rem 1.85rem;
  border: 1px solid var(--line); border-radius: var(--r); background: var(--paper-raised);
}
.product-order h2 { margin: 0 0 0.4rem; font-family: var(--serif); font-size: 1.5rem; color: var(--ink); }
.product-order p { margin: 0 0 1.2rem; color: var(--ink-soft); }
/* A seasonal jar's closing date, in the order block. It wears the same pill as
   the kaart row so a reader who saw it on the price list recognises it here. */
.product-seasonal { margin: 0 0 1.2rem; }
.product-order .mi-tiers { max-width: 380px; }

.prose .cross-link {
  display: flex; flex-direction: column; gap: 0.15rem;
  margin-top: 2rem; padding: 1.15rem 1.4rem;
  border: 1px solid var(--line); border-radius: var(--r); text-decoration: none;
  transition: border-color 200ms var(--ease-out);
}
.prose .cross-link:hover { border-color: var(--olive); }
.cross-link-k { font-size: 0.85rem; color: var(--ink-soft); }
.cross-link-t { font-family: var(--serif); font-weight: 600; font-size: 1.3rem; color: var(--ink); transition: color 200ms var(--ease-out); }
.prose .cross-link:hover .cross-link-t { color: var(--olive); }

/* .prose a is more specific than .btn, so restate the button look inside prose. */
.prose .btn { text-decoration: none; }
.prose .btn-primary, .prose .btn-primary:hover { color: var(--paper); }
.prose .btn-ghost { color: var(--olive); }

.area-towns { font-size: 0.9rem; color: var(--ink-faint); }

/* "Wat we maken" grid on the how-we-make-it page */
.make { margin-top: 2.5rem; }
.make > .lede { margin: 0 0 1.2rem; }
/* The dish index, used by "Onze gerechten" on /keuken and "Wat we maken" on
   /zo-maken-we-het.

   Twenty-four dishes cannot be twenty-four boxes; that is a wall whatever shape
   the box is. Nor can they be twenty-four large photographs: measured at 137px
   the section ran 1822px, and a page of round black bowls is its own monotony,
   because at that size the dips all look alike.

   So the name is the unit and the photograph is demoted to a bullet. Same
   section, 946px. What you scan is the word you came looking for, and the
   picture is there to give the list texture and to confirm you found the right
   thing. It is the kaart's own row (round thumb, serif name) at index scale, so
   nobody has to learn a second pattern.

   auto-fill with a 200px floor reflows from three columns to one without a
   breakpoint, which also keeps Turkish dish names from being squeezed. */
.dish-tray {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr));
  gap: 0.1rem clamp(1rem, 3vw, 2rem);
  align-items: start;
}
.dish-chip {
  display: flex; align-items: center; gap: 0.7rem;
  padding: 0.45rem 0; text-decoration: none; min-width: 0;
}
.dish-chip picture { display: block; width: 44px; flex: none; }
.dish-chip img {
  /* height:auto is required, not decorative: the width/height attributes on the
     img are presentational hints that aspect-ratio does not override, so without
     it every thumbnail renders as a 240px-tall oval. */
  display: block; width: 100%; height: auto; aspect-ratio: 1; object-fit: cover;
  border-radius: 50%; background: var(--paper-2); box-shadow: var(--shadow-thumb);
  transition: transform 260ms var(--ease-out);
}
.dish-chip-n {
  font-family: var(--serif); font-size: 1.05rem; line-height: 1.3;
  color: var(--ink); min-width: 0; overflow-wrap: break-word; hyphens: auto;
  transition: color 200ms var(--ease-out);
}
.dish-chip:hover img, .dish-chip:focus-visible img { transform: scale(1.06); }
.dish-chip:hover .dish-chip-n, .dish-chip:focus-visible .dish-chip-n { color: var(--olive); }
/* .prose underlines and recolours every link; these carry their own treatment. */
.prose a.dish-chip, .prose a.dish-chip:hover { text-decoration: none; color: inherit; }
/* On a phone the auto-fill floor resolves to a single column, which turned a
   24-dish index into a very long scroll. Two fixed columns instead: the thumb
   and the name both step down a little so the longer names (Gemarineerde
   olijven, Rode bieten salade) still get a sensible measure, and a category
   fits on screen instead of running past it. */
@media (max-width: 560px) {
  .dish-tray { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 0.9rem; }
  .dish-chip { gap: 0.55rem; padding: 0.4rem 0; }
  .dish-chip picture { width: 36px; }
  .dish-chip-n { font-size: 0.97rem; line-height: 1.25; }
}

@media (prefers-reduced-motion: reduce) {
  .dish-chip img { transition: none; }
  .dish-chip:hover img, .dish-chip:focus-visible img { transform: none; }
}

/* ============================================================
   /keuken content hub
   ============================================================ */
.crumbs { font-size: 0.85rem; color: var(--ink-faint); margin-bottom: 0.9rem; }
/* Padding, not line-height: an inline box grows its hit area without moving
   the line, which keeps the crumb trail on one row and clears the 24px floor. */
.crumbs a { color: var(--ink-faint); text-decoration: none; padding-block: 0.35rem; }
.crumbs a:hover { color: var(--olive); text-decoration: underline; text-underline-offset: 2px; }

.keuken-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; margin-top: 2rem; }
.keuken-card {
  display: flex; flex-direction: column; gap: 0.5rem;
  padding: 1.35rem 1.5rem; border: 1px solid var(--line); border-radius: var(--r);
  background: var(--paper-raised); text-decoration: none;
  transition: border-color 200ms var(--ease-out);
}
.keuken-card:hover { border-color: var(--olive); }
.keuken-card h2 { font-family: var(--serif); font-weight: 600; color: var(--ink); font-size: 1.3rem; margin: 0; transition: color 200ms var(--ease-out); }
.keuken-card:hover h2 { color: var(--olive); }
.keuken-card p { margin: 0; color: var(--ink-soft); line-height: 1.6; font-size: 0.94rem; }

.keuken-products { margin-top: 2.6rem; }
.keuken-products h2 {
  font-family: var(--serif); font-weight: 600; color: var(--ink);
  font-size: clamp(1.5rem, 1.2rem + 1vw, 1.94rem); line-height: 1.2; margin: 0;
}
.keuken-products > .lede { margin: 0.3rem 0 1.1rem; }
/* 25 dishes in one flat row of chips reads as a wall, so they are grouped under
   the same category headings the kaart uses. */
.keuken-product-group + .keuken-product-group { margin-top: 2rem; }
.keuken-product-cat {
  font-family: var(--serif); font-size: 1.05rem; font-weight: 600; color: var(--ink);
  margin: 0 0 0.6rem;
}
/* The dishes here use the same card as "Wat we maken" on /zo-maken-we-het.
   They used to be narrow pills, which read as tags rather than as somewhere to
   go, and gave the name nowhere to sit next to its kicker. */

/* ============================================================
   Footer
   ============================================================ */
.foot { background: var(--foot-bg); color: var(--foot-text); font-size: 0.875rem; padding-block: clamp(3rem, 5vw, 4rem) 0; }
.foot-top { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: clamp(1.8rem, 4vw, 3rem); }
.foot-brand { display: flex; flex-direction: column; gap: 0.75rem; }
.foot-mark { font-family: var(--serif); font-size: 1.625rem; font-weight: 600; color: var(--paper); text-decoration: none; align-self: start; line-height: 1; }
.foot-blurb { color: var(--foot-text); font-size: 0.875rem; line-height: 1.7; margin: 0; max-width: 30ch; }
.foot-cols { display: contents; }
.foot-col { display: flex; flex-direction: column; align-items: start; gap: 0.4rem; min-width: 0; }
.foot-col-h { font-size: 0.75rem; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase; color: var(--foot-muted); margin-bottom: 0.25rem; }
.foot-col a { color: var(--foot-text); text-decoration: none; display: inline-flex; align-items: center; min-height: 1.75rem; gap: 0.45rem; transition: color 180ms var(--ease-out); }
.foot-col a:hover { color: var(--paper); }
.foot-col svg { width: 1rem; height: 1rem; flex: none; }
.foot-bottom {
  margin-top: clamp(2.25rem, 4vw, 3rem); padding-top: 1.25rem; padding-bottom: 2rem;
  border-top: 1px solid var(--foot-line);
  display: flex; flex-wrap: wrap; gap: 0.4rem 0.6rem; align-items: center;
  font-size: 0.8125rem; color: var(--foot-muted);
}
.foot-bottom .foot-sep { opacity: 0.7; }
.foot-sign { font-family: var(--serif); font-style: italic; font-size: 0.94rem; }
@media (max-width: 860px) {
  .foot-top { grid-template-columns: 1fr 1fr; }
  .foot-brand { grid-column: 1 / -1; }
}
@media (max-width: 460px) {
  .foot-top { grid-template-columns: 1fr; gap: 1.75rem; }
  .foot-bottom { justify-content: flex-start; }
}

/* ============================================================
   IMPRESSIE — self-hosted clips between the story band and the olive CTA
   ============================================================ */
/* ── Impressie ──────────────────────────────────────────────────────────────
   Self-hosted clips between "Onze sofra" (band) and the olive Catering CTA.
   Linen ground, so the band rhythm reads band -> linen -> olive.

   One clip plays at a time and it plays to its end; when it finishes the turn
   passes to the next tile. So the rhythm is the footage's own, not a timer's,
   and the rule under the playing tile is the actual playhead rather than a
   countdown pretending to be one.

   Nothing changes position: the switch is the picture waking up, not a slide.
   That keeps it off the carousel path (arrows, dots, a rail that jumps) and
   keeps exactly one video decoding, which is what a phone can afford.

   The tiles are photographs laid on the cloth, not cards: no box, no padding,
   no border. Same 24px radius and warm shadow as the story photo. */

.impressie { background: var(--paper); }

.impressie-wrap {
  display: grid;
  grid-template-columns: minmax(13rem, 15rem) 1fr;
  gap: clamp(1.75rem, 3vw, 2.75rem);
  align-items: start;
}

/* ---- left column ---- */

.impressie-copy { position: relative; padding-top: 0.35rem; }
.impressie-copy .display { margin: 0.85rem 0 1.15rem; }

.impressie-lede {
  color: var(--ink-soft);
  line-height: 1.75;
  margin: 0;
  max-width: 34ch;
}

/* The owner's handwriting, same job it does on the story photo. */
.impressie-aside {
  display: inline-block;
  margin-top: 1.5rem;
  font-family: var(--script);
  font-weight: 600;
  font-size: 1.5rem;
  line-height: 1.1;
  color: var(--gold-ink);
  transform: rotate(-3deg);
  transform-origin: left center;
}

/* ---- the strip ---- */

.impressie-strip {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* --tiles comes from the clip count, capped at 4 by the build. */
  grid-template-columns: repeat(var(--tiles, 4), minmax(0, 1fr));
  gap: clamp(0.6rem, 1.4vw, 1.15rem);
}

.impressie-tile { min-width: 0; }
.impressie-fig { margin: 0; }

.impressie-frame {
  position: relative;
  display: block;
  aspect-ratio: 9 / 16;
  border-radius: 24px;
  overflow: hidden;
  background: var(--paper-2);
  box-shadow: var(--shadow-photo);
  transition: opacity 460ms var(--ease-out), filter 460ms var(--ease-out),
              box-shadow 460ms var(--ease-out);
}

/* Everything below is the sequencing dress, and it only applies once the
   script has actually taken over (it adds .is-sequencing). Without that gate
   the dimmed idle state IS the default, so every route that declines to play
   (no JS, reduced motion, Save-Data, 2g) would render the whole strip
   permanently greyed and looking broken. Posters at full strength is the
   correct resting state; stepping back is the exception. */
.impressie-strip.is-sequencing .impressie-frame {
  opacity: 0.62;
  filter: saturate(0.72);
  box-shadow: var(--shadow-soft);
}

.impressie-poster,
.impressie-vid {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.impressie-vid {
  opacity: 0;
  transition: opacity 520ms var(--ease-out);
}

.impressie-strip.is-sequencing .impressie-tile.is-live .impressie-frame {
  opacity: 1;
  filter: none;
  box-shadow: var(--shadow-photo);
}
.impressie-tile.is-live .impressie-vid { opacity: 1; }

/* Hovering a tile hands it the turn and plays it from the top. It is the whole
   steering mechanism: no arrows, no dots, nothing to click. */
@media (hover: hover) {
  .impressie-strip.is-sequencing .impressie-fig:hover .impressie-frame {
    opacity: 1;
    filter: none;
  }
  /* Tapping or clicking a tile hands it the turn, so say so. Gated on
     .is-sequencing like everything else here: the player binds that click only
     after it commits to playing, and the routes that decline (reduced motion,
     Save-Data, 2g) never add the class, so the cursor never promises something
     the picture will not do. */
  .impressie-strip.is-sequencing .impressie-fig { cursor: pointer; }
}

/* The playhead, on the linen under the photo rather than over it: a 2px rule
   that tracks the clip's own progress. It says the strip is working through
   something, so the switch reads as rhythm rather than a twitch.
   scaleX, never width. */
.impressie-rule {
  display: none;
  height: 2px;
  margin-top: 0.6rem;
  border-radius: 1px;
  background: var(--line);
  overflow: hidden;
}
/* An empty track under every tile means nothing when there is no playhead to
   put in it, so it only exists while the strip is sequencing. */
.impressie-strip.is-sequencing .impressie-rule { display: block; }

.impressie-tick {
  display: block;
  height: 100%;
  background: var(--olive);
  transform: scaleX(0);
  transform-origin: left center;
}

/* ---- caption ---- */

.impressie-name {
  display: block;
  padding: 0.5rem 0.1rem 0;
  font-family: var(--serif);
  font-weight: 600;
  font-size: 1.0625rem;
  line-height: 1.25;
  color: var(--ink);
}

/* ---- narrow ---- */

@media (max-width: 900px) {
  .impressie-wrap { grid-template-columns: 1fr; gap: 1.75rem; }
  .impressie-lede { max-width: 46ch; }
  .impressie-aside { margin-top: 1.1rem; }

  /* A row of four 9:16 tiles has nowhere to go on a phone, so the strip
     becomes something to thumb through. It bleeds into the gutter on both
     sides so the next tile is visibly cut off: that is the affordance. */
  .impressie-strip {
    display: flex;
    gap: 0.75rem;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    margin-inline: calc(-1 * var(--gutter));
    padding-inline: var(--gutter);
    /* Without this, "snap to start" aligns a tile to the container's border
       edge and swallows the leading gutter, so the first tile sits clipped
       against the screen edge. */
    scroll-padding-inline: var(--gutter);
    padding-bottom: 0.25rem;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .impressie-strip::-webkit-scrollbar { display: none; }

  .impressie-tile {
    flex: 0 0 min(56vw, 15rem);
    scroll-snap-align: start;
  }
  .impressie-frame { border-radius: 18px; }
}

/* ---- reduced motion ---- */

@media (prefers-reduced-motion: reduce) {
  /* The script already declines to sequence here, so the posters are the
     section on their own. This just makes sure of it in CSS too. */
  .impressie-strip .impressie-frame {
    opacity: 1;
    filter: none;
    box-shadow: var(--shadow-photo);
    transition: none;
  }
  .impressie-vid { display: none; }
  .impressie-strip .impressie-rule { display: none; }
}

/* ---------- gastenboek: guest reactions ----------

   One row, three homes: above a dish's order block, on /gastenboek, and (as a
   pull-quote) inside the catering band. The row IS the kaart row: 44px round
   thumb, name, figure on the right, dotted rule between. Nothing new to learn,
   and it looks finished at one reaction as well as at forty, which a grid of
   equal cards does not.

   The rating figure is Cormorant. This stylesheet gives the serif every heading
   and every price figure, and a rating is a figure. */

.prod-reviews { margin-top: 2.6rem; }

/* A section label, not a title: the dish already has the H1, and this block
   should read as an aside to it. Overrides .prose h2 on purpose. */
.prose .rv-h,
.rv-h {
  font-family: var(--sans);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold-ink);
  margin: 0 0 0.9rem;
  line-height: 1.4;
}

.prose .rv-list,
.rv-list { list-style: none; margin: 0; padding: 0; }

.rv-item {
  display: grid;
  grid-template-columns: 44px 1fr auto;
  gap: 0.95rem;
  align-items: start;
  padding: 1.05rem 0;
  border-bottom: 1px dotted var(--leader);
  margin: 0;
}
.rv-item:last-child { border-bottom: 0; }

.rv-thumb { width: 44px; height: 44px; flex: none; }
.rv-thumb picture { display: block; }
.rv-thumb img {
  width: 44px;
  /* height:auto would lose to the width/height attributes, which aspect-ratio
     does not override, and the circle would render as an oval. See DESIGN.md. */
  height: 44px;
  border-radius: 50%;
  object-fit: cover;
  box-shadow: var(--shadow-thumb);
}
/* Catering has no dish to photograph. The initial keeps the round slot rather
   than leaving a hole or borrowing an unrelated dish photo. */
.rv-thumb-initial {
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--olive-tint);
  color: var(--olive-deep);
  font-family: var(--serif);
  font-weight: 600;
  font-size: 1.3rem;
  line-height: 1;
  box-shadow: var(--shadow-thumb);
}

.rv-body { min-width: 0; }
.prose .rv-q,
.rv-q { margin: 0 0 0.35rem; color: var(--ink); line-height: 1.6; max-width: 60ch; }
.prose .rv-by,
.rv-by {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.55rem;
  font-size: 0.83rem;
  color: var(--ink-faint);
}
.rv-name { font-weight: 700; color: var(--ink-soft); }
.rv-dish { font-weight: 700; }

/* Occasion and headcount: the two facts a catering visitor is actually asking
   about, so they are chips rather than a sentence. */
.rv-occ { display: inline-flex; flex-wrap: wrap; gap: 0.3rem; }
.rv-occ > span {
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--olive-deep);
  background: var(--olive-tint);
  border-radius: var(--r-pill);
  padding: 0.16rem 0.55rem;
  font-variant-numeric: tabular-nums;
}

.prose .rv-meta,
.rv-meta { margin: 0; text-align: right; padding-top: 0.15rem; }
.rv-stars { display: block; margin-left: auto; }
.rv-on { fill: var(--olive); }
.rv-off { fill: var(--line-strong); }
.rv-when { display: block; font-size: 0.75rem; color: var(--ink-faint); margin-top: 0.3rem; }

/* The average, in the same serif as the prices. */
.prose .rv-agg,
.rv-agg {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.55rem 0.7rem;
  margin: 1.1rem 0 0;
  font-size: 0.88rem;
  color: var(--ink-faint);
}
.rv-agg .rv-stars { margin-left: 0; }
.rv-fig {
  font-family: var(--serif);
  font-weight: 600;
  font-size: 1.35rem;
  line-height: 1;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.rv-count { font-variant-numeric: tabular-nums; }
.rv-more { font-weight: 700; }

.guestbook .rv-agg { margin-bottom: 1.4rem; }
.rv-empty { color: var(--ink-faint); }

/* One voice inside the olive catering band. Olive disappears on that ground,
   so the stars take the gold the band already uses for its own voice. */
.catering-quote {
  margin: 1.9rem 0 0;
  padding-top: 1.4rem;
  border-top: 1px solid rgba(250, 245, 236, 0.22);
  max-width: 46ch;
}
.catering-quote blockquote {
  margin: 0 0 0.7rem;
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(1.15rem, 1rem + 0.6vw, 1.4rem);
  line-height: 1.42;
  color: #F7F2E5;
}
.catering-quote blockquote::before { content: "\201C"; }
.catering-quote blockquote::after { content: "\201D"; }
.catering-quote figcaption {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.6rem;
  font-size: 0.83rem;
  color: var(--cta-text);
}
.catering-quote .rv-name { color: #FAF5EC; }
.catering-quote .rv-occ > span { background: rgba(250, 245, 236, 0.14); color: #EFEADA; }
/* No star row here on purpose. One voice reads as a recommendation; a rating
   next to it reads as a scorecard, and a catering rating cannot become a rich
   result anyway. The stars stay in the list, where they are a comparison. */

/* Phone: the rating drops under the quote rather than squeezing the text into a
   column two words wide, exactly as the dish index drops to two columns. */
@media (max-width: 600px) {
  .rv-item { grid-template-columns: 36px 1fr; gap: 0.7rem; }
  .rv-thumb, .rv-thumb img { width: 36px; height: 36px; }
  .rv-thumb-initial { font-size: 1.1rem; }
  .rv-meta { grid-column: 2; text-align: left; padding-top: 0.15rem; }
  .rv-stars { margin-left: 0; }
  .rv-when { display: inline; margin-left: 0.5rem; }
}

/* ============================================================
   /catering
   ============================================================ */
.cater-cta { margin-top: 1.6rem; }
.prose .cater-cta a, .prose .cater-form .btn { text-decoration: none; }
.prose .cater-cta .btn-primary, .prose .cater-form .btn-primary { color: var(--paper); }

/* An example table is a kaart row grown up: the name on the left, the figure
   on the right in the serif, a dotted rule underneath, then what is on it. */
.cater-tables { display: grid; gap: 1.6rem; margin-top: 1.7rem; }
.cater-table {
  background: var(--paper-raised); border: 1px solid var(--line); border-radius: var(--r);
  padding: 1.35rem 1.6rem 1.2rem;
}
.cater-table-head {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.3rem 1.25rem;
  padding-bottom: 0.85rem; border-bottom: 1px dotted var(--leader);
}
.prose .cater-table-name {
  font-family: var(--serif); font-weight: 600; color: var(--ink); margin: 0;
  font-size: clamp(1.3rem, 1.15rem + 0.6vw, 1.55rem); line-height: 1.2;
}
.cater-table-people {
  font-family: var(--sans); font-size: 0.8125rem; font-weight: 700; color: var(--ink-faint);
  margin-left: 0.4rem; white-space: nowrap;
}
.prose .cater-table-fig { margin: 0; font-size: 0.875rem; color: var(--ink-soft); white-space: nowrap; }
.cater-table-fig strong { font-family: var(--serif); font-weight: 600; font-size: 1.5rem; color: var(--olive-deep); }
.prose .cater-dishes {
  list-style: none; padding: 0; margin: 0.95rem 0 0; max-width: none;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: 0.1rem 1.5rem;
}
.prose .cater-dishes li { margin: 0; line-height: 1.7; font-size: 0.94rem; }
.prose .cater-table-sum { margin: 0.85rem 0 0; font-size: 0.8125rem; color: var(--ink-faint); text-align: right; max-width: none; }
.prose .cater-note { font-size: 0.875rem; color: var(--ink-faint); margin-top: 0.9rem; }

.cater-occ { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 1.3rem 2rem; margin-top: 1.4rem; }
.prose .cater-occ h3 { margin-top: 0; }
.prose .cater-occ p { margin-top: 0.3rem; }
.step h3 { font-family: var(--sans); font-weight: 700; color: var(--ink); font-size: 1.05rem; margin: 0.2rem 0; }

.cater-voices { margin-top: 2.6rem; }
.cater-faq { margin-top: 1.2rem; }

.cater-form { margin-top: 1.2rem; max-width: 44rem; }
.cater-form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: 0 1.25rem; }
.cater-form .field:last-child { padding-bottom: 0; }
.cater-form .btn { margin-top: 1.4rem; }
.cater-form .form-alt { text-align: left; margin-top: 1rem; }
.cater-form .form-msg { margin: 1rem 0 0; }
.prose .cater-more { margin-top: 2.4rem; }

/* The homepage band's way into the page above. */
.catering .catering-more { margin: 1.1rem 0 0; }
.catering-more a { color: var(--cta-text); font-weight: 700; text-decoration: underline; text-underline-offset: 3px; }
.catering-more a:hover { color: var(--paper); }

/* The narrowest phones (320px). Found by the overflow audit of 2026-10-14:
   the header ran 3px wide on the home page, and the catering tables 8px. */
@media (max-width: 340px) {
  .nav .wrap { gap: 0.4rem; }
  .nav-mobile { gap: 0.25rem; }
  .nav-cta-m { padding: 0.45rem 0.65rem; }
  .cater-table { padding: 1.1rem 1rem 1rem; }
  .prose .cater-table-fig, .cater-table-people { white-space: normal; }
  .btn-lg { padding-left: 1.25rem; padding-right: 1.25rem; }
}
