/* ============================================================
   Studio 48°Nord, Runde 3 Final: „Final-Atelier"
   Warmes Atelier-Klima mit der feineren Typo-Stimme der
   „Stillen Bühne": Fraunces leicht (380–480) als Display,
   Instrument Sans als Body. Farbe kommt aus den Fotos.

   HERKUNFT: 1:1 aus concepts/round3/final-atelier/style.css
   (Design-Wahrheit, von Max abgenommen). Ab hier gehört das
   Stylesheet der site/-Pipeline; Änderungen bewusst und mit
   Screenshot-Abgleich gegen das Konzept.
   ============================================================ */

:root {
  --paper: #f6f0e4;
  --paper-deep: #ede3d0;
  --paper-card: #fbf7ee;
  --ink: #2c261f;
  --ink-soft: #6a6154;
  --line: rgba(44, 38, 31, 0.16);
  --line-soft: rgba(44, 38, 31, 0.09);
  --sage: #6d7561;
  --sage-deep: #4c543f;
  --honey: #a97b40;
  --honey-deep: #7a5620; /* Text-Variante von Honig, ≥4,5:1 auf getönten Chips */
  --ok: #55663f;
  --no: #8c4a38;

  --font-display: "Fraunces", "Georgia", serif;
  --font-body: "Instrument Sans", "Helvetica Neue", Arial, sans-serif;

  --measure: 34em;
  --wrap: 1180px;
  --pad: clamp(20px, 4vw, 48px);
  --gap-section: clamp(72px, 11vw, 150px);
  --radius: 10px;
  /* Höhe der Sterne-Zeile über den Überschriften im Vertrauens-Block. Fest, damit
     alle drei Spalten denselben Platz reservieren und die Überschriften auf einer
     Linie sitzen (siehe .trust-grid). */
  --trust-eyebrow-h: 1.9rem;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

html {
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { transition: none !important; animation: none !important; }
}

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img { display: block; max-width: 100%; height: auto; }

a { color: inherit; }

::selection { background: var(--sage); color: var(--paper); }

.wrap {
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--pad);
}

/* ---------- Typografie ---------- */

h1, h2 {
  font-family: var(--font-display);
  font-weight: 380;
  line-height: 1.1;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
h3 {
  font-family: var(--font-display);
  font-weight: 440;
  line-height: 1.18;
  letter-spacing: -0.005em;
  text-wrap: balance;
}

.display-xl {
  font-size: clamp(2.5rem, 6.4vw, 4.5rem);
}
.display-l {
  font-size: clamp(1.9rem, 4vw, 2.9rem);
}
.display-m {
  font-size: clamp(1.45rem, 2.6vw, 1.9rem);
  line-height: 1.15;
}

.lede {
  font-size: clamp(1.1rem, 1.6vw, 1.25rem);
  line-height: 1.6;
  color: var(--ink-soft);
  max-width: var(--measure);
}

p { max-width: var(--measure); }

em.tone {
  font-style: italic;
  font-family: var(--font-display);
  font-weight: 430;
  letter-spacing: 0;
}

/* Kicker: gesperrte Eyebrow-Zeile mit Haarlinie (Typo-Stimme „Stille Bühne") */
.kicker {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--sage-deep);
  margin-bottom: 1.2rem;
}
.kicker::before {
  content: "";
  width: 2rem;
  height: 1px;
  background: var(--sage-deep);
  opacity: 0.55;
  flex: none;
}

/* ---------- Header ---------- */

.site-header {
  border-bottom: 1px solid var(--line-soft);
  background: var(--paper);
  position: relative;
  z-index: 40;
}
.site-header .bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 76px;
}

/* Echte Wortmarke (logo.svg, dunkle Marke auf warmem Papier) */
.brand-logo {
  display: inline-flex;
  align-items: center;
  padding: 10px 0;
}
.brand-logo img {
  display: block;
  height: 22px;
  width: auto;
}

/* Logo, Menü und Sprachwähler stehen auf EINER Linie (Max, 2026-07-16: „Schrift
   vom Menü sollte auf gleicher Höhe sein wie Logo und Sprachwähler").
   Zwei Dinge hatten das gekippt, beide im Browser nachgemessen:
   1. `align-items` fehlte hier → Default `stretch` → die Listeneinträge wurden
      alle so hoch wie der größte (71,44 px), und die `inline-block`-Links klebten
      oben in dieser Höhe statt in der Mitte zu sitzen.
   2. Der größte war der Sprachwähler, weil `.nav-lang a` unten das Padding von
      `.site-nav a` erbte und es auf das eigene von `.nav-lang` stapelte.
   Ergebnis: das Menü saß 10,50 px über dem Sprachwähler. Beides ist unten
   behoben; `center` hier ist die Hälfte davon. */
.site-nav ul {
  display: flex;
  align-items: center;
  gap: clamp(18px, 3vw, 36px);
  list-style: none;
}
/* `border-block` statt nur `border-bottom`: die 2 px Unterstrich-Reserve gibt es
   damit oben genauso: die Box bleibt symmetrisch um ihren Text, egal ob der
   Unterstrich gerade sichtbar ist. Ohne die Spiegelung sitzt der Text 1 px über
   der Box-Mitte — dieselbe Mechanik, die in der Fix-Runde die Headlines und hier
   die Menüzeile verschoben hat. Nur die Farbe schaltet, nie die Höhe. */
.site-nav a {
  text-decoration: none;
  font-size: 0.9375rem;
  font-weight: 550;
  letter-spacing: 0.01em;
  padding: 12px 2px;
  display: inline-block;
  border-block: 2px solid transparent;
  transition: border-color 0.18s ease;
}
.site-nav a:hover,
.site-nav a[aria-current="page"] { border-bottom-color: var(--sage); }

/* Sprachumschalter: eigener Listeneintrag am Ende der Nav, leicht abgesetzt. */
.nav-lang {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 2px;
  margin-left: 4px;
  border-left: 1px solid var(--line);
  padding-left: 16px;
}
/* Die Sprach-Kürzel sind KEINE Menü-Punkte und dürfen von `.site-nav a` nichts
   erben, was für Menü-Punkte gedacht ist: das Padding (stapelte sich auf das von
   `.nav-lang` und blies den Eintrag auf 71,44 statt 49,44 px auf → Punkt 4) und
   auf Mobile zusätzlich `width: 100%` + den Trennstrich der Menü-Punkte (hing
   unter jeder einzelnen Sprache als loser Strich). Deshalb hier explizit
   zurückgesetzt statt an den Symptomen zu korrigieren.
   `.current` trug den Unterstrich bisher als einzige und war dadurch 2 px höher
   als ihre Nachbarn; die Reserve liegt jetzt bei allen, sichtbar ist sie nur bei
   der aktiven Sprache. */
.nav-lang a,
.nav-lang .current {
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-decoration: none;
  color: var(--ink-soft);
  padding: 0;
  width: auto;
  border-block: 2px solid transparent;
}
.nav-lang a:hover { color: var(--ink); }
.nav-lang .current { color: var(--ink); border-bottom-color: var(--sage); }
.nav-lang .sep { color: var(--line); font-size: 0.75rem; }

/* ---------- Warenkorb-Zähler (Phase `ajax-warenkorb`) ----------
   Er sitzt im `.bar`-Flex NEBEN der Nav, nicht in ihr: unter 760 px klappt die
   Nav hinter den Hamburger, und ein Zähler zum Aufklappen ist keine Rückmeldung.

   `.site-nav { margin-left: auto }` ist der Grund, warum das Logo trotz des
   dritten Kindes links bleibt: `space-between` allein würde die Nav jetzt in die
   Mitte rücken. Das auto-Margin zieht Nav UND Zähler als Gruppe nach rechts —
   die abgenommene Kopfzeile bleibt damit, wie sie ist, der Zähler hängt sich
   hinten an. */
.site-nav { margin-left: auto; }

.cart-link {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  text-decoration: none;
  color: var(--ink);
  /* Gleiche Rechnung wie `.site-nav a`: 2 px Unterstrich-Reserve oben wie unten,
     damit der Zähler auf derselben Linie sitzt wie Menü und Sprachwähler. */
  padding: 12px 2px;
  border-block: 2px solid transparent;
  transition: border-color 0.18s ease;
}
.cart-link:hover { border-bottom-color: var(--sage); }
.cart-link:focus-visible {
  outline: 2px solid var(--sage-deep);
  outline-offset: 3px;
  border-radius: 4px;
}
.cart-link[hidden] { display: none; }
.cart-icon { display: block; }

/* Die Zahl. `tabular-nums` hält die Breite beim Zählen ruhig (sonst wackelt der
   Kreis zwischen „1" und „7"), `min-width` fängt den Sprung 9 -> 10 ab. */
.cart-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--sage);
  color: var(--paper);
  font-size: 0.75rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* Der Sprung beim Hochzählen. Er ist die eigentliche Rückmeldung: der Klick legt
   etwas in den Korb, ohne die Seite zu wechseln — ohne diese Bewegung übersieht
   man oben rechts eine Zahl, die sich um 1 ändert. Die globale
   prefers-reduced-motion-Regel (oben) schaltet ihn mit ab. */
@keyframes cart-bump {
  0%   { transform: scale(1); }
  38%  { transform: scale(1.32); }
  100% { transform: scale(1); }
}
.cart-count.is-bump { animation: cart-bump 0.34s cubic-bezier(0.34, 1.56, 0.64, 1); }

/* Der Kauf-Klick läuft. Kein Spinner: der Endpunkt antwortet in Millisekunden,
   ein Spinner wäre länger sichtbar als der Vorgang dauert. Der Button nimmt sich
   nur zurück und sperrt den Zweitklick (app.js). */
[aria-busy="true"] { opacity: 0.62; cursor: progress; }

