/**
 * Design tokens + base styles for the "bistro-francais" public website template.
 * Values taken 1:1 from the extracted "Le Flâneur" design.
 * This file is loaded standalone by the public template layout — it does not
 * depend on the Tailwind build and must remain self-contained.
 */

/* ── Design-Tokens, Stand v1.7 ─────────────────────────────────────────────
   Aus dem Entwurf übernommen, nicht abgetippt. Gegenüber v1 sind der
   Typo-Maßstab und die Abstände großzügiger (--text-3xl/4xl fließend,
   --space-7..9 größer) — das ist die „Leichtigkeit", die das Konzept meint,
   und sie wirkt deshalb auch auf Coming-Soon- und Pflichtseiten.
   Die @font-face-Regeln stehen in the-new-elegance.css. */

/* Le Flâneur — global entry. @import lines only. */
/* Le Flâneur — colors.
   Base palette is the three swatches from the concept ("Corporate Design Farben"),
   exact fills sampled from the PDF: #D7D1C6, #7A8B99, #66686b.
   Paper + ink are derived from the "Stilelemente" ink illustrations (cream stock, dark ink). */
:root {
  /* base */
  --greige: #d7d1c6;        /* warm greige — primary surface tone */
  --greige-light: #e6e2da;
  --greige-dark: #c3bbac;
  --slate: #7a8b99;         /* slate blue — interactive / accent */
  --slate-light: #a3b0ba;
  --slate-dark: #5d6e7c;
  --slate-deep: #46545f;
  --anthracite: #66686b;    /* anthracite — secondary text, quiet UI */
  --paper: #f6f1e5;         /* cream paper (derived from illustrations) */
  --paper-warm: #efe8d8;
  --ink: #26241f;           /* near-black warm ink (derived) */
  --white: #ffffff;

  /* semantic */
  --surface-page: var(--paper);
  --surface-card: var(--white);
  --surface-tinted: var(--greige-light);
  --surface-inverse: var(--ink);
  --text-heading: var(--ink);
  --text-body: #3c3a34;
  --text-muted: var(--anthracite);
  --text-on-dark: var(--paper);
  --text-accent: var(--slate-dark);
  --interactive: var(--slate-dark);
  --interactive-hover: var(--slate-deep);
  --border-hairline: rgba(38, 36, 31, 0.16);
  --border-strong: rgba(38, 36, 31, 0.4);
  --focus-ring: var(--slate);

  /* semantic status — quiet, derived from palette (no loud red/green in concept) */
  --status-ok: #6d7d5c;
  --status-warn: #a98649;
  --status-error: #a05a4d;
}

/* Le Flâneur — typography.
   Mood from concept: elegant, widely letterspaced serif wordmarks in caps,
   quiet small sub-lines. Display = The New Elegance (brand font, licensed),
   body/UI = Jost (substitute). */
:root {
  --font-serif-display: "The New Elegance", "Cormorant Garamond", "Times New Roman", serif;
  --font-serif-condensed: "The New Elegance Condensed", "The New Elegance", serif;
  --font-elegance-sans: "The New Elegance Sans", "Jost", sans-serif;
  --font-sans-body: "Jost", "Century Gothic", "Futura", sans-serif;

  /* scale — Untergrenzen nach BFSG/EN 301 549 (→ WCAG 2.1 AA): Fließtext 16 px,
     kleinste Auszeichnung 14 px. Darunter gibt es im Design keine Stufe mehr,
     damit keine Aufrufstelle versehentlich unter die Grenze fällt. */
  --text-xs: 14px;
  --text-sm: 16px;
  --text-base: 16px;
  --text-lg: 19px;
  --text-xl: 24px;
  --text-2xl: 32px;
  --text-3xl: clamp(34px, 3.6vw, 44px);
  --text-4xl: clamp(46px, 5vw, 64px);
  --text-5xl: 88px;

  /* semantic type */
  --type-wordmark: 400 var(--text-4xl)/1.12 var(--font-serif-display);
  --type-display: 400 var(--text-3xl)/1.2 var(--font-serif-display);
  --type-title: 500 var(--text-xl)/1.3 var(--font-serif-display);
  --type-eyebrow: 400 var(--text-xs)/1.4 var(--font-sans-body);
  /* Marginalie: löst den gesperrten Versalien-Eyebrow ab. Serif kursiv, gemischt
     geschrieben, normale Laufweite — die Stimme einer Randnotiz, nicht eines Labels. */
  --type-marginal: italic 400 var(--text-base)/1.45 var(--font-serif-display);
  --type-marginal-sm: italic 400 var(--text-sm)/1.45 var(--font-serif-display);
  --type-body: 400 var(--text-base)/1.65 var(--font-sans-body);
  --type-body-serif: 400 var(--text-lg)/1.6 var(--font-serif-display);
  /* Bildunterschriften und Kleingedrucktes: 16 px in Normalstärke. Die vorherige
     Stufe (14 px, Strichstärke 300) lag unter der Grenze und war zu leicht. */
  --type-caption: 400 var(--text-sm)/1.6 var(--font-sans-body);

  /* tracking — the letterspaced caps are THE brand type gesture */
  --tracking-wordmark: 0.28em;
  --tracking-eyebrow: 0.24em;
  --tracking-caps: 0.14em;
  --tracking-body: 0.01em;

  /* The New Elegance sets a very narrow word space, in the italic almost none —
     without this, "auf saarländische" runs together. Applied wherever the
     display serif carries running text. */
  --word-space-serif: 0.06em;
  --word-space-serif-italic: 0.22em;
}

/* Le Flâneur — spacing. Generous whitespace is part of "Leichtigkeit". */
:root {
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;
  --space-9: 96px;
  --space-10: 144px;

  /* Abstand einer Marginalie zur Überschrift darunter: -gap-eyebrow vor
     Display-Titeln, -tight vor Karten- und Listentiteln. Die Namen sind
     geblieben, die Zeile darüber ist seit v2.4 eine Randnotiz. */
  --gap-eyebrow: clamp(20px, 2.2vw, 28px);
  --gap-eyebrow-tight: var(--space-4);

  --measure-prose: 62ch;
  --page-max: 1200px;
  --page-gutter: var(--space-6);
}

/* Le Flâneur — borders, radii, shadows, motion.
   Print-like elegance: hairline rules, near-square corners, shadows almost absent. */
:root {
  --radius-none: 0px;
  --radius-xs: 2px;      /* default for controls */
  --radius-sm: 4px;      /* cards, inputs max */
  --radius-full: 999px;  /* only tags/badges */

  --rule-hairline: 1px solid var(--border-hairline);
  --rule-accent: 2px solid var(--greige-dark);

  --shadow-card: 0 1px 2px rgba(38, 36, 31, 0.06);
  --shadow-overlay: 0 16px 48px rgba(38, 36, 31, 0.22);

  --ease-out: cubic-bezier(0.25, 0.6, 0.3, 1); /* @kind other */
  --duration-fast: 150ms; /* @kind other */
  --duration-base: 250ms; /* @kind other */
  --duration-slow: 450ms; /* @kind other */
}

@keyframes flaneurNudge { 0%, 100% { transform: translateY(0) } 50% { transform: translateY(5px) } }

/* Handgezeichnetes Herz als Scroll-Hinweis: Linien werden nacheinander "gezogen". */
@keyframes flaneurDraw { 0% { stroke-dashoffset: 100 } 34% { stroke-dashoffset: 0 } 82% { stroke-dashoffset: 0; opacity: 1 } 96%, 100% { stroke-dashoffset: 0; opacity: 0 } }

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

body {
  margin: 0;
  background: var(--surface-page);
  color: var(--text-body);
  font-family: var(--font-sans-body);
  letter-spacing: var(--tracking-body);
  font-size: var(--text-base);
  /* 1.65 wie `--type-body` — die Vorlage setzt am body dieselbe Kurzform. */
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--text-accent);
  text-decoration: none;
  transition: color var(--duration-fast) var(--ease-out);
}

