/*
 * Flow-Theme "platform360" (Vaadin 25).
 *
 * Referenziert von @Theme("platform360") auf BootApplication. Ersetzt die frueheren Vaadin-8
 * SCSS-Themes (src/main/webapp/VAADIN/themes/th-*).
 *
 * Aufbau:
 *   1. Marken-Tokens        — die Werte der alten SCSS-Variablen als CSS-Custom-Properties
 *   2. Lumo-Mapping         — dieselben Werte auf die Lumo-Properties gelegt
 *   2a. Abstands-Tokens     — benannte Abstände (--p360-space-*, --p360-dialog-padding …)
 *   2b. Feld-Design         — Fläche und Rand aller Eingabefelder (--p360-field-*)
 *   2c. Reiter              — aktiver Reiter in allen vaadin-tabs, Etagen-Reiter (--p360-floor-tab-*)
 *   3. Chrome              — page-header / page-footer / AppLayout-Drawer
 *   4. Legacy-Klassen       — die von Theme.Styles referenzierten Klassen aus dem alten Theme
 *   5. Views                — Login und öffentliche Seiten, Startseite und weitere Views
 *
 * Quelle der Tokens ist das Vaadin-8-Theme auf origin/beta:
 *   VAADIN/themes/th-new-green/styles.scss        ($primary-color)
 *   VAADIN/themes/themeBase/_immotours.scss       (Menue-/Button-Farben, Status-Gruen/-Rot)
 *   VAADIN/themes/themeBase/platform/variables.scss (Font, Radius, Graustufen)
 *   VAADIN/themes/themeBase/platform/{page,color,component/component,component/emma}.scss
 *
 * Markenfarben je Domain: BootstrapCustomizer setzt --p360-primary (Flaeche), -action, -strong,
 * -text, -accent und -on-dark als Inline-Style am <html> (Werte und Rollen in BrandColor.java).
 * Abschnitt 1 leitet die hellen Toene aus --p360-primary ab.
 */

/* --- 1. Marken-Tokens (1:1 aus dem Vaadin-8-Theme) --- */

html {
    /*
     * th-new-green/styles.scss: $primary-color (Immo-Tours). Gilt nur, wenn die Bootstrap-Seite
     * keine Marke gesetzt hat.
     */
    --p360-primary: #33cc99;
    /*
     * Die hellen Toene rechnen wie Sass lighten(): HSL-Helligkeit plus x Prozentpunkte, bei
     * #33cc99 also exakt die alten festen Werte. Fuer jede andere Marke entstehen dieselben
     * Ableitungen, die Vaadin 8 aus deren $primary-color kompiliert hatte.
     */
    /* variables.scss: $v-selection-color = lighten($primary-color, 15%) */
    --p360-primary-light: hsl(from var(--p360-primary) h s calc(l + 15));
    /* component.scss: .no-table-entries-label = lighten($primary-color, 30%) */
    --p360-primary-lighter: hsl(from var(--p360-primary) h s calc(l + 30));
    /* general.scss: tr:hover = lighten($primary-color, 40%); Grund für Kartensymbole und die Pille „Aktiv“. */
    --p360-primary-tint: hsl(from var(--p360-primary) h s calc(l + 40));
    /*
     * Die Rollen der Markenfarbe, je Marke fest in BrandColor.java (hier die Werte von Immo-Tours).
     * Die Fläche selbst liegt auf Weiß bei 2:1 und ist als Schrift oder Symbol kaum zu erkennen.
     * Wer was nimmt:
     *   -action   Hauptknöpfe (Fläche mit weißer Schrift), Fokusrand, Ring-Bogen
     *   -strong   Symbole und große Zahlen ab etwa 24px
     *   -text     kleiner Text, Links, Überschriften, aktiver Reiter
     *   -accent   Symbole und Links unter dem Zeiger (Abschnitt 2d)
     *   -on-dark  Markenzeichen auf dunkler Fläche (Haken im Erfolgs-Toast)
     * Bei Grün und Orange sind -strong und -text Fast-Schwarz (--p360-ink), die Marke zeigt sich in
     * Flächen, Hauptknöpfen und unter dem Zeiger, dort in der Produktfarbe selbst; der geringere
     * Kontrast ist bewusst akzeptiert (Entscheidung Arne 03.11.). Blau bleibt blau.
     */
    --p360-primary-action: #33cc99;
    --p360-primary-strong: #26302c;
    --p360-primary-text: #26302c;
    --p360-primary-accent: #33cc99;
    --p360-primary-on-dark: #33cc99;
    /* Fast-Schwarz für neutrale Titel und den Erfolgs-Toast; derselbe Ton wie BrandColor.INK. */
    --p360-ink: #26302c;
    /* Grau für Bezeichnungen unter Zahlen in Karten und Ringen. */
    --p360-muted: #5f6b66;

    /* _immotours.scss: $menu_color (linkes Menue) */
    --p360-menu: #0d1522;
    /* variables.scss: $secondary-color (page-footer) */
    --p360-footer: #333333;
    /* _immotours.scss: $btn_login_color (Immotours-Orange) */
    --p360-cta: #ed6f2b;
    /* _immotours.scss: $btn_ok_color */
    --p360-ok: #27a736;
    /* _immotours.scss: $green / $red (Statusfarben) */
    --p360-status-green: #00b06d;
    --p360-status-red: #a83b3b;

    /*
     * Rechnungs-Status als Badge (weisse Schrift). Die Farbtoene stammen aus dem Vaadin-8-Theme
     * (InvoiceStatus#getColor, BillingState#getCssColor), sind aber so weit abgedunkelt, dass
     * Weiss darauf mindestens 4,5:1 erreicht — die Originalwerte lagen zwischen 1,07:1 (Gelb) und
     * 2,83:1 (Gruen) und waren als Untergrund fuer Text unbrauchbar.
     * Wer hier dreht: Weiss auf dem neuen Wert nachmessen, sonst faellt der Badge unter AA.
     */
    --p360-status-draft: #6b6b6b;
    --p360-status-open: #0a6e94;
    --p360-status-paid: #00805a;
    --p360-status-reminder: #8a6d00;
    --p360-status-reminder2: #b35c00;
    --p360-status-uncollectible: #8a1f1f;
    --p360-status-void: #1f1f1f;
    /* variables.scss: $emma-color */
    --p360-emma: #ef971e;
    /*
     * Schrift auf der Emma-Flaeche. Das Vaadin-8-Weiss kam auf dem Orange nur auf 2,3:1 und war
     * als Fliesstext unbrauchbar; dieses Dunkelgrau (aus dem alten $style5-Ton) erreicht 6,7:1.
     * Wer am Orange dreht, misst hier nach -- Ziel ist mindestens 4,5:1.
     */
    --p360-emma-ink: #1e252b;
    /* color.scss: .success-color */
    --p360-success: #86bb3c;
    /* variables.scss: $p-gray-color / $panel-background */
    --p360-gray: #c4c4c4;
    --p360-panel: #fdfdfd;

    /* page.scss: $page-header-height / $page-footer-height, _immotours.scss: $logo_size */
    --p360-header-height: 80px;
    --p360-footer-height: 60px;
    /* Luft ueber und unter dem Produkt-Logo in den Kopfleisten. Bestimmt zusammen mit
       --p360-header-height, wie hoch das Logo steht: 80 - 2x20 = 40px, also die halbe Leiste.
       Hier drehen, wenn das Logo zu gross oder zu klein wirkt — die Regel .product-logo rechnet
       den Wert von der Hoehe ab, das Bild bleibt dadurch in jedem Fall innerhalb der Leiste. */
    --p360-logo-inset: 20px;
    /* page.scss: .page-content */
    --p360-content-max-width: 1300px;
    /* Obergrenze fuer Grid-Views: mehr Spalten als 1300px zulassen, ohne dass Zeilen auf breiten
       Monitoren ueber die gesamte Bildschirmbreite laufen und unlesbar werden. */
    --p360-content-max-width-wide: 1700px;
}

/*
 * Browser ohne Relative Color (vor Chrome 119, Safari 18, Firefox 128) verwerfen die Ableitungen
 * oben; die Toene fielen dann ganz weg. Dort bleiben die festen Immo-Tours-Werte, also gruene
 * Akzente auch bei anderen Marken.
 */
@supports not (color: hsl(from red h s l)) {
    html {
        --p360-primary-light: #70dbb8;
        --p360-primary-lighter: #adebd6;
        --p360-primary-tint: #d6f5eb;
    }
}

/* --- 2. Lumo-Mapping --- */

/*
 * Bewusst Lumo statt Aura: die App laeuft ueber @Theme("platform360") + theme.json/lumoImports,
 * und alle bisher migrierten Views (LoginView, BaseView-Chrome) sind auf --lumo-* geschrieben.
 * Aura (com.vaadin.flow.theme.aura.Aura) definiert keine --lumo-*-Properties, ein Umstieg waere
 * also ein Rewrite aller vorhandenen View-Styles — siehe TODO unten.
 *
 * TODO(design): Aura als Ziel-Theme pruefen, sobald das View-Chrome final ist. Dann entfaellt
 * dieser Block und die --p360-*-Tokens werden auf --aura-accent-color-* / --aura-* gemappt.
 */
html {
    --lumo-primary-color: var(--p360-primary);
    /* Lumo zeichnet daraus den Fokusring; der folgt der Knopffarbe, nicht der hellen Flaeche. */
    --lumo-primary-color-50pct: color-mix(in srgb, var(--p360-primary-action) 50%, transparent);
    --lumo-primary-color-10pct: color-mix(in srgb, var(--p360-primary) 10%, transparent);
    --lumo-primary-text-color: var(--p360-primary-text);
    --lumo-primary-contrast-color: #ffffff;

    --lumo-success-color: var(--p360-ok);
    --lumo-success-text-color: var(--p360-ok);
    --lumo-error-color: var(--p360-status-red);
    --lumo-error-text-color: var(--p360-status-red);
    --lumo-warning-color: var(--p360-emma);
    --lumo-warning-text-color: #a86408;

    /* variables.scss: $v-app-background-color */
    --lumo-base-color: #ffffff;

    /*
     * variables.scss: $v-font-family / $v-font-size. Open Sans kommt aus den @font-face-Regeln
     * darunter, die Systemschriften greifen nur, solange die Datei noch lädt.
     */
    --lumo-font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    --lumo-font-size-m: 15px;
    --lumo-font-size-s: 13px;
    --lumo-font-size-xs: 11px;

    /* variables.scss: $v-border-radius */
    --lumo-border-radius-s: 3px;
    --lumo-border-radius-m: 5px;
    --lumo-border-radius-l: 8px;
}

/*
 * Open Sans wie in Vaadin 8: Valo lieferte genau diese drei Schnitte als Webfont aus (vaadin-themes
 * 8.12.1, valo/fonts/open-sans, Apache License 2.0), die Dateien hier sind dieselben. Ohne eigenen
 * Fett-Schnitt zeichnet `bold` mit Halbfett, ohne Kursiv-Schnitt schrägt der Browser Normal an —
 * beides wie in Vaadin 8. Wer einen Schnitt ergänzt, ändert damit alle fetten bzw. kursiven Texte.
 *
 * Die drei Dateien haben leicht verschiedene Ober- und Unterlängen (Halbfett 618 statt 595 von 2048
 * Einheiten unter der Grundlinie). Ohne die Overrides stünde Halbfett in einer Zeile neben Normal 1px
 * höher, etwa der aktive Reiter neben den inaktiven. Alle drei rechnen deshalb mit den Werten von
 * Normal: 2146/2048 und 595/2048.
 */
@font-face {
    font-family: "Open Sans";
    src: url("./fonts/OpenSans-Light-webfont.woff") format("woff");
    ascent-override: 104.79%;
    descent-override: 29.05%;
    line-gap-override: 0%;
    font-weight: 300;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Open Sans";
    src: url("./fonts/OpenSans-Regular-webfont.woff") format("woff");
    ascent-override: 104.79%;
    descent-override: 29.05%;
    line-gap-override: 0%;
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Open Sans";
    src: url("./fonts/OpenSans-Semibold-webfont.woff") format("woff");
    ascent-override: 104.79%;
    descent-override: 29.05%;
    line-gap-override: 0%;
    font-weight: 600;
    font-style: normal;
    font-display: swap;
}

/* --- 2a. Abstands-Tokens --- */

/*
 * Benannte Abstände für Dialoge und Formulare. Neue Abstände nehmen einen dieser Tokens statt eines
 * px-Werts. Sie zeigen auf die Lumo-Stufen, ändern also nichts am Aussehen; die eigenen Namen gibt es,
 * damit ein Wechsel auf Aura (kennt keine --lumo-space-*) nur diesen Block betrifft.
 * px bleibt für Rahmen, Breakpoints und die gemessenen krpano-Maße.
 */
html {
    --p360-space-xs: var(--lumo-space-xs);
    --p360-space-s: var(--lumo-space-s);
    --p360-space-m: var(--lumo-space-m);
    --p360-space-l: var(--lumo-space-l);
    --p360-space-xl: var(--lumo-space-xl);

    /* Innenabstand der Dialogfläche, so groß wie Lumos eigener */
    --p360-dialog-padding: var(--p360-space-l);
    /* zwischen zwei Bereichen, etwa zwei DialogSections */
    --p360-section-gap: var(--p360-space-l);
    /* zwischen zwei Formularzeilen */
    --p360-form-row-gap: var(--p360-space-s);
    /* zwischen zwei Spalten eines Formulars, so breit wie Lumos eigener */
    --p360-form-column-gap: var(--p360-space-l);
    /* Symbol zu Text, etwa „Ersetzen“ */
    --p360-inline-gap: var(--p360-space-xs);
}

/* --- 2b. Feld-Design --- */

/*
 * Hier wird das Aussehen aller Eingabefelder zentral geändert: Text-, Passwort-, Zahl- und
 * Datumsfelder, Textbereiche und Auswahlfelder. Weiße Fläche, dünner grauer Rand, die Beschriftung
 * steht darüber (Lumo). Einzelne Seiten oder Dialoge setzen am Feld keinen eigenen Hintergrund und
 * keinen eigenen Rand, sonst sieht das Feld dort wieder anders aus.
 *
 * Beispiele: hellgraue Fläche überall  -> --p360-field-background: var(--lumo-contrast-5pct);
 *            kräftigerer Rand überall  -> --p360-field-border-width: 2px;
 *
 * Kontraste gegen Weiß: Rand 1,6:1, bewusst hell (Variante V2, Chris 02.11.2026) und damit unter
 * den 3:1, die WCAG 1.4.11 für Feldgrenzen nennt. Rand unter dem Zeiger 5,3:1, Fokus in der
 * Knopffarbe (Grün 3:1, Orange wie die Knöpfe) und doppelt so breit. Die gestrichelten
 * Upload-Flächen lesen den Rand mit und bekommen einen eigenen, kräftigeren Wert (Abschnitt
 * „Abschnitte ohne Kasten“). Wer hier dreht, misst nach.
 */
html {
    --p360-field-background: var(--lumo-base-color);
    --p360-field-border-width: 1px;
    --p360-field-border-color: #c3ccc8;
    --p360-field-border-color-hover: var(--lumo-contrast-70pct);
    --p360-field-border-color-focus: var(--p360-primary-action);
    --p360-field-focus-border-width: 2px;
}

/*
 * Die Lumo-Tokens sitzen am Feld selbst und nicht an `html`: `--vaadin-input-field-border-*` lesen
 * auch Kästchen und Radios, die hier nicht gemeint sind.
 *
 * Die graue Hover-Fläche von Lumo ist aus; den Hover zeigt der dunklere Rand. Ungültig, deaktiviert
 * und schreibgeschützt bekommen keine Randfarbe von hier, weil Lumo die Farbe in diesen Zuständen
 * am Feld umlegt (ungültig: Fehlerfarbe, schreibgeschützt: gestrichelt) und eine Regel von außen
 * das überstimmen würde. Die Fehlerfläche (Lumo, rötlich) bleibt.
 */
:is(vaadin-text-field, vaadin-password-field, vaadin-email-field, vaadin-number-field,
        vaadin-integer-field, vaadin-big-decimal-field, vaadin-text-area, vaadin-combo-box,
        vaadin-multi-select-combo-box, vaadin-select, vaadin-date-picker, vaadin-time-picker,
        vaadin-date-time-picker) {
    --vaadin-input-field-background: var(--p360-field-background);
    --vaadin-input-field-border-width: var(--p360-field-border-width);
    --vaadin-input-field-hover-highlight-opacity: 0;
}

:is(vaadin-text-field, vaadin-password-field, vaadin-email-field, vaadin-number-field,
        vaadin-integer-field, vaadin-big-decimal-field, vaadin-text-area, vaadin-combo-box,
        vaadin-multi-select-combo-box, vaadin-select, vaadin-date-picker, vaadin-time-picker,
        vaadin-date-time-picker):not([invalid], [disabled], [readonly]) {
    --vaadin-input-field-border-color: var(--p360-field-border-color);
}

:is(vaadin-text-field, vaadin-password-field, vaadin-email-field, vaadin-number-field,
        vaadin-integer-field, vaadin-big-decimal-field, vaadin-text-area, vaadin-combo-box,
        vaadin-multi-select-combo-box, vaadin-select, vaadin-date-picker, vaadin-time-picker,
        vaadin-date-time-picker):not([invalid], [disabled], [readonly], [focused]):hover {
    --vaadin-input-field-border-color: var(--p360-field-border-color-hover);
}

/*
 * Fokus, mit Maus wie mit Tastatur: der Rand nimmt die Knopffarbe an und wird doppelt so breit. Lumo zeigt bei
 * Tastaturfokus statt des Rands einen Ring außen; der bekommt dieselbe Farbe, sein Standard
 * (Markengrün zu 50 %) läge auf Weiß unter 1,5:1.
 */
:is(vaadin-text-field, vaadin-password-field, vaadin-email-field, vaadin-number-field,
        vaadin-integer-field, vaadin-big-decimal-field, vaadin-text-area, vaadin-combo-box,
        vaadin-multi-select-combo-box, vaadin-select, vaadin-date-picker, vaadin-time-picker)[focused]:not([readonly]) {
    --vaadin-input-field-border-width: var(--p360-field-focus-border-width);
}

:is(vaadin-text-field, vaadin-password-field, vaadin-email-field, vaadin-number-field,
        vaadin-integer-field, vaadin-big-decimal-field, vaadin-text-area, vaadin-combo-box,
        vaadin-multi-select-combo-box, vaadin-select, vaadin-date-picker,
        vaadin-time-picker)[focused]:not([invalid], [readonly]) {
    --vaadin-input-field-border-color: var(--p360-field-border-color-focus);
    --vaadin-focus-ring-color: var(--p360-field-border-color-focus);
}

/*
 * Die Beschriftung über dem Feld bleibt beim Fokus grau wie ohne Fokus; den Fokus zeigt der Rand. Lumo
 * färbt sie sonst in der Markenfarbe, und helle Marken sind für die kleine Schrift zu blass. Am `html`,
 * weil alle Lumo-Felder mit Beschriftung das Token lesen, auch Kästchen- und Radio-Gruppen. Der
 * Pflichtfeld-Punkt liest ein eigenes Token und bleibt in der Markenfarbe.
 */
html {
    --vaadin-input-field-focused-label-color: var(--lumo-secondary-text-color);
}

/*
 * Ausgefüllt vom Browser oder von 1Password: das ganze Feld bekommt eine Fläche in Autofill-Blau.
 * Browser-Autofill (`:autofill`) färbt Vaadin selbst über das Token unten bis an den Rand ein.
 * 1Password markiert das `input` mit `data-com-onepassword-filled` und färbt es selbst, mitunter mit
 * Farbverlauf. Das `input` ist so hoch wie das Feld und liegt über dem Rahmen (Lumo zeichnet ihn als
 * inneren Schatten des Felds): bleibt es farbig, verdeckt es den Rahmen oben und unten. Eine Farbe
 * per Inline-Stil mit `!important` schlägt jede Regel hier. Deshalb trägt das Feld das Autofill-Blau,
 * und der Hintergrund des `input` wird auf die Schrift beschnitten (`background-clip: text`, wie
 * Vaadin bei `:autofill`); das wirkt auch gegen Farbe oder Verlauf im Inline-Stil.
 */
html {
    --vaadin-input-field-autofill-background: #e8f0fe;
}

:is(vaadin-text-field, vaadin-password-field, vaadin-email-field, vaadin-number-field,
        vaadin-integer-field, vaadin-big-decimal-field, vaadin-combo-box):has(> input[data-com-onepassword-filled]) {
    --vaadin-input-field-background: var(--vaadin-input-field-autofill-background);
}

:is(vaadin-text-field, vaadin-password-field, vaadin-email-field, vaadin-number-field,
        vaadin-integer-field, vaadin-big-decimal-field, vaadin-combo-box) > input[data-com-onepassword-filled] {
    background-color: transparent !important;
    background-image: none !important;
    -webkit-background-clip: text !important;
    background-clip: text !important;
    box-shadow: none !important;
}

/* Der Textbereich färbt die Fläche unter dem Zeiger selbst grau statt über die Lumo-Hover-Fläche. */
vaadin-text-area:not([invalid], [disabled], [readonly])::part(input-field) {
    background-color: var(--p360-field-background);
}

/*
 * Text-Editor (QuillEditor): Quill bringt einen eigenen Rand mit (#ccc). Hier bekommt er den
 * Feldrand mit Zeiger- und Fokusfarbe; Fläche und Trennlinie der Werkzeugleiste setzt der Abschnitt
 * „Abschnitte ohne Kasten“.
 */
.p360-quill > .ql-toolbar.ql-snow,
.p360-quill > .p360-quill-host.ql-container.ql-snow {
    border-color: var(--p360-field-border-color);
}

.p360-quill:hover > .ql-toolbar.ql-snow,
.p360-quill:hover > .p360-quill-host.ql-container.ql-snow {
    border-color: var(--p360-field-border-color-hover);
}

.p360-quill:focus-within > .ql-toolbar.ql-snow,
.p360-quill:focus-within > .p360-quill-host.ql-container.ql-snow {
    border-color: var(--p360-field-border-color-focus);
}

/* Die Schreibfläche bekommt beim Fokus den doppelten Rand wie ein Feld, nach innen ohne Sprung. */
.p360-quill:focus-within > .p360-quill-host.ql-container.ql-snow {
    box-shadow: inset 0 0 0 calc(var(--p360-field-focus-border-width) - 1px) var(--p360-field-border-color-focus);
}

/*
 * Kästchen und Radios, nicht angehakt: weiße Fläche mit dem Feldrand statt Lumos grauer Fläche, unter
 * dem Zeiger der dunklere Rand wie bei den Feldern. Angehakt bleibt Lumo (Markenfläche, weißer Haken),
 * ebenso ungültig, deaktiviert und schreibgeschützt. Die Schieberegler (`vaadin-switch`) sind nicht
 * gemeint. Lumo zeichnet den Rand als inneren Schatten aus `--vaadin-input-field-border-*` und setzt
 * die Farbe beim Anhaken selbst auf transparent; eine Regel ohne `:not([checked])` überstimmte das.
 */
:is(vaadin-checkbox, vaadin-radio-button):not([checked], [indeterminate], [invalid], [disabled], [readonly]) {
    --vaadin-checkbox-background: var(--p360-field-background);
    --vaadin-checkbox-background-hover: var(--p360-field-background);
    --vaadin-radio-button-background: var(--p360-field-background);
    --vaadin-radio-button-background-hover: var(--p360-field-background);
    --vaadin-input-field-border-width: var(--p360-field-border-width);
    --vaadin-input-field-border-color: var(--p360-field-border-color);
}

:is(vaadin-checkbox, vaadin-radio-button):not([checked], [indeterminate], [invalid], [disabled], [readonly]):hover {
    --vaadin-input-field-border-color: var(--p360-field-border-color-hover);
}

/* --- 2c. Reiter --- */

/*
 * Aktiver Reiter überall (vaadin-tabs, vaadin-tabsheet): Schrift in der lesbaren Markenfarbe und
 * Halbfett, die Linie darunter in der Fläche (Lumo, --lumo-primary-color). Halbfett macht den Text
 * ein paar Pixel breiter; die Reiter rechts davon rücken beim Wechsel entsprechend.
 */
vaadin-tab[selected] {
    color: var(--p360-primary-text);
    font-weight: 600;
}

