/*
 * Öffentlicher Bereich: Kopfzeile, Katalog, Artikeldetail, Anfragekorb.
 * Setzt base.css voraus.
 */

body.site {
    background: var(--white);
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

/* -------------------------------------------------------------- Kopfzeile */

/*
 * Fast deckend statt Milchglas. Ein `backdrop-filter` auf der klebenden
 * Kopfzeile muss bei jedem Scrollbild alles darunter neu weichzeichnen – auch
 * die laufenden Animationen im Kopf der Startseite und die kippenden Kacheln.
 * Safari auf dem iPhone und manche Grafiktreiber unter Windows lassen die
 * Kopfzeile dabei flackern, und bei 92 % Deckkraft sah vom Weichzeichner
 * ohnehin kaum jemand etwas.
 */
.site-header {
    position: sticky;
    top: 0;
    z-index: 40;
    background: rgba(255, 255, 255, .97);
    border-bottom: 1px solid var(--ink-200);
}

.site-header .container {
    display: flex;
    align-items: center;
    gap: var(--space-5);
    min-height: 72px;
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    font-weight: 700;
    font-size: 1.0625rem;
    color: var(--ink-900);
    letter-spacing: -.015em;
}

.brand:hover { text-decoration: none; color: var(--brand-700); }

/*
 * Hinterlegtes Logo statt Namenskürzel.
 *
 * Ein Wort-Bild-Logo mit Slogan ist breit und flach (oft 2:1 bis 3:1) und
 * enthält meist eigenen Rand. Nur die Höhe zu begrenzen macht es deshalb
 * unleserlich klein – es bekommt hier so viel Höhe, wie die Kopfzeile
 * hergibt, und darf dafür breit werden. `object-fit` hält die Proportion,
 * egal welche Datei hochgeladen wird.
 */
.brand-logo {
    height: 52px;
    width: auto;
    max-width: 280px;
    object-fit: contain;
    object-position: left center;
}

/* Mit Logo darf die Kopfzeile etwas höher werden – sonst klebt es am Rand. */
.site-header:has(.brand-logo) .container { min-height: 84px; }

/*
 * Helle Logos brauchen einen dunklen Untergrund – auf der hellen Kopfzeile
 * wären sie sonst schlicht unsichtbar.
 */
.brand-on-dark {
    background: var(--brand-800);
    border-radius: var(--radius);
    padding: 4px var(--space-3);
}

.brand-on-dark:hover { background: var(--brand-900); }

/* Ohne Logo bleibt die Marke wie bisher: Kürzel plus Name. */
.brand:has(.brand-logo) { gap: 0; }

@media (max-width: 860px) {
    .brand-logo { height: 40px; max-width: 190px; }
    .site-header:has(.brand-logo) .container { min-height: 0; }
}

@media (max-width: 420px) {
    .brand-logo { height: 34px; max-width: 150px; }
}

.brand-mark {
    width: 38px; height: 38px;
    border-radius: 11px;
    background: linear-gradient(135deg, var(--brand-600), var(--brand-800));
    color: var(--white);
    display: grid;
    place-items: center;
    font-size: .9375rem;
    font-weight: 700;
    letter-spacing: .02em;
    flex: none;
    box-shadow: var(--shadow-xs);
}

.brand-sub {
    display: block;
    font-size: .6875rem;
    font-weight: 550;
    text-transform: uppercase;
    letter-spacing: .1em;
    color: var(--ink-400);
    line-height: 1.2;
}

.site-nav {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    margin-left: auto;
    min-width: 0;
}

/*
 * Die Kategorien dürfen schrumpfen, der Anfrage-Knopf nie – er ist das Ziel
 * der ganzen Seite und darf auf keiner Breite verschwinden.
 */
.nav-categories {
    display: flex;
    align-items: center;
    align-content: center;
    gap: 2px;
    min-width: 0;
    /*
     * **Umbrechen, nicht überlaufen.** `min-width: 0` erlaubt dieser Zeile,
     * unter die Breite ihres Inhalts zu schrumpfen; die Punkte darin brechen
     * ihren Namen nicht um (`white-space: nowrap`) und schrumpfen deshalb
     * nicht mit. Ohne `flex-wrap` ragten sie damit rechts aus der Zeile heraus
     * – und weil hier bewusst nichts beschnitten wird (siehe unten), legten
     * sich lange Kategorienamen quer über „Anmelden" und „Auftrag". Der
     * Umbruch macht die Kopfzeile in diesem Fall höher, was sichtbar und
     * verständlich ist; überlappende Beschriftungen sind es nicht.
     */
    flex-wrap: wrap;
    /*
     * **Kein `overflow: hidden`.** Ein Klappmenü liegt absolut positioniert
     * unter seinem Auslöser und damit außerhalb dieser Zeile – ein
     * beschneidender Container schnitte es ab, und zwar genau da, wo der
     * Inhalt anfängt. Dass die Zeile im Normalfall gar nicht umbricht, regelt
     * die geschätzte Breite der Punkte im Layout (`$widthBudget`) und nicht
     * das Wegschneiden.
     */
}

.nav-link {
    padding: 8px 12px;
    border-radius: var(--radius);
    font-size: .9375rem;
    font-weight: 520;
    color: var(--ink-600);
    white-space: nowrap;
}

.nav-link:hover { background: var(--ink-100); color: var(--ink-900); text-decoration: none; }

.nav-link.is-active {
    color: var(--brand-700);
    background: var(--brand-50);
    font-weight: 600;
}

/* ------------------------------------------------------- Kategoriemenü */

.nav-menu { position: relative; }

.nav-menu summary {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 8px 14px;
    border-radius: var(--radius);
    font-size: .9375rem;
    font-weight: 520;
    color: var(--ink-600);
    cursor: pointer;
    list-style: none;
    user-select: none;
    white-space: nowrap;
}

/* Das Standard-Dreieck weicht dem Chevron im Text. */
.nav-menu summary::-webkit-details-marker { display: none; }

.nav-menu summary:hover { background: var(--ink-100); color: var(--ink-900); }
.nav-menu summary.is-active { color: var(--brand-700); background: var(--brand-50); }
.nav-menu summary:focus-visible { outline: 2px solid var(--brand-600); outline-offset: 2px; }

.nav-menu[open] summary { background: var(--ink-100); color: var(--ink-900); }
.nav-menu[open] summary svg { transform: rotate(180deg); }
.nav-menu summary svg { transition: transform .15s ease; }

.nav-menu-panel {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 60;
    min-width: 220px;
    padding: 6px;
    background: var(--white);
    border: 1px solid var(--ink-200);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.nav-menu-panel a {
    padding: 8px 12px;
    border-radius: var(--radius-sm);
    font-size: .9375rem;
    color: var(--ink-700);
    white-space: nowrap;
}

.nav-menu-panel a:hover { background: var(--ink-100); color: var(--ink-900); text-decoration: none; }
.nav-menu-panel a.is-active { color: var(--brand-700); background: var(--brand-50); font-weight: 560; }

/*
 * Der Weg zur Oberkategorie selbst. Ein `<summary>` ist kein Link, also stünde
 * „Zelte" sonst nur noch als Beschriftung da – die Seite gäbe es, nur käme
 * niemand mehr hin. Die Linie darunter trennt das Ganze von seinen Teilen.
 */
.nav-menu-all {
    font-weight: 560;
    color: var(--ink-900) !important;
    border-bottom: 1px solid var(--ink-100);
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    margin-bottom: 2px;
    padding-bottom: 10px !important;
}

/* Das Menü fährt kurz aus, statt zu erscheinen – der Weg zeigt, woher es kommt. */
.nav-menu[open] .nav-menu-panel { animation: nav-menu-in .16s ease-out; }

@keyframes nav-menu-in {
    from { opacity: 0; transform: translateY(-6px); }
    to { opacity: 1; transform: none; }
}

/*
 * Auf schmalen Geräten passen Marke, Kategorien und Anfrage-Knopf nicht mehr
 * nebeneinander. Statt Einträge wegzulassen, wandert die Kategoriezeile unter
 * den Kopf und scrollt dort waagerecht – so bleibt jede Kategorie erreichbar.
 */
@media (max-width: 860px) {
    .site-header .container {
        flex-wrap: wrap;
        gap: var(--space-3) var(--space-4);
        padding-block: var(--space-3);
    }

    .site-nav { margin-left: auto; gap: var(--space-2); }

    .nav-categories {
        order: 3;
        width: 100%;
        /*
         * Hier wird gescrollt statt umgebrochen: Die Zeile hat unter dem Kopf
         * die ganze Breite für sich, und eine zweite Zeile darunter schöbe den
         * Inhalt der Seite immer weiter nach unten. Ein umbrechender
         * Flex-Container läuft nie waagerecht über und hätte aus dem Scrollen
         * einen mehrzeiligen Block gemacht.
         */
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        padding-bottom: 2px;
        margin-inline: calc(-1 * var(--space-5));
        padding-inline: var(--space-5);
    }

    .nav-categories::-webkit-scrollbar { display: none; }

    .site-nav {
        display: contents;
    }

    .basket-link { order: 2; }
    .brand-sub { display: none; }

    /*
     * Auf schmalen Geräten scrollt die Kategoriezeile waagerecht – und ein
     * scrollender Container beschneidet, was aus ihm herausragt. `fixed` hängt
     * am Anzeigefenster statt am Container und entkommt dem: Das Menü legt sich
     * über die volle Breite unter die Zeile, statt in ihr zu verschwinden.
     * `top: auto` behält dabei die Stelle, an der es ohnehin stünde.
     */
    .nav-menu-panel {
        position: fixed;
        top: auto;
        left: var(--space-4);
        right: var(--space-4);
        min-width: 0;
        max-height: 60vh;
        overflow-y: auto;
    }
}

@media (max-width: 420px) {
    .basket-link span:not(.basket-count) { display: none; }
}

/*
 * Konto und Auftrag geben keinen Millimeter her. Was die Kategorien mehr
 * brauchen, holen sie sich über einen Umbruch und nicht aus dem Platz dieser
 * beiden – der Auftragsknopf ist das Ziel der ganzen Seite, und ein halb
 * abgeschnittenes „Anmelden" daneben ist kein Einstieg mehr.
 */
.account-link,
.basket-link { flex: none; }

.basket-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: 9px 16px !important;
    border-radius: var(--radius);
    background: var(--brand-700);
    color: var(--white) !important;
    font-weight: 560;
}

.basket-link:hover { background: var(--brand-800) !important; }

.basket-count {
    display: inline-grid;
    place-items: center;
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: 999px;
    /*
     * Weiß auf Dunkel, nicht schwarz: Der Zähler sitzt auf dem dunklen
     * Auftragsknopf in der Kopfzeile. Ein schwarzer Punkt darauf wäre kein
     * Hinweis mehr, sondern ein Loch.
     */
    background: var(--white);
    color: var(--ink-900);
    font-size: .6875rem;
    font-weight: 700;
}

/*
 * Der Kontolink ist bewusst zurückhaltend: Er steht neben dem Anfrageknopf,
 * darf ihm aber nicht die Aufmerksamkeit nehmen.
 */
.account-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 9px 12px !important;
    border-radius: var(--radius);
    color: var(--ink-600) !important;
    font-weight: 520;
    white-space: nowrap;
}

.account-link:hover { background: var(--ink-50); color: var(--brand-700) !important; text-decoration: none; }

/*
 * Auf schmalen Geräten die kurze Beschriftung.
 *
 * Nur das Symbol stehen zu lassen wäre bequemer, hilft aber niemandem: Neben
 * der Einkaufstasche ist ein Personen-Symbol nicht als Konto erkennbar, und
 * ein Einstieg, den noch niemand sucht, muss beschriftet sein. Der
 * Auftragsknopf bleibt trotzdem der auffälligere – er ist gefüllt, das Konto
 * nicht.
 */
.account-link-short { display: none; }

@media (max-width: 640px) {
    .account-link-text { display: none; }
    .account-link-short { display: inline; }
    .account-link { padding: 9px 10px !important; }
}

/* --------------------------------------------------- Suche in der Kopfzeile */

/*
 * Eine Lupe neben „Anmelden", die ein Suchfeld aufklappt (`layout/default`).
 * Ohne Skript hängt das Feld als Klappfeld unter der Lupe; mit Skript
 * (`.is-enhanced`, `initHeaderSearch()`) wird es auf dem Handy zur
 * Vollbildsuche mit eigenem Schließen-Knopf – erst dann gibt es einen Weg
 * hinaus, der nicht die Lupe darunter braucht.
 */
.header-search { position: relative; flex: none; }

.header-search > summary {
    display: inline-grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border-radius: var(--radius);
    color: var(--ink-600);
    cursor: pointer;
    list-style: none;
}
.header-search > summary::-webkit-details-marker { display: none; }
.header-search > summary:hover,
.header-search[open] > summary { background: var(--ink-100); color: var(--ink-900); }
.header-search > summary:focus-visible { outline: 2px solid var(--brand-600); outline-offset: 2px; }

.header-search-panel {
    position: absolute;
    top: calc(100% + 10px);
    right: 0;
    width: min(560px, calc(100vw - 2 * var(--space-4)));
    padding: var(--space-3);
    background: var(--white);
    border: 1px solid var(--ink-200);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    z-index: 60;
}

.header-search-row { display: flex; gap: var(--space-2); }
.header-search-row input { flex: 1; min-width: 0; }

.header-search-close {
    display: none;
    place-items: center;
    width: 42px;
    flex: none;
    border: 0;
    border-radius: var(--radius);
    background: var(--ink-100);
    color: var(--ink-700);
    cursor: pointer;
}

/* Die Vorschläge stehen im Panel und nicht darüber hinaus. */
.header-search .suggestions { max-height: min(60vh, 460px); }

@media (max-width: 860px) {
    /*
     * Auf dem Handy steht die Lupe am Anfang der Kategoriezeile und nicht
     * neben „Konto" und „Auftrag": Dort ist jeder Pixel vergeben, und eine
     * vierte Schaltfläche schob den Auftragsknopf in eine eigene Zeile. Der
     * leere Umbruch (`::after`) zwingt sie in die zweite Zeile, auch wenn
     * oben zufällig Platz wäre – sonst stünde sie je nach Firmenname mal
     * oben, mal unten.
     */
    .site-header .container::after {
        content: "";
        order: 3;
        flex-basis: 100%;
        height: 0;
        margin-top: calc(-1 * var(--space-3));
    }
    .header-search { order: 4; }
    .nav-categories { order: 5; flex: 1 1 0; width: auto; margin-left: 0; padding-left: 0; }
    .header-search > summary { width: 36px; height: 36px; background: var(--ink-50); }

    /* Ohne Skript: über die ganze Breite unter die Kopfzeile, wie die Klappmenüs. */
    .header-search-panel {
        position: fixed;
        top: auto;
        left: var(--space-4);
        right: var(--space-4);
        width: auto;
    }

    /* Mit Skript: die ganze Fläche, das Feld oben, die Vorschläge darunter. */
    .header-search.is-enhanced .header-search-panel {
        inset: 0;
        border: 0;
        border-radius: 0;
        box-shadow: none;
        padding: var(--space-3) var(--space-4);
        z-index: 100;
        overflow-y: auto;
    }
    .header-search.is-enhanced .header-search-close { display: inline-grid; }
    .header-search.is-enhanced .header-search-row .btn { display: none; }
    .header-search.is-enhanced .suggestions {
        position: static;
        margin-top: var(--space-3);
        max-height: none;
        box-shadow: none;
    }
}

/*
 * Solange die Vollbildsuche offen ist, scrollt die Seite darunter nicht mit –
 * und die Kopfzeile liegt über allem, auch über dem Preisbalken der
 * Artikelseite: Die Suche hängt in ihr, und mehr als die Kopfzeile selbst
 * kann sie nicht nach vorn.
 */
html.has-search-open { overflow: hidden; }
html.has-search-open .site-header { z-index: 200; }

/* ---------------------------------------------------------- Ankündigungen */

/*
 * Die Leiste unter der Kopfzeile. Sie klebt **nicht** mit: Die Kopfzeile
 * klebt schon, und zwei klebende Leisten übereinander nähmen auf dem Handy
 * ein Viertel des Bildschirms für etwas, das man einmal gelesen hat.
 */
.announcement {
    border-bottom: 1px solid rgba(0, 0, 0, .08);
    font-size: .9375rem;
    line-height: 1.45;
}

.announcement-info { background: var(--brand-700); color: var(--white); }
.announcement-promo { background: var(--accent-600); color: var(--white); }
.announcement-warning { background: var(--warn-100); color: var(--warn-700); }

.announcement-inner {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding-block: 10px;
}

.announcement-icon {
    flex: none;
    display: inline-flex;
    margin-top: 1px;
}

.announcement-icon svg { width: 18px; height: 18px; }

.announcement-text {
    flex: 1;
    min-width: 0;
    margin: 0;
    overflow-wrap: anywhere;
}

.announcement-link {
    margin-left: .35em;
    color: inherit;
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 2px;
    white-space: nowrap;
}

.announcement-link:hover { color: inherit; text-decoration-thickness: 2px; }