a:hover {
  color: var(--interactive-hover);
  text-decoration: underline;
  text-underline-offset: 3px;
}

::selection {
  background: var(--greige);
  color: var(--ink);
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

/* Eine Zeichnung über einer Seitenüberschrift steht im Entwurf inline, auf
   einer Textzeile: deren Unterlänge gehört zum Abstand nach unten. */
.page-ink {
  display: inline;
}

[x-cloak] {
  display: none !important;
}

/* Der Display-Schnitt setzt eine sehr schmale Wortlücke, in der Kursiven fast
   keine — ohne die Korrektur läuft „auf saarländische" zusammen. Überschriften
   werden nicht getrennt; `break-word` bleibt die letzte Sicherung. */
h1,
h2,
h3,
h4 {
  font-family: var(--font-serif-display);
  color: var(--text-heading);
  margin: 0;
  font-weight: 400;
  word-spacing: var(--word-space-serif);
  overflow-wrap: break-word;
  hyphens: manual;
}

i,
em,
[lang] i {
  word-spacing: var(--word-space-serif-italic);
}

/* -------------------------------------------------------------------- */
/* Layout helpers                                                        */
/* -------------------------------------------------------------------- */

/* `--page-max` ist die Breite des Inhalts, nicht die des Kastens: der Rand
   liegt ausserhalb davon. Die Vorlage rechnet so (sie kennt keinen
   border-box-Reset), und ohne `content-box` waere jede Seite 2 × 32 px
   schmaler als der Entwurf — am deutlichsten sichtbar in der Kopfzeile. */
.page-container {
  box-sizing: content-box;
  max-width: var(--page-max);
  margin: 0 auto;
  padding-left: var(--page-gutter);
  padding-right: var(--page-gutter);
}

.page-container--narrow {
  max-width: 760px;
}

.page-container--prose {
  max-width: 860px;
}

/* Die schmalste Spalte des Entwurfs: Vormerkung und Schlussnote auf „Grand
   Flanier", der Abschluss des Flanierwegs. */
.page-container--tight {
  max-width: 640px;
}

/* Die Feier-Einladung unter den Bereichen steht eine Spur breiter als der
   Seitenkopf — 720 px wie im Entwurf. */
.page-container--mid {
  max-width: 720px;
}

/* Die Speisekarte ist seit v2.4 breiter als die übrigen Textspalten: 960 px
   statt 860. Die Weinkarte trägt lange Lagennamen, die sonst umbrechen. */
.page-container--carte {
  max-width: 960px;
}

.page-section {
  padding: var(--space-8) var(--page-gutter);
}

.page-section--lg {
  padding: var(--space-9) var(--page-gutter);
}

.page-section--card {
  background: var(--surface-card);
}

.page-section--tinted {
  background: var(--surface-tinted);
}

.page-section--inverse {
  background: var(--surface-inverse);
  color: var(--text-on-dark);
}

.page-section--center {
  text-align: center;
}

.page-section--hairline-top {
  border-top: var(--rule-hairline);
}

/* -------------------------------------------------------------------- */
/* Typography utilities                                                   */
/* -------------------------------------------------------------------- */

/* Die Marginalie hat seit v2.4 den gesperrten Versalien-Eyebrow abgelöst: eine
   Randnotiz in kursiver Serif, gemischt geschrieben, in normaler Laufweite.
   Wo der Entwurf die Zeile ganz gestrichen hat — über Seitenüberschriften, an
   den Stationen des Wegs — steht sie gar nicht mehr; geblieben ist sie dort,
   wo sie eine Spalte benennt (Fußzeile, Angabenliste, Buchungsfenster). */
.marginal {
  font: var(--type-marginal);
  word-spacing: var(--word-space-serif-italic);
  color: var(--text-muted);
}

.marginal--on-dark {
  color: var(--greige);
}

/* Die gesperrte Versalienzeile gibt es nur noch im Dialog des Design-Systems:
   `components/core/Dialog.jsx` ist mit v2.4 unverändert geblieben. Auf den
   Seiten der Website steht sie nirgends mehr. */
.eyebrow {
  font: var(--type-eyebrow);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
}

.wordmark {
  font-family: var(--font-serif-display);
  letter-spacing: var(--tracking-wordmark);
  text-transform: uppercase;
  color: var(--text-heading);
  padding-left: 0.28em;
  white-space: nowrap;
}

/* Die Display-Überschrift des Entwurfs (`--type-display`) steht gemischt und
   ungesperrt: gesperrte Versalien sind dort ausschliesslich die Sache der
   Eyebrows und der Wortmarke. Als Versalien gesetzt war jede Seitenüberschrift
   eine andere Type als in der Vorlage. */
.heading-display {
  font: var(--type-display);
  color: var(--text-heading);
}

.heading-display--hero {
  font-size: var(--text-5xl);
}

/* Seit v1.9 tragen alle Unterseiten dieselbe Display-Überschrift: weder eine
   Stufe grösser (`--text-4xl`) noch als gesperrte Wortmarke. Die beiden
   Sonderfassungen sind damit weg. */

.heading-display--on-dark {
  color: var(--text-on-dark);
}

.heading-title {
  font: var(--type-title);
  color: var(--text-heading);
}

.body-serif-italic {
  font: var(--type-body-serif);
  font-style: italic;
  word-spacing: var(--word-space-serif-italic);
  color: var(--text-muted);
}

.text-caption {
  font: var(--type-caption);
  color: var(--text-muted);
}

.text-muted {
  color: var(--text-muted);
}

.measure-prose {
  max-width: var(--measure-prose);
  margin-left: auto;
  margin-right: auto;
}

.section-heading {
  margin-bottom: var(--space-6);
}

/* Einleitungsabsatz unter einer zentrierten Ueberschrift. Er rueckt leicht
   nach oben: die Grundlinie der Ueberschrift laesst sonst eine Luecke, die
   groesser wirkt als der Abstand darunter. */
.section-lead {
  font: var(--type-body);
  color: var(--text-muted);
  max-width: 54ch;
  margin: -8px auto 0;
}

.section-heading--center {
  text-align: center;
}

/* Der 48-px-Akzentstrich unter den Überschriften ist mit v2.4 entfallen: er
   behauptete eine Verfeinerung, die die Type selbst trägt. Mit ihm ging der
   gesperrte Eyebrow darüber — die Überschrift trägt den Abschnitt allein. */

/* -------------------------------------------------------------------- */
/* Nav                                                                    */
/* -------------------------------------------------------------------- */

.site-header {
  background: var(--surface-page);
  border-bottom: var(--rule-hairline);
  position: sticky;
  top: 0;
  z-index: 20;
}

.site-header__inner {
  box-sizing: content-box;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 18px var(--page-gutter);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
}

/* Die Wortmarke ist eine Zeichnung, kein Schriftzug: sie wird ueber die
   Breite skaliert, die Hoehe folgt. Eine feste Hoehe wuerde sie stauchen. */
.site-header__brand { display: block; line-height: 0; }
.site-header__brand img { display: block; width: 168px; height: auto; }

.site-nav {
  display: flex;
  gap: var(--space-6);
  align-items: center;
}

/* Seit v2.4 steht die Kopfzeile gemischt und ungesperrt (15 px Sans), und der
   aktive Punkt trägt keinen Unterstrich mehr: er wird durch die Tinte
   ausgezeichnet, nicht durch einen Strich. `line-height: normal` statt der
   geerbten 1.65, weil die Vorlage die Schrift als Kurzform setzt und damit
   auch die Zeilenhöhe zurücknimmt. */
.site-nav a {
  font-family: var(--font-sans-body);
  font-size: 15px;
  font-weight: 500;
  line-height: normal;
  color: var(--text-muted);
  padding-bottom: 4px;
  text-decoration: none;
}

/* Die Vorlage kennt kein Hover in der Kopfzeile: der aktive Punkt ist die
   einzige Auszeichnung. Die Regel steht nur da, um die globale
   `a:hover`-Farbe und die Unterstreichung zurückzunehmen. */
.site-nav a:hover {
  color: var(--text-muted);
  text-decoration: none;
}

.site-nav a.is-active,
.site-nav a.is-active:hover {
  color: var(--text-heading);
}

/* 44 px Antippfläche mit drei Haarlinien, wie im Entwurf. Der negative
   rechte Rand zieht die Fläche über den Seitenrand hinaus, damit die
   Linien optisch auf der Kante der Wortmarke sitzen. */
.nav-toggle {
  display: none;
  place-items: center;
  /* Der Entwurf sagt `place-items: center`, meint also die Mitte — die drei
     Balkenzeilen entstehen aber implizit und sitzen deshalb am oberen Rand
     der 44-px-Flaeche. Im Entwurf steht der Schalter dadurch rund 15 px
     ueber der Mitte der Wortmarke; `align-content` holt ihn dorthin, wo er
     hingehoert. Die einzige Stelle, an der wir dem Entwurf widersprechen —
     und nur, weil er sich hier selbst widerspricht. */
  align-content: center;
  gap: 5px;
  grid-auto-rows: 1px;
  width: 44px;
  height: 44px;
  margin-right: -10px;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
}

.nav-toggle__bar {
  display: block;
  width: 22px;
  height: 1px;
  background: var(--ink);
  transition: transform 220ms var(--ease-out), opacity 160ms linear;
}

.nav-toggle.is-open .nav-toggle__bar:nth-child(1) { transform: translateY(3px) rotate(45deg); }
.nav-toggle.is-open .nav-toggle__bar:nth-child(2) { opacity: 0; }
.nav-toggle.is-open .nav-toggle__bar:nth-child(3) { transform: translateY(-3px) rotate(-45deg); }

/* Unter 920 px klappt die Navigation hinter den Schalter — derselbe Punkt wie
   im Entwurf. Seit v2.4 legt sie sich als ganze Ebene unter die Kopfleiste,
   statt die Seite nach unten zu schieben: die Punkte stehen mittig im freien
   Raum, ohne Haarlinien, jeder mit 46 px Mindesthoehe. */
@media (max-width: 919px) {
  .nav-toggle { display: grid; }

  /* Die Kopfzeile wird flacher, sobald die Punkte hinter dem Schalter
     liegen: 14 px statt 18 px, wie im Entwurf. */
  .site-header__inner {
    padding: 14px var(--page-gutter);
    /* Die Leiste bleibt ueber der offenen Ebene, damit Wortmarke und
       Schliessen-Schalter sichtbar bleiben. */
    position: relative;
    z-index: 2;
    background: var(--surface-page);
  }

  .site-header__brand img { width: 132px; }

  .site-nav { display: none; }

  /* `--nav-bar-h` misst das Skript im Layout an der Kopfleiste; 72 px ist der
     Wert, den die Vorlage als Ausgangsgroesse setzt, falls das Skript nicht
     dazu kommt. */
  .site-nav.is-open {
    display: flex;
    position: fixed;
    left: 0;
    right: 0;
    top: var(--nav-bar-h, 72px);
    bottom: 0;
    z-index: 1;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    padding: var(--space-6) var(--page-gutter);
    background: var(--surface-page);
  }

  .site-nav.is-open a {
    min-height: 46px;
    padding: 10px 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    font-size: var(--text-lg);
    line-height: 1.2;
  }
}

/* -------------------------------------------------------------------- */
/* Absprung                                                               */
/* -------------------------------------------------------------------- */

/* Hinweiszeile unter einem Absprung: sagt, wohin es geht, bevor geklickt wird.
   Das eigene „Bescheid geben"-Formular ist mit v2.4 entfallen — die Vormerkung
   laeuft ueber OpenTable, wo die Termine stehen. Mit ihm sind die `.notify-*`
   und die Lesehilfe `.sr-only` weggefallen, die nur sein Feld beschriftet hat. */
.offsite-note {
  font: var(--type-caption);
  color: var(--text-muted);
  margin: var(--space-4) 0 0;
}

/* -------------------------------------------------------------------- */
/* Reservierung                                                           */
/* -------------------------------------------------------------------- */

.reserve-grid {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: var(--space-6);
  align-items: start;
}

/* Das OpenTable-Fenster kommt als iframe fremder Hoehe. Die Mindesthoehe
   haelt den Platz frei, solange es laedt — sonst springt die Seite. */
.reserve-widget {
  min-height: 260px;
  display: flex;
  justify-content: center;
}

.reserve-widget iframe { max-width: 100%; }

/* Als Kurzform notiert, damit die Zeilenhöhe wie im Entwurf auf `normal`
   zurückfällt — mit den geerbten 1.65 stand die Karte drei Pixel höher. */
.reserve-phone {
  font: 500 18px var(--font-sans-body);
  color: var(--ink);
  text-decoration: none;
}

@media (max-width: 919px) {
  .reserve-grid { grid-template-columns: 1fr; }
}

/* Gutscheine: das Formular von gastronovi steht in der schmalen Spalte, der
   Abschluss darunter hinter einer Haarlinie — Werte aus dem Entwurf v1.9. */
.voucher-widget {
  min-height: 320px;
}

.voucher-close {
  margin-top: var(--space-8);
  text-align: center;
}

/* -------------------------------------------------------------------- */
/* Einwilligung                                                           */
/* -------------------------------------------------------------------- */

/* Das Band traegt einen Satz und die Entscheidung. Die Einzelheiten stehen
   nicht in einer zweiten Ebene darueber, sondern als Abschnitt in den
   Datenschutzhinweisen — verlinkt, nicht aufgeklappt. Ein Dialog, der einen
   zweiten Dialog oeffnet, ist ein Dialog zu viel. */
.consent-banner {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  background: var(--paper);
  box-shadow: 0 -8px 24px rgba(38, 36, 31, 0.10);
}

.consent-banner__inner {
  box-sizing: content-box;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: var(--space-4) var(--page-gutter);
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-6);
  align-items: center;
}

