/* Das eingebaute [hidden] des Browsers steht in der Rangfolge unter jeder
   eigenen Regel. Ohne diese Zeile blieb `<div id="modal" class="overlay" hidden>`
   wegen `.overlay { display: flex }` sichtbar und legte sich als unsichtbare,
   klickfangende Schicht ueber die Anmeldemaske. */
[hidden] { display: none !important; }

/* Essensplan – Design-Tokens, Light/Dark, grosse Touch-Flaechen fuers Handy. */

:root {
  color-scheme: dark;

  /* Grundflaeche. Der Verlauf und der Shader liegen darueber, siehe body. */
  --bg: #161826;
  /* Referenzwert des hellen Glasfilms. Wird von frontend/test/dom.mjs als
     Pruefflaeche benutzt; die Flaechen selbst kommen aus --glass-*. */
  --surface: rgba(233, 233, 237, .085);
  --surface-2: rgba(15, 16, 25, .42);
  /* Trennlinie zwischen Flaechen. Bei .13 verschwand sie ueber den hellen
     Stellen des Shaders fast vollstaendig. */
  --border: rgba(233, 233, 237, .22);
  /* Bei Eingabefeldern und Kaestchen ist der Rand die einzige Kennzeichnung -
     die Fuellung hebt sich mit 1,29:1 kaum von der Karte ab. Deshalb tragen sie
     einen eigenen, kraeftigeren Rand (3,0:1, WCAG 1.4.11), waehrend die
     Kartenkanten weich bleiben. */
  --field-line: rgba(233, 233, 237, .42);
  --text: #e9e9ed;
  /* Gerechnet wird gegen die hellste Stelle, die unter dem Glas liegen kann:
     die dunkle Kartenbasis ueber der obersten Shader-Stufe #3d3572, darauf der
     helle Rand des Glasverlaufs. Dort kam #a3a7ba auf 4,33:1 und lag damit
     unter der Schwelle - der Wert davor (#9397ab) auf 3,9:1. */
  --muted: #b0b3c4;

  /* Flieder handelt, Mint bestaetigt: alles Erledigte ist mintfarben. */
  --primary: #9184d9;
  --primary-soft: rgba(145, 132, 217, .18);
  --primary-line: rgba(145, 132, 217, .5);
  --on-primary: #e7e5fe;
  --on-primary-soft: #d2cefd;
  --accent-2: #65cbc6;
  --accent-2-soft: rgba(101, 203, 198, .13);
  --accent-2-line: rgba(101, 203, 198, .45);

  /* Die Ampel bleibt gruen/gelb/rot - die Farbe traegt hier Bedeutung. Nur
     --bad und --accent-2 sind aufgehellt, weil Text in diesen Farben auf der
     gleichfarbigen -soft-Flaeche sonst unter 4,5:1 blieb (Badge "Fertig" lag
     bei 3,45:1). --ok und --warn tragen schon von sich aus. */
  --ok: #6fd39b;
  --ok-soft: rgba(111, 211, 155, .14);
  --ok-glow: rgba(111, 211, 155, .7);
  --warn: #e0b57a;
  --warn-soft: rgba(224, 181, 122, .14);
  --warn-glow: rgba(224, 181, 122, .7);
  --bad: #f4a89e;
  --bad-soft: rgba(244, 168, 158, .14);
  --bad-line: rgba(244, 168, 158, .45);
  --bad-glow: rgba(244, 168, 158, .7);

  --scrim: rgba(10, 10, 18, .62);
  --toast-bg: rgba(35, 37, 50, .92);
  --toast-fg: #e9e9ed;

  --cat-obst: #6fd39b;
  --cat-kuehl: #8badf0;
  --cat-fleisch: #e08fa1;
  --cat-tk: #74c0d6;
  --cat-trocken: #cfae5e;
  --cat-brot: #c99a6c;
  --cat-sonst: #aaa1c0;

  /* Glas: Flaeche, Weichzeichner, Innenkante. Karten tragen alle drei,
     Zeilen nur die leichte Flaeche, Leisten die dunkle Variante. */
  /* Unter der hellen Glasschicht liegt eine dunkle Basis. Ohne sie haengt die
     Lesbarkeit davon ab, welche Stelle des bewegten Grundes gerade unter der
     Karte steht - genau das laesst echtes Milchglas auch nicht zu.
     Die Basis traegt .62 statt .45 (Zeilen .52 statt .35): erst damit bleibt
     jeder Text auch ueber der hellsten Shader-Stelle ueber 4,5:1. Der Grund
     scheint weiter durch, er entscheidet nur nicht mehr ueber die Lesbarkeit. */
  --glass-card: linear-gradient(155deg, rgba(233, 233, 237, .115), rgba(233, 233, 237, .038)),
                rgba(18, 19, 32, .62);
  --glass-row: linear-gradient(155deg, rgba(233, 233, 237, .085), rgba(233, 233, 237, .03)),
               rgba(18, 19, 32, .52);
  --glass-bar: linear-gradient(180deg, rgba(22, 24, 38, .78), rgba(22, 24, 38, .3));
  --glass-blur: blur(22px) saturate(160%);
  --glass-blur-row: blur(16px);
  --glass-blur-bar: blur(18px);
  --edge: inset 0 1px 0 rgba(255, 255, 255, .13);
  --shadow: var(--edge), 0 14px 34px rgba(0, 0, 0, .42);
  /* Dunkel genug fuer das Haken-Symbol auf der Mint-Flaeche. */
  --on-accent-2: #0f1019;
  /* Gerichtssymbole tragen einen eigenen warmen Ton. Eine Farbe, eine
     Bedeutung: Flieder handelt, Mint bestaetigt, dieser Ton heisst "Essen".
     Deshalb bekommen die Gerichte keine Naturfarben - das waeren 23 neue
     Bedeutungen in einer Welt, die mit dreien auskommt. */
  --dish: #d8cfbe;

  --radius: 16px;
  --radius-row: 12px;
  --radius-field: 11px;
  --radius-sheet: 22px;
  --tap: 44px;
}

