/*
 * Essenspool - Grundstil
 *
 * Mobile first (Fachkonzept Kapitel 58). Die eigentliche Alltagsoberflaeche
 * entsteht in Sprint 009; hier stehen nur die Grundlagen.
 */

:root {
  color-scheme: light dark;

  --ep-primary: #285943;
  /* Schrift auf der Hauptaktion. Eigene Rolle, weil sie im Dunkelmodus nicht
     der Kartenflaeche folgt: dort steht dunkles Gruen auf hellem Gruen. */
  --ep-on-primary: #ffffff;
  --ep-soft: #e7eee4;
  --ep-text: #202a24;
  /* Das Paket nennt #626b64. Auf der Trennlinienflaeche - .badge--quiet setzt
     leisen Text darauf - waeren das 4,35 : 1 und damit unter der geprueften
     Schwelle. Eine Stufe dunkler erreicht 4,55 : 1. */
  --ep-muted: #5f6862;
  --ep-surface: #ffffff;
  --ep-bg: #f7f5ef;
  --ep-line: #e1e6dd;
  /* Rahmen, die ein Eingabefeld erst erkennbar machen, brauchen 3 : 1 gegen
     Flaeche und Hintergrund (WCAG 1.4.11). --ep-line bleibt dekorativ
     (Sprint 009). */
  --ep-input: #79857c;
  --ep-danger: #a32929;
  /* Warnung und Gelb kennt das Designpaket nicht - beide behalten ihre
     bisherigen Werte und ihre Rolle. */
  --ep-warning: #9a5b00;
  /* "Isst es angepasst" - bisher als fester Wert an drei Stellen. */
  --ep-yellow: #c99700;
  /*
   * Nur der bedeutungstragende Akzent der gelben Familienpraeferenz.
   *
   * --ep-yellow selbst bleibt unangetastet (Schritt 2: Gelb behaelt seinen
   * Wert und seine Rolle). Als 4-px-Balken auf der Abzeichenflaeche erreichte
   * es im Hellmodus aber nur 2,65 : 1 - unter den 3 : 1, die ein Element
   * braucht, das Bedeutung traegt (WCAG 1.4.11). Diese Stufe dunkler erreicht
   * 3,2 : 1 auf der Abzeichenflaeche und 3,6 : 1 auf der Seite.
   *
   * Reine Darstellungsfarbe: Der Fachwert heisst weiter "yellow", die
   * Beschriftung weiter "Isst es angepasst".
   */
  --ep-preference-yellow: #a98000;

  /* Abstandsraster des Designpakets. Es ersetzt den frueheren Einzelwert
     --abstand: 1rem - --ep-s4 ist derselbe Abstand, nur als Teil einer Reihe. */
  --ep-s1: 4px;
  --ep-s2: 8px;
  --ep-s3: 12px;
  --ep-s4: 16px;
  --ep-s5: 20px;
  --ep-s6: 24px;
  --ep-s8: 32px;
  --ep-s12: 48px;

  /* Karten 20 px, Eingaben und Knoepfe 12 px (Styleguide Abschnitt 2). */
  --ep-radius: 12px;
  --ep-card-radius: 20px;

  /* Hauptaktionen mindestens 48 px hoch, jedes andere Bedienziel 44 px. */
  --ep-target: 44px;
  --ep-target-primary: 48px;

  /* Die Shell darf bis 1200 px breit werden (Styleguide Abschnitt 3). Fliesstext
     und Formulare bleiben auf Lesemass - eine 1200 px breite Textspalte liest
     sich schlechter, nicht besser. Nur Kartenraster brechen ueber
     .container--wide aus. */
  --ep-content-max: 1200px;
  --ep-text-max: 46rem;

  /* Breite der Seitenleiste ab 48rem. Eine Groesse fuer die Leiste und fuer die
     Spalte daneben - wie --ep-nav-height fuer die untere Leiste. */
  --ep-sidebar-width: 216px;

  --ep-font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;

  /*
   * TYPOROLLEN - sechs Groessen mit einem Namen, statt vierzehn ohne.
   *
   * Vorher standen im Stylesheet vierzehn verschiedene font-size-Werte. Drei
   * davon - 0,95rem, 0,9rem und 0,85rem - liegen keine anderthalb Pixel
   * auseinander; welcher davon irgendwo stand, entschied die Gewohnheit des
   * Tages, nicht die Rolle des Textes. Die Rolle steht jetzt im Namen:
   *
   *   page     Seitentitel, genau einer je Seite (h1)
   *   section  Abschnitt (h2) - und der Kochmodus, der aus einem Meter
   *            Entfernung gelesen wird
   *   title    Karte, Eintrag, Zeile, Formulargruppe (h3 und ihresgleichen)
   *   body     Fliesstext, Eingabefelder, Knopfbeschriftungen
   *   label    Beschriftung, Navigation, Meldung - alles, was gelesen werden
   *            MUSS, aber nicht Fliesstext ist
   *   small    Meta, Hinweis, Fehlertext, Herkunft, Datum
   *   micro    Abzeichen und Rubrik ueber einer Ueberschrift
   *
   * Nichts ist kleiner geworden: label bleibt bei 0,95rem (die Navigation
   * darf nicht schrumpfen), small hebt 0,85 und 0,82 auf 0,9, micro hebt das
   * Abzeichen von 0,72 auf 0,8. Keine neue Schriftfamilie, kein Webfont - der
   * Stack oben bleibt, wie er ist.
   */
  --ep-type-page: 1.5rem;
  --ep-type-page-wide: 1.85rem;
  --ep-type-section: 1.15rem;
  --ep-type-title: 1.05rem;
  --ep-type-body: 1rem;
  --ep-type-label: 0.95rem;
  --ep-type-small: 0.9rem;
  --ep-type-micro: 0.8rem;

  /*
   * Drei Zeilenhoehen: eng fuer Ueberschriften und Titel, mittel fuer dichte
   * Kartenzeilen, weit fuer alles, was man liest. Vorher waren es neun Werte
   * zwischen 1,25 und 1,6 - der Unterschied zwischen 1,5 und 1,55 ist auf dem
   * Bildschirm niemandem aufgefallen, in der Datei aber jedem.
   */
  --ep-lh-tight: 1.25;
  --ep-lh-meta: 1.4;
  --ep-lh-body: 1.55;

  /* Hoehe der mobilen Navigationsleiste - eine Groesse fuer die Leiste, den
     Platz unter dem Inhalt und den Scroll-Abstand (Sprint 009). */
  --ep-nav-height: 4.5rem;

  /* Safe Area: Home-Indikator und Kameraaussparung. env() steht nur hier - der
     Browsertest ueberschreibt diese drei Werte, um Geraete nachzustellen. */
  --ep-sa-bottom: env(safe-area-inset-bottom, 0px);
  --ep-sa-left: env(safe-area-inset-left, 0px);
  --ep-sa-right: env(safe-area-inset-right, 0px);
}

/*
 * DUNKEL - ZWEI WEGE, EIN TOKENSATZ (Entwurf E3).
 *
 * Es gibt genau zwei Arten, dunkel zu sein:
 *
 *   1. Die Familie hat "Dunkel" gewaehlt. Dann steht `data-theme="dark"` am
 *      html-Element, und zwar serverseitig im ersten Byte.
 *   2. Sie hat nichts gewaehlt ("System"). Dann steht KEIN Attribut da, und
 *      `prefers-color-scheme` entscheidet - genau wie vor E3.
 *
 * Das `:not([data-theme])` in der zweiten Regel ist der ganze Unterschied:
 * Ohne es ueberschriebe die Systemvorgabe eine ausdrueckliche Wahl, und "Hell"
 * waere auf einem dunkel eingestellten Telefon wirkungslos.
 *
 * Die Werte stehen zweimal da, und das ist Absicht: Eine Media-Abfrage umfasst
 * ganze Regeln, also laesst sich derselbe Block nicht aus beiden Kontexten
 * heraus referenzieren. Die Alternative waere ein zweiter Satz
 * Zwischentoken - mehr Namen, mehr Indirektion, derselbe Umfang. Dass beide
 * Bloecke gleich bleiben, sichert StylesheetContractTest zu, nicht die
 * Aufmerksamkeit des naechsten Lesers.
 */
:root[data-theme="dark"] {
  --ep-primary: #a8d5b6;
  --ep-on-primary: #163122;
  --ep-soft: #2a3b30;
  --ep-text: #f4f5f0;
  --ep-muted: #abb7ae;
  --ep-surface: #202a24;
  --ep-bg: #141b18;
  --ep-line: #3c4940;
  --ep-input: #8b9b90;
  --ep-danger: #ffb4ab;
  --ep-warning: #f0b35a;
  --ep-yellow: #c99700;
  /* Im Dunkelmodus liegt der Balken auf dunklem Grund - dort ist der helle
     Ton der richtige und erreicht bereits ueber 5 : 1. */
  --ep-preference-yellow: #c99700;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme]) {
    --ep-primary: #a8d5b6;
    --ep-on-primary: #163122;
    --ep-soft: #2a3b30;
    --ep-text: #f4f5f0;
    --ep-muted: #abb7ae;
    --ep-surface: #202a24;
    --ep-bg: #141b18;
    --ep-line: #3c4940;
    --ep-input: #8b9b90;
    --ep-danger: #ffb4ab;
    --ep-warning: #f0b35a;
    --ep-yellow: #c99700;
    /* Im Dunkelmodus liegt der Balken auf dunklem Grund - dort ist der helle
       Ton der richtige und erreicht bereits ueber 5 : 1. */
    --ep-preference-yellow: #c99700;
  }
}

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

body {
  margin: 0;
  background: var(--ep-bg);
  color: var(--ep-text);
  font-family: var(--ep-font);
  font-size: var(--ep-type-body);
  line-height: var(--ep-lh-body);
  -webkit-text-size-adjust: 100%;
  /* Ein langes Wort ohne Leerzeichen - etwa ein manueller Artikelname in einer
     Meldung - bricht um, statt die Seite waagerecht scrollen zu lassen
     (Sprint 008, Abschlussreview). break-word aendert keine Mindestbreite:
     Tabellen und Flex-Zeilen bleiben, wie sie sind. */
  overflow-wrap: break-word;
}

.container {
  width: 100%;
  max-width: var(--ep-content-max);
  margin-inline: auto;
  padding-inline: var(--ep-s4);
}

/*
 * Der Inhalt bleibt auf Lesemass, auch wenn die Shell breiter wird. Kopfzeile
 * und Fussbereich nutzen dagegen die volle Shellbreite - dort steht kein
 * Fliesstext, sondern Marke, Haushalt und Version.
 */
main.container {
  max-width: var(--ep-text-max);
}

/* Ausnahme fuer Seiten, deren Inhalt ein Kartenraster ist. */
main.container--wide {
  max-width: var(--ep-content-max);
}

/*
 * Die Shell: unter 48rem eine Spalte mit fester Leiste unten, ab 48rem
 * Seitenleiste und Inhalt nebeneinander. Die Navigation steht deshalb neben
 * dem Inhalt und nicht mehr im header (resources/views/layouts/app.php).
 */
.app-shell {
  width: 100%;
}

.skip-link {
  position: absolute;
  left: -999px;
}

.skip-link:focus {
  left: var(--ep-s4);
  top: var(--ep-s4);
  z-index: 10;
  background: var(--ep-surface);
  padding: 0.5rem 0.75rem;
  border-radius: var(--ep-radius);
}

.site-header {
  background: var(--ep-surface);
  border-bottom: 1px solid var(--ep-line);
}

.site-header__inner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0 var(--ep-s4);
  min-height: 3.5rem;
}

.site-header__brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  color: inherit;
  font-weight: 600;
  text-decoration: none;
}

.site-header__mark {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: 8px;
  background: var(--ep-primary);
  color: var(--ep-on-primary);
  font-size: var(--ep-type-micro);
  font-weight: 700;
  letter-spacing: 0.02em;
}

main {
  padding-block: 2rem 3rem;
}

/*
 * Die Kartenflaeche - einmal, fuer alle vier Karten.
 *
 * .card ist die Sektionskarte und traegt rund neunzig Stellen. Die drei
 * uebrigen stehen in einem Raster und bringen ihre eigene Innenaufteilung mit:
 * .recipe-card das Bild bis an den Rand, .day-card den Kopf mit Wochentag,
 * .alternative die Begruendung. Gemeinsam ist ihnen nur die Flaeche, und die
 * steht deshalb nur hier.
 *
 * Vorher hatte jede ihre eigene Kopie - und drei davon den Eingaberadius
 * (12 px) statt des Kartenradius (20 px, Styleguide Abschnitt 2). Das war kein
 * Entwurf, sondern uebersehen: .today-card, .member-card, .shopping-section und
 * .family-notes komponieren laengst mit .card und waren deshalb immer richtig.
 *
 * Bewusst KEIN gemeinsames Polster: .card polstert sich selbst, .recipe-card
 * polstert im Rumpf, .day-card und .alternative haben ihr eigenes Mass. Und
 * bewusst keine Komposition im Markup (class="card day-card"): Die Regel
 * ".card + .card" setzt einen Abstand nach oben - in einem Raster schoebe das
 * die Zellen auseinander.
 */