/* ---------- Der Erfolgszustand am Kauf-Punkt (Befund B4) ----------
   Bisher war die einzige Rückmeldung der Zähler oben rechts. Der Header ist aber
   nicht sticky: wer mitten auf der Seite kauft, sieht ihn gar nicht (gemessen
   y = −492) und klickt aus Zweifel ein zweites Mal. Diese Klasse ist die Antwort
   im Sichtfeld, direkt am geklickten Element.

   EINE Klasse für alle Bausteine am Kauf-Punkt: Haupt-Button, mobile Kauf-Leiste
   und später der „+ Dampflanze dazu"-Chip. Wer sie trägt, hat gekauft.

   Das Häkchen steht hier und nicht in der Copy: sonst müsste jede der fünf
   Sprachen es mitschleppen und könnte es einzeln vergessen. `content` bleibt für
   Screenreader unsichtbar genug (die Ansage läuft über #cart-status), ist aber
   genau das Zeichen, das man aus jedem Warenkorb kennt.

   Die Farbe ist Salbei, nicht Ampelgrün: die abgenommene Palette hat keinen
   Signalton, und ein fremdes Grün an dieser Stelle wäre der erste Bruch in einer
   Seite, die sonst durchgehend Papier und Tinte ist. */
.is-added::before {
  content: "✓";
  font-weight: 700;
}
.btn.is-added,
.btn.is-added:hover {
  background: var(--sage-deep);
  border-color: var(--sage-deep);
  color: var(--paper);
}
/* Ein kurzer Impuls, damit der Wechsel auch dann auffällt, wenn der Button vorher
   schon dunkel war. Dieselbe Kurve wie beim Zähler-Sprung: eine Bewegung, zwei
   Orte, kein zweites Vokabular. Die globale prefers-reduced-motion-Regel schaltet
   ihn mit ab, der Text-/Farbwechsel bleibt. */
@keyframes added-pop {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.04); }
  100% { transform: scale(1); }
}
.btn.is-added { animation: added-pop 0.34s cubic-bezier(0.34, 1.56, 0.64, 1); }

/* Nur für Screenreader: die Meldung „Zum Warenkorb hinzugefügt" (role=status).
   Sehende sehen den Zähler springen, hier steht dasselbe in Worten. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}

/* Checkbox visually-hidden statt display:none, damit sie im
   Accessibility-Tree bleibt; Fokus liegt auf dem Label (tabindex). */
.nav-toggle-box {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}
.nav-toggle-label {
  display: none;
  width: 48px;
  height: 48px;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  border: 1px solid var(--line);
  border-radius: 8px;
}
.nav-toggle-label:focus-visible {
  outline: 2px solid var(--sage-deep);
  outline-offset: 2px;
}
.nav-toggle-label svg { display: block; }
.nav-toggle-label .icon-close { display: none; }

@media (max-width: 760px) {
  .nav-toggle-label { display: inline-flex; }
  /* Auf Mobile ist `.site-nav` absolut positioniert, also aus dem Fluss: im
     `.bar` stehen nur noch Logo, Zähler und Hamburger. `order` gruppiert Zähler
     und Hamburger rechts (Quell-Reihenfolge wäre Hamburger, dann Zähler), das
     auto-Margin schiebt die Gruppe dorthin und lässt das Logo links. */
  .cart-link { order: 1; margin-left: auto; }
  .nav-toggle-label { order: 2; }
  .site-nav {
    display: none;
    position: absolute;
    left: 0; right: 0; top: 100%;
    background: var(--paper);
    border-bottom: 1px solid var(--line);
    padding: 8px var(--pad) 20px;
  }
  /* Aufgeklapptes Menü: gestapelt und linksbündig. `align-items` zeigt in der
     Spalten-Richtung nach LINKS/RECHTS — das `center` von oben würde die Einträge
     hier waagerecht zentrieren, deshalb zurück auf `stretch`. */
  .site-nav ul { flex-direction: column; align-items: stretch; gap: 0; }
  .site-nav a {
    padding: 14px 2px;
    font-size: 1.05rem;
    width: 100%;
    border-block: 0;
    border-bottom: 1px solid var(--line-soft);
  }
  .nav-toggle-box:checked ~ .site-nav { display: block; }
  .nav-toggle-box:checked ~ .nav-toggle-label .icon-open { display: none; }
  .nav-toggle-box:checked ~ .nav-toggle-label .icon-close { display: block; }
  .nav-lang {
    margin-left: 0;
    border-left: none;
    padding-left: 2px;
    padding-top: 16px;
    margin-top: 6px;
    border-top: 1px solid var(--line-soft);
    border-bottom: none;
    gap: 14px;
  }
  /* Muss NACH `.site-nav a` stehen, sonst holt sich der Trennstrich der
     Menü-Punkte die Sprachen zurück (gleiche Spezifität, spätere Regel gewinnt).
     Und es fasst bewusst NUR die Links: `.current` ist ein <span>, den
     `.site-nav a` nie trifft. Nähme man ihn hier mit, überschriebe das
     `transparent` den sage-Unterstrich der aktiven Sprache — die verschwände
     auf Mobile spurlos. Das Padding ist das Touch-Ziel (gemessen 47,77 px). */
  .nav-lang a {
    padding: 10px 0;
    border-block: 2px solid transparent;
  }
  .nav-lang a,
  .nav-lang .current { font-size: 0.9rem; }
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55em;
  min-height: 52px;
  padding: 0.85em 1.7em;
  border-radius: 999px;
  font-family: var(--font-body);
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: 0.015em;
  text-decoration: none;
  border: 1px solid var(--ink);
  transition: background-color 0.18s ease, color 0.18s ease, transform 0.18s ease;
}
.btn-solid { background: var(--ink); color: var(--paper); }
.btn-solid:hover { background: var(--sage-deep); border-color: var(--sage-deep); }
.btn-ghost { background: transparent; color: var(--ink); }
.btn-ghost:hover { background: rgba(44, 38, 31, 0.06); }

/* ---------- Hero ---------- */

.hero {
  padding-block: clamp(44px, 7vw, 96px) 0;
}
.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
  gap: clamp(28px, 5vw, 72px);
  align-items: center;
}
.hero-copy .display-xl { margin-bottom: 1.1rem; }
.hero-copy .lede { margin-bottom: 2rem; }
.hero-ctas { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 1.5rem; }

/* Aggregat früh sichtbar: Bewertungszeile im Hero */
.hero-rating {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.3em 0.7em;
  font-size: 0.9rem;
  color: var(--ink-soft);
  margin-bottom: 2rem;
}
.hero-rating .stars { margin: 0; font-size: 0.85rem; }
.hero-rating strong { color: var(--ink); font-weight: 600; }

.hero-facts {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4em 1.6em;
  font-size: 0.88rem;
  color: var(--ink-soft);
  border-top: 1px solid var(--line);
  padding-top: 1.2rem;
  max-width: 32em;
}
.hero-facts li { display: flex; align-items: baseline; gap: 0.5em; }
.hero-facts li::before {
  content: "";
  width: 5px; height: 5px;
  background: var(--sage);
  border-radius: 1px;
  transform: rotate(45deg) translateY(-1px);
  flex: none;
}

.hero-stage { position: relative; }
.hero-stage .main {
  border-radius: var(--radius);
  overflow: hidden;
}
.hero-stage .main img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
}

@media (max-width: 860px) {
  .hero-grid { grid-template-columns: 1fr; }
  .hero-stage { margin-bottom: 26px; }
}

/* ---------- Sektionen allgemein ---------- */

.section { padding-top: var(--gap-section); }
.section:last-of-type { padding-bottom: var(--gap-section); }

.section-head {
  max-width: 44em;
  margin-bottom: clamp(30px, 4.5vw, 54px);
}
.section-head .display-l { margin-bottom: 0.7rem; }

/* Editorial-Split: Bild-Bühne + Text */
.editorial {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: clamp(28px, 5vw, 80px);
  align-items: center;
}
.editorial.flip { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); }
.editorial.flip .editorial-media { order: 2; }
.editorial-media img {
  width: 100%;
  border-radius: var(--radius);
  object-fit: cover;
}
.editorial-media figcaption {
  font-size: 0.78rem;
  color: var(--ink-soft);
  padding-top: 10px;
  letter-spacing: 0.02em;
}
.editorial-copy .display-l,
.editorial-copy .display-m { margin-bottom: 1rem; }
.editorial-copy p + p { margin-top: 1em; }

@media (max-width: 860px) {
  .editorial, .editorial.flip { grid-template-columns: 1fr; }
  .editorial.flip .editorial-media { order: 0; }
}

/* Doppelbild in der Werkstatt-Story */
.media-pair {
  display: grid;
  grid-template-columns: 3fr 2fr;
  gap: 14px;
  align-items: end;
}
.media-pair img { border-radius: var(--radius); object-fit: cover; width: 100%; }
.media-pair .a img { aspect-ratio: 4 / 3; }
.media-pair .b img { aspect-ratio: 3 / 4; }

/* Werkstatt-Bühne. B4 hatte hier zwei Fotos + einen Drucker-Slot; F1 gab den Slot
   der Founder-Story, weil es kein Drucker-Material gab. Punkt 8 stellte das
   Drucker-Video als vierte Kachel daneben (2x2), Punkt 9 (Max, 2026-07-16) hat das
   zurückgedreht: „beide Videos sind too much in einem Sichtbereich." Der Drucker
   ist jetzt eine Kachel der Instagram-Sektion (`.insta-grid video`), die Bühne ist
   wieder das Trio — den Weg zurück hatte der 2x2-Umbau ausdrücklich offengelassen.

   Aufteilung: links oben das Querformat-Foto, links unten die Founder-Story (beide
   4:3), rechts über BEIDE Zeilen das Hochformat-Foto. `.b` bekommt seine Höhe damit
   aus den Zeilen (`height: 100%`), nicht aus dem Eigenformat des Fotos — genau
   deshalb steht hier kein `aspect-ratio`. Das 8/9 aus dem 2x2-Zwischenstand ist
   weg: es war nur nötig, solange `.b` in EINER Zeile neben `.a` saß und die
   Zeilenhöhe treffen musste. */