* { box-sizing: border-box; }

/* Hanken Grotesk liegt lokal im Paket: per CDN waere die Schrift offline weg,
   und die App soll im Supermarkt ohne Empfang genauso aussehen. Lizenz OFL,
   siehe ../vendor/hanken-grotesk/OFL.txt.

   Warum nicht Inter: an den echten Texten dieser App gemessen ist Hanken
   7-9 % schmaler (Gerichtsnamen 14px/600, Listenposten 17px/600). Auf 375 px
   ist das oft eine Zeile weniger Umbruch je Gericht - genau der Platz, um den
   es auf dem Handy geht. Die Ziffern sind ausserdem von Haus aus tabellarisch,
   was Mengen und Naehrwerte sauber untereinander haelt. */
@font-face {
  font-family: 'Hanken Grotesk';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../vendor/hanken-grotesk/hanken-grotesk.woff2') format('woff2');
}

html, body {
  margin: 0;
  padding: 0;
  color: var(--text);
  font: 400 15px/1.5 'Hanken Grotesk', system-ui, -apple-system, 'Segoe UI', sans-serif;
  -webkit-text-size-adjust: 100%;
  min-height: 100%;
}

/* Der Verlauf traegt den Raum. Er bleibt als Rueckfall stehen, falls der
   WebGL-Grund (shader-bg) nicht startet. */
body {
  background:
    radial-gradient(620px 460px at 12% -6%, rgba(145, 132, 217, .4), transparent 62%),
    radial-gradient(520px 420px at 104% 22%, rgba(95, 201, 196, .16), transparent 60%),
    radial-gradient(760px 540px at 46% 112%, rgba(60, 52, 124, .52), transparent 66%),
    var(--bg);
  background-attachment: fixed;
  /* Ohne isolate kann der Shader-Canvas hinter diesem Hintergrund verschwinden. */
  isolation: isolate;
}

/* backdrop-filter braucht etwas zum Verwischen: alles zwischen Grund und
   Glasflaeche muss durchsichtig bleiben. */
main, .view { background: transparent; }

@keyframes rise { from { opacity: 0; transform: translateY(16px) scale(.985); } to { opacity: 1; transform: none; } }
@keyframes deal { from { opacity: 0; transform: translateY(26px) rotate(-1.6deg) scale(.94); } to { opacity: 1; transform: none; } }
@keyframes pop { 0% { opacity: 0; transform: scale(.4); } 70% { transform: scale(1.18); } 100% { opacity: 1; transform: scale(1); } }
@keyframes bar { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes drift { 0%, 100% { transform: translate3d(0, 0, 0) scale(1); } 50% { transform: translate3d(3%, -4%, 0) scale(1.08); } }
@keyframes sheen { 0% { transform: translateX(-120%); } 55%, 100% { transform: translateX(320%); } }
@keyframes sheet { from { opacity: 0; transform: translateY(58px) scale(.97); } to { opacity: 1; transform: none; } }

/* Wer Bewegung abbestellt hat, bekommt keine. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

h1, h2, h3 { line-height: 1.2; margin: 0 0 .4em; font-weight: 500; }
h1 { font-size: 27px; }
h2 { font-size: 16px; }
h3 { font-size: 14px; font-weight: 600; }
p { margin: 0 0 .7em; }
.muted { color: var(--muted); }
.small { font-size: 12px; line-height: 1.5; }
.center { text-align: center; }

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

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  /* viewport-fit=cover heisst: oben kann eine Notch stehen. Ohne diesen Einzug
     rutschte die Wochenzeile in der installierten App darunter. */
  padding-top: env(safe-area-inset-top);
  background: var(--glass-bar);
  backdrop-filter: var(--glass-blur-bar);
  -webkit-backdrop-filter: var(--glass-blur-bar);
  border-bottom: 1px solid var(--border);
}

.topbar-row {
  display: grid;
  grid-template-columns: var(--tap) 1fr var(--tap);
  align-items: center;
  gap: 4px;
}

.week-label {
  border: 0;
  background: none;
  color: var(--text);
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  min-height: var(--tap);
  padding: 8px 4px;
  cursor: pointer;
}

.view {
  padding: 12px 12px calc(84px + env(safe-area-inset-bottom));
  max-width: 780px;
  margin: 0 auto;
}

.tabs {
  position: sticky;
  bottom: 0;
  z-index: 20;
  display: grid;
  /* So viele Spalten wie Reiter - sonst rutscht der letzte in eine zweite
     Zeile und verschwindet unter dem Bildschirmrand. frontend/test/dom.mjs
     haelt die Zahl an die Reiter in index.html gebunden. */
  grid-template-columns: repeat(5, 1fr);
  gap: 4px;
  padding: 6px 8px calc(6px + env(safe-area-inset-bottom));
  background: var(--glass-bar);
  backdrop-filter: var(--glass-blur-bar);
  -webkit-backdrop-filter: var(--glass-blur-bar);
  border-top: 1px solid var(--border);
}

.tabs a {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-height: var(--tap);
  padding: 5px 0;
  border-radius: var(--radius-row);
  color: var(--muted);
  font-size: 11px;
  font-weight: 500;
  text-decoration: none;
  transition: background .16s, color .16s;
}

.tabs a[aria-current="page"] {
  background: rgba(145, 132, 217, .16);
  color: var(--on-primary-soft);
  font-weight: 600;
}