.announcement-dismiss { flex: none; margin: -4px -6px -4px 0; }

.announcement-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: inherit;
    cursor: pointer;
    opacity: .8;
}

.announcement-close:hover,
.announcement-close:focus-visible { opacity: 1; background: rgba(127, 127, 127, .2); }

.announcement-close svg { width: 16px; height: 16px; }

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

/*
 * Der Lichtschein oben rechts ist eine **Hintergrundebene** und kein
 * positioniertes Pseudo-Element. Als Pseudo-Element ragte er über den Hero
 * hinaus und brauchte dafür `overflow: hidden` – und das schnitt dann auch die
 * Suchvorschläge ab, sobald mehr als zwei Treffer kamen. Ein Hintergrund
 * verlässt sein Element nie, also entfällt das Beschneiden und die Liste darf
 * über den Hero hinauswachsen.
 */
.hero {
    /*
     * Der Lichtfleck oben rechts war bernsteinfarben und ist jetzt ein heller
     * Schleier: Schwarz auf dunklem Petrol wäre kein Akzent, sondern nur ein
     * dunkler Fleck. Was den Verlauf lebendig macht, ist der Helligkeits-
     * unterschied – der Farbton war nie der Punkt.
     */
    background:
        radial-gradient(circle 320px at calc(100% - 80px) 120px,
            rgba(255, 255, 255, .10) 0%, transparent 65%),
        radial-gradient(circle 420px at 12% 115%,
            rgba(0, 0, 0, .35) 0%, transparent 70%),
        linear-gradient(160deg, var(--brand-900) 0%, var(--brand-700) 55%, var(--brand-600) 100%);
    color: var(--white);
    padding: var(--space-8) 0 var(--space-8);
    position: relative;
}

/*
 * Der wandernde Schein.
 *
 * Ein heller Fleck, doppelt so groß wie der Hero, der langsam von rechts
 * unten nach links oben zieht. Er liegt in `.hero-glow` (dem beschneidenden
 * Rahmen weiter unten) und wandert per `transform` – nicht als vierte
 * Hintergrundebene mit wanderndem `background-position`: Das zwang den
 * Browser, den ganzen Hero in jedem Bild neu zu zeichnen, solange die Seite
 * offen war, und kostete auf der Startseite dauerhaft Rechenzeit. Ein
 * `transform` verschiebt nur die fertige Ebene.
 *
 * Langsam genug, dass man es erst beim zweiten Hinsehen merkt: Ein Hero, der
 * sichtbar pulsiert, lenkt vom Suchfeld ab, und das ist der Einstieg.
 */
@media (prefers-reduced-motion: no-preference) {
    .hero-glow::before {
        content: "";
        position: absolute;
        top: 0;
        left: 0;
        width: 200%;
        height: 200%;
        background: radial-gradient(circle at 50% 50%, rgba(255, 255, 255, .09) 0%, transparent 38%);
        animation: hero-drift 26s ease-in-out infinite alternate;
    }
}

@keyframes hero-drift {
    to { transform: translate(-50%, -35%); }
}

/*
 * Tiefe beim Scrollen: Überschrift und Satz treten etwas langsamer zurück als
 * die Seite. Rein über `animation-timeline` – wo der Browser das nicht kennt,
 * steht der Text still, und das ist kein Verlust.
 *
 * Bewusst **nicht** am Suchfeld: Es trägt die Vorschlagsliste, und eine Liste,
 * die beim Scrollen ihrem Feld davonläuft, ist ein Fehler und keine Tiefe.
 * Und bewusst an einem eigenen Umschlag (`.hero-copy`) statt an Überschrift
 * und Satz selbst: Die tragen beim Laden ihre Einblendung aus animate.css, und
 * die bekäme sonst die Zeitleiste des Scrollens mit.
 */
@supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
        .hero-copy {
            animation: hero-depth linear both;
            animation-timeline: view();
            animation-range: exit 0% exit 100%;
        }
    }
}

@keyframes hero-depth {
    to { transform: translateY(48px); opacity: .35; }
}

/*
 * Überschrift und Satz nutzen dieselbe Breite wie das Suchfeld darunter. Mit
 * 22ch/58ch standen sie als schmale Spalte links über einem Feld, das bis zum
 * rechten Rand reicht, und die Überschrift brach als „unver-bindlich" um.
 *
 * Keine automatische Silbentrennung in der Überschrift: Bei voller Breite
 * passt jedes Wort auch auf dem Telefon in die Zeile, und ein Trennstrich in
 * der größten Schrift der Seite fällt mehr auf als ein früherer Umbruch.
 * `overflow-wrap: anywhere` aus `base.css` bleibt die Rückfallebene für ein
 * Wort, das breiter ist als die Spalte. `pretty` verhindert ein einzelnes
 * Wort allein in der letzten Zeile, ohne die Zeilen künstlich zu verkürzen
 * wie `balance`.
 */
.hero h1 { color: var(--white); hyphens: manual; text-wrap: pretty; }
.hero p { color: rgba(255, 255, 255, .82); font-size: 1.0625rem; text-wrap: pretty; }

.hero-inner { position: relative; z-index: 1; }

/*
 * Kopfbild einer Kategorie.
 *
 * Das Bild liegt unter einem Schleier in Markenfarbe, der links dicht und
 * rechts dünn ist: Links stehen Überschrift und Satz in Weiß, und die müssen
 * auf jedem Foto lesbar bleiben – auch auf einem hellen Himmel. Rechts darf
 * das Bild sprechen. Die Lichtflecken entfallen; über einem Foto wären sie
 * nur Nebel, der Rechenzeit kostet.
 *
 * Beim Laden zoomt das Bild einmal langsam heraus. Einmal und nicht im
 * Kreis: Ein Kopf, der sich dauernd bewegt, zieht den Blick vom Suchfeld.
 */
.hero-has-image { padding-top: 96px; padding-bottom: calc(96px + 24px); }

@media (max-width: 720px) {
    .hero-has-image { padding-top: var(--space-8); padding-bottom: calc(var(--space-8) + 24px); }
}

.hero-photo {
    position: absolute;
    inset: 0;
    overflow: hidden;
    z-index: 0;
}

.hero-photo img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
}

.hero-photo::after {
    content: "";
    position: absolute;
    inset: 0;
    background:
        linear-gradient(90deg,
            color-mix(in srgb, var(--brand-900) 90%, transparent) 0%,
            color-mix(in srgb, var(--brand-900) 72%, transparent) 45%,
            color-mix(in srgb, var(--brand-900) 30%, transparent) 100%),
        linear-gradient(0deg, color-mix(in srgb, var(--brand-900) 45%, transparent) 0%, transparent 40%);
}

.hero-has-image .hero-glow { display: none; }

@media (max-width: 720px) {
    /* Einspaltig steht der Text über dem ganzen Bild – der Schleier wird gleichmäßig. */
    .hero-photo::after { background: color-mix(in srgb, var(--brand-900) 78%, transparent); }
}

@media (prefers-reduced-motion: no-preference) {
    .hero-photo img { animation: hero-photo-settle 9s ease-out both; }
}

@keyframes hero-photo-settle {
    from { transform: scale(1.08); }
}

/* ----------------------------------------------------------- Suchleiste */

.search-panel {
    background: var(--white);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: var(--space-5);
    margin-top: var(--space-6);
    color: var(--ink-800);
}

/* Das Suchfeld ist der Einstieg – entsprechend groß. */

.search-main { margin-bottom: var(--space-5); }

.search-main > label {
    font-size: 1rem;
    font-weight: 600;
    color: var(--ink-800);
    margin-bottom: var(--space-3);
}

.search-main-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: 6px 6px 6px var(--space-4);
    border: 2px solid var(--ink-200);
    border-radius: var(--radius-lg);
    background: var(--white);
    transition: border-color var(--transition), box-shadow var(--transition);
}

.search-main-row:focus-within {
    border-color: var(--brand-500);
    box-shadow: 0 0 0 4px var(--brand-100);
}

.search-main-icon { width: 22px; height: 22px; flex: none; color: var(--ink-400); }

.search-main-row input[type="search"] {
    flex: 1;
    min-width: 0;
    border: 0;
    padding: var(--space-3) 0;
    font-size: 1.125rem;
    background: none;
    box-shadow: none;
}

.search-main-row input[type="search"]:focus { outline: none; box-shadow: none; }
.search-main-row input[type="search"]::placeholder { color: var(--ink-400); }
.search-main-row .btn { flex: none; }

@media (max-width: 560px) {
    .search-main-row { flex-wrap: wrap; padding: var(--space-3); }
    .search-main-row input[type="search"] { font-size: 1rem; }
    .search-main-row .btn { width: 100%; }
    .search-main-icon { display: none; }
}

.search-panel .field { margin-bottom: 0; }

/* ------------------------------------------------------- Katalog-Raster */

/*
 * `.section` setzt **nur** den senkrechten Abstand – deshalb `padding-block`
 * und nicht die Kurzform `padding: … 0`.
 *
 * Kundenkonto, Auftragskorb und Portal tragen beide Klassen auf demselben
 * Element (`class="container section"`). Die Kurzform hat dort das
 * `padding-inline` des Containers überschrieben: gleiche Spezifität, später in
 * der Datei, und schon klebte der ganze Inhalt am Bildrand. Auf dem Schreibtisch
 * fiel das nicht auf, weil die Seite ohnehin mittig steht; auf dem Handy ist der
 * Container die ganze Breite.
 */
.section { padding-block: var(--space-7); }
.section-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-bottom: var(--space-5);
}
.section-head p { margin: 0; }

/* ------------------------------------------- Zeitraum in der Trefferzeile */

.result-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
}

.period-edit { position: relative; }

/*
 * Der Aufklapp-Pfeil des Browsers gehört hier nicht hin – die Fläche ist als
 * Knopf gestaltet, und ein Dreieck daneben sähe nach zwei Bedienelementen aus.
 */
.period-edit > summary { list-style: none; cursor: pointer; }
.period-edit > summary::-webkit-details-marker { display: none; }
.period-edit[open] > summary { border-color: var(--ink-400); background: var(--ink-50); }

.period-edit-panel {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    z-index: 60;
    width: min(320px, calc(100vw - 2 * var(--space-4)));
    padding: var(--space-4);
    background: var(--white);
    border: 1px solid var(--ink-200);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    color: var(--ink-800);
}

/*
 * Auf schmalen Geräten wird der Knopf als Anker aufgegeben: Wo genau er nach
 * dem Umbruch der Zeile landet, steht nicht fest, und ein daran ausgerichtetes
 * Feld ragte je nach Textlänge links oder rechts aus dem Bild. Bezugspunkt ist
 * dort deshalb die ganze Zeile – das Feld nimmt ihre Breite ein und liegt damit
 * immer sichtbar.
 */
@media (max-width: 640px) {
    .result-meta { position: relative; }
    .period-edit { position: static; }
    .period-edit-panel { left: 0; right: 0; width: auto; }
}

.category-chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-bottom: var(--space-5);
}

.chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 14px;
    border-radius: 999px;
    border: 1px solid var(--ink-200);
    background: var(--white);
    font-size: .875rem;
    font-weight: 520;
    color: var(--ink-600);
    transition: all var(--transition);
}

.chip:hover {
    border-color: var(--brand-500);
    color: var(--brand-700);
    text-decoration: none;
    transform: translateY(-1px);
    box-shadow: var(--shadow-sm);
}
.chip.is-active { background: var(--brand-700); border-color: var(--brand-700); color: var(--white); }

/* ------------------------------------ Filterleiste über der Trefferliste */

.catalog-filters { margin-bottom: var(--space-5); }

/*
 * Die Leiste und die Chips darunter gehören zusammen – deshalb rückt der
 * Abstand der Leiste heraus und steht einmal am Block.
 */
.catalog-filters .filter-bar { margin-bottom: var(--space-3); }

.active-filters {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.active-filters .label {
    font-size: .8125rem;
    font-weight: 600;
    color: var(--ink-500);
}

/*
 * Das × sitzt im Chip und nicht daneben: Der ganze Chip ist der Link, der
 * diesen Filter zurücknimmt. Ein eigenes Kreuz neben einem Chip, der selbst
 * woandershin führte, wären zwei Ziele in einer Fläche von zwei Zentimetern.
 */
.chip-remove span {
    font-size: 1.125rem;
    line-height: 1;
    color: var(--ink-400);
    transition: color var(--transition);
}

.chip-remove:hover { border-color: var(--accent-600); color: var(--accent-700); }
.chip-remove:hover span { color: var(--accent-600); }

.reset-filters {
    font-size: .8125rem;
    font-weight: 520;
    color: var(--ink-500);
    text-decoration: underline;
}

.reset-filters:hover { color: var(--ink-800); }

.item-grid {
    display: grid;
    gap: var(--space-5);
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}

.item-card {
    display: flex;
    flex-direction: column;
    background: var(--white);
    border: 1px solid var(--ink-200);
    border-radius: var(--radius-lg);
    overflow: hidden;
    transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
}

.item-card:hover {
    transform: translateY(-3px);
    box-shadow: var(--shadow-lg);
    border-color: var(--ink-300);
}

/*
 * Bilder stehen im Quadrat.
 *
 * Ein Lager fotografiert quer und hoch durcheinander – ein Zelt im
 * Querformat, ein Stehtisch im Hochformat. In einem 4:3-Rahmen sah die
 * Katalogliste deshalb unruhig aus: Jede Kachel schnitt anders zu. Das
 * Quadrat behandelt beide Richtungen gleich und ist zugleich das Format, in
 * dem die Bilder in der Mail (56 × 56) ohnehin schon stehen.
 *
 * Beschnitten wird weiter (`object-fit: cover`) und nicht eingepasst: Ein
 * Rahmen mit hellen Balken links und rechts sieht nach einem Fehler aus, und
 * ein Raster, in dem jedes Bild anders groß im Rahmen steht, ist kein Raster.
 */
.item-thumb {
    position: relative;
    aspect-ratio: 1;
    background: var(--ink-100);
    overflow: hidden;
}

/* Der Link füllt die Kachelfläche – klickbar ist das ganze Bild, nicht nur ein
   Teil davon. */
.item-thumb-link {
    display: block;
    width: 100%; height: 100%;
}

.item-thumb img {
    width: 100%; height: 100%;
    object-fit: cover;
    transition: transform 320ms cubic-bezier(.4, 0, .2, 1);
}

.item-card:hover .item-thumb img { transform: scale(1.04); }

.item-thumb .placeholder {
    width: 100%; height: 100%;
    display: grid;
    place-items: center;
    color: var(--ink-300);
    background: linear-gradient(135deg, var(--ink-100), var(--ink-50));
}

.item-thumb .placeholder svg { width: 48px; height: 48px; }

/*
 * Ein Artikel ohne Bild: Markenfläche mit Anfangsbuchstaben
 * (`element/item_placeholder`). Steht hinter den Regeln oben, weil sie
 * gleich spezifisch sind – die spätere gewinnt.
 *
 * Die Schrift wächst mit der Fläche (`cqi`), nicht mit dem Fenster: Dieselbe
 * Fläche ist eine Kachel, das große Galeriebild und das Vorschaubild im
 * Auftragskorb, und überall soll der Buchstabe gleich viel davon einnehmen.
 */
.placeholder.item-placeholder {
    width: 100%; height: 100%;
    display: grid;
    place-items: center;
    container-type: inline-size;
    color: rgba(255, 255, 255, .92);
    background:
        radial-gradient(circle at var(--ph-x, 78%) var(--ph-y, 18%),
            rgba(255, 255, 255, .22) 0%, transparent 46%),
        radial-gradient(rgba(255, 255, 255, .10) 1px, transparent 1.5px) 0 0 / 14px 14px,
        linear-gradient(var(--ph-angle, 150deg), var(--brand-600) 0%, var(--brand-800) 100%);
}

.item-placeholder-1 { --ph-x: 20%; --ph-y: 22%; --ph-angle: 210deg; }
.item-placeholder-2 { --ph-x: 74%; --ph-y: 82%; --ph-angle: 120deg; }
.item-placeholder-3 { --ph-x: 26%; --ph-y: 78%; --ph-angle: 30deg; }

.item-placeholder-letter {
    font-size: 34cqi;
    font-weight: 800;
    line-height: 1;
    letter-spacing: -.04em;
    text-shadow: 0 6px 24px rgba(0, 0, 0, .18);
    user-select: none;
}

.placeholder.item-placeholder svg { width: 28%; height: auto; opacity: .7; }

/* Wie das Bild: Beim Überfahren der Kachel rückt die Fläche ein wenig näher. */
.item-thumb .item-placeholder { transition: transform 320ms cubic-bezier(.4, 0, .2, 1); }
.item-card:hover .item-thumb .item-placeholder { transform: scale(1.04); }

.item-thumb .item-flag {
    position: absolute;
    top: var(--space-3);
    left: var(--space-3);
    backdrop-filter: blur(6px);
    box-shadow: var(--shadow-xs);
}

/*
 * Das Aktionsschild sitzt rechts oben und damit gegenüber dem
 * Verfügbarkeits-Badge: Beide hängen am Bild, und übereinander verdeckte eines
 * das andere.
 */
.item-thumb .item-promo-stack {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 4px;
}

.item-thumb .item-promo { box-shadow: var(--shadow-xs); }

/* „Nur noch 3 Tage" unter dem Aktionsschild – dunkel, damit es neben ihm lesbar bleibt. */
.item-thumb .item-promo-ending {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: var(--ink-900);
    color: var(--white);
    box-shadow: var(--shadow-xs);
}

.item-body {
    padding: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    flex: 1;
}

.item-body .eyebrow {
    font-size: .6875rem;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--brand-600);
}

