/* ==========================================================================
   Theme "haas" — Antiquariat Norbert Haas
   Leitidee "Das Kabinett": Pergamentfläche, Tinte, Messing-Haarlinien,
   Salbei nur als Interaktionsakzent. Alle visuellen Tokens sind CSS-
   Custom-Properties; keine Inline-Styles. Light ist primär, Noir muss
   gleichwertig rendern (gleiche Variablennamen, andere Werte).
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Fonts — lokal gehostet (SIL OFL, Lizenzen unter /static/fonts/)
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Cormorant SC"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/static/fonts/cormorant-sc-regular.woff2") format("woff2");
}
@font-face {
  font-family: "Cormorant SC"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("/static/fonts/cormorant-sc-semibold.woff2") format("woff2");
}
@font-face {
  font-family: "Cormorant SC"; font-style: normal; font-weight: 700; font-display: swap;
  src: url("/static/fonts/cormorant-sc-bold.woff2") format("woff2");
}
@font-face {
  font-family: "Italianno"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/static/fonts/italianno-regular.woff2") format("woff2");
}
@font-face {
  font-family: "Livorno"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/static/fonts/livorno-regular.woff2") format("woff2");
}
/* Metrisch an Livorno angepasste Systemschrift: belegt dieselbe Zeilenbox,
   damit der Wechsel auf die Webschrift nichts verschiebt (CLS 0). Werte aus
   den Font-Metriken gegen Times New Roman/Liberation Serif berechnet. */
@font-face {
  font-family: "Livorno Fallback";
  src: local("Times New Roman"), local("Liberation Serif"), local("Tinos");
  size-adjust: 77.5%; ascent-override: 165%; descent-override: 54.5%; line-gap-override: 0%;
}
@font-face {
  font-family: "EB Garamond"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("/static/fonts/eb-garamond-regular.woff2") format("woff2");
}
@font-face {
  font-family: "EB Garamond"; font-style: normal; font-weight: 500; font-display: swap;
  src: url("/static/fonts/eb-garamond-medium.woff2") format("woff2");
}
@font-face {
  font-family: "EB Garamond"; font-style: normal; font-weight: 600; font-display: swap;
  src: url("/static/fonts/eb-garamond-semibold.woff2") format("woff2");
}
@font-face {
  font-family: "EB Garamond"; font-style: italic; font-weight: 400; font-display: swap;
  src: url("/static/fonts/eb-garamond-italic.woff2") format("woff2");
}

/* --------------------------------------------------------------------------
   2. Tokens — Palette: Pergament / Tinte / Messing / Salbei
   -------------------------------------------------------------------------- */
:root, :root[data-theme="light"] {
  --color-parchment: #F6F2EA;
  --color-ink: #1F1F1F;
  --color-brass: #B39B6A;        /* Haarlinien, Icon-Linien, Zierrat */
  --color-brass-strong: #8A744A; /* Messing als TEXT auf Pergament (AA) */
  --color-sage: #C7D2AD;         /* Interaktionsakzent, nie Fläche */
  --color-sage-strong: #66744B;  /* Salbei als Text/Linie mit Kontrast */

  --color-bg: var(--color-parchment);
  --color-surface: #EFE9DC;      /* Kartuschenton, Karten/Panels */
  --color-surface-alt: #E7DFCC;
  --color-text: var(--color-ink);
  --color-text-muted: #5D574B;
  --color-accent: var(--color-brass-strong);
  --color-accent-contrast: #F8F5EE;
  --color-border: #D8CFBA;
  --color-border-soft: #E6DECB;
  --color-ok: #4E6B3C;
  --color-warn: #9C5220;

  --font-body: "EB Garamond", Georgia, "Times New Roman", serif;
  --font-display: "Cormorant SC", "EB Garamond", serif;
  --font-accent: "Livorno", "Livorno Fallback", serif;

  --icon-stroke: 1.5;            /* eine Linienstärke für alle Icons */
  --radius: 2px;                 /* gestochen, kaum gerundet */
  --radius-plate: 4px;
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --space-5: 2.5rem;
  --space-6: 4rem;
  --shadow-card: 0 1px 2px rgb(31 31 31 / 7%), 0 6px 18px -12px rgb(31 31 31 / 25%);
  --shadow-card-hover: 0 2px 4px rgb(31 31 31 / 8%), 0 14px 30px -14px rgb(31 31 31 / 35%);
  --max-width: 74rem;
  --page-pad: clamp(1rem, 3vw, 2.5rem);
  /* Randband für Banner UND Kopfzeile: minimal großzügiger als page-pad,
     ab 1980px Inhaltsbreite wächst nur noch der Rand — Banner- und
     Nav-Kanten bleiben dadurch exakt in einer Linie. */
  --band-pad: clamp(1.25rem, 3.5vw, 3rem);
  --edge-pad: max(var(--band-pad), calc((100% - 1980px) / 2 + var(--band-pad)));
  --hairline: 1px solid var(--color-border);
  --hairline-brass: 1px solid var(--color-brass);
}

:root[data-theme="dark"] {
  /* Noir: Tinte wird Fläche, Pergament wird Text, Messing hellt auf. */
  --color-parchment: #EDE6D6;
  --color-ink: #161513;
  --color-brass: #C9B37E;
  --color-brass-strong: #C9B37E;
  --color-sage: #8FA06B;
  --color-sage-strong: #ADBE87;

  --color-bg: #161513;
  --color-surface: #201E1A;
  --color-surface-alt: #282520;
  --color-text: var(--color-parchment);
  --color-text-muted: #A89F8C;
  --color-accent: var(--color-brass);
  --color-accent-contrast: #161513;
  --color-border: #3A352C;
  --color-border-soft: #2E2A23;
  --color-ok: #7FA268;
  --color-warn: #CE8756;
  --shadow-card: 0 1px 2px rgb(0 0 0 / 40%), 0 8px 20px -12px rgb(0 0 0 / 60%);
  --shadow-card-hover: 0 2px 4px rgb(0 0 0 / 45%), 0 16px 34px -14px rgb(0 0 0 / 70%);
}

/* --------------------------------------------------------------------------
   3. Base
   -------------------------------------------------------------------------- */
* { box-sizing: border-box; }
/* Scrollbar-Gutter fest reservieren: ohne das verschiebt sich die zentrierte
   Kopfzeile (Banner + Nav) bei jeder Navigation zwischen einer kurzen Seite
   (kein Scrollbalken) und einer langen (Scrollbalken vorhanden) um die halbe
   Scrollbalkenbreite -- .site-main/.site-header sind ueber margin:auto
   zentriert, jede Aenderung der verfuegbaren Breite verschiebt beide Kanten
   gleichzeitig. `scrollbar-gutter: stable` reserviert die Spalte permanent,
   ohne auf Seiten ohne Scrollbedarf einen leeren Scrollbalken einzublenden.
   Fallback fuer Browser ohne Unterstuetzung (u. a. Safari < 18.2): erzwungenes
   `overflow-y: scroll`, die aeltere, aber universell unterstuetzte Variante.
   Bislang gab es hier keine eigene overflow-Regel auf html/body. */
html { font-size: clamp(16px, 15px + 0.3vw, 19px); scrollbar-gutter: stable; }
@supports not (scrollbar-gutter: stable) {
  html { overflow-y: scroll; }
}
body {
  margin: 0;
  /* Papierkorn: gekachelte Noise-Textur direkt als body-Hintergrund.
     GPU-Hinweis (AMD RX 480): KEIN position:fixed/inset:0-Zusatzlayer
     dafür verwenden — ein permanenter Vollbild-Kompositing-Layer friert
     den Renderer ein; ein normal gekachelter Hintergrund nicht. */
  background-color: var(--color-bg);
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="180" height="180"><filter id="n"><feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2"/></filter><rect width="180" height="180" filter="url(%23n)" opacity="0.08"/></svg>');
  color: var(--color-text);
  font-family: var(--font-body);
  line-height: 1.6;
  font-size: 1.05rem;
}

h1, h2, h3, .section-title {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.15;
  /* Konvention 12, zweite Haelfte: ein einzelnes langes Kompositum
     ("Allgemeine Geschäftsbedingungen", "Versandkostenpauschale") ist breiter
     als die Spalte, egal wie die Tracks stehen. Gemessen auf /legal/agb bei
     375px: h1 clientWidth 328 gegen scrollWidth 363, also 5px Seitenueberlauf
     fuer das ganze Dokument. Strukturelle Fixes allein reichen fuer Deutsch
     nicht, der Text braucht einen eigenen Umbruchpunkt. */
  overflow-wrap: break-word;
  letter-spacing: 0.02em;
}
a { color: var(--color-accent); text-decoration: none; }
a:hover { text-decoration: underline; text-decoration-color: var(--color-sage-strong); text-underline-offset: 3px; }
img { max-width: 100%; height: auto; }
::selection { background: var(--color-sage); color: var(--color-ink); }

/* Akzentwort in Livorno + Messing. Livorno gibt es nur in Regular:
   font-synthesis verhindert ein gerechnetes Fett.
   0.85em: Livornos Versalien und Oberlängen reichen bis 1.0em über die
   Grundlinie (Italianno: 0.61em), bei line-height 0 ragt alles darüber
   aus der Zeile heraus. 0.85em ergibt dieselbe Oberlänge (0.85em) und
   x-Höhe (0.39em) wie die frühere Italianno-Einstellung. */
.accent-word {
  font-family: var(--font-accent);
  font-weight: 400;
  font-synthesis: none;
  color: var(--color-brass-strong);
  font-size: 0.85em;
  line-height: 0;                 /* hebt die Zeilenhöhe nicht an */
  letter-spacing: 0;
  text-transform: none;
}

/* Icons: Sprite unter /static/img/icons.svg, Linienstärke via Token dort. */
.icon { width: 1.5rem; height: 1.5rem; flex: none; }
.icon-lg { width: 3rem; height: 3rem; }
.icon-xl { width: 4rem; height: 4rem; }

/* --------------------------------------------------------------------------
   4. Kopfzeile — schlank: Wortmarke · Entdecken · Über uns · Icons rechts
   -------------------------------------------------------------------------- */
.site-header {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  /* gleiche Randkante wie der Banner (--edge-pad): Nav-Inhalt ragt nie
     über die Bannerkanten hinaus, auch nicht auf Ultrabreitschirmen */
  padding: var(--space-2) var(--edge-pad);
  background: var(--color-bg);
  border-bottom: var(--hairline-brass);
  flex-wrap: wrap;
}
.main-nav { display: flex; gap: var(--space-4); align-items: center; }
.main-nav a {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.02rem;
  letter-spacing: 0.05em;
  color: var(--color-text);
  padding: var(--space-1) 0;
  border-bottom: 2px solid transparent;
}
.main-nav a:hover, .main-nav a[aria-current="page"] {
  text-decoration: none;
  border-bottom-color: var(--color-sage-strong);
}
/* Wortloser Home-Link (Kompassrose), erstes Element der Nav: gleiche
   Grundlinie und derselbe Hover-/Fokus-Rahmen wie die Textlinks daneben,
   nur mit etwas kleinerem Icon, damit es sich nicht vor sie draengt. */
.main-nav .nav-home { display: inline-flex; align-items: center; }
.main-nav .nav-home .icon { width: 1.35rem; height: 1.35rem; }

.site-nav { display: flex; gap: var(--space-3); align-items: center; margin-left: auto; }
.site-nav a, .theme-toggle {
  display: inline-flex; align-items: center; gap: var(--space-1);
  color: var(--color-text);
  background: none; border: none; cursor: pointer; padding: var(--space-1);
  font: inherit;
}
.site-nav a:hover, .theme-toggle:hover { color: var(--color-brass-strong); text-decoration: none; }
.nav-icon-label { font-size: 0.82rem; letter-spacing: 0.03em; }

/* Konto-Menü in der Kopfzeile: Abmelden wird per Hover/Fokus eingeblendet,
   damit es von jeder Seite aus erreichbar ist. :focus-within ist keine
   Zierde: solange das Menü verborgen ist, ist sein Button nicht
   fokussierbar; erst das Fokussieren des Konto-Links malt das Menü und
   macht den Button beim nächsten Tab erreichbar. Touch kennt kein Hover:
   Antippen des Links führt zur Kontoseite, die dieselbe Steuerung als
   eigenes Element trägt.

   Das Menü ist breiter als der Link, an dem es hängt, und ragt nach links
   über ihn hinaus -- beide berühren sich also vertikal, aber nicht
   horizontal. Wer diagonal zur Beschriftung des Buttons zieht, und genau
   dorthin schaut das Auge, verlässt die schmale Spalte des Links, bevor er
   die Oberkante des Menüs erreicht; das Menü schloss mitten in der
   Bewegung. Im gbernard-Theme, das dieselbe Struktur nutzt, gemessen:
   39px Trigger gegen 113px Menü, 74px Überhang. Eine abgeschnittene Ecke
   lässt sich nicht wegpolstern, deshalb wird der Korridor auf beiden
   Achsen abgedeckt.
   Der Fänger überspannt die untere Hälfte der Trigger-Zeile und reicht
   nach links über den Überhang, sodass die Diagonale innerhalb von
   .nav-account bleibt. Er ist pointer-events:none, solange das Menü zu
   ist, und kann deshalb nie einen Klick auf ein Nachbarelement schlucken.
   Das Schließen ist zusätzlich verzögert: ein kurzer Hover-Aussetzer
   kostet nichts, erneutes Betreten bricht die Verzögerung ab.
   Verborgen wird per visibility statt display, weil visibility
   transitionierbar ist und den Button trotzdem aus der Tab-Reihenfolge
   nimmt -- genau das trägt den :focus-within-Pfad oben. */