.media-trio {
  display: grid;
  grid-template-columns: 3fr 2fr;
  grid-template-rows: auto auto;
  gap: 14px;
}
.media-trio figure { margin: 0; min-width: 0; }
.media-trio img { border-radius: var(--radius); object-fit: cover; width: 100%; }
.media-trio .a { grid-column: 1; grid-row: 1; }
.media-trio .a img { aspect-ratio: 4 / 3; }
.media-trio .b { grid-column: 2; grid-row: 1 / 3; }
.media-trio .b img { height: 100%; }
.media-trio .ph-slot { grid-column: 1; grid-row: 2; }

/* Founder-Story-Video (F1): dritte Kachel, Platz des entfallenen Drucker-Fotos.
   Eigene Klasse (nicht .workshop-video), damit app.js sie nicht stumm anspielt.
   Hochformat-Clip, cover auf denselben Kachel-Fußabdruck wie das frühere Foto;
   controls startet auf Klick mit Ton. */
.media-trio .founder-media {
  grid-column: 1;
  grid-row: 2;
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  min-width: 0;
}
.founder-video {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--radius);
  background: #0b1b2b;
  display: block;
  cursor: pointer;
}
.founder-media figcaption {
  font-size: 0.78rem;
  color: var(--ink-soft);
  letter-spacing: 0.02em;
}
@media (max-width: 520px) {
  .media-trio { grid-template-columns: 1fr; grid-template-rows: auto; }
  .media-trio .a,
  .media-trio .b,
  .media-trio .ph-slot,
  .media-trio .founder-media { grid-column: 1; grid-row: auto; }
  .media-trio .b img { aspect-ratio: 3 / 4; height: auto; min-height: 0; }
  .founder-video { aspect-ratio: 3 / 4; }
}

/* Fakten-Zeile unter Story-Text */
.fact-row {
  display: flex;
  flex-wrap: wrap;
  gap: 2em;
  margin-top: 1.8rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--line);
}
.fact-row div { min-width: 8em; }
.fact-row dt {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--sage-deep);
  margin-bottom: 0.2em;
}
.fact-row dd {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 420;
}

/* ---------- Sortiment ---------- */

.product-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: clamp(18px, 2.5vw, 30px);
}
.product-card {
  background: var(--paper-card);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.product-card:hover {
  transform: translateY(-3px);
  box-shadow: 0 22px 44px -26px rgba(44, 38, 31, 0.4);
}
/* Karten-Hauptlink (Karte ist <article>, damit der Warenkorb-Link daneben leben kann) */
.card-link {
  color: inherit;
  text-decoration: none;
  display: flex;
  flex-direction: column;
  flex: 1;
}
.card-cart {
  padding: 0 20px 14px;
  font-size: 0.82rem;
  max-width: none;
}
.card-cart a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 4px 0;
  color: var(--sage-deep);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.card-cart a:hover { color: var(--ink); }
.product-card img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}
/* Hover-Video (Punkt 5): Bild und Video liegen deckungsgleich in einem Rahmen.
   Der Rahmen bekommt seine Maße vom BILD — auch das 16:9 der Feature-Karte — und
   das Video legt sich per inset:0 darüber. Damit kennt nur eine Stelle das Seiten-
   verhältnis, und es gibt keinen Layout-Shift: die Kachel ist schon so hoch, wie
   sie bleibt, bevor das erste Video-Byte existiert.
   `overflow: hidden` hält den Bild-Zoom beim Hover im Rahmen, statt ihn über den
   Kartentext laufen zu lassen. */
.card-media {
  position: relative;
  overflow: hidden;
}
.card-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  /* Der Klick gehört der Karte (sie ist der Link): das Video nimmt ihn nicht weg. */
  pointer-events: none;
}
.product-card.is-playing .card-video { opacity: 1; }
@media (prefers-reduced-motion: no-preference) {
  .card-video { transition: opacity 0.28s ease; }
}
.product-card .body {
  padding: 18px 20px 20px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
}
.product-card h3 {
  font-size: 1.18rem;
  font-weight: 440;
  line-height: 1.2;
}
/* ✓-Kurznutzen auf den Sortiment-Karten */
/* `flex: 1` schiebt die Preiszeile an den Kartenboden, damit die Preise einer
   Reihe auf einer Linie stehen — das soll so bleiben. Ohne `align-content: start`
   verteilt das Raster die geschenkte Höhe aber auf seine ZEILEN: in einer Reihe
   mit der doppelt hohen Feature-Karte wuchs diese Liste auf 186,5 px, obwohl ihre
   zwei Haken-Zeilen nur 44 px brauchen. Die Haken trieben dadurch je nach
   Nachbarkarte unterschiedlich weit auseinander. Jetzt behalten die Zeilen ihre
   natürliche Höhe, der Rest bleibt als Luft davor. */
.card-checks {
  list-style: none;
  display: grid;
  align-content: start;
  gap: 0.3em;
  font-size: 0.88rem;
  color: var(--ink-soft);
  line-height: 1.45;
  flex: 1;
}
.card-checks li {
  display: flex;
  gap: 0.55em;
  align-items: baseline;
}
.card-checks li::before {
  content: "✓";
  color: var(--sage-deep);
  font-family: var(--font-display);
  font-weight: 500;
  flex: none;
}
.product-card .price {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 470;
  padding-top: 8px;
}
.product-card .price small {
  display: block;
  font-family: var(--font-body);
  font-weight: 400;
  font-size: 0.72rem;
  color: var(--ink-soft);
  margin-top: 3px;
}
/* Die Feature-Karte trug hier einen zusätzlichen Sage-Rahmen (outline) um die ganze
   Karte, den die normale Karte daneben nicht hat. Max, 2026-07-16 (Punkt 3): weg.
   Sie bleibt damit auf dem `border` aller Karten und hebt sich weiter über ihre
   Größe, das 16:9-Bild und den „UNSER KERNSTÜCK"-Tag ab, nicht über einen Rahmen. */
/* 8 Karten + 1 Doppel-Slot = 9 Slots (seit `pdps-neu`, vorher 6 + 1 = 7): die
   Feature-Karte spannt ab 2 Spalten über 2, damit die letzte Reihe nicht mit
   Leerfläche ausfranst. 9 Slots gehen bei 3 Spalten glatt auf. */
@media (min-width: 620px) {
  .product-card.is-feature { grid-column: span 2; }
  .product-card.is-feature img { aspect-ratio: 16 / 9; }
}
.product-card .card-tag {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--sage-deep);
}

.grid-note {
  margin-top: 1.6rem;
  font-size: 0.88rem;
  color: var(--ink-soft);
}

/* ---------- Kompatibilität ---------- */

.compat {
  background: var(--paper-deep);
  border-block: 1px solid var(--line-soft);
  margin-top: var(--gap-section);
  padding-block: clamp(56px, 8vw, 104px);
}
.compat .section-head { margin-bottom: clamp(24px, 3.5vw, 40px); }

.compat-table {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  max-width: 860px;
}
.compat-item {
  background: var(--paper-card);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 22px 22px 20px;
}
.compat-item .model {
  font-family: var(--font-display);
  font-size: 1.35rem;
  font-weight: 420;
  margin-bottom: 2px;
}
.compat-item .sub {
  font-size: 0.82rem;
  color: var(--ink-soft);
  margin-bottom: 12px;
}
.compat-item .verdict {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 6px 12px;
  border-radius: 999px;
}
.compat-item.yes .verdict { background: rgba(85, 102, 63, 0.14); color: var(--ok); }
/* EC890: keine Absage, sondern Varianten-Hinweis (Honig-Ton statt Rot) */
.compat-item.alt .verdict { background: rgba(169, 123, 64, 0.14); color: var(--honey-deep); }
.compat-item.no .verdict { background: rgba(140, 74, 56, 0.13); color: var(--no); }
.compat-item .why {
  font-size: 0.85rem;
  color: var(--ink-soft);
  margin-top: 12px;
  line-height: 1.5;
}
.compat-note {
  margin-top: 1.4rem;
  font-size: 0.9rem;
  color: var(--ink-soft);
  max-width: 46em;
}

/* Kompatibilitäts-Matrix (Phase seo-storefront): Modell × Teil als echte
   Tabelle unter den drei Magnet-Check-Kacheln. Sie erbt deren Farbcodes
   (.yes/.alt/.no), damit die Sektion eine Sprache spricht. */
.compat-matrix-block {
  margin-top: clamp(36px, 5vw, 64px);
  max-width: 860px;
}
.compat-matrix-block h3 {
  font-family: var(--font-display);
  font-size: clamp(1.3rem, 2.2vw, 1.65rem);
  font-weight: 420;
  margin-bottom: 4px;
}
.compat-matrix-block .lede-s {
  font-size: 0.92rem;
  color: var(--ink-soft);
  margin-bottom: 18px;
}
/* Der Scroll-Container ist Pflicht, nicht Kosmetik: fünf Spalten passen auf
   kein Telefon, und eine Tabelle, die den Body horizontal scrollen lässt,
   zerschießt die ganze Seite. */
.compat-matrix-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: var(--paper-card);
}
/* `separate` + `border-spacing: 0` sieht aus wie `collapse`, umgeht aber die
   Kombination aus zusammengefassten Rahmen und `position: sticky` an
   Tabellenzellen (die klebende erste Spalte unten). Im Testbrowser klebt sie mit
   beiden Werten, gemessen 2026-07-19; WebKit hatte dafür lange einen Bug, und
   hier steht keine Safari-Messung dagegen. Die Variante ohne die fragliche
   Kombination kostet nichts, also nimmt sie diese Tabelle. */
.compat-matrix {
  border-collapse: separate;
  border-spacing: 0;
  width: 100%;
  min-width: 640px;
  font-size: 0.88rem;
}
.compat-matrix th,
.compat-matrix td {
  padding: 12px 14px;
  text-align: left;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: middle;
}
.compat-matrix tbody tr:last-child th,
.compat-matrix tbody tr:last-child td { border-bottom: 0; }
.compat-matrix thead th {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 420;
  white-space: nowrap;
}
.compat-matrix thead th .sub {
  display: block;
  font-family: inherit;
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  color: var(--ink-soft);
}
.compat-matrix tbody th {
  font-weight: 500;
  min-width: 190px;
}
/* Erste Spalte bleibt beim seitlichen Scrollen stehen. Ohne das schiebt sich
   auf dem Telefon der Teil-Name aus dem Bild, sobald man die hinteren Modelle
   ansieht, und die Zeile verliert ihre Beschriftung. */