.item-body h3 { margin: 0; font-size: 1.0625rem; }
.item-body h3 a { color: var(--ink-900); }
.item-body h3 a:hover { color: var(--brand-700); text-decoration: none; }

.item-body .desc {
    font-size: .875rem;
    color: var(--ink-500);
    margin: 0;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.item-meta {
    margin-top: auto;
    padding-top: var(--space-3);
    border-top: 1px solid var(--ink-100);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}

.price {
    font-size: 1.0625rem;
    font-weight: 700;
    color: var(--ink-900);
    font-variant-numeric: tabular-nums;
}

.price small { display: block; font-size: .6875rem; font-weight: 500; color: var(--ink-400); }

.item-actions { padding: 0 var(--space-4) var(--space-4); display: flex; gap: var(--space-2); }
.item-actions .btn { flex: 1; }
.item-actions form { flex: 1; display: flex; margin: 0; }

/* --------------------------------------------------------- Artikeldetail */

/*
 * Drei Bereiche, und ihre Reihenfolge ist der ganze Sinn der Sache.
 *
 * Am Schreibtisch steht die Buchungsspalte rechts neben Kopf und Rumpf und
 * klebt dort. Einspaltig stünde sie hinter allem – gemessen lag der Preis
 * 4951 Pixel tief, also siebeneinhalb Bildschirme. `grid-template-areas`
 * schiebt sie deshalb zwischen Titel und Beschreibung, ohne dass im Markup
 * etwas doppelt vorkommt oder umsortiert wird.
 *
 * `row-gap` und `column-gap` getrennt: Waagerecht trennen 48 Pixel die
 * Spalten, senkrecht sind es 32 – dieselben, die `.stack-lg` zwischen den
 * Karten lässt. Ein gemeinsames `gap` riss zwischen Titel und Beschreibung
 * eine Lücke auf, die es zwischen zwei Karten nicht gibt.
 */
.detail {
    display: grid;
    column-gap: var(--space-7);
    row-gap: var(--space-6);
    grid-template-columns: minmax(0, 1.35fr) minmax(320px, .85fr);
    grid-template-areas:
        "head panel"
        "body panel";
    align-items: start;
    padding: var(--space-6) 0 var(--space-8);
}

.detail-head { grid-area: head; }
.detail-body { grid-area: body; }

/*
 * `minmax(0, 1fr)` statt `1fr`: eine Grid-Spalte schrumpft sonst nie unter die
 * Mindestbreite ihres Inhalts. Eine breite Positionstabelle schöbe damit die
 * ganze Seite waagerecht auf, statt in ihrem eigenen `.table-wrap` zu scrollen.
 */
@media (max-width: 960px) {
    .detail {
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas:
            "head"
            "panel"
            "body";
    }
}

.breadcrumb {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
    font-size: .8125rem;
    color: var(--ink-400);
    padding: var(--space-4) 0 0;
}

.breadcrumb a { color: var(--ink-500); }
.breadcrumb span { color: var(--ink-300); }

/*
 * Das Quadrat gilt auf jeder Breite – auch auf dem Handy.
 *
 * Hier stand einmal 4:3, weil ein quadratisches Bild über die volle
 * Spaltenbreite hoch baut: Bei 390 Pixeln Fensterbreite sind das rund 342
 * Pixel Höhe statt 256, und zusammen mit Kopfzeile, Kategoriezeile und
 * Brotkrumen rückt der Artikelname damit an die untere Bildschirmkante.
 *
 * Dagegen steht der Ausschnitt, und der wiegt schwerer: Ein Lager
 * fotografiert quer und hoch durcheinander, und in einem flacheren Rahmen
 * schneidet jedes Hochformat anders zu. Der Kunde sähe auf der Artikelseite
 * einen anderen Ausschnitt als auf der Kachel, über die er hergekommen ist –
 * und auf dem Handy ist das Bild fast die ganze erste Ansicht. Ein Format für
 * beide Stellen ist die ruhigere Seite, und es ist dasselbe, in dem die
 * Bildauswahl darunter und die Vorschau in der Mail ohnehin schon stehen.
 *
 * `object-fit: cover` bleibt: beschnitten, nicht eingepasst – dieselbe Regel
 * wie bei der Katalogkachel.
 */
.gallery-main {
    aspect-ratio: 1;
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--ink-100);
    border: 1px solid var(--ink-200);
}

.gallery-main img { width: 100%; height: 100%; object-fit: cover; }

.gallery-main .placeholder {
    width: 100%; height: 100%;
    display: grid; place-items: center;
    color: var(--ink-300);
}
.gallery-main .placeholder svg { width: 72px; height: 72px; }

.gallery-thumbs {
    display: flex;
    gap: var(--space-2);
    margin-top: var(--space-3);
    flex-wrap: wrap;
}

/* Auch die Auswahl darunter im Quadrat – sie zeigt dasselbe Bild wie der
   Rahmen darüber und muss denselben Ausschnitt zeigen. */
.gallery-thumbs button {
    width: 72px; height: 72px;
    padding: 0;
    border: 2px solid var(--ink-200);
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--ink-100);
    cursor: pointer;
    transition: border-color var(--transition);
}

.gallery-thumbs button img { width: 100%; height: 100%; object-fit: cover; }
.gallery-thumbs button:hover { border-color: var(--ink-400); }
.gallery-thumbs button[aria-current="true"] { border-color: var(--brand-600); }

.spec-list {
    display: grid;
    gap: 0;
    margin: 0;
}

.spec-list div {
    display: flex;
    justify-content: space-between;
    gap: var(--space-4);
    padding: 10px 0;
    border-bottom: 1px solid var(--ink-100);
    font-size: .9375rem;
}

.spec-list div:last-child { border-bottom: 0; }
.spec-list dt { color: var(--ink-500); margin: 0; }
.spec-list dd { margin: 0; font-weight: 550; color: var(--ink-800); text-align: right; }

/*
 * Häufige Fragen
 *
 * Frage und Antwort stehen untereinander und nicht nebeneinander: Eine Antwort
 * ist ein Satz und keine Angabe, und in einer rechten Spalte wie bei den
 * Details bräche sie über fünf Zeilen um.
 */
.faq-list { display: grid; gap: var(--space-5); }
.faq-item h3 { margin: 0 0 4px; font-size: 1rem; color: var(--ink-800); }
.faq-item p { margin: 0; color: var(--ink-600); font-size: .9375rem; }

/*
 * Auf der Artikelseite als Akkordeon (Marc, 05.10.2026): Jede Frage klappt
 * einzeln auf. Die Antworten stehen trotzdem im Markup und als `FAQPage` in
 * den strukturierten Daten – zugeklappt wird erst im Browser
 * (`initFaqAccordion()`), ohne Skript bleibt alles offen.
 *
 * Die ganze Zeile ist die Fläche zum Tippen, und das Plus rechts dreht sich zum
 * Minus. Die Linie zwischen den Fragen ersetzt den Abstand der offenen Liste:
 * Zugeklappt sind es nur noch Überschriften, und die brauchen eine Kante.
 */
.faq-accordion { gap: 0; }

.faq-accordion .faq-item { border-bottom: 1px solid var(--ink-100); }
.faq-accordion .faq-item:last-child { border-bottom: 0; }

.faq-accordion summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) 0;
    cursor: pointer;
    list-style: none;
}

.faq-accordion summary::-webkit-details-marker { display: none; }
.faq-accordion summary h3 { margin: 0; }
.faq-accordion summary:hover h3 { color: var(--brand-700); }

.faq-accordion summary:focus-visible {
    outline: 2px solid var(--brand-600);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/* Plus aus zwei Strichen; offen fällt der senkrechte um und es bleibt ein Minus. */
.faq-accordion summary::after {
    content: "";
    flex: none;
    width: 14px;
    height: 14px;
    background:
        linear-gradient(currentColor, currentColor) center / 100% 2px no-repeat,
        linear-gradient(currentColor, currentColor) center / 2px 100% no-repeat;
    color: var(--ink-500);
    transition: transform .25s ease, background-size .25s ease;
}

.faq-accordion details[open] > summary::after {
    transform: rotate(180deg);
    background-size: 100% 2px, 2px 0;
}

.faq-accordion .faq-item p { padding-bottom: var(--space-4); }

.faq-accordion details[open] > p { animation: faq-open .25s ease both; }

@keyframes faq-open {
    from { opacity: 0; transform: translateY(-4px); }
}

/*
 * Lange Beschreibung mit „Weiterlesen" (`initReadMore()`).
 *
 * Eingeklappt läuft der Text nach unten in den Kartengrund aus, statt mitten
 * in einer Zeile abzureißen – so sieht man, dass noch etwas kommt. Die Klasse
 * setzt nur das Skript; ohne es steht der ganze Text da.
 */
.read-more.is-collapsed [data-read-more-body] {
    max-height: 320px;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(to bottom, #000 60%, transparent);
    mask-image: linear-gradient(to bottom, #000 60%, transparent);
}

.read-more-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: var(--space-3);
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-weight: 600;
    color: var(--brand-700);
    cursor: pointer;
}

.read-more-toggle::after {
    content: "";
    width: 7px;
    height: 7px;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: translateY(-2px) rotate(45deg);
    transition: transform .2s ease;
}

.read-more-toggle[aria-expanded="true"]::after { transform: translateY(2px) rotate(-135deg); }
.read-more-toggle:hover { text-decoration: underline; }

/* „Das könnte auch passen" als Reihe zum Blättern, wie die Kategoriereihen. */
.related-items .section-head { align-items: center; }
.related-items .section-head h2 { margin: 0; }

/*
 * „Zuletzt angesehen" (`initRecentlyViewed()`): kleine Einträge mit Bild und
 * Namen, kein Preis. Waagerecht zum Wischen, ohne Rastpunkte und Pfeile –
 * es sind höchstens sechs, und sie sind ein Rückweg, kein Schaufenster.
 */
.related-items + .recent-items { padding-top: 0; }

.recent-list {
    display: flex;
    gap: var(--space-3);
    margin: 0;
    padding: 2px 2px var(--space-2);
    list-style: none;
    overflow-x: auto;
    position: relative;
}

.recent-list li { flex: none; width: 140px; }

.recent-item {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-2);
    border: 1px solid var(--ink-200);
    border-radius: var(--radius);
    background: var(--white);
    color: var(--ink-800);
    text-decoration: none;
    transition: border-color .15s ease, box-shadow .15s ease, transform .15s ease;
}

.recent-item:hover {
    border-color: var(--brand-500);
    box-shadow: var(--shadow-sm);
    transform: translateY(-2px);
}

.recent-thumb {
    display: block;
    position: relative;
    aspect-ratio: 1;
    border-radius: var(--radius-sm);
    background: var(--ink-100);
    overflow: hidden;
}

.recent-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }

.recent-thumb.is-empty {
    display: grid;
    place-items: center;
    background: linear-gradient(135deg, var(--brand-600), var(--brand-800));
}

.recent-thumb.is-empty::before {
    content: attr(data-initial);
    font-size: 2.25rem;
    font-weight: 800;
    color: rgba(255, 255, 255, .9);
}

.recent-thumb.is-empty img { position: absolute; opacity: 0; }

.recent-name {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: .8125rem;
    font-weight: 600;
    line-height: 1.35;
}

/* Inhalt eines Sets */

/*
 * Die Menge steht in einer eigenen, rechtsbündigen Spalte fester Breite:
 * „1 ×" und „12 ×" untereinander sollen an derselben Kante enden, sonst
 * franst die Liste aus und lässt sich nicht mehr überfliegen.
 */
.set-contents {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0;
}

.set-contents li {
    display: grid;
    grid-template-columns: 3.25rem 1fr;
    gap: var(--space-3);
    padding: 10px 0;
    border-bottom: 1px solid var(--ink-100);
    font-size: .9375rem;
}

.set-contents li:last-child { border-bottom: 0; }

.set-contents .set-qty {
    text-align: right;
    font-weight: 650;
    color: var(--ink-800);
    font-variant-numeric: tabular-nums;
}

.set-contents .set-name { display: grid; gap: 2px; }
.set-contents .set-name small { font-size: .8125rem; }

/* Einzeiler „Enthält: …" in Kachel, Korb und Formular */
.set-lines { font-size: .8125rem; color: var(--ink-500); }
.set-lines .set-lines-label { font-weight: 600; color: var(--ink-600); }

/* Buchungs-Panel rechts */
.booking-panel {
    grid-area: panel;
    position: sticky;
    top: 96px;
}

/*
 * Der Preisbalken am unteren Rand – nur einspaltig.
 *
 * Zweispaltig klebt die Buchungsspalte ohnehin im Blick; ein zweiter Preis
 * daneben wäre dieselbe Zahl zweimal auf einem Bildschirm. Einspaltig steht
 * sie oben, und wer bis zu den häufigen Fragen gelesen hat, hat sie weit
 * hinter sich.
 *
 * Die Fußzeile bekommt dafür unten Luft: Ein fest positionierter Balken deckt
 * sonst ihre letzten Zeilen zu, und weiterscrollen kann man am Seitenende
 * nicht mehr. `:has()` beantwortet das dort, wo der Balken wirklich steht –
 * er kommt nur auf der Artikelseite vor. Wo `:has()` fehlt, bleibt der
 * Balken, und nur die letzte Zeile der Fußzeile liegt darunter.
 *
 * `env(safe-area-inset-bottom)` hält ihn über dem Balken, mit dem iPhones
 * unten ihre Gestensteuerung anzeigen.
 */
.booking-bar { display: none; }

@media (max-width: 960px) {
    .booking-bar {
        display: flex;
        position: fixed;
        inset: auto 0 0 0;
        z-index: 40;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-4);
        padding: 10px var(--space-5);
        padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
        background: var(--white);
        border-top: 1px solid var(--ink-200);
        box-shadow: 0 -2px 14px rgb(16 24 32 / .1);
    }

    .booking-bar-price { line-height: 1.25; }
    .booking-bar-price strong { font-size: 1.125rem; font-weight: 700; color: var(--ink-900); }
    .booking-bar-price span { display: block; font-size: .75rem; color: var(--ink-500); }

    body:has(.booking-bar) .site-footer { padding-bottom: 84px; }
}

/*
 * Ein Sprungziel unter einer klebenden Kopfzeile.
 *
 * Der Anker des Balkens führt auf die Buchungsspalte, und die läge sonst genau
 * unter der Kopfzeile, die oben klebt. Dieselbe Rechnung gilt für den Verweis
 * auf die Staffelpreise.
 */
.booking-panel,
#staffelpreise { scroll-margin-top: 88px; }

/*
 * Einspaltig klebt sie nicht.
 *
 * `sticky` hat dort nichts mehr, woran es kleben könnte: Die Karte ist höher
 * als der Bildschirm, und was höher ist als sein Fenster, scrollt ohnehin
 * mit. Übrig bliebe allein die Rechenarbeit – und ein Element, das der
 * Browser bei jedem Scrollschritt neu einordnen muss.
 */
@media (max-width: 960px) {
    .booking-panel { position: static; }
}

.booking-price {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    margin-bottom: var(--space-4);
}

.booking-price .amount {
    font-size: 1.75rem;
    font-weight: 700;
    color: var(--ink-900);
    letter-spacing: -.02em;
}

.booking-price .per { color: var(--ink-500); font-size: .875rem; }

.quantity-input { max-width: 110px; }

/* ------------------------------------------------------------ Anfragekorb */

.basket-lines { display: flex; flex-direction: column; }

.basket-line {
    display: grid;
    grid-template-columns: 88px 1fr auto auto auto;
    gap: var(--space-4);
    align-items: center;
    padding: var(--space-4) 0;
    border-bottom: 1px solid var(--ink-100);
}

.basket-line:last-child { border-bottom: 0; }

/*
 * Schmal bekommt die Zeilensumme ihre eigene Zeile neben der Menge: Sie
 * gehört zu ihr – „2 ×" links, „38,00 €" rechts liest sich als eine Aussage.
 * Der Papierkorb rutscht dafür darunter; er ist der seltenste Griff der drei.
 */
@media (max-width: 640px) {
    .basket-line {
        grid-template-columns: 64px minmax(0, 1fr);
        grid-template-areas:
            "thumb   info"
            "qty     sum"
            "actions actions";
    }
    .basket-line .bl-thumb { grid-area: thumb; }
    .basket-line .bl-info { grid-area: info; }
    .basket-line .bl-qty { grid-area: qty; }
    .basket-line .bl-sum { grid-area: sum; }
    .basket-line .bl-actions { grid-area: actions; justify-self: end; }
}