.nav-account { position: relative; display: inline-flex; }
.nav-account::after {
  content: ""; position: absolute; top: 50%; bottom: 0; right: 0; width: 12rem;
  pointer-events: none;
}
.nav-account:hover::after,
.nav-account:focus-within::after { pointer-events: auto; }
.nav-account-menu {
  position: absolute; top: 100%; right: 0; z-index: 50; padding-top: var(--space-2);
  visibility: hidden; opacity: 0;
  transition: opacity 0.12s ease 0.32s, visibility 0s linear 0.44s;
}
.nav-account:hover .nav-account-menu,
.nav-account:focus-within .nav-account-menu {
  visibility: visible; opacity: 1;
  transition: opacity 0.12s ease 0s, visibility 0s linear 0s;
}
.nav-account-logout {
  display: block; white-space: nowrap; cursor: pointer; font: inherit;
  font-size: 0.85rem; letter-spacing: 0.02em;
  background: var(--color-surface); color: var(--color-text);
  border: var(--hairline-brass); border-radius: var(--radius);
  box-shadow: var(--shadow-card); padding: var(--space-1) var(--space-3);
}
.nav-account-logout:hover { background: var(--color-surface-alt); color: var(--color-brass-strong); }

/* Kompakte Header-Suche (auf Unterseiten; die Startseite hat den Such-Hero) */
/* füllt die Lücke zwischen Hauptnav und Icon-Nav vollständig aus */
.header-search { position: relative; display: flex; gap: 0; flex: 1 1 14rem; max-width: none; }
.header-search-input {
  flex: 1; padding: var(--space-1) var(--space-2);
  border: var(--hairline); border-right: none;
  border-radius: var(--radius) 0 0 var(--radius);
  background: var(--color-surface); color: var(--color-text);
  font: inherit; font-size: 0.95rem;
}
.header-search-input:focus { outline: 2px solid var(--color-sage); outline-offset: -1px; }
.header-search-btn {
  display: inline-flex; align-items: center;
  padding: var(--space-1) var(--space-2);
  border: var(--hairline); border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--color-surface-alt); color: var(--color-text); cursor: pointer;
  font: inherit; font-size: 0.95rem;
}
.header-search-btn:hover { color: var(--color-brass-strong); }

/* Autocomplete (Header UND Hero nutzen dasselbe Panel-Markup) */
.autocomplete-panel {
  position: absolute; top: 100%; left: 0; right: 0; z-index: 30;
  background: var(--color-surface); border: var(--hairline);
  border-radius: var(--radius); box-shadow: var(--shadow-card-hover);
  max-height: 24rem; overflow-y: auto;
  text-align: left;
}
.autocomplete-item {
  display: flex; gap: var(--space-2); align-items: center;
  padding: var(--space-2); color: var(--color-text);
}
.autocomplete-item:hover, .autocomplete-item.is-active { background: var(--color-surface-alt); text-decoration: none; }
.autocomplete-item img { width: 2.6rem; height: 2.6rem; object-fit: cover; border-radius: var(--radius); border: var(--hairline); }
.autocomplete-item-title { flex: 1; }
.autocomplete-item-price { color: var(--color-text-muted); white-space: nowrap; font-size: 0.9rem; }
.autocomplete-fuzzy-hint { padding: var(--space-1) var(--space-2); color: var(--color-text-muted); font-size: 0.85rem; border-bottom: var(--hairline); font-style: italic; }
.autocomplete-price-note { padding: var(--space-1) var(--space-2); color: var(--color-text-muted); font-size: 0.75rem; border-top: var(--hairline); }
.autocomplete-item-category { flex-wrap: wrap; }
.autocomplete-item-badge {
  font-size: 0.65rem; text-transform: uppercase; letter-spacing: .04em;
  color: var(--color-accent); border: 1px solid var(--color-accent);
  border-radius: var(--radius); padding: 0 var(--space-1); white-space: nowrap;
}
.autocomplete-item-note {
  flex-basis: 100%; color: var(--color-text-muted); font-size: 0.8rem;
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 3; overflow: hidden;
}

.site-main { max-width: var(--max-width); margin: 0 auto; padding: var(--space-2) var(--page-pad) var(--space-4); }

/* --------------------------------------------------------------------------
   5. Startseite — Hero: Banner + Kartusche + riesige Suche
   -------------------------------------------------------------------------- */
/* Marken-Banner: allererstes Element der Seite (vor der Kopfzeile),
   UNBESCHNITTEN und in voller Breite — der Schriftzug in der Kartusche
   ist das Markenzeichen. Über --edge-pad bei 1980px Inhaltsbreite
   gedeckelt (4K) und exakt bündig mit der Kopfzeile. */
.site-banner { padding: var(--space-2) var(--edge-pad) 0; }
.site-banner img {
  display: block; width: 100%; height: auto;
  border: var(--hairline-brass);
  border-radius: var(--radius-plate);
}
.site-banner a { display: block; }

.hero-search-stage {
  text-align: center;
  /* vertikal gerafft, damit die Suche auf 1080p im ersten Viewport steht;
     volle Inhaltsbreite: die Suche flieht mit Produktregal und Säulen */
  padding: var(--space-3) 0 var(--space-4);
  /* Die rise-*-Animation (fill: both) macht jede Sektion zu einem eigenen
     Stacking Context; ohne eigenes z-index läge das Autocomplete-Panel
     unter den späteren Sektionen. Die Bühne deshalb über sie heben. */
  position: relative;
  z-index: 20;
}
/* trägt zusätzlich .section-title (feine Messing-Linie unter dem Titel);
   h1-Selektor gewinnt gegen die späteren .section-title-Maße */
h1.hero-lede {
  font-family: var(--font-accent);
  font-weight: 400;
  font-synthesis: none;
  font-size: clamp(1.5rem, 1.1rem + 1.8vw, 2.2rem);
  margin: 0 0 var(--space-2);
}
.hero-sub {
  color: var(--color-text-muted);
  margin: 0 0 var(--space-3);
  font-size: 1.05rem;
}
.hero-search { position: relative; display: flex; margin: 0 auto; }
/* Rechteckig statt Pille: Messing-Doppellinie (border + abgesetzte
   outline) wie ein Kupferstich-Rahmen. */