.card,
.recipe-card,
.day-card,
.alternative {
  background: var(--ep-surface);
  border: 1px solid var(--ep-line);
  border-radius: var(--ep-card-radius);
}

.card {
  padding: var(--ep-s6);
}

/*
 * Modifikatoren der Kartenfamilie stehen doppelt: ".card.card--error" statt
 * ".card--error".
 *
 * Die gemeinsame Flaeche oben deklariert "border" und "background". Ein
 * einfacher Modifikator ist genauso spezifisch und gewinnt nur, weil er
 * weiter unten steht - verschoebe jemand die Flaeche ans Dateiende, waere der
 * Rahmen wieder durchgezogen und niemand saehe den Grund. Die doppelte Klasse
 * macht das Ueberschreiben zur Absicht statt zur Reihenfolge. Kein
 * "!important": Das waere dieselbe Aussage, nur ohne Rueckweg.
 *
 * Nur Modifikatoren, die die Flaeche wirklich ueberschreiben, stehen doppelt.
 * .card--narrow (Breite), .day-card--dragging
 * (Transparenz) und .day-card--dragover (Umriss) kommen der Flaeche nicht in
 * die Quere und bleiben einfach.
 */
.card.card--error {
  border-left: 4px solid var(--ep-danger);
}

/*
 * Die Rubrik ueber der Ueberschrift: "Gerichtspool", "Wochenplan", "Fehler
 * 404". Kurz, von uns geschrieben, immer dieselbe - Grossbuchstaben und
 * Sperrung sind hier Rubrik, nicht Dekoration.
 */
.eyebrow {
  margin: 0 0 0.35rem;
  color: var(--ep-muted);
  font-size: var(--ep-type-micro);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/*
 * Steht dort ein NAME aus der Datenbank, faellt beides weg.
 *
 * "OFENGEMÜSE MIT FETA, KICHERERBSEN, ZA'ATAR UND KARAMELLISIERTEN ZWIEBELN"
 * ist keine Rubrik mehr, sondern ein gesperrter Grossbuchstabenblock: Deutsche
 * Woerter werden lang, und Versalien nehmen ihnen die Wortform, an der man sie
 * beim Lesen erkennt. Der Name bleibt in seiner eigenen Schreibweise; klein
 * und leise ist er trotzdem.
 */
.eyebrow--name {
  letter-spacing: normal;
  text-transform: none;
  font-size: var(--ep-type-small);
}

h1 {
  margin: 0 0 0.75rem;
  font-size: var(--ep-type-page);
  line-height: var(--ep-lh-tight);
}

.lead {
  margin: 0 0 1rem;
  color: var(--ep-muted);
}

.muted {
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
}

a {
  color: var(--ep-primary);
}

/*
 * Pruefliste des Installers (installer/system.php, abbruch.php,
 * vorhandene-env.php). Bewusst OHNE Haekchen vor den Punkten: Dort steht auch,
 * was nicht in Ordnung ist - ein Haken vor einer fehlgeschlagenen Pruefung
 * waere eine falsche Aussage. Bis Schritt 5 gab es dafuer gar keine Regel; das
 * Stylesheet kannte nur eine ungenutzte ".checklist" mit Haken.
 */
.check-list {
  margin: 0 0 var(--ep-s4);
  padding: 0;
  list-style: none;
}

.check-list li + li {
  margin-top: var(--ep-s2);
}

/*
 * Das hidden-Attribut gewinnt gegen jede eigene display-Regel.
 *
 * Das Standard-Stylesheet des Browsers setzt fuer [hidden] nur
 * display: none - jede Autorenregel schlaegt das. Die Knoepfe der
 * Formulareditoren stehen mit hidden im HTML und werden erst vom Skript
 * sichtbar gemacht; ohne diese Regel standen sie auch ohne JavaScript da
 * und taten nichts. Beim adversarialen Review zu Sprint 004 gefunden,
 * betrifft auch den Rezepteditor aus Sprint 003.
 */
[hidden] {
  display: none !important;
}

/*
 * Hauptaktion. inline-flex statt inline-block, damit die Beschriftung in der
 * Mindesthoehe mittig sitzt - sonst haengt sie bei kurzen Texten oben und der
 * Knopf sieht kaputt aus. Umbruch bleibt erlaubt: lange Beschriftungen wie
 * "Naechste Woche planen" duerfen auf schmalen Telefonen zweizeilig werden,
 * statt ueber den Rand zu laufen.
 */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ep-s2);
  min-height: var(--ep-target-primary);
  padding: var(--ep-s3) var(--ep-s4);
  border: 0;
  border-radius: var(--ep-radius);
  background: var(--ep-primary);
  color: var(--ep-on-primary);
  /* "font: inherit" MUSS vor dem Gewicht stehen. Die Kurzschreibweise setzt
     font-weight mit zurueck; stand sie danach, war der Knopf nicht halbfett.
     Genau das war bis Schritt 5 der Fall - eine zweite .button-Regel weiter
     unten hob das Gewicht auf, gemessen 400 statt 600. */
  font: inherit;
  font-weight: 600;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
}

.button:hover,
.button:focus-visible {
  filter: brightness(1.08);
}

/*
 * Ein Knopf, der etwas endgueltig entfernt.
 *
 * Bisher gab es keinen: Kategorien und Zutaten werden mit einer leisen
 * Schaltflaeche entfernt und sind in Sekunden wieder angelegt. Ein
 * Kocheintrag nimmt die Bewertungen mehrerer Menschen mit - dieser eine
 * Knopf steht auf einer eigenen Bestaetigungsseite und darf aussehen wie
 * das, was er tut (Sprint 005).
 */
.button--danger {
  background: var(--ep-danger);
  color: var(--ep-on-primary);
}

/*
 * AKTIONSGRUPPEN - zwei oder mehr Aktionen, die raeumlich zusammengehoeren.
 *
 * Eine Aktionsgruppe ist Layout, sonst nichts. Sie entscheidet nicht, WAS eine
 * Aktion ist: Ein Weg zu einer anderen Seite bleibt ein <a>, eine
 * Zustandsaenderung bleibt ein eigenes POST-Formular. Genau deshalb duerfen
 * Link, Formular und destruktives Formular nebeneinander stehen, ohne
 * zusammengefuehrt zu werden - im Flex-Container wird aus einem
 * inline-Formular ohnehin ein Block, und die Formulare bleiben getrennt. Ein
 * Vertragstest haelt fest, dass kein Formular in einem anderen steht.
 *
 * Der Name sagt, WO die Gruppe steht: Formularabschluss, Seitenkopf, Karte,
 * Zeile, Filter, leerer Zustand, Tageskarte. Das Layout kommt ab hier aus
 * EINER Regel statt aus neun - vorher trugen neun Regeln dieselben
 * Deklarationen mit vier verschiedenen Abstaenden (8, 12, 14,4 und 16 px),
 * einer davon ausserhalb der Abstandsskala - und 12 px standen einmal als
 * "0.75rem" und einmal als Token da. Wer abweichen will, braucht einen Grund;
 * die Gruende stehen bei den Varianten.
 *
 * Umbruch statt Abschneiden: Auf 320 px stapeln sich die Aktionen
 * untereinander, in der Reihenfolge des Markups. Kein "order", kein
 * "row-reverse" - die sichtbare Reihenfolge ist die vorgelesene.
 */
.form__actions,
.page-head__actions,
.today-card__actions,
.cooking-mode__actions,
.filters__actions,
.recipe-card__actions,
.empty-state__actions,
.stack-list__actions,
.day-card__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ep-s3);
}

/*
 * Ein Formular in einer Aktionsgruppe traegt keinen eigenen Abstand - den
 * Abstand macht die Gruppe. Bisher stand diese Absicherung zweimal einzeln da
 * (.quick-actions form, .cooking-mode__actions .inline-form) und fehlte bei
 * allen anderen Gruppen.
 */
.form__actions form,
.page-head__actions form,
.today-card__actions form,
.cooking-mode__actions form,
.filters__actions form,
.recipe-card__actions form,
.empty-state__actions form,
.stack-list__actions form,
.day-card__actions form,
.quick-actions form {
  margin: 0;
}

.debug {
  margin-top: 1.25rem;
  background: var(--ep-soft);
  border-radius: var(--ep-radius);
  padding: 0.75rem 1rem;
  font-size: var(--ep-type-small);
}

.debug pre {
  overflow-x: auto;
  white-space: pre;
}

.site-footer {
  border-top: 1px solid var(--ep-line);
  padding-block: 1.25rem;
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
}

/*
 * Eine Fokusdarstellung fuer alles, was man bedienen kann - auch fuer
 * Kaestchen, Auswahlfelder und den Dateiwaehler, die sich bisher auf den
 * Browserstandard verliessen. Drei Pixel in der Hauptfarbe, ueberall
 * dieselben.
 *
 * :where() hat keine Spezifitaet: Eine spaetere Regel kann den Abstand
 * anpassen, ohne dass hier etwas mit !important verteidigt werden muesste.
 * Entfernt wird der Ring nirgends - ein Vertragstest haelt fest, dass
 * "outline: none" in dieser Datei nicht vorkommt.
 */
:where(a, button, summary, input, select, textarea):focus-visible {
  outline: 3px solid var(--ep-primary);
  outline-offset: 1px;
}

/*
 * Links, Knoepfe und aufklappbare Zeilen bekommen mehr Luft: Sie haben keinen
 * eigenen Rahmen, an dem der Ring anliegen koennte.
 */
:where(a, button, summary):focus-visible {
  outline-offset: 3px;
}

@media (min-width: 48rem) {
  h1 {
    font-size: var(--ep-type-page-wide);
  }
}

/* 359 px aus dem Designpaket, hier in rem, damit alle Umschaltpunkte dieselbe
   Einheit haben: schmale Telefone bekommen 20 px Kartenpadding statt 24. */
@media (max-width: 22.4375rem) {
  .card {
    padding: var(--ep-s5);
  }
}

/* --------------------------------------------------------------------------
 * Kopfzeile und Navigation (Sprint 009, Fachkonzept Kapitel 61)
 *
 * Mobil ist der Standard ohne Media Query: fuenf Bereiche fest am unteren
 * Rand. Ab 64rem die Desktop-Leiste unter der Kopfzeile. Umgeschaltet wird
 * ausschliesslich hier, ueber genau eine min-width-Abfrage - nie ueber das
 * hidden-Attribut, das jede Media Query schlaegt.
 *
 * Warum 64rem: Telefone quer und Tablets im Hochformat sind Daumengeraete und
 * behalten die fuenf Reiter. Ein Tablet quer (ab 1024 px) bekommt die
 * Desktop-Leiste - beim Drehen wechselt die Navigation, die Trefferflaechen
 * bleiben wegen des groben Zeigers gross (Abschlussreview). Der Inhalt ist ab
 * 736 px Fensterbreite ohnehin gleich breit.
 * -------------------------------------------------------------------------- */

.site-header__meta {
  display: flex;
  /* Mindestens 8rem neben der Marke, sonst in die naechste Zeile: Bei grosser
     Schrift auf einem schmalen Telefon stand der Haushaltsname sonst Buchstabe
     fuer Buchstabe untereinander (Abschlussreview, Browserpruefung). */
  flex: 1 1 8rem;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 0 0.75rem;
  min-width: 0;
}

.site-header__household {
  min-width: 0;
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
  text-align: right;
  overflow-wrap: anywhere;
}

/* Konto und Abmelden: mobil unter "Mehr", ab 64rem in der Kopfzeile. Ohne
   Haushalt gibt es keine Bereiche - dann stehen beide immer hier. */
.site-header__account {
  display: none;
  align-items: center;
  gap: 0.25rem;
}

.site-header__meta--reduced .site-header__account {
  display: flex;
}

.site-header__logout {
  margin: 0;
}

.site-header__link {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
  padding: 0 0.6rem;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--ep-muted);
  font: inherit;
  font-size: var(--ep-type-label);
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}

.site-header__link.is-active {
  border-bottom-color: var(--ep-primary);
  color: var(--ep-text);
}

.site-nav__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Die feste Leiste. Die Hoehe haben die Links; das Polster fuer die Safe Area
   sitzt am Kasten. Eine Mindesthoehe am gepolsterten Kasten zoege den
   Home-Indikator von der Trefferflaeche ab. Die obere Linie ist ein Schatten,
   damit sie keine Hoehe kostet. */
.site-nav__list--mobile {
  position: fixed;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 20;
  display: grid;
  /* Fuenf gleich breite Spalten, die nie breiter werden als der Bildschirm.
     Reicht der Platz nicht - grosse Schrift, schmales Telefon -, bricht die
     Beschriftung um, statt "Mehr" hinter den Rand zu schieben (Abschlussreview;
     gemessen bei 320 bis 430 px und 100 bis 150 % Schrift). */
  grid-template-columns: repeat(5, minmax(0, 1fr));
  padding: 0 var(--ep-sa-right) var(--ep-sa-bottom) var(--ep-sa-left);
  background: var(--ep-surface);
  box-shadow: 0 -1px 0 var(--ep-line);
}