/*
 * Die Zeilensumme: Menge mal Miettage mal Preis, ausgerechnet. Rechtsbündig,
 * damit sich die Beträge der Positionen untereinander vergleichen lassen –
 * und damit sie sichtbar zur Summe darunter gehören.
 */
.bl-sum {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
    min-width: 5.5rem;
    text-align: right;
}

.bl-sum-amount { font-weight: 700; color: var(--ink-900); white-space: nowrap; }
.bl-sum-calc { font-size: .75rem; color: var(--ink-500); white-space: nowrap; }

.bl-thumb {
    width: 88px; height: 88px;
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--ink-100);
    flex: none;
}

/* Der Link füllt die Vorschau, damit das ganze Bild klickbar ist. */
.bl-thumb a { display: block; width: 100%; height: 100%; }

.bl-thumb img { width: 100%; height: 100%; object-fit: cover; }
.bl-info h4 { margin: 0 0 2px; font-size: .9688rem; }
.bl-info h4 a { color: var(--ink-900); }
.bl-qty input { width: 84px; text-align: center; }

.summary-row {
    display: flex;
    justify-content: space-between;
    gap: var(--space-4);
    padding: 8px 0;
    font-size: .9375rem;
}

.summary-row.total {
    border-top: 1px solid var(--ink-200);
    margin-top: var(--space-2);
    padding-top: var(--space-3);
    font-size: 1.125rem;
    font-weight: 700;
    color: var(--ink-900);
}

/*
 * Der Gesamtpreis setzt sich von allem daneben ab: In der Buchungsspalte
 * stehen darüber Zeitraum und Menge, also die Eingaben – hier steht die
 * Antwort darauf. Ohne die abgesetzte Fläche liest sich beides als eine
 * Fortsetzung des Formulars.
 *
 * Kein eigener Abstand nach oben: Der Block ist ein Kind von `.stack`, und
 * zwei Abstände übereinander sind einer zu viel.
 */
.price-total {
    padding: var(--space-4);
    background: var(--ink-50);
    border: 1px solid var(--ink-200);
    border-radius: var(--radius-lg);
}

/*
 * Die Rechnung über der Aufstellung – „3 × 2 Miettage à 9,50 €". Sie steht
 * als Satz und nicht als Zeile mit Betrag: Ohne Nachlass wäre dieser Betrag
 * derselbe wie die Endsumme darunter, und zwei Stellen mit derselben Zahl
 * sind eine zu viel.
 */
.price-calc {
    margin: 0 0 var(--space-2);
    font-size: .8125rem;
    color: var(--ink-500);
}

.price-note {
    font-size: .75rem;
    margin: var(--space-2) 0 0;
}

/*
 * Die Kaution steht **unter** dem Strich, nicht in der Summe: Sie ist kein
 * Teil des Mietpreises, sondern eine Sicherheit, die wieder hinausgeht. In
 * derselben Aufstellung wie die Beträge addierte der Blick sie zwangsläufig
 * mit; abgesetzt und mit ihrem Satz daneben tut er das nicht.
 */
.deposit-row {
    align-items: flex-start;
    margin-top: var(--space-1);
    padding-top: var(--space-3);
    border-top: 1px dashed var(--ink-200);
}

.deposit-hint {
    display: block;
    font-size: .75rem;
    color: var(--ink-500);
}

/*
 * „Ein Miettag ist eine Nacht" gehört direkt unter den großen Preis und nicht
 * einen Abstand weiter: Es ist die Einheit dieser Zahl und kein eigener
 * Hinweis. Der Abstand der Preiszeile weicht dafür.
 */
.booking-price:has(+ .price-unit-note) { margin-bottom: 4px; }

.price-unit-note {
    margin: 0;
    font-size: .8125rem;
    color: var(--ink-500);
}

/* --------------------------------------------------------- Anfrageformular */

.inquiry-layout {
    display: grid;
    gap: var(--space-6);
    grid-template-columns: minmax(0, 1.5fr) minmax(300px, .8fr);
    align-items: start;
    padding: var(--space-6) 0 var(--space-8);
}

@media (max-width: 900px) {
    .inquiry-layout { grid-template-columns: minmax(0, 1fr); }
}

.inquiry-summary { position: sticky; top: 96px; }

/*
 * Ein Formularblock, der sich zuklappen lässt. Das `summary` übernimmt dabei
 * die Rolle des `legend` und sieht deshalb aus wie eines – ein zweiter Stil
 * für dieselbe Zeile wäre eine Überschrift, die je nach Block anders aussieht.
 *
 * `list-style: none` **und** `::-webkit-details-marker`: Safari hört bis heute
 * nur auf das zweite, und wer eines wegkürzt, hat auf der Hälfte der Geräte
 * zwei Pfeile nebeneinander. Dieselbe Regel wie bei `.card-collapsible`.
 */
.fieldset-collapsible {
    margin: 0 0 var(--space-5);
}

.fieldset-collapsible > summary {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
    list-style: none;
    user-select: none;
    margin-bottom: var(--space-3);
    font-size: .8125rem;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--ink-500);
}

.fieldset-collapsible > summary::-webkit-details-marker { display: none; }

.fieldset-collapsible > summary .muted {
    font-weight: 500;
    text-transform: none;
    letter-spacing: 0;
}

.fieldset-collapsible > summary::after {
    content: "";
    width: 8px;
    height: 8px;
    margin-left: auto;
    border-right: 2px solid var(--ink-400);
    border-bottom: 2px solid var(--ink-400);
    transform: rotate(45deg) translate(-2px, -2px);
    transition: transform var(--transition);
}

.fieldset-collapsible[open] > summary::after {
    transform: rotate(-135deg) translate(-2px, -2px);
}

.fieldset-collapsible > summary:hover::after { border-color: var(--ink-600); }

.thanks {
    max-width: 640px;
    margin: var(--space-8) auto;
    text-align: center;
}

.thanks .check {
    width: 72px; height: 72px;
    margin: 0 auto var(--space-5);
    border-radius: 50%;
    background: var(--ok-100);
    color: var(--ok-500);
    display: grid;
    place-items: center;
}

.thanks .check svg { width: 36px; height: 36px; }

.reference-box {
    display: inline-block;
    padding: var(--space-3) var(--space-5);
    border-radius: var(--radius);
    background: var(--ink-50);
    border: 1px dashed var(--ink-300);
    font-family: var(--font-mono);
    font-size: 1.125rem;
    font-weight: 600;
    color: var(--brand-700);
    letter-spacing: .04em;
}

/* ------------------------------------------------------------- Übergabe */

.handover-grid {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

.handover {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3);
    border: 1px solid var(--ink-200);
    border-radius: var(--radius);
    background: var(--white);
}

/* Die Karte darunter braucht die volle Breite der Karte, nicht die ihres Textes. */
.handover > div { flex: 1; min-width: 0; }

.handover svg { width: 20px; height: 20px; flex: none; color: var(--brand-600); margin-top: 2px; }
.handover strong { display: block; font-size: .9375rem; margin-bottom: 3px; }
.handover span { display: block; font-size: .8125rem; line-height: 1.5; color: var(--ink-700); }
.handover span.muted { color: var(--ink-500); }

/* Die Karte darin hat eigene Spans (Attribution) – für sie gilt das nicht. */
.handover .leaflet-container span { display: inline; font-size: inherit; line-height: inherit; color: inherit; }

/*
 * Mehrere Lager untereinander brauchen eine sichtbare Grenze – sonst liest sich
 * die zweite Adresse als Fortsetzung der ersten.
 */
.handover-sep {
    display: block;
    height: 1px;
    margin: var(--space-2) 0;
    background: var(--ink-200);
}

.handover .handover-site {
    font-size: .8125rem;
    margin-bottom: 1px;
    color: var(--ink-800);
}

/* Abholort zur Auswahl: die ganze Zeile ist anklickbar. */
.pickup-option {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    padding: var(--space-2) 0;
    cursor: pointer;
}

.pickup-option input { margin-top: 4px; flex: none; }
.pickup-option + .pickup-option { border-top: 1px solid var(--ink-200); }

.pickup-locations {
    padding: var(--space-3);
    border: 1px solid var(--ink-200);
    border-radius: var(--radius);
    background: var(--ink-50);
    font-size: .875rem;
}

.pickup-locations .label { display: block; margin-bottom: var(--space-2); }
.pickup-locations p + p { margin-top: var(--space-2); }

/*
 * Zusatzleistungen im Auftragsformular.
 *
 * Der Preis steht rechts auf derselben Zeile wie der Name und nicht darunter:
 * Angekreuzt wird nach „was kostet das?", und untereinander müsste der Blick
 * für jede Zeile zweimal springen. Auf schmalen Geräten bricht er um – ein
 * Betrag, der neben einem abgeschnittenen Namen klebt, ist schlechter als
 * einer in der zweiten Zeile.
 */
.addon-option {
    align-items: flex-start;
    padding: var(--space-3);
    border: 1px solid var(--ink-200);
    border-radius: var(--radius);
    margin-bottom: 0;
}

.addon-option:hover { border-color: var(--brand-500); }
.addon-option:has(input:checked) { border-color: var(--brand-600); background: var(--ink-50); }

.addon-option > span {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0 var(--space-2);
    width: 100%;
}

.addon-option .addon-price { margin-left: auto; white-space: nowrap; font-weight: 600; }
.addon-option .addon-price .muted { font-weight: 400; }
.addon-option .help { flex-basis: 100%; margin: var(--space-1) 0 0; }

.handover.is-off { background: var(--ink-50); border-style: dashed; }
.handover.is-off svg { color: var(--ink-400); }
.handover.is-off strong { color: var(--ink-500); }

/*
 * Nur ein Übergabeweg möglich: Er nimmt die ganze Zeile ein und steht vorn,
 * der andere folgt als schmale Zeile ohne Icon-Gewicht. Die Karte darf in der
 * vollen Breite höher werden – erst dann zeigt sie mehr als die Straße.
 */
.handover-grid.is-single { grid-template-columns: 1fr; }
.handover-grid.is-single > .is-off { order: 1; padding: var(--space-2) var(--space-3); }
.handover-grid.is-single > .is-off strong { margin-bottom: 0; }
.handover-grid.is-single .location-map-consent,
.handover-grid.is-single .location-map-frame { height: 300px; }

@media (max-width: 640px) {
    .handover-grid.is-single .location-map-consent,
    .handover-grid.is-single .location-map-frame { height: 240px; }
}

/* --------------------------------------------------------- Kundenportal */

.portal-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
    padding-bottom: var(--space-5);
    margin-bottom: var(--space-5);
    border-bottom: 1px solid var(--ink-200);
}

.portal-head h1 { margin: 4px 0 6px; }

.portal .eyebrow {
    font-size: .6875rem;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--brand-600);
}

/*
 * Die gemieteten Artikel im Portal: Bild, Name mit Menge und Preis, Summe
 * rechts. Darunter die Zusatzleistungen als Tabelle und die Summe.
 *
 * Auf dem Handy bleiben es drei Spalten, nur das Bild wird kleiner: Die
 * Summe unter den Namen zu schieben hieße, dass sie bei jedem Artikel an
 * einer anderen Höhe steht – rechts bündig lässt sie sich überfliegen.
 */
.portal-items {
    list-style: none;
    margin: 0;
    padding: 0 var(--space-5);
}

.portal-item {
    display: grid;
    grid-template-columns: 104px minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-4) 0;
    border-bottom: 1px solid var(--ink-100);
}

.portal-item:last-child { border-bottom: 0; }

.portal-item-thumb {
    display: block;
    aspect-ratio: 1;
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--ink-100);
}

.portal-item-thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.portal-item-thumb img { transition: transform 320ms cubic-bezier(.4, 0, .2, 1); }
a.portal-item-thumb:hover img { transform: scale(1.04); }

.portal-item-body {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.portal-item-name {
    font-weight: 650;
    font-size: 1.0625rem;
    line-height: 1.3;
    overflow-wrap: anywhere;
}

.portal-item-name a { color: inherit; }
.portal-item-name a:hover { color: var(--brand-700); }

.portal-item-meta { color: var(--ink-600); font-size: .9375rem; font-variant-numeric: tabular-nums; }

.portal-item-note,
.portal-item-body .set-lines { color: var(--ink-500); font-size: .8125rem; }

.portal-item-total {
    font-weight: 650;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    text-align: right;
}

.portal-subhead {
    margin: var(--space-4) var(--space-5) var(--space-2);
    font-size: .8125rem;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--ink-500);
}

.portal-items + .table-wrap { border-top: 1px solid var(--ink-200); }

.portal-totals { border-top: 1px solid var(--ink-200); }
.portal-totals tbody tr:hover { background: none; }
.portal-totals td { border-bottom: 0; padding-top: var(--space-2); padding-bottom: var(--space-2); }
.portal-totals td.num { width: 1%; white-space: nowrap; }
.portal-totals-sum td { font-size: 1.0625rem; padding-top: var(--space-3); padding-bottom: var(--space-4); }

/* Tabelle und Summe an derselben Kante wie die Artikel darüber. */
.portal-services th:first-child, .portal-services td:first-child,
.portal-totals td:first-child { padding-left: var(--space-5); }
.portal-services th:last-child, .portal-services td:last-child,
.portal-totals td:last-child { padding-right: var(--space-5); }

.portal-service-meta { display: none; }

@media (max-width: 640px) {
    .portal-items { padding: 0 var(--space-4); }
    .portal-services th:nth-child(-n+1), .portal-services td:nth-child(-n+1),
    .portal-services th:nth-child(3), .portal-services td:nth-child(3) { display: none; }
    .portal-services th:nth-child(2), .portal-services td:nth-child(2),
    .portal-totals td:first-child { padding-left: var(--space-4); }
    .portal-services th:last-child, .portal-services td:last-child,
    .portal-totals td:last-child { padding-right: var(--space-4); }
    .portal-services td:last-child { white-space: nowrap; vertical-align: top; font-weight: 650; }
    .portal-service-meta { display: block; color: var(--ink-500); font-size: .8125rem; }
    .portal-item { grid-template-columns: 72px minmax(0, 1fr) auto; gap: var(--space-3); }
    .portal-item-name { font-size: 1rem; }
    .portal-subhead { margin-left: var(--space-4); margin-right: var(--space-4); }
}

/*
 * Zwei Zeitfelder nebeneinander – auf dem Handy untereinander, sonst wird die
 * Uhrzeit im Feld abgeschnitten.
 */
.time-pair {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: 1fr 1fr;
}

@media (max-width: 420px) {
    .time-pair { grid-template-columns: 1fr; }
}

/*
 * Menge und „Verfügbarkeit prüfen" in einer Zeile.
 *
 * Die Buchungsspalte war höher als ein Laptopbildschirm, und weil sie klebt,
 * lag „Unverbindlich anfragen" bis zum Seitenende darunter. Jede gesparte
 * Zeile ist hier ein Stück Weg zum Knopf. Der Knopf steht auf der Grundlinie
 * des Feldes, nicht auf der Höhe seines Labels.
 */
.qty-check {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--space-3);
    align-items: end;
}

.booking-panel .qty-check .field { margin-bottom: 0; }
.qty-check .quantity-input { width: 88px; }

/* So hoch wie das Feld daneben, sonst steht der Knopf eine Stufe tiefer. */
.qty-check .btn { min-height: 46px; }

/*
 * Die Buchungsspalte rückt enger zusammen als eine gewöhnliche Karte – sie
 * soll am Schreibtisch samt Knopf möglichst in ein Fenster passen.
 */
.booking-panel .card-body.stack > * + * { margin-top: var(--space-3); }
.booking-panel .field { margin-bottom: var(--space-3); }
.booking-panel .time-pair .field,
.booking-panel .field:last-child { margin-bottom: 0; }
.booking-panel .time-pair { margin-bottom: var(--space-3); }

/*
 * In der Buchungsspalte bleiben Datum und Uhrzeit auch auf dem Handy paarweise
 * nebeneinander: Die Karte ist dort volle Breite, und „06.11.2026" passt in die
 * halbe Spalte. Untereinander waren es vier Felder hoch, und der Knopf lag
 * einen Bildschirm weiter unten. Erst auf ganz schmalen Geräten wird gestapelt.
 */
@media (max-width: 420px) {
    .booking-panel .time-pair { grid-template-columns: 1fr 1fr; gap: var(--space-2); }
    .booking-panel input[type="date"],
    .booking-panel input[type="time"] { padding-inline: 8px; }
}

@media (max-width: 359px) {
    .booking-panel .time-pair { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------------ Kundenkonto */

.account-narrow { max-width: 520px; margin: 0 auto; }
.account-narrow .eyebrow,
.account .eyebrow {
    font-size: .6875rem;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--brand-600);
}

.account-narrow h1 { margin: var(--space-2) 0 var(--space-3); }
.account-narrow .lead { color: var(--ink-600); margin-bottom: var(--space-5); font-size: 1rem; }

.account-actions { display: flex; gap: var(--space-2); flex-wrap: wrap; }

/* Der Code wird abgetippt oder eingefügt – große Ziffern mit viel Abstand
   lassen sich dabei am besten kontrollieren. */
.code-input {
    font-family: var(--font-mono, consolas, monospace);
    font-size: 1.75rem;
    letter-spacing: .35em;
    text-align: center;
    padding-inline: var(--space-3);
}

.order-card {
    border: 1px solid var(--ink-200);
    border-radius: var(--radius-lg);
    background: var(--white);
    padding: var(--space-5);
}

.order-card + .order-card { margin-top: var(--space-4); }

.order-card-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-bottom: var(--space-4);
}