.compat-matrix thead th:first-child,
.compat-matrix tbody th {
  position: sticky;
  left: 0;
  background: var(--paper-card);
  z-index: 1;
}
.compat-matrix .verdict {
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 5px 10px;
  border-radius: 999px;
  white-space: nowrap;
}
.compat-matrix .yes .verdict { background: rgba(85, 102, 63, 0.14); color: var(--ok); }
.compat-matrix .alt .verdict { background: rgba(169, 123, 64, 0.14); color: var(--honey-deep); }
.compat-matrix .no .verdict { background: rgba(140, 74, 56, 0.13); color: var(--no); }
.compat-matrix-legend {
  margin-top: 1rem;
  font-size: 0.85rem;
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 52em;
}
.compat-matrix-legend + .compat-matrix-legend { margin-top: 0.5rem; }

@media (max-width: 720px) {
  .compat-table { grid-template-columns: 1fr; }
}

/* ---------- Vertrauen ---------- */

.trust-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 4vw, 56px);
}
/* Die Sterne über „4,9 von 5 auf Etsy" standen im Fluss und schoben NUR die erste
   Überschrift nach unten — die drei Überschriften saßen auf drei verschiedenen
   Linien (Max, 2026-07-16, Punkt 6). Jetzt reservieren ALLE drei Spalten dieselbe
   Eyebrow-Zeile über der Überschrift, und die Sterne liegen darin, statt sie
   aufzuspannen. Damit startet jedes h3 auf derselben Höhe, ohne leeres Markup in
   den Spalten ohne Sterne und auf jeder Breite gleich (das Reservat hängt an der
   Spalte, nicht am Inhalt). */
.trust-grid article {
  position: relative;
  padding-top: var(--trust-eyebrow-h);
}
.trust-grid .stars {
  position: absolute;
  top: 0;
  left: 0;
  margin: 0;
  line-height: var(--trust-eyebrow-h);
}
.trust-grid article h3 {
  font-size: 1.3rem;
  margin-bottom: 0.5rem;
}
.trust-grid article p {
  font-size: 0.95rem;
  color: var(--ink-soft);
}
.stars {
  color: var(--honey);
  letter-spacing: 0.18em;
  font-size: 0.95rem;
  margin-bottom: 0.6rem;
  display: inline-block;
}
@media (max-width: 760px) {
  .trust-grid { grid-template-columns: 1fr; }
}

/* ---------- Footer ---------- */

.site-footer {
  background: var(--ink);
  color: rgba(246, 240, 228, 0.82);
  margin-top: var(--gap-section);
  padding-block: clamp(48px, 7vw, 84px) 40px;
  font-size: 0.92rem;
}
.site-footer a { color: var(--paper); text-decoration: none; }
.site-footer a:hover { text-decoration: underline; text-underline-offset: 3px; }

.footer-top {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 28px;
  padding-bottom: 34px;
  border-bottom: 1px solid rgba(246, 240, 228, 0.18);
}
/* Logo im dunklen Footer: dunkle Marke invertiert auf Papierton */
.footer-top .brand-logo img {
  height: 20px;
  filter: invert(1) brightness(0.96) sepia(0.08);
  opacity: 0.92;
}
.footer-legal ul {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 22px;
}
.footer-legal a { display: inline-block; padding: 10px 0; }

.footer-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 12px 28px;
  padding-top: 26px;
  font-size: 0.85rem;
  color: rgba(246, 240, 228, 0.62);
}
.footer-meta p { max-width: none; }

.footer-link-btn {
  display: inline-block;
  padding: 10px 0;
  background: none;
  border: none;
  color: var(--paper);
  font: inherit;
  cursor: pointer;
  text-decoration: none;
}
.footer-link-btn:hover { text-decoration: underline; text-underline-offset: 3px; }

/* Der eigene Consent-Balken ist raus (Phase `consent-bruecke`): Complianz ist jetzt
   das einzige CMP, auch auf der Storefront. Sein Banner bringt seine eigene, per JS
   geladene CSS mit (banner-1-optin.css, z-index 99999, eigenes Bottom-Sheet auf
   Mobil). Hier ist deshalb keine eigene Banner-CSS mehr nötig. */

/* ---------- Mobile Kauf-Leiste (Befund B5) ----------
   Gemessen: der CTA einer PDP liegt bei 390 px Breite auf y ≈ 1620, der Viewport
   ist 844 hoch. Der Kauf-Button steht damit fast zwei Screens unter dem Fold, auf
   dem Hauptkanal. Max' Entscheid: Kauf-Leiste, die nach dem ersten Scroll kommt.
   Die Reihenfolge der Seite bleibt unangetastet.

   NUR MOBIL, und diese Datei entscheidet das allein (app.js schaltet ausschließlich
   .is-visible). 860 px ist dieselbe Grenze, an der die PDP von zwei Spalten auf
   eine fällt — genau ab dort rutscht der Kauf-Button unter die Galerie und damit
   aus dem Bild. Eine eigene Zahl hier wäre eine zweite Wahrheit über dasselbe
   Layout. */
.sticky-buy { display: none; }

@media (max-width: 860px) {
  .sticky-buy {
    display: block;
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    /* Weit unter dem Complianz-Banner (z-index 99999): steht das Consent-Banner
       noch offen, deckt sein Bottom-Sheet die Kauf-Leiste vollständig ab — wer noch
       nicht entschieden hat, sieht das Banner, nicht eine Kauf-Leiste davor. */
    z-index: 400;
    background: var(--paper-card);
    border-top: 1px solid var(--line);
    box-shadow: 0 -8px 24px rgba(44, 38, 31, 0.14);
    /* Ausgangszustand: unten aus dem Bild geschoben. `visibility` fährt mit, damit
       die Leiste im ausgefahrenen Zustand nicht doch noch Klicks abfängt oder in
       der Tab-Reihenfolge auftaucht, während sie niemand sieht. */
    transform: translateY(100%);
    visibility: hidden;
    transition: transform 0.22s ease, visibility 0s linear 0.22s;
  }
  .sticky-buy.is-visible {
    transform: translateY(0);
    visibility: visible;
    transition: transform 0.22s ease, visibility 0s;
  }

  .sticky-buy-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 10px var(--pad);
    padding-bottom: max(10px, env(safe-area-inset-bottom));
  }
  /* Preis und Variante nehmen den Platz links, der Button behält rechts seinen.
     `min-width: 0` ist nötig, damit die Variantenzeile kürzen darf statt die
     Leiste zu sprengen (Flex-Kinder haben `min-width: auto`). */
  .sticky-buy-facts { flex: 1 1 auto; min-width: 0; }

  /* Der Button trägt `.buy-cta`, weil daran der EINE Kauf-Weg hängt (delegierter
     Listener in app.js). Damit erbt er auch `.buy-cta { width: 100% }` aus der
     Kauf-Spalte — dort richtig, hier fatal: gemessen nahm er die vollen 350 px
     der Leiste ein, drückte Preis und Variante auf Breite 0 und stand als
     einziges Element darin. Der Selektor ist deshalb bewusst zwei Klassen tief,
     sonst gewinnt die spätere `.buy-cta`-Zeile bei gleicher Spezifität. */
  .sticky-buy .sticky-cta { width: auto; margin-bottom: 0; }
  .sticky-price {
    display: block;
    font-family: var(--font-display);
    font-size: 1.15rem;
    line-height: 1.2;
    color: var(--ink);
  }
  .sticky-variant {
    display: block;
    margin-top: 2px;
    font-size: 0.76rem;
    color: var(--ink-soft);
    /* Ein langer Variantenname („ohne Magnete, für EC890") darf die Leiste nicht
       zweizeilig machen und den Button nach unten drücken. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .sticky-cta {
    flex: none;
    min-height: 46px;
    padding: 0.6em 1.2em;
    font-size: 0.9rem;
    /* Der Kauf-Button bricht nicht um: „In den Warenkorb" zweizeilig macht die
       Leiste hoch und schiebt sie über den Inhalt. */
    white-space: nowrap;
  }

  /* Der Platz am Seitenende. Ohne ihn liegt die Leiste dauerhaft auf den letzten
     Zeilen des Footers, und das sind ausgerechnet Impressum, Datenschutz, AGB und
     Widerruf. Ein Kauf-Button, der die Rechtslinks verdeckt, ist kein Schönheits-
     fehler. Reserviert wird großzügiger als die Leiste hoch ist, damit auch eine
     umbrechende Variantenzeile noch darunter passt. */
  .has-sticky-buy .site-footer { padding-bottom: 104px; }
}

/* ============================================================
   Produktseite
   ============================================================ */

.crumbs {
  font-size: 0.85rem;
  color: var(--ink-soft);
  padding-block: 20px 0;
}
.crumbs a { text-decoration: none; }
.crumbs a:hover { text-decoration: underline; text-underline-offset: 3px; }
.crumbs .sep { margin-inline: 0.5em; color: var(--line); }

.pdp {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 5fr);
  gap: clamp(28px, 5vw, 72px);
  row-gap: clamp(20px, 3vw, 32px);
  padding-top: clamp(20px, 3vw, 40px);
  align-items: start;
}
/* Reviews leben links unter der Galerie, die Kauf-Spalte spannt daneben durch.
   Mobil (1 Spalte) greift die Quell-Reihenfolge: Galerie → Buy-Box → Reviews. */
.pdp .gallery { grid-column: 1; grid-row: 1; }
.pdp .buy-col { grid-column: 2; grid-row: 1 / span 2; }
.pdp .pdp-reviews { grid-column: 1; grid-row: 2; }
@media (max-width: 860px) {
  .pdp { grid-template-columns: 1fr; }
  .pdp .gallery,
  .pdp .buy-col,
  .pdp .pdp-reviews { grid-column: auto; grid-row: auto; }
}