/*
 * Etagen-Reiter: die Etagen über der Standpunktleiste im Editor (Knöpfe, `.view-point-group`) und
 * die Reiter der Standpunkt-Auswahl in Sprungmarken-Ziel und Grundriss-Fenster
 * (`.view-point-selection-tab-sheet`). Alle Werte für beide stehen hier, wie in Vaadin 8: Schrift
 * dunkelgrau, der aktive fett mit dunkler Linie darunter, ohne Markenfarbe.
 */
html {
    --p360-floor-tab-color: #474747;
    --p360-floor-tab-selected-color: #474747;
    --p360-floor-tab-selected-font-weight: bold;
    --p360-floor-tab-selected-line: 1px solid #000;
}

/*
 * Gesperrte Etagen (im Grundriss-Fenster bestimmt die Etagen-Auswahl links den Reiter) in derselben
 * Schrift wie die übrigen; Lumos Grau für gesperrt läge auf Weiß unter 2:1. Gesperrt bleiben sie.
 */
.view-point-selection-tab-sheet vaadin-tab {
    color: var(--p360-floor-tab-color);
    font-weight: normal;
}

.view-point-selection-tab-sheet vaadin-tab[selected] {
    color: var(--p360-floor-tab-selected-color);
    font-weight: var(--p360-floor-tab-selected-font-weight);
}

/*
 * Linie unter dem aktiven Reiter über die ganze Breite statt Lumos kurzem Strich in Markenfarbe.
 * Gezeichnet im ::before, das Lumo für den Strich vorsieht: eine Unterkante am Reiter selbst machte
 * ihn höher, und `box-shadow` belegt Lumos Fokusring.
 */
.view-point-selection-tab-sheet vaadin-tab::after {
    display: none;
}

.view-point-selection-tab-sheet vaadin-tab[selected]::before {
    left: 0;
    width: 100%;
    height: auto;
    border-radius: 0;
    border-bottom: var(--p360-floor-tab-selected-line);
    background-color: transparent;
    transform: none;
}

/*
 * Die Reiterleiste selbst nimmt beim Klick neben die Reiter den Fokus (tabindex -1, per Tab nicht
 * erreichbar). Nach vorheriger Tastatureingabe zeichnete Chrome dann seinen blauen Fokusrahmen um die
 * ganze Leiste. Der Tastaturfokus liegt auf dem einzelnen Reiter und zeigt dort Lumos Fokusring.
 */
.view-point-selection-tab-sheet vaadin-tabs::part(tabs) {
    outline: none;
}

/* --- 2d. Markenfarbe unter dem Zeiger --- */

/*
 * Symbole und Links stehen in -strong bzw. -text und zeigen die Marke erst unter dem Zeiger
 * (Entscheidung Arne 02.11.). Die Symbolknöpfe brauchen den Selektor mit [theme~="icon"]: die Regel
 * für ihre Ruhefarbe in Abschnitt 4 ist spezifischer als `vaadin-button.primary-color:hover`. Die
 * weißen Links im dunklen Seitenfuß bleiben weiß.
 * Weitere Hover in der Akzentfarbe stehen bei ihren Bausteinen: `.primary-color-hover`, das ⓘ
 * (`.p360-info-button`) und die Knöpfe der Bearbeitungslisten (`.item-list-action`).
 */
vaadin-button.primary-color:hover,
vaadin-button[theme~="icon"].primary-color:hover,
a:any-link:hover:not(.page-footer a) {
    color: var(--p360-primary-accent);
}

/* --- 3. Chrome --- */

/*
 * Die Shell ist der AppLayout in `ui.MainLayout` — EIN Chrome fuer alle Views:
 * Top-Bar (navbar-Slot) auf dem Desktop, Drawer (SideNav) auf dem Telefon, beide aus derselben
 * @Menu-Quelle. Umschaltpunkt ist der Phone-Breakpoint 640px aus der Responsive-Konvention;
 * einen zweiten Breakpoint gibt es hier bewusst nicht.
 *
 * Der Drawer laeuft dauerhaft im Overlay-Modus des AppLayouts: die Komponente liest
 * `--vaadin-app-layout-drawer-overlay` bei jedem Resize aus dem Computed Style (Default: true erst
 * ab <=800px). So belegt der Drawer nie Layout-Breite, bringt seinen eigenen Backdrop/Fokus-Trap
 * mit und schliesst sich nach der Navigation selbst — statt dass wir das Verhalten nachbauen.
 */
vaadin-app-layout.p360-shell {
    --vaadin-app-layout-drawer-overlay: true;
}

vaadin-app-layout.p360-shell::part(navbar) {
    background: var(--p360-primary);
    border-bottom: none;
    min-height: var(--p360-header-height);
    padding-inline: var(--lumo-space-l);
}

/* Views mit `isFooterAndHeaderVisible() == false` (Editor, Kartierung) bekommen kein Chrome. */
vaadin-app-layout.p360-no-chrome::part(navbar) {
    display: none;
}

.p360-no-chrome .page-footer {
    display: none;
}

.p360-topbar {
    align-items: center;
    display: flex;
    gap: var(--lumo-space-m);
    height: var(--p360-header-height);
    width: 100%;
    /* page.scss: Menüpunkte 18px, Gewicht 400. Der Name oben rechts übernimmt beides. */
    --p360-header-font: var(--lumo-font-family);
    --p360-header-font-size: 18px;
    /* component.scss: `.product-logo { padding: 14px }`, das Logo steht damit 52px hoch. */
    --p360-logo-inset: 14px;
}

/* Logo links, Menue + Nutzerbereich rechts (wie die Vaadin-8-Kopfzeile). */
.p360-brand {
    display: flex;
    align-items: center;
    height: 100%;
    margin-inline-end: auto;
}

.p360-brand img,
.p360-brand svg {
    max-height: 100%;
}

/*
 * Platzverteilung in der Kopfzeile: der Nutzerbereich (Avatar, Profil, Abmelden) behaelt seine
 * Breite, die Navigation gibt nach. Ohne die beiden Regeln schrumpft der Flex-Container die
 * Nutzer-MenuBar zuerst: bei neun Menue-Eintraegen war der Avatar ab ~1360px Fensterbreite
 * ausgeblendet (`visibility: hidden` an den nicht passenden Buttons) — auf einem 1280er-Laptop war
 * damit weder Profil noch Abmelden erreichbar. `min-width: 0` erlaubt der Navigations-MenuBar,
 * unter ihre Inhaltsbreite zu schrumpfen; ihre eigene Ueberlauf-Schaltflaeche („···") nimmt die
 * Eintraege auf, die nicht mehr passen.
 */
.p360-topbar-menu {
    flex: 0 1 auto;
    min-width: 0;
}

.p360-user-menu {
    flex: 0 0 auto;
}

/*
 * Sichtbarkeits-Umschaltung: unter 640px traegt der Drawer die Navigation, darueber die Top-Bar.
 * Beide Renderer bleiben im DOM — nur einer ist sichtbar.
 */
@media (max-width: 639.98px) {
    .p360-topbar-menu,
    .p360-user-menu {
        display: none;
    }
}

@media (min-width: 640px) {
    .p360-drawer-toggle {
        display: none;
    }
}

/* Drawer-Inhalt: Navigation oben, Nutzer/Abmelden unten. */
.p360-drawer-content {
    display: flex;
    flex-direction: column;
    gap: var(--lumo-space-m);
    min-height: 100%;
    padding: var(--lumo-space-s);
}

.p360-drawer-footer {
    border-top: 1px solid rgba(255, 255, 255, 0.12);
    display: flex;
    flex-direction: column;
    gap: var(--lumo-space-xs);
    margin-top: auto;
    padding-top: var(--lumo-space-s);
}

.p360-drawer-user {
    color: rgba(255, 255, 255, 0.6);
    font-size: var(--lumo-font-size-xs);
    padding-inline: var(--lumo-space-s);
}

