/* Storefront-Styles (storefront-website-konzept.md Scheibe 1).
   Alle Farben/Radius/Font kommen als CSS-Variablen — shop.js überschreibt sie
   aus den Theme-Tokens (CSSOM, CSP-konform). Defaults = Preset „verde". */
/* #1955/AE-926: `hidden` muss gelten. Die Browser-Vorgabe [hidden]{display:none} ist die
   schwaechste Regel; jede Klasse mit `display` (hier z. B. .actions) ueberstimmte sie, und die
   Knoepfe blieben nach einer Ablehnung sichtbar (#1947 F10). Waechter:
   tests/test_oeffentliche_seiten_hidden_gilt.py */
[hidden] { display: none !important; }

:root {
  --bg: #f6f4ee; --card: #fffdf8; --ink: #1b2420; --ink-soft: #5c6b62;
  --line: #e3e0d5; --brand: #2e5542; --brand-dark: #244333; --brand-soft: #dfece4;
  --accent: #b06a3b; --radius: 16px;
  --grade-a: #2e7d5b; --grade-b: #b7791f; --grade-c: #b0603b;
  /* #2632/AE-1486 (H1): Spanne mehrerer Zustände („Grade A–C“) — neutral, weil jede Grade-Farbe
     genau einen Zustand meint; fest wie die Grade-Farben (kein Theme-Token: `--ink-soft` wird in
     dunklen Vorlagen hell, weiße Schrift darauf wäre unlesbar). Weiß auf #4a5560 = 7,6:1. */
  --grade-spanne: #4a5560;
  --shadow: 0 1px 2px rgba(0,0,0,.06), 0 8px 24px rgba(0,0,0,.05);
  --font: "Segoe UI", system-ui, -apple-system, sans-serif;
  --font-display: "Segoe UI", system-ui, -apple-system, sans-serif;
  --display-case: none;
  /* #2582/AE-1452 — Bewegungs-Token: WENIGE Stufen statt freier Zahlen je Regel. Jede Dauer in
     diesem Stylesheet haengt an einer Stufe (Waechter `tests/test_shop_bewegung_2582.py`), und
     der Reduce-Zweig unten setzt ALLE Stufen auf 0 — damit steht jede Bewegung bei „Bewegung
     reduzieren" still, auch eine, die es heute noch nicht gibt (Klasse statt Liste).
     Stufen aus dem Bestand gemessen (31.10.2026): .15 s x5 Knoepfe/Links, .25 s x3 Drawer,
     .35 s Bild-Ueberblendung, .5 s Einblenden. Die zwei .2-s-Stellen (Overlay, Toast) liegen
     jetzt auf `mittel` (+50 ms): das Overlay blendet damit im selben Takt wie der Drawer, zu
     dem es gehoert — die einzige bewusste Verhaltensaenderung der Umstellung.
     Neue Bausteine bleiben bei <= 300 ms; nur der Lade-Schimmer laeuft als Schleife
     (`schleife`), und nur solange geladen wird. */
  --dauer-kurz: 150ms; --dauer-mittel: 250ms; --dauer-ruhig: 350ms; --dauer-lang: 500ms;
  --dauer-schleife: 1400ms;
  /* #2609/AE-1463 — saisonale Stufen (laufen laenger als 5 s, deshalb mit Halt-Knopf):
     ein Schlittenflug ueber die Breite, ein Galopp-Schritt, ein Funkel-Zyklus der Lichter. */
  --dauer-flug: 9000ms; --dauer-galopp: 640ms; --dauer-funkeln: 2800ms;
  --kurve: ease; --kurve-aus: cubic-bezier(.2, .8, .2, 1);
}
/* #2658/AE-1499 — Schrift-Stapel EINMAL, hier. Die Seite trägt nur den Schlüssel
   (`data-schrift` = Token `font_stack`, `data-titelschrift` = `display_font` bzw. bei „inherit“
   der Fließtext-Stapel): der Server setzt ihn schon in der ausgelieferten Seite
   (`shop_html._grundwerte_einsetzen`), `applyTheme` bei jeder Vorschau. Vorher stand die Tabelle
   in `shop.js` (FONTS) und galt erst nach `/config`. Ein unbekannter Schlüssel trifft keine
   Regel und behält den System-Stapel von `:root` — wie vorher `FONTS[x] || FONTS.system`.
   Die Schlüsselmenge = die Literale `FontStack`/`DisplayFont` (Wächter test_shop_ladefolge_2658). */
:root[data-schrift="system"] { --font: "Segoe UI", system-ui, -apple-system, sans-serif; }
:root[data-schrift="serif"] { --font: Georgia, "Times New Roman", serif; }
:root[data-schrift="rounded"] { --font: ui-rounded, "Segoe UI", system-ui, sans-serif; }
:root[data-schrift="mono"] { --font: ui-monospace, Consolas, "Courier New", monospace; }
:root[data-titelschrift="system"] { --font-display: "Segoe UI", system-ui, -apple-system, sans-serif; }
:root[data-titelschrift="serif"] { --font-display: Georgia, "Times New Roman", serif; }
:root[data-titelschrift="rounded"] { --font-display: ui-rounded, "Segoe UI", system-ui, sans-serif; }
:root[data-titelschrift="mono"] { --font-display: ui-monospace, Consolas, "Courier New", monospace; }
/* Nur für Überschriften (System-Fonts — die /shop-CSP lädt keine Webfonts): */
:root[data-titelschrift="condensed"] { --font-display: Bahnschrift, "Arial Narrow", "Segoe UI", sans-serif; }
:root[data-titelschrift="slab"] { --font-display: Rockwell, "Roboto Slab", Georgia, serif; }
:root[data-titelschrift="didone"] { --font-display: Didot, "Bodoni MT", "Playfair Display", Georgia, serif; }
* { box-sizing: border-box; margin: 0; padding: 0; }
/* #1717/AE-695 — ZWEI Ringe, nicht einer, und zwar in DIESEN zwei Farben.
   ── Warum ueberhaupt zwei ──────────────────────────────────────────────────
   `:focus-visible` ist eine GLOBALE Regel: sie trifft JEDES fokussierbare
   Element, gleich auf welcher Flaeche es liegt. Ein EINFARBIGER Ring kann das
   nicht leisten, und das ist gemessen, nicht vermutet: der bisherige
   `--accent`-Ring lag gegen `accent` (der Knopf in `.banner.tone-accent`, :464)
   bei 1,00:1 in 11 von 11 Themes, gegen `brand` (`.banner.tone-brand`) in 8 von
   11 unter dem harten Boden 3,0, gegen `brand_dark` ebenfalls 8, gegen `line`
   4, gegen `brand_soft` 3, gegen `ink` 2 (Register #1717 Etappe 1). Ueber dem
   Boden waren nur `bg` und `card` — genau die zwei Flaechen, die der Vertrag
   misst. `outline-offset: 2px` laesst den Spalt zwischen Element und Ring dem
   VORFAHREN; der `box-shadow` fuellt genau diesen Spalt mit der zweiten Farbe.
   ── Warum INK und CARD und nicht accent/card ───────────────────────────────
   W3C-Technik C40 („Creating a two-color focus indicator to ensure sufficient
   contrast with all components", abgerufen 06.09.2026): die Garantie „mindestens
   EINE der beiden Farben schafft 3:1 gegen JEDEN Untergrund" gilt genau dann,
   wenn die beiden Ringfarben untereinander >= 9:1 haben. Der Beweis ist eine
   Zeile: waeren beide unter 3:1 gegen X, waere ihr Verhaeltnis untereinander
   kleiner als 3*3 = 9.
   ⛔ Der erste Entwurf dieser Scheibe nahm `accent`/`card` — GEMESSEN 3,23:1
   bis 8,45:1 ueber die elf Themes, also UNTER 9. Direkt nachgerechnet blieben
   damit 7 von 88 Theme/Flaechen-Paaren unter 3,0 (`brand_soft` und `line` in
   kreislauf/fjord/depot/atelier). `ink`/`card` liegt bei 13,70:1 bis 18,21:1 —
   die Garantie traegt fuer alle elf ausgelieferten Themes, und der Ring ist
   damit auf JEDER Flaeche identifizierbar, auch auf einer, die es heute noch
   gar nicht gibt. Zusaetzlich ist ink/card als „Fliesstext auf der Karte" schon
   ein erzwungener Vertrag. Bewacht wird beides in
   `tests/test_shop_css_farbnaht.py` (Regel ueber alle `outline`-Regeln, plus
   die 9:1-Nachrechnung ueber alle Vorlagen), nicht als Liste dieser Stellen.
   Muster aus der Praxis: GOV.UK Design System (gelb + schwarz, doppelter
   `box-shadow`) und der „universal focus indicator" (schwarz + weiss). */
:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--card); }
/* `box-shadow` ERSETZT, es ergaenzt nicht. Die drei fokussierbaren Flaechen mit
   eigenem Schatten bekommen ihn deshalb hier zurueck — das ist CSS-Mechanik der
   Kurzschreibweise, keine Fall-Liste ueber Kontraste. */
.gl-nav:focus-visible { box-shadow: 0 0 0 2px var(--card), var(--shadow); }
.drawer:focus-visible { box-shadow: 0 0 0 2px var(--card), -12px 0 40px rgba(0,0,0,.15); }
.legal-panel:focus-visible { box-shadow: 0 0 0 2px var(--card), 12px 0 40px rgba(0,0,0,.15); }
/* clip statt hidden: Full-bleed-Sections (100vw) dürfen keinen horizontalen
   Scrollbalken erzeugen, ohne position:sticky zu brechen (html UND body —
   sonst zählt der 100vw-Überstand weiter zur Scroll-Fläche). */
html, body { overflow-x: clip; }
body {
  font-family: var(--font);
  background: var(--bg); color: var(--ink); line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, .section-title { font-family: var(--font-display); }
h1, h2.section-title, .section-title { text-transform: var(--display-case); }
.wrap { max-width: 1180px; margin: 0 auto; padding: 0 20px; }
header {
  position: sticky; top: 0; z-index: 20;
  background: color-mix(in srgb, var(--bg) 90%, transparent);
  backdrop-filter: blur(8px); border-bottom: 1px solid var(--line);
}
.bar { display: flex; align-items: center; gap: 16px; height: 62px; }
.logo { display: flex; align-items: center; gap: 9px; font-weight: 700; font-size: 1.25rem; letter-spacing: -.02em; }
.logo .mark { width: 30px; height: 30px; border-radius: calc(var(--radius) / 2); background: var(--brand); color: var(--card);
  display: grid; place-items: center; font-weight: 800; font-size: .95rem; }
/* #2205/AE-1152: Logo-Bild statt Kürzel — Höhe der Marke, Breite folgt dem Bild (Querformat-Logos). */
.logo .mark.has-img { width: auto; max-width: 160px; background: none; border-radius: 0; }
.logo .mark.has-img img { display: block; height: 30px; width: auto; max-width: 160px; object-fit: contain; }
.cart-btn { margin-left: auto; border: 1px solid var(--line); background: var(--card); color: var(--ink);
  border-radius: 999px; padding: 9px 16px; font-size: .92rem; font-weight: 600; cursor: pointer;
  display: flex; align-items: center; gap: 8px; font-family: inherit; }
.cart-btn .count { background: var(--brand); color: var(--card); border-radius: 999px; min-width: 20px;
  height: 20px; font-size: .72rem; display: grid; place-items: center; padding: 0 5px; }
.hero { padding: 54px 0 30px; }
.hero h1 { font-size: clamp(2rem, 5vw, 3.1rem); line-height: 1.05; letter-spacing: -.03em; max-width: 18ch; }
.hero p { margin-top: 16px; color: var(--ink-soft); font-size: 1.08rem; max-width: 52ch; }
.hero .trust { margin-top: 22px; display: flex; flex-wrap: wrap; gap: 10px 22px; font-size: .9rem; color: var(--ink-soft); }
.hero .trust b { color: var(--ink); }
.section-title { font-size: 1.35rem; letter-spacing: -.02em; padding: 26px 0 4px; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 18px; padding: 12px 0 34px; }
.card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
  box-shadow: var(--shadow); display: flex; flex-direction: column; }
.thumb { aspect-ratio: 16/9; background: var(--brand-soft);
  display: grid; place-items: center; position: relative; overflow: hidden; }
.thumb svg { width: 44%; max-height: 72%; color: var(--brand); opacity: .55; }
/* Hochformat-Platzhalter (Smartphone/Tablet, #433): ohne Hoehen-Deckel wuerde
   das 16:9-Thumb die tall-SVG oben/unten abschneiden (nur Striche sichtbar). */
/* Produkt-Medien (§87): Bilder gestapelt (Crossfade via .active), feste
   Aspect-Ratio -> kein Layout-Shift; weitere Bilder laden erst beim Hover. */
.thumb img.sl { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; opacity: 0; }
.thumb img.sl.active { opacity: 1; }
/* #2647: nicht ladbares Medienbild — unsichtbar statt Kaputt-Symbol mit Alt-Text über dem
   Etikett; die Karte zeigt dann die Bauform-Zeichnung (.ph-ersatz, Grid-Mitte wie ohne Medien). */
img.bild-fehlt { visibility: hidden; }
.ph-ersatz { display: contents; }
.motion .thumb img.sl { transition: opacity var(--dauer-ruhig) var(--kurve); }
/* #2658/AE-1499: Bilder ploppten in leere Flächen (gemessen 560–660 ms nach dem Inhalt). Jetzt liegt
   die Bauform-Zeichnung von Anfang an darunter (`thumbHtml`), das Bild blendet nach `load` darüber
   ein (`.geladen`, gesetzt in shop.js) und die Zeichnung im selben Takt aus — auch hinter einem
   freigestellten Bild (transparentes PNG) bleibt sie nicht stehen. Ohne `.motion` kein Übergang. */