.order-card-head h2 { font-size: 1.125rem; }

.order-lines { list-style: none; margin: 0 0 var(--space-4); padding: 0; font-size: .9375rem; }
.order-lines li { padding: 6px 0; border-bottom: 1px solid var(--ink-100); }
.order-lines li:last-child { border-bottom: 0; }

.order-card-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    flex-wrap: wrap;
    padding-top: var(--space-3);
    border-top: 1px solid var(--ink-200);
}

/*
 * Der Kopf der Kontoseite: drei Antworten, bevor die Liste kommt.
 * Ein Raster aus drei Kacheln, auf dem Handy untereinander – dort ist „Als
 * Nächstes" die wichtigste und steht deshalb auch im Markup vorn.
 */
.account-overview {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    margin-bottom: var(--space-5);
}

.account-stat {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: var(--space-4) var(--space-5);
    border: 1px solid var(--ink-200);
    border-radius: var(--radius-lg);
    background: var(--white);
    position: relative;
    overflow: hidden;
}

.account-stat::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 3px;
    background: var(--brand-600);
}

.account-stat.is-due::before { background: var(--warn-500); }
.account-stat.is-clear::before { background: var(--ok-500); }

.account-stat-next {
    background: linear-gradient(135deg, var(--brand-50, #eef7f9), var(--white) 70%);
}

.account-stat-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: .6875rem;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--ink-500);
}

.account-stat-label svg { width: 14px; height: 14px; }

.account-stat-value {
    font-size: 1.375rem;
    font-weight: 700;
    color: var(--ink-900);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.account-stat-hint { font-size: .8125rem; color: var(--ink-500); }

.account-tasks { margin-bottom: var(--space-5); border-left: 3px solid var(--accent-500); }
.account-tasks-title { font-size: 1rem; margin: 0 0 var(--space-3); }

.account-task-list { list-style: none; margin: 0; padding: 0; }
.account-task {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    flex-wrap: wrap;
    padding: var(--space-2) 0;
}
.account-task + .account-task { border-top: 1px solid var(--ink-100); }
.account-task-text { display: flex; flex-direction: column; font-size: .9375rem; }
.account-task-text .muted { font-size: .8125rem; }

/*
 * Der Stand eines Auftrags als Leiste. Die Linie zwischen den Punkten ist der
 * Rand des Listenpunkts selbst – ohne zusätzliche Elemente, und damit ohne
 * etwas, das ein Screenreader vorlesen müsste.
 */
/* Fünf bis sieben Stationen – je nachdem, ob Unterschrift und Zahlung dazugehören. */
.order-progress {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    list-style: none;
    margin: 0 0 var(--space-4);
    padding: 0;
    counter-reset: none;
}

.order-progress li {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    text-align: center;
    font-size: .75rem;
    color: var(--ink-500);
}

.order-progress li + li::before {
    content: "";
    position: absolute;
    top: 6px;
    right: 50%;
    width: 100%;
    height: 2px;
    background: var(--ink-200);
    z-index: 0;
}

.order-progress li.is-done + li::before,
.order-progress li.is-done + li.is-current::before { background: var(--brand-600); }

.order-progress-dot {
    position: relative;
    z-index: 1;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: var(--white);
    border: 2px solid var(--ink-300);
}

.order-progress .is-done .order-progress-dot { background: var(--brand-600); border-color: var(--brand-600); }
.order-progress .is-current .order-progress-dot {
    border-color: var(--brand-600);
    box-shadow: 0 0 0 4px var(--brand-100, #d6ecf1);
    animation: progress-pulse 2.4s ease-in-out infinite;
}
.order-progress .is-current { color: var(--ink-900); font-weight: 600; }

@keyframes progress-pulse {
    50% { box-shadow: 0 0 0 7px transparent; }
}

/* Eine Auftragskarte hebt sich beim Überfahren leicht ab – sie ist anklickbar. */
.order-card { transition: box-shadow .2s ease, transform .2s ease; }
.order-card:hover { box-shadow: var(--shadow-md, 0 6px 20px rgba(15, 23, 42, .08)); }

@media (max-width: 480px) {
    .order-progress-label { font-size: .6875rem; }
}

/* ------------------------------------------------------------ 404-Seite */

.not-found {
    max-width: 720px;
    margin: 0 auto;
    text-align: center;
    padding: var(--space-7) 0;
}

.not-found .eyebrow {
    font-size: .6875rem;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--brand-600);
}

.not-found h1 { margin: var(--space-2) 0 var(--space-3); }
.not-found .lead { color: var(--ink-600); margin-bottom: var(--space-6); }

/*
 * Das Suchfeld steht hier auf hellem Grund, nicht wie im Hero auf dunklem –
 * die Beschriftung darüber wäre sonst weiß auf weiß.
 */
.not-found-search .search-field label { color: var(--ink-700); }
.not-found-search .field { margin-bottom: 0; }

/*
 * Hildegard, das Easteregg der Suche: Sie liegt mit den Pfoten auf dem
 * Suchfeld. Der negative Abstand schiebt die Pfoten über den Rand, und sie
 * fängt keine Klicks ab, damit das Feld darunter benutzbar bleibt.
 */
.not-found-search .hildegard {
    position: relative;
    z-index: 2;
    display: block;
    width: 240px;
    max-width: 60%;
    height: auto;
    margin: 0 auto -50px;
    pointer-events: none;
    filter: drop-shadow(0 6px 10px rgba(15, 23, 42, .18));
}

.not-found-categories { margin-top: var(--space-6); }
.not-found-categories .label { display: block; margin-bottom: var(--space-3); }
.not-found-categories .category-chips { justify-content: center; margin-bottom: 0; }

/* ------------------------------------------------------------ Empfehlungen */

/* ------------------------------------ Kategoriereihen zum Blättern */

/*
 * Eine Reihe je Oberkategorie: waagerecht scrollbar, mit Rastpunkten und zwei
 * Knöpfen daneben. Die Knöpfe sind Zugabe – gewischt, gescrollt und mit den
 * Pfeiltasten bedient wird die Reihe auch ohne sie.
 */

.catalog-row + .catalog-row { margin-top: var(--space-7); }

.row-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.carousel-nav { display: flex; gap: var(--space-2); }

/*
 * Senkrecht wird nicht gescrollt, sondern geklippt: `overflow-x: auto` macht
 * aus `overflow-y: visible` von selbst ein `auto`, und der Kachel-Hover hebt
 * die Karte um drei Pixel – daraus wurde sonst bei jedem Überfahren eine
 * zweite Bildlaufleiste. Die Polsterung gibt dem Anheben und dem Schatten den
 * Platz, den das Klippen ihnen nimmt.
 *
 * `position: relative` klippt mit: Ein `overflow` schneidet nur ab, was sein
 * Nachkomme **und** in seinem Bezugssystem ist. Die visuell versteckte Zeile
 * in jeder Kachel (`.visually-hidden`) steht auf `position: absolute` ohne
 * einen positionierten Vorfahren – sie hing damit am Dokument, entkam dem
 * Scroller und machte die Seite auf dem Handy 350 Pixel breiter, als das
 * Fenster ist. Scrollen ließ sie sich dort nicht, aber messen: Was
 * `scrollWidth` sagt, glauben manche mobilen Browser und lassen die Seite
 * seitlich wegkippen.
 */
.carousel {
    position: relative;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    /* Der Rastpunkt sitzt an der Kante des Containers, nicht an der der
       Polsterung – sonst stünde jede eingerastete Kachel acht Pixel links
       neben der Überschrift darüber. */
    scroll-padding-inline-start: var(--space-2);
    margin-inline: calc(var(--space-2) * -1);
    padding: 6px var(--space-2) var(--space-4);
}

.carousel:focus-visible {
    outline: 2px solid var(--brand-600);
    outline-offset: 2px;
    border-radius: var(--radius-lg);
}

/*
 * Eine **ganze** Zahl Kacheln je Ansicht, und deshalb ein Bruchteil der Breite
 * statt fester 280 Pixel.
 *
 * Zwei Dinge hängen daran. Erstens hört am Rand keine Kachel mittendrin auf:
 * Ein senkrecht angeschnittenes Bild sieht aus wie ein verzerrtes, und
 * ausgerechnet im Katalog steht jedes Artikelbild im Quadrat. Zweitens
 * überläuft eine Reihe dadurch früher – mit festen 280 Pixeln passten auf dem
 * Schreibtisch vier Kacheln fast hinein, und eine Reihe, die sich um
 * zweiunddreißig Pixel schieben lässt, ist kein Karussell, sondern ein
 * Wackelkontakt.
 *
 * Drei je Ansicht ist zugleich die Aufteilung des Kachelrasters daneben
 * (`.item-grid`, `minmax(280px, 1fr)`): Dieselbe Kachel in derselben Größe,
 * ob sie nun in einer Reihe oder in einem Raster steht.
 */
.carousel-track {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: calc((100% - 2 * var(--space-5)) / 3);
    gap: var(--space-5);
    align-items: stretch;
}

.carousel-track > * { scroll-snap-align: start; }

@media (max-width: 900px) {
    .carousel-track { grid-auto-columns: calc((100% - var(--space-5)) / 2); }
}

/*
 * Auf dem Handy eine Kachel je Ansicht. Die angeschnittene Nachbarkachel war
 * hier einmal der Hinweis, dass es weitergeht – den geben jetzt die Pfeile,
 * und sie sind der deutlichere: Man kann sie drücken.
 */
@media (max-width: 560px) {
    .carousel-track { grid-auto-columns: 100%; }
}

.featured-section {
    background: var(--brand-50);
    border-block: 1px solid var(--brand-100);
    padding: var(--space-7) 0;
}

/* ------------------------------------------------ Bänder der Startseite */

/*
 * Unter dem Kopf wechseln sich die Abschnitte ab: hell, getönt, hell
 * (`.home-bands` in `Catalog/index.php`). Vorher waren Angebote und
 * Empfehlungen beide getönt und lasen sich wie ein Block; jeder andere
 * Abschnitt stand weiß. Getönt wird jetzt nach der Stelle, nicht nach der
 * Art – fällt ein Abschnitt weg, bleibt der Wechsel trotzdem.
 *
 * Der Slider ist immer das erste Band und bleibt deshalb hell; er trägt
 * seinen eigenen Rahmen.
 */
.home-bands > section {
    background: var(--white);
    border-block: 0;
}

.home-bands > section:not(.hero-slider) { padding-block: var(--space-7); }

.home-bands > section:nth-of-type(even) {
    background: linear-gradient(180deg, var(--brand-50), color-mix(in srgb, var(--brand-50) 55%, var(--white)));
    border-block: 1px solid var(--brand-100);
}

/*
 * Eine kleine Überzeile über jeder Abschnittsüberschrift – sie sagt, was für
 * ein Abschnitt das ist, bevor die Überschrift sagt, worum es geht.
 */
.section-eyebrow {
    margin: 0 0 4px;
    font-size: .75rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .09em;
    color: var(--brand-600);
}

@media (max-width: 640px) {
    .home-bands > section:not(.hero-slider) { padding-block: var(--space-6); }
}

/* ------------------------------------------------- Slider der Startseite */

/*
 * Ein Bild links, Titel, Text und Knopf rechts; auf dem Handy untereinander
 * (`element/hero_slider`). Gebaut wie die Kategoriereihen: ein Scroller mit
 * Rastpunkten, den der Browser von selbst bedienen lässt – `initHeroSlider()`
 * legt nur das Weiterblättern, die Punkte und die Pause darüber.
 *
 * Das Bild steht im Quadrat und wird beschnitten wie auf der Kachel
 * (`object-fit: cover`): derselbe Ausschnitt wie auf Kachel und Artikelseite,
 * und ein Hochformat verliert nicht Kopf und Fuß. Damit die Folie am
 * Schreibtisch nicht so hoch wird wie der Bildschirm, bekommt das Bild die
 * schmalere Spalte (5 von 12) – rund 480 Pixel im Quadrat.
 */
.hero-slider { padding-top: var(--space-6); }

.hero-slider-frame {
    position: relative;
    background: var(--white);
    border: 1px solid var(--ink-200);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow);
    overflow: hidden;
}

/*
 * `position: relative` aus demselben Grund wie bei `.carousel`: Was darin
 * absolut steht, soll vom Scroller beschnitten werden und nicht die Seite auf
 * dem Handy verbreitern.
 */
.hero-slider-track {
    position: relative;
    display: flex;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
}

.hero-slider-track::-webkit-scrollbar { display: none; }

.hero-slider-track:focus-visible {
    outline: 2px solid var(--brand-600);
    outline-offset: -2px;
    border-radius: var(--radius-xl);
}

.hero-slide {
    flex: 0 0 100%;
    scroll-snap-align: start;
    scroll-snap-stop: always;
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    align-items: center;
}

.hero-slide-media {
    display: block;
    aspect-ratio: 1;
    background: var(--ink-100);
    overflow: hidden;
}

.hero-slide-media img {
    width: 100%; height: 100%;
    object-fit: cover;
    transition: transform 600ms cubic-bezier(.4, 0, .2, 1);
}

.hero-slide:hover .hero-slide-media img { transform: scale(1.03); }

.hero-slide-body {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-3);
    /* Unten Platz für Punkte und Pause, die über der Folie liegen. */
    padding: var(--space-6) var(--space-7) calc(var(--space-6) + 40px);
}

.hero-slide-label {
    display: inline-flex;
    padding: 4px 12px;
    border-radius: 999px;
    background: var(--brand-100);
    color: var(--brand-700);
    font-size: .8125rem;
    font-weight: 600;
    letter-spacing: .01em;
}

.hero-slide-title {
    margin: 0;
    font-size: clamp(1.5rem, 1.1rem + 1.4vw, 2.25rem);
    line-height: 1.15;
}