.site-nav__tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.15rem;
  /* Mindesthoehe statt fester Hoehe: Zwei Zeilen Beschriftung passen hinein. */
  min-height: var(--ep-nav-height);
  /* Ohne Seitenpolster: Bei 150 % Schrift auf 320 px passen "Heute" und "Woche"
     sonst nicht mehr in ihre Spalte und brechen mitten im Wort um. */
  padding: 0;
  border-top: 3px solid transparent;
  color: var(--ep-muted);
  /* Keine kleinere Schrift, damit es passt (Fachkonzept Kapitel 58): Es bricht um. */
  font-size: var(--ep-type-label);
  font-weight: 600;
  line-height: 1.1;
  text-align: center;
  text-decoration: none;
  overflow-wrap: anywhere;
  /* Safari trennt nur mit Praefix (Sprint 010). Keine weichen Trennstriche ohne
     Gerätebefund - die mobile Navigation ist eingefroren. */
  -webkit-hyphens: auto;
  hyphens: auto;
}

/* Aktiv: Balken und Farbe, keine fette Schrift - die Breite springt nicht. */
.site-nav__tab.is-active {
  border-top-color: var(--ep-primary);
  color: var(--ep-text);
}

.site-nav__tab:focus-visible {
  outline-offset: -3px;
}

.site-nav__icon {
  flex: none;
}

.site-nav__list--desktop {
  display: none;
}

/* Die Liste erscheint erst ab 48rem, und zwar als Seitenleiste. Der aktive
   Zustand traegt links einen Balken: Der einzige erlaubte forced-colors-Block
   setzt border-color, deshalb bleibt es eine Rahmenfarbe und keine Flaeche. */
.site-nav__link {
  display: flex;
  align-items: center;
  min-height: 2.75rem;
  padding: var(--ep-s2) var(--ep-s4);
  border-inline-start: 3px solid transparent;
  color: var(--ep-muted);
  font-size: var(--ep-type-label);
  font-weight: 600;
  text-decoration: none;
}

.site-nav__link.is-active {
  border-inline-start-color: var(--ep-primary);
  color: var(--ep-text);
}

/* Platz fuer die feste Leiste - nur, wenn sie gerendert wird. Der Abstand
   sitzt am body, damit auch der Footer frei bleibt; scroll-padding haelt ein
   per Tastatur fokussiertes Element ueber der Leiste (WCAG 2.4.11). */
.has-bottom-nav {
  scroll-padding-bottom: calc(var(--ep-nav-height) + var(--ep-sa-bottom) + 0.5rem);
}

.has-bottom-nav body {
  padding-bottom: calc(var(--ep-nav-height) + var(--ep-sa-bottom));
}

/*
 * Ab 50rem wird aus der unteren Leiste eine Seitenleiste (Styleguide Abschnitt 3).
 * Die Navigation steht dafuer als erstes Kind von .app-shell neben dem Inhalt.
 * Die Leiste laeuft im Fluss mit, nicht fest positioniert - eine feste Leiste
 * laege ueber dem fokussierten Skip-Link.
 *
 * 50rem statt der 48rem des Styleguides, gemessen: Bei 768 px bleiben nach der
 * 216 px breiten Leiste 505 px Inhalt. Zwei Gerichtekarten bekaemen davon je
 * 244 px - der Styleguide verlangt im selben Absatz mindestens 260 px. Ab
 * 800 px geht die Rechnung auf. Klassische 768-px-Tablets behalten damit die
 * untere Leiste, die am Touchgeraet ohnehin besser erreichbar ist.
 */
@media (min-width: 50rem) {
  .app-shell {
    display: grid;
    grid-template-columns: var(--ep-sidebar-width) minmax(0, 1fr);
    max-width: var(--ep-content-max);
    margin-inline: auto;
  }

  .site-nav {
    border-inline-end: 1px solid var(--ep-line);
    padding-block: var(--ep-s4);
  }

  .site-nav__list--mobile {
    display: none;
  }

  /* Sieben Punkte untereinander. Bricht eine Beschriftung mit grosser Schrift
     um, waechst die Zeile - die Leiste versteckt keinen Punkt. */
  .site-nav__list--desktop {
    display: flex;
    flex-direction: column;
    gap: var(--ep-s1);
  }

  .has-bottom-nav {
    scroll-padding-bottom: 0;
  }

  .has-bottom-nav body {
    padding-bottom: 0;
  }
}

/*
 * Konto und Abmelden bleiben bis 64rem unter "Mehr". Der Umschaltpunkt ist
 * bewusst ein anderer als der der Navigation: Zwischen 48rem und 64rem ist die
 * Kopfzeile schmal, und beide Wege sind ueber die Seitenleiste erreichbar.
 */
@media (min-width: 64rem) {
  .site-header__account {
    display: flex;
  }
}

/* Erzwungene Farben (etwa der Windows-Kontrastmodus): Der Browser ersetzt auch
   transparente Balken durch die Textfarbe. Ohne diese Regeln truege jeder Reiter
   denselben Balken, und die feste Leiste verloere ihre Trennlinie, die sonst ein
   Schatten ist (Abschlussreview). */
@media (forced-colors: active) {
  .site-nav__tab,
  .site-nav__link,
  .site-header__link {
    border-color: Canvas;
  }

  .site-nav__tab.is-active,
  .site-nav__link.is-active,
  .site-header__link.is-active {
    border-color: Highlight;
  }

  .site-nav__list--mobile {
    border-top: 1px solid CanvasText;
  }
}

@media print {
  .site-nav,
  .site-header__account {
    display: none !important;
  }

  .has-bottom-nav body {
    padding-bottom: 0;
  }
}

/* --------------------------------------------------------------------------
 * Dashboard und Heute (Sprint 009, Fachkonzept Kapitel 8)
 * -------------------------------------------------------------------------- */

.page-intro {
  margin-bottom: var(--ep-s4);
}

.page-intro h1 {
  margin-bottom: 0.25rem;
}

.page-intro .lead {
  margin-bottom: 0;
}

.today-card__plan,
.today-card__note,
.today-card__meta,
.today-card__family,
.today-card__cooked {
  margin: 0 0 0.5rem;
}

/*
 * Ausnahme von der Rollenskala, mit Grund: Das Gericht von heute ist der
 * Inhalt seiner Seite, nicht eine Karte unter vielen. Zwischen Seitentitel
 * (1,5rem) und Abschnitt (1,15rem) hat es eine eigene Groesse - eine Rolle,
 * die es genau einmal gibt, wird kein Token.
 */
.today-card__title {
  margin: 0 0 0.5rem;
  font-size: 1.35rem;
  line-height: var(--ep-lh-tight);
  overflow-wrap: anywhere;
}

.today-card__cooked {
  font-weight: 600;
}

/* Variante: Abstand zum Kartentext darueber. */
.today-card__actions {
  margin-top: var(--ep-s4);
}

/* Ein leiser Link, der fuer sich steht - nicht im Fliesstext: 44 px hoch. */
.link-quiet--standalone {
  display: inline-flex;
  align-items: center;
  min-height: 2.75rem;
}

/*
 * Die Schnellaktionen sind KEINE Aktionsgruppe: Hier steht nichts
 * nebeneinander, sondern jede Aktion in ihrer eigenen Zeile ueber die volle
 * Breite - auf dem Telefon die sicherste Trefferflaeche.
 *
 * Ab 40rem stehen sie in Spalten. Ein Knopf ueber die ganze Breite eines
 * 1280-px-Fensters ist keine Hilfe mehr, sondern eine Flaeche; die volle
 * Breite behaelt ihren Sinn innerhalb der Spalte.
 */
.quick-actions {
  display: grid;
  gap: var(--ep-s2);
  margin: var(--ep-s3) 0 0;
  padding: 0;
  list-style: none;
}

@media (min-width: 40rem) {
  .quick-actions {
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  }
}


.today-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1.25rem;
  margin: var(--ep-s4) 0 0;
}

/* Lange Inhalte (Sprint 009): ein Name mit 120 Zeichen, eine Notiz mit langer
   Adresse oder ein langer eigener Eintrag bricht um, statt die Seite waagerecht
   scrollen zu lassen. Nie fuer Mengen und nie fuer die Leiste. */
.today-card__title,
.day-card__title,
.variant-card p,
.family-list__note,
.notice,
.site-header__meta {
  min-width: 0;
  overflow-wrap: anywhere;
}

.recipe-detail__cook {
  margin: 0 0 1rem;
}

/* --------------------------------------------------------------------------
 * Kochmodus (Sprint 009, Fachkonzept Kapitel 33)
 *
 * Zum Kochen gebaut: groessere Schrift, Mengen in einer eigenen Spalte, die
 * nie umbricht, Schritte mit Luft. Keine eigene Farbe - der Kochmodus ist
 * dieselbe Anwendung, nur naeher am Herd.
 * -------------------------------------------------------------------------- */

.cooking-mode__head {
  margin-bottom: var(--ep-s4);
}

.cooking-mode__head h1 {
  margin-bottom: 0.25rem;
  overflow-wrap: anywhere;
}

.cooking-mode__meta {
  margin: 0 0 0.5rem;
  font-size: var(--ep-type-title);
}

.cooking-mode__hint {
  margin: 0 0 0.35rem;
}

.cooking-mode__cookidoo {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 3rem;
  font-size: var(--ep-type-section);
}

.cooking-mode__notes {
  overflow-wrap: anywhere;
}

.cooking-mode__ingredients .ingredient-list__item {
  font-size: var(--ep-type-section);
}

/* Die Menge richtet sich nach ihrem Text und bricht nie um: "1,5 EL" auf zwei
   Zeilen liest am Herd niemand richtig. */
.cooking-mode__ingredients .ingredient-list__amount {
  flex: 0 0 auto;
  min-width: 5.5rem;
  white-space: nowrap;
}

.cooking-mode__steps .step-list__item {
  margin-bottom: 1rem;
  font-size: var(--ep-type-section);
  line-height: var(--ep-lh-body);
  overflow-wrap: anywhere;
}

.cooking-mode__cooked {
  font-weight: 600;
}

/* --------------------------------------------------------------------------
 * Mehr (Sprint 009, Fachkonzept Kapitel 61)
 * -------------------------------------------------------------------------- */

/*
 * Die Gruppenueberschrift ist leise und benutzt dafuer die vorhandene
 * Beschriftung (.eyebrow--name, also ohne Versalien) statt einer eigenen
 * Typografie: Sie sortiert die Ziele, sie konkurriert nicht mit ihnen. Eine
 * Ueberschrift, die lauter ist als das, was unter ihr steht, liest sich wie ein
 * eigener Bereich. Eigen ist hier nur der Abstand zur Gruppe davor.
 */
.more-list + .more-group {
  margin-top: var(--ep-s4);
}

.more-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.more-list li + li {
  border-top: 1px solid var(--ep-line);
}

/*
 * Der Winkel rechts ist reine Dekoration und steht deshalb im Stylesheet, nicht
 * im Markup: Vorgelesen wuerde "Familie ›" nichts hinzufuegen.
 */
.more-list__link {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.1rem;
  min-height: 2.75rem;
  padding: 0.5rem 1.25rem 0.5rem 0;
  position: relative;
  font-size: var(--ep-type-title);
  /*
   * Der Strich gehoert unter den Namen, nicht unter die Erklaerung: Sonst
   * sehen beide Zeilen gleich wichtig aus, und die Erklaerung liest sich wie
   * ein zweiter Verweis. Die Unterstreichung bleibt - ohne sie waere ein
   * Verweis nur noch an seiner Farbe zu erkennen.
   */
  text-decoration: none;
}

.more-list__name {
  text-decoration: underline;
}

.more-list__link::after {
  content: "›";
  position: absolute;
  top: 50%;
  right: 0.15rem;
  transform: translateY(-50%);
  color: var(--ep-muted);
}

.more-list__hint {
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
}

.more-logout {
  margin: var(--ep-s4) 0 0;
}

.card + .card {
  margin-top: var(--ep-s4);
}

.card--narrow {
  max-width: 34rem;
}

.card.card--muted {
  background: var(--ep-soft);
  border-style: dashed;
}

/*
 * Drei Ebenen, drei erkennbare Groessen: Seite 1,5rem, Abschnitt 1,15rem,
 * Untergliederung 1,05rem.
 *
 * h3 stand bisher gar nicht im Stylesheet und nahm die Browservorgabe von
 * 1,17em - damit war die Untergliederung GROESSER als der Abschnitt darueber.
 * Die drei h3 im Bestand tragen eigene Klassen und haben das nie gezeigt; die
 * naechste haette es.
 */
h2 {
  margin: 0 0 0.5rem;
  font-size: var(--ep-type-section);
  line-height: var(--ep-lh-tight);
}

h3 {
  margin: 0 0 0.5rem;
  font-size: var(--ep-type-title);
  line-height: var(--ep-lh-tight);
}

/* --- Meldungen ------------------------------------------------------------ */

