/* Responsive Grundraster für den neuen Shop - bewusst neu mit CSS-Grid/Flexbox aufgebaut,
   NICHT als Portierung der alten Content/App/responsive.css (dort tabellenbasiertes Layout
   mit nur zwei groben Breakpoints, siehe CLAUDE.md Feature-Block "Fundament").
   Breakpoints: Mobile (<576px, Default), Tablet (>=576px), Desktop (>=992px). */

:root {
	--shop-max-width: 1280px;
	--shop-gap: 1rem;
}

html {
	scroll-behavior: smooth;
	/* Freiraum für die klebende Kopfzeile (siehe ".shop-header"): ohne diese Angabe würde ein
	   Sprung zu einem Seitenanker das Ziel exakt an den Viewport-Rand setzen - also hinter den
	   Header, der dort stehen bleibt. Grob an der Header-Höhe bemessen; unterhalb des
	   Desktop-Breakpoints ist die Kopfzeile zweizeilig und damit höher, dort bleibt ein
	   kleiner Rest verdeckt (bewusst in Kauf genommen, statt die Höhe per JS zu messen -
	   Seitenanker kommen im Shop nur vereinzelt vor). */
	scroll-padding-top: 4.5rem;
}

body {
	margin: 0;
	/* Grundfarbe der Seite. Bewusst hier und nicht an ".shop-layout" - siehe die Begründung zur
	   Malreihenfolge dort. */
	background-color: var(--background, #fff);
}

/* Seitenüberschriften. BlazorBlueprints Zurücksetzung nimmt <h1> jede Auszeichnung - übrig blieben
   16px in normaler Stärke, auf der Warenkorbseite also WENIGER als die 18px der Gesamtsumme
   darunter (Nutzer-Fund: stand "ziemlich mikrig" da).

   Größer, aber ausdrücklich NICHT fett: *"fett sieht so plump aus, es soll filigran bleiben"*.
   Die Größe allein trägt die Rangfolge. Zuerst nur für den Warenkorb gesetzt, auf Nutzerwunsch
   dann für alle Überschriften - deshalb hier global und nicht je Seite.

   Der Abstand nach unten steht mit dabei: mit 16px klebte die Überschrift am Inhalt, mit 28px
   fiele das noch mehr auf. Seiten, die es anders brauchen (Kaufbox der Artikeldetails,
   Login-Karte, Matrix-Kopf), setzen ihn weiterhin selbst. */
h1 {
	margin: 0 0 var(--shop-gap);
	font-size: 1.75rem;
	font-weight: 400;
	line-height: 1.2;
}

/* Nutzerwunsch: Link über dem Footer, der zum Seitenanfang zurückspringt - dezent
   hinterlegt, damit er sich als eigene Leiste vom Inhalt darüber und vom Footer darunter
   absetzt.

   Standardmäßig AUSGEBLENDET (Nutzerwunsch: "nur sichtbar, wenn es notwendig ist") - die
   Modifier-Klasse setzt nach-oben.js, sobald weit genug nach unten gescrollt wurde. Bewusst eine
   eigene Klasse statt des "hidden"-Attributs: "[hidden]" und ".shop-layout__nach-oben" haben
   dieselbe Spezifität, welche Regel gewinnt, hinge dann allein an der Reihenfolge im
   Stylesheet. Ohne JavaScript bleibt die Leiste weg - der Sprung selbst funktioniert dann
   ohnehin nicht (er hängt an einem onclick, siehe MainLayout.razor). */
.shop-layout__nach-oben {
	display: none;
	text-align: center;
	padding: 0.75rem var(--shop-gap);
	background: var(--surface, #f4f4f5);
	color: inherit;
	text-decoration: none;
	transition: background-color 0.2s ease-out, color 0.2s ease-out;
}

.shop-layout__nach-oben--sichtbar {
	display: block;
}

.shop-layout__nach-oben:hover,
.shop-layout__nach-oben:focus-visible {
	color: var(--primary, inherit);
}

/* Wiederverwendbare Utility-Klasse: Beträge/Mengen in Tabellen grundsätzlich rechtsbündig
   (Nutzerwunsch) - bewusst eine einzige, seitenübergreifende Klasse statt einer je-Seite-
   Kopie, da diese Regel für alle Tabellen gleichermaßen gilt (Warenkorb, Belege, Offene
   Posten, Rückstände, ...).
   Echter Fund: die seitenspezifischen "th, td"-Regeln (z.B. ".warenkorb-page__tabelle th,
   .warenkorb-page__tabelle td { text-align: left; ... }") haben durch die Kombination aus
   Klasse+Element-Selektor eine höhere Spezifität (0,1,1) als diese einzelne Klasse (0,1,0) und
   gewannen dadurch unabhängig von der Reihenfolge im Stylesheet - die Beträge waren dadurch
   trotz gesetzter Klasse nie tatsächlich rechtsbündig. "!important" stellt hier bewusst sicher,
   dass diese Utility-Klasse IMMER gewinnt, unabhängig von der Spezifität der jeweiligen
   Tabellen-Regel. */
/* "width: 1%" ist ein etablierter Trick gegen das Standardverhalten von "table-layout: auto"
   bei einer Tabelle mit "width: 100%": ohne explizite Spaltenbreiten verteilt der Browser
   übrig bleibenden Platz proportional auf ALLE Spalten, nicht nur die textlastige erste Spalte
   ("Produkt") - Zahlen-/Mengenspalten wurden dadurch teils deutlich breiter als ihr eigentlicher
   Inhalt (echter Fund, Nutzerbeobachtung: "Abstand von Menge zu Einzelpreis so groß"). Eine
   Spalte mit "width: 1%" bekommt vom Layout-Algorithmus effektiv nur ihre minimale Content-
   Breite zugewiesen, der gesamte Rest fließt automatisch in die einzige Spalte ohne eigene
   Breitenangabe (Produkt) - genau dort ist zusätzlicher Platz auch inhaltlich sinnvoll
   (Artikelbezeichnung). "white-space: nowrap" verhindert dabei, dass der Browser die Spalte
   stattdessen schmaler als ihren Inhalt zieht und Kopfzeilen-/Zellentext umbricht. */
.shop-tabelle__zahl {
	text-align: right !important;
	width: 1%;
	white-space: nowrap;
}

/* Der Mengen-Steller (MengenSteller.razor, Ersatz für das frühere bare Mengenfeld) soll nicht
   auf die volle Zellenbreite gestreckt werden. "text-align: right" auf der Zelle
   (.shop-tabelle__zahl) richtet nur INLINE-Inhalt rechts aus, NICHT einen Flex-Container wie
   ".menge-steller". Nutzerwunsch: der Steller soll mittig unter der Spaltenüberschrift "Menge"
   stehen statt rechtsbündig wie die Preisspalten daneben - bewusst nur HIER als Ausnahme,
   ".shop-tabelle__zahl" selbst bleibt unangetastet (gilt seitenübergreifend auch für echte
   Beträge in Belege/Offene Posten, die weiterhin rechtsbündig richtig sind).
   Echter Fund: "margin: 0 auto" allein zentrierte NICHT - ".menge-steller" ist von Haus aus
   "display: inline-flex" (siehe dortige Basisregel), und auto-Außenabstände wirken nur auf
   BLOCK-Elemente mit eigener definierter Breite zur horizontalen Selbstzentrierung, nicht auf
   Inline-Elemente (dort lösen sie sich zu 0 auf, egal wie breit die Zelle ist). "display: flex"
   macht den Steller hier zu einem echten Block-Flex-Container, wodurch "margin: auto"
   tatsächlich greift. */
.shop-tabelle__zahl .menge-steller {
	display: flex;
	width: fit-content;
	margin: 0 auto;
}

/* Die Spaltenüberschrift "Menge" selbst blieb trotz obiger Zentrierung des Stellers weiterhin
   rechtsbündig (".shop-tabelle__zahl" gilt seitenübergreifend, siehe oben) - Mitte des Worts
   "Menge" und Mitte der darunterstehenden Pille fielen dadurch sichtbar auseinander
   (Nutzerwunsch: "Mitte von dem Wort Menge mittig mit Mitte von der Auswahl-Pille"). Eigene,
   spezifischere Klasse NUR für diese eine Kopfzelle (Warenkorb.razor) statt die gemeinsame
   Utility-Klasse selbst zu ändern - Einzelpreis/Gesamt bleiben unverändert rechtsbündig. */
.shop-tabelle__zahl--mitte {
	text-align: center !important;
}

/* Gemeinsames Erscheinungsbild aller NATIVEN Schaltflächen des Shops (die Bb*-Buttons der
   Bibliothek bringen ihr eigenes, über dieselben Farb-/Radius-Tokens gesteuertes Aussehen
   mit und werden hier bewusst nicht angefasst - siehe theme-tokens.css).
   Vollrund, Primärfarbe, beim Überfahren leicht angehoben mit stärkerem Schatten, beim
   Drücken minimal verkleinert. Als Sammel-Selektor statt eigener Klasse, damit die
   bestehenden Elemente im Markup unverändert bleiben. */
.cookie-banner__button,
.shop-header__anmelden-cta {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	border: 1px solid transparent;
	border-radius: var(--radius-pill, 9999px);
	background: var(--primary, #18181b);
	color: var(--primary-foreground, #fff);
	padding: 0.5rem 1.25rem;
	font: inherit;
	font-weight: 600;
	text-decoration: none;
	box-shadow: var(--shadow-soft, none);
	cursor: pointer;
	transition: background-color 0.2s ease-out, box-shadow 0.2s ease-out, transform 0.2s ease-out;
}

.cookie-banner__button:hover,
.shop-header__anmelden-cta:hover {
	background: color-mix(in srgb, var(--primary, #18181b) 90%, transparent);
	transform: translateY(-2px);
	box-shadow: var(--shadow-card, none);
}

.cookie-banner__button:active,
.shop-header__anmelden-cta:active {
	transform: scale(0.98);
}

.cookie-banner__button:focus-visible,
.shop-header__anmelden-cta:focus-visible {
	outline: 3px solid var(--ring, currentColor);
	outline-offset: 2px;
}

/* ---------------------------------------------------------------------------
   Symbole
   Die SVG-Dateien unter wwwroot/icons/ tragen eine fest eingebackene mittelgraue Füllfarbe.
   Als <img> eingebunden ließen sie sich deshalb nicht einfärben - auf einer farbigen Fläche
   (z.B. dem orangefarbenen Hover eines Buttons) blieben sie dunkel stehen und verschwanden
   fast (Nutzer-Fund am "In den Warenkorb"-Button).
   Statt eines <img> wird die Datei jetzt als MASKE verwendet: das Element selbst ist eine
   Fläche in "currentColor", die Datei bestimmt nur noch deren Form. Dadurch folgt jedes Symbol
   automatisch der Schriftfarbe seiner Umgebung - auch der berechneten Schriftfarbe auf den
   Markenflächen (siehe @supports-Block in theme-tokens.css). Das ist die Voraussetzung dafür,
   dass ein Shop-Betreiber die Farben frei setzen kann, ohne dass Symbole unsichtbar werden.
   Vorbild ist die DeDeNet-Website, die ihre Siegel-Leiste im Footer genauso umsetzt.
   --------------------------------------------------------------------------- */
.shop-ikone {
	display: inline-block;
	flex: 0 0 auto;
	width: 1.5rem;
	height: 1.5rem;
	-webkit-mask: var(--shop-ikone-quelle) no-repeat center / contain;
	mask: var(--shop-ikone-quelle) no-repeat center / contain;
}

/* Die Färbung bewusst nur dort, wo Masken auch wirklich unterstützt werden: ohne Maske würde
   die Hintergrundfarbe als voller Kasten stehen bleiben statt als Symbolform. */
@supports ((mask-image: none) or (-webkit-mask-image: none)) {
	.shop-ikone {
		background-color: currentColor;
	}
}

.shop-ikone--menue { --shop-ikone-quelle: url("../icons/menu.svg"); }
.shop-ikone--filter { --shop-ikone-quelle: url("../icons/filter.svg"); }
.shop-ikone--person { --shop-ikone-quelle: url("../icons/person.svg"); }
.shop-ikone--warenkorb { --shop-ikone-quelle: url("../icons/shopping_cart.svg"); }
.shop-ikone--warenkorb-plus { --shop-ikone-quelle: url("../icons/add_shopping_cart.svg"); }
.shop-ikone--loeschen { --shop-ikone-quelle: url("../icons/delete.svg"); }
.shop-ikone--download { --shop-ikone-quelle: url("../icons/download.svg"); }

.shop-ikone--klein { width: 1.25rem; height: 1.25rem; }
.shop-ikone--gross { width: 1.625rem; height: 1.625rem; }

/* ---------------------------------------------------------------------------
   Umschalter (Segmentierte Auswahl, z.B. "Ansicht: Liste / Kacheln")
   Übernimmt die "outline"-Schaltfläche der DeDeNet-Website (siehe deren button.tsx:
   "border-2 border-primary/30 ... hover:bg-primary-soft hover:border-primary"):
   dauerhaft 2px Rahmen in blasser Primärfarbe, beim Überfahren verdichtet sich der Rahmen
   auf den vollen Ton und die Fläche füllt sich zart. Die AUSGEWÄHLTE Schaltfläche ist
   dagegen voll in der Primärfarbe gefüllt - dafür sorgt bereits ButtonVariant.Default,
   hier kommt nur der passende Rahmen dazu, damit beide Zustände exakt gleich groß bleiben.

   Die Rahmenstärke bleibt in ALLEN drei Zuständen bei 1px (Nutzer-Fund: 2px wirkte gegenüber
   den übrigen Bedienelementen - z.B. dem BbSelect-Trigger direkt darunter, siehe
   ".shop-header__suche input"/"content-seite__form select" - sichtbar zu wuchtig, im Shop
   gilt sonst durchgängig 1px) und wird NICHT erst beim Überfahren erhöht: eine wechselnde
   Rahmenstärke verschiebt die Schaltflächen-Geometrie und lässt die Beschriftung sichtbar
   springen. Der verstärkte Eindruck beim Überfahren entsteht stattdessen über einen inneren
   Schatten (siehe Zustand 2 unten), der Fläche über den Rahmen legt statt ihn zu verbreitern.

   Diese Regeln stehen bewusst ungeschichtet (nicht in @layer): BlazorBlueprint liefert seine
   Utility-Klassen in "@layer utilities" aus, und ungeschichtetes CSS gewinnt dagegen immer -
   siehe den dokumentierten Fund beim "Abmelden"-Eintrag im Konto-Menü. */
.shop-umschalter {
	border-width: 1px;
	transition: background-color 0.2s ease-out, border-color 0.2s ease-out, color 0.2s ease-out,
		box-shadow 0.2s ease-out;
}

/* 1. NICHT AUSGEWÄHLT - weiße Fläche, neutraler Rahmen, normale Schriftfarbe. Kein Primärton:
   der bleibt dem ausgewählten Zustand und der Hover-Rückmeldung vorbehalten, sonst sähe eine
   nicht getroffene Wahl bereits ausgewählt aus. */
.shop-umschalter:not(.shop-umschalter--aktiv) {
	border-color: var(--border, #e4e4e7);
	color: var(--foreground, inherit);
	background: var(--background, #fff);
}

/* 2. ÜBERFAHREN - der Rahmen nimmt die Primärfarbe an UND die Fläche wird dezent grau
   hinterlegt ("--muted"). Die Beschriftung bleibt unverändert dunkel.
   Die zusätzliche Rahmenstärke kommt über einen inneren Schatten statt über "border-width":
   eine wechselnde Rahmenstärke verändert die Geometrie der Schaltfläche und ließe die
   Beschriftung bei jedem Überfahren springen - der Inset-Schatten legt sich dagegen ohne
   Platzbedarf innen an den 1px-Rahmen und lässt ihn insgesamt 2px stark wirken. */
.shop-umschalter:not(.shop-umschalter--aktiv):hover,
.shop-umschalter:not(.shop-umschalter--aktiv):focus-visible {
	border-color: var(--primary, currentColor);
	box-shadow: inset 0 0 0 1px var(--primary, currentColor);
	background: var(--muted, #f4f4f5);
}

/* 3. AUSGEWÄHLT - voll in der Primärfarbe gefüllt (kommt aus ButtonVariant.Default), hier nur
   der passende Rahmen, damit beide Zustände exakt dieselbe Größe haben. */
.shop-umschalter--aktiv {
	border-color: var(--primary, currentColor);
}

/* ---------------------------------------------------------------------------
   Auswahlfelder (BbSelectTrigger)
   Echter Nutzer-Fund: BlazorBlueprints BbSelectTrigger rendert seinen Trigger standardmäßig
   voll in --primary gefüllt - das ergibt bei diesem Theme (--primary = kräftiges Orange)
   einen dauerhaft grell gefüllten Button, wo eigentlich ein gewöhnliches Auswahlfeld stehen
   sollte (verglichen mit z.B. "Artikel pro Seite", das denselben Effekt zeigt, sobald es
   ebenfalls ungestylt bleibt).
   Anders als bei den Umschaltern oben gibt es hier KEIN "AUSGEWÄHLT"-Gegenstück, das gefüllt
   bleiben dürfte: ein Auswahlfeld zeigt immer nur EINEN Wert, es gibt keine sichtbaren,
   nicht gewählten Geschwister wie bei "Liste"/"Kacheln" - eine dauerhafte Füllung hätte hier
   keine Bedeutung und wäre nur lauter. Der Ruhezustand bleibt deshalb IMMER neutral, exakt
   wie Zustand 1 der Umschalter, und bekommt beim Überfahren/Fokus dieselbe Rahmen-Reaktion
   wie Zustand 2 dort (siehe Kommentare oben) - bewusst als eigene Klasse statt die
   Umschalter-Klasse wiederzuverwenden, weil ein Select strukturell kein Button ist und nie
   den "--aktiv"-Zustand bekommen soll.
   Per "Class"-Parameter an JEDEM BbSelectTrigger im Shop zu setzen (aktuell Sortierung/
   Artikel-pro-Seite in ArtikelSuche.razor) - Erklärung, warum das nicht automatisch für
   jeden BbSelectTrigger gilt, siehe Kommentar am Dateianfang zu "Bb*"-Komponenten. */
.shop-select-trigger {
	background: var(--background, #fff) !important;
	color: var(--foreground, inherit) !important;
	border-color: var(--border, #e4e4e7) !important;
	transition: background-color 0.2s ease-out, border-color 0.2s ease-out, box-shadow 0.2s ease-out;
}

.shop-select-trigger:hover,
.shop-select-trigger:focus-visible {
	border-color: var(--primary, currentColor) !important;
	box-shadow: inset 0 0 0 1px var(--primary, currentColor);
	background: var(--muted, #f4f4f5) !important;
}

/* Aufgeklappte Liste eines BbSelect (die Einträge, nicht der Trigger). Echte, im ausgelieferten
   BlazorBlueprint-Paket gefundene Regel (blazorblueprint.css):
   "[role=option][data-focused=true]{background-color:var(--accent);color:var(--accent-foreground)}"
   - seit "--accent: var(--primary)" (Nutzerwunsch, siehe theme-tokens.css) markiert das
   Überfahren/die Tastatur-Navigation dadurch JEDEN Eintrag voll deckend in der Primärfarbe,
   ununterscheidbar vom tatsächlich AUSGEWÄHLTEN Eintrag. Für eine Liste gilt dieselbe Logik wie
   bei den Umschaltern oben: Überfahren bekommt nur eine neutrale Fläche, die volle Füllung bleibt
   dem wirklich ausgewählten Eintrag vorbehalten ("aria-selected" - Pflichtattribut des
   WAI-ARIA-Listbox-Patterns für "role=option", kein geratener Klassenname).
   "!important" wie bei ".shop-select-trigger" oben: die Original-Regel liegt in
   "@layer bb{@layer components{...}}", unlayered CSS (diese Datei) gewinnt zwar bereits ohne
   das (siehe dokumentierter Fund beim "Abmelden"-Eintrag), zusätzliche Absicherung, da diese
   Regel nicht live gegen eine laufende Instanz verifiziert werden konnte. */
[role="option"][data-focused="true"] {
	background-color: var(--surface, #f4f4f5) !important;
	color: var(--foreground, inherit) !important;
}

[role="option"][aria-selected="true"] {
	background-color: var(--primary, currentColor) !important;
	color: var(--primary-foreground, #fff) !important;
}

/* Ausgewählter Eintrag zusätzlich überfahren: bleibt gefüllt (Auswahl hat Vorrang vor bloßem
   Hover), nur eine Spur dunkler als Rückmeldung. */
[role="option"][aria-selected="true"][data-focused="true"] {
	background-color: color-mix(in srgb, var(--primary, #18181b) 88%, black) !important;
	color: var(--primary-foreground, #fff) !important;
}

/* ---------------------------------------------------------------------------
   Mengen-Steller (Components/Shared/MengenSteller.razor)
   Ersetzt die nativen HTML5-Spinner-Pfeile durch ein Minus-/Eingabe-/Plus-Trio in einer
   gemeinsamen, geschlossenen Fläche - Begründung siehe Kommentarkopf der Komponente.
   "overflow: hidden" auf dem Wrapper sorgt dafür, dass der eckige Hover-Hintergrund der
   Schaltflächen an den äußeren, runden Ecken sauber abgeschnitten wird, statt über den
   Rahmen hinauszustehen. Fokus wird bewusst am WRAPPER sichtbar gemacht (":focus-within"),
   nicht am <input> selbst - ein eigener inputseitiger Fokusring läge sonst mitten in der
   Fläche und sähe gegenüber den Schaltflächen links/rechts unvollständig aus. */
.menge-steller {
	display: inline-flex;
	align-items: stretch;
	border: 1px solid var(--input, #ddd);
	border-radius: var(--radius-pill, 9999px);
	background: var(--background, #fff);
	overflow: hidden;
}

.menge-steller:focus-within {
	outline: 2px solid var(--ring, currentColor);
	outline-offset: 2px;
}

.menge-steller__schaltflaeche {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2rem;
	flex: 0 0 auto;
	background: none;
	border: none;
	font-size: 1.125rem;
	line-height: 1;
	color: var(--foreground, #18181b);
	cursor: pointer;
	transition: background-color 0.2s ease-out, color 0.2s ease-out;
}

.menge-steller__schaltflaeche:hover:not(:disabled),
.menge-steller__schaltflaeche:focus-visible {
	background: var(--surface, #f4f4f5);
	color: var(--primary, inherit);
}

/* Nutzerwunsch: die Minus-Schaltfläche ausgegraut, sobald die Mindestmenge erreicht ist -
   siehe "disabled"-Bindung in MengenSteller.razor ("Value <= Min"). */
/* Schmale Variante für die Mengenmatrix: dort steht neben jedem Steller ein Auswahlkästchen, und
   die Matrix hat so viele Spalten wie Varianten - jedes Pixel Feldbreite kostet dort 26 mal Platz.

   Bewusst auf ".artikel-matrix-page" eingegrenzt (Nutzervorgabe "aber nur auf der matrix seite"):
   so kann die Klasse nicht versehentlich anderswo dieselbe Enge erzeugen.

   1,25rem = 20px sind ausgemessen und nicht geschätzt: "99" braucht in der Schrift des Felds
   17,3px, bleibt also mit knapp 3px Luft drin (Nutzervorgabe "99 muss reichen"). Größere Mengen
   bleiben MÖGLICH - der Wert wird nirgends begrenzt, die Eingabe scrollt dann nur im Feld. */
.artikel-matrix-page .menge-steller--schmal .menge-steller__eingabe {
	width: 1.25rem;
	min-width: 1.25rem;
}

.artikel-matrix-page .menge-steller--schmal .menge-steller__schaltflaeche {
	width: 1.75rem;
}

.menge-steller__schaltflaeche:disabled {
	color: var(--muted-foreground, #a1a1aa);
	cursor: not-allowed;
}

.menge-steller__eingabe {
	flex: 1 1 auto;
	min-width: 2.75rem;
	width: 3rem;
	border: none;
	background: transparent;
	text-align: center;
	font: inherit;
	color: inherit;
	padding: 0.375rem 0;
}

/* Eigener Fokusring entfällt bewusst - siehe Kommentar am Wrapper oben, ":focus-within"
   übernimmt das für das gesamte Steuerelement. */
.menge-steller__eingabe:focus-visible {
	outline: none;
}

/* Seitenhintergrund mit Farbwäsche oben und unten, von dedenet.de/dedesales übernommen
   (Nutzerwunsch 09.09.2026, "den können wir überall einbauen"). Die Farben stehen als Tokens in
   theme-tokens.css - hier wird nur die Form gebaut.

   Sechs Ebenen, drei je Kante: zwei weiche Kreise (warm links, kühl rechts) und darüber ein
   linearer Hauch, der zur Seitenmitte hin auf Null läuft. Genau so ist es auf der Website
   aufgebaut; nachgemessen und nicht nachempfunden.

   Am INHALTSBEREICH und nicht am Layout oder am Body. Drei Gründe, alle drei nachgemessen:
   - Er beginnt UNTERHALB der Kopfzeile. Genau das war der Nutzer-Fund ("bei uns fängt der verlauf
     scheinbar direkt oben an ... auf unserer homepage beginnt dieser erst unterhalb des
     headers"): lag der Verlauf am Layout, verbrauchte die Kopfzeile die kräftigste erste Stufe,
     und im sichtbaren Inhalt kam nur noch der blasse Rest an.
   - Er wächst mit dem Inhalt, der untere Verlauf sitzt also am Ende der SEITE und nicht am
     unteren Fensterrand.
   - Er endet über der Fußzeile, die ihre eigene Fläche hat.
   Die Grundfarbe bleibt am Layout: sie soll auch hinter Kopf- und Fußzeile liegen. */
.shop-layout {
	display: flex;
	flex-direction: column;
	min-height: 100vh;
	/* KEINE Grundfarbe hier - die steht am <body>. Grund ist die Malreihenfolge: der Hintergrund
	   eines normalen Blocks wird NACH den Nachfahren mit negativem z-index gemalt. Stand die weiße
	   Fläche hier, deckte sie das Verlaufs-Pseudoelement des Inhaltsbereichs vollständig zu - real
	   passiert, Nutzer-Fund: "jetzt ist gar kein verlauf mehr da". Am <body> liegt sie dagegen
	   unter allem. */
	/* Der Verlauf unten ist volle FENSTERbreite (100vw) und damit um die Breite der Bildlaufleiste
	   breiter als der Inhalt - ohne diesen Beschnitt bekäme die Seite dadurch einen waagerechten
	   Scrollbalken. Bewusst "clip" und nicht "hidden": "hidden" macht das Layout zu einem
	   Scrollbereich und die klebende Kopfzeile würde aufhören zu kleben. */
	overflow-x: clip;
}

/* Der Verlauf liegt auf einem Pseudoelement und nicht auf dem Inhaltsbereich selbst: dieser ist
   auf 1280px begrenzt und zentriert, der Verlauf endete damit an der Spaltenkante statt am
   Fensterrand (Nutzer-Fund: "der verlauf geht nun nicht mehr über die volle breite"). Das
   Pseudoelement wird über die Mitte auf volle Fensterbreite aufgezogen und liegt hinter dem
   Inhalt; SENKRECHT bleibt es an den Inhaltsbereich gebunden, beginnt also weiterhin unter der
   Kopfzeile und endet über der Fußzeile - genau darum ging es beim vorherigen Umbau.

   Die Alternative wäre gewesen, den Verlauf am Layout zu lassen und um die Höhe der Kopfzeile
   nach unten zu schieben. Die ist aber je nach Breite 69 oder 131 px hoch; jede feste Zahl dafür
   wäre beim nächsten Umbau der Kopfzeile falsch. */
.shop-layout__content::before {
	content: "";
	position: absolute;
	top: 0;
	bottom: 0;
	left: 50%;
	width: 100vw;
	transform: translateX(-50%);
	/* Hinter den Inhalt, aber vor die Grundfarbe des Layouts. */
	z-index: -1;
	pointer-events: none;
	/* Die Deckkraft steckt hier und nicht in den Tokens: die Tokens sind fertige FARBEN (aus der
	   Primärfarbe berechnet, siehe theme-tokens.css), und dieselbe Farbe wird an zwei Stellen mit
	   unterschiedlicher Deckkraft gebraucht. "color-mix ... transparent" ist der Weg, der mit einer
	   beliebig notierten Farbe funktioniert - eine Alpha-Angabe wie "rgb(var(--x) / 0.3)" setzte
	   voraus, dass im Token nur Kanäle ohne Alpha stehen. */
	background-image:
		radial-gradient(80% 60% at 15% 0%, color-mix(in srgb, var(--verlauf-warm) 30%, transparent), transparent 70%),
		radial-gradient(75% 60% at 85% 5%, color-mix(in srgb, var(--verlauf-kalt) 30%, transparent), transparent 70%),
		linear-gradient(180deg, color-mix(in srgb, var(--verlauf-warm) 16%, transparent), color-mix(in srgb, var(--verlauf-kalt) 16%, transparent) 25%, transparent 55%),
		radial-gradient(80% 60% at 15% 100%, color-mix(in srgb, var(--verlauf-warm) 30%, transparent), transparent 70%),
		radial-gradient(75% 60% at 85% 95%, color-mix(in srgb, var(--verlauf-kalt) 30%, transparent), transparent 70%),
		linear-gradient(0deg, color-mix(in srgb, var(--verlauf-warm) 16%, transparent), color-mix(in srgb, var(--verlauf-kalt) 16%, transparent) 25%, transparent 55%);
	background-repeat: no-repeat;
	/* Deutlich flacher als auf der Website (dort rund 640/1000 px): deren Seiten sind lang, die
	   beiden Wäschen liegen weit auseinander. Im Shop ist eine Seite oft nur fensterhoch - mit den
	   Originalhöhen überlagerten sich oben und unten und die GANZE Fläche wurde pfirsichfarben
	   (real gesehen auf "Noch kein Zugang?").

	   Zusätzlich zur festen Höhe eine PROZENTUALE Obergrenze, und die ist der eigentliche Schutz:
	   auf dem Smartphone ist die Fußzeile 365 bis 477 px hoch, für den Inhalt bleiben auf einer
	   kurzen Seite nur rund 670 px - feste 352 + 288 px ließen dort gerade noch 30 px Weiß dazwischen,
	   bei noch weniger Inhalt liefe wieder alles ineinander. Mit 40 % / 30 % bleibt IMMER knapp ein
	   Drittel der Fläche unberührt, egal wie kurz die Seite ist; auf langen Seiten greift weiter der
	   feste Wert. */
	background-size:
		100% min(22rem, 40%), 100% min(22rem, 40%), 100% min(22rem, 40%),
		100% min(18rem, 30%), 100% min(18rem, 30%), 100% min(18rem, 30%);
	background-position: top, top, top, bottom, bottom, bottom;
}

.shop-layout__content {
	flex: 1;
	/* Bezugspunkt für das Verlaufs-Pseudoelement oben. */
	position: relative;
	/* "display:flex" hier zusätzlich (nicht nur auf ".shop-layout" selbst) - macht dieses
	   Element gleichzeitig Flex-ITEM (von ".shop-layout") UND Flex-CONTAINER (für sein
	   eigenes Kind). Grund: die frühere Login-Seiten-Lösung ließ ".login-page" per
	   "min-height:100%" gegen dieses Element auflösen - das ist eine reine Block-Box ohne
	   eigenes Flex, und eine Prozent-Höhe eines Kindes gegen einen Flex-ITEM-Elternblock mit
	   nur rechnerisch (nicht per "height") bestimmter Größe löst nicht in jeder Browser-
	   Umgebung zuverlässig auf - real beobachtet (großer Viewport): ".login-page" blieb auf
	   seiner reinen Inhaltshöhe stehen, obwohl deutlich mehr Platz bis zum Footer verfügbar
	   war (Nutzer-Fund: Splash-Bild "schrumpfte" sichtbar und hing oben statt den Freiraum zu
	   füllen/zentriert zu sein). Als echter Flex-Container mit "flex-direction:column" gibt
	   dieses Element seinem Kind über "flex:1" (siehe ".login-page") den Freiraum direkt in
	   echten Pixeln weiter (kein Prozent-Höhen-Sonderfall mehr) - für alle anderen Seiten
	   (jeweils genau ein Kind-Element, siehe deren eigene CSS-Blöcke) unverändert, da ein
	   einzelnes Flex-Item ohne "flex-grow" exakt wie ein normales Block-Kind auf seine
	   Inhaltshöhe kommt. */
	display: flex;
	flex-direction: column;
	width: 100%;
	max-width: var(--shop-max-width);
	margin: 0 auto;
	padding: var(--shop-gap);
	box-sizing: border-box;
}

/* Header/Footer als helle, transluzente "Glas"-Fläche: derselbe Ansatz, der sich bereits bei
   ".login-page__card" bewährt hat (backdrop-filter + color-mix auf einem Theme-Token statt
   eines festen rgba-Werts) - der Seiteninhalt scheint unscharf durch, statt dass eine
   deckende Fläche darüber liegt. Bewusst OHNE Farbverlauf-Overlay (Nutzerentscheidung: keine
   Gradients im Shop), stattdessen eine gleichmäßige Deckung plus dünne Trennlinie.
   Ersetzt den früheren, deckend grauen Header ("--muted-foreground") - die Schriftfarbe kann
   deshalb nicht mehr Weiß sein (wäre auf hellem Glas unlesbar) und ist jetzt "--foreground". */
.shop-header,
.shop-footer {
	background-color: color-mix(in srgb, var(--card, #fff) 72%, transparent);
	backdrop-filter: blur(44px) saturate(1.8);
	-webkit-backdrop-filter: blur(44px) saturate(1.8);
	color: var(--foreground, #18181b);
	box-shadow: var(--shadow-soft, none);
}

/* Kopfzeile bleibt beim Scrollen stehen (Nutzerwunsch, analog zur Website: "sticky top-0").
   Erst dadurch wird der Glaseffekt oben überhaupt sichtbar - der Seiteninhalt zieht unscharf
   darunter durch, statt dass die Fläche nur einmal beim Seitenanfang zu sehen ist.
   z-index 30 ist bewusst gewählt und liegt zwischen den vorhandenen Ebenen dieser Datei:
   über dem Seiteninhalt und den aufklappenden Panels (20), aber unter Login-Modal (50),
   Lightbox (100) und Cookie-Banner (1000) - diese drei sollen die Kopfzeile weiterhin
   verdecken. Das Burger-/Konto-Menü stapelt sich innerhalb des Headers und ist davon nicht
   betroffen. */
.shop-header {
	position: sticky;
	top: 0;
	z-index: 30;
	border-bottom: 1px solid var(--border, #e4e4e7);
}

/* Nutzerwunsch: derselbe Grauton wie bei ".shop-layout__nach-oben" (der Leiste direkt darüber)
   statt der hellen Glasfläche, die für Header/Footer gemeinsam weiter oben definiert ist -
   beide Flächen sollen optisch ineinander übergehen. Überschreibt die dortige
   "background-color"/"backdrop-filter" gezielt nur für den Footer (gleiche Spezifität, aber
   spätere Regel gewinnt) - der Header behält seinen Glaseffekt unverändert. */
.shop-footer {
	border-top: 1px solid var(--border, #e4e4e7);
	background-color: var(--surface, #f4f4f5);
	backdrop-filter: none;
	-webkit-backdrop-filter: none;
}

.shop-header a,
.shop-footer a {
	color: inherit;
}

/* Footer-Links hatten bis hierher keine Hover-Rückmeldung (Nutzer-Fund). Bewusst nur
   Farbwechsel + Unterstreichung statt eines Flächen-Hovers wie im Header: die Links stehen
   hier als reine Textliste ohne umgebende Schaltflächen-Anmutung, ein farbiger Kasten pro
   Zeile würde die ruhige Fußzeile unnötig unruhig machen (dieselbe Entscheidung wie im
   Website-Footer).
   Bewusst nur DIREKTE Kinder der Spalten ("> a") plus die Aufklapp-Beschriftung: die Einträge
   INNERHALB eines aufgeklappten Menü-Panels liegen tiefer verschachtelt und behalten ihren
   eigenen Flächen-Hover (siehe ".shop-footer__menu-item-kinder a:hover" weiter unten) - ohne
   diese Einschränkung bekämen sie zusätzlich eine Unterstreichung, die dort fehl am Platz wäre. */
.shop-footer__spalte > a:hover,
.shop-footer__spalte > a:focus-visible,
.shop-footer__menu-item > summary:hover,
.shop-footer__menu-item > summary:focus-visible {
	color: var(--primary, inherit);
	text-decoration: underline;
	text-underline-offset: 0.25rem;
}

/* Ausnahme von "color: inherit" oben: der Anmelden-CTA ist eine gefüllte Schaltfläche und
   braucht seine eigene Schriftfarbe. Höhere Spezifität nötig, weil ".shop-header a" (Klasse
   + Element) sonst gegen die reine Klasse des Buttons gewinnt. */
.shop-header a.shop-header__anmelden-cta {
	color: var(--primary-foreground, #fff);
}

.shop-header__primary {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	/* Nutzerwunsch: etwas mehr Abstand zwischen Marke/Logo und der Navigation daneben, als der
	   sonst im Shop übliche Spaltenabstand ("--shop-gap"). */
	gap: calc(var(--shop-gap) * 1.5);
	/* Nutzerwunsch: Logo deutlich größer - dafür bewusst weniger vertikaler Abstand im Header
	   als vorher (nur noch ein kleiner Rand über/unter dem Logo), statt den Header insgesamt
	   viel höher werden zu lassen. */
	padding: 0.375rem var(--shop-gap);
	max-width: var(--shop-max-width);
	margin: 0 auto;
}

.shop-header__brand {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	font-weight: 600;
	text-decoration: none;
	color: inherit;
	transition: color 0.2s ease-out;
	/* Ohne "min-width: 0" kann ein Flex-Element nicht unter seine Inhaltsbreite schrumpfen
	   (Standardwert "min-width: auto"). Ein langer Firmenname hat deshalb die Nachbarn in der
	   Kopfzeile zusammengedrückt, statt selbst schmaler zu werden - Nutzer-Fund mit einem
	   bewusst überlangen Testnamen: "Kategorien"/"Highlights" wurden dahinter übereinander
	   gequetscht. */
	min-width: 0;
}

/* Grundzustand des Betreibernamens - gilt so für die anonym erreichbaren Seiten (Login,
   Impressum, Datenschutz, AGB, Registrierung). Dort steht der Name praktisch allein in der
   Kopfzeile, deshalb bewusst OHNE Breitenbegrenzung und OHNE Abkürzung: er soll vollständig
   und in einer Zeile zu lesen sein (Nutzerwunsch).
   "white-space" bleibt bewusst auf dem Standard (also umbruchfähig): auf einem sehr schmalen
   Handy-Display würde ein erzwungenes "nowrap" den Namen seitlich aus der Seite herauslaufen
   lassen und einen waagerechten Scrollbalken erzeugen. Auf jeder normalen Fensterbreite steht
   er ohnehin in einer Zeile.
   Ein einzelnes überlanges Wort (zusammengesetzter Firmenname ohne Leerzeichen) darf notfalls
   innerhalb des Wortes brechen, statt die Kopfzeile zu sprengen. */
.shop-header__betreiber {
	line-height: 1.25;
	overflow-wrap: anywhere;
}

/* Angemeldeter Zustand: hier teilt sich der Name die Kopfzeile mit Navigation, Produktsuche
   und Warenkorb/Konto. Er bekommt deshalb eine Obergrenze und läuft darin über höchstens zwei
   Zeilen, danach wird mit "…" abgeschnitten (der vollständige Name steht als Tooltip am
   Element, siehe SiteHeader.razor). Zwei Zeilen kosten bewusst keine zusätzliche Bauhöhe:
   die Kopfzeilenhöhe wird ohnehin vom deutlich höheren Logo bestimmt (siehe
   ".shop-header__logo", 3.5rem).
   "-webkit-line-clamp" ist trotz des Präfixes der überall unterstützte Weg, Text nach n
   Zeilen abzuschneiden; das ungepräfixte "line-clamp" steht daneben für künftige Browser.
   Die Obergrenze wächst per "clamp()" mit dem Fenster mit, statt ein fester Wert zu sein -
   auf schmalen Geräten bleibt so mehr Platz für Warenkorb/Konto, auf breiten Bildschirmen
   ist mehr vom Namen lesbar. */
.shop-header__betreiber--kompakt {
	display: -webkit-box;
	-webkit-box-orient: vertical;
	-webkit-line-clamp: 2;
	line-clamp: 2;
	overflow: hidden;
	max-width: clamp(9rem, 20vw, 24rem);
}

/* Der Betreibername ist ein Link auf die Startseite, wirkte aber wie reine Beschriftung -
   bewusst nur ein Farbwechsel (kein Flächen-Hover): das Logo daneben liegt im selben Link,
   ein Kasten würde sich unschön darum legen. */
.shop-header__brand:hover,
.shop-header__brand:focus-visible {
	color: var(--primary, inherit);
}

.shop-header__logo {
	height: 3.5rem;
}

/* Standardmäßig (Mobile/Tablet) ausgeblendet - das Burger-Menü übernimmt Kategorien/Highlights,
   siehe .shop-header__burger. Erst ab dem Desktop-Breakpoint sichtbar (@media (min-width: 992px)
   unten), wo wieder genug Platz für die ausgeschriebene Navigation ist. */
.shop-header__nav {
	display: none;
	/* Bewusst KEIN Umbruch und KEIN Schrumpfen: sobald der Platz eng wird, soll der lange
	   Firmenname links nachgeben (siehe ".shop-header__betreiber") - vorher standen
	   "Kategorien" und "Highlights" stattdessen übereinander (Nutzer-Fund).
	   "white-space: nowrap" sitzt bewusst an den Triggern selbst und NICHT hier: die
	   Dropdown-Panels liegen INNERHALB der Navigation (siehe ".shop-header__menu-panel"), sie
	   würden die Angabe erben und ihre Einträge könnten in den 16rem nicht mehr umbrechen. */
	flex-wrap: nowrap;
	flex-shrink: 0;
	/* Hält die Navigationspunkte auf einer gemeinsamen Mittelachse - ohne diese Angabe würden
	   sie sich als Flex-Elemente auf die volle Zeilenhöhe strecken und ihren Text je nach
	   Elementtyp unterschiedlich hoch absetzen (siehe Kommentar bei ".shop-header__nav a"). */
	align-items: center;
	/* Nutzerwunsch: größerer Abstand zwischen den Navigationspunkten selbst als der normale
	   Spaltenabstand ("--shop-gap"), der sonst überall im Shop verwendet wird - hier bewusst
	   auf das Doppelte, damit "Kategorien"/"Highlights" nicht zu eng beieinanderstehen. */
	gap: calc(var(--shop-gap) * 2);
	order: 3;
	width: 100%;
}

/* Hauptnavigation: Unterstrich fährt bei Hover/Fokus von links ein. Der Strich gehört bewusst
   dem Hover/Fokus und NICHT einem "aktiv"-Zustand - sonst stünde dauerhaft ein Strich in der
   Kopfzeile und das Überfahren des aktuellen Punkts bliebe ohne Rückmeldung. */
/* WICHTIG: Kindselektor ">" statt Nachfahren-Selektor - seit "Highlights" ein echtes
   Dropdown-Panel INNERHALB der Navigation rendert (siehe .shop-header__menu-panel), lägen
   dessen Menüeinträge sonst ebenfalls in dieser Regel und bekämen den Hover-Unterstrich der
   Hauptnavigation. */
.shop-header__nav > a,
.shop-header__menu-trigger {
	/* Siehe ".shop-header__nav": der Umbruchschutz gehört an den Trigger, nicht an die
	   Navigation - sonst erbt ihn das Dropdown-Panel darin. */
	white-space: nowrap;
	position: relative;
	/* Beide Punkte müssen exakt gleich hoch sein und ihre Schrift auf derselben Linie zeigen wie
	   der Betreibername links daneben ("demo4 - eCom"): "Kategorien" ist ein <a>, "Highlights"
	   dagegen ein <button> (eigener Dropdown-Trigger), und beide stehen als Flex-Geschwister neben
	   dem viel höheren Logo (siehe ".shop-header__logo", 3.5rem).
	   Zweiter, echter Nutzer-Fund NACH der ersten Korrektur (siehe Git-Historie): der Strich
	   unten war zuvor über "padding-bottom" auf DIESEM Element reserviert - das machte die Box
	   von Kategorien/Highlights asymmetrisch (mehr Raum unten als oben), wodurch ihr Text beim
	   Zentrieren neben dem hohen Logo (".shop-header__primary" hat "align-items: center") nicht
	   auf derselben Höhe landete wie der symmetrisch zentrierte Markenname. Der Strich sitzt
	   jetzt stattdessen über einen NEGATIVEN "bottom"-Versatz auf dem "::after" - der liegt
	   außerhalb des normalen Layouts (absolute Positionierung beeinflusst die Elterngröße nicht)
	   und lässt die Text-Box selbst symmetrisch/unangetastet, exakt wie beim Markennamen. */
	display: inline-flex;
	align-items: center;
	/* Kein Standard-Unterstrich: der Strich unten ist der Hover-Indikator, ein zusätzlicher
	   dauerhafter text-decoration-Strich würde ihn entwerten. */
	text-decoration: none;
	transition: color 0.2s ease-out;
	/* Ab hier reiner Button-Reset - wirkungslos, aber harmlos auf ".shop-header__nav a" (ein
	   <a> hat ohnehin keinen Rahmen/Hintergrund), zwingend nötig auf
	   ".shop-header__menu-trigger" (ein natives <button>). War vorher eine eigene,
	   spätere Regel für nur diese eine Klasse - siehe Kommentar oben, wieso zwei Regeln für
	   dieselbe Eigenschaft hier genau der Fehler waren. */
	background: none;
	border: none;
	font: inherit;
	cursor: pointer;
	/* Gedämpfte Grundfarbe wie im Hauptmenü der Firmenwebsite (Nutzerwunsch): die Kopfzeile
	   bleibt ruhig, die Primärfarbe ist Hover/Fokus und dem OFFENEN Menü vorbehalten (siehe
	   die :hover-Regel unten). Steht hier statt des sonst üblichen "color: inherit" des
	   Button-Resets - bewusst nur EINE Regel für diese Eigenschaft, siehe Kommentar oben. */
	color: var(--muted-foreground, inherit);
}

.shop-header__nav > a::after,
.shop-header__menu-trigger::after {
	content: "";
	position: absolute;
	left: 0;
	right: 0;
	bottom: -0.25rem;
	height: 2px;
	background: var(--primary, currentColor);
	transform: scaleX(0);
	transform-origin: left;
	transition: transform 0.2s ease-out;
}

/* "[aria-expanded=true]" hängt am tatsächlichen Menüzustand (bool "_highlightsOffen" in
   SiteHeader.razor), nicht an der Mausposition - ein per Klick geöffnetes Menü behält Farbe
   und Unterstrich also auch dann, wenn die Maus gerade auf einem Eintrag im Panel steht. */
.shop-header__nav > a:hover,
.shop-header__nav > a:focus-visible,
.shop-header__menu-trigger:hover,
.shop-header__menu-trigger:focus-visible,
.shop-header__menu-trigger[aria-expanded="true"] {
	color: var(--primary, inherit);
}

.shop-header__nav > a:hover::after,
.shop-header__nav > a:focus-visible::after,
.shop-header__menu-trigger:hover::after,
.shop-header__menu-trigger:focus-visible::after,
.shop-header__menu-trigger[aria-expanded="true"]::after {
	transform: scaleX(1);
}

/* ---------------------------------------------------------------------------
   Dropdown-Menüpunkt in der Hauptnavigation (aktuell "Highlights")
   Nutzerwunsch: exakt der Pfeil-/Hover-Effekt aus dem "Produkte"-Menü der
   DeDeNet-Firmenwebsite. Trigger-Farbe und Unterstrich kommen bewusst aus den
   Regeln oben (dieselbe Nav-Klasse, nicht neu gebaut) - hier stehen nur die
   Teile, die es dort noch nicht gab: Pfeil und Panel.
   --------------------------------------------------------------------------- */

/* Der Wrapper ist Bezugspunkt für das Panel darunter UND der Bereich, den die Maus verlassen
   muss, damit das Menü zuklappt (@onmouseleave, siehe SiteHeader.razor) - das Panel liegt
   deshalb bewusst INNERHALB und nicht in einem Portal. */
.shop-header__menu {
	/* Breite des Dropdown-Panels UND der Hover-Brücke darunter - eine Quelle, damit beide nicht
	   auseinanderlaufen (die Brücke muss mindestens so breit sein wie das Panel, sonst reißt der
	   Weg beim diagonalen Hinüberfahren ab). */
	--shop-header-menu-breite: 16rem;
	position: relative;
	display: inline-flex;
	align-items: center;
	gap: 0.25rem;
}

/* Unsichtbare Brücke über den Abstand zwischen Menüwort und Panel ("margin-top" am Panel), damit
   die Maus auf dem Weg nach unten den Wrapper nicht verlässt - siehe die ausführliche Begründung
   am Element selbst in HeaderDropdownMenu.razor. Höhe = Abstand des Panels, Breite = Panelbreite,
   damit auch diagonales Hinüberfahren nicht abreißt. Wird nur gerendert, solange das Menü offen
   ist, und liegt UNTER dem Panel (kein z-index), stört also nichts. */
.shop-header__menu-bruecke {
	position: absolute;
	top: 100%;
	left: 0;
	width: var(--shop-header-menu-breite);
	height: 0.75rem;
}

/* Abstand zwischen Menüwort und Pfeil - der Rest (Höhe, Ausrichtung, Button-Reset) kommt aus
   der gemeinsamen Nav-Regel oben. Der Abstand steht am Wrapper UND am Trigger, weil der Pfeil
   je nach Variante innerhalb des Triggers sitzt (Aufklapp-Knopf) oder als eigener Knopf daneben
   (wenn die Beschriftung ein Link ist, siehe HeaderDropdownMenu). */
.shop-header__menu-trigger {
	gap: 0.25rem;
}

/* Eigener Aufklapp-Knopf neben einer verlinkten Beschriftung. Sieht aus wie ein Teil des
   Menüworts: gleiche gedämpfte Farbe, kein eigener Unterstrich (den trägt der Link daneben,
   und er erscheint über [aria-expanded="true"] ohnehin, sobald das Menü offen ist). */
.shop-header__menu-pfeilknopf {
	display: inline-flex;
	align-items: center;
	padding: 0;
	background: none;
	border: none;
	color: var(--muted-foreground, inherit);
	cursor: pointer;
	transition: color 0.2s ease-out;
}

.shop-header__menu-pfeilknopf:hover,
.shop-header__menu-pfeilknopf:focus-visible,
.shop-header__menu-pfeilknopf[aria-expanded="true"] {
	color: var(--primary, inherit);
}

.shop-header__menu-pfeil {
	flex: 0 0 auto;
	transition: transform 0.2s ease-out;
}

/* Zeigt "Menü ist offen", nicht "Maus ist drüber" - deshalb am Zustandsattribut statt an
   ":hover" (Nutzerwunsch). */
.shop-header__menu-trigger[aria-expanded="true"] .shop-header__menu-pfeil,
.shop-header__menu-pfeilknopf[aria-expanded="true"] .shop-header__menu-pfeil {
	transform: rotate(180deg);
}

/* Panel direkt unter dem Trigger, linksbündig, mit 0.75rem Luft nach oben. Diese Luft ist eine
   echte Lücke OHNE Element - die Maus würde auf dem Weg vom Wort ins Menü den Wrapper
   verlassen und "@onmouseleave" auslösen, das Menü klappte sofort wieder zu. Das "::before"
   unten überbrückt genau diese Lücke unsichtbar: es ist ein Nachfahre des Wrappers, und
   "mouseleave" feuert erst, wenn die Maus Element UND Nachfahren verlässt. */
.shop-header__menu-panel {
	position: absolute;
	top: 100%;
	left: 0;
	z-index: 20;
	/* Nutzer-Fund: ist das Fenster niedriger als das aufgeklappte Menü, waren die unteren
	   Einträge NICHT erreichbar. Grund: der Header ist "position: sticky; top: 0" und das Panel
	   hängt absolut darunter - was unten aus dem Fenster ragt, kann man nicht heranscrollen, denn
	   die Seite dahinter zu scrollen bewegt das Panel nicht mit. Betraf beide Dropdowns
	   (Kategorien wie Highlights) und wird mit den zusätzlichen Kategorien wahrscheinlicher.
	   Abgezogen werden Kopfzeile plus der Abstand darüber; "dvh" wegen der ein-/ausfahrenden
	   Adressleiste auf Mobilgeräten, "vh" bleibt als Rückfall für Browser ohne dvh davor stehen.

	   Gescrollt wird bewusst NICHT hier, sondern in ".shop-header__menu-panel-liste" darin -
	   zweiter Nutzer-Fund: ein Scrollbalken direkt am Panel läuft von Oberkante bis Unterkante
	   und schneidet die abgerundeten Ecken rechts ab. Innerhalb des Innenabstands bleibt er von
	   den Ecken weg. "hidden" hier hält die Rundung sauber, falls doch etwas übersteht. */
	max-height: calc(100vh - 8rem);
	max-height: calc(100dvh - 8rem);
	overflow: hidden;

	display: flex;
	flex-direction: column;
	gap: 0.125rem;
	margin-top: 0.75rem;
	padding: 0.5rem;
	width: var(--shop-header-menu-breite);
	/* Deckendes Panel (kein Glas) - liegt über wechselndem Seiteninhalt und muss immer lesbar
	   bleiben, genau wie .shop-header__burger-menu. */
	background: var(--background, #fff);
	border: 1px solid var(--border, #e4e4e7);
	border-radius: var(--radius, 0.5rem);
	box-shadow: var(--shadow-card, 0 4px 12px rgba(0, 0, 0, 0.15));
}

/* Nutzerwunsch: beim Überfahren bekommt der GESAMTE Eintrag eine Fläche - bewusst OHNE
   zusätzlichen Textfarbwechsel (anders als im Burger-/Footer-Panel), damit Titel und
   Untertitel ihr Größenverhältnis behalten und die Fläche allein die Auswahl anzeigt. */
/* Der eigentliche Scrollbereich des Dropdowns - siehe Kommentar am Panel, warum nicht dort.
   "min-height: 0" ist in einer Flex-Spalte nötig, sonst schrumpft ein Kind nie unter seine
   Inhaltshöhe und die Obergrenze des Panels bliebe wirkungslos. */
.shop-header__menu-panel-liste {
	display: flex;
	flex-direction: column;
	gap: 0.125rem;
	min-height: 0;
	overflow-y: auto;
	overscroll-behavior: contain;
}

.shop-header__menu-eintrag {
	display: flex;
	flex-direction: column;
	gap: 0.125rem;
	padding: 0.5rem 0.75rem;
	border-radius: var(--radius-sm, 0.25rem);
	color: inherit;
	text-decoration: none;
	transition: background-color 0.2s ease-out;
}

.shop-header__menu-eintrag:hover,
.shop-header__menu-eintrag:focus-visible {
	background: var(--surface, #f4f6f7);
}

.shop-header__menu-eintrag-titel {
	font-weight: 600;
}

/* Zweite Zeile eines Eintrags. Wird aktuell nur gerendert, wenn zum Eintrag überhaupt ein
   erklärender Zusatz existiert - die Highlights der alten Web-API liefern ausschließlich
   einen Namen (siehe Highlight.cs), dort bleibt der Eintrag deshalb einzeilig. */
.shop-header__menu-eintrag-untertitel {
	font-size: 0.8125rem;
	line-height: 1.3;
	color: var(--muted-foreground, inherit);
}

/* Burger-Menü (Nutzerwunsch: Hauptmenüpunkte zusammenfassen, wenn der Platz nicht reicht) -
   natives <details>/<summary>, kein JavaScript nötig. Liegt als erstes Kind INNERHALB von
   .shop-header__searchbar (Nutzerwunsch: direkt links neben der Produktsuche, siehe
   SiteHeader.razor) - "order: -1" wirkt jetzt innerhalb DIESES Flex-Containers und hält das
   Menü vor dem Sucheingabe-Formular. Ab dem Desktop-Breakpoint ausgeblendet, siehe
   @media (min-width: 992px) unten - dort ist wieder Platz für die normale .shop-header__nav. */
.shop-header__burger {
	position: relative;
	order: -1;
}

.shop-header__burger summary {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	border-radius: var(--radius-pill, 9999px);
	list-style: none;
	cursor: pointer;
	transition: background-color 0.2s ease-out, color 0.2s ease-out;
}

.shop-header__burger summary::-webkit-details-marker {
	display: none;
}

.shop-header__burger-menu {
	position: absolute;
	top: 100%;
	left: 0;
	/* Dieselbe Falle wie beim Dropdown-Panel (siehe dort): unter dem klebenden Header hängend
	   wäre alles unerreichbar, was aus dem Fenster ragt - und dieses Menü enthält ALLE
	   Hauptpunkte samt Kategorien und Highlights, ist also das längste von allen. Die Kopfzeile
	   ist im Burger-Fall (schmale Geräte) höher, deshalb ein größerer Abzug. Gescrollt wird auch
	   hier innen (".shop-header__burger-liste"), damit der Balken die runden Ecken in Ruhe lässt. */
	max-height: calc(100vh - 11rem);
	max-height: calc(100dvh - 11rem);
	overflow: hidden;
	z-index: 20;
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	margin-top: 0.5rem;
	padding: 0.75rem;
	min-width: 10rem;
	/* Breite aus dem Inhalt, nach oben gedeckelt auf den Bildschirm.
	   "width: max-content" ist hier nötig, nicht nur schön: das Panel ist absolut positioniert
	   und würde sich sonst am Inhalt orientieren - der ist aber der scrollende Kasten
	   (".shop-header__burger-liste", overflow-y: auto), und ein Scroll-Container meldet als
	   Mindestbreite 0. Ohne diese Zeile blieb das Panel real auf seiner min-width stehen, und die
	   zweite Spalte (siehe .shop-header__burger-liste--zwei-spalten) wurde auf 57 px
	   zusammengequetscht. */
	width: max-content;
	max-width: calc(100vw - 1.5rem);
	/* Deckendes Panel (kein Glas) - liegt über wechselndem Seiteninhalt und muss immer
	   lesbar bleiben, siehe auch .shop-footer__menu-item-kinder. */
	background: var(--card, #fff);
	color: var(--card-foreground, #18181b);
	border: 1px solid var(--border, #ddd);
	border-radius: var(--radius, 0.5rem);
	box-shadow: var(--shadow-card, 0 4px 12px rgba(0, 0, 0, 0.15));
}

/* Scrollbereich des Burger-Menüs - siehe ".shop-header__menu-panel-liste", gleiche Begründung. */
.shop-header__burger-liste {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	min-height: 0;
	overflow-y: auto;
	overscroll-behavior: contain;
}

/* Nutzerwunsch: gibt es Highlights oder zusätzliche Kategorien, stehen sie in einer zweiten
   Spalte neben den Warengruppen - sonst wird die Liste auf schmalen Geräten sehr lang.
   "align-items: start" hält beide Spalten oben bündig, statt die kürzere zu strecken.

   Beide Spalten bewusst "auto" (Inhaltsbreite) und NICHT "1fr auto": das Menü ist ein absolut
   positioniertes Panel und damit selbst nur so breit wie sein Inhalt - eine 1fr-Spalte hat darin
   keine Bezugsgröße und fiel real auf 0 px zusammen, die Spalten lagen übereinander. */
.shop-header__burger-liste--zwei-spalten {
	display: grid;
	grid-template-columns: auto auto;
	justify-content: start;
	align-items: start;
	gap: 0.5rem 1.25rem;
}

.shop-header__burger-spalte {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	min-width: 0;
}

/* Unterhalb des Desktop-Breakpoints (siehe @media (min-width: 992px) unten) soll NICHT der
   Konto-Bereich in eine zweite Zeile umbrechen, sondern die Suche (Nutzerwunsch) - deshalb
   hier volle Breite + höchste "order", damit Burger/Logo/Konto zusammen auf der ersten Zeile
   bleiben und die Suche als einzige auf eine eigene zweite Zeile fällt. Ab dem Desktop-
   Breakpoint wieder zurückgesetzt (kein Umbruch dort, siehe .shop-header__primary
   flex-wrap:nowrap). */
.shop-header__searchbar {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	width: 100%;
	order: 10;
}

/* Reines HTML-<input> (kein BbInput) - braucht daher einen eigenen Rahmen/Innenabstand, sonst
   entfernt der Tailwind-Reset von BlazorBlueprint jeglichen Standard-Rahmen (derselbe Fund wie
   bei den früheren Belege-Suchfeldern, siehe CLAUDE.md). */
/* Nutzerwunsch: rutscht die Suche (siehe .shop-header__searchbar) mangels Platz in eine eigene
   Zeile, soll das Eingabefeld dort gemeinsam mit den übrigen Symbolen (Burger/Filter) die
   GESAMTE verfügbare Breite einnehmen, statt in seiner schmalen Standardbreite stehen zu
   bleiben - "flex: 1" auf dem Formular UND auf dem Eingabefeld selbst (das Formular ist
   ebenfalls ein Flex-Container, siehe unten). */
.shop-header__suche {
	display: flex;
	align-items: center;
	gap: 0.25rem;
	flex: 1;
	min-width: 0;
}

.shop-header__suche input {
	flex: 1;
	min-width: 0;
	border: 1px solid var(--input, #ddd);
	border-radius: var(--radius-pill, 9999px);
	padding: 0.375rem 0.875rem;
	font: inherit;
	/* Das Eingabefeld bleibt bewusst deckend hell (--background) statt transluzent wie der
	   Header und braucht daher eine explizit dunkle Schriftfarbe (kein "color: inherit"). */
	color: var(--foreground, #18181b);
	background: var(--background, #fff);
}

/* Nutzerwunsch: das eingebaute Löschkreuz von <input type="search"> wird abgeschaltet.

   Es steht nicht überall zur Verfügung - Firefox kennt es gar nicht, und WebKit/Blink zeigen es
   erst, sobald das Feld angefasst wurde (auf einer frisch geladenen Ergebnisseite also nicht).
   Ein Bedienelement, das je nach Browser da ist oder nicht, ist schlechter als keins: der
   Lösch-Link rechts neben dem Feld (siehe SiteHeader.razor) macht dasselbe, sieht überall gleich
   aus und setzt die Suche wirklich zurück, statt nur das Feld zu leeren. */
.shop-header__suche input::-webkit-search-cancel-button {
	-webkit-appearance: none;
	appearance: none;
	display: none;
}

.shop-header__suche input:focus-visible {
	outline: 3px solid var(--ring, currentColor);
	outline-offset: 2px;
}

/* Nutzerwunsch: Button rechts neben dem Suchfeld, um einen aktiven Suchbegriff zu entfernen. */
.shop-header__suche-loeschen {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 1.5rem;
	height: 1.5rem;
	border-radius: var(--radius-pill, 9999px);
	color: var(--muted-foreground, #71717a);
	text-decoration: none;
	font-size: 0.875rem;
	flex: 0 0 auto;
	transition: background-color 0.2s ease-out, color 0.2s ease-out;
}

.shop-header__filter-toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	background: none;
	border: none;
	padding: 0;
	border-radius: var(--radius-pill, 9999px);
	cursor: pointer;
	transition: background-color 0.2s ease-out, color 0.2s ease-out;
	/* Nutzerwunsch: auf den Fokus-Ring verzichten - er blieb nach dem Klick hängen und war
	   kaum von einem "aktiv"-Zustand zu unterscheiden. Der echte Zustand kommt jetzt über
	   ".shop-header__filter-toggle--aktiv" (siehe SiteHeader.razor). */
	outline: none;
}

/* Alle Symbol-Schaltflächen im Header teilen denselben Hover: ein runder, sehr heller
   Untergrund plus Einfärbung in der Primärfarbe. Bewusst "--surface" (neutrales Grau) statt
   "--accent" (warmer Blassorange-Ton) - in der Kopfzeile bleibt der Orangeton dem
   Anmelden-CTA und den Navigations-Unterstrichen vorbehalten, damit nicht jedes Überfahren
   eines Symbols farbig aufleuchtet. Inhaltsflächen nutzen dagegen --accent. */
.shop-header__suche-loeschen:hover,
.shop-header__suche-loeschen:focus-visible,
.shop-header__filter-toggle:hover,
.shop-header__account-trigger:hover,
.shop-header__account-trigger:focus-visible,
.shop-header__cart:hover,
.shop-header__cart:focus-visible,
.shop-header__burger summary:hover {
	background: var(--surface, #f4f6f7);
	color: var(--primary, inherit);
}

/* Kräftiger als der Hover, damit "Filter ist offen" eindeutig vom bloßen Überfahren
   unterscheidbar bleibt. */
.shop-header__filter-toggle--aktiv,
.shop-header__filter-toggle--aktiv:hover {
	background: var(--primary-soft, rgba(0, 0, 0, 0.15));
	color: var(--primary, inherit);
}

.shop-header__account {
	display: flex;
	align-items: center;
	gap: var(--shop-gap);
	margin-left: auto;
}

.shop-header__account-trigger {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	background: none;
	border: none;
	padding: 0;
	border-radius: var(--radius-pill, 9999px);
	font: inherit;
	color: inherit;
	cursor: pointer;
	transition: background-color 0.2s ease-out, color 0.2s ease-out;
}

/* Nutzerwunsch: Highlights im Burger-Menü nicht mehr hinter einem zweiten, eingeklappten
   <details> verstecken, sondern immer sichtbar und eingerückt direkt unter "Kategorien". */
/* Zwischenüberschrift je Gruppe im Burger-Menü ("Highlights", "Sortimente") - dieselbe Gliederung
   wie im Filterbereich der Artikelsuche, damit beide Wege gleich aussehen. */
.shop-header__burger-gruppentitel {
	margin-top: 0.25rem;
	font-size: 0.75rem;
	font-weight: 600;
	opacity: 0.7;
}

.shop-header__burger-highlight {
	padding-left: 0.75rem;
}

/* Menüeinträge im Burger-/Footer-Panel bekommen denselben Hover wie die Symbol-Schaltflächen
   oben (heller Untergrund + Primärfarbe), nur eckiger statt vollrund - es sind Textzeilen,
   keine runden Symbole. */
.shop-header__burger-menu a,
.shop-footer__menu-item-kinder a {
	padding: 0.25rem 0.5rem;
	border-radius: var(--radius-sm, 0.25rem);
	color: inherit;
	text-decoration: none;
	transition: background-color 0.2s ease-out, color 0.2s ease-out;
}

.shop-header__burger-menu a:hover,
.shop-header__burger-menu a:focus-visible,
.shop-footer__menu-item-kinder a:hover,
.shop-footer__menu-item-kinder a:focus-visible {
	background: var(--surface, #f4f6f7);
	color: var(--primary, inherit);
}

/* Warenkorb-Symbol mit Amazon-artigem Zähler-Badge oben rechts am Symbol (Nutzerwunsch, ersetzt
   das bisherige Emoji + danebenstehende Zahl). */
.shop-header__cart {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2.25rem;
	height: 2.25rem;
	border-radius: var(--radius-pill, 9999px);
	transition: background-color 0.2s ease-out, color 0.2s ease-out;
}

.shop-header__cart-icon {
	position: relative;
	display: inline-flex;
}

.shop-header__cart-count {
	position: absolute;
	top: -0.4rem;
	right: -0.5rem;
	min-width: 1.1rem;
	height: 1.1rem;
	padding: 0 0.25rem;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: var(--radius-pill, 9999px);
	/* War ein fester Amazon-artiger Orangeton auf dem früher grauen Header. Jetzt am
	   Theme hängend, damit ein Betreiber-Retint den Zähler mitnimmt. */
	background: var(--primary, #f90);
	color: var(--primary-foreground, #111);
	font-size: 0.7rem;
	font-weight: 700;
	line-height: 1;
}

/* BbDropdownMenuContent rendert ChildContent innerhalb eines zusätzlichen "p-1"-Wrapper-Divs -
   die Zweispaltigkeit muss deshalb auf dessen direktes Kind-Div wirken, nicht auf die
   Content-Komponente selbst (deren Root-Element bereits andere Flex-/Layout-Klassen trägt). */
.shop-header__account-menu > div {
	display: flex;
	gap: 1.5rem;
}

.shop-header__account-menu-col {
	display: flex;
	flex-direction: column;
	gap: 0.125rem;
	min-width: 10rem;
}

/* Nutzerwunsch: die beiden Spalten (Profil-/Kontoeinträge links, Belege-/Posten-Übersicht
   rechts) zusätzlich durch eine senkrechte Linie trennen - der reine Abstand ließ sie wie
   eine einzige, umbrechende Liste aussehen. Die Linie hängt an der ZWEITEN Spalte
   ("+"-Geschwister-Selektor), damit sie automatisch entfällt, wenn es nur eine Spalte gibt. */
.shop-header__account-menu-col + .shop-header__account-menu-col {
	padding-left: 1.5rem;
	border-left: 1px solid var(--border, #e4e4e7);
}

/* Waagerechte Trennlinie innerhalb einer Spalte (aktuell vor "Abmelden", siehe
   SiteHeader.razor). Die negativen Seitenränder ziehen sie über die Innenabstände der
   Menüeinträge hinaus, damit sie die Spalte wirklich durchtrennt und nicht als kurzer
   Strich in der Mitte schwebt. */
.shop-header__account-menu-trenner {
	height: 1px;
	margin: 0.375rem -0.25rem;
	background: var(--border, #e4e4e7);
}

/* "Abmelden" soll unabhängig von der Eintragsanzahl der Nachbarspalte immer unten stehen -
   sonst hängt die Trennlinie darüber auf halber Höhe in der Luft. */
.shop-header__account-menu-col .shop-header__account-menu-trenner {
	margin-top: auto;
}

.shop-header__account-menu-logout-form {
	margin: 0;
}

/* Layout/Typografie (Größe, Abstand) kommen bereits über dieselben Utility-Klassen wie bei
   BbDropdownMenuItem (siehe SiteHeader.razor) - hier nur der native <button>-Reset, damit
   Browser-Standardschrift/-Rahmen nicht durchschlagen. WICHTIG: kein "font"-Shorthand hier
   setzen - das würde die "text-sm"-Utility-Klasse (Schriftgröße/Zeilenhöhe) überschreiben,
   da webshop.css nach blazorblueprint.css geladen wird (Kaskade gewinnt bei gleicher
   Spezifität) - echter Fund, der zunächst zu abweichender Schriftgröße bei "Abmelden" führte. */
.shop-header__account-menu-logout {
	text-align: left;
	background: none;
	border: none;
	font-family: inherit;
	cursor: pointer;
	/* Nutzerwunsch: deutlich herausstechen - fett UND in der Primärfarbe (Orange), damit
	   "Abmelden" auf einen Blick von den gleichrangig aussehenden Profil-Einträgen zu
	   unterscheiden ist. Bewusst das Theme-Token statt eines festen Orangetons, damit ein
	   Betreiber-Retint den Eintrag mitnimmt (siehe theme-tokens.css). */
	font-weight: 700;
	color: var(--primary, inherit);
}

/* Der Hover-Effekt muss hier EXPLIZIT nachgebaut werden, obwohl die Tailwind-Utility-Klasse
   "hover:bg-accent" bereits auf dem Button steht (siehe SiteHeader.razor): Tailwind v4 kapselt
   seine Utilities in "@layer utilities", und Layer-Regeln verlieren IMMER gegen unlayered CSS
   (wie dieser Datei), unabhängig von Spezifität/Reihenfolge - echter Fund ("Abmelden" hatte
   dadurch trotz gesetzter Utility-Klasse keinen sichtbaren Hover-Effekt). */
.shop-header__account-menu-logout:hover {
	background: var(--surface, rgba(0, 0, 0, 0.05));
	color: var(--primary, inherit);
}

/* Nutzerwunsch: Texte (Kontakt/Hotline/Newsletter/Downloads/Datenschutz/Impressum) und
   Menüeinträge (konfigurierbare Erweiterungen, siehe unten) zweispaltig statt in einer
   einzigen, umbrechenden Zeile - jede Spalte listet ihre Links untereinander. */
/* Spaltenraster analog zum Website-Footer (Nutzerwunsch): linksbündig im selben Inhaltsraster
   wie der Seiteninhalt (--shop-max-width), Spalten oben ausgerichtet. Bewusst CSS-Grid statt
   des vorherigen "flex + justify-content:center": bei nur zwei vorhandenen Spalten schob die
   Zentrierung beide in die Mitte und ließ links eine große Leerfläche - mit festen Rasterspuren
   bleiben sie an ihrem Platz, unabhängig davon, wie viele Spalten tatsächlich gerendert werden
   (die Extras-Spalte entfällt für anonyme Besucher bzw. ohne konfigurierte Menüeinträge).
   Mobil einspaltig, ab Tablet zwei, ab Desktop drei Spuren. */
.shop-footer__spalten {
	display: grid;
	grid-template-columns: 1fr;
	gap: calc(var(--shop-gap) * 2);
	align-items: start;
	padding: calc(var(--shop-gap) * 2) var(--shop-gap);
	max-width: var(--shop-max-width);
	margin: 0 auto;
}

@media (min-width: 576px) {
	.shop-footer__spalten {
		grid-template-columns: repeat(2, minmax(0, 1fr));
	}
}

/* Nutzerwunsch/echter Fund: "1fr" je Spur reserviert IMMER ein gleich großes Drittel der
   kompletten Rasterbreite, unabhängig vom tatsächlichen Inhalt - bei kurzem Inhalt (z.B. nur
   ein einzelner Menüeintrag "DeDeNet Newsletter" in der dritten Spur) klaffte dadurch zwischen
   der Texte- und der Menü-Spalte eine riesige Leerfläche, die wie ein Verrutschen wirkte statt
   einer bewusst gesetzten, ausgewogenen Verteilung. "max-content" lässt jede Spur nur so breit
   werden wie ihr Inhalt - die Spalten rücken dadurch enger zusammen (Abstand bleibt weiterhin
   der definierte Grid-"gap"), überschüssiger Platz sammelt sich am rechten Rand statt zwischen
   den Spalten. */
@media (min-width: 992px) {
	.shop-footer__spalten {
		grid-template-columns: repeat(3, minmax(0, max-content));
		gap: calc(var(--shop-gap) * 3);
	}
}

.shop-footer__spalte {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	/* Verhindert, dass ein langer Menüeintrag die Rasterspur über ihre Spur hinaus aufdehnt
	   ("minmax(0, 1fr)" allein reicht dafür bei Flex-Kindern nicht). */
	min-width: 0;
}

/* Markenspalte: Logo über dem Betreibernamen, beides linksbündig - im Unterschied zu den
   Linkspalten kein Listenabstand, sondern ein kompakter Block. */
.shop-footer__spalte--marke {
	gap: 0.75rem;
}

.shop-footer__logo {
	/* Nutzerwunsch 09.09.2026: von 2.5rem auf 3.5rem, also auf dieselbe Höhe wie im Header. Die
	   ursprüngliche Begründung (Footer ist sekundär, soll der Kopfzeile nicht Konkurrenz machen)
	   war bei den real hinterlegten Logos zu vorsichtig - sie sind meist breit und flach, 2.5rem
	   ließen davon zu wenig übrig. */
	height: 3.5rem;
	width: auto;
	align-self: flex-start;
}

.shop-footer__betreiber {
	font-weight: 600;
}

/* Konfigurierbare Menü-Erweiterungen mit Untermenü (Nutzerwunsch, siehe SiteFooter.razor) -
   natives <details>/<summary>, analog zum Burger-Menü im Header. */
.shop-footer__menu-item {
	position: relative;
}

.shop-footer__menu-item summary {
	cursor: pointer;
	list-style: none;
}

.shop-footer__menu-item summary::-webkit-details-marker {
	display: none;
}

/* Versionshinweis (Nutzerwunsch): bewusst kaum wahrnehmbar - kleinste sinnvolle Schriftgröße,
   stark gedämpfte Farbe, eigene schmale Zeile unterhalb des Spaltenrasters statt einer eigenen
   Rasterspur, damit er die Footer-Struktur nicht stört. */
.shop-footer__version {
	max-width: var(--shop-max-width);
	margin: 0 auto;
	padding: 0 var(--shop-gap) calc(var(--shop-gap) * 0.75);
	text-align: right;
	font-size: 0.6875rem;
	color: var(--muted-foreground, #a1a1aa);
}

.shop-footer__menu-item-kinder {
	position: absolute;
	bottom: 100%;
	left: 50%;
	transform: translateX(-50%);
	z-index: 20;
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	margin-bottom: 0.5rem;
	padding: 0.75rem;
	min-width: 10rem;
	background: var(--card, #fff);
	color: var(--card-foreground, #18181b);
	border: 1px solid var(--border, #ddd);
	border-radius: var(--radius, 0.5rem);
	box-shadow: var(--shadow-card, 0 -4px 12px rgba(0, 0, 0, 0.15));
}

@media (min-width: 992px) {
	.shop-header__primary {
		flex-wrap: nowrap;
	}

	/* Ausgeschriebene Hauptnavigation zurück, Burger-Menü ausgeblendet - ab hier reicht der
	   Platz wieder (Nutzerwunsch-Breakpoint für Punkt 3). */
	.shop-header__nav {
		display: flex;
		order: 0;
		width: auto;
	}

	.shop-header__burger {
		display: none;
	}

	/* Ab hier reicht der Platz für die feste Filter-Sidebar (siehe .artikel-suche-page__filter
	   weiter unten) - der Umschalter im Header wird deshalb nicht mehr gebraucht. */
	.shop-header__filter-toggle {
		display: none;
	}

	/* Zurück zur normalen Inline-Position direkt nach .shop-header__nav (kein Umbruch mehr
	   nötig, .shop-header__primary ist ab hier flex-wrap:nowrap).

	   Nutzerwunsch: die Produktsuche stand hier "recht alleine und klein da" - sie NIMMT JETZT
	   DEN FREIEN PLATZ EIN ("flex: 1 1 ...") statt in ihrer Standardbreite rechts zu kleben.
	   Damit ist auch das frühere "margin-left: auto" entfallen: es sammelte den gesamten freien
	   Platz VOR der Suche, genau das Gegenteil. Den Rechtsschub übernimmt weiter die
	   Konto-Gruppe (".shop-header__account" hat selbst "margin-left: auto") - dadurch bleibt sie
	   am rechten Rand, während die Suche den Raum zwischen Navigation und Symbolen füllt.

	   "min-width" statt einer festen Breite: schrumpft der Platz, gibt die Suche bis zu dieser
	   Grenze nach, danach der lange Firmenname links (siehe ".shop-header__betreiber--kompakt").
	   Unterhalb dieses Breakpoints bleibt es beim bisherigen Verhalten - dort fällt die Suche
	   als einzige in eine eigene zweite Zeile (siehe ".shop-header__searchbar" oben). */
	.shop-header__searchbar {
		width: auto;
		order: 0;
		flex: 1 1 14rem;
		min-width: 10rem;
	}
}

/* Katalog-Block: Artikelsuche (Filter-Sidebar + Ergebnis-Grid in drei Ansichten),
   Artikeldetails (Galerie + Info-Spalte) - siehe CLAUDE.md Feature-Block "Katalog".
   Mobile-first: Sidebar/Filter und mehrspaltige Grids erst ab Tablet-Breite. */

.artikel-grid {
	display: grid;
	grid-template-columns: repeat(2, 1fr);
	gap: var(--shop-gap);
}

.artikel-suche-page {
	display: flex;
	flex-direction: column;
	gap: var(--shop-gap);
}

/* Standardmäßig (Mobile/Tablet) ausgeblendet - über das Filter-Icon im Header einblendbar
   (Nutzerwunsch, ArtikelFilterSichtbarkeitState). Ab dem Desktop-Breakpoint (@media
   (min-width: 992px) unten) immer sichtbar, unabhängig vom Umschalter-Zustand. */
.artikel-suche-page__filter {
	display: none;
	flex-direction: column;
	gap: var(--shop-gap);
}

.artikel-suche-page__filter--offen {
	display: flex;
}

/* Echter Fund: <a>-Elemente sind ohne eigene Regel inline und standen nebeneinander statt als
   Liste untereinander (Nutzerwunsch: Kategorien sollen wie ein Baum/eine Liste wirken, nicht
   wie nebeneinanderstehende Badges). */
.artikel-suche-page__warengruppen {
	display: flex;
	flex-direction: column;
	gap: 0.25rem;
}

/* Zwischenzeile vor den zusätzlichen Kategorien (Auftragsvorlagen mit ECOMAnzeige = Startmenu,
   z.B. "Garnituren"). Sie trennt zwei Dinge, die zwar gleich bedient werden, aber aus
   verschiedenen Quellen stammen: oben der Warengruppenstamm mit Trefferzahlen, unten Vorlagen
   ohne Zahl. Bewusst leiser gesetzt als die Feldbeschriftung "Kategorien" darüber - es ist eine
   Untergliederung, kein zweites Filterfeld. */
.artikel-suche-page__warengruppen-zwischenzeile {
	margin: 0.75rem 0 0.25rem;
	font-size: 0.75rem;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.03em;
	color: var(--muted-foreground, #71717a);
}

/* Kinder der aktiven Hauptgruppe eine Ebene einrücken (Nutzerwunsch: die Kategorien bleiben auf
   jeder Seite sichtbar, der Zweig mit der aktiven Kategorie klappt sich eine Ebene auf). */
.artikel-suche-page__warengruppen-kinder {
	display: flex;
	flex-direction: column;
	gap: 0.25rem;
	margin-left: 1rem;
}

/* Kategorie-Einträge im Filter: bis hierher gab es KEINE Rückmeldung beim Überfahren
   (Nutzer-Fund) - dieselbe Behandlung wie die Einträge in den Dropdown-Panels
   (Burger-/Footer-Menü): heller Untergrund plus Einfärbung in der Primärfarbe. Innenabstand
   und Radius sind dafür nötig, damit der Untergrund als eigene Fläche lesbar ist und nicht
   direkt am Text klebt; der negative Außenabstand links hält die Beschriftungen trotz des
   neuen Innenabstands bündig mit den übrigen Filter-Feldern. */
.artikel-suche-page__warengruppen-link {
	padding: 0.25rem 0.5rem;
	margin-left: -0.5rem;
	border-radius: var(--radius-sm, 0.25rem);
	text-decoration: none;
	transition: background-color 0.2s ease-out, color 0.2s ease-out;
}

.artikel-suche-page__warengruppen-link:hover,
.artikel-suche-page__warengruppen-link:focus-visible {
	background: var(--surface, #f4f6f7);
	color: var(--primary, inherit);
}

.artikel-suche-page__warengruppen-link--aktiv {
	font-weight: 600;
	color: var(--primary, #18181b);
}

/* Artikel ohne Warengruppe (Nutzerwunsch, siehe ArtikelSuche.razor). Bewusst kein Link und
   deshalb auch ohne Hover-Rückmeldung - gedämpft, damit es als Hinweis und nicht als weitere
   Kategorie gelesen wird. Der Innenabstand und der negative Außenabstand entsprechen den
   Kategorie-Einträgen, damit die Beschriftung bündig darunter steht. */
.artikel-suche-page__warengruppen-ohne {
	display: block;
	padding: 0.25rem 0.5rem;
	margin-left: -0.5rem;
	color: var(--muted-foreground, #71717a);
	font-size: 0.875rem;
}

/* Paging und "Alle übernehmen" in derselben Zeile unter der Liste (Nutzerwunsch) - "Alle
   übernehmen" per margin-left:auto auf .artikel-suche-page__alle-uebernehmen rechtsbündig,
   auch wenn kein Paging vorhanden ist (nur eine Seite). */
.artikel-suche-page__unterzeile {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--shop-gap);
	margin-top: var(--shop-gap);
}

.artikel-suche-page__alle-uebernehmen {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	margin-left: auto;
}

.artikel-suche-page__alle-uebernehmen-fehler {
	color: var(--destructive, #b91c1c);
	font-size: 0.8rem;
}

/* Ladeanzeige (siehe LadeAnzeige.razor). Der Text daneben ist nur für Screenreader da - sichtbar
   ist der Kringel. Nicht display:none, das würde ihn auch der Vorlesehilfe entziehen; stattdessen
   das übliche "visually hidden"-Muster. Etwas Luft nach oben, damit der Kringel nicht am
   Seitenkopf klebt, während die Ergebnisliste noch fehlt. */
.lade-anzeige {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: calc(var(--shop-gap) * 2) 0;
	color: var(--muted-foreground, #71717a);
}

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

.artikel-suche-page__pagination {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.25rem;
}

/* Auslassung zwischen zwei Seitenblöcken - kein Button, siehe ArtikelSuche.razor. Die Breite
   entspricht ungefähr einem Zahlen-Button, damit die Leiste beim Blättern nicht springt. */
.artikel-suche-page__pagination-luecke {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 2rem;
	color: var(--muted-foreground);
	user-select: none;
}

/* Nutzerwunsch: "Listenpreise!"-Hinweis überall gleich - EIN gesammeltes Vorkommen pro Seite
   (siehe ArtikelSuche.razor: nicht mehr bei jeder einzelnen Karte), kompakt mit rotem Rahmen
   statt raumgreifendem Fließtext, auf ALLEN Seiten mit Preisanzeige (Artikelliste,
   Artikeldetails, Warenkorb, Bestellabschluss - fehlte dort zunächst komplett). */
.artikel-suche-page__listenpreis,
/* Kein roter Rahmen: der Hinweis ist eine reine Information ("Listenpreis, echter Preis kommt
   erst bei der Faktura") und keine Warnung, mit rotem Rahmen sah er aber wie eine aus. Deshalb
   ein neutraler, gedämpfter Hinweiston auf hellem --muted-Untergrund.

   Der Rahmen war am 09.09.2026 kurz zurückgeholt worden ("sonst passt er nicht in das
   Gesamtbild") und ist auf Nutzerentscheidung noch am selben Tag wieder entfallen - die
   Begründung oben hat weiter Bestand. Geblieben ist aus dem Zwischenschritt allein die
   deutlichere Rundung. Konsistent an allen Stellen, an denen exakt derselbe Text erscheint
   (Artikelliste/Artikeldetails/Warenkorb/Bestellabschluss). */
.artikel-details-page__listenpreis,
.warenkorb-page__listenpreis,
/* Der Gegenpart-Hinweis für Kunden MIT dem Recht "EComKIP" ("Bei allen Preisen handelt es sich
   um Netto Listenpreise.", siehe Warenkorb.razor) - bewusst optisch identisch, es ist dieselbe
   Art von Information an derselben Stelle, nur ein anderer Text. */
.warenkorb-page__preishinweis,
.bestellabschluss-page__listenpreis,
/* Gutschein-/Sonderrabatt-Hinweis unter der Gesamtsumme (Nutzerentscheidung zu Punkt 20/22,
   siehe Bestellabschluss.razor) - bewusst derselbe Hinweiston wie der Listenpreis-Kasten. */
.bestellabschluss-page__rabatt-hinweis {
	margin: var(--shop-gap) 0 0;
	padding: 0.5rem 0.75rem;
	background: var(--muted, #f4f4f5);
	/* Nutzerwunsch: sichtbar abgerundet. Vorher --radius-sm (4px) - auf einem so flachen Kasten
	   war davon kaum etwas zu erkennen. Bewusst NICHT --radius: das sind in diesem Theme 20px, bei
	   37px Kastenhöhe wären die Enden damit halbrund und der Hinweis sähe aus wie eine Pille. */
	border-radius: 0.75rem;
	font-size: 0.875rem;
	color: var(--muted-foreground, #52525b);
}

.artikel-grid--liste {
	grid-template-columns: 1fr;
}

.artikel-card {
	display: flex;
	flex-direction: column;
	gap: 0.375rem;
	padding: 0.75rem;
	/* Weiße Fläche, damit der Seitenverlauf (siehe ".shop-layout") nicht durch die Artikel läuft
	   (Nutzerwunsch 09.09.2026). Gilt für BEIDE Ansichten - die Kachel- und die Listenansicht
	   unterscheiden sich nur in Anordnung und Bildgröße, die Karte ist dieselbe. */
	background: var(--card, #fff);
	border: 1px solid var(--border, #ddd);
	border-radius: var(--radius, 0.5rem);
	box-shadow: var(--shadow-soft, none);
	transition: box-shadow 0.2s ease-out, transform 0.2s ease-out;
}

.artikel-card:hover {
	transform: translateY(-2px);
	box-shadow: var(--shadow-card, none);
}

/* Bewusst NICHT mehr das äußere .artikel-card selbst der Link (Nutzerwunsch: Preis/Menge/
   Warenkorb-Button in der Listenansicht) - ein Klick darauf würde sonst zusätzlich zur
   Artikeldetailseite navigieren. Nur Bild+Info bleiben klickbar. */
.artikel-card__link {
	display: flex;
	flex-direction: column;
	gap: 0.375rem;
	text-decoration: none;
	color: inherit;
}

/* Die Karte hebt sich beim Überfahren bereits an (".artikel-card:hover"), der Titel blieb
   dabei aber unverändert - erst die Einfärbung macht deutlich, dass die Bezeichnung selbst
   der Link zur Detailseite ist. Bewusst an der KARTE aufgehängt (nicht am Link), damit auch
   das Überfahren des Bildes den Titel mitfärbt: Bild und Text liegen im selben <a>.
   Der zweite Selektor deckt die Tastaturbedienung ab - dort gibt es kein :hover. */
.artikel-card:hover .artikel-card__bezeichnung,
.artikel-card__link:focus-visible .artikel-card__bezeichnung {
	color: var(--primary, inherit);
}

.artikel-card__bezeichnung {
	transition: color 0.2s ease-out;
}

.artikel-card--liste {
	flex-direction: row;
	align-items: center;
	flex-wrap: wrap;
}

.artikel-card--liste .artikel-card__link {
	flex-direction: row;
	align-items: center;
	flex: 1 1 auto;
	min-width: 0;
}

.artikel-card__bild {
	width: 100%;
	aspect-ratio: 1;
	object-fit: contain;
}

.artikel-card--liste .artikel-card__bild {
	width: 4rem;
	height: 4rem;
	flex-shrink: 0;
}

.artikel-card__info {
	display: flex;
	flex-direction: column;
	gap: 0.125rem;
	min-width: 0;
}

/* Bestandsanzeige (ArtikelLagerbestand.razor) HINTER der Artikelnummer, rechtsbündig -
   Nutzerwunsch 18.09.2026, echter Fund: eine reine "min-width" auf der Nummer selbst (erster
   Versuch) hatte KEINE Wirkung, weil ".artikel-card__nummer" ein normales Inline-Element ist -
   und Inline-Elemente ignorieren "width"/"min-width" (CSS-Spezifikation, kein Blazor-Fund).
   Jetzt bekommt die ZEILE selbst eine feste Breite, "justify-content: space-between" schiebt
   die Anzeige darin konsequent an den rechten Rand - bündig untereinander, nicht mehr nur der
   zufällig ähnlichen Zahlenlänge nach.
   Nutzerwunsch 18.09.2026 (zweite Anpassung): die Breite ist jetzt bewusst auf die Breite von
   "Fokusartikel"+"Topseller" zusammen kalibriert (gemessen: 147.97px = 9.25rem), damit die
   Pille am rechten Rand GENAU mit dem Ende der Badge-Zeile darunter abschließt - bei DIESEM
   Testbestand haben praktisch alle Artikel exakt diese beiden Badges, deshalb passt es hier
   optisch zusammen. Das ist kein allgemeingültiger Mechanismus: ein Artikel mit anderen/
   weniger/mehr Badges (z.B. nur "Aktion", oder gar keine) hätte eine andere Badge-Zeilenbreite,
   und die Pille bliebe dann trotzdem bei dieser festen Breite stehen - keine echte, für jede
   Badge-Kombination garantierte Ausrichtung, sondern eine auf den Regelfall kalibrierte Näherung.
   Eine ungewöhnlich lange Artikelnummer wird abgeschnitten ("…") statt die Zeile zu sprengen
   und die Ausrichtung wieder zu zerstören. */
.artikel-card__nummer-zeile {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.4rem;
	width: 9.25rem;
}

.artikel-card__nummer {
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
}

/* Preis/Menge/Warenkorb-Button nur in der Listenansicht (Nutzerwunsch, siehe ArtikelCard.razor).
   "margin-left: auto" hält die Gruppe rechtsbündig, wenn sie mangels Platz in .artikel-card--liste
   (Flex-Row mit Wrap) in eine eigene Zeile umbricht (Nutzerwunsch: das Umbrechen selbst ist
   erwünscht, die Felder sollen dabei aber untereinander/rechtsbündig bleiben statt am linken
   Rand zu kleben) - wirkt unabhängig davon, ob umgebrochen wurde oder nicht. */
.artikel-card__warenkorb {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	flex-wrap: wrap;
	margin-left: auto;
}

.artikel-preis {
	font-weight: 600;
	white-space: nowrap;
}


/* Echter Fund (Regression nach Umstieg auf MengenSteller): diese Klasse landet jetzt auf dem
   STELLER-WRAPPER (zwei Schaltflächen + Eingabefeld), nicht mehr auf einem bloßen Eingabefeld -
   die alte feste Breite von 5.5rem war schmaler als die Summe aus beiden 2rem-Schaltflächen und
   dem mindestens 2.75rem breiten Eingabefeld (siehe MengenSteller.razor/.menge-steller). Der
   Wrapper hat zusätzlich "overflow: hidden" (rundet die Ecken sauber ab) - dadurch wurde nicht
   einfach nur zusammengequetscht, sondern die Plus-Schaltfläche am rechten Rand komplett
   abgeschnitten. Keine eigene Breite mehr erzwingen, die natürliche Breite des Stellers reicht. */

.artikel-card__warenkorb-fehler {
	color: var(--destructive, #b91c1c);
	font-size: 0.8rem;
}

/* Nutzerwunsch: mehrere Status-Badges (Fokusartikel/Topseller/Aktion) nebeneinander statt
   untereinander - ohne diesen Wrapper stünde jeder <span> als eigenes Flex-Item der
   Spalten-Eltern (.artikel-card__info) einzeln untereinander. */
.artikel-card__badges {
	display: flex;
	flex-wrap: wrap;
	gap: 0.375rem;
}

.artikel-card__badge {
	align-self: flex-start;
	padding: 0.125rem 0.5rem;
	border-radius: var(--radius-pill, 9999px);
	background: var(--primary, #333);
	color: var(--primary-foreground, #fff);
	font-size: 0.75rem;
}

.artikel-details-page {
	display: flex;
	flex-direction: column;
	gap: var(--shop-gap);
}

/* Nutzerwunsch: dreispaltiges Layout (Bilder / Bezeichnung+Beschreibung+Meldungen /
   Preis+Bestand+Warenkorb) - auf Mobile untereinander gestapelt, ab dem Desktop-Breakpoint
   nebeneinander (siehe @media (min-width: 992px) unten). */
.artikel-details-page__hauptbereich {
	display: flex;
	flex-direction: column;
	gap: var(--shop-gap);
}

/* Nutzerwunsch: die Artikelbezeichnung soll prominenter wirken - ohne eigene Regel bleibt sie
   sonst bei der (durch BlazorBlueprints Tailwind-Reset stark reduzierten) Standard-Größe eines
   <h1>. */
/* Die Artikelbezeichnung war die einzige fette Überschrift, die nach der Umstellung übrig
   geblieben wäre - mitgezogen, sonst widerspräche sie genau der Begründung ("filigran"). Größe,
   Stärke und Zeilenhöhe kommen aus der globalen h1-Regel; hier bleibt der engere Abstand, weil
   direkt darunter die Artikelnummer steht. */
.artikel-details-page__kaufbox h1 {
	margin: 0 0 0.25rem;
}

.artikel-details-page__spalte-bilder {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

/* Immer nur EIN Bild groß zeigen (statt vorher alle Bilder untereinander in voller Größe),
   darunter eine horizontal scrollbare Thumbnail-Leiste. Echter Fund: unterschiedlich hohe
   Bilder ließen beim Wechsel die gesamte Seite darunter springen - "aspect-ratio" erzwingt eine
   FESTE, vom jeweiligen Bild unabhängige Höhe, "object-fit: contain" verhindert Verzerrung/
   Zuschnitt.
   Nutzerwunsch (nach mehreren Anläufen): eine relative Breite ("50% der Spalte") hing von der
   Breite der BILDSPALTE ab - die wurde aber per "flex: 1 1 420px" (siehe Breakpoint-Block
   unten) bewusst stretch-fähig gehalten, wodurch zwischen dem (dadurch zentrierten) Bild und
   der danebenstehenden Kaufbox eine tote Lücke in halber Spaltenbreite entstand, UND das Bild
   nicht mit dem darunterliegenden Abschnitte-Block (Staffelpreise/Historie) linksbündig war
   (der spannt die volle Breite auf, das zentrierte Bild aber nicht). Eine feste "max-width"
   löst beides zugleich: sie hängt nicht mehr von der Spaltenbreite ab (kein Zentrieren nötig,
   "margin" bleibt bei 0 -> linksbündig, exakt auf einer Linie mit dem Abschnitte-Block
   darunter), und die Bildspalte selbst schrumpft jetzt auf ihren Inhalt (siehe
   ".artikel-details-page__spalte-bilder" im Breakpoint-Block: "flex: 0 0 auto" statt "1 1
   420px"), wodurch die Kaufbox direkt im normalen Spaltenabstand danebenrückt statt durch
   Leerraum von ihr getrennt zu sein. */
.artikel-details-page__hauptbild {
	width: 100%;
	max-width: 448px; /* Nutzerwunsch: x1.4 gegenüber den vorherigen 320px */
	aspect-ratio: 1 / 1;
	object-fit: contain;
	cursor: zoom-in;
	/* Nutzerwunsch: Rahmen entfernt - jetzt rahmenlos wie Kaufbox/Staffelpreise-Karte
	   daneben/darunter, nur der Radius bleibt. */
	border-radius: var(--radius, 0.5rem);
	padding: 1.5rem;
	box-sizing: border-box;
	display: block;
	margin: 0;
}

.artikel-details-page__thumbnails {
	display: flex;
	gap: 0.5rem;
	overflow-x: auto;
	max-width: 100%;
	padding-bottom: 0.25rem;
}

.artikel-details-page__thumbnail {
	width: 4rem;
	height: 4rem;
	object-fit: contain;
	flex: 0 0 auto;
	border: 2px solid transparent;
	border-radius: var(--radius-sm, 0.25rem);
	cursor: pointer;
}

.artikel-details-page__thumbnail--aktiv {
	border-color: var(--primary, #18181b);
}

/* Klick auf das Hauptbild vergrößert es (Nutzerwunsch) - einfaches CSS-Overlay ohne JS-Interop
   (siehe CLAUDE.md - JS-Interop ist in dieser App bewusst tabu). */
.artikel-details-page__lightbox {
	position: fixed;
	inset: 0;
	background: rgba(0, 0, 0, 0.85);
	display: flex;
	align-items: center;
	justify-content: center;
	z-index: 100;
	cursor: zoom-out;
}

/* FESTE Bühne statt "so groß wie das Bild" (Nutzer-Fund 09.09.2026: beim Durchklicken der Bilder
   von Produkt 10000 "verspringt die Ansicht auf Grund der unterschiedlichen Bildgrößen").
   Gemessen war das Bildelement je nach Motiv 174x377, 360x330 oder 236x377 groß und sprang dabei
   zwischen den Positionen 13, 75 und 106 px hin und her - die Vor-Schaltfläche steht zwar fest,
   das Bild darunter wanderte aber bei jedem Klick.

   Mit fester Breite UND Höhe bleibt der Rahmen immer gleich, "object-fit: contain" legt das Motiv
   darin ab, ohne es zu verzerren oder zu beschneiden. Dieselbe Lösung wie beim Hauptbild der
   Detailseite (dort über "aspect-ratio"), die aus demselben Grund entstanden ist.

   "pointer-events: none" hält das bisherige Verhalten: ein Klick schließt die Lightbox. Das Bild
   hatte nie ein eigenes stopPropagation, der Klick lief also schon immer an die Fläche darunter
   durch - nur nimmt das Element jetzt fast den ganzen Bildschirm ein und würde sonst die
   Vor/Zurück-Schaltflächen unklickbar machen. */
.artikel-details-page__lightbox img {
	width: 90vw;
	height: 90vh;
	object-fit: contain;
	pointer-events: none;
}

/* Nutzerwunsch/echter Fund: ein serverseitig defektes Bild (siehe "_defekteBilder"-Doku) zeigt
   hier einen lesbaren Platzhalter statt eines kaputten Bild-Icons oder einer leeren Fläche. */
.artikel-details-page__lightbox-fehler {
	color: #fff;
	cursor: default;
}

.artikel-details-page__lightbox-schliessen {
	position: absolute;
	top: 1rem;
	right: 1rem;
	background: none;
	border: none;
	color: #fff;
	font-size: 1.5rem;
	cursor: pointer;
}

/* Nutzerwunsch: auch im gezoomten Bild zwischen den Artikelbildern wechseln können - je ein
   Button links/rechts, vertikal mittig über dem Bild. */
.artikel-details-page__lightbox-nav {
	position: absolute;
	top: 50%;
	transform: translateY(-50%);
	background: rgba(0, 0, 0, 0.4);
	border: none;
	color: #fff;
	/* Flex statt Textausrichtung: der Inhalt ist ein SVG (siehe ArtikelDetails.razor), und damit
	   sitzt der Pfeil geometrisch in der Mitte des Kreises - eine Schriftglyphe tat das nicht. */
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 0;
	width: 2.5rem;
	height: 2.5rem;
	border-radius: var(--radius-pill, 9999px);
	cursor: pointer;
	transition: background-color 0.2s ease-out;
}

/* Bewusst KEIN "--surface"/"--primary" wie sonst: diese beiden Schaltflächen liegen auf der
   abgedunkelten Lightbox-Fläche, wo ein heller Theme-Ton als grelle Kachel wirken würde.
   Rückmeldung deshalb über eine kräftigere Abdunklung bzw. einen leichten Weißschleier. */
.artikel-details-page__lightbox-nav:hover,
.artikel-details-page__lightbox-nav:focus-visible {
	background: rgba(0, 0, 0, 0.7);
}

.artikel-details-page__lightbox-schliessen {
	transition: background-color 0.2s ease-out;
	border-radius: var(--radius-pill, 9999px);
	line-height: 1;
	padding: 0.25rem 0.5rem;
}

.artikel-details-page__lightbox-schliessen:hover,
.artikel-details-page__lightbox-schliessen:focus-visible {
	background: rgba(255, 255, 255, 0.2);
}

.artikel-details-page__lightbox-nav--zurueck {
	left: 1rem;
}

.artikel-details-page__lightbox-nav--weiter {
	right: 1rem;
}

.artikel-details-page__badges {
	display: flex;
	gap: 0.5rem;
	margin-bottom: 0.5rem;
}

.artikel-details-page__badge {
	padding: 0.125rem 0.5rem;
	border-radius: var(--radius-pill, 9999px);
	background: var(--primary, #333);
	color: var(--primary-foreground, #fff);
	font-size: 0.75rem;
}

/* Nutzerwunsch: sehr lange Beschreibungen (z.B. Artikel 10001) zunächst nur bis ungefähr zur
   unteren Kante des Hauptbilds zeigen (siehe dessen max-height 400px) statt sofort komplett -
   "Mehr anzeigen" blendet den Rest ein. Der Fade-Verlauf am unteren Rand signalisiert, dass der
   Text abgeschnitten ist. */
.artikel-details-page__beschreibung--eingeklappt {
	max-height: 400px;
	overflow: hidden;
	position: relative;
	mask-image: linear-gradient(to bottom, #000 calc(100% - 2.5rem), transparent 100%);
	-webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 2.5rem), transparent 100%);
}

.artikel-details-page__beschreibung-mehr {
	display: block;
	margin-top: 0.5rem;
	background: none;
	border: none;
	color: var(--primary, #333);
	text-decoration: underline;
	cursor: pointer;
	padding: 0;
	font-size: 0.9rem;
	transition: color 0.2s ease-out;
}

/* Trägt bereits die Primärfarbe und eine Unterstreichung - beim Überfahren bleibt deshalb nur
   eine Abdunklung als Rückmeldung übrig (eine zweite Unterstreichung gibt es nicht). */
.artikel-details-page__beschreibung-mehr:hover,
.artikel-details-page__beschreibung-mehr:focus-visible {
	color: color-mix(in srgb, var(--primary, #333) 78%, black);
}

/* Nutzerwunsch: Produkteigenschaften/Staffelpreise/Artikel Historie/Einheiten/Stückliste stehen
   GESAMMELT unter der dreispaltigen Ansicht (nicht mehr in der mittleren Info-Spalte). */
.artikel-details-page__abschnitte {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	margin-top: var(--shop-gap);
}

/* Nutzerwunsch: eine schmale Trennlinie über dem ersten Abschnitt (Beschreibung bzw.
   Staffelpreise, falls keine Beschreibung vorhanden ist) - trennt den Bild/Kaufbox-Bereich
   optisch vom Folgenden, ohne einen vollen, kantigen Strich über die ganze Breite zu ziehen.
   Dicke/Farbe entsprechen bewusst dem normalen Karten-Rahmenton (--border), obwohl die Karten
   selbst seit dem letzten Wunsch keinen Rahmen mehr haben - die Linie ist hier ein reines
   Trennelement zwischen Abschnitten, kein Rahmen um eine Fläche.
   Als generierter Inhalt wird die Linie selbst zu einem Flex-Item DIESES Containers (siehe
   "display: flex" oben) - der bereits vorhandene "gap: 1rem" sorgt dadurch von allein für den
   Abstand zur ersten echten Karte darunter, ohne eigene margin-Angabe. */
.artikel-details-page__abschnitte::before {
	content: "";
	display: block;
	width: 80%;
	margin: 0 auto;
	border-top: 1px solid var(--border, #e5e5e5);
}

/* Die Reiter (Staffelpreise/Historie/Einheiten/Stückliste) standen als nackte Schaltflächen-
   Reihe frei unter der dreispaltigen Ansicht - die einzelne orange "Staffelpreise"-Pille wirkte
   dadurch verwaist und ohne Bezug zum Inhalt darunter (Nutzer-Fund). Als eigene Karte gefasst
   gehören Reiter und zugehörige Tabelle sichtbar zusammen, in derselben Panel-Optik wie die
   Preiskarte rechts und die Dropdown-Panels im Header. */
/* Nutzerwunsch: Rahmenlinie entfernt - Radius/Schatten/Hintergrund bleiben, tragen die Fläche
   jetzt allein. */
/* Nutzerwunsch: Rahmenlinie UND Schatten entfernt - nur noch Hintergrund/Radius deuten die
   Fläche an. */
.artikel-details-page__abschnitte > .artikel-details-page__abschnitt {
	margin-top: 0;
	padding: 1.25rem;
	background: var(--card, #fff);
	border-radius: var(--radius, 0.5rem);
}

.artikel-details-page__abschnitt {
	margin-top: 0.5rem;
}

/* Nutzerwunsch: Staffelpreise/Artikel Historie/Einheiten als Reiter statt gleichzeitig
   angezeigt - Buttons in einer Reihe, darunter nur der Inhalt des gewählten Reiters. */
/* Nutzerwunsch: sitzt jetzt auf einem BbButtonGroup-Root statt einem eigenen <div> - "display:
   flex"/"gap" bewusst NICHT mehr selbst gesetzt, das übernimmt BbButtonGroup bereits (kein
   Abstand zwischen den Buttons, damit sie zu einer durchgehenden Pille verschmelzen). Ein
   eigenes "gap" hier würde diese Verschmelzung wieder auseinanderreißen - unlayered CSS
   (diese Datei) gewinnt immer gegen BbButtonGroups eigene, in "@layer utilities" ausgelieferte
   Flex-Regel, siehe den dokumentierten Fund beim "Abmelden"-Eintrag im Konto-Menü. Nur der
   Abstand zur Tabelle darunter bleibt hier bestehen. */
.artikel-details-page__reiter {
	margin-bottom: 0.75rem;
}

.artikel-details-page__eigenschaften th,
.artikel-details-page__eigenschaften td,
.artikel-details-page__tabelle th,
.artikel-details-page__tabelle td {
	padding: 0.25rem 0.75rem 0.25rem 0;
	text-align: left;
}

/* Die eine Kaufbox-Karte (Titel/Preis/Bestand/Mengenwahl/Bestellen), siehe Kommentar am
   ".artikel-details-page__hauptbereich"-Markup zur Zusammenlegung der vorherigen zwei
   getrennten Spalten "spalte-info"/"spalte-preis". */
.artikel-details-page__kaufbox {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	padding: 1.25rem;
	/* Nutzerwunsch: Rahmenlinie UND Schatten entfernt - nur noch der Radius deutet die Fläche
	   an (der Bildrahmen daneben bleibt bewusst bestehen, siehe dessen eigene Regel). */
	border-radius: var(--radius, 0.5rem);
	align-self: flex-start;
}

/* Preis und Mengenwahl bilden jetzt eine gemeinsame Zeile (Nutzerwunsch) - "justify-content:
   space-between" hält den Preisblock links und den Mengen-Steller rechts, "flex-wrap: wrap"
   ist die Rückfalllösung für sehr schmale Ansichten (Preisblock rutscht dann über den Steller). */
.artikel-details-page__preiszeile {
	display: flex;
	flex-wrap: wrap;
	justify-content: space-between;
	align-items: center;
	gap: 0.75rem;
}

/* Preis als klarer Blickfang der Karte - bis hierher hatten Preis, UVP und Lagerbestand
   überhaupt keine eigenen Regeln und wirkten dadurch alle gleich gewichtet (Nutzer-Fund:
   "10,92 netto" war optisch nicht vom UVP darunter zu unterscheiden). */
.artikel-details-page__preis {
	margin: 0;
	font-size: 1.5rem;
	font-weight: 700;
	line-height: 1.2;
}

/* Sternchen-Verweis auf den Listenpreis-Hinweis, der jetzt als Fußnote ganz unten in der Box
   steht (Nutzerwunsch) - bewusst NICHT fett/groß wie der Preis selbst: es ist ein Verweiszeichen,
   kein Teil des Preises. "vertical-align: super" hebt es wie eine echte Fußnotenmarkierung an,
   "font-size: 0.6em" bezieht sich auf die Elterngröße (1.5rem), bleibt also unabhängig von
   Bildschirmgröße im richtigen Verhältnis zum Preis. */
.artikel-details-page__preis-stern {
	font-weight: 400;
	font-size: 0.6em;
	vertical-align: super;
	color: var(--muted-foreground, #71717a);
}

.artikel-details-page__uvp,
.artikel-details-page__lagerbestand {
	margin: 0;
	font-size: 0.875rem;
	color: var(--muted-foreground, #71717a);
}

.artikel-details-page__lagerbestand {
	display: flex;
	align-items: center;
	gap: 0.4rem;
}

/* Ampel-Punkt zur Bestandsanzeige (Backend-Einstellung "Lagerbestand anzeigen" = Ampel/
   Beides) - grün/rot wie die alten ICO_Lager_1/3.png-Symbole, dazu ein neutrales Grau für
   "keine Information" (ICO_Lager_4.png im Original). Eine gemeinsame Klasse für
   Artikeldetailseite (ArtikelDetails.razor) UND Artikelliste (ArtikelLagerbestand.razor),
   damit beide Stellen optisch zwingend gleich bleiben. */
.shop-lagerbestand-ampel {
	display: inline-block;
	width: 0.7rem;
	height: 0.7rem;
	border-radius: 50%;
	flex-shrink: 0;
}

.shop-lagerbestand-ampel--verfuegbar {
	background-color: #16a34a;
}

.shop-lagerbestand-ampel--nicht-verfuegbar {
	background-color: var(--destructive, #dc2626);
}

.shop-lagerbestand-ampel--unbekannt {
	background-color: var(--muted-foreground, #9ca3af);
}

/* Zahlen-Kreis in der Artikelliste (ArtikelLagerbestand.razor) - bei "Absolut" NEUTRAL (kein
   Grün/Rot, die Farbe gehört zur Ampel-Einstellung, nicht zur reinen Zahl), bei "Beides" farbig
   nach Ampel-Logik (dort steht "k.A." drin statt eine "0", wenn kein Datensatz vorhanden ist -
   der Kreis muss dort immer sichtbar bleiben, weil er gleichzeitig die Ampel-Farbe trägt).
   Feste Mindestbreite für "k.A." (Nutzerwunsch 18.09.2026: alle Kreise/Pillen gleich groß,
   egal ob 1-stellige Zahl, mehrstellige Zahl oder "k.A." drinsteht - keine wechselnde
   Kreis-vs-Pille-Optik je Inhalt). Fehlt der Lagerbestand bei "Absolut", wird gar kein Kreis
   gerendert (siehe Komponente) - dort stellt sich die Frage nach fester Breite also nicht. */
.artikel-lagerbestand-zahl {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 1.75rem;
	height: 1.35rem;
	padding: 0 0.35rem;
	border-radius: var(--radius-pill, 999px);
	font-size: 0.7rem;
	font-weight: 600;
	line-height: 1;
	flex-shrink: 0;
	white-space: nowrap;
}

.artikel-lagerbestand-zahl--neutral {
	background-color: var(--muted, #f4f4f5);
	color: var(--muted-foreground, #71717a);
}

.artikel-lagerbestand-zahl--verfuegbar {
	background-color: #16a34a;
	color: #fff;
}

.artikel-lagerbestand-zahl--nicht-verfuegbar {
	background-color: var(--destructive, #dc2626);
	color: #fff;
}

.artikel-lagerbestand-zahl--unbekannt {
	background-color: var(--muted-foreground, #9ca3af);
	color: #fff;
}

/* Steht jetzt als eigener, direkter Flex-Child der spaltigen ".artikel-details-page__kaufbox"
   (Nutzerwunsch: Mengen-Steller neben den Preis, der Button bekam dadurch keine umgebende
   Zeile mehr) - die volle Breite ergibt sich bereits aus dem Standard-"align-items: stretch"
   der Kaufbox, kein eigenes "flex: 1 1 100%" mehr nötig (das bezöge sich in einem
   Spalten-Flex-Container ohnehin auf die Höhe, nicht die Breite). Nur noch der Abstand zur
   Preiszeile/den Meldungen darüber. */
.artikel-details-page__warenkorb-button {
	margin-top: 0.25rem;
}

.artikel-details-page__meldung {
	margin-top: 1rem;
}

@media (min-width: 576px) {
	.artikel-grid--kacheln {
		grid-template-columns: repeat(3, 1fr);
	}
}

@media (min-width: 992px) {
	.artikel-grid--kacheln {
		grid-template-columns: repeat(4, 1fr);
	}

	.artikel-suche-page {
		flex-direction: row;
	}

	.artikel-suche-page__filter {
		display: flex;
		flex: 0 0 220px;
	}

	.artikel-suche-page__ergebnisse {
		flex: 1;
	}

	/* Nutzerwunsch: größerer Abstand zwischen Bild und Kaufbox als der normale Spaltenabstand
	   ("--shop-gap", 1rem) - bewusst nur ab diesem Breakpoint überschrieben: gestapelt
	   (mobil/Tablet) bleibt der normale, kleinere Abstand zwischen Bild und Kaufbox darunter. */
	.artikel-details-page__hauptbereich {
		flex-direction: row;
		gap: calc(var(--shop-gap) * 3);
	}

	/* Nutzerwunsch/echter Fund: mit "flex: 1 1 420px" (Bildspalte wächst mit) stand zwischen dem
	   auf "max-width: 320px" begrenzten Bild und der danebenstehenden Kaufbox eine tote,
	   zusätzliche Lücke - die Spalte selbst war deutlich breiter als das Bild, das sie enthält.
	   "flex: 0 0 auto" lässt die Spalte stattdessen auf ihren Inhalt schrumpfen (das Bild bzw.
	   die Thumbnail-Leiste, je nachdem was breiter ist), wodurch die Kaufbox im normalen
	   Spaltenabstand ("gap" am Hauptbereich) direkt danebenrückt statt durch Leerraum getrennt
	   zu sein. "min-width: 0" bleibt nötig, damit eine breite Thumbnail-Leiste die Spur nicht
	   über das eigentliche Bild hinaus aufdehnt. */
	.artikel-details-page__spalte-bilder {
		/* FESTE Breite, nicht "so breit wie der Inhalt" (Nutzer-Fund 09.09.2026: beim Durchklicken
		   der Bilder "verspringt die Ansicht ... man sieht es am Textbeginn von 'DeDeSales'").
		   Ursache war "flex: 0 0 auto": die Spalte richtete sich nach ihrem breitesten Inhalt, und
		   der wechselte MIT DEM BILD. Bei einem querformatigen Bild bestimmte das Bild die Breite
		   (448 px, seine Obergrenze), bei einem hochformatigen war es die Thumbnail-Leiste
		   (6 x 64 px plus 5 x 8 px Abstand = 424 px). Die Kaufbox daneben rutschte dadurch bei jedem
		   Bildwechsel um 24 px hin und her.

		   448px ist die Obergrenze des Hauptbildes; "min()" hält die Spalte auf schmalen Fenstern
		   weiterhin innerhalb des Platzes. "min-width: 0" bleibt nötig, damit eine breite
		   Thumbnail-Leiste die Spur nicht darüber hinaus aufdehnt - sie scrollt dann in sich. */
		flex: 0 0 auto;
		width: min(448px, 100%);
		min-width: 0;
	}

	/* Nutzerwunsch: x1.3 gegenüber den vorherigen 340px. */
	.artikel-details-page__kaufbox {
		flex: 0 0 442px;
		/* Bleibt beim Scrollen langer Beschreibungen/Staffelpreis-Tabellen im Blick. "top"
		   entspricht dem Wert von "scroll-padding-top" am <html> (siehe dort) - also der Höhe
		   der klebenden Kopfzeile plus etwas Luft, damit die Karte nicht darunter verschwindet.
		   Bewusst nur ab diesem Breakpoint: unterhalb stapeln sich Bild und Kaufbox
		   untereinander, eine klebende Karte würde dort dauerhaft Platz vom Inhalt wegnehmen. */
		position: sticky;
		top: 4.5rem;
	}
}

/* Warenkorb & Bestellung: Positionstabelle wird auf Mobile als gestapelte Karten dargestellt
   (echte <table> mit vielen Spalten passt nicht auf Mobile-Breite) - siehe CLAUDE.md
   Feature-Block "Warenkorb & Bestellung". */

.warenkorb-page__tabelle {
	width: 100%;
	border-collapse: collapse;
}

.warenkorb-page__tabelle th,
.warenkorb-page__tabelle td {
	padding: 0.5rem;
	text-align: left;
	border-bottom: 1px solid var(--border, #ddd);
}

.warenkorb-page__alert {
	margin-bottom: 1rem;
}

.warenkorb-page__nummer {
	display: block;
	font-size: 0.75rem;
	opacity: 0.7;
}

.warenkorb-page__sonderrabatt {
	margin: var(--shop-gap) 0 0;
	font-size: 0.875rem;
	opacity: 0.85;
}

.warenkorb-page__gutschein,
.warenkorb-page__summe,
.warenkorb-page__aktionen {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.5rem;
	padding: var(--shop-gap) 0;
}

/* Nutzerwunsch: "Zur Kasse" ganz nach rechts, abgesetzt von den übrigen (sekundären)
   Aktions-Buttons. */
.warenkorb-page__zur-kasse {
	margin-left: auto;
}

.warenkorb-page__summe {
	justify-content: space-between;
	font-size: 1.125rem;
}

/* Frachtkosten-Zeile unter der Gesamtsumme (siehe Warenkorb.razor) - bewusst kleiner und
   gedämpfter als die Summe selbst, da sie diese ergänzt und nicht mit ihr konkurriert. Das
   negative margin-top hebt das padding der Summenzeile darüber auf, damit beide Zeilen als
   ein Block zusammenstehen. */
.warenkorb-page__versandkosten {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 0.5rem;
	margin-top: calc(var(--shop-gap) * -1);
	font-size: 0.9375rem;
	color: var(--muted-foreground, #52525b);
}

/* Live-Preis-Summe unter Gesamtsumme/Frachtkosten (siehe Warenkorb.razor). Bewusst NICHT rot
   wie im Original (Nutzerentscheidung: zurückhaltender) - gleiche Zeilenform wie die
   Frachtkosten, aber in der normalen Textfarbe und halbfett, weil es eine Summe ist. */
.warenkorb-page__livepreis-summe {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: 0.5rem;
	margin-top: 0.25rem;
	font-size: 0.9375rem;
	font-weight: 600;
	color: var(--foreground, #18181b);
}

/* Hinweis "Klicken Sie auf Live Preisabfrage ..." direkt unter der Buttonleiste - gedämpft
   wie die übrigen Hinweistexte, damit er die Buttons nicht überstrahlt. */
.warenkorb-page__livepreis-hinweis {
	margin: 0.25rem 0 0;
	font-size: 0.875rem;
	color: var(--muted-foreground, #52525b);
}

/* Frachtfreigrenzen-Hinweis ("Lieferung frei Haus" bzw. "Die Frachtfreigrenze von ... wurde
   nicht erreicht.", siehe Warenkorb.razor) - rechtsbündig unter Summe/Frachtkosten, weil er
   inhaltlich zu diesen Zahlen gehört. Der erreichte Fall wird bewusst hervorgehoben (positive
   Aussage), der nicht erreichte bleibt eine neutrale Randnotiz. */
.warenkorb-page__frachtfreigrenze {
	margin: 0.25rem 0 0;
	text-align: right;
	font-size: 0.875rem;
	color: var(--muted-foreground, #52525b);
}

.warenkorb-page__frachtfreigrenze--erreicht {
	font-weight: 600;
	color: var(--foreground, #18181b);
}

/* Direkterfassung als eigene Tabellenzeile (Nutzerwunsch: Mengenfeld/Übernehmen-Button sollen
   unter den übrigen Mengenfeldern/Lösch-Symbolen der Positionsliste liegen), siehe
   Warenkorb.razor. Etwas abgesetzt von den echten Positionszeilen. */
.warenkorb-page__direkterfassung-zeile > td {
	border-top: 1px solid var(--border, #ddd);
}

.warenkorb-page__direkterfassung-nummer {
	display: flex;
	align-items: center;
	gap: 0.25rem;
}

/* Reines natives <input>-Element (Produktnummer, kein BbInput) - braucht daher seinen eigenen
   Rahmen/Innenabstand, analog zu .shop-header__suche input.
   ":not(.menge-steller__eingabe)" ist notwendig, nicht nur defensiv: MengenSteller rendert
   sein eigenes <input> ebenfalls als Nachfahre dieser Tabellenzeile, das brächte ohne den
   Ausschluss einen zweiten, unerwünschten Pill-Rahmen INNERHALB des bereits pill-förmigen
   Steller-Wrappers mit (".warenkorb-page__direkterfassung-zeile input" hat mit Klasse+Element
   höhere Spezifität als ".menge-steller__eingabe" und würde dessen "border: none" sonst
   schlagen, unabhängig von der Reihenfolge im Stylesheet). */
.warenkorb-page__direkterfassung-zeile input:not(.menge-steller__eingabe) {
	border: 1px solid var(--input, #ddd);
	border-radius: var(--radius-pill, 9999px);
	padding: 0.375rem 0.75rem;
	font: inherit;
	color: var(--foreground, #18181b);
	background: var(--background, #fff);
}

/* Nutzerwunsch: Symbol neben dem Produktnummernfeld, um die Eingabe zu leeren. */
.warenkorb-page__direkterfassung-leeren {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 1.75rem;
	height: 1.75rem;
	flex-shrink: 0;
	background: none;
	border: none;
	border-radius: var(--radius-pill, 9999px);
	color: var(--muted-foreground, #71717a);
	cursor: pointer;
}

.warenkorb-page__direkterfassung-leeren:hover {
	background: var(--surface, #f4f4f5);
	color: var(--primary, inherit);
}

.warenkorb-page__direkterfassung-bezeichnung {
	display: block;
	font-size: 0.875rem;
	opacity: 0.85;
}

.warenkorb-page__direkterfassung-submit {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0.5rem;
	background: none;
	border: 1px solid var(--input, #ddd);
	border-radius: var(--radius-pill, 9999px);
	cursor: pointer;
}

.warenkorb-page__direkterfassung-submit:hover {
	background: var(--surface, #f4f4f5);
	color: var(--primary, inherit);
}

.warenkorb-page__direkterfassung-fehler {
	display: block;
	color: var(--destructive, #dc2626);
	font-size: 0.875rem;
}

.warenkorb-page__direkterfassung-erfolg {
	display: block;
	color: var(--muted-foreground, #16a34a);
	font-size: 0.875rem;
}

/* Hinweis bei Matrixartikeln in der Direkterfassung - kein Fehler des Nutzers, deshalb bewusst
   nicht in der Fehlerfarbe, sondern als sachlicher Hinweis mit Link in die Mengenmatrix. */
.warenkorb-page__direkterfassung-hinweis {
	display: block;
	color: var(--muted-foreground, #6b7280);
	font-size: 0.875rem;
}

.warenkorb-page__direkterfassung-hinweis a {
	color: var(--primary, #ea580c);
	font-weight: 500;
}

.bestellabschluss-page__hauptbereich {
	display: flex;
	flex-direction: column;
	gap: 2rem;
}

.bestellabschluss-page__form {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	max-width: 32rem;
}

.bestellabschluss-page__form label {
	display: flex;
	flex-direction: column;
	gap: 0.25rem;
	font-weight: 500;
	font-size: 0.875rem;
}

.bestellabschluss-page__bedingungen {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 0.25rem 1rem;
	font-size: 0.875rem;
}

/* Werte hervorheben (Nutzerwunsch) - das Original setzt sie ebenfalls in <b>
   (bestellabschluss.form.html). 600 statt 700, weil das im Shop durchgehend die Stärke für
   Hervorhebungen ist. Gilt für ALLE Zeilen dieser Liste, also auch die USt-ID: sie steht im
   Original ebenso in <b>, und eine einzelne nicht hervorgehobene Zeile mittendrin sähe nach
   Versehen aus. Vom Nutzer bestätigt ("Ust-ID kann auch fett bleiben"). */
.bestellabschluss-page__bedingungen dd {
	font-weight: 600;
}

.bestellabschluss-page__summe {
	display: flex;
	justify-content: space-between;
	font-size: 1.125rem;
}

.bestellabschluss-page__alert {
	margin-bottom: 1.25rem;
}

/* Eigene Seite "Lieferadresse ändern" (Nutzerwunsch) - siehe LieferadresseAendern.razor. */
.lieferadresse-aendern-page {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	max-width: 32rem;
}

.lieferadresse-aendern-page__optionen {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.lieferadresse-aendern-page__option {
	display: block;
	width: 100%;
	padding: 0.75rem 1rem;
	text-align: left;
	background: var(--card, #fff);
	border: 1px solid var(--border, #ddd);
	border-radius: var(--radius, 0.5rem);
	font: inherit;
	color: inherit;
	cursor: pointer;
}

.lieferadresse-aendern-page__option:hover {
	background: var(--accent, #f4f4f5);
}

.lieferadresse-aendern-page__temp-formular {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	margin-top: 0.75rem;
	padding: 1rem;
	border: 1px solid var(--border, #ddd);
	border-radius: var(--radius, 0.5rem);
}

/* Rechte Spalte auf dem Bestellabschluss: nur lesende Übersicht der Warenkorbpositionen
   (Nutzerwunsch) - bewusst analog zur artikel-details-page__kaufbox-Karte gestaltet
   (Rahmen, feste Breite ab Desktop), aber ohne jegliche Bearbeitungs-Interaktion. */
.bestellabschluss-page__warenkorb-uebersicht {
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
	padding: 1.25rem;
	border: 1px solid var(--border, #e5e5e5);
	border-radius: var(--radius, 0.5rem);
	box-shadow: var(--shadow-soft, none);
	align-self: flex-start;
}

.bestellabschluss-page__warenkorb-uebersicht h2 {
	margin: 0;
	font-size: 1rem;
}

.bestellabschluss-page__positionen {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	margin: 0;
	padding: 0;
	list-style: none;
}

.bestellabschluss-page__positionen li {
	display: grid;
	grid-template-columns: auto 1fr auto auto;
	gap: 0.5rem;
	align-items: center;
	font-size: 0.875rem;
	padding-bottom: 0.5rem;
	border-bottom: 1px solid var(--border, #eee);
}

.bestellabschluss-page__positionen-bild {
	width: 2.5rem;
	height: 2.5rem;
	object-fit: contain;
	border: 1px solid var(--border, #eee);
	border-radius: var(--radius-sm, 0.25rem);
}

.bestellabschluss-page__positionen-menge,
.bestellabschluss-page__positionen-betrag {
	text-align: right;
	white-space: nowrap;
}

/* Nutzerwunsch: nach der Überschrift etwas Platz lassen. */

.bestellabschluss-page__lieferadresse-anzeige {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 0.75rem;
}

/* Nutzerwunsch: Lieferadresse mehrzeilig (Name / Straße / PLZ + Ort) statt einer
   kommagetrennten Zeile. */
.bestellabschluss-page__lieferadresse-text {
	display: flex;
	flex-direction: column;
}

/* Eigene Seite für den Excel-Upload (Nutzerwunsch: statt Popup) - siehe ExcelUpload.razor. */
.warenkorb-excel-page {
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
	max-width: 32rem;
}

.warenkorb-excel-page__spalten {
	margin: 0;
	padding-left: 1.5rem;
}

.warenkorb-excel-page__hinweise {
	margin: 0;
	padding-left: 1.5rem;
	font-size: 0.875rem;
	opacity: 0.85;
}

.warenkorb-excel-page__dropzone {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.5rem;
	padding: 1.5rem;
	border: 2px dashed var(--border, #ddd);
	border-radius: var(--radius, 0.5rem);
	text-align: center;
}

.warenkorb-excel-page__dropzone.warenkorb-excel-dropzone--aktiv,
.warenkorb-excel-dropzone--aktiv {
	border-color: var(--primary, #18181b);
	background: var(--accent, #f4f4f5);
}

@media (min-width: 992px) {
	.bestellabschluss-page__hauptbereich {
		flex-direction: row;
	}

	.bestellabschluss-page__form {
		flex: 1;
	}

	/* Nutzerwunsch: die Produktliste war "unnötig schmal" - von 320px auf 460px verbreitert. */
	.bestellabschluss-page__warenkorb-uebersicht {
		flex: 0 0 460px;
	}
}

.bestellbestaetigung-page {
	display: flex;
	justify-content: center;
	padding: 3rem var(--shop-gap);
}

.bestellbestaetigung-page__card {
	width: 100%;
	max-width: 32rem;
}

/* Seit der global vergrößerten <h1> (siehe die Begründung dort) wirkte der Abstand zwischen
   der Auftragsnummer und dem Button zu knapp - der Browser-Standardabstand des <p> reichte
   nicht mehr, Nutzer-Fund 10.09.2026. */
.bestellbestaetigung-page__card p {
	margin-bottom: calc(var(--shop-gap) * 1.5);
}

/* Generische, wiederverwendbare Mobile-Darstellung für alle Datentabellen (Nutzerwunsch: die
   Anzeige von Belegen/Positionen/Offenen Posten/Rückständen war auf dem Smartphone "zu breit" -
   dieselbe gestapelte "Karten"-Lösung wie zuvor nur für den Warenkorb, jetzt aber als
   gemeinsame Utility-Klasse "shop-tabelle--responsive" statt einer Kopie pro Seite. Ohne die
   (hier ausgeblendete) Kopfzeile wäre bei den gestapelten Zellen nicht erkennbar, welcher Wert
   was bedeutet - jede Zelle bekommt daher ihre Spaltenüberschrift als eigenes Label davor
   (aus dem "data-label"-Attribut auf dem jeweiligen <td>, siehe Warenkorb.razor/Belege.razor/
   OffenePosten.razor/Rueckstaende.razor). Eine Zelle OHNE data-label (z.B. die erste,
   bereits selbsterklärende Spalte) bleibt block-weit ohne Label-Präfix. */
/* Nutzer-Fund (Screenshot iPhone 16 Pro Max, 440 px): die Kopfzeile brauchte dort DREI Zeilen -
   Marke, dann die Warenkorb-/Konto-Symbole allein und rechtsbündig auf einer eigenen Zeile mit
   großer Leerfläche links, dann die Suche. Gemessen: der Markenblock endete bei 356 px, frei
   blieben 68 px, die Symbole brauchen aber 88 px - es fehlten also nur ~20 px, und Flexbox
   bricht um, statt den Markenblock schmaler zu machen.

   Ursache ist die Basisbreite des Firmennamens: "max-width: clamp(9rem, 20vw, 24rem)" liefert
   bei 440 px die Untergrenze von 144 px, und diese 144 px gehen als Basisgröße in die
   Umbruchrechnung ein. Mit "flex-basis: 0" ist die Basis stattdessen null - die Symbole finden
   damit IMMER Platz in der ersten Zeile, und der Name bekommt anschließend den Rest zugewiesen
   (nach oben weiter durch dieselbe max-width begrenzt, die Darstellung auf breiteren Geräten
   ändert sich also nicht). Bewusst nur unterhalb des Desktop-Breakpoints: dort soll der freie
   Platz der Produktsuche zugutekommen, nicht dem Namen (siehe .shop-header__searchbar). */
@media (max-width: 991.98px) {
	/* "flex-basis: 0" statt "auto" ist hier der entscheidende Punkt: mit "auto" bleibt die
	   Basisgröße die Inhaltsbreite (damals Logo + "eCom" + Name = gemessene 356 px), und die
	   geht in die Umbruchrechnung ein - der erste Versuch mit "flex: 1 1 auto" änderte deshalb
	   gar nichts. Mit Basis 0 wächst der Markenblock erst NACH der Platzverteilung in den
	   verbleibenden Raum. */
	.shop-header__brand {
		flex: 1 1 0;
	}

	/* Das Logo darf dabei nicht mitschrumpfen - nachgeben soll der Name. */
	.shop-header__logo {
		flex: 0 0 auto;
	}

	.shop-header__betreiber--kompakt {
		flex: 1 1 0;
		min-width: 0;
	}

	/* Die Symbole sollen nicht schrumpfen, sondern den Namen schrumpfen lassen. */
	.shop-header__account {
		flex-shrink: 0;
	}
}

@media (max-width: 575px) {
	.shop-tabelle--responsive thead {
		display: none;
	}

	.shop-tabelle--responsive,
	.shop-tabelle--responsive tbody,
	.shop-tabelle--responsive tr,
	.shop-tabelle--responsive td {
		display: block;
		width: 100%;
	}

	.shop-tabelle--responsive tr {
		margin-bottom: 1rem;
		padding-bottom: 0.5rem;
		border-bottom: 1px solid var(--border, #ddd);
	}

	.shop-tabelle--responsive td {
		padding: 0.25rem 0;
		border-bottom: none;
	}

	.shop-tabelle--responsive td[data-label] {
		display: flex;
		align-items: center;
		justify-content: space-between;
		gap: 0.5rem;
		text-align: right;
	}

	.shop-tabelle--responsive td[data-label]::before {
		content: attr(data-label);
		font-weight: 600;
		flex-shrink: 0;
		text-align: left;
	}
}

/* Login-Seite: Split-Screen (Nutzerwunsch, 2026-09-04, nach Abstimmung mit Karsten) - links
   Formular auf weißem Grund (inkl. Shop-Branding), rechts das Splash-Bild ganzflächig. Ersetzt die
   vorherige Anmeldebox-als-Overlay-über-Vollbild-Optik (siehe Git-Historie/CLAUDE.md für die
   Vorgängerversion samt "?geschlossen=1"-Mechanik - mit dem neuen Layout entfällt eine
   schließbare Box ersatzlos, das Formular liegt jetzt fest im linken Panel).
   BlazorBlueprint-Komponenten (BbField/BbInput/BbButton/BbAlert) - siehe CLAUDE.md
   "UI-Bibliothek". Nur die Seitenanordnung (Panel/Bild-Aufteilung, Abstände) ist eigenes CSS, das
   eigentliche Komponenten-Aussehen kommt aus blazorblueprint.css/themes.css.
   Mobile-first wie der Rest von webshop.css (Bild unterhalb von 992px ausgeblendet, damit das
   Formular auf schmalen Bildschirmen nicht durch ein zusätzliches Bild nach unten verdrängt wird)
   - Breakpoint bewusst identisch zum bestehenden Desktop-Breakpoint (siehe z.B.
   .shop-header__nav), keine neue Breite eingeführt. */
/* Hier stand kurzzeitig eine graue Fläche nur unter der Login-Seite, damit sich deren Karte
   abhebt. Sie ist wieder raus - der Nutzer hat sie sofort als Fremdkörper erkannt ("da ist jetzt
   plötzlich ein grauer hintergrund, der sonst nirgends ist") und stattdessen den Verlauf der
   Website vorgeschlagen, der jetzt auf JEDER Seite liegt (siehe ".shop-layout"). Damit hebt sich
   die Karte überall gleich ab, ohne Sonderfall. */

.login-page {
	display: flex;
	flex-direction: column;
	/* Luft nach oben und unten - seitlich kommt sie vom Inhaltsbereich. */
	padding: 1.5rem 0;
	/* Bewusst "flex:1" statt einer geschätzten "calc(100vh - Nrem)"-Rechnung oder einer
	   Prozent-Höhe wie "min-height:100%" (siehe Git-Historie der Vorgängerversion) -
	   ".shop-layout__content" ist selbst ein Flex-Container (siehe dortige Doku), gibt den
	   zwischen Header und Footer verfügbaren Freiraum dadurch in ECHTEN Pixeln weiter. */
	flex: 1;
}

/* Die Karte selbst. "overflow: hidden" ist der Grund, warum das Splash-Bild an der Ecke
   mitgerundet wird, ohne dass es eine eigene Rundung braucht. */
.login-page__karte {
	flex: 1;
	display: flex;
	flex-direction: column;
	background: var(--card, #fff);
	border: 1px solid var(--border, #ddd);
	border-radius: var(--radius, 0.5rem);
	overflow: hidden;
	box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
}

.login-page__panel {
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 3rem var(--shop-gap);
	box-sizing: border-box;
}

.login-page__panel-inner {
	width: 100%;
	max-width: 24rem;
	display: flex;
	flex-direction: column;
	gap: 1.5rem;
}

/* Nutzerwunsch: nur noch das Logo, mittig und deutlich größer - der Betreibername steht schon
   im Header und im Footer (siehe Login.razor). "max-width: 100%" verhindert, dass ein sehr breites
   Logo das schmale Panel sprengt. */
.login-page__brand {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.75rem;
	/* Bezugspunkt für die KI-Kennzeichnung am Logo (siehe ".ki-hinweis") - dazu muss der
	   Markenbereich das Logo UMSCHLIESSEN und nicht die ganze Panelbreite einnehmen, sonst säße
	   die Kennzeichnung am Rand des Panels statt am Bild. "margin-inline: auto" hält ihn dabei
	   mittig, was vorher das "justify-content" allein erledigt hat. */
	position: relative;
	width: fit-content;
	max-width: 100%;
	margin-inline: auto;
}

.login-page__logo {
	/* Nutzerwunsch, in zwei Schritten: erst von 5rem auf 7rem, dann "noch etwas größer" auf 9rem.
	   Das ist bewusst die Obergrenze: das Logo von beta19 ist 264x150 px groß, bei 9rem (144 px)
	   wird es also gerade noch verkleinert dargestellt und bleibt scharf. Darüber würde der
	   Browser hochrechnen. In der Breite sind es dann rund 253 px - die Karte ist 384 px breit,
	   passt also weiterhin locker. Wer das Logo größer braucht, sollte eine größere Bilddatei
	   hinterlegen, statt diesen Wert zu erhöhen. */
	height: 9rem;
	width: auto;
	max-width: 100%;
	object-fit: contain;
}

/* Ersatz für ein fehlendes/kaputtes Logo (siehe Login.razor): steht an derselben Stelle wie
   sonst das Logo, also ebenfalls mittig, und bewusst größer als normaler Fließtext - es füllt
   den Platz eines 5rem hohen Logos. Bleibt trotzdem unter der "Anmelden"-Überschrift (1.75rem,
   700), damit es ihr nicht die Hauptrolle streitig macht. */
/* Größe und Strichstärke kommen jetzt aus der globalen h1-Regel (nicht fett, siehe dort); hier
   bleibt nur der engere Abstand, weil direkt darunter der erklärende Satz steht. */
.login-page__title {
	margin: 0 0 0.25rem;
}

.login-page__subtitle {
	color: var(--muted-foreground, #71717a);
	margin: 0;
}

.login-page__form {
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
}

/* "Auf diesem System angemeldet bleiben" - natives Kästchen (die Login-Seite ist ein reines
   HTML-Formular, siehe Login.razor). Nur Ausrichtung und Abstand; Kästchen und Beschriftung
   stehen auf einer Linie, die Beschriftung ist über "for" mit anklickbar. */
.login-page__angemeldet-bleiben {
	display: flex;
	align-items: center;
	gap: 0.5rem;
}

.login-page__angemeldet-bleiben label {
	cursor: pointer;
}

/* Schieber statt Kontrollkästchen (Nutzerwunsch). Bewusst KEIN eigenes Markup mit <span>-Attrappe:
   gestylt wird das native <input type="checkbox"> selbst. Damit bleiben Tastaturbedienung,
   Fokusreihenfolge, Vorlesen und - wichtig auf der Login-Seite - das Mitsenden im Formular genau
   so, wie sie sind. Der Knauf ist ein ::after, die Spur das Element selbst. */
.shop-schieber {
	appearance: none;
	-webkit-appearance: none;
	position: relative;
	flex: 0 0 auto;
	margin: 0;
	width: 2.25rem;
	height: 1.25rem;
	border: 1px solid var(--border, #d4d4d8);
	border-radius: var(--radius-pill, 9999px);
	background: var(--surface, #f4f4f5);
	cursor: pointer;
	transition: background-color 0.2s ease-out, border-color 0.2s ease-out;
}

.shop-schieber::after {
	content: "";
	position: absolute;
	top: 50%;
	left: 0.1875rem;
	width: 0.875rem;
	height: 0.875rem;
	border-radius: var(--radius-pill, 9999px);
	background: #fff;
	box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25);
	/* Die Verschiebung nach oben gehört zur Zentrierung und muss deshalb in BEIDEN Zuständen in
	   der transform stehen - sonst springt der Knauf beim Umschalten nach unten. */
	transform: translateY(-50%);
	transition: transform 0.2s ease-out;
}

.shop-schieber:checked {
	background: var(--primary, #18181b);
	border-color: var(--primary, #18181b);
}

.shop-schieber:checked::after {
	transform: translateY(-50%) translateX(0.9375rem);
}

.shop-schieber:focus-visible {
	outline: 2px solid var(--primary, #18181b);
	outline-offset: 2px;
}

.login-page__submit {
	width: 100%;
}

.login-page__links {
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
	font-size: 0.875rem;
}

.login-page__image {
	display: none;
	background-repeat: no-repeat;
	background-position: center;
	background-size: cover;
	/* Bezugspunkt für die KI-Kennzeichnung (siehe ".ki-hinweis"). */
	position: relative;
}

@media (min-width: 992px) {
	.login-page__karte {
		flex-direction: row;
	}

	/* Feine Kante zwischen den beiden Hälften - der einzige Strich, den die gemeinsame Karte
	   innen braucht. */
	.login-page__image {
		border-left: 1px solid var(--border, #ddd);
	}

	.login-page__panel {
		flex: 1 1 45%;
		max-width: 45%;
	}

	.login-page__image {
		display: block;
		flex: 1 1 55%;
	}
}

/* Registrierungs-Seiten ("Noch kein Kunde"/"Passwort vergessen"/"Noch kein Zugang") - siehe
   CLAUDE.md, dieselbe Card-zentrierte Optik wie die Login-Seite, nur ohne Splash-Hintergrund
   (diese Seiten sind interaktiv, die Login-Seite bewusst nicht - siehe dort). */

.registrierung-page {
	display: flex;
	justify-content: center;
	padding: 3rem var(--shop-gap);
}

.registrierung-page__card {
	width: 100%;
	max-width: 32rem;
}

.registrierung-page__form {
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
}

/* Zustimmungszeile ("Ich habe die Datenschutzbestimmungen gelesen"): Kästchen und Beschriftung
   nebeneinander. Oben ausgerichtet, weil der Satz auf schmalen Geräten umbricht - mittig
   ausgerichtet rutschte das Kästchen dann in die Mitte des Absatzes. Der kleine Versatz setzt es
   auf die Höhe der ERSTEN Zeile. */
.registrierung-page__zustimmung {
	display: flex;
	align-items: flex-start;
	gap: 0.5rem;
}

.registrierung-page__zustimmung > :first-child {
	flex: 0 0 auto;
	margin-top: 0.125rem;
}

.registrierung-page__zustimmung label {
	cursor: pointer;
}

.registrierung-page__aktionen {
	display: flex;
	gap: 0.75rem;
}

.registrierung-page__alert {
	margin-bottom: 1.25rem;
}

.registrierung-page__hinweis {
	margin-bottom: 1.25rem;
	font-size: 0.875rem;
	opacity: 0.85;
}

/* Cookie-Banner - siehe CookieBanner.razor. Fixiert am unteren Bildschirmrand, analog zum
   alten Shop (div.Cookies), nur ohne dessen Inline-Styles. */

.cookie-banner {
	position: fixed;
	left: 0;
	right: 0;
	bottom: 0;
	z-index: 1000;
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	justify-content: space-between;
	gap: var(--shop-gap);
	padding: var(--shop-gap);
	background: var(--card, #fff);
	border-top: 1px solid var(--border, #ddd);
	box-shadow: var(--shadow-card, 0 -2px 8px rgba(0, 0, 0, 0.08));
}

.cookie-banner__text {
	flex: 1;
	min-width: 200px;
	font-size: 0.875rem;
}

.cookie-banner__button {
	flex-shrink: 0;
	border: 1px solid transparent;
	padding: 0.5rem 1.25rem;
	font-size: 0.875rem;
}

.cookie-banner--hidden {
	display: none;
}

/* Echter Fund (Nutzerwunsch "Es fehlt der Footer" auf dem Smartphone): der Cookie-Banner ist
   fest am unteren Bildschirmrand fixiert (siehe oben) - auf schmalen Viewports wird sein Text
   mehrzeilig und der Banner dadurch so hoch, dass er den (nicht fixierten, im normalen Textfluss
   liegenden) Footer beim Herunterscrollen fast komplett verdeckt, solange der Banner noch nicht
   bestätigt wurde. Zusätzlicher Abstand NUR während der Banner sichtbar ist (":not(.cookie-
   banner--hidden)", per JS umgeschaltet, siehe cookies.js) - nach "Verstanden" verschwindet der
   Abstand wieder automatisch mit. */
.shop-layout:has(.cookie-banner:not(.cookie-banner--hidden)) .shop-footer {
	padding-bottom: 7rem;
}

/* Passwort-Zwang bei Erstanmeldung - siehe PasswortZwangOverlay.razor. Deckt bewusst den
   gesamten Viewport ab (höherer z-index als der Cookie-Banner) und lässt keinen Klick auf das,
   was dahinterliegt, durch - das ist der eigentliche "kann nicht weggeklickt werden"-Mechanismus,
   im alten Shop über ngDialog-Optionen (closeByEscape/closeByDocument/showClose = false) gelöst. */
.passwort-zwang-overlay {
	position: fixed;
	inset: 0;
	z-index: 2000;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: var(--shop-gap);
	background: rgba(0, 0, 0, 0.6);
	overflow-y: auto;
}

.passwort-zwang-overlay__card {
	width: 100%;
	max-width: 28rem;
}

.passwort-zwang-overlay__form {
	display: flex;
	flex-direction: column;
	gap: var(--shop-gap);
}

.passwort-zwang-overlay__alert {
	margin-bottom: var(--shop-gap);
}

/* Startseite: Content-Blöcke ("Container", siehe ContainerListe.razor). "Float" = Kacheln im
   Grid, "Slider" = horizontal scrollbare Reihe (bewusst ohne JS-Autoplay, siehe
   ContainerListe.razor) - beides rein per CSS, kein Karussell-Skript nötig. */

.portal-page {
	display: flex;
	flex-direction: column;
	gap: calc(var(--shop-gap) * 2);
}

/* FESTE Spaltenzahl statt "auto-fill" (Nutzerwunsch 09.09.2026): "genau zwei breite kacheln oder
   4 kleine kacheln oder 1 breite kachel und 2 kleine kacheln nebeneinander". Mit vier Spalten geht
   jede dieser drei Kombinationen exakt auf, weil eine Bild+Text-Kachel zwei Spalten belegt.

   Mit "auto-fill" ergaben sich bei 1248px Inhaltsbreite FÜNF Spalten - eine ungerade Zahl, in der
   eine Doppelkachel nie sauber aufgeht: standen zwei schmale Kacheln vorn, blieb für die zweite
   Doppelkachel nur eine Spalte übrig und sie rutschte in die nächste Zeile (genau der Nutzer-Fund
   "es passen zwei breite und ein schmales"). Die Kacheln werden dadurch größer: 300 statt 237 px.

   Unterhalb von 992px zwei Spalten - und zwar bis zum schmalsten Gerät hinunter, ohne Rückfall auf
   eine einzige Spalte (Nutzerwunsch 09.09.2026: "Die Startseite soll in der Smartphone Ansicht
   genau eine Bild+Text Kachel oder zwei kleine Kacheln nebeneinander darstellen"). Zwei Spalten
   leisten beides gleichzeitig: zwei kleine Kacheln nebeneinander, und die Doppelkachel füllt mit
   ihren zwei Spalten genau eine Zeile. */
/* Kennzeichnung "mit KI erzeugt" (siehe Components/Shared/KiHinweis.razor). Sitzt oben rechts IM
   Bild - das umgebende Element muss dafür "position: relative" tragen.

   Grauton und Weiß sind bewusst fest und kommen nicht aus dem Theme: die Pille liegt auf einem
   beliebigen Foto, wo eine Markenfarbe je nach Motiv verschwinden oder mit dem Bildinhalt
   konkurrieren würde. Genau so macht es auch die Firmenwebsite.

   "pointer-events: none" ist notwendig, nicht kosmetisch: viele dieser Bilder liegen in einem
   Link (Container-Kachel) oder öffnen die Lightbox - die Kennzeichnung darf diesen Klick nicht
   abfangen. "user-select: none" verhindert, dass sie beim Ziehen der Kartenreihe mitmarkiert
   wird. */
.ki-hinweis {
	position: absolute;
	top: 0.5rem;
	right: 0.5rem;
	z-index: 2;
	padding: 0.15em 0.6em;
	border-radius: var(--radius-pill, 9999px);
	background: rgba(90, 90, 90, 0.8);
	color: #fff;
	font-size: 0.625rem;
	font-weight: 500;
	letter-spacing: 0.08em;
	line-height: 1.6;
	white-space: nowrap;
	pointer-events: none;
	-webkit-user-select: none;
	user-select: none;
	filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.35));
}

.ki-hinweis b {
	font-weight: 800;
}

/* Steht im Kachelraster das Bild LINKS neben dem Text (Typ "BildMitText"), säße die Kennzeichnung
   an der rechten Kachelkante über dem TEXT - sie muss aber auf dem Bild liegen, denn nur das ist
   KI-erzeugt. Bezugspunkt ist die Kachel (ein <img> kann kein Kindelement tragen), deshalb wird
   der Abstand von rechts bis zur rechten Bildkante gerechnet: das Bild ist
   "50% - halber Rasterabstand - Rahmen" breit, der Rest bis zur Kachelkante ist genau die
   Gegenrechnung dazu plus der übliche halbe Zentimeter Luft.

   Steht das Bild rechts (Typ "TextMitBild"), stimmt die Grundregel bereits.
   Nur im Kachelraster: im Slider füllt das Bild die ganze Karte, dort ist ebenfalls nichts zu
   korrigieren.

   Unterschieden wird über die Lage des TEXTES ("Text ist letztes Kind" = Bild steht davor) und
   NICHT über ":has(> .container-block__image:first-child)" - erster Versuch, der ins Leere lief:
   erstes Kind der Kachel ist seit dieser Änderung die Kennzeichnung selbst. */
.container-block--float .container-block__item--nebeneinander:has(> .container-block__text:last-child) .ki-hinweis {
	right: calc(50% + var(--shop-gap) / 2 + var(--container-rahmen) + 0.5rem);
}

/* Im Slider liegt der Text ALS OVERLAY auf dem Bild - bei "TextMitBild" oben. Dort stieße die
   Kennzeichnung mit ihm zusammen, also wandert sie an den unteren Rand (Nutzer-Fund 10.09.2026:
   "wenn der beim slider der text oben ist sollte das ai generated nach unten"). Erkennungsmerkmal
   ist dasselbe wie bei der Textkante selbst: steht der Text als ERSTES Kind, liegt er oben. */
.container-block--slider .container-block__item--nebeneinander:has(> .container-block__text:first-child) .ki-hinweis {
	top: auto;
	bottom: 0.5rem;
}

.container-block {
	display: grid;
	gap: var(--shop-gap);
	grid-template-columns: repeat(2, 1fr);
	/* Keine Textmarkierung in den Kacheln und im Slider (Nutzerwunsch): beim Ziehen mit der Maus
	   würde sonst der Text blau markiert statt zu scrollen, und markierbar sein muss hier nichts -
	   die Kacheln sind Bild plus kurze Überschrift, meist als Link. */
	-webkit-user-select: none;
	user-select: none;
}

/* Nur WÄHREND des Ziehens: sonst überschriebe der Greifzeiger den Zeigefinger, den eine verlinkte
   Karte zeigen soll. Das Scroll-Einrasten wird beim Ziehen per Skript kurz abgeschaltet und danach
   wieder gesetzt - dadurch rastet die Reihe am Ende von selbst auf eine Karte ein. */
.container-block--zieht {
	cursor: grabbing;
}

.container-block--slider {
	display: flex;
	grid-template-columns: none;
	overflow-x: auto;
	scroll-snap-type: x mandatory;
	/* Bildlaufleiste ausgeblendet (Nutzerwunsch) - scrollbar BLEIBT die Reihe, gezogen wird per
	   Touch und weitergeschaltet über die Pfeile. Deshalb kein "overflow: hidden": das nähme dem
	   Browser genau das Ziehen weg, um das es geht. Der Platz, den die Leiste unten brauchte,
	   entfällt mit ihr. */
	scrollbar-width: none;
	-ms-overflow-style: none;
	padding-bottom: 0;
}

.container-block--slider::-webkit-scrollbar {
	display: none;
}

/* Hülle um die Reihe: Bezugspunkt für die Pfeile, die deshalb NICHT in der scrollbaren Reihe
   liegen dürfen - dort würden sie mitscrollen. */
.container-slider {
	position: relative;
}

.container-block__pfeil {
	position: absolute;
	top: 50%;
	z-index: 2;
	display: flex;
	align-items: center;
	justify-content: center;
	/* 2rem und nicht mehr: der Mittelpunkt sitzt auf der Kante, die halbe Breite (1rem) ragt also
	   nach aussen - genau so viel, wie der Innenabstand des Inhaltsbereichs hergibt (16px). Bei
	   2,5rem ragte die Seite um 4px über und bekam einen waagerechten Scrollbalken (gemessen:
	   scrollWidth 989 bei clientWidth 985). */
	width: 2rem;
	height: 2rem;
	padding: 0;
	border: 1px solid var(--border, #ddd);
	border-radius: 50%;
	background: var(--card, #fff);
	color: var(--text, inherit);
	cursor: pointer;
	box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
}

/* Muss NACH der Grundregel stehen: "display: flex" oben würde das Attribut sonst überstimmen. */
.container-block__pfeil[hidden] {
	display: none;
}

.container-block__pfeil:hover,
.container-block__pfeil:focus-visible {
	background: var(--primary, #18181b);
	color: var(--primary-foreground, #fff);
	border-color: var(--primary, #18181b);
}

/* Nutzerwunsch: der MITTELPUNKT des Kreises sitzt genau auf der Kante der Reihe - er liegt also
   zur Hälfte auf dem Bild und zur Hälfte daneben. Deshalb Kante 0 plus eine Verschiebung um die
   halbe eigene Breite, statt eines Abstands vom Rand. */
.container-block__pfeil--zurueck {
	left: 0;
	transform: translate(-50%, -50%);
}

.container-block__pfeil--vor {
	right: 0;
	transform: translate(50%, -50%);
}

/* Die scrollbare Kartenreihe bleibt bewusst erhalten (statt eines Ein-Bild-Karussells wie im
   alten Shop), damit auch ohne Autoplay mehrere Einträge auf einen Blick sichtbar sind. Die
   BREITE setzen die beiden Sonderfälle weiter unten: dynamisch je Bild ohne Autoplay,
   volle Streifenbreite mit Autoplay. */
.container-block--slider .container-block__item {
	flex: 0 0 auto;
	scroll-snap-align: start;
}

/* Nutzervorschlag: die Karten sind nicht mehr gleich BREIT, sondern gleich HOCH - die Breite
   ergibt sich aus dem Bild. Damit verschwinden die seitlichen Ränder, über die der Nutzer
   gestolpert ist ("beim ersten slider bild ist rechts und links ein rand"): sie entstanden, weil
   ein 3:2-Bild in einen festen 16:9-Rahmen eingepasst wurde. Bei gleicher Höhe passt jeder Rahmen
   exakt zu seinem Bild, unabhängig vom Format - ohne Beschnitt und ohne Leerstreifen.

   Der Autoplay-Streifen ist ausgenommen: der ist ein Hero-Banner über die volle Breite mit
   eigenem 3:1-Format, dort wäre eine mitwachsende Breite sinnlos.

   Die Obergrenze bleibt: ein sehr breites Bild (die Banner auf demo4 sind 4,2:1) würde bei 315 px
   Höhe über 1300 px breit und damit breiter als die Seite. Greift sie, entstehen bei diesem einen
   Bild wieder Streifen oben/unten - das ist der Preis dafür, dass die Reihe nicht von einer
   einzigen Karte gesprengt wird. */
.container-block--slider:not(.container-block--autoplay) .container-block__item {
	width: auto;
	min-width: 12rem;
	max-width: min(720px, 85vw);
}

.container-block--slider:not(.container-block--autoplay) .container-block__image {
	/* Das Verhältnis kommt jetzt vom Bild selbst, nicht mehr aus einer Vorgabe.

	   Die Höhe ist zusätzlich an die Fensterbreite gekoppelt: eine Karte darf höchstens 85vw
	   breit werden, ein 3:2-Bild braucht bei Höhe h also 1,5*h Breite. Ohne die 56vw-Grenze
	   griffe im schmalen Fenster die Breitenbremse und genau die seitlichen Streifen wären
	   zurück, um die es hier geht (bei 500 px Fenster gemessen: 423 statt 472 px). */
	aspect-ratio: auto;
	height: min(315px, 56vw);
	width: auto;
	max-width: 100%;
	max-height: none;
}

/* Autoplay-Slider (AnzeigeIntervallMs > 0, siehe ContainerListe.razor) - reine
   CSS-Animation statt JS-Karussell: die Einträge werden zweimal gerendert, ein endloser
   Loop verschiebt den Track um exakt die Breite eines Satzes (-50%, da doppelt so breit). */

.container-block--autoplay {
	overflow: hidden;
	padding-bottom: 0;
	container-type: inline-size;
	/* Bezugspunkt für die Punktleiste, die über dem Bild am unteren Rand liegt. */
	position: relative;
}

/* Punktleiste (Nutzerwunsch 09.09.2026): je Bild ein Punkt, der aktuelle leuchtet, ein Klick
   springt dorthin. Welcher Punkt wann leuchtet und wohin er springt, steht in den erzeugten
   Regeln je Block (siehe ContainerListe.razor) - hier nur das Aussehen.

   Der aktive Punkt trägt die Primärfarbe (Nutzerwunsch) - er soll als Markierung gelesen werden
   und nicht bloß als "der hellere". Die ruhenden bleiben halbtransparent weiß: sie liegen auf dem
   BILD und nicht auf einer Kartenfläche, eine Themefarbe verschwände dort auf hellen Motiven. */
.container-block__punkte {
	--punkt-aus: rgba(255, 255, 255, 0.45);
	--punkt-an: var(--primary, #fff);
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0.75rem;
	display: flex;
	justify-content: center;
	gap: 0.5rem;
}

.container-block__punkt {
	display: block;
	width: 0.75rem;
	height: 0.75rem;
	border-radius: 50%;
	background: var(--punkt-aus);
	/* Dunkler Saum, damit die Punkte auch auf einem weißen Bildbereich sichtbar bleiben. */
	border: 1px solid rgba(0, 0, 0, 0.35);
	cursor: pointer;
}

/* Das Auswahlfeld selbst ist unsichtbar, bleibt aber bedienbar und im Fokuslauf - deshalb NICHT
   "display: none" (das nähme es aus der Tastaturbedienung). */
.container-block__wahl {
	position: absolute;
	width: 1px;
	height: 1px;
	margin: 0;
	opacity: 0;
	pointer-events: none;
}

.container-block__punkt:has(:focus-visible) {
	outline: 2px solid var(--punkt-an);
	outline-offset: 2px;
}

.container-block__track {
	display: flex;
	/* KEIN Abstand zwischen den Einträgen: im Autoplay ist jeder Eintrag genau so breit wie der
	   sichtbare Ausschnitt, ein Abstand wäre ein Streifen mitten im Bildwechsel - und er würde die
	   Schrittrechnung verschieben, die in Prozent der Track-Breite arbeitet (siehe
	   ContainerListe.razor). */
	gap: 0;
	width: max-content;
	/* Die Schrittfolge kommt aus der Komponente (Standzeit je Bild + kurzes Gleiten), weil
	   @keyframes keine Variablen als Stützstellen kennen. Das lineare Durchlaufen bleibt als
	   Rückfall, falls die Variable einmal fehlt. */
	animation: var(--container-slider-schritte, container-block-scroll) var(--container-slider-dauer, 30s) ease-in-out infinite;
	/* Greift erst, wenn die Animation über einen angeklickten Punkt abgeschaltet ist (eine laufende
	   Animation hat immer Vorrang vor einem Übergang) - dann gleitet der Sprung zum gewählten Bild,
	   statt hart umzuspringen. */
	transition: transform 0.4s ease-in-out;
}

.container-block__track:hover,
.container-block__track:focus-within {
	animation-play-state: paused;
}

/* Volle Containerbreite statt der 320px-Kartenbreite des manuellen Karussells - im
   Autoplay-Modus soll immer nur EIN Eintrag sichtbar sein (klassischer breiter Hero-Slider),
   nicht mehrere nebeneinander. "100cqw" statt Prozent, da eine prozentuale Breite hier gegen
   den eigenen Flex-Container (width: max-content) zirkulär wäre - Container Query Units lösen
   stattdessen zuverlässig gegen die tatsächliche Breite von .container-block--autoplay auf. */
.container-block--autoplay .container-block__item {
	flex: 0 0 auto;
	width: 100cqw;
}

/* Rückfall-Schrittfolge: durchlaufendes Laufband. Sie war bis 09.09.2026 die einzige und hat
   deshalb den Nutzer-Fund ausgelöst - *"die bilder rauschen einfach nur durch. sie müssen aber an
   der entsprechenden für die gewünschte Dauer stehen bleiben"*. Die echte Schrittfolge mit
   Standzeit erzeugt ContainerListe.razor. */
@keyframes container-block-scroll {
	from {
		transform: translateX(0);
	}
	to {
		transform: translateX(-50%);
	}
}

@media (prefers-reduced-motion: reduce) {
	.container-block__track {
		animation: none;
		transition: none;
	}

	/* Der Track steht dann auf dem ersten Bild - also leuchtet auch dessen Punkt, statt dass die
	   Leiste mitläuft oder gar keiner markiert ist. Angeklickt werden können sie weiterhin. */
	.container-block__punkt {
		animation: none;
	}

	.container-block__punkte .container-block__punkt:first-child {
		background: var(--punkt-an);
	}

	/* Der Knauf springt dann ohne Gleiten in seine Endlage - der Zustand bleibt erkennbar, nur
	   die Bewegung entfällt. */
	.shop-schieber,
	.shop-schieber::after {
		transition: none;
	}
}

.container-block__item {
	display: flex;
	flex-direction: column;
	/* Bezugspunkt für die KI-Kennzeichnung auf dem Bild (siehe ".ki-hinweis"). */
	position: relative;
	gap: 0.5rem;
	border-radius: var(--radius, 0.5rem);
	overflow: hidden;
	text-decoration: none;
	color: inherit;
	background: var(--card, #fff);
	/* Als Variable, weil die Bildbreite der "nebeneinander"-Kacheln damit rechnet (siehe dort). */
	--container-rahmen: 1px;
	border: var(--container-rahmen) solid var(--border, #ddd);
	transition: transform 0.2s ease-out, box-shadow 0.2s ease-out;
}

/* Verlinkte Startseiten-Kacheln heben sich beim Überfahren an - dieselbe Geste wie bei den
   Artikelkarten (".artikel-card:hover"), damit "anklickbar" im ganzen Shop gleich aussieht.
   Nur für tatsächlich verlinkte Kacheln: ContainerEintragAnzeige rendert je nach LinkTyp
   entweder ein <a> oder ein <div> (LinkTyp "Kein"/"Auftragsvorlage"), und eine Kachel ohne
   Ziel darf keine Klickbarkeit vortäuschen. */
a.container-block__item:hover,
a.container-block__item:focus-visible {
	transform: translateY(-2px);
	box-shadow: var(--shadow-card, none);
}

.container-block__image {
	width: 100%;
	display: block;
	/* "contain" statt "cover" (Nutzer-Fund 2026-09-04: die im Backend gepflegten Container-Bilder
	   "passten nicht in die Container"): Das Bild wird proportional so weit VERKLEINERT, dass es
	   komplett in den Rahmen passt - vorher schnitt "cover" bei jedem Bild, dessen Seitenverhältnis
	   nicht exakt dem Rahmen entsprach, die überstehenden Ränder ab. Der Rahmen behält sein festes
	   Seitenverhältnis (gleich hohe Kacheln in einer Reihe), ein abweichendes Bild bekommt
	   stattdessen schmale Leerstreifen an den Seiten bzw. oben/unten.
	   Gilt bewusst für ALLE Anzeigearten: eine gemischte Variante (Slider füllend/"cover",
	   nur Float-Kacheln eingepasst) war kurzzeitig umgesetzt und wurde vom Nutzer wieder
	   verworfen - der Beschnitt störte auch im Slider. */
	object-fit: contain;
	aspect-ratio: 16 / 9;
	/* Zusätzliche harte Höhenobergrenze (unabhängig von aspect-ratio/Breitenberechnung) -
	   Nutzer-Fund 2026-08-26: bei 100cqw-Breite (Autoplay-Slider, siehe
	   .container-block--autoplay weiter unten) wurde die Höhe auf einem echten Gerät
	   unverhältnismäßig groß, in dieser Testumgebung nicht reproduzierbar (dort korrektes
	   16:9). Diese Grenze verhindert das unabhängig von der tatsächlichen Ursache. */
	max-height: min(45vw, 420px);
}

/* Float-Kacheln: quadratische Bildfläche (Nutzerentscheidung 2026-09-04, "Variante 1" aus der
   Vorschau) - der alte Shop hatte hier 250x250 px, das neue mitwachsende Raster kommt bei voller
   Seitenbreite auf 235x235 px. Bewusst NUR für den Float-Block: die Slider-Blöcke behalten ihr
   Breitformat (16:9 bzw. 3:1 beim Autoplay-Hero, siehe unten). */
.container-block--float .container-block__image {
	aspect-ratio: 1 / 1;
	/* Nutzerwunsch: "das bild immer so hoch wie den container machen. zur not wird links und
	   rechts abgeschnitten." Damit gilt im Kachelraster "cover" statt des sonst überall
	   verwendeten "contain" (siehe die Grundregel oben) - das Bild füllt die Fläche und wird
	   dafür seitlich beschnitten.

	   Das dreht die Entscheidung vom 04.09.2026 gezielt für DIESE eine Anzeigeart zurück; damals
	   war "cover" überall verworfen worden, weil der Beschnitt auch im Slider störte. Der Slider
	   behält deshalb "contain". */
	object-fit: cover;
	/* Die Höhenbremse der Grundregel (max-height: min(45vw, 420px)) muss hier weg: sie stammt aus
	   dem Autoplay-Streifen mit 100cqw-Breite und würde hier genau das verhindern, worum es geht -
	   eine Kachel mit viel Text kann höher als 420 px werden, das Bild bliebe dann stehen und
	   unten entstünde wieder ein leerer Streifen. Im Kachelraster gibt es das zugrunde liegende
	   Problem nicht: die Höhe kommt aus der Rasterzeile, nicht aus einer Breitenrechnung. */
	max-height: none;
}

/* Einfache Kacheln: ist die Rasterzeile höher als das Bild (weil eine Nachbarkachel mehr Text
   hat), wächst das Bild mit, statt unten einen leeren Streifen zu lassen. */
.container-block--float .container-block__item:not(.container-block__item--nebeneinander) .container-block__image {
	flex: 1 1 auto;
	min-height: 0;
}

/* Bild+Text: das Bild auf die volle Kachelhöhe strecken. Bewusst nur das Bild ("align-self")
   und nicht der ganze Eintrag ("align-items") - der Text soll weiterhin mittig neben dem Bild
   stehen und nicht nach oben rutschen. */
.container-block--float .container-block__item--nebeneinander .container-block__image {
	align-self: stretch;
}

/* Der Autoplay-Slider ist volle Seitenbreite breit (100cqw, siehe oben) - bei 16:9 würde er
   damit über 700px hoch und lief in die "max-height"-Notbremse, was den Rahmen faktisch auf ein
   krummes Verhältnis brachte. Ein eigenes 3:1-Verhältnis für diesen Hero-Streifen bleibt auf
   allen Breiten unter der Obergrenze und gibt Redakteuren ein glattes Zielformat (siehe
   Empfehlung: 1500 x 500 px). */
.container-block--autoplay .container-block__image {
	aspect-ratio: 3 / 1;
}

/* "BildMitText"/"TextMitBild": Bild und Text nebeneinander statt untereinander (Nutzerwunsch).
   Die Seite ergibt sich aus der Reihenfolge im Markup (siehe ContainerEintragAnzeige) - bei
   "BildMitText" steht das Bild links, bei "TextMitBild" rechts.

   Bewusst "flex-wrap" statt einer Medien- oder Container-Abfrage: die Einträge stehen je nach
   Anzeigeart in einem 220px-Raster, in einer 560px-Slider-Karte oder über die volle Breite. Mit
   Mindestbreiten an beiden Teilen bricht die Zeile von selbst um, sobald es zu eng wird - eine
   feste Schwelle wäre für alle drei Fälle gleichzeitig nie richtig. Eine Container-Abfrage am
   Eintrag selbst wäre der Preis nicht wert: sie würde "100cqw" für die Bilder darin umdeuten und
   damit die Breite des Autoplay-Streifens kaputtmachen. */
.container-block__item--nebeneinander {
	flex-direction: row;
	flex-wrap: wrap;
	align-items: center;
	gap: 0;
}

/* Im Kachelraster ist so ein Eintrag DOPPELT so breit wie eine normale Kachel (Nutzerwunsch:
   "die kachel sollte doppelt so breit wie das Bild sein"). Das Bild nimmt davon die Hälfte und ist
   damit genau so breit wie die Nachbarkacheln - die Reihe bleibt optisch im Raster.

   Erster Versuch war eine normale Kachel: bei rund 235 px Breite passen Bild und Text nicht
   nebeneinander, der Umbruch griff, und es stand doch wieder alles untereinander (am echten Stand
   gesehen). Der zweite Versuch nahm die ganze Zeile - zu viel, deshalb jetzt genau zwei Spalten. */
.container-block--float .container-block__item--nebeneinander {
	grid-column: span 2;
}

/* Der frühere Schutz "unter 600px kein span 2" ist entfallen: er war nötig, solange das Raster auf
   schmalen Geräten auf EINE Spalte fiel - "span 2" hätte dann eine zweite, implizite Spalte erzeugt
   und die Seite seitlich hinausragen lassen. Seit es überall mindestens zwei Spalten gibt (siehe
   ".container-block"), kann das nicht mehr passieren. */
@media (min-width: 992px) {
	.container-block {
		grid-template-columns: repeat(4, 1fr);
	}
}

/* Genau EINE Rasterspalte breit - nicht "50%". Nutzer-Fund: "floating container nur mit bild
   sind jetzt niedriger als die anderen beiden, obwohl alle bilder die gleichen maße haben". Grund:
   die doppelte Kachel ist zwei Spalten PLUS dem Abstand dazwischen breit (490 statt 2x237). Die
   Hälfte davon sind 245 - das Bild wurde also einen halben Abstand breiter als eine normale
   Kachel und, weil es quadratisch ist, genauso viel höher. Damit wuchs die Zeilenhöhe, und in den
   reinen Bild-Kacheln blieb unten ein leerer Streifen: sie sahen niedriger aus.
   Der Rahmen muss MIT abgezogen werden - sonst bleibt genau eine Pixelzeile Unterschied stehen
   (vom Nutzer gesehen: "eine pixelzeile würde ich sagen"). Die Rechnung an einem echten Stand:
   die breite Kachel ist außen 490 breit, innen also 488; die Hälfte davon sind 244, minus halber
   Rasterabstand 236 - eine normale Kachel ist außen 237 und innen 235. Der eine fehlende Pixel
   ist der Rahmen, den die breite Kachel einmal hat und den zwei nebeneinanderstehende Kacheln
   zweimal hätten.

   Nur im KACHELRASTER: die ganze Rechnung ist eine Ausrichtung an den Nachbarkacheln. Im Slider
   gibt es kein Raster, dort bestimmt das Bild selbst die Breite (siehe die Slider-Regeln weiter
   oben und die Ergänzung unten). */
.container-block--float .container-block__item--nebeneinander .container-block__image {
	flex: 0 0 calc(50% - var(--shop-gap) / 2 - var(--container-rahmen));
	/* Ohne das gewinnt die AUTOMATISCHE Mindestbreite von Flex-Elementen gegen die Angabe darüber:
	   ein Flex-Element schrumpft von sich aus nie unter seine Inhaltsgröße, und bei einem Bild ist
	   das dessen (über die Höhenbremse gerechnete) Eigengröße. Auf demo4 sind die Container-Bilder
	   1535x1024 px groß - das Bild wurde dadurch 420 statt 235 px breit, Bild und Text passten
	   nicht mehr nebeneinander und die Zeile brach um (Nutzer-Fund: "es sind jetzt auch wieder
	   umbrüche da"). Auf beta19 fiel es nicht auf, weil die Testbilder klein genug waren. */
	min-width: 0;
}

/* Stehen Bild und Text übereinander (schmale Geräte), soll das Bild die Kachel ausfüllen wie in
   jeder anderen Kachel auch - sonst bliebe es bei seiner halben Breite und stünde als schmaler
   Streifen über dem Text (gemessen: 103 px in einer 226 px breiten Kachel).

   Diese Regel MUSS NACH der Grundregel darüber stehen: beide haben dieselbe Spezifität, also
   gewinnt die spätere. Ein erster Versuch stand weiter oben in der Medienabfrage bei den
   Rasterspalten und blieb genau deshalb wirkungslos - der gemessene flex-basis war unverändert
   "calc(50% - 9px)". */
/* Halb Bild, halb Text - AUF JEDER BREITE (Nutzerwunsch 09.09.2026: "das bild und text floating
   element soll weiterhin halb bild halb text sein"). Dafür muss die Mindestbreite des Textes
   (11rem aus der Grundregel) im Kachelraster weg: sie ist der einzige Grund, warum Flexbox auf
   schmalen Geräten umbrach und das Bild dann über dem Text stand. Vorher stand hier eine
   Medienabfrage, die genau diesen Umbruch abgefangen und dem Bild die volle Breite gegeben hat.

   Zu beobachten: unterhalb von rund 400px Gerätebreite wird der Text dadurch schmaler als
   176px - bei 360px sind es noch etwa 150. Der Nutzer sieht sich das an und entscheidet dann. */
.container-block--float .container-block__item--nebeneinander .container-block__text {
	min-width: 0;
}

/* "BildMitText"/"TextMitBild" im SLIDER: der Text liegt AUF dem Bild statt daneben
   (Nutzerentscheidung: *"Dadurch das der Slider eher breitere Bilder erwartet würde ich gerne den
   Text auf dem Bild liegen haben"*). Nebeneinander passte hier nicht: das Bild behält im Slider
   seine Eigenbreite, der Text daneben hätte die Karte entweder gesprengt oder das Bild wieder auf
   die halbe Breite gedrückt - also genau die seitlichen Streifen zurückgeholt, die die dynamische
   Breite gerade beseitigt hat.

   Damit ist die Karte hier wieder eine reine Bildkarte: gleiche Höhe, gleiches Format, gleiche
   Breitenrechnung wie die Nachbarn. Das Kachelraster bleibt beim Nebeneinander (dort sind die
   Bilder quadratisch und schmal, ein Overlay würde das halbe Bild verdecken). */
.container-block--slider .container-block__item--nebeneinander {
	/* Kein Flex mehr: das Bild bestimmt die Kartengröße, der Text wird darauf gelegt. Das
	   "overflow: hidden" der Grundregel schneidet ihn an den runden Ecken sauber ab. */
	display: block;
	position: relative;
}

/* Die Seite, an der der Text hängt, entscheidet weiterhin allein die Reihenfolge im Markup - keine
   zweite Klasse nötig (wie schon beim Nebeneinander): bei "BildMitText" steht der Text NACH dem
   Bild und hängt unten, bei "TextMitBild" davor und hängt oben. */
.container-block--slider .container-block__item--nebeneinander .container-block__text {
	position: absolute;
	left: 0;
	right: 0;
	bottom: 0;
	padding: var(--shop-gap);
	/* Halbtransparente Fläche, damit der Text auf jedem Bild lesbar bleibt (Nutzerwunsch). Fester
	   Schwarzton statt einer Themefarbe: worauf er liegt, ist das Bild und nicht die Kartenfläche -
	   ein heller Themehintergrund würde hier auf einem hellen Foto verschwinden. */
	background: rgba(0, 0, 0, 0.55);
	color: #fff;
}

.container-block--slider .container-block__item--nebeneinander .container-block__text:first-child {
	top: 0;
	bottom: auto;
}

/* Im Autoplay-Streifen liegt unten die Punktleiste - ein Text am unteren Rand braucht deshalb
   Platz darunter, sonst laufen die Zeilen hinter die Punkte. */
.container-block--autoplay .container-block__item--nebeneinander .container-block__text {
	padding-bottom: 2.25rem;
}

.container-block--autoplay .container-block__item--nebeneinander .container-block__text:first-child {
	padding-bottom: var(--shop-gap);
}

.container-block__item--nebeneinander .container-block__text {
	flex: 1 1 50%;
	min-width: 11rem;
	/* In der Zeile braucht der Text rundum denselben Abstand - die Regeln unten sind auf
	   "untereinander" ausgelegt (oben null, wenn ein Bild darüber steht). */
	padding: var(--shop-gap);
}

.container-block__text {
	padding: 0 var(--shop-gap) var(--shop-gap);
}

.container-block__text:first-child {
	padding-top: var(--shop-gap);
}

/* Kundenbereich: Profil-Seite - drei Karten (Allgemein/Passwort/E-Mail) untereinander, siehe
   Profil.razor. Dieselbe zentrierte, max-breite Optik wie die Registrierungs-Seiten. */

.profil-page {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--shop-gap);
	padding: 3rem var(--shop-gap);
}

.profil-page__card {
	width: 100%;
	max-width: 32rem;
}

.profil-page__allgemein {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 0.25rem 1rem;
	margin: 0;
}

.profil-page__allgemein dt {
	font-weight: 500;
	opacity: 0.75;
}

.profil-page__allgemein dd {
	margin: 0;
}

.profil-page__hinweis {
	margin-top: 1rem;
}

.profil-page__form {
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
}

.profil-page__alert {
	margin-bottom: 1.25rem;
}

/* Kundenbereich: Offene Posten - siehe OffenePosten.razor. Tabelle analog zum Warenkorb, aber
   ohne Mobile-Kartenansicht (weniger Spalten, weniger kritisch auf schmalen Bildschirmen). */

.offene-posten-page {
	display: flex;
	justify-content: center;
	padding: 3rem var(--shop-gap);
}

.offene-posten-page__card {
	width: 100%;
	max-width: 48rem;
}

.offene-posten-page__summe {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 0.25rem 1rem;
	margin: 0 0 1.25rem;
}

.offene-posten-page__summe dt {
	font-weight: 500;
	opacity: 0.75;
}

.offene-posten-page__summe dd {
	margin: 0;
}

.offene-posten-page__tabelle {
	width: 100%;
	border-collapse: collapse;
}

.offene-posten-page__tabelle th,
.offene-posten-page__tabelle td {
	padding: 0.5rem;
	text-align: left;
	border-bottom: 1px solid var(--border, #ddd);
}

/* Kundenbereich: Rückstände - siehe Rueckstaende.razor, gleiches Tabellen-Layout wie Offene
   Posten, zusätzlich ein Export-Link. */

.rueckstaende-page {
	display: flex;
	justify-content: center;
	padding: 3rem var(--shop-gap);
}

.rueckstaende-page__card {
	width: 100%;
	max-width: 48rem;
}

.rueckstaende-page__export {
	display: inline-block;
	margin-bottom: 1rem;
	transition: color 0.2s ease-out;
}

/* Reiner Textlink ("Als Datei exportieren") - anders als der gleichnamige Export auf der
   Belege-Seite, der ein Symbol-Button mit Flächen-Hover ist. */
.rueckstaende-page__export:hover,
.rueckstaende-page__export:focus-visible {
	color: var(--primary, inherit);
	text-decoration: underline;
	text-underline-offset: 0.25rem;
}

.rueckstaende-page__tabelle {
	width: 100%;
	border-collapse: collapse;
}

.rueckstaende-page__tabelle th,
.rueckstaende-page__tabelle td {
	padding: 0.5rem;
	text-align: left;
	border-bottom: 1px solid var(--border, #ddd);
}

/* Kundenbereich: Belege - siehe Belege.razor. Master-Detail: BelegArt-Tabs, Kopf-Tabelle,
   darunter die Positionen des ausgewählten Belegs. */

.belege-page {
	display: flex;
	justify-content: center;
	padding: 3rem var(--shop-gap);
}

.belege-page__card {
	width: 100%;
	max-width: 64rem;
}

.belege-page__alert {
	margin-bottom: 1.25rem;
}

.belege-page__belegarten {
	display: flex;
	flex-wrap: wrap;
	gap: 0.5rem;
	margin-bottom: 1rem;
}

.belege-page__belegart {
	padding: 0.375rem 0.75rem;
	border-radius: var(--radius-pill, 9999px);
	border: 1px solid var(--border, #ddd);
	background: var(--card, #fff);
	cursor: pointer;
	font-size: 0.875rem;
}

/* Die noch nicht gewählten Belegart-Pillen gaben beim Überfahren keine Rückmeldung, obwohl
   sie anklickbar sind - dezent gehalten, damit sie nicht mit der gefüllten "aktiv"-Pille
   darunter verwechselt werden. */
.belege-page__belegart {
	transition: background-color 0.2s ease-out, border-color 0.2s ease-out, color 0.2s ease-out;
}

.belege-page__belegart:hover:not(.belege-page__belegart--aktiv),
.belege-page__belegart:focus-visible:not(.belege-page__belegart--aktiv) {
	background: var(--surface, #f4f6f7);
	border-color: var(--primary, #ddd);
	color: var(--primary, inherit);
}

.belege-page__belegart--aktiv {
	background: var(--primary, #18181b);
	color: var(--primary-foreground, #fff);
	border-color: var(--primary, #18181b);
}

.belege-page__suche {
	display: flex;
	gap: 0.5rem;
	margin-bottom: 1rem;
}

.belege-page__tabelle {
	width: 100%;
	border-collapse: collapse;
	margin-bottom: 1.5rem;
}

.belege-page__tabelle th,
.belege-page__tabelle td {
	padding: 0.5rem;
	text-align: left;
	border-bottom: 1px solid var(--border, #ddd);
}

.belege-page__zeile {
	cursor: pointer;
}

.belege-page__zeile:hover {
	background: color-mix(in srgb, var(--primary, #18181b) 5%, transparent);
}

.belege-page__export {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	padding: 0.25rem;
	border-radius: var(--radius-pill, 9999px);
	transition: background-color 0.2s ease-out, color 0.2s ease-out;
}

.belege-page__export:hover {
	background: var(--surface, rgba(0, 0, 0, 0.05));
	color: var(--primary, inherit);
}

.belege-page__zeile--ausgewaehlt {
	background: color-mix(in srgb, var(--primary, #18181b) 8%, transparent);
}

.belege-page__positionen-zeile {
	background: var(--muted, #f4f4f5);
}

.belege-page__tabelle tr.belege-page__positionen-zeile > td {
	padding: 1rem;
}

.belege-page__tabelle--positionen {
	margin-bottom: 0;
}

/* Content & Rechtliches: Impressum/Hotline/Newsletter/Datenschutz/Kontakt/Dateien - siehe
   Pages/Impressum.razor usw. Einheitliche, zentrierte Karten-Optik wie Profil/Registrierung. */

.content-seite {
	display: flex;
	justify-content: center;
	padding: 3rem var(--shop-gap);
}

.content-seite__card {
	width: 100%;
	max-width: 40rem;
}

.content-seite__alert {
	margin-bottom: 1.25rem;
}

.content-seite__akzeptieren {
	margin-top: 1.5rem;
}

.content-seite__form {
	display: flex;
	flex-direction: column;
	gap: 1.25rem;
}

.content-seite__form select,
.content-seite__form textarea {
	width: 100%;
	padding: 0.5rem 0.875rem;
	border: 1px solid var(--border, #ddd);
	font: inherit;
	box-sizing: border-box;
}

.content-seite__form select {
	border-radius: var(--radius-pill, 9999px);
}

/* Mehrzeilig - vollrund wie die einzeiligen Felder würde hier die erste/letzte Textzeile
   anschneiden, deshalb bewusst der Flächen-Radius. */
.content-seite__form textarea {
	border-radius: var(--radius, 0.375rem);
}

.content-seite__dateien {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.5rem;
}

/* ------------------------------------------------------------------------------------------
   Mengenmatrix fuer Matrix-Artikel (Statuscode "99") - siehe Pages/Katalog/ArtikelMatrix.razor.
   Bedienlogik bewusst wie im DeDeSales-Client: Zellen werden selektiert, die Menge wird fuer
   die gesamte Auswahl gemeinsam veraendert. Die Beschriftungen sind hier NICHT diagonal
   gedreht wie im Client - horizontale/vertikale Beschriftungen bleiben im Browser bei langen
   Texten und auf schmalen Viewports lesbar und brauchen keine feste Zellenhoehe.
   ------------------------------------------------------------------------------------------ */
.artikel-matrix-page {
	display: flex;
	flex-direction: column;
	gap: var(--shop-gap);

	/* "width: 100%" ist hier PFLICHT, nicht kosmetisch (echter Fund, Nutzerhinweis): Die Seite
	   liegt in einem column-Flex-Container (.shop-layout__content). Auto-Margins auf der
	   Querachse ("margin: 0 auto") schalten dort das sonst greifende "stretch" ab - das Element
	   wird dadurch shrink-to-fit und richtet sich nach dem BREITESTEN Kind. Das war die
	   Auswahlleiste: ohne Auswahl steht dort ein langer Hinweistext, mit Auswahl nur kurze
	   Bedienelemente - die gesamte Seite samt Tabelle sprang beim ersten Klick real von 736 px
	   auf 518 px. Mit fester Breite hängt die Breite nicht mehr am Inhalt. */
	width: 100%;
	max-width: var(--shop-max-width);
	margin: 0 auto;
	padding: var(--shop-gap);
}

.artikel-matrix-page__kopf {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--shop-gap);
	flex-wrap: wrap;
}

.artikel-matrix-page__titel {
	margin: 0;
	font-size: 1.5rem;
}

.artikel-matrix-page__artikelnummer {
	margin: 0.25rem 0 0;
	color: var(--muted-foreground);
	font-size: 0.875rem;
}

.artikel-matrix-page__auswahlleiste {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	flex-wrap: wrap;
	padding: 0.625rem 0.875rem;
	border: 1px solid var(--border);
	border-radius: 0.5rem;
	background: var(--muted);
}

/* Hinweis und Aktionen teilen sich EINE Rasterzelle und liegen damit übereinander - die Leiste ist
   dadurch immer so hoch (und so breit) wie der grössere der beiden Zustände und springt beim
   Markieren nicht. Siehe die ausführliche Begründung in ArtikelMatrix.razor. */
.artikel-matrix-page__auswahlstapel {
	display: grid;
	flex: 1 1 auto;
	/* Ohne das streckt das Raster seine Kinder auf die volle Zellhöhe, und der Hinweistext klebte
	   oben (Nutzer-Fund). Beide Ebenen sitzen jetzt mittig - bei der höheren der beiden fällt es
	   nicht auf, die niedrigere steht damit zentriert im reservierten Platz. */
	align-items: center;
}

.artikel-matrix-page__auswahlstapel > * {
	grid-area: 1 / 1;
}

.artikel-matrix-page__auswahlaktionen {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	flex-wrap: wrap;
}

/* Behält seinen Platz, ist aber weder sichtbar noch per Tastatur oder Vorlesen erreichbar. */
.artikel-matrix-page__auswahlebene--verborgen {
	visibility: hidden;
}

.artikel-matrix-page__auswahlzahl {
	font-weight: 600;
}

.artikel-matrix-page__hinweis {
	color: var(--muted-foreground);
	font-size: 0.875rem;
}

.artikel-matrix-page__mengenaktionen {
	display: flex;
	gap: 0.25rem;
}

.artikel-matrix-page__meldung {
	margin: 0;
}

/* Breite Matrizen muessen horizontal scrollen koennen, ohne die ganze Seite zu verschieben. */
.artikel-matrix-page__tabellenrahmen {
	overflow-x: auto;
	border: 1px solid var(--border);
	border-radius: 0.5rem;
}

.artikel-matrix-page__matrix {
	border-collapse: collapse;
	width: 100%;
}

.artikel-matrix-page__matrix th,
.artikel-matrix-page__matrix td {
	border: 1px solid var(--border);
	padding: 0;
}

.artikel-matrix-page__ecke {
	background: var(--muted);
}

.artikel-matrix-page__spaltenkopf,
.artikel-matrix-page__zeilenkopf {
	background: var(--muted);
	font-weight: 600;
	white-space: nowrap;
}

/* Zeilenkoepfe bleiben beim horizontalen Scrollen stehen - sonst weiss man in breiten
   Matrizen nicht mehr, in welcher Zeile man gerade Mengen erfasst. */
.artikel-matrix-page__zeilenkopf {
	position: sticky;
	left: 0;
	z-index: 1;
	text-align: left;
}

.artikel-matrix-page__kopfknopf {
	display: block;
	width: 100%;
	padding: 0.5rem 0.75rem;
	border: 0;
	background: none;
	font: inherit;
	color: inherit;
	text-align: inherit;
	cursor: pointer;
}

.artikel-matrix-page__kopfknopf:hover {
	background: var(--accent);
	color: var(--accent-foreground);
}

/* Die Spezifität muss über ".artikel-matrix-page__matrix td" liegen, sonst gewinnt dessen
   "padding: 0" - genau daran ist der Innenabstand hier vorher WIRKUNGSLOS geblieben (die Zelle
   hatte real 0px, nicht die hier notierten 0,25rem). Seitlich etwas mehr Luft auf Nutzerwunsch,
   nachdem das Eingabefeld schmaler geworden ist. */
.artikel-matrix-page__matrix td.artikel-matrix-page__zelle {
	/* Seitlich 0,375rem statt 0,5rem (Nutzerentscheidung): mit 0,5rem kostete der Abstand genau
	   das, was das schmalere Eingabefeld eingespart hatte - die Zelle war wieder bei 120px. */
	padding: 0.25rem 0.375rem;
}

.artikel-matrix-page__zelle {
	text-align: center;
	cursor: pointer;
}

.artikel-matrix-page__zelle--selektiert {
	outline: 2px solid var(--primary);
	outline-offset: -2px;
	background: var(--accent);
}

/* In der markierten Zelle liegt die Fläche auf --accent, und das ist per Theme-Entscheidung
   dieselbe Farbe wie --primary - der Rahmen des Auswahlkästchens (border-primary) verschwand
   dadurch spurlos (Nutzer-Fund). Weiß hebt ihn auf jedem der beiden Zustände ab: gegen die
   orange Zellfläche im leeren, gegen die orange Füllung im angehakten Kästchen.
   Nur in der Matrix - anderswo steht kein Kästchen auf einer Primärfläche. */
.artikel-matrix-page__zelle--selektiert .artikel-matrix-page__zellenwahl button {
	border-color: #fff;
}

/* Zellen mit erfasster Menge bleiben auch ohne Auswahl erkennbar - die Auswahl wird nach dem
   Uebernehmen aufgehoben, die Mengen sollen aber sichtbar bleiben. */
.artikel-matrix-page__zelle--erfasst {
	font-weight: 600;
}

/* Nicht belegte Koordinate: im Client mit "X" gekennzeichnet und nicht auswaehlbar. */
.artikel-matrix-page__zelle--leer {
	background: var(--muted);
	color: var(--muted-foreground);
	cursor: default;
}

/* Auswahlkästchen und Mengensteller NEBENEINANDER (Nutzerentscheidung 11.09.2026: "mengen größer
   99 sind eine seltenheit anstelle die matrix noch größer werden zu lassen, sollten wir es
   nebeneinander packen"). Die Matrix ist waagerecht knapp - jede Spalte kostet Breite -, deshalb
   wird der Mengensteller schmaler statt die Zelle höher. */
.artikel-matrix-page__zelleninhalt {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.375rem;
}

.artikel-matrix-page__zellenwahl {
	display: inline-flex;
	flex: 0 0 auto;
}

.artikel-matrix-page__zelleneingabe {
	display: flex;
	justify-content: center;
}

.artikel-matrix-page__fuss {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: var(--shop-gap);
	flex-wrap: wrap;
}

.artikel-matrix-page__summe {
	color: var(--muted-foreground);
}

/* Matrix-Artikel in der Artikelliste: statt Mengenfeld/Warenkorb-Button nur der Weg in die
   Matrix (siehe ArtikelCard.razor). Die Beschriftung darf in der schmalen Karte nicht
   umbrechen - sonst wird die Zeile zweizeilig und die Karten der Liste unterschiedlich hoch. */
.artikel-card__matrix-button {
	white-space: nowrap;
}