/* Galerie */
.gallery .stage {
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--paper-deep);
  /* Bühnenbild und Bühnenvideo liegen im selben Rahmen; sichtbar ist immer genau
     eines (app.js schaltet über [hidden]). position:relative hält das Video darin. */
  position: relative;
}
.gallery .stage img,
.gallery .stage video {
  width: 100%;
  aspect-ratio: 4 / 4.6;
  object-fit: cover;
  display: block;
}
/* `display: block` oben ist spezifischer als das `display: none`, das der Browser
   dem hidden-Attribut aus seinem Standard-Stylesheet gibt. Ohne diese Zeile bleibt
   das versteckte Element im Fluss und Bild UND Video stehen untereinander — die
   Bühne wird doppelt so hoch (nachgemessen: 1358 statt 679 px). Der Selektor ist
   mit drei Klassen-Gewichten spezifischer als die Regel darüber. */
.gallery .stage [hidden] { display: none; }
/* Das Video im Bühnenrahmen (Punkt 3). `contain` statt `cover`: ein Video ist der
   Beleg, dass das Teil an der Maschine sitzt — davon etwas wegzuschneiden wäre genau
   der Fehler aus Punkt 6, nur in bewegt. Die Fotos dürfen beschnitten werden, weil
   es von ihnen fünf gibt und ein Fokuspunkt je Bild sie rettet; vom Video gibt es
   eines, und es ist 1:1 oder 16:9, während der Rahmen 4:4.6 hoch ist. */
.gallery .stage video {
  object-fit: contain;
  background: #0b1b2b;
}
.gallery .thumbs {
  display: grid;
  /* Spaltenzahl kommt aus dem Build (--thumbs): seit das Video die erste Kachel ist,
     sind es 5 oder 6 statt immer 5. Fest verdrahtet bräche die sechste in eine zweite
     Zeile und stünde dort allein. */
  grid-template-columns: repeat(var(--thumbs, 5), 1fr);
  gap: 10px;
  margin-top: 10px;
}
.gallery .thumbs button {
  padding: 0;
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  overflow: hidden;
  cursor: pointer;
  background: none;
  min-height: 44px;
}
.gallery .thumbs button[aria-pressed="true"] { border-color: var(--ink); }
.gallery .thumbs img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
}
/* Video-Thumbnail (Punkt 3): Play-Zeichen über dem Poster. Ohne das ist die Kachel
   von einem Standbild nicht zu unterscheiden — sie zeigt ja auch eins (das Poster
   des Videos ist ein Produktfoto, das die Seite ohnehin lädt). */
.gallery .thumbs .thumb-video { position: relative; display: block; }
.play-badge {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  pointer-events: none;
}
.play-badge svg {
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: rgba(20, 16, 12, 0.55);
  /* Der Ring trennt das Zeichen von hellen Motiven: auf der weißen Schale wäre ein
     weißes Dreieck sonst unsichtbar. */
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.7);
  padding: 2px;
}

/* Kauf-Spalte */
.buy-col .kicker { margin-bottom: 0.7rem; }
.buy-col h1 { font-size: clamp(1.9rem, 3.6vw, 2.7rem); margin-bottom: 0.4rem; }
.buy-col .hero-rating { margin: 0.4rem 0 0.3rem; }
/* Preis + PAngV-Hinweis (Max, 2026-07-16: „wirkt schlecht zentriert gegenüber
   dem Preis in der Höhe").
   Vorher `align-items: baseline`: die Grundlinie des Hinweises traf die des
   Preises — solange der Hinweis EINZEILIG war, sah das richtig aus. Seit der
   EU-Versand dazugekommen ist, bricht er auf zwei Zeilen um; die zweite hängt
   unter der gemeinsamen Grundlinie und zieht den Block optisch nach unten
   (gemessen: 15,80 px zu tief, Desktop wie Mobile).
   `center` richtet die optischen Mitten aus und ist unabhängig davon, ob der
   Satz auf 1, 2 oder 3 Zeilen läuft — was er je nach Sprache tut. */
.buy-col .price-line {
  display: flex;
  align-items: center;
  gap: 0.8em;
  margin-block: 0.6rem 1.2rem;
}
.buy-col .price-line .price {
  font-family: var(--font-display);
  font-size: 1.7rem;
  font-weight: 420;
  white-space: nowrap;
  flex-shrink: 0;
}
.buy-col .price-line small { font-size: 0.78rem; color: var(--ink-soft); }

.benefit-list {
  list-style: none;
  margin-block: 1.2rem 1.5rem;
  border-block: 1px solid var(--line);
  padding-block: 1.1rem;
  display: grid;
  gap: 0.55em;
}
.benefit-list li {
  display: flex;
  gap: 0.7em;
  font-size: 0.97rem;
  align-items: baseline;
}
/* ✓-Häkchen (übernommen aus „Stille Bühne") */
.benefit-list li::before {
  content: "✓";
  flex: none;
  color: var(--sage-deep);
  font-family: var(--font-display);
  font-weight: 500;
  line-height: 1.4;
}

.machine-check {
  background: var(--paper-deep);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 16px 18px;
  margin-bottom: 1.5rem;
  font-size: 0.92rem;
  /* Sprungziel aus der Galerie (#variante): nicht unter den Sticky-Header rutschen. */
  scroll-margin-top: 96px;
}
/* Kurzes Aufleuchten, wenn man aus der Galerie hierher springt: der Umschalter
   soll sich zu erkennen geben, ohne zu zappeln. */
