/* ============================================================
   workbench — Kelziholz Backoffice, Design "www.kelziholz.de"
   ============================================================
   Drilling neben workbench.css (klassisch) und workbench-warm.css.
   Wieder als exakte Kopie von workbench.css entstanden und dann
   umgestaltet -- gleiche Klassennamen, gleiches Markup, umgeschaltet
   wird nur der <link> in basis.html.

   Vorbild ist der oeffentliche Shop www.kelziholz.de. Von dort
   abgenommen (Stand August 2026, aus den berechneten Stilwerten der
   Seite gelesen, nicht geschaetzt):

     Schrift      Quicksand, durchgehend, auch in den Ueberschriften
     Grund        reines Weiss #FFFFFF, Wechselband #F9F9F9
     Text         reines Schwarz, Sekundaertext #222 und Grau
     Akzentband   Sand #DDD0C0 mit weisser Schrift (Kopf, Fuss, Banner)
     Schaltflaechen  schwarz auf weiss, Radius 40px -- also Pillen
     Ueberschriften  30px, Schnitt 500, ohne Sperrung, oft zentriert
     Flaechen     keine Schatten, Bilder ohne Rundung

   Der Shop ist damit das genaue Gegenteil des warmen Designs: nicht
   Papier und Holz, sondern Weiss und Schwarz mit Sand als einzigem
   Farbakzent.

   Drei Abweichungen, alle aus demselben Grund wie im warmen Design --
   ein Backoffice ist keine Schaufensterseite:

   1. Zahlen bleiben Monospace. Quicksand ist rund und geometrisch;
      Preiskolonnen wuerden darin nicht fluchten.

   2. --accent bleibt ein zurueckgenommenes Gruen. Der Shop kennt als
      Interaktionsfarbe nur Schwarz -- hier bedeutet --accent aber auch
      "gut" (.chip.good, .ar-marge-dot.good, .steps.done, der
      Fortschrittsring). Schwarz kann das nicht tragen, und gegen
      --crit-Rot braucht es einen eigenen Farbton. Das Schwarz des Shops
      steckt stattdessen in --brand und faerbt genau das, was es dort
      auch faerbt: Wortmarke und Hauptschaltflaeche.

   3. Der Shop wechselt weisse und hellgraue Abschnittsbaender ab. Hier
      traegt das hellgraue Band den Grund und die Karten sind weiss --
      andersherum waeren sie auf weissem Grund nur noch an ihrer
      Haarlinie zu erkennen, und davon stehen Dutzende auf einer Seite.
      Weiss bleibt trotzdem die vorherrschende Flaeche, weil die Karten
      den groessten Teil des Bildes ausmachen.
   ============================================================ */

/* Quicksand liegt bereits unter static/schrift/ -- eingefuehrt mit dem
   warmen Design, hier dieselben Dateien. Fraunces braucht dieses Design
   nicht: der Shop setzt auch die Ueberschriften in Quicksand. */
@font-face {
  font-family: 'Quicksand';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('/static/schrift/quicksand-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191,
    U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Quicksand';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url('/static/schrift/quicksand-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
  /* Farbe -- Weiss, Schwarz, Sand. Sonst nichts. */
  --ground:      #F7F7F7;
  --surface:     #FFFFFF;
  --surface-2:   #F1F1F1;
  --sand:        #DDD0C0;
  --line:        #E9E9E9;
  --line-strong: #CFCFCF;
  --ink:         #000000;
  --ink-2:       #3C3C3C;
  --ink-3:       #767676;
  --accent:      #4E7A5E;
  --accent-soft: #E7EFE9;
  --accent-ink:  #33553F;
  --warn:        #9A6B22;
  --warn-soft:   #F6EBD8;
  --crit:        #A83A2E;
  --crit-soft:   #F6E0DD;

  /* Das Schwarz des Shops: Wortmarke und Hauptschaltflaeche. */
  --brand:       #000000;
  --brand-ink:   #FFFFFF;

  /* Typografie -- eine Schrift fuer alles, wie im Shop. */
  --ui: 'Quicksand', ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --data: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;

  /* Raster -- flach und kantig, aber Schaltflaechen als Pillen: das ist
     neben Schwarz das auffaelligste Merkmal des Shops. */
  --r: 4px;
  --pille: 999px;
  --pad: 16px;
  --rail: 236px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --ground:      #0E0E0E;
    --surface:     #161616;
    --surface-2:   #1F1F1F;
    --sand:        #6A5F51;
    --line:        #2A2A2A;
    --line-strong: #3E3E3E;
    --ink:         #FFFFFF;
    --ink-2:       #BDBDBD;
    --ink-3:       #9A9A9A;
    --accent:      #7FB093;
    --accent-soft: #1B2A20;
    --accent-ink:  #A5CDB4;
    --warn:        #D2A05A;
    --warn-soft:   #2E2417;
    --crit:        #DE7A6D;
    --crit-soft:   #33201D;
    --brand:       #FFFFFF;
    --brand-ink:   #000000;
  }
}

:root[data-theme="dark"] {
  --ground:      #0E0E0E;
  --surface:     #161616;
  --surface-2:   #1F1F1F;
  --sand:        #6A5F51;
  --line:        #2A2A2A;
  --line-strong: #3E3E3E;
  --ink:         #FFFFFF;
  --ink-2:       #BDBDBD;
  --ink-3:       #9A9A9A;
  --accent:      #7FB093;
  --accent-soft: #1B2A20;
  --accent-ink:  #A5CDB4;
  --warn:        #D2A05A;
  --warn-soft:   #2E2417;
  --crit:        #DE7A6D;
  --crit-soft:   #33201D;
  --brand:       #FFFFFF;
  --brand-ink:   #000000;
}

:root[data-theme="light"] {
  --ground:      #F7F7F7;
  --surface:     #FFFFFF;
  --surface-2:   #F1F1F1;
  --sand:        #DDD0C0;
  --line:        #E9E9E9;
  --line-strong: #CFCFCF;
  --ink:         #000000;
  --ink-2:       #3C3C3C;
  --ink-3:       #767676;
  --accent:      #4E7A5E;
  --accent-soft: #E7EFE9;
  --accent-ink:  #33553F;
  --warn:        #9A6B22;
  --warn-soft:   #F6EBD8;
  --crit:        #A83A2E;
  --crit-soft:   #F6E0DD;
  --brand:       #000000;
  --brand-ink:   #FFFFFF;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--ui);
  font-size: 15px;
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}

:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: 2px;
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* ---------- Grundbausteine ---------- */

.mono { font-family: var(--data); font-variant-numeric: tabular-nums; }

.eyebrow {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* Der Shop setzt Abschnittsueberschriften in 30px, Schnitt 500, ohne
   Sperrung -- gross und leicht statt klein und fett. Hier eine Stufe
   zurueckgenommen: 30px stehen ueber einer Schaufensterzeile gut, ueber
   einer Bestelltabelle nehmen sie nur Platz weg. Der Schnitt 500 und der
   Verzicht auf negative Sperrung bleiben, das macht den Ton aus. */
h1, h2, h3 { text-wrap: balance; margin: 0; font-weight: 500; }
h1 { font-size: 25px; letter-spacing: 0; }
h2 { font-size: 16px; }
h3 { font-size: 13.5px; }

/* Quicksand laeuft leicht -- halbfette Stellen brauchen mehr Gewicht,
   um sich vom Fliesstext abzusetzen. */
strong, b { font-weight: 700; }

a { color: inherit; }

/* ---------- Gerüst ---------- */

.app {
  display: grid;
  grid-template-columns: 1fr;
  min-height: 100vh;
}
/* Ohne dies bläht eine breite Tabelle die Grid-Spalte auf und der Rumpf
   scrollt seitwärts, statt dass die Tabelle in sich scrollt. */
.app > * { min-width: 0; }
.split > *, .split-even > * { min-width: 0; }

/* Seitenleiste (ab Desktop) */
.rail { display: none; }

/* ---------- Marke ----------
   Das Signet ist das Monogramm KH (Kelziholz). Das eigentliche Firmenlogo --
   die Schreibschrift im gebrochenen Ring, static/kelziholz.svg -- traegt bei
   Favicon-Groesse nicht: sein Strichverhaeltnis liegt bei rund 1:70, die Linie
   waere bei 16 px ein Bruchteil eines Pixels breit. Es bleibt deshalb dem
   grossen Auftritt vorbehalten, hier steht das reduzierte Monogramm.
   Das Signet traegt als einziges Element die Akzentfarbe; die Wortmarke bleibt
   Text (kein Bild), damit sie scharf und kopierbar bleibt. */
.marke {
  width: 1.55em;
  height: 1.55em;
  flex: none;
  color: var(--brand);
}

.rail-brand {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 20px var(--pad) 18px;
  border-bottom: 1px solid var(--line);
  color: inherit;
  text-decoration: none;
}
.rail-brand .wortmarke { display: flex; flex-direction: column; line-height: 1.15; }
.rail-brand strong { font-size: 18px; font-weight: 600; letter-spacing: 0; }
.rail-brand .wortmarke span { font-size: 10px; color: var(--ink-3); letter-spacing: 0.1em; text-transform: uppercase; }
.rail-brand:hover .marke { opacity: .7; }

.rail-nav { display: flex; flex-direction: column; padding: 10px 8px; gap: 1px; }

/* Im Prototyp waren die Navigationspunkte <button data-go>. Echte Seiten
   sind echte Links -- die Regeln gelten deshalb fuer beides. */
.rail-nav a,
.rail-nav button {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 10px;
  border: 0;
  border-radius: var(--r);
  background: none;
  color: var(--ink-2);
  font: inherit;
  font-size: 14px;
  text-align: left;
  cursor: pointer;
  transition: background 120ms ease, color 120ms ease;
}
.rail-nav a { text-decoration: none; }
.rail-nav a:hover,
.rail-nav button:hover { background: var(--surface-2); color: var(--ink); }
/* Das Sandband ist im Shop der einzige Farbakzent (Kopfzeile, Fuss,
   Cookie-Banner). Hier markiert es die aktive Seite -- der einzige Ort in
   der Maske, der ihn bekommt. */
.rail-nav a[aria-current="page"],
.rail-nav button[aria-current="page"] {
  background: var(--sand);
  color: var(--ink);
  font-weight: 700;
}
.rail-nav .glyph { width: 17px; text-align: center; opacity: 0.75; font-size: 13px; }
.rail-nav .count {
  margin-left: auto;
  font-family: var(--data);
  font-size: 11px;
  color: var(--ink-3);
}
.rail-sep { height: 1px; background: var(--line); margin: 10px 10px; }

/* Kopfzeile (mobil) */
.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px var(--pad);
  background: var(--ground);
  border-bottom: 1px solid var(--line);
}
.topbar .who { display: flex; align-items: center; gap: 6px; }
.topbar .who .marke { width: 20px; height: 20px; margin-right: 1px; }
.topbar .who strong { font-size: 17px; font-weight: 600; letter-spacing: 0; }
.topbar .who span { font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-3); }
.topbar .spacer { flex: 1; }

.icon-btn {
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink-2);
  border-radius: var(--r);
  padding: 6px 9px;
  font: inherit;
  font-size: 13px;
  cursor: pointer;
}
.icon-btn:hover { background: var(--surface-2); }

/* Inhaltsbereich */
.main {
  padding: var(--pad) var(--pad) 92px;
  max-width: 1140px;
  width: 100%;
}

/* Im Prototyp blendete JS die Screens um. Hier ist jede Seite eine eigene
   URL -- der Screen ist deshalb immer sichtbar. */
.screen { display: block; }

.screen-head {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 14px;
}
.screen-head .grow { flex: 1; min-width: 180px; }
.screen-head p { margin: 2px 0 0; color: var(--ink-3); font-size: 13px; }

/* Artikelkopf: gewaehlte Variante und Kennzahlen, ueber den Reitern und
   damit in jedem Reiter sichtbar. Vorher steckte die Variante als
   hervorgehobene Tabellenzeile mitten auf der Seite und die Kennzahlen
   standen im letzten Block -- beides ausser Sicht, sobald man arbeitete. */
.artikelkopf {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 12px;
  padding: 12px 14px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  
}
.variantenwahl { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.variantenwahl > label {
  font-size: 10.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  font-weight: 700;
}
.variantenwahl form { display: flex; align-items: center; gap: 6px; }
.variantenwahl select { min-height: 34px; padding: 5px 8px; font-size: 13px; max-width: 320px; }

.kennleiste { display: flex; gap: 20px; flex-wrap: wrap; margin-left: auto; }
.kennleiste .kennwert { min-width: 84px; }
.kennleiste .k {
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-3);
  white-space: nowrap;
}
.kennleiste .v {
  font-size: 19px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.25;
}
.kennleiste .v.gut { color: var(--accent-ink); }
.kennleiste .v.warn { color: var(--warn); }
.kennleiste .v.schlecht { color: var(--crit); }

/* Reiter der Artikel-Detailseite: vier Taetigkeiten statt neun Bloecke
   untereinander. Nicht zu verwechseln mit `.tabbar` -- das ist die
   Hauptnavigation am unteren Bildschirmrand. */
.reiter {
  display: flex;
  gap: 6px;
  flex-wrap: nowrap;
  overflow-x: auto;
  margin-bottom: 12px;
  padding-bottom: 2px;
}
.reiter a {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 15px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-3);
  text-decoration: none;
  white-space: nowrap;
  border: 1px solid var(--line-strong); border-radius: var(--pille);
  background: var(--surface);
}
.reiter a:hover { color: var(--ink); }
.reiter a.on { background: var(--ink); border-color: var(--ink); color: var(--ground); }
/* Der Warnpunkt am Reiter (fehlendes GPSR-Blatt) steht mal auf hellem,
   mal auf dunklem Grund -- deshalb hier kraeftig statt in der weichen
   Chip-Farbe, die auf dem aktiven Reiter untergehen wuerde. */
.reiter a .chip { padding: 0 6px; font-size: 10px; }
.reiter a .chip.warn { background: var(--warn); color: var(--ground); border-color: transparent; }

@media (max-width: 720px) {
  .artikelkopf { align-items: flex-start; }
  .kennleiste { margin-left: 0; gap: 14px; }
  .kennleiste .v { font-size: 17px; }
}

/* Tab-Leiste (mobil, Daumenreichweite) */
.tabbar {
  position: fixed;
  bottom: 0; left: 0; right: 0;
  z-index: 30;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  background: var(--surface);
  border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom);
}
.tabbar a,
.tabbar button {
  border: 0;
  text-decoration: none;
  background: none;
  color: var(--ink-3);
  font: inherit;
  font-size: 10px;
  letter-spacing: 0.02em;
  padding: 9px 2px 10px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  border-top: 2px solid transparent;
  margin-top: -1px;
}
.tabbar .glyph { font-size: 15px; line-height: 1; }
.tabbar a[aria-current="page"],
.tabbar button[aria-current="page"] {
  color: var(--ink);
  border-top-color: var(--brand);
  font-weight: 700;
}