.consent-banner__text {
  font: var(--type-caption);
  color: var(--text-body);
  margin: 0;
  max-width: 72ch;
}

/* Gleiche Fassung, gleiche Groesse, gleiche Reihe: „Ablehnen" darf nicht
   schwerer zu finden sein als „Annehmen". */
.consent-banner__actions {
  display: flex;
  gap: 10px;
  flex-shrink: 0;
}

@media (max-width: 919px) {
  .consent-banner__inner {
    grid-template-columns: 1fr;
    gap: var(--space-4);
  }

  .consent-banner__actions .btn { flex: 1; }
}

/* Der verlinkte Abschnitt in den Datenschutzhinweisen: hier steht, was laeuft,
   und hier wird die Entscheidung geaendert oder widerrufen. */
.consent-details {
  background: var(--surface-tinted);
  padding: var(--space-6);
  margin: var(--space-5) 0;
  display: grid;
  gap: var(--space-5);
}

.consent-details__group-title {
  font: var(--type-caption);
  color: var(--text-muted);
}

.consent-details__group-title--spaced { margin-top: var(--space-3); }

.consent-service__head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  flex-wrap: wrap;
}

.consent-service__label {
  font: 500 var(--text-base)/1.4 var(--font-sans-body);
  color: var(--ink);
}

.consent-service__spacer { flex: 1; min-width: 8px; }

.consent-service__switch {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font: var(--type-caption);
  color: var(--text-body);
}

.consent-service__switch input {
  width: 20px;
  height: 20px;
  accent-color: var(--slate-dark);
  cursor: pointer;
}

.consent-service__note {
  font: var(--type-caption);
  color: var(--text-muted);
  margin-top: 4px;
}

.consent-details__actions {
  display: flex;
  gap: 12px;
  align-items: center;
  flex-wrap: wrap;
}

.consent-details__saved {
  font: var(--type-caption);
  color: var(--text-muted);
}

@media (max-width: 919px) {
  .consent-details { padding: var(--space-5); }

  .consent-details__actions {
    flex-direction: column;
    align-items: stretch;
  }
}

/* -------------------------------------------------------------------- */
/* Footer                                                                 */
/* -------------------------------------------------------------------- */