/*
 * Meldungen.
 *
 * Die Grundform ist die AUSKUNFT - der haeufigste Fall: "Foto nicht lesbar",
 * "Heute schon als gekocht eingetragen", "Fuer diesen Tag gibt es gerade keine
 * Alternative". Weder gut noch schlecht, sondern erklaerend.
 *
 * Vorher war die Grundform gruen und damit faktisch die Erfolgsmeldung,
 * waehrend die Auskunft ein Modifikator war - bei zwoelf Auskuenften gegen
 * eine Erfolgsmeldung. Und .notice--success stand zwar im Markup, hatte aber
 * ueberhaupt keine Regel: Die Erfolgsmeldung sah nur deshalb richtig aus, weil
 * die Grundform zufaellig ihre Farbe trug.
 *
 * Der Balken links traegt die Unterscheidung, der Text die Bedeutung. Eine
 * Meldung ohne Text gibt es nicht - Farbe allein sagt nichts.
 */
.notice {
  margin: 0 0 var(--ep-s4);
  padding: var(--ep-s3) var(--ep-s4);
  border: 1px solid transparent;
  border-left: 4px solid var(--ep-muted);
  border-radius: var(--ep-radius);
  background: var(--ep-soft);
  font-size: var(--ep-type-label);
}

/*
 * Doppelte Klasse wie in der Karten- und der Abzeichenfamilie: Ein Modifikator
 * ueberschreibt die Grundform mit Absicht, nicht wegen der Reihenfolge.
 */
.notice.notice--success {
  border-left-color: var(--ep-primary);
}

/* Kein Fehler, aber nicht zu uebersehen - etwa ein bestaetigtes Gericht, fuer
   das nichts auf der Einkaufsliste steht (Sprint 008). */
.notice.notice--warning {
  border-left-color: var(--ep-warning);
  background: color-mix(in srgb, var(--ep-warning) 12%, var(--ep-surface));
}

.notice.notice--error {
  border-left-color: var(--ep-danger);
  background: color-mix(in srgb, var(--ep-danger) 12%, var(--ep-surface));
}

/* --- Formulare ------------------------------------------------------------ */

/*
 * Der senkrechte Rhythmus eines Formulars kommt aus der Abstandsreihe:
 * Feld zu Feld --ep-s5, Beschriftung zum Control --ep-s1, Control zu Hinweis
 * und Fehler --ep-s1. Das ersetzt 1.25rem, 1.1rem, 0.3rem und 0.35rem - vier
 * Werte, die dasselbe meinten und sich nur nicht einig waren.
 *
 * NICHT aus der Reihe kommen: der 1-px-Rahmen, die Mindesthoehe der
 * Bedienelemente (--ep-target-primary, ein Fingerziel), der Eckenradius und
 * die 16 px Schriftgroesse gegen das Hineinzoomen auf iOS. Das sind keine
 * Abstaende, und --ep-s4 waere dort nur eine Zahl, die zufaellig passt.
 */
.form {
  margin-top: var(--ep-s5);
}

.field {
  margin-bottom: var(--ep-s5);
}

.field label {
  display: block;
  margin-bottom: var(--ep-s1);
  font-weight: 600;
  font-size: var(--ep-type-label);
}

/*
 * Die Haut eines Bedienelements - einmal, fuer alle.
 *
 * Rahmen, Ecke, Flaeche, Schrift: Das stand bisher fuenfmal fast gleich da -
 * im Feld, in der Filterleiste, in der Kategorien- und der Zutatenzeile und
 * am Verschiebefeld der Tageskarte. Vier dieser fuenf nahmen --ep-line als
 * Rahmen. Das ist der dekorative Token; er erreicht gegen die Flaeche keine
 * 3 : 1 (siehe Kommentar an --ep-input). Als einzige sichtbare Grenze eines
 * Bedienelements war er zu schwach - ein Filterfeld war schlechter zu
 * erkennen als dasselbe Feld in einem Formular.
 *
 * Jetzt tragen alle --ep-input. Das Mass bleibt bei jedem selbst: Ein
 * Filterfeld darf kompakter sein als ein Eingabefeld, aber es ist dasselbe
 * Bedienelement.
 */
.field input,
.field select,
.field textarea,
.filters__search input,
.filters__field select,
.category-row input,
.ingredient-row input[type="text"],
.day-card__move select {
  border: 1px solid var(--ep-input);
  border-radius: var(--ep-radius);
  background: var(--ep-surface);
  color: inherit;
  font: inherit;
  /* 16px verhindert das Hineinzoomen auf iOS. */
  font-size: var(--ep-type-body);
}

/*
 * Ein Bedienelement, eine Form - Textfeld, Auswahl und mehrzeiliges Feld
 * sehen gleich aus, weil sie dasselbe tun. Vorher hatten input/select und
 * textarea zwei getrennte Regeln mit unterschiedlichem Polster.
 */
.field input,
.field select,
.field textarea {
  width: 100%;
  min-height: var(--ep-target-primary);
  padding: var(--ep-s3);
}

/*
 * Ein Kaestchen ist kein Textfeld: Es bekommt weder die volle Breite noch die
 * Mindesthoehe eines Eingabefeldes. Die Trefferflaeche traegt das Label.
 */
.field input[type="checkbox"],
.field input[type="radio"] {
  width: auto;
  min-height: 0;
}

/*
 * Ein beanstandetes Feld bekommt einen roten Rahmen - aber nie NUR den. Der
 * Fehlertext steht darunter und ist ueber aria-describedby mit dem Feld
 * verbunden; die Farbe ist die Zugabe, nicht die Aussage.
 *
 * Bewusst an [aria-invalid="true"] und nicht an :invalid: Die Browserpruefung
 * faerbte sonst jedes leere Pflichtfeld schon beim ersten Laden rot, bevor
 * jemand etwas abgeschickt hat. Massgeblich ist das Ergebnis vom Server.
 */
.field input[aria-invalid="true"],
.field select[aria-invalid="true"],
.field textarea[aria-invalid="true"] {
  border-color: var(--ep-danger);
}

/*
 * Hilfetext ist Hilfe, keine Dekoration: Wer ihn braucht, um das Feld richtig
 * auszufuellen, muss ihn lesen koennen. Deshalb --ep-muted (4,5 : 1 gegen die
 * Flaeche) und nicht heller.
 */
.field__hint {
  margin: var(--ep-s1) 0 0;
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
}

/*
 * Die Fehlerliste erbt ihre Farbe NICHT von einem angenommenen weissen Grund.
 * Im Installer steht dieselbe Liste innerhalb eines bereits rot eingefaerbten
 * Meldungskastens; eine eigene Flaeche oder ein aufgehellter Rotton waere dort
 * Rot auf Rot.
 */
.field-errors {
  margin: var(--ep-s1) 0 0;
  padding-left: var(--ep-s5);
  color: var(--ep-danger);
  font-size: var(--ep-type-small);
}

/*
 * Variante: Der Formularabschluss steht mit Abstand unter dem letzten Feld.
 *
 * Er bleibt die kanonische Abschlussgruppe - auch im eingefrorenen Installer,
 * wo er die einzige Gruppe ist. Deshalb behaelt er seinen Namen: Ein
 * Umbenennen haette Installer-Markup angefasst.
 */
.form__actions {
  margin-top: var(--ep-s5);
}

/*
 * Zweitrangige Aktion. Der Rahmen nimmt die Feldrandfarbe, nicht die
 * Trennlinienfarbe: Eine Trennlinie ist dekorativ (Styleguide Abschnitt 2) und
 * erreicht gegen die Flaeche keine 3 : 1 - als einzige sichtbare Grenze eines
 * Bedienelements waere sie zu schwach.
 */
.button--quiet {
  background: transparent;
  border: 1px solid var(--ep-input);
  color: var(--ep-text);
}

/*
 * Ueber die volle Breite - fuer gestapelte Aktionen, die untereinander stehen.
 * Der einzige Massmodifikator der Knopffamilie: Drei fruehere Sonderregeln
 * (.today-card__actions .button, .more-logout__button, .quick-actions__button)
 * senkten nur eine Mindesthoehe unter das, was der Inhalt ohnehin ergibt -
 * gemessen 48,8 bis 50,8 px gegen eine Vorgabe von 44. Sie sind ersatzlos
 * entfallen; geblieben ist die Breite, die wirklich etwas tat.
 */
.button--block {
  display: flex;
  width: 100%;
}

.link-quiet {
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
}

.link-quiet--button {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--ep-type-small);
  text-decoration: underline;
  cursor: pointer;
}

/* --- Listen und Chips ----------------------------------------------------- */

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin: 0 0 1rem;
  padding: 0;
  list-style: none;
}

.chip {
  padding: 0.25rem 0.6rem;
  border-radius: 999px;
  background: var(--ep-soft);
  font-size: var(--ep-type-small);
}

/*
 * Abzeichen.
 *
 * Die Grundform ist RUHIG, nicht laut. Sie traegt Eigenschaften ("Thermomix",
 * "optional", "Standardvorrat") und Zustaende ohne Wertung ("Entwurf",
 * "Archiviert", "Nicht gesperrt"). Vorher war die Grundform kraeftig gruen und
 * das leise Abzeichen die Ausnahme - dadurch sah eine Eigenschaft aus wie ein
 * Erfolg, und "Unbekannter Zustand" sah aus wie "Bestätigt".
 *
 * Der Rahmen steht auf JEDER Variante, auch wo er auf der eigenen Flaeche
 * kaum auffaellt. Im Modus mit erzwungenen Farben ersetzt der Browser die
 * Hintergrundfarbe - ohne Rahmen bliebe vom Abzeichen dort nur Text im Text.
 * Der Rahmen wird ersetzt, aber nicht entfernt, und der gestrichelte Stil
 * ueberlebt ihn.
 *
 * Doppelte Klasse bei den Modifikatoren (".badge.badge--positive"): Sie
 * ueberschreiben Flaeche, Rahmen und Schriftfarbe der Grundform mit Absicht,
 * nicht wegen der Reihenfolge im Stylesheet.
 */
.badge {
  display: inline-block;
  margin-left: 0.35rem;
  padding: 0.1rem 0.45rem;
  border: 1px solid var(--ep-line);
  border-radius: 999px;
  background: var(--ep-line);
  color: var(--ep-text);
  font-size: var(--ep-type-micro);
  font-weight: 600;
  vertical-align: middle;
}

/* Gueltig, verbindlich, abgeschlossen. Sparsam - heute nur "Bestätigt" und
   "Aktiv". */
.badge.badge--positive {
  border-color: var(--ep-primary);
  background: var(--ep-primary);
  color: var(--ep-on-primary);
}

/* Einschraenkung, auf die jemand achten soll: "Pausiert", "Gesperrt bis …". */
.badge.badge--caution {
  border-color: var(--ep-warning);
  background: color-mix(in srgb, var(--ep-warning) 14%, var(--ep-surface));
  color: var(--ep-text);
}

/* Definiert und derzeit unbenutzt: Es gibt heute keinen Zustand, der das
   verdient. Eine Variante zu erfinden, damit sie vorkommt, waere Farbe ohne
   Aussage. */
.badge.badge--negative {
  border-color: var(--ep-danger);
  background: color-mix(in srgb, var(--ep-danger) 12%, var(--ep-surface));
  color: var(--ep-text);
}

/*
 * Ein gespeicherter Wert, den diese Version nicht deuten kann (Regeln 33
 * und 58). Gestrichelt und ungefuellt - erkennbar anders als jede gueltige
 * Variante, und nicht rot: "nicht interpretierbar" ist kein Fehlerzustand.
 * Die Bedeutung steht daneben immer im Text.
 */
.badge.badge--unknown {
  border-style: dashed;
  border-color: var(--ep-muted);
  background: var(--ep-surface);
  color: var(--ep-text);
}

.stack-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.stack-list__item {
  padding: 0.75rem 0;
  border-top: 1px solid var(--ep-line);
}

.stack-list__item:first-child {
  border-top: 0;
}

.stack-list__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem;
}

/* Variante: Bleibt rechts, auch wenn der Text davor umbricht - sonst springen
   die Aktionen je nach Laenge des Gerichtsnamens von rechts nach links.
   Der Umbruch kommt aus der Gruppenregel; bisher fehlte er hier, und zwei
   Aktionen neben einem langen Namen konnten auf 320 px aus der Zeile
   laufen. */
.stack-list__actions {
  margin-left: auto;
}

/*
 * Mitgliederzeilen: die Aktionen IMMER in einer eigenen Zeile, links unter dem
 * Namen (Feinschliff 05, Punkt 6).
 *
 * Gemessen bei 375 px: "Owner · Erwachsene(r) · eigener Zugang" schob die
 * Aktionen in die zweite Zeile, "Jonas · Kind" liess sie daneben stehen - eine
 * Liste aus abwechselnd 51 und 103 px hohen Zeilen, in der ein rechts
 * abgesetztes "Bearbeiten Deaktivieren" ebenso gut zum naechsten Namen gehoeren
 * konnte. Eine feste Zeile je Mitglied ist langweiliger und eindeutig.
 *
 * Ab 48rem passt beides nebeneinander; dann gilt wieder die Grundregel.
 */