/* ---------- Karten & Listen ---------- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
}
.card + .card { margin-top: 12px; }

/* Eine gemeinsame Aufklapp-Optik fuer jeden Klapp-Block im Haus (Karten,
   Spalten im Positionen-Formular, Gruppen im Ergebnis, Gebuehren je
   Kanal) -- derselbe Pfeil, dieselbe Groesse, egal welches Element als
   <summary> darunter liegt. Ein <details> bekommt dafuer zusaetzlich die
   Klasse "klappbar"; die eigentliche <summary> behaelt ihre eigene
   Layout-Klasse (card-head, spaltenkopf, gruppenkopf, ...) und liefert
   nur display:flex und ihren eigenen gap -- der Pfeil ordnet sich dort
   automatisch als erstes Element ein. Drehung statt Zeichenwechsel
   (▸ vs. ▾), damit der Pfeil nicht je Kontext unterschiedlich aussieht. */
details.klappbar > summary { cursor: pointer; list-style: none; }
details.klappbar > summary::-webkit-details-marker { display: none; }
details.klappbar > summary::before {
  content: "▸";
  display: inline-block;
  margin-right: 6px;
  font-size: 10px;
  color: var(--ink-3);
  transition: transform 120ms ease;
}
details.klappbar[open] > summary::before { transform: rotate(90deg); }

.card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 14px;
  border-bottom: 1px solid var(--line);
}
.card-head .grow { flex: 1; }
.card-body { padding: 14px; }

.stack { display: flex; flex-direction: column; gap: 12px; }
.row-between { display: flex; align-items: center; justify-content: space-between; gap: 10px; }

/* Kennzahlen */
.kpis {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
  margin-bottom: 14px;
}
.kpi {
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--line-strong);
  border-radius: var(--r);
  padding: 11px 12px;
}
.kpi.is-crit { border-left-color: var(--crit); }
.kpi.is-warn { border-left-color: var(--warn); }
.kpi.is-good { border-left-color: var(--accent); }
.kpi .n {
  font-family: var(--data);
  font-variant-numeric: tabular-nums;
  font-size: 25px;
  line-height: 1.1;
  letter-spacing: -0.02em;
  display: block;
}
.kpi .l { font-size: 11.5px; color: var(--ink-3); margin-top: 3px; display: block; }

/* Listenzeilen */
.list { display: flex; flex-direction: column; }
a.list-row { text-decoration: none; }
.list-row {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  padding: 12px 14px;
  border: 0;
  border-top: 1px solid var(--line);
  border-left: 3px solid transparent;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: background 110ms ease;
}
.list-row:first-child { border-top: 0; }
.list-row:hover { background: var(--surface-2); }
.list-row.is-crit { border-left-color: var(--crit); }
.list-row.is-warn { border-left-color: var(--warn); }
.list-row.is-good { border-left-color: var(--accent); }
.list-row .grow { flex: 1; min-width: 0; }
.list-row .title { font-weight: 600; font-size: 14.5px; }
.list-row .sub {
  font-size: 12px;
  color: var(--ink-3);
  margin-top: 2px;
  display: flex;
  gap: 7px;
  flex-wrap: wrap;
  align-items: center;
}
.list-row .right { text-align: right; flex-shrink: 0; }
.list-row .chev { color: var(--ink-3); font-size: 14px; flex-shrink: 0; }

.dot { width: 3px; height: 3px; border-radius: 50%; background: var(--line-strong); display: inline-block; }

/* Bestellungen-Uebersicht: zwei austauschbare Ansichten (siehe ?ansicht=
   in bestellungen/liste.html), beide mit ausklappbaren Positionen. Fuer
   die Positionszeile selbst siehe .pos-zeile weiter unten -- gemeinsam
   fuer beide Ansichten per Jinja-Makro erzeugt. */
.pos-zeile { display: flex; align-items: flex-start; gap: 10px; font-size: 13px; }
.pos-zeile .grow { flex: 1; min-width: 0; }
.dringend { color: var(--crit); font-weight: 600; }

/* -- Tabelle -------------------------------------------------------------
   Aufklappen ist ein Klick auf die Zeile (onclick statt <details>: eine
   <tr> kann kein <summary> enthalten). Der Name-Link stoppt seinen Klick
   per stopPropagation, damit er navigiert statt nur auf-/zuzuklappen. */
.bl-tabelle { width: 100%; border-collapse: collapse; font-size: 13px; }
.bl-tabelle thead th {
  text-align: left;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-3);
  font-weight: 600;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line-strong);
  background: var(--surface-2);
  position: sticky;
  top: 0;
}
.bl-tabelle th.num, .bl-tabelle td.num { text-align: right; }
.bl-tabelle td { padding: 10px 12px; border-bottom: 1px solid var(--line); vertical-align: top; }
.bl-tabelle tbody tr.bl-tr { cursor: pointer; }
.bl-tabelle tbody tr.bl-tr:hover { background: var(--surface-2); }
.bl-tabelle tr.bl-tr.is-dringend td:first-child { box-shadow: inset 3px 0 0 var(--crit); }
.bl-tabelle .bl-tri { display: inline-block; width: 12px; color: var(--ink-3); font-size: 9px; transition: transform .12s ease; }
.bl-tabelle tr.bl-tr.open .bl-tri { transform: rotate(90deg); }
.bl-tabelle .bl-name { color: var(--ink); font-weight: 600; text-decoration: none; }
.bl-tabelle .bl-name:hover { color: var(--accent-ink); text-decoration: underline; }
.bl-tabelle .bl-ort { color: var(--ink-3); font-size: 12px; }
.bl-tr-detail td { padding: 0; border-bottom: 1px solid var(--line); }
.bl-tr-detail .bl-tr-detail-in { display: none; padding: 4px 12px 14px 34px; background: var(--surface-2); flex-direction: column; gap: 6px; }
.bl-tr.open + .bl-tr-detail .bl-tr-detail-in { display: flex; }

/* -- Karten ---------------------------------------------------------------
   Prozessstufe als farbige Oberkante -- dieselbe Farbzuordnung wie die
   Status-Chips (siehe STATUS_CHIP in liste.html), nur als Rand statt als
   Chip-Hintergrund. */
.bl-karten { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
.bl-karte {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 9px;
  border-top: 3px solid var(--line-strong);
  overflow: hidden;
}
.bl-karte.is-warn { border-top-color: var(--warn); }
.bl-karte.is-good { border-top-color: var(--accent); }
.bl-karte.is-crit { border-top-color: var(--crit); }
.bl-karte > summary { list-style: none; cursor: pointer; padding: 12px 14px; }
.bl-karte > summary::-webkit-details-marker { display: none; }
.bl-karte > summary:hover { background: var(--surface-2); }
.bl-karte .bl-oben { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }
.bl-karte .bl-name { color: var(--ink); font-weight: 700; text-decoration: none; font-size: 14px; }
.bl-karte .bl-name:hover { color: var(--accent-ink); text-decoration: underline; }
.bl-karte .bl-meta { margin-top: 4px; font-size: 11.5px; color: var(--ink-3); }
.bl-karte .bl-flags { display: flex; gap: 4px; margin-top: 8px; flex-wrap: wrap; }
.bl-karte .bl-flags span {
  font-size: 10px;
  padding: 2px 6px;
  border-radius: 5px;
  background: var(--surface-2);
  color: var(--ink-2);
}
.bl-karte .bl-stats { display: flex; justify-content: space-between; align-items: flex-end; margin-top: 12px; }
.bl-karte .bl-sum { font-size: 16px; font-weight: 700; font-family: var(--data); }
.bl-karte .bl-posn { font-size: 11px; color: var(--ink-3); }
.bl-karte .bl-inhalt {
  padding: 10px 14px 14px;
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 5px;
}

/* -- Artikeluebersicht: zwei austauschbare Ansichten (siehe ?ansicht= in
   artikel/liste.html), analog zu .bl-tabelle/.bl-karten bei Bestellungen.
   Aufklappen ist ein Klick auf die Zeile -- dieselbe onclick-Begruendung
   wie dort: eine <tr> kann kein <summary> enthalten. */
.ar-tabelle { width: 100%; border-collapse: collapse; font-size: 13px; }
.ar-tabelle thead th {
  text-align: left;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-3);
  font-weight: 600;
  padding: 10px 12px;
  border-bottom: 1px solid var(--line-strong);
  background: var(--surface-2);
}
.ar-tabelle th.num, .ar-tabelle td.num { text-align: right; }
.ar-tabelle td { padding: 10px 12px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.ar-tabelle tbody tr.ar-tr { cursor: pointer; }
.ar-tabelle tbody tr.ar-tr:hover { background: var(--surface-2); }
.ar-tabelle .ar-tri { display: inline-block; width: 12px; color: var(--ink-3); font-size: 9px; transition: transform .12s ease; }
.ar-tabelle tr.ar-tr.open .ar-tri { transform: rotate(90deg); }
.ar-tabelle .ar-name-zeile { display: flex; align-items: center; gap: 10px; }
.ar-tabelle .ar-name { color: var(--ink); font-weight: 600; text-decoration: none; font-size: 13.5px; }
.ar-tabelle .ar-name:hover { color: var(--accent-ink); text-decoration: underline; }
.ar-tabelle .ar-sub { font-size: 12px; color: var(--ink-3); margin-top: 1px; }
.ar-kanal-badges { display: flex; gap: 4px; }
.ar-kanal-badge {
  font-size: 10px;
  font-weight: 700;
  padding: 2px 6px;
  border-radius: 5px;
  background: var(--surface-2);
  color: var(--ink-2);
  border: 1px solid var(--line);
}
.ar-tr-detail td { padding: 0; border-bottom: 1px solid var(--line); }
.ar-tr-detail .ar-tr-detail-in {
  display: none;
  padding: 4px 14px 10px 34px;
  background: var(--surface-2);
}
.ar-tr.open + .ar-tr-detail .ar-tr-detail-in { display: block; }

/* -- Produktion ---------------------------------------------------------
   Gesamtbedarf (Kachel/Tabelle, siehe _gesamtbedarf.html) und die Prio-Liste
   (kompakte Karten, ein Klick klappt den Zuschnitt auf -- siehe _macros.html)
   sind zwei unabhaengige Bloecke: der Ansicht-Wechsel und jede Fortschritts-
   Aenderung laden nur den betroffenen Block neu, nie die ganze Seite (siehe
   routers/produktion.py) -- so behalten die anderen Karten ihren Auf/Zu-
   Zustand. */

.kopf-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 20px; flex-wrap: wrap; margin-bottom: 14px; }
.kopf-head h2 { font-size: 16px; }
.kopf-head p { margin: 3px 0 0; font-size: 12.5px; color: var(--ink-3); }

/* Ein Ring-Bauteil fuer alle drei Groessen (Gesamt-Kennzahl, Tabellenzeile,
   Kartenkopf) -- Groesse und Randdicke kommen ueber CSS-Variablen aus dem
   Markup, kein Duplikat je Einsatzort. */
.ring {
  --pct: 0;
  --ring-size: 22px;
  --ring-inset: 3px;
  width: var(--ring-size);
  height: var(--ring-size);
  border-radius: 50%;
  flex-shrink: 0;
  background: conic-gradient(var(--accent) calc(var(--pct) * 1%), var(--surface-2) 0);
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
}
.ring::after { content: ""; position: absolute; inset: var(--ring-inset); border-radius: 50%; background: var(--surface); }
.ring.ring-leer { background: var(--surface-2); }
.ring span { position: relative; font-family: var(--data); font-weight: 700; font-variant-numeric: tabular-nums; }

.gesamt-ring-block { display: flex; align-items: center; gap: 12px; background: var(--surface); border: 1px solid var(--line); border-radius: 9px; padding: 9px 16px 9px 9px; }
.gesamt-text .n { font-size: 12.5px; font-weight: 700; }
.gesamt-text .l { font-size: 11px; color: var(--ink-3); margin-top: 1px; }

/* Gesamtbedarf nach Variante -------------------------------------------- */
.bedarf-kacheln { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; margin-bottom: 30px; }
.bd-kachel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); padding: 11px 13px 12px; }
.bd-kachel-kopf { display: flex; align-items: flex-start; gap: 9px; }
.bd-kachel-titel { flex: 1; min-width: 0; }
.bd-kachel-kopf-zeile { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.bd-sku { font-family: var(--data); font-size: 12px; font-weight: 700; }
.bd-pct { font-family: var(--data); font-size: 11.5px; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.bd-pct.done { color: var(--accent-ink); }
.bd-name { font-size: 11.5px; color: var(--ink-3); margin-top: 1px; }
.bd-bar { height: 4px; border-radius: 3px; background: var(--surface-2); margin-top: 9px; overflow: hidden; }
.bd-bar > i { display: block; height: 100%; background: var(--accent); border-radius: 3px; }
.bd-teile { margin-top: 9px; display: flex; flex-direction: column; gap: 3px; }
.bd-teil-zeile { display: flex; justify-content: space-between; gap: 8px; font-size: 12px; color: var(--ink-2); }
.bd-teil-zeile b { font-family: var(--data); color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }

.bd-quick { display: flex; align-items: center; gap: 6px; }
.bd-tick {
  width: 22px; height: 22px; border-radius: 50%; border: 1.5px solid var(--line-strong);
  background: var(--surface); color: var(--ink-3); cursor: pointer; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center; font-size: 13px; line-height: 1; padding: 0;
  transition: background .12s, border-color .12s, color .12s, transform .06s;
}
.bd-tick:hover { border-color: var(--accent); color: var(--accent-ink); }
.bd-tick:active { transform: scale(.9); }
.bd-tick.maxed { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink); cursor: default; font-size: 12px; }
.bd-tick.maxed:hover { transform: none; }

/* Gesamtbedarf tabellarisch ----------------------------------------------- */
.pr-gesamt-tabelle { width: 100%; border-collapse: collapse; font-size: 13px; }
.pr-gesamt-tabelle thead th {
  text-align: left; font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--ink-3); font-weight: 600; padding: 10px 12px; border-bottom: 1px solid var(--line-strong); background: var(--surface-2);
}
.pr-gesamt-tabelle th.num, .pr-gesamt-tabelle td.num { text-align: right; }
.pr-gesamt-tabelle td { padding: 8px 12px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.pr-gesamt-ring-zelle { width: 40px; }
.pr-gesamt-var-zelle { display: flex; align-items: center; gap: 10px; }

/* Prio-Liste: kompakte Karte, aufklappbar ---------------------------------- */
.a-liste { display: flex; flex-direction: column; gap: 7px; }
.a-karte { background: var(--surface); border: 1px solid var(--line); border-left: 3px solid var(--line-strong); border-radius: var(--r); }
.a-karte.is-dringend { border-left-color: var(--crit); }
.a-kopf { display: flex; align-items: center; gap: 11px; padding: 10px 14px; cursor: pointer; flex-wrap: wrap; }
.a-tri { color: var(--ink-3); font-size: 9px; transition: transform .12s ease; flex-shrink: 0; }
.a-karte.open .a-tri { transform: rotate(90deg); }
.a-id { color: var(--ink); font-weight: 700; text-decoration: none; font-size: 13.5px; }
.a-id:hover { color: var(--accent-ink); text-decoration: underline; }
.a-restzeit { font-size: 12px; color: var(--ink-3); white-space: nowrap; }
.a-restzeit.crit { color: var(--crit); font-weight: 600; }
.a-bedarf { font-family: var(--data); font-size: 12px; color: var(--ink-2); }
.a-spacer { flex: 1; min-width: 8px; }
.a-detail { display: none; padding: 0 14px 14px; border-top: 1px solid var(--line); margin-top: -1px; }
.a-karte.open .a-detail { display: block; padding-top: 12px; }
.a-pos { padding: 10px 0; border-top: 1px solid var(--line); font-size: 13px; }
.a-pos:first-child { border-top: none; padding-top: 0; }
.a-pos-kopf { display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap; }
.a-pos-teile { color: var(--ink-2); font-size: 12px; margin-top: 5px; display: flex; flex-direction: column; gap: 2px; }
.a-mass { font-family: var(--data); color: var(--ink); white-space: nowrap; }
.a-links { display: flex; gap: 12px; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line); font-size: 11.5px; }
.a-links a { color: var(--ink-3); text-decoration: none; }
.a-links a:hover { color: var(--accent-ink); text-decoration: underline; }