.site-footer {
  background: var(--ink);
  color: var(--paper);
}

.site-footer__inner {
  box-sizing: content-box;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: var(--space-8) var(--page-gutter);
  display: grid;
  grid-template-columns: 1.5fr 1fr 1.15fr;
  gap: var(--space-7);
  align-items: start;
}

.site-footer__brand { display: block; width: 230px; height: auto; max-width: 100%; }

/* `--type-caption` ist 300/14 px; die 1.9 Zeilenhöhe setzt der Entwurf im
   Fuß darüber, damit die Spalten luftig stehen. */
.site-footer__tagline {
  font: var(--type-caption);
  line-height: 1.9;
  color: rgba(246, 241, 229, 0.65);
  max-width: 34ch;
  margin: var(--space-4) 0 0;
}

.site-footer__col {
  font: var(--type-caption);
  line-height: 1.9;
  color: rgba(246, 241, 229, 0.65);
}

.site-footer__col-title {
  font: var(--type-marginal);
  word-spacing: var(--word-space-serif-italic);
  color: var(--greige);
  margin-bottom: var(--gap-eyebrow-tight);
}

/* Links in der Kontaktspalte stehen in voller Papierfarbe und damit heller als
   ihr Umfeld (0.65) — das ist ihre einzige Auszeichnung, unterstrichen wird
   nichts. Die Anschrift darüber bleibt Text: sie führt nirgendwohin. */
.site-footer__col a {
  color: var(--paper);
}

/* Die Kontaktspalte gruppiert: Anschrift, Erreichbarkeit, Bankett, Wege
   weiter — zwischen den Gruppen 10 px Luft, wie im Entwurf. */
.site-footer__group {
  margin-top: 10px;
}

.site-footer__bottom {
  border-top: 1px solid rgba(246, 241, 229, 0.15);
}

.site-footer__bottom-inner {
  box-sizing: content-box;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: var(--space-4) var(--page-gutter);
  display: flex;
  justify-content: space-between;
  font: var(--type-caption);
  font-size: var(--text-xs);
  /* 0.72 statt 0.45: bei 14 px auf der dunklen Fläche lag die Zeile unter dem
     geforderten Kontrast. */
  color: rgba(246, 241, 229, 0.72);
}

.site-footer__bottom-inner a {
  color: rgba(246, 241, 229, 0.6);
}

.site-footer__legal-links {
  display: flex;
  gap: 18px;
}

@media (max-width: 919px) {
  .site-footer__inner {
    grid-template-columns: 1fr;
    gap: var(--space-6);
  }

  .site-footer__brand { width: 190px; }

  .site-footer__bottom-inner {
    flex-direction: column;
    gap: 10px;
  }
}

/* -------------------------------------------------------------------- */
/* Buttons                                                                */
/* -------------------------------------------------------------------- */

/* Werte 1:1 aus `components/core/Button.jsx` des Design-Systems:
   12 px Versalien auf einer Zeile, 12/28 px Innenabstand, Radius 2 px.
   `text-indent` fängt die Sperrung hinter dem letzten Buchstaben auf —
   ohne sie steht die Beschriftung optisch links im Kasten. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  font-family: var(--font-sans-body);
  font-size: 13px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: var(--tracking-caps);
  text-indent: var(--tracking-caps);
  text-transform: uppercase;
  padding: 10px 24px;
  min-height: 44px;
  border-radius: var(--radius-xs);
  border: 1px solid transparent;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
  text-decoration: none;
}

.btn:hover {
  text-decoration: none;
}

.btn:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

.btn--primary {
  background: var(--interactive);
  color: var(--paper);
  border-color: transparent;
}

.btn--primary:hover {
  background: var(--interactive-hover);
  color: var(--paper);
}

.btn--secondary {
  background: transparent;
  color: var(--ink);
  border-color: var(--border-strong);
}

.btn--secondary:hover {
  background: var(--greige-light);
  color: var(--ink);
}

/* Der Ghost behält Kasten und Versalien; nur Rahmen und Fläche fehlen,
   bis der Zeiger darauf steht. */
.btn--ghost {
  background: transparent;
  color: var(--text-accent);
  border-color: transparent;
}

.btn--ghost:hover {
  background: rgba(38, 36, 31, 0.05);
  color: var(--text-accent);
}

/* `flush` zieht die Beschriftung an die Kastenkante, damit ein Ghost unter
   einer linksbündigen Textspalte auf deren Kante sitzt statt 24 px eingerückt. */
.btn--flush {
  margin-left: -24px;
  margin-right: -24px;
}

.btn--sm.btn--flush {
  margin-left: -16px;
  margin-right: -16px;
}

/* Werte aus `components/core/Button.jsx`, Stand v2.4. Gesperrte Versalien
   tragen optisch weiter als ihre Pixelzahl — 13 px Caps lesen sich etwa wie
   16 px Gemischtsatz. WCAG/EN 301 549 nennt ohnehin keine Mindestgröße,
   sondern Kontrast und Zielgröße; die 44 px Höhe bleiben deshalb stehen,
   auch wo die Schrift kleiner wird. */
.btn--default {
  padding: 10px 24px;
  font-size: 13px;
  min-height: 44px;
}

.btn--lg {
  padding: 13px 30px;
  font-size: 14px;
  min-height: 44px;
}

.btn--sm {
  padding: 8px 16px;
  font-size: 13px;
  min-height: 40px;
}

/* -------------------------------------------------------------------- */
/* Card                                                                   */
/* -------------------------------------------------------------------- */

.card {
  background: var(--surface-card);
  border: var(--rule-hairline);
  border-radius: var(--radius-sm);
  padding: var(--space-5);
  box-shadow: var(--shadow-card);
}

/* Getönt heisst im Entwurf nur: andere Fläche. Die Haarlinie bleibt — ohne
   sie verliert die Karte ihre Kante gegen den Papierton. */
.card--tinted {
  background: var(--surface-tinted);
}

/* -------------------------------------------------------------------- */
/* Badge                                                                  */
/* -------------------------------------------------------------------- */

/* Werte aus `components/core/Badge.jsx`: eine Pille mit 38 px Mindesthöhe,
   11/18 px Innenabstand und 14 px Versalien. Die Fläche trägt die Farbe, die
   Schrift steht darauf — nicht umgekehrt.

   `flex: 0 0 auto` und `align-self: center` halten die Pille in einer
   dehnenden Zeile auf eigener Höhe, damit die Beschriftung nicht hochrutscht. */
.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: center;
  flex: 0 0 auto;
  box-sizing: border-box;
  min-height: 38px;
  padding: 11px 18px;
  border-radius: var(--radius-full);
  font: 500 var(--text-xs)/1 var(--font-sans-body);
  letter-spacing: var(--tracking-caps);
  text-indent: var(--tracking-caps);
  text-transform: uppercase;
  background: var(--greige-light);
  color: var(--ink);
}

.badge--slate {
  background: var(--slate);
  color: var(--paper);
}

.badge--ink {
  background: var(--ink);
  color: var(--paper);
}

.badge--ok {
  background: var(--status-ok);
  color: var(--paper);
}

.badge--warn {
  background: var(--status-warn);
  color: var(--paper);
}

.badge--error {
  background: var(--status-error);
  color: var(--paper);
}

/* -------------------------------------------------------------------- */
/* Tabs                                                                   */
/* -------------------------------------------------------------------- */

/* Werte aus `components/core/Tabs.jsx`, Stand v2.4: keine Linien mehr — weder
   die Haarlinie unter der Leiste noch die Kante unter dem aktiven Reiter. Der
   aktive Reiter wird durch die Tinte markiert, wie die Navigation und die
   Rubriken der Karte. Die Karte setzt Spalten- und Zeilenabstand selbst
   (`.menu-card .tabs`), weil ihre Reiter umbrechen dürfen. */
.tabs {
  display: flex;
  gap: var(--space-6);
  flex-wrap: wrap;
}

.tabs--center {
  justify-content: center;
}