.stack-list--members .stack-list__actions {
  width: 100%;
  margin-left: 0;
}

@media (min-width: 48rem) {
  .stack-list--members .stack-list__actions {
    width: auto;
    margin-left: auto;
  }
}

.stack-list__button {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 0.65rem 0.75rem;
  border: 1px solid var(--ep-line);
  border-radius: var(--ep-radius);
  background: var(--ep-surface);
  color: inherit;
  font: inherit;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}

.stack-list__button:hover,
.stack-list__button:focus-visible {
  border-color: var(--ep-primary);
}

.data-list {
  display: grid;
  grid-template-columns: minmax(8rem, auto) 1fr;
  gap: 0.35rem 1rem;
  margin: 0;
  font-size: var(--ep-type-label);
}

.data-list dt {
  color: var(--ep-muted);
}

.data-list dd {
  margin: 0;
}

@media (max-width: 30rem) {
  .data-list {
    grid-template-columns: 1fr;
    gap: 0 0;
  }

  .data-list dd {
    margin-bottom: 0.6rem;
  }
}

/* --------------------------------------------------------------------------
 * Sprint 002: Gerichtspool
 * -------------------------------------------------------------------------- */

/*
 * Mehrzeilige Felder teilen sich Flaeche, Rahmen und Polster jetzt mit input
 * und select (siehe oben). Hier bleibt nur, was ein textarea eigen hat: Es
 * waechst nach unten, nicht zur Seite.
 */
.field textarea {
  line-height: var(--ep-lh-body);
  resize: vertical;
}

/*
 * Der Dateiwaehler zeichnet seinen Knopf selbst und vertraegt weder unser
 * Polster noch unseren Rahmen - beides wuerde der Browser doppelt zeichnen.
 * Die Fokusdarstellung ist dieselbe wie ueberall.
 */
.field input[type="file"] {
  min-height: var(--ep-target);
  padding: var(--ep-s2) 0;
  border: 0;
  background: none;
  font-size: var(--ep-type-label);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --- Seitenkopf mit Aktionen --------------------------------------------- */

.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.page-head h1 {
  margin-bottom: 0;
  overflow-wrap: anywhere;
}

/* Der Titelblock darf schmaler werden als sein laengstes Wort - dann bricht der
   Titel um, statt die Seite aufzuschieben (Abschlussreview). */
.page-head > :first-child {
  min-width: 0;
}

/* Die Seitenaktionen selbst brauchen keine eigene Regel: Layout aus der
   Gruppe, Ausrichtung neben der Ueberschrift aus .page-head. */

/* --- Formulargruppen ------------------------------------------------------ */

.form-group {
  /* Ein fieldset ist im Browser mindestens so breit wie sein Inhalt - ohne
     min-width: 0 schob bei grosser Schrift eine lange Beschriftung die Seite
     waagerecht auf (Abschlussreview). */
  min-width: 0;
  margin: 0 0 1.75rem;
  padding: 0;
  border: 0;
  border-top: 1px solid var(--ep-line);
  padding-top: 1.25rem;
}

.form-group:first-of-type {
  border-top: 0;
  padding-top: 0;
}

.form-group legend {
  padding: 0;
  margin-bottom: 0.75rem;
  font-size: var(--ep-type-title);
  font-weight: 600;
}

.field-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr));
  gap: 0 0.75rem;
}

/*
 * Drei kurze Wahlmoeglichkeiten in einer Zeile (E3, Darstellung).
 *
 * Bewusst KEINE Kaestchen wie bei den Vorlieben: "System", "Hell" und
 * "Dunkel" sind drei Woerter, und drei Karten dafuer waeren mehr Rahmen als
 * Inhalt. Nur Layout - Abstand, Umbruch und die Trefferflaeche, die jedes
 * Bedienziel hier hat.
 */
.theme-choice {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1.5rem;
}

.theme-choice__option {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: var(--ep-target);
  cursor: pointer;
}

/* --- Schalter und Mehrfachauswahl ---------------------------------------- */

.switch {
  display: inline-flex;
  align-items: flex-start;
  gap: 0.55rem;
  /* Grosszuegige Trefferflaeche fuer Touch-Geraete. */
  min-height: 2.75rem;
  padding: 0.4rem 0;
  cursor: pointer;
}

.switch input[type="checkbox"] {
  width: 1.25rem;
  height: 1.25rem;
  /* Wie bei .checkbox: Die Trefferflaeche ist die Zeile, nicht das Kaestchen. */
  min-height: 0;
  margin-top: 0.15rem;
  flex: 0 0 auto;
  accent-color: var(--ep-primary);
}

.switch--block {
  display: flex;
  padding: 0.5rem 0;
}

.switch--block .field__hint {
  display: block;
  margin-top: 0.1rem;
}

.checkbox-grid {
  display: grid;
  /* min(..., 100%) wie bei den Filtern: nie breiter als der Platz (Abschlussreview). */
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
  gap: 0 1rem;
}

/* --- Filterleiste --------------------------------------------------------- */

.filters {
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--ep-line);
}

/*
 * Neun Auswahlfelder und fuenf Schalter, zugeklappt (Feinschliff 04, Punkt 8).
 * Die Zusammenfassung ist die Trefferflaeche und bekommt deshalb volle 44 px -
 * dieselbe Regel wie bei den Abschnitten der Einkaufsliste.
 */
.filters__summary {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 44px;
  list-style: none;
  color: var(--ep-muted);
  font-size: var(--ep-type-label);
  cursor: pointer;
}

.filters__summary::-webkit-details-marker {
  display: none;
}

.filters__summary::before {
  /* Leerer Alternativtext: Das Dreieck ist Zeichnung, kein Wort. */
  content: "▸";
  content: "▸" / "";
  display: inline-block;
  width: 0.8em;
}

.filters__more[open] > .filters__summary::before {
  content: "▾";
  content: "▾" / "";
}

.filters__summary .badge {
  margin: 0;
}

.filters__search {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 0.9rem;
}

.filters__search input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0.65rem 0.75rem;
}

.filters__grid {
  display: grid;
  /* min(..., 100%): Bei grosser Schrift auf einem schmalen Telefon waeren 11rem
     breiter als der Platz - dann gilt die volle Breite (Abschlussreview). */
  grid-template-columns: repeat(auto-fit, minmax(min(11rem, 100%), 1fr));
  gap: 0 0.75rem;
}

.filters__field label {
  display: block;
  margin-bottom: 0.25rem;
  font-size: var(--ep-type-small);
  font-weight: 600;
  color: var(--ep-muted);
}

.filters__field select {
  width: 100%;
  /* 44 px - auch auf dem Tablet ein Fingerziel (Abschlussreview). */
  min-height: 2.75rem;
  padding: 0.5rem 0.6rem;
  margin-bottom: 0.75rem;
}

.filters__flags {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1rem;
  margin-bottom: 0.5rem;
}

.result-count {
  margin: 1.25rem 0 0.5rem;
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
}

/* --- Gerichtsliste -------------------------------------------------------- */

.recipe-grid {
  display: grid;
  /* min(): Bei 320 px ist die Spalte schmaler als 15rem - dann eben eine volle
     Spalte statt waagerechten Scrollens (Sprint 009). */
  grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
  gap: var(--ep-s4);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Flaeche siehe oben. overflow: hidden schneidet das Bild an der Rundung ab. */
.recipe-card {
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Archiviert: gestrichelter Rahmen und das Abzeichen - keine Transparenz. Mit
   opacity fiel die Schrift unter 4,5 : 1 (Sprint 009). */
.recipe-card.recipe-card--archived {
  border-style: dashed;
}

.recipe-card__link {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  color: inherit;
  text-decoration: none;
}

.recipe-card__link:hover .recipe-card__name,
.recipe-card__link:focus-visible .recipe-card__name {
  text-decoration: underline;
}

/*
 * DAS BILD IST DIE ANMUTUNG, NICHT DER INHALT (Feinschliff 04, Punkt 23).
 *
 * Mit 4:3 war der Bildbereich auf einem 375-px-Telefon 245 px hoch - mehr als
 * die Haelfte einer 435 px hohen Karte. Auf dem Bildschirm standen anderthalb
 * Gerichte. 16:9 laesst das Foto deutlich, macht aus dem Pool aber wieder eine
 * Liste, durch die man scrollen kann. Auf dem Desktop, wo mehrere Karten
 * nebeneinander stehen, bleibt es beim ruhigeren 4:3.
 */
.recipe-card__image {
  display: block;
  aspect-ratio: 16 / 9;
  background: var(--ep-soft);
}

@media (min-width: 48rem) {
  .recipe-card__image {
    aspect-ratio: 4 / 3;
  }
}

.recipe-card__image img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/*
 * Ausnahme von der Rollenskala: Das ist kein Text, sondern der Anfangsbuchstabe
 * als Bildersatz. Er richtet sich nach der Flaeche, nicht nach der Leseskala.
 */
.recipe-card__placeholder {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  color: var(--ep-primary);
  font-size: 2rem;
  font-weight: 700;
}

.recipe-card__body {
  display: block;
  padding: var(--ep-s3) var(--ep-s4);
}

/* Seit dem Feinschliff 04 eine h2 - sichtbar unveraendert: Groesse und
   Abstaende kommen aus dieser Klasse, nicht aus der Ueberschriftenregel. */
.recipe-card__name {
  display: block;
  margin: 0 0 0.35rem;
  color: inherit;
  font-size: var(--ep-type-title);
  font-weight: 600;
  line-height: var(--ep-lh-tight);
}

.recipe-card__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin-bottom: 0.35rem;
}

.recipe-card__meta .badge {
  margin-left: 0;
}

.recipe-card__categories,
.recipe-card__properties {
  display: block;
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
  line-height: var(--ep-lh-meta);
}

/* Variante: Kartenfuss. Die Karte polstert sich nicht selbst, deshalb polstert
   sich die Gruppe - und setzt sich mit einer Linie vom Inhalt ab. */
.recipe-card__actions {
  padding: var(--ep-s2) var(--ep-s4);
  border-top: 1px solid var(--ep-line);
}

.inline-form {
  display: inline;
}

/* --- Detailansicht -------------------------------------------------------- */

.recipe-detail__badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin: 0 0 1rem;
}

.recipe-detail__badges .badge {
  margin-left: 0;
}

.recipe-detail__figure {
  margin: 0 0 1.25rem;
}

.recipe-detail__figure img {
  display: block;
  width: 100%;
  /* Der Platz steht fest, bevor das Bild geladen ist - nichts springt (Sprint 009). */
  aspect-ratio: 4 / 3;
  max-height: 24rem;
  object-fit: cover;
  border-radius: var(--ep-radius);
}

.recipe-detail__figure figcaption {
  margin-top: 0.35rem;
  font-size: var(--ep-type-small);
}

/* --- Pagination ----------------------------------------------------------- */

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: var(--ep-s4);
}

.pagination__status {
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
}

.archive-hint {
  margin-top: var(--ep-s4);
  text-align: center;
}

/* --- Kategorienverwaltung ------------------------------------------------- */

.category-row {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}

.category-row input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0.5rem 0.6rem;
}

.category-row__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-top: 0.4rem;
  font-size: var(--ep-type-small);
}

/*
 * Leere Zustaende.
 *
 * Leer ist kein Fehler und keine Warnung. Ein Haushalt, der noch keine
 * Gerichte angelegt hat, hat nichts falsch gemacht - er faengt an. Deshalb
 * keine Signalfarbe, kein Rahmenakzent, keine Meldungsflaeche: Der leere
 * Zustand ist gewoehnlicher Seiteninhalt in der Karte, in der er steht.
 *
 * Drei Teile, alle freiwillig ausser dem ersten: eine Aussage, ein erklaerender
 * Satz, und HOECHSTENS eine naechste Handlung. Was die Aussage traegt, ist der
 * Text - nicht ein Bild, nicht ein Zeichen.
 *
 * Hiess vorher .card--empty. Der neue Name sagt, was es ist, statt wo es
 * haengt: Dieselbe Struktur traegt jetzt auch die leere Trefferliste, die
 * vorher als gestrichelte "gedaempfte" Karte auftrat - ein Filter ohne Treffer
 * ist aber kein Sonderzustand der Karte, sondern derselbe Fall.
 */
.empty-state {
  text-align: center;
}

/*
 * Die Erklaerung bleibt lesbar breit, auch wenn die Karte es nicht ist. Eine
 * mittig gesetzte Zeile ueber die volle Breite eines Rasters liest sich
 * schlecht.
 */
.empty-state__text {
  max-width: var(--ep-text-max);
  margin-inline: auto;
}

/* Variante: Der leere Zustand ist mittig gesetzt, seine Handlung auch. */
.empty-state__actions {
  justify-content: center;
  margin-top: var(--ep-s4);
}

/* --- Zutaten und Rezeptinhalt (Sprint 003) -------------------------------- */

.field--grow {
  flex: 1 1 auto;
  min-width: 0;
}

.field--narrow {
  max-width: 14rem;
}

/* Menge und Einheit stehen nebeneinander und brauchen wenig Platz - aber nie
   so wenig, dass der Wert abgeschnitten wird. */