.hero-slide-text {
    margin: 0;
    max-width: 46ch;
    color: var(--ink-600);
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.hero-slide-body .btn { margin-top: var(--space-2); }

.hero-slider-controls {
    position: absolute;
    right: var(--space-7);
    bottom: var(--space-4);
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.hero-slider-controls[hidden] { display: none; }

.hero-slider-dots { display: flex; }

/* Die Fläche ist 24 Pixel groß, der Punkt darin klein – getroffen werden
   muss er auf dem Handy trotzdem. */
.hero-slider-dot {
    width: 24px; height: 24px;
    padding: 0;
    border: 0;
    background: none;
    display: grid;
    place-items: center;
    cursor: pointer;
}

.hero-slider-dot::before {
    content: "";
    width: 8px; height: 8px;
    border-radius: 999px;
    background: var(--ink-300);
    transition: width 240ms ease, background-color 240ms ease;
}

.hero-slider-dot[aria-current="true"]::before { width: 22px; background: var(--brand-600); }

.hero-slider-toggle {
    width: 32px; height: 32px;
    padding: 0;
    border: 1px solid var(--ink-200);
    border-radius: 999px;
    background: var(--white);
    display: grid;
    place-items: center;
    cursor: pointer;
    color: var(--ink-700);
}

.hero-slider-toggle:hover { border-color: var(--ink-400); }

/* Zwei Balken für „anhalten", ein Dreieck für „weiter". */
.hero-slider-pause {
    width: 10px; height: 12px;
    border-inline: 3px solid currentColor;
}

.hero-slider-toggle[data-paused] .hero-slider-pause {
    width: 0; height: 0;
    border-inline: 0;
    border-block: 6px solid transparent;
    border-left: 10px solid currentColor;
    margin-left: 3px;
}

@media (max-width: 760px) {
    .hero-slider { padding-top: var(--space-5); }
    .hero-slider-frame { border-radius: var(--radius-lg); }
    .hero-slide { grid-template-columns: minmax(0, 1fr); align-items: start; }
    .hero-slide-body { padding: var(--space-5) var(--space-5) calc(var(--space-5) + 40px); }
    .hero-slider-controls { left: var(--space-5); right: auto; bottom: var(--space-3); }
}

@media (prefers-reduced-motion: reduce) {
    .hero-slide-media img,
    .hero-slider-dot::before { transition: none; }
    .hero-slide:hover .hero-slide-media img { transform: none; }
}

/* ---------------------------------------------------- Kategoriekacheln */

/*
 * Ein Bild, darüber der Name auf einem Verlauf (`element/category_tiles`).
 * Der Verlauf ist kein Schmuck: Weiße Schrift auf einem fremden Foto ist nur
 * lesbar, wenn darunter etwas Dunkles liegt – und welches Foto dort steht,
 * weiß das Stylesheet nicht.
 */
.category-tiles-section { padding: var(--space-7) 0 var(--space-2); }

.category-tiles-heading { font-size: 1.25rem; margin-bottom: var(--space-4); }

.category-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: var(--space-4);
}

.category-tile {
    position: relative;
    display: block;
    aspect-ratio: 4 / 3;
    border-radius: var(--radius-lg);
    overflow: hidden;
    color: var(--white);
    background: var(--brand-700);
    box-shadow: var(--shadow-sm);
    isolation: isolate;
    transition: box-shadow var(--transition), transform var(--transition);
}

.category-tile:hover {
    text-decoration: none;
    color: var(--white);
    box-shadow: var(--shadow);
    transform: translateY(-2px);
}

.category-tile:focus-visible { outline: 3px solid var(--brand-400); outline-offset: 3px; }

.category-tile-media,
.category-tile-media img,
.category-tile-media .item-placeholder {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

.category-tile-media img {
    object-fit: cover;
    transition: transform 420ms cubic-bezier(.4, 0, .2, 1);
}

.category-tile:hover .category-tile-media img { transform: scale(1.06); }

/*
 * Ohne Bild trägt der Name die Aussage, und der Buchstabe tritt nach oben
 * rechts zurück – mittig stünde er hinter dem Namen und läse sich mit.
 */
.category-tile .placeholder.item-placeholder { place-items: start end; padding: var(--space-3) var(--space-4); }
.category-tile .item-placeholder-letter { font-size: 26cqi; opacity: .3; }

.category-tile::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(to top, rgba(8, 20, 26, .78) 0%, rgba(8, 20, 26, .25) 45%, transparent 70%);
}

.category-tile-text {
    position: absolute;
    left: var(--space-4);
    right: var(--space-4);
    bottom: var(--space-3);
    z-index: 2;
    display: grid;
    gap: 2px;
}

.category-tile-name {
    font-weight: 700;
    font-size: 1.0625rem;
    line-height: 1.25;
    text-wrap: balance;
}

.category-tile-count {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: .8125rem;
    color: rgba(255, 255, 255, .82);
}

.category-tile-count svg { transition: transform var(--transition); }
.category-tile:hover .category-tile-count svg { transform: translateX(3px); }

/*
 * Auf dem Handy eine Reihe zum Wischen statt eines Rasters: Sechs Kacheln
 * untereinander schöben den Katalog um zwei Bildschirme nach unten. Die
 * nächste Kachel ragt sichtbar ins Bild – das ist der Hinweis, dass es
 * weitergeht, ohne dass ein Pfeil danebenstehen muss.
 */
@media (max-width: 640px) {
    .category-tiles {
        display: flex;
        overflow-x: auto;
        scroll-snap-type: x mandatory;
        position: relative;
        padding-bottom: var(--space-2);
        margin-inline: calc(var(--space-5) * -1);
        padding-inline: var(--space-5);
        scroll-padding-inline: var(--space-5);
        scrollbar-width: none;
    }

    .category-tiles::-webkit-scrollbar { display: none; }

    .category-tile {
        flex: 0 0 68%;
        scroll-snap-align: start;
    }
}

@media (prefers-reduced-motion: reduce) {
    .category-tile,
    .category-tile-media img,
    .category-tile-count svg { transition: none; }
    .category-tile:hover { transform: none; }
    .category-tile:hover .category-tile-media img { transform: none; }
}

/* ------------------------------------------------ So einfach geht's */

/*
 * Drei Schritte nebeneinander, verbunden durch eine feine Linie hinter den
 * Symbolen – sie liest sich als Weg, nicht als drei lose Kästen. Auf dem
 * Handy untereinander, die Linie läuft dann senkrecht.
 */
.home-intro { padding: var(--space-7) 0; }

.home-intro-heading { font-size: 1.25rem; margin-bottom: var(--space-5); }

.home-steps {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-5);
    position: relative;
}

.home-steps::before {
    content: "";
    position: absolute;
    top: 26px;
    left: 26px;
    right: 26px;
    height: 2px;
    background: linear-gradient(90deg, var(--brand-200), var(--brand-100));
    z-index: 0;
}

.home-step {
    position: relative;
    z-index: 1;
    display: grid;
    gap: var(--space-3);
    align-content: start;
}

.home-step-icon {
    position: relative;
    width: 54px;
    height: 54px;
    border-radius: 16px;
    display: grid;
    place-items: center;
    color: var(--white);
    background: linear-gradient(135deg, var(--brand-500), var(--brand-800));
    box-shadow: 0 0 0 6px var(--white), var(--shadow-sm);
}

.home-step-icon svg { width: 24px; height: 24px; }

.home-step-number {
    position: absolute;
    top: -6px;
    right: -6px;
    width: 22px;
    height: 22px;
    border-radius: 999px;
    display: grid;
    place-items: center;
    font-size: .75rem;
    font-weight: 700;
    color: var(--white);
    background: var(--accent-600);
    box-shadow: 0 0 0 2px var(--white);
}

.home-step-body { display: grid; gap: 4px; }
.home-step-body strong { font-size: 1.0625rem; color: var(--ink-900); }
.home-step-body span { color: var(--ink-600); font-size: .9375rem; max-width: 34ch; }

.home-promises {
    list-style: none;
    margin: var(--space-6) 0 0;
    padding: var(--space-4) 0 0;
    border-top: 1px solid var(--ink-100);
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-6);
    font-size: .9375rem;
    font-weight: 500;
    color: var(--ink-700);
}

.home-promises li { display: inline-flex; align-items: center; gap: var(--space-2); }
.home-promises svg { color: var(--brand-600); }

@media (max-width: 760px) {
    .home-steps { grid-template-columns: 1fr; gap: var(--space-4); }

    .home-steps::before {
        top: 26px;
        bottom: 26px;
        left: 26px;
        right: auto;
        width: 2px;
        height: auto;
        background: linear-gradient(180deg, var(--brand-200), var(--brand-100));
    }

    .home-step { grid-template-columns: 54px 1fr; gap: var(--space-4); align-items: center; }
}

/* Kennzeichnung "Nur Abholung" / Standort auf Kacheln */
.item-flags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 2px;
}

/* -------------------------------------------------------------- Fußzeile */

.site-footer {
    margin-top: auto;
    background: var(--brand-900);
    color: rgba(255, 255, 255, .7);
    padding: var(--space-7) 0 var(--space-5);
    font-size: .9375rem;
}

.site-footer h4 {
    color: var(--white);
    font-size: .8125rem;
    text-transform: uppercase;
    letter-spacing: .07em;
    margin-bottom: var(--space-3);
}

.site-footer a { color: rgba(255, 255, 255, .78); }
.site-footer a:hover { color: var(--white); }
.site-footer .muted { color: rgba(255, 255, 255, .45); }

.footer-grid {
    display: grid;
    gap: var(--space-6);
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}

/*
 * Die Partnerreihe steht zwischen dem Spaltenraster und dem Zeilenrand mit dem
 * Impressum.
 *
 * Zwei Dinge machen fremde Logos hier erträglich: eine **feste Höhe** statt
 * einer festen Breite – nur so stehen ein breites Wortzeichen und ein rundes
 * Siegel gleich groß nebeneinander –, und `filter: brightness(0) invert(1)`,
 * das jedes Logo weiß zeichnet. Fremde Marken kommen in allen Farben, und in
 * einer dunklen Fußzeile verschwindet die Hälfte davon. Weiß ist die Lösung,
 * die jedes Logo gleich behandelt; beim Überfahren kommt die eigene Farbe
 * zurück, für den, der genau hinsieht.
 */
.footer-partners {
    margin-top: var(--space-6);
    padding-top: var(--space-5);
    border-top: 1px solid rgba(255, 255, 255, .12);
}

.footer-partners ul {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-5);
}

.footer-partners img {
    height: 38px;
    width: auto;
    max-width: 160px;
    object-fit: contain;
    filter: brightness(0) invert(1);
    opacity: .75;
    transition: opacity var(--transition), filter var(--transition);
}

.footer-partners a:hover img,
.footer-partners a:focus-visible img {
    opacity: 1;
    filter: none;
}

@media (max-width: 640px) {
    .footer-partners img { height: 30px; max-width: 120px; }
    .footer-partners ul { gap: var(--space-4); }
}

.footer-bottom {
    margin-top: var(--space-6);
    padding-top: var(--space-4);
    border-top: 1px solid rgba(255, 255, 255, .12);
    font-size: .8125rem;
    color: rgba(255, 255, 255, .5);
    display: flex;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
}

/* ------------------------------------------------------------- Unterlagen */

.document {
    border: 1px solid var(--ink-200);
    border-radius: var(--radius);
    padding: var(--space-4);
    background: var(--white);
}

.document + .document { margin-top: var(--space-3); }

.document-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
}

.signature-form { margin-top: var(--space-4); }

.signature-pad {
    border: 1px solid var(--ink-200);
    border-radius: var(--radius);
    padding: var(--space-3);
    background: var(--ink-50);
    margin-bottom: var(--space-3);
}

.signature-pad-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-2);
}

/*
 * `touch-action: none` ist hier zwingend: ohne die Angabe scrollt die Seite,
 * sobald jemand auf dem Handy mit dem Finger unterschreibt.
 */
.signature-pad canvas {
    display: block;
    width: 100%;
    height: 160px;
    background: var(--white);
    border: 1px dashed var(--ink-300);
    border-radius: var(--radius-sm);
    touch-action: none;
    cursor: crosshair;
    margin-bottom: var(--space-2);
}

/* ============================================================ Mietberater */

/*
 * Der Berater ist eine Folge von Entscheidungen und kein Katalog: Eine schmale
 * Spalte hält den Blick bei der einen Frage, die gerade dransteht. 680 Pixel
 * sind ungefähr eine Textbreite – breiter wandert das Auge über die Zeile, und
 * eine Antwortliste sähe aus wie eine Tabelle.
 */
.advisor-shell { max-width: 680px; margin: 0 auto; }

.advisor-hero {
    display: block;
    width: 100%;
    aspect-ratio: 16 / 9;
    object-fit: cover;
    border-radius: var(--radius) var(--radius) 0 0;
}

.advisor-progress { margin-bottom: var(--space-4); }

.advisor-progress-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    font-size: .875rem;
    margin-bottom: var(--space-2);
}

/*
 * Ein `progress` bringt seine Bedeutung mit, sieht aber in jedem Browser
 * anders aus. Die drei Regeln darunter sind derselbe Balken für alle:
 * `appearance: none` für die neueren, `-webkit-progress-*` für WebKit,
 * `-moz-progress-bar` für Firefox. Wer eine davon wegkürzt, hat auf einem
 * Drittel der Geräte wieder den Systembalken.
 */
.advisor-bar {
    appearance: none;
    -webkit-appearance: none;
    display: block;
    width: 100%;
    height: 6px;
    border: 0;
    border-radius: 999px;
    background: var(--ink-200);
    overflow: hidden;
}

.advisor-bar::-webkit-progress-bar { background: var(--ink-200); border-radius: 999px; }
.advisor-bar::-webkit-progress-value { background: var(--brand-600); border-radius: 999px; }
.advisor-bar::-moz-progress-bar { background: var(--brand-600); border-radius: 999px; }

.advisor-card-step h1 { font-size: 1.375rem; }

.advisor-choices { border: 0; margin: 0; padding: 0; display: grid; gap: var(--space-2); }

/*
 * Die ganze Zeile ist das Ziel und nicht nur der Punkt daneben: Ein Radio von
 * sechzehn Pixeln trifft auf dem Handy niemand zuverlässig. Das Eingabefeld
 * bleibt sichtbar – es sagt, ob hier eine oder mehrere Antworten gehen, und
 * ohne CSS bleibt alles bedienbar.
 */
.advisor-choice {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--ink-200);
    border-radius: var(--radius);
    background: var(--white);
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease;
}

.advisor-choice:hover { border-color: var(--brand-500); background: var(--brand-50); }

/*
 * `:has()` färbt die Zeile der gewählten Antwort. Wo es fehlt, bleibt nur der
 * Punkt gefüllt – das ist weniger deutlich, aber nie falsch.
 */
.advisor-choice:has(input:checked) {
    border-color: var(--brand-600);
    background: var(--brand-50);
}

/* Der Fokusring gehört an die Zeile: Das Feld darin ist zu klein, um ihn zu tragen. */
.advisor-choice:has(input:focus-visible) { outline: 2px solid var(--brand-600); outline-offset: 2px; }

.advisor-choice input { margin-top: .2rem; flex: none; }
.advisor-choice-text { display: grid; gap: 2px; min-width: 0; }
.advisor-choice-text .muted { font-size: .8125rem; }

.advisor-number { display: flex; align-items: flex-end; gap: var(--space-3); }
.advisor-number .field { margin-bottom: 0; flex: 1; }
.advisor-unit { padding-bottom: .6rem; color: var(--ink-600); }

.advisor-nav { margin-top: var(--space-3); }

/*
 * Die Spalte der Frage ist so breit wie die längste Frage und nicht wie die
 * halbe Karte: Zwei gleiche Bruchteile schoben die Antwort auf einem breiten
 * Bildschirm einen halben Meter neben ihre Frage, und dazwischen stand nichts.
 * Die Breite der Liste ist zusätzlich gedeckelt – eine Zeile, die über die
 * volle Kartenbreite läuft, ist zum Lesen ohnehin zu lang.
 */
.advisor-summary {
    display: grid;
    grid-template-columns: minmax(0, max-content) minmax(0, 1fr);
    gap: var(--space-2) var(--space-4);
    max-width: 46rem;
    margin: 0 0 var(--space-4);
    font-size: .875rem;
}

.advisor-summary dt { color: var(--ink-600); }
.advisor-summary dd { margin: 0; font-weight: 600; }

@media (max-width: 560px) {
    .advisor-summary { grid-template-columns: minmax(0, 1fr); gap: 0 0; }
    .advisor-summary dd { margin-bottom: var(--space-3); }
}

/*
 * Die Begründung steht zwischen der Zusammenfassung und den Kacheln und
 * braucht deshalb oben Luft: Ohne sie klebte sie an der Kante der Karte
 * darüber und sah aus wie deren letzte Zeile.
 */
.advisor-reason {
    max-width: 46rem;
    margin: var(--space-4) 0;
    font-size: 1.0625rem;
    line-height: 1.6;
}

.advisor-reason strong { color: var(--ink-900); }

/*
 * Die Begründung steht **unter** der Kachel und nicht darin: Die Kachel ist
 * dieselbe wie im Katalog, und ein Satz mittendrin wäre eine zweite Fassung
 * von ihr, die der ersten hinterherliefe.
 */
.advisor-pick { display: flex; flex-direction: column; gap: var(--space-2); }

.advisor-note {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    margin: 0;
    padding: var(--space-3);
    border-radius: var(--radius);
    background: var(--brand-50);
    color: var(--ink-700);
    font-size: .8125rem;
}

.advisor-note svg { flex: none; color: var(--brand-600); }

/*
 * Die Beraterkachel steht zwischen Artikelkacheln und ist bis auf zwei Dinge
 * dieselbe: Sie braucht keine zweite Zeile mit Abzeichen und keinen Preis, und
 * deshalb bekommt ihr Text den Abstand, den dort sonst die Kategoriezeile hält.
 */
.advisor-card .item-body { padding-top: var(--space-4); }

/*
 * Der Platzhalter ist in der Markenfarbe hinterlegt und nicht im Grau der
 * Artikelkachel: Dort heißt Grau „für diesen Artikel fehlt ein Foto", hier ist
 * es der vorgesehene Zustand – ein Berater hat kein Lagerfoto. Zwischen
 * Artikeln sagt die Fläche zugleich auf einen Blick, dass diese Kachel etwas
 * anderes ist.
 */
.advisor-card .item-thumb .placeholder {
    background: linear-gradient(135deg, var(--brand-50), var(--brand-100));
    color: var(--brand-500);
}

.advisor-card .item-thumb .placeholder svg { width: 64px; height: 64px; }

/* ================================================================ Glanz */

/*
 * Kleine Bewegungen an den Stellen, an denen im Katalog etwas passiert: ein
 * Artikel im Korb, ein abgeschickter Auftrag, eine
 * Unterschrift. Für alles hier gilt dasselbe wie für animate.css (siehe
 * „Farben und Bewegung" in CLAUDE.md):
 *
 * - Sichtbar ist der Endzustand. Ohne JavaScript oder ohne Unterstützung im
 *   Browser steht alles fertig da.
 * - `prefers-reduced-motion` schaltet es ab – über den pauschalen Block in
 *   `base.css`, der jede Animation auf 0,01 ms staucht.
 */

/* --------------------------------------------------------- Seitenwechsel */

/*
 * Bewusst keine Übergänge zwischen den Seiten (`@view-transition`). Sie
 * standen hier einmal – erst das Kachelbild, das in die Galerie gleitet,
 * dann jeder Seitenwechsel. Der Browser muss dafür vor jedem Wechsel die
 * alte Seite abfotografieren und darf die neue erst danach zeigen; gemessen
 * erschien sie damit fast doppelt so spät, und auf dem Handy fühlte sich der
 * ganze Katalog langsamer an. Ein Katalog, der schnell umblättert, ist mehr
 * wert als einer, der schön umblättert.
 */