.tabs__button {
  box-sizing: border-box;
  background: none;
  border: none;
  cursor: pointer;
  font-family: var(--font-sans-body);
  font-size: 15px;
  font-weight: 500;
  line-height: normal;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--text-muted);
  padding: 12px 2px;
  min-height: 44px;
  transition: color var(--duration-fast) var(--ease-out);
}

.tabs__button.is-active {
  color: var(--text-heading);
}

/* -------------------------------------------------------------------- */
/* Forms                                                                  */
/* -------------------------------------------------------------------- */

/* Werte aus `components/forms/Input.jsx`: Beschriftung als gesperrte
   Versalienzeile in 400/12 px mit 6 px Abstand, Feld mit 11/14 px
   Innenabstand — und im Fokus nur die Rahmenfarbe, kein Ring. */
.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.field__label {
  font: var(--type-eyebrow);
  letter-spacing: var(--tracking-eyebrow);
  text-transform: uppercase;
  color: var(--text-muted);
}

.field__control {
  width: 100%;
  box-sizing: border-box;
  font: var(--type-body);
  font-size: var(--text-base);
  color: var(--ink);
  background: var(--surface-card);
  border: var(--rule-hairline);
  border-radius: var(--radius-xs);
  padding: 11px 14px;
  transition: border-color var(--duration-fast) var(--ease-out);
}

.field__control:focus-visible,
.field__control:focus {
  outline: none;
  border-color: var(--focus-ring);
}

.field__options {
  display: flex;
  gap: var(--space-5);
  flex-wrap: wrap;
  padding-top: var(--space-1);
}

/* `components/forms/Radio.jsx`: Text in 14 px, 10 px neben dem Schaltfeld. */
.field__option {
  display: flex;
  align-items: center;
  gap: 10px;
  font: var(--type-body);
  font-size: var(--text-sm);
  color: var(--text-body);
}

.field--checkbox {
  flex-direction: row;
  align-items: center;
}

/* Die Beschriftung einer Kreuzfläche ist Text, keine Versalienzeile —
   `components/forms/Checkbox.jsx`: `--type-body` in 14 px. */
.field--checkbox .field__label {
  text-transform: none;
  letter-spacing: var(--tracking-body);
  font: var(--type-body);
  font-size: var(--text-sm);
  color: var(--text-body);
}

/* -------------------------------------------------------------------- */
/* Dialog                                                                 */
/* -------------------------------------------------------------------- */

.dialog-overlay {
  position: fixed;
  inset: 0;
  background: rgba(38, 36, 31, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
  z-index: 50;
}

.dialog {
  background: var(--surface-card);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-overlay);
  max-width: 480px;
  width: 100%;
  padding: var(--space-6);
}

.dialog__eyebrow {
  margin-bottom: var(--space-2);
}

.dialog__title {
  font-size: var(--text-2xl);
  margin-bottom: var(--space-4);
}

.dialog__body {
  margin-bottom: var(--space-5);
}

.dialog__footer {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-3);
}

/* -------------------------------------------------------------------- */
/* Menu page dotted-line rows                                            */
/* -------------------------------------------------------------------- */

/* Der Inhalt eines Reiters: Gerichte in 24 px Abstand, wie im Entwurf.

   Diese drei Werte standen als Inline-Stil am Element — und genau das war der
   Fehler, der die Karte gedrängt aussehen liess: das Element trägt `x-show`,
   und Alpine *entfernt* beim Einblenden die `display`-Eigenschaft aus dem
   Inline-Stil. Damit fiel `display: grid` weg, die Zeilen standen als Blöcke
   ohne Abstand aufeinander — sichtbar erst, nachdem Alpine gelaufen war.
   Als Klasse notiert kann Alpine sie nicht wegnehmen: beim Ausblenden setzt
   es `display: none` inline (schlägt die Klasse), beim Einblenden nimmt es
   das wieder weg und die Klasse greift. */
.menu-tab-panel {
  --menu-gap: var(--space-5);
  margin-top: var(--space-7);
  display: grid;
  gap: var(--menu-gap);
}

@media (max-width: 919px) {
  /* Am Handy stehen die Gerichte weiter auseinander (32 statt 24 px): die
     Beschreibung darf dort über mehrere Zeilen laufen. */
  .menu-tab-panel { --menu-gap: var(--space-6); margin-top: 0; }
}

/* Die Karte steht in einer Tafel mit grosszügigem Innenabstand; am Handy
   rückt er auf 24/16 px zusammen, damit die Zeilen nicht umbrechen.

   Anders als die Karten des Design-Systems hat sie im Entwurf weder Radius
   noch Schatten — nur Fläche und Haarlinie. */
.menu-card {
  padding: var(--space-7);
  border-radius: var(--radius-none);
  box-shadow: none;
}

/* Die Reiter der Karte dürfen umbrechen — „Weinkarte" ist der siebte. Sie
   stehen enger beieinander als die Reiter des Design-Systems (24 statt 32 px)
   und bekommen zwischen den Zeilen 12 px Luft. */
.menu-card .tabs {
  column-gap: var(--space-5);
  row-gap: var(--space-3);
}

@media (max-width: 919px) {
  .menu-card {
    padding: var(--space-6) var(--space-5);
  }

  /* Am Handy stehen die Kategorien untereinander, nicht als Reiterleiste:
     waagerechtes Scrollen wird nicht entdeckt, und umgebrochene Reiter ergaben
     drei ausgefranste Zeilen. Sie tragen dann dieselbe Serif wie die Rubriken,
     linksbündig — eine Liste, keine Leiste. */
  .menu-card .tabs {
    flex-direction: column;
    align-items: stretch;
    flex-wrap: nowrap;
    column-gap: 0;
    row-gap: 2px;
    margin-bottom: var(--space-7);
  }

  .menu-card .tabs__button {
    font: 400 22px/1.3 var(--font-serif-display);
    word-spacing: var(--word-space-serif);
    letter-spacing: normal;
    text-transform: none;
    white-space: normal;
    text-align: left;
    padding: 11px 0;
    min-height: 46px;
  }

  .plate.menu-block-image {
    width: 200px;
  }

  /* Am Handy steht ein mehrteiliger Preis („3 Stück 10,50 · jede weitere
     + 3,50") unter dem Gericht: rechts umbrochen ergab er drei Zeilen. Ein
     einfacher Preis bleibt daneben stehen. */
  .menu-row--price-own-line { flex-wrap: wrap; }
  .menu-row--price-own-line > div:first-child { flex: 1 1 100%; }
  .menu-row--price-own-line .menu-row__leader { display: none; }
  .menu-row--price-own-line .menu-row__price { white-space: normal; margin-top: 7px; }
}

.menu-row {
  display: flex;
  align-items: baseline;
  gap: 14px;
}

.menu-row > div:first-child {
  min-width: 0;
}

/* Seit v2.4 ohne Punktlinie: zwischen Gericht und Preis steht nur noch Luft.
   Die Punkte waren eine Speisekarten-Konvention, die das Haus nicht spricht. */
.menu-row__leader {
  flex: 1;
  min-width: 12px;
}

.menu-row__price {
  font: 500 var(--text-base)/1.5 var(--font-sans-body);
  color: var(--ink);
  white-space: nowrap;
  flex: 0 0 auto;
  text-align: right;
}

/* Das Gericht steht in der Display-Serif, nicht in der Sans: 19 px am
   Schreibtisch, 18 px am Handy. */
.menu-row__name {
  font: 500 19px/1.35 var(--font-serif-display);
  word-spacing: var(--word-space-serif);
  color: var(--ink);
}

.menu-row__desc {
  font: var(--type-body);
  color: var(--text-muted);
  margin-top: 5px;
}

/* Kennzeichnungen wie „vegan" stehen seit v1.9 in der Textzeile statt als
   Pille neben dem Namen: eine leise Fläche, die den Namen nicht überstimmt. */
.menu-row__tag {
  padding: 1px 7px;
  background: var(--greige-light);
  border-radius: 2px;
  color: var(--text-body);
  white-space: nowrap;
}