/* -------------------------------------------------------------- Bausteine -- */

.card {
  background: var(--glass-card);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 14px;
  margin-bottom: 10px;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: var(--tap);
  /* Zeilenhoehe 1.25 statt 1: deutsche Beschriftungen brechen zweizeilig
     ("Vorraete ausblenden", "Haken zuruecksetzen") und klebten sonst zusammen. */
  padding: 6px 15px;
  border: 1px solid var(--border);
  border-radius: var(--radius-row);
  background: rgba(233, 233, 237, .055);
  color: var(--text);
  font: 600 14.5px/1.25 'Hanken Grotesk', system-ui, sans-serif;
  text-align: center;
  cursor: pointer;
  transition: transform .16s, background .16s, border-color .16s;
}

.btn:active { transform: translateY(1px); }
.btn-primary {
  border-color: rgba(145, 132, 217, .75);
  background: linear-gradient(180deg, rgba(145, 132, 217, .32), rgba(145, 132, 217, .14));
  color: var(--on-primary);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .2), 0 10px 26px rgba(145, 132, 217, .24);
}
.btn-ghost { background: rgba(233, 233, 237, .055); color: var(--text); }
.btn-danger { color: var(--bad); border-color: var(--bad-line); background: transparent; }
.btn-block { width: 100%; }
.btn-lg { min-height: 56px; font-size: 17px; }
.btn:disabled { opacity: .5; cursor: default; }

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap);
  height: var(--tap);
  flex: none;
  border: 1px solid var(--border);
  border-radius: var(--radius-field);
  background: rgba(233, 233, 237, .05);
  color: var(--text);
  font-size: 1rem;
  cursor: pointer;
  transition: transform .16s, background .16s, border-color .16s;
}

.icon-btn.plain { border-color: transparent; background: transparent; }
.icon-btn.active {
  background: var(--primary-soft);
  border-color: var(--primary-line);
  color: var(--on-primary-soft);
}

.row { display: flex; align-items: center; gap: 8px; }
.row-wrap { flex-wrap: wrap; }
.spread { justify-content: space-between; }
.grow { flex: 1; min-width: 0; }

label { display: block; font-size: .85rem; font-weight: 600; margin-bottom: 10px; }

input[type="text"], input[type="password"], input[type="number"], input:not([type]), select, textarea {
  width: 100%;
  min-height: 44px;
  padding: 10px 12px;
  border: 1px solid var(--field-line);
  border-radius: var(--radius-field);
  background: var(--surface-2);
  color: var(--text);
  font: 400 15px/1.4 'Hanken Grotesk', system-ui, sans-serif;
  transition: border-color .16s, box-shadow .16s;
}

.status {
  margin-top: 6px;
  padding: 6px 10px;
  border-radius: 9px;
  font-size: .8rem;
  background: var(--surface-2);
  color: var(--muted);
  text-align: center;
}

.status.offline { background: var(--bad-soft); color: var(--bad); }
.status.connecting { background: var(--primary-soft); color: var(--on-primary-soft); }
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 9px;
  border-radius: 99px;
  background: rgba(233, 233, 237, .07);
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
}

.badge.fav { background: var(--warn-soft); color: var(--warn); }
.badge.conv { background: var(--bad-soft); color: var(--bad); }
.badge.veg { background: var(--ok-soft); color: var(--ok); }
/* Ein Zutatenname ist kurz. Ueber die ganze Breite gezogen sieht das Feld aus,
   als erwarte es einen Satz. */
.eingabe-schmal { max-width: 440px; }

/* Was aus dem Kuehlschrank fehlt, steht in der Warnfarbe - aber das Wort
   "fehlt" traegt die Aussage, nicht der Farbton (WCAG 1.4.1). */
.fehlt-zeile { margin-top: 3px; }
.fehlt { color: var(--warn); font-weight: 600; }

/* ------------------------------------------------------------------ Plan -- */

.day {
  background: var(--glass-card);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  margin-bottom: 10px;
  overflow: hidden;
}

.day.today {
  border-color: var(--primary-line);
  box-shadow: var(--shadow), 0 0 0 4px rgba(145, 132, 217, .1);
}

.day-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 9px 14px;
  background: rgba(233, 233, 237, .05);
  font-size: 14px;
  font-weight: 600;
}

.day.today .day-head { background: rgba(145, 132, 217, .2); color: var(--on-primary-soft); }
.day-head .date { font-weight: 400; font-size: 12px; color: var(--muted); }

.meal {
  display: grid;
  grid-template-columns: 44px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-top: 1px solid var(--border);
  transition: background .16s;
}

.meal .emoji { font-size: 28px; text-align: center; line-height: 1; }
.meal .slot {
  font-size: 10px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--muted);
}
.meal .name {
  font-size: 14px;
  font-weight: 600;
  line-height: 1.3;
  min-width: 0;
  overflow-wrap: break-word;
}
.meal .meta { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 3px; }
.meal.done .name { text-decoration: line-through; color: var(--muted); }
.meal.empty .name { color: var(--muted); font-style: italic; font-weight: 400; }
.meal-actions { display: flex; gap: 4px; }

/* ----------------------------------------------------------------- Ampel -- */

.traffic { display: grid; gap: 6px; }

.traffic-row {
  display: grid;
  grid-template-columns: 18px 1fr;
  align-items: center;
  gap: 9px;
  font-size: 12.5px;
  line-height: 1.5;
}