.field--quantity {
  flex: 0 0 7rem;
}

.field--unit {
  flex: 0 0 8rem;
}

.field--check,
.field--action {
  display: flex;
  align-items: flex-end;
  flex: 0 0 auto;
}

.field--check {
  /* Auf der Grundlinie des Nachbarfeldes, nicht auf der seiner Beschriftung.
     align-items wirkt auf die Kinder - fuer die eigene Lage in der Zeile
     braucht es align-self, sonst gewinnt das flex-start der Zeile. */
  align-self: flex-end;
  padding-bottom: 0.5rem;
}

/*
 * Ein Haekchen ist ein Haekchen.
 *
 * ".field input" setzt width: 100%. Fuer ein Kontrollkaestchen ist das
 * falsch: Es zieht sich ueber die ganze Zeile und steht als flacher Balken
 * ueber seiner Beschriftung - 13 Pixel hoch und kaum zu treffen. Beim
 * Browsercheck zu Sprint 004 aufgefallen, betrifft auch das Feld
 * "Optional" im Rezepteditor aus Sprint 003.
 */
.checkbox {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 44px;
  cursor: pointer;
}

/*
 * Auswahlknoepfe brauchen dieselbe Ausnahme wie Kontrollkaestchen. Ohne sie
 * greift `.field input { width: 100% }`, und der Knopf steht als flacher
 * Balken ueber seiner Beschriftung statt daneben. Beim Browsercheck zu
 * Sprint 007 aufgefallen - derselbe Fehler wie in Sprint 004, nur mit
 * type="radio".
 */
.checkbox input[type="checkbox"],
.checkbox input[type="radio"] {
  width: 1.25rem;
  height: 1.25rem;
  /* Dieselbe Ausnahme wie bei der Breite: ".field input" gibt jedem Feld die
     Mindesthoehe einer Hauptaktion. Ein Haekchen waere damit 48 px hoch. Die
     Trefferflaeche liefert stattdessen die ganze Zeile (.checkbox). */
  min-height: 0;
  flex: 0 0 auto;
  accent-color: var(--ep-primary);
}

/* Auswahlknoepfe und Kontrollkaestchen einer Gruppe stehen untereinander. */
.field > .checkbox {
  display: flex;
}

.editor-rows {
  margin: 0 0 1rem;
  padding: 0;
  list-style: none;
}

.editor-row {
  padding: 0.75rem 0;
  border-top: 1px solid var(--ep-line);
}

.editor-row:first-child {
  border-top: 0;
}

.editor-row__grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0 0.75rem;
  align-items: flex-start;
}

.editor-row__grid + .editor-row__grid {
  margin-top: 0.25rem;
}

.ingredient-form__grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0 0.75rem;
  align-items: flex-start;
}

.ingredient-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}

.ingredient-row input[type="text"] {
  flex: 1 1 12rem;
  min-width: 0;
  padding: 0.5rem 0.6rem;
}

.ingredient-row select {
  flex: 0 1 12rem;
  min-width: 0;
  /* Mindestens 16 Pixel: Darunter zoomt iOS beim Antippen in das Feld hinein
     und die Seite steht danach schief. */
  font-size: var(--ep-type-body);
  min-height: 44px;
}

.ingredient-row__meta {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.75rem;
  margin-top: 0.4rem;
  font-size: var(--ep-type-small);
}

.form__hint {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  align-items: center;
  margin: 0;
  font-size: var(--ep-type-small);
}

/* --- Rezeptansicht -------------------------------------------------------- */

.ingredient-list {
  margin: 0.5rem 0 0;
  padding: 0;
  list-style: none;
}

.ingredient-list__item {
  display: flex;
  gap: 0.75rem;
  padding: 0.4rem 0;
  border-bottom: 1px solid var(--ep-line);
}

.ingredient-list__item:last-child {
  border-bottom: 0;
}

/* Die Mengen stehen in einer eigenen Spalte, damit das Auge sie beim Kochen
   in einer Linie findet. */
.ingredient-list__amount {
  flex: 0 0 6.5rem;
  font-variant-numeric: tabular-nums;
  text-align: right;
  color: var(--ep-muted);
}

.ingredient-list__name {
  flex: 1 1 auto;
  min-width: 0;
}

/* "Menge nicht lesbar" (Sprint 010, Teil K): eine eigene, umbrechende Zeile unter
   dem Namen - nicht in der Mengenspalte, die im Kochmodus nie umbricht. */
.ingredient-list__hint {
  display: block;
  white-space: normal;
  font-size: var(--ep-type-small);
  color: var(--ep-text);
  border-left: 3px solid var(--ep-warning);
  padding-left: 0.4rem;
  margin-top: 0.15rem;
}

.step-list {
  margin: 0.5rem 0 0;
  padding-left: 1.4rem;
}

.step-list__item {
  margin-bottom: 0.6rem;
  line-height: var(--ep-lh-body);
}

.serving-switch {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  align-items: center;
  font-size: var(--ep-type-small);
}

.serving-switch__option {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.75rem;
  min-height: 2.75rem;
  padding: 0 0.5rem;
  border: 1px solid var(--ep-line);
  border-radius: var(--ep-radius);
  text-decoration: none;
  color: inherit;
}

.serving-switch__option.is-active {
  border-color: var(--ep-primary);
  background: var(--ep-soft);
  font-weight: 600;
}

@media (max-width: 30rem) {
  /* Auf dem Telefon stehen Menge und Einheit unter der Zutat statt daneben -
     nebeneinander waeren alle drei Felder zu schmal zum Tippen. */
  .field--quantity,
  .field--unit {
    flex: 1 1 45%;
  }

  .ingredient-list__amount {
    flex: 0 0 5rem;
  }

  .ingredient-row select,
  .ingredient-row input[type="text"] {
    flex: 1 1 100%;
  }
}

/* Berührungsflächen im Rezepteditor.

   Die Entfernen-Schaltflächen sind als leise Textlinks gestaltet und wären
   damit nur so hoch wie ihre Zeile - auf dem Telefon rund 22 Pixel und damit
   zu klein zum Treffen. Sie bekommen deshalb eine Mindesthöhe, ohne ihr
   zurückhaltendes Aussehen zu verlieren: Ein zerstörender Knopf soll nicht
   ins Auge springen, aber treffbar sein. */
.editor-row .link-quiet--button,
.ingredient-row__meta .link-quiet--button {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 0.25rem;
}

/*
 * Treffbare Hoehe fuer leise Schaltflaechen und kleine Links. Bis Sprint 008
 * nur unter 30rem; seit Sprint 009 nach Bedienart statt nach Breite: Ein
 * Tablet ist breit, bedient wird es mit dem Finger. Eine Flaeche von 22 Pixeln
 * trifft mit dem Daumen niemand zuverlaessig - in der Kochhistorie hiesse
 * danebengreifen, den falschen Eintrag zu oeffnen. Die dichten Werte kommen
 * nur mit feinem Zeiger zurueck (weiter unten).
 *
 * Dazugekommen ist "Bearbeiten" in der Gerichtskarte: Der Link stand mit 22 px
 * neben drei 44-px-Schaltflaechen derselben Gruppe - der einzige leise Link
 * einer Aktionsgruppe, der gar keine Trefferflaeche hatte.
 */
.link-quiet--button,
.week-nav .link-quiet,
.day-card__cooked .link-quiet,
.stack-list__actions .link-quiet,
.recipe-card__actions .link-quiet,
.site-header__brand {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

/*
 * Neben einem Knopf bleiben 44 px - auch mit der Maus.
 *
 * "Abbrechen" steht direkt neben "Speichern", "Filter zuruecksetzen" neben
 * "Filter anwenden": 22 Pixel neben 48 laden auch mit einem feinen Zeiger zum
 * Vertippen ein (Sprint 004/009). Die Tageskarte haelt dieselbe Hoehe, weil
 * dort vier Aktionen in einer schmalen Karte stehen.
 *
 * Bisher stand das verstreut: je eine eigene Regel fuer den Formularabschluss
 * und fuer die Tageskarte - und der Filter fiel mit feinem Zeiger auf 24 px
 * zurueck, obwohl direkt daneben ein 48-px-Knopf steht.
 */
.form__actions .link-quiet,
.filters__actions .link-quiet,
.day-card__actions .link-quiet {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

@media (min-width: 48rem) and (hover: hover) and (pointer: fine) {
  /* Dicht wird es nur, wo die ganze Gruppe leise ist: Listenzeilen und
     Gerichtskarten stehen zu vielen untereinander. */
  .link-quiet--button,
  .week-nav .link-quiet,
  .day-card__cooked .link-quiet,
  .stack-list__actions .link-quiet,
  .recipe-card__actions .link-quiet {
    /* Dicht, aber nie unter 24 px (WCAG 2.5.8, Abschlussreview). */
    min-height: 1.5rem;
  }
}

@media (max-width: 30rem) {
  /* Auf dem Telefon steht die Entfernen-Schaltfläche allein in ihrer Zeile und
     darf die volle Breite nutzen - so ist sie eindeutig zu treffen. */
  .editor-row .field--action {
    flex: 1 1 100%;
    justify-content: flex-end;
  }

  .editor-row .field--check {
    flex: 1 1 100%;
    padding-bottom: 0;
  }
}

/* --- Familienpräferenzen (Sprint 004) ------------------------------------- */

.member-card h2 {
  margin-top: 0;
}

/* Die Vorliebe als Schaltergruppe statt als Ausklappmenü: Auf dem Telefon ist
   ein Tipp schneller als zwei, und alle Möglichkeiten sind gleichzeitig zu
   sehen. Technisch sind es gewöhnliche Radiofelder. */
.preference-choice {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 0.5rem;
  margin: 0 0 1rem;
  padding: 0;
  border: 0;
}

.preference-choice__option {
  display: flex;
  gap: 0.6rem;
  align-items: flex-start;
  min-height: 44px;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--ep-line);
  border-left-width: 4px;
  border-radius: var(--ep-radius);
  cursor: pointer;
}

.preference-choice__option input {
  margin-top: 0.2rem;
  flex: 0 0 auto;
}

.preference-choice__label {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  font-size: var(--ep-type-label);
  line-height: var(--ep-lh-meta);
}

/* Die Farbe ist eine Zugabe, keine Information: Jede Stufe trägt ihren Text
   daneben (Barrierefreiheit, Ausdruck, Schwarzweiß). */
.preference-choice__option--green {
  border-left-color: var(--ep-primary);
}

.preference-choice__option--yellow {
  border-left-color: var(--ep-yellow);
}

.preference-choice__option--red {
  border-left-color: var(--ep-danger);
}

.preference-choice__option:has(input:checked) {
  background: var(--ep-soft);
  border-color: var(--ep-primary);
}

.variant-block {
  margin-top: 0.75rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--ep-line);
}

.variant-block > summary {
  min-height: 44px;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  cursor: pointer;
  font-weight: 600;
}

/* --- Familienlage in der Rezeptansicht ------------------------------------ */

.family-list {
  margin: 0.5rem 0 1rem;
  padding: 0;
  list-style: none;
}

.family-list__item {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.75rem;
  align-items: baseline;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--ep-line);
}

.family-list__item:last-child {
  border-bottom: 0;
}

.family-list__name {
  flex: 0 0 7rem;
  font-weight: 600;
}

.family-list__state {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.6rem;
  align-items: baseline;
  flex: 1 1 auto;
  min-width: 0;
}

.family-list__note {
  font-size: var(--ep-type-small);
}

.preference-badge {
  display: inline-flex;
  gap: 0.3rem;
  align-items: baseline;
  padding: 0.15rem 0.5rem;
  border-radius: var(--ep-radius);
  border-left: 4px solid var(--ep-line);
  background: var(--ep-surface-quiet, transparent);
  font-size: var(--ep-type-small);
}

.preference-badge--green {
  border-left-color: var(--ep-primary);
  background: var(--ep-soft);
}

/*
 * Der Balken traegt Bedeutung und braucht deshalb eine eigene, etwas dunklere
 * Farbe; die Flaeche dahinter ist nur ein Hauch und bleibt beim Markenton.
 * Die Familie bleibt fachlich eigenstaendig - das hier ist kein Ton der
 * generischen Zustandsfamilie.
 */
.preference-badge--yellow {
  border-left-color: var(--ep-preference-yellow);
  background: color-mix(in srgb, var(--ep-yellow) 12%, var(--ep-surface));
}

.preference-badge--red {
  border-left-color: var(--ep-danger);
  background: color-mix(in srgb, var(--ep-danger) 10%, var(--ep-surface));
}

.preference-badge--none {
  color: var(--ep-muted);
}

/* "Angabe nicht lesbar" (Sprint 010, Teil C): ein gespeicherter Wert, den diese
   Version nicht kennt - benannt, nie farbig gedeutet. Der Modifikator kommt nie
   aus dem Rohwert. */
.preference-badge--unknown {
  border-left-style: dashed;
  border-left-color: var(--ep-muted);
}