.pr-warnung { font-size: 12px; color: var(--warn); background: var(--warn-soft); border-radius: 6px; padding: 5px 8px; margin-bottom: 10px; }
.pr-stepper { display: flex; align-items: center; gap: 5px; flex-shrink: 0; }
.pr-stepper .btn.sm { min-width: 26px; height: 26px; padding: 0; display: inline-flex; align-items: center; justify-content: center; }
.pr-stepper input { width: 40px; text-align: center; font-size: 13px; padding: 4px 2px; border: 1px solid var(--line); border-radius: 5px; background: var(--surface); color: var(--ink); }
.pr-stepper .pr-von { font-size: 11px; color: var(--ink-3); white-space: nowrap; }

/* Varianten-Auflistung in der Detailzeile -- eine Zeile je Variante mit
   Aktiv-Status, Bezeichnung, Fertigmass und dem manuell hinterlegten VK je
   bespieltem Kanal (VarianteKanal.vk), statt der frueheren Kanalspannen des
   gesamten Artikels. */
.va-liste { display: flex; flex-direction: column; }
.va-zeile { display: flex; align-items: center; gap: 14px; padding: 7px 0; border-bottom: 1px solid var(--line); }
.va-zeile:last-child { border-bottom: none; }
.va-zeile .status { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.va-zeile .status.aktiv { background: var(--accent); }
.va-zeile .status.inaktiv { background: var(--line-strong); }
.va-zeile .name { color: var(--ink); font-weight: 600; font-size: 13px; min-width: 150px; text-decoration: none; }
.va-zeile .name:hover { color: var(--accent-ink); text-decoration: underline; }
.va-zeile .mass { font-family: var(--data); font-size: 12px; color: var(--ink-3); min-width: 130px; }
.va-zeile .preise { display: flex; gap: 12px; margin-left: auto; flex-wrap: wrap; justify-content: flex-end; }
.va-preis { font-family: var(--data); font-size: 12px; color: var(--ink-2); white-space: nowrap; }
.va-preis b { color: var(--ink); font-weight: 600; }

.ar-thumb {
  width: 40px; height: 40px; border-radius: var(--r);
  background: var(--surface-2); border: 1px solid var(--line);
  object-fit: cover; flex-shrink: 0;
}
.ar-thumb.leer { display: flex; align-items: center; justify-content: center; color: var(--ink-3); font-size: 14px; }

/* -- Karten -- */
.ar-karten { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; }
.ar-karte {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
  text-decoration: none;
  color: var(--ink);
  display: block;
}
.ar-karte:hover { border-color: var(--line-strong); }
.ar-karte .ar-bild {
  width: 100%; aspect-ratio: 4/3; object-fit: cover;
  background: var(--surface-2); display: block;
}
.ar-karte .ar-bild.leer {
  display: flex; align-items: center; justify-content: center;
  color: var(--ink-3); font-size: 24px;
}
.ar-karte .ar-body { padding: 11px 12px 12px; }
.ar-karte .ar-name { font-size: 14px; font-weight: 600; margin: 0 0 2px; line-height: 1.3; }
.ar-karte .ar-sub { font-size: 12px; color: var(--ink-3); margin: 0 0 9px; }
.ar-karte .ar-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ar-karte .ar-preis { font-family: var(--data); font-size: 13.5px; font-variant-numeric: tabular-nums; font-weight: 600; }
.ar-karte .ar-marge { display: flex; align-items: center; gap: 5px; font-size: 12px; color: var(--ink-2); }
.ar-marge-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex-shrink: 0; }
.ar-marge-dot.good { background: var(--accent); }
.ar-marge-dot.warn { background: var(--warn); }
.ar-marge-dot.crit { background: var(--crit); }

/* Chips */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  border: 1px solid var(--line-strong);
  border-radius: 100px;
  padding: 2px 9px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ink-2);
  background: var(--surface-2);
  white-space: nowrap;
}
.chip.good { background: var(--accent-soft); border-color: transparent; color: var(--accent-ink); }
.chip.warn { background: var(--warn-soft); border-color: transparent; color: var(--warn); }
.chip.crit { background: var(--crit-soft); border-color: transparent; color: var(--crit); }
.chip.plain { background: transparent; }

/* Warnhinweis: Material oder Arbeitszeit fehlt (Kalkulation) */
.warn-flag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--warn-soft);
  color: var(--warn);
  font-size: 12px;
  line-height: 1;
  cursor: help;
  flex-shrink: 0;
  vertical-align: middle;
}

/* Bildkachel im Kopf der Musterkalkulations-Detailseite */
.k-kopf-bild {
  width: 56px; height: 56px; border-radius: var(--r);
  object-fit: cover; background: var(--surface-2); border: 1px solid var(--line);
  flex-shrink: 0;
}
.k-kopf-bild.leer { display: flex; align-items: center; justify-content: center; color: var(--ink-3); font-size: 18px; }

/* Personalisierungsdateien: Anzeige-Zeilen (bestellungen/detail.html) */
.filetable { display: flex; flex-direction: column; }
.filerow {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 10px;
  padding: 6px 0;
  border-bottom: 1px solid var(--line);
}
.filerow:last-child { border-bottom: none; }
/* Kompakter Klick-Chip statt des langen Roh-Pfads: der volle Pfad steht im
   title-Attribut, sichtbar ist nur der Hinweis (Fallback "Datei oeffnen"). */
.filerow .fileopen {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
  max-width: 100%;
  padding: 5px 10px;
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  font-weight: 600;
  font-size: 13px;
  text-decoration: none;
  color: inherit;
}
.filerow .fileopen:hover { background: var(--surface-2); }
.filerow .fileopen > span:not(.pfeil) {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.filerow .fileopen svg { flex-shrink: 0; }
.filerow .fileopen .pfeil { flex-shrink: 0; color: var(--ink-3); font-weight: 400; }
.filerow .fileopen.leer { color: var(--ink-3); font-weight: 400; }

/* Bruttomarge-Gauge: Ring-Kachel je Verkaufskanal (artikel/detail.html) */
.gauge { display: flex; gap: 12px; align-items: center; }
.gauge .ring { flex-shrink: 0; }
.gauge .ring circle { fill: none; stroke-width: 6; }
.gauge .ring .bahn { stroke: var(--line); }
.gauge .ring .wert {
  stroke-linecap: round;
  transform: rotate(-90deg);
  transform-origin: 50% 50%;
  stroke: var(--line-strong);
}
.gauge.gut .ring .wert { stroke: var(--accent); }
.gauge.warn .ring .wert { stroke: var(--warn); }
.gauge.schlecht .ring .wert { stroke: var(--crit); }
.gauge .zahl { font-family: var(--data); font-variant-numeric: tabular-nums; font-size: 20px; font-weight: 600; }
.gauge.gut .zahl { color: var(--accent-ink); }
.gauge.warn .zahl { color: var(--warn); }
.gauge.schlecht .zahl { color: var(--crit); }
.gauge .label { font-size: 11.5px; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-3); }

/* Fertigungsfortschritt: fünf Segmente = fünf reale Arbeitsschritte */
.steps { display: flex; gap: 2px; align-items: center; }
.steps i {
  display: block;
  width: 13px;
  height: 4px;
  border-radius: 1px;
  background: var(--line-strong);
  opacity: 0.45;
}
.steps i.on { background: var(--warn); opacity: 1; }
.steps.done i.on { background: var(--accent); }
.steps.sm i { width: 9px; height: 3px; }

/* Werkzeugleiste: Kategoriefilter + Suche */
.toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
}
/* Freistehende Pillen statt einer gerahmten Leiste -- so zeigt der Shop
   seine Filter, und es loest nebenbei ein Darstellungsproblem: als
   Flex-Kind wird .seg auf die volle Zeilenbreite gedrueckt und bricht
   intern um. Die zweite Zeile fuellt sie dann nur zur Haelfte, der Rest
   blieb als leere gerundete Flaeche stehen. Ohne eigenen Hintergrund
   gibt es nichts mehr, was leer aussehen koennte.

   Der aktive Filter ist eine schwarze Pille -- dieselbe Sprache wie die
   Hauptschaltflaeche. */
.seg {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 0;
  background: transparent;
  border-radius: 0;
}
.seg a {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 6px 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--pille);
  background: var(--surface);
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-2);
  text-decoration: none;
  white-space: nowrap;
}
.seg a:hover { color: var(--ink); border-color: var(--ink-3); }
.seg a.on {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--brand-ink);
  font-weight: 700;
}
.seg a .n { font-family: var(--data); font-size: 11px; color: var(--ink-3); }
.seg a.on .n { color: var(--brand-ink); opacity: .65; }

.searchform { position: relative; flex: 1; min-width: 180px; max-width: 320px; }
.searchform input {
  min-height: 38px;
  padding: 7px 34px 7px 12px;
  font-size: 13.5px;
  background: var(--surface);
}
.searchform button {
  position: absolute;
  top: 50%;
  right: 4px;
  transform: translateY(-50%);
  width: 28px;
  height: 28px;
  min-height: 0;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--ink-3);
  font-size: 14px;
  cursor: pointer;
}
.searchform button:hover { background: var(--surface-2); color: var(--ink); }

/* Charmanter Link auf die Bezugsquelle: Domain statt voller URL, das Ziel
   steht im title-Tooltip. */
.srclink {
  color: var(--accent-ink);
  font-size: inherit;
}
.srclink-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  margin-left: 5px;
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: 11px;
  text-decoration: none !important;
  vertical-align: middle;
}
.srclink-icon:hover { background: var(--sand); color: var(--ink); }

/* ---------- Tabellen ---------- */

.tablewrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
thead th {
  text-align: left;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-3);
  padding: 8px 10px;
  border-bottom: 1px solid var(--line);
  vertical-align: bottom;
  white-space: nowrap;
}
tbody td {
  padding: 9px 10px;
  border-bottom: 1px solid var(--line);
  vertical-align: middle;
}
tbody tr:last-child td { border-bottom: 0; }
td.num, th.num { text-align: right; font-family: var(--data); font-variant-numeric: tabular-nums; white-space: nowrap; }
td.dim { font-family: var(--data); font-variant-numeric: tabular-nums; white-space: nowrap; letter-spacing: -0.01em; }
tbody tr.sum td { border-top: 1px solid var(--line-strong); font-weight: 600; }
tbody tr:hover td { background: var(--surface-2); }

/* Kopfgruppe ueber mehreren Spalten (z.B. je Verkaufskanal VK + Marge) --
   die Gruppenueberschrift steht zentriert ueber ihren Spalten, eine duenne
   Linie links davon trennt sie vom vorigen Block. */
th.thgruppe { text-align: center; }
th.thgrenze, td.tdgrenze { border-left: 1px solid var(--line); }

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

/* `hidden` muss gewinnen: .field, .grid-2 und .card setzen alle ein eigenes
   display, das das Attribut sonst still ueberstimmt -- ein ausgeblendetes
   Feld waere dann trotzdem sichtbar. */
[hidden] { display: none !important; }

.field { display: flex; flex-direction: column; gap: 5px; }
.field > label { font-size: 12px; font-weight: 600; color: var(--ink-2); }
.field .hint { font-size: 11.5px; color: var(--ink-3); }

.beschreibung-text.gekuerzt {
  max-height: 4.8em;
  overflow: hidden;
  position: relative;
}
.beschreibung-text.gekuerzt::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 1.6em;
  background: linear-gradient(to bottom, transparent, var(--surface));
}

[data-dropzone].ziehen-drueber {
  outline: 2px dashed var(--accent);
  outline-offset: 2px;
  background: var(--surface-2);
}

[data-galerie] [data-bild] { cursor: grab; }
[data-galerie] [data-bild].wird-gezogen { opacity: 0.4; }
[data-galerie] [data-bild].ablageziel { outline: 2px solid var(--accent); outline-offset: 2px; }

input, select, textarea {
  font: inherit;
  font-size: 15px;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  padding: 9px 10px;
  width: 100%;
  min-height: 42px;
}
input.mono, .grid-2 input.mono { font-family: var(--data); font-variant-numeric: tabular-nums; }
input:hover, select:hover { border-color: var(--ink-3); }

.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; }

/* Pillenform und schwarze Hauptschaltflaeche -- neben der Schrift das
   auffaelligste Merkmal des Shops (dort Radius 40px, schwarz auf weiss).
   Die Hauptschaltflaeche nimmt --brand und nicht --accent: im Shop ist
   das Schwarz die Handlungsfarbe, --accent traegt hier die Bedeutung
   "gut" und wird nicht dafuer verbraucht. */
.btn {
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  border-radius: var(--pille);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  padding: 10px 18px;
  min-height: 42px;
  cursor: pointer;
  transition: background 110ms ease, border-color 110ms ease, color 110ms ease;
}
.btn:hover { background: var(--surface-2); border-color: var(--ink-3); }
.btn.primary {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--brand-ink);
}
.btn.primary:hover { background: var(--ink-2); border-color: var(--ink-2); }

/* Warnfarbe fuer den Setzen-Knopf bei negativem Deckungsbeitrag -- kein
   Verbot, nur eine ehrliche Farbe fuer eine bewusste Entscheidung. */
.btn.crit { background: var(--crit-soft); border-color: transparent; color: var(--crit); }
.btn.crit:hover { filter: brightness(0.97); }
.btn.wide { width: 100%; }
.btn.sm { padding: 6px 14px; min-height: 34px; font-size: 13px; }

/* Statusschalter: groß genug für staubige Finger */
.stepper { display: flex; flex-wrap: wrap; gap: 6px; }
.stepper button {
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink-2);
  border-radius: var(--r);
  padding: 9px 11px;
  min-height: 40px;
  cursor: pointer;
  flex: 1 1 auto;
  transition: background 110ms ease;
}
.stepper button:hover { background: var(--surface-2); }
.stepper button.on { background: var(--warn-soft); border-color: var(--warn); color: var(--warn); }
.stepper button.on.final { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink); }