.traffic-row .dot {
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  border-radius: 99px;
  justify-self: center;
  animation: pop .4s both;
}
.traffic-row .dot .ico { width: 11px; height: 11px; stroke-width: 2.6; }
.traffic-row.ok .dot { background: var(--ok-soft); color: var(--ok); }
.traffic-row.warn .dot { background: var(--warn-soft); color: var(--warn); }
.traffic-row.bad .dot { background: var(--bad-soft); color: var(--bad); }
.traffic-row.bad { color: var(--bad); font-weight: 600; }
.traffic-row.warn { font-weight: 600; }

.summary-line { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; }

/* ------------------------------------------------------------- Einkaufen -- */

.progress {
  height: 8px;
  border-radius: 99px;
  background: rgba(15, 16, 25, .5);
  overflow: hidden;
}

.progress > div {
  width: 100%;
  height: 100%;
  border-radius: 99px;
  background: linear-gradient(90deg, var(--primary), var(--accent-2));
  box-shadow: 0 0 14px var(--accent-2-glow);
  /* Die Breite zu animieren erzwingt bei jedem Haken ein neues Layout. scaleX
     laeuft im Compositor - und die Leiste faehrt nicht mehr bei jedem
     Neuaufbau von Null hoch, was im Laden nur ablenkte. */
  transform-origin: left;
  transition: transform .28s cubic-bezier(.2, .8, .3, 1);
}

.cat-head {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 18px 2px 6px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .11em;
  color: var(--muted);
}

.cat-head:first-of-type { margin-top: 4px; }

.cat-head::before {
  content: '';
  width: 9px;
  height: 9px;
  border-radius: 3px;
  background: var(--cat-color, var(--cat-sonst));
  box-shadow: 0 0 10px var(--cat-color, var(--cat-sonst));
}

.item {
  display: grid;
  grid-template-columns: 26px 1fr auto;
  align-items: center;
  gap: 10px;
  min-height: 48px;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-row);
  background: var(--glass-row);
  backdrop-filter: var(--glass-blur-row);
  -webkit-backdrop-filter: var(--glass-blur-row);
  margin-bottom: 6px;
  transition: background .18s, border-color .18s, transform .16s;
}

.item input[type="checkbox"] {
  width: 24px;
  height: 24px;
  accent-color: var(--primary);
  margin: 0;
  flex: none;
}

.item.checked { background: var(--accent-2-soft); border-color: var(--accent-2-line); }
.item.checked .item-name { text-decoration: line-through; color: var(--muted); }
/* Name und Menge stehen zusammen. Vorher lag die Menge an der rechten Kante -
   auf einem breiten Fenster waren das mehrere hundert Pixel Augenwanderung
   zwischen zwei Angaben, die zusammengehoeren. */
.item-line {
  display: flex;
  align-items: baseline;
  gap: 9px;
  flex-wrap: wrap;
}
.item-name { font-size: 17px; font-weight: 600; line-height: 1.25; overflow-wrap: anywhere; }
.item-sub { font-size: 12.5px; color: var(--muted); }
.item-amount {
  font-variant-numeric: tabular-nums;
  font-size: 15px;
  font-weight: 500;
  color: var(--muted);
  white-space: nowrap;
}

/* -------------------------------------------------------------- Overlays -- */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: var(--scrim);
  backdrop-filter: blur(7px);
  -webkit-backdrop-filter: blur(7px);
  overflow-y: auto;
  animation: rise .35s both;
}

.overlay .card { width: 100%; max-width: 460px; margin: auto; }
.login-card h1 { margin-bottom: .1em; }
.login-hint { font-size: .85rem; color: var(--warn); min-height: 1.2em; margin: 0 0 .5em; }

.modal-head { display: flex; justify-content: space-between; align-items: center; gap: 8px; margin-bottom: 10px; }
.modal-body { max-height: 62vh; overflow-y: auto; margin: 0 -4px; padding: 0 4px; }
.modal-foot { display: flex; gap: 8px; margin-top: 12px; }

.toast {
  position: fixed;
  left: 50%;
  bottom: calc(78px + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  z-index: 60;
  max-width: 92vw;
  padding: 10px 16px;
  border-radius: 11px;
  background: var(--toast-bg);
  color: var(--toast-fg);
  font-size: .88rem;
  box-shadow: var(--shadow);
}

/* --------------------------------------------------------------- Rezepte -- */

.recipe-row {
  display: grid;
  grid-template-columns: 38px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-row);
  background: var(--glass-row);
  backdrop-filter: var(--glass-blur-row);
  -webkit-backdrop-filter: var(--glass-blur-row);
  margin-bottom: 6px;
  transition: border-color .16s, transform .16s;
}

.recipe-row .emoji { font-size: 1.5rem; text-align: center; }
.recipe-row.inactive { opacity: .5; }

.ing-row { display: grid; grid-template-columns: 1fr 74px 62px auto; gap: 6px; margin-bottom: 6px; }
.ing-row input, .ing-row select { margin-top: 0; min-height: 40px; }
.ing-row .icon-btn { min-height: 40px; min-width: 40px; }

.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }

.chip {
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  padding: 7px 13px;
  border: 1px solid var(--border);
  border-radius: 99px;
  background: var(--surface-2);
  color: var(--muted);
  font-size: .8rem;
  font-weight: 600;
  cursor: pointer;
  transition: background .16s, border-color .16s, color .16s;
}

/* Gedrueckt heisst nicht nur "andere Farbe": der Haken davor traegt den
   Zustand auch fuer Rot-Gruen-Schwaeche und in Graustufen (WCAG 1.4.1). */
.chip[aria-pressed="true"]::before {
  content: '';
  width: 12px;
  height: 12px;
  margin-right: 7px;
  flex: none;
  background: currentColor;
  clip-path: polygon(14% 47%, 0 61%, 40% 100%, 100% 22%, 85% 8%, 38% 71%);
}