.variant-card {
  margin: 0.75rem 0 0;
  padding: 0.75rem 1rem;
  border-left: 4px solid var(--ep-primary);
  border-radius: var(--ep-radius);
  background: var(--ep-soft);
}

.variant-card h3 {
  margin: 0 0 0.2rem;
  font-size: var(--ep-type-body);
}

.variant-card__name {
  margin: 0 0 0.4rem;
  font-weight: 600;
}

.variant-card p:last-child {
  margin-bottom: 0;
}

.recipe-card__family {
  display: block;
  margin-top: 0.3rem;
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
}

@media (max-width: 30rem) {
  /* Auf dem Telefon steht der Name über der Bewertung statt daneben - sonst
     bleiben für die Stufe und die Notiz nur ein paar Zentimeter. */
  .family-list__name {
    flex: 1 1 100%;
  }

  .preference-choice {
    grid-template-columns: 1fr;
  }
}

/* --- Bewertungen (Sprint 005) --------------------------------------------- */

/*
 * Fuenf Stufen als grosse Flaechen, nicht als Reihe winziger Punkte.
 *
 * Auf 375 Pixeln liegen sie untereinander, auf einem breiten Bildschirm
 * nebeneinander. Jede Stufe traegt ZAHL UND TEXT: "4" allein sagt niemandem
 * etwas, und Farbe allein waere bei einer Farbsehschwaeche, im Ausdruck und in
 * Schwarzweiss keine Aussage.
 */
.rating-choice {
  display: grid;
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  border: 0;
  grid-template-columns: 1fr;
}

@media (min-width: 40rem) {
  .rating-choice {
    grid-template-columns: repeat(3, 1fr);
  }
}

.rating-choice__option {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 52px;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--ep-line);
  border-left: 4px solid var(--ep-line);
  border-radius: var(--ep-radius);
  background: var(--ep-surface);
  cursor: pointer;
}

.rating-choice__option:has(input:checked) {
  border-color: var(--ep-primary);
  background: var(--ep-soft);
}

.rating-choice__option:focus-within {
  outline: 2px solid var(--ep-primary);
  outline-offset: 1px;
}

.rating-choice__option input {
  width: 1.25rem;
  height: 1.25rem;
  flex: 0 0 auto;
  accent-color: var(--ep-primary);
}

.rating-choice__number {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.rating-choice__label {
  color: var(--ep-muted);
}

/* Dieselben drei Farben wie bei den Vorlieben - eine Anwendung, eine
   Farbsprache. Getragen wird die Aussage aber vom Text daneben. */
.rating-choice__option--1,
.rating-choice__option--2 {
  border-left-color: var(--ep-danger);
}

.rating-choice__option--3 {
  border-left-color: var(--ep-yellow);
}

.rating-choice__option--4,
.rating-choice__option--5 {
  border-left-color: var(--ep-primary);
}

/* --- Kochhistorie (Sprint 005) -------------------------------------------- */

.history-entry__recipe {
  margin-left: 0.4rem;
  font-weight: 600;
}

.history-entry__meta {
  display: block;
  margin-top: 0.15rem;
  font-size: var(--ep-type-small);
}

.recipe-card__cooking {
  display: block;
  margin-top: 0.15rem;
  font-size: var(--ep-type-small);
}

/* --- Wochenplan (Sprint 006) ---------------------------------------------- */

.week-status {
  margin: 0.35rem 0 0;
  font-size: var(--ep-type-label);
}

.week-status .badge {
  margin-left: 0;
}

.week-nav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
  margin: var(--ep-s4) 0 0;
  font-size: var(--ep-type-label);
}

/* Sieben Tageskarten: auf dem Telefon untereinander, auf dem Desktop im
   Raster. Keine style-Attribute - die CSP verbietet sie. */
.week-grid {
  display: grid;
  /* minmax(0, 1fr) statt 1fr: Eine Karte darf schmaler werden als ihr breitester
     Inhalt - bei grosser Schrift auf einem schmalen Telefon schob sie sonst die
     Seite auf (Abschlussreview). */
  grid-template-columns: minmax(0, 1fr);
  gap: 0.75rem;
  margin-top: var(--ep-s4);
}

@media (min-width: 40rem) {
  /*
   * 16rem, nicht 17: Neben der Seitenleiste bleiben bei 800 px genau 552 px
   * fuer den Inhalt. Mit 17rem brauchten zwei Spalten 556 - vier Pixel zu
   * viel, und das Raster fiel auf EINE Spalte mit 537 px je Karte zurueck.
   * Ein Tag stand dort breiter als auf dem Schreibtisch und die Woche
   * brauchte genauso viel Scrollen wie auf dem Telefon. Mit 16rem sind es
   * zwei Spalten zu 270 px - ueber den 261 px, die als Untergrenze fuer eine
   * lesbare Tageskarte gemessen wurden. Bei 1280 px bleiben es drei Spalten.
   */
  .week-grid {
    grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr));
  }
}

/* Flaeche siehe oben. Polster aus der Abstandsreihe statt 1rem/1.1rem. */
.day-card {
  display: flex;
  flex-direction: column;
  gap: var(--ep-s2);
  padding: var(--ep-s4);
}

.day-card.day-card--today {
  border-color: var(--ep-primary);
  box-shadow: inset 4px 0 0 var(--ep-primary);
}

.day-card.day-card--empty {
  border-style: dashed;
}

.day-card--dragging {
  opacity: 0.5;
}

.day-card--dragover {
  outline: 2px dashed var(--ep-primary);
  outline-offset: 2px;
}

.day-card[draggable="true"] {
  cursor: grab;
}

.day-card__head {
  display: flex;
  /* Bei grosser Schrift rutscht "Heute" in die naechste Zeile, statt ueber den
     Kartenrand zu ragen (Abschlussreview). */
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 0.5rem;
}

.day-card__weekday {
  font-weight: 600;
}

.day-card__date {
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
}

.day-card__body {
  flex: 1;
}

.day-card__body p {
  margin: 0 0 0.35rem;
}

.day-card__title {
  font-size: var(--ep-type-title);
  font-weight: 600;
}

/* Der Gerichtsname ist ein eigenes Ziel: 44 px Trefferflaeche ueber Polster und
   Gegenrand - die Karte wird dadurch nicht hoeher (Abschlussreview). */
.day-card__title a {
  display: inline-block;
  padding-block: 0.6875rem;
  margin-block: -0.6875rem;
}

.day-card__meta,
.day-card__family {
  font-size: var(--ep-type-small);
}

.day-card__warning,
.picker__warning {
  display: block;
  margin-top: 0.25rem;
  color: var(--ep-danger);
  font-size: var(--ep-type-small);
}

.day-card__cooked {
  font-size: var(--ep-type-label);
  color: var(--ep-primary);
}

.day-card__notice {
  margin-top: 0.35rem;
}

/* Variante: Kartenfuss der Tageskarte - abgesetzt vom Inhalt darueber.
   Layout aus der Gruppenregel, die 44 px aus der Regel darueber. */
.day-card__actions {
  padding-top: var(--ep-s2);
  border-top: 1px solid var(--ep-line);
}

.day-card__move {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-left: auto;
  /* Nie breiter als die Karte: Bei 400 % Zoom stand die Auswahl sonst fuenf
     Pixel ueber dem Rand und die Seite bekam eine waagerechte Bildlaufleiste. */
  max-width: 100%;
}

/* Der Knopf braucht auf dem Telefon eine Trefferflaeche, nicht nur eine
   Zeilenhoehe: 21 px Breite waeren zu wenig (dieselbe Korrektur wie in
   Sprint 005 bei den Listenaktionen). */
.day-card__move button {
  min-width: 2.75rem;
  padding: 0 0.6rem;
}

.day-card__move select {
  /* Eine Auswahl bringt ihre eigene Mindestbreite mit - ohne "min-width: 0"
     schrumpft sie in einem Flex-Container nicht unter die laengste Option,
     und genau daran haengt der Ueberlauf bei starkem Zoom. Der Name steht in
     der visuell versteckten Beschriftung; schmaler heisst nicht namenlos. */
  min-width: 0;
  max-width: 11rem;
  min-height: 2.75rem;
  padding: 0.25rem 0.5rem;
  border: 1px solid var(--ep-input);
  border-radius: 8px;
  background: var(--ep-surface);
  color: var(--ep-text);
  font: inherit;
  /* 16 px: Darunter zoomt iOS beim Antippen in die Seite. Nach "font: inherit" -
     die Kurzschreibweise setzt die Groesse sonst zurueck (Sprint 009,
     Abschlussreview). */
  font-size: var(--ep-type-body);
}

/* --- Tageseditor ---------------------------------------------------------- */

.picker__search {
  display: flex;
  gap: 0.5rem;
  margin-bottom: var(--ep-s4);
}

.picker__search input {
  flex: 1;
  min-width: 0;
  font-size: var(--ep-type-body);
}

.picker__list {
  margin: 0 0 var(--ep-s4);
  padding: 0;
  list-style: none;
}

.picker__item {
  border-top: 1px solid var(--ep-line);
}

.picker__item:first-child {
  border-top: 0;
}

.picker__label {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: 0.75rem;
  align-items: start;
  padding: 0.75rem 0.25rem;
  cursor: pointer;
}

.picker__label input {
  margin-top: 0.3rem;
}

.picker__name {
  font-weight: 600;
}

.picker__meta,
.picker__warning {
  grid-column: 2;
  font-size: var(--ep-type-small);
}

.picker__item--blocked .picker__name {
  color: var(--ep-muted);
}

.type-choice {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.5rem;
  margin: 0 0 var(--ep-s4);
  padding: 0;
  list-style: none;
}

.type-choice__option {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 2.75rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--ep-line);
  border-radius: 8px;
  cursor: pointer;
}

.type-choice__option--custom {
  margin-bottom: 0.5rem;
}

.type-choice li:last-child {
  grid-column: 1 / -1;
}

/* --- Automatische Planung (Sprint 007) ------------------------------------ */

.minute-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: 0.5rem 0.75rem;
}

.minute-grid .field {
  margin-bottom: 0;
}

/* --- Score-Breakdown ------------------------------------------------------ */

/*
 * Der Balken ist eine Klasse je Zehnerstufe und kein style="width: …":
 * Die CSP erlaubt nur style-src 'self', ein Inline-Stil waere lautlos
 * wirkungslos.
 */
.score-breakdown {
  margin: 0.75rem 0 0;
  padding: 0;
  list-style: none;
}

.score-breakdown__row {
  display: grid;
  grid-template-columns: 10rem 1fr auto;
  align-items: center;
  gap: 0.75rem;
  padding: 0.25rem 0;
  font-size: var(--ep-type-small);
}

.score-breakdown__row--inactive {
  color: var(--ep-muted);
}

.score-breakdown__label {
  font-weight: 600;
}

.score-breakdown__bar {
  display: block;
  height: 0.5rem;
  border-radius: 999px;
  background: var(--ep-line);
  overflow: hidden;
}

.score-breakdown__fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--ep-primary);
}

.score-breakdown__row--inactive .score-breakdown__fill {
  background: var(--ep-line);
}

.score-breakdown__fill--0 { width: 0; }
.score-breakdown__fill--10 { width: 10%; }
.score-breakdown__fill--20 { width: 20%; }
.score-breakdown__fill--30 { width: 30%; }
.score-breakdown__fill--40 { width: 40%; }
.score-breakdown__fill--50 { width: 50%; }
.score-breakdown__fill--60 { width: 60%; }
.score-breakdown__fill--70 { width: 70%; }
.score-breakdown__fill--80 { width: 80%; }
.score-breakdown__fill--90 { width: 90%; }
.score-breakdown__fill--100 { width: 100%; }

/*
 * "0/5" bleibt zusammen - der Zusatz dahinter darf umbrechen.
 *
 * Bis hierher galt das nowrap fuer die ganze Zeile, also auch fuer "· noch
 * nicht aktiv". Bei 400 % Zoom (256 px CSS-Breite) schob dieser Satz die Seite
 * um 42 px auf und erzeugte eine waagerechte Bildlaufleiste. Der Zusatz ist
 * Fliesstext und bricht jetzt um; der Punktwert selbst nicht, denn "0/" und
 * "5" auf zwei Zeilen waeren zwei Zahlen.
 */
.score-breakdown__points {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.score-breakdown__points .muted {
  white-space: normal;
}

/* --- Austauschseite ------------------------------------------------------- */

.swap-heading {
  margin-bottom: 0.25rem;
}

.swap-current {
  margin: 0 0 0.25rem;
  font-size: var(--ep-type-section);
  font-weight: 600;
}

.alternatives {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr));
  gap: 1rem;
  margin-top: 1rem;
}

/* Flaeche siehe oben. */
.alternative {
  padding: var(--ep-s4);
}

/*
 * "Am längsten nicht gekocht" - ein ganzer deutscher Satz als Rubrik. In
 * Versalien und gesperrt waren das fuenfundzwanzig Zeichen ohne Wortform, auf
 * 320 px ueber zwei Zeilen. Die Farbe traegt die Rubrik hier ohnehin.
 */