.hero-search-input {
  flex: 1;
  font: inherit;
  font-size: clamp(1.05rem, 1rem + 0.5vw, 1.3rem);
  padding: var(--space-3) var(--space-4);
  padding-left: 3.4rem;
  border: 1.5px solid var(--color-brass);
  border-radius: var(--radius-plate);
  outline: 1px solid var(--color-brass);
  outline-offset: 4px;
  background: var(--color-surface);
  color: var(--color-text);
  box-shadow: var(--shadow-card);
}
.hero-search-input:focus {
  border-color: var(--color-brass-strong);
  outline-color: var(--color-brass-strong);
  /* der Salbei-Schein füllt genau den Spalt der Doppellinie */
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-sage) 55%, transparent), var(--shadow-card);
}
.hero-search-icon {
  position: absolute; left: 1.2rem; top: 50%; translate: 0 -50%;
  color: var(--color-brass-strong); pointer-events: none;
}
.hero-search-btn {
  position: absolute; right: 0.45rem; top: 50%; translate: 0 -50%;
  font-family: var(--font-display); font-weight: 600; font-size: 1rem; letter-spacing: 0.04em;
  padding: var(--space-2) var(--space-4);
  border: none; border-radius: var(--radius); cursor: pointer;
  background: var(--color-brass-strong); color: #FBF8F1;
}
:root[data-theme="dark"] .hero-search-btn { background: var(--color-brass); color: var(--color-ink); }
.hero-search-btn:hover { background: #6E5B39; color: #FBF8F1; }
:root[data-theme="dark"] .hero-search-btn:hover { background: #D8C48F; color: var(--color-ink); }
.hero-search .autocomplete-panel { border-radius: var(--radius-plate); }

/* Fleuron: feiner SVG-Zier-Trenner in Kupferstich-Linie (kein Rasterbild) */
.fleuron { display: block; width: min(18rem, 70%); height: 1.5rem; margin: 0 auto; color: var(--color-brass); }
.ornament-divider { margin: var(--space-5) 0 var(--space-3); }

/* Seitenkopf für Unterseiten (Entdecken, Über uns, ...) */
.page-head { text-align: center; max-width: 44rem; margin: 0 auto var(--space-4); }
.page-title { font-size: clamp(1.6rem, 1.3rem + 1.4vw, 2.4rem); margin: 0 0 var(--space-2); }
.page-head .fleuron { margin-bottom: var(--space-2); }
.page-lede { color: var(--color-text-muted); font-size: 1.08rem; margin: 0; }

/* --------------------------------------------------------------------------
   6. Die drei Säulen — große Kategorie-Karten
   -------------------------------------------------------------------------- */
.pillars {
  display: grid; gap: var(--space-4);
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin: var(--space-4) 0;
}
.pillar-card {
  position: relative; display: block; overflow: hidden;
  border: var(--hairline-brass);
  border-radius: var(--radius-plate);
  background: var(--color-surface);
  box-shadow: var(--shadow-card);
  color: var(--color-text);
}
.pillar-card:hover { text-decoration: none; box-shadow: var(--shadow-card-hover); }
.pillar-card img {
  display: block; width: 100%; object-fit: contain;
  /* contain statt cover (2026-09-22): Kartenblaetter im Motiv wurden bei
     abweichendem Bildformat an- oder abgeschnitten, auf schmalen
     Bildschirmen war teils nur der mittlere Ausschnitt sichtbar. Das
     Motiv soll immer vollstaendig sichtbar sein; der Rand fuellt sich mit
     der Kartenfarbe statt zu beschneiden. */
  background: var(--color-surface);
  /* 3:2 statt 5:2 (2026-09-14): die Kachelmotive sind Kartenblaetter im
     Format ~3:2, ein flacheres Fenster schnitt oben und unten ein Drittel
     weg. */
  aspect-ratio: 3 / 2;
  transition: scale 0.5s ease;
}
.pillar-card:hover img { scale: 1.02; }
.pillar-caption {
  display: flex; align-items: baseline; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-top: var(--hairline);
}
.pillar-title {
  font-family: var(--font-display); font-weight: 600;
  /* Drei Karten je Zeile: kleiner als bei zwei, damit "Alte
     Stadtansichten" einzeilig bleibt und die Karten gleich hoch sind. */
  font-size: clamp(1.15rem, 0.8rem + 0.8vw, 1.55rem);
  margin: 0;
}
/* Tablet-Breite, drei Spalten: ein einzelnes langes Wort
   ("Stadtansichten") ist breiter als die Karte und wuerde abgeschnitten. */
@media (max-width: 64rem) {
  .pillar-caption { padding: var(--space-3); gap: var(--space-2); }
  .pillar-title { min-width: 0; hyphens: auto; }
}
.pillar-card:hover .pillar-title { box-shadow: inset 0 -2px 0 var(--color-sage-strong); }
.pillar-sub { color: var(--color-text-muted); margin: 0; font-style: italic; }
.pillar-caption .icon { color: var(--color-brass-strong); align-self: center; }

/* --------------------------------------------------------------------------
   6b. Neuzugänge-Zeile: Banner (Einstieg in /neu) + Newsletter-Kartusche.
   Das Banner-Bild hat hier KEIN festes Seitenverhältnis — es füllt die
   Zeilenhöhe, die die Kartusche vorgibt (object-fit: cover).
   -------------------------------------------------------------------------- */
.newest-row {
  display: grid; gap: var(--space-4);
  grid-template-columns: minmax(0, 3fr) minmax(20rem, 2fr);
  align-items: stretch;
  margin: var(--space-4) 0;
}
/* Faellt die Newsletter-Kartusche weg (bestaetigter Abonnent), bliebe sonst
   eine leere 2fr-Spalte stehen. */
.newest-row:not(:has(.newsletter-panel)) { grid-template-columns: minmax(0, 1fr); }
.newest-row .pillar-card { display: flex; flex-direction: column; }
.newest-row .pillar-card picture { flex: 1; min-height: 9rem; }
.newest-row .pillar-card img { height: 100%; aspect-ratio: auto; }
.newsletter-panel {
  border: var(--hairline-brass); border-radius: var(--radius-plate);
  background: var(--color-surface);
  box-shadow: var(--shadow-card);
  padding: var(--space-4);
  display: flex; flex-direction: column; justify-content: center;
  gap: var(--space-2);
  text-align: center;
}
.newsletter-panel .fleuron { width: min(11rem, 70%); height: 1.1rem; }
.newsletter-panel-title {
  margin: 0;
  font-size: clamp(1.25rem, 1.05rem + 1vw, 1.8rem);
}
.newsletter-panel-body { margin: 0; color: var(--color-text-muted); font-size: 0.98rem; }

/* Newsletter-Formular (Startseiten-Kartusche UND Footer, s. partials.html) */
.newsletter-row { display: flex; gap: var(--space-1); }
.newsletter-row input {
  flex: 1; min-width: 0; padding: var(--space-1) var(--space-2);
  border: 1px solid var(--color-brass-strong); border-radius: var(--radius);
  background: var(--color-parchment); color: var(--color-ink); font: inherit; font-size: 0.95rem;
}
:root[data-theme="dark"] .newsletter-row input {
  background: var(--color-surface-alt); color: var(--color-text); border-color: var(--color-brass);
}
.newsletter-row input:focus { outline: 2px solid var(--color-sage-strong); outline-offset: 0; }
.newsletter-row .btn { padding: var(--space-1) var(--space-3); font-size: 0.95rem; }
.newsletter-hint { font-size: 0.78rem; opacity: 0.8; margin: var(--space-2) 0 0; text-align: left; }

/* --------------------------------------------------------------------------
   7. Produktregal + Karten (product-card wird engine-weit genutzt)
   -------------------------------------------------------------------------- */
.section-title {
  font-size: clamp(1.4rem, 1.2rem + 1vw, 2rem);
  margin: var(--space-5) 0 var(--space-3);
  text-align: center;
}
.section-title::after {
  content: ""; display: block; width: 4.5rem; height: 1px;
  background: var(--color-brass); margin: var(--space-2) auto 0;
}
.section-title-flush { margin-top: 0; }
.product-grid {
  display: grid; gap: var(--space-4);
  grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
}
.product-card {
  display: flex; flex-direction: column; gap: var(--space-2);
  color: var(--color-text);
  border: var(--hairline); border-radius: var(--radius-plate);
  background: var(--color-surface);
  box-shadow: var(--shadow-card);
  padding: var(--space-2) var(--space-2) var(--space-3);
  transition: box-shadow .15s ease, border-color .15s ease;
}
/* haas 2026-09: ohne den Preis wirkte Bild + Titel lose auf der Flaeche
   (kein gemeinsamer Rahmen mehr, seit die Karte die Preiszeile verlor).
   Der Rahmen liegt jetzt auf .product-card selbst, nicht mehr nur auf dem
   Bild: die ganze Karte wird zum Passepartout, das Bild sitzt darin, der
   Titel darunter - eine Kartusche statt zweier loser Elemente. */
.product-card:hover { text-decoration: none; box-shadow: var(--shadow-card-hover); border-color: var(--color-brass); }
.product-card-img {
  display: block; border-radius: var(--radius);
  background: var(--color-surface-alt); overflow: hidden;
}
.product-card-img img { display: block; width: 100%; aspect-ratio: 4 / 3; object-fit: contain; padding: var(--space-2); }
.product-card-title { font-family: var(--font-display); font-weight: 600; font-size: 1.05rem; line-height: 1.25; }
.product-card:hover .product-card-title { color: var(--color-brass-strong); }
.category-search-card { border-color: var(--color-brass); }
.category-search-card .product-card-img {
  aspect-ratio: 4 / 3; display: flex; align-items: center; justify-content: center;
}
.category-search-badge {
  align-self: flex-start; font-size: 0.65rem; text-transform: uppercase; letter-spacing: .04em;
  color: var(--color-accent); border: 1px solid var(--color-accent);
  border-radius: var(--radius); padding: 0 var(--space-1);
}
.category-search-note {
  /* Full results page has room the autocomplete dropdown doesn't: show much
     more of the note, capped only so a very long blurb can't stretch the
     grid row past reason -- not tied to Settings.SearchNoteLines, which
     governs the tight autocomplete snippet only (see .lines-N below). */
  color: var(--color-text-muted); font-size: 0.85rem;
  display: -webkit-box; -webkit-box-orient: vertical;
  -webkit-line-clamp: 10; overflow: hidden;
}
.category-search-note p { margin: 0; }
/* Settings.SearchNoteLines (autocomplete dropdown only): server picks the
   class (1-10), no inline styles. */
.lines-1 { -webkit-line-clamp: 1; }
.lines-2 { -webkit-line-clamp: 2; }
.lines-3 { -webkit-line-clamp: 3; }
.lines-4 { -webkit-line-clamp: 4; }
.lines-5 { -webkit-line-clamp: 5; }
.lines-6 { -webkit-line-clamp: 6; }
.lines-7 { -webkit-line-clamp: 7; }
.lines-8 { -webkit-line-clamp: 8; }
.lines-9 { -webkit-line-clamp: 9; }
.lines-10 { -webkit-line-clamp: 10; }

/* --------------------------------------------------------------------------
   8. Haus-Zeile (grosse Stich-Icons) + Über-uns-Anriss
   -------------------------------------------------------------------------- */
.haus-row {
  display: grid; gap: var(--space-4);
  grid-template-columns: repeat(3, 1fr);
  margin: var(--space-6) 0;
  text-align: center;
}
.haus-item { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); }
.haus-item .icon-xl { color: var(--color-brass-strong); }
.haus-item h3 { margin: 0; font-size: 1.15rem; }
.haus-item p { margin: 0; color: var(--color-text-muted); font-size: 0.95rem; max-width: 26ch; }

.about-teaser {
  max-width: 42rem; margin: var(--space-6) auto; text-align: center;
}
.about-teaser h2 { font-size: clamp(1.4rem, 1.2rem + 1vw, 2rem); margin: 0 0 var(--space-3); }
.about-teaser p { margin: 0 0 var(--space-3); }
.about-link { font-family: var(--font-display); font-weight: 600; letter-spacing: 0.04em; }

/* --------------------------------------------------------------------------
   9. Produktseite — der Viewer beherrscht die Seite
   -------------------------------------------------------------------------- */
.product-detail { display: block; }
.product-detail-media { margin-bottom: var(--space-4); }

/* Deep-Zoom-Buehne: liegt UEBER dem Vorschaubild, ersetzt es nicht.
   .dz-image-box ist die gemeinsame position:relative-Buehne fuer Vorschau-
   <picture> und Viewer-Overlay; .dz-stage deckt sie transparent ab, bis
   OpenSeadragon seine ersten Kacheln gemalt hat -- so scheint die Vorschau
   bis dahin unveraendert durch, kein leerer Rahmen, kein Blitzen. */
.dz-image-box { position: relative; }
.dz-stage {
  position: absolute;
  inset: 0;
  border-radius: var(--radius-plate);
  overflow: hidden; /* runde Ecken auch fuer den OpenSeadragon-Canvas */
  background: transparent;
}
/* Mindesthoehe des Rahmens, reserviert ab der ersten Darstellung (nicht
   erst beim Aufbau, s. den zweiten Absatz). Ohne sie erbt die Buehne
   die Hoehe des Vorschaubilds, und das ist bei einem Panorama fast nichts:
   die Mailaender Ansicht (12640 x 1400, also 9:1) kommt in der Spalte auf
   rund 147 Pixel Hoehe. Durch ein so flaches Fenster laesst sich ein Blatt
   mit 1400 Pixeln Hoehe nicht sinnvoll betrachten, egal wie weit man
   hineinzoomt. Der Rahmen wird deshalb im Betrachter mindestens gut drei
   Mal so hoch. Ein breites Blatt fuellt ihn dadurch nicht aus: es bleibt
   ganz sichtbar (nichts wird beschnitten) und laesst oben und unten je
   einen leeren Streifen. Damit dieser Streifen nicht wandert - das Blatt
   also nicht an die Ober- oder Unterkante gezogen werden kann - haelt
   deepzoom.js es auf dieser Achse mittig fest (visibilityRatio dort, mit
   Begruendung). Hohe und normalformatige Blaetter sind ohnehin hoeher als
   dieser Wert, fuer sie aendert sich nichts. Der vh-Anteil deckelt den
   Rahmen auf kleinen Geraeten, damit er nicht den halben Bildschirm
   auffrisst.
   Der Platz haengt an data-dzi-eligible, steht also schon im Markup und
   damit ab der ersten Darstellung. Wuerde der Rahmen erst beim Oeffnen
   wachsen, rueckte alles darunter um rund 385 Pixel nach unten - gemessen
   vier Sekunden nach dem Laden, mitten unter dem Anfrage-Knopf, also genau
   dann, wenn jemand danach greift.
   NUR ab Tablettbreite UND ab 30rem Fensterhoehe. Zur Breite: auf dem
   Telefon ist die Spalte so schmal, dass jedes Blatt flacher als dieser
   Wert ist, der Rahmen also fast leer bliebe und rund die Haelfte des
   Bildschirms fuellte. OpenSeadragon setzt darin touch-action:none, womit
   dieser Bereich jede senkrechte Wischgeste schluckt: die Seite liesse
   sich mit dem Finger nicht mehr daran vorbei scrollen.
   Zur Hoehe: die Breitenbedingung allein laesst das quer gehaltene Telefon
   durch (812 x 375 ist breiter als 48rem), und dort waeren die reservierten
   min(28rem, 60vh) = 225 Pixel volle 60 Prozent des Bildschirms - dieselbe
   Wischfalle wie hochkant, nur schlimmer. Unterhalb von 30rem Hoehe (480px)
   gilt deshalb dasselbe wie auf dem Telefon: kein reservierter Platz.
   Bewusst NICHT ausgenommen ist das Tablett hochkant (768 x 1024, auch ein
   Tastgeraet): dort sind es 448 von 1024 Pixeln, also 44 Prozent, mit mehr
   als der halben Seite als Wischflaeche ober- und unterhalb - und genau
   dort ist der grosse Rahmen der Zweck der Uebung.
   Auf dem Telefon bleibt der Rahmen so hoch wie das Blatt, fuer grosse
   Ansichten gibt es den Vollbild-Knopf. */
@media (min-width: 48rem) and (min-height: 30rem) {
  .dz-image-box[data-dzi-eligible] {
    min-height: min(28rem, 60vh);
    display: flex;
    align-items: center;   /* Vorschau mittig, damit der reservierte Platz gewollt aussieht */
    justify-content: center;
  }
}
/* Das ganze Blatt bleibt sichtbar, es wird eingepasst und nicht beschnitten.
   Bei einem flachen Panorama bleiben dadurch Streifen ueber und unter dem
   Blatt frei; die tragen die Seitenfarbe und wechseln mit dem Farbschema
   (--color-bg ist in Noir dunkel), der Betrachter wird also nicht heller
   oder dunkler als die Seite. */
.dz-image-box.dz-aktiv { background: var(--color-bg); }
/* Ausblenden erst, wenn wirklich eine Kachel gemalt ist - gesetzt vom
   tiled-image-drawn-Horcher in deepzoom.js (NICHT tile-drawn: den weist der
   WebGL-Zeichner ab, Begruendung dort). Solange nichts gezeichnet ist, traegt
   die Vorschau den Rahmen; danach saessen Vorschau und eingepasstes Blatt
   nicht pixelgenau aufeinander - Rand, Schatten und die Deckelung durch
   max-height:70vh verschieben die Vorschau gegen das Blatt -, das ergaebe
   dasselbe Blatt zweimal mit Geisterkante. visibility statt display: der
   reservierte Platz und damit die Rahmenhoehe bleiben erhalten. */
.dz-image-box.dz-bereit > .lightbox-link { visibility: hidden; }
.dz-viewer { position: absolute; inset: 0; width: 100%; height: 100%; }
/* Ins Vollbild geht die Buehne, nicht der Betrachter darin (deepzoom.js
   ruft stage.requestFullscreen) -- eine Regel auf .dz-viewer:fullscreen
   griffe nie. Der Betrachter fuellt die Buehne ohnehin per inset:0, es
   bleibt die Flaeche: ohne eigene Farbe schiene der schwarze
   ::backdrop-Grund des Browsers durch die Briefkasten-Streifen eines
   flachen Blattes, waehrend dieselben Streifen im Rahmen die Seitenfarbe
   tragen (dz-aktiv oben). */
.dz-stage:fullscreen { background: var(--color-bg); }
/* Steuerelemente: groß und deckend, damit sie sich klar vom Kartenbild
   abheben und sofort als Bedienelemente erkennbar sind (Betreiber-Vorgabe). */
/* pointer-events: none auf den Leisten, auto auf den Knoepfen: die Leisten
   liegen mit z-index 10 ueber dem Betrachter und wuerden sonst als ganze
   Rechtecke Ziehen und Zoomen schlucken - auch in den Luecken zwischen den
   Knoepfen und, in der Telefonfassung weiter unten, ueber die volle
   Rahmenhoehe (die Leisten spannen dort top/bottom auf, damit die Knoepfe
   nicht groesser als der Rahmen werden koennen). */
.dz-toolbar {
  position: absolute; top: var(--space-2); right: var(--space-2); z-index: 10;
  display: flex; gap: var(--space-2);
  pointer-events: none;
}
.dz-controls {
  position: absolute; top: var(--space-2); left: var(--space-2); z-index: 10;
  display: flex; flex-direction: column; gap: var(--space-2);
  pointer-events: none;
}
.dz-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 3rem; height: 3rem;
  border: 1.5px solid var(--color-brass-strong); border-radius: var(--radius-plate);
  background: var(--color-parchment); color: var(--color-ink); cursor: pointer;
  box-shadow: 0 2px 6px rgb(31 31 31 / 30%);
  pointer-events: auto; /* Gegenstueck zum none der Leisten, s. oben */
}
.dz-btn .icon { width: 1.7rem; height: 1.7rem; }
.dz-btn:hover, .dz-btn:focus-visible { background: var(--color-sage); outline: none; }
/* Genau dort, wo oben KEIN Platz reserviert wird (Telefon, quer gehaltenes
   Telefon), ist die Buehne nur so hoch wie das Blatt - beim flachen
   Panorama gemessene 35 Pixel auf /p/rhein-348266 bei 375px Breite. Der
   senkrechte Dreierstapel mass dort 161 Pixel und der Vollbild-Knopf 48,
   beide ab 9 Pixel unter der Oberkante: vom Knopf blieben 26 Pixel im
   overflow:hidden der Buehne haengen, mehr als die Haelfte. Zwei Griffe
   dagegen, und beide greifen nur, wenn sie gebraucht werden:
   - die Zoom-Knoepfe liegen nebeneinander, der Stapel ist also nur noch
     einen Knopf hoch statt drei;
   - jeder Knopf ist hoechstens so hoch wie die Buehne. Das 100% bezieht
     sich auf die Leiste, die durch top/bottom eine feste Hoehe bekommt,
     aspect-ratio haelt den Knopf quadratisch. Ist das Blatt hoch genug
     (jedes normalformatige), bleibt es bei den vollen 3rem und es aendert
     sich nichts.
   Die Bedingung ist das genaue Gegenstueck zur Hoehenreservierung oben
   (deren "und" wird hier zum Komma, also zum Oder): wo reserviert wird, ist
   der Rahmen immer hoch genug fuer die Knoepfe, und nur dort bleibt es beim
   senkrechten Stapel. */