.menu-row__desc .menu-row__tag + .menu-row__tag,
.menu-row__desc-text + .menu-row__tag {
  margin-left: 8px;
}

/* Die Rubrik trägt seit v2.4 dieselbe Serif wie die Gerichte, nur größer und
   grau — kein eigener Schnitt, keine gesperrten Versalien, keine Haarlinie bis
   zum Rand. Die Hierarchie kommt aus Größe und Farbe. */
/* Die Abstände stehen als Differenz zum Rasterabstand des Reiters: der trägt
   zwischen je zwei Blöcken schon `--menu-gap` bei. Gerechnet kommt heraus, was
   der Entwurf setzt — 64 px über einer Rubrik, 32 px darunter. */
.menu-block-heading {
  font: 400 24px/1.3 var(--font-serif-display);
  word-spacing: var(--word-space-serif);
  color: var(--text-muted);
  margin-top: calc(var(--space-8) - var(--menu-gap, var(--space-5)));
  margin-bottom: calc(var(--space-6) - var(--menu-gap, var(--space-5)));
}

.menu-block-heading:first-child { margin-top: 0; }

@media (max-width: 919px) {
  .menu-block-heading { font-size: 22px; }
  .menu-row__name { font-size: 18px; }

  /* Siehe `partials/menu-block`: dasselbe Wort zweimal untereinander in
     derselben Schrift ist keine Gliederung, sondern ein Fehler. */
  .menu-block-heading--echoes-tab { display: none; }
}

/* Bilder in der Karte sind Tafeln wie am Flanierweg — Passepartout,
   Haarlinie, Graustufen —, 240 px breit (200 px am Handy). Wo sie stehen,
   entscheidet der Redakteur je Bild: mittig wie im Entwurf, oder an eine
   Kante gerückt.

   Die Klasse steht doppelt (`.plate.menu-block-image`), weil `.plate` weiter
   unten `margin: 0` setzt und sonst gewinnt — die Tafel stand dann links
   statt mittig. */
.plate.menu-block-image {
  width: 240px;
  margin-top: var(--space-7);
  margin-left: auto;
  margin-right: auto;
}

.plate.menu-block-image--left {
  margin-left: 0;
  margin-right: auto;
}

.plate.menu-block-image--right {
  margin-left: auto;
  margin-right: 0;
}

.menu-block-image .plate__frame {
  aspect-ratio: 2 / 3;
}

.menu-block-text {
  font: var(--type-caption);
}

/* Die Fußzeile eines Reiters. Seit v2.4 ohne Haarlinie darüber: sie trennte
   eine Angabe ab, die ohnehin leiser steht als alles über ihr. Jeder Reiter
   trägt seine eigene — die Weinkarte rechnet anders ab als die Küche. */
.menu-note {
  margin-top: var(--space-8);
  text-align: center;
  font: var(--type-caption);
  color: var(--text-muted);
}

/* Der Allergenhinweis steht unter der ganzen Karte, gleich unter der Fußzeile
   des Reiters: enger angesetzt, damit beide Zeilen als ein Block lesen. Der
   Link trägt hier eine Unterstreichung — in einer leise gesetzten Zeile ist
   die Farbe allein zu wenig, um ihn als Link zu erkennen. */
.menu-note--allergens {
  margin-top: var(--space-4);
}

.menu-note--allergens a {
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Die Weinkarte steht direkt unter dem Allergenhinweis — enger als der
   Abstand zur Reiter-Fußzeile, damit beide Zeilen als ein Block lesen. */
.menu-note--wine {
  margin-top: var(--space-1);
}

/* Die englische Karte als „EN“-Pille oben rechts im weißen Kasten. Sie
   liegt im oberen Innenabstand der Karte, über der Reiterzeile, und nimmt
   deshalb keinem Reiter Platz weg. Das Kürzel in Versalien wie die Badges;
   `a:hover` unterstreicht hier nicht, die Pille färbt sich stattdessen. */
.menu-card {
  position: relative;
}

.menu-lang {
  position: absolute;
  top: var(--space-5);
  right: var(--space-5);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 4px 10px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-full);
  font: 500 var(--text-xs)/1 var(--font-sans-body);
  letter-spacing: var(--tracking-caps);
  text-indent: var(--tracking-caps);
  color: var(--ink);
  text-decoration: none;
  transition: background-color var(--duration-fast) var(--ease-out), color var(--duration-fast) var(--ease-out);
}

@media (max-width: 919px) {
  .menu-lang {
    top: var(--space-3);
    right: var(--space-3);
  }
}

.menu-lang:hover,
.menu-lang:focus-visible {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
  text-decoration: none;
}

.menu-rule {
  border-top: var(--rule-hairline);
}

/* Hier standen Zellen für Größe und Preis übereinander. Der Entwurf kennt
   das nicht: bei ihm trägt die Preiszeile die Größen mit („6 Stück 12,90 € ·
   12 Stück 19,90 €"). Die Zellen machten aus jeder Zeile mit Größen einen
   zweizeiligen Block, und die Punktlinie davor blieb ein Stummel. */

/* -------------------------------------------------------------------- */
/* Gallery rows (rooms page)                                             */
/* -------------------------------------------------------------------- */

.gallery-row {
  display: grid;
  gap: var(--space-7);
  align-items: center;
}

@media (min-width: 760px) {
  .gallery-row {
    grid-template-columns: 1.4fr 1fr;
  }

  .gallery-row--reverse {
    grid-template-columns: 1fr 1.4fr;
  }

  .gallery-row--reverse .gallery-row__image {
    order: 2;
  }
}

/* -------------------------------------------------------------------- */
/* Event / rooms grids                                                   */
/* -------------------------------------------------------------------- */

.grid-3 {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(3, 1fr);
}

@media (max-width: 919px) {
  .grid-3 {
    grid-template-columns: 1fr;
  }
}

.two-col {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: 1.4fr 1fr;
  align-items: start;
}

@media (max-width: 919px) {
  .two-col {
    grid-template-columns: 1fr;
  }
}

.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px 20px;
}

@media (max-width: 919px) {
  .form-grid {
    grid-template-columns: 1fr;
  }
}

/* -------------------------------------------------------------------- */
/* Mobile (<=640px)                                                       */
/* -------------------------------------------------------------------- */

/* Hier stand eine eigene Token-Fassung für schmale Schirme (kleinere
   Überschriften, engere Abstände, 16 px Seitenrand). Der Entwurf hat sie
   nicht: seine Schriftgrade sind fließend (`clamp()` in --text-3xl/-4xl),
   Abstände und Seitenrand bleiben überall gleich. Die Übersteuerung machte
   die Handy-Fassung durchgehend enger als die Vorlage — deshalb ist sie
   weg. */

/* ── Auftritt ─────────────────────────────────────────────────────────────
   Bewegungsregeln des Entwurfs (siehe `rise()` in seiner shared.jsx): die
   Marke ist ruhig, also bewegt sich nichts schnell und nichts springt.
   Erlaubt sind Deckkraft, ein Weg von 8–16 px nach oben und das Zeichnen
   einer Haarlinie. Verboten: Federn, Skalieren, Drehen, Dauerschleifen.
   Eine Sache erscheint einmal — und bleibt dann, wo sie ist.

   Zwei Wege hinein:
   - Der Kopf der Startseite tritt beim Laden auf. Das ist eine
     CSS-Animation (`.rise` mit Staffel `--1..--6`), sie braucht kein Skript
     und kann deshalb auch nichts verschlucken.
   - Was am Flanierweg liegt, erscheint, wenn es ins Bild kommt. Dafür setzt
     das Skript `is-in` je Station; den Ausgangszustand gibt es überhaupt
     nur, wenn es laufen kann (`js-rise` an <html>).

   Grundsatz „im Zweifel einblenden": ohne Skript, bei abgeschalteter
   Bewegung und bei jedem Messfehler steht alles sichtbar da. Ein
   unsichtbarer Inhalt wäre der schlimmere Fehler. */