.machine-check.is-flash {
  animation: check-flash 1.1s ease-out 1;
}
@keyframes check-flash {
  0%   { box-shadow: 0 0 0 0 rgba(76, 84, 63, 0.45); }
  100% { box-shadow: 0 0 0 10px rgba(76, 84, 63, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .machine-check.is-flash { animation: none; outline: 2px solid var(--sage-deep); outline-offset: 3px; }
}
.machine-check strong { font-weight: 700; }
.machine-check .chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}
.machine-check .chip {
  font-size: 0.8rem;
  font-weight: 700;
  padding: 6px 12px;
  border-radius: 999px;
  border: 1px solid transparent;
}
.machine-check .chip.yes { background: rgba(85, 102, 63, 0.14); color: var(--ok); }
.machine-check .chip.alt { background: rgba(169, 123, 64, 0.14); color: var(--honey-deep); }
.machine-check .chip.no { background: rgba(140, 74, 56, 0.13); color: var(--no); }
.machine-check .more {
  display: inline-block;
  margin-top: 10px;
  font-size: 0.85rem;
}

/* Varianten-Umschalter: ruhige Radio-Karten mit Haarlinie, keine App-Optik */
.variant-options {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin-top: 10px;
}
.variant-options [role="radio"] {
  font: inherit;
  text-align: left;
  cursor: pointer;
  background: var(--paper-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 12px;
  min-height: 44px;
  display: grid;
  gap: 2px;
  transition: border-color 0.18s ease, background-color 0.18s ease;
}
.variant-options [role="radio"]:hover { border-color: var(--ink-soft); }
.variant-options [role="radio"][aria-checked="true"] {
  border-color: var(--ink);
  background: var(--paper);
}
.variant-options [role="radio"]:focus-visible {
  outline: 2px solid var(--sage-deep);
  outline-offset: 2px;
}
.variant-options .v-name {
  display: flex;
  align-items: center;
  gap: 0.5em;
  font-weight: 600;
  font-size: 0.9rem;
}
.variant-options .v-name::before {
  content: "";
  width: 9px;
  height: 9px;
  border-radius: 50%;
  border: 1px solid var(--ink-soft);
  flex: none;
  transition: background-color 0.18s ease, border-color 0.18s ease;
}
.variant-options [aria-checked="true"] .v-name::before {
  background: var(--sage-deep);
  border-color: var(--sage-deep);
}
.variant-options .v-sub {
  font-size: 0.76rem;
  color: var(--ink-soft);
  line-height: 1.35;
}
@media (max-width: 420px) {
  .variant-options { grid-template-columns: 1fr; }
}

.cta-note {
  font-size: 0.78rem;
  color: var(--ink-soft);
  margin: -0.3rem 0 0.9rem;
  max-width: none;
}

/* ---------- Der Add-on-Chip „+ Dampflanze dazulegen" (Phase `dampflanze-addon`)
   Abgeleitet, nicht erfunden. Der Bestand hat zwei Chip-Muster, und nur eines
   passt: `.machine-check .chip` ist eine <span>-Statusanzeige mit Ampelfarbe, also
   ausdrücklich NICHT klickbar. Ein Bedienelement, das aussieht wie eine
   Statusanzeige, ist ein Klick, den niemand versucht. Das nächstliegende
   interaktive Pill-Muster der Seite ist `.variant-options [role="radio"]`, und von
   dort kommen Haarlinie, Radius, Papier-Fläche, Polsterung, Mindesthöhe 44 px,
   Übergangszeit und der Fokusring — Zeile für Zeile dieselben Werte.

   Zwei bewusste Unterschiede zum Radio-Muster, beide inhaltlich:
     * volle Breite statt Zwei-Spalten-Raster. Der Chip ist keine Wahl zwischen
       Alternativen, er ist ein Zusatz. Ein halbbreites Element neben einer Lücke
       läse sich als „hier fehlt die zweite Option".
     * kein Radio-Punkt vor dem Text. Das „+" steht in der Copy und sagt dasselbe,
       nur ohne ein Auswahl-Vokabular zu behaupten, das hier nicht gilt.

   Er ist absichtlich RUHIGER als der Haupt-Button: der ist gefüllt (.btn-solid),
   der Chip ist Umriss auf Papier. Auf dem Geld-Pfad soll genau ein Element das
   lauteste sein, und das ist der Kauf des Hauptprodukts.

   Was er NICHT selbst mitbringt, weil er es erbt: den Erfolgszustand (.is-added,
   inkl. „✓" und Salbei), das Dimmen während des Requests ([aria-busy="true"],
   greift global) und die Ansage (#cart-status). */
.addon {
  margin: -0.2rem 0 1.1rem;
}
.addon-chip {
  display: flex;
  align-items: center;
  gap: 0.4em;
  font: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  text-decoration: none;
  color: var(--ink);
  cursor: pointer;
  background: var(--paper-card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 10px 14px;
  min-height: 44px;
  transition: border-color 0.18s ease, background-color 0.18s ease;
}
.addon-chip:hover { border-color: var(--ink-soft); }
.addon-chip:focus-visible {
  outline: 2px solid var(--sage-deep);
  outline-offset: 2px;
}
/* Dazugelegt: der Chip behält den Zustand (app.js bestellt den Rücksprung-Timer
   ab) und nimmt sich zurück. Salbei-Fläche wie am Haupt-Button, aber ohne dessen
   .btn-Regel — die kommt hier eigens, weil der Chip kein .btn ist. Der Zeiger sagt
   als Erstes, was der Text bestätigt: hier ist nichts mehr zu klicken. */
.addon-chip[aria-disabled="true"] {
  background: var(--sage-deep);
  border-color: var(--sage-deep);
  color: var(--paper);
  cursor: default;
}
.addon-note {
  font-size: 0.78rem;
  color: var(--ink-soft);
  margin: 0.45rem 0 0;
  max-width: none;
}

.buy-cta { width: 100%; margin-bottom: 0.9rem; }
.buy-meta {
  list-style: none;
  font-size: 0.86rem;
  color: var(--ink-soft);
  display: grid;
  gap: 0.35em;
}
.buy-meta li { display: flex; gap: 0.6em; align-items: baseline; }
.buy-meta li::before {
  content: "";
  width: 5px; height: 5px;
  background: var(--sage);
  transform: rotate(45deg);
  flex: none;
}

/* Specs visuell */
.spec-band {
  background: var(--paper-deep);
  border-block: 1px solid var(--line-soft);
  margin-top: var(--gap-section);
  padding-block: clamp(52px, 7vw, 92px);
}
.spec-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 14px;
}
.spec-cell {
  background: var(--paper-card);
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  padding: 20px 20px 18px;
}
.spec-cell dt {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--sage-deep);
  margin-bottom: 0.45em;
}
.spec-cell dd {
  font-family: var(--font-display);
  font-size: 1.12rem;
  font-weight: 430;
  line-height: 1.3;
}
.spec-cell dd small {
  display: block;
  font-family: var(--font-body);
  font-size: 0.8rem;
  color: var(--ink-soft);
  font-weight: 400;
  margin-top: 0.35em;
  line-height: 1.45;
}

/* Infografik-Block auf der Produktseite */
.compat-figure img {
  width: 100%;
  max-width: 520px;
  border-radius: var(--radius);
  border: 1px solid var(--line-soft);
}

/* Pflege-Hinweis (ehrlich, ruhig) */
.care-note {
  border-left: 3px solid var(--honey);
  padding: 0.2em 0 0.2em 1.1em;
  font-size: 0.95rem;
  color: var(--ink-soft);
  max-width: var(--measure);
  margin-top: 1.2rem;
}

/* ============================================================
   Runde-3-Verfeinerung: Bewertungen, Verpackung, Menschen
   ============================================================ */

/* ---------- Bewertungs-Sektion (Homepage) ---------- */

.review-aggregate {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35em 0.9em;
  margin-bottom: clamp(28px, 4vw, 44px);
  padding-bottom: 1.3rem;
  border-bottom: 1px solid var(--line);
}
.review-aggregate .value {
  font-family: var(--font-display);
  font-size: clamp(2rem, 4vw, 2.9rem);
  font-weight: 380;
  line-height: 1;
}
.review-aggregate .stars { margin: 0; font-size: 1rem; }
.review-aggregate .label {
  font-size: 0.9rem;
  color: var(--ink-soft);
}
.review-aggregate .label strong { color: var(--ink); font-weight: 600; }
@media (max-width: 900px) {
  /* Der Aggregat-Text bricht als Ganzes um, nicht mitten im Satz. */
  .review-aggregate .label { flex-basis: 100%; }
}

/* B3 (Max, 2026-07-14): die Sektion wirkte unruhig. Grund war nicht die Optik der
   einzelnen Karte, sondern ihre Ausrichtung: unterschiedlich lange Zitate ließen
   jede Signatur auf einer anderen Höhe enden, und die Aggregat-Zeile brach mitten
   im Satz um. Jetzt: gleich hohe Zellen, Signaturen auf einer Linie unten,
   und der Aggregat-Text bricht unterhalb von 900 px sauber als Ganzes um. */
.review-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
  gap: clamp(22px, 3.5vw, 40px) clamp(24px, 4vw, 56px);
}
/* Grid-Zellen dehnen sich von Haus aus auf die Höhe IHRER Zeile. Zusammen mit
   `margin-top: auto` an der figcaption reicht das, um die Signaturen einer Zeile
   auf eine Linie zu legen. (Kein `grid-auto-rows: 1fr` — das macht ALLE Zeilen
   gleich hoch und bläht die einspaltige Mobil-Ansicht auf die höchste Karte auf.) */
.review {
  border-top: 1px solid var(--line);
  padding-top: 1.1rem;
  display: flex;
  flex-direction: column;
}
.review .stars { font-size: 0.8rem; margin-bottom: 0.5rem; }
.review blockquote {
  font-family: var(--font-display);
  font-weight: 400;
  font-style: italic;
  font-size: 1.08rem;
  line-height: 1.45;
  letter-spacing: 0;
  max-width: 30em;
}
.review figcaption {
  margin-top: auto;            /* Signaturen aller Karten einer Zeile auf einer Linie */
  padding-top: 0.7rem;
  font-size: 0.78rem;
  color: var(--ink-soft);
}
.review figcaption .name {
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink);
  font-size: 0.72rem;
}
.review .signature { display: block; }
/* „zu: <Produkt> · <Monat Jahr>" — steht nur da, wo reviews.json beides kennt.
   Über der Signatur, damit die Namen einer Zeile auf einer Linie enden. */
.review-meta {
  display: block;
  margin-bottom: 0.3rem;
  font-size: 0.76rem;
  color: var(--ink-soft);
}
.review-meta a {
  color: var(--sage-deep);
  text-decoration: underline;
  text-decoration-color: var(--line);
  text-underline-offset: 3px;
}
/* „übersetzt aus dem Deutschen, Original auf Etsy" (B11). Nur auf EN/FR/IT/ES und
   nur an Zitaten, die wirklich übersetzt sind.

   Er steht als ERSTE Zeile der figcaption, direkt unter dem Zitat, und bewusst
   nicht kleiner als der Rest der Bildunterschrift: der Hinweis ist die Bedingung,
   unter der die Übersetzung überhaupt zulässig ist. Ein Herkunfts-Hinweis, den man
   suchen muss, wäre eine Bewertungs-Irreführung mit Feigenblatt. Kursiv statt grau
   in grau, damit er sich von der Produkt-Zuordnung darunter unterscheidet, ohne
   lauter zu werden als das Zitat selbst. */
.review-origin {
  display: block;
  margin-bottom: 0.3rem;
  font-size: 0.76rem;
  font-style: italic;
  color: var(--ink-soft);
}
.review-note {
  margin-top: clamp(20px, 3vw, 32px);
  font-size: 0.82rem;
  color: var(--ink-soft);
  max-width: 46em;
}

/* ---------- Alle Bewertungen aufklappen (Max, 2026-07-28, Punkt 7) ----------

   Die Regel hängt an `.has-more`, und diese Klasse setzt AUSSCHLIESSLICH app.js.
   Ohne JavaScript greift hier also nichts, und alle Stimmen stehen offen da.
   Das ist Absicht und die richtige Richtung des Ausfalls: das Zuklappen ist die
   Bequemlichkeit, das Anzeigen ist der Inhalt. Fällt die Bequemlichkeit aus,
   bleibt der Inhalt. */
.review-grid.has-more .review-extra { display: none; }

.review-more-wrap {
  margin-top: clamp(22px, 3vw, 34px);
  text-align: center;
}
.review-more-wrap .btn { min-width: 16rem; }

/* ---------- Verpackungs-Duo (zwei Hochformate) ---------- */

.media-pair.duo {
  grid-template-columns: 1fr 1fr;
  align-items: start;
}
.media-pair.duo img { aspect-ratio: 3 / 4; }

/* ---------- Menschen hinter dem Studio ---------- */

.people-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(16px, 2.5vw, 28px);
  align-items: start;
}
.people-grid figure { margin: 0; }
.people-grid img {
  width: 100%;
  aspect-ratio: 3 / 4;
  object-fit: cover;
  border-radius: var(--radius);
}
.people-grid figcaption {
  font-size: 0.78rem;
  color: var(--ink-soft);
  padding-top: 10px;
  letter-spacing: 0.02em;
}
.people-note {
  margin-top: 1.6rem;
  font-size: 0.9rem;
  color: var(--ink-soft);
  max-width: 46em;
}
@media (max-width: 720px) {
  .people-grid { grid-template-columns: 1fr; }
  .people-grid img { aspect-ratio: 4 / 3.4; }
}

/* ---------- Kundenfotos: „So sieht's bei euch aus" (C4) ----------
   Sitzt IN der Bewertungs-Sektion, unter den Zitaten: das Foto eines Kunden ist
   derselbe Beweis wie seine Stimme. Es ist bewusst eine ruhige Reihe unter dem
   Zitat-Raster und keine zweite laute Sektion. Die dünne Linie oben trennt die
   beiden Ebenen, ohne einen Kasten zu bauen. */

