/*
 * MWFewoManager — Zeitraum- und Personenwähler, Stylesheet
 *
 * Die Vorlage ist contao-mwfm-backoffice-bundle/mockups/MOCKUP-PICKER-ENTWURF.html.
 * Dieses Stylesheet ist daraus HERAUSGESCHNITTEN, nicht abgetippt: Wer hier
 * etwas ändert, ändert es dort mit, sonst driften Entwurf und Anwendung
 * auseinander — und der Entwurf ist die Referenz, gegen die verglichen wird.
 *
 * ZWEI EBENEN. .picker-scope übersetzt die Tokens der Seite in die
 * --pick-*-Tokens des Bauteils; alles darunter benutzt nur noch diese. Eine
 * Seite ohne eigene Tokens bekommt die Rückfallwerte und sieht trotzdem
 * richtig aus.
 *
 * DIE AMPEL ERBT NICHT. Grün heißt frei, rot heißt belegt — das ist eine
 * Bedeutung, keine Gestaltung, und steht deshalb an der Wurzel statt im
 * Scope. Die Werte sind dieselben wie in ui-default.css (--badge-*-bd);
 * geprüft wird das mit python3 tools/statusfarben-lint.py.
 *
 * Diese Datei ist das einzige Stylesheet des Wählers im Frontend. Die
 * abgelösten datepicker.css und personpicker.css gibt es nicht mehr.
 */

/* =====================================================================
DIE TOKEN-BRÜCKE
   ------------------------------------------------------------------ */

/* DAS BAUTEIL BRINGT SEIN box-sizing SELBST MIT.

   picker.css hat sich bisher darauf verlassen, dass die einbettende
   Seite border-box global setzt. In Anfrage und Buchungsstrecke tut
   sie das — dort lädt das Stylesheet des Bundles. Auf der
   OBJEKTDETAILSEITE ist die Seite das Kundentheme, und dort gilt die
   Annahme nicht.

   Die Wirkung war genau die gemeldete: .pick__trigger hat
   width: 100% und dazu Polsterung und Rahmen. Unter content-box
   addieren sich die auf die vollen 100% — das Feld wurde breiter als
   seine Karte und ragte rechts heraus, sobald ein Zeitraum darin
   stand.

   Derselbe Gedanke wie bei der Klasse picker-scope, die
   MwfmPicker.init() selbst anhängt: Eine Seite, die daran denken
   muss, ist eine Seite, die es irgendwann vergisst. Und bei fünf
   Einbindungen (Anfrage, Buchungsstrecke, Detailseite, Suche, Widget)
   reicht eine, die es anders hält. */
.picker-scope,
.picker-scope *,
.picker-scope *::before,
.picker-scope *::after,
.pick,
.pick *,
.pick *::before,
.pick *::after,
.pick__panel,
.pick__panel *,
.pick__panel *::before,
.pick__panel *::after {
    box-sizing: border-box;
}

/* ZWEI SELEKTOREN, EIN BLOCK — und der zweite ist kein Schmuck.

   Das geöffnete Blatt hängt am body und liegt damit AUSSERHALB von
   .picker-scope. Die Lehre steht zwanzig Zeilen weiter unten, beim
   Tooltip, und ist hier gerissen worden: Ein Element, das man aus einem
   Scope heraushebt, verliert dessen Variablen. Das Blatt erschien ohne
   Hintergrund, ohne Rahmen und ohne Rundung — der Kalender stand
   gespenstisch über dem Seiteninhalt, die Texte darunter schienen
   durch. --pick-color-white war nicht weiß, sondern nichts, und eine
   undefinierte Variable in background heißt nicht „Standardwert",
   sondern „ungültige Angabe".

   Deshalb steht hier .pick__panel--floating daneben: die Klasse, die
   mountPanel() genau dann setzt, wenn das Blatt ausgelagert ist. Kein
   zweiter Block, keine Kopie — eine Definition, zwei Orte, an denen sie
   gilt.

   WAS DAS NICHT KANN: Die Tokens leiten von den Farben der Kundenseite
   ab (var(--color-primary, …)). Stehen die auf :root — der Normalfall
   —, gilt am body dasselbe. Definiert eine Seite sie stattdessen auf
   einem Container, greift für das ausgelagerte Blatt der Fallback: Es
   sieht dann nach Vorgabe aus statt nach Hausfarbe. Sichtbar bleibt es
   in jedem Fall, und das ist der Punkt. */
/* .pick STEHT HIER, WEIL DIE KLASSE DARÜBER VOM SKRIPT KOMMT.

   MwfmPicker.init() hängt picker-scope an — an den Bereich oder, bei
   einer freistehenden Konfiguration, an den body. Bis das Skript läuft,
   ist also KEINES dieser Tokens definiert, und eine undefinierte Variable
   in background oder border bedeutet nicht „Vorgabewert", sondern eine
   ungültige Angabe: Der Wähler erscheint ohne Fläche, ohne Rahmen, ohne
   Rundung — und springt beim ersten Skriptlauf ins Aussehen.

   Gemeldet wurde genau das: „Beim initialen Laden haben die Felder kein
   Layout." Die Ursache ist nicht das Skript, sondern die Abhängigkeit von
   ihm. Ein Bauteil, dessen Aussehen erst nach dem Skript stimmt, flackert
   immer; wie schnell das Skript ist, verschiebt den Zeitpunkt und behebt
   nichts.

   .pick steht im Markup, das der Server ausliefert. Damit gilt die
   Brücke ab dem ersten Malen. .picker-scope bleibt daneben stehen — es
   deckt ab, was AUSSERHALB eines .pick liegt (Hintergrund, Tooltip) und
   ist zugleich der Ort, an dem eine Seite ihre eigenen Werte einhängt.

   [data-pick-tooltip] steht aus demselben Grund in der Liste wie das
   ausgelagerte Blatt: Der Tooltip hängt am body, nicht im Bereich.
   Ohne ihn hier wären dort alle Tokens undefiniert — und undefiniert
   heißt in CSS nicht „Vorgabe", sondern ungültig: ein Kasten ohne
   Farbe, ohne Radius, ohne Dreieck. */
