/* Public site styles — consume tokens.css. No colours/sizes hardcoded here that
   belong in tokens. Photo-resilient, type-led, calm. */

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: var(--sans);
    font-size: 1rem;
    line-height: 1.65;
    transition: background var(--t), color var(--t);
}
/* `overflow-wrap: break-word` is the structural guard, and German is why it is not optional.
   Measured at 375px: <h1>Datenschutzerklärung</h1> is one unbreakable 20-character token that
   laid out 360px wide inside a 316px column, and the page gained a horizontal scrollbar —
   document.body.scrollWidth 382 against an innerWidth of 375. It will happen again with
   Lebensmittelinformationsverordnung, with Öffnungszeiten-Änderung, and with half the
   restaurant names on this platform. Breaking a word is ugly; a page that scrolls sideways on
   a phone is broken. */
h1, h2, h3, h4 { font-family: var(--serif); font-weight: 600; line-height: 1.12; margin: 0 0 .5em; letter-spacing: -.01em; overflow-wrap: break-word; }
p { margin: 0 0 1em; }
/* A postal address is the correct element for a postal address, and every browser sets it in
   italic — which on a street address next to a heading reads as an aside rather than as the
   thing the band exists to tell you. Reset once here rather than fought per page. */
address { font-style: normal; margin: 0 0 1em; }
a { color: var(--link); text-underline-offset: 3px; }
img { max-width: 100%; display: block; }
/* THE ALLERGEN FILTER SILENTLY DID NOTHING, and this one line is why.

   `hidden` is an attribute whose whole effect is `[hidden] { display: none }` in the BROWSER'S
   sheet, and any author `display` at all outranks a UA sheet. menu.js hides a dish with
   `el.hidden = true`; `.mdish` two hundred lines below says `display: flex`. Measured on a real
   card (118 dishes) by clicking the "G · Milch" box the way a guest does: 10 dishes took
   `hidden="true"`, 10 kept `display: flex` and 186px of height, and not one of them left the
   screen. Only whole categories vanished — `.mcat` happens to declare no display — which is
   what made it look half-working rather than broken.

   That is a guest with a milk allergy reading a card that tells them it has filtered itself.

   `!important` rather than source order: a plain `[hidden]` rule (0,1,0) still loses to any
   two-class selector, and this sheet has several (`.dishcard__photo.ph-tile`). The attribute
   means "not relevant in any presentation" — there is no cascade argument to have with it.
   `img { display: block }` one line up is the same trap waiting for any hidden image. */
[hidden] { display: none !important; }
/*
 * TWO RINGS, BECAUSE ONE GROUND IS NOT ENOUGH.
 *
 * The ring was the restaurant's own accent, and inside `.hero--foto` — a near-black plate — that
 * measured 1.73:1. Every focusable element on the first screen, including both call-to-action
 * buttons and the submit button on the booking page, had an indicator a keyboard user could not
 * see. The outer white ring is what makes it work on any ground: the inner line carries the
 * brand, the outer one guarantees it is visible against whatever is behind.
 */
:focus-visible {
    outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 3px;
    box-shadow: 0 0 0 5px color-mix(in oklab, var(--on-dark) 62%, transparent);
}
.hero--foto :focus-visible, .menu-hero :focus-visible,
.cater-hero :focus-visible, .gal-hero :focus-visible, .site-footer :focus-visible {
    outline-color: var(--on-dark);
    box-shadow: 0 0 0 5px color-mix(in oklab, #000 55%, transparent);
}

.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 22px; }
.wrap--narrow { max-width: var(--wrap-narrow); }
/* ⛔ `left: -999px` WAR EIN VERSTECK, DAS VOM ELTERNTEIL ABHING — und genau daran ist es zerbrochen.
 *
 *    Ein absolut positioniertes Element rechnet gegen den nächsten POSITIONIERTEN Vorfahren, nicht
 *    gegen die Seite. Sobald `.skip` in einem Knopf mit `position: relative` sass, hiess -999px
 *    nicht mehr «weit links ausserhalb», sondern «999px links von diesem Knopf» — und das ist auf
 *    einem 1440px-Schirm mitten in der Kopfzeile. Gemessen: der Text «Mein Konto» stand bei x=201
 *    quer über dem Namen des Hauses, der bei x=202 beginnt.
 *
 *    Die Zuschneide-Technik hat keinen Bezugspunkt: 1px gross, weggeschnitten, egal worin sie
 *    steht. Damit kann dieselbe Falle nicht wieder zuschnappen — und `.nav__social`,
 *    `.footer-social` und die Ladenkopf-Zeile benutzen denselben Helfer.
 *
 * ⚠️ `position: fixed` IM FOKUS, aus demselben Grund: der Sprunglink soll oben links am
 *    BILDSCHIRM erscheinen, nicht oben links in irgendeinem Kasten. */
.skip {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.skip:focus {
    position: fixed;
    left: 12px;
    top: 12px;
    width: auto;
    height: auto;
    margin: 0;
    padding: 10px 14px;
    overflow: visible;
    clip-path: none;
    background: var(--surface);
    color: var(--text);
    border-radius: 8px;
    z-index: 100;
}

/* Optional second-voice label (Greek, Turkish, ...) — see data/content.php */
/*
 * ⛔ `--gold-ink` UND NICHT `--gold`, UND DAS IST EIN GEMESSENER FEHLER GEWESEN. Das Beiwort ist
 *    KLEINER TEXT auf Papier, und `--gold` trägt dort: Abend 3,33:1 · Tafel 2,87:1 · Markt
 *    5,00:1. WCAG verlangt 4,5:1 — zwei von drei Themes lagen darunter, Tafel deutlich. In der
 *    Praxis heisst das: ein älterer Gast oder ein Telefon in der Sonne liest das Wort nicht,
 *    das die Küche als ihre Herkunft nennt.
 *
 *    `--gold-ink` ist genau dafür da (`tokens.css:104-122`, dieselbe Farbe 72 % in oklab gegen
 *    ein dunkles Braun gemischt) und trägt 5,33:1 · 4,81:1 · 7,32:1 — nachgerechnet, nicht
 *    übernommen. Die Datei benutzte es bisher an einer einzigen Stelle im Grundblatt
 *    (`.badge--halal`) und sonst nur im Kontor-Block; der Aufbau, den heute JEDES live
 *    geschaltete Haus fährt, bekam die Korrektur nicht.
 *
 * ⚠️ DIE LINIEN BEHALTEN `--gold`. Für eine Regel, eine Zierlinie oder ein Icon reichen 3:1, und
 *    der metallische Ton IST die Zierde — ihn überall dunkler zu machen hiesse, den Charakter
 *    gegen eine Schwelle einzutauschen, die dort gar nicht gilt.
 *
 * ⚠️ UND JEDER DUNKLE GRUND BEHÄLT IHN AUCH. Wo das Beiwort auf einer Fotoplatte steht —
 *    `.hero--foto`, `.menu-hero`, `.cater-hero`, `.gal-hero`, der Fuss, `.cater-shot__eyebrow`
 *    über seinem Verlauf — deklarieren die Regeln dort ihre eigene Farbe, und helles Gold auf
 *    Dunkel ist genau richtig. Nachgesehen, Stelle für Stelle, statt gesucht und ersetzt.
 */
.accent-script { font-family: var(--serif); color: var(--gold-ink); font-size: .95em; letter-spacing: .02em; }
.eyebrow { text-transform: uppercase; letter-spacing: .22em; font-size: .72rem; color: var(--muted); font-family: var(--sans); font-weight: 600; }

/* THE TWO TEXT UTILITIES THE GUEST PAGES ACTUALLY ASK FOR, and the only two rules in this
   commit that are new names rather than existing ones.

   `.muted` was written in four templates and defined in exactly one place — scoped under
   `.m-allergen__body`, on the Speisekarte. Everywhere else (the 404's "zurück zur Startseite",
   the vegan page's allergen footnote, the booking receipt's "save this link") it was body text
   at body weight, so the aside and the sentence above it read as equals. Promoted here; the
   scoped rule keeps only its own size override.

   `.legal-missing` marks an Impressum or Datenschutz field the restaurant has not filled in.
   It has to be visible enough that the owner notices on their own site and calm enough that a
   guest does not read it as an error — an incomplete Impressum is the most commonly abgemahnt
   thing on a German small-business website, and "it looked fine" is how it stays that way for
   two years. Brass rule, muted italic: a margin note, not a red banner. */
.muted { color: var(--muted); }
.legal-missing {
    color: var(--muted); font-style: italic;
    border-left: 3px solid color-mix(in srgb, var(--gold) 60%, transparent);
    padding: 2px 0 2px 14px;
}

/* ---------- Standortband ----------
   Nur bei einem Haus mit mehr als einem Standort. Es steht ÜBER dem Header, weil die erste
   Frage eines Gastes «welches Haus ist das hier?» ist und sie beantwortet sein muss, bevor er
   eine Telefonnummer oder Öffnungszeiten liest — sonst ruft er die falsche Filiale an.

   ⛔ NICHT `sticky`. Der Header ist bereits sticky (z-index 40), und die Speisekarte hat
      zusätzlich eine klebende Kategorieleiste; ein drittes klebendes Element hätte auf einem
      375px-Telefon rund 100px Bildschirm gefressen, bevor ein einziges Gericht zu sehen ist.
      Das Band scrollt weg — es beantwortet eine Frage einmal, es begleitet nicht.

   Die Schrift ist klein, die Trefferfläche nicht: 44px Mindesthöhe, weil das auf einem Telefon
   ein echter Umschalter ist und keine Fußnote. */
.standorte {
    background: var(--plate);
    border-bottom: 1px solid var(--line-soft);
    font-size: .78rem;
    font-family: var(--sans);
}
.standorte__bar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.standorte__label {
    text-transform: uppercase; letter-spacing: .18em; font-size: .68rem;
    color: var(--muted); font-weight: 600; flex: 0 0 auto;
}
.standorte__list { display: flex; align-items: center; gap: 4px; list-style: none; margin: 0; padding: 0; flex-wrap: wrap; }
.standorte__list a,
.standorte__here {
    display: inline-flex; align-items: center; min-height: 44px;
    padding: 0 10px; border-radius: 8px; text-decoration: none; letter-spacing: .01em;
}
/* Der aktuelle Standort ist KEIN Link und sieht auch nicht wie einer aus — ein Link auf die
   Seite, auf der man gerade steht, ist die häufigste kleine Lüge in Navigationen. */
.standorte__here { color: var(--text); font-weight: 600; background: var(--surface); box-shadow: var(--shadow-sm); }
.standorte__list a { color: var(--muted); }
.standorte__list a:hover { color: var(--text); background: var(--surface); }
.standorte__list a:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* ---------- Standortwahl beim ersten Besuch ----------

   ⛔ KEIN OVERLAY, KEIN `position: fixed`, KEIN VERDUNKELTER HINTERGRUND. Das sähe nach Modal
      aus und wäre eines — und ein Modal ohne JavaScript lässt sich nicht schliessen. Dies ist
      ein Abschnitt oben auf der Startseite: er schiebt den Inhalt nach unten statt ihn zu
      verdecken, jede Auswahl ist ein Link, und wer gar nichts anklickt, scrollt einfach vorbei
      und sieht die ganze Seite. Genau das ist bei einem Fenster, das man wegklicken MUSS, nicht
      möglich — und der Gast steht dabei manchmal mit einem Balken Empfang vor der Tür.

   Es trägt die Markenfarbe des Hauses, weil es das Erste ist, was ein Gast sieht. */
.standortwahl {
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--accent) 10%, var(--bg)) 0%,
        var(--bg) 100%);
    border-bottom: 1px solid var(--line-soft);
    padding: 28px 0 24px;
}
.standortwahl__box { max-width: 760px; }
.standortwahl__frage {
    font-family: var(--serif); font-size: clamp(1.5rem, 4vw, 2.1rem);
    margin: 0 0 6px; color: var(--text); line-height: 1.15;
}
.standortwahl__hinweis { color: var(--muted); font-size: .95rem; margin: 0 0 20px; max-width: 46ch; }

/* ZWEI GLEICH BREITE KARTEN, die auf dem Telefon untereinander rutschen. `1fr` statt fester
   Breiten: die Namen sind verschieden lang («Nord» und «Fallersleben»), und zwei verschieden
   breite Karten sähen aus, als sei eine wichtiger. */
.standortwahl__liste {
    list-style: none; margin: 0 0 18px; padding: 0;
    display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 12px;
}
.standortwahl__eintrag { display: flex; }
.standortwahl__ort {
    display: flex; flex-direction: column; gap: 6px; flex: 1;
    /* Grosszügig, weil dies der erste Klick auf der Website ist und mit dem Daumen kommt. */
    min-height: 96px; padding: 16px 18px;
    border: 1px solid var(--line); border-radius: 14px;
    background: var(--surface); text-decoration: none; color: var(--text);
    box-shadow: var(--shadow-sm);
    transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}
.standortwahl__ort:hover {
    border-color: var(--accent);
    box-shadow: var(--shadow-md, var(--shadow-sm));
    transform: translateY(-1px);
}
.standortwahl__ort:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
/* Der Standort, auf dem der Gast gerade steht, ist markiert — aber NICHT ausgegraut: er ist
   eine gültige Wahl, und wer ihn anklickt, hat sich entschieden. */
.standortwahl__ort--hier { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 5%, var(--surface)); }
.standortwahl__kopf { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.standortwahl__name { font-family: var(--serif); font-size: 1.2rem; font-weight: 600; }
.standortwahl__hier {
    font-size: .68rem; text-transform: uppercase; letter-spacing: .12em; font-weight: 600;
    color: var(--accent-ink);
}
.standortwahl__adresse { color: var(--muted); font-size: .88rem; line-height: 1.35; }
/* Zustand: Wort UND Punkt. Der Punkt allein wäre für eine Rot-Grün-Schwäche keine Auskunft. */
.standortwahl__zustand {
    display: inline-flex; align-items: center; gap: 6px; margin-top: auto;
    font-size: .78rem; color: var(--muted);
}
.standortwahl__punkt {
    width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: 0 0 auto;
}
.standortwahl__zustand--offen { color: var(--enamel, #2f7d4f); font-weight: 600; }
/* Der Ausweg ist ein Link und kein Knopf: er ist die leisere der beiden Möglichkeiten und
   soll auch so aussehen. Trotzdem 44 px hoch, weil er auf einem Telefon getroffen werden muss. */
.standortwahl__spaeter {
    display: inline-flex; align-items: center; min-height: 44px;
    color: var(--muted); font-size: .88rem; text-decoration: underline;
    text-underline-offset: 3px;
}
.standortwahl__spaeter:hover { color: var(--text); }
.standortwahl__spaeter:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* DIE MARKENTÜR — `ghien.de`, bevor der Gast weiss, in welches Haus er will.

   Sie benutzt dieselbe `.standortwahl` wie die Filialseite und stellt sie nur anders auf: dort
   ist die Frage ein Abschnitt OBEN AUF einer Seite, hier ist sie die ganze Seite. Deshalb keine
   eigene Stilsprache, sondern drei Regeln, die den Rahmen wegnehmen, den es hier nicht braucht.

   ⚠️ `min-height: 100svh` UND NICHT `100vh`. Auf einem Telefon zählt `vh` die Adressleiste mit,
      die beim Scrollen verschwindet — die Seite wäre bei jedem Aufruf ein Stück zu hoch und
      liesse sich scrollen, obwohl es nichts zu scrollen gibt. */
.page-markentuer {
    min-height: 100svh;
    display: flex; flex-direction: column;
    background: var(--bg); color: var(--text);
}
.page-markentuer #main { flex: 1; display: flex; flex-direction: column; justify-content: center; }

/* Kein Trennstrich nach unten: darunter kommt nichts mehr, was abgegrenzt werden müsste. */
.page-markentuer .standortwahl { border-bottom: 0; padding: 32px 0 28px; }

/* Das Zeichen der MARKE, nicht der Name der Filiale — die Begründung steht im Kopf von
   `markentuer.blade.php`. Höhe fest, Breite dem Bild folgend, wie im Seitenkopf. */
.markentuer__zeichen { text-align: center; padding: 40px 20px 0; }
.markentuer__zeichen img { height: 64px; width: auto; }

.markentuer__fuss {
    padding: 24px 0 32px; text-align: center;
    color: var(--muted); font-size: .85rem;
}
/* 44 px, weil auch ein Pflichtlink auf einem Telefon getroffen werden muss. */
.markentuer__fuss a {
    display: inline-flex; align-items: center; min-height: 44px; padding: 0 10px;
    color: var(--muted); text-decoration: underline; text-underline-offset: 3px;
}
.markentuer__fuss a:hover { color: var(--text); }
.markentuer__fuss a:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/*
 * ⛔ AUF DEM TELEFON NAHM DIESE FRAGE DEN HALBEN ERSTEN BILDSCHIRM. Gemessen bei einem Haus
 *    mit zwei Filialen, 390 × 844: rund 370 px für eine Frage mit zwei Antworten — und darunter
 *    beginnt der Aufmacher, dessen Inhalt am unteren Rand sitzt. Zusammen war der erste
 *    Bildschirm vollständig belegt, bevor das Haus ein einziges Wort über sich gesagt hatte.
 *
 * ⚠️ NICHTS FÄLLT WEG, ALLES WIRD ENGER. Adresse und «Jetzt geöffnet» sind der ausgeschriebene
 *    Grund, warum dieses Fenster mehr ist als das Band (siehe Partial) — sie bleiben. Kleiner
 *    werden die Frage, die Abstände und die Mindesthöhe der Karten; die 44-px-Grenze für den
 *    Daumen ist damit weiterhin doppelt eingehalten.
 */
@media (max-width: 620px) {
    .standortwahl { padding: 18px 0 16px; }
    .standortwahl__frage { font-size: 1.35rem; }
    .standortwahl__hinweis { font-size: .88rem; margin: 0 0 14px; }
    .standortwahl__liste { gap: 9px; margin: 0 0 10px; }
    .standortwahl__ort { min-height: 0; padding: 12px 14px; gap: 3px; }
    .standortwahl__name { font-size: 1.05rem; }
    .standortwahl__spaeter { min-height: 40px; }
}

/* ---------- Header ---------- */
.site-header {
    position: sticky; top: 0; z-index: 40;
    background: color-mix(in srgb, var(--bg) 88%, transparent);
    backdrop-filter: saturate(1.3) blur(8px);
    border-bottom: 1px solid var(--line-soft);
}
/* ⛔ `padding-top/bottom` UND NICHT DIE KURZSCHREIBWEISE — sonst loescht die Zeile den
 *    seitlichen Abstand, den `.wrap` eine Regel weiter oben gibt (`padding: 0 22px`).
 *
 *    Gemessen auf einem emulierten Telefon (390px, DPR 3): `padding-left` war 0, die Marke sass
 *    auf x=0 und der Menueknopf endete auf x=390 — Name und Knopf klebten am Glas. Auf dem
 *    Schreibtisch faellt es nicht auf, weil die Seite bei 1080px ohnehin mittig steht und links
 *    und rechts Luft hat; erst unter der Wrap-Breite wird die Null sichtbar. Es ist die erste
 *    Zeile, die ein Gast auf dem Telefon sieht.
 *
 * ⚠️ DIE 22px WERDEN HIER NICHT WIEDERHOLT. Sie stehen in `.wrap`, und ein zweiter Ort waere
 *    ein zweiter Ort, an dem sie auseinanderlaufen koennen. */
.site-header__bar { display: flex; align-items: center; gap: 16px; padding-top: 12px; padding-bottom: 12px; }
.brand { display: inline-flex; align-items: center; gap: 11px; text-decoration: none; color: var(--text); line-height: 1.05; }
/* ⛔ DAS ZEICHEN BEHÄLT SEINE FORM — ES WURDE VORHER GEQUETSCHT.
      Hier stand `width: 42px; height: 42px` ohne `object-fit`, und die Vorgabe ist `fill`:
      ein Logo, das nicht quadratisch ist, wurde in das Quadrat GEDRÜCKT. Gemessen mit einem
      Zeichen im Verhältnis 3:1 — gezeichnet wurde 1:1, aus dem Kreis wurde ein Ei und die
      Schrift war nicht mehr zu lesen. Das Feld im Panel rät zu einem quadratischen Logo, es
      verlangt es aber nicht, und ein Gasthaus-Logo ist meistens ein breiter Schriftzug.
      Jetzt gibt die HÖHE das Mass und die Breite folgt dem Bild: quadratisch bleibt
      quadratisch, breit bleibt breit — bis zu einer Grenze, damit ein sehr langer Schriftzug
      nicht die halbe Kopfzeile nimmt. */
.brand__mark {
    height: 42px; width: auto; max-width: 168px; object-fit: contain;
    flex: 0 0 auto; border-radius: 10px; background: var(--plate); padding: 3px; box-shadow: var(--shadow-sm);
}
.brand__text { display: inline-flex; flex-direction: column; }
.brand__name { font-family: var(--serif); font-size: 1.15rem; }
.brand__accent { font-size: .74rem; color: var(--gold-ink); letter-spacing: .04em; }
@media (max-width: 400px) { .brand__mark { height: 36px; max-width: 132px; } .brand__name { font-size: 1.02rem; } }
.nav { display: flex; gap: 4px; margin-left: auto; }
/* Direct children only. `.nav a` also matched the address, phone, mail, social and CTA links
   inside the mobile panel and outranked their own rules on specificity, so each of them wore
   menu-item padding, size and opacity it was never meant to have. */
.nav > a { text-decoration: none; color: var(--text); font-size: .92rem; padding: 8px 12px; border-radius: 8px; opacity: .85; }
.nav > a:hover, .nav > a[aria-current="page"] { opacity: 1; color: var(--accent-ink); }
/* `.hcontrols` deleted: a header control cluster with no markup. `.header-actions` beside it
   does the same job and is the one the layout renders. */
/* ⛔ 44 × 44 px, UND DAS IST KEINE AUFRUNDUNG VON 38.
 *
 *    Hier standen `min-width: 38px; height: 38px`, ohne jede Medienabfrage — und an dieser
 *    Klasse hängen die ZWEI Knöpfe, die ein Gast auf dem Telefon am häufigsten trifft: der
 *    Menüknopf (`.navtoggle`) und die Tür zum Konto (`.kontobtn`). Beide sitzen oben rechts,
 *    also unter dem Daumen der Hand, die das Gerät hält, und hinter beiden liegt der einzige
 *    Weg zu dem, was sie öffnen. 38 px sind sechs Pixel unter der Untergrenze, die WCAG 2.2
 *    in 2.5.8 «Target Size (Minimum)» nennt — und sechs Pixel sind auf einem Telefon der
 *    Unterschied zwischen «getroffen» und «die Seite hat gescrollt».
 *
 * ⚠️ DIE BEGRÜNDUNG STEHT WEITER UNTEN SCHON EINMAL AUSGESCHRIEBEN, im Bestellweg bei
 *    `.mdish__add`: «THE BUTTON IS 44px, and that is not a rounding of 42.» Der Bestellweg hat
 *    es von Anfang an so gebaut, der Kopf der Seite nicht. Es ist dieselbe Hand und dasselbe
 *    Telefon; ein Haus mit zwei Massen für denselben Daumen hat kein Mass.
 *
 * ⚠️ `min-height` STATT `height`, UND DAS ZEICHEN BLEIBT BEI 19 px. Die Fläche wächst, das
 *    Bild darin nicht — ein grösserer Knopf, kein grösseres Symbol. Gemessen in der Kopfzeile:
 *    `.brand__mark` ist 42 px hoch (`border-box`), die Zeile wird durch die 44 px also um
 *    genau 2 px höher, nicht um sechs. */
.iconbtn { background: transparent; border: 1px solid var(--line); color: var(--text); border-radius: 8px; min-width: 44px; min-height: 44px; cursor: pointer; font: inherit; font-size: .8rem; display: inline-flex; align-items: center; justify-content: center; }
.iconbtn:hover { border-color: var(--accent); color: var(--accent-ink); }
.header-actions { display: flex; align-items: center; gap: 6px; }
.navtoggle { display: none; }
.nav__head, .nav__foot, .nav__contact { display: none; }   /* only inside the mobile off-canvas panel */
.navtoggle__bars { display: inline-flex; flex-direction: column; justify-content: center; gap: 4px; width: 18px; }
.navtoggle__bars span { display: block; height: 2px; width: 100%; background: currentColor; border-radius: 2px; }

/* Der Weg ins Gästekonto — ein Zeichen im Kopf, kein Wort.
 *
 * ⚠️ `flex: 0 0 auto` UND FESTE MASSE AM SVG. `.iconbtn` ist eine inline-flex-Box, das SVG
 *    darin also ein Flex-Element ohne eigene Breite: unter Druck schrumpft es auf nichts und
 *    zurück bleibt ein leerer Rahmen. Dieselbe Stelle ist an `.iconbtn2 svg`,
 *    `.nav__social a svg` und `.wa-fab__btn svg` schon dreimal dokumentiert.
 *
 * ⚠️ `fill: currentColor`, damit das Zeichen den Hover von `.iconbtn` mitmacht, statt in einer
 *    zweiten Farbe stehen zu bleiben, die niemand pflegt. */
.kontobtn { position: relative; }
.kontobtn svg { width: 19px; height: 19px; flex: 0 0 auto; fill: currentColor; }

/* Der Punkt sagt «Sie sind angemeldet», und ohne ihn ist ein Klick der einzige Weg, das
 * herauszufinden. Der Ring in `--bg` trennt ihn vom Rahmen darunter, sonst verschmelzen die
 * beiden Linien bei 9px zu einem Fleck. */
.kontobtn.is-angemeldet::after {
    content: '';
    position: absolute;
    top: -3px;
    right: -3px;
    width: 9px;
    height: 9px;
    border-radius: 999px;
    background: var(--accent);
    border: 2px solid var(--bg);
}

/* Mobile nav — premium off-canvas panel */
@media (max-width: 860px) {
    .navtoggle { display: inline-flex; }
    .header-actions { margin-left: auto; }
    /* backdrop-filter on the header creates a containing block that would trap the
       position:fixed off-canvas panel to the header's height — drop it on mobile so
       the panel spans the full viewport; give the header a solid background instead. */
    .site-header { -webkit-backdrop-filter: none; backdrop-filter: none; background: var(--bg); }
    .nav {
        position: fixed; inset: 0 0 0 auto;
        width: min(86vw, 340px);
        background: var(--surface); color: var(--text);
        flex-direction: column; gap: 0; padding: 0; margin: 0;
        transform: translateX(100%); transition: transform var(--t);
        box-shadow: -18px 0 50px rgba(20, 16, 12, .28);
        border-left: 1px solid var(--line-soft);
        overflow-y: auto; -webkit-overflow-scrolling: touch;
    }
    .nav.is-open { transform: translateX(0); }
    /* ⛔ AUF DER LADENSEITE LAG DAS PANEL UNTER DEM LADENKOPF. `.page-laden .site-header` ist
       `static` (Abschnitt Ladenkopf, mit Grund) — sein z-index 40 zählt dort also nicht mehr.
       Einen Stapelkontext bildet der Kopf TROTZDEM, wegen `view-transition-name: masthead`; nur
       liegt dieser Kontext jetzt auf Stufe «auto» im Wurzelkontext, und der klebende Ladenkopf
       (40), die Rubrikenleiste (35) und jede positionierte Karte der Speisekarte zeichneten
       darüber — samt dem Panel, das IM Kopf steckt. Zu sehen war nur der Kopf des Panels und der
       Kontaktblock; die Menüpunkte lagen hinter dem Heldenbild. Am Telefon des Betreibers gesehen,
       headless nachgemessen: nav fixed/auto, ladenkopf sticky/40.
       ⛔ DER ERSTE VERSUCH — z-index am Panel selbst — ÄNDERTE NICHTS, weil ein z-index nur
          innerhalb des eigenen Kontexts zählt und der Kontext des Kopfes unten blieb. Gehoben
          wird der KOPF: `relative` statt `static` ändert am Fluss nichts (kein Kleben, das war der
          Grund für `static`), gibt seinem z-index aber wieder eine Bedeutung.
       ⚠️ NUR AUF `.page-laden`, und mit `body.` davor, weil die `static`-Regel weiter unten in der
          Datei steht und bei gleicher Spezifität gewänne. Überall sonst lebt das Panel im
          klebenden Kopf (40) und der Schleier (39) darunter — dort stimmt die Ordnung. */
    body.page-laden .site-header { position: relative; z-index: 60; }
    .page-laden .nav-backdrop { z-index: 49; }
    .nav__head {
        display: flex; align-items: center; justify-content: space-between; gap: 12px;
        padding: 17px 20px; border-bottom: 1px solid var(--line-soft);
        position: sticky; top: 0; background: var(--surface); z-index: 1;
    }
    .nav__brand { display: flex; flex-direction: column; font-family: var(--serif); font-size: 1.02rem; line-height: 1.2; color: var(--text); }
    .nav__brand .accent-script { font-size: .72rem; }
    /* ⚠️ 44 px WIE `.iconbtn` — die Begründung steht dort ausgeschrieben. Dieser Knopf ist der
       Gegenknopf zum Menüknopf: wer das Panel geöffnet hat, schliesst es hier, und ein Ausgang,
       der kleiner ist als der Eingang, ist die schlechtere Hälfte des Tauschs. */
    .nav__close {
        flex: 0 0 auto; min-width: 44px; min-height: 44px; border-radius: 8px; cursor: pointer;
        background: transparent; border: 1px solid var(--line); color: var(--text);
        font-size: 1rem; line-height: 1; display: inline-flex; align-items: center; justify-content: center;
    }
    .nav__close:hover { border-color: var(--accent); color: var(--accent-ink); }
    .nav > a {
        display: flex; align-items: center; justify-content: space-between;
        padding: 15px 20px; font-size: 1.06rem; font-family: var(--serif);
        color: var(--text); opacity: 1; border-radius: 0; border-bottom: 1px solid var(--line-soft);
    }
    /* The contact block brings its own top rule; without this the two stack into a double line. */
    .nav > a:last-of-type { border-bottom: 0; }
    .nav > a[aria-current="page"] { color: var(--accent-ink); }
    .nav > a[aria-current="page"]::after { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--gold); flex: 0 0 auto; }
    .nav__foot { display: flex; gap: 10px; padding: 18px 20px; margin-top: auto; border-top: 1px solid var(--line-soft); }
    .nav__cta {
        flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 7px;
        padding: 12px; border-radius: 999px; text-decoration: none; font-weight: 600; font-size: .92rem;
        border: 1px solid var(--line); color: var(--text); font-family: var(--sans);
    }
    .nav__cta:hover { border-color: var(--accent); color: var(--accent-ink); }
    /* contact + social inside the off-canvas panel */
    .nav__contact { display: block; padding: 16px 20px; border-top: 1px solid var(--line-soft); }
    .nav__addr { font-family: var(--serif); color: var(--muted); margin: 0 0 10px; line-height: 1.5; font-size: .96rem; }
    /* Grid rather than flex: a mail address is long enough to wrap in a 340px panel, and a
       second flex line would slide back under the icon. A fixed icon column keeps every
       wrapped line aligned with the first. */
    .nav__cline {
        display: grid; grid-template-columns: 16px 1fr; align-items: start; gap: 10px;
        padding: 6px 0; color: var(--text); text-decoration: none;
        font-family: var(--sans); font-size: .93rem; line-height: 1.45; overflow-wrap: anywhere;
    }
    .nav__cline svg { color: var(--gold); margin-top: 2px; }
    .nav__cline:hover { color: var(--accent-ink); }
    .nav__social { display: flex; gap: 10px; margin-top: 14px; }
    /* ⚠️ 44 px, VIER MEHR ALS VORHER — siehe `.iconbtn`. Die Ringe stehen nebeneinander in einer
       Reihe mit 10 px Abstand; bei vier Kanälen sind das 4 × 44 + 3 × 10 = 206 px in einem Panel
       von min(86vw, 340px) abzüglich 2 × 20 px Polster, also 300 px. Es passt, und es passt auch
       noch bei fünf. */
    .nav__social a { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; border-radius: 999px; border: 1px solid var(--line); color: var(--text); }
    .nav__social a:hover { color: var(--accent-ink); border-color: var(--accent); }
    /* An SVG is a flex item with no intrinsic width to defend, so it shrinks to 0 and the
       button renders as an empty ring. The phone and mail icons sit in a grid column and are
       safe; these two are not, which is why only they went blank. */
    .nav__social a svg { flex: 0 0 auto; }
    .nav-backdrop {
        position: fixed; inset: 0; background: rgba(20, 16, 12, .5);
        opacity: 0; pointer-events: none; transition: opacity var(--t); z-index: 39;
        -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
    }
    .nav-backdrop.is-open { opacity: 1; pointer-events: auto; }
}

/* ---------- Announcement ---------- */

/* ---------- Hero ---------- */
.hero__accent-watermark {
    position: absolute; right: -2%; top: 50%; transform: translateY(-50%);
    font-family: var(--serif); font-size: clamp(120px, 34vw, 460px); line-height: .8;
    color: transparent; -webkit-text-stroke: 1.5px color-mix(in srgb, var(--gold) 42%, transparent);
    opacity: .5; pointer-events: none; white-space: nowrap; z-index: 0; user-select: none;
}
.hero__inner { position: relative; z-index: 1; max-width: 720px; }
.hero__title { font-size: clamp(2.3rem, 6.2vw, 4.1rem); margin: .18em 0 .35em; }
.hero__intro { font-size: clamp(1.05rem, 2.3vw, 1.3rem); color: var(--muted); max-width: 34em; }
.hero__cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 26px; }

/* `.hero--photo` LIVED HERE AND HAD NO MARKUP ANYWHERE. It was a second photographic hero
   beside `.hero--foto` (further down, the one the homepage actually renders), and it pointed at
   a hardcoded `/assets/img/hero.webp` — a path that does not exist in this repository and that
   twenty restaurants would have had to share if it did. Deleted rather than wired up: the
   photograph arrives per restaurant as `--hero-photo`, never as a path written into this file. */

/* ---------- Buttons ---------- */
:root { --btn-lift: 0; }
/* NO HOVER LIFT. `transform: translateY(-2px)` on every button, card and link was the house
   style of about 2018 and it is the single loudest dated signal on the page — a premium room
   does not bounce when you look at it. What replaces it is what a printed piece does: the
   surface deepens and the edge sharpens. Cheaper too, because a colour change does not
   invalidate layout.

   And it removes an accessibility hole for free: four hover transforms were outside every
   `prefers-reduced-motion` block. */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
    text-decoration: none; font: inherit; font-size: var(--fs-1); font-weight: 600;
    cursor: pointer; padding: var(--s-3) var(--s-5);
    border-radius: var(--r-btn); border: 1px solid transparent;
    transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast),
                box-shadow var(--t-fast);
}
.btn--primary { background: var(--accent); color: var(--on-accent); }
.btn--primary:hover { background: var(--accent-strong); box-shadow: 0 6px 18px color-mix(in oklab, var(--accent) 26%, transparent); }
.btn--ghost { border-color: var(--line); color: var(--text); background: transparent; }
.btn--ghost:hover { border-color: var(--accent); color: var(--accent-ink); background: color-mix(in oklab, var(--accent) 5%, transparent); }

/* ---------- Sections ---------- */
section { padding: var(--s-8) 0; }
/*
 * ⛔ DIE HAUSFARBE MACHTE FAST KEINE ARBEIT. Gemessen an einer ausgelieferten Startseite: sie
 *    stand auf zwei Knöpfen, auf den Preisen und in einem 3-px-Streifen ganz oben — zusammen
 *    rund ein Prozent der Fläche. Das eine, was Pinocchio von Kalyos unterscheiden soll, war
 *    damit das Unauffälligste auf der Seite; ein Kebaphaus, dessen Zeichen rot ist, bekam
 *    «beige mit orangefarbenen Knöpfen».
 *
 * ⚠️ EIN STRICH ÜBER JEDER ABSCHNITTSÜBERSCHRIFT, und zwar aus zwei Gründen der zurückhaltende
 *    Weg: er wiederholt sich fünf- bis achtmal auf einer Seite, und er ist ZIERDE, kein Text.
 *    Für eine Regel oder eine Zierlinie genügen 3:1 — dieselbe Rechnung, mit der `--gold` in
 *    dieser Datei seine Linien behalten durfte, während kleiner Text auf `--gold-ink` musste.
 *    Die Hausfarbe als kleine Schrift auf Papier wäre bei einem hellen Orange unter 3:1
 *    gelandet; als Strich trägt sie.
 */
.section-head { max-width: 40em; margin-bottom: 34px; }
.section-head h2 { font-size: clamp(1.7rem, 3.6vw, 2.5rem); }

.section-head::before {
    content: '';
    display: block;
    width: 44px;
    height: 2px;
    margin-bottom: 16px;
    background: var(--accent);
    border-radius: 2px;
}
.section--surface { background: var(--surface); }

/* Soft-arc section divider — a quiet ornament between bands */
.rule-ornament { height: 40px; background: radial-gradient(120% 100% at 50% 0, transparent 69%, var(--surface) 70%); }
.rule-ornament--up { background: radial-gradient(120% 100% at 50% 100%, transparent 69%, var(--surface) 70%); }

/* ---------- Dish grid ---------- */
.dish-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 18px; }
.dishcard { background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--r); padding: 20px; transition: transform var(--t), box-shadow var(--t); }
.section--surface .dishcard { background: var(--bg); }
/* Same argument as the button. A card that jumps 4px under the cursor is a card that was
   designed when that was new; a card whose edge warms is a card. */
.dishcard:hover { border-color: color-mix(in oklab, var(--gold) 44%, var(--line-soft)); background: color-mix(in oklab, var(--gold) 3%, var(--surface)); }
.dishcard__accent { color: var(--gold-ink); font-family: var(--serif); font-size: .85rem; }
/*
 * ⛔ NAME UND PREIS AUF EINER ZEILE, UND ZWAR AUF JEDER KARTE. Bis hierher standen sie als vier
 *    Blöcke untereinander — Foto, Name, Beschreibung, Preis — in einer Spalte von 230 Pixeln:
 *    sechs Arten Auskunft in vier Schriftgrössen, gestapelt. Die Speisekarte macht es seit jeher
 *    richtig (`.mdish__head`), und die fotolose Fassung dieser Karte seit dem letzten Durchgang;
 *    die Karte MIT Foto war die letzte, die es noch anders machte.
 *
 * ⚠️ AUF EINER GEDRUCKTEN KARTE STEHT DER PREIS IMMER AUF DER HÖHE DES GERICHTS. `grid-row: 2`
 *    setzt ihn neben die Beschreibung — genau der Fehler, den D-91 auf der Kontor-Seite schon
 *    einmal gekostet hat.
 *
 * ⚠️ KONTOR BLEIBT UNBERÜHRT: seine eigenen Regeln (`.layout-kontor .dishcard`, 0,2,0) stehen
 *    später in dieser Datei und überstimmen jede dieser Zeilen. Nachgesehen, nicht gehofft.
 */
/*
 * ⛔ DER PREIS UND DIE ZEICHEN TEILEN SICH DIE LETZTE ZEILE. Vorher waren es vier Blöcke
 *    untereinander — Preis, Buchstaben, «halal», «vegan» — in einer Spalte von 230 Pixeln.
 *    Gemessen an der echten Karte: eine Kachel wurde dadurch höher als ihr eigenes Foto, und
 *    vier Kacheln nebeneinander waren vier verschieden hohe Türme.
 *
 * ⛔ UND KEINE PUNKTLINIE AUF DIESER KARTE, obwohl sie hier zuerst gebaut wurde. Ausprobiert
 *    und am Bildschirm verworfen: bei vier Karten auf 1280 px ist eine Spalte 230 px breit, der
 *    Name bricht auf drei Zeilen, und von der Linie zum Preis bleiben vier Punkte übrig. Die
 *    Geste braucht Breite — sie steht deshalb bei der fotolosen Fassung weiter unten, wo zwei
 *    Karten nebeneinander stehen, und auf der Speisekarte, wo die Zeile die ganze Seite hat.
 *
 * ⚠️ KONTOR BLEIBT UNBERÜHRT: seine eigenen Regeln (`.layout-kontor .dishcard`, 0,2,0) stehen
 *    später in dieser Datei und überstimmen jede dieser Zeilen. Nachgesehen, nicht gehofft.
 */
.dishcard {
    display: grid;
    grid-template-columns: auto 1fr;
    align-content: start;
    column-gap: 10px;
}

.dishcard__photo { grid-row: 1; grid-column: 1 / -1; }
.dishcard__name { grid-row: 2; grid-column: 1 / -1; font-family: var(--serif); font-size: 1.16rem; margin: 2px 0 0; }
.dishcard__desc { grid-row: 3; grid-column: 1 / -1; color: var(--muted); font-size: .88rem; margin: 8px 0 0; }

.dishcard__price {
    grid-row: 4; grid-column: 1;
    align-self: center;
    margin-top: 12px;
    font-family: var(--serif); font-weight: 600; color: var(--accent-ink);
    font-variant-numeric: tabular-nums;
}

/* ⛔ EINE ZEILE STATT DREIER ABSÄTZE. Allergenbuchstaben, «halal» und «vegan» standen als drei
      eigene Blöcke untereinander; in einer 230-px-Spalte sind das drei Umbrüche für Angaben von
      je vier Zeichen. Gemessen: eine Kachel wurde dadurch höher als ihr eigenes Foto. */
.dishcard__tags {
    grid-row: 4; grid-column: 2;
    align-self: center; justify-self: end;
    display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 6px;
    margin-top: 12px;
}
/* DIE ZEICHEN DES HAUSES — 🌶️ am scharfen Gericht, ⭐ an der Empfehlung.

   SIE STEHEN VOR DEN ABZEICHEN UND SEHEN ANDERS AUS ALS SIE. Ein Abzeichen ist ein Wort in
   einer Pille («vegan»), ein Zeichen ist ein Bild ohne Rahmen — sonst liest das Auge beide als
   dieselbe Art Aussage, und die Allergenbuchstaben weiter unten wären die dritte.

   `line-height: 1` und die feste Grösse, weil Emoji je nach Betriebssystem unterschiedlich hoch
   sitzen und eine Zeile sonst je Gericht anders hoch wird. */
.dish-icons { display: inline-flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 10px; margin-right: 8px; }
.dish-icon { font-size: 1rem; line-height: 1; letter-spacing: 1px; cursor: default; }
.badges { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.badge { font-size: .66rem; text-transform: uppercase; letter-spacing: .04em; font-weight: 700; padding: 3px 8px; border-radius: 999px; }
.badge--vegan { background: color-mix(in srgb, var(--enamel) 16%, transparent); color: var(--enamel); }
/* HALAL — a mark, not a decoration, and deliberately NOT the vegan green. Two dietary claims in
   one colour read as one claim. This takes the brass the site already uses for anything the
   house says about itself. */
.badge--halal { background: color-mix(in oklab, var(--gold) 18%, transparent); color: var(--gold-ink); }

/* The sourcing sentence, under the card. Quiet: it is a fact, not a boast. */
.m-halal-note { color: var(--muted); font-size: var(--fs-1); margin: var(--s-5) auto 0; max-width: var(--wrap-narrow); }
.badge--vegetarian { background: transparent; color: var(--enamel); border: 1px solid color-mix(in srgb, var(--enamel) 42%, transparent); }
.badge--spicy { background: color-mix(in srgb, var(--accent) 15%, transparent); color: var(--accent-ink); }

/* ---------- Feature bands ---------- */
.lede { font-size: clamp(1.15rem, 2.6vw, 1.5rem); font-family: var(--serif); line-height: 1.4; max-width: 20em; }
.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(24px, 5vw, 60px); align-items: center; }
@media (max-width: 760px) { .split { grid-template-columns: 1fr; } }

/* ---------- Info / contact ---------- */
.hours-list { list-style: none; padding: 0; margin: 0; }
.hours-list li { display: flex; justify-content: space-between; gap: 16px; padding: 8px 0; border-bottom: 1px solid var(--line-soft); }
.hours-list b { font-weight: 600; }
/* Wunsch zu einer Bestellzeile auf der Quittung — unter dem Gericht, leiser als das Gericht. */
.hours-list .zeilennotiz { display: block; font-weight: 400; color: var(--muted); }
/* "Feiertage: geöffnet" highlighted so guests notice we open on public holidays */
/* HEUTE: der Tag mit einem Punkt in der Hausfarbe, die Zeit in der Textfarbe und fett — nicht
   mehr in Grün. Das Grün war ein Fremdkörper in jedem Haus, dessen Farbe nicht grün ist, und es
   las sich als «offen», obwohl die Zeile nur «heute» weiss. Dieselbe Regel wie in der Fusszeile;
   `hours-heute` setzt das Partial auch an einem Ruhetag. */
.hours-list li.hours-open span { color: var(--text); font-weight: 700; }
.hours-list li.hours-heute b::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 999px; background: var(--accent-ink); margin-right: 8px; vertical-align: 2px; }
/* Die Fusszeile hebt HEUTE anders hervor: `.footer-hours li.hours-heute`, im Fusszeilenblock. */
.feiertag-note { display: inline-flex; align-items: center; gap: 8px; margin: 14px 0 0; font-weight: 600; color: var(--enamel); background: color-mix(in srgb, var(--enamel) 10%, transparent); border: 1px solid color-mix(in srgb, var(--enamel) 32%, transparent); border-radius: 999px; padding: 8px 16px; font-size: .95rem; }
.feiertag-note span { font-weight: 800; }

/* `.map-card` / `.map-attrib` ARE GONE, and this is the one deletion that removes something
   good. The component is a static map PICTURE behind a link — no embed, no third-party request,
   exactly what the Datenschutz page promises. It needs one thing this codebase does not have:
   somewhere for a restaurant to put that picture. There is no `contact.map_image` setting and no
   panel field to fill it, so every restaurant would render the same empty frame or none at all.
   Rather than ship a component nobody can switch on, the rules go; see the handover note. */

/* ---------- Ratgeber / long-form article ---------- */
.article-hero { padding: clamp(40px, 6vw, 72px) 0 clamp(18px, 3vw, 30px); }
.article-hero__title { font-size: clamp(2rem, 4.6vw, 3.1rem); margin: .12em 0 .3em; }
.article-hero__lead { font-size: clamp(1.1rem, 2.2vw, 1.35rem); color: var(--muted); font-family: var(--serif); line-height: 1.5; max-width: 34em; }
.breadcrumb { font-size: .82rem; color: var(--muted); margin: 8px 0 22px; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.breadcrumb a { color: var(--muted); text-decoration: none; }
.breadcrumb a:hover { color: var(--accent-ink); }
.breadcrumb [aria-current] { color: var(--text); }
.article { padding: clamp(28px, 4vw, 52px) 0 clamp(40px, 6vw, 72px); }
/* `.article__title` and `.article__lead` are deleted: a SECOND set of names for the article
   headline and standfirst, sized almost identically to `.article-hero__title` and
   `.article-hero__lead` above, which are the pair ratgeber-artikel.blade.php actually
   renders. Two names for one element is how the wrong one ends up in the next template —
   and here the unused pair was the one that looked more obvious. */
/* THE MEASURE LIVES HERE, and that is the whole shell decision for the text pages.
   Eight guest pages were built out of class names — `.section`, `.prose`, `.page-head` — that
   this stylesheet has never defined, so they rendered with no vertical band at all and their
   paragraphs ran the full 1036px of `.wrap`. Rather than invent a second page vocabulary beside
   the one the design already has, those pages now use the real names: `<section>` for the band
   (44–88px, line 195) and `.wrap--narrow` + this rule for the column.

   The cap sits on the RUNNING TEXT, not on `.article__body` itself. Capping the block would
   also have capped the table inside it, and a table is the one thing on a long page that wants
   every pixel of its column. Measured at 1280px: a paragraph was 676px wide and is now 564px —
   about 66 characters at 1.05rem, inside the 45–75 an eye tracks without losing the line —
   while `.article__body table` still spans the full 676px. */
.article__body { font-size: 1.05rem; line-height: 1.72; }
.article__body > p, .article__body > ul, .article__body > ol, .article__body > blockquote { max-width: 66ch; }
/* THERE WAS NO h1 RULE. This block sized h2 and h3 and stopped, so the page title on the
   Impressum, the Datenschutzerklärung and the invitation fell through to the user agent's
   `2em` — 32px, fixed, on a 316px phone column, which is what put the 382px scrollbar on the
   Datenschutz page. Fluid like every other heading here, and smaller at the bottom of the
   clamp than h2's own maximum would suggest, because these titles are single long German
   compounds rather than sentences. */
.article__body h1 { font-size: clamp(1.75rem, 5vw, 2.6rem); margin: 0 0 .45em; }
.article__body h2 { font-size: clamp(1.4rem, 3vw, 1.9rem); margin: 1.7em 0 .5em; }
.article__body h3 { font-size: 1.2rem; margin: 1.4em 0 .4em; }
.article__body p { margin: 0 0 1.1em; }
.article__body ul, .article__body ol { margin: 0 0 1.2em; padding-left: 1.3em; }
.article__body li { margin: .3em 0; }
.article__body a { color: var(--link); text-decoration: underline; text-underline-offset: 3px; text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent); }
.article__body a:hover { color: var(--accent-ink); }
.article__body blockquote { margin: 1.4em 0; padding: 4px 0 4px 20px; border-left: 3px solid var(--gold); font-family: var(--serif); font-size: 1.15rem; }
.article__body table { width: 100%; border-collapse: collapse; margin: 1.4em 0; font-size: .96rem; display: block; overflow-x: auto; }
.article__body th, .article__body td { text-align: left; padding: 10px 14px; border-bottom: 1px solid var(--line-soft); vertical-align: top; }
.article__body thead th { background: var(--surface); font-family: var(--serif); font-weight: 600; border-bottom: 2px solid color-mix(in srgb, var(--gold) 40%, var(--line-soft)); }
.article__cta { display: flex; flex-wrap: wrap; gap: 12px; margin: 34px 0; }
/* `.article__related` (a "further reading" block) and `.rg-cluster__title` are deleted. No
   template has rendered either, and the Ratgeber index already links every article; a
   related-articles rail on a restaurant with four of them points back at itself. */
.rg-cluster { margin: 40px 0; }
.rg-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; }
.rg-card { display: flex; flex-direction: column; gap: 6px; padding: 18px 20px; border: 1px solid var(--line-soft); border-radius: var(--r); background: var(--surface); text-decoration: none; color: var(--text); transition: transform var(--t), box-shadow var(--t), border-color var(--t); }
.section--surface .rg-card { background: var(--bg); }
.rg-card:hover { box-shadow: var(--shadow-sm); border-color: color-mix(in oklab, var(--gold) 44%, var(--line-soft)); }
.rg-card__cat { font-size: .72rem; text-transform: uppercase; letter-spacing: .12em; color: var(--gold-ink); font-weight: 600; }
.rg-card__title { font-family: var(--serif); font-size: 1.14rem; line-height: 1.25; }
.rg-card__teaser { font-size: .9rem; color: var(--muted); line-height: 1.5; }
.rg-card__more { margin-top: auto; font-size: .86rem; font-weight: 600; color: var(--accent-ink); }
.rg-card--hub { background: color-mix(in srgb, var(--accent) 8%, var(--surface)); }

/* The `.faq` accordion is deleted, not left waiting for markup. Nothing in this repository has
   ever rendered it, and the reason it would stay that way is a product one: the questions have
   to be WRITTEN, by a restaurant owner, and an accordion with two rows in it looks emptier than
   the paragraph it replaced. The Ratgeber already carries anything worth answering at length. */

/* ---------- Footer ---------- */
/* DIESELBE FUSSZEILE AUF JEDER SEITE UND IN JEDEM HAUS. Der Betreiber sah zwei Häuser mit zwei
   verschiedenen Fusszeilen — es war dieselbe Fusszeile auf zwei verschiedenen Seiten: die
   Startseite liess die Zeiten weg, weil sie weiter oben schon standen (die Begründung, und ihre
   Rücknahme, stehen in layout.blade.php). Die Bauteile hier sind für vier Spalten gedacht, von
   denen zwei kommen und gehen (Zeiten, «Gut zu wissen»); nichts darf davon abhängen, dass eine
   bestimmte Spalte da ist. */
.site-footer { background: var(--footer-bg); color: var(--footer-text); padding: 64px 0 104px; margin-top: 40px; border-top: 3px solid var(--gold); }
.site-footer a { color: var(--footer-text); text-decoration: none; }
.site-footer a:hover { color: var(--gold); }
/* FOUR COLUMNS FOR THREE BLOCKS. `1.5fr 1fr 1fr 1fr` was declared against a footer that renders
   a brand block, an hours block and a links block — and the hours block disappears entirely when
   a restaurant has not entered any. So the grid held one permanently empty track, and a second
   one on every restaurant without hours: measured at 1280px, 236px of the footer's width was
   reserved for nothing and the three real blocks were squeezed to the left of it.

   Flex rather than a corrected column count, because the number of children is a runtime fact.
   `min-width: 0` on the children is the wrapping guard — an email address in the brand block is
   one long unbreakable token, and a flex item's default `min-width: auto` will widen the
   document rather than break it. */
.footer-grid { display: flex; flex-wrap: wrap; gap: 44px 40px; }
.footer-grid > * { flex: 1 1 190px; min-width: 0; }
.footer-brand { flex: 1.5 1 250px; }
/* Die Zeitenspalte braucht Platz für «11:30–14:30, 17:00–22:00» neben «Donnerstag», ohne dass die
   Uhrzeit unter den Tag bricht: das sind 262px bei .9rem — daher die breitere Basis. */
.footer-zeiten { flex: 1.3 1 280px; }
/* Das Zeichen auch unten: wer bis zur Fusszeile gelesen hat, soll sehen, WESSEN Haus das war.
   Gleiche Regel wie oben — die Höhe gibt das Mass, die Breite folgt dem Bild. */
.footer-brand__mark {
    display: block; height: 48px; width: auto; max-width: 200px; object-fit: contain;
    margin: 0 0 14px; border-radius: 10px; background: var(--plate); padding: 4px;
}
.footer-brand__name { font-family: var(--serif); font-size: 1.22rem; font-weight: 600; color: #fff; margin: 0 0 4px; display: block; line-height: 1.25; letter-spacing: -.005em; }
/* Der Zusatz («GHIÊN», die Schrift des Hauses in seiner Sprache) steht als Geschwister NEBEN dem
   Namen, nicht darin — die alte Regel `.footer-brand__name .accent-script` traf deshalb nie, und
   der Zusatz trug die Farbe für Papier: `--gold-ink` ist auf #191D21 ein dunkles Ocker. Auf dem
   dunklen Grund gilt das Gold, das für dunkle Gründe gemischt ist. */
.footer-brand .accent-script { display: block; font-size: .78rem; color: var(--on-dark-accent); letter-spacing: .04em; margin: 0 0 18px; }
/* Adresse, Telefon, Post: das Zeichen in einer festen 16px-Spalte, damit eine umbrechende Adresse
   unter ihrer ersten Zeile weiterläuft und nicht unter dem Zeichen. `overflow-wrap: anywhere` für
   die Post-Adresse, die ein einziges Wort ist. */
.footer-kontakt { list-style: none; margin: 0 0 22px; padding: 0; display: grid; gap: 10px; font-size: .93rem; line-height: 1.5; }
.footer-kontakt li { display: grid; grid-template-columns: 16px 1fr; gap: 11px; align-items: start; color: var(--footer-muted); overflow-wrap: anywhere; }
.footer-kontakt svg { width: 16px; height: 16px; margin-top: 3px; color: var(--gold); }
.footer-kontakt a { color: var(--footer-text); }
.footer-kontakt a:hover { color: var(--gold); }
/* Die Spaltentitel als Kolumnentitel: klein, gesperrt, in Versalien, im Gold für dunkle Gründe —
   dieselbe Stimme wie die Signatur unten. Der goldene Unterstrich der ersten Fassung war das
   Bauteil, das ein Betreiber als «Baukasten» erkennt. */
.footer-h { font-family: var(--sans); font-weight: 600; font-size: .74rem; letter-spacing: .16em; text-transform: uppercase; color: var(--on-dark-accent); margin: 0 0 18px; }
.footer-links { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; font-size: .92rem; }
/* Die Zustandszeile: ein Punkt, ein Wort, eine Uhrzeit. Der Punkt trägt die Farbe — grün mit
   einem weichen Hof, wenn offen; das gedämpfte Grau der Fusszeile, wenn zu. */
.footer-status { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 14px; font-size: .9rem; color: #fff; }
.footer-status__punkt { width: 8px; height: 8px; border-radius: 999px; background: var(--footer-dim); flex: 0 0 auto; }
.footer-status__punkt--offen { background: var(--footer-open); box-shadow: 0 0 0 4px color-mix(in srgb, var(--footer-open) 22%, transparent); }
.footer-status__wort { font-weight: 600; }
.footer-status__zeit { color: var(--footer-muted); }
.footer-status__zeit::before { content: "·"; margin-right: 8px; opacity: .6; }
.footer-hours { list-style: none; margin: 0; padding: 0; }
.footer-hours li { display: flex; justify-content: space-between; align-items: baseline; gap: 14px; padding: 7px 0; border-bottom: 1px solid rgba(255, 255, 255, .08); font-size: .9rem; }
.footer-hours li:last-child { border-bottom: 0; }
.footer-hours b { color: var(--footer-text); font-weight: 500; }
/* Tabellenziffern, damit «17:00–22:00» unter «11:30–14:30, 17:00–22:00» in der Spalte steht. */
.footer-hours span { color: var(--footer-muted); font-variant-numeric: tabular-nums; text-align: right; }
/* HEUTE: ein goldener Punkt vor dem Tag, Tag und Zeit in Weiss — nicht in Grün. Grün hiess in der
   ersten Fassung «heute hat das Haus Fenster», und um 23:30 stand die Zeile grün da, während die
   Tür seit anderthalb Stunden zu war. Ob GERADE offen ist, sagt die Zeile darüber; die Tabelle
   sagt nur, welcher Tag heute ist — auch wenn heute Ruhetag ist. */
.footer-hours li.hours-heute b, .footer-hours li.hours-heute span { color: #fff; font-weight: 600; }
.footer-hours li.hours-heute b::before { content: ""; display: inline-block; width: 6px; height: 6px; border-radius: 999px; background: var(--gold); margin-right: 9px; vertical-align: 2px; }
.footer-note { color: var(--footer-dim); font-size: .84rem; margin: 12px 0 0; }
.footer-social { display: flex; gap: 12px; margin-top: 4px; }
.footer-social a { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; border-radius: 999px; border: 1px solid rgba(255, 255, 255, .22); color: var(--footer-text); transition: color var(--t), border-color var(--t), background var(--t); }
.footer-social a:hover { color: var(--gold); border-color: var(--gold); background: rgba(255, 255, 255, .06); }
/* These were 40px circles with the WORD "Instagram" inside them — nine characters at .92rem in
   a 40px box, spilling past the ring on both sides. The rule was written for an icon; the
   markup never had one. `flex: 0 0 auto` for the same reason as everywhere else in this file:
   an SVG is a flex item with no intrinsic width to defend and collapses to an empty ring. */
.footer-social a svg { width: 19px; height: 19px; flex: 0 0 auto; fill: currentColor; }
.footer-bottom { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center; gap: 12px; margin-top: 52px; padding-top: 22px; border-top: 1px solid rgba(255, 255, 255, .12); font-size: .82rem; }
/* Die Rechtszeile gedämpft, die Links darin in der Textfarbe: so lesen sich die Punkte dazwischen
   als Trenner und die Wörter als Wege. Das Jahr mit dem Namen des Hauses steht in der Textfarbe —
   es ist die Unterschrift des Hauses; die Studio-Signatur rechts ist die des Werks. */
.footer-legal { margin: 0; color: var(--footer-muted); }
.footer-legal__jahr { color: var(--footer-text); }

/* ---------- Studio signature ----------
   Authorship, not advertising. The words are the quiet part — 12px, wide tracking, uppercase,
   just over half opacity — and the logo is the only element at full strength. The opacity sits
   on the text span, never on the link, so the mark stays crisp.
   height AND width are both set: the <img> carries width/height attributes for layout
   stability, and those are presentational hints that would otherwise fight `height: 24px`. */
.signature { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 6px 8px; text-decoration: none; color: inherit; }
.signature__by {
    font-family: var(--sans);
    font-size: 11px;
    letter-spacing: .18em;
    text-transform: uppercase;
    opacity: .55;
    line-height: 1.3;
    transition: opacity var(--t);
}
/* 16px für die auf die Buchstaben beschnittene Resmuar-Wortmarke (resmuar.webp, kein Rand):
   das kleine «resmuar» misst damit rund 11px x-Höhe neben den 11px der Rechtszeile — eine Zeile,
   nicht zwei Grössen. Die Bemuar-Marke davor brauchte 14px, weil ihr Zeichen über die
   Buchstaben hinausschoss; dieses Bild hat keinen Rand. */
.signature__mark { height: 16px; width: auto; display: block; }
.signature:hover .signature__by,
.signature:focus-visible .signature__by { opacity: .85; }
@media (max-width: 900px) { .footer-grid { gap: 36px 34px; } }
@media (max-width: 520px) {
    /* One column, said as a basis rather than as a template: the grid is a flex row now. */
    .footer-grid { gap: 32px; }
    .footer-grid > *, .footer-brand, .footer-zeiten { flex-basis: 100%; }
    .footer-bottom { flex-direction: column; align-items: flex-start; }
}

/* ---------- Persistent mobile CTA bar ---------- */
.mobilebar { position: fixed; bottom: 0; left: 0; right: 0; z-index: 45; display: none; background: var(--surface); border-top: 1px solid var(--line); box-shadow: 0 -6px 24px rgba(36,30,26,.10); }
.mobilebar a { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 9px 4px; text-decoration: none; color: var(--text); font-size: .72rem; }
.mobilebar a.is-accent { color: var(--accent-ink); }
/* `.mobilebar a span:first-child { font-size: 1.15rem }` sized an icon that was never there:
   the bar rendered three words in a row and read as a strip of links rather than a set of
   actions. The icons are inline SVG (one less request, and none to anybody else), and an SVG
   inside a flex column has no intrinsic width to defend — `flex: 0 0 auto` is what stops it
   collapsing to a 0px sliver, the same failure already documented on `.nav__social` below. */
.mobilebar__ic { width: 21px; height: 21px; flex: 0 0 auto; }

/* ⛔ BEI OFFENEM MENÜ VERSCHWINDET DIE LEISTE, UND OHNE DIESE ZEILE LAG SIE DARÜBER.
      `.nav` ist zwar `position: fixed`, steht aber IM `.site-header`, und der ist
      `position: sticky; z-index: 40` — also ein eigener Stapelkontext. Das Panel kann auf
      Wurzelebene nie über 40 hinaus; `.mobilebar` liegt bei 45 direkt am `body`. Gemessen auf
      390 × 844 mit offenem Menü: das Element am unteren Bildrand gehört zur Leiste, nicht zum
      Panel — die untersten 62 px des Menüs, wo «Anrufen» und «WhatsApp» stehen, waren verdeckt.

      Die Leiste auszublenden ist die richtige Hälfte des Tauschs: sie bietet dieselben zwei
      Wege an, die das offene Menü ohnehin zeigt. Ein zweiter anklickbarer Navigationsweg hinter
      einem Menü, das sich als Überlagerung ausgibt, ist ausserdem genau das, was ein
      `aria-modal`-Panel nicht haben darf. */
body:has(.nav.is-open) .mobilebar { display: none; }
.mobilebar a.is-accent .mobilebar__ic { color: var(--accent-ink); }
/* Der Warenkorb-Knopf in der Daumenleiste — dieselbe Optik wie die Links daneben, nur als
   <button> (er öffnet die Schublade, er navigiert nicht). */
.mobilebar__korb {
    flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px;
    padding: 9px 4px; border: 0; background: none; color: var(--text);
    font: inherit; font-size: .72rem; cursor: pointer;
}
.mobilebar__glyph { position: relative; display: inline-flex; }
/* Die Zahl sitzt oben rechts am Symbol; leer (und `hidden`), solange der Korb leer ist, damit
   das Symbol nicht «0» trägt. */
.mobilebar__badge {
    position: absolute; top: -7px; left: 100%; transform: translateX(-58%);
    min-width: 16px; height: 16px; padding: 0 4px; box-sizing: border-box;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--accent); color: var(--on-accent);
    font-size: .62rem; font-weight: 700; line-height: 1;
    border-radius: 999px; font-variant-numeric: tabular-nums;
}
.mobilebar__badge[hidden] { display: none; }
@media (max-width: 860px) { .mobilebar { display: flex; } body { padding-bottom: 66px; } }
@media (max-width: 860px) {
    /* AUF DEM TELEFON IST DER KORB DAS SYMBOL IN DER DAUMENLEISTE, nicht eine zweite feste Leiste
       darüber. Die volle `#cartbar` (Summe + «Bestellung ansehen») bleibt dem Breitbild vorbehalten,
       wo es keine Daumenleiste gibt; hier öffnet das Symbol dieselbe Schublade mit Summe und
       Absenden. So stapeln sich nie zwei feste Leisten (der Grund, aus dem `~ .mobilebar` die
       Leiste sonst versteckt), und der Korb ist trotzdem immer sichtbar. Nur die Speisekarte
       (`.page-menu`); `/bestellen` behält seine `#ladenbar` unberührt. `#cartbar` (id) schlägt die
       spätere `~ .mobilebar`-Regel in der Spezifität, unabhängig von der Reihenfolge. */
    .page-menu #cartbar { display: none; }
    .page-menu #cartbar ~ .mobilebar { display: flex; }
}

/* ---------- Speisekarte (menu page) ---------- */
/* Menu hero: a compact photo band so the table-QR landing feels premium at once.

   THE PHOTOGRAPH COMES FROM THE RESTAURANT, NOT FROM THIS FILE. It used to be
   `url("/assets/img/hero.webp")` — a path with no file behind it anywhere in this repository, so
   every Speisekarte load spent a request on a 404 before falling back to the dark plate; and had
   the file existed, twenty restaurants would have opened their card with the same photograph.
   `--hero-photo` is set once on <body> by the layout, from the restaurant's own upload. `none`
   is a valid background-image, so a restaurant with no photo requests nothing at all and still
   gets the finished dark plate — which is the state both demo restaurants are in today. */
.menu-hero {
    position: relative;
    display: flex; flex-direction: column; justify-content: flex-end;
    /* SCHLANKER als zuvor (war 210–320px). Der Kopf ist ein Bild-Band, kein halber Bildschirm —
       der Text darunter (Abholband, Filter) soll ohne langes Scrollen sichtbar sein. */
    min-height: clamp(168px, 24vh, 240px);
    color: var(--on-dark);
    padding: 0 0 clamp(16px, 3vw, 26px);
    overflow: hidden; isolation: isolate;
    /* Rückfall ohne Diashow: Hausfoto (`--hero-photo` vom `<body>`), sonst die dunkle Platte. */
    background: var(--hero-photo, none) center 42% / cover no-repeat, var(--dark-plate);
}
/* DIE DIASHOW liegt UNTER dem Schleier und dem Text. Die Bilder blenden über `.is-active`
   ineinander; ohne Skript trägt das erste `.is-active` und steht still — kein leerer Kopf. */
.menuhero__stage { position: absolute; inset: 0; z-index: 0; }
.menuhero__slide {
    position: absolute; inset: 0; width: 100%; height: 100%;
    object-fit: cover; opacity: 0; transition: opacity 1.1s ease;
}
.menuhero__slide.is-active { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .menuhero__slide { transition: none; } }
/* DER SCHLEIER ALS EIGENE SCHICHT (z-index 1), damit die Schrift auf JEDEM Bild lesbar bleibt.
   Früher lag er im Hintergrund der Box; ein Diashow-Bild darüber hätte ihn verdeckt. */
.menu-hero::after {
    content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
    background:
        linear-gradient(0deg, var(--scrim-86) 0%, var(--scrim-42) 48%, var(--scrim-08) 100%),
        linear-gradient(90deg, var(--scrim-64) 0%, var(--scrim-08) 56%, transparent 100%);
}
.menu-hero .hero__inner { position: relative; z-index: 2; }
.menu-hero .hero__title { color: var(--on-dark); text-shadow: 0 2px 22px rgba(0, 0, 0, .38); margin-bottom: 0; }
.menu-hero .eyebrow, .menu-hero .accent-script { color: var(--on-dark-accent); text-shadow: 0 1px 9px rgba(0, 0, 0, .4); }
/* Die eigenen Worte der Küche über der Karte — schlank und ruhig statt des grossen Absatzes, der
   früher im Kopf stand. Zentriert und schmal, damit sie als Auftakt liest, nicht als Fliesstext. */
.menu-lede {
    margin: clamp(16px, 3vw, 24px) auto 0; max-width: 46em; text-align: center;
    color: var(--muted); font-size: var(--fs-1); line-height: 1.55;
}
/* TAG/NACHT OBEN RECHTS IM KOPF, über dem Foto statt in der Filterleiste (die dadurch auf dem
   Telefon eine Zeile kürzer wird). Auf dem dunklen Bild braucht er eigene Farben: ein ruhiger,
   halbtransparenter Kreis mit hellem Rand und hellem Symbol. Über `.menu-hero .theme-toggle`,
   ohne eine zweite Klasse am Knopf — dessen `class`/`id` hält MenuTest fest. */
.menu-hero .theme-toggle {
    position: absolute; top: clamp(11px, 2.4vw, 18px); right: clamp(11px, 3vw, 20px); z-index: 2;
    width: 38px; height: 38px;
    background: color-mix(in oklab, #000 32%, transparent);
    border-color: color-mix(in oklab, #fff 32%, transparent);
    color: var(--on-dark);
    -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
}
.menu-hero .theme-toggle:hover { border-color: var(--on-dark); color: var(--on-dark); }
.menu-hero .theme-toggle svg { width: 17px; height: 17px; }
/* `.menu-dl`, `.menu-dl-foot` and `.menu-hero__dl` — a "Karte als PDF" button — are deleted.
   No template ever rendered them, and they should not be added: a PDF is a frozen copy of the
   one page on this platform where being out of date is a legal matter rather than an
   embarrassment, and it cannot be filtered by diet or by the allergens a guest is avoiding.
   The HTML card does both, and it is the one that is current. */

/* Elegant placeholder tile for dishes without a photo (serif monogram on warm stone). */
.ph-tile {
    display: flex; align-items: center; justify-content: center;
    background: radial-gradient(120% 120% at 30% 22%, color-mix(in srgb, var(--gold) 13%, var(--surface)), var(--line-soft));
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--gold) 26%, transparent), var(--shadow-sm);
}
.ph-tile__mono { font-family: var(--serif); font-weight: 600; line-height: 1; color: color-mix(in srgb, var(--wine) 62%, var(--gold)); }
.mdish__photo.ph-tile .ph-tile__mono { font-size: 2.4rem; }
.dishcard__photo.ph-tile .ph-tile__mono { font-size: 3.2rem; }
/* Category drink icon inside the placeholder tile (drinks without a photo). */
.ph-tile__ic { width: 48%; height: 48%; color: color-mix(in srgb, var(--wine) 50%, var(--gold)); }
/* On the menu page (table-QR target) the header scrolls away and ONLY a slim category
   bar stays sticky — so filters never eat the mobile screen. */
.page-menu .site-header { position: static; }
/* ---------- outer band + contained control card (editorial-menu-card) ---------- */
/* Schlanker: das Werkzeugband hält Suche, Diät und Nacht — es soll eine Leiste sein, kein Feld. */
.menu-tools { border-bottom: 1px solid var(--line-soft); padding: 11px 0 10px; }

.m-card {
    position: relative;
    display: grid;
    gap: 10px;
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--r);
    padding: 11px 14px 10px;
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}
/* signature: hairline enamel-gold rule across the top edge */
.m-card::before {
    content: "";
    position: absolute; inset: 0 0 auto 0; height: 2px;
    background: linear-gradient(90deg,
        transparent 0,
        color-mix(in srgb, var(--gold) 70%, transparent) 18%,
        color-mix(in srgb, var(--gold) 70%, transparent) 82%,
        transparent 100%);
}
/* ---------- compact toolbar: search-icon · diet · theme-icon ---------- */
.m-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
/* shared round icon button (search + theme)
 *
 * ⚠️ 44 px UND NICHT MEHR 42 — die zwei Pixel sind genau der Satz, der im Bestellweg bei
 *    `.mdish__add` steht: «and that is not a rounding of 42». Zwei Knöpfe, die auf DEMSELBEN
 *    Bildschirm sitzen (Suche und Tag/Nacht in der Leiste der Speisekarte, der Warenkorb-Knopf
 *    im Gericht darunter), hatten zwei verschiedene Trefferflächen. Jetzt eine.
 *
 * ⚠️ DAS ZEICHEN BLEIBT BEI 19 px (`.iconbtn2 svg` weiter unten). Der Ring wird grösser, die
 *    Lupe nicht. */
.iconbtn2 { flex: 0 0 auto; min-width: 44px; min-height: 44px; border-radius: 999px; display: inline-flex; align-items: center; justify-content: center; padding: 0; border: 1px solid var(--line); background: var(--bg); color: var(--muted); cursor: pointer; transition: color var(--t), border-color var(--t); }
.iconbtn2:hover { border-color: var(--accent); color: var(--accent-ink); }
.iconbtn2:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* `flex: 0 0 auto` — `.iconbtn2` is an inline-flex box, so its SVG is a flex item with no
   intrinsic width and shrinks to nothing under pressure, leaving a bare ring. Same failure
   already documented on `.nav__social a svg` and `.wa-fab__btn svg`. */
.iconbtn2 svg { width: 19px; height: 19px; flex: 0 0 auto; }
/* The magnifier button. It shares `.iconbtn2`'s shape; what is its own is that it must not be
   squeezed when `.searchbox.is-open` hands the whole row to the input beside it. */
.searchbox__btn { flex: 0 0 auto; }
/* search: a magnifier that expands to a field on click */
.searchbox { flex: 0 0 auto; display: flex; align-items: center; min-width: 0; }
.searchbox__input { width: 0; min-width: 0; padding: 0; border: 1px solid transparent; border-radius: var(--r-sm); background: var(--bg); color: var(--text); font: inherit; font-size: .95rem; opacity: 0; transition: width var(--t), opacity .2s, padding var(--t), margin var(--t); }
.searchbox__input::placeholder { color: var(--muted); }
.searchbox.is-open { flex: 1 1 100%; order: -1; }
.searchbox.is-open .searchbox__input { flex: 1 1 auto; width: auto; opacity: 1; margin-left: 8px; padding: 11px 14px; border-color: var(--line); }
.searchbox.is-open .searchbox__input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent); }
/* theme toggle — icon only (sun/moon), pushed to the right edge of the bar */
.theme-toggle { margin-left: auto; }
.theme-toggle__ic { width: 19px; height: 19px; }
.theme-toggle__sun { display: none; }
:root[data-theme="dark"] .theme-toggle__moon { display: none; }
:root[data-theme="dark"] .theme-toggle__sun { display: inline; }

/* ---------- diet — quiet segmented control ---------- */
.m-diet { flex: 0 0 auto; }
.m-diet {
    display: inline-flex; gap: 4px; padding: 4px;
    background: color-mix(in srgb, var(--bg) 70%, var(--surface));
    border: 1px solid var(--line-soft); border-radius: 999px;
}
.m-diet .chip {
    font: inherit; font-family: var(--sans); font-size: .84rem;
    cursor: pointer;
    display: inline-flex; align-items: center; gap: 7px;
    border: 1px solid transparent; background: transparent; color: var(--muted);
    padding: 8px 16px; border-radius: 999px; white-space: nowrap;
    transition: color var(--t), background var(--t), border-color var(--t), box-shadow var(--t);
}
.m-diet .chip:hover { color: var(--text); }
.m-diet .chip.is-active {
    background: var(--surface); border-color: var(--line-soft);
    color: var(--accent-ink); font-weight: 600; box-shadow: var(--shadow-sm);
}
.m-diet .chip.is-active::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: var(--gold); }
.m-diet .chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------- (C) allergen ledger — collapsed by default ---------- */
.m-allergen { border-top: 1px solid var(--line-soft); padding-top: 11px; }
.m-allergen__toggle {
    list-style: none; cursor: pointer;
    display: flex; align-items: center; gap: 10px;
    font-family: var(--sans); font-weight: 600; font-size: .9rem; color: var(--link);
    padding: 5px 2px; border-radius: var(--r-sm);
}
.m-allergen__toggle::-webkit-details-marker { display: none; }
.m-allergen__toggle::marker { content: ""; }
.m-allergen__toggle:hover { color: var(--accent-ink); }
.m-allergen__toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.m-allergen__mark { width: 17px; height: 17px; flex: 0 0 auto; color: var(--gold); }
.m-allergen__text { flex: 1 1 auto; }
.m-allergen__chev { width: 16px; height: 16px; flex: 0 0 auto; color: var(--muted); transition: transform var(--t); }
.m-allergen[open] .m-allergen__chev { transform: rotate(180deg); }
.m-allergen[open] .m-allergen__toggle { color: var(--accent-ink); }
.m-allergen__body { padding-top: 8px; }
/* Colour comes from the global `.muted` now; this keeps only what is particular to the ledger. */
.m-allergen__body .muted { margin: 0; font-size: .82rem; line-height: 1.5; max-width: var(--wrap-narrow); }

.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
/* ⛔ `min-height: 44px` — DIESE CHIPS GEHÖREN DEM GAST MIT DER ALLERGIE.
 *
 *    Gemessen waren es rund 31 px hoch: 2 × 7 px Polster, eine Zeile in .82rem und zwei
 *    Haarlinien. Das ist die kleinste Trefferfläche, die diese Seite einem Gast hinstellt —
 *    und sie liegt ausgerechnet an dem Bedienelement, mit dem jemand «Nüsse» aus der Karte
 *    nimmt, bevor er bestellt. Ein Fehlgriff hier ist nicht «Menü statt Konto», sondern eine
 *    Karte, die die falschen Gerichte zeigt.
 *
 * ⚠️ ÜBER DIE HÖHE, NICHT ÜBER DIE SCHRIFT. `min-height` lässt die Pille wachsen, ohne dass
 *    ein Zeichen grösser wird: dieselbe Schrift, dasselbe Kästchen (15 px), dieselbe Breite —
 *    die kommt vom Etikett («Hc · Walnüsse (Schalenfrüchte)») und war nie das Problem. Die
 *    Polsterwerte bleiben stehen, weil sie den Innenabstand LINKS und RECHTS setzen; oben und
 *    unten verteilt `align-items: center` den Rest. */
.ex-chip {
    display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
    min-height: 44px;
    font-family: var(--sans); font-size: .82rem; color: var(--muted);
    border: 1px solid var(--line); background: var(--bg);
    padding: 7px 12px 7px 10px; border-radius: 999px;
    transition: color var(--t), border-color var(--t), background var(--t);
}
.ex-chip:hover { color: var(--text); border-color: var(--accent); }
/* 20 px statt 15: das Kästchen selbst ist das Ziel, das ein Daumen trifft — gemessen als kleinstes
   Bedienelement der Karte (15×15) am 11.09.2026. */
.ex-chip .m-ex { width: 20px; height: 20px; margin: 0; flex: 0 0 auto; cursor: pointer; accent-color: var(--accent); }
/* `min-width: 0` is the overflow guard. The longest label on this control is "Hc · Walnüsse
   (Schalenfrüchte)"; a flex item's default `min-width: auto` refuses to shrink below its
   content, so at the 320px floor the chip pushed the row wider than the viewport and the whole
   page gained a horizontal scrollbar — from a checkbox label. */
.ex-chip__label { min-width: 0; }
.ex-chip:has(.m-ex:checked) {
    color: var(--accent-ink); font-weight: 600;
    border-color: var(--accent); background: color-mix(in srgb, var(--accent) 9%, var(--surface));
}
.ex-chip:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Cereals and nuts have to be named individually (Aa Weizen, Hc Walnüsse), so those
   letters sit under their group name with an "alle" chip in front of them. */
.ex-grp { margin-top: 14px; }
/* ⛔ `--gold-ink` UND NICHT `--c-brass`: DAS HIER IST TEXT, UND ES IST DER DRITTE FUNDORT
   DESSELBEN FEHLERS.

   `tokens.css` schreibt die Regel aus — «--gold is 3.33:1 on the paper — correct for a rule, an
   underline or a border, where 3:1 is the bar, and NOT correct for text.» `.accent-script` und
   `.legend__grid h4` wurden umgestellt; diese Überschrift griff weiterhin auf die ROHE
   Markenfarbe zu, an `--gold` vorbei.

   Gemessen im Nachtmodus, wie die Speisekarte sich öffnet: #9A7B4F auf #1D2227 = 4,06:1,
   verlangt sind 4,5:1. Und es steht ausgerechnet über den Familienüberschriften des
   Allergenfilters — dem Bedienelement, das ein Gast mit einer Allergie benutzt. */
.ex-grp__hd { display: block; font-size: .72rem; text-transform: uppercase; letter-spacing: .08em; color: var(--gold-ink); }
.ex-grp .chips { margin-top: 7px; }

/* ---------- (D) sticky category rail — engraved index + pinned seam ---------- */
.catbar {
    position: sticky; top: 0; z-index: 35;
    background: color-mix(in srgb, var(--bg) 88%, transparent);
    -webkit-backdrop-filter: saturate(1.35) blur(10px);
            backdrop-filter: saturate(1.35) blur(10px);
    border-bottom: 1px solid color-mix(in srgb, var(--gold) 28%, var(--line-soft));
}
.catbar__inner { display: flex; align-items: stretch; gap: 12px; padding-top: 0; padding-bottom: 0; }
/* pinned, non-scrolling accent seam (survives after the toolbar scrolls away) */
.catbar__seam {
    flex: 0 0 auto; align-self: center;
    font-family: var(--serif); font-size: 1rem; letter-spacing: .02em;
    color: var(--gold);
    padding-right: 12px; border-right: 1px solid color-mix(in srgb, var(--gold) 26%, var(--line-soft));
}
.catnav {
    flex: 1 1 auto; min-width: 0;               /* correct horizontal-overflow guard */
    display: flex; gap: 2px; margin: 0; padding: 0;
    overflow-x: auto; scrollbar-width: none;
    -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain;
    /* pure-CSS "more to scroll" edge-fade hint */
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 16px, #000 calc(100% - 16px), transparent 100%);
            mask-image: linear-gradient(90deg, transparent 0, #000 16px, #000 calc(100% - 16px), transparent 100%);
}
.catnav::-webkit-scrollbar { height: 0; }

a.chip--cat {
    position: relative; flex: 0 0 auto;
    padding: 13px 15px 12px; text-decoration: none; white-space: nowrap;
    font-family: var(--serif); font-size: .82rem; font-weight: 600;
    letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
    transition: color var(--t);
}
/* cloisonné cell-wall hairline between labels */
a.chip--cat + a.chip--cat::before {
    content: ""; position: absolute; left: 0; top: 50%; transform: translateY(-50%);
    width: 1px; height: 16px; background: color-mix(in srgb, var(--gold) 40%, transparent);
}
a.chip--cat:hover { color: var(--text); }
a.chip--cat.is-active { color: var(--accent-ink); }
/* the one bold move: a thin gold rule that draws in under the active tab */
a.chip--cat::after {
    content: ""; position: absolute; left: 15px; right: 15px; bottom: 6px; height: 2px;
    background: var(--gold); border-radius: 2px;
    transform: scaleX(0); transform-origin: left center; transition: transform var(--t);
}
a.chip--cat.is-active::after { transform: scaleX(1); }
a.chip--cat:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; border-radius: var(--r-sm); }

/* tapped category headings clear the sticky bar (matches the bar height) */
[id^="cat-"] { scroll-margin-top: 66px; }

/* Desktop: show ALL categories at once (wrap instead of horizontal scroll) so none stay
   hidden off-screen; the phone keeps the compact single scrolling rail. */
@media (min-width: 861px) {
    .catbar__seam { display: none; }   /* reclaim width so all category chips fit in two rows */
    .catnav { flex-wrap: wrap; overflow-x: visible; -webkit-mask-image: none; mask-image: none; gap: 3px 4px; padding: 3px 0; }
    a.chip--cat + a.chip--cat::before { display: none; }
    a.chip--cat { padding: 6px 11px; font-size: .74rem; letter-spacing: .05em; }
    .mcat, [id^="cat-"] { scroll-margin-top: 92px; }   /* clear the wrapped sticky category bar */
}

/* mobile — flawless down to 320px */
@media (max-width: 560px) {
    .menu-tools { padding: 8px 0 8px; }
    .m-card { padding: 9px 12px; gap: 8px; border-radius: var(--r-sm); }
    /* SCHLANKER: Tag/Nacht ist in den Kopf gewandert, also trägt die erste Zeile nur noch Suche
       UND Diät nebeneinander (nicht mehr die Diät auf einer eigenen, zentrierten Zeile). Passt
       die Diät nicht daneben, bricht `.m-bar` (flex-wrap) sie um — der Allergen-Ausschluss steht
       darunter. Zwei knappe Zeilen statt drei hoher. */
    .m-diet { flex: 0 1 auto; order: 0; justify-content: flex-start; }
    .m-diet .chip { padding: 8px 12px; }
    .catbar__inner { gap: 10px; }
}
@media (max-width: 360px) {
    .m-diet .chip { padding: 11px 10px; font-size: .8rem; }
    .ex-chip { font-size: .8rem; }
    .catbar__seam { display: none; }   /* reclaim horizontal rail room at the 320px floor */
}
@media (prefers-reduced-motion: reduce) {
    .m-search, .m-field__icon, .m-diet .chip, .m-allergen__chev, .m-allergen__toggle,
    .ex-chip, a.chip--cat, a.chip--cat::after { transition: none; }
}

.menu-list { padding: 24px 0 8px; }
.mcat { padding: 14px 0; scroll-margin-top: 66px; }
.mcat__title { font-size: clamp(1.5rem, 3vw, 2rem); padding-bottom: 8px; position: relative; margin-bottom: 6px; }
/* Kleiner als die Überschrift, damit das Bild die Rubrik begleitet statt sie anzuführen. */
.mcat__icon { font-size: .8em; margin-right: .35em; vertical-align: baseline; }
.mcat__title::after { content: ""; position: absolute; left: 0; bottom: 0; width: 44px; height: 3px; background: var(--gold); border-radius: 2px; }
/* Multi-column dish grid: the long card reads compact & organised, like a printed menu.
   1 column on phones, 2 on tablets, up to 3 on desktop (auto-fill). */
.mcat__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 340px), 1fr)); column-gap: clamp(20px, 3.4vw, 46px); }
@media (max-width: 560px) { .mcat__grid { grid-template-columns: 1fr; } }
.mdish {
    display: flex; gap: 16px; align-items: flex-start; padding: 18px 2px;
    border-bottom: 1px solid var(--line-soft);
    /* THE SAME GUARD `.mdish__body` CARRIES, one level up — and the level that was missing.
       A grid item's default `min-width: auto` refuses to shrink below its content, so a card
       whose dish name is long ("Cantine San Marzano Primitivo Sessantanni", straight out of a
       real wine list) measured 372px inside a 316px track and pushed the whole document 34px
       sideways on a 375px phone. On the one page a guest opens at the table. */
    min-width: 0;
}
/* German compounds and Italian wine names both do this; neither is a mistake to design around
   with `hidden`, which would cut the name in half rather than wrap it. */
.mdish__name, .mdish__desc { overflow-wrap: anywhere; }
.mdish__photo { width: 84px; height: 84px; object-fit: cover; border-radius: 13px; flex: 0 0 auto; box-shadow: var(--shadow-sm); }
.mdish__body { flex: 1 1 auto; min-width: 0; }
.dishcard__photo { width: 100%; height: 168px; object-fit: cover; border-radius: 12px; margin-bottom: 14px; display: block; }
/* THE SIZING CLASS AND THE TILE ARE ON THE SAME ELEMENT, and the sizing class was winning.
   dishthumb.blade.php writes `class="dishcard__photo ph-tile"`; both selectors are one class
   deep, so the later `display: block` above beat `.ph-tile { display: flex }` and the tile
   stopped centring anything. Measured at 375px on the front page ("Empfehlungen des Hauses",
   four cards with no photograph — day one for every restaurant here): tile 289×168, computed
   display `block`, the monogram at offset 0 / −6 where centred is 127 / 58. The letter hung
   6px ABOVE the top edge of its own tile, with 168px of empty stone underneath it.
   `.mdish__photo` never showed it: that class declares no display, so flex survived there and
   the Speisekarte looked correct while the homepage did not. Compound selector rather than
   `!important`, and it belongs next to the rule it answers. */
.dishcard__photo.ph-tile { display: flex; }
.mdish.is-soldout { opacity: .58; }
/* name · · · · price on one baseline; price is flush-right on every card so the whole
   price column lines up cleanly, like a printed menu. */
.mdish__head { display: flex; align-items: baseline; gap: 6px; }
/* `hyphens: manual` HOLT DIE SILBENTRENNUNG AUS DEM NAMEN. Ererbt von `h1…h4 { hyphens: auto }`
   brach ein Name in einer schmalen Spalte silbenweise («Gebra-tene Au-bergi-nen»); der Name
   soll an Wortgrenzen umbrechen, nicht mitten im Wort. `overflow-wrap` (eine Zeile höher) bleibt
   als Überlauf-Sicherung für das eine Wort, das breiter ist als seine Spalte. */
.mdish__name { font-size: 1.24rem; font-family: var(--serif); margin: 0; hyphens: manual; }
.mdish__lead { flex: 1 1 auto; min-width: 16px; height: 0; border-bottom: 2px dotted var(--line); position: relative; top: -.26em; opacity: .7; }
.mdish__price { flex: 0 0 auto; font-family: var(--serif); font-weight: 600; color: var(--accent-ink); font-size: 1.16rem; text-align: right; white-space: nowrap; }
.mdish__var { display: block; font-size: .92rem; line-height: 1.45; }   /* each drink size on its own line so long variant prices never overflow the cell */
.soldout { font-family: var(--sans); font-size: .64rem; text-transform: uppercase; letter-spacing: .05em; background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent-ink); padding: 2px 8px; border-radius: 999px; margin-left: 8px; vertical-align: middle; }
.mdish__desc { color: var(--muted); font-size: .96rem; margin: 6px 0 0; line-height: 1.55; }
.mdish__tags { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 9px; }
/* `inline-block`, UND DAS WAR EIN GEMESSENER SCHÖNHEITSFEHLER MIT EINEM EINZIGEN WORT ALS
   URSACHE. Diese Regel wurde für ein `<span>` in der Zeile eines Gerichts geschrieben — dort
   umschliesst sie die Buchstaben. Vier Vorlagen schreiben sie aber als `<p>` (die Karte auf der
   Startseite, das Tagesangebot, die SEO-Seite): ein Blockelement, also ein gerahmter Kasten
   über die volle Kartenbreite mit zwei Buchstaben darin, der auf jedem Screenshot wie ein
   leeres Eingabefeld aussieht. Auf der Startseite steht er unter jedem Preis.
   Die `<span>`-Verwendungen sind bereits inline; für sie ändert sich nichts. */
.codes { display: inline-block; font-size: .72rem; color: var(--muted); border: 1px solid var(--line); padding: 2px 7px; border-radius: 6px; letter-spacing: .05em; }
.codes--add { font-style: italic; }
@media (max-width: 560px) {
    /* ⛔ DAS FOTO IST DAS, WAS EIN GERICHT VERKAUFT, und es war das schwächste Element der
       Zeile. 92 statt 74 — bezahlt aus dem Abstand daneben (13 → 12) und aus dem Innenabstand
       (15 → 14), damit die Zeile davon NICHT höher wird. Nachgemessen: Medianhöhe 197px statt
       204px, und die längste Zeile fiel von 311px auf 245px. */
    .mdish__photo { width: 92px; height: 92px; }
    .mdish { gap: 12px; padding: 14px 2px; position: relative; }

    /* AUF DEM TELEFON GEHÖRT DEM NAMEN DIE ERSTE ZEILE. Zwischen Foto und Rand bleiben ~250px;
       Name, Führungspunkte, Preis UND der «+»-Knopf nebeneinander pressten den Namen wieder in
       eine Restspalte. Der Name nimmt jetzt die ganze Breite (`flex-basis: 100%` erzwingt den
       Umbruch), die Führungspunkte entfallen (bei vollbreitem Namen verbinden sie nichts). */
    .mdish__head { flex-wrap: wrap; align-items: center; row-gap: 2px; }
    .mdish__name { flex: 1 1 100%; }
    .mdish__lead { display: none; }

    /* ⛔ DER PREIS STEHT LINKS UNTER DEM NAMEN, NICHT MEHR RECHTS AM RAND.
       Hier stand `margin-left: auto`, und die Begründung dafür war die gedruckte Karte, in der
       eine Preisspalte fluchtet. Auf dem Telefon fluchtet aber nichts: es gibt nur EINE Spalte,
       der Name steht links, und der Preis sprang über die halbe Zeile ans andere Ende —
       gemessen 190px vom Namensanfang entfernt, direkt neben den «+»-Knopf, der damals oben
       rechts sass. Er las sich dadurch wie dessen Beschriftung.
       Unter dem Namen gehört er zu dem, was er kostet. */
    .mdish__price { margin-left: 0; text-align: left; }

    /* ⛔ DER KNOPF WIRD AUS DEM FLUSS GENOMMEN, UND DAS IST DER TEIL, DEN ICH ERST GEMESSEN
       HABE, NACHDEM ICH IHN FALSCH GEMACHT HATTE.
       Erster Entwurf: Knopf in den Kopf, Kopf umbrechen lassen. Der Name gewann eine Zeile
       (drei → zwei), aber der Knopf öffnete mit seinen 44px eine EIGENE Reihe. Nachher
       gemessen: die Zeile wuchs von 202px auf 214px, es passten 3,9 statt 4,1 Gerichte auf
       einen Schirm. Die Änderung war eine Verschlechterung, und die Zahl hat es gesagt, nicht
       das Auge. Absolut positioniert kostet er keine Höhe und sitzt unten rechts, auf Höhe der
       Abzeichen. `padding-right` auf den Abzeichen, damit sie nicht unter ihm durchlaufen. */
    .has-cart .mdish__add { position: absolute; right: 2px; bottom: 14px; margin: 0; }
    .mdish__tags { padding-right: 52px; }
}
/* Measured 1036px wide on the vegan page — a centred italic apology running the full width of
   `.wrap`, which is the one shape that makes a short sentence read as an error message. This
   is the state a restaurant is in on day one, on the homepage, the menu and the vegan page at
   once, so it earns a measure of its own. 46ch rather than 66: it is always a sentence or two,
   and centred text needs a shorter line than left-aligned text to keep its ragged edges calm. */
.m-empty { text-align: center; color: var(--muted); padding: 40px 0; font-style: italic; max-width: 46ch; margin-inline: auto; }

.legend { padding: 18px 0 40px; }
.legend details { border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface); }
.legend summary { cursor: pointer; padding: 14px 16px; font-weight: 600; }
.legend__grid { display: grid; grid-template-columns: 1fr; gap: 20px; padding: 4px 16px 18px; }
@media (min-width: 620px) { .legend__grid { grid-template-columns: 1fr 1fr; } }
/* ⛔ `--gold-ink` UND NICHT `--gold`: DIESE ZEILE IST TEXT.
   `tokens.css` schreibt es aus — «--gold is 3.33:1 on the paper — correct for a rule, an
   underline or a border, where 3:1 is the bar, and NOT correct for text. It was being used for
   both.» Gemessen auf `--surface`: #9A7B4F auf #FBF9F5 = 3,75:1, verlangt sind 4,5:1.
   `.accent-script` wurde damals umgestellt, diese Überschrift blieb stehen — und sie steht
   ausgerechnet über der Allergenlegende, also über dem Text, den ein Gast mit einer Allergie
   liest. Bei Nacht fiel es nicht auf: dort ist `--gold` helles Messing und besteht. */
.legend__grid h4 { font-size: .8rem; text-transform: uppercase; letter-spacing: .06em; color: var(--gold-ink); margin-bottom: 8px; }
.legend__grid ul { list-style: none; margin: 0; padding: 0; font-size: .85rem; color: var(--muted); }
.legend__grid li { margin-bottom: 4px; }
.legend__grid b { color: var(--text); }
.legend__grp { color: var(--c-text); margin-bottom: 6px; }
.legend__grp > ul { margin-top: 3px; padding-left: 12px; border-left: 1px solid var(--c-line); }


/* ============================================================
   PHOTO PAGES — Galerie / Feiern-Catering / Home / Über uns
   (owner-supplied photography; classes namespaced .gal-/.cater-/.home-/.abt-)
   ============================================================ */

/* === Über uns — photo figures (prefix: .abt-) === */
.abt-figure {
    margin: 22px 0 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.abt-figure__img {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 10;
    object-fit: cover;
    object-position: center;
    border-radius: var(--r);
    border: 1px solid var(--line-soft);
    box-shadow: var(--shadow);
    background: var(--surface);
}
.abt-figure__cap {
    font-family: var(--serif);
    font-size: 0.9rem;
    line-height: 1.5;
    color: var(--muted);
}
.abt-figure__cap .accent-script {
    color: var(--gold-ink);
    font-style: normal;
}
@media (max-width: 760px) {
    .abt-figure {
        margin-top: 18px;
    }
    .abt-figure__img {
        aspect-ratio: 4 / 3;
    }
}

/* Lead photo under a text hero (Vegan, Kontakt, ...). */
.leadphoto { padding: clamp(20px, 4vw, 44px) 0 0; }
.leadphoto figure { margin: 0; }
.leadphoto__img { width: 100%; height: auto; display: block; aspect-ratio: 16 / 9; object-fit: cover; border-radius: var(--r); border: 1px solid var(--line-soft); box-shadow: var(--shadow); }
.leadphoto figcaption { margin-top: 11px; font-family: var(--serif); font-size: .92rem; color: var(--muted); }
.leadphoto figcaption .accent-script { color: var(--gold-ink); }
@media (max-width: 560px) { .leadphoto__img { aspect-ratio: 4 / 3; } }

/* `.brandsig` — the logo on a cream plate, as a section of its own — is deleted. It never had
   markup, and the header already carries the wordmark on every page; a second, larger copy of
   it mid-page is a band that says nothing a guest did not already know. */

/* ---- Feiern & Catering: photographic hero + gallery (.cater-) ---- */

/* Photographic hero band */
.cater-hero{
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.cater-hero__img{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 60%;
  z-index: 0;
}
/* THE SCRIM IS A CONTRAST GUARANTEE, NOT A MOOD. Its old shape was .42 at the top easing to
   .30 at 40% — lightest exactly where the H1 sits — and the H1 is pinned to #fff below. On a
   restaurant with no photograph at all, `.cater-hero` rendered with the paper background
   showing through and the title measured 1.14:1 against it in a browser. White on cream. The
   heading of the page with the highest basket value on the site was invisible.

   Two fixes, and the template carries the important one: `.cater-hero` is now only rendered
   when there IS a photograph, so the pinned white can never land on paper. This gradient is the
   second: its floor is .64, which is the alpha at which white still clears 4.5:1 against the
   worst case a guest can upload — a photograph that is pure white. Blended, .64 over #14100C
   gives #686866; white on that measures 5.5:1. Every darker photograph only improves it, so
   the guarantee does not depend on anybody's taste in pictures. */
.cater-hero__scrim{
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(180deg, rgba(20,16,12,.68) 0%, rgba(20,16,12,.64) 45%, rgba(20,16,12,.82) 100%),
    linear-gradient(90deg, rgba(20,16,12,.34) 0%, rgba(20,16,12,0) 70%);
}
.cater-hero__inner{
  position: relative;
  z-index: 2;
}
/* Text stays legible over the photo (light, theme-independent) */
.cater-hero .eyebrow{ color: rgba(255,255,255,.86); }
.cater-hero .hero__title{ color: #fff; }
.cater-hero .hero__intro{ color: rgba(255,255,255,.90); }
.cater-hero .accent-script{ color: var(--gold); }
/* `.cater-hero__accent` is deleted. It was a `z-index` and a 10%-white colour with no position,
   no size and no font — the residue of a watermark whose other half never got written — applied
   to the accent word, which then rendered at 10% opacity on top of the scrim and was
   indistinguishable from nothing. The accent word now uses `.eyebrow` + `.accent-script` like
   it does on every other hero, where it is legible and where one rule governs all of them. */
/* Ghost button needs light treatment over the dark scrim */
.cater-hero .btn--ghost{
  color: #fff;
  border-color: rgba(255,255,255,.55);
  background: transparent;
}
.cater-hero .btn--ghost:hover{
  border-color: #fff;
  background: rgba(255,255,255,.10);
}

/* Celebration gallery */
.cater-gallery{
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-flow: dense;
  gap: 14px;
}
.cater-shot{
  position: relative;
  margin: 0;
  border-radius: var(--r);
  overflow: hidden;
  box-shadow: var(--shadow-sm);
  border: 1px solid var(--line-soft);
  background: var(--surface);
}
.cater-shot img{
  display: block;
  width: 100%;
  height: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  object-position: center;
}
.cater-shot--wide{ grid-column: span 2; }
.cater-shot--wide img{ aspect-ratio: 16 / 9; }
.cater-shot__cap{
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 34px 16px 14px;
  color: #fff;
  font-family: var(--serif);
  font-size: .98rem;
  line-height: 1.3;
  background: linear-gradient(180deg, rgba(20,12,10,0) 0%, rgba(20,12,10,.72) 78%, rgba(20,12,10,.82) 100%);
}
.cater-shot__eyebrow{
  display: block;
  margin-bottom: 3px;
  font-family: var(--sans);
  font-size: .70rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--gold);
}

@media (max-width: 860px){
  .cater-gallery{ grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 520px){
  .cater-gallery{ grid-template-columns: 1fr; }
  .cater-shot--wide{ grid-column: auto; }
  .cater-shot--wide img{ aspect-ratio: 4 / 3; }
}

/* `.scenery-band` — a full-bleed landscape with a headline over it — is deleted, and it is the
   deletion most likely to be argued with, so: it is a 480px-tall band whose entire content is a
   photograph. On a restaurant with one good picture of the room, spending that picture on
   decoration leaves the pages that need it — Über uns, the catering page — with nothing, and on
   a restaurant with none it is 480px of dark plate carrying a sentence. Both demo restaurants
   are in the second state today. It also hardcoded /assets/img/scenery.webp, a file this
   repository does not contain. A photograph earns its place next to something it explains. */

/* ===== Home: Feiern & Catering teaser ===== */
/* height:auto is a fix, not a tweak. The width/height attributes on an <img> map to CSS width
   and height as PRESENTATIONAL HINTS; setting only width in CSS leaves the height hint alive,
   where it outranks aspect-ratio and stretches the photo into a tall strip beside a short text
   column. Anywhere an <img> carries both attributes and the stylesheet sets width, set height. */
.home-feiern__figure { margin: 0; display: grid; gap: 12px; }
.home-feiern__img {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    object-position: center;
    border-radius: var(--r);
    box-shadow: var(--shadow-sm);
}
/* A second room photo is optional. When one is supplied the pair goes side by side in portrait,
   which fills the column height without any single image having to stretch; with only one
   photo the layout must stay a single 4:3 frame, hence :has() rather than a flat two-column. */
.home-feiern__img--sm { display: none; }
@media (min-width: 900px) {
    .home-feiern__figure:has(.home-feiern__img--sm) { grid-template-columns: 1fr 1fr; }
    .home-feiern__figure:has(.home-feiern__img--sm) .home-feiern__img { aspect-ratio: 3 / 4; }
    .home-feiern__img--sm { display: block; }
}

/* `.section--photo` is deleted, and it is a TRAP rather than merely unused. It was driven by
   `style="--bg:url('…')"`, and `--bg` is this design system's semantic name for the page
   background — tokens.css line 40, read by `body` and by every component that paints a surface.
   Setting it to a photograph on a section makes that photograph the background of everything
   nested inside: a `.dishcard` in that band would have inherited `--bg` and painted the picture
   inside the card. Nothing rendered it, so nobody ever saw that happen. A component that
   overwrites a semantic token cannot be fixed by using it carefully. */

/* `.home-intern` — a "Mitarbeiter-Login" strip at the foot of the homepage — is deleted along
   with `.header-login`. A guest deciding where to eat has no use for a staff door, and putting
   one on the front page is the single clearest signal that a site was built by somebody
   thinking about the software rather than about the restaurant. The panel is one hostname away
   and the people who need it have it bookmarked. */

/* Galerie (.gal-) */
/* `padding: 0` because this is a <section> and the element rule at the top of this file gives
   every section 44–88px. The band's own breathing room is on `.gal-hero__inner`; with both in
   force the photograph grew to 700px before its first line of type. */
.gal-hero{position:relative;overflow:hidden;isolation:isolate;min-height:clamp(320px,52vh,520px);display:flex;align-items:flex-end;padding:0;}
.gal-hero__img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center 62%;z-index:-2;}
/* Measured, not eyeballed. `.gal-hero` is `align-items: flex-end`, so its text block starts at
   about 54% of the band's height — where the old stops (.28 → .52 at 58% → .80) put roughly .50
   of scrim. Against the demo photograph the title measured 14.3:1, but against the worst
   photograph a restaurant can upload — a near-white one — it was 4.91:1 at the title, and below
   the line at the eyebrow above it, which is set at 85% white to start with. Floor raised to .62
   across the text zone, the same guarantee `.cater-hero__scrim` carries; the top stop stays low
   so the photograph still reads as a photograph rather than as a tinted panel. */
.gal-hero__scrim{position:absolute;inset:0;z-index:-1;background:linear-gradient(180deg,rgba(24,12,14,.40) 0%,rgba(24,12,14,.62) 52%,rgba(24,12,14,.86) 100%);}
.gal-hero__accent{position:absolute;right:-.04em;bottom:-.22em;z-index:-1;font-family:var(--serif);font-size:clamp(5rem,20vw,15rem);line-height:.8;color:rgba(255,255,255,.10);pointer-events:none;user-select:none;white-space:nowrap;}
.gal-hero__inner{padding-block:clamp(2rem,6vw,4rem);}
.gal-hero .eyebrow{color:rgba(255,255,255,.85);}
.gal-hero .eyebrow .accent-script{color:var(--gold);}
.gal-hero__title{color:#fff;margin:.15em 0 .35em;}
.gal-hero__intro{color:rgba(255,255,255,.92);max-width:56ch;}
.gal-feature{display:grid;grid-template-columns:1.02fr .98fr;gap:clamp(1.4rem,4vw,3rem);align-items:center;}
.gal-feature__media{margin:0;border-radius:var(--r);overflow:hidden;box-shadow:var(--shadow);border:1px solid var(--line-soft);background:var(--surface);}
/* `height: 100%` MEASURED 2px. Its parent is a grid item under `align-items: center`, so the
   figure's height is content-driven — and the content is a lazily loaded <img> with no width or
   height attributes, which occupies nothing until it arrives. 100% of nothing is nothing: the
   whole band laid out 175px tall, the photograph appeared, and everything below it jumped.
   `aspect-ratio` reserves the box from the first paint. Every other photo component in this
   file already declares one; this was the one that did not. */
.gal-feature__media img{display:block;width:100%;height:auto;aspect-ratio:4 / 3;object-fit:cover;transition:transform var(--t) ease;}
.gal-feature__media:hover img{transform:scale(1.03);}
.gal-feature__text p:first-child{margin-top:0;}
.gal-masonry{columns:3 260px;column-gap:18px;}
.gal-figure{break-inside:avoid;-webkit-column-break-inside:avoid;margin:0 0 18px;border-radius:var(--r);overflow:hidden;background:var(--surface);border:1px solid var(--line-soft);box-shadow:var(--shadow-sm);}
.gal-figure img{display:block;width:100%;height:auto;transition:transform var(--t) ease;}
.gal-figure:hover img{transform:scale(1.035);}
.gal-figure figcaption{padding:.65rem .85rem .8rem;font-family:var(--sans);font-size:.86rem;line-height:1.45;color:var(--muted);}
.gal-figure figcaption b{display:block;margin-bottom:.12rem;font-family:var(--serif);font-weight:600;font-size:1rem;color:var(--text);}
.gal-figure figcaption .accent-script{color:var(--gold-ink);}
/* `.gal-cta` (a centred call-to-action band) and `.gal-feature--reverse` (alternating photo
   sides) are deleted. Neither had markup: the gallery promotes exactly ONE photograph into a
   feature block, so there is no second one to alternate with, and its call to action lives
   inside that block where the eye already is rather than in a band of its own. Both come back
   the day a restaurant has enough photographs to justify a second feature — with markup. */
@media (max-width:760px){.gal-feature{grid-template-columns:1fr;}}
@media (max-width:560px){.gal-masonry{columns:1;}}
@media (prefers-reduced-motion:reduce){.gal-figure img,.gal-feature__media img{transition:none;}.gal-figure:hover img,.gal-feature__media:hover img{transform:none;}}

/* ============================================================================
   HERO
   ----------------------------------------------------------------------------
   ONE first screen. The type set, the tonality and the restaurant's own colour
   are injected per restaurant by /assets/brand.css; everything here is shared,
   and that is the product decision: the same design at every restaurant, so an
   improvement made once is an improvement everywhere.

   `.hero` USED TO BE DECLARED TWICE — once here and once two hundred lines up,
   with different padding and only the earlier one carrying `overflow:hidden`.
   The later block won, so the watermark's overflow guard was silently not in
   effect. Merged.
   ========================================================================== */

.hero { position: relative; overflow: hidden; padding: clamp(56px, 9vw, 112px) 0; }
.hero__inner { display: flex; flex-direction: column; align-items: flex-start; gap: 18px; }
.hero__title { margin: 0; font-family: var(--serif); font-weight: 600; line-height: 1.02;
    font-size: clamp(2.4rem, 6vw, 4.2rem); letter-spacing: -.015em; }
/* `.hero__accent` and `.hero__tagline` deleted. Both are survivors of the three merged hero
   variants: the accent word is `.eyebrow > .accent-script` on every hero that renders, and the
   sentence under the title is `.hero__intro`. Two names for one element is how the wrong one
   ends up in a new template. */
.hero__status { margin: 4px 0 0; display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.hero__actions { margin: 10px 0 0; display: flex; gap: 12px; flex-wrap: wrap; }


/* -- The status word ------------------------------------------------------- */
.status { font-family: var(--sans); font-size: .82rem; font-weight: 600;
    letter-spacing: .06em; text-transform: uppercase; padding: 5px 11px; border-radius: 999px; }
/* MEASURED IN A BROWSER, ON A REAL RESTAURANT: «Jetzt geöffnet» came out at 1.39:1.
   It sits inside `.hero--foto`, i.e. on the near-black plate — and it was painted in the
   restaurant's OWN brand colour, tinted 14% of itself. Pinocchio's is #023d21, a very dark
   green, so the one sentence this component exists for was, in practice, not there. The
   CLOSED state used the light `--c-stone-2` and was the loud one: the badge was backwards.

   These two were the only rules on the whole guest side reading the RAW `--c-accent` instead
   of the semantic `--accent`, which is how they escaped the dark-hero treatment everything
   else in this block already had. Now they read the on-dark tokens like their neighbours.

   `--footer-open` is reused rather than a fourth green invented: it is the token that already
   answers «open, on a dark ground», and it computes to 9.4:1 here. */
.status--open { background: color-mix(in srgb, var(--footer-open) 18%, transparent);
    color: var(--footer-open); }
.status--closed { background: color-mix(in srgb, var(--on-dark) 12%, transparent);
    color: var(--on-dark-soft); }
.status__note { font-size: .88rem; color: var(--on-dark-soft); }

/* THE PREFERENCE COVERED FOUR BLOCKS AND MISSED THE MOST VISIBLE MOTION ON THE SITE: every
   card and every button lifted on hover, and the mobile panel slid in, regardless. A reader who
   has asked their operating system for less movement was getting all of it. */
@media (prefers-reduced-motion: reduce) {
    .hero * { transition: none !important; animation: none !important; }
    .btn:hover, .dishcard:hover, .rg-card:hover, .wa-fab__btn:hover { transform: none !important; }
    .nav, .nav-backdrop, .searchbox__input, body { transition: none !important; }
}

/* On a phone the fixed bar at the bottom already offers Anrufen and WhatsApp, so the hero's
   telephone button is the same affordance twice on one screen. The primary action stays. */
@media (max-width: 640px) {
    .hero__actions .btn--ghost { display: none; }
}

/* The accent word keeps its own treatment wherever it appears.
   `.eyebrow` uppercases its contents, which is right for a label like "SEIT 1998" and wrong
   for the one element carrying the restaurant's identity: Trattoria became TRATTORIA on the
   404 while staying Trattoria in the hero. Each typeface set decides how that word is set —
   Handwerk uppercases it deliberately, Klassik and Tafel do not — and a wrapper must not
   overrule the decision. */
.eyebrow .accent-script { text-transform: none; letter-spacing: inherit; }

/* ============================================================
   THE FIRST SCREEN — one variant, bounded height.

   What this replaces measured 1099px in a browser against the reference design's 533px: a
   variant carrying `padding-block: clamp(72px,13vw,168px)` under a title set at
   `clamp(3rem,11vw,7.5rem)`, so the height was whatever the content happened to add up to.
   `min-height` with a ceiling decides it now, and the content sits at the bottom of it.

   `min-height` and `justify-content:flex-end` HAVE TO ARRIVE TOGETHER. Either alone gives a
   tall box with the text stranded in the middle of it.
   ============================================================ */
.hero--foto {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    /*
     * ⛔ 74vh WAR AUF DEM TELEFON EIN GANZER BILDSCHIRM, und bei einem Haus ohne Foto ein
     *    ganzer Bildschirm dunkler Fläche. Gemessen an einem echten Haus mit zwei Standorten:
     *    oben die Standortwahl, darunter diese Platte — ein Gast musste rund 1100 px scrollen,
     *    bevor das erste Gericht kam. Die Zahl steht jetzt so, dass die Kante des nächsten
     *    Bandes am unteren Rand mitsieht; ein Bildschirm, dessen Boden man sieht, wird
     *    weitergescrollt, einer ohne Boden sieht aus wie die ganze Seite.
     *
     * ⚠️ DER DECKEL BLEIBT. `min-height` mit Obergrenze UND `justify-content: flex-end` gehören
     *    zusammen — das steht im Kopf dieses Blocks und gilt unverändert.
     */
    min-height: clamp(400px, 58vh, 560px);
    padding-block: clamp(40px, 6vw, 72px) clamp(30px, 5vw, 58px);
    /*
     * THE DAY-ONE STATE OF EVERY RESTAURANT ON THIS PLATFORM, and it was a flat near-black
     * rectangle up to 640px tall — the least designed surface on the site, shown to the customer
     * on the day they are deciding what they think of us. Most houses arrive with no usable
     * photograph; the fallback is not an edge case, it is the first impression.
     *
     * Two gradients and no image: a warm pool behind the type where the eye lands, and a cool
     * fall to the corner. Nothing moves, nothing loads.
     */
    background:
        radial-gradient(120% 80% at 18% 88%, color-mix(in oklab, var(--c-accent) 34%, transparent) 0%, transparent 62%),
        radial-gradient(90% 70% at 92% 8%, color-mix(in oklab, var(--c-brass) 20%, transparent) 0%, transparent 58%),
        var(--dark-plate);
    color: var(--on-dark);
}
/* The photograph arrives as a custom property from the template, never as a path written into
   this file: twenty restaurants sharing one hardcoded /assets/img/hero.webp is twenty
   restaurants with the same photograph. */
/* ⛔ DER SCHLEIER WAR SO DICHT, DASS DAS FOTO NICHT MEHR ZU SEHEN WAR.

   `--scrim-64` über `--scrim-86` lässt im Mittel rund ein Viertel des Bildes durch. Gemessen am
   ersten echten Hausfoto (mittlere Helligkeit 122 von 255) kommen davon etwa 30 an — ein
   schwarzes Rechteck. Der Kopf sah mit Foto genauso aus wie ohne, nur ohne das Wasserzeichen,
   das bei einem Haus ohne Foto an seiner Stelle steht.

   Jetzt ein eigener Verlauf: oben leicht, unten dicht. Der Titel steht unten links, also liegt
   die Deckung dort, wo Text ist, und das Bild bleibt oben sichtbar.

   ⛔ ALS EIGENE VARIABLE, WEIL ES DEN SCHLEIER ZWEIMAL GIBT. Ohne Skript malt ihn diese Regel;
      mit Skript wandert das Foto auf `::before` (man kann einen Hintergrund nicht
      transformieren) und der Schleier auf `::after`. Beim ersten Versuch habe ich nur die
      Regel hier geändert — und im Browser blieb alles schwarz, weil dort `.js-motion` gilt und
      `::after` weiter die alten Tokens trug. Eine Farbe, zwei Maler: eine Quelle. */
:root { --hero-schleier: linear-gradient(rgba(20, 24, 28, .34) 0%, rgba(20, 24, 28, .62) 55%, rgba(20, 24, 28, .82) 100%); }
.hero--foto.is-photo {
    background-image: var(--hero-schleier), var(--hero-photo);
    background-size: cover;
    background-position: center;
}
/* NO max-width HERE. Constraining the column to 34ch wrapped a restaurant's own name across
   three lines at 60px and pushed the two buttons onto a second row — 603px of content inside a
   533px frame. The measure belongs on the PARAGRAPH, which is the only thing that reads badly
   when it runs long; a title and a pair of buttons want the width they were given. */
.hero--foto .hero__inner { display: flex; flex-direction: column; gap: 16px; }
.hero--foto .hero__title { color: var(--on-dark); font-size: clamp(2.4rem, 5.4vw, 3.8rem); }
.hero--foto .hero__intro { color: var(--on-dark-soft); max-width: 46ch; }
.hero--foto .eyebrow,
.hero--foto .accent-script { color: var(--on-dark-accent); }
.hero--foto .btn--ghost { color: var(--on-dark); border-color: var(--on-dark-soft); }

/* ============================================================
   RESERVIEREN — the floating invitation, and the booking form.

   THE BUTTON CAME BACK. The reference design carries a WhatsApp action that was lost in the
   port: `.wa-fab` on wide screens and `.mobilebar__wa` in the thumb bar. For a German
   restaurant WhatsApp is not a nice extra — it is where the booking conversation actually
   happens, and a guest who wants a table at seven tonight will send a message long before
   they fill in a form.

   `svg { flex: 0 0 auto }` IS LOad-BEARING. Inside a flex row an SVG with no intrinsic width
   collapses to nothing, and the button renders as a green circle with an invisible icon.
   ============================================================ */
/* `.mobilebar__wa` — a brand-green WhatsApp glyph in the thumb bar — is deleted. The row it sat
   in is labelled "Reservieren", not "WhatsApp": the label names the errand, so a green mark from
   a different company beside it points at the wrong noun. The bar is one colour now, and the
   icon uses `.mobilebar__ic` like its neighbours. */

/* ONE DECLARATION, and it used to be three. The block above said "wide screens only", a bare
   `.wa-fab { display: none }` came next, and then a THIRD copy re-declared `display: flex`
   unconditionally — so the last one won and the floating button sat on top of the phone's own
   thumb bar, two WhatsApp actions on one 375px screen with the fixed bar underneath it. The
   comment above the rules described the behaviour the rules had stopped having; measured at
   375px, the green circle was there. Phones get the thumb bar, wide screens get this. */
.wa-fab { display: none; }
@media (min-width: 861px) {
    .wa-fab {
        position: fixed; right: 22px; bottom: 22px; z-index: 35;
        display: flex; flex-direction: column; align-items: flex-end; gap: 10px;
    }
}
.wa-fab__note {
    margin: 0; max-width: 240px;
    background: var(--surface); color: var(--text);
    border: 1px solid var(--line);
    /* square corner bottom-right: the bubble points at the button below it */
    border-radius: 16px 16px 4px 16px;
    padding: 10px 14px; font-size: .88rem; line-height: 1.35;
    box-shadow: 0 12px 32px rgba(20, 16, 12, .18);
    /* No entrance animation, deliberately. A delayed fade-in was tried both ways: `forwards`
       from opacity:0 leaves the text invisible forever if the animation never runs, and
       `backwards` does the same because it holds the hidden first frame for the whole delay —
       a stalled animation clock (throttled background tab, headless engine) never ends it.
       Both fail silently, and the invitation is the entire point of this component. It is a
       small quiet bubble; it does not need a reveal to earn its place. */
}
.wa-fab__btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 56px; height: 56px; border-radius: var(--r-tag);
    /* THE GREEN STAYS — a German guest recognises it and that recognition is the whole point of
       the button. What goes is WhatsApp's own drop-shadow: a coloured glow under a floating
       circle is a 2019 growth-hacking artefact, and it was the loudest object on a premium
       page. A neutral shadow lets it sit ON the page instead of hovering above it. */
    background: #25D366; box-shadow: 0 8px 22px color-mix(in oklab, #000 22%, transparent);
    transition: box-shadow var(--t-fast), filter var(--t-fast);
}
.wa-fab__btn svg { width: 30px; height: 30px; fill: #fff; flex: 0 0 auto; }
.wa-fab__btn:hover { box-shadow: 0 10px 28px color-mix(in oklab, #000 30%, transparent); filter: brightness(1.06); }

/* The booking form. Deliberately plain: native date, time and select controls, because the
   guest most likely to use this is on a pavement with one bar of signal and a form that needs
   a framework to boot is a form that sometimes does not. */
.resform { margin-top: 8px; }
.resform__row { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; }
.resform__field { display: flex; flex-direction: column; gap: 6px; margin: 0 0 16px; }
.resform__field label { font-size: .92rem; color: var(--muted); }
.resform__field input,
.resform__field select,
.resform__field textarea {
    font: inherit; color: var(--text); background: var(--surface);
    border: 1px solid var(--line); border-radius: var(--r-sm);
    padding: 11px 12px; width: 100%;
}
.resform__field input:focus-visible,
.resform__field select:focus-visible,
.resform__field textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.resform__actions { display: flex; gap: 12px; flex-wrap: wrap; margin: 20px 0 12px; }
/* The invitation form reuses this family rather than owning a second one. Two things it needs
   that a booking form does not: a hint under a field, and a validation message.
   THE ERROR IS NOT COLOUR ALONE. `--wine` carries it, and so does the weight and the leading
   rule — colour on its own is invisible to a red-green colourblind owner, who is the exact
   person setting a password on the one page where getting it wrong locks them out. */
.resform__hint { font-size: .86rem; color: var(--muted); line-height: 1.45; }
/* DER WARTELISTEN-SATZ STEHT IM ABSAGEKASTEN, unter dem «wir sind ausgebucht». Er ist bewusst
   ruhiger als die Absage darüber — die Absage ist die Nachricht, das hier ist der Ausweg, und
   zwei fette Zeilen übereinander lesen sich wie zwei Probleme. Der Abstand nach unten trennt
   ihn vom Knopf, damit der Satz nicht wie dessen Beschriftung wirkt. */
.resform__warteliste { font-size: .92rem; color: var(--muted); line-height: 1.5; margin: 10px 0 14px; }
.resform__error {
    font-size: .88rem; font-weight: 600; color: var(--wine);
    border-left: 3px solid var(--wine); padding-left: 10px;
}

/* ZUSAMMENGEKLAPPT AUF DAS, WAS DER GAST GEWÄHLT HAT.
   `is-korb` setzt `cart.js`, nachdem es die Mengenfelder gefüllt hat. Die übrigen Zeilen bleiben
   im Formular und werden mit Menge 0 mitgeschickt — versteckt wird die ANZEIGE, nicht die Zeile.
   Ohne Skript trägt das Formular diese Klasse nie und die ganze Karte steht da.

   ⚠️ DIE SEITENLEISTE UND DER STREIFEN GEHEN MIT. Beide sind Werkzeuge zum AUSSUCHEN — eine
      Rubrikenliste über vier gewählten Gerichten wäre ein Wegweiser durch eine Karte, die gerade
      nicht da ist, und «Am beliebtesten» wäre eine Empfehlung an jemanden, der schon gewählt hat. */
.resform.is-korb .orderline:not(.is-gewaehlt) { display: none; }
.resform.is-korb .mcat:not(:has(.is-gewaehlt)) { display: none; }
.resform.is-korb .catbar,
.resform.is-korb .laden__seite,
.resform.is-korb .beliebt { display: none; }
.resform__mehr { margin: var(--s-3) 0; }

/* The honeypot. NOT `display:none` — bots have known about that for fifteen years. Off-screen,
   out of the tab order, and carrying a label that tells a screen reader to leave it alone. */
.resform__trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* TOUCH TARGETS, MEASURED ON A 375px VIEWPORT.
   The menu button was 38 × 38, the menu-page controls 42 × 42 and the diet chips 42 tall — all
   under the 44 × 44 a thumb needs (WCAG 2.5.5). These are not decorative: the first is the ONLY
   way into navigation on a phone, and the others are the filters a guest with an allergy uses
   standing at a table. Applied here rather than on each rule so a new control inherits it.
   Desktop keeps the tighter sizes: a mouse is precise and 44px buttons look clumsy at 1280. */
@media (max-width: 860px) and (pointer: coarse), (max-width: 560px) {
    .iconbtn, .iconbtn2 { min-width: 44px; min-height: 44px; }
    .m-diet .chip, .chip--cat { min-height: 44px; }
    /* ⛔ `.ex-chip` FEHLTE HIER, und der Kommentar oben nennt genau diese Chips: «die anderen
       sind die Filter, die ein Gast mit einer Allergie benutzt, während er an einem Tisch
       steht». Gemessen 37,6px (13,12px × 1,65 Zeilenkasten + 14px padding + 2px Rahmen), bei
       360px sogar 37,1px — neben den Diät-Chips derselben Zeile, die 44px haben. */
    .ex-chip { min-height: 44px; }
    /* The wordmark is a link to the front page; 36px tall is a target you miss on a tram. */
    .brand { min-height: 44px; }

    .footer-links a, .footer-legal a, .footer-kontakt a, .breadcrumb a {
        display: inline-flex; align-items: center; min-height: 44px;
    }
    /* Mit 44px hohen Zeilen sitzt das Zeichen in der Mitte der Zeile, nicht an ihrer ersten Textzeile. */
    .footer-kontakt li { align-items: center; }
    .footer-kontakt svg { margin-top: 0; }
}


/* ============================================================
   THE 2026 PLATFORM LAYER — four browser features, no markup, no JavaScript.

   Every rule below is additive: an engine that does not know the feature ignores the block and
   the page is exactly what it was. That is the whole reason these are worth having on a site
   whose guests are on whatever phone they happen to own — the modern feel is free, and its
   absence costs nothing.

   NONE OF IT IS DECORATION FOR ITS OWN SAKE. Each one answers something the audit measured.
   ============================================================ */

/* -- 1 · Cross-document view transitions ------------------------------------
   A restaurant site is nine pages a guest walks through: Startseite → Speisekarte → Reservieren.
   Each of those was a white flash and a jump to the top. This is the single change that makes a
   server-rendered site feel like it was built this year, and it is four lines.

   The header and the name are named so they PERSIST across the navigation instead of fading —
   the masthead stays put and the page beneath it changes, which is what the guest expects to
   happen and what a full-page fade denies them. */
@view-transition { navigation: auto; }

::view-transition-old(root) { animation-duration: .18s; }
::view-transition-new(root) { animation-duration: .22s; }

.site-header { view-transition-name: masthead; }
.site-footer { view-transition-name: colophon; }
.brand__name { view-transition-name: house-name; }

/* -- 2 · Container queries --------------------------------------------------
   THE SAME CARD IN THREE DIFFERENT SLOTS. A dish card sits in a four-column grid on the
   homepage, in a two-column band on the Stadt page, and full width on a phone — and it was
   sized by the VIEWPORT, which knows none of that. At 1280px in a narrow column it kept the
   wide-screen padding and the 168px photograph, and looked cramped for reasons no breakpoint
   could see.

   Container queries let the component ask its own slot. Baseline since 2023 and the correct
   tool for exactly this. */
.dish-grid, .rg-cards, .cater-gallery { container-type: inline-size; }

@container (max-width: 300px) {
    .dishcard { padding: var(--s-4); }
    .dishcard__photo { height: 132px; }
    .dishcard__name { font-size: var(--fs-3); }
}

/* -- 3 · A quiet arrival ----------------------------------------------------
   Scroll-driven, so it costs no JavaScript and no timer: the band lifts the last few pixels as
   it enters, once, and never again. `animation-range` stops it finishing at the very bottom of
   the viewport, where a guest would never see it complete.

   ⚠️ INSIDE the reduced-motion guard, not beside it. Four hover transforms were outside that
   guard for a year — a reader who asked their system for less movement got all of it. Motion
   added in 2026 does not get to repeat that. */
@media (prefers-reduced-motion: no-preference) {
    @supports (animation-timeline: view()) {
        main > section {
            animation: band-arrive linear both;
            animation-timeline: view();
            animation-range: entry 0% entry 32%;
        }

        /* The first band is already on screen when the page loads; animating it means the guest
           watches their own homepage assemble, which is the opposite of premium. */
        main > section:first-child { animation: none; }
    }
}

@keyframes band-arrive {
    from { opacity: .4; transform: translateY(10px); }
    to { opacity: 1; transform: none; }
}

/* -- 4 · Long menus stay cheap ---------------------------------------------
   A Speisekarte imported from a PDF runs to 119 dishes. `content-visibility` lets the browser
   skip layout and paint for the sections below the fold until they are needed; the intrinsic
   size keeps the scrollbar honest so the page does not jump as they come in.

   Measured shape rather than a guess: `.mcat` is a heading plus a grid of rows, and 640px is
   about a six-dish section. */
.mcat { content-visibility: auto; contain-intrinsic-size: auto 640px; }

/* -- Typography, restored where it belongs ----------------------------------
   These three were briefly in the base sheet, taken back out because they changed the look of
   every restaurant already live, and now come back DELIBERATELY as part of a design pass the
   operator asked for — rather than as a side effect of building something else.

   `hyphens: auto` is not a nicety in German: `overflow-wrap` breaks a compound WITHOUT drawing
   a hyphen, and «Allergenkennzeich nung» reads as a fault. `text-wrap: pretty` on running text
   kills the one-word last line; `balance` on headings keeps a two-word orphan off the second
   line of a hero. */
h1, h2, h3, h4 { hyphens: auto; text-wrap: balance; }
p, li, .hero__intro, .lede { text-wrap: pretty; }

/* AND A SIZE FOR BARE HEADINGS, which nothing gave them: only headings wearing `.section-head`,
   `.article__body` or a `__title` class were ever sized, so a bare <h2> fell through to the user
   agent's fixed 2em. Measured on the homepage: «Ihre Feier bei uns» at 24px, one band below a
   heading that scales to 40px. Same page, same level, two sizes, and nothing said so. */
h1 { font-size: var(--fs-6); }
h2 { font-size: var(--fs-5); }
h3 { font-size: var(--fs-4); }
h4 { font-size: var(--fs-3); }

/* NO OVERRIDE LIST IS NEEDED, and the first draft had one built on `revert-layer` — which
   would have been a real bug: this stylesheet uses no cascade layers, so outside a layer that
   keyword behaves like `revert` and would have dropped `.section-head h2` to the user agent's
   own size. It is unnecessary anyway. Every heading that was already sized deliberately carries
   a CLASS, and a class selector outranks a bare element selector. The rules above only reach
   the headings nothing had claimed. */

/* -- Gut zu wissen ----------------------------------------------------------
   Die Fakten, die ein Gast vor der Abfahrt braucht. Früher eine Reihe winziger 22px-Icons neben
   Text — leblos. Jetzt ein ruhiges Kartenraster: jede Angabe sitzt in einem Feld auf der
   erhabenen Fläche (--surface über dem Papier --bg), das Zeichen in einem eigenen kleinen Feld.

   ⚠️ JEDE ZAHL UND JEDE FARBE HIER KOMMT AUS EINEM `--info-*`-TOKEN (tokens.css). Wer die
      Auskunftskarte umfärben oder umrunden will, fasst DIE an und nicht diese Regeln — und ein
      zweiter Auskunftsblock anderswo sieht ohne eine neue Zahl gleich aus. Die einzige
      Ausnahme ist der gedämpfte Zustand; warum, steht unten an `.facts__item--no`.

   ⛔ DREI ENTSCHEIDUNGEN, DIE DIE OPTIK NICHT AUFHEBEN DARF — sie sind der Grund, warum der
      Block eine Auskunft BLEIBT und keine Werbung wird:

   1. KEINE Werbe-Badges. «Nur Barzahlung» ist kein Prahlen, sondern eine Warnung, die jemand
      braucht. Die Karte trägt eine leise Kante (--line-soft) und den KLEINSTEN Schatten der
      Leiter (--shadow-xs) — sie hebt sich um ein Haar vom Papier, mehr nicht, und bleibt ein
      Feld zum Sitzen statt eines Reklamebanners. Das Feld hinter dem Zeichen ist ein 10-%-Hauch
      der «Ja»-Tinte, NIE eine satte Füllung: ein kräftig gefärbtes Feld hinter «Terrasse» läse
      sich als beworbenes Feature, und genau das verbietet diese Regel.

   2. Eine NEGATIVE Angabe ist NICHT grün. «Keine Hunde» im selben Email-Grün wie «Hunde
      willkommen» wäre eine mit Farbe erzählte Lüge. Positiv trägt --enamel (Zeichen + Feld),
      negativ fällt Zeichen, Feld UND Text auf --muted — gebunden an `.facts__item--no`.

   3. Negatives ist auch NICHT rot. Ein Haus ohne Terrasse hat an nichts versagt: die gedämpfte
      Variante nimmt keinen Warnton, kein Rot, kein --wine.

   WARUM --enamel BLEIBT UND NICHT --accent WIRD: --enamel ist die dedizierte «ja/offen/vegan»-
   Tinte dieses Systems (die veganen Marken, «heute geöffnet» in der Öffnungsliste). Ein Gast
   liest sie bereits als Zusage — «ja, wir nehmen Karten» gehört in dieselbe Sprache. --accent
   dagegen ist die Marken-/CTA-Farbe: Knöpfe, Links-auf-Akzent, das Wortzeichen. Eine Angabe
   damit zu füllen machte sie zu einem beworbenen Feature (Verstoß gegen Regel 1) — und weil
   --accent je Haus eine laute Markenfarbe sein kann, träte der ganze Block als Banner auf.
   --enamel bei 10 % Wash bleibt ruhig und sachlich; deshalb kein Wechsel. Der Name dafür heisst
   jetzt `--info-icon-color` — überschreibbar, aber mit diesem Vorgabewert.

   RESPONSIVE: `auto-fit, minmax(min(100%, 15rem), 1fr)` folgt der «fluid, not stepped»-Linie
   der Tokens. Aus den vorhandenen Maßen gerechnet: im 1080px-Wrap (~1036px Inhalt) vier
   Spalten, auf einem ~768px-Tablet drei, auf einem 375px-Telefon eine — die eine Spalte fällt
   aus der Rechnung, nicht aus einem Sonderfall. Die EINE Media Query unten macht keine neue
   Spaltenzahl, sondern nimmt auf dem Telefon nur Feld und Polster zurück. */
.facts { padding: var(--s-7) 0; }
/* Die Überschrift ist ein LABEL, kein Titel: sie benennt, was folgt, und tritt dann zurück.
   Deshalb klein, gesperrt, gedämpft — und mit einer Haarlinie darunter, die dem Band eine
   Oberkante gibt, ohne einen Kasten daraus zu machen. */
.facts__h {
    font-family: var(--sans); font-size: var(--fs-0); text-transform: uppercase;
    letter-spacing: .14em; color: var(--muted); margin: 0 0 var(--s-4);
    padding-bottom: var(--s-3); border-bottom: 1px solid var(--info-card-border);
}
.facts__list {
    list-style: none; margin: 0; padding: 0;
    display: grid;
    /*
     * ⛔ 15rem WAREN VIER SPALTEN UND JEDE ANGABE ZWEIZEILIG — im Browser gemessen, nicht
     *    geschätzt. Im 1080er-Wrap (~1034px Inhalt) ergaben 240px vier Karten à 247px; davon
     *    gehen 40px Zeichen, 20px Abstand, 40px Polster und die Kante ab, und für den Satz
     *    bleiben 145px. «Barrierefreie Toilette» misst 151px. Jede Auskunft brach also um, und
     *    zwar ausgerechnet auf dem GROSSEN Bildschirm: `--s-4` ist fluid (16px auf dem Tablet,
     *    20px am Schreibtisch), und so hatte dieselbe Karte am Schreibtisch WENIGER Platz für
     *    Text als auf dem Tablet. Am 834er-Tablet passten die Zeilen, am 1440er nicht.
     *
     *    17rem sind drei Spalten à 335px und 233px für den Satz — die längste Angabe, die
     *    dieses Modul kennt («Keine barrierefreie Toilette», 195px), passt einzeilig. Sechs
     *    einzeilige Karten in 3×2 lesen sich als EIN Block; sechs zweizeilige lesen sich als
     *    sechs Absätze.
     *
     * ⚠️ `min(100%, 17rem)` UND NICHT `17rem`. `minmax(17rem, 1fr)` kann breiter werden als
     *    der Behälter, sobald der Behälter selbst unter 17rem fällt — dann schiebt das Raster
     *    über den Rand und die ganze Seite bekommt eine waagerechte Rolle. Auf einem
     *    320px-Gerät mit Seitenrand ist genau das der Fall. `min()` deckelt die Untergrenze am
     *    Behälter.
     */
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
    gap: var(--s-3);
}
/* Jede Angabe eine ruhige Karte — ein Feld zum Sitzen, kein Badge zum Rufen (Regel 1).
   `align-items: center` an der Karte UND `min-height` am Feld: ohne die Mindesthöhe steht eine
   einzeilige Karte niedriger als die zweizeilige daneben, und eine Reihe aus sechs Auskünften
   sieht aus wie sechs Entscheidungen statt einer. */
.facts__item {
    display: flex; align-items: center; gap: var(--s-4); min-width: 0;
    background: var(--info-card-background);
    border: 1px solid var(--info-card-border);
    border-radius: var(--info-card-radius);
    padding: var(--s-4);
    box-shadow: var(--info-card-shadow);
}
/*
 * DAS ZEICHEN IN EINEM FELD, NICHT MEHR IN EINER SCHEIBE.
 *
 * ⛔ `--r-tag` (999px) WAR HIER FALSCH, und die Begründung steht schon in tokens.css: die
 *    vollrunde Pille ist das freundliche SaaS-Vokabular von 2019, und sechs davon
 *    nebeneinander lesen sich als Dashboard. Der Zeichensatz ist jetzt Phosphor Duotone —
 *    quadratisch gezeichnet, auf einem 256er-Raster —, und ein quadratisch gezeichnetes
 *    Zeichen in einem Kreis sitzt immer ein bisschen verloren. Ein Feld mit derselben Rundung
 *    wie die Karte, nur eine Sprosse kleiner (`--r-sm` in `--r-md`), gibt ihm eine Kante zum
 *    Anlehnen.
 *
 * ⚠️ 40px STATT 44px, GLYPH 22px STATT 24px. Der Doppelton trägt eine Fläche bei 20 % mit —
 *    dasselbe Zeichen wiegt optisch mehr als der Haarstrich davor. Bei gleicher Zahl wurde das
 *    Feld zur Fläche und der Text zur Bildunterschrift; zwei Pixel weniger stellen das
 *    Verhältnis wieder her.
 */
.facts__icon {
    flex: 0 0 auto;
    width: var(--info-icon-size); height: var(--info-icon-size);
    display: grid; place-items: center;
    border-radius: var(--info-icon-radius);
    color: var(--info-icon-color);
    /*
     * ⛔ DIE MISCHUNG STEHT HIER UND NICHT IN tokens.css, UND DAS IST GEMESSEN. Als
     *    `--info-icon-background` dort als `color-mix(… var(--info-icon-color) …)` definiert
     *    war, färbte ein überschriebenes `--info-icon-color` am Block nur das ZEICHEN um und
     *    liess das Feld dahinter stehen: eine benutzerdefinierte Eigenschaft löst ihre
     *    `var()`-Verweise dort auf, wo sie DEKLARIERT wird. An `:root` war die Mischung fertig
     *    gerechnet, bevor ein Theme sie erreichen konnte. An der Regel gerechnet stimmt sie
     *    für jede Überschreibung, egal auf welcher Ebene.
     *
     * ⚠️ DER NAME BLEIBT TROTZDEM DIE ERSTE WAHL: `var(--info-icon-background, …)` heisst
     *    «gesetzt gewinnt, ungesetzt wird gemischt». Kontor setzt ihn auf `transparent` und
     *    bekommt damit gar kein Feld.
     */
    background: var(--info-icon-background,
        color-mix(in oklab, var(--info-icon-color) var(--info-icon-wash), var(--info-card-background)));
    transition: background-color var(--t-fast);
}
/* Feste Maße, wie an jedem Inline-SVG dieses Blattes: ohne sie fällt das SVG im Grid auf null
   zusammen. Die `width`/`height`-Attribute am Element sind die Fassung ohne Stylesheet. */
.facts__icon svg { width: var(--info-icon-glyph); height: var(--info-icon-glyph); display: block; }
/* `text-wrap: balance` gegen die einzelne hängende Silbe: «Karten- und Barzahlung» brach als
   «Karten-und» / «Barzahlung» um und sah aus wie ein Tippfehler. */
.facts__text { font-size: var(--fs-2); line-height: 1.35; text-wrap: balance; }
/* NEGATIV: gedämpft — weder grün (Regel 2) noch rot (Regel 3). Icon, Feld und Text auf --muted.
   ⛔ HIER STEHT ABSICHTLICH KEIN `--info-*`-TOKEN. Der gedämpfte Zustand ist eine Aussage über
      Bedeutung und darf nicht per Theme umfärbbar sein; die Begründung steht in tokens.css. */
.facts__item--no .facts__icon {
    color: var(--muted);
    background: color-mix(in oklab, var(--muted) var(--info-icon-wash), var(--info-card-background));
}
.facts__item--no .facts__text { color: var(--muted); }

/*
 * DER ZEIGER — so leise, dass er keine Handlung verspricht.
 *
 * ⚠️ DIESE KARTEN SIND NICHTS ZUM ANKLICKEN. Ein Anheben, ein Zeigefinger oder ein
 *    Farbwechsel würde eine Handlung ankündigen, die es nicht gibt, und der Gast tippt ins
 *    Leere. Was bleibt, ist eine Fläche, die den Zeiger BEMERKT: die Kante setzt sich von
 *    `--line-soft` auf `--line`, die Scheibe wird von 10 % auf 16 % dichter. Kein `transform`,
 *    kein `cursor`, kein Schatten.
 *
 * ⛔ DIE ÜBERBLENDUNG SITZT AUF `.facts__icon` UND NICHT AUF `.facts__item` — das ist kein
 *    Stilfrage, das ist eine Kollision. Weiter unten in diesem Blatt setzt
 *    `.js-motion .facts__item` ein eigenes `transition: opacity, transform` fürs Einfliegen,
 *    steht später und hat dieselbe Spezifität; jede `transition` hier oben wird davon
 *    vollständig überschrieben (es ist EINE Eigenschaft, keine Liste, die sich ergänzt) — und
 *    dazu käme die Staffelung von bis zu 180 ms, die eine Rückmeldung träge macht. Das Icon
 *    trägt keine Reveal-Regel, also blendet dort über, was überblenden soll. Die 1px-Kante
 *    springt und das sieht kein Mensch.
 *
 * ⚠️ NUR AUF ZEIGERGERÄTEN. Ohne `@media (hover: hover)` bleibt der Zustand auf dem Telefon
 *    nach dem Tippen kleben, bis irgendwo anders hingetippt wird.
 */
@media (hover: hover) {
    .facts__item:hover { border-color: var(--line); }
    .facts__item:hover .facts__icon {
        background: var(--info-icon-background,
            color-mix(in oklab, var(--info-icon-color) var(--info-icon-wash-hover), var(--info-card-background)));
    }
    .facts__item--no:hover .facts__icon {
        background: color-mix(in oklab, var(--muted) var(--info-icon-wash-hover), var(--info-card-background));
    }
}

/*
 * AUF DEM TELEFON WIRD DIE KARTE SCHMALER, NICHT KLEINER GESETZT.
 *
 * Eine Spalte heisst: sechs Karten in voller Breite untereinander, jede mit einem 40px-Feld
 * und `--s-4` Polster ringsum — gemessen rund 470px Band für sechs Zeilen Auskunft. Der Satz
 * bleibt, wie er ist (eine Auskunft, die man kneifen muss, ist keine mehr); das Feld und das
 * Polster geben nach. Die Werte werden AM BLOCK gesetzt, nicht an den Regeln, damit die
 * Ausnahme eine Zeile bleibt und die Bedeutung der Namen sich nicht ändert.
 */
@media (max-width: 560px) {
    .facts {
        --info-icon-size: 36px;
        --info-icon-glyph: 20px;
    }
    .facts__item { padding: var(--s-3); gap: var(--s-3); }
    .facts__list { gap: var(--s-2); }
}

/* Kontor setzt es als geführtes Register, so wie dieser Aufbau sonst einen Fakt stellt — die
   Regel dazu steht unten im Kontor-Block. */

/* -- Warenkorb --------------------------------------------------------------
   The add button and the bar. Both are hidden in the markup and revealed by cart.js, so a
   browser without JavaScript never sees a control that would do nothing — the order form at
   /bestellen remains the path that always works.

   THE BUTTON IS 44px, and that is not a rounding of 42: it is tapped one-handed by somebody
   holding a phone over a table, and it sits beside a price, which is the worst place on the
   page to miss by four pixels. */
.mdish__add { display: none; }

.has-cart .mdish__add {
    display: inline-flex; align-items: center; justify-content: center; gap: 4px;
    min-width: 44px; min-height: 44px; padding: 0 var(--s-3);
    /* ⛔ `margin-left: var(--s-2)` UND NICHT MEHR `auto`. Der Knopf sitzt jetzt IM Kopf, hinter
       dem Preis; ein `auto` schöbe ihn von dort noch einmal weg — an den rechten Rand, also
       wieder fort von dem Preis, zu dem er gehört. Die Führungslinie `.mdish__lead` erledigt
       das Schieben bereits, und sie tut es zwischen NAME und Preis, wo es hingehört. */
    margin-left: var(--s-2); flex: 0 0 auto;
    border: 1px solid var(--line); border-radius: var(--r-btn);
    background: var(--surface); color: var(--text); cursor: pointer;
    font: inherit; font-size: var(--fs-1); font-variant-numeric: tabular-nums;
    transition: border-color var(--t-fast), background var(--t-fast), color var(--t-fast);
}
.has-cart .mdish__add:hover { border-color: var(--accent); color: var(--accent-ink); }

/* THE PER-SIZE BUTTON, and it sits inside the price line rather than after the dish. A Cola at
   two prices is two order lines, so it needs two buttons — one beside each price, where the
   choosing already happens. `margin-left` rather than `auto`, because inside `.mdish__var` there
   is no free row to push against. The 44px minimum stays: this is the smaller-looking control on
   the page and it is still a thumb target on a phone held over a table. */
.has-cart .mdish__add--var {
    margin-left: var(--s-2); padding: 0 var(--s-2); vertical-align: middle;
}
.mdish__add-plus { font-size: 1.15em; line-height: 1; }
/* Empty until something is in the basket, so the button does not read «0». */
.mdish__add-qty:empty { display: none; }
.mdish__add-qty { font-weight: 600; }

.cartbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 46;
    background: color-mix(in oklab, var(--surface) 92%, transparent);
    backdrop-filter: saturate(1.2) blur(10px);
    border-top: 1px solid var(--line);
    padding: var(--s-3) 0;
}
.cartbar[hidden] { display: none; }
.cartbar__inner { display: flex; align-items: center; gap: var(--s-4); }
.cartbar__count { margin: 0; font-size: var(--fs-1); }
.cartbar__clear {
    background: none; border: 0; padding: var(--s-2); cursor: pointer;
    font: inherit; font-size: var(--fs-0); color: var(--muted); text-decoration: underline;
}
.cartbar__go { margin-left: auto; }
.cartbar__sum { margin-left: var(--s-2); color: var(--muted); font-variant-numeric: tabular-nums; }

/* THE LINE ON THE CARD THAT SAYS ORDERS ARE TAKEN — one of the two, never both. `has-cart` is
   set by cart.js, so a browser that never ran it reads the sentence with the link to the plain
   form instead of a sentence about buttons it cannot see. */
.menu-order { margin: var(--s-3) 0 0; font-size: var(--fs-1); }
.menu-order--js { display: none; }
.has-cart .menu-order--js { display: block; }
.has-cart .menu-order--nojs { display: none; }

/* ⛔ ZWEI BEDIENELEMENTE, DIE OHNE SKRIPT NICHTS TATEN UND TROTZDEM DASTANDEN.

   Die Regel steht zweimal im Projekt ausgeschrieben — «ohne Skript filtert es nichts, also darf
   es ohne Skript nicht da sein» und «ein Bedienelement, das nichts tut, ist schlimmer als
   keines: der Gast tippt darauf, nichts geschieht, und er glaubt, die Seite sei kaputt». Der
   zweite Satz nennt sogar ausdrücklich die Speisekarte. Angewandt war sie auf `/bestellen`
   (`.ladensuche` ist dort `hidden`), auf der Speisekarte nicht.

   · `.m-bar` — Suchfeld, Diät-Chips, Allergen-Auswahl. Ohne `menu.js` filtert nichts.
   · `.mobilebar__korb` — der Warenkorb-Knopf im Daumenband. Ohne `cart.js` öffnet er nichts.

   ⚠️ ÜBER DIE MARKE DES JEWEILS ZUSTÄNDIGEN SKRIPTS, nicht über eine gemeinsame: `cart.js` kann
      laufen, während `menu.js` scheitert. Eine Marke für beide hiesse, die Filterleiste zu
      zeigen, weil der Warenkorb funktioniert.

   ⚠️ DER WEG OHNE SKRIPT BLEIBT: `.menu-order--nojs` oben führt weiterhin auf `/bestellen`,
      wo dieselbe Karte ohne eine Zeile JavaScript bestellbar ist. */
html:not(.has-menufilter) .m-bar { display: none; }
html:not(.has-cart) .mobilebar__korb { display: none; }

/* TWO CONTROLS, ONE VISIBLE AT A TIME. The button opens the basket and the order is placed
   there; the link goes to the plain form. `has-cart` is set by cart.js, so a browser that never
   ran it keeps the link — and a control that would do nothing is never on screen. */
.cartbar__go--nojs { margin-left: 0; }
.has-cart .cartbar__go--nojs { display: none; }
button.cartbar__go { display: none; }
.has-cart button.cartbar__go { display: inline-flex; }

/* THE THUMB BAR AND THE BASKET BAR CANNOT BOTH OWN THE BOTTOM OF THE SCREEN. The basket wins
   while it has something in it — a guest mid-order is doing the one thing the bar exists for —
   and the phone bar steps aside rather than stacking. */
.has-cart .cartbar:not([hidden]) ~ .mobilebar,
.cartbar:not([hidden]) ~ .mobilebar { display: none; }

/* ---- The basket panel ------------------------------------------------------------------
   A dialog over the card rather than a second page: choosing and ordering are one act, and
   sending somebody to a page that lists the whole menu again to do the second half was the
   shape this replaces. `/bestellen` stays exactly where it was, for the browser that never
   runs a script and for anybody who prefers one long page. */
.cartdrawer {
    position: fixed; inset: 0; z-index: 60;
    background: color-mix(in oklab, #000 46%, transparent);
    display: flex; align-items: flex-end; justify-content: center;
}
.cartdrawer[hidden] { display: none; }

/* ─── FAZ C · DER PRODUKTDIALOG ──────────────────────────────────────────────────────────
   Dieselbe Bottom-Sheet-Hülle wie der Warenkorb daneben, von cart.js aus dem `data-options`-Attribut
   des angetippten Gericht-Knopfs gefüllt. */
.proddialog {
    position: fixed; inset: 0; z-index: 70;
    background: color-mix(in oklab, #000 46%, transparent);
    display: flex; align-items: flex-end; justify-content: center;
}
.proddialog[hidden] { display: none; }
.proddialog__panel {
    background: var(--surface); color: var(--text);
    width: min(560px, 100%); max-height: min(90vh, 900px);
    display: flex; flex-direction: column;
    border-radius: var(--r) var(--r) 0 0;
    border: 1px solid var(--line); border-bottom: 0;
    box-shadow: 0 -18px 48px rgba(0, 0, 0, .28);
}
@media (min-width: 700px) {
    .proddialog { align-items: center; }
    .proddialog__panel { border-radius: var(--r); border-bottom: 1px solid var(--line); box-shadow: var(--shadow-xl); }
}
.proddialog__head {
    display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
    padding: var(--s-4) var(--s-5); border-bottom: 1px solid var(--line);
}
.proddialog__title { margin: 0; font-size: var(--fs-4); font-family: var(--serif); }
.proddialog__close {
    flex: 0 0 auto; inline-size: 40px; block-size: 40px; border-radius: 999px;
    border: 0; background: transparent; color: var(--muted); cursor: pointer;
    font-size: 1.5rem; line-height: 1; display: inline-flex; align-items: center; justify-content: center;
}
.proddialog__close:hover { background: color-mix(in oklab, var(--text) 8%, transparent); color: var(--text); }
.proddialog__close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; box-shadow: none; }

.proddialog__body { padding: var(--s-3) var(--s-5) var(--s-4); overflow-y: auto; display: flex; flex-direction: column; gap: var(--s-4); }
.pdgroup { border: 0; padding: 0; margin: 0; display: flex; flex-direction: column; }
.pdgroup__name { font-family: var(--sans); font-weight: 600; font-size: var(--fs-2); padding: var(--s-2) 0; border-bottom: 1px solid var(--line-soft); }
.pdgroup__hint { font-weight: 400; color: var(--muted); font-size: var(--fs-0); }
.pdopt {
    display: flex; align-items: center; gap: var(--s-3);
    padding: var(--s-3) 0; border-bottom: 1px solid var(--line-soft); cursor: pointer;
}
.pdopt__input { inline-size: 20px; block-size: 20px; accent-color: var(--accent); flex: 0 0 auto; }
.pdopt__text { flex: 1 1 auto; min-inline-size: 0; }
.pdopt__price { color: var(--muted); font-variant-numeric: tabular-nums; }

.proddialog__foot {
    display: flex; align-items: center; gap: var(--s-3);
    padding: var(--s-4) var(--s-5); border-top: 1px solid var(--line);
}
.proddialog__menge { display: inline-flex; align-items: center; gap: var(--s-3); }
.proddialog__step {
    inline-size: 44px; block-size: 44px; border-radius: 999px;
    border: 1px solid var(--line); background: var(--bg); color: var(--text);
    font-size: 1.25rem; cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
}
.proddialog__step--plus { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.proddialog__zahl { min-inline-size: 1.5em; text-align: center; font-variant-numeric: tabular-nums; font-weight: 600; }
.proddialog__hinzu {
    flex: 1 1 auto; display: inline-flex; align-items: center; justify-content: space-between; gap: var(--s-3);
    padding: 14px 20px; border: 0; border-radius: var(--r-md);
    background: var(--accent); color: var(--on-accent); font: inherit; font-weight: 600; font-size: var(--fs-2); cursor: pointer;
}
.proddialog__hinzu[disabled] { opacity: .5; cursor: not-allowed; }
.proddialog__hinzu-text { white-space: nowrap; }
.proddialog__total { font-variant-numeric: tabular-nums; }

/* Die gewählten Optionen unter einer Warenkorbzeile — «Kalb, Käse, ohne Zwiebel». */
.cartline__opts { display: block; color: var(--muted); font-size: var(--fs-0); margin-top: 2px; }

.cartdrawer__panel {
    background: var(--surface); color: var(--text);
    width: min(560px, 100%); max-height: min(88vh, 900px);
    display: flex; flex-direction: column; overflow: hidden;
    /* `--r` (16px) statt `--r-btn` (8px): dieselbe weiche Rundung wie `.abschluss` auf
       `/bestellen` — die Vorlage, an der dieser Dialog gemessen wird. Ein Blatt aus 8-px-Ecken
       las sich neben den 13–16-px-Karten der Gastseite wie ein Systemdialog, nicht wie Teil des
       Ladens. Nur die beiden OBEREN Ecken, weil das Blatt am unteren Rand klebt. */
    border-radius: var(--r) var(--r) 0 0;
    border: 1px solid var(--line); border-bottom: 0;
    /* GERICHTETER SCHATTEN, mit Absicht kein `--shadow-*`-Token: ein Bottom-Sheet stösst unten an
       den Bildschirmrand, ein nach UNTEN fallender Token-Schatten läge dort ausserhalb und wäre
       unsichtbar. Der Aufwärts-Hub hebt das Blatt von der Karte darunter ab. */
    box-shadow: 0 -18px 48px rgba(0, 0, 0, .28);
}
@media (min-width: 700px) {
    .cartdrawer { align-items: center; }
    /* Auf breiten Schirmen steht das Blatt frei in der Mitte: rundum gerundet und ein
       symmetrischer Token-Schatten, weil hier kein Rand mehr eine Seite abschneidet. */
    .cartdrawer__panel { border-radius: var(--r); border-bottom: 1px solid var(--line); box-shadow: var(--shadow-xl); }
}

.cartdrawer__head {
    display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
    padding: var(--s-4) var(--s-5); border-bottom: 1px solid var(--line);
}
.cartdrawer__title { margin: 0; font-size: var(--fs-4); font-family: var(--serif); }
/* ⛔ IM SCREENSHOT WAR DAS X EIN «ORANGER KASTEN». Der Knopf trägt selbst keinen Rahmen — der
   Kasten war der GLOBALE `:focus-visible` (Akzent-Umriss + 5-px-Hof), und `cart.js` setzt den
   Fokus beim Öffnen genau auf diesen Knopf. Ein programmatischer Fokus zeigt in aktuellen
   Browsern `:focus-visible`, also empfing der erste Blick auf den Dialog den harten Kasten.
   Antwort: ein ruhiger runder Schliess-Knopf mit weichem Hover-Kreis und einem ENGEN Ring ohne
   Hof (`box-shadow: none`), der wie ein Knopf aussieht und nicht wie ein umrahmtes Feld. */
.cartdrawer__close {
    display: inline-flex; align-items: center; justify-content: center;
    /* A thumb target, on the control somebody reaches for first when they change their mind. */
    min-width: 44px; min-height: 44px; padding: 0;
    background: none; border: 0; border-radius: var(--r-tag);
    cursor: pointer; color: var(--muted); font-size: 1.7rem; line-height: 1;
    transition: background var(--t-fast), color var(--t-fast);
}
.cartdrawer__close:hover { background: color-mix(in oklab, var(--accent) 10%, transparent); color: var(--text); }
.cartdrawer__close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; box-shadow: none; }

.cartdrawer__body { flex: 1 1 auto; min-height: 0; padding: var(--s-4) var(--s-5) var(--s-5); overflow-y: auto; }

.cartlines { list-style: none; margin: 0 0 var(--s-4); padding: 0; }
.cartline {
    display: grid; grid-template-columns: 1fr auto auto; align-items: center;
    gap: var(--s-2) var(--s-4); padding: var(--s-3) 0; border-bottom: 1px solid var(--line-soft);
}
.cartline:first-child { padding-top: 0; }
.cartline:last-child { border-bottom: 0; }
/* `align-items: flex-start` IST DER KERN DER KORREKTUR am Allergen-Etikett (siehe `.cartline .codes`
   unten): ohne sie streckt der Flex-Spalten-Standard `stretch` die `.codes`-Marke auf die volle
   Spaltenbreite, und mit ihrem umrandeten Grundstil sah sie dann aus wie ein leeres Eingabefeld. */
.cartline__what { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s-1); min-width: 0; }
.cartline__name { font-weight: 600; font-size: var(--fs-2); line-height: 1.3; overflow-wrap: anywhere; }

/* ⛔ DIE KONKRETE BESCHWERDE: der Zusatzstoff-/Allergen-Code (LMIV Art. 21, MUSS bleiben) sah aus
   wie ein Eingabefeld — Rahmen, heller Grund, linksbündig, direkt über dem echten Mengenwähler.
   Ein Gast hielt das «Hc» für eine Eingabe.

   URSACHE, GEMESSEN AM MARKUP: `cart.js` erzeugt `<span class="codes">`; der Grundstil `.codes`
   (Zeile ~785) trägt `border: 1px solid var(--line)` + hellen Grund und ist auf der Speisekarte
   ein ruhiges Abzeichen, WEIL es dort inline zwischen anderen Tags steht. Im Dialog liegt es in
   einer Flex-SPALTE und wurde auf volle Breite gestreckt — dieselbe Form wie ein `<input>`.

   ⚠️ NUR DIE DARSTELLUNG ÄNDERT SICH, DER CODE BLEIBT. Nicht den globalen `.codes`-Stil anfassen
   (er ist auf Speisekarte und `/bestellen` richtig und wird von Tests geprüft), sondern hier
   scoped überschreiben: nicht mehr strecken (`width: max-content`), den umrandeten «Feld»-Look
   durch einen gefüllten, ruhigen Etikett-Grund ersetzen (`--line-soft`, Rahmen transparent). So
   liest es sich als Info-Abzeichen statt als Formularfeld. */
.cartline .codes {
    align-self: flex-start; width: max-content; max-width: 100%;
    border-color: transparent; background: var(--line-soft); color: var(--muted);
}

/* DER MENGENWÄHLER — auf das Niveau der `.orderline__step`-Knöpfe von `/bestellen` gebracht:
   runde Knöpfe (`--r-tag`), 44 px Trefferfläche, der «+» als gefüllte Haupthandlung mit `--shadow-xs`,
   der «−» als flache Gegenhandlung, und derselbe ENGE Fokusring (Versatz 1 px statt der globalen
   3 px samt Hof), der im gedrängten Cluster «− N +» nicht ans Nachbarfeld rückt. */
.cartline__steps { display: flex; align-items: center; gap: var(--s-1); }
.cartline__step {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 44px; min-height: 44px; padding: 0; flex: 0 0 auto; cursor: pointer;
    border: 1px solid var(--line); border-radius: var(--r-tag);
    background: var(--bg); color: var(--text); font: inherit; font-size: var(--fs-4); line-height: 1;
    transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast), box-shadow var(--t-fast);
}
.cartline__step:hover { border-color: var(--accent); color: var(--accent-ink); }
.cartline__step:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; box-shadow: none; }
.cartline__step--plus {
    background: var(--accent); border-color: var(--accent); color: var(--on-accent);
    box-shadow: var(--shadow-xs);
}
.cartline__step--plus:hover {
    background: var(--accent-strong); border-color: var(--accent-strong); color: var(--on-accent);
    box-shadow: var(--shadow-sm);
}
.cartline__qty { min-width: 2.5ch; text-align: center; font-variant-numeric: tabular-nums; font-weight: 600; }
/* Der Zeilenpreis in der Preisschrift des Hauses, wie `.orderline__price` auf `/bestellen`: er ist
   die Zahl, auf die das Auge in der Zeile zielt. */
.cartline__money { font-variant-numeric: tabular-nums; white-space: nowrap; font-family: var(--serif); font-weight: 600; color: var(--accent-ink); }

/* ⛔ DIESER BLOCK GEHÖRT VOR DEN KONTOR-TEIL — er stand einmal am Dateiende und war damit
      im zweiten Aufbau gelandet. Ab der ersten `.layout-kontor`-Regel bis zum Dateiende gehört
      jede Zeile dem Kontor-Aufbau und MUSS auf `.layout-kontor` lauten; `LayoutKontorTest` prüft
      genau das und hat den Fehler gefunden. Die Schublade ist beiden Aufbauten gemeinsam, also
      steht sie hier bei den übrigen `.cart*`-Regeln. */

/* ═══ «PASST GUT DAZU» IM WARENKORB ══════════════════════════════════════════════════════════
   Was zu dem zuletzt gewählten Gericht am häufigsten mit bestellt wurde. Die Liste kommt aus
   den Bestellungen dieses Hauses (`Menu::pairMap()`), nicht aus einer gepflegten Liste und
   nicht aus eingekaufter Werbung.

   ⚠️ SIE SIEHT NICHT AUS WIE EINE ANZEIGE. Kein Rahmen, keine Farbe, kein Ausrufezeichen —
      eine Zeile Text und drei flache Knöpfe. Ein Vorschlag, der wie Werbung aussieht, wird wie
      Werbung behandelt: übersehen. */
.cartdazu { margin-top: var(--s-5); padding-top: var(--s-4); border-top: 1px solid var(--line-soft); }
.cartdazu__titel { margin: 0 0 var(--s-3); font-family: var(--serif); font-size: var(--fs-2); font-weight: 600; }
.cartdazu__liste { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-2); }

.cartdazu__knopf {
    display: flex; align-items: center; gap: var(--s-3); width: 100%;
    /* 56px, nicht 44: hier steht ein Bild neben dem Namen, und die Zeile ist die Trefferfläche.
       Der Daumen zielt auf die ganze Zeile, nicht auf das «+» am Ende. */
    min-height: 56px; padding: var(--s-2) var(--s-3);
    border: 1px solid var(--line); border-radius: var(--r-btn);
    background: var(--surface); color: var(--text); cursor: pointer; font: inherit; text-align: left;
    transition: border-color var(--t-fast), background var(--t-fast);
}
.cartdazu__knopf:hover { border-color: var(--accent); }
.cartdazu__foto { width: 44px; height: 44px; object-fit: cover; border-radius: var(--r-sm); flex: 0 0 auto; }
.cartdazu__text { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 auto; }
.cartdazu__name { overflow-wrap: anywhere; }
.cartdazu__preis { font-size: var(--fs-0); color: var(--muted); font-variant-numeric: tabular-nums; }
.cartdazu__plus { flex: 0 0 auto; font-size: 1.3em; line-height: 1; color: var(--accent-ink); }

/* SUMME — jetzt die Zahl der Abschlussleiste unten (`.cartdrawer__foot`), nicht mehr ein
   Zeilenende über dem Formular. In der Preisschrift und der Marken-/Akzentfarbe, gross genug,
   dass sie die Zahl ist, auf die das Auge am Absendeknopf trifft. */
.cartdrawer__sum {
    display: flex; justify-content: space-between; align-items: baseline; gap: var(--s-3);
    margin: 0; font-size: var(--fs-3);
}
.cartdrawer__sum span { color: var(--muted); letter-spacing: .01em; }
.cartdrawer__sum strong { font-size: var(--fs-5); font-family: var(--serif); font-weight: 600; color: var(--accent-ink); font-variant-numeric: tabular-nums; line-height: 1; }

/* ── TRINKGELD — freiwillig, fürs Team, an der Theke mitbezahlt ───────────────────────────
   Steht in der Abschlussleiste ÜBER der Summe: der Gast sieht Speisen, Trinkgeld und Summe in
   einer Bewegung, bevor er «Zahlungspflichtig bestellen» drückt. Kein Preis reist mit — nur
   `trinkgeld_cents`, den der Gast selbst bestimmt und der Server begrenzt.
   Chips statt Auswahlliste: vier Berührungen à 44 px, unter jedem Prozentsatz der gerechnete
   Betrag, damit «10 %» nicht abstrakt bleibt. «Eigener Betrag» steht allein in der zweiten
   Zeile, weil vier Wörter in einem Fünftel von 340 px nicht lesbar sind. Die Vorgabe ist «Kein»:
   ein voreingestelltes Trinkgeld wäre ein Betrag, den niemand gewählt hat. */
.trinkgeld { margin: 0; padding: 0; border: 0; min-width: 0; }
/* Die Zeile: Zeichen, Titel, Stand, Pfeil — ein Knopf über die ganze Breite, 44 px hoch, ohne
   eigenen Rahmen, damit er in der Leiste wie eine Zeile liegt und nicht wie ein zweiter Knopf
   neben «Zahlungspflichtig bestellen». Die erste Fassung war ein Block von 150 px; auf dem
   Telefon blieben der Bestellung darüber zwei Zeilen. */
.trinkgeld__kopf {
    display: flex; align-items: center; gap: 8px; width: 100%; min-height: 44px;
    margin: 0; padding: 4px 0; border: 0; background: transparent; color: var(--text);
    font: inherit; text-align: left; cursor: pointer;
}
.trinkgeld__icon { flex: 0 0 auto; color: var(--accent-ink); }
.trinkgeld__titel { font-weight: 600; font-size: var(--fs-1); }
.trinkgeld__stand { margin-left: auto; font-size: var(--fs-1); color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.trinkgeld__stand.is-gewaehlt { color: var(--accent-ink); font-weight: 600; }
.trinkgeld__pfeil { flex: 0 0 auto; width: 8px; height: 8px; border-right: 1.5px solid var(--muted); border-bottom: 1.5px solid var(--muted); transform: translateY(-2px) rotate(45deg); transition: transform var(--t); }
.trinkgeld__kopf[aria-expanded="true"] .trinkgeld__pfeil { transform: translateY(2px) rotate(-135deg); }
.trinkgeld__kopf:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-sm); }
.trinkgeld__blatt { padding: 0 0 var(--s-2); }
.trinkgeld__hinweis { margin: 0 0 var(--s-2); font-size: var(--fs-0); color: var(--muted); }
.trinkgeld__wahl { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; }
.trinkgeld__chip {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
    min-height: 44px; padding: 6px 4px; border-radius: var(--r-md);
    border: 1px solid var(--line); background: var(--surface); color: var(--text);
    font: inherit; font-size: var(--fs-1); font-weight: 600; line-height: 1.1; cursor: pointer;
    transition: border-color var(--t), background var(--t), color var(--t);
}
.trinkgeld__chip--eigen { grid-column: 1 / -1; flex-direction: row; gap: 8px; }
.trinkgeld__chip small { font-size: var(--fs-0); font-weight: 400; color: var(--muted); font-variant-numeric: tabular-nums; }
.trinkgeld__chip small:empty { display: none; }
.trinkgeld__chip:hover { border-color: var(--accent); }
.trinkgeld__chip.is-aktiv { border-color: var(--accent); background: color-mix(in oklab, var(--accent) 12%, var(--surface)); color: var(--accent-ink); }
.trinkgeld__chip.is-aktiv small { color: var(--accent-ink); }
.trinkgeld__chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.trinkgeld__eigen { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); margin: var(--s-3) 0 0; font-size: var(--fs-1); }
.trinkgeld__eigen label { color: var(--muted); }
.trinkgeld__feld { display: inline-flex; align-items: center; gap: 6px; }
.trinkgeld__feld input {
    width: 6em; padding: 9px 10px; border: 1px solid var(--line); border-radius: var(--r-md);
    background: var(--surface); color: var(--text); font: inherit; font-variant-numeric: tabular-nums; text-align: right;
}
.trinkgeld__feld input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent); }
/* Die zwei leisen Zeilen über der Summe — Speisen und Trinkgeld getrennt, damit die grosse Zahl
   darunter nicht behauptet, das Essen sei teurer geworden. */
.cartdrawer__zeile { display: flex; justify-content: space-between; gap: var(--s-3); margin: 0; font-size: var(--fs-1); color: var(--muted); font-variant-numeric: tabular-nums; }
.cartdrawer__empty { margin: var(--s-5) 0; color: var(--muted); text-align: center; }
/* ⛔ «WIR NEHMEN GERADE NICHTS AN» — im Blatt, vor dem Korb, nicht erst nach dem Absenden.
   Dieselbe Einfassung wie `.ladenmeldung` auf `/bestellen`, weil es dieselbe Art Auskunft ist:
   das Haus sagt etwas über sich, und der Gast soll es lesen, BEVOR er tippt. Die linke Kante
   trägt das Gewicht — ein voller Farbblock wäre ein Alarm, und ein Stopp ist keiner. */
.cartdrawer__stopp {
    margin: var(--s-4) 0 0;
    border: 1px solid color-mix(in oklab, var(--wine) 34%, var(--line));
    border-left-width: 4px;
    border-radius: var(--r-md);
    background: color-mix(in oklab, var(--wine) 6%, var(--surface));
    padding: var(--s-3) var(--s-4);
    font-size: var(--fs-1);
    line-height: 1.45;
}
/* Die Zusicherung mit einem ruhigen Info-Zeichen in Messing — kein Warnton, eine Auskunft. */
.cartdrawer__legal { display: flex; gap: var(--s-2); align-items: flex-start; font-size: var(--fs-0); line-height: 1.42; color: var(--muted); margin: 0; }
.cartdrawer__legalicon { flex: 0 0 auto; width: 15px; height: 15px; margin-top: 1px; color: var(--gold-ink); }
.cartdrawer__alt { font-size: var(--fs-0); margin: var(--s-4) 0 0; text-align: center; }
.cartdrawer__form { margin: 0; }
/* DIE FORMFELDER RUHIGER EINGEFASST. Die Felder liegen auf `--surface` (dem Panelgrund) und trugen
   selbst `--surface` — ihre Kante verschwand in der Fläche, auf der sie liegen. Wie im `.abschluss`
   auf `/bestellen` («die Felder liegen auf der Karte, also brauchen sie den Grund der SEITE»)
   bekommen sie hier den Seitengrund `--bg`, damit ihre Einfassung zu sehen ist; dazu eine
   einheitliche 44-px-Feldhöhe (Telefon) und ein kräftigeres Label. */
.cartdrawer__form .resform__field label { font-weight: 600; font-size: var(--fs-0); color: var(--muted); letter-spacing: .01em; }
.cartdrawer__form .resform__field input,
.cartdrawer__form .resform__field select,
.cartdrawer__form .resform__field textarea {
    background: var(--bg); min-height: 46px;
    border: 1px solid var(--line); border-radius: var(--r-sm);
    transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
/* Ein ruhiger, sichtbarer Fokus in der Marken-/Akzentfarbe (ersetzt den globalen Umriss im
   engen Formular durch einen weichen Hof, der nicht ans Nachbarfeld stösst). */
.cartdrawer__form .resform__field input:focus,
.cartdrawer__form .resform__field select:focus,
.cartdrawer__form .resform__field textarea:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 20%, transparent);
    outline: none;
}
.cartdrawer__form .resform__field textarea { min-height: 68px; }

/* ═══ DER BESTELLZETTEL · PREMIUM-AUSSTATTUNG ════════════════════════════════════════════════
   Kopf mit Messing-Beiwort und Griff, ein gegliedertes Formular und eine festgeklebte
   Abschlussleiste (Summe · Zusicherung · Absenden). Alles über Token, damit das Blatt die
   Markenfarbe jedes Hauses trägt und in Tag wie Nacht gleich ruhig steht. */

/* Bottom-Sheet-Griff — nur, wo das Blatt am unteren Rand klebt (Schmalschirm; auf Breitschirmen
   steht der Dialog frei in der Mitte und der Griff ist weg, siehe Media-Query unten). */
.cartdrawer__grip {
    flex: 0 0 auto; width: 40px; height: 4px; border-radius: 999px;
    background: var(--line); margin: 10px auto 2px;
}

/* Kopf: Messing-Beiwort über dem Serifentitel. */
.cartdrawer__heading { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cartdrawer__eyebrow {
    margin: 0; font-family: var(--sans); font-weight: 600;
    font-size: .68rem; letter-spacing: .18em; text-transform: uppercase;
    color: var(--gold-ink);
}

/* Ein Abschnitt des Formulars — «Wann?» und «Wer?» je unter einer Überschrift mit Messing-Icon
   und weicher Trennlinie. Kein <fieldset>: dessen <legend> lässt sich nicht als Flexzeile setzen;
   die Gruppierung ist hier optisch, die Feld-Labels tragen die Semantik. */
.cartsec { margin: 0 0 var(--s-5); }
.cartsec:last-of-type { margin-bottom: 0; }
.cartsec__title {
    display: flex; align-items: center; gap: var(--s-2);
    margin: 0 0 var(--s-3); padding-bottom: var(--s-2);
    font-family: var(--sans); font-weight: 600; font-size: var(--fs-1); color: var(--text);
    border-bottom: 1px solid var(--line-soft);
}
.cartsec__icon { flex: 0 0 auto; width: 18px; height: 18px; color: var(--gold-ink); }

/* «Wann?» als leicht getöntes Kärtchen — die wichtigste Wahl bekommt einen eigenen Grund. */
.cartsec--wann {
    padding: var(--s-4); border: 1px solid var(--line-soft); border-radius: var(--r-md);
    background: color-mix(in oklab, var(--gold) 6%, transparent);
}
.cartsec--wann .cartsec__title { border-bottom-color: color-mix(in oklab, var(--gold) 24%, var(--line-soft)); }

/* Die Abschlussleiste: festgeklebt am Fuss, mit Kante und nach oben fallendem Schatten, der sie
   von der scrollenden Liste abhebt. Der iPhone-Sicherheitsrand wird unten mitgerechnet, damit der
   Knopf nicht unter dem Systembalken sitzt. `[hidden]` (global `!important`) blendet die ganze
   Leiste aus, solange der Korb leer ist. */
.cartdrawer__foot {
    flex: 0 0 auto; display: grid; gap: var(--s-3);
    padding: var(--s-4) var(--s-5);
    padding-bottom: calc(var(--s-4) + env(safe-area-inset-bottom, 0px));
    border-top: 1px solid var(--line);
    background: color-mix(in oklab, var(--gold) 4%, var(--surface));
    box-shadow: 0 -12px 30px rgba(0, 0, 0, .10);
}

/* Der Absendeknopf über die volle Breite — die Haupthandlung des Blattes. Erbt Grund, Schrift
   und Glanz von `.btn--primary`, also die Markenfarbe des Hauses. */
.cartdrawer__send {
    width: 100%; justify-content: center;
    padding: 15px 20px; border-radius: var(--r-md);
    font-size: var(--fs-2); font-weight: 600; letter-spacing: .01em;
}

/* Ein sanftes Auffahren des Blattes, der Hintergrund blendet auf. Auf Breitschirmen steht der
   Dialog mittig und blendet nur auf, statt hochzufahren. Bewegungsarm wird beides still. */
@keyframes cartdrawer-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes cartdrawer-rise { from { transform: translateY(8%); } to { transform: translateY(0); } }
.cartdrawer:not([hidden]) { animation: cartdrawer-fade .2s ease both; }
.cartdrawer:not([hidden]) .cartdrawer__panel { animation: cartdrawer-rise .28s cubic-bezier(.22, .61, .36, 1) both; }
@media (min-width: 700px) {
    .cartdrawer__grip { display: none; }
    .cartdrawer:not([hidden]) .cartdrawer__panel { animation-name: cartdrawer-fade; }
}
@media (prefers-reduced-motion: reduce) {
    .cartdrawer:not([hidden]),
    .cartdrawer:not([hidden]) .cartdrawer__panel { animation: none; }
}

/* The page behind a modal must not scroll under the thumb. Beide Blätter — Warenkorb und
   Store-Details — teilen sich die Sperre. */
.cart-open, .cart-open body,
.sheet-open, .sheet-open body { overflow: hidden; }

/* ---------- Store-Details: Auslöser im Ladenkopf + Inhalt der Schublade ----------
   Die Schublade selbst trägt die `.cartdrawer`-Hülle (Rücken, Blatt, Kopf, Schliessen); hier
   steht nur, was ihr eigen ist. */
.ladenkopf__info {
    flex: 0 0 auto; display: inline-flex; align-items: center; gap: 7px;
    min-height: 40px; padding: 0 14px;
    border: 1px solid var(--line); border-radius: var(--r-btn);
    background: var(--surface); color: var(--muted);
    font: inherit; font-size: var(--fs-1); cursor: pointer;
    transition: border-color var(--t-fast), color var(--t-fast);
}
.ladenkopf__info:hover { border-color: var(--accent); color: var(--accent-ink); }
.ladenkopf__info:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ladenkopf__info svg { width: 18px; height: 18px; flex: 0 0 auto; }

.storedetails__block { margin: 0 0 var(--s-5); }
.storedetails__block:last-child { margin-bottom: 0; }
.storedetails__h {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--s-2);
    margin: 0 0 var(--s-3); font-family: var(--serif); font-size: var(--fs-3);
}
.storedetails__addr { font-style: normal; color: var(--text); line-height: 1.55; font-size: var(--fs-2); }
.storedetails__maps {
    display: inline-flex; align-items: center; gap: 7px; margin-top: var(--s-3);
    color: var(--accent-ink); font-weight: 600; text-decoration: none;
}
.storedetails__maps svg { width: 18px; height: 18px; flex: 0 0 auto; }
.storedetails__maps:hover { text-decoration: underline; }
.storedetails__hint { margin: var(--s-2) 0 0; font-size: var(--fs-0); color: var(--muted); }
.storedetails__kontakt { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-3); }
.storedetails__kontakt li { display: flex; align-items: center; gap: var(--s-3); }
.storedetails__kontakt svg { width: 19px; height: 19px; flex: 0 0 auto; color: var(--gold-ink); }
.storedetails__kontakt a { color: var(--text); text-decoration: none; }
.storedetails__kontakt a:hover { color: var(--accent-ink); text-decoration: underline; }
/* Die Zeitenliste im Blatt trägt keinen eigenen Rahmen — der Kopf trennt sie schon ab. */
.storedetails .hours-list { margin: 0; }

@media (max-width: 560px) {
    /* Auf dem Telefon schrumpft der Auslöser auf sein Symbol, wie der Hörer-Knopf daneben. */
    .ladenkopf__infotext { display: none; }
    .ladenkopf__info { padding: 0; min-width: 44px; justify-content: center; }
}


/* ============================================================
   LAYOUT: KONTOR — the second architecture.

   NOT A SECOND SET OF TEMPLATES. Everything below re-shapes the SAME markup the photographic
   layout renders, hung off one class on <body>. That is the whole reason this can exist: the
   version of this product that forked the templates per variation produced nine markup trees,
   the least-visited drifted first, and no section could be improved without checking the other
   eight. Here a section written tomorrow appears in both layouts because there is only one
   place it can be written.

   WHAT CHANGES, and none of it is colour or type — those stay the theme's job, and all three
   themes (Abend, Tafel, Markt) work with this layout exactly as they do with the other:

     · the masthead: name centred over a rule, navigation as a second row of small capitals
     · the opening: no full-bleed photograph anywhere. A stated plate with the photograph as an
       inset beside it, and the three specialised photo heroes brought onto paper too
     · the rhythm: hairlines and a left-hand label instead of alternating background bands
     · a dish: a ruled line with the price at the right margin, not a card with a photograph
     · the footer: paper rather than near-black
     · typography: sized headings, German hyphenation, balanced heading lines

   WHO IT IS FOR: the house whose photographs are its weakest asset. Measured across the
   restaurants onboarded so far that is most of them, and leading with a poor photograph of an
   empty dining room at eleven in the morning makes a good kitchen look worse than it is.

   ⛔ WHAT THIS BLOCK DELIBERATELY DOES NOT TOUCH, each for a reason written where it lives:
      the photo scrim arithmetic (`.cater-hero__scrim`, `.gal-hero__scrim` — the alpha floors
      that guarantee 4.5:1 over the worst photograph a customer can upload), `[hidden]` and its
      `!important` (the allergen filter; any `display` this block adds to `.mdish`/`.mcat` is
      the exact selector shape that once made ten hidden dishes stay on screen), the touch-target
      block, `.dishcard__photo.ph-tile { display: flex }`, `.legal-missing`, and the German
      wrap guards on headings.
   ============================================================ */

/* -- Masthead -------------------------------------------------------------- */
/* Two centred rows: the house's name, a rule, then the navigation as small capitals — the
   arrangement a printed Speisekarte has used for a century, and the one that survives a long
   German restaurant name without a burger menu.

   NOT STICKY, and that removes a whole class of problem rather than solving it. A two-row
   masthead that follows the reader down the page costs 96–184px of screen, and every sticky
   header on this site is coupled to `scroll-margin-top` values keyed to ITS height (:654, :663,
   :686). A printed card does not follow you; the phone already has the fixed bottom bar. */
/* ==========================================================================
   Startseiten-Bänder — die drei Komponenten, die dieses Stylesheet gelöscht
   hatte, und warum sie zurückkommen dürfen.

   Die Löschnotizen weiter oben (`.scenery-band`, `.section--photo`,
   `.map-card`) sagen dreimal dasselbe: es gab keinen Ort, an dem ein
   Restaurant sein eigenes Bild und seinen eigenen Satz hätte hinterlegen
   können, also hätten zwanzig Häuser denselben leeren Rahmen gerendert. Der
   Einwand war richtig. `App\Site\HomeBands` ist die fehlende Hälfte davon —
   pro Restaurant, im Panel unter «Startseite» zu füllen.

   Was NICHT zurückkommt, und das ist keine Nachlässigkeit:

     · `.section--photo` mit `style="--bg:url(…)"`. `--bg` ist der semantische
       Name des Seitenhintergrunds; ein Band, das ihn auf ein Foto setzt,
       vererbt dieses Foto in jede Karte darin. Die Fotobänder unten tragen
       ein echtes <img> plus Verlauf — wie `.gal-hero` es längst tut. Damit
       gibt es auch alt-Text und loading="lazy", die ein Hintergrundbild nicht
       haben kann.

     · `.home-intern`. Eine Mitarbeitertür auf der Startseite bleibt gelöscht.

     · Vier Stadtseiten in der Fußzeile. Diese Plattform hat genau EINE Stadt
       pro Restaurant (PublicController::localPage) — vier fast gleiche Seiten
       sind eine Doorway-Farm, und der Verzicht ist eine Entscheidung.
   ========================================================================== */

/* -- Fotoband (Herkunft, Vegan) -------------------------------------------- */
/* Ein Band mit Bild, Zeile, Überschrift und Text. Ohne Bild bleibt es ein
   ruhiges dunkles Band und sieht immer noch fertig aus — ein Restaurant ohne
   Landschaftsfoto soll keine leere Fläche bekommen. */
.band-photo {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    min-height: clamp(340px, 52vh, 480px);
    padding: clamp(34px, 6vw, 66px) 0;
    color: var(--on-dark, #FBF8F2);
    background: #1c1610;
}
.band-photo__img {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover; object-position: center;
    z-index: -2;
}
/* Zwei Verläufe, nicht einer: der waagerechte trägt die Schrift links, der
   senkrechte hält die Unterkante dunkel, wo das nächste Band anschließt. Bei
   einem einzigen Verlauf steht heller Text auf hellem Himmel, sobald jemand
   ein Foto mit Wolken hochlädt. */
.band-photo__scrim {
    position: absolute; inset: 0; z-index: -1;
    background:
        linear-gradient(90deg, rgba(18, 12, 7, .82) 0%, rgba(18, 12, 7, .54) 44%, rgba(18, 12, 7, .16) 76%, rgba(18, 12, 7, .04) 100%),
        linear-gradient(0deg, rgba(18, 12, 7, .74) 0%, rgba(18, 12, 7, .26) 38%, rgba(18, 12, 7, 0) 64%);
}
.band-photo__inner { max-width: 600px; }
.band-photo__title { color: #FBF8F2; margin: .14em 0 .32em; text-shadow: 0 2px 22px rgba(0, 0, 0, .42); }
.band-photo__text {
    color: rgba(251, 248, 242, .94);
    font-size: clamp(1.02rem, 2.1vw, 1.2rem);
    line-height: 1.55; max-width: 40em;
    text-shadow: 0 1px 12px rgba(0, 0, 0, .40);
}
.band-photo .eyebrow { color: #F1D79E; text-shadow: 0 1px 10px rgba(0, 0, 0, .45); }
.band-photo .btn--ghost { color: #fff; border-color: rgba(255, 255, 255, .6); }
.band-photo .btn--ghost:hover { color: #fff; border-color: #fff; background: rgba(255, 255, 255, .14); }
/* Das Zitat im Vegan-Band steht rechts neben dem Text, nicht darunter — sonst
   ist das Band auf dem Desktop eine sehr lange schmale Spalte. */
.band-photo__quote {
    font-family: var(--serif); font-style: italic;
    font-size: clamp(1.15rem, 2.4vw, 1.45rem); line-height: 1.4;
    color: rgba(251, 248, 242, .92); max-width: 18em;
    text-shadow: 0 1px 12px rgba(0, 0, 0, .4);
}
@media (min-width: 861px) {
    .band-photo--split .band-photo__inner { max-width: none; }
    .band-photo--split .split { align-items: end; }
}

/* -- Kartenkarte ----------------------------------------------------------- */
/* Ein STANDBILD hinter einem Link, nie ein eingebetteter Kartendienst: eine
   Einbettung wäre eine Fremdanfrage bei jedem Seitenaufruf, sie beendet das
   cookiefreie Versprechen und macht die gedruckte Datenschutzerklärung falsch.
   Das Bild lädt das Restaurant selbst hoch. */
.map-card {
    position: relative; display: block; margin-top: 36px;
    border-radius: var(--r); overflow: hidden;
    box-shadow: var(--shadow); border: 1px solid var(--line-soft);
    text-decoration: none;
}
.map-card__img { width: 100%; height: auto; display: block; aspect-ratio: 2 / 1; object-fit: cover; }
.map-card__overlay {
    position: absolute; left: 0; right: 0; bottom: 0;
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
    gap: 12px; padding: 16px 18px;
    background: linear-gradient(0deg, rgba(19, 12, 7, .86), rgba(19, 12, 7, .12) 70%, transparent);
    color: #fff;
}
.map-card__addr { font-family: var(--serif); font-size: 1.08rem; }
.map-card__btn {
    flex: 0 0 auto; font-weight: 600; font-size: .9rem;
    background: var(--accent); color: var(--on-accent);
    padding: 9px 17px; border-radius: 999px;
    transition: background var(--t-fast);
}
.map-card:hover .map-card__btn { background: var(--accent-strong); }
.map-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.map-attrib { font-size: .74rem; color: var(--muted); margin: 10px 0 0; }


/* ============================================================
   DER LADEN — /bestellen

   ⛔ WAS HIER VORHER STAND: eine Bestellzeile, drei Rasterspalten, eine Trennlinie darunter.
      Gemessen am Laborrestaurant 81 solcher Zeilen in 15 Rubriken, ohne Bild, ohne Beschreibung,
      ohne Weg hindurch. Daneben lag die Seite eines Wettbewerbers, die aussieht wie ein Laden,
      und der Unterschied war nicht Geschmack: unsere Seite war eine PREISLISTE, und eine
      Preisliste verkauft nichts.

   ⚠️ EIN MARKUP-BAUM FÜR ALLE BREITEN (D-30). Es gibt keine zweite Vorlage fürs Telefon und
      keine zweite Rubrikenleiste. Die Seitenleiste ist unter 861px `display: contents` — ihre
      Kinder rutschen damit ins Raster, und die Rubrikenleiste klebt am oberen Rand statt in
      einem hundert Pixel hohen Kasten festzustecken. Darüber ist dieselbe Leiste die senkrechte
      Liste in einer klebenden Spalte.

   ⚠️ ALLE ABSTÄNDE, RUNDUNGEN UND SCHATTEN AUS DER LEITER in tokens.css. In diesem Block steht
      keine rohe Pixelzahl ausser den Trefferflächen (44px, WCAG 2.5.5) und den Bildkanten, die
      eine Rastergrösse sind und keine Abstandsentscheidung.
   ============================================================ */

/*
 * EINE ZAHL FÜR DREI DINGE, damit sie nicht auseinanderlaufen können: die Mindesthöhe des
 * Ladenkopfs, der Abstand, um den die Rubrikenleiste darunter klebt, und der `scroll-margin-top`
 * jedes Sprungziels. Stünde die Höhe dreimal da, verschwände beim nächsten Umbau eine
 * Rubriküberschrift unter der Leiste — und zwar nur auf einer der beiden Breiten.
 */
.page-laden {
    --ladenkopf-h: 58px;
    /* Telefon: Ladenkopf + klebende Rubrikenleiste. */
    --laden-sprung: calc(var(--ladenkopf-h) + 56px);
}

/*
 * ⛔ ZWEI KLEBENDE LEISTEN BEI `top: 0`, UND DIE UNTERE WAR UNSICHTBAR.
 *
 *    `.site-header` klebt mit z-index 40 und einem zu 88 % deckenden Grund; `.catbar` klebt
 *    ebenfalls bei `top: 0`, mit z-index 35. Die Rubrikenleiste lag also hinter dem Seitenkopf.
 *    Die Speisekarte hat für genau das `.page-menu .site-header { position: static }` — auf
 *    `/bestellen` fehlte die Klasse, weil die Leiste dort erst gestern dazukam.
 *
 *    Hier kommt der zweite Grund dazu: der Ladenkopf trägt Marke, Öffnungsstatus und
 *    Telefonnummer. Der Seitenkopf sagte daneben dasselbe ein zweites Mal.
 */
.page-laden .site-header { position: static; }

@media (min-width: 861px) {
    .page-laden {
        --ladenkopf-h: 74px;
        /* Auf dem Desktop klebt nur der Ladenkopf: die Rubriken stehen in der Seitenspalte. */
        --laden-sprung: calc(var(--ladenkopf-h) + var(--s-4));
    }
}

/* -- Der Ladenkopf ---------------------------------------------------------- */
.ladenkopf {
    position: sticky; top: 0; z-index: 40;
    background: color-mix(in srgb, var(--bg) 92%, transparent);
    -webkit-backdrop-filter: saturate(1.3) blur(10px);
            backdrop-filter: saturate(1.3) blur(10px);
    border-bottom: 1px solid var(--line-soft);
}
.ladenkopf__inner {
    display: flex; align-items: center; gap: var(--s-3);
    min-height: var(--ladenkopf-h);
    padding-top: var(--s-2); padding-bottom: var(--s-2);
}
/* Dieselbe Regel wie bei `.brand__mark`: die Höhe gibt das Mass, die Breite folgt dem Bild. */
.ladenkopf__mark {
    height: 40px; width: auto; max-width: 150px; object-fit: contain; flex: 0 0 auto;
    border-radius: var(--r-sm); background: var(--plate); padding: 3px;
    box-shadow: var(--shadow-xs);
}
.ladenkopf__wer { flex: 1 1 auto; min-width: 0; }

/* ⛔ `.ladenkopf__name` ALLEIN REICHT NICHT, UND «GLEICHE SPEZIFITÄT, SPÄTERE QUELLE» AUCH NICHT.
      `.layout-kontor h1` ist (0,2,0) und schlägt eine einzelne Klasse; im Kontor-Aufbau wäre der
      Name hier 2,8rem gross und die Leiste doppelt so hoch, wie ihr eigenes Token behauptet —
      und damit zeigte jedes Sprungziel unter die Leiste.

      Der erste Entwurf löste das mit zwei Klassen und der Quellreihenfolge. Das hielt genau so
      lange, bis dieser Block VOR den Kontor-Block musste (`LayoutKontorTest` liest alles hinter
      der ersten `.layout-kontor`-Regel als dessen Block und verlangt, dass jede Regel darin
      gebunden ist). Eine Zusage, die von der Reihenfolge im File abhängt, ist keine — drei
      Klassen gewinnen, wo die Datei auch steht. */
.page-laden .ladenkopf .ladenkopf__name {
    margin: 0; font-size: var(--fs-3); line-height: 1.15; letter-spacing: 0;
    /* Bricht der Name um, wächst die Leiste über `--ladenkopf-h` hinaus — und dann zeigen alle
       Sprungziele auf eine Stelle, die unter ihr liegt. Ein langer Hausname darf die
       Rubrikenleiste nicht verschieben. */
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ladenkopf__meta {
    margin: 1px 0 0; font-family: var(--sans); font-size: var(--fs-0);
    color: var(--muted); line-height: 1.35;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ladenkopf__punkt { margin: 0 .4em; opacity: .55; }
.ladenkopf__abholung { color: var(--text); font-weight: 600; }

/* DIE FARBE TRÄGT DIE AUSSAGE NICHT ALLEIN. «Jetzt geöffnet» und «Gerade geschlossen» sagen es
   im Text; grün und weinrot sind die Verstärkung, nicht die Nachricht. Ein rotgrünblinder Gast
   erführe sonst nichts. `--enamel` ist die Farbe, mit der dieses Blatt seit jeher «ja» sagt
   (vegan, offen heute in der Zeitenliste), `--wine` die für die Gegenrichtung. */
.ladenstatus { font-weight: 700; }
.ladenstatus--offen { color: var(--enamel); }
.ladenstatus--zu { color: var(--wine); }

.ladenkopf__tel {
    flex: 0 0 auto; min-height: 44px;
    padding: var(--s-2) var(--s-4); border-radius: var(--r-tag);
}
.ladenkopf__tel svg { flex: 0 0 auto; }

@media (max-width: 560px) {
    /* Auf 375px muss die Zeile den Status tragen; die Adresse steht im Fussbereich und auf der
       Startseite und ist hier die entbehrlichere Hälfte. Die Ziffern der Telefonnummer ebenso:
       niemand tippt sie ab, `tel:` wählt. Der runde Hörer bleibt eine 44px-Trefferfläche. */
    .ladenkopf__ort, .ladenkopf__ort + .ladenkopf__punkt { display: none; }
    .ladenkopf__telnr { display: none; }
    .ladenkopf__tel { min-width: 44px; padding: 0; }
}

/* -- Das Band, der Fehlerkasten, die Hausnotiz ------------------------------ */
.laden { padding-bottom: var(--s-7); }

/* Das Foto des Hauses, aus `--hero-photo` — dieselbe Datei, die die Speisekarte schon geladen
   hat, also keine zweite Anfrage. Flach gedeckelt: das Vorbild nimmt hier eine halbe
   Bildschirmhöhe, und die zwei Abholfelder darunter sind die gemessen wichtigste Zeile. */
.ladenbanner {
    margin-top: var(--s-4);
    aspect-ratio: 21 / 7; max-height: 240px;
    border-radius: var(--r);
    background: var(--hero-photo, none) center 55% / cover no-repeat, var(--plate);
    box-shadow: var(--shadow-sm);
}

.ladenmeldung {
    margin-top: var(--s-4);
    border: 1px solid color-mix(in oklab, var(--wine) 34%, var(--line));
    border-left-width: 4px;
    border-radius: var(--r-md);
    background: color-mix(in oklab, var(--wine) 6%, var(--surface));
    padding: var(--s-3) var(--s-4);
}
.ladenmeldung p { margin: 0; }
.ladennotiz { margin-top: var(--s-4); color: var(--muted); font-size: var(--fs-1); }

/* -- Die Abholzeile --------------------------------------------------------- */
/* Sie sieht aus wie ein Band und ist trotzdem nur eine Formularzeile — dieselben zwei Felder,
   die vorher bei 95 % der Seite standen. Der Rahmen macht aus zwei verirrten Feldern über dem
   Essen eine AUSKUNFT: wo, und wann kann ich es holen. */
/* SCHLANK: eine Zeile «wo & wie lange», darunter eine Zeile «wie». Früher ein hohes Feld mit
   zwei Absätzen; jetzt ein flaches Band, das den Blick nicht vom Kopf zur Karte aufhält. */
.abholband {
    margin: var(--s-4) 0 var(--s-5);
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--r);
    box-shadow: var(--shadow-sm);
    padding: var(--s-3) var(--s-4);
}
.abholband__was {
    display: flex; align-items: center; flex-wrap: wrap; gap: var(--s-1) var(--s-2);
    margin: 0; font-size: var(--fs-1); color: var(--muted);
}
.abholband__was b { color: var(--text); }
.abholband__ic { width: 18px; height: 18px; flex: 0 0 auto; color: var(--gold-ink); }
.abholband__punkt { margin: 0 .15em; opacity: .5; }
.abholband__hinweis { margin: var(--s-2) 0 0; font-size: var(--fs-0); color: var(--muted); }
.abholband__hinweis b { color: var(--text); }

/* Der Rahmen sitzt jetzt eine Ebene höher, am Band. Was diese Klasse noch tut, ist der Abstand
   zwischen den beiden Feldern — und die Randlosigkeit darunter, damit das Band nicht unten
   auseinanderfällt. */
.resform__row--wann { gap: var(--s-3); }
.resform__row--wann .resform__field { margin-bottom: 0; }

/* -- «Am beliebtesten» ------------------------------------------------------ */
/*
 * ⚠️ KEINE PFEILKNÖPFE, UND DAS IST BESSER ALS DIE VORLAGE. Sie bräuchten ein Skript, also
 *    müssten sie ohne Skript versteckt sein — und über 861px steht der ganze Streifen ohnehin
 *    auf einmal da, es gäbe also nichts zu blättern. Auf dem Telefon wischt man. Ein Knopf, der
 *    entweder unsichtbar oder überflüssig ist, ist kein Knopf.
 */
.beliebt { padding: 0; margin: 0 0 var(--s-6); }
.beliebt__kopf { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-1) var(--s-3); margin-bottom: var(--s-3); }
.page-laden .beliebt .beliebt__titel { margin: 0; font-size: var(--fs-4); }
.beliebt__quelle { margin: 0; font-size: var(--fs-0); color: var(--muted); }

.beliebt__spur {
    list-style: none; margin: 0; padding: 0 0 var(--s-3);
    display: grid; grid-auto-flow: column; grid-auto-columns: 44%;
    gap: var(--s-3);
    overflow-x: auto; overscroll-behavior-x: contain;
    scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.beliebt__spur::-webkit-scrollbar { height: 0; }

.beliebtkarte { min-width: 0; scroll-snap-align: start; }
.beliebtkarte__link { display: block; text-decoration: none; color: var(--text); }
.beliebtkarte__bild { position: relative; display: block; }
.beliebtkarte__foto {
    width: 100%; aspect-ratio: 1 / 1; object-fit: cover;
    border-radius: var(--r-md); box-shadow: var(--shadow-sm);
    transition: box-shadow var(--t-fast);
}
.beliebtkarte__foto.ph-tile .ph-tile__mono { font-size: 2.6rem; }
/*
 * DIE ZAHL IST DIE BEHAUPTUNG, und sie ist gezählt: `Menu::mostOrderedDishes()` misst
 * Bestellungen der letzten dreissig Tage. Deshalb steht «#1» da und kein Pokal — eine
 * Auszeichnung behauptet ein Urteil, eine Rangzahl behauptet eine Zählung.
 */
.beliebtkarte__rang {
    position: absolute; left: var(--s-2); top: var(--s-2);
    font-family: var(--sans); font-size: var(--fs-0); font-weight: 700;
    background: color-mix(in oklab, var(--surface) 92%, transparent);
    color: var(--gold-ink);
    padding: 2px var(--s-2); border-radius: var(--r-tag);
    box-shadow: var(--shadow-xs);
}
.beliebtkarte__name {
    display: block; margin-top: var(--s-2);
    font-weight: 600; font-size: var(--fs-1); line-height: 1.3;
    overflow-wrap: anywhere;
    transition: color var(--t-fast);
}
.beliebtkarte__fuss {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--s-2); margin: var(--s-1) 0 0; min-height: 44px;
}
.beliebtkarte__preis { font-family: var(--serif); font-weight: 600; color: var(--accent-ink); }

/* Die Listenform des Streifens — wenn nicht jedes Gericht ein Foto hat. Rang, Name, Preis in
   einer Zeile; die Zeile ist der Verweis auf das Gericht weiter unten. Kein Bild, kein
   Monogramm: eine Liste ohne Bühne ist ehrlicher als eine Bühne ohne Bild. */
.beliebt__liste { list-style: none; margin: 0; padding: 0; display: grid; gap: 4px; }
.beliebtzeile { margin: 0; }
.beliebtzeile__link {
    display: grid; grid-template-columns: 1.6rem 1fr auto; align-items: baseline; gap: var(--s-2);
    padding: 10px 12px; border-radius: var(--r-sm); background: var(--surface);
    color: var(--text); text-decoration: none; min-height: 44px;
}
.beliebtzeile__link:hover { background: var(--line-soft); }
.beliebtzeile__rang { font-size: var(--fs-0); font-weight: 600; color: var(--muted); font-variant-numeric: tabular-nums; }
.beliebtzeile__name { font-weight: 500; }
.beliebtzeile__preis { font-weight: 600; color: var(--accent-ink); font-variant-numeric: tabular-nums; white-space: nowrap; }

.beliebtkarte__plus {
    display: inline-flex; align-items: center; justify-content: center; gap: 3px;
    min-width: 44px; min-height: 44px; padding: 0 var(--s-2); flex: 0 0 auto;
    border: 1px solid var(--accent); border-radius: var(--r-tag);
    background: var(--accent); color: var(--on-accent); cursor: pointer;
    font: inherit; font-size: var(--fs-4); line-height: 1;
    font-variant-numeric: tabular-nums;
    box-shadow: var(--shadow-xs);
    transition: background var(--t-fast), box-shadow var(--t-fast);
}
.beliebtkarte__plus:hover { background: var(--accent-strong); box-shadow: var(--shadow-sm); }
/* DERSELBE ENGE FOKUSRING WIE AM MENGENFELD (`.orderline__qty`). Der globale `:focus-visible`
   (site.css :58) versetzt den Rand um 3px und legt einen 5px-Hof darum — richtig für ein Feld
   mit Luft ringsum, zu viel für einen 44px-Knopf in einem gedrängten Kartenfuss. Versatz 1px
   hält den Ring am Knopf. Beide «+» der Seite fokussieren damit gleich. */
.beliebtkarte__plus:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.beliebtkarte__zahl { font-size: var(--fs-1); font-weight: 700; }
.beliebtkarte__zahl:empty { display: none; }

/* ⛔ DIE EINZIGE KARTE DER SEITE, DIE AUF DEN ZEIGER NICHT ANTWORTETE. Die ganze Kachel ist ein
      Link, und Hover tat nichts — während die Gerichtkarte darunter sich längst hebt. Foto steigt
      eine Schattenstufe (`--shadow-md`, über der Ruhe-Stufe `--shadow-sm`), der Name nimmt den
      Akzent an. Dasselbe Vokabular wie bei `.orderline:hover`, nur ohne Rahmen: der Streifen hat
      keinen, seine Karten sind Bild und Wort. In beiden Modi getragen — `--accent`/`--shadow-md`
      sind Tokens, keine festen Farben. */
.beliebtkarte__link:hover .beliebtkarte__foto { box-shadow: var(--shadow-md); }
.beliebtkarte__link:hover .beliebtkarte__name { color: var(--accent-ink); }

@media (min-width: 861px) {
    /*
     * ⛔ ZURÜCK ZUM RASTER — ABER OHNE DEN FEHLER, DER ES DAMALS ZURÜCKGENOMMEN HAT.
     *
     *    Der 178px-Streifen von einst löste das «zu gross», schuf aber am Screenshot ein neues,
     *    gemessenes Problem: auf dem Desktop, wo ohnehin alle vier Karten ins Bild passen, klebten
     *    sie schmal am linken Rand, rechts blieb die halbe Breite leer, und jede Karte war ein
     *    schmales hohes Rechteck mit verrutschtem Preis. Ein waagerechter Scroll-Streifen ist auf
     *    dem Telefon richtig (dort wischt man) und auf dem Desktop verschenkte Fläche.
     *
     * ⚠️ DER BETREIBER WILL HIER EIN GLEICHMÄSSIGES RASTER. `repeat(4, 1fr)` verteilt die (auf VIER
     *    gedeckelten — `mostOrderedDishes(4)`) Karten über die volle Breite; sind es einmal weniger
     *    als vier, sitzen sie in den ersten Spalten statt schmal am Rand zu kleben. `grid-auto-flow:
     *    row` und `overflow-x: visible` heben den Streifen-Grundzustand (Spaltenfluss, Scroll) auf.
     *
     * ⛔ UND DAMIT NICHT WIEDER DAS MENÜ UNTER DEN FALZ RUTSCHT — GENAU DER GRUND DES DAMALIGEN
     *    RÜCKZUGS: das quadratische Foto war die Ursache, bei rund 249px Kartenbreite (Inhalt von
     *    `--wrap` 1080px, vier Spalten) wurde es 249px hoch. Auf dem Desktop bekommt es darum ein
     *    flacheres Seitenverhältnis (4:3 ≈ 187px statt 249px), und die Karte liest sich als Karte,
     *    nicht als Turm. Unter 861px bleibt das Quadrat (1:1) und der Scroll-Streifen (Telefon).
     *
     * ⚠️ NAME + PREIS SITZEN SAUBER: die Karte wird eine senkrechte Flex-Spalte, der Fuss mit dem
     *    Preis rutscht per `margin-top: auto` an den unteren Rand. Weil die Rasterzellen einer Reihe
     *    gleich hoch sind, stehen die Preise über alle vier Karten auf einer Linie — auch wenn ein
     *    Gerichtname zwei Zeilen braucht und der nächste eine.
     */
    .beliebt__spur {
        grid-auto-flow: row;
        grid-template-columns: repeat(4, 1fr);
        overflow-x: visible;
    }
    .beliebtkarte { display: flex; flex-direction: column; }
    .beliebtkarte__fuss { margin-top: auto; }
    .beliebtkarte__foto { aspect-ratio: 4 / 3; }
}

/* -- Das Raster: links der Weg, rechts das Essen ---------------------------- */
.laden__raster { display: block; }

/*
 * ⛔ `position: sticky` IN EINEM KURZEN ELTERNTEIL KLEBT NICHT. Ein klebendes Element wird von
 *    der Box seines Elternteils begrenzt; steckte die Rubrikenleiste auf dem Telefon in einer
 *    Seitenspalte, die nach hundert Pixeln endet, wäre sie nach hundert Pixeln wieder weg —
 *    also genau dann, wenn man sie braucht.
 *
 *    `display: contents` löst die Spalte auf: ihre Kinder werden zu Kindern des Rasters, dessen
 *    Box die ganze Karte umfasst. EIN Markup-Baum, zwei Anordnungen, kein zweites Element.
 */
.laden__seite { display: contents; }

.ladensuche {
    display: flex; align-items: center; gap: var(--s-2);
    background: var(--surface); border: 1px solid var(--line);
    border-radius: var(--r-tag); padding: 0 var(--s-3);
    margin-bottom: var(--s-3);
}
.ladensuche:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent);
}
.ladensuche__ic { width: 18px; height: 18px; flex: 0 0 auto; color: var(--muted); }
.ladensuche__feld {
    flex: 1 1 auto; min-width: 0; min-height: 44px;
    border: 0; background: none; color: var(--text); font: inherit; font-size: var(--fs-1);
}
.ladensuche__feld:focus { outline: none; }
.ladensuche__feld::placeholder { color: var(--muted); }

/* Die Rubrikenleiste klebt UNTER dem Ladenkopf statt hinter ihm. */
.page-laden .catbar { top: var(--ladenkopf-h); }
.page-laden .mcat { padding: var(--s-5) 0 0; }
.page-laden .mcat,
.page-laden [id^="best-"],
.page-laden [id^="gericht-"],
/*
 * ⛔ DAS ABHOLBAND WURDE OBEN VOM KLEBENDEN LADENKOPF ANGESCHNITTEN. Es ist das erste Feld unter
 *    dem klebenden Kopf, und bislang stand es nicht in dieser Liste. Beim Absenden springt der
 *    Browser zum ersten leeren Pflichtfeld — `pickup_date`/`pickup_time` liegen GENAU hier —, und
 *    dessen Oberkante landete unter der Leiste: die Sprechblase «Bitte ausfüllen» zeigte auf etwas
 *    Verdecktes. Auch «Am beliebtesten» steht direkt unter dem Kopf und bekommt denselben Rand.
 *    Ein eigener `scroll-margin`-Rand am FELD, weil der Browser das Feld ins Bild holt, nicht das
 *    Band um es herum.
 */
.page-laden .beliebt,
.page-laden .abholband,
.page-laden .abholband input,
.page-laden .abschluss { scroll-margin-top: var(--laden-sprung); }

.laden__karte {
    /* Die Gerichtkarte fragt IHRE Spalte, nicht den Bildschirm. Bei 1280px steht sie in einer
       800px breiten Spalte und soll zweispaltig sein; auf einem 800px breiten Tablet ohne
       Seitenspalte gilt dieselbe Rechnung. Ein Breakpoint auf die Fensterbreite kennt den
       Unterschied nicht. */
    container-type: inline-size;
}
.mcat__laden { display: grid; grid-template-columns: 1fr; gap: var(--s-3); }

@container (min-width: 620px) {
    .mcat__laden { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/*
 * ⛔ DIE SPEISEKARTE-IM-LADEN WAR ZWEISPALTIG UND QUETSCHTE DEN NAMEN. `.mcat__grid` verteilt
 *    `minmax(340px, 1fr)` — in der Ladenspalte (kein Seitenrand unter 861px, darüber neben der
 *    232px-Leiste) ergab das bei ~800px Ansichtsbreite zwei ~370px-Karten. In einer so schmalen
 *    Karte frass der Führungspunkt-Streifen (`flex: 1 1 auto`) den freien Platz, der Name schrumpfte
 *    auf seine min-content-Breite (~100px) und `hyphens` trennte jede Zeile silbenweise:
 *    «Gebra-tene Au-bergi-nen mit Walnüs-sen».
 *
 * ⚠️ EINE SPALTE, WIE EINE GEDRUCKTE KARTE. Volle Zeilenbreite heisst positiver Freiraum: der
 *    Name steht auf EINER Zeile, die Führungspunkte dehnen sich bis zum Preis, der Preis steht
 *    bündig rechts. Kein Umbruch, keine Trennung. Nur die Laden-Ansicht (`.laden__karte`); die
 *    gewöhnliche Speisekarte ohne Bestellen behält ihr `.mcat__grid` unangetastet.
 */
.laden__karte .mcat__grid { grid-template-columns: 1fr; }

@media (min-width: 861px) {
    .laden__raster {
        display: grid; grid-template-columns: 232px minmax(0, 1fr);
        gap: var(--s-6); align-items: start;
    }
    .laden__seite {
        display: block;
        position: sticky; top: calc(var(--ladenkopf-h) + var(--s-4));
        /* Fünfzehn Rubriken sind länger als mancher Bildschirm hoch ist. */
        max-height: calc(100vh - var(--ladenkopf-h) - var(--s-6));
        overflow-y: auto; scrollbar-width: thin;
    }

    /* Dieselbe Leiste, senkrecht. Die waagerechte Fassung bringt Rahmen, Weichzeichner,
       Randverlauf und Grossbuchstaben mit — in einer Spalte ist davon nichts richtig. */
    .page-laden .laden__seite .catbar {
        position: static; background: none; border: 0;
        -webkit-backdrop-filter: none; backdrop-filter: none;
    }
    .page-laden .laden__seite .catbar__inner { display: block; }
    .page-laden .laden__seite .catnav {
        flex-direction: column; flex-wrap: nowrap; gap: 1px; padding: 0;
        overflow: visible; -webkit-mask-image: none; mask-image: none;
    }
    .page-laden .laden__seite .catbar__seam { display: none; }
    .page-laden .laden__seite a.chip--cat {
        display: block; padding: var(--s-2) var(--s-3);
        font-family: var(--sans); font-size: var(--fs-1); font-weight: 500;
        text-transform: none; letter-spacing: 0;
        border-radius: var(--r-sm); color: var(--muted);
        transition: background var(--t-fast), color var(--t-fast);
    }
    .page-laden .laden__seite a.chip--cat::after { display: none; }
    .page-laden .laden__seite a.chip--cat + a.chip--cat::before { display: none; }
    .page-laden .laden__seite a.chip--cat:hover {
        background: color-mix(in oklab, var(--accent) 7%, transparent); color: var(--text);
    }
    .page-laden .laden__seite a.chip--cat.is-active {
        background: color-mix(in oklab, var(--accent) 12%, transparent);
        color: var(--accent-ink); font-weight: 600;
    }
}

/* -- Die Gerichtkarte -------------------------------------------------------- */
/*
 * ⛔ HIER STAND EINE ZEILE MIT EINER TRENNLINIE DARUNTER, und das war die Preislisten-Optik.
 *
 * ⚠️ DAS BILD SPANNT ZWEI ZEILEN, die Menge steht in der zweiten. Die naheliegende Fassung —
 *    Bild, Text und Menge nebeneinander — kostet in einer zweispaltigen Karte 84 + 152 Pixel,
 *    und vom Gerichtnamen bleiben in einer 300px-Spalte etwa sechzig übrig. Die Menge unter den
 *    Text zu legen gibt dem Namen die ganze Breite und dem Daumen trotzdem jedes Mal dieselbe
 *    Ecke.
 */
.orderline {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "text" "opts" "menge";
    column-gap: var(--s-3); row-gap: var(--s-2);
    padding: var(--s-4);
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--r);
    box-shadow: var(--shadow-xs);
    transition: border-color var(--t-fast), box-shadow var(--t-fast);
}
.orderline:hover {
    border-color: color-mix(in oklab, var(--gold) 44%, var(--line-soft));
    box-shadow: var(--shadow-sm);
}
/* Tastaturfokus hebt die Karte GENAUSO wie der Zeiger. Vorher wechselte nur der Rand die Farbe
   und die Karte blieb flach, während `:hover` sie mit `--shadow-sm` anhob — wer die Seite mit der
   Tabulatortaste bedient, bekam das schwächere Signal. Jetzt dieselbe Erhebung; der Rand bleibt
   `--accent` statt des Gold-Tons vom Hover, weil Fokus die stärkere Aussage ist. */
.orderline:focus-within { border-color: var(--accent); box-shadow: var(--shadow-sm); }

.orderline--foto {
    grid-template-columns: 84px minmax(0, 1fr);
    grid-template-areas: "foto text" "foto opts" "foto menge";
}
.orderline__foto {
    grid-area: foto; align-self: start;
    width: 84px; height: 84px; object-fit: cover;
    border-radius: var(--r-md);
    box-shadow: var(--shadow-xs);
}
.orderline__text { grid-area: text; min-width: 0; }

/* DIE OPTIONEN DES GERICHTS (Faz C) — zwischen Name und Menge, damit der Gast wählt, BEVOR er
   die Anzahl setzt. Ruhig gehalten: es ist ein Formular, kein zweiter Blickfang neben dem Preis. */
.orderline__opts { grid-area: opts; display: flex; flex-direction: column; gap: var(--s-2); }
.optgrp { display: flex; flex-direction: column; gap: 5px; border: 0; padding: 0; margin: 0; min-inline-size: 0; }
.optgrp__name { font-family: var(--sans); font-size: var(--fs-0); font-weight: 600; color: var(--muted); }
.optgrp__hint { font-weight: 400; }
.optgrp__select {
    font: inherit; font-size: var(--fs-0);
    padding: 8px 12px; max-width: 22rem;
    color: var(--text); background: var(--surface);
    border: 1px solid var(--line); border-radius: var(--r-md);
}
.optgrp--single { gap: 4px; }
.optgrp--multi { gap: 6px; }
.optchk { display: inline-flex; align-items: center; gap: 9px; font-size: var(--fs-0); cursor: pointer; }
.optchk input { inline-size: 17px; block-size: 17px; accent-color: var(--accent); flex: 0 0 auto; }
.optchk__text { min-inline-size: 0; }
.orderline__opts .codes { margin-left: 4px; }

.orderline__label { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2); cursor: pointer; }
.orderline__name { font-weight: 600; font-size: var(--fs-2); line-height: 1.3; overflow-wrap: anywhere; }
.orderline__var { color: var(--muted); font-size: var(--fs-0); }
.orderline__price {
    display: block; margin-top: 2px;
    font-family: var(--serif); font-weight: 600; font-size: var(--fs-3);
    color: var(--accent-ink); font-variant-numeric: tabular-nums;
}
/* ZWEI ZEILEN, ABGESCHNITTEN. Vollständig gesetzt wächst eine Karte mit einem langen Absatz auf
   das Dreifache ihrer Nachbarin und das Raster wird zum Flickenteppich. Der ganze Text steht auf
   der Speisekarte; was das Gesetz verlangt, sind die Buchstaben darunter, und die sind
   vollständig. */
.orderline__desc {
    margin: var(--s-2) 0 0; color: var(--muted); font-size: var(--fs-1); line-height: 1.45;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
    overflow-wrap: anywhere;
}
.orderline__text .codes { display: inline-block; margin-top: var(--s-2); }

.orderline__menge {
    grid-area: menge; justify-self: end;
    display: flex; align-items: center; gap: var(--s-1);
}
.orderline__step {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 44px; min-height: 44px; padding: 0; flex: 0 0 auto;
    border: 1px solid var(--line); border-radius: var(--r-tag);
    background: var(--bg); color: var(--text); cursor: pointer;
    font: inherit; font-size: var(--fs-4); line-height: 1;
    transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast), box-shadow var(--t-fast);
}
.orderline__step:hover { border-color: var(--accent); color: var(--accent-ink); }
/* DERSELBE ENGE FOKUSRING WIE AM MENGENFELD, und aus einem greifbaren Grund: der globale
   `:focus-visible` versetzt den Rand um 3px, im gedrängten Cluster «− [Zahl] +»
   (`.orderline__menge` gap `--s-1`, 4–6px) rückt er damit fast ans Nachbarfeld. Versatz 1px
   hält den Ring am Knopf — genau die Fassung, die `.orderline__qty` daneben schon trägt, damit
   der ganze Cluster gleich fokussiert. */
.orderline__step:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.orderline__step--plus {
    background: var(--accent); border-color: var(--accent); color: var(--on-accent);
    /* Der akzentgefüllte «+» ist die Haupthandlung der Zeile und trug als einziger seiner Art
       keinen Schatten — sein Zwilling im Streifen (`.beliebtkarte__plus`) hat `--shadow-xs` seit
       jeher. Gleiche Rolle, gleiche Erhebung. Der «−» daneben bleibt bewusst flach: er ist die
       Gegenhandlung und kein gefüllter Knopf. */
    box-shadow: var(--shadow-xs);
}
.orderline__step--plus:hover {
    background: var(--accent-strong); border-color: var(--accent-strong); color: var(--on-accent);
    box-shadow: var(--shadow-sm);
}

/* 44px hoch, weil es mit dem Daumen auf dem Bürgersteig getroffen wird — dieselbe Untergrenze
   wie bei der mobilen Leiste. `tabular-nums`, damit 1 und 11 die Feldbreite nicht verschieben. */
.orderline__qty {
    width: 62px; min-height: 44px; text-align: center; flex: 0 0 auto;
    font: inherit; font-variant-numeric: tabular-nums;
    border: 1px solid var(--line); border-radius: var(--r-sm);
    background: var(--bg); color: var(--text); padding: 6px 2px;
}
.orderline__qty:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* -- «In den Warenkorb» an der Gerichtzeile --------------------------------- */
/*
 * DERSELBE KNOPF WIE `.mdish__add` AUF DER SPEISEKARTE, nur schreibt er hier in das Mengenfeld
 * daneben statt in `localStorage` — auf `/bestellen` IST das Formular der Korb. Wie dort ist er
 * ohne Skript `display:none` (nicht `hidden`-Attribut), damit ein Browser ohne `cart.js` nie
 * einen Knopf sieht, der nichts täte; der Weg ist dann das Mengenfeld.
 *
 * ⚠️ ER UND DER STEPPER TAUSCHEN SICH AUS, statt beide dazustehen. Solange nichts gewählt ist
 *    (`:not(.is-gewaehlt)`), steht der «In den Warenkorb»-Knopf da und Feld + Stepper treten
 *    zurück; sobald `cart.js` beim ersten Antippen `is-gewaehlt` setzt, greift die Regel nicht
 *    mehr, der Knopf fällt auf seinen `display:none`-Grundzustand zurück und der Stepper-Cluster
 *    ist wieder da. Das ist dieselbe Umschaltung, die der Warenkorb der Speisekarte im Panel
 *    macht — hier an Ort und Stelle, weil `/bestellen` kein zweites Panel hat.
 */
.orderline__add { display: none; }
.has-cart .orderline:not(.is-gewaehlt) .orderline__add {
    display: inline-flex; align-items: center; justify-content: center; gap: var(--s-1);
    min-height: 44px; padding: 0 var(--s-4); flex: 0 0 auto;
    border: 1px solid var(--accent); border-radius: var(--r-tag);
    background: var(--accent); color: var(--on-accent); cursor: pointer;
    font: inherit; font-size: var(--fs-1); font-weight: 600;
    box-shadow: var(--shadow-xs);
    transition: background var(--t-fast), box-shadow var(--t-fast);
}
.has-cart .orderline:not(.is-gewaehlt) .orderline__add:hover {
    background: var(--accent-strong); box-shadow: var(--shadow-sm);
}
/* DERSELBE ENGE FOKUSRING WIE AM «+» daneben (`.orderline__step--plus`): der globale
   `:focus-visible` versetzt den Rand um 3px und legt einen Hof darum — zu viel für einen Knopf
   im gedrängten Mengen-Cluster. Versatz 1px hält den Ring am Knopf. */
.has-cart .orderline:not(.is-gewaehlt) .orderline__add:focus-visible {
    outline: 2px solid var(--accent); outline-offset: 1px;
}
.orderline__add-plus { font-size: 1.15em; line-height: 1; }

/* Solange nichts gewählt ist, tritt der Mengen-Cluster hinter den Knopf zurück. Ist etwas
   gewählt, greift die Regel nicht mehr und Feld + Stepper stehen wieder da (der Stepper hatte sein
   `hidden`-Attribut da bereits von `cart.js` verloren). */
.has-cart .orderline:not(.is-gewaehlt) .orderline__qty,
.has-cart .orderline:not(.is-gewaehlt) .orderline__step { display: none; }

/* -- Der Abschluss ---------------------------------------------------------- */
/* Hier hört das Aussuchen auf und fängt das Bestellen an, und der Wechsel darf zu sehen sein. */
.abschluss {
    margin-top: var(--s-7);
    background: var(--surface);
    border: 1px solid var(--line-soft);
    border-radius: var(--r);
    box-shadow: var(--shadow-sm);
    padding: var(--s-5);
}
.page-laden .abschluss .abschluss__titel { margin: 0 0 var(--s-4); font-size: var(--fs-4); }
/* Die Felder liegen auf der Karte, also brauchen sie den Grund der SEITE, sonst verschwindet
   ihre Kante in der Fläche, auf der sie liegen. */
.abschluss .resform__field input,
.abschluss .resform__field textarea { background: var(--bg); }
.abschluss__recht { font-size: var(--fs-0); color: var(--muted); line-height: 1.55; }
.abschluss__senden { padding: var(--s-3) var(--s-6); font-size: var(--fs-2); min-height: 48px; }

@media (max-width: 560px) {
    .orderline { padding: var(--s-3); }
    .orderline--foto { grid-template-columns: 68px minmax(0, 1fr); }
    .orderline__foto { width: 68px; height: 68px; }
    .abschluss { padding: var(--s-4); }

    /* ⛔ DER STREIFEN WAR DAS GRÖSSTE HINDERNIS ZWISCHEN GAST UND KARTE — gemessen, nicht
       vermutet. Auf einem 390×844-Gerät lag das erste Gericht bei 1437px, also 1,7 Schirme
       tief. Die Aufteilung darüber:

           Kopfleiste       61px
           Hero            203px
           Abholband       170px
           «Am beliebtesten» 526px   ← mehr als Hero und Abholband zusammen
           Suche/Filter     54px
           Rubrikenleiste   48px

       526px kamen aus EINEM Bild: die Karte war 360px breit, `aspect-ratio: 1/1` machte daraus
       360px Höhe. Ein Empfehlungsstreifen, dessen einzelne Kachel höher ist als der Hero, ist
       kein Streifen mehr, sondern eine zweite Startseite.

       ⚠️ 4:3 STATT 1:1, und schmaler: 52% statt 58%. Beides zusammen holt das erste Gericht
          deutlich näher — und die schmalere Kachel bringt den Nebeneffekt, der ohnehin gefehlt
          hat: die NÄCHSTE Karte lugt am Rand herein, und damit sieht ein Gast, dass sich hier
          etwas schieben lässt. Vorher endete die Kachel bündig mit dem Rand und der Streifen
          sah aus wie ein einzelnes Bild. */
    .beliebt__spur { grid-auto-columns: 52%; }

    /* ⚠️ `height: auto` GEHÖRT DAZU, SONST TUT `aspect-ratio` NICHTS. `dishthumb` schreibt
       `width`/`height` als ATTRIBUTE ins `<img>` — richtig so, sie reservieren den Platz und
       verhindern das Springen des Layouts beim Laden. Als Präsentations-Hinweis setzen sie aber
       eine feste Höhe, und `aspect-ratio` greift nur, wenn eine der beiden Achsen `auto` ist.
       Gemessen: Karte 180px breit, Bild trotzdem 360px hoch — die Regel war da und wirkte nicht. */
    .beliebtkarte__foto { aspect-ratio: 4 / 3; height: auto; }
}

@media (prefers-reduced-motion: reduce) {
    .orderline, .orderline__step, .beliebtkarte__plus,
    .beliebtkarte__foto, .beliebtkarte__name,
    .page-laden .laden__seite a.chip--cat { transition: none; }
    .beliebt__spur { scroll-snap-type: none; }
}

/* -- Die Ladenleiste am unteren Rand ---------------------------------------- */
/*
 * ⛔ DER ABSENDEKNOPF LAG HINTER 81 GERICHTEN, bei 96 % der Seite. Das Zusammenklappen greift
 *    nur bei jemandem, der MIT einem Korb von der Speisekarte kommt; wer direkt hier anfängt und
 *    vier Karten antippt, musste an der ganzen Karte vorbeiscrollen.
 *
 * ⚠️ SIE FÜHRT ZUM ABSCHLUSS UND SCHICKT NICHT AB. Ein Absendeknopf in einer schwebenden Leiste
 *    wirft den Gast an Pflichtfelder, die er nie gesehen hat.
 *
 * ⚠️ `env(safe-area-inset-bottom)` — auf einem iPhone liegt sonst die Hälfte der Leiste unter
 *    dem Home-Balken, also genau der Knopf.
 */
.ladenbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 46;
    background: color-mix(in oklab, var(--surface) 94%, transparent);
    -webkit-backdrop-filter: saturate(1.2) blur(10px);
            backdrop-filter: saturate(1.2) blur(10px);
    border-top: 1px solid var(--line);
    box-shadow: var(--shadow-xl);
    padding: var(--s-3) 0 calc(var(--s-3) + env(safe-area-inset-bottom, 0px));
}
.ladenbar__inner { display: flex; align-items: center; gap: var(--s-3); }
.ladenbar__was { margin: 0; font-size: var(--fs-1); min-width: 0; }
.ladenbar__summe {
    margin-left: var(--s-2); font-family: var(--serif); font-weight: 600;
    color: var(--accent-ink); font-variant-numeric: tabular-nums;
}
.ladenbar__weiter { margin-left: auto; flex: 0 0 auto; min-height: 44px; }

/* ZWEI FESTE LEISTEN KÖNNEN NICHT BEIDE DEN UNTEREN RAND HABEN. Die Ladenleiste gewinnt,
   solange etwas gewählt ist — ein Gast mitten in einer Bestellung tut genau das, wofür sie da
   ist — und die Daumenleiste tritt ab, statt sich darüber zu stapeln. Dieselbe Regel wie beim
   Warenkorb der Speisekarte, und derselbe Geschwister-Kombinator: er kann nicht rückwärts
   schauen, also steht die Ladenleiste im Layout VOR `.mobilebar`. */
.ladenbar:not([hidden]) ~ .mobilebar { display: none; }

/* Platz, damit die Leiste den letzten Absatz nicht verdeckt. Unbedingt und nicht nur wenn sie
   steht: eine Seite, deren Fusszeile beim ersten «+» springt, sieht kaputt aus. */
.page-laden .site-footer { padding-bottom: calc(var(--s-8) + env(safe-area-inset-bottom, 0px)); }

@media (max-width: 560px) {
    .ladenbar__weiter { padding: var(--s-2) var(--s-4); }
}


/* ============================================================
   RESERVIERUNGSSEITE — die Karte auf dem Bogen

   ⛔ WAS HIER VORHER STAND, WAR KEINE GESTALTUNG, SONDERN DAS FEHLEN EINER. Ein 640 Pixel
      hoher dunkler Bogen, darunter sieben gleich aussehende Kästen auf der nackten Seitenfläche.
      Die drei Felder, die die ENTSCHEIDUNG sind, hatten dasselbe Gewicht wie die Telefonnummer,
      neben dem Formular war die halbe Seite leer, und kein einziges Zeichen sagte, worum es in
      welcher Zeile geht.

   ⚠️ DIE KARTE LIEGT AUF DEM BOGEN AUF. Ein negativer Aussenabstand zieht sie über die Kante —
      das ist die Bewegung, die «hier ist die Sache, um die es geht» sagt, ohne ein Wort. Der
      Bogen wird dafür flacher: er war ein ganzer Bildschirm Vorspann vor einem Formular.

   ⚠️ ALLE FARBEN KOMMEN AUS DEN MARKEN DES HAUSES (`--accent`, `--surface`, `--line`). Diese
      Seite gehört zwanzig Restaurants gleichzeitig; eine hier erfundene Farbe wäre bei
      neunzehn davon falsch.
   ============================================================ */

.resv-hero {
    /* Flacher als der allgemeine Bogen: hier folgt sofort die Karte, und ein Vorspann, der
       einen ganzen Bildschirm braucht, schiebt das Formular unter die Kante. */
    min-height: clamp(320px, 46vh, 430px);
    padding-bottom: clamp(56px, 8vw, 96px);
}

.resv-hero__zusagen {
    list-style: none;
    margin: 6px 0 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 10px 22px;
}

.resv-hero__zusagen li {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: .92rem;

    /*
     * ⛔ `color: inherit` UND NICHT `--on-dark-soft`, und das war ein gemessener Fehler von mir.
     *    Diese Zeile stand fest auf «hell auf dunkel», weil ich sie gegen Bühnes dunklen Bogen
     *    gebaut und den zweiten Aufbau nie angesehen habe. Kontors Bogen ist PAPIER: dieselben
     *    drei Zusagen trugen dort 1,12:1 (Abend), 1,07:1 (Tafel) und 1,06:1 (Markt) — auf Bühne
     *    14,70:1. Sie waren nicht schwach, sie waren unsichtbar.
     *
     *    Der Bogen setzt seine Schriftfarbe selbst (`--on-dark` bzw. `--text` im Kontor-Block).
     *    Wer sie erbt, ist in beiden Aufbauten richtig, ohne dass eine Vorlage wissen muss,
     *    welcher gerade an ist — genau die Bauform, für die es die zwei Achsen gibt.
     */
    color: inherit;
}

.resv-hero__zusagen svg {
    width: 18px;
    height: 18px;
    flex: 0 0 auto;              /* ohne das fällt ein SVG ohne Eigenbreite im Flex auf null */
    color: var(--on-dark-accent);
}

/* ── Das Raster: Bogen links, Leiste rechts ─────────────────────────────────────────────── */

.resv { position: relative; }

/*
 * ⚠️ `min-width: 0` IST DER GANZE ZWECK DIESER SPALTE. Ein Rasterfeld hat als Mindestbreite
 *    seinen Inhalt; ein langes Wort in der Absage oder ein breites Auswahlfeld schöbe die
 *    Spalte damit über die Seite hinaus und die Leiste daneben aus dem Bild. Der Wrapper hält
 *    ausserdem Absagekasten und Formular zusammen, damit beide in derselben Spalte stehen.
 */
.resv__haupt { min-width: 0; }

.resv__grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 22px;
    /* Zieht die Karte über die Unterkante des Bogens. */
    margin-top: clamp(-84px, -7vw, -48px);
}

@media (min-width: 900px) {
    .resv__grid { grid-template-columns: minmax(0, 1fr) 300px; align-items: start; }
}
/* Ab 1100px darf die Leiste breiter sein: bei 300px brach «11:30–14:30, 17:00–22:00» hinter
   «Samstag» in zwei Zeilen, hinter «Sonntag» nicht — ein Unterschied von drei Pixeln, der wie
   ein Fehler aussah. Gemessen bei 1440. */
@media (min-width: 1100px) {
    .resv__grid { grid-template-columns: minmax(0, 1fr) 330px; }
}
.resv__karte {
    margin: 0;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-xl);
    padding: clamp(20px, 3vw, 34px);
    box-shadow: 0 18px 48px color-mix(in oklab, #000 14%, transparent);
}

/*
 * ⛔ `padding: 0` IST HIER KEINE KOSMETIK. Diese Blöcke sind `<section>` — jeder trägt eine
 *    eigene Überschrift, das ist die richtige Auszeichnung —, und `section` hat auf dieser
 *    Seite ein Bandpolster von rund 88 Pixeln oben UND unten. In einer Karte ergab das zweimal
 *    einen Handbreit Leere mitten im Formular, zwischen «Ihr Tisch» und «Ihre Angaben» und
 *    zwischen der Anmerkung und den Knöpfen. Gemessen im Browser, nicht im Quelltext gesucht:
 *    88.34px, aus einer Regel drei Bildschirme weiter oben.
 */
.resv__block { margin: 0 0 26px; padding: 0; }
.resv__block:last-of-type { margin-bottom: 6px; }

/*
 * ⚠️ DIE ENTSCHEIDUNG BEKOMMT EINEN EIGENEN GRUND. Ein zarter Ton hinter Tag, Uhrzeit und
 *    Personen trennt sie von den Angaben darunter, ohne eine zweite Karte aufzumachen — zwei
 *    Kästen übereinander sähen aus wie zwei Formulare.
 */
.resv__block--wahl {
    /* ⛔ KEIN AKZENTTON MEHR. Bei einem Haus mit Rot als Akzent war die Karte rosa — «pembe»,
       sagte der Betreiber, und wollte einen ruhigeren Pastellton. Messing, wie die Trennlinien
       und die Abschlussleiste des Bestellzettels: warm, leise, auf jedem Haus derselbe, und
       keine Signalfarbe, die «Fehler» oder «Rabatt» sagen könnte. Gemischt mit der Karte
       darunter, nicht mit transparent — damit die Fläche auch im dunklen Thema eine Fläche ist. */
    background: color-mix(in oklab, var(--gold) 9%, var(--surface));
    border: 1px solid color-mix(in oklab, var(--gold) 30%, var(--line-soft));
    border-radius: var(--r-md);
    padding: 18px 18px 4px;
}

.resv__h {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 0 14px;
    font-family: var(--serif);
    font-size: 1.18rem;
    font-weight: 600;
    color: var(--text);
}

/*
 * ⛔ `--accent-ink`, NICHT `--accent-strong` — für jedes Zeichen und jede Ziffer dieser Seite.
 *    Die «dunklere Variante» ist eine FLÄCHE, der gedrückte Knopf, und das Haus darf sie frei
 *    setzen; das Laborhaus hatte Weiss darin. Jedes Zeichen, das die Farbe las — Kalender, Uhr,
 *    Personen, Name, Telefon, Post, die Ziffern der Leiste, das Schild beim Datenschutz — stand
 *    weiss auf Creme: gemessen am 16.09.2026, rgb(255,255,255) auf #FBF9F5, unsichtbar. Was als
 *    Zeichen oder Wort auf Papier steht, liest die Tinte, die App\Site\Kontrast auf 4,5:1 rechnet.
 */

.resv__hicon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    flex: 0 0 auto;
    border-radius: 50%;
    background: color-mix(in oklab, var(--accent) 14%, transparent);
    color: var(--accent-ink);
}

.resv__hicon svg { width: 19px; height: 19px; }

/*
 * ⚠️ DAS ZEICHEN STEHT IN DER BESCHRIFTUNG UND NICHT IM FELD. Im Feld sässe es hinter dem
 *    Text, den der Gast tippt, und bei einem nativen Datumsfeld hinter dessen eigenem
 *    Kalendersymbol — zwei Zeichen in einem Kasten. In der Beschriftung sagt es dasselbe und
 *    steht niemandem im Weg.
 */
.resform__field label {
    display: inline-flex;
    align-items: center;
    gap: 7px;
}

.resform__field label svg {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    color: var(--accent-ink);
    opacity: .85;
}

/* ── Uhrzeit und Personenzahl: eine Auswahl, kein Aufklappmenü ──────────────────────────────
 *
 * ⛔ EIN AUFKLAPPMENÜ VERBIRGT DAS ANGEBOT, bis jemand es antippt. Auf der Seite, auf der ein
 *    Fremder entscheidet, ob er kommt, ist das die falsche Reihenfolge: er soll den Abend
 *    sehen und dann wählen, nicht wählen müssen, um ihn zu sehen.
 *
 * ⚠️ ES SIND GEWÖHNLICHE `input[type=radio]`, nur unsichtbar gemacht statt weggelassen.
 *    Tastatur, Screenreader, Pfeiltasten innerhalb der Gruppe und das Absenden ohne eine
 *    Zeile JavaScript bleiben genau das, was der Browser ohnehin kann. Der Knopf liegt
 *    deckungsgleich über seinem Feld — `opacity: 0` und nicht `display: none`, denn ein
 *    ausgeblendetes Feld ist für die Tastatur nicht mehr da.
 *
 * ⚠️ DER FOKUSRING LIEGT AUF DEM `span`, gesteuert von `:focus-visible` am unsichtbaren Knopf.
 *    Am Knopf selbst wäre er unsichtbar — und ein Formular, das man mit der Tastatur bedient,
 *    ohne zu sehen, wo man steht, ist keins.
 */
.resform__wahl { border: 0; margin: 0 0 20px; padding: 0; min-width: 0; }

.resform__wahlh {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: .92rem;
    color: var(--muted);
    margin: 0 0 9px;
    padding: 0;
}

.resform__wahlh svg {
    width: 16px;
    height: 16px;
    flex: 0 0 auto;
    color: var(--accent-ink);
    opacity: .85;
}

/* Der Tag steht für sich und braucht nicht die halbe Breite des Bogens. */
.resform__row--tag { max-width: 260px; }

/* 78 und nicht 84: gemessen auf einem 390er Telefon passen bei 84 nur ZWEI Zeiten nebeneinander
   und ein Abend wird fünf Reihen hoch. Bei 78 sind es drei, und der Knopf bleibt mit 46 Pixeln
   Höhe immer noch weit über dem, was ein Daumen braucht. */
.resform__zeiten { display: grid; grid-template-columns: repeat(auto-fill, minmax(78px, 1fr)); gap: 8px; }
.resform__zahlen { display: flex; flex-wrap: wrap; gap: 8px; }

.resform__zeit,
.resform__zahl { position: relative; display: block; cursor: pointer; }

/*
 * ⛔ NICHT `opacity: 0`, UND DAS IST KEINE STILFRAGE — `BewegungTest` hat es abgefangen.
 *
 *    Diese Regel verbietet jede Deckkraft-Null ausserhalb von `.js-motion`, weil eine
 *    Einblendung, die nie läuft, den Inhalt für immer verschluckt; genau das ist einmal auf der
 *    Startseite passiert. Der Knopf hier soll dauerhaft unsichtbar sein und hängt an keinem
 *    Skript — aber die Regel unterscheidet das nicht, und sie hat recht damit. Eine Ausnahme
 *    für «Formularfelder» wäre das Loch, durch das der Fehler zurückkommt, für den sie
 *    geschrieben wurde.
 *
 * ⚠️ DIE ÜBLICHE FASSUNG LEISTET DASSELBE. Der Auswahlknopf bleibt für Tastatur und Screenreader
 *    vorhanden, ist einen Pixel gross und weggeschnitten; angeklickt und angetippt wird er über
 *    das `<label>`, das ihn UND seine Beschriftung umschliesst — die ganze Fläche des Knopfes
 *    bleibt also die Trefferfläche. Der Fokusring liegt weiterhin auf dem `span`.
 */
.resform__zeit input,
.resform__zahl input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.resform__zeit span,
.resform__zahl span {
    display: grid;
    place-content: center;
    text-align: center;
    min-height: 46px;
    padding: 12px 8px;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    font-variant-numeric: tabular-nums;
    transition: border-color .12s ease, background .12s ease;
}

.resform__zahl span { min-width: 46px; padding: 12px 12px; }

/* Am `label`, nicht am Knopf: der Knopf ist jetzt einen Pixel gross und wird nie überfahren. */
.resform__zeit:hover span,
.resform__zahl:hover span { border-color: var(--accent); }

.resform__zeit input:focus-visible + span,
.resform__zahl input:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 1px; }

.resform__zeit input:checked + span,
.resform__zahl input:checked + span {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
}

@media (prefers-reduced-motion: reduce) {
    .resform__zeit span,
    .resform__zahl span { transition: none; }
}


/* ── Die Leiste daneben ─────────────────────────────────────────────────────────────────── */

.resv__rail { display: flex; flex-direction: column; gap: 14px; }

.resv__box {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    padding: 16px 18px;
}
/* Die Zeiten in der Leiste: eine Stufe kleiner als auf der Seite, und eine umbrechende zweite
   Zeile steht rechtsbündig unter der ersten statt links unter dem Tag. */
.resv__box .hours-list li { font-size: .92rem; gap: 12px; }
.resv__box .hours-list span { text-align: right; }

/* Auf dem Telefon nehmen beide Knöpfe die ganze Breite: zwei halbbreite Knöpfe untereinander
   sahen aus wie einer, der abgebrochen ist. */
@media (max-width: 560px) {
    .resv .resform__actions .btn { flex: 1 1 100%; }
}/* Der Kontosatz steht jetzt unter der Reihe (siehe reservierung.blade.php); als Absatz braucht
   er den Abstand, den das Feld vorher mitbrachte. */
.resv .resform__row + .resform__hint { margin: -4px 0 18px; }

.resv__boxh {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 12px;
    font-family: var(--serif);
    font-size: 1.02rem;
    font-weight: 600;
    color: var(--text);
}

.resv__boxh svg { width: 17px; height: 17px; flex: 0 0 auto; color: var(--accent-ink); }

.resv__schritte { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; }

.resv__schritte li {
    display: grid;
    grid-template-columns: 26px minmax(0, 1fr);
    gap: 11px;
    align-items: start;
    font-size: .9rem;
    line-height: 1.45;
    color: var(--muted);
}

.resv__schritte b { color: var(--text); font-weight: 600; }

/* Eine Ziffer und kein Haken: die drei Schritte sind eine REIHENFOLGE, kein Zustand. Ein Haken
   an Schritt zwei behauptete, er sei schon passiert. */
.resv__nr {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: color-mix(in oklab, var(--accent) 14%, transparent);
    color: var(--accent-ink);
    font-size: .82rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.resv__anruf {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px;
    border-radius: var(--r-md);
    background: color-mix(in oklab, var(--accent) 10%, var(--surface));
    border: 1px solid color-mix(in oklab, var(--accent) 30%, transparent);
    color: var(--text);
    text-decoration: none;
    font-size: .92rem;
    line-height: 1.4;
    transition: background var(--t-fast), border-color var(--t-fast);
}

.resv__anruf b { font-family: var(--serif); font-size: 1rem; }
.resv__anruf svg { width: 22px; height: 22px; flex: 0 0 auto; color: var(--accent-ink); }
.resv__anruf:hover { background: color-mix(in oklab, var(--accent) 18%, var(--surface)); }

/* ── Der Datenschutzsatz ────────────────────────────────────────────────────────────────── */

/* ⚠️ MIT SCHILD UND OHNE `.muted`. Der Satz ist die einzige Zusage auf dieser Seite, die ein
      Gast im Zweifel WIRKLICH liest — grau und klein gesetzt sah er aus wie Kleingedrucktes,
      also wie das Gegenteil dessen, was er sagt. */
.resform__datenschutz {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    margin: 14px 0 0;
    font-size: .88rem;
    line-height: 1.5;
    color: var(--muted);
}

.resform__datenschutz svg {
    width: 18px;
    height: 18px;
    flex: 0 0 auto;
    margin-top: 2px;
    color: var(--accent-ink);
}


/* ============================================================
   BEWEGUNG — was ankommt, was antwortet, und was ausdrücklich still bleibt

   ⛔ DER RUHEZUSTAND IST SICHTBAR. Versteckt wird ausschliesslich unter `html.js-motion`, und
      diese Klasse setzt `site.js` erst, nachdem es einen IntersectionObserver gefunden UND
      festgestellt hat, dass der Gast keine reduzierte Bewegung wünscht. Ohne Skript, ohne
      Beobachter, mit reduzierter Bewegung oder bei angehaltener Animationsuhr steht die Seite
      vollständig da.

      Diese Datei trägt die Narbe dafür schon: `.wa-fab__note` bekam bewusst KEINE Einblendung,
      weil eine Animation ab `opacity: 0` den Text für immer unsichtbar lässt, sobald sie nicht
      läuft — «Both fail silently, and the invitation is the entire point of this component.»
      Derselbe Fehler, eine Seite weiter, wäre eine Speisekarte ohne Gerichte.

   ⛔ NUR `transform` UND `opacity`. Beide laufen auf der Grafikkarte; `top`, `height` oder
      `margin` zu animieren heisst, den Browser bei jedem Bild neu rechnen zu lassen — auf dem
      Telefon, auf dem diese Seite meistens geöffnet wird, ist das der Unterschied zwischen
      sechzig Bildern und Ruckeln.

   ⚠️ ZWEI AUFBAUTEN, ZWEI BEWEGUNGSSPRACHEN. Was hier steht, ist Bühnes Sprache: das Bild
      führt, also hebt sich eine Karte an und das Foto darin zieht leicht näher. Kontor sagt
      über sich selbst «square, and they do not lift» — dort wäre dieselbe Geste ein Widerspruch
      zum eigenen Charakter. Seine Fassung steht im Kontor-Block und arbeitet mit der Linie und
      der Farbe statt mit der Höhe.

   ⚠️ JEDE BEWEGUNG HIER HAT EINEN GRUND, und wo keiner steht, steht auch keine Bewegung.
      Ankommen führt das Auge eine lange Seite hinunter; Hover sagt «das hier antwortet»;
      der Druckpunkt bestätigt den Finger. Alles andere ist Zierat, der Aufmerksamkeit
      verlangt, statt sie zu lenken.
   ============================================================ */

/* ── Ankommen ──────────────────────────────────────────────────────────────────────────── */

.js-motion .section-head,
.js-motion .dishcard,
.js-motion .band-photo,
.js-motion .facts__item,
.js-motion .rg-card,
.js-motion .split__media,
.js-motion .split__text,
.js-motion .section-head.is-in,
.js-motion .dishcard.is-in,
.js-motion .band-photo.is-in,
.js-motion .facts__item.is-in,
.js-motion .rg-card.is-in,
.js-motion .split__media.is-in,
.js-motion .split__text.is-in {
    opacity: 1;
    transform: translate3d(0, 0, 0);
}

/*
 * ⚠️ DIE STAFFELUNG STEHT IM STYLESHEET UND NICHT IM SKRIPT. Der naheliegende Weg wäre, dem
 *    Beobachter je Element einen Verzögerungswert in `element.style` zu schreiben — und die
 *    Auslieferung dieser Seite verbietet Inline-Stile über die Content-Security-Policy, die am
 *    Webserver gesetzt wird und die der Anwendung übergeordnet ist. `:nth-child()` kann
 *    dasselbe, ohne dass irgendwo ein Stil aus JavaScript entsteht.
 *
 * ⚠️ VIER STUFEN UND DANN SCHLUSS. Bei zwölf Gerichten wäre die zwölfte Karte eine dreiviertel
 *    Sekunde nach der ersten dran, und der Gast wartet auf sein Essen statt es anzusehen.
 */
.js-motion .dish-grid > .dishcard:nth-child(2)  { transition-delay: 70ms; }
.js-motion .dish-grid > .dishcard:nth-child(3)  { transition-delay: 140ms; }
.js-motion .dish-grid > .dishcard:nth-child(n+4) { transition-delay: 210ms; }

.js-motion .facts__list > .facts__item:nth-child(2)   { transition-delay: 60ms; }
.js-motion .facts__list > .facts__item:nth-child(3)   { transition-delay: 120ms; }
.js-motion .facts__list > .facts__item:nth-child(n+4) { transition-delay: 180ms; }

/*
 * ── Der erste Bildschirm kommt an, statt dazustehen ──────────────────────────────────────
 *
 * ⚠️ EINE ANIMATION UND KEIN ÜBERGANG, weil hier kein Zustand wechselt: die Seite lädt, und
 *    der Aufmacher setzt sich. `backwards` hält den ersten Bildrahmen während der Verzögerung —
 *    ohne das blitzt die Zeile kurz an ihrer Endstelle auf, bevor sie zurückspringt.
 */
@keyframes bm-anheben {
    from { opacity: 0; transform: translate3d(0, 18px, 0); }
    to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

.js-motion .hero__inner > .eyebrow  { animation: bm-anheben .62s cubic-bezier(.16, .84, .44, 1) both; }
.js-motion .hero__inner > .hero__title { animation: bm-anheben .62s cubic-bezier(.16, .84, .44, 1) 80ms both; }
.js-motion .hero__inner > .hero__intro { animation: bm-anheben .62s cubic-bezier(.16, .84, .44, 1) 160ms both; }
.js-motion .hero__inner > .hero__status,
.js-motion .hero__inner > .hero__actions,
.js-motion .hero__inner > .resv-hero__zusagen { animation: bm-anheben .62s cubic-bezier(.16, .84, .44, 1) 240ms both; }

/*
 * ── DER AUFMACHER ATMET ────────────────────────────────────────────────────────────────────
 *
 * ⛔ EIN STANDBILD IST DAS EINZIGE, WAS EIN RESTAURANT NICHT IST. Die langsame Zufahrt auf das
 *    Foto — sechsundzwanzig Sekunden von 1,00 auf 1,085 — ist die Geste, mit der jede
 *    Restaurantseite arbeitet, die man sich merkt. Sie ist so langsam, dass man sie nicht als
 *    Bewegung sieht, sondern nur merkt, dass der Raum lebt.
 *
 * ⚠️ SIE IST SICHER, UND ZWAR ANDERS ALS EINE EINBLENDUNG. Hält die Animationsuhr an, bleibt
 *    der ERSTE Rahmen stehen — `scale(1)`, also genau das Bild, das ohne jede Animation zu
 *    sehen wäre. Ein `opacity: 0` am Anfang wäre dagegen ein leerer Aufmacher; deshalb bewegt
 *    sich hier nur die Grösse und nie die Deckkraft.
 *
 * ⚠️ DAS FOTO WANDERT AUF EIN EIGENES BLATT, weil man einen Hintergrund nicht transformieren
 *    kann. `::before` trägt das Bild, `::after` den Schleier darüber, der Inhalt liegt auf
 *    z-index 1 und bleibt oben. Ohne `.js-motion` steht die ursprüngliche Regel unverändert da:
 *    ein Haus ohne JavaScript sieht exakt das, was es heute sieht.
 */
.hero--foto { position: relative; overflow: hidden; }

@keyframes bm-schwenk {
    from { transform: scale(1); }
    to   { transform: scale(1.085); }
}

.js-motion .hero--foto.is-photo { background-image: none; }

.js-motion .hero--foto.is-photo::before {
    content: '';
    position: absolute;
    inset: 0;
    background-image: var(--hero-photo);
    background-size: cover;
    background-position: center;
    transform-origin: 50% 58%;
    animation: bm-schwenk 26s cubic-bezier(.25, .5, .35, 1) both;
}

.js-motion .hero--foto.is-photo::after {
    content: '';
    position: absolute;
    inset: 0;
    /* Derselbe Schleier wie ohne Skript — siehe `--hero-schleier`. */
    background-image: var(--hero-schleier);
}

/*
 * ⚠️ UND DAS BAND SETZT SICH. Dieselbe Bewegung, kürzer und an das Ankommen gekoppelt: das
 *    Foto steht eine Spur zu gross da und findet seine Grösse, während das Band hereinkommt.
 */
.js-motion .band-photo__img {
    transform: scale(1.07);
    transition: transform 1.15s cubic-bezier(.16, .84, .44, 1);
}

.js-motion .band-photo.is-in .band-photo__img { transform: scale(1); }

/*
 * ── DER FORTSCHRITTSFADEN ──────────────────────────────────────────────────────────────────
 *
 * ⚠️ NUR WO ER OHNE SKRIPT GEHT. `animation-timeline: scroll()` bindet die Animation an den
 *    Rollbalken statt an die Uhr — kein Ereignis, kein `requestAnimationFrame`, keine Zeile
 *    JavaScript. `@supports` ist hier tragend: ohne die Abfrage liefe die Animation in älteren
 *    Browsern auf der Zeituhr mit Dauer null und stünde sofort auf voller Breite — ein Faden,
 *    der immer «fertig» sagt.
 */
@supports (animation-timeline: scroll()) {
    @keyframes bm-fortschritt {
        from { transform: scaleX(0); }
        to   { transform: scaleX(1); }
    }

    .js-motion body::before {
        content: '';
        position: fixed;
        inset: 0 0 auto 0;
        height: 3px;
        z-index: 60;
        pointer-events: none;
        transform-origin: 0 50%;
        background: linear-gradient(90deg, var(--accent), var(--gold));
        animation: bm-fortschritt linear both;
        animation-timeline: scroll(root block);
    }
}

/* ── Antworten: Hover und Druckpunkt ───────────────────────────────────────────────────── */

/*
 * ⚠️ DAS FOTO ZIEHT NÄHER, DIE KARTE HEBT SICH. Zusammen ergibt das die Bewegung, die ein
 *    Gedeck macht, wenn man sich darüber beugt — und sie gehört zum fotografischen Aufbau,
 *    dessen ganze These das Bild ist. `overflow: hidden` ist dafür tragend: ohne es wächst das
 *    Foto über die Kante der Karte hinaus.
 */
.dishcard { overflow: hidden; }

.dishcard__photo {
    transition: transform var(--t);
    will-change: auto;
}

@media (hover: hover) {
    .dishcard:hover { transform: translate3d(0, -6px, 0); }
    .dishcard:hover .dishcard__photo { transform: scale(1.07); }
}

/*
 * ⚠️ EIN GLANZ ÜBER DEN HAUPTKNOPF, und nur über diesen einen. «Zur Speisekarte» und «Tisch
 *    anfragen» sind die zwei Handlungen, für die es diese Seite gibt; ein Lichtstreifen, der
 *    beim Zeigen darüberläuft, sagt «hier lang», ohne ein Wort hinzuzufügen. Auf jedem Knopf
 *    wäre dieselbe Geste Kirmes.
 */
.btn--primary { position: relative; overflow: hidden; isolation: isolate; }

.btn--primary::after {
    content: '';
    position: absolute;
    top: 0;
    left: -140%;
    width: 55%;
    height: 100%;
    z-index: -1;
    background: linear-gradient(100deg, transparent, rgb(255 255 255 / .26), transparent);
    transform: skewX(-18deg);
    transition: left .6s cubic-bezier(.22, .61, .36, 1);
}

@media (hover: hover) {
    .btn--primary:hover::after { left: 150%; }
}

/*
 * ⚠️ DER STRICH UNTER DEM MENÜPUNKT ZIEHT SICH, statt zu erscheinen. `::before`, weil `::after`
 *    an dieser Stelle schon den Punkt der aktuellen Seite trägt (eine Regel weiter oben, im
 *    Breitbild-Block) — zwei Gesten auf einem Pseudoelement wären eine, die die andere
 *    überschreibt.
 */
.nav > a { position: relative; }

.nav > a::before {
    content: '';
    position: absolute;
    left: 12px;
    right: 12px;
    bottom: 3px;
    height: 1.5px;
    background: var(--accent);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--t);
}

@media (hover: hover) {
    .nav > a:hover::before { transform: scaleX(1); }
}

/* Der Druckpunkt. Ein Knopf, der auf den Finger nicht reagiert, fühlt sich an, als hätte er
   nicht ausgelöst — und dann drückt man ihn ein zweites Mal. */
.btn:active { transform: translate3d(0, 1px, 0) scale(.988); }
.btn { transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast),
                   box-shadow var(--t-fast), transform var(--t-fast); }

/*
 * ⛔ UND HIER HÖRT ALLES AUF. Der Block gilt für jede Regel dieses Abschnitts, auch für die
 *    Kontor-Fassung weiter unten: wer reduzierte Bewegung eingestellt hat, hat das gesagt, weil
 *    Bewegung ihm körperlich unangenehm ist. `.js-motion` wird zwar ohnehin nicht gesetzt, aber
 *    diese Regel deckt auch den Fall ab, dass jemand die Einstellung ändert, während die Seite
 *    offen ist — dann fällt die Bewegung sofort weg, ohne Neuladen.
 */
@media (prefers-reduced-motion: reduce) {
    .js-motion .section-head,
    .js-motion .dishcard,
    .js-motion .band-photo,
    .js-motion .facts__item,
    .js-motion .rg-card,
    .js-motion .split__media,
    .js-motion .split__text {
        opacity: 1;
        transform: none;
        transition: none;
    }

    .js-motion .hero__inner > * { animation: none; }

    .dishcard,
    .dishcard__photo,
    .btn { transition: none; }

    .dishcard:hover,
    .dishcard:hover .dishcard__photo,
    .btn:active { transform: none; }

    .js-motion .hero--foto.is-photo::before { animation: none; }
    .js-motion .band-photo__img,
    .js-motion .band-photo.is-in .band-photo__img { transform: none; transition: none; }
    .js-motion body::before { display: none; }
    .btn--primary::after,
    .nav > a::before { transition: none; }
    .btn--primary:hover::after { left: -140%; }
    .nav > a:hover::before { transform: scaleX(0); }
}


/* ── Die Karte ────────────────────────────────────────────────────────────────────
 *
 * ⚠️ DER PLATZHALTER IST KEIN GRAUER KASTEN. Er trägt dieselbe Kante, denselben Radius und
 *    denselben Schatten wie die Kartenkarte darunter, dazu die Anschrift und eine Nadel — er
 *    sieht aus wie ein Teil der Seite und nicht wie etwas, das noch lädt. Ein Platzhalter, der
 *    nach Fehler aussieht, wird nicht angeklickt.
 *
 * ⚠️ UND ER HÄLT DIE HÖHE, DIE DIE KARTE GLEICH BRAUCHT. Ohne `aspect-ratio` springt die
 *    halbe Seite nach unten, sobald jemand den Knopf drückt — genau die Bewegung, die einen
 *    Klick auf etwas anderes auslöst, das gerade weggerutscht ist.
 */
.karte {
    margin: clamp(24px, 4vw, 40px) 0 0;
    border: 1px solid var(--line-soft);
    border-radius: var(--r);
    overflow: hidden;
    box-shadow: var(--shadow);
    background: var(--plate);
}

.karte__platzhalter {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    aspect-ratio: 2 / 1;
    min-height: 220px;
    padding: clamp(18px, 4vw, 32px);
    text-align: center;
}

/* ⛔ DAS RASTER IST GEZEICHNET, NICHT GELADEN — UND DAS IST DER GANZE WITZ DARAN.
 *
 *    Der Platzhalter soll nach Karte aussehen, sonst liest ihn niemand als eine und der Knopf
 *    wirkt wie eine Fehlermeldung. Ein Kartenausschnitt als BILD wäre die naheliegende
 *    Lösung und wäre genau das, was hier nicht passieren darf: entweder eine Fremdanfrage
 *    (dann ist die Zwei-Klick-Fassung sinnlos) oder eine Datei, die jemand je Haus hochladen
 *    müsste (dann gibt es sie nie — siehe das Kartenbild davor).
 *
 *    Zwei gekreuzte Verläufe kosten null Anfragen, null Bytes und null Pflege.
 *
 * ⚠️ AUF EINEM EIGENEN `::before`, NICHT AUF DEM KASTEN SELBST: so lässt sich das Raster
 *    zurücknehmen, ohne die Schrift darüber mit blass zu machen. Anschrift und Hinweis
 *    behalten ihren vollen Kontrast, und der bleibt lesbar — auch das ist eine Zusage, die
 *    dieses Haus an anderer Stelle schon einmal teuer gelernt hat.
 */
.karte__platzhalter::before {
    content: '';
    position: absolute;
    inset: 0;
    opacity: .5;
    background-image:
        repeating-linear-gradient(0deg, var(--c-line) 0 1px, transparent 1px 54px),
        repeating-linear-gradient(90deg, var(--c-line) 0 1px, transparent 1px 54px);
    background-position: center;
    /* Zu den Rändern hin ausblenden, damit das Raster nicht wie ein Gitter endet. */
    -webkit-mask-image: radial-gradient(ellipse at center, #000 40%, transparent 78%);
    mask-image: radial-gradient(ellipse at center, #000 40%, transparent 78%);
    pointer-events: none;
}

/* Alles Inhaltliche liegt über dem Raster. */
.karte__nadel,
.karte__adresse,
.karte__knopf,
.karte__hinweis { position: relative; }

/*
 * ⚠️ DIE NADEL TRÄGT DIE AKZENTFARBE, und zwar als einziges Element hier. Auf einer Karte
 *    ist der Punkt das, was man sucht; in Grau wäre er ein Symbol neben einer Anschrift.
 */
.karte__nadel {
    width: 38px;
    height: 38px;
    color: var(--accent, var(--c-ink));
    filter: drop-shadow(0 2px 6px rgba(0, 0, 0, .12));
}

.karte__adresse {
    margin: 0;
    font-family: var(--serif);
    font-size: 1.05rem;
    line-height: 1.45;
}

.karte__knopf { margin-top: 2px; }

.karte__hinweis {
    margin: 0;
    max-width: 42ch;
    font-size: .82rem;
    line-height: 1.5;
    color: var(--muted);
}

/* Das eingesetzte `iframe` erbt die Form des Platzhalters, den es ersetzt. */
.karte__rahmen {
    display: block;
    width: 100%;
    aspect-ratio: 2 / 1;
    min-height: 220px;
    border: 0;
}

.karte__quelle {
    padding: 8px 14px;
    font-size: .78rem;
    color: var(--muted);
    border-top: 1px solid var(--line-soft);
}

.karte__quelle a { color: inherit; }

.karte__nurlink { margin: clamp(20px, 3vw, 32px) 0 0; }

/* Im Laden-Schubfach ist weniger Platz: dort steht die Karte quadratischer und ohne Schatten. */
.storedetails .karte { margin-top: 12px; box-shadow: none; }
.storedetails .karte__platzhalter,
.storedetails .karte__rahmen { aspect-ratio: 4 / 3; min-height: 180px; }

/* ⚠️ UND DIESE DREI EBENFALLS — aus demselben Grund, eine Warnung weiter unten
      ausgeschrieben. Ans Dateiende angehängt lagen sie hinter der ersten
      `.layout-kontor`-Regel, waren dort ungebunden und trugen `[hidden]` mit sich:
      zwei rote Tests aus einer Zeilennummer, genau wie beim Urlaubsband vor ihnen.
      Die Falle erwischt jeden, der «unten anfügen» für neutral hält. */

/* ── Das Gästekonto ────────────────────────────────────────────────────────────
 *
 * ⚠️ EINE LISTE, KEINE TABELLE. Auf einem Telefon — und dort wird diese Seite geöffnet, weil
 *    der Link in der Fusszeile steht — zerfällt eine vierspaltige Tabelle in etwas, das man
 *    seitwärts schieben muss. Die Zeilen stapeln sich hier stattdessen.
 *
 * ⛔ KEINE EIGENE FARBWELT. Die Kontoseite ist Teil der Restaurantseite und erbt deren
 *    Schriften und Töne; ein Konto, das aussieht wie ein fremdes Portal, sieht aus wie eine
 *    Anmeldeseite, die jemand untergeschoben hat.
 */
.konto-liste { list-style: none; margin: 0 0 clamp(20px, 3vw, 32px); padding: 0; }

.konto-liste__eintrag {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 14px;
    padding: 12px 0;
    border-bottom: 1px solid var(--c-line);
}

.konto-liste__wann { font-weight: 600; }
.konto-liste__was { opacity: .85; }

.konto-liste__status {
    margin-left: auto;
    font-size: .9em;
    opacity: .7;
}

/* Die Zeilen einer Bestellung stehen unter der Kopfzeile, nicht daneben. */
.konto-liste__zeilen { flex-basis: 100%; font-size: .9em; opacity: .7; }

.konto-aktionen { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.konto-aktionen__loeschen { font-size: .9em; opacity: .7; }



/* ⚠️ DAS «ODER» ZWISCHEN DEN BEIDEN ANMELDEWEGEN. Eine Linie mit einem Wort darin: ohne sie
 *    stehen zwei Formulare untereinander und sehen aus wie ein einziges, dessen zweite Hälfte
 *    man auch noch ausfüllen soll. */
.konto-oder {
    display: flex;
    align-items: center;
    gap: 14px;
    margin: clamp(22px, 4vw, 34px) 0;
    color: var(--muted);
    font-size: .9rem;
}

/* ⚠️ RECHTSBÜNDIG UND KLEIN: «Passwort vergessen?» gehört neben das Passwortfeld, aber es ist
 *    kein zweites Angebot — es ist der Ausweg für den, bei dem das erste nicht klappt. Grösser
 *    gesetzt sähe es aus, als sei das Vergessen der übliche Fall. */
.konto-vergessen {
    margin-top: -6px;
    text-align: right;
    font-size: .88rem;
}

.konto-oder::before,
.konto-oder::after {
    content: '';
    flex: 1 1 auto;
    /* ⚠️ EIN RAND, KEINE FLÄCHE. Als `height: 1px; background: var(--c-line)` gezeichnet, meldete
     *    `ThemeMarktTest` die Regel zu Recht als Fläche ohne eigene Schriftfarbe — die Wache kann
     *    nicht wissen, dass hier wegen `content: ''` nie ein Zeichen steht. Ein Strich ist ohnehin
     *    ein Rand und keine bemalte Fläche; so gesagt, stimmt die Form mit der Absicht überein. */
    border-top: 1px solid var(--c-line);
}


/* ══ Gerade bei uns — was gerade läuft, im Konto ganz oben ═══════════════════════
 *
 * ⛔ DIE FARBEN SIND DIE DES HAUSES, keine neuen. `--accent` heißt «das Haus arbeitet daran»,
 *    `--enamel` heißt «fertig, komm», `--muted` heißt «noch nichts geschehen». Beide Paletten
 *    definieren alle drei — ein Token, das nur im Tagblock steht, ist genau der Fehler, den
 *    `ThemeMarktTest` seit dem Keksband jagt.
 *
 * ⚠️ SEMANTISCHE FARBE IST NICHT DER AKZENT DER SEITE. Dass «in der Küche» den Hausakzent
 *    trägt, ist kein Zufall, sondern die Aussage: an dieser Bestellung wird gerade gearbeitet.
 *    «Abholbereit» bekommt die zweite Farbe, weil es der einzige Zustand ist, bei dem der Gast
 *    aufstehen muss.
 */
.jetzt { margin: clamp(24px, 5vw, 40px) 0; display: flex; flex-direction: column; gap: 16px; }
.jetzt__titel { margin: 0; }

.jetzt-karte {
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: clamp(16px, 3.5vw, 24px);
    border: 1px solid var(--line);
    border-radius: var(--r-sm, 10px);
    background: var(--surface);
    color: var(--text);
}

/* ⚠️ DER STREIFEN SAGT DEN ZUSTAND, BEVOR EIN WORT GELESEN IST — und er sitzt links, weil
 *    dort das Auge auf einer Karte zuerst ankommt. `border-left` statt eines Pseudo-Elements:
 *    ein Rand verschiebt nichts und kann nicht über den abgerundeten Ecken hängen. */
.jetzt-karte--angenommen { border-left: 3px solid var(--accent); }
.jetzt-karte--fertig { border-left: 3px solid var(--enamel); }
.jetzt-karte--confirmed { border-left: 3px solid var(--enamel); }
.jetzt-karte--wartet,
.jetzt-karte--pending,
.jetzt-karte--waitlisted { border-left: 3px solid var(--line); }

.jetzt-karte__kopf {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px 12px;
    margin: 0;
}

.jetzt-karte__art {
    font-size: .74rem;
    letter-spacing: .09em;
    text-transform: uppercase;
    color: var(--muted);
}

/* `tabular-nums`: Nummern, Uhrzeiten und Beträge stehen hier untereinander, und
 * proportionale Ziffern lassen die Spalte zappeln. */
.jetzt-karte__nr { font-variant-numeric: tabular-nums; }
.jetzt-karte__summe { margin-left: auto; font-variant-numeric: tabular-nums; color: var(--muted); }
.jetzt-karte__satz { margin: 0; }

/* ⚠️ AUF DEM TELEFON WIRD AUS «nach rechts» EIN GESTRANDETES WORT. `margin-left: auto`
 *    schiebt den Betrag an den rechten Rand — sobald die Zeile umbricht, steht er allein auf
 *    einer eigenen Zeile ganz rechts, weit weg von allem, wozu er gehört. Gemessen an
 *    «30.08.2026 um 12:30 Uhr» + «4 Personen» auf 390px: passt nicht nebeneinander, also
 *    bricht es, also stand «4 Personen» verloren rechts. Untereinander links gelesen ergibt
 *    dieselbe Angabe einen Satz. */
@media (max-width: 460px) {
    .jetzt-karte__summe { margin-left: 0; }
}

/* Das Zeichen für den Zustand eines Tisches — dort, wo bei der Bestellung die Stufen stehen. */
.jetzt-karte__marke {
    align-self: flex-start;
    margin: 0;
    padding: 4px 12px;
    border-radius: 999px;
    border: 1px solid var(--line);
    font-size: .82rem;
    color: var(--muted);
}

.jetzt-karte--confirmed .jetzt-karte__marke { border-color: var(--enamel); color: var(--enamel); }

/* ⚠️ `align-self`, SONST WIRD DER ABSAGEKNOPF SO BREIT WIE DIE KARTE. Die Karte ist ein
 *    Flex-Container in Spaltenrichtung; ein Formular darin streckt sich über die volle Breite
 *    und sieht damit aus wie der Hauptweg dieser Seite. Absagen ist der seltene Fall — er
 *    gehört sichtbar, aber nicht gross. `margin: 0`, weil das Formular sonst den Abstand der
 *    Karte (`gap: 14px`) verdoppelt. */
.jetzt-karte__tun { align-self: flex-start; margin: 0; }

/* ── Die Stufen einer Bestellung ───────────────────────────────────────
 *
 * ⚠️ DIE LINIE WIRD AUS DEM SCHRITT SELBST GEZOGEN, nicht aus einem Balken darunter: so
 *    bleibt sie bei jeder Anzahl von Stufen und in jeder Breite an den Punkten hängen. Der
 *    erste Schritt bekommt keine — sonst liefe sie ins Nichts links.
 */
.stufen {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 0;
    margin: 0;
    padding: 0;
    list-style: none;
}

.stufen__schritt {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    text-align: center;
    color: var(--muted);
    font-size: .78rem;
    line-height: 1.25;
}

.stufen__schritt::before {
    content: '';
    position: absolute;
    top: 6px;
    right: 50%;
    left: -50%;
    height: 2px;
    background: var(--line);
}

.stufen__schritt:first-child::before { content: none; }

.stufen__punkt {
    position: relative;
    z-index: 1;
    width: 14px;
    height: 14px;
    border-radius: 999px;
    border: 2px solid var(--line);
    background: var(--surface);
}

.stufen__wort { max-width: 10ch; }

/* Erledigt: die Linie davor und der Punkt tragen die Hausfarbe. */
.stufen__schritt.ist-vorbei { color: var(--text); }
.stufen__schritt.ist-vorbei::before { background: var(--accent); }
.stufen__schritt.ist-vorbei .stufen__punkt { border-color: var(--accent); background: var(--accent); }

/* ⚠️ HIER IST DAS ESSEN GERADE. Der Ring ist grösser als der Punkt und nicht nur farbig:
 *    wer Farben schlecht unterscheidet, sieht trotzdem, welche Stufe die aktuelle ist. */
.stufen__schritt.ist-jetzt { color: var(--text); font-weight: 600; }
.stufen__schritt.ist-jetzt::before { background: var(--accent); }
.stufen__schritt.ist-jetzt .stufen__punkt {
    border-color: var(--accent);
    background: var(--accent);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 24%, transparent);
}

/* «Abholbereit» ist der eine Zustand, bei dem der Gast aufstehen muss — er bekommt die
 * zweite semantische Farbe, nicht den Arbeitsakzent. */
.jetzt-karte--fertig .stufen__schritt.ist-jetzt .stufen__punkt {
    border-color: var(--enamel);
    background: var(--enamel);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--enamel) 24%, transparent);
}

.jetzt-karte--fertig .stufen__schritt.ist-vorbei::before,
.jetzt-karte--fertig .stufen__schritt.ist-jetzt::before { background: var(--enamel); }
.jetzt-karte--fertig .stufen__schritt.ist-vorbei .stufen__punkt { border-color: var(--enamel); background: var(--enamel); }

/* ⚠️ AUF SCHMALEN TELEFONEN wird aus vier Spalten à ~80px ein Silbenbrei. Unter 380px
 *    trägt nur noch die aktuelle Stufe ihr Wort; die Punkte bleiben alle stehen, damit der Weg
 *    als Ganzes sichtbar ist. */
@media (max-width: 380px) {
    .stufen__schritt:not(.ist-jetzt) .stufen__wort { display: none; }
    .stufen__schritt { gap: 0; }
}

/* ── Der Cookie-Hinweis ─────────────────────────────────────────────────────────────
 *
 * ⚠️ UNTEN, SCHMAL, UND NICHT MODAL. Es verdeckt nichts, es sperrt nichts, es verlangt
 *    nichts — weil es nichts zu verlangen hat. Ein Dialog, der die Seite abdunkelt, gehört zu
 *    einer Einwilligung; hier wäre er eine Geste ohne Inhalt.
 *
 * ⚠️ UND ER STEHT NICHT AUF DER MOBILEN LEISTE. Speisekarte und Warenkorb haben unten ihre
 *    eigene feste Leiste; zwei übereinander sind auf einem Telefon der halbe Bildschirm.
 */
.keksband {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 60;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 10px 18px;
    padding: 12px clamp(14px, 4vw, 28px);
    /* ⛔ `--c-paper` DEĞİL, VE BU BİR KONTRAST KIRIĞIYDI. Ham palet jetonu yalnız `:root`'ta
       tanımlı; gece bloğunda karşılığı YOK. Yazı ise `body`'den `--text` miras alıyor ve o
       gece bloğunda açık renge dönüyor — sonuç açık kağıt üzerine açık yazı, ölçülen 1,07:1.
       Speisekarte varsayılan olarak karanlık açıldığı için bu istisna değil, masadaki QR'ı
       okutan misafirin gördüğü İLK şeydi. `--surface` iki palette de tanımlı. */
    background: var(--surface);
    border-top: 1px solid var(--line);
    box-shadow: 0 -6px 24px rgba(0, 0, 0, .07);
}

.keksband[hidden] { display: none; }

.keksband__text {
    margin: 0;
    max-width: 68ch;
    font-size: .92rem;
    line-height: 1.5;
}

.keksband__weg { flex: none; }

/*
 * ⚠️ BEIDE KNÖPFE GLEICH GROSS. Ein «Nur notwendige», das kleiner oder blasser ist als das
 *    «Alles erlauben», ist genau die Bauform, die Aufsichtsbehörden als Dark Pattern
 *    beanstanden — und die eine Einwilligung angreifbar macht, statt sie zu belegen. Der
 *    Unterschied liegt allein in der Füllung, nicht in Grösse, Höhe oder Abstand.
 */
.keksband__knoepfe { display: flex; gap: 10px; flex: none; }
.keksband__knoepfe .keksband__weg { min-width: 10.5em; justify-content: center; }

/* Der Widerruf auf der Datenschutzseite — eine Einwilligung ohne Rückweg ist keine. */
.einwilligung-widerruf { margin-top: 10px; }

/* ⚠️ WO UNTEN SCHON EINE LEISTE SITZT, STEHT DAS BAND NICHT MEHR FEST.
 *
 *    Hier stand `bottom: var(--bottombar-h, 64px)` — ein Token, das es nirgends gibt, also
 *    fiel die Eigenschaft still auf 64px zurück: eine geratene Zahl, die bei jeder Änderung
 *    an der Leiste falsch wird. `DesignSystemTest` hat das gefunden, und zwar genau dafür
 *    ist er da.
 *
 *    Statt einer zweiten Zahl neben der ersten läuft das Band dort einfach im Textfluss mit:
 *    es steht dann über dem Fussbereich, die Leiste bleibt unten, und nichts überdeckt
 *    einander. Ein Hinweis, der einmal gelesen und weggeklickt wird, muss nicht kleben. */
/* ⛔ DIE RICHTUNG WAR FALSCH, ALSO TRAF DIE REGEL NIE. `~` wählt NACHFOLGENDE Geschwister;
   `.keksband` steht aber VOR beiden Leisten im Dokument (layout.blade.php: Band 620,
   `.cartbar` 654, `.mobilebar` 730). Die Regel stand da, las sich richtig und hat nie
   gegriffen — das Band blieb fixiert auf z-60 und deckte die Leisten zu.
   `:has()` fragt in die andere Richtung und wird hier schon anderswo benutzt
   (`.ex-chip:has(.m-ex:checked)`). */
/* ⚠️ DIE WARENKORBLEISTE GILT AUF JEDER BREITE. `.cartbar` ist nicht telefonspezifisch: sie
   steht auf dem Breitbild fest am unteren Rand, sobald etwas im Korb liegt, und `/bestellen`
   trägt sie als zweite Klasse an `.ladenbar`. Diese Hälfte der Regel bleibt also unbedingt. */
.keksband:has(~ .cartbar:not([hidden])) {
    position: static;
    box-shadow: none;
}

/* ⛔ `:has()` LIEST DAS DOKUMENT, NICHT DIE DARSTELLUNG — UND DARAN HING DIE GANZE REGEL.
 *
 *    `.mobilebar` wird BEDINGUNGSLOS ausgeliefert (layout.blade.php); erst die Medienabfrage
 *    weiter oben schaltet sie über 860 px mit `display: none` ab. Ein `:has(~ .mobilebar)`
 *    fragt aber nach dem Element, nicht nach seiner Sichtbarkeit — der Wähler traf also AUCH
 *    auf dem Schreibtisch, wo unten gar keine Leiste steht.
 *
 *    Die Folge war nicht Optik, sondern ein Hinweis, den niemand las: das Einwilligungsband
 *    lief auf jedem Breitbild im Textfluss mit, also irgendwo über dem Fussbereich. Wer nicht
 *    bis ans Seitenende scrollte, hat die Wahl zwischen «Nur notwendige» und «Alles erlauben»
 *    nie zu Gesicht bekommen — und ein Band, das man nicht sieht, belegt keine Einwilligung.
 *
 * ⚠️ DIE ABFRAGE MUSS DIESELBE GRENZE TRAGEN wie die, die `.mobilebar` einschaltet (860 px).
 *    Zwei Zahlen für dieselbe Leiste wären zwei Orte, an denen sie auseinanderlaufen. */
@media (max-width: 860px) {
    .keksband:has(~ .mobilebar) {
        position: static;
        box-shadow: none;
    }
}


/* Der Hinweis auf das Konto, auf dem Beleg. Ein Kasten, kein Knopf: er ist ein Angebot und
   kein Schritt, den der Gast noch vor sich hätte. */
.konto-einladung {
    margin: clamp(18px, 3vw, 28px) 0 0;
    padding: 14px 16px;
    border: 1px solid var(--c-line);
    border-radius: 3px;
    font-size: .95rem;
    line-height: 1.55;
}

/* ⚠️ DIESER BLOCK STEHT VOR DEM KONTOR-BLOCK, UND DAS IST KEINE GESCHMACKSFRAGE.
      `LayoutKontorTest` liest alles hinter der ersten `.layout-kontor`-Regel als dessen
      Block und verlangt, dass JEDE Regel darin an `.layout-kontor` gebunden ist. Ans
      Dateiende angehängt landete «.hinweis-band» dort — ungebunden — und nahm ausserdem
      `[hidden]` mit, das der zweite Wächter dort ebenfalls verbietet. Zwei rote Tests aus
      einer Zeilennummer. Dieselbe Falle steht weiter oben schon einmal beschrieben
      (`.ladenkopf__name`); sie erwischt jeden, der «unten anfügen» für neutral hält. */

/* ==========================================================================
   BETRIEBSFERIEN — das Band und das Türschild
   ==========================================================================

   ⛔ DAS SCHILD TRÄGT SEINE FARBEN SELBST, statt sie aus dem Tag/Nacht-Umschalter zu holen.
      Die Speisekarte läuft standardmässig dunkel, alle anderen Seiten hell — dasselbe Schild
      erschiene also auf zwei Seiten desselben Hauses in zwei Ausführungen. Ein Schild an einer
      Tür sieht abends aus wie morgens. Deshalb Papier, Messing und Tinte, direkt benannt.

   ⚠️ DAS BAND NIMMT NICHT DIE MARKENFARBE, und seit es auch die Ankündigungen trägt, gilt das
      für beide Sorten Hinweis. Die alte `.announce` stand in `--accent`, weil eine Neuigkeit
      dem Haus gehört. «Wir sind zu» gehört keinem — und in einem Haus mit grüner Marke wäre
      ausgerechnet die Schliessung die freundlichste Farbe auf der Seite gewesen. Ein Band, das
      je nach Anlass die Farbe wechselt, wäre ausserdem kein Band mehr, sondern zwei. */

.hinweis-band {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: center;
    gap: 2px var(--s-3);
    padding: 10px var(--s-4);
    background: var(--c-ink);
    color: var(--on-dark);
    font-size: var(--fs-1);
    line-height: 1.45;
    text-align: center;
    /* Der Messingstrich unten trennt das Band vom Kopf darunter, ohne eine zweite Linie zu
       zeichnen: ohne ihn schwimmen zwei dunkle Flächen ineinander, sobald ein Haus einen
       dunklen Kopf hat. */
    border-bottom: 2px solid var(--c-brass);
}

/* ⛔ DER LINK TRÄGT DIE FARBE DES BANDES, NICHT DIE DER SEITE. Ohne diese Zeile erbt eine
   Ankündigung mit Link das globale `a { color: var(--link) }` — Petrol auf Tinte, gemessen
   rund 2,5:1, und zwar in der obersten Zeile der Seite, an der Stelle, die zuerst gelesen
   wird. Der Kontor-Aufbau hatte die Regel längst; der Grundentwurf nicht. */
.hinweis-band a {
    color: var(--on-dark);
    text-decoration: underline;
    text-underline-offset: 3px;
}

.hinweis-band__wort {
    font-weight: 700;
    letter-spacing: .01em;
}

.hinweis-band__satz {
    color: var(--on-dark-soft);
}

/* DAS DEMO-BAND (D-133) trägt Messing statt Tinte: es ist keine Nachricht des Hauses, sondern
   eine der Plattform, und es darf neben einem Urlaubs- oder Hinweisband stehen, ohne mit ihm
   zu einem Balken zu verschwimmen. Tinte auf Messing, gemessen rund 7:1. */
.hinweis-band--demo {
    background: var(--c-brass);
    color: var(--c-ink);
    border-bottom-color: var(--c-ink);
}

.hinweis-band--demo .hinweis-band__satz,
.hinweis-band--demo a {
    color: var(--c-ink);
}

/*
 * ⚠️ IM KOPF, ALSO GEGEN DESSEN DURCHSICHT. `.site-header` liegt bei 88 % Deckkraft mit
 *    `backdrop-filter` über der Seite; ein Band ohne eigene volle Deckkraft liesse den Text
 *    darunter durchscheinen, und «Wir machen Sommerpause» stünde über einem Foto. Deshalb
 *    `--c-ink` ohne Mischung — und der negative Rand, weil `.site-header` selbst keinen
 *    Innenabstand hat, seine Kinder aber im `.wrap` sitzen.
 */
.site-header > .hinweis-band {
    position: relative;
    z-index: 1;
}

/*
 * ⛔ AUF DEM TELEFON KOSTET EIN KLEBENDER HINWEIS ECHTEN PLATZ, und das ist die einzige Zahl,
 *    die hier zählt. Gemessen auf 390 × 844 mit dem vollen Satz: das Band brach auf DREI
 *    Zeilen, der Kopf wurde 157 px hoch — ein Fünftel des Bildschirms, dauerhaft, auf jeder
 *    Seite, während der Gast scrollt. Auf dem Schreibtisch sind es 109 px und niemand merkt es.
 *
 *    Deshalb wird auf schmalen Schirmen der Satz enger gesetzt statt weggelassen: kleinere
 *    Schrift, weniger Innenabstand, engere Zeilen. Die Auskunft bleibt vollständig — sie ist
 *    der Grund, warum das Band überhaupt klebt — sie nimmt nur weniger Raum ein.
 */
@media (max-width: 719px) {
    .hinweis-band {
        gap: 0 var(--s-2);
        padding: 7px var(--s-3);
        font-size: var(--fs-0);
        line-height: 1.35;
    }

    /* ⚠️ `balance` UND NICHT `pretty`, weil hier eine ganze ZEILE auf dem Spiel steht und nicht
          nur ihr Aussehen. Ohne das brach «… ab 17:00 Uhr» so um, dass in der dritten Zeile das
          Wort «Uhr» allein stand: 18 px Kopfhöhe für drei Buchstaben, dauerhaft klebend. */
    .hinweis-band__satz {
        text-wrap: balance;
    }
}

/* Der Trennpunkt erscheint nur, solange beide Teile in einer Zeile stehen. Auf dem Telefon
   brechen sie um, und ein Punkt am Zeilenende wäre ein Satzzeichen, das keines ist. */
@media (min-width: 720px) {
    .hinweis-band__satz::before {
        content: "·";
        margin-right: var(--s-3);
        color: var(--on-dark-accent);
    }
}

/* ---------- Das Schild ---------- */

/* Ohne diese Zeile ist `hidden` wirkungslos, sobald irgendeine Regel `display` setzt — und
   dann liegt ein unsichtbares, aber anklickbares Feld über der ganzen Seite. */
.urlaub[hidden] { display: none; }

.urlaub {
    position: fixed;
    inset: 0;
    z-index: 90;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: clamp(16px, 7vh, 76px) var(--s-4) var(--s-6);
    background: var(--scrim-74);
    -webkit-backdrop-filter: blur(3px);
    backdrop-filter: blur(3px);
    /* Ein langes Schild auf einem kurzen Telefon muss scrollen können. */
    overflow-y: auto;
    overscroll-behavior: contain;
}

/*
 * DIE GANZE AUFHÄNGUNG SCHWINGT, nicht die Karte allein — und der Drehpunkt sitzt oben an der
 * Stange, weil ein Schild dort hängt. Mit `transform-origin: center` würde es sich um seine
 * eigene Mitte drehen, was kein Pendel ist, sondern ein Kreisel.
 */
.urlaub__haenge {
    position: relative;
    width: min(460px, 100%);
    padding-top: 54px;
    transform-origin: 50% 0;
    /* `will-change` nur hier: Motion bewegt `y` und `rotate` gleichzeitig auf diesem einen
       Element, und genau das ist die Stelle, an der ein Telefon sonst ruckelt. */
    will-change: transform;
}

.urlaub__seile {
    position: absolute;
    inset: 0 0 auto;
    height: 54px;
    pointer-events: none;
}

.urlaub__stange {
    position: absolute;
    top: 0;
    left: 50%;
    width: 128px;
    height: 3px;
    margin-left: -64px;
    border-radius: 2px;
    background: linear-gradient(90deg,
        color-mix(in srgb, var(--c-brass) 55%, transparent),
        var(--c-brass) 22%,
        var(--c-brass) 78%,
        color-mix(in srgb, var(--c-brass) 55%, transparent));
}

.urlaub__seil {
    position: absolute;
    top: 3px;
    bottom: 0;
    width: 2px;
    /* Von oben nach unten wachsend, weil Motion `scaleY` von 0 aufzieht: mit dem
       Standard-Ursprung in der Mitte käme die Schnur aus ihrer eigenen Mitte heraus. */
    transform-origin: 50% 0;
    background: linear-gradient(180deg, var(--c-brass), color-mix(in srgb, var(--c-brass) 72%, var(--c-ink)));
}

.urlaub__seil--links  { left: calc(50% - 58px); }
.urlaub__seil--rechts { left: calc(50% + 56px); }

.urlaub__karte {
    position: relative;
    padding: var(--s-6) var(--s-5) var(--s-5);
    background: var(--c-ivory);
    border: 1px solid var(--c-stone);
    border-radius: var(--r-md);
    box-shadow: 0 26px 60px -18px rgba(20, 24, 28, .55), 0 3px 10px rgba(20, 24, 28, .18);
    text-align: center;
    color: var(--c-ink);
}

/* Die zweite, eingerückte Linie — der Doppelrahmen einer gedruckten Karte. Als Pseudoelement,
   damit dafür kein zweites <div> im Dokument steht, das nichts sagt. */
.urlaub__karte::before {
    content: "";
    position: absolute;
    inset: 7px;
    border: 1px solid color-mix(in srgb, var(--c-brass) 40%, transparent);
    border-radius: calc(var(--r-md) - 5px);
    pointer-events: none;
}

/* Die beiden Ösen, an denen die Schnüre sitzen. Ohne sie enden die Schnüre auf dem Papier,
   und das Schild wirkt aufgeklebt statt aufgehängt. */
.urlaub__karte::after {
    content: "";
    position: absolute;
    top: 9px;
    left: 50%;
    width: 6px;
    height: 6px;
    margin-left: -61px;
    border-radius: 50%;
    background: color-mix(in srgb, var(--c-brass) 80%, var(--c-ink));
    box-shadow: 114px 0 0 color-mix(in srgb, var(--c-brass) 80%, var(--c-ink));
}

.urlaub__auge {
    margin: 0 0 var(--s-2);
    font-family: var(--sans);
    font-size: var(--fs-0);
    font-weight: 700;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--c-brass);
}

.urlaub__titel {
    margin: 0 0 var(--s-4);
    font-family: var(--serif);
    font-size: var(--fs-5);
    line-height: 1.18;
    font-weight: 600;
    text-wrap: balance;
}

.urlaub__spanne {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    margin: 0 0 var(--s-4);
    font-family: var(--serif);
    font-size: var(--fs-3);
    line-height: 1.35;
}

.urlaub__bis {
    font-family: var(--sans);
    font-size: var(--fs-0);
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--c-muted);
}

/* «bis» steht zweimal im Dokument: einmal sichtbar als Zierschrift, einmal nur für die
   Vorlesesoftware. Ohne das läse sie entweder das Wort in seiner ganzen Sperrung vor oder,
   wäre es rein dekorativ, zwei Daten ohne jede Beziehung zueinander. */
.urlaub__nur-vorlesen {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.urlaub__rueck {
    display: inline-block;
    margin: 0;
    padding: 10px var(--s-4);
    border: 1px solid color-mix(in srgb, var(--c-brass) 55%, transparent);
    border-radius: var(--r-tag);
    background: color-mix(in srgb, var(--c-brass) 12%, var(--c-paper));
    font-size: var(--fs-2);
    font-weight: 600;
    line-height: 1.4;
    color: var(--gold-ink);
    text-wrap: balance;
}

.urlaub__rand,
.urlaub__gruss {
    margin: var(--s-3) 0 0;
    font-size: var(--fs-1);
    line-height: 1.55;
    color: var(--c-muted);
    text-wrap: pretty;
}

.urlaub__gruss {
    font-family: var(--serif);
    font-style: italic;
    font-size: var(--fs-2);
    color: var(--c-ink);
}

.urlaub__knopf {
    min-height: 44px;
    margin-top: var(--s-5);
    padding: 0 var(--s-5);
    border: 1px solid var(--c-accent);
    border-radius: var(--r-btn);
    background: var(--c-accent);
    /* ⛔ NICHT `--on-accent`, UND DAS WAR EIN ECHTER FEHLER IN DIESEM SCHILD. Der Wert dreht
       sich in der Nachtpalette (#FFFFFF → #12161A), die Fläche `--c-accent` aber nicht. Auf
       der Speisekarte, die standardmässig dunkel öffnet, wäre der Knopf «Verstanden» damit
       dunkler Schiefer mit fast schwarzer Schrift gewesen. Das Schild trägt seine Farben
       bewusst selbst — dann muss es BEIDE selbst tragen, nicht eine davon der Palette
       überlassen. */
    color: var(--on-dark);
    font-family: var(--sans);
    font-size: var(--fs-1);
    font-weight: 600;
    letter-spacing: .01em;
    cursor: pointer;
}

.urlaub__knopf:hover { background: var(--c-accent-2); border-color: var(--c-accent-2); }
.urlaub__knopf:focus-visible { outline: 3px solid var(--c-brass); outline-offset: 2px; }

/*
 * ⚠️ DIE SCHNÜRE VERSCHWINDEN BEI `reduce`, UND DAS IST KEINE SPARMASSNAHME. Ohne Bewegung
 *    hängt das Schild nicht, es liegt da — und zwei Schnüre, die aus dem Nichts an die
 *    Bildschirmkante stossen, sind dann kein Bild mehr, sondern zwei Striche. Motion setzt in
 *    diesem Fall alle Dauern auf null (siehe urlaub.js); hier fällt weg, was nur durch die
 *    Bewegung Sinn ergab.
 */
@media (prefers-reduced-motion: reduce) {
    .urlaub__seile { display: none; }
    .urlaub__haenge { padding-top: 0; }
    .urlaub__karte::after { display: none; }
}

@media (max-width: 480px) {
    .urlaub__karte { padding: var(--s-5) var(--s-4) var(--s-4); }
    .urlaub__stange { width: 96px; margin-left: -48px; }
    .urlaub__seil--links  { left: calc(50% - 42px); }
    .urlaub__seil--rechts { left: calc(50% + 40px); }
    .urlaub__karte::after { margin-left: -45px; box-shadow: 82px 0 0 color-mix(in srgb, var(--c-brass) 80%, var(--c-ink)); }
}

/* Die beiden Daten tragen das Gewicht, das «bis» dazwischen nicht — sonst liest sich die
   Spanne als drei gleichrangige Zeilen, und der Blick findet die Zahlen nicht. */
.urlaub__tag {
    font-weight: 600;
}

/* ==========================================================================
   ÜBER UNS — die Geschichte des Hauses
   ==========================================================================

   ⚠️ AUCH DIESER BLOCK STEHT VOR DEM KONTOR-BLOCK, aus dem Grund, der zwei Kästen weiter
      oben ausgeschrieben ist: alles hinter der ersten `.layout-kontor`-Regel gilt als
      dessen Block, und `LayoutKontorTest` verlangt dort jede Regel gebunden. «Unten
      anfügen» ist in dieser Datei die Falle, nicht die neutrale Wahl.

   ⛔ EINE SCHMALE SPALTE, UND DAS IST DER GANZE ENTWURF. Jedes andere Band der Startseite
      trägt ein bis zwei Sätze neben einem Bild; dies trägt Absätze. Fliesstext liest man
      auf 60 bis 70 Zeichen — `.split` hätte daraus eine halbe Spalte Prosa neben einem
      Foto gemacht, also genau die Form, in der niemand bis zum letzten Absatz kommt.

   ⚠️ DAS BILD STEHT UNTER DEM TEXT. Es bestätigt, was man gelesen hat — der Raum, die
      Familie, der Ofen — und konkurriert nicht mit dem ersten Satz um den Blick. Deshalb
      auch 16:9 und nicht hochkant: ein Porträt neben Prosa wäre wieder der Zweispalter.
   ========================================================================== */

.home-ueber .wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: clamp(20px, 3vw, 32px);
}

.home-ueber__kopf {
    max-width: 34em;
    text-align: center;
}

.home-ueber__kopf h2 { margin: .18em 0 0; }

/* 62 Zeichen, nicht 66 wie beim Fliesstext daneben: dieser Absatz steht mittig, und eine
   zentrierte Spalte darf schmaler sein, weil das Auge den Zeilenanfang nicht sucht. */
.home-ueber__text {
    max-width: 62ch;
}

.home-ueber__text > p + p { margin-top: 1em; }

.home-ueber__figur {
    margin: 0;
    width: 100%;
    max-width: 62rem;
}

.home-ueber__img {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-radius: var(--r-sm);
    box-shadow: var(--shadow-sm);
}

/* ==========================================================================
   GOOGLE-BEWERTUNG — die Bitte am Fuss der Startseite
   ==========================================================================

   ⚠️ AUCH DIESER BLOCK STEHT VOR DEM KONTOR-BLOCK. Alles hinter der ersten
      `.layout-kontor`-Regel gilt als dessen Eigentum und muss dort gebunden sein; «unten
      anfügen» ist in dieser Datei die Falle. Die Kontor-Fassung dieses Bandes steht ganz
      am Ende der Datei, wo sie hingehört.

   ⛔ ZUERST GESTAPELT, AB 821px NEBENEINANDER — und die Richtung ist Absicht. Die
      Zweispalter dieser Datei sind meist andersherum geschrieben (`max-width`), und genau
      daran ist der Kontor-Block schon einmal hängengeblieben: `.layout-kontor .gbew__tat`
      ist (0,2,0) und schlägt ein `.gbew__tat` (0,1,0) auch innerhalb einer Medienabfrage.
      Was für den breiten Schirm gilt, steht deshalb hier UND dort in derselben Abfrage,
      und die schmale Ansicht ist für beide Aufbauten dieselbe.

   ⛔ DIE FÜNF STERNE SIND KEINE NOTE. Warum das Band keine `AggregateRating` ausgibt, steht
      im Partial — hier ist nur wichtig, dass sie golden sind, weil ein Stern golden ist,
      und nicht in der Hausfarbe: sie zitieren Google, nicht das Restaurant. Der Knopf
      darunter trägt die Hausfarbe (`.btn--primary`) und ist damit das einzige Element, das
      von Haus zu Haus wechselt — so wie auf jedem anderen Band dieser Seite.
   ========================================================================== */

/* ENGER ALS DIE BÄNDER DARÜBER, oben wie unten. Der Bandrhythmus dieser Seite ist `--s-8`
   auf jeder Seite, also rund 190px zwischen zwei Abschnitten — richtig für zwei Bänder, die
   beide etwas erzählen. Was hier steht, ist ein Nachsatz von vier Zeilen in einer Karte, und
   am Bildschirm gemessen schwamm sie zwischen der Karte darüber und dem dunklen Fuss
   darunter, statt den Abschluss zu machen. */
.gbew { padding: var(--s-7) 0; }

.gbew__karte {
    display: grid;
    gap: clamp(22px, 3vw, 40px);
    padding: clamp(22px, 3.2vw, 40px);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r);
    box-shadow: var(--shadow-sm);
}

.gbew__wort { display: flex; align-items: flex-start; gap: clamp(14px, 1.8vw, 22px); }

/* `line-height: 0` — sonst setzt die Zeilenhöhe des <span> unter dem G noch einmal ein
   Viertel Zeile Luft, und das Zeichen sitzt sichtbar zu hoch neben der Überschrift. */
.gbew__zeichen { flex: none; line-height: 0; }

/* HÖHE AUSGESCHRIEBEN, NICHT `auto`. Ein Inline-SVG mit viewBox rechnet sich seine Höhe
   zwar aus dem Seitenverhältnis, aber erst nachdem das Bild da ist — bis dahin steht die
   Zeile mit 150px Vorgabehöhe, und die Karte springt beim Aufbau. Beide Zeichen sind
   quadratisch, also ist die Zahl dieselbe wie die Breite. */
.gbew__zeichen svg { width: clamp(38px, 4vw, 52px); height: clamp(38px, 4vw, 52px); }

.gbew__satz { min-width: 0; }
.gbew__titel { font-size: clamp(1.3rem, 2.4vw, 1.85rem); margin: 0 0 .4em; }
/* 46 Zeichen: drei kurze Fragen hintereinander, und keine davon soll über die halbe Karte
   laufen, während rechts daneben der Knopf steht. */
.gbew__text { margin: 0; color: var(--muted); max-width: 46ch; }

.gbew__lead { margin: 0 0 var(--s-3); }

.gbew__sterne { display: flex; gap: 4px; margin: 0 0 var(--s-4); color: #FBBC05; }
.gbew__sterne svg { width: clamp(22px, 2.4vw, 28px); height: clamp(22px, 2.4vw, 28px); fill: currentColor; }

/* Auf dem Telefon über die volle Breite: der Knopf ist das Einzige, was auf diesem Band
   angeklickt werden kann, und ein Daumen trifft eine ganze Zeile besser als ein Wort. */
.gbew__knopf { width: 100%; }

@media (min-width: 821px) {
    .gbew__karte {
        grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
        align-items: center;
    }

    /* Die rechte Hälfte ist eine Handlung und keine Fortsetzung des Textes — mittig
       gesetzt liest sie sich als eigener Block, linksbündig als vierter Absatz. */
    .gbew__tat { text-align: center; }
    .gbew__sterne { justify-content: center; }
    .gbew__knopf { width: auto; }
}

/* ==========================================================================
   DIE STARTSEITE EINES HAUSES, DAS KEINE FOTOS HAT
   ==========================================================================

   ⛔ DER BEFUND, AN ZWEI ECHTEN HÄUSERN GEMESSEN. Das eine: 66 Fotos an 81 Gerichten,
      sechs von sechs Textbändern ausgefüllt — eine Startseite, die trägt. Das andere:
      195 Gerichte, NULL Fotos, NULL ausgefüllte Bänder. Dessen Seite bestand aus einer
      dunklen Platte, vier Monogrammkacheln und «So finden Sie uns».

      Bühne ist nicht kaputt; sie war gegen das erste Haus entworfen, und die Flotte steht
      am anderen Ende. Was hier steht, gestaltet die Zustände, die es bisher nur als
      Weglassung gab — die Aufbauten und die Grammatik bleiben, wie sie sind.

   ⚠️ UND ALLES HIER STEHT VOR DEM KONTOR-BLOCK, aus dem Grund, der weiter oben zweimal
      ausgeschrieben ist: alles hinter der ersten `.layout-kontor`-Regel gilt als dessen
      Eigentum und muss dort gebunden sein. Zusätzlich sind alle Regeln unten mit einer
      Klasse gewichtet — nie mit `:not(:has(…))` —, damit Kontors eigene Regeln
      (gleiche Spezifität, später in der Datei) sie weiter überstimmen. Ein `:has()` hätte
      die Spezifität um eins gehoben und Kontor still auf Bühnes Liste umgestellt: genau
      der Fehler, den D-91 beschreibt.
   ========================================================================== */

/* -- Beiwort UND Küche in einer Zeile -------------------------------------- */
/* `restaurant.cuisine` war an jedem Haus gepflegt und wurde ausschliesslich an Suchmaschinen
   ausgeliefert. Die beiden zusammen sind die billigste Antwort auf «jedes Haus soll nach sich
   selbst aussehen», die dieses Produkt hat. */
.eyebrow--zeile { display: flex; align-items: baseline; flex-wrap: wrap; gap: 9px; margin: 0; }
.eyebrow__punkt { opacity: .5; letter-spacing: 0; }
.eyebrow__kueche { letter-spacing: .2em; }

/* -- Die gerechneten Hausfakten: seit dem 16.09.2026 nicht mehr gezeichnet (D-126) ------- */

/* -- Das Beiwort als Kontur auf der fotolosen Platte ----------------------- */
/*
 * `.hero__accent-watermark` liegt seit langem im Stylesheet und wurde von genau EINER Seite
 * geschrieben: der 404. Die Platte, für die es gedacht ist, hat es nie erreicht.
 *
 * ⚠️ NUR OHNE FOTO — die Vorlage rendert es gar nicht erst, wenn ein Bild da ist: dort ist das
 *    Bild der Grund, und eine Kontur darüber wäre eine zweite Geste auf derselben Fläche.
 * ⚠️ Die Kontur nimmt den hellen Akzentton des dunklen Grundes statt `--gold`; auf der Platte
 *    ist Gold die richtige Familie, aber `--gold` ist für dunkel gedacht zu tief.
 */
.hero--foto .hero__accent-watermark {
    right: -3%;
    -webkit-text-stroke: 1.5px color-mix(in oklab, var(--on-dark-accent) 30%, transparent);
    opacity: .85;
}

/*
 * ⚠️ AUF DEM TELEFON NIEDRIGER — ABER NUR OHNE FOTO. Der Inhalt sitzt am unteren Rand der
 *    Platte (`justify-content: flex-end`); bei 58vh lagen Zustand und die beiden Knöpfe unter
 *    der Kante, und über einem Haus mit zwei Filialen stand davor noch die Standortwahl.
 *    Gemessen bei 390 × 844: der ganze erste Bildschirm war belegt, ohne dass ein Knopf
 *    sichtbar war.
 *
 *    Ein Haus MIT Foto behält seine Höhe. Dort ist die Fläche das Bild, und ein Bild niedriger
 *    zu machen heisst, weniger davon zu zeigen — das ist bei diesen Häusern genau das
 *    Gegenteil dessen, was der Aufbau will.
 */
@media (max-width: 620px) {
    .hero--foto:not(.is-photo) { min-height: clamp(320px, 44vh, 420px); }
}

/* -- Vier Karten ohne ein einziges Foto ------------------------------------ */
/*
 * ⛔ SIE BLEIBEN KARTEN. Der naheliegende Griff wäre, sie zur Liste zu machen — und das wäre
 *    Kontor. Zwei Aufbauten, die im häufigsten Fall gleich aussehen, sind eine Entscheidung,
 *    für die der Betreiber zweimal bezahlt hat. Was verschwindet, ist die leere Bildfläche;
 *    was bleibt, ist Bühnes Grammatik: Fläche, Rundung, Kante.
 *
 * ⚠️ DIE ZEILE AUS NAME, PUNKTLINIE UND PREIS IST HIER ENTSTANDEN und steht inzwischen an
 *    `.dishcard` selbst — die Karte MIT Foto trägt sie seit demselben Durchgang. Alles, was
 *    davon hier stehen bliebe, wäre eine zweite Beschreibung derselben Sache; der eine echte
 *    Unterschied ist die Anzahl der Spalten.
 *
 * ⚠️ ZWEI SPALTEN, NICHT «so viele wie passen». `auto-fit` ergab bei vier Karten auf einem
 *    1280er Schirm drei oben und eine allein darunter — ein Raster, das aussieht, als sei
 *    etwas abgebrochen. Ohne Bild sind die Karten schmal genug für zwei, und zwei Spalten
 *    sind ausserdem, wie eine gedruckte Karte gesetzt wird.
 */
.dish-grid--ohne-foto { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 620px) { .dish-grid--ohne-foto { grid-template-columns: 1fr; } }

/*
 * ⚠️ HIER HAT DIE ZEILE PLATZ, und das ist der ganze Unterschied zur Karte mit Foto: zwei
 *    Spalten statt vier, also rund die doppelte Breite. Der Name bricht nicht auf drei Zeilen,
 *    und die Punktlinie ist eine Linie statt vier Punkten.
 *
 * ⚠️ DER PREIS STEHT AUF DER HÖHE DES NAMENS. `grid-row: 2` setzt ihn neben die Beschreibung —
 *    genau der Fehler, den D-91 auf der Kontor-Seite schon einmal gekostet hat; auf einer
 *    gedruckten Karte steht der Preis IMMER auf der Höhe des Gerichts, das er kostet.
 */
.dish-grid--ohne-foto .dishcard { grid-template-columns: 1fr auto; align-items: baseline; }

.dish-grid--ohne-foto .dishcard__name {
    grid-row: 1; grid-column: 1;
    display: flex; align-items: baseline; gap: 12px;
    margin: 0;
}

/* Dieselbe Geste wie `.mdish__lead` auf der Speisekarte — 2 px gepunktet, um ein Viertel
   Geviert angehoben. Eine dritte Schreibweise für dieselbe Linie wäre die Stelle, an der sie
   eines Tages verschieden aussehen. */
.dish-grid--ohne-foto .dishcard__name::after {
    content: '';
    flex: 1 1 auto;
    min-width: 18px;
    border-bottom: 2px dotted var(--line);
    transform: translateY(-.25em);
}

.dish-grid--ohne-foto .dishcard__price {
    grid-row: 1; grid-column: 2;
    align-self: baseline; margin-top: 0;
    white-space: nowrap;
}

.dish-grid--ohne-foto .dishcard__desc { grid-row: 2; grid-column: 1 / -1; }

.dish-grid--ohne-foto .dishcard__tags {
    grid-row: 3; grid-column: 1 / -1;
    justify-self: start; justify-content: flex-start;
    margin-top: 10px;
}


/* ==========================================================================
   BILDERLEISTE — die Galerie als gefahrene Reihe
   ==========================================================================

   ⛔ WAS SIE ERSETZT, WAR EIN MAUERWERK. `.gal-masonry` zeigte drei Spalten und alle Bilder
      gleichzeitig: bei acht Fotos knapp tausend Pixel Höhe, alles gleich gross, nichts betont.
      Ein Restaurant hat aber ein bestes Foto, und das soll gross sein. (Das Mauerwerk bleibt
      im Blatt und im Einsatz — die SEO-Seiten schreiben es weiter, und dort ist es richtig:
      ein Textdokument mit ein paar Bildern will keine Fahrt.)

   ⛔ DER GANZE MECHANISMUS IST EIN SCROLLBEREICH, KEIN KARUSSELL AUS SKRIPT. `scroll-snap`
      rastet das mittlere Bild ein, der Daumen wischt ohnehin, und ohne JavaScript bleibt alles
      erreichbar. Was Skript braucht, sind nur die Pfeile und der Balken; die tragen `hidden`
      im Markup und werden von `site.js` freigeschaltet.

   ⚠️ DIE VERGRÖSSERUNG DER MITTE KOMMT AUS DER STILVORLAGE UND NICHT AUS EINEM SKRIPT, das bei
      jedem Bild die Position ausrechnet. `animation-timeline: view()` bindet die Bewegung an
      die Sichtbarkeit des Bildes IM Scrollbereich — der Browser rechnet, nichts läuft in
      JavaScript, und ein Browser ohne diese Zeitachse zeigt schlicht alle Bilder gleich gross.
      Der Rückfall ist eine ordentliche Leiste, kein Bruch.

   ⚠️ UND KEINE DECKKRAFT NULL. Die Nachbarn gehen auf .55 herunter, nicht auf 0: was auf
      dieser Seite unsichtbar wird, muss unter `.js-motion` stehen und von einem Wachhund
      zurückgeholt werden können (BewegungTest). Ein Bild, das man halb sieht, ist eine
      Einladung; ein Bild, das verschwindet, ist ein Fehler.
   ========================================================================== */

/* Das Band ist der einzige Abschnitt der Seite, dessen Inhalt ÜBER den Satzspiegel hinausläuft.
   Der Kopf steht deshalb in seinem eigenen `.wrap`, und der Abschnitt selbst gibt seitlich
   nichts vor — sonst stünde die Leiste in einem 22-px-Rahmen und die Nachbarn ragten nicht mehr
   an den Rand. Unten etwas enger, weil eine gefahrene Reihe ihre eigene Luft mitbringt. */
.bleiste-band { overflow: hidden; padding-bottom: var(--s-7); }

.bleiste { position: relative; }

/* Die Breite EINES Bildes, und alles andere rechnet daraus: der seitliche Einzug der Spur
   zentriert damit das erste und das letzte Bild, ohne dass irgendwo eine zweite Zahl steht. */
.bleiste__spur {
    /* 58vw statt 78: bei 78 blieb links und rechts kaum ein Streifen des Nachbarn übrig, und
       eine Reihe, von der man nur EIN Bild sieht, ist keine Reihe. */
    --bleiste-breite: min(58vw, 620px);

    list-style: none;
    margin: 0;
    display: flex;
    gap: clamp(16px, 2.2vw, 30px);
    padding: 8px calc(50% - var(--bleiste-breite) / 2) 10px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    scrollbar-width: none;
}

/* Der Balken darüber sagt bereits, wo man ist — eine zweite Leiste darunter wäre dieselbe
   Auskunft zweimal, und auf dem Telefon zeichnet sie ohnehin niemand. */
.bleiste__spur::-webkit-scrollbar { display: none; }

.bleiste__bild {
    flex: 0 0 var(--bleiste-breite);
    scroll-snap-align: center;
}

/* `position: relative` TRÄGT DIE BILDUNTERSCHRIFT, die jetzt AUF dem Bild liegt. Und keine
   Kante mehr: ein Rahmen um eine Fotografie, die ohnehin auf hellem Papier schwebt, ist die
   Linie zuviel — was sie vom Papier abhebt, ist der Schatten. */
.bleiste__figur {
    position: relative;
    margin: 0;
    border-radius: 14px;
    overflow: hidden;
    background: var(--surface);
    box-shadow: var(--shadow-xl);
}

/* 4:3 UND `object-fit`, WEIL DIE BILDER EINES HAUSES NIE DASSELBE FORMAT HABEN. Im Mauerwerk
   durfte jedes seine eigene Höhe behalten; in einer Reihe wäre das eine Zeile aus Treppen. Die
   Angaben `width`/`height` am Bild bleiben trotzdem stehen — sie reservieren den Platz, bevor
   die Datei da ist. */
.bleiste__foto {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 4 / 3;
    object-fit: cover;
}

/*
 * ⛔ DIE BILDUNTERSCHRIFT LIEGT AUF DEM BILD, NICHT IN EINEM KASTEN DARUNTER. Der weisse
 *    Streifen unter jeder Fotografie brach das Rechteck und sah aus wie ein leeres
 *    Eingabefeld — am Bildschirm gesehen und genau so gemeldet. Auf dem Bild ist sie das, was
 *    sie ist: eine Beschriftung.
 *
 * ⚠️ DER VERLAUF IST DER ALPHA-BODEN, KEIN GESCHMACK. Die Fotografien kommen von den Häusern,
 *    und darunter ist eines, das an dieser Stelle fast weiss ist. `--scrim-74`/`--scrim-90`
 *    sind die Werte, die dieselbe Datei für ihre Foto-Aufmacher ausgerechnet hat; die oberen
 *    drei Viertel bleiben durchsichtig, damit das Bild ein Bild bleibt.
 */
.bleiste__text {
    position: absolute;
    inset: auto 0 0 0;
    padding: 3.2rem 1.15rem 1rem;
    font-family: var(--sans);
    font-size: .88rem;
    line-height: 1.4;
    color: var(--on-dark);
    background: linear-gradient(180deg, transparent 0%, var(--scrim-74) 62%, var(--scrim-90) 100%);
}

/* -- Die Mitte ist gross ---------------------------------------------------- */
/*
 * ⚠️ .76 UND .34, NICHT .88 UND .55. Der erste Versuch war so vorsichtig, dass der Nachbar
 *    nicht kleiner aussah, sondern unscharf — und die Reihe damit wie ein Bauteil, das nicht
 *    fertig geladen hat. Ein Abfall, den man nicht sieht, ist kein Abfall.
 *
 * ⚠️ UND WEITERHIN NICHT AUF NULL: was auf dieser Seite unsichtbar wird, muss unter
 *    `.js-motion` stehen und von einem Wachhund zurückgeholt werden können (BewegungTest).
 */
@supports (animation-timeline: view()) {
    .bleiste__figur {
        animation: bleiste-mitte linear both;
        animation-timeline: view(inline);
        animation-range: cover 6% cover 94%;
    }

    @keyframes bleiste-mitte {
        0%   { transform: scale(.76); opacity: .34; }
        50%  { transform: scale(1);   opacity: 1; }
        100% { transform: scale(.76); opacity: .34; }
    }
}

/* -- Balken und Pfeile: nur mit Skript -------------------------------------- */
/*
 * `[hidden]` STEHT SCHON IM MARKUP; diese Regeln beschreiben, wie die beiden aussehen, sobald
 * `site.js` das Attribut abnimmt. Die Marke am <html> ist zusätzlich da, weil ein Skript, das
 * zur Hälfte durchläuft, sonst einen Knopf ohne Wirkung stehen liesse — dieselbe Bauform wie
 * `html:not(.has-menufilter) .m-bar`.
 */
html:not(.has-bleiste) .bleiste__pfeil,
html:not(.has-bleiste) .bleiste__balken { display: none; }

/* ⛔ UNTER DER LEISTE, NICHT DARÜBER, UND DÜNN. Über den Bildern war er ein zweites Bauteil,
      das mit der Überschrift um dieselbe Zeile stritt; darunter ist er die Fusszeile der Reihe.
      Zwei Pixel statt fünf: er ist eine Auskunft am Rand und kein Anzeigeelement. */
.bleiste__balken {
    width: min(52%, 380px);
    height: 2px;
    margin: clamp(14px, 2vw, 22px) auto 0;
    border-radius: 999px;
    background: var(--line);
    overflow: hidden;
}

.bleiste__lauf {
    display: block;
    height: 100%;
    width: 100%;
    border-radius: inherit;
    /* DIE HAUSFARBE, NICHT DAS ZIERGOLD. Dies ist eine Auskunft — wo bin ich in der Reihe —
       und keine Verzierung; `--gold` trug auf hellem Papier ausserdem so wenig, dass der
       gefahrene Teil vom Rest kaum zu unterscheiden war. */
    background: var(--accent);
    transform-origin: left center;
    /* Von `site.js` gesetzt: 0 bis 1. Ohne Skript ist der Balken ohnehin nicht da. */
    transform: scaleX(var(--bleiste-lauf, .2));
    transition: transform var(--t-fast);
}

/* `calc(50% - 1.4rem)`: der Balken sitzt jetzt UNTER der Leiste und gehört zur Höhe dieses
   Kastens. Ohne die Korrektur stünden die Knöpfe um seine halbe Höhe zu tief — nicht viel, aber
   genug, dass sie nicht mehr auf der Mitte des Bildes liegen. */
.bleiste__pfeil {
    position: absolute;
    top: calc(50% - 1.4rem);
    transform: translateY(-50%);
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    /* 48 px: dieselbe Daumengrösse wie jedes andere Bedienelement dieser Seite. */
    width: 48px;
    height: 48px;
    border-radius: 50%;
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--text);
    cursor: pointer;
    box-shadow: var(--shadow-sm);
    transition: border-color var(--t-fast), color var(--t-fast), box-shadow var(--t-fast);
}

.bleiste__pfeil svg { width: 22px; height: 22px; }
.bleiste__pfeil--zurueck { left: clamp(10px, 3vw, 46px); }
.bleiste__pfeil--vor { right: clamp(10px, 3vw, 46px); }

@media (hover: hover) {
    .bleiste__pfeil:hover { border-color: var(--accent); color: var(--accent-ink); box-shadow: var(--shadow-md); }
}

/* Am Anfang und am Ende der Reihe zeigt der Pfeil nach nirgends. Er bleibt STEHEN und wird
   blass, statt zu verschwinden: ein Knopf, der springt, verschiebt das Bild darunter. */
.bleiste__pfeil[disabled] { opacity: .35; cursor: default; box-shadow: none; }

/*
 * ⛔ AUF DEM TELEFON KEINE PFEILE. Am Bildschirm gemessen bei 390 px: die beiden Knöpfe lagen
 *    mitten auf der Fotografie, der linke halb ausserhalb — und sie tun dort nichts, was der
 *    Daumen nicht besser kann. Ein Bedienelement, das ein Bild verdeckt, um eine Geste zu
 *    ersetzen, die es ohnehin gibt, ist eine Verschlechterung.
 *
 * ⚠️ DER BALKEN BLEIBT. Er verdeckt nichts und beantwortet die einzige Frage, die eine
 *    gefahrene Reihe aufwirft: wie viel kommt noch.
 */
@media (max-width: 860px) {
    .bleiste__pfeil { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    .bleiste__spur { scroll-behavior: auto; }
    .bleiste__figur { animation: none; transform: none; opacity: 1; }
    .bleiste__lauf { transition: none; }
}

/* ==========================================================================
   KARTENSPIEGEL — das Inhaltsverzeichnis der Karte auf der Startseite
   ==========================================================================

   Die Begründung, warum es dieses Band gibt, steht im Partial. Hier zählt die Form: es ist
   ein VERZEICHNIS und keine zweite Speisekarte, also wird es wie eines gesetzt — Name in der
   Hausschrift, Zahl klein und rechts, eine Haarlinie je Zeile. Keine Pillen: eine Reihe
   Pillen liest sich als Filter, und hier ist nichts zu filtern.
   ========================================================================== */

/* OHNE EIGENEN ABSTAND NACH OBEN. Das Band gehört zu den vier Gerichten darüber — es sagt
   «und das ist die ganze Karte». Mit den vollen `--s-8` auf beiden Seiten stünde es als
   dritter, unverbundener Abschnitt da, und genau diese Löcher sind es, die eine Seite mit
   wenigen Bändern leer wirken lassen. */
/* ═══ DER KARTENDECKEL ═══════════════════════════════════════════════
   Hier lag der Kartenspiegel — ein Verzeichnis mit einer Zahl je Zeile. Es war richtig und es
   sah leer aus; ein Verzeichnis IST leer, es zählt auf, was woanders steht.

   ⛔ DIE FLÄCHE MUSS SELBST ETWAS SEIN. Eine grosse Schrift auf einer leeren Fläche wäre
      wieder «schlicht» — genau der Befund, der dieses Band ausgelöst hat. Ein gedruckter
      Kartendeckel trägt einen Rahmen, und die doppelte Haarlinie gibt es sonst NIRGENDS auf
      dieser Seite: sie sagt ohne ein Wort, dass hier die Karte beginnt. */
/* -- «So finden Sie uns»: zwei Spalten, die nicht gleich lang sind ---------- */
/* `.split` zentriert seine Spalten senkrecht, was für ein Bild neben einem Absatz richtig ist.
   Hier stehen links drei Zeilen Adresse und zwei Knöpfe gegen sieben Zeilen Öffnungszeiten:
   zentriert hängt die kurze Spalte in der Mitte und lässt oben und unten eine Fläche frei, die
   auf einer Seite ohne Texte die auffälligste des ganzen Dokuments ist. Beide beginnen jetzt
   oben, wie zwei Spalten eines Dokuments. */
.split--finden { align-items: start; }

/* ═══ DAS RESERVIERUNGSBAND ════════════════════════════════════════════
   An dieser Stelle sind vier Bänder gescheitert, alle mit demselben Satz: «zu schlicht». Alle
   vier SAGTEN etwas, und mit keinem konnte man etwas tun. Hier stehen zwei Felder und ein
   Knopf — die Fläche ist eine Fotografie des Hauses, und der Gast fängt hier wirklich an. */
.resband { padding-top: var(--s-7); padding-bottom: var(--s-7); }

.resband__inner { max-width: 46rem; }

.resband__ueber { color: var(--gold-ink); margin: 0; }

.resband__titel {
    font-family: var(--serif);
    font-size: clamp(1.7rem, 4.2vw, 2.6rem);
    line-height: 1.12;
    margin: clamp(10px, 1.6vw, 16px) 0 clamp(20px, 3vw, 28px);
    text-wrap: balance;
}

/* Zwei Felder und ein Knopf in EINER Zeile, solange sie passt — auf dem Telefon untereinander,
   und dort nimmt der Knopf die volle Breite, weil er dort das Ziel des Daumens ist. */
.resband__form {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--s-3) var(--s-4);
    margin: 0;
}

.resband__feld {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 0;
    flex: 1 1 11rem;
    min-width: 0;
}

.resband__label {
    font-family: var(--sans);
    font-size: var(--fs-0);
    font-weight: 600;
    letter-spacing: .04em;
    color: var(--muted);
}

/* ⚠️ 46 PX HOCH — DIESELBE HÖHE WIE DIE FELDER DER SCHUBLADE. Ein Datumsfeld ist auf dem
      Telefon die Fläche, die den Kalender öffnet; kleiner als der Daumen darf sie nicht sein. */
.resband__eingabe {
    min-height: 46px;
    padding: 0 12px;
    font: inherit;
    color: var(--text);
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    transition: border-color var(--t-fast), box-shadow var(--t-fast);
}

.resband__eingabe:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 20%, transparent);
    outline: none;
}

.resband__knopf { flex: 0 0 auto; min-height: 46px; }

.resband__hinweis {
    margin: var(--s-4) 0 0;
    font-size: var(--fs-0);
    color: var(--muted);
    max-width: 40em;
}

/* ⚠️ UNTEREINANDER HEISST NICHT GEDEHNT. `flex: 1 1 11rem` lässt die Felder in der Zeile
      wachsen — in der SPALTE lässt dasselbe sie in die Höhe wachsen, und dann standen auf dem
      Telefon «Tag» und «Personen» mit einer halben Bildschirmhöhe Foto dazwischen. Gemessen:
      das Band war 616 px hoch, davon der grösste Teil Zwischenraum. */
@media (max-width: 560px) {
    .resband__form { flex-direction: column; align-items: stretch; }
    .resband__feld { flex: 0 0 auto; }
    .resband__knopf { width: 100%; }
}

/* ── auf einer Fotografie ────────────────────────────────────────────
   ⚠️ DER SCHLEIER IST GERECHNET, NICHT GESCHÄTZT. Schrift auf einer Fotografie ist nur so
      lesbar wie das hellste Bild, das jemand hochlädt. Der Verlauf deckt dort, wo der Text
      steht, zusammen rund 69 % — über einer WEISSEN Fläche ergibt #FBF8F2 darauf etwa
      6,7 : 1. AA verlangt 4,5. Wird hier aufgehellt, muss diese Rechnung neu gemacht werden. */
.resband--foto {
    position: relative;
    isolation: isolate;
    overflow: hidden;
}

.resband__foto {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    z-index: -2;
}

/* ⛔ DER VERLAUF LÄUFT NACH RECHTS AUF, UND GENAU DAS WAR AUF DEM TELEFON FALSCH.
      Auf dem Breitbild steht der Text links in einer 46-rem-Spalte, also darf das Bild rechts
      hell bleiben. Auf dem Telefon nimmt derselbe Text die VOLLE Breite — und stand damit über
      dem hellen Ende des Verlaufs. Gemessen am hellsten Punkt hinter der Schrift: 4,41 : 1 für
      die Zeile oben und 4,47 : 1 für die Überschrift, beide unter den geforderten 4,5. Ein
      Verlauf, der eine Spaltenbreite voraussetzt, muss dort aufhören, wo die Spalte aufhört. */
.resband__schleier {
    position: absolute;
    inset: 0;
    z-index: -1;
    background:
        linear-gradient(90deg,
            rgba(18, 12, 7, .80) 0%, rgba(18, 12, 7, .72) 50%, rgba(18, 12, 7, .40) 82%, rgba(18, 12, 7, .22) 100%),
        linear-gradient(0deg, rgba(18, 12, 7, .24) 0%, rgba(18, 12, 7, .12) 100%);
}

/* Schmal: kein seitlicher Verlauf mehr, sondern gleichmässig dicht — gerechnet für den
   schlimmsten Fall, eine WEISSE Fläche darunter: rund 66 % Deckung ergeben mit #FBF8F2
   etwa 6 : 1. */
@media (max-width: 700px) {
    .resband__schleier {
        background: linear-gradient(0deg, rgba(18, 12, 7, .74) 0%, rgba(18, 12, 7, .62) 100%);
    }
}

.resband--foto .resband__ueber { color: #F1D79E; text-shadow: 0 1px 10px rgba(0, 0, 0, .45); }
.resband--foto .resband__titel { color: #FBF8F2; text-shadow: 0 2px 22px rgba(0, 0, 0, .42); }
.resband--foto .resband__label { color: rgba(251, 248, 242, .90); text-shadow: 0 1px 10px rgba(0, 0, 0, .45); }
.resband--foto .resband__hinweis { color: rgba(251, 248, 242, .90); text-shadow: 0 1px 12px rgba(0, 0, 0, .42); }

/* Die Felder bleiben hell und undurchsichtig: ein Eingabefeld, durch das ein Foto scheint, ist
   ein Eingabefeld, dessen Inhalt man rät. */
.resband--foto .resband__eingabe { background: var(--bg); border-color: rgba(255, 255, 255, .34); }

.layout-kontor .site-header { position: static; background: var(--bg); backdrop-filter: none; border-bottom: 0; }
/* Dieselbe Stelle, dieselbe Ursache: die Kurzschreibweise nahm den seitlichen Abstand mit. */
.layout-kontor .site-header__bar { flex-direction: column; gap: 10px; padding-top: 22px; padding-bottom: 0; position: relative; }
.layout-kontor .brand { flex-direction: column; align-items: center; text-align: center; gap: 4px; }
.layout-kontor .brand__text { align-items: center; }
.layout-kontor .brand__name { font-size: clamp(1.35rem, 3.4vw, 1.9rem); letter-spacing: .01em; }
.layout-kontor .brand__accent { letter-spacing: .18em; text-transform: uppercase; font-size: .68rem; color: var(--gold-ink); }

/* `.nav` IS TWO DIFFERENT COMPONENTS AT TWO WIDTHS, and this block was written for only one of
   them. Above 860px it is the row of links under the name — what these rules describe. At or
   below 860px the SAME element is the off-canvas panel that slides in from the right, laid out
   by a `@media (max-width: 860px)` block near :128.

   Unscoped, these rules outranked it: `.layout-kontor .nav` is 0,2,0 against `.nav`'s 0,1,0, so
   specificity wins regardless of source order and regardless of the media query. Measured in a
   real engine at 375px: the panel took the full 375px instead of `min(86vw, 340px)`, and
   `flex-wrap: wrap` + `justify-content: center` against a full-height box broke it into two
   columns — the contact block, with the restaurant's own telephone number, sat 98px off the
   right-hand edge. On a phone that panel is the only place the navigation exists at all.

   A media query rather than a more specific mobile selector: this is a masthead rule, and the
   masthead is a thing Kontor only has on a wide screen. 861px is the first pixel the panel is
   not in play. */
@media (min-width: 861px) {
    .layout-kontor .nav {
        margin-left: 0; justify-content: center; flex-wrap: wrap; width: 100%;
        border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
        margin-top: 14px; padding: 4px 0; gap: 0;
    }
    .layout-kontor .nav > a {
        font-family: var(--sans); font-size: .74rem; text-transform: uppercase;
        letter-spacing: .14em; padding: 12px 14px; border-radius: 0; opacity: .78;
    }
    .layout-kontor .nav > a[aria-current="page"] { opacity: 1; box-shadow: inset 0 -2px 0 var(--gold); }
}

/* Over the first row rather than in it: a centred name with a button beside it is not centred. */
.layout-kontor .header-actions { position: absolute; right: 0; top: 18px; }

/* -- Typography ------------------------------------------------------------ */
/* THESE THREE LIVED IN THE BASE SHEET FOR AN AFTERNOON AND WERE TAKEN BACK OUT. They are real
   improvements and they are not this product's ONLY design — putting them in the base changed
   the look of every restaurant already live, which is precisely what a second layout exists to
   avoid. They belong to Kontor, whose whole character is typographic.

     · a size for bare headings. Nothing sized them: only headings wearing `.section-head`,
       `.article__body` or a `__title` class were ever sized, so a bare <h2> fell through to the
       user agent's fixed 2em — measured on the homepage at 24px, one band below a heading that
       scales to 40px.
     · `hyphens: auto`. `overflow-wrap: break-word` breaks a German compound WITHOUT drawing a
       hyphen, and «Allergenkennzeich nung» reads as a rendering fault. <html lang="de"> is
       already set, so the patterns are there; nobody had asked for them.
     · `text-wrap: balance`, so a two-word orphan does not end a hero title. */
.layout-kontor h1, .layout-kontor h2, .layout-kontor h3, .layout-kontor h4 { hyphens: auto; }
.layout-kontor h1, .layout-kontor h2, .layout-kontor h3 { text-wrap: balance; }
.layout-kontor h1 { font-size: clamp(1.9rem, 5vw, 2.8rem); }
.layout-kontor h2 { font-size: clamp(1.5rem, 3.2vw, 2.1rem); }
.layout-kontor h3 { font-size: 1.2rem; }
.layout-kontor h4 { font-size: 1.05rem; }

/* -- The opening ----------------------------------------------------------- */
/* SCOPED TO `--foto`, and the first version was not — `.layout-kontor .hero__inner` at (0,2,0)
   beat the base rule everywhere, so it also fired inside `.menu-hero` and inside all eight
   plain `.hero` pages, quietly changing a layout it was never written for. */
.layout-kontor .hero--foto {
    display: block;
    background: var(--bg); color: var(--text); min-height: 0;
    padding-block: clamp(26px, 4vw, 46px) clamp(30px, 5vw, 56px);
    border-bottom: 1px solid var(--line);
}
.layout-kontor .hero--foto.is-photo { background-image: none; }

/*
 * ⛔ UND AUCH NICHT ÜBER DIE BEWEGUNG. Die Schwenk-Regeln weiter oben nehmen dem Aufmacher sein
 *    Hintergrundbild ab und malen es stattdessen auf `::before`, den Schleier auf `::after` —
 *    beides an `.js-motion .hero--foto.is-photo` (0,3,0). Kontors eigene Absage gilt aber nur
 *    dem `background-image` DES ELEMENTS; die beiden Blätter darunter hat sie nie erreicht.
 *
 *    Gemessen an einem echten Haus, das inzwischen ein Aufmacherfoto hat: über die volle Breite
 *    lag das Foto samt dunklem Schleier, rechts daneben stand zusätzlich Kontors eigener
 *    Einsatz — dasselbe Bild zweimal — und der Text darüber ist in diesem Aufbau DUNKEL
 *    (`--text`, für Papier gedacht). Dunkle Schrift auf einem abgedunkelten Foto: die
 *    Überschrift und der Einleitungssatz waren am Bildschirm kaum zu lesen.
 *
 * ⚠️ EXAKT DIE FEHLERFORM AUS D-91 — ein Bauteil in einem Aufbau gesehen und für fertig
 *    gehalten. Die Bewegung selbst bleibt: Kontor hat ihre eigene (Linien, Farbe, kein Heben),
 *    und die steht weiter unten in diesem Block.
 */
.layout-kontor .hero--foto.is-photo::before,
.layout-kontor .hero--foto.is-photo::after { display: none; }
.layout-kontor .hero--foto .hero__inner {
    display: grid; grid-template-columns: 1fr; gap: clamp(18px, 2.6vw, 28px);
    max-width: var(--wrap); align-items: start;
}
.layout-kontor .hero--foto .hero__title { color: var(--text); font-size: clamp(2rem, 4.6vw, 3.1rem); margin: 0; }
.layout-kontor .hero--foto .hero__intro { color: var(--muted); }
.layout-kontor .hero--foto .eyebrow,
.layout-kontor .hero--foto .accent-script { color: var(--gold-ink); }
.layout-kontor .hero--foto .btn--ghost { color: var(--text); border-color: var(--line); }

/* The photograph as an inset, drawn on the inner box so no template has to know which layout
   is on — the same reason the photo arrives as a custom property in the first place. */
.layout-kontor .hero--foto.is-photo .hero__inner::after {
    content: ""; display: block; width: 100%; aspect-ratio: 4 / 3;
    background-image: var(--hero-photo); background-size: cover; background-position: center;
    border: 1px solid var(--line); border-radius: var(--r-sm);
}

@media (min-width: 860px) {
    /*
     * POSITION, NOT A GRID SPAN — measured twice, and the second measurement is why.
     *
     * Letting the grid auto-flow filled row by row, so the restaurant's NAME landed in the
     * second column beside its own photograph. Placing the text in column 1 and giving the
     * photograph `grid-row: 1 / -1` is the pattern everyone writes, and it silently does
     * nothing here: with no `grid-template-rows`, `-1` resolves against the EXPLICIT grid,
     * which has one line. The span collapsed to row one, row one grew to the photograph's
     * height, and the opening band came out 815px tall. Nothing errored.
     */
    .layout-kontor .hero--foto.is-photo { min-height: 420px; display: flex; align-items: center; }
    .layout-kontor .hero--foto.is-photo .hero__inner {
        display: block; position: relative; width: 100%; padding-right: 44%;
    }
    .layout-kontor .hero--foto.is-photo .hero__inner > * { margin: 0 0 18px; }
    .layout-kontor .hero--foto.is-photo .hero__inner > :last-child { margin-bottom: 0; }
    .layout-kontor .hero--foto.is-photo .hero__inner::after {
        position: absolute; right: 0; top: 0; width: 40%; height: 100%; aspect-ratio: auto;
    }
}

/* THE PLAIN HERO — eight pages, and the largest type on each of them: Galerie, Catering,
   Kontakt, Ratgeber, Stadt, Über uns, Vegan and the 404. It had no Kontor treatment at all, so
   most of the site was a Kontor masthead on a Bühne page. */
.layout-kontor .hero {
    padding: clamp(30px, 4.5vw, 56px) 0;
    border-bottom: 1px solid var(--line);
}
.layout-kontor .hero__title { font-size: clamp(1.9rem, 4vw, 2.7rem); }
.layout-kontor .hero__inner { max-width: var(--wrap); }
.layout-kontor .hero__cta, .layout-kontor .hero__actions { margin-top: 18px; }

/* THE THREE SPECIALISED PHOTO HEROES. Kontor's thesis is «no full-bleed photograph», and these
   three survived it untouched: the Speisekarte's band, the Catering poster and the Galerie
   plate. They come onto paper too, with the photograph kept as a bordered figure.

   ⚠️ The scrims are NOT relaxed — `.cater-hero__scrim` and `.gal-hero__scrim` stay exactly as
   they are, because their alpha floors are the arithmetic that keeps white text legible over
   whatever a customer uploads. What changes is the frame around them, not the contrast inside. */
.layout-kontor .menu-hero {
    min-height: 0; padding: clamp(24px, 4vw, 44px) 0; background: var(--bg);
    border-bottom: 1px solid var(--line);
}
.layout-kontor .menu-hero .hero__title { color: var(--text); text-shadow: none; }
.layout-kontor .menu-hero .eyebrow,
.layout-kontor .menu-hero .accent-script { color: var(--gold-ink); text-shadow: none; }

.layout-kontor .cater-hero,
.layout-kontor .gal-hero { border-radius: var(--r-sm); overflow: hidden; }
.layout-kontor .cater-hero { margin-top: 22px; }
.layout-kontor .gal-hero { min-height: clamp(260px, 40vh, 400px); }

.layout-kontor .article-hero { padding: clamp(26px, 4vw, 48px) 0 clamp(18px, 2.5vw, 28px); border-bottom: 1px solid var(--line); }

/* The status word sits on paper here, not on a dark plate, so it takes the light-ground pair.
   Without this it inherits the on-dark tokens and prints near-white on cream — which is how
   the FIRST version of this block left it, because it did not carry these three rules at all. */
.layout-kontor .status--open { background: color-mix(in srgb, var(--enamel) 14%, transparent); color: var(--enamel); }
.layout-kontor .status--closed { background: var(--line-soft); color: var(--muted); }
.layout-kontor .status__note { color: var(--muted); }

/* -- Section rhythm -------------------------------------------------------- */
/* Hairlines instead of bands. The alternating paper/surface stripe is the strongest signature
   of the photographic layout, and removing it is most of what makes this read as a different
   design rather than the same one with the pictures turned off. */
.layout-kontor section { padding: clamp(34px, 5vw, 62px) 0; border-top: 1px solid var(--line-soft); }
.layout-kontor main > section:first-child { border-top: 0; }
.layout-kontor .section--surface { background: transparent; }
.layout-kontor .rule-ornament, .layout-kontor .rule-ornament--up { display: none; }

/* The section heading is set as a label down the left, the way a menu card names a course. */
@media (min-width: 860px) {
    .layout-kontor .section-head {
        display: grid; grid-template-columns: 200px 1fr; gap: 28px;
        align-items: start; max-width: none;
    }
    .layout-kontor .section-head > :first-child { grid-column: 1; }
    .layout-kontor .section-head > :first-child ~ * { grid-column: 2; }
}
.layout-kontor .section-head h2 { font-size: clamp(1.2rem, 2vw, 1.5rem); text-transform: uppercase; letter-spacing: .1em; }

/*
 * ⛔ UND OHNE DEN STRICH DER HAUSFARBE. Bühne hat ihn bekommen, weil dort sonst fast nichts die
 *    Farbe des Hauses trägt. Kontor zieht ohnehin über JEDEM Abschnitt eine Haarlinie
 *    (`.layout-kontor section { border-top }`); ein zweiter, farbiger Strich vier Zeilen
 *    darunter wären zwei Linien für dieselbe Trennung — und in einem Aufbau, dessen ganze
 *    Sprache Kanten und Haarlinien sind, ist die zweite die eine zu viel.
 *
 * ⚠️ AUSSERDEM WÜRDE ER DAS RASTER STÖREN. Der Abschnittskopf ist hier ein Zweispalter mit dem
 *    Label in der linken Spalte; ein `::before` am Kopf selbst stünde über beiden Spalten und
 *    damit über einer Fläche, die es gar nicht gibt.
 */
.layout-kontor .section-head::before { display: none; }

/* -- A dish ---------------------------------------------------------------- */
/* `.dish-grid`, NOT `.grid-3`/`.grid-2` — the first version targeted two class names that
   appear nowhere in this stylesheet and nowhere in any template, so every Kontor dish row was
   a ruled line laid out inside a four-column card grid. It looked like a bug because it was
   one, and it was invisible in the CSS: a selector that matches nothing is silent. */
.layout-kontor .dish-grid { display: block; }

.layout-kontor .dishcard {
    background: transparent; border: 0; border-bottom: 1px solid var(--line-soft);
    border-radius: 0; padding: 16px 0;
    display: grid; grid-template-columns: 64px 1fr auto; gap: 4px 16px; align-items: baseline;
}
.layout-kontor .dishcard:hover { transform: none; box-shadow: none; }
.layout-kontor .dishcard__photo { grid-row: 1 / span 4; grid-column: 1; width: 64px; height: 64px; margin: 0; border-radius: var(--r-sm); align-self: start; }
.layout-kontor .dishcard__accent { grid-column: 2; font-size: .7rem; text-transform: uppercase; letter-spacing: .12em; }
/*
 * ⛔ DER PREIS STAND AUF DER ZEILE DER BESCHREIBUNG, NICHT AUF DER DES NAMENS. `grid-row: 2`
 *    setzte ihn neben den Beschreibungstext; auf einer gedruckten Karte steht der Preis
 *    IMMER auf der Höhe des Gerichts, das er kostet. Am Bildschirm las sich die Liste dadurch
 *    verrutscht, ohne dass man sofort sagen konnte, warum.
 *
 * ⛔ UND DIE PUNKTLINIE FEHLTE, OBWOHL SIE VERSPROCHEN WAR. `App\Site\Layout` beschreibt diesen
 *    Aufbau wörtlich als «dishes as a ruled list with a dotted leader to the price» — die
 *    Beschreibung, die dem Wirt im Panel angezeigt wird. Zwischen Name und Preis lagen aber
 *    siebenhundert Pixel Nichts. Die Speisekarte macht es seit jeher richtig (`.mdish__lead`,
 *    2 px gepunktet); hier war dieselbe Geste nur nie gebaut worden.
 *
 * ⚠️ ALS `::after` UND NICHT ALS ELEMENT. Die vier Vorlagen, die eine Gerichtkarte schreiben,
 *    teilen sich beide Aufbauten — keine von ihnen soll wissen müssen, dass es diesen hier
 *    gibt. Dieselbe Begründung wie beim `:has()` eine Regel weiter unten.
 */
.layout-kontor .dishcard__name {
    grid-column: 2; grid-row: 1; margin: 0; font-size: 1.1rem;
    display: flex; align-items: baseline; gap: 12px;
}

.layout-kontor .dishcard__name::after {
    content: '';
    flex: 1 1 auto;
    min-width: 20px;
    border-bottom: 2px dotted var(--line);
    transform: translateY(-.25em);
}

.layout-kontor .dishcard__price { grid-column: 3; grid-row: 1; font-variant-numeric: tabular-nums; white-space: nowrap; }
.layout-kontor .dishcard__desc { grid-column: 2 / -1; margin: 4px 0 0; font-size: .88rem; }
/* ⚠️ `.codes` STEHT JETZT MIT IN DER AUSSCHLUSSLISTE, und das ist keine Kosmetik: fünf
      verkettete `:not()` machen diesen Selektor (0,7,0). Eine eigene Regel für die
      Allergenbuchstaben — auch eine mit vier Klassen — kommt dagegen nicht an, und der Kasten
      landete rechts unter dem Preis. Diese Regel IST die Liste «alles Übrige»; wer etwas
      eigenes bekommt, gehört hier ausgetragen, statt anderswo mehr Gewicht zu suchen. */
/* ⚠️ `.codes` STEHT NICHT MEHR IN DER AUSSCHLUSSLISTE, weil es kein direktes Kind mehr ist —
      die Buchstaben liegen jetzt in `.dishcard__tags`, und DAS fällt hier hinein und bekommt
      damit die Textspalte, was richtig ist. Die Regel weiter unten ändert nur die AUSRICHTUNG
      innerhalb dieser Spalte und streitet deshalb nicht mit den fünf `:not()` um dieselbe
      Eigenschaft. */
.layout-kontor .dishcard > :not(.dishcard__photo):not(.dishcard__accent):not(.dishcard__name):not(.dishcard__price):not(.dishcard__desc) { grid-column: 2 / -1; }

/* TWO PAGES RENDER A DISH CARD WITH NO PHOTOGRAPH — vegan.blade.php and stadt.blade.php never
   include `partials/dishthumb`. Without this the 64px column is an empty gutter on every row.
   `:has()` rather than a second class, because the templates are shared with the other layout
   and neither of them should have to know this layout exists. */
.layout-kontor .dishcard:not(:has(.dishcard__photo)) { grid-template-columns: 1fr auto; }
.layout-kontor .dishcard:not(:has(.dishcard__photo)) > * { grid-column: 1; }
.layout-kontor .dishcard:not(:has(.dishcard__photo)) .dishcard__price { grid-column: 2; }
.layout-kontor .dishcard:not(:has(.dishcard__photo)) .dishcard__desc { grid-column: 1 / -1; }

/*
 * ⛔ DIE ALLERGENBUCHSTABEN STANDEN RECHTS AUSSEN, UNTER DEM PREIS. Sie fallen in die
 *    Sammelregel `> :not(…)` und damit in die Spalten 2 bis Ende; solange `.codes` ein
 *    Blockelement war, füllte der Kasten diese Breite und man sah es nicht. Seit er nur noch
 *    so breit ist wie seine Buchstaben (die Begründung steht bei `.codes`), sitzt das
 *    Kürzelchen am rechten Rand der Zeile — weit weg von dem Gericht, zu dem es gehört.
 *    Es gehört unter die Beschreibung, links, wo es gelesen wird.
 */
/*
 * ⛔ HIER STANDEN ZWEI REGELN AUF `> .codes`, UND SIE TREFFEN NICHTS MEHR. Die Buchstaben sind
 *    kein direktes Kind der Karte mehr: sie liegen zusammen mit «halal» und «vegan» in
 *    `.dishcard__tags`, weil drei einzelne Absätze in einer 230-px-Spalte drei Umbrüche für
 *    Angaben von je vier Zeichen waren. Ein Selektor, der nichts trifft, ist stumm — und diese
 *    Datei hat schon einmal einen ganzen Kontor-Block an genau dieser Form verloren
 *    (`.grid-3`/`.grid-2`, die es nie gab).
 *
 * ⚠️ DIE ZEILE GEHT NACH LINKS, und das ist der eigentliche Befund. Die Grundregel setzt sie
 *    rechtsbündig — auf Bühnes schmaler Karte steht sie damit dem Preis gegenüber, was dort
 *    richtig ist. In Kontors voller Zeilenbreite landete sie dadurch ganz aussen unter dem
 *    Preis, weit weg von dem Gericht, zu dem sie gehört. Sie gehört unter die Beschreibung.
 */
.layout-kontor .dishcard__tags { justify-self: start; justify-content: flex-start; }

/*
 * ⛔ UND DAS BEIWORT ALS KONTUR IST EINE GESTE DER BÜHNE, NICHT DIESES AUFBAUS. Es kam mit der
 *    fotolosen Platte dorthin und erschien hier ungefragt mit: eine ausgeschriebene Kontur in
 *    einem Sandton, der für einen dunklen Grund gemischt ist, quer über Kontors helles Papier.
 *    Kontor sagt über sich «der Name mittig über einer Linie wie auf einer gedruckten Karte»;
 *    ein Plakatbuchstabe dahinter widerspricht dem Satz.
 *
 * ⚠️ NUR IM AUFMACHER. Die 404 benutzt dasselbe Bauteil für ihre riesige «404», und das ist
 *    dort in beiden Aufbauten der Witz, der die Sackgasse gestaltet aussehen lässt.
 */
.layout-kontor .hero--foto .hero__accent-watermark { display: none; }

/* ── Die Bilderleiste im Kontor ───────────────────────────────────────────────────────────
 *
 * ⚠️ DIESELBE LEISTE, ANDERE GRAMMATIK. Die Fahrt, das Einrasten und die grosse Mitte gehören
 *    dem Bauteil und nicht einem Aufbau — was hier wechselt, ist die Sprache der Kanten. Dieser
 *    Block sagt über seine Knöpfe «square, and they do not lift»; eine Fotografie mit
 *    Schlagschatten und 16 px Rundung daneben wäre der Widerspruch, den D-91 auf der
 *    Reservierungsseite schon einmal gekostet hat. Kein Skript, kein Markup weiss davon.
 */
.layout-kontor .bleiste__figur { border-radius: 2px; box-shadow: none; border-color: var(--line); }
.layout-kontor .bleiste__pfeil { border-radius: 2px; box-shadow: none; }
.layout-kontor .bleiste__balken { border-radius: 0; height: 3px; }
.layout-kontor .bleiste__lauf { border-radius: 0; }

/* -- Controls -------------------------------------------------------------- */
/* Square, and they do not lift. The 999px pill and the hover translate are the friendly-SaaS
   vocabulary the photographic layout wears well; a printed card wants an edge.
   The touch-target block is untouched: it sets minimums, and none of these remove one. */
.layout-kontor .btn { border-radius: 2px; letter-spacing: .04em; }
.layout-kontor .btn:hover { transform: none; }
.layout-kontor .btn--ghost { border-width: 1px; }

/* -- Running text ---------------------------------------------------------- */
/* `.article__body` IS the page on Impressum, Datenschutz, a Ratgeber article and the invitation
   — four pages that had not one Kontor rule below the masthead. */
.layout-kontor .article__body h1,
.layout-kontor .article__body h2 { letter-spacing: .01em; }
.layout-kontor .article__body h2 { border-top: 1px solid var(--line-soft); padding-top: .8em; }
.layout-kontor .article__body > p { max-width: 66ch; }
.layout-kontor .breadcrumb { letter-spacing: .1em; text-transform: uppercase; font-size: .68rem; }

/* -- The week -------------------------------------------------------------- */
/* Six pages plus every footer. Already the component nearest this layout's idiom; it only
   needs the rule to be a rule rather than a soft divider. */
.layout-kontor .hours-list li { border-bottom-color: var(--line); }
.layout-kontor .hours-list b { letter-spacing: .04em; }

/* -- Ratgeber cards -------------------------------------------------------- */
/* The whole body of one page, and the card idiom this layout rejected for dishes. */
.layout-kontor .rg-card {
    background: transparent; border: 0; border-top: 1px solid var(--line);
    border-radius: 0; padding: 22px 0;
}
.layout-kontor .rg-card:hover { transform: none; box-shadow: none; }
.layout-kontor .rg-card__cat { color: var(--gold-ink); }

/* -- Day one --------------------------------------------------------------- */
/* What a brand-new restaurant sees on six pages. */
.layout-kontor .m-empty { border-top: 1px solid var(--line-soft); border-bottom: 1px solid var(--line-soft); padding: 28px 0; }

/* -- Das Hinweisband ------------------------------------------------------- */
/* Kontor kennt keine gefüllten Flächen. Das Band wird deshalb flach: eine Zeile Text über
   einer Haarlinie, wie eine Kopfzeile im Satz.

   ⚠️ DIE KINDER MÜSSEN MIT. `.hinweis-band__satz` steht im Grundentwurf auf `--on-dark-soft`,
      weil das Band dort tintenschwarz ist. Ohne diese Zeile wäre die Rückkehrzeile hier
      cremeweiss auf Papier — unsichtbar, und zwar genau die Zeile, die dem Gast sagt, wann er
      wiederkommen kann. Gemessen wurde das nicht: es ist derselbe Fehler, den `.layout-kontor
      .site-footer` neun Kindern weiter unten bereits einmal hatte. */
.layout-kontor .hinweis-band {
    background: transparent; color: var(--text);
    border-bottom: 1px solid var(--line); letter-spacing: .02em;
}
.layout-kontor .hinweis-band__satz { color: var(--muted); }
.layout-kontor .hinweis-band a { color: var(--text); text-decoration: underline; }

/* -- Footer ---------------------------------------------------------------- */
/* Paper, not near-black — and the NINE CHILDREN that were still painted for the dark ground.
   The first version flipped the background and left them, so the restaurant's own name, every
   column heading, every day of the week and the studio wordmark were white on cream. The
   reasoning was already written one rule away, about `--footer-open`; it was applied to one
   selector out of ten. */
.layout-kontor .site-footer {
    background: var(--bg); color: var(--text);
    border-top: 3px double var(--line); margin-top: 0;
}
.layout-kontor .site-footer a { color: var(--text); }
.layout-kontor .site-footer a:hover { color: var(--gold-ink); }
.layout-kontor .footer-brand__name { color: var(--text); }
.layout-kontor .footer-brand .accent-script { color: var(--gold-ink); }
.layout-kontor .footer-h { color: var(--gold-ink); }
.layout-kontor .footer-kontakt li { color: var(--muted); }
.layout-kontor .footer-kontakt svg { color: var(--gold-ink); }
.layout-kontor .footer-kontakt a { color: var(--text); }
.layout-kontor .footer-hours b { color: var(--text); }
.layout-kontor .footer-hours span { color: var(--muted); }
.layout-kontor .footer-hours li { border-bottom-color: var(--line-soft); }
.layout-kontor .site-footer p,
.layout-kontor .footer-note { color: var(--muted); }
.layout-kontor .footer-social a { border-color: var(--line); }
.layout-kontor .footer-social a:hover { background: var(--line-soft); }
.layout-kontor .footer-bottom { border-top-color: var(--line-soft); }
.layout-kontor .footer-legal a { color: var(--text); }
.layout-kontor .footer-legal__jahr { color: var(--text); }

/* Zustandszeile und heutiger Tag auf Papier: `--footer-open` ist für den dunklen Grund gemischt
   und misst auf Papier 2,4:1 — der Punkt nimmt das Arbeitsgrün, die Zeile die Textfarbe.
   `.site-footer .footer-status` und nicht `.footer-status`: die Regel für `.site-footer p` sechs
   Zeilen weiter oben ist spezifischer als eine einzelne Klasse und färbte die Zeile sonst grau. */
.layout-kontor .site-footer .footer-status { color: var(--text); }
.layout-kontor .footer-status__zeit { color: var(--muted); }
.layout-kontor .footer-status__punkt { background: var(--muted); }
.layout-kontor .footer-status__punkt--offen { background: var(--enamel); box-shadow: 0 0 0 4px color-mix(in srgb, var(--enamel) 18%, transparent); }
.layout-kontor .footer-hours li.hours-heute b,
.layout-kontor .footer-hours li.hours-heute span { color: var(--text); }
.layout-kontor .footer-hours li.hours-heute b::before { background: var(--gold-ink); }

/* THE STUDIO WORDMARK IS A WHITE IMAGE. signature.blade.php says so in as many words: the
   file ships sized for the dark footer this design was built around, and «without it the mark
   disappears». On paper it has to be the dark file — and the template already renders both,
   so this is a visibility swap rather than a new asset. */
.layout-kontor .signature__mark { filter: invert(1); opacity: .75; }

/* -- Phone ----------------------------------------------------------------- */
@media (max-width: 560px) {
    /* Measured at 375px: the three-column line squeezed the dish NAME into 132px while the
       price column took 103px for four characters. The price goes under the name, right
       aligned — the shape a printed card takes when the column runs out. */
    .layout-kontor .dishcard { grid-template-columns: 56px 1fr; }
    .layout-kontor .dishcard__photo { width: 56px; height: 56px; }
    .layout-kontor .dishcard__accent,
    .layout-kontor .dishcard__name { grid-column: 2; }
    .layout-kontor .dishcard__price { grid-column: 2; grid-row: auto; justify-self: end; }

    /* Der Preis steht hier UNTER dem Namen, also führt die Punktlinie zu nichts mehr. */
    .layout-kontor .dishcard__name::after { display: none; }
    .layout-kontor .dishcard:not(:has(.dishcard__photo)) { grid-template-columns: 1fr; }
    .layout-kontor .dishcard:not(:has(.dishcard__photo)) .dishcard__price { grid-column: 1; justify-self: end; }
}

/* -- Gut zu wissen, im Kontor ----------------------------------------------
   Kontor führt mit der Schrift und stellt eine Angabe auf einer geführten Linie, nicht auf einer
   Karte. Also kommt die Karte wieder ab — keine Fläche, kein Kasten, kein Schatten, kein Feld
   hinter dem Zeichen — und jede Angabe wird eine Zeile, von der nächsten durch eine Haarlinie
   getrennt. Die «Ja»/«gedämpft»-Tinte aus dem Grundblatt bleibt unangetastet: dass Negatives
   weder grün noch rot ist, ist eine Regel über BEDEUTUNG und ändert sich mit dem Layout nicht.

   ⚠️ DIE AUSNAHMEN WERDEN AN DEN TOKENS GEMACHT, NICHT AN DEN REGELN. `--info-*` am Block
      gesetzt heisst: die Regeln oben bleiben die einzige Stelle, an der `.facts__icon` gebaut
      wird, und Kontor sagt nur, mit welchen Werten. Vorher stand hier `background: none;
      border-radius: 0` als Gegenregel — zwei Orte, an denen dasselbe Feld entsteht, und der
      zweite vergisst irgendwann, was der erste inzwischen tut. Ohne Fläche zählt nur noch das
      Zeichen, also wird der GLYPH gross und der Kasten schrumpft auf ihn zusammen. */
.layout-kontor .facts {
    border-top: 1px solid var(--line-soft);
    --info-icon-background: transparent;
    --info-icon-radius: 0;
    --info-icon-size: 26px;
    --info-icon-glyph: 26px;
}
.layout-kontor .facts__list { gap: 0; grid-template-columns: repeat(auto-fit, minmax(min(100%, 16.25rem), 1fr)); }
.layout-kontor .facts__item {
    background: none; border: 0; border-radius: 0; box-shadow: none;
    border-bottom: 1px solid var(--line-soft); padding: var(--s-3) 0;
}
.layout-kontor .facts__h { letter-spacing: .1em; border-bottom: 0; padding-bottom: 0; }
/* Ohne Fläche gibt es nichts zu verdichten — der Zeiger fasst im Kontor nur die Zeile an. */
@media (hover: hover) {
    .layout-kontor .facts__item:hover { border-color: var(--line-soft); }
    .layout-kontor .facts__item:hover .facts__icon,
    .layout-kontor .facts__item--no:hover .facts__icon { background: transparent; }
}

/* -- Warenkorb, im Kontor -------------------------------------------------- */
.layout-kontor .has-cart .mdish__add,
.has-cart .layout-kontor .mdish__add { border-radius: 2px; }
.layout-kontor .cartbar { background: var(--bg); backdrop-filter: none; border-top: 2px solid var(--line); }

/* -- Im Kontor -------------------------------------------------------------- */
/* Kontor führt mit der Schrift. Ein bildfüllendes Band widerspricht dem, also
   wird es dort zu einem ruhigen Satzband auf der Fläche — die Wörter bleiben,
   das Foto tritt ab. EIN Markup-Baum, der Aufbau ist eine Klasse am <body>. */
.layout-kontor .band-photo {
    min-height: 0; background: var(--surface); color: var(--text);
    padding: var(--s-8) 0;
    border-top: 1px solid var(--line-soft); border-bottom: 1px solid var(--line-soft);
}
.layout-kontor .band-photo__img, .layout-kontor .band-photo__scrim { display: none; }
.layout-kontor .band-photo__title { color: var(--text); text-shadow: none; }
.layout-kontor .band-photo__text, .layout-kontor .band-photo__quote { color: var(--muted); text-shadow: none; }
.layout-kontor .band-photo .eyebrow { color: var(--gold-ink); text-shadow: none; }
.layout-kontor .band-photo .btn--ghost { color: var(--text); border-color: var(--line); }
.layout-kontor .map-card { border-radius: 2px; box-shadow: none; }

/* ── Bewegung im Kontor: die Linie antwortet, nicht die Höhe ───────────────────────────────
 *
 * ⛔ HIER HEBT SICH NICHTS. Dieser Aufbau sagt über seine Knöpfe «square, and they do not
 *    lift», und eine Gerichtzeile, die sich beim Zeigen anhebt, widerspräche demselben Satz.
 *    Eine gedruckte Karte bewegt sich nicht — was sie kann, ist die Linie zum Preis deutlicher
 *    ziehen und den Preis selbst kräftiger setzen. Dieselbe Rückmeldung, andere Sprache.
 *
 * ⚠️ AUCH DAS ANKOMMEN IST LEISER: halb so weit wie auf der Bühne. Ein Satzband, das
 *    fünfzehn Pixel weit hereinfährt, sieht aus wie eine Folie; sechs sehen aus, als hätte
 *    die Seite Luft geholt.
 */
.layout-kontor.js-motion .section-head,
.layout-kontor.js-motion .dishcard,
.layout-kontor.js-motion .band-photo,
.layout-kontor.js-motion .facts__item,
.layout-kontor.js-motion .rg-card,
.layout-kontor.js-motion .split__media,
.layout-kontor.js-motion .split__text { transform: translate3d(0, 6px, 0); }

.layout-kontor .dishcard { overflow: visible; }

/*
 * ⛔ KEINE ZUFAHRT AUF DAS FOTO. Der andere Aufbau lässt sein Bild über die volle Breite atmen;
 *    dieser trägt es als gerahmten Einsatz und führt mit der Schrift. Ein wanderndes Vollbild
 *    wäre hier nicht «auch schön», sondern der Widerspruch zur eigenen These — deshalb werden
 *    beide Blätter des Aufmachers hier ausdrücklich abbestellt.
 */
.layout-kontor.js-motion .hero--foto.is-photo::before,
.layout-kontor.js-motion .hero--foto.is-photo::after { content: none; }

/*
 * ⚠️ STATTDESSEN ZIEHT SICH DIE PUNKTLINIE. Auf einer gedruckten Karte führt sie das Auge vom
 *    Gericht zum Preis; sie beim Hereinkommen von links nach rechts zu ziehen, ist genau die
 *    Bewegung, die ein Setzer mit dem Finger macht. Nichts hebt sich, nichts blendet auf.
 */
.layout-kontor.js-motion .dishcard .dishcard__name::after {
    transform: translateY(-.25em) scaleX(0);
    transform-origin: left center;
    transition: transform .75s cubic-bezier(.16, .84, .44, 1) .12s, border-color var(--t);
}

.layout-kontor.js-motion .dishcard.is-in .dishcard__name::after {
    transform: translateY(-.25em) scaleX(1);
}

.layout-kontor .dishcard__name::after { transition: border-color var(--t); }
.layout-kontor .dishcard__price { transition: color var(--t); }

@media (hover: hover) {
    .layout-kontor .dishcard:hover .dishcard__name::after { border-bottom-color: var(--gold-ink); }
    .layout-kontor .dishcard:hover .dishcard__price { color: var(--accent-strong); }
    .layout-kontor .dishcard:hover .dishcard__photo { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
    .layout-kontor.js-motion .section-head,
    .layout-kontor.js-motion .dishcard,
    .layout-kontor.js-motion .band-photo,
    .layout-kontor.js-motion .facts__item,
    .layout-kontor.js-motion .rg-card,
    .layout-kontor.js-motion .split__media,
    .layout-kontor.js-motion .split__text { transform: none; }

    .layout-kontor .dishcard__name::after,
    .layout-kontor .dishcard__price { transition: none; }

    .layout-kontor.js-motion .dishcard .dishcard__name::after {
        transform: translateY(-.25em) scaleX(1);
        transition: none;
    }
}

/* ── Die Reservierungsseite im Kontor ──────────────────────────────────────────────────────
 *
 * ⛔ SIE WAR HIER KAPUTT, UND ZWAR VON MIR. Die Karte des Bogens wird auf Bühne mit einem
 *    negativen Aussenabstand über die Kante des dunklen Aufmachers gezogen — das ist die
 *    Bewegung, die «hier ist die Sache» sagt. Kontors Bogen ist aber flach, hell und trägt sein
 *    Foto als Einsatz in der rechten Spalte: dieselben 84 Pixel zogen die Karte ÜBER den Text.
 *    Zwei der drei Zusagen lagen halb darunter, die dritte ganz, und «So läuft es» wurde vom
 *    Foto durchgeschnitten. Am Bildschirm gesehen, im Quelltext unsichtbar.
 *
 * ⚠️ UND DIE KARTE LEGT SICH HIER NICHT AUF. Ein schwebender Kasten mit Schlagschatten ist eine
 *    Geste der Bühne; Kontor setzt Kanten und Haarlinien. Dieselbe Karte, andere Sprache.
 */
.layout-kontor .resv__grid { margin-top: clamp(20px, 3vw, 34px); }
.layout-kontor .resv-hero__zusagen svg { color: var(--gold-ink); }
.layout-kontor .resv__karte { box-shadow: none; border-radius: 2px; }
.layout-kontor .resv__box,
.layout-kontor .resv__anruf,
.layout-kontor .resv__block--wahl { border-radius: 2px; }
/* Die Zeit- und Personenknöpfe tragen dieselbe Kante wie alles andere in diesem Layout — ein
   abgerundeter Knopf zwischen lauter rechten Winkeln fällt als Fremdkörper auf. */
.layout-kontor .resform__zeit span,
.layout-kontor .resform__zahl span { border-radius: 2px; }

/* ── Die Bewertungsbitte im Kontor ─────────────────────────────────────────────────────────
 *
 * ⚠️ KEINE KARTE. Ein aufgelegter Kasten mit Rundung und Schlagschatten ist die Geste der
 *    Bühne; Kontor trennt mit Haarlinien, und eine davon zieht der Abschnitt selbst schon
 *    (`.layout-kontor section { border-top }`). Ein Rahmen darin wäre die zweite Linie im
 *    Abstand von vier Zentimetern — dieselbe Doppelung, die weiter oben schon einmal für
 *    `.resv__karte` aufgelöst wurde, mit derselben Begründung.
 *
 * ⚠️ DIE HANDLUNG STEHT RECHTS, und das ist kein anderer Geschmack, sondern dieselbe Flucht
 *    wie bei den Preisen auf der Karte: im Kontor läuft das Auge von der Zeile nach rechts
 *    zu dem, was zu tun ist. Nur ab 821px — darunter stapeln beide Aufbauten gleich, und ein
 *    rechtsbündiger Knopf über die volle Telefonbreite wäre schlicht ein verrutschter.
 */
.layout-kontor .gbew__karte {
    background: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
}

.layout-kontor .gbew__zeichen svg { width: clamp(30px, 3vw, 38px); height: clamp(30px, 3vw, 38px); }
.layout-kontor .gbew__titel { font-size: clamp(1.2rem, 2.2vw, 1.6rem); }

@media (min-width: 821px) {
    .layout-kontor .gbew__tat { text-align: right; }
    .layout-kontor .gbew__sterne { justify-content: flex-end; }
}