/* Wie oben rechts: einzeilig mit „…“, voller Name im Tooltip. */
.p360-drawer-user {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.p360-drawer-logout {
    color: rgba(255, 255, 255, 0.78);
    justify-content: flex-start;
}

/*
 * Content-Bereich: View-Halter + Footer, als Spalte ueber die volle Hoehe des Content-Slots.
 *
 * Bewusst `height: 100%` (definite Hoehe) statt `min-height` + mitscrollendem Footer: die
 * portierten Views sind Vaadin-Full-Height-Layouts (`setSizeFull()` + `expand(...)`, Grids mit
 * eigenem Scroll-Container) und brauchen eine aufloesbare Elternhoehe — mit einer
 * inhaltsgetriebenen Spalte kollabiert diese Kette und die Seite wird beliebig hoch (gemessen:
 * Projektliste 1054px statt 760px, Footer unter dem Falz). Der Footer steht damit wie in Vaadin 8
 * immer unten; ueberlange Inhalte scrollen im Halter.
 */
.p360-shell-content {
    display: flex;
    flex-direction: column;
    height: 100%;
}

.p360-view-holder {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
}

.p360-view-holder > * {
    flex: 1 1 auto;
    min-height: 0;
}

/*
 * page.scss: `.page-header { background: $primary-color }`. Die Hoehe setzt PageHeader per
 * setHeight(Theme.Styles.Constants.HEADER_HEIGHT) — hier nur Farbe und Innenabstand.
 * Die Klasse bleibt, weil PageHeader/PageMenu als Komponenten noch existieren; gerendert wird
 * seit der Chrome-Konsolidierung nur noch die Shell.
 */
.page-header {
    background: var(--p360-primary);
    padding-inline: var(--lumo-space-l);
    flex: 0 0 auto;
}

/*
 * component.scss: `.product-logo { padding: 14px }`. Das Logo steht immer in einer Leiste fester
 * Hoehe (Topbar und Editor-Kopfzeile: 80px) und darf dort weder ueberstehen noch verzerren:
 *
 *   - `box-sizing: border-box` zieht den Innenabstand von der Hoehe ab, statt ihn draufzuschlagen.
 *     Ohne das ergibt `height: 100%` plus 20px Polsterung 120px in einer 80px-Leiste — das Bild
 *     ragt oben und unten heraus.
 *   - `height: 100%` statt `max-height`, damit alle Produktlogos gleich hoch stehen: die
 *     hochgeladenen SVGs bringen sehr unterschiedliche Eigengroessen mit.
 *   - `width: auto` laesst die Breite aus dem Seitenverhaeltnis der Datei folgen; `object-fit`
 *     haelt es auch dann, wenn `max-width` bei einem sehr breiten Logo greift.
 *
 * Die Hoehe gehoert deshalb hierher und nicht als Inline-Style ans Element: ein Inline-Style
 * ueberlebt jede Aenderung an dieser Regel und baut den Ueberstand wieder ein.
 */
.product-logo {
    display: block;
    box-sizing: border-box;
    height: 100%;
    width: auto;
    max-width: 100%;
    object-fit: contain;
    padding-block: var(--p360-logo-inset);
    padding-inline: 0;
    color: #ffffff;
}

/*
 * page.scss: `.v-menubar .v-menubar-menuitem { background: none; border: none; color: white;
 * font-size: 18px; font-weight: 400; padding: 0 15px }`. Flow rendert die MenuBar als
 * <vaadin-menu-bar-button> im Light-DOM, die Buttons sind daher direkt adressierbar.
 *
 * Die Schrift muss am <vaadin-menu-bar-item> im Knopf stehen: Lumo setzt dort die eigene Größe
 * (15px), eine Angabe am Knopf erreicht den Text nicht. Wer die Größe nachmisst, liest sie deshalb
 * am Item ab, nicht am Knopf.
 */
.page-header vaadin-menu-bar-button,
.p360-topbar vaadin-menu-bar-button {
    background: transparent;
    border: none;
    box-shadow: none;
    color: #ffffff;
    font-size: 18px;
    font-weight: 400;
}

.p360-topbar-menu vaadin-menu-bar-button {
    padding-inline: 15px;
}

.p360-topbar vaadin-menu-bar-item {
    font-family: var(--p360-header-font);
    font-size: var(--p360-header-font-size);
    font-weight: 400;
}

/* Valo: Pfeil hinter Einträgen mit Untermenü, 0,7em groß, 0,35em Abstand zum Text. */
.p360-menu-arrow {
    height: 0.7em;
    margin-inline-start: 0.35em;
    width: 0.7em;
}

.page-header vaadin-menu-bar-button:hover,
.page-header vaadin-menu-bar-button[expanded],
.p360-topbar vaadin-menu-bar-button:hover,
.p360-topbar vaadin-menu-bar-button[expanded] {
    background: rgba(255, 255, 255, 0.15);
}

/* Hamburger nur auf dem Telefon — dort ebenfalls weiss auf Markengruen. */
.p360-drawer-toggle {
    color: #ffffff;
}

/* Nutzerbereich rechts: Avatar + Name, der Name in Schrift und Größe der Menüpunkte. */
.p360-user-trigger {
    align-items: center;
    display: flex;
    gap: var(--lumo-space-m);
    color: #ffffff;
    font-family: var(--p360-header-font);
    font-size: var(--p360-header-font-size);
    white-space: nowrap;
}

/*
 * Lange Namen enden einzeilig mit „…“, der volle steht im Tooltip (MainLayout#fillUserMenu). Die
 * Nutzer-MenuBar schrumpft nicht (siehe .p360-user-menu), ein ungekürzter Name verdrängte sonst die
 * Navigation in ihr Überlauf-Menü. Die Obergrenze ist deshalb der Platz, den das längste Kundenmenü
 * (vier Punkte in 18px) neben Logo und Profilbild lässt: rund 1040px sind belegt, bei 1280px
 * bleiben 240px für den Namen. Unter 1140px passt kein lesbarer Name mehr daneben; dort steht wie in
 * Vaadin 8 nur das Profilbild, der Name bleibt im Tooltip.
 */
.p360-user-name {
    max-width: clamp(6rem, calc(100vw - 1040px), 18rem);
    overflow: hidden;
    text-overflow: ellipsis;
}

@media (max-width: 1139.98px) {
    .p360-user-name {
        display: none;
    }
}

/*
 * page.scss: `.user-photo { border-radius: 50%; border: 3px solid white }`. Der Kreis misst mit dem
 * 3px-Rahmen 56px wie in Vaadin 8.
 */
.page-header vaadin-avatar,
.p360-topbar vaadin-avatar,
.user-photo {
    --vaadin-avatar-size: 50px;
    border: 3px solid #ffffff;
    border-radius: 50%;
    color: var(--p360-primary);
}

/*
 * Der Avatar sitzt im Label-Slot eines MenuBar-Knopfes, und die Kette darueber schneidet ihn an
 * zwei Stellen ab: `[part="label"]` traegt `overflow: hidden` (Lumo behandelt den Slot als
 * Textzeile), und der `[part="container"]` der MenuBar ist nur so hoch wie ihr Knopf
 * (Lumo-Default 36px). Der Kreis wird dadurch oben und unten platt.
 *
 * Die Knopfhoehe traegt die Loesung: auf Kreisgroesse waechst der Container mit, der Kreis passt
 * hinein und bleibt vollstaendig anklickbar. Nur der Label-Slot muss zusaetzlich freigegeben
 * werden — er bleibt auch dann ein paar Pixel niedriger als der Knopf.
 */
.p360-user-menu vaadin-menu-bar-button {
    --vaadin-button-height: 56px;
}

.p360-user-menu vaadin-menu-bar-button::part(label) {
    overflow: visible;
}

/*
 * Weiße Fläche unter dem Bild: Ring und Bild enden an derselben Rundung, beide mit Kantenglättung.
 * Auf einer durchscheinenden Fläche schimmert dort ein 1px-Kreis in Markenfarbe zwischen Bild und
 * Ring durch. Die Initialen (nur, wenn das Bild nicht lädt) stehen deshalb in der dunklen
 * Markenfarbe auf Weiß.
 */
.p360-topbar vaadin-avatar {
    background: #ffffff;
    color: var(--p360-primary-text);
}

/*
 * Ohne eigenes Foto zeigt der Avatar die Silhouette aus Vaadin 8 (MainLayout#userAvatar). Der Kopf
 * stößt in der Datei oben an, der Körper unten: 2px Luft über dem Kopf, der Körper bleibt am
 * unteren Rand. Die Seiten rücken um je 1px ein, damit das Bild quadratisch bleibt; dort ist die
 * Datei ohnehin weiß. Breite plus Innenabstand ergeben die 50px des Kreises.
 */
.p360-topbar vaadin-avatar.p360-avatar-silhouette {
    width: 48px;
    height: 48px;
    padding: 2px 1px 0;
}

/* page.scss: `.page-footer { background: $secondary-color; font-size: 11px }` + weisser Text */
.page-footer {
    background: var(--p360-footer);
    color: #ffffff;
    font-size: var(--lumo-font-size-xs);
    min-height: var(--p360-footer-height);
    padding-block: var(--lumo-space-xs);
    flex: 0 0 auto;
}

/* Auf schmalen Viewports muss die Zeile umbrechen, sonst laeuft sie aus dem Footer heraus. */
.page-footer > vaadin-horizontal-layout {
    flex-wrap: wrap;
    row-gap: var(--lumo-space-xs);
}

.page-footer a {
    color: #ffffff;
    font-weight: bold;
    text-decoration: none;
}

/*
 * Seiten-Container (page.scss: `.page-content { max-width: 1300px }`).
 * Der Innenabstand steckt jetzt hier statt in `setMargin(true)` — so bleibt der Abstand auf dem
 * Telefon klein und auf dem Desktop wie gewohnt (Responsive-Konvention §2).
 */
.page-content-wrapper {
    padding: var(--lumo-space-m);
    width: 100%;
}

@media (min-width: 640px) {
    .page-content-wrapper {
        padding: var(--lumo-space-l);
    }
}

.page-content {
    margin-inline: auto;
    max-width: var(--p360-content-max-width);
}

/* Breite Stufe fuer Grid-Views — nur die Obergrenze weicht ab, Zentrierung und Innenabstand bleiben. */
.page-content--wide {
    max-width: var(--p360-content-max-width-wide);
}

/*
 * Ausnahmestufe ohne Obergrenze (BaseView.ContentWidth.FULL). Nur fuer Grids, deren Spaltenzahl
 * auch 1700px sprengt — sonst wandert das Auge beim Zeilenwechsel zu weit (siehe
 * 0-docs/design/full-width-views.md). Der Innenabstand des Wrappers bleibt stehen.
 */
.page-content--full {
    max-width: none;
}

/*
 * AppLayout-Drawer. Das dunkle Menue kam in Vaadin 8 aus _immotours.scss ($menu_color) und lag
 * links; in Flow ist das die mobile Navigation im Drawer.
 */
vaadin-app-layout::part(drawer) {
    background: var(--p360-menu);
    border-inline-end: none;
    padding-block: var(--lumo-space-m);
}

vaadin-side-nav-item::part(content) {
    color: rgba(255, 255, 255, 0.78);
    border-radius: var(--lumo-border-radius-m);
}

/*
 * Der Hover braucht BEIDE Teile: die Beschriftung liegt als geslotteter Textknoten im
 * `link`-Teil, und dessen eigene Lumo-Regel setzt beim Ueberfahren die dunkle Textfarbe —
 * auf dem dunklen Drawer war der Eintrag unter dem Zeiger dadurch praktisch unlesbar.
 */
vaadin-side-nav-item:hover::part(content),
vaadin-side-nav-item:hover::part(link) {
    background: rgba(255, 255, 255, 0.08);
    color: #ffffff;
}

vaadin-side-nav-item[current]::part(content) {
    background: var(--p360-primary);
    color: #ffffff;
    font-weight: 600;
}

vaadin-side-nav::part(label) {
    color: rgba(255, 255, 255, 0.6);
    font-size: var(--lumo-font-size-xs);
    letter-spacing: 0.03em;
    text-transform: uppercase;
}

/* --- 4. Legacy-Klassen (Theme.Styles) --- */

/*
 * component.scss: Groessen-/Zuschnitt-Helfer aus Theme.Styles.Component.
 *
 * `.max-width-250px` deckelt die Medien-Vorschau (MediaComponent haengt sie an die MediaPreview);
 * ohne die Regel wuchs die Std.-Icon-Vorschau im Inhaltstyp-Editor auf ueber 500px und schob den
 * Rest des Formulars aus dem Dialog. `.object-fit-contain` haelt das Vorschaubild im
 * Seitenverhaeltnis — der Browser-Default `fill` zerrte SVG-Icons auf die volle Kachel.
 *
 * Bewusst NICHT portiert: `.object-fit-none` und `.object-fit-cover` — die wuerden die
 * Standpunktleiste umstellen, die Welle K gerade erst eingestellt hat.
 */
.max-width-250px {
    max-width: 250px;
}

.object-fit-contain {
    object-fit: contain;
}

/* component.scss: .round-button / .round-button-small */
.round-button,
.round-button-small {
    border-radius: 50%;
    border: none;
    box-shadow: none;
}

.round-button {
    width: 40px;
    height: 40px;
    min-width: 40px;
    padding: 9px;
}

.round-button-small {
    width: 30px;
    height: 30px;
    min-width: 30px;
    padding: 6px;
}

/* component.scss: .flat-button / .flat-button-gray */
.flat-button {
    --vaadin-button-background: var(--p360-primary-action);
    --vaadin-button-text-color: #ffffff;
    background: var(--p360-primary-action);
    box-shadow: none;
    border: none;
    color: #ffffff;
}

/*
 * Der runde Plus-Knopf (Projekte, Benutzer) ist Fläche wie die Kopfleiste. Andere runde Knöpfe
 * derselben Klasse (Speichern in Dialogen) sind Hauptknöpfe und bleiben in der Knopffarbe.
 */
.flat-button:is(.round-button, .round-button-small):has(use[*|href="#plus"]) {
    --vaadin-button-background: var(--p360-primary);
    background: var(--p360-primary);
}

.flat-button-gray {
    --vaadin-button-background: #c2c2c2;
    --vaadin-button-text-color: #ffffff;
    background: #c2c2c2;
    box-shadow: none;
    border: none;
    color: #ffffff;
}

/*
 * Gesperrte Werkzeugknöpfe (ToolWindow#setToolEnabled) grau. .flat-button setzt den Hintergrund direkt
 * und färbt sonst auch den gesperrten Knopf grün: „Weiter“ im Projekt-Assistenten sähe während der
 * Konvertierung klickbar aus.
 */
.flat-button.tool-button-disabled {
    --vaadin-button-background: var(--p360-gray);
    background: var(--p360-gray);
}

/*
 * Hauptknoepfe in der Knopffarbe statt der hellen Flaeche: --lumo-primary-color bleibt sonst die
 * Flaeche, damit angehakte Kaestchen und die Reiter-Linie hell bleiben. Statt Hintergrund und
 * Farbe direkt zu setzen, werden die Lumo-Tokens am Knopf umbelegt: dadurch treffen Hover,
 * Fokusring und Aktiv-Zustand der LUMO_PRIMARY-Variante automatisch mit.
 */
vaadin-button[theme~="primary"],
.p360-button-dark {
    --lumo-primary-color: var(--p360-primary-action);
    --lumo-primary-text-color: var(--p360-primary-action);
    --lumo-primary-contrast-color: #ffffff;
}

.icon-only-button {
    background: none;
    border: none;
    box-shadow: none;
    width: 32px;
    height: 32px;
    /* Der Lumo-Button bringt die Mindestbreite eines Textknopfs mit (rund 70px); in einer
       Symbolspalte sprengt sie die Zelle und der Zellinhalt wird mit Ellipse abgeschnitten. */
    min-width: 0;
    padding: 5px;
}

/* component.scss: .badge / .no-table-entries-label */
.badge {
    background: var(--p360-primary);
    border-radius: 10px;
    color: #ffffff;
    text-align: center;
    /* Als Inline-Span läge die Pill eng um die Ziffer; mit Mindestbreite und Innenabstand steht
       die Zahl mit Luft darin, auch zweistellig. */
    display: inline-block;
    box-sizing: border-box;
    min-width: 2.5em;
    padding: var(--lumo-space-xs) var(--lumo-space-s);
    line-height: 1;
}

.no-table-entries-label {
    background: var(--p360-primary-lighter);
    border-radius: var(--lumo-border-radius-m);
    min-height: 35px;
    padding: var(--lumo-space-xs) var(--lumo-space-s);
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
}

/* Daten nicht ladbar: neutral-gelb statt des Grüns der Leer-Meldung. */
.no-table-entries-label--warning {
    background: color-mix(in srgb, var(--p360-emma) 22%, #ffffff);
    color: var(--p360-emma-ink);
}

/*
 * color.scss. Eine Klasse fuer Text, Ueberschriften und Symbole; die Elementart entscheidet den Ton:
 * Symbole (Symbolknoepfe, SVGs) und grosse Zahlen (p-font-40) duerfen heller sein als kleiner Text.
 */
.primary-color {
    color: var(--p360-primary-text);
}

vaadin-button[theme~="icon"].primary-color,
.svg-use.primary-color,
vaadin-icon.primary-color,
.p-font-40.primary-color {
    color: var(--p360-primary-strong);
}

.gray-color {
    color: var(--p360-gray);
}

.emma-color {
    color: var(--p360-emma);
}

.white-color,
.p-white-font {
    color: #ffffff;
}

.success-color {
    color: var(--p360-success);
}

/* general.scss: Typografie-Helfer */
.p-bolder-font {
    font-weight: bolder;
}

.p-font-800 {
    font-weight: 800;
}

.p-font-center {
    text-align: center;
}

.p-font-wrap {
    word-break: break-word;
    white-space: normal;
}

.p-small-font {
    font-size: var(--lumo-font-size-xs);
}

.p-font-40 {
    font-size: 40px;
}

.p-font-20 {
    font-size: 20px;
}

.p-hidden {
    display: none;
}

.pointer-cursor {
    cursor: pointer;
}

/*
 * Zeiger über allem, was auf Klick reagiert. Lumo zeigt über Knöpfen, Menüpunkten, Reitern,
 * Kästchen und Schaltern von Haus aus den Pfeil; die Variable stellt sie alle auf einmal um.
 * Eigene klickbare Flächen tragen `.pointer-cursor` oder `.clickable` (Constants.Styles.Clickable,
 * etwa Kachelköpfe mit Klick-Listener und die Standpunkt-Kacheln der Zielauswahl). Tabellen mit
 * anklickbaren Zeilen markieren sich mit `.grid-clickable-rows` (AppGrid von selbst, ein einfaches
 * Grid am Klick-Listener).
 */
html {
    --lumo-clickable-cursor: pointer;
}

.clickable {
    cursor: pointer;
}

vaadin-grid.grid-clickable-rows::part(body-cell),
.tree-menu-panel-tree vaadin-grid::part(body-cell) {
    cursor: pointer;
}

.pointer-events-none {
    pointer-events: none;
}

/* component.scss: .simple-border / .border-bottom / Outline-Helfer */
.simple-border {
    border: 1px solid #c5c5c5;
}

/*
 * Medien-Galerie: Der 1px-Rahmen der Kachel (.simple-border) zählt zur Rasterspur, statt
 * zusätzlich aufzuschlagen. Spuren und Kachelhöhe: Abschnitt „Mobiltelefon“ am Ende.
 */
.media-gallery > * {
    box-sizing: border-box;
}

/*
 * PDF-Plakette auf dem Vorschaubild, unten links. Das Bild füllt die Kachel ganz; als normales
 * Kind stünde die Plakette darunter, außerhalb der Kachel und über der nächsten Reihe.
 */
.media-thumbnail-pdf-badge {
    position: absolute;
    left: 6px;
    bottom: 6px;
    pointer-events: none;
}

.border-bottom {
    border-bottom: 2px solid var(--p360-gray);
}

.simple-outline {
    outline: 1px solid var(--p360-primary);
}

.outline-hover:hover {
    outline: 1px solid var(--p360-primary);
}

.primary-color-hover:hover {
    color: var(--p360-primary-accent);
}

.round-image {
    border-radius: 50%;
}

.disabled-state {
    opacity: 0.4;
}

/*
 * uploader.scss: .p-uploader / .p-uploader:hover / .upload-icon / .upload-label.
 *
 * Der gestrichelte Rahmen ist das einzige Signal, dass die Flaeche anklickbar ist und Dateien
 * annimmt — ohne ihn steht nur ein Symbol im Weiss und niemand erkennt eine Ablageflaeche. Seine
 * Farbe ist die des Feldrands (2b. Feld-Design), damit er wie dort mindestens 3:1 erreicht.
 * Gilt fuer alle Upload-Flaechen: Panoramen, Medien-Kacheln, Schriften, Kontoauszüge sehen gleich aus.
 */
.p-uploader {
    position: relative;
    text-align: center;
    border: 2px dashed var(--p360-field-border-color);
    border-radius: 10px;
    transition: background 0.3s ease-in-out;
}

.p-uploader:hover {
    background: var(--p360-primary-tint);
}

/*
 * In Vaadin 8 war das Symbol ein Label, das seine Groesse vom Layout bekam, und `.upload-icon`
 * deckelte es bei 70px. Der Flow-Port ist ein Span mit der Default-Kantenlaenge 1.5em aus
 * `.svg-use` — ohne Breite hier bleibt das Symbol briefmarkengross.
 */
.p-uploader .svg-use {
    width: 70px;
    max-width: 100%;
}

.p-uploader .upload-icon {
    max-width: 70px;
    max-height: 70px;
}

/*
 * Symbol in der Flächenfarbe der Marke, Beschriftung fast schwarz; unter dem Zeiger nur die hellste
 * Tönung als Grund, auf der beides lesbar bleibt (Entscheidung Chris 03.11.).
 */
.p-uploader .upload-icon {
    color: var(--p360-primary);
}

.p-uploader .upload-label {
    color: var(--p360-primary-text);
}

.p-uploader .upload-label {
    font-size: 1.3em;
}

/*
 * Icon-Kachel der Hotspot-Dialoge (HotSpotIconSelector, 86 px). Mit dem Standard-Innenabstand des
 * Kachel-Inhalts (16 px rundum) ist der Block höher als die Fläche: er steht unten über, wird dort
 * abgeschnitten, und das Symbol klebt an der unteren Linie. Mit dem knappen Abstand passt er hinein
 * und sitzt mittig.
 */
.media-icon-tile .media-tile-content {
    padding: var(--p360-space-xs);
}

/*
 * Jede Upload-Flaeche der Anwendung (DropZoneLayout#attachTo / #asDropTarget) — die eine Stelle
 * fuer die Optik, das Java-Gegenstueck steht in DropZoneLayout.
 *
 * `vaadin-upload` stellt von sich aus den Hinzufuegen-Knopf NEBEN den Inhalt und legt sein eigenes
 * Symbol davor; der Inhalt steht dadurch links aussen statt mittig. Die Regeln raeumen beides und
 * geben der Ablageflaeche die volle Breite und Hoehe:
 *
 *  - `primary-buttons` ist die Zeile mit Knopf und Drop-Label, `drop-label` deren Textteil. Beide
 *    wachsen auf die freie Hoehe, damit `align-items/justify-content: center` etwas zu zentrieren
 *    haben — sonst zentriert sich der Inhalt in seiner eigenen Zeilenhoehe, also nirgends.
 *  - `position: relative` auf `primary-buttons` ist der Bezug fuer den Knopf-Overlay unten. Er
 *    sitzt bewusst NICHT am Wirt: die Dateiliste haengt unter `primary-buttons` und muss klickbar
 *    bleiben (Abbrechen/Entfernen waehrend des Uploads).
 */
.upload-dropzone {
    display: flex;
    flex-direction: column;
    box-sizing: border-box;
    min-width: 0;
    /*
     * Das Innenabstands-Default von `vaadin-upload` laesst rundherum einen Streifen stehen, auf dem
     * weder ein Klick noch ein Drop ankommt — sichtbar ist dort trotzdem schon die Flaeche. Den
     * Abstand zur Linie geben `drop-label` und die Dateiliste selbst.
     */
    padding: 0;
    /* Sicherung: was trotz Umbruch und Kürzung nicht passt, endet an der gestrichelten Linie. */
    overflow: hidden;
}

/* Leerer Platzhalter statt Knopf (asDropTarget) — der Overlay unten ist davon ausgenommen. */
.upload-dropzone > [slot="add-button"]:not(.upload-button-overlay),
.upload-dropzone > [slot="drop-label-icon"] {
    display: none;
}

/*
 * Schrumpft nicht unter seinen Inhalt: Kommt die Dateiliste dazu, nimmt sie sich Platz unterhalb,
 * statt sich über Wolke und Texte zu schieben.
 */
.upload-dropzone::part(primary-buttons) {
    position: relative;
    flex: 1 0 auto;
    padding: 0;
    margin: 0;
}

.upload-dropzone::part(drop-label) {
    display: flex;
    flex: 1 1 auto;
    min-width: 0;
    align-items: center;
    justify-content: center;
    padding: 0;
    margin: 0;
}

/*
 * Abstand der Texte zur Linie, nur an Flächen mit Klick-Knopf (attachTo). Die Medien-Kachel
 * (asDropTarget) behält ihr Symbol in voller Größe.
 */
.upload-dropzone:has(> .upload-button-overlay)::part(drop-label) {
    padding: var(--p360-space-m);
}

/* Wolke, „Hochladen“, Hinweis und Formatliste: mit Abstand untereinander, lange Texte brechen um. */
.upload-dropzone .uploader-content > vaadin-vertical-layout {
    max-width: 100%;
    gap: var(--p360-space-xs);
    text-align: center;
    overflow-wrap: anywhere;
}

/*
 * Dateiliste mit Fortschritt je Datei (E14): unter der Ablage, innerhalb der Linie; mehr als gut zwei
 * Einträge scrollen in sich, statt den Dialog wachsen zu lassen. Lange Dateinamen bleiben einzeilig
 * und enden mit „…“.
 */
.upload-dropzone > vaadin-upload-file-list {
    flex: 0 1 auto;
    min-height: 0;
    max-height: 12rem;
    overflow-y: auto;
    box-sizing: border-box;
    padding-inline: var(--p360-space-s);
    text-align: start;
}

.upload-dropzone > vaadin-upload-file-list:has(vaadin-upload-file) {
    padding-block-end: var(--p360-space-s);
}

.upload-dropzone vaadin-upload-file::part(meta) {
    min-width: 0;
}

.upload-dropzone vaadin-upload-file::part(name) {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/*
 * Größen der Fläche (DropZoneLayout.Size). Jede ist eine Mindesthöhe, die Fläche wächst mit der
 * Dateiliste. Eine feste Höhe am Aufrufer (setHeight) gehört nicht dazu.
 */
.upload-dropzone--tile {
    min-height: 10rem;
}

.upload-dropzone--dialog {
    min-height: 12rem;
}

.upload-dropzone--fill {
    flex: 1 1 auto;
    min-height: 10rem;
}

/*
 * Der Hinzufuegen-Knopf, unsichtbar ueber der ganzen Flaeche (Vaadin-8-Vorlage: uploader.scss,
 * `.upload-button-overlay .v-button { width/height: 100%; opacity: 0 }`). Er bleibt ein echter
 * Knopf, weil `vaadin-upload` den Dateidialog an genau dieses Element haengt — siehe
 * DropZoneLayout. `opacity: 0` statt `visibility/display`, sonst waere er nicht mehr anklickbar.
 */
.upload-button-overlay {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    padding: 0;
    margin: 0;
    border: 0;
    background: transparent;
    opacity: 0;
    cursor: pointer;
}

/* Der Knopf selbst ist unsichtbar; ohne diese Regel gaebe es beim Tabben keinerlei Rueckmeldung. */
.upload-dropzone:has(> .upload-button-overlay:focus-visible) {
    outline: 2px solid var(--p360-primary-action);
    outline-offset: 2px;
}

/*
 * Grundriss-Fenster ohne Etage. Ein leeres schreibgeschütztes Feld blendet Vaadin samt Platzhalter
 * aus; hier ist der Platzhalter „Keine Etage angelegt“ die Aussage und bleibt sichtbar.
 */
.floor-plan-no-floor-chooser {
    width: 15rem;
    max-width: 100%;
}

.floor-plan-no-floor-chooser > input {
    opacity: 1;
}

/* Der Emma-Hinweis steht mittig in der sonst leeren Fläche. */
.floor-plan-no-floor-hint {
    display: flex;
    align-items: center;
    justify-content: center;
    align-self: stretch;
    padding: var(--p360-space-l);
}

/*
 * MediaThumbnail: das Vorschaubild konnte nicht geladen werden. Ohne eigenen Zustand steht dort
 * ein leerer grauer Kasten, der wie eine noch nicht befuellte Kachel aussieht — die Ursachen
 * (Datei fehlt vs. nichts ausgewaehlt) sind dann nicht auseinanderzuhalten.
 */
.media-thumbnail-missing {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    border: 2px solid var(--p360-emma);
    border-radius: 10px;
    box-sizing: border-box;
}

.media-thumbnail-missing > img {
    display: none;
}

/* Der Hinweis gehoert nur in den Fehlerzustand — sonst haengt er unter jedem Vorschaubild. */
.media-thumbnail-missing-hint {
    display: none;
}

.media-thumbnail-missing > .media-thumbnail-missing-hint {
    display: block;
    color: var(--p360-emma);
    font-size: var(--lumo-font-size-s);
    text-align: center;
}

/*
 * Design -> Logo (ProjectLogoChoice): Kachel links, Erklärung daneben. Die Vorschau des
 * Account-Logos hat die Maße der Upload-Kachel, damit die Seite beim Wechsel der Auswahl nicht
 * springt — aber keinen gestrichelten Rahmen, sie nimmt keine Dateien an.
 */
.logo-choice-preview {
    width: 250px;
    max-width: 100%;
    height: 170px;
    box-sizing: border-box;
    overflow: hidden;
    border-radius: 10px;
    background: #efefef;
}

/* Teilt sich die Zeile mit der Kachel; ist weniger Platz als die Basis, bricht sie darunter um. */
.logo-choice-side {
    flex: 1 1 14em;
    min-width: 0;
}

.logo-choice-note {
    color: var(--lumo-secondary-text-color);
    font-size: var(--lumo-font-size-s);
    line-height: var(--lumo-line-height-s);
}

.logo-choice-settings-link vaadin-icon {
    width: 1em;
    height: 1em;
    margin-inline-start: var(--lumo-space-xs);
    vertical-align: -0.125em;
}

/*
 * Ladeanzeige (Klasse `spinner`, Theme.Styles.Component.SPINNER): ein leeres Element, das sich als
 * Ring dreht. Standardgröße 24px; wer per setWidth/setHeight eine eigene setzt (WaitingWindow: 80px),
 * gewinnt über den Inline-Style. Die Aufrufer hängen den Ring in Flex-Layouts, `align-self`
 * zentriert ihn dort.
 */
.spinner {
    flex: none;
    align-self: center;
    box-sizing: border-box;
    width: 24px;
    height: 24px;
    border: 3px solid var(--lumo-contrast-10pct);
    border-top-color: var(--p360-primary);
    border-radius: 50%;
    animation: p360-spin 0.9s linear infinite;
}

@keyframes p360-spin {
    to {
        transform: rotate(360deg);
    }
}

/*
 * Upload-Kachel im Projekt-Assistenten (ViewPointThumbnail). Breite und Höhe gibt das Raster des
 * Assistenten vor; Bild, Titelleiste, Kreuz und Loader liegen übereinander in der Kachel.
 */
.view-point-thumbnail {
    --view-point-thumbnail-title-height: 24px;
    position: relative;
    overflow: hidden;
    background: var(--lumo-contrast-5pct);
}

.view-point-thumbnail-image {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Die helle Leiste hält den Titel auch auf dunklen Fotos (Flur, Treppenhaus) lesbar. */
.view-point-thumbnail-title {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: var(--view-point-thumbnail-title-height);
    line-height: var(--view-point-thumbnail-title-height);
    box-sizing: border-box;
    padding-inline: var(--lumo-space-s);
    background: rgba(255, 255, 255, 0.85);
    font-size: var(--lumo-font-size-s);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/*
 * Während der Verarbeitung stehen Ring und Hinweis als Gruppe mittig in der Kachel. Der Hinweis ist
 * kleiner gesetzt als ein Titel: „Datei wird verarbeitet …“ passt so ab rund 140px Kachelbreite in
 * eine Zeile, schmaler bricht er in zwei gleich lange Zeilen um.
 */
.view-point-thumbnail--loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--lumo-space-xs);
}

.view-point-thumbnail--loading .view-point-thumbnail-title {
    position: static;
    height: auto;
    line-height: var(--lumo-line-height-xs);
    background: none;
    color: var(--lumo-secondary-text-color);
    font-size: var(--lumo-font-size-xs);
    text-align: center;
    white-space: normal;
    text-wrap: balance;
}

/* Weiße Scheibe mit leichtem Schatten: steht auf hellen wie auf dunklen Fotos. */
.view-point-thumbnail-delete {
    position: absolute;
    top: 6px;
    right: 6px;
    width: 26px;
    height: 26px;
    min-width: 0;
    margin: 0;
    padding: 6px;
    border-radius: 50%;
    background: #ffffff;
    color: var(--p360-primary-strong);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
    cursor: pointer;
}

.view-point-thumbnail-delete:hover {
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.45);
}

/*
 * Lumo zeichnet den Tastatur-Fokusring ebenfalls als box-shadow am Knopf; der Schatten oben würde
 * ihn verdrängen. Deshalb stehen hier Ring und Schatten zusammen.
 */
.view-point-thumbnail-delete[focus-ring] {
    box-shadow: 0 0 0 1px var(--lumo-base-color), 0 0 0 3px var(--lumo-primary-color-50pct),
        0 1px 4px rgba(0, 0, 0, 0.3);
}

/* Ohne gespeicherten Standpunkt gibt es nichts zu löschen (RemoveViewPointDialog). */
.view-point-thumbnail--loading .view-point-thumbnail-delete {
    display: none;
}

.view-point-thumbnail-spinner {
    display: none;
}

/* Ring aus `.spinner`, über dem Hinweis. */
.view-point-thumbnail--loading .view-point-thumbnail-spinner {
    display: block;
    width: 28px;
    height: 28px;
}

/* --- SVG-Sprite-Icons (SvgUse / SvgButton) --- */

/*
 * Das Sprite selbst kommt aus BootstrapCustomizer#injectSvgSymbols; SvgUse rendert dazu
 * <svg class="icon"><use href="#symbol"></svg>. Vaadin 8 gab die Groesse in
 * themeBase/_immotours.scss vor (`.icon { width: 100%; height: 100% }`, das SVG fuellte den
 * Label-Container). Ohne diese Regel greift die CSS-Ersatzgroesse fuer <svg> ohne width/height:
 * 300x150 px — die Icons erschienen als riesige Flaechen quer ueber dem Layout.
 */
.icon {
    width: 100%;
    height: 100%;
}

/*
 * Container-Groesse. In Vaadin 8 war das ein Label, das seine Groesse vom Layout bekam; der
 * Flow-Port ist ein Span, und ein Inline-Element loest `height: 100%` nicht auf. Deshalb ein
 * Inline-Block mit quadratischer Default-Kantenlaenge. Aufrufer, die per setWidth()/setHeight()
 * eine Groesse setzen, gewinnen ueber den Inline-Style; `aspect-ratio` ergaenzt dann die jeweils
 * nicht gesetzte Kante, statt sie auf den Default zurueckfallen zu lassen.
 */
.svg-use {
    display: inline-block;
    line-height: 0;
    width: 1.5em;
    height: auto;
    aspect-ratio: 1;
}

/*
 * Nur-Icon-Knoepfe (SvgButton ohne Text). Flow legt deren Icon in den Label-Slot statt in den
 * Prefix-Slot (Button#updateIconSlot: `hasIconOnly()` entfernt das slot-Attribut) und setzt
 * `theme="icon"`. Lumo behandelt `[part="label"]` als Textzeile: Innenabstand, `overflow: hidden`,
 * `text-overflow: ellipsis`, Ausrichtung auf der Schriftlinie. Das `.svg-use` sitzt dadurch um die
 * Unterlaenge der Zeile zu hoch und wird beschnitten, sobald seine 1.5em breiter sind als die
 * Inhaltsbox des Knopfes (`.round-button-small`: 30px - 2*6px = 18px). Der Label-Slot wird deshalb
 * zur zentrierenden Flex-Box ohne Textsemantik.
 */
vaadin-button[theme~="icon"]::part(label) {
    display: flex;
    align-items: center;
    justify-content: center;
    align-self: stretch;
    padding: 0;
    overflow: visible;
    line-height: 0;
}

/*
 * Das Icon folgt der Hoehe der Inhaltsbox statt der 1.5em-Vorgabe oben; `aspect-ratio: 1` aus
 * `.svg-use` ergaenzt die Breite. Aufrufer, die eine Groesse per Inline-Style setzen, gewinnen
 * weiterhin — SvgButton erzeugt sein SvgUse aber ohne Groesse.
 */
vaadin-button[theme~="icon"] > .svg-use {
    width: auto;
    height: 100%;
}

/*
 * emma.scss: `.tooltip .title .icon { width: 20px; height: 20px; top: 3px; margin-right: 5px }`.
 * StaticEmmaMessage#setTitle setzt rohes HTML (`<p class='title'><svg class="icon">…`), das
 * greift also nicht ueber .svg-use. Ohne die Regel zieht `.icon { width/height: 100% }` das
 * Stern-Symbol auf die volle Absatzbreite (~420x150 px).
 *
 * `top` ist gegen die Glyphen gemessen, nicht gegen die Zeilenbox: Die Sternmitte liegt zwischen der
 * Mitte der Großbuchstaben und der der Kleinbuchstaben. Der Wert hängt an der Schrift (Open Sans);
 * wer Schrift oder Größe des Titels ändert, misst nach.
 */
p.title > svg.icon {
    width: 20px;
    height: 20px;
    position: relative;
    top: 5px;
    margin-right: 5px;
    color: var(--p360-emma);
}

/*
 * Statische Emma-Blase (component/emma/StaticEmmaMessage), Emma daneben (component/Avatar):
 * öffentliche Seiten, leere Listen, Startseite.
 *
 * Schrift und Stern in --p360-emma-ink wie beim Emma-Hinweis im Editor: Weiß auf dem Orange
 * käme nur auf 2,3:1, das Dunkelgrau auf 6,7:1 (Begründung am Token).
 *
 * Breite: so breit wie der Text, höchstens so breit wie der Platz. Die Untergrenze verhindert,
 * dass ein Nachbar im Flex-Layout die Blase auf ihre kleinste Breite drückt — dann bräche der Text
 * nach jeder Silbe um.
 */
.static-emma {
    --p360-emma-arrow: 1.25rem;
    position: relative;
    flex: none;
    box-sizing: border-box;
    inline-size: fit-content;
    min-inline-size: min(12.5rem, 100%);
    max-inline-size: 100%;
    padding: var(--lumo-space-s) var(--lumo-space-m);
    gap: var(--lumo-space-xs);
    background: var(--p360-emma);
    color: var(--p360-emma-ink);
    border-radius: var(--lumo-border-radius-m);
    text-align: start;
}

.static-emma p {
    margin: 0;
}

.static-emma p.title {
    font-weight: bold;
}

/* Auf der orangen Fläche wäre der Stern in Emma-Orange unsichtbar. */
.static-emma p.title > svg.icon {
    color: var(--p360-emma-ink);
}

/* Pfeil zu Emma als Dreieck aus Rändern; die Richtung setzt StaticEmmaMessage#setArrowDirection. */
.static-emma::after {
    content: "";
    position: absolute;
    inline-size: 0;
    block-size: 0;
    border: var(--p360-emma-arrow) solid transparent;
}

/* Emma steht darunter, rechtsbündig: der Pfeil sitzt über ihrem Kopf. */
.static-emma.emma-arrow-down::after {
    inset-block-start: 100%;
    inset-inline-end: 3.75rem;
    border-block-start-color: var(--p360-emma);
    border-block-end-width: 0;
}

/* Emma steht links daneben. */
.static-emma.emma-arrow-left::after {
    inset-block-start: 2rem;
    inset-inline-end: 100%;
    border-inline-end-color: var(--p360-emma);
    border-inline-start-width: 0;
}

/* Hinweis in einem Bereich, ohne Emma daneben (StaticEmmaMessage#note). */
.static-emma.emma-no-arrow::after {
    content: none;
}

/*
 * Emma-Bild: die Höhe bestimmt die Größe (die Produkt-SVGs sind nur 150 px hoch), die Breite folgt
 * dem Seitenverhältnis und ist nach oben begrenzt.
 */
.emma-avatar-image {
    flex: none;
    block-size: 21.875rem;
    inline-size: auto;
    max-inline-size: 100%;
    object-fit: contain;
}

.emma-avatar-small .emma-avatar-image {
    block-size: 18.75rem;
}

/* In der vollen Breite des Avatars; Emma steht rechts darunter, der Pfeil zeigt auf sie. */
.emma-avatar-top .static-emma {
    inline-size: auto;
    align-self: stretch;
}

/*
 * Emma links, Blase rechts. Wird es zu eng — der Avatar steht in einer schmalen Spalte oder auf dem
 * Mobiltelefon —, kommt die Blase unter Emma, der Pfeil zeigt nach oben. Gemessen am Avatar selbst
 * (Container-Abfrage), weil er in Spalten unterschiedlicher Breite steht.
 */
.emma-avatar-right {
    container: emma-avatar / inline-size;
}

/* Neben Emma darf die Blase schrumpfen, bis zur Untergrenze aus .static-emma. */
.emma-avatar-row > .static-emma {
    flex: 0 1 auto;
}

@container emma-avatar (max-width: 30rem) {
    .emma-avatar-row {
        flex-direction: column;
    }

    .emma-avatar-row .emma-avatar-image {
        block-size: 12.5rem;
    }

    .emma-avatar-row > .static-emma.emma-arrow-left::after {
        inset-block-start: auto;
        inset-block-end: 100%;
        inset-inline: calc(50% - var(--p360-emma-arrow)) auto;
        border-color: transparent;
        border-block-end-color: var(--p360-emma);
        border-block-start-width: 0;
        border-inline-start-width: var(--p360-emma-arrow);
    }
}

/* emma.scss: .emma-border / .emma-notification */
.emma-border {
    border: 3px solid var(--p360-emma);
    border-radius: 10px;
}

/* Emma-Hinweis als Toast (Toast#emma). Farben aus den Toast-Tokens, Abschnitt „Toasts“ unten. */
vaadin-notification-card.emma-notification::part(content) {
    text-align: left;
}

/*
 * Emma-Hinweis am Bedienelement (component/emma/EmmaHint, Vaadin-25-Popover).
 *
 * Die sichtbare Flaeche liegt im Shadow-DOM (`<vaadin-popover-overlay>` mit
 * `exportparts="backdrop, overlay, content, arrow"`), erreichbar also nur ueber `::part` am Host.
 * Ausgewaehlt wird ueber das `theme`-Attribut statt ueber eine CSS-Klasse: nur `theme` reicht
 * Popover an sein Overlay weiter, und davon haengt auch der Pfeil ab (`theme~="arrow"`).
 *
 * Der Pfeil erbt `background` vom Overlay-Part, faerbt sich also mit.
 */
vaadin-popover[theme~="emma-hint"]::part(overlay) {
    background: var(--p360-emma);
    color: var(--p360-emma-ink);
    border: none;
    box-shadow: var(--lumo-box-shadow-s);
    border-radius: 5px;
}

vaadin-popover[theme~="emma-hint"]::part(content) {
    padding: 10px;
}

/*
 * Mehrere Texte brechen mit <br> nach rund 50 Zeichen um (emma.select.north.direction). Schmaler
 * gesetzt, bricht jede dieser Zeilen noch einmal, und einzelne Wörter stehen allein in der Zeile.
 * Auf dem Handy begrenzt die Bildschirmbreite.
 */
.emma-hint-content {
    max-width: min(400px, calc(100vw - 48px));
    text-align: left;
}

/* Zielauswahl: der Text bleibt einzeilig, sonst stößt die Blase oben an den Fensterrand (EmmaHint#setWide). */
.emma-hint-content.emma-hint-content-wide {
    max-width: min(520px, calc(100vw - 48px));
}

.emma-hint-title {
    display: flex;
    align-items: center;
    gap: 5px;
    font-weight: bold;
}

/*
 * Der Stern sitzt auf der orangen Flaeche -- dort ist das Emma-Orange unsichtbar. Mittig zur Zeile
 * (Flex) läge er 1,4px über der Mitte der Buchstaben; gemessen wie bei p.title > svg.icon.
 */
.emma-hint-title .svg-use {
    width: 20px;
    color: var(--p360-emma-ink);
    position: relative;
    top: 1px;
}

.emma-hint-text {
    display: block;
    margin-top: 3px;
}

/*
 * ⓘ neben einem Titel (component/InfoButton) und seine Erklär-Blase. Die Blase ist weiß, nicht orange:
 * Orange steht für Hinweise, die eine Handlung verlangen (StaticEmmaMessage#note).
 *
 * Der Knopf steht im Fließtext des Titels bzw. im Reiter und ist ein Kreis, so groß wie das Symbol: ohne
 * Fläche, ohne Rand, 6px hinter dem Text. Mit den Lumo-Größen nähme er die Höhe eines normalen Knopfs mit
 * und schöbe die Zeile auseinander. Die Regel für Knöpfe in `.dialog-section` nimmt ihn aus; ihr Rand
 * und `margin: 0` lägen sonst als länglicher Rahmen um das ⓘ und klebten es an den Text.
 */
.p360-info-button {
    --vaadin-button-background: transparent;
    --vaadin-button-border: none;
    vertical-align: middle;
    width: 1rem;
    min-width: 0;
    height: 1rem;
    margin: 0 0 0 6px;
    padding: 0;
    border-radius: 50%;
    color: var(--lumo-secondary-text-color);
    cursor: var(--lumo-clickable-cursor);
}

.p360-info-button:hover {
    color: var(--p360-primary-accent);
}

/* Lumos Klick-Schimmer (`::after`, verschwommen, blendet 1,4 s lang aus) wäre eine Fläche um das ⓘ. */
.p360-info-button::before,
.p360-info-button::after {
    display: none;
}

/*
 * Fokusrahmen nur bei Tastaturbedienung (Lumo setzt `focus-ring` nicht beim Klick): ein Kreis mit 2px
 * Luft zum Symbol, in der Fokusfarbe der Felder. Lumos Ring ist ein `box-shadow` direkt am Rand und
 * läge ohne Luft am Kreis des Symbols.
 */
.p360-info-button[focus-ring] {
    box-shadow: none;
    outline: 2px solid var(--p360-field-border-color-focus);
    outline-offset: 2px;
}

/* Lumo rückt das Symbol mit Innenabstand ein; im Fließtext bliebe davon ein 8-px-Kreis. */
.p360-info-button vaadin-icon {
    width: 1rem;
    height: 1rem;
    padding: 0;
}

/*
 * Pfeil der Blase genau unter der Mitte des ⓘ, aber nicht in der Eckrundung. Die Blase setzt bündig an einer
 * Kante des ⓘ an (BOTTOM_START, BOTTOM_END; fehlt der Platz, wechselt Vaadin die Kante und setzt
 * `start-aligned` bzw. `end-aligned`), die Mitte des Pfeils steht `arrow-inset` von dieser Kante entfernt.
 * Die Blase rückt deshalb um `--p360-info-hint-shift` nach außen, und der Pfeil um genauso viel nach innen:
 * Er zeigt weiter auf die Mitte des ⓘ (halbe Breite 0.5rem) und hält rund 20px Abstand zum Blasenrand.
 * Bei der mittigen Lage (BOTTOM) gilt beides nicht.
 */
vaadin-popover[theme~="info-hint"] {
    --p360-info-hint-shift: 1.25rem;
    --vaadin-popover-arrow-inset: calc(0.5rem + var(--p360-info-hint-shift));
}

vaadin-popover[theme~="info-hint"][start-aligned]::part(overlay) {
    translate: calc(-1 * var(--p360-info-hint-shift)) 0;
}

vaadin-popover[theme~="info-hint"][end-aligned]::part(overlay) {
    translate: var(--p360-info-hint-shift) 0;
}

vaadin-popover[theme~="info-hint"]::part(content) {
    padding: var(--lumo-space-s) var(--lumo-space-m);
}

/* Bis etwa 400 Zeichen; Absätze aus den Sprachdateien (\n) bleiben stehen. */
.info-hint-text {
    max-width: min(26rem, calc(100vw - 3rem));
    white-space: pre-line;
    font-size: var(--lumo-font-size-s);
    font-weight: normal;
    line-height: var(--lumo-line-height-s);
    color: var(--lumo-body-text-color);
    text-align: start;
}

/* --- Toasts: Fläche und Schrift je Art --- */

/*
 * Jede Toast-Art hat ein Paar aus Fläche und Schrift. Die Flächen bleiben die Lumo- bzw. Markenfarben;
 * die Schrift ist so gewählt, dass sie darauf mindestens 4,5:1 erreicht (Werte in den Kommentaren).
 * Wer eine Fläche ändert, misst die Schrift dazu nach.
 *
 * Welche Java-Stelle welche Art erzeugt:
 *   success  — Toast#info, Toast#cInfo (Theme-Variante LUMO_SUCCESS)
 *   error    — Toast#error, Toast#cError, falsches Login, Upload-Fehler (LUMO_ERROR)
 *   warning  — Toast#warning, Toast#cWarning (LUMO_WARNING)
 *   emma     — Toast#emma, orange Hinweise im Editor (Klasse emma-notification)
 *   contrast — Wartungshinweis auf der Login-Seite (LUMO_CONTRAST)
 *   plain    — ohne Variante: AnweisungsNotification, Hinweis „Konto gesperrt“ beim Imitieren
 */
html {
    /*
     * Fast-Schwarz statt Grün, die Marke steckt im Haken (Entscheidung Arne 02.11., Variante c).
     * Weiß 13,6:1; Haken in --p360-primary-on-dark: Grün 6,6:1, Orange 5,9:1, bei Blau weiß.
     */
    --p360-toast-success-background: var(--p360-ink);
    --p360-toast-success-text: #ffffff;
    --p360-toast-error-background: var(--lumo-error-color);
    /* 6,3:1 auf #a83b3b */
    --p360-toast-error-text: #ffffff;
    --p360-toast-warning-background: var(--lumo-warning-color);
    /* 6,7:1 auf #ef971e, dieselbe Schrift wie die Emma-Blase */
    --p360-toast-warning-text: var(--p360-emma-ink);
    --p360-toast-emma-background: var(--p360-emma);
    /* 6,7:1 auf #ef971e; Weiß käme nur auf 2,3:1. */
    --p360-toast-emma-text: var(--p360-emma-ink);
    --p360-toast-contrast-background: var(--lumo-contrast);
    /* 15,6:1 */
    --p360-toast-contrast-text: #ffffff;
    --p360-toast-plain-background: var(--lumo-base-color);
    --p360-toast-plain-text: var(--lumo-body-text-color);
}

/* Je Art die beiden Tokens auf das Paar legen, das die gemeinsamen Regeln darunter lesen. */
vaadin-notification-card {
    --p360-toast-background: var(--p360-toast-plain-background);
    --p360-toast-text: var(--p360-toast-plain-text);
}

vaadin-notification-card[theme~="success"] {
    --p360-toast-background: var(--p360-toast-success-background);
    --p360-toast-text: var(--p360-toast-success-text);
}

vaadin-notification-card[theme~="error"] {
    --p360-toast-background: var(--p360-toast-error-background);
    --p360-toast-text: var(--p360-toast-error-text);
}

vaadin-notification-card[theme~="warning"] {
    --p360-toast-background: var(--p360-toast-warning-background);
    --p360-toast-text: var(--p360-toast-warning-text);
}

vaadin-notification-card[theme~="contrast"] {
    --p360-toast-background: var(--p360-toast-contrast-background);
    --p360-toast-text: var(--p360-toast-contrast-text);
}

vaadin-notification-card.emma-notification {
    --p360-toast-background: var(--p360-toast-emma-background);
    --p360-toast-text: var(--p360-toast-emma-text);
}

/*
 * Die sichtbare Fläche ist der Shadow-Part [part=overlay]; ein Hintergrund am Host malte nur ein
 * Rechteck hinter die Karte. Die Schrift zusätzlich an [part=content], weil Lumo dort je Variante eine
 * eigene Farbe setzt, die sonst vor der geerbten gewinnt. Das Symbol (Haken, Warnung, Ausrufezeichen) übernimmt
 * die Schriftfarbe. Nur die oben belegten Arten — eine weitere Lumo-Variante (etwa primary) behält ihre
 * Lumo-Farben, bis sie hier ein eigenes Paar bekommt.
 */
vaadin-notification-card:is(:not([theme]), [theme~="success"], [theme~="error"], [theme~="warning"], [theme~="contrast"], .emma-notification)::part(overlay) {
    background: var(--p360-toast-background);
    color: var(--p360-toast-text);
}

vaadin-notification-card:is(:not([theme]), [theme~="success"], [theme~="error"], [theme~="warning"], [theme~="contrast"], .emma-notification)::part(content) {
    color: var(--p360-toast-text);
}

/* Der Haken ist ein vaadin-icon im Light DOM der Karte und erbte sonst die weiße Schrift. */
vaadin-notification-card[theme~="success"] vaadin-icon {
    color: var(--p360-primary-on-dark);
}

/* --- 5. Views --- */

.lost-password-link {
    color: var(--lumo-primary-text-color);
    font-size: var(--lumo-font-size-s);
    text-decoration: none;
}

/* --- Panel über dem Produktbild (CenterPanelProductView) --- */

/* Grund, wenn das Produkt kein Markenbild hat (ProductFullSizeView lässt das Bild dann weg). */
.login-page {
    background: var(--lumo-contrast-5pct);
}

/*
 * Login, Passwort vergessen, Registrieren, Passwort setzen, Registrierung bestätigen. Das Produktbild
 * liegt fest hinter der ganzen Seite (object-fit wie background-size: cover), die Ebene mit dem
 * Panel fest darüber. Ist das Panel höher als das Fenster, scrollt diese Ebene; das Bild bleibt
 * stehen und verschiebt nichts.
 */
.login-background-image {
    position: fixed;
    inset: 0;
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
}

.login-background {
    position: fixed;
    inset-block-start: 0;
    inset-inline-start: 0;
    overflow-y: auto;
}

/*
 * Raster aus Logo-Spalte und Inhalt. Die Höhe ergibt sich aus dem Inhalt. Unter 64rem (1024 px)
 * bliebe neben dem Logo für Emma und das Formular kein Platz: dann steht das Logo als Band darüber.
 */
.login-center-panel {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    /* Logo-Spalte und Login-Fußzeile darin teilen sich diese Werte (siehe .login-panel-left). */
    --p360-public-logo-column-bg: rgba(255, 255, 255, 0.65);
    --p360-public-logo-column-blur: blur(24px);
    --p360-public-logo-column-edge: rgba(255, 255, 255, 0.5);
}

@media (min-width: 64rem) {
    .login-center-panel {
        grid-template-columns: 18.75rem minmax(0, 1fr);
    }

    /*
     * Mit Fußzeile (Login): sie steht unten in der Logo-Spalte, der Inhalt reicht über beide Zeilen.
     * Die zusätzliche Höhe des Inhalts geht an die Logo-Zeile, die Fußzeile bleibt so hoch wie ihr Text.
     */
    .login-center-panel:has(> .login-panel-footer) {
        grid-template-rows: 1fr auto;
    }

    .login-center-panel:has(> .login-panel-footer) > .login-panel-right {
        grid-column: 2;
        grid-row: 1 / span 2;
    }

    /*
     * Die Logo-Spalte reicht über beide Zeilen und trägt allein das Milchglas; die Fußzeile liegt ohne
     * eigene Fläche unten darauf. Zwei Milchglas-Flächen übereinander ergäben unten einen helleren Streifen.
     */
    .login-center-panel:has(> .login-panel-footer) > .login-panel-left {
        grid-column: 1;
        grid-row: 1 / span 2;
        /* Unten so viel frei wie die Fußzeile hoch ist: Das Logo steht mittig über den Links, nicht in der ganzen Spalte. */
        padding-block-end: calc(var(--lumo-space-l) + 2.125rem);
    }

    .login-center-panel > .login-panel-footer {
        grid-column: 1;
        grid-row: 2;
        position: relative;
        background: transparent;
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
        border-block-start: none;
        box-shadow: none;
    }
}

.login-panel-left {
    /*
     * Milchglas: kaum Weiß, starke Unschärfe, damit hinter dem Logo das Markenbild erkennbar bleibt;
     * die helle Innenkante grenzt die Spalte vom Bild ab.
     */
    background: var(--p360-public-logo-column-bg);
    -webkit-backdrop-filter: var(--p360-public-logo-column-blur);
    backdrop-filter: var(--p360-public-logo-column-blur);
    box-shadow: inset 0 0 0 1px var(--p360-public-logo-column-edge);
    padding: var(--lumo-space-l) var(--lumo-space-m);
    box-sizing: border-box;
}

/* Maßstab für das Emma-Panel darin (Container-Abfrage an .emma-panel). */
.login-panel-right {
    container: public-panel / inline-size;
    background: var(--lumo-base-color);
}

/* Höhe begrenzt, damit ein quadratisches Kundenlogo das Panel nicht in die Länge zieht. */
.login-center-panel .login-logo {
    inline-size: 100%;
    max-inline-size: 14.375rem;
    max-block-size: 150px;
    object-fit: contain;
    margin: 0;
}

/*
 * Mobiltelefon und schmale Fenster: Das Logo-Band ist eine deckende hellgraue Fläche statt Milchglas.
 * Darüber liegt im Markenbild meist nur helle Decke, dort wirkt Milchglas unruhig und verwaschen.
 */
@media (max-width: 63.99rem) {
    .login-panel-left {
        background: #ededed;
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
        box-shadow: none;
    }
}

.to-login-label {
    padding: 0 var(--lumo-space-l) var(--lumo-space-l);
}

/* Anbieter und Impressum: gestapelt als Abschluss des Panels über einer feinen Linie. */
.login-panel-footer {
    background: var(--lumo-base-color);
    border-block-start: 1px solid var(--lumo-contrast-10pct);
    padding: var(--lumo-space-s) var(--lumo-space-m);
    box-sizing: border-box;
    font-size: var(--lumo-font-size-xs);
    color: var(--lumo-secondary-text-color);
}

/*
 * Der Trennstrich steht genau in der Mitte, auch wenn die beiden Links verschieden lang sind:
 * zwei gleich breite Spalten links und rechts vom Strich. Nur mit beiden Links (Anbieter, Strich,
 * Impressum); fehlt der Anbieter, bleibt das Impressum per Flex mittig.
 */
.login-info-text:has(> :nth-child(3)) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: center;
    column-gap: var(--lumo-space-m);
}

.login-info-text:has(> :nth-child(3)) > :first-child {
    justify-self: end;
    text-align: end;
}

.login-info-text:has(> :nth-child(3)) > :last-child {
    justify-self: start;
}

/*
 * Trennstrich zwischen Anbieter und Impressum: VDividerLabel setzt ihn inline in Hellgrau (#d5d5d5),
 * auf Milchglas und hellem Markenbild ist er so kaum zu sehen. Deshalb hier dunkler und mit fester
 * Höhe; !important, weil der Inline-Stil sonst gewinnt.
 */
.login-info-text > span:not(:first-child):not(:last-child) {
    border-left: 1px solid var(--lumo-contrast-50pct) !important;
    width: 0 !important;
    height: 1em !important;
    align-self: center;
}

/*
 * Emma mit Formular (component/emma/EmmaPanel): untereinander, ab 36rem Panelbreite nebeneinander.
 * Untereinander (Mobiltelefon) steht das Formular oben und Emma darunter, damit Feld und Knopf ohne
 * Scrollen erreichbar sind. Nebeneinander steht Emma oben, das Formular vertikal mittig neben ihr; ist das
 * Formular höher als Emma, bestimmt es die Höhe und beginnt oben. Die DOM-Reihenfolge (Emma, dann
 * Formular) bleibt Lese- und Tab-Reihenfolge; Emma hat nichts Fokussierbares.
 */
.emma-panel {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    box-sizing: border-box;
}

.emma-panel > .emma-avatar {
    flex: none;
    align-self: center;
}

.emma-panel > .emma-panel-content {
    order: -1;
}

/* Gestapelt: Emma kleiner, damit das Formular ohne langes Scrollen erreichbar bleibt. */
.emma-panel .emma-avatar-image {
    block-size: 10rem;
}

@container public-panel (min-width: 36rem) {
    .emma-panel {
        flex-direction: row;
        align-items: flex-start;
    }

    .emma-panel > .emma-avatar {
        align-self: flex-start;
    }

    .emma-panel > .emma-panel-content {
        order: 0;
        align-self: center;
    }

    .emma-panel .emma-avatar-image {
        block-size: 18.75rem;
    }
}

/* --- Startseite: Kachel-Raster --- */

/*
 * Zwei Spalten, jede ein eigener Stapel (.home-column). Die Kacheln liegen NICHT direkt im
 * Raster: eine Zeilenhoehe richtet sich nach der hoechsten Kachel der Zeile, und die rechte
 * Spalte (Ring 250px, Statistik, Abo) haette damit immer die Zeilenhoehen der Projekt- und
 * Rechnungstabelle geerbt — gleich grosse Abstaende sind so nicht zu bekommen.
 *
 * `minmax(0, 1fr)` statt `1fr`: das Mindestmass von `1fr` ist die Inhaltsbreite, und die Kacheln
 * bringen rund 430px mit (Projekt- und Rechnungstabelle). Die Spur waechst sonst ueber den
 * Seiten-Container hinaus, statt die Tabellen schrumpfen zu lassen.
 */
.home-grid {
    display: grid;
    /*
     * 2:1. Links steht die Projekt-Tabelle mit sieben Spalten, rechts „Ihr Abo“ und Statistik,
     * höchstens 300px breit und mittig in ihrer Spur.
     */
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
    gap: var(--lumo-space-xl);
    /*
     * Oberkante beider Spalten: links die Ueberschrift „Neueste Projekte", rechts die Karte
     * „Ihr Abo“. Sie stehen nur so lange buendig, wie der Projekt-Block keinen eigenen
     * Innenabstand traegt (HomePage, setPadding(false)).
     */
    --p360-home-column-top: var(--lumo-space-m);
    /* Abstand zwischen zwei Kacheln derselben Spalte — rechts gilt er dreimal gleich. */
    --p360-home-column-gap: var(--lumo-space-l);
    /* Sichtbarer Abstand ueber „Letzte Abrechnungen": Unterkante Projekt-Tabelle bis Ueberschrift. */
    --p360-home-bills-top: 70px;
    /*
     * Luft unter der untersten Kachel (sie klebte am Fensterrand). Der Abstand gehoert an
     * das Raster und nicht an den Seiten-Container oder den Scroll-Halter: der Seiten-Container
     * steht auf voller Hoehe (BaseView, flex-grow), der Inhalt laeuft ueber ihn hinaus — sein
     * unterer Innenabstand liegt damit UEBER dem Ueberlauf. Und der Innenabstand des
     * Scroll-Halters zaehlt nicht zum Scrollbereich, verkleinert aber jede Seite. Gemessen:
     * Kachelunterkante 40px ueber der Falz statt buendig.
     */
    padding-block-end: var(--lumo-space-xl);
}

.home-column {
    display: flex;
    flex-direction: column;
    gap: var(--p360-home-column-gap);
    /* Ohne das setzt die Projekt-Tabelle die Mindestbreite der Spur und sprengt das Raster. */
    min-width: 0;
    padding-block-start: var(--p360-home-column-top);
}

/* Rechte Spalte: „Ihr Abo“ und Statistik mittig untereinander, so breit wie die Spalte, höchstens 300px. */
.home-column--side {
    align-items: center;
}

.home-column--side > * {
    width: 100%;
    max-width: 300px;
}

/*
 * Der Rechnungs-Block bekommt mehr Luft nach oben als der Spaltenabstand hergibt — sonst liest
 * sich die zweite Tabelle als Fortsetzung der ersten. Der Sollwert steht als Token am Raster und
 * wird gegen den Spaltenabstand verrechnet: wer `--p360-home-column-gap` aendert, verschiebt damit
 * nicht den sichtbaren Abstand ueber „Letzte Abrechnungen".
 */
.home-bills {
    margin-block-start: calc(var(--p360-home-bills-top) - var(--p360-home-column-gap));
}

/* „Tarif buchen": eine zentrierte Spalte statt des Kachel-Rasters. */
.home-grid--single {
    grid-template-columns: 1fr;
    place-items: center;
}

/*
 * Unter 800px ist die zweite Spalte nur noch ein Streifen: die Kacheln (Projekt-Tabelle,
 * Statistik, Abo) brauchen gut 340px. Darunter stehen die beiden Spalten deshalb untereinander,
 * „Ihr Abo“ und Statistik in voller Breite über den Projekten.
 */
@media (max-width: 799.98px) {
    .home-grid {
        grid-template-columns: minmax(0, 1fr);
    }

    .home-column--side {
        order: -1;
    }

    .home-column--side > * {
        max-width: none;
    }
}

/* --- Panel-Karte: jedes BorderTitlePanel (Startseite, Profil -> Tarif & Abrechnung) --- */

/*
 * vaadin-card ist ueber eigene Custom-Properties steuerbar; Farbe, Schriftgroesse und -gewicht des
 * Titels setzt das Komponenten-Stylesheet mit !important und laesst sich NUR ueber diese Tokens
 * aendern — nicht mit einer Regel von aussen.
 *
 * Weiche Karte ohne Band und ohne Rahmen: Titel klein, fett, Fast-Schwarz oben links, ein Zusatz
 * (Monat) grau rechts in derselben Zeile (Arne, 03.11.: „weniger Grün, mehr Schwarz“). Die Marke
 * zeigt sich nur in Symbolen, Ring-Bogen und Knöpfen.
 */
.panel-card-shell {
    --vaadin-card-background: var(--lumo-base-color);
    --vaadin-card-border-radius: 12px;
    --vaadin-card-title-font-size: var(--lumo-font-size-s);
    --vaadin-card-title-font-weight: 700;
    --vaadin-card-title-color: var(--p360-ink);
    /* Abstand Titel → Inhalt; 8px wirkten gedrängt (Chris, 04.11.). */
    --vaadin-card-gap: var(--lumo-space-m);
    --vaadin-card-padding: var(--lumo-space-m);
    --vaadin-card-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 4px 12px rgba(0, 0, 0, 0.06);
    box-shadow: var(--vaadin-card-shadow);
    box-sizing: border-box;
}

.panel-card-shell > [slot="title"] {
    text-align: start;
}

.panel-card-title-suffix {
    color: var(--p360-muted);
    font-size: var(--lumo-font-size-xs);
    white-space: nowrap;
}

/*
 * Der Inhalt nimmt, was der Titel uebrig laesst — und gibt es als feste Bezugshoehe weiter:
 * Statistik-Raster und Abo-Layout stehen auf `sizeFull` und brauchen einen Bezug, der nicht
 * wieder auf `auto` hinauslaeuft.
 */
.panel-card-content {
    box-sizing: border-box;
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    min-height: 0;
    /*
     * Der Inhalts-Teil der Karte ist ein Block, kein Flex-Container — `flex: 1` darin bleibt wirkungslos.
     * Ohne die Prozenthoehe klebt der Inhalt oben, sobald die Karte hoeher ist als er (Tarif & Abrechnung:
     * „Rechnung in" uebernimmt die Hoehe von „Mein Abo"). Steht die Karte auf Inhaltshoehe, loest 100%
     * gegen `auto` auf und aendert nichts.
     */
    height: 100%;
}

.panel-card-content > * {
    flex: 1 1 auto;
    min-height: 0;
}

/* Symbol der Karte: Markenfarbe auf einem hellen Kreis derselben Farbe. */
.svg-use.panel-card-icon {
    box-sizing: border-box;
    width: 40px;
    height: 40px;
    padding: 8px;
    border-radius: 50%;
    background: var(--p360-primary-tint);
    color: var(--p360-primary);
    margin-block-end: var(--lumo-space-xs);
}

.panel-card-value {
    color: var(--p360-ink);
    font-size: 2.25rem;
    font-weight: 600;
    line-height: 1.1;
}

.panel-card-label {
    color: var(--p360-muted);
    font-size: var(--lumo-font-size-s);
    text-align: center;
}

/* Status in „Mein Abo“ und „Ihr Abo“, ein kurzes Wort; neutral, ein laufendes Abo in der hellen Markenfarbe. */
.panel-card-pill {
    display: inline-block;
    max-width: 100%;
    box-sizing: border-box;
    padding: 2px 12px;
    border-radius: 999px;
    background: #f0f3f2;
    color: var(--p360-ink);
    font-size: var(--lumo-font-size-s);
    font-weight: 600;
    line-height: 1.6;
}

.panel-card-pill--accent {
    background: var(--p360-primary-tint);
}

/*
 * Offene Rechnung hell orange, gesperrt/abgelaufen hell rot; die Schrift in dunklem Ton derselben Farbe.
 * Kontrast Schrift/Fläche: #7a4100 auf #fdebd0 7,0:1, #8f1d1d auf #fbe0e0 7,1:1. Feste Töne statt der
 * Markenfarbe: Orange und Rot sagen hier etwas über den Zustand, nicht über die Marke.
 */
.panel-card-pill--warning {
    background: #fdebd0;
    color: #7a4100;
}

.panel-card-pill--error {
    background: #fbe0e0;
    color: #8f1d1d;
}

/* Statistik-Karte: zwei Spalten, je Symbol, Zahl und Bezeichnung untereinander, mittig in der Karte. */
.home-statistics {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto auto auto;
    grid-auto-flow: row;
    justify-items: center;
    align-content: center;
    row-gap: var(--lumo-space-xs);
}

/* --- Abo-Karten: „Ihr Abo“ (Startseite), „Mein Abo“ und „Nutzung“ (Abo-Seite) --- */

/* Tarifname groß, daneben die Status-Pille; bei langem Namen rutscht die Pille darunter. */
.subscription-card-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--lumo-space-xs) var(--lumo-space-s);
}