/* Ergebnisblock des Kalkulators */
.result {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 14px;
}
.result .line {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 5px 0;
  font-size: 13.5px;
  color: var(--ink-2);
}
.result .line span { min-width: 0; }
.result .line b {
  font-family: var(--data);
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  color: var(--ink);
  white-space: nowrap;
  flex-shrink: 0;
}
.result .line.sub { font-size: 12.5px; color: var(--ink-3); padding-left: 12px; }

/* Gruppenkopf innerhalb des Ergebnisblocks -- Material/Versand/Arbeit,
   je mit eigener Zwischensumme. Trennlinie nur ab der zweiten Gruppe,
   die erste beginnt direkt am Rand der Karte. */
.result .gruppenkopf {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 8px 0 3px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
  border-top: 1px solid var(--line);
  margin-top: 6px;
}
.result .gruppe:first-child .gruppenkopf { border-top: 0; margin-top: 0; padding-top: 0; }
.result .gruppenkopf b {
  margin-left: auto;
  font-family: var(--data);
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
  font-weight: 600;
  text-transform: none;
  letter-spacing: normal;
}
.result .total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  margin-top: 10px;
  padding-top: 12px;
  border-top: 1px solid var(--line-strong);
}
.result .total span { font-weight: 600; font-size: 14px; }
/* Der Endpreis steht im Shop schwarz da, nicht in einer Signalfarbe --
   er ist das Ergebnis, keine Bewertung. */
.result .total b {
  font-family: var(--data);
  font-variant-numeric: tabular-nums;
  font-size: 27px;
  letter-spacing: -0.02em;
  color: var(--ink);
}

/* Anteilsbalken der Kostentreiber-Rangliste. Bewusst duenn und ohne
   Beschriftung: die Zahl steht rechts daneben, der Balken traegt nur das
   Groessenverhaeltnis -- er soll die Zeile lesbar machen, nicht sie fuellen. */
.balken {
  height: 4px;
  border-radius: 2px;
  background: var(--line);
  margin-top: 6px;
  max-width: 420px;
  overflow: hidden;
}
.balken span {
  display: block;
  height: 100%;
  background: var(--accent);
  border-radius: 2px;
}

/* Restzeit bis Versand (Bestellungen): Fortschritt zwischen Bestelleingang
   und Versandfrist -- ein voller Balken heisst hier "knapp", nicht
   "fertig". Die Ampelfarbe (siehe services/bestellung.py:restzeit_balken)
   traegt die Dringlichkeit, die Zeile daneben die Resttage in Worten. */
.restzeit { display: flex; flex-direction: column; gap: 4px; min-width: 92px; }
.restzeit .spur {
  height: 6px;
  border-radius: 3px;
  background: var(--line);
  overflow: hidden;
}
.restzeit .spur span { display: block; height: 100%; border-radius: 3px; background: var(--accent); }
.restzeit.warn .spur span { background: var(--warn); }
.restzeit.crit .spur span { background: var(--crit); }
.restzeit .txt { font-size: 11.5px; color: var(--ink-3); white-space: nowrap; }
.restzeit.warn .txt { color: var(--warn); font-weight: 600; }
.restzeit.crit .txt { color: var(--crit); font-weight: 600; }
.restzeit.sm { min-width: 68px; gap: 3px; }
.restzeit.sm .spur { height: 4px; }
.restzeit.sm .txt { font-size: 10.5px; }

/* Das Sandband, zweiter Einsatzort. Dunkler Text darauf, nicht der
   weisse des Shops: dort steht kurzer Auszeichnungstext darin, hier
   ganze Hinweissaetze -- Weiss auf #DDD0C0 kommt auf 1,5:1 und ist als
   Fliesstext nicht lesbar. Mit --ink-2 sind es 7,3:1. */
.note {
  font-size: 12px;
  color: var(--ink-2);
  background: var(--sand);
  border-radius: var(--r);
  padding: 9px 11px;
}
:root[data-theme="dark"] .note { background: var(--surface-2); color: var(--ink-2); }
@media (prefers-color-scheme: dark) { .note { background: var(--surface-2); color: var(--ink-2); } }
:root[data-theme="light"] .note { background: var(--sand); color: var(--ink-2); }

.back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 0;
  background: none;
  color: var(--ink-3);
  font: inherit;
  font-size: 13px;
  padding: 4px 0;
  margin-bottom: 6px;
  cursor: pointer;
}
.back:hover { color: var(--ink); }

.sectiontitle {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 18px 0 8px;
}
.sectiontitle .rule { flex: 1; height: 1px; background: var(--line); }

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

@media (min-width: 900px) {
  .app { grid-template-columns: var(--rail) 1fr; }
  .rail {
    display: block;
    border-right: 1px solid var(--line);
    background: var(--surface);
    position: sticky;
    top: 0;
    height: 100vh;
    overflow-y: auto;
  }
  .topbar { display: none; }
  .tabbar { display: none; }
  .main { padding: 26px 28px 40px; }
  .kpis { grid-template-columns: repeat(4, 1fr); }
  h1 { font-size: 25px; }
  .split { display: grid; grid-template-columns: 1.35fr 1fr; gap: 18px; align-items: start; }
  .split-even { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; align-items: start; }
  /* Die Bildspalte im Artikel-Modul ist auf 280px gedeckelt (siehe .gal) --
     ohne eigene Spaltenbreite bliebe in der 1.35fr-Spur ein Leerraum vor der
     Beschreibung, der wie ein Layoutfehler wirkt. */
  #basisdaten .split { grid-template-columns: minmax(0, 280px) 1fr; }
}

@media (max-width: 899px) {
  .split, .split-even { display: flex; flex-direction: column; gap: 12px; }
  .grid-3 { grid-template-columns: 1fr 1fr; }
  /* Maßfelder bleiben nebeneinander — sie gehören zusammen gelesen */
  .grid-3.dims { grid-template-columns: 1fr 1fr 0.7fr; gap: 8px; }
  .grid-3.dims label { font-size: 11px; }
  .grid-3.dims input { padding-left: 8px; padding-right: 6px; }
}

@media print {
  .rail, .tabbar, .topbar, .no-print { display: none !important; }
  body { background: #fff; color: #000; }
  .main { padding: 0; }
}

/* ---------- Ergaenzungen der Anwendung ----------
   Im Prototyp waren Schaltflaechen und Listenzeilen <button>. In der
   Anwendung fuehren viele davon auf eine andere Seite und sind deshalb
   <a> -- diese Regeln geben ihnen dasselbe Aussehen. */

a.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}

/* Links in Tabellen und Karten tragen ihre Bedeutung aus dem Kontext; eine
   Unterstreichung je Zelle machte die Tabelle unruhig. Beim Ueberfahren
   erscheint sie wieder. */
table a, .card-body a { text-decoration: none; }
table a:hover, .card-body a:hover { text-decoration: underline; }


/* ---------- Arbeitsflaeche: Bloecke aus Karten ----------
   Die Bauteilliste war einmal eine Tabelle mit zehn Spalten. Sie war am
   Schreibtisch knapp lesbar und auf dem Handy unbedienbar. Karten kosten
   mehr vertikalen Platz -- der bewusst bezahlte Preis dafuer, dass dieselbe
   Maske in der Werkstatt am Telefon funktioniert. */

.zeilen { display: flex; flex-direction: column; gap: 8px; }

.zeile {
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
  padding: 10px 11px;
}
.zeile.leer { border-style: dashed; background: transparent; }

.zeile-kopf { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
/* Menge und Betrag stehen im Kopf jeder Zeile, nicht erst in den
   aufgeklappten Feldern -- die Kernzahl soll man ohne Klick sehen. */
.zeile-kopf .zeile-menge,
.zeile-kopf .zeile-betrag {
  font-size: 12.5px;
  color: var(--ink-2);
  white-space: nowrap;
}
.zeile-kopf .zeile-betrag { font-weight: 600; color: var(--ink); }
.zeile-kopf .name { font-size: 14px; font-weight: 600; }
.zeile-kopf .preis {
  font-family: var(--data);
  font-size: 11.5px;
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.zeile-kopf .werkzeug { margin-left: auto; display: flex; gap: 2px; }

/* Pfeile und Kreuz. Klein im Bild, aber 32 px Trefferflaeche -- sie werden
   mit dem Daumen bedient. */
.zeile-kopf .werkzeug button {
  font: inherit;
  font-size: 13px;
  line-height: 1;
  width: 32px;
  min-height: 32px;
  border: 1px solid transparent;
  border-radius: var(--r);
  background: none;
  color: var(--ink-3);
  cursor: pointer;
}
.zeile-kopf .werkzeug button:hover { background: var(--surface-2); color: var(--ink); }
.zeile-kopf .werkzeug button.weg:hover { color: var(--crit); }

.zeile-felder {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 9px;
}
.zeile-felder .feldchen { display: flex; flex-direction: column; gap: 3px; flex: 0 0 auto; }
.zeile-felder .feldchen.grow { flex: 1 1 150px; }
.zeile-felder .feldchen > label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.zeile-felder input,
.zeile-felder select { min-height: 36px; padding: 6px 8px; font-size: 13.5px; }
.zeile-felder input.zahl { width: 74px; text-align: right; }
.zeile-felder input.mass { width: 78px; text-align: right; }
/* Menge und Betrag einer Zeile -- vom Server gerechnet, vom Skript nur
   platziert (siehe app/static/bedarf.js). Kein Eingabefeld, deshalb ohne
   Rahmen, aber gleiche Zeilenhoehe wie die Felder daneben. */
.zeile-felder .feldchen.ergebnis span {
  display: block;
  min-height: 36px;
  padding: 6px 0;
  font-size: 13.5px;
  text-align: right;
  color: var(--ink-2);
  white-space: nowrap;
}

/* Materialbedarf: Material, Verpackung und Porto nebeneinander -- aber nur,
   solange jede Spalte 290 px bekommt. Darunter bricht das Raster von selbst
   auf zwei Spalten und dann auf eine, statt drei Listen in je 110 px zu
   quetschen: `auto-fit` mit `minmax` entscheidet das an der tatsaechlichen
   Breite und nicht an einer geratenen Fensterbreite.

   Senkrechte Trennlinien gibt es nicht mehr. Sie muessten beim Umbruch
   wandern, und sie sind ueberfluessig: die kraeftige Linie unter jeder
   Ueberschrift grenzt die Spalten schon ab. */
.dreier {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  gap: 18px 26px;
  padding: 14px;
}
/* Im Bearbeiten-Modus steht das Raster in einer .card-body, die ihr Polster
   schon mitbringt -- sonst zaehlte es doppelt. */
.card-body > .dreier { padding: 0; }
.bedarf-spalte { min-width: 0; }

/* Der Spaltenkopf sitzt auf einer kraeftigen Linie und traegt die Zahl der
   Positionen. Die Kosten stehen bewusst nicht hier, sondern in der
   Kalkulation -- siehe den Kommentar am Makro `spaltenkopf()`. */
.spaltenkopf {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding-bottom: 7px;
  margin-bottom: 4px;
  border-bottom: 1.5px solid var(--line-strong);
}
.spaltenkopf h3 {
  flex: 1;
  min-width: 0;
  font-size: 11.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.spaltenkopf .anzahl { font-size: 11.5px; color: var(--ink-3); white-space: nowrap; }
.spaltenkopf .summe { font-size: 13px; font-weight: 600; white-space: nowrap; }

/* Lesesicht: Teilname links, darunter Mass, Werkstoff und Bemerkung; Menge
   und Betrag rechtsbuendig mit fester Mindestbreite, damit die Ziffern
   untereinander stehen. Vorher lief beides als eine Kette („0,0030 m³ ·
   5,70 €") und liess sich nicht ueberfliegen. */
.anzeigezeile { padding: 7px 0; border-bottom: 1px solid var(--line); }
.anzeigezeile:last-of-type { border-bottom: 0; }
.anzeigezeile .kopf { display: flex; align-items: baseline; gap: 10px; }
.anzeigezeile .teil {
  flex: 1;
  min-width: 0;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink);
}
/* `flex-shrink: 0`: die beiden Zahlenspalten geben nicht nach. Ohne das
   laufen sie in schmalen Spalten in den Teilnamen hinein. */
.anzeigezeile .menge,
.anzeigezeile .betrag {
  flex: 0 0 auto;
  text-align: right;
  white-space: nowrap;
}
.anzeigezeile .menge { min-width: 72px; font-size: 12px; color: var(--ink-3); }
.anzeigezeile .betrag { min-width: 56px; font-size: 12.5px; color: var(--ink); }
.anzeigezeile .unter {
  margin-top: 2px;
  font-size: 11.5px;
  color: var(--ink-3);
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
.anzeigezeile .bemerkung {
  margin-top: 1px;
  font-size: 11.5px;
  font-style: italic;
  color: var(--ink-3);
}

/* Aufgeschluesseltes Set, zugeklappt. Der Inhalt ist beim Erfassen selten
   die Frage und beim Nachschlagen manchmal die einzige -- die Teilezahl in
   der Zusammenfassung sagt, dass es sich zu oeffnen lohnt. */
.set-teile { margin-top: 9px; }
.set-teile > summary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 0;
  font-size: 11.5px;
  color: var(--ink-3);
  cursor: pointer;
  list-style: none;
}
.set-teile > summary::-webkit-details-marker { display: none; }
.set-teile > summary::after { content: "▸"; font-size: 10px; }
.set-teile[open] > summary::after { content: "▾"; }
.set-teile > summary:hover { color: var(--ink); }
.set-teile .teile {
  margin-top: 4px;
  padding: 6px 0 1px 11px;
  border-left: 2px solid var(--accent);
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.set-teile .teil {
  display: flex;
  gap: 8px;
  font-size: 12px;
  color: var(--ink-3);
}
.set-teile .teil .grow { flex: 1; min-width: 0; }
.set-teile .teil .zahl { font-family: var(--data); font-variant-numeric: tabular-nums; }

/* Suchfeld je Block. Das Aufklappen ist die Aufwertung; ohne Skript bleibt
   darunter ein gewoehnliches <select> mit Knopf stehen. */
.zufuegen { position: relative; margin-top: 10px; }
.zufuegen .treffer {
  position: absolute;
  z-index: 20;
  left: 0;
  right: 0;
  top: calc(100% + 3px);
  max-height: 280px;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14);
}
.zufuegen .treffer button {
  display: flex;
  align-items: baseline;
  gap: 8px;
  width: 100%;
  text-align: left;
  font: inherit;
  font-size: 13.5px;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: none;
  color: var(--ink);
  padding: 9px 11px;
  min-height: 40px;
  cursor: pointer;
}
.zufuegen .treffer button:last-child { border-bottom: 0; }
.zufuegen .treffer button:hover,
.zufuegen .treffer button.aktiv { background: var(--surface-2); }
.zufuegen .treffer .preis {
  margin-left: auto;
  font-family: var(--data);
  font-size: 11.5px;
  color: var(--ink-3);
  white-space: nowrap;
}
.zufuegen .treffer .leer { padding: 11px; font-size: 13px; color: var(--ink-3); }

.blockleer { font-size: 13px; color: var(--ink-3); padding: 2px 0 4px; }

/* Chronik: eine Notiz je Zeile, Datum als Marke davor */
.chronik { display: flex; flex-direction: column; gap: 0; }
.chronik .eintrag {
  display: flex;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}
.chronik .eintrag:last-child { border-bottom: 0; }
.chronik .eintrag time {
  flex: 0 0 84px;
  font-family: var(--data);
  font-size: 12px;
  color: var(--ink-3);
  padding-top: 1px;
}
.chronik .eintrag .text { flex: 1; min-width: 0; white-space: pre-wrap; font-size: 14px; }

/* Anleitung: nummerierte Schritte */
.schritte { display: flex; flex-direction: column; gap: 8px; }
.schritt { display: flex; gap: 10px; }
.schritt .nr {
  flex: 0 0 26px;
  height: 26px;
  border-radius: 100px;
  background: var(--surface-2);
  color: var(--ink-2);
  font-family: var(--data);
  font-size: 12px;
  display: grid;
  place-items: center;
  margin-top: 4px;
}
.schritt .inhalt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.schritt .inhalt .titel { font-weight: 600; font-size: 14px; }
/* Titelfeld und Werkzeuge in einer Zeile: Eingabefelder sind hier sonst
   `width: 100%` und draengen die drei Knoepfe in eine eigene Zeile. */
.schritt .zeile-kopf { align-items: center; flex-wrap: nowrap; }
.schritt .zeile-kopf > input { flex: 1 1 auto; width: auto; min-width: 0; }
.schritt .zeile-kopf .werkzeug { flex: 0 0 auto; }
.schritt .inhalt .text { white-space: pre-wrap; font-size: 13.5px; color: var(--ink-2); }

@media (max-width: 560px) {
  /* Pfeile und Kreuz bleiben neben dem Namen; der Einheitspreis rutscht in
     die zweite Zeile. Sonst nehmen die drei Knoepfe eine ganze Zeile ein,
     bevor die Karte ueberhaupt etwas gesagt hat. */
  .zeile-kopf .name { flex: 1 1 auto; }
  .zeile-kopf .werkzeug { order: 2; }
  .zeile-kopf .preis { order: 3; flex: 1 1 100%; }
  .zeile-felder .feldchen { flex: 1 1 100%; }
  .zeile-felder input.zahl,
  .zeile-felder input.mass { width: 100%; }
  .chronik .eintrag { flex-direction: column; gap: 3px; }
  .chronik .eintrag time { flex: none; }
}

/* Bild-Overlay: Klick auf ein Artikelbild oeffnet die Grossansicht auf der
   Seite statt eines neuen Tabs. */
.bild-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(0, 0, 0, .82);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 32px;
}
.bild-overlay[hidden] { display: none; }
.bild-overlay img {
  max-width: 100%;
  max-height: 100%;
  border-radius: var(--r);
  box-shadow: 0 10px 40px rgba(0, 0, 0, .5);
}
.bild-overlay .schliessen {
  position: absolute;
  top: 18px;
  right: 22px;
  width: 36px;
  height: 36px;
  border-radius: 100px;
  border: 1px solid rgba(255, 255, 255, .3);
  background: rgba(255, 255, 255, .1);
  color: #fff;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.bild-overlay .schliessen:hover { background: rgba(255, 255, 255, .2); }

/* Formular-Overlay: fuer kurze Formulare (z.B. Variante anlegen/bearbeiten),
   die einen eigenen Fokus verdienen statt zwischen anderen Karten zu stehen.
   Serverseitig gerendert, wenn der bearbeiten-Parameter aktiv ist -- kein
   Sichtbarkeits-Toggle per JS noetig, Schliessen ist eine normale Navigation
   ohne diesen Parameter (siehe data-overlay-schliessen-href). */
.form-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(0, 0, 0, .5);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.form-overlay .modal-panel {
  background: var(--surface);
  border-radius: var(--r);
  box-shadow: 0 10px 40px rgba(0, 0, 0, .35);
  width: min(480px, 100%);
  max-height: calc(100vh - 48px);
  overflow-y: auto;
}

/* Ein-/Ausblenden-Schalter -- ein Link, kein Formular: der Zustand steckt in
   der URL (?ohne_arbeitskosten=1), nicht in JS-Status, damit ein Reload oder
   ein geteilter Link denselben Blick zeigt. */
.switch { display: inline-flex; align-items: center; text-decoration: none; }
.switch .track {
  display: block;
  width: 34px;
  height: 20px;
  border-radius: 10px;
  background: var(--line-strong);
  position: relative;
  transition: background .15s ease;
}
.switch .thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .3);
  transition: left .15s ease;
}
.switch.on .track { background: var(--brand); }
.switch.on .thumb { left: 16px; }