.chip[aria-pressed="true"] { background: var(--primary-soft); border-color: var(--primary-line); color: var(--on-primary-soft); }

.settings-grid { display: grid; grid-template-columns: 1fr 96px; gap: 8px 12px; align-items: center; }
.settings-grid label { margin: 0; font-weight: 500; font-size: .9rem; }
.settings-grid input { margin: 0; }

.empty-state { text-align: center; padding: 26px 10px; color: var(--muted); }
.empty-state .ico.big {
  display: block;
  width: 38px;
  height: 38px;
  margin: 0 auto 10px;
  stroke-width: 1.4;
  color: var(--primary);
}

/* Ausnahmeraster fuer die Kochzeit: Tag links, Mittag und Abend daneben. */
.budget-grid {
  display: grid;
  grid-template-columns: 1fr 74px 74px;
  gap: 6px 8px;
  align-items: center;
  margin-top: 8px;
}
.budget-cell { width: 100%; text-align: center; }

/* Tabu-Zeile: Name links, die drei Formen daneben, Entfernen rechts. */
.tabu-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
}
.tabu-row:last-child { border-bottom: 0; }
.tabu-name { font-weight: 550; min-width: 6.5em; }
.tabu-row .chips { flex: 1; }

/* Naehrwerte der Woche: Bezeichnung, Balken, Wert. */
.nutrition-grid {
  display: grid;
  grid-template-columns: 7.5em 1fr auto;
  gap: 7px 10px;
  align-items: center;
}
.nutrition-bar {
  height: 8px;
  border-radius: 99px;
  background: var(--surface-2);
  overflow: hidden;
}
.nutrition-bar > i { display: block; height: 100%; border-radius: 99px; }
.nutrition-bar > i.ok { background: var(--ok); }
.nutrition-bar > i.warn { background: var(--warn); }
.nutrition-bar > i.bad { background: var(--bad); }
.nutrition-value { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* -------------------------------------------------------- Glas-Ergaenzungen -- */

/* Eigenes Icon-Set, als Sprite in index.html eingebettet: kein zweiter Abruf,
   offline ohne Service-Worker-Eintrag, und currentColor greift sicher.
   Nur stroke-width wird geerbt - duenner Strich bei grossen, kraeftiger bei
   kleinen Symbolen, sonst wirkt der Umriss falsch gewichtet. */
.ico {
  width: 24px;
  height: 24px;
  flex: none;
  stroke-width: 1.5;
  vertical-align: -.28em;
}
.tabs .ico { width: 21px; height: 21px; }
.btn .ico, .icon-btn .ico { width: 18px; height: 18px; stroke-width: 1.8; }
.item .box .ico { width: 15px; height: 15px; stroke-width: 2.2; }
.ico-mark { width: 46px; height: 46px; stroke-width: 1.4; color: var(--primary); }
.item-sub .ico, .item-name .ico { width: 14px; height: 14px; stroke-width: 1.8; }

/* Eingabefelder im Fokus: Flieder statt Browser-Blau. Der Ring aus
   :focus-visible bleibt stehen - der Schein allein kam auf 1,22:1 gegen die
   Karte, und wer per Tastatur durch die Einstellungen geht, muss sehen, in
   welchem der zwoelf Zahlenfelder er steht (WCAG 2.4.11). */
input:focus, textarea:focus, select:focus {
  border-color: var(--primary-line);
  box-shadow: 0 0 0 3px rgba(145, 132, 217, .16);
}

.btn:hover { transform: translateY(-1px); }
.btn-primary:hover {
  background: linear-gradient(180deg, rgba(145, 132, 217, .42), rgba(145, 132, 217, .2));
}
.btn-ghost:hover { background: rgba(233, 233, 237, .1); }
.icon-btn:hover { border-color: var(--primary-line); }

.badge.mint { background: var(--accent-2-soft); color: var(--accent-2); }
.badge .ico { width: 12px; height: 12px; stroke-width: 2.1; vertical-align: -.16em; }
.mit-symbol { display: flex; align-items: center; gap: 7px; }
.mit-symbol .ico { width: 19px; height: 19px; color: var(--primary); }

/* Modal ist ein Bottom-Sheet: die Hand liegt unten, der Daumen erreicht es dort. */
#modal.overlay { align-items: flex-end; padding: 0; }
#modal .card {
  width: 100%;
  max-width: 620px;
  margin: 0 10px 10px;
  border-radius: var(--radius-sheet);
  backdrop-filter: blur(28px) saturate(170%);
  -webkit-backdrop-filter: blur(28px) saturate(170%);
  border-color: rgba(233, 233, 237, .16);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .16), 0 -10px 60px rgba(0, 0, 0, .55);
  animation: sheet .5s cubic-bezier(.2, .8, .3, 1) both;
}
#modal .card::before {
  content: '';
  display: block;
  width: 38px;
  height: 4px;
  margin: 0 auto 12px;
  border-radius: 99px;
  background: rgba(233, 233, 237, .22);
}

/* Ampel: eine Leiste aus Segmenten, ein Segment je Regel. */
.traffic-bars { display: flex; gap: 4px; margin-bottom: 10px; }
.traffic-bars > i {
  flex: 1;
  height: 5px;
  border-radius: 99px;
  transform-origin: left;
  animation: bar .5s cubic-bezier(.2, .8, .3, 1) both;
}
.traffic-bars > i.ok { background: var(--ok); }
.traffic-bars > i.warn { background: var(--warn); }
.traffic-bars > i.bad { background: var(--bad); }

/* Hover nur, wo es wirklich einen Zeiger gibt: auf dem Handy loeste die
   Hover-Emulation beim Antippen ein seitliches Wegrutschen der Zeile aus. */