/* Das ⓘ steht im Namen; Flex richtet es auf die Mitte der Schrift aus, wie die Pille daneben. */
.subscription-card-name {
    display: inline-flex;
    align-items: center;
    color: var(--p360-ink);
    font-size: var(--lumo-font-size-xl);
    font-weight: 600;
    line-height: 1.2;
}

/*
 * Zahlen unter dem Kopf: oben eine kräftige Linie wie unter einem Tabellenkopf, zwischen den Zeilen
 * die zarte Zeilenlinie der Tabellen (34h).
 */
.subscription-card-body {
    --p360-subscription-line: #eef1f0;
    display: flex;
    flex-direction: column;
    margin-block-start: var(--lumo-space-m);
    border-top: 2px solid var(--p360-ink);
}

.subscription-card-body > * {
    padding-block: var(--lumo-space-s);
}

.subscription-card-body > * + * {
    border-top: 1px solid var(--p360-subscription-line);
}

/* „Nutzung“ hat keinen Kopf; die Zeilen beginnen direkt unter dem Kartentitel. */
.subscription-card-body:first-child {
    margin-block-start: 0;
    border-top: none;
}

.subscription-card-row {
    display: flex;
    justify-content: space-between;
    gap: var(--lumo-space-s);
    font-size: var(--lumo-font-size-s);
}