.picker-scope,
.pick,
.pick__panel--floating,
[data-pick-tooltip] {
    --pick-color-primary: var(--color-primary, #0072ce);
    --pick-color-primary-hover: var(--color-primary-hover, #005fa3);
    --pick-color-danger: var(--color-danger, #b91c1c);
    --pick-color-warning: var(--color-warning, #d08700);
    --pick-color-bg: var(--color-surface-alt, #f5f7fa);
    --pick-color-white: var(--color-surface, #ffffff);
    --pick-color-text: var(--color-text, #111827);
    --pick-color-text-muted: var(--color-text-muted, #6b7280);

    --pick-border-color: var(--border-color, #e5e7eb);
    --pick-border-radius: var(--border-radius, 8px);
    --pick-border-radius-md: var(--radius-md, 12px);
    --pick-border-width: var(--border-width, 1px);

    --pick-font-size-sm: .875em;

    /* DAS AUSWAHLFELD BRAUCHT EINEN EIGENEN PFEIL.
       Der native verschwindet, sobald irgendein Reset der Kundenseite
       appearance: none auf select setzt — und dann sieht ein
       Auswahlfeld aus wie ein Textfeld, in das man tippen könnte. Am
       Rechner merkt man den Unterschied gar nicht, auf dem Telefon erst
       beim Antippen. Gemeldet wurde genau das an den Altersfeldern der
       Kinder: drei Kästen „17 Jahre" ohne jedes Zeichen.

       Deshalb setzt das Bauteil appearance: none SELBST und bringt
       seinen Pfeil mit. Das ist derselbe Weg wie im Reinigungs-Login
       (contao-mwfm-cleaning-list-bundle/public/override.css) und aus
       demselben Grund: Ein Bauteil, das in einem fremden Stylesheet
       lebt, darf sich auf dessen Vorgaben nicht verlassen.

       ALS SVG, NICHT ALS ZEICHEN. Ein „▾" aus der Schriftart sitzt je
       nach System unterschiedlich hoch und fehlt auf manchen
       Android-Geräten ganz.

       DIE FARBE IST FEST. Ein Datei-URL kann keine CSS-Variable lesen;
       der Wert ist daher die Vorgabe von --pick-color-text-muted.
       Eine Seite, die --color-text-muted umdefiniert, bekommt den
       Pfeil trotzdem in diesem Grau — sichtbar, aber nicht mitgefärbt.

       background MUSS die Kurzform bleiben und Bild UND Farbe
       zusammen setzen, sonst nimmt die Regel darüber dem Pfeil wieder
       seinen Platz. */
    --pick-select-caret: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");

    --pick-tint-primary: color-mix(in srgb, var(--pick-color-primary) 10%, var(--pick-color-white));
    --pick-tint-warning: color-mix(in srgb, var(--pick-color-warning) 14%, var(--pick-color-white));
    --pick-tint-danger: color-mix(in srgb, var(--pick-color-danger) 9%, var(--pick-color-white));

    /* DIE AMPEL ERBT NICHT — und sie ist nicht erfunden. Die Werte
       stehen in contao-mwfm-backoffice-bundle/public/backend/ui-default.css
       — und hier an der WURZEL, siehe unten. */
    color: var(--pick-color-text);
}

/* =====================================================================
   DIE AMPEL STEHT AN DER WURZEL, NICHT IM SCOPE
   =====================================================================
   Sie erbt bewusst NICHT von der Kundenseite: Grün heißt frei, rot
   heißt belegt — das ist eine Bedeutung, keine Gestaltung. Eine Seite
   mit roter Hausfarbe färbte sonst freie Tage rot.

   WARUM AN DER WURZEL. Der Tooltip liegt als eigenes Element am
   Seitenende, damit er jedes Neuzeichnen überlebt und das Fenster
   verlassen kann. Dort gilt .picker-scope nicht mehr — solange die
   Tokens dort hingen, war sein Hintergrund undefiniert und er erschien
   weiß auf weiß. Genau das war im Bildschirmfoto zu sehen: Der Text
   stand da, der Kasten fehlte.

   Die Lehre ist allgemeiner: Ein Element, das man aus einem Scope
   heraushebt, verliert dessen Variablen. Wer es heraushebt, muss die
   Variablen mitnehmen — oder sie gehören von vornherein nicht in den
   Scope. Hier gilt das Zweite. */

:root {
    --calendar-bg-free: #eaf7ef;
    --calendar-fg-free: #46594d;
    --calendar-border-free: #b3dfc1;
    --calendar-bg-booked: #f6d2d2;
    --calendar-fg-booked: #e2a1a1;
    --calendar-border-booked: #e2a1a1;
    --calendar-bg-blocked: #e0e0e0;
    --calendar-fg-blocked: #bcbcbc;
    --calendar-border-blocked: #bcbcbc;
    --calendar-tooltip-bg: #333;
    --calendar-tooltip-fg: #fff;

    /* Das Rundungsmaß des Tooltips. Es steht hier und nicht in
       .picker-scope, weil der Tooltip außerhalb des Bereichs liegt —
       er muss dem Überlauf der Karte entkommen können. Ein Token, das
       nur im Bereich definiert ist, ist für ihn nicht vorhanden, und
       eine undefinierte Variable in border-radius bedeutet nicht
       „Standardwert", sondern eine ungültige Angabe: Die Ecken blieben
       eckig, auch wenn ringsum alles rund ist. Genau so sah es aus. */
    --calendar-tooltip-radius: var(--border-radius, 8px);

    /* Nur im Eigentümer-Login sichtbar — für den Gast ist ein Tag frei
       oder nicht, das Warum geht ihn nichts an. */
    --calendar-bg-reserved: #ffe0cc;
    --calendar-border-reserved: #e8a97a;
    --calendar-bg-owner: #ffe5b4;
    --calendar-border-owner: #f3b97e;
    --calendar-bg-edit: #fff7d1;
    --calendar-border-edit: #f0c36d;
}

/* =====================================================================
DAS AUSLÖSEFELD
   ------------------------------------------------------------------ */

/* DAS FELD MUSS SCHRUMPFEN DÜRFEN.

   min-width: auto ist die Vorgabe für Grid- und Flex-Kinder, und sie
   heißt: „nicht schmaler als mein Inhalt". Der Inhalt ist hier ein
   Zeitraum in voller Länge — „09.12.2026 → 16.12.2026 · 7 Nächte" —
   mit white-space: nowrap. In der 312 Pixel breiten Buchungsbox der
   Objektdetailseite ergibt das eine Mindestbreite von 345 Pixeln, und
   die spannt die Rasterspalte auf: Nicht nur das Feld ragt dann heraus,
   sondern ALLES in der Karte — Überschrift, Preisaufstellung, beide
   Knöpfe, die Trennlinie. Gemessen an der echten Seite: 37 Elemente,
   jedes um dieselben 33,7 Pixel.

   Dass die Kürzung am .pick__value vorbereitet ist (min-width: 0,
   text-overflow: ellipsis), genügt dafür NICHT: Sie erlaubt dem Wert
   zu schrumpfen, sobald das Feld eine Breite hat. Wie breit das Feld
   sein DARF, entscheidet diese Zeile hier — eine Ebene höher.

   Nachgemessen, welche Zeile es wirklich tut: .pick__trigger allein
   ändert nichts (37 Überhänge bleiben), .pick allein räumt sie
   vollständig ab. Deshalb steht hier eine Regel und nicht zwei. */
/* =====================================================================
   DIE KOMPONENTE BRINGT IHRE EIGENEN ABSTÄNDE MIT — ALLE.
   =====================================================================
   Sie steht in einer fremden Seite, und deren Grundtypografie gilt auch
   für sie: Ein p { margin: 0 0 1rem } im Kundentheme trifft jeden
   Absatz, den picker.js erzeugt, und ein ul { margin: 1em 0;
   padding-left: 2.5em } trifft die Legende samt Einzug.

   DAS ADDIERT SICH ZUM RHYTHMUS DER KOMPONENTE, statt ihn zu ersetzen.
   Die Abstände zwischen den Blöcken kommen aus dem gap des
   Kalenderkastens — genau deshalb wurden die Block-margin-bottom hier
   einmal entfernt. Ein geerbtes margin-bottom legt sich dann obendrauf,
   und der Kasten steht auf der einen Kundenseite anders als auf der
   nächsten. Von außen sieht das nach einem Fehler in der Komponente aus.

   Gemessen mit einem typischen Theme (p { margin: 0 0 1rem },
   ul { margin: 1em 0; padding-left: 2.5em }) erbten sechs Bauteile:
   cal__guide, cal__message, cal__legend, cal__legend-item,
   pp__extra und der Einzug der Legende. Drei waren abgesichert, weil
   sie zufällig ein eigenes margin tragen.

   DIESER BLOCK STEHT GANZ OBEN UND NICHT UNTEN. Er setzt nur zurück;
   was die Komponente selbst an Abstand will, steht weiter unten und
   gewinnt durch die Reihenfolge — bei gleicher Spezifität entscheidet
   sie, und genau daran ist hier schon einmal eine Regel gescheitert.
   Wer diesen Block nach unten verschiebt, löscht damit die eigenen
   Abstände der Komponente.
   ------------------------------------------------------------------ */
.pick__head-title,
.pick__sr,
.cal__guide,
.cal__message,
.cal__price-note,
.cal__month-title,
.cal__legend,
.cal__legend-item,
.pp__extra,
.pp__max,
.pp__notice {
    margin: 0;
    padding: 0;
}

.pick { position: relative; min-width: 0; }

/* DIE BESCHRIFTUNG ERBT, SIE BESTIMMT NICHT.

   Sie steht neben den Beschriftungen der Seite — im Suchformular direkt
   neben „ORT", das aus einem gewöhnlichen Contao-Feld kommt. Wenn das
   Bauteil hier eigene Schriftgröße, Stärke oder Versalien setzt, sehen
   zwei Beschriftungen nebeneinander verschieden aus, und keine Seite kann
   das ohne eine Regel auf einen internen Klassennamen richten. Genau das
   war zu sehen: „ORT" in Großbuchstaben, „Reisezeitraum" daneben nicht.

   Deshalb nur, was die Beschriftung zu einer Beschriftung macht: eigene
   Zeile und ein Abstand nach unten. Alles Übrige — Familie, Größe,
   Stärke, Laufweite, Farbe, Versalien — kommt von dort, wo es hingehört:
   von der Seite.

   OHNE THEME IST DAS KEIN VERLUST. Eine Beschriftung in der Schrift ihrer
   Umgebung ist lesbar und unauffällig; sie fällt nur dann auf, wenn sie
   ANDERS ist als die daneben. Wer sie im Bauteil hervorheben will, setzt
   --pick-label-weight — die eine Stelle, an der das Bauteil doch eine
   Meinung anbietet, weil „halbfett" die häufigste ist. */
.pick__label {
    display: block;
    margin-bottom: .35em;
    font: inherit;
    font-weight: var(--pick-label-weight, inherit);
    color: inherit;
}

.pick__trigger {
    display: flex; align-items: center; gap: .7em;
    width: 100%;
    /* Gleiche Höhe wie ein Textfeld daneben, in derselben Rechnung.
       Ändert die Kundenseite die Schriftgröße, wandern beide gemeinsam. */
    min-height: calc(1.4em + 2 * .7em + 2 * var(--pick-border-width));
    padding: .7em .85em;
    font: inherit; text-align: left;
    color: var(--pick-color-text); background: var(--pick-color-white);
    border: var(--pick-border-width) solid var(--pick-border-color);
    border-radius: var(--pick-border-radius);
    cursor: pointer;

    /* DAS FELD IST EIN KNOPF — UND IN EINER FREMDEN SEITE SIEHT MAN IHM
       DAS AN. Ein Kundentheme, das button { } gestaltet, trifft dieses
       Element mit: Schatten, Verlauf, Versalien, Laufweite, Systemoptik
       auf iOS. Die Zeilen darüber decken Schrift, Farbe, Fläche und
       Rahmen ab — hier steht der Rest, den ein Theme sonst mitbringt.

       Gemeldet wurde genau der Schatten: Das Zeitraumfeld trug einen
       Knopfschatten, den kein anderes Feld der Seite hat.

       WARUM KEIN Eingabefeld STATTDESSEN: Der Auslöser trägt drei
       Kinder — Zeichen, Wert, Pfeil —, und ein input kann keine Kinder
       haben. Dazu sendet die Eingabetaste in einem Textfeld das
       Formular ab, was ein Knopf mit type=button nicht tut, und
       Form.isValid() sammelt bewusst über data-required, weil der
       Browser an einem echten Feld seine eigene Fehlerblase zeigt
       (die Begründung steht ausführlich in _field.html.twig).

       box-shadow steht VOR den beiden Zustandsregeln darunter: Der rote
       Ring am ungültigen Feld und der blaue am geöffneten gewinnen, weil
       sie später stehen. Reihenfolge nicht vertauschen.

       outline bleibt unangetastet — das ist der Tastaturfokus, und den
       wegzuräumen wäre ein Rückschritt. */
    appearance: none; -webkit-appearance: none;
    margin: 0; min-width: 0;
    background-image: none; box-shadow: none;
    text-transform: none; letter-spacing: normal;
    text-shadow: none; text-decoration: none;
}
.pick__trigger:hover { border-color: color-mix(in srgb, var(--pick-color-primary) 45%, var(--pick-border-color)); }

/* DER RESET GILT AUCH IN DEN ZUSTÄNDEN — UND NUR DESHALB WIRKT ER.
   Der Block oben wiegt eine Klasse: 0,1,0. Ein Theme, das button
   gestaltet, wiegt 0,0,1 und verliert. Sobald es aber button:hover
   schreibt, wiegt es 0,1,1 und gewinnt wieder — der Schatten war im
   Ruhezustand weg und kam beim Überfahren zurück. Dasselbe gilt für
   :focus und :active, wo Themes gern einen Ring setzen oder den Knopf
   um ein Pixel nach unten schieben.

   Klasse plus Pseudoklasse wiegt 0,2,0 und schlägt beides.

   transform gehört dazu, weil ein gedrückter Knopf sonst wandert,
   während das Blatt darunter an seiner errechneten Stelle stehen
   bleibt — die beiden lösen sich sichtbar voneinander.

   outline steht bewusst NICHT hier: Das ist der Tastaturfokus. Wer ihn
   wegnimmt, macht das Feld für alle unbedienbar, die nicht mit der Maus
   arbeiten.

   ::-moz-focus-inner ist der gepunktete Innenrand, den Firefox seit
   jeher an Knöpfe zeichnet. Er hat kein Gegenstück in den anderen
   Browsern und sieht an einem Feld nach Fehler aus. */
.pick__trigger:hover,
.pick__trigger:focus,
.pick__trigger:focus-visible,
.pick__trigger:active {
    box-shadow: none; background-image: none;
    transform: none; text-shadow: none; filter: none;
    /* Fläche und Schrift werden hier WIEDERHOLT, nicht auf none
       gesetzt: Sie sollen ja etwas sein, nur eben dasselbe wie im
       Ruhezustand. Der Block ganz oben setzt beide — aber mit 0,1,0,
       und damit verliert er gegen ein button:hover mit 0,1,1. Ohne
       diese zwei Zeilen wird das Feld unter der Maus dunkel und der
       Text weiss, weil das Theme seine Knopffarben durchreicht.

       Die Rahmenfarbe steht bewusst NICHT hier: Die eigene
       Hover-Regel darüber setzt sie und wiegt ebenfalls 0,2,0. */
    background-color: var(--pick-color-white);
    color: var(--pick-color-text);
    /* Und der Rest des Resets vom Ruheblock, aus demselben Grund: Dort
       steht er mit 0,1,0 und verliert im Zustand gegen 0,1,1. Ein Theme,
       das button:hover die Polsterung oder die Laufweite ändert, ließ
       das Feld unter der Maus seine Größe wechseln. */
    padding: .7em .85em; min-width: 0; margin: 0;
    text-transform: none; letter-spacing: normal;
    text-decoration: none; border-radius: var(--pick-border-radius);
    appearance: none; -webkit-appearance: none;
}
.pick__trigger::-moz-focus-inner { border: 0; padding: 0; }

/* Ein Feld, dessen Zeitraum nicht gilt. Kein dickerer Rahmen — der
   verschiebt die Geometrie und lässt das Feld springen —, sondern ein
   Ring AUSSERHALB der Kante.

   ZWEIMAL DIE KLASSE, UND DAS IST KEIN VERSEHEN: Der Reset direkt
   darüber wiegt 0,2,0. Stünde hier nur pick__trigger--invalid mit
   0,1,0, verschwände der rote Ring in dem Moment, in dem der Anwender
   die Maus auf das falsche Feld bewegt — also genau dann, wenn er
   hinsieht. Die doppelte Klasse wiegt ebenfalls 0,2,0 und gewinnt,
   weil sie später steht. */
.pick__trigger.pick__trigger--invalid {
    border-color: var(--pick-color-danger);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--pick-color-danger) 30%, transparent);
}

.pick__trigger[aria-expanded="true"] {
    border-color: var(--pick-color-primary);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--pick-color-primary) 18%, transparent);
}
.pick__icon { flex: none; display: inline-flex; color: var(--pick-color-text-muted); }

/* Ein zu langer Wert wird abgeschnitten, nicht umgebrochen — ein
   Umbruch machte das Feld höher als sein Nachbar. min-width: 0 ist
   dafür zwingend: Ohne die Regel weigert sich ein Flex-Kind zu
   schrumpfen und text-overflow greift nie. */
.pick__value {
    flex: 1 1 auto; min-width: 0;
    overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.pick__value--empty { color: var(--pick-color-text-muted); }
.pick__sub { color: var(--pick-color-text-muted); }
.pick__sub::before { content: " · "; }
.pick__caret { flex: none; display: inline-flex; color: var(--pick-color-text-muted); transition: transform .15s ease; }
.pick__trigger[aria-expanded="true"] .pick__caret { transform: rotate(180deg); }

/* =====================================================================
DAS FENSTER
   =====================================================================
   Auf dem Telefon wird aus dem Fenster ein Blatt von unten, über die
   volle Breite und mit einem Griff zum Schließen. Das ist die Form,
   die eine Telefonoberfläche für eine Auswahl benutzt — ein
   freischwebendes Fenster mit 44em Breite hat dort keinen Platz.
   ------------------------------------------------------------------ */

.pick__panel {
    position: absolute; top: calc(100% + .45em); left: 0;
    z-index: 9999;
    background: var(--pick-color-white);
    border: var(--pick-border-width) solid var(--pick-border-color);
    border-radius: var(--pick-border-radius-md);
    box-shadow: 0 14px 44px color-mix(in srgb, var(--pick-color-text) 22%, transparent);
}
.pick__panel[hidden] { display: none; }

/* =====================================================================
   DIE FORM DES BLATTES STEHT IM CSS — UND DAS SKRIPT LIEST SIE AB
   =====================================================================
   Drei Formen: popover hängt am Feld, dialog liegt mittig mit
   abgedunkeltem Grund, sheet fährt auf dem Telefon von unten herein.
   Nur die erste muss ausgerechnet werden; die beiden anderen stellt das
   Stylesheet selbst hin.

   Warum als Eigenschaft und nicht als zweite Medienabfrage im Skript:
   Die Grenze zwischen popover und sheet ist EINE Zahl (46em), und
   sie steht unten in der Medienabfrage. Ein matchMedia('(max-width:
   46em)') im Skript wäre eine zweite Fassung derselben Zahl — und
   die erste Änderung, die nur eine von beiden anfasst, ließe das
   Blatt auf dem Telefon rechnen, wo es nichts zu rechnen gibt.
   So fragt das Skript das Stylesheet, statt es nachzubauen.
   ------------------------------------------------------------------ */
.pick__panel { --pick-panel-form: popover; }

/* AUSGELAGERT AN DEN body.

   Das Blatt hängt beim Öffnen nicht mehr in seinem Feld, sondern am
   Dokument — sonst fängt der Stapelkontext eines Vorfahren jede
   z-index-Zahl ab (die Bildergalerie der Objektdetailseite lag über
   dem geöffneten Kalender, obwohl der auf 9999 stand).

   Damit verliert top: calc(100% + .45em); left: 0 seinen Bezug: Es
   zählte vom Feld, jetzt zählte es vom Dokument. Also fixed, und
   die Zahlen kommen vom Skript aus dem Rechteck des Auslösers.

   Die Regel steht VOR --calendar und vor der Medienabfrage, damit
   beide sie überschreiben: Der Kalender bleibt mittig, das Blatt auf
   dem Telefon bleibt unten. */
.pick__panel--floating { position: fixed; top: auto; left: auto; }

/* =====================================================================
   DER KALENDER IST EIN DIALOG, KEIN AUFKLAPPER
   =====================================================================
   Er ist 44em breit — zwei Monate mit Tagespreis brauchen das, sonst
   bricht „212 €" in der Zelle um. Unter einem Feld hat er damit fast
   nirgends Platz: Auf der Objektdetailseite steht das Feld rechts in
   einer schmalen Spalte, im Filter in einer Reihe mit drei weiteren,
   im alten Widget in einem Rahmen von 320px.

   Jede Stelle einzeln auszurichten — hier linksbündig, dort rechts —
   wäre sechsmal dieselbe Positionsrechnung mit sechs Gelegenheiten,
   sie falsch zu machen. Genau das steht heute schon im Code: Der
   adjust-Callback mit getBoundingClientRect() und Scroll-Offsets
   ist SECHSMAL dupliziert.

   Deshalb öffnet der Kalender überall gleich: mittig auf der Seite,
   mit abgedunkeltem Grund. Das ist die Form, die der echte Datepicker
   im Übrigen schon hat — sein Markup heißt calendar-modal__….

   Der PERSONENWÄHLER bleibt am Feld. Er ist 25em schmal und passt
   überall darunter; ihn ins Zentrum zu heben wäre für drei Zähler
   zu viel Aufwand für den Leser.
   ------------------------------------------------------------------ */

.pick__panel--calendar {
    --pick-panel-form: dialog;
    position: fixed; top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: 44em; max-width: calc(100vw - 2em);
    max-height: calc(100vh - 3em);
    display: flex; flex-direction: column;
}
/* =====================================================================
   EIN ABSTAND FÜR DEN GANZEN KALENDER
   ---------------------------------------------------------------------
   Die Blöcke im Kalender — Anleitung, Meldung, Nächte, Monatsleiste,
   Gitter, Legende, Preishinweis — trugen ihren Abstand vorher selbst,
   und zwar in SECHS verschiedenen Werten:

     Anleitung    .9em   Meldung   .9em    (beide in .875em-Schrift!)
     Nächte       .9em   Leiste    .8em
     Preishinweis .6em   Legende  1em (oben)
     dazu das Polster des Rumpfes: 1.1em

   Dass .9em zweimal dasteht, heißt nicht, dass es zweimal dasselbe
   ist: Meldung und Anleitung setzen font-size: var(--pick-font-size-sm)
   (.875em), und ein em misst sich an der Schrift des EIGENEN Elements.
   Aus .9em wurden dort 0,7875em der Grundschrift, aus .9em an den
   Nächten volle 0,9. Sichtbar wurde das an der Fehlermeldung: darüber
   das Polster des Rumpfes (1,1em), darunter ihr eigener Abstand
   (0,7875em) — sie saß erkennbar nicht mittig zwischen Trennlinie und
   Monatsleiste.

   Jetzt trägt der KASTEN den Abstand, einmal, als gap. Ein gap misst
   sich an der Schrift des Containers, nicht an der der Kinder — damit
   ist jeder Abstand im Kalender derselbe, unabhängig davon, wie groß
   der jeweilige Block seine Schrift stellt. Und er ist derselbe wie das
   Polster zum Rand, also sitzt jeder Block mittig zwischen seinen
   Nachbarn.

   Beide Wurzeln stehen hier: .pick__body ist der gezeichnete Bereich
   im Fenster, [data-inline] der des eingebauten Kalenders. Beide sind
   this.root — render() schreibt direkt hinein, die Blöcke sind also
   unmittelbare Kinder. */
.pick__panel--calendar .pick__body,
[data-inline] {
    display: flex;
    flex-direction: column;
    gap: 1.1em;
}
.pick__panel--calendar .pick__body { overflow-y: auto; }

.pick__panel--persons { width: 25em; max-width: calc(100vw - 2em); }

.pick__head {
    display: flex; align-items: center; justify-content: space-between;
    gap: .5em;
    padding: .9em 1.1em;
    border-bottom: var(--pick-border-width) solid var(--pick-border-color);
}
.pick__head-title { margin: 0; font-size: 1em; font-weight: 800; }
.pick__head-actions { display: flex; gap: .3em; }
.pick__body { padding: 1.1em; }

/* DER FUSS MUSS UMBRECHEN DÜRFEN.
   Drei Aktionen nebeneinander — Zurücksetzen, Abbrechen, Übernehmen —
   brauchen mehr als die 23em des Personenfensters. Ohne flex-wrap
   schiebt die letzte über den Rand hinaus, und zwar sichtbar: Der
   Knopf steht dann halb außerhalb der Karte.

   min-width: 0 an den Knöpfen ist der zweite Teil: Ein Flex-Kind
   weigert sich sonst, unter seine Inhaltsbreite zu schrumpfen, und
   bricht lieber um, als schmaler zu werden. */
.pick__foot {
    display: flex; flex-wrap: wrap; gap: .6em;
    align-items: center; justify-content: space-between;
    padding: .9em 1.1em;
    border-top: var(--pick-border-width) solid var(--pick-border-color);
}
.pick__foot .btn { min-width: 0; }
.pick__reset {
    font: inherit; padding: 0;
    font-size: var(--pick-font-size-sm); font-weight: 600;
    color: var(--pick-color-text-muted);
    background: none; border: 0; cursor: pointer; text-decoration: underline;
}
.pick__foot-right { display: flex; gap: .6em; }

/* Der Monatsstreifen für das Telefon steht NICHT hier, sondern hinter
   .cal__months weiter unten. Warum, steht dort. */

/* =====================================================================
   DIE HAND
   ---------------------------------------------------------------------
   OFFEN über dem Kalender, GESCHLOSSEN während des Ziehens. Der Zeiger
   ist die einzige Stelle, an der überhaupt steht, dass man den Kalender
   greifen kann — eine Geste ohne Hinweis findet niemand, der nicht
   danach sucht.

   GESPERRTE TAGE BEHALTEN not-allowed. Das ist keine Ausnahme, die
   man streichen könnte: Der Zeiger ist dort die Antwort auf „warum
   passiert nichts, wenn ich klicke".

   user-select: none steht am Gitter und NICHT erst am Zug: Bis die
   Anlaufschwelle erreicht ist, hat der Browser die Markierung längst
   begonnen — eine Regel, die erst danach greift, kommt zu spät. Der
   Kalender ist ein Bedienelement; Tageszahlen markiert niemand, um sie
   zu kopieren.

   DIE REIHENFOLGE DER BEIDEN REGELN ENTSCHEIDET. Beide wiegen gleich
   schwer (drei Klassen); die zweite steht deshalb hinter der ersten und
   gewinnt, solange gezogen wird. Wer sie tauscht, bekommt während des
   Zugs wieder die offene Hand.

   Die Klasse sitzt am Wurzelelement des Kalenders, weil render()
   alles darunter austauscht — an .cal__months wäre sie nach dem
   ersten Monatswechsel weg. */
.cal__months,
.cal__months .cal__day:not([aria-disabled="true"]) { cursor: grab; }

.pick--dragging .cal__months,
.pick--dragging .cal__months .cal__day { cursor: grabbing; user-select: none; }

.pick__grip { display: none; }

/* MEDIENABFRAGE, NICHT CONTAINER-ABFRAGE.
   Im Entwurf stand hier @container page, weil die Prüfstandseite einen
   Geräte-Umschalter hat: ein .viewport-Element mit container-name: page,
   an dem sich Telefon und Rechner umschalten lassen. Dieses Element gibt es
   nur dort. Im Echtsystem deklariert niemand einen Container dieses Namens,
   und eine Abfrage auf einen Container, den es nicht gibt, trifft NIE zu —
   sie wirft keinen Fehler, sie tut einfach nichts.

   Das Blatt von unten wäre damit auf keinem Telefon je erschienen. Und es
   ist ohnehin eine Frage des GERÄTS, nicht der Breite des Fensters: Das
   Fenster ist auf dem Rechner 44em breit und läge unter der Schwelle. */
@media (max-width: 46em) {
    .pick__panel, .pick__panel--calendar {
        --pick-panel-form: sheet;
        position: fixed; inset: auto 0 0 0;
        top: auto; left: 0;
        transform: none;
        width: auto; max-width: none;
        max-height: 88vh;
        display: flex; flex-direction: column;
        border-radius: var(--pick-border-radius-md) var(--pick-border-radius-md) 0 0;
        border-bottom: 0;
    }
    .pick__body { overflow-y: auto; flex: 1 1 auto; }
    .pick__grip {
        display: block;
        width: 2.5em; height: .28em;
        margin: .6em auto -.2em;
        background: var(--pick-border-color);
        border-radius: 999px;
    }
    .pick__foot { flex-direction: column-reverse; align-items: stretch; }
    .pick__foot-right { flex-direction: column-reverse; }
    .pick__foot .btn { width: 100%; }
    .pick__reset { padding: .6em 0; }
}

/* DER ABDUNKELNDE GRUND.
   Er gehört zum Kalender, weil dieser als Dialog in der Mitte liegt —
   ohne ihn schwebt ein großer Kasten über einer voll sichtbaren Seite,
   und es ist nicht zu erkennen, dass der Rest gerade nicht bedienbar
   ist. Ein Klick darauf schließt.

   Beim Personenwähler bleibt er aus: Der sitzt am Feld, und eine
   abgedunkelte Seite für drei Zähler wäre unverhältnismässig. Auf dem
   Telefon erscheint er auch dort, denn das Blatt von unten überdeckt
   ohnehin alles. */
.pick__backdrop {
    /* DIE STAPELUNG DES WÄHLERS liegt bewusst hoch: 9998 Grund,
       9999 Blatt, 10000 Tooltip darin. Das Bauteil sitzt in FREMDEN
       Seiten — die Bildergalerie der Objektdetailseite lag mit ihrem
       eigenen z-index über dem Grund und schnitt Bilder mitten in den
       geöffneten Kalender. Wer eine Ebene gegen ein Theme stellt,
       das er nicht kennt, gewinnt nur mit Abstand. */
    position: fixed; inset: 0; z-index: 9998;
    background: rgb(0 0 0 / .45);
}
.pick__backdrop[hidden] { display: none; }
.pick__backdrop--quiet { background: none; }
@media (max-width: 46em) {
    .pick__backdrop--quiet { background: rgb(0 0 0 / .45); }
}

/* =====================================================================
   DAS EINFADEN
   =====================================================================
   Der abgelöste Wähler hatte es, picker.css nicht:
   Das Panel sprang aus dem Nichts an seinen Platz. Gemeldet wurde genau
   das — „zurzeit faden Datepicker und Personpicker ein, das sollte
   beibehalten werden".

   Die Werte sind die der alten Bauteile, nicht neu erfunden:
   0.18s auf dem Rechner (--personpicker-animation-duration-desktop),
   0.25s auf dem Telefon (--personpicker-animation-duration-mobile).
   Ein Wechsel der Geschwindigkeit fällt auf, auch wenn niemand sagen
   kann, woran es liegt.

   WARUM DAS OHNE JAVASCRIPT GEHT: Das Panel wird über [hidden]
   geschaltet, also zwischen display: none und sichtbar. Genau dieser
   Wechsel startet eine animation am Element neu — eine transition
   täte es NICHT, denn sie braucht zwei gerenderte Zustände, und
   display: none ist keiner.

   DER PERSONENWÄHLER SINKT, DER KALENDER BLENDET NUR AUF. Der Wähler
   hängt am Feld und kommt von dort — drei Pixel von oben sagen das
   mit. Der Kalender liegt als Dialog in der Mitte und gehört zu keinem
   Rand; eine Richtung wäre dort eine Behauptung.
   ------------------------------------------------------------------ */

:root {
    --pick-fade-duration-desktop: .18s;
    --pick-fade-duration-mobile: .25s;
}

.pick__panel--persons:not([hidden]) {
    animation: pick-fade-down var(--pick-fade-duration-desktop) ease-out;
}

.pick__panel--calendar:not([hidden]),
.pick__backdrop:not([hidden]) {
    animation: pick-fade-in var(--pick-fade-duration-desktop) ease-out;
}

@keyframes pick-fade-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes pick-fade-down {
    from { opacity: 0; transform: translateY(-3px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Auf dem Telefon sind beide ein Blatt von unten — also kommen beide
   von unten. transform würde hier gegen die Fixierung arbeiten, wenn
   das Panel bereits eine trüge; es trägt in der mobilen Fassung
   ausdrücklich transform: none. */
@media (max-width: 46em) {
    .pick__panel--persons:not([hidden]),
    .pick__panel--calendar:not([hidden]) {
        animation: pick-slide-up var(--pick-fade-duration-mobile) ease-out;
    }

    @keyframes pick-slide-up {
        from { opacity: 0; transform: translateY(100%); }
        to   { opacity: 1; transform: translateY(0); }
    }
}

/* =====================================================================
   .btn GEHÖRT DEM KUNDENTHEME, NICHT UNS
   =====================================================================
   Das Bauteil lebt in fremden Seiten, und .btn ist dort der
   gebräuchlichste Klassenname überhaupt. Im Theme der Demo steht

       .btn     { padding: var(--sp-3) var(--sp-5); border-radius: var(--r-pill);
                  border: 2px solid transparent; font-size: var(--fs-sm); }
       .btn svg { width: 18px; height: 18px; }

   — beides mit derselben Spezifität wie unsere Regeln (0,1,0). Wer
   gewinnt, entscheidet dann allein die Ladereihenfolge der Dateien, und
   die ist je Seite eine andere. Auf der Objektdetailseite gewann das
   Theme: Aus dem 2em großen Schließen-Knopf wurde eine breite Pille mit
   durchsichtigem Rahmen, und die Pfeile im Kalenderkopf verschwanden.

   Deshalb tragen die Knopfregeln hier ihren Ort mit: .picker-scope
   für alles im Bereich, .pick__panel für das ausgelagerte Blatt am
   body. Damit steht 0,2,0 gegen 0,1,0, und die Reihenfolge der
   Dateien spielt keine Rolle mehr.

   NICHT umbenannt — obwohl der eigene Namensraum sauberer wäre: Die
   Klassennamen stehen in zwei jsdom-Proben, in den Entwürfen und in
   den Bauplänen. Ein Umbenennen wäre die größere Änderung mit dem
   gleichen Ergebnis. Wenn der Name doch fällt, dann in einem eigenen
   Schritt und überall zugleich.
   ------------------------------------------------------------------ */
.picker-scope .btn,
.pick__panel .btn {
    font: inherit; padding: .6em 1.3em; font-weight: 700;
    border-radius: var(--pick-border-radius);
    border: var(--pick-border-width) solid transparent; cursor: pointer;

    /* Derselbe Reset wie am Auslöser, und hier noch dringlicher: Das
       Blatt hängt im geöffneten Zustand am body und steht damit MITTEN
       im Kundentheme — anders als der Auslöser, der im Widget hinter
       einem Shadow DOM liegt. Was ein Theme an button { } schreibt,
       trifft „Übernehmen" und „Zurücksetzen" also ungebremst.

       Die Zustandsfarben stehen darunter und gewinnen; keine von ihnen
       setzt einen Schatten, also bleibt es bei none. */
    appearance: none; -webkit-appearance: none;
    margin: 0; min-width: 0;
    background-image: none; box-shadow: none;
    text-transform: none; letter-spacing: normal;
    text-shadow: none; text-decoration: none;
}

/* Und dasselbe in den Zuständen, aus einem Grund, der leicht zu
   übersehen ist: Spezifität entscheidet nur zwischen Regeln, die
   DIESELBE Eigenschaft setzen. Der Block darüber wiegt zwar 0,2,0 und
   schlägt ein button:active mit 0,1,1 — aber er setzt gar kein
   transform. Ein Theme, das den gedrückten Knopf um ein Pixel nach
   unten schiebt, trifft also auf keinen Widerspruch und gewinnt
   kampflos.

   Die Farbregeln stehen absichtlich DANACH: Sie setzen background und
   border-color, hier steht nichts davon, also geraten sie sich nicht
   in die Quere. */
.picker-scope .btn:hover,
.picker-scope .btn:focus,
.picker-scope .btn:focus-visible,
.picker-scope .btn:active,
.pick__panel .btn:hover,
.pick__panel .btn:focus,
.pick__panel .btn:focus-visible,
.pick__panel .btn:active {
    box-shadow: none; background-image: none;
    transform: none; text-shadow: none; filter: none;
}
.picker-scope .btn::-moz-focus-inner,
.pick__panel .btn::-moz-focus-inner { border: 0; padding: 0; }

.picker-scope .btn--primary,
.pick__panel .btn--primary {
    color: var(--pick-color-white); background: var(--pick-color-primary);
    border-color: var(--pick-color-primary);
}
.picker-scope .btn--primary:hover,
.pick__panel .btn--primary:hover { background: var(--pick-color-primary-hover); border-color: var(--pick-color-primary-hover); }
.picker-scope .btn--primary:disabled,
.pick__panel .btn--primary:disabled { opacity: .45; cursor: not-allowed; }
.picker-scope .btn--secondary,
.pick__panel .btn--secondary {
    color: var(--pick-color-text); background: var(--pick-color-white);
    border-color: var(--pick-border-color);
}
.picker-scope .btn--icon,
.pick__panel .btn--icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 2em; height: 2em; padding: 0;
    color: var(--pick-color-text); background: var(--pick-color-white);
    border: var(--pick-border-width) solid var(--pick-border-color);
    border-radius: var(--pick-border-radius);
    cursor: pointer;
}
/* Das Zeichen behält die Größe, die das Markup ihm gibt. Das Theme
   der Demo setzt .btn svg { width: 18px; height: 18px } — in einem
   2em großen Knopf sprengt das die Fläche. */
.picker-scope .btn svg,
.pick__panel .btn svg { width: auto; height: auto; }

.picker-scope .btn--icon:hover:not(:disabled),
.pick__panel .btn--icon:hover:not(:disabled) { border-color: var(--pick-color-primary); color: var(--pick-color-primary); }
/* DER DEAKTIVIERTE KNOPF MUSS DEAKTIVIERT AUSSEHEN.
   opacity: .4 allein reicht nicht: Der Knopf behält damit seine
   weiße Fläche und seinen Rahmen und unterscheidet sich vom aktiven
   nur durch eine Spur weniger Kontrast — auf dem Personenwähler
   standen „−" und „+" bei einem Erwachsenen praktisch gleich da, und
   der Gast klickte ins Leere.

   Die Fläche sagt es jetzt mit: gedämpfter Hintergrund, blasser
   Rahmen, kein Hover. Nicht kräftiger, sondern anders — ein
   deaktivierter Knopf soll nicht auffallen, sondern erkennbar nicht
   zum Klicken einladen. */
.picker-scope .btn--icon:disabled,
.pick__panel .btn--icon:disabled {
    opacity: 1;
    cursor: not-allowed;
    color: var(--pick-color-text-muted);
    background: var(--pick-color-bg);
    border-color: transparent;
}

/* =====================================================================
DIE ANLEITUNGSZEILE UND DIE MELDUNG
   =====================================================================
   Zwei verschiedene Dinge, und sie dürfen nicht dieselbe Zeile teilen:

   Die ANLEITUNG sagt, was als Nächstes zu tun ist — „Anreise wählen",
   dann „Abreise wählen", dann das Ergebnis. Sie ist immer da.

   Die MELDUNG erscheint nur, wenn der Gast etwas versucht hat, das
   nicht geht. Sie sagt WARUM, nicht nur DASS. „Nicht verfügbar" lässt
   ihn raten; „Mindestaufenthalt 7 Nächte, Sie haben 4 gewählt" nicht.

   Stünden beide in einer Zeile, verdrängte die Meldung die Anleitung —
   und der Gast wüsste nach dem Fehler nicht mehr, was er tun soll.
   ------------------------------------------------------------------ */

/* KEIN BALKEN AN DER LINKEN KANTE.
   Der erste Entwurf setzte einen 3px-Streifen in der Primär- bzw.
   Warnfarbe an die linke Seite. Das ist im Projekt abgeschafft — #218
   hat den border-left als Akzent systemweit entfernt, und ich habe
   ihn hier wieder eingeführt, ohne nachzusehen.

   Stattdessen trägt die Fläche selbst die Farbe, und ein Rahmen in
   derselben Farbe umgibt sie vollständig. Das ist ruhiger, funktioniert
   in jeder Schreibrichtung und macht den Kasten nicht asymmetrisch. */
.cal__guide {
    display: flex; align-items: center; gap: .55em;
    padding: .6em .85em;
    font-size: var(--pick-font-size-sm);
    background: var(--pick-tint-primary);
    border: var(--pick-border-width) solid color-mix(in srgb, var(--pick-color-primary) 28%, var(--pick-color-white));
    border-radius: var(--pick-border-radius);
}
.cal__guide-step {
    flex: none;
    padding: .1em .5em;
    font-size: .85em; font-weight: 700;
    color: var(--pick-color-white);
    background: var(--pick-color-primary);
    border-radius: 999px;
}

.cal__message {
    display: flex; align-items: flex-start; gap: .55em;
    padding: .6em .85em;
    font-size: var(--pick-font-size-sm); font-weight: 600;
    background: var(--pick-tint-danger);
    color: color-mix(in srgb, var(--pick-color-danger) 80%, var(--pick-color-text));
    border: var(--pick-border-width) solid color-mix(in srgb, var(--pick-color-danger) 32%, var(--pick-color-white));
    border-radius: var(--pick-border-radius);
}
/* Steht die abgelehnte Strecke im Kalender, gehört die Meldung sichtbar
   zu ihr: voller Rahmen in derselben Farbe wie die Markierung darunter.
   Ohne diesen Bezug sind es zwei rote Dinge auf einer Seite; mit ihm ist
   es eine Aussage und ihre Begründung. Kein Akzent an der linken Kante —
   den gibt es im Projekt nicht mehr (#218). */
.cal__message--blocking { border-color: var(--pick-color-danger); }

.cal__message[hidden] { display: none; }
.cal__message-icon { flex: none; margin-top: .1em; }

/* =====================================================================
DIE AUFENTHALTSDAUER
   =====================================================================
   Das Dropdown gibt es im echten Datepicker bereits
   (calendar-stay-duration-nights, gefüllt mit 1..maxTravelNights).
   Zwei Änderungen schlägt der Entwurf vor:

   1. STUFEN statt jeder Zahl. Eine Liste von 1 bis 31 zwingt zum
      Scrollen durch Zahlen, die niemand sucht — 1 bis 7, dann 10, 14,
      21, 28 deckt ab, wonach wirklich gefragt wird.
   2. DIE ERSTE ZEITRAUMWAHL SETZT DEN WERT. Wer den 3. bis 11. wählt,
      hat acht Nächte gewählt — das Dropdown zeigt danach 8, auch wenn
      8 keine Stufe ist. Eine Stufenliste, die den gewählten Wert nicht
      zeigen kann, wäre eine Liste, die dem Anwender widerspricht.

   Die Obergrenze ist ein Monat. Länger sucht im Filter niemand, und
   der Kalender müsste sonst beliebig viele Monate im Voraus laden.
   ------------------------------------------------------------------ */

.cal__nights {
    display: flex; align-items: center; gap: .6em; padding: .6em .85em;
    font-size: var(--pick-font-size-sm);
    background: var(--pick-color-bg);
    border-radius: var(--pick-border-radius);
}
.cal__nights-label { font-weight: 600; }
.cal__nights select {
    font: inherit; font-size: var(--pick-font-size-sm);
    padding: .35em 2em .35em .5em;
    color: var(--pick-color-text);
    background: var(--pick-select-caret) no-repeat right .55em center / .8em .8em,
                var(--pick-color-white);
    border: var(--pick-border-width) solid var(--pick-border-color);
    border-radius: calc(var(--pick-border-radius) * .7);
    appearance: none; -webkit-appearance: none;
    cursor: pointer;
}

/* =====================================================================
DER KALENDER
   ------------------------------------------------------------------ */

.cal__nav {
    display: flex; align-items: center; justify-content: space-between;
    gap: .5em;
}
.cal__nav-label { font-weight: 700; }

/* JEDE ANZAHL IN EINER ZEILE — OHNE REGEL JE ANZAHL.
   grid-auto-flow: column legt die Monate nebeneinander statt
   untereinander, grid-auto-columns: 1fr gibt jedem denselben Anteil.
   Zwei Monate ergeben zwei gleiche Spalten, vier ergeben vier — ohne dass
   für jede Zahl eine eigene Regel geschrieben werden müsste.

   Nur ORDNEN, nicht entscheiden: WIE VIELE Monate es gibt, sagt
   monthsVisible() im Skript. Es misst die Breite des Kalenders gegen
   monthMinWidthPx (240), begrenzt auf monthsShown, und misst bei jeder
   Größenänderung neu. Das CSS führt hier nur aus, was dort entschieden
   wurde — deshalb braucht es data-months gar nicht mehr zu lesen.

   Hier standen vorher grid-template-columns: 1fr plus eine Sonderregel
   [data-months="2"] { 1fr 1fr }. Das ging nur auf, solange es genau zwei
   Fälle gab. Mit „so viele, wie in eine Zeile passen" wären es vier Regeln
   geworden, und die fünfte hätte beim ersten monthsShown: 5 gefehlt —
   sichtbar als Monate untereinander, also genau der Fehler, den es hier
   schon einmal gab. Eine Regel, die nicht zählt, kann sich nicht
   verzählen.

   Im Entwurf stand davor @container page (min-width: 46em) — eine ZWEITE
   Schwelle neben der des Skripts, und zwar auf einen Container, den es im
   Echtsystem nicht gibt. Beides zusammen ergab: JS setzte
   data-months="2", die Regel griff nie, und die Monate standen
   untereinander. Zwei Schwellen für dieselbe Frage sind auch ohne den
   fehlenden Container eine zu viel — welche gewinnt, sieht man erst am
   Ergebnis. */
.cal__months {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 1.5em;
}

/* =====================================================================
   DER MONATSSTREIFEN — GEWISCHT WIRD VOM BROWSER
   =====================================================================
   Übernommen aus dem Backoffice (booking-datepicker.css, #296). Dort
   ist das seit Längerem so gelöst, und der Weg hat sich als der bessere
   erwiesen: Nicht das Skript fängt eine Geste ab, sondern die Monate
   liegen nebeneinander in einer waagerecht scrollbaren Fläche. Schwung,
   Einrasten und — am wichtigsten — die Unterscheidung zwischen Tippen
   und Ziehen macht dann der Browser.

   Eine abgefangene Geste musste genau diese Unterscheidung nachbauen:
   Schwelle, Zeitsperre gegen den Klick nach dem Wischen, Streifen per
   transform mitziehen. Alles davon fühlte sich fremd an, weil es das
   ist.

   scroll-snap: Der Streifen bleibt auf einem Monat stehen, nicht
   irgendwo dazwischen.
   overscroll-behavior-x: contain: Waagerecht wischen zieht nicht die
   Seite darunter mit.
   scroll-behavior: auto ausdrücklich: Das Bauteil stellt die Position
   nach dem Zeichnen selbst ein, und ein von der Kundenseite geerbtes
   smooth machte daraus eine sichtbare Fahrt durch zwölf Monate.

   ZWEIMAL WIRKUNGSLOS, AUS ZWEI VERSCHIEDENEN GRÜNDEN — deshalb die
   beiden folgenden Absätze.

   ERSTENS: display: flex MUSS DABEISTEHEN. Die Regeln wurden aus dem
   Backoffice übernommen, und dort ist der Monatskasten ein Flexkasten.
   Hier ist .cal__months ein GITTER — und flex-wrap an einem Gitter
   und flex: 0 0 100% an dessen Kindern sind keine falschen Werte,
   sondern gar keine: Der Browser wirft nichts, er ignoriert sie. Der
   Streifen war damit vollständig verdrahtet und vollständig wirkungslos.

   ZWEITENS: DIESER BLOCK MUSS HINTER .cal__months STEHEN. Genau daran
   ist die Behebung des ersten Punktes gescheitert: display: flex wurde
   ergänzt, stand aber 378 Zeilen VOR .cal__months { display: grid }.
   Beide Regeln wiegen gleich schwer — eine Klasse —, also entscheidet
   die Reihenfolge, und das Gitter gewann. Die Datei las sich danach wie
   behoben, der Streifen lag weiter als zwölf Monate untereinander.
   Wer diesen Block verschiebt, nimmt ihm seine Wirkung zurück; eine
   Probe liest deshalb den EFFEKTIVEN display und nicht den Text der
   Regel.
   ------------------------------------------------------------------ */
.cal__months--strip {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scroll-behavior: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.cal__months--strip::-webkit-scrollbar { display: none; }
/* 0 0 100% heißt: genau EIN Monat im Bild, nie anderthalb. Ein
   angeschnittener zweiter sieht aus wie ein Darstellungsfehler, und auf
   einem Telefon ist für ihn ohnehin kein Platz. */
.cal__months--strip > * {
    flex: 0 0 100%;
    min-width: 0;
    scroll-snap-align: start;
}

.cal__month-title { margin: 0 0 .5em; font-size: var(--pick-font-size-sm); font-weight: 700; text-align: center; }

/* min-width: 0 ist hier nicht Kosmetik: Ein Rasterfeld nimmt sonst die
   Mindestbreite seines Inhalts an, und sieben Tageszellen mit Preis
   darin sind breiter als die halbe Spalte. Ohne das läuft der Monat aus
   dem Fenster heraus, statt zu schrumpfen. */
.cal__month { min-width: 0; }

/* Der Kasten um Kopfzeile und Tage. Er trägt role="grid" — dass er
   überhaupt da ist, hat also einen Grund jenseits des Abstands. */
.cal__grid { display: flex; flex-direction: column; gap: 3px; }
/* Ein Gitter mit ECHTEN Zeilen. display: contents an der Woche
   hätte das Rastern einfacher gemacht, nimmt aber in mehreren Browsern
   die Zeilenrolle mit weg — dann ist die Semantik da und wirkt nicht.
   Also rastert jede Woche selbst, und die Wochen stapeln sich. */
/* minmax(0, 1fr), NICHT 1fr. Die Kurzform bedeutet
   minmax(auto, 1fr): Eine Spalte darf wachsen, wenn ihr Inhalt breiter
   ist als ihr Anteil — und dann sind die sieben Spalten nicht mehr
   gleich breit. Aufgefallen ist es beim Umschalten auf CHF, weil
   „229,36 CHF" breiter baut als „212 €"; die Ursache lag aber nicht an
   der Währung, sondern hier, und traf schon vorher jeden Tag mit
   Nachkommastellen. */
.cal__weekdays, .cal__week { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 3px; }
.cal__days { display: flex; flex-direction: column; gap: 3px; }
.cal__weekday { padding: .3em 0; font-size: .72em; font-weight: 700; text-align: center; color: var(--pick-color-text-muted); }

.cal__day {
    position: relative;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: .05em;
    min-height: 3.1em; padding: .3em .15em;
    font: inherit; font-size: .85em;
    color: var(--pick-color-text);
    background: var(--pick-color-white);
    border: 1px solid transparent;
    border-radius: calc(var(--pick-border-radius) * .6);
    cursor: pointer;
}
.cal__day:hover:not([aria-disabled="true"]) { border-color: var(--pick-color-primary); }

/* Nicht buchbare Tage bleiben mit der Tastatur erreichbar — sie sind
   aria-disabled, nicht disabled. Der Zeiger sagt trotzdem, dass hier
   nichts zu holen ist. */
.cal__day[aria-disabled="true"] { cursor: not-allowed; }
.cal__day:focus-visible { outline: 2px solid var(--pick-color-primary); outline-offset: 1px; }
.cal__day--empty { visibility: hidden; }

/* =====================================================================
   HEUTE
   ---------------------------------------------------------------------
   Ein RING, keine Fläche. Die Flächen sind im Kalender vergeben: frei,
   belegt, gesperrt, Wechseltag an- und abreisend, Angebot, reserviert,
   Auswahl. Eine weitere Farbe daneben würde als ein weiterer ZUSTAND
   gelesen — und „heute" ist keiner, sondern ein Standort.

   Als ::after und nicht als border, weil der Rahmen der Zelle schon
   zwei Aufgaben hat: Er zeigt den Überfahr-Zustand und die Auswahl.
   Ein dritter Anspruch darauf hiesse, dass „heute" verschwindet,
   sobald die Maus darüberfährt.

   inset: 2px statt 0: So liegt der Ring INNERHALB des Zellrahmens
   und schluckt ihn nicht. Beide sind dann gleichzeitig zu sehen. */
.cal__day--today::after {
    content: '';
    position: absolute;
    inset: 2px;
    border: 2px solid var(--pick-color-primary);
    border-radius: inherit;
    pointer-events: none;
}

/* Auf einer gefärbten Fläche verschwände der primärfarbene Ring — auf
   dem gewählten Anreisetag liegt genau diese Farbe schon als
   Hintergrund. Dort nimmt er die Schriftfarbe der Zelle, und die ist
   zur Fläche per Definition kontrastreich. */
.cal__day--today.cal__day--selected::after,
.cal__day--today.cal__day--in-range::after { border-color: currentColor; }
.cal__day-number { font-weight: 600; line-height: 1.1; }
/* In der Zelle steht die ZAHL, das Währungszeichen steht einmal über
   dem Gitter. Der Grund ist Breite: Das Zeichen wiederholt sich in
   dreißig Zellen und sagt dreißigmal dasselbe, kostet aber in jeder
   Zelle Platz — und zwar je nach Währung unterschiedlich viel. Ein
   Kalender, dessen Spalten sich mit der Währung verschieben, ist kein
   Raster mehr.

   min-width: 0 gehört dazu: Ohne das bekommt ein Flex-Kind die Breite
   seines Inhalts als Untergrenze und drückt die Zelle doch wieder auf. */
.cal__day-price {
    min-width: 0; max-width: 100%;
    font-size: .72em; line-height: 1;
    color: var(--pick-color-text-muted);
}
.cal__price-note {
    margin: 0; text-align: right;
    font-size: var(--pick-font-size-sm);
    color: var(--pick-color-text-muted);
}

.cal__day--free { color: var(--calendar-fg-free); background: var(--calendar-bg-free); }

/* Farbe UND Schraffur. Wer Rot nicht sieht — rund acht Prozent der
   Männer —, sieht die Streifen. */
.cal__day--booked {
    color: var(--calendar-fg-booked);
    background: repeating-linear-gradient(135deg,
        var(--calendar-bg-booked) 0 4px,
        color-mix(in srgb, var(--calendar-border-booked) 40%, var(--calendar-bg-booked)) 4px 8px);
    cursor: not-allowed;
}
.cal__day--booked .cal__day-number { text-decoration: line-through; }

/* Dasselbe für die gesperrten: Grau auf Weiß ist der schwächste
   Unterschied von allen. Der Strich trägt die Auskunft auch dann, wenn
   die Farbe nicht ankommt — auf einem Ausdruck, bei starkem Licht, oder
   weil jemand Graustufen sieht. */
.cal__day--rule .cal__day-number,
.cal__day--past .cal__day-number { text-decoration: line-through; }

/* KEINE HALBEN TAGE.
   Der erste Entwurf teilte Wechseltage diagonal — halb frei, halb
   belegt, je nach Richtung. Zwei Gründe, das zurückzunehmen:

   1. Der Keil lief in die falsche Richtung, und das fiel erst im
      Bildschirmfoto auf. Ein Anreisetag ist vormittags frei und
      nachmittags belegt; der Verlauf zeigte es umgekehrt. Eine
      Darstellung, die man beim Lesen falsch herum bekommt, ist
      schlimmer als gar keine — sie sieht richtig aus.
   2. Den Zustand gab es im abgelösten Wähler nicht. Ein Tag ist
      dort frei oder belegt, und der Entwurf soll nichts versprechen,
      was die Anwendung nicht hält.

   Ein Wechseltag ist hier also ein ganzer freier Tag mit einem Hinweis
   im Tooltip — die Auskunft bleibt, die Halbierung fällt weg. */

/* =====================================================================
   DIE WEITEREN ZUSTÄNDE — nur im Eigentümer-Login
   =====================================================================
   Der echte Datepicker kennt acht Legendeneinträge: free, booked,
   reserved, blocked, owner, edit, selected, check. Der Gast
   braucht davon zwei — frei oder nicht. Für ihn ist es gleichgültig,
   WARUM ein Tag weg ist.

   Der Eigentümer dagegen sieht seinen eigenen Kalender: Er will wissen,
   ob ein Tag vermietet, nur reserviert, für ein Angebot vorgemerkt oder
   von ihm selbst belegt ist — das sind vier verschiedene Handlungen.
   Deshalb tragen diese Zustände nur im Inline-Modus eine eigene Farbe.

   Die Töne kommen aus der Statuspalette, nicht aus der Kundenseite. */
.cal__day--reserved {
    color: color-mix(in srgb, var(--calendar-border-reserved) 60%, #000);
    background: var(--calendar-bg-reserved);
}
.cal__day--owner {
    color: color-mix(in srgb, var(--calendar-border-owner) 60%, #000);
    background: var(--calendar-bg-owner);
}
.cal__day--option {
    color: color-mix(in srgb, var(--calendar-border-edit) 65%, #000);
    background: var(--calendar-bg-edit);
}

.cal__legend-swatch--reserved { background: var(--calendar-bg-reserved); border-color: var(--calendar-border-reserved); }
.cal__legend-swatch--owner { background: var(--calendar-bg-owner); border-color: var(--calendar-border-owner); }
.cal__legend-swatch--option { background: var(--calendar-bg-edit); border-color: var(--calendar-border-edit); }

/* =====================================================================
   DER INLINE-KALENDER
   =====================================================================
   Kein Fenster, kein Auslöser, keine Fußzeile — er steht einfach auf
   der Seite und zeigt, was frei ist. Das ist die Form für eine
   Objektseite („Verfügbarkeit") und für den Eigentümer-Login.

   Er teilt sich jede Zeile CSS mit dem Kalender im Fenster: Zwei
   Kalender, die nebeneinander gepflegt werden, laufen auseinander —
   genau das ist im Projekt schon passiert, BookingDatepicker im
   Backoffice hat 3.009 eigene Zeilen. Der Unterschied ist allein der
   Rahmen drumherum. */
.cal--inline {
    padding: 1.1em;
    background: var(--pick-color-white);
    border: var(--pick-border-width) solid var(--pick-border-color);
    border-radius: var(--pick-border-radius-md);
}
.cal--inline .cal__day { cursor: default; }

/* AUSGEBLENDET, nicht gesperrt.
   Ein Tag, der aus dem Rahmen fällt — Vergangenheit, jenseits der
   Belegung, unter dem Mindestaufenthalt — wird blass und ohne Preis
   gezeigt. Er bleibt an seiner Rasterstelle, denn ein Kalender mit
   Löchern lässt sich nicht mehr nach Wochentagen lesen. Aber er drängt
   sich nicht mehr auf. */
.cal__day--out { color: var(--calendar-fg-blocked); background: none; cursor: not-allowed; }
.cal__day--out .cal__day-price { visibility: hidden; }
.cal__day--past { opacity: .35; }

/* Gesperrt durch eine REGEL, nicht durch Belegung — kein Anreisetag,
   Mindestaufenthalt. Grau gefüllt statt blass: Der Tag wäre frei, nur
   nicht für diese Wahl. */
.cal__day--rule { color: var(--calendar-fg-blocked); background: var(--calendar-bg-blocked); cursor: not-allowed; }

/* =====================================================================
   DER GEWÄHLTE ZEITRAUM
   =====================================================================
   Drei Stufen statt zwei, damit der Zeitraum als ein Stück lesbar ist:

   Die beiden ENDEN tragen die volle Primärfarbe und ein Pfeilzeichen —
   hinein für die Anreise, hinaus für die Abreise. Das Zeichen ist der
   Grund, warum man die beiden nicht verwechselt; zwei gleich gefärbte
   Zellen sind sonst nur zwei gefärbte Zellen.

   Die TAGE DAZWISCHEN bekommen eine kräftigere Tönung als zuvor und
   keine Rundung: Sie fließen zu einem Band zusammen, statt als Kette
   einzelner Kacheln zu erscheinen. Die Rundung sitzt nur noch außen,
   am ersten und letzten Tag.

   Alles andere bleibt, wie es war. */
.cal__day--selected {
    color: var(--pick-color-white);
    background: var(--pick-color-primary);
    border-color: var(--pick-color-primary);
    z-index: 2;
}
.cal__day--selected .cal__day-price { color: color-mix(in srgb, var(--pick-color-white) 80%, transparent); }

.cal__day--in-range {
    color: color-mix(in srgb, var(--pick-color-primary) 85%, var(--pick-color-text));
    background: color-mix(in srgb, var(--pick-color-primary) 22%, var(--pick-color-white));
    border-radius: 0;
}
.cal__day--in-range .cal__day-price { color: inherit; opacity: .75; }

/* Das Band schließt an den Enden bündig an: Der Anreisetag rundet
   nur links, der Abreisetag nur rechts. Ohne das bleibt zwischen
   Ende und Band ein heller Spalt. */
/* DIE ABGELEHNTE STRECKE. Dieselbe Form, andere Farbe: Der Gast
   soll seine Auswahl wiedererkennen und zugleich sehen, dass sie
   nicht gilt. Die belegten Tage darin behalten ihre eigene Schraffur
   — sie sind der Grund, und der bleibt sichtbar. */
.cal__day--rejected.cal__day--selected {
    background: var(--pick-color-danger);
    border-color: var(--pick-color-danger);
}
.cal__day--rejected.cal__day--in-range {
    color: color-mix(in srgb, var(--pick-color-danger) 85%, var(--pick-color-text));
    background: color-mix(in srgb, var(--pick-color-danger) 16%, var(--pick-color-white));
}
.cal__day--rejected.cal__day--in-range.cal__day--booked {
    color: var(--calendar-fg-booked);
    background: repeating-linear-gradient(135deg,
        var(--calendar-bg-booked) 0 4px,
        color-mix(in srgb, var(--calendar-border-booked) 55%, var(--calendar-bg-booked)) 4px 8px);
}

.cal__day--start { border-end-end-radius: 0; border-start-end-radius: 0; }
.cal__day--end { border-end-start-radius: 0; border-start-start-radius: 0; }

/* Das Pfeilzeichen sitzt IM Tag, über der Zahl. Es kostet keine
   eigene Zeile und ragt nicht aus dem Raster — die frühere Marke
   über dem Tag überdeckte die Zeile darüber. */
.cal__day-arrow {
    display: inline-flex;
    margin-bottom: -.1em;
    color: color-mix(in srgb, var(--pick-color-white) 85%, transparent);
}

/* --------------------------------------------------------------------
   DER TOOLTIP JE TAG
   =====================================================================
   Er sagt, was der Tag kostet und was an ihm gilt. Bewusst KEIN
   title-Attribut: Das erscheint erst nach etwa einer Sekunde, ist auf
   dem Telefon gar nicht erreichbar und lässt sich nicht gestalten.

   Er sitzt an fester Stelle über dem Kalender statt am Tag: Eine Blase
   über einer Zelle der ersten Zeile ragte aus dem Fenster, eine über
   der letzten Spalte nach rechts hinaus. Ein Streifen oben ist immer
   da, immer lesbar und verdeckt keinen anderen Tag.
   ------------------------------------------------------------------ */

.cal__tooltip {
    position: fixed;
    z-index: 10000;
    max-width: 20em;
    padding: .4em .75em;
    font-size: .8em; line-height: 1.4;
    color: var(--calendar-tooltip-fg);
    background: var(--calendar-tooltip-bg);
    /* Der Tooltip folgt dem Grundlayout: eckige Seite, eckiger Tooltip.
       Nach oben begrenzt, weil er nur gut zwei Zeilen hoch ist — ein
       Radius von 20px macht aus ihm eine Lutschtablette, an der das
       Dreieck nicht mehr anliegt. */
    border-radius: min(var(--calendar-tooltip-radius), 1em);
    box-shadow: 0 4px 14px rgb(0 0 0 / .25);
    pointer-events: none;
}
.cal__tooltip[hidden] { display: none; }

/* Das Dreieck zeigt nach unten auf den Tag. */
.cal__tooltip::after {
    content: "";
    position: absolute; top: 100%; left: 50%;
    border: .35em solid transparent;
    border-top-color: var(--calendar-tooltip-bg);
    transform: translateX(-50%);
}

/* Der Vorschlag steht IN der Meldung, nicht darunter: Er gehört zu
   ihr wie die Antwort zur Frage. Unterstrichen, weil er sich wie ein
   Link verhält — ein Klick, und der Tag steht. */
.cal__snap {
    padding: 0; margin-left: .15em;
    font: inherit; font-weight: 700;
    color: inherit; background: none; border: 0;
    text-decoration: underline; cursor: pointer;
}
.cal__snap:hover { text-decoration: none; }
.cal__snap:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }

.cal__legend {
    display: flex; flex-wrap: wrap; gap: .5em 1.1em; padding: 0; list-style: none; padding-top: .9em;
    font-size: .78em; color: var(--pick-color-text-muted);
    border-top: var(--pick-border-width) solid var(--pick-border-color);
}
.cal__legend-item { display: inline-flex; align-items: center; gap: .4em; }

/* Nur für die Sprachausgabe. Nicht display: none — das nimmt den Text
   auch der Sprachausgabe weg, und nicht visibility: hidden, aus
   demselben Grund. Ein Kasten von einem Pixel, aus dem Bild geschoben:
   vorhanden, vorlesbar, unsichtbar. */
.pick__sr {
    position: absolute; width: 1px; height: 1px;
    margin: -1px; padding: 0; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* Wer Bewegung abgeschaltet hat, bekommt keine. Das ist eine
   Systemeinstellung, die Menschen wegen Schwindel und Migräne setzen —
   sie zu überfahren ist kein Gestaltungsspielraum. */
@media (prefers-reduced-motion: reduce) {
    .picker-scope *, .cal__tooltip, .pick__panel, .pick__backdrop {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
    }
}
.cal__legend-swatch { width: 1.1em; height: 1.1em; flex: none; border-radius: 3px; border: 1px solid var(--pick-border-color); }
.cal__legend-swatch--free { background: var(--calendar-bg-free); border-color: var(--calendar-border-free); }
.cal__legend-swatch--booked {
    border-color: var(--calendar-border-booked);
    background: repeating-linear-gradient(135deg,
        var(--calendar-bg-booked) 0 3px,
        color-mix(in srgb, var(--calendar-border-booked) 40%, var(--calendar-bg-booked)) 3px 6px);
}
.cal__legend-swatch--rule { background: var(--calendar-bg-blocked); border-color: var(--calendar-border-blocked); }

/* =====================================================================
DER PERSONENWÄHLER
   ------------------------------------------------------------------ */

.pp__row { display: flex; align-items: center; justify-content: space-between; gap: 1em; padding: .65em 0; }
.pp__row + .pp__row { border-top: var(--pick-border-width) solid var(--pick-border-color); }
.pp__label { font-weight: 600; }
.pp__note { display: block; font-size: .78em; font-weight: 400; color: var(--pick-color-text-muted); }

.pp__stepper { display: flex; align-items: center; gap: .5em; flex: none; }
.pp__button {
    display: inline-flex; align-items: center; justify-content: center;
    width: 2em; height: 2em; padding: 0;
    font: inherit; color: var(--pick-color-primary);
    background: var(--pick-color-white);
    border: var(--pick-border-width) solid var(--pick-border-color);
    border-radius: 50%;
    cursor: pointer;
}
.pp__button:hover:not(:disabled) { border-color: var(--pick-color-primary); background: var(--pick-tint-primary); }
.pp__button:disabled { opacity: .35; cursor: not-allowed; }
.pp__count { min-width: 1.6em; text-align: center; font-weight: 700; font-variant-numeric: tabular-nums; }

/* Ein Altersfeld je Kind. Die Kurtaxe staffelt nach Alter, und ohne
   Alter je Kind kann das System sie nicht rechnen — daran ist #461
   schon einmal gerissen. Sichtbar statt im Aufklapper: Wer sie
   übersieht, bekommt später einen falschen Preis. */
.pp__ages {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(6.5em, 1fr));
    gap: .5em;
    margin-top: .3em; padding: .7em .8em;
    background: var(--pick-color-bg);
    border-radius: var(--pick-border-radius);
}
.pp__age { display: flex; flex-direction: column; gap: .2em; }
.pp__age-label { font-size: .75em; font-weight: 600; color: var(--pick-color-text-muted); }
.pp__age select {
    font: inherit; font-size: var(--pick-font-size-sm);
    padding: .4em 2em .4em .5em;
    color: var(--pick-color-text);
    background: var(--pick-select-caret) no-repeat right .55em center / .8em .8em,
                var(--pick-color-white);
    border: var(--pick-border-width) solid var(--pick-border-color);
    border-radius: calc(var(--pick-border-radius) * .7);
    appearance: none; -webkit-appearance: none;
    cursor: pointer;
}

/* DIE AUFBETTUNG.
   Sie steht in tl_mwfm_vacation_rental_extra_bed — Altersklasse,
   Anzahl, Preis je Nacht, je Jahr. Der Gast sieht sie heute nirgends:
   Der Personenwähler der Objektseite rechnet sie still in maxTotal
   ein, und im Suchformular ist sie hart auf 0 gesetzt.

   Hier bekommt sie eine eigene Zeile — sichtbar getrennt von den
   Plätzen, die im Preis enthalten sind. Denn genau das ist die Frage
   des Gastes: Ab welcher Person wird es teurer? */
.pp__extra {
    margin-top: .8em; padding: .75em .85em;
    font-size: var(--pick-font-size-sm);
    background: var(--pick-tint-warning);
    border-radius: var(--pick-border-radius);
}
.pp__extra-title { display: block; font-weight: 700; margin-bottom: .15em; }

/* Der Hinweis auf die mitschlafenden Kleinen ist eine Nebenauskunft,
   keine Warnung — er darf nicht so laut sein wie die Aufbettungszeile
   darüber, sonst sucht der Anwender einen Fehler, wo keiner ist. */
.pp__extra--quiet {
    color: var(--pick-color-text-muted);
    background: none;
    border-color: transparent;
}

/* Reicht die Aufbettung nicht, ist das kein Hinweis mehr, sondern eine
   Sperre: Es gibt keinen Preis, und der Wähler kann nicht übernommen
   werden. Also in der Warnfarbe, nicht im Fließtext. */
/* Der Hinweis zur Deckelung. Er warnt nicht — die Zahl ist jetzt ja
   gültig —, er erklärt nur, warum sie eine andere ist als angefragt.
   Also der ruhige Hinweiston, nicht die Warnfarbe. */
.pp__notice {
    margin: .9em 0 0; padding: .55em .8em;
    font-size: var(--pick-font-size-sm);
    color: var(--pick-color-text-muted);
    background: var(--pick-color-bg);
    border: var(--pick-border-width) solid var(--pick-border-color);
    border-radius: var(--pick-border-radius);
}

.pp__extra--short {
    color: var(--pick-color-danger);
    background: color-mix(in srgb, var(--pick-color-danger) 8%, var(--pick-color-white));
    border-color: color-mix(in srgb, var(--pick-color-danger) 35%, var(--pick-color-white));
}

.pp__max { margin: .9em 0 0; font-size: var(--pick-font-size-sm); color: var(--pick-color-text-muted); }
.pp__max--reached { color: var(--pick-color-warning); font-weight: 600; }