@media (max-width: 47.99rem), (max-height: 29.99rem) {
  .dz-controls { flex-direction: row; }
  .dz-controls, .dz-toolbar { top: var(--space-1); bottom: var(--space-1); align-items: flex-start; }
  .dz-btn { height: min(3rem, 100%); width: auto; aspect-ratio: 1; }
  .dz-btn .icon { width: min(1.7rem, 60%); height: min(1.7rem, 60%); }
}
:root[data-theme="dark"] .dz-btn { background: #2A2722; color: var(--color-parchment); border-color: var(--color-brass); }
:root[data-theme="dark"] .dz-btn:hover { background: #3A4030; }
/* Lupen-Aufforderung: eigenstaendiger Button (Klick-Ausloeser fuer den
   Viewer-Aufbau), optisch wie zuvor der reine Hinweistext. */
.dz-hint {
  display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  width: 100%;
  color: var(--color-text-muted); font-style: italic; font-size: 0.95rem;
  padding: var(--space-2) 0 0;
  background: none; border: none; font: inherit; font-style: italic; cursor: pointer;
}
/* Der eigene display:flex oben schlaegt sonst das UA-Stylesheet fuer
   [hidden] -- deepzoom.js setzt hintBtn.hidden = true, sobald die Buehne
   steht, und ohne eine eigene Regel bliebe die (dann wirkungslose) Lupen-
   Aufforderung sichtbar stehen.
   visibility statt display: die Aufforderung ist eine 41 Pixel hohe Zeile,
   und sie verschwindet nicht beim Laden, sondern bei Browser-Idle, also bis
   zu vier Sekunden spaeter. Mit display:none sprang dabei alles darunter um
   diese 41 Pixel nach oben - gemessen die Ueberschrift und der Knopf "In den
   Warenkorb", also genau die Stelle, die die Hoehenreservierung oben vor
   ebensolchem Springen schuetzen soll. Unsichtbar heisst weiterhin: kein
   Klickziel (nicht trefferbar) und nicht in der Tab-Reihenfolge. */
.dz-hint[hidden] { display: flex; visibility: hidden; }
.dz-hint:hover, .dz-hint:focus-visible { color: var(--color-brass-strong); outline: none; }
.dz-hint .icon { color: var(--color-brass-strong); }

/* Lightbox-Aufforderung: Gegenstueck zu .dz-hint fuer Bilder ohne
   Zoom-Eignung -- reiner Hinweistext (kein eigenes Klickziel, das
   Hauptbild selbst traegt data-lightbox), sonst signalisiert nur der
   Link-Cursor, dass ein Klick etwas bewirkt. */
.lightbox-hint {
  display: flex; align-items: center; justify-content: center; gap: var(--space-2);
  width: 100%; margin: 0;
  color: var(--color-text-muted); font-style: italic; font-size: 0.95rem;
  padding: var(--space-2) 0 0;
}
.lightbox-hint .icon { color: var(--color-brass-strong); }

/* Klassische Galerie (Produkte ohne Tiles, oder Vorschaubild waehrend der
   Viewer noch nicht aufgebaut ist) */
.product-gallery { text-align: center; }
.product-gallery .lightbox-link { display: block; border: var(--hairline); border-radius: var(--radius-plate); background: var(--color-surface); box-shadow: var(--shadow-card); overflow: hidden; }
.product-gallery .lightbox-link img { display: block; width: 100%; max-height: 70vh; object-fit: contain; }
.product-thumbs { display: flex; gap: var(--space-2); margin-top: var(--space-2); flex-wrap: wrap; justify-content: center; }
.product-thumb { padding: 0; border: 1px solid var(--color-border); border-radius: var(--radius); background: none; cursor: pointer; width: 4.5rem; }
.product-thumb picture { display: block; }
.product-thumb img { display: block; width: 100%; aspect-ratio: 1; object-fit: cover; border-radius: calc(var(--radius) - 1px); }
.product-thumb.is-current { border-color: var(--color-brass-strong); }

/* Info-Teil: Beschreibung fuehrt, Erwerb tritt zurueck */
.product-detail-info {
  display: grid; gap: var(--space-5);
  grid-template-columns: minmax(0, 7fr) minmax(16rem, 3fr);
  align-items: start;
}
.product-field-heading { color: var(--color-text-muted); font-size: 1.1rem; font-style: italic; margin: 0 0 var(--space-2); }
.product-sku { color: var(--color-text-muted); font-size: 0.85rem; margin: 0 0 var(--space-1); }
/* Verfügbarkeit: Status-Punkt in gleicher Farbe + kräftigere Schrift,
   damit "Auf Lager" nicht als blasse Textzeile untergeht */
.product-availability {
  display: inline-flex; align-items: center; gap: 0.5em;
  margin: 0 0 var(--space-2); font-size: 0.98rem; font-weight: 600;
}
.product-availability::before {
  content: ""; width: 0.62em; height: 0.62em; border-radius: 50%; flex: none;
  background: currentColor;
  box-shadow: 0 0 0 3px color-mix(in srgb, currentColor 22%, transparent);
}
.product-availability-in_stock { color: var(--color-ok); }
.product-availability-out_of_stock, .product-availability-reprint { color: var(--color-warn); }
.product-availability-archived { color: var(--color-text-muted); }
/* Kategorie als Eyebrow ueber dem Titel (ersetzt das vereinzelte
   Pillen-Badge, das vorher unter der Spezifikationstabelle "verloren"
   wirkte) -- kleine Versalien, Messing als TEXT (--color-brass-strong,
   AA-gepr. Kontrast auf Pergament laut Farbtabelle oben), dezent, erst im
   Hover eine Aufwertung Richtung Volltintenfarbe. Verlinkt weiterhin zur
   Kategorie-Seite wie zuvor das Badge. */
.product-eyebrow {
  margin: 0 0 var(--space-1); font-family: var(--font-display);
  text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.85rem;
  color: var(--color-brass-strong);
}
.product-eyebrow a { color: inherit; }
.product-eyebrow a:hover { color: var(--color-text); text-decoration: underline; }
.product-eyebrow-sep { margin: 0 0.5em; opacity: 0.6; }
.product-title { margin: 0 0 var(--space-1); font-size: clamp(1.35rem, 1.05rem + 1.3vw, 2rem); }
/* Inschrift: die zweite, eigentliche Stimme direkt unter dem Titel -- der
   Katalog-Kopf ("ORT:", <h1>) bleibt verbindlich, aber die gedruckte
   Inschrift IST inhaltlich der Titel des Blattes, deshalb fast so laut wie
   .product-title und in derselben Display-Schrift (Cormorant SC), die auch
   Titel/Homepage-Ueberschriften traegt -- kein Kursiv-Schnitt (fuer
   Cormorant SC ist keiner geladen, nur 400/600/700 normal). Anfuehrungs-
   zeichen/Doppelpunkt aus dem Rohtext werden beim Import entfernt, hier
   steht nur der reine Wortlaut. Nur gerendert, wenn <titel> tatsaechlich
   eine markierte Inschrift liefert (~27% des Bestands) -- sonst kein
   Absatz, keine Luecke. */
.product-inscription {
  margin: 0 0 var(--space-3); font-family: var(--font-display); font-weight: 600;
  font-size: clamp(1.3rem, 1.05rem + 1vw, 1.8rem); line-height: 1.25;
  color: var(--color-accent);
}
/* Objektbeschreibung (Technik/Kuenstler/Jahr/Mass aus <titel>): dieselbe
   Display-Schrift wie Titel/Inschrift/Homepage-Ueberschriften, damit die
   drei zusammen als EINE typografische Stimme lesen, statt die
   Objektbeschreibung wie Rohtext im UI-Fliesstext (EB Garamond) wirken zu
   lassen. Bleibt in Tintenfarbe (--color-text), nicht gedaempft -- es ist
   der eigentliche Seiteninhalt, nicht eine Randnotiz. */
.product-description {
  max-width: 65ch; font-family: var(--font-display); font-size: 1.15rem; line-height: 1.5;
}
/* Katalogisierungs-Anmerkung (<bemerk>): eigenes "Kartenkarteikarte"-Feld,
   dieselbe Sprache wie die Kartuschen der Startseite (Flaeche, Messing-
   Haarlinie, Radius, Fleuron-Zierrat) statt einer blossen Haarlinie --
   damit sie als eigener Abschnitt erkennbar ist, nicht als Fortsetzung der
   Objektbeschreibung. Bleibt im ruhigen UI-Fliesstext (EB Garamond),
   gedaempfte Farbe: dokumentarischer Charakter (Fundstellen/Provenienz),
   bewusst der Gegenpol zur Display-Schrift der Objektbeschreibung. Steht
   UNTER der Beschreibung (s. product.html). */
.product-teaser {
  font-size: 0.95rem; color: var(--color-text-muted);
  margin-top: var(--space-4); padding: var(--space-3) var(--space-4);
  background: var(--color-surface); border: var(--hairline-brass);
  border-radius: var(--radius-plate); box-shadow: var(--shadow-card);
}
/* Fleuron bleibt zentriert (Zierrat), der Text darunter haengt links an
   der Box-Kante -- wie Titel/Beschreibung darueber, statt als schmalere,
   mittig schwebende Spalte in der jetzt breiten Flaeche zu wirken. */
.product-teaser .fleuron { display: block; width: min(9rem, 60%); height: 0.9rem; margin: 0 auto var(--space-2); }
.product-teaser p { margin: 0; text-align: left; }
.field-value-content { max-width: 65ch; margin: var(--space-3) 0; }
.field-value-content h3 { font-size: 0.95rem; margin: 0 0 var(--space-1); }

/* External video trailer (GDPR two-click embed). Deliberately small/quiet:
   fixed 16:9 box, muted scrim, one small circular play glyph -- no hero
   treatment, no bright colours. The <img>/empty span is always a locally
   served asset (see internal/video, internal/web/video.go); nothing here
   ever requests img.youtube.com/i.ytimg.com. */
.product-trailer { margin: var(--space-3) 0; }
.product-trailer-title { font-size: 0.95rem; margin: 0 0 var(--space-1); }
.product-trailer-frame {
  position: relative; width: 100%; max-width: 22rem; aspect-ratio: 16 / 9;
  border-radius: var(--radius); overflow: hidden;
  background: var(--color-border); box-shadow: var(--shadow-card);
}
.product-trailer-thumb { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.product-trailer-thumb-empty { background: linear-gradient(135deg, var(--color-border), var(--color-surface)); }
.product-trailer-play {
  position: absolute; inset: 0; width: 100%; height: 100%;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--space-1);
  border: none; margin: 0; cursor: pointer; font: inherit; font-size: 0.8rem; font-weight: 600;
  background: rgb(0 0 0 / 0%); color: #fff; text-shadow: 0 1px 3px rgb(0 0 0 / 65%);
  transition: background-color 0.15s ease;
}
.product-trailer-play:hover, .product-trailer-play:focus-visible { background: rgb(0 0 0 / 18%); }
.product-trailer-play-icon {
  width: 2.75rem; height: 2.75rem; border-radius: 50%;
  background: rgb(0 0 0 / 45%); border: 1.5px solid rgb(255 255 255 / 85%);
  display: flex; align-items: center; justify-content: center;
}
.product-trailer-play-icon::before {
  content: ""; margin-left: 0.15em;
  border-style: solid; border-width: 0.5em 0 0.5em 0.85em;
  border-color: transparent transparent transparent #fff;
}
.product-trailer-note { margin: var(--space-1) 0 0; max-width: 32rem; color: var(--color-text-muted); font-size: 0.8rem; }
.product-trailer-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

.product-aside {
  border: var(--hairline); border-radius: var(--radius-plate);
  background: var(--color-surface);
  padding: var(--space-4);
  position: sticky; top: var(--space-3);
}
.product-price { margin: 0 0 var(--space-3); }
.product-price-amount { display: block; font-size: 1.7rem; font-weight: 600; font-family: var(--font-display); }
.product-price-tax { color: var(--color-text-muted); font-size: 0.75rem; }
.product-shipping-estimate { display: block; color: var(--color-text-muted); font-size: 0.85rem; margin-top: var(--space-1); }

.product-specs { border-collapse: collapse; margin: var(--space-4) 0; width: 100%; max-width: 46rem; }
.product-specs th, .product-specs td { text-align: left; vertical-align: top; padding: var(--space-2) var(--space-3) var(--space-2) 0; border-bottom: var(--hairline); }
.product-specs th { font-family: var(--font-display); font-weight: 600; white-space: nowrap; color: var(--color-text-muted); }
.badge { display: inline-block; background: none; border: 1px solid var(--color-sage-strong); color: var(--color-sage-strong); border-radius: 999px; padding: 0 var(--space-2); margin-right: var(--space-1); font-size: 0.85rem; }
:root[data-theme="dark"] .badge { border-color: var(--color-sage); color: var(--color-sage); }

.product-gpsr-title { font-size: 0.95rem; margin: var(--space-3) 0 var(--space-1); }
.product-gpsr-body { white-space: pre-line; color: var(--color-text-muted); font-size: 0.9rem; }

/* --------------------------------------------------------------------------
   10. Buttons, Formulare
   -------------------------------------------------------------------------- */
.btn {
  display: inline-block; padding: var(--space-2) var(--space-4);
  border: 1px solid transparent; border-radius: var(--radius); cursor: pointer;
  font: inherit; font-size: 1rem; letter-spacing: 0.02em;
}
/* Primär-Button in Messing statt hartem Schwarz: warm, passt zur Linie */
.btn-primary {
  background: var(--color-brass-strong);
  color: #FBF8F1;
  border-color: var(--color-brass-strong);
  font-family: var(--font-display); font-weight: 600; letter-spacing: 0.04em;
}
.btn-primary:hover { background: #6E5B39; border-color: #6E5B39; color: #FBF8F1; }
:root[data-theme="dark"] .btn-primary { background: var(--color-brass); color: var(--color-ink); border-color: var(--color-brass); }
:root[data-theme="dark"] .btn-primary:hover { background: #D8C48F; border-color: #D8C48F; }
.btn-small { padding: 0 var(--space-1); font-size: 0.85rem; }
.btn-danger { background: var(--color-warn); color: var(--color-accent-contrast); }
.add-to-cart-form { margin-top: var(--space-3); }
.add-to-cart-form .btn { width: 100%; font-family: var(--font-display); font-weight: 600; font-size: 1.1rem; }

/* Identitätszeile der Kontoseite: Adresse + Abmelden. Die E-Mail-Adresse ist
   ein unteilbares langes Token, deshalb die Grid-Mindestbreiten-Falle
   (Engine-Konvention 12) hier per flex-wrap + overflow-wrap behandeln. */
.account-identity { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-3); }
.account-identity p { margin: 0; min-width: 0; overflow-wrap: break-word; font-size: 1.05rem; }

.auth-columns { display: grid; gap: var(--space-4); grid-template-columns: 1fr; max-width: 60rem; }
.auth-box, .account-section {
  background: var(--color-surface); border: var(--hairline);
  border-radius: var(--radius-plate); padding: var(--space-4); margin-bottom: var(--space-3);
}
.auth-form { display: flex; flex-direction: column; gap: var(--space-2); max-width: 36rem; }
.auth-form label { display: flex; flex-direction: column; gap: var(--space-1); }
.auth-form input, .auth-form textarea, .auth-form select,
.deep-search-field input, .deep-search-field select,
.checkout-fieldset input:not([type="checkbox"]):not([type="radio"]), .checkout-fieldset select {
  padding: var(--space-2); border: var(--hairline);
  border-radius: var(--radius); background: var(--color-bg); color: var(--color-text);
  font: inherit;
}
.auth-form input:focus, .auth-form textarea:focus, .auth-form select:focus,
.deep-search-field input:focus, .deep-search-field select:focus,
.checkout-fieldset input:focus, .checkout-fieldset select:focus, .checkout-fieldset textarea:focus,
.cart-qty-input:focus {
  outline: 2px solid var(--color-sage); outline-offset: 0;
}
.form-error { color: var(--color-warn); font-weight: 600; }
.form-note { color: var(--color-ok); font-weight: 600; }
.form-hint { color: var(--color-text-muted); font-size: 0.9rem; }
/* Off-screen (not display:none) so autofill still finds it -- see the
   accessibility-tree comment on account_password.html's hidden username
   field. */
.visually-hidden-field { position: absolute; left: -9999px; }
/* Change-password field + its show/hide toggle (site.js: type-to-reveal
   with a debounced re-mask, or pin visible/masked via the button). */
.pw-field { display: flex; gap: var(--space-1); align-items: stretch; }
.pw-field input { flex: 1; }
.pw-toggle {
  flex: none; padding: 0 var(--space-2); border: var(--hairline);
  border-radius: var(--radius); background: var(--color-surface); color: var(--color-text);
  cursor: pointer; font-size: 1rem; line-height: 1;
}
.pw-toggle:hover { background: var(--color-border); }
.pw-toggle:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
/* Weak-password advisory interstitial (login_weak.html) */
.login-weak-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; margin: var(--space-3) 0; }
.login-weak-details { margin-top: var(--space-3); }
.login-weak-details ul { margin: var(--space-2) 0 0; padding-left: var(--space-4); display: flex; flex-direction: column; gap: var(--space-1); }
.hp-field { position: absolute; left: -9999px; }

/* --------------------------------------------------------------------------
   11. Warenkorb / Checkout / Bestellungen (Engine-Vertrag, ruhig gestylt)
   -------------------------------------------------------------------------- */
.cart-table, .order-table, .admin-order-table, .order-items-table, .widerruf-review-table { border-collapse: collapse; width: 100%; }
.cart-table th, .cart-table td, .order-table th, .order-table td,
.admin-order-table th, .admin-order-table td, .order-items-table td,
.widerruf-review-table th, .widerruf-review-table td { text-align: left; padding: var(--space-2); border-bottom: var(--hairline); }
.cart-table th, .order-table th { font-family: var(--font-display); font-weight: 600; }
.cart-img img { width: 3.2rem; height: 3.2rem; object-fit: cover; border-radius: var(--radius); border: var(--hairline); }
.cart-qty-form { display: flex; gap: var(--space-1); }
.cart-qty-input { width: 4rem; padding: var(--space-1); border: var(--hairline); border-radius: var(--radius); background: var(--color-bg); color: var(--color-text); font: inherit; }
/* One-of-a-kind product (unique_item): plain "1", no stepper -- see cart.html. */
.cart-qty-fixed { display: inline-block; width: 4rem; padding: var(--space-1); text-align: center; }
.cart-total { font-size: 1.25rem; }
.checkout-fieldset { border: var(--hairline); border-radius: var(--radius-plate); padding: var(--space-4); margin-bottom: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2); background: var(--color-surface); }
.checkout-fieldset legend { font-family: var(--font-display); font-weight: 600; padding: 0 var(--space-1); }
.checkout-fieldset label { display: flex; flex-direction: column; gap: var(--space-1); max-width: 34rem; }
/* Order-note textarea. Browsers default textareas to monospace at ~20 columns,
   which reads as a foreign element next to the theme's serif; this restates
   the control idiom already used by .cart-qty-input above. */
.checkout-fieldset textarea { width: 100%; max-width: 34rem; padding: var(--space-1); border: var(--hairline); border-radius: var(--radius); background: var(--color-bg); color: var(--color-text); font: inherit; resize: vertical; }
.checkout-check { flex-direction: row !important; align-items: center; gap: var(--space-2) !important; }
.checkout-fieldset input[type="checkbox"], .checkout-fieldset input[type="radio"] { accent-color: var(--color-accent); }
.checkout-legal { max-width: 40rem; }
/* Serverseitige Validierung. Die Engine liefert seit e74f8bb sowohl einen
   Seitenfehler (.PageError) als auch Fehler je Feld (.FieldErrors); haas hat
   beides bis 2026-09-07 gar nicht gerendert, das Formular kam einfach
   unveraendert zurueck und der Kunde sah keinen Grund. */
.field-error { border-color: var(--color-warn) !important; box-shadow: 0 0 0 1px var(--color-warn); }
.checkout-page-error { border-left: 3px solid var(--color-warn); padding: var(--space-2) var(--space-3); background: var(--color-surface); }
.checkout-region-note { margin-bottom: var(--space-2); }

/* Toggle switch: CSS-only, checkbox-under-the-hood so it stays a real form
   control. Used where a boolean setting reads better as a switch than a
   plain checkbox (account newsletter toggle). */
.switch { position: relative; display: inline-flex; flex: none; width: 2.75em; height: 1.5em; }
.switch input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.switch-track { position: absolute; inset: 0; background: var(--color-border); border-radius: 999px; transition: background 0.15s ease; pointer-events: none; }
.switch-track::before { content: ""; position: absolute; top: 0.15em; left: 0.15em; width: 1.2em; height: 1.2em; background: var(--color-surface); border-radius: 50%; box-shadow: 0 1px 2px rgb(0 0 0 / 25%); transition: transform 0.15s ease; }
.switch input:checked + .switch-track { background: var(--color-accent); }
.switch input:checked + .switch-track::before { transform: translateX(1.25em); }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--color-accent); outline-offset: 2px; }