/* Die Bezeichnung bleibt einzeilig links oben; ein langer Wert (Zieltarif) bricht rechtsbündig um. */
.subscription-card-row > :first-child {
    flex: 0 0 auto;
    color: var(--p360-muted);
}

.subscription-card-row > :last-child {
    min-width: 0;
    overflow-wrap: anywhere;
    color: var(--p360-ink);
    font-weight: 600;
    text-align: end;
}

.subscription-card-note {
    display: block;
    margin-block-start: var(--lumo-space-s);
    color: var(--p360-muted);
    font-size: var(--lumo-font-size-s);
}

/*
 * Blase am ⓘ hinter dem Tarifnamen: Beschreibung, darunter die Tarifkarte des eigenen Produkts. Auf dem
 * Mobiltelefon bleibt sie im Fenster und scrollt selbst, statt unten abgeschnitten zu werden.
 */
.subscription-card-info {
    display: flex;
    flex-direction: column;
    gap: var(--lumo-space-m);
    max-width: min(20rem, calc(100vw - 3rem));
    max-height: calc(100vh - 8rem);
    overflow-y: auto;
}

/*
 * Die Karte steht schon in der weißen Blase: ohne eigenen Schatten und Innenabstand, durch eine Linie von
 * der Beschreibung getrennt. `height: auto` nimmt die 100% aus `.rate-card-shell` zurück.
 */
.subscription-card-info .subscription-card-info-tariff {
    --vaadin-card-padding: 0;
    --vaadin-card-shadow: none;
    box-shadow: none;
    height: auto;
}

.subscription-card-info .info-hint-text + .subscription-card-info-tariff {
    padding-top: var(--lumo-space-m);
    border-top: 1px solid var(--lumo-contrast-10pct);
}

.subscription-card-actions {
    display: flex;
    flex-direction: column;
    gap: var(--lumo-space-s);
    margin-block-start: var(--lumo-space-m);
}

.subscription-card-actions > vaadin-button {
    width: 100%;
    margin: 0;
}

/* „Abo verwalten“ in der Titelzeile: ein Link in Schriftfarbe, keine zweite Markenfläche neben dem Titel. */
vaadin-button.subscription-card-manage {
    --vaadin-button-text-color: var(--p360-ink);
    color: var(--p360-ink);
    font-size: var(--lumo-font-size-s);
    font-weight: 400;
}

/* Die Beschriftung liegt im Shadow DOM; die Unterstreichung muss an ihr Teil. */
vaadin-button.subscription-card-manage::part(label) {
    text-decoration: underline;
}

/* Belegung eines Kontingents (QuotaRow): Bezeichnung und „8 von 10“, Balken, „2 frei“. */
.quota-row {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.quota-row [hidden] {
    display: none;
}

.quota-row-line {
    display: flex;
    justify-content: space-between;
    gap: var(--lumo-space-s);
    color: var(--p360-muted);
    font-size: var(--lumo-font-size-s);
}

.quota-row-value {
    color: var(--p360-ink);
    font-weight: 700;
    white-space: nowrap;
}

.quota-bar {
    display: flex;
    height: 8px;
    border-radius: 4px;
    background: var(--lumo-contrast-10pct);
    overflow: hidden;
}

.quota-bar-fill {
    height: 100%;
    background: var(--p360-primary);
}

/*
 * Projekte über dem Kontingent: gestreift in der Produktfarbe, durch eine schmale weiße Lücke vom
 * Kontingent getrennt (Variante U1). Grau sähe nach freiem Platz aus, Rot nach einem Fehler.
 */
.quota-bar-over {
    box-sizing: border-box;
    height: 100%;
    border-inline-start: 3px solid var(--lumo-base-color);
    background: repeating-linear-gradient(135deg, var(--p360-primary) 0 3px, var(--lumo-base-color) 3px 6px);
}

.quota-row-note {
    color: var(--p360-muted);
    font-size: var(--lumo-font-size-xs);
    line-height: 1.4;
}

/* --- Profil -> Tarif & Abrechnung -> Abo: „Mein Abo“ und „Nutzung“ nebeneinander --- */

.bill-overview-grid {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--lumo-space-l);
    padding: var(--lumo-space-m);
    /* Volle Breite plus Innenabstand: ohne border-box scrollte der Bereich auf jeder Breite seitwärts. */
    box-sizing: border-box;
}

/*
 * Links das Abo (300px), rechts die Nutzung. Beide Karten strecken sich auf die Höhe der höheren,
 * ihr Inhalt steht oben (Arne: „G2, wenn beides gleich hoch wird“). Auf dem Mobiltelefon untereinander
 * in voller Breite.
 */
.bill-overview-tiles {
    display: grid;
    grid-template-columns: minmax(0, 300px) minmax(0, 1fr);
    align-items: stretch;
    gap: var(--lumo-space-l);
    width: 100%;
}