.customer-band {
  margin-top: clamp(32px, 4.5vw, 56px);
  padding-top: clamp(24px, 3.5vw, 36px);
  border-top: 1px solid var(--line);
}
.customer-head { margin-bottom: 1.2rem; }
.customer-head h3 {
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 1.9vw, 1.4rem);
  font-weight: 430;
  margin-bottom: 0.3rem;
}
.customer-head p {
  font-size: 0.9rem;
  color: var(--ink-soft);
  max-width: 46em;
}
.customer-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(14px, 2vw, 22px);
  align-items: start;
  /* Bewusst schmaler als die Zitat-Reihe darüber: das Band stützt die
     Bewertungen, es tritt nicht gegen sie an. Über die volle Breite wären drei
     leere Quadrate eine Wand, und genau das soll ein Slot nicht sein. */
  max-width: 660px;
}
.customer-grid figure { margin: 0; }
.customer-grid img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--radius);
  display: block;
}
.customer-grid figcaption {
  font-size: 0.76rem;
  color: var(--ink-soft);
  padding-top: 8px;
  letter-spacing: 0.02em;
}
.customer-invite {
  margin-top: 1.2rem;
  font-size: 0.86rem;
  color: var(--ink-soft);
  max-width: 46em;
}
@media (max-width: 720px) {
  /* Auf Mobile bleiben es drei nebeneinander: sie sind hier Beleg, nicht Bühne,
     und drei gestapelte Quadrate wären ein sehr langer Weg zum nächsten Absatz. */
  .customer-grid { gap: 10px; }
  .customer-grid .ph-frame { padding: 10px 8px; gap: 0.4em; }
  .customer-grid .ph-frame .ph-title { font-size: 0.76rem; }
  .customer-grid .ph-frame .ph-sub { font-size: 0.5rem; letter-spacing: 0.1em; }
  .customer-grid .ph-frame .ph-icon { width: 20px; height: 20px; }
}

/* ---------- Instagram: eigene Bilder, kein Meta-Embed (C3) ----------
   Vier quadratische Kacheln im Raster der Feed-Optik, ohne fremden Chrome.
   Kein externer Request: die Bilder liegen auf dem eigenen Server, der
   Profil-Link navigiert erst auf Klick. */

.insta-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(12px, 1.8vw, 20px);
  align-items: start;
}
.insta-grid figure { margin: 0; }
.insta-grid img,
.insta-grid video {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--radius);
  display: block;
}
/* Die Video-Kachel (Drucker, Max 2026-07-17) liegt bewusst in DERSELBEN Regel wie die
   Bilder: sie ist eine Kachel wie die anderen und soll sich nicht anders verhalten.
   `object-fit: cover` schneidet hier nichts weg — die Datei ist 640x640, die Kachel
   1/1 (256 px, im Browser gemessen). Genau dafür wurde sie quadratisch geschnitten;
   der 9:16-Vorgänger hätte den Drucker zum Streifen gekürzt.

   Der Selektor braucht die zwei Klassen NICHT, anders als beim entfallenen
   `.drucker-media .drucker-video`: `.insta-grid video` (0,1,1) schlägt `.workshop-video`
   (0,1,0) über die Spezifität, unabhängig von der Reihenfolge im Stylesheet.

   Kein `cursor: pointer` und kein Zoom-Hover: es ist Tapete, es gibt nichts zu klicken
   (app.js nimmt `.workshop-video` die Controls, eine Tonspur hat die Datei nicht).
   Deshalb sitzt es auch NICHT im `.zoom-frame` der Bild-Kacheln — ein Zoom auf ein
   laufendes Video wäre Bewegung auf Bewegung. */
.insta-grid video { background: var(--paper-deep); }
.insta-grid figcaption {
  font-size: 0.76rem;
  color: var(--ink-soft);
  padding-top: 8px;
  letter-spacing: 0.02em;
}
.insta-cta { margin-top: clamp(22px, 3vw, 32px); }
.insta-note {
  margin-top: 1.1rem;
  font-size: 0.82rem;
  color: var(--ink-soft);
  max-width: 46em;
}
@media (max-width: 720px) {
  .insta-grid { grid-template-columns: repeat(2, 1fr); }
}

/* ---------- Produktnahe Zitate an der Buy-Box (PDP) ---------- */

.buy-reviews {
  margin-top: 1.6rem;
  border-top: 1px solid var(--line);
  padding-top: 1.2rem;
}
.buy-reviews .heading {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--sage-deep);
  margin-bottom: 0.9rem;
}
.buy-reviews .review { border-top: 0; padding-top: 0; }
.buy-reviews .review + .review { margin-top: 1rem; }
.buy-reviews .review blockquote { font-size: 0.98rem; }
.buy-reviews .aggregate {
  margin-top: 1.1rem;
  font-size: 0.82rem;
  color: var(--ink-soft);
}
.buy-reviews .aggregate strong { color: var(--ink); font-weight: 600; }

/* Zitate links unter der Galerie (Spalten-Balance).
   Max, 2026-07-28 (Punkt 4): „Aus den Bewertungen" ist zu klein, es soll
   DIESELBE Schriftgröße haben wie die Benefits-Sektion daneben.

   Am 2026-07-28 live gemessen, statt geschätzt:
     .benefit-list li            15,52 px   (0,97rem)
     .pdp-reviews blockquote     14,72 px   (0,92rem)  ← der Rückstand
     .pdp-reviews .heading       11,20 px   (0,70rem)
     .pdp-reviews figcaption     11,52 px   (0,72rem)

   Das Zitat geht deshalb exakt auf 0,97rem, den gemessenen Wert der
   Benefit-Zeile. Die kleine Kompakt-Variante der Sterne fällt weg: sie war der
   Grund, dass der Block insgesamt geschrumpft wirkte, und sie hat mit der
   Benefits-Größe nichts mehr zu tun, sobald das Zitat sie erreicht.
   Zeilenhöhe zurück auf den Wert der Haupt-Bewertungen (1,45), sonst steht die
   größere Schrift in einem Zeilenabstand, der für die kleinere gerechnet war. */
.pdp-reviews {
  margin-top: 0;
  padding-top: 14px;
}
.pdp-reviews .heading { margin-bottom: 0.7rem; }
.pdp-reviews .review blockquote { font-size: 0.97rem; line-height: 1.45; }

/* ---------- Pfeil-Navigation (Punkt 5, geteilt mit dem Warenkorb) ----------
   Optik nach Max' Vorlage: weißer Kreis, dunkles Chevron. Die Regeln stehen
   HIER und nicht in arrows.js, weil die Storefront ihre Farben aus den Tokens
   zieht; das mu-Plugin bringt für den Warenkorb dieselben Werte als eigene
   Kopie mit (dieselbe Trennung wie bei den Schriften).

   `.has-arrows` setzt arrows.js selbst — ohne JS entsteht kein Knopf und es
   bleibt auch keine leere Fläche für einen zurück. */