/* Admin settings: local image-conversion capability panel (read-only diagnosis). */
.admin-image-diag { background: var(--color-bg); }
.admin-image-diag-warn { border-color: var(--color-warn); box-shadow: 0 0 0 1px var(--color-warn); }
.admin-image-diag-verdict { font-weight: 600; }
.admin-image-diag-warn .admin-image-diag-verdict { color: var(--color-warn); }
.admin-image-diag-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.admin-image-diag-list code { font-size: 0.85rem; color: var(--color-text-muted); }
.admin-image-diag-keydots { display: inline-flex; gap: var(--space-1); margin-left: var(--space-1); }
.admin-image-diag-keydot { display: inline-block; padding: 0 var(--space-1); border-radius: 999px; border: 1px solid var(--color-ok); color: var(--color-ok); font-size: 0.75rem; }
.admin-image-diag-keydot-exhausted { border-color: var(--color-warn); color: var(--color-warn); }

.checkout-billing .billing-fields { display: none; flex-direction: column; gap: var(--space-3); }
.checkout-billing:has(.billing-toggle:checked) .billing-fields { display: flex; }

/* Delivery method (shipping vs. local pickup): the "country not listed"
   notice and the ship/pickup toggling are JS-driven (site.js) -- without JS
   everything just stays visible and editable, per the progressive-
   enhancement convention. */
.checkout-pickup-only-notice, .checkout-ship-notice { color: var(--color-warn); font-weight: 600; }
/* Country pickers (partials.html "country-field"): the delivery-area notice
   under the invoice country, and the "did you mean" hint rendered as a
   link-styled button so applying the suggestion is one click. */
.checkout-ship-notice { margin: 0; }
.country-hint { margin: calc(var(--space-2) * -1) 0 0; }
.country-hint-apply { background: none; border: 0; padding: 0; font: inherit; color: var(--color-accent); text-decoration: underline; cursor: pointer; }
/* Checkout email field: same shape as the country "did you mean" hint just
   above (an underlined link-styled button folded into a .form-hint), plus a
   second, independent line for the live deliverability verdict. */
.email-hint { margin: calc(var(--space-2) * -1) 0 0; }
.email-hint-apply { background: none; border: 0; padding: 0; font: inherit; color: var(--color-accent); text-decoration: underline; cursor: pointer; }
.email-undeliverable-hint { margin: calc(var(--space-2) * -1) 0 0; }
/* A single carrier/payment/delivery option is stated, not offered in a one-entry dropdown. */
.checkout-preselected { margin: 0; }
.checkout-delivery-methods { display: flex; flex-direction: column; gap: var(--space-2); }
.checkout-ship-options { display: flex; flex-direction: column; gap: var(--space-2); }

/* Custom country combobox (partials.html "country-field" + site.js): replaces
   the native <input list="..."> datalist picker, which browsers render with
   their own unstyleable chrome and which, once "Häufig gewählt" pins were
   added, showed the same country twice -- once pinned, once again in the
   full A-Z list. Deliberately mirrors .autocomplete-panel/.autocomplete-item
   above (surface, border, shadow, radius, hover, typography) so the header
   search dropdown and this one read as one system. site.js owns filtering,
   ARIA state and keyboard navigation. */
.country-combobox { position: relative; display: block; width: 100%; }
.country-combobox input { width: 100%; }
.country-options {
  position: absolute; top: 100%; left: 0; right: 0; margin: 2px 0 0;
  z-index: 30; max-height: 16rem; overflow-y: auto;
  list-style: none; padding: 0;
  background: var(--color-surface); border: var(--hairline);
  border-radius: var(--radius); box-shadow: var(--shadow-card-hover);
}
.country-options[hidden] { display: none; }
.country-option { padding: var(--space-2); cursor: pointer; color: var(--color-text); }
.country-option:hover, .country-option.is-active { background: var(--color-surface-alt); }
.country-option-group {
  padding: var(--space-2) var(--space-2) var(--space-1);
  font-size: 0.65rem; text-transform: uppercase; letter-spacing: .04em;
  color: var(--color-text-muted); pointer-events: none;
}
.country-option-group + .country-option { border-top: none; }
.country-option + .country-option-group { border-top: var(--hairline); }

/* Checkout address picker: progressive enhancement. The trigger/clear
   buttons only do anything with JS (site.js), so they stay hidden until it
   sets .js on <html> -- without it the fields are just a normal, fully
   editable form. Functional styling only (usable dialog on a phone); no
   bespoke design pass yet for this theme. */
.address-picker-actions { display: none; gap: var(--space-2); margin-bottom: var(--space-2); }
.js .address-picker-actions { display: flex; }
.address-picker-dialog {
  width: min(46rem, 92vw);
  max-height: min(80vh, 46rem);
  padding: 0;
  border: var(--hairline);
  border-radius: var(--radius-plate);
  background: var(--color-surface);
  color: var(--color-text);
  /* Page dimmer as a spread shadow rather than a translucent ::backdrop
     surface: a full-viewport translucent quad is swallowed by GPU
     compositing on AMD/Vulkan and can stall the renderer outright. Same
     workaround the cart modal and the lightbox already use. */
  box-shadow: var(--shadow-card), 0 0 0 200vmax rgb(0 0 0 / 45%);
}
.address-picker-dialog::backdrop { background: transparent; }

/* Geteilter Bestaetigungsdialog (site.js window.lionizeConfirm), Ersatz fuer
   window.confirm. Abdunklung wie beim Adresswaehler ueber einen
   box-shadow-Spread statt einer durchscheinenden Vollbildebene -- ein
   permanenter Vollbild-Kompositing-Layer friert den Renderer auf der
   AMD-Hardware ein (Konvention 10). */
.confirm-dialog {
  width: min(26rem, 92vw);
  padding: var(--space-4);
  border: var(--hairline-brass);
  border-radius: var(--radius-plate);
  background: var(--color-surface);
  color: var(--color-text);
  font-family: var(--font-body);
  box-shadow: var(--shadow-card), 0 0 0 200vmax rgb(0 0 0 / 45%);
}
.confirm-dialog::backdrop { background: transparent; }
.confirm-dialog-message { margin: 0 0 var(--space-4); }
.confirm-dialog-checkbox { display: flex; align-items: center; gap: var(--space-2); margin: 0 0 var(--space-4); cursor: pointer; }
.confirm-dialog-actions { display: flex; justify-content: flex-end; gap: var(--space-2); }
.address-picker-header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  padding: var(--space-3); border-bottom: var(--hairline);
}
.address-picker-title { margin: 0; font-size: 1.1rem; }
.address-picker-close {
  background: none; border: var(--hairline); border-radius: var(--radius);
  color: var(--color-text); cursor: pointer; padding: var(--space-1) var(--space-2); line-height: 1;
}
.address-picker-close:hover { background: var(--color-bg); }
/* auto-fit reflows the grid on its own: one card per row on a phone, up to
   3-4 on a wide dialog, no media-query thresholds to maintain. */
.address-picker-grid {
  display: grid; gap: var(--space-2);
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  padding: var(--space-3);
  max-height: calc(min(80vh, 46rem) - 4rem);
  overflow-y: auto;
}
.address-card-use {
  display: flex; flex-direction: column; gap: var(--space-1);
  width: 100%; text-align: left; cursor: pointer; font: inherit;
  background: var(--color-bg); color: var(--color-text);
  border: var(--hairline); border-radius: var(--radius); padding: var(--space-2);
}
.address-card-use:hover, .address-card-use:focus-visible { border-color: var(--color-accent); }
.address-card-company, .address-card-name { font-weight: 600; }
.address-card-street, .address-card-city, .address-card-country, .address-card-phone {
  color: var(--color-text-muted); font-size: 0.9rem;
}
.address-card-action { margin-top: var(--space-1); color: var(--color-accent); font-size: 0.8rem; font-weight: 600; }
.bank-details { background: var(--color-bg); border: var(--hairline); border-radius: var(--radius); padding: var(--space-2); }
.pay-completed { color: var(--color-ok); font-weight: 700; }
.order-total-row td { font-size: 1.1rem; }
.order-subtotal-row td { border-top: var(--hairline); border-bottom: none; padding-top: var(--space-1); }
.order-ship-subrow td { color: var(--color-text-muted); font-size: 0.85em; }