@media (max-width: 639.98px) {
    .bill-overview-tiles {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* --- Profil -> Logindaten (LogindatenForm) --- */

/*
 * Eine E-Mail-Adresse hat kaum Stellen zum Umbrechen. Ohne `overflow-wrap: anywhere` gibt sie die
 * Mindestbreite der Zeile vor und schiebt den Stift aus dem Bild — bei 375px lag er rechts außerhalb.
 */
.login-data-row {
    width: 100%;
}

.login-data-row > span {
    min-width: 0;
    overflow-wrap: anywhere;
}

.login-data-row > vaadin-button {
    flex: none;
}

/* --- Profil -> Terminvereinbarung: Platzhalter-Knöpfe (component/templateEditor/TemplateEditor) --- */

/* Schrift neutral statt in der Markenfarbe, auch unter dem Zeiger; die graue Fläche bleibt. */
.template-editor-key-button {
    --vaadin-button-text-color: var(--lumo-body-text-color);
}

/* --- Hotspot-Kontextmenue im Tour-Editor (hotSpotMenu.js) --- */

/*
 * Quelle: Vaadin-8-Theme themeBase/platform/component/component.scss (kompiliert in
 * VAADIN/themes/th-blue/styles.css). Uebernommen ist nur das Minimum, das Add-Button und Menue
 * ueberhaupt sichtbar und klickbar macht — Positionierung, Stapelreihenfolge, Trefferflaeche.
 *
 * Abweichung: Der Add-Button trug in Vaadin 8 zusaetzlich die Valo-Klassen
 * `v-button round-button primary-color`, die es in Vaadin 25 nicht mehr gibt. Sein Aussehen
 * (runde, gefuellte Flaeche mit weissem Plus) steht deshalb hier direkt an `.addBtn`.
 */

/*
 * Oranger Rahmen ums Panorama, solange ein Hotspot platziert wird. Mit `.krpano` davor: Die Regel
 * für `.krpano` weiter unten setzt den Rahmen auf 0 und gewann sonst. `border-box`, sonst ragte
 * das Panorama mit dem Rahmen 10px über die Fensterbreite.
 */
.krpano.hotspot-mode-active {
    box-sizing: border-box;
    border: 5px solid var(--p360-emma);
}

.addBtn {
    position: fixed;
    width: 40px;
    height: 40px;
    z-index: 9999;
    box-sizing: border-box;
    padding: 8px;
    border-radius: 50%;
    background: var(--p360-primary);
    color: #ffffff;
    cursor: pointer;
    box-shadow: var(--lumo-box-shadow-s);
}

.hotSpotMenu {
    position: fixed;
    z-index: 9998;
    border-radius: 5px;
    background: var(--p360-panel);
    box-shadow: var(--lumo-box-shadow-m);
    transition: opacity 300ms ease-in;
    opacity: 0;
}

.hotSpotMenu:not(:empty) {
    opacity: 1;
}

.hotSpotMenu ul {
    list-style-type: none;
    padding: 0;
    margin: 0;
}

.hotSpotMenu ul li {
    display: flex;
    align-items: center;
    gap: 12px;
    cursor: pointer;
    padding: 8px;
    border-bottom: 1px solid #e4e4e4;
    white-space: nowrap;
}

/*
 * `.icon { width: 100%; height: 100% }` aus dem Sprite-Block wuerde das Symbol im Flex-Item auf
 * die CSS-Ersatzgroesse aufziehen; im Vaadin-8-Theme stand die feste Kantenlaenge an derselben
 * Stelle.
 */
.hotSpotMenu ul li .icon {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    color: var(--p360-menu);
}

.hotSpotMenu ul li:first-child {
    border-top-left-radius: 5px;
    border-top-right-radius: 5px;
}

.hotSpotMenu ul li:last-child {
    border-bottom: 0;
    border-bottom-left-radius: 5px;
    border-bottom-right-radius: 5px;
}

.hotSpotMenu ul li:hover {
    background: #e4e4e4;
}

/* --- Tour-Editor: Grundgeruest, Standpunktleiste, Werkzeuge --- */

/*
 * Quelle: Vaadin-8-Theme themeBase/platform/component/editor.scss (+ .overlay-widget aus
 * component.scss). Uebernommen sind Anker, Masse und Positionen — die Deko-Regeln des alten Themes
 * bleiben draussen.
 *
 * Der Editor ist eine feste Flaeche ueber den ganzen Viewport: Kopfzeile (80px) + Pano
 * (Rest), darauf als Overlays die Werkzeugleiste, das Overlay-Widget und die Standpunktleiste.
 * Nichts davon liegt im Fluss — sonst summieren sich die Hoehen und die Seite scrollt.
 */

.editor,
.viewpoint-menu {
    background: #ffffff;
}

/*
 * `position: relative` macht den Editor zum Bezugsrahmen fuer .editor-tools und .overlay-widget
 * (in Vaadin 8 war das der Editor-CssLayout). `overflow: hidden` haelt den Rest drin: der
 * Seiten-Halter (.p360-view-holder) scrollt sonst, sobald ein Kind ueber die Kante laeuft.
 */
.editor {
    position: relative;
    overflow: hidden;
}

.editor-header {
    flex: 0 0 auto;
    padding-inline: var(--lumo-space-m);
    position: relative;
    z-index: 2;
}

/*
 * Standpunkt-Titel in der Editor-Kopfzeile. Er sitzt in der wachsenden Mitte der Leiste;
 * `min-width: 0` hebt das Flex-Default `min-width: auto` auf, sonst schiebt ein langer Titel
 * die Werkzeuge rechts aus der Leiste, statt selbst gekuerzt zu werden.
 */
.editor-title {
    text-align: center;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* editor.scss: `.editor-header-tools .icon-only-button { padding: 2px }` */
.editor-header-tools .icon-only-button {
    padding: 2px;
}

/*
 * Mehr Luft in der Editor-Kopfleiste ab Tablet-Breite (#261): weiter vom Rand und weiter
 * auseinander. Auf dem Mobiltelefon reicht die Breite dafür nicht, dort bleibt der Titel sonst
 * ganz weg.
 */
@media (min-width: 768px) {
    .editor-header {
        padding-inline: var(--lumo-space-xl);
    }

    .editor-header-tools {
        gap: var(--lumo-space-xl);
    }
}

/*
 * editor.scss: `.krpano { height: calc(100% - 80px); width: 100% }`. Die Hoehe setzt TourEditor
 * inline (der KrPano-Wrapper bringt setSizeFull() mit); hier bleiben Breite und der Rahmen, den
 * der Hotspot-Modus einfaerbt.
 */
.krpano {
    width: 100%;
    border: 0 solid var(--p360-primary);
    transition: border 0.2s ease-in;
}

/* component.scss: `.overlay-widget { position: absolute; bottom: 0; left: calc(50vw - 200px) }` */
.overlay-widget {
    position: absolute;
    bottom: 0;
    left: calc(50vw - 200px);
    text-shadow: black 1px 1px 10px;
    z-index: 3;
}

/*
 * Standpunktleiste: faehrt als Filmstreifen von unten ein (Hoehe 250px = VIEWPOINT_PANEL_HEIGHT
 * aus Theme.Styles.Constants, deshalb `bottom: -250px` im geschlossenen Zustand).
 *
 * Der Schatten nur bei ausgefahrener Leiste: eingefahren läge er auf den unteren 6px des Panoramas
 * und dunkelt dort den orangen Platzier-Rahmen ab, der dann schmaler wirkt als die Seiten.
 */
.viewpoint-menu {
    position: fixed;
    bottom: -250px;
    left: 0;
    transition: bottom 0.2s ease-in;
    z-index: 4;
}

.viewpoint-menu.visible {
    bottom: 0;
    box-shadow: 0 -1px 6px rgba(0, 0, 0, 0.2);
}

/*
 * Der aeussere Halter der Kacheln darf nicht scrollen — das macht der innere .scroll-x-Streifen.
 * Ohne `min-height: 0` wachsen Flex-Kinder ueber die 250px der Leiste hinaus.
 */
.viewpoint-scroll-area {
    overflow: hidden;
    min-height: 0;
    min-width: 0;
}

/*
 * component.scss: `div.scroll-x` — in Flow scrollt das Element selbst statt eines Engine-Wrappers.
 * `min-width: 0` hebt das Flex-Default `min-width: auto` auf: sonst bleibt der Streifen so breit wie
 * seine Kacheln, laeuft aus der Leiste heraus und bekommt nie eine Scroll-Leiste.
 */
.scroll-x {
    overflow-x: auto;
    overflow-y: hidden;
    min-width: 0;
}

.scroll-y {
    overflow-y: auto;
    overflow-x: hidden;
}

/*
 * Scroll-Pfeile der Standpunktleiste (ScrollButtonsExtension legt sie in den .scroll-x-Streifen).
 * Bezugsrahmen ist die fixierte .viewpoint-menu — deshalb sitzen sie am Rand der Leiste, scrollen
 * nicht mit den Kacheln mit und werden vom Streifen auch nicht abgeschnitten.
 */
.scroll-button-left,
.scroll-button-right {
    position: absolute;
    width: 64px;
    height: 64px;
    top: calc(50% - 32px);
    z-index: 99;
    cursor: pointer;
    border-radius: 50%;
    background-color: #ffffff;
    color: var(--p360-primary-strong);
    box-shadow: var(--lumo-box-shadow-s);
}

.scroll-button-left {
    left: 40px;
}

.scroll-button-right {
    right: 40px;
}

/*
 * Kachel-grosser Plus-Button am Anfang des Streifens, öffnet „Panoramen hochladen“. Sieht aus wie die
 * Upload-Flächen (.p-uploader): weiß, grauer Strichrand, Symbol in der Flächenfarbe, unter dem Zeiger
 * die helle Tönung (Entscheidung Chris 03.11.).
 */
.add-view-point-button {
    flex: 0 0 auto;
    box-sizing: border-box;
    background: var(--lumo-base-color);
    color: var(--p360-primary);
    border: 2px dashed var(--lumo-contrast-50pct);
    border-radius: 10px;
    padding: 35px;
    /* Lumo gibt Knöpfen oben und unten 4px Rand; die Plus-Kachel stand dadurch tiefer als die Standpunkte. */
    margin: 0;
    transition: background 0.3s ease-in-out;
}

.add-view-point-button:hover {
    background: var(--p360-primary-tint);
    color: var(--p360-primary);
}

/* editor.scss: `.view-point` — Standpunkt-Kachel mit Titelzeile und eingeblendeten Werkzeugen. */
.view-point {
    position: relative;
    overflow: hidden;
    flex: 0 0 auto;
    cursor: pointer;
    border-radius: 10px;
}

.view-point img {
    border-radius: 10px;
    object-fit: cover;
}

.view-point .view-point-caption {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 35px;
    /* Zeilenhöhe = Leistenhöhe: Titel und Stern stehen senkrecht in der Mitte, ohne Innenabstand oben. */
    line-height: 35px;
    background: rgba(255, 255, 255, 0.64);
    text-align: center;
    /* Kameranamen wie "RicohTheta 2021-04-16 (3)" sind laenger als die Kachel. Ohne
       border-box schoebe der Innenabstand die Zeile um seine Breite aus der Kachelmitte. */
    box-sizing: border-box;
    padding-inline: 8px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-weight: 600;
    transition: bottom 0.2s ease-in;
}

/* Startpano: Stern und Titel als eine Zeile, beide an der Mittellinie (Markup aus ViewPointComponent#showStar). */
.view-point .view-point-caption > div {
    display: inline-flex;
    align-items: center;
    gap: var(--lumo-space-xs);
    max-width: 100%;
    height: 100%;
    vertical-align: top;
}

.view-point .view-point-tools {
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 45%;
    /* Ohne border-box käme der Innenabstand zur Höhe dazu, die Leiste bedeckte gut die halbe Kachel. */
    box-sizing: border-box;
    background: rgba(255, 255, 255, 0.64);
    padding: var(--lumo-space-s);
    opacity: 0;
    transform: translateY(65px);
    transition: opacity 0.2s ease-in, transform 0.2s ease-in;
    display: none;
}

/* Drei gleich breite Felder über die ganze Kachel, jedes Symbol in der Mitte seines Drittels. */
.view-point .view-point-tools .icon-only-button {
    width: 100%;
    height: 100%;
    padding: 0;
}

/* Feste Symbolgröße: Die Sprite-Regel `.icon` zöge das Symbol sonst auf die Breite des Drittels. */
.view-point .view-point-tools .icon-only-button svg {
    width: 1.875rem;
    height: 1.875rem;
}

.view-point:hover .view-point-caption {
    bottom: -35px;
}

.view-point:hover .view-point-tools {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    opacity: 1;
    transform: translateY(0);
}

/*
 * Start-Pano-Badge. Der Stern kommt als rohes `<svg class="icon viewpoint-star">` aus
 * ViewPointComponent#showStar — die Sprite-Regel `.icon { width: 100%; height: 100% }` zieht ihn
 * sonst auf die volle Kachelbreite (gemessen 292x150). Vaadin 8 hatte die feste Kantenlaenge an
 * derselben Stelle, ebenfalls mit !important.
 */
.viewpoint-star {
    width: 24px !important;
    height: 24px !important;
    color: var(--p360-emma);
    flex: none;
}

.viewpoint-star-label {
    overflow: hidden;
    text-overflow: ellipsis;
}

/*
 * editor.scss: Gruppenzeile ueber dem Streifen.
 *
 * Die Zeile selbst wird vom Layout zentriert. Der Stift-Button am Ende zaehlte dabei mit und
 * schob die Etagen-Namen aus der Mitte -- deshalb steht er absolut am rechten Rand, und der
 * gleich breite Innenabstand auf beiden Seiten haelt ihm den Platz frei. Wer die Breite aendert,
 * muss beide Seiten gleich halten, sonst steht die Beschriftung wieder schief.
 */
.viewpoint-groups {
    position: relative;
    padding-inline: 40px;
    max-width: 100%;
}

.viewpoint-groups-edit {
    position: absolute;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
}

/* Farbe und Gewicht der Etagen-Knöpfe: Etagen-Reiter in Abschnitt 2c (--p360-floor-tab-*). */
.viewpoint-menu .view-point-group {
    color: var(--p360-floor-tab-color);
    font-weight: normal;
    flex: 0 0 auto;
    padding-block: var(--lumo-space-xs);
    /* Lange Etagen-Namen kuerzen statt umbrechen -- die Zeile ist nur eine Zeile hoch. */
    max-width: 220px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.viewpoint-menu .view-point-group::part(label) {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.view-point-group:hover {
    font-weight: bold;
}

.viewpoint-menu .view-point-group.active-view-point-group {
    color: var(--p360-floor-tab-selected-color);
    font-weight: var(--p360-floor-tab-selected-font-weight);
    border-bottom: var(--p360-floor-tab-selected-line) !important;
    border-radius: 0 !important;
}

/*
 * editor.scss: `.editor-tools { position: absolute; left: 20px; bottom: 20px }` — die Werkzeuge
 * liegen auf dem Pano und weichen nach oben aus, wenn die Standpunktleiste ausgefahren ist.
 */
.editor-tools {
    position: absolute;
    left: 20px;
    bottom: 20px;
    width: auto;
    z-index: 5;
    transition: bottom 0.2s ease-in;
    border-radius: 26px;
    background: rgba(255, 255, 255, 0.85);
    box-shadow: var(--lumo-box-shadow-s);
}

/*
 * EditorTools ist ein VerticalLayout mit den Flow-Defaults padding/spacing. Die Lumo-Regeln dazu
 * haengen am `theme`-Attribut (`vaadin-vertical-layout[theme~="padding"]`) und schlagen eine reine
 * Klassenregel — deshalb hier ebenfalls mit Attribut. Vaadin 8 hatte 6px Spacing, Lumo nimmt 1rem:
 * die vier Werkzeuge standen dadurch ueber 272px verteilt statt als Leiste beieinander.
 */
.editor-tools[theme~="padding"] {
    padding: 6px;
}

.editor-tools[theme~="spacing"] {
    gap: var(--lumo-space-xs);
}

.editor-tools.visible {
    bottom: 270px;
}

/*
 * Unter dem Zeiger Markenfläche mit weißem Symbol. Die Knöpfe sind `primary-color` mit
 * `theme="icon"`; ohne den zweiten Selektor gewönne der Akzent-Hover aus Abschnitt 2d, und das
 * Symbol stünde in -accent auf der Markenfläche.
 */
.editor-tools vaadin-button:hover,
.editor-tools vaadin-button[theme~="icon"].primary-color:hover {
    background: var(--p360-primary);
    color: #ffffff;
}

/* editor.scss: Restliche Editor-Helfer (Upload-Panel, Polygon-Stop, Icon-Groessen). */
.upload-viewpoint-image-panel {
    border: 2px dashed var(--p360-field-border-color);
}

.stop-polygon-button {
    background: var(--p360-primary);
    color: #ffffff;
}

.map-tools {
    position: absolute;
    top: 100px;
    left: 20px;
    background: #ffffff;
    padding: 8px;
    border-radius: 6px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
    z-index: 5;
}

/*
 * Auf dem Mobiltelefon endet die Leiste vor dem Zoom-Knopf (.ol-zoom, rechts oben in der Karte, gut
 * 40px breit). Die beiden Felder teilen sich die Breite und stehen untereinander, sobald keins mehr
 * 160px bekommt. Die flex-basis übersteuert dabei die feste Breite des Suchfelds aus MapEditor.
 */
@media (max-width: 639.98px) {
    .map-tools {
        left: var(--lumo-space-s);
        right: 64px;
        flex-wrap: wrap;
        row-gap: var(--lumo-space-s);
    }

    .map-tools > * {
        flex: 1 1 160px;
        min-width: 0;
    }
}

/* Wie bei .hotSpotMenu: feste Kantenlaenge gegen die Sprite-Regel `.icon { width/height: 100% }`. */
.small-icon {
    width: 20px;
    height: 20px;
}

.table-inline-icon {
    width: 30px;
    height: 30px;
}

.table-inline-icon-text {
    position: relative;
    top: -9px;
    margin-left: 6px;
}

.jump-hotspot-preview-image {
    object-fit: cover;
}

.jump-hotspot-preview-icon {
    z-index: 1000;
}

/* --- Zielvorschau der Sprungmarke (Auge-Overlay auf dem Zielbild) --- */

/*
 * Quelle: Vaadin-8-Theme themeBase/_dashboard.scss (`.zielvorschau`, `.absoluteButton`,
 * `.absoluteButton140px`). Ohne diese Regeln ist der Auge-Span ein normales Inline-Block-Element
 * und stapelt sich mit seinen 140px UEBER das Zielbild, statt darauf zu liegen.
 *
 * In Vaadin 8 kam der Bezugsrahmen vom Valo-Layout (`.v-verticallayout` war `position: relative`);
 * Flow-Layouts sind `static`, deshalb traegt die Zielvorschau die Klasse jetzt selbst.
 */
.zielvorschau {
    position: relative;
}

.absoluteButton {
    position: absolute;
    top: 50%;
    left: 50%;
    height: 70px;
    width: 70px;
    margin-left: -35px;
    margin-top: -35px;
    pointer-events: none;
    opacity: 0.7;
}

.absoluteButton.absoluteButton140px {
    height: 140px;
    width: 140px;
    margin-left: -70px;
    margin-top: -60px;
}

.zielvorschau:hover .absoluteButton {
    opacity: 1;
}

/*
 * _immotours.scss: `.colorWhite use { color: white; fill: currentColor }` und `.svgDropShadow`.
 * Das Vorschau-Auge ist ein Vaadin-SvgUse ohne `krpanoElement` — die Regeln der krpano-Hotspots
 * (nächster Block) greifen hier nicht, es hat seinen eigenen, kräftigeren Schatten.
 */
.jump-hotspot-preview-icon use {
    color: #ffffff;
    fill: currentColor;
}

.jump-hotspot-preview-icon {
    filter: drop-shadow(5px 5px 2px black);
}

/* --- SVG-Hotspots im krpano-DOM (useSVG.js) --- */

/*
 * Sprungmarken, Info-/Foto-/Video-Hotspots ohne eigenes Icon und die Dreh-/Skalier-Griffe zeichnet
 * `krpano/plugins/js/useSVG.js` als `<svg class="svgDropShadow krpanoElement icon colorWhite">`
 * (die Farbklasse setzt Java, SvgStyles.ColorWhite). Die Symbole füllen mit currentColor — ohne
 * diese Regel erben sie die dunkle Lumo-Textfarbe, und der Ring der Sprungmarke steht im Editor
 * dunkelblau statt weiß wie in der veröffentlichten Tour (der Viewer setzt dort `color: #fff`).
 * Eigene Icons sind `<img>` und bleiben unberührt. Der Schatten hält das Weiß auf hellen Wänden
 * sichtbar.
 */
.krpanoElement.colorWhite use {
    color: #ffffff;
    fill: currentColor;
}

.svgDropShadow.krpanoElement {
    filter: drop-shadow(1.5px 1.5px 3px black);
}

/* --- Punkt-Coldspot (Stecknadel) im krpano-DOM --- */

/*
 * Quelle: Vaadin-8-Theme themeBase/_publicView.scss (Block `.punktColdspot`), 1:1 uebernommen.
 * Das Markup erzeugt `krpano/plugins/js/punktColdspot.js` (Container + `.punkt`/`.text`/`.line`)
 * im Light DOM der Seite. Ohne diese Regeln fehlt den Kindern jede Groesse und Positionierung:
 * die Stecknadel bestand nur noch aus ihrer Beschriftung, Kopf und Nadel waren unsichtbar.
 */
.punktColdspot {
    background-color: orangered;
    position: relative;
}

.punktColdspot > div {
    position: absolute;
}

.punktColdspot .punkt {
    background-color: white;
    border-radius: 50%;
    height: 11px;
    width: 11px;
    bottom: -6px;
    left: -5px;
    top: -5px;
    right: -6px;
    box-shadow: 0 0 4px black;
}

.punktColdspot .line {
    background-color: white;
    bottom: 5px;
    width: 1px;
    left: 0;
    box-shadow: 0 0 4px #000000;
}

.punktColdspot .text {
    color: white;
    text-shadow: 0 0 4px #000000;
    left: 10px;
    white-space: pre;
}

.punktColdspot .text.left {
    left: auto;
    right: 20px;
    text-align: right;
}

/* --- Standpunkt-Auswahl (ViewPointSelectionTabSheet) --- */

/*
 * Bereits im Grundriss verknuepfter Standpunkt: graue Flaeche mit Haken ueber der Kachel
 * (ViewPointSelectionTabSheet#disableViewPoint). Ohne diese Markierung sehen verknuepfte und
 * freie Standpunkte gleich aus, und die rechte Spalte des Grundriss-Fensters verliert ihre
 * einzige Aussage: welcher Standpunkt fehlt noch.
 *
 * Die Kachel bleibt bewusst klickbar (anders als blocked-viewpoint) — ein gesetzter Marker
 * laesst sich so erneut anwaehlen. Klicks auf das ::after zielen ohnehin auf die Kachel selbst.
 *
 * position: relative ist Pflicht: das Flow-VerticalLayout bringt keinen Positionierungs-Kontext
 * mit, das Overlay wuerde sonst am Viewport kleben.
 */
.view-point-selection-element {
    position: relative;
}

.view-point-selection-element.assigned-state::after {
    content: "";
    position: absolute;
    inset: 0;
    opacity: 0.7;
    background-color: rgba(0, 0, 0, 0.23);
    /* Haken-Symbol als Data-URI; die Fuellfarbe #31b7bc steckt im SVG und kann kein Token sein. */
    background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20viewBox%3D%270%200%2020%2020%27%3E%3Ccircle%20cx%3D%2710%27%20cy%3D%279.79%27%20r%3D%278.69%27%20style%3D%27fill%3A%23fff%27%2F%3E%3Cpath%20fill%3D%27%2331B7BC%27%20d%3D%27M10%2C0A10%2C10%2C0%2C1%2C0%2C20%2C10%2C10%2C10%2C0%2C0%2C0%2C10%2C0ZM8%2C15%2C3%2C10%2C4.41%2C8.59%2C8%2C12.17l7.59-7.59L17%2C6Z%27%2F%3E%3C%2Fsvg%3E");
    background-repeat: no-repeat;
    background-position: center;
    background-size: 33px;
}

/*
 * Gesperrte Kachel: der Standpunkt, in dem der Nutzer gerade steht, ist als Sprungmarken-Ziel
 * nicht waehlbar (siehe ViewPointSelectionTabSheet#blockViewPoint). Vaadin 8 kannte diesen
 * Zustand nicht — dort liess sich eine Sprungmarke auf den eigenen Standpunkt legen.
 */
.view-point-selection-element.blocked-viewpoint {
    opacity: 0.4;
    filter: grayscale(1);
    pointer-events: none;
    cursor: default;
}

/*
 * Grundriss-Fenster: der gewählte Standpunkt bekommt einen Rahmen um Bild und Titel, sonst sieht man
 * nicht, welchen Marker man gerade setzt. Nach innen versetzt, weil der Scroller alles abschneidet,
 * was über die Kachel hinausragt.
 */
.floor-plan-selection .view-point-selection-element.selected-viewpoint {
    outline: 3px solid var(--p360-primary);
    outline-offset: -3px;
}

/* --- Zielauswahl der Sprungmarke (Zielauswahl) --- */

/*
 * Große Kacheln, damit man die Räume am Vorschaubild erkennt; wie viele in eine Zeile passen,
 * regelt ViewPointSelectionTabSheet#setMinTileWidth. 2:1 wie die erzeugten Vorschaubilder: das
 * feste Verhältnis hält den Platz frei, solange ein Bild noch lädt, und object-fit schneidet
 * abweichende Bilder zu, statt sie zu verzerren. Den Abstand zwischen den Kacheln liefert das
 * Raster, die Kachel selbst braucht keinen Innenabstand.
 */
.jump-destination-selection .view-point-selection-element {
    padding: 0;
    gap: var(--lumo-space-xs);
    cursor: pointer;
}

.jump-destination-selection .view-point-selection-element img {
    display: block;
    aspect-ratio: 2 / 1;
    object-fit: cover;
    border-radius: 10px;
}

/*
 * Rahmen beim Überfahren (hell) und um das bisherige Ziel einer bestehenden Sprungmarke
 * (selected-viewpoint, kräftig) — beide können gleichzeitig zu sehen sein. Nach innen versetzt:
 * der Scroller schneidet alles ab, was über die Kachel hinausragt.
 */
.jump-destination-selection .view-point-selection-element:hover img {
    outline: 3px solid var(--p360-primary-light);
    outline-offset: -3px;
}

.jump-destination-selection .view-point-selection-element.selected-viewpoint img {
    outline: 3px solid var(--p360-primary);
    outline-offset: -3px;
}

/* --- Abstand der Dialoge zum Fensterrand --- */

/*
 * Auf dem Mobiltelefon nimmt ein Dialog fast die ganze Breite: 0,5rem Rand statt Lumos 1rem. Die
 * Eigenschaft liest das Overlay im Shadow-DOM des Dialogs, sie erbt dorthin.
 */
@media (max-width: 639.98px) {
    vaadin-dialog,
    vaadin-confirm-dialog {
        --vaadin-overlay-viewport-inset: var(--p360-space-s);
    }
}

/*
 * Obergrenze für Dialoge mit `setWidth("95%")` bzw. `"100%"` (Grundriss, Info-Hotspot, Karten-Ebenen,
 * Bild- und PDF-Zuschnitt), sonst laufen sie auf breiten Bildschirmen über die ganze Breite. Die
 * Klasse sitzt am <vaadin-dialog>, die Größe am Overlay-Part. Die Breite ist 1000px wie im
 * Vaadin-8-Theme, trotz des Klassennamens; `100%` hält den Dialog im Fenster.
 */
vaadin-dialog.max-width-1200px::part(overlay) {
    max-width: min(1000px, 100%);
}

vaadin-dialog.max-height-800px::part(overlay) {
    max-height: min(800px, 100%);
}

/* --- Schliess-Knopf im Dialog-Header (component/Dialogs) --- */

/*
 * Der Header-Slot eines Dialogs ohne setHeaderTitle sitzt links; mit Titel steht der Knopf sonst
 * direkt daneben statt am rechten Rand. Das auto-Margin schiebt ihn in beiden Faellen nach rechts.
 */
.dialog-close-button {
    margin-inline-start: auto;
}

/* --- Knopfleiste der ToolWindow-Dialoge (component/ToolWindow) --- */

/*
 * Die Leiste ist die letzte Zeile im Inhalt und scrollt mit ihm. Der graue Grund macht sie über die
 * ganze Breite als Leiste erkennbar (Breite und Ausrichtung setzt ToolWindow). Ohne sichtbaren Knopf bekommt sie
 * nichts davon, sonst stünde ein leerer grauer Streifen im Dialog. Der Abstand nach oben trennt sie
 * von grauen Bereichen direkt darüber, die sonst mit ihr zu einer Fläche verschmölzen.
 *
 * Der Rand unten ist der Abstand zum Dialogrand, wenn der Inhalt scrollt: Dann fällt der untere
 * Innenabstand der Dialogfläche weg, und die Leiste stieß an den Rand. Ein Außenabstand oder ein
 * Element hinter der Leiste verlängert die Scrollfläche nicht, der Rand als Teil der Leiste schon;
 * durchsichtig, der Grund endet am Innenabstand.
 *
 * Den Abstand der Knöpfe zum grauen Rand gibt allein der Innenabstand, auf allen vier Seiten gleich.
 * Lumo gibt Knöpfen oben und unten einen eigenen Außenabstand; der stand nur senkrecht, und der
 * rechte Knopf saß halb so weit vom Rand wie vom oberen und unteren.
 *
 * Innenabstand und Ränder machen den Dialog 3 × --p360-space-xs + --p360-space-s höher als die
 * Knöpfe mit ihrem Lumo-Außenabstand; Maße, die seine Höhe vorrechnen (.view-point-panel), zählen
 * das mit.
 *
 * Fläche, Ecken und seitlichen Innenabstand hebt der Abschnitt „Abschnitte ohne Kasten“ auf: Dort
 * ist die Leiste weiß mit einer Linie oben.
 */
.tool-window .tools:has(> :not([hidden])) {
    margin-top: var(--p360-space-s);
    padding: var(--p360-space-s);
    border-bottom: var(--p360-space-xs) solid transparent;
    border-radius: var(--lumo-border-radius-l);
    background: var(--lumo-contrast-5pct);
    background-clip: padding-box;
}

.tool-window .tools > * {
    margin-block: 0;
}

/* --- Formulare (vaadin-form-layout, component/Forms) --- */

/*
 * Den Abstand zwischen zwei Zeilen gibt das Formular, nicht das Feld. Lumo setzt ihn auf 0;
 * Textfelder fielen nicht auf, weil ihre Beschriftung Luft mitbringt, Text-Editor und Upload-Kachel
 * klebten dagegen aneinander. Gilt für alle Formulare; eigene Zeilen- oder Spaltenabstände setzt
 * keines mehr (Regeln in 0-docs/design/dialoge.md).
 */
vaadin-form-layout {
    --vaadin-form-layout-row-spacing: var(--p360-form-row-gap);
    --vaadin-form-layout-column-spacing: var(--p360-form-column-gap);
}

/* --- Grauer Bereich in Dialogen (component/DialogSection) --- */

/*
 * Gruppiert zusammengehoerige Felder eines Dialogs: leicht graue Flaeche, Titel, optional ein Satz
 * Beschreibung. Den Weissraum zwischen zwei Bereichen gibt das umgebende Layout vor
 * (`gap: var(--p360-section-gap)`), der Bereich hat keinen Aussenabstand. Nicht dasselbe wie die
 * Panel-Karte — Regeln in 0-docs/design/dialoge.md.
 *
 * Fläche, Ecken und seitlichen Innenabstand hebt der Abschnitt „Abschnitte ohne Kasten“ auf: Dort
 * trennt eine Linie die Bereiche.
 */
.dialog-section {
    display: flex;
    flex-direction: column;
    gap: var(--lumo-space-xs);
    box-sizing: border-box;
    padding: var(--lumo-space-m);
    border-radius: var(--lumo-border-radius-l);
    background: var(--lumo-contrast-5pct);
}

/* Lumo-Typografie setzt h3 gross und mit Aussenabstand; im Bereich ist der Titel eine kraeftige Zeile. */
.dialog-section-title {
    margin: 0;
    color: var(--lumo-body-text-color);
    font-size: var(--lumo-font-size-s);
    font-weight: 600;
    line-height: var(--lumo-line-height-xs);
}

/* Absätze aus den Sprachdateien (\n) bleiben stehen, etwa der „Hinweis:“ unter Kontakt. */
.dialog-section-description {
    white-space: pre-line;
    color: var(--lumo-secondary-text-color);
    font-size: var(--lumo-font-size-s);
    line-height: var(--lumo-line-height-s);
}

/* Titel und Beschreibung über einem Formular ohne grauen Bereich (Theme.Styles.Component.FORM_INTRO). */
.p360-form-intro {
    display: flex;
    flex-direction: column;
    gap: var(--lumo-space-xs);
}

/* Untereinander und linksbuendig wie im VerticalLayout; volle Breite holt sich ein Feld per setWidthFull(). */
.dialog-section-content {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--p360-form-row-gap);
    margin-top: var(--lumo-space-s);
}

/*
 * Normale Knoepfe sind selbst grau hinterlegt, auf der grauen Flaeche also grau auf grau. Im Bereich
 * sind sie deshalb weiss mit duennem Rand; die Felder sind es ueberall (2b. Feld-Design).
 *
 * Die Tokens sitzen am Knopf selbst, nicht am Bereich: so erben sie nicht in eingebettete
 * Komponenten mit eigener Optik. Die Medien-Kachel (`.p-uploader`, MediaChooser) ist ausgenommen,
 * ihr Upload-Knopf bleibt, wie er ist.
 *
 * Primaer- und Tertiaerknoepfe lesen eigene Tokens und bleiben unveraendert; Klassen, die Hintergrund
 * oder Rand direkt setzen (`.icon-only-button`, `.flat-button`), gewinnen weiterhin. Das ⓘ
 * (`.p360-info-button`, `tertiary-inline`) liest die normalen Tokens und ist deshalb ausgenommen.
 *
 * Ohne Lumos Rand oben und unten: den Abstand zwischen zwei Zeilen gibt im Bereich der gap des
 * Inhalts vor, mit dem Rand waere jede Knopfzeile 8px hoeher als ihr Knopf.
 */
.dialog-section vaadin-button:not(.p-uploader *, .p360-info-button) {
    --vaadin-button-background: var(--lumo-base-color);
    --vaadin-button-border: 1px solid var(--lumo-contrast-20pct);
    margin: 0;
}

/* --- Standpunkt bearbeiten und Standpunkt-Schritt im Assistenten (component/editor/panel/EditViewPointPanel) --- */

/*
 * Links „Startrichtung" mit der krpano-Ansicht als Quadrat, rechts die übrigen Bereiche. Ist um das
 * Panel weniger als 560px Platz, stehen sie untereinander (Telefon). Maßstab ist die Breite um das
 * Panel — Dialog bzw. Assistenten-Schritt —, nicht die des Fensters. Das Panel selbst kann nicht der
 * Container sein, sonst ließe sich seine Spaltenzahl nicht ändern.
 */
:has(> .view-point-panel) {
    container: view-point-panel-host / inline-size;
}

/*
 * Kantenlänge des Quadrats im Dialog. 90vh ist die Höchsthöhe des Dialogs (Dialogs#applySize);
 * abgezogen wird, was über und unter der Ansicht steht: Dialogkopf, Titel und Beschreibung des
 * Bereichs, Innenabstände und Werkzeugleiste. Gemessen sind 262px (Fensterhöhe 640px), der Wert lässt
 * etwas Luft. Wird der Dialog oben oder unten höher, muss er mit, sonst scrollt der Dialog. Neben dem
 * Quadrat bleiben der
 * rechten Spalte mindestens 240px; auf breiten Fenstern greift diese Grenze nie.
 */
.view-point-panel {
    --view-point-panel-view-max: calc(100cqw - 240px - var(--p360-section-gap) - 2 * var(--lumo-space-m));
    --view-point-panel-view-size: min(clamp(320px, calc(90vh - 266px), 500px), var(--view-point-panel-view-max));
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--p360-section-gap);
    box-sizing: border-box;
}

/*
 * Im Assistenten steht das Panel in einem Schritt fester Höhe. Das Quadrat richtet sich dort nach dem
 * Panel selbst (cqh) statt nach dem Fenster; abgezogen werden Titel, Beschreibung und Innenabstände des
 * Bereichs.
 */
.view-point-panel--wizard {
    --view-point-panel-view-size: min(clamp(200px, calc(100cqh - 90px), 500px), var(--view-point-panel-view-max));
    container-type: size;
}

.view-point-panel-start-direction {
    width: var(--view-point-panel-view-size);
    height: var(--view-point-panel-view-size);
}

.view-point-panel-side {
    display: flex;
    flex-direction: column;
    gap: var(--p360-section-gap);
    min-width: 0;
}

/*
 * Knöpfe im Bereich „Panorama“ über die volle Breite: „Ersetzen“ und „Herunterladen“ teilen sich eine Reihe,
 * „Version des Raumes hinzufügen“ steht allein darunter. Ein Raster statt `flex: 1 1 0`: Flex rechnete den
 * Innenabstand von „Ersetzen“ zur Grundgröße, der Download-Knopf im Anchor hat keinen, und die Reihe teilte
 * sich ungleich. Ausgeblendete Knöpfe legen keine Spalte an, der andere nimmt dann die ganze Reihe.
 */
.view-point-panel-file-actions {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: var(--lumo-space-s);
    align-self: stretch;
}

/* Weniger Innenabstand als Lumo, damit „Herunterladen“ auch auf dem Mobiltelefon in die halbe Reihe passt. */
.view-point-panel-file-actions vaadin-button {
    width: 100%;
    padding-inline: var(--lumo-space-s);
}

.view-point-panel-overlay-add {
    flex: 1 1 100%;
}

/*
 * Untereinander ist das Quadrat so breit wie sein Bereich, und Dialog bzw. Schritt scrollen nach unten;
 * seitlich scrollt nichts. Wer scrollen will, wischt neben dem Pano — darauf dreht ein Wischen das Bild.
 */
@container view-point-panel-host (max-width: 559.98px) {
    .view-point-panel {
        --view-point-panel-view-size: min(calc(100cqw - 2 * var(--lumo-space-m)), 500px);
        grid-template-columns: minmax(0, 1fr);
        align-content: start;
    }

    .view-point-panel--wizard {
        overflow-y: auto;
    }
}

/*
 * Auf dem Telefon gibt der Dialog bzw. Schritt um das Panel seinen seitlichen Innenabstand ab: Die
 * Dialogfläche hat schon einen eigenen, der zweite kostete das Quadrat 32px Kantenlänge.
 */
@media (max-width: 639.98px) {
    :has(> .view-point-panel) {
        padding-inline: 0;
    }
}

/* === Abschnitte ohne Kasten: Linie statt grauer Fläche (Variante V2, Welle 33y) === */

/*
 * Chris, 02.11.2026: Abschnitte in Dialogen und auf Formularseiten (DialogSection) stehen ohne
 * Fläche und Rahmen im Weiß. Getrennt werden sie durch Titel, Abstand und eine feine Linie unter
 * jedem Abschnitt außer dem letzten seiner Geschwister; das Ende des Inhalts zeigt die Linie über
 * der Knopfleiste. Ohne seitlichen Innenabstand stehen Titel und Felder bündig mit dem Dialogtitel;
 * oben und unten bleibt er und hält Felder und Linie auseinander.
 *
 * Der Block hebt Fläche, Ecken und seitlichen Innenabstand aus „Grauer Bereich in Dialogen“ und
 * „Knopfleiste der ToolWindow-Dialoge“ auf und muss deshalb hinter beiden stehen.
 */
html {
    --p360-section-divider-color: #e3e8e6;
}

.dialog-section {
    padding-inline: 0;
    border-radius: 0;
    border-bottom: 1px solid var(--p360-section-divider-color);
    background: none;
}

.dialog-section:not(:has(~ .dialog-section)) {
    border-bottom: none;
}

.tool-window .tools:has(> :not([hidden])) {
    padding-inline: 0;
    border-top: 1px solid var(--p360-section-divider-color);
    border-radius: 0;
    background: none;
}

/*
 * „Standpunkt bearbeiten“: Die Maße des Quadrats ziehen den seitlichen Innenabstand der Abschnitte
 * ab (2 × --lumo-space-m). Ohne ihn stünde das Quadrat auf dem Mobiltelefon 32px schmaler als
 * seine Spalte. Untereinander folgt auf „Startrichtung“ die rechte Spalte; die Linie trennt beide
 * wie die übrigen Abschnitte, nebeneinander braucht es keine.
 */
.view-point-panel {
    --view-point-panel-view-max: calc(100cqw - 240px - var(--p360-section-gap));
}

@container view-point-panel-host (max-width: 559.98px) {
    .view-point-panel {
        --view-point-panel-view-size: min(100cqw, 500px);
    }

    .view-point-panel > .dialog-section {
        border-bottom: 1px solid var(--p360-section-divider-color);
    }
}

/*
 * Text-Editor überall in der App, nicht nur in Abschnitten: Werkzeugleiste fast weiß, zur
 * Schreibfläche eine helle Linie. Im Fokus wird der ganze Rahmen samt Linie zur Fokusfarbe
 * (2b. Feld-Design). `.ql-snow` hebt die Regel über quillFlow.css, das Quill erst zur Laufzeit lädt.
 */
.p360-quill > .ql-toolbar.ql-snow {
    background: #f7f9f8;
}

.p360-quill:not(:focus-within) > .ql-toolbar.ql-snow {
    border-bottom-color: #dfe4e2;
}

/*
 * Die gestrichelten Upload-Flächen lesen den Feldrand. Mit dessen hellem Wert wäre ihr Rand, das
 * einzige Zeichen für eine Ablagefläche, kaum zu sehen; sie behalten deshalb 3,1:1.
 */
.p-uploader,
.upload-viewpoint-image-panel {
    --p360-field-border-color: var(--lumo-contrast-50pct);
}

/* --- Standard-Icon für Sprungmarken: Kachel mittig (EditViewPointGroupDetailsPanel, EditViewPointPanel) --- */

/*
 * „Etage bearbeiten" (Assistent und Editor) und „Standpunkt bearbeiten" zeigen die Kachel gleich:
 * Beschriftung darüber, beides mittig. In „Etage bearbeiten" ist die Beschriftung der Titel des
 * Bereichs, in „Standpunkt bearbeiten" eine Zeile im Bereich „Im Viewer".
 */
.dialog-section--centered {
    text-align: center;
}

.dialog-section--centered > .dialog-section-content {
    align-items: center;
}

.view-point-panel-jump-mark-icon {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--lumo-space-s);
    width: 100%;
    text-align: center;
}

.view-point-panel-jump-mark-icon-caption {
    color: var(--lumo-secondary-text-color);
    font-size: var(--lumo-font-size-s);
    font-weight: 500;
}

/* --- Menübaum mit Inhalt daneben (component/TreeMenuPanel: Projekt-Einstellungen, Profil) --- */

/*
 * Unterpunkte rücken nur 1em ein statt der 2em von Lumo. Mehr braucht es nicht, um sie als
 * Unterpunkte zu erkennen, und lange Einträge wie „Datenschutzbestimmung" passen in die Spalte.
 * Lumo setzt die Variable per :host am Tree-Toggle selbst; am Grid gesetzt käme sie nicht an.
 */
.tree-menu-panel vaadin-grid-tree-toggle {
    --vaadin-grid-tree-toggle-level-offset: 1em;
}

/*
 * Nebeneinander: Die Baumspalte ist 300px breit und so hoch wie das Panel. Der Inhalt rechts ist
 * 100 % breit; ohne flex-shrink: 0 schrumpften beide Spalten anteilig, die Baumspalte landete bei gut
 * 200px und kürzte ihre Einträge.
 */
.tree-menu-panel-tree {
    width: 300px;
    height: 100%;
    flex-shrink: 0;
}


/*
 * Übereinander, sobald neben dem Baum weniger als rund 340px für den Inhalt blieben: oben der Baum,
 * so hoch wie seine Einträge und höchstens 40 % der Fensterhöhe, gleich darunter der Inhalt. Maßstab
 * ist die Breite um das Panel, nicht die des Fensters — im Dialog ist weniger Platz als auf einer
 * Seite. Das Panel selbst kann nicht der Container sein, sonst ließe sich seine Richtung nicht ändern.
 */
:has(> .tree-menu-panel) {
    container: tree-menu-host / inline-size;
}

@container tree-menu-host (max-width: 640px) {
    .tree-menu-panel {
        flex-direction: column;
    }

    .tree-menu-panel-tree {
        width: 100%;
        height: auto;
        max-height: 40vh;
    }
}

/* --- Teilen-Fenster: Reiter „Direkt teilen“ / „Einbettung“ (dashboard/widgets/share/ShareTabs) --- */

/*
 * Der Rand kommt vom Fenster; der Reiterinhalt beginnt bündig mit den Reitern. Die Variable statt nur
 * `padding-inline`: Der Scrollbereich zieht sich mit ihr per negativem Außenabstand nach außen, ohne
 * Innenabstand wurde er so 16px breiter als das Fenster und ließ sich seitwärts schieben.
 */
.share-tabs::part(content) {
    --vaadin-scroller-padding-inline: 0;
    padding-inline: 0;
}

.share-tabs-hint {
    color: var(--lumo-secondary-text-color);
    font-size: var(--lumo-font-size-s);
}

/* Angezeigt kleiner als das PNG, damit QR-Code und Download-Knopf auf dem Mobiltelefon ohne Scrollen passen. */
.share-tabs-qr {
    width: 12.5rem;
    height: 12.5rem;
}

/* --- Benutzer & Ansprechpartner (component/UsersAndContactsPage, ContactTable) --- */

.users-page-contingent {
    color: var(--lumo-secondary-text-color);
}

/* Name und Rolle untereinander; lange Namen brechen um statt die Stift-Spalte zu verdrängen. */
.contact-table-name {
    display: flex;
    flex-direction: column;
    min-width: 0;
    white-space: normal;
}

.contact-table-main-contact {
    color: var(--lumo-secondary-text-color);
    font-size: var(--lumo-font-size-s);
}

/* --- Tarifauswahl als Karten (OrderRateTabSheet Schritt 1 / TariffCard) --- */

/*
 * Umbrechende Flex-Reihen statt fester Zeilen: die Zeilenaufteilung steckt nicht mehr im
 * Java-Zaehler, sondern im Umbruch. `justify-content: center` zentriert auch die letzte,
 * unvollstaendige Reihe — in Vaadin 8 machte das `Alignment.MIDDLE_CENTER` auf jeder
 * HorizontalLayout-Zeile. Mit einem 1fr-Raster ginge das nicht: dort sitzen die Karten der
 * letzten Reihe zwangslaeufig in den linken Spalten.
 */
.rate-card-grid {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--lumo-space-l);
    width: 100%;
    align-items: stretch;
}

/*
 * Feste Kartenbreite ist die Voraussetzung fuers Zentrieren: duerften die Karten wachsen, waeren
 * die zwei Karten der letzten Reihe doppelt so breit wie die vier darueber.
 * `max-width: 100%` haelt die Karte auf dem Telefon im Container — die 260px allein wuerden
 * sonst ueber den rechten Rand hinausstehen.
 */
.rate-card-grid > * {
    flex: 0 0 260px;
    max-width: 100%;
}

/*
 * vaadin-card ist durchgehend über eigene Custom-Properties steuerbar; Farbe, Schriftgröße und
 * -gewicht des Titels setzt das Komponenten-Stylesheet mit !important und lässt sich NUR über
 * diese Tokens ändern — nicht mit einer Regel von außen.
 *
 * Weiche Karte wie die Panel-Karten (.panel-card-shell): weiß, ohne Band und ohne Rahmen, Name
 * und Zahlen fast schwarz. Von oben: Name mit ⓘ, Preis, „Auswählen“, Leistungen mit Häkchen,
 * Preis-Zusätze. Die Marke zeigt sich nur in „Auswählen“, den Häkchen und am gewählten Tarif.
 * Beide Klassen-Varianten (--elevated, --outlined über ?variante=a) sehen gleich aus: die
 * Werte hier überstimmen die Rahmen und Schatten der Card-Themes outlined/elevated.
 */
.rate-card-shell {
    --vaadin-card-padding: 18px;
    --vaadin-card-gap: var(--lumo-space-s);
    --vaadin-card-background: var(--lumo-base-color);
    --vaadin-card-border-width: 0;
    --vaadin-card-border-radius: 12px;
    --vaadin-card-shadow: 0 1px 3px rgba(0, 0, 0, 0.08), 0 4px 12px rgba(0, 0, 0, 0.06);
    --vaadin-card-title-color: var(--p360-ink);
    --vaadin-card-title-font-size: 15px;
    --vaadin-card-title-font-weight: 700;
    box-shadow: var(--vaadin-card-shadow);
    box-sizing: border-box;
    /* Gleiche Höhe je Rasterzeile, damit die Karten einer Reihe gleich lang sind. */
    height: 100%;
    transition: box-shadow 120ms ease-out;
}

/*
 * Das ⓘ sitzt im Header-Suffix. Die Card stellt den Suffix an den rechten Rand (1fr auto);
 * hier steht er direkt hinter dem Namen, den Abstand bringt .p360-info-button selbst mit.
 */
.rate-card-shell::part(header) {
    grid-template-columns: minmax(0, max-content) 1fr;
    column-gap: 0;
}

.rate-card-shell > [slot="title"] {
    text-align: start;
}

.rate-card-shell > [slot="header-suffix"] {
    justify-self: start;
}

/*
 * Preisblock: große Zahl, darunter die Abrechnungseinheit. Der Block trägt die Abstände, damit sie
 * auch dann sitzen, wenn ein Produkt keine Abrechnungsperiode führt und die Einheit fehlt.
 */
.rate-card-shell .rate-price-block {
    display: block;
    padding-bottom: var(--lumo-space-s);
}

.rate-card-shell .rate-price {
    display: block;
    color: var(--p360-ink);
    font-size: 2rem;
    font-weight: 700;
    line-height: 1.15;
}

/* Die Einheit ordnet den Preis ein, sie konkurriert nicht mit ihm: klein und grau. */
.rate-card-shell .rate-price-unit {
    display: block;
    color: var(--p360-muted);
    font-size: var(--lumo-font-size-xs);
    line-height: 1.4;
}

/*
 * Flow-VerticalLayout richtet auf der Querachse an `start` aus — „Auswählen“ und die Zeilen der
 * Zusätze wären sonst nur so breit wie ihr Text.
 */
.rate-card-shell .rate-card {
    align-items: stretch;
}

.rate-card-shell .rate-select-button {
    margin: 0 0 var(--lumo-space-m);
    border-radius: 6px;
}

/*
 * Leistungen: je Zeile ein Häkchen in der Knopffarbe, dahinter Wert halbfett und Bezeichnung grau
 * als ein Fließtext. Das Häkchen hängt links heraus, damit umbrechender Text unter dem Text
 * weiterläuft und nicht unter dem Häkchen.
 */
.rate-card-shell .rate-benefits {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: var(--lumo-font-size-s);
    line-height: 1.4;
}

/* In der Zusammenfassung fehlt „Auswählen“ zwischen Preis und Liste; dort hält dieser Abstand sie auseinander. */
.rate-card-shell .rate-price-block + .rate-benefits {
    margin-top: var(--lumo-space-s);
}

.rate-card-shell .rate-benefit {
    position: relative;
    padding-inline-start: 22px;
    color: var(--p360-muted);
}

.rate-card-shell .rate-benefit-icon {
    position: absolute;
    inset-inline-start: 0;
    top: 0.2em;
    width: 14px;
    height: 14px;
    padding: 0;
    color: var(--p360-primary-action);
}

.rate-card-shell .rate-benefit-value {
    color: var(--p360-ink);
    font-weight: 600;
}

/* Nicht enthalten (0): grauer Strich statt Häkchen, die ganze Zeile grau und ohne Hervorhebung. */
.rate-card-shell .rate-benefit--none .rate-benefit-icon,
.rate-card-shell .rate-benefit--none .rate-benefit-value {
    color: var(--p360-muted);
    font-weight: 400;
}

/* Preis-Zusätze sind keine Leistungen: kleiner, grau, ohne Häkchen, Bezeichnung links, Preis rechts. */
.rate-card-shell .rate-extras {
    margin-top: var(--lumo-space-m);
    color: var(--p360-muted);
    font-size: var(--lumo-font-size-xs);
    line-height: 1.5;
}

.rate-card-shell .rate-extras-title {
    display: block;
    color: var(--p360-ink);
    font-weight: 600;
    margin-bottom: 2px;
}

.rate-card-shell .rate-detail-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--lumo-space-s);
}