.s48n-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  /* 44 px ist die kleinste Fläche, die auf dem Handy zuverlässig zu treffen
     ist. Der sichtbare Kreis darf kleiner wirken, das Ziel darf es nicht. */
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid rgba(44, 38, 31, 0.10);
  border-radius: 999px;
  background: #fff;
  color: var(--ink);
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(44, 38, 31, 0.16);
  transition: background-color 140ms ease, box-shadow 140ms ease, opacity 140ms ease;
}
.s48n-arrow svg { width: 20px; height: 20px; }
.s48n-arrow--prev { left: 12px; }
.s48n-arrow--next { right: 12px; }
.s48n-arrow--next svg { transform: rotate(180deg); }
.s48n-arrow:hover { background: var(--paper-card, #fbf7ee); }
.s48n-arrow:focus-visible {
  outline: 2px solid var(--sage-deep);
  outline-offset: 2px;
}
/* Am Rand der Reihe: sichtbar gedämpft, aber weiter anfahrbar (aria-disabled
   statt disabled, siehe arrows.js). Ein Knopf, der ganz verschwindet, lässt die
   Bühne bei jedem Bildwechsel springen. */
.s48n-arrow[aria-disabled="true"] {
  opacity: 0.35;
  cursor: default;
  box-shadow: none;
}
/* Die Bühne trägt die Knöpfe, sie braucht dafür einen Bezugsrahmen. */
.gallery .stage { position: relative; }
.gallery .stage:focus-visible {
  outline: 2px solid var(--sage-deep);
  outline-offset: 3px;
}
@media (max-width: 520px) {
  .s48n-arrow { width: 40px; height: 40px; }
  .s48n-arrow--prev { left: 8px; }
  .s48n-arrow--next { right: 8px; }
}
@media (prefers-reduced-motion: reduce) {
  .s48n-arrow { transition: none; }
}
.pdp-reviews .review figcaption { margin-top: 0.4rem; font-size: 0.72rem; }
.pdp-reviews .review + .review { margin-top: 0.8rem; }
.pdp-reviews .aggregate { margin-top: 0.9rem; font-size: 0.8rem; }

/* ---------- Erklärter Begriff: Tooltip an „Star Seller" (C5) ----------
   Auslöser ist ein echter <button> (Tastatur + Screenreader + Touch), nicht ein
   :hover-only-Span. Sichtbar wird die Blase per Hover, per Fokus UND per Tap
   (app.js setzt .is-open). Ohne JS bleiben Hover und Tastatur-Fokus. */

.tip { position: relative; display: inline-block; }
.tip-trigger {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: help;
  text-decoration: underline dashed 1px;
  text-underline-offset: 4px;
  /* Größeres Tap-Ziel, ohne die Textzeile auseinanderzuziehen: das Padding
     vergrößert die Trefferfläche, der negative Margin nimmt es im Layout zurück. */
  padding: 10px 4px;
  margin: -10px -4px;
}
.tip-trigger:focus-visible {
  outline: 2px solid var(--sage-deep);
  outline-offset: 3px;
  border-radius: 3px;
}
.tip-bubble {
  position: absolute;
  bottom: calc(100% + 10px);
  left: 50%;
  z-index: 60;
  width: min(280px, 78vw);
  transform: translate(-50%, 6px);
  padding: 12px 14px;
  border-radius: 8px;
  border: 1px solid var(--line-soft);
  background: var(--paper-card);
  color: var(--ink);
  box-shadow: 0 10px 25px rgba(44, 38, 31, 0.16);
  font-family: var(--font-body);
  font-size: 0.82rem;
  font-style: normal;
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: 0;
  text-align: left;
  text-transform: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.tip-bubble::after {          /* Pfeil zur Fußnote */
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  margin-left: -6px;
  border: 6px solid transparent;
  border-top-color: var(--paper-card);
}
.tip:hover .tip-bubble,
.tip-trigger:focus-visible + .tip-bubble,
.tip-bubble.is-open {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
}
/* Schmale Viewports: die Blase darf an keinem Rand hinausragen. Bezugsrahmen ist
   deshalb nicht mehr der schmale Trigger (an ihm ausgerichtet läge die 280-px-Blase
   je nach Zeilenumbruch halb außerhalb), sondern der Block, in dem er steht — die
   Blase spannt dessen volle Breite. `.tip` wird dafür static, damit der Block das
   Positionierungs-Elternteil ist. */
@media (max-width: 620px) {
  .tip { position: static; }
  .hero-rating,
  .review-aggregate { position: relative; }
  .tip-bubble {
    left: 0;
    right: 0;
    width: auto;
    transform: translate(0, 6px);
  }
  .tip-bubble::after { left: auto; right: 28px; margin-left: 0; }
  .tip:hover .tip-bubble,
  .tip-trigger:focus-visible + .tip-bubble,
  .tip-bubble.is-open { transform: translate(0, 0); }
}

/* ---------- Beschrifteter Foto-Platzhalter (kein Fake-Bild) ----------
   EIN Muster für alle Asset-Arten: Werkstatt-Foto (.ph-slot), Produkt-Video
   (.ph-video), Kundenfoto und Instagram-Beitrag (.ph-square). Der Slot hat
   immer die Fläche und die Rundung des Bildes, das später hineinkommt: er liest
   als gesetzte Kachel, nicht als Loch (Max, FEEDBACK-R3, Entscheidung 4). */

.ph-frame {
  border: 1px dashed var(--line);
  border-radius: calc(var(--radius) - 4px);
  background: var(--paper-deep);
  aspect-ratio: 1 / 1;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: 0.5em;
  text-align: center;
  padding: 14px 12px;
  margin: 0;
}
/* Bild-Slot in einer Bild-Komposition: gleiche Rundung und gleiche Fläche wie ein
   Foto daneben, damit er als gesetzte Kachel liest und nicht als Fehler. */
.ph-frame.ph-slot {
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  padding: 18px 16px;
  gap: 0.6em;
}
/* Video-Slot: exakt die Fläche, die das Video später einnimmt (16:9 im
   Video-Split), damit die Sektion beim Nachliefern nicht springt. */
.ph-frame.ph-video {
  aspect-ratio: 16 / 9;
  border-radius: var(--radius);
  padding: 18px 16px;
  gap: 0.6em;
}
/* Quadratischer Slot: Kundenfoto-Band und Instagram-Galerie. */
.ph-frame.ph-square {
  aspect-ratio: 1 / 1;
  border-radius: var(--radius);
  padding: 16px 14px;
  gap: 0.55em;
}
.ph-frame .ph-icon {
  width: 26px;
  height: 26px;
  color: var(--sage);
  opacity: 0.75;
}
.ph-frame .ph-title {
  font-family: var(--font-display);
  font-weight: 430;
  font-size: 0.92rem;
  line-height: 1.3;
  text-wrap: balance;
  max-width: 22ch;
}
.ph-frame .ph-sub {
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* ---------- Werkstatt-Video: Split aus Video + kompakter Kauf-Box ----------
   Wiederverwendbares Muster für weitere Produkt-Videos (.video-split). */

.video-section { padding-top: clamp(40px, 6vw, 72px); }
.video-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 72px);
  align-items: center;
}
.video-split-media { margin: 0; }
.workshop-video {
  display: block;
  width: 100%;
  border-radius: var(--radius);
  background: var(--paper-deep);
}
.video-split-media figcaption {
  font-size: 0.78rem;
  color: var(--ink-soft);
  padding-top: 10px;
  letter-spacing: 0.02em;
}
.video-split-buy .kicker { margin-bottom: 0.7rem; }
.video-split-title {
  font-size: clamp(1.5rem, 2.6vw, 2rem);
  margin-bottom: 0.3rem;
}
.video-split-buy .hero-rating { margin: 0.3rem 0 0.3rem; }
/* ZWILLING von `.buy-col .price-line` (Kauf-Spalte der PDP) — wer eine ändert,
   ändert bitte beide. Genau die getrennte Pflege hat den Ausrichtungs-Fehler aus
   Punkt 2 hier überleben lassen: gemeldet hatte Max ihn nur auf der PDP, gesessen
   hat er auch im Video-Block der Startseite (gemessen 14,30 px zu tief).
   Begründung für `center` steht bei der PDP-Regel. */
.video-split-buy .price-line {
  display: flex;
  align-items: center;
  gap: 0.8em;
  margin-block: 0.4rem 0.9rem;
}
.video-split-buy .price-line .price {
  font-family: var(--font-display);
  font-size: 1.45rem;
  font-weight: 420;
  white-space: nowrap;
  flex-shrink: 0;
}
.video-split-buy .price-line small { font-size: 0.78rem; color: var(--ink-soft); }
.video-split-buy .benefit-list {
  margin-block: 1rem 1.3rem;
  padding-block: 0.9rem;
}
.video-split-more {
  margin-top: 0.9rem;
  font-size: 0.88rem;
}
.video-split-more a {
  color: var(--sage-deep);
  text-underline-offset: 3px;
}
.video-split-more a:hover { color: var(--ink); }
@media (max-width: 860px) {
  .video-split { grid-template-columns: 1fr; gap: 24px; }
}

/* ---------- Motion-Schicht (additiv, reduced-motion-gated) ----------
   .will-reveal wird NUR per JS zur Laufzeit gesetzt (nie opacity:0 als
   CSS-Default), Inhalt bleibt ohne JS vollständig sichtbar. */

.will-reveal { opacity: 0; transform: translateY(22px); }
.will-reveal.is-in {
  opacity: 1;
  transform: none;
  transition: opacity 0.7s ease, transform 0.75s cubic-bezier(0.22, 0.61, 0.2, 1);
}
@media (prefers-reduced-motion: reduce) {
  .will-reveal { opacity: 1; transform: none; }
}
@media print {
  .will-reveal { opacity: 1 !important; transform: none !important; }
}

/* Sanfte Hover-Momente: konsistenter Bild-Zoom auf Karten UND Sektions-Fotos.
   Clip immer am Rahmen (overflow hidden), Zoom nur ohne reduced-motion. */

.media-pair figure,
.zoom-frame { overflow: hidden; border-radius: var(--radius); }

@media (prefers-reduced-motion: no-preference) {
  .product-card img,
  .media-pair img,
  .zoom-frame img,
  .hero-stage .main img:not(.has-parallax) {
    transition: transform 0.7s cubic-bezier(0.2, 0.6, 0.2, 1);
  }
  .product-card:hover img { transform: scale(1.04); }
  .media-pair figure:hover img { transform: scale(1.04); }
  .people-grid figure:hover .zoom-frame img,
  .customer-grid figure:hover .zoom-frame img,
  .insta-grid figure:hover .zoom-frame img { transform: scale(1.04); }
  /* Hero: Hover-Zoom nur, solange die JS-Parallax nicht die transform hält. */
  .hero-stage .main:hover img:not(.has-parallax) { transform: scale(1.04); }
}

.site-footer a,
.crumbs a,
.compat-note a,
.compat-matrix tbody th a,
.machine-check .more,
.faq-body a,
.hero-rating a,
.section-head .lede a,
.trust-grid a,
.people-note a,
.buy-reviews a {
  transition: color 0.2s ease;
  text-underline-offset: 3px;
}
.compat-note a:hover,
.compat-matrix tbody th a:hover,
.machine-check .more:hover,
.faq-body a:hover,
.hero-rating a:hover,
.section-head .lede a:hover,
.trust-grid a:hover,
.people-note a:hover,
.buy-reviews a:hover { color: var(--sage-deep); }

/* Parallax-Bild bewegt sich nur via JS-transform, Rahmen clippt. */
.hero-stage .main img[data-parallax] { will-change: transform; }

/* ---------- FAQ (JS-frei via <details>) ---------- */

.faq-list {
  max-width: 46em;
  border-top: 1px solid var(--line);
}
.faq-item { border-bottom: 1px solid var(--line); }
.faq-item summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1em;
  padding: 1.05rem 2px;
  min-height: 44px;
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 440;
  line-height: 1.3;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: "+";
  flex: none;
  font-family: var(--font-display);
  font-size: 1.3rem;
  font-weight: 400;
  color: var(--sage-deep);
  line-height: 1;
  transition: transform 0.25s ease;
}
.faq-item[open] summary::after { transform: rotate(45deg); }
.faq-item summary:hover { color: var(--sage-deep); }
.faq-body {
  padding: 0 2px 1.2rem;
  font-size: 0.95rem;
  color: var(--ink-soft);
}
.faq-body p { max-width: 40em; }
.faq-body a { color: var(--sage-deep); }

/* ---------- Abschluss-Band vor dem Footer ---------- */

.section.closing { padding-bottom: 0; }
.closing-inner {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 4fr);
  gap: clamp(28px, 5vw, 72px);
  align-items: center;
  max-width: 62em;
}
.closing-copy { text-align: center; }
.closing .kicker { justify-content: center; }
.closing .kicker::after {
  content: "";
  width: 2rem;
  height: 1px;
  background: var(--sage-deep);
  opacity: 0.55;
  flex: none;
}
.closing .display-l { margin-bottom: 1.6rem; }
.closing-ctas {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
}
.closing-photo {
  margin: 0;
  border-radius: var(--radius);
  overflow: hidden;
}
.closing-photo img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  height: auto;
  object-fit: cover;
  object-position: center 62%;
}
.section.closing { padding-bottom: var(--gap-section); }
@media (max-width: 760px) {
  .closing-inner { grid-template-columns: 1fr; }
  .closing-photo { max-width: 420px; margin-inline: auto; width: 100%; }
}