/* --------------------------------------------------------------------------
   12. Suche / Kategorien / Blog / Legal
   -------------------------------------------------------------------------- */
/* Entdecken-Seite: Suchpult als eingefasstes Panel statt loser Felder */
.deep-search-form { width: 100%; }
.deep-search-panel {
  border: var(--hairline-brass); border-radius: var(--radius-plate);
  background: var(--color-surface);
  padding: var(--space-4);
  box-shadow: var(--shadow-card);
}
.deep-search-legend {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-family: var(--font-display); font-weight: 600; letter-spacing: 0.05em;
  padding: 0 var(--space-2);
}
.deep-search-legend .icon { color: var(--color-brass-strong); }
.deep-search-grid { display: grid; gap: var(--space-3); grid-template-columns: repeat(2, 1fr); margin-bottom: var(--space-3); }
.deep-search-field { display: flex; flex-direction: column; gap: var(--space-1); }
.deep-search-field > span { font-size: 0.88rem; color: var(--color-text-muted); letter-spacing: 0.02em; }
.deep-search-field-wide { grid-column: 1 / -1; }
.deep-search-actions { text-align: right; }
.deep-search-actions .btn { font-family: var(--font-display); font-weight: 600; font-size: 1.05rem; padding-inline: var(--space-5); }

/* Stöber-Einstiege unter dem Suchpult */
.browse-row { max-width: 56rem; margin: var(--space-5) auto 0; text-align: center; }
.browse-row-lede { color: var(--color-text-muted); margin: 0 0 var(--space-3); font-style: italic; }
.browse-cards { display: grid; gap: var(--space-3); grid-template-columns: repeat(3, 1fr); }
.browse-card {
  display: block; overflow: hidden;
  border: var(--hairline); border-radius: var(--radius-plate);
  background: var(--color-surface); box-shadow: var(--shadow-card);
  color: var(--color-text);
}
.browse-card:hover { text-decoration: none; border-color: var(--color-brass); box-shadow: var(--shadow-card-hover); }
.browse-card img { display: block; width: 100%; aspect-ratio: 3 / 2; object-fit: contain; background: var(--color-surface); }
.browse-card span {
  display: block; padding: var(--space-2);
  font-family: var(--font-display); font-weight: 600; letter-spacing: 0.04em;
}
.browse-card:hover span { color: var(--color-brass-strong); }

/* Sortierzeile auf Kategorie-Seiten */
.category-sort {
  display: flex; gap: var(--space-3); align-items: baseline; flex-wrap: wrap;
  margin: 0 0 var(--space-4);
  padding-bottom: var(--space-2);
  border-bottom: var(--hairline);
  font-size: 0.95rem;
}
.category-sort-label { color: var(--color-text-muted); }
.category-sort a { color: var(--color-text-muted); }
.category-sort a:hover { color: var(--color-text); text-decoration: none; }
.category-sort a.is-active {
  color: var(--color-text);
  box-shadow: inset 0 -2px 0 var(--color-sage-strong);
}
.breadcrumb { color: var(--color-text-muted); margin-bottom: var(--space-3); font-size: 0.9rem; }
.breadcrumb a { color: inherit; }
.category-value-list { line-height: 2.1; }
.category-count { color: var(--color-text-muted); }
.archive-intro { color: var(--color-text-muted); max-width: 46rem; margin: 0 0 var(--space-3); }
.blog-list { list-style: none; padding: 0; max-width: 46rem; }
.blog-list-item { display: flex; justify-content: space-between; gap: var(--space-2); padding: var(--space-2) 0; border-bottom: var(--hairline); }
.blog-date { color: var(--color-text-muted); }

/* Literarische Einzelseite (Über uns & Blog-Posts) und Rechtstexte:
   die Seite selbst bleibt in voller .site-main-Breite wie jede andere
   Seite (einheitliche Randabstaende ueberall, Betreiber-Vorgabe) -
   frueher engten .blog-post/.legal-page hier auf 40rem ein, was neben
   den Kategorieseiten wie eine schmale Spalte in der Mitte wirkte.
   Die Lesbarkeit von langem Fliesstext regeln stattdessen .blog-body/
   .legal-body weiter unten: eine Textmasse innerhalb der vollen Seite,
   nicht die ganze Seite selbst. */
.blog-post .page-head { margin-bottom: var(--space-4); }
.blog-body { font-size: 1.1rem; line-height: 1.75; }
.blog-body h2 {
  text-align: center; font-size: 1.3rem; letter-spacing: 0.05em;
  margin: var(--space-5) 0 var(--space-3);
}
.blog-body h2::after {
  content: ""; display: block; width: 3.5rem; height: 1px;
  background: var(--color-brass); margin: var(--space-2) auto 0;
}
.blog-body strong { font-weight: 600; }
/* Signatur unter dem ersten Absatz von /blog/ueber-uns (Inhalt der
   posts-Zeile, nicht diese Regel, entscheidet die Platzierung).
   Gleicher Duktus wie .footer-signature-name, aber kleiner und
   rechtsbuendig wie eine handschriftliche Unterschrift unter einem Text. */
.blog-signature {
  font-family: var(--font-accent); font-size: 1.9rem; color: var(--color-brass-strong);
  text-align: right; margin: var(--space-2) 0 var(--space-4);
}
:root[data-theme="dark"] .blog-signature { color: var(--color-brass); }
/* Landing-Intro: admin-gepflegte HTML-Sektion, hier die Messetermine
   (Quelle: content/messetermine{,.en}.html + seed-messetermine.sh).
   Ruhig zentriert im Stich-Duktus statt als Kasten. */
.landing-intro { text-align: center; max-width: 44rem; margin: var(--space-5) auto; }
.landing-intro h2 { font-size: clamp(1.4rem, 1.2rem + 1vw, 2rem); margin: 0 0 var(--space-3); }
.landing-intro h2::after {
  content: ""; display: block; width: 4.5rem; height: 1px;
  background: var(--color-brass); margin: var(--space-2) auto 0;
}
.landing-intro p { margin: 0 0 var(--space-2); }
.landing-intro strong { font-family: var(--font-display); font-weight: 600; font-size: 1.15em; }
/* Kein eigener Breitendeckel fuer Fliesstext: Betreiber-Vorgabe ist,
   dass ALLE Seiten denselben Abstand zum Fensterrand halten und die
   Breite von .site-main ausnutzen, so wie es die Kategorieseiten tun.
   Eine engere Lesespalte hier liess Rechtstexte und den Beitrag weiter
   als einzige Seiten schmaler wirken als der Rest. */
.versand-table { border-collapse: collapse; margin: var(--space-2) 0; }
.versand-table th, .versand-table td { padding: var(--space-1) var(--space-2); border-bottom: var(--hairline); text-align: left; }
.price-note { color: var(--color-text-muted); font-size: 0.85rem; margin-left: var(--space-1); }
.price-note a { color: inherit; text-decoration: underline; }

/* --------------------------------------------------------------------------
   13. Lightbox + Cookie-Banner
   GPU-Stolperfalle (AMD RX 480): grossflaechige transluzente backgrounds
   auf position:fixed/inset:0 werden vom Compositing verschluckt. Statt
   background deshalb box-shadow-Spread vom Zentrum (gbernard-Muster).
   -------------------------------------------------------------------------- */
.lightbox-overlay {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: center; justify-content: center;
  padding: var(--space-3); cursor: zoom-out;
  background: none;
  box-shadow: inset 0 0 0 200vmax rgb(22 21 19 / 88%);
}
.lightbox-overlay img { max-width: 95vw; max-height: 95vh; object-fit: contain; position: relative; }
.lightbox-overlay picture { display: contents; }

.cookie-banner {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 50;
  background: var(--color-surface);
  border-top: var(--hairline-brass);
  padding: var(--space-2) var(--space-3);
  display: flex; gap: var(--space-3); align-items: center; justify-content: center; flex-wrap: wrap;
}

/* --------------------------------------------------------------------------
   14. Footer — Salbeigrün wie die Legacy-Seite (Wiedererkennungsanker,
   Betreiber-Vorgabe), Tinte-Text, Signatur in Italianno.
   -------------------------------------------------------------------------- */
.site-footer {
  margin-top: var(--space-4);
  background: var(--color-sage);
  color: var(--color-ink);
  border-top: var(--hairline-brass);
  /* gleiche Randkante wie Banner und Kopfzeile (--edge-pad): Footer-
     Inhalt fluchtet mit den Banner-Kanten statt mit der Lesespalte */
  padding: var(--space-5) var(--edge-pad) var(--space-4);
}
:root[data-theme="dark"] .site-footer {
  /* Noir: salbei-getönte dunkle Fläche statt hellem Grün */
  background: #23271C;
  color: var(--color-parchment);
}
/* Spalten inhaltsbreit und gleichmäßig über die volle Breite verteilt:
   Kontakt flieht links, Rechtliches am rechten Rand. */
.footer-grid {
  display: grid; gap: var(--space-5);
  grid-template-columns: auto auto auto;
  justify-content: space-between;
}
/* feste Basis, sonst kollabiert das Formular in der auto-Spalte */
.footer-newsletter { width: 24rem; max-width: 100%; }
.footer-col h2 {
  font-family: var(--font-display); font-weight: 600; font-size: 1.05rem;
  letter-spacing: 0.08em; color: inherit;
  margin: 0 0 var(--space-2);
}
.footer-col p { margin: 0 0 var(--space-1); font-size: 0.95rem; }
.footer-col a { color: inherit; display: inline-block; padding: 2px 0; }
.footer-col a:hover { text-decoration-color: var(--color-brass-strong); }
.footer-links { list-style: none; padding: 0; margin: 0; }
.footer-newsletter { margin-top: var(--space-3); }
.footer-newsletter-title {
  font-family: var(--font-display); font-weight: 600; font-size: 0.95rem;
  letter-spacing: 0.08em; margin: 0 0 var(--space-2);
}
.footer-signature {
  margin: var(--space-5) 0 0; text-align: center;
  border-top: 1px solid rgb(31 31 31 / 20%); padding-top: var(--space-3);
}
:root[data-theme="dark"] .footer-signature { border-top-color: rgb(237 230 214 / 20%); }
.footer-signature-name {
  font-family: var(--font-accent); font-size: 2.2rem; color: var(--color-brass-strong);
  display: block; line-height: 1.1;
}
:root[data-theme="dark"] .footer-signature-name { color: var(--color-brass); }
.footer-signature-claim { font-size: 0.85rem; opacity: 0.75; }
.footer-price-note { display: block; margin-top: var(--space-2); font-size: 0.85rem; opacity: 0.85; }
.footer-price-note a { color: inherit; text-decoration: underline; }

/* --------------------------------------------------------------------------
   15. Admin (Standard-Funktionalitaet, dezent im Theme-Ton)
   -------------------------------------------------------------------------- */
.admin-nav { display: flex; gap: var(--space-3); padding: var(--space-2) 0; border-bottom: 2px solid var(--color-brass); margin-bottom: var(--space-3); flex-wrap: wrap; }
/* Admin sign-out lives in the admin nav as a form, so its button is dressed
   as one of the surrounding links rather than as a control -- no theme
   overrides .admin-nav a, so they all take the global link colour. Pushed to
   the far end because it is the one item that leaves instead of navigating. */
.admin-nav-logout { margin-left: auto; display: inline-flex; }
.admin-nav-logout button {
  font: inherit; color: var(--color-accent); background: none; border: 0;
  padding: 0; cursor: pointer;
}
.admin-nav-logout button:hover { text-decoration: underline; }
/* Admin-Einstellungen als Tabs (2026-09-08): rein CSS, kein JavaScript. Die
   Anker (#tab-*) stehen vor Nav UND Formular, damit der
   Geschwister-Selektor (~) von jedem Anker aus vorwaerts sowohl das
   passende Panel im Formular als auch den passenden Nav-Link erreicht --
   genau das macht ein URL-Fragment gleichzeitig adressierbar und
   Reload-fest. Panels werden ausschliesslich per display:none
   ausgeblendet, nie aus dem DOM entfernt oder deaktiviert -- jedes Feld
   jedes Tabs bleibt Teil des einen Settings-Formulars und wird beim
   Speichern immer mitgeschickt, unabhaengig vom sichtbaren Tab (s.
   Kommentar in admin_settings.html). "shop" ist der Standard-Tab
   (unbedingt sichtbar); die uebrigen sechs zeigen sich nur, wenn ihr Anker
   :target ist, und blenden dabei "shop" aus. */