.alternative__category {
  margin: 0;
  color: var(--ep-primary);
  font-size: var(--ep-type-small);
  font-weight: 600;
}

.alternative__title {
  margin: 0.25rem 0 0.35rem;
  font-size: var(--ep-type-title);
}

.alternative__reason,
.alternative__score {
  margin: 0 0 0.35rem;
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
}

@media (max-width: 32rem) {
  .score-breakdown__row {
    grid-template-columns: 7rem 1fr auto;
    gap: 0.5rem;
    font-size: var(--ep-type-small);
  }
}

/* --- Einkaufsliste (Sprint 008) ------------------------------------------- */

/* Mobil zuerst und ohne Tabelle: Jede Position ist eine Zeile mit dominantem
   Namen und direkt sichtbarer Menge, Kategorien sind Ueberschriften. Keine
   style-Attribute - die CSP verbietet sie. */

/*
 * DER KOPF DER EINKAUFSLISTE IST EIN VORSPANN.
 *
 * Gemessen bei 375 px vor dem Feinschliff: Ueberschrift, Statistikzeile,
 * Hinzufuegen-Knopf und eine dreizeilige Navigation schoben die erste offene
 * Position auf y = 571 - im Laden sieht man dann keinen einzigen Artikel, bevor
 * man wischt. Zeitraum und Wege bleiben, aber leise und in je einer Zeile.
 */
.shopping-head__range {
  margin: 0.15rem 0 0;
  font-size: var(--ep-type-label);
}

.shopping-head__range .badge {
  margin-left: 0;
}

/* Enger als die Wochenseite: Drei kurze Wege passen damit in eine Zeile statt
   in drei. Die 44 px Trefferhoehe je Link bleiben (Regel aus Sprint 009). */
.shopping-head__nav {
  gap: 0 var(--ep-s4);
  margin-top: 0.15rem;
}

.shopping-notice {
  margin: var(--ep-s4) 0 0;
}

.shopping-empty {
  margin: var(--ep-s4) 0 0.35rem;
}

.shopping-notice__text {
  margin: 0;
}

.shopping-notice__list {
  margin: 0.35rem 0 0;
  padding-left: 1.1rem;
}

.shopping-section {
  margin-top: 0.75rem;
  scroll-margin-top: 1rem;
}

.shopping-section__title {
  margin: 0 0 0.5rem;
  font-size: var(--ep-type-section);
}

.shopping-section__count {
  color: var(--ep-muted);
  font-size: var(--ep-type-label);
  font-weight: 400;
}

/* Die hinteren Bereiche sind zugeklappt; die Zusammenfassung ist die
   Trefferflaeche und bekommt deshalb volle 44 px. */
.shopping-section__summary {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  min-height: 44px;
  list-style: none;
  font-size: var(--ep-type-section);
  font-weight: 600;
  cursor: pointer;
}

/* Die Ueberschrift steckt im summary, damit die beiden hinteren Bereiche in der
   Ueberschriftenliste stehen. Sichtbar bleibt sie, was sie war: die Zeile
   selbst. */
.shopping-section__summary-title {
  margin: 0;
  font: inherit;
}

.shopping-section--secondary[open] > .shopping-section__summary {
  margin-bottom: 0.5rem;
}

/*
 * Leer heisst leise: Eine Sektion ohne Eintraege behaelt ihre Zeile - dass es
 * sie gibt, gehoert zur Orientierung -, aber nicht die Polsterung einer
 * gefuellten Karte. Auf einer frischen Liste standen hier zwei leere Kaesten zu
 * je 94 px untereinander (Feinschliff Einkauf Punkt 10).
 */
.shopping-section--empty {
  padding-top: 0.15rem;
  padding-bottom: 0.15rem;
  background: none;
  border: 0;
  box-shadow: none;
}

.shopping-section--empty > .shopping-section__summary {
  font-size: var(--ep-type-label);
  font-weight: 400;
  color: var(--ep-muted);
}

.shopping-group + .shopping-group {
  margin-top: 1.1rem;
}

/*
 * Die Regalueberschrift ist eine echte h3 - und traegt einen Namen aus den
 * Systemstammdaten: "Nudeln, Reis und Beilagen", "Obst und Gemüse". Als
 * gesperrte Versalien war das die laengste und zugleich kleinste Zeile der
 * Einkaufsliste. Sie bleibt leise, aber lesbar.
 */
.shopping-group__title {
  margin: 0 0 0.15rem;
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
  font-weight: 600;
}

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

/* Ein Raster mit zwei Spalten: Zeilenknopf, Abzeichen und aufgeklappte Herkunft
   nehmen die ganze Breite, die zweite Aktion steht rechts neben "Verwendet fuer".
   Klappt die Herkunft auf, rutscht die Aktion darunter, statt die Quellen auf dem
   Telefon einzuengen. So braucht ein Artikel eine Zeile weniger
   (Browserpruefung 375 px: vorher im Median 141 px je Artikel). */
.shopping-item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 0.75rem;
  padding: 0.25rem 0;
  border-top: 1px solid var(--ep-line);
  /* Nach dem Sprung zur naechsten Zeile bleibt darueber Luft fuer die
     Regalueberschrift (Sprint 009). */
  scroll-margin-top: 3.5rem;
}

.shopping-item__form,
.shopping-item > .shopping-item__main,
.shopping-item__badges,
.shopping-item__sources[open],
.shopping-item__sources:last-child,
.shopping-item__origin:last-child {
  grid-column: 1 / -1;
}

.shopping-item:first-child {
  border-top: 0;
}

.shopping-item__main {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem;
}

/* min-width und overflow-wrap: Ein sehr langer Zutatname bricht um, statt die
   Menge aus dem Bildschirm zu schieben. */
.shopping-item__name {
  flex: 1 1 auto;
  min-width: 0;
  font-size: var(--ep-type-title);
  font-weight: 600;
  overflow-wrap: anywhere;
}

/* Die Menge schrumpft nie unter ihre eigene Breite: "1 Pkg." bleibt stehen,
   auch neben einem sehr langen Namen - dann bricht der Name, nicht die Menge.
   Nur ein langer Hinweis wie "Menge nicht vollstaendig bestimmbar" bricht, und
   zwar an Wortgrenzen. (Browserpruefung 375 px: mit flex-shrink und
   overflow-wrap: anywhere zerfiel "1 Pkg." in einzelne Buchstaben.) */
.shopping-item__amount {
  flex: 0 0 auto;
  max-width: 50%;
  font-variant-numeric: tabular-nums;
  text-align: right;
}

.shopping-item__amount--open {
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
}

/* 8 px zwischen Zeilenknopf und zweiter Aktion: Wer "In den Wagen" meint, trifft
   nicht "Habe ich bereits" (Sprint 009). */
.shopping-item__form {
  margin: 0 0 0.5rem;
}

/* Die ganze Zeile ist der Knopf: Name und Menge sind die Trefferflaeche
   (Punkt 115 und 117). Sieht aus wie eine Zeile, verhaelt sich wie ein Knopf. */
.shopping-item__tap {
  width: 100%;
  min-height: 44px;
  margin: 0;
  padding: 0.3rem 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  align-items: center;
  cursor: pointer;
}

.shopping-item__tap:focus-visible {
  outline: 2px solid var(--ep-primary);
  outline-offset: 2px;
  border-radius: 6px;
}

.shopping-item__check {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 1.4rem;
  height: 1.4rem;
  border: 2px solid var(--ep-muted);
  border-radius: 50%;
  font-size: var(--ep-type-small);
  line-height: 1;
}

.shopping-item--in-cart .shopping-item__check {
  border-color: var(--ep-primary);
  background: var(--ep-primary);
  color: var(--ep-on-primary);
}

.shopping-item--in-cart .shopping-item__check::after {
  content: "✓";
}

.shopping-section__hint {
  margin: -0.25rem 0 0.5rem;
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
}

.shopping-item__badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin: 0.05rem 0 0.1rem;
}

.shopping-item__badges .badge {
  margin: 0;
}

/* Immer die rechte Spalte - neben "Verwendet fuer" oder, bei aufgeklappter
   Herkunft, darunter. */
.shopping-item__secondary {
  grid-column: 2;
  justify-self: end;
  margin: 0;
}

/* Zweite Aktion, Bearbeiten und Herkunft: 44 px im Touch-Layout. Sprint 008 hatte
   36 px gewaehlt; Sprint 009 revidiert das bewusst fuer den Einkauf mit einer
   Hand. Die dichte Zeile gibt es nur noch mit feinem Zeiger (unten). */
.shopping-item__action {
  min-height: 2.75rem;
}

.shopping-item__origin {
  margin: 0;
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
}

/* Bei einem manuellen Artikel ist die Herkunftszeile selbst der Link zum
   Bearbeiten - so hoch wie die zweite Aktion daneben und ohne eigene Zeile
   (Phase manuelle Artikel Punkt 28 und 30). "Manuell hinzugefügt · Bearbeiten"
   brach auf 375 px neben "Habe ich bereits" in zwei Zeilen um: 119 statt rund
   100 px je Artikel. Der Stift sagt, dass sich dahinter etwas aendern laesst. */
.shopping-item__edit {
  display: flex;
  align-items: center;
  justify-self: start;
  gap: 0.35rem;
  min-height: 2.75rem;
  color: var(--ep-muted);
}

.shopping-item__edit::after {
  content: "✎";
  font-size: var(--ep-type-label);
  line-height: 1;
}

.shopping-item__sources {
  margin: 0;
  font-size: var(--ep-type-small);
}

.shopping-item__sources > summary {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  min-height: 2.75rem;
  list-style: none;
  color: var(--ep-muted);
  cursor: pointer;
}

/* Eigene Markierung: Mit display: flex zeichnen nicht alle Browser das
   Dreieck, und ohne es sieht niemand, dass sich etwas aufklappen laesst. */
.shopping-section__summary::-webkit-details-marker,
.shopping-item__sources > summary::-webkit-details-marker {
  display: none;
}

.shopping-section__summary::before,
.shopping-item__sources > summary::before {
  content: "▸";
  /* Leerer Alternativtext: Das Dreieck ist Zeichnung, kein Wort (Sprint 009).
     Browser ohne diese Schreibweise behalten die Zeile davor. */
  content: "▸" / "";
  display: inline-block;
  width: 0.8em;
}

.shopping-section--secondary[open] > .shopping-section__summary::before,
.shopping-item__sources[open] > summary::before {
  content: "▾";
  content: "▾" / "";
}

.shopping-item__source-count {
  font-variant-numeric: tabular-nums;
}

/* Im Wagen: tritt zurueck, bleibt aber lesbar (Punkt 116). */
.shopping-item--in-cart .shopping-item__name,
.shopping-item--in-cart .shopping-item__amount {
  color: var(--ep-muted);
  text-decoration: line-through;
}

.shopping-item--available .shopping-item__name {
  color: var(--ep-muted);
  font-weight: 400;
}

.source-list {
  margin: 0.2rem 0 0.35rem;
  padding: 0 0 0 0.85rem;
  list-style: none;
  border-left: 2px solid var(--ep-line);
}

.source-list__item {
  padding: 0.15rem 0;
  overflow-wrap: anywhere;
}

.source-list__recipe {
  font-weight: 600;
}

.source-list__amount {
  font-variant-numeric: tabular-nums;
}

.source-list__item .badge {
  margin-left: 0.25rem;
}

.source-list__note {
  display: block;
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
}

/* Desktop: dieselbe Struktur, nur dichter (Punkt 121). */
@media (min-width: 48rem) {
  .shopping-item__name {
    font-size: var(--ep-type-body);
  }
}

/* Die dichte Zeile nur mit feinem Zeiger (Sprint 009): Ein Tablet ist breit,
   bedient wird es aber mit dem Finger. 36 px bleiben ueber den 24 px aus
   WCAG 2.5.8. */
@media (min-width: 48rem) and (hover: hover) and (pointer: fine) {
  .shopping-item {
    padding: 0.45rem 0;
  }

  .shopping-item__form {
    margin-bottom: 0;
  }

  .shopping-item__action,
  .shopping-item__edit,
  .shopping-item__sources > summary {
    min-height: 2.25rem;
  }
}

/* --- Standardvorraete (Sprint 008) ---------------------------------------- */

.pantry-group {
  margin: 1rem 0 0;
  padding: 0;
  border: 0;
}

.pantry-group legend {
  margin-bottom: 0.15rem;
  font-weight: 600;
}

.pantry-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.pantry-list__item {
  border-top: 1px solid var(--ep-line);
}

.pantry-list__item:first-child {
  border-top: 0;
}

/* Die ganze Zeile ist die Trefferflaeche des Kontrollkaestchens. */
.pantry-list__label {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  min-height: 44px;
  cursor: pointer;
}

.pantry-list__label input[type="checkbox"] {
  flex: 0 0 auto;
  width: 1.25rem;
  height: 1.25rem;
  margin: 0;
  accent-color: var(--ep-primary);
}

.pantry-list__name {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}

.pantry-list__category {
  flex: 0 1 auto;
  max-width: 45%;
  color: var(--ep-muted);
  font-size: var(--ep-type-small);
  text-align: right;
}