.rate-card-shell .rate-detail-row > :last-child {
    text-align: right;
    white-space: nowrap;
}

/* Fuß nur in der Zusammenfassung: der Bestellknopf füllt die Zeile. */
.rate-card-shell::part(footer) {
    padding-top: var(--lumo-space-s);
}

.rate-card-shell > vaadin-button[slot="footer"] {
    flex: 1 1 auto;
    margin: 0;
    border-radius: 6px;
}

/*
 * Der gewählte Tarif bleibt beim Zurückspringen aus Schritt 2 erkennbar: ein 2px-Ring in der
 * Knopffarbe. Als Outline, nicht als Rahmen — die Karte behält ihre Maße und springt nicht.
 */
.rate-card-shell--selected {
    outline: 2px solid var(--p360-primary-action);
    outline-offset: 0;
}

/* Hover nur als Ergänzung — auf Touch existiert er nicht, deshalb hängt hier keine Information. */
@media (hover: hover) {
    .rate-card-grid > .rate-card-shell:hover {
        --vaadin-card-shadow: 0 2px 6px rgba(0, 0, 0, 0.10), 0 8px 20px rgba(0, 0, 0, 0.08);
    }
}

/*
 * Zusammenfassung in Schritt 3: dieselbe Karte, aber allein neben den Rechnungsdaten statt im
 * Raster — die Breite, die dort `.rate-card-grid > *` vorgibt, braucht sie hier selbst.
 *
 * `height: auto` nimmt die 100% aus `.rate-card-shell` zurueck: eine gesetzte Hoehe schliesst das
 * Strecken der Zeile aus, und ohne Strecken endet die Karte an ihrem Inhalt statt am Fuss der
 * Rechnungsspalte — genau der Versatz, den die Knopfzeile unten ausgleicht.
 */
.rate-card-summary {
    flex: 0 0 260px;
    max-width: 100%;
    height: auto;
}

/*
 * Rechnungsdaten links, Tarifkarte rechts. Auf dem Telefon passen beide nicht nebeneinander;
 * die Flex-Basis der linken Spalte schlaegt dabei das inline gesetzte width:100% (Flow-
 * `setWidthFull`), das sonst schon auf dem Desktop einen Umbruch erzwingen wuerde.
 *
 * Beide Spalten sind gleich hoch (`stretch`) — erst dadurch haben Kartenfuss und Knopfzeile
 * denselben Bezugspunkt, egal welche Spalte den Inhalt vorgibt.
 */
.rate-order-body {
    flex-wrap: wrap;
    align-items: stretch;
}

.rate-order-body > :first-child {
    flex: 1 1 320px;
    min-width: 0;
}

/*
 * Knopfzeile am Fuss der Rechnungsspalte. `margin-top: auto` schiebt sie an den unteren Rand der
 * Spalte, damit sie mit dem Bestellknopf im Kartenfuss auf einer Linie liegt.
 *
 * Die beiden Zugaben sind der Rest, der sonst bliebe: Lumo gibt jedem Knopf 4px Aussenabstand
 * oben und unten (im Kartenfuß ist er auf 0 gesetzt), und der Bestellknopf steht um den
 * Innenabstand der Karte (18px in .rate-card-shell) über ihrer Unterkante — diese Zeile nicht.
 */
.rate-order-actions {
    margin-top: auto;
    padding-bottom: 18px;
}

.rate-order-actions vaadin-button {
    margin-block: 0;
}

/*
 * Hinweis-/Link-Zeile unter dem Assistenten. Sie sitzt ausserhalb des Scrollers: jede Zeile, die
 * sie beim Umbruch dazugewinnt, fehlt dem Inhalt darueber. Kleine Schrift und enge Abstaende
 * halten sie auf dem Telefon bei zwei bis drei flachen Zeilen.
 */
.rate-wizard-footer {
    font-size: var(--lumo-font-size-xs);
    gap: var(--lumo-space-xs);
    row-gap: 0;
    align-items: baseline;
}

/* --- On/Off-Schalter (component/dashboard/widgets/Switch) --- */

/*
 * Quelle: Vaadin-8-Theme themeBase/_dashboard.scss (Block `.switch`), $red/$green als Tokens.
 * Die Komponente baut nur zwei leere Divs und holt Groesse, Farbe und Position komplett aus
 * diesen Regeln — ohne sie steht der Schalter zwar im DOM, ist aber unsichtbar.
 */
.switch {
    cursor: pointer;
    outline: 1px solid #efefef;
}

.switch .switchButton {
    background: var(--p360-status-red);
    transition: all 1s;
}

.switch .switchButton.isOn {
    margin-left: 50%;
    background: var(--p360-status-green);
}

.switch .switchButton .switchIcon {
    display: block;
    margin: auto;
}

/* --- Tabellen: Gitter fuer alle Grids --- */

/*
 * Eine Fassung fuer JEDES vaadin-grid der Anwendung, ohne Schalter an der Wurzel: nur Striche. Kein
 * Aussenrahmen, keine Rundung, weisser Grund; die Kopfzeile ohne Flaeche, fast schwarz und fett, mit
 * einer 2px-Linie darunter; zwischen den Zeilen ein zarter Strich. Senkrechte Linien zwischen den
 * Spalten gibt es nicht, wie in der Vaadin-8-Fassung (Chris, 29.10.); die Spalten trennt die
 * Polsterung der Zellen. Wie die Tabelle unten endet, steht unter „Höhe nach Inhalt“.
 *
 * Den Lumo-Aussenrahmen setzt Lumo per :host; die Regel am Element von aussen gewinnt. Die
 * Zeilenlinien sind Lumos obere Zellkante, die erste Zeile hat keine — darueber steht die Linie des
 * Kopfes. Kein Grid darf die Varianten no-row-borders oder column-borders tragen; die Regeln hier
 * ueberstimmen sie nicht.
 *
 * Ausgenommen sind der Menuebaum (TreeMenuPanel) und die Bearbeitungslisten (.item-list): Sie
 * behalten Lumos Rahmen, Linien und Auswahlton (Chris, 03.11.). Die gerundeten Ecken brauchen dort
 * "clip" und nicht "hidden": hidden macht den Host zum Scroll-Container, und ein Grid mit
 * setAllRowsVisible(true) rendert dann nur die Zeilen, die in die sichtbare Hoehe passen.
 */

vaadin-grid:not(.item-list, .tree-menu-panel-tree *) {
    --p360-grid-line: #eef1f0;
    --p360-grid-edge: #c8cfcc;
    border: 0;
    border-radius: 0;
    background-color: #fff;
}

/* Der Kopf klebt beim Scrollen ueber den Zeilen; seine Zellen sind deckend (Lumo-Grundfarbe). */
vaadin-grid:not(.item-list, .tree-menu-panel-tree *)::part(header-cell) {
    color: var(--p360-ink);
    font-size: 13px;
    font-weight: 700;
}

vaadin-grid:not(.item-list, .tree-menu-panel-tree *)::part(last-header-row-cell) {
    border-bottom: 2px solid var(--p360-ink);
}

vaadin-grid:not(.item-list, .tree-menu-panel-tree *)::part(body-cell) {
    border-top-color: var(--p360-grid-line);
}

:is(vaadin-grid.item-list, .tree-menu-panel-tree vaadin-grid) {
    border-radius: var(--lumo-border-radius-m);
    overflow: clip;
}

/*
 * Symbol-Spalten (Teilen / Einstellungen / Papierkorb): Der Knopf misst 32px, die Lumo-Polsterung
 * der Zelle nimmt links und rechts zusammen 32px — in einer 60px-Spalte blieb kein Platz, der Knopf
 * klebte am linken Rand. Ohne seitliche Polsterung zentriert ihn setTextAlign(CENTER). Dasselbe gilt
 * für das 60px-Vorschaubild in seiner 76px-Spalte (GridColumns.thumbnail).
 *
 * Die Polsterung sitzt nicht an der Zelle selbst, sondern am eingeblendeten
 * <vaadin-grid-cell-content>; von aussen erreichbar ist sie nur ueber das Token, das dort gelesen
 * wird — Custom Properties vererben durch den Slot hindurch.
 */
vaadin-grid::part(icon-cell),
vaadin-grid::part(thumbnail-cell) {
    --vaadin-grid-cell-padding: var(--p360-space-xs) 0;
}

/* --- Tabellen: Höhe nach Inhalt (E30) --- */