/* ------------------------------------------------------ In den Korb */

/*
 * Das Bild des Artikels fliegt zum Auftragsknopf (`initBasketFly()`). Es ist
 * eine Kopie über allem und fängt keinen Klick – das Original bleibt stehen.
 */
.fly-to-basket {
    position: fixed;
    z-index: 1000;
    margin: 0;
    border-radius: var(--radius);
    object-fit: cover;
    pointer-events: none;
    box-shadow: var(--shadow-lg);
}

/* Der Knopf nimmt die Ladung an. */
.basket-link.is-catching { animation: basket-catch .35s ease-out; }

@keyframes basket-catch {
    40% { transform: scale(1.12); }
}

/* ---------------------------------------------------------- Vollbild */

/*
 * Ein Klick auf das große Bild öffnet es über die ganze Fläche
 * (`initLightbox()`). Ein `<dialog>`: Escape, Fokus und das Sperren der Seite
 * dahinter bringt der Browser mit. Ohne JavaScript bleibt das Bild ein Bild.
 */
.gallery-main { position: relative; }

.gallery-main [data-gallery-main] { cursor: zoom-in; }

.gallery-zoom {
    position: absolute;
    right: var(--space-3);
    bottom: var(--space-3);
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, .92);
    color: var(--ink-800);
    box-shadow: var(--shadow-md, 0 6px 20px rgba(15, 23, 42, .12));
    cursor: pointer;
    transition: transform var(--transition);
}

.gallery-zoom:hover { transform: scale(1.08); }
.gallery-zoom svg { width: 20px; height: 20px; }

.lightbox {
    width: 100vw;
    max-width: 100vw;
    height: 100dvh;
    max-height: 100dvh;
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--white);
    overflow: hidden;
}

.lightbox::backdrop { background: rgba(8, 14, 20, .92); }

.lightbox[open] { animation: lightbox-in .22s ease-out; }

@keyframes lightbox-in {
    from { opacity: 0; transform: scale(.97); }
}

.lightbox-stage {
    display: grid;
    place-items: center;
    width: 100%;
    height: 100%;
    padding: 56px var(--space-4);
    touch-action: pan-y;
}

.lightbox-stage img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    border-radius: var(--radius);
    user-select: none;
    -webkit-user-drag: none;
}

.lightbox-close,
.lightbox-nav {
    position: absolute;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, .12);
    color: var(--white);
    cursor: pointer;
    transition: background var(--transition);
}

.lightbox-close:hover,
.lightbox-nav:hover { background: rgba(255, 255, 255, .24); }

.lightbox-close { top: var(--space-3); right: var(--space-3); font-size: 1.5rem; line-height: 1; }
.lightbox-nav { top: 50%; margin-top: -22px; font-size: 1.75rem; line-height: 1; }
.lightbox-prev { left: var(--space-3); }
.lightbox-next { right: var(--space-3); }

.lightbox-count {
    position: absolute;
    bottom: var(--space-4);
    left: 0;
    right: 0;
    text-align: center;
    font-size: .875rem;
    color: rgba(255, 255, 255, .75);
}

/* Auf dem Handy wird gewischt – die Pfeile stünden nur im Bild. */
@media (pointer: coarse) {
    .lightbox-nav { display: none; }
}

/* ------------------------------------------------ Auftrag eingegangen */

/*
 * Der Kreis zieht sich, dann der Haken, dann sprühen ein paar Funken – der
 * eine Moment im ganzen Ablauf, in dem sich etwas freuen darf. Reines SVG und
 * CSS: Es läuft ohne Skript, und mit abgestellter Bewegung steht der fertige
 * Haken da und die Funken gar nicht erst.
 */
.thanks .check-drawn {
    position: relative;
    background: none;
    width: 88px;
    height: 88px;
}

.thanks .check-drawn svg { width: 88px; height: 88px; overflow: visible; }

.check-ring {
    fill: var(--ok-100);
    stroke: var(--ok-500);
    stroke-width: 2.5;
    stroke-dasharray: 151;
    stroke-dashoffset: 151;
    transform: rotate(-90deg);
    transform-box: fill-box;
    transform-origin: center;
    animation: check-stroke .55s cubic-bezier(.65, 0, .45, 1) forwards;
}

.check-mark {
    fill: none;
    stroke: var(--ok-500);
    stroke-width: 4;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 36;
    stroke-dashoffset: 36;
    animation: check-stroke .35s cubic-bezier(.65, 0, .45, 1) .5s forwards;
}

@keyframes check-stroke {
    to { stroke-dashoffset: 0; }
}

.check-sparks { position: absolute; inset: 0; pointer-events: none; }

.check-sparks i {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 8px;
    height: 8px;
    margin: -4px 0 0 -4px;
    border-radius: 50%;
    background: var(--ok-500);
    opacity: 0;
    transform: rotate(var(--a)) translateY(0);
    animation: check-spark .7s ease-out .75s both;
}

.check-sparks i:nth-child(even) { background: var(--brand-500); width: 6px; height: 6px; }
.check-sparks i:nth-child(3n) { background: var(--warn-500, #e0a100); }

@keyframes check-spark {
    0% { opacity: 1; transform: rotate(var(--a)) translateY(-30px) scale(.4); }
    70% { opacity: 1; }
    100% { opacity: 0; transform: rotate(var(--a)) translateY(-66px) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .check-sparks { display: none; }
}

/* --------------------------------------------------- Stand des Auftrags */

/*
 * Die Leiste füllt sich beim Laden Station für Station bis zum aktuellen
 * Stand. Die Füllung liegt als eigene Linie (`::after`) über der grauen
 * Schiene und wächst von links; `--i` ist die Nummer der Station und
 * staffelt beides. Ohne Bewegung steht sie gleich gefüllt da.
 */
.order-progress li.is-done + li::before,
.order-progress li.is-done + li.is-current::before { background: var(--ink-200); }

.order-progress li.is-done + li::after {
    content: "";
    position: absolute;
    top: 6px;
    right: 50%;
    width: 100%;
    height: 2px;
    background: var(--brand-600);
    z-index: 0;
    transform-origin: left center;
    animation: progress-fill .32s ease-out both;
    animation-delay: calc(var(--i, 0) * 180ms - 120ms);
}

.order-progress .is-done .order-progress-dot {
    animation: progress-dot .3s ease-out both;
    animation-delay: calc(var(--i, 0) * 180ms);
}

.order-progress .is-current .order-progress-dot {
    animation:
        progress-dot .3s ease-out both calc(var(--i, 0) * 180ms),
        progress-pulse 2.4s ease-in-out calc(var(--i, 0) * 180ms + .3s) infinite;
}

@keyframes progress-fill {
    from { transform: scaleX(0); }
}

@keyframes progress-dot {
    from { transform: scale(.4); }
    60% { transform: scale(1.2); }
}


/* ---------------------------------------------------------- Das Siegel */

/*
 * Ein unterschriebener Vertrag bekommt im Portal einen Stempel mit Datum und
 * der Unterschrift selbst. Er fällt beim ersten Blick einmal auf das Papier
 * (`stampIn` in `base.css`) und bleibt danach ruhig stehen.
 */
.signed-seal {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-top: var(--space-3);
}

.signed-seal-stamp {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    padding: var(--space-2) var(--space-4);
    border: 3px double var(--ok-500);
    border-radius: var(--radius);
    color: var(--ok-700);
    transform: rotate(-4deg);
    text-transform: uppercase;
    letter-spacing: .08em;
    font-size: .75rem;
    line-height: 1.3;
    background: color-mix(in srgb, var(--ok-100) 60%, transparent);
}

.signed-seal-stamp strong { font-size: .9375rem; letter-spacing: .12em; }

.signed-seal-stamp span { text-transform: none; letter-spacing: 0; }

.signed-seal-signature {
    max-height: 56px;
    max-width: 200px;
    object-fit: contain;
    opacity: .85;
}

/* ------------------------------------------------- Leere Zustände */

/*
 * Die Zeichnungen der leeren Zustände (`element/empty_art`): Korb, Suche,
 * Wegweiser, Kalender, Preisschild. Dieselbe Bildsprache wie „Alles erledigt"
 * in der Verwaltung – weißer Gegenstand, Markenfarbe als Akzent, ein leises
 * Schweben. Die Farben kommen aus den Variablen und folgen der Marke.
 */
.empty-state .empty-art,
.not-found .empty-art {
    width: 160px;
    height: 120px;
    margin: 0 auto var(--space-3);
    color: inherit;
    overflow: visible;
    display: block;
}

.empty-art-shadow {
    fill: var(--ink-100);
    transform-box: fill-box;
    transform-origin: center;
    animation: empty-art-shadow 4.5s ease-in-out infinite;
}

.empty-art-body { animation: empty-art-float 4.5s ease-in-out infinite; }

.empty-art-paper { fill: var(--white); stroke: var(--ink-200); stroke-width: 2; stroke-linejoin: round; }
.empty-art-brand { fill: var(--brand-600); }
.empty-art-on-brand { fill: rgba(255, 255, 255, .7); }
.empty-art-bar { fill: var(--ink-200); }
.empty-art-pole { fill: var(--ink-300); }
.empty-art-rib { fill: none; stroke: var(--ink-200); stroke-width: 3; stroke-linecap: round; }
.empty-art-line { fill: none; stroke: var(--ink-300); stroke-width: 3; stroke-linecap: round; }

/* Was hierher gehört, aber noch fehlt: gestrichelt und ohne Füllung. */
.empty-art-ghost,
.empty-art-ghost-line {
    fill: none;
    stroke: var(--brand-500);
    stroke-width: 1.5;
    stroke-dasharray: 3 3;
    stroke-linecap: round;
    opacity: .7;
}

.empty-art-basket .empty-art-ghost { animation: empty-art-bob 3s ease-in-out infinite; transform-box: fill-box; }
.empty-art-basket .empty-art-ghost + .empty-art-ghost { animation-delay: .6s; }

.empty-art-lens { fill: rgba(255, 255, 255, .75); stroke: var(--brand-600); stroke-width: 5; }
.empty-art-handle { fill: none; stroke: var(--brand-700); stroke-width: 8; stroke-linecap: round; }
.empty-art-mark { fill: none; stroke: var(--brand-600); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.empty-art-dot { fill: var(--brand-600); }

.empty-art-badge { fill: var(--accent-600); }
.empty-art-plus { fill: none; stroke: var(--white); stroke-width: 2.5; stroke-linecap: round; }

.empty-art-sparks path {
    fill: none;
    stroke: var(--brand-500);
    stroke-width: 2;
    stroke-linecap: round;
    transform-box: fill-box;
    transform-origin: center;
    animation: empty-art-spark 2.8s ease-in-out infinite;
}

.empty-art-sparks path:nth-child(2) { animation-delay: .5s; }
.empty-art-sparks path:nth-child(3) { animation-delay: 1s; }

/* Der Wegweiser schwebt nicht – er steht. Er schwankt ein wenig. */
.empty-art-lost .empty-art-body {
    transform-box: fill-box;
    transform-origin: 50% 100%;
    animation-name: empty-art-sway;
}

@keyframes empty-art-float { 50% { transform: translateY(-5px); } }
@keyframes empty-art-shadow { 50% { transform: scaleX(.85); opacity: .7; } }
@keyframes empty-art-bob { 50% { transform: translateY(-4px); } }
@keyframes empty-art-sway { 25% { transform: rotate(-1.5deg); } 75% { transform: rotate(1.5deg); } }

@keyframes empty-art-spark {
    0%, 100% { opacity: .25; transform: scale(.7); }
    50% { opacity: 1; transform: scale(1.1); }
}

/* ------------------------------------------- Anmeldung am Kundenkonto */

/*
 * Formular und Markenfläche nebeneinander (`element/account_aside`). Im
 * Dokument steht das Formular zuerst – auf dem Handy bleibt es oben, und die
 * Fläche folgt darunter als Erklärung. Erst ab 900 px rückt sie nach links.
 */
.auth-split {
    display: grid;
    gap: var(--space-6);
    max-width: 1040px;
    margin: 0 auto;
}

.auth-split .account-narrow { width: 100%; }

.auth-aside {
    position: relative;
    overflow: hidden;
    border-radius: var(--radius-xl);
    padding: var(--space-6);
    color: var(--white);
    background:
        radial-gradient(520px 320px at 10% 0%, rgba(255, 255, 255, .16), transparent 60%),
        radial-gradient(rgba(255, 255, 255, .08) 1px, transparent 1.5px) 0 0 / 16px 16px,
        linear-gradient(160deg, var(--brand-600) 0%, var(--brand-900) 100%);
}

.auth-aside-eyebrow {
    display: block;
    font-size: .6875rem;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: rgba(255, 255, 255, .7);
}

.auth-aside-title {
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1.2;
    margin: var(--space-2) 0 var(--space-5);
}

.auth-aside-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--space-4);
}

.auth-aside-list li {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    font-size: .9375rem;
    line-height: 1.4;
}

.auth-aside-icon {
    flex: none;
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border-radius: 10px;
    background: rgba(255, 255, 255, .14);
}

.auth-aside-icon svg { width: 18px; height: 18px; }

@media (min-width: 900px) {
    .auth-split {
        grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
        grid-template-areas: "aside form";
        align-items: stretch;
        gap: var(--space-7);
    }

    .auth-split .account-narrow { grid-area: form; align-self: center; }

    .auth-aside {
        grid-area: aside;
        display: flex;
        flex-direction: column;
        justify-content: center;
        padding: var(--space-7);
        min-height: 420px;
    }

    .auth-aside-title { font-size: 1.875rem; }
}

/* ===================================================================
 * Spielereien, zweite Runde. Jede ist für sich verzichtbar: Ohne Skript,
 * ohne die Bibliothek von jsDelivr oder mit abgestellter Bewegung steht da,
 * was vorher dastand.
 * =================================================================== */

/* ------------------------------------------------ Hero: Licht und Welle */

/*
 * Zwei Lichtflecken in Markenfarbe und einer in der Akzentfarbe ziehen
 * langsam durch den Hero. Sie stehen in `.hero-glow`, einem eigenen Rahmen
 * mit `overflow: hidden` – der Hero selbst darf nicht beschneiden, sonst
 * schnitte er die Suchvorschläge ab (siehe oben).
 *
 * Unscharf und halb durchsichtig: Ein Fleck mit Kante wäre ein Muster, und
 * ein Muster liest man mit. Mit abgestellter Bewegung stehen sie still da.
 */
.hero-glow {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    z-index: 0;
}

.hero-glow span {
    position: absolute;
    width: 46vmax;
    height: 46vmax;
    opacity: .38;
}

/*
 * Die weiche Kante ist ein Verlauf und kein `filter: blur()`. Drei Flächen
 * von fast der Fensterbreite mit 70 Pixeln Unschärfe musste der Browser vor
 * dem ersten Bild der Startseite durchrechnen – gemessen gut 100 ms später
 * sichtbar. Ein Verlauf von der Farbe ins Durchsichtige sieht gleich aus und
 * kostet nichts.
 */
.hero-glow span:nth-child(1) { left: -12vmax; top: -22vmax; background: radial-gradient(closest-side, var(--brand-500) 35%, transparent); }
.hero-glow span:nth-child(2) { right: -14vmax; top: -6vmax; background: radial-gradient(closest-side, var(--accent-400) 35%, transparent); opacity: .45; }
.hero-glow span:nth-child(3) { left: 30%; bottom: -30vmax; background: radial-gradient(closest-side, var(--brand-600) 35%, transparent); opacity: .3; }

@media (prefers-reduced-motion: no-preference) {
    .hero-glow span:nth-child(1) { animation: hero-blob-a 22s ease-in-out infinite alternate; }
    .hero-glow span:nth-child(2) { animation: hero-blob-b 28s ease-in-out infinite alternate; }
    .hero-glow span:nth-child(3) { animation: hero-blob-c 34s ease-in-out infinite alternate; }
}

@keyframes hero-blob-a { to { transform: translate(18vmax, 10vmax) scale(1.15); } }
@keyframes hero-blob-b { to { transform: translate(-16vmax, 8vmax) scale(.9); } }
@keyframes hero-blob-c { to { transform: translate(-12vmax, -6vmax) scale(1.1); } }

/*
 * Die Welle am unteren Rand. Das Bild ist zwei Wellenlängen breit und
 * wandert um eine davon – so schließt der Anfang nahtlos ans Ende an. Sie hat
 * die Farbe der Seite darunter und steht in ihrem eigenen, beschneidenden
 * Rahmen, aus demselben Grund wie das Licht.
 */
.hero { padding-bottom: calc(var(--space-8) + 24px); }

.hero-wave {
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 36px;
    overflow: hidden;
    pointer-events: none;
    z-index: 0;
}

.hero-wave svg { display: block; width: 200%; height: 100%; fill: var(--white); }

@media (prefers-reduced-motion: no-preference) {
    .hero-wave svg { animation: hero-wave 18s linear infinite; }
}

@keyframes hero-wave { to { transform: translateX(-50%); } }

/* ----------------------------------------------- Kopfzeile beim Scrollen */

/*
 * Weiter unten wird die Kopfzeile schmaler und schwebt über dem Inhalt
 * (`initHeaderShrink()`). Nur am Schreibtisch: Auf dem Handy ist sie ohnehin
 * so knapp, wie sie sein kann.
 */
.site-header { transition: box-shadow .25s ease; }

.site-header.is-condensed {
    box-shadow: 0 10px 30px -18px rgba(16, 23, 28, .35);
}

@media (min-width: 861px) {
    .site-header .container { transition: min-height .25s ease; }
    .brand-logo,
    .brand-mark { transition: height .25s ease, width .25s ease; }

    .site-header.is-condensed .container { min-height: 58px; }
    .site-header.is-condensed:has(.brand-logo) .container { min-height: 62px; }
    .site-header.is-condensed .brand-logo { height: 40px; }
    .site-header.is-condensed .brand-mark { width: 32px; height: 32px; }
}

/* Was der Browser beim Laden scrollt, schaltet ohne Übergang (`initHeaderShrink()`). */
.site-header.is-instant,
.site-header.is-instant .container,
.site-header.is-instant .brand-logo,
.site-header.is-instant .brand-mark { transition: none; }

/* --------------------------------------- Gleitende Markierung der Kategorie */

/*
 * Beim Überfahren folgt eine Pille der Maus durch die Kategorien
 * (`initNavGlider()`); solange sie läuft, tritt der eigene Hover der Punkte
 * zurück, sonst stünden zwei Flächen übereinander.
 */
.nav-categories { position: relative; }

.nav-glider {
    position: absolute;
    left: 0;
    top: 0;
    border-radius: var(--radius);
    background: var(--ink-100);
    opacity: 0;
    pointer-events: none;
    transition: transform .28s cubic-bezier(.3, .7, .3, 1), width .28s cubic-bezier(.3, .7, .3, 1),
        height .28s ease, opacity .18s ease;
}

.nav-glider.is-visible { opacity: 1; }

.nav-categories > .nav-link,
.nav-categories > .nav-menu { position: relative; z-index: 1; }

.nav-categories.is-gliding > .nav-link:not(.is-active):hover,
.nav-categories.is-gliding > .nav-menu:not([open]) > summary:not(.is-active):hover { background: transparent; }

/* ----------------------------------------------------- 3D-Kacheln */

/*
 * vanilla-tilt neigt die Kachel und legt einen Lichtschein darüber
 * (`initTilt()`). Der Schein liegt absolut in der Kachel – sie braucht dafür
 * ein Bezugssystem, und das Beschneiden hat sie ohnehin.
 */
.item-card { position: relative; }

.item-card .js-tilt-glare { border-radius: inherit; z-index: 2; }

/* ------------------------------------------------ Zeitraum-Kalender */

/*
 * flatpickr in den Farben des Betriebs. Seine Klassen bleiben, wie sie sind;
 * hier stehen nur Farben, Schrift und Rundungen – die Regeln laden nach
 * dessen eigenem Stylesheet und gewinnen über die Spezifität.
 */
.flatpickr-calendar {
    font-family: inherit;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    border: 1px solid var(--ink-200);
}

.flatpickr-calendar.arrowTop::before,
.flatpickr-calendar.arrowTop::after { display: none; }

.flatpickr-months .flatpickr-month,
.flatpickr-current-month { color: var(--ink-900); font-weight: 600; }

.flatpickr-weekday { color: var(--ink-500); font-weight: 600; }

.flatpickr-day { border-radius: 10px; color: var(--ink-800); }

.flatpickr-day:hover { background: var(--brand-50); border-color: var(--brand-50); }

.flatpickr-day.today { border-color: var(--brand-500); }

.flatpickr-day.selected,
.flatpickr-day.startRange,
.flatpickr-day.endRange,
.flatpickr-day.selected:hover,
.flatpickr-day.startRange:hover,
.flatpickr-day.endRange:hover {
    background: var(--brand-700);
    border-color: var(--brand-700);
    color: var(--white);
}

.flatpickr-day.inRange,
.flatpickr-day.inRange:hover {
    background: var(--brand-50);
    border-color: var(--brand-50);
    box-shadow: -5px 0 0 var(--brand-50), 5px 0 0 var(--brand-50);
    border-radius: 0;
}

.flatpickr-day.flatpickr-disabled,
.flatpickr-day.flatpickr-disabled:hover { color: var(--ink-300); }

/*
 * Das Anzeigefeld ist schreibgeschützt – getippt wird nicht, gewählt wird im
 * Kalender. Es sieht trotzdem aus wie ein Feld zum Ausfüllen und nicht wie
 * die grau hinterlegten gesperrten (`input[readonly]` in `base.css`).
 */
input.date-display[readonly] { cursor: pointer; background-color: var(--white); color: var(--ink-900); }

/* ------------------------------------------------- Wischgalerie */

/*
 * Auf dem Handy wird die Galerie zum Wischband (`initGallerySwiper()`); die
 * Vorschaubilder darunter treten zurück, die Punkte übernehmen ihre Auskunft.
 */
[data-gallery].has-swiper .gallery-thumbs { display: none; }

.gallery-swiper { width: 100%; height: 100%; }

.gallery-swiper .swiper-slide img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    cursor: zoom-in;
}

.gallery-dots {
    position: absolute;
    left: 0;
    right: 0;
    bottom: var(--space-3);
    z-index: 2;
    display: flex;
    justify-content: center;
    gap: 6px;
    pointer-events: none;
}

.gallery-dots button {
    width: 8px;
    height: 8px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: rgba(255, 255, 255, .6);
    box-shadow: 0 0 0 1px rgba(16, 23, 28, .15);
    pointer-events: auto;
    transition: width .25s ease, background-color .25s ease;
}

.gallery-dots button[aria-current="true"] { width: 22px; background: var(--white); }

[data-gallery].has-swiper .gallery-zoom { z-index: 3; }

/* -------------------------------------------- Auftrag als Schublade */

/*
 * Der Auftragskorb fährt von rechts herein (`initBasketDrawer()`). Ein
 * `<dialog>`: Fokus, Escape und das Sperren der Seite dahinter bringt der
 * Browser mit.
 */
.basket-drawer {
    position: fixed;
    inset: 0 0 0 auto;
    width: min(420px, 100vw);
    max-width: 100vw;
    height: 100%;
    max-height: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    background: var(--white);
    color: var(--ink-800);
    box-shadow: -24px 0 60px rgba(16, 23, 28, .18);
}

.basket-drawer::backdrop { background: rgba(16, 23, 28, .42); backdrop-filter: blur(2px); }

@media (prefers-reduced-motion: no-preference) {
    .basket-drawer[open] { animation: drawer-in .32s cubic-bezier(.2, .8, .2, 1); }
    .basket-drawer[open]::backdrop { animation: drawer-fade .32s ease; }
    .basket-drawer.is-closing { animation: drawer-out .22s ease-in forwards; }
    .basket-drawer.is-closing::backdrop { animation: drawer-fade .22s ease reverse forwards; }
}

@keyframes drawer-in { from { transform: translateX(100%); } }
@keyframes drawer-out { to { transform: translateX(100%); } }
@keyframes drawer-fade { from { opacity: 0; } }

.basket-drawer-panel { display: flex; flex-direction: column; height: 100%; }

.basket-drawer-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--ink-200);
}