.rise {
  animation: flaneurRise 900ms var(--ease-out) both;
}

@keyframes flaneurRise {
  from { opacity: 0; transform: translateY(var(--rise-dist, 12px)); }
  to { opacity: 1; transform: none; }
}

/* Die Staffel des Entwurfs: 140 ms von Zeile zu Zeile, der Weg wird dabei
   kürzer — die letzte Zeile hebt sich kaum noch. */
.rise--1 { animation-delay: 0ms; --rise-dist: 10px; }
.rise--2 { animation-delay: 140ms; --rise-dist: 12px; }
.rise--3 { animation-delay: 280ms; --rise-dist: 12px; }
.rise--4 { animation-delay: 420ms; --rise-dist: 12px; }
.rise--5 { animation-delay: 560ms; --rise-dist: 10px; }
.rise--6 { animation-delay: 700ms; --rise-dist: 8px; }

/* Am Weg: Tafel und Text einer Station heben sich, der Text 180 ms später —
   so liest man erst das Bild, dann die Zeile. */
.js-rise .way-stop__plate,
.js-rise .way-stop__text,
.js-rise .way-mark {
  opacity: 0;
  transform: translateY(16px);
  transition:
    opacity 900ms var(--ease-out),
    transform 900ms var(--ease-out);
}

.js-rise .way-stop__text {
  transition-delay: 180ms;
}

.js-rise .way-mark {
  transform: translateY(10px);
}

.js-rise .way-stop.is-in .way-stop__plate,
.js-rise .way-stop.is-in .way-stop__text,
.js-rise .way-mark.is-in {
  opacity: 1;
  transform: none;
}

/* Der Auftritt eines Wegstücks: einblenden, nicht zeichnen. Querarm und Raute
   sind mit v2.4 entfallen, mit ihnen der gestaffelte Strichauftritt. */
.js-rise .way-stop .way-track__svg {
  opacity: 0;
  transition: opacity 900ms var(--ease-out);
}

.js-rise .way-stop.is-in .way-track__svg {
  opacity: 1;
}

/* Abgeschaltete Bewegung: das Skript setzt `js-rise` dann von sich aus nicht.
   Die Regeln hier sind das Netz darunter — etwa wenn die Einstellung erst
   nach dem Laden umgestellt wird. */
@media (prefers-reduced-motion: reduce) {
  .rise { animation: none; opacity: 1; transform: none; }

  .js-rise .way-stop__plate,
  .js-rise .way-stop__text,
  .js-rise .way-mark,
  .js-rise .way-stop .way-track__svg {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ── Kopf der Startseite ──────────────────────────────────────────────────
   Fast eine volle Bildschirmhöhe, mittig, ohne Bild: die Wortmarke trägt die
   Seite allein. `svh` statt `vh`, weil die Adressleiste mobiler Browser sonst
   den Hinweis „Entdecken" unter den Rand schiebt. */

.hero {
  background: var(--surface-page);
  text-align: center;
  padding: clamp(56px, 8vw, 120px) var(--page-gutter) clamp(110px, 11vw, 160px);
  min-height: calc(100svh - 60px);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: relative;
}

/* Die Adresszeile über der Wortmarke steht seit v2.4 gemischt in der Sans,
   nicht als gesperrte Versalienzeile: 16 px, fast ohne Sperrung. */
.hero__eyebrow {
  font: 400 var(--text-sm)/1.5 var(--font-sans-body);
  letter-spacing: 0.02em;
  color: var(--text-muted);
  margin-bottom: clamp(28px, 4vw, 48px);
}

.hero__wordmark { margin: 0; display: flex; justify-content: center; }

/* Das height-Attribut am img traegt nur das Seitenverhaeltnis — ohne
   `height: auto` zwingt es die Wortmarke auf seinen Wert. */
.hero__wordmark img { width: min(560px, 78vw); height: auto; }

.hero__subline {
  font: var(--type-body-serif);
  font-style: italic;
  word-spacing: var(--word-space-serif-italic);
  color: var(--text-muted);
  margin: clamp(24px, 3.4vw, 42px) 0 clamp(40px, 6vw, 72px);
}

.hero__actions {
  display: flex;
  gap: 18px;
  justify-content: center;
  flex-wrap: wrap;
}

/* Keine Deckkraft auf dem Anker: bei 16 px Sans fiel der Hinweis damit auf
   2,8:1 Kontrast — und die Deckkraft überschrieb zugleich die Einblendung.
   Der Hover läuft deshalb über die Farbe. */
.hero__scroll {
  position: absolute;
  left: 50%;
  bottom: clamp(28px, 4vw, 52px);
  transform: translateX(-50%);
  font: 400 var(--text-sm)/1.5 var(--font-sans-body);
  letter-spacing: 0.02em;
  color: var(--text-muted);
  text-decoration: none;
  transition: color var(--duration-base) var(--ease-out);
}

.hero__scroll:hover { color: var(--ink); text-decoration: none; }

@media (max-width: 919px) {
  .hero__actions { flex-direction: column; width: 100%; max-width: 320px; gap: 12px; }
}


/* ── Der Flanierweg (Startseite v1.7) ─────────────────────────────────────
   Ein Weg führt senkrecht durch die Seite und schwingt zu jeder Station
   hinüber — abwechselnd nach links und rechts, ohne Knick am Übergang. Am
   Scheitel des Bogens sitzt eine Raute, von ihr führt ein kurzer Querarm zur
   Bildkante. Tafel und Text stehen auf einer Höhe, je auf einer Seite.

   Geometrie 1:1 aus dem Entwurf: Spur 300 px breit, Scheitel 124 px aus der
   Mitte, Querarm 26 px. Der Bogen sind zwei Kubiken mit Griffen auf halber
   Höhe — senkrechte Tangente an Anfang und Ende, waagerechte im Scheitel.
   `preserveAspectRatio: none` streckt ihn auf die Höhe der Station,
   `vector-effect` hält die Linie dabei haarfein. */

.way {
  position: relative;
  background: var(--surface-page);
  padding-bottom: var(--space-8);
}

/* Der Weg läuft seit v2.4 nicht durch die Wegmarke: neben Text liest sich die
   Linie als Rahmen um die Worte statt als Weg. Die Marke steht frei, der Weg
   beginnt an der ersten Station. */
.way-mark {
  position: relative;
  padding: var(--space-6) 0;
  text-align: center;
}

.way-mark span {
  position: relative;
  background: var(--surface-page);
  padding: 0 var(--space-5);
  font: var(--type-marginal);
  word-spacing: var(--word-space-serif-italic);
  color: var(--text-muted);
}

.way-stop { position: relative; padding: var(--space-9) 0; }

/* Die Spur, in der der Bogen schwingt. */
.way-track {
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 300px;
  margin-left: -150px;
}

.way-track svg { display: block; width: 100%; height: 100%; overflow: visible; }

/* Zwei Bögen, eine Geometrie: die breite Spur (300 px, Scheitel 124 px aus
   der Mitte) und die schmale Fassung fürs Handy (44 px, Scheitel 13 px). Sie
   brauchen je eine eigene viewBox, weil `preserveAspectRatio: none` den Bogen
   sonst in die Breite zieht — der schmale Weg würde ausschlagen wie der
   breite. */
.way-track .way-track__svg--narrow { display: none; }

/* Ohne non-scaling-stroke (im Markup) wird die Haarlinie beim Strecken dick. */
.way-track__path { fill: none; stroke: var(--border-hairline); stroke-width: 1; }

/* Querarm und Raute am Scheitel sind mit v2.4 entfallen: der Bogen zeigt schon
   zur Tafel, ein Punkt darauf ist Dekor — und ohne den Klecks am Ende war der
   Querarm ein Stummel in der Luft. Geblieben ist die Regel, dass der Weg keine
   Marken setzt: die Linie führt, das Bild hält. */

.way-stop__grid {
  box-sizing: content-box;
  position: relative;
  max-width: var(--page-max);
  margin: 0 auto;
  padding: 0 var(--page-gutter);
  display: grid;
  grid-template-columns: 1fr 300px 1fr;
  align-items: center;
}

/* `grid-row: 1` ist nicht kosmetisch: hängt die Tafel rechts, steht ihre
   Spalte im Markup vor der Textspalte. Die automatische Platzierung geht
   dann nicht mehr zurück in die erste Zeile und setzt den Text darunter —
   die Station wird doppelt so hoch, und der Scheitel des Bogens (auf halber
   Stationshöhe) rutscht unter die Bildmitte, wo die Raute sitzt. Genau die
   Lücke zwischen Raute und Linie. */
.way-stop__col--left { grid-column: 1; grid-row: 1; display: flex; justify-content: flex-end; min-width: 0; }
.way-stop__col--right { grid-column: 3; grid-row: 1; min-width: 0; }

.way-stop__plate { width: min(100%, 300px); }
/* Innenabstand oben, gleich viel negativer Außenabstand: das Layout bleibt,
   aber der Kasten reicht über die Titelzeile hinaus.
   „The New Elegance" meldet eine Oberlänge von 750/1000, ihre Versalien sind
   968 hoch, ein „É" 1206. Bei Zeilenhöhe 1,2 ragt der Titel damit über seinen
   eigenen Kasten. Chrome malt das trotzdem; Safari legt den Block fürs
   Einblenden (`transform`, `opacity`) auf eine eigene Ebene und schneidet
   alles über deren Rand ab — der obere Rand der ersten Zeile fehlte.
   0,4 em der Titelgröße decken auch Akzente auf Versalien. */
.way-stop__text {
  max-width: 40ch;
  text-align: left;
  padding-top: calc(var(--text-3xl) * 0.4);
  margin-top: calc(var(--text-3xl) * -0.4);
}

/* Kein Eyebrow mehr an der Station: Stadt und Nummer stehen schon in der
   Bildunterschrift, der Titel führt den Abschnitt (v2.4). */

.way-stop__title { font: var(--type-display); color: var(--text-heading); margin: 0; }
.way-stop__body { font: var(--type-body); margin: var(--space-5) 0 0; }

/* Abstandsleiter im Textblock einer Station, wie im Entwurf: Eyebrow steht
   frei, Titel atmet 24 px zum Text, Absätze trennen 32 px. Folgt auf den
   Absatz direkt eine Schaltfläche, reichen 16 px — ihr Innenabstand weitet
   die Lücke optisch. */
.way-stop__body--lead-cta { margin-bottom: var(--space-4); }

.way-stop__quote {
  font: var(--type-body);
  font-style: italic;
  color: var(--ink);
  margin: var(--space-6) 0 var(--space-5);
}

/* Der Hinweis steht seit v2.4 auf getönter Fläche statt an einer Haarlinie —
   und ohne die Zeile „In Vorbereitung" darüber: der Satz sagt es selbst. */
.way-stop__notice {
  background: var(--surface-tinted);
  padding: var(--space-4) var(--space-5);
  margin: var(--space-5) 0 var(--space-4);
}

/* Steht die Schaltflaeche allein unter dem Absatz — ohne Hinweis-Kasten
   dazwischen —, bekommt sie seit v2.4 den vollen Abstand statt der engen
   Variante. */
.way-stop__cta--spaced { margin-top: var(--space-5); }

.way-stop__ink { width: 150px; display: block; margin: 0 0 var(--space-5); }

/* Der letzte Weg weiter, direkt hinter dem Schlusspunkt des Weges — ohne
   eigenen Abstand nach oben, den Abstand nach unten trägt `.way`. */
.way__close {
  max-width: 640px;
  margin: 0 auto;
  padding: 0 var(--page-gutter);
  text-align: center;
}

/* Der Weg läuft aus: die Linie verliert sich, statt an einer Kante abzubrechen.
   Kein Schlusspunkt — eine Raute oder ein Punkt am Ende ist eine Behauptung. */
.way-outro { position: relative; height: 90px; }

.way-outro__line {
  position: absolute;
  left: 50%;
  top: 0;
  height: 78px;
  width: 1px;
  background: linear-gradient(to bottom, var(--border-hairline), transparent);
}

/* ── Bildtafel ──────────────────────────────────────────────────────────── */

/* „L'œil du flâneur": Passepartout, Haarlinie, gesperrte Bildunterschrift —
   eine Einheit für Website, Karte und Raumbeschriftung. Werte 1:1 aus
   `components/media/PhotoPlate.jsx`: 16 px Passepartout auf getönter Fläche,
   Haarlinie als Rahmen, das Bild in Graustufen. */
.plate {
  display: flex;
  flex-direction: column;
  margin: 0;
}

.plate__mat {
  background: var(--surface-tinted);
  padding: var(--space-4);
  border: var(--rule-hairline);
}

.plate__frame {
  position: relative;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--greige-dark);
}

.plate img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(1) contrast(1.03);
}