@media (hover: hover) and (pointer: fine) {
  .recipe-row:hover { border-color: var(--primary-line); transform: translateY(-1px); }
  .item:hover:not(.checked) { border-color: var(--primary-line); transform: translateX(2px); }
}

/* Abhaken: Mint als Bestaetigung, mit Schein. */
.item .box {
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  border: 1.5px solid var(--field-line);
  border-radius: 7px;
  background: transparent;
  cursor: pointer;
  transition: background .18s, border-color .18s;
}
.item.checked .box {
  background: var(--accent-2);
  border-color: var(--accent-2);
  color: var(--on-accent-2);
  box-shadow: 0 0 14px var(--accent-2-line);
  animation: pop .45s both;
}
.item:not(.checked) .box .ico { display: none; }

/* Statuspunkt in der Verbindungszeile. */
.status-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 99px;
  margin-right: 6px;
  background: var(--ok);
  box-shadow: 0 0 10px var(--ok-glow);
}
.status-dot.connecting { background: var(--warn); box-shadow: 0 0 10px var(--warn-glow); }
.status-dot.offline { background: var(--bad); box-shadow: 0 0 10px var(--bad-glow); }

/* Der Ansichtswechsel blendet ein - app.js ersetzt das Markup ohnehin. */
#view { animation: rise .28s both; }

/* ------------------------------------------------------------------ Login -- */

#login { align-items: center; position: relative; overflow: hidden; }

/* Zwei weiche Farbkreise, die langsam wandern - der Grund lebt, ohne zu stoeren. */
#login::before, #login::after {
  content: '';
  position: absolute;
  border-radius: 50%;
  filter: blur(18px);
  pointer-events: none;
}
#login::before {
  width: 260px; height: 260px; left: -60px; top: 8%;
  background: rgba(145, 132, 217, .35);
  animation: drift 17s ease-in-out infinite;
}
#login::after {
  width: 300px; height: 300px; right: -80px; bottom: 6%;
  background: rgba(95, 201, 196, .18);
  animation: drift 21s ease-in-out infinite reverse;
}

.login-wrap { width: 100%; max-width: 420px; position: relative; z-index: 1; }
.login-head { text-align: center; margin-bottom: 18px; }
.login-mark { font-size: 46px; line-height: 1; margin-bottom: 8px; }
.login-head h1 { font-size: 27px; font-weight: 500; margin: 0 0 4px; }
.login-head p { font-size: 14px; margin: 0; }

.login-card { position: relative; overflow: hidden; border-radius: 18px; }
.login-card .sheen {
  position: absolute;
  top: 0; bottom: 0; left: 0;
  width: 40%;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .07), transparent);
  animation: sheen 5.5s 1s ease-in-out infinite;
}
#login-submit { min-height: 52px; font-size: 17px; }

/* ------------------------------------------------------------ Kleinigkeiten -- */

.day-head .heute {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--on-primary-soft);
}
.meal.done .emoji { opacity: .55; }
/* 40 statt 34: drei Knoepfe nebeneinander muessen einhandig treffbar sein, ohne
   dass der Gerichtsname auf vier Zeilen bricht. Voll 44 wuerde auf 375px die
   Namensspalte auf rund 120px druecken. */
.meal-actions { gap: 6px; }
.meal-actions .icon-btn { width: 40px; height: 40px; }
.meal-actions .icon-btn.done-on {
  background: var(--accent-2-soft);
  border-color: var(--accent-2-line);
  color: var(--accent-2);
}

/* Vier gleich grosse Knoepfe statt zwei Reihen - spart eine Zeile Hoehe. */
.btn-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 7px;
}
.btn-grid .btn { min-height: 40px; font-size: 13px; padding: 0 10px; }

/* Das echte Kontrollkaestchen bleibt im Markup: Tastatur und Screenreader
   brauchen es. Sichtbar ist die gestaltete Flaeche daneben. */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
.item:focus-within .box { outline: 2px solid var(--primary); outline-offset: 2px; }

/* Der Hinweis nennt das Symbol, das danebensteht - vorher stand dort ein
   Emoji-Schloss, waehrend der Knopf selbst gar nichts zeichnete. */
.plan-hint {
  display: flex;
  align-items: center;
  gap: 5px;
  flex-wrap: wrap;
  margin: 10px 0 0;
}
.plan-hint .ico { width: 15px; height: 15px; stroke-width: 1.9; vertical-align: -.2em; }

/* --------------------------------------------------------- Gerichtssymbole -- */

/* Gefuellte Flaechen statt Striche: ein duenner Umriss verliert bei 22 px die
   Silhouette, sobald das Ding massig ist. Bedienung ist Linie, Gericht Flaeche. */
.dish {
  display: block;
  /* Etwas groesser als die 28px-Emoji davor: eine einfarbige Flaeche wirkt
     leichter als ein buntes Emoji und muss die Spalte trotzdem tragen. */
  width: 33px;
  height: 33px;
  color: var(--dish);
}
.recipe-row .dish, .modal-body .dish { width: 24px; height: 24px; }
/* .emoji zentriert bisher ueber text-align - eine Blockflaeche braucht Rand. */
.emoji .dish { margin: 0 auto; }

/* Auswahl im Rezept-Editor statt eines Feldes, in das man ein Emoji tippt. */
.symbol-picker {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(46px, 1fr));
  gap: 6px;
  margin-top: 6px;
}
.symbol-knopf {
  display: grid;
  place-items: center;
  min-height: var(--tap);
  padding: 4px;
  border: 1px solid var(--border);
  border-radius: var(--radius-field);
  background: var(--surface-2);
  cursor: pointer;
}
.symbol-knopf .dish { width: 26px; height: 26px; }
.symbol-knopf[aria-pressed="true"] {
  border-color: var(--primary-line);
  background: var(--primary-soft);
}
.symbol-knopf[aria-pressed="true"] .dish { color: var(--on-primary-soft); }