/* ---------- Kreuzchen und Radios ----------
   `input { width: 100% }` weiter oben gilt fuer Text-, Zahlen- und
   Auswahlfelder und ist dort richtig. Ein Kreuzchen dehnt sich dadurch aber
   ueber die ganze Zeile und drueckt den Text daneben auf Nullbreite. Die
   aelteren Masken umgehen das mit `style="width:auto;min-height:0"` an jedem
   einzelnen Feld -- hier steht die Regel einmal, damit das nicht jede neue
   Maske neu lernen muss. */
input[type="checkbox"],
input[type="radio"] {
  width: auto;
  min-height: 0;
  flex-shrink: 0;
  accent-color: var(--accent);
}

/* ---------- GPSR ----------
   Lesen zuerst: ein Baustein ist eine Zeile, kein dauerhaft offenes
   Formular. Bearbeitet wird immer nur einer -- dasselbe Prinzip wie bei den
   Bloecken der Artikelseite. */

/* Eine Bausteinzeile in der Lesesicht. */
.gpsr-zeile {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 9px 14px;
  border-top: 1px solid var(--line);
  border-left: 3px solid transparent;
}
.gpsr-zeile:first-child { border-top: 0; }
.gpsr-zeile:hover { background: var(--surface-2); }
.gpsr-zeile.offen { background: var(--surface-2); border-left-color: var(--accent); }
.gpsr-zeile.still { opacity: .55; }
.gpsr-zeile .text { flex: 1; min-width: 0; font-size: 14px; line-height: 1.45; }
.gpsr-zeile .marken { display: flex; gap: 4px; flex-wrap: wrap; flex-shrink: 0; }
.gpsr-zeile .werkzeug {
  display: flex;
  gap: 4px;
  flex-shrink: 0;
  opacity: 0;
  transition: opacity .12s ease;
}
.gpsr-zeile:hover .werkzeug,
.gpsr-zeile:focus-within .werkzeug { opacity: 1; }
@media (hover: none) { .gpsr-zeile .werkzeug { opacity: 1; } }

/* Der kleine Griff fuer Sortieren und Bearbeiten. */
.griff {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 26px;
  padding: 0 6px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--ink-2);
  font: inherit;
  font-size: 12px;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
}
.griff:hover { background: var(--surface-2); color: var(--ink); }

/* Die Zaehlerzeile unter einem Textfeld: Zeichen und „passt auf". */
.gpsr-zaehler {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11.5px;
  color: var(--ink-3);
}
.gpsr-zaehler .balken {
  flex: 1;
  max-width: 160px;
  height: 3px;
  border-radius: 2px;
  background: var(--line);
  overflow: hidden;
}
.gpsr-zaehler .balken i { display: block; height: 100%; background: var(--accent); }
.gpsr-zaehler.eng .balken i { background: var(--warn); }
.gpsr-zaehler.eng { color: var(--warn); }

/* Auswaehlbare Marke: dasselbe Chip, aber als Schalter. */
.marke-schalter { cursor: pointer; user-select: none; }
.marke-schalter input { position: absolute; opacity: 0; pointer-events: none; }
.marke-schalter.on,
.marke-schalter:has(input:checked) {
  background: var(--accent-soft);
  border-color: transparent;
  color: var(--accent-ink);
}

/* Die Bausteinauswahl im Blatt: Kreuzchen, Text, Marken in einer Zeile. */
.wahlzeile {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 8px 14px 8px 11px;
  border-top: 1px solid var(--line);
  border-left: 3px solid transparent;
  cursor: pointer;
  font-size: 13.5px;
  line-height: 1.45;
}
.wahlzeile:first-child { border-top: 0; }
.wahlzeile:hover { background: var(--surface-2); }
.wahlzeile input { margin-top: 3px; }
/* Gewaehlt wird durch einen Balken angezeigt, nicht durch eine gefaerbte
   Flaeche: bei neun von dreizehn angekreuzten Bausteinen waere die halbe
   Seite gruen und die Farbe saegte keine Information mehr aus. Der Balken
   laesst sich am Rand entlang scannen -- „was steckt in diesem Blatt" -- und
   laesst den Text ruhig. */
.wahlzeile.gewaehlt { border-left-color: var(--accent); }
.wahlzeile.gewaehlt .text { color: var(--ink); font-weight: 500; }
.wahlzeile:not(.gewaehlt) .text { color: var(--ink-2); }
.wahlzeile .text { flex: 1; min-width: 0; }

/* Vorschauspalte: bleibt beim Scrollen stehen -- man klickt links und sieht
   rechts, was dabei herauskommt. Ohne `sticky` waere die Vorschau bei der
   vierten Kategorie aus dem Bild. */
@media (min-width: 900px) {
  .vorschau { position: sticky; top: 14px; }
}
.vorschau object {
  width: 100%;
  display: block;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface);
}

/* Das Etikett in der Lesesicht des Artikels: zwei Spalten wie auf dem
   Aufkleber, damit die Vorschau im Kopf schon stimmt. */
.gpsr-karte {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 22px;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--surface-2);
}
.gpsr-karte h3 {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .02em;
  margin: 0 0 3px;
}
.gpsr-karte ul { margin: 0 0 8px; padding-left: 15px; }
.gpsr-karte li { font-size: 12.5px; line-height: 1.5; color: var(--ink-2); }
@media (max-width: 620px) { .gpsr-karte { grid-template-columns: 1fr; } }

/* Der Arbeitsschirm eines Hinweisblattes: links klicken, rechts zusehen.
   Eine eigene Spaltenaufteilung statt der Standard-1.35fr/1fr, weil die
   Vorschau ein Etikett im Querformat zeigt -- in einer 300-px-Spalte waere
   davon fast nur der Rahmen des PDF-Betrachters zu sehen. */
@media (min-width: 900px) {
  .split.gpsr-arbeit { grid-template-columns: minmax(0, 1fr) minmax(340px, 440px); }
}
.vorschau iframe {
  width: 100%;
  display: block;
  border: 1px solid var(--line);
  border-radius: 14px;
  background: var(--surface);
}
.vorschau-fuss {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 8px;
}
.vorschau-fuss .hint { flex: 1; min-width: 0; font-size: 11.5px; line-height: 1.4; }
.vorschau-fuss .btn { flex-shrink: 0; }

/* Anlegezeile eines Hinweisblattes: zwei Felder und die Knoepfe in einer
   Reihe, unten buendig -- die Beschriftungen sitzen ueber den Feldern. */
.blatt-neu {
  display: flex;
  align-items: flex-end;
  gap: 12px;
  padding: 14px;
  margin-bottom: 12px;
}
.blatt-neu .field { min-width: 0; }
.blatt-neu .field.grow { flex: 1; }
@media (max-width: 620px) {
  .blatt-neu { flex-direction: column; align-items: stretch; }
}

/* ---------- Statistik-Kurzfassung im Kopf der Bestellliste ----------
   Der Rest (Verlauf, Ranglisten) lebt in /statistik -- hier nur noch die
   drei Zahlen, wegen derer man frueher aufgeklappt haette. */
.statistik-kurz { margin: 0 0 14px; }
.statistik-kurz .kurz {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 12.5px;
  color: var(--ink-3);
}
.statistik-kurz .kurz b { color: var(--ink); font-weight: 600; }


/* Rangliste (Artikel, Laender) -- dieselbe Bauart wie die Kostentreiber im
   Kalkulationsmodul, damit ein Balken ueberall dasselbe bedeutet. */
.rangliste { display: flex; flex-direction: column; gap: 10px; }
.rangliste .rang .name { font-size: 13.5px; font-weight: 600; }
.rangliste .rang .balken { max-width: none; margin-top: 5px; }
.rangliste .rang .sub {
  display: flex;
  align-items: center;
  gap: 7px;
  flex-wrap: wrap;
  font-size: 11.5px;
  color: var(--ink-3);
  margin-top: 5px;
}

/* ---------- Statistik-Modul (/statistik) ----------
   Eigene Regeln nur fuer das, was der Kopf-Block der Bestellliste nicht
   braucht: Kostenstruktur-Wasserfall, Vollsicht-Tabelle mit Client-Sort,
   Verkaufs-Heatmap. Diagramme (ECharts) tragen ihre eigenen Farben aus den
   CSS-Variablen zur Laufzeit, hier steht nur, was reines CSS ist. */

.wasserfall-zeile {
  display: grid;
  grid-template-columns: 120px 1fr 84px;
  align-items: center;
  gap: 10px;
  font-size: 12.5px;
  padding: 4px 0;
}
.wasserfall-zeile .balken { margin: 0; max-width: none; }
.wasserfall-zeile .balken span.crit { background: var(--crit); }
.wasserfall-zeile .wert { text-align: right; font-family: var(--data); }
.wasserfall-zeile.summe { border-top: 1px solid var(--line-strong); margin-top: 4px; padding-top: 8px; font-weight: 600; }

.zerlegung-liste { font-size: 12.5px; }
.zerlegung-liste .row-between { padding: 3px 0; }
.zerlegung-liste .row-between.summe { border-top: 1px solid var(--line-strong); margin-top: 4px; padding-top: 8px; font-weight: 600; }

table.sortierbar th[data-sort] { cursor: pointer; user-select: none; white-space: nowrap; }
table.sortierbar th[data-sort]:hover { color: var(--ink); }
table.sortierbar th[data-sort] .pfeil { color: var(--ink-3); font-size: 10px; margin-left: 3px; }
table.sortierbar th[data-sort].aktiv .pfeil { color: var(--accent-ink); }

/* Heatmap: CSS-Grid statt Canvas -- die Zelle ist ein echter Link auf
   Zeitraum plus Artikelfilter, kein Overlay darueber. */