.motion .thumb img.sl.active:not(.geladen) { opacity: 0; }
.thumb:has(img.sl.active.geladen) > .ph-ersatz > svg { opacity: 0; }
.motion .thumb > .ph-ersatz > svg { transition: opacity var(--dauer-ruhig) var(--kurve); }
.thumb[data-action] { cursor: pointer; }
/* #2641: `zoom-in` nur, wo ein Zoom dahinter liegt (Element `bild_zoom`, Klasse am <html>). */
html.zoom-an .thumb[data-action="open-gallery"] { cursor: zoom-in; }
.thumb .dots { position: absolute; bottom: 8px; left: 50%; transform: translateX(-50%);
  display: flex; gap: 5px; }
.thumb .dots i { width: 6px; height: 6px; border-radius: 50%;
  background: rgba(255,255,255,.55); box-shadow: 0 0 0 1px rgba(0,0,0,.22); }
.thumb .dots i.on { background: #fff; }
.thumb .vid-badge { position: absolute; right: 10px; bottom: 8px; width: 26px; height: 26px;
  border-radius: 50%; background: rgba(10,12,11,.55); color: #fff; font-size: .62rem;
  display: grid; place-items: center; padding-left: 2px; }
.grade { position: absolute; top: 10px; left: 10px; font-size: .72rem; font-weight: 700;
  color: #fff; border-radius: 999px; padding: 3px 10px; letter-spacing: .02em; }
.g-A { background: var(--grade-a); } .g-B { background: var(--grade-b); } .g-C, .g-D { background: var(--grade-c); }
.g-spanne { background: var(--grade-spanne); }
/* #2625/AE-1479: die CO₂-Angabe ist eine Zeile im Kartenkörper (vorher Plakette „−350 kg CO₂“ auf
   dem Bild). Mit Bezug („Herstellung vermieden*“) passt sie nicht neben das Grade-Etikett. Farbe
   `--ink-soft` wie `.card .specs` — dieselbe Kontrast-Zusage, kein neues Farbpaar. */
.co2-zeile { font-size: .8rem; font-weight: 600; color: var(--ink-soft); }
.card .body { padding: 16px; display: flex; flex-direction: column; gap: 8px; flex: 1; }
.card h3 { font-size: 1.02rem; letter-spacing: -.01em; }
.card .cat { font-size: .78rem; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .08em; }
.card .specs { font-size: .84rem; color: var(--ink-soft); }
.card .foot { margin-top: auto; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding-top: 8px; }
.price { font-size: 1.3rem; font-weight: 700; letter-spacing: -.02em; }
.price small { font-size: .7rem; font-weight: 500; color: var(--ink-soft); display: block; }
/* #2657 A4 (§ 11 PAngV): die Angabe zum vorherigen Preis gehört an die Preisangabe selbst
   (Leitlinie 2021/C 526/02 Abschn. 2.2: „an der Verkaufsstelle“) — gleich gut lesbar wie der
   Steuerhinweis (§ 1 Abs. 3 S. 2 PAngV: klar erkennbar, gut lesbar), kein Rot, kein Streichpreis. */
.price .preis-vorher { font-size: .74rem; font-weight: 500; color: var(--ink-soft); margin: 3px 0 0;
  letter-spacing: 0; line-height: 1.35; }
.price .preis-rabatt { font-weight: 700; color: var(--ink); margin-right: 4px; }
.price .ab { font-size: .8rem; font-weight: 500; color: var(--ink-soft); margin-right: 4px; }
.add { background: var(--brand); color: var(--card); border: none; border-radius: calc(var(--radius) * .6); padding: 10px 14px;
  font-weight: 600; font-size: .9rem; cursor: pointer; transition: background var(--dauer-kurz) var(--kurve); font-family: inherit; }
.add:hover { background: var(--brand-dark); }
.add:disabled { background: var(--ink-soft); cursor: default; opacity: .55; }
/* Varianten-Matrix */
.axis { margin-top: 2px; }
.axis .axis-name { font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .07em; color: var(--ink-soft); margin-bottom: 4px; }
.vals { display: flex; flex-wrap: wrap; gap: 6px; }
.vbtn { border: 1px solid var(--line); background: var(--bg); color: var(--ink); border-radius: 999px;
  padding: 5px 12px; font-size: .82rem; font-weight: 600; cursor: pointer; font-family: inherit; }
.vbtn.sel { background: var(--brand); border-color: var(--brand); color: var(--card); }
.vbtn:disabled { opacity: .35; cursor: default; text-decoration: line-through; }
/* Such-/Filter-Leiste (§90): dieselbe Pill-Sprache wie .vbtn/.cart-btn —
   alles Token-Farben, kein Inline-Style. Freitext + Preisspanne debounced. */
.toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 10px 0 4px; }
.tb-q { flex: 1 1 230px; min-width: 180px; border: 1px solid var(--line); background: var(--card);
  color: var(--ink); border-radius: 999px; padding: 10px 18px; font-size: .92rem; font-family: inherit; }
.tb-q::placeholder { color: var(--ink-soft); opacity: .75; }
.tb-select { border: 1px solid var(--line); background: var(--card); color: var(--ink);
  border-radius: 999px; padding: 10px 14px; font-size: .88rem; font-weight: 600;
  font-family: inherit; cursor: pointer; max-width: 100%; }
/* Preisspanne als EINE Pille — liest sich wie im Text: „300 – 800 €". */
.tb-price { display: inline-flex; align-items: center; gap: 2px; border: 1px solid var(--line);
  background: var(--card); border-radius: 999px; padding: 3px 14px; color: var(--ink-soft); }
.tb-price input { width: 62px; border: none; background: none; color: var(--ink);
  font-family: inherit; font-size: .9rem; padding: 7px 2px; text-align: center;
  appearance: textfield; -moz-appearance: textfield; outline: none; }
.tb-price input::-webkit-outer-spin-button,
.tb-price input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.tb-price input::placeholder { color: var(--ink-soft); opacity: .75; }
/* Fokus sichtbar auf der GRUPPE (die inneren Felder sind rahmenlos). */
.tb-price:focus-within { outline: 2px solid var(--ink); outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--card); }
.tb-dash { padding: 0 2px; }
.tb-eur { font-size: .88rem; padding-left: 4px; }
.tb-meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 16px;
  padding: 10px 2px 2px; font-size: .88rem; color: var(--ink-soft); }