/*
 * Ein Grid mit setAllRowsVisible(true) ist so hoch wie seine Zeilen; den Strich unter der letzten
 * Zeile setzt die Regel weiter unten. Ein expand() oder
 * setSizeFull() am selben Grid setzt flex-grow bzw. height als Inline-Stil und streckt es trotzdem
 * auf die Höhe des Bereichs — die letzte Zeile steht dann offen über einer leeren Fläche. Gegen
 * Inline-Stile hilft nur !important.
 */
vaadin-grid[all-rows-visible] {
    flex-grow: 0 !important;
    height: auto !important;
}

/*
 * AppGrid#setGrowingHeightWithinArea: Mit wenigen Zeilen ist das Grid so hoch wie seine Zeilen, aber
 * nie höher als der Bereich, den es mit vielen Zeilen füllt; passen die Zeilen nicht hinein, scrollt es
 * darin. Dafür müssen das Grid und die Layouts um es herum unter ihre Inhaltshöhe schrumpfen dürfen —
 * mit der Vorgabe min-height: auto schöbe die Zeilenhöhe Reiter und Tabelle über den Seitenrand.
 */
vaadin-grid.app-grid-within-area[all-rows-visible],
:is(vaadin-tabsheet, vaadin-vertical-layout, vaadin-horizontal-layout):has(vaadin-grid.app-grid-within-area[all-rows-visible]) {
    min-height: 0;
}

/*
 * Unterer Abschluss ohne Außenrahmen. Steht die letzte Zeile am Ende der Tabelle (alle Zeilen
 * sichtbar, oder die Zeilen einer langen Liste passen gerade hinein), bekommt sie den Strich, den
 * Lumo ihr nicht gibt. Scrollt die Liste (overflow top/bottom), trägt die Unterkante des Grids einen
 * kräftigeren Strich: Er zeigt, wo die Liste angeschnitten ist — der zarte Zeilenstrich ginge dort
 * unter —, und schließt am Ende des Scrollens die letzte Zeile ab. Beides zugleich gäbe dort eine
 * doppelte Linie. Ohne Zeilen schließt der Strich den Hinweis ab.
 */
vaadin-grid:not(.item-list, .tree-menu-panel-tree *):not([overflow~="top"]):not([overflow~="bottom"])::part(last-row-cell),
vaadin-grid:not(.item-list, .tree-menu-panel-tree *)::part(empty-state) {
    border-bottom: 1px solid var(--p360-grid-line);
}

vaadin-grid:not(.item-list, .tree-menu-panel-tree *):is([overflow~="top"], [overflow~="bottom"]) {
    border-bottom: 1px solid var(--p360-grid-edge);
}

/*
 * AppGrid#setHideWhenEmpty: ohne Zeilen bleibt nur der Hinweis, ohne Kopfzeile und Strich, im Ton
 * von .no-table-entries-label. Die Klasse app-grid-empty setzt das Grid nach jeder Zählung selbst.
 */
vaadin-grid.app-grid-hide-empty.app-grid-empty::part(header-cell) {
    display: none;
}

vaadin-grid.app-grid-hide-empty::part(empty-state) {
    border-bottom: 0;
    background: var(--p360-primary-lighter);
    border-radius: var(--lumo-border-radius-m);
    color: var(--lumo-body-text-color);
    padding: var(--lumo-space-s) var(--lumo-space-m);
    text-align: center;
}

/* --- Tabellen: anklickbare Zeilen unter der Maus (.grid-clickable-rows) --- */

/*
 * Die ganze Zeile färbt sich unter der Maus hell in der Markenfarbe, wie in Vaadin 8
 * (helle Markenfarbe `--p360-primary-tint`, bei Immo-Tours #d6f5eb). Der Zeiger
 * allein fällt kaum auf. Nur an Tabellen mit Klick auf die Zeile (AppGrid#addItemClickListener
 * oder die Klasse am Grid); reine Anzeige-Tabellen bleiben ruhig.
 *
 * Die Zellen malen ihren Hintergrund selbst; die Property am Zeilen-Part erben alle Zellen der
 * Zeile. Nur bei echter Maus: Auf dem Mobiltelefon bliebe die Farbe nach dem Tippen stehen.
 */
vaadin-grid.grid-clickable-rows {
    --p360-grid-row-hover: var(--p360-primary-tint);
}

@media (hover: hover) {
    vaadin-grid.grid-clickable-rows::part(body-row):hover {
        --vaadin-grid-cell-background: var(--p360-grid-row-hover);
    }
}

/* --- Bearbeitungslisten: Etagen, Raumversionen (ItemListEditorPanel) --- */

/*
 * Kurze, sortierbare Listen sehen aus wie eine Liste, nicht wie eine Tabelle: keine Kopfzeile,
 * nur die Linie zwischen den Zeilen, ein stärker gerundeter Außenrahmen und ein Hover-Ton auf der
 * ganzen Zeile. Von der Strich-Fassung der übrigen Grids sind sie ausgenommen.
 */
.item-list-panel {
    gap: var(--lumo-space-s);
}

/*
 * vaadin-grid bringt `flex: 1 1 auto` mit. Sobald die Liste scrollt (setAllRowsVisible aus, feste
 * Höhe), zöge sie sich sonst im Wizard über die ganze Höhe des Schritts.
 */
vaadin-grid.item-list {
    flex-grow: 0;
    border-radius: var(--lumo-border-radius-l);
    /* Lumos Polsterung der Zellen je Seite; die Breite der Knopfspalte (ItemListEditorPanel) rechnet damit. */
    --item-list-cell-padding-inline: var(--lumo-space-m);
}

/* Die Höhe ist ROW_HEIGHT_PX in ItemListEditorPanel — beide Werte gemeinsam ändern. */
vaadin-grid.item-list::part(cell) {
    min-height: 44px;
}

/*
 * Die Zellen malen ihren Hintergrund selbst (aus --vaadin-grid-cell-background). Ein Hintergrund
 * am Zeilen-Part läge unsichtbar darunter; die Property dagegen erben alle Zellen der Zeile.
 */
vaadin-grid.item-list::part(row):hover {
    --vaadin-grid-cell-background: var(--lumo-contrast-5pct);
}

.item-list-drag-handle {
    flex: none;
    width: 16px;
    margin-inline-end: var(--lumo-space-s);
    color: var(--lumo-tertiary-text-color);
    cursor: grab;
}

.item-list-drag-handle:active {
    cursor: grabbing;
}

.item-list-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.item-list-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--lumo-space-xs);
}

/*
 * Dezent grau, erst unter dem Zeiger in der Farbe der Aktion. Ohne den Lumo-Außenabstand des
 * Knopfs (4px oben und unten) wäre jede Zeile 49 statt 44px hoch; die kleinere Polsterung macht
 * das Symbol zierlicher als in den Werkzeugleisten.
 */
.item-list-action {
    margin: 0;
    padding: 7px;
    color: var(--lumo-secondary-text-color);
    border-radius: var(--lumo-border-radius-m);
}

.item-list-action:hover {
    color: var(--p360-primary-accent);
    background: var(--lumo-contrast-10pct);
}

.item-list-action--delete:hover {
    color: var(--lumo-error-text-color);
}

/* Das Plus in Schriftgröße statt der 1.5em, die .svg-use vorgibt, mit etwas Luft zum Text. */
.item-list-add .svg-use {
    width: 1em;
    margin-inline-end: var(--lumo-space-xs);
}

/*
 * Auf dem Telefon gehen die Abstände an den Namen: schmalere Polsterung in den Zellen und am Griff, und
 * der Dialog bzw. Schritt um die Liste gibt seinen seitlichen Innenabstand ab, die Dialogfläche hat
 * schon einen. Mit den Abständen vom Desktop blieben dem Namen bei 375px rund 80px. Griff, Stift und
 * Mülleimer bleiben gleich groß.
 */
@media (max-width: 639.98px) {
    :has(> .item-list-panel) {
        padding-inline: 0;
    }

    vaadin-grid.item-list {
        --item-list-cell-padding-inline: var(--lumo-space-s);
        --vaadin-grid-cell-padding: var(--lumo-space-xs) var(--item-list-cell-padding-inline);
    }

    .item-list-drag-handle {
        margin-inline-end: var(--lumo-space-xs);
    }

    /*
     * Lange Namen brechen in zwei Zeilen um, erst danach kürzt die Ellipse. Die Zeile wird dabei höher
     * als ROW_HEIGHT_PX in ItemListEditorPanel; ab zehn Einträgen hat die Liste dort eine feste Höhe und
     * zeigt dann weniger Zeilen, bevor sie scrollt.
     */
    .item-list-name {
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 2;
        line-clamp: 2;
        white-space: normal;
        overflow-wrap: anywhere;
        hyphens: auto;
    }
}

/* --- Bearbeitungslisten: Namen in der Zeile umbenennen (ItemListEditorPanel#isRenameInline) --- */

/*
 * Fläche und Feld beginnen gleich weit vor dem Text, damit der Name beim Wechsel zwischen Text und
 * Feld stehen bleibt. Im Feld setzt sich der Abstand aus der Polsterung von Feld und Eingabe
 * zusammen; beide sind deshalb unten auf denselben Wert gesetzt.
 */
.item-list-name--renamable,
.item-list-name-field {
    --item-list-name-inset: 6px;
    margin-inline-start: calc(-1 * var(--item-list-name-inset));
}

/* Der Name sieht aus wie Text; erst unter dem Zeiger zeigt eine Fläche, dass ein Klick ihn zum Feld macht. */
.item-list-name--renamable {
    box-sizing: border-box;
    padding: var(--lumo-space-xs) var(--item-list-name-inset);
    border-radius: var(--lumo-border-radius-m);
    cursor: text;
}

.item-list-name--renamable:hover {
    background: var(--lumo-contrast-10pct);
}

/* Ohne den Lumo-Außenabstand des Felds wäre die Zeile höher als ROW_HEIGHT_PX in ItemListEditorPanel. */
.item-list-name-field {
    min-width: 0;
    padding: 0;
}

.item-list-name-field::part(input-field) {
    padding-inline: var(--item-list-name-inset);
}

.item-list-name-field > input {
    padding-inline: 0;
}

/* --- Neues-Projekt-Assistent: Upload-Schritt (NewProjectWizard) --- */

/*
 * Ablage und Kacheln der hochgeladenen Panoramen. Ohne Kacheln hat die Ablage die ganze Fläche.
 * Sobald es Kacheln gibt, stehen sie breit links neben der 250px schmalen Ablage, schmal darunter —
 * nebeneinander blieben auf 375px für drei Spalten rund 25px je Kachel. Die Ablage steht im DOM
 * vorn, damit sie beim Tabben zuerst kommt; breit setzt `row-reverse` sie nach rechts.
 */
.wizard-upload {
    container-type: inline-size;
}

.wizard-upload-row {
    flex-direction: row-reverse;
}

.wizard-upload-drop {
    flex: 1 1 auto;
    height: 80%;
    align-self: center;
}

.wizard-upload-tiles-area {
    display: none;
}

.wizard-upload-row--has-tiles .wizard-upload-tiles-area {
    display: block;
    flex: 1 1 0;
    min-width: 0;
    height: 100%;
}

.wizard-upload-row--has-tiles .wizard-upload-drop {
    flex: 0 0 250px;
}

.wizard-upload-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: var(--lumo-space-m);
    /* Platz für den Scrollbalken des Kachelbereichs, sonst liegt er über der rechten Kachelspalte. */
    padding-inline-end: var(--p360-space-m);
}

/*
 * Schmal: die Ablage oben in voller Breite und so hoch wie ihr Inhalt, darunter die Kacheln in zwei
 * Spalten, die übrige Höhe scrollt. Die Grenze liegt dort, wo neben der Ablage keine zwei Kacheln
 * à 120px mehr Platz haben (250 + 2 × 120 + Abstände + Innenabstand des Schritts).
 */
@container (max-width: 560px) {
    .wizard-upload-row--has-tiles {
        flex-direction: column;
    }

    .wizard-upload-row--has-tiles .wizard-upload-drop {
        flex: 0 0 auto;
        width: 100%;
        height: auto;
        align-self: stretch;
    }

    .wizard-upload-row--has-tiles .wizard-upload-tiles-area {
        width: 100%;
        height: auto;
        min-height: 0;
    }

    .wizard-upload-tiles {
        grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    }
}

/* --- Neues-Projekt-Assistent: Verarbeitungsstand im Upload-Schritt (NewProjectWizard, ViewPointThumbnail) --- */

/*
 * Über der Ablage steht, solange Panoramen hochgeladen oder verarbeitet werden, Ring und Hinweis; so
 * lange ist „Weiter“ gesperrt. Der Hinweis zu einem gescheiterten Panorama steht an derselben Stelle,
 * in Fehlerfarbe.
 */
.wizard-upload-status {
    align-items: center;
    gap: var(--lumo-space-s);
    color: var(--lumo-secondary-text-color);
    font-size: var(--lumo-font-size-s);
}

.wizard-upload-failed-hint {
    color: var(--lumo-error-text-color);
    font-size: var(--lumo-font-size-s);
}

/* Gescheiterte Kachel: kein Bild, der Hinweis mittig wie beim Laden, dazu das Kreuz zum Entfernen. */
.view-point-thumbnail--failed {
    display: flex;
    align-items: center;
    justify-content: center;
    padding-inline: var(--lumo-space-s);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--lumo-error-color) 50%, transparent);
}

.view-point-thumbnail--failed .view-point-thumbnail-title {
    position: static;
    height: auto;
    line-height: var(--lumo-line-height-xs);
    background: none;
    color: var(--lumo-error-text-color);
    font-size: var(--lumo-font-size-xs);
    text-align: center;
    white-space: normal;
    text-wrap: balance;
    overflow-wrap: anywhere;
}

/* --- Buchhaltung: Rechnungs-Statusbadges --- */

/*
 * Rechnungsstatus als Lumo-Badge (`theme="badge"` liefert Radius, Groesse und Zentrierung).
 * Hier kommen nur die Dinge dazu, die Lumo nicht kennt: die feste Farbzuordnung je Status mit
 * weisser Schrift und eine Obergrenze fuer die Breite.
 *
 * `display: block` statt des Lumo-`inline-flex`, weil `text-overflow: ellipsis` einen
 * Block-Container braucht — "Zahlungserinnerung" passt in keine Statusspalte, die noch Platz
 * fuer die restlichen vierzehn laesst. Den vollen Text traegt das title-Attribut.
 */
.invoice-status-badge {
    display: block;
    width: 100%;
    max-width: 122px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-align: center;
    color: #ffffff;
    background-color: var(--p360-status-draft);
}

.invoice-status-badge--DRAFT {
    background-color: var(--p360-status-draft);
}

.invoice-status-badge--OPEN,
.invoice-status-badge--Offen {
    background-color: var(--p360-status-open);
}

.invoice-status-badge--PAID,
.invoice-status-badge--Bezahlt {
    background-color: var(--p360-status-paid);
}

.invoice-status-badge--REMINDER,
.invoice-status-badge--Zahlungserinnerung {
    background-color: var(--p360-status-reminder);
}

.invoice-status-badge--REMINDER2,
.invoice-status-badge--Mahnung1 {
    background-color: var(--p360-status-reminder2);
}

.invoice-status-badge--REMINDER3,
.invoice-status-badge--Mahnung2 {
    background-color: var(--p360-status-red);
}

.invoice-status-badge--UNCOLLECTIBLE {
    background-color: var(--p360-status-uncollectible);
}

.invoice-status-badge--VOID,
.invoice-status-badge--Ungueltig {
    background-color: var(--p360-status-void);
}

/* --- Bild-Detail (media/MediaDetailPanel): Vorschau mit Höchstmaß, Formular daneben oder darunter --- */

/*
 * Vorschau und Formular teilen sich eine Zeile, solange beide ihre Mindestbreite bekommen; sonst
 * steht das Formular unter dem Bild. Die Vorschau ist höchstens 30rem groß, damit ein großes Foto
 * nicht das ganze Fenster füllt.
 */
.media-detail-panel {
    flex-wrap: wrap;
}

.media-detail-preview {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 1 1 16rem;
    min-width: 0;
    max-width: 30rem;
}

/* Höchstens 30rem hoch (auf niedrigen Bildschirmen 60 % der Höhe) und nie größer als die Datei selbst. */
.media-detail-preview img {
    max-width: min(100%, var(--media-natural-width, 100%));
    max-height: min(30rem, 60vh, var(--media-natural-height, 30rem));
}

/* PDF: Bild der ersten Seite als Link auf die Datei; der feine Rand zeigt die Blattkante auf weißem Grund. */
.media-detail-preview--pdf img {
    border: 1px solid var(--lumo-contrast-20pct);
}

.media-detail-form {
    flex: 1 1 18rem;
    min-width: 0;
    max-width: 25rem;
}

/* --- Seitenkopf (component/general/PageHeader): Titel links, Aktionen rechts --- */

/*
 * Ohne eigenen Innenabstand (setPadding(false)): Titel und Inhalt darunter beginnen an derselben
 * Kante. Reicht die Breite nicht für Titel und Aktionen, rutschen die Aktionen in die nächste
 * Zeile, statt die Seite seitwärts zu schieben.
 */
.p360-page-header {
    flex-wrap: wrap;
}

/*
 * Ein Scrollbereich schneidet ab, was über seinen Rand ragt. Reichen Formularfelder bis an den Rand
 * (Inhalt neben dem Menübaum, Dialoge „Neuer Benutzer“ und „Neuer Ansprechpartner“), fehlte ihr
 * Fokusrahmen (2px außen) links bzw. rechts. Der Bereich reicht --lumo-space-xs über seinen Platz
 * hinaus und holt das mit Innenabstand wieder herein: Die Felder bleiben bündig mit Titel und Baum,
 * der Rahmen hat Luft. `content-box`, weil Flow die Breite inline auf 100 % setzt; so zählt der
 * Innenabstand nicht von ihr ab.
 */
.p360-focus-room {
    box-sizing: content-box;
    padding-inline: var(--lumo-space-xs);
    margin-inline: calc(-1 * var(--lumo-space-xs));
}

/*
 * Reiter auf einer Seite (Kunden, Buchhaltung): Den Rand gibt die Seite. Lumos Innenabstand des
 * Reiter-Inhalts rückte Filter und Tabelle 16px hinter Titel und Reiterleiste ein. Wie bei .share-tabs
 * über die Variable, sonst bleibt der negative Außenabstand stehen.
 */
.p360-page-tabs::part(content) {
    --vaadin-scroller-padding-inline: 0;
    padding-inline: 0;
}

/* --- Mobiltelefon (Muster M3): Regeln unter 640px, Umschaltpunkt aus konventionen-responsive.md --- */

/*
 * Hilfsklassen für Dinge, die auf dem Mobiltelefon wegfallen oder nur dort erscheinen, zum Beispiel
 * Werkzeuge der Editor-Kopfleiste und das Menü „⋯“ (component/general/OverflowMenu). Grid-Spalten
 * lassen sich so nicht ausblenden; die schaltet der Server um (component/general/PhoneLayout).
 */
@media (max-width: 639.98px) {
    .p360-phone-hidden {
        display: none;
    }
}

@media (min-width: 640px) {
    .p360-phone-only {
        display: none;
    }
}

/* Menü „⋯“ in einer Tabellenzelle: so breit wie sein Knopf und mittig, wie die Symbolknöpfe daneben. */
.p360-row-actions-menu {
    width: fit-content;
    margin-inline: auto;
}

/*
 * Medien-Galerie (component/media/MediaGallery): höchstens so viele Spalten, wie die Galerie vorgibt
 * (6, in Auswahl-Dialogen 3), aber keine unter 150px. Auf dem Desktop bleibt es bei sechs Spalten,
 * auf dem Mobiltelefon werden es zwei statt sechs 49px schmaler Streifen. Die 0,1px Abzug halten die
 * Spaltenzahl gegen Rundungsfehler: genau ein Sechstel passte sonst gelegentlich nur fünfmal.
 */
.media-gallery {
    grid-template-columns: repeat(auto-fill, minmax(max(150px, calc((100% - (var(--media-gallery-columns) - 1) * var(--media-gallery-gap)) / var(--media-gallery-columns) - 0.1px)), 1fr));
}

.media-gallery > * {
    height: 200px;
}

@media (max-width: 639.98px) {
    .media-gallery > * {
        height: auto;
        min-height: 0;
        aspect-ratio: 1;
    }

    /*
     * Filterzeile (Medienseite): das erste Feld allein in voller Breite, die übrigen teilen sich die
     * nächste Zeile. Über flex-basis, weil Flow flex-grow als Inline-Stil setzt.
     */
    .p360-filter-row > :first-child {
        flex-basis: 100%;
    }
}

/*
 * Besucherstatistik (component/StatisticsChartLayout): Chart.js füllt diese Höhe. Auf dem Desktop
 * entspricht sie dem alten Seitenverhältnis 2:1, auf dem Mobiltelefon bleiben mindestens 240px.
 */
.statistics-chart {
    height: clamp(15rem, 45vw, 38rem);
}

/*
 * Bestellassistent (component/rates/OrderRateTabSheet). Auf dem Mobiltelefon waren die drei Reiter
 * auf je 106px gestaucht („chnungsadres“); an ihrer Stelle steht „Schritt 2 von 3: Rechnungsadresse“.
 * Die Schritte selbst hatten bis zu drei eingerückte Ebenen, die Tarifkarten blieben 247px schmal.
 */
.rate-order-step {
    font-weight: 600;
    padding-block: var(--lumo-space-s);
}

@media (min-width: 640px) {
    .rate-order-step {
        display: none;
    }
}

@media (max-width: 639.98px) {
    .rate-order-tabsheet > vaadin-tabs {
        display: none;
    }

    /*
     * Die Karten gehen über die volle Breite. Ohne Luft schnitt der Scrollbereich ihren Schatten
     * links und rechts ab (die Karten standen randlos auf Weiß), ebenso den Fokusrahmen der
     * Rechnungsdaten. Wie .p360-focus-room: über den Platz hinaus, mit Innenabstand zurück.
     */
    .rate-order-tabsheet::part(content) {
        padding-inline: var(--lumo-space-xs);
        margin-inline: calc(-1 * var(--lumo-space-xs));
    }

    .rate-order-step-content,
    .rate-order-step-content > vaadin-vertical-layout {
        padding-inline: 0;
        margin-inline: 0;
    }

    .rate-card-grid > *,
    .rate-card-summary {
        flex: 1 1 100%;
    }
}

/*
 * Menübaum als Auswahlliste (component/TreeMenuPanel): sobald Baum und Inhalt übereinander stehen,
 * steht statt des Baums eine Auswahlliste über dem Inhalt. Nur bei Panels, die eine haben — das
 * Kategorien-Fenster der Karte borgt sich die Klassen, sein Baum ist der Inhalt.
 */
.tree-menu-panel > .p360-nav-select {
    display: none;
}

@container tree-menu-host (max-width: 640px) {
    .tree-menu-panel > .p360-nav-select {
        display: inline-flex;
    }

    .tree-menu-panel:has(> .p360-nav-select) > .tree-menu-panel-tree {
        display: none;
    }
}

.p360-nav-select-child {
    padding-inline-start: var(--lumo-space-m);
}

.p360-nav-select-group {
    display: block;
    padding: var(--lumo-space-s) var(--lumo-space-m) var(--lumo-space-xs);
    color: var(--lumo-secondary-text-color);
    font-size: var(--lumo-font-size-xs);
    font-weight: 600;
}

/*
 * Editor-Kopfleiste (component/editor/TourEditorHeaderTools): Vorschau, Inhalte und Teilen stehen
 * auf dem Mobiltelefon im Menü „⋯“. Engere Abstände lassen dem Standpunkt-Titel noch etwas mehr.
 */
@media (max-width: 639.98px) {
    .editor-header-tools {
        gap: var(--lumo-space-xs);
    }
}

/* Der Innenabstand zählt zur Mindesthöhe; sonst scrollte der Drawer um diese 16px in sich. */
.p360-drawer-content {
    box-sizing: border-box;
}

/* Logo oben im Drawer, so hoch wie in der Kopfleiste (ui/MainLayout#createDrawer). */
.p360-drawer-brand {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
    height: var(--p360-header-height);
    padding-inline: var(--lumo-space-s);
}

.p360-drawer-brand img,
.p360-drawer-brand svg {
    max-height: 100%;
}

/* „Account“ im Drawer-Fuß sieht aus wie „Abmelden“ darunter. */
.p360-drawer-account {
    color: rgba(255, 255, 255, 0.78);
    justify-content: flex-start;
}