.heatmap-scroll { overflow-x: auto; }
.heatmap-grid { display: grid; font-size: 11px; }
.heatmap-grid .kopf, .heatmap-grid .zeilenkopf {
  color: var(--ink-3);
  font-size: 10.5px;
  padding: 3px 4px;
  white-space: nowrap;
}
.heatmap-grid .zeilenkopf {
  position: sticky;
  left: 0;
  background: var(--surface);
  text-align: left;
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.heatmap-grid .zelle {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 30px;
  height: 26px;
  border-radius: 2px;
  color: var(--ink);
  text-decoration: none;
  font-family: var(--data);
}
.heatmap-grid .zelle:hover { outline: 1px solid var(--accent-ink); }
.heatmap-grid .zelle.leer { color: var(--ink-3); }

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--accent-soft);
  color: var(--accent-ink);
  border-radius: 999px;
  padding: 5px 6px 5px 12px;
  font-size: 12.5px;
  text-decoration: none;
  margin-bottom: 10px;
}
.filter-chip .x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.08);
}

/* Statistik: Jahresvergleich ------------------------------------------- */
.jahres-tabelle { width: 100%; border-collapse: collapse; font-size: 13px; }
.jahres-tabelle th, .jahres-tabelle td { padding: 7px 10px; border-bottom: 1px solid var(--line); }
.jahres-tabelle th { text-align: left; font-size: 11.5px; color: var(--ink-3); font-weight: 600; }
.jahres-tabelle th.num, .jahres-tabelle td.num { text-align: right; }
.jahres-tabelle tbody tr { cursor: pointer; }
.jahres-tabelle tbody tr:hover { background: var(--surface-2); }
.jahres-tabelle td a { color: inherit; text-decoration: none; font-weight: 600; }
/* Schattenzeile: derselbe Ausschnitt des Vorjahres, den das laufende Jahr
   bisher abdeckt. Optisch zurueckgenommen, damit sie als Bezugsgroesse und
   nicht als eigenes Jahr gelesen wird. */
.jahres-tabelle tr.ist-schatten td { color: var(--ink-3); border-bottom-style: dashed; }
.jahres-tabelle tr.ist-schatten td a { font-weight: 400; }
.jahres-tabelle .schatten-pfeil { color: var(--line-strong); margin-right: 4px; }