.settings-anchor { display: block; height: 0; }
.settings-tabs-nav { display: flex; gap: var(--space-1); flex-wrap: wrap; margin-bottom: var(--space-3); border-bottom: var(--hairline); }
.settings-tab-link { padding: var(--space-1) var(--space-2); text-decoration: none; color: var(--color-text-muted); border-bottom: 2px solid transparent; margin-bottom: -1px; font-family: var(--font-display); }
.settings-tab-link:hover { color: var(--color-text); }
.settings-tab-link[href="#tab-shop"] { color: var(--color-text); border-color: var(--color-accent); font-weight: 600; }
.settings-tab-panel { display: none; }
.settings-tab-panel[data-tab="shop"] { display: block; }
#tab-content:target ~ .settings-tabs-nav .settings-tab-link[href="#tab-shop"],
#tab-accounts:target ~ .settings-tabs-nav .settings-tab-link[href="#tab-shop"],
#tab-commerce:target ~ .settings-tabs-nav .settings-tab-link[href="#tab-shop"],
#tab-mail:target ~ .settings-tabs-nav .settings-tab-link[href="#tab-shop"],
#tab-integrations:target ~ .settings-tabs-nav .settings-tab-link[href="#tab-shop"],
#tab-seo:target ~ .settings-tabs-nav .settings-tab-link[href="#tab-shop"] { color: var(--color-text-muted); border-color: transparent; font-weight: 400; }
#tab-content:target ~ .settings-tabs-nav .settings-tab-link[href="#tab-content"],
#tab-accounts:target ~ .settings-tabs-nav .settings-tab-link[href="#tab-accounts"],
#tab-commerce:target ~ .settings-tabs-nav .settings-tab-link[href="#tab-commerce"],
#tab-mail:target ~ .settings-tabs-nav .settings-tab-link[href="#tab-mail"],
#tab-integrations:target ~ .settings-tabs-nav .settings-tab-link[href="#tab-integrations"],
#tab-seo:target ~ .settings-tabs-nav .settings-tab-link[href="#tab-seo"] { color: var(--color-text); border-color: var(--color-accent); font-weight: 600; }
#tab-content:target ~ form .settings-tab-panel[data-tab="shop"],
#tab-accounts:target ~ form .settings-tab-panel[data-tab="shop"],
#tab-commerce:target ~ form .settings-tab-panel[data-tab="shop"],
#tab-mail:target ~ form .settings-tab-panel[data-tab="shop"],
#tab-integrations:target ~ form .settings-tab-panel[data-tab="shop"],
#tab-seo:target ~ form .settings-tab-panel[data-tab="shop"] { display: none; }
#tab-content:target ~ form .settings-tab-panel[data-tab="content"] { display: block; }
#tab-accounts:target ~ form .settings-tab-panel[data-tab="accounts"] { display: block; }
#tab-commerce:target ~ form .settings-tab-panel[data-tab="commerce"] { display: block; }
#tab-mail:target ~ form .settings-tab-panel[data-tab="mail"] { display: block; }
#tab-integrations:target ~ form .settings-tab-panel[data-tab="integrations"] { display: block; }
#tab-integrations:target ~ .settings-tab-panel[data-tab="integrations"] { display: block; }
#tab-seo:target ~ form .settings-tab-panel[data-tab="seo"] { display: block; }
/* Diese Formulare tragen teils mehrere Eingaben, einen Hinweis und zwei
   Knoepfe (Admin-Bestellanpassung). Ohne wrap quetschen sie sich in
   unlesbare Spalten. */
.admin-inline-form { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); margin: 0 var(--space-2) var(--space-2) 0; }
.admin-inline-form input[type="date"], .admin-inline-form input[type="text"] {
  padding: var(--space-1) var(--space-2); border: var(--hairline); border-radius: var(--radius);
  background: var(--color-bg); color: var(--color-text); font: inherit; min-width: 12rem;
}
.admin-inline-form .btn { white-space: nowrap; }
.admin-inline-form .form-hint { flex-basis: 100%; margin: 0; }
.admin-tracking-input { width: 9rem; }
.widerruf-msg-input { width: 100%; min-width: 16rem; }
.admin-image-grid { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.admin-image-item { margin: 0; width: 8rem; }
.admin-image-item img { width: 100%; aspect-ratio: 3/4; object-fit: cover; border-radius: var(--radius); }
.admin-dim-group { display: flex; gap: var(--space-1); align-items: center; flex-wrap: wrap; }
.admin-dim-group input { width: 5rem; }

/* --- Reiter + Formularraster fuer die Produktseite ------------------------
   Die Produktbearbeitung trug sechs Fieldsets und zwei Abschnitte in einer
   einzigen langen Spalte. Reiter plus zweispaltiges Raster bringen den
   Arbeitsbereich auf eine Bildschirmhoehe. Ohne JavaScript ist die
   Reiterleiste ausgeblendet und alle Panels stehen sichtbar untereinander,
   die Seite bleibt also vollstaendig bedienbar (s. site.js). */
.admin-page-title { margin-bottom: var(--space-3); }
.admin-tabs { display: none; }
.js .admin-tabs {
  display: flex; flex-wrap: wrap; gap: var(--space-1);
  border-bottom: 1px solid var(--color-border); margin-bottom: var(--space-4);
}
.admin-tab {
  font: inherit; font-family: var(--font-display); font-weight: 600; font-size: 0.98rem;
  background: none; border: 0; border-bottom: 2px solid transparent;
  margin-bottom: -1px; padding: var(--space-2) var(--space-3);
  color: var(--color-text-muted); cursor: pointer; white-space: nowrap;
}
.admin-tab:hover { color: var(--color-text); }
.admin-tab.is-active { color: var(--color-accent); border-bottom-color: var(--color-brass); }
.admin-tab:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; }

/* Konvention 12: die Spalten muessen schrumpfen duerfen, sonst haelt das
   min-content langer deutscher Feldbeschriftungen den Track auf. */
.admin-field-grid {
  display: grid; gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(min(22rem, 100%), 1fr));
  margin-bottom: var(--space-3);
}
.admin-field-grid > label, .admin-field-grid > .admin-dim-group { min-width: 0; }
.admin-field-wide { grid-column: 1 / -1; }
.admin-field-grid label { display: flex; flex-direction: column; gap: var(--space-1); }
.admin-field-grid label > span:first-child { font-weight: 600; overflow-wrap: break-word; }
.admin-field-grid input[type="text"], .admin-field-grid input[type="url"],
.admin-field-grid input[type="datetime-local"], .admin-field-grid select,
.admin-field-grid textarea {
  width: 100%; padding: var(--space-2); border: var(--hairline); border-radius: var(--radius);
  background: var(--color-bg); color: var(--color-text); font: inherit;
}
.admin-field-grid textarea { resize: vertical; }
.admin-field-grid .form-hint { font-weight: 400; }

.admin-fieldset {
  border: var(--hairline); border-radius: var(--radius-plate);
  padding: var(--space-3) var(--space-4) var(--space-4);
  margin: 0 0 var(--space-4); background: var(--color-surface);
}
.admin-fieldset legend { font-family: var(--font-display); font-weight: 600; padding: 0 var(--space-1); }
.admin-check-grid { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); margin-bottom: var(--space-3); }
.admin-radio-row { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); }

/* Aufklappbare Nebensache innerhalb eines Reiters (z. B. Trailer-Felder),
   gleiche Machart wie die Widerrufsbelehrung im Checkout. */
.admin-subsection { border: var(--hairline); border-radius: var(--radius); background: var(--color-surface); margin-bottom: var(--space-3); }
.admin-subsection > summary {
  display: flex; align-items: center; gap: var(--space-2); cursor: pointer; list-style: none;
  padding: var(--space-2) var(--space-3); font-family: var(--font-display); font-weight: 600;
  color: var(--color-accent);
}
.admin-subsection > summary::-webkit-details-marker { display: none; }
.admin-subsection > summary:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.admin-subsection > summary .chevron {
  flex: none; width: 0.6em; height: 0.6em; margin-left: auto; background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E") center/contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E") center/contain no-repeat;
  transition: transform 0.15s ease;
}
.admin-subsection[open] > summary .chevron { transform: rotate(180deg); }
.admin-subsection > .admin-field-grid { padding: 0 var(--space-3) var(--space-3); margin-bottom: 0; }

/* Speicherleiste haftet unten: der Sinn der Reiter war, nicht scrollen zu
   muessen; ein Knopf am Dokumentende haette das wieder erzwungen. */
.admin-form-actions {
  position: sticky; bottom: 0; z-index: 5;
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3);
  padding: var(--space-3) 0; margin-top: var(--space-3);
  border-top: var(--hairline-brass); background: var(--color-bg);
}
.admin-form-view { margin-left: auto; font-size: 0.92rem; }
.admin-upload-form {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--space-3);
  border: var(--hairline); border-radius: var(--radius-plate);
  padding: var(--space-3); background: var(--color-surface); margin-top: var(--space-4);
}
.admin-upload-form label { display: flex; flex-direction: column; gap: var(--space-1); min-width: 0; }
.admin-upload-form label > span:first-child { font-weight: 600; }
.admin-image-item figcaption { font-size: 0.78rem; color: var(--color-text-muted); }
.admin-image-item form { margin-top: var(--space-1); }
.admin-image-ismain { margin: var(--space-1) 0 0; font-weight: 600; }

/* Hero-Banner-Verwaltung der Engine: das haas-Theme rendert .Hero bewusst
   nicht (der Such-Hero ist fest), Klassen bleiben fuer Admin-Vorschau. */
.hero { position: relative; overflow: hidden; border-radius: var(--radius-plate); background: var(--color-surface); margin-bottom: var(--space-4); }
.hero-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0.35; }
.hero-content { position: relative; padding: var(--space-5) var(--space-4); max-width: 44rem; }
.hero-headline { margin: 0 0 var(--space-2); font-size: 2rem; }
.hero-subline { margin: 0 0 var(--space-3); }
.hero-cta { background: var(--color-surface); color: var(--color-text); }

/* --------------------------------------------------------------------------
   16. Einstiegs-Animation (einmalig, erzaehlt die Hierarchie) + Motion-Regeln
   -------------------------------------------------------------------------- */
@keyframes rise-in {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}
.rise-1, .rise-2, .rise-3 { animation: rise-in 0.7s ease-out both; }
.rise-2 { animation-delay: 0.12s; }
.rise-3 { animation-delay: 0.24s; }

@media (prefers-reduced-motion: reduce) {
  .rise-1, .rise-2, .rise-3 { animation: none; }
  .pillar-card img { transition: none; }
  * { scroll-behavior: auto !important; }
}

/* --------------------------------------------------------------------------
   16b. Checkout-Abschluss & Bestellkorrektur
   Zwei Engine-Vertraege, die haas bis 2026-09 gar nicht bedient hat:
   die Widerrufsbelehrung im Bestellabschluss (§ 356a) samt amtlichem
   Musterformular, und die Entscheidungsseite zur Bestellkorrektur
   (order_revision.html). Gestaltung im Kabinett-Ton: Kartuschenflaeche,
   Messing-Haarlinie, Display-Schrift nur fuer Ueberschriften.
   -------------------------------------------------------------------------- */

/* Breite Tabellen scrollen in ihrem EIGENEN Kasten, nie die Seite. Achtung
   Engine-Konvention 12: overflow-x erzwingt keine Mindestbreite, aber ein
   1fr-Track darueber schon -- deshalb steht dieser Kasten immer in einem
   Container, der schrumpfen darf. */
.table-scroll { overflow-x: auto; }

/* --- Widerrufsbelehrung im Bestellabschluss ------------------------------ */
.checkout-widerruf { margin: var(--space-5) 0 var(--space-4); }
.checkout-widerruf-heading { font-family: var(--font-display); font-weight: 600; font-size: 1.1rem; margin: 0 0 var(--space-2); }
.checkout-widerruf-intro { margin: 0 0 var(--space-2); max-width: 46rem; }
.checkout-widerruf-details {
  border: var(--hairline-brass); border-radius: var(--radius);
  background: var(--color-surface);
}
.checkout-widerruf-details > summary {
  display: flex; align-items: center; gap: var(--space-2); cursor: pointer; list-style: none;
  padding: var(--space-2) var(--space-3);
  font-family: var(--font-display); font-weight: 600; font-size: 0.95rem;
  color: var(--color-accent);
}
.checkout-widerruf-details > summary::-webkit-details-marker { display: none; }
.checkout-widerruf-details > summary:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
/* Chevron als Maske statt als Zeichen: erbt currentColor und dreht sich beim
   Aufklappen, ohne eine zweite Schriftart zu brauchen. */
.checkout-widerruf-details > summary .chevron {
  flex: none; width: 0.6em; height: 0.6em; margin-left: auto; background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E") center/contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E") center/contain no-repeat;
  transition: transform 0.15s ease;
}
.checkout-widerruf-details[open] > summary .chevron { transform: rotate(180deg); }
.checkout-widerruf-body {
  border-top: var(--hairline); padding: var(--space-3);
  font-size: 0.92rem; line-height: 1.6; max-height: 28rem; overflow-y: auto;
}
.checkout-widerruf-body h2 { font-size: 1rem; margin: var(--space-3) 0 var(--space-1); }
.checkout-widerruf-body > :first-child { margin-top: 0; }

/* Das amtliche Muster-Widerrufsformular (internal/web/legaltext.go rendert
   es als <pre class="muster-widerruf">). <pre> haelt die vorgeschriebene
   Zeilenstruktur; ohne pre-wrap laeuft es auf schmalen Schirmen waagerecht
   aus der Seite -- der Wortlaut selbst darf nicht angetastet werden. */
.muster-widerruf {
  white-space: pre-wrap; overflow-wrap: anywhere; font-family: inherit;
  margin: var(--space-3) 0 0; padding: var(--space-3);
  border: var(--hairline); border-radius: var(--radius);
  background: var(--color-surface-alt); font-size: 0.88rem; line-height: 1.55;
}

/* Pflichtangaben nach § 312j Abs. 2 BGB. Zwischen diesem Block und dem
   Bestellbutton steht nichts -- das ist die ganze Pointe der Platzierung. */
.checkout-mandatory { margin: var(--space-4) 0 var(--space-3); }
.checkout-review-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }
.checkout-review-form .checkout-legal { display: flex; margin: var(--space-3) 0; }
.checkout-legal-docs { color: var(--color-text-muted); font-size: 0.92rem; }
.checkout-account-notice { max-width: 46rem; }

/* --- Korrigierte Bestellungen in der Kundenansicht (order_detail.html) ---
   Eine Korrekturzeile ist ein negativer Posten, der eine Teilstornierung
   abbildet. Sie muss als solche lesbar sein, sonst sieht die Zeile aus wie
   ein Fehler und die Summe scheint nicht zu den Posten zu passen. */
.order-corrected-note { margin-bottom: var(--space-2); }
.order-correction-row { color: var(--color-text-muted); }
.order-correction-row td { border-bottom-style: dashed; }
.order-correction-title { font-style: italic; }
.order-correction-amount { white-space: nowrap; }
.order-replacement-row td { background: var(--color-surface); }
.order-item-note { display: block; font-size: 0.85rem; color: var(--color-text-muted); }
.order-ship-frozen-note { display: block; font-size: 0.85rem; color: var(--color-text-muted); }
.order-refund-callout {
  border: var(--hairline-brass); border-radius: var(--radius-plate);
  background: var(--color-surface); padding: var(--space-2) var(--space-3);
  margin: var(--space-3) 0; max-width: 34rem;
}
.order-refund-callout strong { color: var(--color-accent); }