/* ------------------------------------------------------- Aufklappen (fold) -- */

/* Progressive Offenlegung ueber <details>. Bewusst kein eigenes Aufklapp-Bauteil:
   das native Element kann Tastatur, Screenreader und Browser-Suche schon. */
.fold > summary,
.menu > summary {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  column-gap: 10px;
  row-gap: 9px;
  min-height: var(--tap);
  margin: -4px -4px 0;
  padding: 4px;
  border-radius: var(--radius-row);
  list-style: none;
  cursor: pointer;
}
.fold > summary > h2, .menu > summary > h2 { margin: 0; }
.fold > summary > .traffic-bars { grid-column: 1 / -1; margin: 0; }
.fold > summary::-webkit-details-marker,
.menu > summary::-webkit-details-marker { display: none; }

.fold > summary::after,
.menu > summary::after {
  content: '';
  width: 9px;
  height: 9px;
  /* Ohne die feste Zelle rutscht der Pfeil unter die Ampelleiste, weil die
     ueber alle Spalten geht und damit eine zweite Reihe aufmacht. */
  grid-row: 1;
  grid-column: 3;
  justify-self: end;
  align-self: center;
  flex: none;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .18s cubic-bezier(.2, .8, .3, 1);
}
.fold[open] > summary::after,
.menu[open] > summary::after { transform: rotate(-135deg) translate(-3px, -3px); }

.fold > summary > .grow { min-width: 0; }
.fold-body { padding-top: 12px; }

/* Die Massenaktionen der Liste liegen hier drin: im Laden zaehlt die Liste,
   nicht der Werkzeugkasten. */
.menu { margin-top: 10px; }
.menu > summary { font-size: 13px; font-weight: 600; color: var(--muted); }
.menu-body { padding-top: 10px; }

/* ------------------------------------------------------- Kopf der Einkaufsliste -- */

/* Klebt unter der Wochenzeile, damit der Fortschritt im Gang 4 noch sichtbar
   ist. --topbar-h setzt app.js, weil die Statuszeile die Hoehe veraendert. */
.list-bar {
  position: sticky;
  top: var(--topbar-h, 40px);
  z-index: 15;
  margin: -12px -12px 12px;
  padding: 10px 14px 12px;
  background: var(--glass-bar);
  backdrop-filter: var(--glass-blur-bar);
  -webkit-backdrop-filter: var(--glass-blur-bar);
  border-bottom: 1px solid var(--border);
}

.list-bar-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 8px;
}
.list-bar h2 { margin: 0; font-size: 15px; }
.list-bar-count {
  font-variant-numeric: tabular-nums;
  font-size: 15px;
  font-weight: 600;
  color: var(--accent-2);
}
.list-bar .progress { height: 4px; }

/* --------------------------------------------------------- Rueckgaengig -- */

.toast-undo {
  display: flex;
  align-items: center;
  gap: 14px;
}
.toast-undo button {
  min-height: 36px;
  padding: 0 12px;
  border: 1px solid var(--primary-line);
  border-radius: 9px;
  background: var(--primary-soft);
  color: var(--on-primary-soft);
  font: 600 13px/1 'Hanken Grotesk', system-ui, sans-serif;
  cursor: pointer;
}

/* ----------------------------------------------- Flaechen, die der Browser stellt -- */

/* Auswahl, Schreibmarke und Bildlaufleiste gehoeren zum Bild wie alles andere. */
::selection { background: var(--primary-soft); color: var(--on-primary); }
input, textarea, select { caret-color: var(--primary); accent-color: var(--primary); }
* { scrollbar-color: rgba(233, 233, 237, .22) transparent; scrollbar-width: thin; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  border: 3px solid transparent;
  border-radius: 99px;
  background: rgba(233, 233, 237, .22);
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: rgba(233, 233, 237, .34); background-clip: content-box; }

/* ---------------------------------------------------------------- Desktop -- */

/* Auf dem Handy sind das die Tab-Leiste unten und eine Spalte Karten. Ab hier
   wird daraus eine Seitenleiste links und ein Wochenraster.

   Die Woche bekommt die Breite, die sie braucht - nicht die, die uebrig bleibt.
   Sieben Spalten in 1440 px gedraengt liessen je Tag 109 px: jeder Gerichtsname
   brach ueber drei Zeilen, die Tageskarten endeten zwischen 421 und 511 px, und
   unter dem Inhalt standen 170 px leer. Deshalb drei Stufen:

     1100-1399   vier Spalten (Mo-Do / Fr-So), Auswertung darunter
     ab 1400     sieben Spalten, Auswertung darunter
     ab 1750     sieben Spalten, Auswertung in der rechten Spalte

   Erst ab rund 1750 px ist genug da, damit die Woche sieben lesbare Spalten
   behaelt UND die Auswertung daneben passt. */

.side-brand, .side-foot { display: none; }