.tb-reset { background: none; border: none; padding: 0; cursor: pointer; font: inherit;
  font-weight: 600; color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.tb-reset:hover { color: var(--brand); }
.catalog-body.busy .grid { opacity: .55; }
@media (max-width: 640px) { .tb-qwrap { flex-basis: 100%; } }
/* #1998/AE-966 — Suchfeld mit Vorschlaegen, Filter-Seitenleiste, Marken, Telefon-Dialog.
   Alle Farben aus den Theme-Tokens (Farbnaht-Riegel); keine neue Farbe. */
.tb-qwrap { position: relative; flex: 1 1 260px; min-width: 180px; display: flex; }
.tb-qwrap .tb-q { flex: 1 1 auto; min-width: 0; width: 100%; }
.sg-list { position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 25; list-style: none;
  background: var(--card); border: 1px solid var(--line); border-radius: 14px; box-shadow: var(--shadow);
  max-height: min(70vh, 460px); overflow-y: auto; padding: 6px; }
.sg-opt, .sg-all, .sg-none { display: flex; align-items: center; gap: 12px; padding: 8px 10px;
  border-radius: 10px; cursor: pointer; font-size: .9rem; color: var(--ink); }
.sg-opt img, .sg-ph { width: 48px; height: 36px; flex: none; object-fit: cover; border-radius: 6px;
  background: var(--bg); color: var(--ink-soft); display: inline-flex; align-items: center; justify-content: center; }
.sg-ph svg { width: 30px; height: 22px; }
.sg-t { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sg-p { font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.sg-all { justify-content: center; font-weight: 700; color: var(--brand); border-top: 1px solid var(--line);
  border-radius: 0 0 10px 10px; margin-top: 4px; }
.sg-none { cursor: default; color: var(--ink-soft); }
.sg-opt:hover, .sg-all:hover { background: var(--brand-soft); }
/* Markierter Vorschlag (aria-activedescendant): der Fokus bleibt im Feld, also
   zeichnet die Liste den Ring selbst — innen liegend, auf der eigenen Flaeche
   derselben Regel (Farbnaht-Waechter: Ring und Nachbar in EINER Regel). */
.sg-opt.aktiv, .sg-all.aktiv { background: var(--card); outline: 2px solid var(--ink);
  outline-offset: -2px; }
.tb-filter { display: none; border: 1px solid var(--line); background: var(--card); color: var(--ink);
  border-radius: 999px; padding: 10px 16px; font-size: .88rem; font-weight: 600; font-family: inherit;
  cursor: pointer; }
/* Marken: umbrechende Reihe (Baymard: am Telefon gestapelt statt versteckt). */
.chips { list-style: none; display: flex; flex-wrap: wrap; gap: 6px 8px; padding: 8px 0 2px; align-items: center; }
.chip { border: 1px solid var(--brand); background: var(--brand-soft); color: var(--ink); border-radius: 999px;
  padding: 5px 12px; font-size: .82rem; font-weight: 600; font-family: inherit; cursor: pointer; max-width: 100%;
  overflow-wrap: anywhere; text-align: left; }
.chip:hover { background: var(--card); }
.chip-reset { background: none; border: none; padding: 5px 4px; cursor: pointer; font: inherit; font-size: .84rem;
  font-weight: 600; color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
/* Seitenleiste neben dem Katalog (Desktop). */
.flt-layout { display: grid; grid-template-columns: 230px minmax(0, 1fr); gap: 22px; align-items: start; }
.flt-side { padding: 12px 14px 34px 0; position: sticky; top: 12px; max-height: calc(100vh - 24px); overflow-y: auto;
  scrollbar-gutter: stable; scrollbar-width: thin; scrollbar-color: var(--line) transparent; }
/* #2677/AE-1515 (Alexander 02.11.2026): der eigene Scrollbalken der Seitenleiste stand im Browser-Standard
   (hell, breit) direkt an den Trefferzahlen und passte in keinen Stil. Jetzt: Abstand zu den Zahlen
   (padding-right + stabile Rinne, die Zahlen springen nicht, wenn der Balken erscheint), schmal, Daumen
   in der Linienfarbe des Stils, Spur durchsichtig; erst beim Darüberfahren kräftiger (Textfarbe-leise). */
.flt-side:hover, .flt-side:focus-within { scrollbar-color: var(--ink-soft) transparent; }
.flt-group { border: none; border-top: 1px solid var(--line); padding: 12px 0 10px; }
.flt-group:first-child { border-top: none; padding-top: 0; }
.flt-group legend { font-size: .8rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-soft); padding: 0 0 6px; }
.flt-opts { list-style: none; display: flex; flex-direction: column; gap: 2px; }
.flt-opt { display: flex; align-items: center; gap: 8px; padding: 5px 2px; font-size: .9rem; cursor: pointer;
  color: var(--ink); min-height: 32px; }
.flt-opt input { width: 18px; height: 18px; accent-color: var(--brand); flex: none; }
.flt-l { flex: 1; }
.flt-n { font-size: .8rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.flt-opt.aus { cursor: default; color: var(--ink-soft); }
.flt-opt.aus .flt-l { text-decoration: line-through; }
.flt-miss, .flt-hint, .flt-empty { font-size: .78rem; color: var(--ink-soft); padding-top: 4px; }
.flt-miss { font-style: italic; }
/* Telefon-Dialog: der Warenkorb-Drawer als Hülle, Inhalt scrollt, Knopf unten fest. */
.flt-dlg-body { flex: 1; overflow-y: auto; padding: 8px 20px; }
.flt-dlg-foot { padding: 14px 20px; border-top: 1px solid var(--line); }
.flt-dlg-foot .pay { width: 100%; }
@media (max-width: 760px) {
  .flt-layout { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .flt-side { display: none; }
  .tb-filter { display: inline-block; }
  .flt-opt { min-height: 44px; }
}
/* Pager (Weiter/Zurück über X-Total-Count + limit/offset) */
.pager { display: flex; justify-content: center; align-items: center; gap: 14px; padding: 0 0 34px; }
.pg-btn { border: 1px solid var(--line); background: var(--card); color: var(--ink);
  border-radius: 999px; padding: 9px 18px; font-size: .9rem; font-weight: 600;
  font-family: inherit; cursor: pointer; }
.pg-btn:hover:not(:disabled) { border-color: var(--brand); color: var(--brand); }
.pg-btn:disabled { opacity: .4; cursor: default; }
.pg-info { font-size: .88rem; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
/* Nur für Screenreader (Live-Region für die Trefferzahl) */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
/* Kits */
.kit-badge { font-size: .74rem; font-weight: 700; border-radius: 999px; padding: 3px 10px; align-self: flex-start; }
.kit-badge.ok { background: var(--brand-soft); color: var(--brand); }
.kit-badge.out { background: var(--line); color: var(--ink-soft); }
.kit-badge.few { background: color-mix(in srgb, var(--accent) 14%, var(--card)); color: var(--accent); }
/* Sets & Bundles (§88): gleiche Karten-Sprache; das Band trägt ein stilles
   Stapel-Motiv (ein Set = mehrere Geräte) — nur Token-Farben, keine Assets. */
.bundles .bd-sub { color: var(--ink-soft); font-size: .92rem; max-width: 62ch; }
.bd-band { aspect-ratio: 16/9; position: relative; overflow: hidden;
  background: var(--brand-soft);
  background-image: radial-gradient(circle at 80% 16%,
    color-mix(in srgb, var(--brand) 14%, transparent) 0 32%, transparent 32%),
    linear-gradient(135deg, transparent 58%, color-mix(in srgb, var(--accent) 12%, transparent) 58%);
  display: grid; place-items: center; }
.bd-band svg { width: 34%; color: var(--brand); opacity: .55; position: relative; }
.bd-band::before, .bd-band::after { content: ""; position: absolute; left: 50%; top: 50%;
  width: 34%; aspect-ratio: 64/40; border-radius: 6px;
  border: 2px solid color-mix(in srgb, var(--brand) 38%, transparent); }
.bd-band::before { transform: translate(-58%, -60%) rotate(-5deg); }
.bd-band::after { transform: translate(-42%, -40%) rotate(4deg); }
.add.bd-cta { text-decoration: none; display: inline-block; text-align: center; }
.kit-comps { font-size: .84rem; color: var(--ink-soft); }
.kit-comps li { margin-left: 18px; }
.kit-note { font-size: .82rem; color: var(--accent); font-weight: 600; margin-top: auto; padding-top: 8px; }
/* #2632 (M3): der Anfrage-Weg unter dem Hinweis — derselbe Knopf wie „Set anfragen“. */
.kit-cta { padding-top: 10px; }
/* Text-Section */
.text-section { padding: 10px 0 26px; max-width: 70ch; }
.text-section p { color: var(--ink-soft); white-space: pre-line; }
/* Downloads-Section (Register §89): is_public-Dokumente als Datei-Links */
.downloads { padding: 10px 0 26px; max-width: 70ch; }
.downloads .dl-note { color: var(--ink-soft); font-size: .9rem; margin-bottom: 8px; }
.dl-list { list-style: none; display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.dl-link { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px;
  background: var(--card); border: 1px solid var(--line); border-radius: calc(var(--radius) * .6);
  padding: 12px 16px; text-decoration: none; color: var(--ink);
  transition: border-color var(--dauer-kurz) var(--kurve), background var(--dauer-kurz) var(--kurve); }
.dl-link:hover { border-color: var(--brand); background: var(--brand-soft); }
.dl-name { font-weight: 600; }
.dl-name::before { content: "\2193"; color: var(--brand); margin-right: 8px; font-weight: 700; }
.dl-meta { font-size: .8rem; color: var(--ink-soft); margin-left: auto; }
/* Cart drawer */
.overlay { position: fixed; inset: 0; background: rgba(0,0,0,.4); opacity: 0; pointer-events: none;
  transition: opacity var(--dauer-mittel) var(--kurve); z-index: 30; }
.overlay.open { opacity: 1; pointer-events: auto; }
.drawer { position: fixed; top: 0; right: 0; height: 100%; width: min(420px, 100%); background: var(--bg);
  transform: translateX(100%);
  transition: transform var(--dauer-mittel) var(--kurve), visibility 0s var(--dauer-mittel); z-index: 40;
  display: flex; flex-direction: column; box-shadow: -12px 0 40px rgba(0,0,0,.15);
  visibility: hidden; /* geschlossen: unsichtbar UND nicht tab-erreichbar */ }
.drawer.open { transform: translateX(0); visibility: visible;
  transition: transform var(--dauer-mittel) var(--kurve); }
.drawer h2 { font-size: 1.1rem; padding: 20px; border-bottom: 1px solid var(--line); display: flex; justify-content: space-between; }
.drawer h2 button { background: none; border: none; font-size: 1.4rem; cursor: pointer; color: var(--ink-soft); line-height: 1; }
.items { flex: 1; overflow-y: auto; padding: 8px 20px; }
/* #2334/AE-1238 (Alexander 14.10.2026, Laptop 1920x1080 bei 150 %: „der Warenkorb statisch … ich kann
   gar nicht runterscrollen … oben diese zwei kleinen Pfeile“): der Bestellbereich (Hinweis, Summen,
   Pflichtfelder, Rechtstexte) ist hoeher als das Fenster geworden — die Liste schrumpfte auf einen
   Streifen, der Bereich ragte unten hinaus, scrollen ging nicht. Jetzt scrollt der GANZE Warenkorb,
   die Ueberschrift mit dem Schliessen-Knopf bleibt oben. Nur #drawer — Rechts- und Filterfenster
   teilen .drawer und haben eigene Bereiche. */
#drawer { overflow-y: auto; overscroll-behavior: contain; }
#drawer > h2 { position: sticky; top: 0; z-index: 1; background: var(--bg); }
#drawer > .items { flex: 1 0 auto; overflow-y: visible; }
#drawer > .checkout { flex: none; }
.line { display: flex; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--line); align-items: center; }
.line .li-t { flex: 1; font-size: .9rem; }
.line .li-p { font-weight: 600; }
.line .rm { background: none; border: none; color: var(--accent); cursor: pointer; font-size: .82rem; font-family: inherit; }
.empty { color: var(--ink-soft); text-align: center; padding: 40px 0; }
.checkout { padding: 20px; border-top: 1px solid var(--line); background: var(--card); }
.total { display: flex; justify-content: space-between; font-size: 1.05rem; font-weight: 700; margin-bottom: 14px; }
.field { margin-bottom: 10px; }
/* #1787 (Review Nr. 43 F2): sichtbare Pflicht-Kennzeichnung (WCAG 3.3.2).
   Farbe ALLEIN traegt die Aussage nicht (1.4.1) — das Zeichen selbst ist das
   Merkmal, die Farbe nur Verstaerkung. --danger ist der Shop-Token dafuer. */
.pflicht-stern { color: var(--danger, #b3261e); font-weight: 700; }
.pflicht-legende { font-size: .75rem; color: var(--muted, #5d747c); margin: 2px 0 10px; }
/* #1787 (Review Nr. 43 F3): Fehlertext AM FELD (WCAG 3.3.1/3.3.3).
   Die Farbe verstaerkt nur — der Text selbst traegt die Aussage (1.4.1), und
   das Feld traegt zusaetzlich aria-invalid. --danger ist der Shop-Token. */
.feld-fehler { font-size: .78rem; color: var(--danger, #b3261e); margin: 4px 0 0;
  font-weight: 600; }
.field input[aria-invalid="true"], .field select[aria-invalid="true"] {
  border-color: var(--danger, #b3261e); }
.field label { font-size: .8rem; font-weight: 600; display: block; margin-bottom: 4px; }
.field input { width: 100%; padding: 11px 12px; border: 1px solid var(--line); border-radius: calc(var(--radius) * .6);
  font-size: .95rem; background: var(--bg); color: var(--ink); font-family: inherit; }
.field input:focus { outline: none; border-color: var(--brand); }
.field select { width: 100%; padding: 11px 12px; border: 1px solid var(--line); border-radius: calc(var(--radius) * .6);
  font-size: .95rem; background: var(--bg); color: var(--ink); font-family: inherit; }
.field select:focus { outline: none; border-color: var(--brand); }
.field-row { display: flex; gap: 8px; }
.field-row .field { flex: 1; }
.pay { width: 100%; background: var(--brand); color: var(--card); border: none; border-radius: calc(var(--radius) * .7);
  padding: 14px; font-weight: 700; font-size: 1rem; cursor: pointer; margin-top: 4px; font-family: inherit; }
.pay:disabled { opacity: .5; cursor: default; }
.note { font-size: .75rem; color: var(--ink-soft); text-align: center; margin-top: 10px; }
/* §167.1 / #1623: der Reservierungs-Hinweis. Bewusst NICHT `.note` (zentriert,
   .75rem, ausgegraut, unter dem Bestellknopf) — das ist die Form für Kleingedrucktes,
   die man nach dem Klicken liest. Diese Aussage muss der Kunde VOR der Entscheidung
   sehen: linksbündig im Lesefluss, volle Textgröße, abgesetzte Fläche. Kein Rot und
   kein Warnsymbol: es ist eine Auskunft, keine Störung. */
.reshint { font-size: .92rem; line-height: 1.45; color: var(--ink-soft); background: var(--brand-soft);
  border-radius: calc(var(--radius) * .5); padding: 10px 12px; margin-bottom: 12px; }
.done { text-align: center; padding: 30px 20px; }
.done .ok { width: 56px; height: 56px; border-radius: 50%; background: var(--brand-soft); color: var(--brand);
  display: grid; place-items: center; font-size: 1.8rem; margin: 0 auto 14px; }
.done h3 { font-size: 1.15rem; margin-bottom: 6px; }
.done p { color: var(--ink-soft); font-size: .92rem; }
.done .total-line { margin-top: 8px; }
.done .num { font-family: ui-monospace, monospace; font-weight: 700; color: var(--ink); }
.toast { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: var(--ink);
  color: var(--bg); padding: 12px 20px; border-radius: 12px; font-size: .9rem; opacity: 0;
  transition: opacity var(--dauer-mittel) var(--kurve); z-index: 50;
  /* #1998 (Telefon-Probe): der LEERE, unsichtbare Toast ist trotzdem ein 40x24-Kasten
     unten mittig ueber allen Drawern (z-index 50 > 40) und fing dort jeden Tipp ab —
     gemessen am Knopf „Geraete anzeigen" des Filter-Dialogs; derselbe Platz traegt im
     Warenkorb „zahlungspflichtig bestellen". Eine Meldung ist nie ein Bedienelement. */
  pointer-events: none; }
.toast.show { opacity: 1; }
footer { border-top: 1px solid var(--line); padding: 28px 0; color: var(--ink-soft); font-size: .82rem; }
.foot { display: flex; flex-direction: column; gap: 12px; }
.foot-contact { display: flex; flex-wrap: wrap; gap: 6px 20px; align-items: baseline; }
.fc-link { color: var(--ink-soft); text-decoration: none; }
.fc-link:hover { color: var(--brand); text-decoration: underline; }
.fc-addr { color: var(--ink-soft); }
.foot-legal { display: flex; flex-wrap: wrap; gap: 6px 18px; }
.fl-link { background: none; border: none; padding: 0; cursor: pointer; font: inherit;
  color: var(--ink-soft); text-decoration: underline; text-underline-offset: 2px; }
.fl-link:hover { color: var(--brand); }
.foot-hints { display: flex; flex-wrap: wrap; gap: 4px 18px; color: var(--ink-soft); font-size: .78rem; }
/* Rechtstext-Panel (nutzt .drawer-Mechanik, links statt rechts, breiter) */
.legal-panel { right: auto; left: 0; width: min(640px, 100%); transform: translateX(-100%);
  box-shadow: 12px 0 40px rgba(0,0,0,.15); }
.legal-panel.open { transform: translateX(0); }
.legal-body { flex: 1; overflow-y: auto; padding: 20px; white-space: pre-wrap; line-height: 1.6;
  font-size: .9rem; color: var(--ink); }
/* Medien-Galerie (§87): zentriertes Modal — Hauptansicht (contain: Produkt
   IMMER ganz sichtbar, Baymard-Großansicht) + Thumbnail-Leiste + Zähler. */
.gallery { position: fixed; inset: 0; z-index: 40; display: none;
  place-items: center; padding: clamp(10px, 3vw, 28px); }
.gallery.open { display: grid; }
.gallery:focus { outline: none; }
.gl-box { background: var(--card); border-radius: var(--radius); overflow: hidden;
  width: min(1100px, 100%); max-height: 100%; display: flex; flex-direction: column;
  box-shadow: 0 24px 80px rgba(0,0,0,.35); }
.gl-head { display: flex; align-items: baseline; gap: 12px; padding: 14px 18px;
  border-bottom: 1px solid var(--line); font-weight: 700; flex: none; }
.gl-count { color: var(--ink-soft); font-size: .85rem; font-weight: 600;
  font-variant-numeric: tabular-nums; }
.gl-close { margin-left: auto; background: none; border: none; font-size: 1.4rem;
  cursor: pointer; color: var(--ink-soft); line-height: 1; }
.gl-stage { position: relative; height: min(70vh, 760px);
  background: color-mix(in srgb, var(--ink) 5%, var(--bg));
  display: grid; place-items: center; overflow: hidden; }
/* #2641 Z2 (Element `bild_zoom`): Klick/Tipp schaltet Ganz ↔ 2,5×, Ziehen verschiebt, Pinch mit zwei
   Fingern. `touch-action: none` NUR auf der Bühne — sonst bliebe der eigene Pinch dem Browser-Zoom
   unterlegen; der Rest der Seite behält den nativen Zoom. Ohne `.motion` kein Übergang. */
.gl-stage.zoombar { touch-action: none; }
.gl-stage.zoombar .gl-img { cursor: zoom-in; transform-origin: 0 0; }
.gl-stage.zoombar.gezoomt .gl-img { cursor: zoom-out; }
.gl-stage.zoombar.zieht .gl-img { cursor: grabbing; }
.motion .gl-stage.zoombar:not(.zieht) .gl-img { transition: transform var(--dauer-kurz) var(--kurve); }
.gl-zoom { position: absolute; right: 12px; bottom: 12px; display: flex; align-items: center; gap: 2px;
  background: var(--card); border: 1px solid var(--line); border-radius: 999px; padding: 2px;
  box-shadow: var(--shadow); }
.gl-zoom-k { min-width: 44px; height: 44px; border-radius: 999px; border: none; background: none;
  color: var(--ink); font: inherit; font-size: 1.25rem; font-weight: 700; line-height: 1; cursor: pointer; }
.gl-zoom-k:hover:not(:disabled) { background: var(--brand-soft); }
.gl-zoom-k:disabled { opacity: .4; cursor: default; }
.gl-zoom-zurueck { font-size: .8rem; padding: 0 12px; }
.gl-zoom-stand { min-width: 46px; text-align: center; font-size: .8rem; color: var(--ink-soft);
  font-variant-numeric: tabular-nums; }
.gl-img, .gl-video { width: 100%; height: 100%; object-fit: contain; display: block; }
.gl-nav { position: absolute; top: 50%; transform: translateY(-50%); width: 40px; height: 40px;
  border-radius: 50%; border: 1px solid var(--line); background: var(--card); color: var(--ink);
  font-size: 1.3rem; line-height: 1; cursor: pointer; display: grid; place-items: center;
  box-shadow: var(--shadow); }
.gl-nav:hover { border-color: var(--brand); color: var(--brand); }
.gl-prev { left: 12px; } .gl-next { right: 12px; }
.gl-strip { display: flex; gap: 8px; padding: 12px 16px; overflow-x: auto; flex: none; }
.gl-strip:empty { display: none; }
.gl-thumb { flex: none; width: 66px; height: 50px; padding: 0; cursor: pointer;
  border: 2px solid var(--line); border-radius: calc(var(--radius) * .4);
  background: var(--brand-soft); overflow: hidden; display: grid; place-items: center; }
.gl-thumb.sel { border-color: var(--brand); }
.gl-thumb img { width: 100%; height: 100%; object-fit: cover; }
.gl-play { color: var(--brand); font-size: .8rem; }
.state { padding: 60px 0; text-align: center; color: var(--ink-soft); }

/* ═══ Section-Bibliothek v2 (Template-Galerie 07/2026) ═══════════════════════
   Alle Looks entstehen NUR aus Tokens (CSS-Variablen) + diesen first-party
   Klassen — kein Inline-Style, keine Fremd-Assets (CSP script/style 'self'). */

/* Gemeinsame Bausteine */
.bleed { width: 100vw; margin-left: calc(50% - 50vw); }
.kicker { font-size: .8rem; font-weight: 700; text-transform: uppercase; letter-spacing: .14em;
  color: var(--accent); margin-bottom: 14px; }
.kicker.rule { display: flex; align-items: center; gap: 14px; }
.kicker.rule::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.badge { display: inline-block; background: var(--brand-soft); color: var(--brand);
  font-size: .78rem; font-weight: 700; border-radius: 999px; padding: 5px 14px;
  letter-spacing: .04em; margin-bottom: 16px; }
.cta-btn { display: inline-block; margin-top: 24px; background: var(--brand); color: var(--card);
  border: none; border-radius: calc(var(--radius) * .7); padding: 13px 26px; font-weight: 700;
  font-size: .95rem; cursor: pointer; font-family: inherit;
  transition: background var(--dauer-kurz) var(--kurve), transform var(--dauer-kurz) var(--kurve); }
.cta-btn:hover { background: var(--brand-dark); transform: translateY(-1px); }
.media-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ph-panel { width: 100%; height: 100%; min-height: 260px; border-radius: var(--radius);
  background: var(--brand-soft);
  background-image: radial-gradient(circle at 78% 18%,
    color-mix(in srgb, var(--brand) 16%, transparent) 0 34%, transparent 34%),
    linear-gradient(135deg, transparent 55%, color-mix(in srgb, var(--accent) 14%, transparent) 55%);
  display: grid; place-items: center; }
.ph-panel svg { width: 42%; color: var(--brand); opacity: .6; }

/* Hero: Split (Text links, Bild/Panel rechts) */
.hero-split { display: grid; grid-template-columns: 1.05fr .95fr; gap: 44px;
  align-items: center; padding: 58px 0 34px; }
.hero-split h1 { font-size: clamp(2rem, 4.6vw, 3.3rem); line-height: 1.04; letter-spacing: -.02em; }
.hero-split p { margin-top: 16px; color: var(--ink-soft); font-size: 1.08rem; max-width: 48ch; }
.hero-split .hs-media { aspect-ratio: 5/4; border-radius: var(--radius); overflow: hidden; }
@media (max-width: 760px) { .hero-split { grid-template-columns: 1fr; gap: 24px; } }

/* Hero: Full-bleed (Bild + Scrim + große Type) */
.hero-fullbleed { position: relative; display: grid; align-items: end;
  min-height: 420px; overflow: hidden; }
.hero-fullbleed.h-tall { min-height: min(72vh, 620px); }
.hero-fullbleed .hfb-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.hero-fullbleed.no-img { background:
  radial-gradient(120% 90% at 85% 10%, color-mix(in srgb, var(--accent) 26%, transparent), transparent 60%),
  linear-gradient(160deg, var(--brand-dark), color-mix(in srgb, var(--brand-dark) 75%, #000)); }
.hero-fullbleed .hfb-scrim { position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(8,10,9,.62), rgba(8,10,9,.12) 60%, transparent); }
.hero-fullbleed .hfb-content { position: relative; width: 100%; padding-top: 72px; padding-bottom: 46px;
  color: #fff; }
.hero-fullbleed .kicker { color: color-mix(in srgb, var(--accent) 82%, #fff); }
.hero-fullbleed h1 { font-size: clamp(2.4rem, 7vw, 4.6rem); line-height: .98; letter-spacing: -.015em;
  max-width: 16ch; }
.hero-fullbleed p { margin-top: 14px; max-width: 52ch; color: rgba(255,255,255,.82); font-size: 1.06rem; }
.hero-fullbleed .cta-btn { background: var(--accent); color: var(--card); }
.hero-fullbleed .cta-btn:hover { background: color-mix(in srgb, var(--accent) 82%, #000); }

/* Hero: Editorial (Type-first — die Headline IST das Bild) */
.hero-editorial { padding: 64px 0 36px; }
.hero-editorial h1 { font-size: clamp(2.6rem, 8.4vw, 5.6rem); line-height: 1.0;
  letter-spacing: -.02em; max-width: 15ch; }
.hero-editorial p { margin-top: 20px; color: var(--ink-soft); font-size: 1.12rem; max-width: 54ch; }
.ed-meta { display: flex; flex-wrap: wrap; gap: 10px 0; margin-top: 26px;
  border-top: 1px solid var(--line); padding-top: 14px;
  font-size: .82rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-soft); }
.ed-meta span { padding-right: 18px; margin-right: 18px; border-right: 1px solid var(--line); }
.ed-meta span:last-child { border-right: none; }

/* Hero: Stat (Kennzahlen, live aus dem Katalog) */
.hero-stat { padding: 60px 0 36px; }
.hero-stat h1 { font-size: clamp(2rem, 4.8vw, 3.2rem); line-height: 1.06; letter-spacing: -.02em;
  max-width: 22ch; }
.hero-stat p { margin-top: 14px; color: var(--ink-soft); font-size: 1.06rem; max-width: 54ch; }
.hero-stat .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 14px; margin-top: 34px; }
.hero-stat .stat { background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 18px 18px 15px; }
.hero-stat .stat-v { display: block; font-family: var(--font-display);
  font-size: clamp(1.7rem, 3.4vw, 2.5rem); font-weight: 700; letter-spacing: -.02em;
  color: var(--brand); font-variant-numeric: tabular-nums; }
.hero-stat .stat span { font-size: .8rem; color: var(--ink-soft); font-weight: 600; }
/* #2625: fester Bezug unter der CO₂-Kennzahl (Angebot, keine verkaufte Menge) + Methodik. */
.hero-stat .stat-bezug { display: block; margin-top: 4px; font-size: .74rem; line-height: 1.4;
  color: var(--ink-soft); }
.hero-stat .stat-methode { margin-top: 14px; }

/* Marquee (Laufband — Bewegung nur mit .motion, sonst statische Zeile) */
.marquee { border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  padding: 13px 0; margin-top: 18px; margin-bottom: 18px; overflow: hidden; }
/* #2582 Nebenbefund (gemessen 31.10.2026, Chromium): hier stand `margin: 18px 0` — die
   Kurzschreibweise setzte margin-left auf 0 und hob damit `.bleed` (frueher im Stylesheet,
   gleiche Spezifitaet) auf. Das Band begann an der Inhaltskante (x = 150 bei 1440 px) und lief
   rechts 150 px aus dem Fenster (vom `overflow-x: clip` verdeckt) — der Pause-Knopf am rechten
   Rand lag damit ausserhalb des Bildes. Nur oben/unten setzen, dann greift `.bleed`. */
.marquee.tone-brand { background: var(--brand); border-color: var(--brand); color: var(--card); }
.marquee.tone-accent { background: var(--accent); border-color: var(--accent); color: var(--card); }
.marquee.tone-ink { background: var(--ink); border-color: var(--ink); color: var(--bg); }
.mq-track { display: flex; width: max-content; }
.mq-group { display: flex; align-items: center; gap: 2.4rem; padding-right: 2.4rem;
  font-family: var(--font-display); font-weight: 700; font-size: 1.02rem;
  letter-spacing: .02em; text-transform: var(--display-case); white-space: nowrap; }
.mq-dot { opacity: .5; font-size: .8em; }
.motion .mq-track { animation: mq-scroll var(--mq-dur, 30s) linear infinite; }
@keyframes mq-scroll { to { transform: translateX(-50%); } }
/* #2585/AE-1452 — WCAG 2.2.2 Pause, Stop, Hide (Stufe A): eine Bewegung, die von selbst startet,
   laenger als 5 s laeuft und neben anderem Inhalt steht, braucht einen Weg, sie anzuhalten.
   Drei Wege: (1) der Knopf (sichtbar, Tastatur, `aria-pressed`), (2) Maus darueber, (3) Fokus
   darin — die beiden letzten sind Bequemlichkeit, der Knopf ist die Pflicht (Telefon kennt kein
   Hover). Bei „Bewegung reduzieren" laeuft nichts (`.motion` fehlt) — dann gibt es auch nichts
   anzuhalten, und der Knopf bleibt aus. Farben: `inherit` aus der Ton-Flaeche des Abschnitts —
   dasselbe Paar wie die Laufschrift selbst (Vertraege „Umkehrflaeche"/„Hauptknopf"/„Akzentflaeche").
   Der Zustand lebt in `shop.js` (`MQ_ANGEHALTEN`) und ueberlebt so jedes Neuzeichnen. */
.marquee { position: relative; }
.marquee.tone-brand { --mq-grund: var(--brand); }
.marquee.tone-accent { --mq-grund: var(--accent); }
.marquee.tone-ink { --mq-grund: var(--ink); }
/* Die Schrift laeuft rechts unter einem Auslauf in der Bandfarbe aus, statt am Knopf
   zerschnitten zu werden (gemessen im Bildschirmfoto 31.10.: „N(Knopf)t"). */
.motion .marquee::after { content: ""; position: absolute; top: 0; right: 0; bottom: 0; width: 84px;
  background-image: linear-gradient(to right, transparent, var(--mq-grund) 55%); pointer-events: none; }
.mq-pause { display: none; }
.motion .mq-pause { display: grid; place-items: center; position: absolute; right: 12px; top: 50%;
  transform: translateY(-50%); width: 36px; height: 36px; padding: 0; cursor: pointer;
  border-radius: 50%; border: 1.5px solid currentColor; background: inherit; color: inherit;
  z-index: 1; }
.mq-pause svg { width: 14px; height: 14px; fill: currentColor; }
/* #2626: Maus und Fokus halten nur an, solange der Knopf unbenutzt ist (`.mq-bedient`, gesetzt in
   shop.js) — danach entscheidet allein der Knopf, sonst bliebe das Band nach „Weiter“ stehen. */
.marquee:not(.mq-bedient):hover .mq-track, .marquee:not(.mq-bedient):focus-within .mq-track,
.marquee.mq-angehalten .mq-track { animation-play-state: paused; }

/* Trust-Bar */
.trust-bar { display: flex; flex-wrap: wrap; gap: 18px 40px; align-items: center;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  padding: 20px 0; margin: 22px 0; }
.tb-item { display: flex; align-items: center; gap: 12px; }
.tb-item .ic { width: 26px; height: 26px; color: var(--brand); flex: none; }
.tb-item b { display: block; font-size: .92rem; letter-spacing: -.01em; }
.tb-item span { display: block; font-size: .78rem; color: var(--ink-soft); }

/* Feature-Grid */
.feature-grid { padding: 8px 0 26px; }
.fg-grid { display: grid; gap: 16px; padding-top: 12px; }
.fg-grid.cols-2 { grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); }
.fg-grid.cols-3 { grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }
.fg-grid.cols-4 { grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr)); }
.fg-item { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 20px; }
.fg-item .ic { width: 28px; height: 28px; color: var(--accent); margin-bottom: 12px; }
.fg-item h3 { font-size: 1rem; letter-spacing: -.01em; margin-bottom: 6px; }
.fg-item p { font-size: .86rem; color: var(--ink-soft); }

/* Lookbook (Editorial-Bildstrecke; ohne Bilder: Token-Paneele) */
.lookbook { padding: 8px 0 26px; }
.lb-grid { display: grid; gap: 16px; padding-top: 12px;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }
.lb-grid.lb-collage { grid-template-columns: repeat(2, 1fr); }
@media (min-width: 761px) {
  .lb-grid.lb-collage { grid-template-columns: 1.6fr 1fr; grid-auto-rows: 1fr; }
  .lb-grid.lb-collage .lb-item:first-child { grid-row: span 2; }
  .lb-grid.lb-collage .lb-item:first-child .lb-media { aspect-ratio: auto; height: 100%; }
}
.lb-item { display: flex; flex-direction: column; }
.lb-media { aspect-ratio: 4/3; border-radius: var(--radius); overflow: hidden; flex: 1; }
.lb-media .ph-panel { min-height: 100%; border-radius: 0; }
.lb-item figcaption { display: flex; align-items: baseline; gap: 10px; padding: 10px 2px 0; }
.lb-tag { font-size: .72rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent); flex: none; }
.lb-cap { font-size: .88rem; color: var(--ink-soft); }

/* Pull-Quote */
.quote-section { padding: 44px 0; max-width: 34ch; margin: 0 auto; text-align: center; }
.quote-section blockquote { font-family: var(--font-display);
  font-size: clamp(1.5rem, 3.6vw, 2.3rem); line-height: 1.22; letter-spacing: -.01em; }
.quote-section .attr { margin-top: 14px; font-size: .85rem; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft); }

/* Banner (Farbfläche/Poster-Block) */
/* #1708: die .tone-accent-Flächen trugen ein FESTES color:#fff — anders als
   ihre .tone-brand-Geschwister, die var(--card) nutzen. In Themes mit HELLEM
   Akzent war das unlesbar (carbon 2.02:1, nachtschicht 2.23:1 gegen Weiß);
   mit var(--card) sind dieselben Paare 8.45:1 bzw. 7.13:1. Ein fester Wert
   im Renderer kann dem Theme nicht folgen — deshalb überall das Token.
   Geprüft wird das Paar seither als Vertrag „Schrift auf der Akzentfläche“
   in app/commerce/shop_kontrast.py. */
.banner { margin: 24px 0; border-radius: var(--radius); overflow: hidden; }
.banner.tone-brand { background: var(--brand); color: var(--card); }
.banner.tone-accent { background: var(--accent); color: var(--card); }
.banner.tone-ink { background: var(--ink); color: var(--bg); }
.bn-inner { padding: clamp(28px, 5vw, 48px); }
.banner h2 { font-size: clamp(1.5rem, 3.6vw, 2.3rem); letter-spacing: -.015em; line-height: 1.1;
  text-transform: var(--display-case); }
.banner p { margin-top: 10px; max-width: 58ch; opacity: .88; font-size: 1rem; }
.banner .cta-btn { background: var(--card); color: var(--ink); margin-top: 18px; }
.banner .cta-btn:hover { background: var(--bg); }

/* Scroll-Reveal (nur mit .motion — reduced motion sieht alles sofort) */
.motion .reveal { opacity: 0; transform: translateY(14px);
  transition: opacity var(--dauer-lang) var(--kurve), transform var(--dauer-lang) var(--kurve); }
.motion .reveal.in { opacity: 1; transform: none; }

/* === #2582/AE-1452 — Bausteine der Mikro-Bewegung ======================================
   Jeder Baustein haengt an ZWEI Schaltern: `.motion` (System erlaubt Bewegung, gesetzt in
   `boot()`) und `bw-*` (Theme-Token, gesetzt in `applyTheme`). Keyframes stehen nur unter
   `.motion` (Waechter `tests/test_shop_bewegung_2582.py`). Nichts davon blinkt: jeder
   Baustein laeuft genau einmal je Handlung, der Schimmer ist ein weicher Lauf < 1 Hz. */

/* Haken: das bleibende „Im Warenkorb ✓" springt einmal auf — die Bestaetigung BLEIBT
   (NN/g: keine sich selbst ausblendende Rueckmeldung); der Toast bleibt die Ansage. */
.haken { display: inline-block; }
.motion.bw-haken .haken-neu .haken { animation: bw-haken var(--dauer-mittel) var(--kurve-aus) both; }
@keyframes bw-haken {
  from { transform: scale(.3); opacity: 0; }
  60% { transform: scale(1.25); opacity: 1; }
  to { transform: none; opacity: 1; }
}
/* Puls: der Zaehler am Warenkorb waechst einmal kurz — „da ist es hingegangen". */
.motion.bw-puls .cart-btn .count.puls { animation: bw-puls var(--dauer-mittel) var(--kurve-aus); }
@keyframes bw-puls { 50% { transform: scale(1.35); } }

/* Hover-Verlauf der Kaufknoepfe aus den Theme-Farben: `brand_dark` -> 40 % Richtung `accent`.
   Als eigene Ebene (::before), damit er weich einblendet — ein Verlauf selbst ist nicht
   animierbar. Ohne Unterstuetzung fuer `in srgb` verwirft der Browser die Deklaration, und der
   Knopf zeigt wie bisher `brand_dark` (`.add:hover`). Kontrast: Verlaufsvertrag in
   `app/commerce/shop_kontrast.py` (der Anteil 40 % steht dort ein zweites Mal — Waechter). */
.add, .cta-btn { position: relative; isolation: isolate; }
.bw-verlauf .add:not(:disabled)::before,
.bw-verlauf .cta-btn:not(.banner *, .hero-fullbleed *)::before {
  content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background-image: linear-gradient(in srgb 135deg, var(--brand-dark),
    color-mix(in srgb, var(--brand-dark) 60%, var(--accent)));
  opacity: 0; transition: opacity var(--dauer-kurz) var(--kurve); }
.bw-verlauf .add:not(:disabled):hover::before,
.bw-verlauf .cta-btn:not(.banner *, .hero-fullbleed *):hover::before { opacity: 1; }

/* Schimmer beim Laden: die Platzhalter der ausgelieferten Seite (#2658, unten) und die Karten
   eines neu suchenden Katalogs. Ohne `.motion` stehen die Platzhalter still (graue Flaechen). */
.motion .lade-schimmer .lade-f,
.motion.bw-schimmer .catalog-body.busy .card .thumb::after {
  background-image: linear-gradient(100deg, transparent 30%,
    color-mix(in srgb, var(--card) 65%, transparent) 50%, transparent 70%);
  background-size: 250% 100%; animation: bw-schimmer var(--dauer-schleife) linear infinite; }
.motion.bw-schimmer .catalog-body.busy .card .thumb::after {
  content: ""; position: absolute; inset: 0; }
@keyframes bw-schimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }

/* === #2658/AE-1499 — Ladefolge ohne Zwischenzustände (Register §500) =========================
   Gemessen (Playwright, `layout-shift`): der Fuß stand direkt unter „Shop wird geladen …“ im
   Bild und sprang zweimal (Platzhalter, Inhalt) — CLS 0,19 (390 px) / 0,33 (1440 px).
   (1) `main` hält mindestens den Rest der Bildschirmhöhe unter der Kopfleiste (62 px + Linie):
       der Fuß beginnt damit an der Unterkante, und jeder Tausch darüber verschiebt nichts
       Sichtbares (web.dev „Optimize CLS“: Platz im ersten Layout reservieren). Der Fuß bleibt
       im Dokument — Widerrufsbutton und Gewährleistungshinweis sind auch ohne JS erreichbar.
   (2) Platzhalter liefert der Server in der Folge der veröffentlichten Abschnitte
       (`shop_html.platzhalter_html`): Titel, Leiste, Karten, Textblock. Ohne Schimmer
       (`loading_shimmer` aus) bleiben die Flächen unsichtbar, der Platz bleibt. */
#sections { min-height: calc(100vh - 63px); min-height: calc(100svh - 63px); }
.lade-folge { padding-bottom: 34px; }
.lade-f { display: block; border-radius: 6px; background: var(--line); }
.lade-folge:not(.lade-schimmer) .lade-f { background: transparent; }
.lade-folge:not(.lade-schimmer) .lade-karte { border-color: transparent; }
.lade-titel { display: flex; flex-direction: column; gap: 14px; padding: 60px 0 36px; }
.lade-kicker { width: 14rem; max-width: 45%; height: .8rem; }
.lade-h1 { width: 62%; height: clamp(2.2rem, 6.4vw, 4.6rem); }
.lade-text { width: 48%; height: .95rem; }
.lade-kurz { width: 30%; }
.lade-leiste { padding: 18px 0; }
.lade-leiste .lade-f { height: 22px; }
.lade-h2 { width: 16rem; max-width: 60%; height: 1.5rem; margin: 26px 0 4px; }
.lade-karte { display: flex; flex-direction: column; gap: 10px; padding-bottom: 16px; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--radius); }
.lade-karte .lade-bild { aspect-ratio: 16/9; border-radius: 0; }
.lade-karte .lade-text { width: auto; margin: 0 16px; }
.lade-karte .lade-kurz { width: 45%; }
.lade-block { display: flex; flex-direction: column; gap: 12px; padding: 10px 0 24px; }
@media (max-width: 640px) {
  .lade-h1, .lade-text { width: 88%; }
  .lade-kurz { width: 55%; }
}

/* === #2609/AE-1463 — Saisonale Bausteine (Schnee, Schlitten, Lichterkette) =====================
   Gebaut in `shop.js` (`applySaison`), nur wenn das Theme sie einschaltet und der Zeitraum gilt.
   Alle Ebenen liegen AUSSERHALB des Flusses (fest/absolut) — kein Layout-Sprung — und fangen
   keinen Klick (`pointer-events: none`). Farben nur aus Theme-Token (Farbnaht-Wächter). Jede
   Bewegung haengt an `.motion` UND ihrem Schalter; der Knopf in der Kopfleiste haelt alles an
   (`.saison-angehalten`, WCAG 2.2.2). */

/* Schnee: Canvas HINTER dem Inhalt. `z-index: -1` im Wurzel-Stapelkontext malt ueber dem
   Seitengrund, aber unter jeder Flaeche (Karten, Banner, Knoepfe) — Text auf Flaechen bleibt frei. */
.saison-schnee { position: fixed; inset: 0; width: 100%; height: 100%; z-index: -1;
  pointer-events: none; }

/* Schlitten: eigener „Himmel“ unter der Kopfleiste, scrollt mit der Seite weg. Er haengt an einem
   Anker OHNE Hoehe direkt hinter <header> (shop.js `himmelBauen`) — also genau unter der Leiste,
   ohne etwas zu verschieben. #2658/AE-1499: HINTER dem Inhalt (`z-index: -1` wie der Schnee) —
   vorher (z 15) flog er ueber Kicker und Titel des ersten Abschnitts; jetzt liegen Text, Karten
   und Knoepfe darueber, er bleibt sichtbar, wo der Seitengrund frei ist. `overflow: hidden`
   haelt den Flug innerhalb der Fensterbreite — kein Querlauf. */
.saison-anker { position: relative; height: 0; }
.saison-himmel { position: absolute; left: 0; right: 0; top: 0; height: 200px; overflow: hidden;
  pointer-events: none; z-index: -1; }
.schlitten { position: absolute; left: 0; top: 34px; width: 320px; visibility: hidden;
  transform: translateX(calc(-100% - 20px)); color: var(--ink); }
.schlitten svg { display: block; width: 100%; height: auto; overflow: visible; }
.schlitten.fliegt { visibility: visible; }
.sl-korb { fill: var(--accent); }
.sl-mantel { fill: var(--accent); }
.sl-sack { fill: var(--brand-dark); }
.sl-gesicht, .sl-bommel { fill: var(--ink); }
.sl-kufe, .sl-leine { fill: none; stroke: var(--brand); stroke-width: 2.2; stroke-linecap: round; }
.sl-leine { stroke-width: 1.1; opacity: .8; }
.sl-stern { fill: var(--brand); }
.sl-stern.s2 { opacity: .7; } .sl-stern.s3 { opacity: .45; }
.rt-koerper { fill: currentColor; }
.rt-linie, .rt-geweih { fill: none; stroke: currentColor; stroke-width: 2.3; stroke-linecap: round;
  stroke-linejoin: round; }
.rt-geweih { stroke-width: 1.5; }
.rt-nase { fill: var(--accent); filter: drop-shadow(0 0 3px var(--accent)); }
.rt { transform-box: fill-box; }
.motion.bw-schlitten .schlitten.fliegt { animation: saison-flug var(--dauer-flug) linear both; }
.motion.bw-schlitten .schlitten.fliegt .rt { animation: saison-galopp var(--dauer-galopp) ease-in-out infinite; }
.motion.bw-schlitten .schlitten.fliegt .rt-2 { animation-delay: calc(var(--dauer-galopp) * -.5); }
@keyframes saison-flug {
  from { transform: translateX(calc(-100% - 20px)) translateY(46px) rotate(-4deg); }
  45% { transform: translateX(42vw) translateY(4px) rotate(-1deg); }
  to { transform: translateX(calc(100vw + 20px)) translateY(18px) rotate(3deg); }
}
@keyframes saison-galopp { 50% { transform: translateY(-3px); } }

/* Lichterkette: haengt an der Unterkante der klebenden Kopfleiste (wandert mit ihr), ueberdeckt
   den Inhalt darunter um ~2 Zeilenhoehen ohne Klick zu fangen. Vier Gruppen funkeln versetzt,
   langsam (< 0,4 Hz, weit unter der Blitzgrenze von WCAG 2.3.1) und weich. */
.lichterkette { position: absolute; left: 0; right: 0; top: 100%; height: 28px; overflow: hidden;
  pointer-events: none; }
.lichterkette svg { display: block; width: 100%; height: 26px; overflow: visible; }
.lk-draht { fill: none; stroke: var(--ink-soft); stroke-width: 1.2; opacity: .7; }
.lk-fassung { fill: var(--ink-soft); }
.lk-g1 { color: var(--accent); } .lk-g2 { color: var(--brand); }
.lk-g3 { color: var(--ink-soft); } .lk-g4 { color: var(--brand-dark); }
.lk-birne { fill: currentColor; filter: drop-shadow(0 0 3px currentColor); }
.motion.bw-lichter .lk-g { animation: saison-funkeln var(--dauer-funkeln) ease-in-out infinite; }
.motion.bw-lichter .lk-g2 { animation-delay: calc(var(--dauer-funkeln) * -.25); }
.motion.bw-lichter .lk-g3 { animation-delay: calc(var(--dauer-funkeln) * -.5); }
.motion.bw-lichter .lk-g4 { animation-delay: calc(var(--dauer-funkeln) * -.75); }
@keyframes saison-funkeln { 50% { opacity: .38; } }

/* Der Halt (Knopf) — Schlitten bleibt in der Luft stehen, Lichter im jeweiligen Glanz. Die
   Selektoren tragen `.motion` mit, damit sie die Kurzschreibweise `animation` oben (die den
   Abspielzustand auf „running“ zuruecksetzt) bei gleicher Spezifitaet als spaetere Regel schlagen —
   gemessen 31.10.: ohne `.motion` blieb alles „running“. */
.motion.saison-angehalten .schlitten.fliegt, .motion.saison-angehalten .schlitten.fliegt .rt,
.motion.saison-angehalten .lk-g { animation-play-state: paused; }

/* Der Knopf in der Kopfleiste: Schneeflocke mit Pause-/Weiter-Zeichen. Er steht direkt vor dem
   Warenkorb und nimmt dessen `margin-left: auto` — beide bleiben rechts zusammen. */
.saison-pause { margin-left: auto; flex: none; display: inline-flex; align-items: center; gap: 8px;
  height: 40px; padding: 0 14px 0 11px; border-radius: 999px; border: 1px solid var(--line);
  background: var(--card); color: var(--ink); cursor: pointer; font: inherit; font-size: .88rem;
  font-weight: 600; }
.saison-pause + .cart-btn { margin-left: 0; }
.saison-pause:hover { border-color: var(--brand); }
.saison-pause svg { width: 19px; height: 19px; }
.saison-pause .sp-flocke { fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round;
  stroke-linejoin: round; }
.saison-pause .sp-grund { fill: var(--card); }
.saison-pause .sp-zeichen { fill: currentColor; }
.saison-pause[aria-pressed="true"] { border-color: var(--ink-soft); }

@media (max-width: 640px) {
  .saison-himmel { height: 150px; }
  .schlitten { width: 220px; top: 26px; }
  /* Telefon: nur das Zeichen, dafuer die volle Tippflaeche (WCAG 2.5.8 / §217). */
  .saison-pause { width: 44px; height: 44px; padding: 0; justify-content: center; }
  .saison-pause .sp-text { display: none; }
}
@media print {
  .saison-schnee, .saison-himmel, .lichterkette, .saison-pause { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  /* #2582: der Klassen-Schalter — jede Dauer haengt an einer Stufe, also steht mit diesen
     Zeilen JEDE Bewegung still (auch Drawer-Gleiten und Knopf-Anheben, die bis #2582
     trotz „Bewegung reduzieren" liefen). Die Regeln darunter bleiben als zweite Sicherung. */
  :root { --dauer-kurz: 0s; --dauer-mittel: 0s; --dauer-ruhig: 0s; --dauer-lang: 0s;
    --dauer-schleife: 0s; --dauer-flug: 0s; --dauer-galopp: 0s; --dauer-funkeln: 0s; }
  .mq-track { animation: none !important; }
  .reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
  .cta-btn:hover { transform: none; }
}

/* Widerrufsbutton (§356a BGB, seit 19.06.2026 Pflicht): dauerhaft sichtbar +
   eindeutig beschriftet; footer-dezent, aber klar als Button erkennbar. Die
   Bestätigungsseite recycelt das legal-panel (Drawer) — white-space zurück auf
   normal, weil legal-body pre-wrap ist (für Formular-Markup falsch). */
.foot-widerruf { margin-top: 10px; }
.widerruf-trigger { background: none; border: 1px solid var(--line); color: var(--ink-soft);
  padding: 8px 14px; border-radius: calc(var(--radius) * .6); font: inherit; cursor: pointer; }
.widerruf-trigger:hover { border-color: var(--brand); color: var(--brand); }
#widerruf-body { white-space: normal; }
/* #2625/AE-1479: harmonisierte Mitteilung über das gesetzliche Gewährleistungsrecht (DVO (EU)
   2025/1960 Anhang I). Die Grafik bleibt unverändert und ungeschnitten (nur skaliert, nie
   beschnitten, nie gefiltert); keine Rundung, kein Schatten, kein Rahmen AUF dem Bild. Der Fuß-
   Knopf erbt `.widerruf-trigger` (gleiche Stufe wie der Widerrufsbutton). */
.foot-widerruf { display: flex; flex-wrap: wrap; gap: 8px; }
#gewaehrleistung-body { white-space: normal; }
.gw { margin: 0; }
.gw-bild { display: block; width: 100%; max-width: 640px; height: auto; }
.gw figcaption { margin-top: 8px; font-size: .82rem; line-height: 1.5; color: var(--ink-soft); }
.gw-text > summary { cursor: pointer; font-weight: 600; color: var(--ink); }
.gw-text p { margin: 8px 0 0; color: var(--ink); }
.gw-quelle { margin: 8px 0 0; }
.gw-quelle a { color: var(--ink-soft); }
.gw-kasse { margin: 14px 0; padding-top: 12px; border-top: 1px solid var(--line); }
.gw-kasse h3 { margin: 0 0 8px; font-size: .95rem; color: var(--ink); }
.gw-verweis { margin: 10px 0 0; font-size: .85rem; }
.gw-fehlt { color: var(--ink); font-size: .9rem; }
#widerruf-body .wd-intro { color: var(--ink-soft); font-size: .9rem; line-height: 1.55; margin-bottom: 16px; }
#widerruf-body .wd-frist { color: var(--ink); font-size: .9rem; font-weight: 600; line-height: 1.5;
  padding: 9px 12px; border-left: 3px solid var(--brand); border-radius: 6px;
  background: rgba(0,0,0,.03); margin-bottom: 16px; }
#widerruf-body .opt { font-weight: 400; color: var(--ink-soft); }
#widerruf-body textarea { width: 100%; padding: 11px 12px; border: 1px solid var(--line);
  border-radius: calc(var(--radius) * .6); font: inherit; background: var(--card); color: var(--ink);
  box-sizing: border-box; resize: vertical; }
#widerruf-body textarea:focus { outline: none; border-color: var(--brand); }

/* CO₂-Methodik-Aufklapper (§145/#180) — EmpCo-Nachweis dort, wo die Zahl steht.
   Bewusst KEIN Overlay/Tooltip: bleibt im Fluss, verdeckt nichts, Touch-tauglich. */
.co2-method { border: 1px solid var(--line); border-radius: 12px; background: var(--card);
  margin: 0 0 16px; }
.co2-method > summary { cursor: pointer; padding: 12px 16px; font-size: .9rem; font-weight: 600;
  color: var(--brand); list-style: none; }
.co2-method > summary::-webkit-details-marker { display: none; }
.co2-method > summary::before { content: "＋ "; font-weight: 400; }
.co2-method[open] > summary::before { content: "− "; }
/* #1717: hier stand ein EIGENER Fokusring in `--brand`. Er ist ersatzlos
   gestrichen: brand/card traegt die C40-Garantie nicht (gemessen 3,91:1 bis
   21,00:1, also nicht durchgaengig >= 9), und die globale `:focus-visible`-Regel
   deckt dieses Element ohnehin. Ein zweiter Fokusindikator ist ein zweites
   Versprechen, das dieselbe Pruefung noch einmal bestehen muesste. */
.co2-method .m-body { padding: 0 16px 14px; font-size: .84rem; line-height: 1.55; }
.co2-method .m-lead { font-weight: 600; margin: 0 0 10px; }
.co2-method dl { margin: 0; display: grid; gap: 6px; }
.co2-method .m-row { display: grid; grid-template-columns: 8.5rem 1fr; gap: 10px; }
.co2-method dt { color: var(--ink-soft); }
.co2-method dd { margin: 0; }
/* Der Disclaimer ist ein FEATURE, kein Kleingedrucktes: unter EmpCo schützt die
   offen benannte Unsicherheit mehr als eine glattere Zahl ohne Grundlage. */
.co2-method .m-disc { margin: 12px 0 0; padding: 10px 12px; border-radius: 8px;
  background: var(--bg); border: 1px solid var(--line); }
@media (max-width: 560px) { .co2-method .m-row { grid-template-columns: 1fr; gap: 2px; } }
/* #2639/AE-1495 — die wählbaren Darstellungen der Methodik (Element `co2_angabe`): `inline` (offen,
   ohne Aufklapp-Zeile), `fenster` (ein Verweis öffnet den Überlagerer `co2`). Dieselben Farben wie der
   Aufklapper oben — keine neue Farbpaarung (Kontrast-Vertrag `shop_kontrast.py`). */
.co2-method-offen { padding-top: 12px; }
.co2-method-titel { margin: 0 16px 10px; font-size: .9rem; font-weight: 600; color: var(--brand); }
.co2-methodik-link { margin: 0 0 16px; font-size: .85rem; }
#co2-body { white-space: normal; padding: 0; }
#co2-body .co2-method { border: 0; margin: 0; }

/* ── Seiten-Baukasten (AE-161..163): Header-Nav + neue Sections ────────────── */
/* #2205/AE-1153 (Prüfer #2205 B F2): vorher `flex-wrap: wrap; overflow: hidden` in der festen
   62-px-Leiste — die zweite Zeile wurde abgeschnitten, am Telefon standen „Sta“ und „Ve“ unter dem
   Warenkorb. Jetzt EINE Zeile, waagerecht scrollbar (wie `.gl-strip`): kein Eintrag wird je
   abgeschnitten, jeder ist erreichbar. Am Telefon eigene Zeile unter Logo und Warenkorb (unten). */
.page-nav { display: flex; align-items: center; gap: 2px; flex-wrap: nowrap; min-width: 0;
  overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: thin; }
.pn-link { color: var(--ink-soft); text-decoration: none; font-size: .92rem; font-weight: 600;
  padding: 6px 11px; border-radius: 999px; white-space: nowrap; flex: none; }
@media (max-width: 640px) {
  /* Einzeilig bleibt die Leiste 62 px hoch (min-height); mit Seiten wächst sie um die Nav-Zeile. */
  .bar { flex-wrap: wrap; height: auto; min-height: 62px; row-gap: 4px; padding-top: 8px; padding-bottom: 8px; }
  .page-nav:not(:empty) { order: 3; flex-basis: 100%; }
  /* #2658/AE-1499: hat der Shop Seiten in der Navigation, steht die zweite Zeile schon im
     ersten Bild (der Server setzt `.mit-seiten`) — vorher wuchs die Leiste erst mit `/pages`
     um 36 px und schob den Inhalt (gemessen CLS 0,04). Höhe = eine `.pn-link`-Zeile. */
  .bar.mit-seiten .page-nav { order: 3; flex-basis: 100%; min-height: calc(.92rem * 1.5 + 12px); }
  /* Die erste Zeile hat von Anfang an die Höhe des Winterdeko-Knopfs (44 px, kommt per JS):
     vorher wuchs sie beim Einsetzen um 4 px und schob die Nav-Zeile darunter (CLS 0,01).
     Zugleich die volle Tippfläche für den Warenkorb (WCAG 2.5.8 / §217). */
  .cart-btn { min-height: 44px; }
  /* Logo und Warenkorb bleiben in EINER Zeile: die Marke nimmt den Rest (Basis 0, sonst bräche
     ein Logo-Bild + Name den Warenkorb in eine eigene Zeile — gemessen 390 px: 243 + 136 > 350),
     ein langer Shop-Name wird gekürzt statt umzubrechen. */
  .logo { flex: 1 1 0; min-width: 0; }
  #logo-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .cart-btn { flex: none; }
}
.pn-link:hover { color: var(--ink); background: var(--brand-soft); }
.pn-link.active { color: var(--brand-dark); background: var(--brand-soft); }

.columns-section { padding: 10px 0 26px; }
.col-grid { display: grid; gap: 18px; padding-top: 10px; }
.col-grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.col-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.col-grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
.col-item h3 { font-size: 1.02rem; margin-bottom: 6px; }
.col-item p { color: var(--ink-soft); white-space: pre-line; font-size: .95rem; }
@media (max-width: 860px) { .col-grid.cols-3, .col-grid.cols-4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 560px) { .col-grid { grid-template-columns: 1fr !important; } }

.faq-section { padding: 10px 0 26px; max-width: 70ch; }
.faq-list { display: flex; flex-direction: column; gap: 8px; padding-top: 10px; }
.faq-item { background: var(--card); border: 1px solid var(--line);
  border-radius: calc(var(--radius) * .6); padding: 0 14px; }
.faq-item summary { cursor: pointer; font-weight: 600; padding: 12px 0; list-style-position: inside; }
.faq-item[open] summary { border-bottom: 1px solid var(--line); }
.faq-item p { color: var(--ink-soft); white-space: pre-line; padding: 10px 0 14px; }

/* ── Artikel-Detailansicht (#433, Amazon-Muster) ── */
.t-link { cursor: pointer; }
.t-link:hover, .t-link:focus-visible { text-decoration: underline; text-underline-offset: 3px; }
.detail { position: fixed; inset: 0; z-index: 40; display: none; place-items: center;
  padding: 24px; background: rgba(20, 26, 22, .45); }
.detail.open { display: grid; }
.detail:focus { outline: none; }
/* #2617/AE-1472: der Schließen-Knopf ist eine eigene, runde Fläche (als nackte Glyphe in `--ink-soft`
   auf dem Schleier kaum zu sehen; Touch hat kein Escape).
   #2641/AE-1501 (§497 „das X ganz weit oben“): er sitzt an der ECKE DER KARTE — `.dt-rahmen` trägt
   ihn außerhalb der scrollenden `.dt-grid`, halb über der Ecke. Vorher hing er an der Bildschirm-Ecke
   (`.detail` = ganzer Bildschirm): gemessen 262 px neben der Karte bei 1440 px, 138 px darüber am
   Tablet hochkant. `.detail` hat ringsum 24 px Rand — der Knopf (−14 px) bleibt ganz im Bild. */
.dt-rahmen { position: relative; width: 100%; max-width: 880px; }
.dt-rahmen > .dt-close { position: absolute; top: -14px; right: -14px; z-index: 2; width: 44px;
  height: 44px; border-radius: 999px; display: grid; place-items: center; margin: 0;
  background: var(--card); color: var(--ink); border: 1px solid var(--line);
  box-shadow: var(--shadow); font-size: 1.6rem; line-height: 1; }
/* ⛔ `minmax(0, …)` statt nacktem `1fr` — GEMESSEN im Browser am 05.09.2026 (#1609),
   nicht vorsorglich gesetzt. Ein `1fr`-Track hat `min-width: auto` und kann deshalb
   NICHT unter seinen Mindestinhalt schrumpfen. Der Platzhalter traegt `.ph-panel`
   (`height: 100%`) UND `.dt-ph` (`aspect-ratio: 4/3`): seine Hoehe folgt der Zeile,
   also der TEXTSPALTE, und die Seitenverhaeltnis-Regel rechnet daraus seine Breite.
   Je laenger der Text, desto breiter das Bild — und desto schmaler der Text.
   Gemessen mit dem neuen GPSR-Block: Spalten `1058,75px 212,78px` in einem 880 px
   breiten Gitter, die Werte umbrachen auf 179 px. Der Fehler lag latent im Bestand;
   der laengere Inhalt hat ihn nur sichtbar gemacht. `height: auto` + `align-self:
   start` nehmen der Kachel zusaetzlich den Hoehen-Antrieb: das Verhaeltnis rechnet
   jetzt aus der BREITE, wie es gemeint war. */
.dt-grid { position: relative; background: var(--card); border-radius: var(--radius);
  max-width: 880px; width: 100%; max-height: min(88vh, 720px); overflow-y: auto;
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
  gap: 24px; padding: 26px; }
.dt-ph { aspect-ratio: 4 / 3; border-radius: calc(var(--radius) * .6);
  height: auto; align-self: start;
  display: grid; place-items: center; }
.dt-ph svg { width: 40%; max-height: 65%; color: var(--brand); opacity: .55; }
/* #2641 Z1 (#2640 §1): `contain` statt `cover` — vorher fehlten bei Hochformat-Fotos Ober- und
   Unterkante, bei Gebrauchtware genau die Kanten, an denen Dellen sitzen. Der Rand bekommt die
   ruhige Bühnenfläche der Galerie. Zeiger `zoom-in` nur mit Zoom dahinter (`html.zoom-an`). */
.dt-gal { position: relative; }
.dt-gal .dt-main { width: 100%; aspect-ratio: 4 / 3; object-fit: contain;
  background: color-mix(in srgb, var(--ink) 5%, var(--bg));
  border-radius: calc(var(--radius) * .6); cursor: pointer; display: block; }
html.zoom-an .dt-gal .dt-main { cursor: zoom-in; }
.dt-zoom-hinweis { margin: 6px 0 0; font-size: .8rem; color: var(--ink-soft); }
/* Hover-Lupe (Option `klick_zoom_lupe`): IN der Bildfläche, fängt keinen Klick ab. */
.dt-lupe { position: absolute; width: 150px; height: 150px; border-radius: 50%; pointer-events: none;
  border: 2px solid var(--card); box-shadow: var(--shadow); background-repeat: no-repeat;
  background-color: var(--card); z-index: 3; }
.dt-lupe[hidden] { display: none; }
.dt-thumbs { display: flex; gap: 8px; margin-top: 8px; }
.dt-thumbs img { width: 56px; height: 56px; object-fit: cover; cursor: pointer;
  border-radius: calc(var(--radius) * .4); border: 1px solid var(--line); }
/* #2617: Vorschaubilder sind Knöpfe (wechseln das große Bild); das gewählte trägt einen Rahmen in
   der Markenfarbe. Die Bildregel darüber bleibt für die Maße, der Zeiger wird zur Hand. */
.dt-thumb { padding: 0; border: 0; background: none; border-radius: calc(var(--radius) * .4);
  cursor: pointer; line-height: 0; }
.dt-thumb img { cursor: pointer; }
.dt-thumb.sel img { border: 2px solid var(--brand); }
.dt-info h2 { margin: 4px 0 8px; font-size: 1.35rem; line-height: 1.25; }
.dt-badges { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.dt-badges .grade { position: static; }
/* #2625: Produktseite — Angabe + Bezug offen, die Methodik als Aufklapper darunter (Muster GPSR). */
.dt-co2 { margin: 0 0 12px; }
.dt-co2 .co2-zeile { margin: 0; font-size: .9rem; color: var(--ink); }
.co2-bezug { margin: 2px 0 10px; font-size: .8rem; line-height: 1.45; color: var(--ink-soft); }
/* #2632 (N7): Fußnote + Methodik am Ende des Rasters. */
.co2-fuss { margin-top: 20px; }
.dt-co2 .co2-method { margin: 0; }
.dt-price { font-size: 1.3rem; margin-bottom: 8px; }
.dt-lead { color: var(--ink-soft); font-size: .92rem; margin: 0 0 12px; }
/* #1808: der Pflichthinweis zur Differenzbesteuerung (§ 14a Abs. 6 UStG)
   direkt unter dem Preis — dieselbe Farbe/Groesse wie der Lead-Satz, damit
   er als Erlaeuterung des Preises gelesen wird und nicht als Werbezeile. */
.dt-vat { color: var(--ink-soft); font-size: .82rem; margin: 4px 0 12px; }
.dt-specs { display: grid; grid-template-columns: 1fr; gap: 0; margin: 0 0 14px;
  border-top: 1px solid var(--line); }
.dt-specs .dt-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
  padding: 7px 0; border-bottom: 1px solid var(--line); font-size: .9rem; }
.dt-specs dt { color: var(--ink-soft); }
.dt-specs dd { margin: 0; font-weight: 600; }
.dt-docs { margin-bottom: 14px; }
.dt-docs h4 { margin: 0 0 8px; font-size: .95rem; }
.dt-buy { max-width: 320px; }
.dt-hint { color: var(--ink-soft); font-size: .9rem; }
/* #2632 (M4): sagt, wofür die Merkmalstabelle vor der Wahl gilt — mit Abstand zum Gewährleistungs-Link. */
.dt-specs-hinweis { margin: 16px 0 6px; font-size: .85rem; }
/* #2617/AE-1472: Varianten-Wahl auf der Produktseite — dieselben Pillen wie auf der Karte, mit
   etwas mehr Luft (hier ist die Wahl die Hauptsache, nicht ein Nebenfeld der Karte). */
.dt-axes { display: grid; gap: 10px; margin: 4px 0 14px; }
.dt-axes .vbtn { padding: 7px 14px; font-size: .88rem; min-height: 36px; }
/* #2617: das Kartenbild führt zur Produktseite — Zeiger statt Lupe (die Lupe gehört der Galerie). */
.thumb[data-action="detail"] { cursor: pointer; }
.bd-teile { margin: 2px 0 10px; }
@media (max-width: 720px) {
  /* #2312/AE-1224: auch HIER `minmax(0, 1fr)` — die Regel oben (#1609) galt nur dem
     Zweispalter; das Telefon-Raster stand weiter auf nacktem `1fr` und wuchs auf den
     Mindestinhalt (gemessen bei 390 px: Spalte 346,66 px in 290 px Inhaltsbreite, Text und
     Bild rechts abgeschnitten). Waechter: `test_shop_storefront_2312.py`. */
  .dt-grid { grid-template-columns: minmax(0, 1fr); max-height: 92vh; }
}
/* #691 S2: Kenntnisnahme-Zeile im Checkout. Die Links darin sind .fl-link
   (gleiche Optik wie im Footer); die Zeile selbst ist ein Label, damit der
   Klick auf den Text die Checkbox trifft — ausser auf den Links selbst. */
.terms { display: flex; gap: .5rem; align-items: flex-start; margin: .75rem 0 .5rem;
         font-size: .9rem; line-height: 1.45; cursor: pointer; }
.terms input { margin-top: .2rem; width: 1.1rem; height: 1.1rem; flex: 0 0 auto; cursor: pointer; }
.terms .fl-link { padding: 0; font-size: inherit; }
/* #691 S4: Versandzeile im Warenkorb. .warn traegt den GRUND, wenn kein Tarif
   greift — bewusst sichtbar statt stiller Null (PAngV: Gesamtpreis vor dem Klick). */
.total.ship { font-weight: 400; font-size: .95rem; }
/* #1717: `#b45309` war fest verdrahtet und konnte dem Theme nicht folgen —
   auf `nachtschicht`/`carbon` blieben 3,17 bzw. 3,40 gegen `--card`, unter der
   Textschwelle 4,5 fuer diesen Kleintext. `--ink` ist als „Fliesstext auf der
   Karte" ein erzwungener 4,5-Vertrag; das Gewicht traegt die Betonung, nicht
   die Farbe (WCAG 1.4.1: Farbe darf nie der einzige Traeger sein). */
.total.ship.warn span:last-child { color: var(--ink); font-weight: 600;
  text-align: right; max-width: 60%; }
.total-final { border-top: 1px solid rgba(0,0,0,.12); padding-top: .5rem; margin-top: .25rem; }
/* #2657/AE-1505: Rabattcode im Warenkorb. Zugeklappt ist es EIN Verweis („Rabattcode“) — wenig
   Fläche, kein Aufruf zur Codesuche (Recherche #2643 §4.4). Knopf und Feld ≥ 44 px (WCAG 2.5.8
   reicht 24 px; die Kasse folgt dem 44-px-Maß der übrigen Felder). Der Rabatt trägt kein Rot:
   die Zeile ist eine Gutschrift, keine Warnung; das Minuszeichen trägt die Bedeutung. */
.code-feld { margin: 0 0 12px; font-size: .92rem; }
.code-feld > summary { cursor: pointer; font-weight: 600; color: var(--ink); min-height: 44px;
  display: flex; align-items: center; }
.code-feld[open] > summary { margin-bottom: 6px; }
/* Der Aufklapper braucht ein sichtbares Zeichen, dass er aufgeht (im Browser gesehen: ohne Marke
   las sich „Rabattcode“ wie eine Überschrift). Winkel aus Rahmenlinien, dreht beim Öffnen (Dauer-Stufe: steht bei „Bewegung reduzieren“ still, #2582). */
details.code-feld > summary { list-style: none; gap: 8px; text-decoration: underline;
  text-underline-offset: 3px; }
details.code-feld > summary::-webkit-details-marker { display: none; }
details.code-feld > summary::after { content: ""; width: 7px; height: 7px; margin-top: -3px;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(45deg); }
details.code-feld[open] > summary::after { transform: rotate(225deg); margin-top: 3px; }
details.code-feld > summary::after { transition: transform var(--dauer-kurz) var(--kurve); }
.code-inline > label { font-size: .8rem; font-weight: 600; display: block; margin-bottom: 4px; }
.code-form { display: flex; gap: 8px; }
.code-eingabe { flex: 1 1 auto; min-width: 0; min-height: 44px; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: calc(var(--radius) * .6); font: inherit;
  text-transform: uppercase; background: var(--card); color: var(--ink); }
.code-eingabe:focus { outline: none; border-color: var(--brand); }
.code-eingabe[aria-invalid="true"] { border-color: var(--danger, #b3261e); }
.code-knopf { flex: 0 0 auto; min-height: 44px; padding: 0 16px; border: 1px solid var(--brand);
  background: var(--card); color: var(--ink); border-radius: calc(var(--radius) * .6);
  font: inherit; font-weight: 600; cursor: pointer; }
.total.rabatt { font-weight: 400; font-size: .95rem; margin-bottom: 4px; align-items: baseline; }
.total.rabatt .code-weg { margin-left: 8px; font-size: .82rem; text-decoration: underline;
  min-height: 44px; }
.code-frist { font-size: .8rem; color: var(--ink-soft); margin: 0 0 12px; }
.code-hinweis { margin: 0 0 12px; }

/* #322-C3 (#1202): Sprung-Link — visuell versteckt, bei Tastatur-Fokus sichtbar
   (WCAG 2.4.1 Bypass Blocks). Kein display:none — das naehme ihn aus der
   Tab-Reihenfolge; die off-screen-Position laesst ihn fokussierbar. */
.skip-link { position: absolute; left: -999px; top: 0; z-index: 100;
  background: var(--accent, #1f6f52); color: var(--card, #fff); padding: .6rem 1rem;
  border-radius: 0 0 8px 0; font-weight: 600; }
/* #1717: der Ring lag hier auf `#fff` bei NEGATIVEM Offset, also INNERHALB
   des Links und damit auf dessen eigener `--accent`-Flaeche. Gemessen: carbon
   2,02:1, nachtschicht 2,23:1 — unter dem harten Boden, ausgerechnet an dem
   Element, das es nur wegen WCAG 2.4.1 gibt. #1708 hat diese Regel angefasst
   und die `color` auf `var(--card)` gezogen, den `outline` daneben nicht.
   Mit `var(--card)` ist das Paar card/accent derselbe erzwungene Vertrag wie
   die Schrift darauf. */
.skip-link:focus { left: 0; outline: 3px solid var(--card); outline-offset: -6px; }

/* #1609 GPSR Art. 19: die Pflichtangaben am Angebot. Bewusst OFFEN und im
   Fluss der Detailansicht — Art. 19 verlangt sie „eindeutig und gut sichtbar",
   und die Praxis-Leitfaeden (EN/DE/PL/IT, 2026) lesen ein Klick-davor als
   nicht ausreichend. Deshalb kein <details> wie bei .co2-method. */
.gp { border: 1px solid var(--line); border-radius: 12px; background: var(--card);
      padding: 12px 16px 14px; margin: 14px 0; }
.gp h4 { margin: 0 0 8px; font-size: .95rem; }
/* #1717: derselbe feste `#b45309` wie oben. Hier wiegt es schwerer, weil der
   Absatz eine PFLICHTANGABE nach Art. 19 GPSR traegt („eindeutig und gut
   sichtbar"). `--ink` gibt ihm den hoechsten Kontrast, den das Theme hergibt;
   die Auszeichnung uebernimmt die Kante in `--accent` — ein 3px-Strich ist
   Nicht-Text (WCAG 1.4.11, 3:1), und accent/card ist als Vertrag auf 3,0
   gesperrt. Muster aus dem eigenen Bestand: `#widerruf-body .wd-frist`. */
.gp-warn { margin: 0 0 10px; font-size: .84rem; line-height: 1.5; color: var(--ink);
  border-left: 3px solid var(--accent); padding: 2px 0 2px 10px; }
.gp-list { margin: 0; display: grid; gap: 10px; }
.gp-row { display: grid; gap: 2px; }
.gp-dt { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px;
         font-size: .82rem; font-weight: 600; }
.gp-norm { font-weight: 400; color: var(--ink-soft); font-size: .76rem; }
.gp-marke { font-weight: 500; font-size: .72rem; text-transform: uppercase;
            letter-spacing: .04em; color: var(--ink-soft); }
/* #1717: die Marke traegt das WORT „offen" — die Farbe war nie ihr Traeger.
   `--ink` (dunkelster Ton) neben dem `--ink-soft` der Normzeile plus Fettung
   ist derselbe Unterschied, ueberlebt aber Graustufen und jedes Theme. */
.gp-offen .gp-marke { color: var(--ink); font-weight: 700; }
.gp-wert { margin: 0; font-size: .86rem; line-height: 1.5; white-space: pre-wrap; }
.gp-hinweis { margin: 0; font-size: .8rem; line-height: 1.5; color: var(--ink-soft); }
/* #1717 — der einzige der vier festen Werte UNTER dem harten Boden:
   `#92400e` gegen `--card` ist 2,24:1 auf `nachtschicht` und 2,41:1 auf
   `carbon`. Ein Betreiber, der ein dunkles Theme waehlt, macht damit genau die
   Zeile unlesbar, die sagt, WELCHE Pflichtangabe fehlt. */
.gp-offen .gp-hinweis { color: var(--ink); }

/* #1978/AE-947 — Hinweisseite eines Shops, der nicht live ist, und die Leiste der
   Team-Vorschau. Nur bestehende Farbpaare (Kontrastvertraege in
   commerce/shop_kontrast.py): Text ink/ink-soft auf bg, Umkehrflaeche bg auf ink. */
.hinweis-seite { max-width: 40rem; margin: 0 auto; padding: 72px 0; text-align: center; }
.hinweis-seite h1 { font-family: var(--font-display); font-size: 1.8rem; margin: 0 0 12px; }
.hinweis-satz { color: var(--ink-soft); font-size: 1.05rem; line-height: 1.6; margin: 0; white-space: pre-line; }
.hinweis-kontakt { color: var(--ink-soft); margin: 16px 0 0; }
/* #1979: geplantes Ende und die Pflicht-Links, die kein Hinweistext verdrängen kann. */
.hinweis-ende { color: var(--ink); font-weight: 600; margin: 16px 0 0; }
.hinweis-recht { display: flex; flex-wrap: wrap; justify-content: center; gap: 4px 16px; margin: 24px 0 0; }
/* Eigenständige Links, kein Fließtext: volle Tippfläche (WCAG 2.5.8, §217 Telefon). */
.hinweis-recht .fl-link { min-height: 44px; padding: 0 4px; }
.vorschau-leiste { position: sticky; top: 0; z-index: 60; background: var(--ink); color: var(--bg);
  padding: 10px 16px; font-size: .9rem; line-height: 1.4; text-align: center; }

/* #2373/AE-1258 (§395) — Verbindungs-Leiste und der Zustand „keine Verbindung“. Nur bestehende
   Farbpaare (wie die Vorschau-Leiste): Umkehrflaeche bg auf ink; im Inhalt ink/ink-soft auf bg.
   Unten mittig ueber den Drawern (z 40) und dem Toast (z 50) — der Toast rueckt hoch, solange
   die Leiste steht (`body.verbindung-weg`), sonst laegen beide uebereinander. */
.verbindung-leiste { position: fixed; left: 16px; right: 16px; bottom: 16px; z-index: 55;
  max-width: 36rem; margin: 0 auto; display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px;
  background: var(--ink); color: var(--bg); padding: 10px 12px 10px 16px; border-radius: 12px;
  font-size: .92rem; line-height: 1.4; box-shadow: var(--shadow); }
/* Leer = nur fuer Bildschirmleser vorhanden (live-Region muss vor ihrem Inhalt existieren). */
.verbindung-leiste.vl-leer { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; box-shadow: none; }
.verbindung-leiste p { flex: 1 1 14rem; margin: 0; font-weight: 600; }
.vl-knopf { min-height: 44px; padding: 0 16px; border-radius: 10px; cursor: pointer; font: inherit;
  font-weight: 600; background: transparent; color: var(--ink); border: 1.5px solid var(--ink); }
.verbindung-leiste .vl-knopf { color: var(--bg); border-color: var(--bg); }
.vl-knopf:disabled { opacity: .6; cursor: progress; }
body.verbindung-weg .toast { bottom: 96px; }
.state-netz, .state-fehlt { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.state-netz .state-titel, .state-fehlt .state-titel { color: var(--ink); font-weight: 700; font-size: 1.1rem; }

/* ══ #2641/AE-1501 (Register §497) — Filterleiste einklappbar, Kategorie-Leiste, Zustands-Erklärung ══ */
/* Klapp-Knopf: nur, wo es die Seitenleiste gibt (ab 761 px); am Telefon öffnet „Filter“ den Dialog. */
.tb-klappen { display: none; align-items: center; gap: 6px; border: 1px solid var(--line);
  background: var(--card); color: var(--ink); border-radius: 999px; padding: 9px 14px; min-height: 40px;
  font-size: .88rem; font-weight: 600; font-family: inherit; cursor: pointer; }
.tb-klappen:hover { border-color: var(--brand); }
.tb-klappen-pfeil { width: 16px; height: 16px; flex: none; }
.tb-klappen[aria-expanded="false"] .tb-klappen-pfeil { transform: scaleX(-1); }
@media (min-width: 761px) { .tb-klappen { display: inline-flex; } }
/* Eingeklappt: volle Breite und ein kleineres Mindestmaß — 4 × 260 + 3 × 18 = 1094 px, also bei der
   vollen Inhaltsbreite (1140 px) vier Spalten à ~270 px (§497 „vier Spalten, die dann etwas kleiner sind“). */
.flt-layout.flt-zu { grid-template-columns: minmax(0, 1fr); gap: 0; }
.flt-layout.flt-zu > .flt-side { display: none; }
.flt-layout.flt-zu .grid { grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); }

/* Kategorie-Leiste: eigene Zeile unter dem Kopf, FESTE Höhe (der Server gibt sie schon in der Seite
   frei — die Einträge kommen später, die Höhe nicht). Am Telefon waagerecht scrollbar, der letzte
   Eintrag angeschnitten (Hinweis auf Fortsetzung, #2640 §4.3); Tippziel 44 px. Farben wie `.pn-link`. */
.kat-leiste { border-bottom: 1px solid var(--line); background: var(--bg); }
.kat-leiste[hidden] { display: none; }
.kat-zeile { display: flex; align-items: center; gap: 4px; height: 52px; overflow-x: auto;
  overscroll-behavior-x: contain; scrollbar-width: thin; }
.kat-link { flex: none; display: inline-flex; align-items: center; min-height: 44px; padding: 0 14px;
  border-radius: 999px; color: var(--ink-soft); text-decoration: none; font-size: .92rem;
  font-weight: 600; white-space: nowrap; }
.kat-link:hover { color: var(--ink); background: var(--brand-soft); }
.kat-link.active { color: var(--brand-dark); background: var(--brand-soft); }

/* #2667/AE-1511: Aktions-Banner über der Kategorie-Leiste. FESTE Höhe (der Server reserviert die Zeile
   schon in der Seite, `shop_html`; der Text kommt mit `/config`) → kein Nachrutschen (CLS, Klasse
   #2658). Die Bedingungszeile ist wesentliche Information (§ 5a UWG): sie bleibt lesbar — am Telefon
   höchstens drei Zeilen, nie abgeschnitten hinter „…“ bei den üblichen Längen (gemessen ≤ 150 Zeichen). */
/* Die Höhe sitzt an der HÜLLE (`#aktions-banner`), nicht am Inhalt: der Server gibt die leere Hülle frei,
   der Inhalt kommt erst mit `/config` — an der Zeile allein gemessen schob das Einsetzen `#sections` um
   die ganze Höhe (390 px: CLS 0,12, Messung #2667). */
.aktions-banner { box-sizing: border-box; height: 65px; overflow: hidden; background: var(--brand-soft);
  border-bottom: 1px solid var(--line); color: var(--ink); }
.aktions-banner[hidden] { display: none; }
.ab-zeile { display: flex; align-items: center; gap: 16px; height: 100%; overflow: hidden; }
.ab-text { flex: 1 1 auto; min-width: 0; margin: 0; display: flex; flex-direction: column; gap: 2px; }
.ab-satz { font-size: .98rem; font-weight: 700; color: var(--brand-dark); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; }
.ab-bed { font-size: .8rem; color: var(--ink-soft); line-height: 1.3; }
.ab-knopf { flex: none; display: inline-flex; align-items: center; min-height: 44px; padding: 0 16px;
  border-radius: 999px; background: var(--brand); color: var(--card); font-weight: 700; font-size: .9rem;
  text-decoration: none; white-space: nowrap; }
.ab-knopf:hover { background: var(--brand-dark); }
@media (max-width: 640px) {
  /* Werbesatz bis zwei Zeilen, Bedingungen bis drei, Knopf darunter: 148 px (gemessen 390 px). */
  .aktions-banner { height: 148px; }
  .ab-zeile { flex-wrap: wrap; align-content: center; gap: 6px 12px; padding-top: 6px;
    padding-bottom: 6px; }
  .ab-text { flex-basis: 100%; }
  .ab-satz { white-space: normal; font-size: .92rem; }
  .ab-bed { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
  .ab-knopf { min-height: 36px; }
}
.preis-code strong { font-weight: 700; }

/* Zustands-Erklärung: Verweis am Etikett / an der Achse / an der Filtergruppe, Fenster, Aufklapper,
   Abschnitt „Zustände im Vergleich“. */
.dt-badges { align-items: center; }
.zst-link { font-size: .82rem; font-weight: 600; text-transform: none; letter-spacing: 0;
  text-decoration: underline; text-underline-offset: 2px; }
.zst-auf { flex-basis: 100%; font-size: .88rem; }
.zst-auf > summary { cursor: pointer; font-weight: 600; color: var(--ink); }
.zst-auf[open] > summary { margin-bottom: 8px; }
#zustand-body { white-space: normal; }
.zst-stufe { border-top: 1px solid var(--line); padding: 12px 0; }
.zst-stufe:first-child { border-top: none; padding-top: 0; }
.zst-stufe h3 { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 0 0 6px;
  font-size: 1rem; }
.zst-stufe .grade { position: static; }
.zst-marke { font-size: .72rem; font-weight: 700; color: var(--brand-dark); background: var(--brand-soft);
  border-radius: 999px; padding: 2px 8px; }
.zst-kurz { margin: 0 0 8px; }
.zst-kriterien .zst-row { display: grid; grid-template-columns: 7rem minmax(0, 1fr); gap: 10px;
  padding: 3px 0; font-size: .88rem; }
.zst-kriterien dt { color: var(--ink-soft); }
.zst-kriterien dd { margin: 0; }
.zst-fuss { margin-top: 12px; font-size: .8rem; color: var(--ink-soft); }
.zustaende-section { padding: 10px 0 26px; }
.zst-einleitung { color: var(--ink-soft); max-width: 70ch; margin-bottom: 14px; }
.zst-vergleich { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); }
.zst-vergleich .zst-stufe { background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 16px; }