.basket-drawer-head h2 { margin: 0; font-size: 1.125rem; }

.basket-drawer-close {
    width: 36px;
    height: 36px;
    border: 0;
    border-radius: 50%;
    background: var(--ink-100);
    color: var(--ink-700);
    font-size: 1.375rem;
    line-height: 1;
    cursor: pointer;
}

.basket-drawer-close:hover { background: var(--ink-200); }

.basket-drawer-body { flex: 1; overflow-y: auto; padding: var(--space-5); }

.basket-drawer-range {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0 0 var(--space-4);
    color: var(--ink-600);
    font-size: .875rem;
}

.basket-drawer-lines { list-style: none; margin: 0; padding: 0; }

.basket-drawer-lines li {
    display: grid;
    grid-template-columns: 56px minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--ink-100);
}

@media (prefers-reduced-motion: no-preference) {
    .basket-drawer-lines li { animation: drawer-line .4s calc(var(--i, 0) * 60ms + 120ms) cubic-bezier(.2, .8, .2, 1) both; }
}

@keyframes drawer-line { from { opacity: 0; transform: translateX(16px); } }

.basket-drawer-thumb {
    display: block;
    width: 56px;
    height: 56px;
    border-radius: var(--radius);
    overflow: hidden;
    background: var(--ink-100);
}

.basket-drawer-thumb img,
.basket-drawer-thumb .placeholder { width: 100%; height: 100%; object-fit: cover; }

.basket-drawer-thumb .placeholder svg { width: 24px; height: 24px; }

.basket-drawer-info { display: flex; flex-direction: column; min-width: 0; font-size: .9375rem; }

.basket-drawer-info a { color: var(--ink-900); font-weight: 560; overflow-wrap: anywhere; }

.basket-drawer-price { font-weight: 600; white-space: nowrap; }

.basket-drawer-total {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin: var(--space-4) 0 0;
    font-size: 1.0625rem;
}

.basket-drawer-total strong { font-size: 1.25rem; }

.basket-drawer-note { margin: var(--space-4) 0 0; font-size: .875rem; }

.basket-drawer-actions { display: grid; gap: var(--space-2); margin-top: var(--space-5); }

.basket-drawer-empty { text-align: center; padding-top: var(--space-6); }

.basket-drawer-empty .empty-art { width: 140px; height: auto; margin: 0 auto var(--space-3); }

/* Solange die Vorschau lädt: drei schimmernde Zeilen in der Form, die gleich kommt. */
.basket-drawer-loading { display: grid; gap: var(--space-3); }

.basket-drawer-loading i {
    display: block;
    height: 64px;
    border-radius: var(--radius);
    background: linear-gradient(90deg, var(--ink-100) 25%, var(--ink-50, #f6f8f9) 50%, var(--ink-100) 75%);
    background-size: 200% 100%;
    animation: shimmer 1.2s ease-in-out infinite;
}

/* --------------------------------------------------- Preis rollt mit */

/*
 * Der Preis rollt zur neuen Zahl (`rollPrice()`) und färbt sich dabei kurz:
 * grün, wenn er gesunken ist, in der Markenfarbe, wenn er gestiegen ist –
 * nicht rot, ein längerer Zeitraum ist kein Fehler.
 */
.is-rolling-down { animation: price-down .9s ease; }
.is-rolling-up { animation: price-up .9s ease; }

@keyframes price-down { 20% { color: var(--ok-700); } }
@keyframes price-up { 20% { color: var(--brand-600); } }

/* ------------------------------------------- Platzhalter beim Blättern */

/*
 * Nach einem Klick in die Seitennavigation stehen statt der alten Kacheln
 * schimmernde Platzhalter da (`initPagingSkeleton()`). Die Kacheln behalten
 * ihre Maße – nur ihr Inhalt tritt zurück, Bild und Text werden zu Flächen.
 */
.item-grid.is-skeleton .item-card { pointer-events: none; }

.item-grid.is-skeleton .item-thumb > * { visibility: hidden; }

.item-grid.is-skeleton .item-body *,
.item-grid.is-skeleton .item-actions > * {
    color: transparent !important;
    border-color: transparent !important;
    background: var(--ink-100) !important;
    border-radius: 6px;
}

.item-grid.is-skeleton .item-body svg,
.item-grid.is-skeleton .item-body img,
.item-grid.is-skeleton .item-actions svg { visibility: hidden; }

.item-grid.is-skeleton .item-card::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 3;
    background: linear-gradient(100deg, transparent 30%, rgba(255, 255, 255, .65) 50%, transparent 70%);
    background-size: 220% 100%;
    animation: shimmer 1.2s ease-in-out infinite;
}

/* ---------------------------------------------- Zeitstrahl im Portal */

/*
 * Der Weg des Auftrags von oben nach unten (`element/order_timeline`).
 * Erledigtes trägt einen Haken in der Markenfarbe, die nächste Station
 * pulsiert sacht – sie ist die, auf die der Kunde wartet.
 */
.order-timeline { list-style: none; margin: 0; padding: 0; }

.order-timeline li {
    position: relative;
    display: grid;
    grid-template-columns: 24px minmax(0, 1fr);
    gap: var(--space-3);
    padding-bottom: var(--space-4);
}

.order-timeline li:last-child { padding-bottom: 0; }

/* Die Linie zur nächsten Station – erledigt in Farbe, offen gestrichelt. */
.order-timeline li:not(:last-child)::before {
    content: "";
    position: absolute;
    left: 11px;
    top: 24px;
    bottom: 0;
    width: 2px;
    background: repeating-linear-gradient(to bottom, var(--ink-200) 0 4px, transparent 4px 8px);
}

.order-timeline li.is-done:not(:last-child)::before { background: var(--brand-600); }

.order-timeline-dot {
    display: grid;
    place-items: center;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    border: 2px solid var(--ink-200);
    background: var(--white);
    color: var(--white);
}

.order-timeline li.is-done .order-timeline-dot { background: var(--brand-600); border-color: var(--brand-600); }

.order-timeline li.is-next .order-timeline-dot {
    border-color: var(--brand-600);
    box-shadow: 0 0 0 4px var(--brand-100);
}

.order-timeline-text { display: flex; flex-direction: column; padding-top: 1px; font-size: .9375rem; }

.order-timeline-text .muted { font-size: .8125rem; }

.order-timeline li.is-open .order-timeline-text strong { color: var(--ink-500); font-weight: 520; }

.order-timeline li.is-next .order-timeline-text strong { color: var(--brand-700); }

@media (prefers-reduced-motion: no-preference) {
    .order-timeline li {
        animation: timeline-in .45s calc(var(--i, 0) * 90ms) cubic-bezier(.2, .8, .2, 1) both;
    }

    .order-timeline li.is-next .order-timeline-dot { animation: timeline-pulse 2.2s ease-in-out infinite; }
}

@keyframes timeline-in { from { opacity: 0; transform: translateY(8px); } }

/*
 * Im Portal steht der Zeitstrahl ganz oben über die volle Breite. Ab Tablet
 * läuft er von links nach rechts, die Linie zur nächsten Station liegt dann
 * waagerecht zwischen den Punkten; auf dem Handy bleibt er senkrecht.
 */
.portal-timeline { margin: var(--space-2) 0 var(--space-5); }

@media (min-width: 768px) {
    .portal-timeline .order-timeline {
        display: grid;
        grid-auto-flow: column;
        grid-auto-columns: minmax(0, 1fr);
        gap: var(--space-3);
    }

    .portal-timeline .order-timeline li {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: var(--space-2);
        padding-bottom: 0;
        text-align: center;
    }

    .portal-timeline .order-timeline li:not(:last-child)::before {
        left: calc(50% + 16px);
        right: calc(-50% - var(--space-3) + 16px);
        top: 11px;
        bottom: auto;
        width: auto;
        height: 2px;
        background: repeating-linear-gradient(to right, var(--ink-200) 0 4px, transparent 4px 8px);
    }

    .portal-timeline .order-timeline li.is-done:not(:last-child)::before { background: var(--brand-600); }

    .portal-timeline .order-timeline-text { align-items: center; padding-top: 0; }
}


@keyframes timeline-pulse {
    50% { box-shadow: 0 0 0 8px rgba(34, 135, 157, 0); }
}

/* Was gerade hinzugekommen ist – die Meldung, die die Schublade ersetzt. */
.basket-drawer-added {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0 0 var(--space-4);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius);
    background: var(--ok-100);
    color: var(--ok-700);
    font-size: .9375rem;
    font-weight: 560;
}

/* Tage ohne Übergabe in der Datumsauswahl: gesperrt und schraffiert. */
.flatpickr-day.is-closed,
.flatpickr-day.is-closed:hover {
    background-image: repeating-linear-gradient(135deg, transparent 0 5px, rgba(15, 23, 42, .1) 5px 7px);
    color: var(--ink-400);
    text-decoration: line-through;
    text-decoration-color: rgba(15, 23, 42, .25);
}

/*
 * „Neu" und „Beliebt" an der Kachel (`ItemBadgeService`): klein, über dem
 * Namen, neben „Set". „Neu" in der Markenfarbe, „Beliebt" warm – zwei
 * Schilder, die man auf einen Blick auseinanderhält.
 */
.item-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    margin-right: 4px;
    font-weight: 600;
}
.item-badge .icon-xs { width: 12px; height: 12px; }
.item-badge-new {
    background: var(--brand-50, #e8f4f6);
    color: var(--brand-700, #14515f);
}
.item-badge-popular {
    background: #fff1e6;
    color: #b4500a;
}
@media (prefers-reduced-motion: no-preference) {
    .item-badge-popular svg { transform-origin: 50% 90%; animation: badge-flicker 2.4s ease-in-out 3; /* dreimal, dann Ruhe */ }
}
@keyframes badge-flicker {
    0%, 100% { transform: scale(1) rotate(0); }
    40% { transform: scale(1.12) rotate(-4deg); }
    60% { transform: scale(.96) rotate(3deg); }
}
