    :root {
      --primary: #000;
      --secondary: #2f5aae;
      --bg: #ffffff;
      --light: #f4f5f7;
      --border: #e8e8e8;
      --text: #272626;
      --muted: #888;
    }

    * { box-sizing: border-box; }

    body {
      margin: 0;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
      color: var(--text);
      background: var(--bg);
      font-size: 16px;
      line-height: 1.6;
    }

    header.topbar {
      /* FIXED statt sticky (2026-07-07): das Ein-Zeilen-Dock ändert die
         Header-Höhe um ~75px — als sticky änderte das die DOKUMENT-Höhe,
         Chromes Scroll-Anchoring korrigierte die Scroll-Position zurück über
         die Schwelle → Flatter-Loop (headless nachgewiesen: scrollTo(180)
         landete bei y=105). Fixed + konstantes body-padding-top (JS misst
         die Expanded-Höhe, s. initScrollCollapse) hält den Dokumentfluss
         beim Docken unverändert: kein Anchor-Feedback, kein Inhaltssprung —
         es wird nur mehr Inhalt unter der kürzeren Leiste sichtbar. */
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      z-index: 40;
      background: var(--bg);
      /* Scroll-Collapse (Schritt 3): vertikales Padding schrumpft mit --sp
         (Scroll-Fortschritt 0→1, per JS gesetzt). */
      padding: calc(1.1rem - var(--sp, 0) * 0.5rem) 2rem;
      border-bottom: 1px solid var(--border);
      display: flex;
      align-items: center;
      /* Zentriert Brand + Suchfeld als Gruppe (und die flex-basis-100%-
         Zeilen darunter greifen dasselbe über ihre max-width). Seit der
         Embed-Sprach-Switcher weg ist, stand rechts nur Leerraum — das
         Suchfeld gehört in die Mitte (2026-07-05). */
      justify-content: center;
      /* Getrennte Achsen: 2rem HORIZONTAL (Brand ↔ Suchfeld auf einer Zeile),
         aber nur 0.5rem VERTIKAL zwischen den umgebrochenen Voll-Breite-Zeilen
         (Suchfeld → „Zuletzt gesucht" → „Probier mal"). Als Kurzform `2rem`
         schlug der 2rem-Row-Gap voll durch → sichtbare Leerraum-Kaskade im
         Leerzustand (User 2026-07-06). */
      gap: 0.5rem 2rem;
      flex-wrap: wrap;
    }

    header.topbar .brand {
      position: relative;
      /* Eigener Stacking-Kontext: die gelbe Pille (::before, z-index -1)
         bleibt so IN der Marke statt hinter den Topbar-Hintergrund zu fallen. */
      z-index: 0;
      display: inline-block;
      /* Audit-Fund 2026-07-10: hier stand `font: 700 1.25rem var(--wordmark)`,
         aber --wordmark war NIE definiert — die Shorthand war komplett invalid,
         die Marke rendert seit jeher mit Erbwerten (400/16.8px). Gemessen
         prod vs. dev: explizite 700 machte sie sichtbar fetter → tote Zeile
         ersatzlos gestrichen, gelebte Optik eingefroren. Fett = 1 Zeile hier. */
      /* Unsichtbar vergrößerte Tap-Fläche (die Marke ist jetzt ein Link):
         Padding rein, per negativem Margin kompensiert — die Text-Position
         im Layout bleibt unverändert. Horizontal bewusst mehr als vertikal
         (Kapselenden fressen optisch Innenraum; User 2026-07-07 „mehr
         Padding in der Pille"). */
      margin: -8px -4px -8px -10px;
      padding: 8px 10px;
      letter-spacing: -0.01em;
      white-space: nowrap;
      color: inherit;
      text-decoration: none;
      cursor: pointer;
    }
    /* L-Morph (User 2026-07-07 abends, ersetzt den LWN-Morph): „EBENDIGE
       WEINE" (.brand-sq, EIN Segment) squeezt beim Scrollen per max-width/
       opacity komplett weg — übrig bleibt NUR das L (.brand-seg) im gelben
       Kreis. */
    /* An der BOX-Unterkante ausrichten, nicht an der Baseline: sobald ein
       Segment beim Scrollen overflow:hidden bekommt, wandert dessen Baseline
       auf die Unterkante — bottom-Alignment ist davon unberührt, alle Lettern
       bleiben auf einer Linie (User-Bug 2026-07-06). */
    .brand-seg, .brand-sq { display: inline-block; vertical-align: bottom; }
    /* Squeeze NUR im gescrollten Zustand — sonst verschöbe overflow:hidden
       die Grundlinie im Ruhezustand. 175px = Vollbreite „EBENDIGE WEINE"
       (großzügig über 1.25rem-Messung). */
    html.lwn-scrolled .brand-sq {
      overflow: hidden;
      max-width: calc((1 - min(1, var(--sp,0) * 1.7)) * 175px);
      opacity: clamp(0, calc(1 - var(--sp,0) * 1.65), 1);
    }
    /* Gelber KREIS ums L (statt der LWN-Pille): fixe 38px, auf die
       Link-Box zentriert — die ist im Dock nur noch L + Padding, der Kreis
       fadet erst ab sp 0.6 ein, wenn der Squeeze (fertig ~0.59) durch ist,
       und sitzt dann exakt ums L. #FFD700 = Lebendige-Weine-Gelb
       (Customizer, .homepage-first-yellow-block). */
    header.topbar .brand::before {
      content: '';
      position: absolute;
      width: 38px;
      height: 38px;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      border-radius: 50%;
      background: #FFD700;
      opacity: clamp(0, calc((var(--sp,0) - 0.6) * 3.4), 1);
      z-index: -1;
    }
    /* (Geparkte Option: „Auge"-Punkt am gescrollten L — Snippet in
       custom-search/L-DOT-OPTION.md, Vorschau 44223ec4.lwn-search.pages.dev.
       Bewusst NICHT im Prod-Stand.) */
    header.topbar .brand-row {
      display: flex;
      align-items: center;
      gap: 0.9rem;
      min-width: 0;
    }
    /* ── Ein-Zeilen-Dock (User 2026-07-07): ab Scroll ≥ THRESH (Morph fertig,
       sp=1) bilden gedrehtes L + Suchfeld + Schließen-× EINE schmale Zeile.
       JS toggelt html.lwn-collapsed mit Hysterese (an ≥130, aus <105), damit
       der Reflow an der Kante nicht flackert. Zweitrangiges (Sprachwahl,
       Korrektur-Note, Eingrenzen-/Discovery-Zeilen) verschwindet aus der
       Leiste — oben ist alles wieder da. */
    html.lwn-collapsed header.topbar {
      flex-wrap: nowrap;
      align-items: center;
      gap: 0.7rem;
      padding-top: 0.45rem;
      padding-bottom: 0.45rem;
      /* Fester linker Rand für den L-Kreis im Dock (Gegenstück zum
         margin-right der brand-row unten). Überschreibt das jeweilige
         Basis-padding (Desktop 2rem, Mobil 1rem, Embed 64px), damit der Kreis
         in JEDEM Dock-Fall gleich weit vom Panel-Rand sitzt. */
      padding-left: 27px;
    }
    /* width:auto schlägt die Mobile-Regel „brand-row { width:100% }" (die ist
       fürs expandierte Layout Brand↔Sprachwahl) — sonst frisst die Row die
       ganze Dock-Zeile und das Suchfeld schrumpft auf 0. */
    html.lwn-collapsed header.topbar .brand-row { flex: 0 0 auto; width: auto; gap: 0; margin-right: 0.58rem; }
    /* Gleicher Abstand LINKS UND RECHTS vom L-Kreis in ALLEN Dock-Fällen —
       Standalone (Desktop/Mobil) UND Embed (Desktop/Mobil). Die Marke ist
       entgegen der früheren Annahme AUCH im Desktop-Embed sichtbar: die Basis-
       Regel „header.topbar .brand" (2 Klassen) ist spezifischer als
       „.embed-mode .brand { display:none }" (1 Klasse) → das display:none
       greift nie. Links kam bisher aus dem jeweiligen Topbar-padding-left
       (2rem/1rem/64px → Kreis 17/1/49px vom Rand), rechts aus gap+margin
       (~10px) — asymmetrisch und fallabhängig. Fix: FESTES padding-left = 27px
       (→ Kreis 12px vom Rand, weil der 38px-Kreis 15px über die schmale L-Box
       hinausragt) + margin-right 0.58rem (→ ebenfalls 12px rechts).
       User 2026-07-08. */
    html.lwn-collapsed #searchbox { flex: 1 1 auto; min-height: 0; max-width: none; }
    html.lwn-collapsed #searchbox .ais-SearchBox-form { height: 42px; }
    /* 16px halten (iOS-Zoom-Falle beginnt UNTER 16px) */
    html.lwn-collapsed #searchbox .ais-SearchBox-input { font-size: 16px; border-radius: 10px; }
    html.lwn-collapsed #locale-switcher,
    html.lwn-collapsed #correction-note,
    html.lwn-collapsed #suggestions-row,
    html.lwn-collapsed #recent-row,
    html.lwn-collapsed #examples-row { display: none !important; }
    /* Embed-Modus (?embed=1): die Suche läuft als Overlay-iframe im Shop.
       Eigenes Branding raus (der Shop steht drunter), Schließen-× rein —
       fix oben rechts im Panel (position:fixed = iframe-Viewport), damit es
       auch beim Scrollen erreichbar bleibt. Der Header bekommt rechts
       Padding, damit die Eingrenzen-Chips nicht drunter rutschen. */
    #embed-close { display: none; }
    .embed-mode .brand { display: none; }
    /* Symmetrisch: das ×-Freihalte-Padding rechts bekommt ein Pendant links,
       sonst hinge das zentrierte Suchfeld sichtbar aus der Panel-Mitte. */
    .embed-mode header.topbar { padding-right: 64px; padding-left: 64px; }
    /* Im Embed führt die Shop-Sprache (fest verdrahtet via ?lang=) — ein
       eigener Sprach-Switcher wäre eine zweite, konkurrierende Sprachquelle.
       Sprache wechselt man im Shop; die Suche folgt beim nächsten Öffnen. */
    .embed-mode #locale-switcher { display: none; }
    .embed-mode #embed-close {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      position: fixed;
      top: 12px;
      right: 14px;
      z-index: 1200;
      width: 36px;
      height: 36px;
      border: 1px solid #d5d5d5;
      border-radius: 50%;
      background: #fff;
      color: #444;
      font-size: 1.35rem;
      line-height: 1;
      cursor: pointer;
      box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
    }
    .embed-mode #embed-close:hover { border-color: #9aa0b4; color: #000; }
    #locale-switcher { display: inline-flex; gap: 2px; }
    #locale-switcher button {
      border: 1px solid #e0e1e8; background: #fff; color: var(--muted, #6b7280);
      font-size: 0.7rem; font-weight: 600; text-transform: uppercase; letter-spacing: .03em;
      padding: 2px 7px; border-radius: 5px; cursor: pointer; line-height: 1.4;
    }
    #locale-switcher button:hover { border-color: #9aa0b4; }
    #locale-switcher button[aria-pressed="true"] {
      background: #eef6ff; border-color: #1f4788; color: #1f4788;
    }
    /* Results heading: the understood / translated interpretation of the query —
       das „Ich hab dich verstanden"-Aha-Signal. Das Suchfeld behält die
       Original-Eingabe; dies ist jetzt eine H2 (nicht die Seiten-H1). */
    #results-heading {
      font-size: 1.06rem;
      font-weight: 650;
      margin: 0 0 0.55rem;
      letter-spacing: -0.01em;
      color: var(--ink, #111);
      line-height: 1.3;
    }
    /* ✨-Präfix rahmt die H2 als „verstanden: …" (das ehemalige Banner-Signal
       zieht hierher um). Dekorativ; die Groß-Schreibung des ersten Buchstabens
       macht jetzt JS (locale-sicher). */
    #results-heading::before { content: "✨\00a0"; font-weight: 400; }
    #results-heading.hidden { display: none; }
    #searchbox {
      /* flex-basis 340px: passt das Suchfeld nicht mehr in voller Breite neben
         die Brand-Zeile, bricht es auf eine EIGENE volle Zeile um, statt neben
         dem Titel zusammenzuschrumpfen (Mobile-Bug 2026-07-03). */
      flex: 1 1 340px;
      min-width: 0;
      max-width: 720px;
      /* Platz fürs Input reservieren, BEVOR InstantSearch es erzeugt —
         sonst springt der ganze Header nach (größter Einzel-CLS-Beitrag). */
      min-height: 58px;
    }
    /* Höheres, prominenteres Feld (2026-07-05): die Höhe sitzt beim
       Satellite-Theme auf der FORM (2.5rem) — vertikales Input-Padding
       allein bewirkt nichts. Das Reset-× zentriert sich selbst (top:50%). */
    #searchbox .ais-SearchBox-form { height: 58px; transition: height .18s ease; }
    #searchbox .ais-SearchBox-input {
      width: 100%;
      /* Schrift 17px: Mobile braucht ≥16px (iOS-Zoom-Falle) */
      padding: 0 48px 0 44px;
      font-size: 17px;
      border: 2px solid var(--border);
      border-radius: 12px;
      background: #fff;
      transition: all .2s;
    }
    #searchbox .ais-SearchBox-input:focus {
      outline: none;
      border-color: var(--primary);
      box-shadow: 0 4px 12px rgba(0,0,0,.1);
    }
    /* Feld-leeren ≠ Overlay-schließen (User 2026-07-07): das Reset im Feld
       bekommt den iOS-Clear-Glyph (weißes × auf gefülltem grauem Kreis, ⊗) —
       universell als „Eingabe löschen" gelernt. Das große freie × im weißen
       Kreis-Button daneben bleibt allein dem Schließen des Overlays.
       Das InstantSearch-eigene SVG wird versteckt, der Glyph kommt als
       Hintergrundbild auf den Button selbst. */
    /* Lupe ↔ ⊗ im selben Slot (Port aus dem Listing, User 2026-07-11
       „das ist smart"): im Leerzustand zeigt LINKS eine dezente Lupe, bei
       Eingabe übernimmt das ⊗ exakt diesen Platz (Reset von rechts nach
       links verlegt; rechts bleibt der Loading-Indicator). Der 44-px-
       Innenabstand links war ohnehin reserviert; Zustand = die in
       queryHook gepflegte Klasse html.lwn-has-query — kein neues JS. */
    #searchbox .ais-SearchBox-form::before {
      content: '';
      position: absolute;
      left: 14px;
      top: 50%;
      /* KEIN translateY(-50%): das Satellite-Theme zentriert sein
         form::before bereits per negativem margin-top — beides zusammen
         schob die Lupe ~9 px zu hoch (User 2026-07-11). Deterministisch:
         margin-top -10px (halbe Box), transform explizit aus. */
      margin-top: -10px;
      transform: none;
      width: 20px;
      height: 20px;
      background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M10.5 3.5a7 7 0 1 0 0 14 7 7 0 0 0 0-14Zm10 17-4.6-4.6'/%3E%3C/svg%3E") center / 18px 18px no-repeat;
      pointer-events: none;
    }
    html.lwn-has-query #searchbox .ais-SearchBox-form::before { display: none; }
    #searchbox .ais-SearchBox-reset svg { display: none; }
    #searchbox .ais-SearchBox-reset {
      left: 14px;
      right: auto;
      width: 20px;
      height: 20px;
      background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Ccircle cx='10' cy='10' r='10' fill='%23c2c5cf'/%3E%3Cpath d='M6.7 6.7 L13.3 13.3 M13.3 6.7 L6.7 13.3' stroke='%23fff' stroke-width='1.9' stroke-linecap='round'/%3E%3C/svg%3E") center / 18px 18px no-repeat;
      border: none;
    }
    #searchbox .ais-SearchBox-reset:hover,
    #searchbox .ais-SearchBox-reset:focus-visible {
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Ccircle cx='10' cy='10' r='10' fill='%239aa0b4'/%3E%3Cpath d='M6.7 6.7 L13.3 13.3 M13.3 6.7 L6.7 13.3' stroke='%23fff' stroke-width='1.9' stroke-linecap='round'/%3E%3C/svg%3E");
    }
    /* Autocorrect-style note shown right below the searchbox when the LLM
       rewrote the user's query. Click to revert to the original text. */
    #correction-note {
      /* min-width 100% statt max-width 720px: der Flex-Zeilenumbruch rechnet
         mit der an max-width GEKLEMMTEN Basisgröße — mit 720px passte die
         Zeile bei breitem Viewport NEBEN das Suchfeld statt darunter.
         Inhalt mittig, passend zum zentrierten Suchfeld (2026-07-05). */
      flex-basis: 100%;
      min-width: 100%;
      text-align: center;
      margin-top: 6px;
      padding: 0 4px;
      font-size: 0.8rem;
      color: var(--muted);
      line-height: 1.4;
    }
    #correction-note.hidden { display: none; }
    #correction-note .revert-link {
      color: var(--primary);
      text-decoration: none;
      font-weight: 500;
      border-bottom: 1px dashed currentColor;
      cursor: pointer;
    }
    #correction-note .revert-link:hover {
      border-bottom-style: solid;
    }
    #correction-note .original-text {
      font-style: italic;
      color: #444;
    }

    /* Suggestion-chip row under the searchbox. Shows concrete expansion
       terms (e.g. Sushi / Ramen / Curry) for food/cuisine queries — click
       a chip to replace the query with that narrower one.
       #examples-row (Discovery, 2026-07-05) teilt sich das komplette
       Styling: klickbare BEISPIEL-Queries im Leerzustand, die zeigen, dass
       „Riesling zu Hühnchen unter 30 €" hier eine gültige Suche ist. */
    #suggestions-row, #examples-row {
      /* min-width 100%: eigene Zeile erzwingen (s. #correction-note) */
      flex-basis: 100%;
      min-width: 100%;
      margin-top: 4px;
      padding: 0 4px;
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      justify-content: center;
      gap: 6px;
      font-size: 0.8rem;
      color: var(--muted);
      line-height: 1.4;
    }
    #suggestions-row.hidden, #examples-row.hidden { display: none; }
    #suggestions-row .suggestions-label, #examples-row .suggestions-label {
      color: var(--muted);
      font-size: 0.78rem;
      margin-right: 2px;
    }
    #suggestions-row .suggestion-chip, #examples-row .suggestion-chip {
      display: inline-flex;
      align-items: center;
      padding: 3px 10px;
      /* Deutlich sichtbarer Vorschlags-Chip: Violett-Akzent (Palette #5b21b6),
         bewusst NICHT das Blau der „Verstanden als"-Chips — die creme-Variante
         war auf Weiß kaum wahrnehmbar (User-Feedback 2026-07-05). */
      background: #efe3fb;
      border: 1px solid #cdb4f2;
      border-radius: 999px;
      color: #5b21b6;
      cursor: pointer;
      font-size: 0.78rem;
      font-weight: 500;
      transition: background 0.15s, border-color 0.15s, color 0.15s;
    }
    #suggestions-row .suggestion-chip:hover, #examples-row .suggestion-chip:hover {
      background: #5b21b6;
      border-color: #5b21b6;
      color: #fff;
    }
    /* Die „Eingrenzen"-Pills (Speise-Refinements) bleiben neutral grau — nur
       die „Probier mal"-Beispiele tragen den Violett-Akzent (User 2026-07-06).
       Gleiche Spezifität wie die Regel oben → diese (spätere) gewinnt. */
    #suggestions-row .suggestion-chip {
      background: #f3f4f6;
      border-color: #e0e1e8;
      color: #3d3d3d;
      font-weight: 400;
    }
    #suggestions-row .suggestion-chip:hover {
      background: var(--primary);
      border-color: var(--primary);
      color: #fff;
    }

    /* ── Pre-Paint-Gerüst (Boot-Skript im <head>, 2026-07-05) ──
       Ziel: die Seite erscheint im finalen Layout, statt sich sichtbar
       stückweise aufzubauen (Logo-Flash-/CLS-Regression).
       1) Beispiel-Chip-Zeile: Fläche ab dem ersten Frame reservieren,
          solange keine Query in der URL steht (lwn-boot-empty). Die Klasse
          entfernt renderExamples() nach dem ersten echten Render.
       2) Ergebnisbereich (Facetten/Stats/Hits): unsichtbar halten, bis der
          erste Such-Render komplett ist, dann als EINE Einheit einblenden.
          Keyframes-Fallback: falls JS stirbt, nach 4 s trotzdem zeigen. */
    html.lwn-boot-empty #examples-row { display: flex; min-height: 26px; }
    /* ── Leerzustand: „Zuletzt gesucht" (grau/gedämpft) ─────────────────────
       Bewusst dezenter als die farbigen „Probier mal"-Beispiel-Chips: die
       Historie ist schneller Wiederzugriff, kein Blickfang. „Beliebt diese
       Woche"/Trend + Fokus sind NICHT sichtbar — nur internes Signal fürs
       kuratierte #curated-grid. */
    /* Kein eigener Vertikal-Margin mehr: der header.topbar-Row-Gap (0.5rem)
       besorgt den Abstand zur „Probier mal"-Zeile — sonst addierten sich Gap +
       Margin zu einer ungleichmäßigen Lücke (User 2026-07-06). */
    .empty-band { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin: 0; }
    .empty-band.hidden { display: none; }
    /* „Zuletzt gesucht" auf eigener voller Zeile UNTER dem Suchfeld (Desktop),
       zentriert wie „Probier mal" — nicht rechts neben dem Feld (User 2026-07-06).
       Auf Mobile überschreibt die Media-Query auf nowrap/flex-start (swipebar). */
    #recent-row { flex-basis: 100%; min-width: 100%; justify-content: center; }
    .empty-band .suggestions-label { color: var(--muted); font-size: 0.85rem; font-weight: 600; margin-right: 4px; white-space: nowrap; }
    .empty-chip { display: inline-flex; align-items: center; background: transparent; border: 1px solid var(--border); border-radius: 999px; overflow: hidden; }
    .empty-chip-q { background: transparent; border: 0; cursor: pointer; font: inherit; font-size: 0.9rem; color: var(--muted); padding: 5px 4px 5px 12px; }
    .empty-chip-x { background: transparent; border: 0; cursor: pointer; color: var(--muted); font-size: 1rem; line-height: 1; padding: 5px 10px 5px 4px; }
    .empty-chip-q:hover, .empty-chip-x:hover { color: var(--text); }
    .empty-chip:hover { border-color: var(--muted); }
    .empty-clear-all { background: transparent; border: 0; cursor: pointer; color: var(--muted); font-size: 0.82rem; text-decoration: underline; padding: 5px 6px; }
    html.lwn-boot-empty .empty-band:not(.hidden) { min-height: 30px; }
    /* Discovery-Bänder sind Leerzustands-only: sobald eine Query aktiv ist, hart
       ausblenden (Klasse in queryHook gesetzt) — unabhängig vom Render-Timing,
       das sie sonst in der Trefferansicht stehen ließ (Leak-Bug 2026-07-06). */
    html.lwn-has-query #recent-row,
    html.lwn-has-query #examples-row { display: none !important; }
    /* Personalisiertes Grid im Leerzustand — gleiche Karten + gleiches Raster
       wie die Trefferliste. Aktiv via html.lwn-curated (leeres Feld, keine
       Facette): dann tritt #hits/#stats/#pagination zurück, #curated-grid führt. */
    #curated-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1.5rem; }
    #curated-grid.hidden { display: none; }
    html.lwn-curated #hits, html.lwn-curated #stats, html.lwn-curated #pagination { display: none; }
    main.layout { transition: opacity 0.15s ease-out; }
    html.lwn-booting main.layout {
      opacity: 0;
      animation: lwn-reveal 0.15s ease-out 4s forwards;
    }
    @keyframes lwn-reveal { to { opacity: 1; } }

    main.layout {
      display: grid;
      grid-template-columns: 280px 1fr;
      gap: 2rem;
      max-width: 1400px;
      margin: 0 auto;
      padding: 2rem;
    }

    aside.filters { }
    aside.filters h3 {
      font-size: 0.75rem;
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: 0.08em;
      margin: 1.5rem 0 0.5rem;
      color: var(--muted);
    }
    aside.filters h3:first-child { margin-top: 0; }

    .ais-RefinementList-item,
    .ais-Menu-item {
      font-size: 0.9rem;
      padding: 2px 0;
    }
    .ais-RefinementList-checkbox {
      accent-color: var(--primary);
    }
    .ais-RefinementList-count {
      color: var(--muted);
    }

    /* Auto-applied facet items (set by the intent detector) */
    .ais-RefinementList-item.lwn-auto {
      background: #f3e8ff;
      border-radius: 6px;
      padding: 2px 6px;
      margin: 0 -6px;
    }
    .ais-RefinementList-item.lwn-auto > label {
      display: inline-flex;
      align-items: center;
      width: 100%;
    }
    .ais-RefinementList-item.lwn-auto > label::after {
      content: '✨';
      margin-left: auto;
      font-size: 0.75rem;
      color: #5b21b6;
      opacity: 0.85;
    }
    .ais-RefinementList-item.lwn-auto .ais-RefinementList-labelText {
      color: #5b21b6;
      font-weight: 600;
    }

    #stats {
      color: var(--muted);
      font-size: 0.85rem;
      margin-bottom: 1rem;
    }

    /* Hit grid */
    .ais-Hits-list {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
      gap: 1.5rem;
      padding: 0;
      margin: 0;
    }
    /* Trenner zwischen exakten Treffern und semantischen Nachbarn (Hybrid-
       Suche). Wird render-seitig vor dem ersten text_match=0-Hit eingefügt.
       Bewusst prominent (User-Feedback 2026-07-02: „deutlicher"): voller
       Balken statt dünner Linie, plus gedämpfte Karten mit gestricheltem
       Rahmen für alles darunter. */
    .lwn-semantic-divider {
      grid-column: 1 / -1;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 0.75rem;
      color: var(--text);
      font-size: 1.05rem;
      font-weight: 700;
      letter-spacing: -0.01em;
      background: var(--light);
      border: 1px solid var(--border);
      border-radius: 12px;
      padding: 0.9rem 1.25rem;
      margin: 1.5rem 0 0.25rem;
      text-align: center;
    }
    .lwn-semantic-divider::before { content: "≈"; color: var(--muted); font-size: 1.3rem; }
    /* Zero-Exact-Headline: alle Treffer nur „ähnlich" → explizite Ansage über
       der Liste (statt eines Trenners im Nichts). Zero-Hit-Analyse 3. */
    /* verzögert einblenden (wie .lwn-no-results): Übergangs-Renders beim
       LLM-Fallback-Wechsel werden nie sichtbar, nur stabile Zustände. */
    .lwn-zero-exact-headline {
      opacity: 0;
      animation: lwnNoResIn .2s ease .6s forwards;
    }
    .lwn-zero-exact-headline {
      grid-column: 1 / -1;
      color: var(--text);
      font-size: 1.05rem;
      font-weight: 700;
      letter-spacing: -0.01em;
      padding: 0.25rem 0.25rem 0.6rem;
      margin: 0 0 0.25rem;
    }
    /* Antwort-Chip für Service-/Info-Queries (versandkosten → Infoseite) */
    #info-hint { margin: 0.35rem 0 0.75rem; }
    #info-hint.hidden { display: none; }
    .info-hint-chip {
      display: inline-flex;
      align-items: center;
      gap: 0.4rem;
      padding: 0.5rem 0.95rem;
      background: var(--light);
      border: 1px solid var(--border);
      border-radius: 999px;
      color: var(--text);
      font-weight: 600;
      font-size: 0.95rem;
      text-decoration: none;
    }
    .info-hint-chip:hover { box-shadow: 0 1px 6px rgba(0, 0, 0, 0.08); }
    /* Ähnlich-Karten sichtbar absetzen: gestrichelter Rahmen + leicht gedimmt */
    .ais-Hits-item:has(.hit--semantic) {
      border-style: dashed;
      background: #fcfcfd;
    }
    .hit--semantic .hit-image,
    .hit--semantic .hit-image-placeholder { opacity: 0.85; }
    .hit-similar-badge {
      display: inline-block;
      margin-left: 0.4rem;
      padding: 0.05rem 0.45rem;
      font-size: 0.65rem;
      font-weight: 500;
      color: var(--muted);
      border: 1px solid var(--border);
      border-radius: 999px;
      vertical-align: 0.1em;
      white-space: nowrap;
    }
    .ais-Hits-item {
      padding: 0;
      border: 1px solid var(--border);
      border-radius: 12px;
      overflow: hidden;
      background: #fff;
      transition: all 0.2s ease;
      box-shadow: none;
    }
    .ais-Hits-item:hover {
      border-color: #ccc;
      box-shadow: 0 4px 20px rgba(0,0,0,.06);
      transform: translateY(-2px);
    }

    .hit {
      display: flex;
      flex-direction: column;
      height: 100%;
      width: 100%;
    }
    .hit a.hit-link {
      display: flex;
      flex-direction: column;
      text-decoration: none;
      color: var(--text);
      /* flex:1 statt height:100%: füllt die Karte, lässt aber Platz für die
         Warenkorb-Zeile als Geschwister-Element unter dem Link. */
      flex: 1 1 auto;
      /* Fill the grid cell. Without this the <a> shrank to its content width on
         no-image cards (the placeholder div has no intrinsic width like an <img>
         does), so the grey box rendered narrower than imaged cards. */
      width: 100%;
    }
    .hit-price-vat {
      font-size: 0.65rem;
      font-weight: 400;
      color: var(--muted);
      margin-left: 0.3rem;
      white-space: nowrap;
    }
    .hit-price-liter {
      font-size: 0.72rem;
      color: var(--muted);
      margin-top: -0.2rem;
    }
    .hit-cart-row {
      display: flex;
      gap: 0.4rem;
      padding: 0 1rem 1rem;
    }
    .hit-atc {
      border: 1px solid var(--border);
      background: #fff;
      border-radius: 8px;
      padding: 0.45rem 0.6rem;
      font-size: 0.78rem;
      cursor: pointer;
      color: var(--text);
      white-space: nowrap;
      font-family: inherit;
      /* seit cards.js sind das Anchors (href-Fallback), keine Buttons: */
      text-decoration: none;
      text-align: center;
      line-height: 1.3;
    }
    .hit-atc--primary {
      flex: 1;
      background: var(--secondary);
      border-color: var(--secondary);
      color: #fff;
      font-weight: 600;
    }
    .hit-atc:hover { filter: brightness(0.93); }
    .hit-atc.busy { pointer-events: none; opacity: 0.7; }
    /* Rarität — auf Anfrage (cards.js on_request): Anfrage-Button in der
       Warenkorb-Optik, aber EIGENE Klasse — die .hit-atc-Delegationen
       (preventDefault + cartAdd) dürfen den mailto nicht abfangen. */
    .hit-request {
      flex: 1;
      text-align: center;
      border: 1px solid var(--secondary);
      background: var(--secondary);
      color: #fff;
      border-radius: 8px;
      padding: 0.45rem 0.6rem;
      font-size: 0.78rem;
      font-weight: 600;
      text-decoration: none;
      line-height: 1.3;
    }
    .hit-request:hover { filter: brightness(0.93); }
    .hit-price--request { font-size: 0.92rem; }
    .hit-atc.added {
      background: #2e7d32;
      border-color: #2e7d32;
      color: #fff;
    }
    /* Schwebender Mini-Warenkorb unten rechts (Mobile-Filter-FAB sitzt links).
       Erscheint nach dem ersten Hintergrund-Add; Link = Cart-Handoff in den Shop. */
    .mini-cart {
      display: none;
      position: fixed;
      bottom: 1.2rem;
      right: 1.2rem;
      z-index: 1100;
      align-items: center;
      gap: 0.45rem;
      padding: 0.7rem 1.15rem;
      border-radius: 999px;
      background: var(--secondary);
      color: #fff;
      font-size: 0.92rem;
      font-weight: 600;
      text-decoration: none;
      box-shadow: 0 6px 18px rgba(0,0,0,0.22);
      min-height: 48px;
    }
    .mini-cart.visible { display: inline-flex; }
    .mini-cart:hover { filter: brightness(0.93); }
    .mini-cart .mini-cart-icon { flex: none; }
    .mini-cart .mini-cart-cta { font-weight: 400; opacity: 0.92; white-space: nowrap; }
    @keyframes mini-cart-bump {
      0% { transform: scale(1); }
      35% { transform: scale(1.12); }
      100% { transform: scale(1); }
    }
    .mini-cart.bump { animation: mini-cart-bump 320ms ease-out; }
    .hit-image {
      /* FESTE 300px-Höhe (User 2026-07-07, „lieber andersrum"): die alte
         Foto-Anmutung (height-Attribut wirkte als 300px-Hint, mit Luft ums
         Motiv) war die gewollte Referenz — also bekommen Foto UND
         Platzhalter dieselben fixen 300px, statt beide auf 1:1 zu stutzen.
         Karten springen so weiterhin nicht (beide Boxen identisch). */
      width: 100%;
      height: 300px;
      /* Vertikale Luft ums Motiv (User 2026-07-08, Regression-Fix): ohne
         Padding füllt ein Porträt-Flaschenfoto bei contain die volle Höhe und
         der Flaschenboden sitzt direkt auf der border-bottom-„Linie". Padding
         (border-box → 300px bleiben, contain skaliert ins Innere) lässt die
         Flasche oben UND unten frei schweben. Hintergrund/Border bleiben außen. */
      padding: 24px 14px;
      object-fit: contain;
      background: var(--light);
      border-bottom: 1px solid var(--border);
    }
    .hit-image-placeholder {
      /* Must mirror .hit-image's box exactly (same width + height + border),
         otherwise the no-image grey box renders at an inconsistent size — some
         cards filled the full square, others shrank to the text with white around. */
      width: 100%;
      height: 300px;
      background: var(--light);
      border-bottom: 1px solid var(--border);
      display: flex;
      align-items: center;
      justify-content: center;
      color: var(--muted);
      font-size: 0.75rem;
    }
    .hit-body {
      padding: 1rem;
      display: flex;
      flex-direction: column;
      gap: 0.35rem;
      flex: 1;
    }
    .hit-name {
      font-size: 0.95rem;
      font-weight: 600;
      line-height: 1.3;
    }
    .hit-meta {
      font-size: 0.8rem;
      color: var(--muted);
    }
    .hit-pairings {
      margin-top: 0.5rem;
      display: flex;
      flex-wrap: wrap;
      gap: 4px;
    }
    .hit-pairing-chip {
      font-size: 0.7rem;
      padding: 3px 8px;
      background: var(--light);
      border-radius: 20px;
      color: #555;
    }
    .hit-pairing-chip.match {
      background: #fff3cd;
      color: #664d00;
      border: 1px solid #ffe08a;
    }
    .hit-pairing-chip mark {
      background: transparent;
      font-weight: 700;
      color: inherit;
    }
    /* Duftzeile (Aroma-Evidenz 2026-07-23, cards.js): kursiv = sensorisch
       beschreibend, Chip = Speise/Handlung; Kapitälchen-Label vor beiden
       Zonen. --top = v2-Kartenkopf über dem Bild (Default; ?aromen=v1 legt
       die Zeile stattdessen in den Body). */
    .hit-aromas {
      font-size: 0.78rem;
      /* aufrecht statt kursiv (Mitya 2026-07-23) — die Zonen-Trennung
         tragen Position + Kapitälchen-Labels */
      color: #6b6b6b;
      line-height: 1.5;
    }
    .hit-aromas--top {
      padding: 0.5rem 1rem;
      border-bottom: 1px solid var(--border);
      /* zartes Pink (Entscheid Mitya 2026-07-23, „go pink" — nach Review
         von creme/grau/salbei/blau/lila auf der Dev-Preview) */
      background: #fdf0f4;
      /* leerer Kopf (Wein ohne Sensorik-Terme) hält die Bilder im Raster
         auf gleicher Höhe */
      min-height: 2.2rem;
      box-sizing: border-box;
    }
    .hit-aromas mark {
      background: linear-gradient(transparent 62%, #fff3cd 62%);
      color: #664d00;
      font-weight: 600;
    }
    .hit-zone-label {
      font-size: 0.6rem;
      font-weight: 600;
      letter-spacing: 0.09em;
      text-transform: uppercase;
      color: #a5a5a5;
      font-style: normal;
      margin-right: 0.35rem;
      white-space: nowrap;
      align-self: center;
    }
    /* zweites Segment in einem Band (z. B. AUSBAU nach BODEN) */
    .hit-zone-label--sub { margin-left: 0.6rem; }
    /* Fakten-Band UNTER dem Bild (Mitya 2026-07-23): BODEN + AUSBAU im
       Stein-Ton — Terroir/Handwerk als zweites Farbband zum Pink des
       Aromen-Kopfs. Erscheint nur bei Boden-/Ausbau-Match im Render. */
    .hit-facts {
      font-size: 0.78rem;
      color: #6b6b6b;
      line-height: 1.5;
      padding: 0.5rem 1rem;
      background: #f2efe9;
      border-bottom: 1px solid var(--border);
      min-height: 2.2rem;
      box-sizing: border-box;
    }
    .hit-facts mark {
      background: linear-gradient(transparent 62%, #fff3cd 62%);
      color: #664d00;
      font-weight: 600;
    }
    .hit-pairings--under { padding: 0.6rem 1rem 0; margin-top: 0; }

    /* Zero-Hit-Meldung erscheint VERZÖGERT (CTO-Feedback 2026-07-11:
       blinkte beim Tippen). Jeder Zwischenrender erzeugt den Knoten neu
       ⇒ Animations-Uhr startet neu; sichtbar wird nur ein STABILER
       Leerzustand — auch die LLM-Eskalation (450 ms Tipp-Ruhe) läuft
       damit meist unsichtbar durch. */
    .lwn-no-results {
      padding: 3rem;
      text-align: center;
      color: var(--muted);
      opacity: 0;
      animation: lwnNoResIn .2s ease .6s forwards;
    }
    @keyframes lwnNoResIn { to { opacity: 1; } }

    /* Intent-detection banner + chips */
    #intent-banner {
      margin-bottom: 0.75rem;
      min-height: 32px;
      display: flex;
      align-items: center;
      gap: 0.5rem;
      flex-wrap: wrap;
      font-size: 0.85rem;
      color: var(--muted);
      transition: opacity 0.2s ease;
    }
    #intent-banner.hidden { display: none; }
    /* Verschmelzung (2026-07-06, REVIDIERT 2026-07-11): harte Filter zeigen
       NUR die Kontextleisten-Chips (keine Doppelung). Die WEICHEN Signale
       (≈ Speise/Anlass/Küche/Sensorik) waren seither unsichtbar und nicht
       wegklickbar — User: „Wir brauchen Fische und Lachs als Chips" — das
       Banner rendert jetzt wieder, aber NUR Soft-Chips (Filter in
       renderIntentBanner). */
    /* #meta-row: Desktop transparent (Wrapper fällt aus dem Layout, #intent-banner
       rendert wie zuvor am Kopf der Ergebnisse). Mobile → Flex-Zeile, die
       zusätzlich #suggestions-row aufnimmt (Media-Block + placeMetaRow). */
    #meta-row { display: contents; }
    /* Chip-Zähler im Label: nur im Mobile-Collapse sichtbar (Media-Block). */
    .chip-count { display: none; }
    #intent-banner .intent-label {
      display: inline-flex;
      align-items: center;
      gap: 4px;
      padding: 2px 6px;
      background: #eef6ff;
      color: #1f4788;
      border-radius: 6px;
      font-weight: 600;
      font-size: 0.75rem;
    }
    #intent-banner .intent-chip {
      display: inline-flex;
      align-items: center;
      gap: 4px;
      padding: 3px 10px;
      background: #eef6ff;
      color: #1f4788;
      border: 1px solid #cfe0f5;
      border-radius: 20px;
      font-size: 0.75rem;
      font-weight: 500;
      animation: chip-in 0.3s ease;
    }
    #intent-banner .intent-chip .chip-field {
      opacity: 0.6;
      font-weight: 400;
    }
    #intent-banner .intent-chip .chip-x {
      appearance: none;
      background: transparent;
      border: 0;
      color: inherit;
      opacity: 0.5;
      cursor: pointer;
      padding: 0 2px;
      margin-left: 2px;
      font-size: 1rem;
      line-height: 1;
      font-weight: 600;
      transition: opacity 0.15s;
    }
    #intent-banner .intent-chip .chip-x:hover {
      opacity: 1;
    }
    /* Soft-Signale (Sensorik, Speise, Anlass, Küche): sortieren nur, filtern
       nicht — gestrichelt + neutraler Ton, damit sie sich sichtbar von den
       harten Filter-Chips (Rebsorte, Land, …) unterscheiden. */
    #intent-banner .intent-chip--soft {
      background: #f7f8fb;
      color: #4b5568;
      border-style: dashed;
      border-color: #c3c9d6;
    }
    #intent-banner .intent-chip.smart {
      background: #f3e8ff;
      color: #5b21b6;
      border-color: #d8b4fe;
    }
    @keyframes chip-in {
      from { opacity: 0; transform: translateY(-4px); }
      to { opacity: 1; transform: translateY(0); }
    }
    #intent-banner .intent-refining {
      display: inline-flex;
      align-items: center;
      gap: 4px;
      padding: 2px 8px;
      background: #f3e8ff;
      color: #5b21b6;
      border-radius: 6px;
      font-size: 0.75rem;
      font-weight: 600;
    }
    #intent-banner .intent-refining::before {
      content: '';
      display: inline-block;
      width: 10px;
      height: 10px;
      border: 2px solid currentColor;
      border-top-color: transparent;
      border-radius: 50%;
      animation: spin 0.8s linear infinite;
    }
    @keyframes spin { to { transform: rotate(360deg); } }

    .results.refined {
      animation: refined-glow 1s ease;
    }
    @keyframes refined-glow {
      0%, 100% { box-shadow: none; }
      50% { box-shadow: 0 0 0 3px #f3e8ff; }
    }
    .hit-price {
      font-size: 1.05rem;
      font-weight: 700;
      margin-top: auto;
      padding-top: 0.5rem;
    }

    /* Pagination */
    .ais-Pagination {
      margin-top: 2rem;
      display: flex;
      justify-content: center;
    }

    /* Alternatives block — shown when main hits < 3 and a geo filter was auto-applied */
    #alternatives {
      margin-top: 3rem;
      padding-top: 2rem;
      border-top: 1px dashed var(--border);
    }
    #alternatives.hidden { display: none; }
    #alternatives .alternatives-title {
      margin: 0 0 1.25rem;
      font-weight: 400;
    }
    #alternatives .alt-sub {
      display: block;
      font-size: 0.8rem;
      color: var(--muted);
      font-weight: 500;
      text-transform: uppercase;
      letter-spacing: 0.06em;
      margin-bottom: 0.35rem;
    }
    #alternatives .alt-main {
      display: block;
      font-size: 1.05rem;
      font-weight: 700;
      color: var(--text);
      letter-spacing: -0.01em;
    }

    /* When alternatives are shown AND the main result is empty, collapse the
       orphaned "Keine Treffer" block + pagination so the alternatives block
       becomes the primary visual content. */
    body.lwn-alt-empty #hits,
    body.lwn-alt-empty #pagination {
      display: none;
    }
    body.lwn-alt-empty #alternatives {
      margin-top: 0;
      padding-top: 0;
      border-top: none;
    }
    body.lwn-alt-empty #alternatives .alt-main {
      font-size: 1.35rem;
    }
    /* Sparse case (1–2 main hits): keep the separator but hide pagination */
    body.lwn-alt-sparse #pagination {
      display: none;
    }
    #alternatives-hits {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
      gap: 1.25rem;
    }
    #alternatives-hits .alt-hit {
      border: 1px solid var(--border);
      border-radius: 12px;
      background: #fff;
      overflow: hidden;
      opacity: 0.88;
      transition: opacity 0.2s, transform 0.2s, box-shadow 0.2s;
    }
    #alternatives-hits .alt-hit:hover {
      opacity: 1;
      transform: translateY(-2px);
      box-shadow: 0 4px 20px rgba(0,0,0,.06);
    }
    #alternatives-hits .alt-hit a {
      display: flex;
      flex-direction: column;
      text-decoration: none;
      color: var(--text);
    }
    #alternatives-hits .alt-hit img {
      width: 100%;
      height: 300px; /* konsistent zu .hit-image (feste Foto-Höhe, s. dort) */
      object-fit: contain;
      background: var(--light);
      border-bottom: 1px solid var(--border);
    }
    #alternatives-hits .alt-hit .alt-body {
      padding: 0.9rem;
      display: flex;
      flex-direction: column;
      gap: 0.3rem;
      flex: 1;
    }
    #alternatives-hits .alt-hit .alt-name {
      font-size: 0.85rem;
      font-weight: 600;
      line-height: 1.3;
    }
    #alternatives-hits .alt-hit .alt-meta {
      font-size: 0.75rem;
      color: var(--muted);
    }
    #alternatives-hits .alt-hit .alt-country-badge {
      display: inline-block;
      font-size: 0.7rem;
      padding: 2px 8px;
      background: #eef6ff;
      color: #1f4788;
      border-radius: 12px;
      align-self: flex-start;
      margin-top: 4px;
    }
    #alternatives-hits .alt-hit .alt-price {
      font-size: 0.9rem;
      font-weight: 700;
      margin-top: 0.25rem;
    }

    /* ── Results toolbar: [⇅][↗] Trefferzahl · Chip-Schwanz, EINE Zeile ── */
    .results-toolbar {
      display: flex;
      align-items: center;
      justify-content: flex-start;
      gap: 0.7rem;
      /* EINE swipebare Zeile (User 2026-07-07): nichts wrappt mehr — feste
         Controls links, der Chip-Schwanz scrollt horizontal aus dem Bild.
         Weiche Kanten (mask + --fade-l/-r) wie bei #examples-row; die
         Variablen setzt updateExamplesFade() im Render-/Scroll-Hook. */
      flex-wrap: nowrap;
      overflow-x: auto;
      -webkit-overflow-scrolling: touch;
      scrollbar-width: none;
      margin-bottom: 1rem;
      padding: 0.5rem 0;
      border-bottom: 1px solid var(--border);
      -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-l, 0px), #000 calc(100% - var(--fade-r, 0px)), transparent 100%);
              mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-l, 0px), #000 calc(100% - var(--fade-r, 0px)), transparent 100%);
    }
    .results-toolbar::-webkit-scrollbar { display: none; }
    .results-toolbar > * { flex: 0 0 auto; }
    /* Ergebnisse teilen: Icon-Kachel (Einzelsymbol, User 2026-07-07) */
    #share-results {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 38px;
      height: 38px;
      border: 1px solid var(--border);
      background: #fff;
      color: #444;
      border-radius: 8px;
      padding: 0;
      cursor: pointer;
      flex: 0 0 auto;
    }
    #share-results:hover { border-color: #9aa0b4; color: #000; }
    #share-results svg { width: 17px; height: 17px; }
    #share-toast {
      position: fixed;
      bottom: 24px;
      left: 50%;
      transform: translateX(-50%);
      background: #222;
      color: #fff;
      padding: 8px 16px;
      border-radius: 8px;
      font-size: 0.9rem;
      z-index: 1300;
      opacity: 0;
      transition: opacity 0.2s;
      pointer-events: none;
    }
    #share-toast.show { opacity: 1; }
    #applied-refinements {
      /* Kein Shrink mehr: der Chip-Schwanz darf überlaufen — die Toolbar
         selbst scrollt horizontal (swipebare Zeile, 2026-07-07). */
      flex: 0 0 auto;
    }
    #applied-refinements .ais-CurrentRefinements-list {
      display: flex;
      flex-wrap: nowrap;
      gap: 0.35rem;
      padding: 0;
      margin: 0;
      list-style: none;
    }
    #applied-refinements .ais-CurrentRefinements-item { white-space: nowrap; }
    #applied-refinements .ais-CurrentRefinements-item {
      display: inline-flex;
      align-items: center;
      gap: 0.25rem;
      padding: 0.2rem 0.55rem;
      background: #f3f4f6;
      border-radius: 999px;
      font-size: 0.8rem;
    }
    #applied-refinements .ais-CurrentRefinements-label {
      /* Feld-Label ausgeblendet (Verschmelzung 2026-07-06): die Werte sprechen
         für sich („Rot", „trocken", „≤ 15 €"); Preis trägt das € via transformItems. */
      display: none;
    }
    #applied-refinements .ais-CurrentRefinements-category {
      display: inline-flex;
      align-items: center;
      gap: 0.3rem;
    }
    #applied-refinements .ais-CurrentRefinements-delete {
      border: 0;
      background: transparent;
      cursor: pointer;
      font-size: 1rem;
      line-height: 1;
      color: var(--muted);
      min-width: 18px;
      min-height: 18px;
      padding: 0;
    }
    #applied-refinements .ais-CurrentRefinements-delete:hover { color: #111; }

    /* Sort-Dropdown + Teilen-Button als Einheit (rechts neben den
       Applied-Refinements). Auf Mobile bleibt die Einheit eine Zeile. */
    .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
    /* Trefferzahl links in der Kontextleiste — kompakt, tabellarische Ziffern. */
    #ctx-count { flex: 0 0 auto; font-weight: 700; font-size: 0.9rem; color: var(--text); white-space: nowrap; font-variant-numeric: tabular-nums; }
    #ctx-count:empty { display: none; }
    .toolbar-controls {
      display: flex;
      align-items: center;
      gap: 1rem;
      flex: 0 0 auto;
    }
    /* Sort als Icon-Kachel (User 2026-07-07): ⇅-Symbol, das native <select>
       liegt unsichtbar in voller Fläche darüber (öffnet den System-Picker).
       Bei aktiver Sortierung ≠ Relevanz färbt .sort-active die Kachel. */
    .sort-wrap {
      position: relative;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      width: 38px;
      height: 38px;
      border: 1px solid var(--border);
      border-radius: 8px;
      background: #fff;
      color: #444;
      cursor: pointer;
      flex: 0 0 auto;
    }
    .sort-wrap:hover { border-color: #9aa0b4; color: #000; }
    .sort-wrap .sort-icon { width: 17px; height: 17px; }
    .sort-wrap.sort-active { border-color: var(--primary); color: var(--primary); }
    .sort-wrap #sort-select {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      opacity: 0;
      border: 0;
      padding: 0;
      margin: 0;
      appearance: none;
      -webkit-appearance: none;
      cursor: pointer;
    }
    .sort-wrap:has(#sort-select:focus-visible) { outline: 2px solid var(--primary); outline-offset: 1px; }

    /* Native two-handle range sliders for price / vintage. They refine on
       input, no separate apply button. */
    .lwn-range {
      width: 100%;
      padding: 0.2rem 0 0.35rem;
    }
    .lwn-range-values {
      display: flex;
      justify-content: space-between;
      gap: 0.75rem;
      margin-bottom: 0.45rem;
      font-size: 0.78rem;
      color: var(--muted);
      font-variant-numeric: tabular-nums;
    }
    .lwn-range-value {
      display: inline-flex;
      align-items: baseline;
      gap: 0.2rem;
      min-width: 0;
    }
    .lwn-range-value strong {
      color: var(--text);
      font-size: 0.9rem;
      font-weight: 650;
    }
    .lwn-range-track {
      position: relative;
      height: 26px;
      display: flex;
      align-items: center;
    }
    .lwn-range-track::before,
    .lwn-range-fill {
      content: '';
      position: absolute;
      left: 0;
      right: 0;
      top: 50%;
      height: 4px;
      transform: translateY(-50%);
      border-radius: 999px;
    }
    .lwn-range-track::before { background: #e6e7ee; }
    .lwn-range-fill {
      left: var(--range-left, 0%);
      right: var(--range-right, 0%);
      background: var(--primary);
    }
    .lwn-range-input {
      position: absolute;
      left: 0;
      width: 100%;
      height: 26px;
      margin: 0;
      appearance: none;
      -webkit-appearance: none;
      background: transparent;
      pointer-events: none;
    }
    .lwn-range-input::-webkit-slider-thumb {
      appearance: none;
      -webkit-appearance: none;
      width: 18px;
      height: 18px;
      border-radius: 50%;
      background: #fff;
      border: 2px solid var(--primary);
      box-shadow: 0 2px 8px rgba(0,0,0,0.18);
      cursor: pointer;
      pointer-events: auto;
    }
    .lwn-range-input::-moz-range-thumb {
      width: 18px;
      height: 18px;
      border-radius: 50%;
      background: #fff;
      border: 2px solid var(--primary);
      box-shadow: 0 2px 8px rgba(0,0,0,0.18);
      cursor: pointer;
      pointer-events: auto;
    }
    .lwn-range-input::-moz-range-track { background: transparent; border: 0; }
    .lwn-range-empty {
      color: var(--muted);
      font-size: 0.82rem;
    }

    /* Kurzes Hervorheben der Facetten-Leiste (Desktop-Antwort auf
       „Alle Filter“ von der Listing-Seite — Drawer gibt es dort nicht). */
    .filters-flash { box-shadow: 0 0 0 3px rgba(47,90,174,.35); border-radius: 10px; transition: box-shadow .6s ease; }
    /* Mobile filter-drawer toggle. Hidden on desktop, visible < 900px. */
    .filter-toggle {
      display: none;
      position: fixed;
      bottom: 1.2rem;
      left: 1.2rem;
      z-index: 1100;
      padding: 0.75rem 1.1rem;
      border: 0;
      border-radius: 999px;
      background: var(--primary);
      color: #fff;
      font-size: 0.95rem;
      font-weight: 600;
      box-shadow: 0 6px 18px rgba(0,0,0,0.18);
      cursor: pointer;
      min-height: 48px;
      min-width: 48px;
    }
    /* Bei geöffnetem Drawer wird derselbe FAB zur blauen „N Ergebnisse"-Aktion
       (gleiche Form wie die Warenkorb-Pille, --secondary) — ein Steuerelement,
       kein zweiter Button im Drawer. */
    .filter-toggle.showing-results { background: var(--secondary); }

    /* Mobile-only Drawer-Affordanzen: auf Desktop unsichtbar, im Media-Block
       eingeblendet. Backdrop fängt Taps neben dem offenen Drawer ab. */
    #filter-backdrop { display: none; }
    .filter-close { display: none; }
    /* Scroll-Lock, solange der mobile Drawer offen ist (kein Durchscrollen des
       Hintergrunds hinter dem Backdrop). */
    body.drawer-open { overflow: hidden; }

    /* Keyboard-visible focus for all interactive elements */
    :focus-visible {
      outline: 2px solid var(--primary);
      outline-offset: 2px;
      border-radius: 4px;
    }

    /* Respect motion sensitivity */
    @media (prefers-reduced-motion: reduce) {
      *, *::before, *::after {
        transition: none !important;
        animation: none !important;
      }
      .hit:hover { transform: none !important; }
      /* Boot-Reveal-Sicherheitsnetz darf hier nicht sterben (sonst bliebe
         main bei totem JS unsichtbar): 0s-Dauer = diskreter Zustandswechsel
         nach 4 s, keine Bewegung. */
      html.lwn-booting main.layout { animation: lwn-reveal 0s 4s forwards !important; }
    }

    @media (max-width: 900px) {
      main.layout { grid-template-columns: 1fr; padding: 1rem; position: relative; }
      /* minmax(auto,1fr)-Blowout: ohne min-width:0 zwingt die min-content-
         Breite eines Karten-Elements (langer Name/Pairing-Chip) die 1fr-Spalte
         auf ~386px statt 358px → die ganze Ergebnis-Spalte (samt Toolbar mit
         „Teilen") ragte 12px aus dem 390er-Viewport. */
      /* padding-bottom = Freiraum, damit die Pagination oberhalb der schwebenden
         Buttons (Cart-Pill rechts, Filter-FAB links, je bottom:1.2rem + ~48px)
         zur Ruhe kommt — sonst verdeckt die Cart-Pill den „→"-Pfeil der
         Seitenübersicht (User-Report 2026-07-06). env(safe-area-inset-bottom)
         hält zusätzlich die iPhone-Home-Leiste frei. */
      main.layout > section.results {
        min-width: 0;
        padding-bottom: calc(88px + env(safe-area-inset-bottom));
      }

      /* Kompakter Header: Zeile 1 = Titel links + Sprachwahl rechts,
         Zeile 2 = Suchfeld in voller Breite. */
      header.topbar { padding: 0.8rem 1rem; gap: 0.6rem 1rem; }
      header.topbar .brand-row { width: 100%; justify-content: space-between; }
      header.topbar .brand { font-size: 1.05rem; }
      /* Suchfeld auf Mobile: eigene Zeile in KOMPLETTER Breite */
      #searchbox { flex-basis: 100%; max-width: none; }
      /* Beispiel-Chips: EINE swipebare Zeile statt zentriert gestapelt
         (User-Report 2026-07-05: „suchvorschläge stappeln unschön"). Die
         langen Beispiel-Queries wrappten sonst zu 2–3 ragged Zeilen.
         Die weiche Kanten-Ausblendung (mask, --fade-l/-r aus JE nach Scroll-
         Position gesetzt) signalisiert dezent „hier geht's weiter". */
      #examples-row, #recent-row {
        flex-wrap: nowrap;
        overflow-x: auto;
        justify-content: flex-start;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
      }
      #examples-row {
        -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-l, 0px), #000 calc(100% - var(--fade-r, 0px)), transparent 100%);
                mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-l, 0px), #000 calc(100% - var(--fade-r, 0px)), transparent 100%);
      }
      #examples-row::-webkit-scrollbar, #recent-row::-webkit-scrollbar { display: none; }
      #examples-row .suggestions-label,
      #examples-row .suggestion-chip,
      #recent-row .suggestions-label,
      #recent-row .empty-chip,
      #recent-row .empty-clear-all { flex: 0 0 auto; }
      /* Reservierung jetzt = 1 Zeile (vorher 56px für 2 Zeilen Wrap) */
      html.lwn-boot-empty #examples-row { min-height: 34px; }
      /* Embed auf Mobile: das Overlay verdeckt den Shop (samt Logo) fast
         komplett → eigenes Branding wieder einblenden (Desktop: Shop-Logo
         steht sichtbar hinterm Panel, Branding bleibt aus). Einzeilig neben
         Sprachwahl + ×: kompakter + nowrap, rechts nur so viel Padding wie
         das fixe × (right:14px + 36px) braucht. */
      /* header.topbar .brand (0-2-1) schlägt .embed-mode .brand (0-2-0) —
         darum hier mit voller Kette. */
      .embed-mode header.topbar .brand {
        display: block; white-space: nowrap; font-size: 0.95rem;
      }
      /* Das fixe × (oben rechts, y≈12–48px) bekommt seinen Freiraum jetzt
         von Zeile 1 (Brand-Row: padding + min-height) statt vom ganzen
         Header — so läuft das Suchfeld in Zeile 2 über die volle Breite
         (vorher war es rechts 54px verkürzt). */
      .embed-mode header.topbar { padding-right: 1rem; padding-left: 1rem; }
      .embed-mode header.topbar .brand-row { padding-right: 44px; min-height: 38px; }
      /* Ein-Zeilen-Dock im Embed: [L] [Feld] [fixes ×]. Das ×-Freihalte-Padding
         wandert von der Brand-Row an den Header (rechts 58px = right:14 + 36px
         Knopf + 8px Luft), die Brand-Row wird zum reinen L-Träger. Das L etwas
         größer als die 0.95rem-Wortmarke — allein muss es als Marke tragen.
         (Linker L-Kreis-Rand kommt aus dem Basis-Dock-Block: padding-left 27px
         für ALLE Dock-Fälle.) */
      html.lwn-collapsed.embed-mode header.topbar { padding-right: 58px; }
      html.lwn-collapsed.embed-mode header.topbar .brand-row { padding-right: 0; min-height: 0; }
      html.lwn-collapsed.embed-mode header.topbar .brand { font-size: 1.15rem; }

      /* Off-canvas filter drawer on mobile. The toggle button (FAB, bottom-
         left) slides the aside in from the left. Touch targets are 48 px+.
         Backdrop (#filter-backdrop) + Schließen-Button + sticky CTA + Swipe
         machen das Zuklappen intuitiv (User-Report 2026-07-06). */
      .filter-toggle { display: inline-flex; align-items: center; gap: 0.4rem; }

      /* Cart-Pill kompakt: „Zum Warenkorb →" fliegt raus, sonst überlappt
         die Pill den Filter-FAB (375px − FAB ≈ zu wenig für den vollen Text). */
      .mini-cart .mini-cart-cta { display: none; }

      aside.filters {
        position: fixed;
        top: 0;
        left: 0;
        width: min(320px, 88vw);
        height: 100vh;
        background: var(--bg);
        box-shadow: 8px 0 24px rgba(0,0,0,0.15);
        padding: 4rem 1.2rem 5rem;
        overflow-y: auto;
        z-index: 1000;
        transform: translateX(-105%);
        transition: transform 200ms ease-out;
      }
      aside.filters.open { transform: translateX(0); }
      /* Während des Swipe-to-close folgt der Drawer dem Finger — Transition aus,
         damit es sich direkt anfühlt (JS setzt transform inline). */
      aside.filters.dragging { transition: none; }

      /* Halbtransparenter Backdrop: liegt unter dem Drawer (z 1000) und den FABs
         (z 1100), aber über den Ergebnissen → fängt Taps ab. */
      #filter-backdrop {
        display: block;
        position: fixed;
        inset: 0;
        background: rgba(0,0,0,0.42);
        z-index: 990;
        opacity: 0;
        pointer-events: none;
        transition: opacity 200ms ease;
      }
      #filter-backdrop.open { opacity: 1; pointer-events: auto; }

      /* Schließen-× oben rechts im Drawer (der 4rem-Top-Padding lässt Platz). */
      .filter-close {
        display: flex;
        align-items: center;
        justify-content: center;
        position: absolute;
        top: 0.7rem;
        right: 0.7rem;
        width: 40px;
        height: 40px;
        border: 1px solid var(--border);
        border-radius: 999px;
        background: var(--bg);
        color: var(--primary);
        font-size: 1.4rem;
        line-height: 1;
        cursor: pointer;
        z-index: 1;
      }

      /* #meta-row: „Verstanden als" + „Eingrenzen" teilen sich EINE kompakte
         Zeile (placeMetaRow schiebt #suggestions-row hier hinein). */
      #meta-row {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 0.3rem 0.7rem;
        margin: 0 0 0.5rem;
      }
      /* flex:0 0 auto = Inhaltsbreite statt voller Zeile (sonst grabschte sich
         #suggestions-row die ganze meta-row → Intent-Banner wrappte in Zeile 2).
         So sitzen „Verstanden als" + „Eingrenzen" nebeneinander; passt es auf
         sehr schmalen Geräten nicht, wrappt es sauber auf 2 kompakte Zeilen. */
      #meta-row > #intent-banner,
      #meta-row > #suggestions-row {
        margin: 0;
        padding: 0;
        justify-content: flex-start;
        flex: 0 0 auto;
        /* Header-Basisregel setzt #suggestions-row auf min-width:100% (dort volle
           Zeile). In der meta-row muss das weg, sonst grabscht sie die Zeile. */
        min-width: 0;
      }
      /* Die aktiven Filter-Chips (#applied-refinements) wandern per placeMetaRow
         auf Mobile hierher, sodass „Verstanden als" + der aktive Filter-Chip in
         EINER Zeile stehen (statt auf zwei — Regression 2026-07-06). flex:0 1 auto
         = darf schrumpfen, damit die Chips intern wrappen statt zu überlaufen. */
      #meta-row > #applied-refinements {
        margin: 0;
        flex: 0 1 auto;
        min-width: 0;
      }
      #meta-row > #intent-banner .intent-label,
      #meta-row > #suggestions-row .suggestions-label { white-space: nowrap; }
      /* Dezenter „·"-Trenner NUR wenn beide Blöcke sichtbar sind (kein
         führendes „·", falls „Verstanden als" fehlt). :has() → alte Browser
         zeigen einfach keinen Trenner (= Status quo, nie schlechter). Die
         Flex-Gaps (meta-row 0.7rem links, suggestions-row 6px rechts) geben
         dem „·" den Abstand — keine eigenen Ränder nötig. */
      #meta-row:has(> #intent-banner:not(.hidden)) > #suggestions-row:not(.hidden)::before {
        content: '·';
        color: var(--muted);
        opacity: 0.6;
      }

      /* Kontextleiste mobil: gleiche Swipe-Zeile wie Desktop, nur die
         Icon-Kacheln wachsen aufs 44px-Touch-Target. */
      .results-toolbar { gap: 0.5rem; }
      #applied-refinements:has(.ais-CurrentRefinements--noRefinement) { display: none; }
      .toolbar-controls { flex: 0 0 auto; gap: 0.5rem; }
      .sort-wrap, #share-results { width: 44px; height: 44px; }

      /* Touch-friendly targets */
      .ais-RefinementList-item { padding: 6px 0; min-height: 36px; }
      .ais-RangeInput-input, .ais-RangeInput-submit {
        min-height: 44px;
      }

      /* „Verstanden als" + „Eingrenzen:" starten eingeklappt: nur die
         Label-Zeile mit Zähler + Chevron, Tap aufs Label klappt aus.
         Die m-collapsed-Regeln leben bewusst NUR hier im Media-Block —
         Desktop bleibt auch mit gesetzter Klasse voll ausgeklappt. */
      #intent-banner .chip-count,
      #suggestions-row .chip-count { display: inline; margin-left: 3px; opacity: 0.75; }
      #intent-banner .intent-label,
      #suggestions-row .suggestions-label { cursor: pointer; padding: 6px 8px; }
      #intent-banner .intent-label::after,
      #suggestions-row .suggestions-label::after {
        content: '▾';
        margin-left: 5px;
        font-size: 0.95rem;
        line-height: 1;
      }
      #intent-banner.m-collapsed .intent-label::after,
      #suggestions-row.m-collapsed .suggestions-label::after { content: '▸'; }
      #intent-banner.m-collapsed .intent-chip,
      #suggestions-row.m-collapsed .suggestion-chip { display: none; }
      #intent-banner.m-collapsed { min-height: 0; margin-bottom: 0.4rem; }
    }