/* --- Bestandshinweis im Kaufkasten (product.html) ------------------------
   Steht bewusst unter dem Kaufknopf: er ist eine Aufklaerung, kein
   Verkaufsargument, und darf den Kauf nicht optisch blockieren. */
.avail-notice {
  margin-top: var(--space-3); border: var(--hairline); border-radius: var(--radius);
  background: var(--color-surface); font-size: 0.9rem;
}
.avail-notice-flat { padding: var(--space-2) var(--space-3); color: var(--color-text-muted); }
.avail-notice > summary {
  display: flex; align-items: center; gap: var(--space-2); cursor: pointer; list-style: none;
  padding: var(--space-2) var(--space-3);
}
.avail-notice > summary::-webkit-details-marker { display: none; }
.avail-notice > summary:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.avail-notice-fact { display: block; color: var(--color-text-muted); }
.avail-notice-cta { display: block; color: var(--color-accent); text-decoration: underline; }
.avail-notice > summary .chevron {
  flex: none; width: 0.6em; height: 0.6em; margin-left: auto; background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E") center/contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3C/svg%3E") center/contain no-repeat;
  transition: transform 0.15s ease;
}
.avail-notice[open] > summary .chevron { transform: rotate(180deg); }
.avail-notice-body { border-top: var(--hairline); padding: var(--space-3); color: var(--color-text-muted); }
.avail-notice-body p { margin: 0; }

/* --- Entscheidungsseite zur Bestellkorrektur (order_revision.html) -------- */
.decision-order-ref { color: var(--color-text-muted); font-size: 0.9rem; margin: 0 0 var(--space-2); }
.decision-intro { line-height: 1.65; max-width: 46rem; }
.decision-deadline { font-size: 0.9rem; color: var(--color-text-muted); }
.decision-operator-message {
  border-left: 3px solid var(--color-brass); background: var(--color-surface);
  padding: var(--space-3); margin: var(--space-3) 0; max-width: 46rem;
}
.decision-operator-message h2 { font-family: var(--font-display); font-size: 1rem; margin: 0 0 var(--space-1); }
.decision-operator-message p { margin: 0; }
.decision-section { margin-top: var(--space-5); }
.decision-section h2 { font-family: var(--font-display); font-size: 1.1rem; margin: 0 0 var(--space-2); }
.decision-card {
  display: flex; gap: var(--space-3); align-items: flex-start;
  border: var(--hairline); border-radius: var(--radius-plate);
  background: var(--color-surface); padding: var(--space-3);
  box-shadow: var(--shadow-card);
}
.decision-card + .decision-card { margin-top: var(--space-3); }
.decision-card picture, .decision-card > img, .decision-card > a { flex: none; }
.decision-card img { width: 5.2rem; height: auto; border-radius: var(--radius); border: var(--hairline); background: var(--color-surface-alt); }
/* Konvention 12: das Textfeld muss schrumpfen duerfen, und deutsche
   Komposita brauchen zusaetzlich einen Umbruchpunkt. */
.decision-card-body { min-width: 0; flex: 1; overflow-wrap: break-word; }
.decision-card-title { font-family: var(--font-display); font-weight: 600; margin: 0 0 0.2em; }
.decision-card-meta { color: var(--color-text-muted); font-size: 0.88rem; margin: 0 0 0.4em; }
.decision-card-reason { font-size: 0.92rem; margin: 0 0 0.4em; }
.decision-card-price { font-weight: 600; margin: 0; }
.decision-unavail-badge {
  display: inline-block; margin-bottom: var(--space-1);
  padding: 0 var(--space-2); border: 1px solid var(--color-warn); border-radius: 999px;
  color: var(--color-warn); font-size: 0.78rem; letter-spacing: 0.04em; text-transform: uppercase;
}
.decision-substitute { margin-top: var(--space-3); }
.decision-substitute-label { font-family: var(--font-display); font-weight: 600; margin: 0 0 var(--space-2); }
.decision-card-sub { box-shadow: none; background: var(--color-surface-alt); }
.decision-card-sub img { width: 4rem; }
.decision-substitute-switch { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-3); cursor: pointer; }
.decision-refund-box {
  border: var(--hairline-brass); border-radius: var(--radius-plate);
  background: var(--color-surface); padding: var(--space-3);
  margin-top: var(--space-4); max-width: 34rem;
}
.decision-refund-box p { margin: 0 0 0.3em; }
.decision-refund-box p:last-child { margin-bottom: 0; }
.decision-refund-amount { font-size: 1.15rem; }
.decision-refund-amount strong { color: var(--color-accent); }
.decision-ship-note { color: var(--color-text-muted); font-size: 0.9rem; }
.decision-note { display: flex; flex-direction: column; gap: var(--space-1); margin-top: var(--space-4); max-width: 34rem; }
.decision-note textarea {
  width: 100%; padding: var(--space-2); border: var(--hairline); border-radius: var(--radius);
  background: var(--color-bg); color: var(--color-text); font: inherit; resize: vertical;
}
/* Drei Aktionen, drei Gewichte. inline-flex statt Block, sonst zieht sich
   der Hinweis unter dem Zweitknopf auf die volle Zeilenbreite. */
.decision-actions { display: flex; flex-wrap: wrap; gap: var(--space-4); align-items: flex-start; margin-top: var(--space-4); }
.decision-action { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; }
.decision-action-hint { font-size: 0.88rem; color: var(--color-text-muted); text-align: center; }
.decision-action-primary .btn { font-size: 1.05rem; padding: 0.75rem 1.6rem; }
/* Die Gesamtstornierung ist ein gesetzliches Recht (§ 323 Abs. 5 BGB), das
   AGB § 7a Abs. 6 zusagt: leise, aber niemals versteckt. */
.decision-cancel-offer { margin-top: var(--space-5); padding-top: var(--space-3); border-top: var(--hairline); }
.decision-cancel-hint { font-size: 0.88rem; color: var(--color-text-muted); margin: 0 0 var(--space-2); max-width: 46rem; }
.decision-cancel-btn {
  background: none; border: none; padding: 0; font: inherit;
  color: var(--color-text-muted); text-decoration: underline; cursor: pointer;
}
.decision-cancel-btn:hover { color: var(--color-warn); }
.decision-cancel-btn:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
.decision-section-unaffected h2 { font-size: 1rem; color: var(--color-text-muted); }
.decision-unaffected-table { color: var(--color-text-muted); }
.decision-unaffected-table img { width: 2.6rem; height: auto; border-radius: var(--radius); }
.decision-account-hint { margin-top: var(--space-4); }
.decision-cancel-panel {
  border: 1px solid var(--color-warn); border-radius: var(--radius-plate);
  background: var(--color-surface); padding: var(--space-4); margin-top: var(--space-4); max-width: 34rem;
}
.decision-cancel-panel[hidden] { display: none; }
.decision-cancel-panel h3 { font-family: var(--font-display); margin: 0 0 var(--space-2); color: var(--color-warn); }

/* --------------------------------------------------------------------------
   16c. Print — der AGB-Anhang der Bestellbestaetigung
   internal/legalpdf startet headless Chromium gegen die EIGENE Instanz und
   druckt z. B. /legal/agb per --print-to-pdf. Die Seite laeuft dabei durch
   das normale base.html, also mit Banner, Kopfzeile, Cookie-Banner und
   Fuss. @media print ist das einzige Filter davor (Konvention 15). Ohne
   diesen Block enthielt der Anhang nachweislich Navigation, Cookie-Banner
   und das komplette Newsletter-Formular samt DSGVO-Einwilligungstext,
   mitten im Vertragstext (gemessen: 4 Seiten, 1,25 MB).
   Ziel: Wortmarke, Vertragstext, knapper Fuss mit der Anschrift.
   -------------------------------------------------------------------------- */
/* Briefkopf ausschliesslich fuer den Druck, s. base.html. */
.print-letterhead { display: none; }

@media print {
  html, body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }

  /* Die Papierkorn-Textur ist ein SVG-feTurbulence-Filter. Am Schirm ist er
     billig, im Druck kann Chromium ihn nicht vektoriell halten, sobald
     print-color-adjust:exact ihn deckend erzwingt -- er wird auf JEDER Seite
     in Druckaufloesung gerastert und blaeht die Datei auf. Der Druck braucht
     die Textur nicht, nur die Flaechenfarbe. */
  body { background-image: none; }

  /* Alles, was Bedienung ist und kein Vertragstext. */
  .main-nav, .header-search, .site-nav, .cookie-banner,
  .footer-col:not(.footer-col-contact), .footer-price-note,
  .footer-signature-claim { display: none; }

  .site-header { border-bottom: none; padding-block: 0; }
  /* Der Bildschirm-Banner faellt im Druck ganz weg und wird durch das
     JPEG oben ersetzt. Chromium bettet die dekodierte Bitmap ein, nicht
     die Quelldatei: das 960px-WebP (39 KB auf der Platte) wurde im PDF zu
     353 KB Flate-Bitmap und machte damit 85 % des AGB-Anhangs aus. Das
     760px-JPEG bleibt beim Einbetten DCT-komprimiert. */
  .site-banner { display: none; }
  /* Volle Satzbreite: das Druckbanner ist ein 6:1-Streifen, auf A4 rund
     32 mm hoch. Ein Deckel liess rechts einen leeren Streifen stehen. */
  .print-letterhead { display: block; width: 100%; height: auto; margin: 0 0 6mm; }
  .footer-grid { display: block; }

  /* Die Einstiegs-Animationen laufen mit fill: both. Ein Druck, der vor dem
     Ende der Animation ausgeloest wird, friert Abschnitte sonst in ihrem
     Anfangszustand ein -- also unsichtbar oder verschoben. */
  .rise-1, .rise-2, .rise-3 { animation: none; opacity: 1; transform: none; }

  /* Ein Vertragsdokument soll nicht mitten im Satz umbrechen. */
  h1, h2, h3 { break-after: avoid; }
  p, li { break-inside: avoid; }
  a { text-decoration: none; color: inherit; }
}

/* Seitenzahlen fuer alles, was zu PDF gedruckt wird (derzeit nur der
   AGB-Anhang). Chromiums --print-to-pdf beachtet die @page-Randboxen, es
   braucht also weder ein CLI-Flag noch eine Paginierungsbibliothek. */
@page {
  size: A4; /* Chromium druckt sonst Letter */
  @bottom-right {
    content: "Seite " counter(page) " von " counter(pages);
    font-family: Georgia, "Times New Roman", serif;
    font-size: 9pt;
    color: #5D574B;
  }
}

/* --------------------------------------------------------------------------
   17. Responsive
   -------------------------------------------------------------------------- */
@media (max-width: 64rem) {
  .product-detail-info { grid-template-columns: 1fr; }
  .product-aside { position: static; max-width: 30rem; }
}
@media (max-width: 48rem) {
  .site-header { gap: var(--space-2); }
  .header-search { order: 10; flex-basis: 100%; max-width: none; }
  .pillars { grid-template-columns: 1fr; }
  .pillar-card img { aspect-ratio: 3 / 2; }
  /* gestapelt braucht das Banner-Bild wieder ein festes Seitenverhältnis */
  /* minmax(0,…) and not a bare 1fr: a bare fr track is minmax(auto, 1fr),
     and that auto minimum is the content's min-content size, which here
     measures 377px against a 328px column -- so the stacked row could not
     shrink to the viewport and the card hung 19px off the right edge at
     375px. Same failure as .footer-col above, one layer further out: the
     floor sits on the track, so capping the card or the image cannot
     reach it. */
  .newest-row { grid-template-columns: minmax(0, 1fr); }
  .newest-row .pillar-card img { height: auto; aspect-ratio: 3 / 2; }
  .haus-row { grid-template-columns: 1fr; gap: var(--space-5); }
  .browse-cards { grid-template-columns: 1fr; }
  .deep-search-grid { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr; gap: var(--space-4); }
  /* Grid items default to min-width:auto, so .footer-col's automatic
     minimum here is its content's min-content size -- which includes
     .footer-newsletter's fixed 24rem base (set for the 3-column desktop
     layout) as a hard floor. That floors the single stacked column's
     track past the viewport; max-width:100% on the newsletter block
     can't help, since it resolves against a track size that's itself
     being inflated by this same min-content contribution. min-width:0
     on the actual grid item (.footer-col) lets the column -- and the
     newsletter block inside it -- shrink to fit. */
  .footer-col { min-width: 0; }
  .nav-icon-label { display: none; }
  /* Fuenf main-nav-Elemente (Home-Icon + 4 Textlinks) sprengen bei
     var(--space-4)-Abstand die 375px-Breite um ~37px; engerer Abstand
     ab hier reicht, ohne die Links selbst zu verkleinern. */
  .main-nav { gap: var(--space-2); }
  /* .dz-viewer braucht hier keine eigene Regel mehr: seit dem Preview-First-
     Umbau deckt es .dz-stage per inset:0/100% ab, das seinerseits nur die
     Groesse des darunterliegenden Vorschaubilds spiegelt (dessen eigenes
     max-height:70vh bereits mobil greift). */
}
@media (min-width: 48.05rem) {
  .auth-columns { grid-template-columns: 1fr 1fr; }
}

.admin-filter { width: min(26rem, 100%); padding: 0.5rem 0.75rem; border: 1px solid var(--color-border, #c9b99a); background: var(--color-surface); color: inherit; font: inherit; }
.admin-filter-count { margin-left: 0.75rem; opacity: 0.7; }

.admin-availability-btn {
  padding: 0.2rem 0.7rem; white-space: nowrap; cursor: pointer;
  font: inherit; font-size: 0.85rem; letter-spacing: 0.02em;
  background: transparent; color: var(--color-accent);
  border: 1px solid var(--color-accent); border-radius: var(--radius);
}
.admin-availability-btn:hover:not(:disabled) { background: var(--color-accent); color: var(--color-accent-contrast); }
.admin-availability-btn:disabled { opacity: 0.5; cursor: wait; }