/* Statistik: Artikelkacheln -------------------------------------------- */
.art-kacheln { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }
.art-kachel {
  display: block; background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); padding: 11px 13px 12px; text-decoration: none; color: inherit;
  transition: border-color .12s, transform .06s;
}
.art-kachel:hover { border-color: var(--accent); }
.art-kachel:active { transform: scale(.995); }
.art-kachel.ist-verlust { border-left: 3px solid var(--crit); }
.ak-kopf { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.ak-name { font-size: 12.5px; font-weight: 600; line-height: 1.3; }
.ak-wert {
  font-family: var(--data); font-size: 20px; font-weight: 700;
  font-variant-numeric: tabular-nums; margin-top: 8px; line-height: 1.1;
}
.ak-einheit { font-size: 12px; font-weight: 400; color: var(--ink-3); }
.ak-anteil { display: flex; align-items: center; gap: 7px; margin-top: 8px; }
.ak-anteil > span {
  font-family: var(--data); font-size: 10.5px; color: var(--ink-3);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.ak-bar { height: 4px; border-radius: 3px; background: var(--surface-2); overflow: hidden; flex: 1; }
.ak-bar > i { display: block; height: 100%; background: var(--accent); border-radius: 3px; }
.art-kachel.ist-verlust .ak-bar > i { background: var(--crit); }
.ak-zeilen { margin-top: 10px; display: flex; flex-direction: column; gap: 3px; }
.ak-zeilen > div { display: flex; justify-content: space-between; gap: 8px; font-size: 11.5px; color: var(--ink-3); }
.ak-zeilen b {
  font-family: var(--data); color: var(--ink); font-weight: 700;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.ak-zeilen b.is-crit { color: var(--crit); }
#sortiment-tabelle tr.ist-verlust td { background: var(--crit-soft); }
#sortiment-tabelle td a { color: inherit; text-decoration: none; }
#sortiment-tabelle td a:hover { text-decoration: underline; }


/* ============================================================
   Uebersicht (Startseite)
   ============================================================
   Eigene Kachelbauart statt .kpi: die dortige Kachel traegt eine Zahl und
   ein Wort, hier gehoert zu jeder Zahl ihr Bezug -- "255 €" sagt nichts,
   "255 €, 8 % unter dem Vormonat zum gleichen Tag" sagt alles. Deshalb drei
   Zeilen (Label, Wert, Zusatz) und eine Farbstufe, die auch den ruhigen
   Fall kennt. */

.ue-kpis {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  margin-bottom: 10px;
}
.ue-kpis-geld { margin-bottom: 16px; }

.ue-kpi {
  display: block;
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 3px solid var(--line-strong);
  border-radius: var(--r);
  padding: 11px 13px 12px;
  color: inherit;
  text-decoration: none;
  min-width: 0;
}
a.ue-kpi { transition: border-color 110ms ease, background 110ms ease; }
a.ue-kpi:hover { background: var(--surface-2); border-left-color: var(--accent); }

.ue-kpi .l {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.ue-kpi .n {
  display: flex;
  align-items: baseline;
  gap: 7px;
  flex-wrap: wrap;
  font-family: var(--data);
  font-size: 25px;
  font-weight: 700;
  line-height: 1.1;
  margin-top: 7px;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.ue-kpi .s {
  display: block;
  font-size: 11.5px;
  line-height: 1.35;
  color: var(--ink-3);
  margin-top: 6px;
  /* Zwei Zeilen Bezug sind das Maximum: darueber wird aus der Kachel ein
     Absatz, und die Wand verliert ihren Zweck, auf einen Blick lesbar zu
     sein. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Die Nullkachel ist die gute Nachricht -- sie darf nicht schreien, aber
   auch nicht fehlen: sie beweist, dass hingesehen wurde. */
.ue-kpi.is-ruhig { border-left-color: var(--line); }
.ue-kpi.is-ruhig .n { color: var(--ink-3); font-weight: 600; }
.ue-kpi.is-neutral { border-left-color: var(--line-strong); }
.ue-kpi.is-warn { border-left-color: var(--warn); }
.ue-kpi.is-warn .n { color: var(--warn); }
.ue-kpi.is-crit { border-left-color: var(--crit); }
.ue-kpi.is-crit .n { color: var(--crit); }

.ue-trend {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0;
  white-space: nowrap;
}
.ue-trend.up { color: var(--accent-ink); }
.ue-trend.down { color: var(--crit); }

/* Abschnittsueberschrift ueber einer Karte -- kleiner als h1, deutlicher
   als eine Kartenzeile. */
.ue-abschnitt {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
}
.ue-abschnitt h2 { margin: 0; font-size: 14.5px; flex: 1; }

.ue-alarm { border-left: 3px solid var(--crit); margin-bottom: 16px; }
.ue-arbeit { margin-bottom: 16px; }

/* Tages-/Rubrikkopf INNERHALB der Liste: eine Karte je Tag waere ein Stapel
   Karten, und ein Stapel liest sich nicht als Liste. */
.ue-tagkopf {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 7px 14px;
  background: var(--surface-2);
  border-top: 1px solid var(--line);
  border-left: 3px solid transparent;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.ue-tagkopf:first-child { border-top: 0; }
.ue-tagkopf .mono { font-size: 12px; color: var(--ink-3); }
.ue-tagkopf.is-crit { border-left-color: var(--crit); color: var(--crit); }
.ue-tagkopf.is-warn { border-left-color: var(--warn); color: var(--warn); }
.ue-tagkopf.is-ok { border-left-color: var(--accent); }

.ue-hinweis {
  padding: 8px 14px 0;
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--ink-3);
}
.ue-entwarnung { color: var(--ink-3); font-size: 13px; padding: 16px 14px; }
.ue-fuss {
  padding: 9px 14px;
  border-top: 1px solid var(--line);
  font-size: 12px;
  color: var(--ink-3);
}
.ue-sync { margin-top: 10px; font-size: 12.5px; }
.ue-sync .card-body { padding: 10px 14px; }
.ue-sync .mono { color: var(--ink-3); }
.ue-sync.is-warn { border-left: 3px solid var(--warn); }
.ue-sync.is-crit { border-left: 3px solid var(--crit); }
.ue-sync.is-warn span, .ue-sync.is-crit span { color: var(--crit); }
.ue-streifen { margin-top: 10px; font-size: 12.5px; }
.ue-streifen .card-body { padding: 10px 14px; }

/* --- Monatsverlauf: kumuliert gegen den Vormonat, serverseitiges SVG ---
   Tagesbalken eines angefangenen Monats sagen nichts (jeder zweite Tag ist
   null); die kumulierte Kurve beantwortet "liege ich vor oder hinter dem
   Vormonat". ECharts bleibt auf /statistik -- das hier braucht kein
   JavaScript und zieht im Dunkelmodus ueber dieselben Variablen mit. */
.ue-lage { margin-top: 4px; }
.ue-kurve svg { width: 100%; height: auto; display: block; overflow: visible; }
.ue-kurve .flaeche { fill: var(--accent-soft); stroke: none; }
.ue-kurve .linie {
  fill: none;
  stroke: var(--accent);
  stroke-width: 2.5;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.ue-kurve .linie-vor {
  fill: none;
  stroke: var(--line-strong);
  stroke-width: 1.5;
  stroke-dasharray: 5 4;
  stroke-linejoin: round;
}
.ue-legende { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--ink-3); }
.ue-legende i { width: 14px; height: 0; border-top: 2px solid var(--accent); display: inline-block; }
.ue-legende i.s-vor { border-top: 2px dashed var(--line-strong); margin-left: 6px; }

.ue-kurve .heute-linie { stroke: var(--warn); stroke-width: 1; stroke-dasharray: 3 3; opacity: 0.75; }
.ue-kurve .punkt-jetzt { fill: var(--accent); stroke: var(--surface); stroke-width: 2; }
.ue-kurve .punkt-vor { fill: var(--line-strong); stroke: var(--surface); stroke-width: 2; }
.ue-legende i.s-heute {
  border-top: 0;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--warn);
  margin-left: 6px;
}

.ue-kurve-fuss {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px 14px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}
.ue-kurve-fuss .l { display: block; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-3); font-weight: 700; }
.ue-kurve-fuss .n { display: block; font-size: 15px; font-weight: 700; margin-top: 3px; font-variant-numeric: tabular-nums; }

.ue-monate { display: flex; align-items: flex-end; gap: 6px; height: 150px; }
.ue-monat {
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  gap: 4px;
}
.ue-monat .wert { font-size: 10px; color: var(--ink-3); white-space: nowrap; }
.ue-monat .saeule {
  width: 100%;
  min-height: 2px;
  background: var(--sand);
  border-radius: var(--r) var(--r) 0 0;
}
.ue-monat .m { font-family: var(--data); font-size: 10.5px; color: var(--ink-3); }
/* Der laufende Monat ist angeschnitten und darf nicht wie ein
   vollstaendiger neben den anderen stehen. */
.ue-monat.is-laufend .saeule { background: repeating-linear-gradient(135deg, var(--accent) 0 4px, var(--accent-soft) 4px 8px); }
.ue-monat.is-laufend .m { color: var(--ink); font-weight: 700; }

@media (min-width: 900px) {
  .ue-kpis { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .ue-kurve-fuss { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* Auf dem Handy stehen acht Kacheln in vier Reihen -- kompakter Satz, damit
   die Wand knapp eine Bildschirmhoehe bleibt und der Ueberfaellig-Alarm
   direkt darunter beginnt. Die erste Kachel IST bereits der Alarm, insofern
   steht die wichtigste Aussage weiterhin ganz oben. */
@media (max-width: 520px) {
  .ue-kpis { gap: 8px; margin-bottom: 8px; }
  .ue-kpi { padding: 9px 11px 10px; }
  .ue-kpi .l { font-size: 10px; letter-spacing: 0.04em; }
  .ue-kpi .n { font-size: 20px; margin-top: 4px; }
  .ue-kpi .s { font-size: 11px; margin-top: 3px; }
  .ue-monat .wert { display: none; }
  .ue-monate { height: 110px; }
}

/* Umschalter im Kopf des Monatsverlaufs: auf schmalen Schirmen unter die
   Ueberschrift, statt Titel und Segmented Control ineinanderzuquetschen. */
.ue-verlaufkopf { flex-wrap: wrap; }
.ue-verlaufkopf .ue-vergleich-seg { font-size: 12.5px; }
.ue-legende { margin-bottom: 10px; }
@media (max-width: 480px) {
  .ue-verlaufkopf .grow { flex-basis: 100%; }
}

/* ============================================================
   Jahresrueckblick (/jahresrueckblick)
   ------------------------------------------------------------
   Bewusst wenige eigene Klassen: Karte, Tabelle, Chip und
   Segmented Control kommen aus dem Bestand, neu ist nur, was es
   sonst nirgends gibt -- die grosse Zahl mit ihren zwei
   Bezugszeilen, der Schweigegrund und die Monatsmatrix.

   Alle Farben ueber die Variablen dieses Stylesheets: damit zieht
   der Dunkelmodus mit, und derselbe Block steht identisch in allen
   drei Designs (siehe Enum `Design` -- gleiche Klassennamen, eigene
   Datei je Design).
   ============================================================ */

.jr-kapitel { margin-top: 14px; }
.jr-kapitel .card-head h2 { margin: 0; font-size: 14.5px; }

/* Die grossen Zahlen. Auto-fit statt fester Spaltenzahl: dieselbe
   Wand traegt mal drei, mal vier Kacheln (Ouvertuere gegen Kunden). */
.jr-zahlen {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 12px;
}
.jr-zahl {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 12px 14px 13px;
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.jr-zahl .l {
  font-size: 11.5px;
  color: var(--ink-3);
  letter-spacing: 0.03em;
  text-transform: uppercase;
}
.jr-zahl .n {
  font-family: var(--data);
  font-size: 26px;
  font-weight: 600;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
  margin: 2px 0 4px;
}

/* Die Bezugszeile: klein, farbig nur in der Richtung. Sie ist der
   eigentliche Inhalt -- "255 EUR" sagt nichts, "34 % ueber 2025 zum
   gleichen Tag" sagt alles. */
.jr-bezug {
  display: block;
  font-size: 12px;
  color: var(--ink-3);
  line-height: 1.45;
}
.jr-bezug.is-auf { color: var(--accent-ink); }
.jr-bezug.is-ab { color: var(--crit); }

.jr-aussage {
  margin: 0 0 6px;
  font-size: 17px;
  line-height: 1.45;
}
.jr-aussage strong { font-weight: 650; }
.jr-bezugzeile { margin: 0 0 10px; font-size: 13.5px; color: var(--ink-2); line-height: 1.5; }
.jr-fussnote { margin: 10px 0 0; font-size: 12px; color: var(--ink-3); line-height: 1.5; }

/* Der Schweigegrund. Bewusst keine Warnfarbe: dass ein Kapitel
   nichts behaupten kann, ist kein Fehler -- es ist die ehrliche
   Auskunft, und sie steht da, damit ein fehlender Block nicht mit
   "ich sehe gerade nicht alles" verwechselt wird. */
.jr-schweigt {
  margin: 0;
  color: var(--ink-3);
  font-size: 13.5px;
  line-height: 1.5;
  border-left: 2px solid var(--line-strong);
  padding-left: 10px;
}

.jr-unter {
  margin: 16px 0 6px;
  font-size: 12px;
  color: var(--ink-3);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-weight: 600;
}

.jr-liste { list-style: none; margin: 0; padding: 0; }
.jr-liste li {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 7px 0;
  border-top: 1px solid var(--line);
  font-size: 13.5px;
}
.jr-liste li:first-child { border-top: 0; }
.jr-liste li .grow { flex: 1; min-width: 0; }
.jr-liste li .mono { font-family: var(--data); font-variant-numeric: tabular-nums; white-space: nowrap; }
.jr-liste li.is-gewaehlt { font-weight: 600; }

.jr-rang {
  font-family: var(--data);
  font-size: 11.5px;
  color: var(--ink-3);
  min-width: 16px;
}
.jr-schwach { color: var(--ink-3); }

.jr-trenner {
  margin: 28px 0 10px;
  font-size: 12px;
  color: var(--ink-3);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-weight: 600;
  border-top: 1px solid var(--line);
  padding-top: 14px;
}

/* Monatsmatrix: zwoelf Zahlenspalten. Die Jahresspalte bleibt beim
   horizontalen Scrollen stehen -- ohne sie weiss man nach drei
   Spalten nicht mehr, welche Zeile welches Jahr ist. */
.jr-matrix th, .jr-matrix td { white-space: nowrap; }
.jr-matrix tbody th {
  position: sticky;
  left: 0;
  background: var(--surface);
  text-align: left;
  font-family: var(--data);
  z-index: 1;
}
tr.is-gewaehlt { background: var(--surface-2); }
tr.is-gewaehlt th, tr.is-gewaehlt td { font-weight: 600; }
.jr-matrix tr.is-gewaehlt tbody th, .jr-matrix tr.is-gewaehlt th { background: var(--surface-2); }

@media (max-width: 560px) {
  .jr-zahl .n { font-size: 22px; }
  .jr-aussage { font-size: 15.5px; }
  .jr-zahlen { grid-template-columns: 1fr 1fr; gap: 8px; }
  .jr-zahl { padding: 10px 11px 11px; }
}
@media (max-width: 380px) {
  .jr-zahlen { grid-template-columns: 1fr; }
}

/* Die Einheit hinter einer grossen Zahl: "4" allein sagt nicht, ob es
   Bestellungen oder Stueck sind. Klein und in Fliesstext, damit die Zahl
   die Zahl bleibt. */
.jr-zahl .n .jr-einheit {
  font-family: var(--ui);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink-3);
  margin-left: 6px;
}

/* --- Jahresrückblick, Stufe 2: Diagramme und Inszenierung ------------- */

.jr-chart { width: 100%; margin: 4px 0 2px; }

.jr-unterkopf { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.jr-unterkopf .jr-unter { flex: 1; margin-bottom: 6px; }
.jr-umschalter { margin-bottom: 6px; }

/* Sortierbare Kopfzeile: der Pfeil erscheint erst, wenn nach dieser Spalte
   sortiert ist -- sechs Pfeile nebeneinander waeren Rauschen. */
.jr-sortkopf { cursor: pointer; user-select: none; }
.jr-sortkopf:hover { color: var(--ink); }
.jr-sortkopf:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.jr-sortierbar th[aria-sort="descending"]::after { content: " ↓"; color: var(--ink-3); }
.jr-sortierbar th[aria-sort="ascending"]::after { content: " ↑"; color: var(--ink-3); }

/* Die Einblendung beim Hereinscrollen. Gesetzt wird die Klasse nur vom
   Skript und nur ohne prefers-reduced-motion -- ohne JavaScript ist jedes
   Kapitel von Anfang an sichtbar, und das ist der wichtigere Zustand. */
.jr-verdeckt { opacity: 0; transform: translateY(10px); }
.jr-kapitel { transition: opacity 420ms ease, transform 420ms ease; }
@media (prefers-reduced-motion: reduce) {
  .jr-kapitel { transition: none; }
  .jr-verdeckt { opacity: 1; transform: none; }
}

/* Diagramme auf schmalen Schirmen: flacher, damit ein Kapitel nicht drei
   Bildschirmhoehen braucht, und ohne den Beschriftungsrand der Rangkurve
   (die Namen stehen in der Tabelle darunter). */
@media (max-width: 560px) {
  .jr-chart { height: 220px !important; }
  .jr-unterkopf .jr-umschalter { font-size: 12px; }
}

/* Auf schmalen Schirmen unter die Ueberschrift statt daneben -- sonst
   quetscht sich "Die Top 5 nach Stück" auf drei Zeilen neben den Schalter.
   Gleiche Behandlung wie beim Umschalter im Monatsverlauf der Uebersicht. */
@media (max-width: 560px) {
  .jr-unterkopf .jr-unter { flex-basis: 100%; margin-bottom: 2px; }
}

/* ---------- Keywords ----------

   Die Etsy-Ideensammlung je Artikel und je freiem Kalkulationsentwurf
   (templates/recherche/, Markup in _masken.html). Die Bloecke standen
   anfangs mit ihren Massen im style-Attribut jeder einzelnen Zelle --
   rund fuenfzehn Wiederholungen derselben vier Angaben je Vorlage, und
   an keiner davon liess sich etwas aendern, ohne alle zu finden. */

.kw-zurueck { margin: 0 0 4px; }

/* Der Ausgabekasten: die erfassten Keywords als eine Zeile zum Kopieren.
   Er steht offen ganz oben und nicht mehr zugeklappt im Seitenkopf -- er
   ist das Ergebnis der Seite, alles darunter ist Erfassung. */
.kw-ausgabe { margin-bottom: 12px; }
.kw-ausgabe-kopf { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.kw-ausgabe-kopf h2 { margin: 0; }
.kw-ausgabe-kopf .grow { flex: 1; }
.kw-string {
  margin: 0;
  font-family: var(--data);
  font-size: 13px;
  line-height: 1.6;
  color: var(--ink);
  word-break: break-word;
}

/* "ungespeichert": die Seite traegt drei Formulare mit je eigenem Knopf,
   und das Absenden des einen laedt die Seite neu. Welche Karte gerade
   offene Aenderungen haelt, muss man deshalb sehen koennen -- der Rahmen
   sagt es beilaeufig, die Marke im Kopf sagt es ausdruecklich. */
.kw-block.kw-offen { border-color: var(--warn); }
.kw-marke { flex: none; }

.kw-feldchen { display: flex; flex-direction: column; gap: 3px; }
.kw-label {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.kw-zeichen { font-family: var(--data); font-weight: 400; letter-spacing: 0; text-transform: none; }

/* Ein Feld in einer Erfassungszeile -- dieselbe Hoehe wie in .zeile-felder,
   damit Keyword- und Materialzeilen nicht unterschiedlich hoch aussehen. */
.kw-feld { width: 100%; min-height: 36px; padding: 6px 8px; font-size: 14px; }
select.kw-feld { font-size: 13.5px; }
textarea.kw-feld { min-height: 0; }

/* Der Fuss einer Karte: "+ Zeile" links, Speichern rechts. Ohne ihn zieht
   .stack (flex, column) jeden Knopf auf die volle Kartenbreite -- ein
   randloser gruener Balken quer durch die Karte, dreimal je Seite. */
.kw-fuss { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.kw-fuss .grow { flex: 1; }
.kw-hinweis { font-size: 12px; color: var(--ink-3); }

/* Die beiden Zeilenlisten. Frueher <table>, jetzt ein Grid je Zeile --
   nicht aus Geschmack, sondern weil die Bemerkung eine zweite, volle Reihe
   bekommt und ein zweites <tr> fuer bedarf.js ein zweiter Eintrag waere
   (Begruendung im Kopf von templates/recherche/_masken.html).

   Das Raster steht an der ZEILE und nicht an der Liste: bedarf.js haengt
   neue Zeilen in [data-zeilen] und raeumt [data-reserve] weg -- ein Grid
   ueber den Listenkasten haette diese Zwischenbehaelter als seine Zellen
   gesehen. So ist jede Zeile fuer sich ein kleines Grid, und wieviele
   Behaelter dazwischenliegen, ist gleichgueltig. */
.kw-liste { display: flex; flex-direction: column; gap: 5px; }
.kw-zeilen { display: flex; flex-direction: column; gap: 5px; }
/* Ohne das stuende nach dem Wegraeumen der Reservezeilen eine leere Luecke. */
.kw-zeilen:empty { display: none; }

.kw-kopfzeile,
.kw-zeile { display: grid; gap: 3px 6px; align-items: center; }
.kw-zeile { padding: 0 6px; }
.kw-kopfzeile {
  padding: 0 6px 3px;
  border-bottom: 1px solid var(--line);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* Die Bemerkung laeuft ueber die zweite Reihe, eingerueckt unter ihrem
   Feld: sie gehoert zum Keyword bzw. zum Link daneben, nicht zur Nummer.
   Die uebrigen Felder setzt das Grid von selbst in die erste Reihe. */
.kw-raster-kw { grid-template-columns: 44px 26px minmax(0, 1fr) 40px 130px 104px; }
.kw-raster-kw .kw-bemerkungsfeld { grid-column: 3 / -1; }
.kw-raster-link { grid-template-columns: 26px minmax(0, 1fr) 150px 104px; }
.kw-raster-link .kw-bemerkungsfeld { grid-column: 2 / -1; }

/* Das Kaestchen "aktiv" zuerst: es ist die Frage, mit der man durch eine
   fertig recherchierte Liste geht. Grosszuegige Klickflaeche, weil man es
   vierzigmal hintereinander trifft. */
.kw-haken { display: flex; align-items: center; justify-content: center; min-height: 34px; }
.kw-haken input { width: 16px; height: 16px; cursor: pointer; accent-color: var(--accent); }
.kw-nr { color: var(--ink-3); font-size: 12px; text-align: right; }

/* Ein verworfener Kandidat bleibt stehen -- mit ihm die Begruendung, warum
   er verworfen wurde -- tritt aber zurueck. Gedaempft und nicht versteckt:
   die naechste Ueberarbeitung faengt bei genau diesen Zeilen an. */
.kw-zeile.kw-inaktiv { opacity: 0.55; }
.kw-zeile.kw-inaktiv:focus-within { opacity: 1; }

/* Die Zeichenzahl an der Zeile: geaendert wird hier, und ein Tag von
   einundzwanzig Zeichen faellt bei Etsy durch (routers/recherche.py::
   ETSY_TAG_ZEICHEN). Eine Feststellung, keine Sperre. */
.kw-zeilenzeichen {
  font-family: var(--data);
  font-size: 10.5px;
  color: var(--ink-3);
  text-align: right;
}
.kw-zeilenzeichen.kw-zuviel { color: var(--warn); font-weight: 600; }

/* Das Bemerkungsfeld waechst mit seinem Inhalt (recherche.js) -- hier nur
   der Anfangswert und das Recht, von Hand nachzuziehen. */
.kw-bemerkungsfeld { min-height: 32px; resize: vertical; overflow: hidden; }

/* URL-Feld mit dem Pfeil daneben: die Zeile steht immer im Formular, ohne
   diesen Pfeil waere ein gespeicherter Link nur ein Textfeldinhalt. */
.kw-url { display: flex; align-items: center; gap: 4px; }
.kw-url .srclink-icon { margin-left: 0; flex: none; }

/* Pfeile und Kreuz stehen in den Zeilenlisten in einer eigenen Gridspalte
   und nicht in einem .zeile-kopf -- ohne diesen Zusatz blieben sie
   unformatierte Browserknoepfe und brachen in der schmalen Spalte um. */
td .werkzeug,
.kw-zeile .werkzeug { display: flex; gap: 2px; }
td .werkzeug button,
.kw-zeile .werkzeug button {
  font: inherit;
  font-size: 13px;
  line-height: 1;
  width: 32px;
  min-height: 32px;
  border: 1px solid transparent;
  border-radius: var(--r);
  background: none;
  color: var(--ink-3);
  cursor: pointer;
}
td .werkzeug button:hover,
.kw-zeile .werkzeug button:hover { background: var(--surface-2); color: var(--ink); }
td .werkzeug button.weg:hover,
.kw-zeile .werkzeug button.weg:hover { color: var(--crit); }

/* Kennzahlen im Kartenkopf und im Ausgabekasten -- wie sich die Sammlung
   verteilt, wie lang die Zeile ist, ob etwas doppelt dasteht. */
.kw-stat {
  font-family: var(--data);
  font-size: 11.5px;
  color: var(--ink-3);
  white-space: nowrap;
}
.kw-stat-warn { color: var(--warn); }

/* Welche Keywords im Titelvorschlag vorkommen -- eine Feststellung unter
   dem Feld, keine Bewertung daneben. */
.kw-abgleich { font-size: 12px; color: var(--ink-3); margin-top: 2px; }
/* Der eine Fall, in dem eine Feststellung Farbe bekommt: mehr aktive
   Keywords als Etsy Tagplaetze hat, oder eines davon zu lang. Kein Rot --
   nichts ist kaputt, es passt nur nicht alles. */
.kw-abgleich.kw-warnton { color: var(--warn); }

/* Zweimal derselbe Begriff faellt in acht Zeilen sonst nicht auf. */
.kw-doppelt input[data-feld="text"] { border-color: var(--warn); }

/* Die Chips im Ausgabekasten: ein Knopf je aktivem Keyword, Linksklick
   kopiert genau dieses eine. Bei Etsy werden dreizehn Tagfelder einzeln
   befuellt -- aus einer Kommazeile klaubt man sie mit der Maus heraus,
   hier holt man sie mit dreizehn Klicks.

   Ein kopierter Chip bleibt durchgestrichen stehen, bis die Seite neu
   geladen wird: nach dem siebten Feld weiss man sonst nicht mehr, wo man
   war. Nur Anzeige, nichts davon wird gespeichert. */
.kw-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.kw-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  padding: 4px 9px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  background: var(--surface-2);
  color: var(--ink);
  font: inherit;
  font-size: 13px;
  line-height: 1.35;
  cursor: pointer;
}
.kw-chip:hover { border-color: var(--accent); background: var(--accent-soft); }
.kw-chip-zeichen { font-family: var(--data); font-size: 10.5px; color: var(--ink-3); }
/* Laenger als ein Etsy-Tag sein darf. Im Titel und im Fliesstext lebt so
   ein Begriff weiter -- deshalb markiert und nicht entfernt. */
.kw-chip-lang { border-color: var(--warn); }
.kw-chip-lang .kw-chip-zeichen { color: var(--warn); }
.kw-chip-kopiert { opacity: 0.5; }
.kw-chip-kopiert .kw-chip-text { text-decoration: line-through; }
/* Der kurze Moment nach dem Klick -- die Rueckmeldung, dass wirklich etwas
   in der Zwischenablage gelandet ist. */
.kw-chip-frisch { opacity: 1; border-color: var(--accent); background: var(--accent-soft); }

/* In den Uebersichtslisten: "13 von 40". Die kleine Zahl tritt zurueck,
   die grosse ist die Auskunft. */
.kw-vonzahl { color: var(--ink-3); font-size: 11.5px; }

/* Der Vergleichslink als Kurztext: anklickbar, statt einer rohen Adresse in
   einem Textfeld. Das Feld dahinter holt der Stift. */
.kw-linktext {
  flex: 1;
  /* Der Kurztext bricht nicht um -- ohne Deckel zieht er die Spalte auf
     seine natuerliche Breite und drueckt die uebrigen zusammen, statt
     selbst mit Auslassungspunkten zu enden. */
  min-width: 0;
  max-width: 240px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--accent-ink);
  font-size: 13.5px;
}
.kw-stift {
  flex: none;
  width: 28px;
  min-height: 28px;
  border: 1px solid transparent;
  border-radius: var(--r);
  background: none;
  color: var(--ink-3);
  font: inherit;
  font-size: 12px;
  cursor: pointer;
}
.kw-stift:hover { background: var(--surface-2); color: var(--ink); }

/* Die Listen: eine Vorschau der ersten Keywords statt nur ihrer Anzahl --
   "3" beantwortet nicht, ob die drei taugen. */
.kw-vorschau { color: var(--ink-3); font-size: 12.5px; margin-top: 2px; }

/* Die Etsy-Tags: ein Spiegel des Listings, kein Formular. Sie sehen
   deshalb bewusst nicht wie Eingabefelder aus -- Marken in einer Reihe,
   nicht Zeilen in einer Tabelle. Ein fehlender Tag (steht bei Etsy, aber
   nicht in der eigenen Sammlung) ist hervorgehoben, aber nicht gewarnt:
   dass er fehlt, kann voellig richtig sein. */
.kw-etsy-liste {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.kw-etsy-liste li {
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 3px 8px;
  font-size: 12.5px;
  color: var(--ink-2);
  background: var(--surface-2);
}
.kw-etsy-liste li.kw-etsy-fehlt {
  border-color: var(--accent);
  background: var(--accent-soft);
  color: var(--ink);
}
/* ---- Die zweispaltige Arbeitsflaeche ----

   Links die Keywordliste, rechts der Ergebnisstreifen. Die Seite war eine
   Spalte von rund viertausend Pixeln, und der Titel wird AUS den Keywords
   gebaut -- beides war nie gleichzeitig zu sehen (Begruendung im Kopf von
   templates/recherche/_masken.html).

   Erst ab 1180px: darunter hat der Streifen keine sinnvolle Breite mehr und
   die Spalten stehen untereinander -- dieselbe Reihenfolge wie vorher, nur
   ohne Nebeneinander. */
.kw-arbeitsflaeche { display: flex; flex-direction: column; gap: 14px; }
.kw-spalte { display: flex; flex-direction: column; gap: 14px; min-width: 0; }

@media (min-width: 1180px) {
  .kw-arbeitsflaeche {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 436px;
    align-items: start;
    gap: 16px;
  }
  /* Der Streifen laeuft mit: beim Durchgehen einer langen Keywordliste
     soll das Tag-Budget stehenbleiben, sonst waere das Nebeneinander nach
     zwanzig Zeilen wieder ein Untereinander. `top` haelt Abstand zum
     Seitenkopf. */
  .kw-streifen { position: sticky; top: 14px; }
}

/* Der Speicherstand im Seitenkopf -- eine Stelle fuer alle Formulare, statt
   vier Knoepfe. Zurueckhaltend, solange alles gut geht: es ist eine
   Bestaetigung, keine Meldung. */
.kw-stand {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  color: var(--accent-ink);
}
.kw-stand::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
}
.kw-stand-fehler { color: var(--crit); }
.kw-stand-fehler::before { background: var(--crit); }

/* Ein Kartenkopf innerhalb des Kartenkoerpers -- die Karten im Streifen
   haben keine eigene .card-head, weil sie kaum hoeher als ihr Kopf sind. */
.kw-kartenkopf { display: flex; align-items: baseline; gap: 9px; }
.kw-kartenkopf h2 { margin: 0; font-size: 15px; }
.kw-kartenkopf .grow { flex: 1; }

/* Das Tag-Budget: dreizehn Faecher, von denen so viele voll sind, wie
   Keywords angehakt wurden. Etsy nimmt dreizehn Tags -- das stand vorher
   als Satz da; so sieht man es. Ueber dreizehn schlagen alle um, weil dann
   nicht EIN Fach zu viel ist, sondern die Auswahl als ganze nicht passt. */
.kw-faecher { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 3px; }
.kw-fach {
  height: 7px;
  border-radius: 2px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  box-sizing: border-box;
}
.kw-fach.voll { background: var(--accent); border-color: var(--accent); }
.kw-fach.voll.ueber { background: var(--warn); border-color: var(--warn); }

/* Die kommagetrennte Zeile ist der Fall "alles auf einmal" und steht
   deshalb zugeklappt unter den Chips -- die einzeln anklickbaren Chips sind
   der haeufigere Weg (dreizehn Etsy-Felder, dreizehn Klicks). */
.kw-zeile-auf > summary {
  cursor: pointer;
  font-size: 12px;
  color: var(--ink-3);
  list-style-position: inside;
}
.kw-zeile-auf > summary:hover { color: var(--ink); }
.kw-zeile-auf .kw-string { margin-top: 6px; }

/* Welche der gewaehlten Keywords im Titel stehen -- als Marken und nicht
   als Satz: was fehlt, liest man an den offenen Umrissen schneller ab als
   in einer Aufzaehlung. Eine Feststellung, keine Bewertung. */
.kw-titelabgleich { display: flex; flex-wrap: wrap; gap: 4px; }
.kw-titelmarke {
  font-size: 11.5px;
  padding: 2px 7px;
  border-radius: var(--r);
  border: 1px dashed var(--line-strong);
  color: var(--ink-3);
}
.kw-titelmarke.drin {
  border-style: solid;
  border-color: transparent;
  background: var(--accent-soft);
  color: var(--accent-ink);
}

/* Ist und Soll nebeneinander: was am Listing haengt, und was davon in der
   Auswahl noch fehlt. Die Frage beim Optimieren betrifft immer beides. */
.kw-istsoll { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; align-items: start; }
.kw-istsoll > div { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.kw-fehltliste { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--ink-2); }
.kw-fehltliste.kw-fehlt-leer { color: var(--ink-3); }

/* Der Wettbewerb steht zugeklappt: er wird gelesen und selten geaendert.
   Ein <details> und kein Javascript -- der Browser kann das. Das Dreieck
   macht der Browser, der Kopf sieht aus wie jeder andere Kartenkopf. */
.kw-klapp > summary { cursor: pointer; }
/* Das Dreieck selbst zeichnen: `summary` ist hier eine `.card-head`, also
   ein Flex-Kasten -- und der unterdrueckt den nativen Marker. Ohne
   Zeichen sieht man dem Kopf nicht an, dass er sich aufklappen laesst. */
.kw-klapp > summary { list-style: none; }
.kw-klapp > summary::-webkit-details-marker { display: none; }
.kw-klapp > summary::before {
  content: "\25B8";
  flex: none;
  font-size: 11px;
  line-height: 1;
  color: var(--ink-3);
}
.kw-klapp[open] > summary::before { content: "\25BE"; }
.kw-klapp > summary:hover h2 { color: var(--accent-ink); }

/* Der Filter ueber der Keywordliste. `.seg` war bisher nur fuer <a>
   geschrieben (Reiter in den Listen); hier sind es Knoepfe, die nichts
   laden. */
.seg button {
  font: inherit;
  font-size: 12px;
  padding: 5px 11px;
  border: none;
  background: none;
  color: var(--ink-3);
  cursor: pointer;
}
.seg button:hover { color: var(--ink); }
.seg button.on { background: var(--surface); color: var(--ink); box-shadow: 0 1px 2px rgba(0,0,0,0.06); }
.seg.sm button { padding: 4px 9px; font-size: 11.5px; }
.kw-filter { flex: none; margin-left: auto; }

/* Der Kartenkopf der Keywordliste traegt Ueberschrift, Statistik UND
   Filter. Nebeneinander sind das ueber sechshundert Pixel -- in einem
   schmalen Fenster schob das die ganze Seite seitlich hinaus, statt
   umzubrechen. Nur an den Karten dieses Moduls: `.card-head` traegt die
   halbe Anwendung. */
.kw-block .card-head { flex-wrap: wrap; }
.kw-block .card-head .kw-stat { white-space: normal; }

/* Die Keywordliste ist die Arbeitsflaeche und darf die Hoehe nehmen, die
   sie braucht -- der Wettbewerb darunter ist zugeklappt. */
.kw-liste-karte { min-width: 0; }
.kw-zahlkopf { text-align: right; }

/* Der naechste Schritt in den Uebersichtslisten -- abgeleitet aus dem, was
   ohnehin dasteht (routers/recherche.py::_naechster_schritt). Die Farbe
   sagt, ob etwas offen ist, nicht ob etwas falsch ist. */
.kw-schritt {
  display: inline-block;
  font-size: 12px;
  padding: 3px 9px;
  border-radius: var(--r);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink-2);
  white-space: nowrap;
}
.kw-schritt.leer { background: var(--surface-2); color: var(--ink-3); }
.kw-schritt.achtung { background: var(--warn-soft); border-color: var(--warn); color: var(--ink); }
.kw-schritt.bereit,
.kw-schritt.fertig { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink); }

/* Fuer Beschriftungen, die der Bildschirmleser braucht und das Auge nicht
   -- z. B. am Aktiv-Kaestchen, dessen Spaltenkopf zu weit weg steht. */
.visuell-versteckt {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Schmale Fenster: der Kopf faellt weg (er beschriftet Spalten, die es
   dann nicht mehr gibt), jede Zeile wird ein kleiner Kasten. Das Kaestchen
   bleibt in jedem Fall stehen -- es ist der Grund, warum es diese Liste
   in dieser Form gibt. */
@media (max-width: 760px) {
  .kw-kopfzeile { display: none; }
  .kw-zeile {
    padding: 6px;
    border: 1px solid var(--line);
    border-radius: var(--r);
  }
  /* Das Keyword bekommt die ganze Breite -- es ist das, was man liest und
     aendert; Art, Zeichenzahl und Werkzeug ruecken darunter. Die
     Zeichenzahl steht dabei unter dem Kaestchen und nicht in einer eigenen
     Spalte: eine Spalte fuer zwei Ziffern kostet am Telefon ein Drittel
     des Platzes, den das Keyword braucht. */
  .kw-raster-kw { grid-template-columns: 40px minmax(0, 1fr) 104px; }
  .kw-raster-kw .kw-nr { display: none; }
  .kw-raster-kw .kw-text { grid-column: 2 / -1; }
  .kw-raster-kw .kw-zeilenzeichen { grid-column: 1; grid-row: 2; text-align: center; }
  .kw-raster-kw .kw-art { grid-column: 2; }
  .kw-raster-kw .werkzeug { grid-column: 3; }
  .kw-raster-kw .kw-bemerkungsfeld { grid-column: 1 / -1; }

  .kw-raster-link { grid-template-columns: minmax(0, 1fr) 104px; }
  .kw-raster-link .kw-nr { display: none; }
  .kw-raster-link .kw-url { grid-column: 1 / -1; }
  .kw-raster-link .kw-bemerkungsfeld { grid-column: 1 / -1; }
  .kw-linktext { max-width: none; }

  /* Ist und Soll untereinander: zwei Spalten von je 160px sind keine. */
  .kw-istsoll { grid-template-columns: minmax(0, 1fr); }
  /* Der Speicherstand rutscht im Seitenkopf unter den Titel. */
  .kw-stand { order: 9; flex-basis: 100%; }
}

.kw-etsy-fehler { align-self: flex-start; }
/* In der Liste: noch nie abgerufen. Zurueckhaltend, weil es kein Versaeumnis
   beschreibt, sondern eine offene Frage. */
.kw-etsy-leer { color: var(--ink-3); }

/* ============================================================
   Shop-Bericht (/shop-bericht)
   ------------------------------------------------------------
   Bewusst wenige eigene Klassen: Karte, Klapp-Block und Knopf
   kommen aus dem Bestand -- die Quartalswahl ist deshalb eine Reihe
   gewoehnlicher .btn-Knoepfe und traegt in jedem Theme automatisch
   dessen Primaerfarbe. Eigenes CSS gibt es nur fuer das Kachelraster
   und die Hinzufuegen-Kachel.
   ============================================================ */

.sb-quartalswahl { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.sb-jahrwahl { display: flex; align-items: center; gap: 4px; }
.sb-jahr {
  font-family: var(--data);
  font-size: 14px;
  font-weight: 700;
  min-width: 44px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.sb-quartale { display: flex; gap: 4px; }
.sb-quartal-zahl { font-family: var(--data); font-size: 11px; opacity: 0.7; margin-left: 5px; }

.sb-fehler { margin: 12px 0 0; font-size: 13px; color: var(--crit); }

.sb-notiz { margin-top: 14px; }
.sb-notiz-hinweis { font-size: 11.5px; color: var(--ink-3); }
.sb-notiz textarea { width: 100%; resize: vertical; }

.sb-kacheln { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; margin-top: 16px; }
.sb-kachel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r); padding: 14px 14px 10px; display: flex; flex-direction: column; gap: 8px; }
.sb-kachel-link { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; text-align: center; color: inherit; text-decoration: none; padding: 6px 0 2px; }
.sb-kachel-glyph { font-size: 34px; line-height: 1; }
.sb-kachel-titel { font-size: 13px; font-weight: 600; word-break: break-word; }
.sb-kachel-fuss { display: flex; align-items: center; justify-content: space-between; border-top: 1px solid var(--line); padding-top: 8px; }
.sb-kachel-datum { font-size: 11.5px; color: var(--ink-3); }
.sb-loeschen {
  border: none; background: none; color: var(--ink-3); cursor: pointer;
  font-size: 13px; line-height: 1; padding: 2px 4px; border-radius: 4px;
}
.sb-loeschen:hover { color: var(--crit); background: var(--surface-2); }

/* Hochladen ist selbst eine Kachel im selben Raster -- gestrichelt, damit
   sie als Platzhalter und nicht als abgelegte Datei liest. Das Dateifeld
   liegt unsichtbar, aber fokussierbar darin (kein [hidden]), damit die
   Kachel auch mit der Tastatur erreichbar bleibt. */
.sb-kachel-neu { position: relative; }
.sb-neu-flaeche {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 100%;
  min-height: 116px;
  padding: 14px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--r);
  color: var(--ink-3);
  cursor: pointer;
  transition: border-color 110ms ease, color 110ms ease, background 110ms ease;
}
.sb-neu-flaeche:hover,
.sb-neu-flaeche:focus-within { border-color: var(--accent); color: var(--accent-ink); background: var(--surface); }
.sb-neu-flaeche input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0; }
.sb-neu-glyph { font-size: 22px; line-height: 1; }
.sb-neu-text { font-size: 12.5px; font-weight: 600; }

.sb-leer { margin: 16px 0 0; font-size: 13px; color: var(--ink-3); }