@media (min-width: 1100px) {
  #app { padding-left: 236px; }

  .tabs {
    position: fixed;
    inset: 0 auto 0 0;
    width: 236px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 16px 12px;
    border-top: 0;
    border-right: 1px solid var(--border);
  }
  .tabs a {
    flex-direction: row;
    justify-content: flex-start;
    gap: 10px;
    padding: 0 12px;
    min-height: 42px;
    font-size: 14px;
  }
  .tabs a[aria-current="page"] { border: 1px solid var(--primary-line); }

  .side-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 4px 12px 14px;
    font-size: 16px;
    font-weight: 500;
  }
  .side-brand .ico { color: var(--primary); }

  .side-foot {
    display: block;
    margin: auto 0 0;
    padding: 10px 12px;
  }
  .side-foot-name { font-size: 14px; font-weight: 600; }

  .topbar { position: sticky; }
  .view { max-width: none; padding: 16px 24px 28px; }

  /* Grundstufe: Aktionen, darunter die Woche ueber die volle Breite, darunter
     die Auswertung in zwei Spalten. */
  .plan-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "actions" "days" "side";
    gap: 16px;
    align-items: start;
  }
  .plan-actions { grid-area: actions; margin-bottom: 0; }

  .plan-side {
    grid-area: side;
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 16px;
    align-items: start;
  }
  .plan-side > .card { margin-bottom: 0; }

  .days {
    grid-area: days;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 12px;
    align-items: start;
  }

  /* Ein Tag ist eine Spalte: Kopf, darunter Mittag und Abend als Kacheln. */
  .day { margin-bottom: 0; min-width: 0; }
  .day-head { padding: 9px 12px; font-size: 13px; }
  .day-head .date { font-size: 11px; }
  .meal {
    grid-template-columns: minmax(0, 1fr);
    /* Bewusst nicht justify-items: center. Zentrierte Rasterfelder werden auf
       ihre Inhaltsbreite gezogen statt auf die Spaltenbreite - und weil
       overflow-wrap: break-word die Mindestbreite eines Wortes nicht
       verkleinert, blieb "Gemuesepfannkuchen" 120 px breit und ragte aus einer
       88-px-Spalte heraus, wo .day{overflow:hidden} es beidseitig abschnitt.
       Gestreckt bekommt jedes Feld die Spaltenbreite und bricht sauber um;
       zentriert wird ueber text-align und die Flexboxen darunter. */
    justify-items: stretch;
    /* Das Untergitter zieht alle Mittagszellen auf die Hoehe der hoechsten.
       Ohne diese Zeile saesse der Inhalt oben und der Rest der Zelle waere
       eine Luecke; zentriert wirkt die Luft verteilt statt vergessen. */
    align-content: center;
    text-align: center;
    gap: 6px;
    padding: 12px 10px;
    transition: transform .16s, background .16s;
  }
  .meal .emoji { font-size: 26px; }
  .meal .name { font-size: 14px; }
  .meal .meta { justify-content: center; }
  /* Umbrechen statt herausragen: bei sehr schmalen Spalten stehen die drei
     Knoepfe lieber zweizeilig als halb abgeschnitten. */
  .meal-actions { justify-content: center; flex-wrap: wrap; }
  .meal:hover { transform: translateY(-3px); }

  /* Mit der Maus reicht eine kleinere Flaeche als mit dem Daumen. */
  .meal-actions .icon-btn { width: 34px; height: 34px; }

  /* Leerer Slot laedt zum Fuellen ein, statt nur leer zu wirken. */
  .meal.empty {
    border: 1px dashed rgba(233, 233, 237, .2);
    border-radius: var(--radius-row);
    margin: 6px;
  }
  .meal.empty:hover { border-color: var(--primary-line); }
  .meal.empty .emoji { opacity: .5; }
  .meal.empty .ico { width: 22px; height: 22px; }

  /* Ein Formular liest sich nicht ueber 1146 px: die Beschriftung stand links,
     ihr Feld 900 px weiter rechts. Auf dieser Breite bleibt beides beieinander. */
  .formular-spalte { max-width: 760px; }

  /* Einkaufsliste und Rezepte liefen als eine 1146 px breite Spalte: zwischen
     "Brokkoli 400 g" und dem Papierkorb lag ein Meter Leere, und die 43 Posten
     ergaben 3353 px Scrollweg. Im Spaltensatz bleibt die Reihenfolge erhalten -
     man liest die Spalte hinunter und dann die naechste, wie auf einem Zettel. */
  .spalten-liste {
    column-count: 2;
    column-gap: 22px;
  }
  .spalten-liste > * { break-inside: avoid; }
  /* Die Kategorie darf nicht allein am Spaltenfuss stehen bleiben. */
  .spalten-liste > .cat-head { break-after: avoid; }
  .spalten-liste > .cat-head:first-child { margin-top: 2px; }

  /* Mittag und Abend sollen ueber alle Tage auf einer Linie liegen - sonst
     laesst sich "was gibt es diese Woche abends" nicht in einer Zeile lesen,
     und die Spalten enden ausgefranst. Das Untergitter reicht jedem Tag die
     Zeilen des Wochenrasters durch. */
  @supports (grid-template-rows: subgrid) {
    .days { grid-template-rows: repeat(2, auto 1fr 1fr); }
    .day {
      display: grid;
      grid-row: span 3;
      grid-template-rows: subgrid;
    }
  }
}

/* Ab hier tragen sieben Spalten: rund 155 px je Tag, Namen brechen hoechstens
   zweizeilig. */
@media (min-width: 1400px) {
  .days { grid-template-columns: repeat(7, minmax(0, 1fr)); }

  @supports (grid-template-rows: subgrid) {
    .days { grid-template-rows: auto 1fr 1fr; }
  }
}

/* Ab hier ist Platz fuer beides nebeneinander. */
/* Drei Spalten, sobald sie je rund 380 px behalten. */
@media (min-width: 1500px) {
  .spalten-liste { column-count: 3; }
}

@media (min-width: 1750px) {
  .view { margin: 0 auto; max-width: 1800px; }

  .plan-grid {
    grid-template-columns: minmax(0, 1fr) 340px;
    grid-template-areas: "actions side" "days side";
  }
  .plan-side { grid-template-columns: minmax(0, 1fr); }
}