/* Eine Zeile, ein Schnitt. Vorher trug der Ort noch Kursive gegen den
   aufrechten Rest — zwei Schnitte für eine Bildunterschrift. Seit v2.4 eine
   durchgehende Serifenzeile; die Hierarchie kommt aus der Reihenfolge (Ort,
   Stadt, Nummer), nicht aus dem Schnitt. */
.plate__caption {
  margin-top: var(--space-3);
  font: 400 var(--text-sm)/1.55 var(--font-serif-display);
  word-spacing: var(--word-space-serif);
  color: var(--text-body);
  text-wrap: pretty;
}

/* Fehlt das Bild noch, bleibt die Fläche als Platzhalter stehen statt die
   Station einstürzen zu lassen — der Redakteur sieht, wo etwas hingehört,
   und welche Datei dorthin soll. */
.plate--empty {
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(
    45deg,
    var(--greige-light),
    var(--greige-light) 12px,
    var(--surface-page) 12px,
    var(--surface-page) 24px
  );
  border: 1px dashed var(--border-strong);
  display: grid;
  place-items: center;
  align-content: center;
  gap: 6px;
  padding: var(--space-4);
  text-align: center;
}

.plate--empty b {
  font: var(--type-marginal);
  word-spacing: var(--word-space-serif-italic);
  color: var(--text-heading);
}

.plate--empty span { font: var(--type-caption); color: var(--text-muted); }
.plate--empty code { font-family: ui-monospace, monospace; font-size: 11px; color: var(--text-accent); }

@media (max-width: 919px) {
  /* Auf schmalen Schirmen läuft der Weg am linken Rand und schlängelt in
     einer 44 px schmalen Spur weiter — dieselbe Bewegung auf Daumenbreite.
     Die Station steht rechts daneben, Tafel über Text. */
  .way-mark { text-align: left; padding: var(--space-8) 0; padding-left: var(--page-gutter); }
  .way-mark span { padding: 0 12px 0 0; }

  .way-track { left: var(--page-gutter); width: 44px; margin-left: 0; }
  .way-track .way-track__svg--wide { display: none; }
  .way-track .way-track__svg--narrow { display: block; }

  /* Die Unterschrift steht am Handy über dem Bild (captionPlacement="above"),
     damit Bild und Text der Station direkt aufeinander folgen. */
  .plate__caption { order: -1; margin: 0 0 var(--space-3); }

  .way-stop { padding: var(--space-9) 0; }

  .way-stop__grid {
    grid-template-columns: 1fr;
    padding-left: calc(var(--page-gutter) + 47px);
    row-gap: var(--space-7);
  }

  /* Einspaltig: die Zeilenbindung von oben muss weg, sonst lägen Tafel und
     Text übereinander in derselben Zeile. */
  .way-stop__col--left,
  .way-stop__col--right { grid-column: 1; grid-row: auto; display: block; }

  .way-stop__plate { width: 100%; max-width: 320px; }
  .way-stop__text { max-width: 38ch; }

  /* Tafel immer zuerst, egal auf welcher Seite sie am Desktop hängt. */
  .way-stop--plate-right .way-stop__col--right { order: -1; }

  .way-outro__line { left: calc(var(--page-gutter) + 22px); }
}
